Skip to content

Commit

Permalink
Merge pull request #2582 from bakaq/rework_library_interface
Browse files Browse the repository at this point in the history
Rework library interface
  • Loading branch information
mthom authored Dec 14, 2024
2 parents b5030be + 2e910de commit 7e22c12
Show file tree
Hide file tree
Showing 12 changed files with 1,452 additions and 1,632 deletions.
4 changes: 2 additions & 2 deletions benches/run_iai.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ mod setup;
mod iai {
use iai_callgrind::{library_benchmark, library_benchmark_group, main};

use scryer_prolog::QueryResolution;
use scryer_prolog::LeafAnswer;

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 {
fn bench(mut run: impl FnMut() -> Vec<LeafAnswer>) -> Vec<LeafAnswer> {
run()
}

Expand Down
31 changes: 20 additions & 11 deletions benches/setup.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::{collections::BTreeMap, fs, path::Path};

use maplit::btreemap;
use scryer_prolog::{Machine, QueryResolution, Value};
use scryer_prolog::{LeafAnswer, Machine, MachineBuilder, Term};

pub fn prolog_benches() -> BTreeMap<&'static str, PrologBenchmark> {
[
Expand All @@ -10,21 +10,21 @@ pub fn prolog_benches() -> BTreeMap<&'static str, PrologBenchmark> {
"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()) },
btreemap! { "Count" => Term::integer(2869176) },
),
(
"numlist",
"benches/numlist.pl",
"run_numlist(1000000, Head).",
Strategy::Reuse,
btreemap! { "Head" => Value::Integer(1.into())},
btreemap! { "Head" => Term::integer(1) },
),
(
"csv_codename",
"benches/csv.pl",
"get_codename(\"0020\",Name).",
Strategy::Reuse,
btreemap! { "Name" => Value::String("SPACE".into())},
btreemap! { "Name" => Term::string("SPACE") },
),
]
.map(|b| {
Expand Down Expand Up @@ -54,7 +54,7 @@ pub struct PrologBenchmark {
pub filename: &'static str,
pub query: &'static str,
pub strategy: Strategy,
pub bindings: BTreeMap<&'static str, Value>,
pub bindings: BTreeMap<&'static str, Term>,
}

impl PrologBenchmark {
Expand All @@ -64,28 +64,34 @@ impl PrologBenchmark {
.file_stem()
.and_then(|s| s.to_str())
.unwrap();
let mut machine = Machine::new_lib();
let mut machine = MachineBuilder::default().build();
machine.load_module_string(module_name, program);
machine
}

#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
pub fn setup(&self) -> impl FnMut() -> QueryResolution {
pub fn setup(&self) -> impl FnMut() -> Vec<LeafAnswer> {
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()
black_box(
machine
.run_query(black_box(query))
.collect::<Result<Vec<_>, _>>()
.unwrap(),
)
}
}
}

#[cfg(test)]
mod test {

#[test]
fn validate_benchmarks() {
use super::prolog_benches;
use scryer_prolog::{QueryMatch, QueryResolution};
use scryer_prolog::LeafAnswer;
use std::{fmt::Write, fs};

struct BenchResult {
Expand All @@ -100,10 +106,13 @@ mod test {
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 result: Vec<_> = machine
.run_query(r.query)
.collect::<Result<_, _>>()
.unwrap();
let query_inference_count = machine.get_inference_count() - setup_inference_count;

let expected = QueryResolution::Matches(vec![QueryMatch::from(r.bindings.clone())]);
let expected = [LeafAnswer::from_bindings(r.bindings.clone())];
assert_eq!(result, expected, "validating benchmark {}", r.name);

results.push(BenchResult {
Expand Down
16 changes: 9 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
//! A free software ISO Prolog system.
#![recursion_limit = "4112"]
#![deny(missing_docs)]

#[macro_use]
extern crate static_assertions;
#[cfg(test)]
#[macro_use]
extern crate maplit;

#[macro_use]
pub(crate) mod macros;
Expand Down Expand Up @@ -46,24 +45,25 @@ use wasm_bindgen::prelude::*;
// Re-exports
pub use machine::config::*;
pub use machine::lib_machine::*;
pub use machine::parsed_results::*;
pub use machine::Machine;

/// Eval a source file in Wasm.
#[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 mut wam = MachineBuilder::default().build();
let bytes = wam.test_load_string(s);
String::from_utf8_lossy(&bytes).to_string()
}

/// The entry point for the Scryer Prolog CLI.
pub fn run_binary() -> std::process::ExitCode {
use crate::atom_table::Atom;
use crate::machine::{Machine, INTERRUPT};
use crate::machine::INTERRUPT;

#[cfg(feature = "repl")]
ctrlc::set_handler(move || {
Expand All @@ -84,7 +84,9 @@ pub fn run_binary() -> std::process::ExitCode {
.unwrap();

runtime.block_on(async move {
let mut wam = Machine::new(Default::default());
let mut wam = MachineBuilder::default()
.with_streams(StreamConfig::stdio())
.build();
wam.run_module_predicate(atom!("$toplevel"), (atom!("$repl"), 0))
})
}
189 changes: 173 additions & 16 deletions src/machine/config.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,189 @@
pub struct MachineConfig {
pub streams: StreamConfig,
pub toplevel: &'static str,
use std::borrow::Cow;

use rand::{rngs::StdRng, SeedableRng};

use crate::Machine;

use super::{
bootstrapping_compile, current_dir, import_builtin_impls, libraries, load_module, Atom,
CompilationTarget, IndexStore, ListingSource, MachineArgs, MachineState, Stream, StreamOptions,
};

/// Describes how the streams of a [`Machine`](crate::Machine) will be handled.
#[derive(Default)]
pub struct StreamConfig {
inner: StreamConfigInner,
}

impl StreamConfig {
/// Binds the input, output and error streams to stdin, stdout and stderr.
pub fn stdio() -> Self {
StreamConfig {
inner: StreamConfigInner::Stdio,
}
}

/// Binds the output stream to a memory buffer, and the error stream to stderr.
///
/// The input stream is ignored.
pub fn in_memory() -> Self {
StreamConfig {
inner: StreamConfigInner::Memory,
}
}
}

pub enum StreamConfig {
#[derive(Default)]
enum StreamConfigInner {
Stdio,
#[default]
Memory,
}

impl Default for MachineConfig {
/// Describes how a [`Machine`](crate::Machine) will be configured.
pub struct MachineBuilder {
pub(crate) streams: StreamConfig,
pub(crate) toplevel: Cow<'static, str>,
}

impl Default for MachineBuilder {
/// Defaults to using in-memory streams.
fn default() -> Self {
MachineConfig {
streams: StreamConfig::Stdio,
toplevel: include_str!("../toplevel.pl"),
MachineBuilder {
streams: Default::default(),
toplevel: default_toplevel().into(),
}
}
}

impl MachineConfig {
pub fn in_memory() -> Self {
MachineConfig {
streams: StreamConfig::Memory,
..Default::default()
}
impl MachineBuilder {
/// Creates a default configuration.
pub fn new() -> Self {
Default::default()
}

pub fn with_toplevel(mut self, toplevel: &'static str) -> Self {
self.toplevel = toplevel;
/// Uses the given `crate::StreamConfig` in this configuration.
pub fn with_streams(mut self, streams: StreamConfig) -> Self {
self.streams = streams;
self
}

/// Uses the given toplevel in this configuration.
pub fn with_toplevel(mut self, toplevel: impl Into<Cow<'static, str>>) -> Self {
self.toplevel = toplevel.into();
self
}

/// Builds the [`Machine`](crate::Machine) from this configuration.
pub fn build(self) -> Machine {
let args = MachineArgs::new();
let mut machine_st = MachineState::new();

let (user_input, user_output, user_error) = match self.streams.inner {
StreamConfigInner::Stdio => (
Stream::stdin(&mut machine_st.arena, args.add_history),
Stream::stdout(&mut machine_st.arena),
Stream::stderr(&mut machine_st.arena),
),
StreamConfigInner::Memory => (
Stream::Null(StreamOptions::default()),
Stream::from_owned_string("".to_owned(), &mut machine_st.arena),
Stream::stderr(&mut machine_st.arena),
),
};

let mut wam = Machine {
machine_st,
indices: IndexStore::new(),
code: vec![],
user_input,
user_output,
user_error,
load_contexts: vec![],
#[cfg(feature = "ffi")]
foreign_function_table: Default::default(),
rng: StdRng::from_entropy(),
};

let mut lib_path = current_dir();

lib_path.pop();
lib_path.push("lib");

wam.add_impls_to_indices();

bootstrapping_compile(
Stream::from_static_string(
libraries::get("ops_and_meta_predicates")
.expect("library ops_and_meta_predicates should exist"),
&mut wam.machine_st.arena,
),
&mut wam,
ListingSource::from_file_and_path(
atom!("ops_and_meta_predicates.pl"),
lib_path.clone(),
),
)
.unwrap();

bootstrapping_compile(
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()),
)
.unwrap();

if let Some(builtins) = wam.indices.modules.get_mut(&atom!("builtins")) {
load_module(
&mut wam.machine_st,
&mut wam.indices.code_dir,
&mut wam.indices.op_dir,
&mut wam.indices.meta_predicates,
&CompilationTarget::User,
builtins,
);

import_builtin_impls(&wam.indices.code_dir, builtins);
} else {
unreachable!()
}

lib_path.pop(); // remove the "lib" at the end

bootstrapping_compile(
Stream::from_static_string(include_str!("../loader.pl"), &mut wam.machine_st.arena),
&mut wam,
ListingSource::from_file_and_path(atom!("loader.pl"), lib_path.clone()),
)
.unwrap();

wam.configure_modules();

if let Some(loader) = wam.indices.modules.get(&atom!("loader")) {
load_module(
&mut wam.machine_st,
&mut wam.indices.code_dir,
&mut wam.indices.op_dir,
&mut wam.indices.meta_predicates,
&CompilationTarget::User,
loader,
);
} else {
unreachable!()
}

wam.load_special_forms();
wam.load_top_level(self.toplevel);
wam.configure_streams();

wam
}
}

/// Returns a static string slice to the default toplevel
pub fn default_toplevel() -> &'static str {
include_str!("../toplevel.pl")
}
Loading

0 comments on commit 7e22c12

Please sign in to comment.