Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Improve demo site
  • Loading branch information
coolreader18 committed Dec 15, 2018
commit b428f2e3b326d3f6b91795cefeb20560c498860d
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ __pycache__
**/*.pytest_cache
.*sw*
.repl_history.txt
wasm-pack.log
2 changes: 2 additions & 0 deletions wasm/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
bin/
pkg/
2 changes: 2 additions & 0 deletions wasm/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions wasm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ rustpython_parser = {path = "../parser"}
rustpython_vm = {path = "../vm"}
cfg-if = "0.1.2"
wasm-bindgen = "0.2"
js-sys = "0.3"
num-bigint = "0.2.1"
Comment thread
coolreader18 marked this conversation as resolved.
Outdated

[dependencies.web-sys]
version = "0.3"
Expand Down
2 changes: 2 additions & 0 deletions wasm/app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dist/
node_modules/
6 changes: 6 additions & 0 deletions wasm/app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@
height: 2em;
font-size: 24px;
}

#error {
color: tomato;
margin-top: 10px;
}
</style>
</head>
<body>
Expand All @@ -42,6 +47,7 @@ <h1>RustPython Demo</h1>
count += 1
</textarea>
<button id="run-btn">Run &#9655;</button>
<div id="error"></div>
<script src="./bootstrap.js"></script>
<h3>Standard Output</h3>
<textarea id="console">Loading...</textarea>
Expand Down
24 changes: 14 additions & 10 deletions wasm/app/index.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
import * as rp from "rustpython_wasm";

window.rp = rp;
Comment thread
coolreader18 marked this conversation as resolved.

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
114 changes: 103 additions & 11 deletions wasm/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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 {
Comment thread
coolreader18 marked this conversation as resolved.
Outdated
use pyobject::PyObjectKind;
let py_obj = py_obj.borrow();
match py_obj.kind {
Comment thread
coolreader18 marked this conversation as resolved.
Outdated
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<JsValue, JsValue> {
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<JsValue, JsValue> {
//add hash in here
console::log_1(&"Running RustPython".into());
console::log_1(&"Running code:".into());
Expand All @@ -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(
Comment thread
coolreader18 marked this conversation as resolved.
&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())
}
}
}
23 changes: 19 additions & 4 deletions wasm/src/wasm_builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<HtmlTextAreaElement>().unwrap();
let value = textarea.value();
textarea.set_value(&format!("{}{}", value, text));
Expand All @@ -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 {
Comment thread
coolreader18 marked this conversation as resolved.
Outdated
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())
}