diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..24fb4b81 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,166 @@ +name: CI + +on: + push: + branches: [master] + tags: + - "v**" + pull_request: + schedule: + - cron: '0 0 * * 3' # At 12:00 AM, only on Wednesday + workflow_dispatch: + +jobs: + build-test: + runs-on: ${{ matrix.os }} + strategy: + matrix: + include: + - { os: windows-latest, rust-version: stable, shell: 'msys2 {0}' } + - { os: macos-11, rust-version: stable, shell: bash } + - { os: ubuntu-20.04, rust-version: stable, shell: bash, extra: true } + - { os: ubuntu-20.04, rust-version: 1.65, shell: bash } + - { os: ubuntu-20.04, rust-version: beta, shell: bash } + - { os: ubuntu-20.04, rust-version: nightly, shell: bash } + defaults: + run: + shell: ${{ matrix.shell }} + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@master + if: "!contains(matrix.os,'windows')" + id: toolchain + with: + toolchain: ${{ matrix.rust-version }} + components: clippy, rustfmt + - uses: msys2/setup-msys2@v2 + if: contains(matrix.os,'windows') + with: + update: true + install: >- + base-devel + mingw-w64-x86_64-rust + - uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ matrix.os }}_rustc-${{ steps.toolchain.outputs.cachekey }}_cargo-${{ hashFiles('**/Cargo.lock') }} + + # Build and test. + - name: Build library + run: cargo rustc --verbose --lib -- -D warnings + - name: Test + if: "!matrix.extra" + run: cargo test --all --verbose + + # Extra steps only run once to avoid duplication, when matrix.extra is true + - name: Test and report + if: matrix.extra + run: | + cargo install cargo2junit --force + RUSTC_BOOTSTRAP=1 cargo test --all -- -Z unstable-options --format json --report-time | cargo2junit > cargo_test_results.xml + - name: Publish cargo test results artifact + if: matrix.extra + uses: actions/upload-artifact@v3 + with: + name: cargo-test-results + path: cargo_test_results.xml + - name: Publish cargo test summary + if: matrix.extra + uses: EnricoMi/publish-unit-test-result-action/composite@master + with: + check_name: Cargo test summary + files: cargo_test_results.xml + fail_on: nothing + comment_mode: off + - name: Check formatting + if: matrix.extra + run: cargo fmt --check || echo "::warning ::cargo fmt found some formatting changes that may improve readability" + - name: Check clippy + if: matrix.extra + run: cargo clippy --no-deps || echo "::warning ::cargo clippy found some code style changes that may be more idiomatic" + + # On stable rust builds, build a binary and publish as a github actions + # artifact. These binaries could be useful for testing the pipeline but + # are only retained by github for 90 days. + - name: Build release binary + if: contains(matrix.rust-version,'stable') + run: | + cargo rustc --verbose --bin scryer-prolog --release -- -D warnings + echo "$PWD/target/release" >> $GITHUB_PATH + - name: Publish release binary artifact + if: contains(matrix.rust-version,'stable') + uses: actions/upload-artifact@v3 + with: + path: target/release/scryer-prolog* + name: scryer-prolog_${{ matrix.os }} + + logtalk-test: + runs-on: ubuntu-20.04 + needs: [build-test] + steps: + # Download prebuilt ubuntu binary from build-test job, setup logtalk + - uses: actions/download-artifact@v3 + with: + name: scryer-prolog_ubuntu-20.04 + - run: | + chmod +x scryer-prolog + echo "$PWD" >> "$GITHUB_PATH" + - name: Install Logtalk + uses: logtalk-actions/setup-logtalk@master + with: + logtalk-version: git + logtalk-tool-dependencies: false + + # Run logtalk tests. + - name: Run Logtalk's prolog compliance test suite + working-directory: ${{ env.LOGTALKUSER }}/tests/prolog/ + run: | + pwd + scryerlgt -g '{ack(tester)},halt.' + logtalk_tester -p scryer -g "set_logtalk_flag(clean,off)" -w -t 360 \ + -f xunit \ + -s "$LOGTALKUSER/tests/prolog" \ + || echo "::warning ::logtalk compliance suite failed" + # -u "https://github.com/LogtalkDotOrg/logtalk3/tree/$LOGTALK_GIT_HASH/tests/prolog/" \ + - name: Publish Logtalk test logs + uses: actions/upload-artifact@v3 + with: + name: logtalk-test-logs + path: '${{ env.LOGTALKUSER }}/tests/prolog/logtalk_tester_logs' + - name: Publish Logtalk test results artifact + uses: actions/upload-artifact@v3 + with: + name: logtalk-test-results + path: '${{ env.LOGTALKUSER }}/tests/prolog/**/*.xml' + - name: Publish Logtalk test summary + uses: EnricoMi/publish-unit-test-result-action/composite@master + with: + check_name: Logtalk test summary + files: '${{ env.LOGTALKUSER }}/tests/prolog/**/*.xml' + fail_on: nothing + comment_mode: off + + # Publish binaries when building for a tag + release: + runs-on: ubuntu-20.04 + needs: [build-test] + if: startsWith(github.ref, 'refs/tags/v') + steps: + - uses: actions/download-artifact@v3 + - name: Zip binaries for release + run: | + zip scryer-prolog_macos-11.zip ./scryer-prolog_macos-11/scryer-prolog + zip scryer-prolog_ubuntu-20.04.zip ./scryer-prolog_ubuntu-20.04/scryer-prolog + zip scryer-prolog_windows-latest.zip ./scryer-prolog_windows-latest/scryer-prolog.exe + - name: Release + uses: softprops/action-gh-release@v1 + with: + files: | + scryer-prolog_macos-11.zip + scryer-prolog_ubuntu-20.04.zip + scryer-prolog_windows-latest.zip diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index b31829e7..dffe1cd7 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -2,10 +2,10 @@ name: Docker Publish on: push: - tags: [ 'v*.*.*' ] - -env: - IMAGE_NAME: mjt128/scryer-prolog + branches: + - 'master' + tags: + - 'v*.*.*' jobs: build: @@ -18,33 +18,36 @@ jobs: # Workaround: https://github.com/docker/build-push-action/issues/461 - name: Setup Docker buildx - uses: docker/setup-buildx-action@79abd3f86f79a9d68a23c75a09a9a85889262adf + # https://github.com/docker/setup-buildx-action + uses: docker/setup-buildx-action@v2.2.1 # Login against Docker registry - # https://github.com/docker/login-action - name: Log into registry - uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c + # https://github.com/docker/login-action + uses: docker/login-action@v2.1.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} # Extract Docker image tag from git tag. E.g. if git tag is "v0.19.1" then use - # Docker image tag "0.19.1". Tag "latest" is automatically synced with newest - # version. - # https://github.com/docker/metadata-action + # Docker image tag "0.19.1". The "latest" tag reflects the most recent build on + # master. - name: Extract Docker metadata id: meta - uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 + # https://github.com/docker/metadata-action + uses: docker/metadata-action@v4.1.1 with: - images: docker.io/${{ env.IMAGE_NAME }} + images: docker.io/${{ secrets.DOCKERHUB_USERNAME }}/scryer-prolog tags: | type=semver,pattern={{version}} + type=raw,value=latest,enable={{is_default_branch}} + # type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'master') }} # Build and push Docker image with Buildx - # https://github.com/docker/build-push-action - name: Build and push Docker image id: build-and-push - uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc + # https://github.com/docker/build-push-action + uses: docker/build-push-action@v3.2.0 with: context: . push: true diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index edcd1993..00000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,71 +0,0 @@ -name: Test -on: [push, pull_request] - -jobs: - build: - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-20.04, macos-10.15] - rust-version: [stable, beta] - steps: - - name: Checkout sources - uses: actions/checkout@v2 - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: ${{ matrix.rust-version }} - override: true - - name: Build lib - uses: actions-rs/cargo@v1 - with: - command: rustc - args: --verbose --lib -- -D warnings - - name: Build bin - uses: actions-rs/cargo@v1 - with: - command: rustc - args: --verbose --bin scryer-prolog -- -D warnings - - name: Test - uses: actions-rs/cargo@v1 - with: - command: test - args: --verbose --all - - name: Num tests - uses: actions-rs/cargo@v1 - continue-on-error: true - with: - command: test - args: --verbose --all --no-default-features --features num - msrv: - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-20.04, macos-10.15] - steps: - - name: Checkout sources - uses: actions/checkout@v2 - - name: Install cargo-msrv - uses: baptiste0928/cargo-install@v1.1.0 - with: - crate: cargo-msrv - - name: Verify MSRV - run: cargo msrv --verify - windows: - runs-on: windows-latest - defaults: - run: - shell: msys2 {0} - steps: - - name: Setup MSYS2 - uses: msys2/setup-msys2@v2 - with: - update: true - install: >- - base-devel - mingw-w64-x86_64-rust - - name: Checkout sources - uses: actions/checkout@v3 - - name: Test on Windows - run: cargo test --verbose --all diff --git a/Cargo.lock b/Cargo.lock index 5ed25656..c0c62cfc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "aho-corasick" version = "1.0.2" @@ -11,6 +26,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + [[package]] name = "android_system_properties" version = "0.1.5" @@ -40,15 +61,6 @@ dependencies = [ "wait-timeout", ] -[[package]] -name = "autocfg" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" -dependencies = [ - "autocfg 1.1.0", -] - [[package]] name = "autocfg" version = "1.1.0" @@ -56,10 +68,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] -name = "az" -version = "1.2.1" +name = "backtrace" +version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" +checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] [[package]] name = "base64" @@ -67,6 +88,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" +[[package]] +name = "base64" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" + [[package]] name = "bit-set" version = "0.5.3" @@ -88,6 +115,24 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "blake2" version = "0.8.1" @@ -102,11 +147,11 @@ dependencies = [ [[package]] name = "blake2" -version = "0.10.4" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9cf849ee05b2ee5fba5e36f97ff8ec2533916700fc0758d40d92136a42f3388" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" dependencies = [ - "digest 0.10.5", + "digest 0.10.7", ] [[package]] @@ -123,11 +168,11 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] @@ -152,9 +197,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.11.1" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "byte-tools" @@ -170,15 +215,15 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.2.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "cc" -version = "1.0.76" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a284da2e6fe2092f2353e51713435363112dfd60030e22add80be333fb928f" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cfg-if" @@ -188,13 +233,13 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.22" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" +checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" dependencies = [ + "android-tzdata", "iana-time-zone", "js-sys", - "num-integer", "num-traits", "time", "wasm-bindgen", @@ -203,34 +248,15 @@ dependencies = [ [[package]] name = "clipboard-win" -version = "4.4.2" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4ab1b92798304eedc095b53942963240037c0516452cb11aeba709d420b2219" +checksum = "7191c27c2357d9b7ef96baac1773290d4ca63b24205b82a3fd8a0637afcf0362" dependencies = [ "error-code", "str-buf", "winapi", ] -[[package]] -name = "cloudabi" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -dependencies = [ - "bitflags", -] - -[[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 = "core-foundation" version = "0.9.3" @@ -243,9 +269,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "cpu-time" @@ -259,9 +285,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "03e69e28e9f7f77debdedbaafa2866e1de9ba56df55a8bd7cfc724c25a09987c" dependencies = [ "libc", ] @@ -272,7 +298,7 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ebde6a9dd5e331cd6c6f48253254d117642c31653baa475e394657c59c1f7d" dependencies = [ - "bitflags", + "bitflags 1.3.2", "crossterm_winapi", "libc", "mio 0.7.14", @@ -297,8 +323,8 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2db40892a506901e4e8281f00e42687df82d1d3448cb0289ae9183a60cb42ec1" dependencies = [ - "blake2 0.10.4", - "rand_core 0.6.4", + "blake2 0.10.6", + "rand_core", "sha2", ] @@ -308,7 +334,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", "typenum", ] @@ -324,56 +350,103 @@ dependencies = [ [[package]] name = "ctrlc" -version = "3.2.3" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d91974fbbe88ec1df0c24a4f00f99583667a7e2e6272b2b92d294d81e462173" +checksum = "2a011bbe2c35ce9c1f143b7af6f94f29a167beb4cd1d29e6740ce836f723120e" dependencies = [ - "nix 0.25.0", - "winapi", + "nix", + "windows-sys 0.48.0", ] [[package]] -name = "cxx" -version = "1.0.81" +name = "dashmap" +version = "5.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97abf9f0eca9e52b7f81b945524e76710e6cb2366aead23b7d4fbf72e281f888" +checksum = "6943ae99c34386c84a470c499d3414f66502a41340aa895406e0d2e4a207b91d" dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", -] - -[[package]] -name = "cxx-build" -version = "1.0.81" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cc32cc5fea1d894b77d269ddb9f192110069a8a9c1f1d441195fba90553dea3" -dependencies = [ - "cc", - "codespan-reporting", + "cfg-if", + "hashbrown 0.14.0", + "lock_api", "once_cell", - "proc-macro2 1.0.47", - "quote 1.0.21", - "scratch", - "syn 1.0.103", + "parking_lot_core 0.9.8", ] [[package]] -name = "cxxbridge-flags" -version = "1.0.81" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca220e4794c934dc6b1207c3b42856ad4c302f2df1712e9f8d2eec5afaacf1f" +name = "dashu" +version = "0.3.1" +source = "git+https://github.com/coasys/dashu.git#ae7ee53fad213e09da5fe4b30e9e9e8bce96aedd" +dependencies = [ + "dashu-base", + "dashu-float", + "dashu-int", + "dashu-macros", + "dashu-ratio", +] [[package]] -name = "cxxbridge-macro" -version = "1.0.81" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b846f081361125bfc8dc9d3940c84e1fd83ba54bbca7b17cd29483c828be0704" +name = "dashu-base" +version = "0.3.1" +source = "git+https://github.com/coasys/dashu.git#ae7ee53fad213e09da5fe4b30e9e9e8bce96aedd" + +[[package]] +name = "dashu-float" +version = "0.3.2" +source = "git+https://github.com/coasys/dashu.git#ae7ee53fad213e09da5fe4b30e9e9e8bce96aedd" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "dashu-base", + "dashu-int", + "num-order", + "num-traits", + "static_assertions", +] + +[[package]] +name = "dashu-int" +version = "0.3.1" +source = "git+https://github.com/coasys/dashu.git#ae7ee53fad213e09da5fe4b30e9e9e8bce96aedd" +dependencies = [ + "cfg-if", + "dashu-base", + "num-modular 0.6.0", + "num-order", + "num-traits", + "static_assertions", +] + +[[package]] +name = "dashu-macros" +version = "0.3.1" +source = "git+https://github.com/coasys/dashu.git#ae7ee53fad213e09da5fe4b30e9e9e8bce96aedd" +dependencies = [ + "dashu-base", + "dashu-float", + "dashu-int", + "dashu-ratio", + "proc-macro2", + "quote", +] + +[[package]] +name = "dashu-ratio" +version = "0.3.2" +source = "git+https://github.com/coasys/dashu.git#ae7ee53fad213e09da5fe4b30e9e9e8bce96aedd" +dependencies = [ + "dashu-base", + "dashu-float", + "dashu-int", + "num-order", + "num-traits", +] + +[[package]] +name = "derive_deref" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcdbcee2d9941369faba772587a565f4f534e42cb8d17e5295871de730163b2b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -393,13 +466,13 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.5" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer 0.10.3", + "block-buffer 0.10.4", "crypto-common", - "subtle 2.4.1", + "subtle 2.5.0", ] [[package]] @@ -437,18 +510,27 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "ed25519" -version = "1.5.2" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9c280362032ea4203659fc489832d0204ef09f247a0506f170dafcac08c369" +checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" dependencies = [ "signature", ] [[package]] name = "either" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +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", +] [[package]] name = "endian-type" @@ -458,13 +540,13 @@ checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" [[package]] name = "errno" -version = "0.2.8" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -489,22 +571,22 @@ dependencies = [ [[package]] name = "fastrand" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" dependencies = [ "instant", ] [[package]] name = "fd-lock" -version = "3.0.8" +version = "3.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb21c69b9fea5e15dbc1049e4b77145dd0ba1c84019c488102de0dc4ea4b0a27" +checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5" dependencies = [ "cfg-if", - "rustix", - "windows-sys 0.42.0", + "rustix 0.38.1", + "windows-sys 0.48.0", ] [[package]] @@ -529,10 +611,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] -name = "fuchsia-cprng" -version = "0.1.1" +name = "form_urlencoded" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futf" @@ -546,9 +637,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" dependencies = [ "futures-channel", "futures-core", @@ -561,9 +652,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", "futures-sink", @@ -571,15 +662,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-executor" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" dependencies = [ "futures-core", "futures-task", @@ -588,38 +679,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" [[package]] name = "futures-macro" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2", + "quote", + "syn 2.0.22", ] [[package]] name = "futures-sink" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-channel", "futures-core", @@ -653,9 +744,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.6" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -663,15 +754,21 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.8" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "libc", "wasi 0.11.0+wasi-snapshot-preview1", ] +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + [[package]] name = "git-version" version = "0.3.5" @@ -689,26 +786,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe69f1cbdb6e28af2bac214e943b99ce8a0a06b447d15d3e61161b0423139f3f" dependencies = [ "proc-macro-hack", - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", -] - -[[package]] -name = "gmp-mpfr-sys" -version = "1.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea3f42dadb6c75f122e9aa87e757ef11d4282f664c9f2e6476a9c2c8970f9d19" -dependencies = [ - "libc", - "winapi", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] name = "h2" -version = "0.3.15" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" +checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" dependencies = [ "bytes", "fnv", @@ -729,6 +816,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + [[package]] name = "heck" version = "0.3.3" @@ -740,11 +833,17 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.19" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + +[[package]] +name = "home" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" dependencies = [ - "libc", + "windows-sys 0.48.0", ] [[package]] @@ -760,23 +859,23 @@ dependencies = [ [[package]] name = "html5ever" -version = "0.23.0" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce65ac8028cf5a287a7dbf6c4e0a6cf2dcf022ed5b167a81bae66ebf599a8b7" +checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" dependencies = [ "log", "mac", "markup5ever", - "proc-macro2 0.4.30", - "quote 0.6.13", - "syn 0.15.44", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] name = "http" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", @@ -794,6 +893,29 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "http-body" +version = "1.0.0-rc.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "951dfc2e32ac02d67c90c0d65bd27009a635dc9b381a2cc7d284ab01e3a0150d" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08ef12f041acdd397010e5fb6433270c147d3b8b2d0a840cd7fff8e531dca5c8" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body 1.0.0-rc.2", + "pin-project-lite", +] + [[package]] name = "httparse" version = "1.8.0" @@ -808,9 +930,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.23" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", @@ -818,7 +940,7 @@ dependencies = [ "futures-util", "h2", "http", - "http-body", + "http-body 0.4.5", "httparse", "httpdate", "itoa", @@ -830,6 +952,28 @@ dependencies = [ "want", ] +[[package]] +name = "hyper" +version = "1.0.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b75264b2003a3913f118d35c586e535293b3e22e41f074930762929d071e092" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body 1.0.0-rc.2", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "tokio", + "tracing", + "want", +] + [[package]] name = "hyper-tls" version = "0.5.0" @@ -837,7 +981,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ "bytes", - "hyper", + "hyper 0.14.27", "native-tls", "tokio", "tokio-native-tls", @@ -845,36 +989,45 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.53" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "winapi", + "windows", ] [[package]] name = "iana-time-zone-haiku" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ - "cxx", - "cxx-build", + "cc", +] + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", ] [[package]] name = "indexmap" -version = "1.9.1" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ - "autocfg 1.1.0", - "hashbrown", + "autocfg", + "hashbrown 0.12.3", ] [[package]] @@ -888,14 +1041,21 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.1" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7d367024b3f3414d8e01f437f704f41a9f64ab36f9067fa73e526ad4c763c87" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ + "hermit-abi", "libc", - "windows-sys 0.42.0", + "windows-sys 0.48.0", ] +[[package]] +name = "ipnet" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" + [[package]] name = "itertools" version = "0.10.5" @@ -907,24 +1067,27 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] [[package]] name = "keccak" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9b7d56ba4a8344d6be9729995e6b06f928af29998cdf79fe390cbf6b1fee838" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] [[package]] name = "lazy_static" @@ -949,7 +1112,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" dependencies = [ "arrayvec", - "bitflags", + "bitflags 1.3.2", "cfg-if", "ryu", "static_assertions", @@ -957,9 +1120,36 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.137" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "libffi" +version = "3.2.0" +source = "git+https://github.com/coasys/libffi-rs.git?branch=windows-space#f6e9e50efde0aa4e940dd6f709a59bb426875362" +dependencies = [ + "libc", + "libffi-sys", +] + +[[package]] +name = "libffi-sys" +version = "2.3.0" +source = "git+https://github.com/coasys/libffi-rs.git?branch=windows-space#f6e9e50efde0aa4e940dd6f709a59bb426875362" +dependencies = [ + "cc", +] + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] [[package]] name = "libsodium-sys" @@ -974,38 +1164,32 @@ dependencies = [ ] [[package]] -name = "link-cplusplus" -version = "1.0.7" +name = "linux-raw-sys" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" -dependencies = [ - "cc", -] +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.1.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb68f22743a3fb35785f1e7f844ca5a3de2dde5bd0c0ef5b372065814699b121" +checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" [[package]] name = "lock_api" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" dependencies = [ - "autocfg 1.1.0", + "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.17" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "mac" @@ -1021,21 +1205,30 @@ checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" [[package]] name = "markup5ever" -version = "0.8.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1af46a727284117e09780d05038b1ce6fc9c76cc6df183c3dae5a8955a25e21" +checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" dependencies = [ "log", - "phf 0.7.24", + "phf 0.10.1", "phf_codegen", - "serde", - "serde_derive", - "serde_json", "string_cache", "string_cache_codegen", "tendril", ] +[[package]] +name = "markup5ever_rcdom" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9521dd6750f8e80ee6c53d65e2e4656d7de37064f3a7a5d2d11d05df93839c2" +dependencies = [ + "html5ever", + "markup5ever", + "tendril", + "xml5ever", +] + [[package]] name = "match_cfg" version = "0.1.0" @@ -1049,12 +1242,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] -name = "memoffset" -version = "0.6.5" +name = "mime" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" dependencies = [ - "autocfg 1.1.0", + "adler", ] [[package]] @@ -1072,14 +1271,13 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.5" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", - "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.42.0", + "windows-sys 0.48.0", ] [[package]] @@ -1105,9 +1303,9 @@ name = "modular-bitfield-impl" version = "0.11.2" source = "git+https://github.com/mthom/modular-bitfield#213535c684af277563678179d8496f11b84a283f" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -1145,27 +1343,14 @@ dependencies = [ [[package]] name = "nix" -version = "0.23.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6" +checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" dependencies = [ - "bitflags", - "cc", - "cfg-if", - "libc", - "memoffset", -] - -[[package]] -name = "nix" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e322c04a9e3440c327fca7b6c8a63e6890a32fa2ad689db972425f07e0d22abb" -dependencies = [ - "autocfg 1.1.0", - "bitflags", + "bitflags 1.3.2", "cfg-if", "libc", + "static_assertions", ] [[package]] @@ -1183,7 +1368,32 @@ version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ - "autocfg 1.1.0", + "autocfg", + "num-traits", +] + +[[package]] +name = "num-modular" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a5fe11d4135c3bcdf3a95b18b194afa9608a5f6ff034f5d857bc9a27fb0119" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-modular" +version = "0.6.0" +source = "git+https://github.com/coasys/num-modular.git#87d6dc30600207445e07c2cc84e0a47ff58f0aca" + +[[package]] +name = "num-order" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e81e321057a0370997b13e6638bba6bd7f6f426e1f8e9a2562490a28eb23e1bc" +dependencies = [ + "num-modular 0.5.1", "num-traits", ] @@ -1193,24 +1403,33 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ - "autocfg 1.1.0", + "autocfg", ] [[package]] name = "num_cpus" -version = "1.14.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ "hermit-abi", "libc", ] [[package]] -name = "once_cell" -version = "1.16.0" +name = "object" +version = "0.30.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" +checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "opaque-debug" @@ -1220,11 +1439,11 @@ checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" [[package]] name = "openssl" -version = "0.10.42" +version = "0.10.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" +checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "foreign-types", "libc", @@ -1235,13 +1454,13 @@ dependencies = [ [[package]] name = "openssl-macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2", + "quote", + "syn 2.0.22", ] [[package]] @@ -1252,11 +1471,10 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.77" +version = "0.9.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03b84c3b2d099b81f0953422b4d4ad58761589d0229b5506356afca05a3670a" +checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6" dependencies = [ - "autocfg 1.1.0", "cc", "libc", "pkg-config", @@ -1280,7 +1498,7 @@ checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ "instant", "lock_api", - "parking_lot_core 0.8.5", + "parking_lot_core 0.8.6", ] [[package]] @@ -1290,44 +1508,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.4", + "parking_lot_core 0.9.8", ] [[package]] name = "parking_lot_core" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" dependencies = [ "cfg-if", "instant", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", "winapi", ] [[package]] name = "parking_lot_core" -version = "0.9.4" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dc9e0dc2adc1c69d09143aff38d3d30c5c3f0df0dad82e6d25547af174ebec0" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.3.5", "smallvec", - "windows-sys 0.42.0", + "windows-targets", ] [[package]] -name = "phf" -version = "0.7.24" +name = "percent-encoding" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3da44b85f8e8dfaec21adae67f95d93244b2ecf6ad2a692320598dcc8e6dd18" -dependencies = [ - "phf_shared 0.7.24", -] +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "phf" @@ -1341,23 +1556,22 @@ dependencies = [ ] [[package]] -name = "phf_codegen" -version = "0.7.24" +name = "phf" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03e85129e324ad4166b06b2c7491ae27fe3ec353af72e72cd1654c7225d517e" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" dependencies = [ - "phf_generator 0.7.24", - "phf_shared 0.7.24", + "phf_shared 0.10.0", ] [[package]] -name = "phf_generator" -version = "0.7.24" +name = "phf_codegen" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09364cc93c159b8b06b1f4dd8a4398984503483891b0c26b867cf431fb132662" +checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" dependencies = [ - "phf_shared 0.7.24", - "rand 0.6.5", + "phf_generator 0.10.0", + "phf_shared 0.10.0", ] [[package]] @@ -1367,7 +1581,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d43f3220d96e0080cc9ea234978ccd80d904eafb17be31bb0f76daaea6493082" dependencies = [ "phf_shared 0.9.0", - "rand 0.8.5", + "rand", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared 0.10.0", + "rand", ] [[package]] @@ -1379,18 +1603,9 @@ dependencies = [ "phf_generator 0.9.1", "phf_shared 0.9.0", "proc-macro-hack", - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", -] - -[[package]] -name = "phf_shared" -version = "0.7.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" -dependencies = [ - "siphasher 0.2.3", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -1399,7 +1614,16 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a68318426de33640f02be62b4ae8eb1261be2efbc337b60c54d845bf4484e0d9" dependencies = [ - "siphasher 0.3.10", + "siphasher", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", ] [[package]] @@ -1416,9 +1640,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "ppv-lite86" @@ -1434,9 +1658,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "predicates" -version = "2.1.2" +version = "2.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab68289ded120dcbf9d571afcf70163233229052aec9b08ab09532f698d0e1e6" +checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd" dependencies = [ "difflib", "itertools", @@ -1445,15 +1669,15 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6e7125585d872860e9955ca571650b27a4979c5823084168c5ed5bbfb016b56" +checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" [[package]] name = "predicates-tree" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad3f7fa8d61e139cbc7c3edfebf3b6678883a53f5ffac65d1259329a93ee43a5" +checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" dependencies = [ "predicates-core", "termtree", @@ -1461,45 +1685,33 @@ dependencies = [ [[package]] name = "proc-macro-hack" -version = "0.5.19" +version = "0.5.20+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "0.4.30" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -dependencies = [ - "unicode-xid", -] - -[[package]] -name = "proc-macro2" -version = "1.0.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "0.6.13" +version = "1.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" dependencies = [ - "proc-macro2 0.4.30", + "proc-macro2", ] [[package]] -name = "quote" -version = "1.0.21" +name = "radium" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" -dependencies = [ - "proc-macro2 1.0.47", -] +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" [[package]] name = "radix_trie" @@ -1511,25 +1723,6 @@ dependencies = [ "nibble_vec", ] -[[package]] -name = "rand" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" -dependencies = [ - "autocfg 0.1.8", - "libc", - "rand_chacha 0.1.1", - "rand_core 0.4.2", - "rand_hc", - "rand_isaac", - "rand_jitter", - "rand_os", - "rand_pcg", - "rand_xorshift", - "winapi", -] - [[package]] name = "rand" version = "0.8.5" @@ -1537,18 +1730,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" -dependencies = [ - "autocfg 0.1.8", - "rand_core 0.3.1", + "rand_chacha", + "rand_core", ] [[package]] @@ -1558,24 +1741,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.4", + "rand_core", ] -[[package]] -name = "rand_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -dependencies = [ - "rand_core 0.4.2", -] - -[[package]] -name = "rand_core" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" - [[package]] name = "rand_core" version = "0.6.4" @@ -1585,84 +1753,22 @@ dependencies = [ "getrandom", ] -[[package]] -name = "rand_hc" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rand_isaac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rand_jitter" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" -dependencies = [ - "libc", - "rand_core 0.4.2", - "winapi", -] - -[[package]] -name = "rand_os" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" -dependencies = [ - "cloudabi", - "fuchsia-cprng", - "libc", - "rand_core 0.4.2", - "rdrand", - "winapi", -] - -[[package]] -name = "rand_pcg" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" -dependencies = [ - "autocfg 0.1.8", - "rand_core 0.4.2", -] - -[[package]] -name = "rand_xorshift" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rdrand" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -dependencies = [ - "rand_core 0.3.1", -] - [[package]] name = "redox_syscall" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags", + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", ] [[package]] @@ -1672,7 +1778,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ "getrandom", - "redox_syscall", + "redox_syscall 0.2.16", "thiserror", ] @@ -1718,12 +1824,40 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[package]] -name = "remove_dir_all" -version = "0.5.3" +name = "reqwest" +version = "0.11.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" dependencies = [ - "winapi", + "base64 0.21.2", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body 0.4.5", + "hyper 0.14.27", + "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]] @@ -1762,54 +1896,61 @@ dependencies = [ ] [[package]] -name = "rug" -version = "1.17.0" +name = "rustc-demangle" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "203180f444c95eac53586ed04793ecf6454c5d28f9eca8eead815fc19e136c47" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustix" +version = "0.37.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f25693a73057a1b4cb56179dd3c7ea21a7c6c5ee7d85781f5749b46f34b79c" dependencies = [ - "az", - "gmp-mpfr-sys", + "bitflags 1.3.2", + "errno", + "io-lifetimes", "libc", + "linux-raw-sys 0.3.8", + "windows-sys 0.48.0", ] [[package]] name = "rustix" -version = "0.36.1" +version = "0.38.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812a2ec2043c4d6bc6482f5be2ab8244613cac2493d128d36c0759e52a626ab3" +checksum = "fbc6396159432b5c8490d4e301d8c705f61860b8b6c863bf79942ce5401968f3" dependencies = [ - "bitflags", + "bitflags 2.3.3", "errno", - "io-lifetimes", "libc", - "linux-raw-sys", - "windows-sys 0.42.0", + "linux-raw-sys 0.4.3", + "windows-sys 0.48.0", ] [[package]] name = "rustversion" -version = "1.0.9" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" +checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" [[package]] name = "rustyline" -version = "9.1.2" +version = "12.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db7826789c0e25614b03e5a54a0717a86f9ff6e6e5247f92b369472869320039" +checksum = "994eca4bca05c87e86e15d90fc7a91d1be64b4482b38cb2d27474568fe7c9db9" dependencies = [ - "bitflags", + "bitflags 2.3.3", "cfg-if", "clipboard-win", - "dirs-next", "fd-lock", + "home", "libc", "log", "memchr", - "nix 0.23.1", + "nix", "radix_trie", "scopeguard", - "smallvec", "unicode-segmentation", "unicode-width", "utf8parse", @@ -1818,9 +1959,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "same-file" @@ -1833,12 +1974,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" dependencies = [ - "lazy_static", - "windows-sys 0.36.1", + "windows-sys 0.42.0", ] [[package]] @@ -1847,50 +1987,52 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" -[[package]] -name = "scratch" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" - [[package]] name = "scryer-prolog" version = "0.9.1" dependencies = [ "assert_cmd", - "base64", + "base64 0.12.3", + "bit-set", + "bitvec", "blake2 0.8.1", + "bytes", "chrono", "cpu-time", "crossterm", "crrl", "ctrlc", + "dashu", + "derive_deref", "dirs-next", "divrem", "futures", "fxhash", "git-version", "hostname", - "hyper", - "hyper-tls", + "http-body-util", + "hyper 1.0.0-rc.3", "indexmap", "lazy_static", "lexical", "libc", + "libffi", + "libloading", "maplit", "modular-bitfield", "native-tls", "ordered-float", "phf 0.9.0", "predicates-core", - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2", + "quote", + "rand", "ref_thread_local", "regex", + "reqwest", "ring", "ripemd160", "roxmltree", - "rug", "rustyline", "ryu", "select", @@ -1901,7 +2043,7 @@ dependencies = [ "static_assertions", "strum", "strum_macros", - "syn 1.0.103", + "syn 1.0.109", "to-syn-value", "to-syn-value_derive", "tokio", @@ -1910,11 +2052,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.7.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" +checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -1923,9 +2065,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.6.1" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" +checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7" dependencies = [ "core-foundation-sys", "libc", @@ -1933,36 +2075,26 @@ dependencies = [ [[package]] name = "select" -version = "0.4.3" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac645958c62108d11f90f8d34e4dc2799c838fc995ed4c2075867a2a8d5be76b" +checksum = "6f9da09dc3f4dfdb6374cbffff7a2cffcec316874d4429899eefdc97b3b94dcd" dependencies = [ "bit-set", "html5ever", + "markup5ever_rcdom", ] [[package]] name = "serde" -version = "1.0.147" +version = "1.0.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" - -[[package]] -name = "serde_derive" -version = "1.0.147" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" -dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", -] +checksum = "3b88756493a5bd5e5395d53baa70b194b05764ab85b59e43e4b8f4e1192fa9b1" [[package]] name = "serde_json" -version = "1.0.87" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45" +checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3" dependencies = [ "itoa", "ryu", @@ -1970,36 +2102,51 @@ dependencies = [ ] [[package]] -name = "serial_test" -version = "0.5.1" +name = "serde_urlencoded" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0bccbcf40c8938196944a3da0e133e031a33f4d6b72db3bda3cc556e361905d" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serial_test" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e56dd856803e253c8f298af3f4d7eb0ae5e23a737252cd90bb4f3b435033b2d" +dependencies = [ + "dashmap", + "futures", "lazy_static", - "parking_lot 0.11.2", + "log", + "parking_lot 0.12.1", "serial_test_derive", ] [[package]] name = "serial_test_derive" -version = "0.5.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2acd6defeddb41eb60bb468f8825d0cfd0c2a76bc03bfd235b6a1dc4f6a1ad5" +checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2", + "quote", + "syn 2.0.22", ] [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.5", + "digest 0.10.7", ] [[package]] @@ -2017,9 +2164,9 @@ dependencies = [ [[package]] name = "signal-hook" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d" +checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9" dependencies = [ "libc", "signal-hook-registry", @@ -2038,9 +2185,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ "libc", ] @@ -2051,12 +2198,6 @@ version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" -[[package]] -name = "siphasher" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" - [[package]] name = "siphasher" version = "0.3.10" @@ -2065,11 +2206,11 @@ checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" [[package]] name = "slab" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ - "autocfg 1.1.0", + "autocfg", ] [[package]] @@ -2080,9 +2221,9 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "socket2" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", @@ -2120,38 +2261,30 @@ checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0" [[package]] name = "string_cache" -version = "0.7.5" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89c058a82f9fd69b1becf8c274f412281038877c553182f1d02eb027045a2d67" +checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" dependencies = [ - "lazy_static", "new_debug_unreachable", - "phf_shared 0.7.24", + "once_cell", + "parking_lot 0.12.1", + "phf_shared 0.10.0", "precomputed-hash", "serde", - "string_cache_codegen", - "string_cache_shared", ] [[package]] name = "string_cache_codegen" -version = "0.4.4" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f45ed1b65bf9a4bf2f7b7dc59212d1926e9eaf00fa998988e420fd124467c6" +checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" dependencies = [ - "phf_generator 0.7.24", - "phf_shared 0.7.24", - "proc-macro2 1.0.47", - "quote 1.0.21", - "string_cache_shared", + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro2", + "quote", ] -[[package]] -name = "string_cache_shared" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1884d1bc09741d466d9b14e6d37ac89d6909cbcac41dd9ae982d4d063bbedfc" - [[package]] name = "strum" version = "0.23.0" @@ -2165,10 +2298,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bb0dc7ee9c15cea6199cde9a127fa16a4c5819af85395457ad72d68edc85a38" dependencies = [ "heck", - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2", + "quote", "rustversion", - "syn 1.0.103", + "syn 1.0.109", ] [[package]] @@ -2179,44 +2312,50 @@ checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" [[package]] name = "subtle" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" -version = "0.15.44" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "unicode-xid", -] - -[[package]] -name = "syn" -version = "1.0.103" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" -dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2", + "quote", "unicode-ident", ] [[package]] -name = "tempfile" -version = "3.3.0" +name = "syn" +version = "2.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +checksum = "2efbeae7acf4eabd6bcdcbd11c92f45231ddda7539edc7806bd1a04a03b24616" dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" +dependencies = [ + "autocfg", "cfg-if", "fastrand", - "libc", - "redox_syscall", - "remove_dir_all", - "winapi", + "redox_syscall 0.3.5", + "rustix 0.37.21", + "windows-sys 0.48.0", ] [[package]] @@ -2230,59 +2369,65 @@ dependencies = [ "utf-8", ] -[[package]] -name = "termcolor" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" -dependencies = [ - "winapi-util", -] - [[package]] name = "termtree" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95059e91184749cb66be6dc994f67f182b6d897cb3df74a5bf66b5e709295fd8" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "thiserror" -version = "1.0.37" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.37" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2", + "quote", + "syn 2.0.22", ] [[package]] name = "time" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" dependencies = [ "libc", "wasi 0.10.0+wasi-snapshot-preview1", "winapi", ] +[[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 = "to-syn-value" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45dcb7b4108a4793bdd74aa3714296c6eaf43663edf73fa8625d0d7621e68447" dependencies = [ - "syn 1.0.103", + "syn 1.0.109", "to-syn-value_derive", ] @@ -2292,47 +2437,47 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd4fdec6de01b568c1d3721c9d46a352623c536cd55a8a5acfefb63d1fccccbc" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] name = "tokio" -version = "1.21.2" +version = "1.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" +checksum = "374442f06ee49c3a28a8fc9f01a2596fed7559c6b99b31279c3261778e77d84f" dependencies = [ - "autocfg 1.1.0", + "autocfg", + "backtrace", "bytes", "libc", - "memchr", - "mio 0.8.5", + "mio 0.8.8", "num_cpus", "parking_lot 0.12.1", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", - "winapi", + "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" -version = "1.8.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2", + "quote", + "syn 2.0.22", ] [[package]] name = "tokio-native-tls" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ "native-tls", "tokio", @@ -2340,9 +2485,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.4" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" dependencies = [ "bytes", "futures-core", @@ -2371,36 +2516,51 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", ] [[package]] name = "try-lock" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "typenum" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.5" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" + +[[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.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" [[package]] name = "unicode-width" @@ -2408,18 +2568,23 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" -[[package]] -name = "unicode-xid" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" - [[package]] name = "untrusted" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "url" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + [[package]] name = "utf-8" version = "0.7.6" @@ -2428,9 +2593,9 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] name = "utf8parse" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "vcpkg" @@ -2455,22 +2620,20 @@ dependencies = [ [[package]] name = "walkdir" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" dependencies = [ "same-file", - "winapi", "winapi-util", ] [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] @@ -2488,9 +2651,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2498,53 +2661,65 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2", + "quote", + "syn 2.0.22", "wasm-bindgen-shared", ] [[package]] -name = "wasm-bindgen-macro" -version = "0.2.83" +name = "wasm-bindgen-futures" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" dependencies = [ - "quote 1.0.21", + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2", + "quote", + "syn 2.0.22", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "web-sys" -version = "0.3.60" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", @@ -2582,16 +2757,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows-sys" -version = "0.36.1" +name = "windows" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows_aarch64_msvc 0.36.1", - "windows_i686_gnu 0.36.1", - "windows_i686_msvc 0.36.1", - "windows_x86_64_gnu 0.36.1", - "windows_x86_64_msvc 0.36.1", + "windows-targets", ] [[package]] @@ -2600,86 +2771,151 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc 0.42.0", - "windows_i686_gnu 0.42.0", - "windows_i686_msvc 0.42.0", - "windows_x86_64_gnu 0.42.0", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc 0.42.0", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[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.0" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[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.36.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_aarch64_msvc" -version = "0.42.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_i686_gnu" -version = "0.36.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" -version = "0.42.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_msvc" -version = "0.36.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" -version = "0.42.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_x86_64_gnu" -version = "0.36.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" -version = "0.42.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.0" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[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.36.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" -version = "0.42.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +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 = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "xml5ever" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4034e1d05af98b51ad7214527730626f019682d797ba38b51689212118d8e650" +dependencies = [ + "log", + "mac", + "markup5ever", +] [[package]] name = "xmlparser" diff --git a/Cargo.toml b/Cargo.toml index c64b7510..40b66623 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,10 +10,9 @@ license = "BSD-3-Clause" keywords = ["prolog", "prolog-interpreter", "prolog-system"] categories = ["command-line-utilities"] build = "build/main.rs" -rust-version = "1.61" +rust-version = "1.63" [features] -default = ["rug"] [build-dependencies] indexmap = "1.0.2" @@ -27,6 +26,8 @@ to-syn-value_derive = "0.1.0" walkdir = "2" [dependencies] +bit-set = "0.5.3" +bitvec = "1" cpu-time = "1.0.0" crossterm = "0.20.0" dirs-next = "2.0.0" @@ -41,35 +42,41 @@ libc = "0.2.62" modular-bitfield = "0.11.2" ctrlc = "3.2.2" ordered-float = "2.6.0" -phf = { version = "0.9", features = ["macros"] } +phf = { version = "0.9", features = ["macros"] } ref_thread_local = "0.0.0" -rug = { version = "1.15.0", optional = true } -rustyline = "9.0.0" +rustyline = "12.0.0" ring = "0.16.13" ripemd160 = "0.8.0" sha3 = "0.8.2" blake2 = "0.8.1" -crrl ="0.2.0" +crrl = "0.2.0" native-tls = "0.2.4" chrono = "0.4.11" -select = "0.4.3" +select = "0.6.0" roxmltree = "0.11.0" base64 = "0.12.3" smallvec = "1.8.0" sodiumoxide = "0.2.6" static_assertions = "1.1.0" ryu = "1.0.9" -hyper = { version = "0.14", features = ["full"] } -hyper-tls = "0.5.0" -tokio = { version = "1", features = ["full"] } +hyper = { version = "1.0.0-rc.3", features = ["full"] } +tokio = { version = "1.28.2", features = ["full"] } futures = "0.3" regex = "1.9.1" +libloading = "0.7" +derive_deref = "1.1.1" +http-body-util = "0.1.0-rc.2" +bytes = "1" +reqwest = { version = "0.11.18", features = ["blocking"] } +dashu = { git = "https://github.com/coasys/dashu.git" } +libffi = { git = "https://github.com/coasys/libffi-rs.git", branch = "windows-space" } +rand = "0.8.5" [dev-dependencies] assert_cmd = "1.0.3" predicates-core = "1.0.2" -serial_test = "0.5.1" maplit = "1.0.2" +serial_test = "2.0.0" [patch.crates-io] modular-bitfield = { git = "https://github.com/mthom/modular-bitfield" } diff --git a/Dockerfile b/Dockerfile index 501a2dce..0c1195ac 100755 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # See https://github.com/LukeMathWalker/cargo-chef -ARG RUST_VERSION=1.60-buster +ARG RUST_VERSION=1-buster FROM rust:${RUST_VERSION} as planner WORKDIR /scryer-prolog RUN cargo install cargo-chef diff --git a/INDEX.dj b/INDEX.dj new file mode 100644 index 00000000..c7cf6a11 --- /dev/null +++ b/INDEX.dj @@ -0,0 +1,75 @@ +# Scryer Prolog + +``` +?- append("Hello, ", X, "Hello, Scryer Prolog!"). + X = "Scryer Prolog!". +``` + +``` =html +
+

Scryer Prolog Meetup 2023

+

The first annual Scryer Prolog meetup is going to happen in Düsseldorf (Germany) on the 9th and 10th of November 2023. Join us to discover the present and future of Scryer Prolog! Participation is free, registration not required. More details here.

+
+``` + +![scryer](scryer.png){width=128 style=float:right;} [Scryer Prolog](https://github.com/mthom/scryer-prolog) is a free software ISO Prolog system intended to be an industrial +strength production environment *and* a testbed for bleeding edge research in +logic and constraint programming. + +Some of the Scryer Prolog features are: + +* ISO standard compliant +* Integrated constraint programming libraries: [clp(B)](/clpb.html), [clp(Z)](/clpz.html). +* [Definite Clause Grammars](/dcgs.html) +* Coroutining support ([`dif/2`](/dif.html), [`freeze/2`](/freeze.html), ...) +* [Tabling and SLG resolution](/tabling.html) +* Compact string representation +* Network libraries ([TCP sockets](/sockets.html), [HTTP server](/http/http_server.html), [HTTP client](/http/http_open.html), ...) +* [Cryptographical predicates](/crypto.html) +* WAM based engine, cross-platform made in Rust +* _and more..._ + +## What is Prolog? + +Prolog is a logic programming language created by [Alain Colmerauer](https://en.wikipedia.org/wiki/Alain_Colmerauer) and [Robert Kowalski](https://en.wikipedia.org/wiki/Robert_Kowalski) in 1972. +The idea behind Prolog is try to express a task in language similar to First Order Logic. +Prolog systems include _unification_ and _non-determinism_ as key concepts upon which we build programs. + +A Prolog program is made up of predicates which define a relation between its arguments. A predicate +is made from clauses. A clause can be either a fact or a rule. There's also a toplevel, which we +can use to ask and reason about our task. + +It's still to this day one of the best examples and one of the most popular languages in the field +of logic programming. That's because Prolog allows us to elegantly solve many tasks with short and +general programs. + +If you want a more detailed description of Prolog, check [A Tour of Prolog](https://www.youtube.com/watch?v=8XUutFBbUrg). + +If you want to learn more about Prolog history, check the videos [l'Aventure Prolog](https://www.youtube.com/watch?v=74Ig_QKndvE) and [50 years of Prolog and beyond](https://prologyear.logicprogramming.org/videos/PrologDay_Session_1_talk.mp4). + +## Where can I learn Prolog? + +There are a lot of classical Prolog books. Those books can teach you the basics of Prolog. Some +examples are: _The Art of Prolog (Shapiro)_, _Programming in Prolog (Clocksin, Mellish)_ and _The Craft +of Prolog (O'Keefe)_. However, most of them are not updated to _modern_ Prolog. +We recommend _[The Power of Prolog (Markus Triska)](https://www.metalevel.at/prolog)_ for modern Prolog. For reference about +the builtin Prolog modules and libraries in Scryer, check the documentation site. It's this! + +## Downloads + +The latest version of Scryer Prolog is *0.9.1*. And it's already useful for lots of tasks. + +Scryer Prolog can be compiled from source, instructions are on the [GitHub README](https://github.com/mthom/scryer-prolog). It runs on Linux, macOS and Windows. Other operating systems may work but they're not regularly tested. + +If you're in Linux, maybe your distribution already has an Scryer Prolog package. + +There's also a [Docker image](https://github.com/mthom/scryer-prolog#docker-install) available. + +## Support and discussions + +If Scryer Prolog crashes or yields unexpected errors, consider filing +an [issue](https://github.com/mthom/scryer-prolog/issues). + +To get in touch with the Scryer Prolog community, participate in +[discussions](https://github.com/mthom/scryer-prolog/discussions) +or visit our #scryer IRC channel on [Libera](https://libera.chat)! \ No newline at end of file diff --git a/README.md b/README.md index a6b2fbad..0f889c9b 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,17 @@ source industrial strength production environment that is also a testbed for bleeding edge research in logic and constraint programming, which is itself written in a high-level language. +As of July 2023, **Scryer Prolog passes all [syntactic conformity tests](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/conformity_testing)**. + +The homepage of the project is: [**https://www.scryer.pl**](https://www.scryer.pl) + ![Scryer Logo: Cryer](logo/scryer.png) ## Phase 1 Produce an implementation of the Warren Abstract Machine in Rust, done according to the progression of languages in [Warren's Abstract -Machine: A Tutorial -Reconstruction](http://wambook.sourceforge.net/wambook.pdf). +Machine: A Tutorial Reconstruction](https://github.com/mthom/scryer-prolog/blob/master/wambook/wambook.pdf). Phase 1 has been completed in that Scryer Prolog implements in some form all of the WAM book, including lists, cuts, Debray allocation, first @@ -44,7 +47,7 @@ Extend Scryer Prolog to include the following, among other features: - [x] Support for `attribute_goals/2` and `project_attributes/2` - [x] `call_residue_vars/2` - [x] `if_/3` and related predicates, following the developments of the - paper "Indexing `dif/2`". + paper "[Indexing `dif/2`](https://arxiv.org/abs/1607.01590)". - [x] All-solutions predicates (`findall/{3,4}`, `bagof/3`, `setof/3`, `forall/2`). - [x] Clause creation and destruction (`asserta/1`, `assertz/1`, `retract/1`, `abolish/1`) with logical update semantics. @@ -52,24 +55,24 @@ Extend Scryer Prolog to include the following, among other features: `bb_put/2` (non-backtrackable) and `bb_b_put/2` (backtrackable). - [x] Delimited continuations based on reset/3, shift/1 (documented in - "Delimited Continuations for Prolog"). + "[Delimited Continuations for Prolog](https://biblio.ugent.be/publication/5646080/file/5646081)"). - [x] Tabling library based on delimited continuations - (documented in "Tabling as a Library with Delimited Control"). + (documented in "[Tabling as a Library with Delimited Control](https://biblio.ugent.be/publication/6880648/file/6885145.pdf)"). - [x] A _redone_ representation of strings as difference lists of characters, using a packed internal representation. - [x] clp(B) and clp(ℤ) as builtin libraries. - [x] Streams and predicates for stream control. - - [x] A simple sockets library representing TCP connections as streams. + - [x] A simple sockets library representing TCP connections as streams. - [x] Incremental compilation and loading process, newly written, primarily in Prolog. - [ ] Improvements to the WAM compiler and heap representation: - [ ] Replacing choice points pivoting on inlined semi-deterministic predicates (`atom`, `var`, etc) with if/else ladders. (_in progress_) - [ ] Inlining all built-ins and system call instructions. - - [ ] Greatly reducing the number of instructions used to compile disjunctives. + - [x] Greatly reducing the number of instructions used to compile disjunctives. - [ ] Storing short atoms to heap cells without writing them to the atom table. - [ ] A compacting garbage collector satisfying the five properties of - "Precise Garbage Collection in Prolog." (_in progress_) + "[Precise Garbage Collection in Prolog](https://www.complang.tuwien.ac.at/ulrich/papers/PDF/2008-ciclops.pdf)." (_in progress_) - [ ] Mode declarations. ## Phase 3 @@ -88,12 +91,12 @@ nice to have in the future. They'd make a good project for anyone wanting to contribute code to Scryer Prolog. 1. Implement the global analysis techniques described in Peter van -Roy's thesis, "Can Logic Programming Execute as Fast as Imperative -Programming?" +Roy's thesis, "[Can Logic Programming Execute as Fast as Imperative +Programming?](https://www.info.ucl.ac.be/~pvr/Peter.thesis/Peter.thesis.html)" 2. Add unum representation and arithmetic, using either an existing unum implementation or an ad hoc one. Unums are described in -Gustafson's book "The End of Error." +Gustafson's book "[The End of Error](http://www.johngustafson.net/unums.html)." 3. Add concurrent tables to manage shared references to atoms and strings. @@ -114,19 +117,23 @@ distribution should be uninstalled from your system before rustup is used. Currently the only way to install the latest version of Scryer is to -clone directly from this git repository, which can be done as follows: +clone directly from this git repository, and compile the system. This +can be done as follows: ``` $> git clone https://github.com/mthom/scryer-prolog $> cd scryer-prolog -$> cargo run [--release] +$> cargo build --release ``` -The optional `--release` flag will perform various optimizations, -producing a faster executable. +The `--release` flag performs various optimizations, producing a +faster executable. + +After compilation, the executable `scryer-prolog` is available in the +directory `target/release` and can be invoked to run the system. On Windows, Scryer Prolog is easier to build inside a [MSYS2](https://www.msys2.org/) -environment as some crates may require native C compilation. However, +environment as some crates may require native C compilation. However, the resulting binary does not need MSYS2 to run. When executing Scryer in a shell, it is recommended to use a more advanced shell than mintty (the default MSYS2 shell). The [Windows Terminal](https://github.com/microsoft/terminal) works correctly. To build a Windows Installer, you'll need first Scryer Prolog compiled in release mode, then, with WiX Toolset installed, execute: @@ -136,7 +143,7 @@ light.exe scryer-prolog.wixobj ``` It will generate a very basic MSI file which installs the main executable and a shortcut in the Start Menu. It can be installed with a double-click. To uninstall, go to the Control Panel and uninstall as usual. -Scryer Prolog must be built with **Rust 1.57 and up**. +Scryer Prolog must be built with **Rust 1.63 and up**. ### Docker Install @@ -673,10 +680,32 @@ not need additional tools and formalisms for its application, and further, it encourages declarative reasoning that can in principle also be performed automatically. +## Applications + +Scryer Prolog's strong commitment to the Prolog ISO standard makes it +ideally suited for use in corporations and government agencies +that are subject to strict regulations pertaining to interoperability, +standards compliance and warranty. + +Successful existing applications of Scryer Prolog include the +[DocLog](https://github.com/aarroyoc/doclog) system which +generates Scryer's own documentation and homepage, [Symbolic +Analysis of Grants](https://www.brz.gv.at/en/BRZ-Tech-Blog/Tech-Blog-7-Symbolic-Analysis-of-Grants.html) +by the Austrian Federal Computing Center, and parts of the +[precautionary](https://github.com/dcnorris/precautionary/tree/main/exec/prolog) +package for the analysis of dose-escalation trials in the +safety-critical and highly regulated domain of oncology +trial design. + +Scryer Prolog is also very well suited for teaching and learning +Prolog, and for testing syntactic conformance and hence portability of +existing Prolog programs. + ## Support and discussions If Scryer Prolog crashes or yields unexpected errors, consider filing an [issue](https://github.com/mthom/scryer-prolog/issues). To get in touch with the Scryer Prolog community, participate in -[discussions](https://github.com/mthom/scryer-prolog/discussions)! +[discussions](https://github.com/mthom/scryer-prolog/discussions) +or visit our #scryer IRC channel on [Libera](https://libera.chat)! diff --git a/build/instructions_template.rs b/build/instructions_template.rs index bc1ca703..3ac76eff 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -101,8 +101,6 @@ enum BuiltInClauseType { Is(RegType, ArithmeticTerm), #[strum_discriminants(strum(props(Arity = "2", Name = "keysort")))] KeySort, - #[strum_discriminants(strum(props(Arity = "2", Name = "read")))] - Read, #[strum_discriminants(strum(props(Arity = "2", Name = "sort")))] Sort, } @@ -262,6 +260,8 @@ enum SystemClauseType { DeleteFile, #[strum_discriminants(strum(props(Arity = "2", Name = "$rename_file")))] RenameFile, + #[strum_discriminants(strum(props(Arity = "2", Name = "$file_copy")))] + FileCopy, #[strum_discriminants(strum(props(Arity = "2", Name = "$working_directory")))] WorkingDirectory, #[strum_discriminants(strum(props(Arity = "1", Name = "$delete_directory")))] @@ -270,16 +270,10 @@ enum SystemClauseType { PathCanonical, #[strum_discriminants(strum(props(Arity = "3", Name = "$file_time")))] FileTime, - #[strum_discriminants(strum(props(Arity = "1", Name = "$del_attr_non_head")))] - DeleteAttribute, - #[strum_discriminants(strum(props(Arity = "1", Name = "$del_attr_head")))] - DeleteHeadAttribute, #[strum_discriminants(strum(props(Arity = "arity", Name = "$module_call")))] DynamicModuleResolution(usize), #[strum_discriminants(strum(props(Arity = "arity", Name = "$prepare_call_clause")))] PrepareCallClause(usize), - #[strum_discriminants(strum(props(Arity = "1", Name = "$enqueue_attr_var")))] - EnqueueAttributedVar, #[strum_discriminants(strum(props(Arity = "2", Name = "$fetch_global_var")))] FetchGlobalVar, #[strum_discriminants(strum(props(Arity = "1", Name = "$first_stream")))] @@ -296,8 +290,6 @@ enum SystemClauseType { GetCode, #[strum_discriminants(strum(props(Arity = "1", Name = "$get_single_char")))] GetSingleChar, - #[strum_discriminants(strum(props(Arity = "0", Name = "$reset_attr_var_state")))] - ResetAttrVarState, #[strum_discriminants(strum(props(Arity = "2", Name = "$truncate_if_no_lh_growth_diff")))] TruncateIfNoLiftedHeapGrowthDiff, #[strum_discriminants(strum(props(Arity = "1", Name = "$truncate_if_no_lh_growth")))] @@ -312,10 +304,10 @@ enum SystemClauseType { GetBValue, #[strum_discriminants(strum(props(Arity = "3", Name = "$get_cont_chunk")))] GetContinuationChunk, - #[strum_discriminants(strum(props(Arity = "4", Name = "$get_next_db_ref")))] - GetNextDBRef, #[strum_discriminants(strum(props(Arity = "7", Name = "$get_next_op_db_ref")))] GetNextOpDBRef, + #[strum_discriminants(strum(props(Arity = "3", Name = "$lookup_db_ref")))] + LookupDBRef, #[strum_discriminants(strum(props(Arity = "1", Name = "$is_partial_string")))] IsPartialString, #[strum_discriminants(strum(props(Arity = "1", Name = "$halt")))] @@ -328,7 +320,7 @@ enum SystemClauseType { GetSCCCleaner, #[strum_discriminants(strum(props(Arity = "2", Name = "$head_is_dynamic")))] HeadIsDynamic, - #[strum_discriminants(strum(props(Arity = "2", Name = "$install_scc_cleaner")))] + #[strum_discriminants(strum(props(Arity = "1", Name = "$install_scc_cleaner")))] InstallSCCCleaner, #[strum_discriminants(strum(props(Arity = "3", Name = "$install_inference_counter")))] InstallInferenceCounter, @@ -410,12 +402,14 @@ enum SystemClauseType { GetBall, #[strum_discriminants(strum(props(Arity = "1", Name = "$get_current_block")))] GetCurrentBlock, + #[strum_discriminants(strum(props(Arity = "1", Name = "$get_current_scc_block")))] + GetCurrentSCCBlock, #[strum_discriminants(strum(props(Arity = "1", Name = "$get_cp")))] GetCutPoint, - #[strum_discriminants(strum(props(Arity = "1", Name = "$get_staggered_cp")))] - GetStaggeredCutPoint, #[strum_discriminants(strum(props(Arity = "1", Name = "$get_double_quotes")))] GetDoubleQuotes, + #[strum_discriminants(strum(props(Arity = "1", Name = "$get_unknown")))] + GetUnknown, #[strum_discriminants(strum(props(Arity = "1", Name = "$install_new_block")))] InstallNewBlock, #[strum_discriminants(strum(props(Arity = "0", Name = "$maybe")))] @@ -424,10 +418,14 @@ enum SystemClauseType { CurrentTime, #[strum_discriminants(strum(props(Arity = "1", Name = "$quoted_token")))] QuotedToken, - #[strum_discriminants(strum(props(Arity = "2", Name = "$read_term_from_chars")))] + #[strum_discriminants(strum(props(Arity = "2", Name = "$read_from_chars")))] + ReadFromChars, + #[strum_discriminants(strum(props(Arity = "5", Name = "$read_term_from_chars")))] ReadTermFromChars, #[strum_discriminants(strum(props(Arity = "1", Name = "$reset_block")))] ResetBlock, + #[strum_discriminants(strum(props(Arity = "1", Name = "$reset_scc_block")))] + ResetSCCBlock, #[strum_discriminants(strum(props(Arity = "0", Name = "$return_from_verify_attr")))] ReturnFromVerifyAttr, #[strum_discriminants(strum(props(Arity = "1", Name = "$set_ball")))] @@ -442,6 +440,8 @@ enum SystemClauseType { SetCutPointByDefault(RegType), #[strum_discriminants(strum(props(Arity = "1", Name = "$set_double_quotes")))] SetDoubleQuotes, + #[strum_discriminants(strum(props(Arity = "1", Name = "$set_unknown")))] + SetUnknown, #[strum_discriminants(strum(props(Arity = "1", Name = "$set_seed")))] SetSeed, #[strum_discriminants(strum(props(Arity = "4", Name = "$skip_max_list")))] @@ -478,9 +478,11 @@ enum SystemClauseType { UnwindStack, #[strum_discriminants(strum(props(Arity = "4", Name = "$wam_instructions")))] WAMInstructions, - #[strum_discriminants(strum(props(Arity = "7", Name = "$write_term")))] + #[strum_discriminants(strum(props(Arity = "2", Name = "$inlined_instructions")))] + InlinedInstructions, + #[strum_discriminants(strum(props(Arity = "8", Name = "$write_term")))] WriteTerm, - #[strum_discriminants(strum(props(Arity = "7", Name = "$write_term_to_chars")))] + #[strum_discriminants(strum(props(Arity = "8", Name = "$write_term_to_chars")))] WriteTermToChars, #[strum_discriminants(strum(props(Arity = "1", Name = "$scryer_prolog_version")))] ScryerPrologVersion, @@ -554,16 +556,40 @@ enum SystemClauseType { HttpAccept, #[strum_discriminants(strum(props(Arity = "4", Name = "$http_answer")))] HttpAnswer, + #[strum_discriminants(strum(props(Arity = "2", Name = "$load_foreign_lib")))] + LoadForeignLib, + #[strum_discriminants(strum(props(Arity = "3", Name = "$foreign_call")))] + ForeignCall, + #[strum_discriminants(strum(props(Arity = "2", Name = "$define_foreign_struct")))] + DefineForeignStruct, #[strum_discriminants(strum(props(Arity = "3", Name = "$predicate_defined")))] PredicateDefined, #[strum_discriminants(strum(props(Arity = "3", Name = "$strip_module")))] StripModule, #[strum_discriminants(strum(props(Arity = "4", Name = "$compile_inline_or_expanded_goal")))] CompileInlineOrExpandedGoal, - #[strum_discriminants(strum(props(Arity = "arity", Name = "$call_inline")))] - InlineCallN(usize), + #[strum_discriminants(strum(props(Arity = "arity", Name = "$fast_call")))] + FastCallN(usize), #[strum_discriminants(strum(props(Arity = "1", Name = "$is_expanded_or_inlined")))] IsExpandedOrInlined, + #[strum_discriminants(strum(props(Arity = "3", Name = "$get_clause_p")))] + GetClauseP, + #[strum_discriminants(strum(props(Arity = "6", Name = "$invoke_clause_at_p")))] + InvokeClauseAtP, + #[strum_discriminants(strum(props(Arity = "3", Name = "$get_from_attr_list")))] + GetFromAttributedVarList, + #[strum_discriminants(strum(props(Arity = "3", Name = "$put_to_attr_list")))] + PutToAttributedVarList, + #[strum_discriminants(strum(props(Arity = "3", Name = "$del_from_attr_list")))] + DeleteFromAttributedVarList, + #[strum_discriminants(strum(props(Arity = "1", Name = "$delete_all_attributes_from_var")))] + DeleteAllAttributesFromVar, + #[strum_discriminants(strum(props(Arity = "1", Name = "$unattributed_var")))] + UnattributedVar, + #[strum_discriminants(strum(props(Arity = "4", Name = "$get_db_refs")))] + GetDBRefs, + #[strum_discriminants(strum(props(Arity = "2", Name = "$keysort_with_constant_var_ordering")))] + KeySortWithConstantVarOrdering, REPL(REPLCodePtr), } @@ -578,7 +604,7 @@ enum InstructionTemplate { #[strum_discriminants(strum(props(Arity = "4", Name = "get_partial_string")))] GetPartialString(Level, Atom, RegType, bool), #[strum_discriminants(strum(props(Arity = "3", Name = "get_structure")))] - GetStructure(Atom, usize, RegType), + GetStructure(Level, Atom, usize, RegType), #[strum_discriminants(strum(props(Arity = "2", Name = "get_variable")))] GetVariable(RegType, usize), #[strum_discriminants(strum(props(Arity = "2", Name = "get_value")))] @@ -623,8 +649,10 @@ enum InstructionTemplate { Cut(RegType), #[strum_discriminants(strum(props(Arity = "1", Name = "get_level")))] GetLevel(RegType), - #[strum_discriminants(strum(props(Arity = "1", Name = "get_level_and_unify")))] - GetLevelAndUnify(RegType), + #[strum_discriminants(strum(props(Arity = "1", Name = "get_prev_level")))] + GetPrevLevel(RegType), + #[strum_discriminants(strum(props(Arity = "1", Name = "get_cut_point")))] + GetCutPoint(RegType), #[strum_discriminants(strum(props(Arity = "0", Name = "neck_cut")))] NeckCut, // choice instruction @@ -715,10 +743,28 @@ enum InstructionTemplate { Ceiling(ArithmeticTerm, usize), #[strum_discriminants(strum(props(Arity = "1", Name = "floor")))] Floor(ArithmeticTerm, usize), + #[strum_discriminants(strum(props(Arity = "1", Name = "float_fractional_part")))] + FloatFractionalPart(ArithmeticTerm, usize), + #[strum_discriminants(strum(props(Arity = "1", Name = "float_integer_part")))] + FloatIntegerPart(ArithmeticTerm, usize), #[strum_discriminants(strum(props(Arity = "1", Name = "neg")))] Neg(ArithmeticTerm, usize), #[strum_discriminants(strum(props(Arity = "1", Name = "plus")))] Plus(ArithmeticTerm, usize), + #[strum_discriminants(strum(props(Arity = "1", Name = "acosh")))] + ACosh(ArithmeticTerm, usize), + #[strum_discriminants(strum(props(Arity = "1", Name = "asinh")))] + ASinh(ArithmeticTerm, usize), + #[strum_discriminants(strum(props(Arity = "1", Name = "atanh")))] + ATanh(ArithmeticTerm, usize), + #[strum_discriminants(strum(props(Arity = "1", Name = "cosh")))] + Cosh(ArithmeticTerm, usize), + #[strum_discriminants(strum(props(Arity = "1", Name = "sinh")))] + Sinh(ArithmeticTerm, usize), + #[strum_discriminants(strum(props(Arity = "1", Name = "tanh")))] + Tanh(ArithmeticTerm, usize), + #[strum_discriminants(strum(props(Arity = "1", Name = "log10")))] + Log10(ArithmeticTerm, usize), #[strum_discriminants(strum(props(Arity = "1", Name = "bitwise_complement")))] BitwiseComplement(ArithmeticTerm, usize), // control instructions @@ -726,10 +772,8 @@ enum InstructionTemplate { Allocate(usize), // num_frames. #[strum_discriminants(strum(props(Arity = "0", Name = "deallocate")))] Deallocate, - #[strum_discriminants(strum(props(Arity = "3", Name = "jmp_by_call")))] - JmpByCall(usize, usize), // arity, relative offset. - #[strum_discriminants(strum(props(Arity = "3", Name = "jmp_by_execute")))] - JmpByExecute(usize, usize), // arity, relative offset. + #[strum_discriminants(strum(props(Arity = "1", Name = "jmp_by_call")))] + JmpByCall(usize), // relative offset. #[strum_discriminants(strum(props(Arity = "1", Name = "rev_jmp_by")))] RevJmpBy(usize), #[strum_discriminants(strum(props(Arity = "0", Name = "proceed")))] @@ -1100,6 +1144,7 @@ fn generate_instruction_preface() -> TokenStream { } pub type Code = Vec; + pub type CodeDeque = VecDeque; impl Instruction { #[inline] @@ -1282,9 +1327,13 @@ fn generate_instruction_preface() -> TokenStream { let rt_stub = reg_type_into_functor(r); functor!(atom!("get_level"), [str(h, 0)], [rt_stub]) } - &Instruction::GetLevelAndUnify(r) => { + &Instruction::GetPrevLevel(r) => { let rt_stub = reg_type_into_functor(r); - functor!(atom!("get_level_and_unify"), [str(h, 0)], [rt_stub]) + functor!(atom!("get_prev_level"), [str(h, 0)], [rt_stub]) + } + &Instruction::GetCutPoint(r) => { + let rt_stub = reg_type_into_functor(r); + functor!(atom!("get_cut_point"), [str(h, 0)], [rt_stub]) } &Instruction::NeckCut => { functor!(atom!("neck_cut")) @@ -1376,9 +1425,30 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::ATan(ref at, t) => { arith_instr_unary_functor(h, atom!("atan"), arena, at, t) } + &Instruction::ACosh(ref at, t) => { + arith_instr_unary_functor(h, atom!("acosh"), arena, at, t) + } + &Instruction::ASinh(ref at, t) => { + arith_instr_unary_functor(h, atom!("asinh"), arena, at, t) + } + &Instruction::ATanh(ref at, t) => { + arith_instr_unary_functor(h, atom!("atanh"), arena, at, t) + } + &Instruction::Cosh(ref at, t) => { + arith_instr_unary_functor(h, atom!("cosh"), arena, at, t) + } + &Instruction::Sinh(ref at, t) => { + arith_instr_unary_functor(h, atom!("sinh"), arena, at, t) + } + &Instruction::Tanh(ref at, t) => { + arith_instr_unary_functor(h, atom!("tanh"), arena, at, t) + } &Instruction::Sqrt(ref at, t) => { arith_instr_unary_functor(h, atom!("sqrt"), arena, at, t) } + &Instruction::Log10(ref at, t) => { + arith_instr_unary_functor(h, atom!("log10"), arena, at, t) + } &Instruction::Abs(ref at, t) => { arith_instr_unary_functor(h, atom!("abs"), arena, at, t) } @@ -1397,6 +1467,12 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::Floor(ref at, t) => { arith_instr_unary_functor(h, atom!("floor"), arena, at, t) } + &Instruction::FloatFractionalPart(ref at, t) => { + arith_instr_unary_functor(h, atom!("float_fractional_part"), arena, at, t) + } + &Instruction::FloatIntegerPart(ref at, t) => { + arith_instr_unary_functor(h, atom!("float_integer_part"), arena, at, t) + } &Instruction::Neg(ref at, t) => arith_instr_unary_functor( h, atom!("-"), @@ -1439,30 +1515,30 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::DefaultExecuteNamed(arity, name, ..) => { functor!(atom!("execute_default"), [atom(name), fixnum(arity)]) } - &Instruction::CallN(arity, _) => { + &Instruction::CallN(arity) => { functor!(atom!("call_n"), [fixnum(arity)]) } - &Instruction::ExecuteN(arity, _) => { + &Instruction::ExecuteN(arity) => { functor!(atom!("execute_n"), [fixnum(arity)]) } - &Instruction::DefaultCallN(arity, _) => { + &Instruction::DefaultCallN(arity) => { functor!(atom!("call_default_n"), [fixnum(arity)]) } - &Instruction::DefaultExecuteN(arity, _) => { + &Instruction::DefaultExecuteN(arity) => { functor!(atom!("execute_default_n"), [fixnum(arity)]) } - &Instruction::CallInlineCallN(arity, _) => { - functor!(atom!("call_n_inline"), [fixnum(arity)]) + &Instruction::CallFastCallN(arity) => { + functor!(atom!("call_fast_call_n"), [fixnum(arity)]) } - &Instruction::ExecuteInlineCallN(arity, _) => { - functor!(atom!("call_n_inline"), [fixnum(arity)]) + &Instruction::ExecuteFastCallN(arity) => { + functor!(atom!("execute_fast_call_n"), [fixnum(arity)]) } - &Instruction::CallTermGreaterThan(_) | - &Instruction::CallTermLessThan(_) | - &Instruction::CallTermGreaterThanOrEqual(_) | - &Instruction::CallTermLessThanOrEqual(_) | - &Instruction::CallTermEqual(_) | - &Instruction::CallTermNotEqual(_) | + &Instruction::CallTermGreaterThan | + &Instruction::CallTermLessThan | + &Instruction::CallTermGreaterThanOrEqual | + &Instruction::CallTermLessThanOrEqual | + &Instruction::CallTermEqual | + &Instruction::CallTermNotEqual | &Instruction::CallNumberGreaterThan(..) | &Instruction::CallNumberLessThan(..) | &Instruction::CallNumberGreaterThanOrEqual(..) | @@ -1470,545 +1546,569 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::CallNumberEqual(..) | &Instruction::CallNumberNotEqual(..) | &Instruction::CallIs(..) | - &Instruction::CallAcyclicTerm(_) | - &Instruction::CallArg(_) | - &Instruction::CallCompare(_) | - &Instruction::CallCopyTerm(_) | - &Instruction::CallFunctor(_) | - &Instruction::CallGround(_) | - &Instruction::CallKeySort(_) | - &Instruction::CallRead(_) | - &Instruction::CallSort(_) => { + &Instruction::CallAcyclicTerm | + &Instruction::CallArg | + &Instruction::CallCompare | + &Instruction::CallCopyTerm | + &Instruction::CallFunctor | + &Instruction::CallGround | + &Instruction::CallKeySort | + &Instruction::CallSort => { let (name, arity) = self.to_name_and_arity(); functor!(atom!("call"), [atom(name), fixnum(arity)]) } // - &Instruction::ExecuteTermGreaterThan(_) | - &Instruction::ExecuteTermLessThan(_) | - &Instruction::ExecuteTermGreaterThanOrEqual(_) | - &Instruction::ExecuteTermLessThanOrEqual(_) | - &Instruction::ExecuteTermEqual(_) | - &Instruction::ExecuteTermNotEqual(_) | + &Instruction::ExecuteTermGreaterThan | + &Instruction::ExecuteTermLessThan | + &Instruction::ExecuteTermGreaterThanOrEqual | + &Instruction::ExecuteTermLessThanOrEqual | + &Instruction::ExecuteTermEqual | + &Instruction::ExecuteTermNotEqual | &Instruction::ExecuteNumberGreaterThan(..) | &Instruction::ExecuteNumberLessThan(..) | &Instruction::ExecuteNumberGreaterThanOrEqual(..) | &Instruction::ExecuteNumberLessThanOrEqual(..) | &Instruction::ExecuteNumberEqual(..) | &Instruction::ExecuteNumberNotEqual(..) | - &Instruction::ExecuteAcyclicTerm(_) | - &Instruction::ExecuteArg(_) | - &Instruction::ExecuteCompare(_) | - &Instruction::ExecuteCopyTerm(_) | - &Instruction::ExecuteFunctor(_) | - &Instruction::ExecuteGround(_) | + &Instruction::ExecuteAcyclicTerm | + &Instruction::ExecuteArg | + &Instruction::ExecuteCompare | + &Instruction::ExecuteCopyTerm | + &Instruction::ExecuteFunctor | + &Instruction::ExecuteGround | &Instruction::ExecuteIs(..) | - &Instruction::ExecuteKeySort(_) | - &Instruction::ExecuteRead(_) | - &Instruction::ExecuteSort(_) => { + &Instruction::ExecuteKeySort | + &Instruction::ExecuteSort => { let (name, arity) = self.to_name_and_arity(); functor!(atom!("execute"), [atom(name), fixnum(arity)]) } // - &Instruction::DefaultCallTermGreaterThan(_) | - &Instruction::DefaultCallTermLessThan(_) | - &Instruction::DefaultCallTermGreaterThanOrEqual(_) | - &Instruction::DefaultCallTermLessThanOrEqual(_) | - &Instruction::DefaultCallTermEqual(_) | - &Instruction::DefaultCallTermNotEqual(_) | + &Instruction::DefaultCallTermGreaterThan | + &Instruction::DefaultCallTermLessThan | + &Instruction::DefaultCallTermGreaterThanOrEqual | + &Instruction::DefaultCallTermLessThanOrEqual | + &Instruction::DefaultCallTermEqual | + &Instruction::DefaultCallTermNotEqual | &Instruction::DefaultCallNumberGreaterThan(..) | &Instruction::DefaultCallNumberLessThan(..) | &Instruction::DefaultCallNumberGreaterThanOrEqual(..) | &Instruction::DefaultCallNumberLessThanOrEqual(..) | &Instruction::DefaultCallNumberEqual(..) | &Instruction::DefaultCallNumberNotEqual(..) | - &Instruction::DefaultCallAcyclicTerm(_) | - &Instruction::DefaultCallArg(_) | - &Instruction::DefaultCallCompare(_) | - &Instruction::DefaultCallCopyTerm(_) | - &Instruction::DefaultCallFunctor(_) | - &Instruction::DefaultCallGround(_) | + &Instruction::DefaultCallAcyclicTerm | + &Instruction::DefaultCallArg | + &Instruction::DefaultCallCompare | + &Instruction::DefaultCallCopyTerm | + &Instruction::DefaultCallFunctor | + &Instruction::DefaultCallGround | &Instruction::DefaultCallIs(..) | - &Instruction::DefaultCallKeySort(_) | - &Instruction::DefaultCallRead(_) | - &Instruction::DefaultCallSort(_) => { + &Instruction::DefaultCallKeySort | + &Instruction::DefaultCallSort => { let (name, arity) = self.to_name_and_arity(); functor!(atom!("call_default"), [atom(name), fixnum(arity)]) } // - &Instruction::DefaultExecuteTermGreaterThan(_) | - &Instruction::DefaultExecuteTermLessThan(_) | - &Instruction::DefaultExecuteTermGreaterThanOrEqual(_) | - &Instruction::DefaultExecuteTermLessThanOrEqual(_) | - &Instruction::DefaultExecuteTermEqual(_) | - &Instruction::DefaultExecuteTermNotEqual(_) | + &Instruction::DefaultExecuteTermGreaterThan | + &Instruction::DefaultExecuteTermLessThan | + &Instruction::DefaultExecuteTermGreaterThanOrEqual | + &Instruction::DefaultExecuteTermLessThanOrEqual | + &Instruction::DefaultExecuteTermEqual | + &Instruction::DefaultExecuteTermNotEqual | &Instruction::DefaultExecuteNumberGreaterThan(..) | &Instruction::DefaultExecuteNumberLessThan(..) | &Instruction::DefaultExecuteNumberGreaterThanOrEqual(..) | &Instruction::DefaultExecuteNumberLessThanOrEqual(..) | &Instruction::DefaultExecuteNumberEqual(..) | &Instruction::DefaultExecuteNumberNotEqual(..) | - &Instruction::DefaultExecuteAcyclicTerm(_) | - &Instruction::DefaultExecuteArg(_) | - &Instruction::DefaultExecuteCompare(_) | - &Instruction::DefaultExecuteCopyTerm(_) | - &Instruction::DefaultExecuteFunctor(_) | - &Instruction::DefaultExecuteGround(_) | + &Instruction::DefaultExecuteAcyclicTerm | + &Instruction::DefaultExecuteArg | + &Instruction::DefaultExecuteCompare | + &Instruction::DefaultExecuteCopyTerm | + &Instruction::DefaultExecuteFunctor | + &Instruction::DefaultExecuteGround | &Instruction::DefaultExecuteIs(..) | - &Instruction::DefaultExecuteKeySort(_) | - &Instruction::DefaultExecuteRead(_) | - &Instruction::DefaultExecuteSort(_) => { + &Instruction::DefaultExecuteKeySort | + &Instruction::DefaultExecuteSort => { let (name, arity) = self.to_name_and_arity(); functor!(atom!("execute_default"), [atom(name), fixnum(arity)]) } - &Instruction::CallIsAtom(_, _) | - &Instruction::CallIsAtomic(_, _) | - &Instruction::CallIsCompound(_, _) | - &Instruction::CallIsInteger(_, _) | - &Instruction::CallIsNumber(_, _) | - &Instruction::CallIsRational(_, _) | - &Instruction::CallIsFloat(_, _) | - &Instruction::CallIsNonVar(_, _) | - &Instruction::CallIsVar(_, _) => { + &Instruction::CallIsAtom(_) | + &Instruction::CallIsAtomic(_) | + &Instruction::CallIsCompound(_) | + &Instruction::CallIsInteger(_) | + &Instruction::CallIsNumber(_) | + &Instruction::CallIsRational(_) | + &Instruction::CallIsFloat(_) | + &Instruction::CallIsNonVar(_) | + &Instruction::CallIsVar(_) => { let (name, arity) = self.to_name_and_arity(); functor!(atom!("call"), [atom(name), fixnum(arity)]) } - &Instruction::ExecuteIsAtom(_, _) | - &Instruction::ExecuteIsAtomic(_, _) | - &Instruction::ExecuteIsCompound(_, _) | - &Instruction::ExecuteIsInteger(_, _) | - &Instruction::ExecuteIsNumber(_, _) | - &Instruction::ExecuteIsRational(_, _) | - &Instruction::ExecuteIsFloat(_, _) | - &Instruction::ExecuteIsNonVar(_, _) | - &Instruction::ExecuteIsVar(_, _) => { + &Instruction::ExecuteIsAtom(_) | + &Instruction::ExecuteIsAtomic(_) | + &Instruction::ExecuteIsCompound(_) | + &Instruction::ExecuteIsInteger(_) | + &Instruction::ExecuteIsNumber(_) | + &Instruction::ExecuteIsRational(_) | + &Instruction::ExecuteIsFloat(_) | + &Instruction::ExecuteIsNonVar(_) | + &Instruction::ExecuteIsVar(_) => { let (name, arity) = self.to_name_and_arity(); functor!(atom!("execute"), [atom(name), fixnum(arity)]) } // - &Instruction::CallAtomChars(_) | - &Instruction::CallAtomCodes(_) | - &Instruction::CallAtomLength(_) | - &Instruction::CallBindFromRegister(_) | - &Instruction::CallContinuation(_) | - &Instruction::CallCharCode(_) | - &Instruction::CallCharType(_) | - &Instruction::CallCharsToNumber(_) | - &Instruction::CallCodesToNumber(_) | - &Instruction::CallCopyTermWithoutAttrVars(_) | - &Instruction::CallCheckCutPoint(_) | - &Instruction::CallClose(_) | - &Instruction::CallCopyToLiftedHeap(_) | - &Instruction::CallCreatePartialString(_) | - &Instruction::CallCurrentHostname(_) | - &Instruction::CallCurrentInput(_) | - &Instruction::CallCurrentOutput(_) | - &Instruction::CallDirectoryFiles(_) | - &Instruction::CallFileSize(_) | - &Instruction::CallFileExists(_) | - &Instruction::CallDirectoryExists(_) | - &Instruction::CallDirectorySeparator(_) | - &Instruction::CallMakeDirectory(_) | - &Instruction::CallMakeDirectoryPath(_) | - &Instruction::CallDeleteFile(_) | - &Instruction::CallRenameFile(_) | - &Instruction::CallWorkingDirectory(_) | - &Instruction::CallDeleteDirectory(_) | - &Instruction::CallPathCanonical(_) | - &Instruction::CallFileTime(_) | - &Instruction::CallDeleteAttribute(_) | - &Instruction::CallDeleteHeadAttribute(_) | + &Instruction::CallAtomChars | + &Instruction::CallAtomCodes | + &Instruction::CallAtomLength | + &Instruction::CallBindFromRegister | + &Instruction::CallContinuation | + &Instruction::CallCharCode | + &Instruction::CallCharType | + &Instruction::CallCharsToNumber | + &Instruction::CallCodesToNumber | + &Instruction::CallCopyTermWithoutAttrVars | + &Instruction::CallCheckCutPoint | + &Instruction::CallClose | + &Instruction::CallCopyToLiftedHeap | + &Instruction::CallCreatePartialString | + &Instruction::CallCurrentHostname | + &Instruction::CallCurrentInput | + &Instruction::CallCurrentOutput | + &Instruction::CallDirectoryFiles | + &Instruction::CallFileSize | + &Instruction::CallFileExists | + &Instruction::CallDirectoryExists | + &Instruction::CallDirectorySeparator | + &Instruction::CallMakeDirectory | + &Instruction::CallMakeDirectoryPath | + &Instruction::CallDeleteFile | + &Instruction::CallRenameFile | + &Instruction::CallFileCopy | + &Instruction::CallWorkingDirectory | + &Instruction::CallDeleteDirectory | + &Instruction::CallPathCanonical | + &Instruction::CallFileTime | &Instruction::CallDynamicModuleResolution(..) | &Instruction::CallPrepareCallClause(..) | - &Instruction::CallCompileInlineOrExpandedGoal(..) | - &Instruction::CallIsExpandedOrInlined(_) | - &Instruction::CallEnqueueAttributedVar(_) | - &Instruction::CallFetchGlobalVar(_) | - &Instruction::CallFirstStream(_) | - &Instruction::CallFlushOutput(_) | - &Instruction::CallGetByte(_) | - &Instruction::CallGetChar(_) | - &Instruction::CallGetNChars(_) | - &Instruction::CallGetCode(_) | - &Instruction::CallGetSingleChar(_) | - &Instruction::CallResetAttrVarState(_) | - &Instruction::CallTruncateIfNoLiftedHeapGrowthDiff(_) | - &Instruction::CallTruncateIfNoLiftedHeapGrowth(_) | - &Instruction::CallGetAttributedVariableList(_) | - &Instruction::CallGetAttrVarQueueDelimiter(_) | - &Instruction::CallGetAttrVarQueueBeyond(_) | - &Instruction::CallGetBValue(_) | - &Instruction::CallGetContinuationChunk(_) | - &Instruction::CallGetNextDBRef(_) | - &Instruction::CallGetNextOpDBRef(_) | - &Instruction::CallIsPartialString(_) | - &Instruction::CallHalt(_) | - &Instruction::CallGetLiftedHeapFromOffset(_) | - &Instruction::CallGetLiftedHeapFromOffsetDiff(_) | - &Instruction::CallGetSCCCleaner(_) | - &Instruction::CallHeadIsDynamic(_) | - &Instruction::CallInstallSCCCleaner(_) | - &Instruction::CallInstallInferenceCounter(_) | - &Instruction::CallLiftedHeapLength(_) | - &Instruction::CallLoadLibraryAsStream(_) | - &Instruction::CallModuleExists(_) | - &Instruction::CallNextEP(_) | - &Instruction::CallNoSuchPredicate(_) | - &Instruction::CallNumberToChars(_) | - &Instruction::CallNumberToCodes(_) | - &Instruction::CallOpDeclaration(_) | - &Instruction::CallOpen(_) | - &Instruction::CallSetStreamOptions(_) | - &Instruction::CallNextStream(_) | - &Instruction::CallPartialStringTail(_) | - &Instruction::CallPeekByte(_) | - &Instruction::CallPeekChar(_) | - &Instruction::CallPeekCode(_) | - &Instruction::CallPointsToContinuationResetMarker(_) | - &Instruction::CallPutByte(_) | - &Instruction::CallPutChar(_) | - &Instruction::CallPutChars(_) | - &Instruction::CallPutCode(_) | - &Instruction::CallReadQueryTerm(_) | - &Instruction::CallReadTerm(_) | - &Instruction::CallRedoAttrVarBinding(_) | - &Instruction::CallRemoveCallPolicyCheck(_) | - &Instruction::CallRemoveInferenceCounter(_) | - &Instruction::CallResetContinuationMarker(_) | - &Instruction::CallRestoreCutPolicy(_) | + &Instruction::CallCompileInlineOrExpandedGoal | + &Instruction::CallIsExpandedOrInlined | + &Instruction::CallGetClauseP | + &Instruction::CallInvokeClauseAtP | + &Instruction::CallGetFromAttributedVarList | + &Instruction::CallPutToAttributedVarList | + &Instruction::CallDeleteFromAttributedVarList | + &Instruction::CallDeleteAllAttributesFromVar | + &Instruction::CallUnattributedVar | + &Instruction::CallGetDBRefs | + &Instruction::CallKeySortWithConstantVarOrdering | + &Instruction::CallFetchGlobalVar | + &Instruction::CallFirstStream | + &Instruction::CallFlushOutput | + &Instruction::CallGetByte | + &Instruction::CallGetChar | + &Instruction::CallGetNChars | + &Instruction::CallGetCode | + &Instruction::CallGetSingleChar | + &Instruction::CallTruncateIfNoLiftedHeapGrowthDiff | + &Instruction::CallTruncateIfNoLiftedHeapGrowth | + &Instruction::CallGetAttributedVariableList | + &Instruction::CallGetAttrVarQueueDelimiter | + &Instruction::CallGetAttrVarQueueBeyond | + &Instruction::CallGetBValue | + &Instruction::CallGetContinuationChunk | + &Instruction::CallGetNextOpDBRef | + &Instruction::CallLookupDBRef | + &Instruction::CallIsPartialString | + &Instruction::CallHalt | + &Instruction::CallGetLiftedHeapFromOffset | + &Instruction::CallGetLiftedHeapFromOffsetDiff | + &Instruction::CallGetSCCCleaner | + &Instruction::CallHeadIsDynamic | + &Instruction::CallInstallSCCCleaner | + &Instruction::CallInstallInferenceCounter | + &Instruction::CallLiftedHeapLength | + &Instruction::CallLoadLibraryAsStream | + &Instruction::CallModuleExists | + &Instruction::CallNextEP | + &Instruction::CallNoSuchPredicate | + &Instruction::CallNumberToChars | + &Instruction::CallNumberToCodes | + &Instruction::CallOpDeclaration | + &Instruction::CallOpen | + &Instruction::CallSetStreamOptions | + &Instruction::CallNextStream | + &Instruction::CallPartialStringTail | + &Instruction::CallPeekByte | + &Instruction::CallPeekChar | + &Instruction::CallPeekCode | + &Instruction::CallPointsToContinuationResetMarker | + &Instruction::CallPutByte | + &Instruction::CallPutChar | + &Instruction::CallPutChars | + &Instruction::CallPutCode | + &Instruction::CallReadQueryTerm | + &Instruction::CallReadTerm | + &Instruction::CallRedoAttrVarBinding | + &Instruction::CallRemoveCallPolicyCheck | + &Instruction::CallRemoveInferenceCounter | + &Instruction::CallResetContinuationMarker | + &Instruction::CallRestoreCutPolicy | &Instruction::CallSetCutPoint(..) | - &Instruction::CallSetInput(_) | - &Instruction::CallSetOutput(_) | - &Instruction::CallStoreBacktrackableGlobalVar(_) | - &Instruction::CallStoreGlobalVar(_) | - &Instruction::CallStreamProperty(_) | - &Instruction::CallSetStreamPosition(_) | - &Instruction::CallInferenceLevel(_) | - &Instruction::CallCleanUpBlock(_) | - &Instruction::CallFail(_) | - &Instruction::CallGetBall(_) | - &Instruction::CallGetCurrentBlock(_) | - &Instruction::CallGetCutPoint(_) | - &Instruction::CallGetStaggeredCutPoint(_) | - &Instruction::CallGetDoubleQuotes(_) | - &Instruction::CallInstallNewBlock(_) | - &Instruction::CallMaybe(_) | - &Instruction::CallCpuNow(_) | - &Instruction::CallDeterministicLengthRundown(_) | - &Instruction::CallHttpOpen(_) | - &Instruction::CallHttpListen(_) | - &Instruction::CallHttpAccept(_) | - &Instruction::CallHttpAnswer(_) | - &Instruction::CallPredicateDefined(_) | - &Instruction::CallStripModule(_) | - &Instruction::CallCurrentTime(_) | - &Instruction::CallQuotedToken(_) | - &Instruction::CallReadTermFromChars(_) | - &Instruction::CallResetBlock(_) | - &Instruction::CallReturnFromVerifyAttr(_) | - &Instruction::CallSetBall(_) | - &Instruction::CallPushBallStack(_) | - &Instruction::CallPopBallStack(_) | - &Instruction::CallPopFromBallStack(_) | + &Instruction::CallSetInput | + &Instruction::CallSetOutput | + &Instruction::CallStoreBacktrackableGlobalVar | + &Instruction::CallStoreGlobalVar | + &Instruction::CallStreamProperty | + &Instruction::CallSetStreamPosition | + &Instruction::CallInferenceLevel | + &Instruction::CallCleanUpBlock | + &Instruction::CallFail | + &Instruction::CallGetBall | + &Instruction::CallGetCurrentBlock | + &Instruction::CallGetCurrentSCCBlock | + &Instruction::CallGetCutPoint | + &Instruction::CallGetDoubleQuotes | + &Instruction::CallGetUnknown | + &Instruction::CallInstallNewBlock | + &Instruction::CallMaybe | + &Instruction::CallCpuNow | + &Instruction::CallDeterministicLengthRundown | + &Instruction::CallHttpOpen | + &Instruction::CallHttpListen | + &Instruction::CallHttpAccept | + &Instruction::CallHttpAnswer | + &Instruction::CallLoadForeignLib | + &Instruction::CallForeignCall | + &Instruction::CallDefineForeignStruct | + &Instruction::CallPredicateDefined | + &Instruction::CallStripModule | + &Instruction::CallCurrentTime | + &Instruction::CallQuotedToken | + &Instruction::CallReadFromChars | + &Instruction::CallReadTermFromChars | + &Instruction::CallResetBlock | + &Instruction::CallResetSCCBlock | + &Instruction::CallReturnFromVerifyAttr | + &Instruction::CallSetBall | + &Instruction::CallPushBallStack | + &Instruction::CallPopBallStack | + &Instruction::CallPopFromBallStack | &Instruction::CallSetCutPointByDefault(..) | - &Instruction::CallSetDoubleQuotes(_) | - &Instruction::CallSetSeed(_) | - &Instruction::CallSkipMaxList(_) | - &Instruction::CallSleep(_) | - &Instruction::CallSocketClientOpen(_) | - &Instruction::CallSocketServerOpen(_) | - &Instruction::CallSocketServerAccept(_) | - &Instruction::CallSocketServerClose(_) | - &Instruction::CallTLSAcceptClient(_) | - &Instruction::CallTLSClientConnect(_) | - &Instruction::CallSucceed(_) | - &Instruction::CallTermAttributedVariables(_) | - &Instruction::CallTermVariables(_) | - &Instruction::CallTermVariablesUnderMaxDepth(_) | - &Instruction::CallTruncateLiftedHeapTo(_) | - &Instruction::CallUnifyWithOccursCheck(_) | - &Instruction::CallUnwindEnvironments(_) | - &Instruction::CallUnwindStack(_) | - &Instruction::CallWAMInstructions(_) | - &Instruction::CallWriteTerm(_) | - &Instruction::CallWriteTermToChars(_) | - &Instruction::CallScryerPrologVersion(_) | - &Instruction::CallCryptoRandomByte(_) | - &Instruction::CallCryptoDataHash(_) | - &Instruction::CallCryptoDataHKDF(_) | - &Instruction::CallCryptoPasswordHash(_) | - &Instruction::CallCryptoDataEncrypt(_) | - &Instruction::CallCryptoDataDecrypt(_) | - &Instruction::CallCryptoCurveScalarMult(_) | - &Instruction::CallEd25519Sign(_) | - &Instruction::CallEd25519Verify(_) | - &Instruction::CallEd25519NewKeyPair(_) | - &Instruction::CallEd25519KeyPairPublicKey(_) | - &Instruction::CallCurve25519ScalarMult(_) | - &Instruction::CallFirstNonOctet(_) | - &Instruction::CallLoadHTML(_) | - &Instruction::CallLoadXML(_) | - &Instruction::CallGetEnv(_) | - &Instruction::CallSetEnv(_) | - &Instruction::CallUnsetEnv(_) | - &Instruction::CallShell(_) | - &Instruction::CallPID(_) | - &Instruction::CallCharsBase64(_) | - &Instruction::CallDevourWhitespace(_) | - &Instruction::CallIsSTOEnabled(_) | - &Instruction::CallSetSTOAsUnify(_) | - &Instruction::CallSetNSTOAsUnify(_) | - &Instruction::CallSetSTOWithErrorAsUnify(_) | - &Instruction::CallHomeDirectory(_) | - &Instruction::CallDebugHook(_) | - &Instruction::CallAddDiscontiguousPredicate(_) | - &Instruction::CallAddDynamicPredicate(_) | - &Instruction::CallAddMultifilePredicate(_) | - &Instruction::CallAddGoalExpansionClause(_) | - &Instruction::CallAddTermExpansionClause(_) | - &Instruction::CallAddInSituFilenameModule(_) | - &Instruction::CallClauseToEvacuable(_) | - &Instruction::CallScopedClauseToEvacuable(_) | - &Instruction::CallConcludeLoad(_) | - &Instruction::CallDeclareModule(_) | - &Instruction::CallLoadCompiledLibrary(_) | - &Instruction::CallLoadContextSource(_) | - &Instruction::CallLoadContextFile(_) | - &Instruction::CallLoadContextDirectory(_) | - &Instruction::CallLoadContextModule(_) | - &Instruction::CallLoadContextStream(_) | - &Instruction::CallPopLoadContext(_) | - &Instruction::CallPopLoadStatePayload(_) | - &Instruction::CallPushLoadContext(_) | - &Instruction::CallPushLoadStatePayload(_) | - &Instruction::CallUseModule(_) | - &Instruction::CallBuiltInProperty(_) | - &Instruction::CallMetaPredicateProperty(_) | - &Instruction::CallMultifileProperty(_) | - &Instruction::CallDiscontiguousProperty(_) | - &Instruction::CallDynamicProperty(_) | - &Instruction::CallAbolishClause(_) | - &Instruction::CallAsserta(_) | - &Instruction::CallAssertz(_) | - &Instruction::CallRetract(_) | - &Instruction::CallIsConsistentWithTermQueue(_) | - &Instruction::CallFlushTermQueue(_) | - &Instruction::CallRemoveModuleExports(_) | - &Instruction::CallAddNonCountedBacktracking(_) | - &Instruction::CallPopCount(_) => { + &Instruction::CallSetDoubleQuotes | + &Instruction::CallSetUnknown | + &Instruction::CallSetSeed | + &Instruction::CallSkipMaxList | + &Instruction::CallSleep | + &Instruction::CallSocketClientOpen | + &Instruction::CallSocketServerOpen | + &Instruction::CallSocketServerAccept | + &Instruction::CallSocketServerClose | + &Instruction::CallTLSAcceptClient | + &Instruction::CallTLSClientConnect | + &Instruction::CallSucceed | + &Instruction::CallTermAttributedVariables | + &Instruction::CallTermVariables | + &Instruction::CallTermVariablesUnderMaxDepth | + &Instruction::CallTruncateLiftedHeapTo | + &Instruction::CallUnifyWithOccursCheck | + &Instruction::CallUnwindEnvironments | + &Instruction::CallUnwindStack | + &Instruction::CallWAMInstructions | + &Instruction::CallInlinedInstructions | + &Instruction::CallWriteTerm | + &Instruction::CallWriteTermToChars | + &Instruction::CallScryerPrologVersion | + &Instruction::CallCryptoRandomByte | + &Instruction::CallCryptoDataHash | + &Instruction::CallCryptoDataHKDF | + &Instruction::CallCryptoPasswordHash | + &Instruction::CallCryptoDataEncrypt | + &Instruction::CallCryptoDataDecrypt | + &Instruction::CallCryptoCurveScalarMult | + &Instruction::CallEd25519Sign | + &Instruction::CallEd25519Verify | + &Instruction::CallEd25519NewKeyPair | + &Instruction::CallEd25519KeyPairPublicKey | + &Instruction::CallCurve25519ScalarMult | + &Instruction::CallFirstNonOctet | + &Instruction::CallLoadHTML | + &Instruction::CallLoadXML | + &Instruction::CallGetEnv | + &Instruction::CallSetEnv | + &Instruction::CallUnsetEnv | + &Instruction::CallShell | + &Instruction::CallPID | + &Instruction::CallCharsBase64 | + &Instruction::CallDevourWhitespace | + &Instruction::CallIsSTOEnabled | + &Instruction::CallSetSTOAsUnify | + &Instruction::CallSetNSTOAsUnify | + &Instruction::CallSetSTOWithErrorAsUnify | + &Instruction::CallHomeDirectory | + &Instruction::CallDebugHook | + &Instruction::CallAddDiscontiguousPredicate | + &Instruction::CallAddDynamicPredicate | + &Instruction::CallAddMultifilePredicate | + &Instruction::CallAddGoalExpansionClause | + &Instruction::CallAddTermExpansionClause | + &Instruction::CallAddInSituFilenameModule | + &Instruction::CallClauseToEvacuable | + &Instruction::CallScopedClauseToEvacuable | + &Instruction::CallConcludeLoad | + &Instruction::CallDeclareModule | + &Instruction::CallLoadCompiledLibrary | + &Instruction::CallLoadContextSource | + &Instruction::CallLoadContextFile | + &Instruction::CallLoadContextDirectory | + &Instruction::CallLoadContextModule | + &Instruction::CallLoadContextStream | + &Instruction::CallPopLoadContext | + &Instruction::CallPopLoadStatePayload | + &Instruction::CallPushLoadContext | + &Instruction::CallPushLoadStatePayload | + &Instruction::CallUseModule | + &Instruction::CallBuiltInProperty | + &Instruction::CallMetaPredicateProperty | + &Instruction::CallMultifileProperty | + &Instruction::CallDiscontiguousProperty | + &Instruction::CallDynamicProperty | + &Instruction::CallAbolishClause | + &Instruction::CallAsserta | + &Instruction::CallAssertz | + &Instruction::CallRetract | + &Instruction::CallIsConsistentWithTermQueue | + &Instruction::CallFlushTermQueue | + &Instruction::CallRemoveModuleExports | + &Instruction::CallAddNonCountedBacktracking | + &Instruction::CallPopCount => { let (name, arity) = self.to_name_and_arity(); functor!(atom!("call"), [atom(name), fixnum(arity)]) } // - &Instruction::ExecuteAtomChars(_) | - &Instruction::ExecuteAtomCodes(_) | - &Instruction::ExecuteAtomLength(_) | - &Instruction::ExecuteBindFromRegister(_) | - &Instruction::ExecuteContinuation(_) | - &Instruction::ExecuteCharCode(_) | - &Instruction::ExecuteCharType(_) | - &Instruction::ExecuteCharsToNumber(_) | - &Instruction::ExecuteCodesToNumber(_) | - &Instruction::ExecuteCopyTermWithoutAttrVars(_) | - &Instruction::ExecuteCheckCutPoint(_) | - &Instruction::ExecuteClose(_) | - &Instruction::ExecuteCopyToLiftedHeap(_) | - &Instruction::ExecuteCreatePartialString(_) | - &Instruction::ExecuteCurrentHostname(_) | - &Instruction::ExecuteCurrentInput(_) | - &Instruction::ExecuteCurrentOutput(_) | - &Instruction::ExecuteDirectoryFiles(_) | - &Instruction::ExecuteFileSize(_) | - &Instruction::ExecuteFileExists(_) | - &Instruction::ExecuteDirectoryExists(_) | - &Instruction::ExecuteDirectorySeparator(_) | - &Instruction::ExecuteMakeDirectory(_) | - &Instruction::ExecuteMakeDirectoryPath(_) | - &Instruction::ExecuteDeleteFile(_) | - &Instruction::ExecuteRenameFile(_) | - &Instruction::ExecuteWorkingDirectory(_) | - &Instruction::ExecuteDeleteDirectory(_) | - &Instruction::ExecutePathCanonical(_) | - &Instruction::ExecuteFileTime(_) | - &Instruction::ExecuteDeleteAttribute(_) | - &Instruction::ExecuteDeleteHeadAttribute(_) | + &Instruction::ExecuteAtomChars | + &Instruction::ExecuteAtomCodes | + &Instruction::ExecuteAtomLength | + &Instruction::ExecuteBindFromRegister | + &Instruction::ExecuteContinuation | + &Instruction::ExecuteCharCode | + &Instruction::ExecuteCharType | + &Instruction::ExecuteCharsToNumber | + &Instruction::ExecuteCodesToNumber | + &Instruction::ExecuteCopyTermWithoutAttrVars | + &Instruction::ExecuteCheckCutPoint | + &Instruction::ExecuteClose | + &Instruction::ExecuteCopyToLiftedHeap | + &Instruction::ExecuteCreatePartialString | + &Instruction::ExecuteCurrentHostname | + &Instruction::ExecuteCurrentInput | + &Instruction::ExecuteCurrentOutput | + &Instruction::ExecuteDirectoryFiles | + &Instruction::ExecuteFileSize | + &Instruction::ExecuteFileExists | + &Instruction::ExecuteDirectoryExists | + &Instruction::ExecuteDirectorySeparator | + &Instruction::ExecuteMakeDirectory | + &Instruction::ExecuteMakeDirectoryPath | + &Instruction::ExecuteDeleteFile | + &Instruction::ExecuteRenameFile | + &Instruction::ExecuteFileCopy | + &Instruction::ExecuteWorkingDirectory | + &Instruction::ExecuteDeleteDirectory | + &Instruction::ExecutePathCanonical | + &Instruction::ExecuteFileTime | &Instruction::ExecuteDynamicModuleResolution(..) | &Instruction::ExecutePrepareCallClause(..) | - &Instruction::ExecuteCompileInlineOrExpandedGoal(..) | - &Instruction::ExecuteIsExpandedOrInlined(_) | - &Instruction::ExecuteEnqueueAttributedVar(_) | - &Instruction::ExecuteFetchGlobalVar(_) | - &Instruction::ExecuteFirstStream(_) | - &Instruction::ExecuteFlushOutput(_) | - &Instruction::ExecuteGetByte(_) | - &Instruction::ExecuteGetChar(_) | - &Instruction::ExecuteGetNChars(_) | - &Instruction::ExecuteGetCode(_) | - &Instruction::ExecuteGetSingleChar(_) | - &Instruction::ExecuteResetAttrVarState(_) | - &Instruction::ExecuteTruncateIfNoLiftedHeapGrowthDiff(_) | - &Instruction::ExecuteTruncateIfNoLiftedHeapGrowth(_) | - &Instruction::ExecuteGetAttributedVariableList(_) | - &Instruction::ExecuteGetAttrVarQueueDelimiter(_) | - &Instruction::ExecuteGetAttrVarQueueBeyond(_) | - &Instruction::ExecuteGetBValue(_) | - &Instruction::ExecuteGetContinuationChunk(_) | - &Instruction::ExecuteGetNextDBRef(_) | - &Instruction::ExecuteGetNextOpDBRef(_) | - &Instruction::ExecuteIsPartialString(_) | - &Instruction::ExecuteHalt(_) | - &Instruction::ExecuteGetLiftedHeapFromOffset(_) | - &Instruction::ExecuteGetLiftedHeapFromOffsetDiff(_) | - &Instruction::ExecuteGetSCCCleaner(_) | - &Instruction::ExecuteHeadIsDynamic(_) | - &Instruction::ExecuteInstallSCCCleaner(_) | - &Instruction::ExecuteInstallInferenceCounter(_) | - &Instruction::ExecuteLiftedHeapLength(_) | - &Instruction::ExecuteLoadLibraryAsStream(_) | - &Instruction::ExecuteModuleExists(_) | - &Instruction::ExecuteNextEP(_) | - &Instruction::ExecuteNoSuchPredicate(_) | - &Instruction::ExecuteNumberToChars(_) | - &Instruction::ExecuteNumberToCodes(_) | - &Instruction::ExecuteOpDeclaration(_) | - &Instruction::ExecuteOpen(_) | - &Instruction::ExecuteSetStreamOptions(_) | - &Instruction::ExecuteNextStream(_) | - &Instruction::ExecutePartialStringTail(_) | - &Instruction::ExecutePeekByte(_) | - &Instruction::ExecutePeekChar(_) | - &Instruction::ExecutePeekCode(_) | - &Instruction::ExecutePointsToContinuationResetMarker(_) | - &Instruction::ExecutePutByte(_) | - &Instruction::ExecutePutChar(_) | - &Instruction::ExecutePutChars(_) | - &Instruction::ExecutePutCode(_) | - &Instruction::ExecuteReadQueryTerm(_) | - &Instruction::ExecuteReadTerm(_) | - &Instruction::ExecuteRedoAttrVarBinding(_) | - &Instruction::ExecuteRemoveCallPolicyCheck(_) | - &Instruction::ExecuteRemoveInferenceCounter(_) | - &Instruction::ExecuteResetContinuationMarker(_) | - &Instruction::ExecuteRestoreCutPolicy(_) | - &Instruction::ExecuteSetCutPoint(_, _) | - &Instruction::ExecuteSetInput(_) | - &Instruction::ExecuteSetOutput(_) | - &Instruction::ExecuteStoreBacktrackableGlobalVar(_) | - &Instruction::ExecuteStoreGlobalVar(_) | - &Instruction::ExecuteStreamProperty(_) | - &Instruction::ExecuteSetStreamPosition(_) | - &Instruction::ExecuteInferenceLevel(_) | - &Instruction::ExecuteCleanUpBlock(_) | - &Instruction::ExecuteFail(_) | - &Instruction::ExecuteGetBall(_) | - &Instruction::ExecuteGetCurrentBlock(_) | - &Instruction::ExecuteGetCutPoint(_) | - &Instruction::ExecuteGetStaggeredCutPoint(_) | - &Instruction::ExecuteGetDoubleQuotes(_) | - &Instruction::ExecuteInstallNewBlock(_) | - &Instruction::ExecuteMaybe(_) | - &Instruction::ExecuteCpuNow(_) | - &Instruction::ExecuteDeterministicLengthRundown(_) | - &Instruction::ExecuteHttpOpen(_) | - &Instruction::ExecuteHttpListen(_) | - &Instruction::ExecuteHttpAccept(_) | - &Instruction::ExecuteHttpAnswer(_) | - &Instruction::ExecutePredicateDefined(_) | - &Instruction::ExecuteStripModule(_) | - &Instruction::ExecuteCurrentTime(_) | - &Instruction::ExecuteQuotedToken(_) | - &Instruction::ExecuteReadTermFromChars(_) | - &Instruction::ExecuteResetBlock(_) | - &Instruction::ExecuteReturnFromVerifyAttr(_) | - &Instruction::ExecuteSetBall(_) | - &Instruction::ExecutePushBallStack(_) | - &Instruction::ExecutePopBallStack(_) | - &Instruction::ExecutePopFromBallStack(_) | - &Instruction::ExecuteSetCutPointByDefault(_, _) | - &Instruction::ExecuteSetDoubleQuotes(_) | - &Instruction::ExecuteSetSeed(_) | - &Instruction::ExecuteSkipMaxList(_) | - &Instruction::ExecuteSleep(_) | - &Instruction::ExecuteSocketClientOpen(_) | - &Instruction::ExecuteSocketServerOpen(_) | - &Instruction::ExecuteSocketServerAccept(_) | - &Instruction::ExecuteSocketServerClose(_) | - &Instruction::ExecuteTLSAcceptClient(_) | - &Instruction::ExecuteTLSClientConnect(_) | - &Instruction::ExecuteSucceed(_) | - &Instruction::ExecuteTermAttributedVariables(_) | - &Instruction::ExecuteTermVariables(_) | - &Instruction::ExecuteTermVariablesUnderMaxDepth(_) | - &Instruction::ExecuteTruncateLiftedHeapTo(_) | - &Instruction::ExecuteUnifyWithOccursCheck(_) | - &Instruction::ExecuteUnwindEnvironments(_) | - &Instruction::ExecuteUnwindStack(_) | - &Instruction::ExecuteWAMInstructions(_) | - &Instruction::ExecuteWriteTerm(_) | - &Instruction::ExecuteWriteTermToChars(_) | - &Instruction::ExecuteScryerPrologVersion(_) | - &Instruction::ExecuteCryptoRandomByte(_) | - &Instruction::ExecuteCryptoDataHash(_) | - &Instruction::ExecuteCryptoDataHKDF(_) | - &Instruction::ExecuteCryptoPasswordHash(_) | - &Instruction::ExecuteCryptoDataEncrypt(_) | - &Instruction::ExecuteCryptoDataDecrypt(_) | - &Instruction::ExecuteCryptoCurveScalarMult(_) | - &Instruction::ExecuteEd25519Sign(_) | - &Instruction::ExecuteEd25519Verify(_) | - &Instruction::ExecuteEd25519NewKeyPair(_) | - &Instruction::ExecuteEd25519KeyPairPublicKey(_) | - &Instruction::ExecuteCurve25519ScalarMult(_) | - &Instruction::ExecuteFirstNonOctet(_) | - &Instruction::ExecuteLoadHTML(_) | - &Instruction::ExecuteLoadXML(_) | - &Instruction::ExecuteGetEnv(_) | - &Instruction::ExecuteSetEnv(_) | - &Instruction::ExecuteUnsetEnv(_) | - &Instruction::ExecuteShell(_) | - &Instruction::ExecutePID(_) | - &Instruction::ExecuteCharsBase64(_) | - &Instruction::ExecuteDevourWhitespace(_) | - &Instruction::ExecuteIsSTOEnabled(_) | - &Instruction::ExecuteSetSTOAsUnify(_) | - &Instruction::ExecuteSetNSTOAsUnify(_) | - &Instruction::ExecuteSetSTOWithErrorAsUnify(_) | - &Instruction::ExecuteHomeDirectory(_) | - &Instruction::ExecuteDebugHook(_) | - &Instruction::ExecuteAddDiscontiguousPredicate(_) | - &Instruction::ExecuteAddDynamicPredicate(_) | - &Instruction::ExecuteAddMultifilePredicate(_) | - &Instruction::ExecuteAddGoalExpansionClause(_) | - &Instruction::ExecuteAddTermExpansionClause(_) | - &Instruction::ExecuteAddInSituFilenameModule(_) | - &Instruction::ExecuteClauseToEvacuable(_) | - &Instruction::ExecuteScopedClauseToEvacuable(_) | - &Instruction::ExecuteConcludeLoad(_) | - &Instruction::ExecuteDeclareModule(_) | - &Instruction::ExecuteLoadCompiledLibrary(_) | - &Instruction::ExecuteLoadContextSource(_) | - &Instruction::ExecuteLoadContextFile(_) | - &Instruction::ExecuteLoadContextDirectory(_) | - &Instruction::ExecuteLoadContextModule(_) | - &Instruction::ExecuteLoadContextStream(_) | - &Instruction::ExecutePopLoadContext(_) | - &Instruction::ExecutePopLoadStatePayload(_) | - &Instruction::ExecutePushLoadContext(_) | - &Instruction::ExecutePushLoadStatePayload(_) | - &Instruction::ExecuteUseModule(_) | - &Instruction::ExecuteBuiltInProperty(_) | - &Instruction::ExecuteMetaPredicateProperty(_) | - &Instruction::ExecuteMultifileProperty(_) | - &Instruction::ExecuteDiscontiguousProperty(_) | - &Instruction::ExecuteDynamicProperty(_) | - &Instruction::ExecuteAbolishClause(_) | - &Instruction::ExecuteAsserta(_) | - &Instruction::ExecuteAssertz(_) | - &Instruction::ExecuteRetract(_) | - &Instruction::ExecuteIsConsistentWithTermQueue(_) | - &Instruction::ExecuteFlushTermQueue(_) | - &Instruction::ExecuteRemoveModuleExports(_) | - &Instruction::ExecuteAddNonCountedBacktracking(_) | - &Instruction::ExecutePopCount(_) => { + &Instruction::ExecuteCompileInlineOrExpandedGoal | + &Instruction::ExecuteIsExpandedOrInlined | + &Instruction::ExecuteGetClauseP | + &Instruction::ExecuteInvokeClauseAtP | + &Instruction::ExecuteGetFromAttributedVarList | + &Instruction::ExecutePutToAttributedVarList | + &Instruction::ExecuteDeleteFromAttributedVarList | + &Instruction::ExecuteDeleteAllAttributesFromVar | + &Instruction::ExecuteUnattributedVar | + &Instruction::ExecuteGetDBRefs | + &Instruction::ExecuteKeySortWithConstantVarOrdering | + &Instruction::ExecuteFetchGlobalVar | + &Instruction::ExecuteFirstStream | + &Instruction::ExecuteFlushOutput | + &Instruction::ExecuteGetByte | + &Instruction::ExecuteGetChar | + &Instruction::ExecuteGetNChars | + &Instruction::ExecuteGetCode | + &Instruction::ExecuteGetSingleChar | + &Instruction::ExecuteTruncateIfNoLiftedHeapGrowthDiff | + &Instruction::ExecuteTruncateIfNoLiftedHeapGrowth | + &Instruction::ExecuteGetAttributedVariableList | + &Instruction::ExecuteGetAttrVarQueueDelimiter | + &Instruction::ExecuteGetAttrVarQueueBeyond | + &Instruction::ExecuteGetBValue | + &Instruction::ExecuteGetContinuationChunk | + &Instruction::ExecuteGetNextOpDBRef | + &Instruction::ExecuteLookupDBRef | + &Instruction::ExecuteIsPartialString | + &Instruction::ExecuteHalt | + &Instruction::ExecuteGetLiftedHeapFromOffset | + &Instruction::ExecuteGetLiftedHeapFromOffsetDiff | + &Instruction::ExecuteGetSCCCleaner | + &Instruction::ExecuteHeadIsDynamic | + &Instruction::ExecuteInstallSCCCleaner | + &Instruction::ExecuteInstallInferenceCounter | + &Instruction::ExecuteLiftedHeapLength | + &Instruction::ExecuteLoadLibraryAsStream | + &Instruction::ExecuteModuleExists | + &Instruction::ExecuteNextEP | + &Instruction::ExecuteNoSuchPredicate | + &Instruction::ExecuteNumberToChars | + &Instruction::ExecuteNumberToCodes | + &Instruction::ExecuteOpDeclaration | + &Instruction::ExecuteOpen | + &Instruction::ExecuteSetStreamOptions | + &Instruction::ExecuteNextStream | + &Instruction::ExecutePartialStringTail | + &Instruction::ExecutePeekByte | + &Instruction::ExecutePeekChar | + &Instruction::ExecutePeekCode | + &Instruction::ExecutePointsToContinuationResetMarker | + &Instruction::ExecutePutByte | + &Instruction::ExecutePutChar | + &Instruction::ExecutePutChars | + &Instruction::ExecutePutCode | + &Instruction::ExecuteReadQueryTerm | + &Instruction::ExecuteReadTerm | + &Instruction::ExecuteRedoAttrVarBinding | + &Instruction::ExecuteRemoveCallPolicyCheck | + &Instruction::ExecuteRemoveInferenceCounter | + &Instruction::ExecuteResetContinuationMarker | + &Instruction::ExecuteRestoreCutPolicy | + &Instruction::ExecuteSetCutPoint(_) | + &Instruction::ExecuteSetInput | + &Instruction::ExecuteSetOutput | + &Instruction::ExecuteStoreBacktrackableGlobalVar | + &Instruction::ExecuteStoreGlobalVar | + &Instruction::ExecuteStreamProperty | + &Instruction::ExecuteSetStreamPosition | + &Instruction::ExecuteInferenceLevel | + &Instruction::ExecuteCleanUpBlock | + &Instruction::ExecuteFail | + &Instruction::ExecuteGetBall | + &Instruction::ExecuteGetCurrentBlock | + &Instruction::ExecuteGetCurrentSCCBlock | + &Instruction::ExecuteGetCutPoint | + &Instruction::ExecuteGetDoubleQuotes | + &Instruction::ExecuteGetUnknown | + &Instruction::ExecuteInstallNewBlock | + &Instruction::ExecuteMaybe | + &Instruction::ExecuteCpuNow | + &Instruction::ExecuteDeterministicLengthRundown | + &Instruction::ExecuteHttpOpen | + &Instruction::ExecuteHttpListen | + &Instruction::ExecuteHttpAccept | + &Instruction::ExecuteHttpAnswer | + &Instruction::ExecuteLoadForeignLib | + &Instruction::ExecuteForeignCall | + &Instruction::ExecuteDefineForeignStruct | + &Instruction::ExecutePredicateDefined | + &Instruction::ExecuteStripModule | + &Instruction::ExecuteCurrentTime | + &Instruction::ExecuteQuotedToken | + &Instruction::ExecuteReadFromChars | + &Instruction::ExecuteReadTermFromChars | + &Instruction::ExecuteResetBlock | + &Instruction::ExecuteResetSCCBlock | + &Instruction::ExecuteReturnFromVerifyAttr | + &Instruction::ExecuteSetBall | + &Instruction::ExecutePushBallStack | + &Instruction::ExecutePopBallStack | + &Instruction::ExecutePopFromBallStack | + &Instruction::ExecuteSetCutPointByDefault(_) | + &Instruction::ExecuteSetDoubleQuotes | + &Instruction::ExecuteSetUnknown | + &Instruction::ExecuteSetSeed | + &Instruction::ExecuteSkipMaxList | + &Instruction::ExecuteSleep | + &Instruction::ExecuteSocketClientOpen | + &Instruction::ExecuteSocketServerOpen | + &Instruction::ExecuteSocketServerAccept | + &Instruction::ExecuteSocketServerClose | + &Instruction::ExecuteTLSAcceptClient | + &Instruction::ExecuteTLSClientConnect | + &Instruction::ExecuteSucceed | + &Instruction::ExecuteTermAttributedVariables | + &Instruction::ExecuteTermVariables | + &Instruction::ExecuteTermVariablesUnderMaxDepth | + &Instruction::ExecuteTruncateLiftedHeapTo | + &Instruction::ExecuteUnifyWithOccursCheck | + &Instruction::ExecuteUnwindEnvironments | + &Instruction::ExecuteUnwindStack | + &Instruction::ExecuteWAMInstructions | + &Instruction::ExecuteInlinedInstructions | + &Instruction::ExecuteWriteTerm | + &Instruction::ExecuteWriteTermToChars | + &Instruction::ExecuteScryerPrologVersion | + &Instruction::ExecuteCryptoRandomByte | + &Instruction::ExecuteCryptoDataHash | + &Instruction::ExecuteCryptoDataHKDF | + &Instruction::ExecuteCryptoPasswordHash | + &Instruction::ExecuteCryptoDataEncrypt | + &Instruction::ExecuteCryptoDataDecrypt | + &Instruction::ExecuteCryptoCurveScalarMult | + &Instruction::ExecuteEd25519Sign | + &Instruction::ExecuteEd25519Verify | + &Instruction::ExecuteEd25519NewKeyPair | + &Instruction::ExecuteEd25519KeyPairPublicKey | + &Instruction::ExecuteCurve25519ScalarMult | + &Instruction::ExecuteFirstNonOctet | + &Instruction::ExecuteLoadHTML | + &Instruction::ExecuteLoadXML | + &Instruction::ExecuteGetEnv | + &Instruction::ExecuteSetEnv | + &Instruction::ExecuteUnsetEnv | + &Instruction::ExecuteShell | + &Instruction::ExecutePID | + &Instruction::ExecuteCharsBase64 | + &Instruction::ExecuteDevourWhitespace | + &Instruction::ExecuteIsSTOEnabled | + &Instruction::ExecuteSetSTOAsUnify | + &Instruction::ExecuteSetNSTOAsUnify | + &Instruction::ExecuteSetSTOWithErrorAsUnify | + &Instruction::ExecuteHomeDirectory | + &Instruction::ExecuteDebugHook | + &Instruction::ExecuteAddDiscontiguousPredicate | + &Instruction::ExecuteAddDynamicPredicate | + &Instruction::ExecuteAddMultifilePredicate | + &Instruction::ExecuteAddGoalExpansionClause | + &Instruction::ExecuteAddTermExpansionClause | + &Instruction::ExecuteAddInSituFilenameModule | + &Instruction::ExecuteClauseToEvacuable | + &Instruction::ExecuteScopedClauseToEvacuable | + &Instruction::ExecuteConcludeLoad | + &Instruction::ExecuteDeclareModule | + &Instruction::ExecuteLoadCompiledLibrary | + &Instruction::ExecuteLoadContextSource | + &Instruction::ExecuteLoadContextFile | + &Instruction::ExecuteLoadContextDirectory | + &Instruction::ExecuteLoadContextModule | + &Instruction::ExecuteLoadContextStream | + &Instruction::ExecutePopLoadContext | + &Instruction::ExecutePopLoadStatePayload | + &Instruction::ExecutePushLoadContext | + &Instruction::ExecutePushLoadStatePayload | + &Instruction::ExecuteUseModule | + &Instruction::ExecuteBuiltInProperty | + &Instruction::ExecuteMetaPredicateProperty | + &Instruction::ExecuteMultifileProperty | + &Instruction::ExecuteDiscontiguousProperty | + &Instruction::ExecuteDynamicProperty | + &Instruction::ExecuteAbolishClause | + &Instruction::ExecuteAsserta | + &Instruction::ExecuteAssertz | + &Instruction::ExecuteRetract | + &Instruction::ExecuteIsConsistentWithTermQueue | + &Instruction::ExecuteFlushTermQueue | + &Instruction::ExecuteRemoveModuleExports | + &Instruction::ExecuteAddNonCountedBacktracking | + &Instruction::ExecutePopCount => { let (name, arity) = self.to_name_and_arity(); functor!(atom!("execute"), [atom(name), fixnum(arity)]) } @@ -2016,12 +2116,9 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::Deallocate => { functor!(atom!("deallocate")) } - &Instruction::JmpByCall(_, offset, ..) => { + &Instruction::JmpByCall(offset) => { functor!(atom!("jmp_by_call"), [fixnum(offset)]) } - &Instruction::JmpByExecute(_, offset, ..) => { - functor!(atom!("jmp_by_execute"), [fixnum(offset)]) - } &Instruction::RevJmpBy(offset) => { functor!(atom!("rev_jmp_by"), [fixnum(offset)]) } @@ -2063,13 +2160,14 @@ fn generate_instruction_preface() -> TokenStream { [lvl_stub, rt_stub] ) } - &Instruction::GetStructure(name, arity, r) => { + &Instruction::GetStructure(lvl, name, arity, r) => { + let lvl_stub = lvl.into_functor(); let rt_stub = reg_type_into_functor(r); functor!( atom!("get_structure"), - [atom(name), fixnum(arity), str(h, 0)], - [rt_stub] + [str(h, 0), atom(name), fixnum(arity), str(h, 1)], + [lvl_stub, rt_stub] ) } &Instruction::GetValue(r, arg) => { @@ -2216,6 +2314,11 @@ pub fn generate_instructions_rs() -> TokenStream { let mut clause_type_to_instr_arms = vec![]; let mut clause_type_name_arms = vec![]; let mut is_inbuilt_arms = vec![]; + let mut is_inlined_arms = vec![]; + + is_inbuilt_arms.push(quote! { + (atom!(":-"), 1 | 2) => true + }); for (name, arity, variant) in instr_data.compare_number_variants { let ident = variant.ident.clone(); @@ -2267,7 +2370,7 @@ pub fn generate_instructions_rs() -> TokenStream { quote! { ClauseType::Inlined( InlinedClauseType::CompareNumber(CompareNumber::#ident(#(#placeholder_ids),*)) - ) => Instruction::#instr_ident(#(#placeholder_ids),*, 0) + ) => Instruction::#instr_ident(#(*#placeholder_ids),*) } ); @@ -2276,6 +2379,12 @@ pub fn generate_instructions_rs() -> TokenStream { (atom!(#name), #arity) => true } ); + + is_inlined_arms.push( + quote! { + (atom!(#name), #arity) => true + } + ); } for (name, arity, variant) in instr_data.compare_term_variants { @@ -2302,7 +2411,7 @@ pub fn generate_instructions_rs() -> TokenStream { quote! { ClauseType::BuiltIn( BuiltInClauseType::CompareTerm(CompareTerm::#ident) - ) => Instruction::#instr_ident(0) + ) => Instruction::#instr_ident } ); @@ -2363,13 +2472,13 @@ pub fn generate_instructions_rs() -> TokenStream { quote! { ClauseType::BuiltIn( BuiltInClauseType::#ident(#(#placeholder_ids),*) - ) => Instruction::#instr_ident(#(#placeholder_ids),*,0) + ) => Instruction::#instr_ident(#(*#placeholder_ids),*) } } else { quote! { ClauseType::BuiltIn( BuiltInClauseType::#ident - ) => Instruction::#instr_ident(0) + ) => Instruction::#instr_ident } }); @@ -2434,7 +2543,7 @@ pub fn generate_instructions_rs() -> TokenStream { quote! { ClauseType::Inlined( InlinedClauseType::#ident(#(#placeholder_ids),*) - ) => Instruction::#instr_ident(#(#placeholder_ids),*,0) + ) => Instruction::#instr_ident(*#(#placeholder_ids),*) } ); @@ -2443,6 +2552,12 @@ pub fn generate_instructions_rs() -> TokenStream { (atom!(#name), #arity) => true } ); + + is_inlined_arms.push( + quote! { + (atom!(#name), #arity) => true + } + ); } for (name, arity, variant) in instr_data.system_clause_type_variants { @@ -2524,13 +2639,13 @@ pub fn generate_instructions_rs() -> TokenStream { quote! { ClauseType::System( SystemClauseType::#ident(#(#placeholder_ids),*) - ) => Instruction::#instr_ident(#(#placeholder_ids),*,0) + ) => Instruction::#instr_ident(#(*#placeholder_ids),*) } } else { quote! { ClauseType::System( SystemClauseType::#ident - ) => Instruction::#instr_ident(0) + ) => Instruction::#instr_ident } }); @@ -2601,13 +2716,13 @@ pub fn generate_instructions_rs() -> TokenStream { quote! { ClauseType::System(SystemClauseType::REPL( REPLCodePtr::#ident(#(#placeholder_ids),*) - )) => Instruction::#instr_ident(#(#placeholder_ids),*,0) + )) => Instruction::#instr_ident(#(*#placeholder_ids),*) } } else { quote! { ClauseType::System(SystemClauseType::REPL( REPLCodePtr::#ident - )) => Instruction::#instr_ident(0) + )) => Instruction::#instr_ident } }); @@ -2627,7 +2742,7 @@ pub fn generate_instructions_rs() -> TokenStream { }); clause_type_to_instr_arms.push(quote! { - ClauseType::Named(arity, name, idx) => Instruction::CallNamed(arity, name, idx, 0) + ClauseType::Named(arity, name, idx) => Instruction::CallNamed(*arity, *name, *idx) }); clause_type_name_arms.push(quote! { @@ -2678,11 +2793,11 @@ pub fn generate_instructions_rs() -> TokenStream { clause_type_to_instr_arms.push(if !variant_fields.is_empty() { quote! { ClauseType::#ident(#(#placeholder_ids),*) => - Instruction::#ident(#(#placeholder_ids),*,0) + Instruction::#ident(#(*#placeholder_ids),*) } } else { quote! { - ClauseType::#ident => Instruction::#ident(0) + ClauseType::#ident => Instruction::#ident } }); @@ -2739,11 +2854,6 @@ pub fn generate_instructions_rs() -> TokenStream { Instruction::#execute_ident(#(#placeholder_ids),*) } }) - } else if variant_string == "JmpByCall" { - Some(quote! { - Instruction::JmpByCall(#(#placeholder_ids),*) => - Instruction::JmpByExecute(#(#placeholder_ids),*) - }) } else { None } @@ -2807,16 +2917,23 @@ pub fn generate_instructions_rs() -> TokenStream { let enum_arity = if let Fields::Unnamed(fields) = &variant.fields { fields.unnamed.len() } else { - unreachable!() + 0 }; let placeholder_ids: Vec<_> = (0 .. enum_arity) .map(|n| format_ident!("f_{}", n)) .collect(); - Some(quote! { - Instruction::#variant_ident(#(#placeholder_ids),*) => - Instruction::#def_variant_ident(#(#placeholder_ids),*) + Some(if enum_arity == 0 { + quote! { + Instruction::#variant_ident => + Instruction::#def_variant_ident + } + } else { + quote! { + Instruction::#variant_ident(#(#placeholder_ids),*) => + Instruction::#def_variant_ident(#(#placeholder_ids),*) + } }) } else { None @@ -2824,38 +2941,6 @@ pub fn generate_instructions_rs() -> TokenStream { }) .collect(); - let perm_vars_mut_arms: Vec<_> = instr_data.instr_variants - .iter() - .cloned() - .filter_map(|(_, _, _, variant)| { - if !is_callable(&variant.ident) && !is_jmp(&variant.ident) { - return None; - } - - let variant_ident = variant.ident.clone(); - let enum_arity = if let Fields::Unnamed(fields) = &variant.fields { - fields.unnamed.len() - } else { - 0 - }; - - let placeholder_ids: Vec<_> = (1 .. enum_arity) - .map(|_| format_ident!("_")) - .collect(); - - Some(if enum_arity == 1 { - quote! { - Instruction::#variant_ident(ref mut perm_vars) => Some(perm_vars) - } - } else { - quote! { - Instruction::#variant_ident(#(#placeholder_ids),*, ref mut perm_vars) => - Some(perm_vars) - } - }) - }) - .collect(); - let control_flow_arms: Vec<_> = instr_data.instr_variants .iter() .cloned() @@ -2864,10 +2949,22 @@ pub fn generate_instructions_rs() -> TokenStream { return None; } + let enum_arity = if let Fields::Unnamed(fields) = &variant.fields { + fields.unnamed.len() + } else { + 0 + }; + let variant_ident = variant.ident.clone(); - Some(quote! { - Instruction::#variant_ident(..) => true + Some(if enum_arity == 0 { + quote! { + Instruction::#variant_ident => true + } + } else { + quote! { + Instruction::#variant_ident(..) => true + } }) }) .collect(); @@ -2885,27 +2982,59 @@ pub fn generate_instructions_rs() -> TokenStream { }; Some(if variant_string.starts_with("Execute") { - quote! { - (#name, execute, $($args:expr),*) => { - Instruction::#variant_ident($($args),*) + if arity == 0 { + quote! { + (#name, execute) => { + Instruction::#variant_ident + } + } + } else { + quote! { + (#name, execute, $($args:expr),*) => { + Instruction::#variant_ident($($args),*) + } } } } else if variant_string.starts_with("Call") { - quote! { - (#name, $($args:expr),*) => { - Instruction::#variant_ident($($args),*) + if arity == 0 { + quote! { + (#name) => { + Instruction::#variant_ident + } + } + } else { + quote! { + (#name, $($args:expr),*) => { + Instruction::#variant_ident($($args),*) + } } } } else if variant_string.starts_with("DefaultExecute") { - quote! { - (#name, execute, default, $($args:expr),*) => { - Instruction::#variant_ident($($args),*) + if arity == 0 { + quote! { + (#name, execute, default) => { + Instruction::#variant_ident + } + } + } else { + quote! { + (#name, execute, default, $($args:expr),*) => { + Instruction::#variant_ident($($args),*) + } } } } else if variant_string.starts_with("DefaultCall") { - quote! { - (#name, default, $($args:expr),*) => { - Instruction::#variant_ident($($args),*) + if arity == 0 { + quote! { + (#name, default) => { + Instruction::#variant_ident + } + } + } else { + quote! { + (#name, default, $($args:expr),*) => { + Instruction::#variant_ident($($args),*) + } } } } else { @@ -3033,7 +3162,7 @@ pub fn generate_instructions_rs() -> TokenStream { } } - pub fn to_instr(self) -> Instruction { + pub fn to_instr(&self) -> Instruction { match self { #( #clause_type_to_instr_arms, @@ -3057,6 +3186,15 @@ pub fn generate_instructions_rs() -> TokenStream { )* } } + + pub fn is_inlined(name: Atom, arity: usize) -> bool { + match (name, arity) { + #( + #is_inlined_arms, + )* + _ => false, + } + } } #[derive(Clone, Debug)] @@ -3102,15 +3240,6 @@ pub fn generate_instructions_rs() -> TokenStream { } } - pub fn perm_vars_mut(&mut self) -> Option<&mut usize> { - match self { - #( - #perm_vars_mut_arms, - )* - _ => None, - } - } - pub fn is_ctrl_instr(&self) -> bool { match self { &Instruction::Allocate(_) | @@ -3173,41 +3302,6 @@ fn is_jmp(id: &Ident) -> bool { } fn create_instr_variant(id: Ident, mut variant: Variant) -> Variant { - use proc_macro2::Span; - use syn::punctuated::Punctuated; - use syn::token::Paren; - - // add the perm_vars usize field to the variant. - - if is_callable(&id) || is_jmp(&id) { - let field = Field { - attrs: vec![], - vis: Visibility::Inherited, - ident: None, - colon_token: None, - ty: parse_quote! { usize }, - }; - - match &mut variant.fields { - Fields::Unnamed(ref mut fields) => { - fields.unnamed.push(field); - } - Fields::Unit => { - variant.fields = Fields::Unnamed(FieldsUnnamed { - paren_token: Paren(Span::call_site()), - unnamed: { - let mut fields_seq = Punctuated::new(); - fields_seq.push(field); - fields_seq - } - }); - } - _ => { - unreachable!(); - } - } - } - variant.ident = id; variant.attrs.clear(); diff --git a/scryer-prolog.wxs b/scryer-prolog.wxs index 53b21242..fef55e49 100644 --- a/scryer-prolog.wxs +++ b/scryer-prolog.wxs @@ -1,31 +1,28 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/allocator.rs b/src/allocator.rs index 5be3aae1..f689a802 100644 --- a/src/allocator.rs +++ b/src/allocator.rs @@ -1,14 +1,10 @@ use crate::parser::ast::*; -use crate::temp_v; -use crate::fixtures::*; use crate::forms::*; use crate::instructions::*; -use crate::machine::machine_indices::*; use crate::targets::*; use std::cell::Cell; -use std::rc::Rc; pub(crate) trait Allocator { fn new() -> Self; @@ -17,7 +13,7 @@ pub(crate) trait Allocator { &mut self, lvl: Level, context: GenContext, - code: &mut Code, + code: &mut CodeDeque, ); fn mark_non_var<'a, Target: CompilationTarget<'a>>( @@ -25,83 +21,71 @@ pub(crate) trait Allocator { lvl: Level, context: GenContext, cell: &'a Cell, - code: &mut Code, + code: &mut CodeDeque, ); fn mark_reserved_var<'a, Target: CompilationTarget<'a>>( &mut self, - var_name: Rc, + var_num: usize, lvl: Level, cell: &'a Cell, term_loc: GenContext, - code: &mut Code, + code: &mut CodeDeque, r: RegType, is_new_var: bool, ); + fn mark_cut_var(&mut self, var_num: usize, chunk_num: usize) -> RegType; + fn mark_var<'a, Target: CompilationTarget<'a>>( &mut self, - var_name: Rc, + var_num: usize, lvl: Level, cell: &'a Cell, context: GenContext, - code: &mut Code, + code: &mut CodeDeque, ); fn reset(&mut self); - fn reset_contents(&mut self) {} fn reset_arg(&mut self, arg_num: usize); fn reset_at_head(&mut self, args: &Vec); + fn reset_contents(&mut self); fn advance_arg(&mut self); + /* fn bindings(&self) -> &AllocVarDict; fn bindings_mut(&mut self) -> &mut AllocVarDict; - fn take_bindings(self) -> AllocVarDict; + */ + fn max_reg_allocated(&self) -> usize; + // TODO: wha.. why?? grrr. it drains the VarStatus data from vs (which it owns!) + // into self.bindings and perm_vs after all is computed (i.e. vs.populate_restricting_sets() + // and vs.set_perm_vals(has_deep_cut) have both been called). + /* fn drain_var_data<'a>( &mut self, - vs: VariableFixtures<'a>, + vs: VariableFixtures, num_of_chunks: usize, - ) -> VariableFixtures<'a> { + ) -> VariableFixtures { let mut perm_vs = VariableFixtures::new(); - for (var, (var_status, cells)) in vs.into_iter() { + for (var, var_status) in vs.into_iter() { match var_status { VarStatus::Temp(chunk_num, tvd) => { self.bindings_mut() - .insert(var.clone(), VarData::Temp(chunk_num, 0, tvd)); - - if chunk_num + 1 == num_of_chunks { - perm_vs.insert_last_chunk_temp_var(var); - } + .insert(var.clone(), VarAlloc::Temp(chunk_num, 0, tvd)); } VarStatus::Perm(_) => { - self.bindings_mut().insert(var.clone(), VarData::Perm(0)); - perm_vs.insert(var, (var_status, cells)); + self.bindings_mut().insert(var.clone(), VarAlloc::Perm(0)); + perm_vs.insert(var, var_status); } }; } perm_vs } - - fn get(&self, var: Rc) -> RegType { - self.bindings() - .get(&var) - .map_or(temp_v!(0), |v| v.as_reg_type()) - } - - fn is_unbound(&self, var: Rc) -> bool { - self.get(var).reg_num() == 0 - } - - fn record_register(&mut self, var: Rc, r: RegType) { - match self.bindings_mut().get_mut(&var).unwrap() { - &mut VarData::Temp(_, ref mut s, _) => *s = r.reg_num(), - &mut VarData::Perm(ref mut s) => *s = r.reg_num(), - } - } + */ } diff --git a/src/arena.rs b/src/arena.rs index 3e75e120..66b5c27b 100644 --- a/src/arena.rs +++ b/src/arena.rs @@ -6,7 +6,7 @@ use crate::raw_block::*; use crate::read::*; use ordered_float::OrderedFloat; -use crate::parser::rug::{Integer, Rational}; +use crate::parser::dashu::{Integer, Rational}; use std::alloc; use std::fmt; @@ -242,9 +242,11 @@ impl fmt::Display for TypedArenaPtr { } impl TypedArenaPtr { + // data must be allocated in the arena already. #[inline] pub const fn new(data: *mut T) -> Self { - unsafe { TypedArenaPtr(ptr::NonNull::new_unchecked(data)) } + let result = unsafe { TypedArenaPtr(ptr::NonNull::new_unchecked(data)) }; + result } #[inline] @@ -698,9 +700,9 @@ unsafe fn drop_slab_in_place(value: &mut AllocSlab) { ArenaHeaderTag::HttpReadStream => { ptr::drop_in_place(value.payload_offset::>>()); } - ArenaHeaderTag::HttpWriteStream => { - ptr::drop_in_place(value.payload_offset::>>()); - } + ArenaHeaderTag::HttpWriteStream => { + ptr::drop_in_place(value.payload_offset::>>()); + } ArenaHeaderTag::ReadlineStream => { ptr::drop_in_place(value.payload_offset::>()); } @@ -721,12 +723,12 @@ unsafe fn drop_slab_in_place(value: &mut AllocSlab) { ArenaHeaderTag::TcpListener => { ptr::drop_in_place(value.payload_offset::()); } - ArenaHeaderTag::HttpListener => { - ptr::drop_in_place(value.payload_offset::()); - } - ArenaHeaderTag::HttpResponse => { - ptr::drop_in_place(value.payload_offset::()); - } + ArenaHeaderTag::HttpListener => { + ptr::drop_in_place(value.payload_offset::()); + } + ArenaHeaderTag::HttpResponse => { + ptr::drop_in_place(value.payload_offset::()); + } ArenaHeaderTag::StandardOutputStream => { ptr::drop_in_place(value.payload_offset::>()); } @@ -788,7 +790,7 @@ mod tests { use crate::machine::partial_string::*; use ordered_float::OrderedFloat; - use crate::parser::rug::{Integer, Rational}; + use crate::parser::dashu::{Integer, Rational}; #[test] fn float_ptr_cast() { @@ -889,7 +891,7 @@ mod tests { // rational - let big_rat = 2 * Rational::from(1u64 << 63); + let big_rat = Rational::from(2) * Rational::from(1u64 << 63); let big_rat_ptr: TypedArenaPtr = arena_alloc!(big_rat, &mut wam.machine_st.arena); assert!(!big_rat_ptr.as_ptr().is_null()); @@ -915,7 +917,7 @@ mod tests { (HeapCellValueTag::Cons, cons_ptr) => { match_untyped_arena_ptr!(cons_ptr, (ArenaHeaderTag::Rational, n) => { - assert_eq!(&*n, &(2 * Rational::from(1u64 << 63))); + assert_eq!(&*n, &(Rational::from(2) * Rational::from(1u64 << 63))); } _ => unreachable!() ) diff --git a/src/arithmetic.rs b/src/arithmetic.rs index 172af5bb..ded84906 100644 --- a/src/arithmetic.rs +++ b/src/arithmetic.rs @@ -9,11 +9,11 @@ use crate::targets::QueryInstruction; use crate::types::*; use crate::parser::ast::*; -use crate::parser::rug::ops::PowAssign; -use crate::parser::rug::{Assign, Integer, Rational}; +use crate::parser::dashu::{Integer, Rational}; use crate::machine::machine_errors::*; +use dashu::base::Abs; use ordered_float::*; use std::cell::Cell; @@ -22,7 +22,6 @@ use std::convert::TryFrom; use std::f64; use std::num::FpCategory; use std::ops::Div; -use std::rc::Rc; use std::vec::Vec; #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -53,7 +52,7 @@ pub(crate) struct ArithInstructionIterator<'a> { state_stack: Vec>, } -pub(crate) type ArithCont = (Code, Option); +pub(crate) type ArithCont = (CodeDeque, Option); impl<'a> ArithInstructionIterator<'a> { fn push_subterm(&mut self, lvl: Level, term: &'a Term) { @@ -67,19 +66,6 @@ impl<'a> ArithInstructionIterator<'a> { Term::Clause(cell, name, terms) => { TermIterState::Clause(Level::Shallow, 0, cell, *name, terms) } - /* match ClauseType::from(*name, terms.len()) { - ct @ ClauseType::Named(..) => { - Ok(TermIterState::Clause(Level::Shallow, 0, cell, ct, terms)) - } - ct @ ClauseType::Inlined(InlinedClauseType::IsFloat(_)) => { - // let ct = ClauseType::Named(1, atom!("float"), CodeIndex::default()); - Ok(TermIterState::Clause(Level::Shallow, 0, cell, ct, terms)) - } - _ => Err(ArithmeticError::NonEvaluableFunctor( - Literal::Atom(*name), - terms.len(), - )), - }?,*/ Term::Literal(cell, cons) => TermIterState::Literal(Level::Shallow, cell, cons), Term::Cons(..) | Term::PartialString(..) | Term::CompleteString(..) => { return Err(ArithmeticError::NonEvaluableFunctor( @@ -87,7 +73,7 @@ impl<'a> ArithInstructionIterator<'a> { 2, )) } - Term::Var(cell, var) => TermIterState::Var(Level::Shallow, cell, var.clone()), + Term::Var(cell, var_ptr) => TermIterState::Var(Level::Shallow, cell, var_ptr.clone()), }; Ok(ArithInstructionIterator { @@ -100,7 +86,7 @@ impl<'a> ArithInstructionIterator<'a> { pub(crate) enum ArithTermRef<'a> { Literal(&'a Literal), Op(Atom, usize), // name, arity. - Var(Level, &'a Cell, Rc), + Var(Level, &'a Cell, VarPtr), } impl<'a> Iterator for ArithInstructionIterator<'a> { @@ -128,8 +114,8 @@ impl<'a> Iterator for ArithInstructionIterator<'a> { } } TermIterState::Literal(_, _, c) => return Some(Ok(ArithTermRef::Literal(c))), - TermIterState::Var(lvl, cell, var) => { - return Some(Ok(ArithTermRef::Var(lvl, cell, var.clone()))); + TermIterState::Var(lvl, cell, var_ptr) => { + return Some(Ok(ArithTermRef::Var(lvl, cell, var_ptr))); } _ => { return Some(Err(ArithmeticError::NonEvaluableFunctor( @@ -209,6 +195,13 @@ impl<'a> ArithmeticEvaluator<'a> { atom!("sin") => Ok(Instruction::Sin(a1, t)), atom!("tan") => Ok(Instruction::Tan(a1, t)), atom!("log") => Ok(Instruction::Log(a1, t)), + atom!("asinh") => Ok(Instruction::ASinh(a1, t)), + atom!("acosh") => Ok(Instruction::ACosh(a1, t)), + atom!("atanh") => Ok(Instruction::ATanh(a1, t)), + atom!("sinh") => Ok(Instruction::Sinh(a1, t)), + atom!("cosh") => Ok(Instruction::Cosh(a1, t)), + atom!("tanh") => Ok(Instruction::Tanh(a1, t)), + atom!("log10") => Ok(Instruction::Log10(a1, t)), atom!("exp") => Ok(Instruction::Exp(a1, t)), atom!("sqrt") => Ok(Instruction::Sqrt(a1, t)), atom!("acos") => Ok(Instruction::ACos(a1, t)), @@ -219,6 +212,8 @@ impl<'a> ArithmeticEvaluator<'a> { atom!("round") => Ok(Instruction::Round(a1, t)), atom!("ceiling") => Ok(Instruction::Ceiling(a1, t)), atom!("floor") => Ok(Instruction::Floor(a1, t)), + atom!("float_integer_part") => Ok(Instruction::FloatIntegerPart(a1, t)), + atom!("float_fractional_part") => Ok(Instruction::FloatFractionalPart(a1, t)), atom!("sign") => Ok(Instruction::Sign(a1, t)), atom!("\\") => Ok(Instruction::BitwiseComplement(a1, t)), _ => Err(ArithmeticError::NonEvaluableFunctor(Literal::Atom(name), 1)), @@ -320,41 +315,49 @@ impl<'a> ArithmeticEvaluator<'a> { src: &'a Term, term_loc: GenContext, arg: usize, - ) -> Result - { - let mut code = vec![]; + ) -> Result { + let mut code = CodeDeque::new(); let mut iter = src.iter()?; while let Some(term_ref) = iter.next() { match term_ref? { ArithTermRef::Literal(c) => push_literal(&mut self.interm, c)?, ArithTermRef::Var(lvl, cell, name) => { + let var_num = name.to_var_num().unwrap(); + let r = if lvl == Level::Shallow { self.marker.mark_non_callable( - name.clone(), + var_num, arg, term_loc, cell, &mut code, ) } else if term_loc.is_last() || cell.get().norm().reg_num() == 0 { - self.marker.mark_var::( - name.clone(), - lvl, - cell, - term_loc, - &mut code, - ); + let r = self.marker.get_binding(var_num); - self.marker.get_binding(&name).unwrap() + if r.reg_num() == 0 { + self.marker.mark_var::( + var_num, + lvl, + cell, + term_loc, + &mut code, + ); + cell.get().norm() + } else { + self.marker.increment_running_count(var_num); + r + } } else { + self.marker.increment_running_count(var_num); cell.get().norm() }; self.interm.push(ArithmeticTerm::Reg(r)); } ArithTermRef::Op(name, arity) => { - code.push(self.instr_from_clause(name, arity)?); + code.push_back(self.instr_from_clause(name, arity)?); } } } @@ -383,13 +386,11 @@ pub(crate) fn rnd_i<'a>(n: &'a Number, arena: &mut Arena) -> Number { if I64_MIN_TO_F <= f && f <= I64_MAX_TO_F { fixnum!(Number, f.into_inner() as i64, arena) } else { - Number::Integer(arena_alloc!(Integer::from_f64(f.into_inner()).unwrap(), arena)) + Number::Integer(arena_alloc!(Integer::from(f.0 as i64), arena)) } } &Number::Rational(ref r) => { - let r_ref = r.fract_floor_ref(); - let (mut fract, mut floor) = (Rational::new(), Integer::new()); - (&mut fract, &mut floor).assign(r_ref); + let (_, floor) = (r.fract(), r.floor()); if let Some(floor) = floor.to_i64() { fixnum!(Number, floor, arena) @@ -411,9 +412,9 @@ impl From for Integer { pub(crate) fn rnd_f(n: &Number) -> f64 { match n { &Number::Fixnum(n) => n.get_num() as f64, - &Number::Integer(ref n) => n.to_f64(), + &Number::Integer(ref n) => n.to_f64().value(), &Number::Float(OrderedFloat(f)) => f, - &Number::Rational(ref r) => r.to_f64(), + &Number::Rational(ref r) => r.to_f64().value(), } } @@ -444,12 +445,12 @@ pub(crate) fn float_fn_to_f(n: i64) -> Result { #[inline] pub(crate) fn float_i_to_f(n: &Integer) -> Result { - classify_float(n.to_f64()) + classify_float(n.to_f64().value()) } #[inline] pub(crate) fn float_r_to_f(r: &Rational) -> Result { - classify_float(r.to_f64()) + classify_float(r.to_f64().value()) } #[inline] @@ -548,8 +549,8 @@ impl PartialEq for Number { (&Number::Fixnum(n1), &Number::Float(n2)) => OrderedFloat(n1.get_num() as f64).eq(&n2), (&Number::Float(n1), &Number::Fixnum(n2)) => n1.eq(&OrderedFloat(n2.get_num() as f64)), (&Number::Integer(ref n1), &Number::Integer(ref n2)) => n1.eq(n2), - (&Number::Integer(ref n1), Number::Float(n2)) => OrderedFloat(n1.to_f64()).eq(n2), - (&Number::Float(n1), &Number::Integer(ref n2)) => n1.eq(&OrderedFloat(n2.to_f64())), + (&Number::Integer(ref n1), Number::Float(n2)) => OrderedFloat(n1.to_f64().value()).eq(n2), + (&Number::Float(n1), &Number::Integer(ref n2)) => n1.eq(&OrderedFloat(n2.to_f64().value())), (&Number::Integer(ref n1), &Number::Rational(ref n2)) => { #[cfg(feature = "num")] { @@ -570,8 +571,8 @@ impl PartialEq for Number { &**n1 == &**n2 } } - (&Number::Rational(ref n1), &Number::Float(n2)) => OrderedFloat(n1.to_f64()).eq(&n2), - (&Number::Float(n1), &Number::Rational(ref n2)) => n1.eq(&OrderedFloat(n2.to_f64())), + (&Number::Rational(ref n1), &Number::Float(n2)) => OrderedFloat(n1.to_f64().value()).eq(&n2), + (&Number::Float(n1), &Number::Rational(ref n2)) => n1.eq(&OrderedFloat(n2.to_f64().value())), (&Number::Float(f1), &Number::Float(f2)) => f1.eq(&f2), (&Number::Rational(ref r1), &Number::Rational(ref r2)) => r1.eq(&r2), } @@ -639,8 +640,8 @@ impl Ord for Number { (&Number::Fixnum(n1), &Number::Float(n2)) => OrderedFloat(n1.get_num() as f64).cmp(&n2), (&Number::Float(n1), &Number::Fixnum(n2)) => n1.cmp(&OrderedFloat(n2.get_num() as f64)), (&Number::Integer(n1), &Number::Integer(n2)) => (*n1).cmp(&*n2), - (&Number::Integer(n1), Number::Float(n2)) => OrderedFloat(n1.to_f64()).cmp(n2), - (&Number::Float(n1), &Number::Integer(ref n2)) => n1.cmp(&OrderedFloat(n2.to_f64())), + (&Number::Integer(n1), Number::Float(n2)) => OrderedFloat(n1.to_f64().value()).cmp(n2), + (&Number::Float(n1), &Number::Integer(ref n2)) => n1.cmp(&OrderedFloat(n2.to_f64().value())), (&Number::Integer(n1), &Number::Rational(n2)) => { #[cfg(feature = "num")] { @@ -661,8 +662,8 @@ impl Ord for Number { (&*n1).partial_cmp(&*n2).unwrap_or(Ordering::Less) } } - (&Number::Rational(n1), &Number::Float(n2)) => OrderedFloat(n1.to_f64()).cmp(&n2), - (&Number::Float(n1), &Number::Rational(n2)) => n1.cmp(&OrderedFloat(n2.to_f64())), + (&Number::Rational(n1), &Number::Float(n2)) => OrderedFloat(n1.to_f64().value()).cmp(&n2), + (&Number::Float(n1), &Number::Rational(n2)) => n1.cmp(&OrderedFloat(n2.to_f64().value())), (&Number::Float(f1), &Number::Float(f2)) => f1.cmp(&f2), (&Number::Rational(r1), &Number::Rational(r2)) => (*r1).cmp(&*r2), } @@ -691,7 +692,7 @@ impl TryFrom for Number { (HeapCellValueTag::F64, n) => { Ok(Number::Float(*n)) } - (HeapCellValueTag::Fixnum, n) => { + (HeapCellValueTag::Fixnum | HeapCellValueTag::CutPoint, n) => { Ok(Number::Fixnum(n)) } _ => { @@ -703,7 +704,7 @@ impl TryFrom for Number { // Computes n ^ power. Ignores the sign of power. pub(crate) fn binary_pow(mut n: Integer, power: &Integer) -> Integer { - let mut power = Integer::from(power.abs_ref()); + let mut power = Integer::from(power.abs()); if power == 0 { return Integer::from(1); @@ -716,7 +717,7 @@ pub(crate) fn binary_pow(mut n: Integer, power: &Integer) -> Integer { oddand *= &n; } - n.pow_assign(2); + n = n.pow(2); power >>= 1; } diff --git a/src/atom_table.rs b/src/atom_table.rs index b925273a..48f50ec9 100644 --- a/src/atom_table.rs +++ b/src/atom_table.rs @@ -37,42 +37,61 @@ impl From for Atom { } } -#[cfg(test)] -use std::cell::RefCell; - const ATOM_TABLE_INIT_SIZE: usize = 1 << 16; const ATOM_TABLE_ALIGN: usize = 8; #[cfg(test)] thread_local! { - static ATOM_TABLE_BUF_BASE: RefCell<*const u8> = RefCell::new(ptr::null_mut()); + static ATOM_TABLE_BUF_BASE: std::cell::RefCell<*const u8> = std::cell::RefCell::new(ptr::null_mut()); } #[cfg(not(test))] -static mut ATOM_TABLE_BUF_BASE: *const u8 = ptr::null_mut(); +static ATOM_TABLE_BUF_BASE: std::sync::atomic::AtomicPtr = + std::sync::atomic::AtomicPtr::new(ptr::null_mut()); +fn set_atom_tbl_buf_base(old_ptr: *const u8, new_ptr: *const u8) -> Result<(), *const u8> { #[cfg(test)] -fn set_atom_tbl_buf_base(ptr: *const u8) { + { ATOM_TABLE_BUF_BASE.with(|atom_table_buf_base| { - *atom_table_buf_base.borrow_mut() = ptr; - }); + let mut borrow = atom_table_buf_base.borrow_mut(); + if *borrow != old_ptr { + Err(*borrow) + } else { + *borrow = new_ptr; + Ok(()) + } + })?; + }; + #[cfg(not(test))] + { + ATOM_TABLE_BUF_BASE + .compare_exchange( + old_ptr.cast_mut(), + new_ptr.cast_mut(), + std::sync::atomic::Ordering::Relaxed, + std::sync::atomic::Ordering::Relaxed, + ) + .map_err(|ptr| ptr.cast_const()) + }?; + Ok(()) } -#[cfg(test)] pub(crate) fn get_atom_tbl_buf_base() -> *const u8 { + #[cfg(test)] + { ATOM_TABLE_BUF_BASE.with(|atom_table_buf_base| *atom_table_buf_base.borrow()) } - #[cfg(not(test))] -fn set_atom_tbl_buf_base(ptr: *const u8) { - unsafe { - ATOM_TABLE_BUF_BASE = ptr; + { + ATOM_TABLE_BUF_BASE.load(std::sync::atomic::Ordering::Relaxed) } } -#[cfg(not(test))] -pub(crate) fn get_atom_tbl_buf_base() -> *const u8 { - unsafe { ATOM_TABLE_BUF_BASE } +#[test] +#[should_panic(expected = "Overwriting atom table base pointer")] +fn atomtable_is_not_concurrency_safe() { + let _table_a = AtomTable::new(); + let _table_b = AtomTable::new(); } impl RawBlockTraits for AtomTable { @@ -239,8 +258,17 @@ pub struct AtomTable { pub table: IndexSet, } +#[cold] +fn atom_table_base_pointer_mismatch(expected: *const u8, got: *const u8) -> ! { + assert_eq!(expected, got, "Overwriting atom table base pointer, expected old value to be {expected:p}, but found {got:p}"); + unreachable!("This should only be called in a case of a mismatch as such the assert_eq should have failed!") +} + impl Drop for AtomTable { fn drop(&mut self) { + if let Err(got) = set_atom_tbl_buf_base(self.block.base, ptr::null()) { + atom_table_base_pointer_mismatch(self.block.base, got); + } self.block.deallocate(); } } @@ -248,13 +276,17 @@ impl Drop for AtomTable { impl AtomTable { #[inline] pub fn new() -> Self { - let table = Self { - block: RawBlock::new(), - table: IndexSet::new(), - }; + let mut block = RawBlock::new(); - set_atom_tbl_buf_base(table.block.base); - table + if let Err(got) = set_atom_tbl_buf_base(ptr::null(), block.base) { + block.deallocate(); + atom_table_base_pointer_mismatch(ptr::null(), got); + } + + Self { + block, + table: IndexSet::new(), + } } #[inline] @@ -289,8 +321,11 @@ impl AtomTable { ptr = self.block.alloc(size); if ptr.is_null() { + let old_base = self.block.base; self.block.grow(); - set_atom_tbl_buf_base(self.block.base); + if let Err(got) = set_atom_tbl_buf_base(old_base, self.block.base) { + atom_table_base_pointer_mismatch(old_base, got); + } } else { break; } diff --git a/src/bin/scryer-prolog.rs b/src/bin/scryer-prolog.rs index 4dcc05c2..da462526 100644 --- a/src/bin/scryer-prolog.rs +++ b/src/bin/scryer-prolog.rs @@ -1,4 +1,4 @@ -fn main() { +fn main() -> std::process::ExitCode { use std::sync::atomic::Ordering; use scryer_prolog::*; use scryer_prolog::atom_table::Atom; @@ -14,6 +14,6 @@ fn main() { runtime.block_on(async move { let mut wam = machine::Machine::new(Default::default()); - wam.run_top_level(atom!("$toplevel"), (atom!("$repl"), 1)); - }); + wam.run_top_level(atom!("$toplevel"), (atom!("$repl"), 1)) + }) } diff --git a/src/codegen.rs b/src/codegen.rs index ec69f277..7d4b92d3 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -1,11 +1,9 @@ use crate::atom_table::*; use crate::parser::ast::*; -use crate::{perm_v, temp_v}; - +use crate::temp_v; use crate::allocator::*; use crate::arithmetic::*; use crate::debray_allocator::*; -use crate::fixtures::*; use crate::forms::*; use crate::indexing::*; use crate::instructions::*; @@ -14,83 +12,130 @@ use crate::targets::*; use crate::types::*; use crate::instr; +use crate::machine::disjuncts::*; use crate::machine::machine_errors::*; -use indexmap::{IndexMap, IndexSet}; +use fxhash::FxBuildHasher; +use indexmap::IndexSet; use std::cell::Cell; use std::collections::VecDeque; -use std::rc::Rc; #[derive(Debug)] -pub(crate) struct ConjunctInfo<'a> { - pub(crate) perm_vs: VariableFixtures<'a>, - pub(crate) num_of_chunks: usize, - pub(crate) has_deep_cut: bool, +pub struct BranchCodeStack { + pub stack: Vec>, } -impl<'a> ConjunctInfo<'a> { - fn new(perm_vs: VariableFixtures<'a>, num_of_chunks: usize, has_deep_cut: bool) -> Self { - ConjunctInfo { - perm_vs, - num_of_chunks, - has_deep_cut, +pub type SubsumedBranchHits = IndexSet; + +impl BranchCodeStack { + fn new() -> Self { + Self { stack: vec![] } + } + + fn add_new_branch_stack(&mut self) { + self.stack.push(vec![]); + } + + fn add_new_branch(&mut self) { + if self.stack.is_empty() { + self.add_new_branch_stack(); + } + + if let Some(branches) = self.stack.last_mut() { + branches.push(CodeDeque::new()); } } - fn allocates(&self) -> bool { - self.perm_vs.size() > 0 || self.num_of_chunks > 1 || self.has_deep_cut + fn code<'a>(&'a mut self, default_code: &'a mut CodeDeque) -> &'a mut CodeDeque { + self.stack.last_mut() + .and_then(|stack| stack.last_mut()) + .unwrap_or(default_code) } - fn perm_vars(&self) -> usize { - self.perm_vs.size() + self.perm_var_offset() - } + fn push_missing_vars(&mut self, depth: usize, marker: &mut DebrayAllocator) -> SubsumedBranchHits { + let mut subsumed_hits = SubsumedBranchHits::with_hasher(FxBuildHasher::default()); - fn perm_var_offset(&self) -> usize { - self.has_deep_cut as usize - } + for idx in (self.stack.len() - depth .. self.stack.len()).rev() { + let branch = &mut marker.branch_stack[idx]; + let branch_hits = &branch.hits; - fn mark_unsafe_vars(&self, mut unsafe_var_marker: UnsafeVarMarker, code: &mut Code) { - if code.is_empty() { - return; - } + for (&var_num, branches) in branch_hits.iter() { + let record = &marker.var_data.records[var_num]; - let mut code_index = 0; + if record.running_count < record.num_occurrences { + if !branches.all() { + branch.deep_safety.insert(var_num); + branch.shallow_safety.insert(var_num); - for phase in 0.. { - while code[code_index].is_query_instr() { - let query_instr = &mut code[code_index]; + let r = record.allocation.as_reg_type(); - if !unsafe_var_marker.mark_safe_vars(query_instr) { - unsafe_var_marker.mark_phase(query_instr, phase); + // iterate over unset bits. + for branch_idx in branches.iter_zeros() { + if branch_idx + 1 == branches.len() && idx + 1 != self.stack.len() { + break; + } + + self.stack[idx][branch_idx].push_back(instr!("put_variable", r, 0)); + } + } + + subsumed_hits.insert(var_num); } - - code_index += 1; - } - - if code_index + 1 < code.len() { - code_index += 1; - } else { - break; } } - code_index = 0; + subsumed_hits + } - for phase in 0.. { - while code[code_index].is_query_instr() { - let query_instr = &mut code[code_index]; - unsafe_var_marker.mark_unsafe_vars(query_instr, phase); - code_index += 1; - } + fn push_jump_instrs(&mut self, depth: usize) { + // add 2 in each arm length to compensate for each jump + // instruction and each branch instruction not yet added. + let mut jump_span: usize = self.stack[self.stack.len() - depth ..] + .iter() + .map(|branch| branch.iter().map(|code| code.len() + 2).sum::()) + .sum(); - if code_index + 1 < code.len() { - code_index += 1; - } else { - break; + jump_span -= depth; + + for idx in self.stack.len() - depth .. self.stack.len() { + let inner_len = self.stack[idx].len(); + + for (inner_idx, code) in self.stack[idx].iter_mut().enumerate() { + if inner_idx + 1 == inner_len { + jump_span -= code.len() + 1; + } else { + jump_span -= code.len() + 1; + code.push_back(instr!("jmp_by_call", jump_span as usize)); + + jump_span -= 1; + } } } } + + fn pop_branch(&mut self, depth: usize, settings: CodeGenSettings) -> CodeDeque { + let mut combined_code = CodeDeque::new(); + + for mut branch_arm in self.stack.drain(self.stack.len() - depth ..).rev() { + let num_branch_arms = branch_arm.len(); + branch_arm.last_mut().map(|code| code.extend(combined_code.drain(..))); + + for (idx, code) in branch_arm.into_iter().enumerate() { + combined_code.push_back(if idx == 0 { + Instruction::TryMeElse(code.len() + 1) + } else if idx + 1 < num_branch_arms { + settings.retry_me_else(code.len() + 1) + } else { + settings.trust_me() + }); + + combined_code.extend(code.into_iter()); + } + } + + combined_code + } } #[derive(Clone, Copy, Debug)] @@ -212,53 +257,49 @@ impl CodeGenSettings { pub(crate) struct CodeGenerator<'a> { pub(crate) atom_tbl: &'a mut AtomTable, marker: DebrayAllocator, - pub(crate) var_count: IndexMap, usize>, settings: CodeGenSettings, pub(crate) skeleton: PredicateSkeleton, - pub(crate) jmp_by_locs: Vec, - global_jmp_by_locs_offset: usize, } impl DebrayAllocator { fn mark_var_in_non_callable( &mut self, - name: Rc, + var_num: usize, term_loc: GenContext, vr: &Cell, - code: &mut Code, + code: &mut CodeDeque, ) -> RegType { - self.mark_var::(name, Level::Shallow, vr, term_loc, code); - vr.get().norm() - } + self.mark_var::( + var_num, + Level::Shallow, + vr, + term_loc, + code, + ); - #[inline(always)] - pub(crate) fn get_binding(&self, name: &String) -> Option { - match self.bindings().get(name) { - Some(&VarData::Temp(_, t, _)) if t != 0 => Some(RegType::Temp(t)), - Some(&VarData::Perm(p)) if p != 0 => Some(RegType::Perm(p)), - _ => None, - } + vr.get().norm() } pub(crate) fn mark_non_callable( &mut self, - name: Rc, + var_num: usize, arg: usize, term_loc: GenContext, vr: &Cell, - code: &mut Code, + code: &mut CodeDeque, ) -> RegType { - match self.get_binding(&name) { - Some(RegType::Temp(t)) => RegType::Temp(t), - Some(RegType::Perm(p)) => { + match self.get_binding(var_num) { + RegType::Temp(t) if t != 0 => RegType::Temp(t), + RegType::Perm(p) if p != 0 => { if let GenContext::Last(_) = term_loc { - self.mark_var_in_non_callable(name.clone(), term_loc, vr, code); + self.mark_var_in_non_callable(var_num, term_loc, vr, code); temp_v!(arg) } else { + self.increment_running_count(var_num); RegType::Perm(p) } } - None => self.mark_var_in_non_callable(name, term_loc, vr, code), + _ => self.mark_var_in_non_callable(var_num, term_loc, vr, code), } } } @@ -268,7 +309,7 @@ impl DebrayAllocator { fn trim_structure_by_last_arg(instr: &mut Instruction, last_arg: &Term) { match instr { Instruction::PutStructure(_, ref mut arity, _) | - Instruction::GetStructure(_, ref mut arity, _) => { + Instruction::GetStructure(.., ref mut arity, _) => { if let Term::Literal(_, Literal::CodeIndex(_)) = last_arg { // it is acceptable if arity == 0 is the result of // this decrement. call/N will have to read the index @@ -284,56 +325,74 @@ fn trim_structure_by_last_arg(instr: &mut Instruction, last_arg: &Term) { } } +trait AddToFreeList<'a, Target: CompilationTarget<'a>> { + fn add_term_to_free_list(&mut self, r: RegType); + fn add_subterm_to_free_list(&mut self, term: &Term); +} + +impl<'a, 'b> AddToFreeList<'a, FactInstruction> for CodeGenerator<'b> { + fn add_term_to_free_list(&mut self, r: RegType) { + self.marker.add_reg_to_free_list(r); + } + + fn add_subterm_to_free_list(&mut self, _term: &Term) {} +} + +impl<'a, 'b> AddToFreeList<'a, QueryInstruction> for CodeGenerator<'b> { + #[inline(always)] + fn add_term_to_free_list(&mut self, _r: RegType) {} + + #[inline(always)] + fn add_subterm_to_free_list(&mut self, term: &Term) { + if let Some(cell) = structure_cell(term) { + self.marker.add_reg_to_free_list(cell.get()); + } + } +} + +fn structure_cell(term: &Term) -> Option<&Cell> { + match term { + &Term::Cons(ref cell, ..) | + &Term::Clause(ref cell, ..) | + Term::PartialString(ref cell, ..) | + Term::CompleteString(ref cell, ..) => Some(cell), + _ => None, + } +} + impl<'b> CodeGenerator<'b> { pub(crate) fn new(atom_tbl: &'b mut AtomTable, settings: CodeGenSettings) -> Self { CodeGenerator { atom_tbl, marker: DebrayAllocator::new(), - var_count: IndexMap::new(), settings, skeleton: PredicateSkeleton::new(), - jmp_by_locs: vec![], - global_jmp_by_locs_offset: 0, } } - fn update_var_count<'a, Iter: Iterator>>(&mut self, iter: Iter) { - for term in iter { - if let TermRef::Var(_, _, var) = term { - let entry = self.var_count.entry(var).or_insert(0); - *entry += 1; - } - } - } - - fn get_var_count(&self, var: &String) -> usize { - *self.var_count.get(var).unwrap() - } - - fn add_or_increment_void_instr<'a, Target>(target: &mut Code) + fn add_or_increment_void_instr<'a, Target>(target: &mut CodeDeque) where Target: crate::targets::CompilationTarget<'a>, { - if let Some(ref mut instr) = target.last_mut() { + if let Some(ref mut instr) = target.back_mut() { if Target::is_void_instr(&*instr) { Target::incr_void_instr(instr); return; } } - target.push(Target::to_void(1)); + target.push_back(Target::to_void(1)); } fn deep_var_instr<'a, Target: crate::targets::CompilationTarget<'a>>( &mut self, cell: &'a Cell, - var: &Rc, + var_num: usize, term_loc: GenContext, - is_exposed: bool, - target: &mut Code, + target: &mut CodeDeque, ) { - if is_exposed || self.get_var_count(var.as_ref()) > 1 { - self.marker.mark_var::(var.clone(), Level::Deep, cell, term_loc, target); + if self.marker.var_data.records[var_num].num_occurrences > 1 { + self.marker.mark_var::(var_num, Level::Deep, cell, term_loc, target); } else { Self::add_or_increment_void_instr::(target); } @@ -343,13 +402,9 @@ impl<'b> CodeGenerator<'b> { &mut self, subterm: &'a Term, term_loc: GenContext, - is_exposed: bool, - target: &mut Code, + target: &mut CodeDeque, ) { match subterm { - &Term::AnonVar if is_exposed => { - self.marker.mark_anon_var::(Level::Deep, term_loc, target); - } &Term::AnonVar => { Self::add_or_increment_void_instr::(target); } @@ -358,13 +413,13 @@ impl<'b> CodeGenerator<'b> { Term::PartialString(ref cell, ..) | Term::CompleteString(ref cell, ..) => { self.marker.mark_non_var::(Level::Deep, term_loc, cell, target); - target.push(Target::clause_arg_to_instr(cell.get())); + target.push_back(Target::clause_arg_to_instr(cell.get())); } &Term::Literal(_, ref constant) => { - target.push(Target::constant_subterm(constant.clone())); + target.push_back(Target::constant_subterm(constant.clone())); } - &Term::Var(ref cell, ref var) => { - self.deep_var_instr::(cell, var, term_loc, is_exposed, target); + &Term::Var(ref cell, ref var_ptr) => { + self.deep_var_instr::(cell, var_ptr.to_var_num().unwrap(), term_loc, target); } }; } @@ -373,13 +428,13 @@ impl<'b> CodeGenerator<'b> { &mut self, iter: Iter, term_loc: GenContext, - is_exposed: bool, - ) -> Code + ) -> CodeDeque where Target: crate::targets::CompilationTarget<'a>, Iter: Iterator>, + CodeGenerator<'b>: AddToFreeList<'a, Target> { - let mut target: Code = Vec::new(); + let mut target = CodeDeque::new(); for term in iter { match term { @@ -392,65 +447,63 @@ impl<'b> CodeGenerator<'b> { } TermRef::Clause(lvl, cell, name, terms) => { self.marker.mark_non_var::(lvl, term_loc, cell, &mut target); - target.push(Target::to_structure(name, terms.len(), cell.get())); + target.push_back(Target::to_structure(lvl, name, terms.len(), cell.get())); - if let Some(instr) = target.last_mut() { + as AddToFreeList<'a, Target>>::add_term_to_free_list(self, cell.get()); + + if let Some(instr) = target.back_mut() { if let Some(term) = terms.last() { trim_structure_by_last_arg(instr, term); } } for subterm in terms { - self.subterm_to_instr::(subterm, term_loc, is_exposed, &mut target); + self.subterm_to_instr::(subterm, term_loc, &mut target); + } + + for subterm in terms { + as AddToFreeList<'a, Target>>::add_subterm_to_free_list(self, subterm); } } TermRef::Cons(lvl, cell, head, tail) => { self.marker.mark_non_var::(lvl, term_loc, cell, &mut target); - target.push(Target::to_list(lvl, cell.get())); + target.push_back(Target::to_list(lvl, cell.get())); - self.subterm_to_instr::(head, term_loc, is_exposed, &mut target); - self.subterm_to_instr::(tail, term_loc, is_exposed, &mut target); + as AddToFreeList<'a, Target>>::add_term_to_free_list(self, cell.get()); + + self.subterm_to_instr::(head, term_loc, &mut target); + self.subterm_to_instr::(tail, term_loc, &mut target); + + as AddToFreeList<'a, Target>>::add_subterm_to_free_list(self, head); + as AddToFreeList<'a, Target>>::add_subterm_to_free_list(self, tail); } TermRef::Literal(lvl @ Level::Shallow, cell, Literal::String(ref string)) => { self.marker.mark_non_var::(lvl, term_loc, cell, &mut target); - target.push(Target::to_pstr(lvl, *string, cell.get(), false)); + target.push_back(Target::to_pstr(lvl, *string, cell.get(), false)); } TermRef::Literal(lvl @ Level::Shallow, cell, constant) => { self.marker.mark_non_var::(lvl, term_loc, cell, &mut target); - target.push(Target::to_constant(lvl, *constant, cell.get())); + target.push_back(Target::to_constant(lvl, *constant, cell.get())); } TermRef::PartialString(lvl, cell, string, tail) => { self.marker.mark_non_var::(lvl, term_loc, cell, &mut target); let atom = self.atom_tbl.build_with(&string); - target.push(Target::to_pstr(lvl, atom, cell.get(), true)); - self.subterm_to_instr::(tail, term_loc, is_exposed, &mut target); + target.push_back(Target::to_pstr(lvl, atom, cell.get(), true)); + self.subterm_to_instr::(tail, term_loc, &mut target); } TermRef::CompleteString(lvl, cell, atom) => { self.marker.mark_non_var::(lvl, term_loc, cell, &mut target); - target.push(Target::to_pstr(lvl, atom, cell.get(), false)); - } - TermRef::Var(lvl @ Level::Shallow, cell, ref var) if var.as_str() == "!" => { - if self.marker.is_unbound(var.clone()) { - if term_loc != GenContext::Head { - self.marker.mark_reserved_var::( - var.clone(), - lvl, - cell, - term_loc, - &mut target, - perm_v!(1), - false, - ); - - continue; - } - } - - self.marker.mark_var::(var.clone(), lvl, cell, term_loc, &mut target); + target.push_back(Target::to_pstr(lvl, atom, cell.get(), false)); } TermRef::Var(lvl @ Level::Shallow, cell, var) => { - self.marker.mark_var::(var.clone(), lvl, cell, term_loc, &mut target); + self.marker.mark_var::( + var.to_var_num().unwrap(), + lvl, + cell, + term_loc, + &mut target, + ); } _ => {} }; @@ -459,80 +512,27 @@ impl<'b> CodeGenerator<'b> { target } - fn collect_var_data<'a>(&mut self, mut iter: ChunkedIterator<'a>) -> ConjunctInfo<'a> { - let mut vs = VariableFixtures::new(); - - while let Some((chunk_num, lt_arity, chunked_terms)) = iter.next() { - for (i, chunked_term) in chunked_terms.iter().enumerate() { - let term_loc = match chunked_term { - &ChunkedTerm::HeadClause(..) => GenContext::Head, - &ChunkedTerm::BodyTerm(_) => { - if i < chunked_terms.len() - 1 { - GenContext::Mid(chunk_num) - } else { - GenContext::Last(chunk_num) - } - } - }; - - self.update_var_count(chunked_term.post_order_iter()); - vs.mark_vars_in_chunk(chunked_term.post_order_iter(), lt_arity, term_loc); - } + fn add_call(&mut self, code: &mut CodeDeque, call_instr: Instruction, call_policy: CallPolicy) { + if self.marker.in_tail_position && self.marker.var_data.allocates { + code.push_back(instr!("deallocate")); } - let num_of_chunks = iter.chunk_num; - let has_deep_cut = iter.encountered_deep_cut(); - - vs.populate_restricting_sets(); - vs.set_perm_vals(has_deep_cut); - - let vs = self.marker.drain_var_data(vs, num_of_chunks); - ConjunctInfo::new(vs, num_of_chunks, has_deep_cut) - } - - fn add_conditional_call(&mut self, code: &mut Code, qt: &QueryTerm, pvs: usize) { - match qt { - &QueryTerm::Jump(ref vars) => { - self.jmp_by_locs.push(code.len()); - code.push(instr!("jmp_by_call", vars.len(), 0, pvs)); - } - &QueryTerm::Clause(_, ref ct, _, CallPolicy::Default) => { - code.push(call_clause_by_default!(ct.clone(), pvs)); - } - &QueryTerm::Clause(_, ref ct, _, CallPolicy::Counted) => { - code.push(call_clause!(ct.clone(), pvs)); - } - _ => {} - } - } - - fn lco(code: &mut Code) -> usize { - let mut dealloc_index = code.len() - 1; - let last_instr = code.pop(); - - match last_instr { - Some(instr @ Instruction::Proceed) => { - code.push(instr); - } - Some(instr @ Instruction::Cut(_)) => { - dealloc_index += 1; - code.push(instr); - } - Some(mut instr) if instr.is_ctrl_instr() => { - code.push(if instr.perm_vars_mut().is_some() { - instr.to_execute() + match call_policy { + CallPolicy::Default => { + if self.marker.in_tail_position { + code.push_back(call_instr.to_execute().to_default()); } else { - dealloc_index += 1; - instr - }); + code.push_back(call_instr.to_default()) + } } - Some(instr) => { - code.push(instr); + CallPolicy::Counted => { + if self.marker.in_tail_position { + code.push_back(call_instr.to_execute()); + } else { + code.push_back(call_instr) + } } - None => {} } - - dealloc_index } fn compile_inlined<'a>( @@ -540,9 +540,9 @@ impl<'b> CodeGenerator<'b> { ct: &InlinedClauseType, terms: &'a Vec, term_loc: GenContext, - code: &mut Code, + code: &mut CodeDeque, ) -> Result<(), CompilationError> { - match ct { + let call_instr = match ct { &InlinedClauseType::CompareNumber(mut cmp) => { self.marker.reset_arg(2); @@ -560,29 +560,29 @@ impl<'b> CodeGenerator<'b> { let at_1 = at_1.unwrap_or(interm!(1)); let at_2 = at_2.unwrap_or(interm!(2)); - code.push(compare_number_instr!(cmp, at_1, at_2)); + compare_number_instr!(cmp, at_1, at_2) } &InlinedClauseType::IsAtom(..) => match &terms[0] { &Term::Literal(_, Literal::Char(_)) | &Term::Literal(_, Literal::Atom(atom!("[]"))) | &Term::Literal(_, Literal::Atom(..)) => { - code.push(instr!("$succeed", 0)); + instr!("$succeed") } &Term::Var(ref vr, ref name) => { self.marker.reset_arg(1); let r = self.marker.mark_non_callable( - name.clone(), + name.to_var_num().unwrap(), 1, term_loc, vr, code, ); - code.push(instr!("atom", r, 0)); + instr!("atom", r) } _ => { - code.push(instr!("$fail", 0)); + instr!("$fail") } }, &InlinedClauseType::IsAtomic(..) => match &terms[0] { @@ -591,26 +591,26 @@ impl<'b> CodeGenerator<'b> { &Term::Cons(..) | &Term::PartialString(..) | &Term::CompleteString(..) => { - code.push(instr!("$fail", 0)); + instr!("$fail") } &Term::Literal(_, Literal::String(_)) => { - code.push(instr!("$fail", 0)); + instr!("$fail") } &Term::Literal(..) => { - code.push(instr!("$succeed", 0)); + instr!("$succeed") } &Term::Var(ref vr, ref name) => { self.marker.reset_arg(1); let r = self.marker.mark_non_callable( - name.clone(), + name.to_var_num().unwrap(), 1, term_loc, vr, code, ); - code.push(instr!("atomic", r, 0)); + instr!("atomic", r) } }, &InlinedClauseType::IsCompound(..) => match &terms[0] { @@ -619,57 +619,57 @@ impl<'b> CodeGenerator<'b> { &Term::PartialString(..) | &Term::CompleteString(..) | &Term::Literal(_, Literal::String(..)) => { - code.push(instr!("$succeed", 0)); + instr!("$succeed") } &Term::Var(ref vr, ref name) => { self.marker.reset_arg(1); let r = self.marker.mark_non_callable( - name.clone(), + name.to_var_num().unwrap(), 1, term_loc, vr, code, ); - code.push(instr!("compound", r, 0)); + instr!("compound", r) } _ => { - code.push(instr!("$fail", 0)); + instr!("$fail") } }, &InlinedClauseType::IsRational(..) => match &terms[0] { &Term::Literal(_, Literal::Rational(_)) => { - code.push(instr!("$succeed", 0)); + instr!("$succeed") } &Term::Var(ref vr, ref name) => { self.marker.reset_arg(1); - let r = self.marker.mark_non_callable(name.clone(), 1, term_loc, vr, code); - code.push(instr!("rational", r, 0)); + let r = self.marker.mark_non_callable(name.to_var_num().unwrap(), 1, term_loc, vr, code); + instr!("rational", r) } _ => { - code.push(instr!("$fail", 0)); + instr!("$fail") } }, &InlinedClauseType::IsFloat(..) => match &terms[0] { &Term::Literal(_, Literal::Float(_)) => { - code.push(instr!("$succeed", 0)); + instr!("$succeed") } &Term::Var(ref vr, ref name) => { self.marker.reset_arg(1); let r = self.marker.mark_non_callable( - name.clone(), + name.to_var_num().unwrap(), 1, term_loc, vr, code, ); - code.push(instr!("float", r, 0)); + instr!("float", r) } _ => { - code.push(instr!("$fail", 0)); + instr!("$fail") } }, &InlinedClauseType::IsNumber(..) => match &terms[0] { @@ -677,66 +677,66 @@ impl<'b> CodeGenerator<'b> { &Term::Literal(_, Literal::Rational(_)) | &Term::Literal(_, Literal::Integer(_)) | &Term::Literal(_, Literal::Fixnum(_)) => { - code.push(instr!("$succeed", 0)); + instr!("$succeed") } &Term::Var(ref vr, ref name) => { self.marker.reset_arg(1); let r = self.marker.mark_non_callable( - name.clone(), + name.to_var_num().unwrap(), 1, term_loc, vr, code, ); - code.push(instr!("number", r, 0)); + instr!("number", r) } _ => { - code.push(instr!("$fail", 0)); + instr!("$fail") } }, &InlinedClauseType::IsNonVar(..) => match &terms[0] { &Term::AnonVar => { - code.push(instr!("$fail", 0)); + instr!("$fail") } &Term::Var(ref vr, ref name) => { self.marker.reset_arg(1); let r = self.marker.mark_non_callable( - name.clone(), + name.to_var_num().unwrap(), 1, term_loc, vr, code, ); - code.push(instr!("nonvar", r, 0)); + instr!("nonvar", r) } _ => { - code.push(instr!("$succeed", 0)); + instr!("$succeed") } }, &InlinedClauseType::IsInteger(..) => match &terms[0] { &Term::Literal(_, Literal::Integer(_)) | &Term::Literal(_, Literal::Fixnum(_)) => { - code.push(instr!("$succeed", 0)); + instr!("$succeed") } &Term::Var(ref vr, ref name) => { self.marker.reset_arg(1); let r = self.marker.mark_non_callable( - name.clone(), + name.to_var_num().unwrap(), 1, term_loc, vr, code, ); - code.push(instr!("integer", r, 0)); + instr!("integer", r) } _ => { - code.push(instr!("$fail", 0)); + instr!("$fail") } }, &InlinedClauseType::IsVar(..) => match &terms[0] { @@ -745,26 +745,29 @@ impl<'b> CodeGenerator<'b> { &Term::Cons(..) | &Term::PartialString(..) | &Term::CompleteString(..) => { - code.push(instr!("$fail", 0)); + instr!("$fail") } &Term::AnonVar => { - code.push(instr!("$succeed", 0)); + instr!("$succeed") } &Term::Var(ref vr, ref name) => { self.marker.reset_arg(1); let r = self.marker.mark_non_callable( - name.clone(), + name.to_var_num().unwrap(), 1, term_loc, vr, code, ); - code.push(instr!("var", r, 0)); + instr!("var", r) } }, - } + }; + + // inlined predicates are never counted, so this overrides nothing. + self.add_call(code, call_instr, CallPolicy::Counted); Ok(()) } @@ -783,13 +786,13 @@ impl<'b> CodeGenerator<'b> { fn compile_is_call( &mut self, terms: &Vec, - code: &mut Code, + code: &mut CodeDeque, term_loc: GenContext, call_policy: CallPolicy, ) -> Result<(), CompilationError> { macro_rules! compile_expr { ($self:expr, $terms:expr, $term_loc:expr, $code:expr) => ({ - let (acode, at) = $self.compile_arith_expr(&$terms[1], 1, $term_loc, 2)?; + let (acode, at) = $self.compile_arith_expr($terms, 1, $term_loc, 2)?; $code.extend(acode.into_iter()); at }); @@ -799,284 +802,238 @@ impl<'b> CodeGenerator<'b> { let at = match &terms[0] { &Term::Var(ref vr, ref name) => { + let var_num = name.to_var_num().unwrap(); + self.marker.mark_var::( - name.clone(), + var_num, Level::Shallow, vr, term_loc, code, ); - compile_expr!(self, terms, term_loc, code) + self.marker.mark_safe_var_unconditionally(var_num); + + compile_expr!(self, &terms[1], term_loc, code) } &Term::Literal(_, c @ Literal::Integer(_) | c @ Literal::Float(_) | c @ Literal::Rational(_) | c @ Literal::Fixnum(_)) => { let v = HeapCellValue::from(c); - code.push(instr!("put_constant", Level::Shallow, v, temp_v!(1))); + code.push_back(instr!("put_constant", Level::Shallow, v, temp_v!(1))); self.marker.advance_arg(); - compile_expr!(self, terms, term_loc, code) + compile_expr!(self, &terms[1], term_loc, code) } _ => { - code.push(instr!("$fail", 0)); + code.push_back(instr!("$fail")); return Ok(()); } }; let at = at.unwrap_or(interm!(1)); + self.add_call(code, instr!("is", temp_v!(1), at), call_policy); - Ok(if let CallPolicy::Default = call_policy { - code.push(instr!("is", default, temp_v!(1), at, 0)); - } else { - code.push(instr!("is", temp_v!(1), at, 0)); - }) - } - - #[inline] - fn compile_unblocked_cut(&mut self, code: &mut Code, cell: &Cell) { - let r = self.marker.get(Rc::new(String::from("!"))); - cell.set(VarReg::Norm(r)); - code.push(instr!("$set_cp", cell.get().norm(), 0)); - } - - fn compile_get_level_and_unify( - &mut self, - code: &mut Code, - cell: &Cell, - var: Rc, - term_loc: GenContext, - ) { - let mut target = Code::new(); - - self.marker.reset_arg(1); - self.marker.mark_var::(var, Level::Shallow, cell, term_loc, &mut target); - - if !target.is_empty() { - code.extend(target.into_iter()); - } - - code.push(instr!("get_level_and_unify", cell.get().norm())); + Ok(()) } fn compile_seq<'a>( &mut self, - iter: ChunkedIterator<'a>, - conjunct_info: &ConjunctInfo<'a>, - code: &mut Code, - is_exposed: bool, + clauses: &ChunkedTermVec, + code: &mut CodeDeque, ) -> Result<(), CompilationError> { - for (chunk_num, _, terms) in iter.rule_body_iter() { - for (i, term) in terms.iter().enumerate() { - let term_loc = if i + 1 < terms.len() { - GenContext::Mid(chunk_num) - } else { - GenContext::Last(chunk_num) - }; + let mut chunk_num = 0; + let mut branch_code_stack = BranchCodeStack::new(); + let mut clause_iter = ClauseIterator::new(clauses); - match *term { - &QueryTerm::GetLevelAndUnify(ref cell, ref var) => { - self.compile_get_level_and_unify(code, cell, var.clone(), term_loc) - } - &QueryTerm::UnblockedCut(ref cell) => self.compile_unblocked_cut(code, cell), - &QueryTerm::BlockedCut => code.push(if chunk_num == 0 { - Instruction::NeckCut - } else { - Instruction::Cut(perm_v!(1)) - }), - &QueryTerm::Clause( - _, - ClauseType::BuiltIn(BuiltInClauseType::Is(..)), - ref terms, - call_policy, - ) => self.compile_is_call(terms, code, term_loc, call_policy)?, - &QueryTerm::Clause(_, ClauseType::Inlined(ref ct), ref terms, _) => { - self.compile_inlined(ct, terms, term_loc, code)? - } - _ => { - let num_perm_vars = if chunk_num == 0 { - conjunct_info.perm_vars() + while let Some(clause_item) = clause_iter.next() { + match clause_item { + ClauseItem::Chunk(chunk) => { + for (idx, term) in chunk.iter().enumerate() { + let term_loc = if idx + 1 < chunk.len() { + GenContext::Mid(chunk_num) } else { - conjunct_info.perm_vs.vars_above_threshold(i + 1) + self.marker.in_tail_position = clause_iter.in_tail_position(); + GenContext::Last(chunk_num) }; - self.compile_query_line(term, term_loc, code, num_perm_vars, is_exposed); + match term { + &QueryTerm::GetLevel(var_num) => { + let code = branch_code_stack.code(code); + let r = self.marker.mark_cut_var(var_num, chunk_num); + code.push_back(instr!("get_level", r)); + } + &QueryTerm::GetCutPoint { var_num, prev_b } => { + let code = branch_code_stack.code(code); + let r = self.marker.mark_cut_var(var_num, chunk_num); - if self.marker.max_reg_allocated() > MAX_ARITY { - return Err(CompilationError::ExceededMaxArity); + code.push_back(if prev_b { + instr!("get_prev_level", r) + } else { + instr!("get_cut_point", r) + }); + } + &QueryTerm::GlobalCut(var_num) => { + let code = branch_code_stack.code(code); + + if chunk_num == 0 { + code.push_back(instr!("neck_cut")); + } else { + let r = self.marker.get_binding(var_num); + code.push_back(instr!("cut", r)); + } + + if self.marker.in_tail_position { + if self.marker.var_data.allocates { + code.push_back(instr!("deallocate")); + } + + code.push_back(instr!("proceed")); + } + } + &QueryTerm::LocalCut(var_num) => { + let code = branch_code_stack.code(code); + let r = self.marker.get_binding(var_num); + code.push_back(instr!("cut", r)); + + if self.marker.in_tail_position { + if self.marker.var_data.allocates { + code.push_back(instr!("deallocate")); + } + + code.push_back(instr!("proceed")); + } else { + self.marker.free_var(chunk_num, var_num); + } + } + &QueryTerm::Clause( + _, + ClauseType::BuiltIn(BuiltInClauseType::Is(..)), + ref terms, + call_policy, + ) => self.compile_is_call(terms, branch_code_stack.code(code), term_loc, call_policy)?, + &QueryTerm::Clause(_, ClauseType::Inlined(ref ct), ref terms, _) => { + self.compile_inlined(ct, terms, term_loc, branch_code_stack.code(code))? + } + &QueryTerm::Fail => { + branch_code_stack.code(code).push_back(instr!("$fail")); + } + term @ &QueryTerm::Clause(..) => { + self.compile_query_line(term, term_loc, branch_code_stack.code(code)); + + if self.marker.max_reg_allocated() > MAX_ARITY { + return Err(CompilationError::ExceededMaxArity); + } + } } } + + chunk_num += 1; + self.marker.in_tail_position = false; + self.marker.reset_contents(); + } + ClauseItem::FirstBranch(num_branches) => { + branch_code_stack.add_new_branch_stack(); + branch_code_stack.add_new_branch(); + + self.marker.branch_stack.add_branch_stack(num_branches); + self.marker.add_branch(); + } + ClauseItem::NextBranch => { + branch_code_stack.add_new_branch(); + + self.marker.add_branch(); + self.marker.branch_stack.incr_current_branch(); + } + ClauseItem::BranchEnd(depth) => { + if !clause_iter.in_tail_position() { + let subsumed_hits = branch_code_stack.push_missing_vars(depth, &mut self.marker); + self.marker.pop_branch(depth, subsumed_hits); + branch_code_stack.push_jump_instrs(depth); + } else { + self.marker.branch_stack.drain_branches(depth); + } + + let settings = CodeGenSettings { + non_counted_bt: self.settings.non_counted_bt, + is_extensible: false, + global_clock_tick: None, + }; + + let branch_code = branch_code_stack.pop_branch(depth, settings); + branch_code_stack.code(code).extend(branch_code); } } + } - self.marker.reset_contents(); + if self.marker.var_data.allocates { + code.push_front(instr!("allocate", self.marker.num_perm_vars())); } Ok(()) } - fn compile_seq_prelude(&mut self, conjunct_info: &ConjunctInfo, body: &mut Code) { - if conjunct_info.allocates() { - let perm_vars = conjunct_info.perm_vars(); - - body.push(Instruction::Allocate(perm_vars)); - - if conjunct_info.has_deep_cut { - body.push(Instruction::GetLevel(perm_v!(1))); - } - } - } - - fn compile_cleanup<'a>( - &mut self, - code: &mut Code, - conjunct_info: &ConjunctInfo<'a>, - toc: &'a QueryTerm, - ) { - // add a proceed to bookend any trailing cuts. - match toc { - &QueryTerm::BlockedCut | &QueryTerm::UnblockedCut(..) => { - code.push(instr!("proceed")); - } - _ => {} - }; - - // perform lco. - let dealloc_index = Self::lco(code); - - if conjunct_info.allocates() { - let offset = self.global_jmp_by_locs_offset; - - if let Some(jmp_by_offset) = self.jmp_by_locs[offset..].last_mut() { - if *jmp_by_offset == dealloc_index { - *jmp_by_offset += 1; - } - } - - code.insert(dealloc_index, instr!("deallocate")); - } - } - - pub(crate) fn compile_rule(&mut self, rule: &Rule) -> Result { - let iter = ChunkedIterator::from_rule(rule); - let conjunct_info = self.collect_var_data(iter); - - let &Rule { - head: (_, ref args, ref p1), - ref clauses, - } = rule; - - let mut code = Code::new(); + pub(crate) fn compile_rule(&mut self, rule: &Rule, var_data: VarData) -> Result { + let Rule { head: (_, args), clauses } = rule; + self.marker.var_data = var_data; + let mut code = VecDeque::new(); self.marker.reset_at_head(args); - self.compile_seq_prelude(&conjunct_info, &mut code); - let iter = FactIterator::from_rule_head_clause(args); - let mut fact = self.compile_target::(iter, GenContext::Head, false); + let iter = FactIterator::from_rule_head_clause(&args); + let fact = self.compile_target::(iter, GenContext::Head); if self.marker.max_reg_allocated() > MAX_ARITY { return Err(CompilationError::ExceededMaxArity); } - let mut unsafe_var_marker = UnsafeVarMarker::new(); + self.marker.reset_free_list(); + code.extend(fact.into_iter()); - if !fact.is_empty() { - unsafe_var_marker = self.mark_unsafe_fact_vars(&mut fact); - code.extend(fact.into_iter()); - } + self.compile_seq(clauses, &mut code)?; - let iter = ChunkedIterator::from_rule_body(p1, clauses); - self.compile_seq(iter, &conjunct_info, &mut code, false)?; - - conjunct_info.mark_unsafe_vars(unsafe_var_marker, &mut code); - self.compile_cleanup(&mut code, &conjunct_info, clauses.last().unwrap_or(p1)); - - Ok(code) + Ok(Vec::from(code)) } - fn mark_unsafe_fact_vars(&self, fact: &mut Code) -> UnsafeVarMarker { - let mut safe_vars = IndexSet::new(); - - for fact_instr in fact.iter_mut() { - match fact_instr { - &mut Instruction::UnifyValue(r) => { - if !safe_vars.contains(&r) { - *fact_instr = Instruction::UnifyLocalValue(r); - safe_vars.insert(r); - } - } - &mut Instruction::UnifyVariable(r) => { - safe_vars.insert(r); - } - _ => {} - } - } - - UnsafeVarMarker::from_safe_vars(safe_vars) - } - - pub(crate) fn compile_fact(&mut self, term: &Term) -> Result { - self.update_var_count(post_order_iter(term)); - - let mut vs = VariableFixtures::new(); - - vs.mark_vars_in_chunk(post_order_iter(term), term.arity(), GenContext::Head); - - vs.populate_restricting_sets(); - self.marker.drain_var_data(vs, 1); - + pub(crate) fn compile_fact(&mut self, fact: &Fact, var_data: VarData) -> Result { let mut code = Vec::new(); + self.marker.var_data = var_data; - if let &Term::Clause(_, _, ref args) = term { + if let Term::Clause(_, _, args) = &fact.head { self.marker.reset_at_head(args); - let iter = FactInstruction::iter(term); - let mut compiled_fact = self.compile_target::( + let iter = FactInstruction::iter(&fact.head); + let compiled_fact = self.compile_target::( iter, GenContext::Head, - false, ); if self.marker.max_reg_allocated() > MAX_ARITY { return Err(CompilationError::ExceededMaxArity); } - self.mark_unsafe_fact_vars(&mut compiled_fact); - - if !compiled_fact.is_empty() { - code.extend(compiled_fact.into_iter()); - } + code.extend(compiled_fact.into_iter()); } code.push(instr!("proceed")); Ok(code) } - fn compile_query_line( - &mut self, - term: &QueryTerm, - term_loc: GenContext, - code: &mut Code, - num_perm_vars_left: usize, - is_exposed: bool, - ) { + fn compile_query_line(&mut self, term: &QueryTerm, term_loc: GenContext, code: &mut CodeDeque) { self.marker.reset_arg(term.arity()); - let iter = query_term_post_order_iter(term); - let query = self.compile_target::(iter, term_loc, is_exposed); + let iter = QueryIterator::new(term); + let query = self.compile_target::(iter, term_loc); code.extend(query.into_iter()); - self.add_conditional_call(code, term, num_perm_vars_left); - } - #[inline] - fn increment_jmp_by_locs_by(&mut self, incr: usize) { - let offset = self.global_jmp_by_locs_offset; - - for loc in &mut self.jmp_by_locs[offset..] { - *loc += incr; - } + match term { + &QueryTerm::Clause(_, ref ct, _, call_policy) => { + self.add_call(code, ct.to_instr(), call_policy); + } + _ => unreachable!() + }; } fn split_predicate(clauses: &[PredicateClause]) -> Vec { @@ -1141,30 +1098,35 @@ impl<'b> CodeGenerator<'b> { fn compile_pred_subseq( &mut self, - clauses: &[PredicateClause], + clauses: &mut [PredicateClause], optimal_index: usize, ) -> Result { let mut code = VecDeque::new(); let mut code_offsets = CodeOffsets::new(I::new(), optimal_index + 1); let mut skip_stub_try_me_else = false; - let jmp_by_locs_len = self.jmp_by_locs.len(); + let clauses_len = clauses.len(); - for (i, clause) in clauses.iter().enumerate() { + for (i, clause) in clauses.iter_mut().enumerate() { self.marker.reset(); let mut clause_index_info = ClauseIndexInfo::new(code.len()); - self.global_jmp_by_locs_offset = self.jmp_by_locs.len(); let clause_code = match clause { - &PredicateClause::Fact(ref fact, ..) => self.compile_fact(fact)?, - &PredicateClause::Rule(ref rule, ..) => self.compile_rule(rule)?, + PredicateClause::Fact(fact, var_data) => { + let var_data = std::mem::replace(var_data, VarData::default()); + self.compile_fact(&fact, var_data)? + } + PredicateClause::Rule(rule, var_data) => { + let var_data = std::mem::replace(var_data, VarData::default()); + self.compile_rule(&rule, var_data)? + } }; - if clauses.len() > 1 { + if clauses_len > 1 { let choice = match i { 0 => self.settings.internal_try_me_else(clause_code.len() + 1), - _ if i == clauses.len() - 1 => self.settings.internal_trust_me(), + _ if i + 1 == clauses_len => self.settings.internal_trust_me(), _ => self.settings.internal_retry_me_else(clause_code.len() + 1), }; @@ -1190,45 +1152,23 @@ impl<'b> CodeGenerator<'b> { if let Some(arg) = arg { let index = code.len(); - if clauses.len() > 1 || self.settings.is_dynamic() { + if clauses_len > 1 || self.settings.is_extensible { code_offsets.index_term(arg, index, &mut clause_index_info, self.atom_tbl); } } - if !(code_offsets.no_indices() && clauses.len() == 1 && self.settings.is_extensible) { - // the peculiar condition of this block, when false, - // anticipates code.pop_front() being called about a - // dozen lines below. - - if !skip_stub_try_me_else { - // if the condition is false, code_offsets.no_indices() is false, - // so don't repeat the work of the condition on skip_stub_try_me_else - // below. - self.increment_jmp_by_locs_by(code.len()); - } - } - self.skeleton.clauses.push_back(clause_index_info); code.extend(clause_code.into_iter()); } - let index_code = if clauses.len() > 1 || self.settings.is_dynamic() { + let index_code = if clauses_len > 1 || self.settings.is_extensible { code_offsets.compute_indices(skip_stub_try_me_else) } else { vec![] }; - self.global_jmp_by_locs_offset = jmp_by_locs_len; - if !index_code.is_empty() { code.push_front(Instruction::IndexingCode(index_code)); - - if skip_stub_try_me_else { - // skip the TryMeElse(0) also. - self.increment_jmp_by_locs_by(2); - } else { - self.increment_jmp_by_locs_by(1); - } } else if clauses.len() == 1 && self.settings.is_extensible { // the condition is the value of skip_stub_try_me_else, which is // true if the predicate is not dynamic. This operation must apply @@ -1243,7 +1183,7 @@ impl<'b> CodeGenerator<'b> { pub(crate) fn compile_predicate( &mut self, - clauses: &Vec, + mut clauses: Vec, ) -> Result { let mut code = Code::new(); @@ -1254,12 +1194,12 @@ impl<'b> CodeGenerator<'b> { let skel_lower_bound = self.skeleton.clauses.len(); let code_segment = if self.settings.is_dynamic() { self.compile_pred_subseq::( - &clauses[left..right], + &mut clauses[left..right], instantiated_arg_index, )? } else { self.compile_pred_subseq::( - &clauses[left..right], + &mut clauses[left..right], instantiated_arg_index, )? }; @@ -1291,9 +1231,6 @@ impl<'b> CodeGenerator<'b> { } } - self.increment_jmp_by_locs_by(code.len()); - self.global_jmp_by_locs_offset = self.jmp_by_locs.len(); - code.extend(code_segment.into_iter()); } diff --git a/src/debray_allocator.rs b/src/debray_allocator.rs index 1c81fcdd..0337ed4d 100644 --- a/src/debray_allocator.rs +++ b/src/debray_allocator.rs @@ -1,42 +1,252 @@ -use indexmap::IndexMap; - use crate::allocator::*; -use crate::fixtures::*; +use crate::codegen::SubsumedBranchHits; use crate::forms::Level; use crate::instructions::*; -use crate::machine::machine_indices::*; +use crate::machine::disjuncts::VarData; use crate::parser::ast::*; -use crate::targets::CompilationTarget; - -use crate::temp_v; +use crate::targets::*; +use crate::variable_records::*; +use bit_set::*; +use bitvec::prelude::*; use fxhash::FxBuildHasher; +use indexmap::IndexMap; use std::cell::Cell; -use std::collections::BTreeSet; -use std::rc::Rc; +use std::collections::VecDeque; +use std::ops::{Deref, DerefMut}; + +pub type BranchHits = IndexMap; // key: var_num, value: branch arm occurrences. + +#[derive(Debug, Default)] +pub struct BranchOccurrences { + pub hits: BranchHits, + pub shallow_safety: BitSet, // unset means safe, set means unsafe (after the branch merge) + pub deep_safety: BitSet, + pub num_branches: usize, + pub current_branch: usize, + pub subsumed_hits: SubsumedBranchHits, +} + +impl BranchOccurrences { + fn new(num_branches: usize) -> Self { + Self { + hits: BranchHits::with_hasher(FxBuildHasher::default()), + shallow_safety: BitSet::default(), + deep_safety: BitSet::default(), + num_branches, + current_branch: 0, + subsumed_hits: SubsumedBranchHits::with_hasher(FxBuildHasher::default()), + } + } +} + +#[derive(Debug)] +pub(crate) struct BranchStack { + stack: Vec, +} + +impl Deref for BranchStack { + type Target = Vec; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.stack + } +} + +impl DerefMut for BranchStack { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.stack + } +} + +impl BranchStack { + fn branch_subsumes(&self, branch: &BranchDesignator, sub_branch: &BranchDesignator) -> bool { + if branch.branch_stack_num < sub_branch.branch_stack_num { + if branch.branch_stack_num == 0 { + true + } else { + let idx = branch.branch_stack_num - 1; + self[idx].current_branch == branch.branch_num + } + } else { + branch == sub_branch + } + } + + fn safety_unneeded_in_branch(&self, safety: &VarSafetyStatus, branch: &BranchDesignator) -> bool { + match safety { + VarSafetyStatus::Needed => false, + VarSafetyStatus::LocallyUnneeded(planter_branch) => + self.branch_subsumes(planter_branch, branch), + VarSafetyStatus::GloballyUnneeded => true, + } + } + + pub(crate) fn add_branch_occurrence(&mut self, var_num: usize) { + if let Some(occurrences) = self.last_mut() { + debug_assert!(occurrences.current_branch < occurrences.num_branches); + + let num_branches = occurrences.num_branches; + + let entry = occurrences.hits.entry(var_num) + .or_insert_with(|| BitVec::repeat(false, num_branches)); + + entry.set(occurrences.current_branch, true); + occurrences.subsumed_hits.insert(var_num); + } + } + + pub(crate) fn add_branch_stack(&mut self, num_branches: usize) { + self.push(BranchOccurrences::new(num_branches)); + } + + pub(crate) fn current_branch_designator(&self) -> BranchDesignator { + let branch_stack_num = self.len(); + let branch_num = self.last() + .map(|occurrences| occurrences.current_branch) + .unwrap_or(0); + + BranchDesignator { branch_stack_num, branch_num } + } + + #[inline] + pub(crate) fn incr_current_branch(&mut self) { + let branch_occurrences = self.last_mut().unwrap(); + branch_occurrences.current_branch += 1; + } + + #[inline] + pub(crate) fn drain_branches(&mut self, depth: usize) -> std::vec::Drain { + let start_idx = self.len() - depth; + self.drain(start_idx ..) + } +} #[derive(Debug)] pub(crate) struct DebrayAllocator { - bindings: IndexMap, VarData, FxBuildHasher>, + pub(crate) var_data: VarData, // var_data replaces bindings. + pub(crate) branch_stack: BranchStack, + pub(crate) in_tail_position: bool, arg_c: usize, temp_lb: usize, + perm_lb: usize, arity: usize, // 0 if not at head. - contents: IndexMap, FxBuildHasher>, - in_use: BTreeSet, + shallow_temp_mappings: IndexMap, + in_use: BitSet, // deep and non-var allocations + temp_free_list: Vec, + perm_free_list: VecDeque<(usize, usize)>, // chunk_num, var_num } impl DebrayAllocator { - fn is_curr_arg_distinct_from(&self, var: &String) -> bool { - match self.contents.get(&self.arg_c) { - Some(t_var) if **t_var != *var => true, + pub(crate) fn add_branch(&mut self) { + let branch_designator = self.branch_stack.current_branch_designator(); + let subsumed_hits = { + let branch_occurrences = self.branch_stack.last_mut().unwrap(); + + std::mem::replace( + &mut branch_occurrences.subsumed_hits, + SubsumedBranchHits::with_hasher(FxBuildHasher::default()), + ) + }; + + for var_num in subsumed_hits { + match &mut self.var_data.records[var_num].allocation { + VarAlloc::Perm(_, ref mut allocation) => { + match allocation { + PermVarAllocation::Done { shallow_safety, deep_safety, .. } => { + if !self.branch_stack.safety_unneeded_in_branch(shallow_safety, &branch_designator) { + let branch_occurrences = self.branch_stack.last_mut().unwrap(); + branch_occurrences.shallow_safety.insert(var_num); + } + + if !self.branch_stack.safety_unneeded_in_branch(deep_safety, &branch_designator) { + let branch_occurrences = self.branch_stack.last_mut().unwrap(); + branch_occurrences.deep_safety.insert(var_num); + } + } + _ => { + unreachable!(); + } + } + + *allocation = PermVarAllocation::Pending; + } + _ => unreachable!(), + } + } + } + + pub(crate) fn pop_branch(&mut self, depth: usize, subsumed_hits: SubsumedBranchHits) { + let removed_branches = self.branch_stack.drain_branches(depth); + + let (deep_safety, shallow_safety) = removed_branches + .into_iter() + .fold((BitSet::default(), BitSet::default()), + |(mut deep_safety, mut shallow_safety), branch_occurrences| { + deep_safety.union_with(&branch_occurrences.deep_safety); + shallow_safety.union_with(&branch_occurrences.shallow_safety); + + (deep_safety, shallow_safety) + }); + + let branch_designator = self.branch_stack.current_branch_designator(); + + let (deep_safety, shallow_safety) = match self.branch_stack.last_mut() { + Some(latest_branch) => { + latest_branch.deep_safety.union_with(&deep_safety); + latest_branch.shallow_safety.union_with(&shallow_safety); + + (&latest_branch.deep_safety, &latest_branch.shallow_safety) + } + None => (&deep_safety, &shallow_safety) + }; + + for var_num in subsumed_hits.iter().cloned() { + let running_count = self.var_data.records[var_num].running_count; + let num_occurrences = self.var_data.records[var_num].num_occurrences; + + match &mut self.var_data.records[var_num].allocation { + VarAlloc::Perm(_, allocation) => { + let shallow_safety = VarSafetyStatus::needed_if( + shallow_safety.contains(var_num), + branch_designator, + ); + + let deep_safety = VarSafetyStatus::needed_if( + deep_safety.contains(var_num), + branch_designator, + ); + + if running_count < num_occurrences { + *allocation = PermVarAllocation::Done { shallow_safety, deep_safety }; + } + } + _ => unreachable!() + } + } + + if self.branch_stack.len() > 0 { + for var_num in subsumed_hits { + self.branch_stack.add_branch_occurrence(var_num); + } + } + } + + fn is_curr_arg_distinct_from(&self, var_num: usize) -> bool { + match self.shallow_temp_mappings.get(&self.arg_c).cloned() { + Some(t_var) => t_var != var_num, _ => false, } } - fn occurs_shallowly_in_head(&self, var: &String, r: usize) -> bool { - match self.bindings.get(var).unwrap() { - &VarData::Temp(_, _, ref tvd) => tvd.use_set.contains(&(GenContext::Head, r)), + fn occurs_shallowly_in_head(&self, var_num: usize, r: usize) -> bool { + match &self.var_data.records[var_num].allocation { + VarAlloc::Temp { temp_var_data, term_loc: GenContext::Head, .. } => { + temp_var_data.use_set.contains(&(GenContext::Head, r)) + } _ => false, } } @@ -44,13 +254,13 @@ impl DebrayAllocator { #[inline] fn is_in_use(&self, r: usize) -> bool { let in_use_range = r <= self.arity && r >= self.arg_c; - in_use_range || self.in_use.contains(&r) + in_use_range || self.in_use.contains(r) } - fn alloc_with_cr(&self, var: &String) -> usize { - match self.bindings.get(var) { - Some(&VarData::Temp(_, _, ref tvd)) => { - for &(_, reg) in tvd.use_set.iter() { + fn alloc_with_cr(&self, var_num: usize) -> usize { + match &self.var_data.records[var_num].allocation { + VarAlloc::Temp { temp_var_data, .. } => { + for &(_, reg) in temp_var_data.use_set.iter() { if !self.is_in_use(reg) { return reg; } @@ -60,7 +270,7 @@ impl DebrayAllocator { for reg in self.temp_lb.. { if !self.is_in_use(reg) { - if !tvd.no_use_set.contains(®) { + if !temp_var_data.no_use_set.contains(reg) { result = reg; break; } @@ -73,10 +283,10 @@ impl DebrayAllocator { } } - fn alloc_with_ca(&self, var: &String) -> usize { - match self.bindings.get(var) { - Some(&VarData::Temp(_, _, ref tvd)) => { - for &(_, reg) in tvd.use_set.iter() { + fn alloc_with_ca(&self, var_num: usize) -> usize { + match &self.var_data.records[var_num].allocation { + VarAlloc::Temp { temp_var_data, .. } => { + for &(_, reg) in temp_var_data.use_set.iter() { if !self.is_in_use(reg) { return reg; } @@ -86,8 +296,8 @@ impl DebrayAllocator { for reg in self.temp_lb.. { if !self.is_in_use(reg) { - if !tvd.no_use_set.contains(®) { - if !tvd.conflict_set.contains(®) { + if !temp_var_data.no_use_set.contains(reg) { + if !temp_var_data.conflict_set.contains(reg) { result = reg; break; } @@ -101,22 +311,25 @@ impl DebrayAllocator { } } - fn alloc_in_last_goal_hint(&self, chunk_num: usize) -> Option<(Rc, usize)> { + fn alloc_in_last_goal_hint(&self, chunk_num: usize) -> Option<(usize, usize)> { // we want to allocate a register to the k^{th} parameter, par_k. // par_k may not be a temporary variable. let k = self.arg_c; - match self.contents.get(&k) { + match self.shallow_temp_mappings.get(&k).cloned() { Some(t_var) => { // suppose this branch fires. then t_var is a // temp. var. belonging to the current chunk. // consider its use set. T == par_k iff // (GenContext::Last(_), k) is in t_var.use_set. - let tvd = self.bindings.get(t_var).unwrap(); - if let &VarData::Temp(_, _, ref tvd) = tvd { - if !tvd.use_set.contains(&(GenContext::Last(chunk_num), k)) { - return Some((t_var.clone(), self.alloc_with_ca(t_var))); + match &self.var_data.records[t_var].allocation { + VarAlloc::Temp { temp_var_data, .. } => { + if !temp_var_data.use_set.contains(&(GenContext::Last(chunk_num), k)) { + return Some((t_var, self.alloc_with_ca(t_var))); + } + } + _ => { } } @@ -129,21 +342,21 @@ impl DebrayAllocator { fn evacuate_arg<'a, Target: CompilationTarget<'a>>( &mut self, chunk_num: usize, - code: &mut Code, + code: &mut CodeDeque, ) { match self.alloc_in_last_goal_hint(chunk_num) { - Some((var, r)) => { + Some((var_num, r)) => { let k = self.arg_c; if r != k { let r = RegType::Temp(r); - code.push(Target::move_to_register(r, k)); + code.push_back(Target::move_to_register(r, k)); - self.contents.swap_remove(&k); - self.contents.insert(r.reg_num(), var.clone()); + self.shallow_temp_mappings.swap_remove(&k); + self.shallow_temp_mappings.insert(r.reg_num(), var_num); - self.record_register(var, r); + self.var_data.records[var_num].allocation.set_register(r.reg_num()); self.in_use.insert(r.reg_num()); } } @@ -153,27 +366,27 @@ impl DebrayAllocator { fn alloc_reg_to_var<'a, Target: CompilationTarget<'a>>( &mut self, - var: &String, + var_num: usize, lvl: Level, term_loc: GenContext, - target: &mut Vec, + target: &mut CodeDeque, ) -> usize { match term_loc { GenContext::Head => { if let Level::Shallow = lvl { self.evacuate_arg::(0, target); - self.alloc_with_cr(var) + self.alloc_with_cr(var_num) } else { - self.alloc_with_ca(var) + self.alloc_with_ca(var_num) } } - GenContext::Mid(_) => self.alloc_with_ca(var), + GenContext::Mid(_) => self.alloc_with_ca(var_num), GenContext::Last(chunk_num) => { if let Level::Shallow = lvl { self.evacuate_arg::(chunk_num, target); - self.alloc_with_cr(var) + self.alloc_with_cr(var_num) } else { - self.alloc_with_ca(var) + self.alloc_with_ca(var_num) } } } @@ -182,38 +395,238 @@ impl DebrayAllocator { fn alloc_reg_to_non_var(&mut self) -> usize { let mut final_index = 0; + while let Some(r) = self.temp_free_list.pop() { + if !self.is_in_use(r) { + self.in_use.insert(r); + return r; + } + } + for index in self.temp_lb.. { - if !self.in_use.contains(&index) { + if !self.in_use.contains(index) { final_index = index; + self.in_use.insert(final_index); break; } } - self.in_use.insert(final_index); self.temp_lb = final_index + 1; final_index } - fn in_place(&self, var: &String, term_loc: GenContext, r: RegType, k: usize) -> bool { + fn in_place(&self, var_num: usize, term_loc: GenContext, r: RegType, k: usize) -> bool { match term_loc { GenContext::Head if !r.is_perm() => r.reg_num() == k, - _ => match self.bindings().get(var).unwrap() { - &VarData::Temp(_, o, _) if r.reg_num() == k => o == k, - _ => false, + _ => { + match &self.var_data.records[var_num].allocation { + &VarAlloc::Temp { temp_reg, .. } if r.reg_num() == k => + temp_reg == k, + _ => false, + } }, } } + + fn alloc_perm_var(&mut self, var_num: usize, chunk_num: usize) -> usize { + let p = if let Some(p) = self.pop_free_perm(chunk_num) { + p + } else { + let p = self.perm_lb; + self.perm_lb += 1; + + p + }; + + self.var_data.records[var_num].allocation = VarAlloc::Perm(p, PermVarAllocation::done()); + p + } + + pub(crate) fn add_reg_to_free_list(&mut self, r: RegType) { + if let RegType::Temp(r) = r { + self.in_use.remove(r); + self.temp_free_list.push(r); + } + } + + pub fn reset_free_list(&mut self) { + self.temp_free_list.clear(); + } + + #[inline(always)] + pub fn get_binding(&self, var_num: usize) -> RegType { + self.var_data.records[var_num].allocation.as_reg_type() + } + + pub fn num_perm_vars(&self) -> usize { + self.perm_lb - 1 + } + + pub fn increment_running_count(&mut self, var_num: usize) { + self.var_data.records[var_num].running_count += 1; + } + + fn add_perm_to_free_list(&mut self, chunk_num: usize, var_num: usize) { + match &self.var_data.records[var_num].allocation { + VarAlloc::Perm(..) => { + self.perm_free_list.push_back((chunk_num, var_num)); + } + _ => {} + } + } + + fn pop_free_perm(&mut self, chunk_num: usize) -> Option { + while let Some((perm_chunk_num, var_num)) = self.perm_free_list.front().cloned() { + if chunk_num > perm_chunk_num { + self.perm_free_list.pop_front(); + + match &mut self.var_data.records[var_num].allocation { + VarAlloc::Perm(p, PermVarAllocation::Pending) if *p > 0 => { + return Some(std::mem::replace(p, 0)); + } + _ => { + } + } + } else { + return None; + } + } + + None + } + + pub(crate) fn free_var(&mut self, chunk_num: usize, var_num: usize) { + match &mut self.var_data.records[var_num].allocation { + VarAlloc::Perm(_, allocation) => { + *allocation = PermVarAllocation::Pending; + self.add_perm_to_free_list(chunk_num, var_num); + } + _ => { + } + } + } + + pub(crate) fn mark_safe_var_unconditionally(&mut self, var_num: usize) { + let branch_designator = self.branch_stack.current_branch_designator(); + + match &mut self.var_data.records[var_num].allocation { + VarAlloc::Perm(_, PermVarAllocation::Done { deep_safety, shallow_safety, .. }) => { + *deep_safety = VarSafetyStatus::unneeded(branch_designator); + *shallow_safety = VarSafetyStatus::unneeded(branch_designator); + } + VarAlloc::Temp { safety, .. } => { + *safety = VarSafetyStatus::unneeded(branch_designator); + } + _ => unreachable!(), + } + } + + fn mark_safe_var(&mut self, var_num: usize, lvl: Level, term_loc: GenContext) { + let branch_designator = self.branch_stack.current_branch_designator(); + + match &mut self.var_data.records[var_num].allocation { + VarAlloc::Perm(_, PermVarAllocation::Done { deep_safety, shallow_safety, .. }) => { + // GetVariable in head chunk is considered safe. + if lvl == Level::Deep { + *deep_safety = VarSafetyStatus::unneeded(branch_designator); + *shallow_safety = VarSafetyStatus::unneeded(branch_designator); + } else if term_loc == GenContext::Head { + *shallow_safety = VarSafetyStatus::GloballyUnneeded; + } else { + if let Some(temp_var_num) = self.shallow_temp_mappings.get(&self.arg_c).cloned() { + match &mut self.var_data.records[temp_var_num].allocation { + VarAlloc::Temp { ref mut to_perm_var_num, .. } => { + *to_perm_var_num = Some(var_num); + } + _ => unreachable!() + } + } + } + } + VarAlloc::Temp { ref mut safety, .. } => { + *safety = VarSafetyStatus::GloballyUnneeded; + } + _ => { + unreachable!() + } + } + } + + fn argument_to_value<'a, Target: CompilationTarget<'a>>( + &mut self, + var_num: usize, + r: RegType, + arg_c: usize, + ) -> Instruction { + let branch_designator = self.branch_stack.current_branch_designator(); + + match &mut self.var_data.records[var_num].allocation { + VarAlloc::Perm(_, PermVarAllocation::Done { ref mut shallow_safety, .. }) => { + if !self.in_tail_position || self.branch_stack.safety_unneeded_in_branch(shallow_safety, &branch_designator) { + Target::argument_to_value(r, arg_c) + } else { + *shallow_safety = VarSafetyStatus::unneeded(branch_designator); + Target::unsafe_argument_to_value(r, arg_c) + } + } + VarAlloc::Temp { ref mut safety, .. } => { + if self.branch_stack.safety_unneeded_in_branch(safety, &branch_designator) { + Target::argument_to_value(r, arg_c) + } else { + *safety = VarSafetyStatus::GloballyUnneeded; + Target::unsafe_argument_to_value(r, arg_c) + } + } + _ => { + unreachable!() + } + } + } + + fn subterm_to_value<'a, Target: CompilationTarget<'a>>( + &mut self, + var_num: usize, + r: RegType, + ) -> Instruction { + let branch_designator = self.branch_stack.current_branch_designator(); + + match &mut self.var_data.records[var_num].allocation { + VarAlloc::Perm(_, PermVarAllocation::Done { ref mut deep_safety, .. }) => { + if self.branch_stack.safety_unneeded_in_branch(deep_safety, &branch_designator) { + Target::subterm_to_value(r) + } else { + *deep_safety = VarSafetyStatus::unneeded(branch_designator); + Target::unsafe_subterm_to_value(r) + } + } + VarAlloc::Temp { ref mut safety, .. } => { + if self.branch_stack.safety_unneeded_in_branch(safety, &branch_designator) { + Target::subterm_to_value(r) + } else { + *safety = VarSafetyStatus::unneeded(branch_designator); + Target::unsafe_subterm_to_value(r) + } + } + _ => { + unreachable!() + } + } + } } impl Allocator for DebrayAllocator { fn new() -> DebrayAllocator { - DebrayAllocator { + Self { + var_data: VarData::default(), + in_tail_position: false, arity: 0, arg_c: 1, temp_lb: 1, - bindings: IndexMap::with_hasher(FxBuildHasher::default()), - contents: IndexMap::with_hasher(FxBuildHasher::default()), - in_use: BTreeSet::new(), + perm_lb: 1, + shallow_temp_mappings: IndexMap::with_hasher(FxBuildHasher::default()), + in_use: BitSet::default(), + temp_free_list: vec![], + perm_free_list: VecDeque::new(), + branch_stack: BranchStack { stack: vec![] } } } @@ -221,12 +634,12 @@ impl Allocator for DebrayAllocator { &mut self, lvl: Level, term_loc: GenContext, - code: &mut Code, + code: &mut CodeDeque, ) { let r = RegType::Temp(self.alloc_reg_to_non_var()); match lvl { - Level::Deep => code.push(Target::subterm_to_variable(r)), + Level::Deep => code.push_back(Target::subterm_to_variable(r)), Level::Root | Level::Shallow => { let k = self.arg_c; @@ -236,7 +649,7 @@ impl Allocator for DebrayAllocator { self.arg_c += 1; - code.push(Target::argument_to_variable(r, k)); + code.push_back(Target::argument_to_variable(r, k)); } }; } @@ -246,7 +659,7 @@ impl Allocator for DebrayAllocator { lvl: Level, term_loc: GenContext, cell: &'a Cell, - code: &mut Code, + code: &mut CodeDeque, ) { let r = cell.get(); @@ -273,39 +686,49 @@ impl Allocator for DebrayAllocator { fn mark_var<'a, Target: CompilationTarget<'a>>( &mut self, - var: Rc, + var_num: usize, lvl: Level, cell: &'a Cell, term_loc: GenContext, - code: &mut Code, + code: &mut CodeDeque, ) { - let (r, is_new_var) = match self.get(var.clone()) { + let (r, is_new_var) = match self.get_binding(var_num) { RegType::Temp(0) => { - // here, r is temporary *and* unassigned. - let o = self.alloc_reg_to_var::(&var, lvl, term_loc, code); + let o = self.alloc_reg_to_var::(var_num, lvl, term_loc, code); cell.set(VarReg::Norm(RegType::Temp(o))); (RegType::Temp(o), true) } RegType::Perm(0) => { - let pr = cell.get().norm(); - self.record_register(var.clone(), pr); + let p = self.alloc_perm_var(var_num, term_loc.chunk_num()); + (RegType::Perm(p), true) + } + r @ RegType::Perm(_) => { + let is_new_var = match &mut self.var_data.records[var_num].allocation { + VarAlloc::Perm(_, allocation) => if allocation.pending() { + *allocation = PermVarAllocation::done(); + true + } else { + false + }, + _ => unreachable!(), + }; - (pr, true) + (r, is_new_var) } r => (r, false), }; - self.mark_reserved_var::(var, lvl, cell, term_loc, code, r, is_new_var); + self.mark_reserved_var::(var_num, lvl, cell, term_loc, code, r, is_new_var); } fn mark_reserved_var<'a, Target: CompilationTarget<'a>>( &mut self, - var: Rc, + var_num: usize, lvl: Level, cell: &'a Cell, term_loc: GenContext, - code: &mut Code, + code: &mut CodeDeque, r: RegType, is_new_var: bool, ) { @@ -313,84 +736,99 @@ impl Allocator for DebrayAllocator { Level::Root | Level::Shallow => { let k = self.arg_c; - if self.is_curr_arg_distinct_from(&var) { + if self.is_curr_arg_distinct_from(var_num) { self.evacuate_arg::(term_loc.chunk_num(), code); } - self.arg_c += 1; - cell.set(VarReg::ArgAndNorm(r, k)); - if !self.in_place(&var, term_loc, r, k) { + if !self.in_place(var_num, term_loc, r, k) { if is_new_var { - code.push(Target::argument_to_variable(r, k)); + self.mark_safe_var(var_num, lvl, term_loc); + code.push_back(Target::argument_to_variable(r, k)); } else { - code.push(Target::argument_to_value(r, k)); + code.push_back(self.argument_to_value::(var_num, r, k)); } } + + self.arg_c += 1; } Level::Deep if is_new_var => { if let GenContext::Head = term_loc { - if self.occurs_shallowly_in_head(&var, r.reg_num()) { - code.push(Target::subterm_to_value(r)); + if self.occurs_shallowly_in_head(var_num, r.reg_num()) { + code.push_back(self.subterm_to_value::(var_num, r)); } else { - code.push(Target::subterm_to_variable(r)); + self.mark_safe_var(var_num, lvl, term_loc); + code.push_back(Target::subterm_to_variable(r)); } } else { - code.push(Target::subterm_to_variable(r)); + self.mark_safe_var(var_num, lvl, term_loc); + code.push_back(Target::subterm_to_variable(r)); } } - Level::Deep => code.push(Target::subterm_to_value(r)), - }; + Level::Deep => code.push_back(self.subterm_to_value::(var_num, r)), + } + + let o = r.reg_num(); if !r.is_perm() { - let o = r.reg_num(); + self.shallow_temp_mappings.insert(o, var_num); + } else if r.is_perm() && is_new_var { + self.branch_stack.add_branch_occurrence(var_num); + } - self.contents.insert(o, var.clone()); - self.record_register(var.clone(), r); - self.in_use.insert(o); + let record = &mut self.var_data.records[var_num]; + + record.allocation.set_register(o); + + if record.running_count < record.num_occurrences { + record.running_count += 1; + } else { + self.free_var(term_loc.chunk_num(), var_num); + } + + self.in_use.insert(o); + } + + fn mark_cut_var(&mut self, var_num: usize, chunk_num: usize) -> RegType { + match self.get_binding(var_num) { + RegType::Perm(0) | RegType::Temp(0) => { + RegType::Perm(self.alloc_perm_var(var_num, chunk_num)) + } + r => r, } } fn reset(&mut self) { - self.bindings.clear(); - self.contents.clear(); + self.perm_lb = 1; + self.shallow_temp_mappings.clear(); self.in_use.clear(); + self.temp_free_list.clear(); } fn reset_contents(&mut self) { - self.contents.clear(); self.in_use.clear(); + self.shallow_temp_mappings.clear(); + self.temp_free_list.clear(); } fn advance_arg(&mut self) { self.arg_c += 1; } - fn bindings(&self) -> &AllocVarDict { - &self.bindings - } - - fn bindings_mut(&mut self) -> &mut AllocVarDict { - &mut self.bindings - } - - fn take_bindings(self) -> AllocVarDict { - self.bindings - } - fn reset_at_head(&mut self, args: &Vec) { self.reset_arg(args.len()); self.arity = args.len(); for (idx, arg) in args.iter().enumerate() { if let &Term::Var(_, ref var) = arg { - let r = self.get(var.clone()); + let var_num = var.to_var_num().unwrap(); + let r = self.get_binding(var_num); if !r.is_perm() && r.reg_num() == 0 { self.in_use.insert(idx + 1); - self.contents.insert(idx + 1, var.clone()); - self.record_register(var.clone(), temp_v!(idx + 1)); + self.shallow_temp_mappings.insert(idx + 1, var_num); + self.var_data.records[var_num].allocation.set_register(idx + 1); } } } diff --git a/src/ffi.rs b/src/ffi.rs new file mode 100644 index 00000000..a48ed62e --- /dev/null +++ b/src/ffi.rs @@ -0,0 +1,444 @@ +/* How does FFI work? + +Each WAM machine has a ForeignFunctionTable instance that contains a table of functions and structs. + +Structs are defined via foreign_struct/2. Basic types are defined by libffi, but struct types need to +be manually defined to get an ffi_type. Additionally, to recover structs from return arguments, we store +fields and atom_fields, as a way to lookup the content of the struct (fields) and the nested structs (atom_fields). + +Functions are defined via use_foreign_module/2. It opens a library and leaks the memory of the library, +to prevent Rust freeing the memory. There's no way to recover that memory at the moment. We get a pointer for +each function and we build a CIF for each one, with the input arguments and the return argument. + +Exec happens via '$foreign_call', we find the function, we try to cast the values that we have to the definition +of the function, we reserve memory for them and we build an array of pointers. To get the return argument, we +reserve enough memory for the return and we build the Scryer values from them. + +Structs are a bit tricky as they need to be aligned. For that, we reserve enough memory (libffi calculates that) +and for each field: we add to the pointer until we're aligned to the next data type we're going to write, we write it, +and finally we add the pointer the size of what we've written. +*/ + +use crate::atom_table::Atom; + +use std::alloc::{alloc, Layout}; +use std::any::Any; +use std::collections::HashMap; +use std::error::Error; +use std::ffi::{CString, c_void}; +use std::convert::TryFrom; + +use libffi::low::{ffi_cif, types, CodePtr, ffi_abi_FFI_DEFAULT_ABI, prep_cif, ffi_type, type_tag}; +use libloading::{Symbol, Library}; + +pub struct FunctionDefinition { + pub name: String, + pub return_value: Atom, + pub args: Vec, +} + +#[derive(Debug)] +pub struct FunctionImpl { + cif: ffi_cif, + args: Vec<*mut ffi_type>, + code_ptr: CodePtr, + return_struct_name: Option, +} + +#[derive(Debug, Default)] +pub struct ForeignFunctionTable { + table: HashMap, + structs: HashMap, +} + +#[derive(Debug, Clone)] +struct StructImpl { + ffi_type: ffi_type, + fields: Vec<*mut ffi_type>, + atom_fields: Vec, +} + +struct PointerArgs { + pointers: Vec<*mut c_void>, + _memory: Vec>, +} + +impl ForeignFunctionTable { + pub fn merge(&mut self, other: ForeignFunctionTable) { + self.table.extend(other.table); + } + + pub fn define_struct(&mut self, name: &str, atom_fields: Vec) { + let mut fields: Vec<_> = atom_fields.iter().map(|x| self.map_type_ffi(&x)).collect(); + fields.push(std::ptr::null_mut::()); + let mut struct_type: ffi_type = Default::default(); + struct_type.type_ = type_tag::STRUCT; + struct_type.elements = fields.as_mut_ptr(); + self.structs.insert(name.to_string(), StructImpl { ffi_type: struct_type, fields, atom_fields}); + } + + fn map_type_ffi(&mut self, source: &Atom) -> *mut ffi_type { + unsafe { + match source { + atom!("sint64") => &mut types::sint64, + atom!("sint32") => &mut types::sint32, + atom!("sint16") => &mut types::sint16, + atom!("sint8") => &mut types::sint8, + atom!("uint64") => &mut types::uint64, + atom!("uint32") => &mut types::uint32, + atom!("uint16") => &mut types::uint16, + atom!("uint8") => &mut types::uint8, + atom!("bool") => &mut types::sint8, + atom!("void") => &mut types::void, + atom!("cstr") => &mut types::pointer, + atom!("ptr") => &mut types::pointer, + atom!("f32") => &mut types::float, + atom!("f64") => &mut types::double, + struct_name => { + match self.structs.get_mut(struct_name.as_str()) { + Some(ref mut struct_type) => { + &mut struct_type.ffi_type + }, + None => unreachable!() + } + } + } + } + } + + pub(crate) fn load_library(&mut self, library_name: &str, functions: &Vec) -> Result<(), Box> { + let mut ff_table: ForeignFunctionTable = Default::default(); + unsafe { + let library = Library::new(library_name)?; + for function in functions { + let symbol_name: CString = CString::new(function.name.clone())?; + let code_ptr: Symbol<*mut c_void> = library.get(&symbol_name.into_bytes_with_nul())?; + let mut args: Vec<_> = function.args.iter().map(|x| self.map_type_ffi(&x)).collect(); + let mut cif: ffi_cif = Default::default(); + prep_cif( + &mut cif, + ffi_abi_FFI_DEFAULT_ABI, + args.len(), + self.map_type_ffi(&function.return_value), + args.as_mut_ptr() + ).unwrap(); + + let return_struct_name = if (*self.map_type_ffi(&function.return_value)).type_ as u32 == libffi::raw::FFI_TYPE_STRUCT { + Some(function.return_value.as_str().to_string()) + } else { + None + }; + + ff_table.table.insert(function.name.clone(), FunctionImpl { + cif, + args, + code_ptr: CodePtr(code_ptr.into_raw().into_raw() as *mut _), + return_struct_name, + }); + } + std::mem::forget(library); + } + self.merge(ff_table); + Ok(()) + } + + fn build_pointer_args(args: &mut Vec, type_args: &Vec<*mut ffi_type>, structs_table: &mut HashMap) -> Result { + let mut pointers = Vec::with_capacity(args.len()); + let mut _memory = Vec::new(); + for i in 0..args.len() { + let field_type = type_args[i]; + unsafe { + macro_rules! push_int { + ($type:ty) => { + { + let n: $type = <$type>::try_from(args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; + let mut box_value = Box::new(n) as Box; + pointers.push(&mut *box_value as *mut _ as *mut c_void); + _memory.push(box_value); + } + } + } + + match (*field_type).type_ as u32 { + libffi::raw::FFI_TYPE_UINT8 => push_int!(u8), + libffi::raw::FFI_TYPE_SINT8 => push_int!(i8), + libffi::raw::FFI_TYPE_UINT16 => push_int!(u16), + libffi::raw::FFI_TYPE_SINT16 => push_int!(i16), + libffi::raw::FFI_TYPE_UINT32 => push_int!(u32), + libffi::raw::FFI_TYPE_SINT32 => push_int!(i32), + libffi::raw::FFI_TYPE_UINT64 => push_int!(u64), + libffi::raw::FFI_TYPE_SINT64 => push_int!(i64), + libffi::raw::FFI_TYPE_FLOAT => { + let n: f32 = args[i].as_float()? as f32; + let mut box_value = Box::new(n) as Box; + pointers.push(&mut *box_value as *mut _ as *mut c_void); + _memory.push(box_value); + }, + libffi::raw::FFI_TYPE_DOUBLE => { + let n: f64 = args[i].as_float()?; + let mut box_value = Box::new(n) as Box; + pointers.push(&mut *box_value as *mut _ as *mut c_void); + _memory.push(box_value); + }, + libffi::raw::FFI_TYPE_POINTER => { + let ptr: *mut c_void = args[i].as_ptr()?; + pointers.push(ptr); + }, + libffi::raw::FFI_TYPE_STRUCT => { + let (mut ptr, _size, _align) = Self::build_struct(&mut args[i], structs_table)?; + pointers.push(&mut *ptr as *mut _ as *mut c_void); + _memory.push(ptr); + }, + _ => return Err(FFIError::InvalidFFIType) + } + } + } + Ok(PointerArgs { + pointers, + _memory + }) + } + + fn build_struct(arg: &mut Value, structs_table: &mut HashMap) -> Result<(Box, usize, usize), FFIError> { + unsafe { + match arg { + Value::Struct(ref name, ref mut struct_args) => { + if let Some(ref mut struct_type) = structs_table.clone().get_mut(name) { + let layout = Layout::from_size_align(struct_type.ffi_type.size, struct_type.ffi_type.alignment.into()).unwrap(); + let align = struct_type.ffi_type.alignment as usize; + let size = struct_type.ffi_type.size; + let ptr = alloc(layout) as *mut c_void; + let mut field_ptr = ptr; + + for i in 0..(struct_type.fields.len()-1) { + macro_rules! try_write_int { + ($type:ty) => { + { + field_ptr = field_ptr.add(field_ptr.align_offset(std::mem::align_of::<$type>())); + let n: $type = <$type>::try_from(struct_args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; + std::ptr::write(field_ptr as *mut $type, n); + field_ptr = field_ptr.add(std::mem::size_of::<$type>()); + } + } + } + + macro_rules! write { + ($type:ty, $value:expr) => { + { + let data: $type = $value; + std::ptr::write(field_ptr as *mut $type, data); + field_ptr = field_ptr.add(align); + } + } + } + + let field = struct_type.fields[i]; + match (*field).type_ as u32 { + libffi::raw::FFI_TYPE_UINT8 => try_write_int!(u8), + libffi::raw::FFI_TYPE_SINT8 => try_write_int!(i8), + libffi::raw::FFI_TYPE_UINT16 => try_write_int!(u16), + libffi::raw::FFI_TYPE_SINT16 => try_write_int!(i16), + libffi::raw::FFI_TYPE_UINT32 => try_write_int!(u32), + libffi::raw::FFI_TYPE_SINT32 => try_write_int!(i32), + libffi::raw::FFI_TYPE_UINT64 => try_write_int!(u64), + libffi::raw::FFI_TYPE_SINT64 => try_write_int!(i64), + libffi::raw::FFI_TYPE_POINTER => write!(*mut c_void, struct_args[i].as_ptr()?), + libffi::raw::FFI_TYPE_FLOAT => write!(f32, struct_args[i].as_float()? as f32), + libffi::raw::FFI_TYPE_DOUBLE => write!(f64, struct_args[i].as_float()?), + libffi::raw::FFI_TYPE_STRUCT => { + let (struct_ptr, struct_size, struct_align) = Self::build_struct(&mut struct_args[i], structs_table)?; + field_ptr = field_ptr.add(field_ptr.align_offset(struct_align)); + + std::ptr::copy(& *struct_ptr as *const _ as *const c_void, field_ptr as *mut c_void, struct_size); + field_ptr = field_ptr.add(struct_size); + }, + _ => { + unreachable!() + } + } + } + return Ok((Box::from_raw(ptr), size, align)); + } else { + return Err(FFIError::InvalidStructName); + } + } + _ => return Err(FFIError::ValueCast) + } + } + } + + pub fn exec(&mut self, name: &str, mut args: Vec) -> Result { + let function_impl = self.table.get_mut(name).ok_or(FFIError::FunctionNotFound)?; + let mut pointer_args = Self::build_pointer_args(&mut args, &function_impl.args, &mut self.structs)?; + + return unsafe { + macro_rules! call_and_return { + ($type:ty) => { + { + let mut n: Box = Box::new(0); + libffi::raw::ffi_call( + &mut function_impl.cif, + Some(*function_impl.code_ptr.as_safe_fun()), + &mut *n as *mut _ as *mut c_void, + pointer_args.pointers.as_mut_ptr() as *mut *mut c_void + ); + Ok(Value::Int(i64::from(*n))) + } + } + } + + match (*function_impl.cif.rtype).type_ as u32 { + libffi::raw::FFI_TYPE_VOID => call_and_return!(i32), + libffi::raw::FFI_TYPE_UINT8 => call_and_return!(u8), + libffi::raw::FFI_TYPE_SINT8 => call_and_return!(i8), + libffi::raw::FFI_TYPE_UINT16 => call_and_return!(u16), + libffi::raw::FFI_TYPE_SINT16 => call_and_return!(i16), + libffi::raw::FFI_TYPE_UINT32 => call_and_return!(u32), + libffi::raw::FFI_TYPE_SINT32 => call_and_return!(i32), + libffi::raw::FFI_TYPE_UINT64 => { + let mut n: Box = Box::new(0); + libffi::raw::ffi_call( + &mut function_impl.cif, + Some(*function_impl.code_ptr.as_safe_fun()), + &mut *n as *mut _ as *mut c_void, + pointer_args.pointers.as_mut_ptr() as *mut *mut c_void + ); + Ok(Value::Int(i64::try_from(*n).map_err(|_| FFIError::ValueDontFit)?)) + }, + libffi::raw::FFI_TYPE_SINT64 => call_and_return!(i64), + libffi::raw::FFI_TYPE_POINTER => call_and_return!(*mut c_void), + libffi::raw::FFI_TYPE_FLOAT => { + let mut n: Box = Box::new(0.0); + libffi::raw::ffi_call( + &mut function_impl.cif, + Some(*function_impl.code_ptr.as_safe_fun()), + &mut *n as *mut _ as *mut c_void, + pointer_args.pointers.as_mut_ptr() as *mut *mut c_void + ); + Ok(Value::Float((*n).into())) + }, + libffi::raw::FFI_TYPE_DOUBLE => { + let mut n: Box = Box::new(0.0); + libffi::raw::ffi_call( + &mut function_impl.cif, + Some(*function_impl.code_ptr.as_safe_fun()), + &mut *n as *mut _ as *mut c_void, + pointer_args.pointers.as_mut_ptr() as *mut *mut c_void + ); + Ok(Value::Float(*n)) + }, + libffi::raw::FFI_TYPE_STRUCT => { + let name = &function_impl.return_struct_name.clone().ok_or(FFIError::StructNotFound)?; + let struct_type = self.structs.get(name).ok_or(FFIError::StructNotFound)?; + let layout = Layout::from_size_align(struct_type.ffi_type.size, struct_type.ffi_type.alignment.into()).unwrap(); + let ptr = alloc(layout) as *mut c_void; + + libffi::raw::ffi_call( + &mut function_impl.cif, + Some(*function_impl.code_ptr.as_safe_fun()), + &mut *ptr as *mut _ as *mut c_void, + pointer_args.pointers.as_mut_ptr() as *mut *mut c_void + ); + let struct_val = self.read_struct(ptr, name, struct_type); + drop(Box::from_raw(ptr)); + struct_val + } + _ => unreachable!() + } + }; + } + + fn read_struct(&self, ptr: *mut c_void, name: &str, struct_type: &StructImpl) -> Result { + unsafe { + let mut returns = Vec::new(); + let mut field_ptr = ptr; + + for i in 0..(struct_type.fields.len()-1) { + let field = struct_type.fields[i]; + + macro_rules! read_and_push_int { + ($type:ty) => { + { + field_ptr = field_ptr.add(field_ptr.align_offset(std::mem::align_of::<$type>())); + let n = std::ptr::read(field_ptr as *mut $type); + returns.push(Value::Int(i64::from(n))); + field_ptr = field_ptr.add(std::mem::size_of::<$type>()); + } + } + } + + match (*field).type_ as u32 { + libffi::raw::FFI_TYPE_UINT8 => read_and_push_int!(u8), + libffi::raw::FFI_TYPE_SINT8 => read_and_push_int!(i8), + libffi::raw::FFI_TYPE_UINT16 => read_and_push_int!(u16), + libffi::raw::FFI_TYPE_SINT16 => read_and_push_int!(i16), + libffi::raw::FFI_TYPE_UINT32 => read_and_push_int!(u32), + libffi::raw::FFI_TYPE_SINT32 => read_and_push_int!(i32), + libffi::raw::FFI_TYPE_UINT64 => { + field_ptr = field_ptr.add(field_ptr.align_offset(std::mem::align_of::())); + let n = std::ptr::read(field_ptr as *mut u64); + returns.push(Value::Int(i64::try_from(n).map_err(|_| FFIError::ValueDontFit)?)); + field_ptr = field_ptr.add(std::mem::size_of::()); + }, + libffi::raw::FFI_TYPE_SINT64 => read_and_push_int!(i64), + libffi::raw::FFI_TYPE_POINTER => read_and_push_int!(i64), + libffi::raw::FFI_TYPE_STRUCT => { + let substruct = struct_type.atom_fields[i].as_str(); + let struct_type = self.structs.get(substruct).ok_or(FFIError::StructNotFound)?; + field_ptr = field_ptr.add(field_ptr.align_offset(struct_type.ffi_type.alignment as usize)); + let struct_val = self.read_struct(field_ptr, substruct, struct_type); + returns.push(struct_val?); + field_ptr = field_ptr.add(struct_type.ffi_type.size); + }, + _ => { + unreachable!() + } + } + } + Ok(Value::Struct(name.into(), returns)) + } + } +} + +#[derive(Clone, Debug)] +pub enum Value { + Int(i64), + Float(f64), + CString(CString), + Struct(String, Vec), +} + +impl Value { + fn as_int(&self) -> Result { + match self { + Value::Int(n) => Ok(*n), + _ => Err(FFIError::ValueCast), + } + } + + fn as_float(&self) -> Result { + match self { + Value::Float(n) => Ok(*n), + Value::Int(n) => Ok(*n as f64), + _ => Err(FFIError::ValueCast), + } + } + + fn as_ptr(&mut self) -> Result<*mut c_void, FFIError> { + match self { + Value::CString(ref mut cstr) => Ok(&mut *cstr as *mut _ as *mut c_void), + Value::Int(n) => Ok(*n as *mut c_void), + _ => Err(FFIError::ValueCast) + } + } +} + +#[derive(Debug)] +pub enum FFIError { + ValueCast, + ValueDontFit, + InvalidFFIType, + InvalidStructName, + FunctionNotFound, + StructNotFound, +} diff --git a/src/fixtures.rs b/src/fixtures.rs deleted file mode 100644 index 43c3ace8..00000000 --- a/src/fixtures.rs +++ /dev/null @@ -1,320 +0,0 @@ -use crate::parser::ast::*; - -use crate::forms::*; -use crate::instructions::*; -use crate::iterators::*; - -use indexmap::{IndexMap, IndexSet}; - -use std::cell::Cell; -use std::collections::BTreeSet; -use std::mem::swap; -use std::rc::Rc; -use std::vec::Vec; - -// labeled with chunk numbers. -#[derive(Debug)] -pub(crate) enum VarStatus { - Perm(usize), - Temp(usize, TempVarData), // Perm(chunk_num) | Temp(chunk_num, _) -} - -pub(crate) type OccurrenceSet = BTreeSet<(GenContext, usize)>; - -// Perm: 0 initially, a stack register once processed. -// Temp: labeled with chunk_num and temp offset (unassigned if 0). -#[derive(Debug)] -pub(crate) enum VarData { - Perm(usize), - Temp(usize, usize, TempVarData), -} - -impl VarData { - pub(crate) fn as_reg_type(&self) -> RegType { - match self { - &VarData::Temp(_, r, _) => RegType::Temp(r), - &VarData::Perm(r) => RegType::Perm(r), - } - } -} - -#[derive(Debug)] -pub(crate) struct TempVarData { - pub(crate) last_term_arity: usize, - pub(crate) use_set: OccurrenceSet, - pub(crate) no_use_set: BTreeSet, - pub(crate) conflict_set: BTreeSet, -} - -impl TempVarData { - pub(crate) fn new(last_term_arity: usize) -> Self { - TempVarData { - last_term_arity: last_term_arity, - use_set: BTreeSet::new(), - no_use_set: BTreeSet::new(), - conflict_set: BTreeSet::new(), - } - } - - pub(crate) fn uses_reg(&self, reg: usize) -> bool { - for &(_, nreg) in self.use_set.iter() { - if reg == nreg { - return true; - } - } - - return false; - } - - pub(crate) fn populate_conflict_set(&mut self) { - if self.last_term_arity > 0 { - let arity = self.last_term_arity; - let mut conflict_set: BTreeSet = (1..arity).collect(); - - for &(_, reg) in self.use_set.iter() { - conflict_set.remove(®); - } - - self.conflict_set = conflict_set; - } - } -} - -type VariableFixture<'a> = (VarStatus, Vec<&'a Cell>); - -#[derive(Debug)] -pub(crate) struct VariableFixtures<'a> { - perm_vars: IndexMap, VariableFixture<'a>>, - last_chunk_temp_vars: IndexSet>, -} - -impl<'a> VariableFixtures<'a> { - pub(crate) fn new() -> Self { - VariableFixtures { - perm_vars: IndexMap::new(), - last_chunk_temp_vars: IndexSet::new(), - } - } - - pub(crate) fn insert(&mut self, var: Rc, vs: VariableFixture<'a>) { - self.perm_vars.insert(var, vs); - } - - pub(crate) fn insert_last_chunk_temp_var(&mut self, var: Rc) { - self.last_chunk_temp_vars.insert(var); - } - - // computes no_use and conflict sets for all temp vars. - pub(crate) fn populate_restricting_sets(&mut self) { - // three stages: - // 1. move the use sets of each variable to a local IndexMap, use_set - // (iterate mutably, swap mutable refs). - // 2. drain use_set. For each use set of U, add into the - // no-use sets of appropriate variables T =/= U. - // 3. Move the use sets back to their original locations in the fixture. - // Compute the conflict set of u. - - // 1. - let mut use_sets: IndexMap, OccurrenceSet> = IndexMap::new(); - - for (var, &mut (ref mut var_status, _)) in self.iter_mut() { - if let &mut VarStatus::Temp(_, ref mut var_data) = var_status { - let mut use_set = OccurrenceSet::new(); - - swap(&mut var_data.use_set, &mut use_set); - use_sets.insert((*var).clone(), use_set); - } - } - - for (u, use_set) in use_sets.drain(..) { - // 2. - for &(term_loc, reg) in use_set.iter() { - if let GenContext::Last(cn_u) = term_loc { - for (ref t, &mut (ref mut var_status, _)) in self.iter_mut() { - if let &mut VarStatus::Temp(cn_t, ref mut t_data) = var_status { - if cn_u == cn_t && *u != ***t { - if !t_data.uses_reg(reg) { - t_data.no_use_set.insert(reg); - } - } - } - } - } - } - - // 3. - match self.get_mut(u).unwrap() { - &mut (VarStatus::Temp(_, ref mut u_data), _) => { - u_data.use_set = use_set; - u_data.populate_conflict_set(); - } - _ => {} - }; - } - } - - fn get_mut(&mut self, u: Rc) -> Option<&mut VariableFixture<'a>> { - self.perm_vars.get_mut(&u) - } - - fn iter_mut(&mut self) -> indexmap::map::IterMut, VariableFixture<'a>> { - self.perm_vars.iter_mut() - } - - fn record_temp_info(&mut self, tvd: &mut TempVarData, arg_c: usize, term_loc: GenContext) { - match term_loc { - GenContext::Head | GenContext::Last(_) => { - tvd.use_set.insert((term_loc, arg_c)); - } - _ => {} - }; - } - - pub(crate) fn vars_above_threshold(&self, index: usize) -> usize { - let mut var_count = 0; - - for &(ref var_status, _) in self.values() { - if let &VarStatus::Perm(i) = var_status { - if i > index { - var_count += 1; - } - } - } - - var_count - } - - pub(crate) fn mark_vars_in_chunk(&mut self, iter: I, lt_arity: usize, term_loc: GenContext) - where - I: Iterator>, - { - let chunk_num = term_loc.chunk_num(); - let mut arg_c = 1; - - for term_ref in iter { - if let &TermRef::Var(lvl, cell, ref var) = &term_ref { - let mut status = self.perm_vars.swap_remove(var).unwrap_or(( - VarStatus::Temp(chunk_num, TempVarData::new(lt_arity)), - Vec::new(), - )); - - status.1.push(cell); - - match status.0 { - VarStatus::Temp(cn, ref mut tvd) if cn == chunk_num => { - if let Level::Shallow = lvl { - self.record_temp_info(tvd, arg_c, term_loc); - } - } - _ => status.0 = VarStatus::Perm(chunk_num), - }; - - self.perm_vars.insert(var.clone(), status); - } - - if let Level::Shallow = term_ref.level() { - arg_c += 1; - } - } - } - - pub(crate) fn into_iter(self) -> indexmap::map::IntoIter, VariableFixture<'a>> { - self.perm_vars.into_iter() - } - - fn values(&self) -> indexmap::map::Values, VariableFixture<'a>> { - self.perm_vars.values() - } - - pub(crate) fn size(&self) -> usize { - self.perm_vars.len() - } - - pub(crate) fn set_perm_vals(&self, has_deep_cuts: bool) { - let mut values_vec: Vec<_> = self - .values() - .filter_map(|ref v| match &v.0 { - &VarStatus::Perm(i) => Some((i, &v.1)), - _ => None, - }) - .collect(); - - values_vec.sort_by_key(|ref v| v.0); - - let offset = has_deep_cuts as usize; - - for (i, (_, cells)) in values_vec.into_iter().rev().enumerate() { - for cell in cells { - cell.set(VarReg::Norm(RegType::Perm(i + 1 + offset))); - } - } - } -} - -#[derive(Debug)] -pub(crate) struct UnsafeVarMarker { - pub(crate) unsafe_vars: IndexMap, - pub(crate) safe_vars: IndexSet, -} - -impl UnsafeVarMarker { - pub(crate) fn new() -> Self { - UnsafeVarMarker { - unsafe_vars: IndexMap::new(), - safe_vars: IndexSet::new(), - } - } - - pub(crate) fn from_safe_vars(safe_vars: IndexSet) -> Self { - UnsafeVarMarker { - unsafe_vars: IndexMap::new(), - safe_vars, - } - } - - pub(crate) fn mark_safe_vars(&mut self, query_instr: &Instruction) -> bool { - match query_instr { - &Instruction::PutVariable(r @ RegType::Temp(_), _) | - &Instruction::SetVariable(r) => { - self.safe_vars.insert(r); - true - } - _ => false, - } - } - - pub(crate) fn mark_phase(&mut self, query_instr: &Instruction, phase: usize) { - match query_instr { - &Instruction::PutValue(r @ RegType::Perm(_), _) | - &Instruction::SetValue(r) => { - let p = self.unsafe_vars.entry(r).or_insert(0); - *p = phase; - } - _ => {} - } - } - - pub(crate) fn mark_unsafe_vars(&mut self, query_instr: &mut Instruction, phase: usize) { - match query_instr { - &mut Instruction::PutValue(RegType::Perm(i), arg) => { - if let Some(p) = self.unsafe_vars.swap_remove(&RegType::Perm(i)) { - if p == phase { - *query_instr = Instruction::PutUnsafeValue(i, arg); - self.safe_vars.insert(RegType::Perm(i)); - } else { - self.unsafe_vars.insert(RegType::Perm(i), p); - } - } - } - &mut Instruction::SetValue(r) => { - if !self.safe_vars.contains(&r) { - *query_instr = Instruction::SetLocalValue(r); - - self.safe_vars.insert(r); - self.unsafe_vars.remove(&r); - } - } - _ => {} - } - } -} diff --git a/src/forms.rs b/src/forms.rs index 97864370..fd9c1e28 100644 --- a/src/forms.rs +++ b/src/forms.rs @@ -1,13 +1,14 @@ use crate::arena::*; use crate::atom_table::*; use crate::instructions::*; +use crate::machine::disjuncts::VarData; use crate::machine::heap::*; use crate::machine::loader::PredicateQueue; use crate::machine::machine_errors::*; use crate::machine::machine_indices::*; use crate::parser::ast::*; use crate::parser::parser::CompositeOpDesc; -use crate::parser::rug::{Integer, Rational}; +use crate::parser::dashu::{Integer, Rational}; use crate::types::*; use fxhash::FxBuildHasher; @@ -19,26 +20,23 @@ use std::cell::Cell; use std::collections::VecDeque; use std::convert::TryFrom; use std::fmt; -use std::ops::AddAssign; +use std::ops::{AddAssign, Deref, DerefMut}; use std::path::PathBuf; -use std::rc::Rc; use crate::{is_infix, is_postfix}; pub type PredicateKey = (Atom, usize); // name, arity. -pub type Predicate = Vec; - +/* // vars of predicate, toplevel offset. Vec is always a vector // of vars (we get their adjoining cells this way). pub type JumpStub = Vec; +*/ -#[derive(Debug, Clone)] +#[derive(Debug)] pub enum TopLevel { - Fact(Term), // Term, line_num, col_num - Predicate(Predicate), - Query(Vec), - Rule(Rule), // Rule, line_num, col_num + Fact(Fact, VarData), // Term, line_num, col_num + Rule(Rule, VarData), // Rule, line_num, col_num } #[derive(Debug, Clone, Copy)] @@ -57,7 +55,13 @@ impl AppendOrPrepend { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy)] +pub enum VarComparison { + Indistinct, + Distinct +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Level { Deep, Root, @@ -79,38 +83,144 @@ pub enum CallPolicy { Counted, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum ChunkType { + Head, + Mid, + Last, +} + +#[derive(Debug)] +pub enum RootIterationPolicy { + Iterated, + NotIterated, +} + +impl RootIterationPolicy { + #[inline(always)] + pub fn iterable(&self) -> bool { + if let RootIterationPolicy::Iterated = self { + true + } else { + false + } + } +} + +impl ChunkType { + #[inline(always)] + pub fn to_gen_context(self, chunk_num: usize) -> GenContext { + match self { + ChunkType::Head => GenContext::Head, + ChunkType::Mid => GenContext::Mid(chunk_num), + ChunkType::Last => GenContext::Last(chunk_num), + } + } + + #[inline(always)] + pub fn is_last(self) -> bool { + self == ChunkType::Last + } +} + +#[derive(Debug)] +pub enum ChunkedTerms { + Branch(Vec>), + Chunk(VecDeque), +} + +#[derive(Debug)] +pub struct ChunkedTermVec { + pub chunk_vec: VecDeque, +} + +impl Deref for ChunkedTermVec { + type Target = VecDeque; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.chunk_vec + } +} + +impl DerefMut for ChunkedTermVec { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.chunk_vec + } +} + +impl ChunkedTermVec { + #[inline] + pub fn new() -> Self { + Self { chunk_vec: VecDeque::new() } + } + + pub fn reserve_branch(&mut self, capacity: usize) { + self.chunk_vec.push_back(ChunkedTerms::Branch(Vec::with_capacity(capacity))); + } + + pub fn push_branch_arm(&mut self, branch: VecDeque) { + match self.chunk_vec.back_mut().unwrap() { + ChunkedTerms::Branch(branches) => { + branches.push(branch); + } + ChunkedTerms::Chunk(_) => { + self.chunk_vec.push_back(ChunkedTerms::Branch(vec![branch])); + } + } + } + + #[inline] + pub fn add_chunk(&mut self) { + self.chunk_vec.push_back(ChunkedTerms::Chunk(VecDeque::from(vec![]))); + } + + pub fn push_chunk_term(&mut self, term: QueryTerm) { + match self.chunk_vec.back_mut() { + Some(ChunkedTerms::Branch(_)) => { + self.chunk_vec.push_back(ChunkedTerms::Chunk(VecDeque::from(vec![term]))); + } + Some(ChunkedTerms::Chunk(chunk)) => { + chunk.push_back(term); + } + None => { + self.chunk_vec.push_back(ChunkedTerms::Chunk(VecDeque::from(vec![term]))); + } + } + } +} + +#[derive(Debug)] pub enum QueryTerm { // register, clause type, subterms, clause call policy. Clause(Cell, ClauseType, Vec, CallPolicy), - BlockedCut, // a cut which is 'blocked by letters', like the P term in P -> Q. - UnblockedCut(Cell), - GetLevelAndUnify(Cell, Rc), - Jump(JumpStub), + Fail, + LocalCut(usize), // var_num + GlobalCut(usize), // var_num + GetCutPoint { var_num: usize, prev_b: bool }, + GetLevel(usize), // var_num } impl QueryTerm { - pub(crate) fn set_call_policy(&mut self, cp: CallPolicy) { - match self { - &mut QueryTerm::Clause(_, _, _, ref mut clause_cp) => *clause_cp = cp, - _ => {} - } - } - pub(crate) fn arity(&self) -> usize { match self { &QueryTerm::Clause(_, _, ref subterms, ..) => subterms.len(), - &QueryTerm::BlockedCut | &QueryTerm::UnblockedCut(..) => 0, - &QueryTerm::Jump(ref vars) => vars.len(), - &QueryTerm::GetLevelAndUnify(..) => 1, + &QueryTerm::GetLevel(_) | &QueryTerm::GetCutPoint { .. } => 1, + _ => 0, } } } -#[derive(Debug, Clone)] +#[derive(Debug)] +pub struct Fact { + pub(crate) head: Term, +} + +#[derive(Debug)] pub struct Rule { - pub(crate) head: (Atom, Vec, QueryTerm), - pub(crate) clauses: Vec, + pub(crate) head: (Atom, Vec), + pub(crate) clauses: ChunkedTermVec, } #[derive(Clone, Debug, Hash)] @@ -201,29 +311,29 @@ impl ClauseInfo for Rule { impl ClauseInfo for PredicateClause { fn name(&self) -> Option { match self { - &PredicateClause::Fact(ref term, ..) => term.name(), + &PredicateClause::Fact(ref term, ..) => term.head.name(), &PredicateClause::Rule(ref rule, ..) => rule.name(), } } fn arity(&self) -> usize { match self { - &PredicateClause::Fact(ref term, ..) => term.arity(), + &PredicateClause::Fact(ref term, ..) => term.head.arity(), &PredicateClause::Rule(ref rule, ..) => rule.arity(), } } } -#[derive(Debug, Clone)] +#[derive(Debug)] pub enum PredicateClause { - Fact(Term), - Rule(Rule), + Fact(Fact, VarData), + Rule(Rule, VarData), } impl PredicateClause { pub(crate) fn args(&self) -> Option<&[Term]> { match self { - PredicateClause::Fact(term, ..) => match term { + PredicateClause::Fact(term, ..) => match &term.head { Term::Clause(_, _, args) => Some(&args), _ => None, }, @@ -661,9 +771,9 @@ impl Number { pub(crate) fn is_positive(&self) -> bool { match self { &Number::Fixnum(n) => n.get_num() > 0, - &Number::Integer(ref n) => &**n > &0, + &Number::Integer(ref n) => &**n > &Integer::from(0), &Number::Float(f) => f.is_sign_positive(), - &Number::Rational(ref r) => &**r > &0, + &Number::Rational(ref r) => &**r > &Rational::from(0), } } @@ -671,9 +781,9 @@ impl Number { pub(crate) fn is_negative(&self) -> bool { match self { &Number::Fixnum(n) => n.get_num() < 0, - &Number::Integer(ref n) => &**n < &0, + &Number::Integer(ref n) => &**n < &Integer::from(0), &Number::Float(OrderedFloat(f)) => f.is_sign_negative() && OrderedFloat(f) != -0f64, - &Number::Rational(ref r) => &**r < &0, + &Number::Rational(ref r) => &**r < &Rational::from(0), } } @@ -681,9 +791,9 @@ impl Number { pub(crate) fn is_zero(&self) -> bool { match self { &Number::Fixnum(n) => n.get_num() == 0, - &Number::Integer(ref n) => &**n == &0, + &Number::Integer(ref n) => &**n == &Integer::from(0), &Number::Float(f) => f == OrderedFloat(0f64) || f == OrderedFloat(-0f64), - &Number::Rational(ref r) => &**r == &0, + &Number::Rational(ref r) => &**r == &Rational::from(0), } } @@ -812,8 +922,9 @@ impl PredicateInfo { } #[inline] - pub(crate) fn must_retract_local_clauses(&self) -> bool { - self.is_extensible && self.has_clauses && !self.is_discontiguous + pub(crate) fn must_retract_local_clauses(&self, is_cross_module_clause: bool) -> bool { + self.is_extensible && self.has_clauses && !self.is_discontiguous && + !(self.is_multifile && is_cross_module_clause) } } diff --git a/src/heap_iter.rs b/src/heap_iter.rs index 9760be9e..c20840b7 100644 --- a/src/heap_iter.rs +++ b/src/heap_iter.rs @@ -1,8 +1,9 @@ #[cfg(test)] pub(crate) use crate::machine::gc::{IteratorUMP, StacklessPreOrderHeapIter}; -use crate::machine::heap::*; use crate::atom_table::*; +use crate::machine::heap::*; +use crate::machine::stack::*; use crate::types::*; use modular_bitfield::prelude::*; @@ -18,28 +19,45 @@ enum IterStackLocTag { PendingMark, } +#[derive(BitfieldSpecifier, Clone, Copy, Debug, PartialEq, Eq)] +#[bits = 1] +pub enum HeapOrStackTag { + Heap, + Stack, +} + #[bitfield] #[repr(u64)] #[derive(Clone, Copy, Debug)] pub struct IterStackLoc { - value: B62, + pub value: B61, tag: IterStackLocTag, + heap_or_stack: HeapOrStackTag, } impl IterStackLoc { #[inline] - pub fn iterable_heap_loc(h: usize) -> Self { - IterStackLoc::new().with_tag(IterStackLocTag::Iterable).with_value(h as u64) + pub fn iterable_loc(h: usize, heap_or_stack: HeapOrStackTag) -> Self { + IterStackLoc::new() + .with_tag(IterStackLocTag::Iterable) + .with_heap_or_stack(heap_or_stack) + .with_value(h as u64) } #[inline] - pub fn mark_heap_loc(h: usize) -> Self { - IterStackLoc::new().with_tag(IterStackLocTag::Marked).with_value(h as u64) + fn mark_loc(h: usize, heap_or_stack: HeapOrStackTag) -> Self { + IterStackLoc::new() + .with_tag(IterStackLocTag::Marked) + .with_heap_or_stack(heap_or_stack) + .with_value(h as u64) } #[inline] - pub fn pending_mark_heap_loc(h: usize) -> Self { - IterStackLoc::new().with_tag(IterStackLocTag::PendingMark).with_value(h as u64) + fn pending_mark_loc(h: usize, heap_or_stack: HeapOrStackTag) -> Self { + IterStackLoc::new() + .with_tag(IterStackLocTag::PendingMark) + .with_heap_or_stack(heap_or_stack) + .with_value(h as u64) } #[inline] @@ -51,38 +69,35 @@ impl IterStackLoc { pub fn is_pending_mark(self) -> bool { self.tag() == IterStackLocTag::PendingMark } -} -#[inline] -fn forward_if_referent_marked(heap: &mut [HeapCellValue], h: usize) { - read_heap_cell!(heap[h], - (HeapCellValueTag::Str - | HeapCellValueTag::Lis - | HeapCellValueTag::AttrVar - | HeapCellValueTag::Var - | HeapCellValueTag::PStrLoc, vh) => { - if heap[vh].get_mark_bit() { - heap[h].set_forwarding_bit(true); + #[inline] + pub fn as_ref(self) -> Ref { + match self.heap_or_stack() { + HeapOrStackTag::Heap => { + Ref::heap_cell(self.value() as usize) + } + HeapOrStackTag::Stack => { + Ref::stack_cell(self.value() as usize) } } - _ => {} - ) + } } #[derive(Debug)] pub struct StackfulPreOrderHeapIter<'a> { pub heap: &'a mut Vec, + pub machine_stack: &'a mut Stack, stack: Vec, - h: usize, + h: IterStackLoc, } impl<'a> Drop for StackfulPreOrderHeapIter<'a> { fn drop(&mut self) { while let Some(h) = self.stack.pop() { - let h = h.value() as usize; + let cell = self.read_cell_mut(h); - self.heap[h].set_forwarding_bit(false); - self.heap[h].set_mark_bit(false); + cell.set_forwarding_bit(false); + cell.set_mark_bit(false); } self.heap.pop(); @@ -90,48 +105,93 @@ impl<'a> Drop for StackfulPreOrderHeapIter<'a> { } pub trait FocusedHeapIter: Iterator { - fn focus(&self) -> usize; + fn focus(&self) -> IterStackLoc; } impl<'a> FocusedHeapIter for StackfulPreOrderHeapIter<'a> { #[inline] - fn focus(&self) -> usize { + fn focus(&self) -> IterStackLoc { self.h } } impl<'a> StackfulPreOrderHeapIter<'a> { #[inline] - fn new(heap: &'a mut Vec, cell: HeapCellValue) -> Self { - let h = heap.len(); + fn new(heap: &'a mut Vec, stack: &'a mut Stack, cell: HeapCellValue) -> Self { + let h = IterStackLoc::iterable_loc(heap.len(), HeapOrStackTag::Heap); heap.push(cell); Self { heap, h, - stack: vec![IterStackLoc::iterable_heap_loc(h)], + machine_stack: stack, + stack: vec![h], } } #[inline] - pub fn push_stack(&mut self, h: usize) { - self.stack.push(IterStackLoc::iterable_heap_loc(h)); + fn forward_if_referent_marked(&mut self, loc: IterStackLoc) { + read_heap_cell!(self.read_cell(loc), + (HeapCellValueTag::Str | + HeapCellValueTag::Lis | + HeapCellValueTag::AttrVar | + HeapCellValueTag::Var | + HeapCellValueTag::PStrLoc, vh) => { + if self.heap[vh].get_mark_bit() { + self.read_cell_mut(loc).set_forwarding_bit(true); + } + } + (HeapCellValueTag::StackVar, vs) => { + if self.machine_stack[vs].get_mark_bit() { + self.read_cell_mut(loc).set_forwarding_bit(true); + } + } + _ => {} + ); } #[inline] - pub fn stack_last(&self) -> Option { + pub fn push_stack(&mut self, h: IterStackLoc) { + self.stack.push(h); + } + + #[inline] + pub fn read_cell_mut(&mut self, loc: IterStackLoc) -> &mut HeapCellValue { + match loc.heap_or_stack() { + HeapOrStackTag::Heap => { + &mut self.heap[loc.value() as usize] + } + HeapOrStackTag::Stack => { + &mut self.machine_stack[loc.value() as usize] + } + } + } + + #[inline] + pub fn read_cell(&self, loc: IterStackLoc) -> HeapCellValue { + match loc.heap_or_stack() { + HeapOrStackTag::Heap => { + self.heap[loc.value() as usize] + } + HeapOrStackTag::Stack => { + self.machine_stack[loc.value() as usize] + } + } + } + + #[inline] + pub fn stack_last(&self) -> Option { for h in self.stack.iter().rev() { let is_readable_marked = h.is_marked(); - let h = h.value() as usize; - let cell = self.heap[h]; + let cell = self.read_cell(*h); if cell.get_forwarding_bit() { - return Some(h); + return Some(*h); } else if cell.get_mark_bit() && !is_readable_marked { continue; } - return Some(h); + return Some(*h); } None @@ -141,10 +201,9 @@ impl<'a> StackfulPreOrderHeapIter<'a> { pub fn pop_stack(&mut self) -> Option { while let Some(h) = self.stack.pop() { let is_readable_marked = h.is_marked(); - let h = h.value() as usize; - self.h = h; - let cell = &mut self.heap[h]; + self.h = h; + let cell = self.read_cell_mut(h); if cell.get_forwarding_bit() { cell.set_forwarding_bit(false); @@ -159,30 +218,34 @@ impl<'a> StackfulPreOrderHeapIter<'a> { None } - fn push_if_unmarked(&mut self, h: usize) { - if !self.heap[h].get_mark_bit() { - self.heap[h].set_mark_bit(true); - self.stack.push(IterStackLoc::iterable_heap_loc(h)); + #[inline] + pub fn stack_len(&self) -> usize { + self.stack.len() + } + + fn push_if_unmarked(&mut self, loc: IterStackLoc) { + let cell = self.read_cell_mut(loc); + + if !cell.get_mark_bit() { + cell.set_mark_bit(true); + self.stack.push(IterStackLoc::iterable_loc(loc.value() as usize, loc.heap_or_stack())); } } fn follow(&mut self) -> Option { while let Some(h) = self.stack.pop() { if h.is_pending_mark() { - let h = h.value() as usize; - self.push_if_unmarked(h); - self.stack.push(IterStackLoc::mark_heap_loc(h)); + self.stack.push(IterStackLoc::mark_loc(h.value() as usize, h.heap_or_stack())); - forward_if_referent_marked(&mut self.heap, h); + self.forward_if_referent_marked(h); continue; } - let is_readable_marked = h.is_marked(); - let h = h.value() as usize; - self.h = h; - let cell = &mut self.heap[h]; + + let is_readable_marked = h.is_marked(); + let cell = self.read_cell_mut(h); if cell.get_forwarding_bit() { let copy = *cell; @@ -195,50 +258,68 @@ impl<'a> StackfulPreOrderHeapIter<'a> { read_heap_cell!(*cell, (HeapCellValueTag::Str | HeapCellValueTag::PStrLoc, vh) => { - self.push_if_unmarked(vh); - self.stack.push(IterStackLoc::mark_heap_loc(vh)); + let loc = IterStackLoc::iterable_loc(vh, HeapOrStackTag::Heap); + + self.push_if_unmarked(loc); + self.stack.push(IterStackLoc::mark_loc(vh, HeapOrStackTag::Heap)); } (HeapCellValueTag::Lis, vh) => { - self.push_if_unmarked(vh); + let loc = IterStackLoc::iterable_loc(vh, HeapOrStackTag::Heap); - self.stack.push(IterStackLoc::pending_mark_heap_loc(vh + 1)); - self.stack.push(IterStackLoc::mark_heap_loc(vh)); + self.push_if_unmarked(loc); - forward_if_referent_marked(&mut self.heap, vh); + self.stack.push(IterStackLoc::pending_mark_loc(vh + 1, HeapOrStackTag::Heap)); + self.stack.push(IterStackLoc::mark_loc(vh, HeapOrStackTag::Heap)); - return Some(self.heap[h]); + self.forward_if_referent_marked(loc); + + return Some(self.read_cell(h)); } (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, vh) => { - self.push_if_unmarked(vh); - self.stack.push(IterStackLoc::mark_heap_loc(vh)); - forward_if_referent_marked(&mut self.heap, vh); + let loc = IterStackLoc::iterable_loc(vh, HeapOrStackTag::Heap); + + self.push_if_unmarked(loc); + self.stack.push(IterStackLoc::mark_loc(vh, HeapOrStackTag::Heap)); + self.forward_if_referent_marked(loc); + } + (HeapCellValueTag::StackVar, vs) => { + let loc = IterStackLoc::iterable_loc(vs, HeapOrStackTag::Stack); + + self.push_if_unmarked(loc); + self.stack.push(IterStackLoc::mark_loc(vs, HeapOrStackTag::Stack)); + self.forward_if_referent_marked(loc); } (HeapCellValueTag::PStrOffset, offset) => { - self.push_if_unmarked(offset); - self.stack.push(IterStackLoc::iterable_heap_loc(h+1)); + self.push_if_unmarked(IterStackLoc::iterable_loc(offset, HeapOrStackTag::Heap)); + self.stack.push(IterStackLoc::iterable_loc((h.value()+1) as usize, HeapOrStackTag::Heap)); - return Some(self.heap[h]); + return Some(self.read_cell(h)); } (HeapCellValueTag::PStr) => { - self.push_if_unmarked(h); + let tail_loc = IterStackLoc::iterable_loc((h.value()+1) as usize, HeapOrStackTag::Heap); - self.stack.push(IterStackLoc::iterable_heap_loc(h+1)); - forward_if_referent_marked(&mut self.heap, h+1); + self.push_if_unmarked(IterStackLoc::iterable_loc(h.value() as usize, HeapOrStackTag::Heap)); + self.stack.push(tail_loc); + self.forward_if_referent_marked(tail_loc); - return Some(self.heap[h]); + return Some(self.read_cell(h)); } (HeapCellValueTag::Atom, (_name, arity)) => { - for h in (h + 2 .. h + arity + 1).rev() { - self.stack.push(IterStackLoc::pending_mark_heap_loc(h)); + let l = h.value() as usize; + + for l in (l + 2 .. l + arity + 1).rev() { + self.stack.push(IterStackLoc::pending_mark_loc(l, HeapOrStackTag::Heap)); } if arity > 0 { - self.push_if_unmarked(h+1); - self.stack.push(IterStackLoc::mark_heap_loc(h+1)); - forward_if_referent_marked(&mut self.heap, h+1); + let first_arg_loc = IterStackLoc::iterable_loc(l+1, HeapOrStackTag::Heap); + + self.push_if_unmarked(first_arg_loc); + self.stack.push(IterStackLoc::mark_loc(l+1, HeapOrStackTag::Heap)); + self.forward_if_referent_marked(first_arg_loc); } - return Some(self.heap[h]); + return Some(self.read_cell(h)); } _ => { return Some(*cell); @@ -269,19 +350,20 @@ pub(crate) fn stackless_preorder_iter( } #[inline(always)] -pub(crate) fn stackful_preorder_iter( - heap: &mut Vec, +pub(crate) fn stackful_preorder_iter<'a>( + heap: &'a mut Vec, + stack: &'a mut Stack, cell: HeapCellValue, -) -> StackfulPreOrderHeapIter { - StackfulPreOrderHeapIter::new(heap, cell) +) -> StackfulPreOrderHeapIter<'a> { + StackfulPreOrderHeapIter::new(heap, stack, cell) } #[derive(Debug)] pub(crate) struct PostOrderIterator { - focus: usize, + focus: IterStackLoc, base_iter: Iter, base_iter_valid: bool, - parent_stack: Vec<(usize, HeapCellValue, usize)>, // number of children, parent node, focus. + parent_stack: Vec<(usize, HeapCellValue, IterStackLoc)>, // number of children, parent node, focus. } impl Deref for PostOrderIterator { @@ -295,7 +377,7 @@ impl Deref for PostOrderIterator { impl PostOrderIterator { pub(crate) fn new(base_iter: Iter) -> Self { PostOrderIterator { - focus: 0, + focus: IterStackLoc::iterable_loc(0, HeapOrStackTag::Heap), base_iter, base_iter_valid: true, parent_stack: vec![], @@ -352,7 +434,7 @@ impl Iterator for PostOrderIterator { impl FocusedHeapIter for PostOrderIterator { #[inline(always)] - fn focus(&self) -> usize { + fn focus(&self) -> IterStackLoc { self.focus } } @@ -368,7 +450,8 @@ impl PostOrderIterator { if let Some((_child_count, item, focus)) = self.parent_stack.last() { read_heap_cell!(item, (HeapCellValueTag::Atom, (_name, arity)) => { - return focus + arity >= idx_loc && *focus < idx_loc; + let focus = focus.value() as usize; + return focus + arity >= idx_loc && focus < idx_loc; } _ => {} ); @@ -401,9 +484,10 @@ impl<'a> LeftistPostOrderHeapIter<'a> { #[inline] pub(crate) fn stackful_post_order_iter<'a>( heap: &'a mut Heap, + stack: &'a mut Stack, cell: HeapCellValue, ) -> LeftistPostOrderHeapIter<'a> { - PostOrderIterator::new(StackfulPreOrderHeapIter::new(heap, cell)) + PostOrderIterator::new(StackfulPreOrderHeapIter::new(heap, stack, cell)) } #[cfg(test)] @@ -1382,7 +1466,11 @@ mod tests { .extend(functor!(f_atom, [atom(a_atom), atom(b_atom)])); { - let mut iter = StackfulPreOrderHeapIter::new(&mut wam.machine_st.heap, str_loc_as_cell!(0)); + let mut iter = StackfulPreOrderHeapIter::new( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + str_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1413,7 +1501,11 @@ mod tests { )); for _ in 0..20 { - let mut iter = StackfulPreOrderHeapIter::new(&mut wam.machine_st.heap, str_loc_as_cell!(0)); + let mut iter = StackfulPreOrderHeapIter::new( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + str_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1441,7 +1533,12 @@ mod tests { { wam.machine_st.heap.push(heap_loc_as_cell!(0)); - let mut iter = StackfulPreOrderHeapIter::new(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = StackfulPreOrderHeapIter::new( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); + let mut var = heap_loc_as_cell!(0); // self-referencing variables are copied with their forwarding @@ -1463,7 +1560,11 @@ mod tests { wam.machine_st.heap.push(heap_loc_as_cell!(1)); wam.machine_st.heap.push(heap_loc_as_cell!(0)); - let mut iter = StackfulPreOrderHeapIter::new(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = StackfulPreOrderHeapIter::new( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1483,7 +1584,11 @@ mod tests { wam.machine_st.heap.push(empty_list_as_cell!()); { - let mut iter = StackfulPreOrderHeapIter::new(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = StackfulPreOrderHeapIter::new( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1515,7 +1620,11 @@ mod tests { wam.machine_st.heap.push(heap_loc_as_cell!(0)); { - let mut iter = StackfulPreOrderHeapIter::new(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = StackfulPreOrderHeapIter::new( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); // the cycle will be iterated twice before being detected. assert_eq!( @@ -1543,7 +1652,11 @@ mod tests { } { - let mut iter = StackfulPreOrderHeapIter::new(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = StackfulPreOrderHeapIter::new( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); // cut the iteration short to check that all cells are // unmarked and unforwarded by the Drop instance of @@ -1577,7 +1690,11 @@ mod tests { let pstr_cell = wam.machine_st.heap[pstr_var_cell.get_value() as usize]; { - let mut iter = StackfulPreOrderHeapIter::new(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = StackfulPreOrderHeapIter::new( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); assert_eq!( @@ -1597,7 +1714,11 @@ mod tests { let pstr_second_cell = wam.machine_st.heap[pstr_second_var_cell.get_value() as usize]; { - let mut iter = stackful_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackful_preorder_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell); @@ -1616,7 +1737,12 @@ mod tests { wam.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(0i64))); { - let mut iter = stackful_preorder_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); + let mut iter = stackful_preorder_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + pstr_loc_as_cell!(0), + ); + let pstr_offset_cell = pstr_offset_as_cell!(0); // pstr_offset_cell.set_forwarding_bit(true); @@ -1641,7 +1767,12 @@ mod tests { wam.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(1i64))); { - let mut iter = stackful_preorder_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); + let mut iter = stackful_preorder_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + pstr_loc_as_cell!(0), + ); + let pstr_offset_cell = pstr_offset_as_cell!(0); // pstr_offset_cell.set_forwarding_bit(true); @@ -1654,7 +1785,7 @@ mod tests { let h = iter.focus(); - assert_eq!(h, 5); + assert_eq!(h.value(), 5); assert_eq!(unmark_cell_bits!(iter.heap[4]), pstr_offset_as_cell!(0)); assert_eq!(unmark_cell_bits!(iter.heap[5]), fixnum_as_cell!(Fixnum::build_with(1i64))); @@ -1674,7 +1805,11 @@ mod tests { wam.machine_st.heap.extend(functor); { - let mut iter = StackfulPreOrderHeapIter::new(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = StackfulPreOrderHeapIter::new( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1733,7 +1868,11 @@ mod tests { wam.machine_st.heap[4] = list_loc_as_cell!(1); { - let mut iter = stackful_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackful_preorder_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1800,6 +1939,7 @@ mod tests { { let mut iter = StackfulPreOrderHeapIter::new( &mut wam.machine_st.heap, + &mut wam.machine_st.stack, heap_loc_as_cell!(0), ); @@ -1831,6 +1971,7 @@ mod tests { { let mut iter = stackful_preorder_iter( &mut wam.machine_st.heap, + &mut wam.machine_st.stack, heap_loc_as_cell!(0), ); @@ -1865,6 +2006,7 @@ mod tests { { let mut iter = stackful_preorder_iter( &mut wam.machine_st.heap, + &mut wam.machine_st.stack, heap_loc_as_cell!(0), ); @@ -1899,7 +2041,11 @@ mod tests { .extend(functor!(f_atom, [atom(a_atom), atom(b_atom)])); { - let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, str_loc_as_cell!(0)); + let mut iter = stackful_post_order_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + str_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1930,7 +2076,11 @@ mod tests { )); for _ in 0..20 { // 0000 { - let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, str_loc_as_cell!(0)); + let mut iter = stackful_post_order_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + str_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1960,7 +2110,12 @@ mod tests { { wam.machine_st.heap.push(heap_loc_as_cell!(0)); - let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackful_post_order_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); + let mut var = heap_loc_as_cell!(0); // self-referencing variables are copied with their forwarding @@ -1982,7 +2137,11 @@ mod tests { wam.machine_st.heap.push(heap_loc_as_cell!(1)); wam.machine_st.heap.push(heap_loc_as_cell!(0)); - let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackful_post_order_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -2002,7 +2161,11 @@ mod tests { wam.machine_st.heap.push(empty_list_as_cell!()); { - let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackful_post_order_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -2034,7 +2197,11 @@ mod tests { wam.machine_st.heap.push(heap_loc_as_cell!(0)); { - let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackful_post_order_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); // the cycle will be iterated twice before being detected. assert_eq!( @@ -2064,6 +2231,7 @@ mod tests { { let mut iter = stackful_post_order_iter( &mut wam.machine_st.heap, + &mut wam.machine_st.stack, heap_loc_as_cell!(0), ); @@ -2099,7 +2267,11 @@ mod tests { let pstr_cell = wam.machine_st.heap[pstr_var_cell.get_value() as usize]; { - let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); + let mut iter = stackful_post_order_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + pstr_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -2118,7 +2290,11 @@ mod tests { let pstr_second_cell = wam.machine_st.heap[pstr_second_var_cell.get_value() as usize]; { - let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); + let mut iter = stackful_post_order_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + pstr_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -2137,7 +2313,11 @@ mod tests { wam.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(0i64))); { - let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); + let mut iter = stackful_post_order_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + pstr_loc_as_cell!(0), + ); assert_eq!(iter.next().unwrap(), fixnum_as_cell!(Fixnum::build_with(0i64))); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_as_cell!(0)); @@ -2152,7 +2332,11 @@ mod tests { wam.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(1i64))); { - let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); + let mut iter = stackful_post_order_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + pstr_loc_as_cell!(0), + ); assert_eq!(iter.next().unwrap(), fixnum_as_cell!(Fixnum::build_with(1i64))); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_as_cell!(0)); @@ -2176,7 +2360,11 @@ mod tests { wam.machine_st.heap.extend(functor); { - let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackful_post_order_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -2236,7 +2424,11 @@ mod tests { wam.machine_st.heap[4] = list_loc_as_cell!(1); { - let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackful_post_order_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -2343,7 +2535,10 @@ mod tests { )); for _ in 0..20 { - let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, str_loc_as_cell!(0)); + let mut iter = stackless_post_order_iter( + &mut wam.machine_st.heap, + str_loc_as_cell!(0), + ); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), str_loc_as_cell!(0)); @@ -2373,7 +2568,10 @@ mod tests { { wam.machine_st.heap.push(heap_loc_as_cell!(0)); - let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackless_post_order_iter( + &mut wam.machine_st.heap, + heap_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -2389,7 +2587,10 @@ mod tests { wam.machine_st.heap.push(heap_loc_as_cell!(1)); wam.machine_st.heap.push(heap_loc_as_cell!(0)); - let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackless_post_order_iter( + &mut wam.machine_st.heap, + heap_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), diff --git a/src/heap_print.rs b/src/heap_print.rs index 54f52cd6..8be39076 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -1,7 +1,7 @@ use crate::arena::*; use crate::atom_table::*; use crate::parser::ast::*; -use crate::parser::rug::{Integer, Rational}; +use crate::parser::dashu::{Integer, Rational}; use crate::{ alpha_numeric_char, capital_letter_char, cut_char, decimal_digit_char, graphic_token_char, is_fx, is_infix, is_postfix, is_prefix, is_xf, is_xfx, is_xfy, is_yfx, semicolon_char, @@ -14,9 +14,12 @@ use crate::machine::heap::*; use crate::machine::machine_indices::*; use crate::machine::machine_state::pstr_loc_and_offset; use crate::machine::partial_string::*; +use crate::machine::stack::*; use crate::machine::streams::*; use crate::types::*; +use dashu::base::DivRem; +use dashu::base::DivRemEuclid; use ordered_float::OrderedFloat; use indexmap::IndexMap; @@ -25,7 +28,6 @@ use std::cell::Cell; use std::convert::TryFrom; use std::iter::once; use std::net::{IpAddr, TcpListener}; -use std::ops::{Range, RangeFrom}; use std::rc::Rc; /* contains the location, name, precision and Specifier of the parent op. */ @@ -43,6 +45,15 @@ impl DirectedOp { } } + #[inline] + fn is_prefix(&self )-> bool { + match self { + &DirectedOp::Left(_name, cell) | &DirectedOp::Right(_name, cell) => { + is_prefix!(cell.get_spec() as u32) + } + } + } + #[inline] fn is_negative_sign(&self) -> bool { match self { @@ -54,11 +65,7 @@ impl DirectedOp { #[inline] fn is_left(&self) -> bool { - if let &DirectedOp::Left(..) = self { - true - } else { - false - } + matches!(self, DirectedOp::Left(..)) } } @@ -115,7 +122,9 @@ impl<'a> StackfulPreOrderHeapIter<'a> { let mut parent_spec = DirectedOp::Left(atom!("-"), OpDesc::build_with(200, FY as u8)); loop { - read_heap_cell!(self.heap[h], + let cell = self.read_cell(h); + + read_heap_cell!(cell, (HeapCellValueTag::Str, s) => { read_heap_cell!(self.heap[s], (HeapCellValueTag::Atom, (name, _arity)) => { @@ -124,7 +133,7 @@ impl<'a> StackfulPreOrderHeapIter<'a> { if needs_bracketing(spec, &parent_spec) { return false; } else { - h = s + 1; + h = IterStackLoc::iterable_loc(s + 1, HeapOrStackTag::Heap); parent_spec = DirectedOp::Right(name, spec); continue; } @@ -139,7 +148,7 @@ impl<'a> StackfulPreOrderHeapIter<'a> { ) } _ => { - return property_check(self.heap[h]); + return property_check(cell); } ) } @@ -149,12 +158,12 @@ impl<'a> StackfulPreOrderHeapIter<'a> { where P: Fn(HeapCellValue) -> bool, { - let addr = match self.stack_last() { - Some(h) => self.heap[h], + let cell = match self.stack_last() { + Some(h) => self.read_cell(h), None => return false, }; - property_check(addr) + property_check(cell) } } @@ -169,11 +178,16 @@ fn char_to_string(is_quoted: bool, c: char) -> String { '\u{08}' if is_quoted => "\\b".to_string(), // UTF-8 backspace '\u{07}' if is_quoted => "\\a".to_string(), // UTF-8 alert '\\' if is_quoted => "\\\\".to_string(), - '\'' | '\n' | '\r' | '\t' | '\u{0b}' | '\u{0c}' | '\u{08}' | '\u{07}' | '"' | '\\' => { + ' ' | '\'' | '\n' | '\r' | '\t' | '\u{0b}' | '\u{0c}' | '\u{08}' | '\u{07}' | '"' | '\\' => { c.to_string() } - '\u{0}'..='\u{1f}' => format!("\\x{:x}\\", c as u32), // print all other control characters in hex. - _ => c.to_string(), + _ => + if c.is_whitespace() || c.is_control() { + // print all other control and whitespace characters in hex. + format!("\\x{:x}\\", c as u32) + } else { + c.to_string() + } } } @@ -188,7 +202,7 @@ impl NumberFocus { fn is_negative(&self) -> bool { match self { NumberFocus::Unfocused(n) => n.is_negative(), - NumberFocus::Denominator(r) | NumberFocus::Numerator(r) => **r < 0, + NumberFocus::Denominator(r) | NumberFocus::Numerator(r) => **r < Rational::from(0), } } } @@ -212,6 +226,8 @@ enum TokenOrRedirect { Space, LeftCurly, RightCurly, + ChildOpenList, + ChildCloseList, OpenList(Rc>), CloseList(Rc>), HeadTailSeparator, @@ -311,8 +327,7 @@ pub trait HCValueOutputter { fn ends_with(&self, s: &str) -> bool; fn len(&self) -> usize; fn truncate(&mut self, len: usize); - fn range(&self, range: Range) -> &str; - fn range_from(&self, range: RangeFrom) -> &str; + fn as_str(&self) -> &str; } #[derive(Debug)] @@ -367,12 +382,8 @@ impl HCValueOutputter for PrinterOutputter { self.contents.truncate(len); } - fn range(&self, index: Range) -> &str { - &self.contents.as_str()[index] - } - - fn range_from(&self, index: RangeFrom) -> &str { - &self.contents.as_str().get(index).unwrap_or("") + fn as_str(&self) -> &str { + &self.contents } } @@ -392,8 +403,8 @@ fn negated_op_needs_bracketing( && iter.leftmost_leaf_has_property(op_dir, |addr| match Number::try_from(addr) { Ok(Number::Fixnum(n)) => n.get_num() > 0, Ok(Number::Float(f)) => f > OrderedFloat(0f64), - Ok(Number::Integer(n)) => &*n > &0, - Ok(Number::Rational(n)) => &*n > &0, + Ok(Number::Integer(n)) => &*n > &Integer::from(0), + Ok(Number::Rational(n)) => &*n > &Rational::from(0), _ => false, }) } else { @@ -468,17 +479,20 @@ pub fn fmt_float(mut fl: f64) -> String { pub struct HCPrinter<'a, Outputter> { outputter: Outputter, iter: StackfulPreOrderHeapIter<'a>, + atom_tbl: &'a mut AtomTable, op_dir: &'a OpDir, state_stack: Vec, toplevel_spec: Option, last_item_idx: usize, - pub var_names: IndexMap>, + parent_of_first_op: Option<(DirectedOp, usize)>, + pub var_names: IndexMap, pub numbervars_offset: Integer, pub numbervars: bool, pub quoted: bool, pub ignore_ops: bool, pub print_strings_as_strs: bool, pub max_depth: usize, + pub double_quotes: bool, } macro_rules! push_space_if_amb { @@ -499,11 +513,13 @@ pub(crate) fn numbervar(offset: &Integer, addr: HeapCellValue) -> Option 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ]; - let i = n.mod_u(26) as usize; - let j = n.div_rem_floor(Integer::from(26)); + let n_clone: Integer = n.clone(); + + let i = n.div_rem_euclid(Integer::from(26)).1.to_f32().value() as usize; + let j = n_clone.div_rem(Integer::from(26)); let j = <(Integer, Integer)>::from(j).0; - if j == 0 { + if j == Integer::from(0) { CHAR_CODES[i].to_string() } else { format!("{}{}", CHAR_CODES[i], j) @@ -519,7 +535,7 @@ pub(crate) fn numbervar(offset: &Integer, addr: HeapCellValue) -> Option } } Ok(Number::Integer(n)) => { - if &*n >= &0 { + if &*n >= &Integer::from(0) { Some(numbervar(Integer::from(offset + &*n))) } else { None @@ -532,17 +548,21 @@ pub(crate) fn numbervar(offset: &Integer, addr: HeapCellValue) -> Option impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { pub fn new( heap: &'a mut Heap, + atom_tbl: &'a mut AtomTable, + stack: &'a mut Stack, op_dir: &'a OpDir, output: Outputter, cell: HeapCellValue, ) -> Self { HCPrinter { outputter: output, - iter: stackful_preorder_iter(heap, cell), + iter: stackful_preorder_iter(heap, stack, cell), + atom_tbl, op_dir, state_stack: vec![], toplevel_spec: None, last_item_idx: 0, + parent_of_first_op: None, numbervars: false, numbervars_offset: Integer::from(0), quoted: false, @@ -550,59 +570,64 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { var_names: IndexMap::new(), print_strings_as_strs: false, max_depth: 0, + double_quotes: false, } } #[inline] fn ambiguity_check(&self, atom: &str) -> bool { - let tail = self.outputter.range_from(self.last_item_idx..); - requires_space(tail, atom) + let tail = &self.outputter.as_str()[self.last_item_idx..]; + + if !self.quoted || non_quoted_token(atom.chars()) { + requires_space(tail, atom) + } else { + requires_space(tail, "'") + } + } + + fn set_parent_of_first_op(&mut self, parent_op: Option) { + if let Some(op) = parent_op { + if op.is_left() && op.is_prefix() { + self.parent_of_first_op = Some((op, self.last_item_idx)); + } + } } fn enqueue_op(&mut self, mut max_depth: usize, name: Atom, spec: OpDesc) { if is_postfix!(spec.get_spec()) { - if self.check_max_depth(&mut max_depth) { + if self.max_depth_exhausted(max_depth) { + self.iter.pop_stack(); + self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); + } else if self.check_max_depth(&mut max_depth) { self.iter.pop_stack(); self.state_stack.push(TokenOrRedirect::Op(name, spec)); self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); + } else { + let right_directed_op = DirectedOp::Right(name, spec); - return; + self.state_stack.push(TokenOrRedirect::Op(name, spec)); + self.state_stack.push(TokenOrRedirect::CompositeRedirect( + max_depth, + right_directed_op, + )); } - - let right_directed_op = DirectedOp::Right(name, spec); - - self.state_stack.push(TokenOrRedirect::Op(name, spec)); - self.state_stack.push(TokenOrRedirect::CompositeRedirect( - max_depth, - right_directed_op, - )); } else if is_prefix!(spec.get_spec()) { - match name { - atom!("-") | atom!("\\") => { - self.format_prefix_op_with_space(max_depth, name, spec); - return; - } - _ => {} - }; - - if self.check_max_depth(&mut max_depth) { + if self.max_depth_exhausted(max_depth) { + self.iter.pop_stack(); + self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); + return; + } else if self.check_max_depth(&mut max_depth) { self.iter.pop_stack(); self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); self.state_stack.push(TokenOrRedirect::Op(name, spec)); + } else { + let op = DirectedOp::Left(name, spec); - return; + self.state_stack.push(TokenOrRedirect::CompositeRedirect(max_depth, op)); + self.state_stack.push(TokenOrRedirect::Op(name, spec)); } - - let left_directed_op = DirectedOp::Left(name, spec); - - self.state_stack.push(TokenOrRedirect::CompositeRedirect( - max_depth, - left_directed_op, - )); - - self.state_stack.push(TokenOrRedirect::Op(name, spec)); } else { match name.as_str() { "|" => { @@ -612,31 +637,26 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { _ => {} }; - let ellipsis_atom = atom!("..."); - - if self.check_max_depth(&mut max_depth) { + if self.max_depth_exhausted(max_depth) { self.iter.pop_stack(); self.iter.pop_stack(); - self.state_stack.push(TokenOrRedirect::Atom(ellipsis_atom)); + self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); + } else if self.check_max_depth(&mut max_depth) { + self.iter.pop_stack(); + self.iter.pop_stack(); + + self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); self.state_stack.push(TokenOrRedirect::Op(name, spec)); - self.state_stack.push(TokenOrRedirect::Atom(ellipsis_atom)); + self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); + } else { + let left_directed_op = DirectedOp::Left(name, spec); + let right_directed_op = DirectedOp::Right(name, spec); - return; + self.state_stack.push(TokenOrRedirect::CompositeRedirect(max_depth, left_directed_op)); + self.state_stack.push(TokenOrRedirect::Op(name, spec)); + self.state_stack.push(TokenOrRedirect::CompositeRedirect(max_depth, right_directed_op)); } - - let left_directed_op = DirectedOp::Left(name, spec); - let right_directed_op = DirectedOp::Right(name, spec); - - self.state_stack.push(TokenOrRedirect::CompositeRedirect( - max_depth, - left_directed_op, - )); - self.state_stack.push(TokenOrRedirect::Op(name, spec)); - self.state_stack.push(TokenOrRedirect::CompositeRedirect( - max_depth, - right_directed_op, - )); } } @@ -675,27 +695,10 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { true } - fn format_prefix_op_with_space(&mut self, mut max_depth: usize, name: Atom, spec: OpDesc) { - if self.check_max_depth(&mut max_depth) { - self.iter.pop_stack(); - - self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); - self.state_stack.push(TokenOrRedirect::Space); - self.state_stack.push(TokenOrRedirect::Atom(name)); - - return; - } - - let op = DirectedOp::Left(name, spec); - - self.state_stack.push(TokenOrRedirect::CompositeRedirect(max_depth, op)); - self.state_stack.push(TokenOrRedirect::Space); - self.state_stack.push(TokenOrRedirect::Atom(name)); - } - fn format_bar_separator_op(&mut self, mut max_depth: usize, name: Atom, spec: OpDesc) { if self.check_max_depth(&mut max_depth) { self.iter.pop_stack(); + self.iter.pop_stack(); let ellipsis_atom = atom!("..."); @@ -746,14 +749,14 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { fn format_numbered_vars(&mut self) -> bool { let h = self.iter.stack_last().unwrap(); - let addr = self.iter.heap[h]; - let addr = heap_bound_store( + let cell = self.iter.read_cell(h); + let cell = heap_bound_store( &self.iter.heap, - heap_bound_deref(&self.iter.heap, addr), + heap_bound_deref(&self.iter.heap, cell), ); // 7.10.4 - if let Some(var) = numbervar(&self.numbervars_offset, addr) { + if let Some(var) = numbervar(&self.numbervars_offset, cell) { self.iter.pop_stack(); self.state_stack.push(TokenOrRedirect::NumberedVar(var)); return true; @@ -797,13 +800,13 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { }; } - fn offset_as_string(&mut self, h: usize) -> Option { - let addr = self.iter.heap[h]; + fn offset_as_string(&mut self, h: IterStackLoc) -> Option { + let cell = self.iter.read_cell(h); - if let Some(var) = self.var_names.get(&addr) { - read_heap_cell!(addr, + if let Some(var) = self.var_names.get(&cell) { + read_heap_cell!(cell, (HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar) => { - return Some(format!("{}", var.as_str())); + return Some(var.borrow().to_string()); } _ => { self.iter.push_stack(h); @@ -812,7 +815,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { ); } - read_heap_cell!(addr, + read_heap_cell!(cell, (HeapCellValueTag::Lis | HeapCellValueTag::Str, h) => { Some(format!("{}", h)) } @@ -829,41 +832,42 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { } fn check_for_seen(&mut self) -> Option { - if let Some(addr) = self.iter.next() { - let is_cyclic = addr.get_forwarding_bit(); + if let Some(cell) = self.iter.next() { + let is_cyclic = cell.get_forwarding_bit(); - let addr = heap_bound_store( + let cell = heap_bound_store( self.iter.heap, - heap_bound_deref(self.iter.heap, addr), + heap_bound_deref(self.iter.heap, cell), ); + let cell = unmark_cell_bits!(cell); - let addr = unmark_cell_bits!(addr); - - match self.var_names.get(&addr).cloned() { - Some(var) if addr.is_var() => { - // If addr is an unbound variable and maps to + match self.var_names.get(&cell).cloned() { + Some(var) if cell.is_var() => { + // If cell is an unbound variable and maps to // a name via heap_locs, append the name to // the current output, and return None. None // short-circuits handle_heap_term. // self.iter.pop_stack(); - let var_str = var.as_str(); + let var_str = var.borrow().to_string(); - push_space_if_amb!(self, var_str, { - append_str!(self, var_str); + push_space_if_amb!(self, &var_str, { + append_str!(self, &var_str); }); None } var_opt => { - if is_cyclic && addr.is_compound(self.iter.heap) { + if is_cyclic && cell.is_compound(self.iter.heap) { // self-referential variables are marked "cyclic". match var_opt { Some(var) => { // If the term is bound to a named variable, // print the variable's name to output. - push_space_if_amb!(self, &var, { - append_str!(self, &var); + let var_str = var.borrow().to_string(); + + push_space_if_amb!(self, &var_str, { + append_str!(self, &var_str); }); } None => { @@ -877,7 +881,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { return None; } - Some(addr) + Some(cell) } } } else { @@ -886,7 +890,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { } } - fn print_atom(&mut self, atom: Atom) { + fn print_impromptu_atom(&mut self, atom: Atom) { let result = self.print_op_addendum(atom.as_str()); push_space_if_amb!(self, result.as_str(), { @@ -943,13 +947,17 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { } fn print_number(&mut self, max_depth: usize, n: NumberFocus, op: &Option) { - let add_brackets = if let Some(op) = op { - op.is_negative_sign() && !n.is_negative() + let (add_brackets, op_is_prefix) = if let Some(op) = op { + (op.is_negative_sign() && !n.is_negative(), op.is_prefix()) } else { - false + (false, false) }; if add_brackets { + if op_is_prefix && !self.outputter.ends_with(" ") { + push_char!(self, ' '); + } + push_char!(self, '('); } @@ -963,7 +971,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { }); } Number::Rational(r) => { - self.print_rational(max_depth, r); + self.print_rational(max_depth, r, *op); } n => { let output_str = format!("{}", n); @@ -974,14 +982,14 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { } }, NumberFocus::Denominator(r) => { - let output_str = format!("{}", r.denom()); + let output_str = format!("{}", r.denominator()); push_space_if_amb!(self, &output_str, { append_str!(self, &output_str); }); } NumberFocus::Numerator(r) => { - let output_str = format!("{}", r.numer()); + let output_str = format!("{}", r.numerator()); push_space_if_amb!(self, &output_str, { append_str!(self, &output_str); @@ -994,7 +1002,12 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { } } - fn print_rational(&mut self, mut max_depth: usize, r: TypedArenaPtr) { + fn print_rational( + &mut self, + mut max_depth: usize, + r: TypedArenaPtr, + parent_op: Option, + ) { if self.check_max_depth(&mut max_depth) { self.state_stack.push(TokenOrRedirect::Close); self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); @@ -1037,15 +1050,14 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { NumberFocus::Denominator(r), left_directed_op, )); - - self.state_stack - .push(TokenOrRedirect::Op(rdiv_ct, *op_desc)); - + self.state_stack.push(TokenOrRedirect::Op(rdiv_ct, *op_desc)); self.state_stack.push(TokenOrRedirect::NumberFocus( max_depth, NumberFocus::Numerator(r), right_directed_op, )); + + self.set_parent_of_first_op(parent_op); } else { self.state_stack.push(TokenOrRedirect::Close); @@ -1155,7 +1167,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { fn print_list_like(&mut self, mut max_depth: usize) { let focus = self.iter.focus(); - let mut heap_pstr_iter = HeapPStrIter::new(self.iter.heap, focus); + let mut heap_pstr_iter = HeapPStrIter::new(self.iter.heap, focus.value() as usize); if heap_pstr_iter.next().is_some() { while let Some(_) = heap_pstr_iter.next() {} @@ -1167,34 +1179,38 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { let end_cell = heap_pstr_iter.focus; if self.check_max_depth(&mut max_depth) { - self.remove_list_children(focus); + self.remove_list_children(focus.value() as usize); self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); return; } let at_cdr = self.outputter.ends_with("|"); - if !at_cdr && !self.ignore_ops && end_cell.is_string_terminator(&self.iter.heap) { - self.remove_list_children(focus); - return self.print_proper_string(focus, max_depth); + if self.double_quotes { + if !self.ignore_ops && end_cell.is_string_terminator(&self.iter.heap) { + self.remove_list_children(focus.value() as usize); + return self.print_proper_string(focus.value() as usize, max_depth); + } } if self.ignore_ops { self.at_cdr(","); - self.remove_list_children(focus); + self.remove_list_children(focus.value() as usize); - if !self.print_string_as_functor(focus, max_depth) { + if !self.print_string_as_functor(focus.value() as usize, max_depth) { if end_cell == empty_list_as_cell!() { - append_str!(self, "[]"); + if !self.at_cdr("") { + append_str!(self, "[]"); + } } else { self.state_stack.push(TokenOrRedirect::FunctorRedirect(max_depth)); - self.iter.push_stack(end_h); + self.iter.push_stack(IterStackLoc::iterable_loc(end_h, HeapOrStackTag::Heap)); } } } else { let value = heap_bound_store( self.iter.heap, - heap_bound_deref(self.iter.heap, self.iter.heap[focus]), + heap_bound_deref(self.iter.heap, self.iter.read_cell(focus)), ); read_heap_cell!(value, @@ -1203,9 +1219,9 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { } _ => { let switch = Rc::new(Cell::new((!at_cdr, 0))); - self.state_stack.push(TokenOrRedirect::CloseList(switch.clone())); + let switch = self.close_list(switch); - let (h, offset) = pstr_loc_and_offset(self.iter.heap, focus); + let (h, offset) = pstr_loc_and_offset(self.iter.heap, focus.value() as usize); let pstr = cell_as_string!(self.iter.heap[h]); let pstr = pstr.as_str_from(offset.get_num() as usize); @@ -1227,7 +1243,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { self.state_stack.push(TokenOrRedirect::HeadTailSeparator); } else if end_cell != empty_list_as_cell!() { if tag == HeapCellValueTag::PStrOffset { - self.iter.push_stack(end_h); + self.iter.push_stack(IterStackLoc::iterable_loc(end_h, HeapOrStackTag::Heap)); } self.state_stack.push(TokenOrRedirect::FunctorRedirect(max_depth)); @@ -1251,12 +1267,17 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { self.state_stack.pop(); } - self.state_stack.push(TokenOrRedirect::OpenList(switch)); + self.open_list(switch); } ); } } + #[inline] + fn max_depth_exhausted(&self, max_depth: usize) -> bool { + self.max_depth > 0 && max_depth == 0 + } + fn check_max_depth(&self, max_depth: &mut usize) -> bool { if self.max_depth > 0 && *max_depth == 0 { return true; @@ -1269,36 +1290,63 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { false } + fn close_list(&mut self, switch: Rc>) -> Option>> { + if let Some(TokenOrRedirect::Op(_, op_desc)) = self.state_stack.last() { + if is_postfix!(op_desc.get_spec()) || is_infix!(op_desc.get_spec()) { + self.state_stack.push(TokenOrRedirect::ChildCloseList); + return None; + } + } + + self.state_stack.push(TokenOrRedirect::CloseList(switch.clone())); + Some(switch) + } + + fn open_list(&mut self, switch: Option>>) { + self.state_stack.push(match switch { + Some(switch) => TokenOrRedirect::OpenList(switch), + None => TokenOrRedirect::ChildOpenList, + }); + } + fn push_list(&mut self, mut max_depth: usize) { - if self.check_max_depth(&mut max_depth) { + if self.max_depth_exhausted(max_depth) { + self.iter.pop_stack(); + self.iter.pop_stack(); + + self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); + + return; + } else if self.check_max_depth(&mut max_depth) { self.iter.pop_stack(); self.iter.pop_stack(); let cell = Rc::new(Cell::new((true, 0))); - self.state_stack.push(TokenOrRedirect::CloseList(cell.clone())); + let switch = self.close_list(cell); + self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); - self.state_stack.push(TokenOrRedirect::OpenList(cell)); + self.open_list(switch); return; } let cell = Rc::new(Cell::new((true, max_depth))); - self.state_stack.push(TokenOrRedirect::CloseList(cell.clone())); + let switch = self.close_list(cell); self.state_stack.push(TokenOrRedirect::FunctorRedirect(max_depth)); self.state_stack.push(TokenOrRedirect::HeadTailSeparator); // bar - self.state_stack.push(TokenOrRedirect::FunctorRedirect(max_depth)); + self.state_stack.push(TokenOrRedirect::FunctorRedirect(max_depth+1)); - self.state_stack.push(TokenOrRedirect::OpenList(cell)); + self.open_list(switch); } fn handle_op_as_struct( &mut self, name: Atom, arity: usize, - op: &Option, + op: Option, is_functor_redirect: bool, op_desc: OpDesc, negated_operand: bool, @@ -1310,10 +1358,10 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { if self.numbervars && arity == 1 && name == atom!("$VAR") { !self.iter.immediate_leaf_has_property(|addr| { match Number::try_from(addr) { - Ok(Number::Integer(n)) => &*n >= &0, + Ok(Number::Integer(n)) => &*n >= &Integer::from(0), Ok(Number::Fixnum(n)) => n.get_num() >= 0, Ok(Number::Float(f)) => f >= OrderedFloat(0f64), - Ok(Number::Rational(r)) => &*r >= &0, + Ok(Number::Rational(r)) => &*r >= &Integer::from(0), _ => false, } }) && needs_bracketing(op_desc, op) @@ -1329,16 +1377,33 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { if add_brackets { self.state_stack.push(TokenOrRedirect::Close); - } - - if self.format_clause(max_depth, arity, name, Some(op_desc)) && add_brackets { + self.format_clause(max_depth, arity, name, Some(op_desc)); self.state_stack.push(TokenOrRedirect::Open); - if let Some(ref op) = &op { - if op.is_left() && requires_space(op.as_atom().as_str(), "(") { - self.state_stack.push(TokenOrRedirect::Space); + if !self.outputter.ends_with(" ") { + let parent_op = self.parent_of_first_op + .and_then(|(parent_op, last_item_idx)| { + // if parent_op isn't printed to the output string + // already, then it doesn't border the present op + // and we should return None. + if self.last_item_idx == last_item_idx { + Some(parent_op) + } else { + None + } + }); + + for op in &[op, parent_op] { + if let Some(ref op) = &op { + if op.is_left() && (op.is_prefix() || requires_space(op.as_atom().as_str(), "(")) { + self.state_stack.push(TokenOrRedirect::Space); + return; + } + } } } + } else { + self.format_clause(max_depth, arity, name, Some(op_desc)); } } @@ -1402,7 +1467,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { fn print_stream(&mut self, stream: Stream, max_depth: usize) { if let Some(alias) = stream.options().get_alias() { - self.print_atom(alias); + self.print_impromptu_atom(alias); } else { let stream_atom = atom!("$stream"); @@ -1433,80 +1498,99 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { ) { let negated_operand = negated_op_needs_bracketing(&self.iter, self.op_dir, &op); + let print_struct = |printer: &mut Self, name: Atom, arity: usize| { + if name == atom!("[]") && arity == 0 { + if let Some(TokenOrRedirect::CloseList(_)) = printer.state_stack.last() { + if printer.at_cdr("") { + return; + } + } + + append_str!(printer, "[]"); + } else if arity > 0 { + if let Some(spec) = fetch_op_spec(name, arity, printer.op_dir) { + printer.handle_op_as_struct( + name, + arity, + op, + is_functor_redirect, + spec, + negated_operand, + max_depth, + ); + } else { + push_space_if_amb!(printer, name.as_str(), { + printer.format_clause(max_depth, arity, name, None); + }); + } + } else if fetch_op_spec(name, arity, printer.op_dir).is_some() { + let mut result = String::new(); + + if let Some(ref op) = op { + let op_is_prefix = op.is_prefix() && op.is_left(); + + if op_is_prefix || printer.outputter.ends_with(&format!(" {}", op.as_atom().as_str())) { + result.push(' '); + } + + result.push('('); + } + + result += &printer.print_op_addendum(name.as_str()); + + if op.is_some() { + result.push(')'); + } + + push_space_if_amb!(printer, &result, { + append_str!(printer, &result); + }); + } else { + push_space_if_amb!(printer, name.as_str(), { + printer.print_impromptu_atom(name); + }); + } + }; + let addr = match self.check_for_seen() { Some(addr) => addr, None => return, }; + if !addr.is_var() && !addr.is_compound(&self.iter.heap) && self.max_depth_exhausted(max_depth) { + self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); + return; + } + read_heap_cell!(addr, (HeapCellValueTag::Atom, (name, arity)) => { - if name == atom!("[]") && arity == 0 { - if !self.at_cdr("") { - append_str!(self, "[]"); - } - } else if arity > 0 { - if let Some(spec) = fetch_op_spec(name, arity, self.op_dir) { - self.handle_op_as_struct( - name, - arity, - &op, - is_functor_redirect, - spec, - negated_operand, - max_depth, - ); - } else { - push_space_if_amb!(self, name.as_str(), { - self.format_clause(max_depth, arity, name, None); - }); - } - } else if fetch_op_spec(name, arity, self.op_dir).is_some() { - let mut result = String::new(); - - if let Some(ref op) = op { - if self.outputter.ends_with(&format!(" {}", op.as_atom().as_str())) { - result.push(' '); - } - - result.push('('); - } - - result += &self.print_op_addendum(name.as_str()); - - if op.is_some() { - result.push(')'); - } - - push_space_if_amb!(self, &result, { - append_str!(self, &result); - }); - } else { - push_space_if_amb!(self, name.as_str(), { - self.print_atom(name); - }); - } + print_struct(self, name, arity); + } + (HeapCellValueTag::Char, c) => { + let name = self.atom_tbl.build_with(&String::from(c)); + print_struct(self, name, 0); } (HeapCellValueTag::Str, s) => { let (name, arity) = cell_as_atom_cell!(self.iter.heap[s]) .get_name_and_arity(); if let Some(spec) = fetch_op_spec(name, arity, self.op_dir) { - self.handle_op_as_struct( - name, - arity, - &op, - is_functor_redirect, - spec, - negated_operand, - max_depth, - ); + self.handle_op_as_struct( + name, + arity, + op, + is_functor_redirect, + spec, + negated_operand, + max_depth, + ); } else { push_space_if_amb!(self, name.as_str(), { self.format_clause(max_depth, arity, name, None); }); } } - (HeapCellValueTag::Fixnum, n) => { + (HeapCellValueTag::Fixnum | HeapCellValueTag::CutPoint, n) => { self.print_number(max_depth, NumberFocus::Unfocused(Number::Fixnum(n)), &op); } (HeapCellValueTag::F64, f) => { @@ -1532,32 +1616,29 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { }) } } - (HeapCellValueTag::Char, c) => { - print_char!(self, self.quoted, c); - } (HeapCellValueTag::Cons, c) => { match_untyped_arena_ptr!(c, - (ArenaHeaderTag::Integer, n) => { - self.print_number(max_depth, NumberFocus::Unfocused(Number::Integer(n)), &op); - } - (ArenaHeaderTag::Rational, r) => { - self.print_number(max_depth, NumberFocus::Unfocused(Number::Rational(r)), &op); - } - (ArenaHeaderTag::Stream, stream) => { - self.print_stream(stream, max_depth); - } - (ArenaHeaderTag::OssifiedOpDir, _op_dir) => { - self.print_atom(atom!("$ossified_op_dir")); - } - (ArenaHeaderTag::Dropped, _value) => { - self.print_atom(atom!("$dropped_value")); - } - (ArenaHeaderTag::IndexPtr, index_ptr) => { - self.print_index_ptr(*index_ptr, max_depth); - } - _ => { - } - ); + (ArenaHeaderTag::Integer, n) => { + self.print_number(max_depth, NumberFocus::Unfocused(Number::Integer(n)), &op); + } + (ArenaHeaderTag::Rational, r) => { + self.print_number(max_depth, NumberFocus::Unfocused(Number::Rational(r)), &op); + } + (ArenaHeaderTag::Stream, stream) => { + self.print_stream(stream, max_depth); + } + (ArenaHeaderTag::OssifiedOpDir, _op_dir) => { + self.print_impromptu_atom(atom!("$ossified_op_dir")); + } + (ArenaHeaderTag::Dropped, _value) => { + self.print_impromptu_atom(atom!("$dropped_value")); + } + (ArenaHeaderTag::IndexPtr, index_ptr) => { + self.print_index_ptr(*index_ptr, max_depth); + } + _ => { + } + ); } _ => { unreachable!() @@ -1571,7 +1652,6 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { if self.outputter.ends_with("|") { self.outputter.truncate(len - "|".len()); append_str!(self, tr); - true } else { false @@ -1584,10 +1664,16 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { while let Some(loc_data) = self.state_stack.pop() { match loc_data { - TokenOrRedirect::Atom(atom) => self.print_atom(atom), + TokenOrRedirect::Atom(atom) => self.print_impromptu_atom(atom), TokenOrRedirect::BarAsOp => append_str!(self, " | "), TokenOrRedirect::Char(c) => print_char!(self, self.quoted, c), - TokenOrRedirect::Op(atom, _) => self.print_op(atom.as_str()), + TokenOrRedirect::Op(atom, op) => { + self.print_op(atom.as_str()); + + if is_prefix!(op.get_spec()) { + self.set_parent_of_first_op(Some(DirectedOp::Left(atom, op))); + } + } TokenOrRedirect::NumberedVar(num_var) => append_str!(self, &num_var), TokenOrRedirect::CompositeRedirect(max_depth, op) => { self.handle_heap_term(Some(op), false, max_depth) @@ -1602,6 +1688,12 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { TokenOrRedirect::IpAddr(ip) => self.print_ip_addr(ip), TokenOrRedirect::RawPtr(ptr) => self.print_raw_ptr(ptr), TokenOrRedirect::Open => push_char!(self, '('), + TokenOrRedirect::ChildOpenList => { + push_char!(self, '['); + } + TokenOrRedirect::ChildCloseList => { + push_char!(self, ']'); + } TokenOrRedirect::OpenList(delimit) => { if !self.at_cdr(",") { push_char!(self, '['); @@ -1650,6 +1742,8 @@ mod tests { { let printer = HCPrinter::new( &mut wam.machine_st.heap, + &mut wam.machine_st.atom_tbl, + &mut wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1677,6 +1771,8 @@ mod tests { { let printer = HCPrinter::new( &mut wam.machine_st.heap, + &mut wam.machine_st.atom_tbl, + &mut wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1699,6 +1795,8 @@ mod tests { { let printer = HCPrinter::new( &mut wam.machine_st.heap, + &mut wam.machine_st.atom_tbl, + &mut wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1710,14 +1808,14 @@ mod tests { let mut printer = HCPrinter::new( &mut wam.machine_st.heap, + &mut wam.machine_st.atom_tbl, + &mut wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) ); - printer - .var_names - .insert(list_loc_as_cell!(1), Rc::new("L".to_string())); + printer.var_names.insert(list_loc_as_cell!(1), VarPtr::from("L")); let output = printer.print(); @@ -1741,6 +1839,8 @@ mod tests { { let printer = HCPrinter::new( &mut wam.machine_st.heap, + &mut wam.machine_st.atom_tbl, + &mut wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0), @@ -1758,6 +1858,8 @@ mod tests { { let printer = HCPrinter::new( &mut wam.machine_st.heap, + &mut wam.machine_st.atom_tbl, + &mut wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0), @@ -1773,14 +1875,14 @@ mod tests { { let mut printer = HCPrinter::new( &mut wam.machine_st.heap, + &mut wam.machine_st.atom_tbl, + &mut wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) ); - printer - .var_names - .insert(list_loc_as_cell!(1), Rc::new("L".to_string())); + printer.var_names.insert(list_loc_as_cell!(1), VarPtr::from("L")); let output = printer.print(); @@ -1803,6 +1905,8 @@ mod tests { { let mut printer = HCPrinter::new( &mut wam.machine_st.heap, + &mut wam.machine_st.atom_tbl, + &mut wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1812,7 +1916,7 @@ mod tests { let output = printer.print(); - assert_eq!(output.result(), "[_1,_3,_5,_7,_9,...]"); + assert_eq!(output.result(), "[_1,_3,_5,_7,_9|...]"); } all_cells_unmarked(&wam.machine_st.heap); @@ -1824,6 +1928,8 @@ mod tests { { let printer = HCPrinter::new( &mut wam.machine_st.heap, + &mut wam.machine_st.atom_tbl, + &mut wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), pstr_loc_as_cell!(0) @@ -1848,13 +1954,17 @@ mod tests { wam.machine_st.heap.push(empty_list_as_cell!()); { - let printer = HCPrinter::new( + let mut printer = HCPrinter::new( &mut wam.machine_st.heap, + &mut wam.machine_st.atom_tbl, + &mut wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0), ); + printer.double_quotes = true; + let output = printer.print(); assert_eq!(output.result(), "\"abcabc\""); @@ -1873,7 +1983,7 @@ mod tests { assert_eq!( &wam.parse_and_print_term("[a,b,\"a\",[a,b,c]].").unwrap(), - "[a,b,\"a\",\"abc\"]" + "[a,b,[a],[a,b,c]]" ); all_cells_unmarked(&wam.machine_st.heap); @@ -1881,11 +1991,44 @@ mod tests { assert_eq!( &wam.parse_and_print_term("[\"abc\",e,f,[g,e,h,Y,v|[X,Y]]].") .unwrap(), - "[\"abc\",e,f,[g,e,h,Y,v,X,Y]]" + "[[a,b,c],e,f,[g,e,h,Y,v,X,Y]]" ); all_cells_unmarked(&wam.machine_st.heap); assert_eq!(&wam.parse_and_print_term("f((a,b)).").unwrap(), "f((a,b))"); + + all_cells_unmarked(&wam.machine_st.heap); + + wam.op_dir.insert( + (atom!("+"), Fixity::In), + OpDesc::build_with(500, YFX as u8), + ); + wam.op_dir.insert( + (atom!("*"), Fixity::In), + OpDesc::build_with(400, YFX as u8), + ); + + assert_eq!(&wam.parse_and_print_term("[a|[] + b].").unwrap(), "[a|[]+b]"); + + all_cells_unmarked(&wam.machine_st.heap); + + assert_eq!(&wam.parse_and_print_term("[a|[b|c]*d].").unwrap(), "[a|[b|c]*d]"); + + all_cells_unmarked(&wam.machine_st.heap); + + wam.op_dir.insert( + (atom!("fy"), Fixity::Pre), + OpDesc::build_with(9, FY as u8), + ); + + wam.op_dir.insert( + (atom!("yf"), Fixity::Post), + OpDesc::build_with(9, YF as u8), + ); + + assert_eq!(&wam.parse_and_print_term("(fy (fy 1)yf)yf.").unwrap(), "(fy (fy 1)yf)yf"); + + assert_eq!(&wam.parse_and_print_term("fy(fy(yf(fy(1)))).").unwrap(), "fy fy (fy 1)yf"); } } diff --git a/src/http.rs b/src/http.rs index 887366f8..71d1682c 100644 --- a/src/http.rs +++ b/src/http.rs @@ -1,25 +1,54 @@ -use std::sync::Arc; -use std::convert::Infallible; - -use hyper::{Response, Request, Body}; -use tokio::sync::Mutex; -use tokio::sync::mpsc::{channel, Receiver, Sender}; +use std::sync::{Arc, Mutex, Condvar}; +use std::future::Future; +use std::pin::Pin; +use http_body_util::Full; +use bytes::Bytes; +use hyper::service::Service; +use hyper::{body::Incoming as IncomingBody, Request, Response}; pub struct HttpListener { - pub incoming: Receiver + pub incoming: std::sync::mpsc::Receiver } #[derive(Debug)] pub struct HttpRequest { - pub request: Request, + pub request: Request, pub response: HttpResponse, } -pub type HttpResponse = Sender>; +pub type HttpResponse = Arc<(Mutex, Mutex>>>, Condvar)>; -pub async fn serve_req(req: Request, tx: Arc>>) -> Result, Infallible> { - let (response_tx, mut rx) = channel(1); - let http_request = HttpRequest { request: req, response: response_tx }; - tx.lock().await.send(http_request).await.unwrap(); - Ok(rx.recv().await.unwrap()) +pub struct HttpService { + pub tx: std::sync::mpsc::SyncSender, +} + +impl Service> for HttpService { + type Response = Response>; + type Error = hyper::Error; + type Future = Pin> + Send>>; + + fn call(&mut self, req: Request) -> Self::Future { + // new connection! + // we send the Request info to Prolog + let response = Arc::new((Mutex::new(false), Mutex::new(None), Condvar::new())); + let http_request = HttpRequest { request: req, response: Arc::clone(&response) }; + self.tx.send(http_request).unwrap(); + + // we wait for the Response info from Prolog + { + let (ready, _response, cvar) = &*response; + let mut ready = ready.lock().unwrap(); + while !*ready { + ready = cvar.wait(ready).unwrap(); + } + } + { + let (_, response, _) = &*response; + let response = response.lock().unwrap().take(); + let res = response.expect("Data race error in HTTP Server"); + Box::pin(async move { + Ok(res) + }) + } + } } diff --git a/src/iterators.rs b/src/iterators.rs index 62054b04..adec2e4c 100644 --- a/src/iterators.rs +++ b/src/iterators.rs @@ -5,9 +5,7 @@ use crate::parser::ast::*; use std::cell::Cell; use std::collections::VecDeque; -use std::fmt; use std::iter::*; -use std::rc::Rc; use std::vec::Vec; #[derive(Debug, Clone)] @@ -18,34 +16,36 @@ pub(crate) enum TermRef<'a> { Clause(Level, &'a Cell, Atom, &'a Vec), PartialString(Level, &'a Cell, &'a String, &'a Box), CompleteString(Level, &'a Cell, Atom), - Var(Level, &'a Cell, Rc), + Var(Level, &'a Cell, VarPtr), } +/* impl<'a> TermRef<'a> { - pub(crate) fn level(self) -> Level { + pub(crate) fn level(&self) -> Level { match self { - TermRef::AnonVar(lvl) - | TermRef::Cons(lvl, ..) - | TermRef::Literal(lvl, ..) - | TermRef::Var(lvl, ..) - | TermRef::Clause(lvl, ..) - | TermRef::CompleteString(lvl, ..) - | TermRef::PartialString(lvl, ..) => lvl, + TermRef::AnonVar(lvl) | + TermRef::Cons(lvl, ..) | + TermRef::Literal(lvl, ..) | + TermRef::Var(lvl, ..) | + TermRef::Clause(lvl, ..) | + TermRef::CompleteString(lvl, ..) | + TermRef::PartialString(lvl, ..) => *lvl, } } } +*/ #[derive(Debug)] pub(crate) enum TermIterState<'a> { AnonVar(Level), - Literal(Level, &'a Cell, &'a Literal), Clause(Level, usize, &'a Cell, Atom, &'a Vec), + Literal(Level, &'a Cell, &'a Literal), InitialCons(Level, &'a Cell, &'a Term, &'a Term), FinalCons(Level, &'a Cell, &'a Term, &'a Term), InitialPartialString(Level, &'a Cell, &'a String, &'a Box), FinalPartialString(Level, &'a Cell, &'a String, &'a Box), CompleteString(Level, &'a Cell, Atom), - Var(Level, &'a Cell, Rc), + Var(Level, &'a Cell, VarPtr), } impl<'a> TermIterState<'a> { @@ -65,7 +65,7 @@ impl<'a> TermIterState<'a> { Term::CompleteString(cell, atom) => { TermIterState::CompleteString(lvl, cell, *atom) } - Term::Var(cell, var) => TermIterState::Var(lvl, cell, var.clone()), + Term::Var(cell, var_ptr) => TermIterState::Var(lvl, cell, var_ptr.clone()), } } } @@ -77,10 +77,10 @@ pub(crate) struct QueryIterator<'a> { impl<'a> QueryIterator<'a> { fn push_subterm(&mut self, lvl: Level, term: &'a Term) { - self.state_stack - .push(TermIterState::subterm_to_state(lvl, term)); + self.state_stack.push(TermIterState::subterm_to_state(lvl, term)); } + /* fn from_rule_head_clause(terms: &'a Vec) -> Self { let state_stack = terms .iter() @@ -90,6 +90,7 @@ impl<'a> QueryIterator<'a> { QueryIterator { state_stack } } + */ fn from_term(term: &'a Term) -> Self { let state = match term { @@ -106,7 +107,7 @@ impl<'a> QueryIterator<'a> { *name, terms, ), - Term::Var(cell, var) => TermIterState::Var(Level::Root, cell, var.clone()), + Term::Var(cell, var_ptr) => TermIterState::Var(Level::Root, cell, var_ptr.clone()), }; QueryIterator { @@ -114,46 +115,24 @@ impl<'a> QueryIterator<'a> { } } - fn new(term: &'a QueryTerm) -> Self { + fn extend_state(&mut self, lvl: Level, term: &'a QueryTerm) { match term { &QueryTerm::Clause(ref cell, ClauseType::CallN(_), ref terms, _) => { - let state = TermIterState::Clause(Level::Root, 1, cell, atom!("$call"), terms); - QueryIterator { - state_stack: vec![state], - } + self.state_stack.push(TermIterState::Clause(lvl, 1, cell, atom!("$call"), terms)); } &QueryTerm::Clause(ref cell, ref ct, ref terms, _) => { - let state = TermIterState::Clause(Level::Root, 0, cell, ct.name(), terms); - QueryIterator { - state_stack: vec![state], - } + self.state_stack.push(TermIterState::Clause(lvl, 0, cell, ct.name(), terms)); } - &QueryTerm::UnblockedCut(ref cell) => { - let state = TermIterState::Var(Level::Root, cell, Rc::new("!".to_string())); - QueryIterator { - state_stack: vec![state], - } + _ => { } - &QueryTerm::GetLevelAndUnify(ref cell, ref var) => { - let state = TermIterState::Var(Level::Root, cell, var.clone()); - QueryIterator { - state_stack: vec![state], - } - } - &QueryTerm::Jump(ref vars) => { - let state_stack = vars - .iter() - .rev() - .map(|t| TermIterState::subterm_to_state(Level::Shallow, t)) - .collect(); - - QueryIterator { state_stack } - } - &QueryTerm::BlockedCut => QueryIterator { - state_stack: vec![], - }, } } + + pub fn new(term: &'a QueryTerm) -> Self { + let mut iter = QueryIterator { state_stack: vec![] }; + iter.extend_state(Level::Root, term); + iter + } } impl<'a> Iterator for QueryIterator<'a> { @@ -212,8 +191,8 @@ impl<'a> Iterator for QueryIterator<'a> { TermIterState::Literal(lvl, cell, constant) => { return Some(TermRef::Literal(lvl, cell, constant)); } - TermIterState::Var(lvl, cell, var) => { - return Some(TermRef::Var(lvl, cell, var)); + TermIterState::Var(lvl, cell, var_ptr) => { + return Some(TermRef::Var(lvl, cell, var_ptr)); } }; } @@ -225,7 +204,7 @@ impl<'a> Iterator for QueryIterator<'a> { #[derive(Debug)] pub(crate) struct FactIterator<'a> { state_queue: VecDeque>, - iterable_root: bool, + iterable_root: RootIterationPolicy, } impl<'a> FactIterator<'a> { @@ -242,11 +221,11 @@ impl<'a> FactIterator<'a> { FactIterator { state_queue, - iterable_root: false, + iterable_root: RootIterationPolicy::NotIterated, } } - fn new(term: &'a Term, iterable_root: bool) -> Self { + fn new(term: &'a Term, iterable_root: RootIterationPolicy) -> Self { let states = match term { Term::AnonVar => { vec![TermIterState::AnonVar(Level::Root)] @@ -278,8 +257,8 @@ impl<'a> FactIterator<'a> { Term::Literal(cell, constant) => { vec![TermIterState::Literal(Level::Root, cell, constant)] } - Term::Var(cell, var) => { - vec![TermIterState::Var(Level::Root, cell, var.clone())] + Term::Var(cell, var_ptr) => { + vec![TermIterState::Var(Level::Root, cell, var_ptr.clone())] } }; @@ -305,7 +284,7 @@ impl<'a> Iterator for FactIterator<'a> { } match lvl { - Level::Root if !self.iterable_root => continue, + Level::Root if !self.iterable_root.iterable() => continue, _ => return Some(TermRef::Clause(lvl, cell, name, child_terms)), }; } @@ -325,8 +304,8 @@ impl<'a> Iterator for FactIterator<'a> { TermIterState::Literal(lvl, cell, constant) => { return Some(TermRef::Literal(lvl, cell, constant)) } - TermIterState::Var(lvl, cell, var) => { - return Some(TermRef::Var(lvl, cell, var)); + TermIterState::Var(lvl, cell, var_ptr) => { + return Some(TermRef::Var(lvl, cell, var_ptr)); } _ => {} } @@ -340,193 +319,130 @@ pub(crate) fn post_order_iter<'a>(term: &'a Term) -> QueryIterator<'a> { QueryIterator::from_term(term) } -pub(crate) fn breadth_first_iter<'a>(term: &'a Term, iterable_root: bool) -> FactIterator<'a> { +pub(crate) fn breadth_first_iter<'a>(term: &'a Term, iterable_root: RootIterationPolicy) -> FactIterator<'a> { FactIterator::new(term, iterable_root) } +#[derive(Debug, Copy, Clone)] +enum ClauseIteratorState<'a> { + RemainingChunks(&'a VecDeque, usize), + RemainingBranches(&'a Vec>, usize), +} + +#[derive(Debug, Clone)] +pub(crate) enum ClauseItem<'a> { + FirstBranch(usize), + NextBranch, + BranchEnd(usize), + Chunk(&'a VecDeque), +} + #[derive(Debug)] -pub(crate) enum ChunkedTerm<'a> { - HeadClause(Atom, &'a Vec), - BodyTerm(&'a QueryTerm), +pub(crate) struct ClauseIterator<'a> { + state_stack: Vec>, + remaining_chunks_on_stack: usize, } -pub(crate) fn query_term_post_order_iter<'a>(query_term: &'a QueryTerm) -> QueryIterator<'a> { - QueryIterator::new(query_term) -} - -impl<'a> ChunkedTerm<'a> { - pub(crate) fn post_order_iter(&self) -> QueryIterator<'a> { - match self { - &ChunkedTerm::BodyTerm(qt) => QueryIterator::new(qt), - &ChunkedTerm::HeadClause(_, terms) => QueryIterator::from_rule_head_clause(terms), +fn state_from_chunked_terms<'a>(chunk_vec: &'a VecDeque) -> ClauseIteratorState<'a> { + if chunk_vec.len() == 1 { + if let Some(ChunkedTerms::Branch(ref branches)) = chunk_vec.front() { + return ClauseIteratorState::RemainingBranches(branches, 0); } } + + ClauseIteratorState::RemainingChunks(chunk_vec, 0) } -fn contains_cut_var<'a, Iter: Iterator>(terms: Iter) -> bool { - for term in terms { - if let &Term::Var(_, ref var) = term { - if var.as_str() == "!" { - return true; +impl<'a> ClauseIterator<'a> { + pub fn new(clauses: &'a ChunkedTermVec) -> Self { + match state_from_chunked_terms(&clauses.chunk_vec) { + state @ ClauseIteratorState::RemainingBranches(..) => { + Self { + state_stack: vec![state], + remaining_chunks_on_stack: 0, + } + } + state @ ClauseIteratorState::RemainingChunks(..) => { + Self { + state_stack: vec![state], + remaining_chunks_on_stack: 1, + } } } } - false -} - -pub(crate) struct ChunkedIterator<'a> { - pub(crate) chunk_num: usize, - iter: Box> + 'a>, - deep_cut_encountered: bool, - cut_var_in_head: bool, -} - -impl<'a> fmt::Debug for ChunkedIterator<'a> { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.debug_struct("ChunkedIterator") - .field("chunk_num", &self.chunk_num) - // Hacky solution. - .field("iter", &"Box> + 'a>") - .field("deep_cut_encountered", &self.deep_cut_encountered) - .field("cut_var_in_head", &self.cut_var_in_head) - .finish() + #[inline(always)] + pub fn in_tail_position(&self) -> bool { + self.remaining_chunks_on_stack == 0 } -} -type ChunkedIteratorItem<'a> = (usize, usize, Vec>); -type RuleBodyIteratorItem<'a> = (usize, usize, Vec<&'a QueryTerm>); + fn branch_end_depth(&mut self) -> usize { + let mut depth = 1; -impl<'a> ChunkedIterator<'a> { - pub(crate) fn rule_body_iter(self) -> Box> + 'a> { - Box::new(self.filter_map(|(cn, lt_arity, terms)| { - let filtered_terms: Vec<_> = terms - .into_iter() - .filter_map(|ct| match ct { - ChunkedTerm::BodyTerm(qt) => Some(qt), - _ => None, - }) - .collect(); - - if filtered_terms.is_empty() { - None - } else { - Some((cn, lt_arity, filtered_terms)) + while let Some(state) = self.state_stack.pop() { + match state { + ClauseIteratorState::RemainingBranches(terms, focus) if terms.len() == focus => { + depth += 1; + } + _ => { + self.state_stack.push(state); + break; + } } - })) - } - - pub(crate) fn from_rule_body(p1: &'a QueryTerm, clauses: &'a Vec) -> Self { - let inner_iter = Box::new(once(ChunkedTerm::BodyTerm(p1))); - let iter = inner_iter.chain(clauses.iter().map(|t| ChunkedTerm::BodyTerm(t))); - - ChunkedIterator { - chunk_num: 0, - iter: Box::new(iter), - deep_cut_encountered: false, - cut_var_in_head: false, - } - } - - pub(crate) fn from_rule(rule: &'a Rule) -> Self { - let &Rule { - head: (ref name, ref args, ref p1), - ref clauses, - } = rule; - - let iter = once(ChunkedTerm::HeadClause(name.clone(), args)); - let inner_iter = Box::new(once(ChunkedTerm::BodyTerm(p1))); - let iter = iter.chain(inner_iter.chain(clauses.iter().map(|t| ChunkedTerm::BodyTerm(t)))); - - ChunkedIterator { - chunk_num: 0, - iter: Box::new(iter), - deep_cut_encountered: false, - cut_var_in_head: false, - } - } - - pub(crate) fn encountered_deep_cut(&self) -> bool { - self.deep_cut_encountered - } - - fn take_chunk(&mut self, term: ChunkedTerm<'a>) -> (usize, usize, Vec>) { - let mut arity = 0; - let mut item = Some(term); - let mut result = Vec::new(); - - while let Some(term) = item { - match term { - ChunkedTerm::HeadClause(_, terms) => { - if contains_cut_var(terms.iter()) { - self.cut_var_in_head = true; - } - - result.push(term); - } - ChunkedTerm::BodyTerm(&QueryTerm::Jump(ref vars)) => { - result.push(term); - arity = vars.len(); - - if contains_cut_var(vars.iter()) && !self.cut_var_in_head { - self.deep_cut_encountered = true; - } - - break; - } - ChunkedTerm::BodyTerm(&QueryTerm::BlockedCut) => { - result.push(term); - - if self.chunk_num > 0 { - self.deep_cut_encountered = true; - } - } - ChunkedTerm::BodyTerm(&QueryTerm::GetLevelAndUnify(..)) => { - self.deep_cut_encountered = true; - - result.push(term); - arity = 1; - break; - } - ChunkedTerm::BodyTerm(&QueryTerm::UnblockedCut(..)) => { - self.deep_cut_encountered = true; - result.push(term); - } - ChunkedTerm::BodyTerm(&QueryTerm::Clause(_, ClauseType::Inlined(_), ..)) => { - result.push(term) - } - ChunkedTerm::BodyTerm(&QueryTerm::Clause( - _, - ClauseType::CallN(_), - ref subterms, - _, - )) => { - result.push(term); - arity = subterms.len() + 1; - break; - } - ChunkedTerm::BodyTerm(qt) => { - result.push(term); - arity = qt.arity(); - break; - } - }; - - item = self.iter.next(); } - let chunk_num = self.chunk_num; - self.chunk_num += 1; - - (chunk_num, arity, result) + depth } } -impl<'a> Iterator for ChunkedIterator<'a> { - // the chunk number, last term arity, and vector of references. - type Item = ChunkedIteratorItem<'a>; +impl<'a> Iterator for ClauseIterator<'a> { + type Item = ClauseItem<'a>; fn next(&mut self) -> Option { - self.iter.next().map(|term| self.take_chunk(term)) + while let Some(state) = self.state_stack.pop() { + match state { + ClauseIteratorState::RemainingChunks(chunks, focus) if focus < chunks.len() => { + if focus + 1 < chunks.len() { + self.state_stack.push(ClauseIteratorState::RemainingChunks(chunks, focus + 1)); + } else { + self.remaining_chunks_on_stack -= 1; + } + + match &chunks[focus] { + ChunkedTerms::Branch(branches) => { + self.state_stack.push(ClauseIteratorState::RemainingBranches(branches, 0)); + } + ChunkedTerms::Chunk(chunk) => { + return Some(ClauseItem::Chunk(chunk)); + } + } + } + ClauseIteratorState::RemainingChunks(chunks, focus) => { + debug_assert_eq!(chunks.len(), focus); + } + ClauseIteratorState::RemainingBranches(branches, focus) if focus < branches.len() => { + self.state_stack.push(ClauseIteratorState::RemainingBranches(&branches, focus + 1)); + let state = state_from_chunked_terms(&branches[focus]); + + if let ClauseIteratorState::RemainingChunks(..) = &state { + self.remaining_chunks_on_stack += 1; + } + + self.state_stack.push(state); + + return if focus == 0 { + Some(ClauseItem::FirstBranch(branches.len())) + } else { + Some(ClauseItem::NextBranch) + }; + } + ClauseIteratorState::RemainingBranches(branches, focus) => { + debug_assert_eq!(branches.len(), focus); + return Some(ClauseItem::BranchEnd(self.branch_end_depth())); + } + } + } + + None } } diff --git a/src/lib.rs b/src/lib.rs index d643296f..81d8d56e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,7 +17,8 @@ mod allocator; mod arithmetic; pub mod codegen; mod debray_allocator; -mod fixtures; +mod ffi; +mod variable_records; mod forms; mod heap_iter; pub mod heap_print; diff --git a/src/lib/arithmetic.pl b/src/lib/arithmetic.pl index ee719a18..3093d907 100644 --- a/src/lib/arithmetic.pl +++ b/src/lib/arithmetic.pl @@ -1,4 +1,9 @@ -:- module(arithmetic, [expmod/4, lsb/2, msb/2, number_to_rational/2, +/** Arithmetic predicates + +These predicates are additions to standard the arithmetic functions provided by `is/2`. +*/ + +:- module(arithmetic, [expmod/4, lcm/3, lsb/2, msb/2, number_to_rational/2, number_to_rational/3, popcount/2, rational_numerator_denominator/3]). @@ -6,6 +11,10 @@ :- use_module(library(error)). :- use_module(library(lists), [append/3, member/2]). + +%% expmod(+Base, +Expo, +Mod, -R). +% +% Modular exponentiation. Base, Expo and Mod must be integers. expmod(Base, Expo, Mod, R) :- ( member(N, [Base, Expo, Mod]), var(N) -> instantiation_error(expmod/4) ; member(N, [Base, Expo, Mod]), \+ integer(N) -> @@ -28,6 +37,25 @@ expmod_(Base0, Expo0, Mod, C, R) :- Base is (Base0 * Base0) mod Mod, expmod_(Base, Expo, Mod, C, R). +%% lcm(+A, +B, -Lcm) is det. +% +% Calculates the Least common multiple for A and B: the smallest positive integer +% that is divisible by both A and B. +% +% A and B need to be integers. +lcm(A, B, X) :- + builtins:must_be_number(A, lcm/2), + builtins:must_be_number(B, lcm/2), + ( \+ integer(A) -> type_error(integer, A, lcm/2) + ; \+ integer(B) -> type_error(integer, B, lcm/2) + ; (A = 0, B = 0) -> X = 0 + ; builtins:can_be_number(X, lcm/2), + X is abs(B) // gcd(A,B) * abs(A) + ). + +%% lsb(+X, -N). +% +% True iff N is the least significat bit of integer X lsb(X, N) :- builtins:must_be_number(X, lsb/2), ( \+ integer(X) -> type_error(integer, X, lsb/2) @@ -37,6 +65,9 @@ lsb(X, N) :- msb_(X1, -1, N) ). +%% msb(+X, -N). +% +% True iff N is the most significant bit of integer X msb(X, N) :- builtins:must_be_number(X, msb/2), ( \+ integer(X) -> type_error(integer, X, msb/2) @@ -52,6 +83,9 @@ msb_(X, M, N) :- M1 is M + 1, msb_(X1, M1, N). +%% number_to_rational(+Real, -Fraction). +% +% True iff given a number Real, Fraction is the same number represented as a fraction. number_to_rational(Real, Fraction) :- ( var(Real) -> instantiation_error(number_to_rational/2) ; integer(Real) -> Fraction is Real rdiv 1 @@ -110,12 +144,20 @@ simplify_fraction(A0/B0, A/B) :- A is A0 // G, B is B0 // G. +%% rational_numerator_denominator(+Fraction, -Numerator, -Denominator). +% +% True iff given a fraction Fraction, Numerator is the numerator of that fraction +% and Denominator the denominator. rational_numerator_denominator(R, N, D) :- write_term_to_chars(R, [], Cs), append(Ns, [' ', r, d, i, v, ' '|Ds], Cs), number_chars(N, Ns), number_chars(D, Ds). +%% popcount(+Number, -Bits1). +% +% True iff given an integer Number, Bits1 is the amount of 1 bits the binary representation +% of that number has. popcount(X, N) :- must_be(integer, X), '$popcount'(X, N). diff --git a/src/lib/assoc.pl b/src/lib/assoc.pl index 79e174e6..e32e7d82 100644 --- a/src/lib/assoc.pl +++ b/src/lib/assoc.pl @@ -54,28 +54,27 @@ :- use_module(library(lists)). -/** Binary associations +/** Binary associations Assocs are Key-Value associations implemented as a balanced binary tree (AVL tree). -@see library(pairs), library(rbtrees) -@author R.A.O'Keefe, L.Damas, V.S.Costa and Jan Wielemaker +Authors: R.A.O'Keefe, L.Damas, V.S.Costa and Jan Wielemaker */ :- meta_predicate map_assoc(1, ?). :- meta_predicate map_assoc(2, ?, ?). -%! empty_assoc(?Assoc) is semidet. +%% empty_assoc(?Assoc) is semidet. % -% Is true if Assoc is the empty association list. +% Is true if Assoc is the empty association list. empty_assoc(t). -%! assoc_to_list(+Assoc, -Pairs) is det. +%% assoc_to_list(+Assoc, -Pairs) is det. % -% Translate Assoc to a list Pairs of Key-Value pairs. The keys -% in Pairs are sorted in ascending order. +% Translate Assoc to a list Pairs of Key-Value pairs. The keys +% in Pairs are sorted in ascending order. assoc_to_list(Assoc, List) :- assoc_to_list(Assoc, List, []). @@ -86,10 +85,10 @@ assoc_to_list(t(Key,Val,_,L,R), List, Rest) :- assoc_to_list(t, List, List). -%! assoc_to_keys(+Assoc, -Keys) is det. +%% assoc_to_keys(+Assoc, -Keys) is det. % -% True if Keys is the list of keys in Assoc. The keys are sorted -% in ascending order. +% True if Keys is the list of keys in Assoc. The keys are sorted +% in ascending order. assoc_to_keys(Assoc, List) :- assoc_to_keys(Assoc, List, []). @@ -100,11 +99,11 @@ assoc_to_keys(t(Key,_,_,L,R), List, Rest) :- assoc_to_keys(t, List, List). -%! assoc_to_values(+Assoc, -Values) is det. +%% assoc_to_values(+Assoc, -Values) is det. % -% True if Values is the list of values in Assoc. Values are -% ordered in ascending order of the key to which they were -% associated. Values may contain duplicates. +% True if Values is the list of values in Assoc. Values are +% ordered in ascending order of the key to which they were +% associated. Values may contain duplicates. assoc_to_values(Assoc, List) :- assoc_to_values(Assoc, List, []). @@ -114,12 +113,12 @@ assoc_to_values(t(_,Value,_,L,R), List, Rest) :- assoc_to_values(R, More, Rest). assoc_to_values(t, List, List). -%! is_assoc(+Assoc) is semidet. +%% is_assoc(+Assoc) is semidet. % -% True if Assoc is an association list. This predicate checks -% that the structure is valid, elements are in order, and tree -% is balanced to the extent guaranteed by AVL trees. I.e., -% branches of each subtree differ in depth by at most 1. +% True if Assoc is an association list. This predicate checks +% that the structure is valid, elements are in order, and tree +% is balanced to the extent guaranteed by AVL trees. I.e., +% branches of each subtree differ in depth by at most 1. is_assoc(Assoc) :- is_assoc(Assoc, _Min, _Max, _Depth). @@ -151,12 +150,10 @@ balance(=,-). balance(<,<). balance(>,>). -%! gen_assoc(?Key, +Assoc, ?Value) is nondet. +%% gen_assoc(?Key, +Assoc, ?Value) is nondet. % -% True if Key-Value is an association in Assoc. Enumerates keys in -% ascending order on backtracking. -% -% @see get_assoc/3. +% True if Key-Value is an association in Assoc. Enumerates keys in +% ascending order on backtracking. gen_assoc(Key, Assoc, Value) :- ( ground(Key) @@ -171,11 +168,11 @@ gen_assoc_(Key, t(_,_,_,_,R), Val) :- gen_assoc_(Key, R, Val). -%! get_assoc(+Key, +Assoc, -Value) is semidet. +%% get_assoc(+Key, +Assoc, -Value) is semidet. % -% True if Key-Value is an association in Assoc. +% True if Key-Value is an association in Assoc. % -% @error type_error(assoc, Assoc) if Assoc is not an association list. +% Throws error: `type_error(assoc, Assoc)` if Assoc is not an association list. get_assoc(Key, Assoc, Val) :- must_be(assoc, Assoc), @@ -201,9 +198,9 @@ get_assoc(>, Key, _, _, Tree, Val) :- % :- endif. -%! get_assoc(+Key, +Assoc0, ?Val0, ?Assoc, ?Val) is semidet. +%% get_assoc(+Key, +Assoc0, ?Val0, ?Assoc, ?Val) is semidet. % -% True if Key-Val0 is in Assoc0 and Key-Val is in Assoc. +% True if Key-Val0 is in Assoc0 and Key-Val is in Assoc. get_assoc(Key, t(K,V,B,L,R), Val, t(K,NV,B,NL,NR), NVal) :- compare(Rel, Key, K), @@ -216,12 +213,12 @@ get_assoc(>, Key, V, L, R, Val, V, L, NR, NVal) :- get_assoc(Key, R, Val, NR, NVal). -%! list_to_assoc(+Pairs, -Assoc) is det. +%% list_to_assoc(+Pairs, -Assoc) is det. % -% Create an association from a list Pairs of Key-Value pairs. List -% must not contain duplicate keys. +% Create an association from a list Pairs of Key-Value pairs. List +% must not contain duplicate keys. % -% @error domain_error(unique_key_pairs, List) if List contains duplicate keys +% Throws error: `domain_error(unique_key_pairs, List)` if List contains duplicate keys list_to_assoc(List, Assoc) :- ( List = [] -> Assoc = t @@ -246,13 +243,13 @@ list_to_assoc(N, List, More, Depth, t(K,V,Balance,L,R)) :- compare(B, RDepth, LDepth), balance(B, Balance). -%! ord_list_to_assoc(+Pairs, -Assoc) is det. +%% ord_list_to_assoc(+Pairs, -Assoc) is det. % -% Assoc is created from an ordered list Pairs of Key-Value -% pairs. The pairs must occur in strictly ascending order of -% their keys. +% Assoc is created from an ordered list Pairs of Key-Value +% pairs. The pairs must occur in strictly ascending order of +% their keys. % -% @error domain_error(key_ordered_pairs, List) if pairs are not ordered. +% Throws error: `domain_error(key_ordered_pairs, List)` if pairs are not ordered. ord_list_to_assoc(Sorted, Assoc) :- ( Sorted = [] -> Assoc = t @@ -263,9 +260,9 @@ ord_list_to_assoc(Sorted, Assoc) :- ) ). -%! ord_pairs(+Pairs) is semidet +%% ord_pairs(+Pairs) is semidet % -% True if Pairs is a list of Key-Val pairs strictly ordered by key. +% True if Pairs is a list of Key-Val pairs strictly ordered by key. ord_pairs([K-_V|Rest]) :- ord_pairs(Rest, K). @@ -274,9 +271,9 @@ ord_pairs([K-_V|Rest], K0) :- K0 @< K, ord_pairs(Rest, K). -%! map_assoc(:Pred, +Assoc) is semidet. +%% map_assoc(:Pred, +Assoc) is semidet. % -% True if Pred(Value) is true for all values in Assoc. +% True if Pred(Value) is true for all values in Assoc. map_assoc(Pred, T) :- map_assoc_(T, Pred). @@ -287,10 +284,10 @@ map_assoc_(t(_,Val,_,L,R), Pred) :- call(Pred, Val), map_assoc_(R, Pred). -%! map_assoc(:Pred, +Assoc0, ?Assoc) is semidet. +%% map_assoc(:Pred, +Assoc0, ?Assoc) is semidet. % -% Map corresponding values. True if Assoc is Assoc0 with Pred -% applied to all corresponding pairs of of values. +% Map corresponding values. True if Assoc is Assoc0 with Pred +% applied to all corresponding pairs of of values. map_assoc(Pred, T0, T) :- map_assoc_(T0, Pred, T). @@ -302,9 +299,9 @@ map_assoc_(t(Key,Val,B,L0,R0), Pred, t(Key,Ans,B,L1,R1)) :- map_assoc_(R0, Pred, R1). -%! max_assoc(+Assoc, -Key, -Value) is semidet. +%% max_assoc(+Assoc, -Key, -Value) is semidet. % -% True if Key-Value is in Assoc and Key is the largest key. +% True if Key-Value is in Assoc and Key is the largest key. max_assoc(t(K,V,_,_,R), Key, Val) :- max_assoc(R, K, V, Key, Val). @@ -314,9 +311,9 @@ max_assoc(t(K,V,_,_,R), _, _, Key, Val) :- max_assoc(R, K, V, Key, Val). -%! min_assoc(+Assoc, -Key, -Value) is semidet. +%% min_assoc(+Assoc, -Key, -Value) is semidet. % -% True if Key-Value is in assoc and Key is the smallest key. +% True if Key-Value is in assoc and Key is the smallest key. min_assoc(t(K,V,_,L,_), Key, Val) :- min_assoc(L, K, V, Key, Val). @@ -326,10 +323,10 @@ min_assoc(t(K,V,_,L,_), _, _, Key, Val) :- min_assoc(L, K, V, Key, Val). -%! put_assoc(+Key, +Assoc0, +Value, -Assoc) is det. +%% put_assoc(+Key, +Assoc0, +Value, -Assoc) is det. % -% Assoc is Assoc0, except that Key is associated with -% Value. This can be used to insert and change associations. +% Assoc is Assoc0, except that Key is associated with +% Value. This can be used to insert and change associations. put_assoc(Key, A0, Value, A) :- insert(A0, Key, Value, A, _). @@ -361,11 +358,11 @@ table(< , right , - , no , no ) :- !. table(> , left , - , no , no ) :- !. table(> , right , - , no , yes ) :- !. -%! del_min_assoc(+Assoc0, ?Key, ?Val, -Assoc) is semidet. +%% del_min_assoc(+Assoc0, ?Key, ?Val, -Assoc) is semidet. % -% True if Key-Value is in Assoc0 and Key is the smallest key. -% Assoc is Assoc0 with Key-Value removed. Warning: This will -% succeed with _no_ bindings for Key or Val if Assoc0 is empty. +% True if Key-Value is in Assoc0 and Key is the smallest key. +% Assoc is Assoc0 with Key-Value removed. Warning: This will +% succeed with _no_ bindings for Key or Val if Assoc0 is empty. del_min_assoc(Tree, Key, Val, NewTree) :- del_min_assoc(Tree, Key, Val, NewTree, _DepthChanged). @@ -375,11 +372,11 @@ del_min_assoc(t(K,V,B,L,R), Key, Val, NewTree, Changed) :- del_min_assoc(L, Key, Val, NewL, LeftChanged), deladjust(LeftChanged, t(K,V,B,NewL,R), left, NewTree, Changed). -%! del_max_assoc(+Assoc0, ?Key, ?Val, -Assoc) is semidet. +%% del_max_assoc(+Assoc0, ?Key, ?Val, -Assoc) is semidet. % -% True if Key-Value is in Assoc0 and Key is the greatest key. -% Assoc is Assoc0 with Key-Value removed. Warning: This will -% succeed with _no_ bindings for Key or Val if Assoc0 is empty. +% True if Key-Value is in Assoc0 and Key is the greatest key. +% Assoc is Assoc0 with Key-Value removed. Warning: This will +% succeed with _no_ bindings for Key or Val if Assoc0 is empty. del_max_assoc(Tree, Key, Val, NewTree) :- del_max_assoc(Tree, Key, Val, NewTree, _DepthChanged). @@ -389,10 +386,10 @@ del_max_assoc(t(K,V,B,L,R), Key, Val, NewTree, Changed) :- del_max_assoc(R, Key, Val, NewR, RightChanged), deladjust(RightChanged, t(K,V,B,L,NewR), right, NewTree, Changed). -%! del_assoc(+Key, +Assoc0, ?Value, -Assoc) is semidet. +%% del_assoc(+Key, +Assoc0, ?Value, -Assoc) is semidet. % -% True if Key-Value is in Assoc0. Assoc is Assoc0 with -% Key-Value removed. +% True if Key-Value is in Assoc0. Assoc is Assoc0 with +% Key-Value removed. del_assoc(Key, A0, Value, A) :- delete(A0, Key, Value, A, _). diff --git a/src/lib/atts.pl b/src/lib/atts.pl index a9369426..d6ee47a3 100644 --- a/src/lib/atts.pl +++ b/src/lib/atts.pl @@ -19,77 +19,12 @@ '$default_attr_list'(PGs, Module, AttrVar). '$default_attr_list'([], _, _) --> []. -'$absent_attr'(V, Attr) :- - '$get_attr_list'(V, Ls), - '$absent_from_list'(Ls, Attr). - -'$absent_from_list'(X, Attr) :- - ( var(X) -> - true - ; X = [L|Ls], - L \= Attr -> - '$absent_from_list'(Ls, Attr) - ). - -'$get_attr'(V, Attr) :- - '$get_attr_list'(V, Ls), - nonvar(Ls), - '$get_from_list'(Ls, V, Attr). - -'$get_from_list'([L|Ls], V, Attr) :- - nonvar(L), - ( L \= Attr -> - nonvar(Ls), - '$get_from_list'(Ls, V, Attr) - ; L = Attr, - '$enqueue_attr_var'(V) - ). - -'$put_attr'(V, Attr) :- - '$get_attr_list'(V, Ls), - '$add_to_list'(Ls, V, Attr). - -'$add_to_list'(Ls, V, Attr) :- - ( var(Ls) -> - Ls = [Attr | _], - '$enqueue_attr_var'(V) - ; Ls = [_ | Ls0], - '$add_to_list'(Ls0, V, Attr) - ). - -'$del_attr'(Ls0, _, _) :- - var(Ls0), - !. -'$del_attr'(Ls0, V, Attr) :- - Ls0 = [Att | Ls1], - nonvar(Att), - ( Att \= Attr -> - '$del_attr_buried'(Ls0, Ls1, V, Attr) - ; '$enqueue_attr_var'(V), - '$del_attr_head'(V), - '$del_attr'(Ls1, V, Attr) - ). - -'$del_attr_step'(Ls1, V, Attr) :- - ( nonvar(Ls1) -> - Ls1 = [_ | Ls2], - '$del_attr_buried'(Ls1, Ls2, V, Attr) +'$absent_attr'(V, Module, Attr) :- + ( '$get_from_attr_list'(V, Module, Attr) -> + false ; true ). -%% assumptions: Ls0 is a list, Ls1 is its tail; -%% the head of Ls0 can be ignored. -'$del_attr_buried'(Ls0, Ls1, V, Attr) :- - ( var(Ls1) -> true - ; Ls1 = [Att | Ls2] -> - ( Att \= Attr -> - '$del_attr_buried'(Ls1, Ls2, V, Attr) - ; '$enqueue_attr_var'(V), - '$del_attr_non_head'(Ls0), %% set tail of Ls0 = tail of Ls1. can be undone by backtracking. - '$del_attr_step'(Ls1, V, Attr) - ) - ). - '$copy_attr_list'(L, _Module, []) :- var(L), !. '$copy_attr_list'([Module0:Att|Atts], Module, CopiedAtts) :- ( Module0 == Module -> @@ -145,38 +80,28 @@ put_attr(Name, Arity, Module) --> { functor(Attr, Name, Arity) }, [(put_atts(V, +Attr) :- !, - functor(Attr, Head, Arity), - functor(AttrForm, Head, Arity), - '$get_attr_list'(V, Ls), - atts:'$del_attr'(Ls, V, Module:AttrForm), - atts:'$put_attr'(V, Module:Attr)), - (put_atts(V, Attr) :- + '$put_to_attr_list'(V, Module, Attr)), + (put_atts(V, Attr) :- !, - functor(Attr, Head, Arity), - functor(AttrForm, Head, Arity), - '$get_attr_list'(V, Ls), - atts:'$del_attr'(Ls, V, Module:AttrForm), - atts:'$put_attr'(V, Module:Attr)), + '$put_to_attr_list'(V, Module, Attr)), (put_atts(V, -Attr) :- !, - functor(Attr, _, _), - '$get_attr_list'(V, Ls), - atts:'$del_attr'(Ls, V, Module:Attr))]. + '$del_from_attr_list'(V, Module, Attr))]. get_attr(Name, Arity, Module) --> { functor(Attr, Name, Arity) }, [(get_atts(V, +Attr) :- !, functor(Attr, _, _), - atts:'$get_attr'(V, Module:Attr)), + atts:'$get_from_attr_list'(V, Module, Attr)), (get_atts(V, Attr) :- !, functor(Attr, _, _), - atts:'$get_attr'(V, Module:Attr)), + atts:'$get_from_attr_list'(V, Module, Attr)), (get_atts(V, -Attr) :- !, functor(Attr, _, _), - atts:'$absent_attr'(V, Module:Attr))]. + atts:'$absent_attr'(V, Module, Attr))]. user:goal_expansion(Term, M:put_atts(Var, Attr)) :- nonvar(Term), diff --git a/src/lib/between.pl b/src/lib/between.pl index 2711e8e8..db87c45c 100644 --- a/src/lib/between.pl +++ b/src/lib/between.pl @@ -1,3 +1,10 @@ +/** Predicates that generate integers + +These predicates can be used to reason about integers in a reduced domain that +follow some property. `library(clpz)` provides another way of reasoning about +integers that may also be interesting. +*/ + :- module(between, [between/3, gen_int/1, gen_nat/1, numlist/2, numlist/3, repeat/1]). %% TODO: numlist/5. @@ -5,6 +12,24 @@ :- use_module(library(lists), [length/2]). :- use_module(library(error)). +%% between(+Lower, +Upper, -X). +% +% Given Lower and Upper are both integer numbers, true iff X is an integer so that _Lower =< X =< Upper_. +% Can be used both to check if X is between Lower and Upper or to generate an integer between +% Lower and Upper. +% +% Examples: +% +% ``` +% ?- between(10, 20, 15). +% true. +% ?- between(10, 20, 25). +% false. +% ?- between(3, 5, X). +% X = 3 +% ; X = 4 +% ; X = 5. +% ``` between(Lower, Upper, X) :- must_be(integer, Lower), must_be(integer, Upper), @@ -30,6 +55,9 @@ enumerate_nats(I0, N) :- I1 is I0 + 1, enumerate_nats(I1, N). +%% gen_nat(?N) +% +% True iff N is a natural number. gen_nat(N) :- can_be(integer, N), ( var(N) -> enumerate_nats(0, N) @@ -44,6 +72,9 @@ enumerate_ints(I0, N) :- I1 is I0 + 1, enumerate_ints(I1, N). +%% gen_int(?N) +% +% True iff N is an integer. gen_int(N) :- can_be(integer, N), ( var(N) -> enumerate_ints(0, N) @@ -55,9 +86,24 @@ repeat_integer(N) :- repeat_integer(N0) :- N0 > 0, N1 is N0 - 1, repeat_integer(N1). +%% repeat(+N) +% +% Succeeds N times. This predicate is only included for compatibility and *should not be used* +% because it lacks a declarative interpretation. repeat(N) :- must_be(integer, N), repeat_integer(N). +%% numlist(?Upper, ?List) +% +% True iff List is the list of integers _[1, ..., Upper]_. Example: +% +% ``` +% ?- numlist(X, Y). +% X = 1, Y = [1], +% ; X = 2, Y = [1,2] +% ; X = 3, Y = [1,2,3] +% ; ... . +% ``` numlist(Upper, List) :- ( integer(Upper) -> findall(X, between(1, Upper, X), List) ; List = [_|_], length(List, Upper), findall(X, between(1, Upper, X), List) @@ -106,5 +152,14 @@ gen_ints(L, U) :- ), L =< U. +%% numlist(?Lower, ?Upper, ?List). +% +% True iff List is a list of the form _[Lower, ..., Upper]_. +% Example: +% +% ``` +% ?- numlist(5, 10, X). +% X = [5,6,7,8,9,10]. +% ``` numlist(Lower, Upper, List) :- gen_ints(Lower, Upper), findall(X, between(Lower, Upper, X), List). diff --git a/src/lib/builtins.pl b/src/lib/builtins.pl index 1d2d0aed..81f63ddd 100644 --- a/src/lib/builtins.pl +++ b/src/lib/builtins.pl @@ -17,21 +17,39 @@ peek_char/1, peek_char/2, peek_code/1, peek_code/2, put_byte/1, put_byte/2, put_code/1, put_code/2, put_char/1, put_char/2, read/1, - read_term/2, read_term/3, repeat/0, retract/1, - retractall/1, set_prolog_flag/2, set_input/1, - set_stream_position/2, set_output/1, setof/3, - stream_property/2, sub_atom/5, subsumes_term/2, - term_variables/2, throw/1, true/0, - unify_with_occurs_check/2, write/1, write/2, - write_canonical/1, write_canonical/2, + read/2, read_term/2, read_term/3, repeat/0, + retract/1, retractall/1, set_prolog_flag/2, + set_input/1, set_stream_position/2, set_output/1, + setof/3, stream_property/2, sub_atom/5, + subsumes_term/2, term_variables/2, throw/1, + true/0, unify_with_occurs_check/2, write/1, + write/2, write_canonical/1, write_canonical/2, write_term/2, write_term/3, writeq/1, writeq/2]). +/** Builtin predicates + +This library, unlike the rest, is loaded by default and it exposes the most fundamental and general +predicates of the Prolog system under the ISO standard. Basic operators, metaprogramming, exceptions, +internal settings and basic I/O are all here. +*/ + % unify. + +%% =(?X, ?Y) +% +% True if X and Y can be unified. This is the most basic operation of Prolog. +% Unification also happens when doing head matching in a rule. X = X. +%% true. +% +% Always true. true. +%% false. +% +% Always false. false :- '$fail'. @@ -39,22 +57,59 @@ false :- '$fail'. % Once Scryer is bootstrapped, each is replaced with a version that % uses expand_goal to pass the expanded goal along to '$call'. + +%% call(Goal). +% +% Execute the Goal. Typically used when the Goal is not known at compile time. call(_). +%% call(Goal, ExtraArg1). +% +% Execute the Goal with ExtraArg1 appended to the argument list. For example: +% +% ?- call(format("~s~n"), ["Alain Colmerauer"]). +% Alain Colmerauer +% true. +% +% Which is equivalent to: `format("~s~n", ["Alain Colmerauer"]).` call(_, _). +%% call(Goal, ExtraArg1, ExtraArg2). +% +% Execute Goal with ExtraArg1 and ExtraArg2 appended to the argument list. call(_, _, _). +%% call(Goal, ExtraArg1, ExtraArg2, ExtraArg3). +% +% Execute Goal with ExtraArg1, ExtraArg2 and ExtraArg3 appended to the argument list. call(_, _, _, _). +%% call(Goal, ExtraArg1, ExtraArg2, ExtraArg3, ExtraArg4). +% +% Execute Goal with ExtraArg1, ExtraArg2, ExtraArg3 and ExtraArg4 appended to the argument list. call(_, _, _, _, _). +%% call(Goal, ExtraArg1, ExtraArg2, ExtraArg3, ExtraArg4, ExtraArg5). +% +% Execute Goal with ExtraArg1, ExtraArg2, ExtraArg3, ExtraArg4 and ExtraArg5 appended to the argument list. call(_, _, _, _, _, _). +%% call(Goal, ExtraArg1, ExtraArg2, ExtraArg3, ExtraArg4, ExtraArg5, ExtraArg6). +% +% Execute Goal with ExtraArg1, ExtraArg2, ExtraArg3, ExtraArg4, ExtraArg5 and ExtraArg6 appended +% to the argument list. call(_, _, _, _, _, _, _). +%% call(Goal, ExtraArg1, ExtraArg2, ExtraArg3, ExtraArg4, ExtraArg5, ExtraArg6, ExtraArg7). +% +% Execute Goal with ExtraArg1, ExtraArg2, ExtraArg3, ExtraArg4, ExtraArg5, ExtraArg6 and ExtraArg7 +% appended to the argument list. call(_, _, _, _, _, _, _, _). +%% call(Goal, ExtraArg1, ExtraArg2, ExtraArg3, ExtraArg4, ExtraArg5, ExtraArg6, ExtraArg7, ExtraArg8). +% +% Execute Goal with ExtraArg1, ExtraArg2, ExtraArg3, ExtraArg4, ExtraArg5, ExtraArg6, ExtraArg7 and +% ExtraArg8, appended to the argument list. call(_, _, _, _, _, _, _, _, _). @@ -62,6 +117,33 @@ call(_, _, _, _, _, _, _, _, _). % flags. +%% current_prolog_flag(Flag, Value) +% +% True iff Flag is a flag supported by the processor, and Value is the value currently associated with it. +% A flag is a setting which value affects internal operation of the Prolog system. Some flags are read-only, +% while others can be set with `set_prolog_flag/2`. +% +% The flags that Scryer Prolog support are: +% +% * `max_arity`: The max arity a predicate can have in Prolog. On Scryer is set to 1023. Read only. +% * `bounded`: `true` if integer arithmethic is bounded between some min/max values. On Scryer is always set +% to `false` since it supports unbounded integer arithmethic. Read only. +% * `integer_rounding_function`: Describes the rounding donde by `//` and `rem` functions. On Scryer is +% always set to `toward_zero`. Read only +% * `double_quotes`: Determines how double quoted strings are red by Prolog. Scryer uses `chars` by default +% which is a list of one-character atoms. Other values are codes (list of integers representing characters), +% and atom which creates a whole atom for the string value. Read and write. +% * `max_integer`: Maximum integer supported by the system. As Scryer Prolog has unbounded integer arithmethic, +% checking the value of this flag fails. Read only. +% * `min_integer`: Minimum integer supported by the system. As Scryer Prolog has unbounded integer arithmethic, +% checking the value of this flag fails. Read only. +% * `occurs_check`: Returns if the occurs check is enabled. The occurs check prevents the creation cyclic terms. +% Historically the Prolog unification algorithm didn't do that check so changing the value modifies how Prolog +% operates in the low-level. Possible values are `false` (default), `true` (unification has this check +% enabled) and `error` which throws an exception when a cylic term is created. Read and write. +% * `unknown`: How undefined predicates are handled when called. Possible values are `error` (the default, an error is thrown), +% `fail` (the call silently fails) and `warn` (the call fails and a warning about the undefined predicate is printed). +% current_prolog_flag(Flag, Value) :- Flag == max_arity, !, Value = 1023. current_prolog_flag(max_arity, 1023). current_prolog_flag(Flag, Value) :- Flag == bounded, !, Value = false. @@ -70,6 +152,8 @@ current_prolog_flag(Flag, Value) :- Flag == integer_rounding_function, !, Value current_prolog_flag(integer_rounding_function, toward_zero). current_prolog_flag(Flag, Value) :- Flag == double_quotes, !, '$get_double_quotes'(Value). current_prolog_flag(double_quotes, Value) :- '$get_double_quotes'(Value). +current_prolog_flag(Flag, Value) :- Flag == unknown, !, '$get_unknown'(Value). +current_prolog_flag(unknown, Value) :- '$get_unknown'(Value). current_prolog_flag(Flag, _) :- Flag == max_integer, !, '$fail'. current_prolog_flag(Flag, _) :- Flag == min_integer, !, '$fail'. current_prolog_flag(Flag, OccursCheckEnabled) :- @@ -83,6 +167,10 @@ current_prolog_flag(Flag, _) :- nonvar(Flag), throw(error(type_error(atom, Flag), current_prolog_flag/2)). % 8.17.2.3 a +%% set_prolog_flag(Flag, Value). +% +% Sets the internal value of the flag. To see the list of flags supported by Scryer Prolog, +% check `current_prolog_flag/2`. The flags that are read only will fail if you try to change their values set_prolog_flag(Flag, Value) :- (var(Flag) ; var(Value)), throw(error(instantiation_error, set_prolog_flag/2)). % 8.17.1.3 a, b @@ -106,6 +194,12 @@ set_prolog_flag(double_quotes, atom) :- !, '$set_double_quotes'(atom). % 7.11.2.5, list of char codes (UTF8). set_prolog_flag(double_quotes, codes) :- !, '$set_double_quotes'(codes). +set_prolog_flag(unknown, error) :- + !, '$set_unknown'(error). +set_prolog_flag(unknown, warning) :- + !, '$set_unknown'(warning). +set_prolog_flag(unknown, fail) :- + !, '$set_unknown'(fail). set_prolog_flag(occurs_check, true) :- !, '$set_sto_as_unify'. set_prolog_flag(occurs_check, false) :- @@ -123,24 +217,38 @@ set_prolog_flag(Flag, _) :- % control operators. +%% fail. +% +% A predicate that always fails. The more declarative `false/0` should be used instead. fail :- '$fail'. :- meta_predicate \+(0). -\+ G :- call(G), !, false. +%% \+(Goal) +% +% True iff Goal fails +\+ G :- call(G), !, '$fail'. \+ _. - -X \= X :- !, false. +%% \=(?X, ?Y) +% +% True iff X and Y can't be unified +X \= X :- !, '$fail'. _ \= _. :- meta_predicate once(0). +%% once(Goal) +% +% Execute Goal (like `call/1`) but exactly once, ignoring any kind of alternative solutions the original predicate +% could have generated. once(G) :- call(G), !. - +%% repeat. +% +% This predicate succeeds arbitrarily often, generating choice points with that. repeat. repeat :- repeat. @@ -151,101 +259,84 @@ repeat :- repeat. :- meta_predicate ->(0,0). - +%% ->(G1, G2) +% +% If-then and if-then-else constructs G1 -> G2 :- control_entry_point((G1 -> G2)). :- non_counted_backtracking staggered_if_then/2. staggered_if_then(G1, G2) :- - '$get_staggered_cp'(B), call(G1), - '$set_cp'(B), + !, call(G2). +%% ;(G1, G2) +% +% Disjunction (or) G1 ; G2 :- control_entry_point((G1 ; G2)). :- non_counted_backtracking staggered_sc/2. -staggered_sc(G, _) :- call(G). +staggered_sc(G, _) :- + ( nonvar(G), + G = '$call'(builtins:staggered_if_then(G1, G2)) -> + call(G1), + !, + call(G2) + ; call(G) + ). staggered_sc(_, G) :- call(G). - +%% !. +% +% Cut operator. Discards the choicepoints created since entering the prediacate in which the operator appears. +% Using cut is not recommended as it introduces a non-declarative flow of programming and makes it more difficult +% to reason about the programs. Also restricts the ability to run the program with alternative execution strategies !. :- non_counted_backtracking set_cp/1. set_cp(B) :- '$set_cp'(B). +%% ,(G1, G2) +% +% Conjuction (and) ','(G1, G2) :- control_entry_point((G1, G2)). + :- non_counted_backtracking control_entry_point/1. control_entry_point(G) :- functor(G, Name, Arity), - catch(builtins:control_entry_point_(G), - dispatch_prep_error, - builtins:throw(error(type_error(callable, G), Name/Arity))). - - -:- non_counted_backtracking control_entry_point_/1. - -control_entry_point_(G) :- '$get_cp'(B), - dispatch_prep(G,B,Conts), + catch('$call'(builtins:dispatch_prep(G,B,Conts)), + dispatch_prep_error, + '$call'(builtins:throw(error(type_error(callable, G), Name/Arity)))), dispatch_call_list(Conts). - :- non_counted_backtracking cont_list_to_goal/2. cont_list_goal([Cont], Cont) :- !. cont_list_goal(Conts, '$call'(builtins:dispatch_call_list(Conts))). -:- non_counted_backtracking module_qualified_cut/1. - -module_qualified_cut(Gs) :- - ( functor(Gs, call, 1) -> - arg(1, Gs, G1) - ; Gs = G1 - ), - functor(G1, (:), 2), - arg(2, G1, G2), - G2 == !. - - :- non_counted_backtracking dispatch_prep/3. dispatch_prep(Gs, B, [Cont|Conts]) :- ( callable(Gs) -> - ( functor(Gs, ',', 2) -> - arg(1, Gs, G1), - arg(2, Gs, G2), - dispatch_prep(G1, B, IConts1), - cont_list_goal(IConts1, Cont), - dispatch_prep(G2, B, Conts) - ; functor(Gs, ';', 2) -> - arg(1, Gs, G1), - arg(2, Gs, G2), - dispatch_prep(G1, B, IConts0), - dispatch_prep(G2, B, IConts1), - cont_list_goal(IConts0, Cont0), - cont_list_goal(IConts1, Cont1), - Cont = '$call'(builtins:staggered_sc(Cont0, Cont1)), - Conts = [] - ; functor(Gs, ->, 2) -> - arg(1, Gs, G1), - arg(2, Gs, G2), - dispatch_prep(G1, B, IConts1), - dispatch_prep(G2, B, IConts2), - cont_list_goal(IConts1, Cont1), - cont_list_goal(IConts2, Cont2), - Cont = '$call'(builtins:staggered_if_then(Cont1, Cont2)), - Conts = [] - ; ( Gs == ! ; module_qualified_cut(Gs) ) -> + strip_module(Gs, M, Gs0), + ( nonvar(Gs0), + dispatch_prep_(Gs0, B, [Cont|Conts]) -> + true + ; Gs0 == ! -> Cont = '$call'(builtins:set_cp(B)), Conts = [] + ; nonvar(Gs0), + \+ callable(Gs0) -> + throw(dispatch_prep_error) ; Cont = Gs, Conts = [] ) @@ -256,6 +347,28 @@ dispatch_prep(Gs, B, [Cont|Conts]) :- ). +:- non_counted_backtracking dispatch_prep_/3. + +dispatch_prep_((G1, G2), B, [Cont|Conts]) :- + dispatch_prep(G1, B, IConts1), + cont_list_goal(IConts1, Cont), + dispatch_prep(G2, B, Conts). +dispatch_prep_((G1 ; G2), B, [Cont|Conts]) :- + dispatch_prep(G1, B, IConts0), + dispatch_prep(G2, B, IConts1), + cont_list_goal(IConts0, Cont0), + cont_list_goal(IConts1, Cont1), + Cont = '$call'(builtins:staggered_sc(Cont0, Cont1)), + Conts = []. +dispatch_prep_((G1 -> G2), B, [Cont|Conts]) :- + dispatch_prep(G1, B, IConts1), + dispatch_prep(G2, B, IConts2), + cont_list_goal(IConts1, Cont1), + cont_list_goal(IConts2, Cont2), + Cont = '$call'(builtins:staggered_if_then(Cont1, Cont2)), + Conts = []. + + :- non_counted_backtracking dispatch_call_list/1. dispatch_call_list([]). @@ -350,6 +463,13 @@ univ_errors(Term, List, N) :- :- non_counted_backtracking (=..)/2. +%% =..(Term, List) +% +% Univ operator. True iff Term is a term whose functor is the head of the List, and the rest of arguments of Term +% are in tail of the List. Example: +% +% ?- f(a, X) =.. List. +% List = [f,a,X]. Term =.. List :- univ_errors(Term, List, N), univ_worker(Term, List, N). @@ -414,30 +534,34 @@ parse_options_list(Options, Selector, DefaultPairs, OptionValues, Stub) :- parse_write_options(Options, OptionValues, Stub) :- - DefaultOptions = [ignore_ops-false, max_depth-0, numbervars-false, + DefaultOptions = [double_quotes-false, ignore_ops-false, max_depth-0, numbervars-false, quoted-false, variable_names-[]], parse_options_list(Options, builtins:parse_write_options_, DefaultOptions, OptionValues, Stub). + +parse_write_options_(double_quotes(DoubleQuotes), double_quotes-DoubleQuotes) :- + ( nonvar(DoubleQuotes), + lists:member(DoubleQuotes, [true, false]), + ! + ; throw(error(domain_error(write_option, double_quotes(DoubleQuotes)), _)) + ). parse_write_options_(ignore_ops(IgnoreOps), ignore_ops-IgnoreOps) :- ( nonvar(IgnoreOps), lists:member(IgnoreOps, [true, false]), ! - ; - throw(error(domain_error(write_option, ignore_ops(IgnoreOps)), _)) + ; throw(error(domain_error(write_option, ignore_ops(IgnoreOps)), _)) ). parse_write_options_(quoted(Quoted), quoted-Quoted) :- ( nonvar(Quoted), lists:member(Quoted, [true, false]), ! - ; - throw(error(domain_error(write_option, quoted(Quoted)), _)) + ; throw(error(domain_error(write_option, quoted(Quoted)), _)) ). parse_write_options_(numbervars(NumberVars), numbervars-NumberVars) :- ( nonvar(NumberVars), lists:member(NumberVars, [true, false]), ! - ; - throw(error(domain_error(write_option, numbervars(NumberVars)), _)) + ; throw(error(domain_error(write_option, numbervars(NumberVars)), _)) ). parse_write_options_(variable_names(VNNames), variable_names-VNNames) :- must_be_var_names_list(VNNames), @@ -446,8 +570,7 @@ parse_write_options_(max_depth(MaxDepth), max_depth-MaxDepth) :- ( integer(MaxDepth), MaxDepth >= 0, ! - ; - throw(error(domain_error(write_option, max_depth(MaxDepth)), _)) + ; throw(error(domain_error(write_option, max_depth(MaxDepth)), _)) ). parse_write_options_(E, _) :- throw(error(domain_error(write_option, E), _)). @@ -476,36 +599,71 @@ must_be_var_names_list_([VarName | VarNames], List) :- ; throw(error(instantiation_error, write_term/2)) ). - +%% write_term(+Term, +Options). +% +% Write Term to the current output stream according to some output syntax options. +% Options are specified in detail in `write_term/3`. write_term(Term, Options) :- current_output(Stream), write_term(Stream, Term, Options). +%% write_term(+Stream, +Term, +Options). +% +% Write Term to the stream Stream according to some output syntax options. The options avaibale are: +% +% * `ignore_ops(+Boolean)` if `true`, the generic term representation is used everywhere. In `false` +% (default), operators do not use that generic term representation. +% * `max_depth(+N)` if the term is nested deeper than N, print the reminder as ellipses. +% If N = 0 (default), there's no limit. +% * `numbervars(+Boolean)` if true, replaces `$VAR(N)` variables with letters, in order. Default is false. +% * `quoted(+Boolean)` if true, strings and atoms that need quotes to be valid Prolog syntax, are quoted. Default is false. +% * `variable_names(+List)` assign names to variables in term. List should be a list of terms of format `Name=Var`. +% * `double_quotes(+Boolean)` if true, strings are printed in double quotes rather than with list notation. Default is false. write_term(Stream, Term, Options) :- - parse_write_options(Options, [IgnoreOps, MaxDepth, NumberVars, Quoted, VNNames], write_term/3), - '$write_term'(Stream, Term, IgnoreOps, NumberVars, Quoted, VNNames, MaxDepth). + parse_write_options(Options, [DoubleQuotes, IgnoreOps, MaxDepth, NumberVars, Quoted, VNNames], write_term/3), + '$write_term'(Stream, Term, IgnoreOps, NumberVars, Quoted, VNNames, MaxDepth, DoubleQuotes). +%% write(+Term). +% +% Write Term to the current output stream using a syntax similar to Prolog write(Term) :- current_output(Stream), - '$write_term'(Stream, Term, false, true, false, [], 0). + '$write_term'(Stream, Term, false, true, false, [], 0, false). +%% write(+Stream, +Term). +% +% Write Term to the stream Stream using a syntax similar to Prolog write(Stream, Term) :- - '$write_term'(Stream, Term, false, true, false, [], 0). + '$write_term'(Stream, Term, false, true, false, [], 0, false). +%% write_canonical(+Term). +% +% Write Term to the current output stream using canonical Prolog syntax. Can be read back as Prolog terms. write_canonical(Term) :- current_output(Stream), - '$write_term'(Stream, Term, true, false, true, [], 0). + '$write_term'(Stream, Term, true, false, true, [], 0, false). +%% write_canonical(+Stream, +Term). +% +% Write Term to the stream Stream using canonical Prolog syntax. Can be read back as Prolog terms. write_canonical(Stream, Term) :- - '$write_term'(Stream, Term, true, false, true, [], 0). + '$write_term'(Stream, Term, true, false, true, [], 0, false). +%% writeq(+Term). +% +% Write Term to the current output stream using a syntax similar to `write/1` but quoting the atoms that need to be +% quoted according to Prolog syntax. writeq(Term) :- current_output(Stream), - '$write_term'(Stream, Term, false, true, true, [], 0). + '$write_term'(Stream, Term, false, true, true, [], 0, false). +%% writeq(+Stream, +Term). +% +% Write Term to the stream Stream using a syntax similar to `write/1` but quoting the atoms that need to be +% quoted according to Prolog syntax. writeq(Stream, Term) :- - '$write_term'(Stream, Term, false, true, true, [], 0). + '$write_term'(Stream, Term, false, true, true, [], 0, false). select_rightmost_options([Option-Value | OptionPairs], OptionValues) :- ( pairs:same_key(Option, OptionPairs, OtherValues, _), @@ -523,25 +681,62 @@ parse_read_term_options(Options, OptionValues, Stub) :- parse_options_list(Options, builtins:parse_read_term_options_, DefaultOptions, OptionValues, Stub). -parse_read_term_options_(singletons(Vars), singletons-Vars) :- !. -parse_read_term_options_(variables(Vars), variables-Vars) :- !. -parse_read_term_options_(variable_names(Vars), variable_names-Vars) :- !. +parse_read_term_options_(singletons(Vars), singletons-Vars) :- + ( ( var(Vars) + ; '$skip_max_list'(_, _, Vars, Rs), + Rs == [] + ) -> + ! + ; throw(error(domain_error(read_option, singletons(Vars)), read_term/2)) + ). +parse_read_term_options_(variables(Vars), variables-Vars) :- + ( ( var(Vars) + ; '$skip_max_list'(_, _, Vars, Rs), + Rs == [] + ) -> + ! + ; throw(error(domain_error(read_option, variables(Vars)), read_term/2)) + ). +parse_read_term_options_(variable_names(Vars), variable_names-Vars) :- + ( ( var(Vars) + ; '$skip_max_list'(_, _, Vars, Rs), + Rs == [] + ) -> + ! + ; throw(error(domain_error(read_option, variable_names(Vars)), read_term/2)) + ). parse_read_term_options_(E,_) :- throw(error(domain_error(read_option, E), _)). - +%% read_term(+Stream, -Term, +Options). +% +% Read Term from the stream Stream. It supports several options: +% * `variables(-Vars)` unifies Vars with a list of variables in the term. Similar to do `term_variables/2` with the new term. +% * `variable_names(-Vars)` unifies Vars with a list `Name=Var` with Name describing the variable name and Var the variable itself that appears in Term. +% * `singletons` similar to `variable_names` but only reports variables occurring only once in Term. read_term(Stream, Term, Options) :- parse_read_term_options(Options, [Singletons, VariableNames, Variables], read_term/3), '$read_term'(Stream, Term, Singletons, Variables, VariableNames). +%% read_term(-Term, +Options). +% +% Read Term from the current input stream. It supports several options described in more detail in `read_term/3`. read_term(Term, Options) :- current_input(Stream), read_term(Stream, Term, Options). +%% read(-Term). +% +% Read Term from the current input stream with default options. **NOTE** This is not a general predicate +% to read input from a file or the user. Use other predicates like `phrase_from_file/2` for that. read(Term) :- current_input(Stream), - read(Stream, Term). + read_term(Stream, Term, []). + % read(Stream, Term). + +read(Stream, Term) :- + read_term(Stream, Term, []). % ensures List is either a variable or a list. can_be_list(List, _) :- @@ -559,6 +754,13 @@ can_be_list(List, PI) :- % term_variables. +%% term_variables(+Term, -Vars). +% +% True iff given a Term, Vars is a list of all the unique variables that appear in Term. The variables are sorted depth-first +% and left-to-right. +% +% ?- term_variables(f(X, Y, X, g(Z)), Vars). +% Vars = [X, Y, Z]. term_variables(Term, Vars) :- can_be_list(Vars, term_variables/2), '$term_variables'(Term, Vars). @@ -567,6 +769,15 @@ term_variables(Term, Vars) :- :- non_counted_backtracking catch/3. +%% catch(Goal, Catcher, Recover). +% +% Calls Goal, but if it throws an exception that unifies with Catcher, Recover will be called instead +% and the program will be resumed. Example: +% +% ``` +% ?- catch(number_chars(X, "not_a_number"), error(syntax_error(_), _), X = 0). +% X = 0. +% ``` catch(G,C,R) :- '$get_current_block'(Bb), catch(G,C,R,Bb). @@ -591,7 +802,7 @@ catch(G,C,R,Bb) :- end_block(Bb, NBb) :- '$clean_up_block'(NBb), '$reset_block'(Bb). -end_block(Bb, NBb) :- +end_block(_Bb, NBb) :- '$reset_block'(NBb), '$fail'. @@ -607,6 +818,17 @@ handle_ball(_, _, _) :- :- non_counted_backtracking throw/1. +%% throw(+Exception). +% +% Raise the exception Exception. The system looks for the innermost `catch/3` for which Exception +% unifies with Catcher. Example: +% +% ``` +% ?- throw(custom_error(42)). +% throw(custom_error(42)). +% ?- catch(throw(custom_error(42)), custom_error(_), true). +% true. +% ``` throw(Ball) :- ( var(Ball) -> '$set_ball'(error(instantiation_error,throw/1)) @@ -638,6 +860,20 @@ findall_cleanup(LhLength, Error) :- :- non_counted_backtracking findall/3. +%% findall(Template, Goal, Solutions). +% +% Unify Solutions with a list of all values that variables in Template can take in Goal. +% `findall/3` is equivalent to `bagof/3` with all free variables scoped to the Goal (`^` operator) +% except that `bagof/3` fails when no solutions are found and `findall/3` unifies with an empty list. +% Example: +% +% ``` +% f(1,2). +% f(1,3). +% f(1,4). +% ?- findall(X-Y, f(X, Y), Solutions). +% Solutions = [1-2,1-3,1-4]. +% ``` findall(Template, Goal, Solutions) :- error:can_be(list, Solutions), '$lh_length'(LhLength), @@ -661,6 +897,9 @@ findall(Template, Goal, Solutions) :- :- non_counted_backtracking findall/4. +%% findall(Template, Goal, Solutions0, Solutions1) +% +% Similar to `findall/3` but returns the solutions as the difference list Solutions0-Solutions1. findall(Template, Goal, Solutions0, Solutions1) :- error:can_be(list, Solutions0), error:can_be(list, Solutions1), @@ -682,12 +921,45 @@ set_difference([X|Xs], [Y|Ys], Zs) :- set_difference([], _, []) :- !. set_difference(Xs, [], Xs). + +% variant/2 checks whether X is a variant of Y per the definition in +% 7.1.6.1 of the ISO standard. + +:- non_counted_backtracking variant/4. + +variant(X,Y,VPs,VPs0) :- + ( var(X) -> + var(Y), + VPs = [X-Y|VPs0] + ; var(Y) -> + false + ; X =.. [FX | XArgs], + Y =.. [FX | YArgs], + lists:foldl('$call'(builtins:variant), XArgs, YArgs, VPs, VPs0) + ). + +:- non_counted_backtracking variant/2. + +singleton([_]). + +variant(X, Y) :- + variant(X,Y, VPs, []), + keysort(VPs, SVPs), + pairs:group_pairs_by_key(SVPs, SVPKs), + pairs:pairs_values(SVPKs, Vals), + lists:maplist('$call'(builtins:term_variables), Vals, Vs), + lists:maplist('$call'(builtins:singleton), Vs), + term_variables(Vs, YVars), + lists:length(SVPKs, N), + lists:length(YVars, N). + + :- non_counted_backtracking group_by_variant/4. group_by_variant([V2-S2 | Pairs], V1-S1, [S2 | Solutions], Pairs0) :- - V1 = V2, % \+ \+ (V1 = V2), % (2) % iso_ext:variant(V1, V2), % (1) + variant(V1, V2), !, - % V1 = V2, % (3) + V1 = V2, group_by_variant(Pairs, V2-S2, Solutions, Pairs0). group_by_variant(Pairs, _, [], Pairs). @@ -743,13 +1015,31 @@ findall_with_existential(Template, Goal, PairedSolutions, Witnesses0, Witnesses) :- non_counted_backtracking bagof/3. +%% bagof(Template, Goal, Solution). +% +% Unify Solution with a list of alternatives of the variables in Template coming from calling Goal. +% If Goal has no solutions, the predicate fails. +% If free variables that are not in Template appear in Goal, the predicate will backtrack over +% the alternatives of those free variables. However, you can use the syntax `Var^Goal` to not bind +% Var in Goal and prevent that. +% +% Example: +% +% ``` +% f(1, 3). +% f(2, 4). +% ?- bagof(X, f(X, Y), Bag). +% Y = 3, Bag = [1], +% ; Y = 4, Bag = [2]. +% ?- bagof(X, Y^f(X, Y), Bag). +% Bag = [1,2]. +% ``` bagof(Template, Goal, Solution) :- error:can_be(list, Solution), - term_variables(Template, TemplateVars0), - term_variables(Goal, GoalVars0), - sort(TemplateVars0, TemplateVars), - sort(GoalVars0, GoalVars), - set_difference(GoalVars, TemplateVars, Witnesses0), + term_variables(Template, TemplateVars), + term_variables(Goal, GoalVars), + term_variables(TemplateVars+GoalVars, TGVs), + lists:append(TemplateVars, Witnesses0, TGVs), findall_with_existential(Template, Goal, PairedSolutions0, Witnesses0, Witnesses), keysort(PairedSolutions0, PairedSolutions), group_by_variants(PairedSolutions, GroupedSolutions), @@ -771,15 +1061,25 @@ iterate_variants_and_sort([_|GroupSolutions], Ws, Solution) :- :- non_counted_backtracking setof/3. +%% setof(Template, Goal, Solution). +% +% Similar to `bagof/3` but Solution is sorted and duplicates are removed. Example: +% +% ``` +% f(1, 2). +% f(1, 3). +% f(2, 4). +% ?- setof(X, Y^f(X, Y), Set). +% Set = [1, 2]. +% ``` setof(Template, Goal, Solution) :- error:can_be(list, Solution), - term_variables(Template, TemplateVars0), - term_variables(Goal, GoalVars0), - sort(TemplateVars0, TemplateVars), - sort(GoalVars0, GoalVars), - set_difference(GoalVars, TemplateVars, Witnesses0), + term_variables(Template, TemplateVars), + term_variables(Goal, GoalVars), + term_variables(TemplateVars+GoalVars, TGVs), + lists:append(TemplateVars, Witnesses0, TGVs), findall_with_existential(Template, Goal, PairedSolutions0, Witnesses0, Witnesses), - keysort(PairedSolutions0, PairedSolutions), + '$keysort_with_constant_var_ordering'(PairedSolutions0, PairedSolutions), % see 7.2.1 group_by_variants(PairedSolutions, GroupedSolutions), iterate_variants_and_sort(GroupedSolutions, Witnesses, Solution). @@ -810,6 +1110,9 @@ setof(Template, Goal, Solution) :- ; throw(error(type_error(callable, H), clause/2)) ). +%% clause(Head, Body). +% +% True iff Head can be unified with a clause head and Body with its corresponding clause body. clause(H, B) :- ( var(H) -> throw(error(instantiation_error, clause/2)) @@ -833,6 +1136,10 @@ clause(H, B) :- :- meta_predicate asserta(:). +%% asserta(Clause). +% +% Asserts (inserts) a new clause (rule or fact) into the current module. +% The clause will be inserted at the beginning of the module. asserta(Clause0) :- loader:strip_subst_module(Clause0, user, Module, Clause), iso_ext:asserta(Module, Clause). @@ -840,6 +1147,10 @@ asserta(Clause0) :- :- meta_predicate assertz(:). +%% assertz(Clause). +% +% Asserts (inserts) a new clause (rule or fact) into the current module. +% The clase will be inserted at the end of the module. assertz(Clause0) :- loader:strip_subst_module(Clause0, user, Module, Clause), iso_ext:assertz(Module, Clause). @@ -847,6 +1158,10 @@ assertz(Clause0) :- :- meta_predicate retract(:). +%% retract(Clause) +% +% Retracts (deletes) a clause present in the current module. +% It only affects dynamic predicates. retract(Clause0) :- loader:strip_module(Clause0, Module, Clause), ( Clause \= (_ :- _) -> @@ -860,70 +1175,30 @@ retract(Clause0) :- retract_module_clause(Head, Body, Module) ). -module_retract_clauses([Clause|Clauses0], Head, Body, Name, Arity, Module) :- - functor(VarHead, Name, Arity), - findall((VarHead :- VarBody), Module:'$clause'(VarHead, VarBody), Clauses1), - ( first_match_index(Clauses1, (Head :- Body), 0, N) -> +retract_clauses([L-P | Ps], Head, Body, Name, Arity, Module) :- + '$invoke_clause_at_p'(Head, Body, L, P, N, Module), + ( integer(N) -> '$retract_clause'(Name, Arity, N, Module) - ; Clause = (Head :- Body) + ; true % the clause at index N has already been retracted in this + % case but unify (Head :- Body) anyway. ), - ( Clauses0 == [] -> ! + ( Ps == [] -> ! ; true ). +retract_clauses([_ | Ps], Head, Body, Name, Arity, Module) :- + retract_clauses(Ps, Head, Body, Name, Arity, Module). - -module_retract_clauses([_|Clauses0], Head, Body, Name, Arity, Module) :- - module_retract_clauses(Clauses0, Head, Body, Name, Arity, Module). - - -call_module_retract(Head, Body, Name, Arity, Module) :- - findall((Head :- Body), Module:'$clause'(Head, Body), Clauses), - module_retract_clauses(Clauses, Head, Body, Name, Arity, Module). - - -retract_module_clause(Head, Body, Module) :- - ( var(Head) -> - throw(error(instantiation_error, retract/1)) - ; callable(Head), - functor(Head, Name, Arity) -> - ( '$no_such_predicate'(Module, Head) -> - '$fail' - ; '$head_is_dynamic'(Module, Head) -> - ( Module == user -> - call_retract(Head, Body, Name, Arity) - ; call_module_retract(Head, Body, Name, Arity, Module) - ) - ; throw(error(permission_error(modify, static_procedure, Name/Arity), retract/1)) - ) - ; throw(error(type_error(callable, Head), retract/1)) - ). - - -first_match_index([Clause | _], Clause, N, N) :- - !. -first_match_index([_ | Clauses], Clause, N0, N) :- - N1 is N0 + 1, - first_match_index(Clauses, Clause, N1, N). - - -retract_clauses([Clause | Clauses0], Head, Body, Name, Arity) :- - functor(VarHead, Name, Arity), - findall((VarHead :- VarBody), builtins:'$clause'(VarHead, VarBody), Clauses1), - ( first_match_index(Clauses1, (Head :- Body), 0, N) -> - '$retract_clause'(Name, Arity, N, user) - ; Clause = (Head :- Body) +call_retract_helper(Head, Body, P, Module) :- + ( Module == user -> + ClauseQualifier = builtins + ; ClauseQualifier = Module ), - ( Clauses0 == [] -> ! - ; true - ). -retract_clauses([_ | Clauses0], Head, Body, Name, Arity) :- - retract_clauses(Clauses0, Head, Body, Name, Arity). - - -call_retract(Head, Body, Name, Arity) :- - findall((Head :- Body), builtins:'$clause'(Head, Body), Clauses), - retract_clauses(Clauses, Head, Body, Name, Arity). + ClauseQualifier:'$clause'(Head, Body), + '$get_clause_p'(Head, P, Module). +call_retract(Head, Body, Name, Arity, Module) :- + findall(P, builtins:call_retract_helper(Head, Body, P, Module), Ps), + retract_clauses(Ps, Head, Body, Name, Arity, Module). retract_clause(Head, Body) :- ( var(Head) -> @@ -938,15 +1213,32 @@ retract_clause(Head, Body) :- ; '$no_such_predicate'(user, Head) -> '$fail' ; '$head_is_dynamic'(user, Head) -> - call_retract(Head, Body, Name, Arity) + call_retract(Head, Body, Name, Arity, user) ; throw(error(permission_error(modify, static_procedure, Name/Arity), retract/1)) ) ; throw(error(type_error(callable, Head), retract/1)) ). +retract_module_clause(Head, Body, Module) :- + ( var(Head) -> + throw(error(instantiation_error, retract/1)) + ; callable(Head), + functor(Head, Name, Arity) -> + ( '$no_such_predicate'(Module, Head) -> + '$fail' + ; '$head_is_dynamic'(Module, Head) -> + call_retract(Head, Body, Name, Arity, Module) + ; throw(error(permission_error(modify, static_procedure, Name/Arity), retract/1)) + ) + ; throw(error(type_error(callable, Head), retract/1)) + ). :- meta_predicate retractall(:). +%% retractall(Head) +% +% Retracts (deletes) all clauses that unify which head unifies with Head +% It only affects dynamic predicates. retractall(Head) :- retract_clause(Head, _), false. @@ -981,9 +1273,13 @@ module_abolish(Pred, Module) :- ; throw(error(type_error(predicate_indicator, Module:Pred), abolish/1)) ). - :- meta_predicate abolish(:). +%% abolish(Pred). +% +% Pred should satisfy: `Pred = Name/Arity`. +% Deletes all clauses of a predicate with name Name and arity Arity. +% It only affects dynamic predicates abolish(Pred) :- ( var(Pred) -> throw(error(instantiation_error, abolish/1)) @@ -1017,37 +1313,40 @@ abolish(Pred) :- ; throw(error(type_error(predicate_indicator, Pred), abolish/1)) ). - -'$iterate_db_refs'(Name, Arity, Name/Arity). % :- -% '$lookup_db_ref'(Ref, Name, Arity). -'$iterate_db_refs'(RName, RArity, Name/Arity) :- - '$get_next_db_ref'(RName, RArity, RRName, RRArity), - '$iterate_db_refs'(RRName, RRArity, Name/Arity). - - +%% current_predicate(Pred). +% +% Pred must satisfy: `Pred = Name/Arity`. +% True iff there's a predicate Pred that is currently loaded at the moment. +% It can be used to check for existence of a predicate or to enumerate all loaded predicates current_predicate(Pred) :- ( var(Pred) -> - '$get_next_db_ref'(RN, RA, _, _), - '$iterate_db_refs'(RN, RA, Pred) - ; Pred \= _/_ -> - throw(error(type_error(predicate_indicator, Pred), current_predicate/1)) - ; Pred = Name/Arity, - ( nonvar(Name), \+ atom(Name) - ; nonvar(Arity), \+ integer(Arity) - ; integer(Arity), Arity < 0 - ) -> - throw(error(type_error(predicate_indicator, Pred), current_predicate/1)) - ; '$get_next_db_ref'(RN, RA, _, _), - '$iterate_db_refs'(RN, RA, Pred) + '$get_db_refs'(_, _, _, PIs), + lists:member(Pred, PIs) + ; '$strip_module'(Pred, Module, UnqualifiedPred), + ( var(Module), + \+ functor(Pred, (:), 2) + ; atom(Module) + ), + UnqualifiedPred = Name/Arity -> + ( ( nonvar(Name), \+ atom(Name) + ; nonvar(Arity), \+ integer(Arity) + ; integer(Arity), Arity < 0 + ) -> + throw(error(type_error(predicate_indicator, Pred), current_predicate/1)) + ; nonvar(Name), + nonvar(Arity) -> + '$lookup_db_ref'(Module, Name, Arity) + ; '$get_db_refs'(Module, Name, Arity, PIs), + lists:member(UnqualifiedPred, PIs) + ) + ; throw(error(type_error(predicate_indicator, Pred), current_predicate/1)) ). - '$iterate_op_db_refs'(RPriority, RSpec, ROp, _, RPriority, RSpec, ROp). '$iterate_op_db_refs'(RPriority, RSpec, ROp, OssifiedOpDir, Priority, Spec, Op) :- '$get_next_op_db_ref'(RPriority, RSpec, ROp, OssifiedOpDir, RRPriority, RRSpec, RROp), '$iterate_op_db_refs'(RRPriority, RRSpec, RROp, OssifiedOpDir, Priority, Spec, Op). - can_be_op_priority(Priority) :- var(Priority). can_be_op_priority(Priority) :- op_priority(Priority). @@ -1055,6 +1354,10 @@ can_be_op_specifier(Spec) :- var(Spec). can_be_op_specifier(Spec) :- op_specifier(Spec). +%% current_op(Priority, Spec, Op) +% +% True iff there's an operator defined with name Op, with spec Spec and priority Priority. +% Can be used to find all operators currently defined. current_op(Priority, Spec, Op) :- ( can_be_op_priority(Priority), can_be_op_specifier(Spec), @@ -1108,6 +1411,12 @@ op_(Priority, OpSpec, Op) :- '$op'(Priority, OpSpec, Op). +%% op(Priority, Spec, Op) +% +% Declares an operated named Op, with priority Priority and a spec Spec. +% The priority is an integer between 0 (null) and 1200. +% Spec can be: `xf`, `yf`, `xfx`, `xfy`, `yfx`, `fy` and `fx` where f indicates the position of the +% operator and x and y the arguments. op(Priority, OpSpec, Op) :- ( var(Priority) -> throw(error(instantiation_error, op/3)) % 8.14.3.3 a) @@ -1129,9 +1438,15 @@ op(Priority, OpSpec, Op) :- ! ; throw(error(type_error(list, Op), op/3)) % 8.14.3.3 f) ). - +%% halt. +% +% Exits the Prolog system with exit code 0 halt :- halt(0). + +%% halt(+ExitCode) +% +% Exits the Prolog system with exit code N halt(N) :- ( var(N) -> throw(error(instantiation_error, halt/1)) % 8.17.4.3 a) @@ -1142,7 +1457,14 @@ halt(N) :- ; throw(error(domain_error(exit_code, N), halt/1)) ). - +%% atom_length(+Atom, -Length). +% +% True iff Atom is an atom of Length characters. Example: +% +% ``` +% ?- atom_length(marseille, N). +% N = 9. +% ``` atom_length(Atom, Length) :- ( var(Atom) -> throw(error(instantiation_error, atom_length/2)) % 8.16.1.3 a) @@ -1159,7 +1481,17 @@ atom_length(Atom, Length) :- ; throw(error(type_error(atom, Atom), atom_length/2)) % 8.16.1.3 b) ). - +%% atom_chars(?Atom, ?Chars). +% +% Relates an atom with a string in chars representation. It can be used to convert +% between atoms and strings. Examples: +% +% ``` +% ?- atom_chars(marseille, X). +% X = "marseille". +% ?- atom_chars(X, "marseille"). +% X = marseille. +% ``` atom_chars(Atom, List) :- '$skip_max_list'(_, _, List, Tail), ( ( Tail == [] ; var(Tail) ) -> @@ -1180,6 +1512,18 @@ atom_chars(Atom, List) :- ; throw(error(type_error(atom, Atom), atom_chars/2)) ). +%% atom_codes(?Atom, ?Codes). +% +% Relates an atom with a string in codes representation. It can be used to convert +% between atoms and strings. However, codes is not the default representation of double quoutes +% strings in Scryer Prolog. Examples: +% +% ``` +% ?- atom_codes(marseille, X). +% X = [109,97,114,115,101,105,108,108,101]. +% ?- atom_codes(X, [109,97,114,115,101,105,108,108,101]). +% X = marseille. +% ``` atom_codes(Atom, List) :- '$skip_max_list'(_, _, List, Tail), ( ( Tail == [] ; var(Tail) ) -> @@ -1200,7 +1544,16 @@ atom_codes(Atom, List) :- ; throw(error(type_error(atom, Atom), atom_codes/2)) ). - +%% atom_concat(?A1, ?A2, ?A12) +% +% Similar to `append/3` but operating on atom characters. +% If you find yourself using this predicate, consider using strings instead. +% Example: +% +% ``` +% ?- atom_concat(a, X, ab). +% X = b. +% ``` atom_concat(Atom_1, Atom_2, Atom_12) :- error:can_be(atom, Atom_1), error:can_be(atom, Atom_2), @@ -1209,9 +1562,14 @@ atom_concat(Atom_1, Atom_2, Atom_12) :- ( var(Atom_12) -> throw(error(instantiation_error, atom_concat/3)) ; atom_chars(Atom_12, Atom_12_Chars), - lists:append(BeforeChars, AfterChars, Atom_12_Chars), - atom_chars(Atom_1, BeforeChars), - atom_chars(Atom_2, AfterChars) + ( var(Atom_2) -> + lists:append(BeforeChars, AfterChars, Atom_12_Chars), + atom_chars(Atom_2, AfterChars) + ; atom_chars(Atom_2, AfterChars), + lists:append(BeforeChars, AfterChars, Atom_12_Chars), + ! + ), + atom_chars(Atom_1, BeforeChars) ) ; var(Atom_2) -> ( var(Atom_12) -> throw(error(instantiation_error, atom_concat/3)) @@ -1226,7 +1584,21 @@ atom_concat(Atom_1, Atom_2, Atom_12) :- atom_chars(Atom_12, Atom_12_Chars) ). - +%% sub_atom(+Atom, ?Before, ?Length, ?After, ?SubAtom). +% +% Relates an atom to a subatom inside with some key properties: +% +% * SubAtom starts at Before characters (0-based) from Atom +% * SubAtom has Length characters +% * After SubAtom there are After characters in Atom +% +% If you find yourself using this predicate, consider using strings. +% Example: +% +% ``` +% ?- sub_atom(abcdefg, 2, 3, X, SubAtom). +% X = 2, SubAtom = cde. +% ``` sub_atom(Atom, Before, Length, After, Sub_atom) :- error:must_be(atom, Atom), error:can_be(atom, Sub_atom), @@ -1248,7 +1620,14 @@ sub_atom(Atom, Before, Length, After, Sub_atom) :- atom_chars(Sub_atom, LengthChars) ). - +%% char_code(?Char, ?Code) +% +% Relates a Char to its Code (an integer). Example: +% +% ``` +% ?- char_code(a, X). +% X = 97. +% ``` char_code(Char, Code) :- ( var(Char) -> ( var(Code) -> @@ -1269,11 +1648,19 @@ char_code(Char, Code) :- ; throw(error(type_error(character, Char), char_code/2)) ). +%% get_char(-Char). +% +% From the current input stream, unify Char with the next character. +% When there are no more characters to read, Char unifies with `end_of_file`. get_char(C) :- error:can_be(in_character, C), current_input(S), '$get_char'(S, C). +%% get_char(+Stream, -Char). +% +% From the stream Stream, unify Char with the next character. +% When there are no more characters to read, Char unifies with `end_of_file`. get_char(S, C) :- error:can_be(in_character, C), '$get_char'(S, C). @@ -1330,7 +1717,20 @@ codes_or_vars([C|Cs], PI) :- ; codes_or_vars(Cs, PI) ). - +%% number_chars(?N, ?Chars). +% +% Relates a number and its representation as list of chars (string). +% Throws an error if Chars is not the representation of a number. +% Examples: +% +% ``` +% ?- number_chars(42, X). +% X = "42". +% ?- number_chars(X, "42"). +% X = 42. +% ?- number_chars(X, "not_a_number"). +% error(syntax_error(cannot_parse_big_int),number_chars/2:0). +% ``` number_chars(N, Chs) :- ( ground(Chs) -> can_be_number(N, number_chars/2), @@ -1352,7 +1752,20 @@ list_of_ints(Ns) :- error:must_be(list, Ns), lists:maplist(error:must_be(integer), Ns). - +%% number_codes(?N, ?Codes). +% +% Relates a number and its representation as list of codes. +% Throws an error if Codes is not the representation of a number. +% Examples: +% +% ``` +% ?- number_codes(42, X). +% X = [52,50]. +% ?- number_codes(X, [52,50]). +% X = 42. +% ?- number_codes(X, [65]). +% error(syntax_error(cannot_parse_big_int),number_codes/2:0). +% ``` number_codes(N, Chs) :- ( ground(Chs) -> can_be_number(N, number_codes/2), @@ -1369,7 +1782,18 @@ number_codes(N, Chs) :- '$number_to_codes'(N, Chs) ). - +%% subsumes_term(General, Specific) +% +% True iff General can be made equivalent to Specific by only binding variables +% in Generic. The implementation unifies with occurs check always and ensures that +% the variables of Specific did not change. Some examples: +% +% ``` +% ?- subsumes_term(f(A, A), f(2, 2)). +% true. +% ?- subsumes_term(f(A, 2), f(2, A)). +% false. +% ``` subsumes_term(General, Specific) :- \+ \+ ( term_variables(Specific, SVs1), @@ -1378,21 +1802,42 @@ subsumes_term(General, Specific) :- SVs1 == SVs2 ). - +%% unify_with_occurs_check(?X, ?Y). +% +% True iff X and Y unify with occurs check. The occurs check prevents the creation cyclic terms but is +% computationally more expensive. The (=)/2 operator can also do occurs check if enabled +% via `set_prolog_flag/2`. Example: +% +% ``` +% ?- A = f(A). +% A = f(A). +% ?- unify_with_occurs_check(A, f(A)). +% false. +% ``` unify_with_occurs_check(X, Y) :- '$unify_with_occurs_check'(X, Y). - +%% current_input(-Stream). +% +% Unifies with the current input stream. current_input(S) :- '$current_input'(S). +%% current_output(-Stream). +% +% Unifies with the current output stream. current_output(S) :- '$current_output'(S). - +%% set_input(+Stream). +% +% Sets the current input stream to Stream. set_input(S) :- ( var(S) -> throw(error(instantiation_error, set_input/1)) ; '$set_input'(S) ). +%% set_output(Stream). +% +% Sets the current output stream to Stream. set_output(S) :- ( var(S) -> throw(error(instantiation_error, set_output/1)) @@ -1434,11 +1879,35 @@ parse_stream_options_(eof_action(Action), eof_action-Action) :- parse_stream_options_(E, _) :- throw(error(domain_error(stream_option, E), _)). % 8.11.5.3i) - +%% open(+File, +Mode, +Stream). +% +% Equivalent to `open(File, Mode, Stream, [])`. open(SourceSink, Mode, Stream) :- open(SourceSink, Mode, Stream, []). - +%% open(+File, +Mode, -Stream, +StreamOptions). +% +% Opens a file named File with a Mode and StreamOptions, and returns a Stream +% that can be used by other predicates to read and write (depending on Mode). +% +% Mode can be: `read`, `write` or `append`. `read` creates a Stream +% that is read-only, `write` is write-only and `append` +% is write-only but at the end of the file. +% +% The following options are available: +% +% * `alias(+Alias)`: Set an alias to the stream +% * `eof_action(+Action)`: Defined what happens if the end of the stream is reached. Values: `error`, `eof_code` and `reset`. +% * `reposition(+Boolean)`: Specifies whether repositioning is required for the stream. `false` is the default. +% * `type(+Type)`: Type can be `text` or `binary`. Defines the type of the stream, if it's optimized for plain text +% or just binary +% +% Example: +% +% ``` +% ?- open("README.md", read, S, []), get_n_chars(S, 20, C). +% S = '$stream'(0x55dece980218), C = "\n# Scryer Prolog\n\nS ..." +% ``` open(SourceSink, Mode, Stream, StreamOptions) :- ( var(SourceSink) -> throw(error(instantiation_error, open/4)) % 8.11.5.3a) @@ -1476,84 +1945,145 @@ parse_close_options_(force(Force), force-Force) :- parse_close_options_(E, _) :- throw(error(domain_error(close_option, E), _)). - +%% close(+Stream, +CloseOptions). +% +% Closes a stream. It takes a CloseOptions list. The only option available is `force` which takes a `true` +% or `false`. close(Stream, CloseOptions) :- parse_close_options(CloseOptions, [Force], close/2), '$close'(Stream, CloseOptions). +%% close(+Stream). +% +% Closes a stream. Equivalent to `close(Stream, []).`. close(Stream) :- '$close'(Stream, []). - +%% flush_output(+Stream). +% +% Flushes the output of the stream Stream flush_output(S) :- '$flush_output'(S). +%% flush_output. +% +% Flushes the output of the current output stream flush_output :- current_output(S), '$flush_output'(S). - +%% get_byte(+Stream, -Byte). +% +% From the stream Stream, unify Byte with the next byte (an integer between 0 and 255) +% When there are no more bytes to read, Byte unifies with -1. get_byte(S, B) :- '$get_byte'(S, B). +%% get_byte(-Byte). +% +% From the current input stream, unify Byte with the next byte (an integer between 0 and 255) +% When there are no more bytes to read, Byte unifies with -1. get_byte(B) :- current_input(S), '$get_byte'(S, B). - +%% put_char(+Char). +% +% Writes to the current output stream the character Char. put_char(C) :- current_output(S), '$put_char'(S, C). +%% put_char(+Stream, +Char). +% +% Writes to the stream Stream the character Char. put_char(S, C) :- '$put_char'(S, C). - +%% put_byte(+Byte). +% +% Writes to the current output stream the byte Byte (should be an integer between 0 and 255). put_byte(C) :- current_output(S), '$put_byte'(S, C). +%% put_byte(+Stream, +Byte). +% +% Writes to the stream Stream the byte Byte (should be an integer between 0 and 255). put_byte(S, C) :- '$put_byte'(S, C). - +%% put_code(+Code). +% +% Writes to the current output stream the character represented by code Code put_code(C) :- current_output(S), '$put_code'(S, C). +%% put_code(+Stream, +Code). +% +% Writes to the stream Stream the character represented by code Code put_code(S, C) :- '$put_code'(S, C). - +%% get_code(-Code). +% +% From the current input stream, unify Code with the character code of the next character. +% When there are no more characters to read, Code unifies with -1. get_code(C) :- current_input(S), '$get_code'(S, C). +%% get_code(+Stream, -Code). +% +% From the stream Stream, unify Code with the character code of the next character. +% When there are no more characters to read, Code unifies with -1. get_code(S, C) :- '$get_code'(S, C). - +%% peek_byte(+Stream, -Byte). +% +% From the stream Stream, unify Byte with the next byte. However, it doesn't move the stream +% position, allowing it to be read again. peek_byte(S, B) :- '$peek_byte'(S, B). +%% peek_byte(-Byte). +% +% From the current input stream, unify Byte with the next byte. However, it doesn't move the stream +% position, allowing it to be read again. peek_byte(B) :- current_input(S), '$peek_byte'(S, B). - +%% peek_code(-Code). +% +% From the current input stream, unify Code with the character code of the next character. +% However, it doesn't move the stream position, allowing it to be read again. peek_code(C) :- current_input(S), '$peek_code'(S, C). +%% peek_code(+Stream, -Code). +% +% From the stream Stream, unify Code with the character code of the next character. +% However, it doesn't move the stream position, allowing it to be read again. peek_code(S, C) :- '$peek_code'(S, C). - +%% peek_char(-Char). +% +% From the current input stream, unify Char with the next character. +% However, it doesn't move the stream position, allowing it to be read again. peek_char(C) :- current_input(S), '$peek_char'(S, C). +%% peek_char(+Stream, -Char). +% +% From the stream Stream, unify Char with the next character. +% However, it doesn't move the stream position, allowing it to be read again. peek_char(S, C) :- '$peek_char'(S, C). @@ -1595,7 +2125,22 @@ stream_iter(S) :- stream_iter_(S0, S) ). - +%% stream_property(Stream, StreamProperty). +% +% For stream Stream, StreamProperty is a property that applies to that stream. +% StreamProperty can be one of the following: +% +% * `input` if stream is an input stream. +% * `output` if stream is an output stream. +% * `input_output` if stream is both an input and an output stream. +% * `alias(-Alias)` if the stream has an associated alias. +% * `file_name(-FileName)` if Stream is associated to a file, unifies with the name of the file +% * `mode(-Mode)`: Mode unifies with the mode of the stream: `read`, `write` or `append`. +% * `position(position_and_lines_read(P, L))` current position of the stream. +% * `end_of_stream(-X)` where X can be `not`, `at` or `past` depending if the stream has ended or not. +% * `eof_action(-X)` where X can be `error`, `eof_code` or `reset` depending on the action that will happen on the end of the file. +% * `reposition(-Boolean)` specifies if reposition has been enabled for this stream. +% * `type(-Type)` where Type can be `text` or `binary`. stream_property(S, P) :- ( nonvar(P), \+ check_stream_property(P, _, _) -> throw(error(domain_error(stream_property, P), stream_property/2)) @@ -1604,7 +2149,9 @@ stream_property(S, P) :- '$stream_property'(S, PropertyName, PropertyValue) ). - +%% at_end_of_stream(+Stream). +% +% True iff the stream Stream has ended at_end_of_stream(S_or_a) :- ( var(S_or_a) -> throw(error(instantiation_error, at_end_of_stream/1)) @@ -1615,13 +2162,18 @@ at_end_of_stream(S_or_a) :- stream_property(S, end_of_stream(E)), ( E = at -> true ; E = past ). +%% at_end_of_stream. +% +% True iff the current input stream has ended at_end_of_stream :- current_input(S), stream_property(S, end_of_stream(E)), !, ( E = at ; E = past ). - +%% set_stream_position(+Stream, +Position). +% +% Sets the current position of the stream Stream to Position. set_stream_position(S_or_a, Position) :- ( var(Position) -> throw(error(instantiation_error, set_stream_position/2)) @@ -1631,18 +2183,30 @@ set_stream_position(S_or_a, Position) :- ; throw(error(domain_error(stream_position, Position), set_stream_position/2)) ). +%% callable(X). +% +% True iff X is bound o an atom or a compund term. callable(X) :- ( nonvar(X), functor(X, F, _), atom(F) -> true ; false ). +%% nl. +% +% Writes a new line character to the current output stream. nl :- current_output(Stream), nl(Stream). +%% nl(+Stream). +% +% Writes a new line character to the stream Stream. nl(Stream) :- put_char(Stream, '\n'). +%% error(ErrorTerm, ImpDef). +% +% Throws an exception of the following structure: `error(ErrorTerm, ImpDef)`. error(Error_term, Imp_def) :- throw(error(Error_term, Imp_def)). diff --git a/src/lib/charsio.pl b/src/lib/charsio.pl index 4240319d..d47d3642 100644 --- a/src/lib/charsio.pl +++ b/src/lib/charsio.pl @@ -1,9 +1,18 @@ +/** High-level predicates to work with chars and strings + +This module contains predicates that relates strings of chars +to other representations, as well as high-level predicates to +read and write chars. + +*/ + :- module(charsio, [char_type/2, chars_utf8bytes/2, get_single_char/1, get_n_chars/3, - read_line_to_chars/3, + get_line_to_chars/3, read_from_chars/2, + read_term_from_chars/3, write_term_to_chars/3, chars_base64/3]). @@ -65,6 +74,63 @@ extend_var_list_([V|Vs], N, VarList, NewVarList, VarType) :- ). +%% char_type(+Char, -Type). +% +% Given a Char, Type is one of the categories that char fits in. +% Possible categories are: +% +% - `alnum` +% - `alpha` +% - `alphabetic` +% - `alphanumeric` +% - `ascii` +% - `ascii_graphic` +% - `ascii_punctuation` +% - `binary_digit` +% - `control` +% - `decimal_digit` +% - `exponent` +% - `graphic` +% - `graphic_token` +% - `hexadecimal_digit` +% - `layout` +% - `lower` +% - `meta` +% - `numeric` +% - `octal_digit` +% - `octet` +% - `prolog` +% - `sign` +% - `solo` +% - `symbolic_control` +% - `symbolic_hexadecimal` +% - `upper` +% - `to_lower(Lower)` +% - `to_upper(Upper)` +% - `whitespace` +% +% An example: +% +% ``` +% ?- char_type(a, Type). +% Type = alnum +% ; Type = alpha +% ; Type = alphabetic +% ; Type = alphanumeric +% ; Type = ascii +% ; Type = ascii_graphic +% ; Type = hexadecimal_digit +% ; Type = lower +% ; Type = octet +% ; Type = prolog +% ; Type = symbolic_control +% ; Type = to_lower("a") +% ; Type = to_upper("A") +% ; false. +% ``` +% +% Note that uppercase and lowercase transformations use a string. This is because +% some characters do not map 1:1 between lowercase and uppercase. char_type(Char, Type) :- must_be(character, Char), ( ground(Type) -> @@ -102,27 +168,68 @@ ctype(sign). ctype(solo). ctype(symbolic_control). ctype(symbolic_hexadecimal). +ctype(to_lower(_)). +ctype(to_upper(_)). ctype(upper). ctype(whitespace). +%% get_single_char(-Char). +% +% Gets a single char from the current input stream. get_single_char(C) :- ( var(C) -> '$get_single_char'(C) ; atom_length(C, 1) -> '$get_single_char'(C) ; type_error(in_character, C, get_single_char/1) ). - +%% read_from_chars(+Chars, -Term). +% +% Given a string made of chars which contains a representation of +% a Prolog term, Term is the Prolog term represented. Example: +% +% ``` +% ?- read_from_chars("f(x,y).", X). +% X = f(x,y). +% ``` read_from_chars(Chars, Term) :- must_be(chars, Chars), - '$read_term_from_chars'(Chars, Term). + must_be(var, Term), + '$read_from_chars'(Chars, Term). +%% read_term_from_chars(+Chars, -Term, +Options). +% +% Like `read_from_chars`, except the reader is configured according to +% `Options` which are those of `read_term`. +% +% ``` +% ?- read_term_from_chars("f(X,y).", T, [variable_names(['X'=X])]). +% T = f(X,y). +% ``` +read_term_from_chars(Chars, Term, Options) :- + must_be(chars, Chars), + must_be(var, Term), + builtins:parse_read_term_options(Options, [Singletons, VariableNames, Variables], read_term_from_chars/3), + '$read_term_from_chars'(Chars, Term, Singletons, Variables, VariableNames). +%% write_term_to_chars(+Term, +Options, -Chars). +% +% Given a Term which is a Prolog term and a set of options, Chars is +% string representation of that term. Options available are: +% +% * `ignore_ops(+Boolean)` if `true`, the generic term representation is used everywhere. In `false` +% (default), operators do not use that generic term representation. +% * `max_depth(+N)` if the term is nested deeper than N, print the reminder as ellipses. +% If N = 0 (default), there's no limit. +% * `numbervars(+Boolean)` if true, replaces `$VAR(N)` variables with letters, in order. Default is false. +% * `quoted(+Boolean)` if true, strings and atoms that need quotes to be valid Prolog syntax, are quoted. Default is false. +% * `variable_names(+List)` assign names to variables in term. List should be a list of terms of format `Name=Var`. +% * `double_quotes(+Boolean)` if true, strings are printed in double quotes rather than with list notation. Default is false. write_term_to_chars(_, Options, _) :- var(Options), instantiation_error(write_term_to_chars/3). write_term_to_chars(Term, Options, Chars) :- builtins:parse_write_options(Options, - [IgnoreOps, MaxDepth, NumberVars, Quoted, VNNames], + [DoubleQuotes, IgnoreOps, MaxDepth, NumberVars, Quoted, VNNames], write_term_to_chars/3), ( nonvar(Chars) -> throw(error(uninstantiation_error(Chars), write_term_to_chars/3)) @@ -131,7 +238,7 @@ write_term_to_chars(Term, Options, Chars) :- ), term_variables(Term, Vars), extend_var_list(Vars, VNNames, NewVarNames, numbervars), - '$write_term_to_chars'(Chars, Term, IgnoreOps, NumberVars, Quoted, NewVarNames, MaxDepth). + '$write_term_to_chars'(Chars, Term, IgnoreOps, NumberVars, Quoted, NewVarNames, MaxDepth, DoubleQuotes). % Encodes Ch character to list of Bytes. char_utf8bytes(Ch, Bytes) :- @@ -151,6 +258,17 @@ encode(Code, Prefix, Nb) --> % Maps characters and UTF-8 bytes. % If Cs is a variable, parses Bs as a list of UTF-8 bytes. % Otherwise, transform the list of characters Cs to UTF-8 bytes. + +%% chars_utf8bytes(?Chars, ?Bytes). +% +% Maps a string made of chars with a list of UTF-8 bytes. Some examples: +% +% ``` +% ?- chars_utf8bytes("Prolog", X). +% X = [80,114,111,108,111,103]. +% ?- chars_utf8bytes(X, [226, 136, 145]). +% X = "∑". +% ``` chars_utf8bytes(Cs, Bs) :- var(Cs), must_be(list, Bs) -> once(phrase(decode_utf8(Cs), Bs)) @@ -177,58 +295,66 @@ continuation(Code, Chars, Nb) --> [Byte], % each remaining continuation byte (if any) will raise 0xFFFD too continuation(_, ['\xFFFD\'|T], _) --> [_], decode_utf8(T). - -read_line_to_chars(Stream, Cs0, Cs) :- +%% get_line_to_chars(+Stream, -Chars, +InitialChars). +% +% Reads chars from stream Stream until it finds a `\n` character. +% InitialChars will be appended at the end of Chars +get_line_to_chars(Stream, Cs0, Cs) :- '$get_n_chars'(Stream, 1, Char), % this also works for binary streams ( Char == [] -> Cs0 = Cs ; Char = [C], Cs0 = [C|Rest], ( C == '\n' -> Rest = Cs - ; read_line_to_chars(Stream, Rest, Cs) + ; get_line_to_chars(Stream, Rest, Cs) ) ). -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Read N characters from Stream. - - If N is a variable, read until EOF, unifying N with the number of - characters read. -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - +%% get_n_chars(+Stream, ?N, -Chars). +% +% Read N chars from stream Stream. N can be an integer, in that case +% only N chars are read, or a variable, unifying N with the number of chars +% read until it found EOF. get_n_chars(Stream, N, Cs) :- can_be(integer, N), ( var(N) -> - read_to_eof(Stream, Cs), + get_to_eof(Stream, Cs), length(Cs, N) ; N >= 0, '$get_n_chars'(Stream, N, Cs) ). -read_to_eof(Stream, Cs) :- - '$get_n_chars'(Stream, 512, Cs0), +get_n_chars_wrapper(Stream, N, Cs) :- + '$get_n_chars'(Stream, N, Cs). + +get_to_eof(Stream, Cs) :- + catch(get_n_chars_wrapper(Stream, 512, Cs0), + error(syntax_error(unexpected_end_of_file), _), + Cs0 = []), ( Cs0 == [] -> Cs = [] ; partial_string(Cs0, Cs, Rest), - read_to_eof(Stream, Rest) + get_to_eof(Stream, Rest) ). -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Relation between a list of characters Cs and its Base64 encoding Bs, - also a list of characters. - - At least one of the arguments must be instantiated. - - Options are: - - - padding(Boolean) - Whether to use padding: true (the default) or false. - - charset(C) - Either 'standard' (RFC 4648 §4, the default) or 'url' (RFC 4648 §5). - - Example: - - ?- chars_base64("hello", Bs, []). - Bs = "aGVsbG8=". -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +%% chars_base64(?Chars, ?Base64, +Options). +% +% Relation between a list of characters Cs and its Base64 encoding Bs, +% also a list of characters. +% +% At least one of the arguments must be instantiated. +% +% Options are: +% +% - `padding(Boolean)` +% Whether to use padding: true (the default) or false. +% - `charset(C)` +% Either 'standard' (RFC 4648 §4, the default) or 'url' (RFC 4648 §5). +% +% Example: +% +% ``` +% ?- chars_base64("hello", Bs, []). +% Bs = "aGVsbG8=". +% ``` chars_base64(Cs, Bs, Options) :- must_be(list, Options), diff --git a/src/lib/clpb.pl b/src/lib/clpb.pl index e2e9ce66..3b7ac007 100644 --- a/src/lib/clpb.pl +++ b/src/lib/clpb.pl @@ -1,6 +1,6 @@ /* CLP(B): Constraint Logic Programming over Boolean Variables - Copyright (C): 2019 Markus Triska + Copyright (C): 2019-2023 Markus Triska All rights reserved. E-mail: triska@metalevel.at @@ -105,6 +105,262 @@ goal_expansion(del_attr(Var, Module), (var(Var) -> put_atts(Var, -Access);true)) Access =.. [Module,_]. +/** Constraint Logic Programming over Boolean variables + +## Introduction + +This library provides CLP(B), Constraint Logic Programming over +Boolean variables. It can be used to model and solve combinatorial +problems such as verification, allocation and covering tasks. + +CLP(B) is an instance of the general CLP(_X_) scheme, +extending logic programming with reasoning over specialised domains. + +The implementation is based on reduced and ordered Binary Decision +Diagrams (BDDs). + +Benchmarks and usage examples of this library are available from: +[*https://www.metalevel.at/clpb/*](https://www.metalevel.at/clpb/) + +## Boolean expressions + +A _Boolean expression_ is one of: + +| `0` | false | +| `1` | true | +| _variable_ | unknown truth value | +| _atom_ | universally quantified variable | +| ~ _Expr_ | logical NOT | +| _Expr_ + _Expr_ | logical OR | +| _Expr_ * _Expr_ | logical AND | +| _Expr_ # _Expr_ | exclusive OR | +| _Var_ ^ _Expr_ | existential quantification | +| _Expr_ =:= _Expr_ | equality | +| _Expr_ =\= _Expr_ | disequality (same as #) | +| _Expr_ =< _Expr_ | less or equal (implication) | +| _Expr_ >= _Expr_ | greater or equal | +| _Expr_ < _Expr_ | less than | +| _Expr_ > _Expr_ | greater than | +| card(Is,Exprs) | cardinality constraint (_see below_) | +| `+(Exprs)` | n-fold disjunction (_see below_) | +| `*(Exprs)` | n-fold conjunction (_see below_) | + +where _Expr_ again denotes a Boolean expression. + +The Boolean expression `card(Is,Exprs)` is true iff the number of true +expressions in the list `Exprs` is a member of the list `Is` of +integers and integer ranges of the form `From-To`. For example, to +state that precisely two of the three variables `X`, `Y` and `Z` are +`true`, you can use `sat(card([2],[X,Y,Z]))`. + +`+(Exprs)` and `*(Exprs)` denote, respectively, the disjunction and +conjunction of all elements in the list `Exprs` of Boolean +expressions. + +Atoms denote parametric values that are universally quantified. All +universal quantifiers appear implicitly in front of the entire +expression. In residual goals, universally quantified variables always +appear on the right-hand side of equations. Therefore, they can be +used to express functional dependencies on input variables. + +## Interface predicates + +The most frequently used CLP(B) predicates are: + + * `sat(+Expr)` + True iff the Boolean expression Expr is satisfiable. + + * `taut(+Expr, -T)` + If Expr is a tautology with respect to the posted constraints, succeeds + with *T = 1*. If Expr cannot be satisfied, succeeds with *T = 0*. + Otherwise, it fails. + + * `labeling(+Vs)` + Assigns truth values to the variables Vs such that all constraints + are satisfied. + +The unification of a CLP(B) variable _X_ with a term _T_ is equivalent +to posting the constraint sat(X=:=T). + +## Examples + +Here is an example session with a few queries and their answers: + +``` +?- use_module(library(clpb)). + true. + +?- sat(X*Y). + X = 1, Y = 1. + +?- sat(X * ~X). + false. + +?- taut(X * ~X, T). + T = 0, clpb:sat(X=:=X). + +?- sat(X^Y^(X+Y)). + clpb:sat(X=:=X), clpb:sat(Y=:=Y). + +?- sat(X*Y + X*Z), labeling([X,Y,Z]). + X = 1, Y = 0, Z = 1 +; X = 1, Y = 1, Z = 0 +; X = 1, Y = 1, Z = 1. + +?- sat(X =< Y), sat(Y =< Z), taut(X =< Z, T). + T = 1, clpb:sat(X=:=X*Y), clpb:sat(Y=:=Y*Z). + +?- sat(1#X#a#b). + clpb:sat(X=:=a#b). +``` + +The pending residual goals constrain remaining variables to Boolean +expressions and are declaratively equivalent to the original query. +The last example illustrates that when applicable, remaining variables +are expressed as functions of universally quantified variables. + +## Obtaining BDDs + +By default, CLP(B) residual goals appear in (approximately) algebraic +normal form (ANF). This projection is often computationally expensive. +We can assert `clpb:clpb_residuals(bdd)` to see the BDD representation +of all constraints. This results in faster projection to residual +goals, and is also useful for learning more about BDDs. For example: + +``` +?- asserta(clpb:clpb_residuals(bdd)). + true. + +?- sat(X#Y). +node(3)- (v(X, 0)->node(2);node(1)), +node(1)- (v(Y, 1)->true;false), +node(2)- (v(Y, 1)->false;true). +``` + +Note that this representation cannot be pasted back on the toplevel, +and its details are subject to change. Use copy_term/3 to obtain +such answers as Prolog terms. + +The variable order of the BDD is determined by the order in which the +variables first appear in constraints. To obtain different orders, +we can for example use: + +``` +?- sat(+[1,Y,X]), sat(X#Y). +node(3)- (v(Y, 0)->node(2);node(1)), +node(1)- (v(X, 1)->true;false), +node(2)- (v(X, 1)->false;true). +``` + +## Enabling monotonic CLP(B) + +In the default execution mode, CLP(B) constraints are _not_ monotonic. +This means that _adding_ constraints can yield new solutions. For +example: + +``` +?- sat(X=:=1), X = 1+0. + false. + +?- X = 1+0, sat(X=:=1), X = 1+0. + X = 1+0. +``` + +This behaviour is highly problematic from a logical point of view, and +it may render [*declarative +debugging*](https://www.metalevel.at/prolog/debugging) +techniques inapplicable. + +Assert `clpb:monotonic` to make CLP(B) *monotonic*. If this mode is +enabled, then you must wrap CLP(B) variables with the functor +`v/1`. For example: + +``` +?- asserta(clpb:monotonic). + true. + +?- sat(v(X)=:=1#1). + X = 0. +``` + +## Example: Pigeons + +In this example, we are attempting to place _I_ pigeons into _J_ holes +in such a way that each hole contains at most one pigeon. One +interesting property of this task is that it can be formulated using +only _cardinality constraints_ (`card/2`). Another interesting aspect +is that this task has no short resolution refutations in general. + +In the following, we use [*Prolog DCG +notation*](https://www.metalevel.at/prolog/dcg) to describe a +list `Cs` of CLP(B) constraints that must all be satisfied. + +``` +:- use_module(library(clpb)). +:- use_module(library(clpz)). +:- use_module(library(lists)). +:- use_module(library(dcgs)). + +pigeon(I, J, Rows, Cs) :- + length(Rows, I), length(Row, J), + maplist(same_length(Row), Rows), + transpose(Rows, TRows), + phrase((all_cards(Rows,[1]),all_cards(TRows,[0,1])), Cs). + +all_cards([], _) --> []. +all_cards([Ls|Lss], Cs) --> [card(Cs,Ls)], all_cards(Lss, Cs). +``` + +Example queries: + +``` +?- pigeon(9, 8, Rows, Cs), sat(*(Cs)). + false. + +?- pigeon(2, 3, Rows, Cs), sat(*(Cs)), + append(Rows, Vs), labeling(Vs), + maplist(portray_clause, Rows). +[0,0,1]. +[0,1,0]. +etc. +``` + +## Example: Boolean circuit + +Consider a Boolean circuit that express the Boolean function =|XOR|= +with 4 =|NAND|= gates. We can model such a circuit with CLP(B) +constraints as follows: + +``` +:- use_module(library(clpb)). + +nand_gate(X, Y, Z) :- sat(Z =:= ~(X*Y)). + +xor(X, Y, Z) :- + nand_gate(X, Y, T1), + nand_gate(X, T1, T2), + nand_gate(Y, T1, T3), + nand_gate(T2, T3, Z). +``` + +Using universally quantified variables, we can show that the circuit +does compute =|XOR|= as intended: + +``` +?- xor(x, y, Z). + clpb:sat(Z=:=x#y). +``` + +## Acknowledgments + +The interface predicates of this library follow the example of +[*SICStus Prolog*](https://sicstus.sics.se). + +Use SICStus Prolog for higher performance in many cases. + +*/ + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Each CLP(B) variable belongs to exactly one BDD. Each CLP(B) variable gets an attribute (in module "clpb") of the form: @@ -1108,19 +1364,17 @@ indomain(1). % % Examples: % -% == +% ``` % ?- sat(A =< B), Vs = [A,B], sat_count(+[1|Vs], Count). -% Vs = [A, B], -% Count = 3, -% sat(A=:=A*B). +% Vs = [A,B], Count = 3, clpb:sat(A=:=A*B). % % ?- length(Vs, 120), % sat_count(+Vs, CountOr), % sat_count(*(Vs), CountAnd). -% Vs = [...], -% CountOr = 1329227995784915872903807060280344575, -% CountAnd = 1. -% == +% Vs = [...], +% CountOr = 1329227995784915872903807060280344575, +% CountAnd = 1. +% ``` @@ -1248,7 +1502,7 @@ random_bindings(VNum, Node) --> % linear objective function over Boolean variables Vs with integer % coefficients Weights. This predicate assigns 0 and 1 to the % variables in Vs such that all stated constraints are satisfied, and -% Maximum is the maximum of sum(Weight_i*V_i) over all admissible +% Maximum is the maximum of `sum(Weight_i*V_i)` over all admissible % assignments. On backtracking, all admissible assignments that % attain the optimum are generated. % @@ -1257,10 +1511,11 @@ random_bindings(VNum, Node) --> % % Example: % -% == +% ``` % ?- sat(A#B), weighted_maximum([1,2,1], [A,B,C], Maximum). -% A = 0, B = 1, C = 1, Maximum = 3. -% == +% A = 0, B = 1, C = 1, Maximum = 3 +% ; false. +% ``` weighted_maximum(Ws, Vars, Max) :- must_be(list(integer), Ws), @@ -1373,6 +1628,7 @@ skip_to_var_(Var, Weight, [Var0-Weight0|VWs0], VWs) --> attribute_goals(Var) --> { var_index_root(Var, _, Root) }, + !, ( { root_get_formula_bdd(Root, Formula, BDD) } -> { del_bdd(Root) }, ( { clpb_residuals(bdd) } -> @@ -1400,6 +1656,10 @@ attribute_goals(Var) --> booleans(RestVs) ; boolean(Var) % the variable may have occurred only in taut/2 ). +attribute_goals(Var) --> + { get_atts(Var, clpb_bdd(BDD)), + ground(BDD), + put_atts(Var, -clpb_bdd(_)) }. del_clpb(Var) :- del_attr(Var, clpb), diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 2734a683..ed01f079 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -3,7 +3,7 @@ Author: Markus Triska E-mail: triska@metalevel.at WWW: https://www.metalevel.at - Copyright (C): 2016-2022 Markus Triska + Copyright (C): 2016-2023 Markus Triska This library provides CLP(ℤ): @@ -147,7 +147,7 @@ clpz_gcc_num/1, clpz_gcc_occurred/1, queue/2, - enabled/1. + disabled/0. :- dynamic(monotonic/0). :- dynamic(clpz_equal_/2). @@ -220,31 +220,23 @@ partition_([X|Xs], Pred, Ls0, Es0, Gs0) :- :- meta_predicate(include(1, ?, ?)). -include(Goal, Ls0, Ls) :- - include_(Ls0, Goal, Ls). - -include_([], _, []). -include_([L|Ls0], Goal, Ls) :- +include(_, [], []). +include(Goal, [L|Ls0], Ls) :- ( call(Goal, L) -> Ls = [L|Rest] ; Ls = Rest ), - include_(Ls0, Goal, Rest). - + include(Goal, Ls0, Rest). :- meta_predicate(exclude(1, ?, ?)). -exclude(Goal, Ls0, Ls) :- - exclude_(Ls0, Goal, Ls). - -exclude_([], _, []). -exclude_([L|Ls0], Goal, Ls) :- +exclude(_, [], []). +exclude(Goal, [L|Ls0], Ls) :- ( call(Goal, L) -> Ls = Rest ; Ls = [L|Rest] ), - exclude_(Ls0, Goal, Rest). - + exclude(Goal, Ls0, Rest). %:- discontiguous clpz:goal_expansion/5. @@ -282,38 +274,39 @@ exclude_([L|Ls0], Goal, Ls) :- :- op(700, xfx, cis_lt). :- op(1200, xfx, ++>). -/** Constraint Logic Programming over Integers +/** Constraint Logic Programming over Integers -## Introduction {#clpz-intro} +## Introduction This library provides CLP(ℤ): Constraint Logic Programming over Integers. -CLP(ℤ) is an instance of the general CLP(.) scheme, extending logic +CLP(ℤ) is an instance of the general CLP(_X_) scheme, extending logic programming with reasoning over specialised domains. CLP(ℤ) lets us -reason about **integers** in a way that honors the relational nature +reason about *integers* in a way that honors the relational nature of Prolog. There are two major use cases of CLP(ℤ) constraints: - 1. [**declarative integer arithmetic**](<#clpz-integer-arith>) - 2. solving **combinatorial problems** such as planning, scheduling + 1. [*declarative integer arithmetic*](#clpz-integer-arith) + + 2. solving *combinatorial problems* such as planning, scheduling and allocation tasks. The predicates of this library can be classified as: - * _arithmetic_ constraints like #=/2, #>/2 and #\=/2 [](<#clpz-arithmetic>) - * the _membership_ constraints in/2 and ins/2 [](<#clpz-membership>) - * the _enumeration_ predicates indomain/1, label/1 and labeling/2 [](<#clpz-enumeration>) - * _combinatorial_ constraints like all_distinct/1 and global_cardinality/2 [](<#clpz-global>) - * _reification_ predicates such as #<==>/2 [](<#clpz-reification-predicates>) - * _reflection_ predicates such as fd_dom/2 [](<#clpz-reflection-predicates>) + * _arithmetic_ constraints like `(#=)/2`, `(#>)/2` and `(#\=)/2` + * the _membership_ constraints `(in)/2` and `(ins)/2` + * the _enumeration_ predicates `indomain/1`, `label/1` and `labeling/2` + * _combinatorial_ constraints like `all_distinct/1` and `global_cardinality/2` + * _reification_ predicates such as `(#<==>)/2` + * _reflection_ predicates such as `fd_dom/2` -In most cases, [_arithmetic constraints_](<#clpz-arith-constraints>) +In most cases, [_arithmetic constraints_](#clpz-arith-constraints) are the only predicates you will ever need from this library. When reasoning over integers, simply replace low-level arithmetic predicates like `(is)/2` and `(>)/2` by the corresponding CLP(ℤ) -constraints like #=/2 and #>/2 to honor and preserve declarative +constraints like `(#=)/2` and `(#>)/2` to honor and preserve declarative properties of your programs. For satisfactory performance, arithmetic constraints are implicitly rewritten at compilation time so that low-level fallback predicates are automatically used whenever @@ -322,18 +315,18 @@ possible. Almost all Prolog programs also reason about integers. Therefore, it is highly advisable that you make CLP(ℤ) constraints available in all your programs. One way to do this is to put the following directive in -your =|~/.scryerrc|= initialisation file: +your `~/.scryerrc` initialisation file: -== +``` :- use_module(library(clpz)). -== +``` All example programs that appear in the CLP(ℤ) documentation assume that you have done this. Important concepts and principles of this library are illustrated by means of usage examples that are available in a public git repository: -[**github.com/triska/clpz**](https://github.com/triska/clpz) +[*https://github.com/triska/clpz*](https://github.com/triska/clpz) If you are used to the complicated operational considerations that low-level arithmetic primitives necessitate, then moving to CLP(ℤ) @@ -353,7 +346,7 @@ primitives are impure limitations that are better deferred to more advanced lectures. More information about CLP(ℤ) constraints and their implementation is -contained in: [**metalevel.at/drt.pdf**](https://www.metalevel.at/drt.pdf) +contained in: [*metalevel.at/drt.pdf*](https://www.metalevel.at/drt.pdf) The best way to discuss applying, improving and extending CLP(ℤ) constraints is to use the dedicated `clpz` tag on @@ -361,7 +354,8 @@ constraints is to use the dedicated `clpz` tag on foremost CLP(ℤ) experts regularly participate in these discussions and will help you for free on this platform. -## Arithmetic constraints {#clpz-arith-constraints} +{#clpz-arith-constraints} +## Arithmetic constraints In modern Prolog systems, *arithmetic constraints* subsume and supersede low-level predicates over integers. The main advantage of @@ -369,37 +363,37 @@ arithmetic constraints is that they are true _relations_ and can be used in all directions. For most programs, arithmetic constraints are the only predicates you will ever need from this library. -The most important arithmetic constraint is #=/2, which subsumes both -`(is)/2` and `(=:=)/2` over integers. Use #=/2 to make your programs -more general. +The most important arithmetic constraint is `(#=)/2`, which subsumes +both `(is)/2` and `(=:=)/2` over integers. Use `(#=)/2` to make your +programs more general. In total, the arithmetic constraints are: - | Expr1 `#=` Expr2 | Expr1 equals Expr2 | - | Expr1 `#\=` Expr2 | Expr1 is not equal to Expr2 | - | Expr1 `#>=` Expr2 | Expr1 is greater than or equal to Expr2 | - | Expr1 `#=<` Expr2 | Expr1 is less than or equal to Expr2 | - | Expr1 `#>` Expr2 | Expr1 is greater than Expr2 | - | Expr1 `#<` Expr2 | Expr1 is less than Expr2 | +| Expr1 `#=` Expr2 | Expr1 equals Expr2 | +| Expr1 `#\=` Expr2 | Expr1 is not equal to Expr2 | +| Expr1 `#>=` Expr2 | Expr1 is greater than or equal to Expr2 | +| Expr1 `#=<` Expr2 | Expr1 is less than or equal to Expr2 | +| Expr1 `#>` Expr2 | Expr1 is greater than Expr2 | +| Expr1 `#<` Expr2 | Expr1 is less than Expr2 | `Expr1` and `Expr2` denote *arithmetic expressions*, which are: - | _integer_ | Given value | - | _variable_ | Unknown integer | - | ?(_variable_) | Unknown integer | - | -Expr | Unary minus | - | Expr + Expr | Addition | - | Expr * Expr | Multiplication | - | Expr - Expr | Subtraction | - | Expr ^ Expr | Exponentiation | - | min(Expr,Expr) | Minimum of two expressions | - | max(Expr,Expr) | Maximum of two expressions | - | Expr `mod` Expr | Modulo induced by floored division | - | Expr `rem` Expr | Modulo induced by truncated division | - | abs(Expr) | Absolute value | - | sign(Expr) | Sign (-1, 0, 1) of Expr | - | Expr // Expr | Truncated integer division | - | Expr div Expr | Floored integer division | +| _integer_ | Given value | +| _variable_ | Unknown integer | +| #(_variable_) | Unknown integer | +| -Expr | Unary minus | +| Expr + Expr | Addition | +| Expr * Expr | Multiplication | +| Expr - Expr | Subtraction | +| Expr ^ Expr | Exponentiation | +| min(Expr,Expr) | Minimum of two expressions | +| max(Expr,Expr) | Maximum of two expressions | +| Expr `mod` Expr | Modulo induced by floored division | +| Expr `rem` Expr | Modulo induced by truncated division | +| abs(Expr) | Absolute value | +| sign(Expr) | Sign (-1, 0, 1) of Expr | +| Expr // Expr | Truncated integer division | +| Expr div Expr | Floored integer division | where `Expr` again denotes an arithmetic expression. @@ -407,28 +401,29 @@ The bitwise operations `(\)/1`, `(/\)/2`, `(\/)/2`, `(>>)/2`, `(<<)/2`, `lsb/1`, `msb/1`, `popcount/1` and `(xor)/2` are also supported. -## Declarative integer arithmetic {#clpz-integer-arith} +{#clpz-integer-arith} +## Declarative integer arithmetic -The [_arithmetic constraints_](<#clpz-arith-constraints>) #=/2, #>/2 -etc. are meant to be used _instead_ of the primitives `(is)/2`, -`(=:=)/2`, `(>)/2` etc. over integers. Almost all Prolog programs also -reason about integers. Therefore, it is recommended that you put the -following directive in your =|~/.scryerrc|= initialisation file to make -CLP(ℤ) constraints available in all your programs: +The [_arithmetic constraints_](#clpz-arith-constraints) `(#=)/2`, +`(#>)/2` etc. are meant to be used _instead_ of the primitives +`(is)/2`, `(=:=)/2`, `(>)/2` etc. over integers. Almost all Prolog +programs also reason about integers. Therefore, it is recommended that +you put the following directive in your `~/.scryerrc` initialisation +file to make CLP(ℤ) constraints available in all your programs: -== +``` :- use_module(library(clpz)). -== +``` Throughout the following, it is assumed that you have done this. The most basic use of CLP(ℤ) constraints is _evaluation_ of arithmetic expressions involving integers. For example: -== +``` ?- X #= 1+2. -X = 3. -== + X = 3. +``` This could in principle also be achieved with the lower-level predicate `(is)/2`. However, an important advantage of arithmetic @@ -436,22 +431,22 @@ constraints is their purely relational nature: Constraints can be used in _all directions_, also if one or more of their arguments are only partially instantiated. For example: -== +``` ?- 3 #= Y+2. -Y = 1. -== + Y = 1. +``` This relational nature makes CLP(ℤ) constraints easy to explain and use, and well suited for beginners and experienced Prolog programmers alike. In contrast, when using low-level integer arithmetic, we get: -== +``` ?- 3 is Y+2. -ERROR: is/2: Arguments are not sufficiently instantiated + error(instantiation_error,(is)/2). ?- 3 =:= Y+2. -ERROR: =:=/2: Arguments are not sufficiently instantiated -== + error(instantiation_error,(is)/2). +``` Due to the necessary operational considerations, the use of these low-level arithmetic predicates is considerably harder to understand @@ -459,7 +454,7 @@ and should therefore be deferred to more advanced lectures. For supported expressions, CLP(ℤ) constraints are drop-in replacements of these low-level arithmetic predicates, often yielding -more general programs. See [`n_factorial/2`](<#clpz-factorial>) for an +more general programs. See [`n_factorial/2`](#clpz-factorial) for an example. This library uses goal_expansion/2 to automatically rewrite @@ -467,19 +462,19 @@ constraints at compilation time so that low-level arithmetic predicates are _automatically_ used whenever possible. For example, the predicate: -== +``` positive_integer(N) :- N #>= 1. -== +``` is executed as if it were written as: -== +``` positive_integer(N) :- ( integer(N) -> N >= 1 ; N #>= 1 ). -== +``` This illustrates why the performance of CLP(ℤ) constraints is almost always completely satisfactory when they are used in modes that can be @@ -496,58 +491,59 @@ primitives by providing declarative alternatives that are meant to be used instead. -## Example: Factorial relation {#clpz-factorial} +{#clpz-factorial} +## Example: Factorial relation -We illustrate the benefit of using #=/2 for more generality with a +We illustrate the benefit of using `(#=)/2` for more generality with a simple example. Consider first a rather conventional definition of `n_factorial/2`, relating each natural number _N_ to its factorial _F_: -== +``` n_factorial(0, 1). n_factorial(N, F) :- N #> 0, N1 #= N - 1, n_factorial(N1, F1), F #= N * F1. -== +``` This program uses CLP(ℤ) constraints _instead_ of low-level arithmetic throughout, and everything that _would have worked_ with low-level arithmetic _also_ works with CLP(ℤ) constraints, retaining roughly the same performance. For example: -== +``` ?- n_factorial(47, F). -F = 258623241511168180642964355153611979969197632389120000000000 ; -false. -== + F = 258623241511168180642964355153611979969197632389120000000000 +; false. +``` Now the point: Due to the increased flexibility and generality of CLP(ℤ) constraints, we are free to _reorder_ the goals as follows: -== +``` n_factorial(0, 1). n_factorial(N, F) :- N #> 0, N1 #= N - 1, F #= N * F1, n_factorial(N1, F1). -== +``` In this concrete case, _termination_ properties of the predicate are improved. For example, the following queries now both terminate: -== +``` ?- n_factorial(N, 1). -N = 0 ; -N = 1 ; -false. + N = 0 +; N = 1 +; false. ?- n_factorial(N, 3). -false. -== + false. +``` To make the predicate terminate if _any_ argument is instantiated, add the (implied) constraint `F #\= 0` before the recursive call. @@ -558,8 +554,8 @@ The value of CLP(ℤ) constraints does _not_ lie in completely freeing us from _all_ procedural phenomena. For example, the two programs do not even have the same _termination properties_ in all cases. Instead, the primary benefit of CLP(ℤ) constraints is that they allow -you to try different execution orders and apply [**declarative -debugging**](https://www.metalevel.at/prolog/debugging.html) +you to try different execution orders and apply [*declarative +debugging*](https://www.metalevel.at/prolog/debugging) techniques _at all_! Reordering goals (and clauses) can significantly impact the performance of Prolog programs, and you are free to try different variants if you use declarative approaches. Moreover, since @@ -567,27 +563,29 @@ all CLP(ℤ) constraints _always terminate_, placing them earlier can at most _improve_, never worsen, the termination properties of your programs. An additional benefit of CLP(ℤ) constraints is that they eliminate the complexity of introducing `(is)/2` and `(=:=)/2` to -beginners, since _both_ predicates are subsumed by #=/2 when reasoning -over integers. +beginners, since _both_ predicates are subsumed by `(#=)/2` when +reasoning over integers. -## Combinatorial constraints {#clpz-combinatorial} +{#clpz-combinatorial} +## Combinatorial constraints In addition to subsuming and replacing low-level arithmetic predicates, CLP(ℤ) constraints are often used to solve combinatorial problems such as planning, scheduling and allocation tasks. Among the -most frequently used *combinatorial constraints* are all_distinct/1, -global_cardinality/2 and cumulative/2. This library also provides -several other constraints like disjoint2/1 and automaton/8, which are +most frequently used *combinatorial constraints* are `all_distinct/1`, +`global_cardinality/2` and `cumulative/2`. This library also provides +several other constraints like `disjoint2/1` and `automaton/8`, which are useful in more specialized applications. -## Domains {#clpz-domains} +{#clpz-domains} +## Domains Each CLP(ℤ) variable has an associated set of admissible integers, which we call the variable's *domain*. Initially, the domain of each -CLP(ℤ) variable is the set of _all_ integers. CLP(ℤ) constraints -like #=/2, #>/2 and #\=/2 can at most reduce, and never extend, the -domains of their arguments. The constraints in/2 and ins/2 let us -explicitly state domains of CLP(ℤ) variables. The process of +CLP(ℤ) variable is the set of _all_ integers. CLP(ℤ) constraints like +`(#=)/2`, `(#>)/2` and `(#\=)/2` can at most reduce, and never extend, +the domains of their arguments. The constraints `(in)/2` and `(ins)/2` +let us explicitly state domains of CLP(ℤ) variables. The process of determining and adjusting domains of variables is called constraint *propagation*, and it is performed automatically by this library. When the domain of a variable contains only one element, then the variable @@ -596,12 +594,13 @@ is automatically unified to that element. Domains are taken into account when further constraints are stated, and by enumeration predicates like labeling/2. -## Example: Sudoku {#clpz-sudoku} +{#clpz-sudoku} +## Example: Sudoku As another example, consider _Sudoku_: It is a popular puzzle over integers that can be easily solved with CLP(ℤ) constraints. -== +``` sudoku(Rows) :- length(Rows, 9), maplist(same_length(Rows), Rows), append(Rows, Vs), Vs ins 1..9, @@ -627,81 +626,81 @@ problem(1, [[_,_,_,_,_,_,_,_,_], [5,_,_,_,_,_,_,7,3], [_,_,2,_,1,_,_,_,_], [_,_,_,_,4,_,_,_,9]]). -== +``` Sample query: -== -?- problem(1, Rows), sudoku(Rows), maplist(writeln, Rows). -[9,8,7,6,5,4,3,2,1] -[2,4,6,1,7,3,9,8,5] -[3,5,1,9,2,8,7,4,6] -[1,2,8,5,3,7,6,9,4] -[6,3,4,8,9,2,1,5,7] -[7,9,5,4,6,1,8,3,2] -[5,1,9,2,8,6,4,7,3] -[4,7,2,3,1,9,5,6,8] -[8,6,3,7,4,5,2,1,9] -Rows = [[9, 8, 7, 6, 5, 4, 3, 2|...], ... , [...|...]]. -== +``` +?- problem(1, Rows), sudoku(Rows), maplist(portray_clause, Rows). +[9,8,7,6,5,4,3,2,1]. +[2,4,6,1,7,3,9,8,5]. +[3,5,1,9,2,8,7,4,6]. +[1,2,8,5,3,7,6,9,4]. +[6,3,4,8,9,2,1,5,7]. +[7,9,5,4,6,1,8,3,2]. +[5,1,9,2,8,6,4,7,3]. +[4,7,2,3,1,9,5,6,8]. +[8,6,3,7,4,5,2,1,9]. + Rows = [[9,8,7,6,5,4,3,2,1]|...]. +``` In this concrete case, the constraint solver is strong enough to find the unique solution without any search. -## Residual goals {#clpz-residual-goals} +{#clpz-residual-goals} +## Residual goals Here is an example session with a few queries and their answers: -== +``` ?- X #> 3. -X in 4..sup. + clpz:(X in 4..sup). ?- X #\= 20. -X in inf..19\/21..sup. + clpz:(X in inf..19\/21..sup). ?- 2*X #= 10. -X = 5. + X = 5. ?- X*X #= 144. -X in -12\/12. + clpz:(X in-12\/12) +; false. ?- 4*X + 2*Y #= 24, X + Y #= 9, [X,Y] ins 0..sup. -X = 3, -Y = 6. + X = 3, Y = 6. ?- X #= Y #<==> B, X in 0..3, Y in 4..5. -B = 0, -X in 0..3, -Y in 4..5. -== + B = 0, clpz:(X in 0..3), clpz:(Y in 4..5). +``` The answers emitted by the toplevel are called _residual programs_, -and the goals that comprise each answer are called **residual goals**. +and the goals that comprise each answer are called *residual goals*. In each case above, and as for all pure programs, the residual program is declaratively equivalent to the original query. From the residual goals, it is clear that the constraint solver has deduced additional domain restrictions in many cases. To inspect residual goals, it is best to let the toplevel display them -for us. Wrap the call of your predicate into call_residue_vars/2 to +for us. Wrap the call of your predicate into `call_residue_vars/2` to make sure that all constrained variables are displayed. To make the constraints a variable is involved in available as a Prolog term for -further reasoning within your program, use copy_term/3. For example: +further reasoning within your program, use `copy_term/3`. For example: -== +``` ?- X #= Y + Z, X in 0..5, copy_term([X,Y,Z], [X,Y,Z], Gs). Gs = [clpz: (X in 0..5), clpz: (Y+Z#=X)], X in 0..5, Y+Z#=X. -== +``` -This library also provides _reflection_ predicates (like fd_dom/2, -fd_size/2 etc.) with which we can inspect a variable's current +This library also provides _reflection_ predicates (like `fd_dom/2`, +`fd_size/2` etc.) with which we can inspect a variable's current domain. These predicates can be useful if you want to implement your own labeling strategies. -## Core relations and search {#clpz-search} +{#clpz-search} +## Core relations and search Using CLP(ℤ) constraints to solve combinatorial tasks typically consists of two phases: @@ -722,7 +721,7 @@ cryptoarithmetic puzzle SEND + MORE = MONEY, where different letters denote distinct integers between 0 and 9. It can be modeled in CLP(ℤ) as follows: -== +``` puzzle([S,E,N,D] + [M,O,R,E] = [M,O,N,E,Y]) :- Vars = [S,E,N,D,M,O,R,Y], Vars ins 0..9, @@ -731,14 +730,14 @@ puzzle([S,E,N,D] + [M,O,R,E] = [M,O,N,E,Y]) :- M*1000 + O*100 + R*10 + E #= M*10000 + O*1000 + N*100 + E*10 + Y, M #\= 0, S #\= 0. -== +``` -Notice that we are _not_ using labeling/2 in this predicate, so that +Notice that we are _not_ using `labeling/2` in this predicate, so that we can first execute and observe the modeling part in isolation. Sample query and its result (actual variables replaced for readability): -== +``` ?- puzzle(As+Bs=Cs). As = [9, A2, A3, A4], Bs = [1, 0, B3, A2], @@ -750,7 +749,7 @@ A3 in 5..8, A4 in 2..8, B3 in 2..8, C5 in 2..8. -== +``` From this answer, we see that this core relation _terminates_ and is in fact _deterministic_. Moreover, we see from the residual goals that @@ -761,13 +760,11 @@ parts are cleanly separated. Labeling can then be used to search for solutions in a separate predicate or goal: -== +``` ?- puzzle(As+Bs=Cs), label(As). -As = [9, 5, 6, 7], -Bs = [1, 0, 8, 5], -Cs = [1, 0, 6, 5, 2] ; -false. -== + As = [9,5,6,7], Bs = [1,0,8,5], Cs = [1,0,6,5,2] +; false. +``` In this case, it suffices to label a subset of variables to find the puzzle's unique solution, since the constraint solver is strong enough @@ -775,7 +772,8 @@ to reduce the domains of remaining variables to singleton sets. In general though, it is necessary to label all variables to obtain ground solutions. -## Example: Eight queens puzzle {#clpz-n-queens} +{#clpz-n-queens} +## Example: Eight queens puzzle We illustrate the concepts of the preceding sections by means of the so-called _eight queens puzzle_. The task is to place 8 queens on an @@ -801,12 +799,12 @@ column, and which are subject to certain constraints. In fact, let us now generalize the task to the so-called _N queens puzzle_, which is obtained by replacing 8 by _N_ everywhere it occurs in the above description. We implement the above considerations in the -**core relation** `n_queens/2`, where the first argument is the number +*core relation* `n_queens/2`, where the first argument is the number of queens (which is identical to the number of rows and columns of the generalized chessboard), and the second argument is a list of _N_ integers that represents a solution in the form described above. -== +``` n_queens(N, Qs) :- length(Qs, N), Qs ins 1..N, @@ -821,7 +819,7 @@ safe_queens([Q|Qs], Q0, D0) :- abs(Q0 - Q) #\= D0, D1 #= D0 + 1, safe_queens(Qs, Q0, D1). -== +``` Note that all these predicates can be used in _all directions_: We can use them to _find_ solutions, _test_ solutions and _complete_ @@ -829,31 +827,35 @@ partially instantiated solutions. The original task can be readily solved with the following query: -== +``` ?- n_queens(8, Qs), label(Qs). -Qs = [1, 5, 8, 6, 3, 7, 2, 4] . -== + Qs = [1,5,8,6,3,7,2,4] +; ... . +``` Using suitable labeling strategies, we can easily find solutions with 80 queens and more: -== +``` ?- n_queens(80, Qs), labeling([ff], Qs). -Qs = [1, 3, 5, 44, 42, 4, 50, 7, 68|...] . + Qs = [1,3,5,44,42,4,50,7,68,57,76,61,6,39,30,40,8,54,36,41,...] +; ... . ?- time((n_queens(90, Qs), labeling([ff], Qs))). -% 5,904,401 inferences, 0.722 CPU in 0.737 seconds (98% CPU) -Qs = [1, 3, 5, 50, 42, 4, 49, 7, 59|...] . -== + % CPU time: 31.351s + Qs = [1,3,5,50,42,4,49,7,59,48,46,63,6,55,47,64,8,70,58,67,...] +; ... . +``` Experimenting with different search strategies is easy because we have separated the core relation from the actual search. -## Optimisation {#clpz-optimisation} +{#clpz-optimisation} +## Optimisation -We can use labeling/2 to minimize or maximize the value of a CLP(ℤ) +We can use `labeling/2` to minimize or maximize the value of a CLP(ℤ) expression, and generate solutions in increasing or decreasing order of the value. See the labeling options `min(Expr)` and `max(Expr)`, respectively. @@ -868,7 +870,7 @@ If necessary, we can use `once/1` to commit to the first optimal solution. However, it is often very valuable to see alternative solutions that are _also_ optimal, so that we can choose among optimal solutions by other criteria. For the sake of -[**purity**](https://www.metalevel.at/prolog/purity.html) and +[*purity*](https://www.metalevel.at/prolog/purity) and completeness, we recommend to avoid `once/1` and other constructs that lead to impurities in CLP(ℤ) programs. @@ -876,63 +878,67 @@ Related to optimisation with CLP(ℤ) constraints are `library(simplex)` and CLP(Q) which reason about _linear_ constraints over rational numbers. -## Reification {#clpz-reification} +{#clpz-reification} +## Reification -The constraints in/2, #=/2, #\=/2, #/2, #==/2 can be -_reified_, which means reflecting their truth values into Boolean -values represented by the integers 0 and 1. Let P and Q denote -reifiable constraints or Boolean variables, then: +The constraints `(in)/2`, `(#=)/2`, `(#\=)/2`, `(#<)/2`, `(#>)/2`, +`(#=<)/2`, and `(#>=)/2` can be _reified_, which means reflecting +their truth values into Boolean values represented by the integers 0 +and 1. Let P and Q denote reifiable constraints or Boolean variables, +then: - | #\ Q | True iff Q is false | - | P #\/ Q | True iff either P or Q | - | P #/\ Q | True iff both P and Q | - | P #\ Q | True iff either P or Q, but not both | - | P #<==> Q | True iff P and Q are equivalent | - | P #==> Q | True iff P implies Q | - | P #<== Q | True iff Q implies P | +| `#\ Q` | True iff Q is false | +| `P #\/ Q` | True iff either P or Q | +| `P #/\ Q` | True iff both P and Q | +| `P #\ Q` | True iff either P or Q, but not both | +| `P #<==> Q` | True iff P and Q are equivalent | +| `P #==> Q` | True iff P implies Q | +| `P #<== Q` | True iff Q implies P | The constraints of this table are reifiable as well. When reasoning over Boolean variables, also consider using CLP(B) constraints as provided by `library(clpb)`. -## Enabling monotonic CLP(ℤ) {#clpz-monotonicity} +{#clpz-monotonicity} +## Enabling monotonic CLP(ℤ) In the default execution mode, CLP(ℤ) constraints still exhibit some non-relational properties. For example, _adding_ constraints can yield new solutions: -== +``` ?- X #= 2, X = 1+1. -false. + false. ?- X = 1+1, X #= 2, X = 1+1. -X = 1+1. -== + X = 1+1. +``` This behaviour is highly problematic from a logical point of view, and it may render declarative debugging techniques inapplicable. -Assert `clpz:monotonic` to make CLP(ℤ) **monotonic**: This means +Assert `clpz:monotonic` to make CLP(ℤ) *monotonic*: This means that _adding_ new constraints _cannot_ yield new solutions. When this flag is `true`, we must wrap variables that occur in arithmetic expressions with the functor `(?)/1` or `(#)/1`. For example: -== +``` ?- assertz(clpz:monotonic). -true. + true. -?- #(X) #= #(Y) + #(Z). -#(Y)+ #(Z)#= #(X). +?- #X #= #Y + #Z. + clpz:(#Y+ #Z#= #X). ?- X #= 2, X = 1+1. -ERROR: Arguments are not sufficiently instantiated -== + error(instantiation_error,instantiation_error(unknown(_408),1)). +``` The wrapper can be omitted for variables that are already constrained to integers. -## Custom constraints {#clpz-custom-constraints} +{#clpz-custom-constraints} +## Custom constraints We can define custom constraints. The mechanism to do this is not yet finalised, and we welcome suggestions and descriptions of use cases @@ -942,7 +948,7 @@ As an example of how it can be done currently, let us define a new custom constraint `oneground(X,Y,Z)`, where Z shall be 1 if at least one of X and Y is instantiated: -== +``` :- multifile clpz:run_propagator/2. oneground(X, Y, Z) :- @@ -956,29 +962,29 @@ clpz:run_propagator(oneground(X, Y, Z), MState) :- ; integer(Y) -> clpz:kill(MState), Z = 1 ; true ). -== +``` -First, clpz:make_propagator/2 is used to transform a user-defined +First, `clpz:make_propagator/2` is used to transform a user-defined representation of the new constraint to an internal form. With -clpz:init_propagator/2, this internal form is then attached to X and +`clpz:init_propagator/2`, this internal form is then attached to X and Y. From now on, the propagator will be invoked whenever the domains of -X or Y are changed. Then, clpz:trigger_once/1 is used to give the +X or Y are changed. Then, `clpz:trigger_once/1` is used to give the propagator its first chance for propagation even though the variables' -domains have not yet changed. Finally, clpz:run_propagator/2 is +domains have not yet changed. Finally, `clpz:run_propagator/2` is extended to define the actual propagator. As explained, this predicate is automatically called by the constraint solver. The first argument is the user-defined representation of the constraint as used in -clpz:make_propagator/2, and the second argument is a mutable state +`clpz:make_propagator/2`, and the second argument is a mutable state that can be used to prevent further invocations of the propagator when -the constraint has become entailed, by using clpz:kill/1. An example +the constraint has become entailed, by using `clpz:kill/1`. An example of using the new constraint: -== +``` ?- oneground(X, Y, Z), Y = 5. Y = 5, Z = 1, X in inf..sup. -== +``` @author [Markus Triska](https://www.metalevel.at) */ @@ -1718,7 +1724,7 @@ intervals_to_domain(Is, D) :- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% ?Var in +Domain +%% in(?Var, +Domain) % % Var is an element of Domain. Domain is one of: % @@ -1745,7 +1751,7 @@ fd_variable(V) :- ; type_error(integer, V) ). -%% +Vars ins +Domain +%% ins(+Vars, +Domain) % % The variables in the list Vars are elements of Domain. @@ -1850,18 +1856,18 @@ label(Vs) :- labeling([], Vs). % must make Expr ground. If several such options are specified, they % are interpreted from left to right, e.g.: % -% == +% ``` % ?- [X,Y] ins 10..20, labeling([max(X),min(Y)],[X,Y]). -% == +% ``` % % This generates solutions in descending order of X, and for each % binding of X, solutions are generated in ascending order of Y. To % obtain the incomplete behaviour that other systems exhibit with % "maximize(Expr)" and "minimize(Expr)", use once/1, e.g.: % -% == +% ``` % once(labeling([max(Expr)], Vars)) -% == +% ``` % % Labeling is always complete, always terminates, and yields no % redundant solutions. @@ -1917,7 +1923,7 @@ label([], _, Selection, Order, Choice, Optim0, Consistency, Vars) :- exprs_singlevars([], []). exprs_singlevars([E|Es], [SV|SVs]) :- E =.. [F,Expr], - ?(Single) #= Expr, + #Single #= Expr, SV =.. [F,Single], exprs_singlevars(Es, SVs). @@ -2232,12 +2238,12 @@ all_different([X|Right], Left, Orig) :- % can detect that not all variables can assume distinct values given % the following domains: % -% == +% ``` % ?- maplist(in, Vs, % [1\/3..4, 1..2\/4, 1..2\/4, 1..3, 1..3, 1..6]), % all_distinct(Vs). % false. -% == +% ``` all_distinct(Ls) :- fd_must_be_list(Ls, all_distinct(Ls)-1), @@ -2268,13 +2274,13 @@ zero_or_more([_|_], N) :- N #> 0. % The sum of elements of the list Vars is in relation Rel to Expr. % Rel is one of #=, #\=, #<, #>, #=< or #>=. For example: % -% == +% ``` % ?- [A,B,C] ins 0..sup, sum([A,B,C], #=, 100). % A in 0..100, % A+B+C#=100, % B in 0..100, % C in 0..100. -% == +% ``` sum(Vs, Op, Value) :- must_be(list, Vs), @@ -2310,13 +2316,13 @@ single_value(V, V) :- var(V), !, non_monotonic(V). single_value(V, V) :- integer(V). single_value(?(V), V) :- fd_variable(V). -coeff_var_plusterm(C, V, T0, T0+(C* ?(V))). +coeff_var_plusterm(C, V, T0, T0+(C* #V)). coeff_int_linsum(C, I, S0, S) :- S is S0 + C*I. sum([], _, Sum, Op, Value) :- call(Op, Sum, Value). sum([C|Cs], [X|Xs], Acc, Op, Value) :- - ?(NAcc) #= Acc + C* ?(X), + #NAcc #= Acc + C* #X, sum(Cs, Xs, NAcc, Op, Value). multiples([], [], _). @@ -2325,7 +2331,7 @@ multiples([C|Cs], [V|Vs], Left) :- ( N =\= 1, gcd(C,N) =:= 1 -> gcd(Cs, N, GCD0), gcd(Left, GCD0, GCD), - ( GCD > 1 -> ?(V) #= GCD * ?(_) + ( GCD > 1 -> #V #= GCD * #_ ; true ) ; true @@ -2556,21 +2562,21 @@ parse_clpz(E, R, g(constrain_to_integer(E)), g(E = R)], g(integer(E)) => [g(R = E)], ?(E) => [g(must_be_fd_integer(E)), g(R = E)], - #(E) => [g(must_be_fd_integer(E)), g(R = E)], + #E => [g(must_be_fd_integer(E)), g(R = E)], m(A+B) => [p(pplus(A, B, R))], % power_var_num/3 must occur before */2 to be useful g(power_var_num(E, V, N)) => [p(pexp(V, N, R))], m(A*B) => [p(ptimes(A, B, R))], m(A-B) => [p(pplus(R,B,A))], m(-A) => [p(ptimes(-1,A,R))], - m(max(A,B)) => [g(A #=< ?(R)), g(B #=< R), p(pmax(A, B, R))], - m(min(A,B)) => [g(A #>= ?(R)), g(B #>= R), p(pmin(A, B, R))], + m(max(A,B)) => [g(A #=< #R), g(B #=< R), p(pmax(A, B, R))], + m(min(A,B)) => [g(A #>= #R), g(B #>= R), p(pmin(A, B, R))], m(A mod B) => [g(B #\= 0), p(pmod(A, B, R))], m(A rem B) => [g(B #\= 0), p(prem(A, B, R))], - m(abs(A)) => [g(?(R) #>= 0), p(pabs(A, R))], + m(abs(A)) => [g(#R #>= 0), p(pabs(A, R))], m(A/B) => [g(B #\= 0), p(prdiv(A, B, R))], m(A//B) => [g(B #\= 0), p(ptzdiv(A, B, R))], - m(A div B) => [g(?(R) #= (A - (A mod B)) // B)], + m(A div B) => [g(#R #= (A - (A mod B)) // B)], m(A^B) => [p(pexp(A, B, R))], m(sign(A)) => [g(R in -1..1), p(psign(A, R))], % bitwise operations @@ -2614,7 +2620,7 @@ parse_matcher(E, R, Matcher, Clause) :- parse_condition(g(Goal), E, E) --> [Goal, !]. parse_condition(?(E), _, ?(E)) --> [!]. -parse_condition(#(E), _, #(E)) --> [!]. +parse_condition(#E, _, #E) --> [!]. parse_condition(m(Match), _, Match0) --> [!], { copy_term(Match, Match0), @@ -2678,7 +2684,7 @@ clear_queue(queue(Goals,Fast,Slow,Aux)) :- put_atts(Goals, -queue(_,_)), put_atts(Fast, -queue(_,_)), put_atts(Slow, -queue(_,_)), - put_atts(Aux, -enabled(_)). + put_atts(Aux, -disabled). collect_goal(Qs) --> collect_arg(Qs, 1). collect_fast(Qs) --> collect_arg(Qs, 2). @@ -2765,7 +2771,7 @@ matches([ m_c(any(X) #>= any(Y), left_right_linsum_const(X, Y, Cs, Vs, Const)) => [g(( Cs = [1], Vs = [A] -> geq(A, Const) ; Cs = [-1], Vs = [A] -> Const1 is -Const, geq(Const1, A) - ; Cs = [1,1], Vs = [A,B] -> ?(A) + ?(B) #= ?(S), geq(S, Const) + ; Cs = [1,1], Vs = [A,B] -> #A + #B #= #S, geq(S, Const) ; Cs = [1,-1], Vs = [A,B] -> ( Const =:= 0 -> geq(A, B) ; C1 is -Const, @@ -2777,13 +2783,13 @@ matches([ propagator_init_trigger(x_leq_y_plus_c(A, B, C1)) ) ; Cs = [-1,-1], Vs = [A,B] -> - ?(A) + ?(B) #= ?(S), Const1 is -Const, geq(Const1, S) + #A + #B #= #S, Const1 is -Const, geq(Const1, S) ; scalar_product_(#>=, Cs, Vs, Const) ))], m(any(X) - any(Y) #>= integer(C)) => [d(X, X1), d(Y, Y1), g(C1 is -C), p(x_leq_y_plus_c(Y1, X1, C1))], m(integer(X) #>= any(Z) + integer(A)) => [g(C is X - A), r(C, Z)], m(abs(any(X)-any(Y)) #>= any(Z)) => - [d(X, X1), d(Y, Y1), d(Z, Z1), g((abs(?(A))#= ?(B),Y1+A#=X1,Z1#== integer(I)) => [d(X, RX), g((I>0 -> I1 is -I, RX in inf..I1 \/ I..sup; true))], m(integer(I) #>= abs(any(X))) => [d(X, RX), g(I>=0), g(I1 is -I), g(RX in I1..I)], m(any(X) #>= any(Y)) => [d(X, RX), d(Y, RY), g(geq(RX, RY))], @@ -2874,7 +2880,7 @@ matcher(m_c(Matcher,Cond), Gs) --> ). match(any(A), T) --> [A = T]. -match(var(V), T) --> [( nonvar(T), ( T = ?(Var) ; T = #(Var) ) -> +match(var(V), T) --> [( nonvar(T), ( T = ?(Var) ; T = #Var ) -> must_be_fd_integer(Var), V = Var ; v_or_i(T), V = T )]. @@ -2902,27 +2908,27 @@ match_goal(p(Prop), _) --> -%% ?X #>= ?Y +%% #>=(?X, ?Y) % -% Same as Y #=< X. When reasoning over integers, replace >=/2 by #>=/2 +% Same as Y #=< X. When reasoning over integers, replace (>=)/2 by (#>=)/2 % to obtain more general relations. X #>= Y :- clpz_geq(X, Y). clpz_geq(X, Y) :- clpz_geq_(X, Y), reinforce(X), reinforce(Y). -%% ?X #=< ?Y +%% #=<(?X, ?Y) % % The arithmetic expression X is less than or equal to Y. When -% reasoning over integers, replace == X. -%% ?X #= ?Y +%% #=(?X, ?Y) % % The arithmetic expression X equals Y. When reasoning over integers, -% replace is/2 by #=/2 to obtain more general relations. +% replace `(is)/2` by `(#=)/2` to obtain more general relations. X #= Y :- clpz_equal(X, Y). @@ -2937,7 +2943,7 @@ expr_conds(E, E) --> [integer(E)], { var(E), !, \+ monotonic }. expr_conds(E, E) --> { integer(E) }. expr_conds(?(E), E) --> [integer(E)]. -expr_conds(#(E), E) --> [integer(E)]. +expr_conds(#E, E) --> [integer(E)]. expr_conds(-E0, -E) --> expr_conds(E0, E). expr_conds(abs(E0), abs(E)) --> expr_conds(E0, E). expr_conds(A0+B0, A+B) --> expr_conds(A0, A), expr_conds(B0, B). @@ -2962,8 +2968,8 @@ expr_conds(A0>>B0, A>>B) --> expr_conds(A0, A), expr_conds(B0, B). expr_conds(A0/\B0, A/\B) --> expr_conds(A0, A), expr_conds(B0, B). expr_conds(A0\/B0, A\/B) --> expr_conds(A0, A), expr_conds(B0, B). expr_conds(xor(A0,B0), xor(A,B)) --> expr_conds(A0, A), expr_conds(B0, B). -expr_conds(lsb(A0), lsb(A)) --> expr_conds(A0, A). -expr_conds(msb(A0), msb(A)) --> expr_conds(A0, A). +% expr_conds(lsb(A0), lsb(A)) --> expr_conds(A0, A). +% expr_conds(msb(A0), msb(A)) --> expr_conds(A0, A). expr_conds(popcount(A0), Count) --> expr_conds(A0, A), [I is A, arithmetic:popcount(I, Count)]. @@ -3118,7 +3124,7 @@ user:goal_expansion(Goal0, Goal) :- linsum(X, S, S) --> { var(X), !, non_monotonic(X) }, [vn(X,1)]. linsum(I, S0, S) --> { integer(I), S is S0 + I }. linsum(?(X), S, S) --> { must_be_fd_integer(X) }, [vn(X,1)]. -linsum(#(X), S, S) --> { must_be_fd_integer(X) }, [vn(X,1)]. +linsum(#X, S, S) --> { must_be_fd_integer(X) }, [vn(X,1)]. linsum(-A, S0, S) --> mulsum(A, -1, S0, S). linsum(N*A, S0, S) --> { integer(N) }, !, mulsum(A, N, S0, S). linsum(A*N, S0, S) --> { integer(N) }, !, mulsum(A, N, S0, S). @@ -3272,10 +3278,10 @@ integer_kroot_leq(L, U, N, K, R) :- ) ). -%% ?X #\= ?Y +%% #\=(?X, ?Y) % % The arithmetic expressions X and Y evaluate to distinct integers. -% When reasoning over integers, replace =\=/2 by #\=/2 to obtain more +% When reasoning over integers, replace (=\=)/2 by (#\=)/2 to obtain more % general relations. X #\= Y :- clpz_neq(X, Y), do_queue. @@ -3303,7 +3309,7 @@ neq_num(X, N) --> ). -%% ?X #> ?Y +%% #>(?X, ?Y) % % Same as Y #< X. @@ -3312,14 +3318,14 @@ X #> Y :- X #>= Y + 1. %% #<(?X, ?Y) % % The arithmetic expression X is less than Y. When reasoning over -% integers, replace Y :- X #>= Y + 1. % Ms = [ pair(1, 2)-pair(3, 4), % pair(1, 3)-pair(2, 4), % pair(1, 4)-pair(2, 3)]. -% == +% ``` X #< Y :- Y #> X. -%% #\ +Q +%% #\(+Q) % % The reifiable constraint Q does _not_ hold. For example, to obtain % the complement of a domain: % -% == +% ``` % ?- #\ X in -3..0\/10..80. % X in inf.. -4\/1..9\/81..sup. -% == +% ``` #\ Q :- reify(Q, 0), do_queue. -%% ?P #<==> ?Q +%% #<==>(?P, ?Q) % % P and Q are equivalent. For example: % -% == +% ``` % ?- X #= 4 #<==> B, X #\= 4. % B = 0, % X in inf..3\/5..sup. -% == +% ``` % The following example uses reified constraints to relate a list of % finite domain variables to the number of occurrences of a given value: % -% == +% ``` % vs_n_num(Vs, N, Num) :- % maplist(eq_b(N), Vs, Bs), % sum(Bs, #=, Num). % % eq_b(X, Y, B) :- X #= Y #<==> B. -% == +% ``` % % Sample queries and their results: % -% == +% ``` % ?- Vs = [X,Y,Z], Vs ins 0..1, vs_n_num(Vs, 4, Num). % Vs = [X, Y, Z], % Num = 0, @@ -3377,11 +3383,11 @@ X #< Y :- Y #> X. % X = 2, % Y = 2, % Z = 2. -% == +% ``` L #<==> R :- reify(L, B), reify(R, B), do_queue. -%% ?P #==> ?Q +%% #==>(?P, ?Q) % % P implies Q. @@ -3404,13 +3410,13 @@ L #==> R :- append(LPs, RPs, Ps), propagator_init_trigger([LB,RB], pimpl(LB,RB,Ps)). -%% ?P #<== ?Q +%% #<==(?P, ?Q) % % Q implies P. L #<== R :- R #==> L. -%% ?P #/\ ?Q +%% #/\(?P, ?Q) % % P and Q hold. @@ -3439,19 +3445,19 @@ conjunctive_neqs_vals(A #/\ B) --> conjunctive_neqs_vals(A), conjunctive_neqs_vals(B). -%% ?P #\/ ?Q +%% #\/(?P, ?Q) % % P or Q holds. For example, the sum of natural numbers below 1000 % that are multiples of 3 or 5: % -% == +% ``` % ?- findall(N, (N mod 3 #= 0 #\/ N mod 5 #= 0, N in 0..999, % indomain(N)), % Ns), % sum(Ns, #=, Sum). % Ns = [0, 3, 5, 6, 9, 10, 12, 15, 18|...], % Sum = 233168. -% == +% ``` L #\/ R :- ( disjunctive_eqs_var_drep(L #\/ R, Var, Drep) -> Var in Drep @@ -3483,7 +3489,7 @@ disjunctive_eqs_vals(A #\/ B) --> disjunctive_eqs_vals(A), disjunctive_eqs_vals(B). -%% ?P #\ ?Q +%% #\(?P, ?Q) % % Either P holds or Q holds, but not both. @@ -3509,9 +3515,12 @@ L #\ R :- (L #\/ R) #/\ #\ (L #/\ R). undefined, created auxiliary constraints are killed, and the "clpz" attribute is removed from auxiliary variables. - For (/)/2, mod/2 and rem/2, we create a skeleton propagator and + For mod/2, div/2, rem/2 etc. we create a skeleton propagator and remember it as an auxiliary constraint. The pskeleton propagator can use the skeleton when the constraint is defined. + + We cannot use a skeleton propagator for (/)/2, since (/)/2 can + fail in cases such as 0 #==> X #= 1/2, where we expect success. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ parse_reified(E, R, D, @@ -3520,15 +3529,15 @@ parse_reified(E, R, D, g(constrain_to_integer(E)), g(R = E), g(D=1)], g(integer(E)) => [g(R=E), g(D=1)], ?(E) => [g(must_be_fd_integer(E)), g(R=E), g(D=1)], - #(E) => [g(must_be_fd_integer(E)), g(R=E), g(D=1)], + #E => [g(must_be_fd_integer(E)), g(R=E), g(D=1)], m(A+B) => [d(D), p(pplus(A,B,R)), a(A,B,R)], m(A*B) => [d(D), p(ptimes(A,B,R)), a(A,B,R)], m(A-B) => [d(D), p(pplus(R,B,A)), a(A,B,R)], m(-A) => [d(D), p(ptimes(-1,A,R)), a(R)], m(max(A,B)) => [d(D), p(pgeq(R, A)), p(pgeq(R, B)), p(pmax(A,B,R)), a(A,B,R)], m(min(A,B)) => [d(D), p(pgeq(A, R)), p(pgeq(B, R)), p(pmin(A,B,R)), a(A,B,R)], - m(abs(A)) => [g(?(R)#>=0), d(D), p(pabs(A, R)), a(A,R)], - m(A/B) => [skeleton(A,B,D,R,prdiv)], + m(abs(A)) => [g(#R#>=0), d(D), p(pabs(A, R)), a(A,R)], + m(A/B) => [p(preified_slash(A,B,D,R)), a(A,B,R)], m(A//B) => [skeleton(A,B,D,R,ptzdiv)], m(A div B) => [skeleton(A,B,D,R,pdiv)], m(A mod B) => [skeleton(A,B,D,R,pmod)], @@ -3536,14 +3545,15 @@ parse_reified(E, R, D, m(A^B) => [d(D), p(pexp(A,B,R)), a(A,B,R)], % bitwise operations m(\A) => [function(D,\,A,R)], - m(msb(A)) => [function(D,msb,A,R)], - m(lsb(A)) => [function(D,lsb,A,R)], + m(msb(A)) => [g(#A#>0) ,function(D,msb,A,R)], + m(lsb(A)) => [g(#A#>0), function(D,lsb,A,R)], m(popcount(A)) => [function(D,popcount,A,R)], + m(sign(A)) => [function(D,sign,A,R)], m(A< [function(D,<<,A,B,R)], m(A>>B) => [function(D,>>,A,B,R)], m(A/\B) => [function(D,/\,A,B,R)], m(A\/B) => [function(D,\/,A,B,R)], - m(xor(A, B)) => [skeleton(A,B,D,R,pxor)], + m(xor(A, B)) => [function(D,xor,A,B,R)], g(true) => [g(domain_error(clpz_expression, E))]] ). @@ -3573,7 +3583,7 @@ parse_reified(E, R, D, Matcher, Clause) :- reified_condition(g(Goal), E, E, []) --> [{Goal}, !]. reified_condition(?(E), _, ?(E), []) --> [!]. -reified_condition(#(E), _, #(E), []) --> [!]. +reified_condition(#E, _, #E, []) --> [!]. reified_condition(m(Match), _, Match0, Ds) --> [!], { copy_term(Match, Match0), @@ -3637,7 +3647,7 @@ reify(Expr, B, Ps) :- reifiable(E) :- var(E), non_monotonic(E). reifiable(E) :- integer(E), E in 0..1. reifiable(?(E)) :- must_be_fd_integer(E). -reifiable(#(E)) :- must_be_fd_integer(E). +reifiable(#E) :- must_be_fd_integer(E). reifiable(V in _) :- fd_variable(V). reifiable(Expr) :- Expr =.. [Op,Left,Right], @@ -3658,7 +3668,7 @@ reify(E, B) --> { B in 0..1 }, reify_(E, B). reify_(E, B) --> { var(E), !, E = B }. reify_(E, B) --> { integer(E), E = B }. reify_(?(B), B) --> []. -reify_(#(B), B) --> []. +reify_(#B, B) --> []. reify_(V in Drep, B) --> { drep_to_domain(Drep, Dom) }, propagator_init_trigger(reified_in(V,Dom,B)), @@ -3667,7 +3677,7 @@ reify_(tuples_in(Tuples, Relation), B) --> { maplist(relation_tuple_b_prop(Relation), Tuples, Bs, Ps), maplist(monotonic, Bs, Bs1), fold_statement(conjunction, Bs1, And), - ?(B) #<==> And }, + #B #<==> And }, propagator_init_trigger([B], tuples_not_in(Tuples, Relation, B)), kill_reified_tuples(Bs, Ps, Bs), list(Ps), @@ -3769,13 +3779,16 @@ conjunction(E, Conj, Conj #/\ E). disjunction(E, Disj, Disj #\/ E). -var_eq(V, N, ?(V) #= N). +var_eq(V, N, #V #= N). % Match variables to created skeleton. skeleton(Vs, Vs-Prop) :- - maplist(prop_init(Prop), Vs), - trigger_once(Prop). + ( propagator_state(Prop, State), State == dead -> + true + ; maplist(prop_init(Prop), Vs), + trigger_once(Prop) + ). /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - A drep is a user-accessible and visible domain representation. N, @@ -3933,10 +3946,7 @@ put_terminating(X, Dom, Ps) --> ) ). -new_queue(queue(Goals,Fast,Slow,_Aux)) :- - put_atts(Goals, +queue([],_)), - put_atts(Fast, +queue([],_)), - put_atts(Slow, +queue([],_)). +new_queue(queue(_Goals,_Fast,_Slow,_Aux)). queue_goal(Goal) --> insert_queue(Goal, 1). queue_fast(Prop) --> insert_queue(Prop, 2). @@ -3945,11 +3955,10 @@ queue_slow(Prop) --> insert_queue(Prop, 3). insert_queue(Element, Which) --> state(Queue), { arg(Which, Queue, Arg), - get_atts(Arg, queue(Head0,Tail0)), - ( Head0 == [] -> - Head = [Element|Tail] - ; Head = Head0, + ( get_atts(Arg, queue(Head0,Tail0)) -> + Head = Head0, Tail0 = [Element|Tail] + ; Head = [Element|Tail] ), put_atts(Arg, +queue(Head,Tail)) }. @@ -4177,11 +4186,15 @@ do_queue --> ; true ). +:- meta_predicate(ignore(0)). + +ignore(Goal) :- ( Goal -> true ; true ). + print_queue --> state(queue(Goal,Fast,Slow,_)), - { get_atts(Goal, +queue(GHs,_)), - get_atts(Fast, +queue(FHs,_)), - get_atts(Slow, +queue(SHs,_)), + { ignore(get_atts(Goal, +queue(GHs,_))), + ignore(get_atts(Fast, +queue(FHs,_))), + ignore(get_atts(Slow, +queue(SHs,_))), format("Current queue:~n goal: ~q~n fast: ~q~n slow: ~q~n~n", [GHs,FHs,SHs]) }. @@ -4198,13 +4211,13 @@ queue_get_arg_(Queue, Which, Element) :- arg(Which, Queue, Arg), get_atts(Arg, +queue([Element|Elements],Tail)), ( var(Elements) -> - put_atts(Arg, +queue([],_)) + put_atts(Arg, -queue(_,_)) ; put_atts(Arg, +queue(Elements,Tail)) ). -queue_enabled --> state(queue(_,_,_,Aux)), { \+ get_atts(Aux, +enabled(false)) }. -disable_queue --> state(queue(_,_,_,Aux)), { put_atts(Aux, +enabled(false)) }. -enable_queue --> state(queue(_,_,_,Aux)), { put_atts(Aux, +enabled(true)) }. +queue_enabled --> state(queue(_,_,_,Aux)), { \+ get_atts(Aux, disabled) }. +disable_queue --> state(queue(_,_,_,Aux)), { put_atts(Aux, +disabled) }. +enable_queue --> state(queue(_,_,_,Aux)), { put_atts(Aux, -disabled) }. portray_propagator(propagator(P,_), F) :- functor(P, F, _). @@ -4271,7 +4284,7 @@ lex_chain_(Prop, Ls, Prev, Ls) :- lex_le([], []). lex_le([V1|V1s], [V2|V2s]) :- - ?(V1) #=< ?(V2), + #V1 #=< #V2, ( integer(V1) -> ( integer(V2) -> ( V1 =:= V2 -> lex_le(V1s, V2s) ; true ) @@ -4292,18 +4305,18 @@ lex_le([V1|V1s], [V2|V2s]) :- % example, if 1 is compatible with 2 and 5, and 4 is compatible with 0 % and 3: % -% == +% ``` % ?- tuples_in([[X,Y]], [[1,2],[1,5],[4,0],[4,3]]), X = 4. % X = 4, % Y in 0\/3. -% == +% ``` % % As another example, consider a train schedule represented as a list % of quadruples, denoting departure and arrival places and times for % each train. In the following program, Ps is a feasible journey of % length 3 from A to D via trains that are part of the given schedule. % -% == +% ``` % trains([[1,2,0,1], % [2,3,4,5], % [2,3,0,1], @@ -4317,14 +4330,14 @@ lex_le([V1|V1s], [V2|V2s]) :- % T4 #> T3, % trains(Ts), % tuples_in(Ps, Ts). -% == +% ``` % % In this example, the unique solution is found without labeling: % -% == +% ``` % ?- threepath(1, 4, Ps). % Ps = [[1, 2, 0, 1], [2, 3, 4, 5], [3, 4, 8, 9]]. -% == +% ``` tuples_in(Tuples, Relation) :- must_be(list(list), Tuples), @@ -4347,21 +4360,23 @@ list_first_rest([L|Ls], L, Ls). tuple_domain([], _) --> []. tuple_domain([T|Ts], Relation0) --> { maplist(list_first_rest, Relation0, Firsts, Relation1) }, - ( var(T) -> - ( Firsts = [Unique] -> T = Unique - ; { list_to_domain(Firsts, FDom), + ( Firsts = [Unique] -> T = Unique + ; ( var(T) -> + { list_to_domain(Firsts, FDom), fd_get(T, TDom, TPs), domains_intersection(TDom, FDom, TDom1) }, fd_put(T, TDom1, TPs) + ; [] ) - ; [] ), tuple_domain(Ts, Relation1). tuple_freeze(Tuple, Relation) :- - put_attr(R, clpz_relation, Relation), - make_propagator(rel_tuple(R, Tuple), Prop), - tuple_freeze_(Tuple, Prop). + ( ground(Tuple) -> memberchk(Tuple, Relation) + ; put_attr(R, clpz_relation, Relation), + make_propagator(rel_tuple(R, Tuple), Prop), + tuple_freeze_(Tuple, Prop) + ). tuple_freeze_([], _). tuple_freeze_([T|Ts], Prop) :- @@ -4480,19 +4495,25 @@ run_propagator(pgeq(A,B), MState) --> run_propagator(rel_tuple(R, Tuple), MState) --> { get_attr(R, clpz_relation, Relation) }, - ( { ground(Tuple) } -> kill(MState), { memberchk(Tuple, Relation) } + ( { ground(Tuple) } -> + kill(MState), + { del_attr(R, clpz_relation), + memberchk(Tuple, Relation) } ; { relation_unifiable(Relation, Tuple, Us, false, Changed), Us = [_|_] }, ( { Tuple = [First,Second], ( ground(First) ; ground(Second) ) } -> kill(MState) ; [] ), - ( { Us = [Single] } -> kill(MState), Single = Tuple + ( { Us = [Single] } -> + kill(MState), + { del_attr(R, clpz_relation) }, + Single = Tuple ; { Changed } -> - { put_attr(R, clpz_relation, Us), - disable_queue }, + { put_attr(R, clpz_relation, Us) }, + disable_queue, tuple_domain(Tuple, Us), - { enable_queue } + enable_queue ; [] ) ). @@ -4972,7 +4993,6 @@ run_propagator(ptzdiv(X,Y,Z), MState) --> run_propagator(pmod(X,Y,Z), MState) --> ( Y == 0 -> { false } ; Y == Z -> { false } - % ; nonvar(Y), Z == X -> true ; X == Y -> kill(MState), queue_goal(Z = 0) ; true ), @@ -4990,7 +5010,7 @@ run_propagator(pmod(X,Y,Z), MState) --> ), { fd_get(X, XD0, XPs), domain_remove_smaller_than(XD0, XMin, XD2) }, - fd_put(X, XD2, XPs) + fd_put(X, XD2, XPs) % queue_goal(X #>= XMin) ; true ), @@ -4998,12 +5018,10 @@ run_propagator(pmod(X,Y,Z), MState) --> XMax is Z + Y * ((XU - Z) div Y), { fd_get(X, XD1, XPs), domain_remove_greater_than(XD1, XMax, XD3) }, - fd_put(X, XD3, XPs) + fd_put(X, XD3, XPs) % queue_goal(X #=< XMax) ; true ) - % kill(MState), - % queue_goal(X #= Z + Y * _) % Add a variable to be efficient. ; nonvar(Z), nonvar(X) -> ( Z > 0 -> ( X < 0 -> true @@ -5023,13 +5041,13 @@ run_propagator(pmod(X,Y,Z), MState) --> { fd_get(Y, YD, YPs), YMin is Z + 1, domain_remove_smaller_than(YD, YMin, YD1) }, - fd_put(Y, YD1, YPs) + fd_put(Y, YD1, YPs) % queue_goal(Y #> Z) ; Z < 0 -> { fd_get(Y, YD, YPs), YMax is Z - 1, domain_remove_greater_than(YD, YMax, YD1) }, - fd_put(Y, YD1, YPs) + fd_put(Y, YD1, YPs) % queue_goal(Y #< Z) ; true ) @@ -5049,7 +5067,7 @@ run_propagator(pmodz(X,Y,Z), MState) --> queue_goal(Z = X) ; { fd_get(Z, ZD0, ZPs), domain_remove_greater_than(ZD0, X, ZD2) }, - fd_put(Z, ZD2, ZPs) + fd_put(Z, ZD2, ZPs) % queue_goal(Z #=< X) ) ; X < 0 -> @@ -5058,7 +5076,7 @@ run_propagator(pmodz(X,Y,Z), MState) --> queue_goal(Z = X) ; { fd_get(Z, ZD0, ZPs), domain_remove_smaller_than(ZD0, X, ZD2) }, - fd_put(Z, ZD2, ZPs) + fd_put(Z, ZD2, ZPs) % queue_goal(Z #>= X) ) ), @@ -5067,14 +5085,14 @@ run_propagator(pmodz(X,Y,Z), MState) --> { fd_get(Z, ZD1, ZPs), domain_remove_smaller_than(ZD1, 0, ZD3), domain_remove_greater_than(ZD3, ZMax, ZD5) }, - fd_put(Z, ZD5, ZPs) + fd_put(Z, ZD5, ZPs) % queue_goal(Z in 0..ZMax) ; { fd_get(Y, _, n(YL), n(YU), _), YU < 0 } -> ZMin is YL + 1, { fd_get(Z, ZD1, ZPs), domain_remove_greater_than(ZD1, 0, ZD3), domain_remove_smaller_than(ZD3, ZMin, ZD5) }, - fd_put(Z, ZD5, ZPs) + fd_put(Z, ZD5, ZPs) % queue_goal(Z in ZMin..0) ; true ) @@ -5089,7 +5107,7 @@ run_propagator(pmodz(X,Y,Z), MState) --> { fd_get(Z, ZD1, ZPs), domain_remove_greater_than(ZD1, 0, ZD3), domain_remove_smaller_than(ZD3, ZMin, ZD5) }, - fd_put(Z, ZD5, ZPs) + fd_put(Z, ZD5, ZPs) % queue_goal(Z in ZMin..0) ) ; Y > 0 -> @@ -5100,7 +5118,7 @@ run_propagator(pmodz(X,Y,Z), MState) --> { fd_get(Z, ZD1, ZPs), domain_remove_smaller_than(ZD1, 0, ZD3), domain_remove_greater_than(ZD3, ZMax, ZD5) }, - fd_put(Z, ZD5, ZPs) + fd_put(Z, ZD5, ZPs) % queue_goal(Z in 0..ZMax) ) ) @@ -5115,12 +5133,12 @@ run_propagator(pmodz(X,Y,Z), MState) --> ; ( { fd_get(X, _, n(XL), n(XU), _), XL >= 0 } -> { fd_get(Z, ZD0, ZPs), domain_remove_greater_than(ZD0, XU, ZD2) }, - fd_put(Z, ZD2, ZPs) + fd_put(Z, ZD2, ZPs) % queue_goal(Z #=< XU) ; { fd_get(X, _, n(XL), n(XU), _), XU =< 0 } -> { fd_get(Z, ZD0, ZPs), domain_remove_smaller_than(ZD0, XL, ZD2) }, - fd_put(Z, ZD2, ZPs) + fd_put(Z, ZD2, ZPs) % queue_goal(Z #>= XL) ; true ), @@ -5129,67 +5147,69 @@ run_propagator(pmodz(X,Y,Z), MState) --> { fd_get(Z, ZD1, ZPs), domain_remove_smaller_than(ZD1, 0, ZD3), domain_remove_greater_than(ZD3, ZMax, ZD5) }, - fd_put(Z, ZD5, ZPs) + fd_put(Z, ZD5, ZPs) % queue_goal(Z in 0..ZMax) ; { fd_get(Y, _, n(YL), n(YU), _), YU < 0 } -> ZMin is YL + 1, { fd_get(Z, ZD1, ZPs), domain_remove_greater_than(ZD1, 0, ZD3), domain_remove_smaller_than(ZD3, ZMin, ZD5) }, - fd_put(Z, ZD5, ZPs) + fd_put(Z, ZD5, ZPs) % queue_goal(Z in ZMin..0) - ; { fd_get(Y, _, n(YL), n(YU), _) } -> + ; { fd_get(Y, _, n(YL), n(YU), _), YL < 0, YU > 0 } -> ZMin is YL + 1, ZMax is YU - 1, { fd_get(Z, ZD1, ZPs), domain_remove_greater_than(ZD1, ZMax, ZD3), domain_remove_smaller_than(ZD3, ZMin, ZD5) }, - fd_put(Z, ZD5, ZPs) + fd_put(Z, ZD5, ZPs) % queue_goal(Z in ZMin..ZMax) ; { fd_get(Y, _, _, n(YU), _), YU > 0 } -> { fd_get(Z, ZD1, ZPs), ZMax is YU - 1, domain_remove_greater_than(ZD1, ZMax, ZD3) }, - fd_put(Z, ZD3, ZPs) + fd_put(Z, ZD3, ZPs) % queue_goal(Z #< YU) ; { fd_get(Y, _, n(YL), _, _), YL < 0 } -> { fd_get(Z, ZD1, ZPs), ZMin is YL + 1, domain_remove_smaller_than(ZD1, ZMin, ZD3) }, - fd_put(Z, ZD3, ZPs) + fd_put(Z, ZD3, ZPs) % queue_goal(Z #> YL) ; true ) ) ). -run_propagator(pmody(X,Y,Z), MState) --> +run_propagator(pmody(_X,Y,Z), _MState) --> ( nonvar(Y) -> true % Nothing to do. % ; nonvar(X) -> true ; nonvar(Z) -> - ( Z > 0 -> % queue_goal(Y #> Z) + ( Z > 0 -> { fd_get(Y, YD, YPs), YMin is Z + 1, domain_remove_smaller_than(YD, YMin, YD1) }, - fd_put(Y, YD1, YPs) - ; Z < 0 -> % queue_goal(Y #< Z) + fd_put(Y, YD1, YPs) + % queue_goal(Y #> Z) + ; Z < 0 -> { fd_get(Y, YD, YPs), YMax is Z - 1, domain_remove_greater_than(YD, YMax, YD1) }, - fd_put(Y, YD1, YPs) - ; Z =:= 0 -> kill(MState), queue_goal(X / Y #= _) + fd_put(Y, YD1, YPs) + % queue_goal(Y #< Z) + ; Z =:= 0 % Multiple solutions so do nothing special. ) ; ( { fd_get(Z, _, n(ZL), _, _), ZL > 0 } -> { fd_get(Y, YD, YPs), YMin is ZL + 1, domain_remove_smaller_than(YD, YMin, YD1) }, - fd_put(Y, YD1, YPs) + fd_put(Y, YD1, YPs) % queue_goal(Y #> ZL) ; { fd_get(Z, _, _, n(ZU), _), ZU < 0 } -> { fd_get(Y, YD, YPs), YMax is ZU - 1, domain_remove_greater_than(YD, YMax, YD1) }, - fd_put(Y, YD1, YPs) + fd_put(Y, YD1, YPs) % queue_goal(Y #< ZU) ; true ) @@ -5679,8 +5699,11 @@ run_propagator(pfunction(Op,A,B,R), MState) --> run_propagator(pfunction(Op,A,R), MState) --> ( integer(A) -> kill(MState), - Expr =.. [Op,A], - R is Expr + ( Op == msb -> { msb(A, R) } + ; Op == lsb -> { lsb(A, R) } + ; Expr =.. [Op,A], + R is Expr + ) ; [] ). @@ -5832,6 +5855,30 @@ run_propagator(pimpl(X, Y, Ps), MState) --> ; [] ). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +run_propagator(preified_slash(X, Y, D, R), MState) --> + ( Y == 0 -> + kill(MState), + D = 0 + ; Y == 1 -> + kill(MState), + D = 1, + R = X + ; nonvar(X), + nonvar(Y) -> + kill(MState), + ( X mod Y =:= 0 -> + D = 1, + R is X // Y + ; D = 0 + ) + ; D == 1 -> + kill(MState), + queue_goal(X/Y #= R) + ; [] + ). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -5854,6 +5901,11 @@ in_(L, U, X) :- fd_put(X, NXD, XPs). min_max_factor(L1, U1, L2, U2, L3, U3, Min, Max) :- + % use findall/3 to forget auxiliary constraints that are only + % needed temporarily for reasoning about domain boundaries + findall(Min-Max, min_max_factor_(L1, U1, L2, U2, L3, U3, Min, Max), [Min-Max]). + +min_max_factor_(L1, U1, L2, U2, L3, U3, Min, Max) :- ( U1 cis_lt n(0), L2 cis_lt n(0), U2 cis_gt n(0), L3 cis_lt n(0), U3 cis_gt n(0) -> @@ -6124,8 +6176,7 @@ with_local_attributes(Vars, Goal, Result) :- % we made during propagation, and unify the variables % in the thrown copy with Vars in order to get the % intended variables in Result. - asserta(nat_copy(Vars-Result)), - retract(nat_copy(Copy)), + copy_term_nat(Vars-Result, Copy), throw(local_attributes(Copy))), local_attributes(Vars-Result), true). @@ -6420,24 +6471,24 @@ num_subsets([S|Ss], Dom, Num0, Num, NonSubs) :- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% serialized(+Starts, +Durations) +%% serialized(+Starts, +Durations) % -% Describes a set of non-overlapping tasks. -% Starts = [S_1,...,S_n], is a list of variables or integers, -% Durations = [D_1,...,D_n] is a list of non-negative integers. -% Constrains Starts and Durations to denote a set of -% non-overlapping tasks, i.e.: S_i + D_i =< S_j or S_j + D_j =< -% S_i for all 1 =< i < j =< n. Example: +% Describes a set of non-overlapping tasks. +% Starts = [S_1,...,S_n], is a list of variables or integers, +% Durations = [D_1,...,D_n] is a list of non-negative integers. +% Constrains Starts and Durations to denote a set of +% non-overlapping tasks, i.e.: S_i + D_i =< S_j or S_j + D_j =< +% S_i for all 1 =< i < j =< n. Example: % -% == -% ?- length(Vs, 3), -% Vs ins 0..3, -% serialized(Vs, [1,2,3]), -% label(Vs). -% Vs = [0, 1, 3] ; -% Vs = [2, 0, 3] ; -% false. -% == +% ``` +% ?- length(Vs, 3), +% Vs ins 0..3, +% serialized(Vs, [1,2,3]), +% label(Vs). +% Vs = [0,1,3] +% ; Vs = [2,0,3] +% ; false. +% ``` % % @see Dorndorf et al. 2000, "Constraint Propagation Techniques for the % Disjunctive Scheduling Problem" @@ -6517,10 +6568,10 @@ serialize_upper_bound(I, D_I, J, D_J, MState) --> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% element(?N, +Vs, ?V) +%% element(?N, +Vs, ?V) % -% The N-th element of the list of finite domain variables Vs is V. -% Analogous to nth1/3. +% The N-th element of the list of finite domain variables Vs is V. +% Analogous to nth1/3. element(N, Is, V) :- must_be(list, Is), @@ -6536,7 +6587,7 @@ element_domain(V, VD) :- element_([], _, _, _). element_([I|Is], N0, N, V) :- - ?(I) #\= ?(V) #==> ?(N) #\= N0, + #I #\= #V #==> #N #\= N0, N1 is N0 + 1, element_(Is, N1, N, V). @@ -6552,38 +6603,39 @@ integers_remaining([V|Vs], N0, Dom, D0, D) :- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% global_cardinality(+Vs, +Pairs) +%% global_cardinality(+Vs, +Pairs) % -% Global Cardinality constraint. Equivalent to -% global_cardinality(Vs, Pairs, []). Example: +% Global Cardinality constraint. Equivalent to +% `global_cardinality(Vs, Pairs, [])`. Example: % -% == -% ?- Vs = [_,_,_], global_cardinality(Vs, [1-2,3-_]), label(Vs). -% Vs = [1, 1, 3] ; -% Vs = [1, 3, 1] ; -% Vs = [3, 1, 1]. -% == +% ``` +% ?- Vs = [_,_,_], global_cardinality(Vs, [1-2,3-_]), label(Vs). +% Vs = [1,1,3] +% ; Vs = [1,3,1] +% ; Vs = [3,1,1] +% ; false. +% ``` global_cardinality(Xs, Pairs) :- global_cardinality(Xs, Pairs, []). -%% global_cardinality(+Vs, +Pairs, +Options) +%% global_cardinality(+Vs, +Pairs, +Options) % -% Global Cardinality constraint. Vs is a list of finite domain -% variables, Pairs is a list of Key-Num pairs, where Key is an -% integer and Num is a finite domain variable. The constraint holds -% iff each V in Vs is equal to some key, and for each Key-Num pair -% in Pairs, the number of occurrences of Key in Vs is Num. Options -% is a list of options. Supported options are: +% Global Cardinality constraint. Vs is a list of finite domain +% variables, Pairs is a list of Key-Num pairs, where Key is an +% integer and Num is a finite domain variable. The constraint holds +% iff each V in Vs is equal to some key, and for each Key-Num pair +% in Pairs, the number of occurrences of Key in Vs is Num. Options +% is a list of options. Supported options are: % -% * consistency(value) -% A weaker form of consistency is used. +% `consistency(value)` +% A weaker form of consistency is used. % -% * cost(Cost, Matrix) -% Matrix is a list of rows, one for each variable, in the order -% they occur in Vs. Each of these rows is a list of integers, one -% for each key, in the order these keys occur in Pairs. When -% variable v_i is assigned the value of key k_j, then the -% associated cost is Matrix_{ij}. Cost is the sum of all costs. +% `cost(Cost, Matrix)` +% Matrix is a list of rows, one for each variable, in the order +% they occur in Vs. Each of these rows is a list of integers, one +% for each key, in the order these keys occur in Pairs. When +% variable v\_i is assigned the value of key k\_j, then the +% associated cost is Matrix\_{ij}. Cost is the sum of all costs. global_cardinality(Xs, Pairs, Options) :- must_be(list(list), [Xs,Pairs,Options]), @@ -6935,21 +6987,22 @@ all_neq([X|Xs], C) :- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% circuit(+Vs) +%% circuit(+Vs) % -% True iff the list Vs of finite domain variables induces a -% Hamiltonian circuit. The k-th element of Vs denotes the -% successor of node k. Node indexing starts with 1. Examples: +% True iff the list Vs of finite domain variables induces a +% Hamiltonian circuit. The k-th element of Vs denotes the +% successor of node k. Node indexing starts with 1. Examples: % -% == -% ?- length(Vs, _), circuit(Vs), label(Vs). -% Vs = [] ; -% Vs = [1] ; -% Vs = [2, 1] ; -% Vs = [2, 3, 1] ; -% Vs = [3, 1, 2] ; -% Vs = [2, 3, 4, 1] . -% == +% ``` +% ?- length(Vs, _), circuit(Vs), label(Vs). +% Vs = [] +% ; Vs = [1] +% ; Vs = [2,1] +% ; Vs = [2,3,1] +% ; Vs = [3,1,2] +% ; Vs = [2,3,4,1] +% ; ... . +% ``` circuit(Vs) :- must_be(list, Vs), @@ -7036,21 +7089,21 @@ cumulative(Tasks) :- cumulative(Tasks, [limit(1)]). % For example, given the following predicate that relates three tasks % of durations 2 and 3 to a list containing their starting times: % -% == +% ``` % tasks_starts(Tasks, [S1,S2,S3]) :- % Tasks = [task(S1,3,_,1,_), % task(S2,2,_,1,_), % task(S3,2,_,1,_)]. -% == +% ``` % % We can use cumulative/2 as follows, and obtain a schedule: % -% == +% ``` % ?- tasks_starts(Tasks, Starts), Starts ins 0..10, % cumulative(Tasks, [limit(2)]), label(Starts). % Tasks = [task(0, 3, 3, 1, _G36), task(0, 2, 2, 1, _G45), ...], % Starts = [0, 0, 2] . -% == +% ``` cumulative(Tasks, Options) :- must_be(list(list), [Tasks,Options]), @@ -7080,25 +7133,25 @@ cumulative(Tasks, Options) :- fully_elastic_relaxation(Tasks, Limit) :- maplist(task_duration_consumption, Tasks, Ds, Cs), maplist(area, Ds, Cs, As), - sum(As, #=, ?(Area)), - ?(MinTime) #= (Area + Limit - 1) // Limit, + sum(As, #=, #Area), + #MinTime #= (Area + Limit - 1) // Limit, tasks_minstart_maxend(Tasks, MinStart, MaxEnd), MaxEnd #>= MinStart + MinTime. task_duration_consumption(task(_,D,_,C,_), D, C). -area(X, Y, Area) :- ?(Area) #= ?(X) * ?(Y). +area(X, Y, Area) :- #Area #= #X * #Y. tasks_minstart_maxend(Tasks, Start, End) :- maplist(task_start_end, Tasks, [Start0|Starts], [End0|Ends]), foldl(min_, Starts, Start0, Start), foldl(max_, Ends, End0, End). -max_(E, M0, M) :- ?(M) #= max(E, M0). +max_(E, M0, M) :- #M #= max(E, M0). -min_(E, M0, M) :- ?(M) #= min(E, M0). +min_(E, M0, M) :- #M #= min(E, M0). -task_start_end(task(Start,_,End,_,_), ?(Start), ?(End)). +task_start_end(task(Start,_,End,_,_), #Start, #End). /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - All time slots must respect the resource limit. @@ -7113,8 +7166,8 @@ resource_limit(T0, T, Tasks, Bss, L) :- task_bs(Task, InfStart-Bs) :- Task = task(Start,D,End,_,_Id), - ?(D) #> 0, - ?(End) #= ?(Start) + ?(D), + #D #> 0, + #End #= #Start + #D, maplist(finite_domain, [End,Start,D]), fd_inf(Start, InfStart), fd_sup(End, SupEnd), @@ -7124,20 +7177,20 @@ task_bs(Task, InfStart-Bs) :- task_running([], _, _, _). task_running([B|Bs], Start, End, T) :- - ((T #>= Start) #/\ (T #< End)) #<==> ?(B), + ((T #>= Start) #/\ (T #< End)) #<==> #B, T1 is T + 1, task_running(Bs, Start, End, T1). contribution_at(T, Task, Offset-Bs, Contribution) :- Task = task(Start,_,End,C,_), - ?(C) #>= 0, + #C #>= 0, fd_inf(Start, InfStart), fd_sup(End, SupEnd), ( T < InfStart -> Contribution = 0 ; T >= SupEnd -> Contribution = 0 ; Index is T - Offset, nth0(Index, Bs, B), - ?(Contribution) #= B*C + #Contribution #= B*C ). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -7165,10 +7218,10 @@ non_overlapping_(A, B) :- a_not_in_b(B, A). a_not_in_b([_,AX,AW,AY,AH], [_,BX,BW,BY,BH]) :- - ?(AX) #=< ?(BX) #/\ ?(BX) #< ?(AX) + ?(AW) #==> - ?(AY) + ?(AH) #=< ?(BY) #\/ ?(BY) + ?(BH) #=< ?(AY), - ?(AY) #=< ?(BY) #/\ ?(BY) #< ?(AY) + ?(AH) #==> - ?(AX) + ?(AW) #=< ?(BX) #\/ ?(BX) + ?(BW) #=< ?(AX). + #AX #=< #BX #/\ #BX #< #AX + #AW #==> + #AY + #AH #=< #BY #\/ #BY + #BH #=< #AY, + #AY #=< #BY #/\ #BY #< #AY + #AH #==> + #AX + #AW #=< #BX #\/ #BX + #BW #=< #AX. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -7180,22 +7233,23 @@ a_not_in_b([_,AX,AW,AY,AH], [_,BX,BW,BY,BH]) :- % example, a list of binary finite domain variables is constrained to % contain at least two consecutive ones: % -% == -% two_consecutive_ones(Vs) :- -% automaton(Vs, [source(a),sink(c)], -% [arc(a,0,a), arc(a,1,b), -% arc(b,0,a), arc(b,1,c), -% arc(c,0,c), arc(c,1,c)]). -% == +% ``` +% two_consecutive_ones(Vs) :- +% automaton(Vs, [source(a),sink(c)], +% [arc(a,0,a), arc(a,1,b), +% arc(b,0,a), arc(b,1,c), +% arc(c,0,c), arc(c,1,c)]). +% ``` % % Example query: % -% == -% ?- length(Vs, 3), two_consecutive_ones(Vs), label(Vs). -% Vs = [0, 1, 1] ; -% Vs = [1, 1, 0] ; -% Vs = [1, 1, 1]. -% == +% ``` +% ?- length(Vs, 3), two_consecutive_ones(Vs), label(Vs). +% Vs = [0,1,1] +% ; Vs = [1,1,0] +% ; Vs = [1,1,1] +% ; false. +% ``` automaton(Sigs, Ns, As) :- automaton(_, _, Sigs, Ns, As, [], [], _). @@ -7231,7 +7285,7 @@ automaton(Sigs, Ns, As) :- automaton(_, _, Sigs, Ns, As, [], [], _). % number of inflexions, which are switches between strictly ascending % and strictly descending subsequences: % -% == +% ``` % sequence_inflexions(Vs, N) :- % variables_signature(Vs, Sigs), % automaton(Sigs, _, Sigs, @@ -7252,11 +7306,11 @@ automaton(Sigs, Ns, As) :- automaton(_, _, Sigs, Ns, As, [], [], _). % Prev #< V #<==> S #= 1, % Prev #> V #<==> S #= 2, % variables_signature_(Vs, V, Sigs). -% == +% ``` % % Example queries: % -% == +% ``` % ?- sequence_inflexions([1,2,3,3,2,1,3,0], N). % N = 3. % @@ -7264,7 +7318,7 @@ automaton(Sigs, Ns, As) :- automaton(_, _, Sigs, Ns, As, [], [], _). % sequence_inflexions(Ls, 3), label(Ls). % Ls = [0, 1, 0, 1, 0] ; % Ls = [1, 0, 1, 0, 1]. -% == +% ``` template_var_path(V, Var, []) :- var(V), !, V == Var. template_var_path(T, Var, [N|Ns]) :- @@ -7325,7 +7379,7 @@ exprs_values([E0|Es], [V|Vs]) --> { term_variables(E0, EVs0), copy_term(E0, E), term_variables(E, EVs), - ?(V) #= E }, + #V #= E }, match_variables(EVs0, EVs), exprs_values(Es, Vs). @@ -7375,7 +7429,7 @@ source(source(_)). sink(sink(_)). -monotonic(Var, ?(Var)). +monotonic(Var, #Var). arc_normalized(Cs, Arc0, Arc) :- arc_normalized_(Arc0, Cs, Arc). @@ -7392,7 +7446,7 @@ arc_normalized_(arc(S0,L,S), Cs, arc(S0,L,S,Cs)). % deterministic while preserving their generality and completeness. % For example: % -% == +% ``` % n_factorial(N, F) :- % zcompare(C, N, 0), % n_factorial_(C, N, F). @@ -7401,27 +7455,27 @@ arc_normalized_(arc(S0,L,S), Cs, arc(S0,L,S,Cs)). % n_factorial_(>, N, F) :- % F #= F0*N, N1 #= N - 1, % n_factorial(N1, F0). -% == +% ``` % % This version is deterministic if the first argument is instantiated, % because first argument indexing can distinguish the two different % clauses: % -% == +% ``` % ?- n_factorial(30, F). -% F = 265252859812191058636308480000000. -% == +% F = 265252859812191058636308480000000. +% ``` % % The predicate can still be used in all directions, including the % most general query: % -% == +% ``` % ?- n_factorial(N, F). -% N = 0, -% F = 1 ; -% N = F, F = 1 ; -% N = F, F = 2 . -% == +% N = 0, F = 1 +% ; N = 1, F = 1 +% ; N = 2, F = 2 +% ; ... . +% ``` zcompare(Order, A, B) :- ( nonvar(Order) -> @@ -7434,9 +7488,9 @@ zcompare(Order, A, B) :- propagator_init_trigger([A,B], pzcompare(Order, A, B)) ). -zcompare_(=, A, B) :- ?(A) #= ?(B). -zcompare_(<, A, B) :- ?(A) #< ?(B). -zcompare_(>, A, B) :- ?(A) #> ?(B). +zcompare_(=, A, B) :- #A #= #B. +zcompare_(<, A, B) :- #A #< #B. +zcompare_(>, A, B) :- #A #> #B. %% chain(+Relation, +Zs) % @@ -7445,11 +7499,11 @@ zcompare_(>, A, B) :- ?(A) #> ?(B). % Relation, in the order they appear in the list. Relation must be #=, % #=<, #>=, #< or #>. For example: % -% == +% ``` % ?- chain(#>=, [X,Y,Z]). % X#>=Y, % Y#>=Z. -% == +% ``` chain(Relation, Zs) :- must_be(list, Zs), @@ -7469,7 +7523,7 @@ chain_relation(#=<). chain_relation(#>). chain_relation(#>=). -chain(Relation, X, Prev, X) :- call(Relation, ?(Prev), ?(X)). +chain(Relation, X, Prev, X) :- call(Relation, #Prev, #X). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -7521,7 +7575,7 @@ fd_size(X, S) :- %% fd_dom(+Var, -Dom) % -% Dom is the current domain (see in/2) of Var. This predicate is +% Dom is the current domain (see `(in)/2`) of Var. This predicate is % useful if you want to reason about domains. It is _not_ needed if % you only want to display remaining domains; instead, separate your % model from the search part and let the toplevel display this @@ -7532,22 +7586,22 @@ fd_size(X, S) :- % following code, you can convert a _finite_ domain to a list of % integers: % -% == +% ``` % dom_integers(D, Is) :- phrase(dom_integers_(D), Is). % % dom_integers_(I) --> { integer(I) }, [I]. % dom_integers_(L..U) --> { numlist(L, U, Is) }, Is. % dom_integers_(D1\/D2) --> dom_integers_(D1), dom_integers_(D2). -% == +% ``` % % Example: % -% == +% ``` % ?- X in 1..5, X #\= 4, fd_dom(X, D), dom_integers(D, Is). % D = 1..3\/5, % Is = [1,2,3,5], % X in 1..3\/5. -% == +% ``` fd_dom(X, Drep) :- ( fd_get(X, XD, _) -> @@ -7643,10 +7697,6 @@ intervals_to_drep([A0-B0|Rest], Drep0, Drep) :- ), intervals_to_drep(Rest, Drep0 \/ D1, Drep). -attribute_goals(X) --> - { get_atts(X, queue(_,_)) }, - !, - { put_atts(X, -queue(_,_)) }. attribute_goals(X) --> % { get_attr(X, clpz, Attr), format("A: ~w\n", [Attr]) }, { get_attr(X, clpz, clpz_attr(_,_,_,Dom,fd_props(Gs,Bs,Os),_)), @@ -7663,7 +7713,7 @@ attributes_goals([]) --> []. attributes_goals([propagator(P, State)|As]) --> ( { ground(State) } -> [] ; { phrase(attribute_goal_(P), Gs) } -> - { % del_attr(State, clpz_aux), State = processed, + { del_attr(State, clpz_aux), State = processed, ( monotonic -> maplist(unwrap_with(bare_integer), Gs, Gs1) ; maplist(unwrap_with(=), Gs, Gs1) @@ -7677,35 +7727,35 @@ attributes_goals([propagator(P, State)|As]) --> with_clpz(G, clpz:G). unwrap_with(_, V, V) :- var(V), !. -unwrap_with(Goal, ?(V0), V) :- !, call(Goal, V0, V). +unwrap_with(Goal, #V0, V) :- !, call(Goal, V0, V). unwrap_with(Goal, Term0, Term) :- Term0 =.. [F|Args0], maplist(unwrap_with(Goal), Args0, Args), Term =.. [F|Args]. -bare_integer(V0, V) :- ( integer(V0) -> V = V0 ; V = #(V0) ). +bare_integer(V0, V) :- ( integer(V0) -> V = V0 ; V = #V0 ). attribute_goal_(presidual(Goal)) --> [Goal]. -attribute_goal_(pgeq(A,B)) --> [?(A) #>= ?(B)]. -attribute_goal_(pplus(X,Y,Z)) --> [?(X) + ?(Y) #= ?(Z)]. -attribute_goal_(pneq(A,B)) --> [?(A) #\= ?(B)]. -attribute_goal_(ptimes(X,Y,Z)) --> [?(X) * ?(Y) #= ?(Z)]. -attribute_goal_(absdiff_neq(X,Y,C)) --> [abs(?(X) - ?(Y)) #\= C]. -attribute_goal_(x_eq_abs_plus_v(X,V)) --> [?(X) #= abs(?(X)) + ?(V)]. -attribute_goal_(x_neq_y_plus_z(X,Y,Z)) --> [?(X) #\= ?(Y) + ?(Z)]. -attribute_goal_(x_leq_y_plus_c(X,Y,C)) --> [?(X) #=< ?(Y) + C]. -attribute_goal_(ptzdiv(X,Y,Z)) --> [?(X) // ?(Y) #= ?(Z)]. -attribute_goal_(pdiv(X,Y,Z)) --> [?(X) div ?(Y) #= ?(Z)]. -attribute_goal_(prdiv(X,Y,Z)) --> [?(X) / ?(Y) #= ?(Z)]. -attribute_goal_(pexp(X,Y,Z)) --> [?(X) ^ ?(Y) #= ?(Z)]. -attribute_goal_(psign(X,Y)) --> [?(Y) #= sign(?(X))]. -attribute_goal_(pabs(X,Y)) --> [?(Y) #= abs(?(X))]. -attribute_goal_(pmod(X,M,K)) --> [?(X) mod ?(M) #= ?(K)]. -attribute_goal_(prem(X,Y,Z)) --> [?(X) rem ?(Y) #= ?(Z)]. -attribute_goal_(pmax(X,Y,Z)) --> [?(Z) #= max(?(X),?(Y))]. -attribute_goal_(pmin(X,Y,Z)) --> [?(Z) #= min(?(X),?(Y))]. -attribute_goal_(pxor(X,Y,Z)) --> [?(Z) #= xor(?(X), ?(Y))]. -attribute_goal_(ppopcount(X,Y)) --> [?(Y) #= popcount(?(X))]. +attribute_goal_(pgeq(A,B)) --> [#A #>= #B]. +attribute_goal_(pplus(X,Y,Z)) --> [#X + #Y #= #Z]. +attribute_goal_(pneq(A,B)) --> [#A #\= #B]. +attribute_goal_(ptimes(X,Y,Z)) --> [#X * #Y #= #Z]. +attribute_goal_(absdiff_neq(X,Y,C)) --> [abs(#X - #Y) #\= C]. +attribute_goal_(x_eq_abs_plus_v(X,V)) --> [#X #= abs(#X) + #V]. +attribute_goal_(x_neq_y_plus_z(X,Y,Z)) --> [#X #\= #Y + #Z]. +attribute_goal_(x_leq_y_plus_c(X,Y,C)) --> [#X #=< #Y + C]. +attribute_goal_(ptzdiv(X,Y,Z)) --> [#X // #Y #= #Z]. +attribute_goal_(pdiv(X,Y,Z)) --> [#X div #Y #= #Z]. +attribute_goal_(prdiv(X,Y,Z)) --> [#X / #Y #= #Z]. +attribute_goal_(pexp(X,Y,Z)) --> [#X ^ #Y #= #Z]. +attribute_goal_(psign(X,Y)) --> [#Y #= sign(#X)]. +attribute_goal_(pabs(X,Y)) --> [#Y #= abs(#X)]. +attribute_goal_(pmod(X,M,K)) --> [#X mod #M #= #K]. +attribute_goal_(prem(X,Y,Z)) --> [#X rem #Y #= #Z]. +attribute_goal_(pmax(X,Y,Z)) --> [#Z #= max(#X,#Y)]. +attribute_goal_(pmin(X,Y,Z)) --> [#Z #= min(#X,#Y)]. +attribute_goal_(pxor(X,Y,Z)) --> [#Z #= xor(#X, #Y)]. +attribute_goal_(ppopcount(X,Y)) --> [#Y #= popcount(#X)]. attribute_goal_(scalar_product_neq(Cs,Vs,C)) --> [Left #\= Right], { scalar_product_left_right([-1|Cs], [C|Vs], Left, Right) }. @@ -7735,45 +7785,46 @@ attribute_goal_(rel_tuple(R, Tuple)) --> attribute_goal_(pzcompare(O,A,B)) --> [zcompare(O,A,B)]. % reified constraints attribute_goal_(reified_in(V, D, B)) --> - [V in Drep #<==> ?(B)], + [V in Drep #<==> #B], { domain_to_drep(D, Drep) }. attribute_goal_(reified_tuple_in(Tuple, R, B)) --> { get_attr(R, clpz_relation, Rel) }, - [tuples_in([Tuple], Rel) #<==> ?(B)]. + [tuples_in([Tuple], Rel) #<==> #B]. attribute_goal_(kill_reified_tuples(_,_,_)) --> []. attribute_goal_(tuples_not_in(_,_,_)) --> []. -attribute_goal_(reified_fd(V,B)) --> [finite_domain(V) #<==> ?(B)]. +attribute_goal_(reified_fd(V,B)) --> [finite_domain(V) #<==> #B]. attribute_goal_(pskeleton(X,Y,D,_,Z,F)) --> { Prop =.. [F,X,Y,Z], phrase(attribute_goal_(Prop), Goals), list_goal(Goals, Goal) }, - [?(D) #= 1 #==> Goal, ?(Y) #\= 0 #==> ?(D) #= 1]. + [#D #= 1 #==> Goal, #Y #\= 0 #==> #D #= 1]. attribute_goal_(reified_neq(DX,X,DY,Y,_,B)) --> - conjunction(DX, DY, ?(X) #\= ?(Y), B). + conjunction(DX, DY, #X #\= #Y, B). attribute_goal_(reified_eq(DX,X,DY,Y,_,B)) --> - conjunction(DX, DY, ?(X) #= ?(Y), B). + conjunction(DX, DY, #X #= #Y, B). attribute_goal_(reified_geq(DX,X,DY,Y,_,B)) --> - conjunction(DX, DY, ?(X) #>= ?(Y), B). -attribute_goal_(reified_and(X,_,Y,_,B)) --> [?(X) #/\ ?(Y) #<==> ?(B)]. -attribute_goal_(reified_or(X, _, Y, _, B)) --> [?(X) #\/ ?(Y) #<==> ?(B)]. -attribute_goal_(reified_not(X, Y)) --> [#\ ?(X) #<==> ?(Y)]. -attribute_goal_(pimpl(X, Y, _)) --> [?(X) #==> ?(Y)]. + conjunction(DX, DY, #X #>= #Y, B). +attribute_goal_(reified_and(X,_,Y,_,B)) --> [#X #/\ #Y #<==> #B]. +attribute_goal_(reified_or(X, _, Y, _, B)) --> [#X #\/ #Y #<==> #B]. +attribute_goal_(reified_not(X, Y)) --> [#\ #X #<==> #Y]. +attribute_goal_(preified_slash(X, Y, _, R)) --> [#X/ #Y #= R]. +attribute_goal_(pimpl(X, Y, _)) --> [#X #==> #Y]. attribute_goal_(pfunction(Op, A, B, R)) --> - { Expr =.. [Op,?(A),?(B)] }, - [?(R) #= Expr]. + { Expr =.. [Op,#A,#B] }, + [#R #= Expr]. attribute_goal_(pfunction(Op, A, R)) --> - { Expr =.. [Op,?(A)] }, - [?(R) #= Expr]. + { Expr =.. [Op,#A] }, + [#R #= Expr]. conjunction(A, B, G, D) --> - ( { A == 1, B == 1 } -> [G #<==> ?(D)] - ; { A == 1 } -> [(?(B) #/\ G) #<==> ?(D)] - ; { B == 1 } -> [(?(A) #/\ G) #<==> ?(D)] - ; [(?(A) #/\ ?(B) #/\ G) #<==> ?(D)] + ( { A == 1, B == 1 } -> [G #<==> #D] + ; { A == 1 } -> [(#B #/\ G) #<==> #D] + ; { B == 1 } -> [(#A #/\ G) #<==> #D] + ; [(#A #/\ #B #/\ G) #<==> #D] ). original_goal(original_goal(State, Goal)) --> ( { var(State) } -> -% { State = processed }, + { State = processed }, [Goal] ; [] ). @@ -7814,7 +7865,7 @@ scalar_plusterm([CV|CVs], T) :- plusterm_(CV, T0, T0+T) :- coeff_var_term(CV, T). -coeff_var_term(C-V, T) :- ( C =:= 1 -> T = ?(V) ; T = C * ?(V) ). +coeff_var_term(C-V, T) :- ( C =:= 1 -> T = #V ; T = C * #V ). /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Reified predicates for use with predicates from library(reif). diff --git a/src/lib/crypto.pl b/src/lib/crypto.pl index 458283b7..71c0420e 100644 --- a/src/lib/crypto.pl +++ b/src/lib/crypto.pl @@ -1,20 +1,20 @@ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Written 2020, 2021, 2022 by Markus Triska (triska@metalevel.at) + Written 2020-2023 by Markus Triska (triska@metalevel.at) Part of Scryer Prolog. - Predicates for cryptographic applications. +/** Predicates for cryptographic applications. - This library assumes that the Prolog flag double_quotes is set to chars. + This library assumes that the Prolog flag `double_quotes` is set to `chars`. In Scryer Prolog, lists of characters are very efficiently represented, and strings have the advantage that the atom table remains unmodified. Especially for cryptographic applications, it is an advantage that using strings leaves little trace of what was processed in the system. - For predicates that accept an encoding/1 option to specify the encoding - of the input data, if encoding(octet) is used, then the input can also - be specified as a list of bytes, i.e., integers between 0 and 255. -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + For predicates that accept an `encoding/1` option to specify the encoding + of the input data, if `encoding(octet)` is used, then the input can also + be specified as a list of _bytes_, i.e., integers between 0 and 255. +*/ :- module(crypto, [hex_bytes/2, % ?Hex, ?Bytes @@ -48,20 +48,20 @@ :- use_module(library(si)). :- use_module(library(iso_ext), [partial_string/3]). -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - hex_bytes(?Hex, ?Bytes) is det. - - Relation between a hexadecimal sequence and a list of bytes. Hex - is a string of hexadecimal numbers. Bytes is a list of *integers* - between 0 and 255 that represent the sequence as a list of bytes. - At least one of the arguments must be instantiated. - - Example: - - ?- hex_bytes("501ACE", Bs). - Bs = [80,26,206]. -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +%% hex_bytes(?Hex, ?Bytes) is det. +% +% Relation between a hexadecimal sequence and a list of bytes. Hex +% is a string of hexadecimal numbers. Bytes is a list of _integers_ +% between 0 and 255 that represent the sequence as a list of bytes. +% At least one of the arguments must be instantiated. +% +% Example: +% +% ``` +% ?- hex_bytes("501ACE", Bs). +% Bs = [80,26,206]. +% ``` hex_bytes(Hs, Bytes) :- ( ground(Hs) -> @@ -113,47 +113,52 @@ must_be_octet_chars(Chars, Context) :- /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Cryptographically secure random numbers ======================================= - - crypto_n_random_bytes(+N, -Bytes) is det - - Bytes is unified with a list of N cryptographically secure - pseudo-random bytes. Each byte is an integer between 0 and 255. If - the internal pseudo-random number generator (PRNG) has not been - seeded with enough entropy to ensure an unpredictable byte - sequence, an exception is thrown. - - One way to relate such a list of bytes to an _integer_ is to use - CLP(ℤ) constraints as follows: - - :- use_module(library(clpz)). - :- use_module(library(lists)). - - bytes_integer(Bs, N) :- - foldl(pow, Bs, 0-0, N-_). - - pow(B, N0-I0, N-I) :- - B in 0..255, - N #= N0 + B*256^I0, - I #= I0 + 1. - - With this definition, we can generate a random 256-bit integer - _from_ a list of 32 random _bytes_: - - ?- crypto_n_random_bytes(32, Bs), - bytes_integer(Bs, I). - Bs = [146,166,162,210,242,7,25,132,64,94|...], - I = 337420085690608915485...(56 digits omitted). - - The above relation also works in the other direction, letting you - translate an integer _to_ a list of bytes. In addition, you can - use hex_bytes/2 to convert bytes to _tokens_ that can be easily - exchanged in your applications. - - ?- crypto_n_random_bytes(12, Bs), - hex_bytes(Hex, Bs). - Bs = [34,25,50,72,58,63,50,172,32,46|...], Hex = "221932483a3f32ac202 ...". - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +%% crypto_n_random_bytes(+N, -Bytes) is det. +% +% Bytes is unified with a list of N cryptographically secure +% pseudo-random bytes. Each byte is an integer between 0 and 255. If +% the internal pseudo-random number generator (PRNG) has not been +% seeded with enough entropy to ensure an unpredictable byte +% sequence, an exception is thrown. +% +% One way to relate such a list of bytes to an _integer_ is to use +% CLP(ℤ) constraints as follows: +% +% ``` +% :- use_module(library(clpz)). +% :- use_module(library(lists)). +% +% bytes_integer(Bs, N) :- +% foldl(pow, Bs, 0-0, N-_). +% +% pow(B, N0-I0, N-I) :- +% B in 0..255, +% N #= N0 + B*256^I0, +% I #= I0 + 1. +% ``` +% +% With this definition, we can generate a random 256-bit integer +% _from_ a list of 32 random _bytes_: +% +% ``` +% ?- crypto_n_random_bytes(32, Bs), +% bytes_integer(Bs, I). +% Bs = [146,166,162,210,242,7,25,132,64,94|...], +% I = 337420085690608915485...(56 digits omitted). +% ``` +% +% The above relation also works in the other direction, letting you +% translate an integer _to_ a list of bytes. In addition, you can +% use `hex_bytes/2` to convert bytes to _tokens_ that can be easily +% exchanged in your applications. +% +% ``` +% ?- crypto_n_random_bytes(12, Bs), +% hex_bytes(Hex, Bs). +% Bs = [34,25,50,72,58,63,50,172,32,46|...], Hex = "221932483a3f32ac202 ...". +% ``` crypto_n_random_bytes(N, Bs) :- must_be(integer, N), @@ -165,30 +170,34 @@ crypto_random_byte(B) :- '$crypto_random_byte'(B). /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Hashing ======= - - crypto_data_hash(+Data, -Hash, +Options) - - Where Data is a list of characters, and Hash is the computed hash - as a list of hexadecimal characters. - - Options is a list of: - - - algorithm(+A) - where A is one of ripemd160, sha256, sha384, sha512, sha512_256, - sha3_224, sha3_256, sha3_384, sha3_512, blake2s256, blake2b512, - or a variable. If A is a variable, then it is unified with the - default algorithm, which is an algorithm that is considered - cryptographically secure at the time of this writing. - - encoding(+Encoding) - The default encoding is utf8. The alternative is octet, - to treat the input as a list of raw bytes. - - Example: - - ?- crypto_data_hash("abc", Hs, [algorithm(sha256)]). - Hs = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad". - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +%% crypto_data_hash(+Data, -Hash, +Options) +% +% Where Data is a list of characters, and Hash is the computed hash +% as a list of hexadecimal characters. +% +% Options is a list of: +% +% - `algorithm(+A)` +% where `A` is one of `ripemd160`, `sha256`, `sha384`, `sha512`, +% `sha512_256`, `sha3_224`, `sha3_256`, `sha3_384`, +% `sha3_512`, `blake2s256`, `blake2b512`, or a variable. If `A` is +% a variable, then it is unified with the default algorithm, +% which is an algorithm that is considered cryptographically +% secure at the time of this writing. +% +% - `encoding(+Encoding)` +% The default encoding is `utf8`. The alternative is `octet`, to +% treat the input as a list of raw bytes. +% +% Example: +% +% ``` +% ?- crypto_data_hash("abc", Hs, [algorithm(sha256)]). +% Hs = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad". +% ``` + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - SHA256 is the current default for several hash-related predicates. It is deemed sufficiently secure for the foreseeable future. Yet, @@ -238,38 +247,36 @@ hash_algorithm(blake2s256). hash_algorithm(blake2b512). -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - crypto_data_hkdf(+Data, +Length, -Bytes, +Options) is det. - - Concentrate possibly dispersed entropy of Data and then expand it - to the desired length. Data is a list of characters. - - Bytes is unified with a list of bytes of length Length, and is - suitable as input keying material and initialization vectors to - symmetric encryption algorithms. - - Admissible options are: - - - algorithm(+Algorithm) - One of sha256, sha384 or sha512. If you specify a variable, - then it is unified with the algorithm that was used, which is a - cryptographically secure algorithm by default. - - info(+Info) - Optional context and application specific information, - specified as a list of characters. The default is []. - - salt(+List) - Optionally, a list of bytes that are used as salt. The - default is all zeroes. - - encoding(+Encoding) - The default encoding is utf8. The alternative is octet, - to treat the input as a list of raw bytes. - - The `info/1` option can be used to generate multiple keys from a - single master key, using for example values such as "key" and - "iv", or the name of a file that is to be encrypted. - - See crypto_n_random_bytes/2 to obtain a suitable salt. -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +%% crypto_data_hkdf(+Data, +Length, -Bytes, +Options) is det. +% +% Concentrate possibly dispersed entropy of Data and then expand it +% to the desired length. Data is a list of characters. +% +% Bytes is unified with a list of bytes of length Length, and is +% suitable as input keying material and initialization vectors to +% symmetric encryption algorithms. +% +% Admissible options are: +% +% - `algorithm(+Algorithm)` +% One of `sha256`, `sha384` or `sha512`. If you specify a variable, +% then it is unified with the algorithm that was used, which is a +% cryptographically secure algorithm by default. +% - `info(+Info)` +% Optional context and application specific information, +% specified as a list of characters. The default is `[]`. +% - `salt(+List)` +% Optionally, a list of bytes that are used as salt. The +% default is all zeroes. +% - `encoding(+Encoding)` +% The default encoding is `utf8`. The alternative is `octet`, +% to treat the input as a list of raw bytes. +% +% The `info/1` option can be used to generate multiple keys from a +% single master key, using for example values such as "key" and +% "iv", or the name of a file that is to be encrypted. +% +% See `crypto_n_random_bytes/2` to obtain a suitable salt. crypto_data_hkdf(Data0, L, Bytes, Options0) :- functor_hash_options(algorithm, Algorithm, Options0, Options), @@ -323,14 +330,12 @@ chars_bytes_(Cs, Bytes, Context) :- know if you need to rely on any specifics of this format. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - crypto_password_hash(+Password, ?Hash) is semidet. - - If Hash is instantiated, the predicate succeeds _iff_ the hash - matches the given password. Otherwise, the call is equivalent to - crypto_password_hash(Password, Hash, []) and computes a - password-based hash using the default options. -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +%% crypto_password_hash(+Password, ?Hash) is semidet. +% +% If Hash is instantiated, the predicate succeeds _iff_ the hash +% matches the given password. Otherwise, the call is equivalent to +% `crypto_password_hash(Password, Hash, [])` and computes a +% password-based hash using the default options. crypto_password_hash(Password0, Hash) :- ( nonvar(Hash) -> @@ -353,58 +358,56 @@ dollar_segments(Ls, Segments) :- ). -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - crypto_password_hash(+Password, -Hash, +Options) is det. - - Derive Hash based on Password. This predicate is similar to - crypto_data_hash/3 in that it derives a hash from given data. - However, it is tailored for the specific use case of _passwords_. - One essential distinction is that for this use case, the derivation - of a hash should be _as slow as possible_ to counteract brute-force - attacks over possible passwords. - - Another important distinction is that equal passwords must yield, - with very high probability, _different_ hashes. For this reason, - cryptographically strong random numbers are automatically added to - the password before a hash is derived. - - Hash is unified with a string that contains the computed hash and - all parameters that were used, except for the password. Instead of - storing passwords, store these hashes. Later, you can verify the - validity of a password with crypto_password_hash/2, comparing the - then entered password to the stored hash. If you need to export this - atom, you should treat it as opaque ASCII data with up to 255 bytes - of length. The maximal length may increase in the future. - - Admissible options are: - - - algorithm(+Algorithm) - The algorithm to use. Currently, the only available algorithm - is 'pbkdf2-sha512', which is therefore also the default. - - cost(+C) - C is an integer, denoting the binary logarithm of the number - of _iterations_ used for the derivation of the hash. This - means that the number of iterations is set to 2^C. Currently, - the default is 17, and thus more than one hundred _thousand_ - iterations. You should set this option as high as your server - and users can tolerate. The default is subject to change and - will likely increase in the future or adapt to new algorithms. - - salt(+Salt) - Use the given list of bytes as salt. By default, - cryptographically secure random numbers are generated for this - purpose. The default is intended to be secure, and constitutes - the typical use case of this predicate. - - Currently, PBKDF2 with SHA-512 is used as the hash derivation - function, using 128 bits of salt. All default parameters, including - the algorithm, are subject to change, and other algorithms will also - become available in the future. Since computed hashes store all - parameters that were used during their derivation, such changes will - not affect the operation of existing deployments. Note though that - new hashes will then be computed with the new default parameters. - - See crypto_data_hkdf/4 for generating keys from Hash. -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +%% crypto_password_hash(+Password, -Hash, +Options) is det. +% +% Derive Hash based on Password. This predicate is similar to +% `crypto_data_hash/3` in that it derives a hash from given data. +% However, it is tailored for the specific use case of _passwords_. +% One essential distinction is that for this use case, the derivation +% of a hash should be _as slow as possible_ to counteract brute-force +% attacks over possible passwords. +% +% Another important distinction is that equal passwords must yield, +% with very high probability, _different_ hashes. For this reason, +% cryptographically strong random numbers are automatically added to +% the password before a hash is derived. +% +% Hash is unified with a string that contains the computed hash and +% all parameters that were used, except for the password. Instead of +% storing passwords, store these hashes. Later, you can verify the +% validity of a password with `crypto_password_hash/2`, comparing the +% then entered password to the stored hash. If you need to export this +% atom, you should treat it as opaque ASCII data with up to 255 bytes +% of length. The maximal length may increase in the future. +% +% Admissible options are: +% +% - `algorithm(+Algorithm)` +% The algorithm to use. Currently, the only available algorithm +% is `'pbkdf2-sha512'`, which is therefore also the default. +% - `cost(+C)` +% C is an integer, denoting the binary logarithm of the number +% of _iterations_ used for the derivation of the hash. This +% means that the number of iterations is set to 2^C. Currently, +% the default is 17, and thus more than one hundred _thousand_ +% iterations. You should set this option as high as your server +% and users can tolerate. The default is subject to change and +% will likely increase in the future or adapt to new algorithms. +% - `salt(+Salt)` +% Use the given list of bytes as salt. By default, +% cryptographically secure random numbers are generated for this +% purpose. The default is intended to be secure, and constitutes +% the typical use case of this predicate. +% +% Currently, PBKDF2 with SHA-512 is used as the hash derivation +% function, using 128 bits of salt. All default parameters, including +% the algorithm, are subject to change, and other algorithms will also +% become available in the future. Since computed hashes store all +% parameters that were used during their derivation, such changes will +% not affect the operation of existing deployments. Note though that +% new hashes will then be computed with the new default parameters. +% +% See `crypto_data_hkdf/4` for generating keys from Hash. crypto_password_hash(Password0, Hash, Options) :- chars_bytes_(Password0, Password, crypto_password_hash/3), @@ -435,97 +438,94 @@ bytes_base64(Bytes, Base64) :- chars_base64(Chars, Base64, [padding(false)]) ). -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - crypto_data_encrypt(+PlainText, - +Algorithm, - +Key, - +IV, - -CipherText, - +Options). - - Encrypt the given PlainText, using the symmetric algorithm - Algorithm, key Key, and initialization vector (or nonce) IV, to - give CipherText. - - PlainText must be a list of characters, Key and IV must be lists of - bytes, and CipherText is created as a list of characters. - - Keys and IVs can be chosen at random (using for example - crypto_n_random_bytes/2) or derived from input keying material (IKM) - using for example crypto_data_hkdf/4. This input is often a shared - secret, such as a negotiated point on an elliptic curve, or the hash - that was computed from a password via crypto_password_hash/3 with a - freshly generated and specified _salt_. - - Reusing the same combination of Key and IV typically leaks at least - _some_ information about the plaintext. For example, identical - plaintexts will then correspond to identical ciphertexts. For some - algorithms, reusing an IV with the same Key has disastrous results - and can cause the loss of all properties that are otherwise - guaranteed. Especially in such cases, an IV is also called a - _nonce_ (number used once). - - It is safe to store and transfer the used initialization vector (or - nonce) in plain text, but the key _must be kept secret_. - - Currently, the only supported algorithm is 'chacha20-poly1305', a - powerful and efficient _authenticated_ encryption scheme, providing - secrecy and at the same time reliable protection against undetected - _modifications_ of the encrypted data. This is a very good choice - for virtually all use cases. It is a stream cipher and can encrypt - data of any length up to 256 GB. Further, the encrypted data has - exactly the same length as the original, and no padding is used. - - Options: - - - encoding(+Encoding) - Encoding to use for PlainText. Default is utf8. The alternative - is octet to treat PlainText as raw bytes. - - - tag(-List) - For authenticated encryption schemes, List is unified with a - list of _bytes_ holding the tag. This tag must be provided for - decryption. - - - aad(+Data) - Data is additional authenticated data (AAD), a list of - characters. It is authenticated in that it influences the tag, - but it is not encrypted. The encoding/1 option also specifies - the encoding of Data. - - Here is an example encryption and decryption, using the ChaCha20 - stream cipher with the Poly1305 authenticator. This cipher uses a - 256-bit key and a 96-bit nonce, i.e., 32 and 12 _bytes_, - respectively: - - ?- Algorithm = 'chacha20-poly1305', - crypto_n_random_bytes(32, Key), - crypto_n_random_bytes(12, IV), - crypto_data_encrypt("this text is to be encrypted", Algorithm, - Key, IV, CipherText, [tag(Tag)]), - crypto_data_decrypt(CipherText, Algorithm, - Key, IV, RecoveredText, [tag(Tag)]). - - Yielding: - - Algorithm = 'chacha20-poly1305', - Key = [113,247,153,134,177,220,13,193,50,150|...], - IV = [135,20,149,153,63,35,68,114,247,171|...], - CipherText = "\x94\0Ej\x94\®Â\x95\óÑÆXÃn¾ð©b\x1c\ ...", - RecoveredText = "this text is to be ...", - Tag = [152,117,152,17,162,75,150,206,144,40|...] - - In this example, we use crypto_n_random_bytes/2 to generate a key - and nonce from cryptographically secure random numbers. For - repeated applications, you must ensure that a nonce is only used - _once_ together with the same key. Note that for _authenticated_ - encryption schemes, the _tag_ that was computed during encryption - is necessary for decryption. It is safe to store and transfer the - tag in plain text. - - See also crypto_data_decrypt/6, and hex_bytes/2 for conversion - between bytes and hex encoding. -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +%% crypto_data_encrypt(+PlainText, +Algorithm, +Key, +IV, -CipherText, +Options). +% +% Encrypt the given PlainText, using the symmetric algorithm +% Algorithm, key Key, and initialization vector (or nonce) IV, to +% give CipherText. +% +% PlainText must be a list of characters, Key and IV must be lists of +% bytes, and CipherText is created as a list of characters. +% +% Keys and IVs can be chosen at random (using for example +% `crypto_n_random_bytes/2`) or derived from input keying material (IKM) +% using for example `crypto_data_hkdf/4`. This input is often a shared +% secret, such as a negotiated point on an elliptic curve, or the hash +% that was computed from a password via `crypto_password_hash/3` with a +% freshly generated and specified _salt_. +% +% Reusing the same combination of Key and IV typically leaks at least +% _some_ information about the plaintext. For example, identical +% plaintexts will then correspond to identical ciphertexts. For some +% algorithms, reusing an IV with the same Key has disastrous results +% and can cause the loss of all properties that are otherwise +% guaranteed. Especially in such cases, an IV is also called a +% _nonce_ (number used once). +% +% It is safe to store and transfer the used initialization vector (or +% nonce) in plain text, but the key _must be kept secret_. +% +% Currently, the only supported algorithm is 'chacha20-poly1305', a +% powerful and efficient _authenticated_ encryption scheme, providing +% secrecy and at the same time reliable protection against undetected +% _modifications_ of the encrypted data. This is a very good choice +% for virtually all use cases. It is a stream cipher and can encrypt +% data of any length up to 256 GB. Further, the encrypted data has +% exactly the same length as the original, and no padding is used. +% +% Options: +% +% - `encoding(+Encoding)` +% Encoding to use for PlainText. Default is utf8. The alternative +% is octet to treat PlainText as raw bytes. +% +% - `tag(-List)` +% For authenticated encryption schemes, List is unified with a +% list of _bytes_ holding the tag. This tag must be provided for +% decryption. +% +% - `aad(+Data)` +% Data is additional authenticated data (AAD), a list of +% characters. It is authenticated in that it influences the tag, +% but it is not encrypted. The `encoding/1` option also specifies +% the encoding of Data. +% +% Here is an example encryption and decryption, using the ChaCha20 +% stream cipher with the Poly1305 authenticator. This cipher uses a +% 256-bit key and a 96-bit nonce, i.e., 32 and 12 _bytes_, +% respectively: +% +% ``` +% ?- Algorithm = 'chacha20-poly1305', +% crypto_n_random_bytes(32, Key), +% crypto_n_random_bytes(12, IV), +% crypto_data_encrypt("this text is to be encrypted", Algorithm, +% Key, IV, CipherText, [tag(Tag)]), +% crypto_data_decrypt(CipherText, Algorithm, +% Key, IV, RecoveredText, [tag(Tag)]). +% ``` +% +% Yielding: +% +% ``` +% Algorithm = 'chacha20-poly1305', +% Key = [113,247,153,134,177,220,13,193,50,150|...], +% IV = [135,20,149,153,63,35,68,114,247,171|...], +% CipherText = "\x94\0Ej\x94\®Â\x95\óÑÆXÃn¾ð©b\x1c\ ...", +% RecoveredText = "this text is to be ...", +% Tag = [152,117,152,17,162,75,150,206,144,40|...] +% ``` +% +% In this example, we use `crypto_n_random_bytes/2` to generate a key +% and nonce from cryptographically secure random numbers. For +% repeated applications, you must ensure that a nonce is only used +% _once_ together with the same key. Note that for _authenticated_ +% encryption schemes, the _tag_ that was computed during encryption +% is necessary for decryption. It is safe to store and transfer the +% tag in plain text. +% +% See also `crypto_data_decrypt/6`, and `hex_bytes/2` for conversion +% between bytes and hex encoding. crypto_data_encrypt(PlainText0, Algorithm, Key, IV, CipherText, Options) :- options_data_chars(Options, PlainText0, PlainText, Encoding), @@ -549,37 +549,30 @@ algorithm_key_iv('chacha20-poly1305', Key, IV) :- length(Key, 32), length(IV, 12). -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - crypto_data_decrypt(+CipherText, - +Algorithm, - +Key, - +IV, - -PlainText, - +Options). - - Decrypt the given CipherText, using the symmetric algorithm - Algorithm, key Key, and initialization vector IV, to give - PlainText. CipherText must be a list of characters, and Key and IV - must be lists of bytes. PlainText is created as a list of - characters. - - Currently, the only supported algorithm is 'chacha20-poly1305', - a very secure, fast and versatile authenticated encryption method. - - Options is a list of: - - - encoding(+Encoding) - Encoding to use for PlainText. The default is utf8. The - alternative is octet, which is used if the data are raw bytes. - - - tag(+Tag) - For authenticated encryption schemes, the tag must be specified as - a list of bytes exactly as they were generated upon encryption. - - - aad(+Data) - Any additional authenticated data (AAD) must be specified. The - encoding/1 option also specifies the encoding of Data. -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +%% crypto_data_decrypt(+CipherText, +Algorithm, +Key, +IV, -PlainText, +Options). +% +% Decrypt the given CipherText, using the symmetric algorithm +% Algorithm, key Key, and initialization vector IV, to give +% PlainText. CipherText must be a list of characters, and Key and IV +% must be lists of bytes. PlainText is created as a list of +% characters. +% +% Currently, the only supported algorithm is 'chacha20-poly1305', +% a very secure, fast and versatile authenticated encryption method. +% +% Options is a list of: +% +% - `encoding(+Encoding)` +% Encoding to use for PlainText. The default is utf8. The +% alternative is octet, which is used if the data are raw bytes. +% +% - `tag(+Tag)` +% For authenticated encryption schemes, the tag must be specified as +% a list of bytes exactly as they were generated upon encryption. +% +% - `aad(+Data)` +% Any additional authenticated data (AAD) must be specified. The +% `encoding/1` option also specifies the encoding of Data. crypto_data_decrypt(CipherText0, Algorithm, Key, IV, PlainText, Options) :- option(tag(Tag), Options, []), @@ -617,49 +610,53 @@ encoding_chars(utf8, Cs, Cs) :- /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Digital signatures with Ed25519 =============================== - - - ed25519_new_keypair(-Pair) - Yields a new Ed25519 key pair Pair, a list of characters. The - pair contains the private key and must be kept absolutely secret. - Pair can be used for signing. Its public key can be obtained - with ed25519_keypair_public_key/2. - - - ed25519_keypair_public_key(+Pair, -PublicKey) - PublicKey is the public key of the given key pair. The public key - can be used for signature verification, and can be shared freely. - The public key is represented as a list of characters. - - - ed25519_sign(+Key, +Data, -Signature, +Options) - Key and Data must be lists of characters. Key is a key pair in - PKCS#8 v2 format as generated by ed25519_new_keypair/1. Sign Data - with Key, yielding Signature as a list of hexadecimal characters. - - - ed25519_verify(+Key, +Data, +Signature, +Options) - Key and Data must be lists of characters. Key is a public key. - Succeeds if Data was signed with the private key corresponding to - Key, where Signature is a list of hexadecimal characters as - generated by ed25519_sign/4. Fails otherwise. - - Currently, the only option for signing and verifying is: - - - encoding(+Encoding) - The default encoding of Data is utf8. The alternative is octet, - which treats Data as a list of raw bytes. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +%% ed25519_new_keypair(-Pair) +% +% Yields a new Ed25519 key pair Pair, a list of characters. The +% pair contains the private key and must be kept absolutely secret. +% Pair can be used for signing. Its public key can be obtained +% with `ed25519_keypair_public_key/2`. + ed25519_new_keypair(Pair) :- '$ed25519_new_keypair'(Pair). +%% ed25519_keypair_public_key(+Pair, -PublicKey) +% +% PublicKey is the public key of the given key pair. The public key +% can be used for signature verification, and can be shared freely. +% The public key is represented as a list of characters. + ed25519_keypair_public_key(Pair, PublicKey) :- must_be_octet_chars(Pair, ed25519_keypair_public_key), '$ed25519_keypair_public_key'(Pair, PublicKey). +%% ed25519_sign(+Key, +Data, -Signature, +Options) +% +% Key and Data must be lists of characters. Key is a key pair in +% PKCS#8 v2 format as generated by `ed25519_new_keypair/1`. Sign Data +% with Key, yielding Signature as a list of hexadecimal characters. + ed25519_sign(Key, Data0, Signature, Options) :- must_be_octet_chars(Key, ed25519_sign), options_data_chars(Options, Data0, Data, Encoding), '$ed25519_sign'(Key, Data, Encoding, Signature0), hex_bytes(Signature, Signature0). +%% ed25519_verify(+Key, +Data, +Signature, +Options) +% +% Key and Data must be lists of characters. Key is a public key. +% Succeeds if Data was signed with the private key corresponding to +% Key, where Signature is a list of hexadecimal characters as +% generated by `ed25519_sign/4`. Fails otherwise. +% +% Currently, the only option for signing and verifying is: +% +% - `encoding(+Encoding)` +% The default encoding of Data is `utf8`. The alternative is `octet`, +% which treats Data as a list of raw bytes. + ed25519_verify(Key, Data0, Signature0, Options) :- must_be_octet_chars(Key, ed25519_verify), options_data_chars(Options, Data0, Data, Encoding), @@ -669,38 +666,43 @@ ed25519_verify(Key, Data0, Signature0, Options) :- /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - X25519: ECDH key exchange over Curve25519 ========================================= - - Points on Curve25519 are represented as lists of characters that denote - the u-coordinate of the Montgomery curve. - - - curve25519_generator(-Gs) - Gs is the generator point of Curve25519. - - - curve25519_scalar_mult(+Scalar, +Ps, -Rs) - Scalar must be an integer between 0 and 2^256-1, - or a list of 32 bytes, and Ps must be a point on the curve. - Computes the point Rs = Scalar*Ps as mandated by X25519. - - Alice and Bob can use this to establish a shared secret as follows, - where Gs is the generator point of Curve25519: - - 1. Alice creates a random integer a and sends As = a*Gs to Bob. - 2. Bob creates a random integer b and sends Bs = b*Gs to Alice. - 3. Alice computes Rs = a*Bs. - 4. Bob computes Rs = b*As. - 5. Alice and Bob use crypto_data_hkdf/4 on Rs with suitable - (same) parameters to obtain lists of bytes that can be used as - keys and initialization vectors for symmetric encryption. - - If a and b are kept secret, this method is considered very secure. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +%% curve25519_generator(-Gs) +% +% Points on Curve25519 are represented as lists of characters that +% denote the u-coordinate of the Montgomery curve. Gs is the +% generator point of Curve25519. + curve25519_generator(Gs) :- length(Gs0, 32), Gs0 = [9|Zs], maplist(=(0), Zs), maplist(char_code, Gs, Gs0). +%% curve25519_scalar_mult(+Scalar, +Ps, -Rs) +% +% Scalar must be an integer between 0 and 2^256-1, +% or a list of 32 bytes, and Ps must be a point on the curve. +% Computes the point _Rs = Scalar*Ps as_ mandated by X25519. +% +% Alice and Bob can use this to establish a shared secret as follows, +% where Gs is the generator point of Curve25519: +% +% 1. Alice creates a random integer _a_ and sends _As = a*Gs_ to Bob. +% +% 2. Bob creates a random integer _b_ and sends _Bs = b*Gs_ to Alice. +% +% 3. Alice computes _Rs = a*Bs_. +% +% 4. Bob computes _Rs = b*As_. +% +% 5. Alice and Bob use `crypto_data_hkdf/4` on Rs with suitable +% (same) parameters to obtain lists of bytes that can be used as +% keys and initialization vectors for symmetric encryption. +% +% If _a_ and _b_ are kept secret, this method is considered very secure. + curve25519_scalar_mult(Scalar, Point, Result) :- ( integer_si(Scalar) -> length(ScalarBytes, 32), diff --git a/src/lib/csv.pl b/src/lib/csv.pl index 478d425c..d6ad8f73 100644 --- a/src/lib/csv.pl +++ b/src/lib/csv.pl @@ -1,54 +1,67 @@ -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Predicates for parsing CSV data +/** Predicates for parsing CSV data +## Read CSV files. - Read csv files +Only two options with default values: - Only two options with default values : - - token_separator(',') - - with_header(true) +- `token_separator(',')` +- `with_header(true)` - Examples +### Examples: - * parsing a csv string: +Parsing a CSV string: - ?- use_module(library(csv)). - ?- use_module(library(dcgs)). - ?- phrase(parse_csv(Data), "col1,col2,col3,col4\none,2,,three"). - Data = frame(["col1","col2","col3","col4"],[["one",2,[],"three"]]). +``` +?- use_module(library(csv)). +?- use_module(library(dcgs)). +?- phrase(parse_csv(Data), "col1,col2,col3,col4\none,2,,three"). + Data = frame(["col1","col2","col3","col4"],[["one",2,[],"three"]]). +``` - * with some options: +With some options: - ?- phrase(parse_csv(Data, [with_header(false), token_separator(';')]), "one;2;;three"). - Data = frame([],[["one",2,[],"three"]]). +``` +?- phrase(parse_csv(Data, [with_header(false), token_separator(';')]), "one;2;;three"). + Data = frame([],[["one",2,[],"three"]]). +``` - * parsing a csv file: +Parsing a CSV file: - ?- use_module(library(csv)). - ?- use_module(library(pio)). - ?- phrase_from_file(parse_csv(frame(Header, Rows)), './test.csv'). +``` +?- use_module(library(csv)). +?- use_module(library(pio)). +?- phrase_from_file(parse_csv(frame(Header, Rows)), './test.csv'). +``` +## Write CSV files - Write csv files +Four options with default values : - Four options with default values : - - line_separator('\n') - - token_separator(',') - - with_header(true) - - null_value(empty) +- `line_separator('\n')` +- `token_separator(',')` +- `with_header(true)` +- `null_value(empty)` - Examples +### Examples - * writing a csv file: +Writing a CSV file: - ?- use_module(library(csv)). - ?- write_csv('./test.csv', frame(["col1","col2","col3","col4"], [["one",2,[],"three"]])). +``` +?- use_module(library(csv)). +?- write_csv('./test.csv', frame(["col1","col2","col3","col4"], [["one",2,[],"three"]])). +``` - * with some options +With some options - ?- use_module(library(csv)). - ?- write_csv('./test.csv', frame(["col1","col2","col3","col4"], [["one",2,[],"three"]]), [with_header(false), line_separator('\r\n'), token_separator(';'), null_value('\\N')]). -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +``` +?- use_module(library(csv)). +?- write_csv('./test.csv', frame( + ["col1","col2","col3","col4"], + [["one",2,[],"three"]] + ), + [with_header(false), line_separator('\r\n'), token_separator(';'), null_value('\\N')]). +``` +*/ :- module(csv, [ parse_csv//1, diff --git a/src/lib/dcgs.pl b/src/lib/dcgs.pl index 77eb2f5e..1767d2bb 100644 --- a/src/lib/dcgs.pl +++ b/src/lib/dcgs.pl @@ -1,3 +1,13 @@ +/** Support for Definite Clause Grammars. + +A Prolog definite clause grammar (DCG) describes a sequence. Operationally, DCGs +can be used to parse, generate, complete and check sequences manifested as lists. + +Check [The Power of Prolog chapter on DCGs](https://www.metalevel.at/prolog/dcg) +to learn more about them. +*/ + + :- module(dcgs, [op(1105, xfy, '|'), phrase/2, @@ -16,9 +26,44 @@ :- meta_predicate phrase(2, ?, ?). +%% phrase(+Body, ?Ls). +% +% True iff Body describes the list Ls. Body must be a DCG body. +% It is equivalent to `phrase(Body, Ls, [])`. +% +% Examples: +% +% ``` +% as --> []. +% as --> [a], as. +% +% ?- phrase(as, Ls). +% Ls = [] +% ; Ls = "a" +% ; Ls = "aa" +% ; Ls = "aaa" +% ; ... . +% +% ?- phrase(as, "aaa"). +% true. +% ``` + phrase(GRBody, S0) :- phrase(GRBody, S0, []). +%% phrase(+Body, ?Ls, ?Ls0). +% +% True iff Body describes part of the list Ls and the rest of Ls is Ls0. +% +% Example: +% +% ``` +% ?- phrase(seq(X), "aaa", Y). +% X = [], Y = "aaa" +% ; X = "a", Y = "aa" +% ; X = "aa", Y = "a" +% ; X = "aaa", Y = []. +% ``` phrase(GRBody, S0, S) :- strip_module(GRBody, M, GRBody1), ( var(GRBody) -> @@ -30,13 +75,6 @@ phrase(GRBody, S0, S) :- ; call(M:GRBody1, S0, S) ). - -module_call_qualified(M, Call, Call1) :- - ( nonvar(M) -> Call1 = M:Call - ; Call = Call1 - ). - - % The same version of the below two dcg_rule clauses, but with module scoping. dcg_rule(( M:NonTerminal, Terminals --> GRBody ), ( M:Head :- Body )) :- dcg_non_terminal(NonTerminal, S0, S, Head), @@ -82,7 +120,10 @@ dcg_body(NonTerminal, S0, S, Goal1) :- NonTerminal \= ( \+ _ ), loader:strip_module(NonTerminal, M, NonTerminal0), dcg_non_terminal(NonTerminal0, S0, S, Goal0), - module_call_qualified(M, Goal0, Goal1). + ( functor(NonTerminal, (:), 2) -> + Goal1 = M:Goal0 + ; Goal1 = Goal0 + ). % The following constructs in a grammar rule body % are defined in the corresponding subclauses. @@ -131,6 +172,9 @@ user:term_expansion(Term0, Term) :- nonvar(Term0), dcg_rule(Term0, Term). + +%% seq(Seq)// +% % Describes a sequence seq(Xs, Cs0,Cs) :- var(Xs), @@ -141,10 +185,14 @@ seq(Xs, Cs0,Cs) :- seq([]) --> []. seq([E|Es]) --> [E], seq(Es). +%% seqq(SeqOfSeqs)// +% % Describes a sequence of sequences seqq([]) --> []. seqq([Es|Ess]) --> seq(Es), seqq(Ess). +%% ...// +% % Describes an arbitrary number of elements ...(Cs0,Cs) :- Cs0 == [], @@ -163,6 +211,9 @@ user:goal_expansion(phrase(GRBody, S, S0), GRBody2) :- E, dcgs:error_goal(E, GRBody1) ), - module_call_qualified(M, GRBody1, GRBody2). + ( GRBody = (_:_) -> + GRBody2 = M:GRBody1 + ; GRBody2 = GRBody1 + ). user:goal_expansion(phrase(GRBody, S), phrase(GRBody, S, [])). diff --git a/src/lib/debug.pl b/src/lib/debug.pl index ab84f15b..e237d8e1 100644 --- a/src/lib/debug.pl +++ b/src/lib/debug.pl @@ -1,4 +1,22 @@ -% Source: https://stackoverflow.com/a/30791637 +/** Declarative debugging. + + This library provides three predicates with associated operators. + The operators can be placed in front of goals to debug Prolog + programs. + + Of these predicates, the most frequently used is `(*)/1`, with + associated prefix operator `*` (star). Placing `*` in front of a + goal means to _generalize away_ the goal. `* Goal` acts as if `Goal` + did not appear at all in the source code. It is declaratively + equivalent to _commenting out_ the goal, and easier to write, + because `*` can also be placed in front of the last goal in a clause + without any additional changes. + + Source: [https://stackoverflow.com/a/30791637](https://stackoverflow.com/a/30791637) + +*/ + + :- module(debug, [ op(900, fx, $), @@ -15,12 +33,25 @@ :- meta_predicate $(0). :- meta_predicate $-(0). +%% $-(Goal) +% +% Portray exceptions thrown by Goal. + $-(G_0) :- catch(G_0, Ex, ( portray_clause(exception:Ex:G_0), throw(Ex) ) ). +%% $(Goal) +% +% Provide a _trace_ for calls of Goal. + $(G_0) :- portray_clause(call:G_0), $-G_0, portray_clause(exit:G_0). +%% *(Goal) +% +% Generalize away Goal. + + *(_). diff --git a/src/lib/diag.pl b/src/lib/diag.pl index 0fa3896f..fc989b34 100644 --- a/src/lib/diag.pl +++ b/src/lib/diag.pl @@ -1,7 +1,160 @@ -:- module(diag, [wam_instructions/2]). +:- module(diag, [wam_instructions/2, inlined_instructions/2]). + +/** Diagnostics library + + The predicate `wam_instructions/2` _decompiles_ a predicate so that + we can inspect its Warren Abstract Machine (WAM) instructions. + In this way, we can verify and reason about compiled programs, + and detect opportunities for optimization. + + For example, we have: + +``` +?- use_module(library(lists)). + true. +?- use_module(library(diag)). + true. +?- use_module(library(format)). + true. +?- wam_instructions(append/3, Is), + maplist(portray_clause, Is). +switch_on_term(1,external(1),external(2),external(6),fail). +try_me_else(4). +get_constant(level(shallow),[],x(1)). +get_value(x(2),3). +proceed. +trust_me(0). +get_list(level(shallow),x(1)). +unify_variable(x(4)). +unify_variable(x(1)). +get_list(level(shallow),x(3)). +unify_value(x(4)). +unify_variable(x(3)). +execute(append,3). + Is = [switch_on_term(1,external(1),external(2),external(6),fail)|...]. +``` + + `inlined_instructions/2` decompiles predicates at the code offset in + its first argument. + + For example, given the program + +``` +?- [user]. +:- use_module(library(clpz)). + +all_eq(Vs, E) :- maplist(#=(E), Vs). + +``` + + we inspect the code of `all_eqs/2` using `wam_instructions/2`, + revealing: + +``` +?- wam_instructions(all_eq/2, Is), + maplist(portray_clause, Is). +put_structure('$aux',2,x(3)). +set_local_value(x(2)). +set_void(1). +set_constant('$index_ptr'(115334)). +get_variable(x(4),1). +put_structure(:,2,x(1)). +set_constant(user). +set_local_value(x(3)). +get_variable(x(5),2). +put_value(x(4),2). +execute(maplist,2). + Is = [put_structure('$aux',2,x(3)),set_local_value(x(2)),set_void(1),set_constant('$index_ptr'(115334)),get_variable(x(4),1),put_structure(:,2,x(1)),set_constant(user),set_local_value(x(3)),get_variable(x(5),2),put_value(x(4),2),execute(maplist,2)]. +``` + + The `'$index_ptr(115334)` functor gives a code offset to an inlined + predicate compiled for the use of maplist/2. `inlined_instructions/2` + can be used to decompile its source code: + +``` +?- inlined_instructions(115334, Is), + maplist(portray_clause, Is). +allocate(1). +get_level(y(1)). +get_variable(x(5),2). +put_value(x(3),2). +get_variable(x(6),3). +put_value(x(5),3). +put_unsafe_value(1,4). +deallocate. +jmp_by_execute(1). +try_me_else(8). +call(integer,1). +neck_cut. +get_variable(x(5),1). +put_value(x(2),1). +get_variable(x(6),2). +put_value(x(5),2). +jmp_by_execute(7). +try_me_else(12). +allocate(3). +get_level(y(1)). +get_variable(y(3),1). +get_variable(y(2),2). +call_default(true,0). +call(var,1). +cut(y(1)). +put_unsafe_value(3,1). +put_unsafe_value(2,2). +deallocate. +execute_default(is,2). +default_retry_me_else(4). +call(integer,1). +neck_cut. +execute(=:=,2). +default_trust_me(0). +allocate(2). +get_variable(y(1),1). +get_variable(y(2),3). +put_value(y(2),1). +call_default(is,2). +put_unsafe_value(2,1). +put_unsafe_value(1,2). +deallocate. +execute_default(clpz_equal,2). +default_retry_me_else(4). +call(integer,1). +neck_cut. +jmp_by_execute(29). +try_me_else(12). +allocate(3). +get_level(y(1)). +get_variable(y(3),1). +get_variable(y(2),2). +call_default(true,0). +call(var,1). +cut(y(1)). +put_unsafe_value(3,1). +put_unsafe_value(2,2). +deallocate. +execute_default(is,2). +default_trust_me(0). +allocate(2). +get_variable(y(2),1). +get_variable(y(1),3). +put_value(y(1),1). +call_default(is,2). +put_unsafe_value(2,1). +put_unsafe_value(1,2). +deallocate. +execute_default(clpz_equal,2). +default_trust_me(0). +execute_default(clpz_equal,2). + Is = [allocate(1),get_level(y(1)),get_variable(x(5),2),put_value(x(3),2),get_variable(x(6),3),put_value(x(5),3),put_unsafe_value(1,4),deallocate,jmp_by_execute(1),try_me_else(8),call(integer,1),neck_cut,get_variable(x(5),1),put_value(x(2),1),get_variable(x(6),2),put_value(x(5),2),jmp_by_execute(7),try_me_else(12),allocate(3),get_level(...),...]. +``` +*/ + :- use_module(library(error)). +%% wam_instructions(+PI, -Instrs) +% +% _Instrs_ are the WAM instructions corresponding to predicate indicator _PI_. wam_instructions(Clause, Listing) :- ( nonvar(Clause) -> @@ -13,6 +166,16 @@ wam_instructions(Clause, Listing) :- ; throw(error(instantiation_error, wam_instructions/2)) ). +%% inlined_instructions(+IndexPtr, -Instrs) +% +% _Instrs_ are the WAM instructions corresponding to code offset _IndexPtr_. + +inlined_instructions(IndexPtr, Listing) :- + must_be(integer, IndexPtr), + ( IndexPtr >= 0 -> + '$inlined_instructions'(IndexPtr, Listing) + ; throw(error(domain_error(not_less_than_zero, IndexPtr), inlined_instructions/2)) + ). fetch_instructions(Module, Name, Arity, Listing) :- must_be(atom, Module), diff --git a/src/lib/dif.pl b/src/lib/dif.pl index d9c475eb..73d65518 100644 --- a/src/lib/dif.pl +++ b/src/lib/dif.pl @@ -1,3 +1,8 @@ +/** +Provides predicate `dif/2`. `dif/2` is a constraint that is true only if both of its +arguments are different terms. +*/ + :- module(dif, [dif/2]). :- use_module(library(atts)). @@ -35,25 +40,39 @@ verify_attributes(Var, Value, Goals) :- ; Goals = [] ). -% Probably the world's worst dif/2 implementation. I'm open to -% suggestions for improvement. - +%% dif(?X, ?Y). +% +% True iff X and Y are different terms. Unlike `\=/2`, `dif/2` is more declarative because if X and Y can +% unify but they're not yet equal, the decision is delayed, and prevents X and Y to become equal later. +% Examples: +% +% ``` +% ?- dif(a, a). +% false. +% ?- dif(a, b). +% true. +% ?- dif(X, b). +% dif:dif(X,b). +% ?- dif(X, b), X = b. +% false. +% ``` dif(X, Y) :- X \== Y, ( X \= Y -> true - ; ( term_variables(X, XVars), - term_variables(Y, YVars), - dif_set_variables(XVars, X, Y), - dif_set_variables(YVars, X, Y) - ) + ; term_variables(dif(X,Y), Vars), + dif_set_variables(Vars, X, Y) ). -gather_dif_goals([]) --> []. -gather_dif_goals([(X \== Y) | Goals]) --> - [dif:dif(X, Y)], - gather_dif_goals(Goals). +gather_dif_goals(_, []) --> []. +gather_dif_goals(V, [(X \== Y) | Goals]) --> + ( { term_variables(X-Y, [V0 | _]), + V == V0 } -> + [dif:dif(X, Y)] + ; [] + ), + gather_dif_goals(V, Goals). attribute_goals(X) --> { get_atts(X, +dif(Goals)) }, - gather_dif_goals(Goals), + gather_dif_goals(X, Goals), { put_atts(X, -dif(_)) }. diff --git a/src/lib/error.pl b/src/lib/error.pl index 7efb170e..38a93214 100644 --- a/src/lib/error.pl +++ b/src/lib/error.pl @@ -1,5 +1,5 @@ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Written 2018-2022 by Markus Triska (triska@metalevel.at) + Written 2018-2023 by Markus Triska (triska@metalevel.at) I place this code in the public domain. Use it in any way you want. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ @@ -85,11 +85,11 @@ must_be_(list, Term) :- check_(error:ilist, list, Term). must_be_(type, Term) :- check_(error:type, type, Term). must_be_(boolean, Term) :- check_(error:boolean, boolean, Term). must_be_(term, Term) :- - ( \+ ground(Term) -> - instantiation_error(must_be/2) - ; \+ acyclic_term(Term) -> - type_error(term, Term, must_be/2) - ; true + ( acyclic_term(Term) -> + ( ground(Term) -> true + ; instantiation_error(must_be/2) + ) + ; type_error(term, Term, must_be/2) ). % We cannot use maplist(must_be(character), Cs), because library(lists) diff --git a/src/lib/ffi.pl b/src/lib/ffi.pl new file mode 100644 index 00000000..cce60ff6 --- /dev/null +++ b/src/lib/ffi.pl @@ -0,0 +1,104 @@ +:- module(ffi, [use_foreign_module/2, foreign_struct/2]). + +/** Foreign Function Interface + +This module contains predicates used to call native code (exposed by the C ABI). +It uses [libffi](https://sourceware.org/libffi/) under the hood. The bridge is very simple +and is very unsafe and should be used with care. FFI isn't the only way to communicate with +the outside world in Prolog: sockets, pipes and HTTP may be good enough for your use case. + +The main predicate is `use_foreign_module/2`. It takes a library name (which depending on the +operating system could be a `.so`, `.dylib` or `.dll` file). and a list of functions. Each +function is defined by its name, a list of the type of the arguments, and the return argument. + +Types available are: `sint8`, `uint8`, `sint16`, `uint16`, `sint32`, `uint32`, `sint64`, +`uint64`, `f32`, `f64`, `cstr`, `void`, `bool`, `ptr` and custom structs, which can be defined +with `foreign_struct/2`. + +After that, each function on the lists maps to a predicate created in the ffi module which +are used to call the native code. +The predicate takes the functor name after the function name. Then, the arguments are the input +arguments followed by a return argument. However, functions with return type `void` or `bool` +don't have that return argument. Predicates with `void` always succeed and `bool` predicates depend +on the return value on the native side. + +``` +ffi:FUNCTION_NAME(+InputArg1, ..., +InputArgN, -ReturnArg). % for all return types except void and bool +ffi:FUNCTION_NAME(+InputArg1, ..., +InputArgN). % for void and bool +``` + +## Example + +For example, let's see how to define a function from the [raylib](https://www.raylib.com/) library. + +``` +?- use_foreign_module("./libraylib.so", ['InitWindow'([sint32, sint32, cstr], void)]). +``` + +This creates a `'InitWindow'` predicate under the ffi module. Now, we can call it: + +``` +?- ffi:'InitWindow'(800, 600, "Scryer Prolog + Raylib"). +``` + +And a new window should pop up! +*/ + +:- use_module(library(lists)). +:- use_module(library(error)). + +%% foreign_struct(+Name, +Elements). +% +% Defines a new struct type with name Name, composed of the elements Elements, which is a list +% of other types. +% +% The name of the types doesn't matter, but the order of Elements must match the ones in the +% native code. +% +% Example: +% +% ``` +% ?- foreign_struct(color, [uint8, uint8, uint8, uint8]). +% ``` +foreign_struct(Name, Elements) :- + '$define_foreign_struct'(Name, Elements). + +use_foreign_module(LibName, Predicates) :- + '$load_foreign_lib'(LibName, Predicates), + maplist(assert_predicate, Predicates). + +assert_predicate(PredicateDefinition) :- + PredicateDefinition =.. [Name, Inputs, void], + length(Inputs, NumInputs), + functor(Head, Name, NumInputs), + term_variables(Head, TermList), + Body = ( + '$foreign_call'(Name, TermList, _),! + ), + Predicate = (Head:-Body), + assertz(ffi:Predicate). + +assert_predicate(PredicateDefinition) :- + PredicateDefinition =.. [Name, Inputs, bool], + length(Inputs, NumInputs), + functor(Head, Name, NumInputs), + term_variables(Head, TermList), + Body = ( + '$foreign_call'(Name, TermList, 1),! + ), + Predicate = (Head:-Body), + assertz(ffi:Predicate). + +assert_predicate(PredicateDefinition) :- + PredicateDefinition =.. [Name, Inputs, Return], + \+ member(Return, [void, bool]), + length(Inputs, NumInputs), + NumArgs is NumInputs + 1, + functor(Head, Name, NumArgs), + term_variables(Head, TermList), + Body = ( + lists:append(TermListInputs, [TermListReturn], TermList), + '$foreign_call'(Name, TermListInputs, TermListReturn),! + ), + Predicate = (Head:-Body), + assertz(ffi:Predicate). diff --git a/src/lib/files.pl b/src/lib/files.pl index ef73e851..6d89eaad 100644 --- a/src/lib/files.pl +++ b/src/lib/files.pl @@ -1,3 +1,22 @@ +/** Predicates for reasoning about files and directories. + +In this library, directories and files are represented as +_lists of characters_. This is an ideal representation: + +* Lists of characters can be conveniently reasoned about with DCGs + and built-in Prolog predicates from `library(lists)`. This alone + is already a very compelling argument to use them. +* Other Scryer libraries such as `library(http/http_open)` also already + use lists of characters to represent paths. +* File names are mostly ephemeral, so it is good for efficiency + that they can quickly allocated transiently on the heap, leaving the + atom table mostly unaffected. Indexing is almost never needed + for file names. If needed, it should be added to the engine. +* The previous point is also good for security, since the system + leaves little trace of which files were even accessed. +* Scryer Prolog represents lists of characters extremely compactly. +*/ + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Written 2020, 2022 by Markus Triska (triska@metalevel.at) Part of Scryer Prolog. @@ -51,8 +70,9 @@ file_exists/1, directory_exists/1, delete_file/1, - rename_file/2, - delete_directory/1, + rename_file/2, + file_copy/2, + delete_directory/1, make_directory/1, make_directory_path/1, working_directory/2, @@ -67,41 +87,82 @@ :- use_module(library(charsio)). :- use_module(library(dcgs)). +%% directory_files(+Directory, -Files). +% +% Returns the list of files *and* directories available at a specific +% directory in the current system. + directory_files(Directory, Files) :- must_be(chars, Directory), can_be(list, Files), '$directory_files'(Directory, Files). +%% file_size(+File, -Size). +% +% Returns the size (in bytes) of a file. The file must exist. + file_size(File, Size) :- file_must_exist(File, file_size/2), can_be(integer, Size), '$file_size'(File, Size). +%% file_exists(+File). +% +% Succeeds if File is a file that exists in the current system. file_exists(File) :- must_be(chars, File), '$file_exists'(File). +%% directory_exists(+Directory). +% +% Succeeds if Directory is a directory that exists in the current system. directory_exists(Directory) :- must_be(chars, Directory), '$directory_exists'(Directory). +%% make_directory(+Directory). +% +% Succeeds if it creates a new directory named Directory in the current system. +% If you want to create a nested directory, use `make_directory_path/1`. make_directory(Directory) :- must_be(chars, Directory), '$make_directory'(Directory). +%% make_directory_path(+Directory). +% +% Similar to `make_directory/1` but recursively creates directories if they're missing. +% Equivalent to mkdir -p in Unix. make_directory_path(Directory) :- must_be(chars, Directory), '$make_directory_path'(Directory). +%% delete_file(+File). +% +% Succeeds if deletes File from the current system. delete_file(File) :- file_must_exist(File, delete_file/1), '$delete_file'(File). +%% rename_file(+File, +Renamed). +% +% Succeeds if File is renamed to Renamed rename_file(File, Renamed) :- file_must_exist(File, rename_file/2), must_be(chars, Renamed), '$rename_file'(File, Renamed). +%% file_copy(+File, +Copied). +% +% Succeeds if File is copied to Copied +file_copy(File, Copied) :- + file_must_exist(File, file_copy/2), + must_be(chars, Copied), + '$file_copy'(File, Copied). + +%% delete_directory(+Directory). +% +% Succeeds if Directory is deleted from the current system. +% Directory must be empty. delete_directory(Directory) :- directory_must_exist(Directory, delete_directory/1), must_be(chars, Directory), @@ -117,31 +178,31 @@ directory_must_exist(Directory, Context) :- ; throw(error(existence_error(directory, Directory), Context)) ). -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Dir0 is the current working directory, and the working directory - is changed to Dir. - - Use working_directory(Ds, Ds) to determine the current working directory, - and leave it as is. -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +%% workind_directory(Dir0, Dir). +% +% Dir0 is the current working directory, and the working directory +% is changed to Dir. +% +% Use `working_directory/2` to determine the current working directory, +% and leave it as is. working_directory(Dir0, Dir) :- can_be(list, Dir0), can_be(list, Dir), '$working_directory'(Dir0, Dir). -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - True iff Cs is the canonical, absolute path of Ps. - - All intermediate components are normalized, and all symbolic links - are resolved. - - The predicate fails in the following situations, though not - necessarily *only* in these cases: - - 1. Ps is a path that does not exist. - 2. A non-final component in Ps is not a directory. -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +%% path_canonical(Ps, Cs). +% +% True iff Cs is the canonical, absolute path of Ps. +% +% All intermediate components are normalized, and all symbolic links +% are resolved. +% +% The predicate fails in the following situations, though not +% necessarily *only* in these cases: +% +% 1. Ps is a path that does not exist. +% 2. A non-final component in Ps is not a directory. path_canonical(Ps, Cs) :- must_be(chars, Ps), @@ -155,12 +216,27 @@ path_canonical(Ps, Cs) :- For two time stamps A and B, if A precedes B, then A @< B holds. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +%% file_modification_time(+File, -T). +% +% For a file File that must exist, it returns a time stamp T with the modification time +% +% T is a time stamp compatible with `library(time)`. file_modification_time(File, T) :- file_time_(File, modification, T). +%% file_access_time(+File, -T). +% +% For a file File that must exist, it returns a time stamp T with the access time +% +% T is a time stamp compatible with `library(time)`. file_access_time(File, T) :- file_time_(File, access, T). +%% file_creation_time(+File, -T). +% +% For a file File that must exist, it returns a time stamp T with the creation time +% +% T is a time stamp compatible with `library(time)`. file_creation_time(File, T) :- file_time_(File, creation, T). @@ -170,29 +246,31 @@ file_time_(File, Which, T) :- read_from_chars(T0, T). -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - path_segments(Ps, Segments): True iff Segments are the segments of Ps. - - Segments is the list of components of the path Ps that are - separated by the platform-specific directory separator. Each - segment is a list of characters. - - At least one of the arguments must be instantiated. - - Examples: - - ?- path_segments("/hello/there", Segments). - Segments = [[],"hello","there"]. - - ?- path_segments(Path, ["hello","there"]). - Path = "hello/there". - - - To obtain the platform-specific directory separator, you can use: - - ?- path_segments(Separator, ["",""]). - Separator = "/". -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +%% path_segments(Ps, Segments). +% +% True iff Segments are the segments of Ps. +% +% Segments is the list of components of the path Ps that are +% separated by the platform-specific directory separator. Each +% segment is a list of characters. +% +% At least one of the arguments must be instantiated. +% +% Examples: +% +% ``` +% ?- path_segments("/hello/there", Segments). +% Segments = [[],"hello","there"]. +% ?- path_segments(Path, ["hello","there"]). +% Path = "hello/there". +% ``` +% +% To obtain the platform-specific directory separator, you can use: +% +% ``` +% ?- path_segments(Separator, ["",""]). +% Separator = "/". +% ``` path_segments(Path, Segments) :- '$directory_separator'(Sep), diff --git a/src/lib/format.pl b/src/lib/format.pl index fd2a9816..32ad2ff9 100644 --- a/src/lib/format.pl +++ b/src/lib/format.pl @@ -1,83 +1,17 @@ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Written 2020, 2021, 2022 by Markus Triska (triska@metalevel.at) + Written 2020-2023 by Markus Triska (triska@metalevel.at) Part of Scryer Prolog. - - This library provides the nonterminal format_//2 to describe - formatted strings. format/[2,3] are provided for impure output. - - Usage: - ====== - - phrase(format_(FormatString, Arguments), Ls) - - format_//2 describes a list of characters Ls that are formatted - according to FormatString. FormatString is a string (i.e., - a list of characters) that specifies the layout of Ls. - The characters in FormatString are used literally, except - for the following tokens with special meaning: - - ~w use the next available argument from Arguments here - ~q use the next argument here, formatted as by writeq/1 - ~a use the next argument here, which must be an atom - ~s use the next argument here, which must be a string - ~d use the next argument here, which must be an integer - ~f use the next argument here, a floating point number - ~Nf where N is an integer: format the float argument - using N digits after the decimal point - ~Nd like ~d, placing the last N digits after a decimal point; - if N is 0 or omitted, no decimal point is used. - ~ND like ~Nd, separating digits to the left of the decimal point - in groups of three, using the character "," (comma) - ~NU like ~ND, using "_" (underscore) to separate groups of digits - ~NL format an integer so that at most N digits appear on a line. - If N is 0 or omitted, it defaults to 72. - ~Nr where N is an integer between 2 and 36: format the - next argument, which must be an integer, in radix N. - The characters "a" to "z" are used for radices 10 to 36. - If N is omitted, it defaults to 8 (octal). - ~NR like ~Nr, except that "A" to "Z" are used for radices > 9 - ~| place a tab stop at this position - ~N| where N is an integer: place a tab stop at text column N - ~N+ where N is an integer: place a tab stop N characters - after the previous tab stop (or start of line) - ~t distribute spaces evenly between the two closest tab stops - ~`Ct like ~t, use character C instead of spaces to fill the space - ~n newline - ~Nn N newlines - ~i ignore the next argument - ~~ the literal ~ - - Instead of ~N, you can write ~* to use the next argument from Arguments - as the numeric argument. - - The predicate format/2 is like format_//2, except that it outputs - the text on the terminal instead of describing it declaratively. - - format/3, used as format(Stream, FormatString, Arguments), outputs - the described string to the given Stream. If Stream is a binary - stream, then the code of each emitted character must be in 0..255. - - If at all possible, format_//2 should be used, to stress pure parts - that enable easy testing etc. If necessary, you can emit the list Ls - with maplist(put_char, Ls) or, much faster, with format("~s", [Ls]). - Ideally, however, you use phrase_to_file/[2,3] or phrase_to_stream/2 - from library(pio) to write the described list directly to a file - or stream, respectively: phrase_to_stream(format_(..., [...]), S). - The advantage of this is that an ideal implementation writes - the characters as they become known, without manifesting the list. - - The entire library only works if the Prolog flag double_quotes - is set to chars, the default value in Scryer Prolog. This should - also stay that way, to encourage a sensible environment. - - Example: - - ?- phrase(format_("~s~n~`.t~w!~12|", ["hello",there]), Cs). - %@ Cs = "hello\n......there!". - I place this code in the public domain. Use it in any way you want. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/** This library provides the nonterminal `format_//2` to describe + formatted strings. `format/[2,3]` are provided for _impure_ output. + + The entire library only works if the Prolog flag `double_quotes` + is set to `chars`, the default value in Scryer Prolog. This should + also stay that way, to encourage a sensible environment. +*/ + :- module(format, [format_//2, format/2, format/3, @@ -94,6 +28,61 @@ :- use_module(library(between)). :- use_module(library(pio)). +%% format_(+FormatString, +Arguments)// +% +% Usage: +% +% ``` +% phrase(format_(FormatString, Arguments), Ls) +% ``` +% +% `format_//2` describes a list of characters Ls that are formatted +% according to FormatString. FormatString is a string (i.e., a list of +% characters) that specifies the layout of Ls. The characters in +% FormatString are used literally, except for the following tokens +% with special meaning: +% +% | `~w` | use the next available argument from Arguments here | +% | `~q` | use the next argument here, formatted as by `writeq/1` | +% | `~a` | use the next argument here, which must be an atom | +% | `~s` | use the next argument here, which must be a string | +% | `~d` | use the next argument here, which must be an integer | +% | `~f` | use the next argument here, a floating point number | +% | `~Nf` | where N is an integer: format the float argument | +% | | using N digits after the decimal point | +% | `~Nd` | like ~d, placing the last N digits after a decimal point; | +% | | if N is 0 or omitted, no decimal point is used. | +% | `~ND` | like ~Nd, separating digits to the left of the decimal point | +% | | in groups of three, using the character "," (comma) | +% | `~NU` | like ~ND, using "_" (underscore) to separate groups of digits | +% | `~NL` | format an integer so that at most N digits appear on a line. | +% | | If N is 0 or omitted, it defaults to 72. | +% | `~Nr` | where N is an integer between 2 and 36: format the | +% | | next argument, which must be an integer, in radix N. | +% | | The characters "a" to "z" are used for radices 10 to 36. | +% | | If N is omitted, it defaults to 8 (octal). | +% | `~NR` | like ~Nr, except that "A" to "Z" are used for radices > 9 | +% | `~|` | place a tab stop at this position | +% | `~N|` | where N is an integer: place a tab stop at text column N | +% | `~N+` | where N is an integer: place a tab stop N characters | +% | | after the previous tab stop (or start of line) | +% | `~t` | distribute spaces evenly between the two closest tab stops | +% | ``~`Ct`` | like ~t, use character C instead of spaces to fill the space | +% | `~n` | newline | +% | `~Nn` | N newlines | +% | `~i` | ignore the next argument | +% | `~~` | the literal ~ | +% +% Instead of `~N`, you can write `~*` to use the next argument from +% Arguments as the numeric argument. +% +% Example: +% +% ``` +% ?- phrase(format_("~s~n~`.t~w!~12|", ["hello",there]), Cs). +% Cs = "hello\n......there!". +% ``` + format_(Fs, Args) --> { must_be(list, Fs), must_be(list, Args), @@ -414,10 +403,32 @@ digits(uppercase, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"). Impure I/O, implemented as a small wrapper over format_//2. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +%% format(+Fs, +Args) +% +% The predicate `format/2` is like `format_//2`, except that it +% outputs the text on the terminal instead of describing it +% declaratively as a list of characters. +% +% If at all possible, `format_//2` should be used, to stress pure +% parts that enable easy testing etc. If necessary, you can emit the +% described list of characters `Ls` with `maplist(put_char, Ls)` or, +% much faster, with `format("~s", [Ls])`. Ideally, however, you use +% `phrase_to_file/[2,3]` or `phrase_to_stream/2` from `library(pio)` +% to write the described list directly to a file or stream, +% respectively: `phrase_to_stream(format_(..., [...]), S)`. The +% advantage of this is that an ideal implementation writes the +% characters as they become known, without manifesting the list. + format(Fs, Args) :- current_output(Stream), format(Stream, Fs, Args). +%% format(Stream, FormatString, Arguments) +% +% Output the described string to the given Stream. If Stream is a +% binary stream, then the code of each emitted character must be in +% 0..255. + format(Stream, Fs, Args) :- phrase_to_stream(format_(Fs, Args), Stream), flush_output(Stream). @@ -486,11 +497,14 @@ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa In the eventual library organization, portray_clause/1 and related predicates may be placed in their own dedicated library. - - portray_clause/1 is useful for printing solutions in such a way - that they can be read back with read/1. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +%% portray_clause(+Term) +% +% `portray_clause/1` is useful for printing solutions in such a way +% that they can be read back with `read/1`. + portray_clause(Term) :- current_output(Out), portray_clause(Out, Term). diff --git a/src/lib/freeze.pl b/src/lib/freeze.pl index b9f8005d..218a2532 100644 --- a/src/lib/freeze.pl +++ b/src/lib/freeze.pl @@ -1,5 +1,8 @@ :- module(freeze, [freeze/2]). +/** Provides the constraint `freeze/2`. +*/ + :- use_module(library(atts)). :- use_module(library(dcgs)). @@ -19,6 +22,15 @@ verify_attributes(Var, Other, Goals) :- ). verify_attributes(_, _, []). +%% freeze(Var, Goal) +% +% Schedules Goal to be executed when Var is instantiated. This can +% be useful to observe the exact moment a variable becomes bound to a +% more concrete term, for example when creating animations of search +% processes. Higher-level constructs such as `phrase_from_file/2` can +% also be implemented with `freeze/2`, by scheduling a goal that +% reads additional data from a file as soon as it is needed. + freeze(X, Goal) :- put_atts(Fresh, frozen(Goal)), Fresh = X. @@ -26,5 +38,5 @@ freeze(X, Goal) :- attribute_goals(Var) --> { get_atts(Var, frozen(Goals)), put_atts(Var, -frozen(_)) }, - [freeze(Var, Goals)]. + [freeze:freeze(Var, Goals)]. diff --git a/src/lib/gensym.pl b/src/lib/gensym.pl index 92cd4d1f..272e68bd 100644 --- a/src/lib/gensym.pl +++ b/src/lib/gensym.pl @@ -19,14 +19,14 @@ gensym(Base, Unique) :- must_be(var, Unique), atom_si(Base), gensym_key(Base, BaseKey), - ( bb_get(BaseKey, UniqueID0) -> - UniqueID is UniqueID0 + 1, - bb_put(BaseKey, UniqueID), - append_id(Base, UniqueID, Unique) - ; bb_put(BaseKey, 1), - append_id(Base, 1, Unique) - ). + ( bb_get(BaseKey, UniqueID0) -> true + ; UniqueID0 = 0 + ), + UniqueID is UniqueID0 + 1, + append_id(Base, UniqueID, Unique), + bb_put(BaseKey, UniqueID). reset_gensym(Base) :- atom_si(Base), - bb_put(Base, 0). + gensym_key(Base, BaseKey), + bb_put(BaseKey, 0). diff --git a/src/lib/http/http_open.pl b/src/lib/http/http_open.pl index 1c89b5a5..17dc0088 100644 --- a/src/lib/http/http_open.pl +++ b/src/lib/http/http_open.pl @@ -1,34 +1,39 @@ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Written 2022 by Adrián Arroyo Calle (adrian.arroyocalle@gmail.com) Part of Scryer Prolog. +*/ - http_open(+Address, -Stream, +Options) - ====================================== +/** Make HTTP requests. - Yields Stream to read the body of an HTTP reply from Address. - Address is a list of characters, and includes the method. Both HTTP - and HTTPS are supported. - - Options supported: - - * method(+Method): Sets the HTTP method of the call. Method can be get (default), head, delete, post, put or patch. - * data(+Data): Data to be sent in the request. Useful for POST, PUT and PATCH operations. - * size(-Size): Unifies with the value of the Content-Length header - * request_headers(+RequestHeaders): Headers to be used in the request - * headers(-ListHeaders): Unifies with a list with all headers returned in the response - * status_code(-Code): Unifies with the status code of the request (200, 201, 404, ...) - - Example: - - ?- http_open("https://github.com/mthom/scryer-prolog", S, []). - %@ S = '$stream'(0x7fcfc9e00f00). - -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +This library contains the predicate `http_open/3` which allows you to perform HTTP(S) calls. +Useful for making API calls, or parsing websites. It uses Hyper underneath. +*/ :- module(http_open, [http_open/3]). :- use_module(library(lists)). +%% http_open(+Address, -Stream, +Options). +% +% Yields Stream to read the body of an HTTP reply from Address. +% Address is a list of characters, and includes the method. Both HTTP +% and HTTPS are supported. +% +% Options supported: +% +% * `method(+Method)`: Sets the HTTP method of the call. Method can be `get` (default), `head`, `delete`, `post`, `put` or `patch`. +% * `data(+Data)`: Data to be sent in the request. Useful for POST, PUT and PATCH operations. +% * `size(-Size)`: Unifies with the value of the Content-Length header +% * `request_headers(+RequestHeaders)`: Headers to be used in the request +% * `headers(-ListHeaders)`: Unifies with a list with all headers returned in the response +% * `status_code(-Code)`: Unifies with the status code of the request (200, 201, 404, ...) +% +% Example: +% +% ``` +% ?- http_open("https://www.example.com", S, []), get_n_chars(S, N, HTML). +% S = '$stream'(0x7fb548001be8), N = 1256, HTML = "\n true; Method = get), @@ -65,4 +70,4 @@ parse_http_options_(request_headers(Headers), request_headers(Headers)) :- parse_http_options_(size(Size), size(Size)). parse_http_options_(status_code(Code), status_code(Code)). -parse_http_options_(headers(Headers), headers(Headers)). \ No newline at end of file +parse_http_options_(headers(Headers), headers(Headers)). diff --git a/src/lib/http/http_server.pl b/src/lib/http/http_server.pl index 0f467fd4..381d5607 100644 --- a/src/lib/http/http_server.pl +++ b/src/lib/http/http_server.pl @@ -1,51 +1,55 @@ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Written in December 2020 by Adrián Arroyo (adrian.arroyocalle@gmail.com) Updated in March 2022 by Adrián Arroyo to use the Hyper backend - Part of Scryer Prolog + Part of Scryer Prolog. + I place this code in the public domain. Use it in any way you want. +*/ - This library provides an starting point to build HTTP server based applications. - It is based on Hyper, which allows for HTTP/1.0, HTTP/1.1 and HTTP/2. However, - some advanced features that Hyper provides are still not accesible. +/** This library provides an starting point to build HTTP server based applications. +It is based on [Hyper](https://hyper.rs/), which allows for HTTP/1.0, HTTP/1.1 and HTTP/2. However, +some advanced features that Hyper provides are still not accesible. - Usage - ========== - The main predicate of the library is http_listen/2, which needs a port number - (usually 80) and a list of handlers. A handler is a compound term with the functor - as one HTTP method (in lowercase) and followed by a Route Match and a predicate - which will handle the call. +## Usage - text_handler(Request, Response) :- - http_status_code(Response, 200), - http_body(Response, text("Welcome to Scryer Prolog!")). +The main predicate of the library is `http_listen/2`, which needs a port number +(usually 80) and a list of handlers. A handler is a compound term with the functor +as one HTTP method (in lowercase) and followed by a Route Match and a predicate +which will handle the call. - parameter_handler(User, Request, Response) :- - http_body(Response, text(User)). +``` +text_handler(Request, Response) :- + http_status_code(Response, 200), + http_body(Response, text("Welcome to Scryer Prolog!")). - http_listen(7890, [ - get(echo, text_handler), % GET /echo - post(user/User, parameter_handler(User)) % POST /user/ - ]). +parameter_handler(User, Request, Response) :- + http_body(Response, text(User)). - Every handler predicate will have at least 2-arity, with Request and Response. - Although you can work directly with http_request and http_response terms, it is - recommeded to use the helper predicates, which are easier to understand and cleaner: - - http_headers(Response/Request, Headers) - - http_status_code(Responde, StatusCode) - - http_body(Response/Request, text(Body)) - - http_body(Response/Request, binary(Body)) - - http_body(Request, form(Form)) - - http_body(Response, file(Filename)) - - http_redirect(Response, Url) - - http_query(Request, QueryName, QueryValue) +http_listen(7890, [ + get(echo, text_handler), % GET /echo + post(user/User, parameter_handler(User)) % POST /user/ +]). +``` + +Every handler predicate will have at least 2-arity, with Request and Response. +Although you can work directly with `http_request` and `http_response` terms, it is +recommeded to use the helper predicates, which are easier to understand and cleaner: + + - `http_headers(Response/Request, Headers)` + - `http_status_code(Responde, StatusCode)` + - `http_body(Response/Request, text(Body))` + - `http_body(Response/Request, binary(Body))` + - `http_body(Request, form(Form))` + - `http_body(Response, file(Filename))` + - `http_redirect(Response, Url)` + - `http_query(Request, QueryName, QueryValue)` + +Some things that are still missing: - Some things that are still missing: - Read forms in multipart format - HTTP Basic Auth - Session handling via cookies - - HTML Templating - - I place this code in the public domain. Use it in any way you want. -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + - HTML Templating (but you can use [Teruel](https://github.com/aarroyoc/teruel/), [Marquete](https://github.com/aarroyoc/marquete/) or [Djota](https://github.com/aarroyoc/djota) for that) +*/ :- module(http_server, [ @@ -68,6 +72,11 @@ :- use_module(library(pio)). :- use_module(library(time)). +%% http_listen(+Port, +Handlers). +% +% Listens for HTTP connections on port Port. Each handler on the list Handlers should be of the form: `HttpVerb(PathUnification, Predicate)`. +% For example: `get(user/User, get_info(User))` will match an HTTP request that is a GET, the path unifies with /user/User (where User is a variable) +% and it will call `get_info` with three arguments: an `http_request` term, an `http_response` term and User. http_listen(Port, Module:Handlers0) :- must_be(integer, Port), must_be(list, Handlers0), @@ -112,37 +121,44 @@ http_loop(HttpListener, Handlers) :- send_response(ResponseHandle, http_response(StatusCode0, text(ResponseText), ResponseHeaders0)) :- default(StatusCode0, 200, StatusCode), maplist(map_header_kv_2, ResponseHeaders, ResponseHeaders0), - '$http_answer'(ResponseHandle, StatusCode, ResponseHeaders, ResponseStream), - call_cleanup( - format(ResponseStream, "~s", [ResponseText]), - close(ResponseStream) + '$http_answer'(ResponseHandle, StatusCode, ResponseHeaders, ResponseStream0), + open(stream(ResponseStream0), write, ResponseStream, [type(text)]), + catch( + call_cleanup(format(ResponseStream, "~s", [ResponseText]),close(ResponseStream)), + error(existence_error(stream, _), _), + true ). send_response(ResponseHandle, http_response(StatusCode0, bytes(ResponseBytes), ResponseHeaders0)) :- default(StatusCode0, 200, StatusCode), maplist(map_header_kv_2, ResponseHeaders, ResponseHeaders0), '$http_answer'(ResponseHandle, StatusCode, ResponseHeaders, ResponseStream), - call_cleanup( - format(ResponseStream, "~s", [ResponseBytes]), - close(ResponseStream) + catch( + call_cleanup(format(ResponseStream, "~s", [ResponseBytes]),close(ResponseStream)), + error(existence_error(stream, _), _), + true ). send_response(ResponseHandle, http_response(StatusCode0, file(Filename), ResponseHeaders0)) :- default(StatusCode0, 200, StatusCode), maplist(map_header_kv_2, ResponseHeaders, ResponseHeaders0), '$http_answer'(ResponseHandle, StatusCode, ResponseHeaders, ResponseStream), - call_cleanup( - setup_call_cleanup( - open(Filename, read, FileStream, [type(binary)]), - ( - get_n_chars(FileStream, _, FileCs), - format(ResponseStream, "~s", [FileCs]) + catch( + call_cleanup( + setup_call_cleanup( + open(Filename, read, FileStream, [type(binary)]), + ( + get_n_chars(FileStream, _, FileCs), + format(ResponseStream, "~s", [FileCs]) + ), + close(FileStream) ), - close(FileStream) + close(ResponseStream) ), - close(ResponseStream) + error(existence_error(stream, _), _), + true ). - + default(Var, Default, Out) :- (var(Var) -> Out = Default @@ -206,9 +222,21 @@ string_without(Not, [Char|String]) --> string_without(_, []) --> []. +%% http_headers(?Request_Response, ?Headers). +% +% True iff `Request_Response` is a request or response with headers Headers. Can be used both to get headers (usually in from a request) +% and to add headers (usually in a response). http_headers(http_request(Headers, _, _), Headers). http_headers(http_response(_, _, Headers), Headers). +%% http_body(?Request_Response, ?Body). +% +% True iff Body is the body of the request or response. A body can be of the following types: +% +% * `bytes(Bytes)` for both requests and responses, interprets the body as bytes +% * `text(Bytes)` for both requests and responses, interprets the body as text +% * `form(Form)` only for requests, interprets the body as an `application/x-www-form-urlencoded` form. +% * `file(File)` only for responses, interprets the body as the content of a file (useful to send static files). http_body(http_request(_, stream(StreamBody), _), bytes(BytesBody)) :- get_n_chars(StreamBody, _, BytesBody). http_body(http_request(_, stream(StreamBody), _), text(TextBody)) :- get_n_chars(StreamBody, _, TextBody). http_body(http_request(Headers, stream(StreamBody), _), form(FormBody)) :- @@ -218,8 +246,19 @@ http_body(http_request(Headers, stream(StreamBody), _), form(FormBody)) :- http_body(http_request(_, Body, _), Body). http_body(http_response(_, Body, _), Body). +%% http_status_code(?Response, ?StatusCode). +% +% True iff the status code of the response Response unifies with StatusCode. http_status_code(http_response(StatusCode, _, _), StatusCode). + +%% http_redirect(-Response, +Uri). +% +% True iff Response is a response that redirects the user to the uri Uri. http_redirect(http_response(307, text("Moved Temporarily"), ["Location"-Uri]), Uri). + +%% http_query(+Request, ?Key, ?Value). +% +% True iff there's a query in request Request with key Key and value Value. http_query(http_request(_, _, Queries), Key, Value) :- member(Key-Value, Queries). parse_queries([Key-Value|Queries]) --> diff --git a/src/lib/iso_ext.pl b/src/lib/iso_ext.pl index 26b4713c..afc28969 100644 --- a/src/lib/iso_ext.pl +++ b/src/lib/iso_ext.pl @@ -1,3 +1,9 @@ +/** Useful general predicates that are not ISO standard yet + +Predicates available here are similar to the ones defined in builtin.pl, +but they're not part of the ISO Prolog standard at the moment. +*/ + :- module(iso_ext, [bb_b_put/2, bb_get/2, bb_put/2, @@ -9,9 +15,10 @@ partial_string_tail/2, setup_call_cleanup/3, call_nth/2, + countall/2, copy_term_nat/2, - asserta/2, - assertz/2]). + asserta/2, + assertz/2]). :- use_module(library(error), [can_be/2, domain_error/3, @@ -22,25 +29,82 @@ :- meta_predicate(forall(0, 0)). +%% forall(Generate, Test). +% +% For all bindings possible by Generate, Test must be true. +% +% In this example, it checks that all numbers are even: +% +% ``` +% ?- Ns = [2,4,6], forall(member(N, Ns), 0 is N mod 2). +% Ns = [2,4,6]. +% ``` forall(Generate, Test) :- \+ (Generate, \+ Test). -%% (non-)backtrackable global variables. +% (non-)backtrackable global variables. +%% bb_put(+Key, +Value). +% +% Sets a global variable named Key (must be an atom) with value Value. +% The global variable isn't backtrackable. Check `bb_b_put/2` for the +% backtrackable version. +% +% ``` +% ?- bb_put(city, "Valladolid"). +% true. +% ?- bb_get(city, X). +% X = "Valladolid". +% ``` +% +% In this example one can understand the difference between `bb_put/2` and +% `bb_b_put/2`: +% +% ``` +% ?- bb_put(city, "Valladolid"), (bb_put(city, "Salamanca"), false);(bb_get(city, X)). +% X = "Salamanca". +% ?- bb_put(city, "Valladolid"), (bb_b_put(city, "Salamanca"), false);(bb_get(city, X)). +% X = "Valladolid". +% ``` bb_put(Key, Value) :- ( atom(Key) -> '$store_global_var'(Key, Value) ; type_error(atom, Key, bb_put/2) ). -%% backtrackable global variables. +% backtrackable global variables. +%% bb_b_put(+Key, +Value). +% +% Sets a global variable named Key (must be an atom) with value Value. +% The global variable is backtrackable. Check `bb_put/2` for the +% non-backtrackable version. +% +% ``` +% ?- bb_b_put(city, "Valladolid"). +% true. +% ?- bb_get(city, X). +% X = "Valladolid". +% ``` +% +% In this example one can understand the difference between `bb_put/2` and +% `bb_b_put/2`: +% +% ``` +% ?- bb_put(city, "Valladolid"), (bb_put(city, "Salamanca"), false);(bb_get(city, X)). +% X = "Salamanca". +% ?- bb_put(city, "Valladolid"), (bb_b_put(city, "Salamanca"), false);(bb_get(city, X)). +% X = "Valladolid". +% ``` bb_b_put(Key, Value) :- ( atom(Key) -> '$store_backtrackable_global_var'(Key, Value) ; type_error(atom, Key, bb_b_put/2) ). +%% bb_get(+Key, -Value). +% +% Gets the value Value of a global variable named Key (must be an atom) bb_get(Key, Value) :- ( atom(Key) -> '$fetch_global_var'(Key, Value) @@ -52,17 +116,30 @@ bb_get(Key, Value) :- :- meta_predicate(call_cleanup(0, 0)). +%% call_cleanup(Goal, Cleanup). +% +% Executes Goal and then, either on success or failure, executes Cleanup. +% The success or failure of Cleanup is ignored and choice points created inside are destroyed. call_cleanup(G, C) :- setup_call_cleanup(true, G, C). :- meta_predicate(setup_call_cleanup(0, 0, 0)). :- non_counted_backtracking setup_call_cleanup/3. +%% setup_call_cleanup(Setup, Goal, Cleanup). +% +% If Setup succeeds, Cleanup will be called after the execution of Goal. Goal itself can succeed or not. +% +% In this example, we use the predicate to always close an open file: +% +% ``` +% ?- setup_call_cleanup(open(File, read, Stream), do_something_with_stream(Stream), close(Stream)). +% ``` setup_call_cleanup(S, G, C) :- '$get_b_value'(B), '$call_with_inference_counting'(call(S)), '$set_cp_by_default'(B), - '$get_current_block'(Bb), + '$get_current_scc_block'(Bb), ( C = _:CC, var(CC) -> instantiation_error(setup_call_cleanup/3) @@ -75,17 +152,16 @@ setup_call_cleanup(S, G, C) :- scc_helper(C, G, Bb) :- '$get_cp'(Cp), - '$install_scc_cleaner'(C, NBb), + '$install_scc_cleaner'(C), '$call_with_inference_counting'(call(G)), ( '$check_cp'(Cp) -> - '$reset_block'(Bb), + '$reset_scc_block'(Bb), run_cleaners_without_handling(Cp) ; true - ; '$reset_block'(NBb), - '$fail' + ; '$fail' ). scc_helper(_, _, Bb) :- - '$reset_block'(Bb), + '$reset_scc_block'(Bb), '$push_ball_stack', run_cleaners_with_handling, '$pop_from_ball_stack', @@ -99,7 +175,7 @@ scc_helper(_, _, _) :- run_cleaners_with_handling :- '$get_scc_cleaner'(C), - '$get_level'(B), + '$get_cp'(B), catch(C, _, true), '$set_cp_by_default'(B), run_cleaners_with_handling. @@ -110,7 +186,7 @@ run_cleaners_with_handling :- run_cleaners_without_handling(Cp) :- '$get_scc_cleaner'(C), - '$get_level'(B), + '$get_cp'(B), call(C), '$set_cp_by_default'(B), run_cleaners_without_handling(Cp). @@ -144,6 +220,9 @@ handle_ile(B, _, _) :- :- non_counted_backtracking call_with_inference_limit/3. +%% call_with_inference_limit(Goal, Limit, Result). +% +% Similar to `call(Goal)` but it limits the number of inferences for each solution of Goal. call_with_inference_limit(G, L, R) :- ( integer(L) -> ( L < 0 -> @@ -179,13 +258,17 @@ call_with_inference_limit(_, _, R, Bb, B) :- '$remove_inference_counter'(B, _), ( '$get_ball'(Ball), '$push_ball_stack', - '$get_level'(Cp), + '$get_cp'(Cp), '$set_cp_by_default'(Cp) ; '$remove_call_policy_check'(B), '$fail' ), handle_ile(B, Ball, R). +%% partial_string(String, L, L0) +% +% Explicitly construct a partial string "manually". It can be used as an optimized append/3. +% It's not recommended to use this predicate in application code. partial_string(String, L, L0) :- ( String == [] -> L = L0 @@ -195,9 +278,17 @@ partial_string(String, L, L0) :- '$create_partial_string'(Atom, L, L0) ). +%% partial_string(+String) +% +% Succeeds if String is a _partial string_. A partial string is a string composed of several smaller +% strings, even just one. That means all strings in Scryer are partial strings. partial_string(String) :- '$is_partial_string'(String). +%% partial_string_tail(+String, -Tail). +% +% Unifies Tail with the last section of the partial string. +% It's not recommended to use this predicate in application code. partial_string_tail(String, Tail) :- ( partial_string(String) -> '$partial_string_tail'(String, Tail) @@ -209,6 +300,9 @@ partial_string_tail(String, Tail) :- :- meta_predicate(call_nth(0, ?)). +%% call_nth(Goal, N). +% +% Succeeds when Goal succeeded for the Nth time (there are at least N solutions) call_nth(Goal, N) :- can_be(integer, N), ( integer(N) -> @@ -246,17 +340,55 @@ call_nth_nesting(C, ID) :- bb_put(ID, 0), bb_put(i_call_nth_counter, C). +%% countall(Goal, N). +% +% countall(Goal, N) counts all solutions of Goal and unifies N with +% this number of solutions. This predicate always succeeds once. +:- meta_predicate(countall(0, ?)). + +countall(Goal, N) :- + can_be(integer, N), + ( integer(N) -> + ( N < 0 -> + domain_error(not_less_than_zero, N, countall/2) + ; N > 0 + ) + ; true + ), + setup_call_cleanup(call_nth_nesting(C, ID), + ( ( Goal, + bb_get(ID, N0), + N1 is N0 + 1, + bb_put(ID, N1), + false + ; bb_get(ID, N) + ) + ), + ( bb_get(i_call_nth_counter, C) -> + C1 is C - 1, + bb_put(i_call_nth_counter, C1) + ; true + )). + +%% copy_term_nat(Source, Dest) +% +% Similar to `copy_term/2` but without attribute variables copy_term_nat(Source, Dest) :- '$copy_term_without_attr_vars'(Source, Dest). - +%% asserta(Module, Rule_Fact). +% +% Similar to `asserta/1` but allows specifying a Module asserta(Module, (Head :- Body)) :- !, '$asserta'(Module, Head, Body). asserta(Module, Fact) :- '$asserta'(Module, Fact, true). +%% assertz(Module, Rule_Fact). +% +% Similar to `assertz/1` but allows specifying a Module assertz(Module, (Head :- Body)) :- !, '$assertz'(Module, Head, Body). diff --git a/src/lib/lambda.pl b/src/lib/lambda.pl index 56332255..d4aacaa5 100644 --- a/src/lib/lambda.pl +++ b/src/lib/lambda.pl @@ -50,11 +50,13 @@ programming based on call/N. Lambda expressions are represented by ordinary Prolog terms. There are two kinds of lambda expressions: +``` Free+\X1^X2^ ..^XN^Goal \X1^X2^ ..^XN^Goal +``` -The second is a shorthand for t+\X1^X2^..^XN^Goal. +The second is a shorthand for `t+\X1^X2^..^XN^Goal`. Xi are the parameters. @@ -70,20 +72,20 @@ currently not checked. Violations may lead to unexpected bindings. In the following example the parentheses around X>3 are necessary. -== +``` ?- use_module(library(lambda)). ?- use_module(library(lists)). ?- maplist(\X^(X>3),[4,5,9]). true. -== +``` In the following X is a variable that is shared by both instances of the lambda expression. The second query illustrates the cooperation of continuations and lambdas. The lambda expression is in this case a continuation expecting a further argument. -== +``` ?- use_module(library(dif)). true. @@ -92,11 +94,12 @@ continuation expecting a further argument. ?- Xs = [A,B], maplist(X+\dif(X), Xs). Xs = [A,B], dif:dif(X,A), dif:dif(X,B). -== +``` The following queries are all equivalent. To see this, use -the fact f(x,y). -== +the fact `f(x,y)`. + +``` ?- call(f,A1,A2). ?- call(\X^f(X),A1,A2). ?- call(\X^Y^f(X,Y), A1,A2). @@ -105,10 +108,10 @@ the fact f(x,y). ?- call(f(A1),A2). ?- f(A1,A2). A1 = x, A2 = y. -== +``` Further discussions -http://www.complang.tuwien.ac.at/ulrich/Prolog-inedit/ISO-Hiord +[http://www.complang.tuwien.ac.at/ulrich/Prolog-inedit/ISO-Hiord](http://www.complang.tuwien.ac.at/ulrich/Prolog-inedit/ISO-Hiord) @tbd Static expansion similar to apply_macros. @author Ulrich Neumerkel diff --git a/src/lib/lists.pl b/src/lib/lists.pl index 4eb204b1..3d1cc6a2 100644 --- a/src/lib/lists.pl +++ b/src/lib/lists.pl @@ -1,3 +1,7 @@ +/** +List manipulation predicates +*/ + :- module(lists, [member/2, select/3, append/2, append/3, foldl/4, foldl/5, memberchk/2, reverse/2, length/2, maplist/2, maplist/3, maplist/4, maplist/5, maplist/6, @@ -57,6 +61,20 @@ resource_error(Resource, Context) :- throw(error(resource_error(Resource), Context)). +%% length(?Xs, ?N). +% +% Relates a list to its length (number of elements). It can be used to count the elements of a current list or +% to create a list full of free variables with N length. +% +% ``` +% ?- length("abc", 3). +% true. +% ?- length("abc", N). +% N = 3. +% ?- length(Xs, 3). +% Xs = [_A,_B,_C]. +% ``` + length(Xs0, N) :- '$skip_max_list'(M, N, Xs0,Xs), !, @@ -74,7 +92,7 @@ length(_, N) :- length_rundown(Xs, 0) :- !, Xs = []. length_rundown(Vs, N) :- - \+ \+ '$project_atts':copy_term(Vs,Vs,[]), % unconstrained + '$unattributed_var'(Vs), % unconstrained !, '$det_length_rundown'(Vs, N). length_rundown([_|Xs], N) :- % force unification @@ -82,7 +100,7 @@ length_rundown([_|Xs], N) :- % force unification length(Xs, N1). % maybe some new info on Xs failingvarskip(Xs) :- - \+ \+ '$project_atts':copy_term(Xs,Xs,[]), % unconstrained + '$unattributed_var'(Xs), % unconstrained !. failingvarskip([_|Xs0]) :- % force unification '$skip_max_list'(_, _, Xs0,Xs), @@ -95,28 +113,71 @@ length_addendum([_|Xs], N, M) :- M1 is M + 1, length_addendum(Xs, N, M1). +%% member(?X, ?Xs). +% +% Succeeds when X unifies with an item of the list Xs, which can be at any position. +% +% ``` +% ?- member(X, "hello world"). +% X = h +% ; ... . +% ``` -member(X, [X|_]). -member(X, [_|Xs]) :- member(X, Xs). +member(X, [L|Ls]) :- + member_(Ls, L, X). +member_(_, X, X). +member_([L|Ls], _, X) :- + member_(Ls, L, X). +%% select(X, Xs0, Xs1). +% +% Succeeds when the list Xs1 is the list Xs0 without the item X +% +% ``` +% ?- select(c, "abcd", X). +% X = "abd" +% ; false. +% ``` select(X, [X|Xs], Xs). select(X, [Y|Xs], [Y|Ys]) :- select(X, Xs, Ys). - +%% append(+XsXs, ?Xs). +% +% Concatenates a list of lists +% +% ``` +% ?- append([[1, 2], [3]], Xs). +% Xs = [1,2,3]. +% ``` append([], []). append([L0|Ls0], Ls) :- append(L0, Rest, Ls), append(Ls0, Rest). - +%% append(Xs0, Xs1, Xs). +% +% List Xs is the concatenation of Xs0 and Xs1 +% +% ``` +% ?- append([1,2,3], [4,5,6], Xs). +% Xs = [1,2,3,4,5,6]. +% ``` append([], R, R). append([X|L], R, [X|S]) :- append(L, R, S). - +%% memberchk(?X, +Xs). +% +% This predicate is similar to `member/2`, but it only provides a single answer memberchk(X, Xs) :- member(X, Xs), !. - +%% reverse(?Xs, ?Ys). +% +% Xs is the Ys list in reverse order +% +% ?- reverse([1,2,3], [3,2,1]). +% true. +% reverse(Xs, Ys) :- ( nonvar(Xs) -> reverse(Xs, Ys, [], Xs) ; reverse(Ys, Xs, [], Ys) @@ -126,81 +187,141 @@ reverse([], [], YsRev, YsRev). reverse([_|Xs], [Y1|Ys], YsPreludeRev, Xss) :- reverse(Xs, Ys, [Y1|YsPreludeRev], Xss). +%% maplist(+Predicate, ?Xs0). +% +% This is a metapredicate that applies predicate to each element of the list Xs0 +% +% ``` +% ?- maplist(write, [1,2,3]). +% 123 true. +% ``` maplist(_, []). maplist(Cont1, [E1|E1s]) :- call(Cont1, E1), maplist(Cont1, E1s). +%% maplist(+Predicate, ?Xs0, ?Xs1). +% +% This is a metapredicate that applies predicate to each element of the lists Xs0 and Xs1. +% +% ``` +% ?- maplist(length, ["hello", "prolog", "marseille"], Xs1). +% Xs1 = [5,6,9]. +% ``` maplist(_, [], []). maplist(Cont2, [E1|E1s], [E2|E2s]) :- call(Cont2, E1, E2), maplist(Cont2, E1s, E2s). +%% maplist(+Predicate, ?Xs0, ?Xs1, ?Xs2). +% +% This is a metapredicate that applies predicate to each element of the lists Xs0, Xs1 and Xs2. maplist(_, [], [], []). maplist(Cont3, [E1|E1s], [E2|E2s], [E3|E3s]) :- call(Cont3, E1, E2, E3), maplist(Cont3, E1s, E2s, E3s). +%% maplist(+Predicate, ?Xs0, ?Xs1, ?Xs2, ?Xs3). +% +% This is a metapredicate that applies predicate to each element of the lists Xs0, Xs1, Xs2 and Xs3. maplist(_, [], [], [], []). maplist(Cont, [E1|E1s], [E2|E2s], [E3|E3s], [E4|E4s]) :- call(Cont, E1, E2, E3, E4), maplist(Cont, E1s, E2s, E3s, E4s). - +%% maplist(+Predicate, ?Xs0, ?Xs1, ?Xs2, ?Xs3, ?Xs4). +% +% This is a metapredicate that applies predicate to each element of the lists Xs0, Xs1, Xs2, Xs3 and Xs4. maplist(_, [], [], [], [], []). maplist(Cont, [E1|E1s], [E2|E2s], [E3|E3s], [E4|E4s], [E5|E5s]) :- call(Cont, E1, E2, E3, E4, E5), maplist(Cont, E1s, E2s, E3s, E4s, E5s). - +%% maplist(+Predicate, ?Xs0, ?Xs1, ?Xs2, ?Xs3, ?Xs4, ?Xs5). +% +% This is a metapredicate that applies predicate to each element of the lists Xs0, Xs1, Xs2, Xs3, Xs4 and Xs5. maplist(_, [], [], [], [], [], []). maplist(Cont, [E1|E1s], [E2|E2s], [E3|E3s], [E4|E4s], [E5|E5s], [E6|E6s]) :- call(Cont, E1, E2, E3, E4, E5, E6), maplist(Cont, E1s, E2s, E3s, E4s, E5s, E6s). - +%% maplist(+Predicate, ?Xs0, ?Xs1, ?Xs2, ?Xs3, ?Xs4, ?Xs5, ?Xs6). +% +% This is a metapredicate that applies predicate to each element of the lists Xs0, Xs1, Xs2, Xs3, Xs4, Xs5 and Xs6. maplist(_, [], [], [], [], [], [], []). maplist(Cont, [E1|E1s], [E2|E2s], [E3|E3s], [E4|E4s], [E5|E5s], [E6|E6s], [E7|E7s]) :- call(Cont, E1, E2, E3, E4, E5, E6, E7), maplist(Cont, E1s, E2s, E3s, E4s, E5s, E6s, E7s). - +%% maplist(+Predicate, ?Xs0, ?Xs1, ?Xs2, ?Xs3, ?Xs4, ?Xs5, ?Xs6, ?Xs7). +% +% This is a metapredicate that applies predicate to each element of the lists Xs0, Xs1, Xs2, Xs3, Xs4, Xs5, Xs6 and Xs7. maplist(_, [], [], [], [], [], [], [], []). maplist(Cont, [E1|E1s], [E2|E2s], [E3|E3s], [E4|E4s], [E5|E5s], [E6|E6s], [E7|E7s], [E8|E8s]) :- call(Cont, E1, E2, E3, E4, E5, E6, E7, E8), maplist(Cont, E1s, E2s, E3s, E4s, E5s, E6s, E7s, E8s). - +%% sum_list(+Xs, -Sum). +% +% Takes a lists of numbers and unifies Sum with the result of summing all the elements of the list. +% +% ``` +% ?- sum_list([2,2,2], 6). +% true. +% ``` sum_list(Ls, S) :- foldl(lists:sum_, Ls, 0, S). sum_(L, S0, S) :- S is S0 + L. - +%% same_length(?Xs, ?Ys). +% +% Succeeds if Xs and Ys are lists of the same length same_length([], []). same_length([_|As], [_|Bs]) :- same_length(As, Bs). +%% foldl(+Predicate, ?Ls, +A0, ?A). +% +% foldl, sometimes called reduce, is a metapredicate that takes a predicate, a list of items +% and a starting value, and outputs a single value. The predicate _Predicate_ must be able to take the current +% element of the list, the previous value of the computation and the next value of the computation. +% +% For example, if we define sum_ as: +% +% ``` +% sum_(L, S0, S) :- S is S0 + L. +% ``` +% +% Then we can define `sum_list/2` as the following: +% +% ``` +% sum_list(Ls, S) :- foldl(sum_, Ls, 0, S). +% ``` -foldl(Goal_3, Ls, A0, A) :- - foldl_(Ls, Goal_3, A0, A). - -foldl_([], _, A, A). -foldl_([L|Ls], G_3, A0, A) :- +foldl(_, [], A, A). +foldl(G_3, [L|Ls], A0, A) :- call(G_3, L, A0, A1), - foldl_(Ls, G_3, A1, A). + foldl(G_3, Ls, A1, A). +%% foldl(+Predicate, ?Ls0, ?Ls1, +A0, ?A). +% +% Same as `foldl/4` but with an extra list -foldl(Goal_4, Xs, Ys, A0, A) :- - foldl_(Xs, Ys, Goal_4, A0, A). - - -foldl_([], [], _, A, A). -foldl_([X|Xs], [Y|Ys], G_4, A0, A) :- +foldl(_, [], [], A, A). +foldl(G_4, [X|Xs], [Y|Ys], A0, A) :- call(G_4, X, Y, A0, A1), - foldl_(Xs, Ys, G_4, A1, A). + foldl(G_4, Xs, Ys, A1, A). +%% transpose(?Ls, ?Ts). +% +% If Ls is a list of lists, Ts contains the transposition +% +% ``` +% ?- transpose([[1,1],[2,2]], Ts). +% Ts = [[1,2],[1,2]]. +% ``` transpose(Ls, Ts) :- lists_transpose(Ls, Ts). @@ -214,7 +335,14 @@ transpose_(_, Fs, Lists0, Lists) :- list_first_rest([L|Ls], L, Ls). - +%% list_to_set(+Ls0, -Set). +% +% Takes a list Ls0 and returns a list Set that doesn't contain any repeated element +% +% ``` +% ?- list_to_set([2,3,4,4,1,2], Set). +% Set = [2,3,4,1]. +% ``` list_to_set(Ls0, Ls) :- maplist(lists:with_var, Ls0, LVs0), keysort(LVs0, LVs), @@ -242,7 +370,14 @@ unify_same(E-V, Prev-Var, E-V) :- ; true ). - +%% nth0(?N, ?Ls, ?E). +% +% Succeeds if in the N position of the list Ls, we found the element E. The elements start counting from zero. +% +% ``` +% ?- nth0(2, [1,2,3,4], 3). +% true. +% ``` nth0(N, Es0, E) :- nonvar(N), '$skip_max_list'(Skip, N, Es0,Es1), @@ -261,7 +396,6 @@ nth0(N, Es0, E) :- skipn(N0, Es0,Es) :- N0>0, - !, % should not be necessary #1028 N1 is N0-1, Es0 = [_|Es1], skipn(N1, Es1,Es). @@ -277,6 +411,14 @@ nth0_el(N0,N, _,E, [E0|Es0]) :- N1 is N0+1, nth0_el(N1,N, E0,E, Es0). +%% nth1(?N, ?Ls, ?E). +% +% Succeeds if in the N position of the list Ls, we found the element E. The elements start counting from one. +% +% ``` +% ?- nth1(2, [1,2,3,4], 2). +% true. +% ``` nth1(N, Es0, E) :- N \== 0, nth0(N, [_|Es0], E), @@ -284,13 +426,20 @@ nth1(N, Es0, E) :- skipn(N0, Es0,Es, Xs0,Xs) :- N0>0, - !, % should not be necessary #1028 N1 is N0-1, Es0 = [E|Es1], Xs0 = [E|Xs1], skipn(N1, Es1,Es, Xs1,Xs). skipn(0, Es,Es, Xs,Xs). +%% nth0(?N, ?Ls, ?E, ?Rs). +% +% Succeeds if in the N position of the list Ls, we found the element E and the rest of the list is Rs. The elements start counting from zero. +% +% ``` +% ?- nth0(2, [1,2,3,4], 3, [1,2,4]). +% true. +% ``` nth0(N, Es0, E, Es) :- integer(N), N >= 0, @@ -315,45 +464,58 @@ nth0_elx(N0,N, E0,E, [E1|Es0], [E0|Es]) :- % p.p.8.5 +%% nth1(?N, ?Ls, ?E, ?Rs). +% +% Succeeds if in the N position of the list Ls, we found the element E and the rest of the list is Rs. The elements start counting from one. +% +% ``` +% ?- nth1(2, [1,2,3,4], 2, [1,3,4]). +% true. +% ``` nth1(N, Es0, E, Es) :- N \== 0, nth0(N, [_|Es0], E, [_|Es]), N \== 0. - +%% list_max(+Xs, -Max). +% +% Takes a list Xs and unifies with the maximum value of the list list_max([N|Ns], Max) :- foldl(lists:list_max_, Ns, N, Max). list_max_(N, Max0, Max) :- Max is max(N, Max0). +%% list_min(+Xs, -Min). +% +% Takes a list Xs and unifies with the minimum value of the list list_min([N|Ns], Min) :- foldl(lists:list_min_, Ns, N, Min). list_min_(N, Min0, Min) :- Min is min(N, Min0). -%! permutation(?Xs, ?Ys) is nondet. +%% permutation(?Xs, ?Ys) is nondet. % -% True when Xs is a permutation of Ys. This can solve for Ys given -% Xs or Xs given Ys, or even enumerate Xs and Ys together. The -% predicate permutation/2 is primarily intended to generate -% permutations. Note that a list of length N has N! permutations, -% and unbounded permutation generation becomes prohibitively -% expensive, even for rather short lists (10! = 3,628,800). +% True when Xs is a permutation of Ys. This can solve for Ys given +% Xs or Xs given Ys, or even enumerate Xs and Ys together. The +% predicate `permutation/2` is primarily intended to generate +% permutations. Note that a list of length N has N! permutations, +% and unbounded permutation generation becomes prohibitively +% expensive, even for rather short lists (10! = 3,628,800). % -% The example below illustrates that Xs and Ys being proper lists -% is not a sufficient condition to use the above replacement. +% The example below illustrates that Xs and Ys being proper lists +% is not a sufficient condition to use the above replacement. % -% == -% ?- permutation([1,2], [X,Y]). -% X = 1, Y = 2 ; -% X = 2, Y = 1 ; -% false. -% == +% ``` +% ?- permutation([1,2], [X,Y]). +% X = 1, Y = 2 +% ; X = 2, Y = 1 +% ; false. +% ``` % -% @error type_error(list, Arg) if either argument is not a proper -% or partial list. +% Throws `type_error(list, Arg)` if either argument is not a proper +% or partial list. permutation(Xs, Ys) :- '$skip_max_list'(Xlen, _, Xs, XTail), diff --git a/src/lib/ordsets.pl b/src/lib/ordsets.pl index b5886d38..354e674f 100644 --- a/src/lib/ordsets.pl +++ b/src/lib/ordsets.pl @@ -54,39 +54,38 @@ :- use_module(library(lists)). -/** Ordered set manipulation +/** Ordered set manipulation + Ordered sets are lists with unique elements sorted to the standard order -of terms (see sort/2). Exploiting ordering, many of the set operations +of terms (see `sort/2`). Exploiting ordering, many of the set operations can be expressed in order N rather than N^2 when dealing with unordered sets that may contain duplicates. The library(ordsets) is available in a number of Prolog implementations. Our predicates are designed to be -compatible with common practice in the Prolog community. The -implementation is incomplete and relies partly on library(oset), an -older ordered set library distributed with SWI-Prolog. New applications -are advised to use library(ordsets). +compatible with common practice in the Prolog community. Some of these predicates match directly to corresponding list operations. It is advised to use the versions from this library to make -clear you are operating on ordered sets. An exception is member/2. See -ord_memberchk/2. +clear you are operating on ordered sets. An exception is `member/2`. See +`ord_memberchk/2`. + The ordsets library is based on the standard order of terms. This implies it can handle all Prolog terms, including variables. Note however, that the ordering is not stable if a term inside the set is further instantiated. Also note that variable ordering changes if variables in the set are unified with each other or a variable in the -set is unified with a variable that is `older' than the newest variable +set is unified with a variable that is _older_ than the newest variable in the set. In practice, this implies that it is allowed to use member(X, OrdSet) on an ordered set that holds variables only if X is a fresh variable. In other cases one should cease using it as an ordset because the order it relies on may have been changed. */ -%! is_ordset(@Term) is semidet. +%% is_ordset(@Term) is semidet. % -% True if Term is an ordered set. All predicates in this library -% expect ordered sets as input arguments. Failing to fullfil this -% assumption results in undefined behaviour. Typically, ordered -% sets are created by predicates from this library, sort/2 or -% setof/3. +% True if Term is an ordered set. All predicates in this library +% expect ordered sets as input arguments. Failing to fullfil this +% assumption results in undefined behaviour. Typically, ordered +% sets are created by predicates from this library, `sort/2` or +% `setof/3`. is_ordset(Term) :- '$skip_max_list'(_, _, Term, Tail), Tail == [], %% is_list(Term), @@ -102,37 +101,35 @@ is_ordset3([H2|T], H) :- is_ordset3(T, H2). -%! ord_empty(?List) is semidet. +%% ord_empty(?List) is semidet. % -% True when List is the empty ordered set. Simply unifies list -% with the empty list. Not part of Quintus. +% True when List is the empty ordered set. Simply unifies list +% with the empty list. Not part of Quintus. ord_empty([]). -%! ord_seteq(+Set1, +Set2) is semidet. +%% ord_seteq(+Set1, +Set2) is semidet. % -% True if Set1 and Set2 have the same elements. As both are -% canonical sorted lists, this is the same as ==/2. -% -% @compat sicstus +% True if Set1 and Set2 have the same elements. As both are +% canonical sorted lists, this is the same as `==/2`. ord_seteq(Set1, Set2) :- Set1 == Set2. -%! list_to_ord_set(+List, -OrdSet) is det. +%% list_to_ord_set(+List, -OrdSet) is det. % -% Transform a list into an ordered set. This is the same as -% sorting the list. +% Transform a list into an ordered set. This is the same as +% sorting the list. list_to_ord_set(List, Set) :- sort(List, Set). -%! ord_intersect(+Set1, +Set2) is semidet. +%% ord_intersect(+Set1, +Set2) is semidet. % -% True if both ordered sets have a non-empty intersection. +% True if both ordered sets have a non-empty intersection. ord_intersect([H1|T1], L2) :- ord_intersect_(L2, H1, T1). @@ -148,31 +145,29 @@ ord_intersect__(>, H1, T1, _H2, T2) :- ord_intersect_(T2, H1, T1). -%! ord_disjoint(+Set1, +Set2) is semidet. +%% ord_disjoint(+Set1, +Set2) is semidet. % -% True if Set1 and Set2 have no common elements. This is the -% negation of ord_intersect/2. +% True if Set1 and Set2 have no common elements. This is the +% negation of `ord_intersect/2`. ord_disjoint(Set1, Set2) :- \+ ord_intersect(Set1, Set2). -%! ord_intersect(+Set1, +Set2, -Intersection) +%% ord_intersect(+Set1, +Set2, -Intersection) % -% Intersection holds the common elements of Set1 and Set2. +% Intersection holds the common elements of Set1 and Set2. % -% @deprecated Use ord_intersection/3 +% This predicate is *deprecated*. Use `ord_intersection/3` ord_intersect(Set1, Set2, Intersection) :- oset_int(Set1, Set2, Intersection). -%! ord_intersection(+PowerSet, -Intersection) +%% ord_intersection(+PowerSet, -Intersection) % -% Intersection of a powerset. True when Intersection is an ordered -% set holding all elements common to all sets in PowerSet. -% -% @compat sicstus +% Intersection of a powerset. True when Intersection is an ordered +% set holding all elements common to all sets in PowerSet. ord_intersection(PowerSet, Intersection) :- key_by_length(PowerSet, Pairs), @@ -190,10 +185,10 @@ l_int([_-H|T], S0, S) :- l_int(T, S1, S). -%! ord_intersection(+Set1, +Set2, -Intersection) is det. +%% ord_intersection(+Set1, +Set2, -Intersection) is det. % -% Intersection holds the common elements of Set1 and Set2. Uses -% ord_disjoint/2 if Intersection is bound to `[]` on entry. +% Intersection holds the common elements of Set1 and Set2. Uses +% `ord_disjoint/2` if Intersection is bound to `[]` on entry. ord_intersection(Set1, Set2, Intersection) :- ( Intersection == [] @@ -202,13 +197,11 @@ ord_intersection(Set1, Set2, Intersection) :- ). -%! ord_intersection(+Set1, +Set2, ?Intersection, ?Difference) is det. +%% ord_intersection(+Set1, +Set2, ?Intersection, ?Difference) is det. % -% Intersection and difference between two ordered sets. -% Intersection is the intersection between Set1 and Set2, while -% Difference is defined by ord_subtract(Set2, Set1, Difference). -% -% @see ord_intersection/3 and ord_subtract/3. +% Intersection and difference between two ordered sets. +% Intersection is the intersection between Set1 and Set2, while +% Difference is defined by `ord_subtract(Set2, Set1, Difference)`. ord_intersection([], L, [], L) :- !. ord_intersection([_|_], [], [], []) :- !. @@ -224,35 +217,35 @@ ord_intersection2(>, H1, T1, H2, T2, Intersection, [H2|HDiff]) :- ord_intersection([H1|T1], T2, Intersection, HDiff). -%! ord_add_element(+Set1, +Element, ?Set2) is det. +%% ord_add_element(+Set1, +Element, ?Set2) is det. % -% Insert an element into the set. This is the same as -% ord_union(Set1, [Element], Set2). +% Insert an element into the set. This is the same as +% `ord_union(Set1, [Element], Set2)`. ord_add_element(Set1, Element, Set2) :- oset_addel(Set1, Element, Set2). -%! ord_del_element(+Set, +Element, -NewSet) is det. +%% ord_del_element(+Set, +Element, -NewSet) is det. % -% Delete an element from an ordered set. This is the same as -% ord_subtract(Set, [Element], NewSet). +% Delete an element from an ordered set. This is the same as +% `ord_subtract(Set, [Element], NewSet)`. ord_del_element(Set, Element, NewSet) :- oset_delel(Set, Element, NewSet). -%! ord_selectchk(+Item, ?Set1, ?Set2) is semidet. +%% ord_selectchk(+Item, ?Set1, ?Set2) is semidet. % -% Selectchk/3, specialised for ordered sets. Is true when -% select(Item, Set1, Set2) and Set1, Set2 are both sorted lists -% without duplicates. This implementation is only expected to work -% for Item ground and either Set1 or Set2 ground. The "chk" suffix -% is meant to remind you of memberchk/2, which also expects its -% first argument to be ground. ord_selectchk(X, S, T) => -% ord_memberchk(X, S) & \+ ord_memberchk(X, T). +% `selectchk/3`, specialised for ordered sets. Is true when +% select(Item, Set1, Set2) and Set1, Set2 are both sorted lists +% without duplicates. This implementation is only expected to work +% for Item ground and either Set1 or Set2 ground. The "chk" suffix +% is meant to remind you of `memberchk/2`, which also expects its +% first argument to be ground. `ord_selectchk(X, S, T) => +% ord_memberchk(X, S) & \+ ord_memberchk(X, T).` % -% @author Richard O'Keefe +% Author: Richard O'Keefe ord_selectchk(Item, [X|Set1], [X|Set2]) :- X @< Item, @@ -266,19 +259,19 @@ ord_selectchk(Item, [Item|Set1], Set1) :- ). -%! ord_memberchk(+Element, +OrdSet) is semidet. +%% ord_memberchk(+Element, +OrdSet) is semidet. % -% True if Element is a member of OrdSet, compared using ==. Note -% that _enumerating_ elements of an ordered set can be done using -% member/2. +% True if Element is a member of OrdSet, compared using ==. Note +% that _enumerating_ elements of an ordered set can be done using +% `member/2`. % -% Some Prolog implementations also provide ord_member/2, with the -% same semantics as ord_memberchk/2. We believe that having a -% semidet ord_member/2 is unacceptably inconsistent with the *_chk -% convention. Portable code should use ord_memberchk/2 or -% member/2. +% Some Prolog implementations also provide `ord_member/2`, with the +% same semantics as `ord_memberchk/2`. We believe that having a +% semidet `ord_member/2` is unacceptably inconsistent with the \*\_chk +% convention. Portable code should use `ord_memberchk/2` or +% `member/2`. % -% @author Richard O'Keefe +% Author: Richard O'Keefe ord_memberchk(Item, [X1,X2,X3,X4|Xs]) :- !, @@ -303,9 +296,9 @@ ord_memberchk(Item, [X1]) :- Item == X1. -%! ord_subset(+Sub, +Super) is semidet. +%% ord_subset(+Sub, +Super) is semidet. % -% Is true if all elements of Sub are in Super +% Is true if all elements of Sub are in Super ord_subset([], _). ord_subset([H1|T1], [H2|T2]) :- @@ -319,22 +312,20 @@ ord_subset_(=, _, T1, T2) :- ord_subset(T1, T2). -%! ord_subtract(+InOSet, +NotInOSet, -Diff) is det. +%% ord_subtract(+InOSet, +NotInOSet, -Diff) is det. % -% Diff is the set holding all elements of InOSet that are not in -% NotInOSet. +% Diff is the set holding all elements of InOSet that are not in +% NotInOSet. ord_subtract(InOSet, NotInOSet, Diff) :- oset_diff(InOSet, NotInOSet, Diff). -%! ord_union(+SetOfSets, -Union) is det. +%% ord_union(+SetOfSets, -Union) is det. % -% True if Union is the union of all elements in the superset -% SetOfSets. Each member of SetOfSets must be an ordered set, the -% sets need not be ordered in any way. -% -% @author Copied from YAP, probably originally by Richard O'Keefe. +% True if Union is the union of all elements in the superset +% SetOfSets. Each member of SetOfSets must be an ordered set, the +% sets need not be ordered in any way. ord_union([], []). ord_union([Set|Sets], Union) :- @@ -355,18 +346,18 @@ ord_union_all(N, Sets0, Union, Sets) :- ). -%! ord_union(+Set1, +Set2, ?Union) is det. +%% ord_union(+Set1, +Set2, ?Union) is det. % -% Union is the union of Set1 and Set2 +% Union is the union of Set1 and Set2 ord_union(Set1, Set2, Union) :- oset_union(Set1, Set2, Union). -%! ord_union(+Set1, +Set2, -Union, -New) is det. +%% ord_union(+Set1, +Set2, -Union, -New) is det. % -% True iff ord_union(Set1, Set2, Union) and -% ord_subtract(Set2, Set1, New). +% True iff `ord_union(Set1, Set2, Union)` and +% `ord_subtract(Set2, Set1, New)`. ord_union([], Set2, Set2, Set2). ord_union([H|T], Set2, Union, New) :- @@ -390,26 +381,26 @@ ord_union_2([H|T], H2, T2, Union, New) :- ord_union(Order, H, T, H2, T2, Union, New). -%! ord_symdiff(+Set1, +Set2, ?Difference) is det. +%% ord_symdiff(+Set1, +Set2, ?Difference) is det. % -% Is true when Difference is the symmetric difference of Set1 and -% Set2. I.e., Difference contains all elements that are not in the -% intersection of Set1 and Set2. The semantics is the same as the -% sequence below (but the actual implementation requires only a -% single scan). +% Is true when Difference is the symmetric difference of Set1 and +% Set2. I.e., Difference contains all elements that are not in the +% intersection of Set1 and Set2. The semantics is the same as the +% sequence below (but the actual implementation requires only a +% single scan). % -% == -% ord_union(Set1, Set2, Union), -% ord_intersection(Set1, Set2, Intersection), -% ord_subtract(Union, Intersection, Difference). -% == +% ``` +% ord_union(Set1, Set2, Union), +% ord_intersection(Set1, Set2, Intersection), +% ord_subtract(Union, Intersection, Difference). +% ``` % -% For example: +% For example: % -% == -% ?- ord_symdiff([1,2], [2,3], X). -% X = [1,3]. -% == +% ``` +% ?- ord_symdiff([1,2], [2,3], X). +% X = [1,3]. +% ``` ord_symdiff([], Set2, Set2). ord_symdiff([H1|T1], Set2, Difference) :- @@ -457,7 +448,7 @@ ord_symdiff(>, H1, T1, H2, Set2, [H2|Difference]) :- */ -/** Ordered set manipulation +/* Ordered set manipulation This library defines set operations on sets represented as ordered lists. diff --git a/src/lib/os.pl b/src/lib/os.pl index 448b5f74..5d31b5ce 100644 --- a/src/lib/os.pl +++ b/src/lib/os.pl @@ -12,6 +12,12 @@ Public domain code. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/** Predicates for reasoning about the operating system (OS) environment. + +This includes predicates about environment variables, calls to shell and +finding out the PID of the running system. +*/ + :- module(os, [getenv/2, setenv/2, unsetenv/1, @@ -24,25 +30,60 @@ :- use_module(library(lists)). :- use_module(library(si)). +%% getenv(+Key, -Value). +% +% True iff Value contains the value of the environment variable Key. +% Example: +% +% ``` +% ?- getenv("LANG", Ls). +% Ls = "en_US.UTF-8". +% ``` getenv(Key, Value) :- must_be_env_var(Key), '$getenv'(Key, Value). +%% setenv(+Key, +Value). +% +% Sets the environment variable Key to Value setenv(Key, Value) :- must_be_env_var(Key), must_be_chars(Value), '$setenv'(Key, Value). +%% unsetenv(+Key). +% +% Unsets the environment variable Key unsetenv(Key) :- must_be_env_var(Key), '$unsetenv'(Key). +%% shell(+Command) +% +% Equivalent to `shell(Command, 0)`. shell(Command) :- shell(Command, 0). + +%% shell(+Command, -Status). +% +% True iff executes Command in a shell of the operating system and the exit code is Status. +% Keep in mind the shell syntax is dependant on the operating system, so it should be +% used very carefully. +% +% Example (using Linux and fish shell): +% +% ``` +% ?- shell("echo $SHELL", Status). +% /bin/fish +% Status = 0. +% ``` shell(Command, Status) :- must_be_chars(Command), can_be(integer, Status), '$shell'(Command, Status). +%% pid(-PID). +% +% True iff PID is the process identification number of current Scryer Prolog instance. pid(PID) :- can_be(integer, PID), '$pid'(PID). diff --git a/src/lib/pairs.pl b/src/lib/pairs.pl index 8ed74777..17216b49 100644 --- a/src/lib/pairs.pl +++ b/src/lib/pairs.pl @@ -1,3 +1,10 @@ +/** Reasoning about pairs. + + Pairs are Prolog terms with principal functor `(-)/2`. A pair + often has the form `Key-Value`. The predicates of this library + relate pairs to keys and values. +*/ + :- module(pairs, [pairs_keys_values/3, pairs_keys/2, pairs_values/2, @@ -7,12 +14,25 @@ :- meta_predicate map_list_to_pairs(2, ?, ?). +%% pairs_keys_values(?Pairs, ?Keys, ?Values) +% +% The first argument is a list of Pairs, the second the corresponding +% Keys, and the third argument the corresponding values. + pairs_keys_values([], [], []). pairs_keys_values([A-B|ABs], [A|As], [B|Bs]) :- pairs_keys_values(ABs, As, Bs). +%% pairs_keys(?Pairs, ?Keys) +% +% Same as `pairs_keys_values(Pairs, Keys, _)`. + pairs_keys(Ps, Ks) :- pairs_keys_values(Ps, Ks, _). +%% pairs_values(?Pairs, ?Values) +% +% Same as `pairs_keys_values(Pairs, _, Values)`. + pairs_values(Ps, Vs) :- pairs_keys_values(Ps, _, Vs). map_list_to_pairs(Pred, Ls, Ps) :- diff --git a/src/lib/pio.pl b/src/lib/pio.pl index 42f641db..fdc5bcb1 100644 --- a/src/lib/pio.pl +++ b/src/lib/pio.pl @@ -1,13 +1,11 @@ -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Pure I/O - ======== +/** Pure I/O. Our goal is to encourage the use of definite clause grammars (DCGs) - for describing strings. The predicates phrase_from_file/[2,3], - phrase_to_file/[2,3] and phrase_to_stream/2 let us apply DCGs + for describing strings. The predicates `phrase_from_file/[2,3]`, + `phrase_to_file/[2,3]` and `phrase_to_stream/2` let us apply DCGs transparently to files and streams, and therefore decouple side-effects from declarative descriptions. -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +*/ :- module(pio, [phrase_from_file/2, phrase_from_file/3, @@ -29,16 +27,18 @@ :- meta_predicate(phrase_to_file(2, ?, ?)). :- meta_predicate(phrase_to_stream(2, ?)). -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - phrase_from_file(GRBody, File) - - True if grammar rule body GRBody covers the contents of File, - represented as a list of characters. -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +%% phrase_from_file(+GRBody, +File) +% +% True if grammar rule body GRBody covers the contents of File, +% represented as a list of characters. phrase_from_file(NT, File) :- phrase_from_file(NT, File, []). +%% phrase_from_file(+GRBody, +File, +Options) +% +% Like `phrase_from_file/2`, using Options to open the file. + phrase_from_file(NT, File, Options) :- ( var(File) -> instantiation_error(phrase_from_file/3) ; must_be(list, Options), @@ -68,23 +68,22 @@ reader_step(Stream, Pos, Xs0) :- stream_to_lazy_list(Stream, Xs) ). -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - phrase_to_stream(+GRBody, +Stream) - - Emit the list of characters described by the grammar rule body - GRBody to Stream. - - An ideal implementation of phrase_to_stream/2 writes each character - as soon as it becomes known and no choice-points remain, and thus - avoids the manifestation of the entire string in memory. See #691 - for more information. - - The current preliminary implementation is provided so that Prolog - programmers can already get used to describing output with DCGs, - and then writing it to a file when necessary. This simple - implementation suffices as long as the entire contents can be - represented in memory, and thus covers a large number of use cases. -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +%% phrase_to_stream(+GRBody, +Stream) +% +% Emit the list of characters described by the grammar rule body +% GRBody to Stream. +% +% An ideal implementation of `phrase_to_stream/2` writes each +% character as soon as it becomes known and no choice-points remain, +% and thus avoids the manifestation of the entire string in memory. +% See [#691](https://github.com/mthom/scryer-prolog/issues/691) for +% more information. +% +% The current preliminary implementation is provided so that Prolog +% programmers can already get used to describing output with DCGs, +% and then writing it to a file when necessary. This simple +% implementation suffices as long as the entire contents can be +% represented in memory, and thus covers a large number of use cases. phrase_to_stream(GRBody, Stream) :- phrase(GRBody, Cs), @@ -101,14 +100,18 @@ phrase_to_stream(GRBody, Stream) :- % maplist(put_char(Stream), Cs). It also works for binary streams. '$put_chars'(Stream, Cs). -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - phrase_to_file(+GRBody, +File), writing the string described - by GRBody to File. -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +%% phrase_to_file(+GRBody, +File) +% +% Write the string described by GRBody to File. phrase_to_file(GRBody, File) :- phrase_to_file(GRBody, File, []). + +%% phrase_to_file(+GRBody, +File, +Options) +% +% Like `phrase_to_file/2`, using Options to open the file. + phrase_to_file(GRBody, File, Options) :- setup_call_cleanup(open(File, write, Stream, Options), phrase_to_stream(GRBody, Stream), diff --git a/src/lib/random.pl b/src/lib/random.pl index d678aff2..a3296797 100644 --- a/src/lib/random.pl +++ b/src/lib/random.pl @@ -1,24 +1,38 @@ -:- module(random, [maybe/0, random/1, random_integer/3, set_random/1]). +/** +This library provides probabilistic predicates and random number generators. -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - To retain desirable declarative properties, predicates that internally - use random numbers should be equipped with an argument that specifies - the random seed. This makes everything completely reproducible. -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +To retain desirable declarative properties, predicates that internally +use random numbers should be equipped with an argument that specifies +the random seed. This makes everything completely reproducible. +*/ + +:- module(random, [maybe/0, random/1, random_integer/3, set_random/1]). :- use_module(library(error)). -% succeeds with probability 0.5. +%% maybe. +% +% Succeeds with probability 0.5. maybe :- '$maybe'. % The higher the precision, the slower it gets. random_number_precision(64). +%% random(-R). +% +% Generates a random floating number between 0 (inclusive) and 1 (exclusive). random(R) :- var(R), random_number_precision(N), rnd(N, R). +%% random_integer(+Lower, +Upper, -R). +% +% Generates a random integer number between Lower (inclusive) and Upper (exclusive). +% +% Throws `instantiation_error` if Lower or Upper are variables. +% +% Throws `type_error` if Lower or Upper aren't integers. random_integer(Lower, Upper, R) :- var(R), ( (var(Lower) ; var(Upper)) -> @@ -46,6 +60,10 @@ rnd_(N, R0, R) :- R1 is R0 + 1.0 / 2.0 ^ N, rnd_(N1, R1, R). +%% set_random(+Seed). +% +% Sets a seed that will be used for subsequent random generations in this library. +% It's necessary to set a seed to provide reproducible executions using this library. set_random(Seed) :- ( nonvar(Seed) -> ( Seed = seed(S) -> diff --git a/src/lib/reif.pl b/src/lib/reif.pl index 0e43a897..55618e84 100644 --- a/src/lib/reif.pl +++ b/src/lib/reif.pl @@ -1,3 +1,16 @@ +/** Predicates from [*Indexing dif/2*](https://arxiv.org/abs/1607.01590). + +Example: + +``` +?- tfilter(=(a), [X,Y], Es). + X = a, Y = a, Es = "aa" +; X = a, Es = "a", dif:dif(a,Y) +; Y = a, Es = "a", dif:dif(a,X) +; Es = [], dif:dif(a,X), dif:dif(a,Y). +``` +*/ + :- module(reif, [if_/3, (=)/3, (',')/3, (;)/3, cond_t/3, dif/3, memberd_t/3, tfilter/3, tmember/2, tmember_t/3, tpartition/4]). diff --git a/src/lib/sgml.pl b/src/lib/sgml.pl index a0370e8c..bccba1f3 100644 --- a/src/lib/sgml.pl +++ b/src/lib/sgml.pl @@ -2,57 +2,70 @@ Predicates for parsing HTML and XML documents. Written 2020-2022 by Markus Triska (triska@metalevel.at) Part of Scryer Prolog. - - Currently, two predicates are provided: - - - load_html(+Source, -Es, +Options) - - load_xml(+Source, -Es, +Options) - - These predicates parse HTML and XML documents, respectively. - - Source must be one of: - - - a list of characters with the document contents - - stream(S), specifying a stream S from which to read the content - - file(Name), where Name is a list of characters specifying a file name. - - Es is unified with the abstract syntax tree of the parsed document, - represented as a list of elements where each is of the form: - - * a list of characters, representing text - * element(Name, Attrs, Children) - - Name, an atom, is the name of the tag - - Attrs is a list of Key=Value pairs: - Key is an atom, and Value is a list of characters - - Children is a list of elements as specified here. - - Currently, Options are ignored. In the future, more options may be - provided to control parsing. - - Example: - - ?- load_html("Hello!", Es, []). - - Yielding: - - Es = [element(html,[], - [element(head,[], - [element(title,[], - ["Hello!"])]), - element(body,[],[])])]. - - library(xpath) provides convenient reasoning about parsed documents. - For example, to fetch the title of the document above, we can use: - - ?- load_html("Hello!", Es, []), - xpath(Es, //title(text), T). - - Yielding T = "Hello!". - - Use http_open/3 from library(http/http_open) to read answers from - web servers via streams. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/** Predicates for parsing HTML and XML documents. + +Currently, two predicates are provided: + + - `load_html(+Source, -Es, +Options)` + - `load_xml(+Source, -Es, +Options)` + +These predicates parse HTML and XML documents, respectively. + +Source must be one of: + + - a list of characters with the document contents + - `stream(S)`, specifying a stream S from which to read the content + - `file(Name)`, where Name is a list of characters specifying a file name. + +Es is unified with the abstract syntax tree of the parsed document, +represented as a list of elements where each is of the form: + + * a list of characters, representing text + + * `element(Name, Attrs, Children)` + + - `Name`, an atom, is the name of the tag + + - `Attrs` is a list of `Key=Value` pairs: + `Key` is an atom, and `Value` is a list of characters + + - `Children` is a list of elements as specified here. + +Currently, Options are ignored. In the future, more options may be +provided to control parsing. + +Example: + +``` + ?- load_html("Hello!", Es, []). +``` + +Yielding: + +``` + Es = [element(html,[], + [element(head,[], + [element(title,[], + ["Hello!"])]), + element(body,[],[])])]. +``` + +`library(xpath)` provides convenient reasoning about parsed documents. +For example, to fetch the title of the document above, we can use: + +``` + ?- load_html("Hello!", Es, []), + xpath(Es, //title(text), T). +``` + +Yielding `T = "Hello!"`. + +Use `http_open/3` from `library(http/http_open)` to read answers from +web servers via streams. +*/ + :- module(sgml, [load_html/3, load_xml/3]). diff --git a/src/lib/si.pl b/src/lib/si.pl index d697c8a8..4c3b6c65 100644 --- a/src/lib/si.pl +++ b/src/lib/si.pl @@ -1,34 +1,43 @@ -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Safe type tests - =============== +/** Safe type tests. - "si" stands for "sufficiently instantiated". + "si" stands for "sufficiently instantiated". It can also be read as + "safe inference", so possibly also other predicates are candidates + for this library. - These predicates: + A safe type test: - - throw instantiation errors if the argument is + - throws an *instantiation error* if the argument is not sufficiently instantiated to make a sound decision - - succeed if the argument is of the specified type - - fail otherwise. + - *succeeds* if the argument is of the specified type + - *fails* otherwise. - For instance, atom_si(A) yields an *instantiation error* if A is a + For instance, `atom_si(A)` yields an *instantiation error* if `A` is a variable. This is logically sound, since in that case the argument is not sufficiently instantiated to make any decision. - The definitions are taken from: + The definitions are taken from [Safer type tests in Prolog](https://stackoverflow.com/questions/27306453/safer-type-tests-in-prolog). - https://stackoverflow.com/questions/27306453/safer-type-tests-in-prolog + Examples: - "si" can also be read as "safe inference", so possibly also other - predicates are candidates for this library. -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +``` +?- chars_si(Cs). + error(instantiation_error,list_si/1). +?- chars_si([h|Cs]). + error(instantiation_error,list_si/1). +?- chars_si("hello"). + true. +?- chars_si(hello). + false. +``` +*/ :- module(si, [atom_si/1, integer_si/1, atomic_si/1, list_si/1, - chars_si/1]). + chars_si/1, + dif_si/2]). :- use_module(library(lists)). @@ -56,3 +65,9 @@ list_si(L0) :- chars_si(Cs) :- list_si(Cs), '$is_partial_string'(Cs). + +dif_si(X, Y) :- + X \== Y, + ( X \= Y -> true + ; throw(error(instantiation_error,dif_si/2)) + ). diff --git a/src/lib/simplex.pl b/src/lib/simplex.pl index 001571a8..0482d130 100644 --- a/src/lib/simplex.pl +++ b/src/lib/simplex.pl @@ -77,9 +77,9 @@ thesis project, for example. A *linear programming problem* or simply *linear program* (LP) consists of: - - a set of _linear_ **constraints** - - a set of **variables** - - a _linear_ **objective function**. + - a set of _linear_ *constraints* + - a set of *variables* + - a _linear_ *objective function*. The goal is to assign values to the variables so as to _maximize_ (or minimize) the value of the objective function while satisfying all @@ -107,10 +107,10 @@ non-negativity constraints should therefore be stated explicitly. This is the "radiation therapy" example, taken from _Introduction to Operations Research_ by Hillier and Lieberman. -[**Prolog DCG notation**](https://www.metalevel.at/prolog/dcg) is +[*Prolog DCG notation*](https://www.metalevel.at/prolog/dcg) is used to _implicitly_ thread the state through posting the constraints: -== +``` :- use_module(library(simplex)). :- use_module(library(dcgs)). @@ -125,15 +125,15 @@ post_constraints --> constraint([0.6*x1, 0.4*x2] >= 6), constraint([x1] >= 0), constraint([x2] >= 0). -== +``` An example query: -== +``` ?- radiation(S), variable_value(S, x1, Val1), variable_value(S, x2, Val2). S = solved(...), Val1 = 15 rdiv 2, Val2 = 9 rdiv 2. -== +``` ## Example 2 {#simplex-ex-2} @@ -143,7 +143,7 @@ Here is an instance of the knapsack problem described above, where `C variables, `x(1)` and `x(2)` that denote how many items to take of each type. -== +``` :- use_module(library(simplex)). knapsack(S) :- @@ -155,15 +155,15 @@ knapsack_constraints(S) :- constraint([6*x(1), 4*x(2)] =< 8, S0, S1), constraint([x(1)] =< 1, S1, S2), constraint([x(2)] =< 2, S2, S). -== +``` An example query yields: -== +``` ?- knapsack(S), variable_value(S, x(1), X1), variable_value(S, x(2), X2). S = solved(...), X1 = 1 rdiv 1, X2 = 1 rdiv 2. -== +``` That is, we are to take the one item of the first type, and half of one of the items of the other type to maximize the total value of items in the @@ -171,23 +171,23 @@ knapsack. If items can not be split, integrality constraints have to be imposed: -== +``` knapsack_integral(S) :- knapsack_constraints(S0), constraint(integral(x(1)), S0, S1), constraint(integral(x(2)), S1, S2), maximize([7*x(1), 4*x(2)], S2, S). -== +``` Now the result is different: -== +``` ?- knapsack_integral(S), variable_value(S, x(1), X1), variable_value(S, x(2), X2). X1 = 0 X2 = 2 -== +``` That is, we are to take only the _two_ items of the second type. Notice in particular that always choosing the remaining item with best @@ -207,7 +207,7 @@ The task is to find a _minimal_ number of these coins that amount to 111 units in total. We introduce variables `c(1)`, `c(5)` and `c(20)` denoting how many coins to take of the respective type: -== +``` :- use_module(library(simplex)). coins(S) :- @@ -226,16 +226,16 @@ coins --> constraint(integral(c(5))), constraint(integral(c(20))), minimize([c(1), c(5), c(20)]). -== +``` An example query: -== +``` ?- coins(S), variable_value(S, c(1), C1), variable_value(S, c(5), C5), variable_value(S, c(20), C20). S = solved(...), C1 = 1 rdiv 1, C5 = 2 rdiv 1, C20 = 5 rdiv 1. -== +``` @author [Markus Triska](https://www.metalevel.at) */ diff --git a/src/lib/sockets.pl b/src/lib/sockets.pl index e2b1b723..f6689161 100644 --- a/src/lib/sockets.pl +++ b/src/lib/sockets.pl @@ -1,4 +1,9 @@ - +/** +Predicates for handling network sockets, both as a server and as a client. +As a server, you should open a socket an call `socket_server_accept/4` to get a stream for each connection. +As a client, you should just open a socket and you will receive a stream. +In both cases, with a stream, you can use the usual predicates to read and write to the stream. +*/ :- module(sockets, [socket_client_open/3, socket_server_open/2, socket_server_accept/4, @@ -7,6 +12,18 @@ :- use_module(library(error)). +%% socket_client_open(+Addr, -Stream, +Options). +% +% Open a socket to a server, returning a stream. Addr must satisfy `Addr = Address:Port`. +% +% The following options are available: +% +% * `alias(+Alias)`: Set an alias to the stream +% * `eof_action(+Action)`: Defined what happens if the end of the stream is reached. Values: `error`, `eof_code` and `reset`. +% * `reposition(+Boolean)`: Specifies whether repositioning is required for the stream. `false` is the default. +% * `type(+Type)`: Type can be `text` or `binary`. Defines the type of the stream, if it's optimized for plain text +% or just binary +% socket_client_open(Addr, Stream, Options) :- ( var(Addr) -> throw(error(instantiation_error, socket_client_open/3)) @@ -27,7 +44,11 @@ socket_client_open(Addr, Stream, Options) :- socket_client_open/3), '$socket_client_open'(Address, Port, Stream, Alias, EOFAction, Reposition, Type). - +%% socket_server_open(+Addr, -ServerSocket). +% +% Open a server socket, returning a ServerSocket. Use that ServerSocket to accept incoming connections in +% `socket_server_accept/4`. Addr must satisfy `Addr = Address:Port`. Depending on the operating system +% configuration, some ports might be reserved for superusers. socket_server_open(Addr, ServerSocket) :- must_be(var, ServerSocket), ( ( integer(Addr) ; var(Addr) ) -> @@ -39,7 +60,19 @@ socket_server_open(Addr, ServerSocket) :- '$socket_server_open'(Address, Port, ServerSocket) ). - +%% socket_server_accept(+ServerSocket, -Client, -Stream, +Options). +% +% Given a ServerSocket and a list of Options, accepts a incoming connection, returning data from the Client and +% a Stream to read or write data. +% +% The following options are available: +% +% * `alias(+Alias)`: Set an alias to the stream +% * `eof_action(+Action)`: Defined what happens if the end of the stream is reached. Values: `error`, `eof_code` and `reset`. +% * `reposition(+Boolean)`: Specifies whether repositioning is required for the stream. `false` is the default. +% * `type(+Type)`: Type can be `text` or `binary`. Defines the type of the stream, if it's optimized for plain text +% or just binary +% socket_server_accept(ServerSocket, Client, Stream, Options) :- must_be(var, Client), must_be(var, Stream), @@ -48,10 +81,14 @@ socket_server_accept(ServerSocket, Client, Stream, Options) :- socket_server_accept/4), '$socket_server_accept'(ServerSocket, Client, Stream, Alias, EOFAction, Reposition, Type). - +%% socket_server_close(+ServerSocket). +% +% Stops listening on that ServerSocket. It's recommended to always close a ServerSocket once it's no longer needed socket_server_close(ServerSocket) :- '$socket_server_close'(ServerSocket). - +%% current_hostname(-HostName). +% +% Returns the current hostname of the computer in which Scryer Prolog is executing right now current_hostname(HostName) :- '$current_hostname'(HostName). diff --git a/src/lib/tabling.pl b/src/lib/tabling.pl index 94dd4238..e9008085 100644 --- a/src/lib/tabling.pl +++ b/src/lib/tabling.pl @@ -1,3 +1,29 @@ +/** Tabling, also called SLG resolution. + + SLG resolution is an alternative execution strategy that sometimes + helps to improve termination and performance characters of Prolog + predicates. + + To enable this execution strategy for a Prolog predicate, add a + `(table)/1` directive, using the prefix operator `table` that this + module defines. For example, to enable tabling for the predicate + `p/2`, use: + +``` +:- use_module(library(tabling)). + +:- table p/2. + +... +``` + + The possibility to apply different execution strategies is one of + the greatest attractions of pure Prolog code, and one of the + strongest arguments for keeping to the pure core of Prolog as far + as possible. + + Scryer Prolog implements tabling as described by Desouter et al. in [*Tabling as a Library with Delimited Control*](https://www.ijcai.org/Proceedings/16/Papers/619.pdf). +*/ :- module(tabling, [ start_tabling/2, % +Wrapper, :Worker. @@ -138,7 +164,9 @@ activate(Wrapper,Worker,T) :- delim(Wrapper,Worker,Table) :- % debug(tabling, 'ACT: ~p on ~p', [Wrapper, Table]), - reset(Worker,SourceCall,Continuation), + catch(reset(Worker,SourceCall,Continuation), + _, + fail), ( Continuation = none -> ( add_answer(Table,Wrapper) -> true %debug(tabling, 'ADD: ~p', [Wrapper]) diff --git a/src/lib/tabling/batched_worklist.pl b/src/lib/tabling/batched_worklist.pl index 8a8b4117..ce63981e 100644 --- a/src/lib/tabling/batched_worklist.pl +++ b/src/lib/tabling/batched_worklist.pl @@ -49,12 +49,20 @@ :- use_module(library(tabling/double_linked_list)). :- use_module(library(atts)). +:- use_module(library(dcgs)). :- use_module(library(lists)). :- attribute executing_all_work/1, worklist_presence/1, wkl_answer_cluster/1, wkl_suspension_cluster/1, wkl_answer_cluster_pointer_flag/1. verify_attributes(_, _, []). +attribute_goals(X) --> + { put_atts(X, -executing_all_work(_)), + put_atts(X, -worklist_presence(_)), + put_atts(X, -wkl_answer_cluster(_)), + put_atts(X, -wkl_suspension_cluster(_)), + put_atts(X, -wkl_answer_cluster_pointer_flag(_)) }. + /** Tabling Worklist management A batched worklist: a worklist that clusters suspensions and answers as diff --git a/src/lib/tabling/double_linked_list.pl b/src/lib/tabling/double_linked_list.pl index d80ee6db..08a71ae7 100644 --- a/src/lib/tabling/double_linked_list.pl +++ b/src/lib/tabling/double_linked_list.pl @@ -49,9 +49,15 @@ ]). :- use_module(library(atts)). +:- use_module(library(dcgs)). :- attribute dll_element/1, dll_next/1, dll_prev/1. +attribute_goals(X) --> + { put_atts(X, -dll_element(_)), + put_atts(X, -dll_next(_)), + put_atts(X, -dll_prev(_)) }. + % A circular double linked list % ============================= diff --git a/src/lib/tabling/global_worklist.pl b/src/lib/tabling/global_worklist.pl index 55c437f1..124638c3 100644 --- a/src/lib/tabling/global_worklist.pl +++ b/src/lib/tabling/global_worklist.pl @@ -9,12 +9,15 @@ ]). :- use_module(library(atts)). +:- use_module(library(dcgs)). :- use_module(library(iso_ext)). :- attribute table_global_worklist/1. verify_attributes(_, _, []). +attribute_goals(X) --> { put_atts(X, -table_global_worklist(_)) }. + put_new_global_worklist :- ( bb_get(table_global_worklist_initialized, _) -> true diff --git a/src/lib/tabling/table_data_structure.pl b/src/lib/tabling/table_data_structure.pl index 69a057e6..2f36c0e7 100644 --- a/src/lib/tabling/table_data_structure.pl +++ b/src/lib/tabling/table_data_structure.pl @@ -56,6 +56,7 @@ :- use_module(library(tabling/batched_worklist)). :- use_module(library(atts)). +:- use_module(library(dcgs)). :- use_module(library(gensym)). :- use_module(library(iso_ext)). @@ -63,6 +64,10 @@ verify_attributes(_, _, []). +attribute_goals(X) --> + { put_atts(X, -table_status(_)), + put_atts(X, -newly_created_table_identifiers(_)) }. + % This file defines the table datastructure. % % The table datastructure contains the following sub-structures: diff --git a/src/lib/tabling/table_link_manager.pl b/src/lib/tabling/table_link_manager.pl index c346ea10..8d2b2c65 100644 --- a/src/lib/tabling/table_link_manager.pl +++ b/src/lib/tabling/table_link_manager.pl @@ -43,6 +43,7 @@ ]). :- use_module(library(atts)). +:- use_module(library(dcgs)). :- use_module(library(lists)). :- use_module(library(iso_ext)). :- use_module(library(terms)). @@ -53,6 +54,9 @@ verify_attributes(_, _, []). +attribute_goals(X) --> + { put_atts(X, -trie_table_link(_)) }. + % This file defines a call pattern trie. % % This data structure keeps the relation between a variant and the diff --git a/src/lib/tabling/trie.pl b/src/lib/tabling/trie.pl index 2460f70f..6a9024fc 100644 --- a/src/lib/tabling/trie.pl +++ b/src/lib/tabling/trie.pl @@ -45,12 +45,17 @@ :- use_module(library(assoc)). :- use_module(library(atts)). +:- use_module(library(dcgs)). :- use_module(library(lists)). :- attribute maybe_just/1, children/1. verify_attributes(_, _, []). +attribute_goals(X) --> + { put_atts(X, -maybe_just(_)), + put_atts(X, -children(_)) }. + % Implementation of a prefix tree, a.k.a. trie % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/src/lib/time.pl b/src/lib/time.pl index 20187c2d..003f623b 100644 --- a/src/lib/time.pl +++ b/src/lib/time.pl @@ -1,47 +1,11 @@ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Written 2020, 2021 by Markus Triska (triska@metalevel.at) + Written 2020-2023 by Markus Triska (triska@metalevel.at) Part of Scryer Prolog. - - This library provides predicates for reasoning about time. - - current_time(T) yields the current system time in an opaque form, - called a time stamp. Use format_time//2 to describe strings that - contain attributes of the time stamp. - - The nonterminal format_time//2 describes a list of characters that - are formatted according to a format string. Usage: - - phrase(format_time(FormatString, TimeStamp), Cs) - - TimeStamp represents a moment in time in an opaque form, as for - example obtained by current_time/1. - - FormatString is a list of characters that are interpreted literally, - except for the following specifiers (and possibly more in the future): - - %Y year of the time stamp. Example: 2020. - %m month number (01-12), zero-padded to 2 digits - %d day number (01-31), zero-padded to 2 digits - %H hour number (00-24), zero-padded to 2 digits - %M minute number (00-59), zero-padded to 2 digits - %S second number (00-60), zero-padded to 2 digits - %b abbreviated month name, always 3 letters - %a abbreviated weekday name, always 3 letters - %A full weekday name - %j day of the year (001-366), zero-padded to 3 digits - %% the literal % - - Example: - - ?- current_time(T), phrase(format_time("%d.%m.%Y (%H:%M:%S)", T), Cs). - T = [...], Cs = "11.06.2020 (00:24:32)". - - sleep(S) sleeps for S seconds (a floating point number). - - time(Goal) reports the execution time of Goal. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/** This library provides predicates for reasoning about time. +*/ + :- module(time, [max_sleep_time/1, sleep/1, time/1, current_time/1, format_time//2]). :- use_module(library(format)). @@ -51,10 +15,51 @@ :- use_module(library(lists)). :- use_module(library(charsio), [read_from_chars/2]). + +%% current_time(-T) +% +% Yields the current system time _T_ in an opaque form, called a +% _time stamp_. Use `format_time//2` to describe strings that contain +% attributes of the time stamp. + current_time(T) :- '$current_time'(T0), read_from_chars(T0, T). +%% format_time(FormatString, TimeStamp)// +% +% The nonterminal format_time//2 describes a list of characters that +% are formatted according to a format string. Usage: +% +% ``` +% phrase(format_time(FormatString, TimeStamp), Cs) +% ``` +% +% TimeStamp represents a moment in time in an opaque form, as for +% example obtained by `current_time/1`. +% +% FormatString is a list of characters that are interpreted literally, +% except for the following specifiers (and possibly more in the future): +% +% | `%Y` | year of the time stamp. Example: 2020. | +% | `%m` | month number (01-12), zero-padded to 2 digits | +% | `%d` | day number (01-31), zero-padded to 2 digits | +% | `%H` | hour number (00-24), zero-padded to 2 digits | +% | `%M` | minute number (00-59), zero-padded to 2 digits | +% | `%S` | second number (00-60), zero-padded to 2 digits | +% | `%b` | abbreviated month name, always 3 letters | +% | `%a` | abbreviated weekday name, always 3 letters | +% | `%A` | full weekday name | +% | `%j` | day of the year (001-366), zero-padded to 3 digits | +% | `%%` | the literal `%` | +% +% Example: +% +% ``` +% ?- current_time(T), phrase(format_time("%d.%m.%Y (%H:%M:%S)", T), Cs). +% T = [...], Cs = "11.06.2020 (00:24:32)". +% ``` + format_time([], _) --> []. format_time(['%','%'|Fs], T) --> !, "%", format_time(Fs, T). format_time(['%',Spec|Fs], T) --> !, @@ -65,8 +70,17 @@ format_time(['%',Spec|Fs], T) --> !, format_time(Fs, T). format_time([F|Fs], T) --> [F], format_time(Fs, T). +%% max_sleep_time(T) +% +% The maximum admissible time span for `sleep/1`. + max_sleep_time(0xfffffffffffffbff). + +%% sleep(S) +% +% Sleeps for S seconds (a floating point number or integer). + sleep(T) :- builtins:must_be_number(T, sleep), ( T < 0 -> @@ -91,6 +105,11 @@ time_next_id(N) :- ), asserta(time_id(N)). + +%% time(Goal) +% +% Reports the execution time of Goal. + time(Goal) :- '$cpu_now'(T0), time_next_id(ID), diff --git a/src/lib/ugraphs.pl b/src/lib/ugraphs.pl index 159f4c00..1d2a90c2 100644 --- a/src/lib/ugraphs.pl +++ b/src/lib/ugraphs.pl @@ -53,7 +53,7 @@ connect_ugraph/3 % +Graph1, -Start, -Graph ]). -/** Graph manipulation library +/** Graph manipulation library The S-representation of a graph is a list of (vertex-neighbours) pairs, where the pairs are in standard order (as produced by keysort) and the @@ -61,55 +61,56 @@ neighbours of each vertex are also in standard order (as produced by sort). This form is convenient for many calculations. A new UGraph from raw data can be created using -vertices_edges_to_ugraph/3. +`vertices_edges_to_ugraph/3`. Adapted to support some of the functionality of the SICStus ugraphs library by Vitor Santos Costa. Ported from YAP 5.0.1 to SWI-Prolog by Jan Wielemaker. -@author R.A.O'Keefe -@author Vitor Santos Costa -@author Jan Wielemaker -@license BSD-2 or Artistic 2.0 +Ported from SWI-Prolog to Scryer by [Adrián Arroyo Calle](https://adrianistan.eu) + +License: BSD-2 or Artistic 2.0 */ :- use_module(library(lists)). :- use_module(library(pairs)). :- use_module(library(ordsets)). -%! vertices(+Graph, -Vertices) +%% vertices(+Graph, -Vertices) % -% Unify Vertices with all vertices appearing in Graph. Example: +% Unify Vertices with all vertices appearing in Graph. Example: % -% ?- vertices([1-[3,5],2-[4],3-[],4-[5],5-[]], L). -% L = [1, 2, 3, 4, 5] +% ``` +% ?- vertices([1-[3,5],2-[4],3-[],4-[5],5-[]], L). +% L = [1, 2, 3, 4, 5] +% ``` vertices([], []) :- !. vertices([Vertex-_|Graph], [Vertex|Vertices]) :- vertices(Graph, Vertices). -%! vertices_edges_to_ugraph(+Vertices, +Edges, -UGraph) is det. +%% vertices_edges_to_ugraph(+Vertices, +Edges, -UGraph) is det. % -% Create a UGraph from Vertices and edges. Given a graph with a -% set of Vertices and a set of Edges, Graph must unify with the -% corresponding S-representation. Note that the vertices without -% edges will appear in Vertices but not in Edges. Moreover, it is -% sufficient for a vertice to appear in Edges. +% Create a UGraph from Vertices and edges. Given a graph with a +% set of Vertices and a set of Edges, Graph must unify with the +% corresponding S-representation. Note that the vertices without +% edges will appear in Vertices but not in Edges. Moreover, it is +% sufficient for a vertice to appear in Edges. % -% == -% ?- vertices_edges_to_ugraph([],[1-3,2-4,4-5,1-5], L). -% L = [1-[3,5], 2-[4], 3-[], 4-[5], 5-[]] -% == +% ``` +% ?- vertices_edges_to_ugraph([],[1-3,2-4,4-5,1-5], L). +% L = [1-[3,5], 2-[4], 3-[], 4-[5], 5-[]] +% ``` +% +% In this case all vertices are defined implicitly. The next +% example shows three unconnected vertices: % -% In this case all vertices are defined implicitly. The next -% example shows three unconnected vertices: -% -% == -% ?- vertices_edges_to_ugraph([6,7,8],[1-3,2-4,4-5,1-5], L). -% L = [1-[3,5], 2-[4], 3-[], 4-[5], 5-[], 6-[], 7-[], 8-[]] -% == +% ``` +% ?- vertices_edges_to_ugraph([6,7,8],[1-3,2-4,4-5,1-5], L). +% L = [1-[3,5], 2-[4], 3-[], 4-[5], 5-[], 6-[], 7-[], 8-[]] +% ``` vertices_edges_to_ugraph(Vertices, Edges, Graph) :- sort(Edges, EdgeSet), @@ -119,15 +120,15 @@ vertices_edges_to_ugraph(Vertices, Edges, Graph) :- p_to_s_group(VertexSet, EdgeSet, Graph). -%! add_vertices(+Graph, +Vertices, -NewGraph) +%% add_vertices(+Graph, +Vertices, -NewGraph) % -% Unify NewGraph with a new graph obtained by adding the list of -% Vertices to Graph. Example: +% Unify NewGraph with a new graph obtained by adding the list of +% Vertices to Graph. Example: % -% ``` -% ?- add_vertices([1-[3,5],2-[]], [0,1,2,9], NG). -% NG = [0-[], 1-[3,5], 2-[], 9-[]] -% ``` +% ``` +% ?- add_vertices([1-[3,5],2-[]], [0,1,2,9], NG). +% NG = [0-[], 1-[3,5], 2-[], 9-[]] +% ``` % replace with real msort/2 when available msort_(List, Sorted) :- @@ -159,23 +160,18 @@ add_empty_vertices([], []). add_empty_vertices([V|G], [V-[]|NG]) :- add_empty_vertices(G, NG). -%! del_vertices(+Graph, +Vertices, -NewGraph) is det. +%% del_vertices(+Graph, +Vertices, -NewGraph) is det. % -% Unify NewGraph with a new graph obtained by deleting the list of -% Vertices and all the edges that start from or go to a vertex in -% Vertices to the Graph. Example: +% Unify NewGraph with a new graph obtained by deleting the list of +% Vertices and all the edges that start from or go to a vertex in +% Vertices to the Graph. Example: % -% == -% ?- del_vertices([1-[3,5],2-[4],3-[],4-[5],5-[],6-[],7-[2,6],8-[]], -% [2,1], -% NL). -% NL = [3-[],4-[5],5-[],6-[],7-[6],8-[]] -% == -% -% @compat Upto 5.6.48 the argument order was (+Vertices, +Graph, -% -NewGraph). Both YAP and SWI-Prolog have changed the argument -% order for compatibility with recent SICStus as well as -% consistency with del_edges/3. +% ``` +% ?- del_vertices([1-[3,5],2-[4],3-[],4-[5],5-[],6-[],7-[2,6],8-[]], +% [2,1], +% NL). +% NL = [3-[],4-[5],5-[],6-[],7-[6],8-[]] +% ``` del_vertices(Graph, Vertices, NewGraph) :- sort(Vertices, V1), % JW: was msort @@ -204,32 +200,32 @@ split_on_del_vertices(>, V, Edges, [_|Vs], Vs, V1, [V-NEdges|NG], NG) :- ord_subtract(Edges, V1, NEdges). split_on_del_vertices(=, _, _, [_|Vs], Vs, _, NG, NG). -%! add_edges(+Graph, +Edges, -NewGraph) +%% add_edges(+Graph, +Edges, -NewGraph) % -% Unify NewGraph with a new graph obtained by adding the list of Edges -% to Graph. Example: +% Unify NewGraph with a new graph obtained by adding the list of Edges +% to Graph. Example: % -% ``` -% ?- add_edges([1-[3,5],2-[4],3-[],4-[5], -% 5-[],6-[],7-[],8-[]], -% [1-6,2-3,3-2,5-7,3-2,4-5], -% NL). -% NL = [1-[3,5,6], 2-[3,4], 3-[2], 4-[5], -% 5-[7], 6-[], 7-[], 8-[]] -% ``` +% ``` +% ?- add_edges([1-[3,5],2-[4],3-[],4-[5], +% 5-[],6-[],7-[],8-[]], +% [1-6,2-3,3-2,5-7,3-2,4-5], +% NL). +% NL = [1-[3,5,6], 2-[3,4], 3-[2], 4-[5], +% 5-[7], 6-[], 7-[], 8-[]] +% ``` add_edges(Graph, Edges, NewGraph) :- p_to_s_graph(Edges, G1), ugraph_union(Graph, G1, NewGraph). -%! ugraph_union(+Graph1, +Graph2, -NewGraph) +%% ugraph_union(+Graph1, +Graph2, -NewGraph) % -% NewGraph is the union of Graph1 and Graph2. Example: +% NewGraph is the union of Graph1 and Graph2. Example: % -% ``` -% ?- ugraph_union([1-[2],2-[3]],[2-[4],3-[1,2,4]],L). -% L = [1-[2], 2-[3,4], 3-[1,2,4]] -% ``` +% ``` +% ?- ugraph_union([1-[2],2-[3]],[2-[4],3-[1,2,4]],L). +% L = [1-[2], 2-[3,4], 3-[1,2,4]] +% ``` ugraph_union(Set1, [], Set1) :- !. ugraph_union([], Set2, Set2) :- !. @@ -245,25 +241,25 @@ ugraph_union(<, Head1, Tail1, Head2, Tail2, [Head1|Union]) :- ugraph_union(>, Head1, Tail1, Head2, Tail2, [Head2|Union]) :- ugraph_union([Head1|Tail1], Tail2, Union). -%! del_edges(+Graph, +Edges, -NewGraph) +%% del_edges(+Graph, +Edges, -NewGraph) % -% Unify NewGraph with a new graph obtained by removing the list of -% Edges from Graph. Notice that no vertices are deleted. Example: +% Unify NewGraph with a new graph obtained by removing the list of +% Edges from Graph. Notice that no vertices are deleted. Example: % -% ``` -% ?- del_edges([1-[3,5],2-[4],3-[],4-[5],5-[],6-[],7-[],8-[]], -% [1-6,2-3,3-2,5-7,3-2,4-5,1-3], -% NL). -% NL = [1-[5],2-[4],3-[],4-[],5-[],6-[],7-[],8-[]] -% ``` +% ``` +% ?- del_edges([1-[3,5],2-[4],3-[],4-[5],5-[],6-[],7-[],8-[]], +% [1-6,2-3,3-2,5-7,3-2,4-5,1-3], +% NL). +% NL = [1-[5],2-[4],3-[],4-[],5-[],6-[],7-[],8-[]] +% ``` del_edges(Graph, Edges, NewGraph) :- p_to_s_graph(Edges, G1), graph_subtract(Graph, G1, NewGraph). -%! graph_subtract(+Set1, +Set2, ?Difference) +%% graph_subtract(+Set1, +Set2, ?Difference) % -% Is based on ord_subtract +% Is based on `ord_subtract/3` graph_subtract(Set1, [], Set1) :- !. graph_subtract([], _, []). @@ -279,12 +275,14 @@ graph_subtract(<, Head1, Tail1, Head2, Tail2, [Head1|Difference]) :- graph_subtract(>, Head1, Tail1, _, Tail2, Difference) :- graph_subtract([Head1|Tail1], Tail2, Difference). -%! edges(+Graph, -Edges) +%% edges(+Graph, -Edges) % -% Unify Edges with all edges appearing in Graph. Example: +% Unify Edges with all edges appearing in Graph. Example: % -% ?- edges([1-[3,5],2-[4],3-[],4-[5],5-[]], L). -% L = [1-3, 1-5, 2-4, 4-5] +% ``` +% ?- edges([1-[3,5],2-[4],3-[],4-[5],5-[]], L). +% L = [1-3, 1-5, 2-4, 4-5] +% ``` edges(Graph, Edges) :- s_to_p_graph(Graph, Edges). @@ -324,15 +322,15 @@ s_to_p_graph([], _, P_Graph, P_Graph) :- !. s_to_p_graph([Neib|Neibs], Vertex, [Vertex-Neib|P], Rest_P) :- s_to_p_graph(Neibs, Vertex, P, Rest_P). -%! transitive_closure(+Graph, -Closure) +%% transitive_closure(+Graph, -Closure) % -% Generate the graph Closure as the transitive closure of Graph. -% Example: +% Generate the graph Closure as the transitive closure of Graph. +% Example: % -% ``` -% ?- transitive_closure([1-[2,3],2-[4,5],4-[6]],L). -% L = [1-[2,3,4,5,6], 2-[4,5,6], 4-[6]] -% ``` +% ``` +% ?- transitive_closure([1-[2,3],2-[4,5],4-[6]],L). +% L = [1-[2,3,4,5,6], 2-[4,5,6], 4-[6]] +% ``` transitive_closure(Graph, Closure) :- warshall(Graph, Graph, Closure). @@ -354,23 +352,18 @@ warshall([X-Neibs|G], V, Y, [X-Neibs|NewG]) :- warshall(G, V, Y, NewG). warshall([], _, _, []). -%! transpose_ugraph(Graph, NewGraph) is det. +%% transpose_ugraph(Graph, NewGraph) is det. % -% Unify NewGraph with a new graph obtained from Graph by replacing -% all edges of the form V1-V2 by edges of the form V2-V1. The cost -% is O(|V|*log(|V|)). Notice that an undirected graph is its own -% transpose. Example: +% Unify NewGraph with a new graph obtained from Graph by replacing +% all edges of the form V1-V2 by edges of the form V2-V1. The cost +% is O(|V|\*log(|V|)). Notice that an undirected graph is its own +% transpose. Example: % -% == -% ?- transpose([1-[3,5],2-[4],3-[],4-[5], -% 5-[],6-[],7-[],8-[]], NL). -% NL = [1-[],2-[],3-[1],4-[2],5-[1,4],6-[],7-[],8-[]] -% == -% -% @compat This predicate used to be known as transpose/2. -% Following SICStus 4, we reserve transpose/2 for matrix -% transposition and renamed ugraph transposition to -% transpose_ugraph/2. +% ``` +% ?- transpose([1-[3,5],2-[4],3-[],4-[5], +% 5-[],6-[],7-[],8-[]], NL). +% NL = [1-[],2-[],3-[1],4-[2],5-[1,4],6-[],7-[],8-[]] +% ``` transpose_ugraph(Graph, NewGraph) :- edges(Graph, Edges), @@ -382,13 +375,15 @@ flip_edges([], []). flip_edges([Key-Val|Pairs], [Val-Key|Flipped]) :- flip_edges(Pairs, Flipped). -%! compose(+LeftGraph, +RightGraph, -NewGraph) +%% compose(+LeftGraph, +RightGraph, -NewGraph) % -% Compose NewGraph by connecting the _drains_ of LeftGraph to the -% _sources_ of RightGraph. Example: +% Compose NewGraph by connecting the _drains_ of LeftGraph to the +% _sources_ of RightGraph. Example: % -% ?- compose([1-[2],2-[3]],[2-[4],3-[1,2,4]],L). -% L = [1-[4], 2-[1,2,4], 3-[]] +% ``` +% ?- compose([1-[2],2-[3]],[2-[4],3-[1,2,4]],L). +% L = [1-[4], 2-[1,2,4], 3-[]] +% ``` compose(G1, G2, Composition) :- vertices(G1, V1), @@ -423,21 +418,17 @@ compose1(=, V1, Vs1, V1, N2, G2, SoFar, Comp) :- ord_union(N2, SoFar, Next), compose1(Vs1, G2, Next, Comp). -%! top_sort(+Graph, -Sorted) is semidet. -%! top_sort(+Graph, -Sorted, ?Tail) is semidet. +%% top_sort(+Graph, -Sorted) is semidet. % -% Sorted is a topological sorted list of nodes in Graph. A -% toplogical sort is possible if the graph is connected and -% acyclic. In the example we show how topological sorting works -% for a linear graph: +% Sorted is a topological sorted list of nodes in Graph. A +% toplogical sort is possible if the graph is connected and +% acyclic. In the example we show how topological sorting works +% for a linear graph: % -% == -% ?- top_sort([1-[2], 2-[3], 3-[]], L). -% L = [1, 2, 3] -% == -% -% The predicate top_sort/3 is a difference list version of -% top_sort/2. +% ``` +% ?- top_sort([1-[2], 2-[3], 3-[]], L). +% L = [1, 2, 3] +% ``` top_sort(Graph, Sorted) :- vertices_and_zeros(Graph, Vertices, Counts0), @@ -445,6 +436,11 @@ top_sort(Graph, Sorted) :- select_zeros(Counts1, Vertices, Zeros), top_sort(Zeros, Sorted, Graph, Vertices, Counts1). +%% top_sort(+Graph, -Sorted, ?Tail) is semidet. +% +% The predicate `top_sort/3` is a difference list version of +% `top_sort/2`. + top_sort(Graph, Sorted0, Sorted) :- vertices_and_zeros(Graph, Vertices, Counts0), count_edges(Graph, Vertices, Counts0, Counts1), @@ -520,17 +516,21 @@ decr_list(Neibs, [_|Vertices], [N|Counts1], [N|Counts2], Zi, Zo) :- decr_list(Neibs, Vertices, Counts1, Counts2, Zi, Zo). -%! neighbors(+Vertex, +Graph, -Neigbours) is det. -%! neighbours(+Vertex, +Graph, -Neigbours) is det. + +%% neighbours(+Vertex, +Graph, -Neigbours) is det. % -% Neigbours is a sorted list of the neighbours of Vertex in Graph. -% Example: +% Neigbours is a sorted list of the neighbours of Vertex in Graph. +% Example: % -% ``` -% ?- neighbours(4,[1-[3,5],2-[4],3-[], -% 4-[1,2,7,5],5-[],6-[],7-[],8-[]], NL). -% NL = [1,2,7,5] -% ``` +% ``` +% ?- neighbours(4,[1-[3,5],2-[4],3-[], +% 4-[1,2,7,5],5-[],6-[],7-[],8-[]], NL). +% NL = [1,2,7,5] +% ``` + +%% neighbors(+Vertex, +Graph, -Neigbours) is det. +% +% Same as `neighbours/3`. neighbors(Vertex, Graph, Neig) :- neighbours(Vertex, Graph, Neig). @@ -542,24 +542,24 @@ neighbours(V,[_|G],Neig) :- neighbours(V,G,Neig). -%! connect_ugraph(+UGraphIn, -Start, -UGraphOut) is det. +%% connect_ugraph(+UGraphIn, -Start, -UGraphOut) is det. % -% Adds Start as an additional vertex that is connected to all vertices -% in UGraphIn. This can be used to create an topological sort for a -% not connected graph. Start is before any vertex in UGraphIn in the -% standard order of terms. No vertex in UGraphIn can be a variable. +% Adds Start as an additional vertex that is connected to all vertices +% in UGraphIn. This can be used to create an topological sort for a +% not connected graph. Start is before any vertex in UGraphIn in the +% standard order of terms. No vertex in UGraphIn can be a variable. % -% Can be used to order a not-connected graph as follows: +% Can be used to order a not-connected graph as follows: % -% ``` -% top_sort_unconnected(Graph, Vertices) :- -% ( top_sort(Graph, Vertices) -% -> true -% ; connect_ugraph(Graph, Start, Connected), -% top_sort(Connected, Ordered0), -% Ordered0 = [Start|Vertices] -% ). -% ``` +% ``` +% top_sort_unconnected(Graph, Vertices) :- +% ( top_sort(Graph, Vertices) +% -> true +% ; connect_ugraph(Graph, Start, Connected), +% top_sort(Connected, Ordered0), +% Ordered0 = [Start|Vertices] +% ). +% ``` connect_ugraph([], 0, []) :- !. connect_ugraph(Graph, Start, [Start-Vertices|Graph]) :- @@ -567,12 +567,12 @@ connect_ugraph(Graph, Start, [Start-Vertices|Graph]) :- Vertices = [First|_], before(First, Start). -%! before(+Term, -Before) is det. +%% before(+Term, -Before) is det. % -% Unify Before to a term that comes before Term in the standard -% order of terms. +% Unify Before to a term that comes before Term in the standard +% order of terms. % -% @error instantiation_error if Term is unbound. +% Throws `instantiation_error` if Term is unbound. before(X, _) :- var(X), @@ -585,21 +585,22 @@ before(Number, Start) :- before(_, 0). -%! complement(+UGraphIn, -UGraphOut) +%% complement(+UGraphIn, -UGraphOut) % -% UGraphOut is a ugraph with an edge between all vertices that are -% _not_ connected in UGraphIn and all edges from UGraphIn removed. -% Example: +% UGraphOut is a ugraph with an edge between all vertices that are +% _not_ connected in UGraphIn and all edges from UGraphIn removed. +% Example: % -% ``` -% ?- complement([1-[3,5],2-[4],3-[], -% 4-[1,2,7,5],5-[],6-[],7-[],8-[]], NL). -% NL = [1-[2,4,6,7,8],2-[1,3,5,6,7,8],3-[1,2,4,5,6,7,8], -% 4-[3,5,6,8],5-[1,2,3,4,6,7,8],6-[1,2,3,4,5,7,8], -% 7-[1,2,3,4,5,6,8],8-[1,2,3,4,5,6,7]] -% ``` -% -% @tbd Simple two-step algorithm. You could be smarter, I suppose. +% ``` +% ?- complement([1-[3,5],2-[4],3-[], +% 4-[1,2,7,5],5-[],6-[],7-[],8-[]], NL). +% NL = [1-[2,4,6,7,8],2-[1,3,5,6,7,8],3-[1,2,4,5,6,7,8], +% 4-[3,5,6,8],5-[1,2,3,4,6,7,8],6-[1,2,3,4,5,7,8], +% 7-[1,2,3,4,5,6,8],8-[1,2,3,4,5,6,7]] +% ``` + + +% TODO: Simple two-step algorithm. You could be smarter, I suppose. complement(G, NG) :- vertices(G,Vs), @@ -611,13 +612,15 @@ complement([V-Ns|G], Vs, [V-INs|NG]) :- ord_subtract(Vs,Ns1,INs), complement(G, Vs, NG). -%! reachable(+Vertex, +UGraph, -Vertices) +%% reachable(+Vertex, +UGraph, -Vertices) % -% True when Vertices is an ordered set of vertices reachable in -% UGraph, including Vertex. Example: +% True when Vertices is an ordered set of vertices reachable in +% UGraph, including Vertex. Example: % -% ?- reachable(1,[1-[3,5],2-[4],3-[],4-[5],5-[]],V). -% V = [1, 3, 5] +% ``` +% ?- reachable(1,[1-[3,5],2-[4],3-[],4-[5],5-[]],V). +% V = [1, 3, 5] +% ``` reachable(N, G, Rs) :- reachable([N], G, [N], Rs). diff --git a/src/lib/uuid.pl b/src/lib/uuid.pl index 3f89db4b..64d20360 100644 --- a/src/lib/uuid.pl +++ b/src/lib/uuid.pl @@ -1,25 +1,32 @@ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Written in February 2021 by Adrián Arroyo (adrian.arroyocalle@gmail.com) Part of Scryer-Prolog - This library provides reasoning about UUID (only version 4 right now). - There are three predicates: - * uuidv4/1, to generate a new UUIDv4 - * uuidv4_string/1, to generate a new UUIDv4 in string hex representation - * uuid_string/2, to converte between UUID list of bytes and UUID hex representation - - Examples: - ?- uuidv4(X). - X = [42,147,248,242,117,196,79,2,129,159|...]. - ?- uuidv4_string(X). - X = "428499fc-76e3-4240- ...". - ?- uuidv4(X), uuid_string(X, S). - X = [173,12,244,152,139,118,64,139,137,4|...], S = "ad0cf498-8b76-408b- ...". - ?- uuid_string(X, "61ae692e-eaf6-4199-8dd3-9f01db70a20b"). - X = [97,174,105,46,234,246,65,153,141,211|...]. - I place this code in the public domain. Use it in any way you want. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/** +This library provides reasoning and working with [UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier) +(only version 4 right now). + +There are three predicates: + + * `uuidv4/1`, to generate a new UUIDv4 + * `uuidv4_string/1`, to generate a new UUIDv4 in string hex representation + * `uuid_string/2`, to converte between UUID list of bytes and UUID hex representation + +Examples: + +``` +?- uuidv4(X). + X = [42,147,248,242,117,196,79,2,129,159|...]. +?- uuidv4_string(X). + X = "428499fc-76e3-4240- ...". +?- uuidv4(X), uuid_string(X, S). + X = [173,12,244,152,139,118,64,139,137,4|...], S = "ad0cf498-8b76-408b- ...". +?- uuid_string(X, "61ae692e-eaf6-4199-8dd3-9f01db70a20b"). + X = [97,174,105,46,234,246,65,153,141,211|...]. +*/ + :- module(uuid, [ uuidv4/1, uuidv4_string/1, @@ -39,6 +46,10 @@ clock_seq_hi_and_res_clock_seq_low - 2 node - 6 UUID v4 can be generated from a set of 16 random bytes: https://www.rfc-archive.org/getrfc.php?rfc=4122#gsc.tab=0 (section 4.4) */ + +%% uuidv4(-Uuid). +% +% Generates a new UUID v4 (random). It unifies with a list of bytes. uuidv4(Uuid) :- crypto_n_random_bytes(16, Bytes), Bytes = [B1, B2, B3, B4, B5, B6, B7, B8, B9, B10, B11, B12, B13, B14, B15, B16], @@ -52,8 +63,15 @@ uuidv4(Uuid) :- byte_bits(NewTimeHi, NewBitsTimeHi), Uuid = [B1, B2, B3, B4, B5, B6, NewTimeHi, B8, NewClockSeqHi0, B10, B11, B12, B13, B14, B15, B16]. +%% uuidv4_string(-UuidString). +% +% Generates a new UUID v4 (random). It unifies with a string representation of the UUID. +% It is equivalent of calling `uuidv4/1` followed by `uuid_string/2`. uuidv4_string(String) :- uuidv4(Uuid), uuid_string(Uuid, String). +%% uuid_string(?UuidBytes, ?UuidString). +% +% Translates between the bytes representation and the string representation of the same UUID. uuid_string(Uuid, String) :- Uuid = [B1, B2, B3, B4, B5, B6, B7, B8, B9, B10, B11, B12, B13, B14, B15, B16], phrase(uuid_([S1, S2, S3, S4, S5]), String), diff --git a/src/lib/xpath.pl b/src/lib/xpath.pl index b4d74770..a871f78d 100644 --- a/src/lib/xpath.pl +++ b/src/lib/xpath.pl @@ -100,214 +100,215 @@ :- use_module(library(dcgs)). :- use_module(library(si)). -/** Select nodes in an XML DOM +/** Select nodes in an XML DOM The library xpath.pl provides predicates to select nodes from an XML DOM -tree as produced by library(sgml) based on descriptions inspired by the -XPath language. +tree as produced by `library(sgml)` based on descriptions inspired by the +[XPath language](http://www.w3.org/TR/xpath). -The predicate xpath/3 selects a sub-structure of the DOM +The predicate `xpath/3` selects a sub-structure of the DOM non-deterministically based on an XPath-like specification. Not all -selectors of XPath are implemented, but the ability to mix xpath/3 calls +selectors of XPath are implemented, but the ability to mix `xpath/3` calls with arbitrary Prolog code provides a powerful tool for extracting information from XML parse-trees. - -@see http://www.w3.org/TR/xpath */ element_name(element(Name,_,_), Name). element_attributes(element(_,Attributes,_), Attributes). element_content(element(_,_,Content), Content). -%! xpath_chk(+DOM, +Spec, ?Content) is semidet. +%% xpath_chk(+DOM, +Spec, ?Content) is semidet. % -% Semi-deterministic version of xpath/3. +% Semi-deterministic version of `xpath/3`. xpath_chk(DOM, Spec, Content) :- xpath(DOM, Spec, Content), !. -%! xpath(+DOM, +Spec, ?Content) is nondet. +%% xpath(+DOM, +Spec, ?Content) is nondet. % -% Match an element in a DOM structure. The syntax is inspired by -% XPath, using () rather than [] to select inside an element. -% First we can construct paths using / and //: +% Match an element in a DOM structure. The syntax is inspired by +% XPath, using () rather than [] to select inside an element. +% First we can construct paths using / and //: % -% $ =|//|=Term : -% Select any node in the DOM matching term. -% $ =|/|=Term : -% Match the root against Term. -% $ Term : -% Select the immediate children of the root matching Term. +% - *//Term* +% Select any node in the DOM matching term. % -% The Terms above are of type _callable_. The functor specifies -% the element name. The element name '*' refers to any element. -% The name =self= refers to the top-element itself and is often -% used for processing matches of an earlier xpath/3 query. A term -% NS:Term refers to an XML name in the namespace NS. Optional -% arguments specify additional constraints and functions. The -% arguments are processed from left to right. Defined conditional -% argument values are: +% - */Term* +% Match the root against Term. % -% $ index(?Index) : -% True if the element is the Index-th child of its parent, -% where 1 denotes the first child. Index can be one of: -% $ `Var` : -% `Var` is unified with the index of the matched element. -% $ =last= : -% True for the last element. -% $ =last= - `IntExpr` : -% True for the last-minus-nth element. For example, -% `last-1` is the element directly preceding the last one. -% $ `IntExpr` : -% True for the element whose index equals `IntExpr`. -% $ Integer : -% The N-th element with the given name, with 1 denoting the -% first element. Same as index(Integer). -% $ =last= : -% The last element with the given name. Same as -% index(last). -% $ =last= - IntExpr : -% The IntExpr-th element before the last. -% Same as index(last-IntExpr). +% - *Term* +% Select the immediate children of the root matching Term. % -% Defined function argument values are: +% The Terms above are of type _callable_. The functor specifies +% the element name. The element name `*` refers to any element. +% The name _self_ refers to the top-element itself and is often +% used for processing matches of an earlier `xpath/3` query. A term +% NS:Term refers to an XML name in the namespace NS. Optional +% arguments specify additional constraints and functions. The +% arguments are processed from left to right. Defined conditional +% argument values are: % -% $ =self= : -% Evaluate to the entire element -% $ =content= : -% Evaluate to the content of the element (a list) -% $ =text= : -% Evaluates to all text from the sub-tree, represented -% as a list of characters. -% $ `text(atom)` : -% Evaluates to all text from the sub-tree as an atom. -% $ =normalize_space= : -% As =text=, but uses normalize_space/2 to normalise -% white-space in the output -% $ =number= : -% Extract an integer or float from the value. Ignores -% leading and trailing white-space -% $ =|@|=Attribute : -% Evaluates to the value of the given attribute. Attribute -% can be a compound term. In this case the functor name -% denotes the element and arguments perform transformations -% on the attribute value. Defined transformations are: +% - *`index(?Index)`* +% True if the element is the Index-th child of its parent, +% where 1 denotes the first child. Index can be one of: % -% - number -% Translate the value into a number using -% xsd_number_chars/2. -% - integer -% As `number`, but subsequently transform the value -% into an integer using the round/1 function. -% - float -% As `number`, but subsequently transform the value -% into a float using the float/1 function. -% - lower -% Translate the value to lower case, preserving -% the type. -% - upper -% Translate the value to upper case, preserving -% the type. +% - *`Var`* +% `Var` is unified with the index of the matched element. +% - *`last`* +% True for the last element. +% - *`last - IntExpr`* +% True for the last-minus-nth element. For example, +% `last-1` is the element directly preceding the last one. +% - *`IntExpr`* +% True for the element whose index equals `IntExpr`. +% - *`Integer`* +% The N-th element with the given name, with 1 denoting the +% first element. Same as `index(Integer)`. +% - *`last`* +% The last element with the given name. Same as +% `index(last)`. +% - *`last - IntExpr`* +% The IntExpr-th element before the last. +% Same as `index(last-IntExpr)`. % -% In addition, the argument-list can be _conditions_: +% Defined function argument values are: % -% $ Left = Right : -% Succeeds if the left-hand unifies with the right-hand. -% If the left-hand side is a function, this is evaluated. -% The right-hand side is _never_ evaluated, and thus the -% condition `content = content` defines that the content -% of the element is the atom `content`. -% The functions `lower_case` and `upper_case` can be applied -% to Right (see example below). -% $ contains(Haystack, Needle) : -% Succeeds if Needle is a sub-list of Haystack. -% $ XPath : -% Succeeds if XPath matches in the currently selected -% sub-DOM. For example, the following expression finds -% an =h3= element inside a =div= element, where the =div= -% element itself contains an =h2= child with a =strong= -% child. +% - *`self`* +% Evaluate to the entire element +% - *`content`* +% Evaluate to the content of the element (a list) +% - *`text`* +% Evaluates to all text from the sub-tree, represented +% as a list of characters. +% - *`text(atom)`* +% Evaluates to all text from the sub-tree as an atom. +% - *`normalize_space`* +% As `text`, but uses `normalize_space/2` to normalise +% white-space in the output +% - *`number`* +% Extract an integer or float from the value. Ignores +% leading and trailing white-space +% - *`@Attribute`* +% Evaluates to the value of the given attribute. Attribute +% can be a compound term. In this case the functor name +% denotes the element and arguments perform transformations +% on the attribute value. Defined transformations are: % -% == -% //div(h2/strong)/h3 -% == +% - *`number`* +% Translate the value into a number using +% `xsd_number_chars/2`. +% - *`integer`* +% As `number`, but subsequently transform the value +% into an integer using the `round/1` function. +% - *`float`* +% As `number`, but subsequently transform the value +% into a float using the `float/1` function. +% - *`lower`* +% Translate the value to lower case, preserving +% the type. +% - *`upper`* +% Translate the value to upper case, preserving +% the type. % -% This is equivalent to the conjunction of XPath goals below. +% In addition, the argument-list can be _conditions_: % -% == -% ..., -% xpath(DOM, //(div), Div), -% xpath(Div, h2/strong, _), -% xpath(Div, h3, Result) -% == +% - *`Left = Right`* +% Succeeds if the left-hand unifies with the right-hand. +% If the left-hand side is a function, this is evaluated. +% The right-hand side is _never_ evaluated, and thus the +% condition `content = content` defines that the content +% of the element is the atom `content`. +% The functions `lower_case` and `upper_case` can be applied +% to Right (see example below). +% - *`contains(Haystack, Needle)`* +% Succeeds if Needle is a sub-list of Haystack. +% - *`XPath`* +% Succeeds if XPath matches in the currently selected +% sub-DOM. For example, the following expression finds +% an `h3` element inside a `div` element, where the `div` +% element itself contains an `h2` child with a `strong` +% child. % -% **Examples**: +% ``` +% //div(h2/strong)/h3 +% ``` % -% Match each table-row in DOM: +% This is equivalent to the conjunction of XPath goals below. % -% == -% xpath(DOM, //tr, TR) -% == +% ``` +% ..., +% xpath(DOM, //(div), Div), +% xpath(Div, h2/strong, _), +% xpath(Div, h3, Result) +% ``` % -% Match the last cell of each tablerow in DOM. This example -% illustrates that a result can be the input of subsequent xpath/3 -% queries. Using multiple queries on the intermediate TR term -% guarantee that all results come from the same table-row: +% #### Examples % -% == -% xpath(DOM, //tr, TR), -% xpath(TR, /td(last), TD) -% == +% Match each table-row in DOM: % -% Match each =href= attribute in an element +% ``` +% xpath(DOM, //tr, TR) +% ``` % -% == -% xpath(DOM, //a(@href), HREF) -% == +% Match the last cell of each tablerow in DOM. This example +% illustrates that a result can be the input of subsequent `xpath/3` +% queries. Using multiple queries on the intermediate TR term +% guarantee that all results come from the same table-row: % -% Suppose we have a table containing rows where each first column -% is the name of a product with a link to details and the second -% is the price (a number). The following predicate matches the -% name, URL and price: +% ``` +% xpath(DOM, //tr, TR), +% xpath(TR, /td(last), TD) +% ``` % -% == -% product(DOM, Name, URL, Price) :- -% xpath(DOM, //tr, TR), -% xpath(TR, td(1), C1), -% xpath(C1, /self(normalize_space), Name), -% xpath(C1, a(@href), URL), -% xpath(TR, td(2, number), Price). -% == +% Match each `href` attribute in an `` element % -% Suppose we want to select books with genre="thriller" from a -% tree containing elements =||= +% ``` +% xpath(DOM, //a(@href), HREF) +% ``` % -% == -% thriller(DOM, Book) :- -% xpath(DOM, //book(@genre=thiller), Book). -% == +% Suppose we have a table containing rows where each first column +% is the name of a product with a link to details and the second +% is the price (a number). The following predicate matches the +% name, URL and price: % -% Match the elements =||= _and_ =|
|=: +% ``` +% product(DOM, Name, URL, Price) :- +% xpath(DOM, //tr, TR), +% xpath(TR, td(1), C1), +% xpath(C1, /self(normalize_space), Name), +% xpath(C1, a(@href), URL), +% xpath(TR, td(2, number), Price). +% ``` % -% ```prolog -% //table(@align(lower) = center) -% ``` +% Suppose we want to select books with genre="thriller" from a +% tree containing elements `` % -% Get the `width` and `height` of a `div` element as a number, -% and the `div` node itself: +% ``` +% thriller(DOM, Book) :- +% xpath(DOM, //book(@genre=thiller), Book). +% ``` % -% == -% xpath(DOM, //div(@width(number)=W, @height(number)=H), Div) -% == +% Match the elements `
` _and_ `
`: % -% Note that `div` is an infix operator, so parentheses must be -% used in cases like the following: +% ``` +% //table(@align(lower) = center) +% ``` % -% == -% xpath(DOM, //(div), Div) -% == +% Get the `width` and `height` of a `div` element as a number, +% and the `div` node itself: +% +% ``` +% xpath(DOM, //div(@width(number)=W, @height(number)=H), Div) +% ``` +% +% Note that `div` is an infix operator, so parentheses must be +% used in cases like the following: +% +% ``` +% xpath(DOM, //(div), Div) +% ``` xpath(DOM, Spec, Content) :- in_dom(Spec, DOM, Content). diff --git a/src/loader.pl b/src/loader.pl index 8365d761..c7479d50 100644 --- a/src/loader.pl +++ b/src/loader.pl @@ -11,7 +11,6 @@ current_module/1 ]). - :- use_module(library(error)). :- use_module(library(lists)). :- use_module(library(pairs)). @@ -25,10 +24,14 @@ write_error(Error) :- ; write(' ') % if '$first_answer' isn't defined yet or true, % print indentation. ), + ( current_prolog_flag(double_quotes, chars) -> + DQ = true + ; DQ = false + ), ( nonvar(Error), functor(Error, error, 2) -> - writeq(Error) - ; writeq(throw(Error)) + write_term(Error, [ignore_ops(false), numbervars(true), quoted(true), double_quotes(DQ)]) + ; write_term(throw(Error), [ignore_ops(false), numbervars(true), quoted(true), double_quotes(DQ)]) ), write('.'). @@ -221,7 +224,12 @@ complete_partial_goal(N, HeadArg, InnerHeadArgs, SuppArgs, CompleteHeadArg) :- integer(N), N >= 0, HeadArg =.. [Functor | InnerHeadArgs], - length(SuppArgs, N), + % the next two lines are equivalent to length(SuppArgs, N) but + % avoid length/2 so that copy_term/3 (which is invoked by + % length/2) can be bootstrapped without self-reference. + functor(SuppArgsFunctor, '.', N), + SuppArgsFunctor =.. [_ | SuppArgs], + % length(SuppArgs, N), append(InnerHeadArgs, SuppArgs, InnerHeadArgs0), CompleteHeadArg =.. [Functor | InnerHeadArgs0]. @@ -266,6 +274,13 @@ module_expanded_head_variables(Head, HeadVars) :- ). +print_goal_expansion_warning(Pred) :- + nl, + write('Warning: clause body goal expansion failed because '), + writeq(Pred), + write(' is not callable.'), + nl. + expand_term_goals(Terms0, Terms) :- ( Terms0 = (Head1 :- Body0) -> ( var(Head1) -> @@ -274,13 +289,21 @@ expand_term_goals(Terms0, Terms) :- ( atom(Module) -> prolog_load_context(module, Target), module_expanded_head_variables(Head2, HeadVars), - expand_goal(Body0, Target, Body1, HeadVars), + catch(expand_goal(Body0, Target, Body1, HeadVars), + error(type_error(callable, Pred), _), + ( loader:print_goal_expansion_warning(Pred), + builtins:(Body1 = Body0) + )), Terms = (Module:Head2 :- Body1) ; type_error(atom, Module, load/1) ) ; module_expanded_head_variables(Head1, HeadVars), prolog_load_context(module, Target), - expand_goal(Body0, Target, Body1, HeadVars), + catch(expand_goal(Body0, Target, Body1, HeadVars), + error(type_error(callable, Pred), _), + ( loader:print_goal_expansion_warning(Pred), + builtins:(Body1 = Body0) + )), Terms = (Head1 :- Body1) ) ; Terms = Terms0 @@ -537,6 +560,7 @@ open_file(Path, Stream) :- ) ). + use_module(Module, Exports, Evacuable) :- ( var(Module) -> instantiation_error(load/1) @@ -558,7 +582,7 @@ use_module(Module, Exports, Evacuable) :- stream_property(Stream, file_name(PathFileName)), file_load(Stream, PathFileName, Subevacuable), '$use_module'(Evacuable, Subevacuable, Exports) - ; type_error(atom, Library, load/1) + ; type_error(atom, Module, load/1) ) ). @@ -623,7 +647,7 @@ strip_module(Goal, M, G) :- strip_subst_module(Goal, M1, M2, G) :- '$strip_module'(Goal, M2, G), - ( var(M2) -> + ( var(M2), \+ functor(Goal, (:), 2) -> M2 = M1 ; true ). @@ -683,7 +707,10 @@ expand_subgoal(UnexpandedGoals, MS, M, ExpandedGoals, HeadVars) :- expand_module_name(ESG0, MS, M, ESG) :- ( var(ESG0) -> - ESG = M:ESG0 + ( M == user -> + ESG = ESG0 + ; ESG = M:ESG0 + ) ; ESG0 = _:_ -> ESG = ESG0 ; functor(ESG0, F, A0), @@ -748,7 +775,6 @@ expand_module_names(Goals, MetaSpecs, Module, ExpandedGoals, HeadVars) :- ). - :- non_counted_backtracking expand_goal/3. expand_goal(UnexpandedGoals, Module, ExpandedGoals) :- @@ -757,7 +783,6 @@ expand_goal(UnexpandedGoals, Module, ExpandedGoals) :- UnexpandedGoals = ExpandedGoals), !. - :- non_counted_backtracking expand_goal/4. expand_goal(UnexpandedGoals, Module, ExpandedGoals, HeadVars) :- @@ -778,7 +803,6 @@ expand_goal(UnexpandedGoals, Module, ExpandedGoals, HeadVars) :- ) ). - /* * private predicate for use in call/N. it doesn't specially consider * control predicates as expand_goal does with expand_goal_cases. @@ -789,27 +813,20 @@ expand_goal(UnexpandedGoals, Module, ExpandedGoals, HeadVars) :- expand_call_goal(UnexpandedGoals, Module, ExpandedGoals) :- % if a goal isn't callable, defer to call/N to report the error. - catch(loader:expand_call_goal_(UnexpandedGoals, Module, ExpandedGoals), + catch('$call'(loader:expand_call_goal_(UnexpandedGoals, Module, ExpandedGoals)), error(type_error(callable, _), _), - UnexpandedGoals = ExpandedGoals), + '$call'(UnexpandedGoals = ExpandedGoals)), !. - :- non_counted_backtracking expand_call_goal_/3. expand_call_goal_(UnexpandedGoals, Module, ExpandedGoals) :- ( var(UnexpandedGoals) -> - expand_module_names(call(UnexpandedGoals), [0], Module, ExpandedGoals, []) + UnexpandedGoals = ExpandedGoals ; goal_expansion(UnexpandedGoals, Module, UnexpandedGoals1), ( Module \== user -> - goal_expansion(UnexpandedGoals1, user, Goals) - ; Goals = UnexpandedGoals1 - ), - ( predicate_property(Module:Goals, meta_predicate(MetaSpecs0)), - MetaSpecs0 =.. [_ | MetaSpecs] -> - expand_module_names(Goals, MetaSpecs, Module, ExpandedGoals, []) - ; thread_goals(Goals, ExpandedGoals, (',')) - ; Goals = ExpandedGoals + goal_expansion(UnexpandedGoals1, user, ExpandedGoals) + ; ExpandedGoals = UnexpandedGoals1 ) ). @@ -837,7 +854,6 @@ expand_goal_cases((Module:Goals0), _, ExpandedGoals, HeadVars) :- expand_goal(Goals0, Module, Goals1, HeadVars), ExpandedGoals = (Module:Goals1). - :- non_counted_backtracking thread_goals/3. thread_goals(Goals0, Goals1, Functor) :- @@ -852,7 +868,6 @@ thread_goals(Goals0, Goals1, Functor) :- ; Goals1 = Goals0 ). - :- non_counted_backtracking thread_goals/4. thread_goals(Goals0, Goals1, Hole, Functor) :- @@ -871,8 +886,6 @@ thread_goals(Goals0, Goals1, Hole, Functor) :- % % call/{1-64} with dynamic goal expansion. % -% The program used to generate the call/N predicates: -% % :- use_module(library(between)). % :- use_module(library(error)). % :- use_module(library(lists)). @@ -883,22 +896,18 @@ thread_goals(Goals0, Goals1, Hole, Functor) :- % Head =.. [call, G | Args], % CallNHead =.. [call, '$call'(G) | Args], % N1 is N + 1, -% InlineCall =.. ['$call_inline', G0 | Args], -% CallClause =.. ['$prepare_call_clause', G1, M1, G | Args], -% ModuleCallClause0 =.. ['$module_call', M1, G1], -% ModuleCallClause1 =.. ['$module_call', M2, G3], +% StripModule =.. ['$strip_module', G, M1, G1], +% FastCall =.. ['$fast_call', G | Args], +% PrepareCallClause =.. [ '$prepare_call_clause', G2, G1 | Args], +% ModuleCall =.. ['$module_call', M2, G4], % Clauses = [(Head :- var(G), % instantiation_error(call/N1)), -% (Head :- '$strip_module'(G, _, G0), InlineCall), -% (CallNHead :- !, -% CallClause, -% '$call_with_inference_counting'(ModuleCallClause0)), -% (Head :- CallClause, -% ( '$call_inline'(G1) -% ; expand_call_goal(G1, M1, G2), -% strip_subst_module(G2, M1, M2, G3), -% '$call_with_inference_counting'(ModuleCallClause1) -% ))]. +% (Head :- FastCall), +% (Head :- StripModule, +% PrepareCallClause, +% expand_call_goal(G2, M1, G3), +% strip_subst_module(G3, M1, M2, G4), +% '$call_with_inference_counting'(ModuleCall))]. % % generate_call_forms :- % between(1, 64, N), @@ -914,1237 +923,847 @@ thread_goals(Goals0, Goals1, Hole, Functor) :- % The '$call' functor is an escape hatch from goal expansion. So far, % it is used only to avoid infinite recursion into expand_call_goal/3. -:-non_counted_backtracking call/1. +:- non_counted_backtracking call/1. + call(G) :- - var(G), - instantiation_error(call/1). + var(G), + instantiation_error(call/1). call(G) :- - '$strip_module'(G, _, G0), - '$call_inline'(G0). -call('$call'(G0)) :- - !, - '$prepare_call_clause'(G,M,G0), - '$call_with_inference_counting'('$module_call'(M, G)). -call(G) :- - '$prepare_call_clause'(G0,M1,G), - ( '$call_inline'(G0) %% '$call_inline' cuts (only) after succeeding. - ; expand_call_goal(G0, M1, G1), - strip_subst_module(G1, M1, M2, G2), - '$call_with_inference_counting'('$module_call'(M2, G2)) - ). + '$fast_call'(G). +call(G0) :- + '$strip_module'(G0, M0, G1), + expand_call_goal(G1, M0, G2), + strip_subst_module(G2, M0, M1, G3), + '$call_with_inference_counting'('$module_call'(M1, G3)). :-non_counted_backtracking call/2. call(A,B) :- var(A), instantiation_error(call/2). call(A,B) :- - '$strip_module'(A,C,D), - '$call_inline'(D,B). -call('$call'(A),B) :- - !, - '$prepare_call_clause'(C,D,A,B), - '$call_with_inference_counting'('$module_call'(D,C)). + '$fast_call'(A,B). call(A,B) :- - '$prepare_call_clause'(C,D,A,B), - ( '$call_inline'(C) - ; expand_call_goal(C,D,E), - strip_subst_module(E,D,F,G), - '$call_with_inference_counting'('$module_call'(F,G)) - ). + '$strip_module'(A,C,D), + '$prepare_call_clause'(E,D,B), + expand_call_goal(E,C,F), + strip_subst_module(F,C,G,H), + '$call_with_inference_counting'('$module_call'(G,H)). :-non_counted_backtracking call/3. call(A,B,C) :- var(A), instantiation_error(call/3). call(A,B,C) :- - '$strip_module'(A,D,E), - '$call_inline'(E,B,C). -call('$call'(A),B,C) :- - !, - '$prepare_call_clause'(D,E,A,B,C), - '$call_with_inference_counting'('$module_call'(E,D)). + '$fast_call'(A,B,C). call(A,B,C) :- - '$prepare_call_clause'(D,E,A,B,C), - ( '$call_inline'(D) - ; expand_call_goal(D,E,F), - strip_subst_module(F,E,G,H), - '$call_with_inference_counting'('$module_call'(G,H)) - ). + '$strip_module'(A,D,E), + '$prepare_call_clause'(F,E,B,C), + expand_call_goal(F,D,G), + strip_subst_module(G,D,H,I), + '$call_with_inference_counting'('$module_call'(H,I)). :-non_counted_backtracking call/4. call(A,B,C,D) :- var(A), instantiation_error(call/4). call(A,B,C,D) :- - '$strip_module'(A,E,F), - '$call_inline'(F,B,C,D). -call('$call'(A),B,C,D) :- - !, - '$prepare_call_clause'(E,F,A,B,C,D), - '$call_with_inference_counting'('$module_call'(F,E)). + '$fast_call'(A,B,C,D). call(A,B,C,D) :- - '$prepare_call_clause'(E,F,A,B,C,D), - ( '$call_inline'(E) - ; expand_call_goal(E,F,G), - strip_subst_module(G,F,H,I), - '$call_with_inference_counting'('$module_call'(H,I)) - ). + '$strip_module'(A,E,F), + '$prepare_call_clause'(G,F,B,C,D), + expand_call_goal(G,E,H), + strip_subst_module(H,E,I,J), + '$call_with_inference_counting'('$module_call'(I,J)). :-non_counted_backtracking call/5. call(A,B,C,D,E) :- var(A), instantiation_error(call/5). call(A,B,C,D,E) :- - '$strip_module'(A,F,G), - '$call_inline'(G,B,C,D,E). -call('$call'(A),B,C,D,E) :- - !, - '$prepare_call_clause'(F,G,A,B,C,D,E), - '$call_with_inference_counting'('$module_call'(G,F)). + '$fast_call'(A,B,C,D,E). call(A,B,C,D,E) :- - '$prepare_call_clause'(F,G,A,B,C,D,E), - ( '$call_inline'(F) - ; expand_call_goal(F,G,H), - strip_subst_module(H,G,I,J), - '$call_with_inference_counting'('$module_call'(I,J)) - ). + '$strip_module'(A,F,G), + '$prepare_call_clause'(H,G,B,C,D,E), + expand_call_goal(H,F,I), + strip_subst_module(I,F,J,K), + '$call_with_inference_counting'('$module_call'(J,K)). :-non_counted_backtracking call/6. call(A,B,C,D,E,F) :- var(A), instantiation_error(call/6). call(A,B,C,D,E,F) :- - '$strip_module'(A,G,H), - '$call_inline'(H,B,C,D,E,F). -call('$call'(A),B,C,D,E,F) :- - !, - '$prepare_call_clause'(G,H,A,B,C,D,E,F), - '$call_with_inference_counting'('$module_call'(H,G)). + '$fast_call'(A,B,C,D,E,F). call(A,B,C,D,E,F) :- - '$prepare_call_clause'(G,H,A,B,C,D,E,F), - ( '$call_inline'(G) - ; expand_call_goal(G,H,I), - strip_subst_module(I,H,J,K), - '$call_with_inference_counting'('$module_call'(J,K)) - ). + '$strip_module'(A,G,H), + '$prepare_call_clause'(I,H,B,C,D,E,F), + expand_call_goal(I,G,J), + strip_subst_module(J,G,K,L), + '$call_with_inference_counting'('$module_call'(K,L)). :-non_counted_backtracking call/7. call(A,B,C,D,E,F,G) :- var(A), instantiation_error(call/7). call(A,B,C,D,E,F,G) :- - '$strip_module'(A,H,I), - '$call_inline'(I,B,C,D,E,F,G). -call('$call'(A),B,C,D,E,F,G) :- - !, - '$prepare_call_clause'(H,I,A,B,C,D,E,F,G), - '$call_with_inference_counting'('$module_call'(I,H)). + '$fast_call'(A,B,C,D,E,F,G). call(A,B,C,D,E,F,G) :- - '$prepare_call_clause'(H,I,A,B,C,D,E,F,G), - ( '$call_inline'(H) - ; expand_call_goal(H,I,J), - strip_subst_module(J,I,K,L), - '$call_with_inference_counting'('$module_call'(K,L)) - ). + '$strip_module'(A,H,I), + '$prepare_call_clause'(J,I,B,C,D,E,F,G), + expand_call_goal(J,H,K), + strip_subst_module(K,H,L,M), + '$call_with_inference_counting'('$module_call'(L,M)). :-non_counted_backtracking call/8. call(A,B,C,D,E,F,G,H) :- var(A), instantiation_error(call/8). call(A,B,C,D,E,F,G,H) :- - '$strip_module'(A,I,J), - '$call_inline'(J,B,C,D,E,F,G,H). -call('$call'(A),B,C,D,E,F,G,H) :- - !, - '$prepare_call_clause'(I,J,A,B,C,D,E,F,G,H), - '$call_with_inference_counting'('$module_call'(J,I)). + '$fast_call'(A,B,C,D,E,F,G,H). call(A,B,C,D,E,F,G,H) :- - '$prepare_call_clause'(I,J,A,B,C,D,E,F,G,H), - ( '$call_inline'(I) - ; expand_call_goal(I,J,K), - strip_subst_module(K,J,L,M), - '$call_with_inference_counting'('$module_call'(L,M)) - ). + '$strip_module'(A,I,J), + '$prepare_call_clause'(K,J,B,C,D,E,F,G,H), + expand_call_goal(K,I,L), + strip_subst_module(L,I,M,N), + '$call_with_inference_counting'('$module_call'(M,N)). :-non_counted_backtracking call/9. call(A,B,C,D,E,F,G,H,I) :- var(A), instantiation_error(call/9). call(A,B,C,D,E,F,G,H,I) :- - '$strip_module'(A,J,K), - '$call_inline'(K,B,C,D,E,F,G,H,I). -call('$call'(A),B,C,D,E,F,G,H,I) :- - !, - '$prepare_call_clause'(J,K,A,B,C,D,E,F,G,H,I), - '$call_with_inference_counting'('$module_call'(K,J)). + '$fast_call'(A,B,C,D,E,F,G,H,I). call(A,B,C,D,E,F,G,H,I) :- - '$prepare_call_clause'(J,K,A,B,C,D,E,F,G,H,I), - ( '$call_inline'(J) - ; expand_call_goal(J,K,L), - strip_subst_module(L,K,M,N), - '$call_with_inference_counting'('$module_call'(M,N)) - ). + '$strip_module'(A,J,K), + '$prepare_call_clause'(L,K,B,C,D,E,F,G,H,I), + expand_call_goal(L,J,M), + strip_subst_module(M,J,N,O), + '$call_with_inference_counting'('$module_call'(N,O)). :-non_counted_backtracking call/10. call(A,B,C,D,E,F,G,H,I,J) :- var(A), instantiation_error(call/10). call(A,B,C,D,E,F,G,H,I,J) :- - '$strip_module'(A,K,L), - '$call_inline'(L,B,C,D,E,F,G,H,I,J). -call('$call'(A),B,C,D,E,F,G,H,I,J) :- - !, - '$prepare_call_clause'(K,L,A,B,C,D,E,F,G,H,I,J), - '$call_with_inference_counting'('$module_call'(L,K)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J). call(A,B,C,D,E,F,G,H,I,J) :- - '$prepare_call_clause'(K,L,A,B,C,D,E,F,G,H,I,J), - ( '$call_inline'(K) - ; expand_call_goal(K,L,M), - strip_subst_module(M,L,N,O), - '$call_with_inference_counting'('$module_call'(N,O)) - ). + '$strip_module'(A,K,L), + '$prepare_call_clause'(M,L,B,C,D,E,F,G,H,I,J), + expand_call_goal(M,K,N), + strip_subst_module(N,K,O,P), + '$call_with_inference_counting'('$module_call'(O,P)). :-non_counted_backtracking call/11. call(A,B,C,D,E,F,G,H,I,J,K) :- var(A), instantiation_error(call/11). call(A,B,C,D,E,F,G,H,I,J,K) :- - '$strip_module'(A,L,M), - '$call_inline'(M,B,C,D,E,F,G,H,I,J,K). -call('$call'(A),B,C,D,E,F,G,H,I,J,K) :- - !, - '$prepare_call_clause'(L,M,A,B,C,D,E,F,G,H,I,J,K), - '$call_with_inference_counting'('$module_call'(M,L)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K). call(A,B,C,D,E,F,G,H,I,J,K) :- - '$prepare_call_clause'(L,M,A,B,C,D,E,F,G,H,I,J,K), - ( '$call_inline'(L) - ; expand_call_goal(L,M,N), - strip_subst_module(N,M,O,P), - '$call_with_inference_counting'('$module_call'(O,P)) - ). + '$strip_module'(A,L,M), + '$prepare_call_clause'(N,M,B,C,D,E,F,G,H,I,J,K), + expand_call_goal(N,L,O), + strip_subst_module(O,L,P,Q), + '$call_with_inference_counting'('$module_call'(P,Q)). :-non_counted_backtracking call/12. call(A,B,C,D,E,F,G,H,I,J,K,L) :- var(A), instantiation_error(call/12). call(A,B,C,D,E,F,G,H,I,J,K,L) :- - '$strip_module'(A,M,N), - '$call_inline'(N,B,C,D,E,F,G,H,I,J,K,L). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L) :- - !, - '$prepare_call_clause'(M,N,A,B,C,D,E,F,G,H,I,J,K,L), - '$call_with_inference_counting'('$module_call'(N,M)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L). call(A,B,C,D,E,F,G,H,I,J,K,L) :- - '$prepare_call_clause'(M,N,A,B,C,D,E,F,G,H,I,J,K,L), - ( '$call_inline'(M) - ; expand_call_goal(M,N,O), - strip_subst_module(O,N,P,Q), - '$call_with_inference_counting'('$module_call'(P,Q)) - ). + '$strip_module'(A,M,N), + '$prepare_call_clause'(O,N,B,C,D,E,F,G,H,I,J,K,L), + expand_call_goal(O,M,P), + strip_subst_module(P,M,Q,R), + '$call_with_inference_counting'('$module_call'(Q,R)). :-non_counted_backtracking call/13. call(A,B,C,D,E,F,G,H,I,J,K,L,M) :- var(A), instantiation_error(call/13). call(A,B,C,D,E,F,G,H,I,J,K,L,M) :- - '$strip_module'(A,N,O), - '$call_inline'(O,B,C,D,E,F,G,H,I,J,K,L,M). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M) :- - !, - '$prepare_call_clause'(N,O,A,B,C,D,E,F,G,H,I,J,K,L,M), - '$call_with_inference_counting'('$module_call'(O,N)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M). call(A,B,C,D,E,F,G,H,I,J,K,L,M) :- - '$prepare_call_clause'(N,O,A,B,C,D,E,F,G,H,I,J,K,L,M), - ( '$call_inline'(N) - ; expand_call_goal(N,O,P), - strip_subst_module(P,O,Q,R), - '$call_with_inference_counting'('$module_call'(Q,R)) - ). + '$strip_module'(A,N,O), + '$prepare_call_clause'(P,O,B,C,D,E,F,G,H,I,J,K,L,M), + expand_call_goal(P,N,Q), + strip_subst_module(Q,N,R,S), + '$call_with_inference_counting'('$module_call'(R,S)). :-non_counted_backtracking call/14. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N) :- var(A), instantiation_error(call/14). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N) :- - '$strip_module'(A,O,P), - '$call_inline'(P,B,C,D,E,F,G,H,I,J,K,L,M,N). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N) :- - !, - '$prepare_call_clause'(O,P,A,B,C,D,E,F,G,H,I,J,K,L,M,N), - '$call_with_inference_counting'('$module_call'(P,O)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N) :- - '$prepare_call_clause'(O,P,A,B,C,D,E,F,G,H,I,J,K,L,M,N), - ( '$call_inline'(O) - ; expand_call_goal(O,P,Q), - strip_subst_module(Q,P,R,S), - '$call_with_inference_counting'('$module_call'(R,S)) - ). + '$strip_module'(A,O,P), + '$prepare_call_clause'(Q,P,B,C,D,E,F,G,H,I,J,K,L,M,N), + expand_call_goal(Q,O,R), + strip_subst_module(R,O,S,T), + '$call_with_inference_counting'('$module_call'(S,T)). :-non_counted_backtracking call/15. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O) :- var(A), instantiation_error(call/15). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O) :- - '$strip_module'(A,P,Q), - '$call_inline'(Q,B,C,D,E,F,G,H,I,J,K,L,M,N,O). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O) :- - !, - '$prepare_call_clause'(P,Q,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O), - '$call_with_inference_counting'('$module_call'(Q,P)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O) :- - '$prepare_call_clause'(P,Q,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O), - ( '$call_inline'(P) - ; expand_call_goal(P,Q,R), - strip_subst_module(R,Q,S,T), - '$call_with_inference_counting'('$module_call'(S,T)) - ). + '$strip_module'(A,P,Q), + '$prepare_call_clause'(R,Q,B,C,D,E,F,G,H,I,J,K,L,M,N,O), + expand_call_goal(R,P,S), + strip_subst_module(S,P,T,U), + '$call_with_inference_counting'('$module_call'(T,U)). :-non_counted_backtracking call/16. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P) :- var(A), instantiation_error(call/16). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P) :- - '$strip_module'(A,Q,R), - '$call_inline'(R,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P) :- - !, - '$prepare_call_clause'(Q,R,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P), - '$call_with_inference_counting'('$module_call'(R,Q)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P) :- - '$prepare_call_clause'(Q,R,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P), - ( '$call_inline'(Q) - ; expand_call_goal(Q,R,S), - strip_subst_module(S,R,T,U), - '$call_with_inference_counting'('$module_call'(T,U)) - ). + '$strip_module'(A,Q,R), + '$prepare_call_clause'(S,R,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P), + expand_call_goal(S,Q,T), + strip_subst_module(T,Q,U,V), + '$call_with_inference_counting'('$module_call'(U,V)). :-non_counted_backtracking call/17. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q) :- var(A), instantiation_error(call/17). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q) :- - '$strip_module'(A,R,S), - '$call_inline'(S,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q) :- - !, - '$prepare_call_clause'(R,S,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q), - '$call_with_inference_counting'('$module_call'(S,R)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q) :- - '$prepare_call_clause'(R,S,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q), - ( '$call_inline'(R) - ; expand_call_goal(R,S,T), - strip_subst_module(T,S,U,V), - '$call_with_inference_counting'('$module_call'(U,V)) - ). + '$strip_module'(A,R,S), + '$prepare_call_clause'(T,S,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q), + expand_call_goal(T,R,U), + strip_subst_module(U,R,V,W), + '$call_with_inference_counting'('$module_call'(V,W)). :-non_counted_backtracking call/18. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R) :- var(A), instantiation_error(call/18). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R) :- - '$strip_module'(A,S,T), - '$call_inline'(T,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R) :- - !, - '$prepare_call_clause'(S,T,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R), - '$call_with_inference_counting'('$module_call'(T,S)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R) :- - '$prepare_call_clause'(S,T,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R), - ( '$call_inline'(S) - ; expand_call_goal(S,T,U), - strip_subst_module(U,T,V,W), - '$call_with_inference_counting'('$module_call'(V,W)) - ). + '$strip_module'(A,S,T), + '$prepare_call_clause'(U,T,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R), + expand_call_goal(U,S,V), + strip_subst_module(V,S,W,X), + '$call_with_inference_counting'('$module_call'(W,X)). :-non_counted_backtracking call/19. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S) :- var(A), instantiation_error(call/19). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S) :- - '$strip_module'(A,T,U), - '$call_inline'(U,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S) :- - !, - '$prepare_call_clause'(T,U,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S), - '$call_with_inference_counting'('$module_call'(U,T)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S) :- - '$prepare_call_clause'(T,U,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S), - ( '$call_inline'(T) - ; expand_call_goal(T,U,V), - strip_subst_module(V,U,W,X), - '$call_with_inference_counting'('$module_call'(W,X)) - ). + '$strip_module'(A,T,U), + '$prepare_call_clause'(V,U,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S), + expand_call_goal(V,T,W), + strip_subst_module(W,T,X,Y), + '$call_with_inference_counting'('$module_call'(X,Y)). :-non_counted_backtracking call/20. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T) :- var(A), instantiation_error(call/20). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T) :- - '$strip_module'(A,U,V), - '$call_inline'(V,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T) :- - !, - '$prepare_call_clause'(U,V,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T), - '$call_with_inference_counting'('$module_call'(V,U)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T) :- - '$prepare_call_clause'(U,V,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T), - ( '$call_inline'(U) - ; expand_call_goal(U,V,W), - strip_subst_module(W,V,X,Y), - '$call_with_inference_counting'('$module_call'(X,Y)) - ). + '$strip_module'(A,U,V), + '$prepare_call_clause'(W,V,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T), + expand_call_goal(W,U,X), + strip_subst_module(X,U,Y,Z), + '$call_with_inference_counting'('$module_call'(Y,Z)). :-non_counted_backtracking call/21. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U) :- var(A), instantiation_error(call/21). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U) :- - '$strip_module'(A,V,W), - '$call_inline'(W,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U) :- - !, - '$prepare_call_clause'(V,W,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U), - '$call_with_inference_counting'('$module_call'(W,V)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U) :- - '$prepare_call_clause'(V,W,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U), - ( '$call_inline'(V) - ; expand_call_goal(V,W,X), - strip_subst_module(X,W,Y,Z), - '$call_with_inference_counting'('$module_call'(Y,Z)) - ). + '$strip_module'(A,V,W), + '$prepare_call_clause'(X,W,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U), + expand_call_goal(X,V,Y), + strip_subst_module(Y,V,Z,A1), + '$call_with_inference_counting'('$module_call'(Z,A1)). :-non_counted_backtracking call/22. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V) :- var(A), instantiation_error(call/22). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V) :- - '$strip_module'(A,W,X), - '$call_inline'(X,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V) :- - !, - '$prepare_call_clause'(W,X,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V), - '$call_with_inference_counting'('$module_call'(X,W)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V) :- - '$prepare_call_clause'(W,X,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V), - ( '$call_inline'(W) - ; expand_call_goal(W,X,Y), - strip_subst_module(Y,X,Z,A1), - '$call_with_inference_counting'('$module_call'(Z,A1)) - ). + '$strip_module'(A,W,X), + '$prepare_call_clause'(Y,X,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V), + expand_call_goal(Y,W,Z), + strip_subst_module(Z,W,A1,B1), + '$call_with_inference_counting'('$module_call'(A1,B1)). :-non_counted_backtracking call/23. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W) :- var(A), instantiation_error(call/23). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W) :- - '$strip_module'(A,X,Y), - '$call_inline'(Y,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W) :- - !, - '$prepare_call_clause'(X,Y,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W), - '$call_with_inference_counting'('$module_call'(Y,X)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W) :- - '$prepare_call_clause'(X,Y,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W), - ( '$call_inline'(X) - ; expand_call_goal(X,Y,Z), - strip_subst_module(Z,Y,A1,B1), - '$call_with_inference_counting'('$module_call'(A1,B1)) - ). + '$strip_module'(A,X,Y), + '$prepare_call_clause'(Z,Y,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W), + expand_call_goal(Z,X,A1), + strip_subst_module(A1,X,B1,C1), + '$call_with_inference_counting'('$module_call'(B1,C1)). :-non_counted_backtracking call/24. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X) :- var(A), instantiation_error(call/24). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X) :- - '$strip_module'(A,Y,Z), - '$call_inline'(Z,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X) :- - !, - '$prepare_call_clause'(Y,Z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X), - '$call_with_inference_counting'('$module_call'(Z,Y)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X) :- - '$prepare_call_clause'(Y,Z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X), - ( '$call_inline'(Y) - ; expand_call_goal(Y,Z,A1), - strip_subst_module(A1,Z,B1,C1), - '$call_with_inference_counting'('$module_call'(B1,C1)) - ). + '$strip_module'(A,Y,Z), + '$prepare_call_clause'(A1,Z,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X), + expand_call_goal(A1,Y,B1), + strip_subst_module(B1,Y,C1,D1), + '$call_with_inference_counting'('$module_call'(C1,D1)). :-non_counted_backtracking call/25. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y) :- var(A), instantiation_error(call/25). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y) :- - '$strip_module'(A,Z,A1), - '$call_inline'(A1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y) :- - !, - '$prepare_call_clause'(Z,A1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y), - '$call_with_inference_counting'('$module_call'(A1,Z)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y) :- - '$prepare_call_clause'(Z,A1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y), - ( '$call_inline'(Z) - ; expand_call_goal(Z,A1,B1), - strip_subst_module(B1,A1,C1,D1), - '$call_with_inference_counting'('$module_call'(C1,D1)) - ). + '$strip_module'(A,Z,A1), + '$prepare_call_clause'(B1,A1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y), + expand_call_goal(B1,Z,C1), + strip_subst_module(C1,Z,D1,E1), + '$call_with_inference_counting'('$module_call'(D1,E1)). :-non_counted_backtracking call/26. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z) :- var(A), instantiation_error(call/26). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z) :- - '$strip_module'(A,A1,B1), - '$call_inline'(B1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z) :- - !, - '$prepare_call_clause'(A1,B1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z), - '$call_with_inference_counting'('$module_call'(B1,A1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z) :- - '$prepare_call_clause'(A1,B1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z), - ( '$call_inline'(A1) - ; expand_call_goal(A1,B1,C1), - strip_subst_module(C1,B1,D1,E1), - '$call_with_inference_counting'('$module_call'(D1,E1)) - ). + '$strip_module'(A,A1,B1), + '$prepare_call_clause'(C1,B1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z), + expand_call_goal(C1,A1,D1), + strip_subst_module(D1,A1,E1,F1), + '$call_with_inference_counting'('$module_call'(E1,F1)). :-non_counted_backtracking call/27. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1) :- var(A), instantiation_error(call/27). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1) :- - '$strip_module'(A,B1,C1), - '$call_inline'(C1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1) :- - !, - '$prepare_call_clause'(B1,C1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1), - '$call_with_inference_counting'('$module_call'(C1,B1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1) :- - '$prepare_call_clause'(B1,C1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1), - ( '$call_inline'(B1) - ; expand_call_goal(B1,C1,D1), - strip_subst_module(D1,C1,E1,F1), - '$call_with_inference_counting'('$module_call'(E1,F1)) - ). + '$strip_module'(A,B1,C1), + '$prepare_call_clause'(D1,C1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1), + expand_call_goal(D1,B1,E1), + strip_subst_module(E1,B1,F1,G1), + '$call_with_inference_counting'('$module_call'(F1,G1)). :-non_counted_backtracking call/28. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1) :- var(A), instantiation_error(call/28). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1) :- - '$strip_module'(A,C1,D1), - '$call_inline'(D1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1) :- - !, - '$prepare_call_clause'(C1,D1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1), - '$call_with_inference_counting'('$module_call'(D1,C1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1) :- - '$prepare_call_clause'(C1,D1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1), - ( '$call_inline'(C1) - ; expand_call_goal(C1,D1,E1), - strip_subst_module(E1,D1,F1,G1), - '$call_with_inference_counting'('$module_call'(F1,G1)) - ). + '$strip_module'(A,C1,D1), + '$prepare_call_clause'(E1,D1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1), + expand_call_goal(E1,C1,F1), + strip_subst_module(F1,C1,G1,H1), + '$call_with_inference_counting'('$module_call'(G1,H1)). :-non_counted_backtracking call/29. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1) :- var(A), instantiation_error(call/29). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1) :- - '$strip_module'(A,D1,E1), - '$call_inline'(E1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1) :- - !, - '$prepare_call_clause'(D1,E1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1), - '$call_with_inference_counting'('$module_call'(E1,D1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1) :- - '$prepare_call_clause'(D1,E1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1), - ( '$call_inline'(D1) - ; expand_call_goal(D1,E1,F1), - strip_subst_module(F1,E1,G1,H1), - '$call_with_inference_counting'('$module_call'(G1,H1)) - ). + '$strip_module'(A,D1,E1), + '$prepare_call_clause'(F1,E1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1), + expand_call_goal(F1,D1,G1), + strip_subst_module(G1,D1,H1,I1), + '$call_with_inference_counting'('$module_call'(H1,I1)). :-non_counted_backtracking call/30. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1) :- var(A), instantiation_error(call/30). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1) :- - '$strip_module'(A,E1,F1), - '$call_inline'(F1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1) :- - !, - '$prepare_call_clause'(E1,F1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1), - '$call_with_inference_counting'('$module_call'(F1,E1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1) :- - '$prepare_call_clause'(E1,F1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1), - ( '$call_inline'(E1) - ; expand_call_goal(E1,F1,G1), - strip_subst_module(G1,F1,H1,I1), - '$call_with_inference_counting'('$module_call'(H1,I1)) - ). + '$strip_module'(A,E1,F1), + '$prepare_call_clause'(G1,F1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1), + expand_call_goal(G1,E1,H1), + strip_subst_module(H1,E1,I1,J1), + '$call_with_inference_counting'('$module_call'(I1,J1)). :-non_counted_backtracking call/31. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1) :- var(A), instantiation_error(call/31). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1) :- - '$strip_module'(A,F1,G1), - '$call_inline'(G1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1) :- - !, - '$prepare_call_clause'(F1,G1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1), - '$call_with_inference_counting'('$module_call'(G1,F1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1) :- - '$prepare_call_clause'(F1,G1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1), - ( '$call_inline'(F1) - ; expand_call_goal(F1,G1,H1), - strip_subst_module(H1,G1,I1,J1), - '$call_with_inference_counting'('$module_call'(I1,J1)) - ). + '$strip_module'(A,F1,G1), + '$prepare_call_clause'(H1,G1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1), + expand_call_goal(H1,F1,I1), + strip_subst_module(I1,F1,J1,K1), + '$call_with_inference_counting'('$module_call'(J1,K1)). :-non_counted_backtracking call/32. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1) :- var(A), instantiation_error(call/32). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1) :- - '$strip_module'(A,G1,H1), - '$call_inline'(H1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1) :- - !, - '$prepare_call_clause'(G1,H1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1), - '$call_with_inference_counting'('$module_call'(H1,G1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1) :- - '$prepare_call_clause'(G1,H1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1), - ( '$call_inline'(G1) - ; expand_call_goal(G1,H1,I1), - strip_subst_module(I1,H1,J1,K1), - '$call_with_inference_counting'('$module_call'(J1,K1)) - ). + '$strip_module'(A,G1,H1), + '$prepare_call_clause'(I1,H1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1), + expand_call_goal(I1,G1,J1), + strip_subst_module(J1,G1,K1,L1), + '$call_with_inference_counting'('$module_call'(K1,L1)). :-non_counted_backtracking call/33. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1) :- var(A), instantiation_error(call/33). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1) :- - '$strip_module'(A,H1,I1), - '$call_inline'(I1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1) :- - !, - '$prepare_call_clause'(H1,I1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1), - '$call_with_inference_counting'('$module_call'(I1,H1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1) :- - '$prepare_call_clause'(H1,I1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1), - ( '$call_inline'(H1) - ; expand_call_goal(H1,I1,J1), - strip_subst_module(J1,I1,K1,L1), - '$call_with_inference_counting'('$module_call'(K1,L1)) - ). + '$strip_module'(A,H1,I1), + '$prepare_call_clause'(J1,I1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1), + expand_call_goal(J1,H1,K1), + strip_subst_module(K1,H1,L1,M1), + '$call_with_inference_counting'('$module_call'(L1,M1)). :-non_counted_backtracking call/34. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1) :- var(A), instantiation_error(call/34). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1) :- - '$strip_module'(A,I1,J1), - '$call_inline'(J1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1) :- - !, - '$prepare_call_clause'(I1,J1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1), - '$call_with_inference_counting'('$module_call'(J1,I1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1) :- - '$prepare_call_clause'(I1,J1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1), - ( '$call_inline'(I1) - ; expand_call_goal(I1,J1,K1), - strip_subst_module(K1,J1,L1,M1), - '$call_with_inference_counting'('$module_call'(L1,M1)) - ). + '$strip_module'(A,I1,J1), + '$prepare_call_clause'(K1,J1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1), + expand_call_goal(K1,I1,L1), + strip_subst_module(L1,I1,M1,N1), + '$call_with_inference_counting'('$module_call'(M1,N1)). :-non_counted_backtracking call/35. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1) :- var(A), instantiation_error(call/35). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1) :- - '$strip_module'(A,J1,K1), - '$call_inline'(K1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1) :- - !, - '$prepare_call_clause'(J1,K1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1), - '$call_with_inference_counting'('$module_call'(K1,J1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1) :- - '$prepare_call_clause'(J1,K1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1), - ( '$call_inline'(J1) - ; expand_call_goal(J1,K1,L1), - strip_subst_module(L1,K1,M1,N1), - '$call_with_inference_counting'('$module_call'(M1,N1)) - ). + '$strip_module'(A,J1,K1), + '$prepare_call_clause'(L1,K1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1), + expand_call_goal(L1,J1,M1), + strip_subst_module(M1,J1,N1,O1), + '$call_with_inference_counting'('$module_call'(N1,O1)). :-non_counted_backtracking call/36. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1) :- var(A), instantiation_error(call/36). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1) :- - '$strip_module'(A,K1,L1), - '$call_inline'(L1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1) :- - !, - '$prepare_call_clause'(K1,L1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1), - '$call_with_inference_counting'('$module_call'(L1,K1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1) :- - '$prepare_call_clause'(K1,L1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1), - ( '$call_inline'(K1) - ; expand_call_goal(K1,L1,M1), - strip_subst_module(M1,L1,N1,O1), - '$call_with_inference_counting'('$module_call'(N1,O1)) - ). + '$strip_module'(A,K1,L1), + '$prepare_call_clause'(M1,L1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1), + expand_call_goal(M1,K1,N1), + strip_subst_module(N1,K1,O1,P1), + '$call_with_inference_counting'('$module_call'(O1,P1)). :-non_counted_backtracking call/37. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1) :- var(A), instantiation_error(call/37). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1) :- - '$strip_module'(A,L1,M1), - '$call_inline'(M1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1) :- - !, - '$prepare_call_clause'(L1,M1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1), - '$call_with_inference_counting'('$module_call'(M1,L1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1) :- - '$prepare_call_clause'(L1,M1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1), - ( '$call_inline'(L1) - ; expand_call_goal(L1,M1,N1), - strip_subst_module(N1,M1,O1,P1), - '$call_with_inference_counting'('$module_call'(O1,P1)) - ). + '$strip_module'(A,L1,M1), + '$prepare_call_clause'(N1,M1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1), + expand_call_goal(N1,L1,O1), + strip_subst_module(O1,L1,P1,Q1), + '$call_with_inference_counting'('$module_call'(P1,Q1)). :-non_counted_backtracking call/38. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1) :- var(A), instantiation_error(call/38). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1) :- - '$strip_module'(A,M1,N1), - '$call_inline'(N1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1) :- - !, - '$prepare_call_clause'(M1,N1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1), - '$call_with_inference_counting'('$module_call'(N1,M1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1) :- - '$prepare_call_clause'(M1,N1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1), - ( '$call_inline'(M1) - ; expand_call_goal(M1,N1,O1), - strip_subst_module(O1,N1,P1,Q1), - '$call_with_inference_counting'('$module_call'(P1,Q1)) - ). + '$strip_module'(A,M1,N1), + '$prepare_call_clause'(O1,N1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1), + expand_call_goal(O1,M1,P1), + strip_subst_module(P1,M1,Q1,R1), + '$call_with_inference_counting'('$module_call'(Q1,R1)). :-non_counted_backtracking call/39. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1) :- var(A), instantiation_error(call/39). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1) :- - '$strip_module'(A,N1,O1), - '$call_inline'(O1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1) :- - !, - '$prepare_call_clause'(N1,O1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1), - '$call_with_inference_counting'('$module_call'(O1,N1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1) :- - '$prepare_call_clause'(N1,O1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1), - ( '$call_inline'(N1) - ; expand_call_goal(N1,O1,P1), - strip_subst_module(P1,O1,Q1,R1), - '$call_with_inference_counting'('$module_call'(Q1,R1)) - ). + '$strip_module'(A,N1,O1), + '$prepare_call_clause'(P1,O1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1), + expand_call_goal(P1,N1,Q1), + strip_subst_module(Q1,N1,R1,S1), + '$call_with_inference_counting'('$module_call'(R1,S1)). :-non_counted_backtracking call/40. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1) :- var(A), instantiation_error(call/40). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1) :- - '$strip_module'(A,O1,P1), - '$call_inline'(P1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1) :- - !, - '$prepare_call_clause'(O1,P1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1), - '$call_with_inference_counting'('$module_call'(P1,O1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1) :- - '$prepare_call_clause'(O1,P1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1), - ( '$call_inline'(O1) - ; expand_call_goal(O1,P1,Q1), - strip_subst_module(Q1,P1,R1,S1), - '$call_with_inference_counting'('$module_call'(R1,S1)) - ). + '$strip_module'(A,O1,P1), + '$prepare_call_clause'(Q1,P1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1), + expand_call_goal(Q1,O1,R1), + strip_subst_module(R1,O1,S1,T1), + '$call_with_inference_counting'('$module_call'(S1,T1)). :-non_counted_backtracking call/41. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1) :- var(A), instantiation_error(call/41). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1) :- - '$strip_module'(A,P1,Q1), - '$call_inline'(Q1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1) :- - !, - '$prepare_call_clause'(P1,Q1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1), - '$call_with_inference_counting'('$module_call'(Q1,P1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1) :- - '$prepare_call_clause'(P1,Q1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1), - ( '$call_inline'(P1) - ; expand_call_goal(P1,Q1,R1), - strip_subst_module(R1,Q1,S1,T1), - '$call_with_inference_counting'('$module_call'(S1,T1)) - ). + '$strip_module'(A,P1,Q1), + '$prepare_call_clause'(R1,Q1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1), + expand_call_goal(R1,P1,S1), + strip_subst_module(S1,P1,T1,U1), + '$call_with_inference_counting'('$module_call'(T1,U1)). :-non_counted_backtracking call/42. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1) :- var(A), instantiation_error(call/42). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1) :- - '$strip_module'(A,Q1,R1), - '$call_inline'(R1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1) :- - !, - '$prepare_call_clause'(Q1,R1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1), - '$call_with_inference_counting'('$module_call'(R1,Q1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1) :- - '$prepare_call_clause'(Q1,R1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1), - ( '$call_inline'(Q1) - ; expand_call_goal(Q1,R1,S1), - strip_subst_module(S1,R1,T1,U1), - '$call_with_inference_counting'('$module_call'(T1,U1)) - ). + '$strip_module'(A,Q1,R1), + '$prepare_call_clause'(S1,R1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1), + expand_call_goal(S1,Q1,T1), + strip_subst_module(T1,Q1,U1,V1), + '$call_with_inference_counting'('$module_call'(U1,V1)). :-non_counted_backtracking call/43. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1) :- var(A), instantiation_error(call/43). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1) :- - '$strip_module'(A,R1,S1), - '$call_inline'(S1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1) :- - !, - '$prepare_call_clause'(R1,S1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1), - '$call_with_inference_counting'('$module_call'(S1,R1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1) :- - '$prepare_call_clause'(R1,S1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1), - ( '$call_inline'(R1) - ; expand_call_goal(R1,S1,T1), - strip_subst_module(T1,S1,U1,V1), - '$call_with_inference_counting'('$module_call'(U1,V1)) - ). + '$strip_module'(A,R1,S1), + '$prepare_call_clause'(T1,S1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1), + expand_call_goal(T1,R1,U1), + strip_subst_module(U1,R1,V1,W1), + '$call_with_inference_counting'('$module_call'(V1,W1)). :-non_counted_backtracking call/44. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1) :- var(A), instantiation_error(call/44). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1) :- - '$strip_module'(A,S1,T1), - '$call_inline'(T1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1) :- - !, - '$prepare_call_clause'(S1,T1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1), - '$call_with_inference_counting'('$module_call'(T1,S1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1) :- - '$prepare_call_clause'(S1,T1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1), - ( '$call_inline'(S1) - ; expand_call_goal(S1,T1,U1), - strip_subst_module(U1,T1,V1,W1), - '$call_with_inference_counting'('$module_call'(V1,W1)) - ). + '$strip_module'(A,S1,T1), + '$prepare_call_clause'(U1,T1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1), + expand_call_goal(U1,S1,V1), + strip_subst_module(V1,S1,W1,X1), + '$call_with_inference_counting'('$module_call'(W1,X1)). :-non_counted_backtracking call/45. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1) :- var(A), instantiation_error(call/45). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1) :- - '$strip_module'(A,T1,U1), - '$call_inline'(U1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1) :- - !, - '$prepare_call_clause'(T1,U1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1), - '$call_with_inference_counting'('$module_call'(U1,T1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1) :- - '$prepare_call_clause'(T1,U1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1), - ( '$call_inline'(T1) - ; expand_call_goal(T1,U1,V1), - strip_subst_module(V1,U1,W1,X1), - '$call_with_inference_counting'('$module_call'(W1,X1)) - ). + '$strip_module'(A,T1,U1), + '$prepare_call_clause'(V1,U1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1), + expand_call_goal(V1,T1,W1), + strip_subst_module(W1,T1,X1,Y1), + '$call_with_inference_counting'('$module_call'(X1,Y1)). :-non_counted_backtracking call/46. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1) :- var(A), instantiation_error(call/46). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1) :- - '$strip_module'(A,U1,V1), - '$call_inline'(V1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1) :- - !, - '$prepare_call_clause'(U1,V1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1), - '$call_with_inference_counting'('$module_call'(V1,U1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1) :- - '$prepare_call_clause'(U1,V1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1), - ( '$call_inline'(U1) - ; expand_call_goal(U1,V1,W1), - strip_subst_module(W1,V1,X1,Y1), - '$call_with_inference_counting'('$module_call'(X1,Y1)) - ). + '$strip_module'(A,U1,V1), + '$prepare_call_clause'(W1,V1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1), + expand_call_goal(W1,U1,X1), + strip_subst_module(X1,U1,Y1,Z1), + '$call_with_inference_counting'('$module_call'(Y1,Z1)). :-non_counted_backtracking call/47. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1) :- var(A), instantiation_error(call/47). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1) :- - '$strip_module'(A,V1,W1), - '$call_inline'(W1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1) :- - !, - '$prepare_call_clause'(V1,W1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1), - '$call_with_inference_counting'('$module_call'(W1,V1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1) :- - '$prepare_call_clause'(V1,W1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1), - ( '$call_inline'(V1) - ; expand_call_goal(V1,W1,X1), - strip_subst_module(X1,W1,Y1,Z1), - '$call_with_inference_counting'('$module_call'(Y1,Z1)) - ). + '$strip_module'(A,V1,W1), + '$prepare_call_clause'(X1,W1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1), + expand_call_goal(X1,V1,Y1), + strip_subst_module(Y1,V1,Z1,A2), + '$call_with_inference_counting'('$module_call'(Z1,A2)). :-non_counted_backtracking call/48. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1) :- var(A), instantiation_error(call/48). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1) :- - '$strip_module'(A,W1,X1), - '$call_inline'(X1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1) :- - !, - '$prepare_call_clause'(W1,X1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1), - '$call_with_inference_counting'('$module_call'(X1,W1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1) :- - '$prepare_call_clause'(W1,X1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1), - ( '$call_inline'(W1) - ; expand_call_goal(W1,X1,Y1), - strip_subst_module(Y1,X1,Z1,A2), - '$call_with_inference_counting'('$module_call'(Z1,A2)) - ). + '$strip_module'(A,W1,X1), + '$prepare_call_clause'(Y1,X1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1), + expand_call_goal(Y1,W1,Z1), + strip_subst_module(Z1,W1,A2,B2), + '$call_with_inference_counting'('$module_call'(A2,B2)). :-non_counted_backtracking call/49. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1) :- var(A), instantiation_error(call/49). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1) :- - '$strip_module'(A,X1,Y1), - '$call_inline'(Y1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1) :- - !, - '$prepare_call_clause'(X1,Y1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1), - '$call_with_inference_counting'('$module_call'(Y1,X1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1) :- - '$prepare_call_clause'(X1,Y1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1), - ( '$call_inline'(X1) - ; expand_call_goal(X1,Y1,Z1), - strip_subst_module(Z1,Y1,A2,B2), - '$call_with_inference_counting'('$module_call'(A2,B2)) - ). + '$strip_module'(A,X1,Y1), + '$prepare_call_clause'(Z1,Y1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1), + expand_call_goal(Z1,X1,A2), + strip_subst_module(A2,X1,B2,C2), + '$call_with_inference_counting'('$module_call'(B2,C2)). :-non_counted_backtracking call/50. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1) :- var(A), instantiation_error(call/50). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1) :- - '$strip_module'(A,Y1,Z1), - '$call_inline'(Z1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1) :- - !, - '$prepare_call_clause'(Y1,Z1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1), - '$call_with_inference_counting'('$module_call'(Z1,Y1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1) :- - '$prepare_call_clause'(Y1,Z1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1), - ( '$call_inline'(Y1) - ; expand_call_goal(Y1,Z1,A2), - strip_subst_module(A2,Z1,B2,C2), - '$call_with_inference_counting'('$module_call'(B2,C2)) - ). + '$strip_module'(A,Y1,Z1), + '$prepare_call_clause'(A2,Z1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1), + expand_call_goal(A2,Y1,B2), + strip_subst_module(B2,Y1,C2,D2), + '$call_with_inference_counting'('$module_call'(C2,D2)). :-non_counted_backtracking call/51. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1) :- var(A), instantiation_error(call/51). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1) :- - '$strip_module'(A,Z1,A2), - '$call_inline'(A2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1) :- - !, - '$prepare_call_clause'(Z1,A2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1), - '$call_with_inference_counting'('$module_call'(A2,Z1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1) :- - '$prepare_call_clause'(Z1,A2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1), - ( '$call_inline'(Z1) - ; expand_call_goal(Z1,A2,B2), - strip_subst_module(B2,A2,C2,D2), - '$call_with_inference_counting'('$module_call'(C2,D2)) - ). + '$strip_module'(A,Z1,A2), + '$prepare_call_clause'(B2,A2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1), + expand_call_goal(B2,Z1,C2), + strip_subst_module(C2,Z1,D2,E2), + '$call_with_inference_counting'('$module_call'(D2,E2)). :-non_counted_backtracking call/52. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1) :- var(A), instantiation_error(call/52). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1) :- - '$strip_module'(A,A2,B2), - '$call_inline'(B2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1) :- - !, - '$prepare_call_clause'(A2,B2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1), - '$call_with_inference_counting'('$module_call'(B2,A2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1) :- - '$prepare_call_clause'(A2,B2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1), - ( '$call_inline'(A2) - ; expand_call_goal(A2,B2,C2), - strip_subst_module(C2,B2,D2,E2), - '$call_with_inference_counting'('$module_call'(D2,E2)) - ). + '$strip_module'(A,A2,B2), + '$prepare_call_clause'(C2,B2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1), + expand_call_goal(C2,A2,D2), + strip_subst_module(D2,A2,E2,F2), + '$call_with_inference_counting'('$module_call'(E2,F2)). :-non_counted_backtracking call/53. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2) :- var(A), instantiation_error(call/53). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2) :- - '$strip_module'(A,B2,C2), - '$call_inline'(C2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2) :- - !, - '$prepare_call_clause'(B2,C2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2), - '$call_with_inference_counting'('$module_call'(C2,B2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2) :- - '$prepare_call_clause'(B2,C2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2), - ( '$call_inline'(B2) - ; expand_call_goal(B2,C2,D2), - strip_subst_module(D2,C2,E2,F2), - '$call_with_inference_counting'('$module_call'(E2,F2)) - ). + '$strip_module'(A,B2,C2), + '$prepare_call_clause'(D2,C2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2), + expand_call_goal(D2,B2,E2), + strip_subst_module(E2,B2,F2,G2), + '$call_with_inference_counting'('$module_call'(F2,G2)). :-non_counted_backtracking call/54. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2) :- var(A), instantiation_error(call/54). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2) :- - '$strip_module'(A,C2,D2), - '$call_inline'(D2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2) :- - !, - '$prepare_call_clause'(C2,D2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2), - '$call_with_inference_counting'('$module_call'(D2,C2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2) :- - '$prepare_call_clause'(C2,D2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2), - ( '$call_inline'(C2) - ; expand_call_goal(C2,D2,E2), - strip_subst_module(E2,D2,F2,G2), - '$call_with_inference_counting'('$module_call'(F2,G2)) - ). + '$strip_module'(A,C2,D2), + '$prepare_call_clause'(E2,D2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2), + expand_call_goal(E2,C2,F2), + strip_subst_module(F2,C2,G2,H2), + '$call_with_inference_counting'('$module_call'(G2,H2)). :-non_counted_backtracking call/55. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2) :- var(A), instantiation_error(call/55). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2) :- - '$strip_module'(A,D2,E2), - '$call_inline'(E2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2) :- - !, - '$prepare_call_clause'(D2,E2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2), - '$call_with_inference_counting'('$module_call'(E2,D2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2) :- - '$prepare_call_clause'(D2,E2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2), - ( '$call_inline'(D2) - ; expand_call_goal(D2,E2,F2), - strip_subst_module(F2,E2,G2,H2), - '$call_with_inference_counting'('$module_call'(G2,H2)) - ). + '$strip_module'(A,D2,E2), + '$prepare_call_clause'(F2,E2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2), + expand_call_goal(F2,D2,G2), + strip_subst_module(G2,D2,H2,I2), + '$call_with_inference_counting'('$module_call'(H2,I2)). :-non_counted_backtracking call/56. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2) :- var(A), instantiation_error(call/56). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2) :- - '$strip_module'(A,E2,F2), - '$call_inline'(F2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2) :- - !, - '$prepare_call_clause'(E2,F2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2), - '$call_with_inference_counting'('$module_call'(F2,E2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2) :- - '$prepare_call_clause'(E2,F2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2), - ( '$call_inline'(E2) - ; expand_call_goal(E2,F2,G2), - strip_subst_module(G2,F2,H2,I2), - '$call_with_inference_counting'('$module_call'(H2,I2)) - ). + '$strip_module'(A,E2,F2), + '$prepare_call_clause'(G2,F2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2), + expand_call_goal(G2,E2,H2), + strip_subst_module(H2,E2,I2,J2), + '$call_with_inference_counting'('$module_call'(I2,J2)). :-non_counted_backtracking call/57. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2) :- var(A), instantiation_error(call/57). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2) :- - '$strip_module'(A,F2,G2), - '$call_inline'(G2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2) :- - !, - '$prepare_call_clause'(F2,G2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2), - '$call_with_inference_counting'('$module_call'(G2,F2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2) :- - '$prepare_call_clause'(F2,G2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2), - ( '$call_inline'(F2) - ; expand_call_goal(F2,G2,H2), - strip_subst_module(H2,G2,I2,J2), - '$call_with_inference_counting'('$module_call'(I2,J2)) - ). + '$strip_module'(A,F2,G2), + '$prepare_call_clause'(H2,G2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2), + expand_call_goal(H2,F2,I2), + strip_subst_module(I2,F2,J2,K2), + '$call_with_inference_counting'('$module_call'(J2,K2)). :-non_counted_backtracking call/58. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2) :- var(A), instantiation_error(call/58). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2) :- - '$strip_module'(A,G2,H2), - '$call_inline'(H2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2) :- - !, - '$prepare_call_clause'(G2,H2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2), - '$call_with_inference_counting'('$module_call'(H2,G2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2) :- - '$prepare_call_clause'(G2,H2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2), - ( '$call_inline'(G2) - ; expand_call_goal(G2,H2,I2), - strip_subst_module(I2,H2,J2,K2), - '$call_with_inference_counting'('$module_call'(J2,K2)) - ). + '$strip_module'(A,G2,H2), + '$prepare_call_clause'(I2,H2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2), + expand_call_goal(I2,G2,J2), + strip_subst_module(J2,G2,K2,L2), + '$call_with_inference_counting'('$module_call'(K2,L2)). :-non_counted_backtracking call/59. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2) :- var(A), instantiation_error(call/59). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2) :- - '$strip_module'(A,H2,I2), - '$call_inline'(I2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2) :- - !, - '$prepare_call_clause'(H2,I2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2), - '$call_with_inference_counting'('$module_call'(I2,H2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2) :- - '$prepare_call_clause'(H2,I2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2), - ( '$call_inline'(H2) - ; expand_call_goal(H2,I2,J2), - strip_subst_module(J2,I2,K2,L2), - '$call_with_inference_counting'('$module_call'(K2,L2)) - ). + '$strip_module'(A,H2,I2), + '$prepare_call_clause'(J2,I2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2), + expand_call_goal(J2,H2,K2), + strip_subst_module(K2,H2,L2,M2), + '$call_with_inference_counting'('$module_call'(L2,M2)). :-non_counted_backtracking call/60. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2) :- var(A), instantiation_error(call/60). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2) :- - '$strip_module'(A,I2,J2), - '$call_inline'(J2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2) :- - !, - '$prepare_call_clause'(I2,J2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2), - '$call_with_inference_counting'('$module_call'(J2,I2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2) :- - '$prepare_call_clause'(I2,J2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2), - ( '$call_inline'(I2) - ; expand_call_goal(I2,J2,K2), - strip_subst_module(K2,J2,L2,M2), - '$call_with_inference_counting'('$module_call'(L2,M2)) - ). + '$strip_module'(A,I2,J2), + '$prepare_call_clause'(K2,J2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2), + expand_call_goal(K2,I2,L2), + strip_subst_module(L2,I2,M2,N2), + '$call_with_inference_counting'('$module_call'(M2,N2)). :-non_counted_backtracking call/61. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2) :- var(A), instantiation_error(call/61). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2) :- - '$strip_module'(A,J2,K2), - '$call_inline'(K2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2) :- - !, - '$prepare_call_clause'(J2,K2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2), - '$call_with_inference_counting'('$module_call'(K2,J2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2) :- - '$prepare_call_clause'(J2,K2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2), - ( '$call_inline'(J2) - ; expand_call_goal(J2,K2,L2), - strip_subst_module(L2,K2,M2,N2), - '$call_with_inference_counting'('$module_call'(M2,N2)) - ). + '$strip_module'(A,J2,K2), + '$prepare_call_clause'(L2,K2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2), + expand_call_goal(L2,J2,M2), + strip_subst_module(M2,J2,N2,O2), + '$call_with_inference_counting'('$module_call'(N2,O2)). :-non_counted_backtracking call/62. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2) :- var(A), instantiation_error(call/62). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2) :- - '$strip_module'(A,K2,L2), - '$call_inline'(L2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2) :- - !, - '$prepare_call_clause'(K2,L2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2), - '$call_with_inference_counting'('$module_call'(L2,K2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2) :- - '$prepare_call_clause'(K2,L2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2), - ( '$call_inline'(K2) - ; expand_call_goal(K2,L2,M2), - strip_subst_module(M2,L2,N2,O2), - '$call_with_inference_counting'('$module_call'(N2,O2)) - ). + '$strip_module'(A,K2,L2), + '$prepare_call_clause'(M2,L2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2), + expand_call_goal(M2,K2,N2), + strip_subst_module(N2,K2,O2,P2), + '$call_with_inference_counting'('$module_call'(O2,P2)). :-non_counted_backtracking call/63. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2) :- var(A), instantiation_error(call/63). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2) :- - '$strip_module'(A,L2,M2), - '$call_inline'(M2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2) :- - !, - '$prepare_call_clause'(L2,M2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2), - '$call_with_inference_counting'('$module_call'(M2,L2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2) :- - '$prepare_call_clause'(L2,M2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2), - ( '$call_inline'(L2) - ; expand_call_goal(L2,M2,N2), - strip_subst_module(N2,M2,O2,P2), - '$call_with_inference_counting'('$module_call'(O2,P2)) - ). + '$strip_module'(A,L2,M2), + '$prepare_call_clause'(N2,M2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2), + expand_call_goal(N2,L2,O2), + strip_subst_module(O2,L2,P2,Q2), + '$call_with_inference_counting'('$module_call'(P2,Q2)). :-non_counted_backtracking call/64. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2) :- var(A), instantiation_error(call/64). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2) :- - '$strip_module'(A,M2,N2), - '$call_inline'(N2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2) :- - !, - '$prepare_call_clause'(M2,N2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2), - '$call_with_inference_counting'('$module_call'(N2,M2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2) :- - '$prepare_call_clause'(M2,N2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2), - ( '$call_inline'(M2) - ; expand_call_goal(M2,N2,O2), - strip_subst_module(O2,N2,P2,Q2), - '$call_with_inference_counting'('$module_call'(P2,Q2)) - ). + '$strip_module'(A,M2,N2), + '$prepare_call_clause'(O2,N2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2), + expand_call_goal(O2,M2,P2), + strip_subst_module(P2,M2,Q2,R2), + '$call_with_inference_counting'('$module_call'(Q2,R2)). :-non_counted_backtracking call/65. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2) :- var(A), instantiation_error(call/65). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2) :- - '$strip_module'(A,N2,O2), - '$call_inline'(O2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2) :- - !, - '$prepare_call_clause'(N2,O2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2), - '$call_with_inference_counting'('$module_call'(O2,N2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2) :- - '$prepare_call_clause'(N2,O2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2), - ( '$call_inline'(N2) - ; expand_call_goal(N2,O2,P2), - strip_subst_module(P2,O2,Q2,R2), - '$call_with_inference_counting'('$module_call'(Q2,R2)) - ). + '$strip_module'(A,N2,O2), + '$prepare_call_clause'(P2,O2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2), + expand_call_goal(P2,N2,Q2), + strip_subst_module(Q2,N2,R2,S2), + '$call_with_inference_counting'('$module_call'(R2,S2)). diff --git a/src/machine/arithmetic_ops.rs b/src/machine/arithmetic_ops.rs index 6e12b8c6..02087865 100644 --- a/src/machine/arithmetic_ops.rs +++ b/src/machine/arithmetic_ops.rs @@ -1,3 +1,6 @@ +use dashu::base::Abs; +use dashu::base::Gcd; +use dashu::integer::IBig; use divrem::*; use crate::arena::*; @@ -8,7 +11,7 @@ use crate::heap_iter::*; use crate::machine::machine_errors::*; use crate::machine::machine_state::*; use crate::parser::ast::*; -use crate::parser::rug::{Integer, Rational}; +use crate::parser::dashu::{Integer, Rational}; use crate::types::*; use crate::fixnum; @@ -159,7 +162,7 @@ pub(crate) fn add(lhs: Number, rhs: Number, arena: &mut Arena) -> Result { - Ok(Number::arena_from(Integer::from(&*n1) + &*n2, arena)) // add_i + Ok(Number::arena_from(&*n1 + &*n2, arena)) // add_i } (Number::Integer(n1), Number::Float(OrderedFloat(n2))) | (Number::Float(OrderedFloat(n2)), Number::Integer(n1)) => { @@ -167,7 +170,7 @@ pub(crate) fn add(lhs: Number, rhs: Number, arena: &mut Arena) -> Result { - Ok(Number::arena_from(Rational::from(&*n1) + &*n2, arena)) + Ok(Number::arena_from(&*n1 + &*n2, arena)) } (Number::Rational(n1), Number::Float(OrderedFloat(n2))) | (Number::Float(OrderedFloat(n2)), Number::Rational(n1)) => { @@ -177,7 +180,7 @@ pub(crate) fn add(lhs: Number, rhs: Number, arena: &mut Arena) -> Result { - Ok(Number::arena_from(Rational::from(&*r1) + &*r2, arena)) + Ok(Number::arena_from(&*r1 + &*r2, arena)) } } } @@ -191,9 +194,15 @@ pub(crate) fn neg(n: Number, arena: &mut Arena) -> Number { Number::arena_from(-Integer::from(n.get_num()), arena) } } - Number::Integer(n) => Number::arena_from(-Integer::from(&*n), arena), + Number::Integer(n) => { + let n_clone: Integer = (*n).clone(); + Number::arena_from(-Integer::from(n_clone), arena) + }, Number::Float(OrderedFloat(f)) => Number::Float(OrderedFloat(-f)), - Number::Rational(r) => Number::arena_from(-Rational::from(&*r), arena), + Number::Rational(r) => { + let r_clone: Rational = (*r).clone(); + Number::arena_from(-Rational::from(r_clone), arena) + }, } } @@ -203,12 +212,19 @@ pub(crate) fn abs(n: Number, arena: &mut Arena) -> Number { if let Some(n) = n.get_num().checked_abs() { fixnum!(Number, n, arena) } else { - Number::arena_from(Integer::from(n.get_num()).abs(), arena) + let arena_int = Integer::from(n.get_num()); + Number::arena_from(arena_int.abs(), arena) } } - Number::Integer(n) => Number::arena_from(Integer::from(n.abs_ref()), arena), + Number::Integer(n) => { + let n_clone: Integer = (*n).clone(); + Number::arena_from(Integer::from(n_clone.abs()), arena) + }, Number::Float(f) => Number::Float(f.abs()), - Number::Rational(r) => Number::arena_from(Rational::from(r.abs_ref()), arena), + Number::Rational(r) => { + let r_clone: Rational = (*r).clone(); + Number::arena_from(Rational::from(r_clone.abs()), arena) + }, } } @@ -247,7 +263,8 @@ pub(crate) fn mul(lhs: Number, rhs: Number, arena: &mut Arena) -> Result { - Ok(Number::arena_from(Integer::from(&*n1) * &*n2, arena)) // mul_i + let n1_clone: Integer = (*n1).clone(); + Ok(Number::arena_from(Integer::from(n1_clone) * &*n2, arena)) // mul_i } (Number::Integer(n1), Number::Float(OrderedFloat(n2))) | (Number::Float(OrderedFloat(n2)), Number::Integer(n1)) => { @@ -255,7 +272,8 @@ pub(crate) fn mul(lhs: Number, rhs: Number, arena: &mut Arena) -> Result { - Ok(Number::arena_from(Rational::from(&*n1) * &*n2, arena)) + let n1_clone: Integer = (*n1).clone(); + Ok(Number::arena_from(Rational::from(n1_clone) * &*n2, arena)) } (Number::Rational(n1), Number::Float(OrderedFloat(n2))) | (Number::Float(OrderedFloat(n2)), Number::Rational(n1)) => { @@ -265,7 +283,8 @@ pub(crate) fn mul(lhs: Number, rhs: Number, arena: &mut Arena) -> Result { - Ok(Number::arena_from(Rational::from(&*r1) * &*r2, arena)) + let r1_clone: Rational = (*r1).clone(); + Ok(Number::arena_from(Rational::from(r1_clone) * &*r2, arena)) } } } @@ -338,7 +357,7 @@ pub(crate) fn int_pow(n1: Number, n2: Number, arena: &mut Arena) -> Result { let n1_i = n1.get_num(); - if !(n1_i == 1 || n1_i == 0 || n1_i == -1) && &*n2 < &0 { + if !(n1_i == 1 || n1_i == 0 || n1_i == -1) && &*n2 < &Integer::from(0) { let n = Number::Fixnum(n1); Err(numerical_type_error(ValidType::Float, n, stub_gen)) } else { @@ -349,7 +368,7 @@ pub(crate) fn int_pow(n1: Number, n2: Number, arena: &mut Arena) -> Result { let n2_i = n2.get_num(); - if !(&*n1 == &1 || &*n1 == &0 || &*n1 == &-1) && n2_i < 0 { + if !(&*n1 == &Integer::from(1) || &*n1 == &Integer::from(0) || &*n1 == &Integer::from(-1)) && n2_i < 0 { let n = Number::Integer(n1); Err(numerical_type_error(ValidType::Float, n, stub_gen)) } else { @@ -358,7 +377,7 @@ pub(crate) fn int_pow(n1: Number, n2: Number, arena: &mut Arena) -> Result { - if !(&*n1 == &1 || &*n1 == &0 || &*n1 == &-1) && &*n2 < &0 { + if !(&*n1 == &Integer::from(1) || &*n1 == &Integer::from(0) || &*n1 == &Integer::from(-1)) && &*n2 < &Integer::from(0) { let n = Number::Integer(n1); Err(numerical_type_error(ValidType::Float, n, stub_gen)) } else { @@ -521,7 +540,7 @@ pub fn rational_from_number( match n { Number::Fixnum(n) => Ok(arena_alloc!(Rational::from(n.get_num()), arena)), Number::Rational(r) => Ok(r), - Number::Float(OrderedFloat(f)) => match Rational::from_f64(f) { + Number::Float(OrderedFloat(f)) => match Rational::simplest_from_f64(f) { Some(r) => Ok(arena_alloc!(r, arena)), None => Err(Box::new(move |machine_st| { let instantiation_error = machine_st.instantiation_error(); @@ -530,7 +549,10 @@ pub fn rational_from_number( machine_st.error_form(instantiation_error, stub) })), }, - Number::Integer(n) => Ok(arena_alloc!(Rational::from(&*n), arena)), + Number::Integer(n) => { + let n_clone: Integer = (*n).clone(); + Ok(arena_alloc!(Rational::from(n_clone), arena)) + }, } } @@ -590,7 +612,7 @@ pub(crate) fn idiv(n1: Number, n2: Number, arena: &mut Arena) -> Result::from(n1.div_rem_ref(&*n2)).0, + <(Integer, Integer)>::from(n1.div_rem_floor_ref(&*n2)).0, arena, )) } @@ -624,6 +646,10 @@ pub(crate) fn shr(n1: Number, n2: Number, arena: &mut Arena) -> Result { let n1_i = n1.get_num(); @@ -631,33 +657,33 @@ pub(crate) fn shr(n1: Number, n2: Number, arena: &mut Arena) -> Result> n2, arena)); - } else { - return Ok(Number::arena_from(n1 >> u32::max_value(), arena)); + } else { + return Ok(Number::arena_from(n1 >> usize::max_value(), arena)); } } (Number::Fixnum(n1), Number::Integer(n2)) => { let n1 = Integer::from(n1.get_num()); - match n2.to_u32() { + match n2.to_usize() { Some(n2) => Ok(Number::arena_from(n1 >> n2, arena)), - _ => Ok(Number::arena_from(n1 >> u32::max_value(), arena)), + _ => { + Ok(Number::arena_from(n1 >> usize::max_value(), arena)) + }, } } - (Number::Integer(n1), Number::Fixnum(n2)) => match u32::try_from(n2.get_num()) { + (Number::Integer(n1), Number::Fixnum(n2)) => match usize::try_from(n2.get_num()) { Ok(n2) => Ok(Number::arena_from(Integer::from(&*n1 >> n2), arena)), - _ => Ok(Number::arena_from( - Integer::from(&*n1 >> u32::max_value()), - arena, - )), + _ => { + Ok(Number::arena_from(Integer::from(&*n1 >> usize::max_value()),arena)) + }, }, - (Number::Integer(n1), Number::Integer(n2)) => match n2.to_u32() { + (Number::Integer(n1), Number::Integer(n2)) => match n2.to_usize() { Some(n2) => Ok(Number::arena_from(Integer::from(&*n1 >> n2), arena)), - _ => Ok(Number::arena_from( - Integer::from(&*n1 >> u32::max_value()), - arena, - )), + _ => { + Ok(Number::arena_from(Integer::from(&*n1 >> usize::max_value()), arena)) + }, }, (Number::Integer(_), n2) => Err(numerical_type_error(ValidType::Integer, n2, stub_gen)), (Number::Fixnum(_), n2) => Err(numerical_type_error(ValidType::Integer, n2, stub_gen)), @@ -667,10 +693,14 @@ pub(crate) fn shr(n1: Number, n2: Number, arena: &mut Arena) -> Result Result { let stub_gen = || { - let shl_atom = atom!(">>"); + let shl_atom = atom!("<<"); functor_stub(shl_atom, 2) }; + if n2.is_integer() && n2.is_negative() { + return shr(n1, neg(n2, arena), arena); + } + match (n1, n2) { (Number::Fixnum(n1), Number::Fixnum(n2)) => { let n1_i = n1.get_num(); @@ -678,33 +708,33 @@ pub(crate) fn shl(n1: Number, n2: Number, arena: &mut Arena) -> Result { let n1 = Integer::from(n1.get_num()); match n2.to_u32() { - Some(n2) => Ok(Number::arena_from(n1 << n2, arena)), - _ => Ok(Number::arena_from(n1 << u32::max_value(), arena)), + Some(n2) => Ok(Number::arena_from(n1.to_u64().unwrap() << n2, arena)), + _ => { + Ok(Number::arena_from(n1 << usize::max_value(), arena)) + } } } - (Number::Integer(n1), Number::Fixnum(n2)) => match u32::try_from(n2.get_num()) { + (Number::Integer(n1), Number::Fixnum(n2)) => match usize::try_from(n2.get_num()) { Ok(n2) => Ok(Number::arena_from(Integer::from(&*n1 << n2), arena)), - _ => Ok(Number::arena_from( - Integer::from(&*n1 << u32::max_value()), - arena, - )), + _ => { + Ok(Number::arena_from(Integer::from(&*n1 << usize::max_value()),arena)) + } }, (Number::Integer(n1), Number::Integer(n2)) => match n2.to_u32() { - Some(n2) => Ok(Number::arena_from(Integer::from(&*n1 << n2), arena)), - _ => Ok(Number::arena_from( - Integer::from(&*n1 << u32::max_value()), - arena, - )), + Some(n2) => Ok(Number::arena_from(Integer::from(n1.to_u64().unwrap() << n2), arena)), + _ => { + Ok(Number::arena_from(Integer::from(&*n1 << usize::max_value()),arena)) + } }, (Number::Integer(_), n2) => Err(numerical_type_error(ValidType::Integer, n2, stub_gen)), (Number::Fixnum(_), n2) => Err(numerical_type_error(ValidType::Integer, n2, stub_gen)), @@ -918,18 +948,21 @@ pub(crate) fn gcd(n1: Number, n2: Number, arena: &mut Arena) -> Result { let n1 = Integer::from(n1.get_num()); - Ok(Number::arena_from(Integer::from(n2.gcd_ref(&n1)), arena)) + let n2_clone: Integer = (*n2).clone(); + Ok(Number::arena_from(Integer::from(n2_clone.gcd(&n1)), arena)) } (Number::Integer(n1), Number::Integer(n2)) => { - Ok(Number::arena_from(Integer::from(n1.gcd_ref(&n2)), arena)) + let n1_clone: Integer = (*n1).clone(); + Ok(Number::arena_from(Integer::from(n1_clone.gcd(&Integer::from(n2.to_isize().unwrap()))) as IBig, arena)) } (Number::Float(f), _) | (_, Number::Float(f)) => { let n = Number::Float(f); @@ -998,6 +1031,63 @@ pub(crate) fn atan(n1: Number) -> Result { unary_float_fn_template(n1, |f| f.atan()) } +#[inline] +pub(crate) fn asinh(n1: Number) -> Result { + unary_float_fn_template(n1, |f| f.asinh()) +} + +#[inline] +pub(crate) fn acosh(n1: Number) -> Result { + unary_float_fn_template(n1, |f| f.acosh()) +} + +#[inline] +pub(crate) fn atanh(n1: Number) -> Result { + let stub_gen = || { + let is_atom = atom!("is"); + functor_stub(is_atom, 2) + }; + + let f1 = try_numeric_result!(result_f(&n1), stub_gen)?; + + try_numeric_result!(if f1 == 1.0 || f1 == -1.0 { + Err(EvalError::Undefined) + } else { + result_f(&Number::Float(OrderedFloat(f1.atanh()))) + }, + stub_gen) +} + +#[inline] +pub(crate) fn sinh(n1: Number) -> Result { + unary_float_fn_template(n1, |f| f.sinh()) +} + +#[inline] +pub(crate) fn cosh(n1: Number) -> Result { + unary_float_fn_template(n1, |f| f.cosh()) +} + +#[inline] +pub(crate) fn tanh(n1: Number) -> Result { + unary_float_fn_template(n1, |f| f.tanh()) +} + +#[inline] +pub(crate) fn log10(n1: Number) -> Result { + unary_float_fn_template(n1, |f| f.log(10f64)) +} + +#[inline] +pub(crate) fn float_fractional_part(n1: Number) -> Result { + unary_float_fn_template(n1, |f| f.fract()) +} + +#[inline] +pub(crate) fn float_integer_part(n1: Number) -> Result { + unary_float_fn_template(n1, |f| f.trunc()) +} + #[inline] pub(crate) fn sqrt(n1: Number) -> Result { if n1.is_negative() { @@ -1017,6 +1107,7 @@ pub(crate) fn floor(n1: Number, arena: &mut Arena) -> Number { rnd_i(&n1, arena) } + #[inline] pub(crate) fn ceiling(n1: Number, arena: &mut Arena) -> Number { let n1 = neg(n1, arena); @@ -1098,7 +1189,7 @@ impl MachineState { pub(crate) fn arith_eval_by_metacall(&mut self, value: HeapCellValue) -> Result { let stub_gen = || functor_stub(atom!("is"), 2); - let mut iter = stackful_post_order_iter(&mut self.heap, value); + let mut iter = stackful_post_order_iter(&mut self.heap, &mut self.stack, value); while let Some(value) = iter.next() { if value.get_forwarding_bit() { @@ -1115,7 +1206,7 @@ impl MachineState { HeapCellValueTag::PStrLoc) => { (atom!("."), 2) } - (HeapCellValueTag::AttrVar | HeapCellValueTag::Var) => { + (HeapCellValueTag::AttrVar | HeapCellValueTag::Var | HeapCellValueTag::StackVar) => { let err = self.instantiation_error(); return Err(self.error_form(err, stub_gen())); } @@ -1247,6 +1338,33 @@ impl MachineState { atom!("tan") => self.interms.push(Number::Float(OrderedFloat( drop_iter_on_err!(self, iter, tan(a1)) ))), + atom!("cosh") => self.interms.push(Number::Float(OrderedFloat( + drop_iter_on_err!(self, iter, cosh(a1)) + ))), + atom!("sinh") => self.interms.push(Number::Float(OrderedFloat( + drop_iter_on_err!(self, iter, sinh(a1)) + ))), + atom!("tanh") => self.interms.push(Number::Float(OrderedFloat( + drop_iter_on_err!(self, iter, tanh(a1)) + ))), + atom!("acosh") => self.interms.push(Number::Float(OrderedFloat( + drop_iter_on_err!(self, iter, acosh(a1)) + ))), + atom!("asinh") => self.interms.push(Number::Float(OrderedFloat( + drop_iter_on_err!(self, iter, asinh(a1)) + ))), + atom!("atanh") => self.interms.push(Number::Float(OrderedFloat( + drop_iter_on_err!(self, iter, atanh(a1)) + ))), + atom!("log10") => self.interms.push(Number::Float(OrderedFloat( + drop_iter_on_err!(self, iter, log10(a1)) + ))), + atom!("float_fractional_part") => self.interms.push(Number::Float(OrderedFloat( + drop_iter_on_err!(self, iter, float_fractional_part(a1)) + ))), + atom!("float_integer_part") => self.interms.push(Number::Float(OrderedFloat( + drop_iter_on_err!(self, iter, float_integer_part(a1)) + ))), atom!("sqrt") => self.interms.push(Number::Float(OrderedFloat( drop_iter_on_err!(self, iter, sqrt(a1)) ))), diff --git a/src/machine/attributed_variables.pl b/src/machine/attributed_variables.pl index 8c656e31..c288511b 100644 --- a/src/machine/attributed_variables.pl +++ b/src/machine/attributed_variables.pl @@ -1,11 +1,9 @@ :- module('$atts', []). - driver(Vars, Values) :- iterate(Vars, Values, ListOfListsOfGoalLists), !, call_goals(ListOfListsOfGoalLists), - '$reset_attr_var_state', '$return_from_verify_attr'. iterate([Var|VarBindings], [Value|ValueBindings], [ListOfGoalLists | ListsCubed]) :- diff --git a/src/machine/attributed_variables.rs b/src/machine/attributed_variables.rs index f74d3981..633378a0 100644 --- a/src/machine/attributed_variables.rs +++ b/src/machine/attributed_variables.rs @@ -33,8 +33,8 @@ impl AttrVarInitializer { } #[inline] - pub(super) fn reset(&mut self) { - self.attr_var_queue.clear(); + pub(super) fn reset(&mut self, len: usize) { + self.attr_var_queue.truncate(len); self.bindings.clear(); } } @@ -52,6 +52,7 @@ impl MachineState { self.cp = INSTALL_VERIFY_ATTR_INTERRUPT; } + debug_assert_eq!(self.heap[h].get_tag(), HeapCellValueTag::AttrVar); self.attr_var_init.bindings.push((h, addr)); } @@ -63,10 +64,9 @@ impl MachineState { .map(|(ref h, _)| attr_var_as_cell!(*h)); let var_list_addr = heap_loc_as_cell!(iter_to_heap_list(&mut self.heap, iter)); - let iter = self.attr_var_init.bindings.drain(0..).map(|(_, ref v)| *v); - let value_list_addr = heap_loc_as_cell!(iter_to_heap_list(&mut self.heap, iter)); + (var_list_addr, value_list_addr) } @@ -136,7 +136,7 @@ impl MachineState { let mut seen_set = IndexSet::new(); let mut seen_vars = vec![]; - let mut iter = stackful_preorder_iter(&mut self.heap, cell); + let mut iter = stackful_preorder_iter(&mut self.heap, &mut self.stack, cell); while let Some(value) = iter.next() { read_heap_cell!(value, @@ -147,6 +147,16 @@ impl MachineState { let value = unmark_cell_bits!(value); + if h != iter.focus().value() as usize { + let deref_value = heap_bound_store(iter.heap, heap_bound_deref(iter.heap, value)); + + if deref_value.is_compound(iter.heap) { + // a cyclic structure is bound to the attributed variable at h. + // it mustn't be included in seen_vars. + continue; + } + } + seen_vars.push(value); seen_set.insert(h); @@ -157,7 +167,7 @@ impl MachineState { loop { read_heap_cell!(iter.heap[l], (HeapCellValueTag::Lis) => { - iter.push_stack(l); + iter.push_stack(IterStackLoc::iterable_loc(l, HeapOrStackTag::Heap)); // l = elem + 1; break; } diff --git a/src/machine/code_walker.rs b/src/machine/code_walker.rs index fcda8710..c2032727 100644 --- a/src/machine/code_walker.rs +++ b/src/machine/code_walker.rs @@ -1,5 +1,6 @@ use crate::instructions::*; +use fxhash::FxBuildHasher; use indexmap::IndexSet; fn capture_offset(line: &Instruction, index: usize, stack: &mut Vec) -> bool { @@ -7,38 +8,24 @@ fn capture_offset(line: &Instruction, index: usize, stack: &mut Vec) -> b &Instruction::TryMeElse(offset) if offset > 0 => { stack.push(index + offset); } - &Instruction::DefaultRetryMeElse(offset) | - &Instruction::RetryMeElse(offset) - if offset > 0 => - { + &Instruction::DefaultRetryMeElse(offset) | &Instruction::RetryMeElse(offset) if offset > 0 => { stack.push(index + offset); } - &Instruction::DynamicElse(_, _, NextOrFail::Next(offset)) - if offset > 0 => - { + &Instruction::DynamicElse(_, _, NextOrFail::Next(offset)) if offset > 0 => { stack.push(index + offset); } - &Instruction::DynamicInternalElse(_, _, NextOrFail::Next(offset)) - if offset > 0 => - { + &Instruction::DynamicInternalElse(_, _, NextOrFail::Next(offset)) if offset > 0 => { stack.push(index + offset); } - &Instruction::JmpByCall(_, offset, _) => { - stack.push(index + offset); - } - &Instruction::JmpByExecute(_, offset, _) => { - stack.push(index + offset); - return true; - } - &Instruction::Proceed => { + &Instruction::Proceed | &Instruction::JmpByCall(_) => { return true; } &Instruction::RevJmpBy(offset) => { if offset > 0 { stack.push(index - offset); - } else { - return true; } + + return true; } instr if instr.is_execute() => { return true; @@ -55,7 +42,7 @@ fn capture_offset(line: &Instruction, index: usize, stack: &mut Vec) -> b */ pub(crate) fn walk_code(code: &Code, p: usize, mut walker: impl FnMut(&Instruction)) { let mut stack = vec![p]; - let mut visited_indices = IndexSet::new(); + let mut visited_indices = IndexSet::with_hasher(FxBuildHasher::default()); while let Some(first_index) = stack.pop() { if visited_indices.contains(&first_index) { @@ -73,23 +60,3 @@ pub(crate) fn walk_code(code: &Code, p: usize, mut walker: impl FnMut(&Instructi } } } - -/* A function for code walking that might result in modification to - * the code. Otherwise identical to walk_code. - */ -/* -pub(crate) fn walk_code_mut(code: &mut Code, p: usize, mut walker: impl FnMut(&mut Line)) -{ - let mut queue = VecDeque::from(vec![p]); - - while let Some(first_idx) = queue.pop_front() { - let mut last_idx = first_idx; - - capture_next_range(code, &mut queue, &mut last_idx); - - for instr in &mut code[first_idx .. last_idx + 1] { - walker(instr); - } - } -} -*/ diff --git a/src/machine/compile.rs b/src/machine/compile.rs index d7f2492c..0faf34c3 100644 --- a/src/machine/compile.rs +++ b/src/machine/compile.rs @@ -44,60 +44,6 @@ pub(super) fn bootstrapping_compile( Ok(()) } -// throw errors if declaration or query found. -pub(super) fn compile_relation( - cg: &mut CodeGenerator, - tl: &TopLevel, -) -> Result { - match tl { - &TopLevel::Query(_) => Err(CompilationError::ExpectedRel), - &TopLevel::Predicate(ref clauses) => cg.compile_predicate(&clauses), - &TopLevel::Fact(ref fact, ..) => cg.compile_fact(fact), - &TopLevel::Rule(ref rule, ..) => cg.compile_rule(rule), - } -} - -pub(super) fn compile_appendix( - code: &mut Code, - mut queue: VecDeque, - jmp_by_locs: Vec, - non_counted_bt: bool, - atom_tbl: &mut AtomTable, -) -> Result<(), CompilationError> { - let mut jmp_by_locs = VecDeque::from(jmp_by_locs); - - while let Some(jmp_by_offset) = jmp_by_locs.pop_front() { - let code_len = code.len(); - - match &mut code[jmp_by_offset] { - &mut Instruction::JmpByCall(_, ref mut offset, ..) | - &mut Instruction::JmpByExecute(_, ref mut offset, ..) => { - *offset = code_len - jmp_by_offset; - } - _ => { - unreachable!() - } - } - - // false because the inner predicate is a one-off, hence not extensible. - let settings = CodeGenSettings { - global_clock_tick: None, - is_extensible: false, - non_counted_bt, - }; - - let mut cg = CodeGenerator::new(atom_tbl, settings); - - let tl = queue.pop_front().unwrap(); - let decl_code = compile_relation(&mut cg, &tl)?; - - jmp_by_locs.extend(cg.jmp_by_locs.into_iter().map(|offset| offset + code.len())); - code.extend(decl_code.into_iter()); - } - - Ok(()) -} - fn lower_bound_of_target_clause(skeleton: &PredicateSkeleton, target_pos: usize) -> usize { if target_pos == 0 { return 0; @@ -1342,22 +1288,14 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { let mut preprocessor = Preprocessor::new(settings); let clause = self.try_term_to_tl(term, &mut preprocessor)?; - let queue = preprocessor.parse_queue(self)?; + // let queue = preprocessor.parse_queue(self)?; let mut cg = CodeGenerator::new( &mut LS::machine_st(&mut self.payload).atom_tbl, settings, ); - let mut clause_code = cg.compile_predicate(&vec![clause])?; - - compile_appendix( - &mut clause_code, - queue, - cg.jmp_by_locs, - settings.non_counted_bt, - cg.atom_tbl, - )?; + let clause_code = cg.compile_predicate(vec![clause])?; Ok(StandaloneCompileResult { clause_code, @@ -1385,22 +1323,12 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { clauses.push(self.try_term_to_tl(term, &mut preprocessor)?); } - let queue = preprocessor.parse_queue(self)?; - let mut cg = CodeGenerator::new( &mut LS::machine_st(&mut self.payload).atom_tbl, settings, ); - let mut code = cg.compile_predicate(&clauses)?; - - compile_appendix( - &mut code, - queue, - cg.jmp_by_locs, - settings.non_counted_bt, - cg.atom_tbl, - )?; + let mut code = cg.compile_predicate(clauses)?; if settings.is_extensible { let mut clause_clause_locs = VecDeque::new(); @@ -1869,7 +1797,13 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { skeleton.clauses[target_pos + 1].clause_start = skeleton.clauses[target_pos].clause_start; - let index_ptr_opt = if target_pos == 0 { + let update_code_index = target_pos == 0 && + skeleton.clauses[target_pos + 1] + .opt_arg_index_key + .switch_on_term_loc() + .is_none(); + + let index_ptr_opt = if update_code_index { Some(IndexPtr::index(clause_loc)) } else { None @@ -2274,14 +2208,17 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { .ok_or(SessionError::NamelessEntry)?; let listing_src_file_name = self.listing_src_file_name(); - let payload_compilation_target = self.payload.compilation_target; - let mut predicate_info = self - .wam_prelude - .indices - .get_predicate_skeleton(&self.payload.predicates.compilation_target, &key) - .map(|skeleton| skeleton.predicate_info()) - .unwrap_or_default(); + // payload_compilation_target describes the compilation context, + // e.g. compiling + // + // table_wrapper:tabled(get_node(A), b). + // + // without a module declaration means self.payload.compilation_target + // is CompilationTarget::User while self.payload.predicates.compilation_target + // is CompilationTarget::Module(atom!("table_wrapper")). + + let payload_compilation_target = self.payload.compilation_target; let local_predicate_info = self .wam_prelude @@ -2295,34 +2232,37 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { .map(|skeleton| skeleton.predicate_info()) .unwrap_or_default(); - if local_predicate_info.must_retract_local_clauses() { + let mut predicate_info = self + .wam_prelude + .indices + .get_predicate_skeleton(&self.payload.predicates.compilation_target, &key) + .map(|skeleton| skeleton.predicate_info()) + .unwrap_or_default(); + + let is_cross_module_clause = + payload_compilation_target != self.payload.predicates.compilation_target; + + if local_predicate_info.must_retract_local_clauses(is_cross_module_clause) { self.retract_local_clauses(&key, predicate_info.is_dynamic); } - let do_incremental_compile = - if payload_compilation_target == self.payload.predicates.compilation_target { - predicate_info.compile_incrementally() - } else { - local_predicate_info.is_multifile && predicate_info.compile_incrementally() - }; - let predicates_len = self.payload.predicates.len(); let non_counted_bt = self.payload.non_counted_bt_preds.contains(&key); - if do_incremental_compile { + if predicate_info.compile_incrementally() { let predicates = self.payload.predicates.take(); for term in predicates.predicates { self.incremental_compile_clause( key, term, - payload_compilation_target, + self.payload.predicates.compilation_target, non_counted_bt, AppendOrPrepend::Append, )?; } } else { - if payload_compilation_target != self.payload.predicates.compilation_target { + if is_cross_module_clause { if !local_predicate_info.is_extensible { if predicate_info.is_multifile { println!( @@ -2337,9 +2277,11 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { .indices .remove_predicate_skeleton(&self.payload.predicates.compilation_target, &key) { + let compilation_target = self.payload.predicates.compilation_target; + if predicate_info.is_dynamic { let clause_clause_compilation_target = - match self.payload.predicates.compilation_target { + match compilation_target { CompilationTarget::User => { CompilationTarget::Module(atom!("builtins")) } @@ -2358,7 +2300,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { self.payload.retraction_info.push_record( RetractionRecord::RemovedSkeleton( - payload_compilation_target, + compilation_target, key, skeleton, ), @@ -2409,9 +2351,11 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { .clause_clauses.drain(0..std::cmp::min(predicates_len, clause_clauses_len)) .collect(); + let compilation_target = self.payload.predicates.compilation_target; + self.compile_clause_clauses( key, - payload_compilation_target, + compilation_target, clauses_vec.into_iter(), AppendOrPrepend::Append, )?; diff --git a/src/machine/copier.rs b/src/machine/copier.rs index 53325332..0d091468 100644 --- a/src/machine/copier.rs +++ b/src/machine/copier.rs @@ -28,7 +28,10 @@ pub(crate) fn copy_term( attr_var_policy: AttrVarPolicy, ) { let mut copy_term_state = CopyTermState::new(target, attr_var_policy); + copy_term_state.copy_term_impl(addr); + copy_term_state.copy_attr_var_lists(); + copy_term_state.unwind_trail(); } #[derive(Debug)] @@ -38,6 +41,7 @@ struct CopyTermState { old_h: usize, target: T, attr_var_policy: AttrVarPolicy, + attr_var_list_locs: Vec<(usize, HeapCellValue)>, } impl CopyTermState { @@ -48,6 +52,7 @@ impl CopyTermState { old_h: target.threshold(), target, attr_var_policy, + attr_var_list_locs: vec![], } } @@ -86,16 +91,12 @@ impl CopyTermState { self.target.push(hcv); } - let cdr = self - .target - .store(self.target.deref(heap_loc_as_cell!(addr + 1))); + let cdr = self.target.store(self.target.deref(heap_loc_as_cell!(addr + 1))); if !cdr.is_var() { self.trail_list_cell(addr + 1, threshold); } else { - let car = self - .target - .store(self.target.deref(heap_loc_as_cell!(addr))); + let car = self.target.store(self.target.deref(heap_loc_as_cell!(addr))); if !car.is_var() { self.trail_list_cell(addr, threshold); @@ -167,6 +168,51 @@ impl CopyTermState { self.trail.push((Ref::heap_cell(pstr_loc), trail_item)); } + fn copy_attr_var_lists(&mut self) { + while !self.attr_var_list_locs.is_empty() { + let iter = mem::replace(&mut self.attr_var_list_locs, vec![]); + + for (threshold, list_loc) in iter { + self.target[threshold] = list_loc_as_cell!(self.target.threshold()); + self.copy_attr_var_list(list_loc); + } + } + } + + /* + * Attributed variable attribute lists adhere to a particular + * structure which is ensured by this function and not at all by + * the vanilla copier. + */ + fn copy_attr_var_list(&mut self, mut list_addr: HeapCellValue) { + while let HeapCellValueTag::Lis = list_addr.get_tag() { + let threshold = self.target.threshold(); + let heap_loc = list_addr.get_value(); + let str_loc = self.target[heap_loc].get_value(); + + self.target.push(heap_loc_as_cell!(threshold+2)); + self.target.push(heap_loc_as_cell!(threshold+1)); + + read_heap_cell!(self.target[str_loc], + (HeapCellValueTag::Atom) => { + self.target.push(self.target[str_loc]); + } + (HeapCellValueTag::Str) => { + self.copy_term_impl(self.target[str_loc]); + } + _ => { + unreachable!(); + } + ); + + list_addr = self.target[heap_loc + 1]; + + if HeapCellValueTag::Lis == list_addr.get_tag() { + self.target[threshold + 1] = list_loc_as_cell!(self.target.threshold()); + } + } + } + fn reinstantiate_var(&mut self, addr: HeapCellValue, frontier: usize) { read_heap_cell!(addr, (HeapCellValueTag::Var, h) => { @@ -195,9 +241,15 @@ impl CopyTermState { if let AttrVarPolicy::DeepCopy = self.attr_var_policy { self.target.push(attr_var_as_cell!(threshold)); + self.target.push(heap_loc_as_cell!(threshold + 1)); - let list_val = self.target[h + 1]; - self.target.push(list_val); + let old_list_link = self.target[h + 1]; + self.trail.push((Ref::heap_cell(h + 1), old_list_link)); + self.target[h + 1] = heap_loc_as_cell!(threshold + 1); + + if old_list_link.get_tag() == HeapCellValueTag::Lis { + self.attr_var_list_locs.push((threshold + 1, old_list_link)); + } } } _ => { @@ -298,8 +350,6 @@ impl CopyTermState { } ); } - - self.unwind_trail(); } fn unwind_trail(&mut self) { diff --git a/src/machine/disjuncts.rs b/src/machine/disjuncts.rs new file mode 100644 index 00000000..6c6d3a7b --- /dev/null +++ b/src/machine/disjuncts.rs @@ -0,0 +1,837 @@ +use crate::atom_table::*; +use crate::forms::*; +use crate::instructions::*; +use crate::iterators::*; +use crate::machine::loader::*; +use crate::machine::machine_errors::CompilationError; +use crate::machine::preprocessor::*; +use crate::parser::ast::*; +use crate::parser::dashu::Rational; +use crate::variable_records::*; + +use dashu::Integer; +use indexmap::{IndexMap, IndexSet}; + +use std::cell::Cell; +use std::cmp::Ordering; +use std::collections::VecDeque; +use std::hash::{Hash, Hasher}; +use std::ops::{Deref, DerefMut}; + +#[derive(Debug, Clone)] //, PartialOrd, PartialEq, Eq, Hash)] +pub struct BranchNumber { + branch_num: Rational, + delta: Rational, +} + +impl Default for BranchNumber { + fn default() -> Self { + Self { + branch_num: Rational::from(1usize << 63), + delta: Rational::from(1), + } + } +} + +impl PartialEq for BranchNumber { + #[inline] + fn eq(&self, rhs: &BranchNumber) -> bool { + self.branch_num == rhs.branch_num + } +} + +impl Eq for BranchNumber {} + +impl Hash for BranchNumber { + #[inline(always)] + fn hash(&self, hasher: &mut H) { + self.branch_num.hash(hasher) + } +} + +impl PartialOrd for BranchNumber { + #[inline] + fn partial_cmp(&self, rhs: &BranchNumber) -> Option { + self.branch_num.partial_cmp(&rhs.branch_num) + } +} + +impl BranchNumber { + fn split(&self) -> BranchNumber { + BranchNumber { + branch_num: self.branch_num.clone() + &self.delta / Rational::from(2), + delta: &self.delta / Rational::from(4), + } + } + + fn incr_by_delta(&self) -> BranchNumber { + BranchNumber { + branch_num: self.branch_num.clone() + &self.delta, + delta: self.delta.clone(), + } + } + + fn halve_delta(&self) -> BranchNumber { + BranchNumber { + branch_num: self.branch_num.clone(), + delta : &self.delta / Rational::from(2), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct VarInfo { + var_ptr: VarPtr, + chunk_type: ChunkType, + classify_info: ClassifyInfo, + lvl: Level, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ChunkInfo { + chunk_num: usize, + term_loc: GenContext, + // pointer to incidence, term occurrence arity. + vars: Vec, +} + +#[derive(Debug)] +pub struct BranchArm { + pub arm_terms: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct BranchInfo { + branch_num: BranchNumber, + chunks: Vec, +} + +impl BranchInfo { + fn new(branch_num: BranchNumber) -> Self { + Self { branch_num, chunks: vec![] } + } +} + +type BranchMapInt = IndexMap>; + +#[derive(Debug, Clone)] +pub struct BranchMap(BranchMapInt); + +impl Deref for BranchMap { + type Target = BranchMapInt; + + #[inline(always)] + fn deref(&self) -> &BranchMapInt { + &self.0 + } +} + +impl DerefMut for BranchMap { + #[inline(always)] + fn deref_mut(&mut self) -> &mut BranchMapInt { + &mut self.0 + } +} + +type RootSet = IndexSet; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct ClassifyInfo { + arg_c: usize, + arity: usize, +} + +enum TraversalState { + // construct a QueryTerm::Branch with number of disjuncts, reset + // the chunk type to that of the chunk preceding the disjunct and the chunk_num. + BuildDisjunct(usize), + // add the last disjunct to a QueryTerm::Branch, continuing from + // where it leaves off. + BuildFinalDisjunct(usize), + Fail, + GetCutPoint{ var_num: usize, prev_b: bool }, + Cut { var_num: usize, is_global: bool }, + ResetCallPolicy(CallPolicy), + Term(Term), + RemoveBranchNum, // pop the current_branch_num and from the root set. + AddBranchNum(BranchNumber), // set current_branch_num, add it to the root set + RepBranchNum(BranchNumber), // replace current_branch_num and the latest in the root set +} + +#[derive(Debug)] +pub struct VariableClassifier { + call_policy: CallPolicy, + current_branch_num: BranchNumber, + current_chunk_num: usize, + current_chunk_type: ChunkType, + branch_map: BranchMap, + var_num: usize, + root_set: RootSet, + global_cut_var_num: Option, +} + +#[derive(Debug, Default)] +pub struct VarData { + pub records: VariableRecords, + pub global_cut_var_num: Option, + pub allocates: bool, +} + +impl VarData { + fn emit_initial_get_level(&mut self, build_stack: &mut ChunkedTermVec) { + let global_cut_var_num = + if let &Some(global_cut_var_num) = &self.global_cut_var_num { + match &self.records[global_cut_var_num].allocation { + VarAlloc::Perm(..) => Some(global_cut_var_num), + VarAlloc::Temp { term_loc, .. } if term_loc.chunk_num() > 0 => { + Some(global_cut_var_num) + } + _ => None + } + } else { + None + }; + + if let Some(global_cut_var_num) = global_cut_var_num { + let term = QueryTerm::GetLevel(global_cut_var_num); + self.records[global_cut_var_num].allocation = VarAlloc::Perm(0, PermVarAllocation::Pending); + + match build_stack.front_mut() { + Some(ChunkedTerms::Branch(_)) => { + build_stack.push_front(ChunkedTerms::Chunk(VecDeque::from(vec![term]))); + } + Some(ChunkedTerms::Chunk(chunk)) => { + chunk.push_front(term); + } + None => { + unreachable!() + } + } + } + } +} + +pub type ClassifyFactResult = (Term, VarData); +pub type ClassifyRuleResult = (Term, ChunkedTermVec, VarData); + +fn merge_branch_seq(branches: impl Iterator) -> BranchInfo { + let mut branch_info = BranchInfo::new(BranchNumber::default()); + + for mut branch in branches { + branch_info.branch_num = branch.branch_num; + branch_info.chunks.extend(branch.chunks.drain(..)); + } + + branch_info.branch_num.delta = branch_info.branch_num.delta * Integer::from(2); + branch_info.branch_num.branch_num -= &branch_info.branch_num.delta; + + branch_info +} + +fn flatten_into_disjunct(build_stack: &mut ChunkedTermVec, preceding_len: usize) { + let branch_vec = build_stack.drain(preceding_len + 1 ..).collect(); + + if let ChunkedTerms::Branch(ref mut disjuncts) = &mut build_stack[preceding_len] { + disjuncts.push(branch_vec); + } else { + unreachable!(); + } +} + +impl VariableClassifier { + pub fn new(call_policy: CallPolicy) -> Self { + Self { + call_policy, + current_branch_num: BranchNumber::default(), + current_chunk_num: 0, + current_chunk_type: ChunkType::Head, + branch_map: BranchMap(BranchMapInt::new()), + root_set: RootSet::new(), + var_num: 0, + global_cut_var_num: None, + } + } + + pub fn classify_fact(mut self, term: Term) -> Result { + self.classify_head_variables(&term)?; + Ok((term, self.branch_map.separate_and_classify_variables( + self.var_num, + self.global_cut_var_num, + self.current_chunk_num, + ))) + } + + pub fn classify_rule<'a, LS: LoadState<'a>>( + mut self, + loader: &mut Loader<'a, LS>, + head: Term, + body: Term, + ) -> Result { + self.classify_head_variables(&head)?; + self.root_set.insert(self.current_branch_num.clone()); + + let mut query_terms = self.classify_body_variables(loader, body)?; + + self.merge_branches(); + + let mut var_data = self.branch_map.separate_and_classify_variables( + self.var_num, + self.global_cut_var_num, + self.current_chunk_num, + ); + + var_data.emit_initial_get_level(&mut query_terms); + + Ok((head, query_terms, var_data)) + } + + fn merge_branches(&mut self) { + for branches in self.branch_map.values_mut() { + let mut old_branches = std::mem::replace(branches, vec![]); + + while let Some(last_branch_num) = old_branches.last().map(|bi| &bi.branch_num) { + let mut old_branches_len = old_branches.len(); + + for (rev_idx, bi) in old_branches.iter().rev().enumerate() { + if &bi.branch_num > last_branch_num { + old_branches_len = old_branches.len() - rev_idx; + } + } + + let iter = old_branches.drain(old_branches_len - 1 ..); + branches.push(merge_branch_seq(iter)); + } + + branches.reverse(); + } + } + + fn try_set_chunk_at_inlined_boundary(&mut self) -> bool { + if self.current_chunk_type.is_last() { + self.current_chunk_type = ChunkType::Mid; + self.current_chunk_num += 1; + true + } else { + false + } + } + + fn try_set_chunk_at_call_boundary(&mut self) -> bool { + if self.current_chunk_type.is_last() { + self.current_chunk_num += 1; + true + } else { + self.current_chunk_type = ChunkType::Last; + false + } + } + + fn probe_body_term(&mut self, arg_c: usize, arity: usize, term: &Term) { + let classify_info = ClassifyInfo { arg_c, arity }; + + // second arg is true to iterate the root, which may be a variable + for term_ref in breadth_first_iter(term, RootIterationPolicy::Iterated) { + if let TermRef::Var(lvl, _, var_ptr) = term_ref { + // root terms are shallow here (since we're iterating a + // body term) so take the child level. + let lvl = lvl.child_level(); + self.probe_body_var(VarInfo { + var_ptr, + lvl, + classify_info, + chunk_type: self.current_chunk_type, + }); + } + } + } + + fn probe_body_var(&mut self, var_info: VarInfo) { + let term_loc = self.current_chunk_type.to_gen_context(self.current_chunk_num); + + let branch_info_v = self.branch_map.entry(var_info.var_ptr.clone()) + .or_insert_with(|| vec![]); + + let needs_new_branch = if let Some(last_bi) = branch_info_v.last() { + !self.root_set.contains(&last_bi.branch_num) + } else { + true + }; + + if needs_new_branch { + branch_info_v.push(BranchInfo::new(self.current_branch_num.clone())); + } + + let branch_info = branch_info_v.last_mut().unwrap(); + + let needs_new_chunk = if let Some(last_ci) = branch_info.chunks.last() { + last_ci.chunk_num != self.current_chunk_num + } else { + true + }; + + if needs_new_chunk { + branch_info.chunks.push(ChunkInfo { + chunk_num: self.current_chunk_num, + term_loc, + vars: vec![], + }); + } + + let chunk_info = branch_info.chunks.last_mut().unwrap(); + chunk_info.vars.push(var_info); + } + + fn probe_in_situ_var(&mut self, var_num: usize) { + let classify_info = ClassifyInfo { arg_c: 1, arity: 1 }; + + let var_info = VarInfo { + var_ptr: VarPtr::from(Var::InSitu(var_num)), + classify_info, + chunk_type: self.current_chunk_type, + lvl: Level::Shallow, + }; + + self.probe_body_var(var_info); + } + + fn classify_head_variables(&mut self, term: &Term) -> Result<(), CompilationError> { + match term { + Term::Clause(..) | Term::Literal(_, Literal::Atom(_)) => { + } + _ => return Err(CompilationError::InvalidRuleHead), + } + + let mut classify_info = ClassifyInfo { arg_c: 1, arity: term.arity() }; + + match term { + Term::Clause(_, _, terms) => { + for term in terms.into_iter() { + for term_ref in breadth_first_iter(term, RootIterationPolicy::Iterated) { + if let TermRef::Var(lvl, _, var_ptr) = term_ref { + // a body term, so we need the child level here. + let lvl = lvl.child_level(); + + // the body of the if let here is an inlined + // "probe_head_var". note the difference between it + // and "probe_body_var". + let branch_info_v = self.branch_map.entry(var_ptr.clone()) + .or_insert_with(|| vec![]); + + let needs_new_branch = branch_info_v.is_empty(); + + if needs_new_branch { + branch_info_v.push(BranchInfo::new(self.current_branch_num.clone())); + } + + let branch_info = branch_info_v.last_mut().unwrap(); + let needs_new_chunk = branch_info.chunks.is_empty(); + + if needs_new_chunk { + branch_info.chunks.push(ChunkInfo { + chunk_num: self.current_chunk_num, + term_loc: GenContext::Head, + vars: vec![], + }); + } + + let chunk_info = branch_info.chunks.last_mut().unwrap(); + let var_info = VarInfo { + var_ptr, + classify_info, + chunk_type: self.current_chunk_type, + lvl, + }; + + chunk_info.vars.push(var_info); + } + } + + classify_info.arg_c += 1; + } + } + _ => {} + } + + Ok(()) + } + + fn classify_body_variables<'a, LS: LoadState<'a>>( + &mut self, + loader: &mut Loader<'a, LS>, + term: Term, + ) -> Result { + let mut state_stack = vec![TraversalState::Term(term)]; + let mut build_stack = ChunkedTermVec::new(); + + self.current_chunk_type = ChunkType::Mid; + + while let Some(traversal_st) = state_stack.pop() { + match traversal_st { + TraversalState::AddBranchNum(branch_num) => { + self.root_set.insert(branch_num.clone()); + self.current_branch_num = branch_num; + } + TraversalState::RemoveBranchNum => { + self.root_set.pop(); + } + TraversalState::RepBranchNum(branch_num) => { + self.root_set.pop(); + self.root_set.insert(branch_num.clone()); + self.current_branch_num = branch_num; + } + TraversalState::ResetCallPolicy(call_policy) => { + self.call_policy = call_policy; + } + TraversalState::BuildDisjunct(preceding_len) => { + flatten_into_disjunct(&mut build_stack, preceding_len); + + self.current_chunk_type = ChunkType::Mid; + self.current_chunk_num += 1; + } + TraversalState::BuildFinalDisjunct(preceding_len) => { + flatten_into_disjunct(&mut build_stack, preceding_len); + + self.current_chunk_type = ChunkType::Mid; + self.current_chunk_num += 1; + } + TraversalState::GetCutPoint { var_num, prev_b } => { + if self.try_set_chunk_at_inlined_boundary() { + build_stack.add_chunk(); + } + + self.probe_in_situ_var(var_num); + build_stack.push_chunk_term(QueryTerm::GetCutPoint { var_num, prev_b }); + } + TraversalState::Cut { var_num, is_global } => { + if self.try_set_chunk_at_inlined_boundary() { + build_stack.add_chunk(); + } + + self.probe_in_situ_var(var_num); + + build_stack.push_chunk_term( + if is_global { + QueryTerm::GlobalCut(var_num) + } else { + QueryTerm::LocalCut(var_num) + } + ); + } + TraversalState::Fail => { + build_stack.push_chunk_term(QueryTerm::Fail); + } + TraversalState::Term(term) => { + // return true iff new chunk should be added. + let update_chunk_data = |classifier: &mut Self, predicate_name, arity| { + if ClauseType::is_inlined(predicate_name, arity) { + classifier.try_set_chunk_at_inlined_boundary() + } else { + classifier.try_set_chunk_at_call_boundary() + } + }; + + match term { + Term::Clause(_, atom!(","), mut terms) if terms.len() == 2 => { + let tail = terms.pop().unwrap(); + let head = terms.pop().unwrap(); + + let iter = unfold_by_str(tail, atom!(",")) + .into_iter() + .rev() + .chain(std::iter::once(head)) + .map(TraversalState::Term); + + state_stack.extend(iter); + } + Term::Clause(_, atom!(";"), mut terms) if terms.len() == 2 => { + let tail = terms.pop().unwrap(); + let head = terms.pop().unwrap(); + + let first_branch_num = self.current_branch_num.split(); + let branches: Vec<_> = std::iter::once(head) + .chain(unfold_by_str(tail, atom!(";")).into_iter()) + .collect(); + + let mut branch_numbers = vec![first_branch_num]; + + for idx in 1 .. branches.len() { + let succ_branch_number = branch_numbers[idx - 1].incr_by_delta(); + + branch_numbers.push(if idx + 1 < branches.len() { + succ_branch_number.split() + } else { + succ_branch_number + }); + } + + let build_stack_len = build_stack.len(); + build_stack.reserve_branch(branches.len()); + + state_stack.push(TraversalState::RepBranchNum( + self.current_branch_num.halve_delta(), + )); + + let iter = branches.into_iter().zip(branch_numbers.into_iter()); + let final_disjunct_loc = state_stack.len(); + + for (term, branch_num) in iter.rev() { + state_stack.push(TraversalState::BuildDisjunct(build_stack_len)); + state_stack.push(TraversalState::RemoveBranchNum); + state_stack.push(TraversalState::Term(term)); + state_stack.push(TraversalState::AddBranchNum(branch_num)); + } + + if let TraversalState::BuildDisjunct(build_stack_len) = state_stack[final_disjunct_loc] { + state_stack[final_disjunct_loc] = TraversalState::BuildFinalDisjunct(build_stack_len); + } + + self.current_chunk_type = ChunkType::Mid; + self.current_chunk_num += 1; + } + Term::Clause(_, atom!("->"), mut terms) if terms.len() == 2 => { + let then_term = terms.pop().unwrap(); + let if_term = terms.pop().unwrap(); + + let prev_b = if matches!(state_stack.last(), Some(TraversalState::RemoveBranchNum)) { + // check if the second-to-last element is a regular BuildDisjunct, as we don't + // want to add GetPrevLevel in case of a TrustMe. + matches!(state_stack.iter().rev().nth(1), Some(TraversalState::BuildDisjunct(..))) + } else { + false + }; + + state_stack.push(TraversalState::Term(then_term)); + state_stack.push(TraversalState::Cut { var_num: self.var_num, is_global: false }); + state_stack.push(TraversalState::Term(if_term)); + state_stack.push(TraversalState::GetCutPoint { var_num: self.var_num, prev_b }); + + self.var_num += 1; + } + Term::Clause(_, atom!("\\+"), mut terms) if terms.len() == 1 => { + let not_term = terms.pop().unwrap(); + let build_stack_len = build_stack.len(); + + build_stack.reserve_branch(2); + + state_stack.push(TraversalState::BuildFinalDisjunct(build_stack_len)); + state_stack.push(TraversalState::Term(Term::Clause(Cell::default(), atom!("$succeed"), vec![]))); + state_stack.push(TraversalState::BuildDisjunct(build_stack_len)); + state_stack.push(TraversalState::Fail); + state_stack.push(TraversalState::Cut { var_num: self.var_num, is_global: false }); + state_stack.push(TraversalState::Term(not_term)); + state_stack.push(TraversalState::GetCutPoint { var_num: self.var_num, prev_b: true }); + + self.current_chunk_type = ChunkType::Mid; + self.current_chunk_num += 1; + + self.var_num += 1; + } + Term::Clause(_, atom!(":"), mut terms) if terms.len() == 2 => { + let predicate_name = terms.pop().unwrap(); + let module_name = terms.pop().unwrap(); + + match (module_name, predicate_name) { + ( + Term::Literal(_, Literal::Atom(module_name)), + Term::Literal(_, Literal::Atom(predicate_name)), + ) => { + if update_chunk_data(self, predicate_name, 0) { + build_stack.add_chunk(); + } + + build_stack.push_chunk_term( + qualified_clause_to_query_term( + loader, + module_name, + predicate_name, + vec![], + self.call_policy, + ), + ); + } + ( + Term::Literal(_, Literal::Atom(module_name)), + Term::Clause(_, name, terms), + ) => { + if update_chunk_data(self, name, terms.len()) { + build_stack.add_chunk(); + } + + for (arg_c, term) in terms.iter().enumerate() { + self.probe_body_term(arg_c + 1, terms.len(), term); + } + + build_stack.push_chunk_term( + qualified_clause_to_query_term( + loader, + module_name, + name, + terms, + self.call_policy, + ), + ); + } + (module_name, predicate_name) => { + if update_chunk_data(self, atom!("call"), 2) { + build_stack.add_chunk(); + } + + self.probe_body_term(1, 0, &module_name); + self.probe_body_term(2, 0, &predicate_name); + + terms.push(module_name); + terms.push(predicate_name); + + build_stack.push_chunk_term( + clause_to_query_term( + loader, + atom!("call"), + vec![Term::Clause(Cell::default(), atom!(":"), terms)], + self.call_policy, + ), + ); + } + } + } + Term::Clause(_, atom!("$call_with_inference_counting"), mut terms) if terms.len() == 1 => { + state_stack.push(TraversalState::ResetCallPolicy(self.call_policy)); + state_stack.push(TraversalState::Term(terms.pop().unwrap())); + + self.call_policy = CallPolicy::Counted; + } + Term::Clause(_, name, terms) => { + if update_chunk_data(self, name, terms.len()) { + build_stack.add_chunk(); + } + + for (arg_c, term) in terms.iter().enumerate() { + self.probe_body_term(arg_c + 1, terms.len(), term); + } + + build_stack.push_chunk_term( + clause_to_query_term( + loader, + name, + terms, + self.call_policy, + ), + ); + } + var @ Term::Var(..) => { + if update_chunk_data(self, atom!("call"), 1) { + build_stack.add_chunk(); + } + + self.probe_body_term(1, 1, &var); + + build_stack.push_chunk_term( + clause_to_query_term( + loader, + atom!("call"), + vec![var], + self.call_policy, + ), + ); + } + Term::Literal(_, Literal::Atom(atom!("!")) | Literal::Char('!')) => { + if self.global_cut_var_num.is_none() { + self.global_cut_var_num = Some(self.var_num); + self.var_num += 1; + } + + self.probe_in_situ_var(self.global_cut_var_num.unwrap()); + + state_stack.push(TraversalState::Cut { + var_num: self.global_cut_var_num.unwrap(), + is_global: true, + }); + } + Term::Literal(_, Literal::Atom(name)) => { + if update_chunk_data(self, name, 0) { + build_stack.add_chunk(); + } + + build_stack.push_chunk_term( + clause_to_query_term( + loader, + name, + vec![], + self.call_policy, + ), + ); + } + _ => { + return Err(CompilationError::InadmissibleQueryTerm); + } + } + } + } + } + + Ok(build_stack) + } +} + +impl BranchMap { + pub fn separate_and_classify_variables( + &mut self, + var_num: usize, + global_cut_var_num: Option, + current_chunk_num: usize, + ) -> VarData { + let mut var_data = VarData { + records: VariableRecords::new(var_num), + global_cut_var_num, + allocates: current_chunk_num > 0, + }; + + for (var, branches) in self.iter_mut() { + let (mut var_num, var_num_incr) = + if let Var::InSitu(var_num) = *var.borrow() { + (var_num, false) + } else { + (var_data.records.len(), true) + }; + + for branch in branches.iter_mut() { + if var_num_incr { + var_num = var_data.records.len(); + var_data.records.push(VariableRecord::default()); + } + + if branch.chunks.len() <= 1 { // true iff var is a temporary variable. + debug_assert_eq!(branch.chunks.len(), 1); + + let chunk = &mut branch.chunks[0]; + let mut temp_var_data = TempVarData::new(); + + for var_info in chunk.vars.iter_mut() { + if var_info.lvl == Level::Shallow { + let term_loc = var_info.chunk_type.to_gen_context(chunk.chunk_num); + temp_var_data.use_set.insert((term_loc, var_info.classify_info.arg_c)); + } + } + + var_data.records[var_num].allocation = VarAlloc::Temp { + term_loc: chunk.term_loc, + temp_reg: 0, + temp_var_data, + safety: VarSafetyStatus::Needed, + to_perm_var_num: None, + }; + } // else VarAlloc is already a Perm variant, as it's the default. + + for chunk in branch.chunks.iter_mut() { + var_data.records[var_num].num_occurrences += chunk.vars.len(); + + for var_info in chunk.vars.iter_mut() { + var_info.var_ptr.set(Var::Generated(var_num)); + } + } + } + } + + var_data.records.populate_restricting_sets(); + var_data + } +} diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index ee57a83e..6eeedb63 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -9,6 +9,8 @@ use crate::types::*; use crate::try_numeric_result; +use fxhash::FxBuildHasher; + macro_rules! step_or_fail { ($self:expr, $step_e:expr) => { if $self.machine_st.fail { @@ -139,7 +141,7 @@ impl MachineState { Ok(()) } - fn keysort(&mut self) -> CallResult { + fn keysort(&mut self, var_comparison: VarComparison) -> CallResult { self.check_keysort_errors()?; let stub_gen = || functor_stub(atom!("keysort"), 2); @@ -153,7 +155,7 @@ impl MachineState { } key_pairs.sort_by(|a1, a2| { - compare_term_test!(self, a1.0, a2.0).unwrap_or(Ordering::Less) + compare_term_test!(self, a1.0, a2.0, var_comparison).unwrap_or(Ordering::Less) }); let key_pairs = key_pairs.into_iter().map(|kp| kp.1); @@ -182,37 +184,131 @@ impl MachineState { Ok(()) } + + #[inline(always)] + pub(crate) fn select_switch_on_term_index( + &self, + addr: HeapCellValue, + v: IndexingCodePtr, + c: IndexingCodePtr, + l: IndexingCodePtr, + s: IndexingCodePtr, + ) -> IndexingCodePtr { + read_heap_cell!(addr, + (HeapCellValueTag::Var | + HeapCellValueTag::StackVar | + HeapCellValueTag::AttrVar) => { + v + } + (HeapCellValueTag::PStrLoc | + HeapCellValueTag::Lis | + HeapCellValueTag::CStr) => { + l + } + (HeapCellValueTag::Fixnum | + HeapCellValueTag::Char | + HeapCellValueTag::F64) => { + c + } + (HeapCellValueTag::Atom, (_name, arity)) => { + // if arity == 0 { c } else { s } + debug_assert!(arity == 0); + c + } + (HeapCellValueTag::Str, st) => { + let (name, arity) = cell_as_atom_cell!(self.heap[st]) + .get_name_and_arity(); + + match (name, arity) { + (atom!("."), 2) => l, + (_, 0) => c, + _ => s, + } + } + (HeapCellValueTag::Cons, ptr) => { + match ptr.get_tag() { + ArenaHeaderTag::Rational | ArenaHeaderTag::Integer => { + c + } + _ => { + IndexingCodePtr::Fail + } + } + } + _ => { + unreachable!(); + } + ) + } + + #[inline(always)] + pub(crate) fn constant_to_literal(&self, addr: HeapCellValue) -> Literal { + read_heap_cell!(addr, + (HeapCellValueTag::Char, c) => { + Literal::Char(c) + } + (HeapCellValueTag::Fixnum, n) => { + Literal::Fixnum(n) + } + (HeapCellValueTag::F64, f) => { + Literal::Float(f.as_offset()) + } + (HeapCellValueTag::Atom, (atom, arity)) => { + debug_assert_eq!(arity, 0); + Literal::Atom(atom) + } + (HeapCellValueTag::Str, s) => { + Literal::Atom(cell_as_atom_cell!(self.heap[s]).get_name()) + } + (HeapCellValueTag::Cons, cons_ptr) => { + match_untyped_arena_ptr!(cons_ptr, + (ArenaHeaderTag::Rational, r) => { + Literal::Rational(r) + } + (ArenaHeaderTag::Integer, n) => { + Literal::Integer(n) + } + _ => { + unreachable!() + } + ) + } + _ => { + unreachable!() + } + ) + } + + #[inline(always)] + pub(crate) fn select_switch_on_structure_index( + &self, + addr: HeapCellValue, + hm: &IndexMap<(Atom, usize), IndexingCodePtr, FxBuildHasher>, + ) -> IndexingCodePtr { + read_heap_cell!(addr, + (HeapCellValueTag::Atom, (name, arity)) => { + match hm.get(&(name, arity)) { + Some(offset) => *offset, + None => IndexingCodePtr::Fail, + } + } + (HeapCellValueTag::Str, s) => { + let (name, arity) = cell_as_atom_cell!(self.heap[s]) + .get_name_and_arity(); + + match hm.get(&(name, arity)) { + Some(offset) => *offset, + None => IndexingCodePtr::Fail, + } + } + _ => { + IndexingCodePtr::Fail + } + ) + } } impl Machine { - fn read(&mut self) -> CallResult { - let stream = self.machine_st.get_stream_or_alias( - self.machine_st.registers[1], - &self.indices.stream_aliases, - atom!("read"), - 2, - )?; - - match self.machine_st.read(stream, &self.indices.op_dir) { - Ok(offset) => { - let value = self.machine_st.registers[2]; - unify_fn!(&mut self.machine_st, value, heap_loc_as_cell!(offset.heap_loc)); - } - Err(CompilationError::ParserError(ParserError::UnexpectedEOF)) => { - let value = self.machine_st.registers[2]; - self.machine_st.unify_atom(atom!("end_of_file"), value); - } - Err(e) => { - let stub = functor_stub(atom!("read"), 2); - let err = self.machine_st.syntax_error(e); - - return Err(self.machine_st.error_form(err, stub)); - } - }; - - Ok(()) - } - pub(super) fn find_living_dynamic_else(&self, mut p: usize) -> Option<(usize, usize)> { loop { match &self.code[p] { @@ -349,51 +445,7 @@ impl Machine { loop { match &indexing_lines[index] { &IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, v, c, l, s)) => { - let offset = read_heap_cell!(addr, - (HeapCellValueTag::Var | - HeapCellValueTag::StackVar | - HeapCellValueTag::AttrVar) => { - v - } - (HeapCellValueTag::PStrLoc | - HeapCellValueTag::Lis | - HeapCellValueTag::CStr) => { - l - } - (HeapCellValueTag::Fixnum | - HeapCellValueTag::Char | - HeapCellValueTag::F64) => { - c - } - (HeapCellValueTag::Atom, (_name, arity)) => { - // if arity == 0 { c } else { s } - debug_assert!(arity == 0); - c - } - (HeapCellValueTag::Str, st) => { - let (name, arity) = cell_as_atom_cell!(self.machine_st.heap[st]) - .get_name_and_arity(); - - match (name, arity) { - (atom!("."), 2) => l, - (_, 0) => c, - _ => s, - } - } - (HeapCellValueTag::Cons, ptr) => { - match ptr.get_tag() { - ArenaHeaderTag::Rational | ArenaHeaderTag::Integer => { - c - } - _ => { - IndexingCodePtr::Fail - } - } - } - _ => { - unreachable!(); - } - ); + let offset = self.machine_st.select_switch_on_term_index(addr, v, c, l, s); match offset { IndexingCodePtr::Fail => { @@ -423,41 +475,8 @@ impl Machine { } } } - &IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(ref hm)) => { - let lit = read_heap_cell!(addr, - (HeapCellValueTag::Char, c) => { - Literal::Char(c) - } - (HeapCellValueTag::Fixnum, n) => { - Literal::Fixnum(n) - } - (HeapCellValueTag::F64, f) => { - Literal::Float(f.as_offset()) - } - (HeapCellValueTag::Atom, (atom, arity)) => { - debug_assert_eq!(arity, 0); - Literal::Atom(atom) - } - (HeapCellValueTag::Str, s) => { - Literal::Atom(cell_as_atom_cell!(self.machine_st.heap[s]).get_name()) - } - (HeapCellValueTag::Cons, cons_ptr) => { - match_untyped_arena_ptr!(cons_ptr, - (ArenaHeaderTag::Rational, r) => { - Literal::Rational(r) - } - (ArenaHeaderTag::Integer, n) => { - Literal::Integer(n) - } - _ => { - unreachable!() - } - ) - } - _ => { - unreachable!() - } - ); + IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(hm)) => { + let lit = self.machine_st.constant_to_literal(addr); let offset = match hm.get(&lit) { Some(offset) => *offset, @@ -492,27 +511,8 @@ impl Machine { } } } - &IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(ref hm)) => { - let offset = read_heap_cell!(addr, - (HeapCellValueTag::Atom, (name, arity)) => { - match hm.get(&(name, arity)) { - Some(offset) => *offset, - None => IndexingCodePtr::Fail, - } - } - (HeapCellValueTag::Str, s) => { - let (name, arity) = cell_as_atom_cell!(self.machine_st.heap[s]) - .get_name_and_arity(); - - match hm.get(&(name, arity)) { - Some(offset) => *offset, - None => IndexingCodePtr::Fail, - } - } - _ => { - IndexingCodePtr::Fail - } - ); + IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(hm)) => { + let offset = self.machine_st.select_switch_on_structure_index(addr, hm); match offset { IndexingCodePtr::Fail => { @@ -558,7 +558,7 @@ impl Machine { } #[inline(always)] - pub(super) fn dispatch_loop(&mut self) { + pub(super) fn dispatch_loop(&mut self) -> std::process::ExitCode { 'outer: loop { for _ in 0 .. INSTRUCTIONS_PER_INTERRUPT_POLL { match &self.code[self.machine_st.p] { @@ -930,6 +930,69 @@ impl Machine { self.machine_st.p += 1; } + &Instruction::ACosh(ref a1, t) => { + let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1)); + + self.machine_st.interms[t - 1] = Number::Float(OrderedFloat( + try_or_throw_gen!(&mut self.machine_st, acosh(n1)) + )); + + self.machine_st.p += 1; + } + &Instruction::ASinh(ref a1, t) => { + let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1)); + + self.machine_st.interms[t - 1] = Number::Float(OrderedFloat( + try_or_throw_gen!(&mut self.machine_st, asinh(n1)) + )); + + self.machine_st.p += 1; + } + &Instruction::ATanh(ref a1, t) => { + let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1)); + + self.machine_st.interms[t - 1] = Number::Float(OrderedFloat( + try_or_throw_gen!(&mut self.machine_st, atanh(n1)) + )); + + self.machine_st.p += 1; + } + &Instruction::Cosh(ref a1, t) => { + let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1)); + + self.machine_st.interms[t - 1] = Number::Float(OrderedFloat( + try_or_throw_gen!(&mut self.machine_st, cosh(n1)) + )); + + self.machine_st.p += 1; + } + &Instruction::Sinh(ref a1, t) => { + let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1)); + + self.machine_st.interms[t - 1] = Number::Float(OrderedFloat( + try_or_throw_gen!(&mut self.machine_st, sinh(n1)) + )); + + self.machine_st.p += 1; + } + &Instruction::Tanh(ref a1, t) => { + let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1)); + + self.machine_st.interms[t - 1] = Number::Float(OrderedFloat( + try_or_throw_gen!(&mut self.machine_st, tanh(n1)) + )); + + self.machine_st.p += 1; + } + &Instruction::Log10(ref a1, t) => { + let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1)); + + self.machine_st.interms[t - 1] = Number::Float(OrderedFloat( + try_or_throw_gen!(&mut self.machine_st, log10(n1)) + )); + + self.machine_st.p += 1; + } &Instruction::Float(ref a1, t) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1)); @@ -965,6 +1028,24 @@ impl Machine { self.machine_st.interms[t - 1] = floor(n1, &mut self.machine_st.arena); self.machine_st.p += 1; } + &Instruction::FloatFractionalPart(ref a1, t) => { + let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1)); + + self.machine_st.interms[t - 1] = Number::Float(OrderedFloat( + try_or_throw_gen!(&mut self.machine_st, float_fractional_part(n1)) + )); + + self.machine_st.p += 1; + } + &Instruction::FloatIntegerPart(ref a1, t) => { + let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1)); + + self.machine_st.interms[t - 1] = Number::Float(OrderedFloat( + try_or_throw_gen!(&mut self.machine_st, float_integer_part(n1)) + )); + + self.machine_st.p += 1; + } &Instruction::Plus(ref a1, t) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1)); @@ -997,7 +1078,7 @@ impl Machine { fixnum_as_cell!(Fixnum::build_with(self.machine_st.cc as i64)); self.machine_st.num_of_args += 1; - self.machine_st.try_me_else(next_i); + self.try_me_else(next_i); self.machine_st.num_of_args -= 1; } None => { @@ -1010,7 +1091,6 @@ impl Machine { .stack .index_or_frame(self.machine_st.b) .prelude - .univ_prelude .num_cells; self.machine_st.cc = cell_as_fixnum!( @@ -1068,7 +1148,7 @@ impl Machine { fixnum_as_cell!(Fixnum::build_with(self.machine_st.cc as i64)); self.machine_st.num_of_args += 1; - self.machine_st.try_me_else(next_i); + self.try_me_else(next_i); self.machine_st.num_of_args -= 1; } None => { @@ -1081,7 +1161,6 @@ impl Machine { .stack .index_or_frame(self.machine_st.b) .prelude - .univ_prelude .num_cells; self.machine_st.cc = cell_as_fixnum!( @@ -1120,7 +1199,7 @@ impl Machine { } } &Instruction::TryMeElse(offset) => { - self.machine_st.try_me_else(offset); + self.try_me_else(offset); } &Instruction::DefaultRetryMeElse(offset) => { self.retry_me_else(offset); @@ -1151,19 +1230,18 @@ impl Machine { &Instruction::GetLevel(r) => { let b0 = self.machine_st.b0; - self.machine_st[r] = fixnum_as_cell!(Fixnum::build_with(b0 as i64)); + self.machine_st[r] = fixnum_as_cell!(Fixnum::as_cutpoint(b0 as i64)); self.machine_st.p += 1; } - &Instruction::GetLevelAndUnify(r) => { - // let b0 = self.machine_st[perm_v!(1)]; - let b0 = cell_as_fixnum!( - self.machine_st.stack[stack_loc!(AndFrame, self.machine_st.e, 1)] - ); - let a = self.machine_st.store(self.machine_st.deref(self.machine_st[r])); + &Instruction::GetPrevLevel(r) => { + let prev_b = self.machine_st.stack.index_or_frame(self.machine_st.b).prelude.b; - // unify_fn!(&mut self.machine_st, a, b0); - self.machine_st.unify_fixnum(b0, a); - step_or_fail!(self, self.machine_st.p += 1); + self.machine_st[r] = fixnum_as_cell!(Fixnum::as_cutpoint(prev_b as i64)); + self.machine_st.p += 1; + } + &Instruction::GetCutPoint(r) => { + self.machine_st[r] = fixnum_as_cell!(Fixnum::as_cutpoint(self.machine_st.b as i64)); + self.machine_st.p += 1; } &Instruction::Cut(r) => { let value = self.machine_st[r]; @@ -1183,7 +1261,7 @@ impl Machine { &Instruction::Allocate(num_cells) => { self.machine_st.allocate(num_cells); } - &Instruction::DefaultCallAcyclicTerm(_) => { + &Instruction::DefaultCallAcyclicTerm => { let addr = self.machine_st.registers[1]; if self.machine_st.is_cyclic_term(addr) { @@ -1192,7 +1270,7 @@ impl Machine { self.machine_st.p += 1; } } - &Instruction::DefaultExecuteAcyclicTerm(_) => { + &Instruction::DefaultExecuteAcyclicTerm => { let addr = self.machine_st.registers[1]; if self.machine_st.is_cyclic_term(addr) { @@ -1201,23 +1279,23 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::DefaultCallArg(_) => { + &Instruction::DefaultCallArg => { try_or_throw!(self.machine_st, self.machine_st.try_arg()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::DefaultExecuteArg(_) => { + &Instruction::DefaultExecuteArg => { try_or_throw!(self.machine_st, self.machine_st.try_arg()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::DefaultCallCompare(_) => { + &Instruction::DefaultCallCompare => { try_or_throw!(self.machine_st, self.machine_st.compare()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::DefaultExecuteCompare(_) => { + &Instruction::DefaultExecuteCompare => { try_or_throw!(self.machine_st, self.machine_st.compare()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::DefaultCallTermGreaterThan(_) => { + &Instruction::DefaultCallTermGreaterThan => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1227,7 +1305,7 @@ impl Machine { self.machine_st.backtrack(); } } - &Instruction::DefaultExecuteTermGreaterThan(_) => { + &Instruction::DefaultExecuteTermGreaterThan => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1237,7 +1315,7 @@ impl Machine { self.machine_st.backtrack(); } } - &Instruction::DefaultCallTermLessThan(_) => { + &Instruction::DefaultCallTermLessThan => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1247,7 +1325,7 @@ impl Machine { self.machine_st.backtrack(); } } - &Instruction::DefaultExecuteTermLessThan(_) => { + &Instruction::DefaultExecuteTermLessThan => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1257,7 +1335,7 @@ impl Machine { self.machine_st.backtrack(); } } - &Instruction::DefaultCallTermGreaterThanOrEqual(_) => { + &Instruction::DefaultCallTermGreaterThanOrEqual => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1270,7 +1348,7 @@ impl Machine { } } } - &Instruction::DefaultExecuteTermGreaterThanOrEqual(_) => { + &Instruction::DefaultExecuteTermGreaterThanOrEqual => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1283,7 +1361,7 @@ impl Machine { } } } - &Instruction::DefaultCallTermLessThanOrEqual(_) => { + &Instruction::DefaultCallTermLessThanOrEqual => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1296,7 +1374,7 @@ impl Machine { } } } - &Instruction::DefaultExecuteTermLessThanOrEqual(_) => { + &Instruction::DefaultExecuteTermLessThanOrEqual => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1309,24 +1387,11 @@ impl Machine { } } } - &Instruction::DefaultCallRead(_) => { - try_or_throw!(self.machine_st, self.read()); - step_or_fail!(self, self.machine_st.p += 1); - } - &Instruction::DefaultExecuteRead(_) => { - try_or_throw!(self.machine_st, self.read()); - - if self.machine_st.fail { - self.machine_st.backtrack(); - } else { - self.machine_st.p = self.machine_st.cp; - } - } - &Instruction::DefaultCallCopyTerm(_) => { + &Instruction::DefaultCallCopyTerm => { self.machine_st.copy_term(AttrVarPolicy::DeepCopy); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::DefaultExecuteCopyTerm(_) => { + &Instruction::DefaultExecuteCopyTerm => { self.machine_st.copy_term(AttrVarPolicy::DeepCopy); if self.machine_st.fail { @@ -1335,7 +1400,7 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::DefaultCallTermEqual(_) => { + &Instruction::DefaultCallTermEqual => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1345,7 +1410,7 @@ impl Machine { self.machine_st.p += 1; } } - &Instruction::DefaultExecuteTermEqual(_) => { + &Instruction::DefaultExecuteTermEqual => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1355,26 +1420,26 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::DefaultCallGround(_) => { + &Instruction::DefaultCallGround => { if self.machine_st.ground_test() { self.machine_st.backtrack(); } else { self.machine_st.p += 1; } } - &Instruction::DefaultExecuteGround(_) => { + &Instruction::DefaultExecuteGround => { if self.machine_st.ground_test() { self.machine_st.backtrack(); } else { self.machine_st.p = self.machine_st.cp; } } - &Instruction::DefaultCallFunctor(_) => { + &Instruction::DefaultCallFunctor => { try_or_throw!(self.machine_st, self.machine_st.try_functor()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::DefaultExecuteFunctor(_) => { + &Instruction::DefaultExecuteFunctor => { try_or_throw!(self.machine_st, self.machine_st.try_functor()); if self.machine_st.fail { @@ -1383,7 +1448,7 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::DefaultCallTermNotEqual(_) => { + &Instruction::DefaultCallTermNotEqual => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1393,7 +1458,7 @@ impl Machine { self.machine_st.p += 1; } } - &Instruction::DefaultExecuteTermNotEqual(_) => { + &Instruction::DefaultExecuteTermNotEqual => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1403,20 +1468,20 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::DefaultCallSort(_) => { + &Instruction::DefaultCallSort => { try_or_throw!(self.machine_st, self.machine_st.sort()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::DefaultExecuteSort(_) => { + &Instruction::DefaultExecuteSort => { try_or_throw!(self.machine_st, self.machine_st.sort()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::DefaultCallKeySort(_) => { - try_or_throw!(self.machine_st, self.machine_st.keysort()); + &Instruction::DefaultCallKeySort => { + try_or_throw!(self.machine_st, self.machine_st.keysort(VarComparison::Distinct)); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::DefaultExecuteKeySort(_) => { - try_or_throw!(self.machine_st, self.machine_st.keysort()); + &Instruction::DefaultExecuteKeySort => { + try_or_throw!(self.machine_st, self.machine_st.keysort(VarComparison::Distinct)); if self.machine_st.fail { self.machine_st.backtrack(); @@ -1424,15 +1489,15 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::DefaultCallIs(r, at, _) => { + &Instruction::DefaultCallIs(r, at) => { try_or_throw!(self.machine_st, self.machine_st.is(r, at)); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::DefaultExecuteIs(r, at, _) => { + &Instruction::DefaultExecuteIs(r, at) => { try_or_throw!(self.machine_st, self.machine_st.is(r, at)); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallAcyclicTerm(_) => { + &Instruction::CallAcyclicTerm => { let addr = self.machine_st.registers[1]; if self.machine_st.is_cyclic_term(addr) { @@ -1446,7 +1511,7 @@ impl Machine { self.machine_st.p += 1; } } - &Instruction::ExecuteAcyclicTerm(_) => { + &Instruction::ExecuteAcyclicTerm => { let addr = self.machine_st.registers[1]; if self.machine_st.is_cyclic_term(addr) { @@ -1460,7 +1525,7 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::CallArg(_) => { + &Instruction::CallArg => { try_or_throw!(self.machine_st, self.machine_st.try_arg()); if self.machine_st.fail { @@ -1474,7 +1539,7 @@ impl Machine { self.machine_st.p += 1; } } - &Instruction::ExecuteArg(_) => { + &Instruction::ExecuteArg => { try_or_throw!(self.machine_st, self.machine_st.try_arg()); if self.machine_st.fail { @@ -1488,7 +1553,7 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::CallCompare(_) => { + &Instruction::CallCompare => { try_or_throw!(self.machine_st, self.machine_st.compare()); if self.machine_st.fail { @@ -1502,7 +1567,7 @@ impl Machine { self.machine_st.p += 1; } } - &Instruction::ExecuteCompare(_) => { + &Instruction::ExecuteCompare => { try_or_throw!(self.machine_st, self.machine_st.compare()); if self.machine_st.fail { @@ -1516,7 +1581,7 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::CallTermGreaterThan(_) => { + &Instruction::CallTermGreaterThan => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1531,7 +1596,7 @@ impl Machine { self.machine_st.backtrack(); } } - &Instruction::ExecuteTermGreaterThan(_) => { + &Instruction::ExecuteTermGreaterThan => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1546,7 +1611,7 @@ impl Machine { self.machine_st.backtrack(); } } - &Instruction::CallTermLessThan(_) => { + &Instruction::CallTermLessThan => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1561,7 +1626,7 @@ impl Machine { self.machine_st.backtrack(); } } - &Instruction::ExecuteTermLessThan(_) => { + &Instruction::ExecuteTermLessThan => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1576,7 +1641,7 @@ impl Machine { self.machine_st.backtrack(); } } - &Instruction::CallTermGreaterThanOrEqual(_) => { + &Instruction::CallTermGreaterThanOrEqual => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1594,7 +1659,7 @@ impl Machine { } } } - &Instruction::ExecuteTermGreaterThanOrEqual(_) => { + &Instruction::ExecuteTermGreaterThanOrEqual => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1612,7 +1677,7 @@ impl Machine { } } } - &Instruction::CallTermLessThanOrEqual(_) => { + &Instruction::CallTermLessThanOrEqual => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1630,7 +1695,7 @@ impl Machine { } } } - &Instruction::ExecuteTermLessThanOrEqual(_) => { + &Instruction::ExecuteTermLessThanOrEqual => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1648,35 +1713,7 @@ impl Machine { } } } - &Instruction::CallRead(_) => { - try_or_throw!(self.machine_st, self.read()); - - if self.machine_st.fail { - self.machine_st.backtrack(); - } else { - try_or_throw!( - self.machine_st, - (self.machine_st.increment_call_count_fn)(&mut self.machine_st) - ); - - self.machine_st.p += 1; - } - } - &Instruction::ExecuteRead(_) => { - try_or_throw!(self.machine_st, self.read()); - - if self.machine_st.fail { - self.machine_st.backtrack(); - } else { - try_or_throw!( - self.machine_st, - (self.machine_st.increment_call_count_fn)(&mut self.machine_st) - ); - - self.machine_st.p = self.machine_st.cp; - } - } - &Instruction::CallCopyTerm(_) => { + &Instruction::CallCopyTerm => { self.machine_st.copy_term(AttrVarPolicy::DeepCopy); if self.machine_st.fail { @@ -1690,7 +1727,7 @@ impl Machine { self.machine_st.p += 1; } } - &Instruction::ExecuteCopyTerm(_) => { + &Instruction::ExecuteCopyTerm => { self.machine_st.copy_term(AttrVarPolicy::DeepCopy); if self.machine_st.fail { @@ -1704,7 +1741,7 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::CallTermEqual(_) => { + &Instruction::CallTermEqual => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1719,7 +1756,7 @@ impl Machine { self.machine_st.p += 1; } } - &Instruction::ExecuteTermEqual(_) => { + &Instruction::ExecuteTermEqual => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1734,7 +1771,7 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::CallGround(_) => { + &Instruction::CallGround => { if self.machine_st.ground_test() { self.machine_st.backtrack(); } else { @@ -1746,7 +1783,7 @@ impl Machine { self.machine_st.p += 1; } } - &Instruction::ExecuteGround(_) => { + &Instruction::ExecuteGround => { if self.machine_st.ground_test() { self.machine_st.backtrack(); } else { @@ -1758,7 +1795,7 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::CallFunctor(_) => { + &Instruction::CallFunctor => { try_or_throw!(self.machine_st, self.machine_st.try_functor()); if self.machine_st.fail { @@ -1772,7 +1809,7 @@ impl Machine { self.machine_st.p += 1; } } - &Instruction::ExecuteFunctor(_) => { + &Instruction::ExecuteFunctor => { try_or_throw!(self.machine_st, self.machine_st.try_functor()); if self.machine_st.fail { @@ -1786,7 +1823,7 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::CallTermNotEqual(_) => { + &Instruction::CallTermNotEqual => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1801,7 +1838,7 @@ impl Machine { self.machine_st.p += 1; } } - &Instruction::ExecuteTermNotEqual(_) => { + &Instruction::ExecuteTermNotEqual => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1816,7 +1853,7 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::CallSort(_) => { + &Instruction::CallSort => { try_or_throw!(self.machine_st, self.machine_st.sort()); if self.machine_st.fail { @@ -1830,7 +1867,7 @@ impl Machine { self.machine_st.p += 1; } } - &Instruction::ExecuteSort(_) => { + &Instruction::ExecuteSort => { try_or_throw!(self.machine_st, self.machine_st.sort()); if self.machine_st.fail { @@ -1844,8 +1881,8 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::CallKeySort(_) => { - try_or_throw!(self.machine_st, self.machine_st.keysort()); + &Instruction::CallKeySort => { + try_or_throw!(self.machine_st, self.machine_st.keysort(VarComparison::Distinct)); if self.machine_st.fail { self.machine_st.backtrack(); @@ -1858,8 +1895,8 @@ impl Machine { self.machine_st.p += 1; } } - &Instruction::ExecuteKeySort(_) => { - try_or_throw!(self.machine_st, self.machine_st.keysort()); + &Instruction::ExecuteKeySort => { + try_or_throw!(self.machine_st, self.machine_st.keysort(VarComparison::Distinct)); if self.machine_st.fail { self.machine_st.backtrack(); @@ -1872,7 +1909,35 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::CallIs(r, at, _) => { + &Instruction::CallKeySortWithConstantVarOrdering => { + try_or_throw!(self.machine_st, self.machine_st.keysort(VarComparison::Indistinct)); + + if self.machine_st.fail { + self.machine_st.backtrack(); + } else { + try_or_throw!( + self.machine_st, + (self.machine_st.increment_call_count_fn)(&mut self.machine_st) + ); + + self.machine_st.p += 1; + } + } + &Instruction::ExecuteKeySortWithConstantVarOrdering => { + try_or_throw!(self.machine_st, self.machine_st.keysort(VarComparison::Indistinct)); + + if self.machine_st.fail { + self.machine_st.backtrack(); + } else { + try_or_throw!( + self.machine_st, + (self.machine_st.increment_call_count_fn)(&mut self.machine_st) + ); + + self.machine_st.p = self.machine_st.cp; + } + } + &Instruction::CallIs(r, at) => { try_or_throw!(self.machine_st, self.machine_st.is(r, at)); if self.machine_st.fail { @@ -1886,7 +1951,7 @@ impl Machine { self.machine_st.p += 1; } } - &Instruction::ExecuteIs(r, at, _) => { + &Instruction::ExecuteIs(r, at) => { try_or_throw!(self.machine_st, self.machine_st.is(r, at)); if self.machine_st.fail { @@ -1900,7 +1965,7 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::CallN(arity, _) => { + &Instruction::CallN(arity) => { let pred = self.machine_st.registers[1]; for i in 2..arity + 1 { @@ -1923,7 +1988,7 @@ impl Machine { ); } } - &Instruction::ExecuteN(arity, _) => { + &Instruction::ExecuteN(arity) => { let pred = self.machine_st.registers[1]; for i in 2..arity + 1 { @@ -1946,7 +2011,7 @@ impl Machine { ); } } - &Instruction::DefaultCallN(arity, _) => { + &Instruction::DefaultCallN(arity) => { let pred = self.machine_st.registers[1]; for i in 2..arity + 1 { @@ -1964,7 +2029,7 @@ impl Machine { self.machine_st.backtrack(); } } - &Instruction::DefaultExecuteN(arity, _) => { + &Instruction::DefaultExecuteN(arity) => { let pred = self.machine_st.registers[1]; for i in 2..arity + 1 { @@ -1982,7 +2047,7 @@ impl Machine { self.machine_st.backtrack(); } } - &Instruction::CallNumberLessThanOrEqual(ref at_1, ref at_2, _) => { + &Instruction::CallNumberLessThanOrEqual(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2000,7 +2065,7 @@ impl Machine { } } } - &Instruction::ExecuteNumberLessThanOrEqual(ref at_1, ref at_2, _) => { + &Instruction::ExecuteNumberLessThanOrEqual(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2018,7 +2083,7 @@ impl Machine { } } } - &Instruction::CallNumberEqual(ref at_1, ref at_2, _) => { + &Instruction::CallNumberEqual(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2036,7 +2101,7 @@ impl Machine { } } } - &Instruction::ExecuteNumberEqual(ref at_1, ref at_2, _) => { + &Instruction::ExecuteNumberEqual(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2054,7 +2119,7 @@ impl Machine { } } } - &Instruction::CallNumberNotEqual(ref at_1, ref at_2, _) => { + &Instruction::CallNumberNotEqual(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2072,7 +2137,7 @@ impl Machine { } } } - &Instruction::ExecuteNumberNotEqual(ref at_1, ref at_2, _) => { + &Instruction::ExecuteNumberNotEqual(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2090,7 +2155,7 @@ impl Machine { } } } - &Instruction::CallNumberGreaterThanOrEqual(ref at_1, ref at_2, _) => { + &Instruction::CallNumberGreaterThanOrEqual(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2108,7 +2173,7 @@ impl Machine { } } } - &Instruction::ExecuteNumberGreaterThanOrEqual(ref at_1, ref at_2, _) => { + &Instruction::ExecuteNumberGreaterThanOrEqual(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2126,7 +2191,7 @@ impl Machine { } } } - &Instruction::CallNumberGreaterThan(ref at_1, ref at_2, _) => { + &Instruction::CallNumberGreaterThan(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2144,7 +2209,7 @@ impl Machine { } } } - &Instruction::ExecuteNumberGreaterThan(ref at_1, ref at_2, _) => { + &Instruction::ExecuteNumberGreaterThan(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2162,7 +2227,7 @@ impl Machine { } } } - &Instruction::CallNumberLessThan(ref at_1, ref at_2, _) => { + &Instruction::CallNumberLessThan(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2180,7 +2245,7 @@ impl Machine { } } } - &Instruction::ExecuteNumberLessThan(ref at_1, ref at_2, _) => { + &Instruction::ExecuteNumberLessThan(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2198,7 +2263,7 @@ impl Machine { } } } - &Instruction::DefaultCallNumberLessThanOrEqual(ref at_1, ref at_2, _) => { + &Instruction::DefaultCallNumberLessThanOrEqual(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2211,7 +2276,7 @@ impl Machine { } } } - &Instruction::DefaultExecuteNumberLessThanOrEqual(ref at_1, ref at_2, _) => { + &Instruction::DefaultExecuteNumberLessThanOrEqual(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2224,7 +2289,7 @@ impl Machine { } } } - &Instruction::DefaultCallNumberNotEqual(ref at_1, ref at_2, _) => { + &Instruction::DefaultCallNumberNotEqual(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2237,7 +2302,7 @@ impl Machine { } } } - &Instruction::DefaultExecuteNumberNotEqual(ref at_1, ref at_2, _) => { + &Instruction::DefaultExecuteNumberNotEqual(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2250,7 +2315,7 @@ impl Machine { } } } - &Instruction::DefaultCallNumberEqual(ref at_1, ref at_2, _) => { + &Instruction::DefaultCallNumberEqual(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2263,7 +2328,7 @@ impl Machine { } } } - &Instruction::DefaultExecuteNumberEqual(ref at_1, ref at_2, _) => { + &Instruction::DefaultExecuteNumberEqual(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2276,7 +2341,7 @@ impl Machine { } } } - &Instruction::DefaultCallNumberGreaterThanOrEqual(ref at_1, ref at_2, _) => { + &Instruction::DefaultCallNumberGreaterThanOrEqual(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2289,7 +2354,7 @@ impl Machine { } } } - &Instruction::DefaultExecuteNumberGreaterThanOrEqual(ref at_1, ref at_2, _) => { + &Instruction::DefaultExecuteNumberGreaterThanOrEqual(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2302,7 +2367,7 @@ impl Machine { } } } - &Instruction::DefaultCallNumberGreaterThan(ref at_1, ref at_2, _) => { + &Instruction::DefaultCallNumberGreaterThan(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2315,7 +2380,7 @@ impl Machine { } } } - &Instruction::DefaultExecuteNumberGreaterThan(ref at_1, ref at_2, _) => { + &Instruction::DefaultExecuteNumberGreaterThan(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2328,7 +2393,7 @@ impl Machine { } } } - &Instruction::DefaultCallNumberLessThan(ref at_1, ref at_2, _) => { + &Instruction::DefaultCallNumberLessThan(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2341,7 +2406,7 @@ impl Machine { } } } - &Instruction::DefaultExecuteNumberLessThan(ref at_1, ref at_2, _) => { + &Instruction::DefaultExecuteNumberLessThan(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2355,7 +2420,7 @@ impl Machine { } } // - &Instruction::CallIsAtom(r, _) => { + &Instruction::CallIsAtom(r) => { let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r])); read_heap_cell!(d, @@ -2384,7 +2449,7 @@ impl Machine { } ); } - &Instruction::ExecuteIsAtom(r, _) => { + &Instruction::ExecuteIsAtom(r) => { let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r])); read_heap_cell!(d, @@ -2413,7 +2478,7 @@ impl Machine { } ); } - &Instruction::CallIsAtomic(r, _) => { + &Instruction::CallIsAtomic(r) => { let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r])); read_heap_cell!(d, @@ -2443,7 +2508,7 @@ impl Machine { } ); } - &Instruction::ExecuteIsAtomic(r, _) => { + &Instruction::ExecuteIsAtomic(r) => { let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r])); read_heap_cell!(d, @@ -2473,7 +2538,7 @@ impl Machine { } ); } - &Instruction::CallIsCompound(r, _) => { + &Instruction::CallIsCompound(r) => { let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r])); read_heap_cell!(d, @@ -2504,7 +2569,7 @@ impl Machine { } ); } - &Instruction::ExecuteIsCompound(r, _) => { + &Instruction::ExecuteIsCompound(r) => { let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r])); read_heap_cell!(d, @@ -2535,7 +2600,7 @@ impl Machine { } ); } - &Instruction::CallIsInteger(r, _) => { + &Instruction::CallIsInteger(r) => { let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r])); match Number::try_from(d) { @@ -2543,7 +2608,7 @@ impl Machine { self.machine_st.p += 1; } Ok(Number::Rational(n)) => { - if n.denom() == &1 { + if n.denominator().is_one() { self.machine_st.p += 1; } else { self.machine_st.backtrack(); @@ -2554,7 +2619,7 @@ impl Machine { } } } - &Instruction::ExecuteIsInteger(r, _) => { + &Instruction::ExecuteIsInteger(r) => { let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r])); match Number::try_from(d) { @@ -2562,7 +2627,7 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } Ok(Number::Rational(n)) => { - if n.denom() == &1 { + if n.denominator().is_one() { self.machine_st.p = self.machine_st.cp; } else { self.machine_st.backtrack(); @@ -2573,7 +2638,7 @@ impl Machine { } } } - &Instruction::CallIsNumber(r, _) => { + &Instruction::CallIsNumber(r) => { let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r])); match Number::try_from(d) { @@ -2585,7 +2650,7 @@ impl Machine { } } } - &Instruction::ExecuteIsNumber(r, _) => { + &Instruction::ExecuteIsNumber(r) => { let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r])); match Number::try_from(d) { @@ -2597,13 +2662,13 @@ impl Machine { } } } - &Instruction::CallIsRational(r, _) => { + &Instruction::CallIsRational(r) => { let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r])); read_heap_cell!(d, (HeapCellValueTag::Cons, ptr) => { match_untyped_arena_ptr!(ptr, - (ArenaHeaderTag::Rational, _r) => { + (ArenaHeaderTag::Rational | ArenaHeaderTag::Integer, _r) => { self.machine_st.p += 1; } _ => { @@ -2611,18 +2676,21 @@ impl Machine { } ); } + (HeapCellValueTag::Fixnum) => { + self.machine_st.p += 1; + } _ => { self.machine_st.backtrack(); } ); } - &Instruction::ExecuteIsRational(r, _) => { + &Instruction::ExecuteIsRational(r) => { let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r])); read_heap_cell!(d, (HeapCellValueTag::Cons, ptr) => { match_untyped_arena_ptr!(ptr, - (ArenaHeaderTag::Rational, _r) => { + (ArenaHeaderTag::Rational | ArenaHeaderTag::Integer, _r) => { self.machine_st.p = self.machine_st.cp; } _ => { @@ -2630,12 +2698,15 @@ impl Machine { } ); } + (HeapCellValueTag::Fixnum) => { + self.machine_st.p = self.machine_st.cp; + } _ => { self.machine_st.backtrack(); } ); } - &Instruction::CallIsFloat(r, _) => { + &Instruction::CallIsFloat(r) => { let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r])); match Number::try_from(d) { @@ -2647,7 +2718,7 @@ impl Machine { } } } - &Instruction::ExecuteIsFloat(r, _) => { + &Instruction::ExecuteIsFloat(r) => { let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r])); match Number::try_from(d) { @@ -2659,7 +2730,7 @@ impl Machine { } } } - &Instruction::CallIsNonVar(r, _) => { + &Instruction::CallIsNonVar(r) => { let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r])); match d.get_tag() { @@ -2673,7 +2744,7 @@ impl Machine { } } } - &Instruction::ExecuteIsNonVar(r, _) => { + &Instruction::ExecuteIsNonVar(r) => { let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r])); match d.get_tag() { @@ -2687,7 +2758,7 @@ impl Machine { } } } - &Instruction::CallIsVar(r, _) => { + &Instruction::CallIsVar(r) => { let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r])); match d.get_tag() { @@ -2701,7 +2772,7 @@ impl Machine { } } } - &Instruction::ExecuteIsVar(r, _) => { + &Instruction::ExecuteIsVar(r) => { let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r])); match d.get_tag() { @@ -2715,7 +2786,7 @@ impl Machine { } } } - &Instruction::CallNamed(arity, name, ref idx, _) => { + &Instruction::CallNamed(arity, name, ref idx) => { let idx = idx.get(); try_or_throw!( @@ -2732,7 +2803,7 @@ impl Machine { ); } } - &Instruction::ExecuteNamed(arity, name, ref idx, _) => { + &Instruction::ExecuteNamed(arity, name, ref idx) => { let idx = idx.get(); try_or_throw!( @@ -2749,7 +2820,7 @@ impl Machine { ); } } - &Instruction::DefaultCallNamed(arity, name, ref idx, _) => { + &Instruction::DefaultCallNamed(arity, name, ref idx) => { let idx = idx.get(); try_or_throw!( @@ -2761,7 +2832,7 @@ impl Machine { self.machine_st.backtrack(); } } - &Instruction::DefaultExecuteNamed(arity, name, ref idx, _) => { + &Instruction::DefaultExecuteNamed(arity, name, ref idx) => { let idx = idx.get(); try_or_throw!( @@ -2776,15 +2847,7 @@ impl Machine { &Instruction::Deallocate => { self.machine_st.deallocate() } - &Instruction::JmpByCall(arity, offset, _) => { - self.machine_st.num_of_args = arity; - self.machine_st.b0 = self.machine_st.b; - self.machine_st.cp = self.machine_st.p + 1; - self.machine_st.p += offset; - } - &Instruction::JmpByExecute(arity, offset, _) => { - self.machine_st.num_of_args = arity; - self.machine_st.b0 = self.machine_st.b; + &Instruction::JmpByCall(offset) => { self.machine_st.p += offset; } &Instruction::RevJmpBy(offset) => { @@ -2884,7 +2947,7 @@ impl Machine { step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::GetStructure(name, arity, reg) => { + &Instruction::GetStructure(_lvl, name, arity, reg) => { let deref_v = self.machine_st.deref(self.machine_st[reg]); let store_v = self.machine_st.store(deref_v); @@ -3081,7 +3144,7 @@ impl Machine { IndexingLine::IndexedChoice(ref indexed_choice) => { match &indexed_choice[self.machine_st.iip as usize] { &IndexedChoiceInstruction::Try(offset) => { - self.machine_st.indexed_try(offset); + self.indexed_try(offset); } &IndexedChoiceInstruction::Retry(l) => { self.retry(l); @@ -3127,7 +3190,7 @@ impl Machine { fixnum_as_cell!(Fixnum::build_with(self.machine_st.cc as i64)); self.machine_st.num_of_args += 1; - self.machine_st.indexed_try(offset); + self.indexed_try(offset); self.machine_st.num_of_args -= 1; } None => { @@ -3143,7 +3206,6 @@ impl Machine { .stack .index_or_frame(b) .prelude - .univ_prelude .num_cells; self.machine_st.cc = cell_as_fixnum!( @@ -3233,8 +3295,8 @@ impl Machine { self.machine_st.p += 1; } - &Instruction::PutUnsafeValue(n, arg) => { - let s = stack_loc!(AndFrame, self.machine_st.e, n); + &Instruction::PutUnsafeValue(perm_slot, arg) => { + let s = stack_loc!(AndFrame, self.machine_st.e, perm_slot); let addr = self.machine_st.store(self.machine_st.deref(stack_loc_as_cell!(s))); if addr.is_protected(self.machine_st.e) { @@ -3312,11 +3374,11 @@ impl Machine { self.machine_st.p += 1; } // - &Instruction::CallAtomChars(_) => { + &Instruction::CallAtomChars => { self.atom_chars(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteAtomChars(_) => { + &Instruction::ExecuteAtomChars => { self.atom_chars(); if self.machine_st.fail { @@ -3325,7 +3387,7 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::CallAtomCodes(_) => { + &Instruction::CallAtomCodes => { try_or_throw!(self.machine_st, self.atom_codes()); if self.machine_st.fail { @@ -3334,7 +3396,7 @@ impl Machine { self.machine_st.p += 1; } } - &Instruction::ExecuteAtomCodes(_) => { + &Instruction::ExecuteAtomCodes => { try_or_throw!(self.machine_st, self.atom_codes()); if self.machine_st.fail { @@ -3343,245 +3405,237 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::CallAtomLength(_) => { + &Instruction::CallAtomLength => { self.atom_length(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteAtomLength(_) => { + &Instruction::ExecuteAtomLength => { self.atom_length(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallBindFromRegister(_) => { + &Instruction::CallBindFromRegister => { self.bind_from_register(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteBindFromRegister(_) => { + &Instruction::ExecuteBindFromRegister => { self.bind_from_register(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallContinuation(_) => { + &Instruction::CallContinuation => { try_or_throw!(self.machine_st, self.call_continuation(false)); } - &Instruction::ExecuteContinuation(_) => { + &Instruction::ExecuteContinuation => { try_or_throw!(self.machine_st, self.call_continuation(true)); } - &Instruction::CallCharCode(_) => { + &Instruction::CallCharCode => { try_or_throw!(self.machine_st, self.char_code()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCharCode(_) => { + &Instruction::ExecuteCharCode => { try_or_throw!(self.machine_st, self.char_code()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCharType(_) => { + &Instruction::CallCharType => { self.char_type(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCharType(_) => { + &Instruction::ExecuteCharType => { self.char_type(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCharsToNumber(_) => { + &Instruction::CallCharsToNumber => { try_or_throw!(self.machine_st, self.chars_to_number()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCharsToNumber(_) => { + &Instruction::ExecuteCharsToNumber => { try_or_throw!(self.machine_st, self.chars_to_number()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCodesToNumber(_) => { + &Instruction::CallCodesToNumber => { try_or_throw!(self.machine_st, self.codes_to_number()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCodesToNumber(_) => { + &Instruction::ExecuteCodesToNumber => { try_or_throw!(self.machine_st, self.codes_to_number()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCopyTermWithoutAttrVars(_) => { + &Instruction::CallCopyTermWithoutAttrVars => { self.copy_term_without_attr_vars(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCopyTermWithoutAttrVars(_) => { + &Instruction::ExecuteCopyTermWithoutAttrVars => { self.copy_term_without_attr_vars(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCheckCutPoint(_) => { + &Instruction::CallCheckCutPoint => { self.check_cut_point(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCheckCutPoint(_) => { + &Instruction::ExecuteCheckCutPoint => { self.check_cut_point(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallClose(_) => { + &Instruction::CallClose => { try_or_throw!(self.machine_st, self.close()); self.machine_st.p += 1; } - &Instruction::ExecuteClose(_) => { + &Instruction::ExecuteClose => { try_or_throw!(self.machine_st, self.close()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallCopyToLiftedHeap(_) => { + &Instruction::CallCopyToLiftedHeap => { self.copy_to_lifted_heap(); self.machine_st.p += 1; } - &Instruction::ExecuteCopyToLiftedHeap(_) => { + &Instruction::ExecuteCopyToLiftedHeap => { self.copy_to_lifted_heap(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallCreatePartialString(_) => { + &Instruction::CallCreatePartialString => { self.create_partial_string(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCreatePartialString(_) => { + &Instruction::ExecuteCreatePartialString => { self.create_partial_string(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCurrentHostname(_) => { + &Instruction::CallCurrentHostname => { self.current_hostname(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCurrentHostname(_) => { + &Instruction::ExecuteCurrentHostname => { self.current_hostname(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCurrentInput(_) => { + &Instruction::CallCurrentInput => { try_or_throw!(self.machine_st, self.current_input()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCurrentInput(_) => { + &Instruction::ExecuteCurrentInput => { try_or_throw!(self.machine_st, self.current_input()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCurrentOutput(_) => { + &Instruction::CallCurrentOutput => { try_or_throw!(self.machine_st, self.current_output()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCurrentOutput(_) => { + &Instruction::ExecuteCurrentOutput => { try_or_throw!(self.machine_st, self.current_output()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallDirectoryFiles(_) => { + &Instruction::CallDirectoryFiles => { try_or_throw!(self.machine_st, self.directory_files()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteDirectoryFiles(_) => { + &Instruction::ExecuteDirectoryFiles => { try_or_throw!(self.machine_st, self.directory_files()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallFileSize(_) => { + &Instruction::CallFileSize => { self.file_size(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteFileSize(_) => { + &Instruction::ExecuteFileSize => { self.file_size(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallFileExists(_) => { + &Instruction::CallFileExists => { self.file_exists(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteFileExists(_) => { + &Instruction::ExecuteFileExists => { self.file_exists(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallDirectoryExists(_) => { + &Instruction::CallDirectoryExists => { self.directory_exists(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteDirectoryExists(_) => { + &Instruction::ExecuteDirectoryExists => { self.directory_exists(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallDirectorySeparator(_) => { + &Instruction::CallDirectorySeparator => { self.directory_separator(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteDirectorySeparator(_) => { + &Instruction::ExecuteDirectorySeparator => { self.directory_separator(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallMakeDirectory(_) => { + &Instruction::CallMakeDirectory => { self.make_directory(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteMakeDirectory(_) => { + &Instruction::ExecuteMakeDirectory => { self.make_directory(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallMakeDirectoryPath(_) => { + &Instruction::CallMakeDirectoryPath => { self.make_directory_path(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteMakeDirectoryPath(_) => { + &Instruction::ExecuteMakeDirectoryPath => { self.make_directory_path(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallDeleteFile(_) => { + &Instruction::CallDeleteFile => { self.delete_file(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteDeleteFile(_) => { + &Instruction::ExecuteDeleteFile => { self.delete_file(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallRenameFile(_) => { + &Instruction::CallRenameFile => { self.rename_file(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteRenameFile(_) => { + &Instruction::ExecuteRenameFile => { self.rename_file(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallWorkingDirectory(_) => { + &Instruction::CallFileCopy => { + self.file_copy(); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteFileCopy => { + self.file_copy(); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } + &Instruction::CallWorkingDirectory => { try_or_throw!(self.machine_st, self.working_directory()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteWorkingDirectory(_) => { + &Instruction::ExecuteWorkingDirectory => { try_or_throw!(self.machine_st, self.working_directory()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallDeleteDirectory(_) => { + &Instruction::CallDeleteDirectory => { self.delete_directory(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteDeleteDirectory(_) => { + &Instruction::ExecuteDeleteDirectory => { self.delete_directory(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallPathCanonical(_) => { + &Instruction::CallPathCanonical => { try_or_throw!(self.machine_st, self.path_canonical()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecutePathCanonical(_) => { + &Instruction::ExecutePathCanonical => { try_or_throw!(self.machine_st, self.path_canonical()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallFileTime(_) => { + &Instruction::CallFileTime => { self.file_time(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteFileTime(_) => { + &Instruction::ExecuteFileTime => { self.file_time(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallDeleteAttribute(_) => { - self.delete_attribute(); - self.machine_st.p += 1; - } - &Instruction::ExecuteDeleteAttribute(_) => { - self.delete_attribute(); - self.machine_st.p = self.machine_st.cp; - } - &Instruction::CallDeleteHeadAttribute(_) => { - self.delete_head_attribute(); - self.machine_st.p += 1; - } - &Instruction::ExecuteDeleteHeadAttribute(_) => { - self.delete_head_attribute(); - self.machine_st.p = self.machine_st.cp; - } - &Instruction::CallDynamicModuleResolution(arity, _) => { + &Instruction::CallDynamicModuleResolution(arity) => { let (module_name, key) = try_or_throw!( self.machine_st, self.dynamic_module_resolution(arity - 2) @@ -3596,7 +3650,7 @@ impl Machine { self.machine_st.backtrack(); } } - &Instruction::ExecuteDynamicModuleResolution(arity, _) => { + &Instruction::ExecuteDynamicModuleResolution(arity) => { let (module_name, key) = try_or_throw!( self.machine_st, self.dynamic_module_resolution(arity - 2) @@ -3611,444 +3665,423 @@ impl Machine { self.machine_st.backtrack(); } } - &Instruction::CallEnqueueAttributedVar(_) => { - self.enqueue_attributed_var(); - self.machine_st.p += 1; - } - &Instruction::ExecuteEnqueueAttributedVar(_) => { - self.enqueue_attributed_var(); - self.machine_st.p = self.machine_st.cp; - } - &Instruction::CallFetchGlobalVar(_) => { + &Instruction::CallFetchGlobalVar => { self.fetch_global_var(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteFetchGlobalVar(_) => { + &Instruction::ExecuteFetchGlobalVar => { self.fetch_global_var(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallFirstStream(_) => { + &Instruction::CallFirstStream => { self.first_stream(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteFirstStream(_) => { + &Instruction::ExecuteFirstStream => { self.first_stream(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallFlushOutput(_) => { + &Instruction::CallFlushOutput => { try_or_throw!(self.machine_st, self.flush_output()); self.machine_st.p += 1; } - &Instruction::ExecuteFlushOutput(_) => { + &Instruction::ExecuteFlushOutput => { try_or_throw!(self.machine_st, self.flush_output()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallGetByte(_) => { + &Instruction::CallGetByte => { try_or_throw!(self.machine_st, self.get_byte()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetByte(_) => { + &Instruction::ExecuteGetByte => { try_or_throw!(self.machine_st, self.get_byte()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallGetChar(_) => { + &Instruction::CallGetChar => { try_or_throw!(self.machine_st, self.get_char()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetChar(_) => { + &Instruction::ExecuteGetChar => { try_or_throw!(self.machine_st, self.get_char()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallGetNChars(_) => { + &Instruction::CallGetNChars => { try_or_throw!(self.machine_st, self.get_n_chars()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetNChars(_) => { + &Instruction::ExecuteGetNChars => { try_or_throw!(self.machine_st, self.get_n_chars()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallGetCode(_) => { + &Instruction::CallGetCode => { try_or_throw!(self.machine_st, self.get_code()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetCode(_) => { + &Instruction::ExecuteGetCode => { try_or_throw!(self.machine_st, self.get_code()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallGetSingleChar(_) => { + &Instruction::CallGetSingleChar => { try_or_throw!(self.machine_st, self.get_single_char()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetSingleChar(_) => { + &Instruction::ExecuteGetSingleChar => { try_or_throw!(self.machine_st, self.get_single_char()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallResetAttrVarState(_) => { - self.reset_attr_var_state(); - self.machine_st.p += 1; - } - &Instruction::ExecuteResetAttrVarState(_) => { - self.reset_attr_var_state(); - self.machine_st.p = self.machine_st.cp; - } - &Instruction::CallTruncateIfNoLiftedHeapGrowthDiff(_) => { + &Instruction::CallTruncateIfNoLiftedHeapGrowthDiff => { self.truncate_if_no_lifted_heap_growth_diff(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteTruncateIfNoLiftedHeapGrowthDiff(_) => { + &Instruction::ExecuteTruncateIfNoLiftedHeapGrowthDiff => { self.truncate_if_no_lifted_heap_growth_diff(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallTruncateIfNoLiftedHeapGrowth(_) => { + &Instruction::CallTruncateIfNoLiftedHeapGrowth => { self.truncate_if_no_lifted_heap_growth(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteTruncateIfNoLiftedHeapGrowth(_) => { + &Instruction::ExecuteTruncateIfNoLiftedHeapGrowth => { self.truncate_if_no_lifted_heap_growth(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallGetAttributedVariableList(_) => { + &Instruction::CallGetAttributedVariableList => { self.get_attributed_variable_list(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetAttributedVariableList(_) => { + &Instruction::ExecuteGetAttributedVariableList => { self.get_attributed_variable_list(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallGetAttrVarQueueDelimiter(_) => { + &Instruction::CallGetAttrVarQueueDelimiter => { self.get_attr_var_queue_delimiter(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetAttrVarQueueDelimiter(_) => { + &Instruction::ExecuteGetAttrVarQueueDelimiter => { self.get_attr_var_queue_delimiter(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallGetAttrVarQueueBeyond(_) => { + &Instruction::CallGetAttrVarQueueBeyond => { self.get_attr_var_queue_beyond(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetAttrVarQueueBeyond(_) => { + &Instruction::ExecuteGetAttrVarQueueBeyond => { self.get_attr_var_queue_beyond(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallGetBValue(_) => { + &Instruction::CallGetBValue => { self.get_b_value(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetBValue(_) => { + &Instruction::ExecuteGetBValue => { self.get_b_value(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallGetContinuationChunk(_) => { + &Instruction::CallGetContinuationChunk => { self.get_continuation_chunk(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetContinuationChunk(_) => { + &Instruction::ExecuteGetContinuationChunk => { self.get_continuation_chunk(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallGetNextDBRef(_) => { - self.get_next_db_ref(); + &Instruction::CallLookupDBRef => { + self.lookup_db_ref(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetNextDBRef(_) => { - self.get_next_db_ref(); + &Instruction::ExecuteLookupDBRef => { + self.lookup_db_ref(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallGetNextOpDBRef(_) => { + &Instruction::CallGetNextOpDBRef => { self.get_next_op_db_ref(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetNextOpDBRef(_) => { + &Instruction::ExecuteGetNextOpDBRef => { self.get_next_op_db_ref(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallIsPartialString(_) => { + &Instruction::CallIsPartialString => { self.is_partial_string(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteIsPartialString(_) => { + &Instruction::ExecuteIsPartialString => { self.is_partial_string(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallHalt(_) => { - self.halt(); - self.machine_st.p += 1; + &Instruction::CallHalt | &Instruction::ExecuteHalt => { + return self.halt(); } - &Instruction::ExecuteHalt(_) => { - self.halt(); - self.machine_st.p = self.machine_st.cp; - } - &Instruction::CallGetLiftedHeapFromOffset(_) => { + &Instruction::CallGetLiftedHeapFromOffset => { self.get_lifted_heap_from_offset(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetLiftedHeapFromOffset(_) => { + &Instruction::ExecuteGetLiftedHeapFromOffset => { self.get_lifted_heap_from_offset(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallGetLiftedHeapFromOffsetDiff(_) => { + &Instruction::CallGetLiftedHeapFromOffsetDiff => { self.get_lifted_heap_from_offset_diff(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetLiftedHeapFromOffsetDiff(_) => { + &Instruction::ExecuteGetLiftedHeapFromOffsetDiff => { self.get_lifted_heap_from_offset_diff(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallGetSCCCleaner(_) => { + &Instruction::CallGetSCCCleaner => { self.get_scc_cleaner(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetSCCCleaner(_) => { + &Instruction::ExecuteGetSCCCleaner => { self.get_scc_cleaner(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallHeadIsDynamic(_) => { + &Instruction::CallHeadIsDynamic => { self.head_is_dynamic(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteHeadIsDynamic(_) => { + &Instruction::ExecuteHeadIsDynamic => { self.head_is_dynamic(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallInstallSCCCleaner(_) => { + &Instruction::CallInstallSCCCleaner => { self.install_scc_cleaner(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteInstallSCCCleaner(_) => { + &Instruction::ExecuteInstallSCCCleaner => { self.install_scc_cleaner(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallInstallInferenceCounter(_) => { + &Instruction::CallInstallInferenceCounter => { try_or_throw!(self.machine_st, self.install_inference_counter()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteInstallInferenceCounter(_) => { + &Instruction::ExecuteInstallInferenceCounter => { try_or_throw!(self.machine_st, self.install_inference_counter()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallLiftedHeapLength(_) => { + &Instruction::CallLiftedHeapLength => { self.lifted_heap_length(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteLiftedHeapLength(_) => { + &Instruction::ExecuteLiftedHeapLength => { self.lifted_heap_length(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallLoadLibraryAsStream(_) => { + &Instruction::CallLoadLibraryAsStream => { try_or_throw!(self.machine_st, self.load_library_as_stream()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteLoadLibraryAsStream(_) => { + &Instruction::ExecuteLoadLibraryAsStream => { try_or_throw!(self.machine_st, self.load_library_as_stream()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallModuleExists(_) => { + &Instruction::CallModuleExists => { self.module_exists(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteModuleExists(_) => { + &Instruction::ExecuteModuleExists => { self.module_exists(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallNextEP(_) => { + &Instruction::CallNextEP => { self.next_ep(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteNextEP(_) => { + &Instruction::ExecuteNextEP => { self.next_ep(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallNoSuchPredicate(_) => { + &Instruction::CallNoSuchPredicate => { try_or_throw!(self.machine_st, self.no_such_predicate()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteNoSuchPredicate(_) => { + &Instruction::ExecuteNoSuchPredicate => { try_or_throw!(self.machine_st, self.no_such_predicate()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallNumberToChars(_) => { + &Instruction::CallNumberToChars => { self.number_to_chars(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteNumberToChars(_) => { + &Instruction::ExecuteNumberToChars => { self.number_to_chars(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallNumberToCodes(_) => { + &Instruction::CallNumberToCodes => { self.number_to_codes(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteNumberToCodes(_) => { + &Instruction::ExecuteNumberToCodes => { self.number_to_codes(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallOpDeclaration(_) => { + &Instruction::CallOpDeclaration => { try_or_throw!(self.machine_st, self.op_declaration()); self.machine_st.p += 1; } - &Instruction::ExecuteOpDeclaration(_) => { + &Instruction::ExecuteOpDeclaration => { try_or_throw!(self.machine_st, self.op_declaration()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallOpen(_) => { + &Instruction::CallOpen => { try_or_throw!(self.machine_st, self.open()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteOpen(_) => { + &Instruction::ExecuteOpen => { try_or_throw!(self.machine_st, self.open()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallSetStreamOptions(_) => { + &Instruction::CallSetStreamOptions => { try_or_throw!(self.machine_st, self.set_stream_options()); self.machine_st.p += 1; } - &Instruction::ExecuteSetStreamOptions(_) => { + &Instruction::ExecuteSetStreamOptions => { try_or_throw!(self.machine_st, self.set_stream_options()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallNextStream(_) => { + &Instruction::CallNextStream => { self.next_stream(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteNextStream(_) => { + &Instruction::ExecuteNextStream => { self.next_stream(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallPartialStringTail(_) => { + &Instruction::CallPartialStringTail => { self.partial_string_tail(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecutePartialStringTail(_) => { + &Instruction::ExecutePartialStringTail => { self.partial_string_tail(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallPeekByte(_) => { + &Instruction::CallPeekByte => { try_or_throw!(self.machine_st, self.peek_byte()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecutePeekByte(_) => { + &Instruction::ExecutePeekByte => { try_or_throw!(self.machine_st, self.peek_byte()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallPeekChar(_) => { + &Instruction::CallPeekChar => { try_or_throw!(self.machine_st, self.peek_char()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecutePeekChar(_) => { + &Instruction::ExecutePeekChar => { try_or_throw!(self.machine_st, self.peek_char()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallPeekCode(_) => { + &Instruction::CallPeekCode => { try_or_throw!(self.machine_st, self.peek_code()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecutePeekCode(_) => { + &Instruction::ExecutePeekCode => { try_or_throw!(self.machine_st, self.peek_code()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallPointsToContinuationResetMarker(_) => { + &Instruction::CallPointsToContinuationResetMarker => { self.points_to_continuation_reset_marker(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecutePointsToContinuationResetMarker(_) => { + &Instruction::ExecutePointsToContinuationResetMarker => { self.points_to_continuation_reset_marker(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallPutByte(_) => { + &Instruction::CallPutByte => { try_or_throw!(self.machine_st, self.put_byte()); self.machine_st.p += 1; } - &Instruction::ExecutePutByte(_) => { + &Instruction::ExecutePutByte => { try_or_throw!(self.machine_st, self.put_byte()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallPutChar(_) => { + &Instruction::CallPutChar => { try_or_throw!(self.machine_st, self.put_char()); self.machine_st.p += 1; } - &Instruction::ExecutePutChar(_) => { + &Instruction::ExecutePutChar => { try_or_throw!(self.machine_st, self.put_char()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallPutChars(_) => { + &Instruction::CallPutChars => { try_or_throw!(self.machine_st, self.put_chars()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecutePutChars(_) => { + &Instruction::ExecutePutChars => { try_or_throw!(self.machine_st, self.put_chars()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallPutCode(_) => { + &Instruction::CallPutCode => { try_or_throw!(self.machine_st, self.put_code()); self.machine_st.p += 1; } - &Instruction::ExecutePutCode(_) => { + &Instruction::ExecutePutCode => { try_or_throw!(self.machine_st, self.put_code()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallReadQueryTerm(_) => { + &Instruction::CallReadQueryTerm => { try_or_throw!(self.machine_st, self.read_query_term()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteReadQueryTerm(_) => { + &Instruction::ExecuteReadQueryTerm => { try_or_throw!(self.machine_st, self.read_query_term()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallReadTerm(_) => { + &Instruction::CallReadTerm => { try_or_throw!(self.machine_st, self.read_term()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteReadTerm(_) => { + &Instruction::ExecuteReadTerm => { try_or_throw!(self.machine_st, self.read_term()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallRedoAttrVarBinding(_) => { + &Instruction::CallRedoAttrVarBinding => { self.redo_attr_var_binding(); self.machine_st.p += 1; } - &Instruction::ExecuteRedoAttrVarBinding(_) => { + &Instruction::ExecuteRedoAttrVarBinding => { self.redo_attr_var_binding(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallRemoveCallPolicyCheck(_) => { + &Instruction::CallRemoveCallPolicyCheck => { self.remove_call_policy_check(); self.machine_st.p += 1; } - &Instruction::ExecuteRemoveCallPolicyCheck(_) => { + &Instruction::ExecuteRemoveCallPolicyCheck => { self.remove_call_policy_check(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallRemoveInferenceCounter(_) => { + &Instruction::CallRemoveInferenceCounter => { self.remove_inference_counter(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteRemoveInferenceCounter(_) => { + &Instruction::ExecuteRemoveInferenceCounter => { self.remove_inference_counter(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallResetContinuationMarker(_) => { + &Instruction::CallResetContinuationMarker => { self.reset_continuation_marker(); self.machine_st.p += 1; } - &Instruction::ExecuteResetContinuationMarker(_) => { + &Instruction::ExecuteResetContinuationMarker => { self.reset_continuation_marker(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallRestoreCutPolicy(_) => { + &Instruction::CallRestoreCutPolicy => { self.restore_cut_policy(); self.machine_st.p += 1; } - &Instruction::ExecuteRestoreCutPolicy(_) => { + &Instruction::ExecuteRestoreCutPolicy => { self.restore_cut_policy(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallSetCutPoint(r, _) => { + &Instruction::CallSetCutPoint(r) => { if !self.set_cut_point(r) { step_or_fail!(self, self.machine_st.p += 1); } } - &Instruction::ExecuteSetCutPoint(r, _) => { + &Instruction::ExecuteSetCutPoint(r) => { let cp = self.machine_st.cp; if !self.set_cut_point(r) { @@ -4061,1015 +4094,1047 @@ impl Machine { self.machine_st.cp = cp; } } - &Instruction::CallSetInput(_) => { + &Instruction::CallSetInput => { try_or_throw!(self.machine_st, self.set_input()); self.machine_st.p += 1; } - &Instruction::ExecuteSetInput(_) => { + &Instruction::ExecuteSetInput => { try_or_throw!(self.machine_st, self.set_input()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallSetOutput(_) => { + &Instruction::CallSetOutput => { try_or_throw!(self.machine_st, self.set_output()); self.machine_st.p += 1; } - &Instruction::ExecuteSetOutput(_) => { + &Instruction::ExecuteSetOutput => { try_or_throw!(self.machine_st, self.set_output()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallStoreBacktrackableGlobalVar(_) => { + &Instruction::CallStoreBacktrackableGlobalVar => { self.store_backtrackable_global_var(); self.machine_st.p += 1; } - &Instruction::ExecuteStoreBacktrackableGlobalVar(_) => { + &Instruction::ExecuteStoreBacktrackableGlobalVar => { self.store_backtrackable_global_var(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallStoreGlobalVar(_) => { + &Instruction::CallStoreGlobalVar => { self.store_global_var(); self.machine_st.p += 1; } - &Instruction::ExecuteStoreGlobalVar(_) => { + &Instruction::ExecuteStoreGlobalVar => { self.store_global_var(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallStreamProperty(_) => { + &Instruction::CallStreamProperty => { try_or_throw!(self.machine_st, self.stream_property()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteStreamProperty(_) => { + &Instruction::ExecuteStreamProperty => { try_or_throw!(self.machine_st, self.stream_property()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallSetStreamPosition(_) => { + &Instruction::CallSetStreamPosition => { try_or_throw!(self.machine_st, self.set_stream_position()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteSetStreamPosition(_) => { + &Instruction::ExecuteSetStreamPosition => { try_or_throw!(self.machine_st, self.set_stream_position()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallInferenceLevel(_) => { + &Instruction::CallInferenceLevel => { self.inference_level(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteInferenceLevel(_) => { + &Instruction::ExecuteInferenceLevel => { self.inference_level(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCleanUpBlock(_) => { + &Instruction::CallCleanUpBlock => { self.clean_up_block(); self.machine_st.p += 1; } - &Instruction::ExecuteCleanUpBlock(_) => { + &Instruction::ExecuteCleanUpBlock => { self.clean_up_block(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallFail(_) | &Instruction::ExecuteFail(_) => { + &Instruction::CallFail | &Instruction::ExecuteFail => { self.machine_st.backtrack(); } - &Instruction::CallGetBall(_) => { + &Instruction::CallGetBall => { self.get_ball(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetBall(_) => { + &Instruction::ExecuteGetBall => { self.get_ball(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallGetCurrentBlock(_) => { + &Instruction::CallGetCurrentBlock => { self.get_current_block(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetCurrentBlock(_) => { + &Instruction::ExecuteGetCurrentBlock => { self.get_current_block(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallGetCutPoint(_) => { + &Instruction::CallGetCurrentSCCBlock => { + self.get_current_scc_block(); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteGetCurrentSCCBlock => { + self.get_current_scc_block(); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } + &Instruction::CallGetCutPoint => { self.get_cut_point(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetCutPoint(_) => { + &Instruction::ExecuteGetCutPoint => { self.get_cut_point(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallGetStaggeredCutPoint(_) => { - self.get_staggered_cut_point(); - step_or_fail!(self, self.machine_st.p += 1); - } - &Instruction::ExecuteGetStaggeredCutPoint(_) => { - self.get_staggered_cut_point(); - step_or_fail!(self, self.machine_st.p = self.machine_st.cp); - } - &Instruction::CallGetDoubleQuotes(_) => { + &Instruction::CallGetDoubleQuotes => { self.get_double_quotes(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetDoubleQuotes(_) => { + &Instruction::ExecuteGetDoubleQuotes => { self.get_double_quotes(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallInstallNewBlock(_) => { + &Instruction::CallGetUnknown => { + self.get_unknown(); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteGetUnknown => { + self.get_unknown(); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } + &Instruction::CallInstallNewBlock => { self.machine_st.install_new_block(self.machine_st.registers[1]); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteInstallNewBlock(_) => { + &Instruction::ExecuteInstallNewBlock => { self.machine_st.install_new_block(self.machine_st.registers[1]); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallMaybe(_) => { + &Instruction::CallMaybe => { self.maybe(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteMaybe(_) => { + &Instruction::ExecuteMaybe => { self.maybe(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCpuNow(_) => { + &Instruction::CallCpuNow => { self.cpu_now(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCpuNow(_) => { + &Instruction::ExecuteCpuNow => { self.cpu_now(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallDeterministicLengthRundown(_) => { + &Instruction::CallDeterministicLengthRundown => { try_or_throw!(self.machine_st, self.det_length_rundown()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteDeterministicLengthRundown(_) => { + &Instruction::ExecuteDeterministicLengthRundown => { try_or_throw!(self.machine_st, self.det_length_rundown()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallHttpOpen(_) => { + &Instruction::CallHttpOpen => { try_or_throw!(self.machine_st, self.http_open()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteHttpOpen(_) => { + &Instruction::ExecuteHttpOpen => { try_or_throw!(self.machine_st, self.http_open()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallHttpListen(_) => { + &Instruction::CallHttpListen => { try_or_throw!(self.machine_st, self.http_listen()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteHttpListen(_) => { + &Instruction::ExecuteHttpListen => { try_or_throw!(self.machine_st, self.http_listen()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallHttpAccept(_) => { + &Instruction::CallHttpAccept => { try_or_throw!(self.machine_st, self.http_accept()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteHttpAccept(_) => { + &Instruction::ExecuteHttpAccept => { try_or_throw!(self.machine_st, self.http_accept()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallHttpAnswer(_) => { + &Instruction::CallHttpAnswer => { try_or_throw!(self.machine_st, self.http_answer()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteHttpAnswer(_) => { + &Instruction::ExecuteHttpAnswer => { try_or_throw!(self.machine_st, self.http_answer()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCurrentTime(_) => { + &Instruction::CallLoadForeignLib => { + try_or_throw!(self.machine_st, self.load_foreign_lib()); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteLoadForeignLib => { + try_or_throw!(self.machine_st, self.load_foreign_lib()); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } + &Instruction::CallForeignCall => { + try_or_throw!(self.machine_st, self.foreign_call()); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteForeignCall => { + try_or_throw!(self.machine_st, self.foreign_call()); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } + &Instruction::CallDefineForeignStruct => { + try_or_throw!(self.machine_st, self.define_foreign_struct()); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteDefineForeignStruct => { + try_or_throw!(self.machine_st, self.define_foreign_struct()); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } + &Instruction::CallCurrentTime => { self.current_time(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCurrentTime(_) => { + &Instruction::ExecuteCurrentTime => { self.current_time(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallQuotedToken(_) => { + &Instruction::CallQuotedToken => { self.quoted_token(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteQuotedToken(_) => { + &Instruction::ExecuteQuotedToken => { self.quoted_token(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallReadTermFromChars(_) => { + &Instruction::CallReadFromChars => { + try_or_throw!(self.machine_st, self.read_from_chars()); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteReadFromChars => { + try_or_throw!(self.machine_st, self.read_from_chars()); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } + &Instruction::CallReadTermFromChars => { try_or_throw!(self.machine_st, self.read_term_from_chars()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteReadTermFromChars(_) => { + &Instruction::ExecuteReadTermFromChars => { try_or_throw!(self.machine_st, self.read_term_from_chars()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallResetBlock(_) => { + &Instruction::CallResetBlock => { self.reset_block(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteResetBlock(_) => { + &Instruction::ExecuteResetBlock => { self.reset_block(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallReturnFromVerifyAttr(_) | - &Instruction::ExecuteReturnFromVerifyAttr(_) => { + &Instruction::CallResetSCCBlock => { + self.reset_scc_block(); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteResetSCCBlock => { + self.reset_scc_block(); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } + &Instruction::CallReturnFromVerifyAttr | + &Instruction::ExecuteReturnFromVerifyAttr => { self.return_from_verify_attr(); } - &Instruction::CallSetBall(_) => { + &Instruction::CallSetBall => { self.set_ball(); self.machine_st.p += 1; } - &Instruction::ExecuteSetBall(_) => { + &Instruction::ExecuteSetBall => { self.set_ball(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallPushBallStack(_) => { + &Instruction::CallPushBallStack => { self.push_ball_stack(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecutePushBallStack(_) => { + &Instruction::ExecutePushBallStack => { self.push_ball_stack(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallPopBallStack(_) => { + &Instruction::CallPopBallStack => { self.pop_ball_stack(); self.machine_st.p += 1; } - &Instruction::ExecutePopBallStack(_) => { + &Instruction::ExecutePopBallStack => { self.pop_ball_stack(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallPopFromBallStack(_) => { + &Instruction::CallPopFromBallStack => { self.pop_from_ball_stack(); self.machine_st.p += 1; } - &Instruction::ExecutePopFromBallStack(_) => { + &Instruction::ExecutePopFromBallStack => { self.pop_from_ball_stack(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallSetCutPointByDefault(r, _) => { + &Instruction::CallSetCutPointByDefault(r) => { self.set_cut_point_by_default(r); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteSetCutPointByDefault(r, _) => { + &Instruction::ExecuteSetCutPointByDefault(r) => { self.set_cut_point_by_default(r); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallSetDoubleQuotes(_) => { + &Instruction::CallSetDoubleQuotes => { self.set_double_quotes(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteSetDoubleQuotes(_) => { + &Instruction::ExecuteSetDoubleQuotes => { self.set_double_quotes(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallSetSeed(_) => { + &Instruction::CallSetUnknown => { + self.set_unknown(); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteSetUnknown => { + self.set_unknown(); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } + &Instruction::CallSetSeed => { self.set_seed(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteSetSeed(_) => { + &Instruction::ExecuteSetSeed => { self.set_seed(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallSkipMaxList(_) => { + &Instruction::CallSkipMaxList => { try_or_throw!(self.machine_st, self.machine_st.skip_max_list()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteSkipMaxList(_) => { + &Instruction::ExecuteSkipMaxList => { try_or_throw!(self.machine_st, self.machine_st.skip_max_list()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallSleep(_) => { + &Instruction::CallSleep => { self.sleep(); self.machine_st.p += 1; } - &Instruction::ExecuteSleep(_) => { + &Instruction::ExecuteSleep => { self.sleep(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallSocketClientOpen(_) => { + &Instruction::CallSocketClientOpen => { try_or_throw!(self.machine_st, self.socket_client_open()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteSocketClientOpen(_) => { + &Instruction::ExecuteSocketClientOpen => { try_or_throw!(self.machine_st, self.socket_client_open()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallSocketServerOpen(_) => { + &Instruction::CallSocketServerOpen => { try_or_throw!(self.machine_st, self.socket_server_open()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteSocketServerOpen(_) => { + &Instruction::ExecuteSocketServerOpen => { try_or_throw!(self.machine_st, self.socket_server_open()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallSocketServerAccept(_) => { + &Instruction::CallSocketServerAccept => { try_or_throw!(self.machine_st, self.socket_server_accept()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteSocketServerAccept(_) => { + &Instruction::ExecuteSocketServerAccept => { try_or_throw!(self.machine_st, self.socket_server_accept()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallSocketServerClose(_) => { + &Instruction::CallSocketServerClose => { try_or_throw!(self.machine_st, self.socket_server_close()); self.machine_st.p += 1; } - &Instruction::ExecuteSocketServerClose(_) => { + &Instruction::ExecuteSocketServerClose => { try_or_throw!(self.machine_st, self.socket_server_close()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallTLSAcceptClient(_) => { + &Instruction::CallTLSAcceptClient => { try_or_throw!(self.machine_st, self.tls_accept_client()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteTLSAcceptClient(_) => { + &Instruction::ExecuteTLSAcceptClient => { try_or_throw!(self.machine_st, self.tls_accept_client()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallTLSClientConnect(_) => { + &Instruction::CallTLSClientConnect => { try_or_throw!(self.machine_st, self.tls_client_connect()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteTLSClientConnect(_) => { + &Instruction::ExecuteTLSClientConnect => { try_or_throw!(self.machine_st, self.tls_client_connect()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallSucceed(_) => { + &Instruction::CallSucceed => { self.machine_st.p += 1; } - &Instruction::ExecuteSucceed(_) => { + &Instruction::ExecuteSucceed => { self.machine_st.p = self.machine_st.cp; } - &Instruction::CallTermAttributedVariables(_) => { + &Instruction::CallTermAttributedVariables => { self.term_attributed_variables(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteTermAttributedVariables(_) => { + &Instruction::ExecuteTermAttributedVariables => { self.term_attributed_variables(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallTermVariables(_) => { + &Instruction::CallTermVariables => { self.term_variables(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteTermVariables(_) => { + &Instruction::ExecuteTermVariables => { self.term_variables(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallTermVariablesUnderMaxDepth(_) => { + &Instruction::CallTermVariablesUnderMaxDepth => { self.term_variables_under_max_depth(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteTermVariablesUnderMaxDepth(_) => { + &Instruction::ExecuteTermVariablesUnderMaxDepth => { self.term_variables_under_max_depth(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallTruncateLiftedHeapTo(_) => { + &Instruction::CallTruncateLiftedHeapTo => { self.truncate_lifted_heap_to(); self.machine_st.p += 1; } - &Instruction::ExecuteTruncateLiftedHeapTo(_) => { + &Instruction::ExecuteTruncateLiftedHeapTo => { self.truncate_lifted_heap_to(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallUnifyWithOccursCheck(_) => { + &Instruction::CallUnifyWithOccursCheck => { self.unify_with_occurs_check(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteUnifyWithOccursCheck(_) => { + &Instruction::ExecuteUnifyWithOccursCheck => { self.unify_with_occurs_check(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallUnwindEnvironments(_) => { + &Instruction::CallUnwindEnvironments => { if !self.unwind_environments() { self.machine_st.p += 1; } } - &Instruction::ExecuteUnwindEnvironments(_) => { + &Instruction::ExecuteUnwindEnvironments => { if !self.unwind_environments() { self.machine_st.p = self.machine_st.cp; } } - &Instruction::CallUnwindStack(_) | &Instruction::ExecuteUnwindStack(_) => { + &Instruction::CallUnwindStack | &Instruction::ExecuteUnwindStack => { self.machine_st.unwind_stack(); self.machine_st.backtrack(); } - &Instruction::CallWAMInstructions(_) => { + &Instruction::CallWAMInstructions => { try_or_throw!(self.machine_st, self.wam_instructions()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteWAMInstructions(_) => { + &Instruction::ExecuteWAMInstructions => { try_or_throw!(self.machine_st, self.wam_instructions()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallWriteTerm(_) => { + &Instruction::CallInlinedInstructions => { + self.inlined_instructions(); + self.machine_st.p += 1; + } + &Instruction::ExecuteInlinedInstructions => { + self.inlined_instructions(); + self.machine_st.p = self.machine_st.cp; + } + &Instruction::CallWriteTerm => { try_or_throw!(self.machine_st, self.write_term()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteWriteTerm(_) => { + &Instruction::ExecuteWriteTerm => { try_or_throw!(self.machine_st, self.write_term()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallWriteTermToChars(_) => { + &Instruction::CallWriteTermToChars => { try_or_throw!(self.machine_st, self.write_term_to_chars()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteWriteTermToChars(_) => { + &Instruction::ExecuteWriteTermToChars => { try_or_throw!(self.machine_st, self.write_term_to_chars()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallScryerPrologVersion(_) => { + &Instruction::CallScryerPrologVersion => { self.scryer_prolog_version(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteScryerPrologVersion(_) => { + &Instruction::ExecuteScryerPrologVersion => { self.scryer_prolog_version(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCryptoRandomByte(_) => { + &Instruction::CallCryptoRandomByte => { self.crypto_random_byte(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCryptoRandomByte(_) => { + &Instruction::ExecuteCryptoRandomByte => { self.crypto_random_byte(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCryptoDataHash(_) => { + &Instruction::CallCryptoDataHash => { self.crypto_data_hash(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCryptoDataHash(_) => { + &Instruction::ExecuteCryptoDataHash => { self.crypto_data_hash(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCryptoDataHKDF(_) => { + &Instruction::CallCryptoDataHKDF => { self.crypto_data_hkdf(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCryptoDataHKDF(_) => { + &Instruction::ExecuteCryptoDataHKDF => { self.crypto_data_hkdf(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCryptoPasswordHash(_) => { + &Instruction::CallCryptoPasswordHash => { self.crypto_password_hash(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCryptoPasswordHash(_) => { + &Instruction::ExecuteCryptoPasswordHash => { self.crypto_password_hash(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCryptoDataEncrypt(_) => { + &Instruction::CallCryptoDataEncrypt => { self.crypto_data_encrypt(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCryptoDataEncrypt(_) => { + &Instruction::ExecuteCryptoDataEncrypt => { self.crypto_data_encrypt(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCryptoDataDecrypt(_) => { + &Instruction::CallCryptoDataDecrypt => { self.crypto_data_decrypt(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCryptoDataDecrypt(_) => { + &Instruction::ExecuteCryptoDataDecrypt => { self.crypto_data_decrypt(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCryptoCurveScalarMult(_) => { + &Instruction::CallCryptoCurveScalarMult => { self.crypto_curve_scalar_mult(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCryptoCurveScalarMult(_) => { + &Instruction::ExecuteCryptoCurveScalarMult => { self.crypto_curve_scalar_mult(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallEd25519Sign(_) => { + &Instruction::CallEd25519Sign => { self.ed25519_sign(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteEd25519Sign(_) => { + &Instruction::ExecuteEd25519Sign => { self.ed25519_sign(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallEd25519Verify(_) => { + &Instruction::CallEd25519Verify => { self.ed25519_verify(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteEd25519Verify(_) => { + &Instruction::ExecuteEd25519Verify => { self.ed25519_verify(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallEd25519NewKeyPair(_) => { + &Instruction::CallEd25519NewKeyPair => { self.ed25519_new_key_pair(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteEd25519NewKeyPair(_) => { + &Instruction::ExecuteEd25519NewKeyPair => { self.ed25519_new_key_pair(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallEd25519KeyPairPublicKey(_) => { + &Instruction::CallEd25519KeyPairPublicKey => { self.ed25519_key_pair_public_key(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteEd25519KeyPairPublicKey(_) => { + &Instruction::ExecuteEd25519KeyPairPublicKey => { self.ed25519_key_pair_public_key(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCurve25519ScalarMult(_) => { + &Instruction::CallCurve25519ScalarMult => { self.curve25519_scalar_mult(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCurve25519ScalarMult(_) => { + &Instruction::ExecuteCurve25519ScalarMult => { self.curve25519_scalar_mult(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallFirstNonOctet(_) => { + &Instruction::CallFirstNonOctet => { self.first_non_octet(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteFirstNonOctet(_) => { + &Instruction::ExecuteFirstNonOctet => { self.first_non_octet(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallLoadHTML(_) => { + &Instruction::CallLoadHTML => { self.load_html(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteLoadHTML(_) => { + &Instruction::ExecuteLoadHTML => { self.load_html(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallLoadXML(_) => { + &Instruction::CallLoadXML => { self.load_xml(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteLoadXML(_) => { + &Instruction::ExecuteLoadXML => { self.load_xml(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallGetEnv(_) => { + &Instruction::CallGetEnv => { self.get_env(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetEnv(_) => { + &Instruction::ExecuteGetEnv => { self.get_env(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallSetEnv(_) => { + &Instruction::CallSetEnv => { self.set_env(); self.machine_st.p += 1; } - &Instruction::ExecuteSetEnv(_) => { + &Instruction::ExecuteSetEnv => { self.set_env(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallUnsetEnv(_) => { + &Instruction::CallUnsetEnv => { self.unset_env(); self.machine_st.p += 1; } - &Instruction::ExecuteUnsetEnv(_) => { + &Instruction::ExecuteUnsetEnv => { self.unset_env(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallShell(_) => { + &Instruction::CallShell => { self.shell(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteShell(_) => { + &Instruction::ExecuteShell => { self.shell(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallPID(_) => { + &Instruction::CallPID => { self.pid(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecutePID(_) => { + &Instruction::ExecutePID => { self.pid(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCharsBase64(_) => { + &Instruction::CallCharsBase64 => { try_or_throw!(self.machine_st, self.chars_base64()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCharsBase64(_) => { + &Instruction::ExecuteCharsBase64 => { try_or_throw!(self.machine_st, self.chars_base64()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallDevourWhitespace(_) => { + &Instruction::CallDevourWhitespace => { try_or_throw!(self.machine_st, self.devour_whitespace()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteDevourWhitespace(_) => { + &Instruction::ExecuteDevourWhitespace => { try_or_throw!(self.machine_st, self.devour_whitespace()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallIsSTOEnabled(_) => { + &Instruction::CallIsSTOEnabled => { self.is_sto_enabled(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteIsSTOEnabled(_) => { + &Instruction::ExecuteIsSTOEnabled => { self.is_sto_enabled(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallSetSTOAsUnify(_) => { + &Instruction::CallSetSTOAsUnify => { self.set_sto_as_unify(); self.machine_st.p += 1; } - &Instruction::ExecuteSetSTOAsUnify(_) => { + &Instruction::ExecuteSetSTOAsUnify => { self.set_sto_as_unify(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallSetNSTOAsUnify(_) => { + &Instruction::CallSetNSTOAsUnify => { self.set_nsto_as_unify(); self.machine_st.p += 1; } - &Instruction::ExecuteSetNSTOAsUnify(_) => { + &Instruction::ExecuteSetNSTOAsUnify => { self.set_nsto_as_unify(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallSetSTOWithErrorAsUnify(_) => { + &Instruction::CallSetSTOWithErrorAsUnify => { self.set_sto_with_error_as_unify(); self.machine_st.p += 1; } - &Instruction::ExecuteSetSTOWithErrorAsUnify(_) => { + &Instruction::ExecuteSetSTOWithErrorAsUnify => { self.set_sto_with_error_as_unify(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallHomeDirectory(_) => { + &Instruction::CallHomeDirectory => { self.home_directory(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteHomeDirectory(_) => { + &Instruction::ExecuteHomeDirectory => { self.home_directory(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallDebugHook(_) => { + &Instruction::CallDebugHook => { self.debug_hook(); self.machine_st.p += 1; } - &Instruction::ExecuteDebugHook(_) => { + &Instruction::ExecuteDebugHook => { self.debug_hook(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallPopCount(_) => { + &Instruction::CallPopCount => { self.pop_count(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecutePopCount(_) => { + &Instruction::ExecutePopCount => { self.pop_count(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallAddDiscontiguousPredicate(_) => { + &Instruction::CallAddDiscontiguousPredicate => { try_or_throw!(self.machine_st, self.add_discontiguous_predicate()); self.machine_st.p += 1; } - &Instruction::ExecuteAddDiscontiguousPredicate(_) => { + &Instruction::ExecuteAddDiscontiguousPredicate => { try_or_throw!(self.machine_st, self.add_discontiguous_predicate()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallAddDynamicPredicate(_) => { + &Instruction::CallAddDynamicPredicate => { try_or_throw!(self.machine_st, self.add_dynamic_predicate()); self.machine_st.p += 1; } - &Instruction::ExecuteAddDynamicPredicate(_) => { + &Instruction::ExecuteAddDynamicPredicate => { try_or_throw!(self.machine_st, self.add_dynamic_predicate()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallAddMultifilePredicate(_) => { + &Instruction::CallAddMultifilePredicate => { try_or_throw!(self.machine_st, self.add_multifile_predicate()); self.machine_st.p += 1; } - &Instruction::ExecuteAddMultifilePredicate(_) => { + &Instruction::ExecuteAddMultifilePredicate => { try_or_throw!(self.machine_st, self.add_multifile_predicate()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallAddGoalExpansionClause(_) => { + &Instruction::CallAddGoalExpansionClause => { try_or_throw!(self.machine_st, self.add_goal_expansion_clause()); self.machine_st.p += 1; } - &Instruction::ExecuteAddGoalExpansionClause(_) => { + &Instruction::ExecuteAddGoalExpansionClause => { try_or_throw!(self.machine_st, self.add_goal_expansion_clause()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallAddTermExpansionClause(_) => { + &Instruction::CallAddTermExpansionClause => { try_or_throw!(self.machine_st, self.add_term_expansion_clause()); self.machine_st.p += 1; } - &Instruction::ExecuteAddTermExpansionClause(_) => { + &Instruction::ExecuteAddTermExpansionClause => { try_or_throw!(self.machine_st, self.add_term_expansion_clause()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallAddInSituFilenameModule(_) => { + &Instruction::CallAddInSituFilenameModule => { try_or_throw!(self.machine_st, self.add_in_situ_filename_module()); self.machine_st.p += 1; } - &Instruction::ExecuteAddInSituFilenameModule(_) => { + &Instruction::ExecuteAddInSituFilenameModule => { try_or_throw!(self.machine_st, self.add_in_situ_filename_module()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallClauseToEvacuable(_) => { + &Instruction::CallClauseToEvacuable => { try_or_throw!(self.machine_st, self.clause_to_evacuable()); self.machine_st.p += 1; } - &Instruction::ExecuteClauseToEvacuable(_) => { + &Instruction::ExecuteClauseToEvacuable => { try_or_throw!(self.machine_st, self.clause_to_evacuable()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallScopedClauseToEvacuable(_) => { + &Instruction::CallScopedClauseToEvacuable => { try_or_throw!(self.machine_st, self.scoped_clause_to_evacuable()); self.machine_st.p += 1; } - &Instruction::ExecuteScopedClauseToEvacuable(_) => { + &Instruction::ExecuteScopedClauseToEvacuable => { try_or_throw!(self.machine_st, self.scoped_clause_to_evacuable()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallConcludeLoad(_) => { + &Instruction::CallConcludeLoad => { try_or_throw!(self.machine_st, self.conclude_load()); self.machine_st.p += 1; } - &Instruction::ExecuteConcludeLoad(_) => { + &Instruction::ExecuteConcludeLoad => { try_or_throw!(self.machine_st, self.conclude_load()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallDeclareModule(_) => { + &Instruction::CallDeclareModule => { try_or_throw!(self.machine_st, self.declare_module()); self.machine_st.p += 1; } - &Instruction::ExecuteDeclareModule(_) => { + &Instruction::ExecuteDeclareModule => { try_or_throw!(self.machine_st, self.declare_module()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallLoadCompiledLibrary(_) => { + &Instruction::CallLoadCompiledLibrary => { try_or_throw!(self.machine_st, self.load_compiled_library()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteLoadCompiledLibrary(_) => { + &Instruction::ExecuteLoadCompiledLibrary => { try_or_throw!(self.machine_st, self.load_compiled_library()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallLoadContextSource(_) => { + &Instruction::CallLoadContextSource => { self.load_context_source(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteLoadContextSource(_) => { + &Instruction::ExecuteLoadContextSource => { self.load_context_source(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallLoadContextFile(_) => { + &Instruction::CallLoadContextFile => { self.load_context_file(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteLoadContextFile(_) => { + &Instruction::ExecuteLoadContextFile => { self.load_context_file(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallLoadContextDirectory(_) => { + &Instruction::CallLoadContextDirectory => { self.load_context_directory(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteLoadContextDirectory(_) => { + &Instruction::ExecuteLoadContextDirectory => { self.load_context_directory(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallLoadContextModule(_) => { + &Instruction::CallLoadContextModule => { self.load_context_module(self.machine_st.registers[1]); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteLoadContextModule(_) => { + &Instruction::ExecuteLoadContextModule => { self.load_context_module(self.machine_st.registers[1]); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallLoadContextStream(_) => { + &Instruction::CallLoadContextStream => { self.load_context_stream(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteLoadContextStream(_) => { + &Instruction::ExecuteLoadContextStream => { self.load_context_stream(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallPopLoadContext(_) => { + &Instruction::CallPopLoadContext => { self.pop_load_context(); self.machine_st.p += 1; } - &Instruction::ExecutePopLoadContext(_) => { + &Instruction::ExecutePopLoadContext => { self.pop_load_context(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallPopLoadStatePayload(_) => { + &Instruction::CallPopLoadStatePayload => { self.pop_load_state_payload(); self.machine_st.p += 1; } - &Instruction::ExecutePopLoadStatePayload(_) => { + &Instruction::ExecutePopLoadStatePayload => { self.pop_load_state_payload(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallPushLoadContext(_) => { + &Instruction::CallPushLoadContext => { try_or_throw!(self.machine_st, self.push_load_context()); self.machine_st.p += 1; } - &Instruction::ExecutePushLoadContext(_) => { + &Instruction::ExecutePushLoadContext => { try_or_throw!(self.machine_st, self.push_load_context()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallPushLoadStatePayload(_) => { + &Instruction::CallPushLoadStatePayload => { self.push_load_state_payload(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecutePushLoadStatePayload(_) => { + &Instruction::ExecutePushLoadStatePayload => { self.push_load_state_payload(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallUseModule(_) => { + &Instruction::CallUseModule => { try_or_throw!(self.machine_st, self.use_module()); self.machine_st.p += 1; } - &Instruction::ExecuteUseModule(_) => { + &Instruction::ExecuteUseModule => { try_or_throw!(self.machine_st, self.use_module()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallBuiltInProperty(_) => { - self.builtin_property(); + &Instruction::CallBuiltInProperty => { + let key = self + .machine_st + .read_predicate_key(self.machine_st.registers[1], self.machine_st.registers[2]); + + self.machine_st.fail = !self.indices.builtin_property(key); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteBuiltInProperty(_) => { - self.builtin_property(); + &Instruction::ExecuteBuiltInProperty => { + let key = self + .machine_st + .read_predicate_key(self.machine_st.registers[1], self.machine_st.registers[2]); + + self.machine_st.fail = !self.indices.builtin_property(key); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallMetaPredicateProperty(_) => { + &Instruction::CallMetaPredicateProperty => { self.meta_predicate_property(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteMetaPredicateProperty(_) => { + &Instruction::ExecuteMetaPredicateProperty => { self.meta_predicate_property(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallMultifileProperty(_) => { + &Instruction::CallMultifileProperty => { self.multifile_property(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteMultifileProperty(_) => { + &Instruction::ExecuteMultifileProperty => { self.multifile_property(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallDiscontiguousProperty(_) => { + &Instruction::CallDiscontiguousProperty => { self.discontiguous_property(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteDiscontiguousProperty(_) => { + &Instruction::ExecuteDiscontiguousProperty => { self.discontiguous_property(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallDynamicProperty(_) => { + &Instruction::CallDynamicProperty => { self.dynamic_property(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteDynamicProperty(_) => { + &Instruction::ExecuteDynamicProperty => { self.dynamic_property(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallAbolishClause(_) => { + &Instruction::CallAbolishClause => { try_or_throw!(self.machine_st, self.abolish_clause()); self.machine_st.p += 1; } - &Instruction::ExecuteAbolishClause(_) => { + &Instruction::ExecuteAbolishClause => { try_or_throw!(self.machine_st, self.abolish_clause()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallAsserta(_) => { + &Instruction::CallAsserta => { try_or_throw!(self.machine_st, self.compile_assert(AppendOrPrepend::Prepend)); self.machine_st.p += 1; } - &Instruction::ExecuteAsserta(_) => { + &Instruction::ExecuteAsserta => { try_or_throw!(self.machine_st, self.compile_assert(AppendOrPrepend::Prepend)); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallAssertz(_) => { + &Instruction::CallAssertz => { try_or_throw!(self.machine_st, self.compile_assert(AppendOrPrepend::Append)); self.machine_st.p += 1; } - &Instruction::ExecuteAssertz(_) => { + &Instruction::ExecuteAssertz => { try_or_throw!(self.machine_st, self.compile_assert(AppendOrPrepend::Append)); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallRetract(_) => { + &Instruction::CallRetract => { try_or_throw!(self.machine_st, self.retract_clause()); self.machine_st.p += 1; } - &Instruction::ExecuteRetract(_) => { + &Instruction::ExecuteRetract => { try_or_throw!(self.machine_st, self.retract_clause()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallIsConsistentWithTermQueue(_) => { + &Instruction::CallIsConsistentWithTermQueue => { try_or_throw!(self.machine_st, self.is_consistent_with_term_queue()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteIsConsistentWithTermQueue(_) => { + &Instruction::ExecuteIsConsistentWithTermQueue => { try_or_throw!(self.machine_st, self.is_consistent_with_term_queue()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::CallFlushTermQueue(_) => { + &Instruction::CallFlushTermQueue => { try_or_throw!(self.machine_st, self.flush_term_queue()); self.machine_st.p += 1; } - &Instruction::ExecuteFlushTermQueue(_) => { + &Instruction::ExecuteFlushTermQueue => { try_or_throw!(self.machine_st, self.flush_term_queue()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallRemoveModuleExports(_) => { + &Instruction::CallRemoveModuleExports => { try_or_throw!(self.machine_st, self.remove_module_exports()); self.machine_st.p += 1; } - &Instruction::ExecuteRemoveModuleExports(_) => { + &Instruction::ExecuteRemoveModuleExports => { try_or_throw!(self.machine_st, self.remove_module_exports()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallAddNonCountedBacktracking(_) => { + &Instruction::CallAddNonCountedBacktracking => { try_or_throw!(self.machine_st, self.add_non_counted_backtracking()); self.machine_st.p += 1; } - &Instruction::ExecuteAddNonCountedBacktracking(_) => { + &Instruction::ExecuteAddNonCountedBacktracking => { try_or_throw!(self.machine_st, self.add_non_counted_backtracking()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallPredicateDefined(_) => { + &Instruction::CallPredicateDefined => { self.machine_st.fail = !self.predicate_defined(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecutePredicateDefined(_) => { + &Instruction::ExecutePredicateDefined => { self.machine_st.fail = !self.predicate_defined(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallStripModule(_) => { - let (module_loc, qualified_goal) = self.machine_st.strip_module( - self.machine_st.registers[1], - self.machine_st.registers[2], - ); - - let target_module_loc = self.machine_st.registers[2]; - - unify_fn!( - &mut self.machine_st, - module_loc, - target_module_loc - ); - - let target_qualified_goal = self.machine_st.registers[3]; - - unify_fn!( - &mut self.machine_st, - qualified_goal, - target_qualified_goal - ); - + &Instruction::CallStripModule => { + self.strip_module(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteStripModule(_) => { - let (module_loc, qualified_goal) = self.machine_st.strip_module( - self.machine_st.registers[1], - self.machine_st.registers[2], - ); - - let target_module_loc = self.machine_st.registers[2]; - - unify_fn!( - &mut self.machine_st, - module_loc, - target_module_loc - ); - - let target_qualified_goal = self.machine_st.registers[3]; - - unify_fn!( - &mut self.machine_st, - qualified_goal, - target_qualified_goal - ); - + &Instruction::ExecuteStripModule => { + self.strip_module(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallPrepareCallClause(arity, _) => { + &Instruction::CallPrepareCallClause(arity) => { try_or_throw!(self.machine_st, self.prepare_call_clause(arity)); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecutePrepareCallClause(arity, _) => { + &Instruction::ExecutePrepareCallClause(arity) => { try_or_throw!(self.machine_st, self.prepare_call_clause(arity)); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCompileInlineOrExpandedGoal(_) => { + &Instruction::CallCompileInlineOrExpandedGoal => { try_or_throw!(self.machine_st, self.compile_inline_or_expanded_goal()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCompileInlineOrExpandedGoal(_) => { + &Instruction::ExecuteCompileInlineOrExpandedGoal => { try_or_throw!(self.machine_st, self.compile_inline_or_expanded_goal()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallIsExpandedOrInlined(_) => { + &Instruction::CallIsExpandedOrInlined => { self.machine_st.fail = !self.is_expanded_or_inlined(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteIsExpandedOrInlined(_) => { + &Instruction::ExecuteIsExpandedOrInlined => { self.machine_st.fail = !self.is_expanded_or_inlined(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallInlineCallN(arity, _) => { + &Instruction::CallFastCallN(arity) => { let call_at_index = |wam: &mut Machine, name, arity, ptr| { wam.try_call(name, arity, ptr) }; - try_or_throw!(self.machine_st, self.call_inline(arity, call_at_index)); + try_or_throw!(self.machine_st, self.fast_call(arity, call_at_index)); if self.machine_st.fail { self.machine_st.backtrack(); @@ -5080,12 +5145,12 @@ impl Machine { ); } } - &Instruction::ExecuteInlineCallN(arity, _) => { + &Instruction::ExecuteFastCallN(arity) => { let call_at_index = |wam: &mut Machine, name, arity, ptr| { wam.try_execute(name, arity, ptr) }; - try_or_throw!(self.machine_st, self.call_inline(arity, call_at_index)); + try_or_throw!(self.machine_st, self.fast_call(arity, call_at_index)); if self.machine_st.fail { self.machine_st.backtrack(); @@ -5096,6 +5161,176 @@ impl Machine { ); } } + &Instruction::CallGetClauseP => { + let module_name = cell_as_atom!(self.deref_register(3)); + + let (n, p) = self.get_clause_p(module_name); + + let r = self.machine_st.registers[2]; + let r = self.machine_st.store(self.machine_st.deref(r)); + + let h = self.machine_st.heap.len(); + self.machine_st.heap.extend(functor!(atom!("-"), [fixnum(n), fixnum(p)])); + + let r = r.as_var().unwrap(); + self.machine_st.bind(r, str_loc_as_cell!(h)); + + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteGetClauseP => { + let module_name = cell_as_atom!(self.deref_register(3)); + + let (n, p) = self.get_clause_p(module_name); + + let r = self.machine_st.registers[2]; + let r = self.machine_st.store(self.machine_st.deref(r)); + + let h = self.machine_st.heap.len(); + self.machine_st.heap.extend(functor!(atom!("-"), [fixnum(n), fixnum(p)])); + + let r = r.as_var().unwrap(); + self.machine_st.bind(r, str_loc_as_cell!(h)); + + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } + &Instruction::CallInvokeClauseAtP => { + let key_cell = self.machine_st.registers[1]; + let key = self.machine_st.name_and_arity_from_heap(key_cell).unwrap(); + + let l = self.machine_st.registers[3]; + let l = self.machine_st.store(self.machine_st.deref(l)); + + let l = match Number::try_from(l) { + Ok(Number::Fixnum(l)) => l.get_num() as usize, + _ => unreachable!(), + }; + + let p = self.machine_st.registers[4]; + let p = self.machine_st.store(self.machine_st.deref(p)); + + let p = match Number::try_from(p) { + Ok(Number::Fixnum(p)) => p.get_num() as usize, + _ => unreachable!(), + }; + + let module_name = cell_as_atom!(self.deref_register(6)); + + let compilation_target = match module_name { + atom!("user") => CompilationTarget::User, + _ => CompilationTarget::Module(module_name), + }; + + let skeleton = self.indices.get_predicate_skeleton_mut( + &compilation_target, + &key, + ).unwrap(); + + match skeleton.target_pos_of_clause_clause_loc(l) { + Some(n) => { + let r = self.machine_st.store(self.machine_st.deref( + self.machine_st.registers[5], + )); + + self.machine_st.unify_fixnum(Fixnum::build_with(n as i64), r); + } + None => {} + } + + self.machine_st.call_at_index(2, p); + } + &Instruction::ExecuteInvokeClauseAtP => { + let key_cell = self.machine_st.registers[1]; + let key = self.machine_st.name_and_arity_from_heap(key_cell).unwrap(); + + let l = self.machine_st.registers[3]; + let l = self.machine_st.store(self.machine_st.deref(l)); + + let l = match Number::try_from(l) { + Ok(Number::Fixnum(l)) => l.get_num() as usize, + _ => unreachable!(), + }; + + let p = self.machine_st.registers[4]; + let p = self.machine_st.store(self.machine_st.deref(p)); + + let p = match Number::try_from(p) { + Ok(Number::Fixnum(p)) => p.get_num() as usize, + _ => unreachable!(), + }; + + let module_name = cell_as_atom!(self.deref_register(6)); + + let compilation_target = match module_name { + atom!("user") => CompilationTarget::User, + _ => CompilationTarget::Module(module_name), + }; + + let skeleton = self.indices.get_predicate_skeleton_mut( + &compilation_target, + &key, + ).unwrap(); + + match skeleton.target_pos_of_clause_clause_loc(l) { + Some(n) => { + let r = self.machine_st.store(self.machine_st.deref( + self.machine_st.registers[5], + )); + + self.machine_st.unify_fixnum(Fixnum::build_with(n as i64), r); + } + None => {} + } + + self.machine_st.execute_at_index(2, p); + } + &Instruction::CallGetFromAttributedVarList => { + self.get_from_attributed_variable_list(); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteGetFromAttributedVarList => { + self.get_from_attributed_variable_list(); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } + &Instruction::CallPutToAttributedVarList => { + self.put_to_attributed_variable_list(); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecutePutToAttributedVarList => { + self.put_to_attributed_variable_list(); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } + &Instruction::CallDeleteFromAttributedVarList => { + self.delete_from_attributed_variable_list(); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteDeleteFromAttributedVarList => { + self.delete_from_attributed_variable_list(); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } + &Instruction::CallDeleteAllAttributesFromVar => { + self.delete_all_attributes_from_var(); + self.machine_st.p += 1; + } + &Instruction::ExecuteDeleteAllAttributesFromVar => { + self.delete_all_attributes_from_var(); + self.machine_st.p = self.machine_st.cp; + } + &Instruction::CallUnattributedVar => { + self.machine_st.unattributed_var(); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteUnattributedVar => { + self.machine_st.unattributed_var(); + self.machine_st.p = self.machine_st.cp; + } + &Instruction::CallGetDBRefs => { + self.get_db_refs(); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteGetDBRefs => { + self.get_db_refs(); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } } } @@ -5116,5 +5351,7 @@ impl Machine { Err(_) => unreachable!(), } } + + std::process::ExitCode::SUCCESS } } diff --git a/src/machine/gc.rs b/src/machine/gc.rs index 8a884950..1de28ffe 100644 --- a/src/machine/gc.rs +++ b/src/machine/gc.rs @@ -3,7 +3,7 @@ use crate::machine::heap::*; use crate::types::*; #[cfg(test)] -use crate::heap_iter::FocusedHeapIter; +use crate::heap_iter::{IterStackLoc, FocusedHeapIter, HeapOrStackTag}; use core::marker::PhantomData; @@ -75,8 +75,8 @@ pub(crate) struct StacklessPreOrderHeapIter<'a, UMP: UnmarkPolicy> { #[cfg(test)] impl<'a> FocusedHeapIter for StacklessPreOrderHeapIter<'a, IteratorUMP> { #[inline] - fn focus(&self) -> usize { - self.current + fn focus(&self) -> IterStackLoc { + IterStackLoc::iterable_loc(self.current, HeapOrStackTag::Heap) } } diff --git a/src/machine/heap.rs b/src/machine/heap.rs index 59e63009..1cdfeb74 100644 --- a/src/machine/heap.rs +++ b/src/machine/heap.rs @@ -6,7 +6,7 @@ use crate::machine::partial_string::*; use crate::parser::ast::*; use crate::types::*; -use crate::parser::rug::{Integer, Rational}; +use crate::parser::dashu::{Integer, Rational}; use std::convert::TryFrom; diff --git a/src/machine/load_state.rs b/src/machine/load_state.rs index 802d51eb..8273ac41 100644 --- a/src/machine/load_state.rs +++ b/src/machine/load_state.rs @@ -133,7 +133,7 @@ pub(super) fn import_module_exports<'a, LS: LoadState<'a>>( meta_predicates.insert(key, meta_specs.clone()); } - if let Some(src_code_index) = imported_module.code_dir.get(&key) { + if let Some(src_code_index) = imported_module.code_dir.get(&key).cloned() { let arena = &mut LS::machine_st(payload).arena; let target_code_index = code_dir @@ -148,6 +148,10 @@ pub(super) fn import_module_exports<'a, LS: LoadState<'a>>( target_code_index, src_code_index.get(), ); + + if src_code_index.is_dynamic_undefined() { + code_dir.insert(key, src_code_index); + } } else { return Err(SessionError::ModuleDoesNotContainExport( imported_module.module_decl.name, @@ -441,13 +445,11 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { term: Term, preprocessor: &mut Preprocessor, ) -> Result { - let tl = preprocessor.try_term_to_tl(self, term, CutContext::BlocksCuts)?; + let tl = preprocessor.try_term_to_tl(self, term)?; Ok(match tl { - TopLevel::Fact(fact) => PredicateClause::Fact(fact), - TopLevel::Rule(rule) => PredicateClause::Rule(rule), - TopLevel::Query(_) => return Err(SessionError::QueryCannotBeDefinedAsFact), - _ => unreachable!(), + TopLevel::Fact(fact, var_data) => PredicateClause::Fact(fact, var_data), + TopLevel::Rule(rule, var_data) => PredicateClause::Rule(rule, var_data), }) } diff --git a/src/machine/loader.rs b/src/machine/loader.rs index 616fe7ee..9d66ce64 100644 --- a/src/machine/loader.rs +++ b/src/machine/loader.rs @@ -21,7 +21,6 @@ use std::convert::TryFrom; use std::fmt; use std::mem; use std::ops::{Deref, DerefMut}; -use std::rc::Rc; /* * The loader compiles Prolog terms read from a TermStream instance, @@ -329,6 +328,10 @@ impl<'a> LoadState<'a> for LiveLoadAndMachineState<'a> { loader: &Loader<'a, Self>, key: PredicateKey, ) -> Result<(), SessionError> { + if ClauseType::is_inbuilt(key.0, key.1) { + return Err(SessionError::CannotOverwriteBuiltIn(key)); + } + if let Some(builtins) = loader.wam_prelude.indices.modules.get(&atom!("builtins")) { if builtins.module_decl.exports.contains(&ModuleExport::PredicateKey(key)) { return Err(SessionError::CannotOverwriteBuiltIn(key)); @@ -465,6 +468,13 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { } } + pub(crate) fn read_term_from_heap(&mut self, r: RegType) -> Result { + let machine_st = LS::machine_st(&mut self.payload); + let cell = machine_st[r]; + + machine_st.read_term_from_heap(cell) + } + pub(crate) fn load(mut self) -> Result { while let Some(decl) = self.dequeue_terms()? { self.load_decl(decl)?; @@ -531,106 +541,6 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { Ok(()) } - pub(super) fn read_term_from_heap(&mut self, heap_term_loc: RegType) -> Result { - let machine_st = LS::machine_st(&mut self.payload); - let term_addr = machine_st[heap_term_loc]; - - let mut term_stack = vec![]; - let mut iter = stackful_post_order_iter(&mut machine_st.heap, term_addr); - - while let Some(addr) = iter.next() { - let addr = unmark_cell_bits!(addr); - - read_heap_cell!(addr, - (HeapCellValueTag::Lis) => { - use crate::parser::parser::as_partial_string; - - let tail = term_stack.pop().unwrap(); - let head = term_stack.pop().unwrap(); - - match as_partial_string(head, tail) { - Ok((string, Some(tail))) => { - term_stack.push(Term::PartialString(Cell::default(), string, tail)); - } - Ok((string, None)) => { - let atom = machine_st.atom_tbl.build_with(&string); - term_stack.push(Term::CompleteString(Cell::default(), atom)); - } - Err(cons_term) => term_stack.push(cons_term), - } - } - (HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar, h) => { - let offset_string = format!("_{}", h); - term_stack.push(Term::Var(Cell::default(), Rc::new(offset_string))); - } - (HeapCellValueTag::Cons | HeapCellValueTag::CStr | HeapCellValueTag::Fixnum | - HeapCellValueTag::Char | HeapCellValueTag::F64) => { - term_stack.push(Term::Literal(Cell::default(), Literal::try_from(addr).unwrap())); - } - (HeapCellValueTag::Atom, (name, arity)) => { - let h = iter.focus(); - let mut arity = arity; - - if iter.heap.len() > h + arity + 1 { - let value = iter.heap[h + arity + 1]; - - if let Some(idx) = get_structure_index(value) { - // in the second condition, arity == 0, - // meaning idx cannot pertain to this atom - // if it is the direct subterm of a larger - // structure. - if arity > 0 || !iter.direct_subterm_of_str(h) { - term_stack.push( - Term::Literal(Cell::default(), Literal::CodeIndex(idx)) - ); - - arity += 1; - } - } - } - - if arity == 0 { - term_stack.push(Term::Literal(Cell::default(), Literal::Atom(name))); - } else { - let subterms = term_stack - .drain(term_stack.len() - arity ..) - .collect(); - - term_stack.push(Term::Clause(Cell::default(), name, subterms)); - } - } - (HeapCellValueTag::PStr, atom) => { - let tail = term_stack.pop().unwrap(); - - if let Term::Literal(_, Literal::Atom(atom!("[]"))) = &tail { - term_stack.push(Term::CompleteString(Cell::default(), atom)); - } else { - term_stack.push(Term::PartialString( - Cell::default(), - atom.as_str().to_owned(), - Box::new(tail), - )); - } - } - (HeapCellValueTag::PStrLoc, h) => { - let atom = cell_as_atom_cell!(iter.heap[h]).get_name(); - let tail = term_stack.pop().unwrap(); - - term_stack.push(Term::PartialString( - Cell::default(), - atom.as_str().to_owned(), - Box::new(tail), - )); - } - _ => { - } - ); - } - - debug_assert!(term_stack.len() == 1); - Ok(term_stack.pop().unwrap()) - } - fn reset_machine(&mut self) { while let Some(record) = self.payload.retraction_info.records.pop() { match record { @@ -1143,7 +1053,10 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { &mut self, r: RegType, ) -> Result, SessionError> { - let export_list = self.read_term_from_heap(r)?; + let machine_st = LS::machine_st(&mut self.payload); + let cell = machine_st[r]; + + let export_list = machine_st.read_term_from_heap(cell)?; let atom_tbl = &mut LS::machine_st(&mut self.payload).atom_tbl; let export_list = setup_module_export_list(export_list, atom_tbl)?; @@ -1493,6 +1406,104 @@ impl<'a> MachinePreludeView<'a> { } } +impl MachineState { + pub(super) fn read_term_from_heap(&mut self, term_addr: HeapCellValue) -> Result { + let mut term_stack = vec![]; + let mut iter = stackful_post_order_iter(&mut self.heap, &mut self.stack, term_addr); + + while let Some(addr) = iter.next() { + let addr = unmark_cell_bits!(addr); + + read_heap_cell!(addr, + (HeapCellValueTag::Lis) => { + use crate::parser::parser::as_partial_string; + + let tail = term_stack.pop().unwrap(); + let head = term_stack.pop().unwrap(); + + match as_partial_string(head, tail) { + Ok((string, Some(tail))) => { + term_stack.push(Term::PartialString(Cell::default(), string, tail)); + } + Ok((string, None)) => { + let atom = self.atom_tbl.build_with(&string); + term_stack.push(Term::CompleteString(Cell::default(), atom)); + } + Err(cons_term) => term_stack.push(cons_term), + } + } + (HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar, h) => { + term_stack.push(Term::Var(Cell::default(), VarPtr::from(format!("_{}", h)))); + } + (HeapCellValueTag::Cons | HeapCellValueTag::CStr | HeapCellValueTag::Fixnum | + HeapCellValueTag::Char | HeapCellValueTag::F64) => { + term_stack.push(Term::Literal(Cell::default(), Literal::try_from(addr).unwrap())); + } + (HeapCellValueTag::Atom, (name, arity)) => { + let h = iter.focus().value() as usize; + let mut arity = arity; + + if iter.heap.len() > h + arity + 1 { + let value = iter.heap[h + arity + 1]; + + if let Some(idx) = get_structure_index(value) { + // in the second condition, arity == 0, + // meaning idx cannot pertain to this atom + // if it is the direct subterm of a larger + // structure. + if arity > 0 || !iter.direct_subterm_of_str(h) { + term_stack.push( + Term::Literal(Cell::default(), Literal::CodeIndex(idx)) + ); + + arity += 1; + } + } + } + + if arity == 0 { + term_stack.push(Term::Literal(Cell::default(), Literal::Atom(name))); + } else { + let subterms = term_stack + .drain(term_stack.len() - arity ..) + .collect(); + + term_stack.push(Term::Clause(Cell::default(), name, subterms)); + } + } + (HeapCellValueTag::PStr, atom) => { + let tail = term_stack.pop().unwrap(); + + if let Term::Literal(_, Literal::Atom(atom!("[]"))) = &tail { + term_stack.push(Term::CompleteString(Cell::default(), atom)); + } else { + term_stack.push(Term::PartialString( + Cell::default(), + atom.as_str().to_owned(), + Box::new(tail), + )); + } + } + (HeapCellValueTag::PStrLoc, h) => { + let atom = cell_as_atom_cell!(iter.heap[h]).get_name(); + let tail = term_stack.pop().unwrap(); + + term_stack.push(Term::PartialString( + Cell::default(), + atom.as_str().to_owned(), + Box::new(tail), + )); + } + _ => { + } + ); + } + + debug_assert!(term_stack.len() == 1); + Ok(term_stack.pop().unwrap()) + } +} + impl Machine { pub(crate) fn use_module(&mut self) -> CallResult { let subevacuable_addr = self @@ -1620,25 +1631,18 @@ impl Machine { usize, ) -> Result<(), SessionError>, ) -> CallResult { - let module_name = cell_as_atom!( - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])) - ); + let module_name = cell_as_atom!(self.deref_register(1)); let compilation_target = match module_name { atom!("user") => CompilationTarget::User, _ => CompilationTarget::Module(module_name), }; - let predicate_name = cell_as_atom!( - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])) - ); - - let arity = self - .machine_st - .store(self.machine_st.deref(self.machine_st.registers[3])); + let predicate_name = cell_as_atom!(self.deref_register(2)); + let arity = self.deref_register(3); let arity = match Number::try_from(arity) { - Ok(Number::Integer(n)) if &*n >= &0 && &*n <= &MAX_ARITY => Ok(n.to_usize().unwrap()), + Ok(Number::Integer(n)) if &*n >= &Integer::from(0) && &*n <= &Integer::from(MAX_ARITY) => Ok(n.to_usize().unwrap()), Ok(Number::Fixnum(n)) if n.get_num() >= 0 && n.get_num() <= MAX_ARITY as i64 => { Ok(usize::try_from(n.get_num()).unwrap()) } @@ -1692,6 +1696,21 @@ impl Machine { let add_clause = || { let term = loader.read_term_from_heap(temp_v!(2))?; + let indexing_arg = match term.name() { + Some(atom!(":-")) => term.first_arg().and_then(Term::first_arg), + Some(_) => term.first_arg(), + None => None, + }; + + if let Some(indexing_term) = indexing_arg { + if let Some(indexing_name) = indexing_term.name() { + loader.wam_prelude + .indices + .goal_expansion_indices + .insert((indexing_name, indexing_term.arity())); + } + } + loader.incremental_compile_clause( (atom!("goal_expansion"), 2), term, @@ -1962,11 +1981,8 @@ impl Machine { } } - pub(crate) fn compile_assert(&mut self, append_or_prepend: AppendOrPrepend) -> CallResult - { - let module_name = cell_as_atom!( - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])) - ); + pub(crate) fn compile_assert(&mut self, append_or_prepend: AppendOrPrepend) -> CallResult { + let module_name = cell_as_atom!(self.deref_register(1)); let compilation_target = match module_name { atom!("user") => CompilationTarget::User, @@ -1980,13 +1996,20 @@ impl Machine { } }; + let head = self.deref_register(2); + + if head.is_var() { + let err = self.machine_st.instantiation_error(); + return Err(self.machine_st.error_form(err, stub_gen())); + } + let mut compile_assert = || { let mut loader: Loader<'_, LiveLoadAndMachineState<'_>> = Loader::new(self, LiveTermStream::new(ListingSource::User)); loader.payload.compilation_target = compilation_target; - let head = loader.read_term_from_heap(temp_v!(2))?; + let head = LiveLoadAndMachineState::machine_st(&mut loader.payload).read_term_from_heap(head)?; let name = if let Some(name) = head.name() { name @@ -1995,6 +2018,7 @@ impl Machine { }; let arity = head.arity(); + let is_builtin = loader.wam_prelude.indices.builtin_property((name, arity)); let is_dynamic_predicate = loader .wam_prelude @@ -2005,7 +2029,7 @@ impl Machine { ); let no_such_predicate = - if !is_dynamic_predicate && !ClauseType::is_inbuilt(name, arity) { + if !is_dynamic_predicate && !is_builtin { let idx_tag = loader .wam_prelude .indices @@ -2017,8 +2041,9 @@ impl Machine { .map(|code_idx| code_idx.get_tag()) .unwrap_or(IndexPtrTag::DynamicUndefined); - idx_tag == IndexPtrTag::DynamicUndefined || - idx_tag == IndexPtrTag::Undefined + idx_tag == IndexPtrTag::DynamicUndefined || idx_tag == IndexPtrTag::Undefined + } else if is_builtin { + return Err(SessionError::CannotOverwriteBuiltIn((name, arity))); } else { is_dynamic_predicate }; @@ -2445,21 +2470,6 @@ impl Machine { } } } - - pub(crate) fn builtin_property(&mut self) { - let (name, arity) = self - .machine_st - .read_predicate_key(self.machine_st.registers[1], self.machine_st.registers[2]); - - if !ClauseType::is_inbuilt(name, arity) { // ClauseType::from(key.0, key.1, &mut self.machine_st.arena) { - if let Some(module) = self.indices.modules.get(&(atom!("builtins"))) { - self.machine_st.fail = !module.code_dir.contains_key(&(name, arity)); - return; - } - } - - self.machine_st.fail = true; - } } impl<'a> Loader<'a, LiveLoadAndMachineState<'a>> { diff --git a/src/machine/machine_errors.rs b/src/machine/machine_errors.rs index ca2af22e..936200f0 100644 --- a/src/machine/machine_errors.rs +++ b/src/machine/machine_errors.rs @@ -1,10 +1,13 @@ +use crate::arena::*; use crate::atom_table::*; use crate::parser::ast::*; +use crate::ffi::FFIError; use crate::forms::*; use crate::machine::heap::*; use crate::machine::loader::CompilationTarget; use crate::machine::machine_state::*; +use crate::machine::streams::*; use crate::machine::system_calls::BrentAlgState; use crate::types::*; @@ -157,9 +160,29 @@ impl PermissionError for HeapCellValue { index_atom: Atom, perm: Permission, ) -> MachineError { + let cell = read_heap_cell!(self, + (HeapCellValueTag::Cons, ptr) => { + match_untyped_arena_ptr!(ptr, + (ArenaHeaderTag::Stream, stream) => { + if let Some(alias) = stream.options().get_alias() { + atom_as_cell!(alias) + } else { + self + } + } + _ => { + self + } + ) + } + _ => { + self + } + ); + let stub = functor!( atom!("permission_error"), - [atom(perm.as_atom()), atom(index_atom), cell(self)] + [atom(perm.as_atom()), atom(index_atom), cell(cell)] ); MachineError { @@ -419,7 +442,7 @@ impl MachineState { // SessionError::CannotOverwriteImport(pred_atom) => { self.permission_error( Permission::Modify, - atom!("private_procedure"), + atom!("static_procedure"), functor_stub(key.0, key.1).into_iter().collect::(), ) } @@ -515,6 +538,24 @@ impl MachineState { } } + pub(super) fn ffi_error(&mut self, err: FFIError) -> MachineError { + let error_atom = match err { + FFIError::ValueCast => atom!("value_cast"), + FFIError::ValueDontFit => atom!("value_dont_fit"), + FFIError::InvalidFFIType => atom!("invalid_ffi_type"), + FFIError::InvalidStructName => atom!("invalid_struct_name"), + FFIError::FunctionNotFound => atom!("function_not_found"), + FFIError::StructNotFound => atom!("struct_not_found"), + }; + let stub = functor!(atom!("ffi_error"),[atom(error_atom)]); + + MachineError { + stub, + location: None, + from: ErrorProvenance::Constructed, + } + } + pub(super) fn error_form(&mut self, err: MachineError, src: FunctorStub) -> MachineStub { let h = self.heap.len(); let location = err.location; @@ -661,7 +702,6 @@ impl CompilationError { functor!(atom!("no_such_module"), [atom(module_name)]) } &CompilationError::InvalidRuleHead => { - functor!(atom!("invalid_head_of_rule")) // TODO: type_error(callable, _). } &CompilationError::InvalidUseModuleDecl => { @@ -780,7 +820,7 @@ pub enum CycleSearchResult { NotList(usize, HeapCellValue), // the list length until the second argument in the heap PartialList(usize, Ref), // the list length (up to max), and an offset into the heap. ProperList(usize), // the list length. - PStrLocation(usize, usize), // list length (up to max), the heap address of the PStrOffset + PStrLocation(usize, usize, usize), // list length (up to max), the heap address of the PStr, the offset UntouchedList(usize, usize), // list length (up to max), the address of an uniterated Addr::Lis(address). UntouchedCStr(Atom, usize), } diff --git a/src/machine/machine_indices.rs b/src/machine/machine_indices.rs index 1fcd41e4..7389caca 100644 --- a/src/machine/machine_indices.rs +++ b/src/machine/machine_indices.rs @@ -2,21 +2,20 @@ use crate::parser::ast::*; use crate::arena::*; use crate::atom_table::*; -use crate::fixtures::*; use crate::forms::*; +use crate::machine::ClauseType; use crate::machine::loader::*; use crate::machine::machine_state::*; use crate::machine::streams::Stream; use fxhash::FxBuildHasher; -use indexmap::IndexMap; +use indexmap::{IndexMap, IndexSet}; use modular_bitfield::{BitfieldSpecifier, bitfield}; use modular_bitfield::specifiers::*; use std::cmp::Ordering; use std::collections::BTreeSet; use std::ops::{Deref, DerefMut}; -use std::rc::Rc; use crate::types::*; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -228,8 +227,32 @@ impl CodeIndex { } } -pub(crate) type HeapVarDict = IndexMap, HeapCellValue, FxBuildHasher>; -pub(crate) type AllocVarDict = IndexMap, VarData, FxBuildHasher>; +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum VarKey { + AnonVar(usize), + VarPtr(VarPtr), +} + +impl VarKey { + #[inline] + pub(crate) fn to_string(&self) -> String { + match self { + VarKey::AnonVar(h) => format!("_{}", h), + VarKey::VarPtr(var) => var.borrow().to_string(), + } + } + + #[inline(always)] + pub(crate) fn is_anon(&self) -> bool { + if let VarKey::AnonVar(_) = self { + true + } else { + false + } + } +} + +pub(crate) type HeapVarDict = IndexMap; pub(crate) type GlobalVarDir = IndexMap), FxBuildHasher>; @@ -245,12 +268,15 @@ pub(crate) type LocalExtensiblePredicates = pub(crate) type CodeDir = IndexMap; +pub(crate) type GoalExpansionIndices = IndexSet; + #[derive(Debug)] pub struct IndexStore { pub(super) code_dir: CodeDir, pub(super) extensible_predicates: ExtensiblePredicates, pub(super) local_extensible_predicates: LocalExtensiblePredicates, pub(super) global_variables: GlobalVarDir, + pub(super) goal_expansion_indices: GoalExpansionIndices, pub(super) meta_predicates: MetaPredicateDir, pub(super) modules: ModuleDir, pub(super) op_dir: OpDir, @@ -259,6 +285,23 @@ pub struct IndexStore { } impl IndexStore { + pub(crate) fn builtin_property(&self, key: PredicateKey) -> bool { + let (name, arity) = key; + + if !ClauseType::is_inbuilt(name, arity) { + self.modules.get(&(atom!("builtins"))) + .map(|module| module.code_dir.contains_key(&(name, arity))) + .unwrap_or(false) + } else { + true + } + } + + #[inline(always)] + pub(crate) fn goal_expansion_defined(&self, key: PredicateKey) -> bool { + self.goal_expansion_indices.contains(&key) + } + pub(crate) fn get_predicate_skeleton_mut( &mut self, compilation_target: &CompilationTarget, @@ -371,22 +414,11 @@ impl IndexStore { module: Atom, ) -> Option { if module == atom!("user") { - /*match ClauseType::from(name, arity) { - ClauseType::Named(arity, name, _) => */ self.code_dir.get(&(name, arity)).cloned() - /* _ => None, - }*/ } else { self.modules .get(&module) - .and_then(|module|/* |module| match ClauseType::from(name, arity) { - ClauseType::Named(arity, name, _) => { */ - module.code_dir.get(&(name, arity)).cloned() - /* - } - _ => None, - } */ - ) + .and_then(|module| module.code_dir.get(&(name, arity)).cloned()) } } diff --git a/src/machine/machine_state.rs b/src/machine/machine_state.rs index 50431a8a..da6f6641 100644 --- a/src/machine/machine_state.rs +++ b/src/machine/machine_state.rs @@ -12,16 +12,16 @@ use crate::machine::machine_indices::*; use crate::machine::stack::*; use crate::machine::streams::*; use crate::parser::ast::*; +use crate::read::TermWriteResult; use crate::types::*; -use crate::parser::rug::Integer; +use crate::parser::dashu::Integer; use indexmap::IndexMap; use std::convert::TryFrom; use std::fmt; use std::ops::{Index, IndexMut}; -use std::rc::Rc; pub(crate) type Registers = [HeapCellValue; MAX_ARITY + 1]; @@ -50,6 +50,12 @@ pub enum FirstOrNext { Next, } +#[derive(Debug)] +pub enum OnEOF { + Return, + Continue, +} + pub struct MachineState { pub atom_tbl: AtomTable, pub arena: Arena, @@ -74,11 +80,12 @@ pub struct MachineState { pub(super) tr: usize, pub(super) hb: usize, pub(super) block: usize, // an offset into the OR stack. + pub(super) scc_block: usize, // an offset into the OR stack for setup_call_cleanup/3. pub(super) ball: Ball, pub(super) ball_stack: Vec, // save current ball before jumping via, e.g., verify_attr interrupt. pub(super) lifted_heap: Heap, pub(super) interms: Vec, // intermediate numbers. - // locations of cleaners, cut points, the previous block. for setup_call_cleanup. + // locations of cleaners, cut points, the previous scc_block. for setup_call_cleanup/3. pub(super) cont_pts: Vec<(HeapCellValue, usize, usize)>, pub(super) cwil: CWIL, pub(crate) flags: MachineFlags, @@ -113,6 +120,7 @@ impl fmt::Debug for MachineState { .field("tr", &self.tr) .field("hb", &self.hb) .field("block", &self.block) + .field("scc_block", &self.scc_block) .field("ball", &self.ball) .field("ball_stack", &self.ball_stack) .field("lifted_heap", &self.lifted_heap) @@ -192,6 +200,27 @@ pub fn pstr_loc_and_offset(heap: &[HeapCellValue], index: usize) -> (usize, Fixn ) } +fn push_var_eq_functors<'a>( + heap: &mut Heap, + iter: impl Iterator, + atom_tbl: &mut AtomTable, +) -> Vec { + let mut list_of_var_eqs = vec![]; + + for (var, binding) in iter { + let var_atom = atom_tbl.build_with(&var.to_string()); + let h = heap.len(); + + heap.push(atom_as_cell!(atom!("="), 2)); + heap.push(atom_as_cell!(var_atom)); + heap.push(*binding); + + list_of_var_eqs.push(str_loc_as_cell!(h)); + } + + list_of_var_eqs +} + #[derive(Debug)] pub struct Ball { pub(super) boundary: usize, @@ -481,6 +510,133 @@ impl MachineState { } } + pub fn write_read_term_options( + &mut self, + mut var_list: Vec<(VarKey, HeapCellValue, usize)>, + singleton_var_list: Vec, + ) -> CallResult { + var_list.sort_by(|(_,_,idx_1),(_,_,idx_2)| idx_1.cmp(idx_2)); + + let list_of_var_eqs = push_var_eq_functors( + &mut self.heap, + var_list.iter().filter_map(|(var_name, var,_)| if var_name.is_anon() { None } else { Some((var_name,var)) }), + &mut self.atom_tbl, + ); + + let singleton_addr = self.registers[3]; + let singletons_offset = heap_loc_as_cell!( + iter_to_heap_list(&mut self.heap, singleton_var_list.into_iter()) + ); + + unify_fn!(*self, singletons_offset, singleton_addr); + + if self.fail { + return Ok(()); + } + + let vars_addr = self.registers[4]; + let vars_offset = heap_loc_as_cell!( + iter_to_heap_list(&mut self.heap, var_list.into_iter().map(|(_,cell,_)| cell)) + ); + + unify_fn!(*self, vars_offset, vars_addr); + + if self.fail { + return Ok(()); + } + + let var_names_addr = self.registers[5]; + let var_names_offset = heap_loc_as_cell!( + iter_to_heap_list(&mut self.heap, list_of_var_eqs.into_iter()) + ); + + Ok(unify_fn!(*self, var_names_offset, var_names_addr)) + } + + pub fn read_term_body(&mut self, mut term_write_result: TermWriteResult) -> CallResult { + let heap_loc = read_heap_cell!(self.heap[term_write_result.heap_loc], + (HeapCellValueTag::PStr | HeapCellValueTag::PStrOffset) => { + pstr_loc_as_cell!(term_write_result.heap_loc) + } + _ => { + heap_loc_as_cell!(term_write_result.heap_loc) + } + ); + + unify_fn!(*self, heap_loc, self.registers[2]); + + if self.fail { + return Ok(()); + } + + for var in term_write_result.var_dict.values_mut() { + *var = heap_bound_deref(&self.heap, *var); + } + + let mut singleton_var_set: IndexMap = IndexMap::new(); + + for cell in stackful_preorder_iter(&mut self.heap, &mut self.stack, heap_loc) { + let cell = unmark_cell_bits!(cell); + + if let Some(var) = cell.as_var() { + if !singleton_var_set.contains_key(&var) { + singleton_var_set.insert(var, true); + } else { + singleton_var_set.insert(var, false); + } + } + } + + let singleton_var_list = push_var_eq_functors( + &mut self.heap, + term_write_result.var_dict.iter().filter(|(var_name, binding)| { + if var_name.is_anon() { + return false; + } + + if let Some(r) = binding.as_var() { + *singleton_var_set.get(&r).unwrap_or(&false) + } else { + false + } + }), + &mut self.atom_tbl, + ); + + for var in term_write_result.var_dict.values_mut() { + *var = heap_bound_deref(&self.heap, *var); + } + + let mut var_list = Vec::with_capacity(singleton_var_set.len()); + + for (var_name, addr) in term_write_result.var_dict { + if let Some(var) = addr.as_var() { + if let Some(idx) = singleton_var_set.get_index_of(&var) { + var_list.push((var_name, addr, idx)); + } + } + } + + self.write_read_term_options(var_list, singleton_var_list) + } + + pub fn read_term_from_user_input_eof_handler(&mut self, stream: Stream) -> Result { + self.eof_action( + self.registers[2], + stream, + atom!("read_term"), + 3, + )?; + + if stream.options().eof_action() == EOFAction::Reset { + if self.fail == false { + return Ok(OnEOF::Continue); + } + } + + Ok(OnEOF::Return) + } + // Safety: the atom_tbl lives for the lifetime of the machine, as does the helper, so the ptr // will always be valid. pub fn read_term_from_user_input(&mut self, stream: Stream, indices: &mut IndexStore) -> CallResult { @@ -490,40 +646,54 @@ impl MachineState { unsafe { let readline = ptr.as_ptr().as_mut().unwrap(); readline.set_atoms_for_completion(atoms_ptr); - let ret = self.read_term(stream, indices); - return ret + return self.read_term( + stream, + indices, + MachineState::read_term_from_user_input_eof_handler, + ); } } if let Stream::Byte(_) = stream { - return self.read_term(stream, indices) + return self.read_term( + stream, + indices, + MachineState::read_term_from_user_input_eof_handler + ) } unreachable!("Stream must be a Stream::Readline(_)") } - pub fn read_term(&mut self, stream: Stream, indices: &mut IndexStore) -> CallResult { - fn push_var_eq_functors<'a>( - heap: &mut Heap, - iter: impl Iterator, &'a HeapCellValue)>, - atom_tbl: &mut AtomTable, - ) -> Vec { - let mut list_of_var_eqs = vec![]; + pub fn read_term_eof_handler(&mut self, mut stream: Stream) -> Result { + if stream.at_end_of_stream() { + unify!(self, self.registers[2], atom_as_cell!(atom!("end_of_file"))); + stream.set_past_end_of_stream(true); + return Ok(OnEOF::Return); + } else if stream.past_end_of_stream() { + self.eof_action( + self.registers[2], + stream, + atom!("read_term"), + 3, + )?; - for (var, binding) in iter { - let var_atom = atom_tbl.build_with(&var); - let h = heap.len(); - - heap.push(atom_as_cell!(atom!("="), 2)); - heap.push(atom_as_cell!(var_atom)); - heap.push(*binding); - - list_of_var_eqs.push(str_loc_as_cell!(h)); + if stream.options().eof_action() == EOFAction::Reset { + if self.fail == false { + return Ok(OnEOF::Continue); + } } - - list_of_var_eqs } + Ok(OnEOF::Return) + } + + pub fn read_term( + &mut self, + stream: Stream, + indices: &mut IndexStore, + eof_handler: impl Fn(&mut Self, Stream) -> Result, + ) -> CallResult { self.check_stream_properties( stream, StreamType::Text, @@ -542,116 +712,16 @@ impl MachineState { loop { match self.read(stream, &indices.op_dir) { - Ok(mut term_write_result) => { - let heap_loc = read_heap_cell!(self.heap[term_write_result.heap_loc], - (HeapCellValueTag::PStr | HeapCellValueTag::PStrOffset) => { - pstr_loc_as_cell!(term_write_result.heap_loc) - } - _ => { - heap_loc_as_cell!(term_write_result.heap_loc) - } - ); - - let term = self.registers[2]; - unify_fn!(*self, heap_loc, term); - let term = heap_loc; - - if self.fail { - return Ok(()); - } - - let mut singleton_var_set: IndexMap = IndexMap::new(); - - for addr in stackful_preorder_iter(&mut self.heap, term) { - let addr = unmark_cell_bits!(addr); - - if let Some(var) = addr.as_var() { - if !singleton_var_set.contains_key(&var) { - singleton_var_set.insert(var, true); - } else { - singleton_var_set.insert(var, false); - } - } - } - - for var in term_write_result.var_dict.values_mut() { - *var = heap_bound_deref(&self.heap, *var); - } - - let singleton_var_list = push_var_eq_functors( - &mut self.heap, - term_write_result.var_dict.iter().filter(|(_, binding)| { - if let Some(r) = binding.as_var() { - *singleton_var_set.get(&r).unwrap_or(&false) - } else { - false - } - }), - &mut self.atom_tbl, - ); - - let mut var_list = Vec::with_capacity(singleton_var_set.len()); - - for (var_name, addr) in term_write_result.var_dict { - if let Some(var) = addr.as_var() { - let idx = singleton_var_set.get_index_of(&var).unwrap(); - var_list.push((var_name, addr, idx)); - } - } - - var_list.sort_by(|(_,_,idx_1),(_,_,idx_2)| idx_1.cmp(idx_2)); - - let list_of_var_eqs = push_var_eq_functors( - &mut self.heap, - var_list.iter().map(|(var_name, var,_)| (var_name,var)), - &mut self.atom_tbl, - ); - - let singleton_addr = self.registers[3]; - let singletons_offset = heap_loc_as_cell!( - iter_to_heap_list(&mut self.heap, singleton_var_list.into_iter()) - ); - - unify_fn!(*self, singletons_offset, singleton_addr); - - if self.fail { - return Ok(()); - } - - let vars_addr = self.registers[4]; - let vars_offset = heap_loc_as_cell!( - iter_to_heap_list(&mut self.heap, var_list.into_iter().map(|(_,cell,_)| cell)) - ); - - unify_fn!(*self, vars_offset, vars_addr); - - if self.fail { - return Ok(()); - } - - let var_names_addr = self.registers[5]; - let var_names_offset = heap_loc_as_cell!( - iter_to_heap_list(&mut self.heap, list_of_var_eqs.into_iter()) - ); - - return Ok(unify_fn!(*self, var_names_offset, var_names_addr)); - } + Ok(term_write_result) => return self.read_term_body(term_write_result), Err(err) => { - if let CompilationError::ParserError(ParserError::UnexpectedEOF) = err { - self.eof_action( - self.registers[2], - stream, - atom!("read_term"), - 3, - )?; - - if stream.options().eof_action() == EOFAction::Reset { - if self.fail == false { - continue; + match &err { + CompilationError::ParserError(e) if e.is_unexpected_eof() => { + match eof_handler(self, stream)? { + OnEOF::Return => return self.write_read_term_options(vec![], vec![]), + OnEOF::Continue => continue, } } - - return Ok(()); + _ => {} } let stub = functor_stub(atom!("read_term"), 3); @@ -671,13 +741,14 @@ impl MachineState { let numbervars = self.store(self.deref(self.registers[4])); let quoted = self.store(self.deref(self.registers[5])); let max_depth = self.store(self.deref(self.registers[7])); + let double_quotes = self.store(self.deref(self.registers[8])); let term_to_be_printed = self.store(self.deref(self.registers[2])); let stub_gen = || functor_stub(atom!("write_term"), 2); let printer = match self.try_from_list(self.registers[6], stub_gen) { Ok(addrs) => { - let mut var_names: IndexMap> = IndexMap::new(); + let mut var_names: IndexMap = IndexMap::new(); for addr in addrs { read_heap_cell!(addr, @@ -695,18 +766,18 @@ impl MachineState { read_heap_cell!(atom, (HeapCellValueTag::Char, c) => { - var_names.insert(var, Rc::new(c.to_string())); + var_names.insert(var, VarPtr::from(c.to_string())); } (HeapCellValueTag::Atom, (name, _arity)) => { debug_assert_eq!(_arity, 0); - var_names.insert(var, Rc::new(name.as_str().to_owned())); + var_names.insert(var, VarPtr::from(name.as_str())); } (HeapCellValueTag::Str, s) => { let (name, arity) = cell_as_atom_cell!(self.heap[s]) .get_name_and_arity(); debug_assert_eq!(arity, 0); - var_names.insert(var, Rc::new(name.as_str().to_owned())); + var_names.insert(var, VarPtr::from(name.as_str())); } _ => { unreachable!(); @@ -752,7 +823,25 @@ impl MachineState { ); let quoted = read_heap_cell!(quoted, - (HeapCellValueTag::Atom, (name, _arity)) => { + (HeapCellValueTag::Atom, (name, arity)) => { + debug_assert_eq!(arity, 0); + name == atom!("true") + } + (HeapCellValueTag::Str, s) => { + let (name, arity) = cell_as_atom_cell!(self.heap[s]) + .get_name_and_arity(); + + debug_assert_eq!(arity, 0); + name == atom!("true") + } + _ => { + unreachable!() + } + ); + + let double_quotes = read_heap_cell!(double_quotes, + (HeapCellValueTag::Atom, (name, arity)) => { + debug_assert_eq!(arity, 0); name == atom!("true") } (HeapCellValueTag::Str, s) => { @@ -769,6 +858,8 @@ impl MachineState { let mut printer = HCPrinter::new( &mut self.heap, + &mut self.atom_tbl, + &mut self.stack, op_dir, PrinterOutputter::new(), term_to_be_printed, @@ -777,6 +868,7 @@ impl MachineState { printer.ignore_ops = ignore_ops; printer.numbervars = numbervars; printer.quoted = quoted; + printer.double_quotes = double_quotes; match Number::try_from(max_depth) { Ok(Number::Fixnum(n)) => { @@ -824,7 +916,7 @@ impl MachineState { let b = self.b; read_heap_cell!(value, - (HeapCellValueTag::Fixnum, b0) => { + (HeapCellValueTag::CutPoint, b0) => { let b0 = b0.get_num() as usize; if b > b0 { @@ -836,63 +928,6 @@ impl MachineState { } ); } - - #[inline(always)] - pub(super) fn try_me_else(&mut self, offset: usize) { - let n = self.num_of_args; - let b = self.stack.allocate_or_frame(n); - let or_frame = self.stack.index_or_frame_mut(b); - - or_frame.prelude.univ_prelude.num_cells = n; - or_frame.prelude.e = self.e; - or_frame.prelude.cp = self.cp; - or_frame.prelude.b = self.b; - or_frame.prelude.bp = self.p + offset; - or_frame.prelude.boip = 0; - or_frame.prelude.biip = 0; - or_frame.prelude.tr = self.tr; - or_frame.prelude.h = self.heap.len(); - or_frame.prelude.b0 = self.b0; - - self.b = b; - - for i in 0..n { - or_frame[i] = self.registers[i+1]; - } - - self.hb = self.heap.len(); - self.p += 1; - } - - #[inline(always)] - pub(super) fn indexed_try(&mut self, offset: usize) { - let n = self.num_of_args; - let b = self.stack.allocate_or_frame(n); - let or_frame = self.stack.index_or_frame_mut(b); - - or_frame.prelude.univ_prelude.num_cells = n; - or_frame.prelude.e = self.e; - or_frame.prelude.cp = self.cp; - or_frame.prelude.b = self.b; - or_frame.prelude.bp = self.p; // + 1; in self.iip now! - or_frame.prelude.boip = self.oip; - or_frame.prelude.biip = self.iip + 1; - or_frame.prelude.tr = self.tr; - or_frame.prelude.h = self.heap.len(); - or_frame.prelude.b0 = self.b0; - - self.b = b; - - for i in 0..n { - or_frame[i] = self.registers[i+1]; - } - - self.hb = self.heap.len(); - self.p = self.p + offset; - - self.oip = 0; - self.iip = 0; - } } #[derive(Debug)] diff --git a/src/machine/machine_state_impl.rs b/src/machine/machine_state_impl.rs index 00e17649..90036db6 100644 --- a/src/machine/machine_state_impl.rs +++ b/src/machine/machine_state_impl.rs @@ -11,10 +11,10 @@ use crate::machine::machine_indices::*; use crate::machine::machine_state::*; use crate::machine::partial_string::*; use crate::machine::stack::*; +use crate::machine::unify::*; use crate::parser::ast::*; -use crate::parser::rug::{Integer, Rational}; +use crate::parser::dashu::{Integer, Rational}; -use fxhash::FxBuildHasher; use indexmap::IndexSet; use std::cmp::Ordering; @@ -46,6 +46,7 @@ impl MachineState { tr: 0, hb: 0, block: 0, + scc_block: 0, ball: Ball::new(), ball_stack: vec![], lifted_heap: Heap::new(), @@ -130,16 +131,6 @@ impl MachineState { } } } - TrailRef::AttrVarHeapLink(h) => { - if h < self.hb { - self.trail.push(TrailEntry::build_with( - TrailEntryTag::TrailedAttrVarHeapLink, - h as u64, - )); - - self.tr += 1; - } - } TrailRef::AttrVarListLink(h, l) => { if h < self.hb { self.trail.push(TrailEntry::build_with( @@ -245,634 +236,101 @@ impl MachineState { ) } - fn unify_structure(&mut self, s1: usize, value: HeapCellValue) { - // s1 is the value of a STR cell. - let (n1, a1) = cell_as_atom_cell!(self.heap[s1]).get_name_and_arity(); - - read_heap_cell!(value, - (HeapCellValueTag::Str, s2) => { - let (n2, a2) = cell_as_atom_cell!(self.heap[s2]) - .get_name_and_arity(); - - if n1 == n2 && a1 == a2 { - for idx in (0..a1).rev() { - self.pdl.push(heap_loc_as_cell!(s2+1+idx)); - self.pdl.push(heap_loc_as_cell!(s1+1+idx)); - } - } else { - self.fail = true; - } - } - (HeapCellValueTag::Lis, l2) => { - if a1 == 2 && n1 == atom!(".") { - for idx in (0..2).rev() { - self.pdl.push(heap_loc_as_cell!(l2+1+idx)); - self.pdl.push(heap_loc_as_cell!(s1+1+idx)); - } - } else { - self.fail = true; - } - } - (HeapCellValueTag::Atom, (n2, a2)) => { - if !(a1 == 0 && a2 == 0 && n1 == n2) { - self.fail = true; - } - } - (HeapCellValueTag::AttrVar, h) => { - self.bind(Ref::attr_var(h), str_loc_as_cell!(s1)); - } - (HeapCellValueTag::Var, h) => { - self.bind(Ref::heap_cell(h), str_loc_as_cell!(s1)); - } - (HeapCellValueTag::StackVar, s) => { - self.bind(Ref::stack_cell(s), str_loc_as_cell!(s1)); - } - _ => { - self.fail = true; - } - ) + #[inline] + pub(super) fn bind_with_occurs_check_wrapper(&mut self, r: Ref, value: HeapCellValue) { + let mut unifier = CompositeUnifierForOccursCheck::from(DefaultUnifier::from(self)); + unifier.bind(r, value); } - fn unify_list(&mut self, l1: usize, d2: HeapCellValue) { - read_heap_cell!(d2, - (HeapCellValueTag::Lis, l2) => { - for idx in (0..2).rev() { - self.pdl.push(heap_loc_as_cell!(l2 + idx)); - self.pdl.push(heap_loc_as_cell!(l1 + idx)); - } - } - (HeapCellValueTag::Str, s2) => { - let (n2, a2) = cell_as_atom_cell!(self.heap[s2]) - .get_name_and_arity(); - - if a2 == 2 && n2 == atom!(".") { - for idx in (0..2).rev() { - self.pdl.push(heap_loc_as_cell!(s2+1+idx)); - self.pdl.push(heap_loc_as_cell!(l1+idx)); - } - } else { - self.fail = true; - } - } - (HeapCellValueTag::PStrLoc | HeapCellValueTag::CStr | HeapCellValueTag::PStr) => { - self.unify_partial_string(list_loc_as_cell!(l1), d2) - } - (HeapCellValueTag::AttrVar, h) => { - self.bind(Ref::attr_var(h), list_loc_as_cell!(l1)); - } - (HeapCellValueTag::Var, h) => { - self.bind(Ref::heap_cell(h), list_loc_as_cell!(l1)); - } - (HeapCellValueTag::StackVar, s) => { - self.bind(Ref::stack_cell(s), list_loc_as_cell!(l1)); - } - _ => { - self.fail = true; - } - ) - } - - pub fn unify_complete_string(&mut self, atom: Atom, value: HeapCellValue) { - if let Some(r) = value.as_var() { - if atom == atom!("") { - self.bind(r, atom_as_cell!(atom!("[]"))); - } else { - self.bind(r, atom_as_cstr_cell!(atom)); - } - - return; - } - - read_heap_cell!(value, - (HeapCellValueTag::Atom, (cstr_atom, arity)) if atom == atom!("") => { - debug_assert_eq!(arity, 0); - self.fail = cstr_atom != atom!("[]"); - } - (HeapCellValueTag::Str, s) => { - let (name, arity) = cell_as_atom_cell!(self.heap[s]) - .get_name_and_arity(); - - if arity == 0 { - self.fail = atom == atom!("") && name != atom!("[]"); - } else { - // this is intentionally the same policy for - // value.tag() == Lis and PStrLoc. they're not - // grouped together to allow for arity == 0. - self.unify_partial_string(atom_as_cstr_cell!(atom), value); - - if !self.pdl.is_empty() { - self.unify(); - } - } - } - (HeapCellValueTag::CStr, cstr_atom) => { - self.fail = atom != cstr_atom; - } - (HeapCellValueTag::Lis | HeapCellValueTag::PStrLoc) => { - self.unify_partial_string(atom_as_cstr_cell!(atom), value); - - if !self.pdl.is_empty() { - self.unify(); - } - } - _ => { - self.fail = true; - } + #[inline] + pub(super) fn bind_with_occurs_check_with_error_wrapper( + &mut self, + r: Ref, + value: HeapCellValue, + ) { + let mut unifier = CompositeUnifierForOccursCheckWithError::from( + DefaultUnifier::from(self), ); - } - // d1's tag is LIS, STR or PSTRLOC. - pub fn unify_partial_string(&mut self, d1: HeapCellValue, d2: HeapCellValue) { - if let Some(r) = d2.as_var() { - self.bind(r, d1); - return; - } - - let s1 = self.heap.len(); - - self.heap.push(d1); - self.heap.push(d2); - - let mut pstr_iter1 = HeapPStrIter::new(&self.heap, s1); - let mut pstr_iter2 = HeapPStrIter::new(&self.heap, s1 + 1); - - match compare_pstr_prefixes(&mut pstr_iter1, &mut pstr_iter2) { - PStrCmpResult::Ordered(Ordering::Equal) => {} - PStrCmpResult::Ordered(Ordering::Less) => { - if pstr_iter2.focus.as_var().is_none() { - self.fail = true; - } else { - self.pdl.push(empty_list_as_cell!()); - self.pdl.push(pstr_iter2.focus); - } - } - PStrCmpResult::Ordered(Ordering::Greater) => { - if pstr_iter1.focus.as_var().is_none() { - self.fail = true; - } else { - self.pdl.push(empty_list_as_cell!()); - self.pdl.push(pstr_iter1.focus); - } - } - continuable @ PStrCmpResult::FirstIterContinuable(iteratee) | - continuable @ PStrCmpResult::SecondIterContinuable(iteratee) => { - if continuable.is_second_iter() { - std::mem::swap(&mut pstr_iter1, &mut pstr_iter2); - } - - let mut chars_iter = PStrCharsIter { - iter: pstr_iter1, - item: Some(iteratee), - }; - - let mut focus = pstr_iter2.focus; - - 'outer: loop { - while let Some(c) = chars_iter.peek() { - read_heap_cell!(focus, - (HeapCellValueTag::Lis, l) => { - let val = pstr_iter2.heap[l]; - - self.pdl.push(val); - self.pdl.push(char_as_cell!(c)); - - focus = pstr_iter2.heap[l+1]; - } - (HeapCellValueTag::Str, s) => { - let (name, arity) = cell_as_atom_cell!(pstr_iter2.heap[s]) - .get_name_and_arity(); - - if name == atom!(".") && arity == 2 { - self.pdl.push(pstr_iter2.heap[s+1]); - self.pdl.push(char_as_cell!(c)); - - focus = pstr_iter2.heap[s+2]; - } else { - self.fail = true; - break 'outer; - } - } - (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => { - match chars_iter.item.unwrap() { - PStrIteratee::Char(focus, _) => { - self.pdl.push(self.heap[focus]); - self.pdl.push(heap_loc_as_cell!(h)); - } - PStrIteratee::PStrSegment(focus, _, n) => { - read_heap_cell!(self.heap[focus], - (HeapCellValueTag::CStr | HeapCellValueTag::PStr, pstr_atom) => { - if focus < self.heap.len() - 2 { - self.heap.pop(); - self.heap.pop(); - } - - if n == 0 { - let target_cell = match self.heap[focus].get_tag() { - HeapCellValueTag::CStr => { - atom_as_cstr_cell!(pstr_atom) - } - HeapCellValueTag::PStr => { - pstr_loc_as_cell!(focus) - } - _ => { - unreachable!() - } - }; - - self.pdl.push(target_cell); - self.pdl.push(heap_loc_as_cell!(h)); - } else { - let h_len = self.heap.len(); - - self.heap.push(pstr_offset_as_cell!(focus)); - self.heap.push(fixnum_as_cell!( - Fixnum::build_with(n as i64) - )); - - self.pdl.push(pstr_loc_as_cell!(h_len)); - self.pdl.push(heap_loc_as_cell!(h)); - } - - return; - } - (HeapCellValueTag::PStrOffset, pstr_loc) => { - let n0 = cell_as_fixnum!(self.heap[focus+1]) - .get_num() as usize; - - if pstr_loc < self.heap.len() - 2 { - self.heap.pop(); - self.heap.pop(); - } - - if n == n0 { - self.pdl.push(pstr_loc_as_cell!(focus)); - self.pdl.push(heap_loc_as_cell!(h)); - } else { - let h_len = self.heap.len(); - - self.heap.push(pstr_offset_as_cell!(pstr_loc)); - self.heap.push(fixnum_as_cell!( - Fixnum::build_with(n as i64) - )); - - self.pdl.push(pstr_loc_as_cell!(h_len)); - self.pdl.push(heap_loc_as_cell!(h)); - } - - return; - } - _ => { - } - ); - - if focus < self.heap.len() - 2 { - self.heap.pop(); - self.heap.pop(); - } - - self.pdl.push(self.heap[focus]); - self.pdl.push(heap_loc_as_cell!(h)); - - return; - } - } - - break 'outer; - } - _ => { - self.fail = true; - break 'outer; - } - ); - - chars_iter.next(); - } - - chars_iter.iter.next(); - - self.pdl.push(focus); - self.pdl.push(chars_iter.iter.focus); - - break; - } - } - PStrCmpResult::Unordered => { - self.pdl.push(pstr_iter1.focus); - self.pdl.push(pstr_iter2.focus); - } - } - - self.heap.pop(); - self.heap.pop(); - } - - pub fn unify_atom(&mut self, atom: Atom, value: HeapCellValue) { - read_heap_cell!(value, - (HeapCellValueTag::Atom, (name, arity)) => { - self.fail = !(arity == 0 && name == atom); - } - (HeapCellValueTag::Str, s) => { - let (name, arity) = cell_as_atom_cell!(self.heap[s]) - .get_name_and_arity(); - - self.fail = !(arity == 0 && name == atom); - } - (HeapCellValueTag::CStr, cstr_atom) if atom == atom!("[]") => { - self.fail = cstr_atom != atom!(""); - } - (HeapCellValueTag::Char, c1) => { - if let Some(c2) = atom.as_char() { - self.fail = c1 != c2; - } else { - self.fail = true; - } - } - (HeapCellValueTag::AttrVar, h) => { - self.bind(Ref::attr_var(h), atom_as_cell!(atom)); - } - (HeapCellValueTag::Var, h) => { - self.bind(Ref::heap_cell(h), atom_as_cell!(atom)); - } - (HeapCellValueTag::StackVar, s) => { - self.bind(Ref::stack_cell(s), atom_as_cell!(atom)); - } - _ => { - self.fail = true; - } - ); - } - - pub fn unify_char(&mut self, c: char, value: HeapCellValue) { - read_heap_cell!(value, - (HeapCellValueTag::Atom, (name, arity)) => { - if let Some(c2) = name.as_char() { - self.fail = !(c == c2 && arity == 0); - } else { - self.fail = true; - } - } - (HeapCellValueTag::Str, s) => { - let (name, arity) = cell_as_atom_cell!(self.heap[s]) - .get_name_and_arity(); - - if let Some(c2) = name.as_char() { - self.fail = !(c == c2 && arity == 0); - } else { - self.fail = true; - } - } - (HeapCellValueTag::Char, c2) => { - if c != c2 { - self.fail = true; - } - } - (HeapCellValueTag::AttrVar, h) => { - self.bind(Ref::attr_var(h), char_as_cell!(c)); - } - (HeapCellValueTag::Var, h) => { - self.bind(Ref::heap_cell(h), char_as_cell!(c)); - } - (HeapCellValueTag::StackVar, s) => { - self.bind(Ref::stack_cell(s), char_as_cell!(c)); - } - _ => { - self.fail = true; - } - ); - } - - pub fn unify_fixnum(&mut self, n1: Fixnum, value: HeapCellValue) { - if let Some(r) = value.as_var() { - self.bind(r, fixnum_as_cell!(n1)); - return; - } - - match Number::try_from(value) { - Ok(n2) => match n2 { - Number::Fixnum(n2) if n1.get_num() == n2.get_num() => {} - Number::Integer(n2) if n1.get_num() == *n2 => {} - Number::Rational(n2) if n1.get_num() == *n2 => {} - _ => { - self.fail = true; - } - }, - Err(_) => { - self.fail = true; - } - } - } - - pub fn unify_big_int(&mut self, n1: TypedArenaPtr, value: HeapCellValue) { - if let Some(r) = value.as_var() { - self.bind(r, typed_arena_ptr_as_cell!(n1)); - return; - } - - match Number::try_from(value) { - Ok(n2) => match n2 { - Number::Fixnum(n2) if *n1 == n2.get_num() => {} - Number::Integer(n2) if *n1 == *n2 => {} - Number::Rational(n2) if *n1 == *n2 => {} - _ => { - self.fail = true; - } - }, - Err(_) => { - self.fail = true; - } - } - } - - pub fn unify_rational(&mut self, n1: TypedArenaPtr, value: HeapCellValue) { - if let Some(r) = value.as_var() { - self.bind(r, typed_arena_ptr_as_cell!(n1)); - return; - } - - match Number::try_from(value) { - Ok(n2) => match n2 { - Number::Fixnum(n2) if *n1 == n2.get_num() => {} - Number::Integer(n2) if *n1 == *n2 => {} - Number::Rational(n2) if *n1 == *n2 => {} - _ => { - self.fail = true; - } - }, - Err(_) => { - self.fail = true; - } - } - } - - pub fn unify_f64(&mut self, f1: F64Ptr, value: HeapCellValue) { - if let Some(r) = value.as_var() { - self.bind(r, HeapCellValue::from(f1)); - return; - } - - read_heap_cell!(value, - (HeapCellValueTag::F64, f2) => { - self.fail = **f1 != **f2; - } - _ => { - self.fail = true; - } - ); - } - - pub fn unify_constant(&mut self, ptr: UntypedArenaPtr, value: HeapCellValue) { - if let Some(ptr2) = value.to_untyped_arena_ptr() { - if ptr.get_ptr() == ptr2.get_ptr() { - return; - } - } - - match_untyped_arena_ptr!(ptr, - (ArenaHeaderTag::Integer, int_ptr) => { - self.unify_big_int(int_ptr, value); - } - (ArenaHeaderTag::Rational, rat_ptr) => { - self.unify_rational(rat_ptr, value); - } - _ => { - if let Some(r) = value.as_var() { - self.bind(r, untyped_arena_ptr_as_cell!(ptr)); - } else { - self.fail = true; - } - } - ); + unifier.bind(r, value); } pub fn unify(&mut self) { - let mut tabu_list = IndexSet::with_hasher(FxBuildHasher::default()); + let mut unifier = DefaultUnifier::from(self); + unifier.unify_internal(); + } - while !(self.pdl.is_empty() || self.fail) { - let s1 = self.pdl.pop().unwrap(); - let s1 = self.deref(s1); + pub fn unify_structure(&mut self, s1: usize, value: HeapCellValue) { + let mut unifier = DefaultUnifier::from(self); + unifier.unify_structure(s1, value); + } - let s2 = self.pdl.pop().unwrap(); - let s2 = self.deref(s2); + pub fn unify_atom(&mut self, atom: Atom, value: HeapCellValue) { + let mut unifier = DefaultUnifier::from(self); + unifier.unify_atom(atom, value); + } - if s1 != s2 { - let d1 = self.store(s1); - let d2 = self.store(s2); + pub fn unify_list(&mut self, l1: usize, value: HeapCellValue) { + let mut unifier = DefaultUnifier::from(self); + unifier.unify_list(l1, value); + } - read_heap_cell!(d1, - (HeapCellValueTag::AttrVar, h) => { - self.bind(Ref::attr_var(h), d2); - } - (HeapCellValueTag::Var, h) => { - self.bind(Ref::heap_cell(h), d2); - } - (HeapCellValueTag::StackVar, s) => { - self.bind(Ref::stack_cell(s), d2); - } - (HeapCellValueTag::Atom, (name, arity)) => { - debug_assert!(arity == 0); - self.unify_atom(name, d2); - } - (HeapCellValueTag::Str, s1) => { - if tabu_list.contains(&(d1, d2)) { - continue; - } + pub fn unify_complete_string(&mut self, atom: Atom, value: HeapCellValue) { + let mut unifier = DefaultUnifier::from(self); + unifier.unify_complete_string(atom, value); + } - self.unify_structure(s1, d2); + pub fn unify_partial_string(&mut self, value_1: HeapCellValue, value_2: HeapCellValue) { + let mut unifier = DefaultUnifier::from(self); + unifier.unify_partial_string(value_1, value_2); + } - if !self.fail { - let d2 = self.store(d2); - tabu_list.insert((d1, d2)); - } - } - (HeapCellValueTag::Lis, l1) => { - if d2.is_ref() { - if tabu_list.contains(&(d1, d2)) { - continue; - } - } + pub fn unify_char(&mut self, c: char, value: HeapCellValue) { + let mut unifier = DefaultUnifier::from(self); + unifier.unify_char(c, value); + } - self.unify_list(l1, d2); + pub fn unify_fixnum(&mut self, n1: Fixnum, value: HeapCellValue) { + let mut unifier = DefaultUnifier::from(self); + unifier.unify_fixnum(n1, value); + } - if !self.fail { - let d2 = self.store(d2); - tabu_list.insert((d1, d2)); - } - } - (HeapCellValueTag::PStrLoc) => { - read_heap_cell!(d2, - (HeapCellValueTag::PStrLoc | - HeapCellValueTag::Lis | - HeapCellValueTag::Str) => { - if tabu_list.contains(&(d1, d2)) { - continue; - } - } - (HeapCellValueTag::CStr | - HeapCellValueTag::AttrVar | - HeapCellValueTag::Var | - HeapCellValueTag::StackVar) => { - } - _ => { - self.fail = true; - break; - } - ); + pub fn unify_big_int(&mut self, n1: TypedArenaPtr, value: HeapCellValue) { + let mut unifier = DefaultUnifier::from(self); + unifier.unify_big_num(n1, value); + } - self.unify_partial_string(d1, d2); + pub fn unify_rational(&mut self, n1: TypedArenaPtr, value: HeapCellValue) { + let mut unifier = DefaultUnifier::from(self); + unifier.unify_big_num(n1, value); + } - if !self.fail && !d2.is_constant() { - let d2 = self.store(d2); - tabu_list.insert((d1, d2)); - } - } - (HeapCellValueTag::CStr) => { - read_heap_cell!(d2, - (HeapCellValueTag::AttrVar, h) => { - self.bind(Ref::attr_var(h), d1); - continue; - } - (HeapCellValueTag::Var, h) => { - self.bind(Ref::heap_cell(h), d1); - continue; - } - (HeapCellValueTag::StackVar, s) => { - self.bind(Ref::stack_cell(s), d1); - continue; - } - (HeapCellValueTag::Str | - HeapCellValueTag::Lis | - HeapCellValueTag::PStrLoc) => { - } - (HeapCellValueTag::CStr) => { - self.fail = d1 != d2; - continue; - } - _ => { - self.fail = true; - return; - } - ); + pub fn unify_f64(&mut self, f1: F64Ptr, value: HeapCellValue) { + let mut unifier = DefaultUnifier::from(self); + unifier.unify_f64(f1, value); + } - self.unify_partial_string(d2, d1); - } - (HeapCellValueTag::F64, f1) => { - self.unify_f64(f1, d2); - } - (HeapCellValueTag::Fixnum, n1) => { - self.unify_fixnum(n1, d2); - } - (HeapCellValueTag::Char, c1) => { - self.unify_char(c1, d2); - } - (HeapCellValueTag::Cons, ptr_1) => { - self.unify_constant(ptr_1, d2); - } - _ => { - unreachable!(); - } - ); - } - } + pub fn unify_constant(&mut self, ptr: UntypedArenaPtr, value: HeapCellValue) { + let mut unifier = DefaultUnifier::from(self); + unifier.unify_constant(ptr, value); + } + + pub(super) fn unify_with_occurs_check_with_error(&mut self) { + let mut unifier = CompositeUnifierForOccursCheckWithError::from( + DefaultUnifier::from(self), + ); + + unifier.unify_internal(); + } + + pub(super) fn unify_with_occurs_check(&mut self) { + let mut unifier = CompositeUnifierForOccursCheck::from(DefaultUnifier::from(self)); + unifier.unify_internal(); + } + + #[inline(always)] + pub(super) fn effective_block(&self) -> usize { + std::cmp::max(self.block, self.scc_block) } pub(super) fn set_ball(&mut self) { @@ -888,532 +346,12 @@ impl MachineState { ); } + #[inline(always)] pub(super) fn unwind_stack(&mut self) { - self.b = self.block; + self.b = self.effective_block(); self.fail = true; } - #[inline] - pub fn bind_with_occurs_check(&mut self, r: Ref, value: HeapCellValue) -> bool { - if let RefTag::StackCell = r.get_tag() { - // local variable optimization -- r cannot occur in the - // heap structure bound to value, so don't bother - // traversing value. - self.bind(r, value); - return false; - } - - let mut occurs_triggered = false; - - if !value.is_constant() { - for addr in stackful_preorder_iter(&mut self.heap, value) { - let addr = unmark_cell_bits!(addr); - - if let Some(inner_r) = addr.as_var() { - if r == inner_r { - occurs_triggered = true; - break; - } - } - } - } - - if occurs_triggered { - self.fail = true; - } else { - self.bind(r, value); - } - - return occurs_triggered; - } - - #[inline] - pub(super) fn bind_with_occurs_check_wrapper(&mut self, r: Ref, value: HeapCellValue) { - self.bind_with_occurs_check(r, value); - } - - #[inline] - pub(super) fn bind_with_occurs_check_with_error_wrapper( - &mut self, - r: Ref, - value: HeapCellValue, - ) { - if self.bind_with_occurs_check(r, value) { - let err = self.representation_error(RepFlag::Term); - let stub = functor_stub(atom!("unify_with_occurs_check"), 2); - let err = self.error_form(err, stub); - - self.throw_exception(err); - } - } - - pub(super) fn unify_with_occurs_check_with_error(&mut self) { - let mut throw_error = false; - self.unify_with_occurs_check_loop(|| throw_error = true); - - if throw_error { - let err = self.representation_error(RepFlag::Term); - let stub = functor_stub(atom!("unify_with_occurs_check"), 2); - let err = self.error_form(err, stub); - - self.throw_exception(err); - } - } - - pub(super) fn unify_with_occurs_check(&mut self) { - self.unify_with_occurs_check_loop(|| {}) - } - - fn unify_structure_with_occurs_check( - &mut self, - s1: usize, - value: HeapCellValue, - mut occurs_trigger: impl FnMut(), - ) { - // s1 is the value of a STR cell. - let (n1, a1) = cell_as_atom_cell!(self.heap[s1]).get_name_and_arity(); - - read_heap_cell!(value, - (HeapCellValueTag::Str, s2) => { - let (n2, a2) = cell_as_atom_cell!(self.heap[s2]) - .get_name_and_arity(); - - if n1 == n2 && a1 == a2 { - for idx in (0..a1).rev() { - self.pdl.push(heap_loc_as_cell!(s2+1+idx)); - self.pdl.push(heap_loc_as_cell!(s1+1+idx)); - } - } else { - self.fail = true; - } - } - (HeapCellValueTag::Lis, l2) => { - if a1 == 2 && n1 == atom!(".") { - for idx in (0..2).rev() { - self.pdl.push(heap_loc_as_cell!(l2+idx)); - self.pdl.push(heap_loc_as_cell!(s1+1+idx)); - } - } else { - self.fail = true; - } - } - (HeapCellValueTag::Atom, (n2, a2)) => { - self.fail = !(a1 == 0 && a2 == 0 && n1 == n2); - } - (HeapCellValueTag::AttrVar, h) => { - if self.bind_with_occurs_check(Ref::attr_var(h), str_loc_as_cell!(s1)) { - occurs_trigger(); - } - } - (HeapCellValueTag::Var, h) => { - if self.bind_with_occurs_check(Ref::heap_cell(h), str_loc_as_cell!(s1)) { - occurs_trigger(); - } - } - (HeapCellValueTag::StackVar, s) => { - if self.bind_with_occurs_check(Ref::stack_cell(s), str_loc_as_cell!(s1)) { - occurs_trigger(); - } - } - _ => { - self.fail = true; - } - ) - } - - // the return value of unify_partial_string_with_occurs_check is - // interpreted as follows: - // - // Some(None) -- the strings are equal, nothing to unify - // Some(Some(f2,f1)) -- prefixes equal, try to unify focus values f2, f1 - // None -- prefixes not equal, unification fails - // - // d1's tag is assumed to be one of LIS, STR or PSTRLOC. - pub fn unify_partial_string_with_occurs_check( - &mut self, - d1: HeapCellValue, - d2: HeapCellValue, - mut occurs_trigger: impl FnMut(), - ) { - if let Some(r) = d2.as_var() { - if self.bind_with_occurs_check(r, d1) { - occurs_trigger(); - } - - return; - } - - let s1 = self.heap.len(); - - self.heap.push(d1); - self.heap.push(d2); - - let mut pstr_iter1 = HeapPStrIter::new(&self.heap, s1); - let mut pstr_iter2 = HeapPStrIter::new(&self.heap, s1 + 1); - - match compare_pstr_prefixes(&mut pstr_iter1, &mut pstr_iter2) { - PStrCmpResult::Ordered(Ordering::Equal) => {} - PStrCmpResult::Ordered(Ordering::Less) => { - if pstr_iter2.focus.as_var().is_none() { - self.fail = true; - } else { - self.pdl.push(empty_list_as_cell!()); - self.pdl.push(pstr_iter2.focus); - } - } - PStrCmpResult::Ordered(Ordering::Greater) => { - if pstr_iter1.focus.as_var().is_none() { - self.fail = true; - } else { - self.pdl.push(empty_list_as_cell!()); - self.pdl.push(pstr_iter1.focus); - } - } - continuable @ PStrCmpResult::FirstIterContinuable(iteratee) | - continuable @ PStrCmpResult::SecondIterContinuable(iteratee) => { - if continuable.is_second_iter() { - std::mem::swap(&mut pstr_iter1, &mut pstr_iter2); - } - - let mut chars_iter = PStrCharsIter { - iter: pstr_iter1, - item: Some(iteratee), - }; - - let mut focus = pstr_iter2.focus; - - 'outer: loop { - while let Some(c) = chars_iter.peek() { - read_heap_cell!(focus, - (HeapCellValueTag::Lis, l) => { - let val = pstr_iter2.heap[l]; - - self.pdl.push(val); - self.pdl.push(char_as_cell!(c)); - - focus = pstr_iter2.heap[l+1]; - } - (HeapCellValueTag::Str, s) => { - let (name, arity) = cell_as_atom_cell!(pstr_iter2.heap[s]) - .get_name_and_arity(); - - if name == atom!(".") && arity == 2 { - self.pdl.push(pstr_iter2.heap[s+1]); - self.pdl.push(char_as_cell!(c)); - - focus = pstr_iter2.heap[s+2]; - } else { - self.fail = true; - break 'outer; - } - } - (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => { - match chars_iter.item.unwrap() { - PStrIteratee::Char(focus, _) => { - self.pdl.push(self.heap[focus]); - self.pdl.push(heap_loc_as_cell!(h)); - } - PStrIteratee::PStrSegment(focus, _, n) => { - read_heap_cell!(self.heap[focus], - (HeapCellValueTag::CStr | HeapCellValueTag::PStr, pstr_atom) => { - if focus < self.heap.len() - 2 { - self.heap.pop(); - self.heap.pop(); - } - - if n == 0 { - let target_cell = match self.heap[focus].get_tag() { - HeapCellValueTag::CStr => { - atom_as_cstr_cell!(pstr_atom) - } - HeapCellValueTag::PStr => { - pstr_loc_as_cell!(focus) - } - _ => { - unreachable!() - } - }; - - self.pdl.push(target_cell); - self.pdl.push(heap_loc_as_cell!(h)); - } else { - let h_len = self.heap.len(); - - self.heap.push(pstr_offset_as_cell!(focus)); - self.heap.push(fixnum_as_cell!( - Fixnum::build_with(n as i64) - )); - - self.pdl.push(pstr_loc_as_cell!(h_len)); - self.pdl.push(heap_loc_as_cell!(h)); - } - - return; - } - (HeapCellValueTag::PStrOffset, pstr_loc) => { - let n0 = cell_as_fixnum!(self.heap[focus+1]) - .get_num() as usize; - - if pstr_loc < self.heap.len() - 2 { - self.heap.pop(); - self.heap.pop(); - } - - if n == n0 { - self.pdl.push(pstr_loc_as_cell!(focus)); - self.pdl.push(heap_loc_as_cell!(h)); - } else { - let h_len = self.heap.len(); - - self.heap.push(pstr_offset_as_cell!(pstr_loc)); - self.heap.push(fixnum_as_cell!( - Fixnum::build_with(n as i64) - )); - - self.pdl.push(pstr_loc_as_cell!(h_len)); - self.pdl.push(heap_loc_as_cell!(h)); - } - - return; - } - _ => { - } - ); - - if focus < self.heap.len() - 2 { - self.heap.pop(); - self.heap.pop(); - } - - self.pdl.push(self.heap[focus]); - self.pdl.push(heap_loc_as_cell!(h)); - - return; - } - } - - break 'outer; - } - _ => { - self.fail = true; - break 'outer; - } - ); - - chars_iter.next(); - } - - chars_iter.iter.next(); - - self.pdl.push(chars_iter.iter.focus); - self.pdl.push(focus); - - break; - } - } - PStrCmpResult::Unordered => { - self.pdl.push(pstr_iter1.focus); - self.pdl.push(pstr_iter2.focus); - } - } - - self.heap.pop(); - self.heap.pop(); - } - - fn unify_list_with_occurs_trigger( - &mut self, - l1: usize, - d2: HeapCellValue, - mut occurs_trigger: impl FnMut(), - ) { - read_heap_cell!(d2, - (HeapCellValueTag::Lis, l2) => { - for idx in (0..2).rev() { - self.pdl.push(heap_loc_as_cell!(l2+idx)); - self.pdl.push(heap_loc_as_cell!(l1+idx)); - } - } - (HeapCellValueTag::Str, s2) => { - let (n2, a2) = cell_as_atom_cell!(self.heap[s2]) - .get_name_and_arity(); - - if a2 == 2 && n2 == atom!(".") { - for idx in (0..2).rev() { - self.pdl.push(heap_loc_as_cell!(s2+1+idx)); - self.pdl.push(heap_loc_as_cell!(l1+idx)); - } - } else { - self.fail = true; - } - } - (HeapCellValueTag::PStrLoc | HeapCellValueTag::CStr | HeapCellValueTag::PStr) => { - self.unify_partial_string_with_occurs_check( - list_loc_as_cell!(l1), - d2, - &mut occurs_trigger, - ) - } - (HeapCellValueTag::AttrVar, h) => { - if self.bind_with_occurs_check(Ref::attr_var(h), list_loc_as_cell!(l1)) { - occurs_trigger(); - } - } - (HeapCellValueTag::Var, h) => { - if self.bind_with_occurs_check(Ref::heap_cell(h), list_loc_as_cell!(l1)) { - occurs_trigger(); - } - } - (HeapCellValueTag::StackVar, s) => { - if self.bind_with_occurs_check(Ref::stack_cell(s), list_loc_as_cell!(l1)) { - occurs_trigger(); - } - } - _ => { - self.fail = true; - } - ) - } - - pub(super) fn unify_with_occurs_check_loop(&mut self, mut occurs_trigger: impl FnMut()) { - let mut tabu_list = IndexSet::with_hasher(FxBuildHasher::default()); - - // self.fail = false; - - while !(self.pdl.is_empty() || self.fail) { - let s1 = self.pdl.pop().unwrap(); - let s1 = self.deref(s1); - - let s2 = self.pdl.pop().unwrap(); - let s2 = self.deref(s2); - - if s1 != s2 { - let d1 = self.store(s1); - let d2 = self.store(s2); - - read_heap_cell!(d1, - (HeapCellValueTag::AttrVar, h) => { - if self.bind_with_occurs_check(Ref::attr_var(h), d2) { - occurs_trigger(); - } - } - (HeapCellValueTag::Var, h) => { - if self.bind_with_occurs_check(Ref::heap_cell(h), d2) { - occurs_trigger(); - } - } - (HeapCellValueTag::StackVar, s) => { - if self.bind_with_occurs_check(Ref::stack_cell(s), d2) { - occurs_trigger(); - } - } - (HeapCellValueTag::Atom, (name, arity)) => { - debug_assert!(arity == 0); - self.unify_atom(name, d2); - } - (HeapCellValueTag::Str, s1) => { - if tabu_list.contains(&(d1, d2)) { - continue; - } - - self.unify_structure_with_occurs_check(s1, d2, &mut occurs_trigger); - - if !self.fail { - let d2 = self.store(d2); - tabu_list.insert((d1, d2)); - } - } - (HeapCellValueTag::Lis, l1) => { - if d2.is_ref() { - if tabu_list.contains(&(d1, d2)) { - continue; - } - } - - self.unify_list_with_occurs_trigger(l1, d2, &mut occurs_trigger); - - if !self.fail { - let d2 = self.store(d2); - tabu_list.insert((d1, d2)); - } - } - (HeapCellValueTag::PStrLoc) => { - read_heap_cell!(d2, - (HeapCellValueTag::PStrLoc | - HeapCellValueTag::Lis | - HeapCellValueTag::Str) => { - if tabu_list.contains(&(d1, d2)) { - continue; - } - } - (HeapCellValueTag::CStr | - HeapCellValueTag::AttrVar | - HeapCellValueTag::Var | - HeapCellValueTag::StackVar) => { - } - _ => { - self.fail = true; - break; - } - ); - - self.unify_partial_string_with_occurs_check( - d1, - d2, - &mut occurs_trigger, - ); - - if !self.fail && !d2.is_constant() { - let d2 = self.store(d2); - tabu_list.insert((d1, d2)); - } - } - (HeapCellValueTag::CStr) => { - read_heap_cell!(d2, - (HeapCellValueTag::AttrVar, h) => { - self.bind(Ref::attr_var(h), d1); - continue; - } - (HeapCellValueTag::Var, h) => { - self.bind(Ref::heap_cell(h), d1); - continue; - } - (HeapCellValueTag::StackVar, s) => { - self.bind(Ref::stack_cell(s), d1); - continue; - } - (HeapCellValueTag::Str | - HeapCellValueTag::Lis | - HeapCellValueTag::PStrLoc) => { - } - _ => { - self.fail = true; - return; - } - ); - - self.unify_partial_string(d2, d1); - } - (HeapCellValueTag::F64, f1) => { - self.unify_f64(f1, d2); - } - (HeapCellValueTag::Fixnum, n1) => { - self.unify_fixnum(n1, d2); - } - (HeapCellValueTag::Char, c1) => { - self.unify_char(c1, d2); - } - (HeapCellValueTag::Cons, ptr_1) => { - self.unify_constant(ptr_1, d2); - } - _ => { - unreachable!(); - } - ); - } - } - } - pub(crate) fn read_s(&mut self) -> HeapCellValue { match &mut self.s { &mut HeapPtr::HeapCell(h) => self.deref(self.heap[h + self.s_offset]), @@ -1497,7 +435,7 @@ impl MachineState { } } - pub fn compare_term_test(&mut self) -> Option { + pub fn compare_term_test(&mut self, var_comparison: VarComparison) -> Option { let mut tabu_list = IndexSet::new(); while !self.pdl.is_empty() { @@ -1524,12 +462,14 @@ impl MachineState { match order_cat_v1 { Some(TermOrderCategory::Variable) => { - let v1 = v1.as_var().unwrap(); - let v2 = v2.as_var().unwrap(); + if let VarComparison::Distinct = var_comparison { + let v1 = v1.as_var().unwrap(); + let v2 = v2.as_var().unwrap(); - if v1 != v2 { - self.pdl.clear(); - return Some(v1.cmp(&v2)); + if v1 != v2 { + self.pdl.clear(); + return Some(v1.cmp(&v2)); + } } } Some(TermOrderCategory::FloatingPoint) => { @@ -2194,7 +1134,7 @@ impl MachineState { return false; } - let mut iter = stackful_preorder_iter(&mut self.heap, value); + let mut iter = stackful_preorder_iter(&mut self.heap, &mut self.stack, value); while let Some(value) = iter.next() { if value.get_forwarding_bit() { @@ -2364,17 +1304,6 @@ impl MachineState { .unwrap_or(true) } - pub fn reset_block(&mut self, addr: HeapCellValue) { - read_heap_cell!(self.store(addr), - (HeapCellValueTag::Fixnum, n) => { - self.block = n.get_num() as usize; - } - _ => { - self.fail = true; - } - ) - } - #[inline(always)] fn try_functor_compound_case(&mut self, name: Atom, arity: usize) { self.try_functor_unify_components(atom_as_cell!(name), arity); @@ -2456,7 +1385,7 @@ impl MachineState { Ok(Number::Float(_)) => { return type_error(arity); } - Ok(Number::Rational(n)) if n.denom() != &1 => { + Ok(Number::Rational(n)) if !n.denominator().is_one() => { return type_error(arity); } Ok(n) if n > MAX_ARITY => { @@ -2469,7 +1398,7 @@ impl MachineState { let err = self.domain_error(DomainErrorType::NotLessThanZero, n); return Err(self.error_form(err, stub_gen())); } - Ok(Number::Rational(n)) => n.numer().to_i64().unwrap(), + Ok(Number::Rational(n)) => n.numerator().to_i64().unwrap(), Ok(Number::Fixnum(n)) => n.get_num(), Ok(Number::Integer(n)) => n.to_i64().unwrap(), Err(_) => { @@ -2685,6 +1614,8 @@ impl MachineState { // returns true on failure. pub fn ground_test(&mut self) -> bool { + use fxhash::FxBuildHasher; + if self.registers[1].is_constant() { return false; } @@ -2695,13 +1626,15 @@ impl MachineState { return true; } - let mut iter = stackful_preorder_iter(&mut self.heap, value); + let mut visited = IndexSet::with_hasher(FxBuildHasher::default()); + let mut iter = stackful_preorder_iter(&mut self.heap, &mut self.stack, value); + let mut stack_len = 0; while let Some(value) = iter.next() { - let value = unmark_cell_bits!(value); + let mut value = unmark_cell_bits!(value); if value.is_var() { - let value = heap_bound_store( + value = heap_bound_store( iter.heap, heap_bound_deref(iter.heap, value), ); @@ -2710,6 +1643,18 @@ impl MachineState { return true; } } + + if value.is_compound(iter.heap) { + if visited.contains(&value) { + for _ in stack_len .. iter.stack_len() { + iter.pop_stack(); + } + } else { + visited.insert(value); + } + } + + stack_len = iter.stack_len(); } false @@ -2784,8 +1729,11 @@ impl MachineState { self.cp = frame.prelude.cp; self.e = frame.prelude.e; - if e > self.b { - self.stack.truncate(e); + if self.e > self.b { + let frame = self.stack.index_and_frame(self.e); + let size = AndFrame::size_of(frame.prelude.num_cells); + + self.stack.truncate(self.e + size); } self.p += 1; diff --git a/src/machine/mock_wam.rs b/src/machine/mock_wam.rs index 77011b32..35e00eea 100644 --- a/src/machine/mock_wam.rs +++ b/src/machine/mock_wam.rs @@ -61,6 +61,8 @@ impl MockWAM { let mut printer = HCPrinter::new( &mut self.machine_st.heap, + &mut self.machine_st.atom_tbl, + &mut self.machine_st.stack, &self.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(term_write_result.heap_loc), @@ -69,7 +71,12 @@ impl MockWAM { printer.var_names = term_write_result .var_dict .into_iter() - .map(|(var, cell)| (cell, var)) + .map(|(var, cell)| { + match var { + VarKey::VarPtr(var) => (cell, var.clone()), + VarKey::AnonVar(_) => (cell, VarPtr::from(var.to_string())) + } + }) .collect(); Ok(printer.print().result()) diff --git a/src/machine/mod.rs b/src/machine/mod.rs index 03ac6492..a170abd4 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -19,16 +19,19 @@ pub mod machine_state_impl; pub mod mock_wam; pub mod parsed_results; pub mod partial_string; +pub mod disjuncts; pub mod preprocessor; pub mod stack; pub mod streams; pub mod system_calls; pub mod term_stream; +pub mod unify; use crate::arena::*; use crate::arithmetic::*; use crate::atom_table::*; use crate::forms::*; +use crate::ffi::ForeignFunctionTable; use crate::instructions::*; use crate::machine::args::*; use crate::machine::compile::*; @@ -41,7 +44,7 @@ use crate::machine::machine_state::*; use crate::machine::stack::*; use crate::machine::streams::*; use crate::parser::ast::*; -use crate::parser::rug::{Integer, Rational}; +use crate::parser::dashu::{Integer, Rational}; use crate::types::*; use indexmap::IndexMap; @@ -70,6 +73,7 @@ pub struct Machine { pub(super) user_output: Stream, pub(super) user_error: Stream, pub(super) load_contexts: Vec, + pub(super) foreign_function_table: ForeignFunctionTable, } #[derive(Debug)] @@ -205,7 +209,7 @@ impl Machine { self.machine_st.throw_exception(err); } - fn run_module_predicate(&mut self, module_name: Atom, key: PredicateKey) { + fn run_module_predicate(&mut self, module_name: Atom, key: PredicateKey) -> std::process::ExitCode { if let Some(module) = self.indices.modules.get(&module_name) { if let Some(ref code_index) = module.code_dir.get(&key) { let p = code_index.local().unwrap(); @@ -255,31 +259,22 @@ impl Machine { let mut path_buf = current_dir(); path_buf.push("machine/attributed_variables.pl"); - bootstrapping_compile( - Stream::from_static_string( - include_str!("attributed_variables.pl"), - &mut self.machine_st.arena, - ), - self, - ListingSource::from_file_and_path( - atom!("attributed_variables"), - path_buf, - ), - ) - .unwrap(); + let stream = Stream::from_static_string( + include_str!("attributed_variables.pl"), + &mut self.machine_st.arena, + ); + + self.load_file(path_buf.to_str().unwrap(), stream); let mut path_buf = current_dir(); path_buf.push("machine/project_attributes.pl"); - bootstrapping_compile( - Stream::from_static_string( - include_str!("project_attributes.pl"), - &mut self.machine_st.arena, - ), - self, - ListingSource::from_file_and_path(atom!("project_attributes"), path_buf), - ) - .unwrap(); + let stream = Stream::from_static_string( + include_str!("project_attributes.pl"), + &mut self.machine_st.arena, + ); + + self.load_file(path_buf.to_str().unwrap(), stream); if let Some(module) = self.indices.modules.get(&atom!("$atts")) { if let Some(code_index) = module.code_dir.get(&(atom!("driver"), 2)) { @@ -288,7 +283,7 @@ impl Machine { } } - pub fn run_top_level(&mut self, module_name: Atom, key: PredicateKey) { + pub fn run_top_level(&mut self, module_name: Atom, key: PredicateKey) -> std::process::ExitCode { let mut arg_pstrs = vec![]; for arg in env::args() { @@ -304,7 +299,7 @@ impl Machine { arg_pstrs.into_iter() )); - self.run_module_predicate(module_name, key); + self.run_module_predicate(module_name, key) } pub fn set_user_input(&mut self, input: String) { @@ -380,46 +375,45 @@ impl Machine { Instruction::BreakFromDispatchLoop, Instruction::InstallVerifyAttr, Instruction::VerifyAttrInterrupt, - Instruction::ExecuteTermGreaterThan(0), - Instruction::ExecuteTermLessThan(0), - Instruction::ExecuteTermGreaterThanOrEqual(0), - Instruction::ExecuteTermLessThanOrEqual(0), - Instruction::ExecuteTermEqual(0), - Instruction::ExecuteTermNotEqual(0), - Instruction::ExecuteNumberGreaterThan(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2)), 0), - Instruction::ExecuteNumberLessThan(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2)), 0), - Instruction::ExecuteNumberGreaterThanOrEqual(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2)), 0), - Instruction::ExecuteNumberLessThanOrEqual(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2)), 0), - Instruction::ExecuteNumberEqual(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2)), 0), - Instruction::ExecuteNumberNotEqual(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2)), 0), - Instruction::ExecuteIs(temp_v!(1), ar_reg!(temp_v!(2)), 0), - Instruction::ExecuteAcyclicTerm(0), - Instruction::ExecuteArg(0), - Instruction::ExecuteCompare(0), - Instruction::ExecuteCopyTerm(0), - Instruction::ExecuteFunctor(0), - Instruction::ExecuteGround(0), - Instruction::ExecuteKeySort(0), - Instruction::ExecuteRead(0), - Instruction::ExecuteSort(0), - Instruction::ExecuteN(1, 0), - Instruction::ExecuteN(2, 0), - Instruction::ExecuteN(3, 0), - Instruction::ExecuteN(4, 0), - Instruction::ExecuteN(5, 0), - Instruction::ExecuteN(6, 0), - Instruction::ExecuteN(7, 0), - Instruction::ExecuteN(8, 0), - Instruction::ExecuteN(9, 0), - Instruction::ExecuteIsAtom(temp_v!(1), 0), - Instruction::ExecuteIsAtomic(temp_v!(1), 0), - Instruction::ExecuteIsCompound(temp_v!(1), 0), - Instruction::ExecuteIsInteger(temp_v!(1), 0), - Instruction::ExecuteIsNumber(temp_v!(1), 0), - Instruction::ExecuteIsRational(temp_v!(1), 0), - Instruction::ExecuteIsFloat(temp_v!(1), 0), - Instruction::ExecuteIsNonVar(temp_v!(1), 0), - Instruction::ExecuteIsVar(temp_v!(1), 0) + Instruction::ExecuteTermGreaterThan, + Instruction::ExecuteTermLessThan, + Instruction::ExecuteTermGreaterThanOrEqual, + Instruction::ExecuteTermLessThanOrEqual, + Instruction::ExecuteTermEqual, + Instruction::ExecuteTermNotEqual, + Instruction::ExecuteNumberGreaterThan(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))), + Instruction::ExecuteNumberLessThan(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))), + Instruction::ExecuteNumberGreaterThanOrEqual(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))), + Instruction::ExecuteNumberLessThanOrEqual(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))), + Instruction::ExecuteNumberEqual(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))), + Instruction::ExecuteNumberNotEqual(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))), + Instruction::ExecuteIs(temp_v!(1), ar_reg!(temp_v!(2))), + Instruction::ExecuteAcyclicTerm, + Instruction::ExecuteArg, + Instruction::ExecuteCompare, + Instruction::ExecuteCopyTerm, + Instruction::ExecuteFunctor, + Instruction::ExecuteGround, + Instruction::ExecuteKeySort, + Instruction::ExecuteSort, + Instruction::ExecuteN(1), + Instruction::ExecuteN(2), + Instruction::ExecuteN(3), + Instruction::ExecuteN(4), + Instruction::ExecuteN(5), + Instruction::ExecuteN(6), + Instruction::ExecuteN(7), + Instruction::ExecuteN(8), + Instruction::ExecuteN(9), + Instruction::ExecuteIsAtom(temp_v!(1)), + Instruction::ExecuteIsAtomic(temp_v!(1)), + Instruction::ExecuteIsCompound(temp_v!(1)), + Instruction::ExecuteIsInteger(temp_v!(1)), + Instruction::ExecuteIsNumber(temp_v!(1)), + Instruction::ExecuteIsRational(temp_v!(1)), + Instruction::ExecuteIsFloat(temp_v!(1)), + Instruction::ExecuteIsNonVar(temp_v!(1)), + Instruction::ExecuteIsVar(temp_v!(1)) ].into_iter()); for (p, instr) in self.code[impls_offset ..].iter().enumerate() { @@ -458,6 +452,7 @@ impl Machine { user_output, user_error, load_contexts: vec![], + foreign_function_table: Default::default(), }; let mut lib_path = current_dir(); @@ -566,103 +561,436 @@ impl Machine { self.machine_st.verify_attr_interrupt(p, arity); } + fn next_clause_applicable(&mut self, mut offset: usize) -> bool { + loop { + match &self.code[offset] { + Instruction::IndexingCode(indexing_lines) => { + let mut oip = 0; + let mut cell = empty_list_as_cell!(); + + loop { + let indexing_code_ptr = match &indexing_lines[oip] { + &IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(arg, v, c, l, s)) => { + cell = self.deref_register(arg); + self.machine_st.select_switch_on_term_index(cell, v, c, l, s) + } + IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(hm)) => { + let lit = self.machine_st.constant_to_literal(cell); + hm.get(&lit).cloned().unwrap_or(IndexingCodePtr::Fail) + } + IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(hm)) => { + self.machine_st.select_switch_on_structure_index(cell, hm) + } + _ => { + offset += 1; + break; + } + }; + + match indexing_code_ptr { + IndexingCodePtr::External(_) | IndexingCodePtr::DynamicExternal(_) => { + offset += 1; + break; + } + IndexingCodePtr::Internal(i) => oip += i, + IndexingCodePtr::Fail => return false, + } + } + } + &Instruction::GetConstant(Level::Shallow, lit, RegType::Temp(t)) => { + let cell = self.deref_register(t); + + if cell.is_var() { + offset += 1; + } else if lit.get_tag() == HeapCellValueTag::CStr { + read_heap_cell!(cell, + (HeapCellValueTag::CStr) => { + if cell == lit { + offset += 1; + } else { + return false; + } + } + (HeapCellValueTag::Lis | HeapCellValueTag::PStrLoc) => { + offset += 1; + } + (HeapCellValueTag::Str, s) => { + let (name, arity) = cell_as_atom_cell!(self.machine_st.heap[s]) + .get_name_and_arity(); + + if name == atom!(".") && arity == 2 { + offset += 1; + } else { + return false; + } + } + _ => { + return false; + } + ); + } else { + self.machine_st.write_literal_to_var(cell, lit); + + if self.machine_st.fail { + self.machine_st.fail = false; + return false; + } else { + offset += 1; + } + } + } + &Instruction::GetList(Level::Shallow, RegType::Temp(t)) => { + let cell = self.deref_register(t); + + read_heap_cell!(cell, + (HeapCellValueTag::Lis | HeapCellValueTag::PStrLoc | HeapCellValueTag::CStr) => { + offset += 1; + } + (HeapCellValueTag::Str, s) => { + let (name, arity) = cell_as_atom_cell!(self.machine_st.heap[s]).get_name_and_arity(); + + if name == atom!(".") && arity == 2 { + offset += 1; + } else { + return false; + } + } + (HeapCellValueTag::AttrVar | HeapCellValueTag::Var | HeapCellValueTag::StackVar) => { + offset += 1; + } + _ => { + return false; + } + ); + } + &Instruction::GetStructure(Level::Shallow, name, arity, RegType::Temp(t)) => { + let cell = self.deref_register(t); + + read_heap_cell!(cell, + (HeapCellValueTag::Str, s) => { + if (name, arity) == cell_as_atom_cell!(self.machine_st.heap[s]).get_name_and_arity() { + offset += 1; + } else { + return false; + } + } + (HeapCellValueTag::AttrVar | HeapCellValueTag::Var | HeapCellValueTag::StackVar) => { + offset += 1; + } + _ => { + return false; + } + ); + } + &Instruction::GetPartialString(Level::Shallow, string, RegType::Temp(t), has_tail) => { + let cell = self.deref_register(t); + + read_heap_cell!(cell, + (HeapCellValueTag::CStr, cstr) => { + if !has_tail && string != cstr { + return false; + } + + offset += 1; + } + (HeapCellValueTag::Lis | HeapCellValueTag::PStrLoc) => { + offset += 1; + } + (HeapCellValueTag::Str, s) => { + let (name, arity) = cell_as_atom_cell!(self.machine_st.heap[s]).get_name_and_arity(); + + if name == atom!(".") && arity == 2 { + offset += 1; + } else { + return false; + } + } + (HeapCellValueTag::AttrVar | HeapCellValueTag::Var | HeapCellValueTag::StackVar) => { + offset += 1; + } + _ => { + return false; + } + ); + } + Instruction::GetConstant(..) | + Instruction::GetList(..) | + Instruction::GetStructure(..) | + Instruction::GetPartialString(..) | + &Instruction::UnifyVoid(..) | + &Instruction::UnifyConstant(..) | + &Instruction::GetVariable(..) | + &Instruction::GetValue(..) | + &Instruction::UnifyVariable(..) | + &Instruction::UnifyValue(..) | + &Instruction::UnifyLocalValue(..) => { + offset += 1; + } + _ => { + break; + } + } + } + + true + } + + fn next_applicable_clause(&mut self, mut offset: usize) -> Option { + while !self.next_clause_applicable(self.machine_st.p + offset + 1) { + match &self.code[self.machine_st.p + offset] { + &Instruction::DefaultRetryMeElse(o) | &Instruction::RetryMeElse(o) | + &Instruction::DynamicElse(.., NextOrFail::Next(o)) | + &Instruction::DynamicInternalElse(.., NextOrFail::Next(o)) => offset += o, + _ => { + return None; + } + } + } + + Some(offset) + } + + fn next_inner_applicable_clause(&mut self) -> Option { + let mut inner_offset = 1u32; + + loop { + match &self.code[self.machine_st.p] { + Instruction::IndexingCode(indexing_lines) => { + match &indexing_lines[self.machine_st.oip as usize] { + IndexingLine::IndexedChoice(indexed_choice) => { + match &indexed_choice[(self.machine_st.iip + inner_offset) as usize] { + &IndexedChoiceInstruction::Retry(o) => { + if self.next_clause_applicable(self.machine_st.p + o) { + return Some(inner_offset); + } + + inner_offset += 1; + } + &IndexedChoiceInstruction::Trust(o) => { + return if self.next_clause_applicable(self.machine_st.p + o) { + Some(inner_offset) + } else { + None + }; + } + _ => unreachable!(), + } + } + IndexingLine::DynamicIndexedChoice(indexed_choice) => { + let idx = (self.machine_st.iip + inner_offset) as usize; + let o = indexed_choice[idx]; + + if idx + 1 == indexed_choice.len() { + return if self.next_clause_applicable(self.machine_st.p + o) { + Some(inner_offset) + } else { + None + }; + } else { + if self.next_clause_applicable(self.machine_st.p + o) { + return Some(inner_offset); + } + + inner_offset += 1; + } + } + _ => unreachable!(), + } + } + _ => unreachable!(), + } + } + } + + #[inline(always)] + pub(super) fn try_me_else(&mut self, offset: usize) { + if let Some(offset) = self.next_applicable_clause(offset) { + let n = self.machine_st.num_of_args; + let b = self.machine_st.stack.allocate_or_frame(n); + let or_frame = self.machine_st.stack.index_or_frame_mut(b); + + or_frame.prelude.num_cells = n; + or_frame.prelude.e = self.machine_st.e; + or_frame.prelude.cp = self.machine_st.cp; + or_frame.prelude.b = self.machine_st.b; + or_frame.prelude.bp = self.machine_st.p + offset; + or_frame.prelude.boip = 0; + or_frame.prelude.biip = 0; + or_frame.prelude.tr = self.machine_st.tr; + or_frame.prelude.h = self.machine_st.heap.len(); + or_frame.prelude.b0 = self.machine_st.b0; + or_frame.prelude.attr_var_queue_len = self.machine_st.attr_var_init.attr_var_queue.len(); + + self.machine_st.b = b; + + for i in 0..n { + or_frame[i] = self.machine_st.registers[i+1]; + } + + self.machine_st.hb = self.machine_st.heap.len(); + } + + self.machine_st.p += 1; + } + + #[inline(always)] + pub(super) fn indexed_try(&mut self, offset: usize) { + if let Some(iip_offset) = self.next_inner_applicable_clause() { + let n = self.machine_st.num_of_args; + let b = self.machine_st.stack.allocate_or_frame(n); + let or_frame = self.machine_st.stack.index_or_frame_mut(b); + + or_frame.prelude.num_cells = n; + or_frame.prelude.e = self.machine_st.e; + or_frame.prelude.cp = self.machine_st.cp; + or_frame.prelude.b = self.machine_st.b; + or_frame.prelude.bp = self.machine_st.p; + or_frame.prelude.boip = self.machine_st.oip; + or_frame.prelude.biip = self.machine_st.iip + iip_offset; // 1 + or_frame.prelude.tr = self.machine_st.tr; + or_frame.prelude.h = self.machine_st.heap.len(); + or_frame.prelude.b0 = self.machine_st.b0; + or_frame.prelude.attr_var_queue_len = self.machine_st.attr_var_init.attr_var_queue.len(); + + self.machine_st.b = b; + + for i in 0..n { + or_frame[i] = self.machine_st.registers[i+1]; + } + + self.machine_st.hb = self.machine_st.heap.len(); + + self.machine_st.oip = 0; + self.machine_st.iip = 0; + } + + self.machine_st.p += offset; + } + #[inline(always)] fn retry_me_else(&mut self, offset: usize) { let b = self.machine_st.b; let or_frame = self.machine_st.stack.index_or_frame_mut(b); - let n = or_frame.prelude.univ_prelude.num_cells; + let n = or_frame.prelude.num_cells; + + let old_tr = or_frame.prelude.tr; + let curr_tr = self.machine_st.tr; for i in 0..n { self.machine_st.registers[i + 1] = or_frame[i]; } - self.machine_st.num_of_args = n; - self.machine_st.e = or_frame.prelude.e; - self.machine_st.cp = or_frame.prelude.cp; - - or_frame.prelude.bp = self.machine_st.p + offset; - - let old_tr = or_frame.prelude.tr; - let curr_tr = self.machine_st.tr; - let target_h = or_frame.prelude.h; - - self.machine_st.tr = or_frame.prelude.tr; - - self.reset_attr_var_state(); - self.machine_st.hb = target_h; - self.unwind_trail(old_tr, curr_tr); - self.machine_st.trail.truncate(self.machine_st.tr); - self.machine_st.heap.truncate(target_h); + if let Some(offset) = self.next_applicable_clause(offset) { + let or_frame = self.machine_st.stack.index_or_frame_mut(b); - self.machine_st.p += 1; + self.machine_st.num_of_args = n; + self.machine_st.e = or_frame.prelude.e; + self.machine_st.cp = or_frame.prelude.cp; + + or_frame.prelude.bp = self.machine_st.p + offset; + + let target_h = or_frame.prelude.h; + let attr_var_queue_len = or_frame.prelude.attr_var_queue_len; + + self.machine_st.tr = or_frame.prelude.tr; + self.reset_attr_var_state(attr_var_queue_len); + + self.machine_st.hb = target_h; + + self.machine_st.trail.truncate(self.machine_st.tr); + self.machine_st.heap.truncate(target_h); + + self.machine_st.p += 1; + } else { + self.trust_me_epilogue(); + } } #[inline(always)] fn retry(&mut self, offset: usize) { let b = self.machine_st.b; let or_frame = self.machine_st.stack.index_or_frame_mut(b); - let n = or_frame.prelude.univ_prelude.num_cells; + let n = or_frame.prelude.num_cells; + + let old_tr = or_frame.prelude.tr; + let curr_tr = self.machine_st.tr; for i in 0..n { self.machine_st.registers[i+1] = or_frame[i]; } - self.machine_st.num_of_args = n; - self.machine_st.e = or_frame.prelude.e; - self.machine_st.cp = or_frame.prelude.cp; - - or_frame.prelude.biip += 1; - - let old_tr = or_frame.prelude.tr; - let curr_tr = self.machine_st.tr; - let target_h = or_frame.prelude.h; - - self.machine_st.tr = or_frame.prelude.tr; - self.reset_attr_var_state(); - - self.machine_st.hb = target_h; - self.machine_st.p = self.machine_st.p + offset; - self.unwind_trail(old_tr, curr_tr); - self.machine_st.trail.truncate(self.machine_st.tr); - self.machine_st.heap.truncate(target_h); + if let Some(iip_offset) = self.next_inner_applicable_clause() { + let or_frame = self.machine_st.stack.index_or_frame_mut(b); - self.machine_st.oip = 0; - self.machine_st.iip = 0; + self.machine_st.num_of_args = n; + self.machine_st.e = or_frame.prelude.e; + self.machine_st.cp = or_frame.prelude.cp; + + or_frame.prelude.biip += iip_offset; + + let target_h = or_frame.prelude.h; + let attr_var_queue_len = or_frame.prelude.attr_var_queue_len; + + self.machine_st.tr = or_frame.prelude.tr; + self.machine_st.trail.truncate(self.machine_st.tr); + + self.reset_attr_var_state(attr_var_queue_len); + + self.machine_st.hb = target_h; + self.machine_st.p += offset; + + self.machine_st.heap.truncate(target_h); + + self.machine_st.oip = 0; + self.machine_st.iip = 0; + } else { + self.trust_epilogue(offset); + } } #[inline(always)] fn trust(&mut self, offset: usize) { let b = self.machine_st.b; let or_frame = self.machine_st.stack.index_or_frame(b); - let n = or_frame.prelude.univ_prelude.num_cells; + let n = or_frame.prelude.num_cells; + + let old_tr = or_frame.prelude.tr; + let curr_tr = self.machine_st.tr; for i in 0..n { self.machine_st.registers[i+1] = or_frame[i]; } + self.unwind_trail(old_tr, curr_tr); + self.trust_epilogue(offset); + } + + #[inline(always)] + fn trust_epilogue(&mut self, offset: usize) { + let b = self.machine_st.b; + let or_frame = self.machine_st.stack.index_or_frame(b); + let n = or_frame.prelude.num_cells; + self.machine_st.num_of_args = n; self.machine_st.e = or_frame.prelude.e; self.machine_st.cp = or_frame.prelude.cp; - let old_tr = or_frame.prelude.tr; - let curr_tr = self.machine_st.tr; let target_h = or_frame.prelude.h; self.machine_st.tr = or_frame.prelude.tr; + self.machine_st.trail.truncate(self.machine_st.tr); + self.machine_st.b = or_frame.prelude.b; - self.reset_attr_var_state(); + self.reset_attr_var_state(or_frame.prelude.attr_var_queue_len); self.machine_st.hb = target_h; self.machine_st.p = self.machine_st.p + offset; - self.unwind_trail(old_tr, curr_tr); - - self.machine_st.trail.truncate(self.machine_st.tr); self.machine_st.stack.truncate(b); self.machine_st.heap.truncate(target_h); @@ -674,35 +1002,63 @@ impl Machine { fn trust_me(&mut self) { let b = self.machine_st.b; let or_frame = self.machine_st.stack.index_or_frame(b); - let n = or_frame.prelude.univ_prelude.num_cells; + let n = or_frame.prelude.num_cells; for i in 0..n { self.machine_st.registers[i+1] = or_frame[i]; } + let old_tr = or_frame.prelude.tr; + let curr_tr = self.machine_st.tr; + + self.unwind_trail(old_tr, curr_tr); + + self.trust_me_epilogue(); + } + + #[inline(always)] + fn trust_me_epilogue(&mut self) { + let b = self.machine_st.b; + let or_frame = self.machine_st.stack.index_or_frame(b); + let n = or_frame.prelude.num_cells; + self.machine_st.num_of_args = n; self.machine_st.e = or_frame.prelude.e; self.machine_st.cp = or_frame.prelude.cp; - let old_tr = or_frame.prelude.tr; - let curr_tr = self.machine_st.tr; let target_h = or_frame.prelude.h; self.machine_st.tr = or_frame.prelude.tr; self.machine_st.b = or_frame.prelude.b; - self.reset_attr_var_state(); + self.reset_attr_var_state(or_frame.prelude.attr_var_queue_len); self.machine_st.hb = target_h; self.machine_st.p += 1; - self.unwind_trail(old_tr, curr_tr); - self.machine_st.trail.truncate(self.machine_st.tr); self.machine_st.stack.truncate(b); self.machine_st.heap.truncate(target_h); } + #[inline(always)] + fn undefined_procedure(&mut self, name: Atom, arity: usize) -> CallResult { + match self.machine_st.flags.unknown { + Unknown::Error => { + Err(self.machine_st.throw_undefined_error(name, arity)) + } + Unknown::Fail => { + self.machine_st.fail = true; + Ok(()) + } + Unknown::Warn => { + println!("warning: predicate {}/{} is undefined", name.as_str(), arity); + self.machine_st.fail = true; + Ok(()) + } + } + } + #[inline(always)] fn try_call(&mut self, name: Atom, arity: usize, idx: IndexPtr) -> CallResult { let compiled_tl_index = idx.p() as usize; @@ -712,7 +1068,7 @@ impl Machine { self.machine_st.fail = true; } IndexPtrTag::Undefined => { - return Err(self.machine_st.throw_undefined_error(name, arity)); + return self.undefined_procedure(name, arity); } IndexPtrTag::DynamicIndex => { self.machine_st.dynamic_mode = FirstOrNext::First; @@ -735,7 +1091,7 @@ impl Machine { self.machine_st.fail = true; } IndexPtrTag::Undefined => { - return Err(self.machine_st.throw_undefined_error(name, arity)); + return self.undefined_procedure(name, arity); } IndexPtrTag::DynamicIndex => { self.machine_st.dynamic_mode = FirstOrNext::First; @@ -764,7 +1120,7 @@ impl Machine { if let Some(idx) = module.code_dir.get(&(name, arity)).cloned() { self.try_call(name, arity, idx.get()) } else { - Err(self.machine_st.throw_undefined_error(name, arity)) + self.undefined_procedure(name, arity) } } else { let stub = functor_stub(name, arity); @@ -783,14 +1139,14 @@ impl Machine { if let Some(idx) = self.indices.code_dir.get(&(name, arity)).cloned() { self.try_execute(name, arity, idx.get()) } else { - Err(self.machine_st.throw_undefined_error(name, arity)) + self.undefined_procedure(name, arity) } } else { if let Some(module) = self.indices.modules.get(&module_name) { if let Some(idx) = module.code_dir.get(&(name, arity)).cloned() { self.try_execute(name, arity, idx.get()) } else { - Err(self.machine_st.throw_undefined_error(name, arity)) + self.undefined_procedure(name, arity) } } else { let stub = functor_stub(name, arity); @@ -841,7 +1197,7 @@ impl Machine { if let Some(&(_, b_cutoff, prev_block)) = self.machine_st.cont_pts.last() { if self.machine_st.b < b_cutoff { - let (idx, arity) = if self.machine_st.block > prev_block { + let (idx, arity) = if self.machine_st.effective_block() > prev_block { (r_c_w_h, 0) } else { self.machine_st.registers[1] = fixnum_as_cell!( @@ -876,14 +1232,22 @@ impl Machine { TrailEntryTag::TrailedAttrVar => { self.machine_st.heap[h] = attr_var_as_cell!(h); } - TrailEntryTag::TrailedAttrVarHeapLink => { - self.machine_st.heap[h] = heap_loc_as_cell!(h); - } TrailEntryTag::TrailedAttrVarListLink => { let l = self.machine_st.trail[i + 1].get_value() as usize; if l < self.machine_st.hb { - self.machine_st.heap[h] = list_loc_as_cell!(l); + if h == l { + self.machine_st.heap[h] = heap_loc_as_cell!(h); + } else { + read_heap_cell!(self.machine_st.heap[l], + (HeapCellValueTag::Var) => { + self.machine_st.heap[h] = list_loc_as_cell!(l); + } + _ => { + self.machine_st.heap[h] = heap_loc_as_cell!(l); + } + ); + } } else { self.machine_st.heap[h] = heap_loc_as_cell!(h); } @@ -910,4 +1274,4 @@ impl Machine { } } } -} +} \ No newline at end of file diff --git a/src/machine/parsed_results.rs b/src/machine/parsed_results.rs index ecb58c1c..aaec386d 100644 --- a/src/machine/parsed_results.rs +++ b/src/machine/parsed_results.rs @@ -1,6 +1,6 @@ use crate::atom_table::*; use ordered_float::OrderedFloat; -use rug::*; +use dashu::*; use std::collections::BTreeMap; use regex::Regex; use std::collections::HashMap; diff --git a/src/machine/partial_string.rs b/src/machine/partial_string.rs index fce47204..812941fc 100644 --- a/src/machine/partial_string.rs +++ b/src/machine/partial_string.rs @@ -181,7 +181,7 @@ impl<'a> HeapPStrIter<'a> { self.brent_st.hare = result.focus; } else { read_heap_cell!(self.heap[result.focus], - (HeapCellValueTag::Lis | HeapCellValueTag::Str) => { + (HeapCellValueTag::Lis | HeapCellValueTag::Str | HeapCellValueTag::PStr) => { self.focus = self.heap[self.brent_st.hare]; } _ => { diff --git a/src/machine/preprocessor.rs b/src/machine/preprocessor.rs index 37871bb0..a0cab869 100644 --- a/src/machine/preprocessor.rs +++ b/src/machine/preprocessor.rs @@ -2,7 +2,7 @@ use crate::atom_table::*; use crate::codegen::CodeGenSettings; use crate::forms::*; use crate::instructions::*; -use crate::iterators::*; +use crate::machine::disjuncts::*; use crate::machine::loader::*; use crate::machine::machine_errors::*; use crate::parser::ast::*; @@ -10,35 +10,7 @@ use crate::parser::ast::*; use indexmap::IndexSet; use std::cell::Cell; -use std::collections::VecDeque; use std::convert::TryFrom; -use std::rc::Rc; - -/* - * The preprocessor fabricates if-then-else ( .. -> ... ; ...) - * clauses into nameless standalone predicates, which it queues for - * later preprocessing and compilation. Fabricated predicates inherit - * explicit "cut variables" from the handwritten predicate - * surrounding their source if-then-else. They must be specially - * handled. - */ - -#[derive(Clone, Copy, Debug)] -pub(crate) enum CutContext { - BlocksCuts, - HasCutVariable, -} - -pub(crate) fn fold_by_str(terms: I, mut term: Term, sym: Atom) -> Term -where - I: DoubleEndedIterator, -{ - for prec in terms.rev() { - term = Term::Clause(Cell::default(), sym, vec![prec, term]); - } - - term -} pub(crate) fn to_op_decl( prec: u16, @@ -132,6 +104,13 @@ fn setup_module_export( }) } +pub(crate) fn build_rule_body(vars: &[Term], body_term: Term) -> Term { + let head_term = Term::Clause(Cell::default(), atom!(""), vars.iter().cloned().collect()); + let rule = vec![head_term, body_term]; + + Term::Clause(Cell::default(), atom!(":-"), rule) +} + pub(super) fn setup_module_export_list( mut export_list: Term, atom_tbl: &mut AtomTable, @@ -325,110 +304,6 @@ fn setup_meta_predicate<'a, LS: LoadState<'a>>( } } -fn merge_clauses(tls: &mut VecDeque) -> Result { - let mut clauses = vec![]; - - while let Some(tl) = tls.pop_front() { - match tl { - TopLevel::Query(_) if clauses.is_empty() && tls.is_empty() => { - return Ok(tl); - } - TopLevel::Query(_) => { - return Err(CompilationError::InconsistentEntry); - } - TopLevel::Fact(fact) => { - let clause = PredicateClause::Fact(fact); - clauses.push(clause); - } - TopLevel::Rule(rule) => { - let clause = PredicateClause::Rule(rule); - clauses.push(clause); - } - TopLevel::Predicate(predicate) => clauses.extend(predicate.into_iter()), - } - } - - if clauses.is_empty() { - Err(CompilationError::InconsistentEntry) - } else { - Ok(TopLevel::Predicate(clauses)) - } -} - -fn mark_cut_variables_as(terms: &mut Vec, name: Atom) { - for term in terms.iter_mut() { - match term { - &mut Term::Literal(_, Literal::Atom(ref mut var)) if *var == atom!("!") => { - *var = name; - } - _ => {} - } - } -} - -fn mark_cut_variable(term: &mut Term) -> bool { - let cut_var_found = match term { - &mut Term::Literal(_, Literal::Atom(ref var)) if *var == atom!("!") => true, - _ => false, - }; - - if cut_var_found { - *term = Term::Var(Cell::default(), Rc::new(String::from("!"))); - true - } else { - false - } -} - -fn mark_cut_variables(terms: &mut Vec) -> bool { - let mut found_cut_var = false; - - for item in terms.iter_mut() { - found_cut_var = mark_cut_variable(item) || found_cut_var; - } - - found_cut_var -} - -// terms is a list of goals composing one clause in a (;) functor. it -// checks that the first (and only) of these clauses is a ->. if so, -// it expands its terms using a blocked_!. -fn check_for_internal_if_then(terms: &mut Vec) { - if terms.len() != 1 { - return; - } - - if let Some(Term::Clause(_, name, ref subterms)) = terms.last() { - if *name != atom!("->") || subterms.len() != 2 { - return; - } - } else { - return; - } - - if let Some(Term::Clause(_, _, mut subterms)) = terms.pop() { - let mut conq_terms = VecDeque::from(unfold_by_str(subterms.pop().unwrap(), atom!(","))); - let mut pre_cut_terms = VecDeque::from(unfold_by_str(subterms.pop().unwrap(), atom!(","))); - - conq_terms.push_front(Term::Literal( - Cell::default(), - Literal::Atom(atom!("blocked_!")), - )); - - while let Some(term) = pre_cut_terms.pop_back() { - conq_terms.push_front(term); - } - - let tail_term = conq_terms.pop_back().unwrap(); - - terms.push(fold_by_str( - conq_terms.into_iter(), - tail_term, - atom!(","), - )); - } -} - pub(super) fn setup_declaration<'a, LS: LoadState<'a>>( loader: &mut Loader<'a, LS>, mut terms: Vec, @@ -570,7 +445,7 @@ fn build_meta_predicate_clause<'a, LS: LoadState<'a>>( } #[inline] -fn clause_to_query_term<'a, LS: LoadState<'a>>( +pub(super) fn clause_to_query_term<'a, LS: LoadState<'a>>( loader: &mut Loader<'a, LS>, name: Atom, mut terms: Vec, @@ -609,7 +484,7 @@ fn clause_to_query_term<'a, LS: LoadState<'a>>( } #[inline] -fn qualified_clause_to_query_term<'a, LS: LoadState<'a>>( +pub(super) fn qualified_clause_to_query_term<'a, LS: LoadState<'a>>( loader: &mut Loader<'a, LS>, module_name: Atom, name: Atom, @@ -647,308 +522,58 @@ fn qualified_clause_to_query_term<'a, LS: LoadState<'a>>( QueryTerm::Clause(Cell::default(), ct, terms, call_policy) } -fn compute_head(term: &Term) -> Vec { - let mut vars = IndexSet::new(); - - for term in post_order_iter(term) { - if let TermRef::Var(_, _, v) = term { - vars.insert(v.clone()); - } - } - - vars.insert(Rc::new(String::from("!"))); - vars.into_iter() - .map(|v| Term::Var(Cell::default(), v)) - .collect() -} - -pub(crate) fn build_rule_body(vars: &[Term], body_term: Term) -> Term { - let head_term = Term::Clause(Cell::default(), atom!(""), vars.iter().cloned().collect()); - let rule = vec![head_term, body_term]; - - Term::Clause(Cell::default(), atom!(":-"), rule) -} - -// the terms form the body of the rule. We create a head, by -// gathering variables from the body of terms and recording them -// in the head clause. -fn build_rule(body_term: Term) -> (JumpStub, VecDeque) { - // collect the vars of body_term into a head, return the num_vars - // (the arity) as well. - let vars = compute_head(&body_term); - let rule = build_rule_body(&vars, body_term); - - (vars, VecDeque::from(vec![rule])) -} - -fn build_disjunct(body_term: Term) -> (JumpStub, VecDeque) { - let vars = compute_head(&body_term); - let results = unfold_by_str(body_term, atom!(";")) - .into_iter() - .map(|term| { - let mut subterms = unfold_by_str(term, atom!(",")); - mark_cut_variables(&mut subterms); - - check_for_internal_if_then(&mut subterms); - - let term = subterms.pop().unwrap(); - let clause = fold_by_str(subterms.into_iter(), term, atom!(",")); - - build_rule_body(&vars, clause) - }) - .collect(); - - (vars, results) -} - -fn build_if_then(prec: Term, conq: Term) -> (JumpStub, VecDeque) { - let mut prec_seq = unfold_by_str(prec, atom!(",")); - let comma_sym = atom!(","); - let cut_sym = Literal::Atom(atom!("!")); - - prec_seq.push(Term::Literal(Cell::default(), cut_sym)); - - mark_cut_variables_as(&mut prec_seq, atom!("blocked_!")); - - let mut conq_seq = unfold_by_str(conq, atom!(",")); - - mark_cut_variables(&mut conq_seq); - prec_seq.extend(conq_seq.into_iter()); - - let back_term = prec_seq.pop().unwrap(); - let front_term = prec_seq.pop().unwrap(); - - let body_term = Term::Clause( - Cell::default(), - comma_sym, - vec![front_term, back_term], - ); - - build_rule(fold_by_str(prec_seq.into_iter(), body_term, comma_sym)) -} - #[derive(Debug)] pub(crate) struct Preprocessor { - queue: VecDeque>, settings: CodeGenSettings, } impl Preprocessor { pub(super) fn new(settings: CodeGenSettings) -> Self { Preprocessor { - queue: VecDeque::new(), settings, } } - fn setup_fact(&mut self, term: Term) -> Result { + fn setup_fact(&mut self, term: Term) -> Result<(Fact, VarData), CompilationError> { match term { - Term::Clause(..) | Term::Literal(_, Literal::Atom(..)) => Ok(term), + Term::Clause(..) | Term::Literal(_, Literal::Atom(..)) => { + let classifier = VariableClassifier::new( + self.settings.default_call_policy(), + ); + + let (head, var_data) = classifier.classify_fact(term)?; + Ok((Fact { head }, var_data)) + } _ => Err(CompilationError::InadmissibleFact), } } - fn to_query_term<'a, LS: LoadState<'a>>( - &mut self, - loader: &mut Loader<'a, LS>, - term: Term, - ) -> Result { - match term { - Term::Literal(_, Literal::Atom(name)) => { - if name == atom!("!") || name == atom!("blocked_!") { - Ok(QueryTerm::BlockedCut) - } else { - Ok(clause_to_query_term( - loader, - name, - vec![], - self.settings.default_call_policy(), - )) - } - } - Term::Literal(_, Literal::Char('!')) => Ok(QueryTerm::BlockedCut), - Term::Var(_, ref v) if v.as_str() == "!" => { - Ok(QueryTerm::UnblockedCut(Cell::default())) - } - Term::Clause(r, name, mut terms) => match (name, terms.len()) { - (atom!(";"), 2) => { - let term = Term::Clause(r, name, terms); - - let (stub, clauses) = build_disjunct(term); - self.queue.push_back(clauses); - - Ok(QueryTerm::Jump(stub)) - } - (atom!("->"), 2) => { - let conq = terms.pop().unwrap(); - let prec = terms.pop().unwrap(); - - let (stub, clauses) = build_if_then(prec, conq); - self.queue.push_back(clauses); - - Ok(QueryTerm::Jump(stub)) - } - (atom!("\\+"), 1) => { - terms.push(Term::Literal( - Cell::default(), - Literal::Atom(atom!("$fail")), - )); - - let conq = Term::Literal(Cell::default(), Literal::Atom(atom!("true"))); - - let prec = Term::Clause(Cell::default(), atom!("->"), terms); - let terms = vec![prec, conq]; - - let term = Term::Clause(Cell::default(), atom!(";"), terms); - let (stub, clauses) = build_disjunct(term); - - debug_assert!(clauses.len() > 0); - self.queue.push_back(clauses); - - Ok(QueryTerm::Jump(stub)) - } - (atom!("$get_level"), 1) => { - if let Term::Var(_, ref var) = &terms[0] { - Ok(QueryTerm::GetLevelAndUnify(Cell::default(), var.clone())) - } else { - Err(CompilationError::InadmissibleQueryTerm) - } - } - (atom!(":"), 2) => { - let predicate_name = terms.pop().unwrap(); - let module_name = terms.pop().unwrap(); - - match (module_name, predicate_name) { - ( - Term::Literal(_, Literal::Atom(module_name)), - Term::Literal(_, Literal::Atom(predicate_name)), - ) => Ok(qualified_clause_to_query_term( - loader, - module_name, - predicate_name, - vec![], - self.settings.default_call_policy(), - )), - ( - Term::Literal(_, Literal::Atom(module_name)), - Term::Clause(_, name, terms), - ) => Ok(qualified_clause_to_query_term( - loader, - module_name, - name, - terms, - self.settings.default_call_policy() - )), - (module_name, predicate_name) => { - terms.push(module_name); - terms.push(predicate_name); - - Ok(clause_to_query_term( - loader, - atom!("call"), - vec![Term::Clause(r, name, terms)], - self.settings.default_call_policy(), - )) - } - } - } - _ => Ok(clause_to_query_term(loader, name, terms, - self.settings.default_call_policy())), - }, - Term::Var(..) => Ok(QueryTerm::Clause( - Cell::default(), - ClauseType::CallN(1), - vec![term], - self.settings.default_call_policy(), - )), - _ => Err(CompilationError::InadmissibleQueryTerm), - } - } - - fn pre_query_term<'a, LS: LoadState<'a>>( - &mut self, - loader: &mut Loader<'a, LS>, - term: Term, - ) -> Result { - match term { - Term::Clause(r, name, mut subterms) => { - if subterms.len() == 1 && name == atom!("$call_with_inference_counting") { - self.to_query_term(loader, subterms.pop().unwrap()) - .map(|mut query_term| { - query_term.set_call_policy(CallPolicy::Counted); - query_term - }) - } else { - let clause = Term::Clause(r, name, subterms); - self.to_query_term(loader, clause) - } - } - _ => self.to_query_term(loader, term), - } - } - - fn setup_query<'a, LS: LoadState<'a>>( - &mut self, - loader: &mut Loader<'a, LS>, - terms: Vec, - cut_context: CutContext, - ) -> Result, CompilationError> { - let mut query_terms = vec![]; - let mut work_queue = VecDeque::from(terms); - - while let Some(term) = work_queue.pop_front() { - let mut term = term; - - if let Term::Clause(cell, name, terms) = term { - if name == atom!(",") && terms.len() == 2 { - let term = Term::Clause(cell, name, terms); - let mut subterms = unfold_by_str(term, atom!(",")); - - while let Some(subterm) = subterms.pop() { - work_queue.push_front(subterm); - } - - continue; - } else { - term = Term::Clause(cell, name, terms); - } - } - - if let CutContext::HasCutVariable = cut_context { - mark_cut_variable(&mut term); - } - - query_terms.push(self.pre_query_term(loader, term)?); - } - - Ok(query_terms) - } - fn setup_rule<'a, LS: LoadState<'a>>( &mut self, loader: &mut Loader<'a, LS>, - mut terms: Vec, - cut_context: CutContext, - ) -> Result { - let post_head_terms: Vec<_> = terms.drain(1..).collect(); - let mut query_terms = self.setup_query(loader, post_head_terms, cut_context)?; + head: Term, + body: Term, + ) -> Result<(Rule, VarData), CompilationError> { + let classifier = VariableClassifier::new( + self.settings.default_call_policy(), + ); - let clauses = query_terms.drain(1..).collect(); - let qt = query_terms.pop().unwrap(); + let (head, clauses, var_data) = classifier.classify_rule(loader, head, body)?; - match terms.pop().unwrap() { - Term::Clause(_, name, terms) => Ok(Rule { - head: (name, terms, qt), + match head { + Term::Clause(_, name, terms) => Ok((Rule { + head: (name, terms), clauses, - }), - Term::Literal(_, Literal::Atom(name)) => Ok(Rule { - head: (name, vec![], qt), + }, var_data)), + Term::Literal(_, Literal::Atom(name)) => Ok((Rule { + head: (name, vec![]), clauses, - }), + }, var_data)), _ => Err(CompilationError::InvalidRuleHead), } } + /* fn try_term_to_query<'a, LS: LoadState<'a>>( &mut self, loader: &mut Loader<'a, LS>, @@ -961,63 +586,49 @@ impl Preprocessor { cut_context, )?)) } + */ pub(super) fn try_term_to_tl<'a, LS: LoadState<'a>>( &mut self, loader: &mut Loader<'a, LS>, term: Term, - cut_context: CutContext, ) -> Result { match term { - Term::Clause(r, name, terms) => { - if name == atom!("?-") { - self.try_term_to_query(loader, terms, cut_context) - } else if name == atom!(":-") && terms.len() == 2 { - Ok(TopLevel::Rule(self.setup_rule( - loader, - terms, - cut_context, - )?)) + Term::Clause(r, name, mut terms) => { + let is_rule = name == atom!(":-") && terms.len() == 2; + + if is_rule { + let tail = terms.pop().unwrap(); + let head = terms.pop().unwrap(); + + let (rule, var_data) = self.setup_rule(loader, head, tail)?; + Ok(TopLevel::Rule(rule, var_data)) } else { let term = Term::Clause(r, name, terms); - Ok(TopLevel::Fact(self.setup_fact(term)?)) + let (fact, var_data) = self.setup_fact(term)?; + Ok(TopLevel::Fact(fact, var_data)) } } - term => Ok(TopLevel::Fact(self.setup_fact(term)?)), + term => { + let (fact, var_data) = self.setup_fact(term)?; + Ok(TopLevel::Fact(fact, var_data)) + } } } + /* fn try_terms_to_tls<'a, I: IntoIterator, LS: LoadState<'a>>( &mut self, loader: &mut Loader<'a, LS>, terms: I, - cut_context: CutContext, ) -> Result, CompilationError> { let mut results = VecDeque::new(); for term in terms.into_iter() { - results.push_back(self.try_term_to_tl(loader, term, cut_context)?); + results.push_back(self.try_term_to_tl(loader, term)?); } Ok(results) } - - pub(super) fn parse_queue<'a, LS: LoadState<'a>>( - &mut self, - loader: &mut Loader<'a, LS>, - ) -> Result, CompilationError> { - let mut queue = VecDeque::new(); - - while let Some(terms) = self.queue.pop_front() { - let clauses = merge_clauses(&mut self.try_terms_to_tls( - loader, - terms, - CutContext::HasCutVariable, - )?)?; - - queue.push_back(clauses); - } - - Ok(queue) - } + */ } diff --git a/src/machine/project_attributes.pl b/src/machine/project_attributes.pl index b2f75007..f54797c8 100644 --- a/src/machine/project_attributes.pl +++ b/src/machine/project_attributes.pl @@ -1,7 +1,12 @@ :- module('$project_atts', [copy_term/3]). +:- use_module(library(dcgs)). +:- use_module(library(error), [can_be/2]). +:- use_module(library(lambda)). +:- use_module(library(lists), [foldl/4, maplist/2]). + project_attributes(QueryVars, AttrVars) :- - gather_attr_modules(AttrVars, Modules0), + phrase(gather_attr_modules(AttrVars), Modules0), sort(Modules0, Modules), call_project_attributes(Modules, QueryVars, AttrVars). @@ -17,19 +22,14 @@ project_attributes(QueryVars, AttrVars) :- call_project_attributes([], _, _). call_project_attributes([Module|Modules], QueryVars, AttrVars) :- ( catch(Module:project_attributes(QueryVars, AttrVars), - E, - '$project_atts':'$print_project_attributes_exception'(Module, E) - ) + E, + '$project_atts':'$print_project_attributes_exception'(Module, E) + ) -> true ; true ), call_project_attributes(Modules, QueryVars, AttrVars). -call_attribute_goals([], _, _). -call_attribute_goals([Module|Modules], GoalCaller, AttrVars) :- - call(GoalCaller, AttrVars, Module, Goals), - call_attribute_goals(Modules, GoalCaller, AttrVars). - '$print_attribute_goals_exception'(Module, E) :- ( E = error(evaluation_error((Module:attribute_goals)/3), attribute_goals/3) ; E = error(existence_error(procedure, attribute_goals/3), attribute_goals/3) @@ -38,20 +38,6 @@ call_attribute_goals([Module|Modules], GoalCaller, AttrVars) :- nl ). -call_query_var_goals([], _, []). -call_query_var_goals([AttrVar|AttrVars], Module, Goals) :- - ( catch(( Module:attribute_goals(AttrVar, Goals, RGoals0), - atts:'$default_attr_list'(Module, AttrVar, RGoals0, RGoals) - ), - E, - ( '$project_atts':'$print_attribute_goals_exception'(Module, E), - atts:'$default_attr_list'(Module, AttrVar, Goals, RGoals) - )) - -> true - ; atts:'$default_attr_list'(Module, AttrVar, Goals, RGoals) - ), - call_query_var_goals(AttrVars, Module, RGoals). - call_attr_var_goals([], _, []). call_attr_var_goals([AttrVar|AttrVars], Module, Goals) :- ( catch(Module:attribute_goals(AttrVar, Goals, RGoals), @@ -77,25 +63,52 @@ call_attribute_goals_with_module_prefix([Module | Modules], GoalCaller, AttrVars module_prefixed_goals(Goals0, Module, Goals, Gs), call_attribute_goals_with_module_prefix(Modules, GoalCaller, AttrVars, Gs). +gather_attr_modules([]) --> []. +gather_attr_modules([AttrVar|AttrVars]) --> + { '$get_attr_list'(AttrVar, Attrs) }, + copy_attribute_modules(Attrs), + gather_attr_modules(AttrVars). -gather_attr_modules([], []). -gather_attr_modules([AttrVar|AttrVars], Modules) :- - '$get_attr_list'(AttrVar, Attrs), - copy_attribute_modules(Attrs, Modules, Modules0), - gather_attr_modules(AttrVars, Modules0). +copy_attribute_modules(Attrs) --> + { var(Attrs) }, + !. +copy_attribute_modules([Module:_|Attrs]) --> + [Module], + copy_attribute_modules(Attrs). -copy_attribute_modules(Attrs, Ls, Ls) :- - var(Attrs), !. -copy_attribute_modules([Module:_|Attrs], [Module|Modules0], Modules1) :- - copy_attribute_modules(Attrs, Modules0, Modules1). +gather_residual_goals_(M, V, V0, V1) :- + ( catch(M:attribute_goals(V, V0, V1), + E, + ('$project_atts':'$print_attribute_goals_exception'(M, E), + V0 = V1) + ) -> + true + ; V0 = V1 + ). +gather_residual_goals(M, V) --> + gather_residual_goals_(M, V), + atts:'$default_attr_list'(M, V). -copy_term(Source, Dest, Goals) :- - '$term_attributed_variables'(Source, AttrVars), - gather_attr_modules(AttrVars, Modules0), - sort(Modules0, Modules), - call_attribute_goals_with_module_prefix(Modules, '$project_atts':call_query_var_goals, - AttrVars, Goals0), - sort(Goals0, Goals1), - !, - '$copy_term_without_attr_vars'([Source | Goals1], [Dest | Goals]). +gather_residual_goals([]) --> []. +gather_residual_goals([V|Vs]) --> + { '$get_attr_list'(V, Attrs), + phrase(copy_attribute_modules(Attrs), Modules0), + sort(Modules0, Modules) }, + foldl(V+\M^gather_residual_goals(M, V), Modules), + gather_residual_goals(Vs). + +delete_all_attributes_from_var(V) :- '$delete_all_attributes_from_var'(V). + +copy_term(Term, Copy, Gs) :- + can_be(list, Gs), + findall(Term-Rs, term_residual_goals(Term,Rs), [Copy-Gs]), + ( var(Gs) -> + Gs = [] + ; true + ). + +term_residual_goals(Term,Rs) :- + '$term_attributed_variables'(Term, Vs), + phrase(gather_residual_goals(Vs), Rs), + maplist(delete_all_attributes_from_var, Vs). diff --git a/src/machine/stack.rs b/src/machine/stack.rs index 388c3008..1e15e69d 100644 --- a/src/machine/stack.rs +++ b/src/machine/stack.rs @@ -36,14 +36,9 @@ impl Drop for Stack { } } -#[derive(Debug, Clone, Copy)] -pub(crate) struct FramePrelude { - pub(crate) num_cells: usize, -} - #[derive(Debug)] pub(crate) struct AndFramePrelude { - pub(crate) univ_prelude: FramePrelude, + pub(crate) num_cells: usize, pub(crate) e: usize, pub(crate) cp: usize, } @@ -113,7 +108,7 @@ impl IndexMut for Stack { #[derive(Debug)] pub(crate) struct OrFramePrelude { - pub(crate) univ_prelude: FramePrelude, + pub(crate) num_cells: usize, pub(crate) e: usize, pub(crate) cp: usize, pub(crate) b: usize, @@ -123,6 +118,7 @@ pub(crate) struct OrFramePrelude { pub(crate) tr: usize, pub(crate) h: usize, pub(crate) b0: usize, + pub(crate) attr_var_queue_len: usize, } #[derive(Debug)] @@ -206,8 +202,8 @@ impl Stack { offset += mem::size_of::(); } - let and_frame = &mut *(new_ptr as *mut AndFrame); - and_frame.prelude.univ_prelude.num_cells = num_cells; + let and_frame = self.index_and_frame_mut(e); + and_frame.prelude.num_cells = num_cells; e } @@ -230,8 +226,8 @@ impl Stack { offset += mem::size_of::(); } - let or_frame = &mut *(new_ptr as *mut OrFrame); - or_frame.prelude.univ_prelude.num_cells = num_cells; + let or_frame = self.index_or_frame_mut(b); + or_frame.prelude.num_cells = num_cells; b } @@ -297,7 +293,7 @@ mod tests { 0// 10 * mem::size_of::() + prelude_size::() ); - assert_eq!(and_frame.prelude.univ_prelude.num_cells, 10); + assert_eq!(and_frame.prelude.num_cells, 10); for idx in 0..10 { assert_eq!(and_frame[idx + 1], stack_loc_as_cell!(AndFrame, e, idx + 1)); diff --git a/src/machine/streams.rs b/src/machine/streams.rs index 9e558af5..0ea8591d 100644 --- a/src/machine/streams.rs +++ b/src/machine/streams.rs @@ -9,6 +9,7 @@ use crate::machine::machine_errors::*; use crate::machine::machine_indices::*; use crate::machine::machine_state::*; use crate::types::*; +use crate::http::HttpResponse; pub use modular_bitfield::prelude::*; @@ -26,7 +27,6 @@ use std::ops::{Deref, DerefMut}; use std::ptr; use native_tls::TlsStream; -use hyper::body::{Bytes, Sender}; #[derive(Debug, BitfieldSpecifier, Clone, Copy, PartialEq, Eq, Hash)] #[bits = 1] @@ -102,6 +102,13 @@ impl EOFAction { #[derive(Debug)] pub struct ByteStream(Cursor>); +impl ByteStream { + #[inline(always)] + pub fn from_string(string: String) -> Self { + ByteStream(Cursor::new(string.into())) + } +} + impl Read for ByteStream { #[inline] fn read(&mut self, buf: &mut [u8]) -> std::io::Result { @@ -269,28 +276,42 @@ impl Read for HttpReadStream { } pub struct HttpWriteStream { - body_writer: Sender, + status_code: u16, + headers: hyper::HeaderMap, + response: TypedArenaPtr, + buffer: Vec, } impl Debug for HttpWriteStream { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Http Write Stream") + write!(f, "Http Write Stream") } } impl Write for HttpWriteStream { #[inline] fn write(&mut self, buf: &[u8]) -> std::io::Result { - let bytes = Bytes::copy_from_slice(buf); - let len = bytes.len(); - match self.body_writer.try_send_data(bytes) { - Ok(()) => Ok(len), - Err(_) => Err(std::io::Error::from(ErrorKind::Interrupted)) - } + self.buffer.extend_from_slice(buf); + Ok(buf.len()) } #[inline] fn flush(&mut self) -> std::io::Result<()> { + let (ready, response, cvar) = &**self.response; + + let mut ready = ready.lock().unwrap(); + { + let mut response = response.lock().unwrap(); + + let bytes = bytes::Bytes::copy_from_slice(&self.buffer); + let mut response_ = hyper::Response::builder() + .status(self.status_code); + *response_.headers_mut().unwrap() = self.headers.clone(); + *response = Some(response_.body(http_body_util::Full::new(bytes)).unwrap()); + } + *ready = true; + cvar.notify_one(); + Ok(()) } } @@ -505,7 +526,7 @@ impl Stream { ArenaHeaderTag::NamedTcpStream => Stream::NamedTcp(TypedArenaPtr::new(ptr as *mut _)), ArenaHeaderTag::NamedTlsStream => Stream::NamedTls(TypedArenaPtr::new(ptr as *mut _)), ArenaHeaderTag::HttpReadStream => Stream::HttpRead(TypedArenaPtr::new(ptr as *mut _)), - ArenaHeaderTag::HttpWriteStream => Stream::HttpWrite(TypedArenaPtr::new(ptr as *mut _)), + ArenaHeaderTag::HttpWriteStream => Stream::HttpWrite(TypedArenaPtr::new(ptr as *mut _)), ArenaHeaderTag::ReadlineStream => Stream::Readline(TypedArenaPtr::new(ptr as *mut _)), ArenaHeaderTag::StaticStringStream => { Stream::StaticString(TypedArenaPtr::new(ptr as *mut _)) @@ -559,7 +580,7 @@ impl Stream { Stream::NamedTcp(ptr) => ptr.header_ptr(), Stream::NamedTls(ptr) => ptr.header_ptr(), Stream::HttpRead(ptr) => ptr.header_ptr(), - Stream::HttpWrite(ptr) => ptr.header_ptr(), + Stream::HttpWrite(ptr) => ptr.header_ptr(), Stream::Null(_) => ptr::null(), Stream::Readline(ptr) => ptr.header_ptr(), Stream::StandardOutput(ptr) => ptr.header_ptr(), @@ -576,7 +597,7 @@ impl Stream { Stream::NamedTcp(ref ptr) => &ptr.options, Stream::NamedTls(ref ptr) => &ptr.options, Stream::HttpRead(ref ptr) => &ptr.options, - Stream::HttpWrite(ref ptr) => &ptr.options, + Stream::HttpWrite(ref ptr) => &ptr.options, Stream::Null(ref options) => options, Stream::Readline(ref ptr) => &ptr.options, Stream::StandardOutput(ref ptr) => &ptr.options, @@ -593,7 +614,7 @@ impl Stream { Stream::NamedTcp(ref mut ptr) => &mut ptr.options, Stream::NamedTls(ref mut ptr) => &mut ptr.options, Stream::HttpRead(ref mut ptr) => &mut ptr.options, - Stream::HttpWrite(ref mut ptr) => &mut ptr.options, + Stream::HttpWrite(ref mut ptr) => &mut ptr.options, Stream::Null(ref mut options) => options, Stream::Readline(ref mut ptr) => &mut ptr.options, Stream::StandardOutput(ref mut ptr) => &mut ptr.options, @@ -611,7 +632,7 @@ impl Stream { Stream::NamedTcp(ptr) => ptr.lines_read += incr_num_lines_read, Stream::NamedTls(ptr) => ptr.lines_read += incr_num_lines_read, Stream::HttpRead(ptr) => ptr.lines_read += incr_num_lines_read, - Stream::HttpWrite(_) => {} + Stream::HttpWrite(_) => {} Stream::Null(_) => {} Stream::Readline(ptr) => ptr.lines_read += incr_num_lines_read, Stream::StandardOutput(ptr) => ptr.lines_read += incr_num_lines_read, @@ -629,7 +650,7 @@ impl Stream { Stream::NamedTcp(ptr) => ptr.lines_read = value, Stream::NamedTls(ptr) => ptr.lines_read = value, Stream::HttpRead(ptr) => ptr.lines_read = value, - Stream::HttpWrite(_) => {} + Stream::HttpWrite(_) => {} Stream::Null(_) => {} Stream::Readline(ptr) => ptr.lines_read = value, Stream::StandardOutput(ptr) => ptr.lines_read = value, @@ -647,7 +668,7 @@ impl Stream { Stream::NamedTcp(ptr) => ptr.lines_read, Stream::NamedTls(ptr) => ptr.lines_read, Stream::HttpRead(ptr) => ptr.lines_read, - Stream::HttpWrite(_) => 0, + Stream::HttpWrite(_) => 0, Stream::Null(_) => 0, Stream::Readline(ptr) => ptr.lines_read, Stream::StandardOutput(ptr) => ptr.lines_read, @@ -669,7 +690,7 @@ impl CharRead for Stream { Stream::OutputFile(_) | Stream::StandardError(_) | Stream::StandardOutput(_) | - Stream::HttpWrite(_) | + Stream::HttpWrite(_) | Stream::Null(_) => Some(Err(std::io::Error::new( ErrorKind::PermissionDenied, StreamError::ReadFromOutputStream, @@ -689,7 +710,7 @@ impl CharRead for Stream { Stream::OutputFile(_) | Stream::StandardError(_) | Stream::StandardOutput(_) | - Stream::HttpWrite(_) | + Stream::HttpWrite(_) | Stream::Null(_) => Some(Err(std::io::Error::new( ErrorKind::PermissionDenied, StreamError::ReadFromOutputStream, @@ -709,7 +730,7 @@ impl CharRead for Stream { Stream::OutputFile(_) | Stream::StandardError(_) | Stream::StandardOutput(_) | - Stream::HttpWrite(_) | + Stream::HttpWrite(_) | Stream::Null(_) => {} } } @@ -726,7 +747,7 @@ impl CharRead for Stream { Stream::OutputFile(_) | Stream::StandardError(_) | Stream::StandardOutput(_) | - Stream::HttpWrite(_) | + Stream::HttpWrite(_) | Stream::Null(_) => {} } } @@ -744,13 +765,13 @@ impl Read for Stream { Stream::StaticString(src) => (*src).read(buf), Stream::Byte(cursor) => (*cursor).read(buf), Stream::OutputFile(_) - | Stream::StandardError(_) - | Stream::StandardOutput(_) - | Stream::HttpWrite(_) - | Stream::Null(_) => Err(std::io::Error::new( - ErrorKind::PermissionDenied, - StreamError::ReadFromOutputStream, - )), + | Stream::StandardError(_) + | Stream::StandardOutput(_) + | Stream::HttpWrite(_) + | Stream::Null(_) => Err(std::io::Error::new( + ErrorKind::PermissionDenied, + StreamError::ReadFromOutputStream, + )), }; bytes_read @@ -766,7 +787,7 @@ impl Write for Stream { Stream::Byte(ref mut cursor) => cursor.get_mut().write(buf), Stream::StandardOutput(stream) => stream.write(buf), Stream::StandardError(stream) => stream.write(buf), - Stream::HttpWrite(ref mut stream) => stream.get_mut().write(buf), + Stream::HttpWrite(ref mut stream) => stream.get_mut().write(buf), Stream::HttpRead(_) | Stream::StaticString(_) | Stream::Readline(_) | @@ -786,7 +807,7 @@ impl Write for Stream { Stream::Byte(ref mut cursor) => cursor.stream.get_mut().flush(), Stream::StandardError(stream) => stream.stream.flush(), Stream::StandardOutput(stream) => stream.stream.flush(), - Stream::HttpWrite(ref mut stream) => stream.stream.get_mut().flush(), + Stream::HttpWrite(ref mut stream) => stream.stream.get_mut().flush(), Stream::HttpRead(_) | Stream::StaticString(_) | Stream::Readline(_) | @@ -863,19 +884,38 @@ impl PartialEq for Stream { impl Eq for Stream {} +fn cursor_position(past_end_of_stream: &mut bool, cursor: &Cursor, cursor_len: u64) -> AtEndOfStream { + let position = cursor.position(); + + let at_end_of_stream = match position.cmp(&cursor_len) { + Ordering::Equal => AtEndOfStream::At, + Ordering::Greater => { + *past_end_of_stream = true; + AtEndOfStream::Past + } + Ordering::Less => AtEndOfStream::Not, + }; + + at_end_of_stream +} + impl Stream { #[inline] pub(crate) fn position(&mut self) -> Option<(u64, usize)> { // returns lines_read, position. let result = match self { + Stream::Byte(byte_stream_layout) => { + Some(byte_stream_layout.stream.get_ref().0.position()) + } + Stream::StaticString(string_stream_layout) => { + Some(string_stream_layout.stream.stream.position()) + } Stream::InputFile(file_stream) => { file_stream.position() } - Stream::NamedTcp(..) - | Stream::NamedTls(..) - | Stream::Readline(..) - | Stream::StaticString(..) - | Stream::Byte(..) => Some(0), + Stream::NamedTcp(..) | Stream::NamedTls(..) | Stream::Readline(..) => { + Some(0) + } _ => None, }; @@ -913,7 +953,7 @@ impl Stream { Stream::NamedTcp(stream) => stream.past_end_of_stream, Stream::NamedTls(stream) => stream.past_end_of_stream, Stream::HttpRead(stream) => stream.past_end_of_stream, - Stream::HttpWrite(stream) => stream.past_end_of_stream, + Stream::HttpWrite(stream) => stream.past_end_of_stream, Stream::Null(_) => false, Stream::Readline(stream) => stream.past_end_of_stream, Stream::StandardOutput(stream) => stream.past_end_of_stream, @@ -936,7 +976,7 @@ impl Stream { Stream::NamedTcp(stream) => stream.past_end_of_stream = value, Stream::NamedTls(stream) => stream.past_end_of_stream = value, Stream::HttpRead(stream) => stream.past_end_of_stream = value, - Stream::HttpWrite(stream) => stream.past_end_of_stream = value, + Stream::HttpWrite(stream) => stream.past_end_of_stream = value, Stream::Null(_) => {} Stream::Readline(stream) => stream.past_end_of_stream = value, Stream::StandardOutput(stream) => stream.past_end_of_stream = value, @@ -950,38 +990,61 @@ impl Stream { return AtEndOfStream::Past; } - if let Stream::InputFile(stream_layout) = self { - let position = stream_layout.position(); + match self { + Stream::Byte(stream_layout) => { + let StreamLayout { + past_end_of_stream, + stream, + .. + } = &mut **stream_layout; - let StreamLayout { - past_end_of_stream, - stream, - .. - } = &mut **stream_layout; + let cursor_len = stream.get_ref().0.get_ref().len() as u64; + cursor_position(past_end_of_stream, &stream.get_ref().0, cursor_len) + } + Stream::StaticString(stream_layout) => { + let StreamLayout { + past_end_of_stream, + stream, + .. + } = &mut **stream_layout; - match stream.get_ref().file.metadata() { - Ok(metadata) => { - if let Some(position) = position { - return match position.cmp(&metadata.len()) { - Ordering::Equal => AtEndOfStream::At, - Ordering::Less => AtEndOfStream::Not, - Ordering::Greater => { - *past_end_of_stream = true; - AtEndOfStream::Past + let cursor_len = stream.stream.get_ref().len() as u64; + cursor_position(past_end_of_stream, &stream.stream, cursor_len) + } + Stream::InputFile(stream_layout) => { + let position = stream_layout.position(); + + let StreamLayout { + past_end_of_stream, + stream, + .. + } = &mut **stream_layout; + + match stream.get_ref().file.metadata() { + Ok(metadata) => { + if let Some(position) = position { + match position.cmp(&metadata.len()) { + Ordering::Equal => AtEndOfStream::At, + Ordering::Less => AtEndOfStream::Not, + Ordering::Greater => { + *past_end_of_stream = true; + AtEndOfStream::Past + } } - }; - } else { + } else { + *past_end_of_stream = true; + AtEndOfStream::Past + } + } + _ => { *past_end_of_stream = true; AtEndOfStream::Past } } - _ => { - *past_end_of_stream = true; - AtEndOfStream::Past - } } - } else { - AtEndOfStream::Not + _ => { + AtEndOfStream::Not + } } } @@ -1000,10 +1063,10 @@ impl Stream { pub(crate) fn mode(&self) -> Atom { match self { Stream::Byte(_) - | Stream::Readline(_) - | Stream::StaticString(_) - | Stream::HttpRead(_) - | Stream::InputFile(..) => atom!("read"), + | Stream::Readline(_) + | Stream::StaticString(_) + | Stream::HttpRead(_) + | Stream::InputFile(..) => atom!("read"), Stream::NamedTcp(..) | Stream::NamedTls(..) => atom!("read_append"), Stream::OutputFile(file) if file.is_append => atom!("append"), Stream::OutputFile(_) | Stream::StandardError(_) | Stream::StandardOutput(_) | Stream::HttpWrite(_) => atom!("write"), @@ -1077,12 +1140,17 @@ impl Stream { #[inline] pub(crate) fn from_http_sender( - body_writer: Sender, + response: TypedArenaPtr, + status_code: u16, + headers: hyper::HeaderMap, arena: &mut Arena, ) -> Self { Stream::HttpWrite(arena_alloc!( StreamLayout::new(CharReader::new(HttpWriteStream { - body_writer + response, + status_code, + headers, + buffer: Vec::new(), })), arena )) @@ -1135,11 +1203,11 @@ impl Stream { Stream::HttpWrite(ref mut http_stream) => { unsafe { http_stream.set_tag(ArenaHeaderTag::Dropped); - std::ptr::drop_in_place(&mut http_stream.inner_mut().body_writer as *mut _); + std::ptr::drop_in_place(&mut http_stream.inner_mut().buffer as *mut _); } Ok(()) - } + } Stream::InputFile(mut file_stream) => { // close the stream by dropping the inner File. unsafe { @@ -1175,12 +1243,12 @@ impl Stream { pub(crate) fn is_input_stream(&self) -> bool { match self { Stream::NamedTcp(..) - | Stream::NamedTls(..) - | Stream::HttpRead(..) - | Stream::Byte(_) - | Stream::Readline(_) - | Stream::StaticString(_) - | Stream::InputFile(..) => true, + | Stream::NamedTls(..) + | Stream::HttpRead(..) + | Stream::Byte(_) + | Stream::Readline(_) + | Stream::StaticString(_) + | Stream::InputFile(..) => true, _ => false, } } @@ -1189,12 +1257,12 @@ impl Stream { pub(crate) fn is_output_stream(&self) -> bool { match self { Stream::StandardError(_) - | Stream::StandardOutput(_) - | Stream::NamedTcp(..) - | Stream::NamedTls(..) - | Stream::HttpWrite(..) - | Stream::Byte(_) - | Stream::OutputFile(..) => true, + | Stream::StandardOutput(_) + | Stream::NamedTcp(..) + | Stream::NamedTls(..) + | Stream::HttpWrite(..) + | Stream::Byte(_) + | Stream::OutputFile(..) => true, _ => false, } } @@ -1242,12 +1310,9 @@ impl Stream { } } Stream::InputFile(ref mut file) => { - let mut b = [0u8; 1]; - - match file.read(&mut b)? { - 1 => { - file.stream.get_mut().file.seek(SeekFrom::Current(-1))?; - Ok(b[0]) + match file.peek_byte() { + Some(result) => { + Ok(result?) } _ => Err(std::io::Error::new( ErrorKind::UnexpectedEof, @@ -1283,7 +1348,7 @@ impl MachineState { match eof_action { EOFAction::Error => { stream.set_past_end_of_stream(true); - return Err(self.open_past_eos_error(stream, caller, arity)); + Err(self.open_past_eos_error(stream, caller, arity)) } EOFAction::EOFCode => { let end_of_stream = if stream.options().stream_type() == StreamType::Binary { @@ -1313,101 +1378,101 @@ impl MachineState { stream_type: HeapCellValue, ) -> StreamOptions { let alias = read_heap_cell!(self.store(MachineState::deref(self, alias)), - (HeapCellValueTag::Atom, (name, arity)) => { - debug_assert_eq!(arity, 0); + (HeapCellValueTag::Atom, (name, arity)) => { + debug_assert_eq!(arity, 0); - if name != atom!("[]") { - Some(name) - } else { - None - } - } - (HeapCellValueTag::Str, s) => { - let (name, arity) = cell_as_atom_cell!(self.heap[s]) - .get_name_and_arity(); + if name != atom!("[]") { + Some(name) + } else { + None + } + } + (HeapCellValueTag::Str, s) => { + let (name, arity) = cell_as_atom_cell!(self.heap[s]) + .get_name_and_arity(); - debug_assert_eq!(arity, 0); + debug_assert_eq!(arity, 0); - if name != atom!("[]") { - Some(name) - } else { - None - } - } - _ => { - None - } + if name != atom!("[]") { + Some(name) + } else { + None + } + } + _ => { + None + } ); let eof_action = read_heap_cell!(self.store(MachineState::deref(self, eof_action)), - (HeapCellValueTag::Atom, (name, arity)) => { - debug_assert_eq!(arity, 0); + (HeapCellValueTag::Atom, (name, arity)) => { + debug_assert_eq!(arity, 0); - match name { - atom!("eof_code") => EOFAction::EOFCode, - atom!("error") => EOFAction::Error, - atom!("reset") => EOFAction::Reset, - _ => unreachable!(), - } - } - (HeapCellValueTag::Str, s) => { - let (name, arity) = cell_as_atom_cell!(self.heap[s]) - .get_name_and_arity(); + match name { + atom!("eof_code") => EOFAction::EOFCode, + atom!("error") => EOFAction::Error, + atom!("reset") => EOFAction::Reset, + _ => unreachable!(), + } + } + (HeapCellValueTag::Str, s) => { + let (name, arity) = cell_as_atom_cell!(self.heap[s]) + .get_name_and_arity(); - debug_assert_eq!(arity, 0); + debug_assert_eq!(arity, 0); - match name { - atom!("eof_code") => EOFAction::EOFCode, - atom!("error") => EOFAction::Error, - atom!("reset") => EOFAction::Reset, - _ => unreachable!(), - } - } - _ => { - unreachable!() - } + match name { + atom!("eof_code") => EOFAction::EOFCode, + atom!("error") => EOFAction::Error, + atom!("reset") => EOFAction::Reset, + _ => unreachable!(), + } + } + _ => { + unreachable!() + } ); let reposition = read_heap_cell!(self.store(MachineState::deref(self, reposition)), - (HeapCellValueTag::Atom, (name, arity)) => { - debug_assert_eq!(arity, 0); - name == atom!("true") - } - (HeapCellValueTag::Str, s) => { - let (name, arity) = cell_as_atom_cell!(self.heap[s]) - .get_name_and_arity(); + (HeapCellValueTag::Atom, (name, arity)) => { + debug_assert_eq!(arity, 0); + name == atom!("true") + } + (HeapCellValueTag::Str, s) => { + let (name, arity) = cell_as_atom_cell!(self.heap[s]) + .get_name_and_arity(); - debug_assert_eq!(arity, 0); - name == atom!("true") - } - _ => { - unreachable!() - } + debug_assert_eq!(arity, 0); + name == atom!("true") + } + _ => { + unreachable!() + } ); let stream_type = read_heap_cell!(self.store(MachineState::deref(self, stream_type)), - (HeapCellValueTag::Atom, (name, arity)) => { - debug_assert_eq!(arity, 0); - match name { - atom!("text") => StreamType::Text, - atom!("binary") => StreamType::Binary, - _ => unreachable!(), - } - } - (HeapCellValueTag::Str, s) => { - let (name, arity) = cell_as_atom_cell!(self.heap[s]) - .get_name_and_arity(); + (HeapCellValueTag::Atom, (name, arity)) => { + debug_assert_eq!(arity, 0); + match name { + atom!("text") => StreamType::Text, + atom!("binary") => StreamType::Binary, + _ => unreachable!(), + } + } + (HeapCellValueTag::Str, s) => { + let (name, arity) = cell_as_atom_cell!(self.heap[s]) + .get_name_and_arity(); - debug_assert_eq!(arity, 0); - match name { - atom!("text") => StreamType::Text, - atom!("binary") => StreamType::Binary, - _ => unreachable!(), - } - } - _ => { - unreachable!() - } + debug_assert_eq!(arity, 0); + match name { + atom!("text") => StreamType::Text, + atom!("binary") => StreamType::Binary, + _ => unreachable!(), + } + } + _ => { + unreachable!() + } ); let mut options = StreamOptions::default(); @@ -1430,60 +1495,60 @@ impl MachineState { let addr = self.store(MachineState::deref(self, addr)); read_heap_cell!(addr, - (HeapCellValueTag::Atom, (name, arity)) => { - debug_assert_eq!(arity, 0); + (HeapCellValueTag::Atom, (name, arity)) => { + debug_assert_eq!(arity, 0); - return match stream_aliases.get(&name) { - Some(stream) if !stream.is_null_stream() => Ok(*stream), - _ => { - let stub = functor_stub(caller, arity); - let addr = atom_as_cell!(name); + return match stream_aliases.get(&name) { + Some(stream) if !stream.is_null_stream() => Ok(*stream), + _ => { + let stub = functor_stub(caller, arity); + let addr = atom_as_cell!(name); - let existence_error = self.existence_error(ExistenceError::Stream(addr)); + let existence_error = self.existence_error(ExistenceError::Stream(addr)); - Err(self.error_form(existence_error, stub)) - } - }; - } - (HeapCellValueTag::Str, s) => { - let (name, arity) = cell_as_atom_cell!(self.heap[s]) - .get_name_and_arity(); + Err(self.error_form(existence_error, stub)) + } + }; + } + (HeapCellValueTag::Str, s) => { + let (name, arity) = cell_as_atom_cell!(self.heap[s]) + .get_name_and_arity(); - debug_assert_eq!(arity, 0); + debug_assert_eq!(arity, 0); - return match stream_aliases.get(&name) { - Some(stream) if !stream.is_null_stream() => Ok(*stream), - _ => { - let stub = functor_stub(caller, arity); - let addr = atom_as_cell!(name); + return match stream_aliases.get(&name) { + Some(stream) if !stream.is_null_stream() => Ok(*stream), + _ => { + let stub = functor_stub(caller, arity); + let addr = atom_as_cell!(name); - let existence_error = self.existence_error(ExistenceError::Stream(addr)); + let existence_error = self.existence_error(ExistenceError::Stream(addr)); - Err(self.error_form(existence_error, stub)) - } - }; - } - (HeapCellValueTag::Cons, ptr) => { - match_untyped_arena_ptr!(ptr, - (ArenaHeaderTag::Stream, stream) => { - return if stream.is_null_stream() { - Err(self.open_permission_error(stream_as_cell!(stream), caller, arity)) - } else { - Ok(stream) - }; - } - (ArenaHeaderTag::Dropped, _value) => { - let stub = functor_stub(caller, arity); - let err = self.existence_error(ExistenceError::Stream(addr)); + Err(self.error_form(existence_error, stub)) + } + }; + } + (HeapCellValueTag::Cons, ptr) => { + match_untyped_arena_ptr!(ptr, + (ArenaHeaderTag::Stream, stream) => { + return if stream.is_null_stream() { + Err(self.open_permission_error(stream_as_cell!(stream), caller, arity)) + } else { + Ok(stream) + }; + } + (ArenaHeaderTag::Dropped, _value) => { + let stub = functor_stub(caller, arity); + let err = self.existence_error(ExistenceError::Stream(addr)); - return Err(self.error_form(err, stub)); - } - _ => { - } - ); - } - _ => { - } + return Err(self.error_form(err, stub)); + } + _ => { + } + ); + } + _ => { + } ); let stub = functor_stub(caller, arity); @@ -1497,20 +1562,10 @@ impl MachineState { } } - pub(crate) fn open_parsing_stream( - &mut self, - mut stream: Stream, - stub_name: Atom, - stub_arity: usize, - ) -> Result { + pub(crate) fn open_parsing_stream(&mut self, mut stream: Stream) -> Result { match stream.peek_char() { None => Ok(stream), // empty stream is handled gracefully by Lexer::eof - Some(Err(e)) => { - let err = self.session_error(SessionError::from(e)); - let stub = functor_stub(stub_name, stub_arity); - - Err(self.error_form(err, stub)) - } + Some(Err(e)) => Err(ParserError::IO(e)), Some(Ok(c)) => { if c == '\u{feff}' { // skip UTF-8 BOM @@ -1531,7 +1586,15 @@ impl MachineState { arity: usize, ) -> MachineStub { let stub = functor_stub(caller, arity); - let err = self.permission_error(perm, err_atom, stream_as_cell!(stream)); + let err = self.permission_error( + perm, + err_atom, + if let Some(alias) = stream.options().get_alias() { + atom_as_cell!(alias) + } else { + stream_as_cell!(stream) + }, + ); self.error_form(err, stub) } @@ -1699,7 +1762,7 @@ impl MachineState { } ErrorKind::PermissionDenied => { // 8.11.5.3k) - return Err(self.open_permission_error(self[temp_v!(1)], atom!("open"), 4)); + return Err(self.open_permission_error(self.registers[1], atom!("open"), 4)); } _ => { let stub = functor_stub(atom!("open"), 4); diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 1160c1cd..88d1e2ca 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -1,14 +1,16 @@ use crate::parser::ast::*; use crate::parser::parser::*; +use dashu::integer::UBig; use lazy_static::lazy_static; use crate::arena::*; use crate::atom_table::*; use crate::forms::*; +use crate::ffi::*; use crate::heap_iter::*; use crate::heap_print::*; -use crate::http::{self, HttpListener, HttpResponse}; +use crate::http::{HttpService, HttpListener, HttpResponse}; use crate::instructions::*; use crate::machine; use crate::machine::{Machine, VERIFY_ATTR_INTERRUPT_LOC, get_structure_index}; @@ -23,23 +25,26 @@ use crate::machine::preprocessor::to_op_decl; use crate::machine::stack::*; use crate::machine::streams::*; use crate::parser::char_reader::*; -use crate::parser::rug::Integer; -use crate::parser::rug::rand::RandState; +use crate::parser::dashu::Integer; use crate::read::*; use crate::types::*; +use rand::{Rng, SeedableRng}; +use rand::rngs::StdRng; use ordered_float::OrderedFloat; use fxhash::{FxBuildHasher, FxHasher}; use indexmap::IndexSet; -use ref_thread_local::{RefThreadLocal, ref_thread_local}; +pub(crate) use ref_thread_local::RefThreadLocal; +use std::borrow::BorrowMut; use std::cell::Cell; use std::cmp::Ordering; use std::collections::BTreeSet; -use std::convert::{TryFrom, Infallible}; +use std::convert::TryFrom; use std::env; +use std::ffi::CString; use std::fs; use std::hash::{BuildHasher, BuildHasherDefault}; use std::io::{ErrorKind, Read, Write}; @@ -49,9 +54,7 @@ use std::net::{TcpListener, TcpStream, SocketAddr, ToSocketAddrs}; use std::num::NonZeroU32; use std::ops::Sub; use std::process; -use std::rc::Rc; use std::str::FromStr; -use std::sync::Arc; use chrono::{offset::Local, DateTime}; use cpu_time::ProcessTime; @@ -79,17 +82,12 @@ use base64; use roxmltree; use select; -use hyper::{Body, Server, Client, HeaderMap, Method, Request, Response, Uri}; -use hyper::header::{HeaderName, HeaderValue}; -use hyper::body::Buf; -use hyper::service::{make_service_fn, service_fn}; -use hyper_tls::HttpsConnector; -use tokio::sync::Mutex; -use tokio::sync::mpsc::channel; - -ref_thread_local! { - pub(crate) static managed RANDOM_STATE: RandState<'static> = RandState::new(); -} +use hyper::server::conn::http1; +use hyper::header::{HeaderValue, HeaderName}; +use hyper::{HeaderMap, Method}; +use http_body_util::BodyExt; +use bytes::Buf; +use reqwest::Url; pub(crate) fn get_key() -> KeyEvent { let key; @@ -119,6 +117,7 @@ pub struct BrentAlgState { pub power: usize, pub lam: usize, pub pstr_chars: usize, + max_steps: i64, } impl BrentAlgState { @@ -129,6 +128,7 @@ impl BrentAlgState { power: 1, lam: 0, pstr_chars: 0, + max_steps: -1, } } @@ -160,72 +160,95 @@ impl BrentAlgState { return self.lam + self.pstr_chars + self.power - 1; } + #[inline(always)] + pub fn exhausted_max_steps(&self) -> bool { + self.max_steps > -1 && self.num_steps() as i64 >= self.max_steps + } + pub fn to_result(mut self, heap: &[HeapCellValue]) -> CycleSearchResult { + /* if let Some(var) = heap[self.hare].as_var() { return CycleSearchResult::PartialList(self.num_steps(), var); } + */ - read_heap_cell!(heap[self.hare], - (HeapCellValueTag::PStrOffset) => { - let n = cell_as_fixnum!(heap[self.hare+1]).get_num() as usize; + loop { + read_heap_cell!(heap[self.hare], + (HeapCellValueTag::PStrOffset) => { + let (pstr_loc, offset) = pstr_loc_and_offset(heap, self.hare); + let offset = offset.get_num() as usize; - let pstr = cell_as_string!(heap[self.hare]); - self.pstr_chars += pstr.as_str_from(n).chars().count(); + let pstr = cell_as_string!(heap[self.hare]); + self.pstr_chars += pstr.as_str_from(offset).chars().count(); - return CycleSearchResult::PStrLocation(self.num_steps(), n); - } - (HeapCellValueTag::Atom, (name, arity)) => { - return if name == atom!("[]") && arity == 0 { - CycleSearchResult::ProperList(self.num_steps()) - } else { - CycleSearchResult::NotList(self.num_steps(), heap[self.hare]) - }; - } - (HeapCellValueTag::Str, s) => { - let (name, arity) = cell_as_atom_cell!(heap[s]) - .get_name_and_arity(); + return CycleSearchResult::PStrLocation(self.num_steps(), pstr_loc, offset); + } + (HeapCellValueTag::PStrLoc, l) => { + let (_pstr_loc, offset) = pstr_loc_and_offset(heap, l); + let offset = offset.get_num() as usize; + return CycleSearchResult::PStrLocation(self.num_steps(), l, offset); + } + (HeapCellValueTag::Atom, (name, arity)) => { + return if name == atom!("[]") && arity == 0 { + CycleSearchResult::ProperList(self.num_steps()) + } else { + CycleSearchResult::NotList(self.num_steps(), heap[self.hare]) + }; + } + (HeapCellValueTag::Str, s) => { + let (name, arity) = cell_as_atom_cell!(heap[s]) + .get_name_and_arity(); - return if name == atom!("[]") && arity == 0 { - CycleSearchResult::ProperList(self.num_steps()) - } else { - CycleSearchResult::NotList(self.num_steps(), heap[self.hare]) - }; - } - (HeapCellValueTag::Lis, l) => { - return CycleSearchResult::UntouchedList(self.num_steps(), l); - } - _ => { - return CycleSearchResult::NotList(self.num_steps(), heap[self.hare]); - } - ); + return if name == atom!("[]") && arity == 0 { + CycleSearchResult::ProperList(self.num_steps()) + } else { + CycleSearchResult::NotList(self.num_steps(), heap[self.hare]) + }; + } + (HeapCellValueTag::Lis, l) => { + return CycleSearchResult::UntouchedList(self.num_steps(), l); + } + (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => { + if h == self.hare { + let var = heap[self.hare].as_var().unwrap(); + return CycleSearchResult::PartialList(self.num_steps(), var); + } else { + self.hare = h; + } + } + _ => { + return CycleSearchResult::NotList(self.num_steps(), heap[self.hare]); + } + ); + } } - fn add_pstr_chars_and_step(&mut self, heap: &[HeapCellValue], h: usize) -> Option { + fn add_pstr_offset_chars(&mut self, heap: &[HeapCellValue], h: usize, offset: usize) -> Option { read_heap_cell!(heap[h], (HeapCellValueTag::CStr, cstr_atom) => { let cstr = PartialString::from(cstr_atom); + let num_chars = cstr.as_str_from(offset).chars().count(); - self.pstr_chars += cstr.as_str_from(0).chars().count(); - Some(CycleSearchResult::ProperList(self.num_steps())) + if self.max_steps == -1 || self.num_steps() + num_chars < self.max_steps as usize { + self.pstr_chars += num_chars; + Some(CycleSearchResult::ProperList(self.num_steps())) + } else { + let offset = self.num_steps() + num_chars - self.max_steps as usize; + self.pstr_chars += offset; + Some(CycleSearchResult::PStrLocation(self.max_steps as usize, h, offset)) + } } (HeapCellValueTag::PStr, pstr_atom) => { let pstr = PartialString::from(pstr_atom); + let num_chars = pstr.as_str_from(offset).chars().count(); - self.pstr_chars += pstr.as_str_from(0).chars().count() - 1; - self.step(h+1) - } - (HeapCellValueTag::PStrOffset, offset) => { - let pstr = cell_as_string!(heap[offset]); - let n = cell_as_fixnum!(heap[h+1]).get_num() as usize; - - self.pstr_chars += pstr.as_str_from(n).chars().count(); - - if let HeapCellValueTag::PStr = heap[offset].get_tag() { - self.pstr_chars -= 1; - self.step(offset+1) + if self.max_steps == -1 || self.num_steps() + num_chars < self.max_steps as usize { + self.pstr_chars += num_chars - 1; + self.step(h+1) } else { - debug_assert!(heap[offset].get_tag() == HeapCellValueTag::CStr); - Some(CycleSearchResult::ProperList(self.num_steps())) + let offset = self.num_steps() + num_chars - self.max_steps as usize; + self.pstr_chars += offset; + Some(CycleSearchResult::PStrLocation(self.max_steps as usize, h, offset)) } } _ => { @@ -234,6 +257,18 @@ impl BrentAlgState { ) } + fn add_pstr_chars_and_step(&mut self, heap: &[HeapCellValue], h: usize) -> Option { + read_heap_cell!(heap[h], + (HeapCellValueTag::PStrOffset, l) => { + let (pstr_loc, offset) = pstr_loc_and_offset(heap, l); + self.add_pstr_offset_chars(heap, pstr_loc, offset.get_num() as usize) + } + _ => { + self.add_pstr_offset_chars(heap, h, 0) + } + ) + } + #[inline(always)] fn cycle_step(&mut self, heap: &[HeapCellValue]) -> Option { loop { @@ -387,7 +422,7 @@ impl BrentAlgState { } if pstr_chars + 1 > max_steps { - return CycleSearchResult::PStrLocation(max_steps, h_offset); + return CycleSearchResult::PStrLocation(max_steps, h_offset, max_steps); } h_offset+1 @@ -443,9 +478,10 @@ impl BrentAlgState { brent_st.power += 1; // advance a step. brent_st.pstr_chars = pstr_chars; + brent_st.max_steps = max_steps as i64; loop { - if brent_st.num_steps() >= max_steps { + if brent_st.exhausted_max_steps() { return brent_st.to_result(&heap); } @@ -456,14 +492,81 @@ impl BrentAlgState { } } +#[derive(Debug)] +enum MatchSite { + NoMatchVarTail(usize), // no match, we refer to the location of the uninstantiated tail instead. + Match(usize), // a match +} + +#[derive(Debug)] +struct AttrListMatch { + match_site: MatchSite, + prev_tail: Option, +} + impl MachineState { + #[inline(always)] + pub(crate) fn unattributed_var(&mut self) { + let attr_var = self.store(self.deref(self.registers[1])); + + if !attr_var.is_var() { + self.fail = true; + return; + } + + read_heap_cell!(attr_var, + (HeapCellValueTag::AttrVar, h) => { + let list_cell = self.store(self.deref(self.heap[h+1])); + self.fail = list_cell.get_tag() == HeapCellValueTag::Lis; + } + _ => { + } + ); + } + + pub(crate) fn get_attr_var_list(&mut self, attr_var: HeapCellValue) -> Option { + read_heap_cell!(attr_var, + (HeapCellValueTag::AttrVar, h) => { + Some(h + 1) + } + (HeapCellValueTag::Var | HeapCellValueTag::StackVar) => { + // create an AttrVar in the heap. + let h = self.heap.len(); + + self.heap.push(attr_var_as_cell!(h)); + self.heap.push(heap_loc_as_cell!(h+1)); + + self.bind(Ref::attr_var(h), attr_var); + + Some(h + 1) + } + _ => { + None + } + ) + } + + pub(crate) fn name_and_arity_from_heap(&self, cell: HeapCellValue) -> Option { + read_heap_cell!(self.store(self.deref(cell)), + (HeapCellValueTag::Str, s) => { + Some(cell_as_atom_cell!(self.heap[s]).get_name_and_arity()) + } + (HeapCellValueTag::Atom, (name, _arity)) => { + Some((name, 0)) + } + _ => { + None + } + ) + } + #[inline] pub(crate) fn variable_set( &mut self, seen_set: &mut IndexSet, value: HeapCellValue, ) { - let mut iter = stackful_preorder_iter(&mut self.heap, value); + let mut iter = stackful_preorder_iter(&mut self.heap, &mut self.stack, value); while let Some(value) = iter.next() { let value = unmark_cell_bits!(value); @@ -570,8 +673,25 @@ impl MachineState { }; match search_result { - CycleSearchResult::PStrLocation(steps, pstr_loc) => { - self.finalize_skip_max_list(steps as i64, pstr_loc_as_cell!(pstr_loc)); + CycleSearchResult::PStrLocation(steps, pstr_loc, offset) => { + let steps = if max_steps > - 1 { + std::cmp::min(max_steps, steps as i64) + } else { + steps as i64 + }; + + let cell = if offset > 0 { + let h = self.heap.len(); + + self.heap.push(pstr_offset_as_cell!(pstr_loc)); + self.heap.push(fixnum_as_cell!(Fixnum::build_with(offset as i64))); + + pstr_loc_as_cell!(h) + } else { + pstr_loc_as_cell!(pstr_loc) + }; + + self.finalize_skip_max_list(steps, cell); } CycleSearchResult::UntouchedList(n, l) => { self.finalize_skip_max_list(n as i64, list_loc_as_cell!(l)); @@ -653,7 +773,7 @@ impl MachineState { let mut seen_set = IndexSet::new(); { - let mut iter = stackful_post_order_iter(&mut self.heap, term); + let mut iter = stackful_post_order_iter(&mut self.heap, &mut self.stack, term); while let Some(value) = iter.next() { if iter.parent_stack_len() >= max_depth { @@ -679,7 +799,7 @@ impl MachineState { unify_fn!(*self, list_of_vars, outcome); } - #[inline] + #[inline(always)] pub(crate) fn install_new_block(&mut self, value: HeapCellValue) -> usize { let value = self.store(self.deref(value)); @@ -763,54 +883,92 @@ impl MachineState { indices: &IndexStore, stub_gen: impl Fn() -> FunctorStub, ) -> CallResult { + use crate::parser::lexer::*; + let nx = self.store(self.deref(self.registers[2])); - - if let Some(c) = string.chars().last() { - if layout_char!(c) { - let (line_num, col_num) = string.chars().fold((0, 0), |(line_num, col_num), c| { - if new_line_char!(c) { - (1 + line_num, 0) - } else { - (line_num, col_num + 1) - } - }); - let err = ParserError::UnexpectedChar(c, line_num, col_num); - let err = self.syntax_error(err); - - return Err(self.error_form(err, stub_gen())); - } - } - - let mut dot_buf: [u8; '.'.len_utf8()] = [0u8]; - '.'.encode_utf8(&mut dot_buf); - + let add_dot = !string.ends_with("."); let cursor = std::io::Cursor::new(string); - let iter = std::io::Read::chain(cursor, std::io::Cursor::new(dot_buf)); - let mut parser = Parser::new(CharReader::new(iter), self); + let iter = std::io::Read::chain( + cursor, + { + let mut dot_buf: [u8; '.'.len_utf8()] = [0u8]; - match parser.read_term(&CompositeOpDir::new(&indices.op_dir, None)) { + if add_dot { + '.'.encode_utf8(&mut dot_buf); + } + + std::io::Cursor::new(dot_buf) + }, + ); + + let mut lexer = Lexer::new(CharReader::new(iter), self); + let mut tokens = vec![]; + + match lexer.next_token() { + Ok(token @ Token::Literal(Literal::Atom(atom!("-")) | Literal::Char('-'))) => { + tokens.push(token); + + if let Ok(token) = lexer.next_token() { + tokens.push(token); + } + } + Ok(token) => { + tokens.push(token); + } Err(err) => { let err = self.syntax_error(err); return Err(self.error_form(err, stub_gen())); } - Ok(Term::Literal(_, Literal::Rational(n))) => { - self.unify_rational(n, nx); - } - Ok(Term::Literal(_, Literal::Float(n))) => { - self.unify_f64(n.as_ptr(), nx); - } - Ok(Term::Literal(_, Literal::Integer(n))) => { - self.unify_big_int(n, nx); - } - Ok(Term::Literal(_, Literal::Fixnum(n))) => { - self.unify_fixnum(n, nx); - } - _ => { - let err = ParserError::ParseBigInt(0, 0); - let err = self.syntax_error(err); + } - return Err(self.error_form(err, stub_gen())); + loop { + match lexer.lookahead_char() { + Err(e) if e.is_unexpected_eof() => { + let mut parser = Parser::from_lexer(lexer); + let op_dir = CompositeOpDir::new(&indices.op_dir, None); + + tokens.reverse(); + + match parser.read_term(&op_dir, Tokens::Provided(tokens)) { + Err(err) => { + let err = self.syntax_error(err); + return Err(self.error_form(err, stub_gen())); + } + Ok(Term::Literal(_, Literal::Rational(n))) => { + self.unify_rational(n, nx); + } + Ok(Term::Literal(_, Literal::Float(n))) => { + self.unify_f64(n.as_ptr(), nx); + } + Ok(Term::Literal(_, Literal::Integer(n))) => { + self.unify_big_int(n, nx); + } + Ok(Term::Literal(_, Literal::Fixnum(n))) => { + self.unify_fixnum(n, nx); + } + _ => { + let err = ParserError::ParseBigInt(0, 0); + let err = self.syntax_error(err); + + return Err(self.error_form(err, stub_gen())); + } + } + + break; + } + Ok('.') => { + lexer.skip_char('.'); + } + Ok(c) => { + let (line_num, col_num) = (lexer.line_num, lexer.col_num); + + let err = ParserError::UnexpectedChar(c, line_num, col_num); + let err = self.syntax_error(err); + + return Err(self.error_form(err, stub_gen())); + } + Err(_) => unreachable!(), } } @@ -837,7 +995,7 @@ impl MachineState { self.p = cp + 1; - // adjust cut point to occur after call_continuation. + /* if num_cells > 0 { if let HeapCellValueTag::Fixnum = self.heap[s + 2].get_tag() { and_frame[1] = fixnum_as_cell!(Fixnum::build_with(self.b as i64)); @@ -845,9 +1003,15 @@ impl MachineState { and_frame[1] = self.heap[s + 2]; } } + */ - for index in s + 3..s + 2 + num_cells { - and_frame[index - (s + 1)] = self.heap[index]; + for index in s + 2..s + 2 + num_cells { + if let HeapCellValueTag::CutPoint = self.heap[index].get_tag() { + // adjust cut point to occur after call_continuation. + and_frame[index - (s + 1)] = fixnum_as_cell!(Fixnum::as_cutpoint(self.b as i64)); + } else { + and_frame[index - (s + 1)] = self.heap[index]; + } } self.e = e; @@ -921,6 +1085,8 @@ impl MachineState { let mut string = String::new(); for addr in addrs { + let addr = self.store(self.deref(addr)); + match Number::try_from(addr) { Ok(Number::Fixnum(n)) => { match u32::try_from(n.get_num()) { @@ -989,25 +1155,151 @@ impl MachineState { impl Machine { #[inline(always)] - pub(crate) fn call_inline( + pub(crate) fn delete_all_attributes_from_var(&mut self) { + let attr_var = self.deref_register(1); + + if let HeapCellValueTag::AttrVar = attr_var.get_tag() { + let attr_var_loc = attr_var.get_value(); + self.machine_st.heap[attr_var_loc] = heap_loc_as_cell!(attr_var_loc); + self.machine_st.trail(TrailRef::Ref(Ref::attr_var(attr_var_loc))); + } + } + + #[inline(always)] + pub(crate) fn get_clause_p(&self, module_name: Atom) -> (usize, usize) { + use crate::machine::loader::CompilationTarget; + + let key_cell = self.machine_st.registers[1]; + let key = self.machine_st.name_and_arity_from_heap(key_cell).unwrap(); + + let compilation_target = if module_name == atom!("user") { + CompilationTarget::User + } else { + CompilationTarget::Module(module_name) + }; + + let skeleton = self.indices.get_predicate_skeleton( + &compilation_target, + &key, + ).unwrap(); + + if self.machine_st.b > self.machine_st.e { + let or_frame = self.machine_st.stack.index_or_frame(self.machine_st.b); + let bp = or_frame.prelude.bp; + + match &self.code[bp] { + &Instruction::IndexingCode(ref indexing_code) => { + match &indexing_code[or_frame.prelude.boip as usize] { + &IndexingLine::IndexedChoice(ref indexed_choice) => { + let p = or_frame.prelude.biip as usize - 1; + + match &indexed_choice[p] { + &IndexedChoiceInstruction::Try(offset) | + &IndexedChoiceInstruction::Retry(offset) => { + let clause_clause_loc = skeleton.core.clause_clause_locs[p]; + (clause_clause_loc, bp + offset) + } + &IndexedChoiceInstruction::Trust(_) => { + unreachable!() + } + } + } + _ => { + unreachable!() + } + } + } + _ => unreachable!() + } + } else { + let module_name = match compilation_target { + CompilationTarget::User => atom!("builtins"), + CompilationTarget::Module(target) => target, + }; + + let bp = self.indices + .get_predicate_code_index(atom!("$clause"), 2, module_name) + .and_then(|idx| idx.local()) + .unwrap(); + + macro_rules! extract_ptr { + ($ptr: expr) => { + match $ptr { + IndexingCodePtr::External(p) => return ( + skeleton.core.clause_clause_locs.back().cloned().unwrap(), + bp + p, + ), + IndexingCodePtr::Internal(boip) => boip, + _ => unreachable!(), + } + }; + } + + match &self.code[bp] { + &Instruction::IndexingCode(ref indexing_code) => { + let indexing_code_ptr = match &indexing_code[0] { + &IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, c, _, s)) => { + if key.1 > 0 { s } else { c } + } + _ => { + unreachable!() + } + }; + + let boip = extract_ptr!(indexing_code_ptr); + + let boip = match &indexing_code[boip] { + &IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(ref hm)) => { + boip + extract_ptr!(hm.get(&key).cloned().unwrap()) + } + &IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(ref hm)) => { + boip + extract_ptr!(hm.get(&Literal::Atom(key.0)).cloned().unwrap()) + } + _ => boip, + }; + + match &indexing_code[boip] { + &IndexingLine::IndexedChoice(ref indexed_choice) => { + return ( + skeleton.core.clause_clause_locs.back().cloned().unwrap(), + bp + indexed_choice.back().unwrap().offset(), + ); + } + _ => unreachable!(), + } + } + _ => { + return (skeleton.core.clause_clause_locs.back().cloned().unwrap(), bp); + } + } + } + } + + #[inline(always)] + pub(crate) fn deref_register(&self, i: usize) -> HeapCellValue { + self.machine_st.store(self.machine_st.deref(self.machine_st.registers[i])) + } + + #[inline(always)] + pub(crate) fn fast_call( &mut self, arity: usize, call_at_index: impl Fn(&mut Machine, Atom, usize, IndexPtr) -> CallResult, ) -> CallResult { let arity = arity - 1; - let goal = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let (mut module_name, mut goal) = self.machine_st.strip_module( + self.machine_st.registers[1], + heap_loc_as_cell!(0), + ); - let load_registers = |machine_st: &mut MachineState, goal: HeapCellValue| -> Option { + let load_registers = |machine_st: &mut MachineState, goal: HeapCellValue, goal_arity: usize| { read_heap_cell!(goal, - (HeapCellValueTag::Str, s) => { - let (name, goal_arity) = cell_as_atom_cell!(machine_st.heap[s]) - .get_name_and_arity(); - - if goal_arity > 0 { + (HeapCellValueTag::Str | HeapCellValueTag::Atom, s) => { + if goal_arity > 1 { for idx in (1 .. arity + 1).rev() { machine_st.registers[idx + goal_arity] = machine_st.registers[idx + 1]; } - } else { + } else if goal_arity == 0 { for idx in 1 .. arity + 1 { machine_st.registers[idx] = machine_st.registers[idx + 1]; } @@ -1016,8 +1308,6 @@ impl Machine { for idx in 1 .. goal_arity + 1 { machine_st.registers[idx] = machine_st.heap[s+idx]; } - - Some((name, goal_arity)) } _ => { unreachable!() @@ -1025,43 +1315,78 @@ impl Machine { ) }; - read_heap_cell!(goal, + let (mut name, mut goal_arity, index_cell_opt) = read_heap_cell!(goal, (HeapCellValueTag::Str, s) => { - let goal_arity = cell_as_atom_cell!(self.machine_st.heap[s]).get_arity(); + let (name, arity) = cell_as_atom_cell!(self.machine_st.heap[s]).get_name_and_arity(); - if self.machine_st.heap.len() > s + goal_arity + 1 { - let index_cell = self.machine_st.heap[s+goal_arity+1]; - - if let Some(code_index) = get_structure_index(index_cell) { - if code_index.is_undefined() { - self.machine_st.fail = true; - return Ok(()); - } - - match load_registers(&mut self.machine_st, goal) { - Some((name, goal_arity)) => { - let arity = goal_arity + arity; - self.machine_st.neck_cut(); - return call_at_index(self, name, arity, code_index.get()); - } - None => { - } - } - } - } + (name, arity, if self.machine_st.heap.len() > s + arity + 1 { + get_structure_index(self.machine_st.heap[s + arity + 1]) + } else { + None + }) + } + (HeapCellValueTag::Atom, (name, arity)) => { + debug_assert_eq!(arity, 0); + (name, arity, None) } _ => { + self.machine_st.fail = true; + return Ok(()); } ); + let mut arity = arity + goal_arity; + + let index_cell = index_cell_opt.or_else(|| { + let is_internal_call = name == atom!("$call") && goal_arity > 0; + + if !is_internal_call && self.indices.goal_expansion_defined((name, arity)) { + None + } else { + if is_internal_call { + debug_assert_eq!(goal.get_tag(), HeapCellValueTag::Str); + goal = self.machine_st.heap[goal.get_value()+1]; + (module_name, goal) = self.machine_st.strip_module(goal, module_name); + + if let Some((inner_name, inner_arity)) = self.machine_st.name_and_arity_from_heap(goal) { + arity -= goal_arity; + (name, goal_arity) = (inner_name, inner_arity); + arity += goal_arity; + } else { + return None; + } + } + + let module_name = if module_name.get_tag() != HeapCellValueTag::Atom { + if let Some(load_context) = self.load_contexts.last() { + load_context.module + } else { + atom!("user") + } + } else { + cell_as_atom!(module_name) + }; + + self.indices.get_predicate_code_index(name, arity, module_name) + } + }); + + if let Some(code_index) = index_cell { + if !code_index.is_undefined() { + load_registers(&mut self.machine_st, goal, goal_arity); + self.machine_st.neck_cut(); + return call_at_index(self, name, arity, code_index.get()); + } + } + self.machine_st.fail = true; Ok(()) } #[inline(always)] pub(crate) fn compile_inline_or_expanded_goal(&mut self) -> CallResult { - let goal = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); - let module_name = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[4])); + let goal = self.deref_register(1); + let module_name = self.deref_register(4); // supp_vars are the supplementary variables generated by // complete_partial_goal prior to goal_expansion. @@ -1107,10 +1432,15 @@ impl Machine { post_supp_args .zip(supp_vars.iter()) .all(|(arg_term, supp_var)| { - let arg_term = self.machine_st.store(self.machine_st.deref(arg_term)); + let (module_loc, arg_term) = self.machine_st.strip_module( + arg_term, + heap_loc_as_cell!(0), + ); - if arg_term.is_var() && supp_var.is_var() { - return arg_term == *supp_var; + if module_loc.is_var() || module_loc == atom_as_cell!(atom!("user")) { + if arg_term.is_var() && supp_var.is_var() { + return arg_term == *supp_var; + } } false @@ -1194,7 +1524,7 @@ impl Machine { let vars: Vec<_> = vars .union(&result.supp_vars) // difference + union does not cancel. - .map(|v| Term::Var(Cell::default(), Rc::new(format!("_{}", v.get_value())))) + .map(|v| Term::Var(Cell::default(), VarPtr::from(format!("_{}", v.get_value())))) .collect(); let helper_clause_loc = self.code.len(); @@ -1272,35 +1602,12 @@ impl Machine { } #[inline(always)] - pub(crate) fn prepare_call_clause(&mut self, arity: usize) -> CallResult { + pub(crate) fn strip_module(&mut self) { let (module_loc, qualified_goal) = self.machine_st.strip_module( - self.machine_st.registers[3], + self.machine_st.registers[1], self.machine_st.registers[2], ); - // the first three arguments don't belong to the containing call/N. - let arity = arity - 3; - - let (name, narity, s) = self.machine_st.setup_call_n_init_goal_info( - qualified_goal, - arity, - )?; - - let module_loc = self.machine_st.store(self.machine_st.deref(module_loc)); - - if module_loc.is_var() { - self.load_context_module(module_loc); - - if self.machine_st.fail { - self.machine_st.fail = false; - self.machine_st.unify_atom(atom!("user"), module_loc); - - if self.machine_st.fail { - return Ok(()); - } - } - } - let target_module_loc = self.machine_st.registers[2]; unify_fn!( @@ -1309,9 +1616,26 @@ impl Machine { target_module_loc ); - if self.machine_st.fail { - return Ok(()); - } + let target_qualified_goal = self.machine_st.registers[3]; + + unify_fn!( + &mut self.machine_st, + qualified_goal, + target_qualified_goal + ); + } + + #[inline(always)] + pub(crate) fn prepare_call_clause(&mut self, arity: usize) -> CallResult { + let qualified_goal = self.deref_register(2); + + // the first two arguments don't belong to the containing call/N. + let arity = arity - 2; + + let (name, narity, s) = self.machine_st.setup_call_n_init_goal_info( + qualified_goal, + arity, + )?; // assemble goal from pre-loaded (narity) and supplementary // (arity) arguments. @@ -1327,15 +1651,10 @@ impl Machine { } for idx in 1 .. arity + 1 { - self.machine_st.heap.push(self.machine_st.registers[3 + idx]); + self.machine_st.heap.push(self.machine_st.registers[2 + idx]); } - let index_cell = self.machine_st.heap[s + narity + 1]; - - if get_structure_index(index_cell).is_some() { - self.machine_st.heap.push(index_cell); - str_loc_as_cell!(h) - } else if narity + arity > 0 { + if narity + arity > 0 { str_loc_as_cell!(h) } else { heap_loc_as_cell!(h) @@ -1353,18 +1672,77 @@ impl Machine { Ok(()) } + #[inline(always)] + pub(crate) fn dynamic_module_resolution( + &mut self, + narity: usize, + ) -> Result<(Atom, PredicateKey), MachineStub> { + let module_name = self.deref_register(1); + + let module_name = read_heap_cell!(module_name, + (HeapCellValueTag::Atom, (name, _arity)) => { + debug_assert_eq!(_arity, 0); + name + } + (HeapCellValueTag::Str, s) => { + let (module_name, _arity) = cell_as_atom_cell!(self.machine_st.heap[s]) + .get_name_and_arity(); + + debug_assert_eq!(_arity, 0); + module_name + } + _ if module_name.is_var() => { + if let Some(load_context) = self.load_contexts.last() { + load_context.module + } else { + atom!("user") + } + } + _ => { + unreachable!() + } + ); + + let goal = self.deref_register(2); + + let (name, arity, s) = self.machine_st.setup_call_n_init_goal_info(goal, narity)?; + + // TODO: think we just need the 'Greater' branch here. + match arity.cmp(&2) { + Ordering::Less => { + for i in arity + 1..arity + narity + 1 { + self.machine_st.registers[i] = self.machine_st.registers[i + 2 - arity]; + } + } + Ordering::Greater => { + for i in (arity + 1..arity + narity + 1).rev() { + self.machine_st.registers[i] = self.machine_st.registers[i + 2 - arity]; + } + } + Ordering::Equal => {} + } + + let key = (name, arity + narity); + + for i in 1..arity + 1 { + self.machine_st.registers[i] = self.machine_st.heap[s + i]; + } + + Ok((module_name, key)) + } + #[inline(always)] pub(crate) fn is_reset_cont_marker(&self, p: usize) -> bool { match &self.code[p] { - &Instruction::CallResetContinuationMarker(_) | - &Instruction::ExecuteResetContinuationMarker(_) => true, + &Instruction::CallResetContinuationMarker | + &Instruction::ExecuteResetContinuationMarker => true, _ => false } } #[inline(always)] pub(crate) fn bind_from_register(&mut self) { - let reg = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let reg = self.deref_register(2); let n = match Number::try_from(reg) { Ok(Number::Fixnum(n)) => usize::try_from(n.get_num()).ok(), Ok(Number::Integer(n)) => n.to_usize(), @@ -1393,9 +1771,10 @@ impl Machine { Some(host) => { let hostname = self.machine_st.atom_tbl.build_with(host); + let a1 = self.deref_register(1); self.machine_st.unify_atom( hostname, - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])) + a1 ); return; @@ -1410,7 +1789,7 @@ impl Machine { #[inline(always)] pub(crate) fn current_input(&mut self) -> CallResult { - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let addr = self.deref_register(1); let stream = self.user_input; if let Some(var) = addr.as_var() { @@ -1445,7 +1824,7 @@ impl Machine { #[inline(always)] pub(crate) fn current_output(&mut self) -> CallResult { - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let addr = self.deref_register(1); let stream = self.user_output; if let Some(var) = addr.as_var() { @@ -1562,9 +1941,7 @@ impl Machine { #[inline(always)] pub(crate) fn file_time(&mut self) { if let Some(file) = self.machine_st.value_to_str_like(self.machine_st.registers[1]) { - let which = cell_as_atom!(self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[2] - ))); + let which = cell_as_atom!(self.deref_register(2)); if let Ok(md) = fs::metadata(file.as_str()) { if let Ok(time) = match which { @@ -1649,6 +2026,19 @@ impl Machine { self.machine_st.fail = true; } + #[inline(always)] + pub(crate) fn file_copy(&mut self) { + if let Some(file) = self.machine_st.value_to_str_like(self.machine_st.registers[1]) { + if let Some(copied) = self.machine_st.value_to_str_like(self.machine_st.registers[2]) { + if fs::copy(file.as_str(), copied.as_str()).is_ok() { + return; + } + } + } + + self.machine_st.fail = true; + } + #[inline(always)] pub(crate) fn delete_directory(&mut self) { if let Some(dir) = self.machine_st.value_to_str_like(self.machine_st.registers[1]) { @@ -1677,16 +2067,17 @@ impl Machine { let current_atom = self.machine_st.atom_tbl.build_with(¤t); + let a1 = self.deref_register(1); self.machine_st.unify_complete_string( current_atom, - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])), + a1 ); if self.machine_st.fail { return Ok(()); } - let target = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let target = self.deref_register(2); if let Some(next) = self.machine_st.value_to_str_like(target) { if env::set_current_dir(std::path::Path::new(next.as_str())).is_ok() { @@ -1717,9 +2108,10 @@ impl Machine { let canonical_atom = self.machine_st.atom_tbl.build_with(cs); + let a2 = self.deref_register(2); self.machine_st.unify_complete_string( canonical_atom, - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])), + a2 ); return Ok(()); @@ -1735,7 +2127,8 @@ impl Machine { #[inline(always)] pub(crate) fn atom_chars(&mut self) { - let a1 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let a1 = self.deref_register(1); + let a2 = self.deref_register(2); read_heap_cell!(a1, (HeapCellValueTag::Char) => { @@ -1753,7 +2146,7 @@ impl Machine { if arity == 0 { self.machine_st.unify_complete_string( name, - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])), + a2, ); } else { self.machine_st.fail = true; @@ -1763,14 +2156,14 @@ impl Machine { if arity == 0 { self.machine_st.unify_complete_string( name, - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])), + a2, ); } else { self.machine_st.fail = true; } } (HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar) => { - let a2 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let a2 = self.deref_register(2); if let Some(str_like) = self.machine_st.value_to_str_like(a2) { let atom_cell = match str_like { @@ -1800,7 +2193,7 @@ impl Machine { #[inline(always)] pub(crate) fn atom_codes(&mut self) -> CallResult { - let a1 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let a1 = self.deref_register(1); read_heap_cell!(a1, (HeapCellValueTag::Char, c) => { @@ -1861,7 +2254,7 @@ impl Machine { #[inline(always)] pub(crate) fn atom_length(&mut self) { - let a1 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let a1 = self.deref_register(1); let len: i64 = read_heap_cell!(a1, (HeapCellValueTag::Str, s) => { @@ -1891,16 +2284,17 @@ impl Machine { } ); + let a2 = self.deref_register(2); self.machine_st.unify_fixnum( Fixnum::build_with(len), - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])), + a2, ); } #[inline(always)] pub(crate) fn call_continuation(&mut self, last_call: bool) -> CallResult { let stub_gen = || functor_stub(atom!("call_continuation"), 1); - let a1 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let a1 = self.deref_register(1); match self.machine_st.try_from_list(a1, stub_gen) { Err(e) => Err(e), @@ -1925,7 +2319,7 @@ impl Machine { #[inline(always)] pub(crate) fn chars_to_number(&mut self) -> CallResult { let stub_gen = || functor_stub(atom!("number_chars"), 2); - let a1 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let a1 = self.deref_register(1); let atom_or_string = self.machine_st.value_to_str_like(a1).unwrap(); self.machine_st.parse_number_from_string( @@ -1938,7 +2332,7 @@ impl Machine { #[inline(always)] pub(crate) fn create_partial_string(&mut self) { let atom = cell_as_atom!( - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])) + self.deref_register(1) ); if atom == atom!("") { @@ -1961,7 +2355,7 @@ impl Machine { #[inline(always)] pub(crate) fn is_partial_string(&mut self) { - let value = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let value = self.deref_register(1); let h = self.machine_st.heap.len(); self.machine_st.heap.push(value); @@ -1978,7 +2372,8 @@ impl Machine { #[inline(always)] pub(crate) fn partial_string_tail(&mut self) { - let pstr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let pstr = self.deref_register(1); + let a2 = self.deref_register(2); read_heap_cell!(pstr, (HeapCellValueTag::PStrLoc, h) => { @@ -1987,20 +2382,20 @@ impl Machine { if HeapCellValueTag::CStr == self.machine_st.heap[h].get_tag() { self.machine_st.unify_atom( atom!("[]"), - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])) + a2 ); } else { unify_fn!( self.machine_st, heap_loc_as_cell!(h+1), - self.machine_st.registers[2] + a2 ); } } (HeapCellValueTag::CStr) => { self.machine_st.unify_atom( atom!("[]"), - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])) + a2 ); } (HeapCellValueTag::Lis, h) => { @@ -2043,18 +2438,22 @@ impl Machine { } } - if stream.at_end_of_stream() { - stream.set_past_end_of_stream(true); + let addr = self.deref_register(2); + if stream.at_end_of_stream() { self.machine_st.unify_fixnum( Fixnum::build_with(-1), - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])), + addr, ); - return Ok(()); + if self.machine_st.fail { + self.machine_st.fail = false; + } else { + return Ok(()); + } } - let addr = match self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])) { + let addr = match addr { addr if addr.is_var() => addr, addr => match Number::try_from(addr) { Ok(Number::Integer(n)) => { @@ -2084,6 +2483,7 @@ impl Machine { match stream.peek_byte().map_err(|e| e.kind()) { Ok(b) => { self.machine_st.unify_fixnum(Fixnum::build_with(b as i64), addr); + break; } Err(ErrorKind::PermissionDenied) => { self.machine_st.fail = true; @@ -2136,20 +2536,19 @@ impl Machine { } } + let a2 = self.deref_register(2); + if stream.at_end_of_stream() { let end_of_file = atom!("end_of_file"); - stream.set_past_end_of_stream(true); self.machine_st.unify_atom( end_of_file, - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])), + a2, ); return Ok(()); } - let a2 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); - let a2 = read_heap_cell!(a2, (HeapCellValueTag::Char) => { a2 @@ -2233,20 +2632,21 @@ impl Machine { } } - if stream.at_end_of_stream() { - let end_of_file = atom!("end_of_file"); - stream.set_past_end_of_stream(true); + let a2 = self.deref_register(2); - self.machine_st.unify_atom( - end_of_file, - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])), + if stream.at_end_of_stream() { + self.machine_st.unify_fixnum( + Fixnum::build_with(-1), + a2, ); - return Ok(()); + if self.machine_st.fail { + self.machine_st.fail = false; + } else { + return Ok(()); + } } - let a2 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); - let addr = read_heap_cell!(a2, (HeapCellValueTag::Var | HeapCellValueTag::StackVar | HeapCellValueTag::AttrVar) => { a2 @@ -2319,10 +2719,8 @@ impl Machine { #[inline(always)] pub(crate) fn number_to_chars(&mut self) { - let n = self.machine_st.registers[1]; - let chs = self.machine_st.registers[2]; - - let n = self.machine_st.store(self.machine_st.deref(n)); + let n = self.deref_register(1); + let chs = self.deref_register(2); let string = match Number::try_from(n) { Ok(Number::Float(OrderedFloat(n))) => { @@ -2334,7 +2732,7 @@ impl Machine { // n has already been confirmed as an integer, and // internally, Rational is assumed reduced, so its denominator // must be 1. - r.numer().to_string() + r.numerator().to_string() } _ => { unreachable!() @@ -2344,13 +2742,13 @@ impl Machine { let chars_atom = self.machine_st.atom_tbl.build_with(&string.trim()); self.machine_st.unify_complete_string( chars_atom, - self.machine_st.store(self.machine_st.deref(chs)), + chs, ); } #[inline(always)] pub(crate) fn number_to_codes(&mut self) { - let n = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let n = self.deref_register(1); let chs = self.machine_st.registers[2]; let string = match Number::try_from(n) { @@ -2363,7 +2761,7 @@ impl Machine { // n has already been confirmed as an integer, and // internally, Rational is assumed reduced, so its // denominator must be 1. - r.numer().to_string() + r.numerator().to_string() } _ => { unreachable!() @@ -2406,7 +2804,8 @@ impl Machine { #[inline(always)] pub(crate) fn char_code(&mut self) -> CallResult { let stub_gen = || functor_stub(atom!("char_code"), 2); - let a1 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let a1 = self.deref_register(1); + let a2 = self.deref_register(2); let c = read_heap_cell!(a1, (HeapCellValueTag::Atom, (name, _arity)) => { @@ -2423,8 +2822,6 @@ impl Machine { c } _ => { - let a2 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); - match Number::try_from(a2) { Ok(Number::Integer(n)) => { let c = match n.to_u32().and_then(std::char::from_u32) { @@ -2462,7 +2859,7 @@ impl Machine { self.machine_st.unify_fixnum( Fixnum::build_with(c as i64), - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])), + a2, ); Ok(()) @@ -2470,8 +2867,8 @@ impl Machine { #[inline(always)] pub(crate) fn char_type(&mut self) { - let a1 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); - let a2 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let a1 = self.deref_register(1); + let a2 = self.deref_register(2); let c = read_heap_cell!(a1, (HeapCellValueTag::Char, c) => { @@ -2492,73 +2889,106 @@ impl Machine { } ); - let chars = cell_as_atom!(a2); self.machine_st.fail = true; // This predicate fails by default. - macro_rules! macro_check { - ($id:ident, $name:expr) => { - if $id!(c) && chars == $name { - self.machine_st.fail = false; - return; - } - }; - } + read_heap_cell!(a2, + (HeapCellValueTag::Atom, (chars, _arity)) => { + macro_rules! macro_check { + ($id:ident, $name:expr) => { + if $id!(c) && chars == $name { + self.machine_st.fail = false; + return; + } + }; + } + + macro_rules! method_check { + ($id:ident, $name:expr) => { + if c.$id() && chars == $name { + self.machine_st.fail = false; + return; + } + }; + } + + macro_check!(alpha_char, atom!("alpha")); + method_check!(is_alphabetic, atom!("alphabetic")); + method_check!(is_alphanumeric, atom!("alphanumeric")); + macro_check!(alpha_numeric_char, atom!("alnum")); + method_check!(is_ascii, atom!("ascii")); + method_check!(is_ascii_punctuation, atom!("ascii_punctuation")); + method_check!(is_ascii_graphic, atom!("ascii_graphic")); + // macro_check!(backslash_char, atom!("backslash")); + // macro_check!(back_quote_char, atom!("back_quote")); + macro_check!(binary_digit_char, atom!("binary_digit")); + // macro_check!(capital_letter_char, atom!("upper")); + // macro_check!(comment_1_char, "comment_1"); + // macro_check!(comment_2_char, "comment_2"); + method_check!(is_control, atom!("control")); + // macro_check!(cut_char, atom!("cut")); + macro_check!(decimal_digit_char, atom!("decimal_digit")); + // macro_check!(decimal_point_char, atom!("decimal_point")); + // macro_check!(double_quote_char, atom!("double_quote")); + macro_check!(exponent_char, atom!("exponent")); + macro_check!(graphic_char, atom!("graphic")); + macro_check!(graphic_token_char, atom!("graphic_token")); + macro_check!(hexadecimal_digit_char, atom!("hexadecimal_digit")); + macro_check!(layout_char, atom!("layout")); + method_check!(is_lowercase, atom!("lower")); + macro_check!(meta_char, atom!("meta")); + // macro_check!(new_line_char, atom!("new_line")); + method_check!(is_numeric, atom!("numeric")); + macro_check!(octal_digit_char, atom!("octal_digit")); + macro_check!(octet_char, atom!("octet")); + macro_check!(prolog_char, atom!("prolog")); + // macro_check!(semicolon_char, atom!("semicolon")); + macro_check!(sign_char, atom!("sign")); + // macro_check!(single_quote_char, atom!("single_quote")); + // macro_check!(small_letter_char, atom!("lower")); + macro_check!(solo_char, atom!("solo")); + // macro_check!(space_char, atom!("space")); + macro_check!(symbolic_hexadecimal_char, atom!("symbolic_hexadecimal")); + macro_check!(symbolic_control_char, atom!("symbolic_control")); + method_check!(is_uppercase, atom!("upper")); + // macro_check!(variable_indicator_char, atom!("variable_indicator")); + method_check!(is_whitespace, atom!("whitespace")); + } + (HeapCellValueTag::Str, s) => { + let (name, arity) = cell_as_atom_cell!(self.machine_st.heap[s]) + .get_name_and_arity(); + + match (name, arity) { + (atom!("to_upper"), 1) => { + let reg = self.machine_st.deref(self.machine_st.heap[s+1]); + let atom = self.machine_st.atom_tbl.build_with(&c.to_uppercase().to_string()); + let upper_str = string_as_cstr_cell!(atom); + unify!(self.machine_st, reg, upper_str); + self.machine_st.fail = false; + } + (atom!("to_lower"), 1) => { + let reg = self.machine_st.deref(self.machine_st.heap[s+1]); + let atom = self.machine_st.atom_tbl.build_with(&c.to_lowercase().to_string()); + let lower_str = string_as_cstr_cell!(atom); + unify!(self.machine_st, reg, lower_str); + self.machine_st.fail = false; + } + _ => { + unreachable!() + } + }; + } + _ => { + unreachable!() + } + ); + - macro_rules! method_check { - ($id:ident, $name:expr) => { - if c.$id() && chars == $name { - self.machine_st.fail = false; - return; - } - }; - } - macro_check!(alpha_char, atom!("alpha")); - method_check!(is_alphabetic, atom!("alphabetic")); - method_check!(is_alphanumeric, atom!("alphanumeric")); - macro_check!(alpha_numeric_char, atom!("alnum")); - method_check!(is_ascii, atom!("ascii")); - method_check!(is_ascii_punctuation, atom!("ascii_ponctuaction")); - method_check!(is_ascii_graphic, atom!("ascii_graphic")); - // macro_check!(backslash_char, atom!("backslash")); - // macro_check!(back_quote_char, atom!("back_quote")); - macro_check!(binary_digit_char, atom!("binary_digit")); - // macro_check!(capital_letter_char, atom!("upper")); - // macro_check!(comment_1_char, "comment_1"); - // macro_check!(comment_2_char, "comment_2"); - method_check!(is_control, atom!("control")); - // macro_check!(cut_char, atom!("cut")); - macro_check!(decimal_digit_char, atom!("decimal_digit")); - // macro_check!(decimal_point_char, atom!("decimal_point")); - // macro_check!(double_quote_char, atom!("double_quote")); - macro_check!(exponent_char, atom!("exponent")); - macro_check!(graphic_char, atom!("graphic")); - macro_check!(graphic_token_char, atom!("graphic_token")); - macro_check!(hexadecimal_digit_char, atom!("hexadecimal_digit")); - macro_check!(layout_char, atom!("layout")); - method_check!(is_lowercase, atom!("lower")); - macro_check!(meta_char, atom!("meta")); - // macro_check!(new_line_char, atom!("new_line")); - method_check!(is_numeric, atom!("numeric")); - macro_check!(octal_digit_char, atom!("octal_digit")); - macro_check!(octet_char, atom!("octet")); - macro_check!(prolog_char, atom!("prolog")); - // macro_check!(semicolon_char, atom!("semicolon")); - macro_check!(sign_char, atom!("sign")); - // macro_check!(single_quote_char, atom!("single_quote")); - // macro_check!(small_letter_char, atom!("lower")); - macro_check!(solo_char, atom!("solo")); - // macro_check!(space_char, atom!("space")); - macro_check!(symbolic_hexadecimal_char, atom!("symbolic_hexadecimal")); - macro_check!(symbolic_control_char, atom!("symbolic_control")); - method_check!(is_uppercase, atom!("upper")); - // macro_check!(variable_indicator_char, atom!("variable_indicator")); - method_check!(is_whitespace, atom!("whitespace")); } #[inline(always)] pub(crate) fn check_cut_point(&mut self) { - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let addr = self.deref_register(1); let old_b = cell_as_fixnum!(addr).get_num() as usize; let prev_b = self.machine_st.stack.index_or_frame(self.machine_st.b).prelude.b; @@ -2576,7 +3006,7 @@ impl Machine { #[inline(always)] pub(crate) fn fetch_global_var(&mut self) { - let key = cell_as_atom!(self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1]))); + let key = cell_as_atom!(self.deref_register(1)); let addr = self.machine_st.registers[2]; match self.indices.global_variables.get_mut(&key) { @@ -2621,7 +3051,7 @@ impl Machine { )?; let stub_gen = || functor_stub(atom!("put_code"), 2); - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let addr = self.deref_register(2); if addr.is_var() { let err = self.machine_st.instantiation_error(); @@ -2671,7 +3101,7 @@ impl Machine { )?; let stub_gen = || functor_stub(atom!("put_char"), 2); - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let addr = self.deref_register(2); if addr.is_var() { let err = self.machine_st.instantiation_error(); @@ -2679,9 +3109,10 @@ impl Machine { } else { read_heap_cell!(addr, (HeapCellValueTag::Atom, (name, _arity)) => { - let c = name.as_char().unwrap(); - write!(&mut stream, "{}", c).unwrap(); - return Ok(()); + if let Some(c) = name.as_char() { + write!(&mut stream, "{}", c).unwrap(); + return Ok(()); + } } (HeapCellValueTag::Char, c) => { write!(&mut stream, "{}", c).unwrap(); @@ -2717,7 +3148,7 @@ impl Machine { } bytes.push(c as u8); - } + } } else { bytes = string.as_str().bytes().collect(); } @@ -2757,7 +3188,7 @@ impl Machine { )?; let stub_gen = || functor_stub(atom!("put_byte"), 2); - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let addr = self.deref_register(2); if addr.is_var() { let err = self.machine_st.instantiation_error(); @@ -2833,7 +3264,7 @@ impl Machine { } let stub_gen = || functor_stub(atom!("get_byte"), 2); - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let addr = self.deref_register(2); let addr = if addr.is_var() { addr @@ -2912,22 +3343,22 @@ impl Machine { } } + let addr = self.deref_register(2); + if stream.at_end_of_stream() { let end_of_file = atom!("end_of_file"); stream.set_past_end_of_stream(true); self.machine_st.unify_atom( end_of_file, - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])) + addr ); return Ok(()); } let stub_gen = || functor_stub(atom!("get_char"), 2); - let mut iter = self.machine_st.open_parsing_stream(stream, atom!("get_char"), 2)?; - - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let result = self.machine_st.open_parsing_stream(stream); let addr = if addr.is_var() { addr @@ -2946,10 +3377,25 @@ impl Machine { ) }; - loop { - let result = iter.read_char(); + let mut iter = match result { + Ok(iter) => iter, + Err(e) => { + if e.is_unexpected_eof() { + return self.machine_st.eof_action( + self.machine_st.registers[2], + stream, + atom!("get_char"), + 2, + ); + } else { + let err = self.machine_st.session_error(SessionError::from(e)); + return Err(self.machine_st.error_form(err, stub_gen())); + } + } + }; - match result { + loop { + match iter.read_char() { Some(Ok(c)) => { self.machine_st.unify_char(c, addr); break; @@ -2983,7 +3429,7 @@ impl Machine { 3, )?; - let num = match Number::try_from(self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2]))) { + let num = match Number::try_from(self.deref_register(2)) { Ok(Number::Fixnum(n)) => usize::try_from(n.get_num()).unwrap(), Ok(Number::Integer(n)) => match n.to_usize() { Some(u) => u, @@ -3009,7 +3455,13 @@ impl Machine { string.push(c as char); } } else { - let mut iter = self.machine_st.open_parsing_stream(stream, atom!("get_n_chars"), 2)?; + let mut iter = self.machine_st.open_parsing_stream(stream) + .map_err(|e| { + let err = self.machine_st.session_error(SessionError::from(e)); + let stub = functor_stub(atom!("get_n_chars"), 2); + + self.machine_st.error_form(err, stub) + })?; for _ in 0..num { let result = iter.read_char(); @@ -3025,7 +3477,7 @@ impl Machine { } }; - let output = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[3])); + let output = self.deref_register(3); let atom = self.machine_st.atom_tbl.build_with(&string); self.machine_st.unify_complete_string(atom, output); @@ -3057,20 +3509,20 @@ impl Machine { } } + let addr = self.deref_register(2); + if stream.at_end_of_stream() { - let end_of_file = atom!("end_of_file"); stream.set_past_end_of_stream(true); - self.machine_st.unify_atom( - end_of_file, - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])), + self.machine_st.unify_fixnum( + Fixnum::build_with(-1), + addr, ); return Ok(()); } let stub_gen = || functor_stub(atom!("get_code"), 2); - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); let addr = if addr.is_var() { addr @@ -3107,7 +3559,13 @@ impl Machine { } }; - let mut iter = self.machine_st.open_parsing_stream(stream.clone(), atom!("get_code"), 2)?; + let mut iter = self.machine_st.open_parsing_stream(stream) + .map_err(|e| { + let err = self.machine_st.session_error(SessionError::from(e)); + let stub = functor_stub(atom!("get_code"), 2); + + self.machine_st.error_form(err, stub) + })?; loop { let result = iter.read_char(); @@ -3156,9 +3614,7 @@ impl Machine { if let Some(first_stream) = first_stream { let stream = stream_as_cell!(first_stream); - let var = self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[1] - )).as_var().unwrap(); + let var = self.deref_register(1).as_var().unwrap(); self.machine_st.bind(var, stream); } else { @@ -3168,9 +3624,7 @@ impl Machine { #[inline(always)] pub(crate) fn next_stream(&mut self) { - let prev_stream = cell_as_stream!(self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[1] - ))); + let prev_stream = cell_as_stream!(self.deref_register(1)); let mut next_stream = None; let mut null_streams = BTreeSet::new(); @@ -3192,9 +3646,7 @@ impl Machine { self.indices.streams = self.indices.streams.sub(&null_streams); if let Some(next_stream) = next_stream { - let var = self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[2] - )).as_var().unwrap(); + let var = self.deref_register(2).as_var().unwrap(); let next_stream = stream_as_cell!(next_stream); self.machine_st.bind(var, next_stream); @@ -3253,9 +3705,10 @@ impl Machine { _ => unreachable!(), }; + let a1 = self.deref_register(1); self.machine_st.unify_char( c, - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])), + a1, ); Ok(()) @@ -3263,24 +3716,16 @@ impl Machine { #[inline(always)] pub(crate) fn head_is_dynamic(&mut self) { - let module_name = cell_as_atom!(self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[1]) - )); + let module_name = cell_as_atom!(self.deref_register(1)); - let (name, arity) = read_heap_cell!( - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])), - (HeapCellValueTag::Str, s) => { - cell_as_atom_cell!(self.machine_st.heap[s]).get_name_and_arity() + match self.machine_st.name_and_arity_from_heap(self.machine_st.registers[2]) { + Some((name, arity)) => { + self.machine_st.fail = !self.indices.is_dynamic_predicate(module_name, (name, arity)); } - (HeapCellValueTag::Atom, (name, _arity)) => { - (name, 0) + None => { + self.machine_st.fail = true; } - _ => { - unreachable!() - } - ); - - self.machine_st.fail = !self.indices.is_dynamic_predicate(module_name, (name, arity)); + } } #[inline(always)] @@ -3338,7 +3783,7 @@ impl Machine { #[inline(always)] pub(crate) fn copy_to_lifted_heap(&mut self) { let lh_offset = cell_as_fixnum!( - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])) + self.deref_register(1) ).get_num() as usize; let copy_target = self.machine_st.registers[2]; @@ -3354,92 +3799,16 @@ impl Machine { } #[inline(always)] - pub(crate) fn delete_attribute(&mut self) { - let ls0 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); - - if let HeapCellValueTag::Lis = ls0.get_tag() { - let l1 = ls0.get_value(); - let ls1 = self.machine_st.store(self.machine_st.deref(heap_loc_as_cell!(l1 + 1))); - - if let HeapCellValueTag::Lis = ls1.get_tag() { - let l2 = ls1.get_value(); - - let old_addr = self.machine_st.store(self.machine_st.deref(self.machine_st.heap[l1+1])); - let tail = self.machine_st.store(self.machine_st.deref(heap_loc_as_cell!(l2 + 1))); - - let tail = if tail.is_var() { - heap_loc_as_cell!(l1 + 1) - } else { - tail - }; - - let trail_ref = read_heap_cell!(old_addr, - (HeapCellValueTag::Var, h) => { - TrailRef::AttrVarHeapLink(h) - } - (HeapCellValueTag::Lis, l) => { - TrailRef::AttrVarListLink(l1 + 1, l) - } - _ => { - unreachable!() - } - ); - - self.machine_st.heap[l1 + 1] = tail; - self.machine_st.trail(trail_ref); - } - } - } - - #[inline(always)] - pub(crate) fn delete_head_attribute(&mut self) { - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); - - debug_assert_eq!(addr.get_tag(), HeapCellValueTag::AttrVar); - - let h = addr.get_value(); - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.heap[h + 1])); - - debug_assert_eq!(addr.get_tag(), HeapCellValueTag::Lis); - - let l = addr.get_value(); - let tail = self.machine_st.store(self.machine_st.deref(self.machine_st.heap[l + 1])); - - let tail = if tail.is_var() { - self.machine_st.heap[h] = heap_loc_as_cell!(h); - self.machine_st.trail(TrailRef::Ref(Ref::attr_var(h))); - - heap_loc_as_cell!(h + 1) - } else { - tail - }; - - self.machine_st.heap[h + 1] = tail; - self.machine_st.trail(TrailRef::AttrVarListLink(h + 1, l)); - } - - #[inline(always)] - pub(crate) fn dynamic_module_resolution( - &mut self, - narity: usize, - ) -> Result<(Atom, PredicateKey), MachineStub> { - let module_name = self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[1] - )); + pub(crate) fn lookup_db_ref(&mut self) { + let module_name = self.deref_register(1); + let name = cell_as_atom!(self.deref_register(2)); + let arity = cell_as_fixnum!(self.deref_register(3)).get_num() as usize; let module_name = read_heap_cell!(module_name, - (HeapCellValueTag::Atom, (name, _arity)) => { - debug_assert_eq!(_arity, 0); - name - } - (HeapCellValueTag::Str, s) => { - let (module_name, _arity) = cell_as_atom_cell!(self.machine_st.heap[s]) - .get_name_and_arity(); - - debug_assert_eq!(_arity, 0); + (HeapCellValueTag::Atom, (module_name, _arity)) => { module_name } - _ if module_name.is_var() => { + (HeapCellValueTag::AttrVar | HeapCellValueTag::Var) => { atom!("user") } _ => { @@ -3447,100 +3816,105 @@ impl Machine { } ); - let goal = self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[2] - )); - - let (name, arity, s) = self.machine_st.setup_call_n_init_goal_info(goal, narity)?; - - match arity.cmp(&2) { - Ordering::Less => { - for i in arity + 1..arity + narity + 1 { - self.machine_st.registers[i] = self.machine_st.registers[i + 2 - arity]; - } - } - Ordering::Greater => { - for i in (arity + 1..arity + narity + 1).rev() { - self.machine_st.registers[i] = self.machine_st.registers[i + 2 - arity]; - } - } - Ordering::Equal => {} - } - - let key = (name, arity + narity); - - for i in 1..arity + 1 { - self.machine_st.registers[i] = self.machine_st.heap[s + i]; - } - - Ok((module_name, key)) + self.machine_st.fail = self.indices + .get_predicate_code_index(name, arity, module_name) + .is_none(); } #[inline(always)] - pub(crate) fn enqueue_attributed_var(&mut self) { - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + pub(crate) fn get_db_refs(&mut self) { + let name_match: fn(Atom, Atom) -> bool; + let arity_match: fn(usize, usize) -> bool; - read_heap_cell!(addr, - (HeapCellValueTag::AttrVar, h) => { - self.machine_st.attr_var_init.attr_var_queue.push(h); + let module_name = read_heap_cell!(self.deref_register(1), + (HeapCellValueTag::Atom, (module_name, _arity)) => { + module_name + } + (HeapCellValueTag::AttrVar | HeapCellValueTag::Var | HeapCellValueTag::StackVar) => { + atom!("user") } _ => { + unreachable!() } ); - } - #[inline(always)] - pub(crate) fn get_next_db_ref(&mut self) { - let a1 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let atom = self.deref_register(2); - if let Some(name_var) = a1.as_var() { - let mut iter = self.indices.code_dir.iter(); + let pred_atom = if atom.is_var() { + name_match = |_, _| true; + atom!("") + } else { + name_match = |atom_1, atom_2| atom_1 == atom_2; + cell_as_atom!(atom) + }; - while let Some(((name, arity), _)) = iter.next() { - let arity_var = self.machine_st.deref(self.machine_st.registers[2]) - .as_var().unwrap(); + let arity = self.deref_register(3); - self.machine_st.bind(name_var, atom_as_cell!(name)); - self.machine_st.bind(arity_var, fixnum_as_cell!(Fixnum::build_with(*arity as i64))); + let pred_arity = if arity.is_var() { + arity_match = |_, _| true; + 0 + } else { + arity_match = |arity_1, arity_2| arity_1 == arity_2; + let arity = match Number::try_from(arity) { + Ok(Number::Fixnum(n)) => Some(n.get_num() as usize), + Ok(Number::Integer(n)) => n.to_usize(), + _ => None, + }; + + if let Some(arity) = arity { + arity + } else { + self.machine_st.fail = true; return; } + }; - self.machine_st.fail = true; - } else if a1.get_tag() == HeapCellValueTag::Atom { - let name = cell_as_atom!(a1); - let arity = cell_as_fixnum!(self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[2]) - )).get_num() as usize; + let h = self.machine_st.heap.len(); + let mut num_functors = 0; - match self.machine_st.get_next_db_ref(&self.indices, &DBRef::NamedPred(name, arity)) { - Some(DBRef::NamedPred(name, arity)) => { - let atom_var = self.machine_st.deref(self.machine_st.registers[3]) - .as_var().unwrap(); - - let arity_var = self.machine_st.deref(self.machine_st.registers[4]) - .as_var().unwrap(); - - self.machine_st.bind(atom_var, atom_as_cell!(name)); - self.machine_st.bind(arity_var, fixnum_as_cell!(Fixnum::build_with(arity as i64))); - } - Some(DBRef::Op(..)) | None => { + let code_dir = if module_name == atom!("user") { + &self.indices.code_dir + } else { + match self.indices.modules.get(&module_name).map(|module| &module.code_dir) { + Some(code_dir) => code_dir, + None => { self.machine_st.fail = true; + return; } } + }; + + for (name, arity) in code_dir.keys() { + if name_match(pred_atom, *name) && arity_match(pred_arity, *arity) { + self.machine_st.heap.extend( + functor!(atom!("/"), [cell(atom_as_cell!(name)), fixnum(*arity)]), + ); + + num_functors += 1; + } + } + + if num_functors > 0 { + let h = iter_to_heap_list( + &mut self.machine_st.heap, + (0 .. num_functors).map(|i| str_loc_as_cell!(h + 3 * i)), + ); + + unify!(self.machine_st, heap_loc_as_cell!(h), self.machine_st.registers[4]); } else { - self.machine_st.fail = true; + unify!(self.machine_st, empty_list_as_cell!(), self.machine_st.registers[4]); } } #[inline(always)] pub(crate) fn get_next_op_db_ref(&mut self) { - let prec = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let prec = self.deref_register(1); if let Some(prec_var) = prec.as_var() { - let spec = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); - let op = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[3])); - let orig_op = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[7])); + let spec = self.deref_register(2); + let op = self.deref_register(3); + let orig_op = self.deref_register(7); let spec_num = if spec.get_tag() == HeapCellValueTag::Atom { (match cell_as_atom!(spec) { @@ -3667,9 +4041,7 @@ impl Machine { match ossified_op_dir.iter().next() { Some(((op_atom, _), (op_prec, op_spec))) => { - let ossified_op_dir_var = self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[4] - )).as_var().unwrap(); + let ossified_op_dir_var = self.deref_register(4).as_var().unwrap(); let spec_atom = match *op_spec { FX => atom!("fx"), @@ -3699,9 +4071,9 @@ impl Machine { } } } else { - let spec = cell_as_atom!(self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2]))); - let op_atom = cell_as_atom!(self.machine_st.store(self.machine_st.deref(self.machine_st.registers[3]))); - let ossified_op_dir_cell = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[4])); + let spec = cell_as_atom!(self.deref_register(2)); + let op_atom = cell_as_atom!(self.deref_register(3)); + let ossified_op_dir_cell = self.deref_register(4); if ossified_op_dir_cell.is_var() { self.machine_st.fail = true; @@ -3765,9 +4137,24 @@ impl Machine { #[inline(always)] pub(crate) fn maybe(&mut self) { + fn generate_random_bits(num_bits: usize) -> u64 { + let mut rng = rand::thread_rng(); + let rand = rng.borrow_mut(); + let mut random_bits: u64 = 0; + + for _ in 0..num_bits { + random_bits <<= 1; + + if rand.gen_bool(0.5) { + random_bits |= 1; + } + } + + random_bits + } + let result = { - let mut rand = RANDOM_STATE.borrow_mut(); - rand.bits(1) == 0 + generate_random_bits(1) == 0 }; self.machine_st.fail = result; @@ -3784,7 +4171,7 @@ impl Machine { #[inline(always)] pub(crate) fn det_length_rundown(&mut self) -> CallResult { let stub_gen = || functor_stub(atom!("length"), 2); - let len = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let len = self.deref_register(2); let n = match Number::try_from(len) { Ok(Number::Fixnum(n)) => n.get_num() as usize, @@ -3807,7 +4194,7 @@ impl Machine { (0 .. n).map(|i| heap_loc_as_cell!(h + 2 * i + 1)), ); - let tail = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let tail = self.deref_register(1); self.machine_st.bind(tail.as_var().unwrap(), heap_loc_as_cell!(h)); Ok(()) @@ -3815,8 +4202,8 @@ impl Machine { #[inline(always)] pub(crate) fn http_open(&mut self) -> CallResult { - let address_sink = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); - let method = read_heap_cell!(self.machine_st.store(self.machine_st.deref(self.machine_st.registers[3])), + let address_sink = self.deref_register(1); + let method = read_heap_cell!(self.deref_register(3), (HeapCellValueTag::Atom, (name, arity)) => { debug_assert_eq!(arity, 0); match name { @@ -3833,8 +4220,8 @@ impl Machine { unreachable!() } ); - let address_status = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[4])); - let address_data = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[5])); + let address_status = self.deref_register(4); + let address_data = self.deref_register(5); let mut bytes: Vec = Vec::new(); if let Some(string) = self.machine_st.value_to_str_like(address_data) { bytes = string.as_str().bytes().collect(); @@ -3862,64 +4249,65 @@ impl Machine { }; if let Some(address_sink) = self.machine_st.value_to_str_like(address_sink) { let address_string = address_sink.as_str(); //to_string(); - let address: Uri = address_string.parse().unwrap(); + let address: Url = address_string.parse().unwrap(); - let runtime = tokio::runtime::Handle::current(); - let stream = runtime.block_on(async { - let https = HttpsConnector::new(); - let client = Client::builder() - .build::<_, hyper::Body>(https); + let client = reqwest::blocking::Client::builder() + .build() + .unwrap(); - // request - let mut req = Request::builder() - .method(method) - .uri(address) - .body(Body::from(bytes)) - .unwrap(); - // request headers - *req.headers_mut() = headers; - // do it! - let resp = client.request(req).await.unwrap(); - // status code - let status = resp.status().as_u16(); - self.machine_st.unify_fixnum(Fixnum::build_with(status as i64), address_status); - // headers - let headers: Vec = resp.headers().iter().map(|(header_name, header_value)| { - let h = self.machine_st.heap.len(); + // request + let mut req = reqwest::blocking::Request::new(method, address); - let header_term = functor!( - self.machine_st.atom_tbl.build_with(header_name.as_str()), - [cell(string_as_cstr_cell!(self.machine_st.atom_tbl.build_with(header_value.to_str().unwrap())))] - ); + *req.headers_mut() = headers; + if bytes.len() > 0 { + *req.body_mut() = Some(reqwest::blocking::Body::from(bytes)); + } - self.machine_st.heap.extend(header_term.into_iter()); - str_loc_as_cell!(h) - }).collect(); + // do it! + match client.execute(req) { + Ok(resp) => { + // status code + let status = resp.status().as_u16(); + self.machine_st.unify_fixnum(Fixnum::build_with(status as i64), address_status); + // headers + let headers: Vec = resp.headers().iter().map(|(header_name, header_value)| { + let h = self.machine_st.heap.len(); - let headers_list = iter_to_heap_list(&mut self.machine_st.heap, headers.into_iter()); - unify!(self.machine_st, heap_loc_as_cell!(headers_list), self.machine_st.registers[6]); - // body - let buf = hyper::body::aggregate(resp).await.unwrap(); - let reader = buf.reader(); + let header_term = functor!( + self.machine_st.atom_tbl.build_with(header_name.as_str()), + [cell(string_as_cstr_cell!(self.machine_st.atom_tbl.build_with(header_value.to_str().unwrap())))] + ); - let mut stream = Stream::from_http_stream( - self.machine_st.atom_tbl.build_with(&address_string), - Box::new(reader), - &mut self.machine_st.arena - ); - *stream.options_mut() = StreamOptions::default(); - if let Some(alias) = stream.options().get_alias() { - self.indices.stream_aliases.insert(alias, stream); - } + self.machine_st.heap.extend(header_term.into_iter()); + str_loc_as_cell!(h) + }).collect(); - self.indices.streams.insert(stream); + let headers_list = iter_to_heap_list(&mut self.machine_st.heap, headers.into_iter()); + unify!(self.machine_st, heap_loc_as_cell!(headers_list), self.machine_st.registers[6]); + // body + let reader = resp.bytes().unwrap().reader(); - stream_as_cell!(stream) - }); + let mut stream = Stream::from_http_stream( + self.machine_st.atom_tbl.build_with(&address_string), + Box::new(reader), + &mut self.machine_st.arena + ); + *stream.options_mut() = StreamOptions::default(); + if let Some(alias) = stream.options().get_alias() { + self.indices.stream_aliases.insert(alias, stream); + } - let stream_addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); - self.machine_st.bind(stream_addr.as_var().unwrap(), stream); + self.indices.streams.insert(stream); + let stream = stream_as_cell!(stream); + + let stream_addr = self.deref_register(2); + self.machine_st.bind(stream_addr.as_var().unwrap(), stream); + }, + Err(_) => { + self.machine_st.fail = true; + } + } } else { let err = self.machine_st.domain_error(DomainErrorType::SourceSink, address_sink); let stub = functor_stub(atom!("http_open"), 3); @@ -3932,7 +4320,7 @@ impl Machine { #[inline(always)] pub(crate) fn http_listen(&mut self) -> CallResult { - let address_sink = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let address_sink = self.deref_register(1); if let Some(address_str) = self.machine_st.value_to_str_like(address_sink) { let address_string = address_str.as_str(); let addr: SocketAddr = match address_string.to_socket_addrs().ok().and_then(|mut s| s.next()) { @@ -3943,32 +4331,38 @@ impl Machine { } }; - let (tx, rx) = channel(1); - let tx = Arc::new(Mutex::new(tx)); + let (tx, rx) = std::sync::mpsc::sync_channel(1024); let runtime = tokio::runtime::Handle::current(); let _guard = runtime.enter(); - let server = match Server::try_bind(&addr) { - Ok(server) => server, - Err(_) => { - return Err(self.machine_st.open_permission_error(address_sink, atom!("http_listen"), 2)); - } + let listener = match runtime.block_on(async { tokio::net::TcpListener::bind(addr).await }) { + Ok(listener) => listener, + Err(_) => { + return Err(self.machine_st.open_permission_error(address_sink, atom!("http_listen"), 2)); + } }; - runtime.spawn(async move { - let make_svc = make_service_fn(move |_conn| { - let tx = tx.clone(); - async move { Ok::<_, Infallible>(service_fn(move |req| http::serve_req(req, tx.clone()))) } - }); - let server = server.serve(make_svc); - if let Err(_) = server.await { - eprintln!("server error"); + runtime.spawn(async move { + loop { + let tx = tx.clone(); + let (stream, _) = listener.accept().await.unwrap(); + + tokio::task::spawn(async move { + if let Err(err) = http1::Builder::new() + .serve_connection(stream, HttpService { + tx + }) + .await + { + eprintln!("Error serving connection: {:?}", err); + } + }); } }); let http_listener = HttpListener { incoming: rx }; let http_listener = arena_alloc!(http_listener, &mut self.machine_st.arena); - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let addr = self.deref_register(2); self.machine_st.bind(addr.as_var().unwrap(), typed_arena_ptr_as_cell!(http_listener)); } Ok(()) @@ -3976,18 +4370,18 @@ impl Machine { #[inline(always)] pub(crate) fn http_accept(&mut self) -> CallResult { - let culprit = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); - let method = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); - let path = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[3])); - let query = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[5])); - let stream_addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[6])); - let handle_addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[7])); + let culprit = self.deref_register(1); + let method = self.deref_register(2); + let path = self.deref_register(3); + let query = self.deref_register(5); + let stream_addr = self.deref_register(6); + let handle_addr = self.deref_register(7); read_heap_cell!(culprit, (HeapCellValueTag::Cons, cons_ptr) => { match_untyped_arena_ptr!(cons_ptr, (ArenaHeaderTag::HttpListener, http_listener) => { - match http_listener.incoming.blocking_recv() { - Some(request) => { + match http_listener.incoming.recv() { + Ok(request) => { let method_atom = match *request.request.method() { Method::GET => atom!("get"), Method::POST => atom!("post"), @@ -4016,10 +4410,10 @@ impl Machine { let query_str = request.request.uri().query().unwrap_or(""); let query_atom = self.machine_st.atom_tbl.build_with(query_str); let query_cell = string_as_cstr_cell!(query_atom); - + let hyper_req = request.request; let runtime = tokio::runtime::Handle::current(); - let buf = runtime.block_on(async {hyper::body::aggregate(hyper_req).await.unwrap()}); + let buf = runtime.block_on(async {hyper_req.collect().await.unwrap().aggregate()}); let reader = buf.reader(); let mut stream = Stream::from_http_stream( @@ -4041,7 +4435,7 @@ impl Machine { self.machine_st.bind(stream_addr.as_var().unwrap(), stream); self.machine_st.bind(handle_addr.as_var().unwrap(), typed_arena_ptr_as_cell!(handle)); } - None => { + Err(_) => { self.machine_st.fail = true; } } @@ -4060,8 +4454,8 @@ impl Machine { #[inline(always)] pub(crate) fn http_answer(&mut self) -> CallResult { - let culprit = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); - let status_code = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let culprit = self.deref_register(1); + let status_code = self.deref_register(2); let status_code: u16 = match Number::try_from(status_code) { Ok(Number::Fixnum(n)) => n.get_num() as u16, Ok(Number::Integer(n)) => match n.to_u16() { @@ -4093,21 +4487,16 @@ impl Machine { }, Err(e) => return Err(e) }; - let stream_addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[4])); + let stream_addr = self.deref_register(4); read_heap_cell!(culprit, (HeapCellValueTag::Cons, cons_ptr) => { match_untyped_arena_ptr!(cons_ptr, (ArenaHeaderTag::HttpResponse, http_response) => { - let mut response = Response::builder() - .status(status_code); - *response.headers_mut().unwrap() = headers; - let (sender, body) = Body::channel(); - let response = response.body(body).unwrap(); - http_response.blocking_send(response).unwrap(); - let mut stream = Stream::from_http_sender( - sender, + http_response, + status_code, + headers, &mut self.machine_st.arena ); *stream.options_mut() = StreamOptions::default(); @@ -4129,6 +4518,170 @@ impl Machine { Ok(()) } + #[inline(always)] + pub(crate) fn load_foreign_lib(&mut self) -> CallResult { + let library_name = self.deref_register(1); + let args_reg = self.deref_register(2); + if let Some(library_name) = self.machine_st.value_to_str_like(library_name) { + let stub_gen = || functor_stub(atom!("use_foreign_module"), 2); + match self.machine_st.try_from_list(args_reg, stub_gen) { + Ok(addrs) => { + let mut functions = Vec::new(); + for heap_cell in addrs { + read_heap_cell!(heap_cell, + (HeapCellValueTag::Str, s) => { + let name = cell_as_atom_cell!(self.machine_st.heap[s]).get_name(); + let args: Vec = match self.machine_st.try_from_list(self.machine_st.heap[s + 1], stub_gen) { + Ok(addrs) => { + let mut args = Vec::new(); + for heap_cell in addrs { + args.push(cell_as_atom_cell!(heap_cell).get_name()); + } + args + } + Err(e) => return Err(e) + }; + let return_value = cell_as_atom_cell!(self.machine_st.heap[s + 2]); + functions.push(FunctionDefinition { + name: name.as_str().to_string(), + args, + return_value: return_value.get_name(), + }); + } + _ => { + unreachable!() + } + ) + } + if let Ok(_) = self.foreign_function_table.load_library(library_name.as_str(), &functions) { + return Ok(()); + } + } + Err(e) => return Err(e) + }; + } + self.machine_st.fail = true; + Ok(()) + } + + #[inline(always)] + pub(crate) fn foreign_call(&mut self) -> CallResult { + let function_name = self.deref_register(1); + let args_reg = self.deref_register(2); + let return_value = self.deref_register(3); + if let Some(function_name) = self.machine_st.value_to_str_like(function_name) { + let stub_gen = || functor_stub(atom!("foreign_call"), 3); + fn map_arg(mut machine_st: &mut MachineState, source: HeapCellValue) -> crate::ffi::Value { + match Number::try_from(source) { + Ok(Number::Fixnum(n)) => { + Value::Int(n.get_num()) + }, + Ok(Number::Float(n)) => { + Value::Float(n.into_inner()) + }, + _ => { + let stub_gen = || functor_stub(atom!("foreign_call"), 3); + if let Some(string) = machine_st.value_to_str_like(source) { + Value::CString(CString::new(string.as_str()).unwrap()) + } else { + match machine_st.try_from_list(source, stub_gen) { + Ok(args) => { + let mut iter = args.into_iter(); + if let Some(struct_name) = machine_st.value_to_str_like(iter.next().unwrap()) { + Value::Struct(struct_name.as_str().to_string(), iter.map(|x| map_arg(&mut machine_st, x)).collect()) + } else { + unreachable!() + } + } + _ => { + unreachable!() + } + } + } + } + } + } + + match self.machine_st.try_from_list(args_reg, stub_gen) { + Ok(args) => { + let args: Vec<_> = args.into_iter().map(|x| map_arg(&mut self.machine_st, x)).collect(); + match self.foreign_function_table.exec(function_name.as_str(), args) { + Ok(result) => { + match result { + Value::Int(n) => self.machine_st.unify_fixnum(Fixnum::build_with(n), return_value), + Value::Float(n) => { + let n = float_alloc!(n, self.machine_st.arena); + self.machine_st.unify_f64(n, return_value) + }, + Value::Struct(name, args) => { + let struct_value = self.build_struct(&name, args); + unify!(self.machine_st, return_value, struct_value); + } + Value::CString(cstr) => { + let cstr = self.machine_st.atom_tbl.build_with(cstr.to_str().unwrap()); + self.machine_st.unify_complete_string(cstr, return_value); + } + } + return Ok(()); + }, + Err(e) => { + let stub = functor_stub(atom!("current_input"), 1); + let err = self.machine_st.ffi_error(e); + + return Err(self.machine_st.error_form(err, stub)); + } + } + } + Err(e) => return Err(e) + } + } + self.machine_st.fail = true; + Ok(()) + } + + fn build_struct(&mut self, name: &str, mut args: Vec) -> HeapCellValue { + args.insert(0, Value::CString(CString::new(name).unwrap())); + let cells: Vec<_> = args.into_iter() + .map(|val| { + match val { + Value::Int(n) => fixnum_as_cell!(Fixnum::build_with(n)), + Value::Float(n) => HeapCellValue::from(float_alloc!(n, self.machine_st.arena)), + Value::CString(cstr) => atom_as_cell!(self.machine_st.atom_tbl.build_with(&cstr.into_string().unwrap())), + Value::Struct(name, struct_args) => self.build_struct(&name, struct_args), + } + }).collect(); + + heap_loc_as_cell!( + iter_to_heap_list( + &mut self.machine_st.heap, + cells.into_iter() + ) + ) + } + + #[inline(always)] + pub(crate) fn define_foreign_struct(&mut self) -> CallResult { + let struct_name = self.deref_register(1); + let fields_reg = self.deref_register(2); + if let Some(struct_name) = self.machine_st.value_to_str_like(struct_name) { + let stub_gen = || functor_stub(atom!("define_foreign_struct"), 2); + let fields: Vec = match self.machine_st.try_from_list(fields_reg, stub_gen) { + Ok(addrs) => { + let mut args = Vec::new(); + for heap_cell in addrs { + args.push(cell_as_atom_cell!(heap_cell).get_name()); + } + args + } + Err(e) => return Err(e) + }; + self.foreign_function_table.define_struct(struct_name.as_str(), fields); + return Ok(()) + } + self.machine_st.fail = true; + Ok(()) + } + #[inline(always)] pub(crate) fn current_time(&mut self) { let timestamp = self.systemtime_to_timestamp(SystemTime::now()); @@ -4143,7 +4696,7 @@ impl Machine { let stream_type = self.machine_st.registers[7]; let options = self.machine_st.to_stream_options(alias, eof_action, reposition, stream_type); - let src_sink = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let src_sink = self.deref_register(1); if let Some(file_spec) = self.machine_st.value_to_str_like(src_sink) { let file_spec = file_spec.as_atom(&mut self.machine_st.atom_tbl); @@ -4161,7 +4714,7 @@ impl Machine { self.indices.stream_aliases.insert(alias, stream); } - let stream_var = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[3])); + let stream_var = self.deref_register(3); self.machine_st.bind(stream_var.as_var().unwrap(), stream_as_cell!(stream)); } else { let err = self.machine_st.domain_error(DomainErrorType::SourceSink, src_sink); @@ -4175,11 +4728,8 @@ impl Machine { #[inline(always)] pub(crate) fn op_declaration(&mut self) -> CallResult { - let priority = self.machine_st.registers[1]; - let specifier = self.machine_st.registers[2]; - let op = self.machine_st.registers[3]; - - let priority = self.machine_st.store(self.machine_st.deref(priority)); + let priority = self.deref_register(1); + let specifier = cell_as_atom_cell!(self.deref_register(2)).get_name(); let priority = match Number::try_from(priority) { Ok(Number::Integer(n)) => n.to_u16().unwrap(), @@ -4189,10 +4739,7 @@ impl Machine { } }; - let specifier = cell_as_atom_cell!(self.machine_st.store(self.machine_st.deref(specifier))) - .get_name(); - - let op = read_heap_cell!(self.machine_st.store(self.machine_st.deref(op)), + let op = read_heap_cell!(self.deref_register(3), (HeapCellValueTag::Char, c) => { self.machine_st.atom_tbl.build_with(&c.to_string()) } @@ -4266,20 +4813,13 @@ impl Machine { #[inline(always)] pub(crate) fn get_attributed_variable_list(&mut self) { - let attr_var = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let attr_var = self.deref_register(1); let attr_var_list = read_heap_cell!(attr_var, (HeapCellValueTag::AttrVar, h) => { - h + 1 + h+1 } - (HeapCellValueTag::Var | HeapCellValueTag::StackVar) => { - // create an AttrVar in the heap. - let h = self.machine_st.heap.len(); - - self.machine_st.heap.push(attr_var_as_cell!(h)); - self.machine_st.heap.push(heap_loc_as_cell!(h+1)); - - self.machine_st.bind(Ref::attr_var(h), attr_var); - h + 1 + (HeapCellValueTag::Var, h) => { + h } _ => { self.machine_st.fail = true; @@ -4287,22 +4827,59 @@ impl Machine { } ); - let list_addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let list_addr = self.deref_register(2); self.machine_st.bind(Ref::heap_cell(attr_var_list), list_addr); } + #[inline(always)] + pub(crate) fn get_from_attributed_variable_list(&mut self) { + let attr_var = self.deref_register(1); + let attr = self.deref_register(3); + let attr_var_list = read_heap_cell!(attr_var, + (HeapCellValueTag::AttrVar, h) => { + self.machine_st.heap[h+1] + } + _ => { + self.machine_st.fail = true; + return; + } + ); + + let module = self.deref_register(2); + + match self.match_attribute(attr_var_list, module, attr) { + Some(AttrListMatch { match_site: MatchSite::Match(match_site), .. }) => { + let list_head = self.machine_st.heap[match_site]; + + if list_head.get_value() == match_site { + // at the end of the list, no match found in this case. + self.machine_st.fail = true; + } else { + let (_, qualified_goal) = self.machine_st.strip_module( + list_head, + empty_list_as_cell!(), + ); + + unify!(self.machine_st, qualified_goal, attr); + } + } + _ => { + self.machine_st.fail = true; + } + } + } + #[inline(always)] pub(crate) fn get_attr_var_queue_delimiter(&mut self) { - let addr = self.machine_st.registers[1]; + let addr = self.deref_register(1); let value = Fixnum::build_with(self.machine_st.attr_var_init.attr_var_queue.len() as i64); - self.machine_st.unify_fixnum(value, self.machine_st.store(self.machine_st.deref(addr))); + self.machine_st.unify_fixnum(value, addr); } #[inline(always)] pub(crate) fn get_attr_var_queue_beyond(&mut self) { - let addr = self.machine_st.registers[1]; - let addr = self.machine_st.store(self.machine_st.deref(addr)); + let addr = self.deref_register(1); let b = match Number::try_from(addr) { Ok(Number::Integer(n)) => n.to_usize(), @@ -4325,22 +4902,231 @@ impl Machine { } } + #[inline(always)] + pub(crate) fn delete_from_attributed_variable_list(&mut self) { + let attr_var = self.deref_register(1); + let attr = self.deref_register(3); + let attr_var_list = read_heap_cell!(attr_var, + (HeapCellValueTag::AttrVar, h) => { + h + 1 + } + _ => { + return; + } + ); + + let module = self.deref_register(2); + + match self.match_attribute(self.machine_st.heap[attr_var_list], module, attr) { + Some(AttrListMatch { prev_tail, match_site: MatchSite::Match(match_site) }) => { + let prev_tail = if let Some(prev_tail) = prev_tail { + // not at the head. + prev_tail + } else { + if self.machine_st.heap[match_site + 1].is_var() { + let h = attr_var.get_value(); + + self.machine_st.heap[h] = heap_loc_as_cell!(h); + self.machine_st.trail(TrailRef::Ref(Ref::attr_var(h))); + } + + // at the head. + attr_var_list + }; + + if self.machine_st.heap[match_site + 1].get_tag() == HeapCellValueTag::Lis { + let prev_tail_value = self.machine_st.heap[match_site + 1].get_value(); + self.machine_st.heap[prev_tail].set_value(prev_tail_value); + } else { + self.machine_st.heap[prev_tail] = heap_loc_as_cell!(prev_tail); + } + + self.machine_st.trail(TrailRef::AttrVarListLink(prev_tail, match_site)); + } + _ => { + } + } + } + + #[inline(always)] + pub(crate) fn put_to_attributed_variable_list(&mut self) { + let attr_var = self.deref_register(1); + let attr = self.deref_register(3); + let attr_var_list = match self.machine_st.get_attr_var_list(attr_var) { + Some(h) => h, + None => { + self.machine_st.fail = true; + return; + } + }; + + let module = self.deref_register(2); + + /* + * How to handle attribute trailing using just AttrVarListLink (which + * should be re-named to something more general) in unwind_trail: + * + * Given AttrVarListLink(h, l): + * + * 1. Check cell at offset l. + * 2. If h == l, set heap[h] = heap_loc_as_cell!(h). + * 3. If cell is a Var, set heap[h] = list_loc_as_cell!(l). + * 4. Otherwise, cell points to an element of the list which is therefore + * an atom or str. Set heap[h] accordingly. + * + * For this to work, all elements of attributed variable lists must be + * heap cell locs pointing to later elements in the heap, either atoms (0-arity) + * or str cells (> 0-arity). + */ + + let h = self.machine_st.heap.len(); + + self.machine_st.heap.push(str_loc_as_cell!(h+1)); + self.machine_st.heap.extend(functor!(atom!(":"), [cell(module), cell(attr)])); + + match self.match_attribute(self.machine_st.heap[attr_var_list], module, attr) { + Some(AttrListMatch { match_site, .. }) => { + let (match_site, l) = match match_site { + MatchSite::NoMatchVarTail(match_site) => { + let l = self.machine_st.heap[match_site].get_value(); + + // at the end of the (non-empty) list here. + self.machine_st.heap[match_site] = list_loc_as_cell!(h+4); + self.machine_st.heap.push(heap_loc_as_cell!(h)); + self.machine_st.heap.push(heap_loc_as_cell!(h+5)); + + (match_site, l) + } + MatchSite::Match(match_site) => { + let l = self.machine_st.heap[match_site].get_value(); + self.machine_st.heap[match_site].set_value(h); + + (match_site, l) + } + }; + + self.machine_st.trail(TrailRef::AttrVarListLink(match_site, l)); + } + None => { + // the list is empty. + self.machine_st.heap[attr_var_list] = list_loc_as_cell!(h+4); + self.machine_st.heap.push(heap_loc_as_cell!(h)); + self.machine_st.heap.push(heap_loc_as_cell!(h+5)); + + self.machine_st.attr_var_init.attr_var_queue.push(attr_var_list - 1); + self.machine_st.trail(TrailRef::AttrVarListLink(attr_var_list, attr_var_list)); + } + } + } + + fn match_attribute( + &self, + mut attrs_list: HeapCellValue, + module: HeapCellValue, + attr: HeapCellValue, + ) -> Option { + let (name, arity) = match self.machine_st.name_and_arity_from_heap(attr) { + Some(key) => key, + None => { + return None; + } + }; + + let mut prev_tail = None; + + while let HeapCellValueTag::Lis = attrs_list.get_tag() { + let mut list_head = self.machine_st.heap[attrs_list.get_value()]; + + loop { + read_heap_cell!(list_head, + (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => { + debug_assert!(list_head != self.machine_st.heap[h]); + list_head = self.machine_st.heap[h]; + } + (HeapCellValueTag::Str | HeapCellValueTag::Atom) => { + let (module_loc, qualified_goal) = self.machine_st.strip_module( + list_head, + empty_list_as_cell!(), + ); + + let (t_name, t_arity) = self.machine_st + .name_and_arity_from_heap(qualified_goal) + .unwrap(); + + if module == module_loc && name == t_name && arity == t_arity { + return Some(AttrListMatch { + match_site: MatchSite::Match(attrs_list.get_value()), + prev_tail, + }); + } + + break; + } + _ => { + break; + } + ); + } + + let tail_loc = attrs_list.get_value() + 1; + prev_tail = Some(tail_loc); + + // do the work of self.store(self.deref(...)) but inline it + // for speed and simplify it. + let mut list_tail = self.machine_st.heap[tail_loc]; + + loop { + read_heap_cell!(list_tail, + (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => { + if list_tail != self.machine_st.heap[h] { + list_tail = self.machine_st.heap[h]; + } else { + return Some(AttrListMatch { + match_site: MatchSite::NoMatchVarTail(h), + prev_tail, + }); + } + } + (HeapCellValueTag::Lis) => { + attrs_list = list_tail; + break; + } + _ => { + unreachable!() + } + ); + } + } + + None + } + #[inline(always)] pub(crate) fn get_continuation_chunk(&mut self) { - let e = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let e = self.deref_register(1); let e = cell_as_fixnum!(e).get_num() as usize; - let p_functor = self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[2] - )); + let p_functor = self.deref_register(2); - let p = to_local_code_ptr(&self.machine_st.heap, p_functor).unwrap(); - - let num_cells = *self.code[p].perm_vars_mut().unwrap(); + let num_cells = self.machine_st.stack.index_and_frame(e).prelude.num_cells; let mut addrs = vec![]; for idx in 1..num_cells + 1 { - addrs.push(self.machine_st.stack[stack_loc!(AndFrame, e, idx)]); + let addr = self.machine_st.stack[stack_loc!(AndFrame, e, idx)]; + let addr = self.machine_st.store(self.machine_st.deref(addr)); + + // avoid pushing stack variables to the heap where they + // must not go. + if addr.is_stack_var() { + let h = self.machine_st.heap.len(); + + self.machine_st.heap.push(heap_loc_as_cell!(h)); + self.machine_st.bind(Ref::heap_cell(h), addr); + + addrs.push(heap_loc_as_cell!(h)); + } else { + addrs.push(addr); + } } let chunk = str_loc_as_cell!(self.machine_st.heap.len()); @@ -4411,7 +5197,7 @@ impl Machine { #[inline(always)] pub(crate) fn get_double_quotes(&mut self) { - let a1 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let a1 = self.deref_register(1); self.machine_st.unify_atom( match self.machine_st.flags.double_quotes { @@ -4423,22 +5209,36 @@ impl Machine { ); } + #[inline(always)] + pub(crate) fn get_unknown(&mut self) { + let a1 = self.deref_register(1); + + self.machine_st.unify_atom( + match self.machine_st.flags.unknown { + Unknown::Error => atom!("error"), + Unknown::Fail => atom!("fail"), + Unknown::Warn => atom!("warning"), + }, + a1, + ); + } + #[inline(always)] pub(crate) fn get_scc_cleaner(&mut self) { let dest = self.machine_st.registers[1]; - if let Some((addr, b_cutoff, prev_b)) = self.machine_st.cont_pts.pop() { + if let Some((addr, b_cutoff, prev_block)) = self.machine_st.cont_pts.pop() { let b = self.machine_st.stack.index_or_frame(self.machine_st.b).prelude.b; if b <= b_cutoff { - self.machine_st.block = prev_b; + self.machine_st.scc_block = prev_block; if let Some(r) = dest.as_var() { self.machine_st.bind(r, addr); return; } } else { - self.machine_st.cont_pts.push((addr, b_cutoff, prev_b)); + self.machine_st.cont_pts.push((addr, b_cutoff, prev_block)); } } @@ -4446,43 +5246,43 @@ impl Machine { } #[inline(always)] - pub(crate) fn halt(&mut self) { - let code = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + pub(crate) fn halt(&mut self) -> std::process::ExitCode { + let code = self.deref_register(1); let code = match Number::try_from(code) { - Ok(Number::Fixnum(n)) => i32::try_from(n.get_num()).unwrap(), - Ok(Number::Integer(n)) => n.to_i32().unwrap(), + Ok(Number::Fixnum(n)) => u8::try_from(n.get_num()).unwrap(), + Ok(Number::Integer(n)) => n.to_u8().unwrap(), Ok(Number::Rational(r)) => { // n has already been confirmed as an integer, and // internally, Rational is assumed reduced, so its // denominator must be 1. - r.numer().to_i32().unwrap() + r.numerator().to_u8().unwrap() } _ => { unreachable!() } }; - std::process::exit(code); + std::process::ExitCode::from(code) } #[inline(always)] pub(crate) fn install_scc_cleaner(&mut self) { let addr = self.machine_st.registers[1]; let b = self.machine_st.b; - let prev_block = self.machine_st.block; + let prev_block = self.machine_st.scc_block; self.machine_st.run_cleaners_fn = Machine::run_cleaners; - self.machine_st.install_new_block(self.machine_st.registers[2]); + self.machine_st.scc_block = b; self.machine_st.cont_pts.push((addr, b, prev_block)); } #[inline(always)] pub(crate) fn install_inference_counter(&mut self) -> CallResult { // A1 = B, A2 = L - let a1 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); - let a2 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let a1 = self.deref_register(1); + let a2 = self.deref_register(2); let n = match Number::try_from(a2) { Ok(Number::Fixnum(bp)) => bp.get_num() as usize, @@ -4499,37 +5299,35 @@ impl Machine { }; let bp = cell_as_fixnum!(a1).get_num() as usize; + let a3 = self.deref_register(3); let count = self.machine_st.cwil.add_limit(n, bp); - let count = arena_alloc!(count.clone(), &mut self.machine_st.arena); + + if let Some(count) = count.to_i64() { + self.machine_st.unify_fixnum(Fixnum::build_with(count), a3); + } else { + let count = arena_alloc!(count.clone(), &mut self.machine_st.arena); + self.machine_st.unify_big_int(count, a3); + } self.machine_st.increment_call_count_fn = MachineState::increment_call_count; - let a3 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[3])); - self.machine_st.unify_big_int(count, a3); - Ok(()) } #[inline(always)] pub(crate) fn module_exists(&mut self) { - let module = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let module = self.deref_register(1); let module_name = cell_as_atom!(module); self.machine_st.fail = !self.indices.modules.contains_key(&module_name); } - pub(crate) fn predicate_defined(&self) -> bool { - let module_name = cell_as_atom!(self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[1] - ))); + pub(crate) fn predicate_defined(&mut self) -> bool { + let module_name = cell_as_atom!(self.deref_register(1)); + let name = cell_as_atom!(self.deref_register(2)); + let a3 = self.deref_register(3); - let name = cell_as_atom!(self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[2] - ))); - - let arity = match Number::try_from(self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[3] - ))) { + let arity = match Number::try_from(a3) { Ok(Number::Fixnum(n)) => n.get_num() as usize, Ok(Number::Integer(n)) => { if let Some(n) = n.to_usize() { @@ -4553,11 +5351,9 @@ impl Machine { #[inline(always)] pub(crate) fn no_such_predicate(&mut self) -> CallResult { - let module_name = cell_as_atom!(self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[1] - ))); + let module_name = cell_as_atom!(self.deref_register(1)); - let head = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let head = self.deref_register(2); self.machine_st.fail = read_heap_cell!(head, (HeapCellValueTag::Str, s) => { @@ -4613,8 +5409,8 @@ impl Machine { } #[inline(always)] pub(crate) fn redo_attr_var_binding(&mut self) { - let var = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); - let value = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let var = self.deref_register(1); + let value = self.deref_register(2); debug_assert_eq!(HeapCellValueTag::AttrVar, var.get_tag()); self.machine_st.heap[var.get_value()] = value; @@ -4636,16 +5432,14 @@ impl Machine { } #[inline(always)] - pub(crate) fn reset_attr_var_state(&mut self) { + pub(crate) fn reset_attr_var_state(&mut self, queue_len: usize) { self.restore_instr_at_verify_attr_interrupt(); - self.machine_st.attr_var_init.reset(); + self.machine_st.attr_var_init.reset(queue_len); } #[inline(always)] pub(crate) fn remove_call_policy_check(&mut self) { - let bp = cell_as_fixnum!(self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[1] - ))).get_num() as usize; + let bp = cell_as_fixnum!(self.deref_register(1)).get_num() as usize; if bp == self.machine_st.b && self.machine_st.cwil.is_empty() { self.machine_st.cwil.reset(); @@ -4655,21 +5449,27 @@ impl Machine { #[inline(always)] pub(crate) fn remove_inference_counter(&mut self) { - let a1 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let a1 = self.deref_register(1); + let a2 = self.deref_register(2); + let bp = cell_as_fixnum!(a1).get_num() as usize; let count = self.machine_st.cwil.remove_limit(bp).clone(); - let count = arena_alloc!(count.clone(), &mut self.machine_st.arena); - let a2 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); - - self.machine_st.unify_big_int(count, a2); + if let Some(count) = count.to_i64() { + self.machine_st.unify_fixnum(Fixnum::build_with(count), a2); + } else { + let count = arena_alloc!(count.clone(), &mut self.machine_st.arena); + self.machine_st.unify_big_int(count, a2); + } } #[inline(always)] pub(crate) fn return_from_verify_attr(&mut self) { + self.restore_instr_at_verify_attr_interrupt(); + let e = self.machine_st.e; - let frame_len = self.machine_st.stack.index_and_frame(e).prelude.univ_prelude.num_cells; + let frame_len = self.machine_st.stack.index_and_frame(e).prelude.num_cells; for i in 1..frame_len - 2 { self.machine_st.registers[i] = self.machine_st.stack[stack_loc!(AndFrame, e, i)]; @@ -4710,9 +5510,7 @@ impl Machine { #[inline(always)] pub(crate) fn set_input(&mut self) -> CallResult { - let addr = self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[1] - )); + let addr = self.deref_register(1); let stream = self.machine_st.get_stream_or_alias( addr, @@ -4740,7 +5538,7 @@ impl Machine { #[inline(always)] pub(crate) fn set_output(&mut self) -> CallResult { - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let addr = self.deref_register(1); let stream = self.machine_st.get_stream_or_alias( addr, &self.indices.stream_aliases, @@ -4767,7 +5565,7 @@ impl Machine { #[inline(always)] pub(crate) fn set_double_quotes(&mut self) { - let atom = cell_as_atom!(self.machine_st.registers[1]); + let atom = cell_as_atom!(self.deref_register(1)); self.machine_st.flags.double_quotes = match atom { atom!("atom") => DoubleQuotes::Atom, @@ -4780,10 +5578,25 @@ impl Machine { }; } + #[inline(always)] + pub(crate) fn set_unknown(&mut self) { + let atom = cell_as_atom!(self.deref_register(1)); + + self.machine_st.flags.unknown = match atom { + atom!("error") => Unknown::Error, + atom!("fail") => Unknown::Fail, + atom!("warning") => Unknown::Warn, + _ => { + self.machine_st.fail = true; + return; + } + }; + } + #[inline(always)] pub(crate) fn inference_level(&mut self) { - let a1 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); - let a2 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let a1 = self.deref_register(1); + let a2 = self.deref_register(2); let bp = cell_as_fixnum!(a2).get_num() as usize; let prev_b = self.machine_st.stack.index_or_frame(self.machine_st.b).prelude.b; @@ -4797,7 +5610,7 @@ impl Machine { #[inline(always)] pub(crate) fn clean_up_block(&mut self) { - let nb = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let nb = self.deref_register(1); let nb = cell_as_fixnum!(nb).get_num() as usize; let b = self.machine_st.b; @@ -4809,7 +5622,7 @@ impl Machine { #[inline(always)] pub(crate) fn get_ball(&mut self) { - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let addr = self.deref_register(1); let h = self.machine_st.heap.len(); if self.machine_st.ball.stub.len() > 0 { @@ -4851,81 +5664,35 @@ impl Machine { #[inline(always)] pub(crate) fn get_current_block(&mut self) { - let n = Fixnum::build_with(i64::try_from(self.machine_st.block).unwrap()); - self.machine_st.unify_fixnum(n, self.machine_st.registers[1]); + let addr = self.machine_st.registers[1]; + let block = Fixnum::build_with(self.machine_st.block as i64); + + self.machine_st.unify_fixnum(block, addr); + } + + #[inline(always)] + pub(crate) fn get_current_scc_block(&mut self) { + let addr = self.machine_st.registers[1]; + let block = Fixnum::build_with(self.machine_st.scc_block as i64); + + self.machine_st.unify_fixnum(block, addr); } #[inline(always)] pub(crate) fn get_b_value(&mut self) { - let n = Fixnum::build_with(i64::try_from(self.machine_st.b).unwrap()); + let n = Fixnum::as_cutpoint(i64::try_from(self.machine_st.b).unwrap()); self.machine_st.unify_fixnum(n, self.machine_st.registers[1]); } #[inline(always)] pub(crate) fn get_cut_point(&mut self) { - let n = Fixnum::build_with(i64::try_from(self.machine_st.b0).unwrap()); + let n = Fixnum::as_cutpoint(i64::try_from(self.machine_st.b0).unwrap()); self.machine_st.unify_fixnum(n, self.machine_st.registers[1]); } - #[inline(always)] - pub(crate) fn get_staggered_cut_point(&mut self) { - use std::sync::Once; - - let b = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); - - static mut SEMICOLON_SECOND_BRANCH_LOC: usize = 0; - static LOC_INIT: Once = Once::new(); - - let semicolon_second_clause_p = unsafe { - LOC_INIT.call_once(|| { - if let Some(builtins) = self.indices.modules.get(&atom!("builtins")) { - match builtins.code_dir.get(&(atom!("staggered_sc"), 2)).map(|cell| cell.get()) { - Some(ip) if ip.tag() == IndexPtrTag::Index => { - let p = ip.p() as usize; - - match &self.code[p] { - &Instruction::TryMeElse(o) => { - SEMICOLON_SECOND_BRANCH_LOC = p + o; - } - _ => { - unreachable!(); - } - } - } - _ => { - unreachable!(); - } - } - } else { - unreachable!(); - } - }); - - SEMICOLON_SECOND_BRANCH_LOC - }; - - let staggered_b0 = if self.machine_st.b > 0 { - let or_frame = self.machine_st.stack.index_or_frame(self.machine_st.b); - - if or_frame.prelude.bp == semicolon_second_clause_p { - or_frame.prelude.b0 - } else { - self.machine_st.b0 - } - } else { - self.machine_st.b0 - }; - - let staggered_b0 = integer_as_cell!( - Number::arena_from(staggered_b0, &mut self.machine_st.arena) - ); - - self.machine_st.bind(b.as_var().unwrap(), staggered_b0); - } - #[inline(always)] pub(crate) fn next_ep(&mut self) { - let first_arg = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let first_arg = self.deref_register(1); let next_ep_atom = |machine_st: &mut MachineState, name, arity| { debug_assert_eq!(name, atom!("first")); @@ -4994,7 +5761,7 @@ impl Machine { #[inline(always)] pub(crate) fn points_to_continuation_reset_marker(&mut self) { - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let addr = self.deref_register(1); let p = match to_local_code_ptr(&self.machine_st.heap, addr) { Some(p) => p + 1, @@ -5011,7 +5778,7 @@ impl Machine { #[inline(always)] pub(crate) fn quoted_token(&mut self) { - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let addr = self.deref_register(1); read_heap_cell!(addr, (HeapCellValueTag::Fixnum, n) => { @@ -5072,42 +5839,114 @@ impl Machine { 3, )?; - self.machine_st.read_term(stream, &mut self.indices) + if let Stream::Readline(..) = stream { + self.machine_st.read_term(stream, &mut self.indices, MachineState::read_term_from_user_input_eof_handler) + } else { + self.machine_st.read_term(stream, &mut self.indices, MachineState::read_term_eof_handler) + } + } + + #[inline(always)] + fn read_term_and_write_to_heap( + &mut self, + atom_or_string: AtomOrString, + ) -> Result, MachineStub> { + let string = match atom_or_string { + AtomOrString::Atom(atom) if atom == atom!("[]") => "".to_owned(), + _ => atom_or_string.to_string(), + }; + + let chars = CharReader::new(ByteStream::from_string(string)); + let mut parser = Parser::new(chars, &mut self.machine_st); + let op_dir = CompositeOpDir::new(&self.indices.op_dir, None); + + let term_write_result = parser.read_term(&op_dir, Tokens::Default) + .map_err(|err| error_after_read_term(err, 0, &parser)) + .and_then(|term| { + write_term_to_heap( + &term, + &mut self.machine_st.heap, + &mut self.machine_st.atom_tbl, + ) + }); + + match term_write_result { + Ok(term_write_result) => Ok(Some(term_write_result)), + Err(CompilationError::ParserError(e)) if e.is_unexpected_eof() => { + let value = self.machine_st.registers[2]; + self.machine_st.unify_atom(atom!("end_of_file"), value); + + Ok(None) + } + Err(e) => { + let stub = functor_stub(atom!("read_term_from_chars"), 3); + let e = self.machine_st.session_error(SessionError::from(e)); + + Err(self.machine_st.error_form(e, stub)) + } + } + } + + #[inline(always)] + pub(crate) fn read_from_chars(&mut self) -> CallResult { + if let Some(atom_or_string) = self.machine_st.value_to_str_like(self.machine_st.registers[1]) { + if let Some(term_write_result) = self.read_term_and_write_to_heap(atom_or_string)? { + let result = heap_loc_as_cell!(term_write_result.heap_loc); + let var = self.deref_register(2).as_var().unwrap(); + + self.machine_st.bind(var, result); + } + + Ok(()) + } else { + unreachable!() + } } #[inline(always)] pub(crate) fn read_term_from_chars(&mut self) -> CallResult { if let Some(atom_or_string) = self.machine_st.value_to_str_like(self.machine_st.registers[1]) { - let chars = atom_or_string.to_string(); - let stream = Stream::from_owned_string(chars, &mut self.machine_st.arena); - - let term_write_result = match self.machine_st.read(stream, &self.indices.op_dir) { - Ok(term_write_result) => term_write_result, - Err(e) => { - let stub = functor_stub(atom!("read_term_from_chars"), 2); - let e = self.machine_st.session_error(SessionError::from(e)); - - return Err(self.machine_st.error_form(e, stub)); + if let Some(term_write_result) = self.read_term_and_write_to_heap(atom_or_string)? { + self.machine_st.read_term_body(term_write_result) + } else { + if !self.machine_st.fail { + // wrote end_of_file term in this case. + self.machine_st.write_read_term_options(vec![], vec![])?; } - }; - let result = heap_loc_as_cell!(term_write_result.heap_loc); - let var = self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[2] - )).as_var().unwrap(); - - self.machine_st.bind(var, result); + Ok(()) + } } else { unreachable!() } - - Ok(()) } #[inline(always)] pub(crate) fn reset_block(&mut self) { - let addr = self.machine_st.deref(self.machine_st.registers[1]); - self.machine_st.reset_block(addr); + let addr = self.deref_register(1); + + read_heap_cell!(addr, + (HeapCellValueTag::Fixnum, block) => { + self.machine_st.block = block.get_num() as usize; + } + _ => { + self.machine_st.fail = true; + } + ); + } + + #[inline(always)] + pub(crate) fn reset_scc_block(&mut self) { + let addr = self.deref_register(1); + + read_heap_cell!(addr, + (HeapCellValueTag::Fixnum, block) => { + self.machine_st.scc_block = block.get_num() as usize; + } + _ => { + self.machine_st.fail = true; + } + ); } #[inline(always)] @@ -5127,13 +5966,21 @@ impl Machine { #[inline(always)] pub(crate) fn set_seed(&mut self) { - let seed = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); - let mut rand = RANDOM_STATE.borrow_mut(); + let seed = self.deref_register(1); + match Number::try_from(seed) { - Ok(Number::Fixnum(n)) => rand.seed(&Integer::from(n)), - Ok(Number::Integer(n)) => rand.seed(&*n), - Ok(Number::Rational(n)) if n.denom() == &1 => rand.seed(n.numer()), + Ok(Number::Fixnum(n)) => { + let _: StdRng = SeedableRng::seed_from_u64(Integer::from(n).to_u64().unwrap()); + }, + Ok(Number::Integer(n)) => { + let _: StdRng = SeedableRng::seed_from_u64(n.to_u64().unwrap()); + }, + Ok(Number::Rational(n)) => { + if n.denominator() == &UBig::from(1 as u32) { + let _: StdRng = SeedableRng::seed_from_u64(n.numerator().to_u64().unwrap()); + } + }, _ => { self.machine_st.fail = true; } @@ -5142,12 +5989,12 @@ impl Machine { #[inline(always)] pub(crate) fn sleep(&mut self) { - let time = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let time = self.deref_register(1); let time = match Number::try_from(time) { Ok(Number::Float(n)) => n.into_inner(), Ok(Number::Fixnum(n)) => n.get_num() as f64, - Ok(Number::Integer(n)) => n.to_f64(), + Ok(Number::Integer(n)) => n.to_f64().value(), _ => { unreachable!() } @@ -5161,8 +6008,8 @@ impl Machine { #[inline(always)] pub(crate) fn socket_client_open(&mut self) -> CallResult { - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); - let port = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let addr = self.deref_register(1); + let port = self.deref_register(2); let socket_atom = cell_as_atom!(addr); @@ -5249,7 +6096,7 @@ impl Machine { } }; - let stream_addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[3])); + let stream_addr = self.deref_register(3); self.machine_st.bind(stream_addr.as_var().unwrap(), stream); Ok(()) @@ -5257,7 +6104,7 @@ impl Machine { #[inline(always)] pub(crate) fn socket_server_open(&mut self) -> CallResult { - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let addr = self.deref_register(1); let socket_atom = cell_as_atom_cell!(addr).get_name(); let socket_atom = if socket_atom == atom!("[]") { @@ -5266,7 +6113,7 @@ impl Machine { socket_atom }; - let port = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let port = self.deref_register(2); let port = if port.is_var() { String::from("0") @@ -5309,7 +6156,7 @@ impl Machine { } }; - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[3])); + let addr = self.deref_register(3); self.machine_st.bind(addr.as_var().unwrap(), typed_arena_ptr_as_cell!(tcp_listener)); if had_zero_port { @@ -5342,7 +6189,7 @@ impl Machine { } } - let culprit = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let culprit = self.deref_register(1); read_heap_cell!(culprit, (HeapCellValueTag::Cons, cons_ptr) => { @@ -5369,12 +6216,8 @@ impl Machine { let tcp_stream = stream_as_cell!(tcp_stream); let client = atom_as_cell!(client); - let client_addr = self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[2], - )); - let stream_addr = self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[3], - )); + let client_addr = self.deref_register(2); + let stream_addr = self.deref_register(3); self.machine_st.bind(client_addr.as_var().unwrap(), client); self.machine_st.bind(stream_addr.as_var().unwrap(), tcp_stream); @@ -5423,7 +6266,7 @@ impl Machine { self.indices.streams.insert(stream); self.machine_st.heap.push(stream_as_cell!(stream)); - let stream_addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[3])); + let stream_addr = self.deref_register(3); self.machine_st.bind(stream_addr.as_var().unwrap(), stream_as_cell!(stream)); Ok(()) @@ -5473,7 +6316,7 @@ impl Machine { let stream = Stream::from_tls_stream(atom!("TLS"), stream, &mut self.machine_st.arena); self.indices.streams.insert(stream); - let stream_addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[4])); + let stream_addr = self.deref_register(4); self.machine_st.bind(stream_addr.as_var().unwrap(), stream_as_cell!(stream)); } else { unreachable!(); @@ -5484,7 +6327,7 @@ impl Machine { #[inline(always)] pub(crate) fn socket_server_close(&mut self) -> CallResult { - let culprit = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let culprit = self.deref_register(1); read_heap_cell!(culprit, (HeapCellValueTag::Cons, cons_ptr) => { @@ -5533,7 +6376,7 @@ impl Machine { return Err(self.machine_st.error_form(err, stub)); } - let position = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let position = self.deref_register(2); let position = match Number::try_from(position) { Ok(Number::Fixnum(n)) => n.get_num() as u64, @@ -5563,9 +6406,7 @@ impl Machine { 2, )?; - let atom = cell_as_atom!(self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[2] - ))); + let atom = cell_as_atom!(self.deref_register(2)); let property = match atom { atom!("file_name") => { @@ -5637,7 +6478,7 @@ impl Machine { #[inline(always)] pub(crate) fn store_global_var(&mut self) { - let key = cell_as_atom!(self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1]))); + let key = cell_as_atom!(self.deref_register(1)); let value = self.machine_st.registers[2]; let mut ball = Ball::new(); @@ -5655,8 +6496,8 @@ impl Machine { #[inline(always)] pub(crate) fn store_backtrackable_global_var(&mut self) { - let key = cell_as_atom!(self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1]))); - let new_value = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let key = cell_as_atom!(self.deref_register(1)); + let new_value = self.deref_register(2); match self.indices.global_variables.get_mut(&key) { Some((_, ref mut loc)) => match loc { @@ -5681,9 +6522,10 @@ impl Machine { #[inline(always)] pub(crate) fn term_attributed_variables(&mut self) { if self.machine_st.registers[1].is_constant() { + let a2 = self.deref_register(2); self.machine_st.unify_atom( atom!("[]"), - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])), + a2, ); return; @@ -5699,13 +6541,11 @@ impl Machine { #[inline(always)] pub(crate) fn term_variables(&mut self) { - let a1 = self.machine_st.registers[1]; - let a2 = self.machine_st.registers[2]; - - let stored_v = self.machine_st.store(self.machine_st.deref(a1)); + let stored_v = self.deref_register(1); + let a2 = self.deref_register(2); if stored_v.is_constant() { - self.machine_st.unify_atom(atom!("[]"), self.machine_st.store(self.machine_st.deref(a2))); + self.machine_st.unify_atom(atom!("[]"), a2); return; } @@ -5724,7 +6564,7 @@ impl Machine { pub(crate) fn term_variables_under_max_depth(&mut self) { // Term, MaxDepth, VarList let max_depth = cell_as_fixnum!( - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])) + self.deref_register(2) ).get_num() as usize; self.machine_st.term_variables_under_max_depth( @@ -5736,7 +6576,7 @@ impl Machine { #[inline(always)] pub(crate) fn truncate_lifted_heap_to(&mut self) { - let a1 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let a1 = self.deref_register(1); let lh_offset = cell_as_fixnum!(a1).get_num() as usize; self.machine_st.lifted_heap.truncate(lh_offset); @@ -5772,17 +6612,48 @@ impl Machine { false } + fn walk_code_at_ptr(&mut self, index_ptr: usize) -> HeapCellValue { + let mut h = self.machine_st.heap.len(); + + let mut functors = vec![]; + let mut functor_list = vec![]; + + walk_code(&self.code, index_ptr, |instr| { + let old_len = functors.len(); + instr.enqueue_functors(h, &mut self.machine_st.arena, &mut functors); + let new_len = functors.len(); + + for index in old_len..new_len { + let functor_len = functors[index].len(); + + match functor_len { + 0 => {} + 1 => { + functor_list.push(heap_loc_as_cell!(h)); + h += functor_len; + } + _ => { + functor_list.push(str_loc_as_cell!(h)); + h += functor_len; + } + } + } + }); + + for functor in functors { + self.machine_st.heap.extend(functor.into_iter()); + } + + heap_loc_as_cell!( + iter_to_heap_list(&mut self.machine_st.heap, functor_list.into_iter()) + ) + } + #[inline(always)] pub(crate) fn wam_instructions(&mut self) -> CallResult { - let module_name = cell_as_atom!(self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[1]) - )); - - let name = self.machine_st.registers[2]; - let arity = self.machine_st.registers[3]; - - let name = cell_as_atom!(self.machine_st.store(self.machine_st.deref(name))); - let arity = self.machine_st.store(self.machine_st.deref(arity)); + let module_name = cell_as_atom!(self.deref_register(1)); + let name = cell_as_atom!(self.deref_register(2)); + let arity = self.deref_register(3); let arity = match Number::try_from(arity) { Ok(Number::Fixnum(n)) => n.get_num() as usize, @@ -5829,47 +6700,30 @@ impl Machine { } }; - let mut h = self.machine_st.heap.len(); - - let mut functors = vec![]; - let mut functor_list = vec![]; - - walk_code(&self.code, first_idx, |instr| { - let old_len = functors.len(); - instr.enqueue_functors(h, &mut self.machine_st.arena, &mut functors); - let new_len = functors.len(); - - for index in old_len..new_len { - let functor_len = functors[index].len(); - - match functor_len { - 0 => {} - 1 => { - functor_list.push(heap_loc_as_cell!(h)); - h += functor_len; - } - _ => { - functor_list.push(str_loc_as_cell!(h)); - h += functor_len; - } - } - } - }); - - for functor in functors { - self.machine_st.heap.extend(functor.into_iter()); - } - - let listing = heap_loc_as_cell!( - iter_to_heap_list(&mut self.machine_st.heap, functor_list.into_iter()) - ); - + let listing = self.walk_code_at_ptr(first_idx); let listing_var = self.machine_st.registers[4]; unify!(self.machine_st, listing, listing_var); Ok(()) } + #[inline(always)] + pub(crate) fn inlined_instructions(&mut self) { + let index_ptr = self.deref_register(1); + let index_ptr = match Number::try_from(index_ptr) { + Ok(Number::Fixnum(n)) => n.get_num() as usize, + Ok(Number::Integer(n)) => n.to_usize().unwrap(), + _ => { + unreachable!() + } + }; + + let listing = self.walk_code_at_ptr(index_ptr); + let listing_var = self.machine_st.registers[2]; + + unify!(self.machine_st, listing, listing_var); + } + #[inline(always)] pub(crate) fn write_term(&mut self) -> CallResult { let mut stream = self.machine_st.get_stream_or_alias( @@ -5958,9 +6812,7 @@ impl Machine { &mut self.machine_st.atom_tbl, ); - let result_addr = self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[1] - )); + let result_addr = self.deref_register(1); if let Some(var) = result_addr.as_var() { self.machine_st.bind(var, chars); @@ -5978,9 +6830,10 @@ impl Machine { let buffer = git_version!(cargo_prefix = "cargo:", fallback = "unknown"); let buffer_atom = self.machine_st.atom_tbl.build_with(buffer); + let a1 = self.deref_register(1); self.machine_st.unify_complete_string( buffer_atom, - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])), + a1, ); } @@ -6006,10 +6859,10 @@ impl Machine { #[inline(always)] pub(crate) fn crypto_data_hash(&mut self) { - let encoding = cell_as_atom!(self.machine_st.registers[2]); + let encoding = cell_as_atom!(self.deref_register(2)); let bytes = self.string_encoding_bytes(self.machine_st.registers[1], encoding); - let algorithm = cell_as_atom!(self.machine_st.registers[4]); + let algorithm = cell_as_atom!(self.deref_register(4)); let ints_list = match algorithm { atom!("sha3_224") => { @@ -6146,7 +6999,7 @@ impl Machine { #[inline(always)] pub(crate) fn crypto_data_hkdf(&mut self) { - let encoding = cell_as_atom!(self.machine_st.registers[2]); + let encoding = cell_as_atom!(self.deref_register(2)); let data = self.string_encoding_bytes(self.machine_st.registers[1], encoding); let stub1_gen = || functor_stub(atom!("crypto_data_hkdf"), 4); @@ -6155,9 +7008,9 @@ impl Machine { let stub2_gen = || functor_stub(atom!("crypto_data_hkdf"), 4); let info = self.machine_st.integers_to_bytevec(self.machine_st.registers[4], stub2_gen); - let algorithm = cell_as_atom!(self.machine_st.registers[5]); + let algorithm = cell_as_atom!(self.deref_register(5)); - let length = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[6])); + let length = self.deref_register(6); let length = match Number::try_from(length) { Ok(Number::Fixnum(n)) => usize::try_from(n.get_num()).unwrap(), @@ -6219,7 +7072,7 @@ impl Machine { let stub2_gen = || functor_stub(atom!("crypto_password_hash"), 3); let salt = self.machine_st.integers_to_bytevec(self.machine_st.registers[2], stub2_gen); - let iterations = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[3])); + let iterations = self.deref_register(3); let iterations = match Number::try_from(iterations) { Ok(Number::Fixnum(n)) => u64::try_from(n.get_num()).unwrap(), @@ -6261,7 +7114,7 @@ impl Machine { #[inline(always)] pub(crate) fn crypto_data_encrypt(&mut self) { - let encoding = cell_as_atom!(self.machine_st.registers[3]); + let encoding = cell_as_atom!(self.deref_register(3)); let data = self.string_encoding_bytes(self.machine_st.registers[1], encoding); let aad = self.string_encoding_bytes(self.machine_st.registers[2], encoding); @@ -6308,7 +7161,7 @@ impl Machine { #[inline(always)] pub(crate) fn crypto_data_decrypt(&mut self) { let data = self.string_encoding_bytes(self.machine_st.registers[1], atom!("octet")); - let encoding = cell_as_atom!(self.machine_st.registers[5]); + let encoding = cell_as_atom!(self.deref_register(5)); let aad = self.string_encoding_bytes(self.machine_st.registers[2], encoding); let stub1_gen = || functor_stub(atom!("crypto_data_decrypt"), 7); @@ -6401,7 +7254,7 @@ impl Machine { #[inline(always)] pub(crate) fn ed25519_sign(&mut self) { let key = self.string_encoding_bytes(self.machine_st.registers[1], atom!("octet")); - let encoding = cell_as_atom!(self.machine_st.registers[3]); + let encoding = cell_as_atom!(self.deref_register(3)); let data = self.string_encoding_bytes(self.machine_st.registers[2], encoding); let key_pair = match signature::Ed25519KeyPair::from_pkcs8(&key) { @@ -6429,7 +7282,7 @@ impl Machine { #[inline(always)] pub(crate) fn ed25519_verify(&mut self) { let key = self.string_encoding_bytes(self.machine_st.registers[1], atom!("octet")); - let encoding = cell_as_atom!(self.machine_st.registers[3]); + let encoding = cell_as_atom!(self.deref_register(3)); let data = self.string_encoding_bytes(self.machine_st.registers[2], encoding); let stub_gen = || functor_stub(atom!("ed25519_verify"), 5); let signature = self.machine_st.integers_to_bytevec(self.machine_st.registers[4], stub_gen); @@ -6463,7 +7316,7 @@ impl Machine { #[inline(always)] pub(crate) fn first_non_octet(&mut self) { - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let addr = self.deref_register(1); if let Some(string) = self.machine_st.value_to_str_like(addr) { for c in string.as_str().chars() { @@ -6586,8 +7439,9 @@ impl Machine { } } + let a1 = self.deref_register(1); let command = self.machine_st.value_to_str_like( - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])) + a1 ).unwrap(); match env::var("SHELL") { @@ -6617,8 +7471,8 @@ impl Machine { #[inline(always)] pub(crate) fn chars_base64(&mut self) -> CallResult { - let padding = cell_as_atom!(self.machine_st.registers[3]); - let charset = cell_as_atom!(self.machine_st.registers[4]); + let padding = cell_as_atom!(self.deref_register(3)); + let charset = cell_as_atom!(self.deref_register(4)); let config = if padding == atom!("true") { if charset == atom!("standard") { @@ -6634,7 +7488,7 @@ impl Machine { } }; - if self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])).is_var() { + if self.deref_register(1).is_var() { let b64 = self.machine_st.value_to_str_like(self.machine_st.registers[2]).unwrap(); let bytes = base64::decode_config(b64.as_str(), config); @@ -6666,9 +7520,7 @@ impl Machine { #[inline(always)] pub(crate) fn load_library_as_stream(&mut self) -> CallResult { - let library_name = cell_as_atom!(self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[1] - ))); + let library_name = cell_as_atom!(self.deref_register(1)); use crate::machine::LIBRARIES; @@ -6702,19 +7554,29 @@ impl Machine { #[inline(always)] pub(crate) fn devour_whitespace(&mut self) -> CallResult { - let stream = self.machine_st.get_stream_or_alias( + let mut stream = self.machine_st.get_stream_or_alias( self.machine_st.registers[1], &self.indices.stream_aliases, atom!("$devour_whitespace"), 1, )?; - match self.machine_st.devour_whitespace(stream) { + let mut parser = Parser::new(stream, &mut self.machine_st); + + match devour_whitespace(&mut parser) { Ok(false) => { // not at EOF. + stream.add_lines_read(parser.lines_read()); } - _ => { + Ok(true) => { + stream.add_lines_read(parser.lines_read()); self.machine_st.fail = true; } + Err(err) => { + let stub = functor_stub(atom!("load"), 1); + let err = self.machine_st.syntax_error(err); + + return Err(self.machine_st.error_form(err, stub)); + } } Ok(()) @@ -6782,13 +7644,13 @@ impl Machine { #[inline(always)] pub(crate) fn pop_count(&mut self) { - let number = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let number = self.deref_register(1); let pop_count = integer_as_cell!(match Number::try_from(number) { Ok(Number::Fixnum(n)) => { Number::Fixnum(Fixnum::build_with(n.get_num().count_ones() as i64)) } Ok(Number::Integer(n)) => { - Number::arena_from(n.count_ones().unwrap(), &mut self.machine_st.arena) + Number::arena_from(n.count_ones(), &mut self.machine_st.arena) } _ => { unreachable!() @@ -6963,4 +7825,3 @@ impl hkdf::KeyType for MyKey { self.0 } } - diff --git a/src/machine/term_stream.rs b/src/machine/term_stream.rs index bb92c8d4..8c6b055d 100644 --- a/src/machine/term_stream.rs +++ b/src/machine/term_stream.rs @@ -5,6 +5,7 @@ use crate::machine::loader::*; use crate::machine::machine_errors::*; use crate::parser::ast::*; use crate::parser::parser::*; +use crate::read::devour_whitespace; use crate::predicate_queue; @@ -52,14 +53,14 @@ impl<'a> TermStream for BootstrappingTermStream<'a> { fn next(&mut self, op_dir: &CompositeOpDir) -> Result { self.parser.reset(); self.parser - .read_term(op_dir) + .read_term(op_dir, Tokens::Default) .map_err(CompilationError::from) } #[inline] fn eof(&mut self) -> Result { - self.parser.devour_whitespace()?; // eliminate dangling comments before checking for EOF. - Ok(self.parser.eof()?) + devour_whitespace(&mut self.parser) // eliminate dangling comments before checking for EOF. + .map_err(CompilationError::from) } #[inline] @@ -111,7 +112,7 @@ impl TermStream for LiveTermStream { #[inline] fn eof(&mut self) -> Result { - return Ok(self.term_queue.is_empty()); + Ok(self.term_queue.is_empty()) } #[inline] @@ -125,15 +126,15 @@ pub struct InlineTermStream { impl TermStream for InlineTermStream { fn next(&mut self, _: &CompositeOpDir) -> Result { - Err(CompilationError::from(ParserError::UnexpectedEOF)) + Err(CompilationError::from(ParserError::unexpected_eof())) } fn eof(&mut self) -> Result { - Ok(true) + Ok(true) } fn listing_src(&self) -> &ListingSource { - &ListingSource::User + &ListingSource::User } } diff --git a/src/machine/unify.rs b/src/machine/unify.rs new file mode 100644 index 00000000..e73fa4a1 --- /dev/null +++ b/src/machine/unify.rs @@ -0,0 +1,788 @@ +use crate::arena::*; +use crate::forms::*; +use crate::heap_iter::stackful_preorder_iter; +use crate::machine::*; +use crate::machine::machine_state::*; +use crate::machine::partial_string::*; +use crate::types::*; + +use std::cmp::Ordering; +use std::ops::{Deref, DerefMut}; + +use derive_deref::*; +use fxhash::FxBuildHasher; +use indexmap::IndexSet; + +pub(crate) trait Unifier: DerefMut { + fn unify_structure(&mut self, s1: usize, value: HeapCellValue) { + // s1 is the value of a STR cell. + let (n1, a1) = cell_as_atom_cell!(self.heap[s1]).get_name_and_arity(); + + read_heap_cell!(value, + (HeapCellValueTag::Str, s2) => { + let (n2, a2) = cell_as_atom_cell!(self.heap[s2]) + .get_name_and_arity(); + + if n1 == n2 && a1 == a2 { + for idx in (0..a1).rev() { + self.pdl.push(heap_loc_as_cell!(s2+1+idx)); + self.pdl.push(heap_loc_as_cell!(s1+1+idx)); + } + } else { + self.fail = true; + } + } + (HeapCellValueTag::Lis, l2) => { + if a1 == 2 && n1 == atom!(".") { + for idx in (0..2).rev() { + self.pdl.push(heap_loc_as_cell!(l2+1+idx)); + self.pdl.push(heap_loc_as_cell!(s1+1+idx)); + } + } else { + self.fail = true; + } + } + (HeapCellValueTag::Atom, (n2, a2)) => { + self.fail = !(a1 == 0 && a2 == 0 && n1 == n2); + } + (HeapCellValueTag::AttrVar, h) => { + Self::bind(self, Ref::attr_var(h), str_loc_as_cell!(s1)); + } + (HeapCellValueTag::Var, h) => { + Self::bind(self, Ref::heap_cell(h), str_loc_as_cell!(s1)); + } + (HeapCellValueTag::StackVar, s) => { + Self::bind(self, Ref::stack_cell(s), str_loc_as_cell!(s1)); + } + _ => { + self.fail = true; + } + ); + } + + fn unify_list(&mut self, l1: usize, value: HeapCellValue) { + read_heap_cell!(value, + (HeapCellValueTag::Lis, l2) => { + for idx in (0..2).rev() { + self.pdl.push(heap_loc_as_cell!(l2 + idx)); + self.pdl.push(heap_loc_as_cell!(l1 + idx)); + } + } + (HeapCellValueTag::Str, s2) => { + let (n2, a2) = cell_as_atom_cell!(self.heap[s2]) + .get_name_and_arity(); + + if a2 == 2 && n2 == atom!(".") { + for idx in (0..2).rev() { + self.pdl.push(heap_loc_as_cell!(s2+1+idx)); + self.pdl.push(heap_loc_as_cell!(l1+idx)); + } + } else { + self.fail = true; + } + } + (HeapCellValueTag::PStrLoc | HeapCellValueTag::CStr | HeapCellValueTag::PStr) => { + Self::unify_partial_string(self, list_loc_as_cell!(l1), value) + } + (HeapCellValueTag::AttrVar, h) => { + Self::bind(self, Ref::attr_var(h), list_loc_as_cell!(l1)); + } + (HeapCellValueTag::Var, h) => { + Self::bind(self, Ref::heap_cell(h), list_loc_as_cell!(l1)); + } + (HeapCellValueTag::StackVar, s) => { + Self::bind(self, Ref::stack_cell(s), list_loc_as_cell!(l1)); + } + _ => { + self.fail = true; + } + ); + } + + fn unify_complete_string(&mut self, atom: Atom, value: HeapCellValue) { + if let Some(r) = value.as_var() { + if atom == atom!("") { + Self::bind(self, r, atom_as_cell!(atom!("[]"))); + } else { + Self::bind(self, r, atom_as_cstr_cell!(atom)); + } + + return; + } + + read_heap_cell!(value, + (HeapCellValueTag::Atom, (cstr_atom, arity)) if atom == atom!("") => { + debug_assert_eq!(arity, 0); + self.fail = cstr_atom != atom!("[]"); + } + (HeapCellValueTag::Str, s) => { + let (name, arity) = cell_as_atom_cell!(self.heap[s]) + .get_name_and_arity(); + + if arity == 0 { + self.fail = atom == atom!("") && name != atom!("[]"); + } else { + // this is intentionally the same policy for + // value.tag() == Lis and PStrLoc. they're not + // grouped together to allow for arity == 0. + Self::unify_partial_string(self, atom_as_cstr_cell!(atom), value); + + if !self.pdl.is_empty() { + Self::unify_internal(self); + } + } + } + (HeapCellValueTag::CStr, cstr_atom) => { + self.fail = atom != cstr_atom; + } + (HeapCellValueTag::Lis | HeapCellValueTag::PStrLoc) => { + Self::unify_partial_string(self, atom_as_cstr_cell!(atom), value); + + if !self.pdl.is_empty() { + Self::unify_internal(self); + } + } + _ => { + self.fail = true; + } + ); + } + + // the return value of unify_partial_string is interpreted as + // follows: + // + // Some(None) -- the strings are equal, nothing to unify + // Some(Some(f2,f1)) -- prefixes equal, try to unify focus values f2, f1 + // None -- prefixes not equal, unification fails + // + // d1's tag is assumed to be one of LIS, STR or PSTRLOC. + fn unify_partial_string(&mut self, value_1: HeapCellValue, value_2: HeapCellValue) { + if let Some(r) = value_2.as_var() { + Self::bind(self, r, value_1); + return; + } + + let machine_st = self.deref_mut(); + + let s1 = machine_st.heap.len(); + + machine_st.heap.push(value_1); + machine_st.heap.push(value_2); + + let mut pstr_iter1 = HeapPStrIter::new(&machine_st.heap, s1); + let mut pstr_iter2 = HeapPStrIter::new(&machine_st.heap, s1 + 1); + + match compare_pstr_prefixes(&mut pstr_iter1, &mut pstr_iter2) { + PStrCmpResult::Ordered(Ordering::Equal) => {} + PStrCmpResult::Ordered(Ordering::Less) => { + if pstr_iter2.focus.as_var().is_none() { + machine_st.fail = true; + } else { + machine_st.pdl.push(empty_list_as_cell!()); + machine_st.pdl.push(pstr_iter2.focus); + } + } + PStrCmpResult::Ordered(Ordering::Greater) => { + if pstr_iter1.focus.as_var().is_none() { + machine_st.fail = true; + } else { + machine_st.pdl.push(empty_list_as_cell!()); + machine_st.pdl.push(pstr_iter1.focus); + } + } + continuable @ PStrCmpResult::FirstIterContinuable(iteratee) | + continuable @ PStrCmpResult::SecondIterContinuable(iteratee) => { + if continuable.is_second_iter() { + std::mem::swap(&mut pstr_iter1, &mut pstr_iter2); + } + + let mut chars_iter = PStrCharsIter { + iter: pstr_iter1, + item: Some(iteratee), + }; + + let mut focus = pstr_iter2.focus; + + 'outer: loop { + while let Some(c) = chars_iter.peek() { + read_heap_cell!(focus, + (HeapCellValueTag::Lis, l) => { + let val = pstr_iter2.heap[l]; + + machine_st.pdl.push(val); + machine_st.pdl.push(char_as_cell!(c)); + + focus = pstr_iter2.heap[l+1]; + } + (HeapCellValueTag::Str, s) => { + let (name, arity) = cell_as_atom_cell!(pstr_iter2.heap[s]) + .get_name_and_arity(); + + if name == atom!(".") && arity == 2 { + machine_st.pdl.push(pstr_iter2.heap[s+1]); + machine_st.pdl.push(char_as_cell!(c)); + + focus = pstr_iter2.heap[s+2]; + } else { + machine_st.fail = true; + break 'outer; + } + } + (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => { + match chars_iter.item.unwrap() { + PStrIteratee::Char(focus, _) => { + machine_st.pdl.push(machine_st.heap[focus]); + machine_st.pdl.push(heap_loc_as_cell!(h)); + } + PStrIteratee::PStrSegment(focus, _, n) => { + read_heap_cell!(machine_st.heap[focus], + (HeapCellValueTag::CStr | HeapCellValueTag::PStr, pstr_atom) => { + if focus < machine_st.heap.len() - 2 { + machine_st.heap.pop(); + machine_st.heap.pop(); + } + + if n == 0 { + let target_cell = match machine_st.heap[focus].get_tag() { + HeapCellValueTag::CStr => { + atom_as_cstr_cell!(pstr_atom) + } + HeapCellValueTag::PStr => { + pstr_loc_as_cell!(focus) + } + _ => { + unreachable!() + } + }; + + machine_st.pdl.push(target_cell); + machine_st.pdl.push(heap_loc_as_cell!(h)); + } else { + let h_len = machine_st.heap.len(); + + machine_st.heap.push(pstr_offset_as_cell!(focus)); + machine_st.heap.push(fixnum_as_cell!( + Fixnum::build_with(n as i64) + )); + + machine_st.pdl.push(pstr_loc_as_cell!(h_len)); + machine_st.pdl.push(heap_loc_as_cell!(h)); + } + + return; + } + (HeapCellValueTag::PStrOffset, pstr_loc) => { + let n0 = cell_as_fixnum!(machine_st.heap[focus+1]) + .get_num() as usize; + + if pstr_loc < machine_st.heap.len() - 2 { + machine_st.heap.pop(); + machine_st.heap.pop(); + } + + if n == n0 { + machine_st.pdl.push(pstr_loc_as_cell!(focus)); + machine_st.pdl.push(heap_loc_as_cell!(h)); + } else { + let h_len = machine_st.heap.len(); + + machine_st.heap.push(pstr_offset_as_cell!(pstr_loc)); + machine_st.heap.push(fixnum_as_cell!( + Fixnum::build_with(n as i64) + )); + + machine_st.pdl.push(pstr_loc_as_cell!(h_len)); + machine_st.pdl.push(heap_loc_as_cell!(h)); + } + + return; + } + _ => { + } + ); + + if focus < machine_st.heap.len() - 2 { + machine_st.heap.pop(); + machine_st.heap.pop(); + } + + machine_st.pdl.push(machine_st.heap[focus]); + machine_st.pdl.push(heap_loc_as_cell!(h)); + + return; + } + } + + break 'outer; + } + _ => { + machine_st.fail = true; + break 'outer; + } + ); + + chars_iter.next(); + } + + chars_iter.iter.next(); + + machine_st.pdl.push(focus); + machine_st.pdl.push(chars_iter.iter.focus); + + break; + } + } + PStrCmpResult::Unordered => { + machine_st.pdl.push(pstr_iter1.focus); + machine_st.pdl.push(pstr_iter2.focus); + } + } + + machine_st.heap.pop(); + machine_st.heap.pop(); + } + + fn unify_atom(&mut self, atom: Atom, value: HeapCellValue) { + read_heap_cell!(value, + (HeapCellValueTag::Atom, (name, arity)) => { + self.fail = !(arity == 0 && name == atom); + } + (HeapCellValueTag::Str, s) => { + let (name, arity) = cell_as_atom_cell!(self.heap[s]) + .get_name_and_arity(); + + self.fail = !(arity == 0 && name == atom); + } + (HeapCellValueTag::CStr, cstr_atom) if atom == atom!("[]") => { + self.fail = cstr_atom != atom!(""); + } + (HeapCellValueTag::Char, c1) => { + if let Some(c2) = atom.as_char() { + self.fail = c1 != c2; + } else { + self.fail = true; + } + } + (HeapCellValueTag::AttrVar, h) => { + Self::bind(self, Ref::attr_var(h), atom_as_cell!(atom)); + } + (HeapCellValueTag::Var, h) => { + Self::bind(self, Ref::heap_cell(h), atom_as_cell!(atom)); + } + (HeapCellValueTag::StackVar, s) => { + Self::bind(self, Ref::stack_cell(s), atom_as_cell!(atom)); + } + _ => { + self.fail = true; + } + ); + } + + fn unify_char(&mut self, c: char, value: HeapCellValue) { + read_heap_cell!(value, + (HeapCellValueTag::Atom, (name, arity)) => { + if let Some(c2) = name.as_char() { + self.fail = !(c == c2 && arity == 0); + } else { + self.fail = true; + } + } + (HeapCellValueTag::Str, s) => { + let (name, arity) = cell_as_atom_cell!(self.heap[s]) + .get_name_and_arity(); + + if let Some(c2) = name.as_char() { + self.fail = !(c == c2 && arity == 0); + } else { + self.fail = true; + } + } + (HeapCellValueTag::Char, c2) => { + if c != c2 { + self.fail = true; + } + } + (HeapCellValueTag::AttrVar, h) => { + Self::bind(self, Ref::attr_var(h), char_as_cell!(c)); + } + (HeapCellValueTag::Var, h) => { + Self::bind(self, Ref::heap_cell(h), char_as_cell!(c)); + } + (HeapCellValueTag::StackVar, s) => { + Self::bind(self, Ref::stack_cell(s), char_as_cell!(c)); + } + _ => { + self.fail = true; + } + ); + } + + fn unify_fixnum(&mut self, n1: Fixnum, value: HeapCellValue) { + if let Some(r) = value.as_var() { + Self::bind(self, r, fixnum_as_cell!(n1)); + return; + } + + match Number::try_from(value) { + Ok(n2) => match n2 { + Number::Fixnum(n2) if n1.get_num() == n2.get_num() => {} + Number::Integer(n2) if n1.get_num() == *n2 => {} + Number::Rational(n2) if n1.get_num() == *n2 => {} + _ => { + self.fail = true; + } + }, + Err(_) => { + self.fail = true; + } + } + } + + fn unify_big_num(&mut self, n1: TypedArenaPtr, value: HeapCellValue) + where N: PartialEq + + PartialEq + + PartialEq + + ArenaAllocated + { + if let Some(r) = value.as_var() { + Self::bind(self, r, typed_arena_ptr_as_cell!(n1)); + return; + } + + match Number::try_from(value) { + Ok(n2) => match n2 { + Number::Fixnum(n2) if *n1 == n2.get_num() => {} + Number::Integer(n2) if *n1 == *n2 => {} + Number::Rational(n2) if *n1 == *n2 => {} + _ => { + self.fail = true; + } + }, + Err(_) => { + self.fail = true; + } + } + } + + fn unify_f64(&mut self, f1: F64Ptr, value: HeapCellValue) { + if let Some(r) = value.as_var() { + Self::bind(self, r, HeapCellValue::from(f1)); + return; + } + + read_heap_cell!(value, + (HeapCellValueTag::F64, f2) => { + self.fail = **f1 != **f2; + } + _ => { + self.fail = true; + } + ); + } + + fn unify_constant(&mut self, ptr: UntypedArenaPtr, value: HeapCellValue) { + if let Some(ptr2) = value.to_untyped_arena_ptr() { + if ptr.get_ptr() == ptr2.get_ptr() { + return; + } + } + + match_untyped_arena_ptr!(ptr, + (ArenaHeaderTag::Integer, int_ptr) => { + Self::unify_big_num(self, int_ptr, value); + } + (ArenaHeaderTag::Rational, rat_ptr) => { + Self::unify_big_num(self, rat_ptr, value); + } + (ArenaHeaderTag::Stream, stream) => { + read_heap_cell!(value, + (HeapCellValueTag::AttrVar | HeapCellValueTag::Var | HeapCellValueTag::StackVar) => { + Self::bind(self, value.as_var().unwrap(), untyped_arena_ptr_as_cell!(ptr)); + } + (HeapCellValueTag::Atom, (name, arity)) => { + if arity > 0 { + self.fail = true; + } else { + let stream_options = stream.options(); + + if let Some(alias) = stream_options.get_alias() { + self.fail = name != alias; + } else { + self.fail = true; + } + } + } + _ => { + self.fail = true; + } + ); + } + _ => { + if let Some(r) = value.as_var() { + Self::bind(self, r, untyped_arena_ptr_as_cell!(ptr)); + } else { + self.fail = true; + } + } + ); + } + + fn unify_internal(&mut self) { + let mut tabu_list = IndexSet::with_hasher(FxBuildHasher::default()); + + while !(self.pdl.is_empty() || self.fail) { + let s1 = self.pdl.pop().unwrap(); + let s1 = (self.deref() as &MachineState).deref(s1); + + let s2 = self.pdl.pop().unwrap(); + let s2 = (self.deref() as &MachineState).deref(s2); + + if s1 != s2 { + let d1 = self.store(s1); + let d2 = self.store(s2); + + read_heap_cell!(d1, + (HeapCellValueTag::AttrVar, h) => { + Self::bind(self, Ref::attr_var(h), d2); + } + (HeapCellValueTag::Var, h) => { + Self::bind(self, Ref::heap_cell(h), d2); + } + (HeapCellValueTag::StackVar, s) => { + Self::bind(self, Ref::stack_cell(s), d2); + } + (HeapCellValueTag::Atom, (name, arity)) => { + debug_assert_eq!(arity, 0); + Self::unify_atom(self, name, d2); + } + (HeapCellValueTag::Str, s1) => { + if tabu_list.contains(&(d1, d2)) { + continue; + } + + Self::unify_structure(self, s1, d2); + + if !self.fail { + let d2 = self.store(d2); + tabu_list.insert((d1, d2)); + } + } + (HeapCellValueTag::Lis, l1) => { + if d2.is_ref() { + if tabu_list.contains(&(d1, d2)) { + continue; + } + } + + Self::unify_list(self, l1, d2); + + if !self.fail { + let d2 = self.store(d2); + tabu_list.insert((d1, d2)); + } + } + (HeapCellValueTag::PStrLoc) => { + read_heap_cell!(d2, + (HeapCellValueTag::PStrLoc | + HeapCellValueTag::Lis | + HeapCellValueTag::Str) => { + if tabu_list.contains(&(d1, d2)) { + continue; + } + } + (HeapCellValueTag::CStr | + HeapCellValueTag::AttrVar | + HeapCellValueTag::Var | + HeapCellValueTag::StackVar) => { + } + _ => { + self.fail = true; + break; + } + ); + + Self::unify_partial_string(self, d1, d2); + + if !self.fail && !d2.is_constant() { + let d2 = self.store(d2); + tabu_list.insert((d1, d2)); + } + } + (HeapCellValueTag::CStr) => { + read_heap_cell!(d2, + (HeapCellValueTag::AttrVar, h) => { + Self::bind(self, Ref::attr_var(h), d1); + continue; + } + (HeapCellValueTag::Var, h) => { + Self::bind(self, Ref::heap_cell(h), d1); + continue; + } + (HeapCellValueTag::StackVar, s) => { + Self::bind(self, Ref::stack_cell(s), d1); + continue; + } + (HeapCellValueTag::Str | + HeapCellValueTag::Lis | + HeapCellValueTag::PStrLoc) => { + } + (HeapCellValueTag::CStr) => { + self.fail = d1 != d2; + continue; + } + _ => { + self.fail = true; + return; + } + ); + + Self::unify_partial_string(self, d2, d1); + } + (HeapCellValueTag::F64, f1) => { + Self::unify_f64(self, f1, d2); + } + (HeapCellValueTag::Fixnum, n1) => { + Self::unify_fixnum(self, n1, d2); + } + (HeapCellValueTag::Char, c1) => { + Self::unify_char(self, c1, d2); + } + (HeapCellValueTag::Cons, ptr_1) => { + Self::unify_constant(self, ptr_1, d2); + } + _ => { + unreachable!(); + } + ); + } + } + } + + fn bind(&mut self, r: Ref, value: HeapCellValue); +} + +#[inline] +fn bind_with_occurs_check(unifier: &mut U, r: Ref, value: HeapCellValue) -> bool { + if let RefTag::StackCell = r.get_tag() { + // local variable optimization -- r cannot occur in the + // heap structure bound to value, so don't bother + // traversing value. + U::bind(unifier, r, value); + return false; + } + + let mut occurs_triggered = false; + + if !value.is_constant() { + let machine_st: &mut MachineState = unifier.deref_mut(); + + for cell in stackful_preorder_iter(&mut machine_st.heap, &mut machine_st.stack, value) { + let cell = unmark_cell_bits!(cell); + + if let Some(inner_r) = cell.as_var() { + if r == inner_r { + occurs_triggered = true; + break; + } + } + } + } + + if occurs_triggered { + unifier.fail = true; + } else { + U::bind(unifier, r, value); + } + + return occurs_triggered; +} + +#[derive(Deref, DerefMut)] +pub(crate) struct DefaultUnifier<'a> { + machine_st: &'a mut MachineState, +} + +impl<'a> From<&'a mut MachineState> for DefaultUnifier<'a> { + #[inline(always)] + fn from(machine_st: &'a mut MachineState) -> Self { + Self { machine_st } + } +} + +impl<'a> Unifier for DefaultUnifier<'a> { + fn bind(&mut self, r: Ref, value: HeapCellValue) { + self.machine_st.bind(r, value); + } +} + +pub(crate) struct CompositeUnifierForOccursCheck { + unifier: U, +} + +impl Deref for CompositeUnifierForOccursCheck { + type Target = MachineState; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + self.unifier.deref() + } +} + +impl DerefMut for CompositeUnifierForOccursCheck { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + self.unifier.deref_mut() + } +} + +impl From for CompositeUnifierForOccursCheck { + #[inline(always)] + fn from(unifier: U) -> Self { + Self { unifier } + } +} + +impl Unifier for CompositeUnifierForOccursCheck { + fn bind(&mut self, r: Ref, value: HeapCellValue) { + bind_with_occurs_check(&mut self.unifier, r, value); + } +} + +pub(crate) struct CompositeUnifierForOccursCheckWithError { + unifier: U, +} + +impl Deref for CompositeUnifierForOccursCheckWithError { + type Target = MachineState; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + self.unifier.deref() + } +} + +impl DerefMut for CompositeUnifierForOccursCheckWithError { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + self.unifier.deref_mut() + } +} + +impl From for CompositeUnifierForOccursCheckWithError { + #[inline(always)] + fn from(unifier: U) -> Self { + Self { unifier } + } +} + +impl Unifier for CompositeUnifierForOccursCheckWithError { + fn bind(&mut self, r: Ref, value: HeapCellValue) { + if bind_with_occurs_check(&mut self.unifier, r, value) { + let err = self.representation_error(RepFlag::Term); + let stub = functor_stub(atom!("unify_with_occurs_check"), 2); + let err = self.error_form(err, stub); + + self.throw_exception(err); + } + } +} diff --git a/src/macros.rs b/src/macros.rs index 85e2e086..9bd89ab7 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -15,7 +15,7 @@ macro_rules! char_as_cell { macro_rules! fixnum_as_cell { ($n: expr) => { - HeapCellValue::from_bytes($n.into_bytes()) //HeapCellValueTag::Fixnum, $n.get_num() as u64) + HeapCellValue::from_bytes($n.into_bytes()) }; } @@ -378,6 +378,21 @@ macro_rules! read_heap_cell_pat_body { #[allow(unused_braces)] $code }); + ($cell:ident, CutPoint, $value:ident, $code:expr) => ({ + let $value = Fixnum::from_bytes($cell.into_bytes()); + #[allow(unused_braces)] + $code + }); + ($cell:ident, Fixnum | CutPoint, $value:ident, $code:expr) => ({ + let $value = Fixnum::from_bytes($cell.into_bytes()); + #[allow(unused_braces)] + $code + }); + ($cell:ident, CutPoint | Fixnum, $value:ident, $code:expr) => ({ + let $value = Fixnum::from_bytes($cell.into_bytes()); + #[allow(unused_braces)] + $code + }); ($cell:ident, Char, $value:ident, $code:expr) => ({ let $value = unsafe { char::from_u32_unchecked($cell.get_value() as u32) }; #[allow(unused_braces)] @@ -540,23 +555,7 @@ macro_rules! functor_term { macro_rules! compare_number_instr { ($cmp: expr, $at_1: expr, $at_2: expr) => {{ $cmp.set_terms($at_1, $at_2); - call_clause!(ClauseType::Inlined(InlinedClauseType::CompareNumber($cmp)), 0) - }}; -} - -macro_rules! call_clause { - ($clause_type:expr, $pvs:expr) => {{ - let mut instr = $clause_type.to_instr(); - instr.perm_vars_mut().map(|pvs| *pvs = $pvs); - instr - }}; -} - -macro_rules! call_clause_by_default { - ($clause_type:expr, $pvs:expr) => {{ - let mut instr = $clause_type.to_instr().to_default(); - instr.perm_vars_mut().map(|pvs| *pvs = $pvs); - instr + ClauseType::Inlined(InlinedClauseType::CompareNumber($cmp)).to_instr() }}; } @@ -590,6 +589,7 @@ macro_rules! index_store { extensible_predicates: ExtensiblePredicates::with_hasher(FxBuildHasher::default()), local_extensible_predicates: LocalExtensiblePredicates::with_hasher(FxBuildHasher::default()), global_variables: GlobalVarDir::with_hasher(FxBuildHasher::default()), + goal_expansion_indices: GoalExpansionIndices::with_hasher(FxBuildHasher::default()), meta_predicates: MetaPredicateDir::with_hasher(FxBuildHasher::default()), modules: $modules, op_dir: $op_dir, @@ -625,6 +625,12 @@ macro_rules! compare_term_test { $machine_st.pdl.push($e2); $machine_st.pdl.push($e1); - $machine_st.compare_term_test() + $machine_st.compare_term_test(VarComparison::Distinct) + }}; + ($machine_st:expr, $e1:expr, $e2:expr, $var_comparison:expr) => {{ + $machine_st.pdl.push($e2); + $machine_st.pdl.push($e1); + + $machine_st.compare_term_test($var_comparison) }}; } diff --git a/src/parser/ast.rs b/src/parser/ast.rs index b1c0f5d9..fc728782 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -4,15 +4,15 @@ use crate::machine::machine_indices::*; use crate::parser::char_reader::*; use crate::types::HeapCellValueTag; -use std::cell::Cell; +use std::cell::{Cell, Ref, RefCell, RefMut}; use std::fmt; -use std::hash::Hash; -use std::io::{Error as IOError}; -use std::ops::Neg; +use std::hash::{Hash, Hasher}; +use std::io::{Error as IOError, ErrorKind}; +use std::ops::{Deref, Neg}; use std::rc::Rc; use std::vec::Vec; -use crate::parser::rug::{Integer, Rational}; +use crate::parser::dashu::{Integer, Rational}; use fxhash::FxBuildHasher; use indexmap::IndexMap; @@ -227,7 +227,7 @@ macro_rules! perm_v { }; } -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum GenContext { Head, Mid(usize), @@ -303,17 +303,19 @@ pub type OpDir = IndexMap<(Atom, Fixity), OpDesc, FxBuildHasher>; #[derive(Debug, Clone, Copy)] pub struct MachineFlags { pub double_quotes: DoubleQuotes, + pub unknown: Unknown, } impl Default for MachineFlags { fn default() -> Self { MachineFlags { double_quotes: DoubleQuotes::default(), + unknown: Unknown::default(), } } } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] pub enum DoubleQuotes { Atom, Chars, @@ -340,6 +342,34 @@ impl Default for DoubleQuotes { } } +#[derive(Debug, Clone, Copy)] +pub enum Unknown { + Error, + Fail, + Warn, +} + +impl Unknown { + pub fn is_error(self) -> bool { + matches!(self, Unknown::Error) + } + + pub fn is_fail(self) -> bool { + matches!(self, Unknown::Fail) + } + + pub fn is_warn(self) -> bool { + matches!(self, Unknown::Warn) + } +} + +impl Default for Unknown { + #[inline] + fn default() -> Self { + Unknown::Error + } +} + pub fn default_op_dir() -> OpDir { let mut op_dir = OpDir::with_hasher(FxBuildHasher::default()); @@ -380,7 +410,7 @@ pub enum ParserError { NonPrologChar(usize, usize), ParseBigInt(usize, usize), UnexpectedChar(char, usize, usize), - UnexpectedEOF, + // UnexpectedEOF, Utf8Error(usize, usize), } @@ -403,16 +433,30 @@ impl ParserError { ParserError::BackQuotedString(..) => atom!("back_quoted_string"), ParserError::IncompleteReduction(..) => atom!("incomplete_reduction"), ParserError::InvalidSingleQuotedCharacter(..) => atom!("invalid_single_quoted_character"), + ParserError::IO(e) if e.kind() == ErrorKind::UnexpectedEof => atom!("unexpected_end_of_file"), ParserError::IO(_) => atom!("input_output_error"), - ParserError::LexicalError(_) => atom!("lexical_error"), // TODO: ? + ParserError::LexicalError(_) => atom!("lexical_error"), ParserError::MissingQuote(..) => atom!("missing_quote"), ParserError::NonPrologChar(..) => atom!("non_prolog_character"), ParserError::ParseBigInt(..) => atom!("cannot_parse_big_int"), ParserError::UnexpectedChar(..) => atom!("unexpected_char"), - ParserError::UnexpectedEOF => atom!("unexpected_end_of_file"), ParserError::Utf8Error(..) => atom!("utf8_conversion_error"), } } + + #[inline] + pub fn unexpected_eof() -> Self { + ParserError::IO(std::io::Error::from(ErrorKind::UnexpectedEof)) + } + + #[inline] + pub fn is_unexpected_eof(&self) -> bool { + if let ParserError::IO(e) = self { + e.kind() == ErrorKind::UnexpectedEof + } else { + false + } + } } impl From for ParserError { @@ -493,6 +537,21 @@ impl Fixnum { .with_f(false) } + #[inline] + pub fn as_cutpoint(num: i64) -> Self { + Fixnum::new() + .with_num(u64::from_ne_bytes(num.to_ne_bytes()) & ((1 << 56) - 1)) + .with_tag(HeapCellValueTag::CutPoint as u8) + .with_m(false) + .with_f(false) + } + + #[inline] + pub fn get_tag(&self) -> HeapCellValueTag { + use modular_bitfield::Specifier; + HeapCellValueTag::from_bytes(self.tag()).unwrap() + } + #[inline] pub fn build_with_checked(num: i64) -> Result { const UPPER_BOUND: i64 = (1 << 55) - 1; @@ -572,6 +631,110 @@ impl Literal { } } + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct VarPtr(Rc>); + +impl Hash for VarPtr { + #[inline(always)] + fn hash(&self, hasher: &mut H) { + self.borrow().hash(hasher) + } +} + +impl Deref for VarPtr { + type Target = RefCell; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + self.0.deref() + } +} + +impl VarPtr { + #[inline(always)] + pub(crate) fn borrow(&self) -> Ref<'_, Var> { + self.0.borrow() + } + + #[inline(always)] + pub(crate) fn borrow_mut(&self) -> RefMut<'_, Var> { + self.0.borrow_mut() + } + + pub(crate) fn to_var_num(&self) -> Option { + match *self.borrow() { + Var::Generated(var_num) => Some(var_num), + _ => None, + } + } + + pub(crate) fn set(&self, var: Var) { + let mut var_ref = self.borrow_mut(); + *var_ref = var; + } +} + +impl From for VarPtr { + #[inline(always)] + fn from(value: Var) -> VarPtr { + VarPtr(Rc::new(RefCell::new(value))) + } +} + +impl From for VarPtr { + #[inline(always)] + fn from(value: String) -> VarPtr { + VarPtr::from(Var::from(value)) + } +} + +impl From<&str> for VarPtr { + #[inline(always)] + fn from(value: &str) -> VarPtr { + VarPtr::from(value.to_owned()) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum Var { + Generated(usize), + InSitu(usize), + Named(String), +} + +impl From for Var { + #[inline(always)] + fn from(value: String) -> Var { + Var::Named(value) + } +} + +impl From<&str> for Var { + #[inline(always)] + fn from(value: &str) -> Var { + Var::Named(value.to_owned()) + } +} + +impl Var { + #[inline(always)] + pub fn as_str(&self) -> Option<&str> { + match self { + Var::Named(value) => Some(&value), + _ => None, + } + } + + #[inline(always)] + pub fn to_string(&self) -> String { + match self { + Var::InSitu(n) | Var::Generated(n) => format!("_{}", n), + Var::Named(value) => value.to_owned(), + } + } +} + #[derive(Debug, Clone)] pub enum Term { AnonVar, @@ -582,7 +745,7 @@ pub enum Term { // other PartialString variants in as_partial_string. PartialString(Cell, String, Box), CompleteString(Cell, Atom), - Var(Cell, Rc), + Var(Cell, VarPtr), } impl Term { @@ -626,8 +789,25 @@ impl Term { } } +#[inline] +pub fn source_arity(terms: &[Term]) -> usize { + if let Some(last_arg) = terms.last() { + if let Term::Literal(_, Literal::CodeIndex(_)) = last_arg { + return terms.len() - 1; + } + } + + terms.len() +} + fn unfold_by_str_once(term: &mut Term, s: Atom) -> Option<(Term, Term)> { if let Term::Clause(_, ref name, ref mut subterms) = term { + if let Some(last_arg) = subterms.last() { + if let Term::Literal(_, Literal::CodeIndex(_)) = last_arg { + subterms.pop(); + } + } + if name == &s && subterms.len() == 2 { let snd = subterms.pop().unwrap(); let fst = subterms.pop().unwrap(); @@ -650,3 +830,30 @@ pub fn unfold_by_str(mut term: Term, s: Atom) -> Vec { terms.push(term); terms } + +fn unfold_by_str_ref_once(term: &Term, s: Atom) -> Option<(&Term, &Term)> { + if let Term::Clause(_, ref name, ref subterms) = term { + if name == &s && subterms.len() == 2 { + let fst = &subterms[0]; + let snd = &subterms[1]; + + return Some((fst, snd)); + } + } + + None +} + +pub fn unfold_by_str_ref(mut term: &Term, s: Atom) -> Vec<&Term> { + let mut terms = vec![]; + + while let Some((fst, snd)) = unfold_by_str_ref_once(&term, s) { + terms.push(fst); + term = snd; + } + + terms.push(term); + terms +} + + diff --git a/src/parser/char_reader.rs b/src/parser/char_reader.rs index dddabb19..2a1db29f 100644 --- a/src/parser/char_reader.rs +++ b/src/parser/char_reader.rs @@ -20,7 +20,7 @@ use std::str; pub struct CharReader { inner: R, - buf: SmallVec<[u8;4]>, + buf: SmallVec<[u8;32]>, pos: usize, } @@ -111,17 +111,15 @@ impl CharReader { } impl CharReader { - fn refresh_buffer(&mut self) -> io::Result<&[u8]> { + pub fn refresh_buffer(&mut self) -> io::Result<&[u8]> { // If we've reached the end of our internal buffer then we need to fetch // some more data from the underlying reader. // Branch using `>=` instead of the more correct `==` // to tell the compiler that the pos..cap slice is always valid. if self.pos >= self.buf.len() { - debug_assert!(self.pos == self.buf.len()); - self.buf.clear(); - let mut word = [0u8;4]; + let mut word = [0u8; std::mem::size_of::()]; let nread = self.inner.read(&mut word)?; self.buf.extend_from_slice(&word[..nread]); @@ -130,6 +128,19 @@ impl CharReader { Ok(&self.buf[self.pos..]) } + + pub fn peek_byte(&mut self) -> Option> { + match self.refresh_buffer() { + Ok(_buf) => {} + Err(e) => return Some(Err(e)), + } + + return if let Some(b) = self.buf.get(0).cloned() { + Some(Ok(b)) + } else { + None + }; + } } impl CharRead for CharReader { @@ -187,7 +198,7 @@ impl CharRead for CharReader { if self.pos >= self.buf.len() { return None; } else if self.buf.len() - self.pos >= 4 { - return match str::from_utf8(&self.buf[..e.valid_up_to()]) { + return match str::from_utf8(&self.buf[self.pos .. e.valid_up_to()]) { Ok(s) => { let mut chars = s.chars(); let c = chars.next().unwrap(); @@ -195,7 +206,7 @@ impl CharRead for CharReader { Some(Ok(c)) } Err(e) => { - let badbytes = self.buf[..e.valid_up_to()].to_vec(); + let badbytes = self.buf[self.pos .. e.valid_up_to()].to_vec(); Some(Err(io::Error::new(io::ErrorKind::InvalidData, BadUtf8Error { bytes: badbytes }))) @@ -234,10 +245,10 @@ impl CharRead for CharReader { #[inline(always)] fn put_back_char(&mut self, c: char) { let src_len = self.buf.len() - self.pos; - debug_assert!(src_len <= 4); + debug_assert!(src_len <= self.buf.capacity()); let c_len = c.len_utf8(); - let mut shifted_slice = [0u8; 4]; + let mut shifted_slice = [0u8; 32]; shifted_slice[0..src_len].copy_from_slice(&self.buf[self.pos .. self.buf.len()]); diff --git a/src/parser/lexer.rs b/src/parser/lexer.rs index 9aeacd82..3aa82d13 100644 --- a/src/parser/lexer.rs +++ b/src/parser/lexer.rs @@ -5,25 +5,11 @@ use crate::atom_table::*; pub use crate::machine::machine_state::*; use crate::parser::ast::*; use crate::parser::char_reader::*; -use crate::parser::rug::Integer; +use crate::parser::dashu::Integer; use std::convert::TryFrom; use std::fmt; -macro_rules! is_not_eof { - ($parser:expr, $c:expr) => { - match $c { - Ok('\u{0}') => { - $parser.consume('\u{0}'.len_utf8()); - return Ok(true); - } - Ok(c) => c, - Err($crate::parser::ast::ParserError::UnexpectedEOF) => return Ok(true), - Err(e) => return Err(e), - } - }; -} - macro_rules! consume_chars_with { ($token:expr, $e:expr) => { loop { @@ -37,6 +23,12 @@ macro_rules! consume_chars_with { }; } +#[derive(Debug, Default)] +struct LayoutInfo { + inserted: bool, + more: bool, +} + #[derive(Debug, PartialEq)] pub enum Token { Literal(Literal), @@ -94,14 +86,14 @@ impl<'a, R: CharRead> Lexer<'a, R> { pub fn lookahead_char(&mut self) -> Result { match self.reader.peek_char() { Some(Ok(c)) => Ok(c), - _ => Err(ParserError::UnexpectedEOF) + _ => Err(ParserError::unexpected_eof()) } } pub fn read_char(&mut self) -> Result { match self.reader.read_char() { Some(Ok(c)) => Ok(c), - _ => Err(ParserError::UnexpectedEOF) + _ => Err(ParserError::unexpected_eof()) } } @@ -110,7 +102,7 @@ impl<'a, R: CharRead> Lexer<'a, R> { self.reader.put_back_char(c); } - fn skip_char(&mut self, c: char) { + pub fn skip_char(&mut self, c: char) { self.reader.consume(c.len_utf8()); if new_line_char!(c) { @@ -121,18 +113,6 @@ impl<'a, R: CharRead> Lexer<'a, R> { } } - pub fn eof(&mut self) -> Result { - let mut c = is_not_eof!(self.reader, self.lookahead_char()); - - while layout_char!(c) { - self.skip_char(c); - - c = is_not_eof!(self.reader, self.lookahead_char()); - } - - Ok(false) - } - fn single_line_comment(&mut self) -> Result<(), ParserError> { loop { if self.reader.peek_char().is_none() { @@ -168,17 +148,32 @@ impl<'a, R: CharRead> Lexer<'a, R> { let mut c = self.lookahead_char()?; - loop { - while !comment_2_char!(c) { + let mut comment_loop = || -> Result<(), ParserError> { + loop { + while !comment_2_char!(c) { + self.skip_char(c); + c = self.lookahead_char()?; + } + self.skip_char(c); c = self.lookahead_char()?; + + if comment_1_char!(c) { + break; + } } - self.skip_char(c); - c = self.lookahead_char()?; + Ok(()) + }; - if comment_1_char!(c) { - break; + match comment_loop() { + Err(e) if e.is_unexpected_eof() => { + return Err(ParserError::IncompleteReduction(self.line_num, self.col_num)); + } + Err(e) => { + return Err(e); + } + Ok(_) => { } } @@ -859,7 +854,13 @@ impl<'a, R: CharRead> Lexer<'a, R> { self.get_single_quoted_char() .map(|c| Token::Literal(Literal::Fixnum(Fixnum::build_with(c as i64)))) - .or_else(|_| { + .or_else(|err| { + match err { + ParserError::UnexpectedChar('\'', ..) => { + } + err => return Err(err), + } + self.return_char(c); i64::from_str_radix(&token, 10) @@ -908,38 +909,57 @@ impl<'a, R: CharRead> Lexer<'a, R> { } } - pub fn scan_for_layout(&mut self) -> Result { - let mut layout_inserted = false; - let mut more_layout = true; - - loop { - let cr = self.lookahead_char(); - - match cr { - Ok(c) if layout_char!(c) => { - self.skip_char(c); - layout_inserted = true; + fn consume_layout( + &mut self, + c: Option, + layout_info: &mut LayoutInfo, + ) -> Result<(), ParserError> { + match c { + Some(c) if layout_char!(c) => { + self.skip_char(c); + layout_info.inserted = true; + } + Some(c) if end_line_comment_char!(c) => { + self.single_line_comment()?; + layout_info.inserted = true; + } + Some(c) if comment_1_char!(c) => { + if self.bracketed_comment()? { + layout_info.inserted = true; + } else { + layout_info.more = false; } - Ok(c) if end_line_comment_char!(c) => { - self.single_line_comment()?; - layout_inserted = true; - } - Ok(c) if comment_1_char!(c) => { - if self.bracketed_comment()? { - layout_inserted = true; - } else { - more_layout = false; - } - } - _ => more_layout = false, - }; - - if !more_layout { - break; + } + _ => { + layout_info.more = false; } } - Ok(layout_inserted) + Ok(()) + } + + pub fn scan_for_layout(&mut self) -> Result { + match self.lookahead_char() { + Err(e) => { + Err(e) + } + Ok(c) => { + let mut layout_info = LayoutInfo { inserted: false, more: true }; + let mut cr = Some(c); + + loop { + self.consume_layout(cr, &mut layout_info)?; + + if !layout_info.more { + break; + } + + cr = self.lookahead_char().ok(); + } + + Ok(layout_info.inserted) + } + } } pub fn next_token(&mut self) -> Result { @@ -982,7 +1002,7 @@ impl<'a, R: CharRead> Lexer<'a, R> { return Ok(Token::End); } - Err(ParserError::UnexpectedEOF) => { + Err(e) if e.is_unexpected_eof() => { return Ok(Token::End); } _ => { @@ -1034,7 +1054,7 @@ impl<'a, R: CharRead> Lexer<'a, R> { } if c == '\u{0}' { - return Err(ParserError::UnexpectedEOF); + return Err(ParserError::unexpected_eof()) } self.name_token(c) diff --git a/src/parser/macros.rs b/src/parser/macros.rs index 326772cf..3e6826c9 100644 --- a/src/parser/macros.rs +++ b/src/parser/macros.rs @@ -7,14 +7,20 @@ macro_rules! char_class { #[macro_export] macro_rules! alpha_char { ($c: expr) => { - $c.is_alphabetic() || $c == '_' + (!$c.is_numeric() && + !$c.is_whitespace() && + !$c.is_control() && + !$crate::graphic_token_char!($c) && + !$crate::layout_char!($c) && + !$crate::meta_char!($c) && + !$crate::solo_char!($c)) || $c == '_' }; } #[macro_export] macro_rules! alpha_numeric_char { ($c: expr) => { - $crate::alpha_char!($c) || $crate::decimal_digit_char!($c) + $crate::alpha_char!($c) || $c.is_numeric() }; } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index fa7b8859..ef6a8e0a 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1,8 +1,4 @@ -#[cfg(feature = "num-rug-adapter")] -pub use num_rug_adapter as rug; - -#[cfg(feature = "rug")] -pub use rug; +pub use dashu; // #[macro_use] // extern crate lazy_static; diff --git a/src/parser/parser.rs b/src/parser/parser.rs index 74f1b930..edf5d883 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -1,14 +1,16 @@ +use dashu::Integer; +use dashu::Rational; + use crate::arena::*; use crate::atom_table::*; use crate::parser::ast::*; use crate::parser::char_reader::*; use crate::parser::lexer::*; -use crate::parser::rug::ops::NegAssign; use std::cell::Cell; use std::mem; -use std::rc::Rc; +use std::ops::Neg; #[derive(Debug, Clone, Copy, PartialEq)] enum TokenType { @@ -25,6 +27,16 @@ enum TokenType { End, } +/* +Specifies whether the token sequence should be read from the lexer or +provided via the Provided variant. +*/ +#[derive(Debug)] +pub enum Tokens { + Default, + Provided(Vec), +} + impl TokenType { fn is_sep(self) -> bool { matches!( @@ -266,7 +278,7 @@ fn read_tokens(lexer: &mut Lexer) -> Result, ParserEr break; } } - Err(ParserError::UnexpectedEOF) if !tokens.is_empty() => { + Err(e) if e.is_unexpected_eof() && !tokens.is_empty() => { return Err(ParserError::IncompleteReduction( lexer.line_num, lexer.col_num, @@ -303,8 +315,17 @@ impl<'a, R: CharRead> Parser<'a, R> { Parser { lexer: Lexer::new(stream, machine_st), tokens: vec![], - stack: Vec::new(), - terms: Vec::new(), + stack: vec![], + terms: vec![], + } + } + + pub fn from_lexer(lexer: Lexer<'a, R>) -> Self { + Parser { + lexer, + tokens: vec![], + stack: vec![], + terms: vec![], } } @@ -427,7 +448,7 @@ impl<'a, R: CharRead> Parser<'a, R> { if v.trim() == "_" { self.terms.push(Term::AnonVar); } else { - self.terms.push(Term::Var(Cell::default(), Rc::new(v))); + self.terms.push(Term::Var(Cell::default(), VarPtr::from(v))); } TokenType::Term @@ -602,11 +623,6 @@ impl<'a, R: CharRead> Parser<'a, R> { false } - pub fn devour_whitespace(&mut self) -> Result<(), ParserError> { - self.lexer.scan_for_layout()?; - Ok(()) - } - pub fn reset(&mut self) { self.stack.clear() } @@ -828,6 +844,10 @@ impl<'a, R: CharRead> Parser<'a, R> { return false; } + if let Some(TokenType::Open | TokenType::OpenCT) = self.stack.last().map(|token| token.tt) { + return false; + } + let idx = self.stack.len() - 2; let td = self.stack.remove(idx); @@ -861,7 +881,7 @@ impl<'a, R: CharRead> Parser<'a, R> { }) = get_op_desc(name, op_dir) { if (pre > 0 && inf + post > 0) || is_negate!(spec) { - match self.tokens.last().ok_or(ParserError::UnexpectedEOF)? { + match self.tokens.last().ok_or(ParserError::unexpected_eof())? { // do this when layout hasn't been inserted, // ie. why we don't match on Token::Open. Token::OpenCT => { @@ -906,7 +926,7 @@ impl<'a, R: CharRead> Parser<'a, R> { fn negate_number(&mut self, n: N, negator: Negator, constr: ToLiteral) where - Negator: Fn(N) -> N, + Negator: Fn(N, &mut Arena) -> N, ToLiteral: Fn(N, &mut Arena) -> Literal, { if let Some(desc) = self.stack.last().cloned() { @@ -918,7 +938,9 @@ impl<'a, R: CharRead> Parser<'a, R> { self.stack.pop(); self.terms.pop(); - let literal = constr(negator(n), &mut self.lexer.machine_st.arena); + let arena = &mut self.lexer.machine_st.arena; + let literal = constr(negator(n, arena), arena); + self.shift(Token::Literal(literal), 0, TERM); return; @@ -933,24 +955,31 @@ impl<'a, R: CharRead> Parser<'a, R> { } fn shift_token(&mut self, token: Token, op_dir: &CompositeOpDir) -> Result<(), ParserError> { - fn negate_rc(mut t: TypedArenaPtr) -> TypedArenaPtr { - (&mut t).neg_assign(); - t + fn negate_int_rc(t: TypedArenaPtr, arena: &mut Arena) -> TypedArenaPtr { + let i: Integer = (*t).clone(); + let data = i.neg(); + arena_alloc!(data, arena) + } + + fn negate_rat_rc(t: TypedArenaPtr, arena: &mut Arena) -> TypedArenaPtr { + let r: Rational = (*t).clone(); + let data = r.neg(); + arena_alloc!(data, arena) } match token { Token::Literal(Literal::Fixnum(n)) => { - self.negate_number(n, |n| -n, |n, _| Literal::Fixnum(n)) + self.negate_number(n, |n, _| -n, |n, _| Literal::Fixnum(n)) } Token::Literal(Literal::Integer(n)) => { - self.negate_number(n, negate_rc, |n, _| Literal::Integer(n)) + self.negate_number(n, negate_int_rc, |n, _| Literal::Integer(n)) } Token::Literal(Literal::Rational(n)) => { - self.negate_number(n, negate_rc, |r, _| Literal::Rational(r)) + self.negate_number(n, negate_rat_rc, |r, _| Literal::Rational(r)) } Token::Literal(Literal::Float(n)) => self.negate_number( **n.as_ptr(), - |n| -n, + |n, _| -n, |n, arena| Literal::from(float_alloc!(n, arena)), ), Token::Literal(c) => { @@ -1029,11 +1058,6 @@ impl<'a, R: CharRead> Parser<'a, R> { Ok(()) } - #[inline] - pub fn eof(&mut self) -> Result { - self.lexer.eof() - } - #[inline] pub fn add_lines_read(&mut self, lines_read: usize) { self.lexer.line_num += lines_read; @@ -1045,8 +1069,11 @@ impl<'a, R: CharRead> Parser<'a, R> { } // on success, returns the parsed term and the number of lines read. - pub fn read_term(&mut self, op_dir: &CompositeOpDir) -> Result { - self.tokens = read_tokens(&mut self.lexer)?; + pub fn read_term(&mut self, op_dir: &CompositeOpDir, tokens: Tokens) -> Result { + self.tokens = match tokens { + Tokens::Default => read_tokens(&mut self.lexer)?, + Tokens::Provided(tokens) => tokens, + }; while let Some(token) = self.tokens.pop() { self.shift_token(token, op_dir)?; diff --git a/src/read.rs b/src/read.rs index c8743c2f..e23dc302 100644 --- a/src/read.rs +++ b/src/read.rs @@ -17,6 +17,7 @@ use fxhash::FxBuildHasher; use indexmap::IndexSet; use rustyline::error::ReadlineError; +use rustyline::history::DefaultHistory; use rustyline::{Config, Editor}; use std::collections::VecDeque; @@ -24,19 +25,38 @@ use std::io::{Cursor, Error, ErrorKind, Read}; type SubtermDeque = VecDeque<(usize, usize)>; -impl MachineState { - pub(crate) fn devour_whitespace( - &mut self, - mut inner: Stream, - ) -> Result { - let mut parser = Parser::new(inner, self); +pub(crate) fn devour_whitespace<'a, R: CharRead>(parser: &mut Parser<'a, R>) -> Result { + match parser.lexer.scan_for_layout() { + Err(e) if e.is_unexpected_eof() => { + Ok(true) + } + Err(e) => Err(e), + Ok(_) => { + Ok(false) + } + } +} - parser.devour_whitespace()?; - inner.add_lines_read(parser.lines_read()); +pub(crate) fn error_after_read_term( + err: ParserError, + prior_num_lines_read: usize, + parser: &Parser, +) -> CompilationError { + if err.is_unexpected_eof() { + let line_num = parser.lexer.line_num; + let col_num = parser.lexer.col_num; - parser.eof() + // rough overlap with errors 8.14.1.3 k) & l) of the ISO standard here + if !(line_num == prior_num_lines_read && col_num == 0) { + return CompilationError::from(ParserError::IncompleteReduction(line_num, col_num)); + } } + CompilationError::from(err) +} + + +impl MachineState { pub(crate) fn read( &mut self, mut inner: Stream, @@ -45,11 +65,12 @@ impl MachineState { let (term, num_lines_read) = { let prior_num_lines_read = inner.lines_read(); let mut parser = Parser::new(inner, self); + let op_dir = CompositeOpDir::new(op_dir, None); parser.add_lines_read(prior_num_lines_read); - let term = parser.read_term(&CompositeOpDir::new(op_dir, None)) - .map_err(CompilationError::from)?; + let term = parser.read_term(&op_dir, Tokens::Default) + .map_err(|err| error_after_read_term(err, prior_num_lines_read, &parser))?; // CompilationError::from (term, parser.lines_read() - prior_num_lines_read) }; @@ -60,7 +81,6 @@ impl MachineState { } static mut PROMPT: bool = false; - const HISTORY_FILE: &'static str = ".scryer_history"; pub(crate) fn set_prompt(value: bool) { @@ -82,18 +102,21 @@ fn get_prompt() -> &'static str { #[derive(Debug)] pub struct ReadlineStream { - rl: Editor, - pending_input: Cursor, + rl: Editor, + pending_input: CharReader>, add_history: bool, } impl ReadlineStream { #[inline] pub fn new(pending_input: &str, add_history: bool) -> Self { - let config = Config::builder().check_cursor_position(true).build(); + let config = Config::builder() + .check_cursor_position(true) + .build(); + let helper = Helper::new(); - let mut rl = Editor::with_config(config); + let mut rl = Editor::with_config(config).unwrap(); rl.set_helper(Some(helper)); if let Some(mut path) = dirs_next::home_dir() { @@ -103,11 +126,9 @@ impl ReadlineStream { } } - // rl.bind_sequence(KeyEvent::from('\t'), Cmd::Insert(1, "\t".to_string())); - ReadlineStream { rl, - pending_input: Cursor::new(pending_input.to_owned()), + pending_input: CharReader::new(Cursor::new(pending_input.to_owned())), add_history: add_history, } } @@ -119,31 +140,37 @@ impl ReadlineStream { #[inline] pub fn reset(&mut self) { - self.pending_input.get_mut().clear(); - self.pending_input.set_position(0); + self.pending_input.reset_buffer(); + + let pending_input = self.pending_input.get_mut(); + + pending_input.get_mut().clear(); + pending_input.set_position(0); } fn call_readline(&mut self) -> std::io::Result { match self.rl.readline(get_prompt()) { Ok(text) => { - *self.pending_input.get_mut() = text; - self.pending_input.set_position(0); + self.pending_input.reset_buffer(); + + *self.pending_input.get_mut().get_mut() = text; + self.pending_input.get_mut().set_position(0); unsafe { if PROMPT { - self.rl.history_mut().add(self.pending_input.get_ref()); + self.rl.add_history_entry(self.pending_input.get_ref().get_ref()).unwrap(); self.save_history(); PROMPT = false; } + + if self.pending_input.get_ref().get_ref().chars().last() != Some('\n') { + *self.pending_input.get_mut().get_mut() += "\n"; + } } - if self.pending_input.get_ref().chars().last() != Some('\n') { - *self.pending_input.get_mut() += "\n"; - } - - Ok(self.pending_input.get_ref().len()) + Ok(self.pending_input.get_ref().get_ref().len()) } - Err(ReadlineError::Eof) => Ok(0), + Err(ReadlineError::Eof) => Err(Error::from(ErrorKind::UnexpectedEof)), Err(e) => Err(Error::new(ErrorKind::InvalidInput, e)), } } @@ -164,12 +191,13 @@ impl ReadlineStream { } } + #[inline] pub(crate) fn peek_byte(&mut self) -> std::io::Result { + let bytes = self.pending_input.refresh_buffer()?; + let byte = bytes.iter().next().cloned(); + loop { - match self.pending_input.get_ref().bytes().next() { - Some(0) => { - return Ok(0); - } + match byte { Some(b) => { return Ok(b); } @@ -177,10 +205,6 @@ impl ReadlineStream { Err(e) => { return Err(e); } - Ok(0) => { - self.pending_input.get_mut().push('\u{0}'); - return Ok(0); - } _ => { set_prompt(false); } @@ -203,26 +227,18 @@ impl Read for ReadlineStream { } impl CharRead for ReadlineStream { + #[inline] fn peek_char(&mut self) -> Option> { loop { - let pos = self.pending_input.position() as usize; - - match self.pending_input.get_ref()[pos ..].chars().next() { - Some('\u{0}') => { - return Some(Ok('\u{0}')); - } - Some(c) => { + match self.pending_input.peek_char() { + Some(Ok(c)) => { return Some(Ok(c)); } - None => { + _ => { match self.call_readline() { Err(e) => { return Some(Err(e)); } - Ok(0) => { - self.pending_input.get_mut().push('\u{0}'); - return Some(Ok('\u{0}')); - } _ => { set_prompt(false); } @@ -232,21 +248,21 @@ impl CharRead for ReadlineStream { } } + #[inline] fn consume(&mut self, nread: usize) { - let offset = self.pending_input.position() as usize; - self.pending_input.set_position((offset + nread) as u64); + self.pending_input.consume(nread); } + #[inline] fn put_back_char(&mut self, c: char) { - let offset = self.pending_input.position() as usize; - self.pending_input.set_position((offset - c.len_utf8()) as u64); + self.pending_input.put_back_char(c); } } #[inline] -pub(crate) fn write_term_to_heap( - term: &Term, - heap: &mut Heap, +pub(crate) fn write_term_to_heap<'a, 'b>( + term: &'a Term, + heap: &'b mut Heap, atom_tbl: &mut AtomTable, ) -> Result { let term_writer = TermWriter::new(heap, atom_tbl); @@ -279,7 +295,7 @@ impl<'a, 'b> TermWriter<'a, 'b> { } #[inline] - fn modify_head_of_queue(&mut self, term: &TermRef<'a>, h: usize) { + fn modify_head_of_queue(&mut self, term: &TermRef, h: usize) { if let Some((arity, site_h)) = self.queue.pop_front() { self.heap[site_h] = self.term_as_addr(term, h); @@ -295,7 +311,7 @@ impl<'a, 'b> TermWriter<'a, 'b> { self.heap.push(heap_loc_as_cell!(h)); } - fn term_as_addr(&mut self, term: &TermRef<'a>, h: usize) -> HeapCellValue { + fn term_as_addr(&mut self, term: &TermRef, h: usize) -> HeapCellValue { match term { &TermRef::Cons(..) => list_loc_as_cell!(h), &TermRef::AnonVar(_) | &TermRef::Var(..) => heap_loc_as_cell!(h), @@ -314,10 +330,10 @@ impl<'a, 'b> TermWriter<'a, 'b> { } } - fn write_term_to_heap(mut self, term: &'a Term) -> Result { + fn write_term_to_heap(mut self, term: &Term) -> Result { let heap_loc = self.heap.len(); - for term in breadth_first_iter(term, true) { + for term in breadth_first_iter(term, RootIterationPolicy::Iterated) { let h = self.heap.len(); match &term { @@ -368,17 +384,19 @@ impl<'a, 'b> TermWriter<'a, 'b> { self.push_stub_addr(); } } - &TermRef::AnonVar(Level::Root) | &TermRef::Literal(Level::Root, ..) => { + &TermRef::AnonVar(Level::Root) | TermRef::Literal(Level::Root, ..) => { let addr = self.term_as_addr(&term, h); self.heap.push(addr); } - &TermRef::Var(Level::Root, _, ref var) => { + &TermRef::Var(Level::Root, _, ref var_ptr) => { let addr = self.term_as_addr(&term, h); - self.var_dict.insert(var.clone(), heap_loc_as_cell!(h)); + self.var_dict.insert(VarKey::VarPtr(var_ptr.clone()), addr); self.heap.push(addr); } &TermRef::AnonVar(_) => { if let Some((arity, site_h)) = self.queue.pop_front() { + self.var_dict.insert(VarKey::AnonVar(h), heap_loc_as_cell!(site_h)); + if arity > 1 { self.queue.push_front((arity - 1, site_h + 1)); } @@ -405,12 +423,14 @@ impl<'a, 'b> TermWriter<'a, 'b> { continue; } } - &TermRef::Var(_, _, ref var) => { + &TermRef::Var(.., ref var) => { if let Some((arity, site_h)) = self.queue.pop_front() { - if let Some(addr) = self.var_dict.get(var).cloned() { + let var_key = VarKey::VarPtr(var.clone()); + + if let Some(addr) = self.var_dict.get(&var_key).cloned() { self.heap[site_h] = addr; } else { - self.var_dict.insert(var.clone(), heap_loc_as_cell!(site_h)); + self.var_dict.insert(var_key, heap_loc_as_cell!(site_h)); } if arity > 1 { diff --git a/src/targets.rs b/src/targets.rs index cbb469f9..1596aae2 100644 --- a/src/targets.rs +++ b/src/targets.rs @@ -16,7 +16,7 @@ pub(crate) trait CompilationTarget<'a> { fn to_constant(lvl: Level, literal: Literal, r: RegType) -> Instruction; fn to_list(lvl: Level, r: RegType) -> Instruction; - fn to_structure(name: Atom, arity: usize, r: RegType) -> Instruction; + fn to_structure(lvl: Level, name: Atom, arity: usize, r: RegType) -> Instruction; fn to_void(num_subterms: usize) -> Instruction; fn is_void_instr(instr: &Instruction) -> bool; @@ -29,11 +29,13 @@ pub(crate) trait CompilationTarget<'a> { fn argument_to_variable(r: RegType, r: usize) -> Instruction; fn argument_to_value(r: RegType, val: usize) -> Instruction; + fn unsafe_argument_to_value(r: RegType, val: usize) -> Instruction; fn move_to_register(r: RegType, val: usize) -> Instruction; fn subterm_to_variable(r: RegType) -> Instruction; fn subterm_to_value(r: RegType) -> Instruction; + fn unsafe_subterm_to_value(r: RegType) -> Instruction; fn clause_arg_to_instr(r: RegType) -> Instruction; } @@ -42,15 +44,15 @@ impl<'a> CompilationTarget<'a> for FactInstruction { type Iterator = FactIterator<'a>; fn iter(term: &'a Term) -> Self::Iterator { - breadth_first_iter(term, false) // do not iterate over the root clause if one exists. + breadth_first_iter(term, RootIterationPolicy::NotIterated) } fn to_constant(lvl: Level, constant: Literal, reg: RegType) -> Instruction { Instruction::GetConstant(lvl, HeapCellValue::from(constant), reg) } - fn to_structure(name: Atom, arity: usize, reg: RegType) -> Instruction { - Instruction::GetStructure(name, arity, reg) + fn to_structure(lvl: Level, name: Atom, arity: usize, reg: RegType) -> Instruction { + Instruction::GetStructure(lvl, name, arity, reg) } fn to_list(lvl: Level, reg: RegType) -> Instruction { @@ -95,6 +97,10 @@ impl<'a> CompilationTarget<'a> for FactInstruction { Instruction::GetValue(arg, val) } + fn unsafe_argument_to_value(arg: RegType, val: usize) -> Instruction { + Instruction::GetValue(arg, val) + } + fn subterm_to_variable(val: RegType) -> Instruction { Instruction::UnifyVariable(val) } @@ -103,6 +109,10 @@ impl<'a> CompilationTarget<'a> for FactInstruction { Instruction::UnifyValue(val) } + fn unsafe_subterm_to_value(val: RegType) -> Instruction { + Instruction::UnifyLocalValue(val) + } + fn clause_arg_to_instr(val: RegType) -> Instruction { Instruction::UnifyVariable(val) } @@ -115,7 +125,7 @@ impl<'a> CompilationTarget<'a> for QueryInstruction { post_order_iter(term) } - fn to_structure(name: Atom, arity: usize, r: RegType) -> Instruction { + fn to_structure(_lvl: Level, name: Atom, arity: usize, r: RegType) -> Instruction { Instruction::PutStructure(name, arity, r) } @@ -165,6 +175,13 @@ impl<'a> CompilationTarget<'a> for QueryInstruction { Instruction::PutValue(arg, val) } + fn unsafe_argument_to_value(arg: RegType, val: usize) -> Instruction { + match arg { + RegType::Perm(p) => Instruction::PutUnsafeValue(p, val), + RegType::Temp(_) => Instruction::PutValue(arg, val), + } + } + fn subterm_to_variable(val: RegType) -> Instruction { Instruction::SetVariable(val) } @@ -173,6 +190,10 @@ impl<'a> CompilationTarget<'a> for QueryInstruction { Instruction::SetValue(val) } + fn unsafe_subterm_to_value(val: RegType) -> Instruction { + Instruction::SetLocalValue(val) + } + fn clause_arg_to_instr(val: RegType) -> Instruction { Instruction::SetValue(val) } diff --git a/src/toplevel.pl b/src/toplevel.pl index f80adc1e..70b91ccb 100644 --- a/src/toplevel.pl +++ b/src/toplevel.pl @@ -1,6 +1,7 @@ :- module('$toplevel', [argv/1, copy_term/3]). +:- use_module(library(atts), [call_residue_vars/2]). :- use_module(library(charsio)). :- use_module(library(error)). :- use_module(library(files)). @@ -82,7 +83,7 @@ print_help :- print_version :- '$scryer_prolog_version'(Version), - write(Version), nl, + maplist(put_char, Version), nl, halt. gather_goal(Type, Args0, Goals) :- @@ -114,18 +115,27 @@ layout_and_dot([C|Cs]) :- layout_and_dot(Cs). run_goals([]). -run_goals([g(Gs0)|Goals]) :- +run_goals([g(Gs0)|Goals]) :- !, ( ends_with_dot(Gs0) -> Gs1 = Gs0 ; append(Gs0, ".", Gs1) ), - read_from_chars(Gs1, Goal), - ( catch( - user:Goal, - Exception, - (write(Goal), write(' causes: '), write(Exception), nl) % halt? - ) - ; write('Warning: initialization failed for '), - write(Gs0), nl + double_quotes_option(DQ), + catch(read_term_from_chars(Gs1, Goal, [variable_names(VNs)]), + E, + ( write_term(Gs0, [double_quotes(DQ)]), + write(' cannot be read: '), write(E), nl, + halt + ) + ), + ( catch(user:Goal, + Exception, + ( write_term(Goal, [variable_names(VNs),double_quotes(DQ)]), + write(' causes: '), + write_term(Exception, [double_quotes(DQ)]), nl % halt? + ) + ) -> true + ; write('Warning: initialization failed for: '), + write_term(Goal, [variable_names(VNs),double_quotes(DQ)]), nl ), run_goals(Goals). run_goals([Goal|_]) :- @@ -180,8 +190,9 @@ submit_query_and_print_results_(Term, VarList) :- '$get_b_value'(B), bb_put('$report_all', false), bb_put('$report_n_more', 0), - call(user:Term), - write_eqs_and_read_input(B, VarList), + expand_goal(Term, user, Term0), + atts:call_residue_vars(user:Term0, AttrVars), + write_eqs_and_read_input(B, VarList, AttrVars), !. submit_query_and_print_results_(_, _) :- ( bb_get('$answer_count', 0) -> @@ -203,22 +214,29 @@ submit_query_and_print_results(Term, VarList) :- needs_bracketing(Value, Op) :- - catch((functor(Value, F, _), - current_op(EqPrec, EqSpec, Op), - current_op(FPrec, _, F)), - _, - false), - ( EqPrec < FPrec -> - true - ; FPrec > 0, F == Value, graphic_token_char(F) -> - true - ; F \== '.', '$quoted_token'(F) -> - true - ; EqPrec == FPrec, - memberchk(EqSpec, [fx,xfx,yfx]) + nonvar(Value), + functor(Value, F, Arity), + atom(F), + current_op(FPrec, FSpec, F), + current_op(EqPrec, EqSpec, Op), + arity_specifier(Arity, FSpec), + ( Arity =:= 0 + ; EqPrec < FPrec + ; EqPrec =:= FPrec, + member(EqSpec, [fx,xfx,yfx]) + ). + +arity_specifier(0, _). +arity_specifier(1, S) :- atom_chars(S, [_,_]). +arity_specifier(2, S) :- atom_chars(S, [_,_,_]). + +double_quotes_option(DQ) :- + ( current_prolog_flag(double_quotes, chars) -> DQ = true + ; DQ = false ). write_goal(G, VarList, MaxDepth) :- + double_quotes_option(DQ), ( G = (Var = Value) -> ( var(Value) -> select((Var = _), VarList, NewVarList) @@ -226,18 +244,19 @@ write_goal(G, VarList, MaxDepth) :- ), write(Var), write(' = '), - ( needs_bracketing(Value, (=)) -> + ( needs_bracketing(Value, =) -> write('('), - write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth)]), + write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth), double_quotes(DQ)]), write(')') - ; write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth)]) + ; write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth), double_quotes(DQ)]) ) ; G == [] -> write('true') - ; write_term(G, [quoted(true), variable_names(VarList), max_depth(MaxDepth)]) + ; write_term(G, [quoted(true), variable_names(VarList), max_depth(MaxDepth), double_quotes(DQ)]) ). write_last_goal(G, VarList, MaxDepth) :- + double_quotes_option(DQ), ( G = (Var = Value) -> ( var(Value) -> select((Var = _), VarList, NewVarList) @@ -245,11 +264,11 @@ write_last_goal(G, VarList, MaxDepth) :- ), write(Var), write(' = '), - ( needs_bracketing(Value, (=)) -> + ( needs_bracketing(Value, =) -> write('('), - write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth)]), + write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth), double_quotes(DQ)]), write(')') - ; write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth)]), + ; write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth), double_quotes(DQ)]), ( trailing_period_is_ambiguous(Value) -> write(' ') ; true @@ -257,7 +276,7 @@ write_last_goal(G, VarList, MaxDepth) :- ) ; G == [] -> write('true') - ; write_term(G, [quoted(true), variable_names(VarList), max_depth(MaxDepth)]) + ; write_term(G, [quoted(true), variable_names(VarList), max_depth(MaxDepth), double_quotes(DQ)]) ). write_eq((G1, G2), VarList, MaxDepth) :- @@ -269,8 +288,7 @@ write_eq(G, VarList, MaxDepth) :- write_last_goal(G, VarList, MaxDepth). graphic_token_char(C) :- - memberchk(C, ['#', '$', '&', '*', '+', '-', '.', ('/'), ':', - '<', '=', '>', '?', '@', '^', '~', ('\\')]). + memberchk(C, [#, $, &, *, +, -, ., /, :, <, =, >, ?, @, ^, ~, \]). list_last_item([C], C) :- !. list_last_item([_|Cs], D) :- @@ -286,11 +304,10 @@ trailing_period_is_ambiguous(Value) :- term_variables_under_max_depth(Term, MaxDepth, Vars) :- '$term_variables_under_max_depth'(Term, MaxDepth, Vars). -write_eqs_and_read_input(B, VarList) :- +write_eqs_and_read_input(B, VarList, AttrVars) :- gather_query_vars(VarList, OrigVars), % one layer of depth added for (=/2) functor '$term_variables_under_max_depth'(OrigVars, 22, Vars0), - '$term_attributed_variables'(VarList, AttrVars), '$project_atts':project_attributes(Vars0, AttrVars), copy_term(AttrVars, AttrVars, AttrGoals), term_variables(AttrGoals, AttrGoalVars), diff --git a/src/types.rs b/src/types.rs index 168f0e84..12add4ef 100644 --- a/src/types.rs +++ b/src/types.rs @@ -30,6 +30,7 @@ pub enum HeapCellValueTag { Atom = 0b010111, PStr = 0b011001, CStr = 0b011011, + CutPoint = 0b011111, } #[derive(BitfieldSpecifier, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] @@ -50,14 +51,15 @@ pub enum HeapCellValueView { Atom = 0b010111, PStr = 0b011001, CStr = 0b011011, + CutPoint = 0b011111, // trail elements. - TrailedHeapVar = 0b011101, - TrailedStackVar = 0b011111, - TrailedAttrVarHeapLink = 0b100001, + TrailedHeapVar = 0b101111, + TrailedStackVar = 0b101011, + TrailedAttrVar = 0b100001, TrailedAttrVarListLink = 0b100011, TrailedAttachedValue = 0b100101, TrailedBlackboardEntry = 0b100111, - TrailedBlackboardOffset = 0b101001, + TrailedBlackboardOffset = 0b110011, } #[derive(BitfieldSpecifier, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] @@ -182,7 +184,6 @@ impl Ref { #[derive(Debug, Clone, Copy)] pub enum TrailRef { Ref(Ref), - AttrVarHeapLink(usize), AttrVarListLink(usize, usize), BlackboardEntry(Atom), BlackboardOffset(Atom, HeapCellValue), // key atom, key value @@ -191,14 +192,13 @@ pub enum TrailRef { #[derive(BitfieldSpecifier, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] #[bits = 6] pub(crate) enum TrailEntryTag { - TrailedHeapVar = 0b011110, - TrailedStackVar = 0b011111, - TrailedAttrVar = 0b101110, - TrailedAttrVarHeapLink = 0b100010, - TrailedAttrVarListLink = 0b100011, - TrailedAttachedValue = 0b101010, - TrailedBlackboardEntry = 0b100110, - TrailedBlackboardOffset = 0b100111, + TrailedHeapVar = 0b101111, + TrailedStackVar = 0b101011, + TrailedAttrVar = 0b100001, + TrailedAttrVarListLink = 0b100011, + TrailedAttachedValue = 0b100101, + TrailedBlackboardEntry = 0b100111, + TrailedBlackboardOffset = 0b110011, } #[bitfield] diff --git a/src/variable_records.rs b/src/variable_records.rs new file mode 100644 index 00000000..2d19ec08 --- /dev/null +++ b/src/variable_records.rs @@ -0,0 +1,237 @@ +use crate::parser::ast::*; + +use bit_set::*; +use fxhash::FxBuildHasher; +use indexmap::{IndexMap, IndexSet}; +use std::ops::{Deref, DerefMut}; + +#[derive(Debug, Clone)] +pub struct TempVarData { + pub(crate) use_set: IndexSet<(GenContext, usize), FxBuildHasher>, + pub(crate) no_use_set: BitSet, + pub(crate) conflict_set: BitSet, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct BranchDesignator { + pub branch_stack_num: usize, + pub branch_num: usize, +} + +impl BranchDesignator { + #[inline] + pub fn is_sub_branch(&self) -> bool { + self.branch_stack_num > 0 + } +} + +#[derive(Debug, Clone, Copy)] +pub enum VarSafetyStatus { + Needed, + // which branch planted the last unsafe guarded instruction? It may still be needed. + LocallyUnneeded(BranchDesignator), + GloballyUnneeded, +} + +impl VarSafetyStatus { + pub(crate) fn unneeded(current_branch: BranchDesignator) -> Self { + if current_branch.is_sub_branch() { + VarSafetyStatus::LocallyUnneeded(current_branch) + } else { + VarSafetyStatus::GloballyUnneeded + } + } + + #[inline] + pub(crate) fn needed_if(needed: bool, branch_designator: BranchDesignator) -> Self { + if needed { + VarSafetyStatus::Needed + } else if branch_designator.branch_stack_num == 0 { + VarSafetyStatus::GloballyUnneeded + } else { + VarSafetyStatus::LocallyUnneeded(branch_designator) + } + } +} + +#[derive(Debug, Clone, Copy)] +pub enum PermVarAllocation { + Done { shallow_safety: VarSafetyStatus, + deep_safety: VarSafetyStatus }, + Pending, +} + +impl PermVarAllocation { + #[inline] + pub(crate) fn done() -> Self { + PermVarAllocation::Done { + shallow_safety: VarSafetyStatus::Needed, + deep_safety: VarSafetyStatus::Needed, + } + } + + #[inline] + pub(crate) fn pending(&self) -> bool { + match self { + &PermVarAllocation::Pending => true, + _ => false, + } + } +} + +#[derive(Debug, Clone)] +pub enum VarAlloc { + Temp { term_loc: GenContext, + temp_reg: usize, + temp_var_data: TempVarData, + safety: VarSafetyStatus, + to_perm_var_num: Option }, + Perm(usize, PermVarAllocation), // stack offset, allocation info +} + +impl VarAlloc { + #[inline] + pub(crate) fn as_reg_type(&self) -> RegType { + match self { + &VarAlloc::Temp { temp_reg, .. } => RegType::Temp(temp_reg), + &VarAlloc::Perm(r, _) => RegType::Perm(r), + } + } + + #[inline] + pub(crate) fn set_register(&mut self, reg_num: usize) { + match self { + VarAlloc::Perm(ref mut p, _) => *p = reg_num, + VarAlloc::Temp { ref mut temp_reg, .. } => *temp_reg = reg_num, + }; + } +} + +impl TempVarData { + pub(crate) fn new() -> Self { + TempVarData { + use_set: IndexSet::with_hasher(FxBuildHasher::default()), + no_use_set: BitSet::default(), + conflict_set: BitSet::default(), + } + } + + pub(crate) fn uses_reg(&self, reg: usize) -> bool { + for &(_, nreg) in self.use_set.iter() { + if reg == nreg { + return true; + } + } + + return false; + } + + pub(crate) fn populate_conflict_set(&mut self) { + let arity = self.use_set.len(); + let mut conflict_set: BitSet = (1..arity).collect(); + + for &(_, idx) in &self.use_set { + conflict_set.remove(idx); + } + + self.conflict_set = conflict_set; + } +} + +#[derive(Debug, Clone)] +pub struct VariableRecord { + pub allocation: VarAlloc, + pub num_occurrences: usize, + pub running_count: usize, +} + +impl Default for VariableRecord { + fn default() -> Self { + VariableRecord { + allocation: VarAlloc::Perm(0, PermVarAllocation::Pending), + num_occurrences: 0, + running_count: 0, + } + } +} + +#[derive(Clone, Debug, Default)] +pub struct VariableRecords(Vec); + +impl Deref for VariableRecords { + type Target = Vec; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for VariableRecords { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl VariableRecords { + #[inline] + pub(crate) fn new(num_records: usize) -> Self { + Self(vec![VariableRecord::default(); num_records]) + } + + // computes no_use and conflict sets for all temp vars. + pub(crate) fn populate_restricting_sets(&mut self) { + // three stages: + // 1. move the use sets of each variable to a local IndexMap, use_set + // (iterate mutably, swap mutable refs). + // 2. drain use_set. For each use set of U, add into the + // no-use sets of appropriate variables T =/= U. + // 3. Move the use sets back to their original locations in the fixture. + // Compute the conflict set of u. + + // 1. + let mut use_sets: IndexMap> = IndexMap::new(); + + for (var_gen_index, record) in self.0.iter_mut().enumerate() { + match &mut record.allocation { + VarAlloc::Temp { temp_var_data, .. } => { + let use_set = std::mem::replace( + &mut temp_var_data.use_set, + IndexSet::with_hasher(FxBuildHasher::default()), + ); + + use_sets.insert(var_gen_index, use_set); + } + _ => { + } + } + } + + for (u, use_set) in use_sets.drain(..) { + // 2. + for &(term_loc, reg) in &use_set { + if let GenContext::Last(cn_u) = term_loc { + for (var_gen_index, record) in self.0.iter_mut().enumerate() { + match &mut record.allocation { + VarAlloc::Temp { term_loc, temp_var_data, .. } => { + if cn_u == term_loc.chunk_num() && u != var_gen_index { + if !temp_var_data.uses_reg(reg) { + temp_var_data.no_use_set.insert(reg); + } + } + } + _ => {} + } + } + } + } + + // 3. + if let VarAlloc::Temp{ temp_var_data, .. } = &mut self[u].allocation { + temp_var_data.use_set = use_set; + temp_var_data.populate_conflict_set(); + } + } + } +} diff --git a/tests-pl/iso-conformity-tests.pl b/tests-pl/iso-conformity-tests.pl new file mode 100644 index 00000000..94185a07 --- /dev/null +++ b/tests-pl/iso-conformity-tests.pl @@ -0,0 +1,1026 @@ +:- module(iso_conformity_tests, []). + +:- use_module(library(charsio)). +:- use_module(library(dcgs)). +:- use_module(library(files)). +:- use_module(library(format)). +:- use_module(library(iso_ext)). + +writeq_term_to_chars(Term, Chars) :- + Options = [ignore_ops(false), numbervars(true), quoted(true), variable_names([])], + write_term_to_chars(Term, Options, Chars). + +write_term_to_chars(Term, Chars) :- + Options = [ignore_ops(false), numbervars(false), quoted(false), variable_names([])], + write_term_to_chars(Term, Options, Chars). + +write_canonical_term_to_chars(Term, Chars) :- + Options = [ignore_ops(true), numbervars(false), quoted(true), variable_names([])], + write_term_to_chars(Term, Options, Chars). + +test_syntax_error(ReadString, Error) :- + catch((once(read_from_chars(ReadString, _)), + false), + error(Error, _), + true). + +test_1 :- write_term_to_chars('\n', Chars), + Chars = "\n". + +test_2 :- test_syntax_error("'\n", syntax_error(_)). + +test_3 :- test_syntax_error(")\n", syntax_error(incomplete_reduction)). + +test_261 :- test_syntax_error(")\n'\n", syntax_error(invalid_single_quoted_character)). + +test_4 :- test_syntax_error(".\n", syntax_error(incomplete_reduction)). + +test_177 :- test_syntax_error("0'\t=0' .", syntax_error(unexpected_char)). + +test_6 :- test_syntax_error("writeq('\n').", syntax_error(invalid_single_quoted_character)). + +test_7 :- read_from_chars("writeq('\\\n').", T), + T == writeq(''). + +test_8 :- read_from_chars("writeq('\\\na').", T), + T == writeq(a). + +test_9 :- read_from_chars("writeq('a\\\nb').", T), + T == writeq(ab). + +test_10 :- read_from_chars("writeq('a\\\n b').", T), + T == writeq('a b'). + +test_11 :- test_syntax_error("writeq('\\ ').", syntax_error(invalid_single_quoted_character)). + +test_193 :- test_syntax_error("writeq('\\ \n').", syntax_error(invalid_single_quoted_character)). + +test_12 :- test_syntax_error("writeq('\\\t').", syntax_error(invalid_single_quoted_character)). + +test_13 :- read_from_chars("writeq('\\t').", T), + T == writeq('\t'). + +test_14 :- read_from_chars("writeq('\\a').", T), + T == writeq('\a'). + +test_15 :- read_from_chars("writeq('\\7\\').", T), + T == writeq('\a'). + +test_16 :- test_syntax_error("writeq('\\ca').", syntax_error(invalid_single_quoted_character)). + +test_241 :- test_syntax_error("writeq('\\d').", syntax_error(invalid_single_quoted_character)). + +test_17 :- test_syntax_error("writeq('\\e').", syntax_error(invalid_single_quoted_character)). + +test_18 :- read_from_chars("writeq('\\033\\').", T), + T = writeq('\x1b\'). + +test_301 :- read_from_chars("writeq('\\0\\').", T), + T = writeq('\x0\'). + +test_19 :- test_syntax_error("char_code('\\e', C).", syntax_error(invalid_single_quoted_character)). + +test_21 :- test_syntax_error("char_code('\\d', C).", syntax_error(invalid_single_quoted_character)). + +test_22 :- test_syntax_error("writeq('\\u1').", syntax_error(invalid_single_quoted_character)). + +test_23 :- test_syntax_error("X = 0'\\u1.", syntax_error(unexpected_char)). + +test_24 :- test_syntax_error("writeq('\n", syntax_error(invalid_single_quoted_character)). + +test_25 :- test_syntax_error("writeq(.", syntax_error(incomplete_reduction)). + +test_26 :- test_syntax_error("'\\\n''.\n", syntax_error(invalid_single_quoted_character)). + +test_210 :- test_syntax_error("X = 0'\\.", syntax_error(unexpected_char)). + +test_211 :- test_syntax_error("X = 0'\\. .", syntax_error(unexpected_char)). + +test_222 :- writeq_term_to_chars((-)-(-), T), + T == "(-)-(-)". + +test_223 :- writeq_term_to_chars(((:-):-(:-)), T), + T == "(:-):-(:-)". + +test_27 :- writeq_term_to_chars((*)=(*), T), + T == "(*)=(*)". + +test_28 :- writeq_term_to_chars([:-,-], T), + T == "[:-,-]". + +test_29 :- writeq_term_to_chars(f(*), T), + T == "f(*)". + +test_30 :- writeq_term_to_chars(a*(b+c), T), + T == "a*(b+c)". + +test_31 :- writeq_term_to_chars(f(;,'|',';;'), T), + T == "f(;,'|',';;')". + +test_32 :- read_from_chars("[.,.(.,.,.)].", T), + writeq_term_to_chars(T, Chars), + Chars == "['.','.'('.','.','.')]". + +test_33 :- writeq_term_to_chars((a :- b,c), Chars), + Chars == "a:-b,c". + +test_34 :- write_canonical_term_to_chars([a], T), + T == "'.'(a,[])". + +test_35 :- writeq_term_to_chars('/*', Chars), + Chars == "'/*'". + +test_203 :- writeq_term_to_chars(//*, Chars), + Chars == "//*". + +test_282 :- writeq_term_to_chars(//*.*/, Chars), + Chars == "//*.*/". + +test_36 :- writeq_term_to_chars('/**', Chars), + Chars == "'/**'". + +test_37 :- writeq_term_to_chars('*/', Chars), + Chars == "*/". + +test_38 :- "\'\`\"" = "'`""". + +test_179 :- "\'\"" = "'""". + +test_178 :- "\`" = "`". + +test_39 :- '\'\`\"' = '''`"'. + +test_40 :- writeq_term_to_chars('\'\`\"\"', T), + T == "'\\'`\"\"'". + +test_41 :- ('\\') = (\). + +test_42 :- setup_call_cleanup(op(1,xf,xf1), + ( read_from_chars("1xf1 = xf1(1).", T), + call(T) + ), + op(0,xf,xf1)). + +test_43 :- test_syntax_error("X = 0X1.", syntax_error(incomplete_reduction)). + +test_44 :- test_syntax_error("float(.0).", syntax_error(incomplete_reduction)). + +test_45 :- setup_call_cleanup(op(100,xfx,.), + ( read_from_chars("functor(3 .2,F,A).", T), + call(T), + T == functor('.'(3,2),'.',2) + ), + op(0,xfx,.)). + +test_46 :- test_syntax_error("float(- .0).", syntax_error(incomplete_reduction)). + +test_47 :- test_syntax_error("float(1E9).", syntax_error(incomplete_reduction)). + +test_48 :- test_syntax_error("integer(1e).", syntax_error(incomplete_reduction)). + +test_49 :- setup_call_cleanup(op(9,xf,e9), + ( read_from_chars("1e9 = e9(1).", T), + call(T) + ), + op(0,xf,e9)). + +test_50_51_204_220 :- + setup_call_cleanup(op(9,xf,e), + ( read_from_chars("1e-9 = -(e(1),9).", T0), + call(T0), + read_from_chars("1.0e- 9 = -(e(1.0),9).", T1), + call(T1), + read_from_chars("1e.", T2), + writeq_term_to_chars(T2, T3), + T3 == "1 e", + read_from_chars("1.0e.", T4), + writeq_term_to_chars(T4, T5), + T5 == "1.0 e" + ), + op(0,xf,e)). + +test_52 :- setup_call_cleanup(op(9,xfy,e), + ( read_from_chars("1.2e 3 = e(X,Y).", T0), + call(T0) + ), + op(0,xfy,e)). + +test_53 :- writeq_term_to_chars(1.0e100, Chars), + Chars == "1.0e100". + +test_54 :- test_syntax_error("float(1.0ee9).", syntax_error(incomplete_reduction)). + +test_286 :- (- (1)) = -(1). + +test_287 :- (- -1) = -(-1). + +test_288 :- (- 1^2) = ^(-1,2). + +test_56 :- integer(- 1). + +test_57 :- integer('-'1). + +test_58 :- integer('-' 1). + +test_59 :- integer(- /*.*/1). + +test_60 :- test_syntax_error("integer(-/*.*/1).", syntax_error(incomplete_reduction)). + +test_61 :- integer('-'/*.*/1). + +test_62 :- atom(-/*.*/-). + +test_63_180_64 :- setup_call_cleanup(( current_op(P,fy,-), + op(0,fy,-) + ), + ( integer(-1), + integer(- 1) + ), + op(P,fy,-)). + +test_135 :- writeq_term_to_chars(-(1), Chars), + Chars == "- (1)". + +test_136 :- setup_call_cleanup(( current_op(P,fy,-), + op(0,fy,-) + ), + ( writeq_term_to_chars(-(1), Chars), + Chars == "-(1)" + ), + op(P,fy,-)). + +test_182 :- writeq_term_to_chars(-(-1), Chars), + Chars == "- -1". + +test_183 :- writeq_term_to_chars(-(1^2), Chars), + Chars == "- (1^2)". + +test_260 :- writeq_term_to_chars(-(a^2), Chars), + Chars == "- (a^2)". + +test_139 :- writeq_term_to_chars(-((a,b)), Chars), + Chars == "- (a,b)". + +test_218 :- writeq_term_to_chars(-(1*2), Chars), + Chars == "- (1*2)". + +test_140 :- writeq_term_to_chars(-a, Chars), + Chars == "-a". + +test_184 :- writeq_term_to_chars(-(-), Chars), + Chars == "- (-)". + +test_185 :- writeq_term_to_chars(-[-], Chars), + Chars == "-[-]". + +test_188 :- writeq_term_to_chars(-p(c), Chars), + Chars == "-p(c)". + +test_189 :- writeq_term_to_chars(-{}, Chars), + Chars == "-{}". + +test_190 :- writeq_term_to_chars(-{a}, Chars), + Chars == "-{a}". + +test_191 :- writeq_term_to_chars(-(-a), Chars), + Chars == "- -a". + +test_192 :- writeq_term_to_chars(-(-(-a)), Chars), + Chars == "- - -a". + +test_216 :- writeq_term_to_chars(-(-(1)), Chars), + Chars == "- - (1)". + +test_215_248_249 :- + setup_call_cleanup(op(100,yfx,~), + ( read_from_chars("-(1~2~3).", T0), + writeq_term_to_chars(T0, Chars0), + Chars0 == "- (1~2~3)", + read_from_chars("- (1~2).", T1), + writeq_term_to_chars(T1, Chars1), + Chars1 == "- (1~2)", + read_from_chars("1~2.", T2), + writeq_term_to_chars(T2, Chars2), + Chars2 == "1~2" + ), + op(0,yfx,~)). + +test_278 :- setup_call_cleanup(op(9,xfy,.), + ( writeq_term_to_chars(-[1], Chars), + Chars == "-[1]" + ), + op(0,xfy,.)). + +test_279_296 :- + setup_call_cleanup(op(9,xf,'$VAR'), + ( writeq_term_to_chars(-'$VAR'(0), Chars0), + Chars0 == "-A", + writeq_term_to_chars('$VAR'(0), Chars1), + Chars1 == "A" + ), + op(0,xf,'$VAR')). + +test_55 :- setup_call_cleanup(op(1,yf,yf1), + ( read_from_chars("{-1 yf1}={yf1(X)}.", T), + call(T), + T = (_ = { yf1(-1) }) + ), + op(0,yf,yf1)). + +test_65 :- compound(+1). + +test_66 :- compound(+ 1). + +test_277 :- writeq_term_to_chars(+ 1^2, _). + +test_67 :- setup_call_cleanup(( current_op(P,fy,+), + op(0,fy,+) + ), + compound(+1), + op(P,fy,+)). + +test_257 :- writeq_term_to_chars([+{a},+[]], Chars), + Chars == "[+{a},+[]]". + +test_68 :- [(:-)|(:-)]=[:-|:-]. + +test_69 :- test_syntax_error("X=[a|b,c].", syntax_error(incomplete_reduction)). + +test_70 :- catch((op(1000,xfy,','), + false), + error(permission_error(modify, operator, ','), op/3), + true). + +test_71 :- catch((op(1001,xfy,','), + false), + error(permission_error(modify, operator, ','), op/3), + true). + +test_72 :- catch((op(999,xfy,'|'), + false), + error(permission_error(create, operator, '|'), op/3), + true). + +test_73 :- _ = [a|b]. + +test_285 :- test_syntax_error("X = [(a|b)].", syntax_error(_)). + +test_219 :- [a|[]] = [a]. + +test_74 :- test_syntax_error("X = [a|b|c].", syntax_error(incomplete_reduction)). + +test_75 :- test_syntax_error("var(a:-b).", syntax_error(incomplete_reduction)). + +test_76 :- test_syntax_error(":- = :- .", syntax_error(incomplete_reduction)). + +test_77 :- test_syntax_error("- = - .", syntax_error(incomplete_reduction)). + +test_78 :- test_syntax_error("* = * .", syntax_error(incomplete_reduction)). + +test_79 :- current_op(200,fy,-), !. + +test_80 :- current_op(200,fy,+), !. + +test_81 :- {- - c}={-(-(c))}. + +test_82 :- test_syntax_error("(- -) = -(-). ", syntax_error(incomplete_reduction)). + +test_83 :- test_syntax_error("(- - -) = -(-(-)). ", syntax_error(incomplete_reduction)). + +test_84 :- test_syntax_error("(- - - -) = -(-(-(-))). ", syntax_error(incomplete_reduction)). + +test_85 :- test_syntax_error("{:- :- c} = {:-(:-,c)}.", syntax_error(incomplete_reduction)). + +test_86 :- test_syntax_error("{- = - 1}={(-(=)) - 1}. ", syntax_error(incomplete_reduction)). + +test_87 :- test_syntax_error("write_canonical((- = - 1)). ", syntax_error(incomplete_reduction)). + +test_88 :- test_syntax_error("write_canonical((- = -1)). ", syntax_error(incomplete_reduction)). + +test_89 :- test_syntax_error("write_canonical((-;)). ", syntax_error(incomplete_reduction)). + +test_90 :- test_syntax_error("write_canonical((-;-)). ", syntax_error(incomplete_reduction)). + +test_91 :- test_syntax_error("write_canonical((;-;-)). ", syntax_error(incomplete_reduction)). + +test_92 :- test_syntax_error("[:- -c] = [(:- -c)].", syntax_error(incomplete_reduction)). + +test_93 :- test_syntax_error("writeq([a,b|,]).", syntax_error(incomplete_reduction)). + +test_94 :- test_syntax_error("X = {,}.", syntax_error(incomplete_reduction)). + +test_95 :- {1} = {}(1). + +test_96 :- write_canonical_term_to_chars({1}, Chars), + Chars == "{}(1)". + +test_97 :- '[]'(1) = [ ](X), + X == 1. + +test_98 :- test_syntax_error("X = [] (1).", syntax_error(incomplete_reduction)). + +test_99 :- catch((op(100,yfy,op), + false), + error(domain_error(operator_specifier, yfy), op/3), + true). + +test_100 :- '''' = '\''. + +test_101 :- a = '\141\'. + +test_102 :- test_syntax_error("a = '\\141'.", syntax_error(incomplete_reduction)). + +test_103 :- X = '\141\141', + X == a141. + +test_104 :- test_syntax_error("X = '\\9'.", syntax_error(invalid_single_quoted_character)). + +test_105 :- test_syntax_error("X = '\\N'.", syntax_error(invalid_single_quoted_character)). + +test_106 :- test_syntax_error("X = '\\\\'.", syntax_error(incomplete_reduction)). + +test_107 :- test_syntax_error("X = '\\77777777777\\'.", syntax_error(cannot_parse_big_int)). + +test_108 :- a = '\x61\'. + +test_109 :- test_syntax_error("atom_codes('\\xG\\',Cs).", syntax_error(incomplete_reduction)). + +test_110 :- test_syntax_error("atom_codes('\\xG1\\',Cs).", syntax_error(incomplete_reduction)). + +test_111 :- test_syntax_error("atom(`).", syntax_error(incomplete_reduction)). + +test_112 :- test_syntax_error("atom(`+).", syntax_error(incomplete_reduction)). + +test_297 :- test_syntax_error("atom(`\n`).", syntax_error(missing_quote)). + +test_113 :- test_syntax_error("X =`a`.", syntax_error(back_quoted_string)). + +test_114 :- integer(0'\'). + +test_115 :- integer(0'''). + +test_116 :- 0''' = 0'\'. + +test_117 :- test_syntax_error("integer(0'').", syntax_error(incomplete_reduction)). + +test_195_205_196_197 :- + setup_call_cleanup(op(100,xf,''), + ( read_from_chars("(0 '') = ''(X).", T0), + call(T0), + T0 = (_ = ('')(0)), + read_from_chars("0 ''.", T1), + writeq_term_to_chars(T1, C0), + C0 == "0 ''", + read_from_chars("0''.", T2), + writeq_term_to_chars(T2, C1), + C1 == "0 ''" ), + op(0,xf,'')). + +test_118_119_120 :- + setup_call_cleanup(op(100,xfx,''), + ( read_from_chars("functor(0 ''1, F, A).", T0), + call(T0), + T0 = functor(_, (''), 2), + read_from_chars("functor(0''1, F, A).", T1), + call(T1), + T1 = functor(_, (''), 2) + ), + op(0,xfx,'')). + +test_206_207_209_256 :- + setup_call_cleanup(op(100,xf,f), + ( test_syntax_error("0'f'.", syntax_error(incomplete_reduction)), + read_from_chars("0'f'f'.", T0), + writeq_term_to_chars(T0, C0), + C0 == "102 f", + read_from_chars("0'ff.", T1), + writeq_term_to_chars(T1, C1), + C1 == "102 f", + read_from_chars("0f.", T2), + writeq_term_to_chars(T2, C2), + C2 == "0 f" + ), + op(0,xf,f)). + +test_208 :- setup_call_cleanup(op(100,xf,'f '), + ( read_from_chars("0 'f '.", T0), + writeq_term_to_chars(T0, C0), + C0 == "0 'f '"), + op(0,xf,'f ')). + +test_121 :- test_syntax_error("X = 2'1.", syntax_error(incomplete_reduction)). + +test_122_262 :- + setup_call_cleanup(op(100,xfx,'1 '), + ( read_from_chars("functor(2'1 'y, F, A).", T0), + call(T0), + T0 = functor(_, ('1 '), 2), + read_from_chars("functor(2 '1 'y, F, A).", T1), + call(T1), + T1 = functor(_, ('1 '), 2) + ), + op(0,xfx,'1 ')). + +test_123 :- read_from_chars("X = 0'\\x41\\ .", T), + T = (_ = A), + A == 65. + +test_124 :- X =0'\x41\, + X == 65. + +test_125 :- X =0'\x1\, + X == 1. + +test_127 :- X is 16'mod'2, + X == 0. + +test_128 :- X is 37'mod'2, + X == 1. + +test_129 :- test_syntax_error("X is 0'mod'1.", syntax_error(incomplete_reduction)). + +test_130 :- X is 1'+'1, + X == 2. + +test_212 :- read_from_chars("X is 1'\\\n+'1.", T), + T = (_ is 1+1), + call(T). + +test_213 :- read_from_chars("X is 0'\\\n+'1.", T), + T = (_ is 0+1), + call(T). + +test_259 :- read_from_chars("X = 0'\\\n+'/*'. %*/1.", T), + T = (_ = 0+1), + call(T). + +test_303 :- test_syntax_error("X = 0'\\\na.", syntax_error(incomplete_reduction)). + +test_214 :- test_syntax_error("X is 0'\\", syntax_error(incomplete_reduction)). + +test_126 :- test_syntax_error("X = 0'\\\n.\\", syntax_error(incomplete_reduction)). + +test_131_132_133 :- + setup_call_cleanup(op(100,fx,' op'), + ( read_from_chars("' op' '1 '.", T0), + writeq_term_to_chars(T0, C0), + C0 == "' op' '1 '", + read_from_chars("' op'[].", T1), + writeq_term_to_chars(T1, C1), + C1 == "' op'[]" + ), + op(0, fx, ' op') + ). + +test_134 :- + setup_call_cleanup(op(1,xf,xf1), + test_syntax_error("{- =xf1}.", syntax_error(incomplete_reduction)), + op(0,xf,xf1)). + +test_137 :- writeq_term_to_chars(- (a*b), Chars), + Chars == "- (a*b)". + +test_138 :- writeq_term_to_chars(\ (a*b), Chars), + Chars == "\\ (a*b)". + +test_141 :- \+ current_op(_,xfy,.). + +test_142_143_144_221_258 :- + setup_call_cleanup(op(100,xfy,.), + ( read_from_chars("1 .2.", T0), + writeq_term_to_chars(T0, C0), + C0 == "[1|2]", + read_from_chars("[1].", T1), + writeq_term_to_chars(T1, C1), + C1 == "[1]", + read_from_chars("-[1].", T2), + writeq_term_to_chars(T2, C2), + C2 == "-[1]", + read_from_chars("X = 1.e.", T3), + writeq_term_to_chars(T3, C3), + C3 == "A=[1|e]", + read_from_chars("writeq(ok).%\n1=X.", T4), + T4 = writeq(ok) + ), + op(0,xfy,.)). + +test_145 :- write_canonical_term_to_chars('$VAR'(0), Cs), + Cs == "'$VAR'(0)". + +test_146 :- write_term_to_chars('$VAR'(0), [], Cs), + Cs == "$VAR(0)". + +test_244 :- writeq_term_to_chars('$VAR'(0), Cs), + Cs == "A". + +test_245 :- writeq_term_to_chars('$VAR'(-1), Cs), + Cs == "'$VAR'(-1)". + +test_246 :- writeq_term_to_chars('$VAR'(-2), Cs), + Cs == "'$VAR'(-2)". + +test_247 :- writeq_term_to_chars('$VAR'(x), Cs), + Cs == "'$VAR'(x)". + +test_289 :- writeq_term_to_chars('$VAR'('A'), Cs), + Cs == "'$VAR'('A')". + +test_147_148_149_150 :- + setup_call_cleanup(( op(9,fy,fy), + op(9,yf,yf)), + ( read_from_chars("fy 1 yf.", T0), + write_canonical_term_to_chars(T0, C0), + C0 == "fy(yf(1))", + test_syntax_error("fy yf.", syntax_error(incomplete_reduction)), + read_from_chars("fy(yf(1)).", T1), + writeq_term_to_chars(T1, C1), + C1 == "fy 1 yf", + read_from_chars("yf(fy(1)).", T2), + writeq_term_to_chars(T2, C2), + C2 == "(fy 1)yf" + ), + ( op(0,fy,fy), + op(0,yf,yf))). + +test_151_152_153 :- + setup_call_cleanup(( op(9,fy,fy), + op(9,yfx,yfx)), + ( read_from_chars("fy 1 yfx 2.", T0), + write_canonical_term_to_chars(T0, C0), + C0 == "fy(yfx(1,2))", + read_from_chars("fy(yfx(1,2)).", T1), + writeq_term_to_chars(T1, C1), + C1 == "fy 1 yfx 2", + read_from_chars("yfx(fy(1),2).", T2), + writeq_term_to_chars(T2, C2), + C2 == "(fy 1)yfx 2" + ), + ( op(0,fy,fy), + op(0,yfx,yfx))). + +test_154_155_156 :- + setup_call_cleanup(( op(9,yf,yf), + op(9,xfy,xfy)), + ( read_from_chars("1 xfy 2 yf.", T0), + write_canonical_term_to_chars(T0, C0), + C0 == "xfy(1,yf(2))", + read_from_chars("xfy(1,yf(2)).", T1), + writeq_term_to_chars(T1, C1), + C1 == "1 xfy 2 yf", + read_from_chars("yf(xfy(1,2)).", T2), + writeq_term_to_chars(T2, C2), + C2 == "(1 xfy 2)yf" + ), + ( op(0,yf,yf), + op(0,xfy,xfy)) + ). + +test_157 :- setup_call_cleanup((( current_op(P,xfy,:-) -> + true + ; P = 0 + ), + op(0,xfy,:-) + ), + \+ current_op(_,xfx,:-), + ( op(P,xfy,:-), + op(1200,xfx,:-) ) + ). + +test_158 :- catch((op(0,xfy,','), + false), + error(permission_error(modify, operator, (',')), op/3), + true). + +test_159_201_202_160_161 :- + setup_call_cleanup(( op(9,fy,f), + op(9,yf,f)), + ( read_from_chars("f f 0.", T0), + write_canonical_term_to_chars(T0, C0), + C0 == "f(f(0))", + read_from_chars("f(f(0)).", T1), + writeq_term_to_chars(T1, C1), + C1 == "f f 0", + read_from_chars("f 0 f.", T2), + write_canonical_term_to_chars(T2, C2), + C2 == "f(f(0))", + read_from_chars("0 f f.", T3), + write_canonical_term_to_chars(T3, C3), + C3 == "f(f(0))", + test_syntax_error("f f.", syntax_error(incomplete_reduction)) + ), + ( op(0,fy,f), + op(0,yf,f))). + +test_162 :- setup_call_cleanup((op(9,fy,p),op(9,yfx,p)), + test_syntax_error("1 p p p 2.", syntax_error(incomplete_reduction)), + (op(0,fy,p),op(0,yfx,p))). + +test_163 :- setup_call_cleanup((op(9,fy,p),op(9,xfy,p)), + ( read_from_chars("1 p p p 2.", T), + write_canonical_term_to_chars(T, C), + C == "p(1,p(p(2)))" + ), + (op(0,fy,p),op(0,xfy,p))). + +test_164 :- setup_call_cleanup((op(7,fy,p),op(9,yfx,p)), + ( read_from_chars("1 p p p 2.", T), + write_canonical_term_to_chars(T, C), + C == "p(1,p(p(2)))" + ), + (op(0,fy,p),op(0,yfx,p))). + +test_165 :- atom('.''-''.'). + +test_166_167 :- setup_call_cleanup(( current_op(P,xfy,'|') -> + true + ; P = 0 + ), + ( op(0,xfy,'|'), + test_syntax_error("(a|b).", syntax_error(incomplete_reduction))), + op(P,xfy,'|')). + +test_168_169 :- call_cleanup(( op(0,xfy,.), + op(9,yf,.), + read_from_chars(".(.).", T), + writeq_term_to_chars(T, C), + C == "('.')'.'" ), + op(0,yf,.)). + +test_194 :- op(0,xfy,.), + writeq_term_to_chars((.)+(.), C), + C == "'.'+'.'". + +test_170 :- set_prolog_flag(double_quotes,chars). + +test_171 :- writeq_term_to_chars("a", C), + C == "[a]". + +test_229 :- test_syntax_error("\"\\z.\"", syntax_error(missing_quote)). + +test_300 :- writeq_term_to_chars("\0\", C), + C == "['\\x0\\']". + +test_172 :- X is 10.0** -323, + writeq_term_to_chars(X, C), + C == "1.0e-323". + +test_173 :- 1.0e-323=:=10.0** -323. + +test_174 :- -1 = -0x1. + +test_175 :- T = t(0b1,0o1,0x1), + T = t(1,1,1). + +test_176 :- X is 0b1mod 2, + X == 1. + +test_217_181_290 :- + setup_call_cleanup(( current_op(P, xfy, '|') -> + true + ; P = 0 + ), + ( op(1105,xfy,'|'), + read_from_chars("(a-->b,c|d).", T0), + writeq_term_to_chars(T0, C0), + C0 == "a-->b,c | d", + read_from_chars("[(a|b)].", T1), + writeq_term_to_chars(T1, C1), + C1 == "[(a | b)]" + ), + op(P, xfy, '|')). + +test_186 :- X/* /*/=7, + X == 7. + +test_187 :- X/*/*/=7, + X == 7. + +test_198 :- atom($-). + +test_199 :- atom(-$). + +test_200 :- setup_call_cleanup(op(900, fy, [$]), + ( read_from_chars("$a+b.", T), + write_canonical_term_to_chars(T, C), + C == "$(+(a,b))" + ), + op(0,fy,[$])). + +test_224 :- catch((read_from_chars("\\ .", T), + call(T), + false), + error(existence_error(procedure,(\)/0), _), + true). + +test_225 :- char_code(C,0), + writeq_term_to_chars(C, Cs), + Cs == "'\\x0\\'". + +test_250 :- writeq_term_to_chars('\0\', C), + C == "'\\x0\\'". + +test_226 :- write_canonical_term_to_chars(_+_, Cs), + Cs == "+(A,B)". % note that no variable names are supplied by write_canonical_term_to_chars/2. + +test_227 :- write_canonical_term_to_chars(A+A, Cs), + Cs == "+(A,A)". + +test_228 :- test_syntax_error("writeq(0'\\z).", syntax_error(unexpected_char)). + +test_230 :- test_syntax_error("char_code('\\^',X).", syntax_error(invalid_single_quoted_character)). + +test_231 :- test_syntax_error("writeq(0'\\c).", syntax_error(unexpected_char)). + +test_232 :- test_syntax_error("writeq(0'\\ ).", syntax_error(unexpected_char)). + +test_233 :- test_syntax_error("writeq(nop (1)).", syntax_error(incomplete_reduction)). + +test_234_235 :- setup_call_cleanup(op(400,fx,f), + ( read_from_chars("f/*.*/(1,2).", T), + writeq_term_to_chars(T, C), + C == "f (1,2)", + test_syntax_error("1 = f.", syntax_error(incomplete_reduction)) + ), + op(0,fx,f)). + +test_236 :- write_canonical_term_to_chars(a- - -b, Cs), + Cs == "-(a,-(-(b)))". + +test_237 :- catch((op(699,xf,>), + false), + error(permission_error(create,operator,>),op/3), + true). + +test_238 :- writeq_term_to_chars(>(>(a),b), Cs), + Cs == ">(a)>b". + +test_239 :- test_syntax_error("a> >b.", syntax_error(incomplete_reduction)). + +test_242 :- test_syntax_error("a> =b.", syntax_error(incomplete_reduction)). + +test_243 :- test_syntax_error("a>,b.", syntax_error(incomplete_reduction)). + +test_240 :- test_syntax_error("a>.", syntax_error(incomplete_reduction)). + +test_251_263_252_253_254_255 :- + setup_call_cleanup(op(9,yfx,[bop,bo,b,op,xor]), + ( read_from_chars("0 bop 2.", T0), + writeq_term_to_chars(T0, C0), + C0 == "0 bop 2", + read_from_chars("0bo 2.", T1), + writeq_term_to_chars(T1, C1), + C1 == "0 bo 2", + read_from_chars("0b 2.", T2), + writeq_term_to_chars(T2, C2), + C2 == "0 b 2", + read_from_chars("0op 2.", T3), + writeq_term_to_chars(T3, C3), + C3 == "0 op 2", + read_from_chars("0xor 2.", T4), + writeq_term_to_chars(T4, C4), + C4 == "0 xor 2" + ), + op(0,yfx,[bop,bo,b,op,xor])). + +test_264 :- writeq_term_to_chars('^`', C), + C == "'^`'". + +test_265_266_267 :- + setup_call_cleanup(op(9,yf,[b2,o8]), + ( read_from_chars("0b2.", T0), + writeq_term_to_chars(T0, C0), + C0 == "0 b2", + read_from_chars("0o8.", T1), + writeq_term_to_chars(T1, C1), + C1 == "0 o8" + ), + op(0,yf,[b2,o8])). + +test_268 :- catch((op(500,xfy,{}), + false), + error(permission_error(create, operator, {}), op/3), + true). + +test_269 :- writeq_term_to_chars('\b\r\f\t\n', C), + C == "'\\b\\r\\f\\t\\n'". + +test_270 :- + setup_call_cleanup((open("test_270.txt", write, WriteFile), + format(WriteFile, "get_char(Stream, C). %\n", []), + close(WriteFile), + open("test_270.txt", read, ReadFile)), + (read_term(ReadFile, T, []), + T = get_char(ReadFile, C), + call(T), + C == ' '), + (close(ReadFile), + delete_file("test_270.txt"))). + +test_271 :- + setup_call_cleanup((open("test_271.txt", write, WriteFile), + format(WriteFile, "get_char(Stream, C).%\n", []), + close(WriteFile), + open("test_271.txt", read, ReadFile)), + (read_term(ReadFile, T, []), + T = get_char(ReadFile, C), + call(T), + C == '%'), + (close(ReadFile), + delete_file("test_271.txt"))). + +test_272 :- test_syntax_error("writeq(0B1).", syntax_error(incomplete_reduction)). + +test_274_275 :- + setup_call_cleanup(op(20,fx,--), + ( read_from_chars("--(a).", T0), + writeq_term_to_chars(T0, C0), + C0 == "--a", + op(0,fx,--), + read_from_chars("--(a).", T1), + writeq_term_to_chars(T1, C1), + C1 == "--(a)" + ), + op(0,fx,--)). + +test_276 :- writeq_term_to_chars(0xamod 2, C), + C == "10 mod 2". + +test_280 :- writeq_term_to_chars(00'+'1, C), + C == "0+1". + +test_281 :- test_syntax_error("00'a.", syntax_error(incomplete_reduction)). + +test_284 :- test_syntax_error("'\\^J'.", syntax_error(invalid_single_quoted_character)). + +test_291 :- writeq_term_to_chars([(a,b)], C), + C == "[(a,b)]". + +test_292 :- writeq_term_to_chars(1 = \\, C), + C == "1= \\\\". + +test_293 :- test_syntax_error("writeq((,)).", syntax_error(incomplete_reduction)). + +test_294 :- test_syntax_error("writeq({[}).", syntax_error(incomplete_reduction)). + +test_295 :- test_syntax_error("writeq({(}).", syntax_error(incomplete_reduction)). + +test_298 :- writeq_term_to_chars([a,b|c], C), + C == "[a,b|c]". + +test_299 :- (\+ (a,b)) = \+(T), + T == (a,b). + +test_302 :- [] = '[]'. + +test_304 :- setup_call_cleanup(op(300,fy,~), + ( read_from_chars("~ (a = b).", T), + writeq_term_to_chars(T, C), + C == "~ (a=b)" + ), + op(0,fy,~)). + +test_305 :- writeq_term_to_chars(\ (a = b), C), + C == "\\ (a=b)". + +test_306 :- writeq_term_to_chars(+ (a = b), C), + C == "+ (a=b)". + +test_307 :- writeq_term_to_chars([/**/], C), + C == "[]". + +test_308 :- writeq_term_to_chars(.+, C), + C == ".+". + +test_309 :- writeq_term_to_chars({a,b}, C), + C == "{a,b}". + +test_310 :- test_syntax_error("writeq({\\+ (}).", syntax_error(incomplete_reduction)). + +test_311 :- test_syntax_error("Finis ().", syntax_error(incomplete_reduction)). + +test_318 :- writeq_term_to_chars(+((1*2)^3), C), + C == "+ (1*2)^3". + +run_tests([Test|Tests]) --> + ( { call(Test) } -> + [] + ; { format("~a failed!~n", [Test]) }, + [Test] + ), + run_tests(Tests). +run_tests([]) --> []. + +run_tests :- + findall(Test, + ( current_predicate(iso_conformity_tests:Test/0), + once(sub_atom(Test, 0, 5, _, test_)) + ), + Tests), + phrase(run_tests(Tests), FailedTests), + ( FailedTests == [] -> + write('All tests passed') + ; format("Failed ISO conformity tests: ~w", [FailedTests]), + false + ). + +:- initialization(run_tests). diff --git a/tests/scryer/issues.rs b/tests/scryer/issues.rs index af22252a..1f0e2737 100644 --- a/tests/scryer/issues.rs +++ b/tests/scryer/issues.rs @@ -1,4 +1,5 @@ use crate::helper::{load_module_test, run_top_level_test_no_args, run_top_level_test_with_args}; +use scryer_prolog::machine::Machine; use serial_test::serial; // issue #857 @@ -54,10 +55,10 @@ fn handle_residual_goal() { true.\n \ true.\n \ false.\n \ - X = - X.\n \ - dif:dif(- X,X).\n \ + X = -X.\n \ + dif:dif(-X,X).\n \ false.\n \ - Vars = [X], dif:dif(- X,X).\n \ + Vars = [X], dif:dif(-X,X).\n \ true.\n \ true.\n \ true.\n\ @@ -128,10 +129,12 @@ fn compound_goal() { // issue #815 #[test] fn no_stutter() { - run_top_level_test_no_args("write(a), write(b), false.\n\ + run_top_level_test_no_args( + "write(a), write(b), false.\n\ halt.\n\ ", - "ab false.\n") + "ab false.\n", + ) } /* @@ -168,3 +171,16 @@ fn call_0() { " error(existence_error(procedure,call/0),call/0).\n", ); } + +// issue #1206 +#[serial] +#[test] +#[should_panic(expected = "Overwriting atom table base pointer")] +fn atomtable_is_not_concurrency_safe() { + // this is basically the same test as scryer_prolog::atom_table::atomtable_is_not_concurrency_safe + // but for this integration test scryer_prolog is compiled with cfg!(not(test)) while for the unit test it is compiled with cfg!(test) + // as the atom table implementation differ between cfg!(test) and cfg!(not(test)) both test serve a pourpose + // Note: this integration test itself is compiled with cfg!(test) independent of scryer_prolog itself + let _machine_a = Machine::with_test_streams(); + let _machine_b = Machine::with_test_streams(); +} diff --git a/tests/scryer/src_tests.rs b/tests/scryer/src_tests.rs index c7f7c80a..38043098 100644 --- a/tests/scryer/src_tests.rs +++ b/tests/scryer/src_tests.rs @@ -69,3 +69,12 @@ fn setup_call_cleanup_process() { fn clpz_load() { load_module_test("src/tests/clpz/test_clpz.pl", ""); } + +#[serial] +#[test] +fn iso_conformity_tests() { + load_module_test( + "tests-pl/iso-conformity-tests.pl", + "All tests passed", + ); +} diff --git a/tools/showterm.pl b/tools/showterm.pl index 3193802c..899adb68 100644 --- a/tools/showterm.pl +++ b/tools/showterm.pl @@ -75,7 +75,7 @@ dot(Term) :- dot(Term, []). dot(Term, NVs) :- - phrase(term_labels(Term, NVs, 'c'), Ls), + phrase(term_labels(Term, NVs, c), Ls), phrase(("graph G {\n", dots(Ls), "}\n"), DOT), diff --git a/wambook/errata.txt b/wambook/errata.txt new file mode 100644 index 00000000..43ec62d0 --- /dev/null +++ b/wambook/errata.txt @@ -0,0 +1,169 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +Errata for: + + Warren's Abstract Machine: A Tutorial Reconstruction + Hassan Ait-Kaci + MIT Press, Cambridge, MA + 1991 + + ISBN 0-262-51058-8 (paper) + ISBN 0-262-01123-9 (cloth) + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +I am enclosing below the most up-to-date list of typos, bugs, and their - +easy - fixes... Anything else that you will find, please report back to +me. Who knows, one day when my stack is finally empty (ha!), I'll work on a +second edition with extensions. In the mean time, please accept my +apologies for your painful reading. On the other hand, my book's bugs and +typos are a wonderful indicator of who did or not actually try to implement +the code therein! + +These bug reports and fixes are to be credited to James Anhalt III +(anhalt@cs.ucla.edu), Dan Friedman (dfried@cs.indiana.edu), Michael Levy +(mlevy@csr.uvic.ca), Donald A. Smith (dsmith@chaos.cs.brandeis.edu), and +Neng Fa Zhou (zhou@csce.kyushu-u.ac.jp). Big thanks to all. + +-hak ___________________________________________________________________ + Hassan Ait-Kaci, Professor + ___________________________________________________________________ + School of Computing Science phone: +1 (604) 291 55 89 + Simon Fraser University fax: +1 (604) 291 30 45 + Burnaby, British Columbia email: hak@cs.sfu.ca + V5A 1S6, Canada url: http://www.isg.sfu.ca/~hak/ + ___________________________________________________________________ + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +In the definition of get_structure (fig 2.6, page 13) S should be +initialized to 1 before exiting get_structure in either READ or WRITE +modes. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +There is a problem with stack frames allocation. Namely, the book defines +ALLOCATE as: + + (*) if E > B + then newE <- E + CODE[STACK[E + 1] - 1] + 2 + else ... + +While this should be: + + (**) if E > B + then newE <- E + CODE[CP - 1] + 2 + else ... + +The following code shows why: + + a/0 allocate + ... + call b/0,1 + L1 ... + b/0 allocate + ... + call c/0,3 + L2 ... + c/0 allocate + ... + +Now when b/0 calls c/0 the stack should look like: + + |-------| + |CE: |0 <- a's environment + |-------| + |CP: |1 + |-------| + |Y1: |2 + |-------| +E -> |CE: 0 |3 <- b's environment + |-------| + |CP: L1 |4 + |-------| + |Y1: |5 + |-------| + |Y2: |6 + |-------| + |Y3: |7 + |-------| + | |8 <- we want c's environment to start here + |-------| + | |9 + |-------| + +CP = L2 +P = c/0 + +So when c/0 executes allocate ... + +With (*) the new value of E would be: + + E = 3 + CODE[STACK[3 + 1] - 1] + 2 + E = 3 + CODE[L1 - 1] + 2 + E = 3 + 1 + 2 + E = 6 + + which overwrites Y2 and Y3 since it uses the 1 from a/0 + +Whereas (**) gives us: + + E = 3 + CODE[CP - 1] + 2 + E = 3 + CODE[L2 - 1] + 2 + E = 3 + 3 + 2 + E = 8 + + which is right since b/0 wants to save 3 locals + +This same problem exists in all the instructions that deal with +allocating new stack frames. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +As described UNIFY_CONSTANT does not increment the S register, this +makes it very hard to read structures with constants which are not the +last argument. Easy fix... + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +When allocating a new choice or environment frame on the stack, one +should use CP (the continuation pointer) insteads of E+1 (the stored +continuation pointer) to find out the number of Y variables to preserve +in the previous environment frame. This is because the continuation +pointer is stored on the stack only if an ALLOCATE instruction is used +and so only CP has the real value. + +Thus, instead of + + if (E > B) + NewB = E + *(((int *) *(E+1))-1) + 2; + else NewB = B + *B + FIXED_CHOICE_FRAME_SIZE; + +one should use code like + + if (E > B) + NewB = E + *(CP-1) + 2; + else NewB = B + *B + FIXED_CHOICE_FRAME_SIZE; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +The try, retry, and trust instructions reset the HB register to an +incorrect value. The problem is that n is computed from the original +value of B (n <- STACK[B]). Hence n is no longer valid when HB is +re-loaded. The correct code is: + + HB <- STACK[B+STACK[B]+6] + +There is a more subtle related bug that usually doesn't matter very +much: both cut and neck_cut should also reset HB. If they do not, +some uneccessary trailing will occur. This normally doesnt matter +too much (aside from a small performance penalty), but it does turn +out to be a problem if you try to implement Older and Rummel's incremental +garbage collection algorithm, because you end up with dangling trail +references to collected heap storage. O&R's algorithm relies on knowing +that there are no trail references below a certain point into the +tip of the heap. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/wambook/wambook.pdf b/wambook/wambook.pdf new file mode 100644 index 00000000..e2646ffa Binary files /dev/null and b/wambook/wambook.pdf differ