Added the shinglyu/RustPython VM
No files matched your search
@@ -1,5 +1,16 @@
|
||||
# RustPython
|
||||
A Python Interpreter written in Rust
|
||||
|
||||
# Code organization
|
||||
The files in the top level directory are from [windelbouwman/rspython][rspython] which contains an implementation of the parser and vm in `src/`
|
||||
|
||||
An alternative implementation of python virtual machine that are compatible with CPython parser are from [shinglyu/RustPython][rustpython] and is located in the `VM/` folder.
|
||||
|
||||
We are in the process of merging the two implementation to form a single implementation.
|
||||
|
||||
# Credit
|
||||
The initial work was based on [windelbouwman/rspython](https://github.com/windelbouwman/rspython) and [shinglyu/RustPython](https://github.com/shinglyu/RustPython)
|
||||
|
||||
[rspython]: https://github.com/windelbouwman/rspython
|
||||
[rustpython]: https://github.com/shinglyu/RustPython
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Shing Lyu
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,64 @@
|
||||
RustPython
|
||||
==============
|
||||
|
||||
A Python interpreter written in Rust
|
||||
|
||||
# Installation
|
||||
|
||||
```
|
||||
bash init_env.sh
|
||||
```
|
||||
|
||||
# Run
|
||||
|
||||
```
|
||||
./test.sh <path/to/file.py> # compile and run
|
||||
./test.sh <path/to/file.py> --bytecode # print the bytecode in JSON
|
||||
./test.sh <path/to/file.py> --dis # Run python -m dis
|
||||
```
|
||||
|
||||
## Manual
|
||||
Given a python file `test.py`
|
||||
|
||||
```
|
||||
python compile_code.py test.py > test.bytecode
|
||||
|
||||
cd RustPython
|
||||
cargo run ../test.bytecode
|
||||
```
|
||||
|
||||
# Testing & debugging
|
||||
|
||||
```
|
||||
./test_all.sh # Run all tests under tests/
|
||||
```
|
||||
|
||||
* If a test is expected to fail or raise exception, add `xfail_*` prefix to the filename.
|
||||
|
||||
## Logging
|
||||
|
||||
```
|
||||
RUST_LOG=debug ./tests_all.sh
|
||||
```
|
||||
|
||||
# TODOs
|
||||
* Native types => Partial
|
||||
* Control flow => if(v)
|
||||
* assert => OK
|
||||
* Structural types (list, tuple, object)
|
||||
* Strings
|
||||
* Function calls => Blocked by bytecode serializer
|
||||
* Modules import
|
||||
* Generators
|
||||
|
||||
|
||||
# Goals
|
||||
* Support all builtin functions
|
||||
* Runs the [pybenchmark](https://pybenchmarks.org/) benchmark test
|
||||
* Run famous/popular python modules (which?)
|
||||
|
||||
* Compatible with CPython 3.6
|
||||
|
||||
# Rust version
|
||||
rustc 1.20.0-nightly
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dtoa"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "kernel32-sys"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.1.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "0.1.80"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "rustpython"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "0.8.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "serde_codegen"
|
||||
version = "0.8.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_codegen_internals 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.10.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_codegen_internals"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"syn 0.10.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "0.8.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"serde_codegen 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"dtoa 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itoa 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "0.10.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread-id"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "utf8-ranges"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-build"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[metadata]
|
||||
"checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66"
|
||||
"checksum dtoa 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0dd841b58510c9618291ffa448da2e4e0f699d984d436122372f446dae62263d"
|
||||
"checksum env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "15abd780e45b3ea4f76b4e9a26ff4843258dd8a3eed2775a0e7368c2e7936c2f"
|
||||
"checksum itoa 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae3088ea4baeceb0284ee9eea42f591226e6beaecf65373e41b38d95a1b8e7a1"
|
||||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||
"checksum libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)" = "30885bcb161cf67054244d10d4a7f4835ffd58773bc72e07d35fecf472295503"
|
||||
"checksum log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "880f77541efa6e5cc74e76910c9884d9859683118839d6a1dc3b11e63512565b"
|
||||
"checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20"
|
||||
"checksum num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "99843c856d68d8b4313b03a17e33c4bb42ae8f6610ea81b28abe076ac721b9b0"
|
||||
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
|
||||
"checksum regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4fd4ace6a8cf7860714a2c2280d6c1f7e6a413486c13298bbc86fd3da019402f"
|
||||
"checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957"
|
||||
"checksum serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)" = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8"
|
||||
"checksum serde_codegen 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)" = "a4c5d8a33087d8984f9535daa62a6498a08f6476050b00ab9339dd847e4c25cc"
|
||||
"checksum serde_codegen_internals 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "afad7924a009f859f380e4a2e3a509a845c2ac66435fcead74a4d983b21ae806"
|
||||
"checksum serde_derive 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)" = "ce44e5f4264b39e9d29c875357b7cc3ebdfb967bb9e22bfb5e44ffa400af5306"
|
||||
"checksum serde_json 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)" = "67f7d2e9edc3523a9c8ec8cd6ec481b3a27810aafee3e625d311febd3e656b4c"
|
||||
"checksum syn 0.10.8 (registry+https://github.com/rust-lang/crates.io-index)" = "58fd09df59565db3399efbba34ba8a2fec1307511ebd245d0061ff9d42691673"
|
||||
"checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03"
|
||||
"checksum thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8576dbbfcaef9641452d5cf0df9b0e7eeab7694956dd33bb61515fb8f18cfdd5"
|
||||
"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
|
||||
"checksum utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1ca13c08c41c9c3e04224ed9ff80461d97e121589ff27c753a16cb10830ae0f"
|
||||
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
|
||||
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
|
||||
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "rustpython"
|
||||
version = "0.1.0"
|
||||
authors = ["Shing Lyu <shing.lyu@gmail.com>"]
|
||||
|
||||
[dependencies]
|
||||
log = "0.3"
|
||||
env_logger = "0.3"
|
||||
serde = "0.8.22"
|
||||
serde_derive = "0.8"
|
||||
serde_json = "0.8"
|
||||
@@ -0,0 +1,47 @@
|
||||
use NativeType;
|
||||
use std::rc::Rc;
|
||||
use std::ops::Deref;
|
||||
|
||||
pub fn print(args: Vec<Rc<NativeType>>) -> NativeType {
|
||||
for elem in args {
|
||||
// TODO: figure out how python's print vectors
|
||||
match elem.deref() {
|
||||
&NativeType::NoneType => println!("None"),
|
||||
&NativeType::Boolean(ref b)=> {
|
||||
if *b {
|
||||
println!("True");
|
||||
} else {
|
||||
println!("False");
|
||||
}
|
||||
},
|
||||
&NativeType::Int(ref x) => println!("{}", x),
|
||||
&NativeType::Float(ref x) => println!("{}", x),
|
||||
&NativeType::Str(ref x) => println!("{}", x),
|
||||
&NativeType::Unicode(ref x) => println!("{}", x),
|
||||
_ => panic!("Print for {:?} not implemented yet", elem),
|
||||
/*
|
||||
List(Vec<NativeType>),
|
||||
Tuple(Vec<NativeType>),
|
||||
Iter(Vec<NativeType>), // TODO: use Iterator instead
|
||||
Code(PyCodeObject),
|
||||
Function(Function),
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
NativeFunction(fn(Vec<NativeType>) -> NativeType ),
|
||||
*/
|
||||
}
|
||||
}
|
||||
NativeType::NoneType
|
||||
}
|
||||
|
||||
pub fn len(args: Vec<Rc<NativeType>>) -> NativeType {
|
||||
if args.len() != 1 {
|
||||
panic!("len(s) expects exactly one parameter");
|
||||
}
|
||||
let len = match args[0].deref() {
|
||||
&NativeType::List(ref l) => l.borrow().len(),
|
||||
&NativeType::Tuple(ref t) => t.len(),
|
||||
&NativeType::Str(ref s) => s.len(),
|
||||
_ => panic!("TypeError: object of this type has no len()")
|
||||
};
|
||||
NativeType::Int(len as i32)
|
||||
}
|
||||
@@ -0,0 +1,990 @@
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate env_logger;
|
||||
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
extern crate serde_json;
|
||||
|
||||
//extern crate eval; use eval::eval::*;
|
||||
use std::collections::HashMap;
|
||||
use std::cell::RefCell;
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std::rc::Rc;
|
||||
use std::ops::Deref;
|
||||
|
||||
mod builtins;
|
||||
|
||||
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum NativeType{
|
||||
NoneType,
|
||||
Boolean(bool),
|
||||
Int(i32),
|
||||
Float(f64),
|
||||
Str(String),
|
||||
Unicode(String),
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
List(RefCell<Vec<NativeType>>),
|
||||
Tuple(Vec<NativeType>),
|
||||
Iter(Vec<NativeType>), // TODO: use Iterator instead
|
||||
Code(PyCodeObject),
|
||||
Function(Function),
|
||||
Slice(Option<i32>, Option<i32>, Option<i32>), // start, stop, step
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
NativeFunction(fn(Vec<Rc<NativeType>>) -> NativeType ),
|
||||
}
|
||||
|
||||
const CMP_OP: &'static [&'static str] = &[">",
|
||||
"<=",
|
||||
"==",
|
||||
"!=",
|
||||
">",
|
||||
">=",
|
||||
"in",
|
||||
"not in",
|
||||
"is",
|
||||
"is not",
|
||||
"exception match",
|
||||
"BAD"
|
||||
];
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Block {
|
||||
block_type: String, //Enum?
|
||||
handler: usize // The destination we should jump to if the block finishes
|
||||
// level?
|
||||
}
|
||||
|
||||
struct Frame {
|
||||
// TODO: We are using Option<i32> in stack for handline None return value
|
||||
code: PyCodeObject,
|
||||
// We need 1 stack per frame
|
||||
stack: Vec<Rc<NativeType>>, // The main data frame of the stack machine
|
||||
blocks: Vec<Block>, // Block frames, for controling loops and exceptions
|
||||
globals: HashMap<String, Rc<NativeType>>, // Variables
|
||||
locals: HashMap<String, Rc<NativeType>>, // Variables
|
||||
labels: HashMap<usize, usize>, // Maps label id to line number, just for speedup
|
||||
lasti: usize, // index of last instruction ran
|
||||
return_value: NativeType,
|
||||
why: String, //Not sure why we need this //Maybe use a enum if we have fininte options
|
||||
// cmp_op: Vec<&'a Fn(NativeType, NativeType) -> bool>, // TODO: change compare to a function list
|
||||
}
|
||||
|
||||
impl Frame {
|
||||
/// Get the current bytecode offset calculated from curr_frame.lasti
|
||||
fn get_bytecode_offset(&self) -> Option<usize> {
|
||||
// Linear search the labels HashMap, inefficient. Consider build a reverse HashMap
|
||||
let mut last_offset = None;
|
||||
for (offset, instr_idx) in self.labels.iter() {
|
||||
if *instr_idx == self.lasti {
|
||||
last_offset = Some(*offset)
|
||||
}
|
||||
}
|
||||
last_offset
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Function {
|
||||
code: PyCodeObject
|
||||
}
|
||||
|
||||
impl Function {
|
||||
fn new(code: PyCodeObject) -> Function {
|
||||
Function {
|
||||
code: code
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct VirtualMachine{
|
||||
frames: Vec<Frame>,
|
||||
}
|
||||
|
||||
impl VirtualMachine {
|
||||
fn new() -> VirtualMachine {
|
||||
VirtualMachine {
|
||||
frames: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn curr_frame(&mut self) -> &mut Frame {
|
||||
self.frames.last_mut().unwrap()
|
||||
}
|
||||
|
||||
fn pop_frame(&mut self) {
|
||||
self.frames.pop().unwrap();
|
||||
}
|
||||
|
||||
fn unwind(&mut self, reason: String) {
|
||||
let curr_frame = self.curr_frame();
|
||||
let curr_block = curr_frame.blocks[curr_frame.blocks.len()-1].clone(); // use last?
|
||||
curr_frame.why = reason; // Why do we need this?
|
||||
debug!("block status: {:?}, {:?}", curr_block.block_type, curr_frame.why);
|
||||
match (curr_block.block_type.as_ref(), curr_frame.why.as_ref()) {
|
||||
("loop", "break") => {
|
||||
curr_frame.lasti = curr_block.handler; //curr_frame.labels[curr_block.handler]; // Jump to the end
|
||||
// Return the why as None
|
||||
curr_frame.blocks.pop();
|
||||
},
|
||||
("loop", "none") => (), //skipped
|
||||
_ => panic!("block stack operation not implemented")
|
||||
}
|
||||
}
|
||||
|
||||
// Can we get rid of the code paramter?
|
||||
|
||||
fn make_frame(&self, code: PyCodeObject, callargs: HashMap<String, Rc<NativeType>>, globals: Option<HashMap<String, Rc<NativeType>>>) -> Frame {
|
||||
//populate the globals and locals
|
||||
let mut labels = HashMap::new();
|
||||
let mut curr_offset = 0;
|
||||
for (idx, op) in code.co_code.iter().enumerate() {
|
||||
labels.insert(curr_offset, idx);
|
||||
curr_offset += op.0;
|
||||
}
|
||||
//TODO: This is wrong, check https://github.com/nedbat/byterun/blob/31e6c4a8212c35b5157919abff43a7daa0f377c6/byterun/pyvm2.py#L95
|
||||
let globals = match globals {
|
||||
Some(g) => g,
|
||||
None => HashMap::new(),
|
||||
};
|
||||
let mut locals = globals;
|
||||
locals.extend(callargs);
|
||||
|
||||
//TODO: move this into the __builtin__ module when we have a module type
|
||||
locals.insert("print".to_string(), Rc::new(NativeType::NativeFunction(builtins::print)));
|
||||
locals.insert("len".to_string(), Rc::new(NativeType::NativeFunction(builtins::len)));
|
||||
Frame {
|
||||
code: code,
|
||||
stack: vec![],
|
||||
blocks: vec![],
|
||||
// save the callargs as locals
|
||||
globals: locals.clone(),
|
||||
locals: locals,
|
||||
labels: labels,
|
||||
lasti: 0,
|
||||
return_value: NativeType::NoneType,
|
||||
why: "none".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
// The Option<i32> is the return value of the frame, remove when we have implemented frame
|
||||
// TODO: read the op codes directly from the internal code object
|
||||
fn run_frame(&mut self, frame: Frame) -> NativeType {
|
||||
self.frames.push(frame);
|
||||
|
||||
//let mut why = None;
|
||||
// Change this to a loop for jump
|
||||
loop {
|
||||
//while curr_frame.lasti < curr_frame.code.co_code.len() {
|
||||
let op_code = {
|
||||
let curr_frame = self.curr_frame();
|
||||
let op_code = curr_frame.code.co_code[curr_frame.lasti].clone();
|
||||
curr_frame.lasti += 1;
|
||||
op_code
|
||||
};
|
||||
let why = self.dispatch(op_code);
|
||||
/*if curr_frame.blocks.len() > 0 {
|
||||
self.manage_block_stack(&why);
|
||||
}
|
||||
*/
|
||||
if let Some(_) = why {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let return_value = {
|
||||
//let curr_frame = self.frames.last_mut().unwrap();
|
||||
self.curr_frame().return_value.clone()
|
||||
};
|
||||
self.pop_frame();
|
||||
return_value
|
||||
}
|
||||
|
||||
fn run_code(&mut self, code: PyCodeObject) {
|
||||
let frame = self.make_frame(code, HashMap::new(), None);
|
||||
self.run_frame(frame);
|
||||
// check if there are any leftover frame, fail if any
|
||||
}
|
||||
|
||||
fn dispatch(&mut self, op_code: (usize, String, Option<usize>)) -> Option<String> {
|
||||
{
|
||||
debug!("stack:{:?}", self.curr_frame().stack);
|
||||
debug!("env :{:?}", self.curr_frame().locals);
|
||||
debug!("Executing op code: {:?}", op_code);
|
||||
}
|
||||
match (op_code.1.as_ref(), op_code.2) {
|
||||
("LOAD_CONST", Some(consti)) => {
|
||||
// println!("Loading const at index: {}", consti);
|
||||
let curr_frame = self.curr_frame();
|
||||
curr_frame.stack.push(Rc::new(curr_frame.code.co_consts[consti].clone()));
|
||||
None
|
||||
},
|
||||
|
||||
// TODO: universal stack element type
|
||||
("LOAD_CONST", None) => {
|
||||
// println!("Loading const at index: {}", consti);
|
||||
self.curr_frame().stack.push(Rc::new(NativeType::NoneType));
|
||||
None
|
||||
},
|
||||
("POP_TOP", None) => {
|
||||
self.curr_frame().stack.pop();
|
||||
None
|
||||
},
|
||||
("LOAD_FAST", Some(var_num)) => {
|
||||
// println!("Loading const at index: {}", consti);
|
||||
let curr_frame = self.curr_frame();
|
||||
let ref name = curr_frame.code.co_varnames[var_num];
|
||||
curr_frame.stack.push(curr_frame.locals.get::<str>(name).unwrap().clone());
|
||||
None
|
||||
},
|
||||
("STORE_NAME", Some(namei)) => {
|
||||
// println!("Loading const at index: {}", consti);
|
||||
let curr_frame = self.curr_frame();
|
||||
curr_frame.locals.insert(curr_frame.code.co_names[namei].clone(), curr_frame.stack.pop().unwrap().clone());
|
||||
None
|
||||
},
|
||||
("LOAD_NAME", Some(namei)) => {
|
||||
// println!("Loading const at index: {}", consti);
|
||||
let curr_frame = self.curr_frame();
|
||||
if let Some(code) = curr_frame.locals.get::<str>(&curr_frame.code.co_names[namei]) {
|
||||
curr_frame.stack.push(code.clone());
|
||||
}
|
||||
else {
|
||||
panic!("Can't find symbol {:?} in the current frame", &curr_frame.code.co_names[namei]);
|
||||
}
|
||||
None
|
||||
},
|
||||
("LOAD_GLOBAL", Some(namei)) => {
|
||||
// We need to load the underlying value the name points to, but stuff like
|
||||
// AssertionError is in the names right after compile, so we load the string
|
||||
// instead for now
|
||||
let curr_frame = self.curr_frame();
|
||||
let name = &curr_frame.code.co_names[namei];
|
||||
curr_frame.stack.push(curr_frame.globals.get::<str>(name).unwrap().clone());
|
||||
None
|
||||
},
|
||||
|
||||
("BUILD_LIST", Some(count)) => {
|
||||
let curr_frame = self.curr_frame();
|
||||
let mut vec = vec!();
|
||||
for _ in 0..count {
|
||||
vec.push((*curr_frame.stack.pop().unwrap()).clone());
|
||||
}
|
||||
vec.reverse();
|
||||
curr_frame.stack.push(Rc::new(NativeType::List(RefCell::new(vec))));
|
||||
None
|
||||
},
|
||||
|
||||
("BUILD_SLICE", Some(count)) => {
|
||||
let curr_frame = self.curr_frame();
|
||||
assert!(count == 2 || count == 3);
|
||||
let mut vec = vec!();
|
||||
for _ in 0..count {
|
||||
vec.push(curr_frame.stack.pop().unwrap());
|
||||
}
|
||||
vec.reverse();
|
||||
let mut out:Vec<Option<i32>> = vec.into_iter().map(|x| match *x {
|
||||
NativeType::Int(n) => Some(n),
|
||||
NativeType::NoneType => None,
|
||||
_ => panic!("Expect Int or None as BUILD_SLICE arguments, got {:?}", x),
|
||||
}).collect();
|
||||
|
||||
if out.len() == 2 {
|
||||
out.push(None);
|
||||
}
|
||||
assert!(out.len() == 3);
|
||||
// TODO: assert the stop start and step are NativeType::Int
|
||||
// See https://users.rust-lang.org/t/how-do-you-assert-enums/1187/8
|
||||
curr_frame.stack.push(Rc::new(NativeType::Slice(out[0], out[1], out[2])));
|
||||
None
|
||||
},
|
||||
|
||||
("GET_ITER", None) => {
|
||||
let curr_frame = self.curr_frame();
|
||||
let tos = curr_frame.stack.pop().unwrap();
|
||||
let iter = match *tos {
|
||||
//TODO: is this clone right?
|
||||
// Return a Iterator instead vvv
|
||||
NativeType::Tuple(ref vec) => NativeType::Iter(vec.clone()),
|
||||
NativeType::List(ref vec) => NativeType::Iter(vec.borrow().clone()),
|
||||
_ => panic!("TypeError: object is not iterable")
|
||||
};
|
||||
curr_frame.stack.push(Rc::new(iter));
|
||||
None
|
||||
},
|
||||
|
||||
("FOR_ITER", Some(delta)) => {
|
||||
// This function should be rewrote to use Rust native iterator
|
||||
let curr_frame = self.curr_frame();
|
||||
let tos = curr_frame.stack.pop().unwrap();
|
||||
let result = match *tos {
|
||||
NativeType::Iter(ref v) => {
|
||||
if v.len() > 0 {
|
||||
Some(v.clone()) // Unnessary clone here
|
||||
}
|
||||
else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => panic!("FOR_ITER: Not an iterator")
|
||||
};
|
||||
if let Some(vec) = result {
|
||||
let (first, rest) = vec.split_first().unwrap();
|
||||
// Unnessary clone here
|
||||
curr_frame.stack.push(Rc::new(NativeType::Iter(rest.to_vec())));
|
||||
curr_frame.stack.push(Rc::new(first.clone()));
|
||||
}
|
||||
else {
|
||||
// Iterator was already poped in the first line of this function
|
||||
let last_offset = curr_frame.get_bytecode_offset().unwrap();
|
||||
curr_frame.lasti = curr_frame.labels.get(&(last_offset + delta)).unwrap().clone();
|
||||
|
||||
}
|
||||
None
|
||||
},
|
||||
|
||||
("COMPARE_OP", Some(cmp_op_i)) => {
|
||||
let curr_frame = self.curr_frame();
|
||||
let v1 = curr_frame.stack.pop().unwrap();
|
||||
let v2 = curr_frame.stack.pop().unwrap();
|
||||
match CMP_OP[cmp_op_i] {
|
||||
// To avoid branch explotion, use an array of callables instead
|
||||
"==" => {
|
||||
match (v1.deref(), v2.deref()) {
|
||||
(&NativeType::Int(ref v1i), &NativeType::Int(ref v2i)) => {
|
||||
curr_frame.stack.push(Rc::new(NativeType::Boolean(v2i == v1i)));
|
||||
},
|
||||
(&NativeType::Float(ref v1f), &NativeType::Float(ref v2f)) => {
|
||||
curr_frame.stack.push(Rc::new(NativeType::Boolean(v2f == v1f)));
|
||||
},
|
||||
(&NativeType::Str(ref v1s), &NativeType::Str(ref v2s)) => {
|
||||
curr_frame.stack.push(Rc::new(NativeType::Boolean(v2s == v1s)));
|
||||
},
|
||||
(&NativeType::Int(ref v1i), &NativeType::Float(ref v2f)) => {
|
||||
curr_frame.stack.push(Rc::new(NativeType::Boolean(v2f == &(*v1i as f64))));
|
||||
},
|
||||
(&NativeType::List(ref l1), &NativeType::List(ref l2)) => {
|
||||
curr_frame.stack.push(Rc::new(NativeType::Boolean(l2 == l1)));
|
||||
},
|
||||
_ => panic!("TypeError in COMPARE_OP: can't compare {:?} with {:?}", v1, v2)
|
||||
};
|
||||
}
|
||||
">" => {
|
||||
match (v1.deref(), v2.deref()) {
|
||||
(&NativeType::Int(ref v1i), &NativeType::Int(ref v2i)) => {
|
||||
curr_frame.stack.push(Rc::new(NativeType::Boolean(v2i < v1i)));
|
||||
},
|
||||
(&NativeType::Float(ref v1f), &NativeType::Float(ref v2f)) => {
|
||||
curr_frame.stack.push(Rc::new(NativeType::Boolean(v2f < v1f)));
|
||||
},
|
||||
_ => panic!("TypeError in COMPARE_OP")
|
||||
};
|
||||
}
|
||||
_ => panic!("Unimplemented COMPARE_OP operator")
|
||||
|
||||
}
|
||||
None
|
||||
|
||||
},
|
||||
("POP_JUMP_IF_TRUE", Some(ref target)) => {
|
||||
let curr_frame = self.curr_frame();
|
||||
let v = curr_frame.stack.pop().unwrap();
|
||||
if *v == NativeType::Boolean(true) {
|
||||
curr_frame.lasti = curr_frame.labels.get(target).unwrap().clone();
|
||||
}
|
||||
None
|
||||
|
||||
}
|
||||
("POP_JUMP_IF_FALSE", Some(ref target)) => {
|
||||
let curr_frame = self.curr_frame();
|
||||
let v = curr_frame.stack.pop().unwrap();
|
||||
if *v == NativeType::Boolean(false) {
|
||||
curr_frame.lasti = curr_frame.labels.get(target).unwrap().clone();
|
||||
}
|
||||
None
|
||||
|
||||
}
|
||||
("JUMP_FORWARD", Some(ref delta)) => {
|
||||
let curr_frame = self.curr_frame();
|
||||
let last_offset = curr_frame.get_bytecode_offset().unwrap();
|
||||
curr_frame.lasti = curr_frame.labels.get(&(last_offset + delta)).unwrap().clone();
|
||||
None
|
||||
},
|
||||
("JUMP_ABSOLUTE", Some(ref target)) => {
|
||||
let curr_frame = self.curr_frame();
|
||||
curr_frame.lasti = curr_frame.labels.get(target).unwrap().clone();
|
||||
None
|
||||
},
|
||||
("BREAK_LOOP", None) => {
|
||||
// Do we still need to return the why if we use unwind from jsapy?
|
||||
self.unwind("break".to_string());
|
||||
None //?
|
||||
},
|
||||
("RAISE_VARARGS", Some(argc)) => {
|
||||
let curr_frame = self.curr_frame();
|
||||
// let (exception, params, traceback) = match argc {
|
||||
let exception = match argc {
|
||||
1 => curr_frame.stack.pop().unwrap(),
|
||||
0 | 2 | 3 => panic!("Not implemented!"),
|
||||
_ => panic!("Invalid paramter for RAISE_VARARGS, must be between 0 to 3")
|
||||
};
|
||||
panic!("{:?}", exception);
|
||||
}
|
||||
("INPLACE_ADD", None) => {
|
||||
let curr_frame = self.curr_frame();
|
||||
let tos = curr_frame.stack.pop().unwrap();
|
||||
let tos1 = curr_frame.stack.pop().unwrap();
|
||||
match (tos.deref(), tos1.deref()) {
|
||||
(&NativeType::Int(ref tosi), &NativeType::Int(ref tos1i)) => {
|
||||
curr_frame.stack.push(Rc::new(NativeType::Int(tos1i + tosi)));
|
||||
},
|
||||
_ => panic!("TypeError in BINARY_ADD")
|
||||
}
|
||||
None
|
||||
},
|
||||
|
||||
("STORE_SUBSCR", None) => {
|
||||
let curr_frame = self.curr_frame();
|
||||
let tos = curr_frame.stack.pop().unwrap();
|
||||
let tos1 = curr_frame.stack.pop().unwrap();
|
||||
let tos2 = curr_frame.stack.pop().unwrap();
|
||||
match (tos1.deref(), tos.deref()) {
|
||||
(&NativeType::List(ref refl), &NativeType::Int(index)) => {
|
||||
refl.borrow_mut()[index as usize] = (*tos2).clone();
|
||||
},
|
||||
(&NativeType::Str(_), &NativeType::Int(_)) => {
|
||||
// TODO: raise TypeError: 'str' object does not support item assignment
|
||||
panic!("TypeError: 'str' object does not support item assignment")
|
||||
},
|
||||
_ => panic!("TypeError in STORE_SUBSCR")
|
||||
}
|
||||
curr_frame.stack.push(tos1);
|
||||
None
|
||||
},
|
||||
|
||||
("BINARY_ADD", None) => {
|
||||
let curr_frame = self.curr_frame();
|
||||
let v1 = curr_frame.stack.pop().unwrap();
|
||||
let v2 = curr_frame.stack.pop().unwrap();
|
||||
match (v1.deref(), v2.deref()) {
|
||||
(&NativeType::Int(ref v1i), &NativeType::Int(ref v2i)) => {
|
||||
curr_frame.stack.push(Rc::new(NativeType::Int(v2i + v1i)));
|
||||
}
|
||||
(&NativeType::Float(ref v1f), &NativeType::Int(ref v2i)) => {
|
||||
curr_frame.stack.push(Rc::new(NativeType::Float(*v2i as f64 + v1f)));
|
||||
}
|
||||
(&NativeType::Int(ref v1i), &NativeType::Float(ref v2f)) => {
|
||||
curr_frame.stack.push(Rc::new(NativeType::Float(v2f + *v1i as f64)));
|
||||
}
|
||||
(&NativeType::Float(ref v1f), &NativeType::Float(ref v2f)) => {
|
||||
curr_frame.stack.push(Rc::new(NativeType::Float(v2f + v1f)));
|
||||
}
|
||||
(&NativeType::Str(ref str1), &NativeType::Str(ref str2)) => {
|
||||
curr_frame.stack.push(Rc::new(NativeType::Str(format!("{}{}", str2, str1))));
|
||||
}
|
||||
(&NativeType::List(ref l1), &NativeType::List(ref l2)) => {
|
||||
let mut new_l = l2.clone();
|
||||
// TODO: remove unnessary copy
|
||||
new_l.borrow_mut().append(&mut l1.borrow().clone());
|
||||
curr_frame.stack.push(Rc::new(NativeType::List(new_l)));
|
||||
|
||||
}
|
||||
_ => panic!("TypeError in BINARY_ADD")
|
||||
}
|
||||
None
|
||||
},
|
||||
("BINARY_POWER", None) => {
|
||||
let curr_frame = self.curr_frame();
|
||||
let v1 = curr_frame.stack.pop().unwrap();
|
||||
let v2 = curr_frame.stack.pop().unwrap();
|
||||
match (v1.deref(), v2.deref()) {
|
||||
(&NativeType::Int(v1i), &NativeType::Int(v2i)) => {
|
||||
curr_frame.stack.push(Rc::new(NativeType::Int(v2i.pow(v1i as u32))));
|
||||
}
|
||||
(&NativeType::Float(v1f), &NativeType::Int(v2i)) => {
|
||||
curr_frame.stack.push(Rc::new(NativeType::Float((v2i as f64).powf(v1f))));
|
||||
}
|
||||
(&NativeType::Int(v1i), &NativeType::Float(v2f)) => {
|
||||
curr_frame.stack.push(Rc::new(NativeType::Float(v2f.powi(v1i))));
|
||||
}
|
||||
(&NativeType::Float(v1f), &NativeType::Float(v2f)) => {
|
||||
curr_frame.stack.push(Rc::new(NativeType::Float(v2f.powf(v1f))));
|
||||
}
|
||||
_ => panic!("TypeError in BINARY_POWER")
|
||||
}
|
||||
None
|
||||
},
|
||||
("BINARY_MULTIPLY", None) => {
|
||||
let curr_frame = self.curr_frame();
|
||||
let v1 = curr_frame.stack.pop().unwrap();
|
||||
let v2 = curr_frame.stack.pop().unwrap();
|
||||
match (v1.deref(), v2.deref()) {
|
||||
(&NativeType::Int(v1i), &NativeType::Int(v2i)) => {
|
||||
curr_frame.stack.push(Rc::new(NativeType::Int(v2i * v1i)));
|
||||
},
|
||||
/*
|
||||
(NativeType::Float(v1f), NativeType::Int(v2i)) => {
|
||||
curr_frame.stack.push(NativeType::Float((v2i as f64) * v1f));
|
||||
},
|
||||
(NativeType::Int(v1i), NativeType::Float(v2f)) => {
|
||||
curr_frame.stack.push(NativeType::Float(v2f * (v1i as f64)));
|
||||
},
|
||||
(NativeType::Float(v1f), NativeType::Float(v2f)) => {
|
||||
curr_frame.stack.push(NativeType::Float(v2f * v1f));
|
||||
},
|
||||
*/
|
||||
//TODO: String multiply
|
||||
_ => panic!("TypeError in BINARY_MULTIPLY")
|
||||
}
|
||||
None
|
||||
},
|
||||
("BINARY_TRUE_DIVIDE", None) => {
|
||||
let curr_frame = self.curr_frame();
|
||||
let v1 = curr_frame.stack.pop().unwrap();
|
||||
let v2 = curr_frame.stack.pop().unwrap();
|
||||
match (v1.deref(), v2.deref()) {
|
||||
(&NativeType::Int(v1i), &NativeType::Int(v2i)) => {
|
||||
curr_frame.stack.push(Rc::new(NativeType::Int(v2i / v1i)));
|
||||
},
|
||||
_ => panic!("TypeError in BINARY_DIVIDE")
|
||||
}
|
||||
None
|
||||
},
|
||||
("BINARY_MODULO", None) => {
|
||||
let curr_frame = self.curr_frame();
|
||||
let v1 = curr_frame.stack.pop().unwrap();
|
||||
let v2 = curr_frame.stack.pop().unwrap();
|
||||
match (v1.deref(), v2.deref()) {
|
||||
(&NativeType::Int(v1i), &NativeType::Int(v2i)) => {
|
||||
curr_frame.stack.push(Rc::new(NativeType::Int(v2i % v1i)));
|
||||
},
|
||||
_ => panic!("TypeError in BINARY_MODULO")
|
||||
}
|
||||
None
|
||||
},
|
||||
("BINARY_SUBTRACT", None) => {
|
||||
let curr_frame = self.curr_frame();
|
||||
let v1 = curr_frame.stack.pop().unwrap();
|
||||
let v2 = curr_frame.stack.pop().unwrap();
|
||||
match (v1.deref(), v2.deref()) {
|
||||
(&NativeType::Int(v1i), &NativeType::Int(v2i)) => {
|
||||
curr_frame.stack.push(Rc::new(NativeType::Int(v2i - v1i)));
|
||||
},
|
||||
_ => panic!("TypeError in BINARY_SUBSTRACT")
|
||||
}
|
||||
None
|
||||
},
|
||||
|
||||
("BINARY_SUBSCR", None) => {
|
||||
let curr_frame = self.curr_frame();
|
||||
let tos = curr_frame.stack.pop().unwrap();
|
||||
let tos1 = curr_frame.stack.pop().unwrap();
|
||||
debug!("tos: {:?}, tos1: {:?}", tos, tos1);
|
||||
match (tos1.deref(), tos.deref()) {
|
||||
(&NativeType::List(ref l), &NativeType::Int(ref index)) => {
|
||||
let pos_index = (index + l.borrow().len() as i32) % l.borrow().len() as i32;
|
||||
curr_frame.stack.push(Rc::new(l.borrow()[pos_index as usize].clone()))
|
||||
},
|
||||
(&NativeType::List(ref l), &NativeType::Slice(ref opt_start, ref opt_stop, ref opt_step)) => {
|
||||
let start = match opt_start {
|
||||
&Some(start) => ((start + l.borrow().len() as i32) % l.borrow().len() as i32) as usize,
|
||||
&None => 0,
|
||||
};
|
||||
let stop = match opt_stop {
|
||||
&Some(stop) => ((stop + l.borrow().len() as i32) % l.borrow().len() as i32) as usize,
|
||||
&None => l.borrow().len() as usize,
|
||||
};
|
||||
let step = match opt_step {
|
||||
//Some(step) => step as usize,
|
||||
&None => 1 as usize,
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
// TODO: we could potentially avoid this copy and use slice
|
||||
curr_frame.stack.push(Rc::new(NativeType::List(RefCell::new(l.borrow()[start..stop].to_vec()))));
|
||||
},
|
||||
(&NativeType::Tuple(ref t), &NativeType::Int(ref index)) => curr_frame.stack.push(Rc::new(t[*index as usize].clone())),
|
||||
(&NativeType::Str(ref s), &NativeType::Int(ref index)) => {
|
||||
let idx = (index + s.len() as i32) % s.len() as i32;
|
||||
curr_frame.stack.push(Rc::new(NativeType::Str(s.chars().nth(idx as usize).unwrap().to_string())));
|
||||
},
|
||||
(&NativeType::Str(ref s), &NativeType::Slice(ref opt_start, ref opt_stop, ref opt_step)) => {
|
||||
let start = match opt_start {
|
||||
&Some(start) if start > s.len() as i32 => s.len(),
|
||||
&Some(start) if start <= s.len() as i32 => ((start + s.len() as i32) % s.len() as i32) as usize,
|
||||
&Some(_) => panic!("Bad start index for string slicing"),
|
||||
&Some(start) => ((start + s.len() as i32) % s.len() as i32) as usize,
|
||||
&None => 0,
|
||||
};
|
||||
let stop = match opt_stop {
|
||||
&Some(stop) if stop > s.len() as i32 => s.len(),
|
||||
&Some(stop) if stop <= s.len() as i32 => ((stop + s.len() as i32) % s.len() as i32) as usize, // Do we need this modding?
|
||||
&Some(_) => panic!("Bad stop index for string slicing"),
|
||||
&None => s.len() as usize,
|
||||
};
|
||||
let step = match opt_step {
|
||||
//Some(step) => step as usize,
|
||||
&None => 1 as usize,
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
curr_frame.stack.push(Rc::new(NativeType::Str(s[start..stop].to_string())));
|
||||
},
|
||||
// TODO: implement other Slice possibilities
|
||||
_ => panic!("TypeError: indexing type {:?} with index {:?} is not supported (yet?)", tos1, tos)
|
||||
};
|
||||
None
|
||||
},
|
||||
("ROT_TWO", None) => {
|
||||
let curr_frame = self.curr_frame();
|
||||
let tos = curr_frame.stack.pop().unwrap();
|
||||
let tos1 = curr_frame.stack.pop().unwrap();
|
||||
curr_frame.stack.push(tos);
|
||||
curr_frame.stack.push(tos1);
|
||||
None
|
||||
}
|
||||
("UNARY_NEGATIVE", None) => {
|
||||
let curr_frame = self.curr_frame();
|
||||
let v = curr_frame.stack.pop().unwrap();
|
||||
match v.deref() {
|
||||
&NativeType::Int(v1i) => {
|
||||
curr_frame.stack.push(Rc::new(NativeType::Int(-v1i)));
|
||||
},
|
||||
_ => panic!("TypeError in UINARY_NEGATIVE")
|
||||
}
|
||||
None
|
||||
},
|
||||
("UNARY_POSITIVE", None) => {
|
||||
let curr_frame = self.curr_frame();
|
||||
let v = curr_frame.stack.pop().unwrap();
|
||||
// Any case that is not just push back?
|
||||
curr_frame.stack.push(v);
|
||||
None
|
||||
},
|
||||
("PRINT_ITEM", None) => {
|
||||
let curr_frame = self.curr_frame();
|
||||
// TODO: Print without the (...)
|
||||
println!("{:?}", curr_frame.stack.pop().unwrap());
|
||||
None
|
||||
},
|
||||
("PRINT_NEWLINE", None) => {
|
||||
print!("\n");
|
||||
None
|
||||
},
|
||||
("MAKE_FUNCTION", Some(argc)) => {
|
||||
// https://docs.python.org/3.4/library/dis.html#opcode-MAKE_FUNCTION
|
||||
let curr_frame = self.curr_frame();
|
||||
let qualified_name = curr_frame.stack.pop().unwrap();
|
||||
let code_obj = match curr_frame.stack.pop().unwrap().deref() {
|
||||
&NativeType::Code(ref code) => code.clone(),
|
||||
_ => panic!("Second item on the stack should be a code object")
|
||||
};
|
||||
// pop argc arguments
|
||||
// argument: name, args, globals
|
||||
let func = Function::new(code_obj);
|
||||
curr_frame.stack.push(Rc::new(NativeType::Function(func)));
|
||||
None
|
||||
},
|
||||
("CALL_FUNCTION", Some(argc)) => {
|
||||
let kw_count = (argc >> 8) as u8;
|
||||
let pos_count = (argc & 0xFF) as u8;
|
||||
// Pop the arguments based on argc
|
||||
let mut kw_args = HashMap::new();
|
||||
let mut pos_args = Vec::new();
|
||||
{
|
||||
let curr_frame = self.curr_frame();
|
||||
for _ in 0..kw_count {
|
||||
let native_val = curr_frame.stack.pop().unwrap();
|
||||
let native_key = curr_frame.stack.pop().unwrap();
|
||||
if let (ref val, &NativeType::Str(ref key)) = (native_val, native_key.deref()) {
|
||||
|
||||
kw_args.insert(key.clone(), val.clone());
|
||||
}
|
||||
else {
|
||||
panic!("Incorrect type found while building keyword argument list")
|
||||
}
|
||||
}
|
||||
for _ in 0..pos_count {
|
||||
pos_args.push(curr_frame.stack.pop().unwrap());
|
||||
}
|
||||
}
|
||||
let locals = {
|
||||
// FIXME: no clone here
|
||||
self.curr_frame().locals.clone()
|
||||
};
|
||||
|
||||
let func = {
|
||||
match self.curr_frame().stack.pop().unwrap().deref() {
|
||||
&NativeType::Function(ref func) => {
|
||||
// pop argc arguments
|
||||
// argument: name, args, globals
|
||||
// build the callargs hashmap
|
||||
pos_args.reverse();
|
||||
let mut callargs = HashMap::new();
|
||||
for (name, val) in func.code.co_varnames.iter().zip(pos_args) {
|
||||
callargs.insert(name.to_string(), val);
|
||||
}
|
||||
// merge callargs with kw_args
|
||||
let return_value = {
|
||||
let frame = self.make_frame(func.code.clone(), callargs, Some(locals));
|
||||
self.run_frame(frame)
|
||||
};
|
||||
self.curr_frame().stack.push(Rc::new(return_value));
|
||||
},
|
||||
&NativeType::NativeFunction(func) => {
|
||||
pos_args.reverse();
|
||||
let return_value = func(pos_args);
|
||||
self.curr_frame().stack.push(Rc::new(return_value));
|
||||
},
|
||||
_ => panic!("The item on the stack should be a code object")
|
||||
}
|
||||
};
|
||||
None
|
||||
},
|
||||
("RETURN_VALUE", None) => {
|
||||
// Hmmm... what is this used?
|
||||
// I believe we need to push this to the next frame
|
||||
self.curr_frame().return_value = (*self.curr_frame().stack.pop().unwrap()).clone();
|
||||
Some("return".to_string())
|
||||
},
|
||||
("SETUP_LOOP", Some(delta)) => {
|
||||
let curr_frame = self.curr_frame();
|
||||
let curr_offset = curr_frame.get_bytecode_offset().unwrap();
|
||||
curr_frame.blocks.push(Block {
|
||||
block_type: "loop".to_string(),
|
||||
handler: *curr_frame.labels.get(&(curr_offset + delta)).unwrap(),
|
||||
});
|
||||
None
|
||||
},
|
||||
("POP_BLOCK", None) => {
|
||||
self.curr_frame().blocks.pop();
|
||||
None
|
||||
}
|
||||
("SetLineno", _) | ("LABEL", _)=> {
|
||||
// Skip
|
||||
None
|
||||
},
|
||||
(name, _) => {
|
||||
panic!("Unrecongnizable op code: {}", name);
|
||||
}
|
||||
} // end match
|
||||
} // end dispatch function
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PyCodeObject {
|
||||
co_consts: Vec<NativeType>,
|
||||
co_names: Vec<String>,
|
||||
co_code: Vec<(usize, String, Option<usize>)>, //size, name, args
|
||||
co_varnames: Vec<String>,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
fn parse_native_type(val_str: &str) -> Result<NativeType, ()> {
|
||||
// println!("{:?}", val_str);
|
||||
match val_str {
|
||||
"None" => Ok(NativeType::NoneType),
|
||||
"True" => Ok(NativeType::Boolean(true)),
|
||||
"False" => Ok(NativeType::Boolean(false)),
|
||||
_ => {
|
||||
if let Ok(int) = val_str.parse::<i32>() {
|
||||
return Ok(NativeType::Int(int))
|
||||
}
|
||||
|
||||
if let Ok(float) = val_str.parse::<f64>() {
|
||||
return Ok(NativeType::Float(float))
|
||||
}
|
||||
|
||||
if val_str.starts_with("\'") && val_str.ends_with("\'") {
|
||||
return Ok(NativeType::Str(val_str[1..val_str.len()-1].to_string()))
|
||||
}
|
||||
|
||||
if val_str.starts_with("u\'") && val_str.ends_with("\'") {
|
||||
return Ok(NativeType::Unicode(val_str[2..val_str.len()-1].to_string()))
|
||||
}
|
||||
|
||||
if val_str.starts_with("(") && val_str.ends_with(")") {
|
||||
return Ok(NativeType::Str(val_str[1..val_str.len()-1].to_string()))
|
||||
}
|
||||
|
||||
Err(())
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_bytecode(s: &str) -> Code {
|
||||
let lines: Vec<&str> = s.split('\n').collect();
|
||||
|
||||
let (metadata, ops) = lines.split_at(2);
|
||||
// Parsing the first line CONSTS
|
||||
let consts_str: &str = metadata[0]; // line 0 is empty
|
||||
let values_str = &consts_str[("CONSTS: (".len())..(consts_str.len()-1)];
|
||||
let values: Vec<&str> = values_str.split(",").collect();
|
||||
// We need better type definition here
|
||||
let consts: Vec<NativeType>= values.into_iter()
|
||||
.map(|x| x.trim())
|
||||
.filter(|x| x.len() > 0)
|
||||
.map(|x| parse_native_type(x).unwrap())
|
||||
.collect();
|
||||
|
||||
// Parsing the second line NAMES
|
||||
let names_str: &str = metadata[1]; // line 0 is empty
|
||||
let values_str = &names_str[("NAMES: (".len())..(names_str.len()-1)];
|
||||
let values: Vec<&str> = values_str.split(",").collect();
|
||||
// We are assuming the first and last chars are \'
|
||||
let names: Vec<&str>= values.into_iter().map(|x| x.trim())
|
||||
.filter(|x| x.len() > 0)
|
||||
.map(|x| &x[1..(x.len()-1)]).collect();
|
||||
|
||||
// Parsing the op_codes
|
||||
let op_codes: Vec<(&str, Option<usize>)>= ops.into_iter()
|
||||
.map(|x| x.trim())
|
||||
.filter(|x| x.len() > 0)
|
||||
.map(|x| {
|
||||
let op: Vec<&str> = x.split(", ").collect();
|
||||
// println!("{:?}", op);
|
||||
(op[0], op[1].parse::<usize>().ok())
|
||||
}).collect();
|
||||
|
||||
|
||||
Code {
|
||||
consts: consts,
|
||||
op_codes: op_codes,
|
||||
names: names,
|
||||
}
|
||||
}
|
||||
*/
|
||||
fn main() {
|
||||
env_logger::init().unwrap();
|
||||
// TODO: read this from args
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let filename = &args[1];
|
||||
|
||||
let mut f = File::open(filename).unwrap();
|
||||
// println!("Read file");
|
||||
let mut s = String::new();
|
||||
f.read_to_string(&mut s).unwrap();
|
||||
// println!("Read string");
|
||||
let code: PyCodeObject = match serde_json::from_str(&s) {
|
||||
Ok(c) => c,
|
||||
Err(_) => panic!("Fail to parse the bytecode")
|
||||
};
|
||||
|
||||
let mut vm = VirtualMachine::new();
|
||||
vm.run_code(code);
|
||||
// println!("Done");
|
||||
}
|
||||
|
||||
/*
|
||||
#[test]
|
||||
fn test_parse_native_type() {
|
||||
|
||||
assert_eq!(NativeType::NoneType, parse_native_type("None").unwrap());
|
||||
assert_eq!(NativeType::Boolean(true), parse_native_type("True").unwrap());
|
||||
assert_eq!(NativeType::Boolean(false), parse_native_type("False").unwrap());
|
||||
assert_eq!(NativeType::Int(3), parse_native_type("3").unwrap());
|
||||
assert_eq!(NativeType::Float(3.0), parse_native_type("3.0").unwrap());
|
||||
assert_eq!(NativeType::Float(3.5), parse_native_type("3.5").unwrap());
|
||||
assert_eq!(NativeType::Str("foo".to_string()), parse_native_type("\'foo\'").unwrap());
|
||||
assert_eq!(NativeType::Unicode("foo".to_string()), parse_native_type("u\'foo\'").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_bytecode() {
|
||||
|
||||
let input = "CONSTS: (1, None, 2)
|
||||
NAMES: ('a', 'b')
|
||||
SetLineno, 1
|
||||
LOAD_CONST, 2
|
||||
PRINT_ITEM, None
|
||||
PRINT_NEWLINE, None
|
||||
LOAD_CONST, None
|
||||
RETURN_VALUE, None
|
||||
";
|
||||
let expected = Code { // Fill me with a more sensible data
|
||||
consts: vec![NativeType::Int(1), NativeType::NoneType, NativeType::Int(2)],
|
||||
names: vec!["a", "b"],
|
||||
op_codes: vec![
|
||||
("SetLineno", Some(1)),
|
||||
("LOAD_CONST", Some(2)),
|
||||
("PRINT_ITEM", None),
|
||||
("PRINT_NEWLINE", None),
|
||||
("LOAD_CONST", None),
|
||||
("RETURN_VALUE", None)
|
||||
]
|
||||
};
|
||||
|
||||
assert_eq!(expected, parse_bytecode(input));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_single_const_tuple() {
|
||||
let input = "CONSTS: (None,)
|
||||
NAMES: ()
|
||||
SetLineno, 1
|
||||
LOAD_CONST, 0
|
||||
RETURN_VALUE, None
|
||||
";
|
||||
let expected = Code { // Fill me with a more sensible data
|
||||
consts: vec![NativeType::NoneType],
|
||||
names: vec![],
|
||||
op_codes: vec![
|
||||
("SetLineno", Some(1)),
|
||||
("LOAD_CONST", Some(0)),
|
||||
("RETURN_VALUE", None)
|
||||
]
|
||||
};
|
||||
|
||||
assert_eq!(expected, parse_bytecode(input));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vm() {
|
||||
|
||||
let code = PyCodeObject {
|
||||
co_consts: vec![NativeType::Int(1), NativeType::NoneType, NativeType::Int(2)],
|
||||
co_names: vec![],
|
||||
co_code: vec![
|
||||
(3, "LOAD_CONST".to_string(), Some(2)),
|
||||
(1, "PRINT_ITEM".to_string(), None),
|
||||
(1, "PRINT_NEWLINE".to_string(), None),
|
||||
(3, "LOAD_CONST".to_string(), None),
|
||||
(1, "RETURN_VALUE".to_string(), None)
|
||||
]
|
||||
};
|
||||
let mut vm = VirtualMachine::new();
|
||||
assert_eq!((), vm.exec(&code));
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_parse_jsonbytecode() {
|
||||
|
||||
let input = "{\"co_consts\":[{\"Int\":1},\"NoneType\",{\"Int\":2}],\"co_names\":[\"print\"],\"co_code\":[[3,\"LOAD_CONST\",2],[1,\"PRINT_ITEM\",null],[1,\"PRINT_NEWLINE\",null],[3,\"LOAD_CONST\",null],[1,\"RETURN_VALUE\",null]]}";
|
||||
// let input = "{\"co_names\": [\"print\"], \"co_code\": [[\"LOAD_CONST\", 0], [\"LOAD_CONST\", 0], [\"COMPARE_OP\", 2], [\"POP_JUMP_IF_FALSE\", 25], [\"LOAD_NAME\", 0], [\"LOAD_CONST\", 1], [\"CALL_FUNCTION\", 1], [\"POP_TOP\", null], [\"JUMP_FORWARD\", 10], [\"LOAD_NAME\", 0], [\"LOAD_CONST\", 2], [\"CALL_FUNCTION\", 1], [\"POP_TOP\", null], [\"LOAD_CONST\", 0], [\"LOAD_CONST\", 3], [\"COMPARE_OP\", 2], [\"POP_JUMP_IF_FALSE\", 60], [\"LOAD_NAME\", 0], [\"LOAD_CONST\", 1], [\"CALL_FUNCTION\", 1], [\"POP_TOP\", null], [\"JUMP_FORWARD\", 10], [\"LOAD_NAME\", 0], [\"LOAD_CONST\", 2], [\"CALL_FUNCTION\", 1], [\"POP_TOP\", null], [\"LOAD_CONST\", 4], [\"RETURN_VALUE\", null]], \"co_consts\": [{\"Int\": 1}, {\"Str\": \"equal\"}, {\"Str\": \"not equal\"}, {\"Int\": 2}, {\"NoneType\": null}]}";
|
||||
|
||||
let expected = PyCodeObject { // Fill me with a more sensible data
|
||||
co_consts: vec![NativeType::Int(1), NativeType::NoneType, NativeType::Int(2)],
|
||||
co_names: vec!["print".to_string()],
|
||||
co_code: vec![
|
||||
(3, "LOAD_CONST".to_string(), Some(2)),
|
||||
(1, "PRINT_ITEM".to_string(), None),
|
||||
(1, "PRINT_NEWLINE".to_string(), None),
|
||||
(3, "LOAD_CONST".to_string(), None),
|
||||
(1, "RETURN_VALUE".to_string(), None)
|
||||
]
|
||||
};
|
||||
println!("{}", serde_json::to_string(&expected).unwrap());
|
||||
|
||||
let deserialized: PyCodeObject = serde_json::from_str(&input).unwrap();
|
||||
assert_eq!(expected, deserialized)
|
||||
}
|
||||
*/
|
||||
|
||||
#[test]
|
||||
fn test_tuple_serialization(){
|
||||
let tuple = NativeType::Tuple(vec![NativeType::Int(1),NativeType::Int(2)]);
|
||||
println!("{}", serde_json::to_string(&tuple).unwrap());
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import bytecode
|
||||
import sys
|
||||
import json
|
||||
import types
|
||||
|
||||
|
||||
class CodeEncoder(json.JSONEncoder):
|
||||
def default(self, obj):
|
||||
if (isinstance(obj, types.CodeType)):
|
||||
return serialize_code(obj)
|
||||
return json.JSONEncoder.default(self, obj)
|
||||
|
||||
def serialize_code(code):
|
||||
c = bytecode.Bytecode().from_code(code).to_concrete_bytecode()
|
||||
return (
|
||||
{
|
||||
"co_consts": consts_to_rust_enum(c.consts),
|
||||
"co_names": c.names,
|
||||
"co_name": c.name,
|
||||
"co_code": parse_co_code_to_str(c),
|
||||
"co_varnames": c.varnames
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def consts_to_rust_enum(consts):
|
||||
def capitalize_first(s):
|
||||
return s[0].upper() + s[1:]
|
||||
|
||||
def const_to_rust_enum(const):
|
||||
if type(const).__name__ == "tuple":
|
||||
return {capitalize_first(str(type(const).__name__)): list(map(const_to_rust_enum, const))}
|
||||
else:
|
||||
return {capitalize_first(str(type(const).__name__)): const}
|
||||
return list(map(const_to_rust_enum, consts))
|
||||
|
||||
|
||||
def parse_co_code_to_str(c):
|
||||
return list(
|
||||
map(lambda op: (op.size, op.name, op.arg if op.arg != bytecode.UNSET else None),
|
||||
c)
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
filename = sys.argv[1]
|
||||
with open(filename, 'rU') as f:
|
||||
code = f.read()
|
||||
|
||||
code = compile(code, filename, "exec")
|
||||
|
||||
print(CodeEncoder().encode(code))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,30 @@
|
||||
Byterun
|
||||
|
||||
* Builtins are exposted 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 nativefunction `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 conatins the builtins dict
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
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
|
||||
@@ -0,0 +1,13 @@
|
||||
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
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
# 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
|
||||
@@ -0,0 +1,5 @@
|
||||
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/
|
||||
@@ -0,0 +1,43 @@
|
||||
# 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
|
||||
|
||||
@@ -0,0 +1,529 @@
|
||||
<!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>
|
||||
|
After Width: | Height: | Size: 435 KiB |
|
After Width: | Height: | Size: 2.7 MiB |
|
After Width: | Height: | Size: 282 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 56 KiB |
@@ -0,0 +1,286 @@
|
||||
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)
|
||||
|
||||
|
After Width: | Height: | Size: 282 KiB |
@@ -0,0 +1,180 @@
|
||||
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
|
||||
@@ -0,0 +1,6 @@
|
||||
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?
|
||||
@@ -0,0 +1,7 @@
|
||||
virtualenv venv --python=python3
|
||||
source venv/bin/activate
|
||||
pip install bytecode
|
||||
|
||||
source ~/.cargo/env
|
||||
rustup install nightly
|
||||
rustup default nightly
|
||||
@@ -0,0 +1,219 @@
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpython"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/dgrunwald/rust-cpython.git#50e96c9755630604eab09587c8976a8a9c096a7d"
|
||||
dependencies = [
|
||||
"libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"python3-sys 0.2.0 (git+https://github.com/dgrunwald/rust-cpython.git)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kernel32-sys"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "python27-sys"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "python3-sys"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/dgrunwald/rust-cpython.git#50e96c9755630604eab09587c8976a8a9c096a7d"
|
||||
dependencies = [
|
||||
"libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "python_compiler"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cpython 0.2.0 (git+https://github.com/dgrunwald/rust-cpython.git)",
|
||||
"python27-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "0.1.80"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread-id"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ucd-util"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "unreachable"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf8-ranges"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "utf8-ranges"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "void"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-build"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[metadata]
|
||||
"checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66"
|
||||
"checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4"
|
||||
"checksum cpython 0.2.0 (git+https://github.com/dgrunwald/rust-cpython.git)" = "<none>"
|
||||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||
"checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d"
|
||||
"checksum libc 0.2.41 (registry+https://github.com/rust-lang/crates.io-index)" = "ac8ebf8343a981e2fa97042b14768f02ed3e1d602eac06cae6166df3c8ced206"
|
||||
"checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20"
|
||||
"checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d"
|
||||
"checksum num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "775393e285254d2f5004596d69bb8bc1149754570dcc08cf30cabeba67955e28"
|
||||
"checksum python27-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4daec30bfe241e55f2c905cee2badf176ae2be49d9bddf4267103fea780238c3"
|
||||
"checksum python3-sys 0.2.0 (git+https://github.com/dgrunwald/rust-cpython.git)" = "<none>"
|
||||
"checksum regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4fd4ace6a8cf7860714a2c2280d6c1f7e6a413486c13298bbc86fd3da019402f"
|
||||
"checksum regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9329abc99e39129fcceabd24cf5d85b4671ef7c29c50e972bc5afe32438ec384"
|
||||
"checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957"
|
||||
"checksum regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7"
|
||||
"checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03"
|
||||
"checksum thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8576dbbfcaef9641452d5cf0df9b0e7eeab7694956dd33bb61515fb8f18cfdd5"
|
||||
"checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963"
|
||||
"checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d"
|
||||
"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
|
||||
"checksum utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1ca13c08c41c9c3e04224ed9ff80461d97e121589ff27c753a16cb10830ae0f"
|
||||
"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122"
|
||||
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
|
||||
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
|
||||
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "python_compiler"
|
||||
version = "0.1.0"
|
||||
authors = ["Shing Lyu <shing.lyu@gmail.com>"]
|
||||
|
||||
[dependencies]
|
||||
cpython = { git = "https://github.com/dgrunwald/rust-cpython.git" }
|
||||
|
||||
[dependencies.python27-sys]
|
||||
version = "*"
|
||||
@@ -0,0 +1,7 @@
|
||||
extern crate python_compiler;
|
||||
|
||||
use python_compiler::python_compiler::compile;
|
||||
|
||||
fn main() {
|
||||
println!("{:?}", compile());
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
extern crate cpython;
|
||||
extern crate python27_sys;
|
||||
|
||||
pub mod python_compiler;
|
||||
@@ -0,0 +1,35 @@
|
||||
use cpython::Python;
|
||||
use cpython::ObjectProtocol; //for call method
|
||||
use cpython::PyObject;
|
||||
use cpython::PyDict;
|
||||
use python27_sys::PyCodeObject;
|
||||
|
||||
|
||||
//pub fn compile() -> PyObject {
|
||||
pub fn compile(){
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
|
||||
let locals = PyDict::new(py);
|
||||
// TODO: read the filename from commandline
|
||||
//locals.set_item(py, "filename", "../tests/function.py").unwrap();
|
||||
|
||||
let load_file = "\
|
||||
import os
|
||||
print(os.getcwd())
|
||||
filename = '../tests/function.py'
|
||||
with open(filename, 'rU') as f:\
|
||||
code = f.read()\
|
||||
";
|
||||
py.run(load_file, None, Some(&locals)).unwrap();
|
||||
let code = py.eval("compile(code, \"foo\", \"exec\")", None, Some(&locals)).unwrap();
|
||||
//println!("{:?}", code.getattr(py, "co_name").unwrap());
|
||||
//println!("{:?}", code.getattr(py, "co_filename").unwrap());
|
||||
//println!("{:?}", code.getattr(py, "co_code").unwrap());
|
||||
//println!("{:?}", code.getattr(py, "co_freevars").unwrap());
|
||||
//println!("{:?}", code.getattr(py, "co_cellvars").unwrap());
|
||||
println!("{:?}", code.getattr(py, "co_consts").unwrap());
|
||||
//let consts = code.getattr(py, "co_consts").unwrap();
|
||||
//println!("{:?}", consts.get_item(py, 0).unwrap().getattr(py, "co_code"));
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
byteplay==0.2
|
||||
@@ -0,0 +1 @@
|
||||
print(2 + 3)
|
||||
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env bash
|
||||
# Usage: test.sh <tests/test_case.py> [--bytecode|--dis]
|
||||
# --bytecode: print the bytecode
|
||||
# --dis: run dis
|
||||
|
||||
set -e
|
||||
|
||||
MODE="run"
|
||||
|
||||
case "${2}" in
|
||||
"--bytecode" )
|
||||
MODE="view_bytecode"
|
||||
;;
|
||||
"--dis" )
|
||||
MODE="run_dis"
|
||||
;;
|
||||
* )
|
||||
;;
|
||||
esac
|
||||
|
||||
TESTCASE=$(basename ${1})
|
||||
#TMP_FILE="test_${TESTCASE}.bytecode"
|
||||
TMP_FILE="${1}.bytecode"
|
||||
|
||||
python compile_code.py "${1}" > "${TMP_FILE}"
|
||||
|
||||
echo "${MODE}"
|
||||
case "${MODE}" in
|
||||
"run" )
|
||||
cd RustPython
|
||||
RUST_BACKTRACE=1 cargo run "../${TMP_FILE}"
|
||||
;;
|
||||
"view_bytecode" )
|
||||
cat "${TMP_FILE}" | python -m json.tool
|
||||
;;
|
||||
"run_dis" )
|
||||
python -m dis "${1}"
|
||||
;;
|
||||
* )
|
||||
echo "Not a valid mode!"
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
:: win bat
|
||||
|
||||
|
||||
cd tests
|
||||
|
||||
for %%i in (*.py) do python ../compile_code.py %%i >../bytes/%%i.bytecode
|
||||
cd ..
|
||||
|
||||
REM cd RustPython
|
||||
REM for %%i in (../bytes/*.vbytecode) do echo %%i
|
||||
REM cd ..
|
||||
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env bash
|
||||
# set -e
|
||||
|
||||
source venv/bin/activate
|
||||
#TESTCASE=tests/variables.py
|
||||
# TESTCASE=tests/variables.py
|
||||
#TESTCASE=tests/minimum.py
|
||||
unexpected_count=0
|
||||
expected_count=0
|
||||
fail_titles=$""
|
||||
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
for TESTCASE in $(find tests -name \*.py -print)
|
||||
do
|
||||
echo "TEST START: ${TESTCASE}"
|
||||
echo "--------------------------------"
|
||||
FILENAME="$(basename ${TESTCASE})"
|
||||
xfail=false
|
||||
if [ "${FILENAME:0:6}" = "xfail_" ]; then
|
||||
echo "Expected FAIL"
|
||||
xfail=true
|
||||
fi
|
||||
|
||||
|
||||
python compile_code.py $TESTCASE > $TESTCASE.bytecode
|
||||
cd RustPython
|
||||
cargo run ../$TESTCASE.bytecode
|
||||
|
||||
|
||||
if [[ $? -ne 0 ]]; then
|
||||
if [ "${xfail}" = true ]; then
|
||||
echo "== FAIL as expected =="
|
||||
let expected_count=expected_count+1
|
||||
else
|
||||
printf "${RED}== FAIL, expected PASS ==${NC}\n"
|
||||
let unexpected_count=unexpected_count+1
|
||||
fail_titles=$"${fail_titles}\n${TESTCASE}\t${RED}FAIL${NC} (expected PASS)"
|
||||
fi
|
||||
else
|
||||
if [ "${xfail}" = true ]; then
|
||||
printf "${RED}== PASS, expected FAIL ==${NC}\n"
|
||||
let unexpected_count=unexpected_count+1
|
||||
let unexpected_count=unexpected_count+1
|
||||
fail_titles=$"${fail_titles}\n${TESTCASE}\t${RED}PASS${NC} (expected FAIL)"
|
||||
else
|
||||
echo "== PASS as expected =="
|
||||
let expected_count=expected_count+1
|
||||
fi
|
||||
fi
|
||||
cd ..
|
||||
echo "--------------------------------"
|
||||
|
||||
done
|
||||
|
||||
echo "Summary"
|
||||
echo "================"
|
||||
printf "${RED}${unexpected_count} unexpected${NC}, ${expected_count} expected"
|
||||
echo ""
|
||||
echo ""
|
||||
echo "unexpected results:"
|
||||
printf "${fail_titles}"
|
||||
echo ""
|
||||
echo "================"
|
||||
@@ -0,0 +1,10 @@
|
||||
word = "Python"
|
||||
|
||||
assert "Python" == word[:2] + word[2:]
|
||||
assert "Python" == word[:4] + word[4:]
|
||||
|
||||
assert "Py" == word[:2]
|
||||
assert "on" == word[4:]
|
||||
assert "on" == word[-2:]
|
||||
assert "Py" == word[:-4]
|
||||
# assert "Py" == word[::2]
|
||||
@@ -0,0 +1 @@
|
||||
{"co_code": [[3, "LOAD_CONST", 0], [3, "STORE_NAME", 0], [3, "LOAD_CONST", 0], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 1], [3, "LOAD_CONST", 2], [3, "BUILD_SLICE", 2], [1, "BINARY_SUBSCR", null], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 2], [3, "LOAD_CONST", 1], [3, "BUILD_SLICE", 2], [1, "BINARY_SUBSCR", null], [1, "BINARY_ADD", null], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 48], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 0], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 1], [3, "LOAD_CONST", 3], [3, "BUILD_SLICE", 2], [1, "BINARY_SUBSCR", null], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 3], [3, "LOAD_CONST", 1], [3, "BUILD_SLICE", 2], [1, "BINARY_SUBSCR", null], [1, "BINARY_ADD", null], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 90], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 4], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 1], [3, "LOAD_CONST", 2], [3, "BUILD_SLICE", 2], [1, "BINARY_SUBSCR", null], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 118], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 5], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 3], [3, "LOAD_CONST", 1], [3, "BUILD_SLICE", 2], [1, "BINARY_SUBSCR", null], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 146], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 5], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 6], [3, "LOAD_CONST", 1], [3, "BUILD_SLICE", 2], [1, "BINARY_SUBSCR", null], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 174], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 4], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 1], [3, "LOAD_CONST", 7], [3, "BUILD_SLICE", 2], [1, "BINARY_SUBSCR", null], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 202], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 1], [1, "RETURN_VALUE", null]], "co_varnames": [], "co_name": "<module>", "co_names": ["word", "AssertionError"], "co_consts": [{"Str": "Python"}, {"NoneType": null}, {"Int": 2}, {"Int": 4}, {"Str": "Py"}, {"Str": "on"}, {"Int": -2}, {"Int": -4}]}
|
||||
@@ -0,0 +1,3 @@
|
||||
word = "Python"
|
||||
assert "on" == word[4:42]
|
||||
assert "" == word[42:]
|
||||
@@ -0,0 +1 @@
|
||||
{"co_name": "<module>", "co_varnames": [], "co_consts": [{"Str": "Python"}, {"Str": "on"}, {"Int": 4}, {"Int": 42}, {"Str": ""}, {"NoneType": null}], "co_names": ["word", "AssertionError"], "co_code": [[3, "LOAD_CONST", 0], [3, "STORE_NAME", 0], [3, "LOAD_CONST", 1], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 2], [3, "LOAD_CONST", 3], [3, "BUILD_SLICE", 2], [1, "BINARY_SUBSCR", null], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 34], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 4], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 3], [3, "LOAD_CONST", 5], [3, "BUILD_SLICE", 2], [1, "BINARY_SUBSCR", null], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 62], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 5], [1, "RETURN_VALUE", null]]}
|
||||
@@ -0,0 +1 @@
|
||||
{"co_varnames": [], "co_name": "<module>", "co_names": ["word"], "co_consts": [{"Str": "Python"}, {"Str": "J"}, {"Int": 0}, {"Str": "Jy"}, {"Int": 2}, {"NoneType": null}], "co_code": [[3, "LOAD_CONST", 0], [3, "STORE_NAME", 0], [3, "LOAD_CONST", 1], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 2], [1, "STORE_SUBSCR", null], [3, "LOAD_CONST", 3], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 4], [3, "LOAD_CONST", 5], [3, "BUILD_SLICE", 2], [1, "STORE_SUBSCR", null], [3, "LOAD_CONST", 5], [1, "RETURN_VALUE", null]]}
|
||||
@@ -0,0 +1,4 @@
|
||||
word = "Python"
|
||||
|
||||
assert "Jython" == "J" + word[1:]
|
||||
assert "Pypy" == word[:2] + "py"
|
||||
@@ -0,0 +1 @@
|
||||
{"co_varnames": [], "co_names": ["word", "AssertionError"], "co_name": "<module>", "co_consts": [{"Str": "Python"}, {"Str": "Jython"}, {"Str": "J"}, {"Int": 1}, {"NoneType": null}, {"Str": "Pypy"}, {"Int": 2}, {"Str": "py"}], "co_code": [[3, "LOAD_CONST", 0], [3, "STORE_NAME", 0], [3, "LOAD_CONST", 1], [3, "LOAD_CONST", 2], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 3], [3, "LOAD_CONST", 4], [3, "BUILD_SLICE", 2], [1, "BINARY_SUBSCR", null], [1, "BINARY_ADD", null], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 38], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 5], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 4], [3, "LOAD_CONST", 6], [3, "BUILD_SLICE", 2], [1, "BINARY_SUBSCR", null], [3, "LOAD_CONST", 7], [1, "BINARY_ADD", null], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 70], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 4], [1, "RETURN_VALUE", null]]}
|
||||
@@ -0,0 +1,3 @@
|
||||
s = "abcdefg"
|
||||
|
||||
assert 7 == len(s)
|
||||
@@ -0,0 +1 @@
|
||||
{"co_names": ["s", "len", "AssertionError"], "co_consts": [{"Str": "abcdefg"}, {"Int": 7}, {"NoneType": null}], "co_code": [[3, "LOAD_CONST", 0], [3, "STORE_NAME", 0], [3, "LOAD_CONST", 1], [3, "LOAD_NAME", 1], [3, "LOAD_NAME", 0], [3, "CALL_FUNCTION", 1], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 30], [3, "LOAD_GLOBAL", 2], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 2], [1, "RETURN_VALUE", null]], "co_name": "<module>", "co_varnames": []}
|
||||
@@ -0,0 +1,6 @@
|
||||
squares = [1, 4, 9, 16, 25]
|
||||
|
||||
assert 1 == squares[0]
|
||||
assert 25 == squares[-1]
|
||||
assert [9, 16, 25] == squares[-3:]
|
||||
assert squares == squares[:]
|
||||
@@ -0,0 +1 @@
|
||||
{"co_name": "<module>", "co_varnames": [], "co_consts": [{"Int": 1}, {"Int": 4}, {"Int": 9}, {"Int": 16}, {"Int": 25}, {"Int": 0}, {"Int": -1}, {"Int": -3}, {"NoneType": null}], "co_names": ["squares", "AssertionError"], "co_code": [[3, "LOAD_CONST", 0], [3, "LOAD_CONST", 1], [3, "LOAD_CONST", 2], [3, "LOAD_CONST", 3], [3, "LOAD_CONST", 4], [3, "BUILD_LIST", 5], [3, "STORE_NAME", 0], [3, "LOAD_CONST", 0], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 5], [1, "BINARY_SUBSCR", null], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 43], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 4], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 6], [1, "BINARY_SUBSCR", null], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 65], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 2], [3, "LOAD_CONST", 3], [3, "LOAD_CONST", 4], [3, "BUILD_LIST", 3], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 7], [3, "LOAD_CONST", 8], [3, "BUILD_SLICE", 2], [1, "BINARY_SUBSCR", null], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 102], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_NAME", 0], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 8], [3, "LOAD_CONST", 8], [3, "BUILD_SLICE", 2], [1, "BINARY_SUBSCR", null], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 130], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 8], [1, "RETURN_VALUE", null]]}
|
||||
@@ -0,0 +1,3 @@
|
||||
l = [1,2,3]
|
||||
assert [1,2,3,4,5] == (l + [4,5])
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{"co_consts": [{"Int": 1}, {"Int": 2}, {"Int": 3}, {"Int": 4}, {"Int": 5}, {"NoneType": null}], "co_names": ["l", "AssertionError"], "co_name": "<module>", "co_varnames": [], "co_code": [[3, "LOAD_CONST", 0], [3, "LOAD_CONST", 1], [3, "LOAD_CONST", 2], [3, "BUILD_LIST", 3], [3, "STORE_NAME", 0], [3, "LOAD_CONST", 0], [3, "LOAD_CONST", 1], [3, "LOAD_CONST", 2], [3, "LOAD_CONST", 3], [3, "LOAD_CONST", 4], [3, "BUILD_LIST", 5], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 3], [3, "LOAD_CONST", 4], [3, "BUILD_LIST", 2], [1, "BINARY_ADD", null], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 58], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 5], [1, "RETURN_VALUE", null]]}
|
||||
@@ -0,0 +1,3 @@
|
||||
x = [1,999,3]
|
||||
x[1] = 2
|
||||
assert [1,2,3] == x
|
||||
@@ -0,0 +1 @@
|
||||
{"co_consts": [{"Int": 1}, {"Int": 999}, {"Int": 3}, {"Int": 2}, {"NoneType": null}], "co_code": [[3, "LOAD_CONST", 0], [3, "LOAD_CONST", 1], [3, "LOAD_CONST", 2], [3, "BUILD_LIST", 3], [3, "STORE_NAME", 0], [3, "LOAD_CONST", 3], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 0], [1, "STORE_SUBSCR", null], [3, "LOAD_CONST", 0], [3, "LOAD_CONST", 3], [3, "LOAD_CONST", 2], [3, "BUILD_LIST", 3], [3, "LOAD_NAME", 0], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 52], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 4], [1, "RETURN_VALUE", null]], "co_varnames": [], "co_names": ["x", "AssertionError"], "co_name": "<module>"}
|
||||
@@ -0,0 +1,2 @@
|
||||
x = []
|
||||
x.append(1)
|
||||
@@ -0,0 +1 @@
|
||||
{"co_consts": [{"Int": 1}, {"NoneType": null}], "co_name": "<module>", "co_code": [[3, "BUILD_LIST", 0], [3, "STORE_NAME", 0], [3, "LOAD_NAME", 0], [3, "LOAD_ATTR", 1], [3, "LOAD_CONST", 0], [3, "CALL_FUNCTION", 1], [1, "POP_TOP", null], [3, "LOAD_CONST", 1], [1, "RETURN_VALUE", null]], "co_varnames": [], "co_names": ["x", "append"]}
|
||||
@@ -0,0 +1,16 @@
|
||||
# Spec: https://docs.python.org/2/library/types.html
|
||||
print(None)
|
||||
# TypeType
|
||||
# print(True) # LOAD_NAME???
|
||||
print(1)
|
||||
# print(1L) # Long
|
||||
print(1.1)
|
||||
# ComplexType
|
||||
print("abc")
|
||||
print(u"abc")
|
||||
# Structural below
|
||||
# print((1, 2)) # Tuple can be any length, but fixed after declared
|
||||
# x = (1,2)
|
||||
# print(x[0]) # Tuple can be any length, but fixed after declared
|
||||
# print([1, 2, 3])
|
||||
# print({"first":1,"second":2})
|
||||
@@ -0,0 +1 @@
|
||||
{"co_names": ["print"], "co_varnames": [], "co_name": "<module>", "co_consts": [{"NoneType": null}, {"Int": 1}, {"Float": 1.1}, {"Str": "abc"}], "co_code": [[3, "LOAD_NAME", 0], [3, "LOAD_CONST", 0], [3, "CALL_FUNCTION", 1], [1, "POP_TOP", null], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 1], [3, "CALL_FUNCTION", 1], [1, "POP_TOP", null], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 2], [3, "CALL_FUNCTION", 1], [1, "POP_TOP", null], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 3], [3, "CALL_FUNCTION", 1], [1, "POP_TOP", null], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 3], [3, "CALL_FUNCTION", 1], [1, "POP_TOP", null], [3, "LOAD_CONST", 0], [1, "RETURN_VALUE", null]]}
|
||||
@@ -0,0 +1,2 @@
|
||||
assert 3 == len([1,2,3])
|
||||
assert 2 == len((1,2))
|
||||
@@ -0,0 +1 @@
|
||||
{"co_code": [[3, "LOAD_CONST", 0], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 1], [3, "LOAD_CONST", 2], [3, "LOAD_CONST", 0], [3, "BUILD_LIST", 3], [3, "CALL_FUNCTION", 1], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 33], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 2], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 3], [3, "CALL_FUNCTION", 1], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 57], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 4], [1, "RETURN_VALUE", null]], "co_names": ["len", "AssertionError"], "co_consts": [{"Int": 3}, {"Int": 1}, {"Int": 2}, {"Tuple": [{"Int": 1}, {"Int": 2}]}, {"NoneType": null}], "co_varnames": [], "co_name": "<module>"}
|
||||
@@ -0,0 +1,39 @@
|
||||
x = 1
|
||||
y = 1.1
|
||||
assert x+y == 2.1
|
||||
#print(x+y)
|
||||
|
||||
x = 1.1
|
||||
y = 1
|
||||
assert x+y == 2.1
|
||||
#print(x+y)
|
||||
|
||||
x = 1.1
|
||||
y = 2.1
|
||||
assert x+y == 3.2
|
||||
#print(x+y)
|
||||
|
||||
x = "ab"
|
||||
y = "cd"
|
||||
assert x+y == "abcd"
|
||||
#print(x+y)
|
||||
|
||||
x = 2
|
||||
y = 3
|
||||
assert x**y == 8
|
||||
#print(x**y)
|
||||
|
||||
x = 2.0
|
||||
y = 3
|
||||
assert x**y == 8.0
|
||||
#print(x**y)
|
||||
|
||||
x = 2
|
||||
y = 3.0
|
||||
assert x**y == 8.0
|
||||
#print(x**y)
|
||||
|
||||
x = 2.0
|
||||
y = 3.0
|
||||
assert x**y == 8.0
|
||||
#print(x**y)
|
||||
@@ -0,0 +1 @@
|
||||
{"co_code": [[3, "LOAD_CONST", 0], [3, "STORE_NAME", 0], [3, "LOAD_CONST", 1], [3, "STORE_NAME", 1], [3, "LOAD_NAME", 0], [3, "LOAD_NAME", 1], [1, "BINARY_ADD", null], [3, "LOAD_CONST", 2], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 34], [3, "LOAD_GLOBAL", 2], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 1], [3, "STORE_NAME", 0], [3, "LOAD_CONST", 0], [3, "STORE_NAME", 1], [3, "LOAD_NAME", 0], [3, "LOAD_NAME", 1], [1, "BINARY_ADD", null], [3, "LOAD_CONST", 2], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 68], [3, "LOAD_GLOBAL", 2], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 1], [3, "STORE_NAME", 0], [3, "LOAD_CONST", 2], [3, "STORE_NAME", 1], [3, "LOAD_NAME", 0], [3, "LOAD_NAME", 1], [1, "BINARY_ADD", null], [3, "LOAD_CONST", 3], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 102], [3, "LOAD_GLOBAL", 2], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 4], [3, "STORE_NAME", 0], [3, "LOAD_CONST", 5], [3, "STORE_NAME", 1], [3, "LOAD_NAME", 0], [3, "LOAD_NAME", 1], [1, "BINARY_ADD", null], [3, "LOAD_CONST", 6], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 136], [3, "LOAD_GLOBAL", 2], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 7], [3, "STORE_NAME", 0], [3, "LOAD_CONST", 8], [3, "STORE_NAME", 1], [3, "LOAD_NAME", 0], [3, "LOAD_NAME", 1], [1, "BINARY_POWER", null], [3, "LOAD_CONST", 9], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 170], [3, "LOAD_GLOBAL", 2], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 10], [3, "STORE_NAME", 0], [3, "LOAD_CONST", 8], [3, "STORE_NAME", 1], [3, "LOAD_NAME", 0], [3, "LOAD_NAME", 1], [1, "BINARY_POWER", null], [3, "LOAD_CONST", 11], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 204], [3, "LOAD_GLOBAL", 2], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 7], [3, "STORE_NAME", 0], [3, "LOAD_CONST", 12], [3, "STORE_NAME", 1], [3, "LOAD_NAME", 0], [3, "LOAD_NAME", 1], [1, "BINARY_POWER", null], [3, "LOAD_CONST", 11], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 238], [3, "LOAD_GLOBAL", 2], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 10], [3, "STORE_NAME", 0], [3, "LOAD_CONST", 12], [3, "STORE_NAME", 1], [3, "LOAD_NAME", 0], [3, "LOAD_NAME", 1], [1, "BINARY_POWER", null], [3, "LOAD_CONST", 11], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 272], [3, "LOAD_GLOBAL", 2], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 13], [1, "RETURN_VALUE", null]], "co_consts": [{"Int": 1}, {"Float": 1.1}, {"Float": 2.1}, {"Float": 3.2}, {"Str": "ab"}, {"Str": "cd"}, {"Str": "abcd"}, {"Int": 2}, {"Int": 3}, {"Int": 8}, {"Float": 2.0}, {"Float": 8.0}, {"Float": 3.0}, {"NoneType": null}], "co_name": "<module>", "co_varnames": [], "co_names": ["x", "y", "AssertionError"]}
|
||||
@@ -0,0 +1 @@
|
||||
1 + 1.1
|
||||
@@ -0,0 +1 @@
|
||||
{"co_names": [], "co_varnames": [], "co_name": "<module>", "co_code": [[3, "LOAD_CONST", 0], [1, "POP_TOP", null], [3, "LOAD_CONST", 1], [1, "RETURN_VALUE", null]], "co_consts": [{"Float": 2.1}, {"NoneType": null}]}
|
||||
@@ -0,0 +1,6 @@
|
||||
x = 0
|
||||
for i in [1,2,3,4]:
|
||||
x += 1
|
||||
|
||||
assert x == 4
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{"co_name": "<module>", "co_consts": [{"Int": 0}, {"Int": 1}, {"Int": 2}, {"Int": 3}, {"Int": 4}, {"NoneType": null}], "co_names": ["x", "i", "AssertionError"], "co_code": [[3, "LOAD_CONST", 0], [3, "STORE_NAME", 0], [3, "SETUP_LOOP", 36], [3, "LOAD_CONST", 1], [3, "LOAD_CONST", 2], [3, "LOAD_CONST", 3], [3, "LOAD_CONST", 4], [3, "BUILD_LIST", 4], [1, "GET_ITER", null], [3, "FOR_ITER", 16], [3, "STORE_NAME", 1], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 1], [1, "INPLACE_ADD", null], [3, "STORE_NAME", 0], [3, "JUMP_ABSOLUTE", 25], [1, "POP_BLOCK", null], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 4], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 63], [3, "LOAD_GLOBAL", 2], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 5], [1, "RETURN_VALUE", null]], "co_varnames": []}
|
||||
@@ -0,0 +1,4 @@
|
||||
def foo():
|
||||
return 42
|
||||
|
||||
assert foo() == 42
|
||||
@@ -0,0 +1 @@
|
||||
{"co_name": "<module>", "co_varnames": [], "co_code": [[3, "LOAD_CONST", 0], [3, "LOAD_CONST", 1], [3, "MAKE_FUNCTION", 0], [3, "STORE_NAME", 0], [3, "LOAD_NAME", 0], [3, "CALL_FUNCTION", 0], [3, "LOAD_CONST", 2], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 33], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 3], [1, "RETURN_VALUE", null]], "co_consts": [{"Code": {"co_name": "foo", "co_varnames": [], "co_code": [[3, "LOAD_CONST", 1], [1, "RETURN_VALUE", null]], "co_consts": [{"NoneType": null}, {"Int": 42}], "co_names": []}}, {"Str": "foo"}, {"Int": 42}, {"NoneType": null}], "co_names": ["foo", "AssertionError"]}
|
||||
@@ -0,0 +1,17 @@
|
||||
def sum(x, y):
|
||||
return x+y
|
||||
|
||||
# def total(a, b, c, d):
|
||||
# return sum(sum(a,b), sum(c,d))
|
||||
#
|
||||
# assert total(1,1,1,1) == 4
|
||||
# assert total(1,2,3,4) == 10
|
||||
|
||||
assert sum(1,1) == 2
|
||||
assert sum(1,3) == 4
|
||||
|
||||
def sum2y(x, y):
|
||||
return x+y*2
|
||||
|
||||
assert sum2y(1,1) == 3
|
||||
assert sum2y(1,3) == 7
|
||||
@@ -0,0 +1 @@
|
||||
{"co_consts": [{"Code": {"co_consts": [{"NoneType": null}], "co_code": [[3, "LOAD_FAST", 0], [3, "LOAD_FAST", 1], [1, "BINARY_ADD", null], [1, "RETURN_VALUE", null]], "co_names": [], "co_name": "sum", "co_varnames": ["x", "y"]}}, {"Str": "sum"}, {"Int": 1}, {"Int": 2}, {"Int": 3}, {"Int": 4}, {"Code": {"co_consts": [{"NoneType": null}, {"Int": 2}], "co_code": [[3, "LOAD_FAST", 0], [3, "LOAD_FAST", 1], [3, "LOAD_CONST", 1], [1, "BINARY_MULTIPLY", null], [1, "BINARY_ADD", null], [1, "RETURN_VALUE", null]], "co_names": [], "co_name": "sum2y", "co_varnames": ["x", "y"]}}, {"Str": "sum2y"}, {"Int": 7}, {"NoneType": null}], "co_code": [[3, "LOAD_CONST", 0], [3, "LOAD_CONST", 1], [3, "MAKE_FUNCTION", 0], [3, "STORE_NAME", 0], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 2], [3, "LOAD_CONST", 2], [3, "CALL_FUNCTION", 2], [3, "LOAD_CONST", 3], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 39], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 2], [3, "LOAD_CONST", 4], [3, "CALL_FUNCTION", 2], [3, "LOAD_CONST", 5], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 66], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 6], [3, "LOAD_CONST", 7], [3, "MAKE_FUNCTION", 0], [3, "STORE_NAME", 2], [3, "LOAD_NAME", 2], [3, "LOAD_CONST", 2], [3, "LOAD_CONST", 2], [3, "CALL_FUNCTION", 2], [3, "LOAD_CONST", 4], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 105], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_NAME", 2], [3, "LOAD_CONST", 2], [3, "LOAD_CONST", 4], [3, "CALL_FUNCTION", 2], [3, "LOAD_CONST", 8], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 132], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 9], [1, "RETURN_VALUE", null]], "co_names": ["sum", "AssertionError", "sum2y"], "co_name": "<module>", "co_varnames": []}
|
||||
@@ -0,0 +1,8 @@
|
||||
# Blocked on LOAD_GLOBAL
|
||||
def sum(x,y):
|
||||
return x+y
|
||||
|
||||
def total(a,b,c,d):
|
||||
return sum(sum(a,b),sum(c,d))
|
||||
|
||||
assert total(1,2,3,4) == 10
|
||||
@@ -0,0 +1 @@
|
||||
{"co_consts": [{"Code": {"co_consts": [{"NoneType": null}], "co_names": [], "co_code": [[3, "LOAD_FAST", 0], [3, "LOAD_FAST", 1], [1, "BINARY_ADD", null], [1, "RETURN_VALUE", null]], "co_name": "sum", "co_varnames": ["x", "y"]}}, {"Str": "sum"}, {"Code": {"co_consts": [{"NoneType": null}], "co_names": ["sum"], "co_code": [[3, "LOAD_GLOBAL", 0], [3, "LOAD_GLOBAL", 0], [3, "LOAD_FAST", 0], [3, "LOAD_FAST", 1], [3, "CALL_FUNCTION", 2], [3, "LOAD_GLOBAL", 0], [3, "LOAD_FAST", 2], [3, "LOAD_FAST", 3], [3, "CALL_FUNCTION", 2], [3, "CALL_FUNCTION", 2], [1, "RETURN_VALUE", null]], "co_name": "total", "co_varnames": ["a", "b", "c", "d"]}}, {"Str": "total"}, {"Int": 1}, {"Int": 2}, {"Int": 3}, {"Int": 4}, {"Int": 10}, {"NoneType": null}], "co_names": ["sum", "total", "AssertionError"], "co_code": [[3, "LOAD_CONST", 0], [3, "LOAD_CONST", 1], [3, "MAKE_FUNCTION", 0], [3, "STORE_NAME", 0], [3, "LOAD_CONST", 2], [3, "LOAD_CONST", 3], [3, "MAKE_FUNCTION", 0], [3, "STORE_NAME", 1], [3, "LOAD_NAME", 1], [3, "LOAD_CONST", 4], [3, "LOAD_CONST", 5], [3, "LOAD_CONST", 6], [3, "LOAD_CONST", 7], [3, "CALL_FUNCTION", 4], [3, "LOAD_CONST", 8], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 57], [3, "LOAD_GLOBAL", 2], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 9], [1, "RETURN_VALUE", null]], "co_name": "<module>", "co_varnames": []}
|
||||
@@ -0,0 +1,15 @@
|
||||
x = 0
|
||||
if 1 == 1:
|
||||
x += 5
|
||||
else:
|
||||
x += 3
|
||||
|
||||
assert x == 5
|
||||
|
||||
y = 0
|
||||
if 1 == 2:
|
||||
y += 5
|
||||
else:
|
||||
y += 3
|
||||
|
||||
assert y == 3
|
||||
@@ -0,0 +1 @@
|
||||
{"co_names": ["x", "AssertionError", "y"], "co_name": "<module>", "co_consts": [{"Int": 0}, {"Int": 1}, {"Int": 5}, {"Int": 3}, {"Int": 2}, {"NoneType": null}], "co_varnames": [], "co_code": [[3, "LOAD_CONST", 0], [3, "STORE_NAME", 0], [3, "LOAD_CONST", 1], [3, "LOAD_CONST", 1], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_FALSE", 31], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 2], [1, "INPLACE_ADD", null], [3, "STORE_NAME", 0], [3, "JUMP_FORWARD", 10], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 3], [1, "INPLACE_ADD", null], [3, "STORE_NAME", 0], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 2], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 59], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 0], [3, "STORE_NAME", 2], [3, "LOAD_CONST", 1], [3, "LOAD_CONST", 4], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_FALSE", 90], [3, "LOAD_NAME", 2], [3, "LOAD_CONST", 2], [1, "INPLACE_ADD", null], [3, "STORE_NAME", 2], [3, "JUMP_FORWARD", 10], [3, "LOAD_NAME", 2], [3, "LOAD_CONST", 3], [1, "INPLACE_ADD", null], [3, "STORE_NAME", 2], [3, "LOAD_NAME", 2], [3, "LOAD_CONST", 3], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 118], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 5], [1, "RETURN_VALUE", null]]}
|
||||
@@ -0,0 +1,8 @@
|
||||
assert 2 + 2 == 4
|
||||
|
||||
assert 50 - 5*6 == 20
|
||||
|
||||
assert (50 - 5*6) / 4 == 5 # This will crash
|
||||
assert (50 - 5*6) / 4 == 5.0
|
||||
|
||||
assert 8 / 5 == 1.6 # division always returns a floating point number
|
||||
@@ -0,0 +1 @@
|
||||
{"co_consts": [{"Int": 4}, {"Int": 20}, {"Float": 5.0}, {"Int": 5}, {"Float": 1.6}, {"NoneType": null}], "co_name": "<module>", "co_names": ["AssertionError"], "co_code": [[3, "LOAD_CONST", 0], [3, "LOAD_CONST", 0], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 18], [3, "LOAD_GLOBAL", 0], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 1], [3, "LOAD_CONST", 1], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 36], [3, "LOAD_GLOBAL", 0], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 2], [3, "LOAD_CONST", 3], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 54], [3, "LOAD_GLOBAL", 0], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 2], [3, "LOAD_CONST", 2], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 72], [3, "LOAD_GLOBAL", 0], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 4], [3, "LOAD_CONST", 4], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 90], [3, "LOAD_GLOBAL", 0], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 5], [1, "RETURN_VALUE", null]], "co_varnames": []}
|
||||
@@ -0,0 +1,15 @@
|
||||
assert 5.666666666666667 == 17 / 3 # classic division returns a float
|
||||
|
||||
assert 5 == 17 // 3 # floor division discards the fractional part
|
||||
|
||||
assert 2 == 17 % 3 # the % operator returns the remainder of the division
|
||||
|
||||
assert 17 == 5 * 3 + 2 # result * divisor + remainder
|
||||
|
||||
print(17 / 3) # classic division returns a float
|
||||
|
||||
print(17 // 3) # floor division discards the fractional part
|
||||
|
||||
print(17 % 3) # the % operator returns the remainder of the division
|
||||
|
||||
print(5 * 3 + 2) # result * divisor + remainder
|
||||
@@ -0,0 +1 @@
|
||||
{"co_varnames": [], "co_names": ["AssertionError", "print"], "co_name": "<module>", "co_consts": [{"Float": 5.666666666666667}, {"Int": 5}, {"Int": 2}, {"Int": 17}, {"NoneType": null}], "co_code": [[3, "LOAD_CONST", 0], [3, "LOAD_CONST", 0], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 18], [3, "LOAD_GLOBAL", 0], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 1], [3, "LOAD_CONST", 1], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 36], [3, "LOAD_GLOBAL", 0], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 2], [3, "LOAD_CONST", 2], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 54], [3, "LOAD_GLOBAL", 0], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 3], [3, "LOAD_CONST", 3], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 72], [3, "LOAD_GLOBAL", 0], [3, "RAISE_VARARGS", 1], [3, "LOAD_NAME", 1], [3, "LOAD_CONST", 0], [3, "CALL_FUNCTION", 1], [1, "POP_TOP", null], [3, "LOAD_NAME", 1], [3, "LOAD_CONST", 1], [3, "CALL_FUNCTION", 1], [1, "POP_TOP", null], [3, "LOAD_NAME", 1], [3, "LOAD_CONST", 2], [3, "CALL_FUNCTION", 1], [1, "POP_TOP", null], [3, "LOAD_NAME", 1], [3, "LOAD_CONST", 3], [3, "CALL_FUNCTION", 1], [1, "POP_TOP", null], [3, "LOAD_CONST", 4], [1, "RETURN_VALUE", null]]}
|
||||
@@ -0,0 +1,3 @@
|
||||
assert 25 == 5 ** 2 # 5 squared
|
||||
|
||||
assert 128 == 2 ** 7 # 2 to the power of 7
|
||||
@@ -0,0 +1 @@
|
||||
{"co_consts": [{"Int": 25}, {"Int": 128}, {"NoneType": null}], "co_varnames": [], "co_name": "<module>", "co_names": ["AssertionError"], "co_code": [[3, "LOAD_CONST", 0], [3, "LOAD_CONST", 0], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 18], [3, "LOAD_GLOBAL", 0], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 1], [3, "LOAD_CONST", 1], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 36], [3, "LOAD_GLOBAL", 0], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 2], [1, "RETURN_VALUE", null]]}
|
||||
@@ -0,0 +1,4 @@
|
||||
width = 20
|
||||
height = 5 * 9
|
||||
assert 900 == width * height
|
||||
print(width * height)
|
||||
@@ -0,0 +1 @@
|
||||
{"co_code": [[3, "LOAD_CONST", 0], [3, "STORE_NAME", 0], [3, "LOAD_CONST", 1], [3, "STORE_NAME", 1], [3, "LOAD_CONST", 2], [3, "LOAD_NAME", 0], [3, "LOAD_NAME", 1], [1, "BINARY_MULTIPLY", null], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 34], [3, "LOAD_GLOBAL", 2], [3, "RAISE_VARARGS", 1], [3, "LOAD_NAME", 3], [3, "LOAD_NAME", 0], [3, "LOAD_NAME", 1], [1, "BINARY_MULTIPLY", null], [3, "CALL_FUNCTION", 1], [1, "POP_TOP", null], [3, "LOAD_CONST", 3], [1, "RETURN_VALUE", null]], "co_consts": [{"Int": 20}, {"Int": 45}, {"Int": 900}, {"NoneType": null}], "co_name": "<module>", "co_varnames": [], "co_names": ["width", "height", "AssertionError", "print"]}
|
||||
@@ -0,0 +1,3 @@
|
||||
assert 7.5 == 3 * 3.75 / 1.5
|
||||
|
||||
assert 3.5 == 7.0 / 2
|
||||
@@ -0,0 +1 @@
|
||||
{"co_code": [[3, "LOAD_CONST", 0], [3, "LOAD_CONST", 0], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 18], [3, "LOAD_GLOBAL", 0], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 1], [3, "LOAD_CONST", 1], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 36], [3, "LOAD_GLOBAL", 0], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 2], [1, "RETURN_VALUE", null]], "co_consts": [{"Float": 7.5}, {"Float": 3.5}, {"NoneType": null}], "co_name": "<module>", "co_names": ["AssertionError"], "co_varnames": []}
|
||||
@@ -0,0 +1,7 @@
|
||||
assert 'spam eggs' == 'spam eggs' # single quotes
|
||||
assert "doesn't" == 'doesn\'t' # use \' to escape the single quote...
|
||||
assert "doesn't" == "doesn't" # ...or use double quotes instead
|
||||
assert '"Yes," he said.' == '"Yes," he said.'
|
||||
assert '"Yes," he said.' == "\"Yes,\" he said."
|
||||
assert '"Isn\'t," she said.' == '"Isn\'t," she said.'
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{"co_consts": [{"Str": "spam eggs"}, {"Str": "doesn't"}, {"Str": "\"Yes,\" he said."}, {"Str": "\"Isn't,\" she said."}, {"NoneType": null}], "co_code": [[3, "LOAD_CONST", 0], [3, "LOAD_CONST", 0], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 18], [3, "LOAD_GLOBAL", 0], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 1], [3, "LOAD_CONST", 1], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 36], [3, "LOAD_GLOBAL", 0], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 1], [3, "LOAD_CONST", 1], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 54], [3, "LOAD_GLOBAL", 0], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 2], [3, "LOAD_CONST", 2], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 72], [3, "LOAD_GLOBAL", 0], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 2], [3, "LOAD_CONST", 2], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 90], [3, "LOAD_GLOBAL", 0], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 3], [3, "LOAD_CONST", 3], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 108], [3, "LOAD_GLOBAL", 0], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 4], [1, "RETURN_VALUE", null]], "co_varnames": [], "co_names": ["AssertionError"], "co_name": "<module>"}
|
||||
@@ -0,0 +1,6 @@
|
||||
word = 'Python'
|
||||
assert 'P' == word[0] # character in position 0
|
||||
assert 'n' == word[5] # character in position 5
|
||||
assert 'n' == word[-1] # last character
|
||||
assert 'o' == word[-2] # second-last character
|
||||
assert 'P' == word[-6]
|
||||
@@ -0,0 +1 @@
|
||||
{"co_name": "<module>", "co_consts": [{"Str": "Python"}, {"Str": "P"}, {"Int": 0}, {"Str": "n"}, {"Int": 5}, {"Int": -1}, {"Str": "o"}, {"Int": -2}, {"Int": -6}, {"NoneType": null}], "co_names": ["word", "AssertionError"], "co_varnames": [], "co_code": [[3, "LOAD_CONST", 0], [3, "STORE_NAME", 0], [3, "LOAD_CONST", 1], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 2], [1, "BINARY_SUBSCR", null], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 28], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 3], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 4], [1, "BINARY_SUBSCR", null], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 50], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 3], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 5], [1, "BINARY_SUBSCR", null], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 72], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 6], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 7], [1, "BINARY_SUBSCR", null], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 94], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 1], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 8], [1, "BINARY_SUBSCR", null], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 116], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 9], [1, "RETURN_VALUE", null]]}
|
||||
@@ -0,0 +1 @@
|
||||
print('C:\some\name')
|
||||
@@ -0,0 +1 @@
|
||||
{"co_name": "<module>", "co_code": [[3, "LOAD_NAME", 0], [3, "LOAD_CONST", 0], [3, "CALL_FUNCTION", 1], [1, "POP_TOP", null], [3, "LOAD_CONST", 1], [1, "RETURN_VALUE", null]], "co_names": ["print"], "co_consts": [{"Str": "C:\\some\name"}, {"NoneType": null}], "co_varnames": []}
|
||||
@@ -0,0 +1,5 @@
|
||||
print("""\
|
||||
Usage: thingy [OPTIONS]
|
||||
-h Display this usage message
|
||||
-H hostname Hostname to connect to
|
||||
""")
|
||||
@@ -0,0 +1 @@
|
||||
{"co_code": [[3, "LOAD_NAME", 0], [3, "LOAD_CONST", 0], [3, "CALL_FUNCTION", 1], [1, "POP_TOP", null], [3, "LOAD_CONST", 1], [1, "RETURN_VALUE", null]], "co_names": ["print"], "co_varnames": [], "co_consts": [{"Str": "Usage: thingy [OPTIONS]\n -h Display this usage message\n -H hostname Hostname to connect to\n"}, {"NoneType": null}], "co_name": "<module>"}
|
||||
@@ -0,0 +1 @@
|
||||
assert 'unununium' == 3 * 'un' + 'ium'
|
||||
@@ -0,0 +1 @@
|
||||
{"co_code": [[3, "LOAD_CONST", 0], [3, "LOAD_CONST", 0], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 18], [3, "LOAD_GLOBAL", 0], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 1], [1, "RETURN_VALUE", null]], "co_names": ["AssertionError"], "co_name": "<module>", "co_consts": [{"Str": "unununium"}, {"NoneType": null}], "co_varnames": []}
|
||||
@@ -0,0 +1 @@
|
||||
assert 'Python' == 'Py' 'thon'
|
||||
@@ -0,0 +1 @@
|
||||
{"co_consts": [{"Str": "Python"}, {"NoneType": null}], "co_varnames": [], "co_code": [[3, "LOAD_CONST", 0], [3, "LOAD_CONST", 0], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 18], [3, "LOAD_GLOBAL", 0], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 1], [1, "RETURN_VALUE", null]], "co_name": "<module>", "co_names": ["AssertionError"]}
|
||||
@@ -0,0 +1,4 @@
|
||||
x = [1,2,3]
|
||||
assert x[0] == 1
|
||||
assert x[1] == 2
|
||||
# assert x[7]
|
||||
@@ -0,0 +1 @@
|
||||
{"co_names": ["x", "AssertionError"], "co_name": "<module>", "co_code": [[3, "LOAD_CONST", 0], [3, "LOAD_CONST", 1], [3, "LOAD_CONST", 2], [3, "BUILD_LIST", 3], [3, "STORE_NAME", 0], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 3], [1, "BINARY_SUBSCR", null], [3, "LOAD_CONST", 0], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 37], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 0], [1, "BINARY_SUBSCR", null], [3, "LOAD_CONST", 1], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 59], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 4], [1, "RETURN_VALUE", null]], "co_consts": [{"Int": 1}, {"Int": 2}, {"Int": 3}, {"Int": 0}, {"NoneType": null}], "co_varnames": []}
|
||||
@@ -0,0 +1,13 @@
|
||||
# i = 0
|
||||
# while i < 5:
|
||||
# i += 1
|
||||
# # print(i)
|
||||
# assert i == 5
|
||||
|
||||
i = 0
|
||||
while i < 5:
|
||||
i += 1
|
||||
# print(i)
|
||||
if i == 3:
|
||||
break
|
||||
assert i == 3
|
||||
@@ -0,0 +1 @@
|
||||
{"co_name": "<module>", "co_code": [[3, "LOAD_CONST", 0], [3, "STORE_NAME", 0], [3, "SETUP_LOOP", 39], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 1], [3, "COMPARE_OP", 0], [3, "POP_JUMP_IF_FALSE", 47], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 2], [1, "INPLACE_ADD", null], [3, "STORE_NAME", 0], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 3], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_FALSE", 9], [1, "BREAK_LOOP", null], [3, "JUMP_ABSOLUTE", 9], [1, "POP_BLOCK", null], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 3], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 66], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 4], [1, "RETURN_VALUE", null]], "co_consts": [{"Int": 0}, {"Int": 5}, {"Int": 1}, {"Int": 3}, {"NoneType": null}], "co_names": ["i", "AssertionError"], "co_varnames": []}
|
||||
@@ -0,0 +1,18 @@
|
||||
a = 4
|
||||
|
||||
#print(a ** 3)
|
||||
#print(a * 3)
|
||||
#print(a / 2)
|
||||
#print(a % 3)
|
||||
#print(a - 3)
|
||||
#print(-a)
|
||||
#print(+a)
|
||||
|
||||
assert a ** 3 == 64
|
||||
assert a * 3 == 12
|
||||
assert a / 2 == 2
|
||||
assert a % 3 == 1
|
||||
assert a - 3 == 1
|
||||
assert -a == -4
|
||||
assert +a == 4
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{"co_varnames": [], "co_name": "<module>", "co_names": ["a", "AssertionError"], "co_code": [[3, "LOAD_CONST", 0], [3, "STORE_NAME", 0], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 1], [1, "BINARY_POWER", null], [3, "LOAD_CONST", 2], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 28], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 1], [1, "BINARY_MULTIPLY", null], [3, "LOAD_CONST", 3], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 50], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 4], [1, "BINARY_TRUE_DIVIDE", null], [3, "LOAD_CONST", 4], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 72], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 1], [1, "BINARY_MODULO", null], [3, "LOAD_CONST", 5], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 94], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 1], [1, "BINARY_SUBTRACT", null], [3, "LOAD_CONST", 5], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 116], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_NAME", 0], [1, "UNARY_NEGATIVE", null], [3, "LOAD_CONST", 6], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 135], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_NAME", 0], [1, "UNARY_POSITIVE", null], [3, "LOAD_CONST", 0], [3, "COMPARE_OP", 2], [3, "POP_JUMP_IF_TRUE", 154], [3, "LOAD_GLOBAL", 1], [3, "RAISE_VARARGS", 1], [3, "LOAD_CONST", 7], [1, "RETURN_VALUE", null]], "co_consts": [{"Int": 4}, {"Int": 3}, {"Int": 64}, {"Int": 12}, {"Int": 2}, {"Int": 1}, {"Int": -4}, {"NoneType": null}]}
|
||||