Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
03c619f
Rearrange website directory and change webpack config
coolreader18 Dec 19, 2018
4c32693
Switch to using one workspace, move profile to root Cargo.toml
coolreader18 Dec 18, 2018
ca30ebc
Genericize the wasm lib to not be specifically for the demo
coolreader18 Dec 22, 2018
2ea9dca
Add example, change some stuff in the demo to align with example
coolreader18 Dec 23, 2018
d54d2b1
Make scripts executable
coolreader18 Dec 23, 2018
840c507
Improve UX for demo
coolreader18 Dec 23, 2018
ab23f2c
Use str.format for the demo
coolreader18 Dec 23, 2018
24507de
Re-add conflicting files
coolreader18 Dec 24, 2018
ffcd40b
Remove conflicting files
coolreader18 Dec 24, 2018
d1d9585
Change the instances of Fn(..) -> PyResult to a RustPyFunc trait alias
coolreader18 Dec 24, 2018
63b3f3e
Fix blanket impl of RustPyFunc
coolreader18 Dec 24, 2018
132930e
Allow passing closures from JS to python via `vars`
coolreader18 Dec 26, 2018
e0959b9
Implement error conversion for js_to_py
coolreader18 Dec 26, 2018
94d6a91
Fix js_to_py with JS `undefined`
coolreader18 Dec 26, 2018
c38796b
Add some documentation for functions on the demo site
coolreader18 Dec 26, 2018
877206d
Switch from shell to npm scripts for demo using webpack and @wasm-too…
coolreader18 Dec 27, 2018
2968982
Change README portion for compiling wasm
coolreader18 Dec 27, 2018
ada92d3
Add conflicting files
coolreader18 Dec 19, 2018
dfadd03
Remove (now outdated) previously conflicting files
coolreader18 Dec 27, 2018
396842e
Readd conflicting files
coolreader18 Dec 19, 2018
8303743
Remove RustPyFunc trait, just use Fn(..) everywhere
coolreader18 Dec 27, 2018
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
Prev Previous commit
Next Next commit
Readd conflicting files
  • Loading branch information
coolreader18 committed Dec 27, 2018
commit 396842ef9dc36a774ffcd1bdc79ffa4549b52369
89 changes: 89 additions & 0 deletions wasm/app/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>RustPython Demo</title>
<style type="text/css" media="screen">
textarea {
font-family: monospace;
resize: vertical;
}

#code {
height: 35vh;
width: 95vw;
}

#console {
height: 35vh;
width: 95vw;
}

#run-btn {
width: 6em;
height: 2em;
font-size: 24px;
}

#error {
color: tomato;
margin-top: 10px;
font-family: monospace;
}
</style>
</head>
<body>
<h1>RustPython Demo</h1>
<p>
RustPython is a Python interpreter writter in Rust. This demo is
compiled from Rust to WebAssembly so it runs in the browser
</p>
<p>Please input your python code below and click <kbd>Run</kbd>:</p>
<p>
Alternatively, open up your browser's devtools and play with
<code>rp.eval_py('print("a")')</code>
</p>
<textarea id="code">
n1 = 0
n2 = 1
count = 0
until = 10

print("These are the first " + str(until) + " number in a Fibonacci sequence:")

while count < until:
print(n1)
n1, n2 = n2, n1 + n2
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>

<p>Here's some info regarding the <code>rp.eval_py()</code> function</p>
<ul>
<li>
You can return variables from python and get them returned to
JS, with the only requirement being that they're serializable
with <code>json.dumps</code>.
</li>
<li>
You can pass an object as the second argument to the function,
and that will be available in python as the variable
<code>js_vars</code>. Again, only values that can be serialized
with <code>JSON.stringify()</code> will go through.
</li>
</ul>

<!-- "Fork me on GitHub" banner -->
<a href="https://github.com/RustPython/RustPython"
><img
style="position: absolute; top: 0; right: 0; border: 0;"
src="https://s3.amazonaws.com/github/ribbons/forkme_right_green_007200.png"
alt="Fork me on GitHub"
/></a>
</body>
</html>
27 changes: 27 additions & 0 deletions wasm/app/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import * as rp from 'rustpython_wasm';

// so people can play around with it
window.rp = rp;

function runCodeFromTextarea(_) {
const consoleElement = document.getElementById('console');
const errorElement = document.getElementById('error');

// 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);

runCodeFromTextarea(); // Run once for demo
143 changes: 143 additions & 0 deletions wasm/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
mod wasm_builtins;

extern crate js_sys;
extern crate rustpython_vm;
extern crate wasm_bindgen;
extern crate web_sys;

use rustpython_vm::compile;
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<F>(vm: &mut VirtualMachine, source: &str, setup_scope: F) -> PyResult
where
F: Fn(&mut VirtualMachine, &PyObjectRef),
{
// HACK: if the code doesn't end with newline it crashes.
let mut source = source.to_string();
if !source.ends_with('\n') {
source.push('\n');
}

let code_obj = compile::compile(vm, &source, compile::Mode::Exec, None)?;

let builtins = vm.get_builtin_scope();
let mut vars = vm.context().new_scope(Some(builtins));

setup_scope(vm, &mut vars);

vm.run_code_obj(code_obj, vars)
}

#[wasm_bindgen]
pub fn eval_py(source: &str, js_injections: Option<js_sys::Object>) -> Result<JsValue, JsValue> {
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(
&vm.builtins,
"print",
vm.context()
.new_rustfunc(wasm_builtins::builtin_print_console),
);

let res = eval(&mut vm, source, |vm, vars| {
let injections = if let Some(js_injections) = js_injections.clone() {
js_to_py(vm, js_injections.into())
} else {
vm.new_dict()
};

vm.ctx.set_item(vars, "js_vars", injections);
});

res.map(|value| py_to_js(&mut vm, value))
.map_err(|err| py_str_err(&mut vm, &err).into())
}

#[wasm_bindgen]
pub fn run_from_textbox(source: &str) -> Result<JsValue, JsValue> {
//add hash in here
console::log_1(&"Running RustPython".into());
console::log_1(&"Running code:".into());
console::log_1(&source.to_string().into());

let mut vm = VirtualMachine::new();

// We are monkey-patching the builtin print to use console.log
// TODO: monkey-patch sys.stdout instead, after print actually uses sys.stdout
vm.ctx.set_attr(
&vm.builtins,
"print",
vm.context().new_rustfunc(wasm_builtins::builtin_print_html),
);

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())
}
}
}