Added the shinglyu/RustPython VM

This commit is contained in:
Shing Lyu committed 2018-06-06 11:28:06 +02:00
1 parent cb394ebdc4
commit 1edbfd80f3
118 files changed
+3248

No files matched your search

+11
View File
@@ -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
+21
View File
@@ -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.
+64
View File
@@ -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
+205
View File
@@ -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"
+11
View File
@@ -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"
+47
View File
@@ -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)
}
+990
View File
@@ -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());
}
+56
View File
@@ -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()
+30
View File
@@ -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
+17
View File
@@ -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
+13
View File
@@ -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
+13
View File
@@ -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
+5
View File
@@ -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/
+43
View File
@@ -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
![rust_and_servo](data:image/png;base64,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 truncated
---
### Python's architecture
* Interpreted
* Garbage Collected
* Compiler => bytecode => VM
![python](data:image/png;base64,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 truncated
---
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
![qr_code](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJQAAACUCAYAAAB1PADUAAADeElEQVR4nO3dQY7jOBBFwfGg73/l6r02GiIfKbkmYlu2pDI+iARJJT8/Pz8//0Dk36cfgN9FoEgJFCmBIiVQpASKlECREihSAkVKoEgJFCmBIiVQpASKlECR+jO9wOfzKZ7jP1vdvnX3fNfrXT9/d7/6/797nt2m2+OMUKQEipRAkRrXUFf1FvXVGmK1Bpre/3QN9vTve8cIRUqgSAkUqbyGutpdI9Q11nQeaLWm2l3jXe1+DdMIRUqgSAkUqe011NtM54VWvz+dp/o2RihSAkVKoEj972qoet5mtWZa/f63MUKREihSAkVqew31tppgWuOsXm/3//+239cIRUqgSAkUqbyGOv0e2dW0pln9/u6/Xz39+94xQpESKFICRerz7We9nJ4nmu6X+u2MUKQEipRAkcr7Q+3ub7T7vbbd13t63sl7eXwVgSIlUKSO7ymf7tG++/zq/aa9CKb7qeoabjqvpscmryJQpASKVL6Wd3pe6s7uflP1/Z6ugaaMUKQEipRAkRrXUHUPyrvPT++/avda3O6+46ffGzRCkRIoUgJFaryWV88zvW3eajqvVq9drjo9T2WEIiVQpASK1Ov6Q+1eC6xroFXT/VN3nl4LNEKREihSAkVqe2+Dae+Bpz9/NV3Le3ot0zwUX0WgSAkUqe29Daama2tP77eazqOdPptmyghFSqBICRSp433K6/1G9fVXexmc7q2we15ryghFSqBICRSp1+0p392D8m37ke5M+0ud7k9lhCIlUKQEitTxHpt3Tr+LX897Te93+r1Ea3m8mkCREihSx9/L230GcG11La8+K+buee7oD8VXEyhSAkVqe5/y006fJfM2p+fVroxQpASKlECR2t7bYLenz+Tdvee9vv8dNRSvIlCkBIpU3ttgt+kZwE/3Gpje7/SZxKuMUKQEipRAkcr3Q739vbSna5hV0/1PV7t7HxihSAkUKYEitX1P+en9OG/vc17XeHecl8dXEyhSAkXqdb0Npt5Wg02vv2ra/8o8FK8iUKQEitSvq6F29/ne3aPz6f1lU0YoUgJFSqBIHe8P9fbr1/uJ6rNZdteIU0YoUgJFSqBIHT8v7/T9Tp/BO61x7q63+35TRihSAkVKoEh9fX8o3sUIRUqgSAkUKYEiJVCkBIqUQJESKFICRUqgSAkUKYEiJVCkBIqUQJH6C+akK0TJ2q/FAAAAAElFTkSuQmCC)
---
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>
Binary file not shown.

After

Width:  |  Height:  |  Size: 435 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

+286
View File
@@ -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
![rust_and_servo](pic/rust-servo.png)
---
### Python's architecture
* Interpreted
* Garbage Collected
* Compiler => bytecode => VM
![python](pic/python-logo.png)
---
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
![qr_code](pic/repo_QR.png)
---
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)
Binary file not shown.

After

Width:  |  Height:  |  Size: 282 KiB

+180
View File
@@ -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
+6
View File
@@ -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?
+7
View File
@@ -0,0 +1,7 @@
virtualenv venv --python=python3
source venv/bin/activate
pip install bytecode
source ~/.cargo/env
rustup install nightly
rustup default nightly
+219
View File
@@ -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"
+10
View File
@@ -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 = "*"
+7
View File
@@ -0,0 +1,7 @@
extern crate python_compiler;
use python_compiler::python_compiler::compile;
fn main() {
println!("{:?}", compile());
}
+4
View File
@@ -0,0 +1,4 @@
extern crate cpython;
extern crate python27_sys;
pub mod python_compiler;
+35
View File
@@ -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"));
}
+1
View File
@@ -0,0 +1 @@
byteplay==0.2
+1
View File
@@ -0,0 +1 @@
print(2 + 3)
Executable
+43
View File
@@ -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
+11
View File
@@ -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 ..
Executable
+65
View File
@@ -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 "================"
+10
View File
@@ -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]
+1
View File
@@ -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}]}
+3
View File
@@ -0,0 +1,3 @@
word = "Python"
assert "on" == word[4:42]
assert "" == word[42:]
+1
View File
@@ -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]]}
+1
View File
@@ -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]]}
+4
View File
@@ -0,0 +1,4 @@
word = "Python"
assert "Jython" == "J" + word[1:]
assert "Pypy" == word[:2] + "py"
+1
View File
@@ -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]]}
+3
View File
@@ -0,0 +1,3 @@
s = "abcdefg"
assert 7 == len(s)
+1
View File
@@ -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": []}
+6
View File
@@ -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[:]
+1
View File
@@ -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]]}
+3
View File
@@ -0,0 +1,3 @@
l = [1,2,3]
assert [1,2,3,4,5] == (l + [4,5])
+1
View File
@@ -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]]}
+3
View File
@@ -0,0 +1,3 @@
x = [1,999,3]
x[1] = 2
assert [1,2,3] == x
+1
View File
@@ -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>"}
+2
View File
@@ -0,0 +1,2 @@
x = []
x.append(1)
+1
View File
@@ -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"]}
+16
View File
@@ -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})
+1
View File
@@ -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]]}
+2
View File
@@ -0,0 +1,2 @@
assert 3 == len([1,2,3])
assert 2 == len((1,2))
+1
View File
@@ -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>"}
+39
View File
@@ -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)
+1
View File
@@ -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"]}
+1
View File
@@ -0,0 +1 @@
1 + 1.1
+1
View File
@@ -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}]}
+6
View File
@@ -0,0 +1,6 @@
x = 0
for i in [1,2,3,4]:
x += 1
assert x == 4
+1
View File
@@ -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": []}
+4
View File
@@ -0,0 +1,4 @@
def foo():
return 42
assert foo() == 42
+1
View File
@@ -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"]}
+17
View File
@@ -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
+1
View File
@@ -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": []}
+8
View File
@@ -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
+1
View File
@@ -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": []}
+15
View File
@@ -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
+1
View File
@@ -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]]}
+8
View File
@@ -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
+1
View File
@@ -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": []}
+15
View File
@@ -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
+1
View File
@@ -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]]}
+3
View File
@@ -0,0 +1,3 @@
assert 25 == 5 ** 2 # 5 squared
assert 128 == 2 ** 7 # 2 to the power of 7
+1
View File
@@ -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]]}
+4
View File
@@ -0,0 +1,4 @@
width = 20
height = 5 * 9
assert 900 == width * height
print(width * height)
+1
View File
@@ -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"]}
+3
View File
@@ -0,0 +1,3 @@
assert 7.5 == 3 * 3.75 / 1.5
assert 3.5 == 7.0 / 2
+1
View File
@@ -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": []}
+7
View File
@@ -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.'
+1
View File
@@ -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>"}
+6
View File
@@ -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]
+1
View File
@@ -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]]}
+1
View File
@@ -0,0 +1 @@
print('C:\some\name')
+1
View File
@@ -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": []}
+5
View File
@@ -0,0 +1,5 @@
print("""\
Usage: thingy [OPTIONS]
-h Display this usage message
-H hostname Hostname to connect to
""")
+1
View File
@@ -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>"}
+1
View File
@@ -0,0 +1 @@
assert 'unununium' == 3 * 'un' + 'ium'
+1
View File
@@ -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": []}
+1
View File
@@ -0,0 +1 @@
assert 'Python' == 'Py' 'thon'
+1
View File
@@ -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"]}
+4
View File
@@ -0,0 +1,4 @@
x = [1,2,3]
assert x[0] == 1
assert x[1] == 2
# assert x[7]
+1
View File
@@ -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": []}
+13
View File
@@ -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
+1
View File
@@ -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": []}
+18
View File
@@ -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
+1
View File
@@ -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}]}
Loaded 100 of 118 files, more files were not shown because too many files have changed in this diff. Show more