# It is not intended for manual editing.
version = 3
+ [[package]]
+ name = "addr2line"
+ version = "0.19.0"
+ source = "registry+https://github.com/rust-lang/crates.io-index"
+ checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97"
+ dependencies = [
+ "gimli",
+ ]
+
+ [[package]]
+ name = "adler"
+ version = "1.0.2"
+ source = "registry+https://github.com/rust-lang/crates.io-index"
+ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "aho-corasick"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41"
+dependencies = [
+ "memchr",
+]
+
+ [[package]]
+ name = "android-tzdata"
+ version = "0.1.1"
+ source = "registry+https://github.com/rust-lang/crates.io-index"
+ checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
+
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
+[[package]]
+name = "maplit"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
+
[[package]]
name = "markup5ever"
- version = "0.8.1"
+ version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
- checksum = "f1af46a727284117e09780d05038b1ce6fc9c76cc6df183c3dae5a8955a25e21"
+ checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016"
dependencies = [
"log",
- "phf 0.7.24",
+ "phf 0.10.1",
"phf_codegen",
- "serde",
- "serde_derive",
- "serde_json",
"string_cache",
"string_cache_codegen",
"tendril",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
+[[package]]
+name = "regex-automata"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2"
+
[[package]]
- name = "remove_dir_all"
- version = "0.5.3"
+ name = "reqwest"
+ version = "0.11.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
- checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
+ checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55"
dependencies = [
- "winapi",
+ "base64 0.21.2",
+ "bytes",
+ "encoding_rs",
+ "futures-core",
+ "futures-util",
+ "h2",
+ "http",
+ "http-body 0.4.5",
+ "hyper 0.14.27",
+ "hyper-tls",
+ "ipnet",
+ "js-sys",
+ "log",
+ "mime",
+ "native-tls",
+ "once_cell",
+ "percent-encoding",
+ "pin-project-lite",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
+ "tokio",
+ "tokio-native-tls",
+ "tower-service",
+ "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "winreg",
]
[[package]]
"lazy_static",
"lexical",
"libc",
+ "libffi",
+ "libloading",
+ "maplit",
"modular-bitfield",
"native-tls",
"ordered-float",
"phf 0.9.0",
"predicates-core",
- "proc-macro2 1.0.47",
- "quote 1.0.21",
+ "proc-macro2",
+ "quote",
+ "rand",
"ref_thread_local",
+ "regex",
+ "reqwest",
"ring",
"ripemd160",
"roxmltree",
sodiumoxide = "0.2.6"
static_assertions = "1.1.0"
ryu = "1.0.9"
- hyper = { version = "0.14", features = ["full"] }
- hyper-tls = "0.5.0"
- tokio = { version = "1", features = ["full"] }
+ hyper = { version = "1.0.0-rc.3", features = ["full"] }
+ tokio = { version = "1.28.2", features = ["full"] }
futures = "0.3"
+regex = "1.9.1"
+ libloading = "0.7"
+ derive_deref = "1.1.1"
+ http-body-util = "0.1.0-rc.2"
+ bytes = "1"
+ reqwest = { version = "0.11.18", features = ["blocking"] }
+ dashu = { git = "https://github.com/coasys/dashu.git" }
+ libffi = { git = "https://github.com/coasys/libffi-rs.git", branch = "windows-space" }
+ rand = "0.8.5"
[dev-dependencies]
assert_cmd = "1.0.3"
predicates-core = "1.0.2"
- serial_test = "0.5.1"
+maplit = "1.0.2"
+ serial_test = "2.0.0"
[patch.crates-io]
modular-bitfield = { git = "https://github.com/mthom/modular-bitfield" }
- fn main() {
+ fn main() -> std::process::ExitCode {
use std::sync::atomic::Ordering;
use scryer_prolog::*;
+ use scryer_prolog::atom_table::Atom;
ctrlc::set_handler(move || {
scryer_prolog::machine::INTERRUPT.store(true, Ordering::Relaxed);
}).unwrap();
- let mut wam = machine::Machine::new();
- wam.run_top_level()
+ 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));
- });
++ wam.run_top_level(atom!("$toplevel"), (atom!("$repl"), 1))
++ })
}
}
}
- return self.read_term(stream, indices)
+ if let Stream::Byte(_) = stream {
++ return self.read_term(
++ stream,
++ indices,
++ MachineState::read_term_from_user_input_eof_handler
++ )
+ }
+
unreachable!("Stream must be a Stream::Readline(_)")
}
pub mod machine_state;
pub mod machine_state_impl;
pub mod mock_wam;
+pub mod parsed_results;
pub mod partial_string;
+ pub mod disjuncts;
pub mod preprocessor;
pub mod stack;
pub mod streams;
pub(super) user_output: Stream,
pub(super) user_error: Stream,
pub(super) load_contexts: Vec<LoadContext>,
- pub(super) runtime: Runtime,
+ pub(super) foreign_function_table: ForeignFunctionTable,
}
#[derive(Debug)]
}
}
- pub fn run_top_level(&mut self, module_name: Atom, key: PredicateKey) {
- pub fn run_top_level(&mut self) -> std::process::ExitCode {
++ 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() {
));
}
- self.machine_st.registers[1] = heap_loc_as_cell!(
- iter_to_heap_list(&mut self.machine_st.heap, arg_pstrs.into_iter())
- );
+ 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);
++ self.run_module_predicate(module_name, key)
+ }
- self.run_module_predicate(atom!("$toplevel"), (atom!("$repl"), 1))
+ 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) {
user_output,
user_error,
load_contexts: vec![],
- runtime,
+ foreign_function_table: Default::default(),
};
let mut lib_path = current_dir();
--- /dev/null
- use rug::*;
+use crate::atom_table::*;
+use ordered_float::OrderedFloat;
++use dashu::*;
+use std::collections::BTreeMap;
+use regex::Regex;
+use std::collections::HashMap;
+
+pub type QueryResult = Result<QueryResolution, String>;
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum QueryResolution {
+ True,
+ False,
+ Matches(Vec<QueryMatch>),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct QueryMatch {
+ pub bindings: BTreeMap<String, Value>,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum QueryResolutionLine {
+ True,
+ False,
+ Match(BTreeMap<String, Value>),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum Value {
+ Integer(Integer),
+ Rational(Rational),
+ Float(OrderedFloat<f64>),
+ Atom(Atom),
+ String(String),
+ List(Vec<Value>),
+ Structure(Atom, Vec<Value>),
+ Var,
+}
+
+impl From<BTreeMap<&str, Value>> for QueryMatch {
+ fn from(bindings: BTreeMap<&str, Value>) -> Self {
+ QueryMatch {
+ bindings: bindings
+ .into_iter()
+ .map(|(k, v)| (k.to_string(), v))
+ .collect::<BTreeMap<_, _>>(),
+ }
+ }
+}
+
+impl From<BTreeMap<String, Value>> for QueryMatch {
+ fn from(bindings: BTreeMap<String, Value>) -> Self {
+ QueryMatch { bindings }
+ }
+}
+
+impl From<Vec<QueryResolutionLine>> for QueryResolution {
+ fn from(query_result_lines: Vec<QueryResolutionLine>) -> Self {
+ // If there is only one line, and it is true or false, return that.
+ if query_result_lines.len() == 1 {
+ match query_result_lines[0].clone() {
+ QueryResolutionLine::True => return QueryResolution::True,
+ QueryResolutionLine::False => return QueryResolution::False,
+ _ => {}
+ }
+ }
+
+ // If there is only one line, and it is an empty match, return true.
+ if query_result_lines.len() == 1 {
+ match query_result_lines[0].clone() {
+ QueryResolutionLine::Match(m) => {
+ if m.is_empty() {
+ return QueryResolution::True;
+ }
+ }
+ _ => {}
+ }
+ }
+
+ // If there is at least one line with true and no matches, return true.
+ if query_result_lines
+ .iter()
+ .any(|l| l == &QueryResolutionLine::True)
+ && !query_result_lines.iter().any(|l| {
+ if let &QueryResolutionLine::Match(_) = l {
+ true
+ } else {
+ false
+ }
+ })
+ {
+ return QueryResolution::True;
+ }
+
+ // 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
+ }
+ })
+ .map(|l| match l {
+ QueryResolutionLine::Match(m) => QueryMatch::from(m),
+ _ => unreachable!(),
+ })
+ .collect::<Vec<_>>();
+
+ if !all_matches.is_empty() {
+ return QueryResolution::Matches(all_matches);
+ }
+
+ QueryResolution::False
+ }
+}
+
+fn parse_prolog_response(input: &str) -> HashMap<String, String> {
+ let mut map: HashMap<String, String> = HashMap::new();
+ // Use regex to match strings including commas inside them
+ let re = Regex::new(r"(\w+)\s=\s([^,]*'[^']*'[^,]*|[^,]*)").unwrap();
+
+ for cap in re.captures_iter(input) {
+ let key = cap[1].to_string();
+ let value = cap[2].trim_end_matches(',').trim().to_string();
+ // 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("error").next().unwrap().to_string();
+ map.insert(key, value);
+ }
+
+ map
+}
+
+impl TryFrom<String> for QueryResolutionLine {
+ type Error = ();
+ fn try_from(string: String) -> Result<Self, Self::Error> {
+ match string.as_str() {
+ "true" => Ok(QueryResolutionLine::True),
+ "false" => Ok(QueryResolutionLine::False),
+ _ => Ok(QueryResolutionLine::Match(
+ parse_prolog_response(&string)
+ .iter()
+ .map(|(k, v)| -> Result<(String, Value), ()> {
+ let key = k.to_string();
+ let value = v.to_string();
+ Ok((key, Value::try_from(value)?))
+ })
+ .filter_map(Result::ok)
+ .collect::<BTreeMap<_, _>>()
+ )
+ ),
+ }
+ }
+}
+
+impl TryFrom<String> for Value {
+ type Error = ();
+ fn try_from(string: String) -> Result<Self, Self::Error> {
+ let trimmed = string.trim();
+
+ if 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("]") {
+ let mut iter = trimmed[1..trimmed.len() - 1].split(",");
+
+ let mut values = vec![];
+
+ while let Some(s) = iter.next() {
+ values.push(Value::try_from(s.to_string())?);
+ }
+
+ Ok(Value::List(values))
+ } else if trimmed.starts_with("{") && trimmed.ends_with("}") {
+ let mut iter = trimmed[1..trimmed.len() - 1].split(",");
+ let mut values = vec![];
+
+ while let Some(value) = iter.next() {
+ let items: Vec<_> = value.split(":").collect();
+ if items.len() == 2 {
+ let _key = items[0].to_string();
+ let value = items[1].to_string();
+ values.push(Value::try_from(value)?);
+ }
+ }
+
+ Ok(Value::Structure(atom!("{}"), values))
+ } else if trimmed.starts_with("<<") && trimmed.ends_with(">>") {
+ let mut iter = trimmed[2..trimmed.len() - 2].split(",");
+ let mut values = vec![];
+
+ while let Some(value) = iter.next() {
+ let items: Vec<_> = value.split(":").collect();
+ if items.len() == 2 {
+ let _key = items[0].to_string();
+ let value = items[1].to_string();
+ values.push(Value::try_from(value)?);
+ }
+ }
+
+ Ok(Value::Structure(atom!("<<>>"), values))
+ } else if !trimmed.contains(",") && !trimmed.contains("'") && !trimmed.contains("\"") {
+ Ok(Value::String(trimmed.into()))
+ } else {
+ Err(())
+ }
+ }
+}
+
+impl From<&str> for Value {
+ fn from(str: &str) -> Self {
+ Value::String(str.to_string())
+ }
+}
}
};
- let (tx, rx) = channel(1);
- let tx = Arc::new(Mutex::new(tx));
+ let (tx, rx) = std::sync::mpsc::sync_channel(1024);
- let _guard = self.runtime.enter();
- let listener = match self.runtime.block_on(async { tokio::net::TcpListener::bind(addr).await }) {
- Ok(listener) => listener,
- Err(_) => {
- return Err(self.machine_st.open_permission_error(address_sink, atom!("http_listen"), 2));
- }
+ let runtime = tokio::runtime::Handle::current();
+ let _guard = runtime.enter();
- let server = match Server::try_bind(&addr) {
- Ok(server) => server,
- Err(_) => {
- return Err(self.machine_st.open_permission_error(address_sink, atom!("http_listen"), 2));
- }
++ let listener = match runtime.block_on(async { tokio::net::TcpListener::bind(addr).await }) {
++ Ok(listener) => listener,
++ Err(_) => {
++ return Err(self.machine_st.open_permission_error(address_sink, atom!("http_listen"), 2));
++ }
};
- self.runtime.spawn(async move {
++
+ runtime.spawn(async move {
- let make_svc = make_service_fn(move |_conn| {
+ loop {
let tx = tx.clone();
- async move { Ok::<_, Infallible>(service_fn(move |req| http::serve_req(req, tx.clone()))) }
- });
- let server = server.serve(make_svc);
-
- if let Err(_) = server.await {
- eprintln!("server error");
+ let (stream, _) = listener.accept().await.unwrap();
+
+ tokio::task::spawn(async move {
+ if let Err(err) = http1::Builder::new()
+ .serve_connection(stream, HttpService {
+ tx
+ })
+ .await
+ {
+ eprintln!("Error serving connection: {:?}", err);
+ }
+ });
}
});
let http_listener = HttpListener { incoming: rx };
let query_str = request.request.uri().query().unwrap_or("");
let query_atom = self.machine_st.atom_tbl.build_with(query_str);
let query_cell = string_as_cstr_cell!(query_atom);
-
+
let hyper_req = request.request;
- let buf = self.runtime.block_on(async {hyper_req.collect().await.unwrap().aggregate()});
+ let runtime = tokio::runtime::Handle::current();
- let buf = runtime.block_on(async {hyper::body::aggregate(hyper_req).await.unwrap()});
++ let buf = runtime.block_on(async {hyper_req.collect().await.unwrap().aggregate()});
let reader = buf.reader();
let mut stream = Stream::from_http_stream(