Compare commits
276 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e7d9f9010 | ||
|
|
d3edffb7fc | ||
|
|
295a642034 | ||
|
|
ae0999a3bb | ||
|
|
11fd39bbaf | ||
|
|
eb06f19c32 | ||
|
|
ea88ca552d | ||
|
|
eabbf67e86 | ||
|
|
7a636b0394 | ||
|
|
71ea74a077 | ||
|
|
091af28368 | ||
|
|
fbc5345c9a | ||
|
|
9d49415449 | ||
|
|
83218bf0da | ||
|
|
fcd6c3f127 | ||
|
|
efbddeaeee | ||
|
|
daaab1cb37 | ||
|
|
9b73536cdd | ||
|
|
e12d3f14c1 | ||
|
|
ffb77473b2 | ||
|
|
49c0cd3733 | ||
|
|
a236d00d7d | ||
|
|
bb5a59170a | ||
|
|
2b757de73a | ||
|
|
03f796e9ff | ||
|
|
d8be99b6ad | ||
|
|
2a04025419 | ||
|
|
bc14e3aedd | ||
|
|
bcb9dd0860 | ||
|
|
18dd939159 | ||
|
|
1cbc95bd06 | ||
|
|
d1e8ea4dd5 | ||
|
|
068959bd07 | ||
|
|
3a2d57db33 | ||
|
|
3ce16656ee | ||
|
|
732f3ae10a | ||
|
|
a4993284cd | ||
|
|
8aeb326e5c | ||
|
|
304cf424ed | ||
|
|
3cd40419eb | ||
|
|
f8f7b3b7e2 | ||
|
|
39c850b4f4 | ||
|
|
8b6d68a4cd | ||
|
|
149d3932be | ||
|
|
36cbeb3d6c | ||
|
|
875ae5a773 | ||
|
|
70d046499a | ||
|
|
b6db1cde25 | ||
|
|
257209df75 | ||
|
|
d5d514ec96 | ||
|
|
533594b6b6 | ||
|
|
43b6afcdf3 | ||
|
|
2c23f84808 | ||
|
|
ad13aa3801 | ||
|
|
79597b63bf | ||
|
|
bdba7ba0f5 | ||
|
|
6c7d5e8278 | ||
|
|
61ebcdb56e | ||
|
|
f2195c2362 | ||
|
|
6150ca90d3 | ||
|
|
938f588769 | ||
|
|
61d94dceae | ||
|
|
1411417a44 | ||
|
|
609514b4b8 | ||
|
|
1fd9246b16 | ||
|
|
19974e580b | ||
|
|
550efb8416 | ||
|
|
e0c491e9f3 | ||
|
|
f16850e5f8 | ||
|
|
ca0d4e9aac | ||
|
|
6e50268efb | ||
|
|
54b3034905 | ||
|
|
cf9e667b50 | ||
|
|
3f0b471227 | ||
|
|
bfca60aa86 | ||
|
|
6b9a291f4e | ||
|
|
777637001f | ||
|
|
9fcd7776a9 | ||
|
|
4f166f50a8 | ||
|
|
1547839c33 | ||
|
|
5a1423d0f0 | ||
|
|
3faa838ee2 | ||
|
|
e4792429d3 | ||
|
|
d50d425099 | ||
|
|
0df5f7e576 | ||
|
|
b9f1443eb0 | ||
|
|
ac18aed5a7 | ||
|
|
2c60e4183f | ||
|
|
2bbb6487ec | ||
|
|
095fa1227a | ||
|
|
f395d554a5 | ||
|
|
ad614b684c | ||
|
|
898b6b2b25 | ||
|
|
3c5818a040 | ||
|
|
0c313a0849 | ||
|
|
7c9075b4ee | ||
|
|
4c14cbe39a | ||
|
|
afe744aa57 | ||
|
|
89dde3f370 | ||
|
|
f8ddd78776 | ||
|
|
327a677a7c | ||
|
|
48a34aa2cc | ||
|
|
af54fb2aba | ||
|
|
b2dad66270 | ||
|
|
eac7ff680c | ||
|
|
b4db85c8c3 | ||
|
|
6765921666 | ||
|
|
ff293a56e6 | ||
|
|
ab2b652c84 | ||
|
|
83add732e0 | ||
|
|
f8c4679850 | ||
|
|
3da2b8fba2 | ||
|
|
f7dc1d72f6 | ||
|
|
8dffd72db5 | ||
|
|
703a6dddd1 | ||
|
|
5ea262e1d3 | ||
|
|
010b00f349 | ||
|
|
974722bc1c | ||
|
|
695389bda1 | ||
|
|
9cf0d8b484 | ||
|
|
381b4f2857 | ||
|
|
35aa4d9048 | ||
|
|
8e2dcd905b | ||
|
|
081f3c0827 | ||
|
|
8bbb484f94 | ||
|
|
717c3c2db8 | ||
|
|
0aa6abe517 | ||
|
|
4a9f690c16 | ||
|
|
e3d020ea74 | ||
|
|
82378c6338 | ||
|
|
f456c98c5b | ||
|
|
4db325e4b8 | ||
|
|
8f33eaf7d1 | ||
|
|
eba16a2213 | ||
|
|
87f97c7e5c | ||
|
|
2fec440ba6 | ||
|
|
3e2f3ecfdb | ||
|
|
2b6b11e6be | ||
|
|
7da2ba2015 | ||
|
|
bb9c1cf5a2 | ||
|
|
28f8da5ce9 | ||
|
|
c065b52778 | ||
|
|
6a10c6d6a5 | ||
|
|
0623e8e7df | ||
|
|
3c9c7ade8d | ||
|
|
13c85c74db | ||
|
|
5fc21241d6 | ||
|
|
833c3bd77f | ||
|
|
153f04b72a | ||
|
|
8aab887388 | ||
|
|
79a9b950cb | ||
|
|
fba2854f21 | ||
|
|
1c9fd1501a | ||
|
|
b0b89a0aca | ||
|
|
025da8315b | ||
|
|
8002b9a78b | ||
|
|
02f86587b5 | ||
|
|
4d144ea819 | ||
|
|
02c927bb83 | ||
|
|
4a8388a6e9 | ||
|
|
7bf014cdf5 | ||
|
|
b41563e976 | ||
|
|
ead2fccdbb | ||
|
|
817c584993 | ||
|
|
e00112b92f | ||
|
|
4bd0ff0fcb | ||
|
|
ed59aa05b0 | ||
|
|
16ed1040d2 | ||
|
|
fa21ff4813 | ||
|
|
891f7fc892 | ||
|
|
fae9ca1bb9 | ||
|
|
ac71cff338 | ||
|
|
8f39bee463 | ||
|
|
2dc904aa73 | ||
|
|
0ceab41b39 | ||
|
|
2a79d6fc40 | ||
|
|
49e49b425d | ||
|
|
7d0d61d789 | ||
|
|
5ef5a3f30a | ||
|
|
337d9c5583 | ||
|
|
26523139a8 | ||
|
|
65aadb3b88 | ||
|
|
dfad71bc6e | ||
|
|
d8c6fa1fe5 | ||
|
|
18b476af28 | ||
|
|
bd233fedfd | ||
|
|
083546442d | ||
|
|
3343188756 | ||
|
|
ed36aa7313 | ||
|
|
0dc94ecc0b | ||
|
|
453a88f03e | ||
|
|
c79fd74331 | ||
|
|
e446b13376 | ||
|
|
a83f412251 | ||
|
|
1a8c4f9b03 | ||
|
|
c2e1ded852 | ||
|
|
70220687f4 | ||
|
|
69a367d1c9 | ||
|
|
e2bdf59c81 | ||
|
|
6284aa3a3f | ||
|
|
29cd80510b | ||
|
|
9089f9ddb4 | ||
|
|
16dc10ee96 | ||
|
|
4e8f338870 | ||
|
|
b14e363d5e | ||
|
|
87f89545bb | ||
|
|
5064760b1e | ||
|
|
eca4262be6 | ||
|
|
73c26bed2a | ||
|
|
172b6a6f8f | ||
|
|
129cca53ed | ||
|
|
cd1ed106ed | ||
|
|
00aba96185 | ||
|
|
616f071cd9 | ||
|
|
580572aec6 | ||
|
|
47e908bf76 | ||
|
|
11901b5fde | ||
|
|
d63b0a192c | ||
|
|
2fc08dde1e | ||
|
|
193cfabc66 | ||
|
|
7c3ffd0e87 | ||
|
|
797a8f8611 | ||
|
|
f570e566f8 | ||
|
|
96a9dfcc1b | ||
|
|
6fdcd17f32 | ||
|
|
652e824e59 | ||
|
|
f595eaee2e | ||
|
|
9ddeeed683 | ||
|
|
b287f0aedb | ||
|
|
3b019fca1d | ||
|
|
4bd16b8a2e | ||
|
|
f2e044e5b8 | ||
|
|
1cd55d8131 | ||
|
|
14265118f1 | ||
|
|
b66824ffce | ||
|
|
6ec8c9064f | ||
|
|
d4f2f7ba2b | ||
|
|
a05dc79505 | ||
|
|
e90c813528 | ||
|
|
516848e214 | ||
|
|
6063783e9d | ||
|
|
e9e97fe82a | ||
|
|
5686b69013 | ||
|
|
248b05c992 | ||
|
|
a283c8bdc2 | ||
|
|
6b5ec3fab8 | ||
|
|
a0b4162d7a | ||
|
|
119761ad10 | ||
|
|
bf8651db29 | ||
|
|
11d0215963 | ||
|
|
16fd7a8d51 | ||
|
|
5357ecf8e3 | ||
|
|
e7c288f8f0 | ||
|
|
95abc4017e | ||
|
|
b91ce76052 | ||
|
|
617a551a56 | ||
|
|
31247545c3 | ||
|
|
15d112485c | ||
|
|
768f7ce9a7 | ||
|
|
e4d9692535 | ||
|
|
f050e75d81 | ||
|
|
3dcb42af3c | ||
|
|
fdcd92db08 | ||
|
|
c82f1ccde1 | ||
|
|
2b85a307ae | ||
|
|
d7d5dfe059 | ||
|
|
109796122e | ||
|
|
476c1718f9 | ||
|
|
f8b9944f5c | ||
|
|
6e7dbfc75e | ||
|
|
1add5a9a75 | ||
|
|
777e6de9ee | ||
|
|
2443af57d0 | ||
|
|
ed03d5b1b4 | ||
|
|
9376bc6369 | ||
|
|
2f43c3ff40 |
17
.github/actions/setup-rust/action.yml
vendored
17
.github/actions/setup-rust/action.yml
vendored
@@ -1,4 +1,7 @@
|
||||
name: 'Setup Rust'
|
||||
description: |
|
||||
Setup the rust toolchain and environment for the selected toolchain
|
||||
|
||||
inputs:
|
||||
rust-version:
|
||||
required: true
|
||||
@@ -16,7 +19,7 @@ inputs:
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
- uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 # master
|
||||
id: toolchain
|
||||
with:
|
||||
toolchain: ${{ inputs.rust-version }}
|
||||
@@ -33,7 +36,15 @@ runs:
|
||||
echo "CC=clang" >> $GITHUB_ENV
|
||||
echo "PKG_CONFIG_SYSROOT_DIR=/" >> $GITHUB_ENV
|
||||
|
||||
- uses: actions/cache@v4
|
||||
- name: Install s390x cross-compilation toolchain
|
||||
if: ${{ matrix.target == 's390x-unknown-linux-gnu' }}
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y gcc-s390x-linux-gnu
|
||||
echo "CARGO_TARGET_S390X_UNKNOWN_LINUX_GNU_LINKER=s390x-linux-gnu-gcc" >> $GITHUB_ENV
|
||||
|
||||
- uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
@@ -46,7 +57,7 @@ runs:
|
||||
# Remove build artifacts for the current crate, since it will be rebuilt every
|
||||
# run anyway, but keep dependency artifacts to cache them.
|
||||
# Must be placed after actions/cache so its post step runs first.
|
||||
- uses: pyTooling/Actions/with-post-step@v4.1.0
|
||||
- uses: pyTooling/Actions/with-post-step@41aed3e559cbf69bfdb46abb25d7c535900ed5b6 # v7.7.0
|
||||
with:
|
||||
main: bash ./.github/actions/setup-rust/cleanup.sh
|
||||
post: bash ./.github/actions/setup-rust/cleanup.sh
|
||||
|
||||
2
.github/actions/setup-rust/cleanup.sh
vendored
2
.github/actions/setup-rust/cleanup.sh
vendored
@@ -4,7 +4,7 @@ set -e
|
||||
echo Cleanup workspace build artifacts and extra target output
|
||||
|
||||
# clean just the direct members of the current workspace, use cargo metadata to generalize to all rust projects
|
||||
cargo clean -p `cargo metadata --no-deps --offline --format-version 1 | jq -r '[.workspace_members[]|split(" ")|.[0]]|join(" ")'`
|
||||
cargo clean --workspace
|
||||
|
||||
# remove directories in /target/ that are not named `debug` or `release`
|
||||
before=`du -s target | awk '{print $1}'`
|
||||
|
||||
52
.github/dependabot.yml
vendored
Normal file
52
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
version: 2
|
||||
|
||||
updates:
|
||||
# Enable version updates for cargo
|
||||
- package-ecosystem: "cargo"
|
||||
# Look for `Cargo.toml` and `Cargo.lock` files in the `root` directory
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
cooldown:
|
||||
default-days: "7"
|
||||
groups:
|
||||
cargo-incompatible:
|
||||
applies-to: version-updates
|
||||
# TODO: use incompatible update-type once available
|
||||
# see issue https://github.com/dependabot/dependabot-core/issues/9681
|
||||
update-types:
|
||||
- "major"
|
||||
- "minor" # pre-1.0 dependencies
|
||||
cargo-compatible:
|
||||
applies-to: version-updates
|
||||
# TODO: use compatible update-type once available
|
||||
# see issue https://github.com/dependabot/dependabot-core/issues/9681
|
||||
update-types:
|
||||
- "patch"
|
||||
ignore:
|
||||
# ignore all cargo updates for now while dependabot does not respect msrv/rust-version
|
||||
# see issue https://github.com/dependabot/dependabot-core/issues/5423
|
||||
- dependency-name: "*"
|
||||
|
||||
# Enable version updates for Docker
|
||||
- package-ecosystem: "docker"
|
||||
# Look for a `Dockerfile` in the `root` directory
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
cooldown:
|
||||
default-days: "7"
|
||||
|
||||
# Enable version updates for GitHub Actions
|
||||
- package-ecosystem: "github-actions"
|
||||
# Workflow files stored in the default location of `.github/workflows`
|
||||
# You don't need to specify `/.github/workflows` for `directory`. You can use `directory: "/"`.
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
cooldown:
|
||||
default-days: "7"
|
||||
ignore:
|
||||
# these actions doesn't have proper version tags
|
||||
- dependency-name: "dtolnay/rust-toolchain"
|
||||
- dependency-name: "logtalk-actions/setup-logtalk"
|
||||
12
.github/workflows/check_features.sh
vendored
Executable file
12
.github/workflows/check_features.sh
vendored
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
echo "Checking all feature at once"
|
||||
cargo check -q --all-targets --all-features "$@"
|
||||
|
||||
features=$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[] | select(.name = "scryer-prolog") | .features | keys | join(" ")')
|
||||
|
||||
for feature in ${features} ; do
|
||||
echo "Checking feature ${feature} in isolation"
|
||||
cargo check -q --all-targets --no-default-features --features=${feature} "$@"
|
||||
done
|
||||
60
.github/workflows/ci.yml
vendored
60
.github/workflows/ci.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
style:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Setup Rust
|
||||
uses: ./.github/actions/setup-rust
|
||||
with:
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
outputs:
|
||||
msrv: ${{ steps.read-declared-msrv.outputs.msrv }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- id: read-declared-msrv
|
||||
name: Read msrv from Cargo.toml rust_version field
|
||||
run: echo "msrv=$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[] | select(.name = "scryer-prolog") | ."rust_version"')" >> "$GITHUB_OUTPUT"
|
||||
@@ -53,26 +53,30 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
# operating systems
|
||||
- { os: windows-latest, rust-version: stable, target: 'x86_64-pc-windows-msvc', publish: true }
|
||||
- { os: macos-latest, rust-version: stable, target: 'x86_64-apple-darwin', publish: true }
|
||||
- { os: windows-latest, rust-version: stable, target: 'x86_64-pc-windows-msvc', publish: true, check-features: true }
|
||||
- { os: macos-latest, rust-version: stable, target: 'x86_64-apple-darwin', publish: true, check-features: true }
|
||||
# architectures
|
||||
- { os: ubuntu-22.04, rust-version: stable, target: 'x86_64-unknown-linux-gnu', publish: true }
|
||||
- { os: ubuntu-22.04, rust-version: stable, target: 'x86_64-unknown-linux-gnu', publish: true, check-features: true }
|
||||
- { os: ubuntu-22.04, rust-version: stable, target: 'i686-unknown-linux-gnu', publish: true }
|
||||
# FIXME(issue #2138): run wasm tests, failing to run since https://github.com/mthom/scryer-prolog/pull/2137 removed wasm-pack
|
||||
- { os: ubuntu-22.04, rust-version: nightly, target: 'wasm32-unknown-unknown', publish: true, args: '--no-default-features' , test-args: '--no-run --no-default-features', use_swap: true }
|
||||
# Cargo.toml rust-version
|
||||
- { os: ubuntu-22.04, rust-version: "${{ needs.read-msrv.outputs.msrv }}" , target: 'x86_64-unknown-linux-gnu'}
|
||||
- { os: ubuntu-22.04, rust-version: beta, target: 'x86_64-unknown-linux-gnu'}
|
||||
- { os: ubuntu-22.04, rust-version: nightly, target: 'x86_64-unknown-linux-gnu', miri: true, components: "miri"}
|
||||
- { os: ubuntu-22.04, rust-version: "${{ needs.read-msrv.outputs.msrv }}" , target: 'x86_64-unknown-linux-gnu' }
|
||||
- { os: ubuntu-22.04, rust-version: beta, target: 'x86_64-unknown-linux-gnu' }
|
||||
- { os: ubuntu-22.04, rust-version: nightly, target: 'x86_64-unknown-linux-gnu', miri: true, components: "miri" }
|
||||
# run miri for a big-endian target, with all features that are simple to get cross-compiled
|
||||
- { os: ubuntu-22.04, rust-version: nightly, target: 's390x-unknown-linux-gnu', miri: true, components: "miri", args: '--no-default-features --features=all-simple-cross', test-args: '--no-run --no-default-features --features=all-simple-cross' }
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actionhippie/swap-space@v1
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- uses: actionhippie/swap-space@0cffa893f224708cfb6b011690d8ba819d69c10f # v1.1.0
|
||||
if: matrix.use_swap
|
||||
with:
|
||||
size: 10G
|
||||
|
||||
- name: Setup Rust
|
||||
uses: ./.github/actions/setup-rust
|
||||
with:
|
||||
@@ -84,12 +88,17 @@ jobs:
|
||||
# Build and test.
|
||||
- name: Build library
|
||||
run: cargo build --all-targets --target ${{ matrix.target }} ${{ matrix.args }} --verbose
|
||||
|
||||
- name: Test
|
||||
run: cargo test --target ${{ matrix.target }} ${{ matrix.test-args }} --all
|
||||
|
||||
- name: Check features
|
||||
if: matrix.check-features
|
||||
run: bash ./.github/workflows/check_features.sh --target ${{ matrix.target }}
|
||||
|
||||
- name: Check miri
|
||||
if: matrix.miri
|
||||
run: cargo miri test
|
||||
run: cargo miri test --target ${{ matrix.target }} ${{ matrix.args }}
|
||||
|
||||
# On stable rust builds, build a binary and publish as a github actions
|
||||
# artifact. These binaries could be useful for testing the pipeline but
|
||||
@@ -110,7 +119,7 @@ jobs:
|
||||
|
||||
- name: Publish release binary artifact
|
||||
if: matrix.publish
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
path: |
|
||||
target/${{ matrix.target }}/release/scryer-prolog*
|
||||
@@ -123,14 +132,14 @@ jobs:
|
||||
needs: [build-test]
|
||||
steps:
|
||||
# Download prebuilt ubuntu binary from build-test job, setup logtalk
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c #v8.0.1
|
||||
with:
|
||||
name: scryer-prolog_ubuntu-22.04_x86_64-unknown-linux-gnu
|
||||
- run: |
|
||||
chmod +x release/scryer-prolog
|
||||
echo "$PWD/release" >> "$GITHUB_PATH"
|
||||
- name: Install Logtalk
|
||||
uses: logtalk-actions/setup-logtalk@master
|
||||
uses: logtalk-actions/setup-logtalk@4ea002fe3037199afcf1c6c91bf1f57de0f995e6 # master
|
||||
with:
|
||||
logtalk-version: "3.70.0"
|
||||
logtalk-tool-dependencies: false
|
||||
@@ -147,17 +156,17 @@ jobs:
|
||||
|| 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@v4
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: logtalk-test-logs
|
||||
path: '${{ env.LOGTALKUSER }}/tests/prolog/logtalk_tester_logs'
|
||||
- name: Publish Logtalk test results artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
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
|
||||
uses: EnricoMi/publish-unit-test-result-action/composite@c950f6fb443cb5af20a377fd0dfaa78838901040 # v2.23.0
|
||||
with:
|
||||
check_name: Logtalk test summary
|
||||
files: '${{ env.LOGTALKUSER }}/tests/prolog/**/*.xml'
|
||||
@@ -167,7 +176,7 @@ jobs:
|
||||
report:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Setup Rust
|
||||
uses: ./.github/actions/setup-rust
|
||||
with:
|
||||
@@ -180,18 +189,19 @@ jobs:
|
||||
version=`yq -ptoml -oj -r '.package[] | select(.name == "iai-callgrind") | .version' Cargo.lock`
|
||||
echo installing iai-callgrind "$version"
|
||||
cargo install iai-callgrind-runner --force --version "$version"
|
||||
sudo apt install valgrind -y
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install valgrind -y
|
||||
|
||||
- name: Test and report
|
||||
run: |
|
||||
RUSTC_BOOTSTRAP=1 cargo test --all -- -Z unstable-options --format json --report-time | cargo2junit > cargo_test_results.xml
|
||||
- name: Publish cargo test results artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: cargo-test-results
|
||||
path: cargo_test_results.xml
|
||||
- name: Publish cargo test summary
|
||||
uses: EnricoMi/publish-unit-test-result-action/composite@master
|
||||
uses: EnricoMi/publish-unit-test-result-action/composite@c950f6fb443cb5af20a377fd0dfaa78838901040 # v2.23.0
|
||||
with:
|
||||
check_name: Cargo test summary
|
||||
files: cargo_test_results.xml
|
||||
@@ -205,7 +215,7 @@ jobs:
|
||||
- run: cargo bench --bench run_criterion -- --profile-time 60
|
||||
|
||||
- name: Publish benchmark results
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: benchmark-results
|
||||
path: |
|
||||
@@ -215,11 +225,11 @@ jobs:
|
||||
|
||||
# Publish binaries when building for a tag
|
||||
release:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
needs: [build-test]
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
steps:
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c #v8.0.1
|
||||
- name: Zip binaries for release
|
||||
run: |
|
||||
zip scryer-prolog_macos-latest.zip ./scryer-prolog_macos-latest_x86_64-apple-darwin/scryer-prolog
|
||||
@@ -228,7 +238,7 @@ jobs:
|
||||
zip scryer-prolog_windows-latest.zip ./scryer-prolog_windows-latest_x86_64-pc-windows-msvc/scryer-prolog.exe
|
||||
zip scryer-prolog_wasm32.zip ./scryer-prolog_ubuntu-22.04_wasm32-unknown-unknown/scryer-prolog.wasm
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1
|
||||
with:
|
||||
files: |
|
||||
scryer-prolog_macos-latest.zip
|
||||
|
||||
12
.github/workflows/docker-publish.yml
vendored
12
.github/workflows/docker-publish.yml
vendored
@@ -14,17 +14,17 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
# Workaround: https://github.com/docker/build-push-action/issues/461
|
||||
- name: Setup Docker buildx
|
||||
# https://github.com/docker/setup-buildx-action
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
|
||||
|
||||
# Login against Docker registry
|
||||
- name: Log into registry
|
||||
# https://github.com/docker/login-action
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
- name: Extract Docker metadata
|
||||
id: meta
|
||||
# https://github.com/docker/metadata-action
|
||||
uses: docker/metadata-action@v5
|
||||
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
|
||||
with:
|
||||
images: docker.io/${{ secrets.DOCKERHUB_USERNAME }}/scryer-prolog
|
||||
tags: |
|
||||
@@ -47,8 +47,8 @@ jobs:
|
||||
- name: Build and push Docker image
|
||||
id: build-and-push
|
||||
# https://github.com/docker/build-push-action
|
||||
uses: docker/build-push-action@v6
|
||||
# v4 adds SLSA Provenance attestation which is
|
||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
|
||||
# v4 adds SLSA Provenance attestation which is
|
||||
# - unsupported by AWS Lambda
|
||||
# - limited support by Google Cloud Run
|
||||
# > If deploying a multi-architecture image, the manifest list must include linux/amd64.
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -3,3 +3,5 @@ target/
|
||||
.direnv/
|
||||
|
||||
|
||||
__pycache__
|
||||
*.pyc
|
||||
1576
Cargo.lock
generated
1576
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
41
Cargo.toml
41
Cargo.toml
@@ -2,7 +2,7 @@
|
||||
name = "scryer-prolog"
|
||||
version = "0.10.0"
|
||||
authors = ["Mark Thom <markjordanthom@gmail.com>"]
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
description = "A modern Prolog implementation written mostly in Rust."
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/mthom/scryer-prolog"
|
||||
@@ -11,28 +11,39 @@ keywords = ["prolog", "prolog-interpreter", "prolog-system"]
|
||||
categories = ["command-line-utilities"]
|
||||
build = "build/main.rs"
|
||||
# Remember to check CI
|
||||
rust-version = "1.85"
|
||||
rust-version = "1.93.1"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[features]
|
||||
default = ["ffi", "repl", "hostname", "tls", "http", "crypto-full"]
|
||||
default = ["all-simple-cross", "tls", "http"]
|
||||
# activates all features that depend on no non pure-rust dependencies
|
||||
#
|
||||
# currently does not include
|
||||
# ffi due to libffi
|
||||
# tls, http due to openssl
|
||||
# crypto-full due to ring
|
||||
all-pure = ["repl", "hostname"]
|
||||
# enables all features that are simple to get working for cross-compliation
|
||||
# currently all but tls, http as those depend on openssl
|
||||
all-simple-cross = ["all-pure", "ffi", "crypto-full"]
|
||||
ffi = ["dep:libffi"]
|
||||
repl = ["dep:crossterm", "dep:ctrlc", "dep:rustyline"]
|
||||
hostname = ["dep:hostname"]
|
||||
tls = ["dep:native-tls"]
|
||||
http = ["dep:warp", "dep:reqwest"]
|
||||
crypto-full = []
|
||||
# crypto function that require non pure-rust dependencies
|
||||
crypto-full = ["dep:ring"]
|
||||
|
||||
[lints.clippy]
|
||||
collapsible_match = "allow"
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = { level = "deny", check-cfg = [
|
||||
'cfg(rust_version, values("1.87.0"))',
|
||||
] }
|
||||
|
||||
unexpected_cfgs = "deny"
|
||||
function_casts_as_integer = "deny"
|
||||
|
||||
[build-dependencies]
|
||||
indexmap = "2.3.0"
|
||||
proc-macro2 = "1.0.86"
|
||||
quote = "1.0.36"
|
||||
strum = "0.26"
|
||||
@@ -40,7 +51,6 @@ strum_macros = "0.26"
|
||||
syn = { version = "2.0.72", features = ['full', 'visit', 'extra-traits'] }
|
||||
to-syn-value = "0.1.1"
|
||||
to-syn-value_derive = "0.1.1"
|
||||
version_check = "0.9.5"
|
||||
walkdir = "2"
|
||||
|
||||
[dependencies]
|
||||
@@ -61,17 +71,18 @@ futures = "0.3"
|
||||
fxhash = "0.2.1"
|
||||
git-version = "0.3.9"
|
||||
indexmap = "2.3.0"
|
||||
lazy_static = "1.5.0"
|
||||
lexical = "7.0.4"
|
||||
libc = "0.2.155"
|
||||
libloading = "0.8"
|
||||
scryer-modular-bitfield = "0.11.4"
|
||||
modular-bitfield = "0.13.1"
|
||||
num-order = { version = "1.2.0" }
|
||||
ordered-float = "5.0.0"
|
||||
phf = { version = "0.11", features = ["macros"] }
|
||||
puruspe = "0.4.1"
|
||||
rand = "0.8.5"
|
||||
ring = { version = "0.17.8", features = ["wasm32_unknown_unknown_js"] }
|
||||
ring = { version = "0.17.8", features = [
|
||||
"wasm32_unknown_unknown_js",
|
||||
], optional = true }
|
||||
ripemd = "0.1.3"
|
||||
roxmltree = "0.20.0"
|
||||
ryu = "1.0.18"
|
||||
@@ -93,12 +104,12 @@ parking_lot = "0.12.4"
|
||||
crossterm = { version = "0.28.1", optional = true }
|
||||
ctrlc = { version = "3.4.4", optional = true }
|
||||
hostname = { version = "0.4.0", optional = true }
|
||||
libffi = { version = "4.0.0", optional = true }
|
||||
libffi = { version = "5.1.0", optional = true }
|
||||
native-tls = { version = "0.2.12", optional = true }
|
||||
# the version requirement of reqwest is kept low for compatibility with old deno versions
|
||||
# that pin reqwest to 0.11.20
|
||||
reqwest = { version = "0.11.0", optional = true }
|
||||
rustyline = { version = "14.0.0", optional = true }
|
||||
rustyline = { version = "18.0.0", optional = true }
|
||||
tokio = { version = "1.39.2", features = ["full"] }
|
||||
warp = { version = "0.3.7", features = ["tls"], optional = true }
|
||||
|
||||
|
||||
13
INDEX.dj
13
INDEX.dj
@@ -55,14 +55,13 @@ the builtin Prolog modules and libraries in Scryer, check the documentation site
|
||||
|
||||
## Downloads
|
||||
|
||||
The latest version of Scryer Prolog is *0.9.4*. And it's already useful for lots of tasks.
|
||||
The latest version of Scryer Prolog is *0.10.0*. And it's already useful for lots of tasks.
|
||||
|
||||
| Windows (64 bits) | [Download](https://scryerprologrelease.blob.core.windows.net/release-094/scryer-prolog_windows-latest_x86_64-pc-windows-msvc.zip) |
|
||||
| macOS (Intel) | [Download](https://scryerprologrelease.blob.core.windows.net/release-094/scryer-prolog_macos-11_x86_64-apple-darwin.zip) |
|
||||
| macOS (ARM) | [Download](https://scryerprologrelease.blob.core.windows.net/release-094/scryer-prolog-macos-arm.zip) |
|
||||
| Linux (Ubuntu 20.04, 64 bits) | [Download](https://scryerprologrelease.blob.core.windows.net/release-094/scryer-prolog_ubuntu-20.04_x86_64-unknown-linux-gnu.zip) |
|
||||
| Linux (Ubuntu 22.04, 64 bits) | [Download](https://scryerprologrelease.blob.core.windows.net/release-094/scryer-prolog_ubuntu-22.04_x86_64-unknown-linux-gnu.zip) |
|
||||
| Linux (Ubuntu 22.04, 32 bits) | [Download](https://scryerprologrelease.blob.core.windows.net/release-094/scryer-prolog_ubuntu-22.04_i686-unknown-linux-gnu.zip) |
|
||||
| Windows (64 bits) | [Download](https://scryerprologrelease.blob.core.windows.net/release-0100/scryer-prolog_windows-latest_x86_64-pc-windows-msvc.zip) |
|
||||
| macOS (Intel) | [Download](https://scryerprologrelease.blob.core.windows.net/release-0100/scryer-prolog_macos-11_x86_64-apple-darwin.zip) |
|
||||
| macOS (ARM) | [Download](https://scryerprologrelease.blob.core.windows.net/release-0100/scryer-prolog--0.10.0.arm64_tahoe.bottle.tar.gz) |
|
||||
| Linux (Ubuntu 22.04, 64 bits) | [Download](https://scryerprologrelease.blob.core.windows.net/release-0100/scryer-prolog_ubuntu-22.04_x86_64-unknown-linux-gnu.zip) |
|
||||
| Linux (Ubuntu 22.04, 32 bits) | [Download](https://scryerprologrelease.blob.core.windows.net/release-0100/scryer-prolog_ubuntu-22.04_i686-unknown-linux-gnu.zip) |
|
||||
|
||||
Scryer Prolog can also 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.
|
||||
|
||||
|
||||
19
README.md
19
README.md
@@ -73,9 +73,10 @@ Extend Scryer Prolog to include the following, among other features:
|
||||
- [ ] Inlining all built-ins and system call instructions.
|
||||
- [x] Greatly reducing the number of instructions used to compile disjunctives.
|
||||
- [x] Storing short atoms to heap cells without writing them to the atom table.
|
||||
- [ ] Configurable JIT/on-demand indexing over all arguments
|
||||
(documented in "[Demand-Driven Indexing of Prolog Clauses](https://user.it.uu.se/~kostis/Papers/iclp07.pdf)"). (_in progress_)
|
||||
- [ ] A compacting garbage collector satisfying the five properties of
|
||||
"[Precise Garbage Collection in Prolog](https://www.complang.tuwien.ac.at/ulrich/papers/PDF/2008-ciclops.pdf)." (_in progress_)
|
||||
- [ ] Mode declarations.
|
||||
|
||||
## Phase 3
|
||||
|
||||
@@ -103,8 +104,6 @@ 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.
|
||||
|
||||
4. Add some form of JIT predicate indexing.
|
||||
|
||||
## Installing Scryer Prolog
|
||||
|
||||
### Binaries
|
||||
@@ -159,9 +158,9 @@ during the installation of the rust toolchain.
|
||||
#### From Crates.io [](https://crates.io/crates/scryer-prolog) 
|
||||
|
||||
> [!NOTE]
|
||||
> The lates crates.io release can be significantly behind the version available in the git repository
|
||||
> The latest crates.io release can be significantly behind the version available in the git repository
|
||||
> The crates.io badge in this sections title is a link to the crates.io page.
|
||||
> The msrv badge in the section title referece to the minimum rust toolchain version required to compile the latest crates.io release
|
||||
> The msrv badge in the section title references the minimum rust toolchain version required to compile the latest crates.io release
|
||||
|
||||
`scryer-prolog` is also release on crates.io and can be installed with
|
||||
|
||||
@@ -880,6 +879,8 @@ Successful existing applications of Scryer Prolog include:
|
||||
trial design, described in [*An Executable Specification of
|
||||
Oncology Dose-Escalation Protocols with Prolog*](https://arxiv.org/abs/2402.08334)
|
||||
and culminating in [**DEDUCTION**](https://codeberg.org/dcnorris/DEDUCTION)
|
||||
- the core tax engine for VAT reasoning and compliance of the
|
||||
Belgian company [VATmiraal](https://vatmiraal.be/)
|
||||
- semantic reasoning and queries in [AD4M](https://github.com/coasys/ad4m),
|
||||
an agent-centric distributed application meta-ontology.
|
||||
|
||||
@@ -900,9 +901,11 @@ in person.
|
||||
and [discussion](https://github.com/mthom/scryer-prolog/discussions/2160).
|
||||
- [Scryer Prolog Meetup 2024](https://www.digitalaustria.gv.at/wissenswertes/events/scryerprologmeetup2024)
|
||||
in Vienna, Austria. Its [announcement and discussion](https://github.com/mthom/scryer-prolog/discussions/2377).
|
||||
- **Save the date:** The [Scryer Prolog Meetup 2025](https://hsd-pbsa.de/veranstaltung/scryer-prolog-meetup-2025/)
|
||||
will take place on Nov. 13th and 14th 2025 in Düsseldorf, Germany.
|
||||
Its [announcement](https://github.com/mthom/scryer-prolog/discussions/2948).
|
||||
- [Scryer Prolog Meetup 2025](https://hsd-pbsa.de/veranstaltung/scryer-prolog-meetup-2025/)
|
||||
in Düsseldorf, Germany. Its [announcement and discussion](https://github.com/mthom/scryer-prolog/discussions/2948).
|
||||
- **Attend, present and represent:** The [Scryer Prolog Meetup 2026](https://www.digitalaustria.gv.at/wissenswertes/events/scryerprologmeetup2026.html)
|
||||
will take place on Oct. 24th and 25th 2026 in Vienna, Austria.
|
||||
Its [announcement](https://github.com/mthom/scryer-prolog/discussions/3327).
|
||||
|
||||
## Support and discussions
|
||||
|
||||
|
||||
35
_typos.toml
Normal file
35
_typos.toml
Normal file
@@ -0,0 +1,35 @@
|
||||
# config for https://github.com/crate-ci/typos
|
||||
|
||||
[default]
|
||||
# example from https://github.com/crate-ci/typos/blob/master/docs/reference.md#example-configurations
|
||||
extend-ignore-re = [
|
||||
"(#|//)\\s*spellchecker:ignore-next-line\\n.*"
|
||||
]
|
||||
|
||||
# correct word key to value
|
||||
# can be used to ignore a typo by adding an entry <typo> = "<typo>"
|
||||
[default.extend-words]
|
||||
|
||||
# correct identifier key to value
|
||||
# can be used to ignore a typo by adding an entry <typo> = "<typo>"
|
||||
[default.extend-identifiers]
|
||||
interm = "interm"
|
||||
IntermReg = "IntermReg"
|
||||
|
||||
[type.rust]
|
||||
extend-glob = [ "*.rs" ]
|
||||
[type.rust.extend-identifiers]
|
||||
consts = "consts" # std::{f32,f64}::consts
|
||||
|
||||
|
||||
[type.prolog]
|
||||
extend-glob = ["*.pl"]
|
||||
check-file = false
|
||||
|
||||
[type.stdout]
|
||||
extend-glob = ["*.stdout"]
|
||||
check-file = false
|
||||
|
||||
|
||||
[files]
|
||||
extend-exclude = ["lib_integration_test_commands.txt"]
|
||||
@@ -49,7 +49,7 @@ once.
|
||||
|
||||
## Adding benchmarks
|
||||
|
||||
This design is meant to suppoort defining lots of benchmarks.
|
||||
This design is meant to support defining lots of benchmarks.
|
||||
|
||||
To add a new benchmark:
|
||||
|
||||
@@ -77,7 +77,7 @@ Some tips:
|
||||
cumbersome to run.
|
||||
* Consider that the library runtime actually parses the text output of the top
|
||||
level. So don't use custom outputs or it will fail to parse. Also keep the
|
||||
output small so it doesn't just benchmark the ouput parsing code.
|
||||
output small so it doesn't just benchmark the output parsing code.
|
||||
* DO test the output of the benchmark run, we don't want to count broken
|
||||
benchmarks.
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
||||
use criterion::{BatchSize, Criterion, criterion_group, criterion_main};
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
|
||||
@@ -70,7 +70,7 @@ impl PrologBenchmark {
|
||||
}
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
pub fn setup(&self) -> impl FnMut() -> Vec<LeafAnswer> {
|
||||
pub fn setup(&self) -> impl FnMut() -> Vec<LeafAnswer> + use<> {
|
||||
let mut machine = self.make_machine();
|
||||
let query = self.query;
|
||||
move || {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,20 +4,22 @@ mod static_string_indexing;
|
||||
use instructions_template::generate_instructions_rs;
|
||||
use static_string_indexing::index_static_strings;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::MAIN_SEPARATOR_STR;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::path::MAIN_SEPARATOR_STR;
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
fn find_prolog_files(path_prefix: &str, current_dir: &Path) -> Vec<(String, PathBuf)> {
|
||||
let mut libraries = vec![];
|
||||
// use a BTreeMap to get a stable order independent of fs enumeration order
|
||||
let mut libraries = BTreeMap::new();
|
||||
|
||||
let entries = match current_dir.read_dir() {
|
||||
Ok(entries) => entries,
|
||||
Err(_) => return libraries,
|
||||
Err(_) => return vec![],
|
||||
};
|
||||
|
||||
for entry in entries.filter_map(Result::ok).map(|e| e.path()) {
|
||||
@@ -34,19 +36,15 @@ fn find_prolog_files(path_prefix: &str, current_dir: &Path) -> Vec<(String, Path
|
||||
let name = entry.file_stem().unwrap().to_str().unwrap();
|
||||
let lib_name = format!("{path_prefix}{name}");
|
||||
|
||||
libraries.push((lib_name, entry));
|
||||
libraries.insert(lib_name, entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
libraries
|
||||
libraries.into_iter().collect()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if version_check::is_min_version("1.87.0").unwrap_or(false) {
|
||||
println!(r#"cargo:rustc-cfg=rust_version="1.87.0""#);
|
||||
}
|
||||
|
||||
let has_rustfmt = Command::new("rustfmt")
|
||||
.arg("--version")
|
||||
.stdin(Stdio::inherit())
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use syn::parse::*;
|
||||
use syn::visit::*;
|
||||
use syn::*;
|
||||
|
||||
use indexmap::IndexSet;
|
||||
|
||||
struct StaticStrVisitor {
|
||||
static_strs: IndexSet<String>,
|
||||
static_strs: BTreeSet<String>,
|
||||
}
|
||||
|
||||
impl StaticStrVisitor {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
static_strs: IndexSet::new(),
|
||||
static_strs: BTreeSet::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -166,9 +166,9 @@ pub fn index_static_strings(instruction_rs_path: &std::path::Path) -> TokenStrea
|
||||
visitor.visit_file(&syntax)
|
||||
}
|
||||
|
||||
let mut static_str_keys = vec![];
|
||||
let mut static_strs = vec![];
|
||||
let mut static_str_indices = vec![];
|
||||
let static_str_keys: Vec<_> = visitor.static_strs.iter().collect();
|
||||
let mut static_strs = Vec::with_capacity(static_str_keys.len());
|
||||
let mut static_str_indices = Vec::with_capacity(static_str_keys.len());
|
||||
|
||||
let indices: Vec<u64> = visitor
|
||||
.static_strs
|
||||
@@ -176,8 +176,6 @@ pub fn index_static_strings(instruction_rs_path: &std::path::Path) -> TokenStrea
|
||||
.map(|string| {
|
||||
let index = static_string_index(string, static_strs.len());
|
||||
|
||||
static_str_keys.push(string);
|
||||
|
||||
if index & 1 == 1 {
|
||||
index
|
||||
} else {
|
||||
|
||||
26
clippy.toml
Normal file
26
clippy.toml
Normal file
@@ -0,0 +1,26 @@
|
||||
disallowed-macros = [
|
||||
# https://rust-lang.github.io/rust-clippy/master/#disallowed_macros
|
||||
|
||||
# list of macros that may panic on allocation failure e.g.
|
||||
|
||||
# "std::vec",
|
||||
]
|
||||
|
||||
disallowed-methods = [
|
||||
# https://rust-lang.github.io/rust-clippy/master/#disallowed_method
|
||||
|
||||
# list of methods that may panic on allocation failure
|
||||
# though not including things that can be used correctly by reversing ahead of time (i.e. std::vec::Vec::try_reserve + std::iter::Extend::extend ).
|
||||
|
||||
# "std::iter::Iter::collect",
|
||||
# { path = "std::vec::Vec::with_capacity", replacement = "std::vec::Vec::new + std::vec::Vec::try_reserve" },
|
||||
# { path = "std::string::String::with_capacity", replacement = "std::string::String::new + std::string::String::try_reserve" },
|
||||
]
|
||||
|
||||
disallowed-types = [
|
||||
# https://rust-lang.github.io/rust-clippy/master/#disallowed_types
|
||||
|
||||
# list of types that can't be used without risking a panic due to allocation failure
|
||||
|
||||
# { path = "std::collections::BTreeMap", reason = "unlike Vec and HashMap BTreeMap cannot reserve capacity ahead of time (i.e. try_reserve) making it unusable without risk of oom panic"},
|
||||
]
|
||||
@@ -6,8 +6,7 @@ omit(["ops_and_meta_predicates.pl", "tabling"]).
|
||||
learn_pages_source_folder("learn").
|
||||
learn_pages_categories(["First steps", "Tutorials"]).
|
||||
learn_pages([
|
||||
page("Let's play Brisca", "Tutorials", "lets-play-brisca.dj"),
|
||||
page("Test page", "First steps", "test-page.dj")
|
||||
page("Let's play Brisca", "Tutorials", "lets-play-brisca.dj")
|
||||
]).
|
||||
copy_file("logo/scryer.png", "scryer.png").
|
||||
copy_file("learn/Spanish_deck_Fournier.jpg", "learn/Spanish_deck_Fournier.jpg").
|
||||
|
||||
@@ -23,7 +23,7 @@ The rules for knowing which players takes the round are the following:
|
||||
|
||||
First, we need to decide a representation of our cards. Coming from another languages we can think that a good representation might be a class or a struct, with two fields, one for the number and the other for the suite, but Prolog doesn't have objects. We can use a list with two elements. But lists are better when we're dealing with variable length data. We could also use a compound term. This is the right choice if our fields are fixed.
|
||||
|
||||
A compound term is defined by an atom, followed by the data itself enclosed by parenthesis and separated by comma. Like this: `card(oros, 4)`. Yes, very similar to predicates. In fact the only difference is how we use them, because they're the same. If we pass a compund term in the first level of a query, or inside a call/N, Prolog will treat it as code instead of data. This is one the the examples of Prolog being a homoiconic language.
|
||||
A compound term is defined by an atom, followed by the data itself enclosed by parenthesis and separated by comma. Like this: `card(oros, 4)`. Yes, very similar to predicates. In fact the only difference is how we use them, because they're the same. If we pass a compound term in the first level of a query, or inside a call/N, Prolog will treat it as code instead of data. This is one the the examples of Prolog being a homoiconic language.
|
||||
|
||||
We can go further, Prolog is very flexible and we can define custom operators easily if we want. Those are also compound terms, but with a different syntax. There's an operator already defined that is very useful for us: the dash. We can just join two pieces of data with a dash, and they'll be together in the same structure. This is usually called "pair".
|
||||
|
||||
@@ -98,7 +98,7 @@ cards_score_(X) -->
|
||||
cards_score_(X1).
|
||||
```
|
||||
|
||||
In DCGs, to match an item of the sequence, we use brackets. We use braces to introduce normal Prolog code. Calling other DCGs (in this case, the same, as it's a recursive one), it's just calling it again. Notice in this code that we are doing the addition of X0 and X1 when we still don't know the value of X1. This would be an error in the traditional arithmethic system of Prolog, but it's valid with clpz. clpz allows us to have a more declarative arithmethic, at least with integers.
|
||||
In DCGs, to match an item of the sequence, we use brackets. We use braces to introduce normal Prolog code. Calling other DCGs (in this case, the same, as it's a recursive one), it's just calling it again. Notice in this code that we are doing the addition of X0 and X1 when we still don't know the value of X1. This would be an error in the traditional arithmetic system of Prolog, but it's valid with clpz. clpz allows us to have a more declarative arithmetic, at least with integers.
|
||||
|
||||
Now we can try this code using `phrase/2` which is needed to jump to a DCG.
|
||||
|
||||
@@ -276,7 +276,7 @@ Let's ask ourselves what is a procedure. It's a sequence. And we have already se
|
||||
|
||||
The basic idea however is having _explicit_ states. The predicates that we're going to write will take an state (a view of the world at a certain point) and will give us the next state.
|
||||
|
||||
Let's define the state first. In a game of Brisca we have players. Each player has the three cards he can choose to put (less if we're running out of cards) and the cards he has got from winning rounds. Aditionally we have a stock, a trump suite and the order to play, which is usually from the player who won the last round and going to the right. We could store the data in a list, with different compound terms:
|
||||
Let's define the state first. In a game of Brisca we have players. Each player has the three cards he can choose to put (less if we're running out of cards) and the cards he has got from winning rounds. Additionally we have a stock, a trump suite and the order to play, which is usually from the player who won the last round and going to the right. We could store the data in a list, with different compound terms:
|
||||
|
||||
```
|
||||
[players([player(Name, PlayableCards, WonCards), player(Name, PlayableCards, WonCards), ...]), stock(Cards), trump(Trump)]
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# Test page
|
||||
|
||||
This is a page about Scryer Prolog
|
||||
218
src/arena.rs
218
src/arena.rs
@@ -1,7 +1,6 @@
|
||||
#![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work
|
||||
|
||||
#[cfg(feature = "http")]
|
||||
use crate::http::{HttpListener, HttpResponse};
|
||||
use crate::machine::heap::AllocError;
|
||||
use crate::machine::loader::LiveLoadState;
|
||||
use crate::machine::streams::*;
|
||||
use crate::offset_table::*;
|
||||
@@ -14,16 +13,16 @@ use ordered_float::OrderedFloat;
|
||||
use std::fmt;
|
||||
use std::fmt::Debug;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::io::PipeReader;
|
||||
use std::io::PipeWriter;
|
||||
use std::mem;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::net::TcpListener;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::process::Child;
|
||||
use std::ptr;
|
||||
use std::ptr::addr_of_mut;
|
||||
use std::ptr::NonNull;
|
||||
|
||||
use crate::machine::streams::{PipeReader, PipeWriter};
|
||||
use std::ptr::addr_of_mut;
|
||||
|
||||
macro_rules! arena_alloc {
|
||||
($e:expr, $arena:expr) => {{
|
||||
@@ -33,9 +32,7 @@ macro_rules! arena_alloc {
|
||||
}
|
||||
|
||||
macro_rules! float_alloc {
|
||||
($e:expr, $arena:expr) => {{
|
||||
$arena.f64_tbl.build_with(OrderedFloat($e))
|
||||
}};
|
||||
($e:expr, $arena:expr) => {{ $arena.f64_tbl.build_with(OrderedFloat($e)) }};
|
||||
}
|
||||
|
||||
pub fn header_offset_from_payload<T: ?Sized + ArenaAllocated>() -> usize
|
||||
@@ -50,7 +47,7 @@ where
|
||||
payload_offset - header_offset
|
||||
}
|
||||
|
||||
#[derive(BitfieldSpecifier, Copy, Clone, Debug, PartialEq)]
|
||||
#[derive(Specifier, Copy, Clone, Debug, PartialEq)]
|
||||
#[bits = 7]
|
||||
pub enum ArenaHeaderTag {
|
||||
Integer = 0b10,
|
||||
@@ -283,9 +280,14 @@ pub trait ArenaAllocated {
|
||||
where
|
||||
Self::Payload: Sized,
|
||||
{
|
||||
TypedArenaPtr(NonNull::new_unchecked(
|
||||
ptr.payload_offset().cast_mut().cast::<Self::Payload>(),
|
||||
))
|
||||
unsafe {
|
||||
TypedArenaPtr(NonNull::new_unchecked(
|
||||
ptr.get_ptr()
|
||||
.byte_add(Self::header_offset_from_payload())
|
||||
.cast_mut()
|
||||
.cast::<Self::Payload>(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::missing_safety_doc)]
|
||||
@@ -443,7 +445,7 @@ impl<T: ?Sized + ArenaAllocated> TypedAllocSlab<T> {
|
||||
pub fn to_untyped(self: Box<Self>) -> (TypedArenaPtr<T>, UntypedArenaSlab) {
|
||||
let raw_box = Box::into_raw(self);
|
||||
|
||||
// safety: the pointer from Box::into_raw fullfills addr_of_mut's saftey requirements
|
||||
// safety: the pointer from Box::into_raw fulfills addr_of_mut's safety requirements
|
||||
let payload_ptr = unsafe { addr_of_mut!((*raw_box).payload) };
|
||||
|
||||
(
|
||||
@@ -485,12 +487,12 @@ unsafe impl Sync for Arena {}
|
||||
#[allow(clippy::new_without_default)]
|
||||
impl Arena {
|
||||
#[inline]
|
||||
pub fn new() -> Self {
|
||||
Arena {
|
||||
pub fn new() -> Result<Self, AllocError> {
|
||||
Ok(Arena {
|
||||
base: None,
|
||||
f64_tbl: F64Table::new(),
|
||||
code_index_tbl: CodeIndexTable::new(),
|
||||
}
|
||||
f64_tbl: F64Table::new()?,
|
||||
code_index_tbl: CodeIndexTable::new()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -500,82 +502,83 @@ unsafe fn drop_slab_in_place(value: NonNull<AllocSlab>, tag: ArenaHeaderTag) {
|
||||
<$payload as ArenaAllocated>::dealloc($value.cast::<TypedAllocSlab<$payload>>())
|
||||
};
|
||||
}
|
||||
|
||||
match tag {
|
||||
ArenaHeaderTag::Integer => {
|
||||
drop_typed_slab_in_place!(Integer, value);
|
||||
}
|
||||
ArenaHeaderTag::Rational => {
|
||||
drop_typed_slab_in_place!(Rational, value);
|
||||
}
|
||||
ArenaHeaderTag::InputFileStream => {
|
||||
drop_typed_slab_in_place!(InputFileStream, value);
|
||||
}
|
||||
ArenaHeaderTag::OutputFileStream => {
|
||||
drop_typed_slab_in_place!(OutputFileStream, value);
|
||||
}
|
||||
ArenaHeaderTag::NamedTcpStream => {
|
||||
drop_typed_slab_in_place!(NamedTcpStream, value);
|
||||
}
|
||||
ArenaHeaderTag::NamedTlsStream => {
|
||||
#[cfg(feature = "tls")]
|
||||
drop_typed_slab_in_place!(NamedTlsStream, value);
|
||||
}
|
||||
ArenaHeaderTag::HttpReadStream => {
|
||||
#[cfg(feature = "http")]
|
||||
drop_typed_slab_in_place!(HttpReadStream, value);
|
||||
}
|
||||
ArenaHeaderTag::HttpWriteStream => {
|
||||
#[cfg(feature = "http")]
|
||||
drop_typed_slab_in_place!(HttpWriteStream, value);
|
||||
}
|
||||
ArenaHeaderTag::ReadlineStream => {
|
||||
drop_typed_slab_in_place!(ReadlineStream, value);
|
||||
}
|
||||
ArenaHeaderTag::StaticStringStream => {
|
||||
drop_typed_slab_in_place!(StaticStringStream, value);
|
||||
}
|
||||
ArenaHeaderTag::ByteStream => {
|
||||
drop_typed_slab_in_place!(ByteStream, value);
|
||||
}
|
||||
ArenaHeaderTag::CallbackStream => {
|
||||
drop_typed_slab_in_place!(CallbackStream, value);
|
||||
}
|
||||
ArenaHeaderTag::InputChannelStream => {
|
||||
drop_typed_slab_in_place!(InputChannelStream, value);
|
||||
}
|
||||
ArenaHeaderTag::LiveLoadState | ArenaHeaderTag::InactiveLoadState => {
|
||||
drop_typed_slab_in_place!(LiveLoadState, value);
|
||||
}
|
||||
ArenaHeaderTag::Dropped => {}
|
||||
ArenaHeaderTag::TcpListener => {
|
||||
drop_typed_slab_in_place!(TcpListener, value);
|
||||
}
|
||||
ArenaHeaderTag::HttpListener => {
|
||||
#[cfg(feature = "http")]
|
||||
drop_typed_slab_in_place!(HttpListener, value);
|
||||
}
|
||||
ArenaHeaderTag::HttpResponse => {
|
||||
#[cfg(feature = "http")]
|
||||
drop_typed_slab_in_place!(HttpResponse, value);
|
||||
}
|
||||
ArenaHeaderTag::StandardOutputStream => {
|
||||
drop_typed_slab_in_place!(StandardOutputStream, value);
|
||||
}
|
||||
ArenaHeaderTag::StandardErrorStream => {
|
||||
drop_typed_slab_in_place!(StandardErrorStream, value);
|
||||
}
|
||||
ArenaHeaderTag::PipeReader => {
|
||||
drop_typed_slab_in_place!(PipeReader, value);
|
||||
}
|
||||
ArenaHeaderTag::PipeWriter => {
|
||||
drop_typed_slab_in_place!(PipeWriter, value);
|
||||
}
|
||||
ArenaHeaderTag::ChildProcess => {
|
||||
drop_typed_slab_in_place!(Child, value);
|
||||
}
|
||||
ArenaHeaderTag::NullStream => {
|
||||
unreachable!("NullStream is never arena allocated!");
|
||||
unsafe {
|
||||
match tag {
|
||||
ArenaHeaderTag::Integer => {
|
||||
drop_typed_slab_in_place!(Integer, value);
|
||||
}
|
||||
ArenaHeaderTag::Rational => {
|
||||
drop_typed_slab_in_place!(Rational, value);
|
||||
}
|
||||
ArenaHeaderTag::InputFileStream => {
|
||||
drop_typed_slab_in_place!(InputFileStream, value);
|
||||
}
|
||||
ArenaHeaderTag::OutputFileStream => {
|
||||
drop_typed_slab_in_place!(OutputFileStream, value);
|
||||
}
|
||||
ArenaHeaderTag::NamedTcpStream => {
|
||||
drop_typed_slab_in_place!(NamedTcpStream, value);
|
||||
}
|
||||
ArenaHeaderTag::NamedTlsStream => {
|
||||
#[cfg(feature = "tls")]
|
||||
drop_typed_slab_in_place!(NamedTlsStream, value);
|
||||
}
|
||||
ArenaHeaderTag::HttpReadStream => {
|
||||
#[cfg(feature = "http")]
|
||||
drop_typed_slab_in_place!(HttpReadStream, value);
|
||||
}
|
||||
ArenaHeaderTag::HttpWriteStream => {
|
||||
#[cfg(feature = "http")]
|
||||
drop_typed_slab_in_place!(HttpWriteStream, value);
|
||||
}
|
||||
ArenaHeaderTag::ReadlineStream => {
|
||||
drop_typed_slab_in_place!(ReadlineStream, value);
|
||||
}
|
||||
ArenaHeaderTag::StaticStringStream => {
|
||||
drop_typed_slab_in_place!(StaticStringStream, value);
|
||||
}
|
||||
ArenaHeaderTag::ByteStream => {
|
||||
drop_typed_slab_in_place!(ByteStream, value);
|
||||
}
|
||||
ArenaHeaderTag::CallbackStream => {
|
||||
drop_typed_slab_in_place!(CallbackStream, value);
|
||||
}
|
||||
ArenaHeaderTag::InputChannelStream => {
|
||||
drop_typed_slab_in_place!(InputChannelStream, value);
|
||||
}
|
||||
ArenaHeaderTag::LiveLoadState | ArenaHeaderTag::InactiveLoadState => {
|
||||
drop_typed_slab_in_place!(LiveLoadState, value);
|
||||
}
|
||||
ArenaHeaderTag::Dropped => {}
|
||||
ArenaHeaderTag::TcpListener => {
|
||||
drop_typed_slab_in_place!(TcpListener, value);
|
||||
}
|
||||
ArenaHeaderTag::HttpListener => {
|
||||
#[cfg(feature = "http")]
|
||||
drop_typed_slab_in_place!(HttpListener, value);
|
||||
}
|
||||
ArenaHeaderTag::HttpResponse => {
|
||||
#[cfg(feature = "http")]
|
||||
drop_typed_slab_in_place!(HttpResponse, value);
|
||||
}
|
||||
ArenaHeaderTag::StandardOutputStream => {
|
||||
drop_typed_slab_in_place!(StandardOutputStream, value);
|
||||
}
|
||||
ArenaHeaderTag::StandardErrorStream => {
|
||||
drop_typed_slab_in_place!(StandardErrorStream, value);
|
||||
}
|
||||
ArenaHeaderTag::PipeReader => {
|
||||
drop_typed_slab_in_place!(PipeReader, value);
|
||||
}
|
||||
ArenaHeaderTag::PipeWriter => {
|
||||
drop_typed_slab_in_place!(PipeWriter, value);
|
||||
}
|
||||
ArenaHeaderTag::ChildProcess => {
|
||||
drop_typed_slab_in_place!(Child, value);
|
||||
}
|
||||
ArenaHeaderTag::NullStream => {
|
||||
unreachable!("NullStream is never arena allocated!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -634,16 +637,22 @@ mod tests {
|
||||
#[test]
|
||||
fn heap_cell_value_const_cast() {
|
||||
let mut wam = MockWAM::new();
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
let const_value = HeapCellValue::from(ConsPtr::build_with(
|
||||
std::ptr::without_provenance(0x0000_0431),
|
||||
ConsPtrMaskTag::Cons,
|
||||
));
|
||||
assert_eq!(ConsPtr::NICHE_SHIFT, 0);
|
||||
|
||||
#[cfg(not(target_pointer_width = "32"))]
|
||||
assert_eq!(ConsPtr::NICHE_SHIFT, 3);
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
let dummy_ptr: *const ArenaHeader = std::ptr::without_provenance(0x0000_0438);
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
let const_value = HeapCellValue::from(ConsPtr::build_with(
|
||||
std::ptr::without_provenance(0x0000_5555_ff00_0431),
|
||||
ConsPtrMaskTag::Cons,
|
||||
));
|
||||
let dummy_ptr: *const ArenaHeader = std::ptr::without_provenance(0x0000_5555_ff00_0438);
|
||||
|
||||
assert!(dummy_ptr.is_aligned());
|
||||
|
||||
let const_value = HeapCellValue::from_arena_header_ptr(dummy_ptr);
|
||||
|
||||
match const_value.to_untyped_arena_ptr() {
|
||||
Some(arena_ptr) => {
|
||||
@@ -658,8 +667,7 @@ mod tests {
|
||||
}
|
||||
|
||||
let stream = Stream::from_static_string("test", &mut wam.machine_st.arena);
|
||||
let stream_cell =
|
||||
HeapCellValue::from(ConsPtr::build_with(stream.as_ptr(), ConsPtrMaskTag::Cons));
|
||||
let stream_cell = HeapCellValue::from_arena_header_ptr(stream.as_ptr());
|
||||
|
||||
match stream_cell.to_untyped_arena_ptr() {
|
||||
Some(arena_ptr) => {
|
||||
@@ -728,7 +736,7 @@ mod tests {
|
||||
Some(untyped_arena_ptr) => {
|
||||
assert_eq!(
|
||||
Some(big_rat_ptr.header_ptr()),
|
||||
Some(untyped_arena_ptr.into()),
|
||||
Some(untyped_arena_ptr.get_ptr()),
|
||||
);
|
||||
}
|
||||
None => {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work
|
||||
|
||||
use crate::allocator::*;
|
||||
use crate::arena::*;
|
||||
use crate::atom_table::*;
|
||||
@@ -22,7 +20,7 @@ use num_order::NumOrd;
|
||||
use ordered_float::{Float, OrderedFloat};
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::cmp::{max, min, Ordering};
|
||||
use std::cmp::Ordering;
|
||||
use std::convert::TryFrom;
|
||||
use std::f64;
|
||||
use std::num::FpCategory;
|
||||
@@ -31,21 +29,11 @@ use std::vec::Vec;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum ArithmeticTerm {
|
||||
IntermReg(usize),
|
||||
Reg(RegType),
|
||||
Interm(usize),
|
||||
Number(Number),
|
||||
}
|
||||
|
||||
impl ArithmeticTerm {
|
||||
pub(crate) fn interm_or(&self, interm: usize) -> usize {
|
||||
if let &ArithmeticTerm::Interm(interm) = self {
|
||||
interm
|
||||
} else {
|
||||
interm
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ArithmeticTerm {
|
||||
fn default() -> Self {
|
||||
ArithmeticTerm::Number(Number::default())
|
||||
@@ -57,7 +45,7 @@ pub(crate) struct ArithInstructionIterator<'a> {
|
||||
state_stack: Vec<TermIterState<'a>>,
|
||||
}
|
||||
|
||||
pub(crate) type ArithCont = (CodeDeque, Option<ArithmeticTerm>);
|
||||
pub(crate) type ArithCont = (CodeDeque, ArithmeticTerm);
|
||||
|
||||
impl<'a> ArithInstructionIterator<'a> {
|
||||
fn push_subterm(&mut self, lvl: Level, term: &'a Term) {
|
||||
@@ -76,7 +64,7 @@ impl<'a> ArithInstructionIterator<'a> {
|
||||
return Err(ArithmeticError::NonEvaluableFunctor(
|
||||
Literal::Atom(atom!(".")),
|
||||
2,
|
||||
))
|
||||
));
|
||||
}
|
||||
Term::Var(cell, var_ptr) => TermIterState::Var(Level::Shallow, cell, var_ptr.clone()),
|
||||
};
|
||||
@@ -90,7 +78,7 @@ impl<'a> ArithInstructionIterator<'a> {
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum ArithTermRef<'a> {
|
||||
Literal(Literal),
|
||||
Op(Atom, usize), // name, arity.
|
||||
Op(Level, &'a Cell<RegType>, Atom, usize), // name, arity.
|
||||
Var(Level, &'a Cell<VarReg>, VarPtr),
|
||||
}
|
||||
|
||||
@@ -105,7 +93,7 @@ impl<'a> Iterator for ArithInstructionIterator<'a> {
|
||||
let arity = subterms.len();
|
||||
|
||||
if child_num == arity {
|
||||
return Some(Ok(ArithTermRef::Op(name, arity)));
|
||||
return Some(Ok(ArithTermRef::Op(lvl, cell, name, arity)));
|
||||
} else {
|
||||
self.state_stack.push(TermIterState::Clause(
|
||||
lvl,
|
||||
@@ -139,7 +127,6 @@ impl<'a> Iterator for ArithInstructionIterator<'a> {
|
||||
pub(crate) struct ArithmeticEvaluator<'a> {
|
||||
marker: &'a mut DebrayAllocator,
|
||||
interm: Vec<ArithmeticTerm>,
|
||||
interm_c: usize,
|
||||
}
|
||||
|
||||
pub(crate) trait ArithmeticTermIter<'a> {
|
||||
@@ -180,11 +167,10 @@ fn push_literal(interm: &mut Vec<ArithmeticTerm>, c: &Literal) -> Result<(), Ari
|
||||
}
|
||||
|
||||
impl<'a> ArithmeticEvaluator<'a> {
|
||||
pub(crate) fn new(marker: &'a mut DebrayAllocator, target_int: usize) -> Self {
|
||||
pub(crate) fn new(marker: &'a mut DebrayAllocator) -> Self {
|
||||
ArithmeticEvaluator {
|
||||
marker,
|
||||
interm: Vec::new(),
|
||||
interm_c: target_int,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,56 +238,37 @@ impl<'a> ArithmeticEvaluator<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn incr_interm(&mut self) -> usize {
|
||||
let temp = self.interm_c;
|
||||
|
||||
self.interm.push(ArithmeticTerm::Interm(temp));
|
||||
self.interm_c += 1;
|
||||
|
||||
temp
|
||||
fn try_add_to_free_list(&mut self, a1: ArithmeticTerm) {
|
||||
if let ArithmeticTerm::IntermReg(t) = a1 {
|
||||
self.marker.add_reg_to_free_list(RegType::Temp(t));
|
||||
}
|
||||
}
|
||||
|
||||
fn instr_from_clause(
|
||||
&mut self,
|
||||
name: Atom,
|
||||
arity: usize,
|
||||
arg: usize,
|
||||
) -> Result<Instruction, ArithmeticError> {
|
||||
match arity {
|
||||
1 => {
|
||||
let a1 = self.interm.pop().unwrap();
|
||||
|
||||
let ninterm = if a1.interm_or(0) == 0 {
|
||||
self.incr_interm()
|
||||
} else {
|
||||
self.interm.push(a1);
|
||||
a1.interm_or(0)
|
||||
};
|
||||
self.interm.push(ArithmeticTerm::IntermReg(arg));
|
||||
self.try_add_to_free_list(a1);
|
||||
|
||||
self.get_unary_instr(name, a1, ninterm)
|
||||
self.get_unary_instr(name, a1, arg)
|
||||
}
|
||||
2 => {
|
||||
let a2 = self.interm.pop().unwrap();
|
||||
let a1 = self.interm.pop().unwrap();
|
||||
|
||||
let min_interm = min(a1.interm_or(0), a2.interm_or(0));
|
||||
self.interm.push(ArithmeticTerm::IntermReg(arg));
|
||||
|
||||
let ninterm = if min_interm == 0 {
|
||||
let max_interm = max(a1.interm_or(0), a2.interm_or(0));
|
||||
self.try_add_to_free_list(a1);
|
||||
self.try_add_to_free_list(a2);
|
||||
|
||||
if max_interm == 0 {
|
||||
self.incr_interm()
|
||||
} else {
|
||||
self.interm.push(ArithmeticTerm::Interm(max_interm));
|
||||
self.interm_c = max_interm + 1;
|
||||
max_interm
|
||||
}
|
||||
} else {
|
||||
self.interm.push(ArithmeticTerm::Interm(min_interm));
|
||||
self.interm_c = min_interm + 1;
|
||||
min_interm
|
||||
};
|
||||
|
||||
self.get_binary_instr(name, a1, a2, ninterm)
|
||||
self.get_binary_instr(name, a1, a2, arg)
|
||||
}
|
||||
_ => Err(ArithmeticError::NonEvaluableFunctor(
|
||||
Literal::Atom(name),
|
||||
@@ -346,13 +313,20 @@ impl<'a> ArithmeticEvaluator<'a> {
|
||||
|
||||
self.interm.push(ArithmeticTerm::Reg(r));
|
||||
}
|
||||
ArithTermRef::Op(name, arity) => {
|
||||
code.push_back(self.instr_from_clause(name, arity)?);
|
||||
ArithTermRef::Op(lvl, cell, name, arity) => {
|
||||
self.marker
|
||||
.mark_non_var::<QueryInstruction>(lvl, term_loc, cell, &mut code);
|
||||
|
||||
if let RegType::Temp(t) = cell.get() {
|
||||
code.push_back(self.instr_from_clause(name, arity, t)?);
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok((code, self.interm.pop()))
|
||||
Ok((code, self.interm.pop().unwrap()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -387,7 +361,7 @@ pub(crate) fn rnd_i(n: &'_ Number, arena: &mut Arena) -> Result<Number, EvalErro
|
||||
)))
|
||||
}
|
||||
}
|
||||
Number::Rational(ref r) => {
|
||||
Number::Rational(r) => {
|
||||
let floor = r.floor();
|
||||
|
||||
if let Ok(value) = Fixnum::build_with_checked(&floor) {
|
||||
@@ -410,9 +384,9 @@ impl From<Fixnum> 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().value(),
|
||||
Number::Integer(n) => n.to_f64().value(),
|
||||
&Number::Float(OrderedFloat(f)) => f,
|
||||
Number::Rational(ref r) => r.to_f64().value(),
|
||||
Number::Rational(r) => r.to_f64().value(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -540,33 +514,23 @@ impl PartialEq for Number {
|
||||
fn eq(&self, rhs: &Self) -> bool {
|
||||
match (self, rhs) {
|
||||
(&Number::Fixnum(n1), &Number::Fixnum(n2)) => n1.eq(&n2),
|
||||
(&Number::Fixnum(n1), Number::Integer(ref n2)) => n1.get_num().num_eq(&**n2),
|
||||
(Number::Integer(ref n1), &Number::Fixnum(n2)) => n1.num_eq(&n2.get_num()),
|
||||
(&Number::Fixnum(n1), Number::Rational(ref n2)) => {
|
||||
(&Number::Fixnum(n1), Number::Integer(n2)) => n1.get_num().num_eq(&**n2),
|
||||
(Number::Integer(n1), &Number::Fixnum(n2)) => n1.num_eq(&n2.get_num()),
|
||||
(&Number::Fixnum(n1), Number::Rational(n2)) => {
|
||||
Integer::from(n1.get_num()).num_eq(&**n2)
|
||||
}
|
||||
(Number::Rational(ref n1), &Number::Fixnum(n2)) => {
|
||||
n1.num_eq(&Integer::from(n2.get_num()))
|
||||
}
|
||||
(Number::Rational(n1), &Number::Fixnum(n2)) => n1.num_eq(&Integer::from(n2.get_num())),
|
||||
(&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().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)) => n1.num_eq(&**n2),
|
||||
(Number::Rational(ref n1), Number::Integer(ref n2)) => n1.num_eq(&**n2),
|
||||
(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::Integer(n1), Number::Integer(n2)) => n1.eq(n2),
|
||||
(Number::Integer(n1), Number::Float(n2)) => OrderedFloat(n1.to_f64().value()).eq(n2),
|
||||
(&Number::Float(n1), Number::Integer(n2)) => n1.eq(&OrderedFloat(n2.to_f64().value())),
|
||||
(Number::Integer(n1), Number::Rational(n2)) => n1.num_eq(&**n2),
|
||||
(Number::Rational(n1), Number::Integer(n2)) => n1.num_eq(&**n2),
|
||||
(Number::Rational(n1), &Number::Float(n2)) => OrderedFloat(n1.to_f64().value()).eq(&n2),
|
||||
(&Number::Float(n1), Number::Rational(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),
|
||||
(Number::Rational(r1), Number::Rational(r2)) => r1.eq(r2),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -633,9 +597,7 @@ impl Ord for Number {
|
||||
(&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().value()).cmp(n2),
|
||||
(&Number::Float(n1), Number::Integer(ref n2)) => {
|
||||
n1.cmp(&OrderedFloat(n2.to_f64().value()))
|
||||
}
|
||||
(&Number::Float(n1), Number::Integer(n2)) => n1.cmp(&OrderedFloat(n2.to_f64().value())),
|
||||
(&Number::Integer(n1), &Number::Rational(n2)) => {
|
||||
(*n1).num_partial_cmp(&*n2).unwrap_or(Ordering::Less)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work
|
||||
|
||||
use crate::machine::heap::AllocError;
|
||||
use crate::parser::ast::MAX_ARITY;
|
||||
use crate::raw_block::*;
|
||||
use crate::types::*;
|
||||
@@ -15,13 +14,13 @@ use std::sync::Mutex;
|
||||
use std::sync::RwLock;
|
||||
use std::sync::Weak;
|
||||
|
||||
use arcu::Rcu;
|
||||
use arcu::atomic::Arcu;
|
||||
use arcu::epoch_counters::GlobalEpochCounterPool;
|
||||
use arcu::rcu_ref::RcuRef;
|
||||
use arcu::Rcu;
|
||||
use indexmap::IndexSet;
|
||||
|
||||
use scryer_modular_bitfield::prelude::*;
|
||||
use modular_bitfield::prelude::*;
|
||||
|
||||
#[bitfield]
|
||||
#[repr(u64)]
|
||||
@@ -307,8 +306,7 @@ impl Atom {
|
||||
AtomTableRef::try_map(atom_table.inner.read(), |buf| unsafe {
|
||||
let ptr = buf
|
||||
.block
|
||||
.base
|
||||
.add(self.flat_index() as usize - STRINGS.len());
|
||||
.get_unchecked(self.flat_index() as usize - STRINGS.len());
|
||||
// TODO use std::ptr::from_raw_parts instead when feature ptr_metadata is stable rust-lang/rust#81513
|
||||
let atom_data = &*(std::ptr::slice_from_raw_parts(ptr, 0) as *const AtomData);
|
||||
let len = atom_data.header.len();
|
||||
@@ -346,11 +344,7 @@ impl Atom {
|
||||
let c1 = it.next();
|
||||
let c2 = it.next();
|
||||
|
||||
if c2.is_none() {
|
||||
c1
|
||||
} else {
|
||||
None
|
||||
}
|
||||
if c2.is_none() { c1 } else { None }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -390,9 +384,11 @@ impl Atom {
|
||||
}
|
||||
|
||||
unsafe fn write_to_ptr(string: &str, ptr: *mut u8) {
|
||||
ptr::write(ptr as *mut _, AtomHeader::build_with(string.len() as u64));
|
||||
let str_ptr = ptr.add(mem::size_of::<AtomHeader>());
|
||||
ptr::copy_nonoverlapping(string.as_ptr(), str_ptr, string.len());
|
||||
unsafe {
|
||||
ptr::write(ptr as *mut _, AtomHeader::build_with(string.len() as u64));
|
||||
let str_ptr = ptr.add(mem::size_of::<AtomHeader>());
|
||||
ptr::copy_nonoverlapping(string.as_ptr(), str_ptr, string.len());
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Atom {
|
||||
@@ -436,21 +432,21 @@ impl InnerAtomTable {
|
||||
|
||||
impl AtomTable {
|
||||
#[inline]
|
||||
pub fn new() -> Arc<Self> {
|
||||
pub fn new() -> Result<Arc<Self>, AllocError> {
|
||||
let upgraded = global_atom_table().read().unwrap().upgrade();
|
||||
// don't inline upgraded, otherwise temporary will be dropped too late in case of None
|
||||
if let Some(atom_table) = upgraded {
|
||||
atom_table
|
||||
Ok(atom_table)
|
||||
} else {
|
||||
let mut guard = global_atom_table().write().unwrap();
|
||||
// try to upgrade again in case we lost the race on the write lock
|
||||
if let Some(atom_table) = guard.upgrade() {
|
||||
atom_table
|
||||
Ok(atom_table)
|
||||
} else {
|
||||
let atom_table = Arc::new(Self {
|
||||
inner: Arcu::new(
|
||||
InnerAtomTable {
|
||||
block: RawBlock::new(),
|
||||
block: RawBlock::new()?,
|
||||
table: Arcu::new(IndexSet::new(), GlobalEpochCounterPool),
|
||||
},
|
||||
GlobalEpochCounterPool,
|
||||
@@ -458,11 +454,16 @@ impl AtomTable {
|
||||
update: Mutex::new(()),
|
||||
});
|
||||
*guard = Arc::downgrade(&atom_table);
|
||||
atom_table
|
||||
Ok(atom_table)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn retrieve() -> Arc<Self> {
|
||||
global_atom_table().read().unwrap().upgrade().unwrap()
|
||||
}
|
||||
|
||||
pub fn active_table(&self) -> RcuRef<IndexSet<Atom>, IndexSet<Atom>> {
|
||||
self.inner.read().table.read()
|
||||
}
|
||||
@@ -516,12 +517,13 @@ impl AtomTable {
|
||||
}
|
||||
};
|
||||
|
||||
let ptr_base = block_epoch.block.base.addr();
|
||||
// SAFETY: `len_ptr` was obtained from `block_epoch.block.alloc()`
|
||||
let len_offset = block_epoch.block.get_offset(len_ptr);
|
||||
|
||||
write_to_ptr(string, len_ptr);
|
||||
|
||||
let atom = AtomCell::new()
|
||||
.with_name((STRINGS.len() + len_ptr.addr() - ptr_base) as u64)
|
||||
.with_name((STRINGS.len() + len_offset) as u64)
|
||||
.with_arity(0)
|
||||
.with_f(false)
|
||||
.with_m(false)
|
||||
@@ -533,7 +535,7 @@ impl AtomTable {
|
||||
table.insert(atom);
|
||||
block_epoch.table.replace(table);
|
||||
|
||||
// expicit drop to ensure we don't accidentally drop it early
|
||||
// explicit drop to ensure we don't accidentally drop it early
|
||||
drop(update_guard);
|
||||
|
||||
return atom;
|
||||
|
||||
@@ -145,7 +145,7 @@ impl BranchCodeStack {
|
||||
settings.trust_me()
|
||||
});
|
||||
|
||||
combined_code.extend(code.into_iter());
|
||||
combined_code.extend(code);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -345,10 +345,10 @@ impl<'a> AddToFreeList<'a, QueryInstruction> for CodeGenerator {
|
||||
|
||||
fn structure_cell(term: &Term) -> Option<&Cell<RegType>> {
|
||||
match term {
|
||||
&Term::Cons(ref cell, ..)
|
||||
| &Term::Clause(ref cell, ..)
|
||||
| Term::PartialString(ref cell, ..)
|
||||
| Term::CompleteString(ref cell, ..) => Some(cell),
|
||||
Term::Cons(cell, ..)
|
||||
| Term::Clause(cell, ..)
|
||||
| Term::PartialString(cell, ..)
|
||||
| Term::CompleteString(cell, ..) => Some(cell),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -401,18 +401,18 @@ impl CodeGenerator {
|
||||
&Term::AnonVar => {
|
||||
Self::add_or_increment_void_instr::<Target>(target);
|
||||
}
|
||||
&Term::Cons(ref cell, ..)
|
||||
| &Term::Clause(ref cell, ..)
|
||||
| Term::PartialString(ref cell, ..)
|
||||
| Term::CompleteString(ref cell, ..) => {
|
||||
Term::Cons(cell, ..)
|
||||
| Term::Clause(cell, ..)
|
||||
| Term::PartialString(cell, ..)
|
||||
| Term::CompleteString(cell, ..) => {
|
||||
self.marker
|
||||
.mark_non_var::<Target>(Level::Deep, term_loc, cell, target);
|
||||
target.push_back(Target::clause_arg_to_instr(cell.get()));
|
||||
}
|
||||
Term::Literal(_, ref constant) => {
|
||||
Term::Literal(_, constant) => {
|
||||
target.push_back(Target::constant_subterm(*constant));
|
||||
}
|
||||
Term::Var(ref cell, ref var_ptr) => {
|
||||
Term::Var(cell, var_ptr) => {
|
||||
self.deep_var_instr::<Target>(
|
||||
cell,
|
||||
var_ptr.to_var_num().unwrap(),
|
||||
@@ -560,27 +560,19 @@ impl CodeGenerator {
|
||||
&InlinedClauseType::CompareNumber(mut cmp) => {
|
||||
self.marker.reset_arg(2);
|
||||
|
||||
let (mut lcode, at_1) = self.compile_arith_expr(&terms[0], 1, term_loc, 1)?;
|
||||
|
||||
if !matches!(terms[0], Term::Var(..)) {
|
||||
self.marker.advance_arg();
|
||||
}
|
||||
|
||||
let (mut rcode, at_2) = self.compile_arith_expr(&terms[1], 2, term_loc, 2)?;
|
||||
let (mut lcode, at_1) = self.compile_arith_expr(&terms[0], term_loc, 1)?;
|
||||
let (mut rcode, at_2) = self.compile_arith_expr(&terms[1], term_loc, 2)?;
|
||||
|
||||
code.append(&mut lcode);
|
||||
code.append(&mut rcode);
|
||||
|
||||
let at_1 = at_1.unwrap_or(interm!(1));
|
||||
let at_2 = at_2.unwrap_or(interm!(2));
|
||||
|
||||
compare_number_instr!(cmp, at_1, at_2)
|
||||
}
|
||||
InlinedClauseType::IsAtom(..) => match &terms[0] {
|
||||
Term::Literal(_, Literal::Atom(..)) => {
|
||||
instr!("$succeed")
|
||||
}
|
||||
Term::Var(ref vr, ref name) => {
|
||||
Term::Var(vr, name) => {
|
||||
self.marker.reset_arg(1);
|
||||
|
||||
let r = self.marker.mark_non_callable(
|
||||
@@ -608,7 +600,7 @@ impl CodeGenerator {
|
||||
Term::Literal(..) => {
|
||||
instr!("$succeed")
|
||||
}
|
||||
Term::Var(ref vr, ref name) => {
|
||||
Term::Var(vr, name) => {
|
||||
self.marker.reset_arg(1);
|
||||
|
||||
let r = self.marker.mark_non_callable(
|
||||
@@ -629,7 +621,7 @@ impl CodeGenerator {
|
||||
| Term::CompleteString(..) => {
|
||||
instr!("$succeed")
|
||||
}
|
||||
Term::Var(ref vr, ref name) => {
|
||||
Term::Var(vr, name) => {
|
||||
self.marker.reset_arg(1);
|
||||
|
||||
let r = self.marker.mark_non_callable(
|
||||
@@ -647,7 +639,10 @@ impl CodeGenerator {
|
||||
}
|
||||
},
|
||||
InlinedClauseType::IsRational(..) => match terms[0] {
|
||||
Term::Literal(_, Literal::Rational(_)) => {
|
||||
Term::Literal(
|
||||
_,
|
||||
Literal::Rational(_) | Literal::Fixnum(_) | Literal::Integer(_),
|
||||
) => {
|
||||
instr!("$succeed")
|
||||
}
|
||||
Term::Var(ref vr, ref name) => {
|
||||
@@ -687,10 +682,13 @@ impl CodeGenerator {
|
||||
}
|
||||
},
|
||||
InlinedClauseType::IsNumber(..) => match terms[0] {
|
||||
Term::Literal(_, Literal::F64(..))
|
||||
| Term::Literal(_, Literal::Rational(_))
|
||||
| Term::Literal(_, Literal::Integer(_))
|
||||
| Term::Literal(_, Literal::Fixnum(_)) => {
|
||||
Term::Literal(
|
||||
_,
|
||||
Literal::F64(..)
|
||||
| Literal::Rational(_)
|
||||
| Literal::Integer(_)
|
||||
| Literal::Fixnum(_),
|
||||
) => {
|
||||
instr!("$succeed")
|
||||
}
|
||||
Term::Var(ref vr, ref name) => {
|
||||
@@ -735,7 +733,7 @@ impl CodeGenerator {
|
||||
Term::Literal(_, Literal::Integer(_)) | Term::Literal(_, Literal::Fixnum(_)) => {
|
||||
instr!("$succeed")
|
||||
}
|
||||
Term::Var(ref vr, name) => {
|
||||
Term::Var(vr, name) => {
|
||||
self.marker.reset_arg(1);
|
||||
|
||||
let r = self.marker.mark_non_callable(
|
||||
@@ -787,11 +785,10 @@ impl CodeGenerator {
|
||||
fn compile_arith_expr(
|
||||
&mut self,
|
||||
term: &Term,
|
||||
target_int: usize,
|
||||
term_loc: GenContext,
|
||||
arg: usize,
|
||||
) -> Result<ArithCont, ArithmeticError> {
|
||||
let mut evaluator = ArithmeticEvaluator::new(&mut self.marker, target_int);
|
||||
let mut evaluator = ArithmeticEvaluator::new(&mut self.marker);
|
||||
evaluator.compile_is(term, term_loc, arg)
|
||||
}
|
||||
|
||||
@@ -804,7 +801,7 @@ impl CodeGenerator {
|
||||
) -> Result<(), CompilationError> {
|
||||
macro_rules! compile_expr {
|
||||
($self:expr, $terms:expr, $term_loc:expr, $code:expr) => {{
|
||||
let (acode, at) = $self.compile_arith_expr($terms, 1, $term_loc, 2)?;
|
||||
let (acode, at) = $self.compile_arith_expr($terms, $term_loc, 2)?;
|
||||
$code.extend(acode.into_iter());
|
||||
at
|
||||
}};
|
||||
@@ -831,7 +828,7 @@ impl CodeGenerator {
|
||||
self.marker
|
||||
.mark_anon_var::<QueryInstruction>(Level::Shallow, term_loc, code);
|
||||
|
||||
if let Term::Var(ref vr, ref var) = &terms[1] {
|
||||
if let Term::Var(vr, var) = &terms[1] {
|
||||
let var_num = var.to_var_num().unwrap();
|
||||
|
||||
// if var is an anonymous variable, insert
|
||||
@@ -859,16 +856,18 @@ impl CodeGenerator {
|
||||
}
|
||||
}
|
||||
Term::Literal(
|
||||
_,
|
||||
ref cell,
|
||||
c @ Literal::Integer(_)
|
||||
| c @ Literal::F64(..)
|
||||
| c @ Literal::Rational(_)
|
||||
| c @ Literal::Fixnum(_),
|
||||
) => {
|
||||
let v = HeapCellValue::from(c);
|
||||
code.push_back(instr!("put_constant", Level::Shallow, v, temp_v!(1)));
|
||||
|
||||
self.marker.advance_arg();
|
||||
self.marker
|
||||
.mark_non_var::<QueryInstruction>(Level::Shallow, term_loc, cell, code);
|
||||
|
||||
code.push_back(instr!("put_constant", Level::Shallow, v, temp_v!(1)));
|
||||
compile_expr!(self, &terms[1], term_loc, code)
|
||||
}
|
||||
_ => {
|
||||
@@ -877,7 +876,6 @@ impl CodeGenerator {
|
||||
}
|
||||
};
|
||||
|
||||
let at = at.unwrap_or(interm!(1));
|
||||
self.add_call(code, instr!("is", temp_v!(1), at), call_policy);
|
||||
Ok(())
|
||||
}
|
||||
@@ -996,20 +994,27 @@ impl CodeGenerator {
|
||||
self.marker.in_tail_position = false;
|
||||
self.marker.reset_contents();
|
||||
}
|
||||
ClauseItem::FirstBranch(num_branches) => {
|
||||
ClauseItem::FirstBranch {
|
||||
branch_num,
|
||||
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
|
||||
.branch_stack
|
||||
.add_branch_stack(branch_num.clone(), num_branches);
|
||||
self.marker.add_branch();
|
||||
}
|
||||
ClauseItem::NextBranch => {
|
||||
ClauseItem::NextBranch { branch_num } => {
|
||||
branch_code_stack.add_new_branch();
|
||||
|
||||
self.marker.add_branch();
|
||||
self.marker.branch_stack.incr_current_branch();
|
||||
self.marker
|
||||
.branch_stack
|
||||
.incr_current_branch(branch_num.clone());
|
||||
}
|
||||
ClauseItem::BranchEnd(depth) => {
|
||||
ClauseItem::BranchEnd { depth } => {
|
||||
if !clause_iter.in_tail_position() {
|
||||
let subsumed_hits =
|
||||
branch_code_stack.push_missing_vars(depth, &mut self.marker);
|
||||
@@ -1231,7 +1236,7 @@ impl CodeGenerator {
|
||||
}
|
||||
|
||||
self.skeleton.clauses.push_back(clause_index_info);
|
||||
code.extend(clause_code.into_iter());
|
||||
code.extend(clause_code);
|
||||
}
|
||||
|
||||
let index_code = if clauses_len > 1 || self.settings.is_extensible {
|
||||
@@ -1308,7 +1313,7 @@ impl CodeGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
code.extend(code_segment.into_iter());
|
||||
code.extend(code_segment);
|
||||
}
|
||||
|
||||
Ok(code)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::allocator::*;
|
||||
use crate::codegen::SubsumedBranchHits;
|
||||
use crate::forms::{GenContext, Level};
|
||||
use crate::forms::{BranchNumber, GenContext, Level};
|
||||
use crate::instructions::*;
|
||||
use crate::machine::disjuncts::VarData;
|
||||
use crate::parser::ast::*;
|
||||
@@ -15,6 +15,7 @@ use indexmap::IndexMap;
|
||||
use std::cell::Cell;
|
||||
use std::collections::VecDeque;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::Arc;
|
||||
|
||||
pub type BranchHits = IndexMap<usize, BitVec, FxBuildHasher>; // key: var_num, value: branch arm occurrences.
|
||||
|
||||
@@ -24,24 +25,26 @@ pub struct BranchOccurrences {
|
||||
pub shallow_safety: BitSet<usize>, // unset means safe, set means unsafe (after the branch merge)
|
||||
pub deep_safety: BitSet<usize>,
|
||||
pub num_branches: usize,
|
||||
pub current_branch: usize,
|
||||
pub current_branch_idx: usize,
|
||||
pub current_branch_num: Arc<BranchNumber>,
|
||||
pub subsumed_hits: SubsumedBranchHits,
|
||||
}
|
||||
|
||||
impl BranchOccurrences {
|
||||
fn new(num_branches: usize) -> Self {
|
||||
fn new(current_branch_num: Arc<BranchNumber>, num_branches: usize) -> Self {
|
||||
Self {
|
||||
hits: BranchHits::with_hasher(FxBuildHasher::default()),
|
||||
shallow_safety: BitSet::default(),
|
||||
deep_safety: BitSet::default(),
|
||||
num_branches,
|
||||
current_branch: 0,
|
||||
current_branch_idx: 0,
|
||||
current_branch_num,
|
||||
subsumed_hits: SubsumedBranchHits::with_hasher(FxBuildHasher::default()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_branch_occurrence(&mut self, var_num: usize) {
|
||||
debug_assert!(self.current_branch < self.num_branches);
|
||||
debug_assert!(self.current_branch_idx < self.num_branches);
|
||||
let num_branches = self.num_branches;
|
||||
|
||||
let entry = self
|
||||
@@ -49,7 +52,7 @@ impl BranchOccurrences {
|
||||
.entry(var_num)
|
||||
.or_insert_with(|| BitVec::repeat(false, num_branches));
|
||||
|
||||
entry.set(self.current_branch, true);
|
||||
entry.set(self.current_branch_idx, true);
|
||||
self.subsumed_hits.insert(var_num);
|
||||
}
|
||||
}
|
||||
@@ -76,19 +79,6 @@ impl DerefMut for BranchStack {
|
||||
}
|
||||
|
||||
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,
|
||||
@@ -96,9 +86,9 @@ impl BranchStack {
|
||||
) -> bool {
|
||||
match safety {
|
||||
VarSafetyStatus::Needed => false,
|
||||
VarSafetyStatus::LocallyUnneeded(planter_branch) => {
|
||||
self.branch_subsumes(planter_branch, branch)
|
||||
}
|
||||
VarSafetyStatus::LocallyUnneeded(planter_branch) => planter_branch
|
||||
.branch_num
|
||||
.has_as_subbranch(&branch.branch_num),
|
||||
VarSafetyStatus::GloballyUnneeded => true,
|
||||
}
|
||||
}
|
||||
@@ -109,27 +99,24 @@ impl BranchStack {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_branch_stack(&mut self, num_branches: usize) {
|
||||
self.push(BranchOccurrences::new(num_branches));
|
||||
pub(crate) fn add_branch_stack(&mut self, branch_num: Arc<BranchNumber>, num_branches: usize) {
|
||||
self.push(BranchOccurrences::new(branch_num, 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);
|
||||
.map(|occurrences| occurrences.current_branch_num.clone())
|
||||
.unwrap_or_default();
|
||||
|
||||
BranchDesignator {
|
||||
branch_stack_num,
|
||||
branch_num,
|
||||
}
|
||||
BranchDesignator { branch_num }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn incr_current_branch(&mut self) {
|
||||
pub(crate) fn incr_current_branch(&mut self, branch_num: Arc<BranchNumber>) {
|
||||
let branch_occurrences = self.last_mut().unwrap();
|
||||
branch_occurrences.current_branch += 1;
|
||||
branch_occurrences.current_branch_idx += 1;
|
||||
branch_occurrences.current_branch_num = branch_num;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -171,7 +158,7 @@ impl DebrayAllocator {
|
||||
|
||||
for var_num in subsumed_hits {
|
||||
match &mut self.var_data.records[var_num].allocation {
|
||||
VarAlloc::Perm(_, ref mut allocation) => {
|
||||
VarAlloc::Perm(_, allocation) => {
|
||||
if let PermVarAllocation::Done {
|
||||
shallow_safety,
|
||||
deep_safety,
|
||||
@@ -215,7 +202,7 @@ impl DebrayAllocator {
|
||||
},
|
||||
);
|
||||
|
||||
let branch_designator = self.branch_stack.current_branch_designator();
|
||||
let branch_designator = Arc::new(self.branch_stack.current_branch_designator());
|
||||
|
||||
let (deep_safety, shallow_safety) = match self.branch_stack.last_mut() {
|
||||
Some(latest_branch) => {
|
||||
@@ -235,12 +222,12 @@ impl DebrayAllocator {
|
||||
VarAlloc::Perm(_, allocation) => {
|
||||
let shallow_safety = VarSafetyStatus::needed_if(
|
||||
shallow_safety.contains(var_num),
|
||||
branch_designator,
|
||||
&branch_designator,
|
||||
);
|
||||
|
||||
let deep_safety = VarSafetyStatus::needed_if(
|
||||
deep_safety.contains(var_num),
|
||||
branch_designator,
|
||||
&branch_designator,
|
||||
);
|
||||
|
||||
if running_count < num_occurrences {
|
||||
@@ -520,7 +507,7 @@ impl DebrayAllocator {
|
||||
}
|
||||
|
||||
pub(crate) fn mark_safe_var_unconditionally(&mut self, var_num: usize) {
|
||||
let branch_designator = self.branch_stack.current_branch_designator();
|
||||
let branch_designator = Arc::new(self.branch_stack.current_branch_designator());
|
||||
|
||||
match &mut self.var_data.records[var_num].allocation {
|
||||
VarAlloc::Perm(
|
||||
@@ -531,11 +518,11 @@ impl DebrayAllocator {
|
||||
..
|
||||
},
|
||||
) => {
|
||||
*deep_safety = VarSafetyStatus::unneeded(branch_designator);
|
||||
*shallow_safety = VarSafetyStatus::unneeded(branch_designator);
|
||||
*deep_safety = VarSafetyStatus::unneeded(&branch_designator);
|
||||
*shallow_safety = VarSafetyStatus::unneeded(&branch_designator);
|
||||
}
|
||||
VarAlloc::Temp { safety, .. } => {
|
||||
*safety = VarSafetyStatus::unneeded(branch_designator);
|
||||
*safety = VarSafetyStatus::unneeded(&branch_designator);
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
@@ -544,7 +531,7 @@ impl DebrayAllocator {
|
||||
}
|
||||
|
||||
fn mark_safe_var(&mut self, var_num: usize, lvl: Level, term_loc: GenContext) {
|
||||
let branch_designator = self.branch_stack.current_branch_designator();
|
||||
let branch_designator = Arc::new(self.branch_stack.current_branch_designator());
|
||||
|
||||
match &mut self.var_data.records[var_num].allocation {
|
||||
VarAlloc::Perm(
|
||||
@@ -557,15 +544,14 @@ impl DebrayAllocator {
|
||||
) => {
|
||||
// GetVariable in head chunk is considered safe.
|
||||
if lvl == Level::Deep {
|
||||
*deep_safety = VarSafetyStatus::unneeded(branch_designator);
|
||||
*shallow_safety = VarSafetyStatus::unneeded(branch_designator);
|
||||
*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) {
|
||||
match &mut self.var_data.records[temp_var_num].allocation {
|
||||
VarAlloc::Temp {
|
||||
ref mut to_perm_var_num,
|
||||
..
|
||||
to_perm_var_num, ..
|
||||
} => {
|
||||
*to_perm_var_num = Some(var_num);
|
||||
}
|
||||
@@ -573,7 +559,7 @@ impl DebrayAllocator {
|
||||
}
|
||||
}
|
||||
}
|
||||
VarAlloc::Temp { ref mut safety, .. } => {
|
||||
VarAlloc::Temp { safety, .. } => {
|
||||
*safety = VarSafetyStatus::GloballyUnneeded;
|
||||
}
|
||||
_ => {
|
||||
@@ -588,16 +574,10 @@ impl DebrayAllocator {
|
||||
r: RegType,
|
||||
arg_c: usize,
|
||||
) -> Instruction {
|
||||
let branch_designator = self.branch_stack.current_branch_designator();
|
||||
let branch_designator = Arc::new(self.branch_stack.current_branch_designator());
|
||||
|
||||
match &mut self.var_data.records[var_num].allocation {
|
||||
VarAlloc::Perm(
|
||||
_,
|
||||
PermVarAllocation::Done {
|
||||
ref mut shallow_safety,
|
||||
..
|
||||
},
|
||||
) => {
|
||||
VarAlloc::Perm(_, PermVarAllocation::Done { shallow_safety, .. }) => {
|
||||
if !self.in_tail_position
|
||||
|| self
|
||||
.branch_stack
|
||||
@@ -605,7 +585,7 @@ impl DebrayAllocator {
|
||||
{
|
||||
Target::argument_to_value(r, arg_c)
|
||||
} else {
|
||||
*shallow_safety = VarSafetyStatus::unneeded(branch_designator);
|
||||
*shallow_safety = VarSafetyStatus::unneeded(&branch_designator);
|
||||
Target::unsafe_argument_to_value(r, arg_c)
|
||||
}
|
||||
}
|
||||
@@ -624,34 +604,28 @@ impl DebrayAllocator {
|
||||
var_num: usize,
|
||||
r: RegType,
|
||||
) -> Instruction {
|
||||
let branch_designator = self.branch_stack.current_branch_designator();
|
||||
let branch_designator = Arc::new(self.branch_stack.current_branch_designator());
|
||||
|
||||
match &mut self.var_data.records[var_num].allocation {
|
||||
VarAlloc::Perm(
|
||||
_,
|
||||
PermVarAllocation::Done {
|
||||
ref mut deep_safety,
|
||||
..
|
||||
},
|
||||
) => {
|
||||
VarAlloc::Perm(_, PermVarAllocation::Done { 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);
|
||||
*deep_safety = VarSafetyStatus::unneeded(&branch_designator);
|
||||
Target::unsafe_subterm_to_value(r)
|
||||
}
|
||||
}
|
||||
VarAlloc::Temp { ref mut safety, .. } => {
|
||||
VarAlloc::Temp { safety, .. } => {
|
||||
if self
|
||||
.branch_stack
|
||||
.safety_unneeded_in_branch(safety, &branch_designator)
|
||||
{
|
||||
Target::subterm_to_value(r)
|
||||
} else {
|
||||
*safety = VarSafetyStatus::unneeded(branch_designator);
|
||||
*safety = VarSafetyStatus::unneeded(&branch_designator);
|
||||
Target::unsafe_subterm_to_value(r)
|
||||
}
|
||||
}
|
||||
@@ -887,7 +861,7 @@ impl Allocator for DebrayAllocator {
|
||||
self.arity = args.len();
|
||||
|
||||
for (idx, arg) in args.iter().enumerate() {
|
||||
if let Term::Var(_, ref var) = arg {
|
||||
if let Term::Var(_, var) = arg {
|
||||
let var_num = var.to_var_num().unwrap();
|
||||
let r = self.get_binding(var_num);
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
:- use_module(library(charsio)).
|
||||
:- use_module(library(lists)).
|
||||
:- use_module(library(pio)).
|
||||
:- use_module(library(dcgs)).
|
||||
|
||||
:- initialization(unit_test).
|
||||
|
||||
@@ -10,22 +12,12 @@ unit_test :-
|
||||
Cs = "a£\x2124\".
|
||||
|
||||
write_f :-
|
||||
open('x.txt', write, Stream, [type(binary)]),
|
||||
F = put_byte(Stream),
|
||||
chars_utf8bytes("£\x2124\\x2764\\x1F496\\n", Bs),
|
||||
maplist(F, Bs),
|
||||
close(Stream).
|
||||
|
||||
get_bytes(Stream, Res) :- get_bytes(Stream, [], Res).
|
||||
get_bytes(Stream, Acc, Res) :-
|
||||
get_byte(Stream, B),
|
||||
(B =:= -1 ->
|
||||
reverse(Acc, Res)
|
||||
; get_bytes(Stream, [B|Acc], Res)).
|
||||
maplist(char_code, Cs, Bs),
|
||||
phrase_to_file(Cs, "x.txt", [type(binary)]).
|
||||
|
||||
read_f :-
|
||||
open('x.txt', read, Stream, [type(binary)]),
|
||||
get_bytes(Stream, Bs),
|
||||
chars_utf8bytes(Cs, Bs),
|
||||
write(Cs),
|
||||
close(Stream).
|
||||
phrase_from_file(seq(Cs), "x.txt", [type(binary)]),
|
||||
maplist(char_code, Cs, Bs),
|
||||
chars_utf8bytes(Chars, Bs),
|
||||
write(Chars).
|
||||
|
||||
228
src/ffi.rs
228
src/ffi.rs
@@ -26,17 +26,16 @@ use crate::machine::machine_errors::DomainErrorType;
|
||||
use crate::parser::ast::{Fixnum, MightNotFitInFixnum};
|
||||
|
||||
use dashu::Integer;
|
||||
use libffi::middle::{Arg, Cif, CodePtr, Type};
|
||||
use libffi::middle::{Arg, Cif, CodePtr, Ret, Type};
|
||||
use libloading::{Library, Symbol};
|
||||
use ordered_float::OrderedFloat;
|
||||
use std::alloc::{self, Layout};
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error;
|
||||
use std::ffi::{c_char, c_void, CStr, CString};
|
||||
use std::ffi::{CStr, CString, c_char, c_void};
|
||||
use std::fmt::Debug;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::ops::Deref;
|
||||
use std::ptr::NonNull;
|
||||
|
||||
pub struct FunctionDefinition {
|
||||
@@ -55,7 +54,9 @@ pub struct FunctionImpl {
|
||||
|
||||
impl FunctionImpl {
|
||||
unsafe fn call_void(&self, args: &[Arg], _: &mut Arena) -> Result<Value, FfiError> {
|
||||
self.cif.call::<()>(self.code_ptr, args);
|
||||
unsafe {
|
||||
self.cif.call_return_into(self.code_ptr, args, Ret::void());
|
||||
}
|
||||
Ok(Value::Number(Number::Fixnum(Fixnum::build_with(0))))
|
||||
}
|
||||
|
||||
@@ -64,7 +65,7 @@ impl FunctionImpl {
|
||||
Integer: From<T>,
|
||||
T: Copy + TryInto<i64> + MightNotFitInFixnum,
|
||||
{
|
||||
let n = self.cif.call::<T>(self.code_ptr, args);
|
||||
let n = unsafe { self.cif.call::<T>(self.code_ptr, args) };
|
||||
Ok(Value::Number(fixnum!(Number, n, arena)))
|
||||
}
|
||||
|
||||
@@ -72,7 +73,7 @@ impl FunctionImpl {
|
||||
where
|
||||
T: Into<f64>,
|
||||
{
|
||||
let n = self.cif.call::<T>(self.code_ptr, args);
|
||||
let n = unsafe { self.cif.call::<T>(self.code_ptr, args) };
|
||||
Ok(Value::Number(Number::Float(OrderedFloat(n.into()))))
|
||||
}
|
||||
|
||||
@@ -114,12 +115,8 @@ impl FunctionImpl {
|
||||
let alloc = FfiStruct::new(layout, FfiAllocator::Rust)?;
|
||||
|
||||
unsafe {
|
||||
libffi::raw::ffi_call(
|
||||
self.cif.as_raw_ptr(),
|
||||
Some(*self.code_ptr.as_safe_fun()),
|
||||
alloc.ptr.as_ptr(),
|
||||
args.as_ptr() as *mut *mut c_void,
|
||||
)
|
||||
self.cif
|
||||
.call_return_into(self.code_ptr, args, alloc.as_ret())
|
||||
};
|
||||
|
||||
let struct_val =
|
||||
@@ -152,7 +149,7 @@ impl FunctionImpl {
|
||||
FfiType::Ptr => FunctionImpl::call_ptr,
|
||||
FfiType::CStr => FunctionImpl::call_cstr,
|
||||
FfiType::Struct(name) => {
|
||||
return unsafe { self.call_struct(name, args, arena, structs_table) }
|
||||
return unsafe { self.call_struct(name, args, arena, structs_table) };
|
||||
}
|
||||
};
|
||||
unsafe { call_fn(self, args, arena) }
|
||||
@@ -180,10 +177,17 @@ impl StructImpl {
|
||||
|
||||
fn build(
|
||||
&self,
|
||||
name: Atom,
|
||||
structs_table: &HashMap<Atom, StructImpl>,
|
||||
struct_args: &mut [Value],
|
||||
) -> Result<FfiStruct, FfiError> {
|
||||
let args = ArgValue::build_args(struct_args, &self.fields, structs_table)?;
|
||||
let args = ArgValue::build_args(
|
||||
name,
|
||||
ArgCountMismatchKind::Struct,
|
||||
struct_args,
|
||||
&self.fields,
|
||||
structs_table,
|
||||
)?;
|
||||
|
||||
let alloc = FfiStruct::new(self.layout()?, FfiAllocator::Rust)?;
|
||||
|
||||
@@ -200,7 +204,9 @@ impl StructImpl {
|
||||
.extend(Layout::new::<T>())
|
||||
.map_err(|_| FfiError::LayoutError)?;
|
||||
*layout = new_layout;
|
||||
ptr.byte_offset(offset as isize).cast::<T>().write(val);
|
||||
unsafe {
|
||||
ptr.byte_offset(offset as isize).cast::<T>().write(val);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -261,7 +267,7 @@ impl StructImpl {
|
||||
.extend(Layout::new::<T>())
|
||||
.map_err(|_| FfiError::LayoutError)?;
|
||||
*layout = new_layout;
|
||||
let n = std::ptr::read::<T>(ptr.byte_offset(offset as isize).cast());
|
||||
let n = unsafe { std::ptr::read::<T>(ptr.byte_offset(offset as isize).cast()) };
|
||||
Ok(n)
|
||||
}
|
||||
|
||||
@@ -274,7 +280,7 @@ impl StructImpl {
|
||||
T: Copy + TryInto<i64> + MightNotFitInFixnum,
|
||||
Integer: From<T>,
|
||||
{
|
||||
let n = read_primitive::<T>(ptr, layout)?;
|
||||
let n = unsafe { read_primitive::<T>(ptr, layout)? };
|
||||
Ok(Value::Number(fixnum!(Number, n, arena)))
|
||||
}
|
||||
|
||||
@@ -285,7 +291,7 @@ impl StructImpl {
|
||||
where
|
||||
T: Into<f64>,
|
||||
{
|
||||
let n = read_primitive::<T>(ptr, layout)?;
|
||||
let n = unsafe { read_primitive::<T>(ptr, layout)? };
|
||||
Ok(Value::Number(Number::Float(OrderedFloat(n.into()))))
|
||||
}
|
||||
|
||||
@@ -329,7 +335,9 @@ impl StructImpl {
|
||||
substruct_type.read(field_ptr, *substruct, struct_table, arena)?;
|
||||
Ok(struct_val)
|
||||
}
|
||||
FfiType::Void => return Err(FfiError::VoidArgumentType),
|
||||
FfiType::Void => {
|
||||
return Err(FfiError::UnsupportedArgumentType(Some(atom!("void"))));
|
||||
}
|
||||
};
|
||||
returns.push(val?);
|
||||
}
|
||||
@@ -338,48 +346,6 @@ impl StructImpl {
|
||||
}
|
||||
}
|
||||
|
||||
struct PointerArgs<'a, 'val> {
|
||||
memory: Vec<Arg>,
|
||||
phantom: PhantomData<&'a mut ArgValue<'val>>,
|
||||
}
|
||||
|
||||
impl<'args, 'val> PointerArgs<'args, 'val> {
|
||||
fn new(args: &'args [ArgValue<'val>]) -> Self {
|
||||
let args = args
|
||||
.iter()
|
||||
.map(|arg| match arg {
|
||||
ArgValue::U8(a) => libffi::middle::arg(a),
|
||||
ArgValue::I8(a) => libffi::middle::arg(a),
|
||||
ArgValue::U16(a) => libffi::middle::arg(a),
|
||||
ArgValue::I16(a) => libffi::middle::arg(a),
|
||||
ArgValue::U32(a) => libffi::middle::arg(a),
|
||||
ArgValue::I32(a) => libffi::middle::arg(a),
|
||||
ArgValue::U64(a) => libffi::middle::arg(a),
|
||||
ArgValue::I64(a) => libffi::middle::arg(a),
|
||||
ArgValue::F32(a) => libffi::middle::arg(a),
|
||||
ArgValue::F64(a) => libffi::middle::arg(a),
|
||||
ArgValue::Ptr(ptr, _) => Arg::new(ptr),
|
||||
ArgValue::Struct(s) => unsafe {
|
||||
std::mem::transmute::<*mut c_void, Arg>(s.ptr.as_ptr())
|
||||
},
|
||||
})
|
||||
.collect();
|
||||
|
||||
PointerArgs {
|
||||
memory: args,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for PointerArgs<'_, '_> {
|
||||
type Target = [Arg];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.memory
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum FfiType {
|
||||
Void,
|
||||
@@ -399,9 +365,49 @@ enum FfiType {
|
||||
Struct(Atom),
|
||||
}
|
||||
|
||||
trait ToFfiType {
|
||||
const TYPE: FfiType;
|
||||
}
|
||||
|
||||
macro_rules! impl_to_ffi_type {
|
||||
($($t:ty => $v:ident);*$(;)?) => {
|
||||
$(
|
||||
impl ToFfiType for $t {
|
||||
const TYPE: FfiType = FfiType::$v;
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
impl_to_ffi_type!(
|
||||
u8 => U8;
|
||||
i8 => I8;
|
||||
u16 => U16;
|
||||
i16 => I16;
|
||||
u32 => U32;
|
||||
i32 => I32;
|
||||
u64 => U64;
|
||||
i64 => I64;
|
||||
f32 => F32;
|
||||
f64 => F64;
|
||||
);
|
||||
|
||||
impl FfiType {
|
||||
fn from_atom(atom: &Atom) -> Self {
|
||||
match atom {
|
||||
atom!("char") => <core::ffi::c_char as ToFfiType>::TYPE,
|
||||
atom!("uchar") => <core::ffi::c_uchar as ToFfiType>::TYPE,
|
||||
atom!("schar") => <core::ffi::c_schar as ToFfiType>::TYPE,
|
||||
atom!("short") => <core::ffi::c_short as ToFfiType>::TYPE,
|
||||
atom!("ushort") => <core::ffi::c_ushort as ToFfiType>::TYPE,
|
||||
atom!("int") => <core::ffi::c_int as ToFfiType>::TYPE,
|
||||
atom!("uint") => <core::ffi::c_uint as ToFfiType>::TYPE,
|
||||
atom!("long") => <core::ffi::c_long as ToFfiType>::TYPE,
|
||||
atom!("ulong") => <core::ffi::c_ulong as ToFfiType>::TYPE,
|
||||
atom!("longlong") => <core::ffi::c_longlong as ToFfiType>::TYPE,
|
||||
atom!("ulonglong") => <core::ffi::c_ulonglong as ToFfiType>::TYPE,
|
||||
atom!("float") => <core::ffi::c_float as ToFfiType>::TYPE,
|
||||
atom!("double") => <core::ffi::c_double as ToFfiType>::TYPE,
|
||||
atom!("sint64") | atom!("i64") => Self::I64,
|
||||
atom!("sint32") | atom!("i32") => Self::I32,
|
||||
atom!("sint16") | atom!("i16") => Self::I16,
|
||||
@@ -490,19 +496,30 @@ impl<'val> ArgValue<'val> {
|
||||
return Err(FfiError::StructNotFound(*arg_type_name));
|
||||
};
|
||||
|
||||
Ok(Self::Struct(struct_type.build(structs_table, args)?))
|
||||
Ok(Self::Struct(struct_type.build(
|
||||
*arg_type_name,
|
||||
structs_table,
|
||||
args,
|
||||
)?))
|
||||
}
|
||||
FfiType::Void => Err(FfiError::VoidArgumentType),
|
||||
FfiType::Void => Err(FfiError::UnsupportedArgumentType(Some(atom!("void")))),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_args(
|
||||
name: Atom,
|
||||
kind: ArgCountMismatchKind,
|
||||
args: &'val mut [Value],
|
||||
types: &[FfiType],
|
||||
structs_table: &HashMap<Atom, StructImpl>,
|
||||
) -> Result<Vec<Self>, FfiError> {
|
||||
if types.len() != args.len() {
|
||||
return Err(FfiError::ArgCountMismatch);
|
||||
return Err(FfiError::ArgCountMismatch {
|
||||
name,
|
||||
kind,
|
||||
expected: types.len(),
|
||||
got: args.len(),
|
||||
});
|
||||
}
|
||||
|
||||
args.iter_mut()
|
||||
@@ -510,6 +527,28 @@ impl<'val> ArgValue<'val> {
|
||||
.map(|(arg, arg_type)| ArgValue::new(arg, arg_type, structs_table))
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
}
|
||||
|
||||
fn as_arg<'res, 'this: 'res>(&'this self) -> Arg<'res>
|
||||
where
|
||||
'val: 'res,
|
||||
{
|
||||
match self {
|
||||
ArgValue::U8(a) => libffi::middle::arg(a),
|
||||
ArgValue::I8(a) => libffi::middle::arg(a),
|
||||
ArgValue::U16(a) => libffi::middle::arg(a),
|
||||
ArgValue::I16(a) => libffi::middle::arg(a),
|
||||
ArgValue::U32(a) => libffi::middle::arg(a),
|
||||
ArgValue::I32(a) => libffi::middle::arg(a),
|
||||
ArgValue::U64(a) => libffi::middle::arg(a),
|
||||
ArgValue::I64(a) => libffi::middle::arg(a),
|
||||
ArgValue::F32(a) => libffi::middle::arg(a),
|
||||
ArgValue::F64(a) => libffi::middle::arg(a),
|
||||
ArgValue::Ptr(ptr, _) => Arg::new(ptr),
|
||||
ArgValue::Struct(s) => unsafe {
|
||||
std::mem::transmute::<*mut c_void, Arg>(s.ptr.as_ptr())
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct FfiStruct {
|
||||
@@ -570,6 +609,10 @@ impl FfiStruct {
|
||||
allocator,
|
||||
})
|
||||
}
|
||||
|
||||
fn as_ret(&self) -> libffi::middle::Ret<'_> {
|
||||
unsafe { std::mem::transmute::<*mut c_void, Ret>(self.ptr.as_ptr()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for FfiStruct {
|
||||
@@ -659,11 +702,17 @@ impl ForeignFunctionTable {
|
||||
let fn_impl = self
|
||||
.table
|
||||
.get(&fn_name)
|
||||
.ok_or(FfiError::FunctionNotFound(fn_name))?;
|
||||
.ok_or(FfiError::FunctionNotFound(fn_name, args.len()))?;
|
||||
|
||||
let args = ArgValue::build_args(&mut args, &fn_impl.args, &self.structs)?;
|
||||
let args = ArgValue::build_args(
|
||||
fn_name,
|
||||
ArgCountMismatchKind::Function,
|
||||
&mut args,
|
||||
&fn_impl.args,
|
||||
&self.structs,
|
||||
)?;
|
||||
|
||||
let args = PointerArgs::new(&args);
|
||||
let args: Vec<_> = args.iter().map(|v| v.as_arg()).collect();
|
||||
|
||||
fn_impl.call(&args, arena, &self.structs)
|
||||
}
|
||||
@@ -691,9 +740,9 @@ impl ForeignFunctionTable {
|
||||
}
|
||||
|
||||
match FfiType::from_atom(&kind) {
|
||||
FfiType::Void => Err(FfiError::VoidArgumentType),
|
||||
FfiType::Void => Err(FfiError::UnsupportedArgumentType(Some(atom!("void")))),
|
||||
FfiType::Bool => {
|
||||
let val = args.as_int::<u8>()?;
|
||||
let val = args.as_int::<i8>()?;
|
||||
let init = match val {
|
||||
0 => false,
|
||||
1 => true,
|
||||
@@ -720,7 +769,7 @@ impl ForeignFunctionTable {
|
||||
|
||||
let (_, args) = args.as_struct()?;
|
||||
|
||||
let ffi_struct = struct_impl.build(&self.structs, args)?;
|
||||
let ffi_struct = struct_impl.build(kind, &self.structs, args)?;
|
||||
|
||||
let ptr = ManuallyDrop::new(ffi_struct).ptr;
|
||||
|
||||
@@ -744,7 +793,7 @@ impl ForeignFunctionTable {
|
||||
T: Copy + TryInto<i64> + MightNotFitInFixnum,
|
||||
Integer: From<T>,
|
||||
{
|
||||
let n = ptr.cast::<T>().read();
|
||||
let n = unsafe { ptr.cast::<T>().read() };
|
||||
Value::Number(fixnum!(Number, n, arena))
|
||||
}
|
||||
|
||||
@@ -755,9 +804,9 @@ impl ForeignFunctionTable {
|
||||
};
|
||||
|
||||
match FfiType::from_atom(&kind) {
|
||||
FfiType::Void => Err(FfiError::VoidArgumentType),
|
||||
FfiType::Bool | FfiType::U8 => Ok(unsafe { read_int::<u8>(ptr, arena) }),
|
||||
FfiType::I8 => Ok(unsafe { read_int::<i8>(ptr, arena) }),
|
||||
FfiType::Void => Err(FfiError::UnsupportedArgumentType(Some(atom!("void")))),
|
||||
FfiType::U8 => Ok(unsafe { read_int::<u8>(ptr, arena) }),
|
||||
FfiType::Bool | FfiType::I8 => Ok(unsafe { read_int::<i8>(ptr, arena) }),
|
||||
FfiType::U16 => Ok(unsafe { read_int::<u16>(ptr, arena) }),
|
||||
FfiType::I16 => Ok(unsafe { read_int::<i16>(ptr, arena) }),
|
||||
FfiType::U32 => Ok(unsafe { read_int::<u32>(ptr, arena) }),
|
||||
@@ -805,7 +854,7 @@ impl ForeignFunctionTable {
|
||||
};
|
||||
|
||||
match FfiType::from_atom(&kind) {
|
||||
FfiType::Void => return Err(FfiError::VoidArgumentType),
|
||||
FfiType::Void => return Err(FfiError::UnsupportedArgumentType(Some(atom!("void")))),
|
||||
FfiType::Bool => deallocate_primitive::<bool>(allocator, ptr),
|
||||
FfiType::U8 => deallocate_primitive::<u8>(allocator, ptr),
|
||||
FfiType::I8 => deallocate_primitive::<i8>(allocator, ptr),
|
||||
@@ -879,7 +928,7 @@ impl Value {
|
||||
|
||||
fn as_ptr(&mut self) -> Result<*mut c_void, FfiError> {
|
||||
match self {
|
||||
Value::CString(ref mut cstr) => Ok(cstr.as_ptr().cast_mut().cast()),
|
||||
Value::CString(cstr) => Ok(cstr.as_ptr().cast_mut().cast()),
|
||||
Value::Number(Number::Fixnum(fixnum)) => Ok(std::ptr::with_exposed_provenance_mut(
|
||||
fixnum.get_num() as usize,
|
||||
)),
|
||||
@@ -902,20 +951,37 @@ impl Value {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum FfiError {
|
||||
ValueCast(Atom, Atom),
|
||||
ValueOutOfRange(DomainErrorType, Value),
|
||||
VoidArgumentType,
|
||||
FunctionNotFound(Atom),
|
||||
UnsupportedArgumentType(Option<Atom>),
|
||||
FunctionNotFound(Atom, usize),
|
||||
StructNotFound(Atom),
|
||||
ArgCountMismatch,
|
||||
ArgCountMismatch {
|
||||
name: Atom, // ffi function or struct
|
||||
kind: ArgCountMismatchKind,
|
||||
|
||||
#[allow(dead_code, reason = "will be used by PR 3173")]
|
||||
expected: usize,
|
||||
got: usize,
|
||||
},
|
||||
AllocationFailed,
|
||||
// LayoutError should never occour
|
||||
// LayoutError should never occur
|
||||
LayoutError,
|
||||
UnsupportedTypedef,
|
||||
UnsupportedAbi,
|
||||
CStrFieldType,
|
||||
NullPtr,
|
||||
#[doc(hidden)]
|
||||
#[non_exhaustive]
|
||||
Other,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum ArgCountMismatchKind {
|
||||
Function,
|
||||
Struct,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for FfiError {
|
||||
@@ -931,6 +997,8 @@ impl From<libffi::low::Error> for FfiError {
|
||||
match value {
|
||||
libffi::low::Error::Typedef => FfiError::UnsupportedTypedef,
|
||||
libffi::low::Error::Abi => FfiError::UnsupportedAbi,
|
||||
libffi::low::Error::ArgType => FfiError::UnsupportedArgumentType(None),
|
||||
_ => FfiError::Other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
132
src/forms.rs
132
src/forms.rs
@@ -18,11 +18,14 @@ use indexmap::{IndexMap, IndexSet};
|
||||
use ordered_float::OrderedFloat;
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::VecDeque;
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::{AddAssign, Deref, DerefMut};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub type PredicateKey = (Atom, usize); // name, arity.
|
||||
|
||||
@@ -42,12 +45,6 @@ impl AppendOrPrepend {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum VarComparison {
|
||||
Indistinct,
|
||||
Distinct,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum Level {
|
||||
Deep,
|
||||
@@ -128,10 +125,82 @@ impl ChunkType {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)] //, PartialOrd, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct BranchNumber {
|
||||
pub(crate) branch_num: Rational,
|
||||
pub(crate) delta: Rational,
|
||||
}
|
||||
|
||||
impl Default for BranchNumber {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
branch_num: Rational::from(0),
|
||||
delta: Rational::from(1u64 << 31),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<BranchNumber> 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<H: Hasher>(&self, hasher: &mut H) {
|
||||
self.branch_num.hash(hasher)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<BranchNumber> for BranchNumber {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, rhs: &BranchNumber) -> Option<Ordering> {
|
||||
self.branch_num.partial_cmp(&rhs.branch_num)
|
||||
}
|
||||
}
|
||||
|
||||
impl BranchNumber {
|
||||
pub(crate) fn has_as_subbranch(&self, other: &Self) -> bool {
|
||||
other.delta <= self.delta
|
||||
&& other.branch_num >= self.branch_num
|
||||
&& other.branch_num < &self.branch_num + &self.delta
|
||||
}
|
||||
|
||||
pub(crate) fn split(&self) -> BranchNumber {
|
||||
BranchNumber {
|
||||
branch_num: self.branch_num.clone() + &self.delta / Rational::from(2),
|
||||
delta: &self.delta / Rational::from(4),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn incr_by_delta(&self) -> BranchNumber {
|
||||
BranchNumber {
|
||||
branch_num: self.branch_num.clone() + &self.delta,
|
||||
delta: self.delta.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn halve_delta(&self) -> BranchNumber {
|
||||
BranchNumber {
|
||||
branch_num: self.branch_num.clone(),
|
||||
delta: &self.delta / Rational::from(2),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ChunkedTerms {
|
||||
Branch(Vec<VecDeque<ChunkedTerms>>),
|
||||
Chunk { terms: VecDeque<QueryTerm> },
|
||||
Branch {
|
||||
branch_nums: Vec<Arc<BranchNumber>>,
|
||||
arms: Vec<VecDeque<ChunkedTerms>>,
|
||||
},
|
||||
Chunk {
|
||||
terms: VecDeque<QueryTerm>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -165,21 +234,22 @@ impl ChunkedTermVec {
|
||||
}
|
||||
|
||||
pub fn reserve_branch(&mut self, capacity: usize) {
|
||||
self.chunk_vec
|
||||
.push_back(ChunkedTerms::Branch(Vec::with_capacity(capacity)));
|
||||
self.chunk_vec.push_back(ChunkedTerms::Branch {
|
||||
branch_nums: Vec::with_capacity(capacity),
|
||||
arms: Vec::with_capacity(capacity),
|
||||
});
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn add_chunk(&mut self) {
|
||||
let chunk = ChunkedTerms::Chunk {
|
||||
self.chunk_vec.push_back(ChunkedTerms::Chunk {
|
||||
terms: VecDeque::from(vec![]),
|
||||
};
|
||||
self.chunk_vec.push_back(chunk);
|
||||
});
|
||||
}
|
||||
|
||||
pub fn push_chunk_term(&mut self, term: QueryTerm) {
|
||||
match self.chunk_vec.back_mut() {
|
||||
Some(ChunkedTerms::Branch(_)) => {
|
||||
Some(ChunkedTerms::Branch { .. }) => {
|
||||
let chunk = ChunkedTerms::Chunk {
|
||||
terms: VecDeque::from(vec![term]),
|
||||
};
|
||||
@@ -319,15 +389,15 @@ impl ClauseInfo for Rule {
|
||||
impl ClauseInfo for PredicateClause {
|
||||
fn name(&self) -> Option<Atom> {
|
||||
match self {
|
||||
PredicateClause::Fact(ref term, ..) => term.head.name(),
|
||||
PredicateClause::Rule(ref rule, ..) => rule.name(),
|
||||
PredicateClause::Fact(term, ..) => term.head.name(),
|
||||
PredicateClause::Rule(rule, ..) => rule.name(),
|
||||
}
|
||||
}
|
||||
|
||||
fn arity(&self) -> usize {
|
||||
match self {
|
||||
PredicateClause::Fact(ref term, ..) => term.head.arity(),
|
||||
PredicateClause::Rule(ref rule, ..) => rule.arity(),
|
||||
PredicateClause::Fact(term, ..) => term.head.arity(),
|
||||
PredicateClause::Rule(rule, ..) => rule.arity(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -512,7 +582,7 @@ pub(crate) fn fetch_op_spec_from_existing(
|
||||
op_desc: Option<OpDesc>,
|
||||
op_dir: &OpDir,
|
||||
) -> Option<OpDesc> {
|
||||
if let Some(ref op_desc) = &op_desc {
|
||||
if let Some(op_desc) = &op_desc {
|
||||
if op_desc.arity() != arity {
|
||||
/* it's possible to extend operator functors with
|
||||
* additional terms. When that happens,
|
||||
@@ -773,9 +843,9 @@ impl Number {
|
||||
pub(crate) fn is_positive(&self) -> bool {
|
||||
match self {
|
||||
Number::Fixnum(n) => n.get_num() > 0,
|
||||
Number::Integer(ref n) => n.is_positive(),
|
||||
Number::Integer(n) => n.is_positive(),
|
||||
Number::Float(f) => f.is_sign_positive(),
|
||||
Number::Rational(ref r) => r.is_positive(),
|
||||
Number::Rational(r) => r.is_positive(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -783,9 +853,9 @@ impl Number {
|
||||
pub(crate) fn is_negative(&self) -> bool {
|
||||
match self {
|
||||
Number::Fixnum(n) => n.get_num() < 0,
|
||||
Number::Integer(ref n) => n.is_negative(),
|
||||
Number::Integer(n) => n.is_negative(),
|
||||
&Number::Float(OrderedFloat(f)) => f.is_sign_negative() && f != -0f64,
|
||||
Number::Rational(ref r) => r.is_negative(),
|
||||
Number::Rational(r) => r.is_negative(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -793,9 +863,9 @@ impl Number {
|
||||
pub(crate) fn is_zero(&self) -> bool {
|
||||
match self {
|
||||
Number::Fixnum(n) => n.get_num() == 0,
|
||||
Number::Integer(ref n) => n.is_zero(),
|
||||
Number::Integer(n) => n.is_zero(),
|
||||
&Number::Float(OrderedFloat(f)) => f == 0.0 || f == -0.0,
|
||||
Number::Rational(ref r) => r.is_zero(),
|
||||
Number::Rational(r) => r.is_zero(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -850,9 +920,9 @@ impl OptArgIndexKey {
|
||||
#[inline]
|
||||
pub(crate) fn set_switch_on_term_loc(&mut self, value: usize) {
|
||||
match self {
|
||||
OptArgIndexKey::Literal(_, ref mut loc, ..)
|
||||
| OptArgIndexKey::Structure(_, ref mut loc, ..)
|
||||
| OptArgIndexKey::List(_, ref mut loc) => {
|
||||
OptArgIndexKey::Literal(_, loc, ..)
|
||||
| OptArgIndexKey::Structure(_, loc, ..)
|
||||
| OptArgIndexKey::List(_, loc) => {
|
||||
*loc = value;
|
||||
}
|
||||
OptArgIndexKey::None => {}
|
||||
@@ -864,9 +934,9 @@ impl AddAssign<usize> for OptArgIndexKey {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, n: usize) {
|
||||
match self {
|
||||
OptArgIndexKey::Literal(_, ref mut o, ..)
|
||||
| OptArgIndexKey::List(_, ref mut o)
|
||||
| OptArgIndexKey::Structure(_, ref mut o, ..) => {
|
||||
OptArgIndexKey::Literal(_, o, ..)
|
||||
| OptArgIndexKey::List(_, o)
|
||||
| OptArgIndexKey::Structure(_, o, ..) => {
|
||||
*o += n;
|
||||
}
|
||||
OptArgIndexKey::None => {}
|
||||
|
||||
@@ -230,9 +230,9 @@ pub(crate) fn variadic_functor(
|
||||
#[allow(unused_parens)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use FunctorElement::*;
|
||||
use indexmap::indexmap;
|
||||
use std::string::String;
|
||||
use FunctorElement::*;
|
||||
|
||||
#[test]
|
||||
fn basic_terms() {
|
||||
@@ -551,7 +551,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn inlined_atoms() {
|
||||
let atom_table = AtomTable::new();
|
||||
let atom_table = AtomTable::new().unwrap();
|
||||
let inlined = AtomTable::build_with(&atom_table, "inline");
|
||||
|
||||
assert!(inlined.is_inlined());
|
||||
|
||||
342
src/heap_iter.rs
342
src/heap_iter.rs
@@ -1,20 +1,348 @@
|
||||
#![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work
|
||||
|
||||
use crate::arena::Arena;
|
||||
use crate::forms::Number;
|
||||
#[cfg(test)]
|
||||
pub(crate) use crate::machine::gc::StacklessPreOrderHeapIter;
|
||||
|
||||
use crate::atom_table::*;
|
||||
use crate::machine::cycle_detection::*;
|
||||
use crate::machine::heap::*;
|
||||
use crate::machine::machine_indices::TermOrderCategory;
|
||||
use crate::machine::stack::*;
|
||||
use crate::parser::lexer::MachineState;
|
||||
use crate::types::*;
|
||||
|
||||
use core::marker::PhantomData;
|
||||
use scryer_modular_bitfield::prelude::*;
|
||||
use fxhash::FxBuildHasher;
|
||||
use indexmap::IndexSet;
|
||||
use modular_bitfield::prelude::*;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use std::ops::Deref;
|
||||
use std::vec::Vec;
|
||||
|
||||
// iterate through the subterms of a pair of terms
|
||||
// for as long as they structurally agree, reporting
|
||||
// the earliest (pre-order) difference before returning
|
||||
// None's and pairs of variables as the Iterator Item.
|
||||
|
||||
pub struct ParallelHeapIter<'a> {
|
||||
stack: Vec<(HeapCellValue, HeapCellValue)>,
|
||||
heap: &'a Heap,
|
||||
arena: &'a Arena,
|
||||
tabu_list: IndexSet<(usize, usize), FxBuildHasher>,
|
||||
}
|
||||
|
||||
impl<'a> ParallelHeapIter<'a> {
|
||||
pub fn from(machine_st: &'a MachineState, h1: HeapCellValue, h2: HeapCellValue) -> Self {
|
||||
Self {
|
||||
stack: vec![(h1, h2)],
|
||||
heap: &machine_st.heap,
|
||||
arena: &machine_st.arena,
|
||||
tabu_list: IndexSet::with_hasher(FxBuildHasher::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
pub enum TermPair {
|
||||
Vars(usize, usize),
|
||||
Less(HeapCellValue, HeapCellValue),
|
||||
Greater(HeapCellValue, HeapCellValue),
|
||||
Unordered(HeapCellValue, HeapCellValue),
|
||||
}
|
||||
|
||||
impl ParallelHeapIter<'_> {
|
||||
#[inline]
|
||||
fn parallel_cmp<Cmp: Ord>(
|
||||
&mut self,
|
||||
v1: Cmp,
|
||||
v2: Cmp,
|
||||
h1: HeapCellValue,
|
||||
h2: HeapCellValue,
|
||||
) -> Option<TermPair> {
|
||||
match v1.cmp(&v2) {
|
||||
Ordering::Greater => {
|
||||
self.stack.clear();
|
||||
Some(TermPair::Greater(h1, h2))
|
||||
}
|
||||
Ordering::Less => {
|
||||
self.stack.clear();
|
||||
Some(TermPair::Less(h1, h2))
|
||||
}
|
||||
Ordering::Equal => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! some_or_return {
|
||||
($e:expr) => {
|
||||
if let Some(x) = $e {
|
||||
return Some(x);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl Iterator for ParallelHeapIter<'_> {
|
||||
type Item = TermPair;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
use crate::offset_table::F64Offset;
|
||||
|
||||
while let Some((s1, s2)) = self.stack.pop() {
|
||||
let s1 = heap_bound_deref(self.heap, s1);
|
||||
let s2 = heap_bound_deref(self.heap, s2);
|
||||
|
||||
let v1 = heap_bound_store(self.heap, s1);
|
||||
let v2 = heap_bound_store(self.heap, s2);
|
||||
|
||||
let order_cat_v1 = v1.order_category(self.heap);
|
||||
let order_cat_v2 = v2.order_category(self.heap);
|
||||
|
||||
some_or_return!(self.parallel_cmp(order_cat_v1, order_cat_v2, v1, v2));
|
||||
|
||||
match order_cat_v1 {
|
||||
Some(TermOrderCategory::Variable) => {
|
||||
let v1 = v1.get_value() as usize;
|
||||
let v2 = v2.get_value() as usize;
|
||||
|
||||
return Some(TermPair::Vars(v1, v2));
|
||||
}
|
||||
Some(TermOrderCategory::FloatingPoint) => {
|
||||
let v1_offset = cell_as_f64_offset!(v1);
|
||||
let v2_offset = cell_as_f64_offset!(v2);
|
||||
|
||||
let v1_f64 = self.arena.f64_tbl.get_entry(v1_offset);
|
||||
let v2_f64 = self.arena.f64_tbl.get_entry(v2_offset);
|
||||
|
||||
some_or_return!(self.parallel_cmp(v1_f64, v2_f64, v1, v2));
|
||||
}
|
||||
Some(TermOrderCategory::Integer) => {
|
||||
let v1_int = Number::try_from((v1, &self.arena.f64_tbl)).unwrap();
|
||||
let v2_int = Number::try_from((v2, &self.arena.f64_tbl)).unwrap();
|
||||
|
||||
some_or_return!(self.parallel_cmp(v1_int, v2_int, v1, v2));
|
||||
}
|
||||
Some(TermOrderCategory::Atom) => {
|
||||
read_heap_cell!(v1,
|
||||
(HeapCellValueTag::Atom, (n1, _a1)) => {
|
||||
read_heap_cell!(v2,
|
||||
(HeapCellValueTag::Atom, (n2, _a2)) => {
|
||||
some_or_return!(self.parallel_cmp(n1, n2, v1, v2));
|
||||
}
|
||||
(HeapCellValueTag::Str, s) => {
|
||||
let n2 = cell_as_atom_cell!(self.heap[s])
|
||||
.get_name();
|
||||
|
||||
some_or_return!(self.parallel_cmp(n1, n2, v1, v2));
|
||||
}
|
||||
_ => {
|
||||
unreachable!();
|
||||
}
|
||||
)
|
||||
}
|
||||
(HeapCellValueTag::Str, s) => {
|
||||
let n1 = cell_as_atom_cell!(self.heap[s])
|
||||
.get_name();
|
||||
|
||||
read_heap_cell!(v2,
|
||||
(HeapCellValueTag::Atom, (n2, _a2)) => {
|
||||
some_or_return!(self.parallel_cmp(n1, n2, v1, v2));
|
||||
}
|
||||
(HeapCellValueTag::Str, s) => {
|
||||
let n2 = cell_as_atom_cell!(self.heap[s])
|
||||
.get_name();
|
||||
|
||||
some_or_return!(self.parallel_cmp(n1, n2, v1, v2));
|
||||
}
|
||||
_ => {
|
||||
unreachable!();
|
||||
}
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
)
|
||||
}
|
||||
Some(TermOrderCategory::Compound) => {
|
||||
read_heap_cell!(v1,
|
||||
(HeapCellValueTag::Lis, l1) => {
|
||||
read_heap_cell!(v2,
|
||||
(HeapCellValueTag::PStrLoc, l2) => {
|
||||
if self.tabu_list.contains(&(l1, l2)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
self.tabu_list.insert((l1, l2));
|
||||
|
||||
// like the action of partial_string_to_stack here but the
|
||||
// ordering of stack pushes is (crucially for comparison
|
||||
// correctness) different.
|
||||
let (c, succ_cell) = self.heap.last_str_char_and_tail(l2);
|
||||
|
||||
self.stack.push((heap_loc_as_cell!(l1 + 1), succ_cell));
|
||||
self.stack.push((heap_loc_as_cell!(l1), char_as_cell!(c)));
|
||||
}
|
||||
(HeapCellValueTag::Lis, l2) => {
|
||||
if self.tabu_list.contains(&(l1, l2)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
self.tabu_list.insert((l1, l2));
|
||||
|
||||
self.stack.push((self.heap[l1 + 1], self.heap[l2 + 1]));
|
||||
self.stack.push((self.heap[l1], self.heap[l2]));
|
||||
}
|
||||
(HeapCellValueTag::Str, s2) => {
|
||||
if self.tabu_list.contains(&(l1, s2)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let (n2, a2) = cell_as_atom_cell!(self.heap[s2])
|
||||
.get_name_and_arity();
|
||||
|
||||
some_or_return!(self.parallel_cmp((2, atom!(".")), (a2, n2), v1, v2));
|
||||
|
||||
self.tabu_list.insert((l1, s2));
|
||||
|
||||
self.stack.push((self.heap[l1 + 1], self.heap[s2 + 2]));
|
||||
self.stack.push((self.heap[l1], self.heap[s2 + 1]));
|
||||
}
|
||||
_ => {
|
||||
unreachable!();
|
||||
}
|
||||
)
|
||||
}
|
||||
(HeapCellValueTag::PStrLoc, l1) => {
|
||||
read_heap_cell!(v2,
|
||||
(HeapCellValueTag::PStrLoc, l2) => {
|
||||
if self.tabu_list.contains(&(l1, l2)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
match self.heap.compare_pstr_segments(l1, l2) {
|
||||
PStrSegmentCmpResult::Continue(v1, v2) => {
|
||||
self.tabu_list.insert((l1, l2));
|
||||
|
||||
self.stack.push((v1.offset_by(l1), v2.offset_by(l2)));
|
||||
}
|
||||
PStrSegmentCmpResult::Less => {
|
||||
self.stack.clear();
|
||||
return Some(TermPair::Less(v1, v2));
|
||||
}
|
||||
PStrSegmentCmpResult::Greater => {
|
||||
self.stack.clear();
|
||||
return Some(TermPair::Greater(v1, v2));
|
||||
}
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::Lis, l2) => {
|
||||
if self.tabu_list.contains(&(l1, l2)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
self.tabu_list.insert((l1, l2));
|
||||
|
||||
let (c, succ_cell) = self.heap.last_str_char_and_tail(l1);
|
||||
|
||||
self.stack.push((succ_cell, heap_loc_as_cell!(l2 + 1)));
|
||||
self.stack.push((char_as_cell!(c), heap_loc_as_cell!(l2)));
|
||||
}
|
||||
(HeapCellValueTag::Str, s2) => {
|
||||
if self.tabu_list.contains(&(l1, s2)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
self.tabu_list.insert((l1, s2));
|
||||
|
||||
let (n2, a2) = cell_as_atom_cell!(self.heap[s2])
|
||||
.get_name_and_arity();
|
||||
|
||||
some_or_return!(self.parallel_cmp((2, atom!(".")), (a2, n2), v1, v2));
|
||||
|
||||
let (c, succ_cell) = self.heap.last_str_char_and_tail(l1);
|
||||
|
||||
self.stack.push((succ_cell, heap_loc_as_cell!(s2+2)));
|
||||
self.stack.push((char_as_cell!(c), heap_loc_as_cell!(s2+1)));
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
);
|
||||
}
|
||||
(HeapCellValueTag::Str, s1) => {
|
||||
read_heap_cell!(v2,
|
||||
(HeapCellValueTag::Str, s2) => {
|
||||
if self.tabu_list.contains(&(s1, s2)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let (n1, a1) = cell_as_atom_cell!(self.heap[s1])
|
||||
.get_name_and_arity();
|
||||
|
||||
let (n2, a2) = cell_as_atom_cell!(self.heap[s2])
|
||||
.get_name_and_arity();
|
||||
|
||||
some_or_return!(self.parallel_cmp((a1, n1), (a2, n2), v1, v2));
|
||||
|
||||
self.tabu_list.insert((s1, s2));
|
||||
|
||||
for idx in (1 .. a1+1).rev() {
|
||||
self.stack.push((self.heap[s1+idx], self.heap[s2+idx]));
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::Lis, l2) => {
|
||||
if self.tabu_list.contains(&(s1, l2)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let (n1, a1) = cell_as_atom_cell!(self.heap[s1])
|
||||
.get_name_and_arity();
|
||||
|
||||
some_or_return!(self.parallel_cmp((a1, n1), (2, atom!(".")), v1, v2));
|
||||
|
||||
self.stack.push((self.heap[s1+1], self.heap[l2]));
|
||||
|
||||
self.stack.push((self.heap[s1+2], self.heap[l2+1]));
|
||||
}
|
||||
(HeapCellValueTag::PStrLoc, l2) => {
|
||||
if self.tabu_list.contains(&(s1, l2)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let (n1, a1) = cell_as_atom_cell!(self.heap[s1])
|
||||
.get_name_and_arity();
|
||||
|
||||
some_or_return!(self.parallel_cmp((a1, n1), (2, atom!(".")), v1, v2));
|
||||
|
||||
self.tabu_list.insert((s1, l2));
|
||||
|
||||
let (c, succ_cell) = self.heap.last_str_char_and_tail(l2);
|
||||
|
||||
self.stack.push((heap_loc_as_cell!(s1+2), succ_cell));
|
||||
self.stack.push((heap_loc_as_cell!(s1+1), char_as_cell!(c)));
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
);
|
||||
}
|
||||
None => {
|
||||
return Some(TermPair::Unordered(v1, v2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn eager_stackful_preorder_iter(
|
||||
heap: &mut Heap,
|
||||
@@ -34,7 +362,7 @@ pub struct EagerStackfulPreOrderHeapIter<'a> {
|
||||
start_value: HeapCellValue,
|
||||
iter_stack: Vec<HeapCellValue>,
|
||||
mark_phase: bool,
|
||||
heap: &'a mut Heap,
|
||||
pub heap: &'a mut Heap,
|
||||
}
|
||||
|
||||
impl<'a> Drop for EagerStackfulPreOrderHeapIter<'a> {
|
||||
@@ -118,8 +446,8 @@ impl<'a> EagerStackfulPreOrderHeapIter<'a> {
|
||||
(HeapCellValueTag::PStrLoc, h) => {
|
||||
let tail_idx = self.heap.scan_slice_to_str(h).tail_idx;
|
||||
|
||||
self.heap[tail_idx].set_mark_bit(self.mark_phase);
|
||||
self.iter_stack.push(self.heap[tail_idx]);
|
||||
self.heap[tail_idx].set_mark_bit(self.mark_phase);
|
||||
}
|
||||
_ => {
|
||||
}
|
||||
@@ -141,7 +469,7 @@ impl<'a> Iterator for EagerStackfulPreOrderHeapIter<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(BitfieldSpecifier, Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[derive(Specifier, Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[bits = 2]
|
||||
enum IterStackLocTag {
|
||||
Iterable,
|
||||
@@ -149,7 +477,7 @@ enum IterStackLocTag {
|
||||
PendingMark,
|
||||
}
|
||||
|
||||
#[derive(BitfieldSpecifier, Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[derive(Specifier, Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[bits = 1]
|
||||
pub enum HeapOrStackTag {
|
||||
Heap,
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::atom_table::*;
|
||||
use crate::parser::ast::*;
|
||||
use crate::parser::dashu::base::RemEuclid;
|
||||
use crate::parser::dashu::integer::Sign;
|
||||
use crate::parser::dashu::{ibig, Integer, Rational};
|
||||
use crate::parser::dashu::{Integer, Rational, ibig};
|
||||
|
||||
use crate::forms::*;
|
||||
use crate::heap_iter::*;
|
||||
@@ -387,7 +387,7 @@ fn negated_op_needs_bracketing(
|
||||
op_dir: &OpDir,
|
||||
op: &Option<DirectedOp>,
|
||||
) -> bool {
|
||||
if let Some(ref op) = op {
|
||||
if let Some(op) = op {
|
||||
op.is_negative_sign()
|
||||
&& iter.leftmost_leaf_has_property(op_dir, |addr| {
|
||||
match Number::try_from((addr, f64_tbl)) {
|
||||
@@ -1452,7 +1452,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
||||
});
|
||||
|
||||
for op in &[op, parent_op] {
|
||||
if let Some(ref op) = &op {
|
||||
if let Some(op) = &op {
|
||||
if op.is_left()
|
||||
&& (op.is_prefix() || requires_space(&op.as_atom().as_str(), "("))
|
||||
{
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
use bytes::{buf::Reader, Bytes};
|
||||
use bytes::{Bytes, buf::Reader};
|
||||
use std::sync::{Arc, Condvar, Mutex};
|
||||
use tokio::sync::Notify;
|
||||
|
||||
use warp::http;
|
||||
|
||||
pub struct HttpListener {
|
||||
pub incoming: std::sync::mpsc::Receiver<HttpRequest>,
|
||||
pub warp_shutdown: Arc<Notify>,
|
||||
}
|
||||
|
||||
pub struct HttpRequest {
|
||||
|
||||
298
src/indexing.rs
298
src/indexing.rs
@@ -167,7 +167,7 @@ impl<'a> IndexingCodeMergingPtr<'a> {
|
||||
.push(IndexingLine::IndexedChoice(third_level_index));
|
||||
|
||||
match &mut self.indexing_code[self.offset] {
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(ref mut constants)) => {
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(constants)) => {
|
||||
constants.insert(
|
||||
constant,
|
||||
IndexingCodePtr::Internal(indexing_code_len - self.offset),
|
||||
@@ -196,7 +196,7 @@ impl<'a> IndexingCodeMergingPtr<'a> {
|
||||
.push(IndexingLine::DynamicIndexedChoice(third_level_index));
|
||||
|
||||
match &mut self.indexing_code[self.offset] {
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(ref mut constants)) => {
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(constants)) => {
|
||||
constants.insert(
|
||||
constant,
|
||||
IndexingCodePtr::Internal(indexing_code_len - self.offset),
|
||||
@@ -210,22 +210,22 @@ impl<'a> IndexingCodeMergingPtr<'a> {
|
||||
|
||||
fn extend_indexed_choice(&mut self, index: usize) {
|
||||
match &mut self.indexing_code[self.offset] {
|
||||
IndexingLine::IndexedChoice(ref mut indexed_choice_instrs)
|
||||
IndexingLine::IndexedChoice(indexed_choice_instrs)
|
||||
if self.append_or_prepend.is_append() =>
|
||||
{
|
||||
uncap_choice_seq_with_trust(indexed_choice_instrs.make_contiguous());
|
||||
indexed_choice_instrs.push_back(IndexedChoiceInstruction::Trust(index));
|
||||
}
|
||||
IndexingLine::IndexedChoice(ref mut indexed_choice_instrs) => {
|
||||
IndexingLine::IndexedChoice(indexed_choice_instrs) => {
|
||||
uncap_choice_seq_with_try(indexed_choice_instrs.make_contiguous());
|
||||
indexed_choice_instrs.push_front(IndexedChoiceInstruction::Try(index));
|
||||
}
|
||||
IndexingLine::DynamicIndexedChoice(ref mut indexed_choice_instrs)
|
||||
IndexingLine::DynamicIndexedChoice(indexed_choice_instrs)
|
||||
if self.append_or_prepend.is_append() =>
|
||||
{
|
||||
indexed_choice_instrs.push_back(index);
|
||||
}
|
||||
IndexingLine::DynamicIndexedChoice(ref mut indexed_choice_instrs) => {
|
||||
IndexingLine::DynamicIndexedChoice(indexed_choice_instrs) => {
|
||||
indexed_choice_instrs.push_front(index);
|
||||
}
|
||||
_ => {
|
||||
@@ -244,7 +244,7 @@ impl<'a> IndexingCodeMergingPtr<'a> {
|
||||
let indexing_code_len = self.indexing_code.len();
|
||||
|
||||
match &mut self.indexing_code[self.offset] {
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, ref mut c, ..)) => {
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, c, ..)) => {
|
||||
match *c {
|
||||
IndexingCodePtr::Fail if self.is_dynamic => {
|
||||
*c = IndexingCodePtr::DynamicExternal(index);
|
||||
@@ -322,7 +322,7 @@ impl<'a> IndexingCodeMergingPtr<'a> {
|
||||
let indexing_code_len = self.indexing_code.len();
|
||||
|
||||
match &mut self.indexing_code[self.offset] {
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, ref mut c, ..)) => {
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, c, ..)) => {
|
||||
match *c {
|
||||
IndexingCodePtr::Fail if self.is_dynamic => {
|
||||
*c = IndexingCodePtr::DynamicExternal(index);
|
||||
@@ -445,7 +445,7 @@ impl<'a> IndexingCodeMergingPtr<'a> {
|
||||
.push(IndexingLine::IndexedChoice(third_level_index));
|
||||
|
||||
match &mut self.indexing_code[self.offset] {
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(ref mut structures)) => {
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(structures)) => {
|
||||
structures.insert(
|
||||
key,
|
||||
IndexingCodePtr::Internal(indexing_code_len - self.offset),
|
||||
@@ -474,7 +474,7 @@ impl<'a> IndexingCodeMergingPtr<'a> {
|
||||
.push(IndexingLine::DynamicIndexedChoice(third_level_index));
|
||||
|
||||
match &mut self.indexing_code[self.offset] {
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(ref mut structures)) => {
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(structures)) => {
|
||||
structures.insert(
|
||||
key,
|
||||
IndexingCodePtr::Internal(indexing_code_len - self.offset),
|
||||
@@ -491,33 +491,29 @@ impl<'a> IndexingCodeMergingPtr<'a> {
|
||||
let indexing_code_len = self.indexing_code.len();
|
||||
|
||||
match &mut self.indexing_code[self.offset] {
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
ref mut s,
|
||||
)) => match *s {
|
||||
IndexingCodePtr::Fail if self.is_dynamic => {
|
||||
*s = IndexingCodePtr::DynamicExternal(index);
|
||||
break;
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, _, _, s)) => {
|
||||
match *s {
|
||||
IndexingCodePtr::Fail if self.is_dynamic => {
|
||||
*s = IndexingCodePtr::DynamicExternal(index);
|
||||
break;
|
||||
}
|
||||
IndexingCodePtr::Fail => {
|
||||
*s = IndexingCodePtr::External(index);
|
||||
break;
|
||||
}
|
||||
IndexingCodePtr::DynamicExternal(o) => {
|
||||
*s = IndexingCodePtr::Internal(indexing_code_len - self.offset);
|
||||
self.internalize_structure(IndexingCodePtr::DynamicExternal(o));
|
||||
}
|
||||
IndexingCodePtr::External(o) => {
|
||||
*s = IndexingCodePtr::Internal(indexing_code_len - self.offset);
|
||||
self.internalize_structure(IndexingCodePtr::External(o));
|
||||
}
|
||||
IndexingCodePtr::Internal(o) => {
|
||||
self.offset += o;
|
||||
}
|
||||
}
|
||||
IndexingCodePtr::Fail => {
|
||||
*s = IndexingCodePtr::External(index);
|
||||
break;
|
||||
}
|
||||
IndexingCodePtr::DynamicExternal(o) => {
|
||||
*s = IndexingCodePtr::Internal(indexing_code_len - self.offset);
|
||||
self.internalize_structure(IndexingCodePtr::DynamicExternal(o));
|
||||
}
|
||||
IndexingCodePtr::External(o) => {
|
||||
*s = IndexingCodePtr::Internal(indexing_code_len - self.offset);
|
||||
self.internalize_structure(IndexingCodePtr::External(o));
|
||||
}
|
||||
IndexingCodePtr::Internal(o) => {
|
||||
self.offset += o;
|
||||
}
|
||||
},
|
||||
}
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(structures)) => {
|
||||
match structures.get(&key).cloned() {
|
||||
None | Some(IndexingCodePtr::Fail) if self.is_dynamic => {
|
||||
@@ -559,52 +555,50 @@ impl<'a> IndexingCodeMergingPtr<'a> {
|
||||
let indexing_code_len = self.indexing_code.len();
|
||||
|
||||
match &mut self.indexing_code[self.offset] {
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, _, ref mut l, _)) => {
|
||||
match *l {
|
||||
IndexingCodePtr::Fail if self.is_dynamic => {
|
||||
*l = IndexingCodePtr::DynamicExternal(index);
|
||||
}
|
||||
IndexingCodePtr::Fail => {
|
||||
*l = IndexingCodePtr::External(index);
|
||||
}
|
||||
IndexingCodePtr::DynamicExternal(o) => {
|
||||
*l = IndexingCodePtr::Internal(indexing_code_len - self.offset);
|
||||
|
||||
let third_level_index = if self.append_or_prepend.is_append() {
|
||||
vec![o, index].into()
|
||||
} else {
|
||||
vec![index, o].into()
|
||||
};
|
||||
|
||||
self.indexing_code
|
||||
.push(IndexingLine::DynamicIndexedChoice(third_level_index));
|
||||
}
|
||||
IndexingCodePtr::External(o) => {
|
||||
*l = IndexingCodePtr::Internal(indexing_code_len - self.offset);
|
||||
|
||||
let third_level_index = if self.append_or_prepend.is_append() {
|
||||
vec![
|
||||
IndexedChoiceInstruction::Try(o),
|
||||
IndexedChoiceInstruction::Trust(index),
|
||||
]
|
||||
.into()
|
||||
} else {
|
||||
vec![
|
||||
IndexedChoiceInstruction::Try(index),
|
||||
IndexedChoiceInstruction::Trust(o),
|
||||
]
|
||||
.into()
|
||||
};
|
||||
|
||||
self.indexing_code
|
||||
.push(IndexingLine::IndexedChoice(third_level_index));
|
||||
}
|
||||
IndexingCodePtr::Internal(o) => {
|
||||
self.offset += o;
|
||||
self.extend_indexed_choice(index);
|
||||
}
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, _, l, _)) => match *l {
|
||||
IndexingCodePtr::Fail if self.is_dynamic => {
|
||||
*l = IndexingCodePtr::DynamicExternal(index);
|
||||
}
|
||||
}
|
||||
IndexingCodePtr::Fail => {
|
||||
*l = IndexingCodePtr::External(index);
|
||||
}
|
||||
IndexingCodePtr::DynamicExternal(o) => {
|
||||
*l = IndexingCodePtr::Internal(indexing_code_len - self.offset);
|
||||
|
||||
let third_level_index = if self.append_or_prepend.is_append() {
|
||||
vec![o, index].into()
|
||||
} else {
|
||||
vec![index, o].into()
|
||||
};
|
||||
|
||||
self.indexing_code
|
||||
.push(IndexingLine::DynamicIndexedChoice(third_level_index));
|
||||
}
|
||||
IndexingCodePtr::External(o) => {
|
||||
*l = IndexingCodePtr::Internal(indexing_code_len - self.offset);
|
||||
|
||||
let third_level_index = if self.append_or_prepend.is_append() {
|
||||
vec![
|
||||
IndexedChoiceInstruction::Try(o),
|
||||
IndexedChoiceInstruction::Trust(index),
|
||||
]
|
||||
.into()
|
||||
} else {
|
||||
vec![
|
||||
IndexedChoiceInstruction::Try(index),
|
||||
IndexedChoiceInstruction::Trust(o),
|
||||
]
|
||||
.into()
|
||||
};
|
||||
|
||||
self.indexing_code
|
||||
.push(IndexingLine::IndexedChoice(third_level_index));
|
||||
}
|
||||
IndexingCodePtr::Internal(o) => {
|
||||
self.offset += o;
|
||||
self.extend_indexed_choice(index);
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
@@ -632,7 +626,7 @@ pub(crate) fn merge_clause_index(
|
||||
);
|
||||
|
||||
match &opt_arg_index_key {
|
||||
OptArgIndexKey::Literal(_, index_loc, constant, ref overlapping_constants) => {
|
||||
OptArgIndexKey::Literal(_, index_loc, constant, overlapping_constants) => {
|
||||
let offset = new_clause_loc - index_loc + 1;
|
||||
merging_ptr.index_constant(HeapCellValue::from(*constant), offset);
|
||||
|
||||
@@ -676,20 +670,18 @@ pub(crate) fn remove_constant_indices(
|
||||
let iter = once(&constant).chain(overlapping_constants.iter());
|
||||
|
||||
match &mut indexing_code[index] {
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, ref mut c, ..)) => {
|
||||
match *c {
|
||||
IndexingCodePtr::DynamicExternal(_) | IndexingCodePtr::External(_) => {
|
||||
*c = IndexingCodePtr::Fail;
|
||||
return;
|
||||
}
|
||||
IndexingCodePtr::Internal(o) => {
|
||||
index += o;
|
||||
}
|
||||
IndexingCodePtr::Fail => {
|
||||
return;
|
||||
}
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, c, ..)) => match *c {
|
||||
IndexingCodePtr::DynamicExternal(_) | IndexingCodePtr::External(_) => {
|
||||
*c = IndexingCodePtr::Fail;
|
||||
return;
|
||||
}
|
||||
}
|
||||
IndexingCodePtr::Internal(o) => {
|
||||
index += o;
|
||||
}
|
||||
IndexingCodePtr::Fail => {
|
||||
return;
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
@@ -700,9 +692,7 @@ pub(crate) fn remove_constant_indices(
|
||||
for constant in iter.map(|l| HeapCellValue::from(*l)) {
|
||||
loop {
|
||||
match &mut indexing_code[index] {
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(
|
||||
ref mut constants,
|
||||
)) => {
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(constants)) => {
|
||||
constants_index = index;
|
||||
|
||||
match constants.get(&constant).cloned() {
|
||||
@@ -720,7 +710,7 @@ pub(crate) fn remove_constant_indices(
|
||||
}
|
||||
}
|
||||
}
|
||||
IndexingLine::IndexedChoice(ref mut indexed_choice_instrs) => {
|
||||
IndexingLine::IndexedChoice(indexed_choice_instrs) => {
|
||||
StaticCodeIndices::remove_instruction_with_offset(
|
||||
indexed_choice_instrs,
|
||||
offset,
|
||||
@@ -734,13 +724,13 @@ pub(crate) fn remove_constant_indices(
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(
|
||||
_,
|
||||
_,
|
||||
ref mut c,
|
||||
c,
|
||||
..,
|
||||
)) => {
|
||||
*c = ext;
|
||||
}
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(
|
||||
ref mut constants,
|
||||
constants,
|
||||
)) => {
|
||||
constants.insert(constant, ext);
|
||||
}
|
||||
@@ -753,7 +743,7 @@ pub(crate) fn remove_constant_indices(
|
||||
|
||||
break;
|
||||
}
|
||||
IndexingLine::DynamicIndexedChoice(ref mut indexed_choice_instrs) => {
|
||||
IndexingLine::DynamicIndexedChoice(indexed_choice_instrs) => {
|
||||
DynamicCodeIndices::remove_instruction_with_offset(
|
||||
indexed_choice_instrs,
|
||||
offset,
|
||||
@@ -767,13 +757,13 @@ pub(crate) fn remove_constant_indices(
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(
|
||||
_,
|
||||
_,
|
||||
ref mut c,
|
||||
c,
|
||||
..,
|
||||
)) => {
|
||||
*c = ext;
|
||||
}
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(
|
||||
ref mut constants,
|
||||
constants,
|
||||
)) => {
|
||||
constants.insert(constant, ext);
|
||||
}
|
||||
@@ -794,11 +784,11 @@ pub(crate) fn remove_constant_indices(
|
||||
}
|
||||
|
||||
match &indexing_code[constants_index] {
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(ref constants))
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(constants))
|
||||
if constants.is_empty() =>
|
||||
{
|
||||
match &mut indexing_code[0] {
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, ref mut c, ..)) => {
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, c, ..)) => {
|
||||
*c = IndexingCodePtr::Fail;
|
||||
}
|
||||
_ => {
|
||||
@@ -819,20 +809,18 @@ pub(crate) fn remove_structure_index(
|
||||
let mut index = 0;
|
||||
|
||||
match &mut indexing_code[index] {
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, _, _, ref mut s)) => {
|
||||
match *s {
|
||||
IndexingCodePtr::DynamicExternal(_) | IndexingCodePtr::External(_) => {
|
||||
*s = IndexingCodePtr::Fail;
|
||||
return;
|
||||
}
|
||||
IndexingCodePtr::Internal(o) => {
|
||||
index += o;
|
||||
}
|
||||
IndexingCodePtr::Fail => {
|
||||
return;
|
||||
}
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, _, _, s)) => match *s {
|
||||
IndexingCodePtr::DynamicExternal(_) | IndexingCodePtr::External(_) => {
|
||||
*s = IndexingCodePtr::Fail;
|
||||
return;
|
||||
}
|
||||
}
|
||||
IndexingCodePtr::Internal(o) => {
|
||||
index += o;
|
||||
}
|
||||
IndexingCodePtr::Fail => {
|
||||
return;
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
@@ -842,7 +830,7 @@ pub(crate) fn remove_structure_index(
|
||||
|
||||
loop {
|
||||
match &mut indexing_code[index] {
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(ref mut structures)) => {
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(structures)) => {
|
||||
structures_index = index;
|
||||
|
||||
match structures.get(&(name, arity)).cloned() {
|
||||
@@ -859,7 +847,7 @@ pub(crate) fn remove_structure_index(
|
||||
}
|
||||
}
|
||||
}
|
||||
IndexingLine::IndexedChoice(ref mut indexed_choice_instrs) => {
|
||||
IndexingLine::IndexedChoice(indexed_choice_instrs) => {
|
||||
StaticCodeIndices::remove_instruction_with_offset(indexed_choice_instrs, offset);
|
||||
|
||||
if indexed_choice_instrs.len() == 1 {
|
||||
@@ -872,12 +860,12 @@ pub(crate) fn remove_structure_index(
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
ref mut s,
|
||||
s,
|
||||
)) => {
|
||||
*s = ext;
|
||||
}
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(
|
||||
ref mut structures,
|
||||
structures,
|
||||
)) => {
|
||||
structures.insert((name, arity), ext);
|
||||
}
|
||||
@@ -890,7 +878,7 @@ pub(crate) fn remove_structure_index(
|
||||
|
||||
break;
|
||||
}
|
||||
IndexingLine::DynamicIndexedChoice(ref mut indexed_choice_instrs) => {
|
||||
IndexingLine::DynamicIndexedChoice(indexed_choice_instrs) => {
|
||||
DynamicCodeIndices::remove_instruction_with_offset(indexed_choice_instrs, offset);
|
||||
|
||||
if indexed_choice_instrs.len() == 1 {
|
||||
@@ -903,12 +891,12 @@ pub(crate) fn remove_structure_index(
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
ref mut s,
|
||||
s,
|
||||
)) => {
|
||||
*s = ext;
|
||||
}
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(
|
||||
ref mut structures,
|
||||
structures,
|
||||
)) => {
|
||||
structures.insert((name, arity), ext);
|
||||
}
|
||||
@@ -928,17 +916,11 @@ pub(crate) fn remove_structure_index(
|
||||
}
|
||||
|
||||
match &indexing_code[structures_index] {
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(ref structures))
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(structures))
|
||||
if structures.is_empty() =>
|
||||
{
|
||||
match &mut indexing_code[0] {
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
ref mut s,
|
||||
)) => {
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, _, _, s)) => {
|
||||
*s = IndexingCodePtr::Fail;
|
||||
}
|
||||
_ => {
|
||||
@@ -954,27 +936,25 @@ pub(crate) fn remove_list_index(indexing_code: &mut [IndexingLine], offset: usiz
|
||||
let mut index = 0;
|
||||
|
||||
match &mut indexing_code[index] {
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, _, ref mut l, _)) => {
|
||||
match *l {
|
||||
IndexingCodePtr::DynamicExternal(_) | IndexingCodePtr::External(_) => {
|
||||
*l = IndexingCodePtr::Fail;
|
||||
return;
|
||||
}
|
||||
IndexingCodePtr::Internal(o) => {
|
||||
index += o;
|
||||
}
|
||||
IndexingCodePtr::Fail => {
|
||||
return;
|
||||
}
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, _, l, _)) => match *l {
|
||||
IndexingCodePtr::DynamicExternal(_) | IndexingCodePtr::External(_) => {
|
||||
*l = IndexingCodePtr::Fail;
|
||||
return;
|
||||
}
|
||||
}
|
||||
IndexingCodePtr::Internal(o) => {
|
||||
index += o;
|
||||
}
|
||||
IndexingCodePtr::Fail => {
|
||||
return;
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
match &mut indexing_code[index] {
|
||||
IndexingLine::IndexedChoice(ref mut indexed_choice_instrs) => {
|
||||
IndexingLine::IndexedChoice(indexed_choice_instrs) => {
|
||||
StaticCodeIndices::remove_instruction_with_offset(indexed_choice_instrs, offset);
|
||||
|
||||
if indexed_choice_instrs.len() == 1 {
|
||||
@@ -986,7 +966,7 @@ pub(crate) fn remove_list_index(indexing_code: &mut [IndexingLine], offset: usiz
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
ref mut l,
|
||||
l,
|
||||
_,
|
||||
)) => {
|
||||
*l = ext;
|
||||
@@ -998,7 +978,7 @@ pub(crate) fn remove_list_index(indexing_code: &mut [IndexingLine], offset: usiz
|
||||
}
|
||||
}
|
||||
}
|
||||
IndexingLine::DynamicIndexedChoice(ref mut indexed_choice_instrs) => {
|
||||
IndexingLine::DynamicIndexedChoice(indexed_choice_instrs) => {
|
||||
DynamicCodeIndices::remove_instruction_with_offset(indexed_choice_instrs, offset);
|
||||
|
||||
if indexed_choice_instrs.len() == 1 {
|
||||
@@ -1010,7 +990,7 @@ pub(crate) fn remove_list_index(indexing_code: &mut [IndexingLine], offset: usiz
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
ref mut l,
|
||||
l,
|
||||
_,
|
||||
)) => {
|
||||
*l = ext;
|
||||
@@ -1034,7 +1014,7 @@ pub(crate) fn remove_index(
|
||||
clause_loc: usize,
|
||||
) {
|
||||
match opt_arg_index_key {
|
||||
OptArgIndexKey::Literal(_, _, constant, ref overlapping_constants) => {
|
||||
OptArgIndexKey::Literal(_, _, constant, overlapping_constants) => {
|
||||
remove_constant_indices(*constant, *overlapping_constants, indexing_code, clause_loc);
|
||||
}
|
||||
OptArgIndexKey::Structure(_, _, name, arity) => {
|
||||
@@ -1328,9 +1308,7 @@ impl Indexer for DynamicCodeIndices {
|
||||
for (key, code) in indices.into_iter() {
|
||||
if code.len() > 1 {
|
||||
index_locs.insert(key, IndexingCodePtr::Internal(prelude.len() + 1));
|
||||
prelude.push_back(IndexingLine::DynamicIndexedChoice(
|
||||
code.into_iter().collect(),
|
||||
));
|
||||
prelude.push_back(IndexingLine::DynamicIndexedChoice(code));
|
||||
} else if let Some(i) = code.front() {
|
||||
index_locs.insert(key, IndexingCodePtr::DynamicExternal(*i));
|
||||
}
|
||||
@@ -1369,9 +1347,7 @@ impl Indexer for DynamicCodeIndices {
|
||||
) -> IndexingCodePtr {
|
||||
if lists.len() > 1 {
|
||||
let lists = std::mem::take(lists);
|
||||
prelude.push_back(IndexingLine::DynamicIndexedChoice(
|
||||
lists.into_iter().collect(),
|
||||
));
|
||||
prelude.push_back(IndexingLine::DynamicIndexedChoice(lists));
|
||||
IndexingCodePtr::Internal(1)
|
||||
} else {
|
||||
lists
|
||||
@@ -1529,11 +1505,11 @@ impl<I: Indexer> CodeOffsets<I> {
|
||||
&mut prelude,
|
||||
);
|
||||
|
||||
if let IndexingCodePtr::Internal(ref mut i) = &mut str_loc {
|
||||
if let IndexingCodePtr::Internal(i) = &mut str_loc {
|
||||
*i += emitted_switch_on_constant as usize; // con_loc.is_internal() as usize;
|
||||
}
|
||||
|
||||
if let IndexingCodePtr::Internal(ref mut i) = &mut lst_loc {
|
||||
if let IndexingCodePtr::Internal(i) = &mut lst_loc {
|
||||
*i += emitted_switch_on_constant as usize; // con_loc.is_internal() as usize;
|
||||
*i += emitted_switch_on_structure as usize; // str_loc.is_internal() as usize;
|
||||
}
|
||||
@@ -1548,6 +1524,6 @@ impl<I: Indexer> CodeOffsets<I> {
|
||||
str_loc,
|
||||
)));
|
||||
|
||||
prelude.into_iter().collect()
|
||||
prelude.into()
|
||||
}
|
||||
}
|
||||
|
||||
1386
src/instructions.rs
Normal file
1386
src/instructions.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -7,6 +7,7 @@ use std::cell::Cell;
|
||||
use std::collections::VecDeque;
|
||||
use std::iter::*;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use std::vec::Vec;
|
||||
|
||||
#[allow(clippy::borrowed_box)]
|
||||
@@ -89,7 +90,7 @@ impl<'a> QueryIterator<'a> {
|
||||
| Term::CompleteString(..) => {
|
||||
return QueryIterator {
|
||||
state_stack: vec![],
|
||||
}
|
||||
};
|
||||
}
|
||||
Term::Clause(r, name, terms) => TermIterState::Clause(Level::Root, 0, r, *name, terms),
|
||||
Term::Var(cell, var_ptr) => TermIterState::Var(Level::Root, cell, var_ptr.clone()),
|
||||
@@ -102,11 +103,11 @@ impl<'a> QueryIterator<'a> {
|
||||
|
||||
fn extend_state(&mut self, lvl: Level, term: &'a QueryTerm) {
|
||||
match term {
|
||||
QueryTerm::Clause(ref cell, ClauseType::CallN(_), ref terms, _) => {
|
||||
QueryTerm::Clause(cell, ClauseType::CallN(_), terms, _) => {
|
||||
self.state_stack
|
||||
.push(TermIterState::Clause(lvl, 1, cell, atom!("$call"), terms));
|
||||
}
|
||||
QueryTerm::Clause(ref cell, ref ct, ref terms, _) => {
|
||||
QueryTerm::Clause(cell, ct, terms, _) => {
|
||||
self.state_stack
|
||||
.push(TermIterState::Clause(lvl, 0, cell, ct.name(), terms));
|
||||
}
|
||||
@@ -142,7 +143,7 @@ impl<'a> Iterator for QueryIterator<'a> {
|
||||
return match lvl {
|
||||
Level::Root => None,
|
||||
lvl => Some(TermRef::Clause(lvl, cell, name, child_terms)),
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
} else {
|
||||
@@ -292,7 +293,7 @@ impl<'a> Iterator for FactIterator<'a> {
|
||||
return Some(TermRef::CompleteString(lvl, cell, atom));
|
||||
}
|
||||
TermIterState::Literal(lvl, cell, constant) => {
|
||||
return Some(TermRef::Literal(lvl, cell, constant))
|
||||
return Some(TermRef::Literal(lvl, cell, constant));
|
||||
}
|
||||
TermIterState::Var(lvl, cell, var_ptr) => {
|
||||
return Some(TermRef::Var(lvl, cell, var_ptr));
|
||||
@@ -319,15 +320,28 @@ pub(crate) fn breadth_first_iter(
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
enum ClauseIteratorState<'a> {
|
||||
RemainingChunks(&'a VecDeque<ChunkedTerms>, usize),
|
||||
RemainingBranches(&'a Vec<VecDeque<ChunkedTerms>>, usize),
|
||||
RemainingBranches(
|
||||
&'a Vec<Arc<BranchNumber>>,
|
||||
&'a Vec<VecDeque<ChunkedTerms>>,
|
||||
usize,
|
||||
),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) enum ClauseItem<'a> {
|
||||
FirstBranch(usize),
|
||||
NextBranch,
|
||||
BranchEnd(usize),
|
||||
Chunk { terms: &'a VecDeque<QueryTerm> },
|
||||
FirstBranch {
|
||||
branch_num: &'a Arc<BranchNumber>,
|
||||
num_branches: usize,
|
||||
},
|
||||
NextBranch {
|
||||
branch_num: &'a Arc<BranchNumber>,
|
||||
},
|
||||
BranchEnd {
|
||||
depth: usize,
|
||||
},
|
||||
Chunk {
|
||||
terms: &'a VecDeque<QueryTerm>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -338,8 +352,8 @@ pub(crate) struct ClauseIterator<'a> {
|
||||
|
||||
fn state_from_chunked_terms(chunk_vec: &VecDeque<ChunkedTerms>) -> ClauseIteratorState<'_> {
|
||||
if chunk_vec.len() == 1 {
|
||||
if let Some(ChunkedTerms::Branch(ref branches)) = chunk_vec.front() {
|
||||
return ClauseIteratorState::RemainingBranches(branches, 0);
|
||||
if let Some(ChunkedTerms::Branch { branch_nums, arms }) = chunk_vec.front() {
|
||||
return ClauseIteratorState::RemainingBranches(branch_nums, arms, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -370,7 +384,9 @@ impl<'a> ClauseIterator<'a> {
|
||||
|
||||
while let Some(state) = self.state_stack.pop() {
|
||||
match state {
|
||||
ClauseIteratorState::RemainingBranches(terms, focus) if terms.len() == focus => {
|
||||
ClauseIteratorState::RemainingBranches(_branch_nums, terms, focus)
|
||||
if terms.len() == focus =>
|
||||
{
|
||||
depth += 1;
|
||||
}
|
||||
_ => {
|
||||
@@ -399,11 +415,11 @@ impl<'a> Iterator for ClauseIterator<'a> {
|
||||
}
|
||||
|
||||
match &chunks[focus] {
|
||||
ChunkedTerms::Branch(branches) => {
|
||||
ChunkedTerms::Branch { branch_nums, arms } => {
|
||||
self.state_stack
|
||||
.push(ClauseIteratorState::RemainingBranches(branches, 0));
|
||||
.push(ClauseIteratorState::RemainingBranches(branch_nums, arms, 0));
|
||||
}
|
||||
ChunkedTerms::Chunk { ref terms } => {
|
||||
ChunkedTerms::Chunk { terms } => {
|
||||
return Some(ClauseItem::Chunk { terms });
|
||||
}
|
||||
}
|
||||
@@ -411,11 +427,15 @@ impl<'a> Iterator for ClauseIterator<'a> {
|
||||
ClauseIteratorState::RemainingChunks(chunks, focus) => {
|
||||
debug_assert_eq!(chunks.len(), focus);
|
||||
}
|
||||
ClauseIteratorState::RemainingBranches(branches, focus)
|
||||
ClauseIteratorState::RemainingBranches(branch_nums, branches, focus)
|
||||
if focus < branches.len() =>
|
||||
{
|
||||
self.state_stack
|
||||
.push(ClauseIteratorState::RemainingBranches(branches, focus + 1));
|
||||
.push(ClauseIteratorState::RemainingBranches(
|
||||
branch_nums,
|
||||
branches,
|
||||
focus + 1,
|
||||
));
|
||||
let state = state_from_chunked_terms(&branches[focus]);
|
||||
|
||||
if let ClauseIteratorState::RemainingChunks(..) = &state {
|
||||
@@ -425,14 +445,21 @@ impl<'a> Iterator for ClauseIterator<'a> {
|
||||
self.state_stack.push(state);
|
||||
|
||||
return if focus == 0 {
|
||||
Some(ClauseItem::FirstBranch(branches.len()))
|
||||
Some(ClauseItem::FirstBranch {
|
||||
branch_num: &branch_nums[0],
|
||||
num_branches: branches.len(),
|
||||
})
|
||||
} else {
|
||||
Some(ClauseItem::NextBranch)
|
||||
Some(ClauseItem::NextBranch {
|
||||
branch_num: &branch_nums[focus],
|
||||
})
|
||||
};
|
||||
}
|
||||
ClauseIteratorState::RemainingBranches(branches, focus) => {
|
||||
ClauseIteratorState::RemainingBranches(_branch_nums, branches, focus) => {
|
||||
debug_assert_eq!(branches.len(), focus);
|
||||
return Some(ClauseItem::BranchEnd(self.branch_end_depth()));
|
||||
return Some(ClauseItem::BranchEnd {
|
||||
depth: self.branch_end_depth(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
10
src/lib.rs
10
src/lib.rs
@@ -29,10 +29,10 @@ pub(crate) mod heap_print;
|
||||
mod http;
|
||||
mod indexing;
|
||||
mod variable_records;
|
||||
|
||||
#[macro_use]
|
||||
pub(crate) mod instructions {
|
||||
include!(concat!(env!("OUT_DIR"), "/instructions.rs"));
|
||||
}
|
||||
pub(crate) mod instructions;
|
||||
|
||||
mod iterators;
|
||||
pub(crate) mod machine;
|
||||
mod raw_block;
|
||||
@@ -43,9 +43,9 @@ mod targets;
|
||||
pub(crate) mod types;
|
||||
|
||||
// Re-exports
|
||||
pub use machine::Machine;
|
||||
pub use machine::config::*;
|
||||
pub use machine::lib_machine::*;
|
||||
pub use machine::Machine;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub mod wasm;
|
||||
@@ -54,6 +54,8 @@ pub mod wasm;
|
||||
/// The entry point for the Scryer Prolog CLI.
|
||||
pub fn run_binary() -> std::process::ExitCode {
|
||||
use crate::atom_table::Atom;
|
||||
|
||||
#[cfg(feature = "repl")]
|
||||
use crate::machine::INTERRUPT;
|
||||
|
||||
#[cfg(feature = "repl")]
|
||||
|
||||
@@ -59,7 +59,7 @@ get_attrs_var_check(Module) -->
|
||||
!,
|
||||
'$get_attr_list'(Var, Ls),
|
||||
nonvar(Ls),
|
||||
atts:'$copy_attr_list'(Ls, Module, Attr))].
|
||||
atts:'$copy_attr_list'(Ls, Module, Attr))].
|
||||
|
||||
put_attrs(Name/Arity, Module) -->
|
||||
put_attr(Name, Arity, Module),
|
||||
|
||||
@@ -12,7 +12,7 @@ integers that may also be interesting.
|
||||
:- use_module(library(lists), [length/2]).
|
||||
:- use_module(library(error)).
|
||||
|
||||
%% between(+Lower, +Upper, -X).
|
||||
%% 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
|
||||
|
||||
@@ -944,65 +944,6 @@ findall(Template, Goal, Solutions0, Solutions1) :-
|
||||
builtins:findall_cleanup(LhLength, Error)
|
||||
).
|
||||
|
||||
:- non_counted_backtracking set_difference/3.
|
||||
|
||||
set_difference([X|Xs], [Y|Ys], Zs) :-
|
||||
X == Y, !, set_difference(Xs, [Y|Ys], Zs).
|
||||
set_difference([X|Xs], [Y|Ys], [X|Zs]) :-
|
||||
X @< Y, !, set_difference(Xs, [Y|Ys], Zs).
|
||||
set_difference([X|Xs], [Y|Ys], Zs) :-
|
||||
X @> Y, !, set_difference([X|Xs], 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) :-
|
||||
variant(V1, V2),
|
||||
!,
|
||||
V1 = V2,
|
||||
group_by_variant(Pairs, V2-S2, Solutions, Pairs0).
|
||||
group_by_variant(Pairs, _, [], Pairs).
|
||||
|
||||
:- non_counted_backtracking group_by_variants/2.
|
||||
|
||||
group_by_variants([V-S|Pairs], [V-Solution|Solutions]) :-
|
||||
group_by_variant([V-S|Pairs], V-S, Solution, Pairs0),
|
||||
group_by_variants(Pairs0, Solutions).
|
||||
group_by_variants([], []).
|
||||
|
||||
:- non_counted_backtracking iterate_variants/3.
|
||||
|
||||
@@ -1035,9 +976,7 @@ findall_with_existential(Template, Goal, PairedSolutions, Witnesses0, Witnesses)
|
||||
( Goal1 = _ ^ _ ) ->
|
||||
rightmost_power(Goal1, Goal2, ExistentialVars0),
|
||||
term_variables(ExistentialVars0, ExistentialVars),
|
||||
sort(Witnesses0, Witnesses1),
|
||||
sort(ExistentialVars, ExistentialVars1),
|
||||
set_difference(Witnesses1, ExistentialVars1, Witnesses),
|
||||
lists:append(Witnesses0, Witnesses, ExistentialVars),
|
||||
expand_goal(M:Goal2, M, Goal3),
|
||||
findall(Witnesses-Template, Goal3, PairedSolutions)
|
||||
; Witnesses = Witnesses0,
|
||||
@@ -1045,6 +984,34 @@ findall_with_existential(Template, Goal, PairedSolutions, Witnesses0, Witnesses)
|
||||
).
|
||||
|
||||
|
||||
:- non_counted_backtracking split_by_variant/4.
|
||||
|
||||
:- non_counted_backtracking split_by_variant/3.
|
||||
|
||||
:- non_counted_backtracking unify_variant_variables/2.
|
||||
|
||||
split_by_variant([V2-S2 | Pairs], V1-S1, Solutions, Rest) :-
|
||||
( V1 == V2 ->
|
||||
Solutions = [S2 | Solutions1],
|
||||
split_by_variant(Pairs, V1-S1, Solutions1, Rest)
|
||||
; Solutions = [],
|
||||
Rest = [V2-S2 | Pairs]
|
||||
).
|
||||
split_by_variant([], _, [], []).
|
||||
|
||||
split_by_variant([V-S|Pairs], Ws, Solutions) :-
|
||||
split_by_variant(Pairs, V-S, Solutions0, Rest),
|
||||
( Rest == [] -> V = Ws, Solutions = [S|Solutions0]
|
||||
; V = Ws, Solutions = [S|Solutions0]
|
||||
; split_by_variant(Rest, Ws, Solutions)
|
||||
).
|
||||
|
||||
unify_variant_variables([], _Dict).
|
||||
unify_variant_variables([V-_S|Pairs], Dict) :-
|
||||
term_variables(V, VVars),
|
||||
lists:append(VVars, _, Dict),
|
||||
unify_variant_variables(Pairs, Dict).
|
||||
|
||||
:- meta_predicate(bagof(?, 0, ?)).
|
||||
|
||||
:- non_counted_backtracking bagof/3.
|
||||
@@ -1074,22 +1041,10 @@ bagof(Template, Goal, Solution) :-
|
||||
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),
|
||||
iterate_variants(GroupedSolutions, Witnesses, Solution).
|
||||
|
||||
:- non_counted_backtracking iterate_variants_and_sort/3.
|
||||
|
||||
iterate_variants_and_sort([V-Solution0|GroupSolutions], V, Solution) :-
|
||||
sort(Solution0, Solution1),
|
||||
Solution1 = Solution,
|
||||
( GroupSolutions == [] -> !
|
||||
; true
|
||||
).
|
||||
iterate_variants_and_sort([_|GroupSolutions], Ws, Solution) :-
|
||||
iterate_variants_and_sort(GroupSolutions, Ws, Solution).
|
||||
|
||||
findall_with_existential(Template, Goal, PairedSolutions, Witnesses0, Witnesses),
|
||||
unify_variant_variables(PairedSolutions, _Dict),
|
||||
keysort(PairedSolutions, PairedSolutions1),
|
||||
split_by_variant(PairedSolutions1, Witnesses, Solution).
|
||||
|
||||
:- meta_predicate(setof(?, 0, ?)).
|
||||
|
||||
@@ -1112,10 +1067,10 @@ setof(Template, Goal, Solution) :-
|
||||
term_variables(Goal, GoalVars),
|
||||
term_variables(TemplateVars+GoalVars, TGVs),
|
||||
lists:append(TemplateVars, Witnesses0, TGVs),
|
||||
findall_with_existential(Template, Goal, PairedSolutions0, Witnesses0, Witnesses),
|
||||
'$keysort_with_constant_var_ordering'(PairedSolutions0, PairedSolutions), % see 7.2.1
|
||||
group_by_variants(PairedSolutions, GroupedSolutions),
|
||||
iterate_variants_and_sort(GroupedSolutions, Witnesses, Solution).
|
||||
findall_with_existential(Template, Goal, PairedSolutions, Witnesses0, Witnesses),
|
||||
unify_variant_variables(PairedSolutions, _Dict),
|
||||
sort(PairedSolutions, PairedSolutions1),
|
||||
split_by_variant(PairedSolutions1, Witnesses, Solution).
|
||||
|
||||
% Clause retrieval and information.
|
||||
|
||||
@@ -1551,7 +1506,7 @@ atom_chars(Atom, List) :-
|
||||
%% 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
|
||||
% between atoms and strings. However, codes is not the default representation of double quoted
|
||||
% strings in Scryer Prolog. Examples:
|
||||
%
|
||||
% ```
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
Author: Markus Triska
|
||||
E-mail: triska@metalevel.at
|
||||
WWW: https://www.metalevel.at
|
||||
Copyright (C): 2016-2024 Markus Triska
|
||||
Copyright (C): 2016-2026 Markus Triska
|
||||
|
||||
This library provides CLP(ℤ):
|
||||
|
||||
@@ -6844,6 +6844,22 @@ gcc_global(Vs, KNs) -->
|
||||
% the propagator of tuples_in/2). Hence: We need this only if
|
||||
% an example shows it, ideally found by a systematic search
|
||||
% that can be used to test the implementation.
|
||||
%
|
||||
% UPDATE: A case that requires gcc_check//1 was recently found,
|
||||
% see: https://github.com/mthom/scryer-prolog/issues/3369.
|
||||
%
|
||||
% We need to study carefully what exactly happens in this case.
|
||||
% The example involves reified constraints, which post constraints
|
||||
% in their propagators. The example also involves multiple queues,
|
||||
% because not all reified constraints use variables_same_queue/1
|
||||
% (for instance (#==>)/2 does not), and variables_same_queue/1 would
|
||||
% in fact need changes to handle cases where variables no longer
|
||||
% have a clpz attribute attached. Such cases arise for variables
|
||||
% involved in entailed reified constraints.
|
||||
|
||||
gcc_check(KNs), % see comment above.
|
||||
do_queue, % is this needed? this (also) needs more analysis.
|
||||
|
||||
{ with_local_attributes(Vs,
|
||||
(gcc_arcs(KNs, S, Vals),
|
||||
variables_with_num_occurrences(Vs, VNs),
|
||||
|
||||
@@ -11,34 +11,75 @@ The main predicate is `use_foreign_module/2`. It takes a library name (which dep
|
||||
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`/`i8`, `uint8`/`u8`, `sint16`/`i16`, `uint16`/`u16`, `sint32`/`i32`, `uint32`/`u32`, `sint64`/`i64`,
|
||||
`uint64`/`u64`, `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.
|
||||
For each function in the list a predicate of the same name is generated in the ffi module which
|
||||
can then be used to call the native code.
|
||||
The predicates arguments are the input arguments of the foreign function and depending on the return type an extra argument for the return value.
|
||||
Functions with return type `void` or `bool` don't have this extra argument.
|
||||
Predicates for functions with return type `void` always succeed.
|
||||
Predicates for functions with retun type `bool` succeed iff the return value is 1.
|
||||
|
||||
```
|
||||
ffi:FUNCTION_NAME(+InputArg1, ..., +InputArgN, -ReturnArg). % for all return types except void and bool
|
||||
ffi:FUNCTION_NAME(+InputArg1, ..., +InputArgN). % for void and bool
|
||||
```
|
||||
|
||||
## Notes regarding cstr
|
||||
## Available types are
|
||||
|
||||
### Basic C Types
|
||||
|
||||
[C Types Reference](https://en.cppreference.com/w/c/language/types.html)
|
||||
|
||||
- `void`,
|
||||
- `char`, `uchar`, `schar`
|
||||
- `short`, `ushort`
|
||||
- `int`, `uint`
|
||||
- `long`, `ulong`,
|
||||
- `longlong`, `ulonglong`,
|
||||
- `float`, `double`
|
||||
|
||||
### Fixed Width Integer Types
|
||||
|
||||
[C Fixed With Integer Types Reference](https://en.cppreference.com/w/c/types/integer.html)
|
||||
|
||||
- `sint8`/`i8`, `uint8`/`u8`,
|
||||
- `sint16`/`i16`, `uint16`/`u16`,
|
||||
- `sint32`/`i32`, `uint32`/`u32`,
|
||||
- `sint64`/`i64`, `uint64`/`u64`,
|
||||
|
||||
### Fixed Width Floating-Point Types
|
||||
|
||||
[C++ Fixed Width Floating-Point Types Reference](https://en.cppreference.com/w/cpp/types/floating-point.html)
|
||||
|
||||
- `f32`, `f64`
|
||||
|
||||
### Other Types
|
||||
|
||||
- `cstr`,
|
||||
- `ptr`,
|
||||
- `bool` and,
|
||||
- custom structs, which can be defined with `foreign_struct/2`.
|
||||
|
||||
### Notes regarding bool
|
||||
|
||||
- Not necessarily compatible with the fundamental C type bool.
|
||||
- Same as `i8` but only values 0 and 1 are valid values.
|
||||
|
||||
### Notes regarding cstr
|
||||
|
||||
- When using `cstr` as an argument type the string will be deallocated once the function returns.
|
||||
- When using `cstr` as a return type the string will be copied and won't be deallocated.
|
||||
|
||||
- When an ffi function returns bytes that are not a valid utf8-string the bytes will be turned into a list of `codes` (integers)
|
||||
instead of a string (list of `chars`). Note: passing a list of `codes` is not accepted in argument position.
|
||||
- In argument position you can also pass a pointer directly instead of a string,
|
||||
e.g. to pass a null-pointer one can provide the integer 0 as the argument.
|
||||
- In return position a null-pointer will be returned as the integer 0
|
||||
|
||||
## 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)]).
|
||||
?- use_foreign_module("./libraylib.so", ['InitWindow'([int, int, cstr], void)]).
|
||||
```
|
||||
|
||||
This creates a `'InitWindow'` predicate under the ffi module. Now, we can call it:
|
||||
@@ -143,7 +184,7 @@ allocate(Allocator, Type, Args, Ptr) :-
|
||||
%
|
||||
% Read a value of Type from the pointer Ptr and unify the read value with Value
|
||||
%
|
||||
% For type cstr take read a nul-terminated utf-8 string starting at Ptr.
|
||||
% For type cstr read a nul-terminated utf-8 string starting at Ptr.
|
||||
%
|
||||
read_ptr(Type, Ptr, Value) :-
|
||||
must_be(atom, Type),
|
||||
@@ -163,7 +204,7 @@ deallocate(Allocator, Type, Ptr) :-
|
||||
|
||||
%% array_type(+ElemType, +Len, -ArrayType)
|
||||
%
|
||||
% unify the ffi type for an array of lenth Len with element type ElemType with ArrayType
|
||||
% unify the ffi type for an array of length Len with element type ElemType with ArrayType
|
||||
%
|
||||
array_type(ElemType, Len, ArrayType) :-
|
||||
(Len =< 0 -> domain_error(greater_than_zero, Len, array_type/3); true),
|
||||
@@ -183,7 +224,7 @@ array_type(ElemType, Len, ArrayType) :-
|
||||
% Allocate the Locals, evaluate the Goal and deallocate the Locals.
|
||||
% The Locals will also be cleandup when Goal fails or throws an error.
|
||||
%
|
||||
% Locals is a list of local variable definitions let(-Ptr, +Type, +Args).
|
||||
% Locals is a list of local variable definitions `let(-Ptr, +Type, +Args)`.
|
||||
% Ptr will be unified with the pointer to the local of Type initialized with Args.
|
||||
%
|
||||
with_locals(Locals, Goal) :-
|
||||
|
||||
@@ -112,12 +112,29 @@ module_qualification(M, H0, H) :-
|
||||
H0 =.. [Method, Path, Goal],
|
||||
H =.. [Method, Path, M:Goal].
|
||||
|
||||
http_listen__(Addr, HttpListener, TLSKey, TLSCert, ContentLengthLimit) :-
|
||||
'$http_listen'(Addr, HttpListener, TLSKey, TLSCert, ContentLengthLimit).
|
||||
|
||||
http_listen_stop_(HttpListener) :-
|
||||
'$http_listen_stop'(HttpListener).
|
||||
|
||||
http_accept_(HttpListener, RequestMethod, RequestPath, RequestHeaders, RequestQuery, RequestStream, ResponseHandle) :-
|
||||
'$http_accept'(HttpListener, RequestMethod, RequestPath, RequestHeaders, RequestQuery, RequestStream, ResponseHandle).
|
||||
|
||||
http_answer_(ResponseHandle, Code, Headers, ResponseStream) :-
|
||||
'$http_answer'(ResponseHandle, Code, Headers, ResponseStream).
|
||||
|
||||
http_listen_(Port, Handlers, Options) :-
|
||||
parse_options(Options, TLSKey, TLSCert, ContentLengthLimit),
|
||||
phrase(format_("0.0.0.0:~d", [Port]), Addr),
|
||||
'$http_listen'(Addr, HttpListener, TLSKey, TLSCert, ContentLengthLimit),!,
|
||||
format("Listening at ~s\n", [Addr]),
|
||||
http_loop(HttpListener, Handlers).
|
||||
setup_call_cleanup(
|
||||
(
|
||||
http_listen__(Addr, HttpListener, TLSKey, TLSCert, ContentLengthLimit),
|
||||
format("Listening at http://~s\n", [Addr])
|
||||
),
|
||||
http_loop(HttpListener, Handlers),
|
||||
http_listen_stop_(HttpListener)
|
||||
).
|
||||
|
||||
parse_options(Options, TLSKey, TLSCert, ContentLengthLimit) :-
|
||||
member_option_default(tls_key, Options, "", TLSKey),
|
||||
@@ -131,37 +148,56 @@ member_option_default(Key, List, _Default, Value) :-
|
||||
member_option_default(Key, List, Default, Default) :-
|
||||
X =.. [Key, _],
|
||||
\+ member(X, List).
|
||||
|
||||
|
||||
http_loop(HttpListener, Handlers) :-
|
||||
'$http_accept'(HttpListener, RequestMethod, RequestPath, RequestHeaders, RequestQuery, RequestStream, ResponseHandle),
|
||||
current_time(Time),
|
||||
phrase(format_time("%Y-%m-%d (%H:%M:%S)", Time), TimeString),
|
||||
format("~s ~w ~s\n", [TimeString, RequestMethod, RequestPath]),
|
||||
maplist(map_header_kv, RequestHeaders, RequestHeadersKV),
|
||||
phrase(parse_queries(RequestQueries), RequestQuery),
|
||||
(
|
||||
match_handler(Handlers, RequestMethod, RequestPath, Handler) ->
|
||||
(
|
||||
HttpRequest = http_request(RequestHeadersKV, stream(RequestStream), RequestQueries),
|
||||
HttpResponse = http_response(_, _, _),
|
||||
(call(Handler, HttpRequest, HttpResponse) ->
|
||||
send_response(ResponseHandle, HttpResponse)
|
||||
; (
|
||||
'$http_answer'(ResponseHandle, 500, [], ResponseStream),
|
||||
call_cleanup(format(ResponseStream, "Internal Server Error", []), close(ResponseStream)))
|
||||
)
|
||||
)
|
||||
; (
|
||||
'$http_answer'(ResponseHandle, 404, [], ResponseStream),
|
||||
call_cleanup(format(ResponseStream, "Not Found", []), close(ResponseStream)))
|
||||
),
|
||||
time((
|
||||
http_accept_(HttpListener, RequestMethod, RequestPath, RequestHeaders, RequestQuery, RequestStream, ResponseHandle),
|
||||
current_time(Time),
|
||||
phrase(format_time("%Y-%m-%d (%H:%M:%S)", Time), TimeString),
|
||||
format("~s ~w ~s", [TimeString, RequestMethod, RequestPath]),
|
||||
maplist(map_header_kv, RequestHeaders, RequestHeadersKV),
|
||||
phrase(parse_queries(RequestQueries), RequestQuery),
|
||||
(
|
||||
match_handler(Handlers, RequestMethod, RequestPath, Handler) ->
|
||||
(
|
||||
HttpRequest = http_request(RequestHeadersKV, stream(RequestStream), RequestQueries),
|
||||
HttpResponse = http_response(_, _, _),
|
||||
catch(
|
||||
(call(Handler, HttpRequest, HttpResponse) ->
|
||||
send_response(ResponseHandle, HttpResponse)
|
||||
;
|
||||
setup_call_cleanup(
|
||||
http_answer_(ResponseHandle, 500, [], ResponseStream),
|
||||
format(ResponseStream, "Internal Server Error", []),
|
||||
close(ResponseStream)
|
||||
),
|
||||
throw(handler_not_available(Handler, RequestMethod, RequestPath, RequestQuery, RequestHeaders))
|
||||
),
|
||||
HandlerError,
|
||||
(
|
||||
setup_call_cleanup(
|
||||
http_answer_(ResponseHandle, 500, [], ResponseStream),
|
||||
format(ResponseStream, "Internal Server Error", []),
|
||||
close(ResponseStream)
|
||||
),
|
||||
throw(HandlerError)
|
||||
)
|
||||
)
|
||||
)
|
||||
;
|
||||
setup_call_cleanup(
|
||||
http_answer_(ResponseHandle, 404, [], ResponseStream),
|
||||
format(ResponseStream, "Not Found", []),
|
||||
close(ResponseStream)
|
||||
)
|
||||
)
|
||||
)),
|
||||
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, ResponseStream0),
|
||||
http_answer_(ResponseHandle, StatusCode, ResponseHeaders, ResponseStream0),
|
||||
open(stream(ResponseStream0), write, ResponseStream, [type(text)]),
|
||||
catch(
|
||||
call_cleanup(format(ResponseStream, "~s", [ResponseText]),close(ResponseStream)),
|
||||
@@ -172,9 +208,9 @@ send_response(ResponseHandle, http_response(StatusCode0, text(ResponseText), Res
|
||||
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),
|
||||
http_answer_(ResponseHandle, StatusCode, ResponseHeaders, ResponseStream),
|
||||
catch(
|
||||
call_cleanup(format(ResponseStream, "~s", [ResponseBytes]),close(ResponseStream)),
|
||||
call_cleanup(format(ResponseStream, "~s", [ResponseBytes]),close(ResponseStream)),
|
||||
error(existence_error(stream, _), _),
|
||||
true
|
||||
).
|
||||
@@ -182,7 +218,7 @@ send_response(ResponseHandle, http_response(StatusCode0, bytes(ResponseBytes), R
|
||||
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),
|
||||
http_answer_(ResponseHandle, StatusCode, ResponseHeaders, ResponseStream),
|
||||
catch(
|
||||
call_cleanup(
|
||||
setup_call_cleanup(
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
List manipulation predicates
|
||||
*/
|
||||
|
||||
:- module(lists, [member/2, select/3, append/2, append/3, foldl/4, foldl/5,
|
||||
:- module(lists, [member/2, select/3, append/2, append/3, foldl/4, foldl/5, foldl/6,
|
||||
memberchk/2, reverse/2, length/2, maplist/2,
|
||||
maplist/3, maplist/4, maplist/5, maplist/6,
|
||||
maplist/7, maplist/8, maplist/9, same_length/2, nth0/3, nth0/4, nth1/3, nth1/4,
|
||||
@@ -314,6 +314,16 @@ foldl(G_4, [X|Xs], [Y|Ys], A0, A) :-
|
||||
call(G_4, X, Y, A0, A1),
|
||||
foldl(G_4, Xs, Ys, A1, A).
|
||||
|
||||
|
||||
%% foldl(+Goal, ?Ls0, ?Ls1, ?Ls2, +A0, ?A).
|
||||
%
|
||||
% Like `foldl/4`, with 2 additional lists.
|
||||
|
||||
foldl(_, [], [], [], A, A).
|
||||
foldl(G_5, [X|Xs], [Y|Ys], [Z|Zs], A0, A) :-
|
||||
call(G_5, X, Y, Z, A0, A1),
|
||||
foldl(G_5, Xs, Ys, Zs, A1, A).
|
||||
|
||||
%% transpose(?Ls, ?Ts).
|
||||
%
|
||||
% If Ls is a list of lists, Ts contains the transposition
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
% Efforts toward literate tests with quads
|
||||
|
||||
|
||||
:- module(quadtests, [check_module_quads/2]).
|
||||
|
||||
:- use_module(library(iso_ext)).
|
||||
:- use_module(library(pio)).
|
||||
:- use_module(library(lists)).
|
||||
@@ -10,9 +12,8 @@
|
||||
:- use_module(library(lambda)).
|
||||
:- use_module(library(error)).
|
||||
:- use_module(library(time)).
|
||||
|
||||
:- use_module(testutils).
|
||||
:- use_module(special_functions).
|
||||
:- use_module(library('numerics/testutils')).
|
||||
:- use_module(library('numerics/special_functions')).
|
||||
|
||||
portray_term(Stream) :-
|
||||
read_term(Stream, Term, []),
|
||||
@@ -39,7 +40,7 @@ check_module_quads(Module, Quads) :-
|
||||
zip(Qs, ADs, Quads),
|
||||
length(Qs, NQ),
|
||||
format("% Checking ~d quads ..~n", [NQ]),
|
||||
maplist(check_qu_ad, Qs, ADs).
|
||||
maplist(check_qu_ad(Module), Qs, ADs).
|
||||
|
||||
read_quads(Module, Quads) :-
|
||||
module_terms(Module, Terms),
|
||||
@@ -70,6 +71,7 @@ term_type(Term-_, Type) :-
|
||||
( Term = (?- _) -> Type = query
|
||||
; Term = (_,_) -> Type = answer_description
|
||||
; Term = (_;_) -> Type = answer_description
|
||||
; Term = (_ = _) -> Type = answer_description
|
||||
; Term == true -> Type = answer_description
|
||||
; Term == false -> Type = answer_description
|
||||
; Type = clause
|
||||
@@ -114,22 +116,22 @@ zip([], [], []).
|
||||
Xs = [1,2,3], Ys = [4,5,6].
|
||||
|
||||
% 3. Demonstrate checking 1 quad, the top two elements of a QAs list.
|
||||
check_qu_ad(Q-QVN, A-AVN) :-
|
||||
check_qu_ad(Module, Q-QVN, A-AVN) :-
|
||||
Q = ?-(G),
|
||||
phrase(portray_clause_(Q), LitQ), % NB: LitQ terminates w/ newline
|
||||
format("% CHECKING.. ",[]),
|
||||
( A == true -> call(G)
|
||||
; A == false -> ( call(G) -> false
|
||||
( A == true -> call(Module:G)
|
||||
; A == false -> ( call(Module:G) -> false
|
||||
; true
|
||||
)
|
||||
; phrase(unconj(A), As) ->
|
||||
( length(As, N),
|
||||
n_answers(N, A, AVN, ADs),
|
||||
n_answers(N, G, QVN, Answers),
|
||||
n_answers(N, Module:G, QVN, Answers),
|
||||
maplist(contains, ADs, Answers)
|
||||
)
|
||||
; % Otherwise, we have the ',' case of a solitary answer
|
||||
call(G),
|
||||
call(Module:G),
|
||||
call(A),
|
||||
QVN == AVN
|
||||
),
|
||||
@@ -141,7 +143,7 @@ contains(AD, Answer) :- append(Answer, _, AD).
|
||||
?- contains(['Xs'=[C],'L'=1,'_A'=C,'_B'=D], ['Xs'=[A],'L'=1]).
|
||||
C = A.
|
||||
|
||||
?- check_qu_ad((?-length(_F,_G))-['Xs'=_F,'L'=_G],(_H=[],_I=0;_H=[_J],_I=1;_H=[_J,_K],_I=2;...)-['Xs'=_H,'L'=_I,'_A'=_J,'_B'=_K]).
|
||||
?- check_qu_ad(quadtests, (?-length(_F,_G))-['Xs'=_F,'L'=_G],(_H=[],_I=0;_H=[_J],_I=1;_H=[_J,_K],_I=2;...)-['Xs'=_H,'L'=_I,'_A'=_J,'_B'=_K]).
|
||||
% CHECKING.. (?-length(A,B)).
|
||||
_F = [_A,_B], _G = 2, _H = [_J,_K], _I = 2.
|
||||
|
||||
@@ -182,6 +184,7 @@ n_answers_(N, G, VN, ADs) :-
|
||||
!,
|
||||
retract('$anstack'(As)),
|
||||
reverse(As, ADs).
|
||||
|
||||
|
||||
?- n_answers(3, length(Xs, L), ('Xs'=Xs,'Len'=L), ADs).
|
||||
Xs = [_A,_B], L = 2, ADs = [('Xs'=[],'Len'=0),('Xs'=[_C],'Len'=1),('Xs'=[_D,_E],'Len'=2)].
|
||||
|
||||
|
||||
@@ -95,10 +95,12 @@ process_wait(Process, Status) :- call_with_error_context(process_wait(Process, S
|
||||
% `Options` is a a list of the following options
|
||||
%
|
||||
% * timeout(Timeout) supported values for `Timeout` are 0 or `infinite`
|
||||
% * release(Bool) supported values for `Bool` are `true` or `false`
|
||||
%
|
||||
% Each options may be specified at most once, when an option is not specified the following defaults apply:
|
||||
%
|
||||
% - timeout(infinite)
|
||||
% - release(true)
|
||||
%
|
||||
process_wait(Process, Status, Options) :- call_with_error_context(process_wait_(Process, Status, Options), predicate-process_wait/3).
|
||||
|
||||
@@ -106,17 +108,29 @@ process_wait_(Process, Status, Options) :-
|
||||
valid_process(Process),
|
||||
check_options(
|
||||
[
|
||||
option([timeout], valid_timeout, timeout(infinite), timeout(Timeout))
|
||||
option([timeout], valid_timeout, timeout(infinite), timeout(Timeout)),
|
||||
option([release], valid_release, release(true), release(Release))
|
||||
],
|
||||
Options,
|
||||
process_wait_option
|
||||
),
|
||||
'$process_wait'(Process, Exit, Timeout),
|
||||
((true = Release) -> '$process_release'(Process) ; true),
|
||||
Exit = Status.
|
||||
|
||||
valid_timeout(timeout(infinite)).
|
||||
valid_timeout(timeout(0)).
|
||||
|
||||
valid_release(release(Arg)) :-
|
||||
( var(Arg) -> instantiation_error([])
|
||||
; valid_bool(Arg) -> true
|
||||
; domain_error(boolean, Arg, [])
|
||||
).
|
||||
|
||||
|
||||
valid_bool(true).
|
||||
valid_bool(false).
|
||||
|
||||
|
||||
%% process_kill(+Process).
|
||||
%
|
||||
@@ -139,10 +153,9 @@ process_kill_(Process) :-
|
||||
%
|
||||
process_release(Process) :- call_with_error_context(process_release_(Process), predicate-process_release/1).
|
||||
|
||||
process_release_(Process) :-
|
||||
process_release_(Process) :-
|
||||
valid_process(Process),
|
||||
process_wait(Process, _),
|
||||
'$process_release'(Process).
|
||||
process_wait(Process, _).
|
||||
|
||||
|
||||
must_be_known_options(Valid, Options, Domain) :- call_with_error_context(must_be_known_options_(Valid, [], Options, Domain),predicate-must_be_known_options/3).
|
||||
@@ -184,9 +197,9 @@ find_option(Names, Found, T) :-
|
||||
functor(Found, Name, 1),
|
||||
memberd_t(Name, Names, T).
|
||||
|
||||
valid_stdio(IO) :- arg(1, IO, Arg),
|
||||
( var(Arg) -> instantiation_error([])
|
||||
; valid_stdio_(Arg) -> true
|
||||
valid_stdio(IO) :- arg(1, IO, Arg),
|
||||
( var(Arg) -> instantiation_error([])
|
||||
; valid_stdio_(Arg) -> true
|
||||
; domain_error(stdio_spec, Arg, [])
|
||||
).
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
term_si/1,
|
||||
chars_si/1,
|
||||
dif_si/2,
|
||||
not_si/1,
|
||||
when_si/2]).
|
||||
|
||||
:- use_module(library(lists)).
|
||||
@@ -100,6 +101,17 @@ dif_si(X, Y) :-
|
||||
; throw(error(instantiation_error,dif_si/2))
|
||||
).
|
||||
|
||||
%% not_si(+Goal).
|
||||
%
|
||||
% True if Goal is not provable. Instantiation error if Goal is not
|
||||
% ground.
|
||||
|
||||
:- meta_predicate(not_si(0)).
|
||||
|
||||
not_si(Goal) :-
|
||||
term_si(Goal),
|
||||
\+ Goal.
|
||||
|
||||
:- meta_predicate(when_si(+, 0)).
|
||||
|
||||
%% when_si(Condition, Goal).
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use std::collections::BTreeSet;
|
||||
use std::env;
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -8,9 +7,8 @@ pub struct MachineArgs {
|
||||
|
||||
impl MachineArgs {
|
||||
pub fn new() -> Self {
|
||||
let args: BTreeSet<String> = env::args().collect();
|
||||
Self {
|
||||
add_history: !args.contains("--no-add-history"),
|
||||
add_history: env::args().all(|arg| arg != "--no-add-history"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use dashu::base::{Abs, Gcd, Signed, UnsignedAbs};
|
||||
use dashu::integer::fast_div::ConstDivisor;
|
||||
use dashu::integer::IBig;
|
||||
use dashu::integer::fast_div::ConstDivisor;
|
||||
use divrem::*;
|
||||
use num_order::NumOrd;
|
||||
|
||||
@@ -21,7 +21,6 @@ use ordered_float::{Float, OrderedFloat};
|
||||
use std::cmp;
|
||||
use std::convert::TryFrom;
|
||||
use std::f64;
|
||||
use std::mem;
|
||||
|
||||
macro_rules! try_numeric_result {
|
||||
($e: expr, $stub_gen: expr) => {
|
||||
@@ -1120,23 +1119,18 @@ pub(crate) fn bitwise_complement(n1: Number, arena: &mut Arena) -> Result<Number
|
||||
impl MachineState {
|
||||
#[inline]
|
||||
pub fn get_number(&mut self, at: &ArithmeticTerm) -> Result<Number, MachineStub> {
|
||||
match at {
|
||||
&ArithmeticTerm::Reg(r) => {
|
||||
let value = self.store(self.deref(self[r]));
|
||||
let value = match at {
|
||||
&ArithmeticTerm::Reg(r) => self.store(self.deref(self[r])),
|
||||
&ArithmeticTerm::IntermReg(i) => self.registers[i],
|
||||
ArithmeticTerm::Number(n) => return Ok(*n),
|
||||
};
|
||||
|
||||
match Number::try_from((value, &self.arena.f64_tbl)) {
|
||||
Ok(n) => Ok(n),
|
||||
Err(_) => {
|
||||
self.heap[0] = value;
|
||||
self.arith_eval_by_metacall(0)
|
||||
}
|
||||
}
|
||||
match Number::try_from((value, &self.arena.f64_tbl)) {
|
||||
Ok(n) => Ok(n),
|
||||
Err(_) => {
|
||||
self.heap[0] = value;
|
||||
self.arith_eval_by_metacall(0)
|
||||
}
|
||||
&ArithmeticTerm::Interm(i) => Ok(mem::replace(
|
||||
&mut self.interms[i - 1],
|
||||
Number::Fixnum(Fixnum::build_with(0)),
|
||||
)),
|
||||
ArithmeticTerm::Number(n) => Ok(*n),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1158,6 +1152,7 @@ impl MachineState {
|
||||
term_loc: usize,
|
||||
) -> Result<Number, MachineStub> {
|
||||
let stub_gen = || functor_stub(atom!("is"), 2);
|
||||
let mut interms = vec![];
|
||||
let mut iter =
|
||||
stackful_post_order_iter::<NonListElider>(&mut self.heap, &mut self.stack, term_loc);
|
||||
|
||||
@@ -1194,38 +1189,38 @@ impl MachineState {
|
||||
read_heap_cell!(value,
|
||||
(HeapCellValueTag::Atom, (name, arity)) => {
|
||||
if arity == 2 {
|
||||
let a2 = self.interms.pop().unwrap();
|
||||
let a1 = self.interms.pop().unwrap();
|
||||
let a2 = interms.pop().unwrap();
|
||||
let a1 = interms.pop().unwrap();
|
||||
|
||||
match name {
|
||||
atom!("+") => self.interms.push(drop_iter_on_err!(
|
||||
atom!("+") => interms.push(drop_iter_on_err!(
|
||||
self,
|
||||
iter,
|
||||
try_numeric_result!(add(a1, a2, &mut self.arena), stub_gen)
|
||||
)),
|
||||
atom!("-") => self.interms.push(drop_iter_on_err!(
|
||||
atom!("-") => interms.push(drop_iter_on_err!(
|
||||
self,
|
||||
iter,
|
||||
try_numeric_result!(sub(a1, a2, &mut self.arena), stub_gen)
|
||||
)),
|
||||
atom!("*") => self.interms.push(drop_iter_on_err!(
|
||||
atom!("*") => interms.push(drop_iter_on_err!(
|
||||
self,
|
||||
iter,
|
||||
try_numeric_result!(mul(a1, a2, &mut self.arena), stub_gen)
|
||||
)),
|
||||
atom!("/") => self.interms.push(
|
||||
atom!("/") => interms.push(
|
||||
drop_iter_on_err!(self, iter, div(a1, a2))
|
||||
),
|
||||
atom!("**") => self.interms.push(
|
||||
atom!("**") => interms.push(
|
||||
drop_iter_on_err!(self, iter, pow(a1, a2, atom!("is")))
|
||||
),
|
||||
atom!("^") => self.interms.push(
|
||||
atom!("^") => interms.push(
|
||||
drop_iter_on_err!(self, iter, int_pow(a1, a2, &mut self.arena))
|
||||
),
|
||||
atom!("max") => self.interms.push(
|
||||
atom!("max") => interms.push(
|
||||
drop_iter_on_err!(self, iter, max(a1, a2))
|
||||
),
|
||||
atom!("min") => self.interms.push(
|
||||
atom!("min") => interms.push(
|
||||
drop_iter_on_err!(self, iter, min(a1, a2))
|
||||
),
|
||||
atom!("rdiv") => {
|
||||
@@ -1246,39 +1241,39 @@ impl MachineState {
|
||||
&mut self.arena
|
||||
);
|
||||
|
||||
self.interms.push(Number::Rational(result));
|
||||
interms.push(Number::Rational(result));
|
||||
}
|
||||
atom!("//") => self.interms.push(
|
||||
atom!("//") => interms.push(
|
||||
drop_iter_on_err!(self, iter, idiv(a1, a2, &mut self.arena))
|
||||
),
|
||||
atom!("div") => self.interms.push(
|
||||
atom!("div") => interms.push(
|
||||
drop_iter_on_err!(self, iter, int_floor_div(a1, a2, &mut self.arena))
|
||||
),
|
||||
atom!(">>") => self.interms.push(
|
||||
atom!(">>") => interms.push(
|
||||
drop_iter_on_err!(self, iter, shr(a1, a2, &mut self.arena))
|
||||
),
|
||||
atom!("<<") => self.interms.push(
|
||||
atom!("<<") => interms.push(
|
||||
drop_iter_on_err!(self, iter, shl(a1, a2, &mut self.arena))
|
||||
),
|
||||
atom!("/\\") => self.interms.push(
|
||||
atom!("/\\") => interms.push(
|
||||
drop_iter_on_err!(self, iter, and(a1, a2, &mut self.arena))
|
||||
),
|
||||
atom!("\\/") => self.interms.push(
|
||||
atom!("\\/") => interms.push(
|
||||
drop_iter_on_err!(self, iter, or(a1, a2, &mut self.arena))
|
||||
),
|
||||
atom!("xor") => self.interms.push(
|
||||
atom!("xor") => interms.push(
|
||||
drop_iter_on_err!(self, iter, xor(a1, a2, &mut self.arena))
|
||||
),
|
||||
atom!("mod") => self.interms.push(
|
||||
atom!("mod") => interms.push(
|
||||
drop_iter_on_err!(self, iter, modulus(a1, a2, &mut self.arena))
|
||||
),
|
||||
atom!("rem") => self.interms.push(
|
||||
atom!("rem") => interms.push(
|
||||
drop_iter_on_err!(self, iter, remainder(a1, a2, &mut self.arena))
|
||||
),
|
||||
atom!("atan2") => self.interms.push(Number::Float(OrderedFloat(
|
||||
atom!("atan2") => interms.push(Number::Float(OrderedFloat(
|
||||
drop_iter_on_err!(self, iter, atan2(a1, a2))
|
||||
))),
|
||||
atom!("gcd") => self.interms.push(
|
||||
atom!("gcd") => interms.push(
|
||||
drop_iter_on_err!(self, iter, gcd(a1, a2, &mut self.arena))
|
||||
),
|
||||
_ => {
|
||||
@@ -1294,56 +1289,56 @@ impl MachineState {
|
||||
|
||||
continue;
|
||||
} else if arity == 1 {
|
||||
let a1 = self.interms.pop().unwrap();
|
||||
let a1 = interms.pop().unwrap();
|
||||
|
||||
match name {
|
||||
atom!("-") => self.interms.push(neg(a1, &mut self.arena)),
|
||||
atom!("+") => self.interms.push(a1),
|
||||
atom!("cos") => self.interms.push(Number::Float(OrderedFloat(
|
||||
atom!("-") => interms.push(neg(a1, &mut self.arena)),
|
||||
atom!("+") => interms.push(a1),
|
||||
atom!("cos") => interms.push(Number::Float(OrderedFloat(
|
||||
drop_iter_on_err!(self, iter, cos(a1))
|
||||
))),
|
||||
atom!("sin") => self.interms.push(Number::Float(OrderedFloat(
|
||||
atom!("sin") => interms.push(Number::Float(OrderedFloat(
|
||||
drop_iter_on_err!(self, iter, sin(a1))
|
||||
))),
|
||||
atom!("tan") => self.interms.push(Number::Float(OrderedFloat(
|
||||
atom!("tan") => interms.push(Number::Float(OrderedFloat(
|
||||
drop_iter_on_err!(self, iter, tan(a1))
|
||||
))),
|
||||
atom!("float_fractional_part") => self.interms.push(Number::Float(OrderedFloat(
|
||||
atom!("float_fractional_part") => 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(
|
||||
atom!("float_integer_part") => interms.push(Number::Float(OrderedFloat(
|
||||
drop_iter_on_err!(self, iter, float_integer_part(a1))
|
||||
))),
|
||||
atom!("sqrt") => self.interms.push(Number::Float(OrderedFloat(
|
||||
atom!("sqrt") => interms.push(Number::Float(OrderedFloat(
|
||||
drop_iter_on_err!(self, iter, sqrt(a1))
|
||||
))),
|
||||
atom!("log") => self.interms.push(Number::Float(OrderedFloat(
|
||||
atom!("log") => interms.push(Number::Float(OrderedFloat(
|
||||
drop_iter_on_err!(self, iter, log(a1))
|
||||
))),
|
||||
atom!("exp") => self.interms.push(Number::Float(OrderedFloat(
|
||||
atom!("exp") => interms.push(Number::Float(OrderedFloat(
|
||||
drop_iter_on_err!(self, iter, exp(a1))
|
||||
))),
|
||||
atom!("acos") => self.interms.push(Number::Float(OrderedFloat(
|
||||
atom!("acos") => interms.push(Number::Float(OrderedFloat(
|
||||
drop_iter_on_err!(self, iter, acos(a1))
|
||||
))),
|
||||
atom!("asin") => self.interms.push(Number::Float(OrderedFloat(
|
||||
atom!("asin") => interms.push(Number::Float(OrderedFloat(
|
||||
drop_iter_on_err!(self, iter, asin(a1))
|
||||
))),
|
||||
atom!("atan") => self.interms.push(Number::Float(OrderedFloat(
|
||||
atom!("atan") => interms.push(Number::Float(OrderedFloat(
|
||||
drop_iter_on_err!(self, iter, atan(a1))
|
||||
))),
|
||||
atom!("abs") => self.interms.push(abs(a1, &mut self.arena)),
|
||||
atom!("float") => self.interms.push(Number::Float(OrderedFloat(
|
||||
atom!("abs") => interms.push(abs(a1, &mut self.arena)),
|
||||
atom!("float") => interms.push(Number::Float(OrderedFloat(
|
||||
drop_iter_on_err!(self, iter, float(a1))
|
||||
))),
|
||||
atom!("truncate") => self.interms.push(truncate(a1, &mut self.arena)),
|
||||
atom!("round") => self.interms.push(drop_iter_on_err!(self, iter, round(a1, &mut self.arena))),
|
||||
atom!("ceiling") => self.interms.push(ceiling(a1, &mut self.arena)),
|
||||
atom!("floor") => self.interms.push(floor(a1, &mut self.arena)),
|
||||
atom!("\\") => self.interms.push(
|
||||
atom!("truncate") => interms.push(truncate(a1, &mut self.arena)),
|
||||
atom!("round") => interms.push(drop_iter_on_err!(self, iter, round(a1, &mut self.arena))),
|
||||
atom!("ceiling") => interms.push(ceiling(a1, &mut self.arena)),
|
||||
atom!("floor") => interms.push(floor(a1, &mut self.arena)),
|
||||
atom!("\\") => interms.push(
|
||||
drop_iter_on_err!(self, iter, bitwise_complement(a1, &mut self.arena))
|
||||
),
|
||||
atom!("sign") => self.interms.push(a1.sign()),
|
||||
atom!("sign") => interms.push(a1.sign()),
|
||||
_ => {
|
||||
let evaluable_stub = functor_stub(name, 1);
|
||||
std::mem::drop(iter);
|
||||
@@ -1362,15 +1357,15 @@ impl MachineState {
|
||||
} else if arity == 0 {
|
||||
match name {
|
||||
atom!("pi") => {
|
||||
self.interms.push(Number::Float(OrderedFloat(f64::consts::PI)));
|
||||
interms.push(Number::Float(OrderedFloat(f64::consts::PI)));
|
||||
continue;
|
||||
}
|
||||
atom!("e") => {
|
||||
self.interms.push(Number::Float(OrderedFloat(f64::consts::E)));
|
||||
interms.push(Number::Float(OrderedFloat(f64::consts::E)));
|
||||
continue;
|
||||
}
|
||||
atom!("epsilon") => {
|
||||
self.interms.push(Number::Float(OrderedFloat(f64::EPSILON)));
|
||||
interms.push(Number::Float(OrderedFloat(f64::EPSILON)));
|
||||
continue;
|
||||
}
|
||||
_ => {
|
||||
@@ -1386,19 +1381,19 @@ impl MachineState {
|
||||
return Err(self.error_form(evaluable_error, stub));
|
||||
}
|
||||
(HeapCellValueTag::Fixnum, n) => {
|
||||
self.interms.push(Number::Fixnum(n));
|
||||
interms.push(Number::Fixnum(n));
|
||||
}
|
||||
(HeapCellValueTag::F64Offset, offset) => {
|
||||
let fl = self.arena.f64_tbl.get_entry(offset);
|
||||
self.interms.push(Number::Float(fl));
|
||||
interms.push(Number::Float(fl));
|
||||
}
|
||||
(HeapCellValueTag::Cons, ptr) => {
|
||||
match_untyped_arena_ptr!(ptr,
|
||||
(ArenaHeaderTag::Integer, n) => {
|
||||
self.interms.push(Number::Integer(n));
|
||||
interms.push(Number::Integer(n));
|
||||
}
|
||||
(ArenaHeaderTag::Rational, r) => {
|
||||
self.interms.push(Number::Rational(r));
|
||||
interms.push(Number::Rational(r));
|
||||
}
|
||||
_ => {
|
||||
std::mem::drop(iter);
|
||||
@@ -1429,7 +1424,7 @@ impl MachineState {
|
||||
)
|
||||
}
|
||||
|
||||
Ok(self.interms.pop().unwrap())
|
||||
Ok(interms.pop().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ pub(super) struct AttrVarInitializer {
|
||||
pub(super) bindings: Bindings,
|
||||
pub(super) p: usize,
|
||||
pub(super) cp: usize,
|
||||
// pub(super) instigating_p: usize,
|
||||
pub(super) verify_attrs_loc: usize,
|
||||
}
|
||||
|
||||
@@ -41,27 +40,29 @@ impl MachineState {
|
||||
pub(super) fn push_attr_var_binding(&mut self, h: usize, addr: HeapCellValue) {
|
||||
if self.attr_var_init.bindings.is_empty() {
|
||||
// save self.p and self.cp and ensure that the next
|
||||
// instruction is InstallVerifyAttrInterrupt.
|
||||
// instruction is RunVerifyAttrInterrupt.
|
||||
|
||||
self.attr_var_init.p = self.p;
|
||||
self.attr_var_init.cp = self.cp;
|
||||
|
||||
self.p = INSTALL_VERIFY_ATTR_INTERRUPT - 1;
|
||||
self.cp = INSTALL_VERIFY_ATTR_INTERRUPT;
|
||||
self.p = VERIFY_ATTR_INTERRUPT_LOC - 1;
|
||||
self.cp = VERIFY_ATTR_INTERRUPT_LOC;
|
||||
}
|
||||
|
||||
debug_assert_eq!(self.heap[h].get_tag(), HeapCellValueTag::AttrVar);
|
||||
self.attr_var_init.bindings.push((h, addr));
|
||||
}
|
||||
|
||||
fn populate_var_and_value_lists(&mut self) -> Result<(HeapCellValue, HeapCellValue), usize> {
|
||||
fn populate_var_and_value_lists(
|
||||
&mut self,
|
||||
) -> Result<(HeapCellValue, HeapCellValue), AllocError> {
|
||||
let size = self.attr_var_init.bindings.len();
|
||||
|
||||
let iter = self
|
||||
.attr_var_init
|
||||
.bindings
|
||||
.iter()
|
||||
.map(|(ref h, _)| attr_var_as_cell!(*h));
|
||||
.map(|(h, _)| attr_var_as_cell!(*h));
|
||||
|
||||
let var_list_addr = sized_iter_to_heap_list(&mut self.heap, size, iter)?;
|
||||
let iter = self.attr_var_init.bindings.drain(0..).map(|(_, ref v)| *v);
|
||||
@@ -70,7 +71,7 @@ impl MachineState {
|
||||
Ok((var_list_addr, value_list_addr))
|
||||
}
|
||||
|
||||
fn verify_attributes(&mut self) -> Result<(), usize> {
|
||||
fn verify_attributes(&mut self) -> Result<(), AllocError> {
|
||||
for (h, _) in &self.attr_var_init.bindings {
|
||||
self.heap[*h] = attr_var_as_cell!(*h);
|
||||
}
|
||||
@@ -102,21 +103,24 @@ impl MachineState {
|
||||
.collect()
|
||||
};
|
||||
|
||||
attr_vars.sort_unstable_by(|a1, a2| {
|
||||
compare_term_test!(self, *a1, *a2).unwrap_or(Ordering::Less)
|
||||
});
|
||||
attr_vars
|
||||
.sort_unstable_by(|a1, a2| self.compare_term_test(*a1, *a2).unwrap_or(Ordering::Less));
|
||||
|
||||
attr_vars.dedup();
|
||||
attr_vars
|
||||
}
|
||||
|
||||
pub(super) fn verify_attr_interrupt(&mut self, p: usize, arity: usize) -> Result<(), usize> {
|
||||
self.allocate(arity + 3);
|
||||
pub(super) fn verify_attr_interrupt(
|
||||
&mut self,
|
||||
p: usize,
|
||||
arity: usize,
|
||||
) -> Result<(), AllocError> {
|
||||
self.allocate(arity + 3)?;
|
||||
|
||||
let e = self.e;
|
||||
let and_frame = self.stack.index_and_frame_mut(e);
|
||||
|
||||
for i in 1..arity + 1 {
|
||||
for i in 1..=arity {
|
||||
and_frame[i] = self.registers[i];
|
||||
}
|
||||
|
||||
|
||||
@@ -80,19 +80,19 @@ fn derelictize_try_me_else(
|
||||
) -> Option<usize> {
|
||||
match &mut code[index] {
|
||||
Instruction::DynamicElse(_, _, NextOrFail::Next(0)) => None,
|
||||
Instruction::DynamicElse(_, _, NextOrFail::Next(ref mut o)) => {
|
||||
Instruction::DynamicElse(_, _, NextOrFail::Next(o)) => {
|
||||
retraction_info.push_record(RetractionRecord::ReplacedDynamicElseOffset(index, *o));
|
||||
Some(mem::replace(o, 0))
|
||||
}
|
||||
Instruction::DynamicInternalElse(_, _, NextOrFail::Next(0)) => None,
|
||||
Instruction::DynamicInternalElse(_, _, NextOrFail::Next(ref mut o)) => {
|
||||
Instruction::DynamicInternalElse(_, _, NextOrFail::Next(o)) => {
|
||||
retraction_info.push_record(RetractionRecord::ReplacedDynamicElseOffset(index, *o));
|
||||
Some(mem::replace(o, 0))
|
||||
}
|
||||
Instruction::DynamicElse(_, _, NextOrFail::Fail(_))
|
||||
| Instruction::DynamicInternalElse(_, _, NextOrFail::Fail(_)) => None,
|
||||
Instruction::TryMeElse(0) => None,
|
||||
Instruction::TryMeElse(ref mut o) => {
|
||||
Instruction::TryMeElse(o) => {
|
||||
retraction_info.push_record(RetractionRecord::ModifiedTryMeElse(index, *o));
|
||||
Some(mem::replace(o, 0))
|
||||
}
|
||||
@@ -275,7 +275,7 @@ fn merge_indexed_subsequences(
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
if let Instruction::TryMeElse(ref mut o) = &mut code[inner_try_me_else_loc] {
|
||||
if let Instruction::TryMeElse(o) = &mut code[inner_try_me_else_loc] {
|
||||
retraction_info.push_record(RetractionRecord::ModifiedTryMeElse(
|
||||
inner_try_me_else_loc,
|
||||
*o,
|
||||
@@ -324,7 +324,7 @@ fn merge_indexed_subsequences(
|
||||
);
|
||||
}
|
||||
None => {
|
||||
if let Instruction::TryMeElse(ref mut o) = &mut code[outer_threaded_choice_instr_loc] {
|
||||
if let Instruction::TryMeElse(o) = &mut code[outer_threaded_choice_instr_loc] {
|
||||
retraction_info
|
||||
.push_record(RetractionRecord::ModifiedTryMeElse(inner_trust_me_loc, *o));
|
||||
|
||||
@@ -472,7 +472,7 @@ fn set_switch_var_offset(
|
||||
let target_indexing_line = code[index_loc].to_indexing_line_mut().unwrap();
|
||||
|
||||
let old_v = match &mut target_indexing_line[0] {
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, ref mut v, ..)) => match *v {
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, v, ..)) => match *v {
|
||||
IndexingCodePtr::DynamicExternal(_) => {
|
||||
mem::replace(v, IndexingCodePtr::DynamicExternal(offset))
|
||||
}
|
||||
@@ -497,7 +497,7 @@ fn internalize_choice_instr_at(
|
||||
match &mut code[instr_loc] {
|
||||
Instruction::DynamicElse(_, _, NextOrFail::Fail(_))
|
||||
| Instruction::DynamicInternalElse(_, _, NextOrFail::Fail(_)) => {}
|
||||
Instruction::DynamicElse(_, _, ref mut o @ NextOrFail::Next(0)) => {
|
||||
Instruction::DynamicElse(_, _, o @ NextOrFail::Next(0)) => {
|
||||
retraction_info.push_record(RetractionRecord::ReplacedDynamicElseOffset(instr_loc, 0));
|
||||
*o = NextOrFail::Fail(0);
|
||||
}
|
||||
@@ -516,7 +516,7 @@ fn internalize_choice_instr_at(
|
||||
}
|
||||
}
|
||||
}
|
||||
Instruction::DynamicInternalElse(_, _, ref mut o @ NextOrFail::Next(0)) => {
|
||||
Instruction::DynamicInternalElse(_, _, o @ NextOrFail::Next(0)) => {
|
||||
retraction_info.push_record(RetractionRecord::ReplacedDynamicElseOffset(instr_loc, 0));
|
||||
*o = NextOrFail::Fail(0);
|
||||
}
|
||||
@@ -564,16 +564,14 @@ fn thread_choice_instr_at_to(
|
||||
) {
|
||||
loop {
|
||||
match &mut code[instr_loc] {
|
||||
Instruction::TryMeElse(ref mut o) | Instruction::RetryMeElse(ref mut o)
|
||||
if target_loc >= instr_loc =>
|
||||
{
|
||||
Instruction::TryMeElse(o) | Instruction::RetryMeElse(o) if target_loc >= instr_loc => {
|
||||
retraction_info.push_record(RetractionRecord::ReplacedChoiceOffset(instr_loc, *o));
|
||||
|
||||
*o = target_loc - instr_loc;
|
||||
return;
|
||||
}
|
||||
Instruction::DynamicElse(_, _, NextOrFail::Next(ref mut o))
|
||||
| Instruction::DynamicInternalElse(_, _, NextOrFail::Next(ref mut o))
|
||||
Instruction::DynamicElse(_, _, NextOrFail::Next(o))
|
||||
| Instruction::DynamicInternalElse(_, _, NextOrFail::Next(o))
|
||||
if target_loc >= instr_loc =>
|
||||
{
|
||||
retraction_info
|
||||
@@ -588,7 +586,7 @@ fn thread_choice_instr_at_to(
|
||||
Instruction::TryMeElse(o) | Instruction::RetryMeElse(o) => {
|
||||
instr_loc += *o;
|
||||
}
|
||||
Instruction::RevJmpBy(ref mut o) if instr_loc >= target_loc => {
|
||||
Instruction::RevJmpBy(o) if instr_loc >= target_loc => {
|
||||
retraction_info.push_record(RetractionRecord::ModifiedRevJmpBy(instr_loc, *o));
|
||||
|
||||
*o = instr_loc - target_loc;
|
||||
@@ -631,7 +629,7 @@ fn thread_choice_instr_at_to(
|
||||
Instruction::DynamicInternalElse(_, _, NextOrFail::Fail(o)) if *o > 0 => {
|
||||
instr_loc += *o;
|
||||
}
|
||||
Instruction::TrustMe(ref mut o) if target_loc >= instr_loc => {
|
||||
Instruction::TrustMe(o) if target_loc >= instr_loc => {
|
||||
retraction_info.push_record(
|
||||
RetractionRecord::AppendedTrustMe(instr_loc, *o, false),
|
||||
//choice_instr.is_default()),
|
||||
@@ -657,7 +655,7 @@ fn remove_non_leading_clause(
|
||||
retraction_info: &mut RetractionInfo,
|
||||
) -> Option<IndexPtr> {
|
||||
match &mut code[non_indexed_choice_instr_loc] {
|
||||
Instruction::RetryMeElse(ref mut o) => {
|
||||
Instruction::RetryMeElse(o) => {
|
||||
let o = *o;
|
||||
|
||||
thread_choice_instr_at_to(
|
||||
@@ -680,7 +678,7 @@ fn remove_non_leading_clause(
|
||||
|
||||
None
|
||||
}
|
||||
Instruction::TryMeElse(ref mut o) => {
|
||||
Instruction::TryMeElse(o) => {
|
||||
retraction_info.push_record(RetractionRecord::ModifiedTryMeElse(
|
||||
preceding_choice_instr_loc,
|
||||
*o,
|
||||
@@ -730,7 +728,7 @@ fn remove_leading_unindexed_clause(
|
||||
retraction_info: &mut RetractionInfo,
|
||||
) -> Option<IndexPtr> {
|
||||
match &mut code[non_indexed_choice_instr_loc] {
|
||||
Instruction::TryMeElse(ref mut o) => {
|
||||
Instruction::TryMeElse(o) => {
|
||||
if *o > 0 {
|
||||
retraction_info.push_record(RetractionRecord::ModifiedTryMeElse(
|
||||
non_indexed_choice_instr_loc,
|
||||
@@ -935,10 +933,10 @@ fn prepend_compiled_clause(
|
||||
let prepend_queue_len = prepend_queue.len();
|
||||
|
||||
match &mut prepend_queue[1] {
|
||||
Instruction::TryMeElse(ref mut o) if *o == 0 => {
|
||||
Instruction::TryMeElse(o) if *o == 0 => {
|
||||
*o = prepend_queue_len - 2;
|
||||
}
|
||||
Instruction::DynamicInternalElse(_, _, ref mut o @ NextOrFail::Next(0)) => {
|
||||
Instruction::DynamicInternalElse(_, _, o @ NextOrFail::Next(0)) => {
|
||||
*o = NextOrFail::Fail(prepend_queue_len - 2);
|
||||
}
|
||||
_ => {
|
||||
@@ -1632,8 +1630,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
};
|
||||
|
||||
match &mut self.wam_prelude.code[clause_loc] {
|
||||
Instruction::DynamicElse(_, ref mut d, _)
|
||||
| Instruction::DynamicInternalElse(_, ref mut d, _) => {
|
||||
Instruction::DynamicElse(_, d, _) | Instruction::DynamicInternalElse(_, d, _) => {
|
||||
*d = Death::Finite(LS::machine_st(&mut self.payload).global_clock);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
use std::borrow::Cow;
|
||||
use std::io::Write;
|
||||
use std::sync::mpsc::{channel, Receiver, Sender};
|
||||
use std::sync::mpsc::{Receiver, Sender, channel};
|
||||
|
||||
use rand::{rngs::StdRng, SeedableRng};
|
||||
use rand::{SeedableRng, rngs::StdRng};
|
||||
|
||||
use crate::Machine;
|
||||
|
||||
use super::{
|
||||
bootstrapping_compile, current_dir, import_builtin_impls, libraries, load_module, Arena, Atom,
|
||||
Callback, CompilationTarget, IndexStore, ListingSource, MachineArgs, MachineState, Stream,
|
||||
Arena, Atom, Callback, CompilationTarget, IndexStore, ListingSource, MachineArgs, MachineState,
|
||||
Stream, bootstrapping_compile, current_dir, import_builtin_impls, libraries, load_module,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
|
||||
@@ -7,14 +7,14 @@ use crate::machine::heap::*;
|
||||
use crate::machine::stack::*;
|
||||
use crate::types::*;
|
||||
|
||||
use scryer_modular_bitfield::specifiers::*;
|
||||
use scryer_modular_bitfield::*;
|
||||
use modular_bitfield::specifiers::*;
|
||||
use modular_bitfield::*;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::mem;
|
||||
use std::ops::{IndexMut, Range};
|
||||
|
||||
#[derive(BitfieldSpecifier, Copy, Clone, Debug)]
|
||||
#[derive(Specifier, Copy, Clone, Debug)]
|
||||
#[bits = 6]
|
||||
enum TrailRefTag {
|
||||
HeapCell = 0b001011,
|
||||
@@ -81,16 +81,16 @@ pub trait CopierTarget: IndexMut<usize, Output = HeapCellValue> {
|
||||
fn threshold(&self) -> usize;
|
||||
// returns the tail location of the pstr on success
|
||||
fn as_slice_from<'a>(&'a self, from: usize) -> Box<dyn Iterator<Item = u8> + 'a>;
|
||||
fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result<usize, usize>;
|
||||
fn reserve(&mut self, num_cells: usize) -> Result<HeapWriter<'_>, usize>;
|
||||
fn copy_slice_to_end(&mut self, bounds: Range<usize>) -> Result<(), usize>;
|
||||
fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result<usize, AllocError>;
|
||||
fn reserve(&mut self, num_cells: usize) -> Result<HeapWriter<'_>, AllocError>;
|
||||
fn copy_slice_to_end(&mut self, bounds: Range<usize>) -> Result<(), AllocError>;
|
||||
}
|
||||
|
||||
pub(crate) fn copy_term<T: CopierTarget>(
|
||||
target: T,
|
||||
addr: HeapCellValue,
|
||||
attr_var_policy: AttrVarPolicy,
|
||||
) -> Result<usize, usize> {
|
||||
) -> Result<usize, AllocError> {
|
||||
let mut copy_term_state = CopyTermState::new(target, attr_var_policy);
|
||||
let old_threshold = copy_term_state.target.threshold();
|
||||
|
||||
@@ -147,7 +147,7 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
self.trail.push((TrailRef::heap_cell(addr), trail_item));
|
||||
}
|
||||
|
||||
fn copy_list(&mut self, addr: usize) -> Result<(), usize> {
|
||||
fn copy_list(&mut self, addr: usize) -> Result<(), AllocError> {
|
||||
for offset in 0..2 {
|
||||
read_heap_cell!(self.target[addr + offset],
|
||||
(HeapCellValueTag::Lis, h) => {
|
||||
@@ -192,7 +192,7 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn copy_partial_string(&mut self, pstr_loc: usize) -> Result<(), usize> {
|
||||
fn copy_partial_string(&mut self, pstr_loc: usize) -> Result<(), AllocError> {
|
||||
match self.pstr_loc_locs.range_mut(..=pstr_loc).next_back() {
|
||||
Some((
|
||||
_prev_pstr_loc,
|
||||
@@ -247,11 +247,12 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
self.trail
|
||||
.push((TrailRef::pstr_loc(pstr_loc_idx), old_cell));
|
||||
|
||||
let old_tail_idx = if (pstr_loc + offset + 1) % Heap::heap_cell_alignment() == 0 {
|
||||
cell_index!(pstr_loc + offset) + 2
|
||||
} else {
|
||||
cell_index!(pstr_loc + offset) + 1
|
||||
};
|
||||
let old_tail_idx =
|
||||
if (pstr_loc + offset + 1).is_multiple_of(Heap::heap_cell_alignment()) {
|
||||
cell_index!(pstr_loc + offset) + 2
|
||||
} else {
|
||||
cell_index!(pstr_loc + offset) + 1
|
||||
};
|
||||
|
||||
let tail_cell = self.target[old_tail_idx];
|
||||
|
||||
@@ -279,7 +280,7 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn copy_attr_var_lists(&mut self) -> Result<(), usize> {
|
||||
fn copy_attr_var_lists(&mut self) -> Result<(), AllocError> {
|
||||
while !self.attr_var_list_locs.is_empty() {
|
||||
let mut list_loc_vec = std::mem::take(&mut self.attr_var_list_locs);
|
||||
|
||||
@@ -298,13 +299,13 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
* 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) -> Result<(), usize> {
|
||||
fn copy_attr_var_list(&mut self, mut list_addr: HeapCellValue) -> Result<(), AllocError> {
|
||||
while let HeapCellValueTag::Lis = list_addr.get_tag() {
|
||||
let threshold = self.target.threshold();
|
||||
let heap_loc = list_addr.get_value() as usize;
|
||||
let str_loc = self.target[heap_loc].get_value() as usize;
|
||||
let str_cell = self.target[str_loc];
|
||||
let mut writer = self.target.reserve(3).unwrap();
|
||||
let mut writer = self.target.reserve(3)?;
|
||||
|
||||
writer.write_with(|section| {
|
||||
section.push_cell(heap_loc_as_cell!(threshold + 2));
|
||||
@@ -328,7 +329,11 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reinstantiate_var(&mut self, addr: HeapCellValue, frontier: usize) -> Result<(), usize> {
|
||||
fn reinstantiate_var(
|
||||
&mut self,
|
||||
addr: HeapCellValue,
|
||||
frontier: usize,
|
||||
) -> Result<(), AllocError> {
|
||||
read_heap_cell!(addr,
|
||||
(HeapCellValueTag::Var, h) => {
|
||||
self.target[frontier] = heap_loc_as_cell!(frontier);
|
||||
@@ -379,8 +384,7 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn copy_var(&mut self, addr: HeapCellValue) -> Result<(), usize> {
|
||||
let index = addr.get_value() as usize;
|
||||
fn copy_var(&mut self, addr: HeapCellValue) -> Result<(), AllocError> {
|
||||
let rd = self.target.deref(addr);
|
||||
let ra = self.target.store(rd);
|
||||
|
||||
@@ -393,6 +397,7 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::Lis, h) => {
|
||||
let index = rd.get_value() as usize;
|
||||
if h >= self.old_h && self.target[index].get_mark_bit() {
|
||||
*self.value_at_scan() = heap_loc_as_cell!(
|
||||
if ra.get_forwarding_bit() {
|
||||
@@ -419,7 +424,7 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn copy_structure(&mut self, addr: usize) -> Result<(), usize> {
|
||||
fn copy_structure(&mut self, addr: usize) -> Result<(), AllocError> {
|
||||
read_heap_cell!(self.target[addr],
|
||||
(HeapCellValueTag::Atom, (_name, arity)) => {
|
||||
let threshold = self.target.threshold();
|
||||
@@ -429,7 +434,7 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
let str_cell = if get_structure_index(index_cell).is_some() {
|
||||
// copy the index pointer trailing this
|
||||
// inlined or expanded goal.
|
||||
let mut writer = self.target.reserve(1).unwrap();
|
||||
let mut writer = self.target.reserve(1)?;
|
||||
|
||||
writer.write_with(|section| {
|
||||
section.push_cell(index_cell);
|
||||
@@ -462,7 +467,7 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn copy_term_impl(&mut self, addr: HeapCellValue) -> Result<(), usize> {
|
||||
fn copy_term_impl(&mut self, addr: HeapCellValue) -> Result<(), AllocError> {
|
||||
self.scan = self.target.threshold();
|
||||
let mut writer = self.target.reserve(1)?;
|
||||
|
||||
@@ -501,7 +506,7 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn copy_pstrs(&mut self) -> Result<(), usize> {
|
||||
fn copy_pstrs(&mut self) -> Result<(), AllocError> {
|
||||
while let Some((least_pstr_loc, pstr_data)) = self.pstr_loc_locs.pop_first() {
|
||||
let threshold = heap_index!(self.target.threshold());
|
||||
|
||||
|
||||
@@ -6,78 +6,16 @@ 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::hash::Hash;
|
||||
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(1u64 << 63),
|
||||
delta: Rational::from(1),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<BranchNumber> 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<H: Hasher>(&self, hasher: &mut H) {
|
||||
self.branch_num.hash(hasher)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<BranchNumber> for BranchNumber {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, rhs: &BranchNumber) -> Option<Ordering> {
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct VarInfo {
|
||||
@@ -97,12 +35,12 @@ pub struct ChunkInfo {
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct BranchInfo {
|
||||
branch_num: BranchNumber,
|
||||
branch_num: Arc<BranchNumber>,
|
||||
chunks: Vec<ChunkInfo>,
|
||||
}
|
||||
|
||||
impl BranchInfo {
|
||||
fn new(branch_num: BranchNumber) -> Self {
|
||||
fn new(branch_num: Arc<BranchNumber>) -> Self {
|
||||
Self {
|
||||
branch_num,
|
||||
chunks: vec![],
|
||||
@@ -131,7 +69,7 @@ impl DerefMut for BranchMap {
|
||||
}
|
||||
}
|
||||
|
||||
type RootSet = IndexSet<BranchNumber>;
|
||||
type RootSet = IndexSet<Arc<BranchNumber>>;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct ClassifyInfo {
|
||||
@@ -140,12 +78,13 @@ pub struct ClassifyInfo {
|
||||
}
|
||||
|
||||
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.
|
||||
// pop the latest branch number from the root set and use it to 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),
|
||||
BuildFinalDisjunct(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 },
|
||||
@@ -154,15 +93,14 @@ enum TraversalState {
|
||||
Term(Term),
|
||||
OverrideGlobalCutVar(usize),
|
||||
ResetGlobalCutVarOverride(Option<usize>),
|
||||
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
|
||||
AddBranchNum(Arc<BranchNumber>), // set current_branch_num, add it to the root set
|
||||
RepBranchNum(Arc<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_branch_num: Arc<BranchNumber>,
|
||||
current_chunk_num: usize,
|
||||
current_chunk_type: ChunkType,
|
||||
branch_map: BranchMap,
|
||||
@@ -199,7 +137,7 @@ impl VarData {
|
||||
VarAlloc::Perm(0, PermVarAllocation::Pending);
|
||||
|
||||
match build_stack.front_mut() {
|
||||
Some(ChunkedTerms::Branch(_)) => {
|
||||
Some(ChunkedTerms::Branch { .. }) => {
|
||||
build_stack.push_front(ChunkedTerms::Chunk {
|
||||
terms: VecDeque::from(vec![term]),
|
||||
});
|
||||
@@ -219,24 +157,33 @@ pub type ClassifyFactResult = (Term, VarData);
|
||||
pub type ClassifyRuleResult = (Term, ChunkedTermVec, VarData);
|
||||
|
||||
fn merge_branch_seq(branches: impl Iterator<Item = BranchInfo>) -> BranchInfo {
|
||||
let mut branch_info = BranchInfo::new(BranchNumber::default());
|
||||
let mut branch_info = BranchInfo::new(Arc::new(BranchNumber::default()));
|
||||
|
||||
for mut branch in branches {
|
||||
branch_info.branch_num = branch.branch_num;
|
||||
branch_info.chunks.append(&mut branch.chunks);
|
||||
}
|
||||
|
||||
branch_info.branch_num.delta = branch_info.branch_num.delta * Integer::from(2);
|
||||
branch_info.branch_num.branch_num -= &branch_info.branch_num.delta;
|
||||
let new_delta = branch_info.branch_num.delta.clone() * Integer::from(2);
|
||||
|
||||
branch_info.branch_num = Arc::new(BranchNumber {
|
||||
branch_num: branch_info.branch_num.branch_num.clone() - &new_delta,
|
||||
delta: new_delta,
|
||||
});
|
||||
|
||||
branch_info
|
||||
}
|
||||
|
||||
fn flatten_into_disjunct(build_stack: &mut ChunkedTermVec, preceding_len: usize) {
|
||||
fn flatten_into_disjunct(
|
||||
build_stack: &mut ChunkedTermVec,
|
||||
branch_num: Arc<BranchNumber>,
|
||||
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);
|
||||
if let ChunkedTerms::Branch { branch_nums, arms } = &mut build_stack[preceding_len] {
|
||||
branch_nums.push(branch_num);
|
||||
arms.push(branch_vec);
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
@@ -246,7 +193,7 @@ impl VariableClassifier {
|
||||
pub fn new(call_policy: CallPolicy) -> Self {
|
||||
Self {
|
||||
call_policy,
|
||||
current_branch_num: BranchNumber::default(),
|
||||
current_branch_num: Arc::new(BranchNumber::default()),
|
||||
current_chunk_num: 0,
|
||||
current_chunk_type: ChunkType::Head,
|
||||
branch_map: BranchMap(BranchMapInt::new()),
|
||||
@@ -477,9 +424,6 @@ impl VariableClassifier {
|
||||
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());
|
||||
@@ -488,14 +432,10 @@ impl VariableClassifier {
|
||||
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);
|
||||
TraversalState::BuildDisjunct(preceding_len)
|
||||
| TraversalState::BuildFinalDisjunct(preceding_len) => {
|
||||
let branch_num = self.root_set.pop().unwrap();
|
||||
flatten_into_disjunct(&mut build_stack, branch_num, preceding_len);
|
||||
|
||||
self.current_chunk_type = ChunkType::Mid;
|
||||
self.current_chunk_num += 1;
|
||||
@@ -607,9 +547,9 @@ impl VariableClassifier {
|
||||
let tail = terms.pop().unwrap();
|
||||
let head = terms.pop().unwrap();
|
||||
|
||||
let first_branch_num = self.current_branch_num.split();
|
||||
let first_branch_num = Arc::new(self.current_branch_num.split());
|
||||
let branches: Vec<_> = std::iter::once(head)
|
||||
.chain(unfold_by_str(tail, atom!(";")).into_iter())
|
||||
.chain(unfold_by_str(tail, atom!(";")))
|
||||
.collect();
|
||||
|
||||
let mut branch_numbers = vec![first_branch_num];
|
||||
@@ -618,25 +558,24 @@ impl VariableClassifier {
|
||||
let succ_branch_number = branch_numbers[idx - 1].incr_by_delta();
|
||||
|
||||
branch_numbers.push(if idx + 1 < branches.len() {
|
||||
succ_branch_number.split()
|
||||
Arc::new(succ_branch_number.split())
|
||||
} else {
|
||||
succ_branch_number
|
||||
Arc::new(succ_branch_number)
|
||||
});
|
||||
}
|
||||
|
||||
let build_stack_len = build_stack.len();
|
||||
build_stack.reserve_branch(branches.len());
|
||||
|
||||
state_stack.push(TraversalState::RepBranchNum(
|
||||
state_stack.push(TraversalState::RepBranchNum(Arc::new(
|
||||
self.current_branch_num.halve_delta(),
|
||||
));
|
||||
)));
|
||||
|
||||
let iter = branches.into_iter().zip(branch_numbers.into_iter());
|
||||
let iter = branches.into_iter().zip(branch_numbers);
|
||||
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));
|
||||
}
|
||||
@@ -655,23 +594,18 @@ impl VariableClassifier {
|
||||
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.
|
||||
match state_stack.iter().rev().nth(1) {
|
||||
Some(&TraversalState::BuildDisjunct(preceding_len)) => {
|
||||
preceding_len + 1 == build_stack.len()
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let prev_b =
|
||||
if let Some(&TraversalState::BuildDisjunct(preceding_len)) =
|
||||
state_stack.last()
|
||||
{
|
||||
// check if the second-to-last element
|
||||
// is a regular BuildDisjunct, as we
|
||||
// don't want to add GetPrevLevel in
|
||||
// case of a TrustMe.
|
||||
preceding_len + 1 == build_stack.len()
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
state_stack.push(TraversalState::Term(then_term));
|
||||
state_stack.push(TraversalState::Cut {
|
||||
@@ -690,14 +624,21 @@ impl VariableClassifier {
|
||||
let not_term = terms.pop().unwrap();
|
||||
let build_stack_len = build_stack.len();
|
||||
|
||||
let first_branch_num = Arc::new(self.current_branch_num.split());
|
||||
let second_branch_num = Arc::new(first_branch_num.incr_by_delta());
|
||||
|
||||
build_stack.reserve_branch(2);
|
||||
|
||||
state_stack.push(TraversalState::RepBranchNum(Arc::new(
|
||||
self.current_branch_num.halve_delta(),
|
||||
)));
|
||||
state_stack.push(TraversalState::BuildFinalDisjunct(build_stack_len));
|
||||
state_stack.push(TraversalState::Term(Term::Clause(
|
||||
Cell::default(),
|
||||
atom!("$succeed"),
|
||||
vec![],
|
||||
)));
|
||||
state_stack.push(TraversalState::AddBranchNum(second_branch_num));
|
||||
state_stack.push(TraversalState::BuildDisjunct(build_stack_len));
|
||||
state_stack.push(TraversalState::Fail);
|
||||
state_stack.push(TraversalState::CutPrev(self.var_num));
|
||||
@@ -710,6 +651,7 @@ impl VariableClassifier {
|
||||
var_num: self.var_num,
|
||||
prev_b: false,
|
||||
});
|
||||
state_stack.push(TraversalState::AddBranchNum(first_branch_num));
|
||||
|
||||
self.current_chunk_type = ChunkType::Mid;
|
||||
self.current_chunk_num += 1;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -690,8 +690,7 @@ mod tests {
|
||||
|
||||
// term is: [a, <stream ptr>]
|
||||
let stream = Stream::from_static_string("test", &mut wam.machine_st.arena);
|
||||
let stream_cell =
|
||||
HeapCellValue::from(ConsPtr::build_with(stream.as_ptr(), ConsPtrMaskTag::Cons));
|
||||
let stream_cell = HeapCellValue::from_arena_header_ptr(stream.as_ptr());
|
||||
|
||||
let mut writer = wam.machine_st.heap.reserve(16).unwrap();
|
||||
|
||||
|
||||
@@ -5,16 +5,25 @@ use crate::types::*;
|
||||
|
||||
use std::alloc;
|
||||
use std::convert::TryFrom;
|
||||
use std::num::NonZero;
|
||||
use std::ops::{Bound, Index, IndexMut, Range, RangeBounds};
|
||||
use std::ptr;
|
||||
use std::sync::Once;
|
||||
|
||||
const ALIGN: usize = Heap::heap_cell_alignment();
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AllocError;
|
||||
|
||||
impl AllocError {
|
||||
pub(crate) fn resource_error_offset(&self, heap: &mut Heap) -> usize {
|
||||
heap.resource_error_offset()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Heap {
|
||||
inner: InnerHeap,
|
||||
resource_err_loc: usize,
|
||||
resource_err_loc: Option<NonZero<usize>>,
|
||||
}
|
||||
|
||||
impl Drop for Heap {
|
||||
@@ -63,12 +72,15 @@ impl InnerHeap {
|
||||
"Allocation too large. We should probably GC (TODO)"
|
||||
);
|
||||
|
||||
let new_ptr = if self.byte_cap == 0 {
|
||||
alloc::alloc(new_layout)
|
||||
} else {
|
||||
let old_layout =
|
||||
alloc::Layout::from_size_align(self.byte_cap, size_of::<HeapCellValue>()).unwrap();
|
||||
alloc::realloc(self.ptr, old_layout, new_layout.size())
|
||||
let new_ptr = unsafe {
|
||||
if self.byte_cap == 0 {
|
||||
alloc::alloc(new_layout)
|
||||
} else {
|
||||
let old_layout =
|
||||
alloc::Layout::from_size_align(self.byte_cap, size_of::<HeapCellValue>())
|
||||
.unwrap();
|
||||
alloc::realloc(self.ptr, old_layout, new_layout.size())
|
||||
}
|
||||
};
|
||||
|
||||
if !new_ptr.is_null() {
|
||||
@@ -85,8 +97,6 @@ impl InnerHeap {
|
||||
unsafe impl Send for Heap {}
|
||||
unsafe impl Sync for Heap {}
|
||||
|
||||
static RESOURCE_ERROR_OFFSET_INIT: Once = Once::new();
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct HeapStringScan<'a> {
|
||||
pub string: &'a str,
|
||||
@@ -99,7 +109,8 @@ unsafe fn scan_slice_to_str(heap_slice: &[u8]) -> HeapStringScan<'_> {
|
||||
.iter()
|
||||
.position(|b| *b == 0u8)
|
||||
.unwrap_or(heap_slice.len());
|
||||
let zero_byte_addr = heap_slice.as_ptr().add(string_len);
|
||||
|
||||
let zero_byte_addr = unsafe { heap_slice.as_ptr().add(string_len) };
|
||||
|
||||
let sentinel_len = pstr_sentinel_length(zero_byte_addr.addr());
|
||||
let tail_idx = cell_index!(
|
||||
@@ -110,7 +121,7 @@ unsafe fn scan_slice_to_str(heap_slice: &[u8]) -> HeapStringScan<'_> {
|
||||
let str_slice = &heap_slice[..string_len];
|
||||
|
||||
HeapStringScan {
|
||||
string: std::str::from_utf8_unchecked(str_slice),
|
||||
string: unsafe { std::str::from_utf8_unchecked(str_slice) },
|
||||
tail_idx,
|
||||
}
|
||||
}
|
||||
@@ -132,7 +143,7 @@ unsafe fn scan_slice_to_str_from_start(heap_slice: &[u8]) -> HeapStringScan<'_>
|
||||
let str_slice = &heap_slice[..string_len];
|
||||
|
||||
HeapStringScan {
|
||||
string: std::str::from_utf8_unchecked(str_slice),
|
||||
string: unsafe { std::str::from_utf8_unchecked(str_slice) },
|
||||
tail_idx,
|
||||
}
|
||||
}
|
||||
@@ -268,30 +279,32 @@ impl ReservedHeapSection {
|
||||
src.as_ptr(),
|
||||
self.heap_ptr.add(heap_index!(self.heap_cell_len)),
|
||||
str_byte_len,
|
||||
);
|
||||
)
|
||||
};
|
||||
|
||||
let zero_region_idx = heap_index!(self.heap_cell_len) + str_byte_len;
|
||||
let align_offset = pstr_sentinel_length(zero_region_idx);
|
||||
let zero_region_idx = heap_index!(self.heap_cell_len) + str_byte_len;
|
||||
let align_offset = pstr_sentinel_length(zero_region_idx);
|
||||
|
||||
ptr::write_bytes(self.heap_ptr.add(zero_region_idx), 0u8, align_offset);
|
||||
unsafe { ptr::write_bytes(self.heap_ptr.add(zero_region_idx), 0u8, align_offset) };
|
||||
|
||||
cells_written = if align_offset == 1 {
|
||||
cells_written = if align_offset == 1 {
|
||||
unsafe {
|
||||
ptr::write_bytes(
|
||||
self.heap_ptr.add(zero_region_idx + 1),
|
||||
0u8,
|
||||
size_of::<HeapCellValue>(),
|
||||
);
|
||||
|
||||
// ensure there are at least two bytes in the boundary
|
||||
// buffer separating the string data from the tail
|
||||
// cell
|
||||
cell_index!(src.len() + align_offset + size_of::<HeapCellValue>())
|
||||
} else {
|
||||
cell_index!(src.len() + align_offset)
|
||||
)
|
||||
};
|
||||
|
||||
self.heap_cell_len += cells_written;
|
||||
}
|
||||
// ensure there are at least two bytes in the boundary
|
||||
// buffer separating the string data from the tail
|
||||
// cell
|
||||
cell_index!(src.len() + align_offset + size_of::<HeapCellValue>())
|
||||
} else {
|
||||
cell_index!(src.len() + align_offset)
|
||||
};
|
||||
|
||||
self.heap_cell_len += cells_written;
|
||||
|
||||
cells_written
|
||||
}
|
||||
@@ -438,16 +451,12 @@ impl Index<usize> for ReservedHeapSection {
|
||||
|
||||
/// Computes the number of bytes required to pad a string of length `chunk_len`
|
||||
/// with zeroes, such that `chunk_len + pstr_sentinel_length(chunk_len)` is a
|
||||
/// multiple of `Heap::heap_cell_alignement()`.
|
||||
/// multiple of `Heap::heap_cell_alignment()`.
|
||||
fn pstr_sentinel_length(chunk_len: usize) -> usize {
|
||||
let res = chunk_len.next_multiple_of(ALIGN) - chunk_len;
|
||||
|
||||
// No bytes available in last chunk
|
||||
if res == 0 {
|
||||
ALIGN
|
||||
} else {
|
||||
res
|
||||
}
|
||||
if res == 0 { ALIGN } else { res }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
@@ -563,14 +572,14 @@ impl Heap {
|
||||
byte_len: 0,
|
||||
byte_cap: 0,
|
||||
},
|
||||
resource_err_loc: 0,
|
||||
resource_err_loc: None,
|
||||
}
|
||||
}
|
||||
|
||||
// takes a heap index, returns a cell index
|
||||
#[inline]
|
||||
pub const fn pstr_tail_idx(pstr_zero_byte_loc: usize) -> usize {
|
||||
if (pstr_zero_byte_loc + 1) % Heap::heap_cell_alignment() == 0 {
|
||||
if (pstr_zero_byte_loc + 1).is_multiple_of(Heap::heap_cell_alignment()) {
|
||||
cell_index!(pstr_zero_byte_loc) + 2
|
||||
} else {
|
||||
cell_index!(pstr_zero_byte_loc) + 1
|
||||
@@ -579,18 +588,20 @@ impl Heap {
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn grow(&mut self) -> bool {
|
||||
self.inner.grow()
|
||||
unsafe { self.inner.grow() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn resource_error_offset(&self) -> usize {
|
||||
self.resource_err_loc
|
||||
.expect("`error(resource_error(memory), [])` should be stored at the start of the heap")
|
||||
.get()
|
||||
}
|
||||
|
||||
pub(crate) fn with_cell_capacity(cap: usize) -> Result<Self, usize> {
|
||||
pub(crate) fn with_cell_capacity(cap: usize) -> Result<Self, AllocError> {
|
||||
let ptr = unsafe {
|
||||
let layout = alloc::Layout::from_size_align(
|
||||
cap * size_of::<HeapCellValue>(),
|
||||
heap_index_checked!(cap).ok_or(AllocError)?,
|
||||
size_of::<HeapCellValue>(),
|
||||
)
|
||||
.unwrap();
|
||||
@@ -598,7 +609,7 @@ impl Heap {
|
||||
};
|
||||
|
||||
if ptr.is_null() {
|
||||
panic!("could not allocate {} bytes for heap!", heap_index!(cap))
|
||||
Err(AllocError)
|
||||
} else {
|
||||
Ok(Self {
|
||||
inner: InnerHeap {
|
||||
@@ -607,26 +618,24 @@ impl Heap {
|
||||
byte_cap: heap_index!(cap),
|
||||
},
|
||||
// pstr_vec: bitvec![],
|
||||
resource_err_loc: 0,
|
||||
resource_err_loc: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reserve(&mut self, num_cells: usize) -> Result<HeapWriter<'_>, usize> {
|
||||
pub fn reserve(&mut self, num_cells: usize) -> Result<HeapWriter<'_>, AllocError> {
|
||||
let section;
|
||||
let len = heap_index!(num_cells);
|
||||
let len = heap_index_checked!(num_cells).ok_or(AllocError)?;
|
||||
|
||||
loop {
|
||||
unsafe {
|
||||
if self.free_space() >= len {
|
||||
section = ReservedHeapSection {
|
||||
heap_ptr: self.inner.ptr,
|
||||
heap_cell_len: self.cell_len(),
|
||||
};
|
||||
break;
|
||||
} else if !self.grow() {
|
||||
return Err(self.resource_error_offset());
|
||||
}
|
||||
if self.free_space() >= len {
|
||||
section = ReservedHeapSection {
|
||||
heap_ptr: self.inner.ptr,
|
||||
heap_cell_len: self.cell_len(),
|
||||
};
|
||||
break;
|
||||
} else if unsafe { !self.grow() } {
|
||||
return Err(AllocError);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -649,7 +658,7 @@ impl Heap {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn append(&mut self, other_heap: &impl SizedHeap) -> Result<(), usize> {
|
||||
pub(crate) fn append(&mut self, other_heap: &impl SizedHeap) -> Result<(), AllocError> {
|
||||
let other_len = heap_index!(other_heap.cell_len());
|
||||
|
||||
loop {
|
||||
@@ -665,7 +674,7 @@ impl Heap {
|
||||
self.inner.byte_len += heap_index!(other_heap.cell_len());
|
||||
break;
|
||||
} else if unsafe { !self.grow() } {
|
||||
return Err(self.resource_error_offset());
|
||||
return Err(AllocError);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -678,26 +687,26 @@ impl Heap {
|
||||
}
|
||||
|
||||
pub(crate) fn clear(&mut self) {
|
||||
unsafe {
|
||||
let layout =
|
||||
alloc::Layout::from_size_align(self.inner.byte_cap, size_of::<HeapCellValue>())
|
||||
.unwrap();
|
||||
alloc::dealloc(self.inner.ptr, layout);
|
||||
}
|
||||
|
||||
self.inner.ptr = ptr::null_mut();
|
||||
self.inner.byte_len = 0;
|
||||
self.inner.byte_cap = 0;
|
||||
*self = Heap::new();
|
||||
}
|
||||
|
||||
pub(crate) fn store_resource_error(&mut self) {
|
||||
RESOURCE_ERROR_OFFSET_INIT.call_once(move || {
|
||||
let stub = functor!(atom!("resource_error"), [atom_as_cell((atom!("memory")))]);
|
||||
self.resource_err_loc = cell_index!(self.inner.byte_len);
|
||||
if self.resource_err_loc.is_none() {
|
||||
let stub = functor!(
|
||||
atom!("error"),
|
||||
[
|
||||
functor((atom!("resource_error")), [atom_as_cell((atom!("memory")))]),
|
||||
atom_as_cell((atom!("[]")))
|
||||
]
|
||||
);
|
||||
|
||||
self.resource_err_loc = Some(NonZero::new(cell_index!(self.inner.byte_len)).expect(
|
||||
"index 0 should already be taken by an interstitial cell reserved by the runtime",
|
||||
));
|
||||
|
||||
let mut writer = Heap::functor_writer(stub);
|
||||
writer(self).unwrap();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -742,10 +751,10 @@ impl Heap {
|
||||
|
||||
// either succeed & return nothing or fail & return an offset into
|
||||
// the heap to a pre-allocated resource error
|
||||
pub(crate) fn push_cell(&mut self, cell: HeapCellValue) -> Result<(), usize> {
|
||||
pub(crate) fn push_cell(&mut self, cell: HeapCellValue) -> Result<(), AllocError> {
|
||||
unsafe {
|
||||
if self.inner.byte_len == self.inner.byte_cap && !self.grow() {
|
||||
return Err(self.resource_error_offset());
|
||||
return Err(AllocError);
|
||||
}
|
||||
|
||||
// SAFETY:
|
||||
@@ -779,7 +788,7 @@ impl Heap {
|
||||
Range { start, end }
|
||||
}
|
||||
|
||||
pub fn allocate_pstr(&mut self, src: &str) -> Result<HeapCellValue, usize> {
|
||||
pub fn allocate_pstr(&mut self, src: &str) -> Result<HeapCellValue, AllocError> {
|
||||
let size_in_heap = Self::compute_pstr_size(src);
|
||||
let mut writer = self.reserve(size_in_heap)?;
|
||||
let HeapSectionWriteResult { result, .. } =
|
||||
@@ -794,7 +803,7 @@ impl Heap {
|
||||
// note that allocate_cstr emits a tail cell to the string (completing it with the empty list)
|
||||
// unlike any version of allocate_pstr.
|
||||
|
||||
pub fn allocate_cstr(&mut self, src: &str) -> Result<HeapCellValue, usize> {
|
||||
pub fn allocate_cstr(&mut self, src: &str) -> Result<HeapCellValue, AllocError> {
|
||||
let size_in_heap = Self::compute_pstr_size(src);
|
||||
let mut writer = self.reserve(size_in_heap + 1)?;
|
||||
let HeapSectionWriteResult { result, .. } =
|
||||
@@ -849,15 +858,15 @@ impl Heap {
|
||||
|
||||
// copies only the string, not its tail. returns the cell index of
|
||||
// the tail location
|
||||
pub(crate) fn copy_pstr_within(&mut self, pstr_loc: usize) -> Result<usize, usize> {
|
||||
pub(crate) fn copy_pstr_within(&mut self, pstr_loc: usize) -> Result<usize, AllocError> {
|
||||
let HeapStringScan { string, tail_idx } = self.scan_slice_to_str(pstr_loc);
|
||||
let s_len = string.len();
|
||||
|
||||
let align_offset = pstr_sentinel_length(s_len);
|
||||
let copy_size = s_len + align_offset;
|
||||
|
||||
unsafe {
|
||||
loop {
|
||||
loop {
|
||||
unsafe {
|
||||
if self.free_space() >= copy_size {
|
||||
let slice =
|
||||
std::slice::from_raw_parts_mut(self.inner.ptr, self.inner.byte_len + s_len);
|
||||
@@ -884,7 +893,7 @@ impl Heap {
|
||||
|
||||
break;
|
||||
} else if !self.grow() {
|
||||
return Err(self.resource_error_offset());
|
||||
return Err(AllocError);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -893,7 +902,10 @@ impl Heap {
|
||||
}
|
||||
|
||||
// src is a cell-indexed range.
|
||||
pub(crate) fn copy_slice_to_end<R: RangeBounds<usize>>(&mut self, src: R) -> Result<(), usize> {
|
||||
pub(crate) fn copy_slice_to_end<R: RangeBounds<usize>>(
|
||||
&mut self,
|
||||
src: R,
|
||||
) -> Result<(), AllocError> {
|
||||
let range = self.slice_range(src);
|
||||
let len = range.end - range.start;
|
||||
|
||||
@@ -911,7 +923,7 @@ impl Heap {
|
||||
|
||||
break;
|
||||
} else if !self.grow() {
|
||||
return Err(self.resource_error_offset());
|
||||
return Err(AllocError);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -970,7 +982,7 @@ impl Heap {
|
||||
|
||||
pub(crate) fn functor_writer(
|
||||
functor: Vec<FunctorElement>,
|
||||
) -> impl FnMut(&mut Heap) -> Result<HeapCellValue, usize> {
|
||||
) -> impl FnMut(&mut Heap) -> Result<HeapCellValue, AllocError> {
|
||||
let size = Heap::compute_functor_byte_size(&functor);
|
||||
let mut functor_writer = ReservedHeapSection::functor_writer(functor);
|
||||
|
||||
@@ -1133,10 +1145,12 @@ pub fn sized_iter_to_heap_list<SrcT: Into<HeapCellValue>>(
|
||||
heap: &mut Heap,
|
||||
size: usize,
|
||||
values: impl Iterator<Item = SrcT>,
|
||||
) -> Result<HeapCellValue, usize> {
|
||||
) -> Result<HeapCellValue, AllocError> {
|
||||
if size > 0 {
|
||||
let h = heap.cell_len();
|
||||
let mut writer = heap.reserve(1 + 2 * size)?;
|
||||
// not using checked_add for 1 + as the result of multiplying by 2 will be even and the largest representable usize is odd,
|
||||
// so the addition cannot overflow
|
||||
let mut writer = heap.reserve(1 + size.checked_mul(2).ok_or(AllocError)?)?;
|
||||
|
||||
writer.write_with(|section| {
|
||||
for (idx, value) in values.enumerate() {
|
||||
|
||||
@@ -3,22 +3,23 @@ use std::collections::BTreeMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::atom_table;
|
||||
use crate::heap_iter::{stackful_post_order_iter, NonListElider};
|
||||
use crate::heap_iter::{NonListElider, stackful_post_order_iter};
|
||||
use crate::machine::heap::AllocError;
|
||||
use crate::machine::machine_indices::VarKey;
|
||||
use crate::machine::mock_wam::CompositeOpDir;
|
||||
use crate::machine::{
|
||||
ArenaHeaderTag, Fixnum, Number, BREAK_FROM_DISPATCH_LOOP_LOC, LIB_QUERY_SUCCESS,
|
||||
ArenaHeaderTag, BREAK_FROM_DISPATCH_LOOP_LOC, Fixnum, LIB_QUERY_SUCCESS, Number,
|
||||
};
|
||||
use crate::offset_table::*;
|
||||
use crate::parser::ast::{Var, VarPtr};
|
||||
use crate::parser::parser::{Parser, Tokens};
|
||||
use crate::read::{write_term_to_heap, TermWriteResult};
|
||||
use crate::read::{TermWriteResult, write_term_to_heap};
|
||||
use crate::types::UntypedArenaPtr;
|
||||
|
||||
use dashu::{Integer, Rational};
|
||||
use indexmap::IndexMap;
|
||||
|
||||
use super::{streams::Stream, Atom, AtomCell, HeapCellValue, HeapCellValueTag, Machine};
|
||||
use super::{Atom, AtomCell, HeapCellValue, HeapCellValueTag, Machine, streams::Stream};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
@@ -128,9 +129,11 @@ impl Term {
|
||||
pub fn try_conjunction(value: impl IntoIterator<Item = Term>) -> Option<Self> {
|
||||
let mut iter = value.into_iter();
|
||||
iter.next().map(|first| {
|
||||
Term::try_conjunction(iter)
|
||||
.map(|rest| Term::compound(",", [first.clone(), rest]))
|
||||
.unwrap_or(first)
|
||||
if let Some(rest) = Term::try_conjunction(iter) {
|
||||
Term::compound(",", [first, rest])
|
||||
} else {
|
||||
first
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -143,9 +146,11 @@ impl Term {
|
||||
pub fn try_disjunction(value: impl IntoIterator<Item = Term>) -> Option<Self> {
|
||||
let mut iter = value.into_iter();
|
||||
iter.next().map(|first| {
|
||||
Term::try_disjunction(iter)
|
||||
.map(|rest| Term::compound(";", [first.clone(), rest]))
|
||||
.unwrap_or(first)
|
||||
if let Some(rest) = Term::try_disjunction(iter) {
|
||||
Term::compound(";", [first, rest])
|
||||
} else {
|
||||
first
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -155,9 +160,14 @@ impl Term {
|
||||
fn count_to_letter_code(mut count: usize) -> String {
|
||||
let mut letters = Vec::new();
|
||||
|
||||
// +2 rather than +1 to account for the _ at the end
|
||||
let length = count.checked_ilog(26).unwrap_or(0) as usize + 2;
|
||||
|
||||
letters.reserve(length);
|
||||
|
||||
loop {
|
||||
let letter_idx = (count % 26) as u32;
|
||||
letters.push(char::from_u32('A' as u32 + letter_idx).unwrap());
|
||||
let letter_idx = (count % 26) as u8;
|
||||
letters.push(b'A' + letter_idx);
|
||||
count /= 26;
|
||||
|
||||
if count == 0 {
|
||||
@@ -165,7 +175,15 @@ fn count_to_letter_code(mut count: usize) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
letters.into_iter().chain("_".chars()).rev().collect()
|
||||
letters.push(b'_');
|
||||
|
||||
debug_assert_eq!(length, letters.len());
|
||||
|
||||
letters.reverse();
|
||||
|
||||
// Safety: we only push ascii chars A-Z and _
|
||||
// an ascii only byte sequence is always valid utf-8
|
||||
unsafe { String::from_utf8_unchecked(letters) }
|
||||
}
|
||||
|
||||
impl Term {
|
||||
@@ -207,14 +225,14 @@ impl Term {
|
||||
|
||||
let list = match tail {
|
||||
Term::Atom(atom) if atom == "[]" => match head {
|
||||
Term::Atom(ref a) if a.chars().collect::<Vec<_>>().len() == 1 => {
|
||||
Term::Atom(ref a) if a.chars().count() == 1 => {
|
||||
// Handle lists of char as strings
|
||||
Term::String(a.to_string())
|
||||
}
|
||||
_ => Term::List(vec![head]),
|
||||
},
|
||||
Term::List(elems) if elems.is_empty() => match head {
|
||||
Term::Atom(ref a) if a.chars().collect::<Vec<_>>().len() == 1 => {
|
||||
Term::Atom(ref a) if a.chars().count() == 1 => {
|
||||
// Handle lists of char as strings
|
||||
Term::String(a.to_string())
|
||||
},
|
||||
@@ -225,7 +243,7 @@ impl Term {
|
||||
Term::List(elems)
|
||||
},
|
||||
Term::String(mut elems) => match head {
|
||||
Term::Atom(ref a) if a.chars().collect::<Vec<_>>().len() == 1 => {
|
||||
Term::Atom(ref a) if a.chars().count() == 1 => {
|
||||
// Handle lists of char as strings
|
||||
elems.insert(0, a.chars().next().unwrap());
|
||||
Term::String(elems)
|
||||
@@ -397,7 +415,7 @@ pub struct QueryState<'a> {
|
||||
|
||||
impl Drop for QueryState<'_> {
|
||||
fn drop(&mut self) {
|
||||
// FIXME: This may be wrong if the iterator is not fully consumend, but from testing it
|
||||
// FIXME: This may be wrong if the iterator is not fully consumed, but from testing it
|
||||
// seems fine. Is this really ok?
|
||||
self.machine.trust_me();
|
||||
}
|
||||
@@ -428,14 +446,15 @@ impl Iterator for QueryState<'_> {
|
||||
// contained in self.machine_st.ball.
|
||||
let h = machine.machine_st.heap.cell_len();
|
||||
|
||||
if let Err(resource_err_loc) = machine
|
||||
if let Err(err) = machine
|
||||
.machine_st
|
||||
.heap
|
||||
.append(&machine.machine_st.ball.stub)
|
||||
{
|
||||
let resource_error_offset = err.resource_error_offset(&mut machine.machine_st.heap);
|
||||
return Some(Err(Term::from_heapcell(
|
||||
machine,
|
||||
machine.machine_st.heap[resource_err_loc],
|
||||
machine.machine_st.heap[resource_error_offset],
|
||||
&mut IndexMap::new(),
|
||||
)));
|
||||
}
|
||||
@@ -536,11 +555,11 @@ impl Machine {
|
||||
self.run_module_predicate(atom!("loader"), (atom!("consult_stream"), 2));
|
||||
}
|
||||
|
||||
pub(crate) fn allocate_stub_choice_point(&mut self) {
|
||||
pub(crate) fn allocate_stub_choice_point(&mut self) -> Result<(), AllocError> {
|
||||
// NOTE: create a choice point to terminate the dispatch_loop
|
||||
// if an exception is thrown.
|
||||
|
||||
let stub_b = self.machine_st.stack.allocate_or_frame(0);
|
||||
let stub_b = self.machine_st.stack.allocate_or_frame(0)?;
|
||||
let or_frame = self.machine_st.stack.index_or_frame_mut(stub_b);
|
||||
|
||||
or_frame.prelude.num_cells = 0;
|
||||
@@ -558,6 +577,8 @@ impl Machine {
|
||||
self.machine_st.b = stub_b;
|
||||
self.machine_st.hb = self.machine_st.heap.cell_len();
|
||||
self.machine_st.block = stub_b;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Runs a query.
|
||||
@@ -571,7 +592,8 @@ impl Machine {
|
||||
.read_term(&op_dir, Tokens::Default)
|
||||
.expect("Failed to parse query");
|
||||
|
||||
self.allocate_stub_choice_point();
|
||||
self.allocate_stub_choice_point()
|
||||
.expect("failed to allocate stub choice point");
|
||||
|
||||
// Write parsed term to heap
|
||||
let term_write_result = write_term_to_heap(&term, &mut self.machine_st.heap)
|
||||
@@ -619,3 +641,11 @@ impl Machine {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_count_to_letter_code() {
|
||||
for idx in 0..1000 {
|
||||
// ensure the debug assert doesn't trigger
|
||||
count_to_letter_code(idx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::MachineBuilder;
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||
fn programatic_query() {
|
||||
fn programmatic_query() {
|
||||
let mut machine = MachineBuilder::default().build();
|
||||
|
||||
machine.load_module_string(
|
||||
|
||||
@@ -34,7 +34,7 @@ pub(super) fn set_code_index<'a, LS: LoadState<'a>>(
|
||||
RetractionRecord::ReplacedUserPredicate(key, replaced)
|
||||
}
|
||||
}
|
||||
CompilationTarget::Module(ref module_name) => {
|
||||
CompilationTarget::Module(module_name) => {
|
||||
if IndexPtrTag::Undefined == code_idx_ptr.tag() {
|
||||
*code_idx_ptr = code_ptr;
|
||||
RetractionRecord::AddedModulePredicate(*module_name, key)
|
||||
@@ -98,7 +98,7 @@ pub(super) fn add_op_decl(
|
||||
CompilationTarget::User => {
|
||||
retraction_info.push_record(RetractionRecord::ReplacedUserOp(*op_decl, op_desc));
|
||||
}
|
||||
CompilationTarget::Module(ref module_name) => {
|
||||
CompilationTarget::Module(module_name) => {
|
||||
retraction_info.push_record(RetractionRecord::ReplacedModuleOp(
|
||||
*module_name,
|
||||
*op_decl,
|
||||
@@ -110,7 +110,7 @@ pub(super) fn add_op_decl(
|
||||
CompilationTarget::User => {
|
||||
retraction_info.push_record(RetractionRecord::AddedUserOp(*op_decl));
|
||||
}
|
||||
CompilationTarget::Module(ref module_name) => {
|
||||
CompilationTarget::Module(module_name) => {
|
||||
retraction_info
|
||||
.push_record(RetractionRecord::AddedModuleOp(*module_name, *op_decl));
|
||||
}
|
||||
@@ -167,7 +167,7 @@ pub(super) fn import_module_exports<'a, LS: LoadState<'a>>(
|
||||
));
|
||||
}
|
||||
}
|
||||
ModuleExport::OpDecl(ref op_decl) => {
|
||||
ModuleExport::OpDecl(op_decl) => {
|
||||
add_op_decl(
|
||||
&mut payload.retraction_info,
|
||||
compilation_target,
|
||||
@@ -221,7 +221,7 @@ fn import_module_exports_into_module<'a, LS: LoadState<'a>>(
|
||||
));
|
||||
}
|
||||
}
|
||||
ModuleExport::OpDecl(ref op_decl) => {
|
||||
ModuleExport::OpDecl(op_decl) => {
|
||||
add_op_decl_as_module_export::<LS>(payload, op_dir, wam_op_dir, op_decl);
|
||||
}
|
||||
}
|
||||
@@ -276,7 +276,7 @@ fn import_qualified_module_exports<'a, LS: LoadState<'a>>(
|
||||
));
|
||||
}
|
||||
}
|
||||
ModuleExport::OpDecl(ref op_decl) => {
|
||||
ModuleExport::OpDecl(op_decl) => {
|
||||
add_op_decl(
|
||||
&mut payload.retraction_info,
|
||||
compilation_target,
|
||||
@@ -336,7 +336,7 @@ fn import_qualified_module_exports_into_module<'a, LS: LoadState<'a>>(
|
||||
));
|
||||
}
|
||||
}
|
||||
ModuleExport::OpDecl(ref op_decl) => {
|
||||
ModuleExport::OpDecl(op_decl) => {
|
||||
add_op_decl_as_module_export::<LS>(payload, op_dir, wam_op_dir, op_decl);
|
||||
}
|
||||
}
|
||||
@@ -545,7 +545,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
) {
|
||||
for export in removed_module.module_decl.exports.iter() {
|
||||
match export {
|
||||
ModuleExport::PredicateKey(ref key) => {
|
||||
ModuleExport::PredicateKey(key) => {
|
||||
if let (Some(module_code_idx), Some(target_code_idx)) = (
|
||||
removed_module.code_dir.get(key).cloned(),
|
||||
code_dir.get_mut(key).cloned(),
|
||||
@@ -1050,7 +1050,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
);
|
||||
|
||||
for export in &module.module_decl.exports {
|
||||
if let ModuleExport::OpDecl(ref op_decl) = export {
|
||||
if let ModuleExport::OpDecl(op_decl) = export {
|
||||
add_op_decl_as_module_export::<LS>(
|
||||
&mut self.payload,
|
||||
&mut module.op_dir,
|
||||
@@ -1084,7 +1084,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
&mut self.wam_prelude.indices.meta_predicates,
|
||||
)?;
|
||||
}
|
||||
CompilationTarget::Module(ref defining_module_name) => {
|
||||
CompilationTarget::Module(defining_module_name) => {
|
||||
match self
|
||||
.wam_prelude
|
||||
.indices
|
||||
@@ -1137,7 +1137,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
&exports,
|
||||
&mut self.wam_prelude,
|
||||
),
|
||||
CompilationTarget::Module(ref defining_module_name) => {
|
||||
CompilationTarget::Module(defining_module_name) => {
|
||||
match self
|
||||
.wam_prelude
|
||||
.indices
|
||||
|
||||
@@ -160,7 +160,7 @@ impl fmt::Display for CompilationTarget {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
CompilationTarget::User => write!(f, "user"),
|
||||
CompilationTarget::Module(ref module_name) => write!(f, "{}", module_name.as_str()),
|
||||
CompilationTarget::Module(module_name) => write!(f, "{}", module_name.as_str()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -819,11 +819,8 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
if let Instruction::IndexingCode(ref mut indexing_code) =
|
||||
self.wam_prelude.code[index_loc]
|
||||
{
|
||||
if let IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(
|
||||
_,
|
||||
ref mut v,
|
||||
..,
|
||||
)) = &mut indexing_code[0]
|
||||
if let IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, v, ..)) =
|
||||
&mut indexing_code[0]
|
||||
{
|
||||
*v = old_v;
|
||||
}
|
||||
@@ -1110,7 +1107,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
}
|
||||
}
|
||||
}
|
||||
CompilationTarget::Module(ref module_name) => {
|
||||
CompilationTarget::Module(module_name) => {
|
||||
match self.wam_prelude.indices.modules.get_mut(module_name) {
|
||||
Some(ref mut module) => match module.extensible_predicates.get_mut(&key) {
|
||||
Some(ref mut skeleton) => {
|
||||
@@ -1364,14 +1361,12 @@ impl<'a> MachinePreludeView<'a> {
|
||||
) -> CompositeOpDir<'_, '_> {
|
||||
match compilation_target {
|
||||
CompilationTarget::User => CompositeOpDir::new(&self.indices.op_dir, None),
|
||||
CompilationTarget::Module(ref module_name) => {
|
||||
match self.indices.modules.get(module_name) {
|
||||
Some(module) => CompositeOpDir::new(&self.indices.op_dir, Some(&module.op_dir)),
|
||||
None => {
|
||||
unreachable!()
|
||||
}
|
||||
CompilationTarget::Module(module_name) => match self.indices.modules.get(module_name) {
|
||||
Some(module) => CompositeOpDir::new(&self.indices.op_dir, Some(&module.op_dir)),
|
||||
None => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1504,9 +1499,10 @@ impl Machine {
|
||||
}
|
||||
|
||||
pub(crate) fn load_compiled_library(&mut self) -> CallResult {
|
||||
let library = cell_as_atom!(self
|
||||
.machine_st
|
||||
.store(self.machine_st.deref(self.machine_st.registers[1])));
|
||||
let library = cell_as_atom!(
|
||||
self.machine_st
|
||||
.store(self.machine_st.deref(self.machine_st.registers[1]))
|
||||
);
|
||||
|
||||
if let Some(module) = self.indices.modules.get(&library) {
|
||||
if let ListingSource::DynamicallyGenerated = module.listing_src {
|
||||
@@ -1537,9 +1533,10 @@ impl Machine {
|
||||
}
|
||||
|
||||
pub(crate) fn declare_module(&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.machine_st
|
||||
.store(self.machine_st.deref(self.machine_st.registers[1]))
|
||||
);
|
||||
|
||||
let mut loader = self.loader_from_heap_evacuable(temp_v!(3));
|
||||
|
||||
@@ -1649,9 +1646,10 @@ impl Machine {
|
||||
}
|
||||
|
||||
pub(crate) fn add_goal_expansion_clause(&mut self) -> CallResult {
|
||||
let target_module_name = cell_as_atom!(self
|
||||
.machine_st
|
||||
.store(self.machine_st.deref(self.machine_st.registers[1])));
|
||||
let target_module_name = cell_as_atom!(
|
||||
self.machine_st
|
||||
.store(self.machine_st.deref(self.machine_st.registers[1]))
|
||||
);
|
||||
|
||||
let mut loader = self.loader_from_heap_evacuable(temp_v!(3));
|
||||
|
||||
@@ -1744,9 +1742,10 @@ impl Machine {
|
||||
&mut self,
|
||||
r: RegType,
|
||||
) -> Loader<'_, LiveLoadAndMachineState<'_>> {
|
||||
let mut load_state = cell_as_load_state_payload!(self
|
||||
.machine_st
|
||||
.store(self.machine_st.deref(self.machine_st[r])));
|
||||
let mut load_state = cell_as_load_state_payload!(
|
||||
self.machine_st
|
||||
.store(self.machine_st.deref(self.machine_st[r]))
|
||||
);
|
||||
|
||||
load_state.set_tag(ArenaHeaderTag::LiveLoadState);
|
||||
|
||||
@@ -1842,9 +1841,7 @@ impl Machine {
|
||||
let err = self.machine_st.permission_error(
|
||||
Permission::Modify,
|
||||
atom!("static_procedure"),
|
||||
functor_stub(atom!(":"), 2)
|
||||
.into_iter()
|
||||
.collect::<MachineStub>(),
|
||||
functor_stub(atom!(":"), 2),
|
||||
);
|
||||
|
||||
self.machine_st
|
||||
@@ -2100,9 +2097,10 @@ impl Machine {
|
||||
}
|
||||
|
||||
pub(crate) fn abolish_clause(&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.machine_st
|
||||
.store(self.machine_st.deref(self.machine_st.registers[1]))
|
||||
);
|
||||
|
||||
let key = self
|
||||
.machine_st
|
||||
@@ -2216,9 +2214,10 @@ impl Machine {
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let module_name = cell_as_atom!(self
|
||||
.machine_st
|
||||
.store(self.machine_st.deref(self.machine_st.registers[4])));
|
||||
let module_name = cell_as_atom!(
|
||||
self.machine_st
|
||||
.store(self.machine_st.deref(self.machine_st.registers[4]))
|
||||
);
|
||||
|
||||
let compilation_target = match module_name {
|
||||
atom!("user") => CompilationTarget::User,
|
||||
@@ -2271,9 +2270,10 @@ impl Machine {
|
||||
}
|
||||
|
||||
pub(crate) fn is_consistent_with_term_queue(&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.machine_st
|
||||
.store(self.machine_st.deref(self.machine_st.registers[1]))
|
||||
);
|
||||
|
||||
let key = self
|
||||
.machine_st
|
||||
@@ -2311,9 +2311,10 @@ impl Machine {
|
||||
}
|
||||
|
||||
pub(crate) fn remove_module_exports(&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.machine_st
|
||||
.store(self.machine_st.deref(self.machine_st.registers[1]))
|
||||
);
|
||||
|
||||
let mut loader = self.loader_from_heap_evacuable(temp_v!(2));
|
||||
|
||||
@@ -2339,9 +2340,10 @@ impl Machine {
|
||||
}
|
||||
|
||||
pub(crate) fn meta_predicate_property(&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.machine_st
|
||||
.store(self.machine_st.deref(self.machine_st.registers[1]))
|
||||
);
|
||||
|
||||
let (predicate_name, arity) = self
|
||||
.machine_st
|
||||
@@ -2361,8 +2363,8 @@ impl Machine {
|
||||
|
||||
let mut writer = match self.machine_st.heap.reserve(3 + meta_specs.len()) {
|
||||
Ok(writer) => writer,
|
||||
Err(err_loc) => {
|
||||
self.machine_st.throw_resource_error(err_loc);
|
||||
Err(err) => {
|
||||
self.machine_st.throw_resource_error(err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
@@ -2376,7 +2378,7 @@ impl Machine {
|
||||
MetaSpec::Plus => atom_as_cell!(atom!("-")),
|
||||
MetaSpec::Either => atom_as_cell!(atom!("?")),
|
||||
MetaSpec::Colon => atom_as_cell!(atom!(":")),
|
||||
MetaSpec::RequiresExpansionWithArgument(ref arg_num) => {
|
||||
MetaSpec::RequiresExpansionWithArgument(arg_num) => {
|
||||
fixnum_as_cell!(/* FIXME this is not safe */ unsafe {
|
||||
Fixnum::build_with_unchecked(*arg_num as i64)
|
||||
})
|
||||
@@ -2403,9 +2405,10 @@ impl Machine {
|
||||
}
|
||||
|
||||
pub(crate) fn dynamic_property(&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.machine_st
|
||||
.store(self.machine_st.deref(self.machine_st.registers[1]))
|
||||
);
|
||||
|
||||
let key = self
|
||||
.machine_st
|
||||
@@ -2430,9 +2433,10 @@ impl Machine {
|
||||
}
|
||||
|
||||
pub(crate) fn multifile_property(&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.machine_st
|
||||
.store(self.machine_st.deref(self.machine_st.registers[1]))
|
||||
);
|
||||
|
||||
let key = self
|
||||
.machine_st
|
||||
@@ -2457,9 +2461,10 @@ impl Machine {
|
||||
}
|
||||
|
||||
pub(crate) fn discontiguous_property(&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.machine_st
|
||||
.store(self.machine_st.deref(self.machine_st.registers[1]))
|
||||
);
|
||||
|
||||
let key = self
|
||||
.machine_st
|
||||
|
||||
@@ -6,6 +6,7 @@ use crate::parser::ast::*;
|
||||
use crate::ffi::{self, FfiError};
|
||||
use crate::forms::*;
|
||||
use crate::functor_macro::*;
|
||||
use crate::machine::heap::AllocError;
|
||||
use crate::machine::heap::*;
|
||||
use crate::machine::loader::CompilationTarget;
|
||||
use crate::machine::machine_state::*;
|
||||
@@ -19,7 +20,7 @@ pub type MachineStubGen = Box<dyn Fn(&mut MachineState) -> MachineStub>;
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct MachineError {
|
||||
stub: MachineStub,
|
||||
location: Option<(usize, usize)>, // line_num, col_num
|
||||
location: Option<Location>,
|
||||
}
|
||||
|
||||
// from 7.12.2 b) of 13211-1:1995
|
||||
@@ -75,7 +76,6 @@ impl ValidType {
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub(crate) enum ResourceError {
|
||||
FiniteMemory(HeapCellValue),
|
||||
OutOfFiles,
|
||||
}
|
||||
|
||||
@@ -329,12 +329,6 @@ impl MachineState {
|
||||
|
||||
pub(super) fn resource_error(err: ResourceError) -> MachineError {
|
||||
let stub = match err {
|
||||
ResourceError::FiniteMemory(size_requested) => {
|
||||
functor!(
|
||||
atom!("resource_error"),
|
||||
[atom_as_cell((atom!("finite_memory"))), cell(size_requested)]
|
||||
)
|
||||
}
|
||||
ResourceError::OutOfFiles => {
|
||||
functor!(
|
||||
atom!("resource_error"),
|
||||
@@ -447,10 +441,26 @@ impl MachineState {
|
||||
location: None,
|
||||
}
|
||||
}
|
||||
ExistenceError::FfiFunction(atom) => {
|
||||
ExistenceError::FfiFunction(name, arity) => {
|
||||
let culprit = functor!(atom!("/"), [atom_as_cell(name), fixnum(arity)]);
|
||||
let stub = functor!(
|
||||
atom!("existence_error"),
|
||||
[atom_as_cell((atom!("ffi_function"))), atom_as_cell(atom)]
|
||||
[atom_as_cell((atom!("ffi_function"))), functor(culprit)]
|
||||
);
|
||||
|
||||
MachineError {
|
||||
stub,
|
||||
location: None,
|
||||
}
|
||||
}
|
||||
ExistenceError::FfiStructConstructor(name, arity) => {
|
||||
let culprit = functor!(atom!("/"), [atom_as_cell(name), fixnum(arity)]);
|
||||
let stub = functor!(
|
||||
atom!("existence_error"),
|
||||
[
|
||||
atom_as_cell((atom!("ffi_struct_constructor"))),
|
||||
functor(culprit)
|
||||
]
|
||||
);
|
||||
|
||||
MachineError {
|
||||
@@ -629,6 +639,19 @@ impl MachineState {
|
||||
return self.directive_error(err);
|
||||
}
|
||||
|
||||
if let CompilationError::FiniteMemoryInHeap(err) = err {
|
||||
// err.resource_error_offset() should be the address of the error/2 functor in the pre-allocated term error(resource_error(memory), [])
|
||||
let err_loc = err.resource_error_offset(&mut self.heap);
|
||||
let stub = vec![FunctorElement::AbsoluteCell(
|
||||
// err_loc + 1 should be the functors first argument which should be a str cell pointing at the resource_error/1 functor
|
||||
self.heap[err_loc + 1],
|
||||
)];
|
||||
return MachineError {
|
||||
stub,
|
||||
location: None,
|
||||
};
|
||||
}
|
||||
|
||||
let location = err.line_and_col_num();
|
||||
let stub = err.as_functor();
|
||||
|
||||
@@ -688,13 +711,25 @@ impl MachineState {
|
||||
}
|
||||
}
|
||||
FfiError::ValueOutOfRange(domain, culprit) => self.domain_error(domain, culprit),
|
||||
FfiError::FunctionNotFound(name) => {
|
||||
self.existence_error(ExistenceError::FfiFunction(name))
|
||||
FfiError::FunctionNotFound(name, arity) => {
|
||||
self.existence_error(ExistenceError::FfiFunction(name, arity))
|
||||
}
|
||||
FfiError::StructNotFound(name) => {
|
||||
self.existence_error(ExistenceError::FfiStructType(name))
|
||||
}
|
||||
FfiError::ArgCountMismatch => self.unreachable_error(),
|
||||
FfiError::ArgCountMismatch {
|
||||
name,
|
||||
kind,
|
||||
expected: _,
|
||||
got,
|
||||
} => match kind {
|
||||
ffi::ArgCountMismatchKind::Function => {
|
||||
self.existence_error(ExistenceError::FfiFunction(name, got))
|
||||
}
|
||||
ffi::ArgCountMismatchKind::Struct => {
|
||||
self.existence_error(ExistenceError::FfiStructConstructor(name, got))
|
||||
}
|
||||
},
|
||||
FfiError::AllocationFailed => MachineError {
|
||||
stub: functor!(atom!("resource_error"), [atom_as_cell((atom!("heap")))]),
|
||||
location: None,
|
||||
@@ -702,10 +737,13 @@ impl MachineState {
|
||||
FfiError::LayoutError => self.representation_error(RepFlag::FfiLayout),
|
||||
FfiError::UnsupportedTypedef => self.representation_error(RepFlag::FfiLayout),
|
||||
FfiError::UnsupportedAbi => self.representation_error(RepFlag::FfiAbi),
|
||||
FfiError::VoidArgumentType => self.domain_error(
|
||||
FfiError::UnsupportedArgumentType(None) => self.domain_error(
|
||||
DomainErrorType::FfiArgumentType,
|
||||
atom_as_cell!(atom!("void")),
|
||||
atom_as_cell!(atom!("unknown")),
|
||||
),
|
||||
FfiError::UnsupportedArgumentType(Some(kind)) => {
|
||||
self.domain_error(DomainErrorType::FfiArgumentType, atom_as_cell!(kind))
|
||||
}
|
||||
FfiError::CStrFieldType => self.domain_error(
|
||||
DomainErrorType::NonCStrFfiArgumentType,
|
||||
atom_as_cell!(atom!("cstr")),
|
||||
@@ -714,18 +752,20 @@ impl MachineState {
|
||||
DomainErrorType::NonNullPtr,
|
||||
fixnum_as_cell!(Fixnum::build_with(0)),
|
||||
),
|
||||
FfiError::Other => self.unreachable_error(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn error_form(&mut self, err: MachineError, src: MachineStub) -> MachineStub {
|
||||
if let Some((line_num, _col_num)) = err.location {
|
||||
if let Some(location) = err.location {
|
||||
let line = location.line();
|
||||
functor!(
|
||||
atom!("error"),
|
||||
[
|
||||
functor((err.stub)),
|
||||
functor(
|
||||
(atom!(":")),
|
||||
[functor(src), number(line_num, (&mut self.arena))]
|
||||
[functor(src), number(line, (&mut self.arena))]
|
||||
)
|
||||
]
|
||||
)
|
||||
@@ -735,10 +775,19 @@ impl MachineState {
|
||||
}
|
||||
|
||||
// throw an error pre-allocated in the heap
|
||||
pub(super) fn throw_resource_error(&mut self, err_loc: usize) {
|
||||
self.registers[1] = str_loc_as_cell!(err_loc);
|
||||
pub(super) fn throw_resource_error(&mut self, err: AllocError) {
|
||||
if self.throwing_resource_error {
|
||||
panic!(
|
||||
"attempted to throw `error(resource_error(memory), [])` while attempting to throw `error(resource_error(memory), [])`"
|
||||
);
|
||||
}
|
||||
self.throwing_resource_error = true;
|
||||
|
||||
self.registers[1] = str_loc_as_cell!(err.resource_error_offset(&mut self.heap));
|
||||
self.set_ball();
|
||||
self.unwind_stack();
|
||||
|
||||
self.throwing_resource_error = false;
|
||||
}
|
||||
|
||||
pub(super) fn throw_exception(&mut self, err: MachineStub) {
|
||||
@@ -749,8 +798,8 @@ impl MachineState {
|
||||
|
||||
self.registers[1] = match writer(&mut self.heap) {
|
||||
Ok(loc) => loc,
|
||||
Err(resource_err_loc) => {
|
||||
self.throw_resource_error(resource_err_loc);
|
||||
Err(err) => {
|
||||
self.throw_resource_error(err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
@@ -774,7 +823,13 @@ pub enum CompilationError {
|
||||
InvalidRuleHead,
|
||||
InvalidUseModuleDecl,
|
||||
InvalidModuleResolution(Atom),
|
||||
FiniteMemoryInHeap(usize),
|
||||
FiniteMemoryInHeap(AllocError),
|
||||
}
|
||||
|
||||
impl From<AllocError> for CompilationError {
|
||||
fn from(value: AllocError) -> Self {
|
||||
Self::FiniteMemoryInHeap(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -805,9 +860,9 @@ impl From<ParserError> for CompilationError {
|
||||
}
|
||||
|
||||
impl CompilationError {
|
||||
pub(crate) fn line_and_col_num(&self) -> Option<(usize, usize)> {
|
||||
pub(crate) fn line_and_col_num(&self) -> Option<Location> {
|
||||
match self {
|
||||
CompilationError::ParserError(err) => err.line_and_col_num(),
|
||||
CompilationError::ParserError(err) => err.location(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -847,11 +902,11 @@ impl CompilationError {
|
||||
CompilationError::InvalidUseModuleDecl => {
|
||||
functor!(atom!("invalid_use_module_declaration"))
|
||||
}
|
||||
CompilationError::ParserError(ref err) => {
|
||||
CompilationError::ParserError(err) => {
|
||||
functor!(err.as_atom())
|
||||
}
|
||||
CompilationError::FiniteMemoryInHeap(h) => {
|
||||
vec![FunctorElement::AbsoluteCell(str_loc_as_cell!(*h))]
|
||||
CompilationError::FiniteMemoryInHeap(_) => {
|
||||
functor!(atom!("resource_error"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -884,6 +939,7 @@ impl Permission {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[cfg_attr(not(feature = "ffi"), expect(dead_code))]
|
||||
pub(crate) enum DomainErrorType {
|
||||
IOMode,
|
||||
NotLessThanZero,
|
||||
@@ -934,6 +990,7 @@ impl DomainErrorType {
|
||||
|
||||
// from 7.12.2 f) of 13211-1:1995
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[cfg_attr(not(feature = "ffi"), expect(dead_code))]
|
||||
pub(crate) enum RepFlag {
|
||||
Character,
|
||||
CharacterCode,
|
||||
@@ -1125,6 +1182,7 @@ impl MachineState {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(not(feature = "ffi"), expect(dead_code))]
|
||||
pub enum ExistenceError {
|
||||
Module(Atom),
|
||||
ModuleSource(ModuleSource),
|
||||
@@ -1137,7 +1195,8 @@ pub enum ExistenceError {
|
||||
SourceSink(HeapCellValue),
|
||||
Stream(HeapCellValue),
|
||||
Process(HeapCellValue),
|
||||
FfiFunction(Atom),
|
||||
FfiFunction(Atom, usize),
|
||||
FfiStructConstructor(Atom, usize),
|
||||
FfiStructType(Atom),
|
||||
}
|
||||
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
#![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work
|
||||
|
||||
use crate::parser::ast::*;
|
||||
|
||||
use crate::atom_table::*;
|
||||
use crate::forms::*;
|
||||
use crate::machine::ClauseType;
|
||||
use crate::machine::MachineStubGen;
|
||||
use crate::machine::loader::*;
|
||||
use crate::machine::machine_state::*;
|
||||
use crate::machine::streams::{Stream, StreamOptions};
|
||||
use crate::machine::ClauseType;
|
||||
use crate::machine::MachineStubGen;
|
||||
use crate::offset_table::*;
|
||||
|
||||
use fxhash::FxBuildHasher;
|
||||
use indexmap::{IndexMap, IndexSet};
|
||||
use scryer_modular_bitfield::specifiers::*;
|
||||
use scryer_modular_bitfield::{bitfield, BitfieldSpecifier};
|
||||
use modular_bitfield::specifiers::*;
|
||||
use modular_bitfield::{Specifier, bitfield};
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::BTreeSet;
|
||||
@@ -65,7 +63,7 @@ impl PartialOrd<Ref> for HeapCellValue {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(BitfieldSpecifier, Copy, Clone, Debug, PartialEq)]
|
||||
#[derive(Specifier, Copy, Clone, Debug, PartialEq)]
|
||||
#[bits = 7]
|
||||
pub enum IndexPtrTag {
|
||||
DynamicUndefined = 0b1000101, // a predicate, declared as dynamic, whose location in code is as yet undefined.
|
||||
@@ -294,7 +292,7 @@ impl IndexStore {
|
||||
) -> Option<&mut PredicateSkeleton> {
|
||||
match compilation_target {
|
||||
CompilationTarget::User => self.extensible_predicates.get_mut(key),
|
||||
CompilationTarget::Module(ref module_name) => {
|
||||
CompilationTarget::Module(module_name) => {
|
||||
if let Some(module) = self.modules.get_mut(module_name) {
|
||||
module.extensible_predicates.get_mut(key)
|
||||
} else {
|
||||
@@ -311,7 +309,7 @@ impl IndexStore {
|
||||
) -> Option<&PredicateSkeleton> {
|
||||
match compilation_target {
|
||||
CompilationTarget::User => self.extensible_predicates.get(key),
|
||||
CompilationTarget::Module(ref module_name) => {
|
||||
CompilationTarget::Module(module_name) => {
|
||||
if let Some(module) = self.modules.get(module_name) {
|
||||
module.extensible_predicates.get(key)
|
||||
} else {
|
||||
@@ -382,7 +380,7 @@ impl IndexStore {
|
||||
) -> Option<PredicateSkeleton> {
|
||||
match compilation_target {
|
||||
CompilationTarget::User => self.extensible_predicates.swap_remove(key),
|
||||
CompilationTarget::Module(ref module_name) => {
|
||||
CompilationTarget::Module(module_name) => {
|
||||
if let Some(module) = self.modules.get_mut(module_name) {
|
||||
module.extensible_predicates.swap_remove(key)
|
||||
} else {
|
||||
@@ -415,7 +413,7 @@ impl IndexStore {
|
||||
) -> Option<&Vec<MetaSpec>> {
|
||||
match compilation_target {
|
||||
CompilationTarget::User => self.meta_predicates.get(&(name, arity)),
|
||||
CompilationTarget::Module(ref module_name) => match self.modules.get(module_name) {
|
||||
CompilationTarget::Module(module_name) => match self.modules.get(module_name) {
|
||||
Some(module) => module
|
||||
.meta_predicates
|
||||
.get(&(name, arity))
|
||||
|
||||
@@ -3,21 +3,22 @@ use crate::atom_table::*;
|
||||
use crate::forms::*;
|
||||
use crate::heap_iter::*;
|
||||
use crate::heap_print::*;
|
||||
use crate::machine::Machine;
|
||||
use crate::machine::attributed_variables::*;
|
||||
use crate::machine::copier::*;
|
||||
use crate::machine::heap::AllocError;
|
||||
use crate::machine::heap::*;
|
||||
use crate::machine::machine_errors::*;
|
||||
use crate::machine::machine_indices::*;
|
||||
use crate::machine::stack::*;
|
||||
use crate::machine::streams::*;
|
||||
use crate::machine::Machine;
|
||||
use crate::parser::ast::*;
|
||||
use crate::read::TermWriteResult;
|
||||
use crate::types::*;
|
||||
|
||||
use crate::parser::dashu::Integer;
|
||||
|
||||
use fxhash::FxBuildHasher;
|
||||
use indexmap::IndexMap;
|
||||
use indexmap::IndexSet;
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
@@ -56,11 +57,68 @@ pub enum OnEOF {
|
||||
Return,
|
||||
Continue,
|
||||
}
|
||||
pub(crate) trait OccursCheckImpl {
|
||||
fn flag_value(&self) -> Atom;
|
||||
fn unify(&self, state: &mut MachineState);
|
||||
fn bind(&self, state: &mut MachineState, r: Ref, h: HeapCellValue);
|
||||
}
|
||||
|
||||
/// Not subject to occurs-check
|
||||
pub(crate) struct Nsto;
|
||||
|
||||
impl OccursCheckImpl for Nsto {
|
||||
fn flag_value(&self) -> Atom {
|
||||
atom!("false")
|
||||
}
|
||||
|
||||
fn unify(&self, state: &mut MachineState) {
|
||||
state.unify();
|
||||
}
|
||||
|
||||
fn bind(&self, state: &mut MachineState, r: Ref, h: HeapCellValue) {
|
||||
state.bind(r, h);
|
||||
}
|
||||
}
|
||||
|
||||
/// Subject to occurs-check
|
||||
pub(crate) struct Sto;
|
||||
|
||||
impl OccursCheckImpl for Sto {
|
||||
fn flag_value(&self) -> Atom {
|
||||
atom!("true")
|
||||
}
|
||||
|
||||
fn unify(&self, state: &mut MachineState) {
|
||||
state.unify_with_occurs_check();
|
||||
}
|
||||
|
||||
fn bind(&self, state: &mut MachineState, r: Ref, h: HeapCellValue) {
|
||||
state.bind_with_occurs_check_wrapper(r, h);
|
||||
}
|
||||
}
|
||||
|
||||
/// Subject to occurs-check -> error
|
||||
pub(crate) struct StoError;
|
||||
|
||||
impl OccursCheckImpl for StoError {
|
||||
fn flag_value(&self) -> Atom {
|
||||
atom!("error")
|
||||
}
|
||||
|
||||
fn unify(&self, state: &mut MachineState) {
|
||||
state.unify_with_occurs_check_with_error();
|
||||
}
|
||||
|
||||
fn bind(&self, state: &mut MachineState, r: Ref, h: HeapCellValue) {
|
||||
state.bind_with_occurs_check_with_error_wrapper(r, h);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MachineState {
|
||||
pub atom_tbl: Arc<AtomTable>,
|
||||
pub arena: Arena,
|
||||
pub(super) pdl: Vec<HeapCellValue>,
|
||||
pub(super) pdl: Vec<(HeapCellValue, HeapCellValue)>,
|
||||
pub(super) unify_tabu_list: IndexSet<(HeapCellValue, HeapCellValue), FxBuildHasher>,
|
||||
pub(super) s: HeapPtr,
|
||||
pub(super) s_offset: usize,
|
||||
pub(super) p: usize,
|
||||
@@ -73,6 +131,7 @@ pub struct MachineState {
|
||||
pub(super) cp: usize,
|
||||
pub(super) attr_var_init: AttrVarInitializer,
|
||||
pub(super) fail: bool,
|
||||
pub throwing_resource_error: bool,
|
||||
pub heap: Heap,
|
||||
pub(super) mode: MachineMode,
|
||||
pub(crate) stack: Stack,
|
||||
@@ -85,7 +144,6 @@ pub struct MachineState {
|
||||
pub(super) ball: Ball,
|
||||
pub(super) ball_stack: Vec<Ball>, // save current ball before jumping via, e.g., verify_attr interrupt.
|
||||
pub(super) lifted_heap: Heap,
|
||||
pub(super) interms: Vec<Number>, // intermediate numbers.
|
||||
// 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,
|
||||
@@ -93,8 +151,7 @@ pub struct MachineState {
|
||||
pub(crate) cc: usize,
|
||||
pub(crate) global_clock: usize,
|
||||
pub(crate) dynamic_mode: FirstOrNext,
|
||||
pub(crate) unify_fn: fn(&mut MachineState),
|
||||
pub(crate) bind_fn: fn(&mut MachineState, Ref, HeapCellValue),
|
||||
pub(crate) occurs_check: &'static dyn OccursCheckImpl,
|
||||
pub(crate) run_cleaners_fn: fn(&mut Machine) -> bool,
|
||||
}
|
||||
|
||||
@@ -124,33 +181,12 @@ impl fmt::Debug for MachineState {
|
||||
.field("ball", &self.ball)
|
||||
.field("ball_stack", &self.ball_stack)
|
||||
.field("lifted_heap", &self.lifted_heap)
|
||||
.field("interms", &self.interms)
|
||||
.field("flags", &self.flags)
|
||||
.field("cc", &self.cc)
|
||||
.field("global_clock", &self.global_clock)
|
||||
.field("dynamic_mode", &self.dynamic_mode)
|
||||
.field(
|
||||
"unify_fn",
|
||||
if self.unify_fn as usize == MachineState::unify as usize {
|
||||
&"MachineState::unify"
|
||||
} else if self.unify_fn as usize == MachineState::unify_with_occurs_check as usize {
|
||||
&"MachineState::unify_with_occurs_check"
|
||||
} else {
|
||||
&"MachineState::unify_with_occurs_check_with_error"
|
||||
},
|
||||
)
|
||||
.field(
|
||||
"bind_fn",
|
||||
if self.bind_fn as usize == MachineState::bind as usize {
|
||||
&"MachineState::bind"
|
||||
} else if self.bind_fn as usize
|
||||
== MachineState::bind_with_occurs_check_wrapper as usize
|
||||
{
|
||||
&"MachineState::bind_with_occurs_check"
|
||||
} else {
|
||||
&"MachineState::bind_with_occurs_check_with_error_wrapper"
|
||||
},
|
||||
)
|
||||
.field("unify_fn", &&*self.occurs_check.flag_value().as_str())
|
||||
.field("bind_fn", &&*self.occurs_check.flag_value().as_str())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -192,7 +228,7 @@ fn push_var_eq_functors<'a>(
|
||||
size: usize,
|
||||
iter: impl Iterator<Item = (&'a VarKey, &'a HeapCellValue)>,
|
||||
atom_tbl: &AtomTable,
|
||||
) -> Result<HeapCellValue, usize> {
|
||||
) -> Result<HeapCellValue, AllocError> {
|
||||
let src_h = heap.cell_len();
|
||||
|
||||
let true_size = if size > 0 {
|
||||
@@ -257,7 +293,7 @@ impl Ball {
|
||||
self.stub.clear();
|
||||
}
|
||||
|
||||
pub(super) fn copy_and_align_to(&self, dest: &mut Heap) -> Result<usize, usize> {
|
||||
pub(super) fn copy_and_align_to(&self, dest: &mut Heap) -> Result<usize, AllocError> {
|
||||
let h = dest.cell_len();
|
||||
let diff = self.boundary as i64 - h as i64;
|
||||
|
||||
@@ -346,17 +382,17 @@ impl<'a> CopierTarget for CopyTerm<'a> {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result<usize, usize> {
|
||||
fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result<usize, AllocError> {
|
||||
self.state.heap.copy_pstr_within(pstr_loc)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn reserve(&mut self, num_cells: usize) -> Result<HeapWriter<'_>, usize> {
|
||||
fn reserve(&mut self, num_cells: usize) -> Result<HeapWriter<'_>, AllocError> {
|
||||
self.state.heap.reserve(num_cells)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn copy_slice_to_end(&mut self, bounds: Range<usize>) -> Result<(), usize> {
|
||||
fn copy_slice_to_end(&mut self, bounds: Range<usize>) -> Result<(), AllocError> {
|
||||
self.state.heap.copy_slice_to_end(bounds)
|
||||
}
|
||||
}
|
||||
@@ -455,7 +491,7 @@ impl<'a> CopierTarget for CopyBallTerm<'a> {
|
||||
self.stack
|
||||
}
|
||||
|
||||
fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result<usize, usize> {
|
||||
fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result<usize, AllocError> {
|
||||
debug_assert!(pstr_loc < self.heap.byte_len());
|
||||
|
||||
let HeapStringScan { string, tail_idx } = self.heap.scan_slice_to_str(pstr_loc);
|
||||
@@ -477,11 +513,11 @@ impl<'a> CopierTarget for CopyBallTerm<'a> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reserve(&mut self, num_cells: usize) -> Result<HeapWriter<'_>, usize> {
|
||||
fn reserve(&mut self, num_cells: usize) -> Result<HeapWriter<'_>, AllocError> {
|
||||
self.stub.reserve(num_cells)
|
||||
}
|
||||
|
||||
fn copy_slice_to_end(&mut self, bounds: Range<usize>) -> Result<(), usize> {
|
||||
fn copy_slice_to_end(&mut self, bounds: Range<usize>) -> Result<(), AllocError> {
|
||||
let len = bounds.end - bounds.start;
|
||||
let mut stub_writer = self.stub.reserve(len)?;
|
||||
|
||||
@@ -515,7 +551,7 @@ impl MachineState {
|
||||
return true;
|
||||
}
|
||||
|
||||
self.cwil.global_count += 1;
|
||||
self.cwil.global_count = self.cwil.global_count.strict_add(1);
|
||||
|
||||
if let Some(&(ref limit, block)) = self.cwil.limits.last() {
|
||||
if self.cwil.local_count == *limit {
|
||||
@@ -617,7 +653,7 @@ impl MachineState {
|
||||
mut var_list: Vec<(VarKey, HeapCellValue, usize)>,
|
||||
singleton_heap_list: HeapCellValue,
|
||||
) -> CallResult {
|
||||
var_list.sort_by(|(_, _, idx_1), (_, _, idx_2)| idx_1.cmp(idx_2));
|
||||
var_list.sort_by_key(|(_, _, idx_1)| *idx_1);
|
||||
|
||||
let singleton_addr = self.registers[3];
|
||||
unify_fn!(*self, singleton_heap_list, singleton_addr);
|
||||
@@ -1077,47 +1113,47 @@ impl MachineState {
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct CWIL {
|
||||
local_count: Integer,
|
||||
pub(crate) global_count: Integer,
|
||||
limits: Vec<(Integer, usize)>,
|
||||
local_count: u128,
|
||||
pub(crate) global_count: u128,
|
||||
limits: Vec<(u128, usize)>,
|
||||
pub(crate) inference_limit_exceeded: bool,
|
||||
}
|
||||
|
||||
impl CWIL {
|
||||
pub(crate) fn new() -> Self {
|
||||
CWIL {
|
||||
local_count: Integer::from(0),
|
||||
global_count: Integer::from(0),
|
||||
local_count: 0,
|
||||
global_count: 0,
|
||||
limits: vec![],
|
||||
inference_limit_exceeded: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_limit(&mut self, mut limit: Integer, block: usize) -> &Integer {
|
||||
limit += &self.local_count;
|
||||
pub(crate) fn add_limit(&mut self, mut limit: u128, block: usize) -> u128 {
|
||||
limit = limit.strict_add(self.local_count);
|
||||
|
||||
match self.limits.last() {
|
||||
Some((ref inner_limit, _)) if *inner_limit <= limit => {}
|
||||
Some((inner_limit, _)) if *inner_limit <= limit => {}
|
||||
_ => self.limits.push((limit, block)),
|
||||
}
|
||||
|
||||
&self.local_count
|
||||
self.local_count
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn remove_limit(&mut self, block: usize) -> &Integer {
|
||||
pub(crate) fn remove_limit(&mut self, block: usize) -> u128 {
|
||||
if let Some((_, bl)) = self.limits.last() {
|
||||
if bl == &block {
|
||||
self.limits.pop();
|
||||
}
|
||||
}
|
||||
|
||||
&self.local_count
|
||||
self.local_count
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn reset(&mut self) {
|
||||
self.local_count = Integer::from(0);
|
||||
self.local_count = 0;
|
||||
self.limits.clear();
|
||||
self.inference_limit_exceeded = false;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
use fxhash::FxBuildHasher;
|
||||
use indexmap::IndexSet;
|
||||
|
||||
use crate::arena::*;
|
||||
use crate::atom_table::*;
|
||||
use crate::forms::*;
|
||||
use crate::heap_iter::*;
|
||||
use crate::machine::attributed_variables::*;
|
||||
use crate::machine::copier::*;
|
||||
use crate::machine::heap::AllocError;
|
||||
use crate::machine::heap::*;
|
||||
use crate::machine::machine_errors::*;
|
||||
use crate::machine::machine_indices::*;
|
||||
use crate::machine::machine_state::*;
|
||||
use crate::machine::partial_string::*;
|
||||
use crate::machine::stack::*;
|
||||
@@ -16,8 +19,6 @@ use crate::parser::ast::*;
|
||||
use crate::parser::dashu::{Integer, Rational};
|
||||
use crate::types::*;
|
||||
|
||||
use indexmap::IndexSet;
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
@@ -30,9 +31,10 @@ impl MachineState {
|
||||
heap.store_resource_error();
|
||||
|
||||
MachineState {
|
||||
arena: Arena::new(),
|
||||
atom_tbl: AtomTable::new(),
|
||||
arena: Arena::new().unwrap(),
|
||||
atom_tbl: AtomTable::new().unwrap(),
|
||||
pdl: Vec::with_capacity(1024),
|
||||
unify_tabu_list: IndexSet::with_hasher(FxBuildHasher::default()),
|
||||
s: HeapPtr::default(),
|
||||
s_offset: 0,
|
||||
p: 0,
|
||||
@@ -47,7 +49,7 @@ impl MachineState {
|
||||
fail: false,
|
||||
heap,
|
||||
mode: MachineMode::Write,
|
||||
stack: Stack::new(),
|
||||
stack: Stack::new().unwrap(),
|
||||
registers: [heap_loc_as_cell!(0); MAX_ARITY + 1], // self.registers[0] is never used.
|
||||
trail: vec![],
|
||||
tr: 0,
|
||||
@@ -57,16 +59,15 @@ impl MachineState {
|
||||
ball: Ball::new(),
|
||||
ball_stack: vec![],
|
||||
lifted_heap: Heap::new(),
|
||||
interms: vec![Number::default(); 256],
|
||||
cont_pts: Vec::with_capacity(256),
|
||||
cwil: CWIL::new(),
|
||||
flags: MachineFlags::default(),
|
||||
cc: 0,
|
||||
global_clock: 0,
|
||||
dynamic_mode: FirstOrNext::First,
|
||||
unify_fn: MachineState::unify,
|
||||
bind_fn: MachineState::bind,
|
||||
occurs_check: &Nsto,
|
||||
run_cleaners_fn: |_| false,
|
||||
throwing_resource_error: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,8 +175,8 @@ impl MachineState {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn allocate(&mut self, num_cells: usize) {
|
||||
let e = self.stack.allocate_and_frame(num_cells);
|
||||
pub fn allocate(&mut self, num_cells: usize) -> Result<(), AllocError> {
|
||||
let e = self.stack.allocate_and_frame(num_cells)?;
|
||||
let and_frame = self.stack.index_and_frame_mut(e);
|
||||
|
||||
and_frame.prelude.e = self.e;
|
||||
@@ -183,6 +184,8 @@ impl MachineState {
|
||||
|
||||
self.e = e;
|
||||
self.p += 1;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn bind(&mut self, r1: Ref, a2: HeapCellValue) {
|
||||
@@ -377,347 +380,6 @@ impl MachineState {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compare_term_test(&mut self, var_comparison: VarComparison) -> Option<Ordering> {
|
||||
let mut tabu_list = IndexSet::new();
|
||||
|
||||
while let Some(s1) = self.pdl.pop() {
|
||||
let s1 = self.deref(s1);
|
||||
|
||||
let s2 = self.pdl.pop().unwrap();
|
||||
let s2 = self.deref(s2);
|
||||
|
||||
if s1 == s2 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let v1 = self.store(s1);
|
||||
let v2 = self.store(s2);
|
||||
|
||||
let order_cat_v1 = v1.order_category(&self.heap);
|
||||
let order_cat_v2 = v2.order_category(&self.heap);
|
||||
|
||||
if order_cat_v1 != order_cat_v2 {
|
||||
self.pdl.clear();
|
||||
return Some(order_cat_v1.cmp(&order_cat_v2));
|
||||
}
|
||||
|
||||
match order_cat_v1 {
|
||||
Some(TermOrderCategory::Variable) => {
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(TermOrderCategory::FloatingPoint) => {
|
||||
let v1 = cell_as_f64_offset!(v1);
|
||||
let v2 = cell_as_f64_offset!(v2);
|
||||
|
||||
let v1 = self.arena.f64_tbl.get_entry(v1);
|
||||
let v2 = self.arena.f64_tbl.get_entry(v2);
|
||||
|
||||
if v1 != v2 {
|
||||
self.pdl.clear();
|
||||
return Some(v1.cmp(&v2));
|
||||
}
|
||||
}
|
||||
Some(TermOrderCategory::Integer) => {
|
||||
let v1 = Number::try_from((v1, &self.arena.f64_tbl)).unwrap();
|
||||
let v2 = Number::try_from((v2, &self.arena.f64_tbl)).unwrap();
|
||||
|
||||
if v1 != v2 {
|
||||
self.pdl.clear();
|
||||
return Some(v1.cmp(&v2));
|
||||
}
|
||||
}
|
||||
Some(TermOrderCategory::Atom) => {
|
||||
read_heap_cell!(v1,
|
||||
(HeapCellValueTag::Atom, (n1, _a1)) => {
|
||||
read_heap_cell!(v2,
|
||||
(HeapCellValueTag::Atom, (n2, _a2)) => {
|
||||
if n1 != n2 {
|
||||
self.pdl.clear();
|
||||
return Some(n1.cmp(&n2));
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::Str, s) => {
|
||||
let n2 = cell_as_atom_cell!(self.heap[s])
|
||||
.get_name();
|
||||
|
||||
if n1 != n2 {
|
||||
self.pdl.clear();
|
||||
return Some(n1.cmp(&n2));
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
unreachable!();
|
||||
}
|
||||
)
|
||||
}
|
||||
(HeapCellValueTag::Str, s) => {
|
||||
let n1 = cell_as_atom_cell!(self.heap[s])
|
||||
.get_name();
|
||||
|
||||
read_heap_cell!(v2,
|
||||
(HeapCellValueTag::Atom, (n2, _a2)) => {
|
||||
if n1 != n2 {
|
||||
self.pdl.clear();
|
||||
return Some(n1.cmp(&n2));
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::Str, s) => {
|
||||
let n2 = cell_as_atom_cell!(self.heap[s])
|
||||
.get_name();
|
||||
|
||||
if n1 != n2 {
|
||||
self.pdl.clear();
|
||||
return Some(n1.cmp(&n2));
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
unreachable!();
|
||||
}
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
)
|
||||
}
|
||||
Some(TermOrderCategory::Compound) => {
|
||||
read_heap_cell!(v1,
|
||||
(HeapCellValueTag::Lis, l1) => {
|
||||
read_heap_cell!(v2,
|
||||
(HeapCellValueTag::PStrLoc, l2) => {
|
||||
if tabu_list.contains(&(l1, l2)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tabu_list.insert((l1, l2));
|
||||
|
||||
// like the action of
|
||||
// partial_string_to_pdl here but
|
||||
// the ordering of PDL pushes is
|
||||
// (crucially for comparison
|
||||
// correctness) different.
|
||||
let (c, succ_cell) = self.heap.last_str_char_and_tail(l2);
|
||||
|
||||
self.pdl.push(succ_cell);
|
||||
self.pdl.push(heap_loc_as_cell!(l1 + 1));
|
||||
|
||||
self.pdl.push(char_as_cell!(c));
|
||||
self.pdl.push(heap_loc_as_cell!(l1));
|
||||
}
|
||||
(HeapCellValueTag::Lis, l2) => {
|
||||
if tabu_list.contains(&(l1, l2)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tabu_list.insert((l1, l2));
|
||||
|
||||
self.pdl.push(self.heap[l2 + 1]);
|
||||
self.pdl.push(self.heap[l1 + 1]);
|
||||
|
||||
self.pdl.push(self.heap[l2]);
|
||||
self.pdl.push(self.heap[l1]);
|
||||
}
|
||||
(HeapCellValueTag::Str, s2) => {
|
||||
if tabu_list.contains(&(l1, s2)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let (name, arity) = cell_as_atom_cell!(self.heap[s2])
|
||||
.get_name_and_arity();
|
||||
|
||||
match (2, atom!(".")).cmp(&(arity, name)) {
|
||||
Ordering::Equal => {
|
||||
tabu_list.insert((l1, s2));
|
||||
|
||||
self.pdl.push(self.heap[s2 + 2]);
|
||||
self.pdl.push(self.heap[l1 + 1]);
|
||||
|
||||
self.pdl.push(self.heap[s2 + 1]);
|
||||
self.pdl.push(self.heap[l1]);
|
||||
}
|
||||
ordering => {
|
||||
self.pdl.clear();
|
||||
return Some(ordering);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
unreachable!();
|
||||
}
|
||||
)
|
||||
}
|
||||
(HeapCellValueTag::PStrLoc, l1) => {
|
||||
read_heap_cell!(v2,
|
||||
(HeapCellValueTag::PStrLoc, l2) => {
|
||||
if tabu_list.contains(&(l1, l2)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tabu_list.insert((l1, l2));
|
||||
|
||||
match self.heap.compare_pstr_segments(l1, l2) {
|
||||
PStrSegmentCmpResult::Continue(v1, v2) => {
|
||||
self.pdl.push(v1.offset_by(l1));
|
||||
self.pdl.push(v2.offset_by(l2));
|
||||
}
|
||||
PStrSegmentCmpResult::Less => {
|
||||
self.pdl.clear();
|
||||
return Some(Ordering::Less);
|
||||
}
|
||||
PStrSegmentCmpResult::Greater => {
|
||||
self.pdl.clear();
|
||||
return Some(Ordering::Greater);
|
||||
}
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::Lis, l2) => {
|
||||
if tabu_list.contains(&(l1, l2)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tabu_list.insert((l1, l2));
|
||||
|
||||
let (c, succ_cell) = self.heap.last_str_char_and_tail(l1);
|
||||
|
||||
self.pdl.push(succ_cell);
|
||||
self.pdl.push(heap_loc_as_cell!(l2 + 1));
|
||||
|
||||
self.pdl.push(char_as_cell!(c));
|
||||
self.pdl.push(heap_loc_as_cell!(l2));
|
||||
}
|
||||
(HeapCellValueTag::Str, s2) => {
|
||||
if tabu_list.contains(&(l1, s2)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tabu_list.insert((l1, s2));
|
||||
|
||||
let (n2, a2) = cell_as_atom_cell!(self.heap[s2])
|
||||
.get_name_and_arity();
|
||||
|
||||
match (2, atom!(".")).cmp(&(a2,n2)) {
|
||||
Ordering::Equal => {
|
||||
let (c, succ_cell) = self.heap.last_str_char_and_tail(l1);
|
||||
|
||||
self.pdl.push(heap_loc_as_cell!(s2+2));
|
||||
self.pdl.push(succ_cell);
|
||||
|
||||
self.pdl.push(heap_loc_as_cell!(s2+1));
|
||||
self.pdl.push(char_as_cell!(c));
|
||||
}
|
||||
ordering => {
|
||||
self.pdl.clear();
|
||||
return Some(ordering);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
);
|
||||
}
|
||||
(HeapCellValueTag::Str, s1) => {
|
||||
read_heap_cell!(v2,
|
||||
(HeapCellValueTag::Str, s2) => {
|
||||
if tabu_list.contains(&(s1, s2)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let (n1, a1) = cell_as_atom_cell!(self.heap[s1])
|
||||
.get_name_and_arity();
|
||||
|
||||
let (n2, a2) = cell_as_atom_cell!(self.heap[s2])
|
||||
.get_name_and_arity();
|
||||
|
||||
match (a1,n1).cmp(&(a2, n2)) {
|
||||
Ordering::Equal => {
|
||||
tabu_list.insert((s1, s2));
|
||||
|
||||
for idx in (1 .. a1+1).rev() {
|
||||
self.pdl.push(self.heap[s2+idx]);
|
||||
self.pdl.push(self.heap[s1+idx]);
|
||||
}
|
||||
}
|
||||
ordering => {
|
||||
self.pdl.clear();
|
||||
return Some(ordering);
|
||||
}
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::Lis, l2) => {
|
||||
if tabu_list.contains(&(s1, l2)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tabu_list.insert((s1, l2));
|
||||
|
||||
let (n1, a1) = cell_as_atom_cell!(self.heap[s1])
|
||||
.get_name_and_arity();
|
||||
|
||||
match (a1,n1).cmp(&(2, atom!("."))) {
|
||||
Ordering::Equal => {
|
||||
self.pdl.push(self.heap[l2]);
|
||||
self.pdl.push(self.heap[s1+1]);
|
||||
|
||||
self.pdl.push(self.heap[l2+1]);
|
||||
self.pdl.push(self.heap[s1+2]);
|
||||
}
|
||||
ordering => {
|
||||
self.pdl.clear();
|
||||
return Some(ordering);
|
||||
}
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::PStrLoc, l2) => {
|
||||
let (n1, a1) = cell_as_atom_cell!(self.heap[s1])
|
||||
.get_name_and_arity();
|
||||
|
||||
match (a1,n1).cmp(&(2, atom!("."))) {
|
||||
Ordering::Equal => {
|
||||
let (c, succ_cell) = self.heap.last_str_char_and_tail(l2);
|
||||
|
||||
self.pdl.push(succ_cell);
|
||||
self.pdl.push(heap_loc_as_cell!(s1+2));
|
||||
|
||||
self.pdl.push(char_as_cell!(c));
|
||||
self.pdl.push(heap_loc_as_cell!(s1+1));
|
||||
}
|
||||
ordering => {
|
||||
self.pdl.clear();
|
||||
return Some(ordering);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
);
|
||||
}
|
||||
None => {
|
||||
if v1 != v2 {
|
||||
self.pdl.clear();
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(Ordering::Equal)
|
||||
}
|
||||
|
||||
pub(crate) fn setup_call_n_init_goal_info(
|
||||
&mut self,
|
||||
goal: HeapCellValue,
|
||||
@@ -888,16 +550,37 @@ impl MachineState {
|
||||
}
|
||||
|
||||
// returns true on failure, false on success.
|
||||
pub fn eq_test(&mut self, h1: HeapCellValue, h2: HeapCellValue) -> bool {
|
||||
pub fn eq_test(&self, h1: HeapCellValue, h2: HeapCellValue) -> bool {
|
||||
if h1 == h2 {
|
||||
return false;
|
||||
}
|
||||
|
||||
compare_term_test!(self, h1, h2)
|
||||
.map(|o| o != Ordering::Equal)
|
||||
self.compare_term_test(h1, h2)
|
||||
.map(|o| !o.is_eq())
|
||||
.unwrap_or(true)
|
||||
}
|
||||
|
||||
pub fn compare_term_test(&self, h1: HeapCellValue, h2: HeapCellValue) -> Option<Ordering> {
|
||||
// all derefs downstack from this function are heap bound so
|
||||
// dereference stack vars pointing into the heap while possible
|
||||
let h1 = self.store(h1);
|
||||
let h2 = self.store(h2);
|
||||
|
||||
for term_pair in ParallelHeapIter::from(self, h1, h2) {
|
||||
match term_pair {
|
||||
TermPair::Vars(v1_offset, v2_offset) if v1_offset != v2_offset => {
|
||||
return Some(v1_offset.cmp(&v2_offset));
|
||||
}
|
||||
TermPair::Less(..) => return Some(Ordering::Less),
|
||||
TermPair::Greater(..) => return Some(Ordering::Greater),
|
||||
TermPair::Unordered(cell_1, cell_2) if cell_1 != cell_2 => return None,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Some(Ordering::Equal)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn try_functor_compound_case(&mut self, name: Atom, arity: usize) {
|
||||
self.try_functor_unify_components(atom_as_cell!(name), arity);
|
||||
@@ -922,7 +605,7 @@ impl MachineState {
|
||||
name: Atom,
|
||||
arity: usize,
|
||||
r: Ref,
|
||||
) -> Result<(), usize> {
|
||||
) -> Result<(), AllocError> {
|
||||
let h = self.heap.cell_len();
|
||||
let mut writer = self.heap.reserve(arity + 1)?;
|
||||
|
||||
@@ -949,7 +632,7 @@ impl MachineState {
|
||||
}
|
||||
};
|
||||
|
||||
(self.bind_fn)(self, r, f_a);
|
||||
self.occurs_check.bind(self, r, f_a);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1259,7 +942,10 @@ impl MachineState {
|
||||
}
|
||||
|
||||
// see 8.4.4.3 of Draft Technical Corrigendum 2 for an error guide.
|
||||
pub fn project_onto_key(&mut self, value: HeapCellValue) -> Result<HeapCellValue, MachineStub> {
|
||||
pub fn key_val_pair(
|
||||
&mut self,
|
||||
value: HeapCellValue,
|
||||
) -> Result<(HeapCellValue, HeapCellValue), MachineStub> {
|
||||
let stub_gen = || functor_stub(atom!("keysort"), 2);
|
||||
let store_v = self.store(self.deref(value));
|
||||
|
||||
@@ -1273,7 +959,7 @@ impl MachineState {
|
||||
let (name, arity) = cell_as_atom_cell!(self.heap[s]).get_name_and_arity();
|
||||
|
||||
if name == atom!("-") && arity == 2 {
|
||||
Ok(heap_loc_as_cell!(s + 1))
|
||||
Ok((heap_loc_as_cell!(s+1), heap_loc_as_cell!(s+2)))
|
||||
} else {
|
||||
let err = self.type_error(ValidType::Pair, self.heap[s]);
|
||||
Err(self.error_form(err, stub_gen()))
|
||||
|
||||
@@ -168,7 +168,7 @@ impl<'a> CopierTarget for TermCopyingMockWAM<'a> {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result<usize, usize> {
|
||||
fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result<usize, AllocError> {
|
||||
self.wam.machine_st.heap.copy_pstr_within(pstr_loc)
|
||||
}
|
||||
|
||||
@@ -178,12 +178,12 @@ impl<'a> CopierTarget for TermCopyingMockWAM<'a> {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn reserve(&mut self, num_cells: usize) -> Result<HeapWriter<'_>, usize> {
|
||||
fn reserve(&mut self, num_cells: usize) -> Result<HeapWriter<'_>, AllocError> {
|
||||
self.wam.machine_st.heap.reserve(num_cells)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn copy_slice_to_end(&mut self, bounds: Range<usize>) -> Result<(), usize> {
|
||||
fn copy_slice_to_end(&mut self, bounds: Range<usize>) -> Result<(), AllocError> {
|
||||
self.wam.machine_st.heap.copy_slice_to_end(bounds)
|
||||
}
|
||||
}
|
||||
@@ -581,56 +581,44 @@ mod tests {
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
compare_term_test!(wam, wam.heap[0], wam.heap[1]),
|
||||
wam.compare_term_test(wam.heap[0], wam.heap[1]),
|
||||
Some(Ordering::Less)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
compare_term_test!(wam, wam.heap[1], wam.heap[0]),
|
||||
wam.compare_term_test(wam.heap[1], wam.heap[0]),
|
||||
Some(Ordering::Greater)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
compare_term_test!(wam, wam.heap[0], wam.heap[0]),
|
||||
wam.compare_term_test(wam.heap[0], wam.heap[0]),
|
||||
Some(Ordering::Equal)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
compare_term_test!(wam, wam.heap[1], wam.heap[1]),
|
||||
wam.compare_term_test(wam.heap[1], wam.heap[1]),
|
||||
Some(Ordering::Equal)
|
||||
);
|
||||
|
||||
let cstr_cell = wam.heap.allocate_cstr("string").unwrap();
|
||||
|
||||
assert_eq!(
|
||||
compare_term_test!(wam, atom_as_cell!(atom!("atom")), cstr_cell),
|
||||
wam.compare_term_test(atom_as_cell!(atom!("atom")), cstr_cell),
|
||||
Some(Ordering::Less)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
compare_term_test!(
|
||||
wam,
|
||||
atom_as_cell!(atom!("atom")),
|
||||
atom_as_cell!(atom!("atom"))
|
||||
),
|
||||
wam.compare_term_test(atom_as_cell!(atom!("atom")), atom_as_cell!(atom!("atom"))),
|
||||
Some(Ordering::Equal)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
compare_term_test!(
|
||||
wam,
|
||||
atom_as_cell!(atom!("atom")),
|
||||
atom_as_cell!(atom!("aaa"))
|
||||
),
|
||||
wam.compare_term_test(atom_as_cell!(atom!("atom")), atom_as_cell!(atom!("aaa"))),
|
||||
Some(Ordering::Greater)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
compare_term_test!(
|
||||
wam,
|
||||
fixnum_as_cell!(Fixnum::build_with(6)),
|
||||
heap_loc_as_cell!(1)
|
||||
),
|
||||
wam.compare_term_test(fixnum_as_cell!(Fixnum::build_with(6)), heap_loc_as_cell!(1)),
|
||||
Some(Ordering::Greater)
|
||||
);
|
||||
|
||||
@@ -644,12 +632,12 @@ mod tests {
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
compare_term_test!(wam, heap_loc_as_cell!(0), heap_loc_as_cell!(0)),
|
||||
wam.compare_term_test(str_loc_as_cell!(0), str_loc_as_cell!(0)),
|
||||
Some(Ordering::Equal)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
compare_term_test!(wam, heap_loc_as_cell!(0), atom_as_cell!(atom!("a"))),
|
||||
wam.compare_term_test(str_loc_as_cell!(0), atom_as_cell!(atom!("a"))),
|
||||
Some(Ordering::Greater)
|
||||
);
|
||||
|
||||
@@ -676,23 +664,22 @@ mod tests {
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
compare_term_test!(wam, heap_loc_as_cell!(7), heap_loc_as_cell!(7)),
|
||||
wam.compare_term_test(heap_loc_as_cell!(7), heap_loc_as_cell!(7)),
|
||||
Some(Ordering::Equal)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
compare_term_test!(wam, heap_loc_as_cell!(0), heap_loc_as_cell!(7)),
|
||||
wam.compare_term_test(heap_loc_as_cell!(0), heap_loc_as_cell!(7)),
|
||||
Some(Ordering::Greater)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
compare_term_test!(wam, empty_list_as_cell!(), heap_loc_as_cell!(7)),
|
||||
wam.compare_term_test(empty_list_as_cell!(), heap_loc_as_cell!(7)),
|
||||
Some(Ordering::Less)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
compare_term_test!(
|
||||
wam,
|
||||
wam.compare_term_test(
|
||||
empty_list_as_cell!(),
|
||||
fixnum_as_cell!(Fixnum::build_with(1))
|
||||
),
|
||||
@@ -702,29 +689,29 @@ mod tests {
|
||||
let cstr_cell = wam.heap.allocate_cstr("string").unwrap();
|
||||
|
||||
assert_eq!(
|
||||
compare_term_test!(wam, empty_list_as_cell!(), cstr_cell),
|
||||
wam.compare_term_test(empty_list_as_cell!(), cstr_cell),
|
||||
Some(Ordering::Less)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
compare_term_test!(wam, empty_list_as_cell!(), atom_as_cell!(atom!("atom"))),
|
||||
wam.compare_term_test(empty_list_as_cell!(), atom_as_cell!(atom!("atom"))),
|
||||
Some(Ordering::Less)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
compare_term_test!(wam, atom_as_cell!(atom!("atom")), empty_list_as_cell!()),
|
||||
wam.compare_term_test(atom_as_cell!(atom!("atom")), empty_list_as_cell!()),
|
||||
Some(Ordering::Greater)
|
||||
);
|
||||
|
||||
let one_p_one = HeapCellValue::from(float_alloc!(1.1, &mut wam.arena));
|
||||
|
||||
assert_eq!(
|
||||
compare_term_test!(wam, one_p_one, fixnum_as_cell!(Fixnum::build_with(1))),
|
||||
wam.compare_term_test(one_p_one, fixnum_as_cell!(Fixnum::build_with(1))),
|
||||
Some(Ordering::Less)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
compare_term_test!(wam, fixnum_as_cell!(Fixnum::build_with(1)), one_p_one),
|
||||
wam.compare_term_test(fixnum_as_cell!(Fixnum::build_with(1)), one_p_one),
|
||||
Some(Ordering::Greater)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ use crate::instructions::*;
|
||||
use crate::machine::args::*;
|
||||
use crate::machine::compile::*;
|
||||
use crate::machine::copier::*;
|
||||
use crate::machine::heap::AllocError;
|
||||
use crate::machine::heap::*;
|
||||
use crate::machine::loader::*;
|
||||
use crate::machine::machine_errors::*;
|
||||
@@ -51,7 +52,6 @@ use crate::parser::dashu::{Integer, Rational};
|
||||
use crate::types::*;
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use lazy_static::lazy_static;
|
||||
use ordered_float::OrderedFloat;
|
||||
|
||||
use rand::rngs::StdRng;
|
||||
@@ -60,12 +60,11 @@ use std::cmp::Ordering;
|
||||
use std::env;
|
||||
use std::io::Read;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::process::ExitCode;
|
||||
use std::sync::OnceLock;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref INTERRUPT: AtomicBool = AtomicBool::new(false);
|
||||
}
|
||||
pub static INTERRUPT: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
/// An instance of Scryer Prolog.
|
||||
///
|
||||
@@ -141,9 +140,8 @@ mod libraries {
|
||||
}
|
||||
|
||||
pub static BREAK_FROM_DISPATCH_LOOP_LOC: usize = 0;
|
||||
pub static INSTALL_VERIFY_ATTR_INTERRUPT: usize = 1;
|
||||
pub static VERIFY_ATTR_INTERRUPT_LOC: usize = 2;
|
||||
pub static LIB_QUERY_SUCCESS: usize = 3;
|
||||
pub static VERIFY_ATTR_INTERRUPT_LOC: usize = 1;
|
||||
pub static LIB_QUERY_SUCCESS: usize = 2;
|
||||
|
||||
pub struct MachinePreludeView<'a> {
|
||||
pub indices: &'a mut IndexStore,
|
||||
@@ -233,12 +231,7 @@ impl Machine {
|
||||
|
||||
/// Gets the current inference count.
|
||||
pub fn get_inference_count(&mut self) -> u64 {
|
||||
self.machine_st
|
||||
.cwil
|
||||
.global_count
|
||||
.clone()
|
||||
.try_into()
|
||||
.unwrap()
|
||||
self.machine_st.cwil.global_count.try_into().unwrap()
|
||||
}
|
||||
|
||||
/// Runs the predicate `key` in `module_name` until completion.
|
||||
@@ -261,7 +254,9 @@ impl Machine {
|
||||
let p = index_ptr.local().unwrap();
|
||||
|
||||
// Leave a halting choice point to backtrack to in case the predicate fails or throws.
|
||||
self.allocate_stub_choice_point();
|
||||
if self.allocate_stub_choice_point().is_err() {
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
|
||||
self.machine_st.cp = BREAK_FROM_DISPATCH_LOOP_LOC;
|
||||
self.machine_st.p = p;
|
||||
@@ -414,12 +409,11 @@ impl Machine {
|
||||
}
|
||||
|
||||
pub(crate) fn add_impls_to_indices(&mut self) {
|
||||
let impls_offset = self.code.len() + 4;
|
||||
let impls_offset = self.code.len() + 3;
|
||||
|
||||
self.code.extend(vec![
|
||||
Instruction::BreakFromDispatchLoop,
|
||||
Instruction::InstallVerifyAttr,
|
||||
Instruction::VerifyAttrInterrupt(0),
|
||||
Instruction::RunVerifyAttr,
|
||||
Instruction::BreakFromDispatchLoop, // the location of LIB_QUERY_SUCCESS
|
||||
Instruction::ExecuteTermGreaterThan,
|
||||
Instruction::ExecuteTermLessThan,
|
||||
@@ -729,10 +723,10 @@ impl Machine {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) fn try_me_else(&mut self, offset: usize) {
|
||||
pub(super) fn try_me_else(&mut self, offset: usize) -> Result<(), AllocError> {
|
||||
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 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;
|
||||
@@ -758,13 +752,15 @@ impl Machine {
|
||||
}
|
||||
|
||||
self.machine_st.p += 1;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) fn indexed_try(&mut self, offset: usize) {
|
||||
pub(super) fn indexed_try(&mut self, offset: usize) -> Result<(), AllocError> {
|
||||
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 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;
|
||||
@@ -793,6 +789,7 @@ impl Machine {
|
||||
}
|
||||
|
||||
self.machine_st.p += offset;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -1227,7 +1224,7 @@ impl Machine {
|
||||
let key = Atom::from(h as u64);
|
||||
|
||||
match self.indices.global_variables.get_mut(&key) {
|
||||
Some((_, ref mut loc)) => *loc = None,
|
||||
Some((_, loc)) => *loc = None,
|
||||
None => unreachable!(),
|
||||
}
|
||||
}
|
||||
@@ -1236,7 +1233,7 @@ impl Machine {
|
||||
let value_cell = HeapCellValue::from(u64::from(self.machine_st.trail[i + 1]));
|
||||
|
||||
match self.indices.global_variables.get_mut(&key) {
|
||||
Some((_, ref mut loc)) => *loc = Some(value_cell),
|
||||
Some((_, loc)) => *loc = Some(value_cell),
|
||||
None => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,13 +5,42 @@ use crate::machine::machine_errors::CycleSearchResult;
|
||||
use crate::machine::system_calls::BrentAlgState;
|
||||
use crate::types::*;
|
||||
|
||||
trait StepperImpl {
|
||||
fn step<'a>(&self, iter: &mut HeapPStrIter<'a>) -> Option<PStrIteratee>;
|
||||
fn is_cyclic(&self) -> bool;
|
||||
}
|
||||
|
||||
struct PreCycleDiscoverStepper;
|
||||
|
||||
impl StepperImpl for PreCycleDiscoverStepper {
|
||||
fn step<'a>(&self, iter: &mut HeapPStrIter<'a>) -> Option<PStrIteratee> {
|
||||
iter.pre_cycle_discovery_stepper()
|
||||
}
|
||||
|
||||
fn is_cyclic(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
struct PostCycleDiscoverStepper;
|
||||
|
||||
impl StepperImpl for PostCycleDiscoverStepper {
|
||||
fn step<'a>(&self, iter: &mut HeapPStrIter<'a>) -> Option<PStrIteratee> {
|
||||
iter.post_cycle_discovery_stepper()
|
||||
}
|
||||
|
||||
fn is_cyclic(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct HeapPStrIter<'a> {
|
||||
pub heap: &'a Heap,
|
||||
// pub focus: HeapCellValue,
|
||||
orig_focus: usize,
|
||||
brent_st: BrentAlgState,
|
||||
stepper: fn(&mut HeapPStrIter<'a>) -> Option<PStrIteratee>,
|
||||
stepper: &'static dyn StepperImpl,
|
||||
}
|
||||
|
||||
struct PStrIterStep {
|
||||
@@ -33,7 +62,7 @@ impl<'a> HeapPStrIter<'a> {
|
||||
heap,
|
||||
orig_focus,
|
||||
brent_st: BrentAlgState::new(orig_focus),
|
||||
stepper: HeapPStrIter::pre_cycle_discovery_stepper,
|
||||
stepper: &PreCycleDiscoverStepper,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,7 +190,7 @@ impl<'a> HeapPStrIter<'a> {
|
||||
debug_assert!(matches!(cycle_result, CycleSearchResult::Cyclic { .. }));
|
||||
|
||||
self.walk_hare_to_cycle_end();
|
||||
self.stepper = HeapPStrIter::post_cycle_discovery_stepper;
|
||||
self.stepper = &PostCycleDiscoverStepper;
|
||||
}
|
||||
None => {
|
||||
// self.focus = self.heap[next_hare];
|
||||
@@ -194,7 +223,7 @@ impl<'a> HeapPStrIter<'a> {
|
||||
}
|
||||
|
||||
pub(crate) fn is_cyclic(&self) -> bool {
|
||||
self.stepper as usize == Self::post_cycle_discovery_stepper as usize
|
||||
self.stepper.is_cyclic()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,7 +232,7 @@ impl<'a> Iterator for HeapPStrIter<'a> {
|
||||
|
||||
#[inline(always)]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
(self.stepper)(self)
|
||||
self.stepper.step(self)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,6 +246,8 @@ mod test {
|
||||
fn pstr_iter_tests() {
|
||||
let mut wam = MockWAM::new();
|
||||
|
||||
let init_len = wam.machine_st.heap.cell_len();
|
||||
|
||||
let pstr_cell = wam.machine_st.heap.allocate_pstr("abc ").unwrap();
|
||||
wam.machine_st
|
||||
.heap
|
||||
@@ -233,7 +264,7 @@ mod test {
|
||||
assert_eq!(
|
||||
iter.next(),
|
||||
Some(PStrIteratee::PStrSlice {
|
||||
slice_loc: heap_index!(1),
|
||||
slice_loc: heap_index!(init_len),
|
||||
slice_len: "abc ".len()
|
||||
}),
|
||||
);
|
||||
@@ -241,9 +272,9 @@ mod test {
|
||||
assert!(!iter.is_cyclic());
|
||||
}
|
||||
|
||||
assert_eq!(wam.machine_st.heap[2], empty_list_as_cell!());
|
||||
assert_eq!(wam.machine_st.heap[init_len + 1], empty_list_as_cell!());
|
||||
|
||||
wam.machine_st.heap[2] = pstr_loc_as_cell!(heap_index!(3));
|
||||
wam.machine_st.heap[init_len + 1] = pstr_loc_as_cell!(heap_index!(init_len + 2));
|
||||
|
||||
wam.machine_st.heap.allocate_pstr("def").unwrap();
|
||||
let h = wam.machine_st.heap.cell_len();
|
||||
@@ -256,14 +287,14 @@ mod test {
|
||||
assert_eq!(
|
||||
iter.next(),
|
||||
Some(PStrIteratee::PStrSlice {
|
||||
slice_loc: heap_index!(1),
|
||||
slice_loc: heap_index!(init_len),
|
||||
slice_len: "abc ".len()
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
iter.next(),
|
||||
Some(PStrIteratee::PStrSlice {
|
||||
slice_loc: heap_index!(3),
|
||||
slice_loc: heap_index!(init_len + 2),
|
||||
slice_len: "def".len(),
|
||||
})
|
||||
);
|
||||
@@ -282,14 +313,14 @@ mod test {
|
||||
assert_eq!(
|
||||
iter.next(),
|
||||
Some(PStrIteratee::PStrSlice {
|
||||
slice_loc: heap_index!(1),
|
||||
slice_loc: heap_index!(init_len),
|
||||
slice_len: "abc ".len()
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
iter.next(),
|
||||
Some(PStrIteratee::PStrSlice {
|
||||
slice_loc: heap_index!(3),
|
||||
slice_loc: heap_index!(init_len + 2),
|
||||
slice_len: "def".len(),
|
||||
})
|
||||
);
|
||||
@@ -298,7 +329,7 @@ mod test {
|
||||
assert!(!iter.is_cyclic());
|
||||
}
|
||||
|
||||
wam.machine_st.heap[h] = pstr_loc_as_cell!(heap_index!(3));
|
||||
wam.machine_st.heap[h] = pstr_loc_as_cell!(heap_index!(init_len + 2));
|
||||
|
||||
{
|
||||
let mut iter = HeapPStrIter::new(&wam.machine_st.heap, 0);
|
||||
|
||||
@@ -37,7 +37,7 @@ fn setup_op_decl(mut terms: Vec<Term>) -> Result<OpDecl, CompilationError> {
|
||||
other => {
|
||||
return Err(CompilationError::InvalidDirective(
|
||||
DirectiveError::InvalidOpDeclSpecDomain(other),
|
||||
))
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -82,7 +82,7 @@ fn setup_op_decl(mut terms: Vec<Term>) -> Result<OpDecl, CompilationError> {
|
||||
|
||||
fn setup_predicate_indicator(term: &mut Term) -> Result<PredicateKey, CompilationError> {
|
||||
match term {
|
||||
Term::Clause(_, slash, ref mut terms)
|
||||
Term::Clause(_, slash, terms)
|
||||
if (*slash == atom!("/") || *slash == atom!("//")) && terms.len() == 2 =>
|
||||
{
|
||||
let arity = terms.pop().unwrap();
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use std::alloc;
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
|
||||
pub(crate) trait RawBlockTraits {
|
||||
fn init_size() -> usize;
|
||||
fn align() -> usize;
|
||||
|
||||
#[inline]
|
||||
fn base_offset(base: *const u8) -> *const u8 {
|
||||
base
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct RawBlock<T: RawBlockTraits> {
|
||||
pub(crate) size: usize,
|
||||
pub(crate) base: *const u8,
|
||||
pub(crate) top: *const u8,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: RawBlockTraits> RawBlock<T> {
|
||||
pub(crate) fn new() -> Self {
|
||||
let mut block = RawBlock {
|
||||
size: 0,
|
||||
base: ptr::null(),
|
||||
top: ptr::null(),
|
||||
_marker: PhantomData,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
block.grow();
|
||||
}
|
||||
|
||||
block
|
||||
}
|
||||
|
||||
unsafe fn init_at_size(&mut self, cap: usize) {
|
||||
let layout = alloc::Layout::from_size_align_unchecked(cap, T::align());
|
||||
|
||||
self.base = alloc::alloc(layout) as *const _;
|
||||
self.size = cap;
|
||||
|
||||
self.top = T::base_offset(self.base);
|
||||
}
|
||||
|
||||
pub(super) unsafe fn grow(&mut self) {
|
||||
if self.size == 0 {
|
||||
self.init_at_size(T::init_size());
|
||||
} else {
|
||||
let layout = alloc::Layout::from_size_align_unchecked(T::init_size(), T::align());
|
||||
let top_dist = self.top as usize - self.base as usize;
|
||||
|
||||
self.base = alloc::realloc(self.base as *mut _, layout, self.size * 2) as *const _;
|
||||
self.top = (self.base as usize + top_dist) as *const _;
|
||||
self.size *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
fn empty_block() -> Self {
|
||||
RawBlock {
|
||||
size: 0,
|
||||
base: ptr::null(),
|
||||
top: ptr::null(),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn take(&mut self) -> Self {
|
||||
mem::replace(self, Self::empty_block())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn free_space(&self) -> usize {
|
||||
debug_assert!(
|
||||
self.top >= self.base,
|
||||
"self.top = {:?} < {:?} = self.base",
|
||||
self.top,
|
||||
self.base
|
||||
);
|
||||
|
||||
self.size - (self.top as usize - self.base as usize)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) unsafe fn new_block(&mut self, size: usize) -> *const u8 {
|
||||
loop {
|
||||
if self.free_space() >= size {
|
||||
return (self.top as usize + size) as *const _;
|
||||
} else {
|
||||
self.grow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn deallocate(&mut self) {
|
||||
unsafe {
|
||||
let layout = alloc::Layout::from_size_align_unchecked(self.size, T::align());
|
||||
|
||||
alloc::dealloc(self.base as *mut u8, layout);
|
||||
|
||||
self.top = ptr::null();
|
||||
self.base = ptr::null();
|
||||
self.size = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
use core::marker::PhantomData;
|
||||
use std::ptr::NonNull;
|
||||
|
||||
use crate::machine::heap::AllocError;
|
||||
use crate::raw_block::*;
|
||||
use crate::types::*;
|
||||
|
||||
@@ -87,14 +89,20 @@ impl Index<usize> for Stack {
|
||||
|
||||
#[inline]
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
unsafe { &*self.buf.base.add(index).cast() }
|
||||
unsafe {
|
||||
let ptr = self.buf.get_unchecked(index);
|
||||
&*ptr.cast::<HeapCellValue>()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<usize> for Stack {
|
||||
#[inline]
|
||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||
unsafe { &mut *self.buf.base.add(index).cast_mut().cast() }
|
||||
unsafe {
|
||||
let ptr = self.buf.get_unchecked(index);
|
||||
&mut *ptr.cast_mut().cast::<HeapCellValue>()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,41 +167,39 @@ impl OrFrame {
|
||||
}
|
||||
|
||||
impl Stack {
|
||||
pub(crate) fn new() -> Self {
|
||||
Stack {
|
||||
buf: RawBlock::new(),
|
||||
pub(crate) fn new() -> Result<Self, AllocError> {
|
||||
Ok(Stack {
|
||||
buf: RawBlock::new()?,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn alloc(&mut self, frame_size: usize) -> *mut u8 {
|
||||
unsafe fn alloc(&mut self, frame_size: usize) -> Result<NonNull<u8>, AllocError> {
|
||||
loop {
|
||||
let ptr = self.buf.alloc(frame_size);
|
||||
|
||||
if ptr.is_null() {
|
||||
if !self.buf.grow() {
|
||||
panic!("growing the stack failed")
|
||||
unsafe {
|
||||
let ptr = self.buf.alloc(frame_size);
|
||||
if let Some(ptr) = NonNull::new(ptr) {
|
||||
return Ok(ptr);
|
||||
}
|
||||
} else {
|
||||
return ptr;
|
||||
self.buf.grow()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn allocate_and_frame(&mut self, num_cells: usize) -> usize {
|
||||
pub(crate) fn allocate_and_frame(&mut self, num_cells: usize) -> Result<usize, AllocError> {
|
||||
let frame_size = AndFrame::size_of(num_cells);
|
||||
|
||||
unsafe {
|
||||
let e = (*self.buf.ptr.get_mut()).addr() - self.buf.base.addr();
|
||||
let new_ptr = self.alloc(frame_size);
|
||||
let e = self.buf.used_bytes();
|
||||
let new_ptr = self.alloc(frame_size)?;
|
||||
let mut offset = prelude_size::<AndFramePrelude>();
|
||||
|
||||
for idx in 0..num_cells {
|
||||
let cell_ptr = new_ptr.add(offset) as *mut HeapCellValue;
|
||||
ptr::write(cell_ptr, stack_loc_as_cell!(AndFrame, e, idx + 1));
|
||||
let cell_ptr = new_ptr.add(offset).cast::<HeapCellValue>();
|
||||
ptr::write(cell_ptr.as_ptr(), stack_loc_as_cell!(AndFrame, e, idx + 1));
|
||||
|
||||
// Because in the Index and IndexMut inplementations we need to get this from
|
||||
// Because in the Index and IndexMut implementations we need to get this from
|
||||
// exposed provenance, we need to expose the provenance here, even though we don't
|
||||
// actually use the value for anything. This is a reminder that `expose_provenance`
|
||||
// isn't just a cast from a pointer to an integer but has actual side effects.
|
||||
@@ -205,27 +211,27 @@ impl Stack {
|
||||
let and_frame = self.index_and_frame_mut(e);
|
||||
and_frame.prelude.num_cells = num_cells;
|
||||
|
||||
e
|
||||
Ok(e)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn top(&self) -> usize {
|
||||
unsafe { (*self.buf.ptr.get()).addr() - self.buf.base.addr() }
|
||||
self.buf.used_bytes()
|
||||
}
|
||||
|
||||
pub(crate) fn allocate_or_frame(&mut self, num_cells: usize) -> usize {
|
||||
pub(crate) fn allocate_or_frame(&mut self, num_cells: usize) -> Result<usize, AllocError> {
|
||||
let frame_size = OrFrame::size_of(num_cells);
|
||||
|
||||
unsafe {
|
||||
let b = (*self.buf.ptr.get_mut()).addr() - self.buf.base.addr();
|
||||
let new_ptr = self.alloc(frame_size);
|
||||
let b = self.buf.used_bytes();
|
||||
let new_ptr = self.alloc(frame_size)?;
|
||||
let mut offset = prelude_size::<OrFramePrelude>();
|
||||
|
||||
for idx in 0..num_cells {
|
||||
let cell_ptr = new_ptr.byte_add(offset) as *mut HeapCellValue;
|
||||
ptr::write(cell_ptr, stack_loc_as_cell!(OrFrame, b, idx));
|
||||
let cell_ptr = new_ptr.byte_add(offset).cast::<HeapCellValue>();
|
||||
ptr::write(cell_ptr.as_ptr(), stack_loc_as_cell!(OrFrame, b, idx));
|
||||
|
||||
// Because in the Index and IndexMut inplementations we need to get this from
|
||||
// Because in the Index and IndexMut implementations we need to get this from
|
||||
// exposed provenance, we need to expose the provenance here, even though we don't
|
||||
// actually use the value for anything. This is a reminder that `expose_provenance`
|
||||
// isn't just a cast from a pointer to an integer but has actual side effects.
|
||||
@@ -237,41 +243,61 @@ impl Stack {
|
||||
let or_frame = self.index_or_frame_mut(b);
|
||||
or_frame.prelude.num_cells = num_cells;
|
||||
|
||||
b
|
||||
Ok(b)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_raw(&self, index: usize) -> *const u8 {
|
||||
debug_assert!(index < self.buf.used_bytes());
|
||||
|
||||
unsafe { self.buf.get_unchecked(index) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn index_and_frame(&self, e: usize) -> &AndFrame {
|
||||
unsafe { &*self.buf.base.add(e).cast() }
|
||||
let ptr = self.get_raw(e);
|
||||
|
||||
unsafe { &*ptr.cast::<AndFrame>() }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn index_and_frame_mut(&mut self, e: usize) -> &mut AndFrame {
|
||||
unsafe {
|
||||
// This is doing alignment wrong
|
||||
let ptr = self.buf.base.add(e);
|
||||
&mut *(ptr as *mut AndFrame)
|
||||
}
|
||||
let ptr = self.get_raw(e);
|
||||
|
||||
unsafe { &mut *ptr.cast_mut().cast::<AndFrame>() }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn index_or_frame(&self, b: usize) -> &OrFrame {
|
||||
unsafe { &*self.buf.base.add(b).cast() }
|
||||
let ptr = self.get_raw(b);
|
||||
|
||||
unsafe { &*ptr.cast::<OrFrame>() }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn index_or_frame_mut(&mut self, b: usize) -> &mut OrFrame {
|
||||
unsafe { &mut *self.buf.base.add(b).cast_mut().cast() }
|
||||
let ptr = self.get_raw(b);
|
||||
|
||||
unsafe { &mut *ptr.cast_mut().cast::<OrFrame>() }
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// The stack must contain a valid OrFrame at [`self.top()`](Self::top),
|
||||
/// which can only be achieved by allocating it in the first place and later truncating the stack.
|
||||
///
|
||||
/// No allocation must have been done since the last call to [`truncate()`](Self::truncate).
|
||||
#[inline(always)]
|
||||
pub(crate) unsafe fn index_dangling_or_frame(&self) -> &OrFrame {
|
||||
unsafe {
|
||||
let ptr = self.buf.get_unchecked(self.top());
|
||||
&*ptr.cast::<OrFrame>()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn truncate(&mut self, b: usize) {
|
||||
let base = unsafe { self.buf.base.add(b) };
|
||||
|
||||
if base < (*self.buf.ptr.get_mut()) {
|
||||
*self.buf.ptr.get_mut() = base.cast_mut();
|
||||
}
|
||||
self.buf.shift_back(b);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,7 +311,7 @@ mod tests {
|
||||
fn stack_tests() {
|
||||
let mut wam = MockWAM::new();
|
||||
|
||||
let e = wam.machine_st.stack.allocate_and_frame(10); // create an AND frame!
|
||||
let e = wam.machine_st.stack.allocate_and_frame(10).unwrap(); // create an AND frame!
|
||||
let and_frame = wam.machine_st.stack.index_and_frame_mut(e);
|
||||
|
||||
assert_eq!(
|
||||
@@ -303,7 +329,7 @@ mod tests {
|
||||
|
||||
assert_eq!(and_frame[5], empty_list_as_cell!());
|
||||
|
||||
let b = wam.machine_st.stack.allocate_or_frame(5);
|
||||
let b = wam.machine_st.stack.allocate_or_frame(5).unwrap();
|
||||
|
||||
let or_frame = wam.machine_st.stack.index_or_frame_mut(b);
|
||||
|
||||
@@ -311,7 +337,7 @@ mod tests {
|
||||
assert_eq!(or_frame[idx], stack_loc_as_cell!(OrFrame, b, idx));
|
||||
}
|
||||
|
||||
let next_e = wam.machine_st.stack.allocate_and_frame(9); // create an AND frame!
|
||||
let next_e = wam.machine_st.stack.allocate_and_frame(9).unwrap(); // create an AND frame!
|
||||
let and_frame = wam.machine_st.stack.index_and_frame_mut(next_e);
|
||||
|
||||
for idx in 0..9 {
|
||||
|
||||
@@ -12,10 +12,10 @@ use crate::machine::machine_indices::*;
|
||||
use crate::machine::machine_state::*;
|
||||
use crate::types::*;
|
||||
|
||||
pub use scryer_modular_bitfield::prelude::*;
|
||||
pub use modular_bitfield::prelude::*;
|
||||
|
||||
#[cfg(feature = "http")]
|
||||
use bytes::{buf::Reader as BufReader, Buf, Bytes};
|
||||
use bytes::{Buf, Bytes, buf::Reader as BufReader};
|
||||
use std::cmp::Ordering;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
@@ -23,6 +23,8 @@ use std::fmt::Debug;
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::hash::Hash;
|
||||
use std::io;
|
||||
use std::io::PipeReader;
|
||||
use std::io::PipeWriter;
|
||||
use std::io::{Cursor, ErrorKind, Read, Seek, SeekFrom, Write};
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::net::{Shutdown, TcpStream};
|
||||
@@ -38,10 +40,7 @@ use native_tls::TlsStream;
|
||||
#[cfg(feature = "http")]
|
||||
use warp::hyper;
|
||||
|
||||
mod compat;
|
||||
pub use compat::*;
|
||||
|
||||
#[derive(Debug, BitfieldSpecifier, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Specifier, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[bits = 1]
|
||||
pub enum StreamType {
|
||||
Binary,
|
||||
@@ -74,7 +73,7 @@ impl StreamType {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, BitfieldSpecifier, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Specifier, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[bits = 2]
|
||||
pub enum EOFAction {
|
||||
EOFCode,
|
||||
@@ -82,7 +81,7 @@ pub enum EOFAction {
|
||||
Reset,
|
||||
}
|
||||
|
||||
#[derive(Debug, BitfieldSpecifier, Copy, Clone, PartialEq)]
|
||||
#[derive(Debug, Specifier, Copy, Clone, PartialEq)]
|
||||
#[bits = 2]
|
||||
pub(crate) enum AtEndOfStream {
|
||||
Not,
|
||||
@@ -328,10 +327,7 @@ impl Write for HttpWriteStream {
|
||||
}
|
||||
|
||||
#[cfg(feature = "http")]
|
||||
impl HttpWriteStream {
|
||||
// TODO why is this suddenly dead code and should it be used somewhere?
|
||||
// Should this be impl Drop for HttpWriteStream?
|
||||
#[allow(dead_code)]
|
||||
impl Drop for HttpWriteStream {
|
||||
fn drop(&mut self) {
|
||||
let headers = unsafe { std::mem::ManuallyDrop::take(&mut self.headers) };
|
||||
let buffer = unsafe { std::mem::ManuallyDrop::take(&mut self.buffer) };
|
||||
@@ -660,6 +656,18 @@ impl Stream {
|
||||
|
||||
#[inline]
|
||||
pub fn stdin(arena: &mut Arena, add_history: bool) -> Stream {
|
||||
#[cfg(unix)]
|
||||
use std::io::IsTerminal;
|
||||
#[cfg(unix)]
|
||||
if !std::io::stdin().is_terminal() {
|
||||
use std::os::unix::io::{FromRawFd, RawFd};
|
||||
// dup fd 0 so the File can be owned (and closed later) without
|
||||
// closing the real stdin.
|
||||
let fd = unsafe { libc::dup(0 as RawFd) };
|
||||
let file = unsafe { File::from_raw_fd(fd) };
|
||||
return Stream::from_file_as_input(atom!("user_input"), file, arena);
|
||||
}
|
||||
|
||||
Stream::Readline(arena_alloc!(
|
||||
StreamLayout::new(ReadlineStream::new("", add_history)),
|
||||
arena
|
||||
@@ -742,49 +750,49 @@ impl Stream {
|
||||
|
||||
pub fn options(&self) -> &StreamOptions {
|
||||
match self {
|
||||
Stream::Byte(ref ptr) => &ptr.options,
|
||||
Stream::InputFile(ref ptr) => &ptr.options,
|
||||
Stream::OutputFile(ref ptr) => &ptr.options,
|
||||
Stream::StaticString(ref ptr) => &ptr.options,
|
||||
Stream::NamedTcp(ref ptr) => &ptr.options,
|
||||
Stream::Byte(ptr) => &ptr.options,
|
||||
Stream::InputFile(ptr) => &ptr.options,
|
||||
Stream::OutputFile(ptr) => &ptr.options,
|
||||
Stream::StaticString(ptr) => &ptr.options,
|
||||
Stream::NamedTcp(ptr) => &ptr.options,
|
||||
#[cfg(feature = "tls")]
|
||||
Stream::NamedTls(ref ptr) => &ptr.options,
|
||||
Stream::NamedTls(ptr) => &ptr.options,
|
||||
#[cfg(feature = "http")]
|
||||
Stream::HttpRead(ref ptr) => &ptr.options,
|
||||
Stream::HttpRead(ptr) => &ptr.options,
|
||||
#[cfg(feature = "http")]
|
||||
Stream::HttpWrite(ref ptr) => &ptr.options,
|
||||
Stream::Null(ref options) => options,
|
||||
Stream::Readline(ref ptr) => &ptr.options,
|
||||
Stream::StandardOutput(ref ptr) => &ptr.options,
|
||||
Stream::StandardError(ref ptr) => &ptr.options,
|
||||
Stream::Callback(ref ptr) => &ptr.options,
|
||||
Stream::InputChannel(ref ptr) => &ptr.options,
|
||||
Stream::PipeReader(ref ptr) => &ptr.options,
|
||||
Stream::PipeWriter(ref ptr) => &ptr.options,
|
||||
Stream::HttpWrite(ptr) => &ptr.options,
|
||||
Stream::Null(options) => options,
|
||||
Stream::Readline(ptr) => &ptr.options,
|
||||
Stream::StandardOutput(ptr) => &ptr.options,
|
||||
Stream::StandardError(ptr) => &ptr.options,
|
||||
Stream::Callback(ptr) => &ptr.options,
|
||||
Stream::InputChannel(ptr) => &ptr.options,
|
||||
Stream::PipeReader(ptr) => &ptr.options,
|
||||
Stream::PipeWriter(ptr) => &ptr.options,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn options_mut(&mut self) -> &mut StreamOptions {
|
||||
match self {
|
||||
Stream::Byte(ref mut ptr) => &mut ptr.options,
|
||||
Stream::InputFile(ref mut ptr) => &mut ptr.options,
|
||||
Stream::OutputFile(ref mut ptr) => &mut ptr.options,
|
||||
Stream::StaticString(ref mut ptr) => &mut ptr.options,
|
||||
Stream::NamedTcp(ref mut ptr) => &mut ptr.options,
|
||||
Stream::Byte(ptr) => &mut ptr.options,
|
||||
Stream::InputFile(ptr) => &mut ptr.options,
|
||||
Stream::OutputFile(ptr) => &mut ptr.options,
|
||||
Stream::StaticString(ptr) => &mut ptr.options,
|
||||
Stream::NamedTcp(ptr) => &mut ptr.options,
|
||||
#[cfg(feature = "tls")]
|
||||
Stream::NamedTls(ref mut ptr) => &mut ptr.options,
|
||||
Stream::NamedTls(ptr) => &mut ptr.options,
|
||||
#[cfg(feature = "http")]
|
||||
Stream::HttpRead(ref mut ptr) => &mut ptr.options,
|
||||
Stream::HttpRead(ptr) => &mut ptr.options,
|
||||
#[cfg(feature = "http")]
|
||||
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,
|
||||
Stream::StandardError(ref mut ptr) => &mut ptr.options,
|
||||
Stream::Callback(ref mut ptr) => &mut ptr.options,
|
||||
Stream::InputChannel(ref mut ptr) => &mut ptr.options,
|
||||
Stream::PipeReader(ref mut ptr) => &mut ptr.options,
|
||||
Stream::PipeWriter(ref mut ptr) => &mut ptr.options,
|
||||
Stream::HttpWrite(ptr) => &mut ptr.options,
|
||||
Stream::Null(options) => options,
|
||||
Stream::Readline(ptr) => &mut ptr.options,
|
||||
Stream::StandardOutput(ptr) => &mut ptr.options,
|
||||
Stream::StandardError(ptr) => &mut ptr.options,
|
||||
Stream::Callback(ptr) => &mut ptr.options,
|
||||
Stream::InputChannel(ptr) => &mut ptr.options,
|
||||
Stream::PipeReader(ptr) => &mut ptr.options,
|
||||
Stream::PipeWriter(ptr) => &mut ptr.options,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -952,17 +960,17 @@ impl CharRead for Stream {
|
||||
|
||||
fn consume(&mut self, nread: usize) {
|
||||
match self {
|
||||
Stream::InputFile(ref mut file) => file.consume(nread),
|
||||
Stream::NamedTcp(ref mut tcp_stream) => tcp_stream.consume(nread),
|
||||
Stream::InputFile(file) => file.consume(nread),
|
||||
Stream::NamedTcp(tcp_stream) => tcp_stream.consume(nread),
|
||||
#[cfg(feature = "tls")]
|
||||
Stream::NamedTls(ref mut tls_stream) => tls_stream.consume(nread),
|
||||
Stream::NamedTls(tls_stream) => tls_stream.consume(nread),
|
||||
#[cfg(feature = "http")]
|
||||
Stream::HttpRead(ref mut http_stream) => http_stream.consume(nread),
|
||||
Stream::Readline(ref mut rl_stream) => rl_stream.consume(nread),
|
||||
Stream::StaticString(ref mut src) => src.consume(nread),
|
||||
Stream::Byte(ref mut cursor) => cursor.consume(nread),
|
||||
Stream::InputChannel(ref mut cursor) => cursor.consume(nread),
|
||||
Stream::PipeReader(ref mut cursor) => cursor.consume(nread),
|
||||
Stream::HttpRead(http_stream) => http_stream.consume(nread),
|
||||
Stream::Readline(rl_stream) => rl_stream.consume(nread),
|
||||
Stream::StaticString(src) => src.consume(nread),
|
||||
Stream::Byte(cursor) => cursor.consume(nread),
|
||||
Stream::InputChannel(cursor) => cursor.consume(nread),
|
||||
Stream::PipeReader(cursor) => cursor.consume(nread),
|
||||
#[cfg(feature = "http")]
|
||||
Stream::HttpWrite(_) => {}
|
||||
Stream::OutputFile(_)
|
||||
@@ -1011,17 +1019,17 @@ impl Read for Stream {
|
||||
impl Write for Stream {
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
match self {
|
||||
Stream::OutputFile(ref mut file) => file.write(buf),
|
||||
Stream::NamedTcp(ref mut tcp_stream) => tcp_stream.get_mut().write(buf),
|
||||
Stream::OutputFile(file) => file.write(buf),
|
||||
Stream::NamedTcp(tcp_stream) => tcp_stream.get_mut().write(buf),
|
||||
#[cfg(feature = "tls")]
|
||||
Stream::NamedTls(ref mut tls_stream) => tls_stream.get_mut().write(buf),
|
||||
Stream::Byte(ref mut cursor) => cursor.get_mut().write(buf),
|
||||
Stream::Callback(ref mut callback_stream) => callback_stream.get_mut().write(buf),
|
||||
Stream::NamedTls(tls_stream) => tls_stream.get_mut().write(buf),
|
||||
Stream::Byte(cursor) => cursor.get_mut().write(buf),
|
||||
Stream::Callback(callback_stream) => callback_stream.get_mut().write(buf),
|
||||
Stream::StandardOutput(stream) => stream.write(buf),
|
||||
Stream::StandardError(stream) => stream.write(buf),
|
||||
#[cfg(feature = "http")]
|
||||
Stream::HttpWrite(ref mut stream) => stream.get_mut().write(buf),
|
||||
Stream::PipeWriter(ref mut stream) => stream.get_mut().write(buf),
|
||||
Stream::HttpWrite(stream) => stream.get_mut().write(buf),
|
||||
Stream::PipeWriter(stream) => stream.get_mut().write(buf),
|
||||
#[cfg(feature = "http")]
|
||||
Stream::HttpRead(_) => Err(std::io::Error::new(
|
||||
ErrorKind::PermissionDenied,
|
||||
@@ -1041,17 +1049,17 @@ impl Write for Stream {
|
||||
|
||||
fn flush(&mut self) -> std::io::Result<()> {
|
||||
match self {
|
||||
Stream::OutputFile(ref mut file) => file.stream.flush(),
|
||||
Stream::NamedTcp(ref mut tcp_stream) => tcp_stream.stream.get_mut().flush(),
|
||||
Stream::OutputFile(file) => file.stream.flush(),
|
||||
Stream::NamedTcp(tcp_stream) => tcp_stream.stream.get_mut().flush(),
|
||||
#[cfg(feature = "tls")]
|
||||
Stream::NamedTls(ref mut tls_stream) => tls_stream.stream.get_mut().flush(),
|
||||
Stream::Byte(ref mut cursor) => cursor.stream.get_mut().flush(),
|
||||
Stream::Callback(ref mut callback_stream) => callback_stream.stream.get_mut().flush(),
|
||||
Stream::NamedTls(tls_stream) => tls_stream.stream.get_mut().flush(),
|
||||
Stream::Byte(cursor) => cursor.stream.get_mut().flush(),
|
||||
Stream::Callback(callback_stream) => callback_stream.stream.get_mut().flush(),
|
||||
Stream::StandardError(stream) => stream.stream.flush(),
|
||||
Stream::StandardOutput(stream) => stream.stream.flush(),
|
||||
Stream::PipeWriter(ref mut stream) => stream.stream.get_mut().flush(),
|
||||
Stream::PipeWriter(stream) => stream.stream.get_mut().flush(),
|
||||
#[cfg(feature = "http")]
|
||||
Stream::HttpWrite(ref mut stream) => stream.stream.get_mut().flush(),
|
||||
Stream::HttpWrite(stream) => stream.stream.get_mut().flush(),
|
||||
#[cfg(feature = "http")]
|
||||
Stream::HttpRead(_) => Err(std::io::Error::new(
|
||||
ErrorKind::PermissionDenied,
|
||||
@@ -1522,58 +1530,58 @@ impl Stream {
|
||||
#[inline]
|
||||
pub(crate) fn close(&mut self) -> Result<(), std::io::Error> {
|
||||
match self {
|
||||
Stream::NamedTcp(ref mut tcp_stream) => {
|
||||
Stream::NamedTcp(tcp_stream) => {
|
||||
tcp_stream.inner_mut().tcp_stream.shutdown(Shutdown::Both)
|
||||
}
|
||||
#[cfg(feature = "tls")]
|
||||
Stream::NamedTls(ref mut tls_stream) => tls_stream.inner_mut().tls_stream.shutdown(),
|
||||
Stream::NamedTls(tls_stream) => tls_stream.inner_mut().tls_stream.shutdown(),
|
||||
#[cfg(feature = "http")]
|
||||
Stream::HttpRead(ref mut http_stream) => {
|
||||
Stream::HttpRead(http_stream) => {
|
||||
http_stream.drop_payload();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
#[cfg(feature = "http")]
|
||||
Stream::HttpWrite(mut http_stream) => {
|
||||
&mut Stream::HttpWrite(mut http_stream) => {
|
||||
http_stream.drop_payload();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Stream::InputFile(mut file_stream) => {
|
||||
&mut Stream::InputFile(mut file_stream) => {
|
||||
// close the stream by dropping the inner File.
|
||||
file_stream.drop_payload();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Stream::OutputFile(mut file_stream) => {
|
||||
&mut Stream::OutputFile(mut file_stream) => {
|
||||
// close the stream by dropping the inner File.
|
||||
file_stream.drop_payload();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Stream::Byte(mut stream) => {
|
||||
&mut Stream::Byte(mut stream) => {
|
||||
stream.drop_payload();
|
||||
Ok(())
|
||||
}
|
||||
Stream::Callback(mut stream) => {
|
||||
&mut Stream::Callback(mut stream) => {
|
||||
stream.drop_payload();
|
||||
Ok(())
|
||||
}
|
||||
Stream::InputChannel(mut stream) => {
|
||||
&mut Stream::InputChannel(mut stream) => {
|
||||
stream.drop_payload();
|
||||
Ok(())
|
||||
}
|
||||
Stream::StaticString(mut stream) => {
|
||||
&mut Stream::StaticString(mut stream) => {
|
||||
stream.drop_payload();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Stream::PipeReader(mut stream) => {
|
||||
&mut Stream::PipeReader(mut stream) => {
|
||||
stream.drop_payload();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Stream::PipeWriter(mut stream) => {
|
||||
&mut Stream::PipeWriter(mut stream) => {
|
||||
stream.drop_payload();
|
||||
Ok(())
|
||||
}
|
||||
@@ -1636,11 +1644,11 @@ impl Stream {
|
||||
self.set_past_end_of_stream(false);
|
||||
|
||||
match self {
|
||||
Stream::Byte(ref mut cursor) => {
|
||||
Stream::Byte(cursor) => {
|
||||
cursor.stream.get_mut().0.set_position(0);
|
||||
true
|
||||
}
|
||||
Stream::InputFile(ref mut file_stream) => {
|
||||
Stream::InputFile(file_stream) => {
|
||||
file_stream
|
||||
.stream
|
||||
.get_mut()
|
||||
@@ -1649,11 +1657,11 @@ impl Stream {
|
||||
.unwrap();
|
||||
true
|
||||
}
|
||||
Stream::Readline(ref mut readline_stream) => {
|
||||
Stream::Readline(readline_stream) => {
|
||||
readline_stream.reset();
|
||||
true
|
||||
}
|
||||
Stream::InputChannel(ref mut input_channel_stream) => {
|
||||
Stream::InputChannel(input_channel_stream) => {
|
||||
input_channel_stream.stream.get_mut().inner.set_position(0);
|
||||
true
|
||||
}
|
||||
@@ -1664,7 +1672,7 @@ impl Stream {
|
||||
#[inline]
|
||||
pub(crate) fn peek_byte(&mut self) -> std::io::Result<u8> {
|
||||
match self {
|
||||
Stream::Byte(ref mut cursor) => {
|
||||
Stream::Byte(cursor) => {
|
||||
let mut b = [0u8; 1];
|
||||
let pos = cursor.stream.get_mut().0.position();
|
||||
|
||||
@@ -1676,15 +1684,15 @@ impl Stream {
|
||||
_ => Err(std::io::Error::new(ErrorKind::UnexpectedEof, "end of file")),
|
||||
}
|
||||
}
|
||||
Stream::InputFile(ref mut file) => match file.peek_byte() {
|
||||
Stream::InputFile(file) => match file.peek_byte() {
|
||||
Some(result) => Ok(result?),
|
||||
_ => Err(std::io::Error::new(
|
||||
ErrorKind::UnexpectedEof,
|
||||
StreamError::PeekByteFailed,
|
||||
)),
|
||||
},
|
||||
Stream::Readline(ref mut stream) => stream.stream.peek_byte(),
|
||||
Stream::NamedTcp(ref mut stream) => {
|
||||
Stream::Readline(stream) => stream.stream.peek_byte(),
|
||||
Stream::NamedTcp(stream) => {
|
||||
let mut b = [0u8; 1];
|
||||
stream.stream.get_mut().tcp_stream.peek(&mut b)?;
|
||||
Ok(b[0])
|
||||
@@ -1950,7 +1958,7 @@ impl MachineState {
|
||||
) -> Result<Stream, ParserError> {
|
||||
match stream.peek_char() {
|
||||
None => Ok(stream), // empty stream is handled gracefully by Lexer::eof
|
||||
Some(Err(e)) => Err(ParserError::IO(e)),
|
||||
Some(Err(e)) => Err(ParserError::from(e)),
|
||||
Some(Ok(c)) => {
|
||||
if c == '\u{feff}' {
|
||||
// skip UTF-8 BOM
|
||||
@@ -2187,8 +2195,8 @@ mod test {
|
||||
use crate::*;
|
||||
use std::{cell::RefCell, io::Read, io::Write, rc::Rc};
|
||||
|
||||
use crate::machine::config::*;
|
||||
use crate::LeafAnswer;
|
||||
use crate::machine::config::*;
|
||||
|
||||
use super::{Stream, StreamOptions};
|
||||
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
#[cfg(rust_version = "1.87.0")]
|
||||
pub use ge_1_87_0::{PipeReader, PipeWriter};
|
||||
|
||||
#[cfg(not(rust_version = "1.87.0"))]
|
||||
pub use lt_1_87_0::{PipeReader, PipeWriter};
|
||||
|
||||
#[cfg(not(rust_version = "1.87.0"))]
|
||||
pub(crate) use lt_1_87_0::PipeReaderInner;
|
||||
|
||||
#[cfg(not(rust_version = "1.87.0"))]
|
||||
mod lt_1_87_0 {
|
||||
use std::process::{ChildStderr, ChildStdout};
|
||||
|
||||
pub type PipeWriter = std::process::ChildStdin;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PipeReader(pub(crate) PipeReaderInner);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum PipeReaderInner {
|
||||
Stdout(ChildStdout),
|
||||
Stderr(ChildStderr),
|
||||
}
|
||||
|
||||
impl std::io::Read for PipeReader {
|
||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
match &mut self.0 {
|
||||
PipeReaderInner::Stdout(child_stdout) => child_stdout.read(buf),
|
||||
PipeReaderInner::Stderr(child_stderr) => child_stderr.read(buf),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_vectored(
|
||||
&mut self,
|
||||
bufs: &mut [std::io::IoSliceMut<'_>],
|
||||
) -> std::io::Result<usize> {
|
||||
match &mut self.0 {
|
||||
PipeReaderInner::Stdout(child_stdout) => child_stdout.read_vectored(bufs),
|
||||
PipeReaderInner::Stderr(child_stderr) => child_stderr.read_vectored(bufs),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> std::io::Result<usize> {
|
||||
match &mut self.0 {
|
||||
PipeReaderInner::Stdout(child_stdout) => child_stdout.read_to_end(buf),
|
||||
PipeReaderInner::Stderr(child_stderr) => child_stderr.read_to_end(buf),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_to_string(&mut self, buf: &mut String) -> std::io::Result<usize> {
|
||||
match &mut self.0 {
|
||||
PipeReaderInner::Stdout(child_stdout) => child_stdout.read_to_string(buf),
|
||||
PipeReaderInner::Stderr(child_stderr) => child_stderr.read_to_string(buf),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> std::io::Result<()> {
|
||||
match &mut self.0 {
|
||||
PipeReaderInner::Stdout(child_stdout) => child_stdout.read_exact(buf),
|
||||
PipeReaderInner::Stderr(child_stderr) => child_stderr.read_exact(buf),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(rust_version = "1.87.0")]
|
||||
mod ge_1_87_0 {
|
||||
pub type PipeReader = std::io::PipeReader;
|
||||
pub type PipeWriter = std::io::PipeWriter;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
use crate::machine::Number;
|
||||
use crate::Machine;
|
||||
use crate::machine::Number;
|
||||
use ordered_float::OrderedFloat;
|
||||
use puruspe::beta::*;
|
||||
use puruspe::error::*;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::arena::*;
|
||||
use crate::forms::*;
|
||||
use crate::heap_iter::{stackful_preorder_iter, NonListElider};
|
||||
use crate::heap_iter::{NonListElider, stackful_preorder_iter};
|
||||
use crate::machine::machine_state::*;
|
||||
use crate::machine::*;
|
||||
use crate::offset_table::*;
|
||||
@@ -9,19 +9,14 @@ use crate::types::*;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use derive_more::*;
|
||||
use fxhash::FxBuildHasher;
|
||||
use indexmap::IndexSet;
|
||||
use num_order::NumOrd;
|
||||
|
||||
impl MachineState {
|
||||
pub(crate) fn partial_string_to_pdl(&mut self, pstr_loc: usize, l: usize) {
|
||||
let (c, succ_cell) = self.heap.last_str_char_and_tail(pstr_loc);
|
||||
|
||||
self.pdl.push(heap_loc_as_cell!(l + 1));
|
||||
self.pdl.push(succ_cell);
|
||||
|
||||
self.pdl.push(heap_loc_as_cell!(l));
|
||||
self.pdl.push(char_as_cell!(c));
|
||||
self.pdl.push((succ_cell, heap_loc_as_cell!(l + 1)));
|
||||
self.pdl.push((char_as_cell!(c), heap_loc_as_cell!(l)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,8 +32,7 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
|
||||
|
||||
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));
|
||||
self.pdl.push((heap_loc_as_cell!(s1+1+idx), heap_loc_as_cell!(s2+1+idx)));
|
||||
}
|
||||
} else {
|
||||
self.fail = true;
|
||||
@@ -47,8 +41,7 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
|
||||
(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));
|
||||
self.pdl.push((heap_loc_as_cell!(s1+1+idx), heap_loc_as_cell!(l2+1+idx)));
|
||||
}
|
||||
} else {
|
||||
self.fail = true;
|
||||
@@ -76,8 +69,7 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
|
||||
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));
|
||||
self.pdl.push((heap_loc_as_cell!(l1 + idx), heap_loc_as_cell!(l2 + idx)));
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::Str, s2) => {
|
||||
@@ -86,8 +78,7 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
|
||||
|
||||
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));
|
||||
self.pdl.push((heap_loc_as_cell!(l1+idx), heap_loc_as_cell!(s2+1+idx)));
|
||||
}
|
||||
} else {
|
||||
self.fail = true;
|
||||
@@ -136,8 +127,7 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
|
||||
(HeapCellValueTag::PStrLoc, other_pstr_loc) => {
|
||||
match machine_st.heap.compare_pstr_segments(pstr_loc, other_pstr_loc) {
|
||||
PStrSegmentCmpResult::Continue(v1, v2) => {
|
||||
machine_st.pdl.push(v1.offset_by(pstr_loc));
|
||||
machine_st.pdl.push(v2.offset_by(other_pstr_loc));
|
||||
machine_st.pdl.push((v2.offset_by(other_pstr_loc), v1.offset_by(pstr_loc)));
|
||||
}
|
||||
_ => {
|
||||
machine_st.fail = true;
|
||||
@@ -355,13 +345,15 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
|
||||
}
|
||||
|
||||
fn unify_internal(&mut self) {
|
||||
let mut tabu_list = IndexSet::with_hasher(FxBuildHasher::default());
|
||||
debug_assert!(self.unify_tabu_list.is_empty());
|
||||
|
||||
while let Some((s1, s2)) = self.pdl.pop() {
|
||||
if self.fail {
|
||||
// FIXME(msrv) FIXME(edition2024) use let chain to move this to check this in the while condition
|
||||
break;
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -383,7 +375,7 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
|
||||
Self::unify_atom(self, name, d2);
|
||||
}
|
||||
(HeapCellValueTag::Str, s1) => {
|
||||
if tabu_list.contains(&(d1, d2)) {
|
||||
if self.unify_tabu_list.contains(&(d1, d2)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -391,11 +383,11 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
|
||||
|
||||
if !self.fail {
|
||||
let d2 = self.store(d2);
|
||||
tabu_list.insert((d1, d2));
|
||||
self.unify_tabu_list.insert((d1, d2));
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::Lis, l1) => {
|
||||
if d2.is_ref() && tabu_list.contains(&(d1, d2)) {
|
||||
if d2.is_ref() && self.unify_tabu_list.contains(&(d1, d2)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -403,7 +395,7 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
|
||||
|
||||
if !self.fail {
|
||||
let d2 = self.store(d2);
|
||||
tabu_list.insert((d1, d2));
|
||||
self.unify_tabu_list.insert((d1, d2));
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::PStrLoc, l) => {
|
||||
@@ -411,7 +403,7 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
|
||||
(HeapCellValueTag::PStrLoc |
|
||||
HeapCellValueTag::Lis |
|
||||
HeapCellValueTag::Str) => {
|
||||
if tabu_list.contains(&(d1, d2)) {
|
||||
if self.unify_tabu_list.contains(&(d1, d2)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -429,7 +421,7 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
|
||||
|
||||
if !self.fail && !d2.is_constant() {
|
||||
let d2 = self.store(d2);
|
||||
tabu_list.insert((d1, d2));
|
||||
self.unify_tabu_list.insert((d1, d2));
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::F64Offset, f1) => {
|
||||
@@ -450,6 +442,8 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
self.unify_tabu_list.clear();
|
||||
}
|
||||
|
||||
fn bind(&mut self, r: Ref, value: HeapCellValue);
|
||||
|
||||
@@ -138,14 +138,7 @@ macro_rules! typed_arena_ptr_as_cell {
|
||||
}
|
||||
|
||||
macro_rules! raw_ptr_as_cell {
|
||||
($ptr:expr) => {{
|
||||
// Cell is 64-bit, but raw ptr is 32-bit in 32-bit systems
|
||||
let ptr: *const _ = $ptr;
|
||||
// This needs to expose provenance because it needs to be turned back into a pointer
|
||||
// in contexts where there is no available provenance locally. For example, in
|
||||
// `ConsPtr::as_ptr`.
|
||||
HeapCellValue::from_ptr_addr(ptr.expose_provenance())
|
||||
}};
|
||||
($ptr:expr) => {{ HeapCellValue::from_arena_header_ptr($ptr) }};
|
||||
}
|
||||
|
||||
macro_rules! untyped_arena_ptr_as_cell {
|
||||
@@ -369,12 +362,6 @@ macro_rules! compare_number_instr {
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! interm {
|
||||
($n: expr) => {
|
||||
ArithmeticTerm::Interm($n)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! ar_reg {
|
||||
($r: expr) => {
|
||||
ArithmeticTerm::Reg($r)
|
||||
@@ -412,47 +399,32 @@ macro_rules! index_store {
|
||||
}
|
||||
|
||||
macro_rules! unify {
|
||||
($machine_st:expr, $($value:expr),*) => {{
|
||||
$($machine_st.pdl.push($value);)*
|
||||
($machine_st:expr, $($v1:expr, $v2:expr),*) => {{
|
||||
$($machine_st.pdl.push(($v2, $v1));)*
|
||||
$machine_st.unify()
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! unify_fn {
|
||||
($machine_st:expr, $($value:expr),*) => {{
|
||||
$($machine_st.pdl.push($value);)*
|
||||
($machine_st.unify_fn)(&mut $machine_st)
|
||||
($machine_st:expr, $($v1:expr, $v2: expr),*) => {{
|
||||
$($machine_st.pdl.push(($v2, $v1));)*
|
||||
$machine_st.occurs_check.unify(&mut $machine_st)
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! unify_with_occurs_check {
|
||||
($machine_st:expr, $($value:expr),*) => {{
|
||||
$($machine_st.pdl.push($value);)*
|
||||
($machine_st:expr, $($v1:expr, $v2:expr),*) => {{
|
||||
$($machine_st.pdl.push(($v2, $v1));)*
|
||||
$machine_st.unify_with_occurs_check()
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! compare_term_test {
|
||||
($machine_st:expr, $e1:expr, $e2:expr) => {{
|
||||
$machine_st.pdl.push($e2);
|
||||
$machine_st.pdl.push($e1);
|
||||
|
||||
$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)
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! step_or_resource_error {
|
||||
($machine_st:expr, $val:expr) => {{
|
||||
match $val {
|
||||
Ok(r) => r,
|
||||
Err(err_loc) => {
|
||||
$machine_st.throw_resource_error(err_loc);
|
||||
Err(err) => {
|
||||
$machine_st.throw_resource_error(err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -460,8 +432,8 @@ macro_rules! step_or_resource_error {
|
||||
($machine_st:expr, $val:expr, $fail:block) => {{
|
||||
match $val {
|
||||
Ok(r) => r,
|
||||
Err(err_loc) => {
|
||||
$machine_st.throw_resource_error(err_loc);
|
||||
Err(err) => {
|
||||
$machine_st.throw_resource_error(err);
|
||||
$fail
|
||||
}
|
||||
}
|
||||
@@ -476,14 +448,29 @@ macro_rules! resource_error_call_result {
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! heap_index {
|
||||
macro_rules! heap_index_checked {
|
||||
($idx:expr) => {
|
||||
($idx) * std::mem::size_of::<HeapCellValue>()
|
||||
std::mem::size_of::<HeapCellValue>().checked_mul($idx)
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) use heap_index_checked;
|
||||
|
||||
macro_rules! heap_index {
|
||||
($idx:expr) => {{
|
||||
let idx = $idx;
|
||||
$crate::macros::heap_index_checked!(idx).unwrap_or_else(|| {
|
||||
panic!(
|
||||
"overflow while calculating heap index {idx} * {} > {}",
|
||||
std::mem::size_of::<HeapCellValue>(),
|
||||
usize::MAX,
|
||||
)
|
||||
})
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! cell_index {
|
||||
($idx:expr) => {
|
||||
(($idx) / std::mem::size_of::<HeapCellValue>())
|
||||
($idx) / std::mem::size_of::<HeapCellValue>()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,17 +2,18 @@ use std::cell::UnsafeCell;
|
||||
use std::sync::Arc;
|
||||
use std::{fmt, mem, ptr};
|
||||
|
||||
use arcu::Rcu;
|
||||
use arcu::atomic::Arcu;
|
||||
use arcu::epoch_counters::GlobalEpochCounterPool;
|
||||
use arcu::rcu_ref::RcuRef;
|
||||
use arcu::Rcu;
|
||||
use fxhash::FxBuildHasher;
|
||||
use indexmap::IndexMap;
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
|
||||
use crate::machine::heap::AllocError;
|
||||
use crate::machine::machine_indices::IndexPtr;
|
||||
use crate::raw_block::RawBlock;
|
||||
use crate::raw_block::RawBlockTraits;
|
||||
use crate::raw_block::{RawBlock, RawBlockConcurrent};
|
||||
|
||||
use ordered_float::OrderedFloat;
|
||||
|
||||
@@ -58,8 +59,10 @@ impl<T: RawBlockTraits> From<Arc<ConcurrentOffsetTable<T>>> for OffsetTableImpl<
|
||||
|
||||
impl<T: fmt::Debug + RawBlockTraits> OffsetTableImpl<T> {
|
||||
#[inline(always)]
|
||||
pub fn new() -> Self {
|
||||
Self(InnerOffsetTableImpl::Serial(SerialOffsetTable::new()))
|
||||
pub fn new() -> Result<Self, AllocError> {
|
||||
Ok(Self(
|
||||
InnerOffsetTableImpl::Serial(SerialOffsetTable::new()?),
|
||||
))
|
||||
}
|
||||
|
||||
#[must_use = "the returned concurrent table must be absorbed into the owned OffsetTable"]
|
||||
@@ -90,8 +93,9 @@ impl<T: fmt::Debug + RawBlockTraits> OffsetTableImpl<T> {
|
||||
// this shouldn't be able to fail
|
||||
let raw_block =
|
||||
Arc::try_unwrap(table.block.replace(RawBlock::empty_block())).unwrap();
|
||||
self.0 =
|
||||
InnerOffsetTableImpl::Serial(SerialOffsetTable { block: raw_block });
|
||||
self.0 = InnerOffsetTableImpl::Serial(SerialOffsetTable {
|
||||
block: raw_block.into(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
Err(table_arc) => {
|
||||
@@ -116,7 +120,7 @@ impl<T: fmt::Debug + RawBlockTraits> OffsetTableImpl<T> {
|
||||
|
||||
impl<T: fmt::Debug + RawBlockTraits> Default for OffsetTableImpl<T> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
Self::new().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,7 +131,7 @@ struct SerialOffsetTable<T: RawBlockTraits> {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ConcurrentOffsetTable<T: RawBlockTraits> {
|
||||
block: Arcu<RawBlock<T>, GlobalEpochCounterPool>,
|
||||
block: Arcu<RawBlock<T, RawBlockConcurrent>, GlobalEpochCounterPool>,
|
||||
growth_lock: RwLock<()>,
|
||||
offset_locks: RwLock<Vec<RwLock<()>>>,
|
||||
}
|
||||
@@ -201,40 +205,44 @@ impl OffsetTable<IndexPtr> for OffsetTableImpl<IndexPtr> {
|
||||
|
||||
impl<T: RawBlockTraits> SerialOffsetTable<T> {
|
||||
#[inline]
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
block: RawBlock::new(),
|
||||
}
|
||||
fn new() -> Result<Self, AllocError> {
|
||||
Ok(Self {
|
||||
block: RawBlock::new()?,
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn build_with(&mut self, value: T) -> usize {
|
||||
let mut ptr;
|
||||
unsafe {
|
||||
let mut ptr;
|
||||
|
||||
loop {
|
||||
ptr = self.block.alloc(size_of::<T>());
|
||||
loop {
|
||||
ptr = self.block.alloc(size_of::<T>());
|
||||
|
||||
if ptr.is_null() {
|
||||
let new_block = self.block.grow_new().unwrap();
|
||||
self.block = new_block;
|
||||
} else {
|
||||
break;
|
||||
if ptr.is_null() {
|
||||
let new_block = self.block.grow_new().unwrap();
|
||||
self.block = new_block;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ptr::write(ptr as *mut T, value);
|
||||
ptr.addr() - self.block.base.addr()
|
||||
ptr::write(ptr as *mut T, value);
|
||||
// SAFETY: `ptr` was obtained from `self.block.alloc()`
|
||||
self.block.get_offset(ptr)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn lookup(&self, offset: usize) -> &T {
|
||||
&*self.block.base.add(offset).cast::<T>()
|
||||
unsafe { &*self.block.get_unchecked(offset).cast::<T>() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn lookup_mut(&mut self, offset: usize) -> &mut T {
|
||||
&mut *self.block.base.add(offset).cast::<T>().cast_mut()
|
||||
unsafe { &mut *self.block.get_unchecked(offset).cast::<T>().cast_mut() }
|
||||
}
|
||||
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
fn to_concurrent(&mut self) -> ConcurrentOffsetTable<T>
|
||||
where
|
||||
T: fmt::Debug,
|
||||
@@ -244,8 +252,9 @@ impl<T: RawBlockTraits> SerialOffsetTable<T> {
|
||||
};
|
||||
|
||||
let serial_tbl = mem::replace(self, empty_serial_tbl);
|
||||
let num_tbl_entries = serial_tbl.block.size() / size_of::<T>();
|
||||
let block = Arcu::new(serial_tbl.block, GlobalEpochCounterPool);
|
||||
let num_tbl_entries = serial_tbl.block.used_bytes() / size_of::<T>();
|
||||
let raw_block: RawBlock<T, RawBlockConcurrent> = serial_tbl.block.into();
|
||||
let block = Arcu::new(raw_block, GlobalEpochCounterPool);
|
||||
|
||||
let offset_locks: Vec<RwLock<()>> = (0..num_tbl_entries).map(|_| RwLock::new(())).collect();
|
||||
|
||||
@@ -279,7 +288,7 @@ impl<T: RawBlockTraits> ConcurrentOffsetTable<T> {
|
||||
}
|
||||
}
|
||||
|
||||
let new_tbl_sz = block_epoch.size() / size_of::<T>();
|
||||
let new_tbl_sz = block_epoch.used_bytes() / size_of::<T>();
|
||||
let mut offset_locks = self.offset_locks.write();
|
||||
|
||||
offset_locks.resize_with(new_tbl_sz, || RwLock::new(()));
|
||||
@@ -288,7 +297,8 @@ impl<T: RawBlockTraits> ConcurrentOffsetTable<T> {
|
||||
ptr::write(ptr as *mut T, value);
|
||||
}
|
||||
|
||||
let value = ptr.addr() - block_epoch.base.addr();
|
||||
// SAFETY: `ptr` was obtained from `block_epoch.alloc()`
|
||||
let value = unsafe { block_epoch.get_offset(ptr) };
|
||||
|
||||
// AtomTable would have to update the index table at this point
|
||||
// explicit drop to ensure we don't accidentally drop it early
|
||||
@@ -303,7 +313,7 @@ impl<T: RawBlockTraits> ConcurrentOffsetTable<T> {
|
||||
let inner_offset_lock = outer_offset_lock[offset / size_of::<T>()].read();
|
||||
|
||||
let rcu_ref = RcuRef::try_map(self.block.read(), |raw_block| unsafe {
|
||||
raw_block.base.add(offset).cast::<T>().as_ref()
|
||||
raw_block.get_unchecked(offset).cast::<T>().as_ref()
|
||||
})
|
||||
.expect("offset valid");
|
||||
|
||||
@@ -322,8 +332,7 @@ impl<T: RawBlockTraits> ConcurrentOffsetTable<T> {
|
||||
|
||||
let rcu_ref = RcuRef::try_map(self.block.read(), |raw_block| unsafe {
|
||||
raw_block
|
||||
.base
|
||||
.add(offset)
|
||||
.get_unchecked(offset)
|
||||
.cast_mut()
|
||||
.cast::<UnsafeCell<T>>()
|
||||
.as_ref()
|
||||
@@ -374,11 +383,11 @@ pub enum F64Table {
|
||||
}
|
||||
|
||||
impl F64Table {
|
||||
pub fn new() -> Self {
|
||||
Self::Serial(SerialF64Table {
|
||||
pub fn new() -> Result<Self, AllocError> {
|
||||
Ok(Self::Serial(SerialF64Table {
|
||||
indirection_tbl: IndexMap::with_hasher(FxBuildHasher::new()),
|
||||
offset_tbl: SerialOffsetTable::new(),
|
||||
})
|
||||
offset_tbl: SerialOffsetTable::new()?,
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn build_with(&mut self, value: OrderedFloat<f64>) -> F64Offset {
|
||||
@@ -398,6 +407,8 @@ impl F64Table {
|
||||
// which breaks the invariant indirection_tbl is meant to enforce.
|
||||
// Since this branch is never invoked, it does no harm, but that
|
||||
// that will eventually change.
|
||||
//
|
||||
// Note: may be indirectly fixed by the use of the new RawBlockConcurrency trait.
|
||||
{
|
||||
let indirection_tbl = concurrent_tbl.indirection_tbl.lock();
|
||||
|
||||
@@ -468,7 +479,9 @@ impl F64Table {
|
||||
.unwrap();
|
||||
*self = Self::Serial(SerialF64Table {
|
||||
indirection_tbl: indirection_tbl.into_inner(),
|
||||
offset_tbl: SerialOffsetTable { block: raw_block },
|
||||
offset_tbl: SerialOffsetTable {
|
||||
block: raw_block.into(),
|
||||
},
|
||||
});
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
#![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work
|
||||
|
||||
use crate::arena::*;
|
||||
use crate::atom_table::*;
|
||||
use crate::offset_table::*;
|
||||
use crate::parser::char_reader::*;
|
||||
use crate::types::HeapCellValueTag;
|
||||
|
||||
use std::cell::{Cell, Ref, RefCell, RefMut};
|
||||
@@ -22,9 +19,9 @@ use dashu::Integer;
|
||||
use dashu::Rational;
|
||||
use fxhash::FxBuildHasher;
|
||||
use indexmap::IndexMap;
|
||||
use modular_bitfield::error::OutOfBounds;
|
||||
use modular_bitfield::prelude::*;
|
||||
use ordered_float::OrderedFloat;
|
||||
use scryer_modular_bitfield::error::OutOfBounds;
|
||||
use scryer_modular_bitfield::prelude::*;
|
||||
|
||||
pub type Specifier = u32;
|
||||
|
||||
@@ -249,6 +246,7 @@ pub enum GInteger {
|
||||
|
||||
impl GInteger {
|
||||
#[inline]
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
pub fn to_literal(self) -> Literal {
|
||||
match self {
|
||||
GInteger::Integer(integer) => Literal::Integer(integer),
|
||||
@@ -359,16 +357,12 @@ impl OpDesc {
|
||||
|
||||
#[inline]
|
||||
pub fn get_spec(self) -> OpDeclSpec {
|
||||
OpDeclSpec::try_from(self.spec()).expect("OpDecl always contains a valud OpDeclSpec")
|
||||
OpDeclSpec::try_from(self.spec()).expect("OpDecl always contains a valid OpDeclSpec")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn arity(self) -> usize {
|
||||
if !self.get_spec().is_infix() {
|
||||
1
|
||||
} else {
|
||||
2
|
||||
}
|
||||
if !self.get_spec().is_infix() { 1 } else { 2 }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -424,72 +418,92 @@ pub enum ArithmeticError {
|
||||
UninstantiatedVar,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Location {
|
||||
pub(super) line: usize,
|
||||
pub(super) column: usize,
|
||||
}
|
||||
|
||||
impl Location {
|
||||
// beginning of file
|
||||
pub(crate) const BOF: Self = Self { line: 0, column: 0 };
|
||||
|
||||
pub fn line(&self) -> usize {
|
||||
self.line
|
||||
}
|
||||
|
||||
pub fn column(&self) -> usize {
|
||||
self.column
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
pub enum ParserError {
|
||||
BackQuotedString(usize, usize),
|
||||
pub struct ParserError {
|
||||
pub(crate) location: Option<Location>,
|
||||
pub(crate) kind: ParserErrorKind,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
pub(crate) enum ParserErrorKind {
|
||||
BackQuotedString,
|
||||
IO(IOError),
|
||||
IncompleteReduction(usize, usize),
|
||||
InfiniteFloat(usize, usize),
|
||||
IncompleteReduction,
|
||||
InfiniteFloat,
|
||||
InvalidSingleQuotedCharacter(char),
|
||||
LexicalError(lexical::Error),
|
||||
MissingQuote(usize, usize),
|
||||
NonPrologChar(usize, usize),
|
||||
ParseBigInt(usize, usize),
|
||||
UnexpectedChar(char, usize, usize),
|
||||
ParseFloat,
|
||||
MissingQuote,
|
||||
NonPrologChar,
|
||||
ParseBigInt,
|
||||
UnexpectedChar(char),
|
||||
// UnexpectedEOF,
|
||||
Utf8Error(usize, usize),
|
||||
Utf8Error,
|
||||
}
|
||||
|
||||
impl ParserError {
|
||||
pub fn line_and_col_num(&self) -> Option<(usize, usize)> {
|
||||
match self {
|
||||
&ParserError::BackQuotedString(line_num, col_num)
|
||||
| &ParserError::IncompleteReduction(line_num, col_num)
|
||||
| &ParserError::InfiniteFloat(line_num, col_num)
|
||||
| &ParserError::MissingQuote(line_num, col_num)
|
||||
| &ParserError::NonPrologChar(line_num, col_num)
|
||||
| &ParserError::ParseBigInt(line_num, col_num)
|
||||
| &ParserError::UnexpectedChar(_, line_num, col_num)
|
||||
| &ParserError::Utf8Error(line_num, col_num) => Some((line_num, col_num)),
|
||||
_ => None,
|
||||
}
|
||||
pub(crate) fn location(&self) -> Option<Location> {
|
||||
self.location.as_ref().cloned()
|
||||
}
|
||||
|
||||
pub fn as_atom(&self) -> Atom {
|
||||
match self {
|
||||
ParserError::BackQuotedString(..) => atom!("back_quoted_string"),
|
||||
ParserError::IncompleteReduction(..) => atom!("incomplete_reduction"),
|
||||
ParserError::InvalidSingleQuotedCharacter(..) => {
|
||||
pub(crate) fn as_atom(&self) -> Atom {
|
||||
match &self.kind {
|
||||
ParserErrorKind::BackQuotedString => atom!("back_quoted_string"),
|
||||
ParserErrorKind::IncompleteReduction => atom!("incomplete_reduction"),
|
||||
ParserErrorKind::InvalidSingleQuotedCharacter(..) => {
|
||||
atom!("invalid_single_quoted_character")
|
||||
}
|
||||
ParserError::InfiniteFloat(..) => {
|
||||
ParserErrorKind::InfiniteFloat => {
|
||||
atom!("infinite_float")
|
||||
}
|
||||
ParserError::IO(e) if e.kind() == ErrorKind::UnexpectedEof => {
|
||||
ParserErrorKind::IO(e) if e.kind() == ErrorKind::UnexpectedEof => {
|
||||
atom!("unexpected_end_of_file")
|
||||
}
|
||||
ParserError::IO(e) if e.kind() == ErrorKind::InvalidData => {
|
||||
ParserErrorKind::IO(e) if e.kind() == ErrorKind::InvalidData => {
|
||||
atom!("invalid_data")
|
||||
}
|
||||
ParserError::IO(_) => atom!("input_output_error"),
|
||||
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::Utf8Error(..) => atom!("utf8_conversion_error"),
|
||||
ParserErrorKind::IO(_) => atom!("input_output_error"),
|
||||
ParserErrorKind::ParseFloat => atom!("parse_float"),
|
||||
ParserErrorKind::MissingQuote => atom!("missing_quote"),
|
||||
ParserErrorKind::NonPrologChar => atom!("non_prolog_character"),
|
||||
ParserErrorKind::ParseBigInt => atom!("cannot_parse_big_int"),
|
||||
ParserErrorKind::UnexpectedChar(..) => atom!("unexpected_char"),
|
||||
ParserErrorKind::Utf8Error => atom!("utf8_conversion_error"),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn unexpected_eof() -> Self {
|
||||
ParserError::IO(std::io::Error::from(ErrorKind::UnexpectedEof))
|
||||
pub(crate) fn unexpected_eof() -> Self {
|
||||
ParserError {
|
||||
location: None,
|
||||
kind: ParserErrorKind::IO(std::io::Error::from(ErrorKind::UnexpectedEof)),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_unexpected_eof(&self) -> bool {
|
||||
if let ParserError::IO(e) = self {
|
||||
pub(crate) fn is_unexpected_eof(&self) -> bool {
|
||||
if let ParserErrorKind::IO(e) = &self.kind {
|
||||
e.kind() == ErrorKind::UnexpectedEof
|
||||
} else {
|
||||
false
|
||||
@@ -497,24 +511,11 @@ impl ParserError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<lexical::Error> for ParserError {
|
||||
fn from(e: lexical::Error) -> ParserError {
|
||||
ParserError::LexicalError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IOError> for ParserError {
|
||||
fn from(e: IOError) -> ParserError {
|
||||
ParserError::IO(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&IOError> for ParserError {
|
||||
fn from(error: &IOError) -> ParserError {
|
||||
if error.get_ref().filter(|e| e.is::<BadUtf8Error>()).is_some() {
|
||||
ParserError::Utf8Error(0, 0)
|
||||
} else {
|
||||
ParserError::IO(error.kind().into())
|
||||
ParserError {
|
||||
location: None,
|
||||
kind: ParserErrorKind::IO(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -603,6 +604,7 @@ mod private {
|
||||
impl<T: FitsInFixnumSeal> MightNotFitInFixnumSeal for T {}
|
||||
impl MightNotFitInFixnumSeal for i64 {}
|
||||
impl MightNotFitInFixnumSeal for u64 {}
|
||||
impl MightNotFitInFixnumSeal for u128 {}
|
||||
impl MightNotFitInFixnumSeal for &Integer {}
|
||||
impl MightNotFitInFixnumSeal for Integer {}
|
||||
impl MightNotFitInFixnumSeal for usize {}
|
||||
@@ -669,7 +671,7 @@ impl Fixnum {
|
||||
|
||||
#[inline]
|
||||
pub fn get_tag(&self) -> HeapCellValueTag {
|
||||
use scryer_modular_bitfield::Specifier;
|
||||
use modular_bitfield::Specifier;
|
||||
HeapCellValueTag::from_bytes(self.tag()).unwrap()
|
||||
}
|
||||
|
||||
@@ -871,7 +873,7 @@ pub enum Term {
|
||||
impl Term {
|
||||
pub fn first_arg(&self) -> Option<&Term> {
|
||||
match self {
|
||||
Term::Clause(_, _, ref terms) => terms.first(),
|
||||
Term::Clause(_, _, terms) => terms.first(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -886,14 +888,14 @@ impl Term {
|
||||
|
||||
pub fn arity(&self) -> usize {
|
||||
match self {
|
||||
Term::Clause(_, _, ref child_terms, ..) => child_terms.len(),
|
||||
Term::Clause(_, _, child_terms, ..) => child_terms.len(),
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) 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 &mut Term::Clause(_, ref name, ref mut subterms) = term {
|
||||
if let Some(Term::Literal(_, Literal::CodeIndexOffset(_))) = subterms.last() {
|
||||
subterms.pop();
|
||||
}
|
||||
|
||||
@@ -102,19 +102,27 @@ impl<R> CharReader<R> {
|
||||
}
|
||||
|
||||
impl<R: Read> CharReader<R> {
|
||||
pub fn read_chunk(&mut self) -> io::Result<usize> {
|
||||
let mut chunk = [0u8; 8 * 1024];
|
||||
let nread = self.inner.read(&mut chunk)?;
|
||||
self.buf.extend_from_slice(&chunk[..nread]);
|
||||
Ok(nread)
|
||||
}
|
||||
|
||||
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() {
|
||||
self.buf.clear();
|
||||
// make some space in buf
|
||||
if self.buf.len() > 4 {
|
||||
// keep 4 bytes so that put_back_char can put back at least one char
|
||||
self.buf.drain(4..);
|
||||
}
|
||||
self.pos = self.buf.len();
|
||||
|
||||
let mut word = [0u8; std::mem::size_of::<char>()];
|
||||
let nread = self.inner.read(&mut word)?;
|
||||
|
||||
self.buf.extend_from_slice(&word[..nread]);
|
||||
self.pos = 0;
|
||||
self.read_chunk()?;
|
||||
}
|
||||
|
||||
Ok(&self.buf[self.pos..])
|
||||
@@ -135,7 +143,7 @@ impl<R: Read> CharRead for CharReader<R> {
|
||||
Err(e) => return Some(Err(e)),
|
||||
}
|
||||
|
||||
let bad_bytes_error = |buf: &[u8]| {
|
||||
fn bad_bytes_error(buf: &[u8]) -> std::io::Error {
|
||||
// If we have 4 bytes that still don't make up
|
||||
// a valid code point, then we have garbage.
|
||||
|
||||
@@ -143,110 +151,95 @@ impl<R: Read> CharRead for CharReader<R> {
|
||||
// leading bytes until either the buffer is
|
||||
// empty, or we have a valid code point.
|
||||
|
||||
let mut split_point = 1;
|
||||
let mut badbytes = vec![];
|
||||
// note we might have a sequence of invalid bytes followed by valid bytes followed by invalid bytes
|
||||
|
||||
loop {
|
||||
let (bad, rest) = buf.split_at(split_point);
|
||||
let err = str::from_utf8(buf).expect_err("the start of buf should be invalid utf-8");
|
||||
assert_eq!(err.valid_up_to(), 0, "the error should be a prefix");
|
||||
|
||||
if rest.is_empty() || str::from_utf8(rest).is_ok() {
|
||||
badbytes.extend_from_slice(bad);
|
||||
break;
|
||||
}
|
||||
let invalid_prefix = err.error_len().expect("we should have at least 4 bytes");
|
||||
|
||||
split_point += 1;
|
||||
}
|
||||
let bad_bytes = buf[..invalid_prefix].to_vec();
|
||||
|
||||
// Raise the error. If we still have data in
|
||||
// the buffer, it will be returned on the next
|
||||
// loop.
|
||||
io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
BadUtf8Error { bytes: bad_bytes },
|
||||
)
|
||||
}
|
||||
|
||||
io::Error::new(io::ErrorKind::InvalidData, BadUtf8Error { bytes: badbytes })
|
||||
};
|
||||
|
||||
loop {
|
||||
// while we haven't consumed all bytes from the buffer
|
||||
while self.pos < self.buf.len() {
|
||||
// buf must be non-empty
|
||||
let buf = &self.buf[self.pos..];
|
||||
|
||||
if !buf.is_empty() {
|
||||
let e = match str::from_utf8(buf) {
|
||||
Ok(s) => {
|
||||
let mut chars = s.chars();
|
||||
let c = chars.next().unwrap();
|
||||
// we need at most 4 bytes for a char so don't decode the whole buffer
|
||||
// as it can be quite large and we are going to discard the remaining chars anyway
|
||||
// if there is a valid prefix
|
||||
let prefix = if buf.len() > 4 { &buf[..4] } else { buf };
|
||||
|
||||
return Some(Ok(c));
|
||||
}
|
||||
Err(e) => e,
|
||||
};
|
||||
let e = match str::from_utf8(prefix) {
|
||||
Ok(s) => {
|
||||
let mut chars = s.chars();
|
||||
let c = chars.next().expect(
|
||||
"a non-empty buffer that is valid utf-8 contains at least one character",
|
||||
);
|
||||
|
||||
if buf.len() - e.valid_up_to() >= 4 {
|
||||
return Some(Err(bad_bytes_error(buf)));
|
||||
} else if self.pos >= self.buf.len() {
|
||||
return None;
|
||||
} else if self.buf.len() - self.pos >= 4 && self.pos < e.valid_up_to() {
|
||||
return match str::from_utf8(&self.buf[self.pos..self.pos + e.valid_up_to()]) {
|
||||
Ok(s) => {
|
||||
let mut chars = s.chars();
|
||||
let c = chars.next().unwrap();
|
||||
|
||||
Some(Ok(c))
|
||||
}
|
||||
Err(e) => {
|
||||
let badbytes = self.buf[self.pos..self.pos + e.valid_up_to()].to_vec();
|
||||
|
||||
Some(Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
BadUtf8Error { bytes: badbytes },
|
||||
)))
|
||||
}
|
||||
};
|
||||
} else {
|
||||
let buf_len = self.buf.len();
|
||||
|
||||
for (c, idx) in (self.pos..buf_len).enumerate() {
|
||||
self.buf[c] = self.buf[idx];
|
||||
}
|
||||
|
||||
self.buf.truncate(buf_len - self.pos);
|
||||
|
||||
let buf_len = self.buf.len();
|
||||
self.pos = 0;
|
||||
|
||||
if buf_len >= 4 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut word = [0u8; 4];
|
||||
let word_slice = &mut word[buf_len..4];
|
||||
|
||||
match self.inner.read(word_slice) {
|
||||
Err(e) => return Some(Err(e)),
|
||||
Ok(0) => return Some(Err(bad_bytes_error(&self.buf))),
|
||||
Ok(nread) => {
|
||||
self.buf.extend_from_slice(&word_slice[0..nread]);
|
||||
}
|
||||
}
|
||||
return Some(Ok(c));
|
||||
}
|
||||
Err(e) => e,
|
||||
};
|
||||
|
||||
if e.valid_up_to() != 0 {
|
||||
// the valid prefix is non-empty so it is guaranteed that we can decode at least one char
|
||||
let c = str::from_utf8(&prefix[..e.valid_up_to()])
|
||||
.expect("prefix is verified valid up to this point")
|
||||
.chars()
|
||||
.next()
|
||||
.expect("the valid prefix was non-empty");
|
||||
return Some(Ok(c));
|
||||
}
|
||||
|
||||
if e.error_len().is_some() {
|
||||
return Some(Err(bad_bytes_error(buf)));
|
||||
}
|
||||
|
||||
// buf is too short to determine if the remaining bytes in buf are a valid char
|
||||
// i.e. the content of buf is a prefix of a valid utf-8 encoded char
|
||||
//
|
||||
// we need to read more data from the underlying stream
|
||||
// so that we can determine its validity
|
||||
|
||||
if self.buf.len() > 4 {
|
||||
// keep a prefix of 4 bytes so that we can put back at least one char
|
||||
self.buf.drain(4..self.pos);
|
||||
self.pos = 4;
|
||||
}
|
||||
|
||||
match self.read_chunk() {
|
||||
Err(e) => return Some(Err(e)),
|
||||
Ok(0) => return Some(Err(bad_bytes_error(&self.buf))),
|
||||
Ok(_) => {
|
||||
// successfully filled the buffer with another chunk of data
|
||||
}
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn put_back_char(&mut self, c: char) {
|
||||
let src_len = self.buf.len() - self.pos;
|
||||
debug_assert!(src_len <= self.buf.capacity());
|
||||
|
||||
let c_len = c.len_utf8();
|
||||
let mut shifted_slice = [0u8; 32];
|
||||
if c_len <= self.pos {
|
||||
self.pos -= c_len;
|
||||
} else {
|
||||
self.buf.insert_from_slice(
|
||||
0,
|
||||
&[0u8; 4/* char::MAX_LEN_UTF8 once msrv reached 1.93 */][..c_len - self.pos],
|
||||
);
|
||||
self.pos = 0;
|
||||
}
|
||||
|
||||
shifted_slice[0..src_len].copy_from_slice(&self.buf[self.pos..self.buf.len()]);
|
||||
|
||||
self.buf.resize(c_len, 0);
|
||||
self.buf.extend_from_slice(&shifted_slice[0..src_len]);
|
||||
self.pos = 0;
|
||||
|
||||
c.encode_utf8(&mut self.buf[0..c_len]);
|
||||
c.encode_utf8(&mut self.buf[self.pos..]);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -381,6 +374,51 @@ mod tests {
|
||||
assert!(read_string.read_char().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interspersed_bad_utf8() {
|
||||
let mut read_string = CharReader::new(Cursor::new(b"a string\xffmore_text\xff"));
|
||||
|
||||
for c in "a string".chars() {
|
||||
assert_eq!(read_string.peek_char().unwrap().ok(), Some(c));
|
||||
assert_eq!(read_string.read_char().unwrap().ok(), Some(c));
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
read_string.peek_char().unwrap().unwrap_err().kind(),
|
||||
std::io::ErrorKind::InvalidData
|
||||
);
|
||||
|
||||
let err = read_string
|
||||
.read_char()
|
||||
.unwrap()
|
||||
.unwrap_err()
|
||||
.downcast::<BadUtf8Error>()
|
||||
.unwrap();
|
||||
|
||||
read_string.consume(err.bytes.len());
|
||||
|
||||
for c in "more_text".chars() {
|
||||
assert_eq!(read_string.peek_char().unwrap().ok(), Some(c));
|
||||
assert_eq!(read_string.read_char().unwrap().ok(), Some(c));
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
read_string.peek_char().unwrap().unwrap_err().kind(),
|
||||
std::io::ErrorKind::InvalidData
|
||||
);
|
||||
|
||||
let err = read_string
|
||||
.read_char()
|
||||
.unwrap()
|
||||
.unwrap_err()
|
||||
.downcast::<BadUtf8Error>()
|
||||
.unwrap();
|
||||
|
||||
read_string.consume(err.bytes.len());
|
||||
|
||||
assert!(read_string.read_char().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn greek_string() {
|
||||
let mut read_string = CharReader::new(Cursor::new("λέξη"));
|
||||
@@ -406,7 +444,6 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "slow and not very relevant")]
|
||||
fn greek_lorem_ipsum() {
|
||||
let lorem_ipsum = "Λορεμ ιπσθμ δολορ σιτ αμετ, οφφενδιτ
|
||||
εφφιcιενδι σιτ ει, ηαρθμ λεγερε qθαερενδθμ ιθσ νε. Ηασ νο εροσ
|
||||
@@ -478,7 +515,6 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "slow and not very relevant")]
|
||||
fn armenian_lorem_ipsum() {
|
||||
let lorem_ipsum = "լոռեմ իպսում դոլոռ սիթ ամեթ, նովում գռաեծո
|
||||
սեա եա, աբհոռռեանթ դիսպութանդո եի քուի. իդ քուոդ ինդոծթում
|
||||
@@ -552,7 +588,6 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "slow and not very relevant")]
|
||||
fn russian_lorem_ipsum() {
|
||||
let lorem_ipsum = "Лорем ипсум долор сит амет, атяуи дицам еи
|
||||
сит, ид сеа фацилис елаборарет. Меа еу яуас алияуид, те яуи
|
||||
|
||||
@@ -16,7 +16,10 @@ macro_rules! consume_chars_with {
|
||||
match $e {
|
||||
Ok(Some(c)) => $token.push(c),
|
||||
Ok(None) => continue,
|
||||
Err($crate::parser::ast::ParserError::UnexpectedChar(..)) => break,
|
||||
Err($crate::parser::ast::ParserError {
|
||||
kind: $crate::parser::ast::ParserErrorKind::UnexpectedChar(..),
|
||||
..
|
||||
}) => break,
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
@@ -82,7 +85,7 @@ macro_rules! try_nt {
|
||||
Ok(NumberToken::Partial($token))
|
||||
} else {
|
||||
Err(e)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}};
|
||||
@@ -91,27 +94,45 @@ macro_rules! try_nt {
|
||||
pub(crate) struct Lexer<'a, R> {
|
||||
pub(crate) reader: R,
|
||||
pub(crate) machine_st: &'a mut MachineState,
|
||||
pub(crate) line_num: usize,
|
||||
pub(crate) col_num: usize,
|
||||
pub(crate) location: Location,
|
||||
}
|
||||
|
||||
impl<'a, R: fmt::Debug> fmt::Debug for Lexer<'a, R> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("LexerParser")
|
||||
.field("reader", &"&'a mut R") // Hacky solution.
|
||||
.field("line_num", &self.line_num)
|
||||
.field("col_num", &self.col_num)
|
||||
.field("location", &self.location)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> Lexer<'_, R> {
|
||||
pub(crate) fn located_error(&self, kind: ParserErrorKind) -> ParserError {
|
||||
ParserError {
|
||||
location: Some(self.location.clone()),
|
||||
kind,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn parse_big_int_error(&self) -> ParserError {
|
||||
self.located_error(ParserErrorKind::ParseBigInt)
|
||||
}
|
||||
|
||||
pub(crate) fn incomplete_reduction(&self) -> ParserError {
|
||||
self.located_error(ParserErrorKind::IncompleteReduction)
|
||||
}
|
||||
|
||||
pub(crate) fn unexpected_char(&self, c: char) -> ParserError {
|
||||
self.located_error(ParserErrorKind::UnexpectedChar(c))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
pub fn new(src: R, machine_st: &'a mut MachineState) -> Self {
|
||||
Self {
|
||||
reader: src,
|
||||
machine_st,
|
||||
line_num: 0,
|
||||
col_num: 0,
|
||||
location: Location::BOF,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,10 +159,10 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
self.reader.consume(c.len_utf8());
|
||||
|
||||
if new_line_char!(c) {
|
||||
self.line_num += 1;
|
||||
self.col_num = 0;
|
||||
self.location.line += 1;
|
||||
self.location.column = 0;
|
||||
} else {
|
||||
self.col_num += 1;
|
||||
self.location.column += 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,10 +221,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
|
||||
match comment_loop() {
|
||||
Err(e) if e.is_unexpected_eof() => {
|
||||
return Err(ParserError::IncompleteReduction(
|
||||
self.line_num,
|
||||
self.col_num,
|
||||
));
|
||||
return Err(self.incomplete_reduction());
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(e);
|
||||
@@ -215,7 +233,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
self.skip_char(c);
|
||||
Ok(true)
|
||||
} else {
|
||||
Err(ParserError::NonPrologChar(self.line_num, self.col_num))
|
||||
Err(self.located_error(ParserErrorKind::NonPrologChar))
|
||||
}
|
||||
} else {
|
||||
self.return_char('/');
|
||||
@@ -232,7 +250,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
|
||||
if !back_quote_char!(c2) {
|
||||
self.return_char(c);
|
||||
Err(ParserError::UnexpectedChar(c, self.line_num, self.col_num))
|
||||
Err(self.unexpected_char(c))
|
||||
} else {
|
||||
self.skip_char(c2);
|
||||
Ok(c2)
|
||||
@@ -257,7 +275,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
Ok(None)
|
||||
} else {
|
||||
self.return_char(c);
|
||||
Err(ParserError::UnexpectedChar(c, self.line_num, self.col_num))
|
||||
Err(self.unexpected_char(c))
|
||||
}
|
||||
} else {
|
||||
self.get_back_quoted_char().map(Some)
|
||||
@@ -279,10 +297,13 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
self.skip_char(c);
|
||||
Ok(token)
|
||||
} else {
|
||||
Err(ParserError::MissingQuote(self.line_num, self.col_num))
|
||||
Err({
|
||||
let this = &self;
|
||||
this.located_error(ParserErrorKind::MissingQuote)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
Err(ParserError::UnexpectedChar(c, self.line_num, self.col_num))
|
||||
Err(self.unexpected_char(c))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -313,7 +334,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
|
||||
if !single_quote_char!(c2) {
|
||||
self.return_char(c);
|
||||
Err(ParserError::UnexpectedChar(c, self.line_num, self.col_num))
|
||||
Err(self.unexpected_char(c))
|
||||
} else {
|
||||
self.skip_char(c2);
|
||||
Ok(c2)
|
||||
@@ -354,7 +375,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
|
||||
if !double_quote_char!(c2) {
|
||||
self.return_char(c);
|
||||
Err(ParserError::UnexpectedChar(c, self.line_num, self.col_num))
|
||||
Err(self.unexpected_char(c))
|
||||
} else {
|
||||
self.skip_char(c2);
|
||||
Ok(c2)
|
||||
@@ -378,7 +399,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
't' => '\t',
|
||||
'n' => '\n',
|
||||
'r' => '\r',
|
||||
c => return Err(ParserError::UnexpectedChar(c, self.line_num, self.col_num)),
|
||||
c => return Err(self.unexpected_char(c)),
|
||||
};
|
||||
|
||||
self.skip_char(c);
|
||||
@@ -396,10 +417,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
if hexadecimal_digit_char!(c) {
|
||||
self.escape_sequence_to_char(|c| hexadecimal_digit_char!(c), 16)
|
||||
} else {
|
||||
Err(ParserError::IncompleteReduction(
|
||||
self.line_num,
|
||||
self.col_num,
|
||||
))
|
||||
Err(self.incomplete_reduction())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -425,17 +443,11 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
if backslash_char!(c) {
|
||||
self.skip_char(c);
|
||||
u32::from_str_radix(&token, radix).map_or_else(
|
||||
|_| Err(ParserError::ParseBigInt(self.line_num, self.col_num)),
|
||||
|n| {
|
||||
char::try_from(n)
|
||||
.map_err(|_| ParserError::Utf8Error(self.line_num, self.col_num))
|
||||
},
|
||||
|_| Err(self.parse_big_int_error()),
|
||||
|n| char::try_from(n).map_err(|_| self.located_error(ParserErrorKind::Utf8Error)),
|
||||
)
|
||||
} else {
|
||||
Err(ParserError::IncompleteReduction(
|
||||
self.line_num,
|
||||
self.col_num,
|
||||
))
|
||||
Err(self.incomplete_reduction())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -447,7 +459,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
Ok(c)
|
||||
} else {
|
||||
if !backslash_char!(c) {
|
||||
return Err(ParserError::UnexpectedChar(c, self.line_num, self.col_num));
|
||||
return Err(self.unexpected_char(c));
|
||||
}
|
||||
|
||||
self.skip_char(c);
|
||||
@@ -478,7 +490,10 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
self.skip_char(c);
|
||||
Ok(token)
|
||||
} else {
|
||||
Err(ParserError::MissingQuote(self.line_num, self.col_num))
|
||||
Err({
|
||||
let this = &self;
|
||||
this.located_error(ParserErrorKind::MissingQuote)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -509,7 +524,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
.map(NumberToken::Integer)
|
||||
} else {
|
||||
self.return_char(start);
|
||||
Err(ParserError::ParseBigInt(self.line_num, self.col_num))
|
||||
Err(self.parse_big_int_error())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -540,7 +555,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
.map(NumberToken::Integer)
|
||||
} else {
|
||||
self.return_char(start);
|
||||
Err(ParserError::ParseBigInt(self.line_num, self.col_num))
|
||||
Err(self.parse_big_int_error())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -571,7 +586,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
.map(NumberToken::Integer)
|
||||
} else {
|
||||
self.return_char(start);
|
||||
Err(ParserError::ParseBigInt(self.line_num, self.col_num))
|
||||
Err(self.parse_big_int_error())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -644,11 +659,11 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err(ParserError::InvalidSingleQuotedCharacter(c));
|
||||
return Err(self.located_error(ParserErrorKind::InvalidSingleQuotedCharacter(c)));
|
||||
}
|
||||
} else {
|
||||
match self.get_back_quoted_string() {
|
||||
Ok(_) => return Err(ParserError::BackQuotedString(self.line_num, self.col_num)),
|
||||
Ok(_) => return Err(self.located_error(ParserErrorKind::BackQuotedString)),
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
@@ -669,7 +684,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
) -> Result<(F64Offset, OrderedFloat<f64>), ParserError> {
|
||||
self.return_char(token.pop().unwrap());
|
||||
|
||||
let n = parse_float_lossy(&token)?;
|
||||
let n = self.parse_float_lossy(&token)?;
|
||||
let offset = float_alloc!(n, self.machine_st.arena);
|
||||
|
||||
Ok((offset, OrderedFloat(n)))
|
||||
@@ -686,7 +701,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
if decimal_digit_char!(c) {
|
||||
Ok(c)
|
||||
} else {
|
||||
Err(ParserError::ParseBigInt(self.line_num, self.col_num))
|
||||
Err(self.parse_big_int_error())
|
||||
}
|
||||
} else {
|
||||
Ok(c)
|
||||
@@ -708,7 +723,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
.or_else(|_| {
|
||||
Integer::from_str_radix(token, radix)
|
||||
.map(|n| GInteger::Integer(arena_alloc!(n, &mut self.machine_st.arena)))
|
||||
.map_err(|_| ParserError::ParseBigInt(self.line_num, self.col_num))
|
||||
.map_err(|_| self.parse_big_int_error())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -756,7 +771,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
Err(_) => {
|
||||
return self
|
||||
.vacate_with_float(token)
|
||||
.map(|(offset, fl)| NumberToken::Float(offset, fl))
|
||||
.map(|(offset, fl)| NumberToken::Float(offset, fl));
|
||||
}
|
||||
Ok(c) => c,
|
||||
};
|
||||
@@ -806,7 +821,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
}
|
||||
}
|
||||
|
||||
let n = parse_float_lossy(&token)?;
|
||||
let n = self.parse_float_lossy(&token)?;
|
||||
let offset = float_alloc!(n, self.machine_st.arena);
|
||||
|
||||
Ok(NumberToken::Float(offset, OrderedFloat(n)))
|
||||
@@ -815,7 +830,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
.map(|(offset, fl)| NumberToken::Float(offset, fl))
|
||||
}
|
||||
} else {
|
||||
let n = parse_float_lossy(&token)?;
|
||||
let n = self.parse_float_lossy(&token)?;
|
||||
let offset = float_alloc!(n, self.machine_st.arena);
|
||||
Ok(NumberToken::Float(offset, OrderedFloat(n)))
|
||||
}
|
||||
@@ -826,7 +841,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
} else if token.starts_with('0') && token.len() == 1 {
|
||||
if c == 'x' {
|
||||
self.hexadecimal_constant(c).or_else(|e| {
|
||||
if let ParserError::ParseBigInt(..) = e {
|
||||
if let ParserErrorKind::ParseBigInt = e.kind {
|
||||
self.parse_integer(&token).map(NumberToken::Integer)
|
||||
} else {
|
||||
Err(e)
|
||||
@@ -834,7 +849,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
})
|
||||
} else if c == 'o' {
|
||||
self.octal_constant(c).or_else(|e| {
|
||||
if let ParserError::ParseBigInt(..) = e {
|
||||
if let ParserErrorKind::ParseBigInt = e.kind {
|
||||
self.parse_integer(&token).map(NumberToken::Integer)
|
||||
} else {
|
||||
Err(e)
|
||||
@@ -842,7 +857,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
})
|
||||
} else if c == 'b' {
|
||||
self.binary_constant(c).or_else(|e| {
|
||||
if let ParserError::ParseBigInt(..) = e {
|
||||
if let ParserErrorKind::ParseBigInt = e.kind {
|
||||
self.parse_integer(&token).map(NumberToken::Integer)
|
||||
} else {
|
||||
Err(e)
|
||||
@@ -871,9 +886,9 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
self.get_single_quoted_char()
|
||||
.map(|c| NumberToken::Integer(GInteger::Fixnum(Fixnum::build_with(c))))
|
||||
.or_else(|err| {
|
||||
match err {
|
||||
ParserError::UnexpectedChar('\'', ..) => {}
|
||||
err => return Err(err),
|
||||
match &err.kind {
|
||||
ParserErrorKind::UnexpectedChar('\'', ..) => {}
|
||||
_ => return Err(err),
|
||||
}
|
||||
|
||||
self.return_char(c);
|
||||
@@ -954,7 +969,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
Ok(NumberToken::Partial(token_string)) => match self.parse_integer(&token_string) {
|
||||
Ok(n) => Ok(Token::Literal(n.to_literal())),
|
||||
Err(_) => {
|
||||
let n = parse_float_lossy(&token_string)?;
|
||||
let n = self.parse_float_lossy(&token_string)?;
|
||||
let offset = float_alloc!(n, self.machine_st.arena);
|
||||
Ok(Token::Literal(Literal::F64(offset, OrderedFloat(n))))
|
||||
}
|
||||
@@ -1068,14 +1083,17 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_float_lossy(token: &str) -> Result<f64, ParserError> {
|
||||
const FORMAT: u128 = lexical::format::STANDARD;
|
||||
let options = lexical::ParseFloatOptions::builder()
|
||||
.lossy(true)
|
||||
.build()
|
||||
.unwrap();
|
||||
let n = lexical::parse_with_options::<f64, _, FORMAT>(token.as_bytes(), &options)?;
|
||||
Ok(n)
|
||||
fn parse_float_lossy(&self, token: &str) -> Result<f64, ParserError> {
|
||||
const FORMAT: u128 = lexical::format::STANDARD;
|
||||
let Ok(options) = lexical::ParseFloatOptions::builder().lossy(true).build() else {
|
||||
return Err(self.located_error(ParserErrorKind::ParseFloat));
|
||||
};
|
||||
|
||||
let Ok(n) = lexical::parse_with_options::<f64, _, FORMAT>(token.as_bytes(), &options)
|
||||
else {
|
||||
return Err(self.located_error(ParserErrorKind::ParseFloat));
|
||||
};
|
||||
Ok(n)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,10 +254,7 @@ pub fn read_tokens<R: CharRead>(lexer: &mut Lexer<'_, R>) -> Result<Vec<Token>,
|
||||
}
|
||||
}
|
||||
Err(e) if e.is_unexpected_eof() && !tokens.is_empty() => {
|
||||
return Err(ParserError::IncompleteReduction(
|
||||
lexer.line_num,
|
||||
lexer.col_num,
|
||||
));
|
||||
return Err(lexer.incomplete_reduction());
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(e);
|
||||
@@ -700,12 +697,7 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
} else {
|
||||
let term = match self.terms.pop() {
|
||||
Some(term) => term,
|
||||
_ => {
|
||||
return Err(ParserError::IncompleteReduction(
|
||||
self.lexer.line_num,
|
||||
self.lexer.col_num,
|
||||
))
|
||||
}
|
||||
_ => return Err(self.lexer.incomplete_reduction()),
|
||||
};
|
||||
|
||||
if self.stack[idx].priority > 1000 {
|
||||
@@ -718,10 +710,7 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
};
|
||||
|
||||
if arity > self.terms.len() {
|
||||
return Err(ParserError::IncompleteReduction(
|
||||
self.lexer.line_num,
|
||||
self.lexer.col_num,
|
||||
));
|
||||
return Err(self.lexer.incomplete_reduction());
|
||||
}
|
||||
|
||||
let idx = self.terms.len() - arity;
|
||||
@@ -789,12 +778,7 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
|
||||
let term = match self.terms.pop() {
|
||||
Some(term) => term,
|
||||
_ => {
|
||||
return Err(ParserError::IncompleteReduction(
|
||||
self.lexer.line_num,
|
||||
self.lexer.col_num,
|
||||
))
|
||||
}
|
||||
_ => return Err(self.lexer.incomplete_reduction()),
|
||||
};
|
||||
|
||||
self.terms
|
||||
@@ -972,10 +956,7 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
self.negate_number(n, negate_rat_rc, Literal::Rational)
|
||||
}
|
||||
Token::Literal(Literal::F64(_offset, n)) if n.is_infinite() => {
|
||||
return Err(ParserError::InfiniteFloat(
|
||||
self.lexer.line_num,
|
||||
self.lexer.col_num,
|
||||
));
|
||||
return Err(self.lexer.located_error(ParserErrorKind::InfiniteFloat));
|
||||
}
|
||||
Token::Literal(Literal::F64(offset, n)) => {
|
||||
self.negate_number((offset, n), negate_f64, |(offset, n)| {
|
||||
@@ -997,28 +978,19 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
Token::OpenCT => self.shift(Token::OpenCT, 1300, DELIMITER),
|
||||
Token::Close => {
|
||||
if !self.reduce_term() && !self.reduce_brackets() {
|
||||
return Err(ParserError::IncompleteReduction(
|
||||
self.lexer.line_num,
|
||||
self.lexer.col_num,
|
||||
));
|
||||
return Err(self.lexer.incomplete_reduction());
|
||||
}
|
||||
}
|
||||
Token::OpenList => self.shift(Token::OpenList, 1300, DELIMITER),
|
||||
Token::CloseList => {
|
||||
if !self.reduce_list()? {
|
||||
return Err(ParserError::IncompleteReduction(
|
||||
self.lexer.line_num,
|
||||
self.lexer.col_num,
|
||||
));
|
||||
return Err(self.lexer.incomplete_reduction());
|
||||
}
|
||||
}
|
||||
Token::OpenCurly => self.shift(Token::OpenCurly, 1300, DELIMITER),
|
||||
Token::CloseCurly => {
|
||||
if !self.reduce_curly()? {
|
||||
return Err(ParserError::IncompleteReduction(
|
||||
self.lexer.line_num,
|
||||
self.lexer.col_num,
|
||||
));
|
||||
return Err(self.lexer.incomplete_reduction());
|
||||
}
|
||||
}
|
||||
Token::HeadTailSeparator => {
|
||||
@@ -1051,12 +1023,7 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
| Some(TokenType::OpenList)
|
||||
| Some(TokenType::OpenCurly)
|
||||
| Some(TokenType::HeadTailSeparator)
|
||||
| Some(TokenType::Comma) => {
|
||||
return Err(ParserError::IncompleteReduction(
|
||||
self.lexer.line_num,
|
||||
self.lexer.col_num,
|
||||
))
|
||||
}
|
||||
| Some(TokenType::Comma) => return Err(self.lexer.incomplete_reduction()),
|
||||
_ => {}
|
||||
},
|
||||
}
|
||||
@@ -1066,12 +1033,12 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
|
||||
#[inline]
|
||||
pub fn add_lines_read(&mut self, lines_read: usize) {
|
||||
self.lexer.line_num += lines_read;
|
||||
self.lexer.location.line += lines_read;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn lines_read(&self) -> usize {
|
||||
self.lexer.line_num
|
||||
self.lexer.location.line
|
||||
}
|
||||
|
||||
// on success, returns the parsed term and the number of lines read.
|
||||
@@ -1092,10 +1059,7 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
self.reduce_op(1400);
|
||||
|
||||
if self.terms.len() > 1 || self.stack.len() > 1 {
|
||||
return Err(ParserError::IncompleteReduction(
|
||||
self.lexer.line_num,
|
||||
self.lexer.col_num,
|
||||
));
|
||||
return Err(self.lexer.incomplete_reduction());
|
||||
}
|
||||
|
||||
match self.terms.pop() {
|
||||
@@ -1103,16 +1067,10 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
if self.terms.is_empty() {
|
||||
Ok(term)
|
||||
} else {
|
||||
Err(ParserError::IncompleteReduction(
|
||||
self.lexer.line_num,
|
||||
self.lexer.col_num,
|
||||
))
|
||||
Err(self.lexer.incomplete_reduction())
|
||||
}
|
||||
}
|
||||
_ => Err(ParserError::IncompleteReduction(
|
||||
self.lexer.line_num,
|
||||
self.lexer.col_num,
|
||||
)),
|
||||
_ => Err(self.lexer.incomplete_reduction()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
417
src/raw_block.rs
417
src/raw_block.rs
@@ -1,136 +1,417 @@
|
||||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use std::alloc;
|
||||
use std::cell::UnsafeCell;
|
||||
use std::cell::Cell;
|
||||
use std::ptr;
|
||||
use std::sync::atomic::{AtomicPtr, Ordering};
|
||||
|
||||
trait PtrCellTrait: std::fmt::Debug {
|
||||
fn new(val: *mut u8) -> Self;
|
||||
|
||||
fn get(&self) -> *mut u8;
|
||||
|
||||
/// Modifies the wrapped value.
|
||||
fn set(&mut self, val: *mut u8);
|
||||
|
||||
/// Performs an atomic compare-and-swap on the wrapped value.
|
||||
///
|
||||
/// If the compare succeeded and `cb` returns `Some(new_ptr)`, stored `new_ptr` and returns `Ok(old_ptr).
|
||||
///
|
||||
/// If `cb` returns `None`, returns `Err(old_ptr)`.
|
||||
///
|
||||
/// May retry multiple times if the comparison fails.
|
||||
fn try_update(&self, cb: impl Fn(*mut u8) -> Option<*mut u8>) -> Result<*mut u8, *mut u8>;
|
||||
}
|
||||
|
||||
impl PtrCellTrait for Cell<*mut u8> {
|
||||
fn new(val: *mut u8) -> Self {
|
||||
Cell::new(val)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn get(&self) -> *mut u8 {
|
||||
Cell::get(self)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn set(&mut self, val: *mut u8) {
|
||||
Cell::set(self, val)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn try_update(&self, cb: impl Fn(*mut u8) -> Option<*mut u8>) -> Result<*mut u8, *mut u8> {
|
||||
let val = Cell::get(self);
|
||||
if let Some(new_val) = cb(val) {
|
||||
Cell::set(self, new_val);
|
||||
Ok(val)
|
||||
} else {
|
||||
Err(val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PtrCellTrait for AtomicPtr<u8> {
|
||||
fn new(val: *mut u8) -> Self {
|
||||
AtomicPtr::new(val)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn get(&self) -> *mut u8 {
|
||||
self.load(Ordering::Acquire)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn set(&mut self, val: *mut u8) {
|
||||
*self.get_mut() = val;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_update(&self, cb: impl Fn(*mut u8) -> Option<*mut u8>) -> Result<*mut u8, *mut u8> {
|
||||
let mut prev = PtrCellTrait::get(self);
|
||||
|
||||
while let Some(next) = cb(prev) {
|
||||
match self.compare_exchange_weak(prev, next, Ordering::Relaxed, Ordering::Acquire) {
|
||||
x @ Ok(_) => return x,
|
||||
Err(next_prev) => prev = next_prev,
|
||||
}
|
||||
}
|
||||
|
||||
Err(prev)
|
||||
|
||||
// TODO: replace with the following once 1.95 is the msrv:
|
||||
// AtomicPtr::try_update(self, Ordering::Relaxed, Ordering::Acquire, cb)
|
||||
}
|
||||
}
|
||||
|
||||
/// Allows the choice of implementation for the mutable pointer in [`RawBlock`].
|
||||
/// Can be one of:
|
||||
/// - [`RawBlockSerial`] (using a [`Cell<*mut u8>`])
|
||||
/// - [`RawBlockConcurrent`] (using a [`AtomicPtr<u8>`])
|
||||
pub(crate) trait RawBlockConcurrency {
|
||||
#[allow(private_bounds)]
|
||||
type PtrCell: PtrCellTrait;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct RawBlockSerial();
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct RawBlockConcurrent();
|
||||
|
||||
impl RawBlockConcurrency for RawBlockSerial {
|
||||
type PtrCell = Cell<*mut u8>;
|
||||
}
|
||||
|
||||
impl RawBlockConcurrency for RawBlockConcurrent {
|
||||
type PtrCell = AtomicPtr<u8>;
|
||||
}
|
||||
|
||||
use crate::machine::heap::AllocError;
|
||||
|
||||
pub trait RawBlockTraits {
|
||||
/// ## Safety
|
||||
///
|
||||
/// Must be non-zero.
|
||||
fn init_size() -> usize;
|
||||
|
||||
/// ## Safety
|
||||
///
|
||||
/// Must be constant.
|
||||
///
|
||||
/// Must respect the invariants of [`std::alloc::Layout::from_size_align()`], namely:
|
||||
/// - must not be zero
|
||||
/// - must be a power of two
|
||||
/// - must not overflow `usize`
|
||||
fn align() -> usize;
|
||||
}
|
||||
|
||||
/// A block of memory with fast, lock-free appends.
|
||||
///
|
||||
/// ## Invariants
|
||||
///
|
||||
/// - `base.is_null()` iff `capacity == 0`
|
||||
/// - if `!base.is_null()`, then `ptr.get()` is in the same allocation as `base`.
|
||||
#[derive(Debug)]
|
||||
pub struct RawBlock<T: RawBlockTraits> {
|
||||
pub base: *const u8,
|
||||
pub top: *const u8,
|
||||
pub ptr: UnsafeCell<*mut u8>,
|
||||
pub struct RawBlock<T: RawBlockTraits, C: RawBlockConcurrency = RawBlockSerial> {
|
||||
base: *const u8,
|
||||
capacity: usize,
|
||||
|
||||
ptr: C::PtrCell,
|
||||
_marker: PhantomData<T>,
|
||||
_c_marker: PhantomData<C>,
|
||||
}
|
||||
|
||||
impl<T: RawBlockTraits> RawBlock<T> {
|
||||
impl<T: RawBlockTraits, C: RawBlockConcurrency> RawBlock<T, C> {
|
||||
#[inline]
|
||||
pub fn empty_block() -> Self {
|
||||
RawBlock {
|
||||
base: ptr::null(),
|
||||
top: ptr::null(),
|
||||
ptr: UnsafeCell::new(ptr::null_mut()),
|
||||
capacity: 0,
|
||||
ptr: C::PtrCell::new(ptr::null_mut()),
|
||||
_marker: PhantomData,
|
||||
_c_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::new_without_default)]
|
||||
pub fn new() -> Self {
|
||||
pub fn new() -> Result<Self, AllocError> {
|
||||
let mut block = Self::empty_block();
|
||||
|
||||
unsafe {
|
||||
block.grow();
|
||||
block.grow()?;
|
||||
}
|
||||
|
||||
block
|
||||
Ok(block)
|
||||
}
|
||||
|
||||
unsafe fn init_at_size(&mut self, cap: usize) {
|
||||
let layout = alloc::Layout::from_size_align_unchecked(cap, T::align());
|
||||
let new_base = alloc::alloc(layout).cast_const();
|
||||
/// ## Safety
|
||||
///
|
||||
/// Assumes that the object has not been initialized before (ie. `self.base.is_null()`)
|
||||
/// and assumes that `cap > 0`.
|
||||
unsafe fn init_at_size(&mut self, cap: usize) -> Result<(), AllocError> {
|
||||
debug_assert!(cap > 0);
|
||||
debug_assert!(self.base.is_null());
|
||||
|
||||
// SAFETY:
|
||||
// - Guaranteed by caller: `cap > 0`
|
||||
// - Guaranteed by caller: `T::align()` respects the invariants of `Layout::from_size_align`
|
||||
let new_base = unsafe {
|
||||
let layout = alloc::Layout::from_size_align_unchecked(cap, T::align());
|
||||
alloc::alloc(layout).cast_const()
|
||||
};
|
||||
|
||||
if new_base.is_null() {
|
||||
panic!(
|
||||
"failed to allocate in init_at_size for {}",
|
||||
std::any::type_name::<Self>()
|
||||
);
|
||||
return Err(AllocError);
|
||||
}
|
||||
|
||||
self.base = new_base;
|
||||
self.top = self.base.add(cap);
|
||||
*self.ptr.get_mut() = self.base.cast_mut();
|
||||
self.capacity = cap;
|
||||
self.ptr.set(self.base.cast_mut());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub unsafe fn grow(&mut self) -> bool {
|
||||
if self.base.is_null() {
|
||||
self.init_at_size(T::init_size());
|
||||
true
|
||||
} else {
|
||||
let size = self.size();
|
||||
let layout = alloc::Layout::from_size_align_unchecked(size, T::align());
|
||||
/// ## Safety
|
||||
///
|
||||
/// Invalidates all pointers previously obtained by [`RawBlock::get()`] or [`RawBlock::alloc()`].
|
||||
pub unsafe fn grow(&mut self) -> Result<(), AllocError> {
|
||||
self.debug_check_invariants();
|
||||
|
||||
if self.base.is_null() {
|
||||
// SAFETY:
|
||||
// - Guaranteed by caller: `T::init_size() > 0`
|
||||
// - Asserted: `self.base.is_null()`
|
||||
unsafe { self.init_at_size(T::init_size()) }
|
||||
} else {
|
||||
let size = self.capacity();
|
||||
let used_bytes = self.used_bytes();
|
||||
// SAFETY:
|
||||
// - Guaranteed by caller: `T::align()` respects the invariants of `Layout::from_size_align`
|
||||
// - Asserted: `!self.base.is_null()`
|
||||
// - Invariant: `self.base.is_null()` iff `self.capacity() == 0`
|
||||
// - Thus `self.capacity() > 0`
|
||||
let new_base = unsafe {
|
||||
let layout = alloc::Layout::from_size_align_unchecked(size, T::align());
|
||||
|
||||
alloc::realloc(self.base.cast_mut(), layout, size * 2).cast_const()
|
||||
};
|
||||
|
||||
let new_base = alloc::realloc(self.base.cast_mut(), layout, size * 2).cast_const();
|
||||
if new_base.is_null() {
|
||||
false
|
||||
Err(AllocError)
|
||||
} else {
|
||||
self.base = new_base;
|
||||
self.top = self.base.add(size * 2);
|
||||
*self.ptr.get_mut() = self.base.add(size).cast_mut();
|
||||
true
|
||||
self.capacity = size * 2;
|
||||
// SAFETY:
|
||||
// - Invariant: `used_bytes < size`
|
||||
// - Definition: `new_base` has allocation size `2 * size`
|
||||
let new_ptr = unsafe { self.base.add(used_bytes).cast_mut() };
|
||||
self.ptr.set(new_ptr);
|
||||
|
||||
self.debug_check_invariants();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn grow_new(&self) -> Option<Self> {
|
||||
pub unsafe fn grow_new(&self) -> Result<Self, AllocError> {
|
||||
self.debug_check_invariants();
|
||||
if self.base.is_null() {
|
||||
Some(Self::new())
|
||||
Self::new()
|
||||
} else {
|
||||
let mut new_block = Self::empty_block();
|
||||
new_block.init_at_size(self.size() * 2);
|
||||
if new_block.base.is_null() {
|
||||
// allocation failed
|
||||
None
|
||||
} else {
|
||||
let allocated = (*self.ptr.get()).addr() - self.base.addr();
|
||||
self.base.copy_to(new_block.base.cast_mut(), allocated);
|
||||
*new_block.ptr.get_mut() = new_block.base.add(allocated).cast_mut();
|
||||
Some(new_block)
|
||||
// SAFETY:
|
||||
// - Asserted: !self.base.is_null()
|
||||
// - Invariant: self.base.is_null() iff self.capacity == 0
|
||||
// - Thus self.capacity > 0
|
||||
// - Definition: `new_block` was not yet initialized
|
||||
unsafe {
|
||||
new_block.init_at_size(self.capacity() * 2)?;
|
||||
}
|
||||
|
||||
let used_bytes = self.used_bytes();
|
||||
|
||||
// SAFETY:
|
||||
// - Definition: `self.base` contains `self.allocated()` bytes
|
||||
// - Invariant: `self.used_bytes() < self.allocated()`
|
||||
unsafe {
|
||||
self.base.copy_to(new_block.base.cast_mut(), used_bytes);
|
||||
new_block.ptr.set(new_block.base.add(used_bytes).cast_mut());
|
||||
}
|
||||
|
||||
new_block.debug_check_invariants();
|
||||
|
||||
Ok(new_block)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn debug_check_invariants(&self) {
|
||||
if cfg!(debug_assertions) {
|
||||
assert!(
|
||||
self.ptr.get().cast_const() >= self.base,
|
||||
"self.ptr = {:?} < {:?} = self.base",
|
||||
self.ptr.get(),
|
||||
self.base
|
||||
);
|
||||
|
||||
assert!(self.used_bytes() <= self.capacity());
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn size(&self) -> usize {
|
||||
self.top.addr() - self.base.addr()
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.capacity
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn free_space(&self) -> usize {
|
||||
debug_assert!(
|
||||
*self.ptr.get() as *const _ >= self.base,
|
||||
"self.ptr = {:?} < {:?} = self.base",
|
||||
*self.ptr.get(),
|
||||
self.base
|
||||
);
|
||||
|
||||
self.top.addr() - (*self.ptr.get()).addr()
|
||||
#[inline]
|
||||
pub fn used_bytes(&self) -> usize {
|
||||
// SAFETY:
|
||||
// - Invariant: `ptr` is in the same allocation as `base`
|
||||
unsafe { self.ptr.get().offset_from(self.base) as usize }
|
||||
}
|
||||
|
||||
pub unsafe fn alloc(&self, size: usize) -> *mut u8 {
|
||||
let aligned_size = size.next_multiple_of(size);
|
||||
if self.free_space() >= aligned_size {
|
||||
let ptr = *self.ptr.get();
|
||||
*self.ptr.get() = ptr.add(aligned_size) as *mut _;
|
||||
ptr
|
||||
} else {
|
||||
ptr::null_mut()
|
||||
self.debug_check_invariants();
|
||||
|
||||
let aligned_size = size.next_multiple_of(T::align());
|
||||
|
||||
match self.ptr.try_update(|ptr| {
|
||||
// SAFETY:
|
||||
// - Invariant: `ptr` is in the same allocation as `base`
|
||||
let free_bytes = unsafe { self.capacity() - ptr.offset_from(self.base) as usize };
|
||||
|
||||
if free_bytes >= aligned_size {
|
||||
Some(unsafe { ptr.add(aligned_size) })
|
||||
} else {
|
||||
// Not enough space: don't allocate and return a null pointer
|
||||
None
|
||||
}
|
||||
}) {
|
||||
Ok(ptr) => ptr,
|
||||
Err(_) => ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Moves `ptr` back to `new_size`.
|
||||
///
|
||||
/// Note that this method does *not* deallocate what was placed in the [`RawBlock`].
|
||||
/// Pointers to data past `new_size` remain valid until the next call to [`RawBlock::alloc()`].
|
||||
pub fn shift_back(&mut self, new_size: usize) {
|
||||
self.debug_check_invariants();
|
||||
|
||||
assert!(
|
||||
new_size <= self.used_bytes(),
|
||||
"Shrink cannot grow: new_size = {:?} > allocated = {:?}",
|
||||
new_size,
|
||||
self.used_bytes()
|
||||
);
|
||||
|
||||
// SAFETY:
|
||||
// - Asserted: new_size <= self.capacity
|
||||
// - Definition: self.base := alloc(self.capacity)
|
||||
let new_ptr = unsafe { self.base.add(new_size) };
|
||||
|
||||
debug_assert!(new_ptr as usize <= self.ptr.get() as usize,);
|
||||
|
||||
self.ptr.set(new_ptr.cast_mut());
|
||||
|
||||
self.debug_check_invariants();
|
||||
}
|
||||
|
||||
/// Returns a pointer at a given `offset` within the block of memory.
|
||||
///
|
||||
/// Panics if that range of bytes wasn't allocated yet with [`RawBlock::alloc()`].
|
||||
pub fn get(&self, offset: usize) -> *const u8 {
|
||||
assert!(offset < self.used_bytes());
|
||||
|
||||
// SAFETY: Asserted.
|
||||
unsafe { self.get_unchecked(offset) }
|
||||
}
|
||||
|
||||
/// Returns a pointer at a given `offset` within the block of memory.
|
||||
///
|
||||
/// ## Safety
|
||||
///
|
||||
/// Assumes that `offset < self.capacity()`.
|
||||
#[inline]
|
||||
pub unsafe fn get_unchecked(&self, offset: usize) -> *const u8 {
|
||||
debug_assert!(
|
||||
offset < self.capacity(),
|
||||
"offset out of bounds: offset is {:?} but {:?} bytes are available",
|
||||
offset,
|
||||
self.used_bytes()
|
||||
);
|
||||
|
||||
// SAFETY: Guaranteed by caller.
|
||||
unsafe { self.base.add(offset) }
|
||||
}
|
||||
|
||||
/// ## Safety
|
||||
///
|
||||
/// `ptr` is a valid pointer be obtained from [`RawBlock::get()`] or [`RawBlock::alloc()`].
|
||||
#[inline]
|
||||
pub unsafe fn get_offset(&self, ptr: *const u8) -> usize {
|
||||
// SAFETY:
|
||||
// - Guaranteed by caller: `ptr` is still valid
|
||||
// - Guranteed by caller: `ptr` was obtained from `get()` or `alloc()`
|
||||
// - get() and alloc() return pointers in the same allocation as `self.base`
|
||||
// - All functions modifying `self.base` invalidate pointers in their contract
|
||||
// - Thus `ptr` and `self.base` originate from the same allocation
|
||||
unsafe { ptr.offset_from(self.base) as usize }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RawBlockTraits> Drop for RawBlock<T> {
|
||||
impl<T: RawBlockTraits, C: RawBlockConcurrency> Drop for RawBlock<T, C> {
|
||||
fn drop(&mut self) {
|
||||
if !self.base.is_null() {
|
||||
unsafe {
|
||||
let layout = alloc::Layout::from_size_align_unchecked(self.size(), T::align());
|
||||
let layout = alloc::Layout::from_size_align_unchecked(self.capacity(), T::align());
|
||||
alloc::dealloc(self.base as *mut _, layout);
|
||||
}
|
||||
|
||||
self.top = ptr::null();
|
||||
self.base = ptr::null();
|
||||
*self.ptr.get_mut() = ptr::null_mut();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RawBlockTraits> From<RawBlock<T, RawBlockConcurrent>> for RawBlock<T, RawBlockSerial> {
|
||||
fn from(other: RawBlock<T, RawBlockConcurrent>) -> Self {
|
||||
Self {
|
||||
base: other.base,
|
||||
capacity: other.capacity,
|
||||
ptr: PtrCellTrait::new(other.ptr.get()),
|
||||
_marker: PhantomData,
|
||||
_c_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RawBlockTraits> From<RawBlock<T, RawBlockSerial>> for RawBlock<T, RawBlockConcurrent> {
|
||||
fn from(other: RawBlock<T, RawBlockSerial>) -> Self {
|
||||
Self {
|
||||
base: other.base,
|
||||
capacity: other.capacity,
|
||||
ptr: PtrCellTrait::new(other.ptr.get()),
|
||||
_marker: PhantomData,
|
||||
_c_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
34
src/read.rs
34
src/read.rs
@@ -1,3 +1,8 @@
|
||||
#[cfg(feature = "repl")]
|
||||
pub mod fallback_mode;
|
||||
#[cfg(feature = "repl")]
|
||||
pub mod user_interaction;
|
||||
|
||||
use crate::parser::ast::*;
|
||||
use crate::parser::lexer::Lexer;
|
||||
use crate::parser::parser::*;
|
||||
@@ -48,12 +53,11 @@ pub(crate) fn error_after_read_term<R>(
|
||||
parser: &Parser<R>,
|
||||
) -> CompilationError {
|
||||
if err.is_unexpected_eof() {
|
||||
let line_num = parser.lexer.line_num;
|
||||
let col_num = parser.lexer.col_num;
|
||||
let location = &parser.lexer.location;
|
||||
|
||||
// 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));
|
||||
if !(location.line() == prior_num_lines_read && location.column() == 0) {
|
||||
return CompilationError::from(parser.lexer.incomplete_reduction());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,13 +102,7 @@ pub(crate) fn set_prompt(value: bool) {
|
||||
#[cfg(feature = "repl")]
|
||||
#[inline]
|
||||
fn get_prompt() -> &'static str {
|
||||
unsafe {
|
||||
if PROMPT {
|
||||
"?- "
|
||||
} else {
|
||||
""
|
||||
}
|
||||
}
|
||||
if unsafe { PROMPT } { "?- " } else { "" }
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
@@ -379,9 +377,7 @@ impl<'a> TermWriter<'a> {
|
||||
|
||||
#[inline]
|
||||
fn push_cell(&mut self, cell: HeapCellValue) -> Result<(), CompilationError> {
|
||||
self.heap
|
||||
.push_cell(cell)
|
||||
.map_err(CompilationError::FiniteMemoryInHeap)
|
||||
Ok(self.heap.push_cell(cell)?)
|
||||
}
|
||||
|
||||
fn term_as_addr(&mut self, term: &TermRef, h: usize) -> HeapCellValue {
|
||||
@@ -475,10 +471,7 @@ impl<'a> TermWriter<'a> {
|
||||
self.push_stub_addr()?;
|
||||
}
|
||||
|
||||
let cell = self
|
||||
.heap
|
||||
.allocate_cstr(src)
|
||||
.map_err(CompilationError::FiniteMemoryInHeap)?;
|
||||
let cell = self.heap.allocate_cstr(src)?;
|
||||
|
||||
let new_h = self.heap.cell_len();
|
||||
self.push_cell(cell)?;
|
||||
@@ -496,10 +489,7 @@ impl<'a> TermWriter<'a> {
|
||||
self.push_stub_addr()?;
|
||||
}
|
||||
|
||||
let cell = self
|
||||
.heap
|
||||
.allocate_pstr(src)
|
||||
.map_err(CompilationError::FiniteMemoryInHeap)?;
|
||||
let cell = self.heap.allocate_pstr(src)?;
|
||||
|
||||
let tail_h = self.heap.cell_len();
|
||||
self.push_stub_addr()?;
|
||||
|
||||
46
src/read/fallback_mode.rs
Normal file
46
src/read/fallback_mode.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyEventState, KeyModifiers};
|
||||
use std::io::{Error, ErrorKind, Read};
|
||||
|
||||
// Provide graceful degradation limited support mode if reading from stdin that does not
|
||||
// support terminal features.
|
||||
//
|
||||
// Retrieve a single byte from stdin, does not support full Unicode decoding; only convert
|
||||
// byte to KeyEvent char if it falls within the ASCII subset of UTF-8.
|
||||
// Does not support passing through Ctrl-C as a KeyEvent.
|
||||
//
|
||||
// We are not supporting multibyte UTF-8 encodings because we are only
|
||||
// interested in single ASCII characters which are 1-byte UTF-8 characters.
|
||||
// Multibyte non-ASCII characters would be ignored anyway, so it's not worth the effort.
|
||||
//
|
||||
// It is expected that `limited_support_read` will only be used when prompting for user
|
||||
// input during solution enumeration and the available commands are single ASCII characters.
|
||||
pub(crate) fn limited_support_read() -> std::io::Result<KeyEvent> {
|
||||
#[allow(
|
||||
clippy::unbuffered_bytes,
|
||||
reason = "We are aware of `bytes()` performance pitfall but don't expect it to be relevant here, we are doing single byte user input I/O."
|
||||
)]
|
||||
let byte_or_none = std::io::stdin().bytes().next();
|
||||
|
||||
match byte_or_none {
|
||||
Some(byte) => match byte {
|
||||
Ok(b) => {
|
||||
if b.is_ascii() {
|
||||
Ok(map_ascii_to_keyevent(b as char))
|
||||
} else {
|
||||
Err(Error::new(ErrorKind::Unsupported, "not supported input"))
|
||||
}
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
},
|
||||
None => Err(Error::new(ErrorKind::UnexpectedEof, "EOF")),
|
||||
}
|
||||
}
|
||||
|
||||
fn map_ascii_to_keyevent(c: char) -> KeyEvent {
|
||||
KeyEvent {
|
||||
code: KeyCode::Char(c),
|
||||
modifiers: KeyModifiers::NONE,
|
||||
kind: KeyEventKind::Press,
|
||||
state: KeyEventState::empty(),
|
||||
}
|
||||
}
|
||||
43
src/read/user_interaction.rs
Normal file
43
src/read/user_interaction.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
use crossterm::event::{Event, KeyEventKind, read};
|
||||
pub(crate) use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
|
||||
use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
|
||||
use crossterm::tty::IsTty;
|
||||
|
||||
use crate::read::fallback_mode;
|
||||
|
||||
pub(crate) fn get_key() -> KeyEvent {
|
||||
let key;
|
||||
if supported_terminal() {
|
||||
enable_raw_mode().expect("failed to enable raw mode");
|
||||
loop {
|
||||
let key_ = read();
|
||||
if let Ok(Event::Key(key_)) = key_ {
|
||||
if key_.kind != KeyEventKind::Release {
|
||||
match key_.code {
|
||||
KeyCode::Char(_) | KeyCode::Enter | KeyCode::Tab => {
|
||||
key = key_;
|
||||
break;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
disable_raw_mode().expect("failed to disable raw mode");
|
||||
} else {
|
||||
// stdin is not supported terminal type, fallback to limited support mode.
|
||||
loop {
|
||||
let key_ = fallback_mode::limited_support_read();
|
||||
if let Ok(key_) = key_ {
|
||||
key = key_;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
key
|
||||
}
|
||||
|
||||
fn supported_terminal() -> bool {
|
||||
// If OS is Windows and stdin is not a tty, it's either a pipe or an unsupported terminal configuration.
|
||||
cfg!(not(windows)) || std::io::stdin().is_tty()
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
use rustyline::completion::Completer;
|
||||
use rustyline::highlight::{Highlighter, MatchingBracketHighlighter};
|
||||
use rustyline::highlight::{CmdKind, Highlighter, MatchingBracketHighlighter};
|
||||
use rustyline::hint::Hinter;
|
||||
use rustyline::validate::Validator;
|
||||
use rustyline::{Context, Helper as RlHelper, Result};
|
||||
@@ -10,14 +10,14 @@ use crate::atom_table::{AtomString, AtomTable, STATIC_ATOMS_MAP};
|
||||
|
||||
// TODO: Maybe add validation to the helper
|
||||
pub struct Helper {
|
||||
highligher: MatchingBracketHighlighter,
|
||||
highlighter: MatchingBracketHighlighter,
|
||||
pub atoms: Weak<AtomTable>,
|
||||
}
|
||||
|
||||
impl Helper {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
highligher: MatchingBracketHighlighter::new(),
|
||||
highlighter: MatchingBracketHighlighter::new(),
|
||||
atoms: Weak::new(),
|
||||
}
|
||||
}
|
||||
@@ -90,11 +90,11 @@ impl Completer for Helper {
|
||||
|
||||
impl Highlighter for Helper {
|
||||
fn highlight<'l>(&self, line: &'l str, pos: usize) -> std::borrow::Cow<'l, str> {
|
||||
self.highligher.highlight(line, pos)
|
||||
self.highlighter.highlight(line, pos)
|
||||
}
|
||||
|
||||
fn highlight_char(&self, line: &str, pos: usize, forced: bool) -> bool {
|
||||
self.highligher.highlight_char(line, pos, forced)
|
||||
fn highlight_char(&self, line: &str, pos: usize, forced: CmdKind) -> bool {
|
||||
self.highlighter.highlight_char(line, pos, forced)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ test_queries_on_builtins :-
|
||||
\+ float([1,2,_]),
|
||||
\+ (X is 3 rdiv 4, float(X)),
|
||||
\+ \+ (X is 3 rdiv 4, rational(X)),
|
||||
\+ rational(3),
|
||||
rational(3),
|
||||
\+ rational(f(_)),
|
||||
\+ rational("sdfa"),
|
||||
\+ rational(atom),
|
||||
|
||||
106
src/toplevel.pl
106
src/toplevel.pl
@@ -16,6 +16,8 @@
|
||||
|
||||
:- dynamic(disabled_init_file/0).
|
||||
:- dynamic(started/0).
|
||||
:- dynamic(custom_toplevel/1).
|
||||
:- dynamic(g_caused_exception/2).
|
||||
|
||||
load_scryerrc :-
|
||||
( '$home_directory'(HomeDir) ->
|
||||
@@ -53,7 +55,18 @@ start_repl :-
|
||||
; true
|
||||
),
|
||||
(\+ disabled_init_file -> load_scryerrc ; true),
|
||||
repl.
|
||||
start_toplevel.
|
||||
|
||||
start_toplevel :-
|
||||
( custom_toplevel(Goal) ->
|
||||
catch(user:call(Goal),
|
||||
Exception,
|
||||
( print_exception(Exception),
|
||||
halt(1)
|
||||
)
|
||||
)
|
||||
; repl
|
||||
).
|
||||
|
||||
args_consults_goals([], [], []).
|
||||
args_consults_goals([Arg|Args], Consults, Goals) :-
|
||||
@@ -64,19 +77,19 @@ arg_consults_goals(c(Mod), Args, [c(Mod)|Consults], Goals) :-
|
||||
arg_consults_goals(g(Goal), Args, Consults, [g(Goal)|Goals]) :-
|
||||
args_consults_goals(Args, Consults, Goals).
|
||||
|
||||
delegate_task([], []).
|
||||
delegate_task([], Goals0) :-
|
||||
(\+ disabled_init_file -> load_scryerrc ; true),
|
||||
reverse(Goals0, Goals1),
|
||||
args_consults_goals(Goals1, Consults, Goals),
|
||||
run_goals(Consults),
|
||||
run_goals(Goals),
|
||||
repl.
|
||||
start_toplevel.
|
||||
|
||||
delegate_task([Arg0|Args], Goals0) :-
|
||||
( ( member(Arg0, ["-h", "--help"]) -> print_help
|
||||
; member(Arg0, ["-v", "--version"]) -> print_version
|
||||
; member(Arg0, ["-g", "--goal"]) -> gather_goal(g, Args, Goals0)
|
||||
; member(Arg0, ["-t"]) -> gather_toplevel(Args, Goals0)
|
||||
; member(Arg0, ["-f"]) -> disable_init_file
|
||||
; member(Arg0, ["--no-add-history"]) -> ignore_machine_arg
|
||||
),
|
||||
@@ -96,6 +109,8 @@ print_help :-
|
||||
write('Print version information and exit'), nl,
|
||||
write(' -g, --goal GOAL '),
|
||||
write('Run the query GOAL'), nl,
|
||||
write(' -t GOAL '),
|
||||
write('Use GOAL as custom toplevel'), nl,
|
||||
write(' -f '),
|
||||
write('Fast startup. Do not load initialization file (~/.scryerrc)'), nl,
|
||||
write(' --no-add-history '),
|
||||
@@ -117,6 +132,17 @@ gather_goal(Type, Args0, Goals) :-
|
||||
Gs =.. [Type, Gs1],
|
||||
delegate_task(Args, [Gs|Goals]).
|
||||
|
||||
gather_toplevel(Args0, Goals0) :-
|
||||
length(Args0, N),
|
||||
( N < 1 -> print_help, halt
|
||||
; true
|
||||
),
|
||||
[TopLevel|Args] = Args0,
|
||||
atom_chars(Goal, TopLevel),
|
||||
retractall(custom_toplevel(_)),
|
||||
asserta(custom_toplevel(Goal)),
|
||||
delegate_task(Args, Goals0).
|
||||
|
||||
disable_init_file :-
|
||||
asserta('disabled_init_file').
|
||||
|
||||
@@ -154,7 +180,8 @@ run_goals([g(Gs0)|Goals]) :- !,
|
||||
Exception,
|
||||
( write_term(Goal, [variable_names(VNs),double_quotes(DQ)]),
|
||||
write(' causes: '),
|
||||
write_term(Exception, [double_quotes(DQ)]), nl % halt?
|
||||
write_term(Exception, [double_quotes(DQ)]), nl,
|
||||
asserta(g_caused_exception(Goal, Exception))
|
||||
)
|
||||
) -> true
|
||||
; write('% Warning: initialization failed for: '),
|
||||
@@ -193,6 +220,77 @@ expand_op_list([Op | OtherOps], Pri, Spec, [(:- op(Pri, Spec, Op)) | OtherResult
|
||||
expand_op_list(OtherOps, Pri, Spec, OtherResults).
|
||||
|
||||
|
||||
% Implement the include/1 directive via term expansion.
|
||||
|
||||
user:term_expansion((:- Include), Clauses) :-
|
||||
nonvar(Include),
|
||||
Include = include(File0),
|
||||
( si:atom_si(File0) ->
|
||||
atom_chars(File0, File),
|
||||
format("% Warning: include/1: atom arguments are deprecated. Use chars for file paths:~n", []),
|
||||
format("% :- include(\"~s\").~n", [File])
|
||||
; error:must_be(chars, File0),
|
||||
File = File0
|
||||
),
|
||||
'$toplevel':gather_clauses_from_file(File, Clauses).
|
||||
|
||||
|
||||
gather_clauses_from_file(File, Clauses) :-
|
||||
( file_exists(File) ->
|
||||
setup_call_cleanup(open(File, read, Stream),
|
||||
gather_clauses_(Stream, File, Clauses),
|
||||
close(Stream))
|
||||
; format("include/1: ~s does not exist.", [File]),
|
||||
Clauses = []
|
||||
).
|
||||
|
||||
|
||||
gather_clauses_(Stream, _, []) :- at_end_of_stream(Stream), !.
|
||||
gather_clauses_(Stream, File, Clauses) :-
|
||||
catch((read(Stream, Clause),
|
||||
Continue = true),
|
||||
Error,
|
||||
( Error = error(syntax_error(incomplete_reduction),_),
|
||||
at_end_of_stream(Stream) ->
|
||||
true
|
||||
; format("~s: ~q~n", [File,Error])
|
||||
)),
|
||||
( Continue == true ->
|
||||
( var(Clause) ->
|
||||
format("~s: variable clause is ignored.~n", [File]),
|
||||
gather_clauses_(Stream, File, Clauses)
|
||||
; Clause = (?- _Query) ->
|
||||
devour_answer_descriptions(Stream, File, Clauses)
|
||||
; Clauses = [Clause|Rest],
|
||||
gather_clauses_(Stream, File, Rest)
|
||||
)
|
||||
; Clauses = []
|
||||
).
|
||||
|
||||
|
||||
devour_answer_descriptions(Stream, File, Clauses) :-
|
||||
catch((read(Stream, Clause),
|
||||
Continue = true),
|
||||
Error,
|
||||
( Error = error(syntax_error(incomplete_reduction),_),
|
||||
at_end_of_stream(Stream) ->
|
||||
true
|
||||
; format("~s: ~q~n", [File,Error])
|
||||
)),
|
||||
( Continue == true ->
|
||||
( var(Clause) ->
|
||||
format("~s: variable clause is ignored.~n", [File])
|
||||
; Clause = (?- _Query) ->
|
||||
devour_answer_descriptions(Stream, File, Clauses)
|
||||
; loader:answer_description(Clause) ->
|
||||
devour_answer_descriptions(Stream, File, Clauses)
|
||||
; Clauses = [Clause|Rest],
|
||||
gather_clauses_(Stream, File, Rest)
|
||||
)
|
||||
; Clauses = []
|
||||
).
|
||||
|
||||
|
||||
read_and_match :-
|
||||
'$read_query_term'(_, Term, _, _, VarList),
|
||||
instruction_match(Term, VarList).
|
||||
|
||||
122
src/types.rs
122
src/types.rs
@@ -1,5 +1,3 @@
|
||||
#![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work
|
||||
|
||||
use crate::arena::*;
|
||||
use crate::atom_table::*;
|
||||
use crate::forms::*;
|
||||
@@ -18,7 +16,8 @@ use std::ops::{Add, Sub, SubAssign};
|
||||
|
||||
use dashu::{Integer, Rational};
|
||||
|
||||
#[derive(BitfieldSpecifier, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
// Variant tag MUST be odd for all but Cons
|
||||
#[derive(Specifier, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[repr(u8)]
|
||||
#[bits = 6]
|
||||
pub enum HeapCellValueTag {
|
||||
@@ -45,34 +44,7 @@ pub enum HeapCellValueTag {
|
||||
TrailedBlackboardOffset = 0b110001,
|
||||
}
|
||||
|
||||
#[derive(BitfieldSpecifier, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[repr(u8)]
|
||||
#[bits = 6]
|
||||
pub enum HeapCellValueView {
|
||||
Str = 0b000001,
|
||||
Lis = 0b000101,
|
||||
Var = 0b001011,
|
||||
StackVar = 0b001101,
|
||||
AttrVar = 0b010001,
|
||||
PStrLoc = 0b010011,
|
||||
// constants.
|
||||
Cons = 0b0,
|
||||
F64Offset = 0b010101,
|
||||
Fixnum = 0b011001,
|
||||
CodeIndexOffset = 0b011011,
|
||||
Atom = 0b011111,
|
||||
CutPoint = 0b011101,
|
||||
// trail elements.
|
||||
TrailedHeapVar = 0b100001,
|
||||
TrailedStackVar = 0b100011,
|
||||
TrailedAttrVar = 0b100101,
|
||||
TrailedAttrVarListLink = 0b101001,
|
||||
TrailedAttachedValue = 0b101011,
|
||||
TrailedBlackboardEntry = 0b101101,
|
||||
TrailedBlackboardOffset = 0b110001,
|
||||
}
|
||||
|
||||
#[derive(BitfieldSpecifier, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[derive(Specifier, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[bits = 1]
|
||||
pub enum ConsPtrMaskTag {
|
||||
Cons = 0b0,
|
||||
@@ -90,19 +62,46 @@ pub struct ConsPtr {
|
||||
}
|
||||
|
||||
impl ConsPtr {
|
||||
// ConstPtr.ptr has 61 bits if usize on the current arch is larger than that
|
||||
// use the niche provided by the alignment of ArenaHeader.
|
||||
// The niece is that the log2(alignment) least significant bits are always 0, so we can shift the
|
||||
// address down by that many places without losing information.
|
||||
// That way with an alignment of 8 a 64-bit address fits into 61-bits after shifting it down by 3 places
|
||||
// and as a result be can store it without loss in ConsPtr.ptr
|
||||
pub(crate) const NICHE_SHIFT: u32 = if usize::BITS > 61 {
|
||||
std::mem::align_of::<ArenaHeader>().ilog2()
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
#[inline(always)]
|
||||
pub fn build_with(ptr: *const ArenaHeader, tag: ConsPtrMaskTag) -> Self {
|
||||
pub fn from_ptr(ptr: *const ArenaHeader) -> Self {
|
||||
Self::build_with(ptr, ConsPtrMaskTag::Cons)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn build_with(ptr: *const ArenaHeader, tag: ConsPtrMaskTag) -> Self {
|
||||
let mut addr = u64::try_from(ptr.expose_provenance())
|
||||
.expect("pointer address {ptr:p} should fit into u64");
|
||||
|
||||
debug_assert_eq!(addr % std::mem::align_of::<ArenaHeader>() as u64, 0);
|
||||
|
||||
addr >>= Self::NICHE_SHIFT;
|
||||
|
||||
ConsPtr::new()
|
||||
.with_ptr(ptr.expose_provenance() as u64)
|
||||
.with_ptr(addr)
|
||||
.with_f(false)
|
||||
.with_m(false)
|
||||
.with_tag(tag)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn as_ptr(self) -> *mut u8 {
|
||||
let addr: u64 = self.ptr();
|
||||
std::ptr::with_exposed_provenance_mut(addr as usize)
|
||||
pub fn as_ptr(self) -> *const ArenaHeader {
|
||||
let mut addr: u64 = self.ptr();
|
||||
|
||||
addr <<= Self::NICHE_SHIFT;
|
||||
|
||||
std::ptr::with_exposed_provenance(addr as usize)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -111,7 +110,7 @@ impl ConsPtr {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(BitfieldSpecifier, Copy, Clone, Debug)]
|
||||
#[derive(Specifier, Copy, Clone, Debug)]
|
||||
#[bits = 6]
|
||||
pub(crate) enum RefTag {
|
||||
HeapCell = 0b001011,
|
||||
@@ -198,7 +197,7 @@ pub enum TrailRef {
|
||||
}
|
||||
|
||||
#[allow(clippy::enum_variant_names)] // allow the common "Trailed" prefix
|
||||
#[derive(BitfieldSpecifier, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[derive(Specifier, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[bits = 6]
|
||||
pub(crate) enum TrailEntryTag {
|
||||
TrailedHeapVar = 0b101111,
|
||||
@@ -365,7 +364,7 @@ where
|
||||
{
|
||||
#[inline]
|
||||
fn from(arena_ptr: TypedArenaPtr<T>) -> HeapCellValue {
|
||||
HeapCellValue::from(arena_ptr.header_ptr().expose_provenance() as u64)
|
||||
HeapCellValue::from_arena_header_ptr(arena_ptr.header_ptr())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,18 +385,6 @@ impl From<CodeIndexOffset> for HeapCellValue {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ConsPtr> for HeapCellValue {
|
||||
#[inline(always)]
|
||||
fn from(cons_ptr: ConsPtr) -> HeapCellValue {
|
||||
HeapCellValue::from_bytes(
|
||||
ConsPtr::from(cons_ptr.as_ptr().expose_provenance() as u64)
|
||||
.with_tag(ConsPtrMaskTag::Cons)
|
||||
.with_m(false)
|
||||
.into_bytes(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(Number, &mut Arena)> for HeapCellValue {
|
||||
#[inline(always)]
|
||||
fn from((n, arena): (Number, &mut Arena)) -> HeapCellValue {
|
||||
@@ -587,12 +574,12 @@ impl HeapCellValue {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_ptr_addr(ptr_bytes: usize) -> Self {
|
||||
HeapCellValue::from_bytes((ptr_bytes as u64).to_ne_bytes())
|
||||
pub fn from_arena_header_ptr(ptr: *const ArenaHeader) -> Self {
|
||||
HeapCellValue::from_bytes(ConsPtr::from_ptr(ptr).into_bytes())
|
||||
}
|
||||
|
||||
pub fn to_ptr_addr(self) -> usize {
|
||||
u64::from_ne_bytes(self.into_bytes()) as usize
|
||||
pub fn to_arena_header_ptr(self) -> *const ArenaHeader {
|
||||
ConsPtr::from_bytes(self.into_bytes()).as_ptr()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -736,21 +723,14 @@ const_assert!(mem::size_of::<UntypedArenaPtr>() == 8);
|
||||
impl From<*const ArenaHeader> for UntypedArenaPtr {
|
||||
#[inline]
|
||||
fn from(ptr: *const ArenaHeader) -> UntypedArenaPtr {
|
||||
UntypedArenaPtr::build_with(ptr.expose_provenance())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<*const IndexPtr> for UntypedArenaPtr {
|
||||
#[inline]
|
||||
fn from(ptr: *const IndexPtr) -> UntypedArenaPtr {
|
||||
UntypedArenaPtr::build_with(ptr.expose_provenance())
|
||||
UntypedArenaPtr::from_bytes(ConsPtr::from_ptr(ptr).into_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<UntypedArenaPtr> for *const ArenaHeader {
|
||||
#[inline]
|
||||
fn from(ptr: UntypedArenaPtr) -> *const ArenaHeader {
|
||||
ptr.get_ptr().cast::<ArenaHeader>()
|
||||
ptr.get_ptr()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -761,9 +741,8 @@ impl UntypedArenaPtr {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_ptr(self) -> *const u8 {
|
||||
let addr: u64 = self.ptr();
|
||||
std::ptr::with_exposed_provenance(addr as usize)
|
||||
pub fn get_ptr(self) -> *const ArenaHeader {
|
||||
ConsPtr::from_bytes(self.into_bytes()).as_ptr()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -775,20 +754,15 @@ impl UntypedArenaPtr {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn payload_offset(self) -> *const u8 {
|
||||
unsafe { self.get_ptr().add(size_of::<ArenaHeader>()) }
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// - this UntypedArenaPtr actuall pointee type is T
|
||||
/// - this UntypedArenaPtr actual pointee type is T
|
||||
/// - the pointer must be non-null
|
||||
#[inline]
|
||||
pub unsafe fn as_typed_ptr<T: ?Sized + ArenaAllocated>(self) -> TypedArenaPtr<T>
|
||||
where
|
||||
T::Payload: Sized,
|
||||
{
|
||||
T::typed_ptr(self)
|
||||
unsafe { T::typed_ptr(self) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
use crate::forms::GenContext;
|
||||
use crate::forms::{BranchNumber, GenContext};
|
||||
use crate::parser::ast::*;
|
||||
|
||||
use bit_set::*;
|
||||
use fxhash::FxBuildHasher;
|
||||
use indexmap::{IndexMap, IndexSet};
|
||||
use num_order::NumOrd;
|
||||
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TempVarData {
|
||||
@@ -13,20 +16,19 @@ pub struct TempVarData {
|
||||
pub(crate) conflict_set: BitSet<usize>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct BranchDesignator {
|
||||
pub branch_stack_num: usize,
|
||||
pub branch_num: usize,
|
||||
pub branch_num: Arc<BranchNumber>,
|
||||
}
|
||||
|
||||
impl BranchDesignator {
|
||||
#[inline]
|
||||
pub fn is_sub_branch(&self) -> bool {
|
||||
self.branch_stack_num > 0
|
||||
self.branch_num.branch_num.num_gt(&0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum VarSafetyStatus {
|
||||
Needed,
|
||||
// which branch planted the last unsafe guarded instruction? It may still be needed.
|
||||
@@ -35,27 +37,27 @@ pub enum VarSafetyStatus {
|
||||
}
|
||||
|
||||
impl VarSafetyStatus {
|
||||
pub(crate) fn unneeded(current_branch: BranchDesignator) -> Self {
|
||||
pub(crate) fn unneeded(current_branch: &BranchDesignator) -> Self {
|
||||
if current_branch.is_sub_branch() {
|
||||
VarSafetyStatus::LocallyUnneeded(current_branch)
|
||||
VarSafetyStatus::LocallyUnneeded(current_branch.clone())
|
||||
} else {
|
||||
VarSafetyStatus::GloballyUnneeded
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn needed_if(needed: bool, branch_designator: BranchDesignator) -> Self {
|
||||
pub(crate) fn needed_if(needed: bool, branch_designator: &BranchDesignator) -> Self {
|
||||
if needed {
|
||||
VarSafetyStatus::Needed
|
||||
} else if branch_designator.branch_stack_num == 0 {
|
||||
} else if branch_designator.branch_num.branch_num.num_eq(&0) {
|
||||
VarSafetyStatus::GloballyUnneeded
|
||||
} else {
|
||||
VarSafetyStatus::LocallyUnneeded(branch_designator)
|
||||
VarSafetyStatus::LocallyUnneeded(branch_designator.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum PermVarAllocation {
|
||||
Done {
|
||||
shallow_safety: VarSafetyStatus,
|
||||
@@ -103,10 +105,8 @@ impl VarAlloc {
|
||||
#[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,
|
||||
VarAlloc::Perm(p, _) => *p = reg_num,
|
||||
VarAlloc::Temp { temp_reg, .. } => *temp_reg = reg_num,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
32
src/wasm.rs
32
src/wasm.rs
@@ -129,23 +129,21 @@ impl WasmQueryState {
|
||||
let mut error = None;
|
||||
let mut to_drop = false;
|
||||
match &mut self.inner {
|
||||
Some(ref mut inner) => {
|
||||
inner.with_query_state_mut(|query_state| match query_state.next() {
|
||||
Some(Ok(leaf_answer)) => {
|
||||
js_sys::Reflect::set(&ret, &"value".into(), &leaf_answer.into()).unwrap();
|
||||
js_sys::Reflect::set(&ret, &"done".into(), &false.into()).unwrap();
|
||||
}
|
||||
Some(Err(error_term)) => {
|
||||
let js_error = js_sys::Error::new("Prolog error");
|
||||
js_error.set_cause(&error_term.into());
|
||||
error = Some(js_error);
|
||||
}
|
||||
None => {
|
||||
js_sys::Reflect::set(&ret, &"done".into(), &true.into()).unwrap();
|
||||
to_drop = true;
|
||||
}
|
||||
})
|
||||
}
|
||||
Some(inner) => inner.with_query_state_mut(|query_state| match query_state.next() {
|
||||
Some(Ok(leaf_answer)) => {
|
||||
js_sys::Reflect::set(&ret, &"value".into(), &leaf_answer.into()).unwrap();
|
||||
js_sys::Reflect::set(&ret, &"done".into(), &false.into()).unwrap();
|
||||
}
|
||||
Some(Err(error_term)) => {
|
||||
let js_error = js_sys::Error::new("Prolog error");
|
||||
js_error.set_cause(&error_term.into());
|
||||
error = Some(js_error);
|
||||
}
|
||||
None => {
|
||||
js_sys::Reflect::set(&ret, &"done".into(), &true.into()).unwrap();
|
||||
to_drop = true;
|
||||
}
|
||||
}),
|
||||
None => return Err(js_sys::Error::new("This query was already dropped").into()),
|
||||
}
|
||||
|
||||
|
||||
11
tests-pl/discussion3359.pl
Normal file
11
tests-pl/discussion3359.pl
Normal file
@@ -0,0 +1,11 @@
|
||||
:- use_module(library(dcgs)).
|
||||
:- use_module(library(tabling)).
|
||||
|
||||
:- table expr//0.
|
||||
|
||||
expr --> "1".
|
||||
expr --> expr, "+", expr.
|
||||
|
||||
run :- phrase(expr, "1+1+1+1+1").
|
||||
|
||||
:- initialization(run).
|
||||
16
tests-pl/ffi_utf8_panic.pl
Normal file
16
tests-pl/ffi_utf8_panic.pl
Normal file
@@ -0,0 +1,16 @@
|
||||
:- use_module(library(os)).
|
||||
:- use_module(library(ffi)).
|
||||
|
||||
init :-
|
||||
read(Body),
|
||||
term_variables(Body, [LIB]),
|
||||
Body,
|
||||
use_foreign_module(LIB, [
|
||||
'ffi_invalid_utf8_cstr'([], cstr)
|
||||
]).
|
||||
|
||||
test :-
|
||||
ffi:'ffi_invalid_utf8_cstr'(Str),
|
||||
write(Str), nl.
|
||||
|
||||
:- initialization((init,test)).
|
||||
@@ -1,5 +1,26 @@
|
||||
:- module(http_open_hanging, [submit_request/0]).
|
||||
% Server
|
||||
:- use_module(library(process)).
|
||||
:- use_module(library(iso_ext)).
|
||||
:- use_module(library(os)).
|
||||
|
||||
prolog_path(Prolog) :-
|
||||
read(Body),
|
||||
term_variables(Body, [Prolog]),
|
||||
Body.
|
||||
|
||||
server_start([Process,Out]) :-
|
||||
prolog_path(Prolog),
|
||||
process_create(Prolog,
|
||||
["tests-pl/issue-http_open-hanging_server", "-t", "server"],
|
||||
[process(Process), stdout(pipe(Out))]).
|
||||
|
||||
server_wait_start([_Process, Out]) :-
|
||||
get_char(Out, _C).
|
||||
|
||||
server_stop([Process,_Out]) :-
|
||||
process_kill(Process).
|
||||
|
||||
% Client
|
||||
:- use_module(library(charsio)).
|
||||
:- use_module(library(http/http_open)).
|
||||
|
||||
@@ -10,14 +31,21 @@ send_request :-
|
||||
request_headers([]),
|
||||
headers(_)
|
||||
],
|
||||
http_open("https://scryer.pl", _Stream, Options),
|
||||
http_open("http://localhost:8472", _Stream, Options),
|
||||
write_term('received response with status code':StatusCode, []), nl.
|
||||
|
||||
main :-
|
||||
send_request,
|
||||
send_request,
|
||||
send_request,
|
||||
send_request,
|
||||
send_request.
|
||||
setup_call_cleanup(
|
||||
server_start(Server),
|
||||
(
|
||||
server_wait_start(Server),
|
||||
send_request,
|
||||
send_request,
|
||||
send_request,
|
||||
send_request,
|
||||
send_request
|
||||
),
|
||||
server_stop(Server)
|
||||
).
|
||||
|
||||
:- initialization(main).
|
||||
8
tests-pl/issue-http_open-hanging_server.pl
Normal file
8
tests-pl/issue-http_open-hanging_server.pl
Normal file
@@ -0,0 +1,8 @@
|
||||
:- use_module(library(process)).
|
||||
:- use_module(library(http/http_server)).
|
||||
|
||||
server :-
|
||||
http_listen(8472, [get(/, hello)]).
|
||||
|
||||
hello(_Req, Res) :-
|
||||
http_body(Res, text("Ok")).
|
||||
38
tests-pl/issue-interrupt-nontermination.pl
Normal file
38
tests-pl/issue-interrupt-nontermination.pl
Normal file
@@ -0,0 +1,38 @@
|
||||
% Server
|
||||
:- use_module(library(process)).
|
||||
:- use_module(library(charsio)).
|
||||
:- use_module(library(lists)).
|
||||
:- use_module(library(time)).
|
||||
|
||||
prolog_path(Prolog) :-
|
||||
read(Body),
|
||||
term_variables(Body, [Prolog]),
|
||||
Body.
|
||||
|
||||
main :-
|
||||
prolog_path(Prolog),
|
||||
CMD = "\
|
||||
use_module(library(os)), \
|
||||
pid(PID), \
|
||||
write(PID), nl, \
|
||||
asserta((f :- f)), \
|
||||
catch(f, Err, (write(Err),nl)), \
|
||||
write(done), nl, \
|
||||
halt.",
|
||||
process_create("tests-pl/pty_exec.py", [Prolog, "-g", CMD], [stdout(pipe(O))]),
|
||||
get_line_to_chars(O, PID0, ""),
|
||||
append(PID, "\r\n", PID0),
|
||||
process_create("kill", ["-s", "INT", PID], []),
|
||||
sleep(5),
|
||||
% second kill should fail because process should exit after one kill
|
||||
process_create("kill", ["-s", "INT", PID], [stderr(null), process(PK)]),
|
||||
process_wait(PK, Status),
|
||||
status_report(Status, PID), nl.
|
||||
|
||||
status_report(exit(1), _PID) :- write(ok).
|
||||
status_report(exit(0), PID) :-
|
||||
write(not_dead),
|
||||
% kill by force to exit cleanly
|
||||
process_create("kill", ["-9", PID], []).
|
||||
|
||||
:- initialization(main).
|
||||
2
tests-pl/issue2914.pl
Normal file
2
tests-pl/issue2914.pl
Normal file
@@ -0,0 +1,2 @@
|
||||
:- initialization(main).
|
||||
main :- ( current_prolog_flag(X, X) -> write(true) ; write(false) ).
|
||||
5
tests-pl/issue3256.pl
Normal file
5
tests-pl/issue3256.pl
Normal file
@@ -0,0 +1,5 @@
|
||||
:- use_module(library(sgml)).
|
||||
|
||||
test :- load_xml("<foo><bar>hello</bar></foo>", Es, []), write(Es).
|
||||
|
||||
:- initialization(test).
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user