Update documentation folder and readme.
No files matched your search
@@ -105,99 +105,31 @@ There also are some unit tests, you can run those with cargo:
|
||||
$ cargo test --all
|
||||
```
|
||||
|
||||
# Using another standard library
|
||||
# Using a standard library
|
||||
|
||||
As of now the standard library is under construction.
|
||||
As of now the standard library is under construction. You can
|
||||
use a standard library by setting the RUSTPYTHONPATH environment
|
||||
variable.
|
||||
|
||||
To do this, follow this method:
|
||||
|
||||
```shell
|
||||
$ export RUSTPYTHONPATH=~/GIT/RustPython/Lib
|
||||
$ cargo run -- -c 'import xdrlib'
|
||||
```
|
||||
|
||||
You can play around
|
||||
with other standard libraries for python. For example,
|
||||
the [ouroboros library](https://github.com/pybee/ouroboros).
|
||||
|
||||
To do this, follow this method:
|
||||
|
||||
```shell
|
||||
$ cd ~/GIT
|
||||
$ git clone git@github.com:pybee/ouroboros.git
|
||||
$ export RUSTPYTHONPATH=~/GIT/ouroboros/ouroboros
|
||||
$ cd RustPython
|
||||
$ cargo run -- -c 'import statistics'
|
||||
```
|
||||
|
||||
# Compiling to WebAssembly
|
||||
|
||||
At this stage RustPython only has preliminary support for web assembly. The instructions here are intended for developers or those wishing to run a toy example.
|
||||
|
||||
## Setup
|
||||
|
||||
To get started, install [wasm-pack](https://rustwasm.github.io/wasm-pack/installer/) and `npm`. ([wasm-bindgen](https://rustwasm.github.io/wasm-bindgen/whirlwind-tour/basic-usage.html) should be installed by `wasm-pack`. if not, install it yourself)
|
||||
|
||||
<!-- Using `rustup` add the compile target `wasm32-unknown-emscripten`. To do so you will need to have [rustup](https://rustup.rs/) installed.
|
||||
|
||||
```bash
|
||||
rustup target add wasm32-unknown-emscripten
|
||||
```
|
||||
|
||||
Next, install `emsdk`:
|
||||
|
||||
```bash
|
||||
curl https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-portable.tar.gz | tar -zxv
|
||||
cd emsdk-portable/
|
||||
./emsdk update
|
||||
./emsdk install sdk-incoming-64bit
|
||||
./emsdk activate sdk-incoming-64bit
|
||||
``` -->
|
||||
|
||||
## Build
|
||||
|
||||
Move into the `wasm` directory. This directory contains a library crate for interop
|
||||
with python to rust to js and back in `wasm/lib`, the demo website found at
|
||||
https://rustpython.github.io/demo in `wasm/demo`, and an example of how to use
|
||||
the crate as a library in one's own JS app in `wasm/example`.
|
||||
|
||||
```sh
|
||||
cd wasm
|
||||
```
|
||||
|
||||
Go to the demo directory. This is the best way of seeing the changes made to either
|
||||
the library or the JS demo, as the `rustpython_wasm` module is set to the global
|
||||
JS variable `rp` on the website.
|
||||
|
||||
```sh
|
||||
cd demo
|
||||
```
|
||||
|
||||
Now, start the webpack development server. It'll compile the crate and then
|
||||
the demo app. This will likely take a long time, both the wasm-pack portion and
|
||||
the webpack portion (from after it says "Your crate has been correctly compiled"),
|
||||
so be patient.
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
You can now open the webpage on https://localhost:8080 and Python code in either
|
||||
the text box or browser devtools with:
|
||||
|
||||
```js
|
||||
rp.pyEval(
|
||||
`
|
||||
print(js_vars['a'] * 9)
|
||||
`,
|
||||
{
|
||||
vars: {
|
||||
a: 9
|
||||
}
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
Alternatively, you can run `npm run build` to build the app once, without watching
|
||||
for changes, or `npm run dist` to build the app in release mode, both for the
|
||||
crate and webpack.
|
||||
[See this doc](wasm/README.md)
|
||||
|
||||
# Code style
|
||||
|
||||
The code style used is the default [rustfmt](https://github.com/rust-lang/rustfmt) codestyle. Please format your code accordingly.
|
||||
We also use [clippy](https://github.com/rust-lang/rust-clippy) to detect rust code issues.
|
||||
|
||||
# Community
|
||||
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
Byterun
|
||||
|
||||
* Builtins are exposed to frame.f_builtins
|
||||
* f_builtins is assigned during frame creation,
|
||||
self.f_builtins = f_locals['__builtins__']
|
||||
if hasattr(self.f_builtins, '__dict__'):
|
||||
self.f_builtins = self.f_builtins.__dict__
|
||||
* f_locals has a __`____builtins___` field which is directly the `__builtins__` module
|
||||
|
||||
|
||||
Jaspy
|
||||
|
||||
* The `module()` function creates either a NativeModule or PythonModule
|
||||
* The objects in the module are PyType.native
|
||||
* The function call is abstracted as a `call` function, which handles different
|
||||
|
||||
* IMPORT_NAME depends on `__import__()` in builtins
|
||||
|
||||
TODO:
|
||||
|
||||
* Implement a new type NativeFunction
|
||||
* Wrap a function pointer in NativeFunction
|
||||
* Refactor the CALL_FUNCTION case so it can call both python function and native function
|
||||
* During frame creation, force push a native function `print` into the namespace
|
||||
* Modify LOAD_* so they can search for names in builtins
|
||||
|
||||
* Create a module type
|
||||
* In VM initialization, load the builtins module into locals
|
||||
* During frame creation, create a field that contains the builtins dict
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
https://wiki.python.org/moin/SimplePrograms
|
||||
Simple HTTP Server
|
||||
bottle.py
|
||||
http://pypi-ranking.info/alltime
|
||||
- simplejson
|
||||
- pep8
|
||||
- httplib2
|
||||
- argparse
|
||||
|
||||
http://learning-python.com/books/lp4e-examples.html
|
||||
|
||||
https://docs.python.org/3/tutorial/introduction.html#numbers
|
||||
|
||||
http://doc.pypy.org/en/latest/getting-started-dev.html#running-pypy-s-unit-tests
|
||||
|
||||
CPython Regression suite
|
||||
https://github.com/python/cpython/tree/master/Lib/test
|
||||
@@ -1,13 +0,0 @@
|
||||
Create a frame class
|
||||
change the environment to locals
|
||||
turn run into run_code -> run_frame
|
||||
|
||||
TEST
|
||||
|
||||
Create a function class
|
||||
implement MAKE_FUNCTION
|
||||
implement CALL_FUNCTION
|
||||
implement RETURN_VALUE
|
||||
|
||||
TEST function
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
# TODO: other notes should be put into here
|
||||
|
||||
# getattr()
|
||||
- Required by this opcode [LOAD_ATTR](https://docs.python.org/3/library/dis.html#opcode-LOAD_ATTR)
|
||||
- The builtin function: https://docs.python.org/3/library/functions.html?highlight=getattr#getattr
|
||||
-
|
||||
|
||||
# Memory management
|
||||
- https://docs.python.org/3.6/c-api/memory.html
|
||||
|
||||
# Bootstraping
|
||||
- http://doc.pypy.org/en/latest/coding-guide.html#our-runtime-interpreter-is-rpython
|
||||
- http://www.aosabook.org/en/pypy.html
|
||||
@@ -1,5 +0,0 @@
|
||||
Python bytecode: https://docs.python.org/3.4/library/dis.html
|
||||
Python VM in python: https://github.com/nedbat/byterun
|
||||
Python VM in JS: https://github.com/koehlma/jaspy
|
||||
|
||||
http://www.skulpt.org/
|
||||
@@ -1,43 +0,0 @@
|
||||
# Sharing plan
|
||||
|
||||
## Topics
|
||||
|
||||
* Python architecture
|
||||
* Compiler -> VM
|
||||
* Flavors
|
||||
* Cpython
|
||||
* Pypy
|
||||
* Byterun
|
||||
* JSapy (?)
|
||||
* Why Rust
|
||||
* Security
|
||||
* Learning
|
||||
* Implementation plan
|
||||
* VM first
|
||||
* Compiler second
|
||||
|
||||
* Tools for study
|
||||
* dis doc
|
||||
* byterun doc
|
||||
* bytrun code
|
||||
* cpython source
|
||||
|
||||
---
|
||||
* Python VM
|
||||
* Stack machine
|
||||
|
||||
* Load add print
|
||||
* dis
|
||||
* Interpreter loop
|
||||
* Python Types
|
||||
|
||||
* Control flow
|
||||
* Jump
|
||||
* If
|
||||
* Loop
|
||||
|
||||
---
|
||||
* Function call
|
||||
* Frame
|
||||
* Builtins
|
||||
|
||||
@@ -1,529 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Flavors (powered by MDSlides)</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<style>/* Color */
|
||||
.remark-slide-container {
|
||||
background-color: black;
|
||||
}
|
||||
.remark-slide-content {
|
||||
background-color:#0d0d0d;
|
||||
color:lightgrey;
|
||||
background-size:cover;
|
||||
background-position:0,0;
|
||||
font-family: Arial,sans-serif;
|
||||
}
|
||||
|
||||
a {
|
||||
/* color:lime; */
|
||||
color: #ff8100
|
||||
}
|
||||
|
||||
.remark-inline-code {
|
||||
background-color:#1B1B1B;
|
||||
border-radius: 0.35rem;
|
||||
}
|
||||
|
||||
/* Font size */
|
||||
|
||||
html {font-size:22px;}
|
||||
|
||||
.remark-slide-content h1 {
|
||||
margin: 0;
|
||||
font-size: 3.998rem;
|
||||
}
|
||||
|
||||
.remark-slide-content h2 {font-size: 2.827rem;}
|
||||
|
||||
.remark-slide-content h3 {font-size: 1.999rem;}
|
||||
|
||||
.remark-slide-content h4 {font-size: 1.414rem;}
|
||||
|
||||
.remark-slide-content small, .font_small {font-size: 0.707rem;}
|
||||
|
||||
.remark-slide-content li,p{
|
||||
font-size: 1.414rem;
|
||||
line-height: 1.999rem;
|
||||
margin:0px;
|
||||
}
|
||||
|
||||
.remark-slide-content li,p,table {
|
||||
font-size: 1.414rem;
|
||||
line-height: 1.999rem;
|
||||
border-spacing: 0.707rem;
|
||||
}
|
||||
|
||||
.remark-slide-content table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.remark-slide-content table thead th{
|
||||
border-bottom: solid;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.remark-slide-content table td{
|
||||
padding-right: 0.707rem;
|
||||
}
|
||||
|
||||
.remark-slide-content .footnote {
|
||||
line-height: 1.414rem;
|
||||
}
|
||||
|
||||
.remark-code {
|
||||
background-color: black;
|
||||
font-size: 0.9998rem;
|
||||
word-wrap:auto;
|
||||
font-family: "courier new", monospace;
|
||||
}
|
||||
|
||||
/* Images */
|
||||
|
||||
img { max-width:100%;
|
||||
max-height:100%;
|
||||
}
|
||||
|
||||
/* Custom page classes */
|
||||
|
||||
.bleed {
|
||||
background-size:cover !important;
|
||||
background-position:center !important;
|
||||
}
|
||||
|
||||
/* Custom block/inline classes */
|
||||
|
||||
.footnote {
|
||||
font-size: 0.707rem;
|
||||
}
|
||||
|
||||
.red {
|
||||
color:red;
|
||||
}
|
||||
|
||||
.green {
|
||||
color:green;
|
||||
}
|
||||
|
||||
.halfwidth {
|
||||
/* for images */
|
||||
width: 49%;
|
||||
display:inline-block;
|
||||
}
|
||||
|
||||
.fullwidth {
|
||||
/* for images */
|
||||
width: 100%;
|
||||
display:inline-block;
|
||||
}
|
||||
|
||||
.column-2 {
|
||||
display:inline-block;
|
||||
width: 49.5%;
|
||||
}
|
||||
|
||||
.float-right-half {
|
||||
width: 50%;
|
||||
float:right;
|
||||
}
|
||||
|
||||
.text-bg h1 {
|
||||
background-color: rgba(0,0,0,0.5);
|
||||
}
|
||||
</style>
|
||||
<style>/*
|
||||
|
||||
Monokai Sublime style. Derived from Monokai by noformnocontent http://nn.mit-license.org/
|
||||
|
||||
*/
|
||||
|
||||
.hljs default {
|
||||
color: #f8f8f2;
|
||||
background: #23241f;
|
||||
font-family: "courier new", monospace;
|
||||
}
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding: 0.5em;
|
||||
background: #23241f !important;
|
||||
}
|
||||
|
||||
.hljs,
|
||||
.hljs-tag,
|
||||
.hljs-subst {
|
||||
color: #f8f8f2 !important;
|
||||
}
|
||||
|
||||
.hljs-strong,
|
||||
.hljs-emphasis {
|
||||
color: #a8a8a2 !important;
|
||||
}
|
||||
|
||||
.hljs-bullet,
|
||||
.hljs-quote,
|
||||
.hljs-number,
|
||||
.hljs-regexp,
|
||||
.hljs-literal,
|
||||
.hljs-link {
|
||||
color: #ae81ff !important;
|
||||
}
|
||||
|
||||
.hljs-code,
|
||||
.hljs-title,
|
||||
.hljs-section,
|
||||
.hljs-selector-class {
|
||||
color: #a6e22e !important;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic !important;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-name,
|
||||
.hljs-attr {
|
||||
color: #f92672 !important;
|
||||
}
|
||||
|
||||
.hljs-symbol,
|
||||
.hljs-attribute {
|
||||
color: #66d9ef !important;
|
||||
}
|
||||
|
||||
.hljs-params,
|
||||
.hljs-class .hljs-title {
|
||||
color: #f8f8f2 !important;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-type,
|
||||
.hljs-built_in,
|
||||
.hljs-builtin-name,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-attr,
|
||||
.hljs-selector-pseudo,
|
||||
.hljs-addition,
|
||||
.hljs-variable,
|
||||
.hljs-template-variable {
|
||||
color: #e6db74 !important;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-deletion,
|
||||
.hljs-meta {
|
||||
color: #75715e !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body onload="var slideshow = remark.create();">
|
||||
<textarea id="source">
|
||||
class: center, middle
|
||||
##Python Interpreter in Rust
|
||||
#### 2017/8/6, COSCUP
|
||||
#### Shing Lyu
|
||||
|
||||
|
||||
???
|
||||
top, middle, bottom
|
||||
left, center, right
|
||||
|
||||
---
|
||||
### About Me
|
||||
* 呂行 Shing Lyu
|
||||
* Mozilla engineer
|
||||
* Servo team
|
||||
|
||||

|
||||
class: center, middle, bleed, text-bg
|
||||
# Flavors
|
||||
|
||||
|
||||
---
|
||||
### Python Flavors
|
||||
* CPython (THE python)
|
||||
* Jython (JVM)
|
||||
* IronPython (.NET)
|
||||
* Pypy
|
||||
* Educational
|
||||
* Byterun
|
||||
* Jsapy (JS)
|
||||
* Brython (Python in browser)
|
||||
|
||||
---
|
||||
class: center, middle
|
||||
|
||||
# Why & How?
|
||||
---
|
||||
### Why rewriting Python in Rust?
|
||||
* Memory safety (?)
|
||||
* Learn about Python's internal
|
||||
* Learn to write Rust from scratch
|
||||
* FUN!
|
||||
|
||||
---
|
||||
### Implementation strategy
|
||||
* Mostly follow CPython 3.6
|
||||
* Focus on the VM first, then the compiler
|
||||
* Use the Python built-in compiler to generate bytecode
|
||||
* Focus on learning rather than usability
|
||||
|
||||
---
|
||||
### Milestones
|
||||
* Basic arithmetics
|
||||
* Variables
|
||||
* Control flows (require JUMP)
|
||||
* Function call (require call stack)
|
||||
* Built-in functions (require native code)
|
||||
* Run Python tutorial example code <= We're here
|
||||
* Exceptions
|
||||
* GC
|
||||
* Run popular libraries
|
||||
|
||||
|
||||
---
|
||||
class: center, middle, bleed, text-bg
|
||||
background-image: url('pic/car_cutaway.jpg')
|
||||
# Python Internals
|
||||
|
||||
---
|
||||
### How Python VM works
|
||||
* Stack machine
|
||||
* Call stack and frames
|
||||
* Has a NAMES list and CONSTS list
|
||||
* Has a STACK as workspace
|
||||
* Accepts Python bytecode
|
||||
* `python -m dis source.py`
|
||||
|
||||
---
|
||||
|
||||
### A simple Python code
|
||||
|
||||
```
|
||||
#!/usr/bin/env python3
|
||||
print(1+1)
|
||||
```
|
||||
|
||||
Running `python3 -m dis source.py` gives us
|
||||
|
||||
```
|
||||
1 0 LOAD_NAME 0 (print)
|
||||
3 LOAD_CONST 2 (2)
|
||||
6 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
|
||||
9 POP_TOP
|
||||
10 LOAD_CONST 1 (None)
|
||||
13 RETURN_VALUE
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### LOAD_NAME "print"
|
||||
* NAMES = ["print"]
|
||||
* CONSTS = [None, 2]
|
||||
* STACK:
|
||||
|
||||
```
|
||||
| |
|
||||
| print (native code)|
|
||||
+--------------------+
|
||||
```
|
||||
---
|
||||
### LOAD_CONST 2
|
||||
* NAMES = ["print"]
|
||||
* CONSTS = [None, 2]
|
||||
* STACK:
|
||||
|
||||
```
|
||||
| |
|
||||
| 2 |
|
||||
| print (native code)|
|
||||
+--------------------+
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### CALL_FUNCTION 1
|
||||
1. `argument = stack.pop()` (argument == 2)
|
||||
2. `function = stack.top()` (function == print)
|
||||
3. call `print(2)`
|
||||
|
||||
* NAMES = ["print"]
|
||||
* CONSTS = [None, 2]
|
||||
* STACK:
|
||||
|
||||
```
|
||||
| |
|
||||
| print (native code)|
|
||||
+--------------------+
|
||||
```
|
||||
---
|
||||
### POP_TOP
|
||||
* NAMES = ["print"]
|
||||
* CONSTS = [None, 2]
|
||||
* STACK:
|
||||
|
||||
```
|
||||
| |
|
||||
| (empty) |
|
||||
+--------------------+
|
||||
```
|
||||
|
||||
---
|
||||
### LOAD_CONST None
|
||||
* NAMES = ["print"]
|
||||
* CONSTS = [None, 2]
|
||||
* STACK:
|
||||
|
||||
```
|
||||
| |
|
||||
| None |
|
||||
+--------------------+
|
||||
```
|
||||
|
||||
---
|
||||
### RETURN_VALUE
|
||||
|
||||
(returns top of stack == None)
|
||||
|
||||
---
|
||||
|
||||
class: center, middle, bleed, text-bg
|
||||
background-image: url('pic/electronic_parts.jpg')
|
||||
# Technical Detail
|
||||
|
||||
---
|
||||
|
||||
### Bytecode format
|
||||
* `dis` output format is for human reader
|
||||
* Implementing a `dis` format parser is a waste of time
|
||||
* Emit JSON bytecode using the [bytecode](https://pypi.python.org/pypi/bytecode/0.5) module
|
||||
|
||||
```
|
||||
|
||||
code = compile(f,...) # Python built-in, return a Code object
|
||||
|
||||
bytecode.Bytecode()
|
||||
.from_code(code)
|
||||
.to_concrete_bytecode()
|
||||
```
|
||||
* Load into Rust using `serde_json`
|
||||
---
|
||||
|
||||
### Types
|
||||
* Everything is a `PyObject` in CPython
|
||||
* We'll need that class hierarchy eventually
|
||||
* Use a Rust `enum` for now
|
||||
|
||||
```
|
||||
pub enum NativeType{
|
||||
NoneType,
|
||||
Boolean(bool),
|
||||
Int(i32),
|
||||
Str(String),
|
||||
Tuple(Vec<NativeType>),
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Testing
|
||||
* `assert` is essential to for unittests
|
||||
* `assert` raises `AssertionError`
|
||||
* Use `panic!()` before we implement exception
|
||||
|
||||
```
|
||||
assert 1 == 1
|
||||
```
|
||||
```
|
||||
1 0 LOAD_CONST 0 (1)
|
||||
3 LOAD_CONST 0 (1)
|
||||
6 COMPARE_OP 2 (==)
|
||||
9 POP_JUMP_IF_TRUE 18
|
||||
12 LOAD_GLOBAL 0 (AssertionError)
|
||||
15 RAISE_VARARGS 1
|
||||
>> 18 LOAD_CONST 1 (None)
|
||||
21 RETURN_VALUE
|
||||
```
|
||||
|
||||
---
|
||||
### Native Function
|
||||
|
||||
* e.g. `print()`
|
||||
|
||||
```
|
||||
pub enum NativeType {
|
||||
NativeFunction(fn(Vec<NativeType>) -> NativeType),
|
||||
...
|
||||
}
|
||||
|
||||
match stack.pop() {
|
||||
NativeFunction(func) => return_val = func(),
|
||||
_ => ...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Next steps
|
||||
* Exceptions
|
||||
* Make it run a small but popular tool/library
|
||||
* Implement the parser
|
||||
* Figure out garbage collection
|
||||
* Performance benchmarking
|
||||
|
||||
---
|
||||
### Contribute
|
||||
|
||||
## https://github.com/shinglyu/RustPython
|
||||
|
||||

|
||||
|
||||
---
|
||||
class: middle, center
|
||||
|
||||
# Thank you
|
||||
|
||||
---
|
||||
|
||||
### References
|
||||
* [`dis` documentation](https://docs.python.org/3.4/library/dis.html)
|
||||
* [byterun](http://www.aosabook.org/en/500L/a-python-interpreter-written-in-python.html)
|
||||
* [byterun (GitHub)](https://github.com/nedbat/byterun/)
|
||||
* [cpython source code](https://github.com/python/cpython)
|
||||
|
||||
|
||||
</textarea>
|
||||
<!-- autoreload -->
|
||||
<script type="text/javascript">
|
||||
/* Force all links to be opened in new tab/window */
|
||||
window.addEventListener('load', function(){
|
||||
var links = document.getElementsByTagName('a');
|
||||
var linksList = Array.prototype.slice.call(links);
|
||||
linksList.forEach(function(anchor_elem){
|
||||
anchor_elem.setAttribute('target', '_blank');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
document.write(unescape("%3Cscript type='text/javascript'%3E" + "require%3Dfunction%28e%2Ct%2Cr%29%7Bfunction%20a%28r%2Cn%29%7Bif%28%21t%5Br%5D%29%7Bif%28%21e%5Br%5D%29%7Bvar%20s%3D%22function%22%3D%3Dtypeof%20require%26%26require%3Bif%28%21n%26%26s%29return%20s%28r%2C%210%29%3Bif%28i%29return%20i%28r%2C%210%29%3Bthrow%20new%20Error%28%22Cannot%20find%20module%20%27%22+r+%22%27%22%29%7Dvar%20l%3Dt%5Br%5D%3D%7Bexports%3A%7B%7D%7D%3Be%5Br%5D%5B0%5D.call%28l.exports%2Cfunction%28t%29%7Bvar%20i%3De%5Br%5D%5B1%5D%5Bt%5D%3Breturn%20a%28i%3Fi%3At%29%7D%2Cl%2Cl.exports%29%7Dreturn%20t%5Br%5D.exports%7Dfor%28var%20i%3D%22function%22%3D%3Dtypeof%20require%26%26require%2Cn%3D0%3Bn%3Cr.length%3Bn++%29a%28r%5Bn%5D%29%3Breturn%20a%7D%28%7B%22components/printing%22%3A%5Bfunction%28e%2Ct%29%7Bt.exports%3De%28%22yoGRCZ%22%29%7D%2C%7B%7D%5D%2CyoGRCZ%3A%5Bfunction%28e%2Ct%29%7Bfunction%20r%28%29%7B%7Dvar%20a%3De%28%22events%22%29.EventEmitter%2Ci%3De%28%22components/styler%22%29%2Cn%3D%22landscape%22%2Cs%3D%22portrait%22%2Cl%3D681%2Co%3D908%3Br.prototype%3Dnew%20a%2Cr.prototype.init%3Dfunction%28%29%7Bvar%20e%3Dthis%3Breturn%20this.setPageOrientation%28n%29%2Cwindow.matchMedia%3F%28window.matchMedia%28%22print%22%29.addListener%28function%28t%29%7Be.onPrint%28t%29%7D%29%2Cvoid%200%29%3A%211%7D%2Cr.prototype.onPrint%3Dfunction%28e%29%7Be.matches%26%26this.emit%28%22print%22%2C%7BisPortrait%3A%22portrait%22%3D%3D%3Dthis._orientation%2CpageHeight%3Athis._pageHeight%2CpageWidth%3Athis._pageWidth%7D%29%7D%2Cr.prototype.setPageOrientation%3Dfunction%28e%29%7Bif%28e%3D%3D%3Ds%29this._pageHeight%3Do%2Cthis._pageWidth%3Dl%3Belse%7Bif%28e%21%3D%3Dn%29throw%20new%20Error%28%22Unknown%20print%20orientation%3A%20%22+e%29%3Bthis._pageHeight%3Dl%2Cthis._pageWidth%3Do%7Dthis._orientation%3De%2Ci.setPageSize%28this._pageWidth+%22px%20%22+this._pageHeight+%22px%22%29%7D%2Ct.exports%3Dnew%20r%7D%2C%7Bevents%3A1%2C%22components/styler%22%3A%22syTcZF%22%7D%5D%2C%22components/slide-number%22%3A%5Bfunction%28e%2Ct%29%7Bt.exports%3De%28%22GSZq7a%22%29%7D%2C%7B%7D%5D%2CGSZq7a%3A%5Bfunction%28e%2Ct%29%7Bfunction%20r%28e%2Ct%29%7Bvar%20r%3Dthis%3Br.slide%3De%2Cr.slideshow%3Dt%2Cr.element%3Ddocument.createElement%28%22div%22%29%2Cr.element.className%3D%22remark-slide-number%22%2Cr.element.innerHTML%3Da%28r.slide%2Cr.slideshow%29%7Dfunction%20a%28e%2Ct%29%7Bvar%20r%3Dt.getSlideNumberFormat%28%29%2Ca%3Dt.getSlides%28%29%2Cn%3Di%28e%2Ct%29%2Cs%3Di%28a%5Ba.length-1%5D%2Ct%29%3Breturn%22function%22%3D%3Dtypeof%20r%3Fr.call%28t%2Cn%2Cs%29%3Ar.replace%28%22%25current%25%22%2Cn%29.replace%28%22%25total%25%22%2Cs%29%7Dfunction%20i%28e%2Ct%29%7Bvar%20r%2Ca%3Dt.getSlides%28%29%2Ci%3D0%3Bfor%28r%3D0%3Br%3C%3De.getSlideIndex%28%29%26%26r%3Ca.length%3B++r%29%22false%22%21%3D%3Da%5Br%5D.properties.count%26%26%28i+%3D1%29%3Breturn%20Math.max%281%2Ci%29%7Dt.exports%3Dr%7D%2C%7B%7D%5D%2C2%3A%5Bfunction%28e%2Ct%29%7Bvar%20r%3Dt.exports%3D%7B%7D%3Br.nextTick%3Dfunction%28%29%7Bvar%20e%3D%22undefined%22%21%3Dtypeof%20window%26%26window.setImmediate%2Ct%3D%22undefined%22%21%3Dtypeof%20window%26%26window.postMessage%26%26window.addEventListener%3Bif%28e%29return%20function%28e%29%7Breturn%20window.setImmediate%28e%29%7D%3Bif%28t%29%7Bvar%20r%3D%5B%5D%3Breturn%20window.addEventListener%28%22message%22%2Cfunction%28e%29%7Bvar%20t%3De.source%3Bif%28%28t%3D%3D%3Dwindow%7C%7Cnull%3D%3D%3Dt%29%26%26%22process-tick%22%3D%3D%3De.data%26%26%28e.stopPropagation%28%29%2Cr.length%3E0%29%29%7Bvar%20a%3Dr.shift%28%29%3Ba%28%29%7D%7D%2C%210%29%2Cfunction%28e%29%7Br.push%28e%29%2Cwindow.postMessage%28%22process-tick%22%2C%22*%22%29%7D%7Dreturn%20function%28e%29%7BsetTimeout%28e%2C0%29%7D%7D%28%29%2Cr.title%3D%22browser%22%2Cr.browser%3D%210%2Cr.env%3D%7B%7D%2Cr.argv%3D%5B%5D%2Cr.binding%3Dfunction%28%29%7Bthrow%20new%20Error%28%22process.binding%20is%20not%20supported%22%29%7D%2Cr.cwd%3Dfunction%28%29%7Breturn%22/%22%7D%2Cr.chdir%3Dfunction%28%29%7Bthrow%20new%20Error%28%22process.chdir%20is%20not%20supported%22%29%7D%7D%2C%7B%7D%5D%2C1%3A%5Bfunction%28e%2Ct%2Cr%29%7B%21function%28e%29%7Bfunction%20t%28e%2Ct%29%7Bif%28e.indexOf%29return%20e.indexOf%28t%29%3Bfor%28var%20r%3D0%3Br%3Ce.length%3Br++%29if%28t%3D%3D%3De%5Br%5D%29return%20r%3Breturn-1%7De.EventEmitter%7C%7C%28e.EventEmitter%3Dfunction%28%29%7B%7D%29%3Bvar%20a%3Dr.EventEmitter%3De.EventEmitter%2Ci%3D%22function%22%3D%3Dtypeof%20Array.isArray%3FArray.isArray%3Afunction%28e%29%7Breturn%22%5Bobject%20Array%5D%22%3D%3D%3DObject.prototype.toString.call%28e%29%7D%2Cn%3D10%3Ba.prototype.setMaxListeners%3Dfunction%28e%29%7Bthis._events%7C%7C%28this._events%3D%7B%7D%29%2Cthis._events.maxListeners%3De%7D%2Ca.prototype.emit%3Dfunction%28e%29%7Bif%28%22error%22%3D%3D%3De%26%26%28%21this._events%7C%7C%21this._events.error%7C%7Ci%28this._events.error%29%26%26%21this._events.error.length%29%29throw%20arguments%5B1%5Dinstanceof%20Error%3Farguments%5B1%5D%3Anew%20Error%28%22Uncaught%2C%20unspecified%20%27error%27%20event.%22%29%3Bif%28%21thisLine truncated
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
Before Width: | Height: | Size: 435 KiB |
|
Before Width: | Height: | Size: 2.7 MiB |
|
Before Width: | Height: | Size: 282 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 56 KiB |
@@ -1,286 +0,0 @@
|
||||
class: center, middle
|
||||
##Python Interpreter in Rust
|
||||
#### 2017/8/6, COSCUP
|
||||
#### Shing Lyu
|
||||
|
||||
|
||||
???
|
||||
top, middle, bottom
|
||||
left, center, right
|
||||
|
||||
---
|
||||
### About Me
|
||||
* 呂行 Shing Lyu
|
||||
* Mozilla engineer
|
||||
* Servo team
|
||||
|
||||

|
||||
---
|
||||
### Python's architecture
|
||||
* Interpreted
|
||||
* Garbage Collected
|
||||
* Compiler => bytecode => VM
|
||||
|
||||

|
||||
|
||||
---
|
||||
background-image: url('pic/ice-cream.jpg')
|
||||
class: center, middle, bleed, text-bg
|
||||
# Flavors
|
||||
|
||||
|
||||
---
|
||||
### Python Flavors
|
||||
* CPython (THE python)
|
||||
* Jython (JVM)
|
||||
* IronPython (.NET)
|
||||
* Pypy
|
||||
* Educational
|
||||
* Byterun
|
||||
* Jsapy (JS)
|
||||
* Brython (Python in browser)
|
||||
|
||||
---
|
||||
class: center, middle
|
||||
|
||||
# Why & How?
|
||||
---
|
||||
### Why rewriting Python in Rust?
|
||||
* Memory safety (?)
|
||||
* Learn about Python's internal
|
||||
* Learn to write Rust from scratch
|
||||
* FUN!
|
||||
|
||||
---
|
||||
### Implementation strategy
|
||||
* Mostly follow CPython 3.6
|
||||
* Focus on the VM first, then the compiler
|
||||
* Use the Python built-in compiler to generate bytecode
|
||||
* Focus on learning rather than usability
|
||||
|
||||
---
|
||||
### Milestones
|
||||
* Basic arithmetics
|
||||
* Variables
|
||||
* Control flows (require JUMP)
|
||||
* Function call (require call stack)
|
||||
* Built-in functions (require native code)
|
||||
* Run Python tutorial example code <= We're here
|
||||
* Exceptions
|
||||
* GC
|
||||
* Run popular libraries
|
||||
|
||||
|
||||
---
|
||||
class: center, middle, bleed, text-bg
|
||||
background-image: url('pic/car_cutaway.jpg')
|
||||
# Python Internals
|
||||
|
||||
---
|
||||
### How Python VM works
|
||||
* Stack machine
|
||||
* Call stack and frames
|
||||
* Has a NAMES list and CONSTS list
|
||||
* Has a STACK as workspace
|
||||
* Accepts Python bytecode
|
||||
* `python -m dis source.py`
|
||||
|
||||
---
|
||||
|
||||
### A simple Python code
|
||||
|
||||
```
|
||||
#!/usr/bin/env python3
|
||||
print(1+1)
|
||||
```
|
||||
|
||||
Running `python3 -m dis source.py` gives us
|
||||
|
||||
```
|
||||
1 0 LOAD_NAME 0 (print)
|
||||
3 LOAD_CONST 2 (2)
|
||||
6 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
|
||||
9 POP_TOP
|
||||
10 LOAD_CONST 1 (None)
|
||||
13 RETURN_VALUE
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### LOAD_NAME "print"
|
||||
* NAMES = ["print"]
|
||||
* CONSTS = [None, 2]
|
||||
* STACK:
|
||||
|
||||
```
|
||||
| |
|
||||
| print (native code)|
|
||||
+--------------------+
|
||||
```
|
||||
---
|
||||
### LOAD_CONST 2
|
||||
* NAMES = ["print"]
|
||||
* CONSTS = [None, 2]
|
||||
* STACK:
|
||||
|
||||
```
|
||||
| |
|
||||
| 2 |
|
||||
| print (native code)|
|
||||
+--------------------+
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### CALL_FUNCTION 1
|
||||
1. `argument = stack.pop()` (argument == 2)
|
||||
2. `function = stack.top()` (function == print)
|
||||
3. call `print(2)`
|
||||
|
||||
* NAMES = ["print"]
|
||||
* CONSTS = [None, 2]
|
||||
* STACK:
|
||||
|
||||
```
|
||||
| |
|
||||
| print (native code)|
|
||||
+--------------------+
|
||||
```
|
||||
---
|
||||
### POP_TOP
|
||||
* NAMES = ["print"]
|
||||
* CONSTS = [None, 2]
|
||||
* STACK:
|
||||
|
||||
```
|
||||
| |
|
||||
| (empty) |
|
||||
+--------------------+
|
||||
```
|
||||
|
||||
---
|
||||
### LOAD_CONST None
|
||||
* NAMES = ["print"]
|
||||
* CONSTS = [None, 2]
|
||||
* STACK:
|
||||
|
||||
```
|
||||
| |
|
||||
| None |
|
||||
+--------------------+
|
||||
```
|
||||
|
||||
---
|
||||
### RETURN_VALUE
|
||||
|
||||
(returns top of stack == None)
|
||||
|
||||
---
|
||||
|
||||
class: center, middle, bleed, text-bg
|
||||
background-image: url('pic/electronic_parts.jpg')
|
||||
# Technical Detail
|
||||
|
||||
---
|
||||
|
||||
### Bytecode format
|
||||
* `dis` output format is for human reader
|
||||
* Implementing a `dis` format parser is a waste of time
|
||||
* Emit JSON bytecode using the [bytecode](https://pypi.python.org/pypi/bytecode/0.5) module
|
||||
|
||||
```
|
||||
|
||||
code = compile(f,...) # Python built-in, return a Code object
|
||||
|
||||
bytecode.Bytecode()
|
||||
.from_code(code)
|
||||
.to_concrete_bytecode()
|
||||
```
|
||||
* Load into Rust using `serde_json`
|
||||
---
|
||||
|
||||
### Types
|
||||
* Everything is a `PyObject` in CPython
|
||||
* We'll need that class hierarchy eventually
|
||||
* Use a Rust `enum` for now
|
||||
|
||||
```
|
||||
pub enum NativeType{
|
||||
NoneType,
|
||||
Boolean(bool),
|
||||
Int(i32),
|
||||
Str(String),
|
||||
Tuple(Vec<NativeType>),
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Testing
|
||||
* `assert` is essential to for unittests
|
||||
* `assert` raises `AssertionError`
|
||||
* Use `panic!()` before we implement exception
|
||||
|
||||
```
|
||||
assert 1 == 1
|
||||
```
|
||||
```
|
||||
1 0 LOAD_CONST 0 (1)
|
||||
3 LOAD_CONST 0 (1)
|
||||
6 COMPARE_OP 2 (==)
|
||||
9 POP_JUMP_IF_TRUE 18
|
||||
12 LOAD_GLOBAL 0 (AssertionError)
|
||||
15 RAISE_VARARGS 1
|
||||
>> 18 LOAD_CONST 1 (None)
|
||||
21 RETURN_VALUE
|
||||
```
|
||||
|
||||
---
|
||||
### Native Function
|
||||
|
||||
* e.g. `print()`
|
||||
|
||||
```
|
||||
pub enum NativeType {
|
||||
NativeFunction(fn(Vec<NativeType>) -> NativeType),
|
||||
...
|
||||
}
|
||||
|
||||
match stack.pop() {
|
||||
NativeFunction(func) => return_val = func(),
|
||||
_ => ...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Next steps
|
||||
* Exceptions
|
||||
* Make it run a small but popular tool/library
|
||||
* Implement the parser
|
||||
* Figure out garbage collection
|
||||
* Performance benchmarking
|
||||
|
||||
---
|
||||
### Contribute
|
||||
|
||||
## https://github.com/shinglyu/RustPython
|
||||
|
||||

|
||||
|
||||
---
|
||||
class: middle, center
|
||||
|
||||
# Thank you
|
||||
|
||||
---
|
||||
|
||||
### References
|
||||
* [`dis` documentation](https://docs.python.org/3.4/library/dis.html)
|
||||
* [byterun](http://www.aosabook.org/en/500L/a-python-interpreter-written-in-python.html)
|
||||
* [byterun (GitHub)](https://github.com/nedbat/byterun/)
|
||||
* [cpython source code](https://github.com/python/cpython)
|
||||
|
||||
|
Before Width: | Height: | Size: 282 KiB |
@@ -1,180 +0,0 @@
|
||||
class: center, middle
|
||||
##Python Interpreter in Rust
|
||||
###Introduction
|
||||
#### 2017/3/28
|
||||
#### Shing Lyu
|
||||
|
||||
|
||||
???
|
||||
top, middle, bottom
|
||||
left, center, right
|
||||
|
||||
---
|
||||
name: toc
|
||||
###Agenda
|
||||
1. Category
|
||||
1. Category
|
||||
1. Category
|
||||
1. Category
|
||||
1. Category
|
||||
1. Category
|
||||
1. Category
|
||||
|
||||
???
|
||||
This is a template
|
||||
---
|
||||
|
||||
### Python's architecture
|
||||
* Interpreted
|
||||
* Garbage Collected
|
||||
* Compiler => bytecode => VM
|
||||
|
||||
---
|
||||
background-image: url('pic/ice-cream.jpg')
|
||||
class: bleed
|
||||
# Flavors
|
||||
|
||||
|
||||
---
|
||||
### Python Flavors
|
||||
* CPython (THE python)
|
||||
* Jython (JVM)
|
||||
* IronPython (.NET)
|
||||
* Pypy
|
||||
* Educational
|
||||
* Byterun
|
||||
* Jsapy (JS)
|
||||
* Brython (Python in browser)
|
||||
|
||||
---
|
||||
### Why rewriting Python in Rust?
|
||||
* Memory safety
|
||||
|
||||
* Learn about Python internal
|
||||
* Learn real world Rust
|
||||
|
||||
---
|
||||
### Implementation strategy
|
||||
* Focus on the VM first, then the compiler
|
||||
* Reuse the Python built-in compiler to generate bytecode
|
||||
* Basic arithmetics
|
||||
* Control flows (require JUMP)
|
||||
* Function call (require call stack)
|
||||
* Built-in functions (require native code)
|
||||
* Run popular libraries
|
||||
|
||||
|
||||
---
|
||||
### References
|
||||
* [`dis` documentation](https://docs.python.org/3.4/library/dis.html)
|
||||
* [byterun](http://www.aosabook.org/en/500L/a-python-interpreter-written-in-python.html)
|
||||
* [byterun (GitHub)](https://github.com/nedbat/byterun/)
|
||||
* [cpython source code](https://github.com/python/cpython)
|
||||
|
||||
---
|
||||
### How Python VM works
|
||||
* Stack machine
|
||||
* Accepts Python bytecode
|
||||
* `python -m dis source.py`
|
||||
|
||||
---
|
||||
|
||||
### A simple Python code
|
||||
|
||||
```
|
||||
#!/usr/bin/env python3
|
||||
print(1+1)
|
||||
```
|
||||
|
||||
We run `python3 -m dis source.py`
|
||||
|
||||
---
|
||||
|
||||
### The bytecode
|
||||
|
||||
```
|
||||
1 0 LOAD_NAME 0 (print)
|
||||
3 LOAD_CONST 2 (2)
|
||||
6 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
|
||||
9 POP_TOP
|
||||
10 LOAD_CONST 1 (None)
|
||||
13 RETURN_VALUE
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### LOAD_NAME "print"
|
||||
* NAMES = ["print"]
|
||||
* CONSTS = [None, 2]
|
||||
* STACK:
|
||||
|
||||
```
|
||||
| |
|
||||
| print (native code)|
|
||||
+--------------------+
|
||||
```
|
||||
---
|
||||
### LOAD_CONST 2
|
||||
* NAMES = ["print"]
|
||||
* CONSTS = [None, 2]
|
||||
* STACK:
|
||||
|
||||
```
|
||||
| |
|
||||
| 2 |
|
||||
| print (native code)|
|
||||
+--------------------+
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### CALL_FUNCTION 1
|
||||
1. argument = stack.pop() (argument == 2)
|
||||
2. function = stack.top() (function == print)
|
||||
3. call print(2)
|
||||
|
||||
* NAMES = ["print"]
|
||||
* CONSTS = [None, 2]
|
||||
* STACK:
|
||||
|
||||
```
|
||||
| |
|
||||
| print (native code)|
|
||||
+--------------------+
|
||||
```
|
||||
---
|
||||
### POP_TOP
|
||||
* NAMES = ["print"]
|
||||
* CONSTS = [None, 2]
|
||||
* STACK:
|
||||
|
||||
```
|
||||
| |
|
||||
| (empty) |
|
||||
+--------------------+
|
||||
```
|
||||
|
||||
---
|
||||
### LOAD_CONST 1
|
||||
* NAMES = ["print"]
|
||||
* CONSTS = [None, 2]
|
||||
* STACK:
|
||||
|
||||
```
|
||||
| |
|
||||
| None |
|
||||
+--------------------+
|
||||
```
|
||||
|
||||
---
|
||||
### RETURN_VALUE
|
||||
|
||||
(returns top of stack == None)
|
||||
|
||||
---
|
||||
|
||||
### Next step
|
||||
* Make it run a small but popular tool/library
|
||||
* Implement the parser
|
||||
* Performance benchmarking
|
||||
@@ -1,6 +0,0 @@
|
||||
Topic to study
|
||||
==================
|
||||
* How to save a Rust Iterator on the stack?
|
||||
* Study how blocks are handled in CPython.
|
||||
* The `why` var?
|
||||
* Why unwind everything when a `break` happened?
|
||||
@@ -10,4 +10,4 @@ pytest = "*"
|
||||
[dev-packages]
|
||||
|
||||
[requires]
|
||||
python_version = "3.6"
|
||||
python_version = "3"
|
||||
@@ -0,0 +1,72 @@
|
||||
|
||||
# Compiling to webassembly
|
||||
|
||||
At this stage RustPython only has preliminary support for web assembly. The instructions here are intended for developers or those wishing to run a toy example.
|
||||
|
||||
## Setup
|
||||
|
||||
To get started, install [wasm-pack](https://rustwasm.github.io/wasm-pack/installer/) and `npm`. ([wasm-bindgen](https://rustwasm.github.io/wasm-bindgen/whirlwind-tour/basic-usage.html) should be installed by `wasm-pack`. if not, install it yourself)
|
||||
|
||||
<!-- Using `rustup` add the compile target `wasm32-unknown-emscripten`. To do so you will need to have [rustup](https://rustup.rs/) installed.
|
||||
|
||||
```bash
|
||||
rustup target add wasm32-unknown-emscripten
|
||||
```
|
||||
|
||||
Next, install `emsdk`:
|
||||
|
||||
```bash
|
||||
curl https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-portable.tar.gz | tar -zxv
|
||||
cd emsdk-portable/
|
||||
./emsdk update
|
||||
./emsdk install sdk-incoming-64bit
|
||||
./emsdk activate sdk-incoming-64bit
|
||||
``` -->
|
||||
|
||||
## Build
|
||||
|
||||
Move into the `wasm` directory. This directory contains a library crate for interop
|
||||
with python to rust to js and back in `wasm/lib`, the demo website found at
|
||||
https://rustpython.github.io/demo in `wasm/demo`, and an example of how to use
|
||||
the crate as a library in one's own JS app in `wasm/example`.
|
||||
|
||||
```sh
|
||||
cd wasm
|
||||
```
|
||||
|
||||
Go to the demo directory. This is the best way of seeing the changes made to either
|
||||
the library or the JS demo, as the `rustpython_wasm` module is set to the global
|
||||
JS variable `rp` on the website.
|
||||
|
||||
```sh
|
||||
cd demo
|
||||
```
|
||||
|
||||
Now, start the webpack development server. It'll compile the crate and then
|
||||
the demo app. This will likely take a long time, both the wasm-pack portion and
|
||||
the webpack portion (from after it says "Your crate has been correctly compiled"),
|
||||
so be patient.
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
You can now open the webpage on https://localhost:8080 and Python code in either
|
||||
the text box or browser devtools with:
|
||||
|
||||
```js
|
||||
rp.pyEval(
|
||||
`
|
||||
print(js_vars['a'] * 9)
|
||||
`,
|
||||
{
|
||||
vars: {
|
||||
a: 9
|
||||
}
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
Alternatively, you can run `npm run build` to build the app once, without watching
|
||||
for changes, or `npm run dist` to build the app in release mode, both for the
|
||||
crate and webpack.
|
||||