Skip to content

Commit ada92d3

Browse files
committed
Add conflicting files
1 parent 2968982 commit ada92d3

File tree

3 files changed

+259
-0
lines changed

3 files changed

+259
-0
lines changed

wasm/app/index.html

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8" />
5+
<title>RustPython Demo</title>
6+
<style type="text/css" media="screen">
7+
textarea {
8+
font-family: monospace;
9+
resize: vertical;
10+
}
11+
12+
#code {
13+
height: 35vh;
14+
width: 95vw;
15+
}
16+
17+
#console {
18+
height: 35vh;
19+
width: 95vw;
20+
}
21+
22+
#run-btn {
23+
width: 6em;
24+
height: 2em;
25+
font-size: 24px;
26+
}
27+
28+
#error {
29+
color: tomato;
30+
margin-top: 10px;
31+
font-family: monospace;
32+
}
33+
</style>
34+
</head>
35+
<body>
36+
<h1>RustPython Demo</h1>
37+
<p>
38+
RustPython is a Python interpreter writter in Rust. This demo is
39+
compiled from Rust to WebAssembly so it runs in the browser
40+
</p>
41+
<p>Please input your python code below and click <kbd>Run</kbd>:</p>
42+
<p>
43+
Alternatively, open up your browser's devtools and play with
44+
<code>rp.eval_py('print("a")')</code>
45+
</p>
46+
<textarea id="code">
47+
n1 = 0
48+
n2 = 1
49+
count = 0
50+
until = 10
51+
52+
print("These are the first " + str(until) + " number in a Fibonacci sequence:")
53+
54+
while count < until:
55+
print(n1)
56+
n1, n2 = n2, n1 + n2
57+
count += 1
58+
59+
</textarea>
60+
<button id="run-btn">Run &#9655;</button>
61+
<div id="error"></div>
62+
<script src="./bootstrap.js"></script>
63+
<h3>Standard Output</h3>
64+
<textarea id="console">Loading...</textarea>
65+
66+
<p>Here's some info regarding the <code>rp.eval_py()</code> function</p>
67+
<ul>
68+
<li>
69+
You can return variables from python and get them returned to
70+
JS, with the only requirement being that they're serializable
71+
with <code>json.dumps</code>.
72+
</li>
73+
<li>
74+
You can pass an object as the second argument to the function,
75+
and that will be available in python as the variable
76+
<code>js_vars</code>. Again, only values that can be serialized
77+
with <code>JSON.stringify()</code> will go through.
78+
</li>
79+
</ul>
80+
81+
<!-- "Fork me on GitHub" banner -->
82+
<a href="https://github.com/RustPython/RustPython"
83+
><img
84+
style="position: absolute; top: 0; right: 0; border: 0;"
85+
src="https://s3.amazonaws.com/github/ribbons/forkme_right_green_007200.png"
86+
alt="Fork me on GitHub"
87+
/></a>
88+
</body>
89+
</html>

wasm/app/index.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import * as rp from 'rustpython_wasm';
2+
3+
// so people can play around with it
4+
window.rp = rp;
5+
6+
function runCodeFromTextarea(_) {
7+
const consoleElement = document.getElementById('console');
8+
const errorElement = document.getElementById('error');
9+
10+
// Clean the console and errors
11+
consoleElement.value = '';
12+
errorElement.textContent = '';
13+
14+
const code = document.getElementById('code').value;
15+
try {
16+
rp.run_from_textbox(code);
17+
} catch (e) {
18+
errorElement.textContent = e;
19+
console.error(e);
20+
}
21+
}
22+
23+
document
24+
.getElementById('run-btn')
25+
.addEventListener('click', runCodeFromTextarea);
26+
27+
runCodeFromTextarea(); // Run once for demo

