diff --git a/wasm/app/index.js b/wasm/app/index.js
index ef223a5a04..c718372eb2 100644
--- a/wasm/app/index.js
+++ b/wasm/app/index.js
@@ -1,26 +1,30 @@
import * as rp from "rustpython_wasm";
+window.rp = rp;
+
function runCodeFromTextarea(_) {
- const consoleElement = document.getElementById('console');
+ const consoleElement = document.getElementById("console");
+ const errorElement = document.getElementById("error");
// Clean the console
- consoleElement.value = '';
+ consoleElement.value = "";
- const code = document.getElementById('code').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');
+ if (!code.endsWith("\n")) {
+ // HACK: if the code doesn't end with newline it crashes.
+ rp.run_code(code + "\n");
return;
}
rp.run_code(code);
-
- } catch(e) {
- consoleElement.value = 'Execution failed. Please check if your Python code has any syntax error.';
+ } 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 36cdf8d089..a241e45f8f 100644
--- a/wasm/src/lib.rs
+++ b/wasm/src/lib.rs
@@ -1,17 +1,95 @@
mod wasm_builtins;
+extern crate js_sys;
+extern crate num_bigint;
extern crate rustpython_vm;
extern crate wasm_bindgen;
extern crate web_sys;
-use rustpython_vm::VirtualMachine;
+use num_bigint::BigInt;
use rustpython_vm::compile;
-use rustpython_vm::pyobject::AttributeProtocol;
+use rustpython_vm::pyobject::{self, IdProtocol, 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 {
+ use pyobject::PyObjectKind;
+ let py_obj = py_obj.borrow();
+ match py_obj.kind {
+ PyObjectKind::String { ref value } => value.into(),
+ PyObjectKind::Integer { ref value } => {
+ if let Some(ref typ) = py_obj.typ {
+ if typ.is(&vm.ctx.bool_type()) {
+ let out_bool = value == &BigInt::new(num_bigint::Sign::Plus, vec![1]);
+ return out_bool.into();
+ }
+ }
+ let int = vm.ctx.new_int(value.clone());
+ rustpython_vm::obj::objfloat::make_float(vm, &int)
+ .unwrap()
+ .into()
+ }
+ PyObjectKind::Float { ref value } => JsValue::from_f64(*value),
+ PyObjectKind::Bytes { ref value } => {
+ let arr = js_sys::Uint8Array::new(&JsValue::from(value.len() as u32));
+ for (i, byte) in value.iter().enumerate() {
+ console::log_1(&JsValue::from(i as u32));
+ js_sys::Reflect::set(&arr, &JsValue::from(i as u32), &JsValue::from(*byte))
+ .unwrap();
+ }
+ arr.into()
+ }
+ PyObjectKind::Sequence { ref elements } => {
+ let arr = js_sys::Array::new();
+ for val in elements {
+ arr.push(&py_to_js(vm, val));
+ }
+ arr.into()
+ }
+ PyObjectKind::Dict { ref elements } => {
+ let obj = js_sys::Object::new();
+ for (key, (_, val)) in elements {
+ js_sys::Reflect::set(&obj, &key.into(), &py_to_js(vm, val))
+ .expect("couldn't set property of object");
+ }
+ obj.into()
+ }
+ PyObjectKind::None => JsValue::UNDEFINED,
+ _ => JsValue::UNDEFINED,
+ }
+}
+
+fn eval(vm: &mut VirtualMachine, source: &str) -> PyResult {
+ let code_obj = compile::compile(vm, &source.to_string(), compile::Mode::Exec, None)?;
+
+ let builtins = vm.get_builtin_scope();
+ let vars = vm.context().new_scope(Some(builtins));
+ vm.run_code_obj(code_obj, vars)
+}
+
+#[wasm_bindgen]
+pub fn eval_py(source: &str) -> Result {
+ let mut vm = VirtualMachine::new();
+
+ vm.ctx.set_attr(
+ &vm.builtins,
+ "print",
+ vm.context().new_rustfunc(wasm_builtins::builtin_log),
+ );
+
+ eval(&mut vm, source)
+ .map(|value| py_to_js(&mut vm, &value))
+ .map_err(|err| py_str_err(&mut vm, &err).into())
+}
+
#[wasm_bindgen]
-pub fn run_code(source: &str) -> () {
+pub fn run_code(source: &str) -> Result {
//add hash in here
console::log_1(&"Running RustPython".into());
console::log_1(&"Running code:".into());
@@ -20,14 +98,28 @@ pub fn run_code(source: &str) -> () {
let mut vm = VirtualMachine::new();
// We are monkey-patching the builtin print to use console.log
// TODO: moneky-patch sys.stdout instead, after print actually uses sys.stdout
- vm.builtins.set_attr("print", vm.context().new_rustfunc(wasm_builtins::builtin_print));
-
- let code_obj = compile::compile(&mut vm, &source.to_string(), compile::Mode::Exec, None);
+ vm.ctx.set_attr(
+ &vm.builtins,
+ "print",
+ vm.context().new_rustfunc(wasm_builtins::builtin_print),
+ );
- let builtins = vm.get_builtin_scope();
- let vars = vm.context().new_scope(Some(builtins));
- match vm.run_code_obj(code_obj.unwrap(), vars) {
- Ok(_value) => console::log_1(&"Execution successful".into()),
- Err(_) => console::log_1(&"Execution failed".into()),
+ match eval(&mut vm, source) {
+ Ok(value) => {
+ console::log_1(&"Execution successful".into());
+ match value.borrow().kind {
+ pyobject::PyObjectKind::None => {}
+ _ => {
+ if let Ok(text) = vm.to_pystr(&value) {
+ wasm_builtins::print_to_html(&text);
+ }
+ }
+ }
+ Ok(JsValue::UNDEFINED)
+ }
+ Err(err) => {
+ console::log_1(&"Execution failed".into());
+ Err(py_str_err(&mut vm, &err).into())
+ }
}
}
diff --git a/wasm/src/wasm_builtins.rs b/wasm/src/wasm_builtins.rs
index c3d601a8cd..1bf5b1aa4f 100644
--- a/wasm/src/wasm_builtins.rs
+++ b/wasm/src/wasm_builtins.rs
@@ -4,21 +4,25 @@
//! desktop.
//! Implements functions listed here: https://docs.python.org/3/library/builtins.html
//!
+extern crate js_sys;
extern crate wasm_bindgen;
extern crate web_sys;
+use js_sys::Array;
use rustpython_vm::obj::objstr;
+use rustpython_vm::pyobject::{PyFuncArgs, PyResult};
use rustpython_vm::VirtualMachine;
-use rustpython_vm::pyobject::{ PyFuncArgs, PyResult };
use wasm_bindgen::JsCast;
-use web_sys::{HtmlTextAreaElement, window};
+use web_sys::{console, window, HtmlTextAreaElement};
// The HTML id of the textarea element that act as our STDOUT
const CONSOLE_ELEMENT_ID: &str = "console";
-fn print_to_html(text: &str) {
+pub fn print_to_html(text: &str) {
let document = window().unwrap().document().unwrap();
- let element = document.get_element_by_id(CONSOLE_ELEMENT_ID).expect("Can't find the console textarea");
+ let element = document
+ .get_element_by_id(CONSOLE_ELEMENT_ID)
+ .expect("Can't find the console textarea");
let textarea = element.dyn_ref::().unwrap();
let value = textarea.value();
textarea.set_value(&format!("{}{}", value, text));
@@ -38,3 +42,14 @@ pub fn builtin_print(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
}
Ok(vm.get_none())
}
+
+pub fn builtin_log(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
+ let arr = Array::new();
+ for a in args.args {
+ let v = vm.to_str(&a)?;
+ let s = objstr::get_value(&v);
+ arr.push(&s.into());
+ }
+ console::log(&arr);
+ Ok(vm.get_none())
+}
From f8cce25f695c5b0eb4bf7220728e5a5747236f9c Mon Sep 17 00:00:00 2001
From: coolreader18 <33094578+coolreader18@users.noreply.github.com>
Date: Sat, 15 Dec 2018 01:31:44 -0600
Subject: [PATCH 2/8] Formatting; move the `+ '\n'` hack to eval().
---
wasm/app/.prettierrc | 4 ++++
wasm/app/bootstrap.js | 5 +++--
wasm/app/index.html | 24 +++++++++++++++++++-----
wasm/app/index.js | 33 ++++++++++++++-------------------
wasm/src/lib.rs | 12 ++++++++++--
5 files changed, 50 insertions(+), 28 deletions(-)
create mode 100644 wasm/app/.prettierrc
diff --git a/wasm/app/.prettierrc b/wasm/app/.prettierrc
new file mode 100644
index 0000000000..96c36f53c9
--- /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 7934d627e8..61136ee9b8 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 ac57dd17db..a8bcaf44f0 100644
--- a/wasm/app/index.html
+++ b/wasm/app/index.html
@@ -1,7 +1,7 @@
-
+
RustPython Demo
-
-
-
RustPython Demo
-
- 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:
-
- Alternatively, open up your browser's devtools and play with
- rp.eval_py('print("a")')
-
-
+
+
+
+
Standard Output
+
Loading...
-
-
+
Here's some info regarding the rp.eval_py() function
+
+
+ You can return variables from python and get them returned to
+ JS, with the only requirement being that they're serializable
+ with json.dumps.
+
+
+ You can pass an object as the second argument to the function,
+ and that will be available in python as the variable
+ 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 4463b47746..d21ac7a045 100644
--- a/wasm/app/index.js
+++ b/wasm/app/index.js
@@ -6,8 +6,10 @@ window.rp = rp;
function runCodeFromTextarea(_) {
const consoleElement = document.getElementById('console');
const errorElement = document.getElementById('error');
- // Clean the console
+
+ // Clean the console and errors
consoleElement.value = '';
+ errorElement.textContent = '';
const code = document.getElementById('code').value;
try {
diff --git a/wasm/src/lib.rs b/wasm/src/lib.rs
index 5a9b97c5da..9ac5677d79 100644
--- a/wasm/src/lib.rs
+++ b/wasm/src/lib.rs
@@ -5,7 +5,6 @@ extern crate rustpython_vm;
extern crate wasm_bindgen;
extern crate web_sys;
-use js_sys::Reflect;
use rustpython_vm::compile;
use rustpython_vm::pyobject::{self, PyObjectRef, PyResult};
use rustpython_vm::VirtualMachine;
@@ -77,6 +76,12 @@ where
#[wasm_bindgen]
pub fn eval_py(source: &str, js_injections: Option) -> Result {
+ if let Some(js_injections) = js_injections.clone() {
+ if !js_injections.is_object() {
+ return Err(js_sys::TypeError::new("The second argument must be an object").into());
+ }
+ }
+
let mut vm = VirtualMachine::new();
vm.ctx.set_attr(
@@ -87,24 +92,11 @@ pub fn eval_py(source: &str, js_injections: Option) -> Result