Skip to content

Commit b428f2e

Browse files
committed
Improve demo site
1 parent 62c53d8 commit b428f2e

File tree

9 files changed

+151
-25
lines changed

9 files changed

+151
-25
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ __pycache__
66
**/*.pytest_cache
77
.*sw*
88
.repl_history.txt
9+
wasm-pack.log

wasm/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
bin/
2+
pkg/

wasm/Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

wasm/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ rustpython_parser = {path = "../parser"}
1414
rustpython_vm = {path = "../vm"}
1515
cfg-if = "0.1.2"
1616
wasm-bindgen = "0.2"
17+
js-sys = "0.3"
18+
num-bigint = "0.2.1"
1719

1820
[dependencies.web-sys]
1921
version = "0.3"

wasm/app/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
dist/
2+
node_modules/

wasm/app/index.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@
2323
height: 2em;
2424
font-size: 24px;
2525
}
26+
27+
#error {
28+
color: tomato;
29+
margin-top: 10px;
30+
}
2631
</style>
2732
</head>
2833
<body>
@@ -42,6 +47,7 @@ <h1>RustPython Demo</h1>
4247
count += 1
4348
</textarea>
4449
<button id="run-btn">Run &#9655;</button>
50+
<div id="error"></div>
4551
<script src="./bootstrap.js"></script>
4652
<h3>Standard Output</h3>
4753
<textarea id="console">Loading...</textarea>

wasm/app/index.js

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,30 @@
11
import * as rp from "rustpython_wasm";
22

3+
window.rp = rp;
4+
35
function runCodeFromTextarea(_) {
4-
const consoleElement = document.getElementById('console');
6+
const consoleElement = document.getElementById("console");
7+
const errorElement = document.getElementById("error");
58
// Clean the console
6-
consoleElement.value = '';
9+
consoleElement.value = "";
710

8-
const code = document.getElementById('code').value;
11+
const code = document.getElementById("code").value;
912
try {
10-
if (!code.endsWith('\n')) { // HACK: if the code doesn't end with newline it crashes.
11-
rp.run_code(code + '\n');
13+
if (!code.endsWith("\n")) {
14+
// HACK: if the code doesn't end with newline it crashes.
15+
rp.run_code(code + "\n");
1216
return;
1317
}
1418

1519
rp.run_code(code);
16-
17-
} catch(e) {
18-
consoleElement.value = 'Execution failed. Please check if your Python code has any syntax error.';
20+
} catch (e) {
21+
errorElement.textContent = e;
1922
console.error(e);
2023
}
21-
2224
}
2325

24-
document.getElementById('run-btn').addEventListener('click', runCodeFromTextarea);
26+
document
27+
.getElementById("run-btn")
28+
.addEventListener("click", runCodeFromTextarea);
2529

2630
runCodeFromTextarea(); // Run once for demo

wasm/src/lib.rs

Lines changed: 103 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,95 @@
11
mod wasm_builtins;
22

3+
extern crate js_sys;
4+
extern crate num_bigint;
35
extern crate rustpython_vm;
46
extern crate wasm_bindgen;
57
extern crate web_sys;
68

7-
use rustpython_vm::VirtualMachine;
9+
use num_bigint::BigInt;
810
use rustpython_vm::compile;
9-
use rustpython_vm::pyobject::AttributeProtocol;
11+
use rustpython_vm::pyobject::{self, IdProtocol, PyObjectRef, PyResult};
12+
use rustpython_vm::VirtualMachine;
1013
use wasm_bindgen::prelude::*;
1114
use web_sys::console;
1215

16+
fn py_str_err(vm: &mut VirtualMachine, py_err: &PyObjectRef) -> String {
17+
vm.to_pystr(&py_err)
18+
.unwrap_or_else(|_| "Error, and error getting error message".into())
19+
}
20+
21+
fn py_to_js(vm: &mut VirtualMachine, py_obj: &PyObjectRef) -> JsValue {
22+
use pyobject::PyObjectKind;
23+
let py_obj = py_obj.borrow();
24+
match py_obj.kind {
25+
PyObjectKind::String { ref value } => value.into(),
26+
PyObjectKind::Integer { ref value } => {
27+
if let Some(ref typ) = py_obj.typ {
28+
if typ.is(&vm.ctx.bool_type()) {
29+
let out_bool = value == &BigInt::new(num_bigint::Sign::Plus, vec![1]);
30+
return out_bool.into();
31+
}
32+
}
33+
let int = vm.ctx.new_int(value.clone());
34+
rustpython_vm::obj::objfloat::make_float(vm, &int)
35+
.unwrap()
36+
.into()
37+
}
38+
PyObjectKind::Float { ref value } => JsValue::from_f64(*value),
39+
PyObjectKind::Bytes { ref value } => {
40+
let arr = js_sys::Uint8Array::new(&JsValue::from(value.len() as u32));
41+
for (i, byte) in value.iter().enumerate() {
42+
console::log_1(&JsValue::from(i as u32));
43+
js_sys::Reflect::set(&arr, &JsValue::from(i as u32), &JsValue::from(*byte))
44+
.unwrap();
45+
}
46+
arr.into()
47+
}
48+
PyObjectKind::Sequence { ref elements } => {
49+
let arr = js_sys::Array::new();
50+
for val in elements {
51+
arr.push(&py_to_js(vm, val));
52+
}
53+
arr.into()
54+
}
55+
PyObjectKind::Dict { ref elements } => {
56+
let obj = js_sys::Object::new();
57+
for (key, (_, val)) in elements {
58+
js_sys::Reflect::set(&obj, &key.into(), &py_to_js(vm, val))
59+
.expect("couldn't set property of object");
60+
}
61+
obj.into()
62+
}
63+
PyObjectKind::None => JsValue::UNDEFINED,
64+
_ => JsValue::UNDEFINED,
65+
}
66+
}
67+
68+
fn eval(vm: &mut VirtualMachine, source: &str) -> PyResult {
69+
let code_obj = compile::compile(vm, &source.to_string(), compile::Mode::Exec, None)?;
70+
71+
let builtins = vm.get_builtin_scope();
72+
let vars = vm.context().new_scope(Some(builtins));
73+
vm.run_code_obj(code_obj, vars)
74+
}
75+
76+
#[wasm_bindgen]
77+
pub fn eval_py(source: &str) -> Result<JsValue, JsValue> {
78+
let mut vm = VirtualMachine::new();
79+
80+
vm.ctx.set_attr(
81+
&vm.builtins,
82+
"print",
83+
vm.context().new_rustfunc(wasm_builtins::builtin_log),
84+
);
85+
86+
eval(&mut vm, source)
87+
.map(|value| py_to_js(&mut vm, &value))
88+
.map_err(|err| py_str_err(&mut vm, &err).into())
89+
}
90+
1391
#[wasm_bindgen]
14-
pub fn run_code(source: &str) -> () {
92+
pub fn run_code(source: &str) -> Result<JsValue, JsValue> {
1593
//add hash in here
1694
console::log_1(&"Running RustPython".into());
1795
console::log_1(&"Running code:".into());
@@ -20,14 +98,28 @@ pub fn run_code(source: &str) -> () {
2098
let mut vm = VirtualMachine::new();
2199
// We are monkey-patching the builtin print to use console.log
22100
// TODO: moneky-patch sys.stdout instead, after print actually uses sys.stdout
23-
vm.builtins.set_attr("print", vm.context().new_rustfunc(wasm_builtins::builtin_print));
24-
25-
let code_obj = compile::compile(&mut vm, &source.to_string(), compile::Mode::Exec, None);
101+
vm.ctx.set_attr(
102+
&vm.builtins,
103+
"print",
104+
vm.context().new_rustfunc(wasm_builtins::builtin_print),
105+
);
26106

27-
let builtins = vm.get_builtin_scope();
28-
let vars = vm.context().new_scope(Some(builtins));
29-
match vm.run_code_obj(code_obj.unwrap(), vars) {
30-
Ok(_value) => console::log_1(&"Execution successful".into()),
31-
Err(_) => console::log_1(&"Execution failed".into()),
107+
match eval(&mut vm, source) {
108+
Ok(value) => {
109+
console::log_1(&"Execution successful".into());
110+
match value.borrow().kind {
111+
pyobject::PyObjectKind::None => {}
112+
_ => {
113+
if let Ok(text) = vm.to_pystr(&value) {
114+
wasm_builtins::print_to_html(&text);
115+
}
116+
}
117+
}
118+
Ok(JsValue::UNDEFINED)
119+
}
120+
Err(err) => {
121+
console::log_1(&"Execution failed".into());
122+
Err(py_str_err(&mut vm, &err).into())
123+
}
32124
}
33125
}

wasm/src/wasm_builtins.rs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,25 @@
44
//! desktop.
55
//! Implements functions listed here: https://docs.python.org/3/library/builtins.html
66
//!
7+
extern crate js_sys;
78
extern crate wasm_bindgen;
89
extern crate web_sys;
910

11+
use js_sys::Array;
1012
use rustpython_vm::obj::objstr;
13+
use rustpython_vm::pyobject::{PyFuncArgs, PyResult};
1114
use rustpython_vm::VirtualMachine;
12-
use rustpython_vm::pyobject::{ PyFuncArgs, PyResult };
1315
use wasm_bindgen::JsCast;
14-
use web_sys::{HtmlTextAreaElement, window};
16+
use web_sys::{console, window, HtmlTextAreaElement};
1517

1618
// The HTML id of the textarea element that act as our STDOUT
1719
const CONSOLE_ELEMENT_ID: &str = "console";
1820

19-
fn print_to_html(text: &str) {
21+
pub fn print_to_html(text: &str) {
2022
let document = window().unwrap().document().unwrap();
21-
let element = document.get_element_by_id(CONSOLE_ELEMENT_ID).expect("Can't find the console textarea");
23+
let element = document
24+
.get_element_by_id(CONSOLE_ELEMENT_ID)
25+
.expect("Can't find the console textarea");
2226
let textarea = element.dyn_ref::<HtmlTextAreaElement>().unwrap();
2327
let value = textarea.value();
2428
textarea.set_value(&format!("{}{}", value, text));
@@ -38,3 +42,14 @@ pub fn builtin_print(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
3842
}
3943
Ok(vm.get_none())
4044
}
45+
46+
pub fn builtin_log(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
47+
let arr = Array::new();
48+
for a in args.args {
49+
let v = vm.to_str(&a)?;
50+
let s = objstr::get_value(&v);
51+
arr.push(&s.into());
52+
}
53+
console::log(&arr);
54+
Ok(vm.get_none())
55+
}

0 commit comments

Comments
 (0)