wasm/src/lib.rs

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
mod wasm_builtins;
2+
3+
extern crate js_sys;
4+
extern crate rustpython_vm;
5+
extern crate wasm_bindgen;
6+
extern crate web_sys;
7+
8+
use rustpython_vm::compile;
9+
use rustpython_vm::pyobject::{self, PyObjectRef, PyResult};
10+
use rustpython_vm::VirtualMachine;
11+
use wasm_bindgen::prelude::*;
12+
use web_sys::console;
13+
14+
fn py_str_err(vm: &mut VirtualMachine, py_err: &PyObjectRef) -> String {
15+
vm.to_pystr(&py_err)
16+
.unwrap_or_else(|_| "Error, and error getting error message".into())
17+
}
18+
19+
fn py_to_js(vm: &mut VirtualMachine, py_obj: PyObjectRef) -> JsValue {
20+
let dumps = rustpython_vm::import::import(
21+
vm,
22+
std::path::PathBuf::default(),
23+
"json",
24+
&Some("dumps".into()),
25+
)
26+
.expect("Couldn't get json.dumps function");
27+
match vm.invoke(dumps, pyobject::PyFuncArgs::new(vec![py_obj], vec![])) {
28+
Ok(value) => {
29+
let json = vm.to_pystr(&value).unwrap();
30+
js_sys::JSON::parse(&json).unwrap_or(JsValue::UNDEFINED)
31+
}
32+
Err(_) => JsValue::UNDEFINED,
33+
}
34+
}
35+
36+
fn js_to_py(vm: &mut VirtualMachine, js_val: JsValue) -> PyObjectRef {
37+
let json = match js_sys::JSON::stringify(&js_val) {
38+
Ok(json) => String::from(json),
39+
Err(_) => return vm.get_none(),
40+
};
41+
42+
let loads = rustpython_vm::import::import(
43+
vm,
44+
std::path::PathBuf::default(),
45+
"json",
46+
&Some("loads".into()),
47+
)
48+
.expect("Couldn't get json.loads function");
49+
50+
let py_json = vm.new_str(json);
51+
52+
vm.invoke(loads, pyobject::PyFuncArgs::new(vec![py_json], vec![]))
53+
// can safely unwrap because we know it's valid JSON
54+
.unwrap()
55+
}
56+
57+
fn eval<F>(vm: &mut VirtualMachine, source: &str, setup_scope: F) -> PyResult
58+
where
59+
F: Fn(&mut VirtualMachine, &PyObjectRef),
60+
{
61+
// HACK: if the code doesn't end with newline it crashes.
62+
let mut source = source.to_string();
63+
if !source.ends_with('\n') {
64+
source.push('\n');
65+
}
66+
67+
let code_obj = compile::compile(vm, &source, compile::Mode::Exec, None)?;
68+
69+
let builtins = vm.get_builtin_scope();
70+
let mut vars = vm.context().new_scope(Some(builtins));
71+
72+
setup_scope(vm, &mut vars);
73+
74+
vm.run_code_obj(code_obj, vars)
75+
}
76+
77+
#[wasm_bindgen]
78+
pub fn eval_py(source: &str, js_injections: Option<js_sys::Object>) -> Result<JsValue, JsValue> {
79+
if let Some(js_injections) = js_injections.clone() {
80+
if !js_injections.is_object() {
81+
return Err(js_sys::TypeError::new("The second argument must be an object").into());
82+
}
83+
}
84+
85+
let mut vm = VirtualMachine::new();
86+
87+
vm.ctx.set_attr(
88+
&vm.builtins,
89+
"print",
90+
vm.context()
91+
.new_rustfunc(wasm_builtins::builtin_print_console),
92+
);
93+
94+
let res = eval(&mut vm, source, |vm, vars| {
95+
let injections = if let Some(js_injections) = js_injections.clone() {
96+
js_to_py(vm, js_injections.into())
97+
} else {
98+
vm.new_dict()
99+
};
100+
101+
vm.ctx.set_item(vars, "js_vars", injections);
102+
});
103+
104+
res.map(|value| py_to_js(&mut vm, value))
105+
.map_err(|err| py_str_err(&mut vm, &err).into())
106+
}
107+
108+
#[wasm_bindgen]
109+
pub fn run_from_textbox(source: &str) -> Result<JsValue, JsValue> {
110+
//add hash in here
111+
console::log_1(&"Running RustPython".into());
112+
console::log_1(&"Running code:".into());
113+
console::log_1(&source.to_string().into());
114+
115+
let mut vm = VirtualMachine::new();
116+
117+
// We are monkey-patching the builtin print to use console.log
118+
// TODO: monkey-patch sys.stdout instead, after print actually uses sys.stdout
119+
vm.ctx.set_attr(
120+
&vm.builtins,
121+
"print",
122+
vm.context().new_rustfunc(wasm_builtins::builtin_print_html),
123+
);
124+
125+
match eval(&mut vm, source, |_, _| {}) {
126+
Ok(value) => {
127+
console::log_1(&"Execution successful".into());
128+
match value.borrow().kind {
129+
pyobject::PyObjectKind::None => {}
130+
_ => {
131+
if let Ok(text) = vm.to_pystr(&value) {
132+
wasm_builtins::print_to_html(&text);
133+
}
134+
}
135+
}
136+
Ok(JsValue::UNDEFINED)
137+
}
138+
Err(err) => {
139+
console::log_1(&"Execution failed".into());
140+
Err(py_str_err(&mut vm, &err).into())
141+
}
142+
}
143+
}

0 commit comments

Comments
 (0)