From: Nicolas Luck Date: Thu, 3 Aug 2023 18:16:32 +0000 (+0200) Subject: Merge branch 'master' into library-use-case X-Git-Tag: remove^2~41 X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=48a483581952920fb4fa5e4fdcd7eaca9b723be0;p=scryer-prolog.git Merge branch 'master' into library-use-case # Conflicts: # Cargo.lock # Cargo.toml # src/bin/scryer-prolog.rs # src/loader.pl # src/machine/mock_wam.rs # src/machine/mod.rs # src/machine/system_calls.rs --- 48a483581952920fb4fa5e4fdcd7eaca9b723be0 diff --cc Cargo.lock index 5ed25656,d681658f..c0c62cfc --- a/Cargo.lock +++ b/Cargo.lock @@@ -2,15 -2,27 +2,36 @@@ # 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" @@@ -1013,24 -1188,15 +1197,21 @@@ version = "0.1.1 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", @@@ -1700,30 -1779,41 +1806,58 @@@ version = "0.1.10 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]] @@@ -1877,16 -1972,18 +2016,20 @@@ dependencies = "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", diff --cc Cargo.toml index c64b7510,b5f330f7..40b66623 --- a/Cargo.toml +++ b/Cargo.toml @@@ -59,17 -59,22 +59,24 @@@ smallvec = "1.8.0 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" } diff --cc src/bin/scryer-prolog.rs index 4dcc05c2,e15bae14..da462526 --- a/src/bin/scryer-prolog.rs +++ b/src/bin/scryer-prolog.rs @@@ -1,19 -1,11 +1,19 @@@ - 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)) ++ }) } diff --cc src/machine/machine_state.rs index 50431a8a,ccd65764..da6f6641 --- a/src/machine/machine_state.rs +++ b/src/machine/machine_state.rs @@@ -495,10 -654,6 +654,14 @@@ impl MachineState } } + if let Stream::Byte(_) = stream { - return self.read_term(stream, indices) ++ return self.read_term( ++ stream, ++ indices, ++ MachineState::read_term_from_user_input_eof_handler ++ ) + } + unreachable!("Stream must be a Stream::Readline(_)") } diff --cc src/machine/mod.rs index 03ac6492,a3f0a24c..a170abd4 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@@ -17,8 -15,8 +17,9 @@@ pub mod machine_indices 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; @@@ -70,6 -67,8 +73,7 @@@ pub struct Machine pub(super) user_output: Stream, pub(super) user_error: Stream, pub(super) load_contexts: Vec, - pub(super) runtime: Runtime, + pub(super) foreign_function_table: ForeignFunctionTable, } #[derive(Debug)] @@@ -288,7 -283,7 +283,7 @@@ impl Machine } } - 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() { @@@ -299,21 -294,11 +294,21 @@@ )); } - 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) { @@@ -458,6 -436,8 +452,7 @@@ user_output, user_error, load_contexts: vec![], - runtime, + foreign_function_table: Default::default(), }; let mut lib_path = current_dir(); diff --cc src/machine/parsed_results.rs index ecb58c1c,00000000..aaec386d mode 100644,000000..100644 --- a/src/machine/parsed_results.rs +++ b/src/machine/parsed_results.rs @@@ -1,220 -1,0 +1,220 @@@ +use crate::atom_table::*; +use ordered_float::OrderedFloat; - use rug::*; ++use dashu::*; +use std::collections::BTreeMap; +use regex::Regex; +use std::collections::HashMap; + +pub type QueryResult = Result; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum QueryResolution { + True, + False, + Matches(Vec), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct QueryMatch { + pub bindings: BTreeMap, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum QueryResolutionLine { + True, + False, + Match(BTreeMap), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Value { + Integer(Integer), + Rational(Rational), + Float(OrderedFloat), + Atom(Atom), + String(String), + List(Vec), + Structure(Atom, Vec), + Var, +} + +impl From> for QueryMatch { + fn from(bindings: BTreeMap<&str, Value>) -> Self { + QueryMatch { + bindings: bindings + .into_iter() + .map(|(k, v)| (k.to_string(), v)) + .collect::>(), + } + } +} + +impl From> for QueryMatch { + fn from(bindings: BTreeMap) -> Self { + QueryMatch { bindings } + } +} + +impl From> for QueryResolution { + fn from(query_result_lines: Vec) -> 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::>(); + + if !all_matches.is_empty() { + return QueryResolution::Matches(all_matches); + } + + QueryResolution::False + } +} + +fn parse_prolog_response(input: &str) -> HashMap { + let mut map: HashMap = 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 for QueryResolutionLine { + type Error = (); + fn try_from(string: String) -> Result { + 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::>() + ) + ), + } + } +} + +impl TryFrom for Value { + type Error = (); + fn try_from(string: String) -> Result { + 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()) + } +} diff --cc src/machine/system_calls.rs index 1160c1cd,3d18dc6b..88d1e2ca --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@@ -3943,27 -4331,31 +4331,33 @@@ impl Machine } }; - 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 }; @@@ -4016,10 -4408,9 +4410,10 @@@ 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(