]> Repositorios git - scryer-prolog.git/commitdiff
Add js_eval/2 for wasm32
authorAdrián Arroyo Calle <[email protected]>
Mon, 20 Nov 2023 16:45:22 +0000 (17:45 +0100)
committerAdrián Arroyo Calle <[email protected]>
Wed, 22 Nov 2023 20:21:19 +0000 (21:21 +0100)
Cargo.lock
Cargo.toml
build/instructions_template.rs
src/forms.rs
src/lib.rs
src/lib/wasm.pl [new file with mode: 0644]
src/machine/dispatch.rs
src/machine/system_calls.rs

index bcb67381f0a0186ff38cfe36bb1017f719464e4d..ef7a35c8a121e3c69acc9e5bbf47d2c84274b772 100644 (file)
@@ -354,16 +354,6 @@ dependencies = [
  "wasm-bindgen",
 ]
 
-[[package]]
-name = "console_log"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "be8aed40e4edbf4d3b4431ab260b63fdc40f5780a4766824329ea0f1eefe3c0f"
-dependencies = [
- "log",
- "web-sys",
-]
-
 [[package]]
 name = "core-foundation"
 version = "0.9.3"
@@ -2326,7 +2316,6 @@ dependencies = [
  "bytes",
  "chrono",
  "console_error_panic_hook",
- "console_log",
  "cpu-time",
  "criterion",
  "crossterm",
@@ -2343,6 +2332,7 @@ dependencies = [
  "hostname",
  "iai-callgrind",
  "indexmap",
+ "js-sys",
  "lazy_static",
  "lexical",
  "libc",
index b36e4e1380d16ab1d4c319c58b313cacab0d809a..5a7cc04bbe4c17d85da598fcd338f453a4d93fe8 100644 (file)
@@ -95,11 +95,11 @@ tokio = { version = "1.28.2", features = [
 
 [target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies]
 console_error_panic_hook = "0.1"
-console_log = "1.0"
 wasm-bindgen = "0.2.87"
 wasm-bindgen-futures = "0.4"
 serde-wasm-bindgen = "0.5"
 web-sys = { version = "0.3", features = ["Document", "Window", "Element"] }
+js-sys = "0.3"
 
 [target.'cfg(target_os = "wasi")'.dependencies]
 ring-wasi = { version = "0.16.25" }
index 69cf2d12e986837c7c8e566fc61f36f77c384179..82cc96ea7f0a99b2ba1228f04e547d83e9ac94e7 100644 (file)
@@ -573,6 +573,8 @@ enum SystemClauseType {
     ForeignCall,
     #[strum_discriminants(strum(props(Arity = "2", Name = "$define_foreign_struct")))]
     DefineForeignStruct,
+    #[strum_discriminants(strum(props(Arity = "2", Name = "$js_eval")))]
+    JsEval,
     #[strum_discriminants(strum(props(Arity = "3", Name = "$predicate_defined")))]
     PredicateDefined,
     #[strum_discriminants(strum(props(Arity = "3", Name = "$strip_module")))]
@@ -1774,6 +1776,7 @@ fn generate_instruction_preface() -> TokenStream {
                     &Instruction::CallLoadForeignLib |
                     &Instruction::CallForeignCall |
                     &Instruction::CallDefineForeignStruct |
+                    &Instruction::CallJsEval |
                     &Instruction::CallPredicateDefined |
                     &Instruction::CallStripModule |
                     &Instruction::CallCurrentTime |
@@ -2008,6 +2011,7 @@ fn generate_instruction_preface() -> TokenStream {
                     &Instruction::ExecuteLoadForeignLib |
                     &Instruction::ExecuteForeignCall |
                     &Instruction::ExecuteDefineForeignStruct |
+                    &Instruction::ExecuteJsEval |
                     &Instruction::ExecutePredicateDefined |
                     &Instruction::ExecuteStripModule |
                     &Instruction::ExecuteCurrentTime |
index 3a2b743f2e043b62fcb2514310500c43d7741f73..d18d41f1539a171e8885c1b8fc7fefd8ed15ff40 100644 (file)
@@ -744,11 +744,11 @@ impl Number {
             Number::Float(f) => Number::Float(OrderedFloat(f.signum())),
             _ => {
                 if self.is_positive() {
-                   if self.is_zero() {
-                       Number::Fixnum(Fixnum::build_with(0))
-                   } else {
-                       Number::Fixnum(Fixnum::build_with(1))
-                   }
+                    if self.is_zero() {
+                        Number::Fixnum(Fixnum::build_with(0))
+                    } else {
+                        Number::Fixnum(Fixnum::build_with(1))
+                    }
                 } else if self.is_negative() {
                     Number::Fixnum(Fixnum::build_with(-1))
                 } else {
index bf7ddc57c9f1c947458a449b034b5771fb0077e0..56d967eb256d77831883d71465c21e8a1ef2b4d8 100644 (file)
@@ -51,7 +51,8 @@ use wasm_bindgen::prelude::*;
 #[wasm_bindgen]
 pub fn eval_code(s: &str) -> String {
     use machine::mock_wam::*;
-    use web_sys::console;
+
+    console_error_panic_hook::set_once();
 
     let mut wam = Machine::with_test_streams();
     let bytes = wam.test_load_string(s);
diff --git a/src/lib/wasm.pl b/src/lib/wasm.pl
new file mode 100644 (file)
index 0000000..2ade164
--- /dev/null
@@ -0,0 +1,29 @@
+/** Predicates for the WebAssembly platform
+
+This module contains predicates that are only available in
+the WASM (WebAssembly) version of Scryer Prolog.
+*/
+
+:- module(wasm, [js_eval/2]).
+
+:- use_module(library(error)).
+
+%% js_eval(+JsCode, -Result).
+%
+% Executes a JavaScript snippet `JsCode` using the platform
+% `eval` function. `Result` takes the return value of that code.
+% Strings, booleans, numbers, null and undefined are directly mapped to Prolog.
+% Arrays, objects, bigints, symbols and functions are not mapped.
+% Instead, a `js_{type}` atom will be returned.
+%
+% Example (on a browser):
+%
+% ```
+% ?- js_eval("prompt('What is your name?')", Name).
+%    % A prompt is showed, with a textbox.
+%    Name = "Whatever was written on the textbox".
+% ```
+js_eval(JsCode, Result) :-
+    must_be(chars, JsCode),
+    can_be(chars, Result),
+    '$js_eval'(JsCode, Result).
index 6afce9263334c809e96b842211a29c42d3e151f2..2fdf5c2f1d9c2753b856803fd3f89b936a76f400 100644 (file)
@@ -4128,6 +4128,14 @@ impl Machine {
                         try_or_throw!(self.machine_st, self.define_foreign_struct());
                         step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
                     }
+                    &Instruction::CallJsEval => {
+                        try_or_throw!(self.machine_st, self.js_eval());
+                        step_or_fail!(self, self.machine_st.p += 1);
+                    }
+                    &Instruction::ExecuteJsEval => {
+                        try_or_throw!(self.machine_st, self.js_eval());
+                        step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
+                    }
                     &Instruction::CallCurrentTime => {
                         self.current_time();
                         step_or_fail!(self, self.machine_st.p += 1);
index aa78f1a1079b459d9c85025412e632a06450ed7c..97f67fe2ba5788a067fd8bf2def539b96bbbc9a4 100644 (file)
@@ -4879,6 +4879,70 @@ impl Machine {
         Ok(())
     }
 
+    #[cfg(not(target_arch = "wasm32"))]
+    #[inline(always)]
+    pub(crate) fn js_eval(&mut self) -> CallResult {
+        unimplemented!()
+    }
+
+    #[cfg(target_arch = "wasm32")]
+    #[inline(always)]
+    pub(crate) fn js_eval(&mut self) -> CallResult {
+        let code = self.deref_register(1);
+        let result_reg = self.deref_register(2);
+        if let Some(code) = self.machine_st.value_to_str_like(code) {
+            let result = match js_sys::eval(&code.as_str()) {
+                Ok(result) => self.unify_js_value(result, result_reg),
+                Err(result) => self.unify_js_value(result, result_reg),
+            };
+            return Ok(());
+        }
+        self.machine_st.fail = true;
+        Ok(())
+    }
+
+    #[cfg(target_arch = "wasm32")]
+    fn unify_js_value(&mut self, result: wasm_bindgen::JsValue, result_reg: HeapCellValue) {
+        match result.as_bool() {
+            Some(result) => match result {
+                true => self.machine_st.unify_atom(atom!("true"), result_reg),
+                false => self.machine_st.unify_atom(atom!("false"), result_reg),
+            },
+            None => match result.as_f64() {
+                Some(result) => {
+                    let n = float_alloc!(result, self.machine_st.arena);
+                    self.machine_st.unify_f64(n, result_reg);
+                }
+                None => match result.as_string() {
+                    Some(result) => {
+                        let result = AtomTable::build_with(&self.machine_st.atom_tbl, &result);
+                        self.machine_st.unify_complete_string(result, result_reg);
+                    }
+                    None => {
+                        if result.is_null() {
+                            self.machine_st.unify_atom(atom!("null"), result_reg);
+                        } else if result.is_undefined() {
+                            self.machine_st.unify_atom(atom!("undefined"), result_reg);
+                        } else if result.is_symbol() {
+                            self.machine_st.unify_atom(atom!("js_symbol"), result_reg);
+                        } else if result.is_object() {
+                            self.machine_st.unify_atom(atom!("js_object"), result_reg);
+                        } else if result.is_array() {
+                            self.machine_st.unify_atom(atom!("js_array"), result_reg);
+                        } else if result.is_function() {
+                            self.machine_st.unify_atom(atom!("js_function"), result_reg);
+                        } else if result.is_bigint() {
+                            self.machine_st.unify_atom(atom!("js_bigint"), result_reg);
+                        } else {
+                            self.machine_st
+                                .unify_atom(atom!("js_unknown_type"), result_reg);
+                        }
+                    }
+                },
+            },
+        }
+    }
+
     #[inline(always)]
     pub(crate) fn current_time(&mut self) {
         let timestamp = self.systemtime_to_timestamp(SystemTime::now());