Merge pull request #2309 from coasys/library-use-case
Iron-out edge cases for library use-case, adding extensive real-world test assertions
This commit is contained in:
11
.github/workflows/ci.yml
vendored
11
.github/workflows/ci.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
|||||||
- name: Setup Rust
|
- name: Setup Rust
|
||||||
uses: ./.github/actions/setup-rust
|
uses: ./.github/actions/setup-rust
|
||||||
with:
|
with:
|
||||||
rust-version: nightly
|
rust-version: stable
|
||||||
targets: x86_64-unknown-linux-gnu
|
targets: x86_64-unknown-linux-gnu
|
||||||
components: clippy, rustfmt
|
components: clippy, rustfmt
|
||||||
cache-context: style
|
cache-context: style
|
||||||
@@ -44,7 +44,8 @@ jobs:
|
|||||||
# architectures
|
# architectures
|
||||||
- { os: ubuntu-22.04, rust-version: stable, target: 'x86_64-unknown-linux-gnu', publish: true }
|
- { os: ubuntu-22.04, rust-version: stable, target: 'x86_64-unknown-linux-gnu', publish: true }
|
||||||
- { os: ubuntu-22.04, rust-version: stable, target: 'i686-unknown-linux-gnu', publish: true }
|
- { os: ubuntu-22.04, rust-version: stable, target: 'i686-unknown-linux-gnu', publish: true }
|
||||||
- { os: ubuntu-22.04, rust-version: nightly, target: 'wasm32-unknown-unknown', publish: true, args: '--no-default-features' }
|
# 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' }
|
||||||
# rust versions
|
# rust versions
|
||||||
- { os: ubuntu-22.04, rust-version: "1.70", target: 'x86_64-unknown-linux-gnu'}
|
- { 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: beta, target: 'x86_64-unknown-linux-gnu'}
|
||||||
@@ -63,11 +64,9 @@ jobs:
|
|||||||
|
|
||||||
# Build and test.
|
# Build and test.
|
||||||
- name: Build library
|
- name: Build library
|
||||||
continue-on-error: ${{ contains(matrix.target,'wasm32') }} # allow wasm builds to fail tests for now
|
|
||||||
run: cargo build --all-targets --target ${{ matrix.target }} ${{ matrix.args }} --verbose
|
run: cargo build --all-targets --target ${{ matrix.target }} ${{ matrix.args }} --verbose
|
||||||
- name: Test
|
- name: Test
|
||||||
continue-on-error: ${{ contains(matrix.target,'wasm32') }} # allow wasm builds to fail tests for now
|
run: cargo test --target ${{ matrix.target }} ${{ matrix.test-args }} --all
|
||||||
run: cargo test --target ${{ matrix.target }} ${{ matrix.args }} --all
|
|
||||||
|
|
||||||
# On stable rust builds, build a binary and publish as a github actions
|
# On stable rust builds, build a binary and publish as a github actions
|
||||||
# artifact. These binaries could be useful for testing the pipeline but
|
# artifact. These binaries could be useful for testing the pipeline but
|
||||||
@@ -138,7 +137,7 @@ jobs:
|
|||||||
- name: Setup Rust
|
- name: Setup Rust
|
||||||
uses: ./.github/actions/setup-rust
|
uses: ./.github/actions/setup-rust
|
||||||
with:
|
with:
|
||||||
rust-version: nightly
|
rust-version: stable
|
||||||
targets: x86_64-unknown-linux-gnu
|
targets: x86_64-unknown-linux-gnu
|
||||||
cache-context: report
|
cache-context: report
|
||||||
- run: |
|
- run: |
|
||||||
|
|||||||
6
Cargo.lock
generated
6
Cargo.lock
generated
@@ -19,9 +19,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ahash"
|
name = "ahash"
|
||||||
version = "0.8.6"
|
version = "0.8.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a"
|
checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"getrandom",
|
"getrandom",
|
||||||
@@ -2602,7 +2602,9 @@ dependencies = [
|
|||||||
"rustyline",
|
"rustyline",
|
||||||
"ryu",
|
"ryu",
|
||||||
"select",
|
"select",
|
||||||
|
"serde",
|
||||||
"serde-wasm-bindgen",
|
"serde-wasm-bindgen",
|
||||||
|
"serde_json",
|
||||||
"serial_test",
|
"serial_test",
|
||||||
"sha3 0.8.2",
|
"sha3 0.8.2",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
|
|||||||
60
Cargo.toml
60
Cargo.toml
@@ -37,52 +37,55 @@ to-syn-value_derive = "0.1.1"
|
|||||||
walkdir = "2"
|
walkdir = "2"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
base64 = "0.12.3"
|
||||||
bit-set = "0.5.3"
|
bit-set = "0.5.3"
|
||||||
bitvec = "1"
|
bitvec = "1"
|
||||||
|
blake2 = "0.8.1"
|
||||||
|
bytes = "1"
|
||||||
|
chrono = "0.4.11"
|
||||||
cpu-time = "1.0.0"
|
cpu-time = "1.0.0"
|
||||||
|
crrl = "0.6.0"
|
||||||
|
dashu = "0.4.0"
|
||||||
|
derive_deref = "1.1.1"
|
||||||
dirs-next = "2.0.0"
|
dirs-next = "2.0.0"
|
||||||
divrem = "0.1.0"
|
divrem = "0.1.0"
|
||||||
|
futures = "0.3"
|
||||||
fxhash = "0.2.1"
|
fxhash = "0.2.1"
|
||||||
git-version = "0.3.4"
|
git-version = "0.3.4"
|
||||||
indexmap = "1.0.2"
|
indexmap = "1.0.2"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
lexical = "5.2.2"
|
lexical = "5.2.2"
|
||||||
libc = "0.2.62"
|
libc = "0.2.62"
|
||||||
|
libloading = "0.7"
|
||||||
modular-bitfield = "0.11.2"
|
modular-bitfield = "0.11.2"
|
||||||
|
num-order = { version = "1.2.0" }
|
||||||
ordered-float = "2.6.0"
|
ordered-float = "2.6.0"
|
||||||
phf = { version = "0.9", features = ["macros"] }
|
phf = { version = "0.9", features = ["macros"] }
|
||||||
|
rand = "0.8.5"
|
||||||
ref_thread_local = "0.0.0"
|
ref_thread_local = "0.0.0"
|
||||||
|
regex = "1.9.1"
|
||||||
|
ring = { version = "0.17.5", features = ["wasm32_unknown_unknown_js"] }
|
||||||
ripemd160 = "0.8.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"
|
roxmltree = "0.11.0"
|
||||||
base64 = "0.12.3"
|
ryu = "1.0.9"
|
||||||
|
select = "0.6.0"
|
||||||
|
sha3 = "0.8.2"
|
||||||
smallvec = "1.8.0"
|
smallvec = "1.8.0"
|
||||||
static_assertions = "1.1.0"
|
static_assertions = "1.1.0"
|
||||||
ryu = "1.0.9"
|
|
||||||
futures = "0.3"
|
serde_json = "1.0.95"
|
||||||
regex = "1.9.1"
|
serde = "1.0.159"
|
||||||
libloading = "0.7"
|
|
||||||
derive_deref = "1.1.1"
|
|
||||||
bytes = "1"
|
|
||||||
dashu = "0.4.0"
|
|
||||||
num-order = { version = "1.2.0" }
|
|
||||||
rand = "0.8.5"
|
|
||||||
ring = { version = "0.17.5", features = ["wasm32_unknown_unknown_js"] }
|
|
||||||
|
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
libffi = { version = "3.2.0", optional = true }
|
|
||||||
hostname = { version = "0.3.1", optional = true }
|
|
||||||
crossterm = { version = "0.20.0", optional = true }
|
crossterm = { version = "0.20.0", optional = true }
|
||||||
ctrlc = { version = "3.2.2", optional = true }
|
ctrlc = { version = "3.2.2", optional = true }
|
||||||
rustyline = { version = "12.0.0", optional = true }
|
hostname = { version = "0.3.1", optional = true }
|
||||||
|
libffi = { version = "3.2.0", optional = true }
|
||||||
native-tls = { version = "0.2.4", optional = true }
|
native-tls = { version = "0.2.4", optional = true }
|
||||||
warp = { version = "=0.3.5", features = ["tls"], optional = true }
|
|
||||||
reqwest = { version = "0.11.18", optional = true }
|
reqwest = { version = "0.11.18", optional = true }
|
||||||
|
rustyline = { version = "12.0.0", optional = true }
|
||||||
tokio = { version = "1.28.2", features = ["full"] }
|
tokio = { version = "1.28.2", features = ["full"] }
|
||||||
|
warp = { version = "=0.3.5", features = ["tls"], optional = true }
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
getrandom = { version = "0.2.10", features = ["js"] }
|
getrandom = { version = "0.2.10", features = ["js"] }
|
||||||
@@ -99,19 +102,26 @@ console_error_panic_hook = "0.1"
|
|||||||
wasm-bindgen = "0.2.87"
|
wasm-bindgen = "0.2.87"
|
||||||
wasm-bindgen-futures = "0.4"
|
wasm-bindgen-futures = "0.4"
|
||||||
serde-wasm-bindgen = "0.5"
|
serde-wasm-bindgen = "0.5"
|
||||||
web-sys = { version = "0.3", features = ["Document", "Window", "Element", "Performance"] }
|
web-sys = { version = "0.3", features = [
|
||||||
|
"Document",
|
||||||
|
"Window",
|
||||||
|
"Element",
|
||||||
|
"Performance",
|
||||||
|
] }
|
||||||
js-sys = "0.3"
|
js-sys = "0.3"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
assert_cmd = "1.0.3"
|
|
||||||
predicates-core = "1.0.2"
|
|
||||||
maplit = "1.0.2"
|
maplit = "1.0.2"
|
||||||
|
predicates-core = "1.0.2"
|
||||||
serial_test = "2.0.0"
|
serial_test = "2.0.0"
|
||||||
iai-callgrind = { git = "https://github.com/iai-callgrind/iai-callgrind.git", rev = "c77bc3c83d7f4e976cc42d4597236a8db259e772" }
|
|
||||||
|
[target.'cfg(not(all(target_arch = "wasm32", target_os = "unknown")))'.dev-dependencies]
|
||||||
|
assert_cmd = "1.0.3"
|
||||||
criterion = "0.5.1"
|
criterion = "0.5.1"
|
||||||
|
iai-callgrind = { git = "https://github.com/iai-callgrind/iai-callgrind.git", rev = "c77bc3c83d7f4e976cc42d4597236a8db259e772" }
|
||||||
trycmd = "0.14.19"
|
trycmd = "0.14.19"
|
||||||
|
|
||||||
[target.'cfg(not(target_os = "windows"))'.dev-dependencies]
|
[target.'cfg(not(any(target_os = "windows", all(target_arch = "wasm32", target_os = "unknown"))))'.dev-dependencies]
|
||||||
pprof = { version = "0.13.0", features = ["criterion", "flamegraph"] }
|
pprof = { version = "0.13.0", features = ["criterion", "flamegraph"] }
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
|
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||||
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
||||||
|
|
||||||
|
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||||
#[cfg(not(target_os = "windows"))]
|
#[cfg(not(target_os = "windows"))]
|
||||||
use pprof::criterion::{Output, PProfProfiler};
|
use pprof::criterion::{Output, PProfProfiler};
|
||||||
|
|
||||||
|
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||||
mod setup;
|
mod setup;
|
||||||
|
|
||||||
|
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||||
fn bench_criterion(c: &mut Criterion) {
|
fn bench_criterion(c: &mut Criterion) {
|
||||||
for (&name, bench) in setup::prolog_benches().iter() {
|
for (&name, bench) in setup::prolog_benches().iter() {
|
||||||
match bench.strategy {
|
match bench.strategy {
|
||||||
@@ -15,7 +19,7 @@ fn bench_criterion(c: &mut Criterion) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||||
#[cfg(not(target_os = "windows"))]
|
#[cfg(not(target_os = "windows"))]
|
||||||
fn config() -> Criterion {
|
fn config() -> Criterion {
|
||||||
Criterion::default()
|
Criterion::default()
|
||||||
@@ -28,9 +32,15 @@ fn config() -> Criterion {
|
|||||||
Criterion::default().sample_size(20)
|
Criterion::default().sample_size(20)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||||
criterion_group!(
|
criterion_group!(
|
||||||
name = benches;
|
name = benches;
|
||||||
config = config();
|
config = config();
|
||||||
targets = bench_criterion
|
targets = bench_criterion
|
||||||
);
|
);
|
||||||
|
|
||||||
|
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||||
criterion_main!(benches);
|
criterion_main!(benches);
|
||||||
|
|
||||||
|
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
|
||||||
|
fn main() {}
|
||||||
|
|||||||
@@ -1,18 +1,38 @@
|
|||||||
use iai_callgrind::{library_benchmark, library_benchmark_group, main};
|
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||||
use scryer_prolog::machine::parsed_results::QueryResolution;
|
|
||||||
|
|
||||||
mod setup;
|
mod setup;
|
||||||
|
|
||||||
#[library_benchmark]
|
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||||
#[bench::count_edges(setup::prolog_benches()["count_edges"].setup())]
|
mod iai {
|
||||||
#[bench::numlist(setup::prolog_benches()["numlist"].setup())]
|
use iai_callgrind::{library_benchmark, library_benchmark_group, main};
|
||||||
#[bench::csv_codename(setup::prolog_benches()["csv_codename"].setup())]
|
|
||||||
fn bench(mut run: impl FnMut() -> QueryResolution) -> QueryResolution {
|
use scryer_prolog::machine::parsed_results::QueryResolution;
|
||||||
run()
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
library_benchmark_group!(
|
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||||
name = benches;
|
fn main() {
|
||||||
benchmarks = bench
|
iai::call_main();
|
||||||
);
|
}
|
||||||
main!(library_benchmark_groups = benches);
|
|
||||||
|
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
|
||||||
|
fn main() {}
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ impl PrologBenchmark {
|
|||||||
machine
|
machine
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||||
pub fn setup(&self) -> impl FnMut() -> QueryResolution {
|
pub fn setup(&self) -> impl FnMut() -> QueryResolution {
|
||||||
let mut machine = self.make_machine();
|
let mut machine = self.make_machine();
|
||||||
let query = self.query;
|
let query = self.query;
|
||||||
|
|||||||
@@ -1157,27 +1157,27 @@ fn generate_instruction_preface() -> TokenStream {
|
|||||||
impl Instruction {
|
impl Instruction {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn registers(&self) -> Vec<RegType> {
|
pub fn registers(&self) -> Vec<RegType> {
|
||||||
match self {
|
match *self {
|
||||||
&Instruction::GetConstant(_, _, r) => vec![r],
|
Instruction::GetConstant(_, _, r) => vec![r],
|
||||||
&Instruction::GetList(_, r) => vec![r],
|
Instruction::GetList(_, r) => vec![r],
|
||||||
&Instruction::GetPartialString(_, _, r, _) => vec![r],
|
Instruction::GetPartialString(_, _, r, _) => vec![r],
|
||||||
&Instruction::GetStructure(_, _, _, r) => vec![r],
|
Instruction::GetStructure(_, _, _, r) => vec![r],
|
||||||
&Instruction::GetVariable(r, t) => vec![r, temp_v!(t)],
|
Instruction::GetVariable(r, t) => vec![r, temp_v!(t)],
|
||||||
&Instruction::GetValue(r, t) => vec![r, temp_v!(t)],
|
Instruction::GetValue(r, t) => vec![r, temp_v!(t)],
|
||||||
&Instruction::UnifyLocalValue(r) => vec![r],
|
Instruction::UnifyLocalValue(r) => vec![r],
|
||||||
&Instruction::UnifyVariable(r) => vec![r],
|
Instruction::UnifyVariable(r) => vec![r],
|
||||||
&Instruction::PutConstant(_, _, r) => vec![r],
|
Instruction::PutConstant(_, _, r) => vec![r],
|
||||||
&Instruction::PutList(_, r) => vec![r],
|
Instruction::PutList(_, r) => vec![r],
|
||||||
&Instruction::PutPartialString(_, _, r, _) => vec![r],
|
Instruction::PutPartialString(_, _, r, _) => vec![r],
|
||||||
&Instruction::PutStructure(_, _, r) => vec![r],
|
Instruction::PutStructure(_, _, r) => vec![r],
|
||||||
&Instruction::PutValue(r, t) => vec![r, temp_v!(t)],
|
Instruction::PutValue(r, t) => vec![r, temp_v!(t)],
|
||||||
&Instruction::PutVariable(r, t) => vec![r, temp_v!(t)],
|
Instruction::PutVariable(r, t) => vec![r, temp_v!(t)],
|
||||||
&Instruction::SetLocalValue(r) => vec![r],
|
Instruction::SetLocalValue(r) => vec![r],
|
||||||
&Instruction::SetVariable(r) => vec![r],
|
Instruction::SetVariable(r) => vec![r],
|
||||||
&Instruction::SetValue(r) => vec![r],
|
Instruction::SetValue(r) => vec![r],
|
||||||
&Instruction::GetLevel(r) => vec![r],
|
Instruction::GetLevel(r) => vec![r],
|
||||||
&Instruction::GetPrevLevel(r) => vec![r],
|
Instruction::GetPrevLevel(r) => vec![r],
|
||||||
&Instruction::GetCutPoint(r) => vec![r],
|
Instruction::GetCutPoint(r) => vec![r],
|
||||||
_ => vec![],
|
_ => vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
fn main() -> std::process::ExitCode {
|
fn main() -> std::process::ExitCode {
|
||||||
use scryer_prolog::atom_table::Atom;
|
use scryer_prolog::atom_table::Atom;
|
||||||
use scryer_prolog::*;
|
use scryer_prolog::*;
|
||||||
use std::sync::atomic::Ordering;
|
|
||||||
|
|
||||||
#[cfg(feature = "repl")]
|
#[cfg(feature = "repl")]
|
||||||
ctrlc::set_handler(move || {
|
ctrlc::set_handler(move || {
|
||||||
scryer_prolog::machine::INTERRUPT.store(true, Ordering::Relaxed);
|
scryer_prolog::machine::INTERRUPT.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|||||||
31
src/ffi.rs
31
src/ffi.rs
@@ -27,6 +27,7 @@ use std::collections::HashMap;
|
|||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::ffi::{c_void, CString};
|
use std::ffi::{c_void, CString};
|
||||||
|
use std::ptr::addr_of_mut;
|
||||||
|
|
||||||
use libffi::low::type_tag::STRUCT;
|
use libffi::low::type_tag::STRUCT;
|
||||||
use libffi::low::{ffi_abi_FFI_DEFAULT_ABI, ffi_cif, ffi_type, prep_cif, types, CodePtr};
|
use libffi::low::{ffi_abi_FFI_DEFAULT_ABI, ffi_cif, ffi_type, prep_cif, types, CodePtr};
|
||||||
@@ -90,20 +91,20 @@ impl ForeignFunctionTable {
|
|||||||
fn map_type_ffi(&mut self, source: &Atom) -> *mut ffi_type {
|
fn map_type_ffi(&mut self, source: &Atom) -> *mut ffi_type {
|
||||||
unsafe {
|
unsafe {
|
||||||
match source {
|
match source {
|
||||||
atom!("sint64") => &mut types::sint64,
|
atom!("sint64") => addr_of_mut!(types::sint64),
|
||||||
atom!("sint32") => &mut types::sint32,
|
atom!("sint32") => addr_of_mut!(types::sint32),
|
||||||
atom!("sint16") => &mut types::sint16,
|
atom!("sint16") => addr_of_mut!(types::sint16),
|
||||||
atom!("sint8") => &mut types::sint8,
|
atom!("sint8") => addr_of_mut!(types::sint8),
|
||||||
atom!("uint64") => &mut types::uint64,
|
atom!("uint64") => addr_of_mut!(types::uint64),
|
||||||
atom!("uint32") => &mut types::uint32,
|
atom!("uint32") => addr_of_mut!(types::uint32),
|
||||||
atom!("uint16") => &mut types::uint16,
|
atom!("uint16") => addr_of_mut!(types::uint16),
|
||||||
atom!("uint8") => &mut types::uint8,
|
atom!("uint8") => addr_of_mut!(types::uint8),
|
||||||
atom!("bool") => &mut types::sint8,
|
atom!("bool") => addr_of_mut!(types::sint8),
|
||||||
atom!("void") => &mut types::void,
|
atom!("void") => addr_of_mut!(types::void),
|
||||||
atom!("cstr") => &mut types::pointer,
|
atom!("cstr") => addr_of_mut!(types::pointer),
|
||||||
atom!("ptr") => &mut types::pointer,
|
atom!("ptr") => addr_of_mut!(types::pointer),
|
||||||
atom!("f32") => &mut types::float,
|
atom!("f32") => addr_of_mut!(types::float),
|
||||||
atom!("f64") => &mut types::double,
|
atom!("f64") => addr_of_mut!(types::double),
|
||||||
struct_name => match self.structs.get_mut(&*struct_name.as_str()) {
|
struct_name => match self.structs.get_mut(&*struct_name.as_str()) {
|
||||||
Some(ref mut struct_type) => &mut struct_type.ffi_type,
|
Some(ref mut struct_type) => &mut struct_type.ffi_type,
|
||||||
None => unreachable!(),
|
None => unreachable!(),
|
||||||
@@ -161,7 +162,7 @@ impl ForeignFunctionTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn build_pointer_args(
|
fn build_pointer_args(
|
||||||
args: &mut Vec<Value>,
|
args: &mut [Value],
|
||||||
type_args: &[*mut ffi_type],
|
type_args: &[*mut ffi_type],
|
||||||
structs_table: &mut HashMap<String, StructImpl>,
|
structs_table: &mut HashMap<String, StructImpl>,
|
||||||
) -> Result<PointerArgs, FFIError> {
|
) -> Result<PointerArgs, FFIError> {
|
||||||
|
|||||||
@@ -1727,7 +1727,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
|||||||
self.print_stream(stream, max_depth);
|
self.print_stream(stream, max_depth);
|
||||||
}
|
}
|
||||||
(ArenaHeaderTag::TcpListener, listener) => {
|
(ArenaHeaderTag::TcpListener, listener) => {
|
||||||
self.print_tcp_listener(&*listener, max_depth);
|
self.print_tcp_listener(&listener, max_depth);
|
||||||
}
|
}
|
||||||
(ArenaHeaderTag::Dropped, _value) => {
|
(ArenaHeaderTag::Dropped, _value) => {
|
||||||
self.print_impromptu_atom(atom!("$dropped_value"));
|
self.print_impromptu_atom(atom!("$dropped_value"));
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -39,10 +39,10 @@ impl Machine {
|
|||||||
|
|
||||||
fn allocate_stub_choice_point(&mut self) {
|
fn allocate_stub_choice_point(&mut self) {
|
||||||
// NOTE: create a choice point to terminate the dispatch_loop
|
// 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 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.num_cells = 0;
|
||||||
or_frame.prelude.e = 0;
|
or_frame.prelude.e = 0;
|
||||||
@@ -58,6 +58,7 @@ impl Machine {
|
|||||||
|
|
||||||
self.machine_st.b = stub_b;
|
self.machine_st.b = stub_b;
|
||||||
self.machine_st.hb = self.machine_st.heap.len();
|
self.machine_st.hb = self.machine_st.heap.len();
|
||||||
|
self.machine_st.block = stub_b;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_query(&mut self, query: String) -> QueryResult {
|
pub fn run_query(&mut self, query: String) -> QueryResult {
|
||||||
@@ -72,24 +73,13 @@ impl Machine {
|
|||||||
.read_term(&op_dir, Tokens::Default)
|
.read_term(&op_dir, Tokens::Default)
|
||||||
.expect("Failed to parse query");
|
.expect("Failed to parse query");
|
||||||
|
|
||||||
|
self.allocate_stub_choice_point();
|
||||||
|
|
||||||
// Write parsed term to heap
|
// Write parsed term to heap
|
||||||
let term_write_result =
|
let term_write_result =
|
||||||
write_term_to_heap(&term, &mut self.machine_st.heap, &self.machine_st.atom_tbl)
|
write_term_to_heap(&term, &mut self.machine_st.heap, &self.machine_st.atom_tbl)
|
||||||
.expect("couldn't write term to heap");
|
.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();
|
|
||||||
self.machine_st.b0 = self.machine_st.b;
|
|
||||||
|
|
||||||
let var_names: IndexMap<_, _> = term_write_result
|
let var_names: IndexMap<_, _> = term_write_result
|
||||||
.var_dict
|
.var_dict
|
||||||
.iter()
|
.iter()
|
||||||
@@ -102,7 +92,19 @@ impl Machine {
|
|||||||
})
|
})
|
||||||
.collect();
|
.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 stub_b = self.machine_st.b;
|
||||||
|
|
||||||
@@ -156,17 +158,17 @@ impl Machine {
|
|||||||
};
|
};
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if term_write_result.var_dict.is_empty() {
|
if self.machine_st.p == LIB_QUERY_SUCCESS {
|
||||||
if self.machine_st.p == LIB_QUERY_SUCCESS {
|
if term_write_result.var_dict.is_empty() {
|
||||||
matches.push(QueryResolutionLine::True);
|
matches.push(QueryResolutionLine::True);
|
||||||
break;
|
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;
|
|
||||||
}
|
}
|
||||||
|
} 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();
|
let mut bindings: BTreeMap<String, Value> = BTreeMap::new();
|
||||||
@@ -462,6 +464,7 @@ mod tests {
|
|||||||
let blocks = code.split("=====");
|
let blocks = code.split("=====");
|
||||||
|
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
|
let mut last_result: Option<_> = None;
|
||||||
// Iterate over the blocks
|
// Iterate over the blocks
|
||||||
for block in blocks {
|
for block in blocks {
|
||||||
// Trim the block to remove any leading or trailing whitespace
|
// Trim the block to remove any leading or trailing whitespace
|
||||||
@@ -474,20 +477,24 @@ mod tests {
|
|||||||
|
|
||||||
// Check if the block is a query
|
// Check if the block is a query
|
||||||
if let Some(query) = block.strip_prefix("query") {
|
if let Some(query) = block.strip_prefix("query") {
|
||||||
i += 1;
|
|
||||||
println!("query #{}: {}", i, query);
|
|
||||||
// Parse and execute the query
|
// Parse and execute the query
|
||||||
let result = machine.run_query(query.to_string());
|
let result = machine.run_query(query.to_string());
|
||||||
|
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
|
|
||||||
// Print the result
|
last_result = Some(result);
|
||||||
println!("{:?}", result);
|
|
||||||
} else if let Some(code) = block.strip_prefix("consult") {
|
} else if let Some(code) = block.strip_prefix("consult") {
|
||||||
println!("load code: {}", code);
|
|
||||||
|
|
||||||
// Load the code into the machine
|
// Load the code into the machine
|
||||||
machine.consult_module_string("facts", code.to_string());
|
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. {}=======\n{}\n===============",
|
||||||
|
i,
|
||||||
|
last_result.to_string().trim()
|
||||||
|
);
|
||||||
|
assert_eq!(last_result.to_string().trim(), result.to_string().trim(),)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -524,4 +531,82 @@ mod tests {
|
|||||||
),]))
|
),]))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
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]
|
||||||
|
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]
|
||||||
|
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"))
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1022,19 +1022,6 @@ pub enum SessionError {
|
|||||||
QueryCannotBeDefinedAsFact,
|
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 {
|
impl From<std::io::Error> for SessionError {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(err: std::io::Error) -> SessionError {
|
fn from(err: std::io::Error) -> SessionError {
|
||||||
@@ -1055,10 +1042,3 @@ impl From<CompilationError> for SessionError {
|
|||||||
SessionError::CompilationError(err)
|
SessionError::CompilationError(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ParserError> for EvalSession {
|
|
||||||
#[inline]
|
|
||||||
fn from(err: ParserError) -> Self {
|
|
||||||
EvalSession::from(SessionError::from(err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -13,6 +13,84 @@ pub enum QueryResolution {
|
|||||||
Matches(Vec<QueryMatch>),
|
Matches(Vec<QueryMatch>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn prolog_value_to_json_string(value: Value) -> String {
|
||||||
|
match value {
|
||||||
|
Value::Integer(i) => format!("{}", i),
|
||||||
|
Value::Float(f) => format!("{}", f),
|
||||||
|
Value::Rational(r) => format!("{}", r),
|
||||||
|
Value::Atom(a) => format!("{}", 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
|
||||||
|
format!(
|
||||||
|
"\"{}\"",
|
||||||
|
s.replace('\"', "\\\"")
|
||||||
|
.replace('\n', "\\n")
|
||||||
|
.replace('\t', "\\t")
|
||||||
|
.replace('\r', "\\r")
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
//return valid json string
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Value::List(l) => {
|
||||||
|
let mut string_result = "[".to_string();
|
||||||
|
for (i, v) in l.iter().enumerate() {
|
||||||
|
if i > 0 {
|
||||||
|
string_result.push(',');
|
||||||
|
}
|
||||||
|
string_result.push_str(&prolog_value_to_json_string(v.clone()));
|
||||||
|
}
|
||||||
|
string_result.push(']');
|
||||||
|
string_result
|
||||||
|
}
|
||||||
|
Value::Structure(s, l) => {
|
||||||
|
let mut string_result = format!("\"{}\":[", s.as_str());
|
||||||
|
for (i, v) in l.iter().enumerate() {
|
||||||
|
if i > 0 {
|
||||||
|
string_result.push(',');
|
||||||
|
}
|
||||||
|
string_result.push_str(&prolog_value_to_json_string(v.clone()));
|
||||||
|
}
|
||||||
|
string_result.push(']');
|
||||||
|
string_result
|
||||||
|
}
|
||||||
|
_ => "null".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prolog_match_to_json_string(query_match: &QueryMatch) -> String {
|
||||||
|
let mut string_result = "{".to_string();
|
||||||
|
for (i, (k, v)) in query_match.bindings.iter().enumerate() {
|
||||||
|
if i > 0 {
|
||||||
|
string_result.push(',');
|
||||||
|
}
|
||||||
|
string_result.push_str(&format!(
|
||||||
|
"\"{}\":{}",
|
||||||
|
k,
|
||||||
|
prolog_value_to_json_string(v.clone())
|
||||||
|
));
|
||||||
|
}
|
||||||
|
string_result.push('}');
|
||||||
|
string_result
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToString for QueryResolution {
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
match self {
|
||||||
|
QueryResolution::True => "true".to_string(),
|
||||||
|
QueryResolution::False => "false".to_string(),
|
||||||
|
QueryResolution::Matches(matches) => {
|
||||||
|
let matches_json: Vec<String> =
|
||||||
|
matches.iter().map(prolog_match_to_json_string).collect();
|
||||||
|
format!("[{}]", matches_json.join(","))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct QueryMatch {
|
pub struct QueryMatch {
|
||||||
pub bindings: BTreeMap<String, Value>,
|
pub bindings: BTreeMap<String, Value>,
|
||||||
|
|||||||
@@ -229,7 +229,7 @@ impl<R: Read> CharRead for CharReader<R> {
|
|||||||
|
|
||||||
match self.inner.read(word_slice) {
|
match self.inner.read(word_slice) {
|
||||||
Err(e) => return Some(Err(e)),
|
Err(e) => return Some(Err(e)),
|
||||||
Ok(nread) if nread == 0 => return Some(Err(bad_bytes_error(&self.buf))),
|
Ok(0) => return Some(Err(bad_bytes_error(&self.buf))),
|
||||||
Ok(nread) => {
|
Ok(nread) => {
|
||||||
self.buf.extend_from_slice(&word_slice[0..nread]);
|
self.buf.extend_from_slice(&word_slice[0..nread]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ mod src_tests;
|
|||||||
/// to re-generate all reference output files run `TRYCMD=overwrite cargo test -- cli_test`
|
/// to re-generate all reference output files run `TRYCMD=overwrite cargo test -- cli_test`
|
||||||
/// then check that the changes are as expected e.g. by looking at the `git diff`
|
/// then check that the changes are as expected e.g. by looking at the `git diff`
|
||||||
#[test]
|
#[test]
|
||||||
|
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||||
#[cfg_attr(miri, ignore = "blocked on crossbeam UB")]
|
#[cfg_attr(miri, ignore = "blocked on crossbeam UB")]
|
||||||
fn cli_tests() {
|
fn cli_tests() {
|
||||||
trycmd::TestCases::new()
|
trycmd::TestCases::new()
|
||||||
|
|||||||
Reference in New Issue
Block a user