Merge remote-tracking branch 'upstream/master' into issue-2588
This commit is contained in:
0
.dockerignore
Executable file → Normal file
0
.dockerignore
Executable file → Normal file
2
.git-blame-ignore-revs
Normal file
2
.git-blame-ignore-revs
Normal file
@@ -0,0 +1,2 @@
|
||||
# Resolved all lints and formatted the codebase
|
||||
9444e62df9820d6bfd96dbd8849e177bc5cecc2e
|
||||
52
.github/actions/setup-rust/action.yml
vendored
Normal file
52
.github/actions/setup-rust/action.yml
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
name: 'Setup Rust'
|
||||
inputs:
|
||||
rust-version:
|
||||
required: true
|
||||
type: string
|
||||
targets:
|
||||
required: true
|
||||
type: string
|
||||
components:
|
||||
required: false
|
||||
default:
|
||||
cache-context:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
id: toolchain
|
||||
with:
|
||||
toolchain: ${{ inputs.rust-version }}
|
||||
targets: ${{ inputs.targets }}
|
||||
components: ${{ inputs.components }}
|
||||
|
||||
- name: Install i686 dependencies
|
||||
if: "contains(inputs.targets,'i686')"
|
||||
shell: bash
|
||||
run: |
|
||||
sudo dpkg --add-architecture i386
|
||||
sudo apt-get update
|
||||
sudo apt-get install libssl-dev:i386 gcc-multilib clang -y
|
||||
echo "CC=clang" >> $GITHUB_ENV
|
||||
echo "PKG_CONFIG_SYSROOT_DIR=/" >> $GITHUB_ENV
|
||||
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
target/
|
||||
key: ${{ inputs.cache-context }}_${{ inputs.targets }}_rustc-${{ steps.toolchain.outputs.cachekey }}_cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
# 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@v0.4.6
|
||||
with:
|
||||
main: bash ./.github/actions/setup-rust/cleanup.sh
|
||||
post: bash ./.github/actions/setup-rust/cleanup.sh
|
||||
13
.github/actions/setup-rust/cleanup.sh
vendored
Executable file
13
.github/actions/setup-rust/cleanup.sh
vendored
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
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(" ")'`
|
||||
|
||||
# remove directories in /target/ that are not named `debug` or `release`
|
||||
before=`du -s target | awk '{print $1}'`
|
||||
find ./target -maxdepth 1 -type d ! -name debug ! -name release ! -name target -exec rm -r {} \;
|
||||
after=`du -s target | awk '{print $1}'`
|
||||
echo Deleted $(($before - $after)) bytes from target directory
|
||||
151
.github/workflows/ci.yml
vendored
151
.github/workflows/ci.yml
vendored
@@ -10,7 +10,27 @@ on:
|
||||
- cron: '0 0 * * 3' # At 12:00 AM, only on Wednesday
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
checks: write
|
||||
|
||||
jobs:
|
||||
style:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup Rust
|
||||
uses: ./.github/actions/setup-rust
|
||||
with:
|
||||
rust-version: stable
|
||||
targets: x86_64-unknown-linux-gnu
|
||||
components: clippy, rustfmt
|
||||
cache-context: style
|
||||
|
||||
- name: Check formatting
|
||||
run: cargo fmt --check
|
||||
- name: Check clippy
|
||||
run: cargo clippy --no-deps --all-targets
|
||||
|
||||
build-test:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
@@ -18,79 +38,45 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
# operating systems
|
||||
- { os: windows-latest, rust-version: stable, publish: true, target: 'x86_64-pc-windows-msvc'}
|
||||
- { os: macos-11, rust-version: stable, publish: true, target: 'x86_64-apple-darwin' }
|
||||
- { os: ubuntu-20.04, rust-version: stable, publish: true, target: 'x86_64-unknown-linux-gnu' }
|
||||
- { 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: ubuntu-20.04, rust-version: stable, target: 'x86_64-unknown-linux-gnu', publish: true }
|
||||
# architectures
|
||||
- { os: ubuntu-22.04, rust-version: stable, publish: true, target: 'x86_64-unknown-linux-gnu', extra: true }
|
||||
- { os: ubuntu-22.04, rust-version: stable, publish: true, target: 'i686-unknown-linux-gnu' }
|
||||
- { os: ubuntu-22.04, rust-version: nightly, publish: true, target: 'wasm32-unknown-unknown', args: '--no-default-features' }
|
||||
- { os: ubuntu-22.04, rust-version: stable, target: 'x86_64-unknown-linux-gnu', publish: 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: "1.77", target: 'x86_64-unknown-linux-gnu'}
|
||||
# rust versions
|
||||
- { os: ubuntu-22.04, rust-version: "1.70", 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'}
|
||||
- { os: ubuntu-22.04, rust-version: nightly, target: 'x86_64-unknown-linux-gnu', miri: true, components: "miri"}
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
id: toolchain
|
||||
- uses: actionhippie/swap-space@v1
|
||||
if: matrix.use_swap
|
||||
with:
|
||||
toolchain: ${{ matrix.rust-version }}
|
||||
size: 10G
|
||||
- name: Setup Rust
|
||||
uses: ./.github/actions/setup-rust
|
||||
with:
|
||||
rust-version: ${{ matrix.rust-version }}
|
||||
targets: ${{ matrix.target }}
|
||||
components: clippy, rustfmt
|
||||
- name: Install i686 dependencies
|
||||
if: "contains(matrix.target,'i686')"
|
||||
run: |
|
||||
sudo dpkg --add-architecture i386
|
||||
sudo apt-get update
|
||||
sudo apt-get install libssl-dev:i386 gcc-multilib clang -y
|
||||
echo "CC=clang" >> $GITHUB_ENV
|
||||
echo "PKG_CONFIG_SYSROOT_DIR=/" >> $GITHUB_ENV
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
target/
|
||||
key: ${{ matrix.os }}_${{ matrix.target }}_rustc-${{ steps.toolchain.outputs.cachekey }}_cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
cache-context: ${{ matrix.os }}
|
||||
components: ${{ matrix.components }}
|
||||
|
||||
# Build and test.
|
||||
- name: Build library
|
||||
run: cargo rustc --lib --target ${{ matrix.target }} ${{ matrix.args }} --verbose
|
||||
run: cargo build --all-targets --target ${{ matrix.target }} ${{ matrix.args }} --verbose
|
||||
- name: Test
|
||||
if: "!matrix.extra"
|
||||
run: cargo test --target ${{ matrix.target }} ${{ matrix.args }} --all --verbose || echo "::warning ::Tests failed"
|
||||
run: cargo test --target ${{ matrix.target }} ${{ matrix.test-args }} --all
|
||||
|
||||
# Extra steps only run once to avoid duplication, when matrix.extra is true
|
||||
- name: Test and report
|
||||
if: matrix.extra
|
||||
run: |
|
||||
cargo install cargo2junit --force
|
||||
RUSTC_BOOTSTRAP=1 cargo test --all -- -Z unstable-options --format json --report-time | cargo2junit > cargo_test_results.xml
|
||||
- name: Publish cargo test results artifact
|
||||
if: matrix.extra
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: cargo-test-results
|
||||
path: cargo_test_results.xml
|
||||
- name: Publish cargo test summary
|
||||
if: matrix.extra
|
||||
uses: EnricoMi/publish-unit-test-result-action/composite@master
|
||||
with:
|
||||
check_name: Cargo test summary
|
||||
files: cargo_test_results.xml
|
||||
fail_on: nothing
|
||||
comment_mode: off
|
||||
- name: Check formatting
|
||||
if: matrix.extra
|
||||
run: cargo fmt --check || echo "::warning ::cargo fmt found some formatting changes that may improve readability"
|
||||
- name: Check clippy
|
||||
if: matrix.extra
|
||||
run: cargo clippy --no-deps || echo "::warning ::cargo clippy found some code style changes that may be more idiomatic"
|
||||
- name: Check miri
|
||||
if: matrix.miri
|
||||
run: cargo miri test
|
||||
|
||||
# On stable rust builds, build a binary and publish as a github actions
|
||||
# artifact. These binaries could be useful for testing the pipeline but
|
||||
@@ -154,6 +140,55 @@ jobs:
|
||||
fail_on: nothing
|
||||
comment_mode: off
|
||||
|
||||
report:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup Rust
|
||||
uses: ./.github/actions/setup-rust
|
||||
with:
|
||||
rust-version: stable
|
||||
targets: x86_64-unknown-linux-gnu
|
||||
cache-context: report
|
||||
- name: Install CLI tools
|
||||
run: |
|
||||
cargo install cargo2junit --force
|
||||
version=`yq -ptoml -oy -r '.target.*.dev-dependencies.iai-callgrind|select(.)' Cargo.toml`
|
||||
echo installing iai-callgrind "$version"
|
||||
cargo install iai-callgrind-runner --force --version "$version"
|
||||
sudo apt 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@v3
|
||||
with:
|
||||
name: cargo-test-results
|
||||
path: cargo_test_results.xml
|
||||
- name: Publish cargo test summary
|
||||
uses: EnricoMi/publish-unit-test-result-action/composite@master
|
||||
with:
|
||||
check_name: Cargo test summary
|
||||
files: cargo_test_results.xml
|
||||
fail_on: nothing
|
||||
comment_mode: off
|
||||
|
||||
- run: cargo build --all-targets --release
|
||||
- run: cargo test --bench setup --release
|
||||
- run: cargo bench --bench run_iai -- --save-summary=json
|
||||
- run: cargo bench --bench run_criterion
|
||||
- run: cargo bench --bench run_criterion -- --profile-time 60
|
||||
|
||||
- name: Publish benchmark results
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: benchmark-results
|
||||
path: |
|
||||
target/criterion/*
|
||||
target/iai/*
|
||||
target/benchmark_inference_counts.json
|
||||
|
||||
# Publish binaries when building for a tag
|
||||
release:
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,5 +1,5 @@
|
||||
src/static_atoms.rs
|
||||
target/
|
||||
|
||||
.direnv/
|
||||
|
||||
|
||||
|
||||
2409
Cargo.lock
generated
2409
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
161
Cargo.toml
161
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "scryer-prolog"
|
||||
version = "0.9.3"
|
||||
version = "0.9.4"
|
||||
authors = ["Mark Thom <markjordanthom@gmail.com>"]
|
||||
edition = "2021"
|
||||
description = "A modern Prolog implementation written mostly in Rust."
|
||||
@@ -10,7 +10,7 @@ license = "BSD-3-Clause"
|
||||
keywords = ["prolog", "prolog-interpreter", "prolog-system"]
|
||||
categories = ["command-line-utilities"]
|
||||
build = "build/main.rs"
|
||||
rust-version = "1.70"
|
||||
rust-version = "1.77"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
@@ -22,94 +22,127 @@ repl = ["dep:crossterm", "dep:ctrlc", "dep:rustyline"]
|
||||
hostname = ["dep:hostname"]
|
||||
tls = ["dep:native-tls"]
|
||||
http = ["dep:warp", "dep:reqwest"]
|
||||
rust_beta_channel = []
|
||||
crypto-full = []
|
||||
"rust-version-1.80" = []
|
||||
|
||||
[build-dependencies]
|
||||
indexmap = "1.0.2"
|
||||
proc-macro2 = "1.0.36"
|
||||
quote = "1.0.15"
|
||||
strum = "0.23"
|
||||
strum_macros = "0.23"
|
||||
syn = { version = "2.0.32", features = ['full', 'visit', 'extra-traits'] }
|
||||
indexmap = "2.3.0"
|
||||
proc-macro2 = "1.0.86"
|
||||
quote = "1.0.36"
|
||||
strum = "0.26"
|
||||
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"
|
||||
walkdir = "2"
|
||||
|
||||
[dependencies]
|
||||
bit-set = "0.5.3"
|
||||
arcu = { version = "0.1.1", features = ["thread_local_counter"] }
|
||||
base64 = "0.22.1"
|
||||
bit-set = "0.8.0"
|
||||
bitvec = "1"
|
||||
cpu-time = "1.0.0"
|
||||
dirs-next = "2.0.0"
|
||||
divrem = "0.1.0"
|
||||
fxhash = "0.2.1"
|
||||
git-version = "0.3.4"
|
||||
indexmap = "1.0.2"
|
||||
lazy_static = "1.4.0"
|
||||
lexical = "5.2.2"
|
||||
libc = "0.2.62"
|
||||
modular-bitfield = "0.11.2"
|
||||
ordered-float = "2.6.0"
|
||||
phf = { version = "0.9", features = ["macros"] }
|
||||
ref_thread_local = "0.0.0"
|
||||
ripemd160 = "0.8.0"
|
||||
sha3 = "0.8.2"
|
||||
blake2 = "0.8.1"
|
||||
crrl = "0.6.0"
|
||||
chrono = "0.4.11"
|
||||
select = "0.6.0"
|
||||
roxmltree = "0.11.0"
|
||||
base64 = "0.12.3"
|
||||
smallvec = "1.8.0"
|
||||
static_assertions = "1.1.0"
|
||||
ryu = "1.0.9"
|
||||
futures = "0.3"
|
||||
regex = "1.9.1"
|
||||
libloading = "0.7"
|
||||
derive_deref = "1.1.1"
|
||||
blake2 = "0.10.6"
|
||||
bytes = "1"
|
||||
dashu = "0.4.0"
|
||||
chrono = "0.4.38"
|
||||
cpu-time = "1.0.0"
|
||||
crrl = "0.9.0"
|
||||
dashu = "0.4.2"
|
||||
derive_more = "0.99.18"
|
||||
dirs-next = "2.0.0"
|
||||
divrem = "1.0.0"
|
||||
futures = "0.3"
|
||||
fxhash = "0.2.1"
|
||||
git-version = "0.3.9"
|
||||
indexmap = "2.3.0"
|
||||
lazy_static = "1.5.0"
|
||||
lexical = "6.1.1"
|
||||
libc = "0.2.155"
|
||||
libloading = "0.8"
|
||||
scryer-modular-bitfield = "0.11.4"
|
||||
num-order = { version = "1.2.0" }
|
||||
ordered-float = "4.2.2"
|
||||
phf = { version = "0.11", features = ["macros"] }
|
||||
rand = "0.8.5"
|
||||
regex = "1.10.6"
|
||||
ring = { version = "0.17.8", features = ["wasm32_unknown_unknown_js"] }
|
||||
ripemd = "0.1.3"
|
||||
roxmltree = "0.20.0"
|
||||
ryu = "1.0.18"
|
||||
sha3 = "0.10.8"
|
||||
smallvec = "1.13.2"
|
||||
static_assertions = "1.1.0"
|
||||
|
||||
scraper = { version = "0.19.1", default-features = false, features = [
|
||||
"errors",
|
||||
] }
|
||||
ego-tree = "0.6.2"
|
||||
|
||||
|
||||
serde_json = "1.0.122"
|
||||
serde = "1.0.204"
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
crossterm = { version = "0.28.1", optional = true }
|
||||
ctrlc = { version = "3.4.4", optional = true }
|
||||
hostname = { version = "0.4.0", optional = true }
|
||||
libffi = { version = "3.2.0", optional = true }
|
||||
hostname = { version = "0.3.1", optional = true }
|
||||
crossterm = { version = "0.20.0", optional = true }
|
||||
ctrlc = { version = "3.2.2", optional = true }
|
||||
rustyline = { version = "12.0.0", optional = true }
|
||||
native-tls = { version = "0.2.4", optional = true }
|
||||
warp = { version = "=0.3.5", features = ["tls"], optional = true }
|
||||
reqwest = { version = "0.11.18", features = ["blocking"], optional = true }
|
||||
tokio = { version = "1.28.2", features = ["full"] }
|
||||
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 }
|
||||
tokio = { version = "1.39.2", features = ["full"] }
|
||||
warp = { version = "0.3.7", features = ["tls"], optional = true }
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
getrandom = { version = "0.2.10", features = ["js"] }
|
||||
tokio = { version = "1.28.2", features = ["sync", "macros", "io-util", "rt", "time"] }
|
||||
getrandom = { version = "0.2.15", features = ["js"] }
|
||||
tokio = { version = "1.39.2", features = [
|
||||
"sync",
|
||||
"macros",
|
||||
"io-util",
|
||||
"rt",
|
||||
"time",
|
||||
] }
|
||||
|
||||
[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies]
|
||||
console_error_panic_hook = "0.1"
|
||||
console_log = "1.0"
|
||||
wasm-bindgen = "0.2.87"
|
||||
wasm-bindgen = "0.2.92"
|
||||
wasm-bindgen-futures = "0.4"
|
||||
serde-wasm-bindgen = "0.5"
|
||||
serde-wasm-bindgen = "0.6"
|
||||
web-sys = { version = "0.3", features = [
|
||||
"Document",
|
||||
"Window",
|
||||
"Element",
|
||||
]}
|
||||
|
||||
[target.'cfg(target_os = "wasi")'.dependencies]
|
||||
ring-wasi = { version = "0.16.25" }
|
||||
|
||||
[target.'cfg(not(target_os = "wasi"))'.dependencies]
|
||||
ring = { version = "0.16.13" }
|
||||
"Performance",
|
||||
] }
|
||||
js-sys = "0.3"
|
||||
|
||||
[dev-dependencies]
|
||||
assert_cmd = "1.0.3"
|
||||
predicates-core = "1.0.2"
|
||||
maplit = "1.0.2"
|
||||
serial_test = "2.0.0"
|
||||
predicates-core = "1.0.8"
|
||||
serial_test = "3.1.1"
|
||||
|
||||
[patch.crates-io]
|
||||
modular-bitfield = { git = "https://github.com/mthom/modular-bitfield" }
|
||||
[target.'cfg(not(all(target_arch = "wasm32", target_os = "unknown")))'.dev-dependencies]
|
||||
assert_cmd = "2.0.15"
|
||||
criterion = "0.5.1"
|
||||
iai-callgrind = "0.12.1"
|
||||
trycmd = "0.15.6"
|
||||
|
||||
[target.'cfg(not(any(target_os = "windows", all(target_arch = "wasm32", target_os = "unknown"))))'.dev-dependencies]
|
||||
pprof = { version = "0.13.0", features = ["criterion", "flamegraph"] }
|
||||
|
||||
[profile.bench]
|
||||
lto = true
|
||||
opt-level = 3
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
opt-level = 3
|
||||
|
||||
[[bench]]
|
||||
name = "run_criterion"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "run_iai"
|
||||
harness = false
|
||||
|
||||
0
Dockerfile
Executable file → Normal file
0
Dockerfile
Executable file → Normal file
18
INDEX.dj
18
INDEX.dj
@@ -7,8 +7,8 @@
|
||||
|
||||
``` =html
|
||||
<div style="border: solid #00007f 3px;padding-left: 15px;padding-right: 15px;font-style: italic;background-color: #00007f30;">
|
||||
<h4>Scryer Prolog Meetup 2023</h4>
|
||||
<p>The first annual Scryer Prolog meetup is going to happen in Düsseldorf (Germany) on the 9th and 10th of November 2023. Join us to discover the present and future of Scryer Prolog! Participation is free, registration not required. <a href="https://hsd-pbsa.de/veranstaltung/scryer-prolog-meetup-2023/">More details here.</a></p>
|
||||
<h4>Scryer Prolog Meetup 2024</h4>
|
||||
<p>The second annual Scryer Prolog meetup is going to happen in Vienna (Austria) on the 7th and 8th of November 2024. Join us to discover the present and future of Scryer Prolog! Participation is free, registration is required. <a href="https://www.digitalaustria.gv.at/eng/insights/Digital-Austria-Events-EN/Scryer-Prolog-Meetup-2024.html">More details here.</a></p>
|
||||
</div>
|
||||
```
|
||||
|
||||
@@ -28,6 +28,7 @@ Some of the Scryer Prolog features are:
|
||||
* [Cryptographical predicates](/crypto.html)
|
||||
* [Foreign Function Interface](/ffi.html)
|
||||
* WebAssembly support
|
||||
* Usable as a library
|
||||
* WAM based engine, cross-platform made in Rust
|
||||
* _and more..._
|
||||
|
||||
@@ -61,11 +62,14 @@ the builtin Prolog modules and libraries in Scryer, check the documentation site
|
||||
|
||||
## Downloads
|
||||
|
||||
The latest version of Scryer Prolog is *0.9.2*. And it's already useful for lots of tasks.
|
||||
The latest version of Scryer Prolog is *0.9.4*. And it's already useful for lots of tasks.
|
||||
|
||||
| Windows | [Download](https://github.com/mthom/scryer-prolog/releases/download/v0.9.2/scryer-prolog_windows-latest.zip) |
|
||||
| macOS (Intel) | [Download](https://github.com/mthom/scryer-prolog/releases/download/v0.9.2/scryer-prolog_macos-11.zip) |
|
||||
| Linux | [Download](https://github.com/mthom/scryer-prolog/releases/download/v0.9.2/scryer-prolog_ubuntu-20.04.zip) |
|
||||
| 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) |
|
||||
|
||||
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.
|
||||
|
||||
@@ -80,4 +84,4 @@ an [issue](https://github.com/mthom/scryer-prolog/issues).
|
||||
|
||||
To get in touch with the Scryer Prolog community, participate in
|
||||
[discussions](https://github.com/mthom/scryer-prolog/discussions)
|
||||
or visit our #scryer IRC channel on [Libera](https://libera.chat)!
|
||||
or visit our #scryer IRC channel on [Libera](https://libera.chat)!
|
||||
|
||||
54
README.md
54
README.md
@@ -1,3 +1,12 @@
|
||||
# Announcing the Scryer Prolog Meetup 2024
|
||||
|
||||
This year we will meet at the Hotel Stefanie in Vienna to discuss
|
||||
present and future developments in the Scryer Prolog system.
|
||||
|
||||
Details here: [https://www.digitalaustria.gv.at/eng/insights/Digital-Austria-Events-EN/Scryer-Prolog-Meetup-2024.html](https://www.digitalaustria.gv.at/eng/insights/Digital-Austria-Events-EN/Scryer-Prolog-Meetup-2024.html).
|
||||
|
||||
Many thanks to the Austrian Federal Ministry of Finance for hosting
|
||||
the event!
|
||||
|
||||
# Scryer Prolog
|
||||
|
||||
@@ -6,7 +15,10 @@ source industrial strength production environment that is also a
|
||||
testbed for bleeding edge research in logic and constraint
|
||||
programming, which is itself written in a high-level language.
|
||||
|
||||
As of July 2023, **Scryer Prolog passes all [syntactic conformity tests](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/conformity_testing)**.
|
||||
**Scryer Prolog passes all tests** of
|
||||
[syntactic conformity](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/conformity_testing),
|
||||
[`variable_names/1`](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/variable_names) and
|
||||
[`dif/2`](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/dif).
|
||||
|
||||
The homepage of the project is: [**https://www.scryer.pl**](https://www.scryer.pl)
|
||||
|
||||
@@ -110,7 +122,7 @@ strings.
|
||||
Precompiled binaries for several platforms are available for download
|
||||
at:
|
||||
|
||||
**https://github.com/mthom/scryer-prolog/releases/tag/v0.9.2**
|
||||
**https://github.com/mthom/scryer-prolog/releases/latest**
|
||||
|
||||
### Native Compilation
|
||||
|
||||
@@ -313,6 +325,35 @@ To quit Scryer Prolog, use the standard predicate `halt/0`:
|
||||
?- halt.
|
||||
```
|
||||
|
||||
### Starting Scryer Prolog
|
||||
|
||||
Scryer Prolog can be started from the command line by specifying
|
||||
options, files and additional arguments. All components are optional:
|
||||
|
||||
<pre>
|
||||
scryer-prolog [OPTIONS] [FILES] [-- ARGUMENTS]
|
||||
</pre>
|
||||
|
||||
The supported options are:
|
||||
|
||||
```
|
||||
-h, --help Display help message
|
||||
-v, --version Print version information and exit
|
||||
-g, --goal GOAL Run the query GOAL after consulting files
|
||||
-f Fast startup. Do not load initialization file (~/.scryerrc)
|
||||
--no-add-history Prevent adding input to history file (~/.scryer_history)
|
||||
```
|
||||
|
||||
All specified Prolog files are consulted.
|
||||
|
||||
After Prolog files, application-specific arguments can be specified on
|
||||
the command line. These arguments can be accessed from within Prolog
|
||||
applications with the predicate `argv/1`, which yields the list
|
||||
of arguments represented as strings.
|
||||
|
||||
Prolog files can also be turned into *shell scripts* as explained in
|
||||
https://github.com/mthom/scryer-prolog/issues/2170#issuecomment-1821713993.
|
||||
|
||||
### Dynamic operators
|
||||
|
||||
Scryer supports dynamic operators. Using the built-in
|
||||
@@ -767,13 +808,14 @@ standards compliance and warranty.
|
||||
|
||||
Successful existing applications of Scryer Prolog include the
|
||||
[DocLog](https://github.com/aarroyoc/doclog) system which
|
||||
generates Scryer's own documentation and homepage, [Symbolic
|
||||
Analysis of Grants](https://www.brz.gv.at/en/BRZ-Tech-Blog/Tech-Blog-7-Symbolic-Analysis-of-Grants.html)
|
||||
by the Austrian Federal Computing Center, and parts of the
|
||||
generates Scryer's own documentation and homepage, [reasoning
|
||||
about business grants](https://arxiv.org/abs/2406.15293)
|
||||
in the Austrian public administration, and parts of the
|
||||
[precautionary](https://github.com/dcnorris/precautionary/tree/main/exec/prolog)
|
||||
package for the analysis of dose-escalation trials in the
|
||||
safety-critical and highly regulated domain of oncology
|
||||
trial design.
|
||||
trial design, described in [*An Executable Specification of
|
||||
Oncology Dose-Escalation Protocols with Prolog*](https://arxiv.org/abs/2402.08334).
|
||||
|
||||
Scryer Prolog is also very well suited for teaching and learning
|
||||
Prolog, and for testing syntactic conformance and hence portability of
|
||||
|
||||
95
benches/README.md
Normal file
95
benches/README.md
Normal file
@@ -0,0 +1,95 @@
|
||||
# About benches
|
||||
|
||||
The `benches` directory contains benchmarks that test scryer-prolog performance.
|
||||
|
||||
Benchmarks are run via two harnesses:
|
||||
|
||||
* `criterion` - criterion performs statistical analysis of benchmark runs and is
|
||||
great for benchmarking locally.
|
||||
* `iai-callgrind` - this runs the benchmark with callgrind, which is able to
|
||||
precisely track the number of instructions executed during the run. This is
|
||||
especially helpful in a public CI runner context where neighboring VMs can
|
||||
cause a very high wall time variance. While instructions executed is only
|
||||
correlated with the desired metric (wall time), this is a good tradeoff for CI
|
||||
where that metric is unreliable.
|
||||
|
||||
Run benchmarks with the following commands:
|
||||
|
||||
```
|
||||
cargo bench --bench run_criterion
|
||||
|
||||
# run a particular criterion benchmark
|
||||
cargo bench --bench run_criterion -- <benchmark_name>
|
||||
|
||||
# run in profiling mode which outputs flamegraphs. Set profile time in seconds:
|
||||
cargo bench --bench run_criterion -- --profile-time <time>
|
||||
|
||||
# to run iai, you need valgrind installed and to install iai-callgrind-runner
|
||||
# at the same version as is in Cargo.toml:
|
||||
cargo install iai-callgrind-runner --version 0.7.3
|
||||
|
||||
cargo bench --bench run_iai
|
||||
```
|
||||
|
||||
For consistency, both runners -- `run_iai.rs` and `run_criterion.rs` -- import
|
||||
the same setup code from `setup.rs`.
|
||||
|
||||
## Setup
|
||||
|
||||
`setup.rs` contains the setup code to run benchmarks. `fn prolog_benches()` at
|
||||
the top of the file is where the benchmarks are defined.
|
||||
|
||||
Benchmarks are organized around running queries against a prolog module file.
|
||||
Before a benchmark starts, `benchmark.setup()` is called which reads the module
|
||||
file and initializes a new `scryer_prolog::machine::Machine`.
|
||||
|
||||
Each benchmark measurement is done by running a query against the machine. In
|
||||
the case of criterion each query is run many times, in the case of iai it's run
|
||||
once.
|
||||
|
||||
## Adding benchmarks
|
||||
|
||||
This design is meant to suppoort defining lots of benchmarks.
|
||||
|
||||
To add a new benchmark:
|
||||
|
||||
* Add a new file `benches/[module].pl` that contains setup prolog code. Import
|
||||
libraries, define predicates, etc.
|
||||
* Add a new section in `setup.rs::prolog_benchmarks()` that refers to to the
|
||||
file and write a query to be benchmarked.
|
||||
* If the query mutates the machine, then use `Strategy::Fresh` so the criterion
|
||||
benchmark will recreate a new machine for each benchmark run, otherwise use
|
||||
`Strategy::Reuse` which has lower overhead. (This is not used by the iai
|
||||
benchmark because it only runs once anyway.)
|
||||
|
||||
Some tips:
|
||||
|
||||
* The goal of benchmarking is to know if a library or engine change improved
|
||||
performance or not.
|
||||
* Once a benchmark is defined and named, avoid changing it's definition. If a
|
||||
benchmark needs to change to be more useful, give the new definition a new
|
||||
name instead. This will prevent charts from showing wild changes in
|
||||
performance just because the definition changed (see previous).
|
||||
* Aim for queries to execute in less than 0.5s realtime. Longer runtimes make it
|
||||
easier for humans to see big differences, but benchmarks either run 10x slower
|
||||
(iai) or execute repeatedly to attain statistical significance (criterion) and
|
||||
in both cases benchmarking queries that take longer than about 0.5s are
|
||||
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.
|
||||
* DO test the output of the benchmark run, we don't want to count broken
|
||||
benchmarks.
|
||||
|
||||
## CI
|
||||
|
||||
Both benchmark harnesses are run in `.github/workflows/ci.yaml` in the `report`
|
||||
job, and the results are published as build artifacts.
|
||||
|
||||
## Todo
|
||||
|
||||
- [ ] Currently, the execution time to load a module is not benchmarked. It
|
||||
would be nice to have at least one benchmark for loading a module (probably a
|
||||
big one).
|
||||
- [ ] Write a new action that downloads the test and benchmark results
|
||||
artifacts, plots them over time, and publishes a report to github pages.
|
||||
41
benches/csv.pl
Normal file
41
benches/csv.pl
Normal file
File diff suppressed because one or more lines are too long
130
benches/edges.pl
Normal file
130
benches/edges.pl
Normal file
@@ -0,0 +1,130 @@
|
||||
:- use_module(library(clpb)).
|
||||
:- use_module(library(assoc)).
|
||||
:- use_module(library(lists)).
|
||||
:- use_module(library(pairs)).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Contiguous United States and DC as they appear in SGB:
|
||||
http://www-cs-faculty.stanford.edu/~uno/sgb.html
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
edge(al, fl).
|
||||
edge(al, ga).
|
||||
edge(al, ms).
|
||||
edge(al, tn).
|
||||
edge(ar, la).
|
||||
edge(ar, mo).
|
||||
edge(ar, ms).
|
||||
edge(ar, ok).
|
||||
edge(ar, tn).
|
||||
edge(ar, tx).
|
||||
edge(az, ca).
|
||||
edge(az, nm).
|
||||
edge(az, nv).
|
||||
edge(az, ut).
|
||||
edge(ca, nv).
|
||||
edge(ca, or).
|
||||
edge(co, ks).
|
||||
edge(co, ne).
|
||||
edge(co, nm).
|
||||
edge(co, ok).
|
||||
edge(co, ut).
|
||||
edge(co, wy).
|
||||
edge(ct, ma).
|
||||
edge(ct, ny).
|
||||
edge(ct, ri).
|
||||
edge(dc, md).
|
||||
edge(dc, va).
|
||||
edge(de, md).
|
||||
edge(de, nj).
|
||||
edge(de, pa).
|
||||
edge(fl, ga).
|
||||
edge(ga, nc).
|
||||
edge(ga, sc).
|
||||
edge(ga, tn).
|
||||
edge(ia, il).
|
||||
edge(ia, mn).
|
||||
edge(ia, mo).
|
||||
edge(ia, ne).
|
||||
edge(ia, sd).
|
||||
edge(ia, wi).
|
||||
edge(id, mt).
|
||||
edge(id, nv).
|
||||
edge(id, or).
|
||||
edge(id, ut).
|
||||
edge(id, wa).
|
||||
edge(id, wy).
|
||||
edge(il, in).
|
||||
edge(il, ky).
|
||||
edge(il, mo).
|
||||
edge(il, wi).
|
||||
edge(in, ky).
|
||||
edge(in, mi).
|
||||
edge(in, oh).
|
||||
edge(ks, mo).
|
||||
edge(ks, ne).
|
||||
edge(ks, ok).
|
||||
edge(ky, mo).
|
||||
edge(ky, oh).
|
||||
edge(ky, tn).
|
||||
edge(ky, va).
|
||||
edge(ky, wv).
|
||||
edge(la, ms).
|
||||
edge(la, tx).
|
||||
edge(ma, nh).
|
||||
edge(ma, ny).
|
||||
edge(ma, ri).
|
||||
edge(ma, vt).
|
||||
edge(md, pa).
|
||||
edge(md, va).
|
||||
edge(md, wv).
|
||||
edge(me, nh).
|
||||
edge(mi, oh).
|
||||
edge(mi, wi).
|
||||
edge(mn, nd).
|
||||
edge(mn, sd).
|
||||
edge(mn, wi).
|
||||
edge(mo, ne).
|
||||
edge(mo, ok).
|
||||
edge(mo, tn).
|
||||
edge(ms, tn).
|
||||
edge(mt, nd).
|
||||
edge(mt, sd).
|
||||
edge(mt, wy).
|
||||
edge(nc, sc).
|
||||
edge(nc, tn).
|
||||
edge(nc, va).
|
||||
edge(nd, sd).
|
||||
edge(ne, sd).
|
||||
edge(ne, wy).
|
||||
edge(nh, vt).
|
||||
edge(nj, ny).
|
||||
edge(nj, pa).
|
||||
edge(nm, ok).
|
||||
edge(nm, tx).
|
||||
edge(nv, or).
|
||||
edge(nv, ut).
|
||||
edge(ny, pa).
|
||||
edge(ny, vt).
|
||||
edge(oh, pa).
|
||||
edge(oh, wv).
|
||||
edge(ok, tx).
|
||||
edge(or, wa).
|
||||
edge(pa, wv).
|
||||
edge(sd, wy).
|
||||
edge(tn, va).
|
||||
edge(ut, wy).
|
||||
edge(va, wv).
|
||||
|
||||
independent_set(G, *(NBs)) :-
|
||||
findall(U-V, (edge(U, V),G@<U), Edges),
|
||||
setof(U, V^(member(U-V, Edges);member(V-U, Edges)), Nodes),
|
||||
pairs_keys_values(Pairs, Nodes, _),
|
||||
list_to_assoc(Pairs, Assoc),
|
||||
maplist(not_both(Assoc), Edges, NBs).
|
||||
|
||||
not_both(Assoc, U-V, ~BU + ~BV) :-
|
||||
get_assoc(U, Assoc, BU),
|
||||
get_assoc(V, Assoc, BV).
|
||||
|
||||
independent_set_count(G, Count) :- independent_set(G, Sat), sat_count(Sat, Count).
|
||||
2
benches/numlist.pl
Normal file
2
benches/numlist.pl
Normal file
@@ -0,0 +1,2 @@
|
||||
:- use_module(library(between)).
|
||||
run_numlist(Upper, Head) :- numlist(1, Upper, L), L = [Head|_].
|
||||
46
benches/run_criterion.rs
Normal file
46
benches/run_criterion.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
use pprof::criterion::{Output, PProfProfiler};
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
mod setup;
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
fn bench_criterion(c: &mut Criterion) {
|
||||
for (&name, bench) in setup::prolog_benches().iter() {
|
||||
match bench.strategy {
|
||||
setup::Strategy::Fresh => c.bench_function(name, |b| {
|
||||
b.iter_batched(|| bench.setup(), |mut r| r(), BatchSize::LargeInput)
|
||||
}),
|
||||
setup::Strategy::Reuse => c.bench_function(name, |b| b.iter(bench.setup())),
|
||||
};
|
||||
}
|
||||
}
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn config() -> Criterion {
|
||||
Criterion::default()
|
||||
.sample_size(20)
|
||||
.with_profiler(PProfProfiler::new(100, Output::Flamegraph(None)))
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn config() -> Criterion {
|
||||
Criterion::default().sample_size(20)
|
||||
}
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
criterion_group!(
|
||||
name = benches;
|
||||
config = config();
|
||||
targets = bench_criterion
|
||||
);
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
criterion_main!(benches);
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
|
||||
fn main() {}
|
||||
38
benches/run_iai.rs
Normal file
38
benches/run_iai.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
mod setup;
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
mod iai {
|
||||
use iai_callgrind::{library_benchmark, library_benchmark_group, main};
|
||||
|
||||
use scryer_prolog::QueryResolution;
|
||||
|
||||
use super::setup;
|
||||
|
||||
#[library_benchmark]
|
||||
#[bench::count_edges(setup::prolog_benches()["count_edges"].setup())]
|
||||
#[bench::numlist(setup::prolog_benches()["numlist"].setup())]
|
||||
#[bench::csv_codename(setup::prolog_benches()["csv_codename"].setup())]
|
||||
fn bench(mut run: impl FnMut() -> QueryResolution) -> QueryResolution {
|
||||
run()
|
||||
}
|
||||
|
||||
library_benchmark_group!(
|
||||
name = benches;
|
||||
benchmarks = bench
|
||||
);
|
||||
|
||||
main!(library_benchmark_groups = benches);
|
||||
|
||||
pub fn call_main() {
|
||||
main()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
fn main() {
|
||||
iai::call_main();
|
||||
}
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
|
||||
fn main() {}
|
||||
131
benches/setup.rs
Normal file
131
benches/setup.rs
Normal file
@@ -0,0 +1,131 @@
|
||||
use std::{collections::BTreeMap, fs, path::Path};
|
||||
|
||||
use maplit::btreemap;
|
||||
use scryer_prolog::{Machine, QueryResolution, Value};
|
||||
|
||||
pub fn prolog_benches() -> BTreeMap<&'static str, PrologBenchmark> {
|
||||
[
|
||||
(
|
||||
"count_edges", // name of the benchmark
|
||||
"benches/edges.pl", // name of the prolog module file to load. use the same file in multiple benchmarks
|
||||
"independent_set_count(ky, Count).", // query to benchmark in the context of the loaded module. consider making the query adjustable to tune the run time to ~0.1s
|
||||
Strategy::Reuse,
|
||||
btreemap! { "Count" => Value::Integer(2869176.into()) },
|
||||
),
|
||||
(
|
||||
"numlist",
|
||||
"benches/numlist.pl",
|
||||
"run_numlist(1000000, Head).",
|
||||
Strategy::Reuse,
|
||||
btreemap! { "Head" => Value::Integer(1.into())},
|
||||
),
|
||||
(
|
||||
"csv_codename",
|
||||
"benches/csv.pl",
|
||||
"get_codename(\"0020\",Name).",
|
||||
Strategy::Reuse,
|
||||
btreemap! { "Name" => Value::String("SPACE".into())},
|
||||
),
|
||||
]
|
||||
.map(|b| {
|
||||
(
|
||||
b.0,
|
||||
PrologBenchmark {
|
||||
name: b.0,
|
||||
filename: b.1,
|
||||
query: b.2,
|
||||
strategy: b.3,
|
||||
bindings: b.4,
|
||||
},
|
||||
)
|
||||
})
|
||||
.into()
|
||||
}
|
||||
|
||||
pub enum Strategy {
|
||||
#[allow(dead_code)]
|
||||
Fresh,
|
||||
Reuse,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct PrologBenchmark {
|
||||
pub name: &'static str,
|
||||
pub filename: &'static str,
|
||||
pub query: &'static str,
|
||||
pub strategy: Strategy,
|
||||
pub bindings: BTreeMap<&'static str, Value>,
|
||||
}
|
||||
|
||||
impl PrologBenchmark {
|
||||
pub fn make_machine(&self) -> Machine {
|
||||
let program = fs::read_to_string(self.filename).unwrap();
|
||||
let module_name = Path::new(self.filename)
|
||||
.file_stem()
|
||||
.and_then(|s| s.to_str())
|
||||
.unwrap();
|
||||
let mut machine = Machine::new_lib();
|
||||
machine.load_module_string(module_name, program);
|
||||
machine
|
||||
}
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
pub fn setup(&self) -> impl FnMut() -> QueryResolution {
|
||||
let mut machine = self.make_machine();
|
||||
let query = self.query;
|
||||
move || {
|
||||
use criterion::black_box;
|
||||
black_box(machine.run_query(black_box(query.to_string()))).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
#[test]
|
||||
fn validate_benchmarks() {
|
||||
use super::prolog_benches;
|
||||
use scryer_prolog::{QueryMatch, QueryResolution};
|
||||
use std::{fmt::Write, fs};
|
||||
|
||||
struct BenchResult {
|
||||
pub name: &'static str,
|
||||
pub setup_inference_count: u64,
|
||||
pub query_inference_count: u64,
|
||||
}
|
||||
|
||||
let mut results: Vec<BenchResult> = vec![];
|
||||
|
||||
for (_, r) in prolog_benches() {
|
||||
let mut machine = r.make_machine();
|
||||
let setup_inference_count = machine.get_inference_count();
|
||||
|
||||
let result = machine.run_query(r.query.to_string()).unwrap();
|
||||
let query_inference_count = machine.get_inference_count() - setup_inference_count;
|
||||
|
||||
let expected = QueryResolution::Matches(vec![QueryMatch::from(r.bindings.clone())]);
|
||||
assert_eq!(result, expected, "validating benchmark {}", r.name);
|
||||
|
||||
results.push(BenchResult {
|
||||
name: r.name,
|
||||
setup_inference_count,
|
||||
query_inference_count,
|
||||
})
|
||||
}
|
||||
|
||||
let mut json: String = Default::default();
|
||||
json.push('[');
|
||||
for r in results {
|
||||
json.push('\n');
|
||||
write!(
|
||||
json,
|
||||
r#"{{"name":"{}","setup_inference_count":{},"query_inference_count":{}}},"#,
|
||||
r.name, r.setup_inference_count, r.query_inference_count
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
json.pop(); // trailing comma
|
||||
json.push_str("\n]");
|
||||
fs::write("target/benchmark_inference_counts.json", json).expect("Unable to write file");
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ struct Level;
|
||||
struct NextOrFail;
|
||||
struct RegType;
|
||||
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
#[allow(dead_code)]
|
||||
#[derive(ToDeriveInput, EnumDiscriminants)]
|
||||
#[strum_discriminants(derive(EnumProperty, EnumString))]
|
||||
@@ -49,6 +50,7 @@ enum CompareNumber {
|
||||
NumberEqual(ArithmeticTerm, ArithmeticTerm),
|
||||
}
|
||||
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
#[allow(dead_code)]
|
||||
#[derive(ToDeriveInput, EnumDiscriminants)]
|
||||
#[strum_discriminants(derive(EnumProperty, EnumString))]
|
||||
@@ -135,7 +137,7 @@ enum InlinedClauseType {
|
||||
#[allow(dead_code)]
|
||||
#[derive(ToDeriveInput, EnumDiscriminants)]
|
||||
#[strum_discriminants(derive(EnumProperty, EnumString))]
|
||||
enum REPLCodePtr {
|
||||
enum ReplCodePtr {
|
||||
#[strum_discriminants(strum(props(Arity = "4", Name = "$add_discontiguous_predicate")))]
|
||||
AddDiscontiguousPredicate,
|
||||
#[strum_discriminants(strum(props(Arity = "4", Name = "$add_dynamic_predicate")))]
|
||||
@@ -206,6 +208,7 @@ enum REPLCodePtr {
|
||||
AddNonCountedBacktracking,
|
||||
}
|
||||
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
#[allow(dead_code)]
|
||||
#[derive(ToDeriveInput, EnumDiscriminants)]
|
||||
#[strum_discriminants(derive(EnumProperty, EnumString))]
|
||||
@@ -326,6 +329,8 @@ enum SystemClauseType {
|
||||
InstallSCCCleaner,
|
||||
#[strum_discriminants(strum(props(Arity = "3", Name = "$install_inference_counter")))]
|
||||
InstallInferenceCounter,
|
||||
#[strum_discriminants(strum(props(Arity = "1", Name = "$inference_count")))]
|
||||
InferenceCount,
|
||||
#[strum_discriminants(strum(props(Arity = "1", Name = "$lh_length")))]
|
||||
LiftedHeapLength,
|
||||
#[strum_discriminants(strum(props(Arity = "3", Name = "$load_library_as_stream")))]
|
||||
@@ -492,6 +497,8 @@ enum SystemClauseType {
|
||||
CryptoRandomByte,
|
||||
#[strum_discriminants(strum(props(Arity = "4", Name = "$crypto_data_hash")))]
|
||||
CryptoDataHash,
|
||||
#[strum_discriminants(strum(props(Arity = "5", Name = "$crypto_hmac")))]
|
||||
CryptoHMAC,
|
||||
#[strum_discriminants(strum(props(Arity = "7", Name = "$crypto_data_hkdf")))]
|
||||
CryptoDataHKDF,
|
||||
#[strum_discriminants(strum(props(Arity = "4", Name = "$crypto_password_hash")))]
|
||||
@@ -506,18 +513,12 @@ enum SystemClauseType {
|
||||
#[cfg(feature = "crypto-full")]
|
||||
#[strum_discriminants(strum(props(Arity = "6", Name = "$crypto_data_decrypt")))]
|
||||
CryptoDataDecrypt,
|
||||
#[cfg(feature = "crypto-full")]
|
||||
#[strum_discriminants(strum(props(Arity = "4", Name = "$ed25519_sign")))]
|
||||
Ed25519Sign,
|
||||
#[cfg(feature = "crypto-full")]
|
||||
#[strum_discriminants(strum(props(Arity = "4", Name = "$ed25519_verify")))]
|
||||
Ed25519Verify,
|
||||
#[cfg(feature = "crypto-full")]
|
||||
#[strum_discriminants(strum(props(Arity = "1", Name = "$ed25519_new_keypair")))]
|
||||
Ed25519NewKeyPair,
|
||||
#[cfg(feature = "crypto-full")]
|
||||
#[strum_discriminants(strum(props(Arity = "2", Name = "$ed25519_keypair_public_key")))]
|
||||
Ed25519KeyPairPublicKey,
|
||||
#[strum_discriminants(strum(props(Arity = "4", Name = "$ed25519_sign_raw")))]
|
||||
Ed25519SignRaw,
|
||||
#[strum_discriminants(strum(props(Arity = "4", Name = "$ed25519_verify_raw")))]
|
||||
Ed25519VerifyRaw,
|
||||
#[strum_discriminants(strum(props(Arity = "2", Name = "$ed25519_seed_to_public_key")))]
|
||||
Ed25519SeedToPublicKey,
|
||||
#[strum_discriminants(strum(props(Arity = "2", Name = "$first_non_octet")))]
|
||||
FirstNonOctet,
|
||||
#[strum_discriminants(strum(props(Arity = "3", Name = "$load_html")))]
|
||||
@@ -533,7 +534,7 @@ enum SystemClauseType {
|
||||
#[strum_discriminants(strum(props(Arity = "2", Name = "$shell")))]
|
||||
Shell,
|
||||
#[strum_discriminants(strum(props(Arity = "1", Name = "$pid")))]
|
||||
PID,
|
||||
Pid,
|
||||
#[strum_discriminants(strum(props(Arity = "4", Name = "$chars_base64")))]
|
||||
CharsBase64,
|
||||
#[strum_discriminants(strum(props(Arity = "1", Name = "$devour_whitespace")))]
|
||||
@@ -570,6 +571,8 @@ enum SystemClauseType {
|
||||
ForeignCall,
|
||||
#[strum_discriminants(strum(props(Arity = "2", Name = "$define_foreign_struct")))]
|
||||
DefineForeignStruct,
|
||||
#[strum_discriminants(strum(props(Arity = "2", Name = "$js_eval")))]
|
||||
JsEval,
|
||||
#[strum_discriminants(strum(props(Arity = "3", Name = "$predicate_defined")))]
|
||||
PredicateDefined,
|
||||
#[strum_discriminants(strum(props(Arity = "3", Name = "$strip_module")))]
|
||||
@@ -603,7 +606,9 @@ enum SystemClauseType {
|
||||
KeySortWithConstantVarOrdering,
|
||||
#[strum_discriminants(strum(props(Arity = "0", Name = "$inference_limit_exceeded")))]
|
||||
InferenceLimitExceeded,
|
||||
REPL(REPLCodePtr),
|
||||
#[strum_discriminants(strum(props(Arity = "1", Name = "$argv")))]
|
||||
Argv,
|
||||
Repl(ReplCodePtr),
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
@@ -789,8 +794,8 @@ enum InstructionTemplate {
|
||||
#[strum_discriminants(strum(props(Arity = "0", Name = "install_verify_attr")))]
|
||||
InstallVerifyAttr,
|
||||
// call verify_attrs.
|
||||
#[strum_discriminants(strum(props(Arity = "0", Name = "verify_attr_interrupt")))]
|
||||
VerifyAttrInterrupt,
|
||||
#[strum_discriminants(strum(props(Arity = "1", Name = "verify_attr_interrupt")))]
|
||||
VerifyAttrInterrupt(usize),
|
||||
// procedures
|
||||
CallClause(ClauseType, usize, usize, bool, bool), // ClauseType,
|
||||
// arity,
|
||||
@@ -806,7 +811,7 @@ fn derive_input(ty: &Type) -> Option<DeriveInput> {
|
||||
let system_clause_type: Type = parse_quote! { SystemClauseType };
|
||||
let compare_term_type: Type = parse_quote! { CompareTerm };
|
||||
let compare_number_type: Type = parse_quote! { CompareNumber };
|
||||
let repl_code_ptr_type: Type = parse_quote! { REPLCodePtr };
|
||||
let repl_code_ptr_type: Type = parse_quote! { ReplCodePtr };
|
||||
|
||||
if ty == &clause_type {
|
||||
Some(ClauseType::to_derive_input())
|
||||
@@ -821,7 +826,7 @@ fn derive_input(ty: &Type) -> Option<DeriveInput> {
|
||||
} else if ty == &compare_term_type {
|
||||
Some(CompareTerm::to_derive_input())
|
||||
} else if ty == &repl_code_ptr_type {
|
||||
Some(REPLCodePtr::to_derive_input())
|
||||
Some(ReplCodePtr::to_derive_input())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -897,13 +902,13 @@ fn generate_instruction_preface() -> TokenStream {
|
||||
}
|
||||
|
||||
impl ArithmeticTerm {
|
||||
fn into_functor(&self, arena: &mut Arena) -> MachineStub {
|
||||
fn into_functor(self, arena: &mut Arena) -> MachineStub {
|
||||
match self {
|
||||
&ArithmeticTerm::Reg(r) => reg_type_into_functor(r),
|
||||
&ArithmeticTerm::Interm(i) => {
|
||||
ArithmeticTerm::Reg(r) => reg_type_into_functor(r),
|
||||
ArithmeticTerm::Interm(i) => {
|
||||
functor!(atom!("intermediate"), [fixnum(i)])
|
||||
}
|
||||
&ArithmeticTerm::Number(n) => {
|
||||
ArithmeticTerm::Number(n) => {
|
||||
vec![HeapCellValue::from((n, arena))]
|
||||
}
|
||||
}
|
||||
@@ -925,26 +930,17 @@ fn generate_instruction_preface() -> TokenStream {
|
||||
impl NextOrFail {
|
||||
#[inline]
|
||||
pub fn is_next(&self) -> bool {
|
||||
if let NextOrFail::Next(_) = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
matches!(self, NextOrFail::Next(_))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum Death {
|
||||
Finite(usize),
|
||||
#[default]
|
||||
Infinity,
|
||||
}
|
||||
|
||||
impl Default for Death {
|
||||
fn default() -> Self {
|
||||
Death::Infinity
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum IndexedChoiceInstruction {
|
||||
Retry(usize),
|
||||
@@ -956,30 +952,30 @@ fn generate_instruction_preface() -> TokenStream {
|
||||
|
||||
impl IndexedChoiceInstruction {
|
||||
pub(crate) fn offset(&self) -> usize {
|
||||
match self {
|
||||
&IndexedChoiceInstruction::Retry(offset) => offset,
|
||||
&IndexedChoiceInstruction::Trust(offset) => offset,
|
||||
&IndexedChoiceInstruction::Try(offset) => offset,
|
||||
&IndexedChoiceInstruction::DefaultRetry(offset) => offset,
|
||||
&IndexedChoiceInstruction::DefaultTrust(offset) => offset,
|
||||
match *self {
|
||||
IndexedChoiceInstruction::Retry(offset) => offset,
|
||||
IndexedChoiceInstruction::Trust(offset) => offset,
|
||||
IndexedChoiceInstruction::Try(offset) => offset,
|
||||
IndexedChoiceInstruction::DefaultRetry(offset) => offset,
|
||||
IndexedChoiceInstruction::DefaultTrust(offset) => offset,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn to_functor(&self) -> MachineStub {
|
||||
pub(crate) fn to_functor(self) -> MachineStub {
|
||||
match self {
|
||||
&IndexedChoiceInstruction::Try(offset) => {
|
||||
IndexedChoiceInstruction::Try(offset) => {
|
||||
functor!(atom!("try"), [fixnum(offset)])
|
||||
}
|
||||
&IndexedChoiceInstruction::Trust(offset) => {
|
||||
IndexedChoiceInstruction::Trust(offset) => {
|
||||
functor!(atom!("trust"), [fixnum(offset)])
|
||||
}
|
||||
&IndexedChoiceInstruction::Retry(offset) => {
|
||||
IndexedChoiceInstruction::Retry(offset) => {
|
||||
functor!(atom!("retry"), [fixnum(offset)])
|
||||
}
|
||||
&IndexedChoiceInstruction::DefaultTrust(offset) => {
|
||||
IndexedChoiceInstruction::DefaultTrust(offset) => {
|
||||
functor!(atom!("default_trust"), [fixnum(offset)])
|
||||
}
|
||||
&IndexedChoiceInstruction::DefaultRetry(offset) => {
|
||||
IndexedChoiceInstruction::DefaultRetry(offset) => {
|
||||
functor!(atom!("default_retry"), [fixnum(offset)])
|
||||
}
|
||||
}
|
||||
@@ -987,6 +983,7 @@ fn generate_instruction_preface() -> TokenStream {
|
||||
}
|
||||
|
||||
/// `IndexingInstruction` cf. page 110 of wambook.
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum IndexingInstruction {
|
||||
// The first index is the optimal argument being indexed.
|
||||
@@ -1021,6 +1018,13 @@ fn generate_instruction_preface() -> TokenStream {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_external(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
IndexingCodePtr::External(_) | IndexingCodePtr::DynamicExternal(_)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexingInstruction {
|
||||
@@ -1038,7 +1042,7 @@ fn generate_instruction_preface() -> TokenStream {
|
||||
]
|
||||
)
|
||||
}
|
||||
&IndexingInstruction::SwitchOnConstant(ref constants) => {
|
||||
IndexingInstruction::SwitchOnConstant(constants) => {
|
||||
let mut key_value_list_stub = vec![];
|
||||
let orig_h = h;
|
||||
|
||||
@@ -1066,7 +1070,7 @@ fn generate_instruction_preface() -> TokenStream {
|
||||
[key_value_list_stub]
|
||||
)
|
||||
}
|
||||
&IndexingInstruction::SwitchOnStructure(ref structures) => {
|
||||
IndexingInstruction::SwitchOnStructure(structures) => {
|
||||
let mut key_value_list_stub = vec![];
|
||||
let orig_h = h;
|
||||
|
||||
@@ -1159,6 +1163,33 @@ fn generate_instruction_preface() -> TokenStream {
|
||||
pub type CodeDeque = VecDeque<Instruction>;
|
||||
|
||||
impl Instruction {
|
||||
#[inline]
|
||||
pub fn registers(&self) -> Vec<RegType> {
|
||||
match *self {
|
||||
Instruction::GetConstant(_, _, r) => vec![r],
|
||||
Instruction::GetList(_, r) => vec![r],
|
||||
Instruction::GetPartialString(_, _, r, _) => vec![r],
|
||||
Instruction::GetStructure(_, _, _, r) => vec![r],
|
||||
Instruction::GetVariable(r, t) => vec![r, temp_v!(t)],
|
||||
Instruction::GetValue(r, t) => vec![r, temp_v!(t)],
|
||||
Instruction::UnifyLocalValue(r) => vec![r],
|
||||
Instruction::UnifyVariable(r) => vec![r],
|
||||
Instruction::PutConstant(_, _, r) => vec![r],
|
||||
Instruction::PutList(_, r) => vec![r],
|
||||
Instruction::PutPartialString(_, _, r, _) => vec![r],
|
||||
Instruction::PutStructure(_, _, r) => vec![r],
|
||||
Instruction::PutValue(r, t) => vec![r, temp_v!(t)],
|
||||
Instruction::PutVariable(r, t) => vec![r, temp_v!(t)],
|
||||
Instruction::SetLocalValue(r) => vec![r],
|
||||
Instruction::SetVariable(r) => vec![r],
|
||||
Instruction::SetValue(r) => vec![r],
|
||||
Instruction::GetLevel(r) => vec![r],
|
||||
Instruction::GetPrevLevel(r) => vec![r],
|
||||
Instruction::GetCutPoint(r) => vec![r],
|
||||
_ => vec![],
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_indexing_line_mut(&mut self) -> Option<&mut Vec<IndexingLine>> {
|
||||
match self {
|
||||
@@ -1177,7 +1208,7 @@ fn generate_instruction_preface() -> TokenStream {
|
||||
|
||||
#[inline]
|
||||
pub fn is_head_instr(&self) -> bool {
|
||||
match self {
|
||||
matches!(self,
|
||||
Instruction::Deallocate |
|
||||
Instruction::GetConstant(..) |
|
||||
Instruction::GetList(..) |
|
||||
@@ -1201,9 +1232,10 @@ fn generate_instruction_preface() -> TokenStream {
|
||||
Instruction::SetLocalValue(..) |
|
||||
Instruction::SetVariable(..) |
|
||||
Instruction::SetValue(..) |
|
||||
Instruction::SetVoid(..) => true,
|
||||
_ => false,
|
||||
}
|
||||
Instruction::SetVoid(..) |
|
||||
Instruction::GetLevel(..) |
|
||||
Instruction::GetPrevLevel(..) |
|
||||
Instruction::GetCutPoint(..))
|
||||
}
|
||||
|
||||
pub fn enqueue_functors(
|
||||
@@ -1213,7 +1245,7 @@ fn generate_instruction_preface() -> TokenStream {
|
||||
functors: &mut Vec<MachineStub>,
|
||||
) {
|
||||
match self {
|
||||
&Instruction::IndexingCode(ref indexing_instrs) => {
|
||||
Instruction::IndexingCode(indexing_instrs) => {
|
||||
for indexing_instr in indexing_instrs {
|
||||
match indexing_instr {
|
||||
IndexingLine::Indexing(indexing_instr) => {
|
||||
@@ -1248,8 +1280,8 @@ fn generate_instruction_preface() -> TokenStream {
|
||||
&Instruction::InstallVerifyAttr => {
|
||||
functor!(atom!("install_verify_attr"))
|
||||
}
|
||||
&Instruction::VerifyAttrInterrupt => {
|
||||
functor!(atom!("verify_attr_interrupt"))
|
||||
&Instruction::VerifyAttrInterrupt(arity) => {
|
||||
functor!(atom!("verify_attr_interrupt"), [fixnum(arity)])
|
||||
}
|
||||
&Instruction::DynamicElse(birth, death, next_or_fail) => {
|
||||
match (death, next_or_fail) {
|
||||
@@ -1728,6 +1760,7 @@ fn generate_instruction_preface() -> TokenStream {
|
||||
&Instruction::CallHeadIsDynamic |
|
||||
&Instruction::CallInstallSCCCleaner |
|
||||
&Instruction::CallInstallInferenceCounter |
|
||||
&Instruction::CallInferenceCount |
|
||||
&Instruction::CallLiftedHeapLength |
|
||||
&Instruction::CallLoadLibraryAsStream |
|
||||
&Instruction::CallModuleExists |
|
||||
@@ -1782,6 +1815,7 @@ fn generate_instruction_preface() -> TokenStream {
|
||||
&Instruction::CallLoadForeignLib |
|
||||
&Instruction::CallForeignCall |
|
||||
&Instruction::CallDefineForeignStruct |
|
||||
&Instruction::CallJsEval |
|
||||
&Instruction::CallPredicateDefined |
|
||||
&Instruction::CallStripModule |
|
||||
&Instruction::CallCurrentTime |
|
||||
@@ -1822,6 +1856,7 @@ fn generate_instruction_preface() -> TokenStream {
|
||||
&Instruction::CallScryerPrologVersion |
|
||||
&Instruction::CallCryptoRandomByte |
|
||||
&Instruction::CallCryptoDataHash |
|
||||
&Instruction::CallCryptoHMAC |
|
||||
&Instruction::CallCryptoDataHKDF |
|
||||
&Instruction::CallCryptoPasswordHash |
|
||||
&Instruction::CallCryptoCurveScalarMult |
|
||||
@@ -1833,7 +1868,7 @@ fn generate_instruction_preface() -> TokenStream {
|
||||
&Instruction::CallSetEnv |
|
||||
&Instruction::CallUnsetEnv |
|
||||
&Instruction::CallShell |
|
||||
&Instruction::CallPID |
|
||||
&Instruction::CallPid |
|
||||
&Instruction::CallCharsBase64 |
|
||||
&Instruction::CallDevourWhitespace |
|
||||
&Instruction::CallIsSTOEnabled |
|
||||
@@ -1876,18 +1911,18 @@ fn generate_instruction_preface() -> TokenStream {
|
||||
&Instruction::CallFlushTermQueue |
|
||||
&Instruction::CallRemoveModuleExports |
|
||||
&Instruction::CallAddNonCountedBacktracking |
|
||||
&Instruction::CallPopCount => {
|
||||
&Instruction::CallPopCount |
|
||||
&Instruction::CallArgv |
|
||||
&Instruction::CallEd25519SignRaw |
|
||||
&Instruction::CallEd25519VerifyRaw |
|
||||
&Instruction::CallEd25519SeedToPublicKey => {
|
||||
let (name, arity) = self.to_name_and_arity();
|
||||
functor!(atom!("call"), [atom(name), fixnum(arity)])
|
||||
}
|
||||
//
|
||||
#[cfg(feature = "crypto-full")]
|
||||
&Instruction::CallCryptoDataEncrypt |
|
||||
&Instruction::CallCryptoDataDecrypt |
|
||||
&Instruction::CallEd25519Sign |
|
||||
&Instruction::CallEd25519Verify |
|
||||
&Instruction::CallEd25519NewKeyPair |
|
||||
&Instruction::CallEd25519KeyPairPublicKey => {
|
||||
&Instruction::CallCryptoDataDecrypt => {
|
||||
let (name, arity) = self.to_name_and_arity();
|
||||
functor!(atom!("call"), [atom(name), fixnum(arity)])
|
||||
}
|
||||
@@ -1962,6 +1997,7 @@ fn generate_instruction_preface() -> TokenStream {
|
||||
&Instruction::ExecuteHeadIsDynamic |
|
||||
&Instruction::ExecuteInstallSCCCleaner |
|
||||
&Instruction::ExecuteInstallInferenceCounter |
|
||||
&Instruction::ExecuteInferenceCount |
|
||||
&Instruction::ExecuteLiftedHeapLength |
|
||||
&Instruction::ExecuteLoadLibraryAsStream |
|
||||
&Instruction::ExecuteModuleExists |
|
||||
@@ -2016,6 +2052,7 @@ fn generate_instruction_preface() -> TokenStream {
|
||||
&Instruction::ExecuteLoadForeignLib |
|
||||
&Instruction::ExecuteForeignCall |
|
||||
&Instruction::ExecuteDefineForeignStruct |
|
||||
&Instruction::ExecuteJsEval |
|
||||
&Instruction::ExecutePredicateDefined |
|
||||
&Instruction::ExecuteStripModule |
|
||||
&Instruction::ExecuteCurrentTime |
|
||||
@@ -2056,6 +2093,7 @@ fn generate_instruction_preface() -> TokenStream {
|
||||
&Instruction::ExecuteScryerPrologVersion |
|
||||
&Instruction::ExecuteCryptoRandomByte |
|
||||
&Instruction::ExecuteCryptoDataHash |
|
||||
&Instruction::ExecuteCryptoHMAC |
|
||||
&Instruction::ExecuteCryptoDataHKDF |
|
||||
&Instruction::ExecuteCryptoPasswordHash |
|
||||
&Instruction::ExecuteCryptoCurveScalarMult |
|
||||
@@ -2067,7 +2105,7 @@ fn generate_instruction_preface() -> TokenStream {
|
||||
&Instruction::ExecuteSetEnv |
|
||||
&Instruction::ExecuteUnsetEnv |
|
||||
&Instruction::ExecuteShell |
|
||||
&Instruction::ExecutePID |
|
||||
&Instruction::ExecutePid |
|
||||
&Instruction::ExecuteCharsBase64 |
|
||||
&Instruction::ExecuteDevourWhitespace |
|
||||
&Instruction::ExecuteIsSTOEnabled |
|
||||
@@ -2110,18 +2148,18 @@ fn generate_instruction_preface() -> TokenStream {
|
||||
&Instruction::ExecuteFlushTermQueue |
|
||||
&Instruction::ExecuteRemoveModuleExports |
|
||||
&Instruction::ExecuteAddNonCountedBacktracking |
|
||||
&Instruction::ExecutePopCount => {
|
||||
&Instruction::ExecutePopCount |
|
||||
&Instruction::ExecuteArgv |
|
||||
&Instruction::ExecuteEd25519SignRaw |
|
||||
&Instruction::ExecuteEd25519VerifyRaw |
|
||||
&Instruction::ExecuteEd25519SeedToPublicKey => {
|
||||
let (name, arity) = self.to_name_and_arity();
|
||||
functor!(atom!("execute"), [atom(name), fixnum(arity)])
|
||||
}
|
||||
//
|
||||
#[cfg(feature = "crypto-full")]
|
||||
&Instruction::ExecuteCryptoDataEncrypt |
|
||||
&Instruction::ExecuteCryptoDataDecrypt |
|
||||
&Instruction::ExecuteEd25519Sign |
|
||||
&Instruction::ExecuteEd25519Verify |
|
||||
&Instruction::ExecuteEd25519NewKeyPair |
|
||||
&Instruction::ExecuteEd25519KeyPairPublicKey => {
|
||||
&Instruction::ExecuteCryptoDataDecrypt => {
|
||||
let (name, arity) = self.to_name_and_arity();
|
||||
functor!(atom!("execute"), [atom(name), fixnum(arity)])
|
||||
}
|
||||
@@ -2320,7 +2358,7 @@ pub fn generate_instructions_rs() -> TokenStream {
|
||||
let builtin_type_variants = attributeless_enum::<BuiltInClauseType>();
|
||||
let inlined_type_variants = attributeless_enum::<InlinedClauseType>();
|
||||
let system_clause_type_variants = attributeless_enum::<SystemClauseType>();
|
||||
let repl_code_ptr_variants = attributeless_enum::<REPLCodePtr>();
|
||||
let repl_code_ptr_variants = attributeless_enum::<ReplCodePtr>();
|
||||
let compare_number_variants = attributeless_enum::<CompareNumber>();
|
||||
let compare_term_variants = attributeless_enum::<CompareTerm>();
|
||||
|
||||
@@ -2331,7 +2369,7 @@ pub fn generate_instructions_rs() -> TokenStream {
|
||||
let mut is_inlined_arms = vec![];
|
||||
|
||||
is_inbuilt_arms.push(quote! {
|
||||
(atom!(":-"), 1 | 2) => true
|
||||
(atom!(":-"), 1 | 2)
|
||||
});
|
||||
|
||||
for (name, arity, variant) in instr_data.compare_number_variants {
|
||||
@@ -2388,11 +2426,11 @@ pub fn generate_instructions_rs() -> TokenStream {
|
||||
});
|
||||
|
||||
is_inbuilt_arms.push(quote! {
|
||||
(atom!(#name), #arity) => true
|
||||
(atom!(#name), #arity)
|
||||
});
|
||||
|
||||
is_inlined_arms.push(quote! {
|
||||
(atom!(#name), #arity) => true
|
||||
(atom!(#name), #arity)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2421,7 +2459,7 @@ pub fn generate_instructions_rs() -> TokenStream {
|
||||
});
|
||||
|
||||
is_inbuilt_arms.push(quote! {
|
||||
(atom!(#name), #arity) => true
|
||||
(atom!(#name), #arity)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2487,7 +2525,7 @@ pub fn generate_instructions_rs() -> TokenStream {
|
||||
});
|
||||
|
||||
is_inbuilt_arms.push(quote! {
|
||||
(atom!(#name), #arity) => true
|
||||
(atom!(#name), #arity)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2549,16 +2587,17 @@ pub fn generate_instructions_rs() -> TokenStream {
|
||||
});
|
||||
|
||||
is_inbuilt_arms.push(quote! {
|
||||
(atom!(#name), #arity) => true
|
||||
(atom!(#name), #arity)
|
||||
});
|
||||
|
||||
is_inlined_arms.push(quote! {
|
||||
(atom!(#name), #arity) => true
|
||||
(atom!(#name), #arity)
|
||||
});
|
||||
}
|
||||
|
||||
for (name, arity, variant) in instr_data.system_clause_type_variants {
|
||||
let ident = variant.ident.clone();
|
||||
let ident_s = ident.to_string();
|
||||
|
||||
let variant_fields: Vec<_> = variant
|
||||
.fields
|
||||
@@ -2574,19 +2613,13 @@ pub fn generate_instructions_rs() -> TokenStream {
|
||||
.collect();
|
||||
|
||||
clause_type_from_name_and_arity_arms.push(if !variant_fields.is_empty() {
|
||||
if ident.to_string() == "SetCutPoint" {
|
||||
if ident_s == "SetCutPoint" || ident_s == "SetCutPointByDefault" {
|
||||
quote! {
|
||||
(atom!(#name), #arity) => ClauseType::System(
|
||||
SystemClauseType::#ident(temp_v!(1))
|
||||
)
|
||||
}
|
||||
} else if ident.to_string() == "SetCutPointByDefault" {
|
||||
quote! {
|
||||
(atom!(#name), #arity) => ClauseType::System(
|
||||
SystemClauseType::#ident(temp_v!(1))
|
||||
)
|
||||
}
|
||||
} else if ident.to_string() == "InlineCallN" {
|
||||
} else if ident_s == "InlineCallN" {
|
||||
quote! {
|
||||
(atom!(#name), arity) => ClauseType::System(
|
||||
SystemClauseType::#ident(arity)
|
||||
@@ -2649,11 +2682,11 @@ pub fn generate_instructions_rs() -> TokenStream {
|
||||
|
||||
is_inbuilt_arms.push(if let Arity::Ident("arity") = &arity {
|
||||
quote! {
|
||||
(atom!(#name), _arity) => true
|
||||
(atom!(#name), _)
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
(atom!(#name), #arity) => true
|
||||
(atom!(#name), #arity)
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -2676,14 +2709,14 @@ pub fn generate_instructions_rs() -> TokenStream {
|
||||
|
||||
clause_type_from_name_and_arity_arms.push(if !variant_fields.is_empty() {
|
||||
quote! {
|
||||
(atom!(#name), #arity) => ClauseType::System(SystemClauseType::REPL(
|
||||
REPLCodePtr::#ident(#(#variant_fields),*)
|
||||
(atom!(#name), #arity) => ClauseType::System(SystemClauseType::Repl(
|
||||
ReplCodePtr::#ident(#(#variant_fields),*)
|
||||
))
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
(atom!(#name), #arity) => ClauseType::System(SystemClauseType::REPL(
|
||||
REPLCodePtr::#ident
|
||||
(atom!(#name), #arity) => ClauseType::System(SystemClauseType::Repl(
|
||||
ReplCodePtr::#ident
|
||||
))
|
||||
}
|
||||
});
|
||||
@@ -2691,13 +2724,13 @@ pub fn generate_instructions_rs() -> TokenStream {
|
||||
clause_type_name_arms.push(if !variant_fields.is_empty() {
|
||||
quote! {
|
||||
ClauseType::System(
|
||||
SystemClauseType::REPL(REPLCodePtr::#ident(..))
|
||||
SystemClauseType::Repl(ReplCodePtr::#ident(..))
|
||||
) => atom!(#name)
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
ClauseType::System(
|
||||
SystemClauseType::REPL(REPLCodePtr::#ident)
|
||||
SystemClauseType::Repl(ReplCodePtr::#ident)
|
||||
) => atom!(#name)
|
||||
}
|
||||
});
|
||||
@@ -2711,20 +2744,20 @@ pub fn generate_instructions_rs() -> TokenStream {
|
||||
|
||||
clause_type_to_instr_arms.push(if !variant_fields.is_empty() {
|
||||
quote! {
|
||||
ClauseType::System(SystemClauseType::REPL(
|
||||
REPLCodePtr::#ident(#(#placeholder_ids),*)
|
||||
ClauseType::System(SystemClauseType::Repl(
|
||||
ReplCodePtr::#ident(#(#placeholder_ids),*)
|
||||
)) => Instruction::#instr_ident(#(*#placeholder_ids),*)
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
ClauseType::System(SystemClauseType::REPL(
|
||||
REPLCodePtr::#ident
|
||||
ClauseType::System(SystemClauseType::Repl(
|
||||
ReplCodePtr::#ident
|
||||
)) => Instruction::#instr_ident
|
||||
}
|
||||
});
|
||||
|
||||
is_inbuilt_arms.push(quote! {
|
||||
(atom!(#name), #arity) => true
|
||||
(atom!(#name), #arity)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2798,7 +2831,7 @@ pub fn generate_instructions_rs() -> TokenStream {
|
||||
});
|
||||
|
||||
is_inbuilt_arms.push(quote! {
|
||||
(atom!(#name), _arity) => true
|
||||
(atom!(#name), _)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2819,8 +2852,8 @@ pub fn generate_instructions_rs() -> TokenStream {
|
||||
let placeholder_ids: Vec<_> =
|
||||
(0..enum_arity).map(|n| format_ident!("f_{}", n)).collect();
|
||||
|
||||
if variant_string.starts_with("Call") {
|
||||
let execute_ident = format_ident!("Execute{}", variant_string["Call".len()..]);
|
||||
if let Some(variant_suffix) = variant_string.strip_prefix("Call") {
|
||||
let execute_ident = format_ident!("Execute{}", variant_suffix);
|
||||
|
||||
Some(if enum_arity == 0 {
|
||||
quote! {
|
||||
@@ -2833,9 +2866,8 @@ pub fn generate_instructions_rs() -> TokenStream {
|
||||
Instruction::#execute_ident(#(#placeholder_ids),*)
|
||||
}
|
||||
})
|
||||
} else if variant_string.starts_with("DefaultCall") {
|
||||
let execute_ident =
|
||||
format_ident!("DefaultExecute{}", variant_string["DefaultCall".len()..]);
|
||||
} else if let Some(variant_suffix) = variant_string.strip_prefix("DefaultCall") {
|
||||
let execute_ident = format_ident!("DefaultExecute{}", variant_suffix);
|
||||
|
||||
Some(if enum_arity == 0 {
|
||||
quote! {
|
||||
@@ -2868,29 +2900,20 @@ pub fn generate_instructions_rs() -> TokenStream {
|
||||
0
|
||||
};
|
||||
|
||||
if variant_string.starts_with("Execute") {
|
||||
if variant_string.starts_with("Execute") || variant_string.starts_with("DefaultExecute")
|
||||
{
|
||||
Some(if enum_arity == 0 {
|
||||
quote! {
|
||||
Instruction::#variant_ident => true
|
||||
Instruction::#variant_ident
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
Instruction::#variant_ident(..) => true
|
||||
}
|
||||
})
|
||||
} else if variant_string.starts_with("DefaultExecute") {
|
||||
Some(if enum_arity == 0 {
|
||||
quote! {
|
||||
Instruction::#variant_ident => true
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
Instruction::#variant_ident(..) => true
|
||||
Instruction::#variant_ident(..)
|
||||
}
|
||||
})
|
||||
} else if variant_string == "JmpByExecute" {
|
||||
Some(quote! {
|
||||
Instruction::#variant_ident(..) => true
|
||||
Instruction::#variant_ident(..)
|
||||
})
|
||||
} else {
|
||||
None
|
||||
@@ -2955,11 +2978,11 @@ pub fn generate_instructions_rs() -> TokenStream {
|
||||
|
||||
Some(if enum_arity == 0 {
|
||||
quote! {
|
||||
Instruction::#variant_ident => true
|
||||
Instruction::#variant_ident
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
Instruction::#variant_ident(..) => true
|
||||
Instruction::#variant_ident(..)
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -2970,7 +2993,7 @@ pub fn generate_instructions_rs() -> TokenStream {
|
||||
.iter()
|
||||
.rev() // produce default, execute & default & execute cases first.
|
||||
.cloned()
|
||||
.filter_map(|(name, arity, _, variant)| {
|
||||
.map(|(name, arity, _, variant)| {
|
||||
let variant_ident = variant.ident.clone();
|
||||
let variant_string = variant.ident.to_string();
|
||||
let arity = match arity {
|
||||
@@ -2978,6 +3001,7 @@ pub fn generate_instructions_rs() -> TokenStream {
|
||||
_ => 1,
|
||||
};
|
||||
|
||||
#[allow(clippy::collapsible_else_if)]
|
||||
Some(if variant_string.starts_with("Execute") {
|
||||
if arity == 0 {
|
||||
quote! {
|
||||
@@ -3066,10 +3090,10 @@ pub fn generate_instructions_rs() -> TokenStream {
|
||||
|
||||
match arity {
|
||||
Arity::Static(_) if enum_arity == 0 => {
|
||||
quote! { &Instruction::#ident => (atom!(#name), #arity) }
|
||||
quote! { Instruction::#ident => (atom!(#name), #arity) }
|
||||
}
|
||||
Arity::Static(_) => {
|
||||
quote! { &Instruction::#ident(..) => (atom!(#name), #arity) }
|
||||
quote! { Instruction::#ident(..) => (atom!(#name), #arity) }
|
||||
}
|
||||
Arity::Ident(_) if enum_arity == 0 => {
|
||||
quote! { &Instruction::#ident(#arity) => (atom!(#name), #arity) }
|
||||
@@ -3086,6 +3110,7 @@ pub fn generate_instructions_rs() -> TokenStream {
|
||||
quote! {
|
||||
#preface_tokens
|
||||
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum CompareTerm {
|
||||
#(
|
||||
@@ -3093,6 +3118,7 @@ pub fn generate_instructions_rs() -> TokenStream {
|
||||
)*
|
||||
}
|
||||
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum CompareNumber {
|
||||
#(
|
||||
@@ -3138,7 +3164,7 @@ pub fn generate_instructions_rs() -> TokenStream {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum REPLCodePtr {
|
||||
pub enum ReplCodePtr {
|
||||
#(
|
||||
#repl_code_ptr_variants,
|
||||
)*
|
||||
@@ -3169,12 +3195,9 @@ pub fn generate_instructions_rs() -> TokenStream {
|
||||
}
|
||||
|
||||
pub fn is_inbuilt(name: Atom, arity: usize) -> bool {
|
||||
match (name, arity) {
|
||||
#(
|
||||
#is_inbuilt_arms,
|
||||
)*
|
||||
_ => false,
|
||||
}
|
||||
matches!((name, arity),
|
||||
#(#is_inbuilt_arms)|*
|
||||
)
|
||||
}
|
||||
|
||||
pub fn name(&self) -> Atom {
|
||||
@@ -3186,12 +3209,9 @@ pub fn generate_instructions_rs() -> TokenStream {
|
||||
}
|
||||
|
||||
pub fn is_inlined(name: Atom, arity: usize) -> bool {
|
||||
match (name, arity) {
|
||||
#(
|
||||
#is_inlined_arms,
|
||||
)*
|
||||
_ => false,
|
||||
}
|
||||
matches!((name, arity),
|
||||
#(#is_inlined_arms)|*
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3211,7 +3231,7 @@ pub fn generate_instructions_rs() -> TokenStream {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_default(self) -> Instruction {
|
||||
pub fn into_default(self) -> Instruction {
|
||||
match self {
|
||||
#(
|
||||
#to_default_arms,
|
||||
@@ -3220,7 +3240,7 @@ pub fn generate_instructions_rs() -> TokenStream {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_execute(self) -> Instruction {
|
||||
pub fn into_execute(self) -> Instruction {
|
||||
match self {
|
||||
#(
|
||||
#to_execute_arms,
|
||||
@@ -3230,29 +3250,25 @@ pub fn generate_instructions_rs() -> TokenStream {
|
||||
}
|
||||
|
||||
pub fn is_execute(&self) -> bool {
|
||||
match self {
|
||||
#(
|
||||
#is_execute_arms,
|
||||
)*
|
||||
_ => false,
|
||||
}
|
||||
matches!(self,
|
||||
#(#is_execute_arms)|*
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn is_ctrl_instr(&self) -> bool {
|
||||
match self {
|
||||
&Instruction::Allocate(_) |
|
||||
&Instruction::Deallocate |
|
||||
&Instruction::Proceed |
|
||||
&Instruction::RevJmpBy(_) => true,
|
||||
#(
|
||||
#control_flow_arms,
|
||||
)*
|
||||
_ => false,
|
||||
}
|
||||
matches!(self,
|
||||
Instruction::Allocate(_) |
|
||||
Instruction::Deallocate |
|
||||
Instruction::Proceed |
|
||||
Instruction::RevJmpBy(_) |
|
||||
#(#control_flow_arms)|*
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn is_query_instr(&self) -> bool {
|
||||
match self {
|
||||
matches!(self,
|
||||
&Instruction::GetVariable(..) |
|
||||
&Instruction::PutConstant(..) |
|
||||
&Instruction::PutList(..) |
|
||||
@@ -3265,20 +3281,19 @@ pub fn generate_instructions_rs() -> TokenStream {
|
||||
&Instruction::SetLocalValue(..) |
|
||||
&Instruction::SetVariable(..) |
|
||||
&Instruction::SetValue(..) |
|
||||
&Instruction::SetVoid(..) => true,
|
||||
_ => false,
|
||||
}
|
||||
&Instruction::SetVoid(..)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! _instr {
|
||||
#(
|
||||
#instr_macro_arms
|
||||
);*
|
||||
}
|
||||
|
||||
pub use _instr as instr; // https://github.com/rust-lang/rust/pull/52234#issuecomment-976702997
|
||||
// https://github.com/rust-lang/rust/pull/52234#issuecomment-976702997
|
||||
pub(crate) use _instr as instr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3335,7 +3350,8 @@ enum Arity {
|
||||
|
||||
impl From<&'static str> for Arity {
|
||||
fn from(arity: &'static str) -> Self {
|
||||
usize::from_str_radix(&arity, 10)
|
||||
arity
|
||||
.parse::<usize>()
|
||||
.map(Arity::Static)
|
||||
.unwrap_or_else(|_| Arity::Ident(arity))
|
||||
}
|
||||
@@ -3405,8 +3421,8 @@ impl InstructionData {
|
||||
);
|
||||
|
||||
(name, arity, CountableInference::NotCounted)
|
||||
} else if id == "REPLCodePtr" {
|
||||
let (name, arity) = add_discriminant_data::<REPLCodePtrDiscriminants>(
|
||||
} else if id == "ReplCodePtr" {
|
||||
let (name, arity) = add_discriminant_data::<ReplCodePtrDiscriminants>(
|
||||
&variant,
|
||||
prefix,
|
||||
&mut self.repl_code_ptr_variants,
|
||||
@@ -3442,13 +3458,12 @@ impl InstructionData {
|
||||
panic!("type ID is: {}", id);
|
||||
};
|
||||
|
||||
let v_string = variant.ident.to_string();
|
||||
|
||||
let v_ident = if v_string.starts_with("Call") {
|
||||
format_ident!("{}", v_string["Call".len()..])
|
||||
} else {
|
||||
variant.ident.clone()
|
||||
};
|
||||
let v_ident = variant
|
||||
.ident
|
||||
.to_string()
|
||||
.strip_prefix("Call")
|
||||
.map(|s| format_ident!("{}", s))
|
||||
.unwrap_or_else(|| variant.ident.clone());
|
||||
|
||||
let generated_variant =
|
||||
create_instr_variant(format_ident!("{}{}", prefix, v_ident), variant.clone());
|
||||
|
||||
@@ -5,40 +5,40 @@ use instructions_template::generate_instructions_rs;
|
||||
use static_string_indexing::index_static_strings;
|
||||
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
fn find_prolog_files(libraries: &mut File, prefix: &str, current_dir: &Path) {
|
||||
fn find_prolog_files(path_prefix: &str, current_dir: &Path) -> Vec<(String, PathBuf)> {
|
||||
let mut libraries = vec![];
|
||||
|
||||
let entries = match current_dir.read_dir() {
|
||||
Ok(entries) => entries,
|
||||
Err(_) => return,
|
||||
Err(_) => return libraries,
|
||||
};
|
||||
|
||||
for entry in entries.filter_map(Result::ok).map(|e| e.path()) {
|
||||
if entry.is_dir() {
|
||||
if let Some(file_name) = entry.file_name() {
|
||||
let new_prefix = prefix.to_owned() + file_name.to_str().unwrap() + "/";
|
||||
find_prolog_files(libraries, &new_prefix, &entry);
|
||||
let file_name = file_name.to_str().unwrap();
|
||||
let new_path_prefix = format!("{path_prefix}{file_name}/");
|
||||
let new_libs = find_prolog_files(&new_path_prefix, &entry);
|
||||
libraries.extend(new_libs);
|
||||
}
|
||||
} else if entry.is_file() {
|
||||
let ext = std::ffi::OsStr::new("pl");
|
||||
if entry.extension() == Some(ext) {
|
||||
let contain = String::from_utf8(fs::read(&entry).unwrap()).unwrap();
|
||||
let name = entry.file_stem().unwrap().to_str().unwrap();
|
||||
let lib_name = format!("{path_prefix}{name}");
|
||||
|
||||
let line = format!(
|
||||
" m.insert(\"{}\",\n{:?});\n",
|
||||
prefix.to_owned() + name,
|
||||
contain
|
||||
);
|
||||
|
||||
libraries.write_all(line.as_bytes()).unwrap();
|
||||
libraries.push((lib_name, entry));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
libraries
|
||||
}
|
||||
|
||||
fn main() {
|
||||
@@ -55,19 +55,41 @@ fn main() {
|
||||
let out_dir = env::var("OUT_DIR").unwrap();
|
||||
let dest_path = Path::new(&out_dir).join("libraries.rs");
|
||||
|
||||
let mut libraries = File::create(&dest_path).unwrap();
|
||||
let lib_path = Path::new("src/lib");
|
||||
let mut libraries = File::create(dest_path).unwrap();
|
||||
let lib_path = Path::new("src").join("lib");
|
||||
|
||||
libraries
|
||||
.write_all(
|
||||
b"ref_thread_local::ref_thread_local! {
|
||||
pub(crate) static managed LIBRARIES: IndexMap<&'static str, &'static str> = {
|
||||
let mut m = IndexMap::new();\n",
|
||||
)
|
||||
.unwrap();
|
||||
let constants = find_prolog_files("", &lib_path);
|
||||
|
||||
find_prolog_files(&mut libraries, "", &lib_path);
|
||||
libraries.write_all(b"\n m\n };\n}\n").unwrap();
|
||||
let out_dir = std::env::var("OUT_DIR").unwrap();
|
||||
let out_dir_path: &Path = out_dir.as_ref();
|
||||
let manifest_dir = &std::env::var("CARGO_MANIFEST_DIR").unwrap();
|
||||
let manifest_dir_path: &Path = manifest_dir.as_ref();
|
||||
|
||||
let prefix: PathBuf = if let Ok(diff) = out_dir_path.strip_prefix(manifest_dir_path) {
|
||||
let mut path = PathBuf::from(".");
|
||||
for comp in diff.components() {
|
||||
match comp {
|
||||
std::path::Component::Normal(_) => path.push(".."),
|
||||
std::path::Component::CurDir => (),
|
||||
std::path::Component::Prefix(_)
|
||||
| std::path::Component::RootDir
|
||||
| std::path::Component::ParentDir => {
|
||||
path = manifest_dir_path.to_path_buf();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
path
|
||||
} else {
|
||||
manifest_dir_path.to_path_buf()
|
||||
};
|
||||
|
||||
writeln!(libraries, "{{").unwrap();
|
||||
for (name, lib_path) in constants {
|
||||
let path: PathBuf = prefix.join(lib_path);
|
||||
writeln!(libraries, "m.insert(\"{name}\", include_str!({path:?}));").unwrap();
|
||||
}
|
||||
writeln!(libraries, "}}").unwrap();
|
||||
|
||||
let instructions_path = Path::new(&out_dir).join("instructions.rs");
|
||||
let mut instructions_file = File::create(&instructions_path).unwrap();
|
||||
|
||||
@@ -35,7 +35,7 @@ impl Parse for ReadHeapCellExprAndArms {
|
||||
arms.push(input.parse()?);
|
||||
|
||||
while !input.is_empty() {
|
||||
if let Ok(_) = input.parse::<Token![,]>() {}
|
||||
let _ = input.parse::<Token![,]>();
|
||||
arms.push(input.parse()?);
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ impl Parse for MacroFnArgs {
|
||||
}
|
||||
|
||||
while !input.is_empty() {
|
||||
if let Ok(_) = input.parse::<Token![,]>() {}
|
||||
let _ = input.parse::<Token![,]>();
|
||||
args.push(input.parse()?);
|
||||
}
|
||||
|
||||
@@ -65,22 +65,20 @@ impl<'ast> Visit<'ast> for StaticStrVisitor {
|
||||
let Macro { path, .. } = m;
|
||||
|
||||
if path.is_ident("atom") {
|
||||
if let Some(Lit::Str(string)) = m.parse_body::<Lit>().ok() {
|
||||
if let Ok(Lit::Str(string)) = m.parse_body::<Lit>() {
|
||||
self.static_strs.insert(string.value());
|
||||
}
|
||||
} else if path.is_ident("read_heap_cell") || path.is_ident("match_untyped_arena_ptr") {
|
||||
if let Some(m) = m.parse_body::<ReadHeapCellExprAndArms>().ok() {
|
||||
if let Ok(m) = m.parse_body::<ReadHeapCellExprAndArms>() {
|
||||
self.visit_expr(&m.expr);
|
||||
|
||||
for e in m.arms {
|
||||
self.visit_arm(&e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let Some(m) = m.parse_body::<MacroFnArgs>().ok() {
|
||||
for e in m.args {
|
||||
self.visit_expr(&e);
|
||||
}
|
||||
} else if let Ok(m) = m.parse_body::<MacroFnArgs>() {
|
||||
for e in m.args {
|
||||
self.visit_expr(&e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -147,9 +145,8 @@ pub fn index_static_strings(instruction_rs_path: &std::path::Path) -> TokenStrea
|
||||
visitor.visit_file(&syntax);
|
||||
}
|
||||
|
||||
match process_filepath(instruction_rs_path) {
|
||||
Ok(syntax) => visitor.visit_file(&syntax),
|
||||
Err(_) => {}
|
||||
if let Ok(syntax) = process_filepath(instruction_rs_path) {
|
||||
visitor.visit_file(&syntax)
|
||||
}
|
||||
|
||||
let indices = (0..visitor.static_strs.len()).map(|i| (i << 3) as u64);
|
||||
@@ -159,15 +156,12 @@ pub fn index_static_strings(instruction_rs_path: &std::path::Path) -> TokenStrea
|
||||
let static_strs: &Vec<_> = &visitor.static_strs.into_iter().collect();
|
||||
|
||||
quote! {
|
||||
use phf;
|
||||
|
||||
static STRINGS: [&'static str; #static_strs_len] = [
|
||||
static STRINGS: [&str; #static_strs_len] = [
|
||||
#(
|
||||
#static_strs,
|
||||
)*
|
||||
];
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! atom {
|
||||
#((#static_strs) => { Atom { index: #indices_iter } };)*
|
||||
}
|
||||
|
||||
11
doclog.config.pl
Normal file
11
doclog.config.pl
Normal file
@@ -0,0 +1,11 @@
|
||||
project_name("Scryer Prolog").
|
||||
readme_file("INDEX.dj").
|
||||
source_lib_folder("src/lib").
|
||||
websource("https://github.com/mthom/scryer-prolog/tree/master/src/lib").
|
||||
omit(["ops_and_meta_predicates.pl", "tabling"]).
|
||||
learn_pages_source_folder("learn").
|
||||
learn_pages_categories(["First steps"]).
|
||||
learn_pages([
|
||||
page("Test page", "First steps", "test-page.dj")
|
||||
]).
|
||||
copy_file("logo/scryer.png", "scryer.png").
|
||||
82
flake.lock
generated
Normal file
82
flake.lock
generated
Normal file
@@ -0,0 +1,82 @@
|
||||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1710146030,
|
||||
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1723541349,
|
||||
"narHash": "sha256-LrmeqqHdPgAJsVKIJja8jGgRG/CA2y6SGT2TjX5Do68=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "4877ea239f4d02410c3516101faf35a81af0c30e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"rust-overlay": "rust-overlay"
|
||||
}
|
||||
},
|
||||
"rust-overlay": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1723515680,
|
||||
"narHash": "sha256-nHdKymsHCVIh0Wdm4MvSgxcTTg34FJIYHRQkQYaSuvk=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "4ee3d9e9569f70d7bb40f28804d6fe950c81eab3",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
69
flake.nix
Normal file
69
flake.nix
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
description = "A modern Prolog implementation written mostly in Rust";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
rust-overlay = {
|
||||
url = "github:oxalica/rust-overlay";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
};
|
||||
|
||||
outputs = { nixpkgs, flake-utils, rust-overlay, ... }:
|
||||
let
|
||||
meta = (builtins.fromTOML (builtins.readFile ./Cargo.toml)).package;
|
||||
inherit (meta) name version;
|
||||
overlays = [
|
||||
(import rust-overlay)
|
||||
(self: super: {
|
||||
rustToolchainDev = super.rust-bin.stable.latest.default.override {
|
||||
extensions = [ "rust-src" "rust-analyzer" ];
|
||||
};
|
||||
rustToolchainNightly = super.rust-bin.selectLatestNightlyWith (toolchain:
|
||||
toolchain.default.override {
|
||||
extensions = [ "rust-src" "rust-analyzer" "miri" ];
|
||||
}
|
||||
);
|
||||
})
|
||||
];
|
||||
in flake-utils.lib.eachDefaultSystem(system:
|
||||
let
|
||||
pkgs = import nixpkgs { inherit system overlays; };
|
||||
nativeBuildInputs = with pkgs; [ pkg-config ];
|
||||
buildInputs = with pkgs; [ openssl ];
|
||||
in
|
||||
{
|
||||
devShells = {
|
||||
default = pkgs.mkShell {
|
||||
nativeBuildInputs = nativeBuildInputs;
|
||||
buildInputs = buildInputs ++ (with pkgs; [
|
||||
rustToolchainDev
|
||||
]);
|
||||
};
|
||||
# For use with Miri and stuff like it
|
||||
nightly = pkgs.mkShell {
|
||||
nativeBuildInputs = nativeBuildInputs;
|
||||
buildInputs = buildInputs ++ (with pkgs; [
|
||||
rustToolchainNightly
|
||||
]);
|
||||
};
|
||||
};
|
||||
|
||||
packages = rec {
|
||||
default = scryer-prolog;
|
||||
scryer-prolog = pkgs.rustPlatform.buildRustPackage {
|
||||
pname = name;
|
||||
inherit version;
|
||||
src = ./.;
|
||||
nativeBuildInputs = nativeBuildInputs;
|
||||
buildInputs = buildInputs;
|
||||
cargoLock = {
|
||||
lockFile = ./Cargo.lock;
|
||||
};
|
||||
release = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
491
flamegraph.svg
491
flamegraph.svg
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 4.8 MiB |
3
learn/test-page.dj
Normal file
3
learn/test-page.dj
Normal file
@@ -0,0 +1,3 @@
|
||||
# Test page
|
||||
|
||||
This is a page about Scryer Prolog
|
||||
@@ -24,11 +24,12 @@ pub(crate) trait Allocator {
|
||||
code: &mut CodeDeque,
|
||||
);
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn mark_reserved_var<'a, Target: CompilationTarget<'a>>(
|
||||
&mut self,
|
||||
var_num: usize,
|
||||
lvl: Level,
|
||||
cell: &'a Cell<VarReg>,
|
||||
cell: &Cell<VarReg>,
|
||||
term_loc: GenContext,
|
||||
code: &mut CodeDeque,
|
||||
r: RegType,
|
||||
@@ -41,51 +42,16 @@ pub(crate) trait Allocator {
|
||||
&mut self,
|
||||
var_num: usize,
|
||||
lvl: Level,
|
||||
cell: &'a Cell<VarReg>,
|
||||
cell: &Cell<VarReg>,
|
||||
context: GenContext,
|
||||
code: &mut CodeDeque,
|
||||
);
|
||||
|
||||
fn reset(&mut self);
|
||||
fn reset_arg(&mut self, arg_num: usize);
|
||||
fn reset_at_head(&mut self, args: &Vec<Term>);
|
||||
fn reset_at_head(&mut self, args: &[Term]);
|
||||
fn reset_contents(&mut self);
|
||||
|
||||
fn advance_arg(&mut self);
|
||||
|
||||
/*
|
||||
fn bindings(&self) -> &AllocVarDict;
|
||||
fn bindings_mut(&mut self) -> &mut AllocVarDict;
|
||||
fn take_bindings(self) -> AllocVarDict;
|
||||
*/
|
||||
|
||||
fn max_reg_allocated(&self) -> usize;
|
||||
|
||||
// TODO: wha.. why?? grrr. it drains the VarStatus data from vs (which it owns!)
|
||||
// into self.bindings and perm_vs after all is computed (i.e. vs.populate_restricting_sets()
|
||||
// and vs.set_perm_vals(has_deep_cut) have both been called).
|
||||
/*
|
||||
fn drain_var_data<'a>(
|
||||
&mut self,
|
||||
vs: VariableFixtures,
|
||||
num_of_chunks: usize,
|
||||
) -> VariableFixtures {
|
||||
let mut perm_vs = VariableFixtures::new();
|
||||
|
||||
for (var, var_status) in vs.into_iter() {
|
||||
match var_status {
|
||||
VarStatus::Temp(chunk_num, tvd) => {
|
||||
self.bindings_mut()
|
||||
.insert(var.clone(), VarAlloc::Temp(chunk_num, 0, tvd));
|
||||
}
|
||||
VarStatus::Perm(_) => {
|
||||
self.bindings_mut().insert(var.clone(), VarAlloc::Perm(0));
|
||||
perm_vs.insert(var, var_status);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
perm_vs
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
653
src/arena.rs
653
src/arena.rs
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,5 @@
|
||||
#![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work
|
||||
|
||||
use crate::allocator::*;
|
||||
use crate::arena::*;
|
||||
use crate::atom_table::*;
|
||||
@@ -16,7 +18,7 @@ use crate::machine::machine_errors::*;
|
||||
use dashu::base::Abs;
|
||||
use dashu::base::BitTest;
|
||||
use num_order::NumOrd;
|
||||
use ordered_float::*;
|
||||
use ordered_float::{Float, OrderedFloat};
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::cmp::{max, min, Ordering};
|
||||
@@ -166,7 +168,7 @@ fn push_literal(interm: &mut Vec<ArithmeticTerm>, c: &Literal) -> Result<(), Ari
|
||||
Number::Float(OrderedFloat(std::f64::consts::PI)),
|
||||
)),
|
||||
Literal::Atom(name) if name == &atom!("epsilon") => interm.push(ArithmeticTerm::Number(
|
||||
Number::Float(OrderedFloat(std::f64::EPSILON)),
|
||||
Number::Float(OrderedFloat(f64::EPSILON)),
|
||||
)),
|
||||
_ => return Err(ArithmeticError::NonEvaluableFunctor(*c, 0)),
|
||||
}
|
||||
@@ -268,7 +270,7 @@ impl<'a> ArithmeticEvaluator<'a> {
|
||||
let ninterm = if a1.interm_or(0) == 0 {
|
||||
self.incr_interm()
|
||||
} else {
|
||||
self.interm.push(a1.clone());
|
||||
self.interm.push(a1);
|
||||
a1.interm_or(0)
|
||||
};
|
||||
|
||||
@@ -312,9 +314,8 @@ impl<'a> ArithmeticEvaluator<'a> {
|
||||
arg: usize,
|
||||
) -> Result<ArithCont, ArithmeticError> {
|
||||
let mut code = CodeDeque::new();
|
||||
let mut iter = src.iter()?;
|
||||
|
||||
while let Some(term_ref) = iter.next() {
|
||||
for term_ref in src.iter()? {
|
||||
match term_ref? {
|
||||
ArithTermRef::Literal(c) => push_literal(&mut self.interm, c)?,
|
||||
ArithTermRef::Var(lvl, cell, name) => {
|
||||
@@ -353,17 +354,17 @@ impl<'a> ArithmeticEvaluator<'a> {
|
||||
}
|
||||
|
||||
// integer division rounding function -- 9.1.3.1.
|
||||
pub(crate) fn rnd_i<'a>(n: &'a Number, arena: &mut Arena) -> Number {
|
||||
pub(crate) fn rnd_i(n: &'_ Number, arena: &mut Arena) -> Number {
|
||||
match n {
|
||||
&Number::Integer(i) => {
|
||||
let result = (&*i).try_into();
|
||||
if let Ok(value) = result{
|
||||
if let Ok(value) = result {
|
||||
fixnum!(Number, value, arena)
|
||||
} else {
|
||||
*n
|
||||
}
|
||||
}
|
||||
&Number::Fixnum(_) => *n,
|
||||
Number::Fixnum(_) => *n,
|
||||
&Number::Float(f) => {
|
||||
let f = f.floor();
|
||||
|
||||
@@ -376,7 +377,7 @@ pub(crate) fn rnd_i<'a>(n: &'a Number, arena: &mut Arena) -> Number {
|
||||
Number::Integer(arena_alloc!(Integer::from(f.0 as i64), arena))
|
||||
}
|
||||
}
|
||||
&Number::Rational(ref r) => {
|
||||
Number::Rational(ref r) => {
|
||||
let (_, floor) = (r.fract(), r.floor());
|
||||
|
||||
if let Ok(value) = (&floor).try_into() {
|
||||
@@ -399,9 +400,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(ref n) => n.to_f64().value(),
|
||||
&Number::Float(OrderedFloat(f)) => f,
|
||||
&Number::Rational(ref r) => r.to_f64().value(),
|
||||
Number::Rational(ref r) => r.to_f64().value(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -529,47 +530,33 @@ 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)) => Integer::from(n1.get_num()).num_eq(&**n2),
|
||||
(&Number::Rational(ref n1), &Number::Fixnum(n2)) => (&**n1).num_eq(&Integer::from(n2.get_num())),
|
||||
(&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)) => {
|
||||
Integer::from(n1.get_num()).num_eq(&**n2)
|
||||
}
|
||||
(Number::Rational(ref 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)) => {
|
||||
(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)) => {
|
||||
(&Number::Float(n1), Number::Integer(ref n2)) => {
|
||||
n1.eq(&OrderedFloat(n2.to_f64().value()))
|
||||
}
|
||||
(&Number::Integer(ref n1), &Number::Rational(ref n2)) => {
|
||||
#[cfg(feature = "num")]
|
||||
{
|
||||
&Rational::from(&**n1) == &**n2
|
||||
}
|
||||
#[cfg(not(feature = "num"))]
|
||||
{
|
||||
(&**n1).num_eq(&**n2)
|
||||
}
|
||||
}
|
||||
(&Number::Rational(ref n1), &Number::Integer(ref n2)) => {
|
||||
#[cfg(feature = "num")]
|
||||
{
|
||||
&**n1 == &Rational::from(&**n2)
|
||||
}
|
||||
#[cfg(not(feature = "num"))]
|
||||
{
|
||||
(&**n1).num_eq(&**n2)
|
||||
}
|
||||
}
|
||||
(&Number::Rational(ref n1), &Number::Float(n2)) => {
|
||||
(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)) => {
|
||||
(&Number::Float(n1), Number::Rational(ref n2)) => {
|
||||
n1.eq(&OrderedFloat(n2.to_f64().value()))
|
||||
}
|
||||
(&Number::Float(f1), &Number::Float(f2)) => f1.eq(&f2),
|
||||
(&Number::Rational(ref r1), &Number::Rational(ref r2)) => r1.eq(&r2),
|
||||
(Number::Rational(ref r1), Number::Rational(ref r2)) => r1.eq(r2),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -589,8 +576,8 @@ impl PartialOrd<usize> for Number {
|
||||
(n as usize).partial_cmp(rhs)
|
||||
}
|
||||
}
|
||||
Number::Integer(n) => Some((&**n).num_cmp(rhs)),
|
||||
Number::Rational(r) => Some((&**r).num_cmp(&Integer::from(*rhs))),
|
||||
Number::Integer(n) => Some((n).num_cmp(rhs)),
|
||||
Number::Rational(r) => Some((r).num_cmp(&Integer::from(*rhs))),
|
||||
Number::Float(f) => f.partial_cmp(&OrderedFloat(*rhs as f64)),
|
||||
}
|
||||
}
|
||||
@@ -609,8 +596,8 @@ impl PartialEq<usize> for Number {
|
||||
(n as usize).eq(rhs)
|
||||
}
|
||||
}
|
||||
Number::Integer(n) => (&**n).num_eq(rhs),
|
||||
Number::Rational(r) => (&**r).num_eq(&Integer::from(*rhs)),
|
||||
Number::Integer(n) => (n).num_eq(rhs),
|
||||
Number::Rational(r) => (r).num_eq(&Integer::from(*rhs)),
|
||||
Number::Float(f) => f.eq(&OrderedFloat(*rhs as f64)),
|
||||
}
|
||||
}
|
||||
@@ -626,38 +613,24 @@ impl Ord for Number {
|
||||
fn cmp(&self, rhs: &Number) -> Ordering {
|
||||
match (self, rhs) {
|
||||
(&Number::Fixnum(n1), &Number::Fixnum(n2)) => n1.get_num().cmp(&n2.get_num()),
|
||||
(&Number::Fixnum(n1), Number::Integer(n2)) => Integer::from(n1.get_num()).cmp(&*n2),
|
||||
(Number::Integer(n1), &Number::Fixnum(n2)) => (&**n1).cmp(&Integer::from(n2.get_num())),
|
||||
(&Number::Fixnum(n1), Number::Rational(n2)) => Rational::from(n1.get_num()).cmp(&*n2),
|
||||
(&Number::Fixnum(n1), Number::Integer(n2)) => Integer::from(n1.get_num()).cmp(n2),
|
||||
(Number::Integer(n1), &Number::Fixnum(n2)) => (**n1).cmp(&Integer::from(n2.get_num())),
|
||||
(&Number::Fixnum(n1), Number::Rational(n2)) => Rational::from(n1.get_num()).cmp(n2),
|
||||
(Number::Rational(n1), &Number::Fixnum(n2)) => {
|
||||
(&**n1).cmp(&Rational::from(n2.get_num()))
|
||||
(**n1).cmp(&Rational::from(n2.get_num()))
|
||||
}
|
||||
(&Number::Fixnum(n1), &Number::Float(n2)) => OrderedFloat(n1.get_num() as f64).cmp(&n2),
|
||||
(&Number::Float(n1), &Number::Fixnum(n2)) => n1.cmp(&OrderedFloat(n2.get_num() as f64)),
|
||||
(&Number::Integer(n1), &Number::Integer(n2)) => (*n1).cmp(&*n2),
|
||||
(&Number::Integer(n1), Number::Float(n2)) => OrderedFloat(n1.to_f64().value()).cmp(n2),
|
||||
(&Number::Float(n1), &Number::Integer(ref n2)) => {
|
||||
(&Number::Float(n1), Number::Integer(ref n2)) => {
|
||||
n1.cmp(&OrderedFloat(n2.to_f64().value()))
|
||||
}
|
||||
(&Number::Integer(n1), &Number::Rational(n2)) => {
|
||||
#[cfg(feature = "num")]
|
||||
{
|
||||
Rational::from(&**n1).cmp(n2)
|
||||
}
|
||||
#[cfg(not(feature = "num"))]
|
||||
{
|
||||
(&*n1).num_partial_cmp(&*n2).unwrap_or(Ordering::Less)
|
||||
}
|
||||
(*n1).num_partial_cmp(&*n2).unwrap_or(Ordering::Less)
|
||||
}
|
||||
(&Number::Rational(n1), &Number::Integer(n2)) => {
|
||||
#[cfg(feature = "num")]
|
||||
{
|
||||
(&**n1).cmp(&Rational::from(&**n2))
|
||||
}
|
||||
#[cfg(not(feature = "num"))]
|
||||
{
|
||||
(&*n1).num_partial_cmp(&*n2).unwrap_or(Ordering::Less)
|
||||
}
|
||||
(*n1).num_partial_cmp(&*n2).unwrap_or(Ordering::Less)
|
||||
}
|
||||
(&Number::Rational(n1), &Number::Float(n2)) => {
|
||||
OrderedFloat(n1.to_f64().value()).cmp(&n2)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work
|
||||
|
||||
use crate::parser::ast::MAX_ARITY;
|
||||
use crate::raw_block::*;
|
||||
use crate::rcu::{Rcu, RcuRef};
|
||||
use crate::types::*;
|
||||
|
||||
use std::cmp::Ordering;
|
||||
@@ -8,16 +9,19 @@ use std::hash::{Hash, Hasher};
|
||||
use std::mem;
|
||||
use std::ops::Deref;
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
use std::str;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use std::sync::RwLock;
|
||||
use std::sync::Weak;
|
||||
|
||||
use arcu::atomic::Arcu;
|
||||
use arcu::epoch_counters::GlobalEpochCounterPool;
|
||||
use arcu::rcu_ref::RcuRef;
|
||||
use arcu::Rcu;
|
||||
use indexmap::IndexSet;
|
||||
|
||||
use modular_bitfield::prelude::*;
|
||||
use scryer_modular_bitfield::prelude::*;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Atom {
|
||||
@@ -52,24 +56,25 @@ impl indexmap::Equivalent<Atom> for str {
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<str> for Atom {
|
||||
fn eq(&self, other: &str) -> bool {
|
||||
self.as_str().deref() == other
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<&str> for Atom {
|
||||
fn eq(&self, &other: &&str) -> bool {
|
||||
self.as_str().deref() == other
|
||||
}
|
||||
}
|
||||
|
||||
const ATOM_TABLE_INIT_SIZE: usize = 1 << 16;
|
||||
const ATOM_TABLE_ALIGN: usize = 8;
|
||||
|
||||
#[inline(always)]
|
||||
fn global_atom_table() -> &'static RwLock<Weak<AtomTable>> {
|
||||
#[cfg(feature = "rust_beta_channel")]
|
||||
{
|
||||
// const Weak::new will be stabilized in 1.73 which is currently in beta,
|
||||
// till then we need a OnceLock for initialization
|
||||
static GLOBAL_ATOM_TABLE: RwLock<Weak<AtomTable>> = RwLock::const_new(Weak::new());
|
||||
&GLOBAL_ATOM_TABLE
|
||||
}
|
||||
#[cfg(not(feature = "rust_beta_channel"))]
|
||||
{
|
||||
use std::sync::OnceLock;
|
||||
static GLOBAL_ATOM_TABLE: OnceLock<RwLock<Weak<AtomTable>>> = OnceLock::new();
|
||||
GLOBAL_ATOM_TABLE.get_or_init(|| RwLock::new(Weak::new()))
|
||||
}
|
||||
static GLOBAL_ATOM_TABLE: RwLock<Weak<AtomTable>> = RwLock::new(Weak::new());
|
||||
&GLOBAL_ATOM_TABLE
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -99,6 +104,12 @@ struct AtomHeader {
|
||||
padding: B13,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct AtomData {
|
||||
header: AtomHeader,
|
||||
data: str,
|
||||
}
|
||||
|
||||
impl AtomHeader {
|
||||
fn build_with(len: u64) -> Self {
|
||||
AtomHeader::new().with_len(len).with_m(false)
|
||||
@@ -113,13 +124,6 @@ impl Hash for Atom {
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! is_char {
|
||||
($s:expr) => {
|
||||
!$s.is_empty() && $s.chars().nth(1).is_none()
|
||||
};
|
||||
}
|
||||
|
||||
pub enum AtomString<'a> {
|
||||
Static(&'a str),
|
||||
Dynamic(AtomTableRef<str>),
|
||||
@@ -177,19 +181,23 @@ impl Atom {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn as_ptr(self) -> Option<AtomTableRef<u8>> {
|
||||
pub fn as_ptr(self) -> Option<AtomTableRef<AtomData>> {
|
||||
if self.is_static() {
|
||||
None
|
||||
} else {
|
||||
let atom_table =
|
||||
arc_atom_table().expect("We should only have an Atom while there is an AtomTable");
|
||||
unsafe {
|
||||
AtomTableRef::try_map(atom_table.buf(), |buf| {
|
||||
(buf as *const u8)
|
||||
.offset(((self.index as usize) - (STRINGS.len() << 3)) as isize)
|
||||
.as_ref()
|
||||
})
|
||||
}
|
||||
|
||||
AtomTableRef::try_map(atom_table.inner.read(), |buf| unsafe {
|
||||
let ptr = buf
|
||||
.block
|
||||
.base
|
||||
.add((self.index as usize) - (STRINGS.len() << 3));
|
||||
// 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();
|
||||
Some(&*(std::ptr::slice_from_raw_parts(ptr, len as usize) as *const AtomData))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,15 +211,18 @@ impl Atom {
|
||||
if self.is_static() {
|
||||
STRINGS[(self.index >> 3) as usize].len()
|
||||
} else {
|
||||
let ptr = self.as_ptr().unwrap();
|
||||
let ptr = ptr.deref() as *const u8 as *const AtomHeader;
|
||||
unsafe { ptr::read(ptr) }.len() as _
|
||||
let len: u64 = self.as_ptr().unwrap().header.len();
|
||||
len as usize
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_empty(self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn flat_index(self) -> u64 {
|
||||
(self.index >> 3) as u64
|
||||
self.index >> 3
|
||||
}
|
||||
|
||||
pub fn as_char(self) -> Option<char> {
|
||||
@@ -232,20 +243,10 @@ impl Atom {
|
||||
pub fn as_str(&self) -> AtomString<'static> {
|
||||
if self.is_static() {
|
||||
AtomString::Static(STRINGS[(self.index >> 3) as usize])
|
||||
} else if let Some(ptr) = self.as_ptr() {
|
||||
AtomString::Dynamic(AtomTableRef::map(ptr, |ptr| &ptr.data))
|
||||
} else {
|
||||
if let Some(ptr) = self.as_ptr() {
|
||||
AtomString::Dynamic(AtomTableRef::map(ptr, |ptr| {
|
||||
let header =
|
||||
unsafe { ptr::read::<AtomHeader>(ptr as *const u8 as *const AtomHeader) };
|
||||
let len = header.len() as usize;
|
||||
let buf =
|
||||
unsafe { (ptr as *const u8).offset(mem::size_of::<AtomHeader>() as isize) };
|
||||
|
||||
unsafe { str::from_utf8_unchecked(slice::from_raw_parts(buf, len)) }
|
||||
}))
|
||||
} else {
|
||||
AtomString::Static(&STRINGS[(self.index >> 3) as usize])
|
||||
}
|
||||
AtomString::Static(STRINGS[(self.index >> 3) as usize])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,14 +259,14 @@ impl Atom {
|
||||
return *self;
|
||||
};
|
||||
|
||||
AtomTable::build_with(&atom_tbl, &sub_str)
|
||||
AtomTable::build_with(atom_tbl, sub_str)
|
||||
}
|
||||
}
|
||||
|
||||
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 as usize + mem::size_of::<AtomHeader>()) as *mut u8;
|
||||
ptr::copy_nonoverlapping(string.as_ptr(), str_ptr as *mut u8, string.len());
|
||||
let str_ptr = ptr.add(mem::size_of::<AtomHeader>());
|
||||
ptr::copy_nonoverlapping(string.as_ptr(), str_ptr, string.len());
|
||||
}
|
||||
|
||||
impl PartialOrd for Atom {
|
||||
@@ -285,17 +286,17 @@ impl Ord for Atom {
|
||||
#[derive(Debug)]
|
||||
pub struct InnerAtomTable {
|
||||
block: RawBlock<AtomTable>,
|
||||
pub table: Rcu<IndexSet<Atom>>,
|
||||
pub table: Arcu<IndexSet<Atom>, GlobalEpochCounterPool>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AtomTable {
|
||||
inner: Rcu<InnerAtomTable>,
|
||||
inner: Arcu<InnerAtomTable, GlobalEpochCounterPool>,
|
||||
// this lock is taking during resizing
|
||||
update: Mutex<()>,
|
||||
}
|
||||
|
||||
pub type AtomTableRef<M> = RcuRef<InnerAtomTable, M>;
|
||||
pub type AtomTableRef<M> = arcu::rcu_ref::RcuRef<InnerAtomTable, M>;
|
||||
|
||||
impl InnerAtomTable {
|
||||
#[inline(always)]
|
||||
@@ -303,7 +304,7 @@ impl InnerAtomTable {
|
||||
STATIC_ATOMS_MAP
|
||||
.get(string)
|
||||
.cloned()
|
||||
.or_else(|| self.table.active_epoch().get(string).cloned())
|
||||
.or_else(|| self.table.read().get(string).cloned())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,10 +322,13 @@ impl AtomTable {
|
||||
atom_table
|
||||
} else {
|
||||
let atom_table = Arc::new(Self {
|
||||
inner: Rcu::new(InnerAtomTable {
|
||||
block: RawBlock::new(),
|
||||
table: Rcu::new(IndexSet::new()),
|
||||
}),
|
||||
inner: Arcu::new(
|
||||
InnerAtomTable {
|
||||
block: RawBlock::new(),
|
||||
table: Arcu::new(IndexSet::new(), GlobalEpochCounterPool),
|
||||
},
|
||||
GlobalEpochCounterPool,
|
||||
),
|
||||
update: Mutex::new(()),
|
||||
});
|
||||
*guard = Arc::downgrade(&atom_table);
|
||||
@@ -333,21 +337,14 @@ impl AtomTable {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn buf(&self) -> AtomTableRef<u8> {
|
||||
AtomTableRef::<InnerAtomTable>::map(self.inner.active_epoch(), |inner| {
|
||||
unsafe { inner.block.base.as_ref() }.unwrap()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn active_table(&self) -> RcuRef<IndexSet<Atom>, IndexSet<Atom>> {
|
||||
self.inner.active_epoch().table.active_epoch()
|
||||
self.inner.read().table.read()
|
||||
}
|
||||
|
||||
pub fn build_with(atom_table: &AtomTable, string: &str) -> Atom {
|
||||
loop {
|
||||
let mut block_epoch = atom_table.inner.active_epoch();
|
||||
let mut table_epoch = block_epoch.table.active_epoch();
|
||||
let mut block_epoch = atom_table.inner.read();
|
||||
let mut table_epoch = block_epoch.table.read();
|
||||
|
||||
if let Some(atom) = block_epoch.lookup_str(string) {
|
||||
return atom;
|
||||
@@ -356,10 +353,8 @@ impl AtomTable {
|
||||
// take a lock to prevent concurrent updates
|
||||
let update_guard = atom_table.update.lock().unwrap();
|
||||
|
||||
let is_same_allocation =
|
||||
RcuRef::same_epoch(&block_epoch, &atom_table.inner.active_epoch());
|
||||
let is_same_atom_list =
|
||||
RcuRef::same_epoch(&table_epoch, &block_epoch.table.active_epoch());
|
||||
let is_same_allocation = RcuRef::same_epoch(&block_epoch, &atom_table.inner.read());
|
||||
let is_same_atom_list = RcuRef::same_epoch(&table_epoch, &block_epoch.table.read());
|
||||
|
||||
if !(is_same_allocation && is_same_atom_list) {
|
||||
// some other thread raced us between our lookup and
|
||||
@@ -369,8 +364,7 @@ impl AtomTable {
|
||||
}
|
||||
|
||||
let size = mem::size_of::<AtomHeader>() + string.len();
|
||||
let align_offset = 8 * mem::align_of::<AtomHeader>();
|
||||
let size = (size & !(align_offset - 1)) + align_offset;
|
||||
let size = size.next_multiple_of(AtomTable::align());
|
||||
|
||||
unsafe {
|
||||
let len_ptr = loop {
|
||||
@@ -379,14 +373,14 @@ impl AtomTable {
|
||||
if ptr.is_null() {
|
||||
// garbage collection would go here
|
||||
let new_block = block_epoch.block.grow_new().unwrap();
|
||||
let new_table = Rcu::new(table_epoch.clone());
|
||||
let new_table = Arcu::new(table_epoch.clone(), GlobalEpochCounterPool);
|
||||
let new_alloc = InnerAtomTable {
|
||||
block: new_block,
|
||||
table: new_table,
|
||||
};
|
||||
atom_table.inner.replace(new_alloc);
|
||||
block_epoch = atom_table.inner.active_epoch();
|
||||
table_epoch = block_epoch.table.active_epoch();
|
||||
block_epoch = atom_table.inner.read();
|
||||
table_epoch = block_epoch.table.read();
|
||||
} else {
|
||||
break ptr;
|
||||
}
|
||||
|
||||
@@ -1,28 +1,3 @@
|
||||
fn main() -> std::process::ExitCode {
|
||||
use scryer_prolog::*;
|
||||
use scryer_prolog::atom_table::Atom;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
#[cfg(feature = "repl")]
|
||||
ctrlc::set_handler(move || {
|
||||
scryer_prolog::machine::INTERRUPT.store(true, Ordering::Relaxed);
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let runtime = tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
runtime.block_on(async move {
|
||||
let mut wam = machine::Machine::new(Default::default());
|
||||
wam.run_top_level(atom!("$toplevel"), (atom!("$repl"), 1))
|
||||
})
|
||||
scryer_prolog::run_binary()
|
||||
}
|
||||
|
||||
177
src/codegen.rs
177
src/codegen.rs
@@ -8,10 +8,9 @@ use crate::instructions::*;
|
||||
use crate::iterators::*;
|
||||
use crate::parser::ast::*;
|
||||
use crate::targets::*;
|
||||
use crate::temp_v;
|
||||
use crate::types::*;
|
||||
use crate::variable_records::*;
|
||||
|
||||
use crate::instr;
|
||||
use crate::machine::disjuncts::*;
|
||||
use crate::machine::machine_errors::*;
|
||||
|
||||
@@ -60,6 +59,7 @@ impl BranchCodeStack {
|
||||
marker: &mut DebrayAllocator,
|
||||
) -> SubsumedBranchHits {
|
||||
let mut subsumed_hits = SubsumedBranchHits::with_hasher(FxBuildHasher::default());
|
||||
let mut propagated_var_nums = IndexSet::with_hasher(FxBuildHasher::default());
|
||||
|
||||
for idx in (self.stack.len() - depth..self.stack.len()).rev() {
|
||||
let branch = &mut marker.branch_stack[idx];
|
||||
@@ -85,9 +85,17 @@ impl BranchCodeStack {
|
||||
}
|
||||
}
|
||||
|
||||
if idx > self.stack.len() - depth {
|
||||
propagated_var_nums.insert(var_num);
|
||||
}
|
||||
|
||||
subsumed_hits.insert(var_num);
|
||||
}
|
||||
}
|
||||
|
||||
for var_num in propagated_var_nums.drain(..) {
|
||||
marker.branch_stack[idx - 1].add_branch_occurrence(var_num);
|
||||
}
|
||||
}
|
||||
|
||||
subsumed_hits
|
||||
@@ -111,7 +119,7 @@ impl BranchCodeStack {
|
||||
jump_span -= code.len() + 1;
|
||||
} else {
|
||||
jump_span -= code.len() + 1;
|
||||
code.push_back(instr!("jmp_by_call", jump_span as usize));
|
||||
code.push_back(instr!("jmp_by_call", jump_span));
|
||||
|
||||
jump_span -= 1;
|
||||
}
|
||||
@@ -124,9 +132,9 @@ impl BranchCodeStack {
|
||||
|
||||
for mut branch_arm in self.stack.drain(self.stack.len() - depth..).rev() {
|
||||
let num_branch_arms = branch_arm.len();
|
||||
branch_arm
|
||||
.last_mut()
|
||||
.map(|code| code.extend(combined_code.drain(..)));
|
||||
if let Some(code) = branch_arm.last_mut() {
|
||||
code.extend(combined_code.drain(..))
|
||||
}
|
||||
|
||||
for (idx, code) in branch_arm.into_iter().enumerate() {
|
||||
combined_code.push_back(if idx == 0 {
|
||||
@@ -277,7 +285,6 @@ impl DebrayAllocator {
|
||||
code: &mut CodeDeque,
|
||||
) -> RegType {
|
||||
self.mark_var::<QueryInstruction>(var_num, Level::Shallow, vr, term_loc, code);
|
||||
|
||||
vr.get().norm()
|
||||
}
|
||||
|
||||
@@ -296,7 +303,14 @@ impl DebrayAllocator {
|
||||
self.mark_var_in_non_callable(var_num, term_loc, vr, code);
|
||||
temp_v!(arg)
|
||||
} else {
|
||||
self.increment_running_count(var_num);
|
||||
if let VarAlloc::Perm(_, PermVarAllocation::Pending) =
|
||||
&self.var_data.records[var_num].allocation
|
||||
{
|
||||
self.mark_var_in_non_callable(var_num, term_loc, vr, code);
|
||||
} else {
|
||||
self.increment_running_count(var_num);
|
||||
}
|
||||
|
||||
RegType::Perm(p)
|
||||
}
|
||||
}
|
||||
@@ -376,7 +390,7 @@ impl<'b> CodeGenerator<'b> {
|
||||
Target: crate::targets::CompilationTarget<'a>,
|
||||
{
|
||||
if let Some(ref mut instr) = target.back_mut() {
|
||||
if Target::is_void_instr(&*instr) {
|
||||
if Target::is_void_instr(instr) {
|
||||
Target::incr_void_instr(instr);
|
||||
return;
|
||||
}
|
||||
@@ -418,10 +432,10 @@ impl<'b> CodeGenerator<'b> {
|
||||
.mark_non_var::<Target>(Level::Deep, term_loc, cell, target);
|
||||
target.push_back(Target::clause_arg_to_instr(cell.get()));
|
||||
}
|
||||
&Term::Literal(_, ref constant) => {
|
||||
target.push_back(Target::constant_subterm(constant.clone()));
|
||||
Term::Literal(_, ref constant) => {
|
||||
target.push_back(Target::constant_subterm(*constant));
|
||||
}
|
||||
&Term::Var(ref cell, ref var_ptr) => {
|
||||
Term::Var(ref cell, ref var_ptr) => {
|
||||
self.deep_var_instr::<Target>(
|
||||
cell,
|
||||
var_ptr.to_var_num().unwrap(),
|
||||
@@ -509,7 +523,7 @@ impl<'b> CodeGenerator<'b> {
|
||||
TermRef::PartialString(lvl, cell, string, tail) => {
|
||||
self.marker
|
||||
.mark_non_var::<Target>(lvl, term_loc, cell, &mut target);
|
||||
let atom = AtomTable::build_with(&self.atom_tbl, &string);
|
||||
let atom = AtomTable::build_with(self.atom_tbl, string);
|
||||
|
||||
target.push_back(Target::to_pstr(lvl, atom, cell.get(), true));
|
||||
self.subterm_to_instr::<Target>(tail, term_loc, &mut target);
|
||||
@@ -543,14 +557,14 @@ impl<'b> CodeGenerator<'b> {
|
||||
match call_policy {
|
||||
CallPolicy::Default => {
|
||||
if self.marker.in_tail_position {
|
||||
code.push_back(call_instr.to_execute().to_default());
|
||||
code.push_back(call_instr.into_execute().into_default());
|
||||
} else {
|
||||
code.push_back(call_instr.to_default())
|
||||
code.push_back(call_instr.into_default())
|
||||
}
|
||||
}
|
||||
CallPolicy::Counted => {
|
||||
if self.marker.in_tail_position {
|
||||
code.push_back(call_instr.to_execute());
|
||||
code.push_back(call_instr.into_execute());
|
||||
} else {
|
||||
code.push_back(call_instr)
|
||||
}
|
||||
@@ -558,10 +572,10 @@ impl<'b> CodeGenerator<'b> {
|
||||
}
|
||||
}
|
||||
|
||||
fn compile_inlined<'a>(
|
||||
fn compile_inlined(
|
||||
&mut self,
|
||||
ct: &InlinedClauseType,
|
||||
terms: &'a Vec<Term>,
|
||||
terms: &'_ [Term],
|
||||
term_loc: GenContext,
|
||||
code: &mut CodeDeque,
|
||||
) -> Result<(), CompilationError> {
|
||||
@@ -585,13 +599,13 @@ impl<'b> CodeGenerator<'b> {
|
||||
|
||||
compare_number_instr!(cmp, at_1, at_2)
|
||||
}
|
||||
&InlinedClauseType::IsAtom(..) => match &terms[0] {
|
||||
&Term::Literal(_, Literal::Char(_))
|
||||
| &Term::Literal(_, Literal::Atom(atom!("[]")))
|
||||
| &Term::Literal(_, Literal::Atom(..)) => {
|
||||
InlinedClauseType::IsAtom(..) => match &terms[0] {
|
||||
Term::Literal(_, Literal::Char(_))
|
||||
| Term::Literal(_, Literal::Atom(atom!("[]")))
|
||||
| Term::Literal(_, Literal::Atom(..)) => {
|
||||
instr!("$succeed")
|
||||
}
|
||||
&Term::Var(ref vr, ref name) => {
|
||||
Term::Var(ref vr, ref name) => {
|
||||
self.marker.reset_arg(1);
|
||||
|
||||
let r = self.marker.mark_non_callable(
|
||||
@@ -608,21 +622,21 @@ impl<'b> CodeGenerator<'b> {
|
||||
instr!("$fail")
|
||||
}
|
||||
},
|
||||
&InlinedClauseType::IsAtomic(..) => match &terms[0] {
|
||||
&Term::AnonVar
|
||||
| &Term::Clause(..)
|
||||
| &Term::Cons(..)
|
||||
| &Term::PartialString(..)
|
||||
| &Term::CompleteString(..) => {
|
||||
InlinedClauseType::IsAtomic(..) => match &terms[0] {
|
||||
Term::AnonVar
|
||||
| Term::Clause(..)
|
||||
| Term::Cons(..)
|
||||
| Term::PartialString(..)
|
||||
| Term::CompleteString(..) => {
|
||||
instr!("$fail")
|
||||
}
|
||||
&Term::Literal(_, Literal::String(_)) => {
|
||||
Term::Literal(_, Literal::String(_)) => {
|
||||
instr!("$fail")
|
||||
}
|
||||
&Term::Literal(..) => {
|
||||
Term::Literal(..) => {
|
||||
instr!("$succeed")
|
||||
}
|
||||
&Term::Var(ref vr, ref name) => {
|
||||
Term::Var(ref vr, ref name) => {
|
||||
self.marker.reset_arg(1);
|
||||
|
||||
let r = self.marker.mark_non_callable(
|
||||
@@ -636,15 +650,15 @@ impl<'b> CodeGenerator<'b> {
|
||||
instr!("atomic", r)
|
||||
}
|
||||
},
|
||||
&InlinedClauseType::IsCompound(..) => match &terms[0] {
|
||||
&Term::Clause(..)
|
||||
| &Term::Cons(..)
|
||||
| &Term::PartialString(..)
|
||||
| &Term::CompleteString(..)
|
||||
| &Term::Literal(_, Literal::String(..)) => {
|
||||
InlinedClauseType::IsCompound(..) => match &terms[0] {
|
||||
Term::Clause(..)
|
||||
| Term::Cons(..)
|
||||
| Term::PartialString(..)
|
||||
| Term::CompleteString(..)
|
||||
| Term::Literal(_, Literal::String(..)) => {
|
||||
instr!("$succeed")
|
||||
}
|
||||
&Term::Var(ref vr, ref name) => {
|
||||
Term::Var(ref vr, ref name) => {
|
||||
self.marker.reset_arg(1);
|
||||
|
||||
let r = self.marker.mark_non_callable(
|
||||
@@ -661,11 +675,11 @@ impl<'b> CodeGenerator<'b> {
|
||||
instr!("$fail")
|
||||
}
|
||||
},
|
||||
&InlinedClauseType::IsRational(..) => match &terms[0] {
|
||||
&Term::Literal(_, Literal::Rational(_)) => {
|
||||
InlinedClauseType::IsRational(..) => match terms[0] {
|
||||
Term::Literal(_, Literal::Rational(_)) => {
|
||||
instr!("$succeed")
|
||||
}
|
||||
&Term::Var(ref vr, ref name) => {
|
||||
Term::Var(ref vr, ref name) => {
|
||||
self.marker.reset_arg(1);
|
||||
let r = self.marker.mark_non_callable(
|
||||
name.to_var_num().unwrap(),
|
||||
@@ -680,11 +694,11 @@ impl<'b> CodeGenerator<'b> {
|
||||
instr!("$fail")
|
||||
}
|
||||
},
|
||||
&InlinedClauseType::IsFloat(..) => match &terms[0] {
|
||||
&Term::Literal(_, Literal::Float(_)) => {
|
||||
InlinedClauseType::IsFloat(..) => match terms[0] {
|
||||
Term::Literal(_, Literal::Float(_)) => {
|
||||
instr!("$succeed")
|
||||
}
|
||||
&Term::Var(ref vr, ref name) => {
|
||||
Term::Var(ref vr, ref name) => {
|
||||
self.marker.reset_arg(1);
|
||||
|
||||
let r = self.marker.mark_non_callable(
|
||||
@@ -701,14 +715,14 @@ impl<'b> CodeGenerator<'b> {
|
||||
instr!("$fail")
|
||||
}
|
||||
},
|
||||
&InlinedClauseType::IsNumber(..) => match &terms[0] {
|
||||
&Term::Literal(_, Literal::Float(_))
|
||||
| &Term::Literal(_, Literal::Rational(_))
|
||||
| &Term::Literal(_, Literal::Integer(_))
|
||||
| &Term::Literal(_, Literal::Fixnum(_)) => {
|
||||
InlinedClauseType::IsNumber(..) => match terms[0] {
|
||||
Term::Literal(_, Literal::Float(_))
|
||||
| Term::Literal(_, Literal::Rational(_))
|
||||
| Term::Literal(_, Literal::Integer(_))
|
||||
| Term::Literal(_, Literal::Fixnum(_)) => {
|
||||
instr!("$succeed")
|
||||
}
|
||||
&Term::Var(ref vr, ref name) => {
|
||||
Term::Var(ref vr, ref name) => {
|
||||
self.marker.reset_arg(1);
|
||||
|
||||
let r = self.marker.mark_non_callable(
|
||||
@@ -725,11 +739,11 @@ impl<'b> CodeGenerator<'b> {
|
||||
instr!("$fail")
|
||||
}
|
||||
},
|
||||
&InlinedClauseType::IsNonVar(..) => match &terms[0] {
|
||||
&Term::AnonVar => {
|
||||
InlinedClauseType::IsNonVar(..) => match terms[0] {
|
||||
Term::AnonVar => {
|
||||
instr!("$fail")
|
||||
}
|
||||
&Term::Var(ref vr, ref name) => {
|
||||
Term::Var(ref vr, ref name) => {
|
||||
self.marker.reset_arg(1);
|
||||
|
||||
let r = self.marker.mark_non_callable(
|
||||
@@ -746,11 +760,11 @@ impl<'b> CodeGenerator<'b> {
|
||||
instr!("$succeed")
|
||||
}
|
||||
},
|
||||
&InlinedClauseType::IsInteger(..) => match &terms[0] {
|
||||
&Term::Literal(_, Literal::Integer(_)) | &Term::Literal(_, Literal::Fixnum(_)) => {
|
||||
InlinedClauseType::IsInteger(..) => match &terms[0] {
|
||||
Term::Literal(_, Literal::Integer(_)) | Term::Literal(_, Literal::Fixnum(_)) => {
|
||||
instr!("$succeed")
|
||||
}
|
||||
&Term::Var(ref vr, ref name) => {
|
||||
Term::Var(ref vr, name) => {
|
||||
self.marker.reset_arg(1);
|
||||
|
||||
let r = self.marker.mark_non_callable(
|
||||
@@ -767,18 +781,18 @@ impl<'b> CodeGenerator<'b> {
|
||||
instr!("$fail")
|
||||
}
|
||||
},
|
||||
&InlinedClauseType::IsVar(..) => match &terms[0] {
|
||||
&Term::Literal(..)
|
||||
| &Term::Clause(..)
|
||||
| &Term::Cons(..)
|
||||
| &Term::PartialString(..)
|
||||
| &Term::CompleteString(..) => {
|
||||
InlinedClauseType::IsVar(..) => match terms[0] {
|
||||
Term::Literal(..)
|
||||
| Term::Clause(..)
|
||||
| Term::Cons(..)
|
||||
| Term::PartialString(..)
|
||||
| Term::CompleteString(..) => {
|
||||
instr!("$fail")
|
||||
}
|
||||
&Term::AnonVar => {
|
||||
Term::AnonVar => {
|
||||
instr!("$succeed")
|
||||
}
|
||||
&Term::Var(ref vr, ref name) => {
|
||||
Term::Var(ref vr, ref name) => {
|
||||
self.marker.reset_arg(1);
|
||||
|
||||
let r = self.marker.mark_non_callable(
|
||||
@@ -813,7 +827,7 @@ impl<'b> CodeGenerator<'b> {
|
||||
|
||||
fn compile_is_call(
|
||||
&mut self,
|
||||
terms: &Vec<Term>,
|
||||
terms: &[Term],
|
||||
code: &mut CodeDeque,
|
||||
term_loc: GenContext,
|
||||
call_policy: CallPolicy,
|
||||
@@ -828,8 +842,8 @@ impl<'b> CodeGenerator<'b> {
|
||||
|
||||
self.marker.reset_arg(2);
|
||||
|
||||
let at = match &terms[0] {
|
||||
&Term::Var(ref vr, ref name) => {
|
||||
let at = match terms[0] {
|
||||
Term::Var(ref vr, ref name) => {
|
||||
let var_num = name.to_var_num().unwrap();
|
||||
|
||||
if self.marker.var_data.records[var_num].num_occurrences > 1 {
|
||||
@@ -844,6 +858,9 @@ impl<'b> CodeGenerator<'b> {
|
||||
self.marker.mark_safe_var_unconditionally(var_num);
|
||||
compile_expr!(self, &terms[1], term_loc, code)
|
||||
} else {
|
||||
self.marker
|
||||
.mark_anon_var::<QueryInstruction>(Level::Shallow, term_loc, code);
|
||||
|
||||
if let Term::Var(ref vr, ref var) = &terms[1] {
|
||||
let var_num = var.to_var_num().unwrap();
|
||||
|
||||
@@ -871,7 +888,7 @@ impl<'b> CodeGenerator<'b> {
|
||||
compile_expr!(self, &terms[1], term_loc, code)
|
||||
}
|
||||
}
|
||||
&Term::Literal(
|
||||
Term::Literal(
|
||||
_,
|
||||
c @ Literal::Integer(_)
|
||||
| c @ Literal::Float(_)
|
||||
@@ -896,7 +913,7 @@ impl<'b> CodeGenerator<'b> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compile_seq<'a>(
|
||||
fn compile_seq(
|
||||
&mut self,
|
||||
clauses: &ChunkedTermVec,
|
||||
code: &mut CodeDeque,
|
||||
@@ -1066,7 +1083,7 @@ impl<'b> CodeGenerator<'b> {
|
||||
|
||||
self.marker.reset_at_head(args);
|
||||
|
||||
let iter = FactIterator::from_rule_head_clause(&args);
|
||||
let iter = FactIterator::from_rule_head_clause(args);
|
||||
let fact = self.compile_target::<FactInstruction, _>(iter, GenContext::Head);
|
||||
|
||||
if self.marker.max_reg_allocated() > MAX_ARITY {
|
||||
@@ -1074,7 +1091,7 @@ impl<'b> CodeGenerator<'b> {
|
||||
}
|
||||
|
||||
self.marker.reset_free_list();
|
||||
code.extend(fact.into_iter());
|
||||
code.extend(fact);
|
||||
|
||||
self.compile_seq(clauses, &mut code)?;
|
||||
|
||||
@@ -1099,7 +1116,7 @@ impl<'b> CodeGenerator<'b> {
|
||||
return Err(CompilationError::ExceededMaxArity);
|
||||
}
|
||||
|
||||
code.extend(compiled_fact.into_iter());
|
||||
code.extend(compiled_fact);
|
||||
}
|
||||
|
||||
code.push(instr!("proceed"));
|
||||
@@ -1112,7 +1129,7 @@ impl<'b> CodeGenerator<'b> {
|
||||
let iter = QueryIterator::new(term);
|
||||
let query = self.compile_target::<QueryInstruction, _>(iter, term_loc);
|
||||
|
||||
code.extend(query.into_iter());
|
||||
code.extend(query);
|
||||
|
||||
match term {
|
||||
&QueryTerm::Clause(_, ref ct, _, call_policy) => {
|
||||
@@ -1204,12 +1221,12 @@ impl<'b> CodeGenerator<'b> {
|
||||
|
||||
let clause_code = match clause {
|
||||
PredicateClause::Fact(fact, var_data) => {
|
||||
let var_data = std::mem::replace(var_data, VarData::default());
|
||||
self.compile_fact(&fact, var_data)?
|
||||
let var_data = std::mem::take(var_data);
|
||||
self.compile_fact(fact, var_data)?
|
||||
}
|
||||
PredicateClause::Rule(rule, var_data) => {
|
||||
let var_data = std::mem::replace(var_data, VarData::default());
|
||||
self.compile_rule(&rule, var_data)?
|
||||
let var_data = std::mem::take(var_data);
|
||||
self.compile_rule(rule, var_data)?
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1237,9 +1254,7 @@ impl<'b> CodeGenerator<'b> {
|
||||
skip_stub_try_me_else = !self.settings.is_dynamic();
|
||||
}
|
||||
|
||||
let arg = clause
|
||||
.args()
|
||||
.and_then(|args| args.iter().nth(optimal_index));
|
||||
let arg = clause.args().and_then(|args| args.get(optimal_index));
|
||||
|
||||
if let Some(arg) = arg {
|
||||
let index = code.len();
|
||||
|
||||
@@ -39,6 +39,19 @@ impl BranchOccurrences {
|
||||
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);
|
||||
let num_branches = self.num_branches;
|
||||
|
||||
let entry = self
|
||||
.hits
|
||||
.entry(var_num)
|
||||
.or_insert_with(|| BitVec::repeat(false, num_branches));
|
||||
|
||||
entry.set(self.current_branch, true);
|
||||
self.subsumed_hits.insert(var_num);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -92,17 +105,7 @@ impl BranchStack {
|
||||
|
||||
pub(crate) fn add_branch_occurrence(&mut self, var_num: usize) {
|
||||
if let Some(occurrences) = self.last_mut() {
|
||||
debug_assert!(occurrences.current_branch < occurrences.num_branches);
|
||||
|
||||
let num_branches = occurrences.num_branches;
|
||||
|
||||
let entry = occurrences
|
||||
.hits
|
||||
.entry(var_num)
|
||||
.or_insert_with(|| BitVec::repeat(false, num_branches));
|
||||
|
||||
entry.set(occurrences.current_branch, true);
|
||||
occurrences.subsumed_hits.insert(var_num);
|
||||
occurrences.add_branch_occurrence(var_num);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,30 +169,26 @@ impl DebrayAllocator {
|
||||
for var_num in subsumed_hits {
|
||||
match &mut self.var_data.records[var_num].allocation {
|
||||
VarAlloc::Perm(_, ref mut allocation) => {
|
||||
match allocation {
|
||||
PermVarAllocation::Done {
|
||||
shallow_safety,
|
||||
deep_safety,
|
||||
..
|
||||
} => {
|
||||
if !self
|
||||
.branch_stack
|
||||
.safety_unneeded_in_branch(shallow_safety, &branch_designator)
|
||||
{
|
||||
let branch_occurrences = self.branch_stack.last_mut().unwrap();
|
||||
branch_occurrences.shallow_safety.insert(var_num);
|
||||
}
|
||||
|
||||
if !self
|
||||
.branch_stack
|
||||
.safety_unneeded_in_branch(deep_safety, &branch_designator)
|
||||
{
|
||||
let branch_occurrences = self.branch_stack.last_mut().unwrap();
|
||||
branch_occurrences.deep_safety.insert(var_num);
|
||||
}
|
||||
if let PermVarAllocation::Done {
|
||||
shallow_safety,
|
||||
deep_safety,
|
||||
..
|
||||
} = allocation
|
||||
{
|
||||
if !self
|
||||
.branch_stack
|
||||
.safety_unneeded_in_branch(shallow_safety, &branch_designator)
|
||||
{
|
||||
let branch_occurrences = self.branch_stack.last_mut().unwrap();
|
||||
branch_occurrences.shallow_safety.insert(var_num);
|
||||
}
|
||||
_ => {
|
||||
unreachable!();
|
||||
|
||||
if !self
|
||||
.branch_stack
|
||||
.safety_unneeded_in_branch(deep_safety, &branch_designator)
|
||||
{
|
||||
let branch_occurrences = self.branch_stack.last_mut().unwrap();
|
||||
branch_occurrences.deep_safety.insert(var_num);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,11 +294,9 @@ impl DebrayAllocator {
|
||||
let mut result = 0;
|
||||
|
||||
for reg in self.temp_lb.. {
|
||||
if !self.is_in_use(reg) {
|
||||
if !temp_var_data.no_use_set.contains(reg) {
|
||||
result = reg;
|
||||
break;
|
||||
}
|
||||
if !self.is_in_use(reg) && !temp_var_data.no_use_set.contains(reg) {
|
||||
result = reg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,13 +318,12 @@ impl DebrayAllocator {
|
||||
let mut result = 0;
|
||||
|
||||
for reg in self.temp_lb.. {
|
||||
if !self.is_in_use(reg) {
|
||||
if !temp_var_data.no_use_set.contains(reg) {
|
||||
if !temp_var_data.conflict_set.contains(reg) {
|
||||
result = reg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if !self.is_in_use(reg)
|
||||
&& !temp_var_data.no_use_set.contains(reg)
|
||||
&& !temp_var_data.conflict_set.contains(reg)
|
||||
{
|
||||
result = reg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -349,16 +345,15 @@ impl DebrayAllocator {
|
||||
// consider its use set. T == par_k iff
|
||||
// (GenContext::Last(_), k) is in t_var.use_set.
|
||||
|
||||
match &self.var_data.records[t_var].allocation {
|
||||
VarAlloc::Temp { temp_var_data, .. } => {
|
||||
if !temp_var_data
|
||||
.use_set
|
||||
.contains(&(GenContext::Last(chunk_num), k))
|
||||
{
|
||||
return Some((t_var, self.alloc_with_ca(t_var)));
|
||||
}
|
||||
if let VarAlloc::Temp { temp_var_data, .. } =
|
||||
&self.var_data.records[t_var].allocation
|
||||
{
|
||||
if !temp_var_data
|
||||
.use_set
|
||||
.contains(&(GenContext::Last(chunk_num), k))
|
||||
{
|
||||
return Some((t_var, self.alloc_with_ca(t_var)));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
None
|
||||
@@ -372,25 +367,22 @@ impl DebrayAllocator {
|
||||
chunk_num: usize,
|
||||
code: &mut CodeDeque,
|
||||
) {
|
||||
match self.alloc_in_last_goal_hint(chunk_num) {
|
||||
Some((var_num, r)) => {
|
||||
let k = self.arg_c;
|
||||
if let Some((var_num, r)) = self.alloc_in_last_goal_hint(chunk_num) {
|
||||
let k = self.arg_c;
|
||||
|
||||
if r != k {
|
||||
let r = RegType::Temp(r);
|
||||
if r != k {
|
||||
let r = RegType::Temp(r);
|
||||
|
||||
code.push_back(Target::move_to_register(r, k));
|
||||
code.push_back(Target::move_to_register(r, k));
|
||||
|
||||
self.shallow_temp_mappings.swap_remove(&k);
|
||||
self.shallow_temp_mappings.insert(r.reg_num(), var_num);
|
||||
self.shallow_temp_mappings.swap_remove(&k);
|
||||
self.shallow_temp_mappings.insert(r.reg_num(), var_num);
|
||||
|
||||
self.var_data.records[var_num]
|
||||
.allocation
|
||||
.set_register(r.reg_num());
|
||||
self.in_use.insert(r.reg_num());
|
||||
}
|
||||
self.var_data.records[var_num]
|
||||
.allocation
|
||||
.set_register(r.reg_num());
|
||||
self.in_use.insert(r.reg_num());
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -493,11 +485,8 @@ impl DebrayAllocator {
|
||||
}
|
||||
|
||||
fn add_perm_to_free_list(&mut self, chunk_num: usize, var_num: usize) {
|
||||
match &self.var_data.records[var_num].allocation {
|
||||
VarAlloc::Perm(..) => {
|
||||
self.perm_free_list.push_back((chunk_num, var_num));
|
||||
}
|
||||
_ => {}
|
||||
if let VarAlloc::Perm(..) = &self.var_data.records[var_num].allocation {
|
||||
self.perm_free_list.push_back((chunk_num, var_num));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -521,12 +510,9 @@ impl DebrayAllocator {
|
||||
}
|
||||
|
||||
pub(crate) fn free_var(&mut self, chunk_num: usize, var_num: usize) {
|
||||
match &mut self.var_data.records[var_num].allocation {
|
||||
VarAlloc::Perm(_, allocation) => {
|
||||
*allocation = PermVarAllocation::Pending;
|
||||
self.add_perm_to_free_list(chunk_num, var_num);
|
||||
}
|
||||
_ => {}
|
||||
if let VarAlloc::Perm(_, allocation) = &mut self.var_data.records[var_num].allocation {
|
||||
*allocation = PermVarAllocation::Pending;
|
||||
self.add_perm_to_free_list(chunk_num, var_num);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -570,18 +556,15 @@ impl DebrayAllocator {
|
||||
*shallow_safety = VarSafetyStatus::unneeded(branch_designator);
|
||||
} else if term_loc == GenContext::Head {
|
||||
*shallow_safety = VarSafetyStatus::GloballyUnneeded;
|
||||
} else {
|
||||
if let Some(temp_var_num) = self.shallow_temp_mappings.get(&self.arg_c).cloned()
|
||||
{
|
||||
match &mut self.var_data.records[temp_var_num].allocation {
|
||||
VarAlloc::Temp {
|
||||
ref mut to_perm_var_num,
|
||||
..
|
||||
} => {
|
||||
*to_perm_var_num = Some(var_num);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
} 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 = Some(var_num);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -621,16 +604,9 @@ impl DebrayAllocator {
|
||||
Target::unsafe_argument_to_value(r, arg_c)
|
||||
}
|
||||
}
|
||||
VarAlloc::Temp { ref mut safety, .. } => {
|
||||
if self
|
||||
.branch_stack
|
||||
.safety_unneeded_in_branch(safety, &branch_designator)
|
||||
{
|
||||
Target::argument_to_value(r, arg_c)
|
||||
} else {
|
||||
*safety = VarSafetyStatus::GloballyUnneeded;
|
||||
Target::unsafe_argument_to_value(r, arg_c)
|
||||
}
|
||||
VarAlloc::Temp { .. } => {
|
||||
debug_assert!(matches!(r, RegType::Temp(_)));
|
||||
Target::argument_to_value(r, arg_c)
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
@@ -756,7 +732,7 @@ impl Allocator for DebrayAllocator {
|
||||
&mut self,
|
||||
var_num: usize,
|
||||
lvl: Level,
|
||||
cell: &'a Cell<VarReg>,
|
||||
cell: &Cell<VarReg>,
|
||||
term_loc: GenContext,
|
||||
code: &mut CodeDeque,
|
||||
) {
|
||||
@@ -764,11 +740,11 @@ impl Allocator for DebrayAllocator {
|
||||
RegType::Temp(0) => {
|
||||
let o = self.alloc_reg_to_var::<Target>(var_num, lvl, term_loc, code);
|
||||
cell.set(VarReg::Norm(RegType::Temp(o)));
|
||||
|
||||
(RegType::Temp(o), true)
|
||||
}
|
||||
RegType::Perm(0) => {
|
||||
let p = self.alloc_perm_var(var_num, term_loc.chunk_num());
|
||||
cell.set(VarReg::Norm(RegType::Perm(p)));
|
||||
(RegType::Perm(p), true)
|
||||
}
|
||||
r @ RegType::Perm(_) => {
|
||||
@@ -796,7 +772,7 @@ impl Allocator for DebrayAllocator {
|
||||
&mut self,
|
||||
var_num: usize,
|
||||
lvl: Level,
|
||||
cell: &'a Cell<VarReg>,
|
||||
cell: &Cell<VarReg>,
|
||||
term_loc: GenContext,
|
||||
code: &mut CodeDeque,
|
||||
r: RegType,
|
||||
@@ -862,8 +838,21 @@ impl Allocator for DebrayAllocator {
|
||||
|
||||
fn mark_cut_var(&mut self, var_num: usize, chunk_num: usize) -> RegType {
|
||||
match self.get_binding(var_num) {
|
||||
RegType::Perm(0) | RegType::Temp(0) => {
|
||||
RegType::Perm(self.alloc_perm_var(var_num, chunk_num))
|
||||
RegType::Perm(0) => RegType::Perm(self.alloc_perm_var(var_num, chunk_num)),
|
||||
RegType::Temp(0) => {
|
||||
let t = self.alloc_reg_to_non_var();
|
||||
|
||||
match &mut self.var_data.records[var_num].allocation {
|
||||
VarAlloc::Temp {
|
||||
temp_reg, safety, ..
|
||||
} => {
|
||||
*temp_reg = t;
|
||||
*safety = VarSafetyStatus::GloballyUnneeded;
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
RegType::Temp(t)
|
||||
}
|
||||
r => r,
|
||||
}
|
||||
@@ -886,12 +875,12 @@ impl Allocator for DebrayAllocator {
|
||||
self.arg_c += 1;
|
||||
}
|
||||
|
||||
fn reset_at_head(&mut self, args: &Vec<Term>) {
|
||||
fn reset_at_head(&mut self, args: &[Term]) {
|
||||
self.reset_arg(args.len());
|
||||
self.arity = args.len();
|
||||
|
||||
for (idx, arg) in args.iter().enumerate() {
|
||||
if let &Term::Var(_, ref var) = arg {
|
||||
if let Term::Var(_, ref var) = arg {
|
||||
let var_num = var.to_var_num().unwrap();
|
||||
let r = self.get_binding(var_num);
|
||||
|
||||
|
||||
171
src/ffi.rs
171
src/ffi.rs
@@ -21,14 +21,16 @@ and finally we add the pointer the size of what we've written.
|
||||
|
||||
use crate::atom_table::Atom;
|
||||
|
||||
use std::alloc::{alloc, Layout};
|
||||
use std::alloc::{self, Layout};
|
||||
use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryFrom;
|
||||
use std::error::Error;
|
||||
use std::ffi::{c_void, CString};
|
||||
use std::ptr::addr_of_mut;
|
||||
|
||||
use libffi::low::{ffi_abi_FFI_DEFAULT_ABI, ffi_cif, ffi_type, prep_cif, type_tag, types, CodePtr};
|
||||
use libffi::low::type_tag::STRUCT;
|
||||
use libffi::low::{ffi_abi_FFI_DEFAULT_ABI, ffi_cif, ffi_type, prep_cif, types, CodePtr};
|
||||
use libloading::{Library, Symbol};
|
||||
|
||||
pub struct FunctionDefinition {
|
||||
@@ -69,11 +71,13 @@ impl ForeignFunctionTable {
|
||||
}
|
||||
|
||||
pub fn define_struct(&mut self, name: &str, atom_fields: Vec<Atom>) {
|
||||
let mut fields: Vec<_> = atom_fields.iter().map(|x| self.map_type_ffi(&x)).collect();
|
||||
let mut fields: Vec<_> = atom_fields.iter().map(|x| self.map_type_ffi(x)).collect();
|
||||
fields.push(std::ptr::null_mut::<ffi_type>());
|
||||
let mut struct_type: ffi_type = Default::default();
|
||||
struct_type.type_ = type_tag::STRUCT;
|
||||
struct_type.elements = fields.as_mut_ptr();
|
||||
let struct_type = ffi_type {
|
||||
type_: STRUCT,
|
||||
elements: fields.as_mut_ptr(),
|
||||
..Default::default()
|
||||
};
|
||||
self.structs.insert(
|
||||
name.to_string(),
|
||||
StructImpl {
|
||||
@@ -87,20 +91,20 @@ impl ForeignFunctionTable {
|
||||
fn map_type_ffi(&mut self, source: &Atom) -> *mut ffi_type {
|
||||
unsafe {
|
||||
match source {
|
||||
atom!("sint64") => &mut types::sint64,
|
||||
atom!("sint32") => &mut types::sint32,
|
||||
atom!("sint16") => &mut types::sint16,
|
||||
atom!("sint8") => &mut types::sint8,
|
||||
atom!("uint64") => &mut types::uint64,
|
||||
atom!("uint32") => &mut types::uint32,
|
||||
atom!("uint16") => &mut types::uint16,
|
||||
atom!("uint8") => &mut types::uint8,
|
||||
atom!("bool") => &mut types::sint8,
|
||||
atom!("void") => &mut types::void,
|
||||
atom!("cstr") => &mut types::pointer,
|
||||
atom!("ptr") => &mut types::pointer,
|
||||
atom!("f32") => &mut types::float,
|
||||
atom!("f64") => &mut types::double,
|
||||
atom!("sint64") => addr_of_mut!(types::sint64),
|
||||
atom!("sint32") => addr_of_mut!(types::sint32),
|
||||
atom!("sint16") => addr_of_mut!(types::sint16),
|
||||
atom!("sint8") => addr_of_mut!(types::sint8),
|
||||
atom!("uint64") => addr_of_mut!(types::uint64),
|
||||
atom!("uint32") => addr_of_mut!(types::uint32),
|
||||
atom!("uint16") => addr_of_mut!(types::uint16),
|
||||
atom!("uint8") => addr_of_mut!(types::uint8),
|
||||
atom!("bool") => addr_of_mut!(types::sint8),
|
||||
atom!("void") => addr_of_mut!(types::void),
|
||||
atom!("cstr") => addr_of_mut!(types::pointer),
|
||||
atom!("ptr") => addr_of_mut!(types::pointer),
|
||||
atom!("f32") => addr_of_mut!(types::float),
|
||||
atom!("f64") => addr_of_mut!(types::double),
|
||||
struct_name => match self.structs.get_mut(&*struct_name.as_str()) {
|
||||
Some(ref mut struct_type) => &mut struct_type.ffi_type,
|
||||
None => unreachable!(),
|
||||
@@ -121,11 +125,7 @@ impl ForeignFunctionTable {
|
||||
let symbol_name: CString = CString::new(function.name.clone())?;
|
||||
let code_ptr: Symbol<*mut c_void> =
|
||||
library.get(&symbol_name.into_bytes_with_nul())?;
|
||||
let mut args: Vec<_> = function
|
||||
.args
|
||||
.iter()
|
||||
.map(|x| self.map_type_ffi(&x))
|
||||
.collect();
|
||||
let mut args: Vec<_> = function.args.iter().map(|x| self.map_type_ffi(x)).collect();
|
||||
let mut cif: ffi_cif = Default::default();
|
||||
prep_cif(
|
||||
&mut cif,
|
||||
@@ -150,7 +150,7 @@ impl ForeignFunctionTable {
|
||||
FunctionImpl {
|
||||
cif,
|
||||
args,
|
||||
code_ptr: CodePtr(code_ptr.into_raw().into_raw() as *mut _),
|
||||
code_ptr: CodePtr(code_ptr.into_raw().as_raw_ptr()),
|
||||
return_struct_name,
|
||||
},
|
||||
);
|
||||
@@ -162,8 +162,8 @@ impl ForeignFunctionTable {
|
||||
}
|
||||
|
||||
fn build_pointer_args(
|
||||
args: &mut Vec<Value>,
|
||||
type_args: &Vec<*mut ffi_type>,
|
||||
args: &mut [Value],
|
||||
type_args: &[*mut ffi_type],
|
||||
structs_table: &mut HashMap<String, StructImpl>,
|
||||
) -> Result<PointerArgs, FFIError> {
|
||||
let mut pointers = Vec::with_capacity(args.len());
|
||||
@@ -223,41 +223,47 @@ impl ForeignFunctionTable {
|
||||
arg: &mut Value,
|
||||
structs_table: &mut HashMap<String, StructImpl>,
|
||||
) -> Result<(Box<dyn Any>, usize, usize), FFIError> {
|
||||
unsafe {
|
||||
match arg {
|
||||
Value::Struct(ref name, ref mut struct_args) => {
|
||||
if let Some(ref mut struct_type) = structs_table.clone().get_mut(name) {
|
||||
let layout = Layout::from_size_align(
|
||||
struct_type.ffi_type.size,
|
||||
struct_type.ffi_type.alignment.into(),
|
||||
)
|
||||
.unwrap();
|
||||
let align = struct_type.ffi_type.alignment as usize;
|
||||
let size = struct_type.ffi_type.size;
|
||||
let ptr = alloc(layout) as *mut c_void;
|
||||
let mut field_ptr = ptr;
|
||||
match arg {
|
||||
Value::Struct(ref name, ref mut struct_args) => {
|
||||
if let Some(ref mut struct_type) = structs_table.clone().get_mut(name) {
|
||||
let layout = Layout::from_size_align(
|
||||
struct_type.ffi_type.size,
|
||||
struct_type.ffi_type.alignment.into(),
|
||||
)
|
||||
.unwrap();
|
||||
let align = struct_type.ffi_type.alignment as usize;
|
||||
let size = struct_type.ffi_type.size;
|
||||
let ptr = unsafe { alloc::alloc(layout) as *mut c_void };
|
||||
|
||||
for i in 0..(struct_type.fields.len() - 1) {
|
||||
macro_rules! try_write_int {
|
||||
($type:ty) => {{
|
||||
field_ptr = field_ptr
|
||||
.add(field_ptr.align_offset(std::mem::align_of::<$type>()));
|
||||
let n: $type = <$type>::try_from(struct_args[i].as_int()?)
|
||||
.map_err(|_| FFIError::ValueDontFit)?;
|
||||
std::ptr::write(field_ptr as *mut $type, n);
|
||||
field_ptr = field_ptr.add(std::mem::size_of::<$type>());
|
||||
}};
|
||||
}
|
||||
if ptr.is_null() {
|
||||
panic!("allocation failed")
|
||||
}
|
||||
|
||||
macro_rules! write {
|
||||
($type:ty, $value:expr) => {{
|
||||
let data: $type = $value;
|
||||
std::ptr::write(field_ptr as *mut $type, data);
|
||||
field_ptr = field_ptr.add(align);
|
||||
}};
|
||||
}
|
||||
let mut field_ptr = ptr;
|
||||
|
||||
let field = struct_type.fields[i];
|
||||
#[allow(clippy::needless_range_loop)]
|
||||
for i in 0..(struct_type.fields.len() - 1) {
|
||||
macro_rules! try_write_int {
|
||||
($type:ty) => {{
|
||||
field_ptr = field_ptr
|
||||
.add(field_ptr.align_offset(std::mem::align_of::<$type>()));
|
||||
let n: $type = <$type>::try_from(struct_args[i].as_int()?)
|
||||
.map_err(|_| FFIError::ValueDontFit)?;
|
||||
std::ptr::write(field_ptr as *mut $type, n);
|
||||
field_ptr = field_ptr.add(std::mem::size_of::<$type>());
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! write {
|
||||
($type:ty, $value:expr) => {{
|
||||
let data: $type = $value;
|
||||
std::ptr::write(field_ptr as *mut $type, data);
|
||||
field_ptr = field_ptr.add(align);
|
||||
}};
|
||||
}
|
||||
|
||||
let field = struct_type.fields[i];
|
||||
unsafe {
|
||||
match (*field).type_ as u32 {
|
||||
libffi::raw::FFI_TYPE_UINT8 => try_write_int!(u8),
|
||||
libffi::raw::FFI_TYPE_SINT8 => try_write_int!(i8),
|
||||
@@ -283,7 +289,7 @@ impl ForeignFunctionTable {
|
||||
|
||||
std::ptr::copy(
|
||||
&*struct_ptr as *const _ as *const c_void,
|
||||
field_ptr as *mut c_void,
|
||||
field_ptr,
|
||||
struct_size,
|
||||
);
|
||||
field_ptr = field_ptr.add(struct_size);
|
||||
@@ -293,13 +299,15 @@ impl ForeignFunctionTable {
|
||||
}
|
||||
}
|
||||
}
|
||||
return Ok((Box::from_raw(ptr), size, align));
|
||||
} else {
|
||||
return Err(FFIError::InvalidStructName);
|
||||
}
|
||||
|
||||
#[allow(clippy::from_raw_with_void_ptr)]
|
||||
Ok((unsafe { Box::from_raw(ptr) }, size, align))
|
||||
} else {
|
||||
Err(FFIError::InvalidStructName)
|
||||
}
|
||||
_ => return Err(FFIError::ValueCast),
|
||||
}
|
||||
_ => Err(FFIError::ValueCast),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,7 +344,7 @@ impl ForeignFunctionTable {
|
||||
&mut function_impl.cif,
|
||||
Some(*function_impl.code_ptr.as_safe_fun()),
|
||||
&mut *n as *mut _ as *mut c_void,
|
||||
pointer_args.pointers.as_mut_ptr() as *mut *mut c_void,
|
||||
pointer_args.pointers.as_mut_ptr(),
|
||||
);
|
||||
Ok(Value::Int(
|
||||
i64::try_from(*n).map_err(|_| FFIError::ValueDontFit)?,
|
||||
@@ -350,7 +358,7 @@ impl ForeignFunctionTable {
|
||||
&mut function_impl.cif,
|
||||
Some(*function_impl.code_ptr.as_safe_fun()),
|
||||
&mut *n as *mut _ as *mut c_void,
|
||||
pointer_args.pointers.as_mut_ptr() as *mut *mut c_void,
|
||||
pointer_args.pointers.as_mut_ptr(),
|
||||
);
|
||||
Ok(Value::Float((*n).into()))
|
||||
}
|
||||
@@ -360,7 +368,7 @@ impl ForeignFunctionTable {
|
||||
&mut function_impl.cif,
|
||||
Some(*function_impl.code_ptr.as_safe_fun()),
|
||||
&mut *n as *mut _ as *mut c_void,
|
||||
pointer_args.pointers.as_mut_ptr() as *mut *mut c_void,
|
||||
pointer_args.pointers.as_mut_ptr(),
|
||||
);
|
||||
Ok(Value::Float(*n))
|
||||
}
|
||||
@@ -375,15 +383,20 @@ impl ForeignFunctionTable {
|
||||
struct_type.ffi_type.alignment.into(),
|
||||
)
|
||||
.unwrap();
|
||||
let ptr = alloc(layout) as *mut c_void;
|
||||
let ptr = alloc::alloc(layout) as *mut c_void;
|
||||
|
||||
if ptr.is_null() {
|
||||
panic!("allocation failed")
|
||||
}
|
||||
|
||||
libffi::raw::ffi_call(
|
||||
&mut function_impl.cif,
|
||||
Some(*function_impl.code_ptr.as_safe_fun()),
|
||||
&mut *ptr as *mut _ as *mut c_void,
|
||||
pointer_args.pointers.as_mut_ptr() as *mut *mut c_void,
|
||||
&mut *ptr as *mut _,
|
||||
pointer_args.pointers.as_mut_ptr(),
|
||||
);
|
||||
let struct_val = self.read_struct(ptr, name, struct_type);
|
||||
#[allow(clippy::from_raw_with_void_ptr)]
|
||||
drop(Box::from_raw(ptr));
|
||||
struct_val
|
||||
}
|
||||
@@ -433,6 +446,20 @@ impl ForeignFunctionTable {
|
||||
}
|
||||
libffi::raw::FFI_TYPE_SINT64 => read_and_push_int!(i64),
|
||||
libffi::raw::FFI_TYPE_POINTER => read_and_push_int!(i64),
|
||||
libffi::raw::FFI_TYPE_FLOAT => {
|
||||
field_ptr =
|
||||
field_ptr.add(field_ptr.align_offset(std::mem::align_of::<f32>()));
|
||||
let n: f32 = std::ptr::read(field_ptr as *mut f32);
|
||||
returns.push(Value::Float(n.into()));
|
||||
field_ptr = field_ptr.add(std::mem::size_of::<f32>());
|
||||
}
|
||||
libffi::raw::FFI_TYPE_DOUBLE => {
|
||||
field_ptr =
|
||||
field_ptr.add(field_ptr.align_offset(std::mem::align_of::<f64>()));
|
||||
let n: f64 = std::ptr::read(field_ptr as *mut f64);
|
||||
returns.push(Value::Float(n));
|
||||
field_ptr = field_ptr.add(std::mem::size_of::<f64>());
|
||||
}
|
||||
libffi::raw::FFI_TYPE_STRUCT => {
|
||||
let substruct = struct_type.atom_fields[i].as_str();
|
||||
let struct_type = self
|
||||
@@ -441,7 +468,7 @@ impl ForeignFunctionTable {
|
||||
.ok_or(FFIError::StructNotFound)?;
|
||||
field_ptr = field_ptr
|
||||
.add(field_ptr.align_offset(struct_type.ffi_type.alignment as usize));
|
||||
let struct_val = self.read_struct(field_ptr, &*substruct, struct_type);
|
||||
let struct_val = self.read_struct(field_ptr, &substruct, struct_type);
|
||||
returns.push(struct_val?);
|
||||
field_ptr = field_ptr.add(struct_type.ffi_type.size);
|
||||
}
|
||||
|
||||
135
src/forms.rs
135
src/forms.rs
@@ -11,6 +11,7 @@ use crate::parser::dashu::{Integer, Rational};
|
||||
use crate::parser::parser::CompositeOpDesc;
|
||||
use crate::types::*;
|
||||
|
||||
use dashu::base::Signed;
|
||||
use fxhash::FxBuildHasher;
|
||||
|
||||
use indexmap::{IndexMap, IndexSet};
|
||||
@@ -23,8 +24,6 @@ use std::fmt;
|
||||
use std::ops::{AddAssign, Deref, DerefMut};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::{is_infix, is_postfix};
|
||||
|
||||
pub type PredicateKey = (Atom, usize); // name, arity.
|
||||
|
||||
/*
|
||||
@@ -99,11 +98,7 @@ pub enum RootIterationPolicy {
|
||||
impl RootIterationPolicy {
|
||||
#[inline(always)]
|
||||
pub fn iterable(&self) -> bool {
|
||||
if let RootIterationPolicy::Iterated = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
matches!(self, RootIterationPolicy::Iterated)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,6 +146,7 @@ impl DerefMut for ChunkedTermVec {
|
||||
}
|
||||
|
||||
impl ChunkedTermVec {
|
||||
#[allow(clippy::new_without_default)]
|
||||
#[inline]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
@@ -163,17 +159,6 @@ impl ChunkedTermVec {
|
||||
.push_back(ChunkedTerms::Branch(Vec::with_capacity(capacity)));
|
||||
}
|
||||
|
||||
pub fn push_branch_arm(&mut self, branch: VecDeque<ChunkedTerms>) {
|
||||
match self.chunk_vec.back_mut().unwrap() {
|
||||
ChunkedTerms::Branch(branches) => {
|
||||
branches.push(branch);
|
||||
}
|
||||
ChunkedTerms::Chunk(_) => {
|
||||
self.chunk_vec.push_back(ChunkedTerms::Branch(vec![branch]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn add_chunk(&mut self) {
|
||||
self.chunk_vec
|
||||
@@ -202,8 +187,8 @@ pub enum QueryTerm {
|
||||
// register, clause type, subterms, clause call policy.
|
||||
Clause(Cell<RegType>, ClauseType, Vec<Term>, CallPolicy),
|
||||
Fail,
|
||||
LocalCut { var_num: usize, cut_prev: bool }, // var_num
|
||||
GlobalCut(usize), // var_num
|
||||
LocalCut { var_num: usize, cut_prev: bool }, // var_num
|
||||
GlobalCut(usize), // var_num
|
||||
GetCutPoint { var_num: usize, prev_b: bool },
|
||||
GetLevel(usize), // var_num
|
||||
}
|
||||
@@ -211,7 +196,7 @@ pub enum QueryTerm {
|
||||
impl QueryTerm {
|
||||
pub(crate) fn arity(&self) -> usize {
|
||||
match self {
|
||||
&QueryTerm::Clause(_, _, ref subterms, ..) => subterms.len(),
|
||||
QueryTerm::Clause(_, _, subterms, ..) => subterms.len(),
|
||||
&QueryTerm::GetLevel(_) | &QueryTerm::GetCutPoint { .. } => 1,
|
||||
_ => 0,
|
||||
}
|
||||
@@ -316,15 +301,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(ref term, ..) => term.head.name(),
|
||||
PredicateClause::Rule(ref rule, ..) => rule.name(),
|
||||
}
|
||||
}
|
||||
|
||||
fn arity(&self) -> usize {
|
||||
match self {
|
||||
&PredicateClause::Fact(ref term, ..) => term.head.arity(),
|
||||
&PredicateClause::Rule(ref rule, ..) => rule.arity(),
|
||||
PredicateClause::Fact(ref term, ..) => term.head.arity(),
|
||||
PredicateClause::Rule(ref rule, ..) => rule.arity(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -339,7 +324,7 @@ impl PredicateClause {
|
||||
pub(crate) fn args(&self) -> Option<&[Term]> {
|
||||
match self {
|
||||
PredicateClause::Fact(term, ..) => match &term.head {
|
||||
Term::Clause(_, _, args) => Some(&args),
|
||||
Term::Clause(_, _, args) => Some(args),
|
||||
_ => None,
|
||||
},
|
||||
PredicateClause::Rule(rule, ..) => {
|
||||
@@ -405,16 +390,6 @@ pub struct OpDecl {
|
||||
pub(crate) name: Atom,
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn fixity(spec: u32) -> Fixity {
|
||||
match spec {
|
||||
XFY | XFX | YFX => Fixity::In,
|
||||
XF | YF => Fixity::Post,
|
||||
FX | FY => Fixity::Pre,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
impl OpDecl {
|
||||
#[inline]
|
||||
pub(crate) fn new(op_desc: OpDesc, name: Atom) -> Self {
|
||||
@@ -431,15 +406,12 @@ impl OpDecl {
|
||||
}
|
||||
|
||||
pub(crate) fn insert_into_op_dir(&self, op_dir: &mut OpDir) -> Option<OpDesc> {
|
||||
let key = (self.name, fixity(self.op_desc.get_spec() as u32));
|
||||
let key = (self.name, self.op_desc.get_spec().fixity());
|
||||
|
||||
match op_dir.get_mut(&key) {
|
||||
Some(cell) => {
|
||||
let (old_prec, old_spec) = cell.get();
|
||||
cell.set(self.op_desc.get_prec(), self.op_desc.get_spec());
|
||||
return Some(OpDesc::build_with(old_prec, old_spec));
|
||||
}
|
||||
None => {}
|
||||
if let Some(cell) = op_dir.get_mut(&key) {
|
||||
let (old_prec, old_spec) = cell.get();
|
||||
cell.set(self.op_desc.get_prec(), self.op_desc.get_spec());
|
||||
return Some(OpDesc::build_with(old_prec, old_spec));
|
||||
}
|
||||
|
||||
op_dir.insert(key, self.op_desc)
|
||||
@@ -450,9 +422,9 @@ impl OpDecl {
|
||||
existing_desc: Option<CompositeOpDesc>,
|
||||
op_dir: &mut OpDir,
|
||||
) -> Result<(), SessionError> {
|
||||
let (spec, name) = (self.op_desc.get_spec(), self.name.clone());
|
||||
let (spec, name) = (self.op_desc.get_spec(), self.name);
|
||||
|
||||
if is_infix!(spec as u32) {
|
||||
if spec.is_infix() {
|
||||
if let Some(desc) = existing_desc {
|
||||
if desc.post > 0 {
|
||||
return Err(SessionError::OpIsInfixAndPostFix(name));
|
||||
@@ -460,7 +432,7 @@ impl OpDecl {
|
||||
}
|
||||
}
|
||||
|
||||
if is_postfix!(spec as u32) {
|
||||
if spec.is_postfix() {
|
||||
if let Some(desc) = existing_desc {
|
||||
if desc.inf > 0 {
|
||||
return Err(SessionError::OpIsInfixAndPostFix(name));
|
||||
@@ -484,7 +456,7 @@ impl AtomOrString {
|
||||
pub fn as_atom(&self, atom_tbl: &AtomTable) -> Atom {
|
||||
match self {
|
||||
&AtomOrString::Atom(atom) => atom,
|
||||
AtomOrString::String(string) => AtomTable::build_with(atom_tbl, &string),
|
||||
AtomOrString::String(string) => AtomTable::build_with(atom_tbl, string),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -496,10 +468,11 @@ impl AtomOrString {
|
||||
AtomOrString::String(string) => AtomString::Static(string.as_str()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_string(self) -> String {
|
||||
match self {
|
||||
impl From<AtomOrString> for String {
|
||||
fn from(val: AtomOrString) -> Self {
|
||||
match val {
|
||||
AtomOrString::Atom(atom) => atom.as_str().to_owned(),
|
||||
AtomOrString::String(string) => string,
|
||||
}
|
||||
@@ -543,7 +516,7 @@ pub(crate) fn fetch_op_spec(name: Atom, arity: usize, op_dir: &OpDir) -> Option<
|
||||
}
|
||||
}),
|
||||
1 => {
|
||||
if let Some(op_desc) = op_dir.get(&(name.clone(), Fixity::Pre)) {
|
||||
if let Some(op_desc) = op_dir.get(&(name, Fixity::Pre)) {
|
||||
if op_desc.get_prec() > 0 {
|
||||
return Some(*op_desc);
|
||||
}
|
||||
@@ -744,11 +717,15 @@ impl ArenaFrom<Number> for HeapCellValue {
|
||||
impl Number {
|
||||
pub(crate) fn sign(&self) -> Number {
|
||||
match self {
|
||||
&Number::Float(f) if f == 0.0 => Number::Float(OrderedFloat(0f64)),
|
||||
&Number::Float(f) => Number::Float(OrderedFloat(f.signum())),
|
||||
Number::Float(f) if *f == 0.0 => Number::Float(OrderedFloat(0f64)),
|
||||
Number::Float(f) => Number::Float(OrderedFloat(f.signum())),
|
||||
_ => {
|
||||
if self.is_positive() {
|
||||
Number::Fixnum(Fixnum::build_with(1))
|
||||
if self.is_zero() {
|
||||
Number::Fixnum(Fixnum::build_with(0))
|
||||
} else {
|
||||
Number::Fixnum(Fixnum::build_with(1))
|
||||
}
|
||||
} else if self.is_negative() {
|
||||
Number::Fixnum(Fixnum::build_with(-1))
|
||||
} else {
|
||||
@@ -761,39 +738,36 @@ impl Number {
|
||||
#[inline]
|
||||
pub(crate) fn is_positive(&self) -> bool {
|
||||
match self {
|
||||
&Number::Fixnum(n) => n.get_num() > 0,
|
||||
&Number::Integer(ref n) => &**n > &Integer::from(0),
|
||||
&Number::Float(f) => f.is_sign_positive(),
|
||||
&Number::Rational(ref r) => &**r > &Rational::from(0),
|
||||
Number::Fixnum(n) => n.get_num() > 0,
|
||||
Number::Integer(ref n) => n.is_positive(),
|
||||
Number::Float(f) => f.is_sign_positive(),
|
||||
Number::Rational(ref r) => r.is_positive(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn is_negative(&self) -> bool {
|
||||
match self {
|
||||
&Number::Fixnum(n) => n.get_num() < 0,
|
||||
&Number::Integer(ref n) => &**n < &Integer::from(0),
|
||||
&Number::Float(OrderedFloat(f)) => f.is_sign_negative() && OrderedFloat(f) != -0f64,
|
||||
&Number::Rational(ref r) => &**r < &Rational::from(0),
|
||||
Number::Fixnum(n) => n.get_num() < 0,
|
||||
Number::Integer(ref n) => n.is_negative(),
|
||||
&Number::Float(OrderedFloat(f)) => f.is_sign_negative() && f != -0f64,
|
||||
Number::Rational(ref r) => r.is_negative(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn is_zero(&self) -> bool {
|
||||
match self {
|
||||
&Number::Fixnum(n) => n.get_num() == 0,
|
||||
&Number::Integer(ref n) => &**n == &Integer::from(0),
|
||||
&Number::Float(f) => f == OrderedFloat(0f64) || f == OrderedFloat(-0f64),
|
||||
&Number::Rational(ref r) => &**r == &Rational::from(0),
|
||||
Number::Fixnum(n) => n.get_num() == 0,
|
||||
Number::Integer(ref n) => n.is_zero(),
|
||||
&Number::Float(OrderedFloat(f)) => f == 0.0 || f == -0.0,
|
||||
Number::Rational(ref r) => r.is_zero(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn is_integer(&self) -> bool {
|
||||
match self {
|
||||
Number::Fixnum(_) | Number::Integer(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self, Number::Fixnum(_) | Number::Integer(_))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -883,7 +857,7 @@ impl ClauseIndexInfo {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub(crate) struct PredicateInfo {
|
||||
pub(crate) is_extensible: bool,
|
||||
pub(crate) is_discontiguous: bool,
|
||||
@@ -892,19 +866,6 @@ pub(crate) struct PredicateInfo {
|
||||
pub(crate) has_clauses: bool,
|
||||
}
|
||||
|
||||
impl Default for PredicateInfo {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
PredicateInfo {
|
||||
is_extensible: false,
|
||||
is_discontiguous: false,
|
||||
is_dynamic: false,
|
||||
is_multifile: false,
|
||||
has_clauses: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PredicateInfo {
|
||||
#[inline]
|
||||
pub(crate) fn compile_incrementally(&self) -> bool {
|
||||
@@ -963,7 +924,7 @@ impl LocalPredicateSkeleton {
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn add_retracted_dynamic_clause_info(&mut self, clause_info: ClauseIndexInfo) {
|
||||
debug_assert_eq!(self.is_dynamic, true);
|
||||
debug_assert!(self.is_dynamic);
|
||||
|
||||
if self.retracted_dynamic_clauses.is_none() {
|
||||
self.retracted_dynamic_clauses = Some(vec![]);
|
||||
@@ -1008,7 +969,7 @@ impl PredicateSkeleton {
|
||||
) -> Option<usize> {
|
||||
let search_result = self.core.clause_clause_locs.make_contiguous()
|
||||
[0..self.core.clause_assert_margin]
|
||||
.binary_search_by(|loc| clause_clause_loc.cmp(&loc));
|
||||
.binary_search_by(|loc| clause_clause_loc.cmp(loc));
|
||||
|
||||
match search_result {
|
||||
Ok(loc) => Some(loc),
|
||||
|
||||
117
src/heap_iter.rs
117
src/heap_iter.rs
@@ -1,3 +1,5 @@
|
||||
#![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) use crate::machine::gc::StacklessPreOrderHeapIter;
|
||||
|
||||
@@ -8,7 +10,7 @@ use crate::machine::stack::*;
|
||||
use crate::types::*;
|
||||
|
||||
use core::marker::PhantomData;
|
||||
use modular_bitfield::prelude::*;
|
||||
use scryer_modular_bitfield::prelude::*;
|
||||
|
||||
use std::ops::Deref;
|
||||
use std::vec::Vec;
|
||||
@@ -43,7 +45,7 @@ impl<'a> Drop for EagerStackfulPreOrderHeapIter<'a> {
|
||||
self.start_value.set_mark_bit(true);
|
||||
self.iter_stack.push(self.start_value);
|
||||
|
||||
while let Some(_) = self.follow() {}
|
||||
while self.follow().is_some() {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,28 +89,28 @@ impl<'a> EagerStackfulPreOrderHeapIter<'a> {
|
||||
let arity = cell_as_atom_cell!(self.heap[s]).get_arity();
|
||||
|
||||
for idx in (s + 1 .. s + arity + 1).rev() {
|
||||
if self.heap[idx].get_mark_bit() != self.mark_phase {
|
||||
if self.heap[idx].get_mark_bit() != self.mark_phase {
|
||||
self.iter_stack.push(self.heap[idx]);
|
||||
self.heap[idx].set_mark_bit(self.mark_phase);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::Lis, l) => {
|
||||
if self.heap[l+1].get_mark_bit() != self.mark_phase {
|
||||
self.iter_stack.push(self.heap[l+1]);
|
||||
self.heap[l+1].set_mark_bit(self.mark_phase);
|
||||
}
|
||||
if self.heap[l+1].get_mark_bit() != self.mark_phase {
|
||||
self.iter_stack.push(self.heap[l+1]);
|
||||
self.heap[l+1].set_mark_bit(self.mark_phase);
|
||||
}
|
||||
|
||||
if self.heap[l].get_mark_bit() != self.mark_phase {
|
||||
self.iter_stack.push(self.heap[l]);
|
||||
self.heap[l].set_mark_bit(self.mark_phase);
|
||||
}
|
||||
if self.heap[l].get_mark_bit() != self.mark_phase {
|
||||
self.iter_stack.push(self.heap[l]);
|
||||
self.heap[l].set_mark_bit(self.mark_phase);
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => {
|
||||
let var_value = self.heap[h];
|
||||
self.heap[h].set_mark_bit(self.mark_phase);
|
||||
|
||||
if !(self.heap[h].is_var() && self.heap[h].get_value() as usize == h) {
|
||||
if var_value.get_mark_bit() || !(self.heap[h].is_var() && self.heap[h].get_value() as usize == h) {
|
||||
self.iter_stack.push(var_value);
|
||||
continue;
|
||||
}
|
||||
@@ -125,12 +127,13 @@ impl<'a> EagerStackfulPreOrderHeapIter<'a> {
|
||||
continue;
|
||||
}
|
||||
|
||||
let value = self.heap[h+1];
|
||||
|
||||
self.heap[h].set_mark_bit(self.mark_phase);
|
||||
self.heap[h+1].set_mark_bit(self.mark_phase);
|
||||
|
||||
self.iter_stack.push(value);
|
||||
if self.heap[h].get_tag() == HeapCellValueTag::PStr {
|
||||
let value = self.heap[h+1];
|
||||
self.heap[h+1].set_mark_bit(self.mark_phase);
|
||||
self.iter_stack.push(value);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
}
|
||||
@@ -270,7 +273,9 @@ pub trait FocusedHeapIter: Iterator<Item = HeapCellValue> {
|
||||
fn focus(&self) -> IterStackLoc;
|
||||
}
|
||||
|
||||
impl<'a, ElideLists: ListElisionPolicy> FocusedHeapIter for StackfulPreOrderHeapIter<'a, ElideLists> {
|
||||
impl<'a, ElideLists: ListElisionPolicy> FocusedHeapIter
|
||||
for StackfulPreOrderHeapIter<'a, ElideLists>
|
||||
{
|
||||
#[inline]
|
||||
fn focus(&self) -> IterStackLoc {
|
||||
self.h
|
||||
@@ -506,10 +511,10 @@ impl<'a, ElideLists: ListElisionPolicy> Iterator for StackfulPreOrderHeapIter<'a
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn cycle_detecting_stackless_preorder_iter<'a>(
|
||||
heap: &'a mut [HeapCellValue],
|
||||
pub(crate) fn cycle_detecting_stackless_preorder_iter(
|
||||
heap: &'_ mut [HeapCellValue],
|
||||
start: usize,
|
||||
) -> CycleDetectingIter<'a, true> {
|
||||
) -> CycleDetectingIter<'_, true> {
|
||||
// const generics argument of true so that cycle discovery stops
|
||||
// the iterator.
|
||||
CycleDetectingIter::new(heap, start)
|
||||
@@ -660,29 +665,30 @@ pub(crate) fn stackful_post_order_iter<'a, ElideLists: ListElisionPolicy>(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::machine::gc::IteratorUMP;
|
||||
use crate::machine::mock_wam::*;
|
||||
use crate::machine::gc::{IteratorUMP};
|
||||
|
||||
pub(crate) type RightistPostOrderHeapIter<'a> =
|
||||
PostOrderIterator<StacklessPreOrderHeapIter<'a, IteratorUMP>>;
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn stackless_preorder_iter(
|
||||
heap: &mut Vec<HeapCellValue>,
|
||||
heap: &mut [HeapCellValue],
|
||||
start: usize,
|
||||
) -> StacklessPreOrderHeapIter<IteratorUMP> {
|
||||
StacklessPreOrderHeapIter::<IteratorUMP>::new(heap, start)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn stackless_post_order_iter<'a>(
|
||||
heap: &'a mut Heap,
|
||||
pub(crate) fn stackless_post_order_iter(
|
||||
heap: &'_ mut Heap,
|
||||
start: usize,
|
||||
) -> RightistPostOrderHeapIter<'a> {
|
||||
) -> RightistPostOrderHeapIter {
|
||||
PostOrderIterator::new(stackless_preorder_iter(heap, start))
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||
fn heap_stackless_iter_tests() {
|
||||
let mut wam = MockWAM::new();
|
||||
|
||||
@@ -954,7 +960,10 @@ mod tests {
|
||||
let pstr_offset_cell = pstr_offset_as_cell!(0);
|
||||
|
||||
assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_cell);
|
||||
assert_eq!(unmark_cell_bits!(iter.next().unwrap()), fixnum_as_cell!(Fixnum::build_with(2)));
|
||||
assert_eq!(
|
||||
unmark_cell_bits!(iter.next().unwrap()),
|
||||
fixnum_as_cell!(Fixnum::build_with(2))
|
||||
);
|
||||
assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell);
|
||||
assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell);
|
||||
|
||||
@@ -999,11 +1008,17 @@ mod tests {
|
||||
let pstr_offset_cell = pstr_offset_as_cell!(0);
|
||||
|
||||
assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell);
|
||||
assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_loc_as_cell!(4));
|
||||
assert_eq!(
|
||||
unmark_cell_bits!(iter.next().unwrap()),
|
||||
pstr_loc_as_cell!(4)
|
||||
);
|
||||
|
||||
assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_cell);
|
||||
assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_cell);
|
||||
assert_eq!(unmark_cell_bits!(iter.next().unwrap()), fixnum_as_cell!(Fixnum::build_with(0)));
|
||||
assert_eq!(
|
||||
unmark_cell_bits!(iter.next().unwrap()),
|
||||
fixnum_as_cell!(Fixnum::build_with(0))
|
||||
);
|
||||
|
||||
assert_eq!(iter.next(), None);
|
||||
}
|
||||
@@ -1016,7 +1031,10 @@ mod tests {
|
||||
let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 6);
|
||||
|
||||
assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell);
|
||||
assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_loc_as_cell!(4));
|
||||
assert_eq!(
|
||||
unmark_cell_bits!(iter.next().unwrap()),
|
||||
pstr_loc_as_cell!(4)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
unmark_cell_bits!(iter.next().unwrap()),
|
||||
@@ -1035,7 +1053,10 @@ mod tests {
|
||||
}
|
||||
|
||||
assert_eq!(wam.machine_st.heap[4], pstr_offset_as_cell!(0));
|
||||
assert_eq!(wam.machine_st.heap[5], fixnum_as_cell!(Fixnum::build_with(1i64)));
|
||||
assert_eq!(
|
||||
wam.machine_st.heap[5],
|
||||
fixnum_as_cell!(Fixnum::build_with(1i64))
|
||||
);
|
||||
|
||||
all_cells_unmarked(&wam.machine_st.heap);
|
||||
|
||||
@@ -1501,7 +1522,9 @@ mod tests {
|
||||
wam.machine_st.heap.clear();
|
||||
|
||||
{
|
||||
wam.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(0)));
|
||||
wam.machine_st
|
||||
.heap
|
||||
.push(fixnum_as_cell!(Fixnum::build_with(0)));
|
||||
|
||||
let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 0);
|
||||
|
||||
@@ -1536,7 +1559,10 @@ mod tests {
|
||||
atom_as_cell!(atom!("y"))
|
||||
);
|
||||
|
||||
assert_eq!(unmark_cell_bits!(iter.next().unwrap()), heap_loc_as_cell!(0));
|
||||
assert_eq!(
|
||||
unmark_cell_bits!(iter.next().unwrap()),
|
||||
heap_loc_as_cell!(0)
|
||||
);
|
||||
|
||||
assert!(iter.next().is_none());
|
||||
}
|
||||
@@ -1685,10 +1711,7 @@ mod tests {
|
||||
wam.machine_st.heap.push(heap_loc_as_cell!(0));
|
||||
|
||||
{
|
||||
let mut iter = stackless_preorder_iter(
|
||||
&mut wam.machine_st.heap,
|
||||
9,
|
||||
);
|
||||
let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 9);
|
||||
|
||||
/*
|
||||
while let Some(_) = iter.next() {
|
||||
@@ -2889,8 +2912,7 @@ mod tests {
|
||||
{
|
||||
wam.machine_st.heap.push(heap_loc_as_cell!(0));
|
||||
|
||||
let mut iter =
|
||||
stackless_post_order_iter(&mut wam.machine_st.heap, 0);
|
||||
let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 0);
|
||||
|
||||
assert_eq!(
|
||||
unmark_cell_bits!(iter.next().unwrap()),
|
||||
@@ -2931,8 +2953,7 @@ mod tests {
|
||||
wam.machine_st.heap.push(empty_list_as_cell!());
|
||||
|
||||
{
|
||||
let mut iter =
|
||||
stackless_post_order_iter(&mut wam.machine_st.heap, 0);
|
||||
let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 0);
|
||||
|
||||
assert_eq!(
|
||||
unmark_cell_bits!(iter.next().unwrap()),
|
||||
@@ -2964,8 +2985,7 @@ mod tests {
|
||||
wam.machine_st.heap.push(heap_loc_as_cell!(0));
|
||||
|
||||
{
|
||||
let mut iter =
|
||||
stackless_post_order_iter(&mut wam.machine_st.heap, 0);
|
||||
let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 0);
|
||||
|
||||
// the cycle will be iterated twice before being detected.
|
||||
assert_eq!(
|
||||
@@ -2993,8 +3013,7 @@ mod tests {
|
||||
}
|
||||
|
||||
{
|
||||
let mut iter =
|
||||
stackless_post_order_iter(&mut wam.machine_st.heap, 0);
|
||||
let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 0);
|
||||
|
||||
// cut the iteration short to check that all cells are
|
||||
// unmarked and unforwarded by the Drop instance of
|
||||
@@ -3031,8 +3050,7 @@ mod tests {
|
||||
wam.machine_st.heap.push(pstr_loc_as_cell!(0));
|
||||
|
||||
{
|
||||
let mut iter =
|
||||
stackless_post_order_iter(&mut wam.machine_st.heap, 2);
|
||||
let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 2);
|
||||
|
||||
assert_eq!(
|
||||
unmark_cell_bits!(iter.next().unwrap()),
|
||||
@@ -3119,10 +3137,7 @@ mod tests {
|
||||
unmark_cell_bits!(iter.next().unwrap()),
|
||||
heap_loc_as_cell!(3)
|
||||
);
|
||||
assert_eq!(
|
||||
unmark_cell_bits!(iter.next().unwrap()),
|
||||
pstr_second_cell
|
||||
);
|
||||
assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell);
|
||||
assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell);
|
||||
assert_eq!(iter.next(), None);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
use crate::arena::*;
|
||||
use crate::atom_table::*;
|
||||
use crate::parser::ast::*;
|
||||
use crate::parser::dashu::{ibig, Integer, Rational};
|
||||
use crate::parser::dashu::base::RemEuclid;
|
||||
use crate::parser::dashu::integer::Sign;
|
||||
use crate::{
|
||||
alpha_numeric_char, capital_letter_char, cut_char, decimal_digit_char, graphic_token_char,
|
||||
is_fx, is_infix, is_postfix, is_prefix, is_xf, is_xfx, is_xfy, is_yfx, semicolon_char,
|
||||
sign_char, single_quote_char, small_letter_char, solo_char, variable_indicator_char,
|
||||
};
|
||||
use crate::parser::dashu::{ibig, Integer, Rational};
|
||||
|
||||
use crate::forms::*;
|
||||
use crate::heap_iter::*;
|
||||
@@ -20,6 +15,7 @@ use crate::machine::stack::*;
|
||||
use crate::machine::streams::*;
|
||||
use crate::types::*;
|
||||
|
||||
use dashu::base::Signed;
|
||||
use ordered_float::OrderedFloat;
|
||||
|
||||
use indexmap::IndexMap;
|
||||
@@ -50,7 +46,7 @@ impl DirectedOp {
|
||||
fn is_prefix(&self) -> bool {
|
||||
match self {
|
||||
&DirectedOp::Left(_name, cell) | &DirectedOp::Right(_name, cell) => {
|
||||
is_prefix!(cell.get_spec() as u32)
|
||||
cell.get_spec().is_prefix()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,7 +55,7 @@ impl DirectedOp {
|
||||
fn is_negative_sign(&self) -> bool {
|
||||
match self {
|
||||
&DirectedOp::Left(name, cell) | &DirectedOp::Right(name, cell) => {
|
||||
name == atom!("-") && is_prefix!(cell.get_spec() as u32)
|
||||
name == atom!("-") && cell.get_spec().is_prefix()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,24 +73,24 @@ fn needs_bracketing(child_desc: OpDesc, op: &DirectedOp) -> bool {
|
||||
|
||||
if &*name.as_str() == "-" {
|
||||
let child_assoc = child_desc.get_spec();
|
||||
if is_prefix!(spec) && (is_postfix!(child_assoc) || is_infix!(child_assoc)) {
|
||||
if spec.is_prefix() && (child_assoc.is_postfix() || child_assoc.is_infix()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
let is_strict_right = is_yfx!(spec) || is_xfx!(spec) || is_fx!(spec);
|
||||
let is_strict_right = spec.is_strict_right();
|
||||
child_desc.get_prec() > priority
|
||||
|| (child_desc.get_prec() == priority && is_strict_right)
|
||||
}
|
||||
DirectedOp::Right(_, cell) => {
|
||||
let (priority, spec) = cell.get();
|
||||
let is_strict_left = is_xfx!(spec) || is_xfy!(spec) || is_xf!(spec);
|
||||
let is_strict_left = spec.is_strict_left();
|
||||
|
||||
if child_desc.get_prec() > priority
|
||||
|| (child_desc.get_prec() == priority && is_strict_left)
|
||||
{
|
||||
true
|
||||
} else if (is_postfix!(spec) || is_infix!(spec)) && !is_postfix!(child_desc.get_spec())
|
||||
} else if (spec.is_postfix() || spec.is_infix()) && !child_desc.get_spec().is_postfix()
|
||||
{
|
||||
*cell != child_desc && child_desc.get_prec() == priority
|
||||
} else {
|
||||
@@ -120,7 +116,7 @@ impl<'a, ElideLists> StackfulPreOrderHeapIter<'a, ElideLists> {
|
||||
None => return false,
|
||||
};
|
||||
|
||||
let mut parent_spec = DirectedOp::Left(atom!("-"), OpDesc::build_with(200, FY as u8));
|
||||
let mut parent_spec = DirectedOp::Left(atom!("-"), OpDesc::build_with(200, FY));
|
||||
|
||||
loop {
|
||||
let cell = self.read_cell(h);
|
||||
@@ -130,7 +126,7 @@ impl<'a, ElideLists> StackfulPreOrderHeapIter<'a, ElideLists> {
|
||||
read_heap_cell!(self.heap[s],
|
||||
(HeapCellValueTag::Atom, (name, _arity)) => {
|
||||
if let Some(spec) = fetch_atom_op_spec(name, None, op_dir) {
|
||||
if is_postfix!(spec.get_spec() as u32) || is_infix!(spec.get_spec() as u32) {
|
||||
if spec.get_spec().is_postfix() || spec.get_spec().is_infix() {
|
||||
if needs_bracketing(spec, &parent_spec) {
|
||||
return false;
|
||||
} else {
|
||||
@@ -258,9 +254,7 @@ pub(crate) fn requires_space(atom: &str, op: &str) -> bool {
|
||||
oc == '(' || alpha_numeric_char!(oc)
|
||||
} else if graphic_token_char!(ac) {
|
||||
graphic_token_char!(oc)
|
||||
} else if variable_indicator_char!(ac) {
|
||||
alpha_numeric_char!(oc)
|
||||
} else if capital_letter_char!(ac) {
|
||||
} else if variable_indicator_char!(ac) || capital_letter_char!(ac) {
|
||||
alpha_numeric_char!(oc)
|
||||
} else if sign_char!(ac) {
|
||||
sign_char!(oc) || decimal_digit_char!(oc)
|
||||
@@ -277,7 +271,7 @@ pub(crate) fn requires_space(atom: &str, op: &str) -> bool {
|
||||
|
||||
fn non_quoted_graphic_token<Iter: Iterator<Item = char>>(mut iter: Iter, c: char) -> bool {
|
||||
if c == '/' {
|
||||
return match iter.next() {
|
||||
match iter.next() {
|
||||
None => true,
|
||||
Some('*') => false, // if we start with comment token, we must quote.
|
||||
Some(c) => {
|
||||
@@ -287,9 +281,9 @@ fn non_quoted_graphic_token<Iter: Iterator<Item = char>>(mut iter: Iter, c: char
|
||||
false
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
} else if c == '.' {
|
||||
return match iter.next() {
|
||||
match iter.next() {
|
||||
None => false,
|
||||
Some(c) => {
|
||||
if graphic_token_char!(c) {
|
||||
@@ -298,7 +292,7 @@ fn non_quoted_graphic_token<Iter: Iterator<Item = char>>(mut iter: Iter, c: char
|
||||
false
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
} else {
|
||||
iter.all(|c| graphic_token_char!(c))
|
||||
}
|
||||
@@ -310,9 +304,7 @@ pub(super) fn non_quoted_token<Iter: Iterator<Item = char>>(mut iter: Iter) -> b
|
||||
iter.all(|c| alpha_numeric_char!(c))
|
||||
} else if graphic_token_char!(c) {
|
||||
non_quoted_graphic_token(iter, c)
|
||||
} else if semicolon_char!(c) {
|
||||
iter.next().is_none()
|
||||
} else if cut_char!(c) {
|
||||
} else if semicolon_char!(c) || cut_char!(c) {
|
||||
iter.next().is_none()
|
||||
} else if c == '[' {
|
||||
iter.next() == Some(']') && iter.next().is_none()
|
||||
@@ -328,14 +320,13 @@ pub(super) fn non_quoted_token<Iter: Iterator<Item = char>>(mut iter: Iter) -> b
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::len_without_is_empty)]
|
||||
pub trait HCValueOutputter {
|
||||
type Output;
|
||||
|
||||
fn new() -> Self;
|
||||
fn push_char(&mut self, c: char);
|
||||
fn append(&mut self, s: &str);
|
||||
fn begin_new_var(&mut self);
|
||||
fn insert(&mut self, index: usize, c: char);
|
||||
fn result(self) -> Self::Output;
|
||||
fn ends_with(&self, s: &str) -> bool;
|
||||
fn len(&self) -> usize;
|
||||
@@ -369,16 +360,6 @@ impl HCValueOutputter for PrinterOutputter {
|
||||
self.contents.push(c);
|
||||
}
|
||||
|
||||
fn begin_new_var(&mut self) {
|
||||
if self.contents.len() != 0 {
|
||||
self.contents += ", ";
|
||||
}
|
||||
}
|
||||
|
||||
fn insert(&mut self, idx: usize, c: char) {
|
||||
self.contents.insert(idx, c);
|
||||
}
|
||||
|
||||
fn result(self) -> Self::Output {
|
||||
self.contents
|
||||
}
|
||||
@@ -415,9 +396,9 @@ fn negated_op_needs_bracketing(
|
||||
op.is_negative_sign()
|
||||
&& iter.leftmost_leaf_has_property(op_dir, |addr| match Number::try_from(addr) {
|
||||
Ok(Number::Fixnum(n)) => n.get_num() > 0,
|
||||
Ok(Number::Float(f)) => f > OrderedFloat(0f64),
|
||||
Ok(Number::Integer(n)) => &*n > &Integer::from(0),
|
||||
Ok(Number::Rational(n)) => &*n > &Rational::from(0),
|
||||
Ok(Number::Float(OrderedFloat(f))) => f > 0f64,
|
||||
Ok(Number::Integer(n)) => n.is_positive(),
|
||||
Ok(Number::Rational(n)) => n.is_positive(),
|
||||
_ => false,
|
||||
})
|
||||
} else {
|
||||
@@ -503,7 +484,6 @@ pub struct HCPrinter<'a, Outputter> {
|
||||
pub numbervars: bool,
|
||||
pub quoted: bool,
|
||||
pub ignore_ops: bool,
|
||||
pub print_strings_as_strs: bool,
|
||||
pub max_depth: usize,
|
||||
pub double_quotes: bool,
|
||||
}
|
||||
@@ -537,20 +517,10 @@ pub(crate) fn numbervar(offset: &Integer, addr: HeapCellValue) -> Option<String>
|
||||
}
|
||||
|
||||
match Number::try_from(addr) {
|
||||
Ok(Number::Fixnum(n)) => {
|
||||
if n.get_num() >= 0 {
|
||||
Some(numbervar(offset + Integer::from(n.get_num())))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
Ok(Number::Integer(n)) => {
|
||||
if &*n >= &Integer::from(0) {
|
||||
Some(numbervar(Integer::from(offset + &*n)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
Ok(Number::Fixnum(n)) if n.get_num() >= 0 => {
|
||||
Some(numbervar(offset + Integer::from(n.get_num())))
|
||||
}
|
||||
Ok(Number::Integer(n)) if !n.is_negative() => Some(numbervar(Integer::from(offset + &*n))),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -578,7 +548,6 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
||||
quoted: false,
|
||||
ignore_ops: false,
|
||||
var_names: IndexMap::new(),
|
||||
print_strings_as_strs: false,
|
||||
max_depth: 0,
|
||||
double_quotes: false,
|
||||
}
|
||||
@@ -604,7 +573,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
||||
}
|
||||
|
||||
fn enqueue_op(&mut self, mut max_depth: usize, name: Atom, spec: OpDesc) {
|
||||
if is_postfix!(spec.get_spec()) {
|
||||
if spec.get_spec().is_postfix() {
|
||||
if self.max_depth_exhausted(max_depth) {
|
||||
self.iter.pop_stack();
|
||||
self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
|
||||
@@ -622,7 +591,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
||||
right_directed_op,
|
||||
));
|
||||
}
|
||||
} else if is_prefix!(spec.get_spec()) {
|
||||
} else if spec.get_spec().is_prefix() {
|
||||
if self.max_depth_exhausted(max_depth) {
|
||||
self.iter.pop_stack();
|
||||
self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
|
||||
@@ -640,12 +609,9 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
||||
self.state_stack.push(TokenOrRedirect::Op(name, spec));
|
||||
}
|
||||
} else {
|
||||
match &*name.as_str() {
|
||||
"|" => {
|
||||
self.format_bar_separator_op(max_depth, name, spec);
|
||||
return;
|
||||
}
|
||||
_ => {}
|
||||
if let "|" = &*name.as_str() {
|
||||
self.format_bar_separator_op(max_depth, name, spec);
|
||||
return;
|
||||
};
|
||||
|
||||
if self.max_depth_exhausted(max_depth) {
|
||||
@@ -654,25 +620,22 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
||||
|
||||
self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
|
||||
} else if self.check_max_depth(&mut max_depth) {
|
||||
if is_xfy!(spec.get_spec()) {
|
||||
if matches!(spec.get_spec(), XFY) {
|
||||
let left_directed_op = DirectedOp::Left(name, spec);
|
||||
|
||||
self.state_stack.push(TokenOrRedirect::CompositeRedirect(
|
||||
0,
|
||||
left_directed_op,
|
||||
));
|
||||
self.state_stack
|
||||
.push(TokenOrRedirect::CompositeRedirect(0, left_directed_op));
|
||||
|
||||
self.state_stack.push(TokenOrRedirect::Op(name, spec));
|
||||
self.state_stack.push(TokenOrRedirect::StackPop);
|
||||
} else { // is_yfx!
|
||||
} else {
|
||||
// is_yfx!
|
||||
let right_directed_op = DirectedOp::Right(name, spec);
|
||||
|
||||
self.state_stack.push(TokenOrRedirect::StackPop);
|
||||
self.state_stack.push(TokenOrRedirect::Op(name, spec));
|
||||
self.state_stack.push(TokenOrRedirect::CompositeRedirect(
|
||||
0,
|
||||
right_directed_op,
|
||||
));
|
||||
self.state_stack
|
||||
.push(TokenOrRedirect::CompositeRedirect(0, right_directed_op));
|
||||
}
|
||||
} else {
|
||||
let left_directed_op = DirectedOp::Left(name, spec);
|
||||
@@ -783,7 +746,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
||||
let h = self.iter.stack_last().unwrap();
|
||||
|
||||
let cell = self.iter.read_cell(h);
|
||||
let cell = heap_bound_store(&self.iter.heap, heap_bound_deref(&self.iter.heap, cell));
|
||||
let cell = heap_bound_store(self.iter.heap, heap_bound_deref(self.iter.heap, cell));
|
||||
|
||||
// 7.10.4
|
||||
if let Some(var) = numbervar(&self.numbervars_offset, cell) {
|
||||
@@ -802,20 +765,16 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
||||
name: Atom,
|
||||
op_desc: Option<OpDesc>,
|
||||
) -> bool {
|
||||
if self.numbervars && is_numbered_var(name, arity) {
|
||||
if self.format_numbered_vars() {
|
||||
return true;
|
||||
}
|
||||
if self.numbervars && is_numbered_var(name, arity) && self.format_numbered_vars() {
|
||||
return true;
|
||||
}
|
||||
|
||||
let dot_atom = atom!(".");
|
||||
|
||||
if let Some(spec) = op_desc {
|
||||
if dot_atom == name && is_infix!(spec.get_spec()) {
|
||||
if !self.ignore_ops {
|
||||
self.push_list(max_depth);
|
||||
return true;
|
||||
}
|
||||
if dot_atom == name && spec.get_spec().is_infix() && !self.ignore_ops {
|
||||
self.push_list(max_depth);
|
||||
return true;
|
||||
}
|
||||
|
||||
if !self.ignore_ops && spec.get_prec() > 0 {
|
||||
@@ -824,10 +783,10 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
||||
}
|
||||
}
|
||||
|
||||
return match (name, arity) {
|
||||
match (name, arity) {
|
||||
(atom!("{}"), 1) if !self.ignore_ops => self.format_curly_braces(max_depth),
|
||||
_ => self.format_struct(max_depth, arity, name),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn offset_as_string(&mut self, h: IterStackLoc) -> Option<String> {
|
||||
@@ -866,7 +825,8 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
||||
loop {
|
||||
let is_cyclic = orig_cell.get_forwarding_bit();
|
||||
|
||||
let cell = heap_bound_store(self.iter.heap, heap_bound_deref(self.iter.heap, orig_cell));
|
||||
let cell =
|
||||
heap_bound_store(self.iter.heap, heap_bound_deref(self.iter.heap, orig_cell));
|
||||
let cell = unmark_cell_bits!(cell);
|
||||
|
||||
match self.var_names.get(&cell).cloned() {
|
||||
@@ -933,7 +893,10 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
||||
}
|
||||
|
||||
let h = cell.get_value() as usize;
|
||||
self.iter.push_stack(IterStackLoc::iterable_loc(h, HeapOrStackTag::Heap));
|
||||
self.iter.push_stack(IterStackLoc::iterable_loc(
|
||||
h,
|
||||
HeapOrStackTag::Heap,
|
||||
));
|
||||
|
||||
if let Some(cell) = self.iter.next() {
|
||||
orig_cell = cell;
|
||||
@@ -951,13 +914,13 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while let Some(_) = self.iter.pop_stack() {}
|
||||
while self.iter.pop_stack().is_none() {}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn print_impromptu_atom(&mut self, atom: Atom) {
|
||||
let result = self.print_op_addendum(&*atom.as_str());
|
||||
let result = self.print_op_addendum(&atom.as_str());
|
||||
|
||||
push_space_if_amb!(self, result.as_str(), {
|
||||
append_str!(self, &result);
|
||||
@@ -1145,8 +1108,6 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
||||
self.state_stack.push(TokenOrRedirect::Open);
|
||||
self.state_stack.push(TokenOrRedirect::Atom(rdiv_ct));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
@@ -1241,8 +1202,13 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
||||
let focus = self.iter.focus();
|
||||
let mut heap_pstr_iter = HeapPStrIter::new(self.iter.heap, focus.value() as usize);
|
||||
|
||||
let next_h;
|
||||
let next_hare;
|
||||
|
||||
if heap_pstr_iter.next().is_some() {
|
||||
while let Some(_) = heap_pstr_iter.next() {}
|
||||
next_h = heap_pstr_iter.focus;
|
||||
next_hare = heap_pstr_iter.focus();
|
||||
for _ in heap_pstr_iter.by_ref() {}
|
||||
} else {
|
||||
return self.push_list(max_depth);
|
||||
}
|
||||
@@ -1258,11 +1224,9 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
||||
|
||||
let at_cdr = self.outputter.ends_with("|");
|
||||
|
||||
if self.double_quotes {
|
||||
if !self.ignore_ops && end_cell.is_string_terminator(&self.iter.heap) {
|
||||
self.remove_list_children(focus.value() as usize);
|
||||
return self.print_proper_string(focus.value() as usize, max_depth);
|
||||
}
|
||||
if self.double_quotes && !self.ignore_ops && end_cell.is_string_terminator(self.iter.heap) {
|
||||
self.remove_list_children(focus.value() as usize);
|
||||
return self.print_proper_string(focus.value() as usize, max_depth);
|
||||
}
|
||||
|
||||
if self.ignore_ops {
|
||||
@@ -1275,8 +1239,10 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
||||
append_str!(self, "[]");
|
||||
}
|
||||
} else {
|
||||
self.state_stack.push(TokenOrRedirect::FunctorRedirect(max_depth));
|
||||
self.iter.push_stack(IterStackLoc::iterable_loc(end_h, HeapOrStackTag::Heap));
|
||||
self.state_stack
|
||||
.push(TokenOrRedirect::FunctorRedirect(max_depth));
|
||||
self.iter
|
||||
.push_stack(IterStackLoc::iterable_loc(end_h, HeapOrStackTag::Heap));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -1287,7 +1253,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
||||
|
||||
read_heap_cell!(value,
|
||||
(HeapCellValueTag::Lis) => {
|
||||
return self.push_list(max_depth);
|
||||
self.push_list(max_depth)
|
||||
}
|
||||
_ => {
|
||||
let switch = Rc::new(Cell::new((!at_cdr, 0)));
|
||||
@@ -1303,7 +1269,8 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
||||
// print an extraneous number. pstr offset value cells are never
|
||||
// used by the iterator to mark cyclic terms so the removal is safe.
|
||||
self.iter.pop_stack();
|
||||
Some(end_h)
|
||||
Some(next_hare)
|
||||
// Some(end_h)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@@ -1311,7 +1278,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
||||
if !self.max_depth_exhausted(max_depth) {
|
||||
let pstr = cell_as_string!(self.iter.heap[h]);
|
||||
self.state_stack.push(TokenOrRedirect::CommaSeparatedCharList(CommaSeparatedCharList {
|
||||
pstr, offset, max_depth, end_cell, end_h,
|
||||
pstr, offset, max_depth, end_cell: next_h, end_h,
|
||||
}));
|
||||
} else {
|
||||
self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
|
||||
@@ -1342,7 +1309,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
||||
|
||||
fn close_list(&mut self, switch: Rc<Cell<(bool, usize)>>) -> Option<Rc<Cell<(bool, usize)>>> {
|
||||
if let Some(TokenOrRedirect::Op(_, op_desc)) = self.state_stack.last() {
|
||||
if is_postfix!(op_desc.get_spec()) || is_infix!(op_desc.get_spec()) {
|
||||
if op_desc.get_spec().is_postfix() || op_desc.get_spec().is_infix() {
|
||||
self.state_stack.push(TokenOrRedirect::ChildCloseList);
|
||||
return None;
|
||||
}
|
||||
@@ -1386,13 +1353,16 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
||||
|
||||
let switch = self.close_list(cell);
|
||||
|
||||
self.state_stack.push(TokenOrRedirect::FunctorRedirect(max_depth));
|
||||
self.state_stack
|
||||
.push(TokenOrRedirect::FunctorRedirect(max_depth));
|
||||
self.state_stack.push(TokenOrRedirect::HeadTailSeparator); // bar
|
||||
self.state_stack.push(TokenOrRedirect::FunctorRedirect(max_depth + 1));
|
||||
self.state_stack
|
||||
.push(TokenOrRedirect::FunctorRedirect(max_depth + 1));
|
||||
|
||||
self.open_list(switch);
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn handle_op_as_struct(
|
||||
&mut self,
|
||||
name: Atom,
|
||||
@@ -1448,7 +1418,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
||||
for op in &[op, parent_op] {
|
||||
if let Some(ref op) = &op {
|
||||
if op.is_left()
|
||||
&& (op.is_prefix() || requires_space(&*op.as_atom().as_str(), "("))
|
||||
&& (op.is_prefix() || requires_space(&op.as_atom().as_str(), "("))
|
||||
{
|
||||
self.state_stack.push(TokenOrRedirect::Space);
|
||||
return;
|
||||
@@ -1461,9 +1431,8 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn print_tcp_listener(&mut self, tcp_listener: &TcpListener, max_depth: usize) {
|
||||
let (ip, port) = if let Some(addr) = tcp_listener.local_addr().ok() {
|
||||
let (ip, port) = if let Ok(addr) = tcp_listener.local_addr() {
|
||||
(addr.ip(), addr.port())
|
||||
} else {
|
||||
let disconnected_atom = atom!("$disconnected_tcp_listener");
|
||||
@@ -1545,24 +1514,33 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
||||
}
|
||||
|
||||
fn print_comma_separated_char_list(&mut self, char_list: CommaSeparatedCharList) {
|
||||
let CommaSeparatedCharList { pstr, offset, max_depth, end_cell, end_h } = char_list;
|
||||
let CommaSeparatedCharList {
|
||||
pstr,
|
||||
offset,
|
||||
max_depth,
|
||||
end_cell,
|
||||
end_h,
|
||||
} = char_list;
|
||||
let pstr_str = pstr.as_str_from(offset);
|
||||
|
||||
if let Some(c) = pstr_str.chars().next() {
|
||||
let offset = offset + c.len_utf8();
|
||||
|
||||
if !self.max_depth_exhausted(max_depth) {
|
||||
self.state_stack.push(TokenOrRedirect::CommaSeparatedCharList(CommaSeparatedCharList {
|
||||
pstr,
|
||||
offset,
|
||||
max_depth: max_depth.saturating_sub(1),
|
||||
end_cell,
|
||||
end_h,
|
||||
}));
|
||||
self.state_stack
|
||||
.push(TokenOrRedirect::CommaSeparatedCharList(
|
||||
CommaSeparatedCharList {
|
||||
pstr,
|
||||
offset,
|
||||
max_depth: max_depth.saturating_sub(1),
|
||||
end_cell,
|
||||
end_h,
|
||||
},
|
||||
));
|
||||
|
||||
let max_depth_allows = self.max_depth == 0 || max_depth > 1;
|
||||
|
||||
if max_depth_allows && pstr_str.chars().skip(1).next().is_some() {
|
||||
if max_depth_allows && pstr_str.chars().nth(1).is_some() {
|
||||
self.state_stack.push(TokenOrRedirect::Comma);
|
||||
}
|
||||
|
||||
@@ -1576,10 +1554,12 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
||||
self.state_stack.push(TokenOrRedirect::HeadTailSeparator);
|
||||
} else if end_cell != empty_list_as_cell!() {
|
||||
if let Some(end_h) = end_h {
|
||||
self.iter.push_stack(IterStackLoc::iterable_loc(end_h, HeapOrStackTag::Heap));
|
||||
self.iter
|
||||
.push_stack(IterStackLoc::iterable_loc(end_h, HeapOrStackTag::Heap));
|
||||
}
|
||||
|
||||
self.state_stack.push(TokenOrRedirect::FunctorRedirect(max_depth + 1));
|
||||
self.state_stack
|
||||
.push(TokenOrRedirect::FunctorRedirect(max_depth + 1));
|
||||
self.state_stack.push(TokenOrRedirect::HeadTailSeparator);
|
||||
}
|
||||
}
|
||||
@@ -1599,13 +1579,12 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
||||
|
||||
let print_struct = |printer: &mut Self, name: Atom, arity: usize| {
|
||||
if name == atom!("[]") && arity == 0 {
|
||||
match printer.state_stack.last() {
|
||||
Some(TokenOrRedirect::CloseList(_) | TokenOrRedirect::ChildCloseList) => {
|
||||
if printer.at_cdr("") {
|
||||
return;
|
||||
}
|
||||
if let Some(TokenOrRedirect::CloseList(_) | TokenOrRedirect::ChildCloseList) =
|
||||
printer.state_stack.last()
|
||||
{
|
||||
if printer.at_cdr("") {
|
||||
return;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
append_str!(printer, "[]");
|
||||
@@ -1642,7 +1621,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
||||
result.push('(');
|
||||
}
|
||||
|
||||
result += &printer.print_op_addendum(&*name.as_str());
|
||||
result += &printer.print_op_addendum(&name.as_str());
|
||||
|
||||
if op.is_some() {
|
||||
result.push(')');
|
||||
@@ -1652,14 +1631,14 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
||||
append_str!(printer, &result);
|
||||
});
|
||||
} else {
|
||||
push_space_if_amb!(printer, &*name.as_str(), {
|
||||
push_space_if_amb!(printer, &name.as_str(), {
|
||||
printer.print_impromptu_atom(name);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if !addr.is_var()
|
||||
&& !addr.is_compound(&self.iter.heap)
|
||||
&& !addr.is_compound(self.iter.heap)
|
||||
&& self.max_depth_exhausted(max_depth)
|
||||
{
|
||||
if !(addr == atom_as_cell!(atom!("[]")) && self.at_cdr("")) {
|
||||
@@ -1734,6 +1713,9 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
||||
(ArenaHeaderTag::Stream, stream) => {
|
||||
self.print_stream(stream, max_depth);
|
||||
}
|
||||
(ArenaHeaderTag::TcpListener, listener) => {
|
||||
self.print_tcp_listener(&listener, max_depth);
|
||||
}
|
||||
(ArenaHeaderTag::Dropped, _value) => {
|
||||
self.print_impromptu_atom(atom!("$dropped_value"));
|
||||
}
|
||||
@@ -1769,12 +1751,12 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
||||
while let Some(loc_data) = self.state_stack.pop() {
|
||||
match loc_data {
|
||||
TokenOrRedirect::Atom(atom) => self.print_impromptu_atom(atom),
|
||||
TokenOrRedirect::BarAsOp => append_str!(self, " | "),
|
||||
TokenOrRedirect::BarAsOp => append_str!(self, "|"),
|
||||
TokenOrRedirect::Char(c) => print_char!(self, self.quoted, c),
|
||||
TokenOrRedirect::Op(atom, op) => {
|
||||
self.print_op(&*atom.as_str());
|
||||
self.print_op(&atom.as_str());
|
||||
|
||||
if is_prefix!(op.get_spec()) {
|
||||
if op.get_spec().is_prefix() {
|
||||
self.set_parent_of_first_op(Some(DirectedOp::Left(atom, op)));
|
||||
}
|
||||
}
|
||||
@@ -1840,6 +1822,7 @@ mod tests {
|
||||
use crate::machine::mock_wam::*;
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||
fn term_printing_tests() {
|
||||
let mut wam = MockWAM::new();
|
||||
|
||||
@@ -2118,9 +2101,9 @@ mod tests {
|
||||
all_cells_unmarked(&wam.machine_st.heap);
|
||||
|
||||
wam.op_dir
|
||||
.insert((atom!("+"), Fixity::In), OpDesc::build_with(500, YFX as u8));
|
||||
.insert((atom!("+"), Fixity::In), OpDesc::build_with(500, YFX));
|
||||
wam.op_dir
|
||||
.insert((atom!("*"), Fixity::In), OpDesc::build_with(400, YFX as u8));
|
||||
.insert((atom!("*"), Fixity::In), OpDesc::build_with(400, YFX));
|
||||
|
||||
assert_eq!(
|
||||
&wam.parse_and_print_term("[a|[] + b].").unwrap(),
|
||||
@@ -2137,10 +2120,10 @@ mod tests {
|
||||
all_cells_unmarked(&wam.machine_st.heap);
|
||||
|
||||
wam.op_dir
|
||||
.insert((atom!("fy"), Fixity::Pre), OpDesc::build_with(9, FY as u8));
|
||||
.insert((atom!("fy"), Fixity::Pre), OpDesc::build_with(9, FY));
|
||||
|
||||
wam.op_dir
|
||||
.insert((atom!("yf"), Fixity::Post), OpDesc::build_with(9, YF as u8));
|
||||
.insert((atom!("yf"), Fixity::Post), OpDesc::build_with(9, YF));
|
||||
|
||||
assert_eq!(
|
||||
&wam.parse_and_print_term("(fy (fy 1)yf)yf.").unwrap(),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use std::sync::{Arc, Mutex, Condvar};
|
||||
use std::io::BufRead;
|
||||
use bytes::{buf::Reader, Bytes};
|
||||
use std::sync::{Arc, Condvar, Mutex};
|
||||
|
||||
use warp::http;
|
||||
|
||||
@@ -19,5 +19,5 @@ pub struct HttpRequestData {
|
||||
pub headers: http::HeaderMap,
|
||||
pub path: String,
|
||||
pub query: String,
|
||||
pub body: Box<dyn BufRead + Send>,
|
||||
pub body: Reader<Bytes>,
|
||||
}
|
||||
|
||||
152
src/indexing.rs
152
src/indexing.rs
@@ -114,13 +114,13 @@ impl<'a> IndexingCodeMergingPtr<'a> {
|
||||
Some(OptArgIndexKey::Literal(_, _, constant, _)) => {
|
||||
constants.insert(*constant, constant_ptr);
|
||||
}
|
||||
_ if constant_ptr.is_external() => {
|
||||
// this must be a defunct clause, because it's been deleted
|
||||
// from the skeleton.
|
||||
debug_assert!(constant_key.is_none());
|
||||
}
|
||||
_ => {
|
||||
if let IndexingCodePtr::DynamicExternal(_) = constant_ptr {
|
||||
// this must be a defunct clause, because it's been deleted
|
||||
// from the skeleton.
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -665,7 +665,7 @@ pub(crate) fn merge_clause_index(
|
||||
pub(crate) fn remove_constant_indices(
|
||||
constant: Literal,
|
||||
overlapping_constants: &[Literal],
|
||||
indexing_code: &mut Vec<IndexingLine>,
|
||||
indexing_code: &mut [IndexingLine],
|
||||
offset: usize,
|
||||
) {
|
||||
let mut index = 0;
|
||||
@@ -707,7 +707,7 @@ pub(crate) fn remove_constant_indices(
|
||||
Some(IndexingCodePtr::DynamicExternal(_))
|
||||
| Some(IndexingCodePtr::External(_))
|
||||
| Some(IndexingCodePtr::Fail) => {
|
||||
constants.remove(&constant);
|
||||
constants.swap_remove(&constant);
|
||||
break;
|
||||
}
|
||||
Some(IndexingCodePtr::Internal(o)) => {
|
||||
@@ -811,7 +811,7 @@ pub(crate) fn remove_constant_indices(
|
||||
pub(crate) fn remove_structure_index(
|
||||
name: Atom,
|
||||
arity: usize,
|
||||
indexing_code: &mut Vec<IndexingLine>,
|
||||
indexing_code: &mut [IndexingLine],
|
||||
offset: usize,
|
||||
) {
|
||||
let mut index = 0;
|
||||
@@ -843,10 +843,10 @@ pub(crate) fn remove_structure_index(
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(ref mut structures)) => {
|
||||
structures_index = index;
|
||||
|
||||
match structures.get(&(name.clone(), arity)).cloned() {
|
||||
match structures.get(&(name, arity)).cloned() {
|
||||
Some(IndexingCodePtr::DynamicExternal(_))
|
||||
| Some(IndexingCodePtr::External(_)) => {
|
||||
structures.remove(&(name.clone(), arity));
|
||||
structures.swap_remove(&(name, arity));
|
||||
break;
|
||||
}
|
||||
Some(IndexingCodePtr::Internal(o)) => {
|
||||
@@ -877,7 +877,7 @@ pub(crate) fn remove_structure_index(
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(
|
||||
ref mut structures,
|
||||
)) => {
|
||||
structures.insert((name.clone(), arity), ext);
|
||||
structures.insert((name, arity), ext);
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
@@ -908,7 +908,7 @@ pub(crate) fn remove_structure_index(
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(
|
||||
ref mut structures,
|
||||
)) => {
|
||||
structures.insert((name.clone(), arity), ext);
|
||||
structures.insert((name, arity), ext);
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
@@ -948,7 +948,7 @@ pub(crate) fn remove_structure_index(
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn remove_list_index(indexing_code: &mut Vec<IndexingLine>, offset: usize) {
|
||||
pub(crate) fn remove_list_index(indexing_code: &mut [IndexingLine], offset: usize) {
|
||||
let mut index = 0;
|
||||
|
||||
match &mut indexing_code[index] {
|
||||
@@ -1028,7 +1028,7 @@ pub(crate) fn remove_list_index(indexing_code: &mut Vec<IndexingLine>, offset: u
|
||||
|
||||
pub(crate) fn remove_index(
|
||||
opt_arg_index_key: &OptArgIndexKey,
|
||||
indexing_code: &mut Vec<IndexingLine>,
|
||||
indexing_code: &mut [IndexingLine],
|
||||
clause_loc: usize,
|
||||
) {
|
||||
match opt_arg_index_key {
|
||||
@@ -1049,46 +1049,50 @@ pub(crate) fn remove_index(
|
||||
|
||||
#[inline]
|
||||
fn cap_choice_seq(prelude: &mut [IndexedChoiceInstruction]) {
|
||||
prelude.first_mut().map(|instr| {
|
||||
if let Some(instr) = prelude.first_mut() {
|
||||
*instr = IndexedChoiceInstruction::Try(instr.offset());
|
||||
});
|
||||
}
|
||||
|
||||
cap_choice_seq_with_trust(prelude);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn cap_choice_seq_with_trust(prelude: &mut [IndexedChoiceInstruction]) {
|
||||
prelude.last_mut().map(|instr| match instr {
|
||||
IndexedChoiceInstruction::Retry(i) => {
|
||||
*instr = IndexedChoiceInstruction::Trust(*i);
|
||||
if let Some(instr) = prelude.last_mut() {
|
||||
match instr {
|
||||
IndexedChoiceInstruction::Retry(i) => {
|
||||
*instr = IndexedChoiceInstruction::Trust(*i);
|
||||
}
|
||||
IndexedChoiceInstruction::DefaultRetry(i) => {
|
||||
*instr = IndexedChoiceInstruction::DefaultTrust(*i);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
IndexedChoiceInstruction::DefaultRetry(i) => {
|
||||
*instr = IndexedChoiceInstruction::DefaultTrust(*i);
|
||||
}
|
||||
_ => {}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn uncap_choice_seq_with_trust(prelude: &mut [IndexedChoiceInstruction]) {
|
||||
prelude.last_mut().map(|instr| match instr {
|
||||
IndexedChoiceInstruction::Trust(i) => {
|
||||
*instr = IndexedChoiceInstruction::Retry(*i);
|
||||
if let Some(instr) = prelude.last_mut() {
|
||||
match instr {
|
||||
IndexedChoiceInstruction::Trust(i) => {
|
||||
*instr = IndexedChoiceInstruction::Retry(*i);
|
||||
}
|
||||
IndexedChoiceInstruction::DefaultTrust(i) => {
|
||||
*instr = IndexedChoiceInstruction::DefaultRetry(*i);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
IndexedChoiceInstruction::DefaultTrust(i) => {
|
||||
*instr = IndexedChoiceInstruction::DefaultRetry(*i);
|
||||
}
|
||||
_ => {}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn uncap_choice_seq_with_try(prelude: &mut [IndexedChoiceInstruction]) {
|
||||
prelude.first_mut().map(|instr| {
|
||||
if let Some(instr) = prelude.first_mut() {
|
||||
if let IndexedChoiceInstruction::Try(i) = instr {
|
||||
*instr = IndexedChoiceInstruction::Retry(*i);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn constant_key_alternatives(
|
||||
@@ -1105,28 +1109,24 @@ pub(crate) fn constant_key_alternatives(
|
||||
}
|
||||
}
|
||||
Literal::Char(c) => {
|
||||
let atom = AtomTable::build_with(&atom_tbl, &c.to_string());
|
||||
let atom = AtomTable::build_with(atom_tbl, &c.to_string());
|
||||
constants.push(Literal::Atom(atom));
|
||||
}
|
||||
/*
|
||||
// constant_to_literal takes care of the downward conversion from Integer to Fixnum
|
||||
// if possible.
|
||||
Literal::Fixnum(ref n) => {
|
||||
constants.push(Literal::Integer(arena_alloc!(n, arena))); //Rc::new(Integer::from(*n))));
|
||||
|
||||
/*
|
||||
if *n >= 0 {
|
||||
if let Ok(n) = usize::try_from(*n) {
|
||||
constants.push(Literal::Usize(n));
|
||||
}
|
||||
}
|
||||
*/
|
||||
constants.push(Literal::Integer(arena_alloc!(n, arena)));
|
||||
}
|
||||
*/
|
||||
Literal::Integer(ref n) => {
|
||||
let result = (&**n).try_into();
|
||||
if let Ok(value) = result {
|
||||
Fixnum::build_with_checked(value).map(|n| {
|
||||
constants.push(Literal::Fixnum(n));
|
||||
}).unwrap();
|
||||
Fixnum::build_with_checked(value)
|
||||
.map(|n| {
|
||||
constants.push(Literal::Fixnum(n));
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
@@ -1245,10 +1245,8 @@ impl Indexer for StaticCodeIndices {
|
||||
index_locs.insert(key, IndexingCodePtr::Internal(prelude.len() + 1));
|
||||
cap_choice_seq_with_trust(code.make_contiguous());
|
||||
prelude.push_back(IndexingLine::from(code));
|
||||
} else {
|
||||
code.front().map(|i| {
|
||||
index_locs.insert(key, IndexingCodePtr::External(i.offset()));
|
||||
});
|
||||
} else if let Some(i) = code.front() {
|
||||
index_locs.insert(key, IndexingCodePtr::External(i.offset()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1285,7 +1283,7 @@ impl Indexer for StaticCodeIndices {
|
||||
) -> IndexingCodePtr {
|
||||
if lists.len() > 1 {
|
||||
cap_choice_seq_with_trust(lists.make_contiguous());
|
||||
let lists = mem::replace(lists, VecDeque::new());
|
||||
let lists = std::mem::take(lists);
|
||||
prelude.push_back(IndexingLine::from(lists));
|
||||
|
||||
IndexingCodePtr::Internal(1)
|
||||
@@ -1361,10 +1359,8 @@ impl Indexer for DynamicCodeIndices {
|
||||
prelude.push_back(IndexingLine::DynamicIndexedChoice(
|
||||
code.into_iter().collect(),
|
||||
));
|
||||
} else {
|
||||
code.front().map(|i| {
|
||||
index_locs.insert(key, IndexingCodePtr::DynamicExternal(*i));
|
||||
});
|
||||
} else if let Some(i) = code.front() {
|
||||
index_locs.insert(key, IndexingCodePtr::DynamicExternal(*i));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1400,7 +1396,7 @@ impl Indexer for DynamicCodeIndices {
|
||||
prelude: &mut VecDeque<IndexingLine>,
|
||||
) -> IndexingCodePtr {
|
||||
if lists.len() > 1 {
|
||||
let lists = mem::replace(lists, VecDeque::new());
|
||||
let lists = std::mem::take(lists);
|
||||
prelude.push_back(IndexingLine::DynamicIndexedChoice(
|
||||
lists.into_iter().collect(),
|
||||
));
|
||||
@@ -1458,11 +1454,7 @@ impl<I: Indexer> CodeOffsets<I> {
|
||||
index: usize,
|
||||
) -> Vec<Literal> {
|
||||
let overlapping_constants = constant_key_alternatives(constant, atom_tbl);
|
||||
let code = self
|
||||
.indices
|
||||
.constants()
|
||||
.entry(constant)
|
||||
.or_insert(VecDeque::new());
|
||||
let code = self.indices.constants().entry(constant).or_default();
|
||||
|
||||
let is_initial_index = code.is_empty();
|
||||
code.push_back(I::compute_index(
|
||||
@@ -1472,11 +1464,7 @@ impl<I: Indexer> CodeOffsets<I> {
|
||||
));
|
||||
|
||||
for constant in &overlapping_constants {
|
||||
let code = self
|
||||
.indices
|
||||
.constants()
|
||||
.entry(*constant)
|
||||
.or_insert(VecDeque::new());
|
||||
let code = self.indices.constants().entry(*constant).or_default();
|
||||
|
||||
let is_initial_index = code.is_empty();
|
||||
let index = I::compute_index(is_initial_index, index, self.non_counted_bt);
|
||||
@@ -1488,11 +1476,7 @@ impl<I: Indexer> CodeOffsets<I> {
|
||||
}
|
||||
|
||||
fn index_structure(&mut self, name: Atom, arity: usize, index: usize) -> usize {
|
||||
let code = self
|
||||
.indices
|
||||
.structures()
|
||||
.entry((name.clone(), arity))
|
||||
.or_insert(VecDeque::new());
|
||||
let code = self.indices.structures().entry((name, arity)).or_default();
|
||||
|
||||
let code_len = code.len();
|
||||
let is_initial_index = code.is_empty();
|
||||
@@ -1523,7 +1507,7 @@ impl<I: Indexer> CodeOffsets<I> {
|
||||
}
|
||||
&Term::Clause(_, name, ref terms) => {
|
||||
clause_index_info.opt_arg_index_key =
|
||||
OptArgIndexKey::Structure(self.optimal_index, 0, name.clone(), terms.len());
|
||||
OptArgIndexKey::Structure(self.optimal_index, 0, name, terms.len());
|
||||
|
||||
self.index_structure(name, terms.len(), index);
|
||||
}
|
||||
@@ -1575,20 +1559,14 @@ impl<I: Indexer> CodeOffsets<I> {
|
||||
&mut prelude,
|
||||
);
|
||||
|
||||
match &mut str_loc {
|
||||
IndexingCodePtr::Internal(ref mut i) => {
|
||||
*i += emitted_switch_on_constant as usize; // con_loc.is_internal() as usize;
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
if let IndexingCodePtr::Internal(ref mut i) = &mut str_loc {
|
||||
*i += emitted_switch_on_constant as usize; // con_loc.is_internal() as usize;
|
||||
}
|
||||
|
||||
match &mut lst_loc {
|
||||
IndexingCodePtr::Internal(ref mut i) => {
|
||||
*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;
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
if let IndexingCodePtr::Internal(ref mut 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;
|
||||
}
|
||||
|
||||
let var_offset = 1 + skip_stub_try_me_else as usize;
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ use std::collections::VecDeque;
|
||||
use std::iter::*;
|
||||
use std::vec::Vec;
|
||||
|
||||
#[allow(clippy::borrowed_box)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) enum TermRef<'a> {
|
||||
AnonVar(Level),
|
||||
@@ -35,6 +36,7 @@ impl<'a> TermRef<'a> {
|
||||
}
|
||||
*/
|
||||
|
||||
#[allow(clippy::borrowed_box)]
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum TermIterState<'a> {
|
||||
AnonVar(Level),
|
||||
@@ -113,11 +115,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(ref cell, ClauseType::CallN(_), ref terms, _) => {
|
||||
self.state_stack
|
||||
.push(TermIterState::Clause(lvl, 1, cell, atom!("$call"), terms));
|
||||
}
|
||||
&QueryTerm::Clause(ref cell, ref ct, ref terms, _) => {
|
||||
QueryTerm::Clause(ref cell, ref ct, ref terms, _) => {
|
||||
self.state_stack
|
||||
.push(TermIterState::Clause(lvl, 0, cell, ct.name(), terms));
|
||||
}
|
||||
@@ -214,7 +216,7 @@ impl<'a> FactIterator<'a> {
|
||||
.push_back(TermIterState::subterm_to_state(lvl, term));
|
||||
}
|
||||
|
||||
pub(crate) fn from_rule_head_clause(terms: &'a Vec<Term>) -> Self {
|
||||
pub(crate) fn from_rule_head_clause(terms: &'a [Term]) -> Self {
|
||||
let state_queue = terms
|
||||
.iter()
|
||||
.map(|bt| TermIterState::subterm_to_state(Level::Shallow, bt))
|
||||
@@ -312,14 +314,14 @@ impl<'a> Iterator for FactIterator<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn post_order_iter<'a>(term: &'a Term) -> QueryIterator<'a> {
|
||||
pub(crate) fn post_order_iter(term: &'_ Term) -> QueryIterator {
|
||||
QueryIterator::from_term(term)
|
||||
}
|
||||
|
||||
pub(crate) fn breadth_first_iter<'a>(
|
||||
term: &'a Term,
|
||||
pub(crate) fn breadth_first_iter(
|
||||
term: &'_ Term,
|
||||
iterable_root: RootIterationPolicy,
|
||||
) -> FactIterator<'a> {
|
||||
) -> FactIterator {
|
||||
FactIterator::new(term, iterable_root)
|
||||
}
|
||||
|
||||
@@ -343,7 +345,7 @@ pub(crate) struct ClauseIterator<'a> {
|
||||
remaining_chunks_on_stack: usize,
|
||||
}
|
||||
|
||||
fn state_from_chunked_terms<'a>(chunk_vec: &'a VecDeque<ChunkedTerms>) -> ClauseIteratorState<'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);
|
||||
@@ -422,7 +424,7 @@ impl<'a> Iterator for ClauseIterator<'a> {
|
||||
if focus < branches.len() =>
|
||||
{
|
||||
self.state_stack
|
||||
.push(ClauseIteratorState::RemainingBranches(&branches, focus + 1));
|
||||
.push(ClauseIteratorState::RemainingBranches(branches, focus + 1));
|
||||
let state = state_from_chunked_terms(&branches[focus]);
|
||||
|
||||
if let ClauseIteratorState::RemainingChunks(..) = &state {
|
||||
|
||||
63
src/lib.rs
63
src/lib.rs
@@ -3,55 +3,88 @@
|
||||
#[macro_use]
|
||||
extern crate static_assertions;
|
||||
#[cfg(test)]
|
||||
#[macro_use] extern crate maplit;
|
||||
#[macro_use]
|
||||
extern crate maplit;
|
||||
|
||||
#[macro_use]
|
||||
pub mod macros;
|
||||
pub(crate) mod macros;
|
||||
#[macro_use]
|
||||
pub mod atom_table;
|
||||
pub(crate) mod atom_table;
|
||||
#[macro_use]
|
||||
pub mod arena;
|
||||
pub(crate) mod arena;
|
||||
#[macro_use]
|
||||
pub mod parser;
|
||||
pub(crate) mod parser;
|
||||
mod allocator;
|
||||
mod arithmetic;
|
||||
pub mod codegen;
|
||||
pub(crate) mod codegen;
|
||||
mod debray_allocator;
|
||||
#[cfg(feature = "ffi")]
|
||||
mod ffi;
|
||||
mod forms;
|
||||
mod heap_iter;
|
||||
pub mod heap_print;
|
||||
pub(crate) mod heap_print;
|
||||
#[cfg(feature = "http")]
|
||||
mod http;
|
||||
mod indexing;
|
||||
mod variable_records;
|
||||
#[macro_use]
|
||||
pub mod instructions {
|
||||
pub(crate) mod instructions {
|
||||
include!(concat!(env!("OUT_DIR"), "/instructions.rs"));
|
||||
}
|
||||
mod iterators;
|
||||
pub mod machine;
|
||||
pub(crate) mod machine;
|
||||
mod raw_block;
|
||||
pub mod read;
|
||||
pub(crate) mod read;
|
||||
#[cfg(feature = "repl")]
|
||||
mod repl_helper;
|
||||
mod targets;
|
||||
pub mod types;
|
||||
|
||||
use instructions::instr;
|
||||
|
||||
mod rcu;
|
||||
pub(crate) mod types;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
// Re-exports
|
||||
pub use machine::config::*;
|
||||
pub use machine::lib_machine::*;
|
||||
pub use machine::parsed_results::*;
|
||||
pub use machine::Machine;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[wasm_bindgen]
|
||||
pub fn eval_code(s: &str) -> String {
|
||||
use machine::mock_wam::*;
|
||||
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
let mut wam = Machine::with_test_streams();
|
||||
let bytes = wam.test_load_string(s);
|
||||
String::from_utf8_lossy(&bytes).to_string()
|
||||
}
|
||||
|
||||
pub fn run_binary() -> std::process::ExitCode {
|
||||
use crate::atom_table::Atom;
|
||||
use crate::machine::{Machine, INTERRUPT};
|
||||
|
||||
#[cfg(feature = "repl")]
|
||||
ctrlc::set_handler(move || {
|
||||
INTERRUPT.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let runtime = tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
runtime.block_on(async move {
|
||||
let mut wam = Machine::new(Default::default());
|
||||
wam.run_module_predicate(atom!("$toplevel"), (atom!("$repl"), 0))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
:- module(atts, [op(1199, fx, attribute),
|
||||
call_residue_vars/2,
|
||||
term_attributed_variables/2]).
|
||||
|
||||
:- use_module(library(dcgs)).
|
||||
:- use_module(library(error)).
|
||||
:- use_module(library(terms)).
|
||||
|
||||
/* represent the list of attributes belonging to a variable,
|
||||
@@ -110,12 +110,5 @@ user:goal_expansion(Term, M:get_atts(Var, Attr)) :-
|
||||
nonvar(Term),
|
||||
Term = get_atts(Var, M, Attr).
|
||||
|
||||
:- meta_predicate call_residue_vars(0, ?).
|
||||
|
||||
call_residue_vars(Goal, Vars) :-
|
||||
'$get_attr_var_queue_delim'(B),
|
||||
call(Goal),
|
||||
'$get_attr_var_queue_beyond'(B, Vars).
|
||||
|
||||
term_attributed_variables(Term, Vars) :-
|
||||
'$term_attributed_variables'(Term, Vars).
|
||||
|
||||
@@ -1173,8 +1173,13 @@ clause(H, B) :-
|
||||
% The clause will be inserted at the beginning of the module.
|
||||
asserta(Clause0) :-
|
||||
loader:strip_subst_module(Clause0, user, Module, Clause),
|
||||
iso_ext:asserta(Module, Clause).
|
||||
asserta_(Module, Clause).
|
||||
|
||||
asserta_(Module, (Head :- Body)) :-
|
||||
!,
|
||||
'$asserta'(Module, Head, Body).
|
||||
asserta_(Module, Fact) :-
|
||||
'$asserta'(Module, Fact, true).
|
||||
|
||||
:- meta_predicate assertz(:).
|
||||
|
||||
@@ -1184,7 +1189,13 @@ asserta(Clause0) :-
|
||||
% The clase will be inserted at the end of the module.
|
||||
assertz(Clause0) :-
|
||||
loader:strip_subst_module(Clause0, user, Module, Clause),
|
||||
iso_ext:assertz(Module, Clause).
|
||||
assertz_(Module, Clause).
|
||||
|
||||
assertz_(Module, (Head :- Body)) :-
|
||||
!,
|
||||
'$assertz'(Module, Head, Body).
|
||||
assertz_(Module, Fact) :-
|
||||
'$assertz'(Module, Fact, true).
|
||||
|
||||
|
||||
:- meta_predicate retract(:).
|
||||
@@ -1203,6 +1214,9 @@ retract(Clause0) :-
|
||||
Body = true,
|
||||
retract_module_clause(Head, Body, Module)
|
||||
; Clause = (Head :- Body) ->
|
||||
( var(Module) -> Module = user
|
||||
; true
|
||||
),
|
||||
retract_module_clause(Head, Body, Module)
|
||||
).
|
||||
|
||||
@@ -1225,7 +1239,10 @@ call_retract_helper(Head, Body, P, Module) :-
|
||||
; ClauseQualifier = Module
|
||||
),
|
||||
ClauseQualifier:'$clause'(Head, Body),
|
||||
'$get_clause_p'(Head, P, Module).
|
||||
% ensure '$get_clause_p'/3 is not the last clause so it can
|
||||
% recover the choice point of '$clause' if necessary.
|
||||
'$get_clause_p'(Head, P, Module),
|
||||
true.
|
||||
|
||||
call_retract(Head, Body, Name, Arity, Module) :-
|
||||
findall(P, builtins:call_retract_helper(Head, Body, P, Module), Ps),
|
||||
|
||||
@@ -20,6 +20,7 @@ read and write chars.
|
||||
:- use_module(library(iso_ext)).
|
||||
:- use_module(library(error)).
|
||||
:- use_module(library(lists)).
|
||||
:- use_module(library(between)).
|
||||
:- use_module(library(iso_ext), [partial_string/1,partial_string/3]).
|
||||
|
||||
fabricate_var_name(VarType, VarName, N) :-
|
||||
@@ -74,9 +75,10 @@ extend_var_list_([V|Vs], N, VarList, NewVarList, VarType) :-
|
||||
).
|
||||
|
||||
|
||||
%% char_type(+Char, -Type).
|
||||
%% char_type(?Char, ?Type).
|
||||
%
|
||||
% Given a Char, Type is one of the categories that char fits in.
|
||||
% Type is one of the categories that Char fits in.
|
||||
% At least one of the arguments must be ground.
|
||||
% Possible categories are:
|
||||
%
|
||||
% - `alnum`
|
||||
@@ -132,17 +134,27 @@ extend_var_list_([V|Vs], N, VarList, NewVarList, VarType) :-
|
||||
% Note that uppercase and lowercase transformations use a string. This is because
|
||||
% some characters do not map 1:1 between lowercase and uppercase.
|
||||
char_type(Char, Type) :-
|
||||
must_be(character, Char),
|
||||
( ground(Type) ->
|
||||
( ctype(Type) ->
|
||||
'$char_type'(Char, Type)
|
||||
; domain_error(char_type, Type, char_type/2)
|
||||
)
|
||||
; ctype(Type),
|
||||
can_be(character, Char),
|
||||
( \+ ctype(Type) ->
|
||||
domain_error(char_type, Type, char_type/2)
|
||||
; true
|
||||
),
|
||||
( ground(Char) ->
|
||||
ctype(Type),
|
||||
'$char_type'(Char, Type)
|
||||
; ground(Type) ->
|
||||
ccode(Code),
|
||||
char_code(Char, Code),
|
||||
'$char_type'(Char, Type)
|
||||
; must_be(character, Char)
|
||||
).
|
||||
|
||||
|
||||
% 0xD800 to 0xDFFF are surrogate code points used by UTF-16.
|
||||
|
||||
ccode(Code) :- between(0, 0xD7FF, Code).
|
||||
ccode(Code) :- between(0xE000, 0x10FFFF, Code).
|
||||
|
||||
ctype(alnum).
|
||||
ctype(alpha).
|
||||
ctype(alphabetic).
|
||||
|
||||
@@ -189,18 +189,18 @@ A _Boolean expression_ is one of:
|
||||
| `1` | true |
|
||||
| _variable_ | unknown truth value |
|
||||
| _atom_ | universally quantified variable |
|
||||
| ~ _Expr_ | logical NOT |
|
||||
| _Expr_ + _Expr_ | logical OR |
|
||||
| _Expr_ * _Expr_ | logical AND |
|
||||
| _Expr_ # _Expr_ | exclusive OR |
|
||||
| _Var_ ^ _Expr_ | existential quantification |
|
||||
| _Expr_ =:= _Expr_ | equality |
|
||||
| _Expr_ =\= _Expr_ | disequality (same as #) |
|
||||
| _Expr_ =< _Expr_ | less or equal (implication) |
|
||||
| _Expr_ >= _Expr_ | greater or equal |
|
||||
| _Expr_ < _Expr_ | less than |
|
||||
| _Expr_ > _Expr_ | greater than |
|
||||
| card(Is,Exprs) | cardinality constraint (_see below_) |
|
||||
| `~` _Expr_ | logical NOT |
|
||||
| _Expr_ `+` _Expr_ | logical OR |
|
||||
| _Expr_ `*` _Expr_ | logical AND |
|
||||
| _Expr_ `#` _Expr_ | exclusive OR |
|
||||
| _Var_ `^` _Expr_ | existential quantification |
|
||||
| _Expr_ `=:=` _Expr_ | equality |
|
||||
| _Expr_ `=\=` _Expr_ | disequality (same as #) |
|
||||
| _Expr_ `=<` _Expr_ | less or equal (implication) |
|
||||
| _Expr_ `>=` _Expr_ | greater or equal |
|
||||
| _Expr_ `<` _Expr_ | less than |
|
||||
| _Expr_ `>` _Expr_ | greater than |
|
||||
| `card(Is,Exprs)` | cardinality constraint (_see below_) |
|
||||
| `+(Exprs)` | n-fold disjunction (_see below_) |
|
||||
| `*(Exprs)` | n-fold conjunction (_see below_) |
|
||||
|
||||
@@ -1251,7 +1251,7 @@ bdd_restriction_(Node, VI, Value, Res) -->
|
||||
node_id(Node, ID) },
|
||||
( { I0 =:= VI } ->
|
||||
( { Value =:= 0 } -> { Res = Low }
|
||||
; { Value =:= 1 } -> { Res = High }
|
||||
; { Res = High }
|
||||
)
|
||||
; { I0 > VI } -> { Res = Node }
|
||||
; state(G0), { get_assoc(ID, G0, Res) } -> []
|
||||
|
||||
349
src/lib/clpz.pl
349
src/lib/clpz.pl
@@ -3,7 +3,7 @@
|
||||
Author: Markus Triska
|
||||
E-mail: triska@metalevel.at
|
||||
WWW: https://www.metalevel.at
|
||||
Copyright (C): 2016-2023 Markus Triska
|
||||
Copyright (C): 2016-2024 Markus Triska
|
||||
|
||||
This library provides CLP(ℤ):
|
||||
|
||||
@@ -102,6 +102,7 @@
|
||||
fd_dom/2,
|
||||
|
||||
% for use in predicates from library(reif)
|
||||
clpz_t/2,
|
||||
(#=)/3,
|
||||
(#<)/3
|
||||
|
||||
@@ -1015,6 +1016,9 @@ X in inf..sup.
|
||||
needed to schedule the propagators!
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
:- meta_predicate(duophrase(4, ?, ?)).
|
||||
:- meta_predicate(duophrase(4, ?, ?, ?, ?)).
|
||||
|
||||
duophrase(NT, As, Bs) :-
|
||||
duophrase(NT, As, [], Bs, []).
|
||||
|
||||
@@ -1735,7 +1739,7 @@ intervals_to_domain(Is, D) :-
|
||||
% _Lower_ must be an integer or the atom *inf*, which
|
||||
% denotes negative infinity. _Upper_ must be an integer or
|
||||
% the atom *sup*, which denotes positive infinity.
|
||||
% * Domain1 \/ Domain2
|
||||
% * Domain1 `\/` Domain2
|
||||
% The union of Domain1 and Domain2.
|
||||
|
||||
Var in Dom :- clpz_in(Var, Dom).
|
||||
@@ -2409,22 +2413,22 @@ sum_finite_domains([C|Cs], [V|Vs], Inf0, Sup0, Inf, Sup) ++>
|
||||
),
|
||||
sum_finite_domains(Cs, Vs, Inf2, Sup2, Inf, Sup).
|
||||
|
||||
remove_dist_upper_lower([], _, _, _).
|
||||
remove_dist_upper_lower([C|Cs], [V|Vs], D1, D2) :-
|
||||
( fd_get(V, VD, VPs) ->
|
||||
remove_dist_upper_lower([], _, _, _) --> [].
|
||||
remove_dist_upper_lower([C|Cs], [V|Vs], D1, D2) -->
|
||||
( { fd_get(V, VD, VPs) } ->
|
||||
( C < 0 ->
|
||||
domain_supremum(VD, n(Sup)),
|
||||
L is Sup + D1//C,
|
||||
domain_remove_smaller_than(VD, L, VD1),
|
||||
domain_infimum(VD1, n(Inf)),
|
||||
G is Inf - D2//C,
|
||||
domain_remove_greater_than(VD1, G, VD2)
|
||||
; domain_infimum(VD, n(Inf)),
|
||||
G is Inf + D1//C,
|
||||
domain_remove_greater_than(VD, G, VD1),
|
||||
domain_supremum(VD1, n(Sup)),
|
||||
L is Sup - D2//C,
|
||||
domain_remove_smaller_than(VD1, L, VD2)
|
||||
{ domain_supremum(VD, n(Sup)),
|
||||
L is Sup + D1//C,
|
||||
domain_remove_smaller_than(VD, L, VD1),
|
||||
domain_infimum(VD1, n(Inf)),
|
||||
G is Inf - D2//C,
|
||||
domain_remove_greater_than(VD1, G, VD2) }
|
||||
; { domain_infimum(VD, n(Inf)),
|
||||
G is Inf + D1//C,
|
||||
domain_remove_greater_than(VD, G, VD1),
|
||||
domain_supremum(VD1, n(Sup)),
|
||||
L is Sup - D2//C,
|
||||
domain_remove_smaller_than(VD1, L, VD2) }
|
||||
),
|
||||
fd_put(V, VD2, VPs)
|
||||
; true
|
||||
@@ -2432,16 +2436,16 @@ remove_dist_upper_lower([C|Cs], [V|Vs], D1, D2) :-
|
||||
remove_dist_upper_lower(Cs, Vs, D1, D2).
|
||||
|
||||
|
||||
remove_dist_upper_leq([], _, _).
|
||||
remove_dist_upper_leq([C|Cs], [V|Vs], D1) :-
|
||||
( fd_get(V, VD, VPs) ->
|
||||
remove_dist_upper_leq([], _, _) --> [].
|
||||
remove_dist_upper_leq([C|Cs], [V|Vs], D1) -->
|
||||
( { fd_get(V, VD, VPs) } ->
|
||||
( C < 0 ->
|
||||
domain_supremum(VD, n(Sup)),
|
||||
L is Sup + D1//C,
|
||||
domain_remove_smaller_than(VD, L, VD1)
|
||||
; domain_infimum(VD, n(Inf)),
|
||||
G is Inf + D1//C,
|
||||
domain_remove_greater_than(VD, G, VD1)
|
||||
{ domain_supremum(VD, n(Sup)),
|
||||
L is Sup + D1//C,
|
||||
domain_remove_smaller_than(VD, L, VD1) }
|
||||
; { domain_infimum(VD, n(Inf)),
|
||||
G is Inf + D1//C,
|
||||
domain_remove_greater_than(VD, G, VD1) }
|
||||
),
|
||||
fd_put(V, VD1, VPs)
|
||||
; true
|
||||
@@ -2449,18 +2453,18 @@ remove_dist_upper_leq([C|Cs], [V|Vs], D1) :-
|
||||
remove_dist_upper_leq(Cs, Vs, D1).
|
||||
|
||||
|
||||
remove_dist_upper([], _).
|
||||
remove_dist_upper([C*V|CVs], D) :-
|
||||
( fd_get(V, VD, VPs) ->
|
||||
remove_dist_upper([], _) --> [].
|
||||
remove_dist_upper([C*V|CVs], D) -->
|
||||
( { fd_get(V, VD, VPs) } ->
|
||||
( C < 0 ->
|
||||
( domain_supremum(VD, n(Sup)) ->
|
||||
L is Sup + D//C,
|
||||
domain_remove_smaller_than(VD, L, VD1)
|
||||
( { domain_supremum(VD, n(Sup)) } ->
|
||||
{ L is Sup + D//C,
|
||||
domain_remove_smaller_than(VD, L, VD1) }
|
||||
; VD1 = VD
|
||||
)
|
||||
; ( domain_infimum(VD, n(Inf)) ->
|
||||
G is Inf + D//C,
|
||||
domain_remove_greater_than(VD, G, VD1)
|
||||
; ( { domain_infimum(VD, n(Inf)) } ->
|
||||
{ G is Inf + D//C,
|
||||
domain_remove_greater_than(VD, G, VD1) }
|
||||
; VD1 = VD
|
||||
)
|
||||
),
|
||||
@@ -2469,18 +2473,18 @@ remove_dist_upper([C*V|CVs], D) :-
|
||||
),
|
||||
remove_dist_upper(CVs, D).
|
||||
|
||||
remove_dist_lower([], _).
|
||||
remove_dist_lower([C*V|CVs], D) :-
|
||||
( fd_get(V, VD, VPs) ->
|
||||
remove_dist_lower([], _) --> [].
|
||||
remove_dist_lower([C*V|CVs], D) -->
|
||||
( { fd_get(V, VD, VPs) } ->
|
||||
( C < 0 ->
|
||||
( domain_infimum(VD, n(Inf)) ->
|
||||
G is Inf - D//C,
|
||||
domain_remove_greater_than(VD, G, VD1)
|
||||
( { domain_infimum(VD, n(Inf)) } ->
|
||||
{ G is Inf - D//C,
|
||||
domain_remove_greater_than(VD, G, VD1) }
|
||||
; VD1 = VD
|
||||
)
|
||||
; ( domain_supremum(VD, n(Sup)) ->
|
||||
L is Sup - D//C,
|
||||
domain_remove_smaller_than(VD, L, VD1)
|
||||
; ( { domain_supremum(VD, n(Sup)) } ->
|
||||
{ L is Sup - D//C,
|
||||
domain_remove_smaller_than(VD, L, VD1) }
|
||||
; VD1 = VD
|
||||
)
|
||||
),
|
||||
@@ -2489,26 +2493,26 @@ remove_dist_lower([C*V|CVs], D) :-
|
||||
),
|
||||
remove_dist_lower(CVs, D).
|
||||
|
||||
remove_upper([], _).
|
||||
remove_upper([C*X|CXs], Max) :-
|
||||
( fd_get(X, XD, XPs) ->
|
||||
remove_upper([], _) --> [].
|
||||
remove_upper([C*X|CXs], Max) -->
|
||||
( { fd_get(X, XD, XPs) } ->
|
||||
D is Max//C,
|
||||
( C < 0 ->
|
||||
domain_remove_smaller_than(XD, D, XD1)
|
||||
; domain_remove_greater_than(XD, D, XD1)
|
||||
{ domain_remove_smaller_than(XD, D, XD1) }
|
||||
; { domain_remove_greater_than(XD, D, XD1) }
|
||||
),
|
||||
fd_put(X, XD1, XPs)
|
||||
; true
|
||||
),
|
||||
remove_upper(CXs, Max).
|
||||
|
||||
remove_lower([], _).
|
||||
remove_lower([C*X|CXs], Min) :-
|
||||
( fd_get(X, XD, XPs) ->
|
||||
remove_lower([], _) --> [].
|
||||
remove_lower([C*X|CXs], Min) -->
|
||||
( { fd_get(X, XD, XPs) } ->
|
||||
D is -Min//C,
|
||||
( C < 0 ->
|
||||
domain_remove_greater_than(XD, D, XD1)
|
||||
; domain_remove_smaller_than(XD, D, XD1)
|
||||
{ domain_remove_greater_than(XD, D, XD1) }
|
||||
; { domain_remove_smaller_than(XD, D, XD1) }
|
||||
),
|
||||
fd_put(X, XD1, XPs)
|
||||
; true
|
||||
@@ -2747,20 +2751,24 @@ propagator_init_trigger(Vs, P) :-
|
||||
prop_init(Prop, V) :- init_propagator(V, Prop).
|
||||
|
||||
geq(A, B) :-
|
||||
( fd_get(A, AD, APs) ->
|
||||
domain_infimum(AD, AI),
|
||||
( fd_get(B, BD, _) ->
|
||||
domain_supremum(BD, BS),
|
||||
( AI cis_geq BS -> true
|
||||
; propagator_init_trigger(pgeq(A,B))
|
||||
new_queue(Q),
|
||||
phrase((geq(A, B),do_queue), [Q], _).
|
||||
|
||||
geq(A, B) -->
|
||||
( { fd_get(A, AD, APs) } ->
|
||||
{ domain_infimum(AD, AI) },
|
||||
( { fd_get(B, BD, _) } ->
|
||||
{ domain_supremum(BD, BS) },
|
||||
( { AI cis_geq BS } -> true
|
||||
; { propagator_init_trigger(pgeq(A,B)) }
|
||||
)
|
||||
; ( AI cis_geq n(B) -> true
|
||||
; domain_remove_smaller_than(AD, B, AD1),
|
||||
; ( { AI cis_geq n(B) } -> true
|
||||
; { domain_remove_smaller_than(AD, B, AD1) },
|
||||
fd_put(A, AD1, APs)
|
||||
)
|
||||
)
|
||||
; fd_get(B, BD, BPs) ->
|
||||
domain_remove_greater_than(BD, A, BD1),
|
||||
; { fd_get(B, BD, BPs) } ->
|
||||
{ domain_remove_greater_than(BD, A, BD1) },
|
||||
fd_put(B, BD1, BPs)
|
||||
; A >= B
|
||||
).
|
||||
@@ -4164,6 +4172,7 @@ var(V) --> { var(V) }.
|
||||
ground(T) --> { ground(T) }.
|
||||
|
||||
true --> [].
|
||||
false --> { false }.
|
||||
|
||||
X >= Y --> { X >= Y }.
|
||||
X =< Y --> { X =< Y }.
|
||||
@@ -4221,10 +4230,7 @@ activate_propagator(propagator(P,State)) -->
|
||||
)
|
||||
).
|
||||
|
||||
enable_queue :- true. % NOP
|
||||
disable_queue :- true. % NOP
|
||||
|
||||
%do_queue --> print_queue, { false }.
|
||||
%do_queue --> print_queue, false.
|
||||
do_queue -->
|
||||
( queue_enabled ->
|
||||
( queue_get_goal(Goal) -> { call(Goal) }, do_queue
|
||||
@@ -4507,13 +4513,13 @@ run_propagator(pelement(N, Is, V), MState) -->
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
run_propagator(pgcc_single(Vs, Pairs), _) --> { gcc_global(Vs, Pairs) }.
|
||||
run_propagator(pgcc_single(Vs, Pairs), _) --> gcc_global(Vs, Pairs).
|
||||
|
||||
run_propagator(pgcc_check_single(Pairs), _) --> { gcc_check(Pairs) }.
|
||||
run_propagator(pgcc_check_single(Pairs), _) --> gcc_check(Pairs).
|
||||
|
||||
run_propagator(pgcc_check(Pairs), _) --> { gcc_check(Pairs) }.
|
||||
run_propagator(pgcc_check(Pairs), _) --> gcc_check(Pairs).
|
||||
|
||||
run_propagator(pgcc(Vs, _, Pairs), _) --> { gcc_global(Vs, Pairs) }.
|
||||
run_propagator(pgcc(Vs, _, Pairs), _) --> gcc_global(Vs, Pairs).
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
@@ -4588,7 +4594,7 @@ run_propagator(pserialized(S_I, D_I, S_J, D_J, _), MState) -->
|
||||
kill(MState),
|
||||
( S_I + D_I =< S_J -> []
|
||||
; S_J + D_J =< S_I -> []
|
||||
; { false }
|
||||
; false
|
||||
)
|
||||
; serialize_lower_upper(S_I, D_I, S_J, D_J, MState),
|
||||
serialize_lower_upper(S_J, D_J, S_I, D_I, MState)
|
||||
@@ -4661,7 +4667,7 @@ run_propagator(x_eq_abs_plus_v(X,V), MState) -->
|
||||
( nonvar(V) ->
|
||||
( V =:= 0 -> kill(MState), { X in 0..sup }
|
||||
; V < 0 -> kill(MState), { X #= V / 2 }
|
||||
; V > 0 -> { false }
|
||||
; false % V > 0
|
||||
)
|
||||
; nonvar(X) ->
|
||||
kill(MState),
|
||||
@@ -4753,55 +4759,55 @@ run_propagator(scalar_product_neq(Cs0,Vs0,P0), MState) -->
|
||||
) }.
|
||||
|
||||
run_propagator(scalar_product_leq(Cs0,Vs0,P0), MState) -->
|
||||
{ coeffs_variables_const(Cs0, Vs0, Cs, Vs, 0, I),
|
||||
P is P0 - I,
|
||||
( Vs = [] -> kill(MState), P >= 0
|
||||
; duophrase(sum_finite_domains(Cs, Vs, 0, 0, Inf, Sup), Infs, Sups),
|
||||
D1 is P - Inf,
|
||||
disable_queue,
|
||||
( Infs == [], Sups == [] ->
|
||||
Inf =< P,
|
||||
( Sup =< P -> kill(MState)
|
||||
; remove_dist_upper_leq(Cs, Vs, D1)
|
||||
)
|
||||
; Infs == [] -> Inf =< P, remove_dist_upper(Sups, D1)
|
||||
; Infs = [_] -> remove_upper(Infs, D1)
|
||||
; true
|
||||
),
|
||||
enable_queue
|
||||
) }.
|
||||
{ coeffs_variables_const(Cs0, Vs0, Cs, Vs, 0, I) },
|
||||
P is P0 - I,
|
||||
( Vs = [] -> kill(MState), P >= 0
|
||||
; { duophrase(sum_finite_domains(Cs, Vs, 0, 0, Inf, Sup), Infs, Sups) },
|
||||
D1 is P - Inf,
|
||||
disable_queue,
|
||||
( Infs == [], Sups == [] ->
|
||||
Inf =< P,
|
||||
( Sup =< P -> kill(MState)
|
||||
; remove_dist_upper_leq(Cs, Vs, D1)
|
||||
)
|
||||
; Infs == [] -> Inf =< P, remove_dist_upper(Sups, D1)
|
||||
; Infs = [_] -> remove_upper(Infs, D1)
|
||||
; true
|
||||
),
|
||||
enable_queue
|
||||
).
|
||||
|
||||
run_propagator(scalar_product_eq(Cs0,Vs0,P0), MState) -->
|
||||
{ coeffs_variables_const(Cs0, Vs0, Cs, Vs, 0, I),
|
||||
P is P0 - I,
|
||||
( Vs = [] -> kill(MState), P =:= 0
|
||||
; Vs = [V], Cs = [C] -> kill(MState), P mod C =:= 0, V is P // C
|
||||
; Cs == [1,1] -> kill(MState), Vs = [A,B], A + B #= P
|
||||
; Cs == [1,-1] -> kill(MState), Vs = [A,B], A #= P + B
|
||||
; Cs == [-1,1] -> kill(MState), Vs = [A,B], B #= P + A
|
||||
; Cs == [-1,-1] -> kill(MState), Vs = [A,B], P1 is -P, A + B #= P1
|
||||
; P =:= 0, Cs == [1,1,-1] -> kill(MState), Vs = [A,B,C], A + B #= C
|
||||
; P =:= 0, Cs == [1,-1,1] -> kill(MState), Vs = [A,B,C], A + C #= B
|
||||
; P =:= 0, Cs == [-1,1,1] -> kill(MState), Vs = [A,B,C], B + C #= A
|
||||
; duophrase(sum_finite_domains(Cs, Vs, 0, 0, Inf, Sup), Infs, Sups),
|
||||
% nl, writeln(Infs-Sups-Inf-Sup),
|
||||
D1 is P - Inf,
|
||||
D2 is Sup - P,
|
||||
disable_queue,
|
||||
( Infs == [], Sups == [] ->
|
||||
between(Inf, Sup, P),
|
||||
remove_dist_upper_lower(Cs, Vs, D1, D2)
|
||||
; Sups = [] -> P =< Sup, remove_dist_lower(Infs, D2)
|
||||
; Infs = [] -> Inf =< P, remove_dist_upper(Sups, D1)
|
||||
; Sups = [_], Infs = [_] ->
|
||||
remove_lower(Sups, D2),
|
||||
remove_upper(Infs, D1)
|
||||
; Infs = [_] -> remove_upper(Infs, D1)
|
||||
; Sups = [_] -> remove_lower(Sups, D2)
|
||||
; true
|
||||
),
|
||||
enable_queue
|
||||
) }.
|
||||
{ coeffs_variables_const(Cs0, Vs0, Cs, Vs, 0, I) },
|
||||
P is P0 - I,
|
||||
( Vs = [] -> kill(MState), P =:= 0
|
||||
; Vs = [V], Cs = [C] -> kill(MState), P mod C =:= 0, V is P // C
|
||||
; Cs == [1,1] -> kill(MState), Vs = [A,B], { A + B #= P }
|
||||
; Cs == [1,-1] -> kill(MState), Vs = [A,B], { A #= P + B }
|
||||
; Cs == [-1,1] -> kill(MState), Vs = [A,B], { B #= P + A }
|
||||
; Cs == [-1,-1] -> kill(MState), Vs = [A,B], P1 is -P, { A + B #= P1 }
|
||||
; P =:= 0, Cs == [1,1,-1] -> kill(MState), Vs = [A,B,C], { A + B #= C }
|
||||
; P =:= 0, Cs == [1,-1,1] -> kill(MState), Vs = [A,B,C], { A + C #= B }
|
||||
; P =:= 0, Cs == [-1,1,1] -> kill(MState), Vs = [A,B,C], { B + C #= A }
|
||||
; { duophrase(sum_finite_domains(Cs, Vs, 0, 0, Inf, Sup), Infs, Sups) },
|
||||
% { nl, writeln(Infs-Sups-Inf-Sup) },
|
||||
D1 is P - Inf,
|
||||
D2 is Sup - P,
|
||||
disable_queue,
|
||||
( Infs == [], Sups == [] ->
|
||||
{ between(Inf, Sup, P) },
|
||||
remove_dist_upper_lower(Cs, Vs, D1, D2)
|
||||
; Sups = [] -> P =< Sup, remove_dist_lower(Infs, D2)
|
||||
; Infs = [] -> Inf =< P, remove_dist_upper(Sups, D1)
|
||||
; Sups = [_], Infs = [_] ->
|
||||
remove_lower(Sups, D2),
|
||||
remove_upper(Infs, D1)
|
||||
; Infs = [_] -> remove_upper(Infs, D1)
|
||||
; Sups = [_] -> remove_lower(Sups, D2)
|
||||
; true
|
||||
),
|
||||
enable_queue
|
||||
).
|
||||
|
||||
% X + Y = Z
|
||||
run_propagator(pplus(X,Y,Z,Morph), MState) -->
|
||||
@@ -5048,8 +5054,8 @@ run_propagator(ptzdiv(X,Y,Z,Morph), MState) -->
|
||||
%% % Z = X mod Y
|
||||
|
||||
run_propagator(pmod(X,Y,Z), MState) -->
|
||||
( Y == 0 -> { false }
|
||||
; Y == Z -> { false }
|
||||
( Y == 0 -> false
|
||||
; Y == Z -> false
|
||||
; X == Y -> kill(MState), queue_goal(Z = 0)
|
||||
; true
|
||||
),
|
||||
@@ -5058,7 +5064,7 @@ run_propagator(pmod(X,Y,Z), MState) -->
|
||||
Z is X mod Y
|
||||
; nonvar(Y), nonvar(Z) ->
|
||||
( Y > 0 -> Z >= 0, Z < Y
|
||||
; Y < 0 -> Z =< 0, Z > Y
|
||||
; Z =< 0, Z > Y % Y < 0
|
||||
),
|
||||
( { fd_get(X, _, n(XL), _, _) } ->
|
||||
( (XL - Z) mod Y =\= 0 ->
|
||||
@@ -5127,7 +5133,7 @@ run_propagator(pmodz(X,Y,Z), MState) -->
|
||||
fd_put(Z, ZD2, ZPs)
|
||||
% queue_goal(Z #=< X)
|
||||
)
|
||||
; X < 0 ->
|
||||
; X < 0,
|
||||
( { fd_get(Y, _, _, n(YU), _), YU < X } ->
|
||||
kill(MState),
|
||||
queue_goal(Z = X)
|
||||
@@ -5167,7 +5173,7 @@ run_propagator(pmodz(X,Y,Z), MState) -->
|
||||
fd_put(Z, ZD5, ZPs)
|
||||
% queue_goal(Z in ZMin..0)
|
||||
)
|
||||
; Y > 0 ->
|
||||
; Y > 0,
|
||||
( { fd_get(X, _, n(XL), n(XU), _), XL >= 0, Y > XU } ->
|
||||
kill(MState),
|
||||
queue_goal(Z = X)
|
||||
@@ -5378,8 +5384,9 @@ run_propagator(pmax(X,Y,Z), MState) -->
|
||||
; nonvar(Z) ->
|
||||
( Z =:= X -> kill(MState), queue_goal(X #>= Y)
|
||||
; Z > X -> queue_goal(Z = Y)
|
||||
; { false } % Z < X
|
||||
; false % Z < X
|
||||
)
|
||||
; Y == Z -> kill(MState), queue_goal(Y #>= X)
|
||||
; { fd_get(Y, _, YInf, YSup, _) },
|
||||
( { YInf cis_gt n(X) } -> queue_goal(Z = Y)
|
||||
; { YSup cis_lt n(X) } -> queue_goal(Z = X)
|
||||
@@ -5394,7 +5401,7 @@ run_propagator(pmax(X,Y,Z), MState) -->
|
||||
; { fd_get(Z, ZD, ZPs) } ->
|
||||
{ fd_get(X, _, XInf, XSup, _),
|
||||
fd_get(Y, _, YInf, YSup, _) },
|
||||
( { YInf cis_gt YSup } -> kill(MState), queue_goal(Z = Y)
|
||||
( { YInf cis_gt XSup } -> kill(MState), queue_goal(Z = Y)
|
||||
; { YSup cis_lt XInf } -> kill(MState), queue_goal(Z = X)
|
||||
; { n(M) cis max(XSup, YSup) } ->
|
||||
{ domain_remove_greater_than(ZD, M, ZD1) },
|
||||
@@ -5413,8 +5420,9 @@ run_propagator(pmin(X,Y,Z), MState) -->
|
||||
; nonvar(Z) ->
|
||||
( Z =:= X -> kill(MState), { X #=< Y }
|
||||
; Z < X -> Z = Y
|
||||
; { false } % Z > X
|
||||
; false % Z > X
|
||||
)
|
||||
; Y == Z -> kill(MState), queue_goal(Y #=< X)
|
||||
; { fd_get(Y, _, YInf, YSup, _) },
|
||||
( { YSup cis_lt n(X) } -> Z = Y
|
||||
; { YInf cis_gt n(X) } -> Z = X
|
||||
@@ -5429,7 +5437,7 @@ run_propagator(pmin(X,Y,Z), MState) -->
|
||||
; { fd_get(Z, ZD, ZPs) } ->
|
||||
{ fd_get(X, _, XInf, XSup, _),
|
||||
fd_get(Y, _, YInf, YSup, _) },
|
||||
( { YSup cis_lt YInf } -> kill(MState), Z = Y
|
||||
( { YSup cis_lt XInf } -> kill(MState), Z = Y
|
||||
; { YInf cis_gt XSup } -> kill(MState), Z = X
|
||||
; { n(M) cis min(XInf, YInf) } ->
|
||||
{ domain_remove_smaller_than(ZD, M, ZD1) },
|
||||
@@ -5448,6 +5456,7 @@ run_propagator(pexp(X,Y,Z,Morph), MState) -->
|
||||
morph_into_propagator(MState, [Y,Z], reified_eq(1,Y,1,0,[],Z), Morph)
|
||||
; Y == 0 -> kill(MState), Z = 1
|
||||
; Y == 1 -> kill(MState), Z = X
|
||||
; Y == Z -> kill(MState), X = Y, queue_goal(X in -1\/1)
|
||||
; nonvar(X) ->
|
||||
( nonvar(Y) ->
|
||||
( Y >= 0 -> true ; X =:= -1 ),
|
||||
@@ -5536,7 +5545,7 @@ run_propagator(pexp(X,Y,Z,Morph), MState) -->
|
||||
fd_put(Z, ZD2, ZPs),
|
||||
{ ( even(Y), ZU = n(Num) ->
|
||||
integer_kth_root_leq(Num, Y, RU),
|
||||
( XL cis_geq n(0), ZL = n(Num1) ->
|
||||
( XL cis_geq n(0), ZL = n(Num1), Num1 >= 0 ->
|
||||
integer_kth_root_leq(Num1, Y, RL0),
|
||||
( RL0^Y < Num1 -> RL is RL0 + 1
|
||||
; RL = RL0
|
||||
@@ -5726,8 +5735,7 @@ run_propagator(reified_fd(V,B), MState) -->
|
||||
B = 1
|
||||
; { B == 0 } ->
|
||||
( { fd_inf(V, inf) } -> []
|
||||
; { fd_sup(V, sup) } -> []
|
||||
; { false }
|
||||
; { fd_sup(V, sup) }
|
||||
)
|
||||
; []
|
||||
).
|
||||
@@ -6788,13 +6796,20 @@ gcc_pairs([Key-Num0|KNs], Vs, [Key-Num|Rest]) :-
|
||||
Constraint", AAAI-96 Portland, OR, USA, pp 209--215, 1996
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
gcc_global(Vs, KNs) :-
|
||||
gcc_check(KNs),
|
||||
% previously: call do_queue/0 (now a NOP) here to reach a
|
||||
% fix-point: all elements of clpz_gcc_vs must be variables. We
|
||||
% must ensure this holds if gcc_check/1 is later rewritten to
|
||||
% actually disable the queue.
|
||||
with_local_attributes(Vs,
|
||||
gcc_global(Vs, KNs) -->
|
||||
% at this point, all elements of clpz_gcc_vs must be
|
||||
% variables, which a previously scheduled and called
|
||||
% gcc_check//1 ensures. Note that gcc_check//1 disables the
|
||||
% queue and accumulates constraints in the queue. Do we need
|
||||
% to insert a call of do_queue//0 here to reach a fixpoint? I
|
||||
% think not, because verify_attributes/3 gives each variable
|
||||
% that is involved in a unification an opportunity to schedule
|
||||
% its propagators, even if the unifications happen
|
||||
% simultaneously (such as [A,B] = [0,1], which can happen in
|
||||
% 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.
|
||||
{ with_local_attributes(Vs,
|
||||
(gcc_arcs(KNs, S, Vals),
|
||||
variables_with_num_occurrences(Vs, VNs),
|
||||
maplist(target_to_v(T), VNs),
|
||||
@@ -6805,9 +6820,9 @@ gcc_global(Vs, KNs) :-
|
||||
gcc_consistent(T),
|
||||
scc(Vals, gcc_successors),
|
||||
phrase(gcc_goals(Vals), Gs)
|
||||
; Gs = [] )), Gs),
|
||||
; Gs = [] )), Gs) },
|
||||
disable_queue,
|
||||
maplist(call, Gs),
|
||||
neq_nums(Gs),
|
||||
enable_queue.
|
||||
|
||||
gcc_consistent(T) :-
|
||||
@@ -6834,7 +6849,7 @@ gcc_edge_goal(arc_to(_,_,V,F), Val) -->
|
||||
get_attr(Val, lowlink, L2),
|
||||
L1 =\= L2,
|
||||
get_attr(Val, value, Value) } ->
|
||||
[clpz:neq_num(V, Value)]
|
||||
[neq_num(V, Value)]
|
||||
; []
|
||||
).
|
||||
|
||||
@@ -7004,7 +7019,7 @@ gcc_succ_edge(arc_from(_,_,V,F)) -->
|
||||
consistency.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
gcc_check(Pairs) :-
|
||||
gcc_check(Pairs) -->
|
||||
disable_queue,
|
||||
gcc_check_(Pairs),
|
||||
enable_queue.
|
||||
@@ -7014,36 +7029,36 @@ gcc_done(Num) :-
|
||||
del_attr(Num, clpz_gcc_num),
|
||||
del_attr(Num, clpz_gcc_occurred).
|
||||
|
||||
gcc_check_([]).
|
||||
gcc_check_([Key-Num0|KNs]) :-
|
||||
( get_attr(Num0, clpz_gcc_vs, Vs) ->
|
||||
get_attr(Num0, clpz_gcc_num, Num),
|
||||
get_attr(Num0, clpz_gcc_occurred, Occ0),
|
||||
vs_key_min_others(Vs, Key, 0, Min, Os),
|
||||
put_attr(Num0, clpz_gcc_vs, Os),
|
||||
put_attr(Num0, clpz_gcc_occurred, Occ1),
|
||||
Occ1 is Occ0 + Min,
|
||||
gcc_check_([]) --> [].
|
||||
gcc_check_([Key-Num0|KNs]) -->
|
||||
( { get_attr(Num0, clpz_gcc_vs, Vs) } ->
|
||||
{ get_attr(Num0, clpz_gcc_num, Num),
|
||||
get_attr(Num0, clpz_gcc_occurred, Occ0),
|
||||
vs_key_min_others(Vs, Key, 0, Min, Os),
|
||||
put_attr(Num0, clpz_gcc_vs, Os),
|
||||
put_attr(Num0, clpz_gcc_occurred, Occ1),
|
||||
Occ1 is Occ0 + Min },
|
||||
geq(Num, Occ1),
|
||||
% The queue is disabled for efficiency here in any case.
|
||||
% If it were enabled, make sure to retain the invariant
|
||||
% that gcc_global is never triggered during an
|
||||
% inconsistent state (after gcc_done/1 but before all
|
||||
% relevant constraints are posted).
|
||||
( Occ1 == Num -> all_neq(Os, Key), gcc_done(Num0)
|
||||
; Os == [] -> gcc_done(Num0), Num = Occ1
|
||||
; length(Os, L),
|
||||
Max is Occ1 + L,
|
||||
( Occ1 == Num -> all_neq(Os, Key), { gcc_done(Num0) }
|
||||
; Os == [] -> { gcc_done(Num0) }, Num = Occ1
|
||||
; { length(Os, L),
|
||||
Max is Occ1 + L },
|
||||
geq(Max, Num),
|
||||
( nonvar(Num) -> Diff is Num - Occ1
|
||||
; fd_get(Num, ND, _),
|
||||
domain_infimum(ND, n(NInf)),
|
||||
( { nonvar(Num) } -> Diff is Num - Occ1
|
||||
; { fd_get(Num, ND, _),
|
||||
domain_infimum(ND, n(NInf)) },
|
||||
Diff is NInf - Occ1
|
||||
),
|
||||
L >= Diff,
|
||||
( L =:= Diff ->
|
||||
Num is Occ1 + Diff,
|
||||
maplist(=(Key), Os),
|
||||
gcc_done(Num0)
|
||||
{ maplist(=(Key), Os),
|
||||
gcc_done(Num0) }
|
||||
; true
|
||||
)
|
||||
)
|
||||
@@ -7967,13 +7982,13 @@ coeff_var_term(C-V, T) :- ( C =:= 1 -> T = #V ; T = C * #V ).
|
||||
Reified predicates for use with predicates from library(reif).
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
#=(X, Y, T) :-
|
||||
X #= Y #<==> B,
|
||||
clpz_t(Expr, T) :-
|
||||
Expr #<==> #B,
|
||||
zo_t(B, T).
|
||||
|
||||
#<(X, Y, T) :-
|
||||
X #< Y #<==> B,
|
||||
zo_t(B, T).
|
||||
#=(X, Y, T) :- clpz_t(X #= Y, T).
|
||||
|
||||
#<(X, Y, T) :- clpz_t(X #< Y, T).
|
||||
|
||||
zo_t(0, false).
|
||||
zo_t(1, true).
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Written 2020-2023 by Markus Triska (triska@metalevel.at)
|
||||
Written 2020-2024 by Markus Triska (triska@metalevel.at)
|
||||
Part of Scryer Prolog.
|
||||
|
||||
/** Predicates for cryptographic applications.
|
||||
@@ -10,10 +10,6 @@
|
||||
|
||||
Especially for cryptographic applications, it is an advantage that
|
||||
using strings leaves little trace of what was processed in the system.
|
||||
|
||||
For predicates that accept an `encoding/1` option to specify the encoding
|
||||
of the input data, if `encoding(octet)` is used, then the input can also
|
||||
be specified as a list of _bytes_, i.e., integers between 0 and 255.
|
||||
*/
|
||||
|
||||
:- module(crypto,
|
||||
@@ -25,6 +21,7 @@
|
||||
crypto_password_hash/3, % +Password, -Hash, +Options
|
||||
crypto_data_encrypt/6, % +PlainText, +Algorithm, +Key, +IV, -CipherText, +Options
|
||||
crypto_data_decrypt/6, % +CipherText, +Algorithm, +Key, +IV, -PlainText, +Options
|
||||
ed25519_seed_keypair/2, % +Seed, -KeyPair
|
||||
ed25519_new_keypair/1, % -KeyPair
|
||||
ed25519_keypair_public_key/2, % +KeyPair, +PublicKey
|
||||
ed25519_sign/4, % +KeyPair, +Data, -Signature, +Options
|
||||
@@ -90,9 +87,36 @@ bytes_hex([B|Bs]) --> [C0,C1],
|
||||
},
|
||||
bytes_hex(Bs).
|
||||
|
||||
char_hexval(C, H) :-
|
||||
integer(H),
|
||||
!,
|
||||
hexval_char(H, C).
|
||||
char_hexval(C, H) :- nth0(H, "0123456789abcdef", C), !.
|
||||
char_hexval(C, H) :- nth0(H, "0123456789ABCDEF", C), !.
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
We specialize char_hexval/2 for use if the value is given,
|
||||
so that it works in constant time in this case.
|
||||
|
||||
The security of HMAC verification depends on this property.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
hexval_char(0, '0').
|
||||
hexval_char(1, '1').
|
||||
hexval_char(2, '2').
|
||||
hexval_char(3, '3').
|
||||
hexval_char(4, '4').
|
||||
hexval_char(5, '5').
|
||||
hexval_char(6, '6').
|
||||
hexval_char(7, '7').
|
||||
hexval_char(8, '8').
|
||||
hexval_char(9, '9').
|
||||
hexval_char(0xa, a).
|
||||
hexval_char(0xb, b).
|
||||
hexval_char(0xc, c).
|
||||
hexval_char(0xd, d).
|
||||
hexval_char(0xe, e).
|
||||
hexval_char(0xf, f).
|
||||
|
||||
must_be_bytes(Bytes, Context) :-
|
||||
must_be(list, Bytes),
|
||||
@@ -189,7 +213,19 @@ crypto_random_byte(B) :- '$crypto_random_byte'(B).
|
||||
%
|
||||
% - `encoding(+Encoding)`
|
||||
% The default encoding is `utf8`. The alternative is `octet`, to
|
||||
% treat the input as a list of raw bytes.
|
||||
% use the character code of each character in Data as a byte
|
||||
% value.
|
||||
%
|
||||
% - `hmac(+Key)`
|
||||
% Compute a hash-based message authentication code (HMAC) using
|
||||
% Key, a list of bytes. This option is currently supported for
|
||||
% algorithms `sha256`, `sha384` and `sha512`. If `Hash` is
|
||||
% instantiated, then it is compared with the computed HMAC
|
||||
% in such a way that no information about the expected HMAC
|
||||
% is revealed, using a comparison of strings that always takes
|
||||
% the same time independent of whether and where the strings
|
||||
% differ. This option can therefore also be used to safely
|
||||
% _verify_ a given HMAC.
|
||||
%
|
||||
% Example:
|
||||
%
|
||||
@@ -213,8 +249,33 @@ crypto_data_hash(Data0, Hash, Options0) :-
|
||||
( hash_algorithm(A) -> true
|
||||
; domain_error(hash_algorithm, A, crypto_data_hash/3)
|
||||
),
|
||||
'$crypto_data_hash'(Data, Encoding, HashBytes, A),
|
||||
hex_bytes(Hash, HashBytes).
|
||||
( member(HMAC, Options0), nonvar(HMAC), HMAC = hmac(Ks) ->
|
||||
must_be_bytes(Ks, crypto_data_hash/3),
|
||||
hmac_algorithm(A),
|
||||
'$crypto_hmac'(Data, Encoding, Ks, HashBytes, A),
|
||||
( var(Hash) ->
|
||||
hex_bytes(Hash, HashBytes)
|
||||
; must_be(chars, Hash),
|
||||
hex_bytes(HashMAC, HashBytes),
|
||||
chars_equal_constant_time(Hash, HashMAC)
|
||||
)
|
||||
; '$crypto_data_hash'(Data, Encoding, HashBytes, A),
|
||||
hex_bytes(Hash, HashBytes)
|
||||
).
|
||||
|
||||
chars_equal_constant_time(As, Bs) :-
|
||||
maplist(chars_xor, As, Bs, Xs),
|
||||
sum_list(Xs, Sum),
|
||||
Sum =:= 0.
|
||||
|
||||
chars_xor(A, B, Xor) :-
|
||||
char_code(A, CA),
|
||||
char_code(B, CB),
|
||||
Xor is xor(CA,CB).
|
||||
|
||||
hmac_algorithm(sha256).
|
||||
hmac_algorithm(sha384).
|
||||
hmac_algorithm(sha512).
|
||||
|
||||
options_data_chars(Options, Data, Chars, Encoding) :-
|
||||
option(encoding(Encoding), Options, utf8),
|
||||
@@ -270,7 +331,8 @@ hash_algorithm(blake2b512).
|
||||
% default is all zeroes.
|
||||
% - `encoding(+Encoding)`
|
||||
% The default encoding is `utf8`. The alternative is `octet`,
|
||||
% to treat the input as a list of raw bytes.
|
||||
% to use the character code of each character in Data as a byte
|
||||
% value.
|
||||
%
|
||||
% The `info/1` option can be used to generate multiple keys from a
|
||||
% single master key, using for example values such as "key" and
|
||||
@@ -476,8 +538,9 @@ bytes_base64(Bytes, Base64) :-
|
||||
% Options:
|
||||
%
|
||||
% - `encoding(+Encoding)`
|
||||
% Encoding to use for PlainText. Default is utf8. The alternative
|
||||
% is octet to treat PlainText as raw bytes.
|
||||
% Encoding to use for PlainText. The default is `utf8`. The
|
||||
% alternative is `octet`, to use the character code of each
|
||||
% character in PlainText as a byte value.
|
||||
%
|
||||
% - `tag(-List)`
|
||||
% For authenticated encryption schemes, List is unified with a
|
||||
@@ -563,8 +626,9 @@ algorithm_key_iv('chacha20-poly1305', Key, IV) :-
|
||||
% Options is a list of:
|
||||
%
|
||||
% - `encoding(+Encoding)`
|
||||
% Encoding to use for PlainText. The default is utf8. The
|
||||
% alternative is octet, which is used if the data are raw bytes.
|
||||
% Encoding to use for PlainText. The default is `utf8`. The
|
||||
% alternative is `octet`, to obtain a list of characters where each
|
||||
% character code corresponds to a decrypted octet of CipherText.
|
||||
%
|
||||
% - `tag(+Tag)`
|
||||
% For authenticated encryption schemes, the tag must be specified as
|
||||
@@ -600,6 +664,8 @@ crypto_data_decrypt(CipherText0, Algorithm, Key, IV, PlainText, Options) :-
|
||||
encoding_chars(octet, Bs, Cs) :-
|
||||
must_be(list, Bs),
|
||||
( maplist(integer, Bs) ->
|
||||
% the ability to use integers is deprecated and a
|
||||
% candidate for removal in the future!
|
||||
maplist(char_code, Cs, Bs)
|
||||
; Bs = Cs
|
||||
),
|
||||
@@ -612,6 +678,50 @@ encoding_chars(utf8, Cs, Cs) :-
|
||||
===============================
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
%% ed25519_seed_keypair(+Seed, -Pair)
|
||||
%
|
||||
% Use Seed to deterministically generate an Ed25519 key pair Pair, a
|
||||
% list of characters. Seed must be a list of 32 bytes. It can be
|
||||
% chosen at random (using for example `crypto_n_random_bytes/2`) or
|
||||
% derived from input keying material (IKM) using for example
|
||||
% `crypto_data_hkdf/4`. The pair contains the private key and must be
|
||||
% kept absolutely secret. Pair can be used for signing. Its public
|
||||
% key can be obtained with `ed25519_keypair_public_key/2`.
|
||||
|
||||
ed25519_seed_keypair(Seed, Pair) :-
|
||||
must_be_bytes(Seed, ed25519_keypair_from_seed/2),
|
||||
length(Seed, 32),
|
||||
'$ed25519_seed_to_public_key'(Seed, Public),
|
||||
maplist(char_code, Public, PublicBytes),
|
||||
phrase(ed25519_PKCS8v2(Seed,PublicBytes), DERs),
|
||||
maplist(char_code, Pair, DERs).
|
||||
|
||||
% DER (and hence BER) encoding of an Ed25519 private key and
|
||||
% corresponding public key in PKCS#8v2 format (RFC 5958) as specified
|
||||
% in RFC 8410.
|
||||
|
||||
ed25519_PKCS8v2(Seed, PublicBytes) -->
|
||||
[0x30,81], % a SEQUENCE of 81 bytes follows
|
||||
|
||||
% the publicKey is present, hence we set version to v2
|
||||
[2,1,1], % the integer 1 denoting version 2 (awesome design!)
|
||||
|
||||
% privateKeyAlgorithm: SEQUENCE
|
||||
[0x30,5], % a SEQUENCE of 5 bytes follows
|
||||
[6,3], % an OBJECT IDENTIFIER of 3 bytes follows
|
||||
[43,101,112], % OID of Ed25519
|
||||
|
||||
% privateKey: OCTET STRING
|
||||
[4,34], % an OCTET STRING of 34 bytes follows
|
||||
[4,32], % an OCTET STRING of 32 bytes follows
|
||||
seq(Seed), % the seed is the private key
|
||||
|
||||
% publicKey: [1] IMPLICIT BIT STRING; context-specific, hence bit 7 set
|
||||
[0b10000001], % the public key follows
|
||||
[33], % a BIT STRING of length 33 follows
|
||||
[0], % 32 bytes is divisible by 8, hence 0 unused bits
|
||||
seq(PublicBytes).
|
||||
|
||||
%% ed25519_new_keypair(-Pair)
|
||||
%
|
||||
% Yields a new Ed25519 key pair Pair, a list of characters. The
|
||||
@@ -620,7 +730,8 @@ encoding_chars(utf8, Cs, Cs) :-
|
||||
% with `ed25519_keypair_public_key/2`.
|
||||
|
||||
ed25519_new_keypair(Pair) :-
|
||||
'$ed25519_new_keypair'(Pair).
|
||||
crypto_n_random_bytes(32, Bytes),
|
||||
ed25519_seed_keypair(Bytes, Pair).
|
||||
|
||||
%% ed25519_keypair_public_key(+Pair, -PublicKey)
|
||||
%
|
||||
@@ -629,8 +740,11 @@ ed25519_new_keypair(Pair) :-
|
||||
% The public key is represented as a list of characters.
|
||||
|
||||
ed25519_keypair_public_key(Pair, PublicKey) :-
|
||||
must_be_octet_chars(Pair, ed25519_keypair_public_key),
|
||||
'$ed25519_keypair_public_key'(Pair, PublicKey).
|
||||
must_be_octet_chars(Pair, ed25519_keypair_public_key/2),
|
||||
reverse(Pair, RPs),
|
||||
length(RPublicKey, 32),
|
||||
phrase((seq(RPublicKey),...), RPs),
|
||||
reverse(RPublicKey, PublicKey).
|
||||
|
||||
%% ed25519_sign(+Key, +Data, -Signature, +Options)
|
||||
%
|
||||
@@ -638,10 +752,154 @@ ed25519_keypair_public_key(Pair, PublicKey) :-
|
||||
% PKCS#8 v2 format as generated by `ed25519_new_keypair/1`. Sign Data
|
||||
% with Key, yielding Signature as a list of hexadecimal characters.
|
||||
|
||||
ed25519_sign(Key, Data0, Signature, Options) :-
|
||||
must_be_octet_chars(Key, ed25519_sign),
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Side-channel attacks on Ed25519 predicates
|
||||
==========================================
|
||||
|
||||
Ed25519 predicates where the private key occurs as part of the
|
||||
arguments are potentially subject to side-channel attacks, since
|
||||
key pairs are represented as strings in the context of Ed25519.
|
||||
|
||||
The compact string representation used by Scryer Prolog means that
|
||||
different characters may occupy different numbers of bytes: Due
|
||||
to UTF-8 encoding, characters with codes 1..127 occupy exactly 1 byte,
|
||||
characters with codes 128..2047 occupy exactly 2 bytes, and '0'
|
||||
is represented as a list element occupying an entire cell and
|
||||
dedicated list constructor in addition to string termination and
|
||||
possibly padding.
|
||||
|
||||
This difference is located at the level of the Rust engine. To
|
||||
Prolog code, any two characters look conceptually the same (i.e.,
|
||||
they are both atoms of length 1), and the internal difference in
|
||||
representation cannot be observed at all.
|
||||
|
||||
Very precise timing information or other measurements about
|
||||
operations that reason about such strings may yield information
|
||||
that is meant to stay secret. For example, if Ks is a secret key
|
||||
stored as a list of characters, then the time it takes to run
|
||||
phrase(..., Ks) may reveal the number of bytes in Ks that are 0 or
|
||||
greater than 127.
|
||||
|
||||
To test whether it is possible to detect such differences, I use
|
||||
exp(N) which succeeds exactly 2^N times:
|
||||
|
||||
exp(E) :-
|
||||
N is 2^E,
|
||||
between(1, N, _).
|
||||
|
||||
Here is an example query that uses partial_string/1 to traverse
|
||||
various strings consisting uniformly of characters with the same
|
||||
code, such as 0, 32, 255 and others, in the hope to detect
|
||||
differences in timing if only in such extreme cases:
|
||||
|
||||
?- length(Ls, 256),
|
||||
member(Code, [12,0,55,0,0,32,255,10,0,127,64]),
|
||||
portray_clause(byte=Code),
|
||||
maplist(=(Code), Ls),
|
||||
atom_codes(A, Ls),
|
||||
atom_chars(A, Cs),
|
||||
time((exp(21),partial_string(Cs),false)).
|
||||
%@ byte=12.
|
||||
%@ % CPU time: 1.537s, 14_680_107 inferences
|
||||
%@ byte=0.
|
||||
%@ % CPU time: 1.526s, 14_680_107 inferences
|
||||
%@ byte=55.
|
||||
%@ % CPU time: 1.534s, 14_680_107 inferences
|
||||
%@ byte=0.
|
||||
%@ % CPU time: 1.556s, 14_680_107 inferences
|
||||
%@ byte=0.
|
||||
%@ % CPU time: 1.520s, 14_680_107 inferences
|
||||
%@ byte=32.
|
||||
%@ % CPU time: 1.524s, 14_680_107 inferences
|
||||
%@ byte=255.
|
||||
%@ % CPU time: 1.522s, 14_680_107 inferences
|
||||
%@ byte=10.
|
||||
%@ % CPU time: 1.526s, 14_680_107 inferences
|
||||
%@ byte=0.
|
||||
%@ % CPU time: 1.522s, 14_680_107 inferences
|
||||
%@ byte=127.
|
||||
%@ % CPU time: 1.522s, 14_680_107 inferences
|
||||
%@ byte=64.
|
||||
%@ % CPU time: 1.517s, 14_680_107 inferences
|
||||
%@ false.
|
||||
|
||||
This shows that there is enough variety between runs that
|
||||
traversing a list with 256 elements that are all '\x0\' may even,
|
||||
and unexpectedly, be faster than traversing a list consisting
|
||||
entirely of characters with character code 32, which in turn may be
|
||||
slower than processing a list with 256 characters that all have
|
||||
code 255 and thus occupy twice as much space. This holds even over
|
||||
millions of runs. Reasons for such variety can include CPU power
|
||||
saving mechanisms, dynamic optimizations, prefetching heuristics,
|
||||
branch prediction algorithms, varying system loads etc.
|
||||
|
||||
This gives rise to the suspicion that any such timing differences
|
||||
would be extremely hard to exploit, at least on the architecture I
|
||||
tested it on, also since partial_string/1 is a very low-level
|
||||
operation and any actual processing (using phrase/2 etc.) would
|
||||
introduce additional overheads that in all likelihood far outweigh
|
||||
any differences that can be measured with partial_string/1.
|
||||
|
||||
Any resulting differences in timing and resource use, if they are
|
||||
measurable at all in any way, can at most reveal one bit per byte.
|
||||
Note also that Ed25519 private keys are chosen randomly, and hence
|
||||
half of their bytes are expected to be in 128..255. The predicates
|
||||
remain completely safe to use in all scenarios where no information
|
||||
about the private key can be gathered by unauthorized parties.
|
||||
|
||||
Still, the concern remains: We know that different keys may occupy
|
||||
different numbers of bytes in the internal compact representation
|
||||
of strings used by Scryer Prolog, and it may be possible to exploit
|
||||
these differences to obtain information that is meant to be kept
|
||||
secret. We must therefore keep an eye on this issue. For example,
|
||||
it may become a concern on very slow devices such as ID-cards where
|
||||
Scryer Prolog may be deployed in the future and where such timing
|
||||
differences may be detectable, or if Scryer Prolog itself becomes
|
||||
so fast that the relative overhead of such low-level operations
|
||||
becomes greater and thus more easily measurable.
|
||||
|
||||
Possible mitigations in such situations would be to:
|
||||
|
||||
1. use lists of integers to represent Ed25519 key pairs, resulting
|
||||
in a 24-fold space increase. This may be prohibitive in
|
||||
applications that manage a great number of keys. The Rust code
|
||||
would not be affected by this change, since it already operates
|
||||
on bytes. A Prolog application may implement this privately, and
|
||||
also encourage an API change of this library.
|
||||
2. introduce a compact internal representation for lists of bytes,
|
||||
which appear to Prolog programs as lists of characters.
|
||||
|
||||
(2) seems to be the better solution despite the implementation
|
||||
overhead: In addition to the improved security properties due to
|
||||
the elimination of side-channel attacks when reasoning about keys,
|
||||
all applications that reason about binary data would benefit from a
|
||||
more compact representation of binary data. Such an additional
|
||||
compact representation should only be attempted if the amount of
|
||||
Rust code it impacts is kept to the absolute minimum, certainly
|
||||
much smaller than what the compact string representation as it is
|
||||
currently implemented affects.
|
||||
|
||||
*No* solution would be to:
|
||||
|
||||
- eliminate the compact string representation from the engine and
|
||||
use plain lists of characters to represent Ed25519 key pairs,
|
||||
- continue to use lists of characters for Ed25519 key pairs,
|
||||
and ensure that they are never coalesced into compact strings
|
||||
(a future GC compaction step may need to be adapted for this)
|
||||
|
||||
This is because atom names are still represented in UTF-8 encoding,
|
||||
and are hence also susceptible to side-channel attacks due to their
|
||||
using different numbers of bytes for different codes.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
ed25519_sign(KeyPair, Data0, Signature, Options) :-
|
||||
must_be_octet_chars(KeyPair, ed25519_sign/4),
|
||||
length(Prefix, 16),
|
||||
length(PrivateKeyChars, 32),
|
||||
phrase((seq(Prefix),seq(PrivateKeyChars),...), KeyPair),
|
||||
maplist(char_code, PrivateKeyChars, PrivateKey),
|
||||
options_data_chars(Options, Data0, Data, Encoding),
|
||||
'$ed25519_sign'(Key, Data, Encoding, Signature0),
|
||||
'$ed25519_sign_raw'(PrivateKey, Data, Encoding, Signature0),
|
||||
hex_bytes(Signature, Signature0).
|
||||
|
||||
%% ed25519_verify(+Key, +Data, +Signature, +Options)
|
||||
@@ -655,13 +913,14 @@ ed25519_sign(Key, Data0, Signature, Options) :-
|
||||
%
|
||||
% - `encoding(+Encoding)`
|
||||
% The default encoding of Data is `utf8`. The alternative is `octet`,
|
||||
% which treats Data as a list of raw bytes.
|
||||
% to use the character code of each character in Data as a byte
|
||||
% value.
|
||||
|
||||
ed25519_verify(Key, Data0, Signature0, Options) :-
|
||||
must_be_octet_chars(Key, ed25519_verify),
|
||||
must_be_octet_chars(Key, ed25519_verify/4),
|
||||
options_data_chars(Options, Data0, Data, Encoding),
|
||||
hex_bytes(Signature0, Signature),
|
||||
'$ed25519_verify'(Key, Data, Encoding, Signature).
|
||||
'$ed25519_verify_raw'(Key, Data, Encoding, Signature).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
X25519: ECDH key exchange over Curve25519
|
||||
@@ -761,9 +1020,26 @@ curve_a(curve(_,_,A,_,_,_,_,_), A).
|
||||
curve_b(curve(_,_,_,B,_,_,_,_), B).
|
||||
curve_field_length(curve(_,_,_,_,_,_,FieldLength,_), FieldLength).
|
||||
|
||||
%% crypto_curve_generator(+Curve, -G)
|
||||
%
|
||||
% Yields the generator point G of Curve.
|
||||
|
||||
crypto_curve_generator(curve(_,_,_,_,G,_,_,_), G).
|
||||
|
||||
%% crypto_curve_order(+Curve, -Order)
|
||||
%
|
||||
% Yields the order of Curve.
|
||||
|
||||
crypto_curve_order(curve(_,_,_,_,_,Order,_,_), Order).
|
||||
|
||||
%% crypto_curve_scalar_mult(+Curve, +Scalar, +Point, -Result)
|
||||
%
|
||||
% Computes the point _Result = Scalar*Point_. Scalar must be an
|
||||
% integer, and Point must be a point on Curve. This operation can be
|
||||
% used to negotiate a shared secret over a public channel. Consider
|
||||
% using `curve25519_scalar_mult/3` instead for more desirable
|
||||
% security properties.
|
||||
|
||||
crypto_curve_scalar_mult(Curve, Scalar, point(X,Y), point(RX, RY)) :-
|
||||
must_be(integer, Scalar),
|
||||
must_be_on_curve(Curve, point(X,Y)),
|
||||
@@ -830,6 +1106,12 @@ fitting_exponent(N, E0, E) :-
|
||||
fitting_exponent(N, E1, E)
|
||||
).
|
||||
|
||||
%% crypto_name_curve(+Name, -Curve)
|
||||
%
|
||||
% Yields a representation of the elliptic curve with name Name.
|
||||
% Currently, the only supported name is `secp256k1`, a Koblitz curve
|
||||
% regarded as secure.
|
||||
|
||||
crypto_name_curve(secp256k1,
|
||||
curve(secp256k1,
|
||||
0x00fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f,
|
||||
|
||||
@@ -221,7 +221,7 @@ row([X | Y], Opt) -->
|
||||
!,
|
||||
( separator(Opt) ->
|
||||
row(Y, Opt)
|
||||
; end_token ->
|
||||
; end_token,
|
||||
{ Y = [] }).
|
||||
|
||||
|
||||
|
||||
@@ -12,9 +12,12 @@ to learn more about them.
|
||||
[op(1105, xfy, '|'),
|
||||
phrase/2,
|
||||
phrase/3,
|
||||
phrase/4,
|
||||
phrase/5,
|
||||
seq//1,
|
||||
seqq//1,
|
||||
... //0
|
||||
... //0,
|
||||
(-->)/2
|
||||
]).
|
||||
|
||||
:- use_module(library(error)).
|
||||
@@ -26,6 +29,14 @@ to learn more about them.
|
||||
|
||||
:- meta_predicate phrase(2, ?, ?).
|
||||
|
||||
:- meta_predicate phrase(2, ?, ?, ?).
|
||||
|
||||
:- meta_predicate phrase(2, ?, ?, ?, ?).
|
||||
|
||||
:- meta_predicate(','(2, 2, ?, ?)).
|
||||
|
||||
:- meta_predicate(;(2, 2, ?, ?)).
|
||||
|
||||
%% phrase(+Body, ?Ls).
|
||||
%
|
||||
% True iff Body describes the list Ls. Body must be a DCG body.
|
||||
@@ -75,6 +86,34 @@ phrase(GRBody, S0, S) :-
|
||||
; call(M:GRBody1, S0, S)
|
||||
).
|
||||
|
||||
phrase(GRBody, Arg, S0, S) :-
|
||||
strip_module(GRBody, M, GRBody1),
|
||||
( var(GRBody) ->
|
||||
instantiation_error(phrase/4)
|
||||
; nonvar(GRBody1),
|
||||
GRBody1 =.. GRBodys1,
|
||||
append(GRBodys1, [Arg], GRBodys2),
|
||||
GRBody2 =.. GRBodys2,
|
||||
dcg_constr(GRBody2),
|
||||
dcg_body(GRBody2, S0, S, GRBody3) ->
|
||||
call(M:GRBody3)
|
||||
; call(M:GRBody1, Arg, S0, S)
|
||||
).
|
||||
|
||||
phrase(GRBody, Arg1, Arg2, S0, S) :-
|
||||
strip_module(GRBody, M, GRBody1),
|
||||
( var(GRBody) ->
|
||||
instantiation_error(phrase/5)
|
||||
; nonvar(GRBody1),
|
||||
GRBody1 =.. GRBodys1,
|
||||
append(GRBodys1, [Arg1,Arg2], GRBodys2),
|
||||
GRBody2 =.. GRBodys2,
|
||||
dcg_constr(GRBody2),
|
||||
dcg_body(GRBody2, S0, S, GRBody3) ->
|
||||
call(M:GRBody3)
|
||||
; call(M:GRBody1, Arg1, Arg2, S0, S)
|
||||
).
|
||||
|
||||
% The same version of the below two dcg_rule clauses, but with module scoping.
|
||||
dcg_rule(( M:NonTerminal, Terminals --> GRBody ), ( M:Head :- Body )) :-
|
||||
dcg_non_terminal(NonTerminal, S0, S, Head),
|
||||
@@ -101,7 +140,10 @@ dcg_rule(( NonTerminal --> GRBody ), ( Head :- Body )) :-
|
||||
dcg_non_terminal(NonTerminal, S0, S, Goal) :-
|
||||
NonTerminal =.. NonTerminalUniv,
|
||||
append(NonTerminalUniv, [S0, S], GoalUniv),
|
||||
Goal =.. GoalUniv.
|
||||
( callable(NonTerminal) ->
|
||||
Goal =.. GoalUniv
|
||||
; Goal = NonTerminal % let call/N throw an error instead of throwing one here.
|
||||
).
|
||||
|
||||
dcg_terminals(Terminals, S0, S, S0 = List) :-
|
||||
append(Terminals, S, List).
|
||||
@@ -116,8 +158,6 @@ dcg_body(GRBody, S0, S, Body) :-
|
||||
dcg_body(NonTerminal, S0, S, Goal1) :-
|
||||
nonvar(NonTerminal),
|
||||
\+ dcg_constr(NonTerminal),
|
||||
NonTerminal \= ( _ -> _ ),
|
||||
NonTerminal \= ( \+ _ ),
|
||||
loader:strip_module(NonTerminal, M, NonTerminal0),
|
||||
dcg_non_terminal(NonTerminal0, S0, S, Goal0),
|
||||
( functor(NonTerminal, (:), 2) ->
|
||||
@@ -135,9 +175,13 @@ dcg_constr(( _'|'_ )). % 7.14.6 - alternative
|
||||
dcg_constr({_}). % 7.14.7
|
||||
dcg_constr(call(_)). % 7.14.8
|
||||
dcg_constr(phrase(_)). % 7.14.9
|
||||
dcg_constr(phrase(_,_)). % extension of 7.14.9
|
||||
dcg_constr(phrase(_,_,_)). % extension of 7.14.9
|
||||
dcg_constr(!). % 7.14.10
|
||||
%% dcg_constr(\+ _). % 7.14.11 - not (existence implementation dep.)
|
||||
dcg_constr((_->_)). % 7.14.12 - if-then (existence implementation dep.)
|
||||
dcg_constr(\+ G_0) :- % 7.14.11 - not (existence implementation def.)
|
||||
throw(error(representation_error(dcg_body), [culprit- (\+ G_0)])).
|
||||
dcg_constr((If->Then)) :- % 7.14.12 - if-then (existence implementation def.)
|
||||
throw(error(representation_error(dcg_body), [culprit- (If->Then)])).
|
||||
|
||||
% The principal functor of the first argument indicates
|
||||
% the construct to be expanded.
|
||||
@@ -162,8 +206,10 @@ dcg_cbody(( GREither '|' GROr ), S0, S, ( Either ; Or )) :-
|
||||
dcg_cbody({Goal}, S0, S, ( Goal, S0 = S )).
|
||||
dcg_cbody(call(Cont), S0, S, call(Cont, S0, S)).
|
||||
dcg_cbody(phrase(Body), S0, S, phrase(Body, S0, S)).
|
||||
dcg_cbody(phrase(Body, Arg), S0, S, phrase(Body, Arg, S0, S)).
|
||||
dcg_cbody(phrase(Body, Arg1, Arg2), S0, S, phrase(Body, Arg1, Arg2, S0, S)).
|
||||
dcg_cbody(!, S0, S, ( !, S0 = S )).
|
||||
dcg_cbody(\+ GRBody, S0, S, ( \+ phrase(GRBody,S0,_), S0 = S )).
|
||||
% dcg_cbody(\+ GRBody, S0, S, ( \+ phrase(GRBody,S0,_), S0 = S )).
|
||||
dcg_cbody(( GRIf -> GRThen ), S0, S, ( If -> Then )) :-
|
||||
dcg_body(GRIf, S0, S1, If),
|
||||
dcg_body(GRThen, S1, S, Then).
|
||||
@@ -200,8 +246,13 @@ seqq([Es|Ess]) --> seq(Es), seqq(Ess).
|
||||
Cs0 = Cs.
|
||||
... --> [] | [_], ... .
|
||||
|
||||
% defer instantiation errors until runtime. instantiations may be made
|
||||
% then.
|
||||
error_goal(error(instantiation_error, _Context), _).
|
||||
error_goal(error(E, must_be/2), error(E, must_be/2)).
|
||||
error_goal(error(E, (=..)/2), error(E, (=..)/2)).
|
||||
error_goal(error(representation_error(dcg_body), Context),
|
||||
error(representation_error(dcg_body), Context)).
|
||||
error_goal(E, _) :- throw(E).
|
||||
|
||||
user:goal_expansion(phrase(GRBody, S, S0), GRBody2) :-
|
||||
@@ -211,9 +262,20 @@ user:goal_expansion(phrase(GRBody, S, S0), GRBody2) :-
|
||||
E,
|
||||
dcgs:error_goal(E, GRBody1)
|
||||
),
|
||||
( GRBody = (_:_) ->
|
||||
( E = error(instantiation_error, _),
|
||||
GRBody0 = [T|Ts] ->
|
||||
GRBody2 = (error:must_be(list, [T|Ts]),
|
||||
lists:append([T|Ts], S0, S))
|
||||
; GRBody = (_:_) ->
|
||||
GRBody2 = M:GRBody1
|
||||
; GRBody2 = GRBody1
|
||||
).
|
||||
|
||||
user:goal_expansion(phrase(GRBody, S), phrase(GRBody, S, [])).
|
||||
|
||||
|
||||
% (-->)/2 behaves as if it didn't exist. We export (and define) it
|
||||
% only so that clauses for (-->)/2 cannot be asserted when
|
||||
% library(dcgs) is loaded.
|
||||
|
||||
(_-->_) :- throw(error(existence_error(procedure,(-->)/2),(-->)/2)).
|
||||
|
||||
@@ -53,5 +53,4 @@ $(G_0) :-
|
||||
%
|
||||
% Generalize away Goal.
|
||||
|
||||
|
||||
*(_).
|
||||
|
||||
@@ -33,11 +33,13 @@ remove_goal([G0|G0s], Goal0, Goals) :-
|
||||
|
||||
vars_remove_goal([], _).
|
||||
vars_remove_goal([Var|Vars], Goal0) :-
|
||||
get_atts(Var, +dif(Goals0)),
|
||||
remove_goal(Goals0, Goal0, Goals),
|
||||
( Goals = [] ->
|
||||
put_atts(Var, -dif(_))
|
||||
; put_atts(Var, +dif(Goals))
|
||||
( get_atts(Var, +dif(Goals0)) ->
|
||||
remove_goal(Goals0, Goal0, Goals),
|
||||
( Goals = [] ->
|
||||
put_atts(Var, -dif(_))
|
||||
; put_atts(Var, +dif(Goals))
|
||||
)
|
||||
; true
|
||||
),
|
||||
vars_remove_goal(Vars, Goal0).
|
||||
|
||||
|
||||
@@ -14,31 +14,29 @@
|
||||
:- meta_predicate check_(1, ?, ?).
|
||||
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
must_be(Type, Term)
|
||||
|
||||
This predicate is intended for type-checks of built-in predicates.
|
||||
|
||||
It asserts that Term is:
|
||||
|
||||
1) instantiated *and*
|
||||
2) instantiated to an instance of the given Type.
|
||||
|
||||
It corresponds to usage mode +Term.
|
||||
|
||||
Currently, the following types are supported:
|
||||
|
||||
- atom
|
||||
- boolean
|
||||
- character
|
||||
- chars
|
||||
- in_character
|
||||
- integer
|
||||
- list
|
||||
- octet_character
|
||||
- octet_chars
|
||||
- term
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
%% must_be(Type, Term)
|
||||
%
|
||||
% This predicate is intended for type-checks of built-in predicates.
|
||||
%
|
||||
% It asserts that Term is:
|
||||
%
|
||||
% 1) instantiated *and*
|
||||
% 2) instantiated to an instance of the given Type.
|
||||
%
|
||||
% It corresponds to usage mode +Term.
|
||||
%
|
||||
% Currently, the following types are supported:
|
||||
%
|
||||
% - atom
|
||||
% - boolean
|
||||
% - character
|
||||
% - chars
|
||||
% - in_character
|
||||
% - integer
|
||||
% - list
|
||||
% - octet_character
|
||||
% - octet_chars
|
||||
% - term
|
||||
|
||||
must_be(Type, Term) :-
|
||||
must_be_(type, Type),
|
||||
@@ -142,19 +140,16 @@ type(boolean).
|
||||
type(term).
|
||||
type(not_less_than_zero).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
can_be(Type, Term)
|
||||
|
||||
This predicate is intended for type-checks of built-in predicates.
|
||||
|
||||
It asserts that there is a substitution which, if applied to Term,
|
||||
makes it an instance of Type.
|
||||
|
||||
It corresponds to usage mode ?Term.
|
||||
|
||||
It supports the same types as must_be/2.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
%% can_be(Type, Term)
|
||||
%
|
||||
% This predicate is intended for type-checks of built-in predicates.
|
||||
%
|
||||
% It asserts that there is a substitution which, if applied to Term,
|
||||
% makes it an instance of Type.
|
||||
%
|
||||
% It corresponds to usage mode ?Term.
|
||||
%
|
||||
% It supports the same types as must_be/2.
|
||||
|
||||
can_be(Type, Term) :-
|
||||
must_be(type, Type),
|
||||
|
||||
@@ -178,7 +178,7 @@ directory_must_exist(Directory, Context) :-
|
||||
; throw(error(existence_error(directory, Directory), Context))
|
||||
).
|
||||
|
||||
%% workind_directory(Dir0, Dir).
|
||||
%% working_directory(Dir0, Dir).
|
||||
%
|
||||
% Dir0 is the current working directory, and the working directory
|
||||
% is changed to Dir.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Written 2020-2023 by Markus Triska (triska@metalevel.at)
|
||||
Written 2020-2024 by Markus Triska (triska@metalevel.at)
|
||||
Part of Scryer Prolog.
|
||||
I place this code in the public domain. Use it in any way you want.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
@@ -84,12 +84,66 @@
|
||||
% ```
|
||||
|
||||
format_(Fs, Args) -->
|
||||
{ must_be(list, Fs),
|
||||
must_be(list, Args),
|
||||
unique_variable_names(Args, VNs),
|
||||
phrase(cells(Fs,Args,0,[],VNs), Cells) },
|
||||
{ format_args_cells(Fs, Args, Cells) },
|
||||
format_cells(Cells).
|
||||
|
||||
format_args_cells(Fs, Args, Cells) :-
|
||||
must_be(chars, Fs),
|
||||
must_be(list, Args),
|
||||
unique_variable_names(Args, VNs),
|
||||
phrase(cells(Fs,Args,0,[],VNs), Cells).
|
||||
|
||||
unique_variable_names(Term, VNs) :-
|
||||
term_variables(Term, Vs),
|
||||
foldl(var_name, Vs, VNs, 0, _).
|
||||
|
||||
var_name(V, Name=V, Num0, Num) :-
|
||||
charsio:fabricate_var_name(numbervars, Name, Num0),
|
||||
Num is Num0 + 1.
|
||||
|
||||
user:goal_expansion(format_(Fs,Args,Cs0,Cs),
|
||||
format:format_cells(Cells, Cs0, Cs)) :-
|
||||
catch(format_args_cells(Fs,Args,Cells),
|
||||
E,
|
||||
% no partial evaluation for uses of format_//2 that
|
||||
% cannot be compiled statically, for example those where
|
||||
% the argument list is a variable, or where ~*n occurs
|
||||
% in the format string, or a domain error occurs
|
||||
( ( E = error(instantiation_error,_)
|
||||
; E = error(domain_error(_,_), _)
|
||||
) ->
|
||||
false
|
||||
; throw(E)
|
||||
)).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Partial evaluation of goals involving conditions that can be
|
||||
checked at compilation time. This is especially useful for the
|
||||
common case of conditions that test a numeric argument against 0.
|
||||
It is currently used for the goals of ~d.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
goal_pe(G0, G) :- var(G0), !, G = G0.
|
||||
goal_pe((A0,B0), (A,B)) :- !, goal_pe(A0, A), goal_pe(B0, B).
|
||||
goal_pe((Body0 ; Else0), Body) :-
|
||||
nonvar(Body0),
|
||||
Body0 = ( If -> Then0 ),
|
||||
!,
|
||||
( ground(If) ->
|
||||
( If ->
|
||||
goal_pe(Then0, Body)
|
||||
; goal_pe(Else0, Body)
|
||||
)
|
||||
; goal_pe(Then0, Then),
|
||||
goal_pe(Else0, Else),
|
||||
Body = ( If -> Then ; Else )
|
||||
).
|
||||
goal_pe(Goal, Goal).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
format_cells//1 is an interpreter for cells, describing a string.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
format_cells([]) --> [].
|
||||
format_cells([Cell|Cells]) -->
|
||||
format_cell(Cell),
|
||||
@@ -125,6 +179,7 @@ format_element(glue(Fill,Num)) -->
|
||||
{ length(Ls, Num),
|
||||
maplist(=(Fill), Ls) },
|
||||
seq(Ls).
|
||||
format_element(goal(_)) --> [].
|
||||
|
||||
elements_gluevars([], N, N) --> [].
|
||||
elements_gluevars([E|Es], N0, N) -->
|
||||
@@ -135,6 +190,7 @@ element_gluevar(chars(Cs), N0, N) -->
|
||||
{ length(Cs, L),
|
||||
N is N0 + L }.
|
||||
element_gluevar(glue(_,V), N, N) --> [V].
|
||||
element_gluevar(goal(G), N, N) --> { G }.
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Our key datastructure is a list of cells and newlines.
|
||||
@@ -142,12 +198,16 @@ element_gluevar(glue(_,V), N, N) --> [V].
|
||||
From and To denote the positions of surrounding tab stops.
|
||||
|
||||
Elements is a list of elements that occur in a cell,
|
||||
namely terms of the form chars(Cs) and glue(Char, Var).
|
||||
namely terms of the form chars(Cs), glue(Char, Var)
|
||||
and goal(G).
|
||||
|
||||
"glue" elements (TeX terminology) are evenly stretched
|
||||
to fill the remaining whitespace in the cell. For each
|
||||
glue element, the character Char is used for filling,
|
||||
and Var is a free variable that is used when the
|
||||
available space is distributed.
|
||||
available space is distributed. Goals are dynamically
|
||||
executed to obtain characters. In this way, format strings
|
||||
can be parsed and compiled statically when possible.
|
||||
|
||||
newline is used if ~n occurs in a format string.
|
||||
It is used because a newline character does not
|
||||
@@ -161,54 +221,55 @@ cells([], Args, Tab, Es, _) --> !,
|
||||
cells([~,~|Fs], Args, Tab, Es, VNs) --> !,
|
||||
cells(Fs, Args, Tab, [chars("~")|Es], VNs).
|
||||
cells([~,w|Fs], [Arg|Args], Tab, Es, VNs) --> !,
|
||||
{ write_term_to_chars(Arg, [numbervars(true),variable_names(VNs)], Chars) },
|
||||
cells(Fs, Args, Tab, [chars(Chars)|Es], VNs).
|
||||
{ G = write_term_to_chars(Arg, [numbervars(true),variable_names(VNs)], Chars) },
|
||||
cells(Fs, Args, Tab, [chars(Chars),goal(G)|Es], VNs).
|
||||
cells([~,q|Fs], [Arg|Args], Tab, Es, VNs) --> !,
|
||||
{ write_term_to_chars(Arg, [quoted(true),numbervars(true),variable_names(VNs)], Chars) },
|
||||
cells(Fs, Args, Tab, [chars(Chars)|Es], VNs).
|
||||
{ G = write_term_to_chars(Arg, [quoted(true),numbervars(true),variable_names(VNs)], Chars) },
|
||||
cells(Fs, Args, Tab, [chars(Chars),goal(G)|Es], VNs).
|
||||
cells([~,a|Fs], [Arg|Args], Tab, Es, VNs) --> !,
|
||||
{ atom_chars(Arg, Chars) },
|
||||
cells(Fs, Args, Tab, [chars(Chars)|Es], VNs).
|
||||
{ G = atom_chars(Arg, Chars) },
|
||||
cells(Fs, Args, Tab, [chars(Chars),goal(G)|Es], VNs).
|
||||
cells([~|Fs0], Args0, Tab, Es, VNs) -->
|
||||
{ numeric_argument(Fs0, Num, [d|Fs], Args0, [Arg0|Args]) },
|
||||
!,
|
||||
{ Arg is Arg0, % evaluate compound expression
|
||||
must_be(integer, Arg),
|
||||
number_chars(Arg, Cs0) },
|
||||
( { Num =:= 0 } -> { Cs = Cs0 }
|
||||
; { length(Cs0, L),
|
||||
( L =< Num ->
|
||||
Delta is Num - L,
|
||||
length(Zs, Delta),
|
||||
maplist(=('0'), Zs),
|
||||
phrase(("0.",seq(Zs),seq(Cs0)), Cs)
|
||||
; BeforeComma is L - Num,
|
||||
length(Bs, BeforeComma),
|
||||
append(Bs, Ds, Cs0),
|
||||
phrase((seq(Bs),".",seq(Ds)), Cs)
|
||||
) }
|
||||
),
|
||||
cells(Fs, Args, Tab, [chars(Cs)|Es], VNs).
|
||||
{ G0 = ( Arg is Arg0, % evaluate compound expression
|
||||
must_be(integer, Arg),
|
||||
number_chars(Arg, Cs0),
|
||||
( Num =:= 0 -> Cs = Cs0
|
||||
; length(Cs0, L),
|
||||
( L =< Num ->
|
||||
Delta is Num - L,
|
||||
length(Zs, Delta),
|
||||
maplist(=('0'), Zs),
|
||||
phrase(("0.",seq(Zs),seq(Cs0)), Cs)
|
||||
; BeforeComma is L - Num,
|
||||
length(Bs, BeforeComma),
|
||||
append(Bs, Ds, Cs0),
|
||||
phrase((seq(Bs),".",seq(Ds)), Cs)
|
||||
)
|
||||
)),
|
||||
goal_pe(G0, G) },
|
||||
cells(Fs, Args, Tab, [chars(Cs),goal(G)|Es], VNs).
|
||||
cells([~|Fs0], Args0, Tab, Es, VNs) -->
|
||||
{ numeric_argument(Fs0, Num, ['D'|Fs], Args0, [Arg|Args]) },
|
||||
!,
|
||||
{ separate_digits_fractional(Arg, ',', Num, Cs) },
|
||||
cells(Fs, Args, Tab, [chars(Cs)|Es], VNs).
|
||||
{ G = separate_digits_fractional(Arg, ',', Num, Cs) },
|
||||
cells(Fs, Args, Tab, [chars(Cs),goal(G)|Es], VNs).
|
||||
cells([~|Fs0], Args0, Tab, Es, VNs) -->
|
||||
{ numeric_argument(Fs0, Num, ['U'|Fs], Args0, [Arg|Args]) },
|
||||
!,
|
||||
{ separate_digits_fractional(Arg, '_', Num, Cs) },
|
||||
cells(Fs, Args, Tab, [chars(Cs)|Es], VNs).
|
||||
{ G = separate_digits_fractional(Arg, '_', Num, Cs) },
|
||||
cells(Fs, Args, Tab, [chars(Cs),goal(G)|Es], VNs).
|
||||
cells([~|Fs0], Args0, Tab, Es, VNs) -->
|
||||
{ numeric_argument(Fs0, Num0, ['L'|Fs], Args0, [Arg|Args]) },
|
||||
!,
|
||||
{ ( Num0 =:= 0 ->
|
||||
Num = 72
|
||||
; Num = Num0
|
||||
),
|
||||
phrase(format_("~d", [Arg]), Cs0),
|
||||
phrase(split_lines_width(Cs0, Num), Cs) },
|
||||
cells(Fs, Args, Tab, [chars(Cs)|Es], VNs).
|
||||
{ G = (( Num0 =:= 0 ->
|
||||
Num = 72
|
||||
; Num = Num0
|
||||
),
|
||||
phrase(format_("~d", [Arg]), Cs0),
|
||||
phrase(split_lines_width(Cs0, Num), Cs) ) },
|
||||
cells(Fs, Args, Tab, [chars(Cs),goal(G)|Es], VNs).
|
||||
cells([~,i|Fs], [_|Args], Tab, Es, VNs) --> !,
|
||||
cells(Fs, Args, Tab, Es, VNs).
|
||||
cells([~,n|Fs], Args, Tab, Es, VNs) --> !,
|
||||
@@ -224,58 +285,63 @@ cells([~|Fs0], Args0, Tab, Es, VNs) -->
|
||||
cells([~,s|Fs], [Arg|Args], Tab, Es, VNs) --> !,
|
||||
cells(Fs, Args, Tab, [chars(Arg)|Es], VNs).
|
||||
cells([~,f|Fs], [Arg|Args], Tab, Es, VNs) --> !,
|
||||
{ format_number_chars(Arg, Chars) },
|
||||
cells(Fs, Args, Tab, [chars(Chars)|Es], VNs).
|
||||
{ G = format_number_chars(Arg, Chars) },
|
||||
cells(Fs, Args, Tab, [chars(Chars),goal(G)|Es], VNs).
|
||||
cells([~|Fs0], Args0, Tab, Es, VNs) -->
|
||||
{ numeric_argument(Fs0, Num, [f|Fs], Args0, [Arg|Args]) },
|
||||
!,
|
||||
{ format_number_chars(Arg, Cs0),
|
||||
phrase(upto_what(Bs, .), Cs0, Cs),
|
||||
( Num =:= 0 -> Chars = Bs
|
||||
; ( Cs = ['.'|Rest] ->
|
||||
length(Rest, L),
|
||||
( Num < L ->
|
||||
length(Ds, Num),
|
||||
append(Ds, _, Rest)
|
||||
; Num =:= L ->
|
||||
Ds = Rest
|
||||
; Num > L,
|
||||
Delta is Num - L,
|
||||
% we should look into the float with
|
||||
% greater accuracy here, and use the
|
||||
% actual digits instead of 0.
|
||||
length(Zs, Delta),
|
||||
maplist(=('0'), Zs),
|
||||
append(Rest, Zs, Ds)
|
||||
)
|
||||
; length(Ds, Num),
|
||||
maplist(=('0'), Ds)
|
||||
),
|
||||
append(Bs, ['.'|Ds], Chars)
|
||||
) },
|
||||
cells(Fs, Args, Tab, [chars(Chars)|Es], VNs).
|
||||
{ G = (format_number_chars(Arg, Cs0),
|
||||
phrase(upto_what(Bs, .), Cs0, Cs),
|
||||
( Num =:= 0 -> Chars = Bs
|
||||
; ( Cs = ['.'|Rest] ->
|
||||
length(Rest, L),
|
||||
( Num < L ->
|
||||
length(Ds, Num),
|
||||
append(Ds, _, Rest)
|
||||
; Num =:= L ->
|
||||
Ds = Rest
|
||||
; Num > L,
|
||||
Delta is Num - L,
|
||||
% we should look into the float with
|
||||
% greater accuracy here, and use the
|
||||
% actual digits instead of 0.
|
||||
length(Zs, Delta),
|
||||
maplist(=('0'), Zs),
|
||||
append(Rest, Zs, Ds)
|
||||
)
|
||||
; length(Ds, Num),
|
||||
maplist(=('0'), Ds)
|
||||
),
|
||||
append(Bs, ['.'|Ds], Chars)
|
||||
)) },
|
||||
cells(Fs, Args, Tab, [chars(Chars),goal(G)|Es], VNs).
|
||||
cells([~,r|Fs], Args, Tab, Es, VNs) --> !,
|
||||
cells([~,'8',r|Fs], Args, Tab, Es, VNs).
|
||||
cells([~|Fs0], Args0, Tab, Es, VNs) -->
|
||||
{ numeric_argument(Fs0, Num, [r|Fs], Args0, [Arg|Args]) },
|
||||
!,
|
||||
{ integer_to_radix(Arg, Num, lowercase, Cs) },
|
||||
cells(Fs, Args, Tab, [chars(Cs)|Es], VNs).
|
||||
{ G = integer_to_radix(Arg, Num, lowercase, Cs) },
|
||||
cells(Fs, Args, Tab, [chars(Cs),goal(G)|Es], VNs).
|
||||
cells([~,'R'|Fs], Args, Tab, Es, VNs) --> !,
|
||||
cells([~,'8','R'|Fs], Args, Tab, Es, VNs).
|
||||
cells([~|Fs0], Args0, Tab, Es, VNs) -->
|
||||
{ numeric_argument(Fs0, Num, ['R'|Fs], Args0, [Arg|Args]) },
|
||||
!,
|
||||
{ integer_to_radix(Arg, Num, uppercase, Cs) },
|
||||
cells(Fs, Args, Tab, [chars(Cs)|Es], VNs).
|
||||
{ G = integer_to_radix(Arg, Num, uppercase, Cs) },
|
||||
cells(Fs, Args, Tab, [chars(Cs),goal(G)|Es], VNs).
|
||||
cells([~,'`',Char,t|Fs], Args, Tab, Es, VNs) --> !,
|
||||
cells(Fs, Args, Tab, [glue(Char,_)|Es], VNs).
|
||||
cells([~,t|Fs], Args, Tab, Es, VNs) --> !,
|
||||
cells(Fs, Args, Tab, [glue(' ',_)|Es], VNs).
|
||||
cells([~,'|'|Fs], Args, Tab0, Es, VNs) --> !,
|
||||
{ phrase(elements_gluevars(Es, 0, Width), _),
|
||||
Tab is Tab0 + Width },
|
||||
cell(Tab0, Tab, Es),
|
||||
( { ground(Tab0), Es = [chars(Cs)], ground(Cs) } ->
|
||||
{ length(Cs, Width),
|
||||
Tab is Tab0 + Width },
|
||||
cell(Tab0, Tab, Es)
|
||||
; { G = (phrase(elements_gluevars(Es, 0, Width), _),
|
||||
Tab is Tab0 + Width) },
|
||||
cell(Tab0, Tab, [goal(G)|Es])
|
||||
),
|
||||
cells(Fs, Args, Tab, [], VNs).
|
||||
cells([~|Fs0], Args0, Tab, Es, VNs) -->
|
||||
{ numeric_argument(Fs0, Num, ['|'|Fs], Args0, Args) },
|
||||
@@ -285,8 +351,12 @@ cells([~|Fs0], Args0, Tab, Es, VNs) -->
|
||||
cells([~|Fs0], Args0, Tab0, Es, VNs) -->
|
||||
{ numeric_argument(Fs0, Num, [+|Fs], Args0, Args) },
|
||||
!,
|
||||
{ Tab is Tab0 + Num },
|
||||
cell(Tab0, Tab, Es),
|
||||
( { ground(Tab0+Num) } ->
|
||||
{ Tab is Tab0 + Num },
|
||||
cell(Tab0, Tab, Es)
|
||||
; { G = (Tab is Tab0 + Num) },
|
||||
cell(Tab0, Tab, [goal(G)|Es])
|
||||
),
|
||||
cells(Fs, Args, Tab, [], VNs).
|
||||
cells([~|Cs], Args, _, _, _) -->
|
||||
( { Args == [] } ->
|
||||
@@ -302,14 +372,14 @@ format_number_chars(N0, Chars) :-
|
||||
N is N0, % evaluate compound expression
|
||||
number_chars(N, Chars).
|
||||
|
||||
n_newlines(0) --> !.
|
||||
n_newlines(N0) --> { N0 > 0, N is N0 - 1 }, [newline], n_newlines(N).
|
||||
n_newlines(0) --> [].
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
?- phrase(upto_what(Cs, ~), "abc~test", Rest).
|
||||
Cs = [a,b,c], Rest = [~,t,e,s,t].
|
||||
?- phrase(upto_what(Cs, ~), "abc", Rest).
|
||||
Cs = [a,b,c], Rest = [].
|
||||
?- phrase(format:upto_what(Cs, ~), "abc~test", Rest).
|
||||
Cs = "abc", Rest = "~test".
|
||||
?- phrase(format:upto_what(Cs, ~), "abc", Rest).
|
||||
Cs = "abc", Rest = [].
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
separate_digits_fractional(Arg, Sep, Num, Cs) :-
|
||||
@@ -419,9 +489,11 @@ digits(uppercase, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ").
|
||||
% advantage of this is that an ideal implementation writes the
|
||||
% characters as they become known, without manifesting the list.
|
||||
|
||||
format(Fs, Args) :-
|
||||
current_output(Stream),
|
||||
format(Stream, Fs, Args).
|
||||
format(_, _) :- not_used.
|
||||
|
||||
user:goal_expansion(format(Fs, Args),
|
||||
( current_output(Stream),
|
||||
format(Stream, Fs, Args))).
|
||||
|
||||
%% format(Stream, FormatString, Arguments)
|
||||
%
|
||||
@@ -429,9 +501,11 @@ format(Fs, Args) :-
|
||||
% binary stream, then the code of each emitted character must be in
|
||||
% 0..255.
|
||||
|
||||
format(Stream, Fs, Args) :-
|
||||
phrase_to_stream(format_(Fs, Args), Stream),
|
||||
flush_output(Stream).
|
||||
format(_, _, _) :- not_used.
|
||||
|
||||
user:goal_expansion(format(Stream, Fs, Args),
|
||||
( pio:phrase_to_stream(format:format_(Fs, Args), Stream),
|
||||
flush_output(Stream))).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
?- phrase(format:cells("hello", [], 0, [], []), Cs).
|
||||
@@ -444,9 +518,9 @@ format(Stream, Fs, Args) :-
|
||||
?- phrase(format:cells("~`at~50|", [], 0, [], []), Cs),
|
||||
phrase(format:format_cells(Cs), Ls).
|
||||
?- phrase(format:cells("~ta~t~tb~tc~21|", [], 0, [], []), Cs).
|
||||
Cs = [cell(0,21,[glue(' ',_A),chars("a"),glue(' ',_B),glue(' ',_C),chars("b"),glue(' ',_D),chars("c ...")])]
|
||||
Cs = [cell(0,21,[glue(' ',_A),chars("a"),glue(' ',_B),glue(' ',_C),chars("b"),glue(' ',_D),chars("c")])].
|
||||
?- phrase(format:cells("~ta~t~4|", [], 0, [], []), Cs).
|
||||
Cs = [cell(0,4,[glue(' ',_A),chars("a"),glue(' ',_B)])]
|
||||
Cs = [cell(0,4,[glue(' ',_A),chars("a"),glue(' ',_B)])].
|
||||
|
||||
?- phrase(format:format_cell(cell(0,1,[glue(a,_94)])), Ls).
|
||||
|
||||
@@ -517,14 +591,6 @@ portray_clause_(Term) -->
|
||||
{ unique_variable_names(Term, VNs) },
|
||||
portray_(Term, VNs), ".\n".
|
||||
|
||||
unique_variable_names(Term, VNs) :-
|
||||
term_variables(Term, Vs),
|
||||
foldl(var_name, Vs, VNs, 0, _).
|
||||
|
||||
var_name(V, Name=V, Num0, Num) :-
|
||||
charsio:fabricate_var_name(numbervars, Name, Num0),
|
||||
Num is Num0 + 1.
|
||||
|
||||
literal(Lit, VNs) -->
|
||||
{ write_term_to_chars(Lit, [quoted(true),variable_names(VNs),double_quotes(true)], Ls) },
|
||||
seq(Ls).
|
||||
|
||||
@@ -9,6 +9,7 @@ but they're not part of the ISO Prolog standard at the moment.
|
||||
bb_put/2,
|
||||
call_cleanup/2,
|
||||
call_with_inference_limit/3,
|
||||
call_residue_vars/2,
|
||||
forall/2,
|
||||
partial_string/1,
|
||||
partial_string/3,
|
||||
@@ -18,8 +19,7 @@ but they're not part of the ISO Prolog standard at the moment.
|
||||
call_nth/2,
|
||||
countall/2,
|
||||
copy_term_nat/2,
|
||||
asserta/2,
|
||||
assertz/2]).
|
||||
copy_term/3]).
|
||||
|
||||
:- use_module(library(error), [can_be/2,
|
||||
domain_error/3,
|
||||
@@ -28,6 +28,8 @@ but they're not part of the ISO Prolog standard at the moment.
|
||||
|
||||
:- use_module(library(lists), [maplist/3]).
|
||||
|
||||
:- use_module(library('$project_atts')).
|
||||
|
||||
:- meta_predicate(forall(0, 0)).
|
||||
|
||||
%% forall(Generate, Test).
|
||||
@@ -221,6 +223,7 @@ run_cleaners_without_handling(Cp) :-
|
||||
%% call_with_inference_limit(Goal, Limit, Result).
|
||||
%
|
||||
% Similar to `call(Goal)` but it limits the number of inferences for each solution of Goal.
|
||||
% Calls to it may be nested, but only the last limit will be in power.
|
||||
call_with_inference_limit(G, L, R) :-
|
||||
( integer(L) ->
|
||||
( L < 0 ->
|
||||
@@ -384,21 +387,23 @@ countall(Goal, N) :-
|
||||
copy_term_nat(Source, Dest) :-
|
||||
'$copy_term_without_attr_vars'(Source, Dest).
|
||||
|
||||
%% asserta(Module, Rule_Fact).
|
||||
%% copy_term(+Term, -Copy, -Gs).
|
||||
%
|
||||
% Similar to `asserta/1` but allows specifying a Module
|
||||
asserta(Module, (Head :- Body)) :-
|
||||
!,
|
||||
'$asserta'(Module, Head, Body).
|
||||
asserta(Module, Fact) :-
|
||||
'$asserta'(Module, Fact, true).
|
||||
% Produce a deep copy of Term and unify it to Copy, without attributes.
|
||||
% Unify Gs with a list of goals that represent the attributes of Term.
|
||||
% Similar to `copy_term/2` but splitting the attributes.
|
||||
copy_term(Term, Copy, Gs) :-
|
||||
can_be(list, Gs),
|
||||
findall(Term-Rs, '$project_atts':term_residual_goals(Term,Rs), [Copy-Gs]),
|
||||
( var(Gs) ->
|
||||
Gs = []
|
||||
; true
|
||||
).
|
||||
|
||||
%% assertz(Module, Rule_Fact).
|
||||
%
|
||||
% Similar to `assertz/1` but allows specifying a Module
|
||||
assertz(Module, (Head :- Body)) :-
|
||||
!,
|
||||
'$assertz'(Module, Head, Body).
|
||||
assertz(Module, Fact) :-
|
||||
'$assertz'(Module, Fact, true).
|
||||
:- meta_predicate call_residue_vars(0, ?).
|
||||
|
||||
call_residue_vars(Goal, Vars) :-
|
||||
can_be(list, Vars),
|
||||
'$get_attr_var_queue_delim'(B),
|
||||
call(Goal),
|
||||
'$get_attr_var_queue_beyond'(B, Vars).
|
||||
|
||||
@@ -5,9 +5,16 @@
|
||||
|
||||
:- op(1199, fx, meta_predicate).
|
||||
|
||||
/* this is an implementation specific declarative operator used to implement call_with_inference_limit/3
|
||||
and setup_call_cleanup/3. switches to the default trust_me and retry_me_else. Indexing choice
|
||||
instructions are unchanged. */
|
||||
% Implementation specific declarative operator used to implement
|
||||
% call_with_inference_limit/3 and setup_call_cleanup/3. Compiler switches
|
||||
% to the default trust_me, retry_me_else and some other instructions for all
|
||||
% predicates that are marked with it. Indexing choice instructions are unchanged.
|
||||
%
|
||||
% Implementation details:
|
||||
% Default instructions are not subject to inference counting, so their
|
||||
% execution will not be considered if they happen to be called by
|
||||
% call_with_inference_limit/3.
|
||||
%
|
||||
:- op(700, fx, non_counted_backtracking).
|
||||
|
||||
% arithmetic operators.
|
||||
|
||||
@@ -23,7 +23,9 @@ finding out the PID of the running system.
|
||||
unsetenv/1,
|
||||
shell/1,
|
||||
shell/2,
|
||||
pid/1]).
|
||||
pid/1,
|
||||
raw_argv/1,
|
||||
argv/1]).
|
||||
|
||||
:- use_module(library(error)).
|
||||
:- use_module(library(charsio)).
|
||||
@@ -110,3 +112,34 @@ permitted('_').
|
||||
must_be_chars(Cs) :-
|
||||
must_be(list, Cs),
|
||||
maplist(must_be(character), Cs).
|
||||
|
||||
%% raw_argv(-Argv)
|
||||
%
|
||||
% True iff Argv is the list of arguments that this program was started with (usually passed via command line).
|
||||
% In contrast to `argv/1`, this version includes every argument, without any postprocessing, just as the operating
|
||||
% system reports it to the system. This includes-flags of Scryer itself, which are not needed in general.
|
||||
raw_argv(Argv) :-
|
||||
can_be(list, Argv),
|
||||
'$argv'(Argv).
|
||||
|
||||
%% argv(-Argv)
|
||||
%
|
||||
% True if Argv is the list of arguments that this program was started with (usually passed via command line).
|
||||
% In this version, only arguments specific to the program are passed. To differentiate between the system
|
||||
% arguments and the program arguments, we use `--` as a separator.
|
||||
%
|
||||
% Example:
|
||||
%
|
||||
% ```
|
||||
% % Call with scryer-prolog -f -- -t hello
|
||||
% ?- argv(X).
|
||||
% X = ["-t", "hello"].
|
||||
% ```
|
||||
argv(Argv) :-
|
||||
can_be(list, Argv),
|
||||
'$argv'(Argv0),
|
||||
( append(_, ["--"|Argv1], Argv0) ->
|
||||
Argv = Argv1
|
||||
;
|
||||
Argv = []
|
||||
).
|
||||
|
||||
@@ -37,8 +37,10 @@
|
||||
atomic_si/1,
|
||||
list_si/1,
|
||||
character_si/1,
|
||||
term_si/1,
|
||||
chars_si/1,
|
||||
dif_si/2]).
|
||||
dif_si/2,
|
||||
when_si/2]).
|
||||
|
||||
:- use_module(library(lists)).
|
||||
|
||||
@@ -68,6 +70,11 @@ character_si(Ch) :-
|
||||
atom(Ch),
|
||||
atom_length(Ch,1).
|
||||
|
||||
term_si(Term) :-
|
||||
( ground(Term) -> acyclic_term(Term)
|
||||
; throw(error(instantiation_error, term_si/1))
|
||||
).
|
||||
|
||||
chars_si(Chs0) :-
|
||||
'$skip_max_list'(_,_, Chs0,Chs),
|
||||
( nonvar(Chs) -> Chs == [] ; true ), % fails for infinite lists too
|
||||
@@ -92,3 +99,31 @@ dif_si(X, Y) :-
|
||||
( X \= Y -> true
|
||||
; throw(error(instantiation_error,dif_si/2))
|
||||
).
|
||||
|
||||
:- meta_predicate(when_si(+, 0)).
|
||||
|
||||
%% when_si(Condition, Goal).
|
||||
%
|
||||
% Executes Goal when Condition becomes true. Throws an instantiation error if
|
||||
% it can't decide.
|
||||
when_si(Condition, Goal) :-
|
||||
% Taken from https://stackoverflow.com/a/40449516
|
||||
( when_condition_si(Condition) ->
|
||||
( Condition ->
|
||||
Goal
|
||||
; throw(error(instantiation_error,when_si/2))
|
||||
)
|
||||
; throw(error(domain_error(when_condition_si, Condition),_))
|
||||
).
|
||||
|
||||
when_condition_si(Cond) :-
|
||||
var(Cond), !, throw(error(instantiation_error,when_condition_si/2)).
|
||||
when_condition_si(ground(_)).
|
||||
when_condition_si(nonvar(_)).
|
||||
when_condition_si((A, B)) :-
|
||||
when_condition_si(A),
|
||||
when_condition_si(B).
|
||||
when_condition_si((A ; B)) :-
|
||||
when_condition_si(A),
|
||||
when_condition_si(B).
|
||||
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Written 2020-2023 by Markus Triska (triska@metalevel.at)
|
||||
Written 2020-2024 by Markus Triska (triska@metalevel.at)
|
||||
Part of Scryer Prolog.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
/** This library provides predicates for reasoning about time.
|
||||
*/
|
||||
|
||||
:- module(time, [max_sleep_time/1, sleep/1, time/1, current_time/1, format_time//2]).
|
||||
:- module(time, [max_sleep_time/1,
|
||||
sleep/1,
|
||||
time/1,
|
||||
current_time/1,
|
||||
format_time//2,
|
||||
statistics/2
|
||||
]).
|
||||
|
||||
:- use_module(library(format)).
|
||||
:- use_module(library(iso_ext)).
|
||||
@@ -91,12 +97,21 @@ sleep(T) :-
|
||||
).
|
||||
|
||||
|
||||
% '$cpu_now' can be replaced by statistics/2 once that is implemented.
|
||||
%% statistics(?Keyword, ?List)
|
||||
%
|
||||
% Preliminary support for statistics/2, yielding timing information.
|
||||
% The only supported `Keyword` is `runtime`. The first element of
|
||||
% `List` is the CPU time in milliseconds, the second element is
|
||||
% currently not supported.
|
||||
|
||||
statistics(runtime, [T,unsupported]) :-
|
||||
'$cpu_now'(T0),
|
||||
T is T0*1000.
|
||||
|
||||
:- meta_predicate time(0).
|
||||
|
||||
:- dynamic(time_id/1).
|
||||
:- dynamic(time_state/2).
|
||||
:- dynamic(time_state/3).
|
||||
|
||||
time_next_id(N) :-
|
||||
( retract(time_id(N0)) ->
|
||||
@@ -111,9 +126,9 @@ time_next_id(N) :-
|
||||
% Reports the execution time of Goal.
|
||||
|
||||
time(Goal) :-
|
||||
'$cpu_now'(T0),
|
||||
cputime_inferences(T0, I0),
|
||||
time_next_id(ID),
|
||||
setup_call_cleanup(asserta(time_state(ID, T0)),
|
||||
setup_call_cleanup(asserta(time_state(ID, T0, I0)),
|
||||
( call_cleanup(catch(Goal, E, (report_time(ID),throw(E))),
|
||||
Det = true),
|
||||
time_true(ID),
|
||||
@@ -123,49 +138,72 @@ time(Goal) :-
|
||||
; report_time(ID),
|
||||
false
|
||||
),
|
||||
retract(time_state(ID, _))).
|
||||
retract(time_state(ID, _, _))).
|
||||
|
||||
cputime_inferences(T, I) :-
|
||||
'$cpu_now'(T),
|
||||
'$inference_count'(I).
|
||||
|
||||
time_true(ID) :-
|
||||
report_time(ID).
|
||||
time_true(ID) :-
|
||||
% on backtracking, update the stored CPU time for this ID
|
||||
retract(time_state(ID, _)),
|
||||
'$cpu_now'(T0),
|
||||
asserta(time_state(ID, T0)),
|
||||
retract(time_state(ID, _, _)),
|
||||
cputime_inferences(T0, I0),
|
||||
asserta(time_state(ID, T0, I0)),
|
||||
false.
|
||||
|
||||
report_time(ID) :-
|
||||
time_state(ID, T0),
|
||||
'$cpu_now'(T),
|
||||
time_state(ID, T0, I0),
|
||||
cputime_inferences(T, I),
|
||||
Time is T - T0,
|
||||
Inferences0 is I - I0,
|
||||
% we must subtract the number of inferences that time/1 itself takes;
|
||||
% this may have to be adapted if the implementation changes,
|
||||
% so that (for example) true/1 takes exactly 1 inference.
|
||||
( bb_get('$answer_count', 0) ->
|
||||
Inferences is Inferences0 - 60,
|
||||
Pre = " ", Post = ""
|
||||
; Pre = "", Post = " "
|
||||
; Inferences is Inferences0 - 9,
|
||||
Pre = "", Post = " "
|
||||
),
|
||||
format("~s% CPU time: ~3fs~n~s", [Pre,Time,Post]).
|
||||
phrase((Pre,"% CPU time: ", format_("~3f", [Time]), "s, ",
|
||||
format_("~U", [Inferences])," inference",s_if_necessary(Inferences),"\n",
|
||||
Post), Cs),
|
||||
format("~s", [Cs]).
|
||||
|
||||
s_if_necessary(Inferences) -->
|
||||
{ compare(C, 1, Inferences) },
|
||||
s_(C).
|
||||
|
||||
s_(=) --> "".
|
||||
s_(<) --> "s".
|
||||
s_(>) --> " (exception?)".
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
?- time((true;false)).
|
||||
%@ % CPU time: 0.006s
|
||||
%@ true
|
||||
%@ ; % CPU time: 0.001s
|
||||
%@ false.
|
||||
% CPU time: 0.000s, 1 inference
|
||||
true
|
||||
; % CPU time: 0.000s, 0 inference (exception?)
|
||||
false.
|
||||
|
||||
:- time(use_module(library(clpz))).
|
||||
%@ % CPU time: 3.711s
|
||||
%@ true.
|
||||
% CPU time: 0.343s, 409_874 inferences
|
||||
true.
|
||||
|
||||
:- time(use_module(library(lists))).
|
||||
%@ % CPU time: 0.006s
|
||||
%@ true.
|
||||
% CPU time: 0.000s, 19 inferences
|
||||
true.
|
||||
|
||||
?- time(member(X, "abc")).
|
||||
%@ % CPU time: 0.005s
|
||||
%@ X = a
|
||||
%@ ; % CPU time: 0.000s
|
||||
%@ X = b
|
||||
%@ ; % CPU time: 0.000s
|
||||
%@ X = c
|
||||
%@ ; % CPU time: 0.000s
|
||||
%@ false.
|
||||
% CPU time: 0.000s, 1 inference
|
||||
X = a
|
||||
; % CPU time: 0.000s, 3 inferences
|
||||
X = b
|
||||
; % CPU time: 0.000s, 3 inferences
|
||||
X = c.
|
||||
|
||||
?- time((repeat,false)).
|
||||
% CPU time: 2.726s, 53_330_502 inferences
|
||||
error('$interrupt_thrown',repl/0).
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
29
src/lib/wasm.pl
Normal file
29
src/lib/wasm.pl
Normal file
@@ -0,0 +1,29 @@
|
||||
/** Predicates for the WebAssembly platform
|
||||
|
||||
This module contains predicates that are only available in
|
||||
the WASM (WebAssembly) version of Scryer Prolog.
|
||||
*/
|
||||
|
||||
:- module(wasm, [js_eval/2]).
|
||||
|
||||
:- use_module(library(error)).
|
||||
|
||||
%% js_eval(+JsCode, -Result).
|
||||
%
|
||||
% Executes a JavaScript snippet `JsCode` using the platform
|
||||
% `eval` function. `Result` takes the return value of that code.
|
||||
% Strings, booleans, numbers, null and undefined are directly mapped to Prolog.
|
||||
% Arrays, objects, bigints, symbols and functions are not mapped.
|
||||
% Instead, a `js_{type}` atom will be returned.
|
||||
%
|
||||
% Example (on a browser):
|
||||
%
|
||||
% ```
|
||||
% ?- js_eval("prompt('What is your name?')", Name).
|
||||
% % A prompt is showed, with a textbox.
|
||||
% Name = "Whatever was written on the textbox".
|
||||
% ```
|
||||
js_eval(JsCode, Result) :-
|
||||
must_be(chars, JsCode),
|
||||
can_be(chars, Result),
|
||||
'$js_eval'(JsCode, Result).
|
||||
106
src/lib/when.pl
Normal file
106
src/lib/when.pl
Normal file
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
Provides the predicate `when/2`.
|
||||
*/
|
||||
|
||||
:- module(when, [when/2]).
|
||||
|
||||
:- use_module(library(atts)).
|
||||
:- use_module(library(dcgs)).
|
||||
:- use_module(library(lists)).
|
||||
:- use_module(library(lambda)).
|
||||
|
||||
:- use_module(library(format)).
|
||||
:- use_module(library(debug)).
|
||||
|
||||
:- attribute when_list/1.
|
||||
|
||||
:- meta_predicate(when(+, 0)).
|
||||
|
||||
%% when(Condition, Goal).
|
||||
%
|
||||
% Executes Goal when Condition becomes true.
|
||||
when(Condition, Goal) :-
|
||||
( when_condition(Condition) ->
|
||||
( Condition ->
|
||||
Goal
|
||||
; term_variables(Condition, Vars),
|
||||
maplist(
|
||||
[Goal, Condition]+\Var^(
|
||||
get_atts(Var, when_list(Whens0)) ->
|
||||
Whens = [when(Condition, Goal) | Whens0],
|
||||
put_atts(Var, when_list(Whens))
|
||||
; put_atts(Var, when_list([when(Condition, Goal)]))
|
||||
),
|
||||
Vars
|
||||
)
|
||||
)
|
||||
; throw(error(domain_error(when_condition, Condition),_))
|
||||
).
|
||||
|
||||
when_condition(Cond) :-
|
||||
% Should this be delayed?
|
||||
var(Cond), !, throw(error(instantiation_error,when_condition/1)).
|
||||
when_condition(ground(_)).
|
||||
when_condition(nonvar(_)).
|
||||
when_condition((A, B)) :-
|
||||
when_condition(A),
|
||||
when_condition(B).
|
||||
when_condition((A ; B)) :-
|
||||
when_condition(A),
|
||||
when_condition(B).
|
||||
|
||||
remove_goal([], _, []).
|
||||
remove_goal([G0|G0s], Goal, Goals) :-
|
||||
( G0 == Goal ->
|
||||
remove_goal(G0s, Goal, Goals)
|
||||
; Goals = [G0|Goals1],
|
||||
remove_goal(G0s, Goal, Goals1)
|
||||
).
|
||||
|
||||
vars_remove_goal(Vars, Goal) :-
|
||||
maplist(
|
||||
Goal+\Var^(
|
||||
get_atts(Var, when_list(Whens0)) ->
|
||||
remove_goal(Whens0, Goal, Whens),
|
||||
( Whens = [] ->
|
||||
put_atts(Var, -when_list(_))
|
||||
; put_atts(Var, when_list(Whens))
|
||||
)
|
||||
; true
|
||||
),
|
||||
Vars
|
||||
).
|
||||
|
||||
reinforce_goal(Goal0, Goal) :-
|
||||
Goal = (
|
||||
term_variables(Goal0, Vars),
|
||||
when:vars_remove_goal(Vars, Goal0),
|
||||
Goal0
|
||||
).
|
||||
|
||||
verify_attributes(Var, Value, Goals) :-
|
||||
( get_atts(Var, when_list(Whens)) ->
|
||||
( var(Value) ->
|
||||
( get_atts(Value, when_list(WhensValue)) ->
|
||||
append(Whens, WhensValue, WhensNew),
|
||||
put_atts(Value, when_list(WhensNew))
|
||||
; put_atts(Value, when_list(Whens))
|
||||
),
|
||||
Goals = []
|
||||
; maplist(reinforce_goal, Whens, Goals)
|
||||
)
|
||||
; Goals = []
|
||||
).
|
||||
|
||||
gather_when_goals([], _) --> [].
|
||||
gather_when_goals([When|Whens], Var) -->
|
||||
( { term_variables(When, [V0|_]), Var == V0 } ->
|
||||
[when:When]
|
||||
; []
|
||||
),
|
||||
gather_when_goals(Whens, Var).
|
||||
|
||||
attribute_goals(Var) -->
|
||||
{ get_atts(Var, when_list(Whens)) },
|
||||
gather_when_goals(Whens, Var),
|
||||
{ put_atts(Var, -when_list(_)) }.
|
||||
171
src/loader.pl
171
src/loader.pl
@@ -112,7 +112,7 @@ success_or_warning(Goal) :-
|
||||
( call(Goal) ->
|
||||
true
|
||||
; %% initialization goals can fail without thwarting the load.
|
||||
write('Warning: initialization/1 failed for: '),
|
||||
write('% Warning: initialization/1 failed for: '),
|
||||
writeq(Goal),
|
||||
nl
|
||||
).
|
||||
@@ -138,7 +138,7 @@ file_load_cleanup(Evacuable, Error) :-
|
||||
load_context(Module),
|
||||
abolish(Module:'$initialization_goals'/1),
|
||||
unload_evacuable(Evacuable),
|
||||
( clause('$toplevel':argv(_), _) ->
|
||||
( clause('$toplevel':started, _) ->
|
||||
% let the toplevel call loader:write_error/1
|
||||
throw(Error)
|
||||
; '$print_message_and_fail'(Error)
|
||||
@@ -188,7 +188,7 @@ warn_about_singletons([], _).
|
||||
warn_about_singletons([Singleton|Singletons], LinesRead) :-
|
||||
( filter_anonymous_vars([Singleton|Singletons], VarEqs),
|
||||
VarEqs \== [] ->
|
||||
write('Warning: singleton variables '),
|
||||
write('% Warning: singleton variables '),
|
||||
print_comma_separated_list(VarEqs),
|
||||
write(' at line '),
|
||||
write(LinesRead),
|
||||
@@ -223,22 +223,25 @@ compile_term(Term, Evacuable) :-
|
||||
( var(Terms) ->
|
||||
instantiation_error(load/1)
|
||||
; Terms = [_|_] ->
|
||||
compile_dispatch_or_clause_on_list(Terms, Evacuable)
|
||||
; compile_dispatch_or_clause(Terms, Evacuable)
|
||||
compile_dispatch_or_clause_on_list(list(Term), Terms, Evacuable)
|
||||
; compile_dispatch_or_clause(term(Term), Terms, Evacuable)
|
||||
).
|
||||
|
||||
complete_partial_goal(N, HeadArg, InnerHeadArgs, SuppArgs, CompleteHeadArg) :-
|
||||
integer(N),
|
||||
N >= 0,
|
||||
HeadArg =.. [Functor | InnerHeadArgs],
|
||||
% the next two lines are equivalent to length(SuppArgs, N) but
|
||||
% avoid length/2 so that copy_term/3 (which is invoked by
|
||||
% length/2) can be bootstrapped without self-reference.
|
||||
functor(SuppArgsFunctor, '.', N),
|
||||
SuppArgsFunctor =.. [_ | SuppArgs],
|
||||
% length(SuppArgs, N),
|
||||
append(InnerHeadArgs, SuppArgs, InnerHeadArgs0),
|
||||
CompleteHeadArg =.. [Functor | InnerHeadArgs0].
|
||||
( callable(Functor) ->
|
||||
% the next two lines are equivalent to length(SuppArgs, N) but
|
||||
% avoid length/2 so that copy_term/3 (which is invoked by
|
||||
% length/2) can be bootstrapped without self-reference.
|
||||
functor(SuppArgsFunctor, '.', N),
|
||||
SuppArgsFunctor =.. [_ | SuppArgs],
|
||||
% length(SuppArgs, N),
|
||||
append(InnerHeadArgs, SuppArgs, InnerHeadArgs0),
|
||||
CompleteHeadArg =.. [Functor | InnerHeadArgs0]
|
||||
; type_error(callable, Functor, _)
|
||||
).
|
||||
|
||||
inner_meta_specs(0, HeadArg, InnerHeadArgs, InnerMetaSpecs) :-
|
||||
!,
|
||||
@@ -283,7 +286,7 @@ module_expanded_head_variables(Head, HeadVars) :-
|
||||
|
||||
print_goal_expansion_warning(Pred) :-
|
||||
nl,
|
||||
write('Warning: clause body goal expansion failed because '),
|
||||
write('% Warning: clause body goal expansion failed because '),
|
||||
writeq(Pred),
|
||||
write(' is not callable.'),
|
||||
nl.
|
||||
@@ -296,7 +299,7 @@ expand_term_goals(Terms0, Terms) :-
|
||||
( atom(Module) ->
|
||||
prolog_load_context(module, Target),
|
||||
module_expanded_head_variables(Head2, HeadVars),
|
||||
catch(expand_goal(Body0, Target, Body1, HeadVars),
|
||||
catch(expand_goal(Body0, Target, Body1, HeadVars, []),
|
||||
error(type_error(callable, Pred), _),
|
||||
( loader:print_goal_expansion_warning(Pred),
|
||||
builtins:(Body1 = Body0)
|
||||
@@ -306,7 +309,7 @@ expand_term_goals(Terms0, Terms) :-
|
||||
)
|
||||
; module_expanded_head_variables(Head1, HeadVars),
|
||||
prolog_load_context(module, Target),
|
||||
catch(expand_goal(Body0, Target, Body1, HeadVars),
|
||||
catch(expand_goal(Body0, Target, Body1, HeadVars, []),
|
||||
error(type_error(callable, Pred), _),
|
||||
( loader:print_goal_expansion_warning(Pred),
|
||||
builtins:(Body1 = Body0)
|
||||
@@ -327,18 +330,18 @@ expand_terms_and_goals(Term, Terms) :-
|
||||
).
|
||||
|
||||
|
||||
compile_dispatch_or_clause_on_list([], Evacuable).
|
||||
compile_dispatch_or_clause_on_list([Term | Terms], Evacuable) :-
|
||||
compile_dispatch_or_clause(Term, Evacuable),
|
||||
compile_dispatch_or_clause_on_list(Terms, Evacuable).
|
||||
compile_dispatch_or_clause_on_list(OrigTerm, [], Evacuable).
|
||||
compile_dispatch_or_clause_on_list(OrigTerm, [Term | Terms], Evacuable) :-
|
||||
compile_dispatch_or_clause(OrigTerm, Term, Evacuable),
|
||||
compile_dispatch_or_clause_on_list(OrigTerm, Terms, Evacuable).
|
||||
|
||||
|
||||
compile_dispatch_or_clause(Term, Evacuable) :-
|
||||
compile_dispatch_or_clause(OrigTerm, Term, Evacuable) :-
|
||||
( var(Term) ->
|
||||
instantiation_error(load/1)
|
||||
; compile_dispatch(Term, Evacuable) ->
|
||||
'$flush_term_queue'(Evacuable)
|
||||
; compile_clause(Term, Evacuable)
|
||||
; compile_clause(OrigTerm, Term, Evacuable)
|
||||
).
|
||||
|
||||
|
||||
@@ -464,39 +467,46 @@ compile_declaration(non_counted_backtracking(Name/Arity), Evacuable) :-
|
||||
; domain_error(not_less_than_zero, Arity, load/1)
|
||||
).
|
||||
|
||||
recompile_term(list(OrigTerm), Term, Evacuable) :-
|
||||
% since OrigTerm expanded to a list, its contents are considered a
|
||||
% unit to be compiled simultaneously, and so its clauses are not
|
||||
% re-expanded when their predecessors are compiled.
|
||||
compile_clause(list(OrigTerm), Term, Evacuable).
|
||||
recompile_term(term(OrigTerm), _Term, Evacuable) :-
|
||||
compile_term(OrigTerm, Evacuable).
|
||||
|
||||
compile_clause((Target:Head :- Body), Evacuable) :-
|
||||
compile_clause(OrigTerm, (Target:Head :- Body), Evacuable) :-
|
||||
!,
|
||||
functor(Head, Name, Arity),
|
||||
( '$is_consistent_with_term_queue'(Target, Name, Arity, Evacuable) ->
|
||||
'$scoped_clause_to_evacuable'(Target, (Head :- Body), Evacuable)
|
||||
; '$flush_term_queue'(Evacuable),
|
||||
compile_term((Target:Head :- Body), Evacuable)
|
||||
recompile_term(OrigTerm, (Target:Head :- Body), Evacuable)
|
||||
).
|
||||
compile_clause(Target:Head, Evacuable) :-
|
||||
compile_clause(OrigTerm, Target:Head, Evacuable) :-
|
||||
!,
|
||||
functor(Head, Name, Arity),
|
||||
( '$is_consistent_with_term_queue'(Target, Name, Arity, Evacuable) ->
|
||||
'$scoped_clause_to_evacuable'(Target, Head, Evacuable)
|
||||
; '$flush_term_queue'(Evacuable),
|
||||
compile_term(Target:Head, Evacuable)
|
||||
recompile_term(OrigTerm, Target:Head, Evacuable)
|
||||
).
|
||||
compile_clause((Head :- Body), Evacuable) :-
|
||||
compile_clause(OrigTerm, (Head :- Body), Evacuable) :-
|
||||
!,
|
||||
prolog_load_context(module, Target),
|
||||
functor(Head, Name, Arity),
|
||||
( '$is_consistent_with_term_queue'(Target, Name, Arity, Evacuable) ->
|
||||
'$clause_to_evacuable'((Head :- Body), Evacuable)
|
||||
; '$flush_term_queue'(Evacuable),
|
||||
compile_term((Head :- Body), Evacuable)
|
||||
recompile_term(OrigTerm, (Head :- Body), Evacuable)
|
||||
).
|
||||
compile_clause(Head, Evacuable) :-
|
||||
compile_clause(OrigTerm, Head, Evacuable) :-
|
||||
prolog_load_context(module, Target),
|
||||
functor(Head, Name, Arity),
|
||||
( '$is_consistent_with_term_queue'(Target, Name, Arity, Evacuable) ->
|
||||
'$clause_to_evacuable'(Head, Evacuable)
|
||||
; '$flush_term_queue'(Evacuable),
|
||||
compile_term(Head, Evacuable)
|
||||
recompile_term(OrigTerm, Head, Evacuable)
|
||||
).
|
||||
|
||||
|
||||
@@ -726,9 +736,9 @@ subgoal_expansion(Goal, Module, ExpandedGoal) :-
|
||||
).
|
||||
|
||||
|
||||
:- non_counted_backtracking expand_subgoal/5.
|
||||
:- non_counted_backtracking expand_subgoal/6.
|
||||
|
||||
expand_subgoal(UnexpandedGoals, MS, M, ExpandedGoals, HeadVars) :-
|
||||
expand_subgoal(UnexpandedGoals, MS, M, ExpandedGoals, HeadVars, TGs) :-
|
||||
strip_subst_module(UnexpandedGoals, M, Module, UnexpandedGoals0),
|
||||
nonvar(UnexpandedGoals0),
|
||||
complete_partial_goal(MS, UnexpandedGoals0, _, SuppArgs, UnexpandedGoals1),
|
||||
@@ -740,7 +750,7 @@ expand_subgoal(UnexpandedGoals, MS, M, ExpandedGoals, HeadVars) :-
|
||||
),
|
||||
strip_subst_module(UnexpandedGoals3, Module, Module1, UnexpandedGoals4),
|
||||
( inner_meta_specs(0, UnexpandedGoals4, _, MetaSpecs) ->
|
||||
expand_module_names(UnexpandedGoals4, MetaSpecs, Module1, ExpandedGoals0, HeadVars)
|
||||
expand_module_names(UnexpandedGoals4, MetaSpecs, Module1, ExpandedGoals0, HeadVars, TGs)
|
||||
; ExpandedGoals0 = UnexpandedGoals4
|
||||
),
|
||||
'$compile_inline_or_expanded_goal'(ExpandedGoals0, SuppArgs, ExpandedGoals1, Module1, UnexpandedGoals0),
|
||||
@@ -769,10 +779,10 @@ expand_module_name(ESG0, MS, M, ESG) :-
|
||||
|
||||
:- non_counted_backtracking eq_member/2.
|
||||
|
||||
eq_member(V, [L-_|Ls]) :-
|
||||
eq_member(V-M, [L-M|Ls]) :-
|
||||
V == L.
|
||||
eq_member(V, [_|Ls]) :-
|
||||
eq_member(V, Ls).
|
||||
eq_member(V-M, [_|Ls]) :-
|
||||
eq_member(V-M, Ls).
|
||||
|
||||
:- non_counted_backtracking qualified_spec/1.
|
||||
|
||||
@@ -780,13 +790,21 @@ qualified_spec((:)).
|
||||
qualified_spec(MS) :- integer(MS), MS >= 0.
|
||||
|
||||
|
||||
:- non_counted_backtracking expand_meta_predicate_subgoals/5.
|
||||
:- non_counted_backtracking expand_meta_predicate_subgoals/6.
|
||||
|
||||
expand_meta_predicate_subgoals([SG | SGs], [MS | MSs], M, [ESG | ESGs], HeadVars) :-
|
||||
expand_meta_predicate_subgoals([SG | SGs], [MS | MSs], M, [ESG | ESGs], HeadVars, TGs) :-
|
||||
( var(SG) ->
|
||||
( qualified_spec(MS) ->
|
||||
( eq_member(SG, HeadVars) ->
|
||||
( eq_member(SG-_, HeadVars) ->
|
||||
ESG = SG
|
||||
; eq_member(SG-TG, TGs),
|
||||
% transitive goals come about from previous equalities:
|
||||
% if SG was bound by (=)/2 to a potential goal TG earlier
|
||||
% in the goal sequence, expand TG and substitute SG with it
|
||||
% in this subgoal context. the binding to SG must not be
|
||||
% changed.
|
||||
expand_subgoal(TG, MS, M, ESG, HeadVars, TGs) ->
|
||||
true
|
||||
; expand_module_name(SG, MS, M, ESG)
|
||||
)
|
||||
; ESG = SG
|
||||
@@ -795,26 +813,26 @@ expand_meta_predicate_subgoals([SG | SGs], [MS | MSs], M, [ESG | ESGs], HeadVars
|
||||
expand_module_name(SG, MS, M, ESG)
|
||||
; '$is_expanded_or_inlined'(SG) ->
|
||||
ESG = SG
|
||||
; expand_subgoal(SG, MS, M, ESG, HeadVars) ->
|
||||
; expand_subgoal(SG, MS, M, ESG, HeadVars, TGs) ->
|
||||
true
|
||||
; integer(MS),
|
||||
MS >= 0 ->
|
||||
expand_module_name(SG, MS, M, ESG)
|
||||
; SG = ESG
|
||||
),
|
||||
expand_meta_predicate_subgoals(SGs, MSs, M, ESGs, HeadVars).
|
||||
expand_meta_predicate_subgoals(SGs, MSs, M, ESGs, HeadVars, TGs).
|
||||
|
||||
expand_meta_predicate_subgoals([], _, _, [], _).
|
||||
expand_meta_predicate_subgoals([], _, _, [], _, _).
|
||||
|
||||
:- non_counted_backtracking expand_module_names/5.
|
||||
:- non_counted_backtracking expand_module_names/6.
|
||||
|
||||
expand_module_names(Goals, MetaSpecs, Module, ExpandedGoals, HeadVars) :-
|
||||
expand_module_names(Goals, MetaSpecs, Module, ExpandedGoals, HeadVars, TGs) :-
|
||||
Goals =.. [GoalFunctor | SubGoals],
|
||||
( GoalFunctor == (:),
|
||||
SubGoals = [M, SubGoal] ->
|
||||
expand_module_names(SubGoal, MetaSpecs, M, ExpandedSubGoal, HeadVars),
|
||||
expand_module_names(SubGoal, MetaSpecs, M, ExpandedSubGoal, HeadVars, TGs),
|
||||
expand_module_name(ExpandedSubGoal, 0, M, ExpandedGoals)
|
||||
; expand_meta_predicate_subgoals(SubGoals, MetaSpecs, Module, ExpandedGoalList, HeadVars),
|
||||
; expand_meta_predicate_subgoals(SubGoals, MetaSpecs, Module, ExpandedGoalList, HeadVars, TGs),
|
||||
ExpandedGoals =.. [GoalFunctor | ExpandedGoalList]
|
||||
).
|
||||
|
||||
@@ -822,26 +840,26 @@ expand_module_names(Goals, MetaSpecs, Module, ExpandedGoals, HeadVars) :-
|
||||
:- non_counted_backtracking expand_goal/3.
|
||||
|
||||
expand_goal(UnexpandedGoals, Module, ExpandedGoals) :-
|
||||
catch(loader:expand_goal(UnexpandedGoals, Module, ExpandedGoals, []),
|
||||
catch(loader:expand_goal(UnexpandedGoals, Module, ExpandedGoals, [], []),
|
||||
error(type_error(callable, _), _),
|
||||
UnexpandedGoals = ExpandedGoals),
|
||||
!.
|
||||
|
||||
:- non_counted_backtracking expand_goal/4.
|
||||
:- non_counted_backtracking expand_goal/5.
|
||||
|
||||
expand_goal(UnexpandedGoals, Module, ExpandedGoals, HeadVars) :-
|
||||
expand_goal(UnexpandedGoals, Module, ExpandedGoals, HeadVars, TGs) :-
|
||||
( var(UnexpandedGoals) ->
|
||||
expand_module_names(call(UnexpandedGoals), [0], Module, ExpandedGoals, HeadVars)
|
||||
expand_module_names(call(UnexpandedGoals), [0], Module, ExpandedGoals, HeadVars, TGs)
|
||||
; goal_expansion(UnexpandedGoals, Module, UnexpandedGoals1),
|
||||
( Module \== user ->
|
||||
goal_expansion(UnexpandedGoals1, user, Goals)
|
||||
; Goals = UnexpandedGoals1
|
||||
),
|
||||
( expand_goal_cases(Goals, Module, ExpandedGoals, HeadVars) ->
|
||||
( expand_goal_cases(Goals, Module, ExpandedGoals, HeadVars, TGs) ->
|
||||
true
|
||||
; predicate_property(Module:Goals, meta_predicate(MetaSpecs0)),
|
||||
MetaSpecs0 =.. [_ | MetaSpecs] ->
|
||||
expand_module_names(Goals, MetaSpecs, Module, ExpandedGoals, HeadVars)
|
||||
expand_module_names(Goals, MetaSpecs, Module, ExpandedGoals, HeadVars, TGs)
|
||||
; thread_goals(Goals, ExpandedGoals, (','))
|
||||
; Goals = ExpandedGoals
|
||||
)
|
||||
@@ -869,33 +887,50 @@ expand_call_goal_(UnexpandedGoals, Module, ExpandedGoals) :-
|
||||
UnexpandedGoals = ExpandedGoals
|
||||
; goal_expansion(UnexpandedGoals, Module, UnexpandedGoals1),
|
||||
( Module \== user ->
|
||||
goal_expansion(UnexpandedGoals1, user, ExpandedGoals)
|
||||
goal_expansion(UnexpandedGoals1, user, Goals),
|
||||
( predicate_property(Module:Goals, meta_predicate(MetaSpecs0)),
|
||||
MetaSpecs0 =.. [_ | MetaSpecs] ->
|
||||
expand_module_names(Goals, MetaSpecs, Module, ExpandedGoals, [], [])
|
||||
; ExpandedGoals = Goals
|
||||
)
|
||||
; ExpandedGoals = UnexpandedGoals1
|
||||
)
|
||||
).
|
||||
|
||||
:- non_counted_backtracking expand_goal_cases/4.
|
||||
:- non_counted_backtracking transitive_goal/3.
|
||||
|
||||
expand_goal_cases((Goal0, Goals0), Module, ExpandedGoals, HeadVars) :-
|
||||
( expand_goal(Goal0, Module, Goal1, HeadVars) ->
|
||||
expand_goal(Goals0, Module, Goals1, HeadVars),
|
||||
transitive_goal(G, TGs0, TGs1) :-
|
||||
( G = (G1 = PotentialGoal),
|
||||
callable(PotentialGoal),
|
||||
subsumes_term(G1, PotentialGoal) ->
|
||||
TGs1 = [G1-PotentialGoal|TGs0]
|
||||
; TGs1 = TGs0
|
||||
).
|
||||
|
||||
:- non_counted_backtracking expand_goal_cases/5.
|
||||
|
||||
expand_goal_cases((Goal0, Goals0), Module, ExpandedGoals, HeadVars, TGs) :-
|
||||
( expand_goal(Goal0, Module, Goal1, HeadVars, TGs) ->
|
||||
transitive_goal(Goal0, TGs, TGs1),
|
||||
expand_goal(Goals0, Module, Goals1, HeadVars, TGs1),
|
||||
thread_goals(Goal1, ExpandedGoals, Goals1, (','))
|
||||
; expand_goal(Goals0, Module, Goals1, HeadVars),
|
||||
; expand_goal(Goals0, Module, Goals1, HeadVars, TGs),
|
||||
ExpandedGoals = (Goal0, Goals1)
|
||||
).
|
||||
expand_goal_cases((Goals0 -> Goals1), Module, ExpandedGoals, HeadVars) :-
|
||||
expand_goal(Goals0, Module, ExpandedGoals0, HeadVars),
|
||||
expand_goal(Goals1, Module, ExpandedGoals1, HeadVars),
|
||||
expand_goal_cases((Goals0 -> Goals1), Module, ExpandedGoals, HeadVars, TGs) :-
|
||||
expand_goal(Goals0, Module, ExpandedGoals0, HeadVars, TGs),
|
||||
transitive_goal(ExpandedGoals0, TGs, TGs1),
|
||||
expand_goal(Goals1, Module, ExpandedGoals1, HeadVars, TGs1),
|
||||
ExpandedGoals = (ExpandedGoals0 -> ExpandedGoals1).
|
||||
expand_goal_cases((Goals0 ; Goals1), Module, ExpandedGoals, HeadVars) :-
|
||||
expand_goal(Goals0, Module, ExpandedGoals0, HeadVars),
|
||||
expand_goal(Goals1, Module, ExpandedGoals1, HeadVars),
|
||||
expand_goal_cases((Goals0 ; Goals1), Module, ExpandedGoals, HeadVars, TGs) :-
|
||||
expand_goal(Goals0, Module, ExpandedGoals0, HeadVars, TGs),
|
||||
expand_goal(Goals1, Module, ExpandedGoals1, HeadVars, TGs),
|
||||
ExpandedGoals = (ExpandedGoals0 ; ExpandedGoals1).
|
||||
expand_goal_cases((\+ Goals0), Module, ExpandedGoals, HeadVars) :-
|
||||
expand_goal(Goals0, Module, Goals1, HeadVars),
|
||||
expand_goal_cases((\+ Goals0), Module, ExpandedGoals, HeadVars, TGs) :-
|
||||
expand_goal(Goals0, Module, Goals1, HeadVars, TGs),
|
||||
ExpandedGoals = (\+ Goals1).
|
||||
expand_goal_cases((Module:Goals0), _, ExpandedGoals, HeadVars) :-
|
||||
expand_goal(Goals0, Module, Goals1, HeadVars),
|
||||
expand_goal_cases((Module:Goals0), _, ExpandedGoals, HeadVars, TGs) :-
|
||||
expand_goal(Goals0, Module, Goals1, HeadVars, TGs),
|
||||
ExpandedGoals = (Module:Goals1).
|
||||
|
||||
:- non_counted_backtracking thread_goals/3.
|
||||
|
||||
@@ -14,3 +14,9 @@ impl MachineArgs {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for MachineArgs {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use dashu::base::{Abs, Gcd, Signed, UnsignedAbs};
|
||||
use dashu::integer::IBig;
|
||||
use dashu::integer::fast_div::ConstDivisor;
|
||||
use dashu::integer::IBig;
|
||||
use divrem::*;
|
||||
use num_order::NumOrd;
|
||||
|
||||
@@ -15,16 +15,13 @@ use crate::parser::ast::*;
|
||||
use crate::parser::dashu::{Integer, Rational};
|
||||
use crate::types::*;
|
||||
|
||||
use crate::fixnum;
|
||||
|
||||
use ordered_float::*;
|
||||
use ordered_float::{Float, OrderedFloat};
|
||||
|
||||
use std::cmp;
|
||||
use std::convert::TryFrom;
|
||||
use std::f64;
|
||||
use std::mem;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! try_numeric_result {
|
||||
($e: expr, $stub_gen: expr) => {
|
||||
match $e {
|
||||
@@ -84,18 +81,18 @@ fn numerical_type_error(
|
||||
|
||||
fn isize_gcd(n1: isize, n2: isize) -> Option<isize> {
|
||||
if n1 == 0 {
|
||||
return n2.checked_abs().map(|n| n as isize);
|
||||
return n2.checked_abs();
|
||||
}
|
||||
|
||||
if n2 == 0 {
|
||||
return n1.checked_abs().map(|n| n as isize);
|
||||
return n1.checked_abs();
|
||||
}
|
||||
|
||||
let n1 = n1.checked_abs();
|
||||
let n2 = n2.checked_abs();
|
||||
|
||||
let mut n1 = if let Some(n1) = n1 { n1 } else { return None };
|
||||
let mut n2 = if let Some(n2) = n2 { n2 } else { return None };
|
||||
let mut n1 = n1?;
|
||||
let mut n2 = n2?;
|
||||
|
||||
let mut shift = 0;
|
||||
|
||||
@@ -115,9 +112,7 @@ fn isize_gcd(n1: isize, n2: isize) -> Option<isize> {
|
||||
}
|
||||
|
||||
if n1 > n2 {
|
||||
let t = n2;
|
||||
n2 = n1;
|
||||
n1 = t;
|
||||
std::mem::swap(&mut n2, &mut n1);
|
||||
}
|
||||
|
||||
n2 -= n1;
|
||||
@@ -350,22 +345,18 @@ pub(crate) fn int_pow(n1: Number, n2: Number, arena: &mut Arena) -> Result<Numbe
|
||||
(Number::Fixnum(n1), Number::Integer(n2)) => {
|
||||
let n1_i = n1.get_num();
|
||||
|
||||
if !(n1_i == 1 || n1_i == 0 || n1_i == -1) && &*n2 < &Integer::from(0) {
|
||||
if !(n1_i == 1 || n1_i == 0 || n1_i == -1) && n2.is_negative() {
|
||||
let n = Number::Fixnum(n1);
|
||||
Err(numerical_type_error(ValidType::Float, n, stub_gen))
|
||||
} else {
|
||||
let n1 = Integer::from(n1_i);
|
||||
Ok(Number::arena_from(binary_pow(n1, &*n2), arena))
|
||||
Ok(Number::arena_from(binary_pow(n1, &n2), arena))
|
||||
}
|
||||
}
|
||||
(Number::Integer(n1), Number::Fixnum(n2)) => {
|
||||
let n2_i = n2.get_num();
|
||||
|
||||
if !(&*n1 == &Integer::from(1)
|
||||
|| &*n1 == &Integer::from(0)
|
||||
|| &*n1 == &Integer::from(-1))
|
||||
&& n2_i < 0
|
||||
{
|
||||
if !(n1.is_one() || n1.is_zero() || n1.num_eq(&-1)) && n2_i < 0 {
|
||||
let n = Number::Integer(n1);
|
||||
Err(numerical_type_error(ValidType::Float, n, stub_gen))
|
||||
} else {
|
||||
@@ -374,15 +365,11 @@ pub(crate) fn int_pow(n1: Number, n2: Number, arena: &mut Arena) -> Result<Numbe
|
||||
}
|
||||
}
|
||||
(Number::Integer(n1), Number::Integer(n2)) => {
|
||||
if !(&*n1 == &Integer::from(1)
|
||||
|| &*n1 == &Integer::from(0)
|
||||
|| &*n1 == &Integer::from(-1))
|
||||
&& &*n2 < &Integer::from(0)
|
||||
{
|
||||
if !(n1.is_one() || n1.is_zero() || n1.num_eq(&-1)) && n2.is_negative() {
|
||||
let n = Number::Integer(n1);
|
||||
Err(numerical_type_error(ValidType::Float, n, stub_gen))
|
||||
} else {
|
||||
Ok(Number::arena_from(binary_pow((*n1).clone(), &*n2), arena))
|
||||
Ok(Number::arena_from(binary_pow((*n1).clone(), &n2), arena))
|
||||
}
|
||||
}
|
||||
(n1, Number::Integer(n2)) => {
|
||||
@@ -455,14 +442,14 @@ pub(crate) fn max(n1: Number, n2: Number) -> Result<Number, MachineStubGen> {
|
||||
}
|
||||
}
|
||||
(Number::Fixnum(n1), Number::Integer(n2)) => {
|
||||
if (&*n2).num_gt(&n1.get_num()) {
|
||||
if (*n2).num_gt(&n1.get_num()) {
|
||||
Ok(Number::Integer(n2))
|
||||
} else {
|
||||
Ok(Number::Fixnum(n1))
|
||||
}
|
||||
}
|
||||
(Number::Integer(n1), Number::Fixnum(n2)) => {
|
||||
if (&*n1).num_gt(&n2.get_num()) {
|
||||
if (*n1).num_gt(&n2.get_num()) {
|
||||
Ok(Number::Integer(n1))
|
||||
} else {
|
||||
Ok(Number::Fixnum(n2))
|
||||
@@ -499,14 +486,14 @@ pub(crate) fn min(n1: Number, n2: Number) -> Result<Number, MachineStubGen> {
|
||||
}
|
||||
}
|
||||
(Number::Fixnum(n1), Number::Integer(n2)) => {
|
||||
if (&*n2).num_lt(&n1.get_num()) {
|
||||
if (*n2).num_lt(&n1.get_num()) {
|
||||
Ok(Number::Integer(n2))
|
||||
} else {
|
||||
Ok(Number::Fixnum(n1))
|
||||
}
|
||||
}
|
||||
(Number::Integer(n1), Number::Fixnum(n2)) => {
|
||||
if (&*n1).num_lt(&n2.get_num()) {
|
||||
if (*n1).num_lt(&n2.get_num()) {
|
||||
Ok(Number::Integer(n1))
|
||||
} else {
|
||||
Ok(Number::Fixnum(n2))
|
||||
@@ -583,15 +570,13 @@ pub(crate) fn idiv(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number,
|
||||
(Number::Fixnum(n1), Number::Fixnum(n2)) => {
|
||||
if n2.get_num() == 0 {
|
||||
Err(zero_divisor_eval_error(stub_gen))
|
||||
} else if let Some(result) = n1.get_num().checked_div(n2.get_num()) {
|
||||
Ok(Number::arena_from(result, arena))
|
||||
} else {
|
||||
if let Some(result) = n1.get_num().checked_div(n2.get_num()) {
|
||||
Ok(Number::arena_from(result, arena))
|
||||
} else {
|
||||
let n1 = Integer::from(n1.get_num());
|
||||
let n2 = Integer::from(n2.get_num());
|
||||
let n1 = Integer::from(n1.get_num());
|
||||
let n2 = Integer::from(n2.get_num());
|
||||
|
||||
Ok(Number::arena_from(n1 / n2, arena))
|
||||
}
|
||||
Ok(Number::arena_from(n1 / n2, arena))
|
||||
}
|
||||
}
|
||||
(Number::Fixnum(n1), Number::Integer(n2)) => {
|
||||
@@ -653,12 +638,17 @@ pub(crate) fn shr(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number, M
|
||||
let n1_i = n1.get_num();
|
||||
let n2_i = n2.get_num();
|
||||
|
||||
let n1 = Integer::from(n1_i);
|
||||
|
||||
// FIXME(arithmetic_overflow)
|
||||
// what should this do for too large n2,
|
||||
// - logical right shift should probably turn to 0
|
||||
// - arithmetic right shift should maybe differ for negative numbers
|
||||
//
|
||||
// note: negaitve n2 is already handled above
|
||||
#[allow(arithmetic_overflow)]
|
||||
if let Ok(n2) = usize::try_from(n2_i) {
|
||||
return Ok(Number::arena_from(n1 >> n2, arena));
|
||||
Ok(Number::arena_from(n1_i >> n2, arena))
|
||||
} else {
|
||||
return Ok(Number::arena_from(n1 >> usize::max_value(), arena));
|
||||
Ok(Number::arena_from(n1_i >> usize::MAX, arena))
|
||||
}
|
||||
}
|
||||
(Number::Fixnum(n1), Number::Integer(n2)) => {
|
||||
@@ -667,33 +657,22 @@ pub(crate) fn shr(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number, M
|
||||
let result: Result<usize, _> = (&*n2).try_into();
|
||||
|
||||
match result {
|
||||
Ok(n2) => {
|
||||
Ok(Number::arena_from(n1 >> n2, arena))
|
||||
}
|
||||
Err(_) => {
|
||||
Ok(Number::arena_from(n1 >> usize::max_value(), arena))
|
||||
}
|
||||
Ok(n2) => Ok(Number::arena_from(n1 >> n2, arena)),
|
||||
Err(_) => Ok(Number::arena_from(n1 >> usize::MAX, arena)),
|
||||
}
|
||||
}
|
||||
(Number::Integer(n1), Number::Fixnum(n2)) => match usize::try_from(n2.get_num()) {
|
||||
Ok(n2) => Ok(Number::arena_from(Integer::from(&*n1 >> n2), arena)),
|
||||
_ => Ok(Number::arena_from(
|
||||
Integer::from(&*n1 >> usize::max_value()),
|
||||
arena,
|
||||
)),
|
||||
_ => Ok(Number::arena_from(Integer::from(&*n1 >> usize::MAX), arena)),
|
||||
},
|
||||
(Number::Integer(n1), Number::Integer(n2)) => {
|
||||
let result: Result<usize, _> = (&*n2).try_into();
|
||||
|
||||
match result {
|
||||
Ok(n2) => {
|
||||
Ok(Number::arena_from(Integer::from(&*n1 >> n2), arena))
|
||||
}
|
||||
Err(_) => {
|
||||
Ok(Number::arena_from(Integer::from(&*n1 >> usize::max_value()), arena))
|
||||
}
|
||||
Ok(n2) => Ok(Number::arena_from(Integer::from(&*n1 >> n2), arena)),
|
||||
Err(_) => Ok(Number::arena_from(Integer::from(&*n1 >> usize::MAX), arena)),
|
||||
}
|
||||
},
|
||||
}
|
||||
(Number::Integer(_), n2) => Err(numerical_type_error(ValidType::Integer, n2, stub_gen)),
|
||||
(Number::Fixnum(_), n2) => Err(numerical_type_error(ValidType::Integer, n2, stub_gen)),
|
||||
(n1, _) => Err(numerical_type_error(ValidType::Integer, n1, stub_gen)),
|
||||
@@ -715,42 +694,28 @@ pub(crate) fn shl(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number, M
|
||||
let n1_i = n1.get_num();
|
||||
let n2_i = n2.get_num();
|
||||
|
||||
let n1 = Integer::from(n1_i);
|
||||
|
||||
if let Ok(n2) = usize::try_from(n2_i) {
|
||||
return Ok(Number::arena_from(n1 << n2, arena));
|
||||
Ok(Number::arena_from(n1_i << n2, arena))
|
||||
} else {
|
||||
return Ok(Number::arena_from(n1 << usize::max_value(), arena));
|
||||
let n1 = Integer::from(n1_i);
|
||||
Ok(Number::arena_from(n1 << usize::MAX, arena))
|
||||
}
|
||||
}
|
||||
(Number::Fixnum(n1), Number::Integer(n2)) => {
|
||||
let n1 = Integer::from(n1.get_num());
|
||||
|
||||
match (&*n2).try_into() as Result<u32, _> {
|
||||
Ok(n2) => {
|
||||
let n1: u64 = n1.try_into().unwrap();
|
||||
Ok(Number::arena_from(n1 << n2, arena))
|
||||
},
|
||||
_ => {
|
||||
Ok(Number::arena_from(n1 << usize::max_value(), arena))
|
||||
}
|
||||
match (&*n2).try_into() as Result<usize, _> {
|
||||
Ok(n2) => Ok(Number::arena_from(n1 << n2, arena)),
|
||||
_ => Ok(Number::arena_from(n1 << usize::MAX, arena)),
|
||||
}
|
||||
}
|
||||
(Number::Integer(n1), Number::Fixnum(n2)) => match usize::try_from(n2.get_num()) {
|
||||
Ok(n2) => Ok(Number::arena_from(Integer::from(&*n1 << n2), arena)),
|
||||
_ => Ok(Number::arena_from(
|
||||
Integer::from(&*n1 << usize::max_value()),
|
||||
arena,
|
||||
)),
|
||||
_ => Ok(Number::arena_from(Integer::from(&*n1 << usize::MAX), arena)),
|
||||
},
|
||||
(Number::Integer(n1), Number::Integer(n2)) => match (&*n2).try_into() as Result<u32, _> {
|
||||
Ok(n2) => {
|
||||
let n1: u64 = (&*n1).try_into().unwrap();
|
||||
Ok(Number::arena_from(Integer::from(n1 << n2), arena))
|
||||
},
|
||||
_ => {
|
||||
Ok(Number::arena_from(Integer::from(&*n1 << usize::max_value()),arena))
|
||||
}
|
||||
(Number::Integer(n1), Number::Integer(n2)) => match (&*n2).try_into() as Result<usize, _> {
|
||||
Ok(n2) => Ok(Number::arena_from(Integer::from(&*n1 << n2), arena)),
|
||||
_ => Ok(Number::arena_from(Integer::from(&*n1 << usize::MAX), arena)),
|
||||
},
|
||||
(Number::Integer(_), n2) => Err(numerical_type_error(ValidType::Integer, n2, stub_gen)),
|
||||
(Number::Fixnum(_), n2) => Err(numerical_type_error(ValidType::Integer, n2, stub_gen)),
|
||||
@@ -882,7 +847,7 @@ pub(crate) fn modulus(x: Number, y: Number, arena: &mut Arena) -> Result<Number,
|
||||
Err(zero_divisor_eval_error(stub_gen))
|
||||
} else {
|
||||
let n1 = Integer::from(n1.get_num());
|
||||
Ok(Number::arena_from(ibig_rem_floor(&n1, &*n2), arena))
|
||||
Ok(Number::arena_from(ibig_rem_floor(&n1, &n2), arena))
|
||||
}
|
||||
}
|
||||
(Number::Integer(n1), Number::Fixnum(n2)) => {
|
||||
@@ -892,14 +857,14 @@ pub(crate) fn modulus(x: Number, y: Number, arena: &mut Arena) -> Result<Number,
|
||||
Err(zero_divisor_eval_error(stub_gen))
|
||||
} else {
|
||||
let n2 = Integer::from(n2_i);
|
||||
Ok(Number::arena_from(ibig_rem_floor(&*n1, &n2), arena))
|
||||
Ok(Number::arena_from(ibig_rem_floor(&n1, &n2), arena))
|
||||
}
|
||||
}
|
||||
(Number::Integer(n1), Number::Integer(n2)) => {
|
||||
if n2.is_zero() {
|
||||
Err(zero_divisor_eval_error(stub_gen))
|
||||
} else {
|
||||
Ok(Number::arena_from(ibig_rem_floor(&*n1, &*n2), arena))
|
||||
Ok(Number::arena_from(ibig_rem_floor(&n1, &n2), arena))
|
||||
}
|
||||
}
|
||||
(Number::Integer(_), n2) | (Number::Fixnum(_), n2) => {
|
||||
@@ -1145,7 +1110,7 @@ impl MachineState {
|
||||
&mut self.interms[i - 1],
|
||||
Number::Fixnum(Fixnum::build_with(0)),
|
||||
)),
|
||||
&ArithmeticTerm::Number(n) => Ok(n),
|
||||
ArithmeticTerm::Number(n) => Ok(*n),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1167,8 +1132,8 @@ impl MachineState {
|
||||
value: HeapCellValue,
|
||||
) -> Result<Number, MachineStub> {
|
||||
let stub_gen = || functor_stub(atom!("is"), 2);
|
||||
let mut iter = stackful_post_order_iter::<NonListElider>
|
||||
(&mut self.heap, &mut self.stack, value);
|
||||
let mut iter =
|
||||
stackful_post_order_iter::<NonListElider>(&mut self.heap, &mut self.stack, value);
|
||||
|
||||
while let Some(value) = iter.next() {
|
||||
if value.get_forwarding_bit() {
|
||||
@@ -1451,11 +1416,11 @@ mod tests {
|
||||
let mut wam = MachineState::new();
|
||||
let mut op_dir = default_op_dir();
|
||||
|
||||
op_dir.insert((atom!("+"), Fixity::In), OpDesc::build_with(500, YFX as u8));
|
||||
op_dir.insert((atom!("-"), Fixity::In), OpDesc::build_with(500, YFX as u8));
|
||||
op_dir.insert((atom!("-"), Fixity::Pre), OpDesc::build_with(200, FY as u8));
|
||||
op_dir.insert((atom!("*"), Fixity::In), OpDesc::build_with(400, YFX as u8));
|
||||
op_dir.insert((atom!("/"), Fixity::In), OpDesc::build_with(400, YFX as u8));
|
||||
op_dir.insert((atom!("+"), Fixity::In), OpDesc::build_with(500, YFX));
|
||||
op_dir.insert((atom!("-"), Fixity::In), OpDesc::build_with(500, YFX));
|
||||
op_dir.insert((atom!("-"), Fixity::Pre), OpDesc::build_with(200, FY));
|
||||
op_dir.insert((atom!("*"), Fixity::In), OpDesc::build_with(400, YFX));
|
||||
op_dir.insert((atom!("/"), Fixity::In), OpDesc::build_with(400, YFX));
|
||||
|
||||
let term_write_result =
|
||||
parse_and_write_parsed_term_to_heap(&mut wam, "3 + 4 - 1 + 2.", &op_dir).unwrap();
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use crate::heap_iter::*;
|
||||
use crate::machine::*;
|
||||
use crate::parser::ast::*;
|
||||
use crate::temp_v;
|
||||
use crate::types::*;
|
||||
|
||||
use indexmap::IndexSet;
|
||||
@@ -133,8 +132,8 @@ impl MachineState {
|
||||
let mut seen_set = IndexSet::new();
|
||||
let mut seen_vars = vec![];
|
||||
|
||||
let mut iter = stackful_preorder_iter::<NonListElider>
|
||||
(&mut self.heap, &mut self.stack, cell);
|
||||
let mut iter =
|
||||
stackful_preorder_iter::<NonListElider>(&mut self.heap, &mut self.stack, cell);
|
||||
|
||||
while let Some(value) = iter.next() {
|
||||
read_heap_cell!(value,
|
||||
|
||||
@@ -42,42 +42,35 @@ pub(super) fn bootstrapping_compile(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn lower_bound_of_target_clause(skeleton: &PredicateSkeleton, target_pos: usize) -> usize {
|
||||
fn lower_bound_of_target_clause(skeleton: &mut PredicateSkeleton, target_pos: usize) -> usize {
|
||||
if target_pos == 0 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let arg_num = skeleton.clauses[target_pos - 1].opt_arg_index_key.arg_num();
|
||||
debug_assert!(skeleton.clauses.len() >= 2);
|
||||
|
||||
if arg_num == 0 {
|
||||
return target_pos - 1;
|
||||
}
|
||||
let index = target_pos - 1;
|
||||
|
||||
let mut index_loc_opt = None;
|
||||
let index = if let Some(index_loc) = skeleton.clauses[index]
|
||||
.opt_arg_index_key
|
||||
.switch_on_term_loc()
|
||||
{
|
||||
let search_result = skeleton.clauses.make_contiguous()
|
||||
[0..skeleton.core.clause_assert_margin]
|
||||
.partition_point(|clause_index_info| clause_index_info.clause_start > index_loc);
|
||||
|
||||
for index in (0..target_pos).rev() {
|
||||
let current_arg_num = skeleton.clauses[index].opt_arg_index_key.arg_num();
|
||||
|
||||
if current_arg_num == 0 || current_arg_num != arg_num {
|
||||
return index + 1;
|
||||
}
|
||||
|
||||
if let Some(index_loc) = index_loc_opt {
|
||||
let current_index_loc = skeleton.clauses[index]
|
||||
.opt_arg_index_key
|
||||
.switch_on_term_loc();
|
||||
|
||||
if Some(index_loc) != current_index_loc {
|
||||
return index + 1;
|
||||
}
|
||||
if search_result < skeleton.core.clause_assert_margin {
|
||||
search_result
|
||||
} else {
|
||||
index_loc_opt = skeleton.clauses[index]
|
||||
.opt_arg_index_key
|
||||
.switch_on_term_loc();
|
||||
skeleton.clauses.make_contiguous()[skeleton.core.clause_assert_margin..]
|
||||
.partition_point(|clause_index_info| clause_index_info.clause_start < index_loc)
|
||||
+ skeleton.core.clause_assert_margin
|
||||
}
|
||||
}
|
||||
} else {
|
||||
index
|
||||
};
|
||||
|
||||
0
|
||||
index.clamp(0, skeleton.clauses.len() - 2)
|
||||
}
|
||||
|
||||
fn derelictize_try_me_else(
|
||||
@@ -282,28 +275,25 @@ fn merge_indexed_subsequences(
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
match &mut code[inner_try_me_else_loc] {
|
||||
Instruction::TryMeElse(ref mut o) => {
|
||||
retraction_info.push_record(RetractionRecord::ModifiedTryMeElse(
|
||||
inner_try_me_else_loc,
|
||||
*o,
|
||||
));
|
||||
if let Instruction::TryMeElse(ref mut o) = &mut code[inner_try_me_else_loc] {
|
||||
retraction_info.push_record(RetractionRecord::ModifiedTryMeElse(
|
||||
inner_try_me_else_loc,
|
||||
*o,
|
||||
));
|
||||
|
||||
match *o {
|
||||
0 => {
|
||||
code[inner_try_me_else_loc] = Instruction::TrustMe(0);
|
||||
}
|
||||
o => match &code[inner_try_me_else_loc + o] {
|
||||
Instruction::RevJmpBy(0) => {
|
||||
code[inner_try_me_else_loc] = Instruction::TrustMe(o);
|
||||
}
|
||||
_ => {
|
||||
code[inner_try_me_else_loc] = Instruction::RetryMeElse(o);
|
||||
}
|
||||
},
|
||||
match *o {
|
||||
0 => {
|
||||
code[inner_try_me_else_loc] = Instruction::TrustMe(0);
|
||||
}
|
||||
o => match &code[inner_try_me_else_loc + o] {
|
||||
Instruction::RevJmpBy(0) => {
|
||||
code[inner_try_me_else_loc] = Instruction::TrustMe(o);
|
||||
}
|
||||
_ => {
|
||||
code[inner_try_me_else_loc] = Instruction::RetryMeElse(o);
|
||||
}
|
||||
},
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
thread_choice_instr_at_to(
|
||||
@@ -333,8 +323,8 @@ fn merge_indexed_subsequences(
|
||||
retraction_info,
|
||||
);
|
||||
}
|
||||
None => match &mut code[outer_threaded_choice_instr_loc] {
|
||||
Instruction::TryMeElse(ref mut o) => {
|
||||
None => {
|
||||
if let Instruction::TryMeElse(ref mut o) = &mut code[outer_threaded_choice_instr_loc] {
|
||||
retraction_info
|
||||
.push_record(RetractionRecord::ModifiedTryMeElse(inner_trust_me_loc, *o));
|
||||
|
||||
@@ -342,8 +332,7 @@ fn merge_indexed_subsequences(
|
||||
|
||||
return Some(IndexPtr::index(outer_threaded_choice_instr_loc + 1));
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
@@ -919,7 +908,7 @@ fn prepend_compiled_clause(
|
||||
retraction_info,
|
||||
);
|
||||
|
||||
code.extend(prepend_queue.into_iter());
|
||||
code.extend(prepend_queue);
|
||||
|
||||
if skeleton.core.is_dynamic {
|
||||
clause_loc
|
||||
@@ -975,7 +964,7 @@ fn prepend_compiled_clause(
|
||||
|
||||
internalize_choice_instr_at(code, old_clause_start, retraction_info);
|
||||
|
||||
code.extend(prepend_queue.into_iter());
|
||||
code.extend(prepend_queue);
|
||||
|
||||
clause_loc // + (outer_thread_choice_offset == 0 as usize)
|
||||
}
|
||||
@@ -1004,7 +993,7 @@ fn prepend_compiled_clause(
|
||||
|
||||
internalize_choice_instr_at(code, old_clause_start, retraction_info);
|
||||
|
||||
code.extend(prepend_queue.into_iter());
|
||||
code.extend(prepend_queue);
|
||||
|
||||
// skeleton.clauses[0].opt_arg_index_key += clause_loc;
|
||||
skeleton.clauses[0].clause_start = clause_loc;
|
||||
@@ -1029,7 +1018,7 @@ fn prepend_compiled_clause(
|
||||
|
||||
internalize_choice_instr_at(code, old_clause_start, retraction_info);
|
||||
|
||||
code.extend(prepend_queue.into_iter());
|
||||
code.extend(prepend_queue);
|
||||
|
||||
// skeleton.clauses[0].opt_arg_index_key += clause_loc;
|
||||
skeleton.clauses[0].clause_start = clause_loc;
|
||||
@@ -1134,21 +1123,18 @@ fn append_compiled_clause(
|
||||
skeleton.clauses[target_pos].opt_arg_index_key += clause_loc;
|
||||
code.extend(clause_code.drain(1..));
|
||||
|
||||
match skeleton.clauses[target_pos]
|
||||
if let Some(index_loc) = skeleton.clauses[target_pos]
|
||||
.opt_arg_index_key
|
||||
.switch_on_term_loc()
|
||||
{
|
||||
Some(index_loc) => {
|
||||
// point to the inner-threaded TryMeElse(0) if target_pos is
|
||||
// indexed, and make switch_on_term point one line after it in
|
||||
// its variable offset.
|
||||
skeleton.clauses[target_pos].clause_start += 2;
|
||||
// point to the inner-threaded TryMeElse(0) if target_pos is
|
||||
// indexed, and make switch_on_term point one line after it in
|
||||
// its variable offset.
|
||||
skeleton.clauses[target_pos].clause_start += 2;
|
||||
|
||||
if !skeleton.core.is_dynamic {
|
||||
set_switch_var_offset(code, index_loc, 2, retraction_info);
|
||||
}
|
||||
if !skeleton.core.is_dynamic {
|
||||
set_switch_var_offset(code, index_loc, 2, retraction_info);
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
|
||||
match skeleton.clauses[lower_bound]
|
||||
@@ -1208,11 +1194,8 @@ fn print_overwrite_warning(
|
||||
key: PredicateKey,
|
||||
is_dynamic: bool,
|
||||
) {
|
||||
if let CompilationTarget::Module(module_name) = compilation_target {
|
||||
match module_name {
|
||||
atom!("builtins") | atom!("loader") => return,
|
||||
_ => {}
|
||||
}
|
||||
if let CompilationTarget::Module(atom!("builtins") | atom!("loader")) = compilation_target {
|
||||
return;
|
||||
}
|
||||
|
||||
match code_ptr.tag() {
|
||||
@@ -1222,7 +1205,7 @@ fn print_overwrite_warning(
|
||||
}
|
||||
|
||||
println!(
|
||||
"Warning: overwriting {}/{} because the clauses are discontiguous",
|
||||
"% Warning: overwriting {}/{} because the clauses are discontiguous",
|
||||
key.0.as_str(),
|
||||
key.1
|
||||
);
|
||||
@@ -1302,11 +1285,8 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
clause_clause_locs.push_back(clause_index_info.clause_start);
|
||||
}
|
||||
|
||||
match &mut code[0] {
|
||||
Instruction::TryMeElse(0) => {
|
||||
code_ptr += 1;
|
||||
}
|
||||
_ => {}
|
||||
if let Instruction::TryMeElse(0) = &mut code[0] {
|
||||
code_ptr += 1;
|
||||
}
|
||||
|
||||
match self
|
||||
@@ -1317,7 +1297,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
Some(skeleton) => {
|
||||
let skeleton_clause_len = skeleton.clauses.len();
|
||||
|
||||
skeleton.clauses.extend(cg.skeleton.clauses.into_iter());
|
||||
skeleton.clauses.extend(cg.skeleton.clauses);
|
||||
skeleton
|
||||
.core
|
||||
.clause_clause_locs
|
||||
@@ -1337,7 +1317,8 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
.clause_clause_locs
|
||||
.extend(&clause_clause_locs.make_contiguous()[0..]);
|
||||
|
||||
let skeleton = cg.skeleton;
|
||||
let mut skeleton = cg.skeleton;
|
||||
skeleton.core.is_dynamic = settings.is_dynamic();
|
||||
|
||||
self.add_extensible_predicate(key, skeleton, predicates.compilation_target);
|
||||
}
|
||||
@@ -1371,7 +1352,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
index_ptr,
|
||||
);
|
||||
|
||||
self.wam_prelude.code.extend(code.into_iter());
|
||||
self.wam_prelude.code.extend(code);
|
||||
Ok(code_index)
|
||||
}
|
||||
|
||||
@@ -1537,6 +1518,8 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
|
||||
let code_len = self.wam_prelude.code.len();
|
||||
|
||||
standalone_skeleton.clauses[0].clause_start += code_len;
|
||||
|
||||
let skeleton = match self
|
||||
.wam_prelude
|
||||
.indices
|
||||
@@ -1549,8 +1532,8 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
match append_or_prepend {
|
||||
AppendOrPrepend::Append => {
|
||||
let clause_index_info = standalone_skeleton.clauses.pop_back().unwrap();
|
||||
skeleton.clauses.push_back(clause_index_info);
|
||||
|
||||
skeleton.clauses.push_back(clause_index_info);
|
||||
skeleton.core.clause_clause_locs.push_back(code_len);
|
||||
|
||||
self.payload
|
||||
@@ -1563,7 +1546,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
let global_clock = LS::machine_st(&mut self.payload).global_clock;
|
||||
|
||||
let result = append_compiled_clause(
|
||||
&mut self.wam_prelude.code,
|
||||
self.wam_prelude.code,
|
||||
clause_code,
|
||||
skeleton,
|
||||
&mut self.payload.retraction_info,
|
||||
@@ -1603,7 +1586,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
let global_clock = LS::machine_st(&mut self.payload).global_clock;
|
||||
|
||||
let new_code_ptr = prepend_compiled_clause(
|
||||
&mut self.wam_prelude.code,
|
||||
self.wam_prelude.code,
|
||||
compilation_target,
|
||||
key,
|
||||
clause_code,
|
||||
@@ -1646,7 +1629,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
.switch_on_term_loc()
|
||||
{
|
||||
Some(index_loc) => find_inner_choice_instr(
|
||||
&self.wam_prelude.code,
|
||||
self.wam_prelude.code,
|
||||
skeleton.clauses[target_pos].clause_start,
|
||||
index_loc,
|
||||
),
|
||||
@@ -1687,115 +1670,109 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
if target_pos == 0 || (lower_bound + 1 == target_pos && lower_bound_is_unindexed) {
|
||||
// the clause preceding target_pos, if there is one, is of
|
||||
// key type OptArgIndexKey::None.
|
||||
match skeleton.clauses[target_pos]
|
||||
if let Some(index_loc) = skeleton.clauses[target_pos]
|
||||
.opt_arg_index_key
|
||||
.switch_on_term_loc()
|
||||
{
|
||||
Some(index_loc) => {
|
||||
let inner_clause_start = find_inner_choice_instr(
|
||||
code,
|
||||
skeleton.clauses[target_pos].clause_start,
|
||||
index_loc,
|
||||
);
|
||||
let inner_clause_start = find_inner_choice_instr(
|
||||
code,
|
||||
skeleton.clauses[target_pos].clause_start,
|
||||
index_loc,
|
||||
);
|
||||
|
||||
remove_index_from_subsequence(
|
||||
code,
|
||||
&skeleton.clauses[target_pos].opt_arg_index_key,
|
||||
inner_clause_start,
|
||||
&mut self.payload.retraction_info,
|
||||
);
|
||||
remove_index_from_subsequence(
|
||||
code,
|
||||
&skeleton.clauses[target_pos].opt_arg_index_key,
|
||||
inner_clause_start,
|
||||
&mut self.payload.retraction_info,
|
||||
);
|
||||
|
||||
match derelictize_try_me_else(
|
||||
code,
|
||||
inner_clause_start,
|
||||
&mut self.payload.retraction_info,
|
||||
) {
|
||||
Some(offset) => {
|
||||
let instr_loc = find_inner_choice_instr(
|
||||
code,
|
||||
inner_clause_start + offset,
|
||||
index_loc,
|
||||
);
|
||||
match derelictize_try_me_else(
|
||||
code,
|
||||
inner_clause_start,
|
||||
&mut self.payload.retraction_info,
|
||||
) {
|
||||
Some(offset) => {
|
||||
let instr_loc =
|
||||
find_inner_choice_instr(code, inner_clause_start + offset, index_loc);
|
||||
|
||||
let clause_loc = blunt_leading_choice_instr(
|
||||
code,
|
||||
instr_loc,
|
||||
&mut self.payload.retraction_info,
|
||||
);
|
||||
let clause_loc = blunt_leading_choice_instr(
|
||||
code,
|
||||
instr_loc,
|
||||
&mut self.payload.retraction_info,
|
||||
);
|
||||
|
||||
set_switch_var_offset(
|
||||
code,
|
||||
index_loc,
|
||||
clause_loc - index_loc,
|
||||
&mut self.payload.retraction_info,
|
||||
);
|
||||
set_switch_var_offset(
|
||||
code,
|
||||
index_loc,
|
||||
clause_loc - index_loc,
|
||||
&mut self.payload.retraction_info,
|
||||
);
|
||||
|
||||
self.payload.retraction_info.push_record(
|
||||
RetractionRecord::SkeletonClauseStartReplaced(
|
||||
payload_compilation_target,
|
||||
key,
|
||||
target_pos + 1,
|
||||
skeleton.clauses[target_pos + 1].clause_start,
|
||||
),
|
||||
);
|
||||
|
||||
skeleton.clauses[target_pos + 1].clause_start =
|
||||
skeleton.clauses[target_pos].clause_start;
|
||||
|
||||
let update_code_index = target_pos == 0
|
||||
&& skeleton.clauses[target_pos + 1]
|
||||
.opt_arg_index_key
|
||||
.switch_on_term_loc()
|
||||
.is_none();
|
||||
|
||||
let index_ptr_opt = if update_code_index {
|
||||
Some(IndexPtr::index(clause_loc))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
return finalize_retract(
|
||||
key,
|
||||
self.payload.retraction_info.push_record(
|
||||
RetractionRecord::SkeletonClauseStartReplaced(
|
||||
payload_compilation_target,
|
||||
skeleton,
|
||||
code_index,
|
||||
target_pos,
|
||||
index_ptr_opt,
|
||||
&mut self.payload.retraction_info,
|
||||
);
|
||||
}
|
||||
None => {
|
||||
let index_ptr_opt = if target_pos > 0 {
|
||||
let preceding_choice_instr_loc =
|
||||
skeleton.clauses[target_pos - 1].clause_start;
|
||||
|
||||
remove_non_leading_clause(
|
||||
code,
|
||||
preceding_choice_instr_loc,
|
||||
skeleton.clauses[target_pos].clause_start - 2,
|
||||
&mut self.payload.retraction_info,
|
||||
)
|
||||
} else {
|
||||
remove_leading_unindexed_clause(
|
||||
code,
|
||||
skeleton.clauses[target_pos].clause_start - 2,
|
||||
&mut self.payload.retraction_info,
|
||||
)
|
||||
};
|
||||
|
||||
return finalize_retract(
|
||||
key,
|
||||
payload_compilation_target,
|
||||
skeleton,
|
||||
code_index,
|
||||
target_pos,
|
||||
index_ptr_opt,
|
||||
target_pos + 1,
|
||||
skeleton.clauses[target_pos + 1].clause_start,
|
||||
),
|
||||
);
|
||||
|
||||
skeleton.clauses[target_pos + 1].clause_start =
|
||||
skeleton.clauses[target_pos].clause_start;
|
||||
|
||||
let update_code_index = target_pos == 0
|
||||
&& skeleton.clauses[target_pos + 1]
|
||||
.opt_arg_index_key
|
||||
.switch_on_term_loc()
|
||||
.is_none();
|
||||
|
||||
let index_ptr_opt = if update_code_index {
|
||||
Some(IndexPtr::index(clause_loc))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
return finalize_retract(
|
||||
key,
|
||||
payload_compilation_target,
|
||||
skeleton,
|
||||
code_index,
|
||||
target_pos,
|
||||
index_ptr_opt,
|
||||
&mut self.payload.retraction_info,
|
||||
);
|
||||
}
|
||||
None => {
|
||||
let index_ptr_opt = if target_pos > 0 {
|
||||
let preceding_choice_instr_loc =
|
||||
skeleton.clauses[target_pos - 1].clause_start;
|
||||
|
||||
remove_non_leading_clause(
|
||||
code,
|
||||
preceding_choice_instr_loc,
|
||||
skeleton.clauses[target_pos].clause_start - 2,
|
||||
&mut self.payload.retraction_info,
|
||||
);
|
||||
}
|
||||
)
|
||||
} else {
|
||||
remove_leading_unindexed_clause(
|
||||
code,
|
||||
skeleton.clauses[target_pos].clause_start - 2,
|
||||
&mut self.payload.retraction_info,
|
||||
)
|
||||
};
|
||||
|
||||
return finalize_retract(
|
||||
key,
|
||||
payload_compilation_target,
|
||||
skeleton,
|
||||
code_index,
|
||||
target_pos,
|
||||
index_ptr_opt,
|
||||
&mut self.payload.retraction_info,
|
||||
);
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1824,16 +1801,13 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
Instruction::RevJmpBy(target_indexing_loc - later_indexing_loc),
|
||||
);
|
||||
|
||||
match target_indexing_line {
|
||||
Instruction::IndexingCode(indexing_code) => {
|
||||
self.payload.retraction_info.push_record(
|
||||
RetractionRecord::ReplacedIndexingLine(
|
||||
target_indexing_loc,
|
||||
indexing_code,
|
||||
),
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
if let Instruction::IndexingCode(indexing_code) = target_indexing_line {
|
||||
self.payload.retraction_info.push_record(
|
||||
RetractionRecord::ReplacedIndexingLine(
|
||||
target_indexing_loc,
|
||||
indexing_code,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
result = merge_indexed_subsequences(
|
||||
@@ -1977,16 +1951,15 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
&mut self.payload.retraction_info,
|
||||
);
|
||||
|
||||
match &mut code[preceding_choice_instr_loc] {
|
||||
Instruction::TryMeElse(0) => {
|
||||
set_switch_var_offset(
|
||||
code,
|
||||
index_loc,
|
||||
preceding_choice_instr_loc + 1 - index_loc,
|
||||
&mut self.payload.retraction_info,
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
if let Instruction::TryMeElse(0) =
|
||||
&mut code[preceding_choice_instr_loc]
|
||||
{
|
||||
set_switch_var_offset(
|
||||
code,
|
||||
index_loc,
|
||||
preceding_choice_instr_loc + 1 - index_loc,
|
||||
&mut self.payload.retraction_info,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2068,16 +2041,11 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
{
|
||||
Some(skeleton) if append_or_prepend.is_append() => {
|
||||
let tail_num = skeleton.core.clause_clause_locs.len() - num_clause_predicates;
|
||||
skeleton.core.clause_clause_locs.make_contiguous()[tail_num..]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect()
|
||||
skeleton.core.clause_clause_locs.make_contiguous()[tail_num..].to_vec()
|
||||
}
|
||||
Some(skeleton) => skeleton.core.clause_clause_locs.make_contiguous()
|
||||
[0..num_clause_predicates]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect(),
|
||||
.to_vec(),
|
||||
None => {
|
||||
unreachable!()
|
||||
}
|
||||
@@ -2173,7 +2141,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
.map(|skeleton| skeleton.predicate_info())
|
||||
.unwrap_or_default();
|
||||
|
||||
let mut predicate_info = self
|
||||
let predicate_info = self
|
||||
.wam_prelude
|
||||
.indices
|
||||
.get_predicate_skeleton(&self.payload.predicates.compilation_target, &key)
|
||||
@@ -2205,46 +2173,45 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
)?;
|
||||
}
|
||||
} else {
|
||||
if is_cross_module_clause {
|
||||
if !local_predicate_info.is_extensible {
|
||||
if predicate_info.is_multifile {
|
||||
println!(
|
||||
"Warning: overwriting multifile predicate {}:{}/{} because \
|
||||
it was not locally declared multifile.",
|
||||
self.payload.predicates.compilation_target,
|
||||
key.0.as_str(),
|
||||
key.1
|
||||
if is_cross_module_clause && !local_predicate_info.is_extensible {
|
||||
if predicate_info.is_multifile {
|
||||
println!(
|
||||
"% Warning: overwriting multifile predicate {}:{}/{} because \
|
||||
it was not locally declared multifile.",
|
||||
self.payload.predicates.compilation_target,
|
||||
key.0.as_str(),
|
||||
key.1
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(skeleton) = self
|
||||
.wam_prelude
|
||||
.indices
|
||||
.remove_predicate_skeleton(&self.payload.predicates.compilation_target, &key)
|
||||
{
|
||||
let compilation_target = self.payload.predicates.compilation_target;
|
||||
|
||||
if predicate_info.is_dynamic {
|
||||
let clause_clause_compilation_target = match compilation_target {
|
||||
CompilationTarget::User => CompilationTarget::Module(atom!("builtins")),
|
||||
module => module,
|
||||
};
|
||||
|
||||
self.retract_local_clauses_by_locs(
|
||||
clause_clause_compilation_target,
|
||||
(atom!("$clause"), 2),
|
||||
(0..skeleton.clauses.len()).map(Some).collect(),
|
||||
false, // the builtin M:'$clause'/2 is never dynamic.
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(skeleton) = self.wam_prelude.indices.remove_predicate_skeleton(
|
||||
&self.payload.predicates.compilation_target,
|
||||
&key,
|
||||
) {
|
||||
let compilation_target = self.payload.predicates.compilation_target;
|
||||
|
||||
if predicate_info.is_dynamic {
|
||||
let clause_clause_compilation_target = match compilation_target {
|
||||
CompilationTarget::User => {
|
||||
CompilationTarget::Module(atom!("builtins"))
|
||||
}
|
||||
module => module,
|
||||
};
|
||||
|
||||
self.retract_local_clauses_by_locs(
|
||||
clause_clause_compilation_target,
|
||||
(atom!("$clause"), 2),
|
||||
(0..skeleton.clauses.len()).map(Some).collect(),
|
||||
false, // the builtin M:'$clause'/2 is never dynamic.
|
||||
);
|
||||
|
||||
predicate_info.is_dynamic = false;
|
||||
}
|
||||
|
||||
self.payload.retraction_info.push_record(
|
||||
RetractionRecord::RemovedSkeleton(compilation_target, key, skeleton),
|
||||
);
|
||||
}
|
||||
self.payload
|
||||
.retraction_info
|
||||
.push_record(RetractionRecord::RemovedSkeleton(
|
||||
compilation_target,
|
||||
key,
|
||||
skeleton,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2262,20 +2229,17 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
let code_index = self.compile(key, predicates, settings)?;
|
||||
|
||||
if let Some(filename) = self.listing_src_file_name() {
|
||||
match self.wam_prelude.indices.modules.get_mut(&filename) {
|
||||
Some(ref mut module) => {
|
||||
let index_ptr = code_index.get();
|
||||
let code_index = module.code_dir.entry(key).or_insert(code_index).clone();
|
||||
if let Some(ref mut module) = self.wam_prelude.indices.modules.get_mut(&filename) {
|
||||
let index_ptr = code_index.get();
|
||||
let code_index = *module.code_dir.entry(key).or_insert(code_index);
|
||||
|
||||
set_code_index(
|
||||
&mut self.payload.retraction_info,
|
||||
&CompilationTarget::Module(filename),
|
||||
key,
|
||||
code_index,
|
||||
index_ptr,
|
||||
);
|
||||
}
|
||||
None => {}
|
||||
set_code_index(
|
||||
&mut self.payload.retraction_info,
|
||||
&CompilationTarget::Module(filename),
|
||||
key,
|
||||
code_index,
|
||||
index_ptr,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2344,7 +2308,7 @@ impl Machine {
|
||||
};
|
||||
|
||||
let StandaloneCompileResult { clause_code, .. } = compile()?;
|
||||
self.code.extend(clause_code.into_iter());
|
||||
self.code.extend(clause_code);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ pub trait CopierTarget: IndexMut<usize, Output = HeapCellValue> {
|
||||
fn store(&self, value: HeapCellValue) -> HeapCellValue;
|
||||
fn deref(&self, value: HeapCellValue) -> HeapCellValue;
|
||||
fn push(&mut self, value: HeapCellValue);
|
||||
fn push_attr_var_queue(&mut self, attr_var_loc: usize);
|
||||
fn stack(&mut self) -> &mut Stack;
|
||||
fn threshold(&self) -> usize;
|
||||
}
|
||||
@@ -73,7 +74,6 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
if h >= self.old_h {
|
||||
*self.value_at_scan() = list_loc_as_cell!(h);
|
||||
self.scan += 1;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -96,14 +96,19 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
.store(self.target.deref(heap_loc_as_cell!(addr + 1)));
|
||||
|
||||
if !cdr.is_var() {
|
||||
// mark addr + 1 as a list back edge in the cdr of the list
|
||||
self.trail_list_cell(addr + 1, threshold);
|
||||
self.target[addr + 1].set_mark_bit(true);
|
||||
self.target[addr + 1].set_forwarding_bit(true);
|
||||
} else {
|
||||
let car = self
|
||||
.target
|
||||
.store(self.target.deref(heap_loc_as_cell!(addr)));
|
||||
|
||||
if !car.is_var() {
|
||||
// mark addr as a list back edge in the car of the list
|
||||
self.trail_list_cell(addr, threshold);
|
||||
self.target[addr].set_mark_bit(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,10 +179,11 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
|
||||
fn copy_attr_var_lists(&mut self) {
|
||||
while !self.attr_var_list_locs.is_empty() {
|
||||
let iter = mem::replace(&mut self.attr_var_list_locs, vec![]);
|
||||
let iter = std::mem::take(&mut self.attr_var_list_locs);
|
||||
|
||||
for (threshold, list_loc) in iter {
|
||||
self.target[threshold] = list_loc_as_cell!(self.target.threshold());
|
||||
self.target.push_attr_var_queue(threshold - 1);
|
||||
self.copy_attr_var_list(list_loc);
|
||||
}
|
||||
}
|
||||
@@ -263,6 +269,7 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
}
|
||||
|
||||
fn copy_var(&mut self, addr: HeapCellValue) {
|
||||
let index = addr.get_value() as usize;
|
||||
let rd = self.target.deref(addr);
|
||||
let ra = self.target.store(rd);
|
||||
|
||||
@@ -271,7 +278,20 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
if h >= self.old_h {
|
||||
*self.value_at_scan() = ra;
|
||||
self.scan += 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::Lis, h) => {
|
||||
if h >= self.old_h && self.target[index].get_mark_bit() {
|
||||
*self.value_at_scan() = heap_loc_as_cell!(
|
||||
if ra.get_forwarding_bit() {
|
||||
h + 1
|
||||
} else {
|
||||
h
|
||||
}
|
||||
);
|
||||
|
||||
self.scan += 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -356,12 +376,16 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
}
|
||||
}
|
||||
|
||||
fn unwind_trail(&mut self) {
|
||||
for (r, value) in self.trail.drain(0..) {
|
||||
fn unwind_trail(mut self) {
|
||||
for (r, value) in self.trail {
|
||||
let index = r.get_value() as usize;
|
||||
|
||||
match r.get_tag() {
|
||||
RefTag::AttrVar | RefTag::HeapCell => self.target[index] = value,
|
||||
RefTag::AttrVar | RefTag::HeapCell => {
|
||||
self.target[index] = value;
|
||||
self.target[index].set_mark_bit(false);
|
||||
self.target[index].set_forwarding_bit(false);
|
||||
}
|
||||
RefTag::StackCell => self.target.stack()[index] = value,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ impl<'a, const STOP_AT_CYCLES: bool> CycleDetectingIter<'a, STOP_AT_CYCLES> {
|
||||
fn traverse_subterm(&mut self, h: usize, arity: usize) -> Option<usize> {
|
||||
let mut last_cell_loc = h + arity - 1;
|
||||
|
||||
for idx in (h .. h + arity).rev() {
|
||||
for idx in (h..h + arity).rev() {
|
||||
if self.heap[idx].get_forwarding_bit() {
|
||||
if self.cycle_detection_active() {
|
||||
self.cycle_found = true;
|
||||
@@ -93,8 +93,8 @@ impl<'a, const STOP_AT_CYCLES: bool> CycleDetectingIter<'a, STOP_AT_CYCLES> {
|
||||
|
||||
#[inline]
|
||||
fn continue_forwarding(&self) -> bool {
|
||||
self.heap[self.current].get_mark_bit() != self.mark_phase ||
|
||||
self.heap[self.current].get_forwarding_bit()
|
||||
self.heap[self.current].get_mark_bit() != self.mark_phase
|
||||
|| self.heap[self.current].get_forwarding_bit()
|
||||
}
|
||||
|
||||
fn forward(&mut self) -> Option<HeapCellValue> {
|
||||
@@ -150,7 +150,7 @@ impl<'a, const STOP_AT_CYCLES: bool> CycleDetectingIter<'a, STOP_AT_CYCLES> {
|
||||
}
|
||||
|
||||
if self.cycle_detection_active() {
|
||||
for idx in (h + 1 .. last_cell_loc).rev() {
|
||||
for idx in (h + 1..last_cell_loc).rev() {
|
||||
if self.heap[idx].get_forwarding_bit() {
|
||||
self.cycle_found = true;
|
||||
return None;
|
||||
@@ -176,7 +176,7 @@ impl<'a, const STOP_AT_CYCLES: bool> CycleDetectingIter<'a, STOP_AT_CYCLES> {
|
||||
};
|
||||
|
||||
if self.cycle_detection_active() {
|
||||
for idx in (self.next as usize .. last_cell_loc).rev() {
|
||||
for idx in (self.next as usize..last_cell_loc).rev() {
|
||||
if self.heap[idx].get_forwarding_bit() {
|
||||
self.cycle_found = true;
|
||||
return None;
|
||||
@@ -309,19 +309,19 @@ impl<'a, const STOP_AT_CYCLES: bool> CycleDetectingIter<'a, STOP_AT_CYCLES> {
|
||||
HeapCellValueTag::Str => {
|
||||
let mut new_str_back_link = self.current;
|
||||
|
||||
for idx in (0 .. self.current).rev() {
|
||||
if self.heap[idx].get_tag() == HeapCellValueTag::Atom {
|
||||
if cell_as_atom_cell!(self.heap[idx]).get_arity() > 0 {
|
||||
new_str_back_link = idx;
|
||||
break;
|
||||
}
|
||||
for idx in (0..self.current).rev() {
|
||||
if self.heap[idx].get_tag() == HeapCellValueTag::Atom
|
||||
&& cell_as_atom_cell!(self.heap[idx]).get_arity() > 0
|
||||
{
|
||||
new_str_back_link = idx;
|
||||
break;
|
||||
}
|
||||
|
||||
if self.heap[idx].get_mark_bit() != self.mark_phase {
|
||||
if !self.heap[idx].get_forwarding_bit() {
|
||||
new_str_back_link = idx;
|
||||
break;
|
||||
}
|
||||
if self.heap[idx].get_mark_bit() != self.mark_phase
|
||||
&& !self.heap[idx].get_forwarding_bit()
|
||||
{
|
||||
new_str_back_link = idx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -402,7 +402,7 @@ impl<'a, const STOP_AT_CYCLES: bool> CycleDetectingIter<'a, STOP_AT_CYCLES> {
|
||||
self.next = self.heap[self.start].get_value();
|
||||
self.current = self.start;
|
||||
|
||||
while let Some(_) = self.forward() {}
|
||||
while self.forward().is_some() {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -415,7 +415,6 @@ impl<'a, const STOP_AT_CYCLES: bool> Iterator for CycleDetectingIter<'a, STOP_AT
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<'a, const STOP_AT_CYCLES: bool> Drop for CycleDetectingIter<'a, STOP_AT_CYCLES> {
|
||||
fn drop(&mut self) {
|
||||
self.invert_marker();
|
||||
|
||||
@@ -95,11 +95,6 @@ pub struct ChunkInfo {
|
||||
vars: Vec<VarInfo>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BranchArm {
|
||||
pub arm_terms: Vec<QueryTerm>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct BranchInfo {
|
||||
branch_num: BranchNumber,
|
||||
@@ -226,7 +221,7 @@ fn merge_branch_seq(branches: impl Iterator<Item = BranchInfo>) -> BranchInfo {
|
||||
|
||||
for mut branch in branches {
|
||||
branch_info.branch_num = branch.branch_num;
|
||||
branch_info.chunks.extend(branch.chunks.drain(..));
|
||||
branch_info.chunks.append(&mut branch.chunks);
|
||||
}
|
||||
|
||||
branch_info.branch_num.delta = branch_info.branch_num.delta * Integer::from(2);
|
||||
@@ -298,7 +293,7 @@ impl VariableClassifier {
|
||||
|
||||
fn merge_branches(&mut self) {
|
||||
for branches in self.branch_map.values_mut() {
|
||||
let mut old_branches = std::mem::replace(branches, vec![]);
|
||||
let mut old_branches = std::mem::take(branches);
|
||||
|
||||
while let Some(last_branch_num) = old_branches.last().map(|bi| &bi.branch_num) {
|
||||
let mut old_branches_len = old_branches.len();
|
||||
@@ -361,10 +356,7 @@ impl VariableClassifier {
|
||||
.current_chunk_type
|
||||
.to_gen_context(self.current_chunk_num);
|
||||
|
||||
let branch_info_v = self
|
||||
.branch_map
|
||||
.entry(var_info.var_ptr.clone())
|
||||
.or_insert_with(|| vec![]);
|
||||
let branch_info_v = self.branch_map.entry(var_info.var_ptr.clone()).or_default();
|
||||
|
||||
let needs_new_branch = if let Some(last_bi) = branch_info_v.last() {
|
||||
!self.root_set.contains(&last_bi.branch_num)
|
||||
@@ -420,56 +412,49 @@ impl VariableClassifier {
|
||||
arity: term.arity(),
|
||||
};
|
||||
|
||||
match term {
|
||||
Term::Clause(_, _, terms) => {
|
||||
for term in terms.into_iter() {
|
||||
for term_ref in breadth_first_iter(term, RootIterationPolicy::Iterated) {
|
||||
if let TermRef::Var(lvl, _, var_ptr) = term_ref {
|
||||
// a body term, so we need the child level here.
|
||||
let lvl = lvl.child_level();
|
||||
if let Term::Clause(_, _, terms) = term {
|
||||
for term in terms.iter() {
|
||||
for term_ref in breadth_first_iter(term, RootIterationPolicy::Iterated) {
|
||||
if let TermRef::Var(lvl, _, var_ptr) = term_ref {
|
||||
// a body term, so we need the child level here.
|
||||
let lvl = lvl.child_level();
|
||||
|
||||
// the body of the if let here is an inlined
|
||||
// "probe_head_var". note the difference between it
|
||||
// and "probe_body_var".
|
||||
let branch_info_v = self
|
||||
.branch_map
|
||||
.entry(var_ptr.clone())
|
||||
.or_insert_with(|| vec![]);
|
||||
// the body of the if let here is an inlined
|
||||
// "probe_head_var". note the difference between it
|
||||
// and "probe_body_var".
|
||||
let branch_info_v = self.branch_map.entry(var_ptr.clone()).or_default();
|
||||
|
||||
let needs_new_branch = branch_info_v.is_empty();
|
||||
let needs_new_branch = branch_info_v.is_empty();
|
||||
|
||||
if needs_new_branch {
|
||||
branch_info_v
|
||||
.push(BranchInfo::new(self.current_branch_num.clone()));
|
||||
}
|
||||
|
||||
let branch_info = branch_info_v.last_mut().unwrap();
|
||||
let needs_new_chunk = branch_info.chunks.is_empty();
|
||||
|
||||
if needs_new_chunk {
|
||||
branch_info.chunks.push(ChunkInfo {
|
||||
chunk_num: self.current_chunk_num,
|
||||
term_loc: GenContext::Head,
|
||||
vars: vec![],
|
||||
});
|
||||
}
|
||||
|
||||
let chunk_info = branch_info.chunks.last_mut().unwrap();
|
||||
let var_info = VarInfo {
|
||||
var_ptr,
|
||||
classify_info,
|
||||
chunk_type: self.current_chunk_type,
|
||||
lvl,
|
||||
};
|
||||
|
||||
chunk_info.vars.push(var_info);
|
||||
if needs_new_branch {
|
||||
branch_info_v.push(BranchInfo::new(self.current_branch_num.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
classify_info.arg_c += 1;
|
||||
let branch_info = branch_info_v.last_mut().unwrap();
|
||||
let needs_new_chunk = branch_info.chunks.is_empty();
|
||||
|
||||
if needs_new_chunk {
|
||||
branch_info.chunks.push(ChunkInfo {
|
||||
chunk_num: self.current_chunk_num,
|
||||
term_loc: GenContext::Head,
|
||||
vars: vec![],
|
||||
});
|
||||
}
|
||||
|
||||
let chunk_info = branch_info.chunks.last_mut().unwrap();
|
||||
let var_info = VarInfo {
|
||||
var_ptr,
|
||||
classify_info,
|
||||
chunk_type: self.current_chunk_type,
|
||||
lvl,
|
||||
};
|
||||
|
||||
chunk_info.vars.push(var_info);
|
||||
}
|
||||
}
|
||||
|
||||
classify_info.arg_c += 1;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -538,7 +523,10 @@ impl VariableClassifier {
|
||||
build_stack.push_chunk_term(if is_global {
|
||||
QueryTerm::GlobalCut(var_num)
|
||||
} else {
|
||||
QueryTerm::LocalCut { var_num, cut_prev: false }
|
||||
QueryTerm::LocalCut {
|
||||
var_num,
|
||||
cut_prev: false,
|
||||
}
|
||||
});
|
||||
}
|
||||
TraversalState::CutPrev(var_num) => {
|
||||
@@ -548,7 +536,10 @@ impl VariableClassifier {
|
||||
|
||||
self.probe_in_situ_var(var_num);
|
||||
|
||||
build_stack.push_chunk_term(QueryTerm::LocalCut { var_num, cut_prev: true });
|
||||
build_stack.push_chunk_term(QueryTerm::LocalCut {
|
||||
var_num,
|
||||
cut_prev: true,
|
||||
});
|
||||
}
|
||||
TraversalState::Fail => {
|
||||
build_stack.push_chunk_term(QueryTerm::Fail);
|
||||
@@ -667,12 +658,16 @@ impl VariableClassifier {
|
||||
state_stack.last(),
|
||||
Some(TraversalState::RemoveBranchNum)
|
||||
) {
|
||||
// check if the second-to-last element is a regular BuildDisjunct, as we don't
|
||||
// want to add GetPrevLevel in case of a TrustMe.
|
||||
matches!(
|
||||
state_stack.iter().rev().nth(1),
|
||||
Some(TraversalState::BuildDisjunct(..))
|
||||
)
|
||||
// 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
|
||||
};
|
||||
@@ -705,7 +700,9 @@ impl VariableClassifier {
|
||||
state_stack.push(TraversalState::BuildDisjunct(build_stack_len));
|
||||
state_stack.push(TraversalState::Fail);
|
||||
state_stack.push(TraversalState::CutPrev(self.var_num));
|
||||
state_stack.push(TraversalState::ResetGlobalCutVarOverride(self.global_cut_var_num_override));
|
||||
state_stack.push(TraversalState::ResetGlobalCutVarOverride(
|
||||
self.global_cut_var_num_override,
|
||||
));
|
||||
state_stack.push(TraversalState::Term(not_term));
|
||||
state_stack.push(TraversalState::OverrideGlobalCutVar(self.var_num));
|
||||
state_stack.push(TraversalState::GetCutPoint {
|
||||
|
||||
@@ -7,8 +7,6 @@ use crate::machine::machine_state::*;
|
||||
use crate::machine::*;
|
||||
use crate::types::*;
|
||||
|
||||
use crate::try_numeric_result;
|
||||
|
||||
use fxhash::FxBuildHasher;
|
||||
|
||||
macro_rules! step_or_fail {
|
||||
@@ -36,7 +34,7 @@ macro_rules! try_or_throw {
|
||||
|
||||
macro_rules! increment_call_count {
|
||||
($s:expr) => {{
|
||||
if !($s.increment_call_count_fn)(&mut $s) {
|
||||
if !$s.increment_call_count() {
|
||||
$s.backtrack();
|
||||
continue;
|
||||
}
|
||||
@@ -208,6 +206,7 @@ impl MachineState {
|
||||
l
|
||||
}
|
||||
(HeapCellValueTag::Fixnum |
|
||||
HeapCellValueTag::CutPoint |
|
||||
HeapCellValueTag::Char |
|
||||
HeapCellValueTag::F64) => {
|
||||
c
|
||||
@@ -268,7 +267,16 @@ impl MachineState {
|
||||
Literal::Rational(r)
|
||||
}
|
||||
(ArenaHeaderTag::Integer, n) => {
|
||||
Literal::Integer(n)
|
||||
let result = (&*n).try_into();
|
||||
|
||||
match result {
|
||||
Ok(fixnum) => if let Ok(n) = Fixnum::build_with_checked(fixnum) {
|
||||
Literal::Fixnum(n)
|
||||
} else {
|
||||
Literal::Integer(n)
|
||||
},
|
||||
Err(_) => Literal::Integer(n)
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
@@ -313,8 +321,8 @@ impl MachineState {
|
||||
impl Machine {
|
||||
pub(super) fn find_living_dynamic_else(&self, mut p: usize) -> Option<(usize, usize)> {
|
||||
loop {
|
||||
match &self.code[p] {
|
||||
&Instruction::DynamicElse(birth, death, NextOrFail::Next(i)) => {
|
||||
match self.code[p] {
|
||||
Instruction::DynamicElse(birth, death, NextOrFail::Next(i)) => {
|
||||
if birth < self.machine_st.cc && Death::Finite(self.machine_st.cc) <= death {
|
||||
return Some((p, i));
|
||||
} else if i > 0 {
|
||||
@@ -323,14 +331,14 @@ impl Machine {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
&Instruction::DynamicElse(birth, death, NextOrFail::Fail(_)) => {
|
||||
Instruction::DynamicElse(birth, death, NextOrFail::Fail(_)) => {
|
||||
if birth < self.machine_st.cc && Death::Finite(self.machine_st.cc) <= death {
|
||||
return Some((p, 0));
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
&Instruction::DynamicInternalElse(birth, death, NextOrFail::Next(i)) => {
|
||||
Instruction::DynamicInternalElse(birth, death, NextOrFail::Next(i)) => {
|
||||
if birth < self.machine_st.cc && Death::Finite(self.machine_st.cc) <= death {
|
||||
return Some((p, i));
|
||||
} else if i > 0 {
|
||||
@@ -339,14 +347,14 @@ impl Machine {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
&Instruction::DynamicInternalElse(birth, death, NextOrFail::Fail(_)) => {
|
||||
Instruction::DynamicInternalElse(birth, death, NextOrFail::Fail(_)) => {
|
||||
if birth < self.machine_st.cc && Death::Finite(self.machine_st.cc) <= death {
|
||||
return Some((p, 0));
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
&Instruction::RevJmpBy(i) => {
|
||||
Instruction::RevJmpBy(i) => {
|
||||
p -= i;
|
||||
}
|
||||
_ => {
|
||||
@@ -395,25 +403,14 @@ impl Machine {
|
||||
fn execute_switch_on_term(&mut self) {
|
||||
#[inline(always)]
|
||||
fn dynamic_external_of_clause_is_valid(machine: &mut Machine, p: usize) -> bool {
|
||||
match &machine.code[p] {
|
||||
Instruction::DynamicInternalElse(..) => {
|
||||
machine.machine_st.dynamic_mode = FirstOrNext::First;
|
||||
return true;
|
||||
}
|
||||
_ => {}
|
||||
if let Instruction::DynamicInternalElse(..) = machine.code[p] {
|
||||
machine.machine_st.dynamic_mode = FirstOrNext::First;
|
||||
return true;
|
||||
}
|
||||
|
||||
match &machine.code[p - 1] {
|
||||
&Instruction::DynamicInternalElse(birth, death, _) => {
|
||||
if birth < machine.machine_st.cc
|
||||
&& Death::Finite(machine.machine_st.cc) <= death
|
||||
{
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
if let Instruction::DynamicInternalElse(birth, death, _) = machine.code[p - 1] {
|
||||
return birth < machine.machine_st.cc
|
||||
&& Death::Finite(machine.machine_st.cc) <= death;
|
||||
}
|
||||
|
||||
true
|
||||
@@ -567,20 +564,29 @@ impl Machine {
|
||||
}
|
||||
|
||||
let mut p = self.machine_st.p;
|
||||
let mut arity = 0;
|
||||
|
||||
while self.code[p].is_head_instr() {
|
||||
for r in self.code[p].registers() {
|
||||
if let RegType::Temp(t) = r {
|
||||
arity = std::cmp::max(arity, t);
|
||||
}
|
||||
}
|
||||
|
||||
p += 1;
|
||||
}
|
||||
|
||||
let instr =
|
||||
std::mem::replace(&mut self.code[p], Instruction::VerifyAttrInterrupt);
|
||||
let instr = std::mem::replace(
|
||||
&mut self.code[p],
|
||||
Instruction::VerifyAttrInterrupt(arity),
|
||||
);
|
||||
|
||||
self.code[VERIFY_ATTR_INTERRUPT_LOC] = instr;
|
||||
self.machine_st.attr_var_init.cp = p;
|
||||
}
|
||||
&Instruction::VerifyAttrInterrupt => {
|
||||
let (_, arity) = self.code[VERIFY_ATTR_INTERRUPT_LOC].to_name_and_arity();
|
||||
let arity = std::cmp::max(arity, self.machine_st.num_of_args);
|
||||
&Instruction::VerifyAttrInterrupt(arity) => {
|
||||
// let (_, arity) = self.code[VERIFY_ATTR_INTERRUPT_LOC].to_name_and_arity();
|
||||
// let arity = std::cmp::max(arity, self.machine_st.num_of_args);
|
||||
self.run_verify_attr_interrupt(arity);
|
||||
}
|
||||
&Instruction::Add(ref a1, ref a2, t) => {
|
||||
@@ -1896,7 +1902,7 @@ impl Machine {
|
||||
self.machine_st.backtrack();
|
||||
}
|
||||
}
|
||||
&Instruction::CallNumberLessThanOrEqual(ref at_1, ref at_2) => {
|
||||
Instruction::CallNumberLessThanOrEqual(ref at_1, ref at_2) => {
|
||||
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
|
||||
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
|
||||
|
||||
@@ -1910,7 +1916,7 @@ impl Machine {
|
||||
}
|
||||
}
|
||||
}
|
||||
&Instruction::ExecuteNumberLessThanOrEqual(ref at_1, ref at_2) => {
|
||||
Instruction::ExecuteNumberLessThanOrEqual(ref at_1, ref at_2) => {
|
||||
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
|
||||
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
|
||||
|
||||
@@ -1924,7 +1930,7 @@ impl Machine {
|
||||
}
|
||||
}
|
||||
}
|
||||
&Instruction::CallNumberEqual(ref at_1, ref at_2) => {
|
||||
Instruction::CallNumberEqual(ref at_1, ref at_2) => {
|
||||
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
|
||||
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
|
||||
|
||||
@@ -1938,7 +1944,7 @@ impl Machine {
|
||||
}
|
||||
}
|
||||
}
|
||||
&Instruction::ExecuteNumberEqual(ref at_1, ref at_2) => {
|
||||
Instruction::ExecuteNumberEqual(ref at_1, ref at_2) => {
|
||||
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
|
||||
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
|
||||
|
||||
@@ -1952,7 +1958,7 @@ impl Machine {
|
||||
}
|
||||
}
|
||||
}
|
||||
&Instruction::CallNumberNotEqual(ref at_1, ref at_2) => {
|
||||
Instruction::CallNumberNotEqual(ref at_1, ref at_2) => {
|
||||
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
|
||||
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
|
||||
|
||||
@@ -1966,7 +1972,7 @@ impl Machine {
|
||||
}
|
||||
}
|
||||
}
|
||||
&Instruction::ExecuteNumberNotEqual(ref at_1, ref at_2) => {
|
||||
Instruction::ExecuteNumberNotEqual(ref at_1, ref at_2) => {
|
||||
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
|
||||
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
|
||||
|
||||
@@ -1980,7 +1986,7 @@ impl Machine {
|
||||
}
|
||||
}
|
||||
}
|
||||
&Instruction::CallNumberGreaterThanOrEqual(ref at_1, ref at_2) => {
|
||||
Instruction::CallNumberGreaterThanOrEqual(ref at_1, ref at_2) => {
|
||||
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
|
||||
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
|
||||
|
||||
@@ -1994,7 +2000,7 @@ impl Machine {
|
||||
}
|
||||
}
|
||||
}
|
||||
&Instruction::ExecuteNumberGreaterThanOrEqual(ref at_1, ref at_2) => {
|
||||
Instruction::ExecuteNumberGreaterThanOrEqual(ref at_1, ref at_2) => {
|
||||
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
|
||||
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
|
||||
|
||||
@@ -2008,7 +2014,7 @@ impl Machine {
|
||||
}
|
||||
}
|
||||
}
|
||||
&Instruction::CallNumberGreaterThan(ref at_1, ref at_2) => {
|
||||
Instruction::CallNumberGreaterThan(ref at_1, ref at_2) => {
|
||||
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
|
||||
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
|
||||
|
||||
@@ -2022,7 +2028,7 @@ impl Machine {
|
||||
}
|
||||
}
|
||||
}
|
||||
&Instruction::ExecuteNumberGreaterThan(ref at_1, ref at_2) => {
|
||||
Instruction::ExecuteNumberGreaterThan(ref at_1, ref at_2) => {
|
||||
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
|
||||
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
|
||||
|
||||
@@ -2036,7 +2042,7 @@ impl Machine {
|
||||
}
|
||||
}
|
||||
}
|
||||
&Instruction::CallNumberLessThan(ref at_1, ref at_2) => {
|
||||
Instruction::CallNumberLessThan(ref at_1, ref at_2) => {
|
||||
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
|
||||
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
|
||||
|
||||
@@ -2050,7 +2056,7 @@ impl Machine {
|
||||
}
|
||||
}
|
||||
}
|
||||
&Instruction::ExecuteNumberLessThan(ref at_1, ref at_2) => {
|
||||
Instruction::ExecuteNumberLessThan(ref at_1, ref at_2) => {
|
||||
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
|
||||
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
|
||||
|
||||
@@ -2064,7 +2070,7 @@ impl Machine {
|
||||
}
|
||||
}
|
||||
}
|
||||
&Instruction::DefaultCallNumberLessThanOrEqual(ref at_1, ref at_2) => {
|
||||
Instruction::DefaultCallNumberLessThanOrEqual(ref at_1, ref at_2) => {
|
||||
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
|
||||
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
|
||||
|
||||
@@ -2077,7 +2083,7 @@ impl Machine {
|
||||
}
|
||||
}
|
||||
}
|
||||
&Instruction::DefaultExecuteNumberLessThanOrEqual(ref at_1, ref at_2) => {
|
||||
Instruction::DefaultExecuteNumberLessThanOrEqual(ref at_1, ref at_2) => {
|
||||
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
|
||||
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
|
||||
|
||||
@@ -2090,7 +2096,7 @@ impl Machine {
|
||||
}
|
||||
}
|
||||
}
|
||||
&Instruction::DefaultCallNumberNotEqual(ref at_1, ref at_2) => {
|
||||
Instruction::DefaultCallNumberNotEqual(ref at_1, ref at_2) => {
|
||||
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
|
||||
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
|
||||
|
||||
@@ -2103,7 +2109,7 @@ impl Machine {
|
||||
}
|
||||
}
|
||||
}
|
||||
&Instruction::DefaultExecuteNumberNotEqual(ref at_1, ref at_2) => {
|
||||
Instruction::DefaultExecuteNumberNotEqual(ref at_1, ref at_2) => {
|
||||
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
|
||||
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
|
||||
|
||||
@@ -2116,7 +2122,7 @@ impl Machine {
|
||||
}
|
||||
}
|
||||
}
|
||||
&Instruction::DefaultCallNumberEqual(ref at_1, ref at_2) => {
|
||||
Instruction::DefaultCallNumberEqual(ref at_1, ref at_2) => {
|
||||
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
|
||||
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
|
||||
|
||||
@@ -2129,7 +2135,7 @@ impl Machine {
|
||||
}
|
||||
}
|
||||
}
|
||||
&Instruction::DefaultExecuteNumberEqual(ref at_1, ref at_2) => {
|
||||
Instruction::DefaultExecuteNumberEqual(ref at_1, ref at_2) => {
|
||||
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
|
||||
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
|
||||
|
||||
@@ -2142,7 +2148,7 @@ impl Machine {
|
||||
}
|
||||
}
|
||||
}
|
||||
&Instruction::DefaultCallNumberGreaterThanOrEqual(ref at_1, ref at_2) => {
|
||||
Instruction::DefaultCallNumberGreaterThanOrEqual(ref at_1, ref at_2) => {
|
||||
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
|
||||
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
|
||||
|
||||
@@ -2155,7 +2161,7 @@ impl Machine {
|
||||
}
|
||||
}
|
||||
}
|
||||
&Instruction::DefaultExecuteNumberGreaterThanOrEqual(ref at_1, ref at_2) => {
|
||||
Instruction::DefaultExecuteNumberGreaterThanOrEqual(ref at_1, ref at_2) => {
|
||||
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
|
||||
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
|
||||
|
||||
@@ -2168,7 +2174,7 @@ impl Machine {
|
||||
}
|
||||
}
|
||||
}
|
||||
&Instruction::DefaultCallNumberGreaterThan(ref at_1, ref at_2) => {
|
||||
Instruction::DefaultCallNumberGreaterThan(ref at_1, ref at_2) => {
|
||||
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
|
||||
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
|
||||
|
||||
@@ -2181,7 +2187,7 @@ impl Machine {
|
||||
}
|
||||
}
|
||||
}
|
||||
&Instruction::DefaultExecuteNumberGreaterThan(ref at_1, ref at_2) => {
|
||||
Instruction::DefaultExecuteNumberGreaterThan(ref at_1, ref at_2) => {
|
||||
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
|
||||
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
|
||||
|
||||
@@ -2194,7 +2200,7 @@ impl Machine {
|
||||
}
|
||||
}
|
||||
}
|
||||
&Instruction::DefaultCallNumberLessThan(ref at_1, ref at_2) => {
|
||||
Instruction::DefaultCallNumberLessThan(ref at_1, ref at_2) => {
|
||||
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
|
||||
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
|
||||
|
||||
@@ -2207,7 +2213,7 @@ impl Machine {
|
||||
}
|
||||
}
|
||||
}
|
||||
&Instruction::DefaultExecuteNumberLessThan(ref at_1, ref at_2) => {
|
||||
Instruction::DefaultExecuteNumberLessThan(ref at_1, ref at_2) => {
|
||||
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
|
||||
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
|
||||
|
||||
@@ -2955,7 +2961,7 @@ impl Machine {
|
||||
|
||||
self.machine_st.p += 1;
|
||||
}
|
||||
&Instruction::IndexingCode(ref indexing_lines) => {
|
||||
Instruction::IndexingCode(ref indexing_lines) => {
|
||||
match &indexing_lines[self.machine_st.oip as usize] {
|
||||
IndexingLine::Indexing(_) => {
|
||||
self.execute_switch_on_term();
|
||||
@@ -2965,22 +2971,22 @@ impl Machine {
|
||||
}
|
||||
}
|
||||
IndexingLine::IndexedChoice(ref indexed_choice) => {
|
||||
match &indexed_choice[self.machine_st.iip as usize] {
|
||||
&IndexedChoiceInstruction::Try(offset) => {
|
||||
match indexed_choice[self.machine_st.iip as usize] {
|
||||
IndexedChoiceInstruction::Try(offset) => {
|
||||
self.indexed_try(offset);
|
||||
}
|
||||
&IndexedChoiceInstruction::Retry(l) => {
|
||||
IndexedChoiceInstruction::Retry(l) => {
|
||||
self.retry(l);
|
||||
increment_call_count!(self.machine_st);
|
||||
}
|
||||
&IndexedChoiceInstruction::DefaultRetry(l) => {
|
||||
IndexedChoiceInstruction::DefaultRetry(l) => {
|
||||
self.retry(l);
|
||||
}
|
||||
&IndexedChoiceInstruction::Trust(l) => {
|
||||
IndexedChoiceInstruction::Trust(l) => {
|
||||
self.trust(l);
|
||||
increment_call_count!(self.machine_st);
|
||||
}
|
||||
&IndexedChoiceInstruction::DefaultTrust(l) => {
|
||||
IndexedChoiceInstruction::DefaultTrust(l) => {
|
||||
self.trust(l);
|
||||
}
|
||||
}
|
||||
@@ -3686,6 +3692,16 @@ impl Machine {
|
||||
try_or_throw!(self.machine_st, self.install_inference_counter());
|
||||
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
|
||||
}
|
||||
&Instruction::CallInferenceCount => {
|
||||
let global_count = self.machine_st.cwil.global_count.clone();
|
||||
self.inference_count(self.machine_st.registers[1], global_count);
|
||||
step_or_fail!(self, self.machine_st.p += 1);
|
||||
}
|
||||
&Instruction::ExecuteInferenceCount => {
|
||||
let global_count = self.machine_st.cwil.global_count.clone();
|
||||
self.inference_count(self.machine_st.registers[1], global_count);
|
||||
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
|
||||
}
|
||||
&Instruction::CallLiftedHeapLength => {
|
||||
self.lifted_heap_length();
|
||||
step_or_fail!(self, self.machine_st.p += 1);
|
||||
@@ -4139,6 +4155,22 @@ impl Machine {
|
||||
try_or_throw!(self.machine_st, self.define_foreign_struct());
|
||||
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
|
||||
}
|
||||
&Instruction::CallJsEval => {
|
||||
try_or_throw!(self.machine_st, self.js_eval());
|
||||
step_or_fail!(self, self.machine_st.p += 1);
|
||||
}
|
||||
&Instruction::ExecuteJsEval => {
|
||||
try_or_throw!(self.machine_st, self.js_eval());
|
||||
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
|
||||
}
|
||||
&Instruction::CallArgv => {
|
||||
try_or_throw!(self.machine_st, self.argv());
|
||||
step_or_fail!(self, self.machine_st.p += 1);
|
||||
}
|
||||
&Instruction::ExecuteArgv => {
|
||||
try_or_throw!(self.machine_st, self.argv());
|
||||
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
|
||||
}
|
||||
&Instruction::CallCurrentTime => {
|
||||
self.current_time();
|
||||
step_or_fail!(self, self.machine_st.p += 1);
|
||||
@@ -4439,6 +4471,14 @@ impl Machine {
|
||||
self.crypto_data_hash();
|
||||
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
|
||||
}
|
||||
&Instruction::CallCryptoHMAC => {
|
||||
self.crypto_hmac();
|
||||
step_or_fail!(self, self.machine_st.p += 1);
|
||||
}
|
||||
&Instruction::ExecuteCryptoHMAC => {
|
||||
self.crypto_hmac();
|
||||
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
|
||||
}
|
||||
&Instruction::CallCryptoDataHKDF => {
|
||||
self.crypto_data_hkdf();
|
||||
step_or_fail!(self, self.machine_st.p += 1);
|
||||
@@ -4483,44 +4523,28 @@ impl Machine {
|
||||
self.crypto_curve_scalar_mult();
|
||||
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
|
||||
}
|
||||
#[cfg(feature = "crypto-full")]
|
||||
&Instruction::CallEd25519Sign => {
|
||||
self.ed25519_sign();
|
||||
&Instruction::CallEd25519SignRaw => {
|
||||
self.ed25519_sign_raw();
|
||||
step_or_fail!(self, self.machine_st.p += 1);
|
||||
}
|
||||
#[cfg(feature = "crypto-full")]
|
||||
&Instruction::ExecuteEd25519Sign => {
|
||||
self.ed25519_sign();
|
||||
&Instruction::ExecuteEd25519SignRaw => {
|
||||
self.ed25519_sign_raw();
|
||||
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
|
||||
}
|
||||
#[cfg(feature = "crypto-full")]
|
||||
&Instruction::CallEd25519Verify => {
|
||||
self.ed25519_verify();
|
||||
&Instruction::CallEd25519VerifyRaw => {
|
||||
self.ed25519_verify_raw();
|
||||
step_or_fail!(self, self.machine_st.p += 1);
|
||||
}
|
||||
#[cfg(feature = "crypto-full")]
|
||||
&Instruction::ExecuteEd25519Verify => {
|
||||
self.ed25519_verify();
|
||||
&Instruction::ExecuteEd25519VerifyRaw => {
|
||||
self.ed25519_verify_raw();
|
||||
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
|
||||
}
|
||||
#[cfg(feature = "crypto-full")]
|
||||
&Instruction::CallEd25519NewKeyPair => {
|
||||
self.ed25519_new_key_pair();
|
||||
&Instruction::CallEd25519SeedToPublicKey => {
|
||||
self.ed25519_seed_to_public_key();
|
||||
step_or_fail!(self, self.machine_st.p += 1);
|
||||
}
|
||||
#[cfg(feature = "crypto-full")]
|
||||
&Instruction::ExecuteEd25519NewKeyPair => {
|
||||
self.ed25519_new_key_pair();
|
||||
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
|
||||
}
|
||||
#[cfg(feature = "crypto-full")]
|
||||
&Instruction::CallEd25519KeyPairPublicKey => {
|
||||
self.ed25519_key_pair_public_key();
|
||||
step_or_fail!(self, self.machine_st.p += 1);
|
||||
}
|
||||
#[cfg(feature = "crypto-full")]
|
||||
&Instruction::ExecuteEd25519KeyPairPublicKey => {
|
||||
self.ed25519_key_pair_public_key();
|
||||
&Instruction::ExecuteEd25519SeedToPublicKey => {
|
||||
self.ed25519_seed_to_public_key();
|
||||
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
|
||||
}
|
||||
&Instruction::CallCurve25519ScalarMult => {
|
||||
@@ -4587,11 +4611,11 @@ impl Machine {
|
||||
self.shell();
|
||||
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
|
||||
}
|
||||
&Instruction::CallPID => {
|
||||
&Instruction::CallPid => {
|
||||
self.pid();
|
||||
step_or_fail!(self, self.machine_st.p += 1);
|
||||
}
|
||||
&Instruction::ExecutePID => {
|
||||
&Instruction::ExecutePid => {
|
||||
self.pid();
|
||||
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
|
||||
}
|
||||
@@ -5089,16 +5113,13 @@ impl Machine {
|
||||
.get_predicate_skeleton_mut(&compilation_target, &key)
|
||||
.unwrap();
|
||||
|
||||
match skeleton.target_pos_of_clause_clause_loc(l) {
|
||||
Some(n) => {
|
||||
let r = self
|
||||
.machine_st
|
||||
.store(self.machine_st.deref(self.machine_st.registers[5]));
|
||||
if let Some(n) = skeleton.target_pos_of_clause_clause_loc(l) {
|
||||
let r = self
|
||||
.machine_st
|
||||
.store(self.machine_st.deref(self.machine_st.registers[5]));
|
||||
|
||||
self.machine_st
|
||||
.unify_fixnum(Fixnum::build_with(n as i64), r);
|
||||
}
|
||||
None => {}
|
||||
self.machine_st
|
||||
.unify_fixnum(Fixnum::build_with(n as i64), r);
|
||||
}
|
||||
|
||||
self.machine_st.call_at_index(2, p);
|
||||
@@ -5135,16 +5156,13 @@ impl Machine {
|
||||
.get_predicate_skeleton_mut(&compilation_target, &key)
|
||||
.unwrap();
|
||||
|
||||
match skeleton.target_pos_of_clause_clause_loc(l) {
|
||||
Some(n) => {
|
||||
let r = self
|
||||
.machine_st
|
||||
.store(self.machine_st.deref(self.machine_st.registers[5]));
|
||||
if let Some(n) = skeleton.target_pos_of_clause_clause_loc(l) {
|
||||
let r = self
|
||||
.machine_st
|
||||
.store(self.machine_st.deref(self.machine_st.registers[5]));
|
||||
|
||||
self.machine_st
|
||||
.unify_fixnum(Fixnum::build_with(n as i64), r);
|
||||
}
|
||||
None => {}
|
||||
self.machine_st
|
||||
.unify_fixnum(Fixnum::build_with(n as i64), r);
|
||||
}
|
||||
|
||||
self.machine_st.execute_at_index(2, p);
|
||||
@@ -5226,7 +5244,7 @@ impl Machine {
|
||||
// So we only have access to a runtime handle in here and can't shut it down.
|
||||
// Since I'm not aware of the consequences of deactivating this new code which came in while PR 1880
|
||||
// was not merged, I'm only deactivating it for now.
|
||||
|
||||
|
||||
//#[cfg(not(target_arch = "wasm32"))]
|
||||
//let runtime = tokio::runtime::Runtime::new().unwrap();
|
||||
//#[cfg(target_arch = "wasm32")]
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use crate::atom_table::*;
|
||||
use crate::machine::heap::*;
|
||||
use crate::types::*;
|
||||
@@ -9,14 +11,22 @@ pub(crate) trait UnmarkPolicy {
|
||||
fn forward_attr_var(iter: &mut StacklessPreOrderHeapIter<Self>) -> Option<HeapCellValue>
|
||||
where
|
||||
Self: Sized;
|
||||
fn invert_marker(iter: &mut StacklessPreOrderHeapIter<Self>) where Self: Sized;
|
||||
fn invert_marker(iter: &mut StacklessPreOrderHeapIter<Self>)
|
||||
where
|
||||
Self: Sized;
|
||||
fn mark_phase(&self) -> bool;
|
||||
#[inline]
|
||||
fn report_var_link(iter: &StacklessPreOrderHeapIter<Self>) -> bool where Self: Sized {
|
||||
fn report_var_link(iter: &StacklessPreOrderHeapIter<Self>) -> bool
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
iter.heap[iter.next as usize].get_mark_bit() == iter.iter_state.mark_phase()
|
||||
}
|
||||
#[inline(always)]
|
||||
fn record_focus(_iter: &mut StacklessPreOrderHeapIter<Self>) where Self: Sized {
|
||||
fn record_focus(_iter: &mut StacklessPreOrderHeapIter<Self>)
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +44,7 @@ fn invert_marker<UMP: UnmarkPolicy>(iter: &mut StacklessPreOrderHeapIter<UMP>) {
|
||||
iter.next = iter.heap[iter.start].get_value();
|
||||
iter.current = iter.start;
|
||||
|
||||
while let Some(_) = iter.forward() {}
|
||||
while iter.forward().is_some() {}
|
||||
}
|
||||
|
||||
impl UnmarkPolicy for IteratorUMP {
|
||||
@@ -139,7 +149,7 @@ impl<'a> StacklessPreOrderHeapIter<'a, IteratorUMP> {
|
||||
start,
|
||||
current: start,
|
||||
next,
|
||||
iter_state: IteratorUMP { mark_phase: true,},
|
||||
iter_state: IteratorUMP { mark_phase: true },
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -189,11 +199,9 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> {
|
||||
return Some(cell);
|
||||
}
|
||||
|
||||
if self.next < self.heap.len() as u64 {
|
||||
if UMP::report_var_link(self) {
|
||||
let tag = HeapCellValueTag::AttrVar;
|
||||
return Some(HeapCellValue::build_with(tag, next as u64));
|
||||
}
|
||||
if self.next < self.heap.len() as u64 && UMP::report_var_link(self) {
|
||||
let tag = HeapCellValueTag::AttrVar;
|
||||
return Some(HeapCellValue::build_with(tag, next as u64));
|
||||
}
|
||||
}
|
||||
HeapCellValueTag::Var => {
|
||||
@@ -203,11 +211,9 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> {
|
||||
return Some(cell);
|
||||
}
|
||||
|
||||
if self.next < self.heap.len() as u64 {
|
||||
if UMP::report_var_link(self) {
|
||||
let tag = HeapCellValueTag::Var;
|
||||
return Some(HeapCellValue::build_with(tag, next as u64));
|
||||
}
|
||||
if self.next < self.heap.len() as u64 && UMP::report_var_link(self) {
|
||||
let tag = HeapCellValueTag::Var;
|
||||
return Some(HeapCellValue::build_with(tag, next as u64));
|
||||
}
|
||||
}
|
||||
HeapCellValueTag::Str => {
|
||||
@@ -311,10 +317,8 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> {
|
||||
return Some(self.backward_and_return());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if self.backward() {
|
||||
return None;
|
||||
}
|
||||
} else if self.backward() {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -358,7 +362,7 @@ impl<'a, UMP: UnmarkPolicy> Iterator for StacklessPreOrderHeapIter<'a, UMP> {
|
||||
|
||||
pub fn mark_cells(heap: &mut Heap, start: usize) {
|
||||
let mut iter = StacklessPreOrderHeapIter::<MarkerUMP>::new(heap, start);
|
||||
while let Some(_) = iter.forward() {}
|
||||
while iter.forward().is_some() {}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -665,14 +669,18 @@ mod tests {
|
||||
|
||||
wam.machine_st.heap.push(pstr_loc_as_cell!(1));
|
||||
|
||||
let pstr_var_cell = put_partial_string(&mut wam.machine_st.heap, "abc ", &wam.machine_st.atom_tbl);
|
||||
let pstr_var_cell =
|
||||
put_partial_string(&mut wam.machine_st.heap, "abc ", &wam.machine_st.atom_tbl);
|
||||
let pstr_cell = wam.machine_st.heap[pstr_var_cell.get_value() as usize];
|
||||
|
||||
mark_cells(&mut wam.machine_st.heap, 0);
|
||||
|
||||
all_cells_marked_and_unforwarded(&wam.machine_st.heap);
|
||||
|
||||
assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), pstr_loc_as_cell!(1));
|
||||
assert_eq!(
|
||||
unmark_cell_bits!(wam.machine_st.heap[0]),
|
||||
pstr_loc_as_cell!(1)
|
||||
);
|
||||
assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_cell);
|
||||
assert_eq!(
|
||||
unmark_cell_bits!(wam.machine_st.heap[2]),
|
||||
@@ -720,7 +728,7 @@ mod tests {
|
||||
|
||||
mark_cells(&mut wam.machine_st.heap, 7);
|
||||
|
||||
all_cells_marked_and_unforwarded(&wam.machine_st.heap[1 ..]);
|
||||
all_cells_marked_and_unforwarded(&wam.machine_st.heap[1..]);
|
||||
|
||||
assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_cell);
|
||||
assert_eq!(
|
||||
@@ -1536,10 +1544,10 @@ mod tests {
|
||||
|
||||
mark_cells(&mut wam.machine_st.heap, 0);
|
||||
|
||||
all_cells_marked_and_unforwarded(&mut wam.machine_st.heap[0..24]);
|
||||
all_cells_marked_and_unforwarded(&wam.machine_st.heap[0..24]);
|
||||
|
||||
for cell in &wam.machine_st.heap[24..] {
|
||||
assert_eq!(cell.get_mark_bit(), false);
|
||||
assert!(!cell.get_mark_bit());
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
|
||||
@@ -69,8 +69,8 @@ impl TryFrom<HeapCellValue> for Literal {
|
||||
(ArenaHeaderTag::Rational, n) => {
|
||||
Ok(Literal::Rational(n))
|
||||
}
|
||||
(ArenaHeaderTag::IndexPtr, _ip) => {
|
||||
Ok(Literal::CodeIndex(CodeIndex::from(cons_ptr)))
|
||||
(ArenaHeaderTag::IndexPtr, ip) => {
|
||||
Ok(Literal::CodeIndex(CodeIndex::from(ip)))
|
||||
}
|
||||
_ => {
|
||||
Err(())
|
||||
@@ -169,7 +169,7 @@ pub(crate) fn allocate_pstr(heap: &mut Heap, mut src: &str, atom_tbl: &AtomTable
|
||||
let orig_h = heap.len();
|
||||
|
||||
loop {
|
||||
if src == "" {
|
||||
if src.is_empty() {
|
||||
return if orig_h == heap.len() {
|
||||
None
|
||||
} else {
|
||||
@@ -199,7 +199,7 @@ pub(crate) fn allocate_pstr(heap: &mut Heap, mut src: &str, atom_tbl: &AtomTable
|
||||
|
||||
heap.push(string_as_pstr_cell!(pstr));
|
||||
|
||||
if rest_src != "" {
|
||||
if !rest_src.is_empty() {
|
||||
heap.push(pstr_loc_as_cell!(h + 2));
|
||||
src = rest_src;
|
||||
} else {
|
||||
@@ -249,7 +249,7 @@ pub(crate) fn to_local_code_ptr(heap: &Heap, addr: HeapCellValue) -> Option<usiz
|
||||
Ok(Number::Integer(n)) => {
|
||||
let value: usize = (&*n).try_into().unwrap();
|
||||
Some(value)
|
||||
},
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
};
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,22 +1,147 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::atom_table;
|
||||
use crate::heap_print::{HCPrinter, HCValueOutputter, PrinterOutputter};
|
||||
use crate::machine::{BREAK_FROM_DISPATCH_LOOP_LOC, LIB_QUERY_SUCCESS};
|
||||
use crate::machine::mock_wam::CompositeOpDir;
|
||||
use crate::parser::parser::{Parser, Tokens};
|
||||
use crate::read::write_term_to_heap;
|
||||
use crate::machine::machine_indices::VarKey;
|
||||
use crate::machine::mock_wam::CompositeOpDir;
|
||||
use crate::machine::{BREAK_FROM_DISPATCH_LOOP_LOC, LIB_QUERY_SUCCESS};
|
||||
use crate::parser::ast::{Var, VarPtr};
|
||||
use crate::parser::parser::{Parser, Tokens};
|
||||
use crate::read::{write_term_to_heap, TermWriteResult};
|
||||
use indexmap::IndexMap;
|
||||
|
||||
use super::{
|
||||
Machine, MachineConfig, QueryResult, QueryResolutionLine,
|
||||
Atom, AtomCell, HeapCellValue, HeapCellValueTag, Value, QueryResolution,
|
||||
streams::Stream
|
||||
streams::Stream, Atom, AtomCell, HeapCellValue, HeapCellValueTag, Machine, MachineConfig,
|
||||
QueryResolutionLine, QueryResult, Value,
|
||||
};
|
||||
|
||||
pub struct QueryState<'a> {
|
||||
machine: &'a mut Machine,
|
||||
term: TermWriteResult,
|
||||
stub_b: usize,
|
||||
var_names: IndexMap<HeapCellValue, VarPtr>,
|
||||
called: bool,
|
||||
}
|
||||
|
||||
impl Drop for QueryState<'_> {
|
||||
fn drop(&mut self) {
|
||||
// This may be wrong if the iterator is not fully consumend, but from testing it seems
|
||||
// fine.
|
||||
self.machine.trust_me();
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for QueryState<'_> {
|
||||
type Item = Result<QueryResolutionLine, String>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let var_names = &mut self.var_names;
|
||||
let term_write_result = &self.term;
|
||||
let machine = &mut self.machine;
|
||||
|
||||
// No more choicepoints, end iteration
|
||||
if self.called && machine.machine_st.b <= self.stub_b {
|
||||
return None;
|
||||
}
|
||||
|
||||
machine.dispatch_loop();
|
||||
|
||||
self.called = true;
|
||||
|
||||
if !machine.machine_st.ball.stub.is_empty() {
|
||||
// NOTE: this means an exception was thrown, at which
|
||||
// point we backtracked to the stub choice point.
|
||||
// this should halt the search for solutions as it
|
||||
// does in the Scryer top-level. the exception term is
|
||||
// contained in self.machine_st.ball.
|
||||
let error_string = self
|
||||
.machine
|
||||
.machine_st
|
||||
.ball
|
||||
.stub
|
||||
.iter()
|
||||
.filter(|h| {
|
||||
matches!(
|
||||
h.get_tag(),
|
||||
HeapCellValueTag::Atom | HeapCellValueTag::Fixnum
|
||||
)
|
||||
})
|
||||
.map(|h| match h.get_tag() {
|
||||
HeapCellValueTag::Atom => {
|
||||
let (name, _) = cell_as_atom_cell!(h).get_name_and_arity();
|
||||
name.as_str().to_string()
|
||||
}
|
||||
HeapCellValueTag::Fixnum => h.get_value().clone().to_string(),
|
||||
_ => unreachable!(),
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
.join(" ");
|
||||
|
||||
return Some(Err(error_string));
|
||||
}
|
||||
|
||||
if machine.machine_st.p == LIB_QUERY_SUCCESS {
|
||||
if term_write_result.var_dict.is_empty() {
|
||||
self.machine.machine_st.backtrack();
|
||||
return Some(Ok(QueryResolutionLine::True));
|
||||
}
|
||||
} else if machine.machine_st.p == BREAK_FROM_DISPATCH_LOOP_LOC {
|
||||
return Some(Ok(QueryResolutionLine::False));
|
||||
}
|
||||
|
||||
let mut bindings: BTreeMap<String, Value> = BTreeMap::new();
|
||||
|
||||
let var_dict = &term_write_result.var_dict;
|
||||
|
||||
for (var_key, term_to_be_printed) in var_dict.iter() {
|
||||
let mut var_name = var_key.to_string();
|
||||
if var_name.starts_with('_') {
|
||||
let should_print = var_names.values().any(|x| match x.borrow().clone() {
|
||||
Var::Named(v) => v == var_name,
|
||||
_ => false,
|
||||
});
|
||||
if !should_print {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let mut term =
|
||||
Value::from_heapcell(machine, *term_to_be_printed, &mut var_names.clone());
|
||||
|
||||
if let Value::Var(ref term_str) = term {
|
||||
if *term_str == var_name {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Var dict is in the order things appear in the query. If var_name appears
|
||||
// after term in the query, switch their places.
|
||||
let var_name_idx = var_dict
|
||||
.get_index_of(&VarKey::VarPtr(Var::Named(var_name.clone()).into()))
|
||||
.unwrap();
|
||||
let term_idx =
|
||||
var_dict.get_index_of(&VarKey::VarPtr(Var::Named(term_str.clone()).into()));
|
||||
if let Some(idx) = term_idx {
|
||||
if idx < var_name_idx {
|
||||
let new_term = Value::Var(var_name);
|
||||
let new_var_name = term_str.into();
|
||||
term = new_term;
|
||||
var_name = new_var_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bindings.insert(var_name, term);
|
||||
}
|
||||
|
||||
// NOTE: there are outstanding choicepoints, backtrack
|
||||
// through them for further solutions. if
|
||||
// self.machine_st.b == stub_b we've backtracked to the stub
|
||||
// choice point, so we should break.
|
||||
self.machine.machine_st.backtrack();
|
||||
|
||||
Some(Ok(QueryResolutionLine::Match(bindings)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Machine {
|
||||
pub fn new_lib() -> Self {
|
||||
Machine::new(MachineConfig::in_memory())
|
||||
@@ -30,17 +155,20 @@ impl Machine {
|
||||
pub fn consult_module_string(&mut self, module_name: &str, program: String) {
|
||||
let stream = Stream::from_owned_string(program, &mut self.machine_st.arena);
|
||||
self.machine_st.registers[1] = stream_as_cell!(stream);
|
||||
self.machine_st.registers[2] = atom_as_cell!(&atom_table::AtomTable::build_with(&self.machine_st.atom_tbl, module_name));
|
||||
self.machine_st.registers[2] = atom_as_cell!(&atom_table::AtomTable::build_with(
|
||||
&self.machine_st.atom_tbl,
|
||||
module_name
|
||||
));
|
||||
|
||||
self.run_module_predicate(atom!("loader"), (atom!("consult_stream"), 2));
|
||||
}
|
||||
|
||||
fn allocate_stub_choice_point(&mut self) {
|
||||
// NOTE: create a choice point to terminate the dispatch_loop
|
||||
// if an exception is thrown. since the and/or stack is presumed empty,
|
||||
// if an exception is thrown.
|
||||
|
||||
let stub_b = self.machine_st.stack.allocate_or_frame(0);
|
||||
let or_frame = self.machine_st.stack.index_or_frame_mut(0);
|
||||
let or_frame = self.machine_st.stack.index_or_frame_mut(stub_b);
|
||||
|
||||
or_frame.prelude.num_cells = 0;
|
||||
or_frame.prelude.e = 0;
|
||||
@@ -55,28 +183,34 @@ impl Machine {
|
||||
or_frame.prelude.attr_var_queue_len = 0;
|
||||
|
||||
self.machine_st.b = stub_b;
|
||||
self.machine_st.hb = self.machine_st.heap.len();
|
||||
self.machine_st.block = stub_b;
|
||||
}
|
||||
|
||||
pub fn run_query(&mut self, query: String) -> QueryResult {
|
||||
println!("Query: {}", query);
|
||||
// Parse the query so we can analyze and then call the term
|
||||
self.run_query_iter(query).collect()
|
||||
}
|
||||
|
||||
pub fn run_query_iter(&mut self, query: String) -> QueryState {
|
||||
let mut parser = Parser::new(
|
||||
Stream::from_owned_string(query, &mut self.machine_st.arena),
|
||||
&mut self.machine_st
|
||||
&mut self.machine_st,
|
||||
);
|
||||
let op_dir = CompositeOpDir::new(&self.indices.op_dir, None);
|
||||
let term = parser.read_term(&op_dir, Tokens::Default).expect("Failed to parse query");
|
||||
let term = parser
|
||||
.read_term(&op_dir, Tokens::Default)
|
||||
.expect("Failed to parse query");
|
||||
|
||||
self.allocate_stub_choice_point();
|
||||
|
||||
// Write parsed term to heap
|
||||
let term_write_result = write_term_to_heap(&term, &mut self.machine_st.heap, &mut self.machine_st.atom_tbl).expect("couldn't write term to heap");
|
||||
let term_write_result =
|
||||
write_term_to_heap(&term, &mut self.machine_st.heap, &self.machine_st.atom_tbl)
|
||||
.expect("couldn't write term to heap");
|
||||
|
||||
// Write term to heap
|
||||
self.machine_st.registers[1] = self.machine_st.heap[term_write_result.heap_loc];
|
||||
|
||||
self.machine_st.cp = LIB_QUERY_SUCCESS; // BREAK_FROM_DISPATCH_LOOP_LOC;
|
||||
self.machine_st.p = self.indices.code_dir.get(&(atom!("call"), 1)).expect("couldn't get code index").local().unwrap();
|
||||
|
||||
let var_names: IndexMap<_, _> = term_write_result.var_dict.iter()
|
||||
let var_names: IndexMap<_, _> = term_write_result
|
||||
.var_dict
|
||||
.iter()
|
||||
.map(|(var_key, cell)| match var_key {
|
||||
// NOTE: not the intention behind Var::InSitu here but
|
||||
// we can hijack it to store anonymous variables
|
||||
@@ -86,133 +220,38 @@ impl Machine {
|
||||
})
|
||||
.collect();
|
||||
|
||||
self.allocate_stub_choice_point();
|
||||
// Write term to heap
|
||||
self.machine_st.registers[1] = self.machine_st.heap[term_write_result.heap_loc];
|
||||
|
||||
self.machine_st.cp = LIB_QUERY_SUCCESS; // BREAK_FROM_DISPATCH_LOOP_LOC;
|
||||
let call_index_p = self
|
||||
.indices
|
||||
.code_dir
|
||||
.get(&(atom!("call"), 1))
|
||||
.expect("couldn't get code index")
|
||||
.local()
|
||||
.unwrap();
|
||||
|
||||
self.machine_st.execute_at_index(1, call_index_p);
|
||||
|
||||
let stub_b = self.machine_st.b;
|
||||
|
||||
let mut matches: Vec<QueryResolutionLine> = Vec::new();
|
||||
// Call the term
|
||||
loop {
|
||||
self.dispatch_loop();
|
||||
|
||||
//println!("b: {}", self.machine_st.b);
|
||||
//println!("stub_b: {}", stub_b);
|
||||
//println!("fail: {}", self.machine_st.fail);
|
||||
|
||||
if self.machine_st.ball.stub.len() != 0 {
|
||||
// NOTE: this means an exception was thrown, at which
|
||||
// point we backtracked to the stub choice point.
|
||||
// this should halt the search for solutions as it
|
||||
// does in the Scryer top-level. the exception term is
|
||||
// contained in self.machine_st.ball.
|
||||
let error_string = self.machine_st.ball.stub
|
||||
.iter()
|
||||
.filter(|h| match h.get_tag() {
|
||||
HeapCellValueTag::Atom => true,
|
||||
HeapCellValueTag::Fixnum => true,
|
||||
_ => false,
|
||||
})
|
||||
.map(|h| match h.get_tag() {
|
||||
HeapCellValueTag::Atom => {
|
||||
let (name, _) = cell_as_atom_cell!(h).get_name_and_arity();
|
||||
name.as_str().to_string()
|
||||
}
|
||||
HeapCellValueTag::Fixnum => {
|
||||
h.get_value().clone().to_string()
|
||||
},
|
||||
_ => unreachable!(),
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
.join(" ");
|
||||
|
||||
return Err(error_string);
|
||||
}
|
||||
|
||||
/*
|
||||
if self.machine_st.fail {
|
||||
// NOTE: only print results on success
|
||||
self.machine_st.fail = false;
|
||||
println!("fail!");
|
||||
matches.push(QueryResolutionLine::False);
|
||||
break;
|
||||
};
|
||||
*/
|
||||
|
||||
if term_write_result.var_dict.is_empty() {
|
||||
if self.machine_st.p == LIB_QUERY_SUCCESS {
|
||||
matches.push(QueryResolutionLine::True);
|
||||
break;
|
||||
} else if self.machine_st.p == BREAK_FROM_DISPATCH_LOOP_LOC {
|
||||
// NOTE: only print results on success
|
||||
// self.machine_st.fail = false;
|
||||
// println!("b == stub_b");
|
||||
matches.push(QueryResolutionLine::False);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let mut bindings: BTreeMap<String, Value> = BTreeMap::new();
|
||||
|
||||
for (var_key, term_to_be_printed) in &term_write_result.var_dict {
|
||||
if var_key.to_string().starts_with("_") {
|
||||
continue;
|
||||
}
|
||||
let mut printer = HCPrinter::new(
|
||||
&mut self.machine_st.heap,
|
||||
Arc::clone(&self.machine_st.atom_tbl),
|
||||
&mut self.machine_st.stack,
|
||||
&self.indices.op_dir,
|
||||
PrinterOutputter::new(),
|
||||
*term_to_be_printed,
|
||||
);
|
||||
|
||||
printer.ignore_ops = false;
|
||||
printer.numbervars = true;
|
||||
printer.quoted = true;
|
||||
printer.max_depth = 1000; // NOTE: set this to 0 for unbounded depth
|
||||
printer.double_quotes = true;
|
||||
printer.var_names = var_names.clone();
|
||||
|
||||
let outputter = printer.print();
|
||||
|
||||
let output: String = outputter.result();
|
||||
println!("Result: {} = {}", var_key.to_string(), output);
|
||||
|
||||
bindings.insert(var_key.to_string(), Value::try_from(output).expect("asdfs"));
|
||||
}
|
||||
|
||||
matches.push(QueryResolutionLine::Match(bindings));
|
||||
|
||||
// NOTE: there are outstanding choicepoints, backtrack
|
||||
// through them for further solutions. if
|
||||
// self.machine_st.b == stub_b we've backtracked to the stub
|
||||
// choice point, so we should break.
|
||||
self.machine_st.backtrack();
|
||||
|
||||
if self.machine_st.b <= stub_b {
|
||||
// NOTE: out of choicepoints to backtrack through, no
|
||||
// more solutions to gather.
|
||||
break;
|
||||
}
|
||||
QueryState {
|
||||
machine: self,
|
||||
term: term_write_result,
|
||||
stub_b,
|
||||
var_names,
|
||||
called: false,
|
||||
}
|
||||
|
||||
// NOTE: deallocate stub choice point
|
||||
if self.machine_st.b == stub_b {
|
||||
self.trust_me();
|
||||
}
|
||||
|
||||
Ok(QueryResolution::from(matches))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ordered_float::OrderedFloat;
|
||||
|
||||
use super::*;
|
||||
use crate::machine::{QueryMatch, Value, QueryResolution};
|
||||
use crate::machine::{QueryMatch, QueryResolution, Value};
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||
fn programatic_query() {
|
||||
let mut machine = Machine::new_lib();
|
||||
|
||||
@@ -220,9 +259,9 @@ mod tests {
|
||||
"facts",
|
||||
String::from(
|
||||
r#"
|
||||
triple("a", "p1", "b").
|
||||
triple("a", "p2", "b").
|
||||
"#,
|
||||
triple("a", "p1", "b").
|
||||
triple("a", "p2", "b").
|
||||
"#,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -252,52 +291,60 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||
fn failing_query() {
|
||||
let mut machine = Machine::new_lib();
|
||||
let query = String::from(r#"triple("a",P,"b")."#);
|
||||
let output = machine.run_query(query);
|
||||
assert_eq!(
|
||||
output,
|
||||
Err(String::from("error existence_error procedure / triple 3 / triple 3"))
|
||||
Err(String::from(
|
||||
"error existence_error procedure / triple 3 / triple 3"
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||
fn complex_results() {
|
||||
let mut machine = Machine::new_lib();
|
||||
machine.load_module_string(
|
||||
"facts",
|
||||
r#"
|
||||
:- discontiguous(subject_class/2).
|
||||
:- discontiguous(constructor/2).
|
||||
r#"
|
||||
:- discontiguous(subject_class/2).
|
||||
:- discontiguous(constructor/2).
|
||||
|
||||
subject_class("Todo", c).
|
||||
constructor(c, '[{action: "addLink", source: "this", predicate: "todo://state", target: "todo://ready"}]').
|
||||
subject_class("Todo", c).
|
||||
constructor(c, '[{action: "addLink", source: "this", predicate: "todo://state", target: "todo://ready"}]').
|
||||
|
||||
subject_class("Recipe", xyz).
|
||||
constructor(xyz, '[{action: "addLink", source: "this", predicate: "recipe://title", target: "literal://string:Meta%20Muffins"}]').
|
||||
subject_class("Recipe", xyz).
|
||||
constructor(xyz, '[{action: "addLink", source: "this", predicate: "recipe://title", target: "literal://string:Meta%20Muffins"}]').
|
||||
"#.to_string());
|
||||
|
||||
let result = machine.run_query(String::from("subject_class(\"Todo\", C), constructor(C, Actions)."));
|
||||
let result = machine.run_query(String::from(
|
||||
"subject_class(\"Todo\", C), constructor(C, Actions).",
|
||||
));
|
||||
assert_eq!(
|
||||
result,
|
||||
Ok(QueryResolution::Matches(vec![
|
||||
QueryMatch::from(btreemap! {
|
||||
"C" => Value::from("c"),
|
||||
"Actions" => Value::from("[{action: \"addLink\", source: \"this\", predicate: \"todo://state\", target: \"todo://ready\"}]"),
|
||||
}),
|
||||
]))
|
||||
Ok(QueryResolution::Matches(vec![QueryMatch::from(
|
||||
btreemap! {
|
||||
"C" => Value::Atom("c".into()),
|
||||
"Actions" => Value::Atom("[{action: \"addLink\", source: \"this\", predicate: \"todo://state\", target: \"todo://ready\"}]".into()),
|
||||
}
|
||||
),]))
|
||||
);
|
||||
|
||||
let result = machine.run_query(String::from("subject_class(\"Recipe\", C), constructor(C, Actions)."));
|
||||
let result = machine.run_query(String::from(
|
||||
"subject_class(\"Recipe\", C), constructor(C, Actions).",
|
||||
));
|
||||
assert_eq!(
|
||||
result,
|
||||
Ok(QueryResolution::Matches(vec![
|
||||
QueryMatch::from(btreemap! {
|
||||
"C" => Value::from("xyz"),
|
||||
"Actions" => Value::from("[{action: \"addLink\", source: \"this\", predicate: \"recipe://title\", target: \"literal://string:Meta%20Muffins\"}]"),
|
||||
}),
|
||||
]))
|
||||
Ok(QueryResolution::Matches(vec![QueryMatch::from(
|
||||
btreemap! {
|
||||
"C" => Value::Atom("xyz".into()),
|
||||
"Actions" => Value::Atom("[{action: \"addLink\", source: \"this\", predicate: \"recipe://title\", target: \"literal://string:Meta%20Muffins\"}]".into()),
|
||||
}
|
||||
),]))
|
||||
);
|
||||
|
||||
let result = machine.run_query(String::from("subject_class(Class, _)."));
|
||||
@@ -305,43 +352,60 @@ mod tests {
|
||||
result,
|
||||
Ok(QueryResolution::Matches(vec![
|
||||
QueryMatch::from(btreemap! {
|
||||
"Class" => Value::from("Todo")
|
||||
"Class" => Value::String("Todo".into())
|
||||
}),
|
||||
QueryMatch::from(btreemap! {
|
||||
"Class" => Value::from("Recipe")
|
||||
"Class" => Value::String("Recipe".into())
|
||||
}),
|
||||
]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||
fn empty_predicate() {
|
||||
let mut machine = Machine::new_lib();
|
||||
machine.load_module_string(
|
||||
"facts",
|
||||
r#"
|
||||
:- discontiguous(subject_class/2).
|
||||
"#
|
||||
.to_string(),
|
||||
);
|
||||
|
||||
let result = machine.run_query(String::from("subject_class(X, _)."));
|
||||
assert_eq!(result, Ok(QueryResolution::False));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||
fn list_results() {
|
||||
let mut machine = Machine::new_lib();
|
||||
machine.load_module_string(
|
||||
"facts",
|
||||
r#"
|
||||
list([1,2,3]).
|
||||
"#.to_string());
|
||||
r#"
|
||||
list([1,2,3]).
|
||||
"#
|
||||
.to_string(),
|
||||
);
|
||||
|
||||
let result = machine.run_query(String::from("list(X)."));
|
||||
assert_eq!(
|
||||
result,
|
||||
Ok(QueryResolution::Matches(vec![
|
||||
QueryMatch::from(btreemap! {
|
||||
"X" => Value::List(
|
||||
Vec::from([
|
||||
Value::Float(OrderedFloat::from(1.0)),
|
||||
Value::Float(OrderedFloat::from(2.0)),
|
||||
Value::Float(OrderedFloat::from(3.0))
|
||||
])
|
||||
)
|
||||
}),
|
||||
]))
|
||||
Ok(QueryResolution::Matches(vec![QueryMatch::from(
|
||||
btreemap! {
|
||||
"X" => Value::List(vec![
|
||||
Value::Integer(1.into()),
|
||||
Value::Integer(2.into()),
|
||||
Value::Integer(3.into()),
|
||||
]),
|
||||
}
|
||||
),]))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||
fn consult() {
|
||||
let mut machine = Machine::new_lib();
|
||||
|
||||
@@ -349,9 +413,9 @@ mod tests {
|
||||
"facts",
|
||||
String::from(
|
||||
r#"
|
||||
triple("a", "p1", "b").
|
||||
triple("a", "p2", "b").
|
||||
"#,
|
||||
triple("a", "p1", "b").
|
||||
triple("a", "p2", "b").
|
||||
"#,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -383,8 +447,8 @@ mod tests {
|
||||
"facts",
|
||||
String::from(
|
||||
r#"
|
||||
triple("a", "new", "b").
|
||||
"#,
|
||||
triple("a", "new", "b").
|
||||
"#,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -397,12 +461,12 @@ mod tests {
|
||||
machine.run_query(String::from(r#"triple("a","new","b")."#)),
|
||||
Ok(QueryResolution::True)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
#[ignore = "fails on windows"]
|
||||
#[test]
|
||||
fn stress_integration_test() {
|
||||
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||
#[ignore = "uses old flawed interface"]
|
||||
fn integration_test() {
|
||||
let mut machine = Machine::new_lib();
|
||||
|
||||
// File with test commands, i.e. program code to consult and queries to run
|
||||
@@ -412,6 +476,7 @@ mod tests {
|
||||
let blocks = code.split("=====");
|
||||
|
||||
let mut i = 0;
|
||||
let mut last_result: Option<_> = None;
|
||||
// Iterate over the blocks
|
||||
for block in blocks {
|
||||
// Trim the block to remove any leading or trailing whitespace
|
||||
@@ -423,32 +488,27 @@ mod tests {
|
||||
}
|
||||
|
||||
// Check if the block is a query
|
||||
if block.starts_with("query") {
|
||||
// Extract the query from the block
|
||||
let query = &block[5..];
|
||||
|
||||
i += 1;
|
||||
println!("query #{}: {}", i, query);
|
||||
if let Some(query) = block.strip_prefix("query") {
|
||||
// Parse and execute the query
|
||||
let result = machine.run_query(query.to_string());
|
||||
|
||||
assert!(result.is_ok());
|
||||
|
||||
// Print the result
|
||||
println!("{:?}", result);
|
||||
} else if block.starts_with("consult") {
|
||||
// Extract the code from the block
|
||||
let code = &block[7..];
|
||||
|
||||
println!("load code: {}", code);
|
||||
|
||||
last_result = Some(result);
|
||||
} else if let Some(code) = block.strip_prefix("consult") {
|
||||
// Load the code into the machine
|
||||
machine.consult_module_string("facts", code.to_string());
|
||||
} else if let Some(result) = block.strip_prefix("result") {
|
||||
i += 1;
|
||||
if let Some(Ok(ref last_result)) = last_result {
|
||||
println!("\n\n=====Result No. {i}=======\n{last_result}\n===============");
|
||||
assert_eq!(last_result.to_string(), result.to_string().trim(),)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||
fn findall() {
|
||||
let mut machine = Machine::new_lib();
|
||||
|
||||
@@ -456,29 +516,317 @@ mod tests {
|
||||
"facts",
|
||||
String::from(
|
||||
r#"
|
||||
triple("a", "p1", "b").
|
||||
triple("a", "p2", "b").
|
||||
"#,
|
||||
triple("a", "p1", "b").
|
||||
triple("a", "p2", "b").
|
||||
"#,
|
||||
),
|
||||
);
|
||||
|
||||
let query = String::from(r#"findall([Predicate, Target], triple(_,Predicate,Target), Result)."#);
|
||||
let query =
|
||||
String::from(r#"findall([Predicate, Target], triple(_,Predicate,Target), Result)."#);
|
||||
let output = machine.run_query(query);
|
||||
assert_eq!(
|
||||
output,
|
||||
Ok(QueryResolution::Matches(vec![
|
||||
QueryMatch::from(btreemap! {
|
||||
"Predicate" => Value::from("Predicate"),
|
||||
Ok(QueryResolution::Matches(vec![QueryMatch::from(
|
||||
btreemap! {
|
||||
"Result" => Value::List(
|
||||
Vec::from([
|
||||
Value::List([Value::from("p1"), Value::from("b")].into()),
|
||||
Value::List([Value::from("p2"), Value::from("b")].into()),
|
||||
])
|
||||
),
|
||||
"Target" => Value::from("Target"),
|
||||
}
|
||||
),]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||
fn dont_return_partial_matches() {
|
||||
let mut machine = Machine::new_lib();
|
||||
|
||||
machine.consult_module_string(
|
||||
"facts",
|
||||
String::from(
|
||||
r#"
|
||||
:- discontiguous(property_resolve/2).
|
||||
subject_class("Todo", c).
|
||||
"#,
|
||||
),
|
||||
);
|
||||
|
||||
let query = String::from(r#"property_resolve(C, "isLiked"), subject_class("Todo", C)."#);
|
||||
let output = machine.run_query(query);
|
||||
assert_eq!(output, Ok(QueryResolution::False));
|
||||
|
||||
let query = String::from(r#"subject_class("Todo", C), property_resolve(C, "isLiked")."#);
|
||||
let output = machine.run_query(query);
|
||||
assert_eq!(output, Ok(QueryResolution::False));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||
fn dont_return_partial_matches_without_discountiguous() {
|
||||
let mut machine = Machine::new_lib();
|
||||
|
||||
machine.consult_module_string(
|
||||
"facts",
|
||||
String::from(
|
||||
r#"
|
||||
a("true for a").
|
||||
b("true for b").
|
||||
"#,
|
||||
),
|
||||
);
|
||||
|
||||
let query = String::from(r#"a("true for a")."#);
|
||||
let output = machine.run_query(query);
|
||||
assert_eq!(output, Ok(QueryResolution::True));
|
||||
|
||||
let query = String::from(r#"a("true for a"), b("true for b")."#);
|
||||
let output = machine.run_query(query);
|
||||
assert_eq!(output, Ok(QueryResolution::True));
|
||||
|
||||
let query = String::from(r#"a("true for b"), b("true for b")."#);
|
||||
let output = machine.run_query(query);
|
||||
assert_eq!(output, Ok(QueryResolution::False));
|
||||
|
||||
let query = String::from(r#"a("true for a"), b("true for a")."#);
|
||||
let output = machine.run_query(query);
|
||||
assert_eq!(output, Ok(QueryResolution::False));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||
fn non_existent_predicate_should_not_cause_panic_when_other_predicates_are_defined() {
|
||||
let mut machine = Machine::new_lib();
|
||||
|
||||
machine.consult_module_string(
|
||||
"facts",
|
||||
String::from(
|
||||
r#"
|
||||
triple("a", "p1", "b").
|
||||
triple("a", "p2", "b").
|
||||
"#,
|
||||
),
|
||||
);
|
||||
|
||||
let query = String::from("non_existent_predicate(\"a\",\"p1\",\"b\").");
|
||||
|
||||
let result = machine.run_query(query);
|
||||
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(String::from("error existence_error procedure / non_existent_predicate 3 / non_existent_predicate 3"))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)]
|
||||
fn atom_quoting() {
|
||||
let mut machine = Machine::new_lib();
|
||||
|
||||
let query = "X = '.'.".into();
|
||||
|
||||
let result = machine.run_query(query);
|
||||
|
||||
assert_eq!(
|
||||
result,
|
||||
Ok(QueryResolution::Matches(vec![QueryMatch::from(
|
||||
btreemap! {
|
||||
"X" => Value::Atom(".".into()),
|
||||
}
|
||||
)]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)]
|
||||
fn rational_number() {
|
||||
use crate::parser::dashu::rational::RBig;
|
||||
let mut machine = Machine::new_lib();
|
||||
|
||||
let query = "X is 1 rdiv 2.".into();
|
||||
|
||||
let result = machine.run_query(query);
|
||||
|
||||
assert_eq!(
|
||||
result,
|
||||
Ok(QueryResolution::Matches(vec![QueryMatch::from(
|
||||
btreemap! {
|
||||
"X" => Value::Rational(RBig::from_parts(1.into(), 2u32.into())),
|
||||
}
|
||||
)]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)]
|
||||
fn big_integer() {
|
||||
use crate::parser::dashu::integer::IBig;
|
||||
let mut machine = Machine::new_lib();
|
||||
|
||||
let query = "X is 10^100.".into();
|
||||
|
||||
let result = machine.run_query(query);
|
||||
|
||||
assert_eq!(
|
||||
result,
|
||||
Ok(QueryResolution::Matches(vec![QueryMatch::from(
|
||||
btreemap! {
|
||||
"X" => Value::Integer(IBig::from(10).pow(100)),
|
||||
}
|
||||
)]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)]
|
||||
fn complicated_term() {
|
||||
let mut machine = Machine::new_lib();
|
||||
|
||||
let query = "X = a(\"asdf\", [42, 2.54, asdf, a, [a,b|_], Z]).".into();
|
||||
|
||||
let result = machine.run_query(query);
|
||||
|
||||
let expected = Value::Structure(
|
||||
// Composite term
|
||||
"a".into(),
|
||||
vec![
|
||||
Value::String("asdf".into()), // String
|
||||
Value::List(vec![
|
||||
Value::Integer(42.into()), // Fixnum
|
||||
Value::Float(2.54.into()), // Float
|
||||
Value::Atom("asdf".into()), // Atom
|
||||
Value::Atom("a".into()), // Char
|
||||
Value::Structure(
|
||||
// Partial string
|
||||
".".into(),
|
||||
vec![
|
||||
Value::Atom("a".into()),
|
||||
Value::Structure(
|
||||
".".into(),
|
||||
vec![
|
||||
Value::Atom("b".into()),
|
||||
Value::Var("_A".into()), // Anonymous variable
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
Value::Var("Z".into()), // Named variable
|
||||
]),
|
||||
],
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
result,
|
||||
Ok(QueryResolution::Matches(vec![QueryMatch::from(
|
||||
btreemap! {
|
||||
"X" => expected,
|
||||
}
|
||||
)]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||
fn issue_2341() {
|
||||
let mut machine = Machine::new_lib();
|
||||
|
||||
machine.load_module_string(
|
||||
"facts",
|
||||
String::from(
|
||||
r#"
|
||||
male(stephen).
|
||||
parent(albert,edward).
|
||||
father(F,C):-parent(F,C),male(F).
|
||||
"#,
|
||||
),
|
||||
);
|
||||
|
||||
let query = String::from(r#"father(F,C)."#);
|
||||
let output = machine.run_query(query);
|
||||
|
||||
assert_eq!(output, Ok(QueryResolution::False));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)]
|
||||
fn query_iterator_determinism() {
|
||||
let mut machine = Machine::new_lib();
|
||||
|
||||
{
|
||||
let mut iterator = machine.run_query_iter("X = 1.".into());
|
||||
|
||||
iterator.next();
|
||||
assert_eq!(iterator.next(), None);
|
||||
}
|
||||
|
||||
{
|
||||
let mut iterator = machine.run_query_iter("X = 1 ; false.".into());
|
||||
|
||||
iterator.next();
|
||||
|
||||
assert_eq!(iterator.next(), Some(Ok(QueryResolutionLine::False)));
|
||||
assert_eq!(iterator.next(), None);
|
||||
}
|
||||
|
||||
{
|
||||
let mut iterator = machine.run_query_iter("false.".into());
|
||||
|
||||
assert_eq!(iterator.next(), Some(Ok(QueryResolutionLine::False)));
|
||||
assert_eq!(iterator.next(), None);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)]
|
||||
fn query_iterator_backtracking_when_no_variables() {
|
||||
let mut machine = Machine::new_lib();
|
||||
|
||||
let mut iterator = machine.run_query_iter("true;false.".into());
|
||||
|
||||
assert_eq!(iterator.next(), Some(Ok(QueryResolutionLine::True)));
|
||||
assert_eq!(iterator.next(), Some(Ok(QueryResolutionLine::False)));
|
||||
assert_eq!(iterator.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)]
|
||||
fn differentiate_anonymous_variables() {
|
||||
let mut machine = Machine::new_lib();
|
||||
|
||||
let result = machine.run_query("A = [_,_], _B = 1 ; B = [_,_].".into());
|
||||
|
||||
assert_eq!(
|
||||
result,
|
||||
Ok(QueryResolution::Matches(vec![
|
||||
QueryMatch::from(btreemap! {
|
||||
"A" => Value::List(vec![Value::Var("_A".into()), Value::Var("_C".into())]),
|
||||
"_B" => Value::Integer(1.into()),
|
||||
}),
|
||||
QueryMatch::from(btreemap! {
|
||||
"B" => Value::List(vec![Value::Var("_A".into()), Value::Var("_C".into())]),
|
||||
}),
|
||||
]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)]
|
||||
fn order_of_variables_in_binding() {
|
||||
let mut machine = Machine::new_lib();
|
||||
|
||||
let result = machine.run_query("X = Y, Z = W.".into());
|
||||
|
||||
assert_eq!(
|
||||
result,
|
||||
Ok(QueryResolution::Matches(vec![QueryMatch::from(
|
||||
btreemap! {
|
||||
"X" => Value::Var("Y".into()),
|
||||
"Z" => Value::Var("W".into()),
|
||||
}
|
||||
),]))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ use crate::parser::ast::*;
|
||||
|
||||
use fxhash::FxBuildHasher;
|
||||
use indexmap::IndexSet;
|
||||
pub use ref_thread_local::RefThreadLocal;
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::fs::File;
|
||||
@@ -137,10 +136,9 @@ pub(super) fn import_module_exports<'a, LS: LoadState<'a>>(
|
||||
if let Some(src_code_index) = imported_module.code_dir.get(&key).cloned() {
|
||||
let arena = &mut LS::machine_st(payload).arena;
|
||||
|
||||
let target_code_index = code_dir
|
||||
let target_code_index = *code_dir
|
||||
.entry(key)
|
||||
.or_insert_with(|| CodeIndex::default(arena))
|
||||
.clone();
|
||||
.or_insert_with(|| CodeIndex::default(arena));
|
||||
|
||||
set_code_index(
|
||||
&mut payload.retraction_info,
|
||||
@@ -189,16 +187,15 @@ fn import_module_exports_into_module<'a, LS: LoadState<'a>>(
|
||||
let key = (*name, *arity);
|
||||
|
||||
if let Some(meta_specs) = imported_module.meta_predicates.get(&key) {
|
||||
meta_predicates.insert(key.clone(), meta_specs.clone());
|
||||
meta_predicates.insert(key, meta_specs.clone());
|
||||
}
|
||||
|
||||
if let Some(src_code_index) = imported_module.code_dir.get(&key) {
|
||||
let arena = &mut LS::machine_st(payload).arena;
|
||||
|
||||
let target_code_index = code_dir
|
||||
let target_code_index = *code_dir
|
||||
.entry(key)
|
||||
.or_insert_with(|| CodeIndex::default(arena))
|
||||
.clone();
|
||||
.or_insert_with(|| CodeIndex::default(arena));
|
||||
|
||||
set_code_index(
|
||||
&mut payload.retraction_info,
|
||||
@@ -209,7 +206,7 @@ fn import_module_exports_into_module<'a, LS: LoadState<'a>>(
|
||||
);
|
||||
} else {
|
||||
return Err(SessionError::ModuleDoesNotContainExport(
|
||||
imported_module.module_decl.name.clone(),
|
||||
imported_module.module_decl.name,
|
||||
(*name, *arity),
|
||||
));
|
||||
}
|
||||
@@ -243,18 +240,17 @@ fn import_qualified_module_exports<'a, LS: LoadState<'a>>(
|
||||
wam_prelude
|
||||
.indices
|
||||
.meta_predicates
|
||||
.insert(key.clone(), meta_specs.clone());
|
||||
.insert(key, meta_specs.clone());
|
||||
}
|
||||
|
||||
if let Some(src_code_index) = imported_module.code_dir.get(&key) {
|
||||
let arena = &mut LS::machine_st(payload).arena;
|
||||
|
||||
let target_code_index = wam_prelude
|
||||
let target_code_index = *wam_prelude
|
||||
.indices
|
||||
.code_dir
|
||||
.entry(key.clone())
|
||||
.or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), arena))
|
||||
.clone();
|
||||
.entry(key)
|
||||
.or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), arena));
|
||||
|
||||
set_code_index(
|
||||
&mut payload.retraction_info,
|
||||
@@ -265,7 +261,7 @@ fn import_qualified_module_exports<'a, LS: LoadState<'a>>(
|
||||
);
|
||||
} else {
|
||||
return Err(SessionError::ModuleDoesNotContainExport(
|
||||
imported_module.module_decl.name.clone(),
|
||||
imported_module.module_decl.name,
|
||||
(*name, *arity),
|
||||
));
|
||||
}
|
||||
@@ -311,10 +307,9 @@ fn import_qualified_module_exports_into_module<'a, LS: LoadState<'a>>(
|
||||
if let Some(src_code_index) = imported_module.code_dir.get(&key) {
|
||||
let arena = &mut LS::machine_st(payload).arena;
|
||||
|
||||
let target_code_index = code_dir
|
||||
let target_code_index = *code_dir
|
||||
.entry(key)
|
||||
.or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), arena))
|
||||
.clone();
|
||||
.or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), arena));
|
||||
|
||||
set_code_index(
|
||||
&mut payload.retraction_info,
|
||||
@@ -325,7 +320,7 @@ fn import_qualified_module_exports_into_module<'a, LS: LoadState<'a>>(
|
||||
);
|
||||
} else {
|
||||
return Err(SessionError::ModuleDoesNotContainExport(
|
||||
imported_module.module_decl.name.clone(),
|
||||
imported_module.module_decl.name,
|
||||
(*name, *arity),
|
||||
));
|
||||
}
|
||||
@@ -423,7 +418,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
payload_compilation_target,
|
||||
clause_clause_compilation_target,
|
||||
key,
|
||||
mem::replace(&mut skeleton.clause_clause_locs, VecDeque::new()),
|
||||
std::mem::take(&mut skeleton.clause_clause_locs),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -436,7 +431,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
}
|
||||
};
|
||||
|
||||
self.retract_local_clauses_impl(clause_clause_compilation_target, key, &clause_locs);
|
||||
self.retract_local_clauses_impl(clause_clause_compilation_target, key, clause_locs);
|
||||
}
|
||||
|
||||
pub(super) fn try_term_to_tl(
|
||||
@@ -465,29 +460,53 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
}
|
||||
|
||||
pub(super) fn remove_replaced_in_situ_module(&mut self, module_name: Atom) {
|
||||
let mut removed_module = match self.wam_prelude.indices.modules.remove(&module_name) {
|
||||
let mut removed_module = match self.wam_prelude.indices.modules.swap_remove(&module_name) {
|
||||
Some(module) => module,
|
||||
None => return,
|
||||
};
|
||||
|
||||
for (key, code_index) in removed_module.code_dir.iter_mut() {
|
||||
match removed_module
|
||||
.local_extensible_predicates
|
||||
.get(&(CompilationTarget::User, *key))
|
||||
{
|
||||
Some(skeleton) if skeleton.is_multifile => continue,
|
||||
_ => {}
|
||||
let mut skipped_local_predicates = IndexSet::with_hasher(FxBuildHasher::default());
|
||||
|
||||
for ((local_compilation_target, key), skeleton) in
|
||||
removed_module.local_extensible_predicates.iter()
|
||||
{
|
||||
skipped_local_predicates.insert(key);
|
||||
|
||||
if skeleton.is_multifile {
|
||||
continue;
|
||||
}
|
||||
|
||||
let old_index_ptr = code_index.replace(IndexPtr::undefined());
|
||||
if let Some(code_index) = removed_module.code_dir.get_mut(key) {
|
||||
if let Some(global_skeleton) = self
|
||||
.wam_prelude
|
||||
.indices
|
||||
.get_predicate_skeleton(local_compilation_target, key)
|
||||
{
|
||||
let old_index_ptr = code_index.replace(if global_skeleton.core.is_dynamic {
|
||||
IndexPtr::dynamic_undefined()
|
||||
} else {
|
||||
IndexPtr::undefined()
|
||||
});
|
||||
|
||||
self.payload
|
||||
.retraction_info
|
||||
.push_record(RetractionRecord::ReplacedModulePredicate(
|
||||
module_name,
|
||||
*key,
|
||||
old_index_ptr,
|
||||
));
|
||||
self.payload.retraction_info.push_record(
|
||||
RetractionRecord::ReplacedModulePredicate(module_name, *key, old_index_ptr),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (key, code_index) in removed_module.code_dir.iter_mut() {
|
||||
if skipped_local_predicates.contains(key) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if !code_index.is_undefined() && !code_index.is_dynamic_undefined() {
|
||||
let old_index_ptr = code_index.replace(IndexPtr::undefined());
|
||||
|
||||
self.payload.retraction_info.push_record(
|
||||
RetractionRecord::ReplacedModulePredicate(module_name, *key, old_index_ptr),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (key, skeleton) in removed_module.extensible_predicates.drain(..) {
|
||||
@@ -507,7 +526,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
}
|
||||
|
||||
pub(super) fn remove_module_exports(&mut self, module_name: Atom) {
|
||||
let removed_module = match self.wam_prelude.indices.modules.remove(&module_name) {
|
||||
let removed_module = match self.wam_prelude.indices.modules.swap_remove(&module_name) {
|
||||
Some(module) => module,
|
||||
None => return,
|
||||
};
|
||||
@@ -537,7 +556,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
}
|
||||
ModuleExport::OpDecl(op_decl) => {
|
||||
let op_dir_value_opt = op_dir
|
||||
.remove(&(op_decl.name, fixity(op_decl.op_desc.get_spec() as u32)));
|
||||
.swap_remove(&(op_decl.name, op_decl.op_desc.get_spec().fixity()));
|
||||
|
||||
if let Some(op_desc) = op_dir_value_opt {
|
||||
retraction_info.push_record(op_retractor(*op_decl, op_desc));
|
||||
@@ -600,30 +619,22 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
key: PredicateKey,
|
||||
) -> CodeIndex {
|
||||
match self.wam_prelude.indices.modules.get_mut(&module_name) {
|
||||
Some(ref mut module) => module
|
||||
.code_dir
|
||||
.entry(key)
|
||||
.or_insert_with(|| {
|
||||
CodeIndex::new(
|
||||
IndexPtr::undefined(),
|
||||
&mut LS::machine_st(&mut self.payload).arena,
|
||||
)
|
||||
})
|
||||
.clone(),
|
||||
Some(ref mut module) => *module.code_dir.entry(key).or_insert_with(|| {
|
||||
CodeIndex::new(
|
||||
IndexPtr::undefined(),
|
||||
&mut LS::machine_st(&mut self.payload).arena,
|
||||
)
|
||||
}),
|
||||
None => {
|
||||
self.add_dynamically_generated_module(module_name);
|
||||
|
||||
match self.wam_prelude.indices.modules.get_mut(&module_name) {
|
||||
Some(ref mut module) => module
|
||||
.code_dir
|
||||
.entry(key)
|
||||
.or_insert_with(|| {
|
||||
CodeIndex::new(
|
||||
IndexPtr::undefined(),
|
||||
&mut LS::machine_st(&mut self.payload).arena,
|
||||
)
|
||||
})
|
||||
.clone(),
|
||||
Some(ref mut module) => *module.code_dir.entry(key).or_insert_with(|| {
|
||||
CodeIndex::new(
|
||||
IndexPtr::undefined(),
|
||||
&mut LS::machine_st(&mut self.payload).arena,
|
||||
)
|
||||
}),
|
||||
None => {
|
||||
unreachable!()
|
||||
}
|
||||
@@ -640,13 +651,12 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
let arena = &mut LS::machine_st(&mut self.payload).arena;
|
||||
|
||||
match compilation_target {
|
||||
CompilationTarget::User => self
|
||||
CompilationTarget::User => *self
|
||||
.wam_prelude
|
||||
.indices
|
||||
.code_dir
|
||||
.entry(key)
|
||||
.or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), arena))
|
||||
.clone(),
|
||||
.or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), arena)),
|
||||
CompilationTarget::Module(module_name) => {
|
||||
self.get_or_insert_local_code_index(module_name, key)
|
||||
}
|
||||
@@ -661,13 +671,12 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
let arena = &mut LS::machine_st(&mut self.payload).arena;
|
||||
|
||||
if module_name == atom!("user") {
|
||||
return self
|
||||
return *self
|
||||
.wam_prelude
|
||||
.indices
|
||||
.code_dir
|
||||
.entry(key)
|
||||
.or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), arena))
|
||||
.clone();
|
||||
.or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), arena));
|
||||
} else {
|
||||
self.get_or_insert_local_code_index(module_name, key)
|
||||
}
|
||||
@@ -694,7 +703,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
}
|
||||
CompilationTarget::Module(module_name) => {
|
||||
if let Some(module) = self.wam_prelude.indices.modules.get_mut(&module_name) {
|
||||
module.extensible_predicates.insert(key.clone(), skeleton);
|
||||
module.extensible_predicates.insert(key, skeleton);
|
||||
|
||||
let record = RetractionRecord::AddedExtensiblePredicate(
|
||||
CompilationTarget::Module(module_name),
|
||||
@@ -747,11 +756,10 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
match payload_compilation_target {
|
||||
CompilationTarget::User => {
|
||||
if let Some(filename) = listing_src_file_name {
|
||||
match self.wam_prelude.indices.modules.get_mut(&filename) {
|
||||
Some(ref mut module) => {
|
||||
op_decl.insert_into_op_dir(&mut module.op_dir);
|
||||
}
|
||||
None => {}
|
||||
if let Some(ref mut module) =
|
||||
self.wam_prelude.indices.modules.get_mut(&filename)
|
||||
{
|
||||
op_decl.insert_into_op_dir(&mut module.op_dir);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -855,48 +863,43 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
match self.wam_prelude.indices.modules.get_mut(&module_name) {
|
||||
Some(ref mut module) => {
|
||||
match module.meta_predicates.insert(key.clone(), meta_specs) {
|
||||
Some(old_meta_specs) => {
|
||||
self.payload.retraction_info.push_record(
|
||||
RetractionRecord::ReplacedMetaPredicate(
|
||||
module_name,
|
||||
key.0,
|
||||
old_meta_specs,
|
||||
),
|
||||
);
|
||||
}
|
||||
None => {
|
||||
self.payload.retraction_info.push_record(
|
||||
RetractionRecord::AddedMetaPredicate(module_name, key),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
self.add_dynamically_generated_module(module_name);
|
||||
|
||||
if let Some(module) = self.wam_prelude.indices.modules.get_mut(&module_name)
|
||||
{
|
||||
module.meta_predicates.insert(key.clone(), meta_specs);
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
_ => match self.wam_prelude.indices.modules.get_mut(&module_name) {
|
||||
Some(ref mut module) => match module.meta_predicates.insert(key, meta_specs) {
|
||||
Some(old_meta_specs) => {
|
||||
self.payload.retraction_info.push_record(
|
||||
RetractionRecord::AddedMetaPredicate(module_name.clone(), key),
|
||||
RetractionRecord::ReplacedMetaPredicate(
|
||||
module_name,
|
||||
key.0,
|
||||
old_meta_specs,
|
||||
),
|
||||
);
|
||||
}
|
||||
None => {
|
||||
self.payload
|
||||
.retraction_info
|
||||
.push_record(RetractionRecord::AddedMetaPredicate(module_name, key));
|
||||
}
|
||||
},
|
||||
None => {
|
||||
self.add_dynamically_generated_module(module_name);
|
||||
|
||||
if let Some(module) = self.wam_prelude.indices.modules.get_mut(&module_name) {
|
||||
module.meta_predicates.insert(key, meta_specs);
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
self.payload
|
||||
.retraction_info
|
||||
.push_record(RetractionRecord::AddedMetaPredicate(module_name, key));
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn add_dynamically_generated_module(&mut self, module_name: Atom) {
|
||||
let module_decl = ModuleDecl {
|
||||
name: module_name.clone(),
|
||||
name: module_name,
|
||||
exports: vec![],
|
||||
};
|
||||
|
||||
@@ -912,12 +915,9 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
|
||||
self.payload
|
||||
.retraction_info
|
||||
.push_record(RetractionRecord::AddedModule(module_name.clone()));
|
||||
.push_record(RetractionRecord::AddedModule(module_name));
|
||||
|
||||
self.wam_prelude
|
||||
.indices
|
||||
.modules
|
||||
.insert(module_name.clone(), module);
|
||||
self.wam_prelude.indices.modules.insert(module_name, module);
|
||||
}
|
||||
|
||||
fn import_builtins_in_module(
|
||||
@@ -956,51 +956,48 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
self.remove_module_exports(module_name);
|
||||
self.remove_replaced_in_situ_module(module_name);
|
||||
|
||||
match self.wam_prelude.indices.modules.get_mut(&module_name) {
|
||||
Some(module) => {
|
||||
let old_module_decl = mem::replace(&mut module.module_decl, module_decl.clone());
|
||||
if let Some(module) = self.wam_prelude.indices.modules.get_mut(&module_name) {
|
||||
let old_module_decl = mem::replace(&mut module.module_decl, module_decl.clone());
|
||||
|
||||
let local_extensible_predicates = mem::replace(
|
||||
&mut module.local_extensible_predicates,
|
||||
LocalExtensiblePredicates::with_hasher(FxBuildHasher::default()),
|
||||
let local_extensible_predicates = mem::replace(
|
||||
&mut module.local_extensible_predicates,
|
||||
LocalExtensiblePredicates::with_hasher(FxBuildHasher::default()),
|
||||
);
|
||||
|
||||
for ((compilation_target, key), skeleton) in local_extensible_predicates.iter() {
|
||||
self.retract_local_clauses_impl(
|
||||
*compilation_target,
|
||||
*key,
|
||||
&skeleton.clause_clause_locs,
|
||||
);
|
||||
|
||||
for ((compilation_target, key), skeleton) in local_extensible_predicates.iter() {
|
||||
self.retract_local_clauses_impl(
|
||||
*compilation_target,
|
||||
*key,
|
||||
let is_dynamic = self
|
||||
.wam_prelude
|
||||
.indices
|
||||
.get_predicate_skeleton(compilation_target, key)
|
||||
.map(|skeleton| skeleton.core.is_dynamic)
|
||||
.unwrap_or(false);
|
||||
|
||||
if is_dynamic {
|
||||
let clause_clause_compilation_target = match compilation_target {
|
||||
CompilationTarget::User => CompilationTarget::Module(atom!("builtins")),
|
||||
module => *module,
|
||||
};
|
||||
|
||||
self.retract_local_clause_clauses(
|
||||
clause_clause_compilation_target,
|
||||
&skeleton.clause_clause_locs,
|
||||
);
|
||||
|
||||
let is_dynamic = self
|
||||
.wam_prelude
|
||||
.indices
|
||||
.get_predicate_skeleton(compilation_target, key)
|
||||
.map(|skeleton| skeleton.core.is_dynamic)
|
||||
.unwrap_or(false);
|
||||
|
||||
if is_dynamic {
|
||||
let clause_clause_compilation_target = match compilation_target {
|
||||
CompilationTarget::User => CompilationTarget::Module(atom!("builtins")),
|
||||
module => module.clone(),
|
||||
};
|
||||
|
||||
self.retract_local_clause_clauses(
|
||||
clause_clause_compilation_target,
|
||||
&skeleton.clause_clause_locs,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
self.payload
|
||||
.retraction_info
|
||||
.push_record(RetractionRecord::ReplacedModule(
|
||||
old_module_decl,
|
||||
listing_src.clone(),
|
||||
local_extensible_predicates,
|
||||
));
|
||||
}
|
||||
None => {}
|
||||
|
||||
self.payload
|
||||
.retraction_info
|
||||
.push_record(RetractionRecord::ReplacedModule(
|
||||
old_module_decl,
|
||||
listing_src.clone(),
|
||||
local_extensible_predicates,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1020,7 +1017,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
|
||||
self.reset_in_situ_module(module_decl.clone(), &listing_src);
|
||||
|
||||
let mut module = match self.wam_prelude.indices.modules.remove(&module_name) {
|
||||
let mut module = match self.wam_prelude.indices.modules.swap_remove(&module_name) {
|
||||
Some(mut module) => {
|
||||
module.listing_src = listing_src;
|
||||
module
|
||||
@@ -1062,7 +1059,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
}
|
||||
|
||||
pub(super) fn import_module(&mut self, module_name: Atom) -> Result<(), SessionError> {
|
||||
if let Some(module) = self.wam_prelude.indices.modules.remove(&module_name) {
|
||||
if let Some(module) = self.wam_prelude.indices.modules.swap_remove(&module_name) {
|
||||
let payload_compilation_target = self.payload.compilation_target;
|
||||
|
||||
match &payload_compilation_target {
|
||||
@@ -1118,7 +1115,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
module_name: Atom,
|
||||
exports: IndexSet<ModuleExport>,
|
||||
) -> Result<(), SessionError> {
|
||||
if let Some(module) = self.wam_prelude.indices.modules.remove(&module_name) {
|
||||
if let Some(module) = self.wam_prelude.indices.modules.swap_remove(&module_name) {
|
||||
let payload_compilation_target = self.payload.compilation_target;
|
||||
|
||||
let result = match &payload_compilation_target {
|
||||
@@ -1178,13 +1175,13 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
ListingSource::File(filename, path_buf),
|
||||
)
|
||||
}
|
||||
ModuleSource::Library(library) => match LIBRARIES.borrow().get(&*library.as_str()) {
|
||||
ModuleSource::Library(library) => match libraries::get(&library.as_str()) {
|
||||
Some(code) => {
|
||||
if let Some(ref module) = self.wam_prelude.indices.modules.get(&library) {
|
||||
if let Some(module) = self.wam_prelude.indices.modules.get(&library) {
|
||||
if let ListingSource::DynamicallyGenerated = &module.listing_src {
|
||||
(
|
||||
Stream::from_static_string(
|
||||
*code,
|
||||
code,
|
||||
&mut LS::machine_st(&mut self.payload).arena,
|
||||
),
|
||||
ListingSource::User,
|
||||
@@ -1195,7 +1192,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
} else {
|
||||
(
|
||||
Stream::from_static_string(
|
||||
*code,
|
||||
code,
|
||||
&mut LS::machine_st(&mut self.payload).arena,
|
||||
),
|
||||
ListingSource::User,
|
||||
@@ -1259,14 +1256,14 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
ListingSource::File(filename, path_buf),
|
||||
)
|
||||
}
|
||||
ModuleSource::Library(library) => match LIBRARIES.borrow().get(&*library.as_str()) {
|
||||
ModuleSource::Library(library) => match libraries::get(&library.as_str()) {
|
||||
Some(code) => {
|
||||
if self.wam_prelude.indices.modules.contains_key(&library) {
|
||||
return self.import_qualified_module(library, exports);
|
||||
} else {
|
||||
(
|
||||
Stream::from_static_string(
|
||||
*code,
|
||||
code,
|
||||
&mut LS::machine_st(&mut self.payload).arena,
|
||||
),
|
||||
ListingSource::User,
|
||||
|
||||
@@ -19,7 +19,6 @@ use std::cell::Cell;
|
||||
use std::collections::VecDeque;
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
use std::mem;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
/*
|
||||
@@ -136,7 +135,7 @@ impl RetractionInfo {
|
||||
|
||||
Self {
|
||||
orig_code_extent,
|
||||
records: mem::replace(&mut self.records, vec![]),
|
||||
records: std::mem::take(&mut self.records),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -149,9 +148,10 @@ impl<'a, LS: LoadState<'a>> Drop for Loader<'a, LS> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Default)]
|
||||
pub enum CompilationTarget {
|
||||
Module(Atom),
|
||||
#[default]
|
||||
User,
|
||||
}
|
||||
|
||||
@@ -164,13 +164,6 @@ impl fmt::Display for CompilationTarget {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for CompilationTarget {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
CompilationTarget::User
|
||||
}
|
||||
}
|
||||
|
||||
impl CompilationTarget {
|
||||
#[inline]
|
||||
pub(crate) fn module_name(&self) -> Atom {
|
||||
@@ -207,8 +200,8 @@ impl PredicateQueue {
|
||||
#[inline]
|
||||
pub(super) fn take(&mut self) -> Self {
|
||||
Self {
|
||||
predicates: mem::replace(&mut self.predicates, vec![]),
|
||||
compilation_target: self.compilation_target.clone(),
|
||||
predicates: std::mem::take(&mut self.predicates),
|
||||
compilation_target: self.compilation_target,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,7 +211,6 @@ impl PredicateQueue {
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! predicate_queue {
|
||||
[$($v:expr),*] => (
|
||||
PredicateQueue {
|
||||
@@ -311,11 +303,15 @@ impl<'a> LoadState<'a> for LiveLoadAndMachineState<'a> {
|
||||
|
||||
#[inline(always)]
|
||||
fn evacuate(mut loader: Loader<'a, Self>) -> Result<Self::Evacuable, SessionError> {
|
||||
loader
|
||||
.payload
|
||||
.load_state
|
||||
.set_tag(ArenaHeaderTag::InactiveLoadState);
|
||||
Ok(loader.payload.load_state)
|
||||
if loader.payload.load_state.get_tag() != ArenaHeaderTag::Dropped {
|
||||
loader
|
||||
.payload
|
||||
.load_state
|
||||
.set_tag(ArenaHeaderTag::InactiveLoadState);
|
||||
Ok(loader.payload.load_state)
|
||||
} else {
|
||||
unreachable!("we never evacuate after dropping")
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -326,8 +322,8 @@ impl<'a> LoadState<'a> for LiveLoadAndMachineState<'a> {
|
||||
#[inline(always)]
|
||||
fn reset_machine(loader: &mut Loader<'a, Self>) {
|
||||
if loader.payload.load_state.get_tag() != ArenaHeaderTag::Dropped {
|
||||
loader.payload.load_state.set_tag(ArenaHeaderTag::Dropped);
|
||||
loader.reset_machine();
|
||||
loader.payload.load_state.drop_payload();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,7 +356,7 @@ impl<'a> LoadState<'a> for LiveLoadAndMachineState<'a> {
|
||||
|
||||
#[inline]
|
||||
fn err_on_builtin_module_overwrite(module_name: Atom) -> Result<(), SessionError> {
|
||||
if LIBRARIES.borrow().contains_key(&*module_name.as_str()) {
|
||||
if libraries::contains(&module_name.as_str()) {
|
||||
Err(SessionError::CannotOverwriteBuiltInModule(module_name))
|
||||
} else {
|
||||
Ok(())
|
||||
@@ -404,7 +400,7 @@ impl<'a> LoadState<'a> for BootstrappingLoadState<'a> {
|
||||
|
||||
#[inline(always)]
|
||||
fn machine_st(loader: &mut Self::LoaderFieldType) -> &mut MachineState {
|
||||
&mut loader.term_stream.parser.lexer.machine_st
|
||||
loader.term_stream.parser.lexer.machine_st
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -467,7 +463,7 @@ impl<'a> LoadState<'a> for InlineLoadState<'a> {
|
||||
|
||||
#[inline(always)]
|
||||
fn machine_st(load_state: &mut Self::LoaderFieldType) -> &mut MachineState {
|
||||
&mut load_state.machine_st
|
||||
load_state.machine_st
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -577,7 +573,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
RetractionRecord::AddedMetaPredicate(target_module_name, key) => {
|
||||
match target_module_name {
|
||||
atom!("user") => {
|
||||
self.wam_prelude.indices.meta_predicates.remove(&key);
|
||||
self.wam_prelude.indices.meta_predicates.swap_remove(&key);
|
||||
}
|
||||
_ => match self
|
||||
.wam_prelude
|
||||
@@ -586,7 +582,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
.get_mut(&target_module_name)
|
||||
{
|
||||
Some(ref mut module) => {
|
||||
module.meta_predicates.remove(&key);
|
||||
module.meta_predicates.swap_remove(&key);
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
@@ -620,7 +616,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
}
|
||||
}
|
||||
RetractionRecord::AddedModule(module_name) => {
|
||||
self.wam_prelude.indices.modules.remove(&module_name);
|
||||
self.wam_prelude.indices.modules.swap_remove(&module_name);
|
||||
}
|
||||
RetractionRecord::ReplacedModule(
|
||||
module_decl,
|
||||
@@ -639,22 +635,19 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
RetractionRecord::AddedDiscontiguousPredicate(compilation_target, key) => {
|
||||
match compilation_target {
|
||||
CompilationTarget::User => {
|
||||
self.wam_prelude
|
||||
.indices
|
||||
.extensible_predicates
|
||||
.get_mut(&key)
|
||||
.map(|skeleton| {
|
||||
skeleton.core.is_discontiguous = false;
|
||||
});
|
||||
if let Some(skeleton) =
|
||||
self.wam_prelude.indices.extensible_predicates.get_mut(&key)
|
||||
{
|
||||
skeleton.core.is_discontiguous = false;
|
||||
}
|
||||
}
|
||||
CompilationTarget::Module(module_name) => {
|
||||
match self.wam_prelude.indices.modules.get_mut(&module_name) {
|
||||
Some(ref mut module) => {
|
||||
module.extensible_predicates.get_mut(&key).map(|skeleton| {
|
||||
skeleton.core.is_discontiguous = false;
|
||||
});
|
||||
if let Some(ref mut module) =
|
||||
self.wam_prelude.indices.modules.get_mut(&module_name)
|
||||
{
|
||||
if let Some(skeleton) = module.extensible_predicates.get_mut(&key) {
|
||||
skeleton.core.is_discontiguous = false;
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -662,23 +655,20 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
RetractionRecord::AddedDynamicPredicate(compilation_target, key) => {
|
||||
match compilation_target {
|
||||
CompilationTarget::User => {
|
||||
self.wam_prelude
|
||||
.indices
|
||||
.extensible_predicates
|
||||
.get_mut(&key)
|
||||
.map(|skeleton| {
|
||||
skeleton.core.is_dynamic = false;
|
||||
});
|
||||
if let Some(skeleton) =
|
||||
self.wam_prelude.indices.extensible_predicates.get_mut(&key)
|
||||
{
|
||||
skeleton.core.is_dynamic = false;
|
||||
}
|
||||
}
|
||||
CompilationTarget::Module(module_name) => {
|
||||
match self.wam_prelude.indices.modules.get_mut(&module_name) {
|
||||
Some(ref mut module) => {
|
||||
module.extensible_predicates.get_mut(&key).map(|skeleton| {
|
||||
skeleton.core.is_dynamic = false;
|
||||
skeleton.core.retracted_dynamic_clauses = None;
|
||||
});
|
||||
}
|
||||
None => {}
|
||||
if let Some(ref mut module) =
|
||||
self.wam_prelude.indices.modules.get_mut(&module_name)
|
||||
{
|
||||
if let Some(skeleton) = module.extensible_predicates.get_mut(&key) {
|
||||
skeleton.core.is_dynamic = false;
|
||||
skeleton.core.retracted_dynamic_clauses = None;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -686,60 +676,52 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
RetractionRecord::AddedMultifilePredicate(compilation_target, key) => {
|
||||
match compilation_target {
|
||||
CompilationTarget::User => {
|
||||
self.wam_prelude
|
||||
.indices
|
||||
.extensible_predicates
|
||||
.get_mut(&key)
|
||||
.map(|skeleton| {
|
||||
skeleton.core.is_multifile = false;
|
||||
});
|
||||
if let Some(skeleton) =
|
||||
self.wam_prelude.indices.extensible_predicates.get_mut(&key)
|
||||
{
|
||||
skeleton.core.is_multifile = false;
|
||||
}
|
||||
}
|
||||
CompilationTarget::Module(module_name) => {
|
||||
match self.wam_prelude.indices.modules.get_mut(&module_name) {
|
||||
Some(ref mut module) => {
|
||||
module.extensible_predicates.get_mut(&key).map(|skeleton| {
|
||||
skeleton.core.is_multifile = false;
|
||||
});
|
||||
if let Some(ref mut module) =
|
||||
self.wam_prelude.indices.modules.get_mut(&module_name)
|
||||
{
|
||||
if let Some(skeleton) = module.extensible_predicates.get_mut(&key) {
|
||||
skeleton.core.is_multifile = false;
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
RetractionRecord::AddedModuleOp(module_name, mut op_decl) => {
|
||||
match self.wam_prelude.indices.modules.get_mut(&module_name) {
|
||||
Some(ref mut module) => {
|
||||
op_decl.remove(&mut module.op_dir);
|
||||
}
|
||||
None => {}
|
||||
if let Some(ref mut module) =
|
||||
self.wam_prelude.indices.modules.get_mut(&module_name)
|
||||
{
|
||||
op_decl.remove(&mut module.op_dir);
|
||||
}
|
||||
}
|
||||
RetractionRecord::ReplacedModuleOp(module_name, mut op_decl, op_desc) => {
|
||||
match self.wam_prelude.indices.modules.get_mut(&module_name) {
|
||||
Some(ref mut module) => {
|
||||
op_decl.op_desc = op_desc;
|
||||
op_decl.insert_into_op_dir(&mut module.op_dir);
|
||||
}
|
||||
None => {}
|
||||
if let Some(ref mut module) =
|
||||
self.wam_prelude.indices.modules.get_mut(&module_name)
|
||||
{
|
||||
op_decl.op_desc = op_desc;
|
||||
op_decl.insert_into_op_dir(&mut module.op_dir);
|
||||
}
|
||||
}
|
||||
RetractionRecord::AddedModulePredicate(module_name, key) => {
|
||||
match self.wam_prelude.indices.modules.get_mut(&module_name) {
|
||||
Some(ref mut module) => {
|
||||
module.code_dir.remove(&key);
|
||||
}
|
||||
None => {}
|
||||
if let Some(ref mut module) =
|
||||
self.wam_prelude.indices.modules.get_mut(&module_name)
|
||||
{
|
||||
module.code_dir.swap_remove(&key);
|
||||
}
|
||||
}
|
||||
RetractionRecord::ReplacedModulePredicate(module_name, key, old_code_idx) => {
|
||||
match self.wam_prelude.indices.modules.get_mut(&module_name) {
|
||||
Some(ref mut module) => {
|
||||
module
|
||||
.code_dir
|
||||
.get_mut(&key)
|
||||
.map(|code_idx| code_idx.set(old_code_idx));
|
||||
if let Some(ref mut module) =
|
||||
self.wam_prelude.indices.modules.get_mut(&module_name)
|
||||
{
|
||||
if let Some(code_idx) = module.code_dir.get_mut(&key) {
|
||||
code_idx.set(old_code_idx)
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
RetractionRecord::AddedExtensiblePredicate(compilation_target, key) => {
|
||||
@@ -755,14 +737,12 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
op_decl.insert_into_op_dir(&mut self.wam_prelude.indices.op_dir);
|
||||
}
|
||||
RetractionRecord::AddedUserPredicate(key) => {
|
||||
self.wam_prelude.indices.code_dir.remove(&key);
|
||||
self.wam_prelude.indices.code_dir.swap_remove(&key);
|
||||
}
|
||||
RetractionRecord::ReplacedUserPredicate(key, old_code_idx) => {
|
||||
self.wam_prelude
|
||||
.indices
|
||||
.code_dir
|
||||
.get_mut(&key)
|
||||
.map(|code_idx| code_idx.set(old_code_idx));
|
||||
if let Some(code_idx) = self.wam_prelude.indices.code_dir.get_mut(&key) {
|
||||
code_idx.set(old_code_idx)
|
||||
}
|
||||
}
|
||||
RetractionRecord::AddedIndex(index_key, clause_loc) => {
|
||||
if let Some(index_loc) = index_key.switch_on_term_loc() {
|
||||
@@ -832,20 +812,17 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
};
|
||||
}
|
||||
RetractionRecord::ReplacedSwitchOnTermVarIndex(index_loc, old_v) => {
|
||||
match self.wam_prelude.code[index_loc] {
|
||||
Instruction::IndexingCode(ref mut indexing_code) => {
|
||||
match &mut indexing_code[0] {
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(
|
||||
_,
|
||||
ref mut v,
|
||||
..,
|
||||
)) => {
|
||||
*v = old_v;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
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]
|
||||
{
|
||||
*v = old_v;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
RetractionRecord::ModifiedTryMeElse(instr_loc, o) => {
|
||||
@@ -858,30 +835,24 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
self.wam_prelude.code[instr_loc] = Instruction::RevJmpBy(o);
|
||||
}
|
||||
RetractionRecord::SkeletonClausePopBack(compilation_target, key) => {
|
||||
match self
|
||||
if let Some(skeleton) = self
|
||||
.wam_prelude
|
||||
.indices
|
||||
.get_predicate_skeleton_mut(&compilation_target, &key)
|
||||
{
|
||||
Some(skeleton) => {
|
||||
skeleton.clauses.pop_back();
|
||||
skeleton.core.clause_clause_locs.pop_back();
|
||||
}
|
||||
None => {}
|
||||
skeleton.clauses.pop_back();
|
||||
skeleton.core.clause_clause_locs.pop_back();
|
||||
}
|
||||
}
|
||||
RetractionRecord::SkeletonClausePopFront(compilation_target, key) => {
|
||||
match self
|
||||
if let Some(skeleton) = self
|
||||
.wam_prelude
|
||||
.indices
|
||||
.get_predicate_skeleton_mut(&compilation_target, &key)
|
||||
{
|
||||
Some(skeleton) => {
|
||||
skeleton.clauses.pop_front();
|
||||
skeleton.core.clause_clause_locs.pop_front();
|
||||
skeleton.core.clause_assert_margin -= 1;
|
||||
}
|
||||
None => {}
|
||||
skeleton.clauses.pop_front();
|
||||
skeleton.core.clause_clause_locs.pop_front();
|
||||
skeleton.core.clause_assert_margin -= 1;
|
||||
}
|
||||
}
|
||||
RetractionRecord::SkeletonLocalClauseClausePopFront(
|
||||
@@ -891,16 +862,15 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
) => {
|
||||
let listing_src_file_name = self.listing_src_file_name();
|
||||
|
||||
match self.wam_prelude.indices.get_local_predicate_skeleton_mut(
|
||||
src_compilation_target,
|
||||
local_compilation_target,
|
||||
listing_src_file_name,
|
||||
key,
|
||||
) {
|
||||
Some(skeleton) => {
|
||||
skeleton.clause_clause_locs.pop_front();
|
||||
}
|
||||
None => {}
|
||||
if let Some(skeleton) =
|
||||
self.wam_prelude.indices.get_local_predicate_skeleton_mut(
|
||||
src_compilation_target,
|
||||
local_compilation_target,
|
||||
listing_src_file_name,
|
||||
key,
|
||||
)
|
||||
{
|
||||
skeleton.clause_clause_locs.pop_front();
|
||||
}
|
||||
}
|
||||
RetractionRecord::SkeletonLocalClauseClausePopBack(
|
||||
@@ -910,16 +880,15 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
) => {
|
||||
let listing_src_file_name = self.listing_src_file_name();
|
||||
|
||||
match self.wam_prelude.indices.get_local_predicate_skeleton_mut(
|
||||
src_compilation_target,
|
||||
local_compilation_target,
|
||||
listing_src_file_name,
|
||||
key,
|
||||
) {
|
||||
Some(skeleton) => {
|
||||
skeleton.clause_clause_locs.pop_back();
|
||||
}
|
||||
None => {}
|
||||
if let Some(skeleton) =
|
||||
self.wam_prelude.indices.get_local_predicate_skeleton_mut(
|
||||
src_compilation_target,
|
||||
local_compilation_target,
|
||||
listing_src_file_name,
|
||||
key,
|
||||
)
|
||||
{
|
||||
skeleton.clause_clause_locs.pop_back();
|
||||
}
|
||||
}
|
||||
RetractionRecord::SkeletonLocalClauseTruncateBack(
|
||||
@@ -930,29 +899,25 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
) => {
|
||||
let listing_src_file_name = self.listing_src_file_name();
|
||||
|
||||
match self.wam_prelude.indices.get_local_predicate_skeleton_mut(
|
||||
src_compilation_target,
|
||||
local_compilation_target,
|
||||
listing_src_file_name,
|
||||
key,
|
||||
) {
|
||||
Some(skeleton) => {
|
||||
skeleton.clause_clause_locs.truncate(len);
|
||||
}
|
||||
None => {}
|
||||
if let Some(skeleton) =
|
||||
self.wam_prelude.indices.get_local_predicate_skeleton_mut(
|
||||
src_compilation_target,
|
||||
local_compilation_target,
|
||||
listing_src_file_name,
|
||||
key,
|
||||
)
|
||||
{
|
||||
skeleton.clause_clause_locs.truncate(len);
|
||||
}
|
||||
}
|
||||
RetractionRecord::SkeletonClauseTruncateBack(compilation_target, key, len) => {
|
||||
match self
|
||||
if let Some(skeleton) = self
|
||||
.wam_prelude
|
||||
.indices
|
||||
.get_predicate_skeleton_mut(&compilation_target, &key)
|
||||
{
|
||||
Some(skeleton) => {
|
||||
skeleton.clauses.truncate(len);
|
||||
skeleton.core.clause_clause_locs.truncate(len);
|
||||
}
|
||||
None => {}
|
||||
skeleton.clauses.truncate(len);
|
||||
skeleton.core.clause_clause_locs.truncate(len);
|
||||
}
|
||||
}
|
||||
RetractionRecord::SkeletonClauseStartReplaced(
|
||||
@@ -961,15 +926,12 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
target_pos,
|
||||
clause_start,
|
||||
) => {
|
||||
match self
|
||||
if let Some(skeleton) = self
|
||||
.wam_prelude
|
||||
.indices
|
||||
.get_predicate_skeleton_mut(&compilation_target, &key)
|
||||
{
|
||||
Some(skeleton) => {
|
||||
skeleton.clauses[target_pos].clause_start = clause_start;
|
||||
}
|
||||
None => {}
|
||||
skeleton.clauses[target_pos].clause_start = clause_start;
|
||||
}
|
||||
}
|
||||
RetractionRecord::RemovedDynamicSkeletonClause(
|
||||
@@ -978,26 +940,22 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
target_pos,
|
||||
clause_clause_loc,
|
||||
) => {
|
||||
match self
|
||||
if let Some(skeleton) = self
|
||||
.wam_prelude
|
||||
.indices
|
||||
.get_predicate_skeleton_mut(&compilation_target, &key)
|
||||
{
|
||||
Some(skeleton) => {
|
||||
if let Some(removed_clauses) =
|
||||
&mut skeleton.core.retracted_dynamic_clauses
|
||||
{
|
||||
let clause_index_info = removed_clauses.pop().unwrap();
|
||||
if let Some(removed_clauses) = &mut skeleton.core.retracted_dynamic_clauses
|
||||
{
|
||||
let clause_index_info = removed_clauses.pop().unwrap();
|
||||
|
||||
skeleton
|
||||
.core
|
||||
.clause_clause_locs
|
||||
.insert(target_pos, clause_clause_loc);
|
||||
skeleton
|
||||
.core
|
||||
.clause_clause_locs
|
||||
.insert(target_pos, clause_clause_loc);
|
||||
|
||||
skeleton.clauses.insert(target_pos, clause_index_info);
|
||||
}
|
||||
skeleton.clauses.insert(target_pos, clause_index_info);
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
RetractionRecord::RemovedSkeletonClause(
|
||||
@@ -1007,19 +965,16 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
clause_index_info,
|
||||
clause_clause_loc,
|
||||
) => {
|
||||
match self
|
||||
if let Some(skeleton) = self
|
||||
.wam_prelude
|
||||
.indices
|
||||
.get_predicate_skeleton_mut(&compilation_target, &key)
|
||||
{
|
||||
Some(skeleton) => {
|
||||
skeleton
|
||||
.core
|
||||
.clause_clause_locs
|
||||
.insert(target_pos, clause_clause_loc);
|
||||
skeleton.clauses.insert(target_pos, clause_index_info);
|
||||
}
|
||||
None => {}
|
||||
skeleton
|
||||
.core
|
||||
.clause_clause_locs
|
||||
.insert(target_pos, clause_clause_loc);
|
||||
skeleton.clauses.insert(target_pos, clause_index_info);
|
||||
}
|
||||
}
|
||||
RetractionRecord::ReplacedIndexingLine(index_loc, indexing_code) => {
|
||||
@@ -1033,14 +988,15 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
) => {
|
||||
let listing_src_file_name = self.listing_src_file_name();
|
||||
|
||||
match self.wam_prelude.indices.get_local_predicate_skeleton_mut(
|
||||
compilation_target,
|
||||
local_compilation_target,
|
||||
listing_src_file_name,
|
||||
key,
|
||||
) {
|
||||
Some(skeleton) => skeleton.clause_clause_locs = clause_locs,
|
||||
None => {}
|
||||
if let Some(skeleton) =
|
||||
self.wam_prelude.indices.get_local_predicate_skeleton_mut(
|
||||
compilation_target,
|
||||
local_compilation_target,
|
||||
listing_src_file_name,
|
||||
key,
|
||||
)
|
||||
{
|
||||
skeleton.clause_clause_locs = clause_locs
|
||||
}
|
||||
}
|
||||
RetractionRecord::RemovedSkeleton(compilation_target, key, skeleton) => {
|
||||
@@ -1091,7 +1047,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
|
||||
let export_list = machine_st.read_term_from_heap(cell);
|
||||
let atom_tbl = &mut LS::machine_st(&mut self.payload).atom_tbl;
|
||||
let export_list = setup_module_export_list(export_list, &atom_tbl)?;
|
||||
let export_list = setup_module_export_list(export_list, atom_tbl)?;
|
||||
|
||||
Ok(export_list.into_iter().collect())
|
||||
}
|
||||
@@ -1363,7 +1319,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
*key,
|
||||
) {
|
||||
Some(skeleton) if !skeleton.clause_clause_locs.is_empty() => {
|
||||
mem::replace(&mut skeleton.clause_clause_locs, VecDeque::new())
|
||||
std::mem::take(&mut skeleton.clause_clause_locs)
|
||||
}
|
||||
_ => return,
|
||||
};
|
||||
@@ -1400,9 +1356,7 @@ impl<'a> MachinePreludeView<'a> {
|
||||
CompilationTarget::User => CompositeOpDir::new(&self.indices.op_dir, None),
|
||||
CompilationTarget::Module(ref module_name) => {
|
||||
match self.indices.modules.get(module_name) {
|
||||
Some(ref module) => {
|
||||
CompositeOpDir::new(&self.indices.op_dir, Some(&module.op_dir))
|
||||
}
|
||||
Some(module) => CompositeOpDir::new(&self.indices.op_dir, Some(&module.op_dir)),
|
||||
None => {
|
||||
unreachable!()
|
||||
}
|
||||
@@ -1413,13 +1367,10 @@ impl<'a> MachinePreludeView<'a> {
|
||||
}
|
||||
|
||||
impl MachineState {
|
||||
pub(super) fn read_term_from_heap(
|
||||
&mut self,
|
||||
term_addr: HeapCellValue,
|
||||
) -> Term {
|
||||
pub(super) fn read_term_from_heap(&mut self, term_addr: HeapCellValue) -> Term {
|
||||
let mut term_stack = vec![];
|
||||
let mut iter = stackful_post_order_iter::<NonListElider>
|
||||
(&mut self.heap, &mut self.stack, term_addr);
|
||||
let mut iter =
|
||||
stackful_post_order_iter::<NonListElider>(&mut self.heap, &mut self.stack, term_addr);
|
||||
|
||||
while let Some(addr) = iter.next() {
|
||||
let addr = unmark_cell_bits!(addr);
|
||||
@@ -1652,10 +1603,10 @@ impl Machine {
|
||||
|
||||
let arity = self.deref_register(3);
|
||||
let arity = match Number::try_from(arity) {
|
||||
Ok(Number::Integer(n)) if &*n >= &Integer::ZERO && &*n <= &Integer::from(MAX_ARITY) => {
|
||||
Ok(Number::Integer(n)) if *n >= Integer::ZERO && *n <= Integer::from(MAX_ARITY) => {
|
||||
let value: usize = (&*n).try_into().unwrap();
|
||||
Ok(value)
|
||||
},
|
||||
}
|
||||
Ok(Number::Fixnum(n)) if n.get_num() >= 0 && n.get_num() <= MAX_ARITY as i64 => {
|
||||
Ok(usize::try_from(n.get_num()).unwrap())
|
||||
}
|
||||
@@ -1770,14 +1721,11 @@ impl Machine {
|
||||
&ListingSource::DynamicallyGenerated,
|
||||
);
|
||||
|
||||
match loader.wam_prelude.indices.modules.get_mut(&module_name) {
|
||||
Some(module) => {
|
||||
for (key, value) in module.op_dir.drain(0..) {
|
||||
let mut op_decl = OpDecl::new(value, key.0);
|
||||
op_decl.remove(&mut loader.wam_prelude.indices.op_dir);
|
||||
}
|
||||
if let Some(module) = loader.wam_prelude.indices.modules.get_mut(&module_name) {
|
||||
for (key, value) in module.op_dir.drain(0..) {
|
||||
let mut op_decl = OpDecl::new(value, key.0);
|
||||
op_decl.remove(&mut loader.wam_prelude.indices.op_dir);
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1789,10 +1737,10 @@ impl Machine {
|
||||
self.restore_load_state_payload(result)
|
||||
}
|
||||
|
||||
pub(crate) fn loader_from_heap_evacuable<'a>(
|
||||
&'a mut self,
|
||||
pub(crate) fn loader_from_heap_evacuable(
|
||||
&mut self,
|
||||
r: RegType,
|
||||
) -> Loader<'a, LiveLoadAndMachineState<'a>> {
|
||||
) -> Loader<'_, LiveLoadAndMachineState<'_>> {
|
||||
let mut load_state = cell_as_load_state_payload!(self
|
||||
.machine_st
|
||||
.store(self.machine_st.deref(self.machine_st[r])));
|
||||
@@ -1812,7 +1760,7 @@ impl Machine {
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn push_load_state_payload(&mut self) {
|
||||
let payload = arena_alloc!(
|
||||
let payload: TypedArenaPtr<LiveLoadState> = arena_alloc!(
|
||||
LoadStatePayload::new(self.code.len(), LiveTermStream::new(ListingSource::User),),
|
||||
&mut self.machine_st.arena
|
||||
);
|
||||
@@ -1839,11 +1787,8 @@ impl Machine {
|
||||
(HeapCellValueTag::Cons, cons_ptr) => {
|
||||
match_untyped_arena_ptr!(cons_ptr,
|
||||
(ArenaHeaderTag::LiveLoadState, payload) => {
|
||||
unsafe {
|
||||
std::ptr::drop_in_place(
|
||||
payload.as_ptr() as *mut LiveLoadState,
|
||||
);
|
||||
}
|
||||
let mut payload = payload;
|
||||
payload.drop_payload()
|
||||
}
|
||||
_ => {}
|
||||
);
|
||||
@@ -1868,7 +1813,7 @@ impl Machine {
|
||||
let path = cell_as_atom!(self.deref_register(2));
|
||||
|
||||
self.load_contexts
|
||||
.push(LoadContext::new(&*path.as_str(), stream));
|
||||
.push(LoadContext::new(&path.as_str(), stream));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1888,9 +1833,33 @@ impl Machine {
|
||||
}
|
||||
|
||||
pub(crate) fn scoped_clause_to_evacuable(&mut self) -> CallResult {
|
||||
let module_name = cell_as_atom!(self
|
||||
.machine_st
|
||||
.store(self.machine_st.deref(self.machine_st.registers[1])));
|
||||
let target = self.deref_register(1);
|
||||
|
||||
let mut permission_error = || {
|
||||
let err = self.machine_st.permission_error(
|
||||
Permission::Modify,
|
||||
atom!("static_procedure"),
|
||||
functor_stub(atom!(":"), 2)
|
||||
.into_iter()
|
||||
.collect::<MachineStub>(),
|
||||
);
|
||||
|
||||
self.machine_st
|
||||
.error_form(err, functor_stub(atom!("load"), 1))
|
||||
};
|
||||
|
||||
let module_name = read_heap_cell!(target,
|
||||
(HeapCellValueTag::Atom, (name, arity)) => {
|
||||
if arity == 0 {
|
||||
name
|
||||
} else {
|
||||
return Err(permission_error());
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return Err(permission_error());
|
||||
}
|
||||
);
|
||||
|
||||
let loader = self.loader_from_heap_evacuable(temp_v!(3));
|
||||
|
||||
@@ -2003,11 +1972,13 @@ impl Machine {
|
||||
_ => CompilationTarget::Module(module_name),
|
||||
};
|
||||
|
||||
let stub_gen = || match append_or_prepend {
|
||||
AppendOrPrepend::Append => functor_stub(atom!("assertz"), 1),
|
||||
AppendOrPrepend::Prepend => functor_stub(atom!("asserta"), 1),
|
||||
let key = match append_or_prepend {
|
||||
AppendOrPrepend::Append => (atom!("assertz"), 1),
|
||||
AppendOrPrepend::Prepend => (atom!("asserta"), 1),
|
||||
};
|
||||
|
||||
let stub_gen = || functor_stub(key.0, key.1);
|
||||
|
||||
let head = self.deref_register(2);
|
||||
|
||||
if head.is_var() {
|
||||
@@ -2021,8 +1992,8 @@ impl Machine {
|
||||
|
||||
loader.payload.compilation_target = compilation_target;
|
||||
|
||||
let head = LiveLoadAndMachineState::machine_st(&mut loader.payload)
|
||||
.read_term_from_heap(head);
|
||||
let head =
|
||||
LiveLoadAndMachineState::machine_st(&mut loader.payload).read_term_from_heap(head);
|
||||
|
||||
let name = if let Some(name) = head.name() {
|
||||
name
|
||||
@@ -2046,7 +2017,11 @@ impl Machine {
|
||||
.map(|code_idx| code_idx.get_tag())
|
||||
.unwrap_or(IndexPtrTag::DynamicUndefined);
|
||||
|
||||
idx_tag == IndexPtrTag::DynamicUndefined || idx_tag == IndexPtrTag::Undefined
|
||||
if idx_tag == IndexPtrTag::Index {
|
||||
return Err(SessionError::CannotOverwriteStaticProcedure((name, arity)));
|
||||
} else {
|
||||
idx_tag == IndexPtrTag::Undefined || idx_tag == IndexPtrTag::DynamicUndefined
|
||||
}
|
||||
} else if is_builtin {
|
||||
return Err(SessionError::CannotOverwriteBuiltIn((name, arity)));
|
||||
} else {
|
||||
@@ -2218,7 +2193,7 @@ impl Machine {
|
||||
Ok(Number::Integer(n)) => {
|
||||
let value: usize = (&*n).try_into().unwrap();
|
||||
value
|
||||
},
|
||||
}
|
||||
Ok(Number::Fixnum(n)) => usize::try_from(n.get_num()).unwrap(),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
@@ -2520,7 +2495,7 @@ pub(super) fn load_module(
|
||||
|
||||
import_module_exports::<LiveLoadAndMachineState>(
|
||||
&mut payload,
|
||||
&compilation_target,
|
||||
compilation_target,
|
||||
module,
|
||||
code_dir,
|
||||
op_dir,
|
||||
|
||||
@@ -80,7 +80,7 @@ impl ValidType {
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub(crate) enum ResourceError {
|
||||
FiniteMemory(HeapCellValue),
|
||||
OutOfFiles
|
||||
OutOfFiles,
|
||||
}
|
||||
|
||||
pub(crate) trait TypeError {
|
||||
@@ -170,7 +170,11 @@ impl PermissionError for Atom {
|
||||
) -> MachineError {
|
||||
let stub = functor!(
|
||||
atom!("permission_error"),
|
||||
[atom(perm.as_atom()), atom(index_atom), cell(atom_as_cell!(self))]
|
||||
[
|
||||
atom(perm.as_atom()),
|
||||
atom(index_atom),
|
||||
cell(atom_as_cell!(self))
|
||||
]
|
||||
);
|
||||
|
||||
MachineError {
|
||||
@@ -262,6 +266,26 @@ impl DomainError for HeapCellValue {
|
||||
}
|
||||
}
|
||||
|
||||
impl DomainError for FunctorStub {
|
||||
fn domain_error(
|
||||
self,
|
||||
machine_st: &mut MachineState,
|
||||
valid_type: DomainErrorType,
|
||||
) -> MachineError {
|
||||
let stub = functor!(
|
||||
atom!("domain_error"),
|
||||
[atom(valid_type.as_atom()), str(machine_st.heap.len(), 0)],
|
||||
[self]
|
||||
);
|
||||
|
||||
MachineError {
|
||||
stub,
|
||||
location: None,
|
||||
from: ErrorProvenance::Constructed,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DomainError for Number {
|
||||
fn domain_error(self, machine_st: &mut MachineState, error: DomainErrorType) -> MachineError {
|
||||
let stub = functor!(
|
||||
@@ -319,10 +343,7 @@ impl MachineState {
|
||||
)
|
||||
}
|
||||
ResourceError::OutOfFiles => {
|
||||
functor!(
|
||||
atom!("resource_error"),
|
||||
[atom(atom!("file_descriptors"))]
|
||||
)
|
||||
functor!(atom!("resource_error"), [atom(atom!("file_descriptors"))])
|
||||
}
|
||||
};
|
||||
|
||||
@@ -355,13 +376,21 @@ impl MachineState {
|
||||
from: ErrorProvenance::Received,
|
||||
}
|
||||
}
|
||||
ExistenceError::QualifiedProcedure { module_name, name, arity } => {
|
||||
ExistenceError::QualifiedProcedure {
|
||||
module_name,
|
||||
name,
|
||||
arity,
|
||||
} => {
|
||||
let h = self.heap.len();
|
||||
|
||||
let ind_stub = functor!(atom!("/"), [atom(name), fixnum(arity)]);
|
||||
let res_stub = functor!(atom!(":"), [atom(module_name), str(h + 3, 0)], [ind_stub]);
|
||||
|
||||
let stub = functor!(atom!("existence_error"), [atom(atom!("procedure")), str(h, 0)], [res_stub]);
|
||||
let stub = functor!(
|
||||
atom!("existence_error"),
|
||||
[atom(atom!("procedure")), str(h, 0)],
|
||||
[res_stub]
|
||||
);
|
||||
|
||||
MachineError {
|
||||
stub,
|
||||
@@ -472,31 +501,34 @@ impl MachineState {
|
||||
|
||||
pub(super) fn session_error(&mut self, err: SessionError) -> MachineError {
|
||||
match err {
|
||||
SessionError::CannotOverwriteBuiltIn(key) => {
|
||||
self.permission_error(
|
||||
Permission::Modify,
|
||||
atom!("static_procedure"),
|
||||
functor_stub(key.0, key.1)
|
||||
.into_iter()
|
||||
.collect::<MachineStub>(),
|
||||
)
|
||||
}
|
||||
SessionError::CannotOverwriteBuiltIn(key) => self.permission_error(
|
||||
Permission::Modify,
|
||||
atom!("static_procedure"),
|
||||
functor_stub(key.0, key.1)
|
||||
.into_iter()
|
||||
.collect::<MachineStub>(),
|
||||
),
|
||||
SessionError::CannotOverwriteStaticProcedure(key) => self.permission_error(
|
||||
Permission::Modify,
|
||||
atom!("static_procedure"),
|
||||
functor_stub(key.0, key.1)
|
||||
.into_iter()
|
||||
.collect::<MachineStub>(),
|
||||
),
|
||||
SessionError::CannotOverwriteBuiltInModule(module) => {
|
||||
self.permission_error(
|
||||
Permission::Modify,
|
||||
atom!("static_module"),
|
||||
module,
|
||||
)
|
||||
self.permission_error(Permission::Modify, atom!("static_module"), module)
|
||||
}
|
||||
SessionError::ExistenceError(err) => self.existence_error(err),
|
||||
SessionError::ModuleDoesNotContainExport(..) => {
|
||||
let error_atom = atom!("module_does_not_contain_claimed_export");
|
||||
SessionError::ModuleDoesNotContainExport(module_name, key) => {
|
||||
let functor_stub = functor_stub(key.0, key.1);
|
||||
|
||||
self.permission_error(
|
||||
Permission::Access,
|
||||
atom!("private_procedure"),
|
||||
functor!(error_atom),
|
||||
)
|
||||
let stub = functor!(
|
||||
atom!("module_does_not_contain_claimed_export"),
|
||||
[atom(module_name), str(self.heap.len() + 4, 0)],
|
||||
[functor_stub]
|
||||
);
|
||||
|
||||
self.permission_error(Permission::Access, atom!("private_procedure"), stub)
|
||||
}
|
||||
SessionError::ModuleCannotImportSelf(module_name) => {
|
||||
let error_atom = atom!("module_cannot_import_self");
|
||||
@@ -540,15 +572,6 @@ impl MachineState {
|
||||
stub,
|
||||
)
|
||||
}
|
||||
SessionError::QueryCannotBeDefinedAsFact => {
|
||||
let error_atom = atom!("query_cannot_be_defined_as_fact");
|
||||
|
||||
self.permission_error(
|
||||
Permission::Create,
|
||||
atom!("static_procedure"),
|
||||
functor!(error_atom),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -559,10 +582,15 @@ impl MachineState {
|
||||
return self.arithmetic_error(err);
|
||||
}
|
||||
|
||||
if let CompilationError::InvalidDirective(err) = err {
|
||||
return self.directive_error(err);
|
||||
}
|
||||
|
||||
let location = err.line_and_col_num();
|
||||
let len = self.heap.len();
|
||||
let stub = err.as_functor();
|
||||
|
||||
let stub = functor!(atom!("syntax_error"), [str(self.heap.len(), 0)], [stub]);
|
||||
let stub = functor!(atom!("syntax_error"), [str(len, 0)], [stub]);
|
||||
|
||||
MachineError {
|
||||
stub,
|
||||
@@ -641,7 +669,7 @@ impl MachineState {
|
||||
self.ball.boundary = 0;
|
||||
self.ball.stub.truncate(0);
|
||||
|
||||
self.heap.extend(err.into_iter());
|
||||
self.heap.extend(err);
|
||||
|
||||
self.registers[1] = if err_len == 1 {
|
||||
heap_loc_as_cell!(h)
|
||||
@@ -673,19 +701,29 @@ impl MachineError {
|
||||
pub enum CompilationError {
|
||||
Arithmetic(ArithmeticError),
|
||||
ParserError(ParserError),
|
||||
CannotParseCyclicTerm,
|
||||
ExceededMaxArity,
|
||||
ExpectedRel,
|
||||
InadmissibleFact,
|
||||
InadmissibleQueryTerm,
|
||||
InconsistentEntry,
|
||||
InvalidDirective(DirectiveError),
|
||||
InvalidMetaPredicateDecl,
|
||||
InvalidModuleDecl,
|
||||
InvalidModuleExport,
|
||||
InvalidRuleHead,
|
||||
InvalidUseModuleDecl,
|
||||
InvalidModuleResolution(Atom),
|
||||
UnreadableTerm,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum DirectiveError {
|
||||
ExpectedDirective(Term),
|
||||
InvalidDirective(Atom, usize /* arity */),
|
||||
InvalidOpDeclNameType(Term),
|
||||
InvalidOpDeclSpecDomain(Term),
|
||||
InvalidOpDeclSpecValue(Atom),
|
||||
InvalidOpDeclPrecType(Term),
|
||||
InvalidOpDeclPrecDomain(Fixnum),
|
||||
ShallNotCreate(Atom),
|
||||
ShallNotModify(Atom),
|
||||
}
|
||||
|
||||
impl From<ArithmeticError> for CompilationError {
|
||||
@@ -705,60 +743,51 @@ impl From<ParserError> for CompilationError {
|
||||
impl CompilationError {
|
||||
pub(crate) fn line_and_col_num(&self) -> Option<(usize, usize)> {
|
||||
match self {
|
||||
&CompilationError::ParserError(ref err) => err.line_and_col_num(),
|
||||
CompilationError::ParserError(err) => err.line_and_col_num(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn as_functor(&self) -> MachineStub {
|
||||
match self {
|
||||
&CompilationError::Arithmetic(..) => {
|
||||
CompilationError::Arithmetic(..) => {
|
||||
functor!(atom!("arithmetic_error"))
|
||||
}
|
||||
&CompilationError::CannotParseCyclicTerm => {
|
||||
functor!(atom!("cannot_parse_cyclic_term"))
|
||||
}
|
||||
&CompilationError::ExceededMaxArity => {
|
||||
CompilationError::ExceededMaxArity => {
|
||||
functor!(atom!("exceeded_max_arity"))
|
||||
}
|
||||
&CompilationError::ExpectedRel => {
|
||||
functor!(atom!("expected_relation"))
|
||||
}
|
||||
&CompilationError::InadmissibleFact => {
|
||||
CompilationError::InadmissibleFact => {
|
||||
// TODO: type_error(callable, _).
|
||||
functor!(atom!("inadmissible_fact"))
|
||||
}
|
||||
&CompilationError::InadmissibleQueryTerm => {
|
||||
CompilationError::InadmissibleQueryTerm => {
|
||||
// TODO: type_error(callable, _).
|
||||
functor!(atom!("inadmissible_query_term"))
|
||||
}
|
||||
&CompilationError::InconsistentEntry => {
|
||||
functor!(atom!("inconsistent_entry"))
|
||||
CompilationError::InvalidDirective(_) => {
|
||||
functor!(atom!("directive_error"))
|
||||
}
|
||||
&CompilationError::InvalidMetaPredicateDecl => {
|
||||
CompilationError::InvalidMetaPredicateDecl => {
|
||||
functor!(atom!("invalid_meta_predicate_decl"))
|
||||
}
|
||||
&CompilationError::InvalidModuleDecl => {
|
||||
CompilationError::InvalidModuleDecl => {
|
||||
functor!(atom!("invalid_module_declaration"))
|
||||
}
|
||||
&CompilationError::InvalidModuleExport => {
|
||||
CompilationError::InvalidModuleExport => {
|
||||
functor!(atom!("invalid_module_export"))
|
||||
}
|
||||
&CompilationError::InvalidModuleResolution(ref module_name) => {
|
||||
CompilationError::InvalidModuleResolution(ref module_name) => {
|
||||
functor!(atom!("no_such_module"), [atom(module_name)])
|
||||
}
|
||||
&CompilationError::InvalidRuleHead => {
|
||||
CompilationError::InvalidRuleHead => {
|
||||
functor!(atom!("invalid_head_of_rule")) // TODO: type_error(callable, _).
|
||||
}
|
||||
&CompilationError::InvalidUseModuleDecl => {
|
||||
CompilationError::InvalidUseModuleDecl => {
|
||||
functor!(atom!("invalid_use_module_declaration"))
|
||||
}
|
||||
&CompilationError::ParserError(ref err) => {
|
||||
CompilationError::ParserError(ref err) => {
|
||||
functor!(err.as_atom())
|
||||
}
|
||||
&CompilationError::UnreadableTerm => {
|
||||
functor!(atom!("unreadable_term"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -797,6 +826,9 @@ pub(crate) enum DomainErrorType {
|
||||
SourceSink,
|
||||
Stream,
|
||||
StreamOrAlias,
|
||||
OperatorSpecifier,
|
||||
OperatorPriority,
|
||||
Directive,
|
||||
}
|
||||
|
||||
impl DomainErrorType {
|
||||
@@ -808,6 +840,9 @@ impl DomainErrorType {
|
||||
DomainErrorType::SourceSink => atom!("source_sink"),
|
||||
DomainErrorType::Stream => atom!("stream"),
|
||||
DomainErrorType::StreamOrAlias => atom!("stream_or_alias"),
|
||||
DomainErrorType::OperatorSpecifier => atom!("operator_specifier"),
|
||||
DomainErrorType::OperatorPriority => atom!("operator_priority"),
|
||||
DomainErrorType::Directive => atom!("directive"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -986,7 +1021,11 @@ pub enum ExistenceError {
|
||||
Module(Atom),
|
||||
ModuleSource(ModuleSource),
|
||||
Procedure(Atom, usize),
|
||||
QualifiedProcedure { module_name: Atom, name: Atom, arity: usize },
|
||||
QualifiedProcedure {
|
||||
module_name: Atom,
|
||||
name: Atom,
|
||||
arity: usize,
|
||||
},
|
||||
SourceSink(HeapCellValue),
|
||||
Stream(HeapCellValue),
|
||||
}
|
||||
@@ -996,26 +1035,13 @@ pub enum SessionError {
|
||||
CompilationError(CompilationError),
|
||||
CannotOverwriteBuiltIn(PredicateKey),
|
||||
CannotOverwriteBuiltInModule(Atom),
|
||||
CannotOverwriteStaticProcedure(PredicateKey),
|
||||
ExistenceError(ExistenceError),
|
||||
ModuleDoesNotContainExport(Atom, PredicateKey),
|
||||
ModuleCannotImportSelf(Atom),
|
||||
NamelessEntry,
|
||||
OpIsInfixAndPostFix(Atom),
|
||||
PredicateNotMultifileOrDiscontiguous(CompilationTarget, PredicateKey),
|
||||
QueryCannotBeDefinedAsFact,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum EvalSession {
|
||||
// EntrySuccess,
|
||||
Error(SessionError),
|
||||
}
|
||||
|
||||
impl From<SessionError> for EvalSession {
|
||||
#[inline]
|
||||
fn from(err: SessionError) -> Self {
|
||||
EvalSession::Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for SessionError {
|
||||
@@ -1038,10 +1064,3 @@ impl From<CompilationError> for SessionError {
|
||||
SessionError::CompilationError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ParserError> for EvalSession {
|
||||
#[inline]
|
||||
fn from(err: ParserError) -> Self {
|
||||
EvalSession::from(SessionError::from(err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work
|
||||
|
||||
use crate::parser::ast::*;
|
||||
|
||||
use crate::arena::*;
|
||||
@@ -10,16 +12,14 @@ use crate::machine::ClauseType;
|
||||
|
||||
use fxhash::FxBuildHasher;
|
||||
use indexmap::{IndexMap, IndexSet};
|
||||
use modular_bitfield::specifiers::*;
|
||||
use modular_bitfield::{bitfield, BitfieldSpecifier};
|
||||
use scryer_modular_bitfield::specifiers::*;
|
||||
use scryer_modular_bitfield::{bitfield, BitfieldSpecifier};
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::BTreeSet;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use crate::types::*;
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub(crate) struct OrderedOpDirKey(pub(crate) Atom, pub(crate) Fixity);
|
||||
|
||||
// 7.2
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
@@ -118,18 +118,12 @@ impl IndexPtr {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn is_undefined(&self) -> bool {
|
||||
match self.tag() {
|
||||
IndexPtrTag::Undefined => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self.tag(), IndexPtrTag::Undefined)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn is_dynamic_undefined(&self) -> bool {
|
||||
match self.tag() {
|
||||
IndexPtrTag::DynamicUndefined => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self.tag(), IndexPtrTag::DynamicUndefined)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,13 +159,6 @@ impl From<CodeIndex> for UntypedArenaPtr {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<UntypedArenaPtr> for CodeIndex {
|
||||
#[inline(always)]
|
||||
fn from(ptr: UntypedArenaPtr) -> CodeIndex {
|
||||
CodeIndex(TypedArenaPtr::new(ptr.get_ptr() as *mut IndexPtr))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TypedArenaPtr<IndexPtr>> for CodeIndex {
|
||||
#[inline(always)]
|
||||
fn from(ptr: TypedArenaPtr<IndexPtr>) -> CodeIndex {
|
||||
@@ -231,6 +218,7 @@ pub enum VarKey {
|
||||
}
|
||||
|
||||
impl VarKey {
|
||||
#[allow(clippy::inherent_to_string)]
|
||||
#[inline]
|
||||
pub(crate) fn to_string(&self) -> String {
|
||||
match self {
|
||||
@@ -241,11 +229,7 @@ impl VarKey {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn is_anon(&self) -> bool {
|
||||
if let VarKey::AnonVar(_) = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
matches!(self, VarKey::AnonVar(_))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,8 +280,27 @@ impl IndexStore {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn goal_expansion_defined(&self, key: PredicateKey) -> bool {
|
||||
self.goal_expansion_indices.contains(&key)
|
||||
pub(crate) fn goal_expansion_defined(&self, key: PredicateKey, module_name: Atom) -> bool {
|
||||
let compilation_target = match module_name {
|
||||
atom!("user") => CompilationTarget::User,
|
||||
_ => CompilationTarget::Module(module_name),
|
||||
};
|
||||
|
||||
match key {
|
||||
_ if self.goal_expansion_indices.contains(&key) => true,
|
||||
_ => self
|
||||
.get_meta_predicate_spec(key.0, key.1, &compilation_target)
|
||||
.map(|meta_specs| {
|
||||
meta_specs.iter().find(|meta_spec| {
|
||||
matches!(
|
||||
meta_spec,
|
||||
MetaSpec::Colon | MetaSpec::RequiresExpansionWithArgument(_)
|
||||
)
|
||||
})
|
||||
})
|
||||
.map(|meta_spec_opt| meta_spec_opt.is_some())
|
||||
.unwrap_or(false),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_predicate_skeleton_mut(
|
||||
@@ -394,10 +397,10 @@ impl IndexStore {
|
||||
key: &PredicateKey,
|
||||
) -> Option<PredicateSkeleton> {
|
||||
match compilation_target {
|
||||
CompilationTarget::User => self.extensible_predicates.remove(key),
|
||||
CompilationTarget::User => self.extensible_predicates.swap_remove(key),
|
||||
CompilationTarget::Module(ref module_name) => {
|
||||
if let Some(module) = self.modules.get_mut(module_name) {
|
||||
module.extensible_predicates.remove(key)
|
||||
module.extensible_predicates.swap_remove(key)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -429,9 +432,9 @@ impl IndexStore {
|
||||
match compilation_target {
|
||||
CompilationTarget::User => self.meta_predicates.get(&(name, arity)),
|
||||
CompilationTarget::Module(ref module_name) => match self.modules.get(module_name) {
|
||||
Some(ref module) => module
|
||||
Some(module) => module
|
||||
.meta_predicates
|
||||
.get(&(name.clone(), arity))
|
||||
.get(&(name, arity))
|
||||
.or_else(|| self.meta_predicates.get(&(name, arity))),
|
||||
None => self.meta_predicates.get(&(name, arity)),
|
||||
},
|
||||
@@ -446,7 +449,7 @@ impl IndexStore {
|
||||
.map(|skeleton| skeleton.core.is_dynamic)
|
||||
.unwrap_or(false),
|
||||
_ => match self.modules.get(&module_name) {
|
||||
Some(ref module) => module
|
||||
Some(module) => module
|
||||
.extensible_predicates
|
||||
.get(&key)
|
||||
.map(|skeleton| skeleton.core.is_dynamic)
|
||||
|
||||
@@ -96,7 +96,6 @@ pub struct MachineState {
|
||||
pub(crate) unify_fn: fn(&mut MachineState),
|
||||
pub(crate) bind_fn: fn(&mut MachineState, Ref, HeapCellValue),
|
||||
pub(crate) run_cleaners_fn: fn(&mut Machine) -> bool,
|
||||
pub(crate) increment_call_count_fn: fn(&mut MachineState) -> bool,
|
||||
}
|
||||
|
||||
impl fmt::Debug for MachineState {
|
||||
@@ -290,6 +289,11 @@ impl<'a> CopierTarget for CopyTerm<'a> {
|
||||
self.state.heap.push(hcv);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn push_attr_var_queue(&mut self, attr_var_loc: usize) {
|
||||
self.state.attr_var_init.attr_var_queue.push(attr_var_loc);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn store(&self, value: HeapCellValue) -> HeapCellValue {
|
||||
self.state.store(value)
|
||||
@@ -308,6 +312,7 @@ impl<'a> CopierTarget for CopyTerm<'a> {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct CopyBallTerm<'a> {
|
||||
attr_var_queue: &'a mut Vec<usize>,
|
||||
stack: &'a mut Stack,
|
||||
heap: &'a mut Heap,
|
||||
heap_boundary: usize,
|
||||
@@ -315,10 +320,16 @@ pub(super) struct CopyBallTerm<'a> {
|
||||
}
|
||||
|
||||
impl<'a> CopyBallTerm<'a> {
|
||||
pub(super) fn new(stack: &'a mut Stack, heap: &'a mut Heap, stub: &'a mut Heap) -> Self {
|
||||
pub(super) fn new(
|
||||
attr_var_queue: &'a mut Vec<usize>,
|
||||
stack: &'a mut Stack,
|
||||
heap: &'a mut Heap,
|
||||
stub: &'a mut Heap,
|
||||
) -> Self {
|
||||
let hb = heap.len();
|
||||
|
||||
CopyBallTerm {
|
||||
attr_var_queue,
|
||||
stack,
|
||||
heap,
|
||||
heap_boundary: hb,
|
||||
@@ -360,6 +371,11 @@ impl<'a> CopierTarget for CopyBallTerm<'a> {
|
||||
self.stub.push(value);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn push_attr_var_queue(&mut self, attr_var_loc: usize) {
|
||||
self.attr_var_queue.push(attr_var_loc);
|
||||
}
|
||||
|
||||
fn store(&self, value: HeapCellValue) -> HeapCellValue {
|
||||
read_heap_cell!(value,
|
||||
(HeapCellValueTag::Var | HeapCellValueTag::AttrVar, h) => {
|
||||
@@ -413,19 +429,21 @@ impl MachineState {
|
||||
}
|
||||
|
||||
pub(crate) fn increment_call_count(&mut self) -> bool {
|
||||
if self.cwil.inference_limit_exceeded || self.ball.stub.len() > 0 {
|
||||
if self.cwil.inference_limit_exceeded || !self.ball.stub.is_empty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
self.cwil.global_count += 1;
|
||||
|
||||
if let Some(&(ref limit, block)) = self.cwil.limits.last() {
|
||||
if self.cwil.count == *limit {
|
||||
if self.cwil.local_count == *limit {
|
||||
self.cwil.inference_limit_exceeded = true;
|
||||
self.block = block;
|
||||
self.unwind_stack();
|
||||
|
||||
return false;
|
||||
} else {
|
||||
self.cwil.count += 1;
|
||||
self.cwil.local_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -590,7 +608,9 @@ impl MachineState {
|
||||
|
||||
let mut singleton_var_set: IndexMap<Ref, bool> = IndexMap::new();
|
||||
|
||||
for cell in stackful_preorder_iter::<NonListElider>(&mut self.heap, &mut self.stack, heap_loc) {
|
||||
for cell in
|
||||
stackful_preorder_iter::<NonListElider>(&mut self.heap, &mut self.stack, heap_loc)
|
||||
{
|
||||
let cell = unmark_cell_bits!(cell);
|
||||
|
||||
if let Some(var) = cell.as_var() {
|
||||
@@ -644,10 +664,8 @@ impl MachineState {
|
||||
) -> Result<OnEOF, MachineStub> {
|
||||
self.eof_action(self.registers[2], stream, atom!("read_term"), 3)?;
|
||||
|
||||
if stream.options().eof_action() == EOFAction::Reset {
|
||||
if self.fail == false {
|
||||
return Ok(OnEOF::Continue);
|
||||
}
|
||||
if stream.options().eof_action() == EOFAction::Reset && !self.fail {
|
||||
return Ok(OnEOF::Continue);
|
||||
}
|
||||
|
||||
Ok(OnEOF::Return)
|
||||
@@ -661,23 +679,21 @@ impl MachineState {
|
||||
indices: &mut IndexStore,
|
||||
) -> CallResult {
|
||||
if let Stream::Readline(ptr) = stream {
|
||||
unsafe {
|
||||
let readline = ptr.as_ptr().as_mut().unwrap();
|
||||
readline.set_atoms_for_completion(&self.atom_tbl);
|
||||
return self.read_term(
|
||||
stream,
|
||||
indices,
|
||||
MachineState::read_term_from_user_input_eof_handler,
|
||||
);
|
||||
}
|
||||
let readline = unsafe { ptr.as_ptr().as_mut() }.unwrap();
|
||||
readline.set_atoms_for_completion(&self.atom_tbl);
|
||||
return self.read_term(
|
||||
stream,
|
||||
indices,
|
||||
MachineState::read_term_from_user_input_eof_handler,
|
||||
);
|
||||
}
|
||||
|
||||
if let Stream::Byte(_) = stream {
|
||||
return self.read_term(
|
||||
stream,
|
||||
indices,
|
||||
MachineState::read_term_from_user_input_eof_handler
|
||||
)
|
||||
stream,
|
||||
indices,
|
||||
MachineState::read_term_from_user_input_eof_handler,
|
||||
);
|
||||
}
|
||||
|
||||
unreachable!("Stream must be a Stream::Readline(_)")
|
||||
@@ -691,10 +707,8 @@ impl MachineState {
|
||||
} else if stream.past_end_of_stream() {
|
||||
self.eof_action(self.registers[2], stream, atom!("read_term"), 3)?;
|
||||
|
||||
if stream.options().eof_action() == EOFAction::Reset {
|
||||
if self.fail == false {
|
||||
return Ok(OnEOF::Continue);
|
||||
}
|
||||
if stream.options().eof_action() == EOFAction::Reset && !self.fail {
|
||||
return Ok(OnEOF::Continue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -716,11 +730,7 @@ impl MachineState {
|
||||
)?;
|
||||
|
||||
if stream.past_end_of_stream() {
|
||||
if EOFAction::Reset != stream.options().eof_action() {
|
||||
return Ok(());
|
||||
} else if self.fail {
|
||||
return Ok(());
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
loop {
|
||||
@@ -968,11 +978,49 @@ impl MachineState {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn directive_error(&mut self, err: DirectiveError) -> MachineError {
|
||||
match err {
|
||||
DirectiveError::ExpectedDirective(_term) => self.domain_error(
|
||||
DomainErrorType::Directive,
|
||||
atom_as_cell!(atom!("todo_insert_invalid_term_here")),
|
||||
),
|
||||
DirectiveError::InvalidDirective(name, arity) => {
|
||||
self.domain_error(DomainErrorType::Directive, functor_stub(name, arity))
|
||||
}
|
||||
DirectiveError::InvalidOpDeclNameType(_term) => self.type_error(
|
||||
ValidType::List,
|
||||
atom_as_cell!(atom!("todo_insert_invalid_term_here")),
|
||||
),
|
||||
DirectiveError::InvalidOpDeclSpecDomain(_term) => self.domain_error(
|
||||
DomainErrorType::OperatorSpecifier,
|
||||
atom_as_cell!(atom!("todo_insert_invalid_term_here")),
|
||||
),
|
||||
DirectiveError::InvalidOpDeclSpecValue(atom) => {
|
||||
self.domain_error(DomainErrorType::OperatorSpecifier, atom_as_cell!(atom))
|
||||
}
|
||||
DirectiveError::InvalidOpDeclPrecType(_term) => self.type_error(
|
||||
ValidType::Integer,
|
||||
atom_as_cell!(atom!("todo_insert_invalid_term_here")),
|
||||
),
|
||||
DirectiveError::InvalidOpDeclPrecDomain(num) => {
|
||||
self.domain_error(DomainErrorType::OperatorPriority, fixnum_as_cell!(num))
|
||||
}
|
||||
DirectiveError::ShallNotCreate(atom) => {
|
||||
self.permission_error(Permission::Create, atom!("operator"), atom)
|
||||
}
|
||||
DirectiveError::ShallNotModify(atom) => {
|
||||
self.permission_error(Permission::Modify, atom!("operator"), atom)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct CWIL {
|
||||
count: Integer,
|
||||
local_count: Integer,
|
||||
pub(crate) global_count: Integer,
|
||||
limits: Vec<(Integer, usize)>,
|
||||
pub(crate) inference_limit_exceeded: bool,
|
||||
}
|
||||
@@ -980,22 +1028,22 @@ pub(crate) struct CWIL {
|
||||
impl CWIL {
|
||||
pub(crate) fn new() -> Self {
|
||||
CWIL {
|
||||
count: Integer::from(0),
|
||||
local_count: Integer::from(0),
|
||||
global_count: Integer::from(0),
|
||||
limits: vec![],
|
||||
inference_limit_exceeded: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_limit(&mut self, limit: usize, block: usize) -> &Integer {
|
||||
let mut limit = Integer::from(limit);
|
||||
limit += &self.count;
|
||||
pub(crate) fn add_limit(&mut self, mut limit: Integer, block: usize) -> &Integer {
|
||||
limit += &self.local_count;
|
||||
|
||||
match self.limits.last() {
|
||||
Some((ref inner_limit, _)) if *inner_limit <= limit => {}
|
||||
_ => self.limits.push((limit, block)),
|
||||
};
|
||||
}
|
||||
|
||||
&self.count
|
||||
&self.local_count
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -1006,12 +1054,12 @@ impl CWIL {
|
||||
}
|
||||
}
|
||||
|
||||
&self.count
|
||||
&self.local_count
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn reset(&mut self) {
|
||||
self.count = Integer::from(0);
|
||||
self.local_count = Integer::from(0);
|
||||
self.limits.clear();
|
||||
self.inference_limit_exceeded = false;
|
||||
}
|
||||
|
||||
@@ -60,7 +60,6 @@ impl MachineState {
|
||||
unify_fn: MachineState::unify,
|
||||
bind_fn: MachineState::bind,
|
||||
run_cleaners_fn: |_| false,
|
||||
increment_call_count_fn: |_| true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,7 +148,7 @@ impl MachineState {
|
||||
TrailRef::BlackboardEntry(key_atom) => {
|
||||
self.trail.push(TrailEntry::build_with(
|
||||
TrailEntryTag::TrailedBlackboardEntry,
|
||||
key_atom.index as u64,
|
||||
key_atom.index,
|
||||
));
|
||||
|
||||
self.tr += 1;
|
||||
@@ -157,7 +156,7 @@ impl MachineState {
|
||||
TrailRef::BlackboardOffset(key_atom, value_cell) => {
|
||||
self.trail.push(TrailEntry::build_with(
|
||||
TrailEntryTag::TrailedBlackboardOffset,
|
||||
key_atom.index as u64,
|
||||
key_atom.index,
|
||||
));
|
||||
|
||||
self.trail
|
||||
@@ -257,31 +256,16 @@ impl MachineState {
|
||||
unifier.unify_internal();
|
||||
}
|
||||
|
||||
pub fn unify_structure(&mut self, s1: usize, value: HeapCellValue) {
|
||||
let mut unifier = DefaultUnifier::from(self);
|
||||
unifier.unify_structure(s1, value);
|
||||
}
|
||||
|
||||
pub fn unify_atom(&mut self, atom: Atom, value: HeapCellValue) {
|
||||
let mut unifier = DefaultUnifier::from(self);
|
||||
unifier.unify_atom(atom, value);
|
||||
}
|
||||
|
||||
pub fn unify_list(&mut self, l1: usize, value: HeapCellValue) {
|
||||
let mut unifier = DefaultUnifier::from(self);
|
||||
unifier.unify_list(l1, value);
|
||||
}
|
||||
|
||||
pub fn unify_complete_string(&mut self, atom: Atom, value: HeapCellValue) {
|
||||
let mut unifier = DefaultUnifier::from(self);
|
||||
unifier.unify_complete_string(atom, value);
|
||||
}
|
||||
|
||||
pub fn unify_partial_string(&mut self, value_1: HeapCellValue, value_2: HeapCellValue) {
|
||||
let mut unifier = DefaultUnifier::from(self);
|
||||
unifier.unify_partial_string(value_1, value_2);
|
||||
}
|
||||
|
||||
pub fn unify_char(&mut self, c: char, value: HeapCellValue) {
|
||||
let mut unifier = DefaultUnifier::from(self);
|
||||
unifier.unify_char(c, value);
|
||||
@@ -335,7 +319,12 @@ impl MachineState {
|
||||
self.ball.boundary = self.heap.len();
|
||||
|
||||
copy_term(
|
||||
CopyBallTerm::new(&mut self.stack, &mut self.heap, &mut self.ball.stub),
|
||||
CopyBallTerm::new(
|
||||
&mut self.attr_var_init.attr_var_queue,
|
||||
&mut self.stack,
|
||||
&mut self.heap,
|
||||
&mut self.ball.stub,
|
||||
),
|
||||
addr,
|
||||
AttrVarPolicy::DeepCopy,
|
||||
);
|
||||
@@ -432,8 +421,7 @@ impl MachineState {
|
||||
pub fn compare_term_test(&mut self, var_comparison: VarComparison) -> Option<Ordering> {
|
||||
let mut tabu_list = IndexSet::new();
|
||||
|
||||
while !self.pdl.is_empty() {
|
||||
let s1 = self.pdl.pop().unwrap();
|
||||
while let Some(s1) = self.pdl.pop() {
|
||||
let s1 = self.deref(s1);
|
||||
|
||||
let s2 = self.pdl.pop().unwrap();
|
||||
@@ -896,7 +884,7 @@ impl MachineState {
|
||||
|
||||
let s = string.as_str();
|
||||
|
||||
match heap_pstr_iter.compare_pstr_to_string(&*s) {
|
||||
match heap_pstr_iter.compare_pstr_to_string(&s) {
|
||||
Some(PStrPrefixCmpResult {
|
||||
focus,
|
||||
offset,
|
||||
@@ -1142,7 +1130,7 @@ impl MachineState {
|
||||
|
||||
let cycle_found = {
|
||||
let mut iter = cycle_detecting_stackless_preorder_iter(&mut self.heap, h);
|
||||
while let Some(_) = iter.next() {}
|
||||
for _ in iter.by_ref() {}
|
||||
iter.cycle_found()
|
||||
};
|
||||
|
||||
@@ -1376,7 +1364,7 @@ impl MachineState {
|
||||
|
||||
let mut type_error = |arity| {
|
||||
let err = self.type_error(ValidType::Integer, arity);
|
||||
return Err(self.error_form(err, stub_gen()));
|
||||
Err(self.error_form(err, stub_gen()))
|
||||
};
|
||||
|
||||
let arity = match Number::try_from(arity) {
|
||||
@@ -1447,10 +1435,15 @@ impl MachineState {
|
||||
a1.as_var().unwrap(),
|
||||
);
|
||||
}
|
||||
(HeapCellValueTag::Cons | HeapCellValueTag::Fixnum |
|
||||
HeapCellValueTag::F64) if arity != 0 => {
|
||||
let err = self.type_error(ValidType::Atom, store_name);
|
||||
return Err(self.error_form(err, stub_gen())); // 8.5.1.3 e)
|
||||
}
|
||||
_ => {
|
||||
let err = self.type_error(ValidType::Atomic, store_name);
|
||||
return Err(self.error_form(err, stub_gen()));
|
||||
} // 8.5.1.3 c)
|
||||
return Err(self.error_form(err, stub_gen())); // 8.5.1.3 c)
|
||||
}
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
@@ -1573,7 +1566,7 @@ impl MachineState {
|
||||
) -> Result<Vec<HeapCellValue>, MachineStub> {
|
||||
let mut heap_pstr_iter = HeapPStrIter::new(&self.heap, h);
|
||||
|
||||
while let Some(iteratee) = heap_pstr_iter.next() {
|
||||
for iteratee in heap_pstr_iter.by_ref() {
|
||||
match iteratee {
|
||||
PStrIteratee::Char(_, c) => chars.push(char_as_cell!(c)),
|
||||
PStrIteratee::PStrSegment(_, pstr_atom, n) => {
|
||||
@@ -1644,10 +1637,11 @@ impl MachineState {
|
||||
let addr = self.store(self.deref(addr));
|
||||
|
||||
match Number::try_from(addr) {
|
||||
Ok(Number::Fixnum(n)) => match u8::try_from(n.get_num()) {
|
||||
Ok(b) => bytes.push(b),
|
||||
Err(_) => {}
|
||||
},
|
||||
Ok(Number::Fixnum(n)) => {
|
||||
if let Ok(b) = u8::try_from(n.get_num()) {
|
||||
bytes.push(b)
|
||||
}
|
||||
}
|
||||
Ok(Number::Integer(n)) => {
|
||||
let b: u8 = (&*n).try_into().unwrap();
|
||||
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
pub use crate::arena::*;
|
||||
pub use crate::atom_table::*;
|
||||
use crate::heap_print::*;
|
||||
pub use crate::machine::heap::*;
|
||||
pub use crate::machine::machine_state::*;
|
||||
pub use crate::machine::stack::*;
|
||||
pub use crate::machine::streams::*;
|
||||
pub use crate::machine::*;
|
||||
pub use crate::parser::ast::*;
|
||||
@@ -23,9 +20,10 @@ use std::ops::{Deref, DerefMut, Index, IndexMut};
|
||||
pub struct MockWAM {
|
||||
pub machine_st: MachineState,
|
||||
pub op_dir: OpDir,
|
||||
pub flags: MachineFlags,
|
||||
//pub flags: MachineFlags,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl MockWAM {
|
||||
pub fn new() -> Self {
|
||||
let op_dir = default_op_dir();
|
||||
@@ -33,7 +31,7 @@ impl MockWAM {
|
||||
Self {
|
||||
machine_st: MachineState::new(),
|
||||
op_dir,
|
||||
flags: MachineFlags::default(),
|
||||
//flags: MachineFlags::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,6 +80,12 @@ impl MockWAM {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for MockWAM {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub struct TermCopyingMockWAM<'a> {
|
||||
pub wam: &'a mut MockWAM,
|
||||
@@ -109,14 +113,14 @@ impl<'a> Deref for TermCopyingMockWAM<'a> {
|
||||
type Target = MockWAM;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.wam
|
||||
self.wam
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl<'a> DerefMut for TermCopyingMockWAM<'a> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.wam
|
||||
self.wam
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,6 +157,14 @@ impl<'a> CopierTarget for TermCopyingMockWAM<'a> {
|
||||
self.wam.machine_st.heap.push(val);
|
||||
}
|
||||
|
||||
fn push_attr_var_queue(&mut self, attr_var_loc: usize) {
|
||||
self.wam
|
||||
.machine_st
|
||||
.attr_var_init
|
||||
.attr_var_queue
|
||||
.push(attr_var_loc);
|
||||
}
|
||||
|
||||
fn stack(&mut self) -> &mut Stack {
|
||||
&mut self.wam.machine_st.stack
|
||||
}
|
||||
@@ -165,9 +177,8 @@ impl<'a> CopierTarget for TermCopyingMockWAM<'a> {
|
||||
#[cfg(test)]
|
||||
pub fn all_cells_marked_and_unforwarded(heap: &[HeapCellValue]) {
|
||||
for (idx, cell) in heap.iter().enumerate() {
|
||||
assert_eq!(
|
||||
assert!(
|
||||
cell.get_mark_bit(),
|
||||
true,
|
||||
"cell {:?} at index {} is not marked",
|
||||
cell,
|
||||
idx
|
||||
@@ -230,20 +241,16 @@ impl Machine {
|
||||
&mut self.machine_st.arena,
|
||||
);
|
||||
|
||||
self.load_file(file.into(), stream);
|
||||
self.load_file(file, stream);
|
||||
self.user_output.bytes().map(|b| b.unwrap()).collect()
|
||||
}
|
||||
|
||||
pub fn test_load_string(&mut self, code: &str) -> Vec<u8> {
|
||||
let stream = Stream::from_owned_string(
|
||||
code.to_owned(),
|
||||
&mut self.machine_st.arena,
|
||||
);
|
||||
let stream = Stream::from_owned_string(code.to_owned(), &mut self.machine_st.arena);
|
||||
|
||||
self.load_file("<stdin>".into(), stream);
|
||||
self.load_file("<stdin>", stream);
|
||||
self.user_output.bytes().map(|b| b.unwrap()).collect()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -255,11 +262,11 @@ mod tests {
|
||||
let mut wam = MachineState::new();
|
||||
let mut op_dir = default_op_dir();
|
||||
|
||||
op_dir.insert((atom!("+"), Fixity::In), OpDesc::build_with(500, YFX as u8));
|
||||
op_dir.insert((atom!("-"), Fixity::In), OpDesc::build_with(500, YFX as u8));
|
||||
op_dir.insert((atom!("*"), Fixity::In), OpDesc::build_with(500, YFX as u8));
|
||||
op_dir.insert((atom!("/"), Fixity::In), OpDesc::build_with(400, YFX as u8));
|
||||
op_dir.insert((atom!("="), Fixity::In), OpDesc::build_with(700, XFX as u8));
|
||||
op_dir.insert((atom!("+"), Fixity::In), OpDesc::build_with(500, YFX));
|
||||
op_dir.insert((atom!("-"), Fixity::In), OpDesc::build_with(500, YFX));
|
||||
op_dir.insert((atom!("*"), Fixity::In), OpDesc::build_with(500, YFX));
|
||||
op_dir.insert((atom!("/"), Fixity::In), OpDesc::build_with(400, YFX));
|
||||
op_dir.insert((atom!("="), Fixity::In), OpDesc::build_with(700, XFX));
|
||||
|
||||
{
|
||||
parse_and_write_parsed_term_to_heap(&mut wam, "f(X,X).", &op_dir).unwrap();
|
||||
@@ -476,10 +483,10 @@ mod tests {
|
||||
let mut wam = MachineState::new();
|
||||
let mut op_dir = default_op_dir();
|
||||
|
||||
op_dir.insert((atom!("+"), Fixity::In), OpDesc::build_with(500, YFX as u8));
|
||||
op_dir.insert((atom!("-"), Fixity::In), OpDesc::build_with(500, YFX as u8));
|
||||
op_dir.insert((atom!("*"), Fixity::In), OpDesc::build_with(400, YFX as u8));
|
||||
op_dir.insert((atom!("/"), Fixity::In), OpDesc::build_with(400, YFX as u8));
|
||||
op_dir.insert((atom!("+"), Fixity::In), OpDesc::build_with(500, YFX));
|
||||
op_dir.insert((atom!("-"), Fixity::In), OpDesc::build_with(500, YFX));
|
||||
op_dir.insert((atom!("*"), Fixity::In), OpDesc::build_with(400, YFX));
|
||||
op_dir.insert((atom!("/"), Fixity::In), OpDesc::build_with(400, YFX));
|
||||
|
||||
{
|
||||
parse_and_write_parsed_term_to_heap(&mut wam, "f(X,X).", &op_dir).unwrap();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod args;
|
||||
#[macro_use]
|
||||
pub mod arithmetic_ops;
|
||||
pub mod attributed_variables;
|
||||
pub mod code_walker;
|
||||
@@ -53,16 +54,17 @@ use indexmap::IndexMap;
|
||||
use lazy_static::lazy_static;
|
||||
use ordered_float::OrderedFloat;
|
||||
|
||||
use rand::rngs::StdRng;
|
||||
use rand::SeedableRng;
|
||||
use std::cmp::Ordering;
|
||||
use std::env;
|
||||
use std::io::Read;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use self::config::MachineConfig;
|
||||
use self::parsed_results::*;
|
||||
use rand::rngs::StdRng;
|
||||
use rand::SeedableRng;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref INTERRUPT: AtomicBool = AtomicBool::new(false);
|
||||
@@ -110,10 +112,59 @@ impl LoadContext {
|
||||
|
||||
#[inline]
|
||||
fn current_dir() -> PathBuf {
|
||||
env::current_dir().unwrap_or(PathBuf::from("./"))
|
||||
if !cfg!(miri) {
|
||||
env::current_dir().unwrap_or(PathBuf::from("./"))
|
||||
} else {
|
||||
PathBuf::from("./")
|
||||
}
|
||||
}
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/libraries.rs"));
|
||||
#[cfg(not(feature = "rust-version-1.80"))]
|
||||
mod libraries {
|
||||
use indexmap::IndexMap;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
fn libraries() -> &'static IndexMap<&'static str, &'static str> {
|
||||
static LIBRARIES: OnceLock<IndexMap<&'static str, &'static str>> = OnceLock::new();
|
||||
LIBRARIES.get_or_init(|| {
|
||||
let mut m = IndexMap::new();
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/libraries.rs"));
|
||||
|
||||
m
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn contains(name: &str) -> bool {
|
||||
libraries().contains_key(name)
|
||||
}
|
||||
|
||||
pub(crate) fn get(name: &str) -> Option<&'static str> {
|
||||
libraries().get(name).copied()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rust-version-1.80")]
|
||||
mod libraries {
|
||||
use indexmap::IndexMap;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
static LIBRARIES: LazyLock<IndexMap<&'static str, &'static str>> = LazyLock::new(|| {
|
||||
let mut m = IndexMap::new();
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/libraries.rs"));
|
||||
|
||||
m
|
||||
});
|
||||
|
||||
pub(crate) fn contains(name: &str) -> bool {
|
||||
LIBRARIES.contains_key(name)
|
||||
}
|
||||
|
||||
pub(crate) fn get(name: &str) -> Option<&'static str> {
|
||||
LIBRARIES.get(name).copied()
|
||||
}
|
||||
}
|
||||
|
||||
pub static BREAK_FROM_DISPATCH_LOOP_LOC: usize = 0;
|
||||
pub static INSTALL_VERIFY_ATTR_INTERRUPT: usize = 1;
|
||||
@@ -172,7 +223,7 @@ pub(crate) fn import_builtin_impls(code_dir: &CodeDir, builtins: &mut Module) {
|
||||
|
||||
for key in keys {
|
||||
let idx = code_dir.get(&key).unwrap();
|
||||
builtins.code_dir.insert(key, idx.clone());
|
||||
builtins.code_dir.insert(key, *idx);
|
||||
builtins
|
||||
.module_decl
|
||||
.exports
|
||||
@@ -200,7 +251,7 @@ pub(crate) fn get_structure_index(value: HeapCellValue) -> Option<CodeIndex> {
|
||||
|
||||
impl Machine {
|
||||
#[inline]
|
||||
pub fn prelude_view_and_machine_st(&mut self) -> (MachinePreludeView, &mut MachineState) {
|
||||
fn prelude_view_and_machine_st(&mut self) -> (MachinePreludeView, &mut MachineState) {
|
||||
(
|
||||
MachinePreludeView {
|
||||
indices: &mut self.indices,
|
||||
@@ -211,21 +262,22 @@ impl Machine {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn throw_session_error(&mut self, err: SessionError, key: PredicateKey) {
|
||||
let err = self.machine_st.session_error(err);
|
||||
let stub = functor_stub(key.0, key.1);
|
||||
let err = self.machine_st.error_form(err, stub);
|
||||
|
||||
self.machine_st.throw_exception(err);
|
||||
pub fn get_inference_count(&mut self) -> u64 {
|
||||
self.machine_st
|
||||
.cwil
|
||||
.global_count
|
||||
.clone()
|
||||
.try_into()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn run_module_predicate(
|
||||
pub(crate) fn run_module_predicate(
|
||||
&mut self,
|
||||
module_name: Atom,
|
||||
key: PredicateKey,
|
||||
) -> std::process::ExitCode {
|
||||
if let Some(module) = self.indices.modules.get(&module_name) {
|
||||
if let Some(ref code_index) = module.code_dir.get(&key) {
|
||||
if let Some(code_index) = module.code_dir.get(&key) {
|
||||
let p = code_index.local().unwrap();
|
||||
|
||||
self.machine_st.cp = BREAK_FROM_DISPATCH_LOOP_LOC;
|
||||
@@ -238,7 +290,7 @@ impl Machine {
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
pub fn load_file(&mut self, path: &str, stream: Stream) {
|
||||
fn load_file(&mut self, path: &str, stream: Stream) {
|
||||
self.machine_st.registers[1] = stream_as_cell!(stream);
|
||||
self.machine_st.registers[2] =
|
||||
atom_as_cell!(AtomTable::build_with(&self.machine_st.atom_tbl, path));
|
||||
@@ -252,9 +304,7 @@ impl Machine {
|
||||
path_buf.push("src/toplevel.pl");
|
||||
|
||||
let path = path_buf.to_str().unwrap();
|
||||
let toplevel_stream =
|
||||
Stream::from_static_string(program, &mut self.machine_st.arena);
|
||||
|
||||
let toplevel_stream = Stream::from_static_string(program, &mut self.machine_st.arena);
|
||||
|
||||
self.load_file(path, toplevel_stream);
|
||||
|
||||
@@ -300,34 +350,6 @@ impl Machine {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_top_level(&mut self, module_name: Atom, key: PredicateKey) -> std::process::ExitCode {
|
||||
let mut arg_pstrs = vec![];
|
||||
|
||||
for arg in env::args() {
|
||||
arg_pstrs.push(put_complete_string(
|
||||
&mut self.machine_st.heap,
|
||||
&arg,
|
||||
&self.machine_st.atom_tbl,
|
||||
));
|
||||
}
|
||||
|
||||
self.machine_st.registers[1] = heap_loc_as_cell!(iter_to_heap_list(
|
||||
&mut self.machine_st.heap,
|
||||
arg_pstrs.into_iter()
|
||||
));
|
||||
|
||||
self.run_module_predicate(module_name, key)
|
||||
}
|
||||
|
||||
pub fn set_user_input(&mut self, input: String) {
|
||||
self.user_input = Stream::from_owned_string(input, &mut self.machine_st.arena);
|
||||
}
|
||||
|
||||
pub fn get_user_output(&self) -> String {
|
||||
let output_bytes: Vec<_> = self.user_output.bytes().map(|b| b.unwrap()).collect();
|
||||
String::from_utf8(output_bytes).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn configure_modules(&mut self) {
|
||||
fn update_call_n_indices(
|
||||
loader: &Module,
|
||||
@@ -400,57 +422,51 @@ impl Machine {
|
||||
pub(crate) fn add_impls_to_indices(&mut self) {
|
||||
let impls_offset = self.code.len() + 4;
|
||||
|
||||
self.code.extend(
|
||||
vec![
|
||||
Instruction::BreakFromDispatchLoop,
|
||||
Instruction::InstallVerifyAttr,
|
||||
Instruction::VerifyAttrInterrupt,
|
||||
Instruction::BreakFromDispatchLoop, // the location of LIB_QUERY_SUCCESS
|
||||
Instruction::ExecuteTermGreaterThan,
|
||||
Instruction::ExecuteTermLessThan,
|
||||
Instruction::ExecuteTermGreaterThanOrEqual,
|
||||
Instruction::ExecuteTermLessThanOrEqual,
|
||||
Instruction::ExecuteTermEqual,
|
||||
Instruction::ExecuteTermNotEqual,
|
||||
Instruction::ExecuteNumberGreaterThan(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))),
|
||||
Instruction::ExecuteNumberLessThan(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))),
|
||||
Instruction::ExecuteNumberGreaterThanOrEqual(
|
||||
ar_reg!(temp_v!(1)),
|
||||
ar_reg!(temp_v!(2)),
|
||||
),
|
||||
Instruction::ExecuteNumberLessThanOrEqual(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))),
|
||||
Instruction::ExecuteNumberEqual(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))),
|
||||
Instruction::ExecuteNumberNotEqual(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))),
|
||||
Instruction::ExecuteIs(temp_v!(1), ar_reg!(temp_v!(2))),
|
||||
Instruction::ExecuteAcyclicTerm,
|
||||
Instruction::ExecuteArg,
|
||||
Instruction::ExecuteCompare,
|
||||
Instruction::ExecuteCopyTerm,
|
||||
Instruction::ExecuteFunctor,
|
||||
Instruction::ExecuteGround,
|
||||
Instruction::ExecuteKeySort,
|
||||
Instruction::ExecuteSort,
|
||||
Instruction::ExecuteN(1),
|
||||
Instruction::ExecuteN(2),
|
||||
Instruction::ExecuteN(3),
|
||||
Instruction::ExecuteN(4),
|
||||
Instruction::ExecuteN(5),
|
||||
Instruction::ExecuteN(6),
|
||||
Instruction::ExecuteN(7),
|
||||
Instruction::ExecuteN(8),
|
||||
Instruction::ExecuteN(9),
|
||||
Instruction::ExecuteIsAtom(temp_v!(1)),
|
||||
Instruction::ExecuteIsAtomic(temp_v!(1)),
|
||||
Instruction::ExecuteIsCompound(temp_v!(1)),
|
||||
Instruction::ExecuteIsInteger(temp_v!(1)),
|
||||
Instruction::ExecuteIsNumber(temp_v!(1)),
|
||||
Instruction::ExecuteIsRational(temp_v!(1)),
|
||||
Instruction::ExecuteIsFloat(temp_v!(1)),
|
||||
Instruction::ExecuteIsNonVar(temp_v!(1)),
|
||||
Instruction::ExecuteIsVar(temp_v!(1)),
|
||||
]
|
||||
.into_iter(),
|
||||
);
|
||||
self.code.extend(vec![
|
||||
Instruction::BreakFromDispatchLoop,
|
||||
Instruction::InstallVerifyAttr,
|
||||
Instruction::VerifyAttrInterrupt(0),
|
||||
Instruction::BreakFromDispatchLoop, // the location of LIB_QUERY_SUCCESS
|
||||
Instruction::ExecuteTermGreaterThan,
|
||||
Instruction::ExecuteTermLessThan,
|
||||
Instruction::ExecuteTermGreaterThanOrEqual,
|
||||
Instruction::ExecuteTermLessThanOrEqual,
|
||||
Instruction::ExecuteTermEqual,
|
||||
Instruction::ExecuteTermNotEqual,
|
||||
Instruction::ExecuteNumberGreaterThan(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))),
|
||||
Instruction::ExecuteNumberLessThan(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))),
|
||||
Instruction::ExecuteNumberGreaterThanOrEqual(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))),
|
||||
Instruction::ExecuteNumberLessThanOrEqual(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))),
|
||||
Instruction::ExecuteNumberEqual(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))),
|
||||
Instruction::ExecuteNumberNotEqual(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))),
|
||||
Instruction::ExecuteIs(temp_v!(1), ar_reg!(temp_v!(2))),
|
||||
Instruction::ExecuteAcyclicTerm,
|
||||
Instruction::ExecuteArg,
|
||||
Instruction::ExecuteCompare,
|
||||
Instruction::ExecuteCopyTerm,
|
||||
Instruction::ExecuteFunctor,
|
||||
Instruction::ExecuteGround,
|
||||
Instruction::ExecuteKeySort,
|
||||
Instruction::ExecuteSort,
|
||||
Instruction::ExecuteN(1),
|
||||
Instruction::ExecuteN(2),
|
||||
Instruction::ExecuteN(3),
|
||||
Instruction::ExecuteN(4),
|
||||
Instruction::ExecuteN(5),
|
||||
Instruction::ExecuteN(6),
|
||||
Instruction::ExecuteN(7),
|
||||
Instruction::ExecuteN(8),
|
||||
Instruction::ExecuteN(9),
|
||||
Instruction::ExecuteIsAtom(temp_v!(1)),
|
||||
Instruction::ExecuteIsAtomic(temp_v!(1)),
|
||||
Instruction::ExecuteIsCompound(temp_v!(1)),
|
||||
Instruction::ExecuteIsInteger(temp_v!(1)),
|
||||
Instruction::ExecuteIsNumber(temp_v!(1)),
|
||||
Instruction::ExecuteIsRational(temp_v!(1)),
|
||||
Instruction::ExecuteIsFloat(temp_v!(1)),
|
||||
Instruction::ExecuteIsNonVar(temp_v!(1)),
|
||||
Instruction::ExecuteIsVar(temp_v!(1)),
|
||||
]);
|
||||
|
||||
for (p, instr) in self.code[impls_offset..].iter().enumerate() {
|
||||
let key = instr.to_name_and_arity();
|
||||
@@ -464,9 +480,8 @@ impl Machine {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::new_without_default)]
|
||||
pub fn new(config: MachineConfig) -> Self {
|
||||
use ref_thread_local::RefThreadLocal;
|
||||
|
||||
let args = MachineArgs::new();
|
||||
let mut machine_st = MachineState::new();
|
||||
|
||||
@@ -505,7 +520,8 @@ impl Machine {
|
||||
|
||||
bootstrapping_compile(
|
||||
Stream::from_static_string(
|
||||
LIBRARIES.borrow()["ops_and_meta_predicates"],
|
||||
libraries::get("ops_and_meta_predicates")
|
||||
.expect("library ops_and_meta_predicates should exist"),
|
||||
&mut wam.machine_st.arena,
|
||||
),
|
||||
&mut wam,
|
||||
@@ -517,7 +533,10 @@ impl Machine {
|
||||
.unwrap();
|
||||
|
||||
bootstrapping_compile(
|
||||
Stream::from_static_string(LIBRARIES.borrow()["builtins"], &mut wam.machine_st.arena),
|
||||
Stream::from_static_string(
|
||||
libraries::get("builtins").expect("library builtins should exist"),
|
||||
&mut wam.machine_st.arena,
|
||||
),
|
||||
&mut wam,
|
||||
ListingSource::from_file_and_path(atom!("builtins.pl"), lib_path.clone()),
|
||||
)
|
||||
@@ -919,8 +938,8 @@ impl Machine {
|
||||
|
||||
self.machine_st.hb = self.machine_st.heap.len();
|
||||
|
||||
self.machine_st.oip = 0;
|
||||
self.machine_st.iip = 0;
|
||||
// self.machine_st.oip = 0;
|
||||
// self.machine_st.iip = 0;
|
||||
}
|
||||
|
||||
self.machine_st.p += offset;
|
||||
@@ -1004,8 +1023,22 @@ impl Machine {
|
||||
|
||||
self.machine_st.heap.truncate(target_h);
|
||||
|
||||
self.machine_st.oip = 0;
|
||||
self.machine_st.iip = 0;
|
||||
// these registers don't need to be reset here and MUST
|
||||
// NOT be (nor in indexed_try! trust_epilogue is an
|
||||
// exception, see next paragraph)! oip could be reset
|
||||
// without any adverse effects but iip is needed by
|
||||
// get_clause_p to find the last executed clause/2 clause.
|
||||
|
||||
// trust_epilogue must reset these for the sake of
|
||||
// subsequent predicates beginning with
|
||||
// switch_to_term. get_clause_p copes by checking
|
||||
// self.machine_st.b > self.machine.e: if true, it is safe
|
||||
// to use self.machine_st.iip; if false, use the choice
|
||||
// point left at the top of the stack by '$clause'
|
||||
// (specifically its biip value).
|
||||
|
||||
// self.machine_st.oip = 0;
|
||||
// self.machine_st.iip = 0;
|
||||
} else {
|
||||
self.trust_epilogue(offset);
|
||||
}
|
||||
@@ -1048,7 +1081,7 @@ impl Machine {
|
||||
self.reset_attr_var_state(or_frame.prelude.attr_var_queue_len);
|
||||
|
||||
self.machine_st.hb = target_h;
|
||||
self.machine_st.p = self.machine_st.p + offset;
|
||||
self.machine_st.p += offset;
|
||||
|
||||
self.machine_st.stack.truncate(b);
|
||||
self.machine_st.heap.truncate(target_h);
|
||||
@@ -1110,7 +1143,7 @@ impl Machine {
|
||||
}
|
||||
Unknown::Warn => {
|
||||
println!(
|
||||
"warning: predicate {}/{} is undefined",
|
||||
"% Warning: predicate {}/{} is undefined",
|
||||
name.as_str(),
|
||||
arity
|
||||
);
|
||||
@@ -1174,21 +1207,23 @@ impl Machine {
|
||||
} else {
|
||||
Err(self.machine_st.throw_undefined_error(name, arity))
|
||||
}
|
||||
} else {
|
||||
if let Some(module) = self.indices.modules.get(&module_name) {
|
||||
if let Some(idx) = module.code_dir.get(&(name, arity)).cloned() {
|
||||
self.try_call(name, arity, idx.get())
|
||||
} else {
|
||||
self.undefined_procedure(name, arity)
|
||||
}
|
||||
} else if let Some(module) = self.indices.modules.get(&module_name) {
|
||||
if let Some(idx) = module.code_dir.get(&(name, arity)).cloned() {
|
||||
self.try_call(name, arity, idx.get())
|
||||
} else {
|
||||
let stub = functor_stub(name, arity);
|
||||
let err = self
|
||||
.machine_st
|
||||
.existence_error(ExistenceError::QualifiedProcedure { module_name, name, arity });
|
||||
|
||||
Err(self.machine_st.error_form(err, stub))
|
||||
self.undefined_procedure(name, arity)
|
||||
}
|
||||
} else {
|
||||
let stub = functor_stub(name, arity);
|
||||
let err = self
|
||||
.machine_st
|
||||
.existence_error(ExistenceError::QualifiedProcedure {
|
||||
module_name,
|
||||
name,
|
||||
arity,
|
||||
});
|
||||
|
||||
Err(self.machine_st.error_form(err, stub))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1202,21 +1237,23 @@ impl Machine {
|
||||
} else {
|
||||
self.undefined_procedure(name, arity)
|
||||
}
|
||||
} else {
|
||||
if let Some(module) = self.indices.modules.get(&module_name) {
|
||||
if let Some(idx) = module.code_dir.get(&(name, arity)).cloned() {
|
||||
self.try_execute(name, arity, idx.get())
|
||||
} else {
|
||||
self.undefined_procedure(name, arity)
|
||||
}
|
||||
} else if let Some(module) = self.indices.modules.get(&module_name) {
|
||||
if let Some(idx) = module.code_dir.get(&(name, arity)).cloned() {
|
||||
self.try_execute(name, arity, idx.get())
|
||||
} else {
|
||||
let stub = functor_stub(name, arity);
|
||||
let err = self
|
||||
.machine_st
|
||||
.existence_error(ExistenceError::QualifiedProcedure { module_name, name, arity });
|
||||
|
||||
Err(self.machine_st.error_form(err, stub))
|
||||
self.undefined_procedure(name, arity)
|
||||
}
|
||||
} else {
|
||||
let stub = functor_stub(name, arity);
|
||||
let err = self
|
||||
.machine_st
|
||||
.existence_error(ExistenceError::QualifiedProcedure {
|
||||
module_name,
|
||||
name,
|
||||
arity,
|
||||
});
|
||||
|
||||
Err(self.machine_st.error_form(err, stub))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1234,33 +1271,25 @@ impl Machine {
|
||||
|
||||
#[inline(always)]
|
||||
fn run_cleaners(&mut self) -> bool {
|
||||
use std::sync::Once;
|
||||
static CLEANER_INIT: OnceLock<(usize, usize)> = OnceLock::new();
|
||||
|
||||
static CLEANER_INIT: Once = Once::new();
|
||||
let (r_c_w_h, r_c_wo_h) = *CLEANER_INIT.get_or_init(|| {
|
||||
let r_c_w_h_atom = atom!("run_cleaners_with_handling");
|
||||
let r_c_wo_h_atom = atom!("run_cleaners_without_handling");
|
||||
let iso_ext = atom!("iso_ext");
|
||||
|
||||
static mut RCWH: usize = 0;
|
||||
static mut RCWOH: usize = 0;
|
||||
|
||||
let (r_c_w_h, r_c_wo_h) = unsafe {
|
||||
CLEANER_INIT.call_once(|| {
|
||||
let r_c_w_h_atom = atom!("run_cleaners_with_handling");
|
||||
let r_c_wo_h_atom = atom!("run_cleaners_without_handling");
|
||||
let iso_ext = atom!("iso_ext");
|
||||
|
||||
RCWH = self
|
||||
.indices
|
||||
.get_predicate_code_index(r_c_w_h_atom, 0, iso_ext)
|
||||
.and_then(|item| item.local())
|
||||
.unwrap();
|
||||
RCWOH = self
|
||||
.indices
|
||||
.get_predicate_code_index(r_c_wo_h_atom, 1, iso_ext)
|
||||
.and_then(|item| item.local())
|
||||
.unwrap();
|
||||
});
|
||||
|
||||
(RCWH, RCWOH)
|
||||
};
|
||||
let r_c_w_h = self
|
||||
.indices
|
||||
.get_predicate_code_index(r_c_w_h_atom, 0, iso_ext)
|
||||
.and_then(|item| item.local())
|
||||
.unwrap();
|
||||
let r_c_wo_h = self
|
||||
.indices
|
||||
.get_predicate_code_index(r_c_wo_h_atom, 1, iso_ext)
|
||||
.and_then(|item| item.local())
|
||||
.unwrap();
|
||||
(r_c_w_h, r_c_wo_h)
|
||||
});
|
||||
|
||||
if let Some(&(_, b_cutoff, prev_block)) = self.machine_st.cont_pts.last() {
|
||||
if self.machine_st.b < b_cutoff {
|
||||
|
||||
@@ -1,8 +1,19 @@
|
||||
use crate::atom_table::*;
|
||||
use ordered_float::OrderedFloat;
|
||||
use crate::heap_iter::{stackful_post_order_iter, NonListElider};
|
||||
use crate::machine::{F64Offset, F64Ptr, Fixnum, HeapCellValueTag};
|
||||
use crate::parser::ast::{Var, VarPtr};
|
||||
use dashu::*;
|
||||
use indexmap::IndexMap;
|
||||
use ordered_float::OrderedFloat;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Display;
|
||||
use std::fmt::Write;
|
||||
use std::iter::FromIterator;
|
||||
|
||||
use super::Machine;
|
||||
use super::{HeapCellValue, Number};
|
||||
|
||||
pub type QueryResult = Result<QueryResolution, String>;
|
||||
|
||||
@@ -13,6 +24,99 @@ pub enum QueryResolution {
|
||||
Matches(Vec<QueryMatch>),
|
||||
}
|
||||
|
||||
fn write_prolog_value_as_json<W: Write>(
|
||||
writer: &mut W,
|
||||
value: &Value,
|
||||
) -> Result<(), std::fmt::Error> {
|
||||
match value {
|
||||
Value::Integer(i) => write!(writer, "{}", i),
|
||||
Value::Float(f) => write!(writer, "{}", f),
|
||||
Value::Rational(r) => write!(writer, "{}", r),
|
||||
Value::Atom(a) => writer.write_str(a.as_str()),
|
||||
Value::String(s) => {
|
||||
if let Err(_e) = serde_json::from_str::<serde_json::Value>(s.as_str()) {
|
||||
//treat as string literal
|
||||
//escape double quotes
|
||||
write!(
|
||||
writer,
|
||||
"\"{}\"",
|
||||
s.replace('\"', "\\\"")
|
||||
.replace('\n', "\\n")
|
||||
.replace('\t', "\\t")
|
||||
.replace('\r', "\\r")
|
||||
)
|
||||
} else {
|
||||
//return valid json string
|
||||
writer.write_str(s)
|
||||
}
|
||||
}
|
||||
Value::List(l) => {
|
||||
writer.write_char('[')?;
|
||||
if let Some((first, rest)) = l.split_first() {
|
||||
write_prolog_value_as_json(writer, first)?;
|
||||
|
||||
for other in rest {
|
||||
writer.write_char(',')?;
|
||||
write_prolog_value_as_json(writer, other)?;
|
||||
}
|
||||
}
|
||||
writer.write_char(']')
|
||||
}
|
||||
Value::Structure(s, l) => {
|
||||
write!(writer, "\"{}\":[", s.as_str())?;
|
||||
|
||||
if let Some((first, rest)) = l.split_first() {
|
||||
write_prolog_value_as_json(writer, first)?;
|
||||
for other in rest {
|
||||
writer.write_char(',')?;
|
||||
write_prolog_value_as_json(writer, other)?;
|
||||
}
|
||||
}
|
||||
writer.write_char(']')
|
||||
}
|
||||
_ => writer.write_str("null"),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_prolog_match_as_json<W: std::fmt::Write>(
|
||||
writer: &mut W,
|
||||
query_match: &QueryMatch,
|
||||
) -> Result<(), std::fmt::Error> {
|
||||
writer.write_char('{')?;
|
||||
let mut iter = query_match.bindings.iter();
|
||||
|
||||
if let Some((k, v)) = iter.next() {
|
||||
write!(writer, "\"{k}\":")?;
|
||||
write_prolog_value_as_json(writer, v)?;
|
||||
|
||||
for (k, v) in iter {
|
||||
write!(writer, ",\"{k}\":")?;
|
||||
write_prolog_value_as_json(writer, v)?;
|
||||
}
|
||||
}
|
||||
writer.write_char('}')
|
||||
}
|
||||
|
||||
impl Display for QueryResolution {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
QueryResolution::True => f.write_str("true"),
|
||||
QueryResolution::False => f.write_str("false"),
|
||||
QueryResolution::Matches(matches) => {
|
||||
f.write_char('[')?;
|
||||
if let Some((first, rest)) = matches.split_first() {
|
||||
write_prolog_match_as_json(f, first)?;
|
||||
for other in rest {
|
||||
f.write_char(',')?;
|
||||
write_prolog_match_as_json(f, other)?;
|
||||
}
|
||||
}
|
||||
f.write_char(']')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct QueryMatch {
|
||||
pub bindings: BTreeMap<String, Value>,
|
||||
@@ -30,11 +134,267 @@ pub enum Value {
|
||||
Integer(Integer),
|
||||
Rational(Rational),
|
||||
Float(OrderedFloat<f64>),
|
||||
Atom(Atom),
|
||||
Atom(String),
|
||||
String(String),
|
||||
List(Vec<Value>),
|
||||
Structure(Atom, Vec<Value>),
|
||||
Var,
|
||||
Structure(String, Vec<Value>),
|
||||
Var(String),
|
||||
}
|
||||
|
||||
/// This is an auxiliary function to turn a count into names of anonymous variables like _A, _B,
|
||||
/// _AB, etc...
|
||||
fn count_to_letter_code(mut count: usize) -> String {
|
||||
let mut letters = Vec::new();
|
||||
|
||||
loop {
|
||||
let letter_idx = (count % 26) as u32;
|
||||
letters.push(char::from_u32('A' as u32 + letter_idx).unwrap());
|
||||
count /= 26;
|
||||
|
||||
if count == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
letters.into_iter().chain("_".chars()).rev().collect()
|
||||
}
|
||||
|
||||
impl Value {
|
||||
pub(crate) fn from_heapcell(
|
||||
machine: &mut Machine,
|
||||
heap_cell: HeapCellValue,
|
||||
var_names: &mut IndexMap<HeapCellValue, VarPtr>,
|
||||
) -> Self {
|
||||
// Adapted from MachineState::read_term_from_heap
|
||||
let mut term_stack = vec![];
|
||||
let iter = stackful_post_order_iter::<NonListElider>(
|
||||
&mut machine.machine_st.heap,
|
||||
&mut machine.machine_st.stack,
|
||||
heap_cell,
|
||||
);
|
||||
|
||||
let mut anon_count: usize = 0;
|
||||
let var_ptr_cmp = |a, b| match a {
|
||||
Var::Named(name_a) => match b {
|
||||
Var::Named(name_b) => name_a.cmp(&name_b),
|
||||
_ => Ordering::Less,
|
||||
},
|
||||
_ => match b {
|
||||
Var::Named(_) => Ordering::Greater,
|
||||
_ => Ordering::Equal,
|
||||
},
|
||||
};
|
||||
|
||||
for addr in iter {
|
||||
let addr = unmark_cell_bits!(addr);
|
||||
|
||||
read_heap_cell!(addr,
|
||||
(HeapCellValueTag::Lis) => {
|
||||
let tail = term_stack.pop().unwrap();
|
||||
let head = term_stack.pop().unwrap();
|
||||
|
||||
let list = match tail {
|
||||
Value::Atom(atom) if atom == "[]" => match head {
|
||||
Value::Atom(ref a) if a.chars().collect::<Vec<_>>().len() == 1 => {
|
||||
// Handle lists of char as strings
|
||||
Value::String(a.to_string())
|
||||
}
|
||||
_ => Value::List(vec![head]),
|
||||
},
|
||||
Value::List(elems) if elems.is_empty() => match head {
|
||||
Value::Atom(ref a) if a.chars().collect::<Vec<_>>().len() == 1 => {
|
||||
// Handle lists of char as strings
|
||||
Value::String(a.to_string())
|
||||
},
|
||||
_ => Value::List(vec![head]),
|
||||
},
|
||||
Value::List(mut elems) => {
|
||||
elems.insert(0, head);
|
||||
Value::List(elems)
|
||||
},
|
||||
Value::String(mut elems) => match head {
|
||||
Value::Atom(ref a) if a.chars().collect::<Vec<_>>().len() == 1 => {
|
||||
// Handle lists of char as strings
|
||||
elems.insert(0, a.chars().next().unwrap());
|
||||
Value::String(elems)
|
||||
},
|
||||
_ => {
|
||||
let mut elems: Vec<Value> = elems
|
||||
.chars()
|
||||
.map(|x| Value::Atom(x.into()))
|
||||
.collect();
|
||||
elems.insert(0, head);
|
||||
Value::List(elems)
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
Value::Structure(".".into(), vec![head, tail])
|
||||
}
|
||||
};
|
||||
term_stack.push(list);
|
||||
}
|
||||
(HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar) => {
|
||||
let var = var_names.get(&addr).map(|x| x.borrow().clone());
|
||||
match var {
|
||||
Some(Var::Named(name)) => term_stack.push(Value::Var(name)),
|
||||
_ => {
|
||||
let anon_name = loop {
|
||||
// Generate a name for the anonymous variable
|
||||
let anon_name = count_to_letter_code(anon_count);
|
||||
|
||||
// Find if this name is already being used
|
||||
var_names.sort_by(|_, a, _, b| {
|
||||
var_ptr_cmp(a.borrow().clone(), b.borrow().clone())
|
||||
});
|
||||
let binary_result = var_names.binary_search_by(|_,a| {
|
||||
let var_ptr = Var::Named(anon_name.clone());
|
||||
var_ptr_cmp(a.borrow().clone(), var_ptr.clone())
|
||||
});
|
||||
|
||||
match binary_result {
|
||||
Ok(_) => anon_count += 1, // Name already used
|
||||
Err(_) => {
|
||||
// Name not used, assign it to this variable
|
||||
let var_ptr = VarPtr::from(Var::Named(anon_name.clone()));
|
||||
var_names.insert(addr, var_ptr);
|
||||
break anon_name;
|
||||
},
|
||||
}
|
||||
};
|
||||
term_stack.push(Value::Var(anon_name));
|
||||
},
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::F64, f) => {
|
||||
term_stack.push(Value::Float(*f));
|
||||
}
|
||||
(HeapCellValueTag::Char, c) => {
|
||||
term_stack.push(Value::Atom(c.into()));
|
||||
}
|
||||
(HeapCellValueTag::Fixnum, n) => {
|
||||
term_stack.push(Value::Integer(n.into()));
|
||||
}
|
||||
(HeapCellValueTag::Cons) => {
|
||||
match Number::try_from(addr) {
|
||||
Ok(Number::Integer(i)) => term_stack.push(Value::Integer((*i).clone())),
|
||||
Ok(Number::Rational(r)) => term_stack.push(Value::Rational((*r).clone())),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::CStr, s) => {
|
||||
term_stack.push(Value::String(s.as_str().to_string()));
|
||||
}
|
||||
(HeapCellValueTag::Atom, (name, arity)) => {
|
||||
//let h = iter.focus().value() as usize;
|
||||
//let mut arity = arity;
|
||||
|
||||
// Not sure why/if this is needed.
|
||||
// Might find out with better testing later.
|
||||
/*
|
||||
if iter.heap.len() > h + arity + 1 {
|
||||
let value = iter.heap[h + arity + 1];
|
||||
|
||||
if let Some(idx) = get_structure_index(value) {
|
||||
// in the second condition, arity == 0,
|
||||
// meaning idx cannot pertain to this atom
|
||||
// if it is the direct subterm of a larger
|
||||
// structure.
|
||||
if arity > 0 || !iter.direct_subterm_of_str(h) {
|
||||
term_stack.push(
|
||||
Term::Literal(Cell::default(), Literal::CodeIndex(idx))
|
||||
);
|
||||
|
||||
arity += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
if arity == 0 {
|
||||
let atom_name = name.as_str().to_string();
|
||||
if atom_name == "[]" {
|
||||
term_stack.push(Value::List(vec![]));
|
||||
} else {
|
||||
term_stack.push(Value::Atom(atom_name));
|
||||
}
|
||||
} else {
|
||||
let subterms = term_stack
|
||||
.drain(term_stack.len() - arity ..)
|
||||
.collect();
|
||||
|
||||
term_stack.push(Value::Structure(name.as_str().to_string(), subterms));
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::PStr, atom) => {
|
||||
let tail = term_stack.pop().unwrap();
|
||||
|
||||
match tail {
|
||||
Value::Atom(atom) => {
|
||||
if atom == "[]" {
|
||||
term_stack.push(Value::String(atom.as_str().to_string()));
|
||||
}
|
||||
},
|
||||
Value::List(l) => {
|
||||
let mut list: Vec<Value> = atom
|
||||
.as_str()
|
||||
.to_string()
|
||||
.chars()
|
||||
.map(|x| Value::Atom(x.to_string()))
|
||||
.collect();
|
||||
list.extend(l.into_iter());
|
||||
term_stack.push(Value::List(list));
|
||||
},
|
||||
_ => {
|
||||
let mut list: Vec<Value> = atom
|
||||
.as_str()
|
||||
.to_string()
|
||||
.chars()
|
||||
.map(|x| Value::Atom(x.to_string()))
|
||||
.collect();
|
||||
|
||||
let mut partial_list = Value::Structure(
|
||||
".".into(),
|
||||
vec![
|
||||
list.pop().unwrap(),
|
||||
tail,
|
||||
],
|
||||
);
|
||||
|
||||
while let Some(last) = list.pop() {
|
||||
partial_list = Value::Structure(
|
||||
".".into(),
|
||||
vec![
|
||||
last,
|
||||
partial_list,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
term_stack.push(partial_list);
|
||||
}
|
||||
}
|
||||
}
|
||||
// I dont know if this is needed here.
|
||||
/*
|
||||
(HeapCellValueTag::PStrLoc, h) => {
|
||||
let atom = cell_as_atom_cell!(iter.heap[h]).get_name();
|
||||
let tail = term_stack.pop().unwrap();
|
||||
|
||||
term_stack.push(Term::PartialString(
|
||||
Cell::default(),
|
||||
atom.as_str().to_owned(),
|
||||
Box::new(tail),
|
||||
));
|
||||
}
|
||||
*/
|
||||
_ => {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
debug_assert_eq!(term_stack.len(), 1);
|
||||
term_stack.pop().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BTreeMap<&str, Value>> for QueryMatch {
|
||||
@@ -65,15 +425,12 @@ impl From<Vec<QueryResolutionLine>> for QueryResolution {
|
||||
}
|
||||
}
|
||||
|
||||
// If there is only one line, and it is an empty match, return true.
|
||||
// If there is only one line, and it is an empty match, return false.
|
||||
if query_result_lines.len() == 1 {
|
||||
match query_result_lines[0].clone() {
|
||||
QueryResolutionLine::Match(m) => {
|
||||
if m.is_empty() {
|
||||
return QueryResolution::True;
|
||||
}
|
||||
if let QueryResolutionLine::Match(m) = query_result_lines[0].clone() {
|
||||
if m.is_empty() {
|
||||
return QueryResolution::False;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,13 +438,9 @@ impl From<Vec<QueryResolutionLine>> for QueryResolution {
|
||||
if query_result_lines
|
||||
.iter()
|
||||
.any(|l| l == &QueryResolutionLine::True)
|
||||
&& !query_result_lines.iter().any(|l| {
|
||||
if let &QueryResolutionLine::Match(_) = l {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
&& !query_result_lines
|
||||
.iter()
|
||||
.any(|l| matches!(l, QueryResolutionLine::Match(_)))
|
||||
{
|
||||
return QueryResolution::True;
|
||||
}
|
||||
@@ -95,13 +448,7 @@ impl From<Vec<QueryResolutionLine>> for QueryResolution {
|
||||
// If there is at least one match, return all matches.
|
||||
let all_matches = query_result_lines
|
||||
.into_iter()
|
||||
.filter(|l| {
|
||||
if let &QueryResolutionLine::Match(_) = l {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
.filter(|l| matches!(l, QueryResolutionLine::Match(_)))
|
||||
.map(|l| match l {
|
||||
QueryResolutionLine::Match(m) => QueryMatch::from(m),
|
||||
_ => unreachable!(),
|
||||
@@ -116,6 +463,14 @@ impl From<Vec<QueryResolutionLine>> for QueryResolution {
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<QueryResolutionLine> for QueryResolution {
|
||||
fn from_iter<I: IntoIterator<Item = QueryResolutionLine>>(iter: I) -> Self {
|
||||
// TODO: Probably a good idea to implement From<Vec<QueryResolutionLine>> based on this
|
||||
// instead.
|
||||
iter.into_iter().collect::<Vec<_>>().into()
|
||||
}
|
||||
}
|
||||
|
||||
fn split_response_string(input: &str) -> Vec<String> {
|
||||
let mut level_bracket = 0;
|
||||
let mut level_parenthesis = 0;
|
||||
@@ -132,7 +487,11 @@ fn split_response_string(input: &str) -> Vec<String> {
|
||||
')' => level_parenthesis -= 1,
|
||||
'"' => in_double_quotes = !in_double_quotes,
|
||||
'\'' => in_single_quotes = !in_single_quotes,
|
||||
',' if level_bracket == 0 && level_parenthesis == 0 && !in_double_quotes && !in_single_quotes => {
|
||||
',' if level_bracket == 0
|
||||
&& level_parenthesis == 0
|
||||
&& !in_double_quotes
|
||||
&& !in_single_quotes =>
|
||||
{
|
||||
result.push(input[start..i].trim().to_string());
|
||||
start = i + 1;
|
||||
}
|
||||
@@ -167,13 +526,13 @@ fn parse_prolog_response(input: &str) -> HashMap<String, String> {
|
||||
let key = result.0;
|
||||
let value = result.1;
|
||||
// cut off at given characters/strings:
|
||||
let value = value.split("\n").next().unwrap().to_string();
|
||||
let value = value.split(" ").next().unwrap().to_string();
|
||||
let value = value.split("\t").next().unwrap().to_string();
|
||||
let value = value.split('\n').next().unwrap().to_string();
|
||||
let value = value.split(' ').next().unwrap().to_string();
|
||||
let value = value.split('\t').next().unwrap().to_string();
|
||||
let value = value.split("error").next().unwrap().to_string();
|
||||
map.insert(key, value);
|
||||
}
|
||||
|
||||
|
||||
map
|
||||
}
|
||||
|
||||
@@ -192,9 +551,8 @@ impl TryFrom<String> for QueryResolutionLine {
|
||||
Ok((key, Value::try_from(value)?))
|
||||
})
|
||||
.filter_map(Result::ok)
|
||||
.collect::<BTreeMap<_, _>>()
|
||||
)
|
||||
),
|
||||
.collect::<BTreeMap<_, _>>(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -229,25 +587,25 @@ impl TryFrom<String> for Value {
|
||||
Ok(Value::Float(OrderedFloat(float_value)))
|
||||
} else if let Ok(int_value) = string.parse::<i128>() {
|
||||
Ok(Value::Integer(int_value.into()))
|
||||
} else if trimmed.starts_with("'") && trimmed.ends_with("'") {
|
||||
} else if trimmed.starts_with('\'') && trimmed.ends_with('\'')
|
||||
|| trimmed.starts_with('"') && trimmed.ends_with('"')
|
||||
{
|
||||
Ok(Value::String(trimmed[1..trimmed.len() - 1].into()))
|
||||
} else if trimmed.starts_with("\"") && trimmed.ends_with("\"") {
|
||||
Ok(Value::String(trimmed[1..trimmed.len() - 1].into()))
|
||||
} else if trimmed.starts_with("[") && trimmed.ends_with("]") {
|
||||
} else if trimmed.starts_with('[') && trimmed.ends_with(']') {
|
||||
let split = split_nested_list(&trimmed[1..trimmed.len() - 1]);
|
||||
|
||||
|
||||
let values = split
|
||||
.into_iter()
|
||||
.map(Value::try_from)
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
Ok(Value::List(values))
|
||||
} else if trimmed.starts_with("{") && trimmed.ends_with("}") {
|
||||
let mut iter = trimmed[1..trimmed.len() - 1].split(",");
|
||||
} else if trimmed.starts_with('{') && trimmed.ends_with('}') {
|
||||
let iter = trimmed[1..trimmed.len() - 1].split(',');
|
||||
let mut values = vec![];
|
||||
|
||||
while let Some(value) = iter.next() {
|
||||
let items: Vec<_> = value.split(":").collect();
|
||||
for value in iter {
|
||||
let items: Vec<_> = value.split(':').collect();
|
||||
if items.len() == 2 {
|
||||
let _key = items[0].to_string();
|
||||
let value = items[1].to_string();
|
||||
@@ -255,13 +613,13 @@ impl TryFrom<String> for Value {
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Value::Structure(atom!("{}"), values))
|
||||
Ok(Value::Structure("{}".into(), values))
|
||||
} else if trimmed.starts_with("<<") && trimmed.ends_with(">>") {
|
||||
let mut iter = trimmed[2..trimmed.len() - 2].split(",");
|
||||
let iter = trimmed[2..trimmed.len() - 2].split(',');
|
||||
let mut values = vec![];
|
||||
|
||||
while let Some(value) = iter.next() {
|
||||
let items: Vec<_> = value.split(":").collect();
|
||||
for value in iter {
|
||||
let items: Vec<_> = value.split(':').collect();
|
||||
if items.len() == 2 {
|
||||
let _key = items[0].to_string();
|
||||
let value = items[1].to_string();
|
||||
@@ -269,8 +627,8 @@ impl TryFrom<String> for Value {
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Value::Structure(atom!("<<>>"), values))
|
||||
} else if !trimmed.contains(",") && !trimmed.contains("'") && !trimmed.contains("\"") {
|
||||
Ok(Value::Structure("<<>>".into(), values))
|
||||
} else if !trimmed.contains(',') && !trimmed.contains('\'') && !trimmed.contains('"') {
|
||||
Ok(Value::String(trimmed.into()))
|
||||
} else {
|
||||
Err(())
|
||||
|
||||
@@ -34,10 +34,10 @@ impl From<Atom> for PartialString {
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Atom> for PartialString {
|
||||
impl From<PartialString> for Atom {
|
||||
#[inline]
|
||||
fn into(self: Self) -> Atom {
|
||||
self.0
|
||||
fn from(val: PartialString) -> Self {
|
||||
val.0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ impl PartialString {
|
||||
#[inline]
|
||||
pub(super) fn new<'a>(src: &'a str, atom_tbl: &AtomTable) -> Option<(Self, &'a str)> {
|
||||
let terminator_idx = scan_for_terminator(src.chars());
|
||||
let pstr = PartialString(AtomTable::build_with(&atom_tbl, &src[..terminator_idx]));
|
||||
let pstr = PartialString(AtomTable::build_with(atom_tbl, &src[..terminator_idx]));
|
||||
Some(if terminator_idx < src.as_bytes().len() {
|
||||
(pstr, &src[terminator_idx + 1..])
|
||||
} else {
|
||||
@@ -68,7 +68,7 @@ pub struct HeapPStrIter<'a> {
|
||||
stepper: fn(&mut HeapPStrIter<'a>) -> Option<PStrIteratee>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct PStrPrefixCmpResult {
|
||||
pub focus: usize,
|
||||
pub offset: usize,
|
||||
@@ -103,11 +103,6 @@ impl<'a> HeapPStrIter<'a> {
|
||||
self.focus.is_string_terminator(self.heap)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn num_steps(&self) -> usize {
|
||||
self.brent_st.num_steps()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn chars(mut self) -> PStrCharsIter<'a> {
|
||||
let item = self.next();
|
||||
@@ -154,13 +149,13 @@ impl<'a> HeapPStrIter<'a> {
|
||||
let s = &s[result.prefix_len..];
|
||||
|
||||
if s.len() >= t.len() {
|
||||
if (&*s).starts_with(&*t) {
|
||||
if s.starts_with(&*t) {
|
||||
result.prefix_len += t.len();
|
||||
result.offset += t.len();
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
} else if t.starts_with(&s) {
|
||||
} else if t.starts_with(s) {
|
||||
result.prefix_len += s.len();
|
||||
result.offset += s.len();
|
||||
|
||||
@@ -193,7 +188,7 @@ impl<'a> HeapPStrIter<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
final_result
|
||||
Some(result)
|
||||
}
|
||||
|
||||
fn walk_hare_to_cycle_end(&mut self) {
|
||||
@@ -218,10 +213,10 @@ impl<'a> HeapPStrIter<'a> {
|
||||
self.brent_st.hare = orig_hare;
|
||||
}
|
||||
|
||||
pub fn to_string(&mut self) -> String {
|
||||
pub fn to_string_mut(&mut self) -> String {
|
||||
let mut buf = String::with_capacity(32);
|
||||
|
||||
while let Some(iteratee) = self.next() {
|
||||
for iteratee in self.by_ref() {
|
||||
match iteratee {
|
||||
PStrIteratee::Char(_, c) => {
|
||||
buf.push(c);
|
||||
@@ -334,14 +329,10 @@ impl<'a> HeapPStrIter<'a> {
|
||||
heap_bound_deref(self.heap, self.heap[h]),
|
||||
);
|
||||
|
||||
return if let Some(c) = value.as_char() {
|
||||
Some(PStrIterStep {
|
||||
iteratee: PStrIteratee::Char(curr_hare, c),
|
||||
next_hare: h+1,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
return value.as_char().map(|c| PStrIterStep {
|
||||
iteratee: PStrIteratee::Char(curr_hare, c),
|
||||
next_hare: h+1,
|
||||
});
|
||||
}
|
||||
(HeapCellValueTag::Str, s) => {
|
||||
let (name, arity) = cell_as_atom_cell!(self.heap[s])
|
||||
@@ -353,14 +344,10 @@ impl<'a> HeapPStrIter<'a> {
|
||||
heap_bound_deref(self.heap, self.heap[s+1]),
|
||||
);
|
||||
|
||||
if let Some(c) = value.as_char() {
|
||||
Some(PStrIterStep {
|
||||
iteratee: PStrIteratee::Char(curr_hare, c),
|
||||
next_hare: s+2,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
value.as_char().map(|c| PStrIterStep {
|
||||
iteratee: PStrIteratee::Char(curr_hare, c),
|
||||
next_hare: s+2,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@@ -405,10 +392,7 @@ impl<'a> HeapPStrIter<'a> {
|
||||
|
||||
match self.brent_st.step(next_hare) {
|
||||
Some(cycle_result) => {
|
||||
debug_assert!(match cycle_result {
|
||||
CycleSearchResult::Cyclic(..) => true,
|
||||
_ => false,
|
||||
});
|
||||
debug_assert!(matches!(cycle_result, CycleSearchResult::Cyclic(..)));
|
||||
|
||||
self.walk_hare_to_cycle_end();
|
||||
self.stepper = HeapPStrIter::post_cycle_discovery_stepper;
|
||||
@@ -550,11 +534,7 @@ pub enum PStrCmpResult {
|
||||
impl PStrCmpResult {
|
||||
#[inline]
|
||||
pub fn is_second_iter(&self) -> bool {
|
||||
if let PStrCmpResult::SecondIterContinuable(_) = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
matches!(self, PStrCmpResult::SecondIterContinuable(_))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -600,8 +580,8 @@ pub fn compare_pstr_prefixes<'a>(
|
||||
return PStrCmpResult::Ordered(c1.cmp(&c2));
|
||||
}
|
||||
|
||||
cycle_detection_step(i1, i2, &step_1);
|
||||
let both_cyclic = cycle_detection_step(i2, i1, &step_2);
|
||||
cycle_detection_step(i1, i2, step_1);
|
||||
let both_cyclic = cycle_detection_step(i2, i1, step_2);
|
||||
|
||||
r1 = step(i1, i1.brent_st.hare);
|
||||
r2 = step(i2, i2.brent_st.hare);
|
||||
@@ -623,15 +603,15 @@ pub fn compare_pstr_prefixes<'a>(
|
||||
if n1 < pstr_atom.len() {
|
||||
step_2.iteratee = PStrIteratee::PStrSegment(f2, pstr_atom, n1);
|
||||
|
||||
let c1_result = cycle_detection_step(i1, i2, &step_1);
|
||||
let c1_result = cycle_detection_step(i1, i2, step_1);
|
||||
r1 = step(i1, i1.brent_st.hare);
|
||||
|
||||
if !c1_result {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
cycle_detection_step(i1, i2, &step_1);
|
||||
let both_cyclic = cycle_detection_step(i2, i1, &step_2);
|
||||
cycle_detection_step(i1, i2, step_1);
|
||||
let both_cyclic = cycle_detection_step(i2, i1, step_2);
|
||||
|
||||
r1 = step(i1, i1.brent_st.hare);
|
||||
r2 = step(i2, i2.brent_st.hare);
|
||||
@@ -641,7 +621,7 @@ pub fn compare_pstr_prefixes<'a>(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let c2_result = cycle_detection_step(i2, i1, &step_2);
|
||||
let c2_result = cycle_detection_step(i2, i1, step_2);
|
||||
r2 = step(i2, i2.brent_st.hare);
|
||||
|
||||
if !c2_result {
|
||||
@@ -662,15 +642,15 @@ pub fn compare_pstr_prefixes<'a>(
|
||||
if n1 < pstr_atom.len() {
|
||||
step_1.iteratee = PStrIteratee::PStrSegment(f1, pstr_atom, n1);
|
||||
|
||||
let c2_result = cycle_detection_step(i2, i1, &step_2);
|
||||
let c2_result = cycle_detection_step(i2, i1, step_2);
|
||||
r2 = step(i2, step_2.next_hare);
|
||||
|
||||
if !c2_result {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
cycle_detection_step(i1, i2, &step_1);
|
||||
let both_cyclic = cycle_detection_step(i2, i1, &step_2);
|
||||
cycle_detection_step(i1, i2, step_1);
|
||||
let both_cyclic = cycle_detection_step(i2, i1, step_2);
|
||||
|
||||
r1 = step(i1, i1.brent_st.hare);
|
||||
r2 = step(i2, i2.brent_st.hare);
|
||||
@@ -680,7 +660,7 @@ pub fn compare_pstr_prefixes<'a>(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let c1_result = cycle_detection_step(i1, i2, &step_1);
|
||||
let c1_result = cycle_detection_step(i1, i2, step_1);
|
||||
r1 = step(i1, i1.brent_st.hare);
|
||||
|
||||
if !c1_result {
|
||||
@@ -693,8 +673,8 @@ pub fn compare_pstr_prefixes<'a>(
|
||||
PStrIteratee::PStrSegment(f2, pstr2_atom, n2),
|
||||
) => {
|
||||
if pstr1_atom == pstr2_atom && n1 == n2 {
|
||||
cycle_detection_step(i1, i2, &step_1);
|
||||
let both_cyclic = cycle_detection_step(i2, i1, &step_2);
|
||||
cycle_detection_step(i1, i2, step_1);
|
||||
let both_cyclic = cycle_detection_step(i2, i1, step_2);
|
||||
|
||||
r1 = step(i1, i1.brent_st.hare);
|
||||
r2 = step(i2, i2.brent_st.hare);
|
||||
@@ -713,9 +693,9 @@ pub fn compare_pstr_prefixes<'a>(
|
||||
let str2 = pstr2.as_str_from(n2);
|
||||
|
||||
match str1.len().cmp(&str2.len()) {
|
||||
Ordering::Equal if &*str1 == &*str2 => {
|
||||
cycle_detection_step(i1, i2, &step_1);
|
||||
let both_cyclic = cycle_detection_step(i2, i1, &step_2);
|
||||
Ordering::Equal if *str1 == *str2 => {
|
||||
cycle_detection_step(i1, i2, step_1);
|
||||
let both_cyclic = cycle_detection_step(i2, i1, step_2);
|
||||
|
||||
r1 = step(i1, i1.brent_st.hare);
|
||||
r2 = step(i2, i2.brent_st.hare);
|
||||
@@ -727,7 +707,7 @@ pub fn compare_pstr_prefixes<'a>(
|
||||
Ordering::Less if str2.starts_with(&*str1) => {
|
||||
step_2.iteratee =
|
||||
PStrIteratee::PStrSegment(f2, pstr2_atom, n2 + str1.len());
|
||||
let c1_result = cycle_detection_step(i1, i2, &step_1);
|
||||
let c1_result = cycle_detection_step(i1, i2, step_1);
|
||||
r1 = step(i1, i1.brent_st.hare);
|
||||
|
||||
if !c1_result {
|
||||
@@ -737,7 +717,7 @@ pub fn compare_pstr_prefixes<'a>(
|
||||
Ordering::Greater if str1.starts_with(&*str2) => {
|
||||
step_1.iteratee =
|
||||
PStrIteratee::PStrSegment(f1, pstr1_atom, n1 + str2.len());
|
||||
let c2_result = cycle_detection_step(i2, i1, &step_2);
|
||||
let c2_result = cycle_detection_step(i2, i1, step_2);
|
||||
r2 = step(i2, i2.brent_st.hare);
|
||||
|
||||
if !c2_result {
|
||||
@@ -778,20 +758,34 @@ pub fn compare_pstr_prefixes<'a>(
|
||||
if i1.focus == empty_list_as_cell!() {
|
||||
PStrCmpResult::Ordered(Ordering::Less)
|
||||
} else {
|
||||
PStrCmpResult::SecondIterContinuable(r2.unwrap().iteratee)
|
||||
let r2_step = r2.unwrap();
|
||||
|
||||
// advance i2 to the next character so the same character
|
||||
// isn't repeated
|
||||
if matches!(r2_step.iteratee, PStrIteratee::Char(..)) {
|
||||
cycle_detection_step(i2, i1, &r2_step);
|
||||
}
|
||||
|
||||
PStrCmpResult::SecondIterContinuable(r2_step.iteratee)
|
||||
}
|
||||
} else if r2_at_end {
|
||||
if i2.focus == empty_list_as_cell!() {
|
||||
PStrCmpResult::Ordered(Ordering::Greater)
|
||||
} else {
|
||||
PStrCmpResult::FirstIterContinuable(r1.unwrap().iteratee)
|
||||
let r1_step = r1.unwrap();
|
||||
|
||||
// advance i1 to the next character so the same character
|
||||
// isn't repeated
|
||||
if matches!(r1_step.iteratee, PStrIteratee::Char(..)) {
|
||||
cycle_detection_step(i1, i2, &r1_step);
|
||||
}
|
||||
|
||||
PStrCmpResult::FirstIterContinuable(r1_step.iteratee)
|
||||
}
|
||||
} else if i1.is_continuable() && i2.is_continuable() {
|
||||
PStrCmpResult::Ordered(Ordering::Equal)
|
||||
} else {
|
||||
if i1.is_continuable() && i2.is_continuable() {
|
||||
PStrCmpResult::Ordered(Ordering::Equal)
|
||||
} else {
|
||||
PStrCmpResult::Unordered
|
||||
}
|
||||
PStrCmpResult::Unordered
|
||||
}
|
||||
}
|
||||
|
||||
@@ -801,6 +795,7 @@ mod test {
|
||||
use crate::machine::mock_wam::*;
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||
fn pstr_iter_tests() {
|
||||
let mut wam = MockWAM::new();
|
||||
|
||||
@@ -885,7 +880,7 @@ mod test {
|
||||
{
|
||||
let mut iter = HeapPStrIter::new(&wam.machine_st.heap, 0);
|
||||
|
||||
while let Some(_) = iter.next() {}
|
||||
for _ in iter.by_ref() {}
|
||||
|
||||
assert!(!iter.at_string_terminator());
|
||||
}
|
||||
@@ -1009,7 +1004,7 @@ mod test {
|
||||
|
||||
unify!(wam.machine_st, cstr_var_cell, heap_loc_as_cell!(1));
|
||||
|
||||
assert_eq!(wam.machine_st.fail, false);
|
||||
assert!(!wam.machine_st.fail);
|
||||
|
||||
assert_eq!(wam.machine_st.heap[2], char_as_cell!('a'),);
|
||||
|
||||
@@ -1032,7 +1027,7 @@ mod test {
|
||||
|
||||
unify!(wam.machine_st, cstr_var_cell, heap_loc_as_cell!(1));
|
||||
|
||||
assert_eq!(wam.machine_st.fail, false);
|
||||
assert!(!wam.machine_st.fail);
|
||||
|
||||
// test "abc" = [X,b,Z].
|
||||
|
||||
@@ -1054,7 +1049,7 @@ mod test {
|
||||
|
||||
unify!(wam.machine_st, cstr_var_cell, heap_loc_as_cell!(1));
|
||||
|
||||
assert_eq!(wam.machine_st.fail, false);
|
||||
assert!(!wam.machine_st.fail);
|
||||
|
||||
assert_eq!(wam.machine_st.heap[2], char_as_cell!('a'),);
|
||||
|
||||
@@ -1075,7 +1070,7 @@ mod test {
|
||||
|
||||
print_heap_terms(wam.machine_st.heap.iter(), 0);
|
||||
|
||||
assert_eq!(wam.machine_st.fail, false);
|
||||
assert!(!wam.machine_st.fail);
|
||||
|
||||
assert_eq!(wam.machine_st.heap[2], pstr_loc_as_cell!(5));
|
||||
assert_eq!(wam.machine_st.heap[3], pstr_loc_as_cell!(1));
|
||||
@@ -1105,9 +1100,119 @@ mod test {
|
||||
Some(PStrIteratee::PStrSegment(2, atom!("abc"), 1))
|
||||
);
|
||||
|
||||
// assert!(iter.next().is_none());
|
||||
|
||||
while let Some(_) = iter.next() {}
|
||||
for _ in iter {}
|
||||
}
|
||||
|
||||
// #2293, test1.
|
||||
|
||||
wam.machine_st.heap.clear();
|
||||
|
||||
wam.machine_st.heap.push(atom_as_cstr_cell!(atom!("a ")));
|
||||
wam.machine_st.heap.push(heap_loc_as_cell!(1));
|
||||
wam.machine_st.heap.push(list_loc_as_cell!(3));
|
||||
wam.machine_st.heap.push(char_as_cell!(' '));
|
||||
wam.machine_st.heap.push(empty_list_as_cell!());
|
||||
|
||||
unify!(wam.machine_st, list_loc_as_cell!(1), heap_loc_as_cell!(0));
|
||||
|
||||
assert!(!wam.machine_st.fail);
|
||||
|
||||
// #2293, test2.
|
||||
|
||||
wam.machine_st.heap.clear();
|
||||
|
||||
wam.machine_st.heap.push(atom_as_cstr_cell!(atom!(" a")));
|
||||
wam.machine_st.heap.push(char_as_cell!(' '));
|
||||
wam.machine_st.heap.push(list_loc_as_cell!(3));
|
||||
wam.machine_st.heap.push(heap_loc_as_cell!(3));
|
||||
wam.machine_st.heap.push(empty_list_as_cell!());
|
||||
|
||||
unify!(wam.machine_st, list_loc_as_cell!(1), heap_loc_as_cell!(0));
|
||||
|
||||
assert!(!wam.machine_st.fail);
|
||||
|
||||
// #2293, test3.
|
||||
|
||||
wam.machine_st.heap.clear();
|
||||
|
||||
wam.machine_st.heap.push(atom_as_cstr_cell!(atom!("a b")));
|
||||
wam.machine_st.heap.push(heap_loc_as_cell!(1));
|
||||
wam.machine_st.heap.push(list_loc_as_cell!(3));
|
||||
wam.machine_st.heap.push(char_as_cell!(' '));
|
||||
wam.machine_st.heap.push(list_loc_as_cell!(5));
|
||||
wam.machine_st.heap.push(heap_loc_as_cell!(5));
|
||||
wam.machine_st.heap.push(empty_list_as_cell!());
|
||||
|
||||
unify!(wam.machine_st, list_loc_as_cell!(1), heap_loc_as_cell!(0));
|
||||
|
||||
assert!(!wam.machine_st.fail);
|
||||
|
||||
// #2293, test4.
|
||||
|
||||
wam.machine_st.heap.clear();
|
||||
|
||||
wam.machine_st.heap.push(atom_as_cstr_cell!(atom!(" a ")));
|
||||
wam.machine_st.heap.push(char_as_cell!(' '));
|
||||
wam.machine_st.heap.push(list_loc_as_cell!(3));
|
||||
wam.machine_st.heap.push(heap_loc_as_cell!(3));
|
||||
wam.machine_st.heap.push(list_loc_as_cell!(5));
|
||||
wam.machine_st.heap.push(char_as_cell!(' '));
|
||||
wam.machine_st.heap.push(empty_list_as_cell!());
|
||||
|
||||
unify!(wam.machine_st, list_loc_as_cell!(1), heap_loc_as_cell!(0));
|
||||
|
||||
assert!(!wam.machine_st.fail);
|
||||
|
||||
// #2293, test5.
|
||||
|
||||
wam.machine_st.heap.clear();
|
||||
|
||||
wam.machine_st.heap.push(atom_as_cstr_cell!(atom!(" a bc")));
|
||||
wam.machine_st.heap.push(char_as_cell!(' '));
|
||||
wam.machine_st.heap.push(list_loc_as_cell!(3));
|
||||
wam.machine_st.heap.push(heap_loc_as_cell!(3));
|
||||
wam.machine_st.heap.push(list_loc_as_cell!(5));
|
||||
wam.machine_st.heap.push(char_as_cell!(' '));
|
||||
wam.machine_st.heap.push(heap_loc_as_cell!(6));
|
||||
|
||||
unify!(wam.machine_st, list_loc_as_cell!(1), heap_loc_as_cell!(0));
|
||||
|
||||
assert!(!wam.machine_st.fail);
|
||||
|
||||
// #2293, test6.
|
||||
|
||||
wam.machine_st.heap.clear();
|
||||
|
||||
wam.machine_st.heap.push(atom_as_cstr_cell!(atom!("abc")));
|
||||
wam.machine_st.heap.push(heap_loc_as_cell!(1));
|
||||
wam.machine_st.heap.push(list_loc_as_cell!(3));
|
||||
wam.machine_st.heap.push(char_as_cell!('b'));
|
||||
wam.machine_st.heap.push(list_loc_as_cell!(5));
|
||||
wam.machine_st.heap.push(heap_loc_as_cell!(5));
|
||||
wam.machine_st.heap.push(empty_list_as_cell!());
|
||||
|
||||
unify!(wam.machine_st, list_loc_as_cell!(1), heap_loc_as_cell!(0));
|
||||
|
||||
assert!(!wam.machine_st.fail);
|
||||
|
||||
// #2293, test7.
|
||||
|
||||
wam.machine_st.heap.clear();
|
||||
|
||||
wam.machine_st.heap.push(atom_as_cstr_cell!(atom!("abcde")));
|
||||
wam.machine_st.heap.push(char_as_cell!('a'));
|
||||
wam.machine_st.heap.push(list_loc_as_cell!(3));
|
||||
wam.machine_st.heap.push(heap_loc_as_cell!(3));
|
||||
wam.machine_st.heap.push(list_loc_as_cell!(5));
|
||||
wam.machine_st.heap.push(char_as_cell!('c'));
|
||||
wam.machine_st.heap.push(list_loc_as_cell!(7));
|
||||
wam.machine_st.heap.push(heap_loc_as_cell!(7));
|
||||
wam.machine_st.heap.push(list_loc_as_cell!(9));
|
||||
wam.machine_st.heap.push(char_as_cell!('e'));
|
||||
wam.machine_st.heap.push(empty_list_as_cell!());
|
||||
|
||||
unify!(wam.machine_st, list_loc_as_cell!(1), heap_loc_as_cell!(0));
|
||||
|
||||
assert!(!wam.machine_st.fail);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,42 +11,74 @@ use indexmap::IndexSet;
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::convert::TryFrom;
|
||||
pub(crate) fn to_op_decl(prec: u16, spec: OpDeclSpec, name: Atom) -> OpDecl {
|
||||
OpDecl::new(OpDesc::build_with(prec, spec), name)
|
||||
}
|
||||
|
||||
pub(crate) fn to_op_decl(prec: u16, spec: Atom, name: Atom) -> Result<OpDecl, CompilationError> {
|
||||
match spec {
|
||||
atom!("xfx") => Ok(OpDecl::new(OpDesc::build_with(prec, XFX as u8), name)),
|
||||
atom!("xfy") => Ok(OpDecl::new(OpDesc::build_with(prec, XFY as u8), name)),
|
||||
atom!("yfx") => Ok(OpDecl::new(OpDesc::build_with(prec, YFX as u8), name)),
|
||||
atom!("fx") => Ok(OpDecl::new(OpDesc::build_with(prec, FX as u8), name)),
|
||||
atom!("fy") => Ok(OpDecl::new(OpDesc::build_with(prec, FY as u8), name)),
|
||||
atom!("xf") => Ok(OpDecl::new(OpDesc::build_with(prec, XF as u8), name)),
|
||||
atom!("yf") => Ok(OpDecl::new(OpDesc::build_with(prec, YF as u8), name)),
|
||||
_ => Err(CompilationError::InconsistentEntry),
|
||||
}
|
||||
pub(crate) fn to_op_decl_spec(spec: Atom) -> Result<OpDeclSpec, CompilationError> {
|
||||
OpDeclSpec::try_from(spec).map_err(|_err| {
|
||||
CompilationError::InvalidDirective(DirectiveError::InvalidOpDeclSpecValue(spec))
|
||||
})
|
||||
}
|
||||
|
||||
fn setup_op_decl(mut terms: Vec<Term>, atom_tbl: &AtomTable) -> Result<OpDecl, CompilationError> {
|
||||
// should allow non-partial lists?
|
||||
let name = match terms.pop().unwrap() {
|
||||
Term::Literal(_, Literal::Atom(name)) => name,
|
||||
Term::Literal(_, Literal::Char(c)) => AtomTable::build_with(atom_tbl, &c.to_string()),
|
||||
_ => return Err(CompilationError::InconsistentEntry),
|
||||
other => {
|
||||
return Err(CompilationError::InvalidDirective(
|
||||
DirectiveError::InvalidOpDeclNameType(other),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
let spec = match terms.pop().unwrap() {
|
||||
Term::Literal(_, Literal::Atom(name)) => name,
|
||||
Term::Literal(_, Literal::Char(c)) => AtomTable::build_with(atom_tbl, &c.to_string()),
|
||||
_ => return Err(CompilationError::InconsistentEntry),
|
||||
other => {
|
||||
return Err(CompilationError::InvalidDirective(
|
||||
DirectiveError::InvalidOpDeclSpecDomain(other),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
let spec = to_op_decl_spec(spec)?;
|
||||
|
||||
let prec = match terms.pop().unwrap() {
|
||||
Term::Literal(_, Literal::Fixnum(bi)) => match u16::try_from(bi.get_num()) {
|
||||
Ok(n) if n <= 1200 => n,
|
||||
_ => return Err(CompilationError::InconsistentEntry),
|
||||
_ => {
|
||||
return Err(CompilationError::InvalidDirective(
|
||||
DirectiveError::InvalidOpDeclPrecDomain(bi),
|
||||
));
|
||||
}
|
||||
},
|
||||
_ => return Err(CompilationError::InconsistentEntry),
|
||||
other => {
|
||||
return Err(CompilationError::InvalidDirective(
|
||||
DirectiveError::InvalidOpDeclPrecType(other),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
to_op_decl(prec, spec, name)
|
||||
if name == "[]" || name == "{}" {
|
||||
return Err(CompilationError::InvalidDirective(
|
||||
DirectiveError::ShallNotCreate(name),
|
||||
));
|
||||
}
|
||||
|
||||
if name == "," {
|
||||
return Err(CompilationError::InvalidDirective(
|
||||
DirectiveError::ShallNotModify(name),
|
||||
));
|
||||
}
|
||||
|
||||
if name == "|" && (prec < 1001 || !spec.is_infix()) {
|
||||
return Err(CompilationError::InvalidDirective(
|
||||
DirectiveError::ShallNotCreate(name),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(to_op_decl(prec, spec, name))
|
||||
}
|
||||
|
||||
fn setup_predicate_indicator(term: &mut Term) -> Result<PredicateKey, CompilationError> {
|
||||
@@ -100,7 +132,7 @@ fn setup_module_export(
|
||||
}
|
||||
|
||||
pub(crate) fn build_rule_body(vars: &[Term], body_term: Term) -> Term {
|
||||
let head_term = Term::Clause(Cell::default(), atom!(""), vars.iter().cloned().collect());
|
||||
let head_term = Term::Clause(Cell::default(), atom!(""), vars.to_vec());
|
||||
let rule = vec![head_term, body_term];
|
||||
|
||||
Term::Clause(Cell::default(), atom!(":-"), rule)
|
||||
@@ -238,7 +270,7 @@ fn setup_meta_predicate<'a, LS: LoadState<'a>>(
|
||||
) -> Result<(Atom, Vec<MetaSpec>), CompilationError> {
|
||||
let mut meta_specs = vec![];
|
||||
|
||||
for meta_spec in terms.into_iter() {
|
||||
for meta_spec in terms.iter_mut() {
|
||||
match meta_spec {
|
||||
Term::Literal(_, Literal::Atom(meta_spec)) => {
|
||||
let meta_spec = match meta_spec {
|
||||
@@ -310,11 +342,11 @@ pub(super) fn setup_declaration<'a, LS: LoadState<'a>>(
|
||||
}
|
||||
(atom!("module"), 2) => {
|
||||
let atom_tbl = &mut LS::machine_st(&mut loader.payload).atom_tbl;
|
||||
Ok(Declaration::Module(setup_module_decl(terms, &atom_tbl)?))
|
||||
Ok(Declaration::Module(setup_module_decl(terms, atom_tbl)?))
|
||||
}
|
||||
(atom!("op"), 3) => {
|
||||
let atom_tbl = &mut LS::machine_st(&mut loader.payload).atom_tbl;
|
||||
Ok(Declaration::Op(setup_op_decl(terms, &atom_tbl)?))
|
||||
Ok(Declaration::Op(setup_op_decl(terms, atom_tbl)?))
|
||||
}
|
||||
(atom!("non_counted_backtracking"), 1) => {
|
||||
let (name, arity) = setup_predicate_indicator(&mut terms.pop().unwrap())?;
|
||||
@@ -323,7 +355,7 @@ pub(super) fn setup_declaration<'a, LS: LoadState<'a>>(
|
||||
(atom!("use_module"), 1) => Ok(Declaration::UseModule(setup_use_module_decl(terms)?)),
|
||||
(atom!("use_module"), 2) => {
|
||||
let atom_tbl = &mut LS::machine_st(&mut loader.payload).atom_tbl;
|
||||
let (name, exports) = setup_qualified_import(terms, &atom_tbl)?;
|
||||
let (name, exports) = setup_qualified_import(terms, atom_tbl)?;
|
||||
|
||||
Ok(Declaration::UseQualifiedModule(name, exports))
|
||||
}
|
||||
@@ -331,9 +363,13 @@ pub(super) fn setup_declaration<'a, LS: LoadState<'a>>(
|
||||
let (module_name, name, meta_specs) = setup_meta_predicate(terms, loader)?;
|
||||
Ok(Declaration::MetaPredicate(module_name, name, meta_specs))
|
||||
}
|
||||
_ => Err(CompilationError::InconsistentEntry),
|
||||
_ => Err(CompilationError::InvalidDirective(
|
||||
DirectiveError::InvalidDirective(name, terms.len()),
|
||||
)),
|
||||
},
|
||||
_ => Err(CompilationError::InconsistentEntry),
|
||||
other => Err(CompilationError::InvalidDirective(
|
||||
DirectiveError::ExpectedDirective(other),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -565,21 +601,6 @@ impl Preprocessor {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
fn try_term_to_query<'a, LS: LoadState<'a>>(
|
||||
&mut self,
|
||||
loader: &mut Loader<'a, LS>,
|
||||
terms: Vec<Term>,
|
||||
cut_context: CutContext,
|
||||
) -> Result<TopLevel, CompilationError> {
|
||||
Ok(TopLevel::Query(self.setup_query(
|
||||
loader,
|
||||
terms,
|
||||
cut_context,
|
||||
)?))
|
||||
}
|
||||
*/
|
||||
|
||||
pub(super) fn try_term_to_tl<'a, LS: LoadState<'a>>(
|
||||
&mut self,
|
||||
loader: &mut Loader<'a, LS>,
|
||||
@@ -607,20 +628,4 @@ impl Preprocessor {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
fn try_terms_to_tls<'a, I: IntoIterator<Item = Term>, LS: LoadState<'a>>(
|
||||
&mut self,
|
||||
loader: &mut Loader<'a, LS>,
|
||||
terms: I,
|
||||
) -> Result<VecDeque<TopLevel>, CompilationError> {
|
||||
let mut results = VecDeque::new();
|
||||
|
||||
for term in terms.into_iter() {
|
||||
results.push_back(self.try_term_to_tl(loader, term)?);
|
||||
}
|
||||
|
||||
Ok(results)
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
:- module('$project_atts', [copy_term/3]).
|
||||
:- module('$project_atts', []).
|
||||
|
||||
:- use_module(library(dcgs)).
|
||||
:- use_module(library(error), [can_be/2]).
|
||||
@@ -100,14 +100,6 @@ gather_residual_goals([V|Vs]) -->
|
||||
|
||||
delete_all_attributes_from_var(V) :- '$delete_all_attributes_from_var'(V).
|
||||
|
||||
copy_term(Term, Copy, Gs) :-
|
||||
can_be(list, Gs),
|
||||
findall(Term-Rs, term_residual_goals(Term,Rs), [Copy-Gs]),
|
||||
( var(Gs) ->
|
||||
Gs = []
|
||||
; true
|
||||
).
|
||||
|
||||
term_residual_goals(Term,Rs) :-
|
||||
'$term_attributed_variables'(Term, Vs),
|
||||
phrase(gather_residual_goals(Vs), Rs),
|
||||
|
||||
@@ -15,7 +15,9 @@ impl RawBlockTraits for Stack {
|
||||
|
||||
#[inline]
|
||||
fn align() -> usize {
|
||||
mem::align_of::<HeapCellValue>()
|
||||
mem::align_of::<OrFrame>()
|
||||
.max(mem::align_of::<AndFrame>())
|
||||
.max(mem::align_of::<HeapCellValue>())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +58,7 @@ impl Index<usize> for AndFrame {
|
||||
let index_offset = (index - 1) * mem::size_of::<HeapCellValue>();
|
||||
|
||||
unsafe {
|
||||
let ptr = mem::transmute::<&AndFrame, *const u8>(self);
|
||||
let ptr = self as *const crate::machine::stack::AndFrame as *const u8;
|
||||
let ptr = ptr as usize + prelude_offset + index_offset;
|
||||
|
||||
&*(ptr as *const HeapCellValue)
|
||||
@@ -70,7 +72,7 @@ impl IndexMut<usize> for AndFrame {
|
||||
let index_offset = (index - 1) * mem::size_of::<HeapCellValue>();
|
||||
|
||||
unsafe {
|
||||
let ptr = mem::transmute::<&mut AndFrame, *const u8>(self);
|
||||
let ptr = self as *mut crate::machine::stack::AndFrame as *const u8;
|
||||
let ptr = ptr as usize + prelude_offset + index_offset;
|
||||
|
||||
&mut *(ptr as *mut HeapCellValue)
|
||||
@@ -129,7 +131,7 @@ impl Index<usize> for OrFrame {
|
||||
let index_offset = index * mem::size_of::<HeapCellValue>();
|
||||
|
||||
unsafe {
|
||||
let ptr = mem::transmute::<&OrFrame, *const u8>(self);
|
||||
let ptr = self as *const crate::machine::stack::OrFrame as *const u8;
|
||||
let ptr = ptr as usize + prelude_offset + index_offset;
|
||||
|
||||
&*(ptr as *const HeapCellValue)
|
||||
@@ -144,7 +146,7 @@ impl IndexMut<usize> for OrFrame {
|
||||
let index_offset = index * mem::size_of::<HeapCellValue>();
|
||||
|
||||
unsafe {
|
||||
let ptr = mem::transmute::<&mut OrFrame, *const u8>(self);
|
||||
let ptr = self as *mut crate::machine::stack::OrFrame as *const u8;
|
||||
let ptr = ptr as usize + prelude_offset + index_offset;
|
||||
|
||||
&mut *(ptr as *mut HeapCellValue)
|
||||
@@ -172,7 +174,9 @@ impl Stack {
|
||||
let ptr = self.buf.alloc(frame_size);
|
||||
|
||||
if ptr.is_null() {
|
||||
self.buf.grow();
|
||||
if !self.buf.grow() {
|
||||
panic!("growing the stack failed")
|
||||
}
|
||||
} else {
|
||||
return ptr;
|
||||
}
|
||||
@@ -189,7 +193,7 @@ impl Stack {
|
||||
|
||||
for idx in 0..num_cells {
|
||||
ptr::write(
|
||||
(new_ptr as usize + offset) as *mut HeapCellValue,
|
||||
new_ptr.add(offset) as *mut HeapCellValue,
|
||||
stack_loc_as_cell!(AndFrame, e, idx + 1),
|
||||
);
|
||||
|
||||
@@ -203,6 +207,10 @@ impl Stack {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn top(&self) -> usize {
|
||||
unsafe { (*self.buf.ptr.get()) as usize - self.buf.base as usize }
|
||||
}
|
||||
|
||||
pub(crate) fn allocate_or_frame(&mut self, num_cells: usize) -> usize {
|
||||
let frame_size = OrFrame::size_of(num_cells);
|
||||
|
||||
@@ -238,7 +246,8 @@ impl Stack {
|
||||
#[inline(always)]
|
||||
pub(crate) fn index_and_frame_mut(&mut self, e: usize) -> &mut AndFrame {
|
||||
unsafe {
|
||||
let ptr = self.buf.base as usize + e;
|
||||
// This is doing alignment wrong
|
||||
let ptr = self.buf.base.add(e);
|
||||
&mut *(ptr as *mut AndFrame)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,10 @@ use crate::machine::machine_indices::*;
|
||||
use crate::machine::machine_state::*;
|
||||
use crate::types::*;
|
||||
|
||||
pub use modular_bitfield::prelude::*;
|
||||
pub use scryer_modular_bitfield::prelude::*;
|
||||
|
||||
#[cfg(feature = "http")]
|
||||
use bytes::{buf::Reader as BufReader, Buf, Bytes};
|
||||
use std::cmp::Ordering;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
@@ -22,11 +24,9 @@ use std::fs::{File, OpenOptions};
|
||||
use std::hash::Hash;
|
||||
use std::io;
|
||||
use std::io::{Cursor, ErrorKind, Read, Seek, SeekFrom, Write};
|
||||
#[cfg(feature = "http")]
|
||||
use std::io::BufRead;
|
||||
use std::mem;
|
||||
use std::net::{Shutdown, TcpStream};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::path::PathBuf;
|
||||
use std::ptr;
|
||||
|
||||
#[cfg(feature = "tls")]
|
||||
@@ -161,7 +161,7 @@ impl StreamLayout<CharReader<InputFileStream>> {
|
||||
// its pending buffer length from position.
|
||||
self.get_mut()
|
||||
.file
|
||||
.seek(SeekFrom::Current(0))
|
||||
.stream_position()
|
||||
.map(|pos| pos - self.stream.rem_buf_len() as u64)
|
||||
.ok()
|
||||
}
|
||||
@@ -274,7 +274,7 @@ impl Write for NamedTlsStream {
|
||||
#[cfg(feature = "http")]
|
||||
pub struct HttpReadStream {
|
||||
url: Atom,
|
||||
body_reader: Box<dyn BufRead>,
|
||||
body_reader: BufReader<Bytes>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "http")]
|
||||
@@ -295,9 +295,9 @@ impl Read for HttpReadStream {
|
||||
#[cfg(feature = "http")]
|
||||
pub struct HttpWriteStream {
|
||||
status_code: u16,
|
||||
headers: mem::ManuallyDrop<hyper::HeaderMap>,
|
||||
headers: std::mem::ManuallyDrop<hyper::HeaderMap>,
|
||||
response: TypedArenaPtr<HttpResponse>,
|
||||
buffer: mem::ManuallyDrop<Vec<u8>>,
|
||||
buffer: std::mem::ManuallyDrop<Vec<u8>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "http")]
|
||||
@@ -317,33 +317,34 @@ impl Write for HttpWriteStream {
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> std::io::Result<()> {
|
||||
Ok(())
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[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)]
|
||||
fn drop(&mut self) {
|
||||
let headers = unsafe { mem::ManuallyDrop::take(&mut self.headers) };
|
||||
let buffer = unsafe { mem::ManuallyDrop::take(&mut self.buffer) };
|
||||
|
||||
let (ready, response, cvar) = &**self.response;
|
||||
let headers = unsafe { std::mem::ManuallyDrop::take(&mut self.headers) };
|
||||
let buffer = unsafe { std::mem::ManuallyDrop::take(&mut self.buffer) };
|
||||
|
||||
let mut ready = ready.lock().unwrap();
|
||||
{
|
||||
let mut response = response.lock().unwrap();
|
||||
|
||||
let mut response_ = warp::http::Response::builder()
|
||||
.status(self.status_code);
|
||||
*response_.headers_mut().unwrap() = headers;
|
||||
*response = Some(response_.body(warp::hyper::Body::from(buffer)).unwrap());
|
||||
}
|
||||
*ready = true;
|
||||
cvar.notify_one();
|
||||
let (ready, response, cvar) = &**self.response;
|
||||
|
||||
let mut ready = ready.lock().unwrap();
|
||||
{
|
||||
let mut response = response.lock().unwrap();
|
||||
|
||||
let mut response_ = warp::http::Response::builder().status(self.status_code);
|
||||
*response_.headers_mut().unwrap() = headers;
|
||||
*response = Some(response_.body(warp::hyper::Body::from(buffer)).unwrap());
|
||||
}
|
||||
*ready = true;
|
||||
cvar.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct StandardOutputStream {}
|
||||
|
||||
@@ -389,7 +390,7 @@ impl StreamOptions {
|
||||
#[inline]
|
||||
pub fn get_alias(self) -> Option<Atom> {
|
||||
if self.has_alias() {
|
||||
Some(Atom::from((self.alias() as u64) << 3))
|
||||
Some(Atom::from(self.alias() << 3))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -453,25 +454,33 @@ impl<T> DerefMut for StreamLayout<T> {
|
||||
|
||||
macro_rules! arena_allocated_impl_for_stream {
|
||||
($stream_type:ty, $stream_tag:ident) => {
|
||||
impl ArenaAllocated for StreamLayout<$stream_type> {
|
||||
type PtrToAllocated = TypedArenaPtr<StreamLayout<$stream_type>>;
|
||||
impl $crate::arena::AllocateInArena<$stream_tag> for StreamLayout<$stream_type> {
|
||||
fn arena_allocate(self, arena: &mut Arena) -> TypedArenaPtr<$stream_tag> {
|
||||
$stream_tag::alloc(arena, core::mem::ManuallyDrop::new(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl ArenaAllocated for $stream_tag {
|
||||
type Payload = core::mem::ManuallyDrop<StreamLayout<$stream_type>>;
|
||||
|
||||
#[inline]
|
||||
fn tag() -> ArenaHeaderTag {
|
||||
ArenaHeaderTag::$stream_tag
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size(&self) -> usize {
|
||||
mem::size_of::<StreamLayout<$stream_type>>()
|
||||
}
|
||||
unsafe fn dealloc(ptr: std::ptr::NonNull<TypedAllocSlab<Self>>) {
|
||||
let mut slab = unsafe { Box::from_raw(ptr.as_ptr()) };
|
||||
|
||||
#[inline]
|
||||
fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated {
|
||||
unsafe {
|
||||
ptr::write(dst, self);
|
||||
TypedArenaPtr::new(dst as *mut Self)
|
||||
match slab.tag() {
|
||||
ArenaHeaderTag::$stream_tag => {
|
||||
unsafe { std::mem::ManuallyDrop::drop(slab.payload()) };
|
||||
}
|
||||
ArenaHeaderTag::Dropped => {}
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
drop(slab);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -494,36 +503,31 @@ arena_allocated_impl_for_stream!(StandardErrorStream, StandardErrorStream);
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum Stream {
|
||||
Byte(TypedArenaPtr<StreamLayout<CharReader<ByteStream>>>),
|
||||
InputFile(TypedArenaPtr<StreamLayout<CharReader<InputFileStream>>>),
|
||||
OutputFile(TypedArenaPtr<StreamLayout<OutputFileStream>>),
|
||||
StaticString(TypedArenaPtr<StreamLayout<StaticStringStream>>),
|
||||
NamedTcp(TypedArenaPtr<StreamLayout<CharReader<NamedTcpStream>>>),
|
||||
Byte(TypedArenaPtr<ByteStream>),
|
||||
InputFile(TypedArenaPtr<InputFileStream>),
|
||||
OutputFile(TypedArenaPtr<OutputFileStream>),
|
||||
StaticString(TypedArenaPtr<StaticStringStream>),
|
||||
NamedTcp(TypedArenaPtr<NamedTcpStream>),
|
||||
#[cfg(feature = "tls")]
|
||||
NamedTls(TypedArenaPtr<StreamLayout<CharReader<NamedTlsStream>>>),
|
||||
NamedTls(TypedArenaPtr<NamedTlsStream>),
|
||||
#[cfg(feature = "http")]
|
||||
HttpRead(TypedArenaPtr<StreamLayout<CharReader<HttpReadStream>>>),
|
||||
HttpRead(TypedArenaPtr<HttpReadStream>),
|
||||
#[cfg(feature = "http")]
|
||||
HttpWrite(TypedArenaPtr<StreamLayout<CharReader<HttpWriteStream>>>),
|
||||
HttpWrite(TypedArenaPtr<HttpWriteStream>),
|
||||
Null(StreamOptions),
|
||||
Readline(TypedArenaPtr<StreamLayout<ReadlineStream>>),
|
||||
StandardOutput(TypedArenaPtr<StreamLayout<StandardOutputStream>>),
|
||||
StandardError(TypedArenaPtr<StreamLayout<StandardErrorStream>>),
|
||||
Readline(TypedArenaPtr<ReadlineStream>),
|
||||
StandardOutput(TypedArenaPtr<StandardOutputStream>),
|
||||
StandardError(TypedArenaPtr<StandardErrorStream>),
|
||||
}
|
||||
|
||||
impl From<TypedArenaPtr<StreamLayout<ReadlineStream>>> for Stream {
|
||||
impl From<TypedArenaPtr<ReadlineStream>> for Stream {
|
||||
#[inline]
|
||||
fn from(stream: TypedArenaPtr<StreamLayout<ReadlineStream>>) -> Stream {
|
||||
fn from(stream: TypedArenaPtr<ReadlineStream>) -> Stream {
|
||||
Stream::Readline(stream)
|
||||
}
|
||||
}
|
||||
|
||||
impl Stream {
|
||||
#[inline]
|
||||
pub fn from_readline_stream(stream: ReadlineStream, arena: &mut Arena) -> Stream {
|
||||
Stream::Readline(arena_alloc!(StreamLayout::new(stream), arena))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_owned_string(string: String, arena: &mut Arena) -> Stream {
|
||||
Stream::Byte(arena_alloc!(
|
||||
@@ -552,29 +556,27 @@ impl Stream {
|
||||
))
|
||||
}
|
||||
|
||||
pub fn from_tag(tag: ArenaHeaderTag, ptr: *const u8) -> Self {
|
||||
pub fn from_tag(tag: ArenaHeaderTag, ptr: UntypedArenaPtr) -> Self {
|
||||
match tag {
|
||||
ArenaHeaderTag::ByteStream => Stream::Byte(TypedArenaPtr::new(ptr as *mut _)),
|
||||
ArenaHeaderTag::InputFileStream => Stream::InputFile(TypedArenaPtr::new(ptr as *mut _)),
|
||||
ArenaHeaderTag::OutputFileStream => {
|
||||
Stream::OutputFile(TypedArenaPtr::new(ptr as *mut _))
|
||||
}
|
||||
ArenaHeaderTag::NamedTcpStream => Stream::NamedTcp(TypedArenaPtr::new(ptr as *mut _)),
|
||||
ArenaHeaderTag::ByteStream => Stream::Byte(unsafe { ptr.as_typed_ptr() }),
|
||||
ArenaHeaderTag::InputFileStream => Stream::InputFile(unsafe { ptr.as_typed_ptr() }),
|
||||
ArenaHeaderTag::OutputFileStream => Stream::OutputFile(unsafe { ptr.as_typed_ptr() }),
|
||||
ArenaHeaderTag::NamedTcpStream => Stream::NamedTcp(unsafe { ptr.as_typed_ptr() }),
|
||||
#[cfg(feature = "tls")]
|
||||
ArenaHeaderTag::NamedTlsStream => Stream::NamedTls(TypedArenaPtr::new(ptr as *mut _)),
|
||||
ArenaHeaderTag::NamedTlsStream => Stream::NamedTls(unsafe { ptr.as_typed_ptr() }),
|
||||
#[cfg(feature = "http")]
|
||||
ArenaHeaderTag::HttpReadStream => Stream::HttpRead(TypedArenaPtr::new(ptr as *mut _)),
|
||||
ArenaHeaderTag::HttpReadStream => Stream::HttpRead(unsafe { ptr.as_typed_ptr() }),
|
||||
#[cfg(feature = "http")]
|
||||
ArenaHeaderTag::HttpWriteStream => Stream::HttpWrite(TypedArenaPtr::new(ptr as *mut _)),
|
||||
ArenaHeaderTag::ReadlineStream => Stream::Readline(TypedArenaPtr::new(ptr as *mut _)),
|
||||
ArenaHeaderTag::HttpWriteStream => Stream::HttpWrite(unsafe { ptr.as_typed_ptr() }),
|
||||
ArenaHeaderTag::ReadlineStream => Stream::Readline(unsafe { ptr.as_typed_ptr() }),
|
||||
ArenaHeaderTag::StaticStringStream => {
|
||||
Stream::StaticString(TypedArenaPtr::new(ptr as *mut _))
|
||||
Stream::StaticString(unsafe { ptr.as_typed_ptr() })
|
||||
}
|
||||
ArenaHeaderTag::StandardOutputStream => {
|
||||
Stream::StandardOutput(TypedArenaPtr::new(ptr as *mut _))
|
||||
Stream::StandardOutput(unsafe { ptr.as_typed_ptr() })
|
||||
}
|
||||
ArenaHeaderTag::StandardErrorStream => {
|
||||
Stream::StandardError(TypedArenaPtr::new(ptr as *mut _))
|
||||
Stream::StandardError(unsafe { ptr.as_typed_ptr() })
|
||||
}
|
||||
ArenaHeaderTag::Dropped | ArenaHeaderTag::NullStream => {
|
||||
Stream::Null(StreamOptions::default())
|
||||
@@ -585,29 +587,17 @@ impl Stream {
|
||||
|
||||
#[inline]
|
||||
pub fn is_stderr(&self) -> bool {
|
||||
if let Stream::StandardError(_) = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
matches!(self, Stream::StandardError(_))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_stdout(&self) -> bool {
|
||||
if let Stream::StandardOutput(_) = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
matches!(self, Stream::StandardOutput(_))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_stdin(&self) -> bool {
|
||||
if let Stream::Readline(_) = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
matches!(self, Stream::Readline(_))
|
||||
}
|
||||
|
||||
pub fn as_ptr(&self) -> *const ArenaHeader {
|
||||
@@ -831,7 +821,7 @@ impl CharRead for Stream {
|
||||
impl Read for Stream {
|
||||
#[inline]
|
||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
let bytes_read = match self {
|
||||
match self {
|
||||
Stream::InputFile(file) => (*file).read(buf),
|
||||
Stream::NamedTcp(tcp_stream) => (*tcp_stream).read(buf),
|
||||
#[cfg(feature = "tls")]
|
||||
@@ -853,9 +843,7 @@ impl Read for Stream {
|
||||
ErrorKind::PermissionDenied,
|
||||
StreamError::ReadFromOutputStream,
|
||||
)),
|
||||
};
|
||||
|
||||
bytes_read
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -984,18 +972,14 @@ fn cursor_position<T>(
|
||||
cursor: &Cursor<T>,
|
||||
cursor_len: u64,
|
||||
) -> AtEndOfStream {
|
||||
let position = cursor.position();
|
||||
|
||||
let at_end_of_stream = match position.cmp(&cursor_len) {
|
||||
match cursor.position().cmp(&cursor_len) {
|
||||
Ordering::Equal => AtEndOfStream::At,
|
||||
Ordering::Greater => {
|
||||
*past_end_of_stream = true;
|
||||
AtEndOfStream::Past
|
||||
}
|
||||
Ordering::Less => AtEndOfStream::Not,
|
||||
};
|
||||
|
||||
at_end_of_stream
|
||||
}
|
||||
}
|
||||
|
||||
impl Stream {
|
||||
@@ -1021,26 +1005,23 @@ impl Stream {
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn set_position(&mut self, position: u64) {
|
||||
match self {
|
||||
Stream::InputFile(stream_layout) => {
|
||||
let StreamLayout {
|
||||
past_end_of_stream,
|
||||
stream,
|
||||
..
|
||||
} = &mut **stream_layout;
|
||||
if let Stream::InputFile(stream_layout) = self {
|
||||
let StreamLayout {
|
||||
past_end_of_stream,
|
||||
stream,
|
||||
..
|
||||
} = &mut ***stream_layout;
|
||||
|
||||
stream
|
||||
.get_mut()
|
||||
.file
|
||||
.seek(SeekFrom::Start(position))
|
||||
.unwrap();
|
||||
stream.reset_buffer(); // flush the internal buffer.
|
||||
stream
|
||||
.get_mut()
|
||||
.file
|
||||
.seek(SeekFrom::Start(position))
|
||||
.unwrap();
|
||||
stream.reset_buffer(); // flush the internal buffer.
|
||||
|
||||
if let Ok(metadata) = stream.get_ref().file.metadata() {
|
||||
*past_end_of_stream = position > metadata.len();
|
||||
}
|
||||
if let Ok(metadata) = stream.get_ref().file.metadata() {
|
||||
*past_end_of_stream = position > metadata.len();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1103,7 +1084,7 @@ impl Stream {
|
||||
past_end_of_stream,
|
||||
stream,
|
||||
..
|
||||
} = &mut **stream_layout;
|
||||
} = &mut ***stream_layout;
|
||||
|
||||
let cursor_len = stream.get_ref().0.get_ref().len() as u64;
|
||||
cursor_position(past_end_of_stream, &stream.get_ref().0, cursor_len)
|
||||
@@ -1113,7 +1094,7 @@ impl Stream {
|
||||
past_end_of_stream,
|
||||
stream,
|
||||
..
|
||||
} = &mut **stream_layout;
|
||||
} = &mut ***stream_layout;
|
||||
|
||||
let cursor_len = stream.stream.get_ref().len() as u64;
|
||||
cursor_position(past_end_of_stream, &stream.stream, cursor_len)
|
||||
@@ -1125,7 +1106,7 @@ impl Stream {
|
||||
past_end_of_stream,
|
||||
stream,
|
||||
..
|
||||
} = &mut **stream_layout;
|
||||
} = &mut ***stream_layout;
|
||||
|
||||
match stream.get_ref().file.metadata() {
|
||||
Ok(metadata) => {
|
||||
@@ -1149,6 +1130,20 @@ impl Stream {
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "http")]
|
||||
Stream::HttpRead(stream_layout) => {
|
||||
if stream_layout
|
||||
.stream
|
||||
.get_ref()
|
||||
.body_reader
|
||||
.get_ref()
|
||||
.has_remaining()
|
||||
{
|
||||
AtEndOfStream::Not
|
||||
} else {
|
||||
AtEndOfStream::Past
|
||||
}
|
||||
}
|
||||
_ => AtEndOfStream::Not,
|
||||
}
|
||||
}
|
||||
@@ -1237,7 +1232,7 @@ impl Stream {
|
||||
#[inline]
|
||||
pub(crate) fn from_http_stream(
|
||||
url: Atom,
|
||||
http_stream: Box<dyn BufRead>,
|
||||
http_stream: BufReader<Bytes>,
|
||||
arena: &mut Arena,
|
||||
) -> Self {
|
||||
Stream::HttpRead(arena_alloc!(
|
||||
@@ -1257,15 +1252,15 @@ impl Stream {
|
||||
headers: hyper::HeaderMap,
|
||||
arena: &mut Arena,
|
||||
) -> Self {
|
||||
Stream::HttpWrite(arena_alloc!(
|
||||
StreamLayout::new(CharReader::new(HttpWriteStream {
|
||||
response,
|
||||
status_code,
|
||||
headers: mem::ManuallyDrop::new(headers),
|
||||
buffer: mem::ManuallyDrop::new(Vec::new()),
|
||||
})),
|
||||
arena
|
||||
))
|
||||
Stream::HttpWrite(arena_alloc!(
|
||||
StreamLayout::new(CharReader::new(HttpWriteStream {
|
||||
response,
|
||||
status_code,
|
||||
headers: std::mem::ManuallyDrop::new(headers),
|
||||
buffer: std::mem::ManuallyDrop::new(Vec::new()),
|
||||
})),
|
||||
arena
|
||||
))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -1305,38 +1300,25 @@ impl Stream {
|
||||
Stream::NamedTls(ref mut tls_stream) => tls_stream.inner_mut().tls_stream.shutdown(),
|
||||
#[cfg(feature = "http")]
|
||||
Stream::HttpRead(ref mut http_stream) => {
|
||||
unsafe {
|
||||
http_stream.set_tag(ArenaHeaderTag::Dropped);
|
||||
std::ptr::drop_in_place(&mut http_stream.inner_mut().body_reader as *mut _);
|
||||
}
|
||||
http_stream.drop_payload();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
#[cfg(feature = "http")]
|
||||
Stream::HttpWrite(ref mut http_stream) => {
|
||||
http_stream.inner_mut().drop();
|
||||
unsafe {
|
||||
http_stream.set_tag(ArenaHeaderTag::Dropped);
|
||||
std::ptr::drop_in_place(&mut http_stream.inner_mut().buffer as *mut _);
|
||||
}
|
||||
Stream::HttpWrite(mut http_stream) => {
|
||||
http_stream.drop_payload();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Stream::InputFile(mut file_stream) => {
|
||||
// close the stream by dropping the inner File.
|
||||
unsafe {
|
||||
file_stream.set_tag(ArenaHeaderTag::Dropped);
|
||||
std::ptr::drop_in_place(&mut file_stream.inner_mut().file as *mut _);
|
||||
}
|
||||
file_stream.drop_payload();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Stream::OutputFile(mut file_stream) => {
|
||||
// close the stream by dropping the inner File.
|
||||
unsafe {
|
||||
file_stream.set_tag(ArenaHeaderTag::Dropped);
|
||||
std::ptr::drop_in_place(&mut file_stream.file as *mut _);
|
||||
}
|
||||
file_stream.drop_payload();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1346,11 +1328,7 @@ impl Stream {
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn is_null_stream(&self) -> bool {
|
||||
if let Stream::Null(_) = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
matches!(self, Stream::Null(_))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -1391,29 +1369,25 @@ impl Stream {
|
||||
self.set_lines_read(0);
|
||||
self.set_past_end_of_stream(false);
|
||||
|
||||
loop {
|
||||
match self {
|
||||
Stream::Byte(ref mut cursor) => {
|
||||
cursor.stream.get_mut().0.set_position(0);
|
||||
return true;
|
||||
}
|
||||
Stream::InputFile(ref mut file_stream) => {
|
||||
file_stream
|
||||
.stream
|
||||
.get_mut()
|
||||
.file
|
||||
.seek(SeekFrom::Start(0))
|
||||
.unwrap();
|
||||
return true;
|
||||
}
|
||||
Stream::Readline(ref mut readline_stream) => {
|
||||
readline_stream.reset();
|
||||
return true;
|
||||
}
|
||||
_ => {
|
||||
return false;
|
||||
}
|
||||
match self {
|
||||
Stream::Byte(ref mut cursor) => {
|
||||
cursor.stream.get_mut().0.set_position(0);
|
||||
true
|
||||
}
|
||||
Stream::InputFile(ref mut file_stream) => {
|
||||
file_stream
|
||||
.stream
|
||||
.get_mut()
|
||||
.file
|
||||
.seek(SeekFrom::Start(0))
|
||||
.unwrap();
|
||||
true
|
||||
}
|
||||
Stream::Readline(ref mut readline_stream) => {
|
||||
readline_stream.reset();
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1484,12 +1458,13 @@ impl MachineState {
|
||||
stream.set_past_end_of_stream(true);
|
||||
}
|
||||
|
||||
Ok(self.fail = stream.past_end_of_stream())
|
||||
self.fail = stream.past_end_of_stream();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn to_stream_options(
|
||||
pub(crate) fn get_stream_options(
|
||||
&mut self,
|
||||
alias: HeapCellValue,
|
||||
eof_action: HeapCellValue,
|
||||
@@ -1782,9 +1757,9 @@ impl MachineState {
|
||||
caller: Atom,
|
||||
arity: usize,
|
||||
) -> CallResult {
|
||||
let opt_err = if input.is_some() && !stream.is_input_stream() {
|
||||
Some(atom!("stream")) // 8.14.2.3 g)
|
||||
} else if input.is_none() && !stream.is_output_stream() {
|
||||
let opt_err = if input.is_some() && !stream.is_input_stream()
|
||||
|| input.is_none() && !stream.is_output_stream()
|
||||
{
|
||||
Some(atom!("stream")) // 8.14.2.3 g)
|
||||
} else if stream.options().stream_type() != expected_type {
|
||||
Some(expected_type.other().as_atom()) // 8.14.2.3 h)
|
||||
@@ -1866,42 +1841,55 @@ impl MachineState {
|
||||
}
|
||||
};
|
||||
|
||||
let file = match open_options.open(&*file_spec.as_str()) {
|
||||
Ok(file) => file,
|
||||
Err(err) => {
|
||||
match err.kind() {
|
||||
ErrorKind::NotFound => {
|
||||
// 8.11.5.3j)
|
||||
let stub = functor_stub(atom!("open"), 4);
|
||||
let mut path = PathBuf::from(&*file_spec.as_str());
|
||||
|
||||
let err =
|
||||
self.existence_error(ExistenceError::SourceSink(self[temp_v!(1)]));
|
||||
loop {
|
||||
let file = match open_options.open(&path) {
|
||||
Ok(file) => file,
|
||||
Err(err) => {
|
||||
match err.kind() {
|
||||
ErrorKind::NotFound => {
|
||||
// 8.11.5.3j)
|
||||
let stub = functor_stub(atom!("open"), 4);
|
||||
|
||||
return Err(self.error_form(err, stub));
|
||||
let err =
|
||||
self.existence_error(ExistenceError::SourceSink(self[temp_v!(1)]));
|
||||
|
||||
return Err(self.error_form(err, stub));
|
||||
}
|
||||
ErrorKind::PermissionDenied => {
|
||||
// 8.11.5.3k)
|
||||
return Err(self.open_permission_error(
|
||||
self.registers[1],
|
||||
atom!("open"),
|
||||
4,
|
||||
));
|
||||
}
|
||||
_ => {
|
||||
// assume the OS is out of file descriptors.
|
||||
let stub = functor_stub(atom!("open"), 4);
|
||||
let err = self.resource_error(ResourceError::OutOfFiles);
|
||||
|
||||
return Err(self.error_form(err, stub));
|
||||
}
|
||||
}
|
||||
ErrorKind::PermissionDenied => {
|
||||
// 8.11.5.3k)
|
||||
return Err(self.open_permission_error(
|
||||
self.registers[1],
|
||||
atom!("open"),
|
||||
4,
|
||||
));
|
||||
}
|
||||
_ => {
|
||||
// assume the OS is out of file descriptors.
|
||||
let stub = functor_stub(atom!("open"), 4);
|
||||
let err = self.resource_error(ResourceError::OutOfFiles);
|
||||
}
|
||||
};
|
||||
|
||||
return Err(self.error_form(err, stub));
|
||||
if path.extension().is_none() {
|
||||
if let Ok(metadata) = file.metadata() {
|
||||
if metadata.is_dir() {
|
||||
path.set_extension("pl");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(if is_input_file {
|
||||
Stream::from_file_as_input(file_spec, file, &mut self.arena)
|
||||
} else {
|
||||
Stream::from_file_as_output(file_spec, file, in_append_mode, &mut self.arena)
|
||||
})
|
||||
return Ok(if is_input_file {
|
||||
Stream::from_file_as_input(file_spec, file, &mut self.arena)
|
||||
} else {
|
||||
Stream::from_file_as_output(file_spec, file, in_append_mode, &mut self.arena)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,8 +7,6 @@ use crate::parser::ast::*;
|
||||
use crate::parser::parser::*;
|
||||
use crate::read::devour_whitespace;
|
||||
|
||||
use crate::predicate_queue;
|
||||
|
||||
use fxhash::FxBuildHasher;
|
||||
use indexmap::IndexSet;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::arena::*;
|
||||
use crate::forms::*;
|
||||
use crate::heap_iter::{NonListElider, stackful_preorder_iter};
|
||||
use crate::heap_iter::{stackful_preorder_iter, NonListElider};
|
||||
use crate::machine::machine_state::*;
|
||||
use crate::machine::partial_string::*;
|
||||
use crate::machine::*;
|
||||
@@ -9,7 +9,7 @@ use crate::types::*;
|
||||
use std::cmp::Ordering;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use derive_deref::*;
|
||||
use derive_more::*;
|
||||
use fxhash::FxBuildHasher;
|
||||
use indexmap::IndexSet;
|
||||
use num_order::NumOrd;
|
||||
@@ -173,6 +173,98 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
|
||||
let mut pstr_iter1 = HeapPStrIter::new(&machine_st.heap, s1);
|
||||
let mut pstr_iter2 = HeapPStrIter::new(&machine_st.heap, s1 + 1);
|
||||
|
||||
fn unify_sequence(
|
||||
machine_st: &mut MachineState,
|
||||
iter: PStrIteratee,
|
||||
source_cell: HeapCellValue,
|
||||
) -> bool {
|
||||
match iter {
|
||||
PStrIteratee::Char(focus, _) => {
|
||||
machine_st.pdl.push(machine_st.heap[focus]);
|
||||
machine_st.pdl.push(source_cell);
|
||||
}
|
||||
PStrIteratee::PStrSegment(focus, _, n) => {
|
||||
read_heap_cell!(machine_st.heap[focus],
|
||||
(HeapCellValueTag::CStr | HeapCellValueTag::PStr, pstr_atom) => {
|
||||
if focus < machine_st.heap.len() - 2 {
|
||||
machine_st.heap.pop();
|
||||
machine_st.heap.pop();
|
||||
}
|
||||
|
||||
if n == 0 {
|
||||
let target_cell = match machine_st.heap[focus].get_tag() {
|
||||
HeapCellValueTag::CStr => {
|
||||
atom_as_cstr_cell!(pstr_atom)
|
||||
}
|
||||
HeapCellValueTag::PStr => {
|
||||
pstr_loc_as_cell!(focus)
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
};
|
||||
|
||||
machine_st.pdl.push(target_cell);
|
||||
machine_st.pdl.push(source_cell);
|
||||
} else {
|
||||
let h_len = machine_st.heap.len();
|
||||
|
||||
machine_st.heap.push(pstr_offset_as_cell!(focus));
|
||||
machine_st.heap.push(fixnum_as_cell!(
|
||||
Fixnum::build_with(n as i64)
|
||||
));
|
||||
|
||||
machine_st.pdl.push(pstr_loc_as_cell!(h_len));
|
||||
machine_st.pdl.push(source_cell);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
(HeapCellValueTag::PStrOffset, pstr_loc) => {
|
||||
let n0 = cell_as_fixnum!(machine_st.heap[focus+1])
|
||||
.get_num() as usize;
|
||||
|
||||
if pstr_loc < machine_st.heap.len() - 2 {
|
||||
machine_st.heap.pop();
|
||||
machine_st.heap.pop();
|
||||
}
|
||||
|
||||
if n == n0 {
|
||||
machine_st.pdl.push(pstr_loc_as_cell!(focus));
|
||||
machine_st.pdl.push(source_cell);
|
||||
} else {
|
||||
let h_len = machine_st.heap.len();
|
||||
|
||||
machine_st.heap.push(pstr_offset_as_cell!(pstr_loc));
|
||||
machine_st.heap.push(fixnum_as_cell!(
|
||||
Fixnum::build_with(n as i64)
|
||||
));
|
||||
|
||||
machine_st.pdl.push(pstr_loc_as_cell!(h_len));
|
||||
machine_st.pdl.push(source_cell);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
_ => {
|
||||
}
|
||||
);
|
||||
|
||||
if focus < machine_st.heap.len() - 2 {
|
||||
machine_st.heap.pop();
|
||||
machine_st.heap.pop();
|
||||
}
|
||||
|
||||
machine_st.pdl.push(machine_st.heap[focus]);
|
||||
machine_st.pdl.push(source_cell);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
match compare_pstr_prefixes(&mut pstr_iter1, &mut pstr_iter2) {
|
||||
PStrCmpResult::Ordered(Ordering::Equal) => {}
|
||||
PStrCmpResult::Ordered(Ordering::Less) => {
|
||||
@@ -204,7 +296,7 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
|
||||
|
||||
let mut focus = pstr_iter2.focus;
|
||||
|
||||
'outer: loop {
|
||||
'outer: {
|
||||
while let Some(c) = chars_iter.peek() {
|
||||
read_heap_cell!(focus,
|
||||
(HeapCellValueTag::Lis, l) => {
|
||||
@@ -229,89 +321,13 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
|
||||
break 'outer;
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::CStr | HeapCellValueTag::PStrLoc) => {
|
||||
unify_sequence(machine_st, chars_iter.item.unwrap(), focus);
|
||||
return;
|
||||
}
|
||||
(HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => {
|
||||
match chars_iter.item.unwrap() {
|
||||
PStrIteratee::Char(focus, _) => {
|
||||
machine_st.pdl.push(machine_st.heap[focus]);
|
||||
machine_st.pdl.push(heap_loc_as_cell!(h));
|
||||
}
|
||||
PStrIteratee::PStrSegment(focus, _, n) => {
|
||||
read_heap_cell!(machine_st.heap[focus],
|
||||
(HeapCellValueTag::CStr | HeapCellValueTag::PStr, pstr_atom) => {
|
||||
if focus < machine_st.heap.len() - 2 {
|
||||
machine_st.heap.pop();
|
||||
machine_st.heap.pop();
|
||||
}
|
||||
|
||||
if n == 0 {
|
||||
let target_cell = match machine_st.heap[focus].get_tag() {
|
||||
HeapCellValueTag::CStr => {
|
||||
atom_as_cstr_cell!(pstr_atom)
|
||||
}
|
||||
HeapCellValueTag::PStr => {
|
||||
pstr_loc_as_cell!(focus)
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
};
|
||||
|
||||
machine_st.pdl.push(target_cell);
|
||||
machine_st.pdl.push(heap_loc_as_cell!(h));
|
||||
} else {
|
||||
let h_len = machine_st.heap.len();
|
||||
|
||||
machine_st.heap.push(pstr_offset_as_cell!(focus));
|
||||
machine_st.heap.push(fixnum_as_cell!(
|
||||
Fixnum::build_with(n as i64)
|
||||
));
|
||||
|
||||
machine_st.pdl.push(pstr_loc_as_cell!(h_len));
|
||||
machine_st.pdl.push(heap_loc_as_cell!(h));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
(HeapCellValueTag::PStrOffset, pstr_loc) => {
|
||||
let n0 = cell_as_fixnum!(machine_st.heap[focus+1])
|
||||
.get_num() as usize;
|
||||
|
||||
if pstr_loc < machine_st.heap.len() - 2 {
|
||||
machine_st.heap.pop();
|
||||
machine_st.heap.pop();
|
||||
}
|
||||
|
||||
if n == n0 {
|
||||
machine_st.pdl.push(pstr_loc_as_cell!(focus));
|
||||
machine_st.pdl.push(heap_loc_as_cell!(h));
|
||||
} else {
|
||||
let h_len = machine_st.heap.len();
|
||||
|
||||
machine_st.heap.push(pstr_offset_as_cell!(pstr_loc));
|
||||
machine_st.heap.push(fixnum_as_cell!(
|
||||
Fixnum::build_with(n as i64)
|
||||
));
|
||||
|
||||
machine_st.pdl.push(pstr_loc_as_cell!(h_len));
|
||||
machine_st.pdl.push(heap_loc_as_cell!(h));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
_ => {
|
||||
}
|
||||
);
|
||||
|
||||
if focus < machine_st.heap.len() - 2 {
|
||||
machine_st.heap.pop();
|
||||
machine_st.heap.pop();
|
||||
}
|
||||
|
||||
machine_st.pdl.push(machine_st.heap[focus]);
|
||||
machine_st.pdl.push(heap_loc_as_cell!(h));
|
||||
|
||||
return;
|
||||
}
|
||||
if unify_sequence(machine_st, chars_iter.item.unwrap(), heap_loc_as_cell!(h)) {
|
||||
return;
|
||||
}
|
||||
|
||||
break 'outer;
|
||||
@@ -329,8 +345,6 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
|
||||
|
||||
machine_st.pdl.push(focus);
|
||||
machine_st.pdl.push(chars_iter.iter.focus);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
PStrCmpResult::Unordered => {
|
||||
@@ -439,30 +453,6 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
|
||||
}
|
||||
}
|
||||
|
||||
fn unify_big_num<N>(&mut self, n1: TypedArenaPtr<N>, value: HeapCellValue)
|
||||
where
|
||||
N: PartialEq<Rational> + PartialEq<Integer> + PartialEq<i64> + ArenaAllocated,
|
||||
{
|
||||
if let Some(r) = value.as_var() {
|
||||
Self::bind(self, r, typed_arena_ptr_as_cell!(n1));
|
||||
return;
|
||||
}
|
||||
|
||||
match Number::try_from(value) {
|
||||
Ok(n2) => match n2 {
|
||||
Number::Fixnum(n2) if *n1 == n2.get_num() => {}
|
||||
Number::Integer(n2) if *n1 == *n2 => {}
|
||||
Number::Rational(n2) if *n1 == *n2 => {}
|
||||
_ => {
|
||||
self.fail = true;
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
self.fail = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn unify_big_integer(&mut self, n1: TypedArenaPtr<Integer>, value: HeapCellValue) {
|
||||
if let Some(r) = value.as_var() {
|
||||
Self::bind(self, r, typed_arena_ptr_as_cell!(n1));
|
||||
@@ -609,10 +599,8 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::Lis, l1) => {
|
||||
if d2.is_ref() {
|
||||
if tabu_list.contains(&(d1, d2)) {
|
||||
continue;
|
||||
}
|
||||
if d2.is_ref() && tabu_list.contains(&(d1, d2)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Self::unify_list(self, l1, d2);
|
||||
@@ -720,7 +708,11 @@ fn bind_with_occurs_check<U: Unifier>(unifier: &mut U, r: Ref, value: HeapCellVa
|
||||
if !value.is_constant() {
|
||||
let machine_st: &mut MachineState = unifier.deref_mut();
|
||||
|
||||
for cell in stackful_preorder_iter::<NonListElider>(&mut machine_st.heap, &mut machine_st.stack, value) {
|
||||
for cell in stackful_preorder_iter::<NonListElider>(
|
||||
&mut machine_st.heap,
|
||||
&mut machine_st.stack,
|
||||
value,
|
||||
) {
|
||||
let cell = unmark_cell_bits!(cell);
|
||||
|
||||
if let Some(inner_r) = cell.as_var() {
|
||||
@@ -738,10 +730,11 @@ fn bind_with_occurs_check<U: Unifier>(unifier: &mut U, r: Ref, value: HeapCellVa
|
||||
U::bind(unifier, r, value);
|
||||
}
|
||||
|
||||
return occurs_triggered;
|
||||
occurs_triggered
|
||||
}
|
||||
|
||||
#[derive(Deref, DerefMut)]
|
||||
#[deref(forward)]
|
||||
pub(crate) struct DefaultUnifier<'a> {
|
||||
machine_st: &'a mut MachineState,
|
||||
}
|
||||
|
||||
@@ -144,7 +144,6 @@ macro_rules! stack_loc_as_cell {
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! heap_loc_as_cell {
|
||||
($h:expr) => {
|
||||
HeapCellValue::build_with(HeapCellValueTag::Var, $h as u64)
|
||||
@@ -171,15 +170,18 @@ macro_rules! typed_arena_ptr_as_cell {
|
||||
}
|
||||
|
||||
macro_rules! raw_ptr_as_cell {
|
||||
($ptr:expr) => {
|
||||
($ptr:expr) => {{
|
||||
// Cell is 64-bit, but raw ptr is 32-bit in 32-bit systems
|
||||
HeapCellValue::from_raw_ptr_bytes(unsafe { std::mem::transmute($ptr) })
|
||||
};
|
||||
// TODO use <*{const,mut} _>::addr instead of as when the strict_provenance feature is stable rust-lang/rust#95228
|
||||
// we might need <*{const,mut} _>::expose_provenance for strict provenance, dependening on how we recreate a pointer later
|
||||
let ptr : *const _ = $ptr;
|
||||
HeapCellValue::from_ptr_addr(ptr as usize)
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! untyped_arena_ptr_as_cell {
|
||||
($ptr:expr) => {
|
||||
HeapCellValue::from_bytes(unsafe { std::mem::transmute($ptr) })
|
||||
HeapCellValue::from_bytes(UntypedArenaPtr::into_bytes($ptr))
|
||||
};
|
||||
}
|
||||
|
||||
@@ -224,86 +226,69 @@ macro_rules! stream_as_cell {
|
||||
macro_rules! cell_as_stream {
|
||||
($cell:expr) => {{
|
||||
let ptr = cell_as_untyped_arena_ptr!($cell);
|
||||
Stream::from_tag(ptr.get_tag(), ptr.payload_offset())
|
||||
Stream::from_tag(ptr.get_tag(), ptr)
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! cell_as_load_state_payload {
|
||||
($cell:expr) => {
|
||||
unsafe {
|
||||
let ptr = cell_as_untyped_arena_ptr!($cell);
|
||||
let ptr = std::mem::transmute::<_, *mut LiveLoadState>(ptr.payload_offset());
|
||||
|
||||
TypedArenaPtr::new(ptr)
|
||||
}
|
||||
};
|
||||
($cell:expr) => {{
|
||||
let ptr = cell_as_untyped_arena_ptr!($cell);
|
||||
unsafe { ptr.as_typed_ptr::<LiveLoadState>() }
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! match_untyped_arena_ptr_pat_body {
|
||||
($ptr:ident, Integer, $n:ident, $code:expr) => {{
|
||||
let payload_ptr = unsafe { std::mem::transmute::<_, *mut Integer>($ptr.payload_offset()) };
|
||||
let $n = TypedArenaPtr::new(payload_ptr);
|
||||
let $n = unsafe { $ptr.as_typed_ptr::<Integer>() };
|
||||
#[allow(unused_braces)]
|
||||
$code
|
||||
}};
|
||||
($ptr:ident, Rational, $n:ident, $code:expr) => {{
|
||||
let payload_ptr = unsafe { std::mem::transmute::<_, *mut Rational>($ptr.payload_offset()) };
|
||||
let $n = TypedArenaPtr::new(payload_ptr);
|
||||
let $n = unsafe { $ptr.as_typed_ptr::<Rational>() };
|
||||
#[allow(unused_braces)]
|
||||
$code
|
||||
}};
|
||||
($ptr:ident, OssifiedOpDir, $n:ident, $code:expr) => {{
|
||||
let payload_ptr =
|
||||
unsafe { std::mem::transmute::<_, *mut OssifiedOpDir>($ptr.payload_offset()) };
|
||||
let $n = TypedArenaPtr::new(payload_ptr);
|
||||
let $n = unsafe { $ptr.as_typed_ptr::<OssifiedOpDir>() };
|
||||
#[allow(unused_braces)]
|
||||
$code
|
||||
}};
|
||||
($ptr:ident, LiveLoadState, $n:ident, $code:expr) => {{
|
||||
let payload_ptr =
|
||||
unsafe { std::mem::transmute::<_, *mut LiveLoadState>($ptr.payload_offset()) };
|
||||
let $n = TypedArenaPtr::new(payload_ptr);
|
||||
let $n = unsafe { $ptr.as_typed_ptr::<LiveLoadState>() };
|
||||
#[allow(unused_braces)]
|
||||
$code
|
||||
}};
|
||||
($ptr:ident, Stream, $s:ident, $code:expr) => {{
|
||||
let $s = Stream::from_tag($ptr.get_tag(), $ptr.payload_offset());
|
||||
let $s = Stream::from_tag($ptr.get_tag(), $ptr);
|
||||
#[allow(unused_braces)]
|
||||
$code
|
||||
}};
|
||||
($ptr:ident, TcpListener, $listener:ident, $code:expr) => {{
|
||||
let payload_ptr =
|
||||
unsafe { std::mem::transmute::<_, *mut TcpListener>($ptr.payload_offset()) };
|
||||
#[allow(unused_mut)]
|
||||
let mut $listener = TypedArenaPtr::new(payload_ptr);
|
||||
let mut $listener = unsafe { $ptr.as_typed_ptr::<TcpListener>() };
|
||||
#[allow(unused_braces)]
|
||||
$code
|
||||
}};
|
||||
($ptr:ident, HttpListener, $listener:ident, $code:expr) => {{
|
||||
let payload_ptr =
|
||||
unsafe { std::mem::transmute::<_, *mut HttpListener>($ptr.payload_offset()) };
|
||||
#[allow(unused_mut)]
|
||||
let mut $listener = TypedArenaPtr::new(payload_ptr);
|
||||
let mut $listener = unsafe { $ptr.as_typed_ptr::<HttpListener>() };
|
||||
#[allow(unused_braces)]
|
||||
$code
|
||||
}};
|
||||
($ptr:ident, HttpResponse, $listener:ident, $code:expr) => {{
|
||||
let payload_ptr =
|
||||
unsafe { std::mem::transmute::<_, *mut HttpResponse>($ptr.payload_offset()) };
|
||||
#[allow(unused_mut)]
|
||||
let mut $listener = TypedArenaPtr::new(payload_ptr);
|
||||
let mut $listener = unsafe { $ptr.as_typed_ptr::<HttpResponse>() };
|
||||
#[allow(unused_braces)]
|
||||
$code
|
||||
}};
|
||||
($ptr:ident, IndexPtr, $ip:ident, $code:expr) => {{
|
||||
#[allow(unused_mut)]
|
||||
let mut $ip =
|
||||
TypedArenaPtr::new(unsafe { std::mem::transmute::<_, *mut IndexPtr>($ptr.get_ptr()) });
|
||||
let mut $ip = unsafe { $ptr.as_typed_ptr::<IndexPtr>() };
|
||||
#[allow(unused_braces)]
|
||||
$code
|
||||
}};
|
||||
($ptr:ident, $($tags:tt)|+, $s:ident, $code:expr) => {{
|
||||
let $s = Stream::from_tag($ptr.get_tag(), $ptr.payload_offset());
|
||||
let $s = Stream::from_tag($ptr.get_tag(), $ptr);
|
||||
#[allow(unused_braces)]
|
||||
$code
|
||||
}};
|
||||
@@ -338,6 +323,7 @@ macro_rules! match_untyped_arena_ptr {
|
||||
($ptr:expr, $( ($(ArenaHeaderTag::$tag:tt)|+, $n:ident) => $code:block $(,)?)+ $(_ => $misc_code:expr $(,)?)?) => ({
|
||||
let ptr_id = $ptr;
|
||||
|
||||
#[allow(clippy::toplevel_ref_arg)]
|
||||
match ptr_id.get_tag() {
|
||||
$($(match_untyped_arena_ptr_pat!($tag) => {
|
||||
match_untyped_arena_ptr_pat_body!(ptr_id, $tag, $n, $code)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work
|
||||
|
||||
use crate::arena::*;
|
||||
use crate::atom_table::*;
|
||||
use crate::machine::machine_indices::*;
|
||||
@@ -17,20 +19,120 @@ use crate::parser::dashu::{Integer, Rational};
|
||||
|
||||
use fxhash::FxBuildHasher;
|
||||
use indexmap::IndexMap;
|
||||
use modular_bitfield::error::OutOfBounds;
|
||||
use modular_bitfield::prelude::*;
|
||||
use scryer_modular_bitfield::error::OutOfBounds;
|
||||
use scryer_modular_bitfield::prelude::*;
|
||||
|
||||
pub type Specifier = u32;
|
||||
|
||||
pub const MAX_ARITY: usize = 1023;
|
||||
|
||||
pub const XFX: u32 = 0x0001;
|
||||
pub const XFY: u32 = 0x0002;
|
||||
pub const YFX: u32 = 0x0004;
|
||||
pub const XF: u32 = 0x0010;
|
||||
pub const YF: u32 = 0x0020;
|
||||
pub const FX: u32 = 0x0040;
|
||||
pub const FY: u32 = 0x0080;
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub enum OpDeclSpec {
|
||||
XFX = 0x0001,
|
||||
XFY = 0x0002,
|
||||
YFX = 0x0004,
|
||||
XF = 0x0010,
|
||||
YF = 0x0020,
|
||||
FX = 0x0040,
|
||||
FY = 0x0080,
|
||||
}
|
||||
|
||||
pub use OpDeclSpec::*;
|
||||
|
||||
impl OpDeclSpec {
|
||||
pub const fn value(self) -> u32 {
|
||||
self as u32
|
||||
}
|
||||
|
||||
pub fn get_spec(self) -> Atom {
|
||||
match self {
|
||||
XFX => atom!("xfx"),
|
||||
XFY => atom!("xfy"),
|
||||
YFX => atom!("yfx"),
|
||||
FX => atom!("fx"),
|
||||
FY => atom!("fy"),
|
||||
XF => atom!("xf"),
|
||||
YF => atom!("yf"),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn is_prefix(self) -> bool {
|
||||
matches!(self, Self::FX | Self::FY)
|
||||
}
|
||||
|
||||
pub const fn is_postfix(self) -> bool {
|
||||
matches!(self, Self::XF | Self::YF)
|
||||
}
|
||||
|
||||
pub const fn is_infix(self) -> bool {
|
||||
matches!(self, Self::XFX | Self::XFY | Self::YFX)
|
||||
}
|
||||
|
||||
pub const fn is_strict_left(self) -> bool {
|
||||
matches!(self, Self::XFX | Self::XFY | Self::XF)
|
||||
}
|
||||
|
||||
pub const fn is_strict_right(self) -> bool {
|
||||
matches!(self, Self::XFX | Self::YFX | Self::FX)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn fixity(self) -> Fixity {
|
||||
match self {
|
||||
XFY | XFX | YFX => Fixity::In,
|
||||
XF | YF => Fixity::Post,
|
||||
FX | FY => Fixity::Pre,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<OpDeclSpec> for u8 {
|
||||
fn from(value: OpDeclSpec) -> Self {
|
||||
value as u8
|
||||
}
|
||||
}
|
||||
|
||||
impl From<OpDeclSpec> for u32 {
|
||||
fn from(value: OpDeclSpec) -> Self {
|
||||
value as u32
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for OpDeclSpec {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
Ok(match value {
|
||||
0x0001 => XFX,
|
||||
0x0002 => XFY,
|
||||
0x0004 => YFX,
|
||||
0x0010 => XF,
|
||||
0x0020 => YF,
|
||||
0x0040 => FX,
|
||||
0x0080 => FY,
|
||||
_ => return Err(()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Atom> for OpDeclSpec {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: Atom) -> Result<Self, Self::Error> {
|
||||
Ok(match value {
|
||||
atom!("xfx") => Self::XFX,
|
||||
atom!("xfy") => Self::XFY,
|
||||
atom!("yfx") => Self::YFX,
|
||||
atom!("fx") => Self::FX,
|
||||
atom!("fy") => Self::FY,
|
||||
atom!("xf") => Self::XF,
|
||||
atom!("yf") => Self::YF,
|
||||
_ => return Err(()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub const DELIMITER: u32 = 0x0100;
|
||||
pub const TERM: u32 = 0x1000;
|
||||
pub const LTERM: u32 = 0x3000;
|
||||
@@ -38,7 +140,6 @@ pub const BTERM: u32 = 0x11000;
|
||||
|
||||
pub const NEGATIVE_SIGN: u32 = 0x0200;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! fixnum {
|
||||
($wrapper:tt, $n:expr, $arena:expr) => {
|
||||
Fixnum::build_with_checked($n)
|
||||
@@ -49,26 +150,26 @@ macro_rules! fixnum {
|
||||
|
||||
macro_rules! is_term {
|
||||
($x:expr) => {
|
||||
($x as u32 & $crate::parser::ast::TERM) != 0
|
||||
($x as u32 & $crate::parser::ast::TERM) != 0 || is_negate!($x)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! is_lterm {
|
||||
($x:expr) => {
|
||||
($x as u32 & $crate::parser::ast::LTERM) != 0
|
||||
($x as u32 & $crate::parser::ast::LTERM) != 0 || is_negate!($x)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! is_op {
|
||||
($x:expr) => {
|
||||
$x as u32
|
||||
& ($crate::parser::ast::XF
|
||||
| $crate::parser::ast::YF
|
||||
| $crate::parser::ast::FX
|
||||
| $crate::parser::ast::FY
|
||||
| $crate::parser::ast::XFX
|
||||
| $crate::parser::ast::XFY
|
||||
| $crate::parser::ast::YFX)
|
||||
& ($crate::parser::ast::XF as u32
|
||||
| $crate::parser::ast::YF as u32
|
||||
| $crate::parser::ast::FX as u32
|
||||
| $crate::parser::ast::FY as u32
|
||||
| $crate::parser::ast::XFX as u32
|
||||
| $crate::parser::ast::XFY as u32
|
||||
| $crate::parser::ast::YFX as u32)
|
||||
!= 0
|
||||
};
|
||||
}
|
||||
@@ -79,75 +180,60 @@ macro_rules! is_negate {
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! is_prefix {
|
||||
($x:expr) => {
|
||||
$x as u32 & ($crate::parser::ast::FX | $crate::parser::ast::FY) != 0
|
||||
$x as u32 & ($crate::parser::ast::FX as u32 | $crate::parser::ast::FY as u32) != 0
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! is_postfix {
|
||||
($x:expr) => {
|
||||
$x as u32 & ($crate::parser::ast::XF | $crate::parser::ast::YF) != 0
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! is_infix {
|
||||
($x:expr) => {
|
||||
($x as u32
|
||||
& ($crate::parser::ast::XFX | $crate::parser::ast::XFY | $crate::parser::ast::YFX))
|
||||
& ($crate::parser::ast::XFX as u32
|
||||
| $crate::parser::ast::XFY as u32
|
||||
| $crate::parser::ast::YFX as u32))
|
||||
!= 0
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! is_xfx {
|
||||
($x:expr) => {
|
||||
($x as u32 & $crate::parser::ast::XFX) != 0
|
||||
($x as u32 & $crate::parser::ast::XFX as u32) != 0
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! is_xfy {
|
||||
($x:expr) => {
|
||||
($x as u32 & $crate::parser::ast::XFY) != 0
|
||||
($x as u32 & $crate::parser::ast::XFY as u32) != 0
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! is_yfx {
|
||||
($x:expr) => {
|
||||
($x as u32 & $crate::parser::ast::YFX) != 0
|
||||
($x as u32 & $crate::parser::ast::YFX as u32) != 0
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! is_yf {
|
||||
($x:expr) => {
|
||||
($x as u32 & $crate::parser::ast::YF) != 0
|
||||
($x as u32 & $crate::parser::ast::YF as u32) != 0
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! is_xf {
|
||||
($x:expr) => {
|
||||
($x as u32 & $crate::parser::ast::XF) != 0
|
||||
($x as u32 & $crate::parser::ast::XF as u32) != 0
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! is_fx {
|
||||
($x:expr) => {
|
||||
($x as u32 & $crate::parser::ast::FX) != 0
|
||||
($x as u32 & $crate::parser::ast::FX as u32) != 0
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! is_fy {
|
||||
($x:expr) => {
|
||||
($x as u32 & $crate::parser::ast::FY) != 0
|
||||
($x as u32 & $crate::parser::ast::FY as u32) != 0
|
||||
};
|
||||
}
|
||||
|
||||
@@ -215,20 +301,12 @@ impl Default for VarReg {
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! temp_v {
|
||||
($x:expr) => {
|
||||
$crate::parser::ast::RegType::Temp($x)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! perm_v {
|
||||
($x:expr) => {
|
||||
$crate::parser::ast::RegType::Perm($x)
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum GenContext {
|
||||
Head,
|
||||
@@ -247,11 +325,7 @@ impl GenContext {
|
||||
|
||||
#[inline]
|
||||
pub fn is_last(self) -> bool {
|
||||
if let GenContext::Last(_) = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
matches!(self, GenContext::Last(_))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,18 +340,18 @@ pub struct OpDesc {
|
||||
|
||||
impl OpDesc {
|
||||
#[inline]
|
||||
pub fn build_with(prec: u16, spec: u8) -> Self {
|
||||
OpDesc::new().with_spec(spec).with_prec(prec)
|
||||
pub fn build_with(prec: u16, spec: OpDeclSpec) -> Self {
|
||||
OpDesc::new().with_spec(spec as u8).with_prec(prec)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get(self) -> (u16, u8) {
|
||||
(self.prec(), self.spec())
|
||||
pub fn get(self) -> (u16, OpDeclSpec) {
|
||||
(self.prec(), self.get_spec())
|
||||
}
|
||||
|
||||
pub fn set(&mut self, prec: u16, spec: u8) {
|
||||
pub fn set(&mut self, prec: u16, spec: OpDeclSpec) {
|
||||
self.set_prec(prec);
|
||||
self.set_spec(spec);
|
||||
self.set_spec(spec as u8);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -286,13 +360,13 @@ impl OpDesc {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_spec(self) -> u8 {
|
||||
self.spec()
|
||||
pub fn get_spec(self) -> OpDeclSpec {
|
||||
OpDeclSpec::try_from(self.spec()).expect("OpDecl always contains a valud OpDeclSpec")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn arity(self) -> usize {
|
||||
if self.spec() as u32 & (XFX | XFY | YFX) == 0 {
|
||||
if !self.get_spec().is_infix() {
|
||||
1
|
||||
} else {
|
||||
2
|
||||
@@ -303,24 +377,16 @@ impl OpDesc {
|
||||
// name and fixity -> operator type and precedence.
|
||||
pub type OpDir = IndexMap<(Atom, Fixity), OpDesc, FxBuildHasher>;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
pub struct MachineFlags {
|
||||
pub double_quotes: DoubleQuotes,
|
||||
pub unknown: Unknown,
|
||||
}
|
||||
|
||||
impl Default for MachineFlags {
|
||||
fn default() -> Self {
|
||||
MachineFlags {
|
||||
double_quotes: DoubleQuotes::default(),
|
||||
unknown: Unknown::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq)]
|
||||
pub enum DoubleQuotes {
|
||||
Atom,
|
||||
#[default]
|
||||
Chars,
|
||||
Codes,
|
||||
}
|
||||
@@ -330,68 +396,26 @@ impl DoubleQuotes {
|
||||
matches!(self, DoubleQuotes::Chars)
|
||||
}
|
||||
|
||||
pub fn is_atom(self) -> bool {
|
||||
matches!(self, DoubleQuotes::Atom)
|
||||
}
|
||||
|
||||
pub fn is_codes(self) -> bool {
|
||||
matches!(self, DoubleQuotes::Codes)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DoubleQuotes {
|
||||
fn default() -> Self {
|
||||
DoubleQuotes::Chars
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub enum Unknown {
|
||||
#[default]
|
||||
Error,
|
||||
Fail,
|
||||
Warn,
|
||||
}
|
||||
|
||||
impl Unknown {
|
||||
pub fn is_error(self) -> bool {
|
||||
matches!(self, Unknown::Error)
|
||||
}
|
||||
|
||||
pub fn is_fail(self) -> bool {
|
||||
matches!(self, Unknown::Fail)
|
||||
}
|
||||
|
||||
pub fn is_warn(self) -> bool {
|
||||
matches!(self, Unknown::Warn)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Unknown {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
Unknown::Error
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_op_dir() -> OpDir {
|
||||
let mut op_dir = OpDir::with_hasher(FxBuildHasher::default());
|
||||
|
||||
op_dir.insert(
|
||||
(atom!(":-"), Fixity::In),
|
||||
OpDesc::build_with(1200, XFX as u8),
|
||||
);
|
||||
op_dir.insert(
|
||||
(atom!(":-"), Fixity::Pre),
|
||||
OpDesc::build_with(1200, FX as u8),
|
||||
);
|
||||
op_dir.insert(
|
||||
(atom!("?-"), Fixity::Pre),
|
||||
OpDesc::build_with(1200, FX as u8),
|
||||
);
|
||||
op_dir.insert(
|
||||
(atom!(","), Fixity::In),
|
||||
OpDesc::build_with(1000, XFY as u8),
|
||||
);
|
||||
op_dir.insert((atom!(":-"), Fixity::In), OpDesc::build_with(1200, XFX));
|
||||
op_dir.insert((atom!(":-"), Fixity::Pre), OpDesc::build_with(1200, FX));
|
||||
op_dir.insert((atom!("?-"), Fixity::Pre), OpDesc::build_with(1200, FX));
|
||||
op_dir.insert((atom!(","), Fixity::In), OpDesc::build_with(1000, XFY));
|
||||
|
||||
op_dir
|
||||
}
|
||||
@@ -402,6 +426,7 @@ pub enum ArithmeticError {
|
||||
UninstantiatedVar,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
pub enum ParserError {
|
||||
BackQuotedString(usize, usize),
|
||||
@@ -441,6 +466,9 @@ impl ParserError {
|
||||
ParserError::IO(e) if e.kind() == ErrorKind::UnexpectedEof => {
|
||||
atom!("unexpected_end_of_file")
|
||||
}
|
||||
ParserError::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"),
|
||||
@@ -505,7 +533,7 @@ impl<'a, 'b> CompositeOpDir<'a, 'b> {
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn get(&self, name: Atom, fixity: Fixity) -> Option<OpDesc> {
|
||||
let entry = if let Some(ref primary_op_dir) = &self.primary_op_dir {
|
||||
let entry = if let Some(primary_op_dir) = &self.primary_op_dir {
|
||||
primary_op_dir.get(&(name, fixity))
|
||||
} else {
|
||||
None
|
||||
@@ -558,7 +586,7 @@ impl Fixnum {
|
||||
|
||||
#[inline]
|
||||
pub fn get_tag(&self) -> HeapCellValueTag {
|
||||
use modular_bitfield::Specifier;
|
||||
use scryer_modular_bitfield::Specifier;
|
||||
HeapCellValueTag::from_bytes(self.tag()).unwrap()
|
||||
}
|
||||
|
||||
@@ -567,7 +595,7 @@ impl Fixnum {
|
||||
const UPPER_BOUND: i64 = (1 << 55) - 1;
|
||||
const LOWER_BOUND: i64 = -(1 << 55);
|
||||
|
||||
if LOWER_BOUND <= num && num <= UPPER_BOUND {
|
||||
if (LOWER_BOUND..=UPPER_BOUND).contains(&num) {
|
||||
Ok(Fixnum::new()
|
||||
.with_m(false)
|
||||
.with_f(false)
|
||||
@@ -582,7 +610,7 @@ impl Fixnum {
|
||||
pub fn get_num(self) -> i64 {
|
||||
let n = self.num() as i64;
|
||||
let (n, overflowed) = (n << 8).overflowing_shr(8);
|
||||
debug_assert_eq!(overflowed, false);
|
||||
debug_assert!(!overflowed);
|
||||
n
|
||||
}
|
||||
}
|
||||
@@ -633,7 +661,7 @@ impl fmt::Display for Literal {
|
||||
}
|
||||
|
||||
impl Literal {
|
||||
pub fn to_atom(&self, atom_tbl: &Arc<AtomTable>) -> Option<Atom> {
|
||||
pub fn as_atom(&self, atom_tbl: &Arc<AtomTable>) -> Option<Atom> {
|
||||
match self {
|
||||
Literal::Atom(atom) => Some(atom.defrock_brackets(atom_tbl)),
|
||||
_ => None,
|
||||
@@ -727,14 +755,7 @@ impl From<&str> for Var {
|
||||
}
|
||||
|
||||
impl Var {
|
||||
#[inline(always)]
|
||||
pub fn as_str(&self) -> Option<&str> {
|
||||
match self {
|
||||
Var::Named(value) => Some(&value),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::inherent_to_string)]
|
||||
#[inline(always)]
|
||||
pub fn to_string(&self) -> String {
|
||||
match self {
|
||||
@@ -758,13 +779,6 @@ pub enum Term {
|
||||
}
|
||||
|
||||
impl Term {
|
||||
pub fn into_literal(self) -> Option<Literal> {
|
||||
match self {
|
||||
Term::Literal(_, c) => Some(c),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn first_arg(&self) -> Option<&Term> {
|
||||
match self {
|
||||
Term::Clause(_, _, ref terms) => terms.first(),
|
||||
@@ -772,15 +786,6 @@ impl Term {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_name(&mut self, new_name: Atom) {
|
||||
match self {
|
||||
Term::Literal(_, Literal::Atom(ref mut atom)) | Term::Clause(_, ref mut atom, ..) => {
|
||||
*atom = new_name;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> Option<Atom> {
|
||||
match self {
|
||||
&Term::Literal(_, Literal::Atom(ref atom)) | &Term::Clause(_, ref atom, ..) => {
|
||||
@@ -798,23 +803,10 @@ impl Term {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn source_arity(terms: &[Term]) -> usize {
|
||||
if let Some(last_arg) = terms.last() {
|
||||
if let Term::Literal(_, Literal::CodeIndex(_)) = last_arg {
|
||||
return terms.len() - 1;
|
||||
}
|
||||
}
|
||||
|
||||
terms.len()
|
||||
}
|
||||
|
||||
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 Some(last_arg) = subterms.last() {
|
||||
if let Term::Literal(_, Literal::CodeIndex(_)) = last_arg {
|
||||
subterms.pop();
|
||||
}
|
||||
if let Some(Term::Literal(_, Literal::CodeIndex(_))) = subterms.last() {
|
||||
subterms.pop();
|
||||
}
|
||||
|
||||
if name == &s && subterms.len() == 2 {
|
||||
|
||||
@@ -52,11 +52,6 @@ impl<R> CharReader<R> {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn inner(&self) -> &R {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn inner_mut(&mut self) -> &mut R {
|
||||
&mut self.inner
|
||||
@@ -100,10 +95,6 @@ impl<R> CharReader<R> {
|
||||
&self.buf[self.pos..]
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> R {
|
||||
self.inner
|
||||
}
|
||||
|
||||
pub fn reset_buffer(&mut self) {
|
||||
self.buf.clear();
|
||||
self.pos = 0;
|
||||
@@ -131,15 +122,9 @@ impl<R: Read> CharReader<R> {
|
||||
|
||||
pub fn peek_byte(&mut self) -> Option<io::Result<u8>> {
|
||||
match self.refresh_buffer() {
|
||||
Ok(_buf) => {}
|
||||
Err(e) => return Some(Err(e)),
|
||||
Ok(_buf) => _buf.first().cloned().map(Ok),
|
||||
Err(e) => Some(Err(e)),
|
||||
}
|
||||
|
||||
return if let Some(b) = self.buf.get(0).cloned() {
|
||||
Some(Ok(b))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,6 +135,35 @@ impl<R: Read> CharRead for CharReader<R> {
|
||||
Err(e) => return Some(Err(e)),
|
||||
}
|
||||
|
||||
let bad_bytes_error = |buf: &[u8]| {
|
||||
// If we have 4 bytes that still don't make up
|
||||
// a valid code point, then we have garbage.
|
||||
|
||||
// We have bad data in the buffer. Remove
|
||||
// leading bytes until either the buffer is
|
||||
// empty, or we have a valid code point.
|
||||
|
||||
let mut split_point = 1;
|
||||
let mut badbytes = vec![];
|
||||
|
||||
loop {
|
||||
let (bad, rest) = buf.split_at(split_point);
|
||||
|
||||
if rest.is_empty() || str::from_utf8(rest).is_ok() {
|
||||
badbytes.extend_from_slice(bad);
|
||||
break;
|
||||
}
|
||||
|
||||
split_point += 1;
|
||||
}
|
||||
|
||||
// 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: badbytes })
|
||||
};
|
||||
|
||||
loop {
|
||||
let buf = &self.buf[self.pos..];
|
||||
|
||||
@@ -165,77 +179,51 @@ impl<R: Read> CharRead for CharReader<R> {
|
||||
};
|
||||
|
||||
if buf.len() - e.valid_up_to() >= 4 {
|
||||
// If we have 4 bytes that still don't make up
|
||||
// a valid code point, then we have garbage.
|
||||
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();
|
||||
|
||||
// We have bad data in the buffer. Remove
|
||||
// leading bytes until either the buffer is
|
||||
// empty, or we have a valid code point.
|
||||
|
||||
let mut split_point = 1;
|
||||
let mut badbytes = vec![];
|
||||
|
||||
loop {
|
||||
let (bad, rest) = buf.split_at(split_point);
|
||||
|
||||
if rest.is_empty() || str::from_utf8(rest).is_ok() {
|
||||
badbytes.extend_from_slice(bad);
|
||||
break;
|
||||
Some(Ok(c))
|
||||
}
|
||||
Err(e) => {
|
||||
let badbytes = self.buf[self.pos..self.pos + e.valid_up_to()].to_vec();
|
||||
|
||||
split_point += 1;
|
||||
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];
|
||||
}
|
||||
|
||||
// Raise the error. If we still have data in
|
||||
// the buffer, it will be returned on the next
|
||||
// loop.
|
||||
self.buf.truncate(buf_len - self.pos);
|
||||
|
||||
return Some(Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
BadUtf8Error { bytes: badbytes },
|
||||
)));
|
||||
} else {
|
||||
if self.pos >= self.buf.len() {
|
||||
return None;
|
||||
} else if self.buf.len() - self.pos >= 4 {
|
||||
return match str::from_utf8(&self.buf[self.pos..e.valid_up_to()]) {
|
||||
Ok(s) => {
|
||||
let mut chars = s.chars();
|
||||
let c = chars.next().unwrap();
|
||||
let buf_len = self.buf.len();
|
||||
self.pos = 0;
|
||||
|
||||
Some(Ok(c))
|
||||
}
|
||||
Err(e) => {
|
||||
let badbytes = self.buf[self.pos..e.valid_up_to()].to_vec();
|
||||
if buf_len >= 4 {
|
||||
continue;
|
||||
}
|
||||
|
||||
Some(Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
BadUtf8Error { bytes: badbytes },
|
||||
)))
|
||||
}
|
||||
};
|
||||
} else {
|
||||
let buf_len = self.buf.len();
|
||||
let mut word = [0u8; 4];
|
||||
let word_slice = &mut word[buf_len..4];
|
||||
|
||||
for (c, idx) in (self.pos..buf_len).enumerate() {
|
||||
self.buf[c] = self.buf[idx];
|
||||
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]);
|
||||
}
|
||||
|
||||
self.buf.truncate(buf_len - self.pos);
|
||||
|
||||
let buf_len = self.buf.len();
|
||||
|
||||
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(nread) => {
|
||||
self.buf.extend_from_slice(&word_slice[0..nread]);
|
||||
}
|
||||
}
|
||||
|
||||
self.pos = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -330,12 +318,11 @@ impl<R: Read> Read for CharReader<R> {
|
||||
return self.inner.read_vectored(bufs);
|
||||
}
|
||||
|
||||
let nread = {
|
||||
self.refresh_buffer()?;
|
||||
(&self.buf[self.pos..]).read_vectored(bufs)?
|
||||
};
|
||||
self.refresh_buffer()?;
|
||||
|
||||
let nread = (&self.buf[self.pos..]).read_vectored(bufs)?;
|
||||
self.consume(nread);
|
||||
|
||||
Ok(nread)
|
||||
}
|
||||
}
|
||||
@@ -419,6 +406,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "slow and not very relevant")]
|
||||
fn greek_lorem_ipsum() {
|
||||
let lorem_ipsum = "Λορεμ ιπσθμ δολορ σιτ αμετ, οφφενδιτ
|
||||
εφφιcιενδι σιτ ει, ηαρθμ λεγερε qθαερενδθμ ιθσ νε. Ηασ νο εροσ
|
||||
@@ -490,6 +478,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "slow and not very relevant")]
|
||||
fn armenian_lorem_ipsum() {
|
||||
let lorem_ipsum = "լոռեմ իպսում դոլոռ սիթ ամեթ, նովում գռաեծո
|
||||
սեա եա, աբհոռռեանթ դիսպութանդո եի քուի. իդ քուոդ ինդոծթում
|
||||
@@ -563,6 +552,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "slow and not very relevant")]
|
||||
fn russian_lorem_ipsum() {
|
||||
let lorem_ipsum = "Лорем ипсум долор сит амет, атяуи дицам еи
|
||||
сит, ид сеа фацилис елаборарет. Меа еу яуас алияуид, те яуи
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
use lexical::parse_lossy;
|
||||
|
||||
use crate::arena::ArenaAllocated;
|
||||
use crate::atom_table::*;
|
||||
pub use crate::machine::machine_state::*;
|
||||
use crate::parser::ast::*;
|
||||
@@ -48,11 +45,7 @@ pub enum Token {
|
||||
impl Token {
|
||||
#[inline]
|
||||
pub(super) fn is_end(&self) -> bool {
|
||||
if let Token::End = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
matches!(self, Token::End)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -604,10 +597,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if cut_char!(c) {
|
||||
self.skip_char(c);
|
||||
token.push(c);
|
||||
} else if semicolon_char!(c) {
|
||||
} else if cut_char!(c) || semicolon_char!(c) {
|
||||
self.skip_char(c);
|
||||
token.push(c);
|
||||
} else if single_quote_char!(c) {
|
||||
@@ -646,7 +636,9 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
|
||||
fn vacate_with_float(&mut self, mut token: String) -> Result<Token, ParserError> {
|
||||
self.return_char(token.pop().unwrap());
|
||||
let n = parse_lossy::<f64, _>(token.as_bytes())?;
|
||||
|
||||
let n = parse_float_lossy(&token)?;
|
||||
|
||||
Ok(Token::Literal(Literal::from(float_alloc!(
|
||||
n,
|
||||
self.machine_st.arena
|
||||
@@ -690,7 +682,8 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
if self.reader.peek_char().is_none() {
|
||||
self.return_char('.');
|
||||
|
||||
i64::from_str_radix(&token, 10)
|
||||
token
|
||||
.parse::<i64>()
|
||||
.map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena)))
|
||||
.or_else(|_| {
|
||||
token
|
||||
@@ -720,12 +713,12 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
token.push(c);
|
||||
|
||||
let c = match self.lookahead_char() {
|
||||
Err(_) => return Ok(self.vacate_with_float(token)?),
|
||||
Err(_) => return self.vacate_with_float(token),
|
||||
Ok(c) => c,
|
||||
};
|
||||
|
||||
if !sign_char!(c) && !decimal_digit_char!(c) {
|
||||
return Ok(self.vacate_with_float(token)?);
|
||||
return self.vacate_with_float(token);
|
||||
}
|
||||
|
||||
if sign_char!(c) {
|
||||
@@ -735,14 +728,14 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
let c = match self.lookahead_char() {
|
||||
Err(_) => {
|
||||
self.return_char(token.pop().unwrap());
|
||||
return Ok(self.vacate_with_float(token)?);
|
||||
return self.vacate_with_float(token);
|
||||
}
|
||||
Ok(c) => c,
|
||||
};
|
||||
|
||||
if !decimal_digit_char!(c) {
|
||||
self.return_char(token.pop().unwrap());
|
||||
return Ok(self.vacate_with_float(token)?);
|
||||
return self.vacate_with_float(token);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -763,16 +756,16 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
}
|
||||
}
|
||||
|
||||
let n = parse_lossy::<f64, _>(token.as_bytes())?;
|
||||
let n = parse_float_lossy(&token)?;
|
||||
Ok(Token::Literal(Literal::from(float_alloc!(
|
||||
n,
|
||||
self.machine_st.arena
|
||||
))))
|
||||
} else {
|
||||
return Ok(self.vacate_with_float(token)?);
|
||||
return self.vacate_with_float(token);
|
||||
}
|
||||
} else {
|
||||
let n = parse_lossy::<f64, _>(token.as_bytes())?;
|
||||
let n = parse_float_lossy(&token)?;
|
||||
Ok(Token::Literal(Literal::from(float_alloc!(
|
||||
n,
|
||||
self.machine_st.arena
|
||||
@@ -781,7 +774,147 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
} else {
|
||||
self.return_char('.');
|
||||
|
||||
i64::from_str_radix(&token, 10)
|
||||
token
|
||||
.parse::<i64>()
|
||||
.map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena)))
|
||||
.or_else(|_| {
|
||||
token
|
||||
.parse::<Integer>()
|
||||
.map(|n| {
|
||||
Token::Literal(Literal::Integer(arena_alloc!(
|
||||
n,
|
||||
&mut self.machine_st.arena
|
||||
)))
|
||||
})
|
||||
.map_err(|_| ParserError::ParseBigInt(self.line_num, self.col_num))
|
||||
})
|
||||
}
|
||||
} else if token.starts_with('0') && token.len() == 1 {
|
||||
if c == 'x' {
|
||||
self.hexadecimal_constant(c).or_else(|e| {
|
||||
if let ParserError::ParseBigInt(..) = e {
|
||||
token
|
||||
.parse::<i64>()
|
||||
.map(|n| {
|
||||
Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena))
|
||||
})
|
||||
.or_else(|_| {
|
||||
token
|
||||
.parse::<Integer>()
|
||||
.map(|n| {
|
||||
Token::Literal(Literal::Integer(arena_alloc!(
|
||||
n,
|
||||
&mut self.machine_st.arena
|
||||
)))
|
||||
})
|
||||
.map_err(|_| {
|
||||
ParserError::ParseBigInt(self.line_num, self.col_num)
|
||||
})
|
||||
})
|
||||
} else {
|
||||
Err(e)
|
||||
}
|
||||
})
|
||||
} else if c == 'o' {
|
||||
self.octal_constant(c).or_else(|e| {
|
||||
if let ParserError::ParseBigInt(..) = e {
|
||||
token
|
||||
.parse::<i64>()
|
||||
.map(|n| {
|
||||
Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena))
|
||||
})
|
||||
.or_else(|_| {
|
||||
token
|
||||
.parse::<Integer>()
|
||||
.map(|n| {
|
||||
Token::Literal(Literal::Integer(arena_alloc!(
|
||||
n,
|
||||
&mut self.machine_st.arena
|
||||
)))
|
||||
})
|
||||
.map_err(|_| {
|
||||
ParserError::ParseBigInt(self.line_num, self.col_num)
|
||||
})
|
||||
})
|
||||
} else {
|
||||
Err(e)
|
||||
}
|
||||
})
|
||||
} else if c == 'b' {
|
||||
self.binary_constant(c).or_else(|e| {
|
||||
if let ParserError::ParseBigInt(..) = e {
|
||||
token
|
||||
.parse::<i64>()
|
||||
.map(|n| {
|
||||
Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena))
|
||||
})
|
||||
.or_else(|_| {
|
||||
token
|
||||
.parse::<Integer>()
|
||||
.map(|n| {
|
||||
Token::Literal(Literal::Integer(arena_alloc!(
|
||||
n,
|
||||
&mut self.machine_st.arena
|
||||
)))
|
||||
})
|
||||
.map_err(|_| {
|
||||
ParserError::ParseBigInt(self.line_num, self.col_num)
|
||||
})
|
||||
})
|
||||
} else {
|
||||
Err(e)
|
||||
}
|
||||
})
|
||||
} else if single_quote_char!(c) {
|
||||
self.skip_char(c);
|
||||
let c = self.lookahead_char()?;
|
||||
|
||||
if backslash_char!(c) {
|
||||
self.skip_char(c);
|
||||
let c = self.lookahead_char()?;
|
||||
|
||||
if new_line_char!(c) {
|
||||
self.skip_char(c);
|
||||
self.return_char('\'');
|
||||
|
||||
return Ok(Token::Literal(Literal::Fixnum(Fixnum::build_with(0))));
|
||||
} else {
|
||||
self.return_char('\\');
|
||||
}
|
||||
}
|
||||
|
||||
self.get_single_quoted_char()
|
||||
.map(|c| Token::Literal(Literal::Fixnum(Fixnum::build_with(c as i64))))
|
||||
.or_else(|err| {
|
||||
match err {
|
||||
ParserError::UnexpectedChar('\'', ..) => {}
|
||||
err => return Err(err),
|
||||
}
|
||||
|
||||
self.return_char(c);
|
||||
|
||||
token
|
||||
.parse::<i64>()
|
||||
.map(|n| {
|
||||
Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena))
|
||||
})
|
||||
.or_else(|_| {
|
||||
token
|
||||
.parse::<Integer>()
|
||||
.map(|n| {
|
||||
Token::Literal(Literal::Integer(arena_alloc!(
|
||||
n,
|
||||
&mut self.machine_st.arena
|
||||
)))
|
||||
})
|
||||
.map_err(|_| {
|
||||
ParserError::ParseBigInt(self.line_num, self.col_num)
|
||||
})
|
||||
})
|
||||
})
|
||||
} else {
|
||||
token
|
||||
.parse::<i64>()
|
||||
.map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena)))
|
||||
.or_else(|_| {
|
||||
token
|
||||
@@ -796,155 +929,20 @@ 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 {
|
||||
i64::from_str_radix(&token, 10)
|
||||
.map(|n| {
|
||||
Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena))
|
||||
})
|
||||
.or_else(|_| {
|
||||
token
|
||||
.parse::<Integer>()
|
||||
.map(|n| {
|
||||
Token::Literal(Literal::Integer(arena_alloc!(
|
||||
n,
|
||||
&mut self.machine_st.arena
|
||||
)))
|
||||
})
|
||||
.map_err(|_| {
|
||||
ParserError::ParseBigInt(self.line_num, self.col_num)
|
||||
})
|
||||
})
|
||||
} else {
|
||||
Err(e)
|
||||
}
|
||||
})
|
||||
} else if c == 'o' {
|
||||
self.octal_constant(c).or_else(|e| {
|
||||
if let ParserError::ParseBigInt(..) = e {
|
||||
i64::from_str_radix(&token, 10)
|
||||
.map(|n| {
|
||||
Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena))
|
||||
})
|
||||
.or_else(|_| {
|
||||
token
|
||||
.parse::<Integer>()
|
||||
.map(|n| {
|
||||
Token::Literal(Literal::Integer(arena_alloc!(
|
||||
n,
|
||||
&mut self.machine_st.arena
|
||||
)))
|
||||
})
|
||||
.map_err(|_| {
|
||||
ParserError::ParseBigInt(self.line_num, self.col_num)
|
||||
})
|
||||
})
|
||||
} else {
|
||||
Err(e)
|
||||
}
|
||||
})
|
||||
} else if c == 'b' {
|
||||
self.binary_constant(c).or_else(|e| {
|
||||
if let ParserError::ParseBigInt(..) = e {
|
||||
i64::from_str_radix(&token, 10)
|
||||
.map(|n| {
|
||||
Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena))
|
||||
})
|
||||
.or_else(|_| {
|
||||
token
|
||||
.parse::<Integer>()
|
||||
.map(|n| {
|
||||
Token::Literal(Literal::Integer(arena_alloc!(
|
||||
n,
|
||||
&mut self.machine_st.arena
|
||||
)))
|
||||
})
|
||||
.map_err(|_| {
|
||||
ParserError::ParseBigInt(self.line_num, self.col_num)
|
||||
})
|
||||
})
|
||||
} else {
|
||||
Err(e)
|
||||
}
|
||||
})
|
||||
} else if single_quote_char!(c) {
|
||||
self.skip_char(c);
|
||||
let c = self.lookahead_char()?;
|
||||
|
||||
if backslash_char!(c) {
|
||||
self.skip_char(c);
|
||||
let c = self.lookahead_char()?;
|
||||
|
||||
if new_line_char!(c) {
|
||||
self.skip_char(c);
|
||||
self.return_char('\'');
|
||||
|
||||
return Ok(Token::Literal(Literal::Fixnum(Fixnum::build_with(0))));
|
||||
} else {
|
||||
self.return_char('\\');
|
||||
}
|
||||
}
|
||||
|
||||
self.get_single_quoted_char()
|
||||
.map(|c| Token::Literal(Literal::Fixnum(Fixnum::build_with(c as i64))))
|
||||
.or_else(|err| {
|
||||
match err {
|
||||
ParserError::UnexpectedChar('\'', ..) => {}
|
||||
err => return Err(err),
|
||||
}
|
||||
|
||||
self.return_char(c);
|
||||
|
||||
i64::from_str_radix(&token, 10)
|
||||
.map(|n| {
|
||||
Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena))
|
||||
})
|
||||
.or_else(|_| {
|
||||
token
|
||||
.parse::<Integer>()
|
||||
.map(|n| {
|
||||
Token::Literal(Literal::Integer(arena_alloc!(
|
||||
n,
|
||||
&mut self.machine_st.arena
|
||||
)))
|
||||
})
|
||||
.map_err(|_| {
|
||||
ParserError::ParseBigInt(self.line_num, self.col_num)
|
||||
})
|
||||
})
|
||||
token
|
||||
.parse::<i64>()
|
||||
.map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena)))
|
||||
.or_else(|_| {
|
||||
token
|
||||
.parse::<Integer>()
|
||||
.map(|n| {
|
||||
Token::Literal(Literal::Integer(arena_alloc!(
|
||||
n,
|
||||
&mut self.machine_st.arena
|
||||
)))
|
||||
})
|
||||
} else {
|
||||
i64::from_str_radix(&token, 10)
|
||||
.map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena)))
|
||||
.or_else(|_| {
|
||||
token
|
||||
.parse::<Integer>()
|
||||
.map(|n| {
|
||||
Token::Literal(Literal::Integer(arena_alloc!(
|
||||
n,
|
||||
&mut self.machine_st.arena
|
||||
)))
|
||||
})
|
||||
.map_err(|_| ParserError::ParseBigInt(self.line_num, self.col_num))
|
||||
})
|
||||
}
|
||||
} else {
|
||||
i64::from_str_radix(&token, 10)
|
||||
.map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena)))
|
||||
.or_else(|_| {
|
||||
token
|
||||
.parse::<Integer>()
|
||||
.map(|n| {
|
||||
Token::Literal(Literal::Integer(arena_alloc!(
|
||||
n,
|
||||
&mut self.machine_st.arena
|
||||
)))
|
||||
})
|
||||
.map_err(|_| ParserError::ParseBigInt(self.line_num, self.col_num))
|
||||
})
|
||||
}
|
||||
.map_err(|_| ParserError::ParseBigInt(self.line_num, self.col_num))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -953,6 +951,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
c: Option<char>,
|
||||
layout_info: &mut LayoutInfo,
|
||||
) -> Result<(), ParserError> {
|
||||
#[allow(clippy::redundant_guards)]
|
||||
match c {
|
||||
Some(c) if layout_char!(c) => {
|
||||
self.skip_char(c);
|
||||
@@ -1103,3 +1102,13 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -1,237 +1,204 @@
|
||||
#[macro_export]
|
||||
macro_rules! char_class {
|
||||
($c: expr, [$head:expr]) => ($c == $head);
|
||||
($c: expr, [$head:expr $(, $cs:expr)+]) => ($c == $head || $crate::char_class!($c, [$($cs),*]));
|
||||
($c: expr, [$head:expr $(, $cs:expr)+]) => ($c == $head || char_class!($c, [$($cs),*]));
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! alpha_char {
|
||||
($c: expr) => {
|
||||
(!$c.is_numeric()
|
||||
&& !$c.is_whitespace()
|
||||
&& !$c.is_control()
|
||||
&& !$crate::graphic_token_char!($c)
|
||||
&& !$crate::layout_char!($c)
|
||||
&& !$crate::meta_char!($c)
|
||||
&& !$crate::solo_char!($c))
|
||||
&& !graphic_token_char!($c)
|
||||
&& !layout_char!($c)
|
||||
&& !meta_char!($c)
|
||||
&& !solo_char!($c))
|
||||
|| $c == '_'
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! alpha_numeric_char {
|
||||
($c: expr) => {
|
||||
$crate::alpha_char!($c) || $c.is_numeric()
|
||||
alpha_char!($c) || $c.is_numeric()
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! backslash_char {
|
||||
($c: expr) => {
|
||||
$c == '\\'
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! back_quote_char {
|
||||
($c: expr) => {
|
||||
$c == '`'
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! octet_char {
|
||||
($c: expr) => {
|
||||
('\u{0000}'..='\u{00FF}').contains(&$c)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! capital_letter_char {
|
||||
($c: expr) => {
|
||||
$c.is_uppercase()
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! comment_1_char {
|
||||
($c: expr) => {
|
||||
$c == '/'
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! comment_2_char {
|
||||
($c: expr) => {
|
||||
$c == '*'
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! cut_char {
|
||||
($c: expr) => {
|
||||
$c == '!'
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! decimal_digit_char {
|
||||
($c: expr) => {
|
||||
('0'..='9').contains(&$c)
|
||||
$c.is_ascii_digit()
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! decimal_point_char {
|
||||
($c: expr) => {
|
||||
$c == '.'
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! double_quote_char {
|
||||
($c: expr) => {
|
||||
$c == '"'
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! end_line_comment_char {
|
||||
($c: expr) => {
|
||||
$c == '%'
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! exponent_char {
|
||||
($c: expr) => {
|
||||
$c == 'e' || $c == 'E'
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! graphic_char {
|
||||
($c: expr) => ($crate::char_class!($c, ['#', '$', '&', '*', '+', '-', '.', '/', ':',
|
||||
($c: expr) => (char_class!($c, ['#', '$', '&', '*', '+', '-', '.', '/', ':',
|
||||
'<', '=', '>', '?', '@', '^', '~']))
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! graphic_token_char {
|
||||
($c: expr) => {
|
||||
$crate::graphic_char!($c) || $crate::backslash_char!($c)
|
||||
graphic_char!($c) || backslash_char!($c)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! hexadecimal_digit_char {
|
||||
($c: expr) => {
|
||||
('0'..='9').contains(&$c) || ('A'..='F').contains(&$c) || ('a'..='f').contains(&$c)
|
||||
$c.is_ascii_digit() || ('A'..='F').contains(&$c) || ('a'..='f').contains(&$c)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! layout_char {
|
||||
($c: expr) => {
|
||||
$crate::char_class!($c, [' ', '\r', '\n', '\t', '\u{0B}', '\u{0C}'])
|
||||
char_class!($c, [' ', '\r', '\n', '\t', '\u{0B}', '\u{0C}'])
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! meta_char {
|
||||
($c: expr) => {
|
||||
$crate::char_class!($c, ['\\', '\'', '"', '`'])
|
||||
char_class!($c, ['\\', '\'', '"', '`'])
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! new_line_char {
|
||||
($c: expr) => {
|
||||
$c == '\n'
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! octal_digit_char {
|
||||
($c: expr) => {
|
||||
('0'..='7').contains(&$c)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! binary_digit_char {
|
||||
($c: expr) => {
|
||||
$c == '0' || $c == '1'
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! prolog_char {
|
||||
($c: expr) => {
|
||||
$crate::graphic_char!($c)
|
||||
|| $crate::alpha_numeric_char!($c)
|
||||
|| $crate::solo_char!($c)
|
||||
|| $crate::layout_char!($c)
|
||||
|| $crate::meta_char!($c)
|
||||
graphic_char!($c)
|
||||
|| alpha_numeric_char!($c)
|
||||
|| solo_char!($c)
|
||||
|| layout_char!($c)
|
||||
|| meta_char!($c)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! semicolon_char {
|
||||
($c: expr) => {
|
||||
$c == ';'
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! sign_char {
|
||||
($c: expr) => {
|
||||
$c == '-' || $c == '+'
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! single_quote_char {
|
||||
($c: expr) => {
|
||||
$c == '\''
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! small_letter_char {
|
||||
($c: expr) => {
|
||||
$c.is_alphabetic() && !$c.is_uppercase()
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! solo_char {
|
||||
($c: expr) => {
|
||||
$crate::char_class!($c, ['!', '(', ')', ',', ';', '[', ']', '{', '}', '|', '%'])
|
||||
char_class!($c, ['!', '(', ')', ',', ';', '[', ']', '{', '}', '|', '%'])
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! space_char {
|
||||
($c: expr) => {
|
||||
$c == ' '
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! symbolic_control_char {
|
||||
($c: expr) => {
|
||||
$crate::char_class!($c, ['a', 'b', 'f', 'n', 'r', 't', 'v', '0'])
|
||||
char_class!($c, ['a', 'b', 'f', 'n', 'r', 't', 'v', '0'])
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! symbolic_hexadecimal_char {
|
||||
($c: expr) => {
|
||||
$c == 'x'
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! variable_indicator_char {
|
||||
($c: expr) => {
|
||||
$c == '_'
|
||||
|
||||
@@ -11,4 +11,5 @@ pub mod ast;
|
||||
#[macro_use]
|
||||
pub mod macros;
|
||||
pub mod lexer;
|
||||
#[allow(clippy::module_inception)]
|
||||
pub mod parser;
|
||||
|
||||
@@ -97,14 +97,19 @@ pub(crate) fn as_partial_string(
|
||||
string.push(*c);
|
||||
}
|
||||
_ => {
|
||||
return Err(Term::Cons(Cell::default(), Box::new(head), orig_tail));
|
||||
tail = Term::Cons(
|
||||
Cell::default(),
|
||||
Box::new((**prev).clone()),
|
||||
Box::new((**succ).clone()),
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
tail_ref = succ;
|
||||
}
|
||||
Term::PartialString(_, pstr, tail) => {
|
||||
string += &pstr;
|
||||
string += pstr;
|
||||
tail_ref = tail;
|
||||
}
|
||||
Term::CompleteString(_, cstr) => {
|
||||
@@ -145,6 +150,7 @@ pub fn get_op_desc(name: Atom, op_dir: &CompositeOpDir) -> Option<CompositeOpDes
|
||||
op_desc.pre = pri as usize;
|
||||
op_desc.spec |= spec as u32;
|
||||
} else if name == atom!("-") {
|
||||
// used to denote a negative sign that should be treated as an atom and not an operator
|
||||
op_desc.spec |= NEGATIVE_SIGN;
|
||||
}
|
||||
}
|
||||
@@ -174,31 +180,6 @@ pub fn get_op_desc(name: Atom, op_dir: &CompositeOpDir) -> Option<CompositeOpDes
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_clause_spec(name: Atom, arity: usize, op_dir: &CompositeOpDir) -> Option<OpDesc> {
|
||||
match arity {
|
||||
1 => {
|
||||
/* This is a clause with an operator principal functor. Prefix operators
|
||||
are supposed over post.
|
||||
*/
|
||||
if let Some(cell) = op_dir.get(name, Fixity::Pre) {
|
||||
return Some(cell);
|
||||
}
|
||||
|
||||
if let Some(cell) = op_dir.get(name, Fixity::Post) {
|
||||
return Some(cell);
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
if let Some(cell) = op_dir.get(name, Fixity::In) {
|
||||
return Some(cell);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn affirm_xfx(priority: usize, d2: TokenDesc, d3: TokenDesc, d1: TokenDesc) -> bool {
|
||||
d2.priority <= priority
|
||||
&& is_term!(d3.spec)
|
||||
@@ -335,16 +316,6 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn line_num(&self) -> usize {
|
||||
self.lexer.line_num
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn col_num(&self) -> usize {
|
||||
self.lexer.col_num
|
||||
}
|
||||
|
||||
fn get_term_name(&mut self, td: TokenDesc) -> Option<Atom> {
|
||||
match td.tt {
|
||||
TokenType::HeadTailSeparator => Some(atom!("|")),
|
||||
@@ -379,10 +350,10 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
}
|
||||
}
|
||||
|
||||
fn push_unary_op(&mut self, td: TokenDesc, spec: Specifier, assoc: u32) {
|
||||
fn push_unary_op(&mut self, td: TokenDesc, spec: Specifier, assoc: OpDeclSpec) {
|
||||
if let Some(mut arg1) = self.terms.pop() {
|
||||
if let Some(mut name) = self.terms.pop() {
|
||||
if is_postfix!(assoc) {
|
||||
if assoc.is_postfix() {
|
||||
mem::swap(&mut arg1, &mut name);
|
||||
}
|
||||
|
||||
@@ -464,7 +435,12 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
Token::End => TokenType::End,
|
||||
};
|
||||
|
||||
self.stack.push(TokenDesc { tt, priority, spec, unfold_bounds: 0, });
|
||||
self.stack.push(TokenDesc {
|
||||
tt,
|
||||
priority,
|
||||
spec,
|
||||
unfold_bounds: 0,
|
||||
});
|
||||
}
|
||||
|
||||
fn reduce_op(&mut self, priority: usize) {
|
||||
@@ -472,10 +448,9 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
if let Some(desc1) = self.stack.pop() {
|
||||
if let Some(desc2) = self.stack.pop() {
|
||||
if let Some(desc3) = self.stack.pop() {
|
||||
if is_xfx!(desc2.spec) && affirm_xfx(priority, desc2, desc3, desc1) {
|
||||
self.push_binary_op(desc2, LTERM);
|
||||
continue;
|
||||
} else if is_yfx!(desc2.spec) && affirm_yfx(priority, desc2, desc3, desc1) {
|
||||
if is_xfx!(desc2.spec) && affirm_xfx(priority, desc2, desc3, desc1)
|
||||
|| is_yfx!(desc2.spec) && affirm_yfx(priority, desc2, desc3, desc1)
|
||||
{
|
||||
self.push_binary_op(desc2, LTERM);
|
||||
continue;
|
||||
} else if is_xfy!(desc2.spec) && affirm_xfy(priority, desc2, desc3, desc1) {
|
||||
@@ -555,10 +530,12 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
if self.stack.len() > 2 * arity {
|
||||
let idx = self.stack.len() - 2 * arity - 1;
|
||||
|
||||
if is_infix!(self.stack[idx].spec) && idx > 0 {
|
||||
if !is_op!(self.stack[idx - 1].spec) && !self.stack[idx - 1].tt.is_sep() {
|
||||
return false;
|
||||
}
|
||||
if is_infix!(self.stack[idx].spec)
|
||||
&& idx > 0
|
||||
&& !is_op!(self.stack[idx - 1].spec)
|
||||
&& !self.stack[idx - 1].tt.is_sep()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
@@ -571,59 +548,57 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
let stack_len = self.stack.len() - 2 * arity - 1;
|
||||
let idx = self.terms.len() - arity;
|
||||
|
||||
if TokenType::Term == self.stack[stack_len].tt {
|
||||
if atomize_term(&self.lexer.machine_st.atom_tbl, &self.terms[idx - 1]).is_some() {
|
||||
self.stack.truncate(stack_len + 1);
|
||||
if TokenType::Term == self.stack[stack_len].tt
|
||||
&& atomize_term(&self.lexer.machine_st.atom_tbl, &self.terms[idx - 1]).is_some()
|
||||
{
|
||||
self.stack.truncate(stack_len + 1);
|
||||
|
||||
let mut subterms: Vec<_> = self.terms.drain(idx..).collect();
|
||||
let mut subterms: Vec<_> = self.terms.drain(idx..).collect();
|
||||
|
||||
if let Some(name) = self
|
||||
.terms
|
||||
.pop()
|
||||
.and_then(|t| atomize_term(&self.lexer.machine_st.atom_tbl, &t))
|
||||
{
|
||||
// reduce the '.' functor to a cons cell if it applies.
|
||||
if name == atom!(".") && subterms.len() == 2 {
|
||||
let tail = subterms.pop().unwrap();
|
||||
let head = subterms.pop().unwrap();
|
||||
if let Some(name) = self
|
||||
.terms
|
||||
.pop()
|
||||
.and_then(|t| atomize_term(&self.lexer.machine_st.atom_tbl, &t))
|
||||
{
|
||||
// reduce the '.' functor to a cons cell if it applies.
|
||||
if name == atom!(".") && subterms.len() == 2 {
|
||||
let tail = subterms.pop().unwrap();
|
||||
let head = subterms.pop().unwrap();
|
||||
|
||||
self.terms.push(match as_partial_string(head, tail) {
|
||||
Ok((string_buf, Some(tail))) => {
|
||||
Term::PartialString(Cell::default(), string_buf, tail)
|
||||
}
|
||||
Ok((string_buf, None)) => {
|
||||
let atom = AtomTable::build_with(
|
||||
&self.lexer.machine_st.atom_tbl,
|
||||
&string_buf,
|
||||
);
|
||||
Term::CompleteString(Cell::default(), atom)
|
||||
}
|
||||
Err(term) => term,
|
||||
});
|
||||
} else {
|
||||
self.terms
|
||||
.push(Term::Clause(Cell::default(), name, subterms));
|
||||
}
|
||||
|
||||
if let Some(&mut TokenDesc {
|
||||
ref mut tt,
|
||||
ref mut priority,
|
||||
ref mut spec,
|
||||
ref mut unfold_bounds,
|
||||
}) = self.stack.last_mut()
|
||||
{
|
||||
if *spec == BTERM {
|
||||
return false;
|
||||
self.terms.push(match as_partial_string(head, tail) {
|
||||
Ok((string_buf, Some(tail))) => {
|
||||
Term::PartialString(Cell::default(), string_buf, tail)
|
||||
}
|
||||
Ok((string_buf, None)) => {
|
||||
let atom =
|
||||
AtomTable::build_with(&self.lexer.machine_st.atom_tbl, &string_buf);
|
||||
Term::CompleteString(Cell::default(), atom)
|
||||
}
|
||||
Err(term) => term,
|
||||
});
|
||||
} else {
|
||||
self.terms
|
||||
.push(Term::Clause(Cell::default(), name, subterms));
|
||||
}
|
||||
|
||||
*tt = TokenType::Term;
|
||||
*priority = 0;
|
||||
*spec = TERM;
|
||||
*unfold_bounds = 0;
|
||||
if let Some(&mut TokenDesc {
|
||||
ref mut tt,
|
||||
ref mut priority,
|
||||
ref mut spec,
|
||||
ref mut unfold_bounds,
|
||||
}) = self.stack.last_mut()
|
||||
{
|
||||
if *spec == BTERM {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
*tt = TokenType::Term;
|
||||
*priority = 0;
|
||||
*spec = TERM;
|
||||
*unfold_bounds = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -642,34 +617,31 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
/* '|' is a head-tail separator here, not
|
||||
* an operator, so expand the
|
||||
* terms it compacted out again. */
|
||||
match (term.name(), term.arity()) {
|
||||
(Some(name), 2) if name == atom!(",") => {
|
||||
let terms = if op_desc.unfold_bounds == 0 {
|
||||
unfold_by_str(term, atom!(","))
|
||||
} else {
|
||||
let mut terms = vec![];
|
||||
if let (Some(atom!(",")), 2) = (term.name(), term.arity()) {
|
||||
let terms = if op_desc.unfold_bounds == 0 {
|
||||
unfold_by_str(term, atom!(","))
|
||||
} else {
|
||||
let mut terms = vec![];
|
||||
|
||||
while let Some((fst, snd)) = unfold_by_str_once(&mut term, atom!(",")) {
|
||||
terms.push(fst);
|
||||
term = snd;
|
||||
while let Some((fst, snd)) = unfold_by_str_once(&mut term, atom!(",")) {
|
||||
terms.push(fst);
|
||||
term = snd;
|
||||
|
||||
op_desc.unfold_bounds -= 2;
|
||||
op_desc.unfold_bounds -= 2;
|
||||
|
||||
if op_desc.unfold_bounds == 0 {
|
||||
break;
|
||||
}
|
||||
if op_desc.unfold_bounds == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
terms.push(term);
|
||||
terms
|
||||
};
|
||||
terms.push(term);
|
||||
terms
|
||||
};
|
||||
|
||||
let arity = terms.len() - 1;
|
||||
let arity = terms.len() - 1;
|
||||
|
||||
self.terms.extend(terms.into_iter());
|
||||
return arity;
|
||||
}
|
||||
_ => {}
|
||||
self.terms.extend(terms);
|
||||
return arity;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -687,23 +659,20 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
// expect a term or non-comma operator.
|
||||
if let TokenType::Comma = desc.tt {
|
||||
return None;
|
||||
} else if is_term!(desc.spec) || is_op!(desc.spec) {
|
||||
} else if is_term!(desc.spec) || is_op!(desc.spec) || is_negate!(desc.spec) {
|
||||
arity += 1;
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
} else {
|
||||
if desc.tt == TokenType::HeadTailSeparator {
|
||||
if arity == 1 {
|
||||
continue;
|
||||
}
|
||||
|
||||
return None;
|
||||
} else if desc.tt == TokenType::OpenList {
|
||||
return Some(arity);
|
||||
} else if desc.tt != TokenType::Comma {
|
||||
return None;
|
||||
} else if desc.tt == TokenType::HeadTailSeparator {
|
||||
if arity == 1 {
|
||||
continue;
|
||||
}
|
||||
return None;
|
||||
} else if desc.tt == TokenType::OpenList {
|
||||
return Some(arity);
|
||||
} else if desc.tt != TokenType::Comma {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -882,7 +851,7 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
.push(Term::Literal(Cell::default(), Literal::Atom(atom)));
|
||||
}
|
||||
|
||||
self.stack[idx].spec = if self.stack[idx].priority > 0 { TERM } else { BTERM };
|
||||
self.stack[idx].spec = BTERM;
|
||||
self.stack[idx].tt = TokenType::Term;
|
||||
self.stack[idx].priority = 0;
|
||||
|
||||
@@ -908,10 +877,11 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
// can't be prefix, so either inf == 0
|
||||
// or post == 0.
|
||||
self.reduce_op(inf + post);
|
||||
|
||||
// let fixity = if inf > 0 { Fixity::In } else { Fixity::Post };
|
||||
|
||||
self.promote_atom_op(name, inf + post, spec & (XFX | XFY | YFX | YF | XF));
|
||||
self.promote_atom_op(
|
||||
name,
|
||||
inf + post,
|
||||
spec & (XFX as u32 | XFY as u32 | YFX as u32 | YF as u32 | XF as u32),
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
self.reduce_op(inf + post);
|
||||
@@ -922,14 +892,22 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
self.promote_atom_op(
|
||||
name,
|
||||
inf + post,
|
||||
spec & (XFX | XFY | YFX | XF | YF),
|
||||
spec & (XFX as u32
|
||||
| XFY as u32
|
||||
| YFX as u32
|
||||
| XF as u32
|
||||
| YF as u32),
|
||||
);
|
||||
} else {
|
||||
self.promote_atom_op(name, pre, spec & (FX | FY | NEGATIVE_SIGN));
|
||||
|
||||
return Ok(true);
|
||||
}
|
||||
} else {
|
||||
self.promote_atom_op(name, pre, spec & (FX | FY | NEGATIVE_SIGN));
|
||||
}
|
||||
|
||||
self.promote_atom_op(
|
||||
name,
|
||||
pre,
|
||||
spec & (FX as u32 | FY as u32 | NEGATIVE_SIGN),
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -1018,13 +996,11 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
Token::Open => self.shift(Token::Open, 1300, DELIMITER),
|
||||
Token::OpenCT => self.shift(Token::OpenCT, 1300, DELIMITER),
|
||||
Token::Close => {
|
||||
if !self.reduce_term() {
|
||||
if !self.reduce_brackets() {
|
||||
return Err(ParserError::IncompleteReduction(
|
||||
self.lexer.line_num,
|
||||
self.lexer.col_num,
|
||||
));
|
||||
}
|
||||
if !self.reduce_term() && !self.reduce_brackets() {
|
||||
return Err(ParserError::IncompleteReduction(
|
||||
self.lexer.line_num,
|
||||
self.lexer.col_num,
|
||||
));
|
||||
}
|
||||
}
|
||||
Token::OpenList => self.shift(Token::OpenList, 1300, DELIMITER),
|
||||
@@ -1067,7 +1043,7 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
}
|
||||
Token::Comma => {
|
||||
self.reduce_op(1000);
|
||||
self.shift(Token::Comma, 1000, XFY);
|
||||
self.shift(Token::Comma, 1000, XFY as u32);
|
||||
}
|
||||
Token::End => match self.stack.last().map(|t| t.tt) {
|
||||
Some(TokenType::Open)
|
||||
|
||||
@@ -28,6 +28,7 @@ impl<T: RawBlockTraits> RawBlock<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::new_without_default)]
|
||||
pub fn new() -> Self {
|
||||
let mut block = Self::empty_block();
|
||||
|
||||
@@ -40,22 +41,35 @@ impl<T: RawBlockTraits> RawBlock<T> {
|
||||
|
||||
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.top = (self.base as usize + cap) as *const _;
|
||||
*self.ptr.get_mut() = self.base as *mut _;
|
||||
let new_base = alloc::alloc(layout).cast_const();
|
||||
if new_base.is_null() {
|
||||
panic!(
|
||||
"failed to allocate in init_at_size for {}",
|
||||
std::any::type_name::<Self>()
|
||||
);
|
||||
}
|
||||
self.base = new_base;
|
||||
self.top = self.base.add(cap);
|
||||
*self.ptr.get_mut() = self.base.cast_mut();
|
||||
}
|
||||
|
||||
pub unsafe fn grow(&mut self) {
|
||||
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());
|
||||
|
||||
self.base = alloc::realloc(self.base as *mut _, layout, size * 2) as *const _;
|
||||
self.top = (self.base as usize + size * 2) as *const _;
|
||||
*self.ptr.get_mut() = (self.base as usize + size) as *mut _;
|
||||
let new_base = alloc::realloc(self.base.cast_mut(), layout, size * 2).cast_const();
|
||||
if new_base.is_null() {
|
||||
false
|
||||
} else {
|
||||
self.base = new_base;
|
||||
self.top = (self.base as usize + size * 2) as *const _;
|
||||
*self.ptr.get_mut() = (self.base as usize + size) as *mut _;
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +85,7 @@ impl<T: RawBlockTraits> RawBlock<T> {
|
||||
} else {
|
||||
let allocated = (*self.ptr.get()) as usize - self.base as usize;
|
||||
self.base.copy_to(new_block.base.cast_mut(), allocated);
|
||||
*new_block.ptr.get_mut() = new_block.base.offset(allocated as isize).cast_mut();
|
||||
*new_block.ptr.get_mut() = new_block.base.add(allocated).cast_mut();
|
||||
Some(new_block)
|
||||
}
|
||||
}
|
||||
@@ -95,9 +109,10 @@ impl<T: RawBlockTraits> RawBlock<T> {
|
||||
}
|
||||
|
||||
pub unsafe fn alloc(&self, size: usize) -> *mut u8 {
|
||||
if self.free_space() >= size {
|
||||
let aligned_size = size.next_multiple_of(size);
|
||||
if self.free_space() >= aligned_size {
|
||||
let ptr = *self.ptr.get();
|
||||
*self.ptr.get() = (ptr as usize + size) as *mut _;
|
||||
*self.ptr.get() = ptr.add(aligned_size) as *mut _;
|
||||
ptr
|
||||
} else {
|
||||
ptr::null_mut()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user