]> Repositorios git - scryer-prolog.git/commitdiff
Merge branch 'master' into library-use-case
authorNicolas Luck <[email protected]>
Thu, 3 Aug 2023 18:16:32 +0000 (20:16 +0200)
committerNicolas Luck <[email protected]>
Thu, 3 Aug 2023 18:16:32 +0000 (20:16 +0200)
# 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

1  2 
Cargo.lock
Cargo.toml
src/bin/scryer-prolog.rs
src/lib.rs
src/loader.pl
src/machine/machine_state.rs
src/machine/mock_wam.rs
src/machine/mod.rs
src/machine/parsed_results.rs
src/machine/system_calls.rs
src/toplevel.pl

diff --cc Cargo.lock
index 5ed25656cd780414dd185179ecf8da3f209db14a,d681658f51cfb9406f4ba1bc12ea7b0371aea738..c0c62cfc1b69600472dad14f56e2195c014be47c
@@@ -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 c64b7510208b444c0ef0085a661b7202c87c6b7f,b5f330f74ca4392532f9f555680a361d4766c355..40b66623c5aa33bc3716926ed8cdd65c9c892c1d
@@@ -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" }
index 4dcc05c26bf9718a28f5d97774de9e3e1d118c62,e15bae144feb28f2b1f8e665f8988c6927ab2515..da462526393cfa11dd808166477c89e4804d3f41
@@@ -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/lib.rs
Simple merge
diff --cc src/loader.pl
Simple merge
index 50431a8aa24d5e01de514a39603ae5ac55ef0bdc,ccd657648e29e04b8445088ca2e4eb54985bc217..da6f664133cc4d22d62a7422840ba14b4e76ac44
@@@ -495,10 -654,6 +654,14 @@@ impl MachineState 
              }
          }
  
-             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(_)")
      }
  
Simple merge
index 03ac6492519124e66175a1859bffc0cffa7302a0,a3f0a24c2d171e07d46ed7add9e22e49f2e33cde..a170abd4c3f5905008fbe719d396cccd02ba19e1
@@@ -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<LoadContext>,
 -    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() {
              ));
          }
  
 -        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();
index ecb58c1cb16a50daae381f51030ea3b441d08859,0000000000000000000000000000000000000000..aaec386d914ef5dbf66ab4696c4203c8e76f0d28
mode 100644,000000..100644
--- /dev/null
@@@ -1,220 -1,0 +1,220 @@@
- 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())
 +    }
 +}
index 1160c1cdb372a6e2a1f43a8c95247aa76899921e,3d18dc6be53bcc8189b734ca906859b515dee113..88d1e2cada0c14ee80178e847b8be09ad1bc910e
@@@ -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 };
                                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(
diff --cc src/toplevel.pl
Simple merge