diff --git a/.gitignore b/.gitignore index b1df9e59fa5..bdf973f0337 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ __pycache__ **/*.pytest_cache .*sw* .repl_history.txt +wasm-pack.log diff --git a/wasm/.gitignore b/wasm/.gitignore new file mode 100644 index 00000000000..882d44992fa --- /dev/null +++ b/wasm/.gitignore @@ -0,0 +1,2 @@ +bin/ +pkg/ diff --git a/wasm/Cargo.lock b/wasm/Cargo.lock index e4cc8e60e84..9bad7eebad8 100644 --- a/wasm/Cargo.lock +++ b/wasm/Cargo.lock @@ -577,6 +577,7 @@ name = "rustpython_wasm" version = "0.1.0" dependencies = [ "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "js-sys 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "rustpython_parser 0.0.1", "rustpython_vm 0.1.0", "wasm-bindgen 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/wasm/Cargo.toml b/wasm/Cargo.toml index cfca2acb716..4be409dfc2d 100644 --- a/wasm/Cargo.toml +++ b/wasm/Cargo.toml @@ -14,6 +14,7 @@ rustpython_parser = {path = "../parser"} rustpython_vm = {path = "../vm"} cfg-if = "0.1.2" wasm-bindgen = "0.2" +js-sys = "0.3" [dependencies.web-sys] version = "0.3" diff --git a/wasm/app/.gitignore b/wasm/app/.gitignore new file mode 100644 index 00000000000..1eae0cf6700 --- /dev/null +++ b/wasm/app/.gitignore @@ -0,0 +1,2 @@ +dist/ +node_modules/ diff --git a/wasm/app/.prettierrc b/wasm/app/.prettierrc new file mode 100644 index 00000000000..96c36f53c9f --- /dev/null +++ b/wasm/app/.prettierrc @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "tabWidth": 4 +} diff --git a/wasm/app/bootstrap.js b/wasm/app/bootstrap.js index 7934d627e82..61136ee9b8e 100644 --- a/wasm/app/bootstrap.js +++ b/wasm/app/bootstrap.js @@ -1,5 +1,6 @@ // A dependency graph that contains any wasm must all be imported // asynchronously. This `bootstrap.js` file does the single async import, so // that no one else needs to worry about it again. -import("./index.js") - .catch(e => console.error("Error importing `index.js`:", e)); +import('./index.js').catch(e => + console.error('Error importing `index.js`:', e) +); diff --git a/wasm/app/index.html b/wasm/app/index.html index bb7f2a98fe2..f46ce1251a3 100644 --- a/wasm/app/index.html +++ b/wasm/app/index.html @@ -1,35 +1,50 @@ -
- -RustPython is a Python interpreter writter in Rust. This demo is compiled from Rust to WebAssembly so it runs in the browser
-Please input your python code below and click Run:
- + + + +Here's some info regarding the rp.eval_py() function
json.dumps.
+ js_vars. Again, only values that can be serialized
+ with JSON.stringify() will go through.
+
+
diff --git a/wasm/app/index.js b/wasm/app/index.js
index ef223a5a041..d21ac7a0450 100644
--- a/wasm/app/index.js
+++ b/wasm/app/index.js
@@ -1,26 +1,27 @@
-import * as rp from "rustpython_wasm";
+import * as rp from 'rustpython_wasm';
-function runCodeFromTextarea(_) {
- const consoleElement = document.getElementById('console');
- // Clean the console
- consoleElement.value = '';
-
- const code = document.getElementById('code').value;
- try {
- if (!code.endsWith('\n')) { // HACK: if the code doesn't end with newline it crashes.
- rp.run_code(code + '\n');
- return;
- }
+// so people can play around with it
+window.rp = rp;
- rp.run_code(code);
+function runCodeFromTextarea(_) {
+ const consoleElement = document.getElementById('console');
+ const errorElement = document.getElementById('error');
- } catch(e) {
- consoleElement.value = 'Execution failed. Please check if your Python code has any syntax error.';
- console.error(e);
- }
+ // Clean the console and errors
+ consoleElement.value = '';
+ errorElement.textContent = '';
+ const code = document.getElementById('code').value;
+ try {
+ rp.run_from_textbox(code);
+ } catch (e) {
+ errorElement.textContent = e;
+ console.error(e);
+ }
}
-document.getElementById('run-btn').addEventListener('click', runCodeFromTextarea);
+document
+ .getElementById('run-btn')
+ .addEventListener('click', runCodeFromTextarea);
runCodeFromTextarea(); // Run once for demo
diff --git a/wasm/src/lib.rs b/wasm/src/lib.rs
index 36cdf8d0895..9ac5677d793 100644
--- a/wasm/src/lib.rs
+++ b/wasm/src/lib.rs
@@ -1,33 +1,143 @@
mod wasm_builtins;
+extern crate js_sys;
extern crate rustpython_vm;
extern crate wasm_bindgen;
extern crate web_sys;
-use rustpython_vm::VirtualMachine;
use rustpython_vm::compile;
-use rustpython_vm::pyobject::AttributeProtocol;
+use rustpython_vm::pyobject::{self, PyObjectRef, PyResult};
+use rustpython_vm::VirtualMachine;
use wasm_bindgen::prelude::*;
use web_sys::console;
+fn py_str_err(vm: &mut VirtualMachine, py_err: &PyObjectRef) -> String {
+ vm.to_pystr(&py_err)
+ .unwrap_or_else(|_| "Error, and error getting error message".into())
+}
+
+fn py_to_js(vm: &mut VirtualMachine, py_obj: PyObjectRef) -> JsValue {
+ let dumps = rustpython_vm::import::import(
+ vm,
+ std::path::PathBuf::default(),
+ "json",
+ &Some("dumps".into()),
+ )
+ .expect("Couldn't get json.dumps function");
+ match vm.invoke(dumps, pyobject::PyFuncArgs::new(vec![py_obj], vec![])) {
+ Ok(value) => {
+ let json = vm.to_pystr(&value).unwrap();
+ js_sys::JSON::parse(&json).unwrap_or(JsValue::UNDEFINED)
+ }
+ Err(_) => JsValue::UNDEFINED,
+ }
+}
+
+fn js_to_py(vm: &mut VirtualMachine, js_val: JsValue) -> PyObjectRef {
+ let json = match js_sys::JSON::stringify(&js_val) {
+ Ok(json) => String::from(json),
+ Err(_) => return vm.get_none(),
+ };
+
+ let loads = rustpython_vm::import::import(
+ vm,
+ std::path::PathBuf::default(),
+ "json",
+ &Some("loads".into()),
+ )
+ .expect("Couldn't get json.loads function");
+
+ let py_json = vm.new_str(json);
+
+ vm.invoke(loads, pyobject::PyFuncArgs::new(vec![py_json], vec![]))
+ // can safely unwrap because we know it's valid JSON
+ .unwrap()
+}
+
+fn eval