diff --git a/README.md b/README.md index 3900c11e0..00a425c0d 100644 --- a/README.md +++ b/README.md @@ -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 + diff --git a/VM/LICENSE b/VM/LICENSE new file mode 100644 index 000000000..d3a3b7bbb --- /dev/null +++ b/VM/LICENSE @@ -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. diff --git a/VM/README.md b/VM/README.md new file mode 100644 index 000000000..34bbf14b5 --- /dev/null +++ b/VM/README.md @@ -0,0 +1,64 @@ +RustPython +============== + +A Python interpreter written in Rust + +# Installation + +``` +bash init_env.sh +``` + +# Run + +``` +./test.sh # compile and run +./test.sh --bytecode # print the bytecode in JSON +./test.sh --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 + diff --git a/VM/RustPython/Cargo.lock b/VM/RustPython/Cargo.lock new file mode 100644 index 000000000..9a7a1f3ae --- /dev/null +++ b/VM/RustPython/Cargo.lock @@ -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" diff --git a/VM/RustPython/Cargo.toml b/VM/RustPython/Cargo.toml new file mode 100644 index 000000000..39d864dcf --- /dev/null +++ b/VM/RustPython/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "rustpython" +version = "0.1.0" +authors = ["Shing Lyu "] + +[dependencies] +log = "0.3" +env_logger = "0.3" +serde = "0.8.22" +serde_derive = "0.8" +serde_json = "0.8" diff --git a/VM/RustPython/src/builtins.rs b/VM/RustPython/src/builtins.rs new file mode 100644 index 000000000..1dbd1ef4a --- /dev/null +++ b/VM/RustPython/src/builtins.rs @@ -0,0 +1,47 @@ +use NativeType; +use std::rc::Rc; +use std::ops::Deref; + +pub fn print(args: Vec>) -> 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), + Tuple(Vec), + Iter(Vec), // TODO: use Iterator instead + Code(PyCodeObject), + Function(Function), + #[serde(skip_serializing, skip_deserializing)] + NativeFunction(fn(Vec) -> NativeType ), + */ + } + } + NativeType::NoneType +} + +pub fn len(args: Vec>) -> 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) +} diff --git a/VM/RustPython/src/main.rs b/VM/RustPython/src/main.rs new file mode 100644 index 000000000..6b61c3aad --- /dev/null +++ b/VM/RustPython/src/main.rs @@ -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>), + Tuple(Vec), + Iter(Vec), // TODO: use Iterator instead + Code(PyCodeObject), + Function(Function), + Slice(Option, Option, Option), // start, stop, step + #[serde(skip_serializing, skip_deserializing)] + NativeFunction(fn(Vec>) -> 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 in stack for handline None return value + code: PyCodeObject, + // We need 1 stack per frame + stack: Vec>, // The main data frame of the stack machine + blocks: Vec, // Block frames, for controling loops and exceptions + globals: HashMap>, // Variables + locals: HashMap>, // Variables + labels: HashMap, // 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 { + // 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, +} + +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>, globals: Option>>) -> 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 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)) -> Option { + { + 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::(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::(&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::(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> = 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, + co_names: Vec, + co_code: Vec<(usize, String, Option)>, //size, name, args + co_varnames: Vec, +} + + +/* +fn parse_native_type(val_str: &str) -> Result { + // 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::() { + return Ok(NativeType::Int(int)) + } + + if let Ok(float) = val_str.parse::() { + 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= 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)>= 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::().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 = 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()); +} diff --git a/VM/compile_code.py b/VM/compile_code.py new file mode 100644 index 000000000..651ce266b --- /dev/null +++ b/VM/compile_code.py @@ -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() diff --git a/VM/docs/builtins.md b/VM/docs/builtins.md new file mode 100644 index 000000000..d918f0861 --- /dev/null +++ b/VM/docs/builtins.md @@ -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 + diff --git a/VM/docs/compat-test.md b/VM/docs/compat-test.md new file mode 100644 index 000000000..35c84fdc0 --- /dev/null +++ b/VM/docs/compat-test.md @@ -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 diff --git a/VM/docs/frameTODO.txt b/VM/docs/frameTODO.txt new file mode 100644 index 000000000..58b661ade --- /dev/null +++ b/VM/docs/frameTODO.txt @@ -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 + diff --git a/VM/docs/insidepythonpresentation-120724001527-phpapp02.pdf b/VM/docs/insidepythonpresentation-120724001527-phpapp02.pdf new file mode 100644 index 000000000..8e17cb246 Binary files /dev/null and b/VM/docs/insidepythonpresentation-120724001527-phpapp02.pdf differ diff --git a/VM/docs/notes.md b/VM/docs/notes.md new file mode 100644 index 000000000..53f543e57 --- /dev/null +++ b/VM/docs/notes.md @@ -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 diff --git a/VM/docs/reference.md b/VM/docs/reference.md new file mode 100644 index 000000000..393d52948 --- /dev/null +++ b/VM/docs/reference.md @@ -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/ diff --git a/VM/docs/sharing.md b/VM/docs/sharing.md new file mode 100644 index 000000000..19a4970fd --- /dev/null +++ b/VM/docs/sharing.md @@ -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 + diff --git a/VM/docs/slides/201708_COSCUP/coscup-2017-rustpython_slide.html b/VM/docs/slides/201708_COSCUP/coscup-2017-rustpython_slide.html new file mode 100644 index 000000000..d453add7b --- /dev/null +++ b/VM/docs/slides/201708_COSCUP/coscup-2017-rustpython_slide.html @@ -0,0 +1,529 @@ + + + + Flavors (powered by MDSlides) + + + + + + + + + + + diff --git a/VM/docs/slides/201708_COSCUP/pic/car_cutaway.jpg b/VM/docs/slides/201708_COSCUP/pic/car_cutaway.jpg new file mode 100644 index 000000000..9c09878e2 Binary files /dev/null and b/VM/docs/slides/201708_COSCUP/pic/car_cutaway.jpg differ diff --git a/VM/docs/slides/201708_COSCUP/pic/electronic_parts.jpg b/VM/docs/slides/201708_COSCUP/pic/electronic_parts.jpg new file mode 100644 index 000000000..d04c95f91 Binary files /dev/null and b/VM/docs/slides/201708_COSCUP/pic/electronic_parts.jpg differ diff --git a/VM/docs/slides/201708_COSCUP/pic/ice-cream.jpg b/VM/docs/slides/201708_COSCUP/pic/ice-cream.jpg new file mode 100644 index 000000000..bc224b521 Binary files /dev/null and b/VM/docs/slides/201708_COSCUP/pic/ice-cream.jpg differ diff --git a/VM/docs/slides/201708_COSCUP/pic/python-logo.png b/VM/docs/slides/201708_COSCUP/pic/python-logo.png new file mode 100644 index 000000000..738f6ed41 Binary files /dev/null and b/VM/docs/slides/201708_COSCUP/pic/python-logo.png differ diff --git a/VM/docs/slides/201708_COSCUP/pic/repo_QR.png b/VM/docs/slides/201708_COSCUP/pic/repo_QR.png new file mode 100644 index 000000000..c103af99e Binary files /dev/null and b/VM/docs/slides/201708_COSCUP/pic/repo_QR.png differ diff --git a/VM/docs/slides/201708_COSCUP/pic/rust-servo.png b/VM/docs/slides/201708_COSCUP/pic/rust-servo.png new file mode 100644 index 000000000..e7f077930 Binary files /dev/null and b/VM/docs/slides/201708_COSCUP/pic/rust-servo.png differ diff --git a/VM/docs/slides/201708_COSCUP/slide.md b/VM/docs/slides/201708_COSCUP/slide.md new file mode 100644 index 000000000..fec3019cb --- /dev/null +++ b/VM/docs/slides/201708_COSCUP/slide.md @@ -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), + ... +} +``` + +--- + +### 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), + ... +} + +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) + diff --git a/VM/docs/slides/intro/pic/ice-cream.jpg b/VM/docs/slides/intro/pic/ice-cream.jpg new file mode 100644 index 000000000..bc224b521 Binary files /dev/null and b/VM/docs/slides/intro/pic/ice-cream.jpg differ diff --git a/VM/docs/slides/intro/slide.md b/VM/docs/slides/intro/slide.md new file mode 100644 index 000000000..76839286f --- /dev/null +++ b/VM/docs/slides/intro/slide.md @@ -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 diff --git a/VM/docs/study.md b/VM/docs/study.md new file mode 100644 index 000000000..aff15aaf9 --- /dev/null +++ b/VM/docs/study.md @@ -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? diff --git a/VM/init_env.sh b/VM/init_env.sh new file mode 100644 index 000000000..7842e1c54 --- /dev/null +++ b/VM/init_env.sh @@ -0,0 +1,7 @@ +virtualenv venv --python=python3 +source venv/bin/activate +pip install bytecode + +source ~/.cargo/env +rustup install nightly +rustup default nightly diff --git a/VM/python_compiler/Cargo.lock b/VM/python_compiler/Cargo.lock new file mode 100644 index 000000000..fa14ac681 --- /dev/null +++ b/VM/python_compiler/Cargo.lock @@ -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)" = "" +"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)" = "" +"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" diff --git a/VM/python_compiler/Cargo.toml b/VM/python_compiler/Cargo.toml new file mode 100644 index 000000000..505bc8f4f --- /dev/null +++ b/VM/python_compiler/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "python_compiler" +version = "0.1.0" +authors = ["Shing Lyu "] + +[dependencies] +cpython = { git = "https://github.com/dgrunwald/rust-cpython.git" } + +[dependencies.python27-sys] +version = "*" diff --git a/VM/python_compiler/examples/compile.rs b/VM/python_compiler/examples/compile.rs new file mode 100644 index 000000000..1efdaad51 --- /dev/null +++ b/VM/python_compiler/examples/compile.rs @@ -0,0 +1,7 @@ +extern crate python_compiler; + +use python_compiler::python_compiler::compile; + +fn main() { + println!("{:?}", compile()); +} diff --git a/VM/python_compiler/src/lib.rs b/VM/python_compiler/src/lib.rs new file mode 100644 index 000000000..0d4a2b4d6 --- /dev/null +++ b/VM/python_compiler/src/lib.rs @@ -0,0 +1,4 @@ +extern crate cpython; +extern crate python27_sys; + +pub mod python_compiler; diff --git a/VM/python_compiler/src/python_compiler.rs b/VM/python_compiler/src/python_compiler.rs new file mode 100644 index 000000000..6d34979ff --- /dev/null +++ b/VM/python_compiler/src/python_compiler.rs @@ -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")); + +} diff --git a/VM/requirements.txt b/VM/requirements.txt new file mode 100644 index 000000000..027584177 --- /dev/null +++ b/VM/requirements.txt @@ -0,0 +1 @@ +byteplay==0.2 diff --git a/VM/test.py b/VM/test.py new file mode 100644 index 000000000..816fea865 --- /dev/null +++ b/VM/test.py @@ -0,0 +1 @@ +print(2 + 3) diff --git a/VM/test.sh b/VM/test.sh new file mode 100755 index 000000000..03828b006 --- /dev/null +++ b/VM/test.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +# Usage: test.sh [--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 + diff --git a/VM/test_all.bat b/VM/test_all.bat new file mode 100644 index 000000000..139811e43 --- /dev/null +++ b/VM/test_all.bat @@ -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 .. diff --git a/VM/test_all.sh b/VM/test_all.sh new file mode 100755 index 000000000..0f9808e5f --- /dev/null +++ b/VM/test_all.sh @@ -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 "================" diff --git a/VM/tests/3.1.2.13.py b/VM/tests/3.1.2.13.py new file mode 100644 index 000000000..baf0192b1 --- /dev/null +++ b/VM/tests/3.1.2.13.py @@ -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] diff --git a/VM/tests/3.1.2.13.py.bytecode b/VM/tests/3.1.2.13.py.bytecode new file mode 100644 index 000000000..17b0b93ec --- /dev/null +++ b/VM/tests/3.1.2.13.py.bytecode @@ -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": "", "co_names": ["word", "AssertionError"], "co_consts": [{"Str": "Python"}, {"NoneType": null}, {"Int": 2}, {"Int": 4}, {"Str": "Py"}, {"Str": "on"}, {"Int": -2}, {"Int": -4}]} diff --git a/VM/tests/3.1.2.16.py b/VM/tests/3.1.2.16.py new file mode 100644 index 000000000..59afe4030 --- /dev/null +++ b/VM/tests/3.1.2.16.py @@ -0,0 +1,3 @@ +word = "Python" +assert "on" == word[4:42] +assert "" == word[42:] diff --git a/VM/tests/3.1.2.16.py.bytecode b/VM/tests/3.1.2.16.py.bytecode new file mode 100644 index 000000000..6888963b4 --- /dev/null +++ b/VM/tests/3.1.2.16.py.bytecode @@ -0,0 +1 @@ +{"co_name": "", "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]]} diff --git a/VM/tests/3.1.2.17.py.bytecode b/VM/tests/3.1.2.17.py.bytecode new file mode 100644 index 000000000..326409654 --- /dev/null +++ b/VM/tests/3.1.2.17.py.bytecode @@ -0,0 +1 @@ +{"co_varnames": [], "co_name": "", "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]]} diff --git a/VM/tests/3.1.2.18.py b/VM/tests/3.1.2.18.py new file mode 100644 index 000000000..a41a2dddd --- /dev/null +++ b/VM/tests/3.1.2.18.py @@ -0,0 +1,4 @@ +word = "Python" + +assert "Jython" == "J" + word[1:] +assert "Pypy" == word[:2] + "py" diff --git a/VM/tests/3.1.2.18.py.bytecode b/VM/tests/3.1.2.18.py.bytecode new file mode 100644 index 000000000..fdb5c263b --- /dev/null +++ b/VM/tests/3.1.2.18.py.bytecode @@ -0,0 +1 @@ +{"co_varnames": [], "co_names": ["word", "AssertionError"], "co_name": "", "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]]} diff --git a/VM/tests/3.1.2.19.py b/VM/tests/3.1.2.19.py new file mode 100644 index 000000000..8e262aa2f --- /dev/null +++ b/VM/tests/3.1.2.19.py @@ -0,0 +1,3 @@ +s = "abcdefg" + +assert 7 == len(s) diff --git a/VM/tests/3.1.2.19.py.bytecode b/VM/tests/3.1.2.19.py.bytecode new file mode 100644 index 000000000..11c588d3c --- /dev/null +++ b/VM/tests/3.1.2.19.py.bytecode @@ -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": "", "co_varnames": []} diff --git a/VM/tests/3.1.3.2.py b/VM/tests/3.1.3.2.py new file mode 100644 index 000000000..243d95e97 --- /dev/null +++ b/VM/tests/3.1.3.2.py @@ -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[:] diff --git a/VM/tests/3.1.3.2.py.bytecode b/VM/tests/3.1.3.2.py.bytecode new file mode 100644 index 000000000..bb7e9aec1 --- /dev/null +++ b/VM/tests/3.1.3.2.py.bytecode @@ -0,0 +1 @@ +{"co_name": "", "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]]} diff --git a/VM/tests/3.1.3.4.py b/VM/tests/3.1.3.4.py new file mode 100644 index 000000000..426c78b42 --- /dev/null +++ b/VM/tests/3.1.3.4.py @@ -0,0 +1,3 @@ +l = [1,2,3] +assert [1,2,3,4,5] == (l + [4,5]) + diff --git a/VM/tests/3.1.3.4.py.bytecode b/VM/tests/3.1.3.4.py.bytecode new file mode 100644 index 000000000..c70f10b1d --- /dev/null +++ b/VM/tests/3.1.3.4.py.bytecode @@ -0,0 +1 @@ +{"co_consts": [{"Int": 1}, {"Int": 2}, {"Int": 3}, {"Int": 4}, {"Int": 5}, {"NoneType": null}], "co_names": ["l", "AssertionError"], "co_name": "", "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]]} diff --git a/VM/tests/3.1.3.5.py b/VM/tests/3.1.3.5.py new file mode 100644 index 000000000..c841430b1 --- /dev/null +++ b/VM/tests/3.1.3.5.py @@ -0,0 +1,3 @@ +x = [1,999,3] +x[1] = 2 +assert [1,2,3] == x diff --git a/VM/tests/3.1.3.5.py.bytecode b/VM/tests/3.1.3.5.py.bytecode new file mode 100644 index 000000000..db94a34f4 --- /dev/null +++ b/VM/tests/3.1.3.5.py.bytecode @@ -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": ""} diff --git a/VM/tests/append.py b/VM/tests/append.py new file mode 100644 index 000000000..a0490cb6d --- /dev/null +++ b/VM/tests/append.py @@ -0,0 +1,2 @@ +x = [] +x.append(1) diff --git a/VM/tests/append.py.bytecode b/VM/tests/append.py.bytecode new file mode 100644 index 000000000..8b5a11267 --- /dev/null +++ b/VM/tests/append.py.bytecode @@ -0,0 +1 @@ +{"co_consts": [{"Int": 1}, {"NoneType": null}], "co_name": "", "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"]} diff --git a/VM/tests/basic_types.py b/VM/tests/basic_types.py new file mode 100644 index 000000000..34c35e117 --- /dev/null +++ b/VM/tests/basic_types.py @@ -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}) diff --git a/VM/tests/basic_types.py.bytecode b/VM/tests/basic_types.py.bytecode new file mode 100644 index 000000000..125ac3523 --- /dev/null +++ b/VM/tests/basic_types.py.bytecode @@ -0,0 +1 @@ +{"co_names": ["print"], "co_varnames": [], "co_name": "", "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]]} diff --git a/VM/tests/builtin_len.py b/VM/tests/builtin_len.py new file mode 100644 index 000000000..4872f20c4 --- /dev/null +++ b/VM/tests/builtin_len.py @@ -0,0 +1,2 @@ +assert 3 == len([1,2,3]) +assert 2 == len((1,2)) diff --git a/VM/tests/builtin_len.py.bytecode b/VM/tests/builtin_len.py.bytecode new file mode 100644 index 000000000..81ab9c7cc --- /dev/null +++ b/VM/tests/builtin_len.py.bytecode @@ -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": ""} diff --git a/VM/tests/cast.py b/VM/tests/cast.py new file mode 100644 index 000000000..fdf1613be --- /dev/null +++ b/VM/tests/cast.py @@ -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) diff --git a/VM/tests/cast.py.bytecode b/VM/tests/cast.py.bytecode new file mode 100644 index 000000000..84d1af4b9 --- /dev/null +++ b/VM/tests/cast.py.bytecode @@ -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": "", "co_varnames": [], "co_names": ["x", "y", "AssertionError"]} diff --git a/VM/tests/floats.py b/VM/tests/floats.py new file mode 100644 index 000000000..d4cc279a8 --- /dev/null +++ b/VM/tests/floats.py @@ -0,0 +1 @@ +1 + 1.1 diff --git a/VM/tests/floats.py.bytecode b/VM/tests/floats.py.bytecode new file mode 100644 index 000000000..70f695d7a --- /dev/null +++ b/VM/tests/floats.py.bytecode @@ -0,0 +1 @@ +{"co_names": [], "co_varnames": [], "co_name": "", "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}]} diff --git a/VM/tests/for.py b/VM/tests/for.py new file mode 100644 index 000000000..fa0147bca --- /dev/null +++ b/VM/tests/for.py @@ -0,0 +1,6 @@ +x = 0 +for i in [1,2,3,4]: + x += 1 + +assert x == 4 + diff --git a/VM/tests/for.py.bytecode b/VM/tests/for.py.bytecode new file mode 100644 index 000000000..2fb8ff948 --- /dev/null +++ b/VM/tests/for.py.bytecode @@ -0,0 +1 @@ +{"co_name": "", "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": []} diff --git a/VM/tests/function.py b/VM/tests/function.py new file mode 100644 index 000000000..a69178bb7 --- /dev/null +++ b/VM/tests/function.py @@ -0,0 +1,4 @@ +def foo(): + return 42 + +assert foo() == 42 diff --git a/VM/tests/function.py.bytecode b/VM/tests/function.py.bytecode new file mode 100644 index 000000000..09c10c20c --- /dev/null +++ b/VM/tests/function.py.bytecode @@ -0,0 +1 @@ +{"co_name": "", "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"]} diff --git a/VM/tests/function_args.py b/VM/tests/function_args.py new file mode 100644 index 000000000..729cd52f9 --- /dev/null +++ b/VM/tests/function_args.py @@ -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 diff --git a/VM/tests/function_args.py.bytecode b/VM/tests/function_args.py.bytecode new file mode 100644 index 000000000..72b7c01cc --- /dev/null +++ b/VM/tests/function_args.py.bytecode @@ -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": "", "co_varnames": []} diff --git a/VM/tests/function_nested.py b/VM/tests/function_nested.py new file mode 100644 index 000000000..fd3ed5be3 --- /dev/null +++ b/VM/tests/function_nested.py @@ -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 diff --git a/VM/tests/function_nested.py.bytecode b/VM/tests/function_nested.py.bytecode new file mode 100644 index 000000000..60e134ce7 --- /dev/null +++ b/VM/tests/function_nested.py.bytecode @@ -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": "", "co_varnames": []} diff --git a/VM/tests/if.py b/VM/tests/if.py new file mode 100644 index 000000000..f91edd302 --- /dev/null +++ b/VM/tests/if.py @@ -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 diff --git a/VM/tests/if.py.bytecode b/VM/tests/if.py.bytecode new file mode 100644 index 000000000..95e5521c6 --- /dev/null +++ b/VM/tests/if.py.bytecode @@ -0,0 +1 @@ +{"co_names": ["x", "AssertionError", "y"], "co_name": "", "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]]} diff --git a/VM/tests/intro/3.1.1.1.py b/VM/tests/intro/3.1.1.1.py new file mode 100644 index 000000000..8ef39bc8d --- /dev/null +++ b/VM/tests/intro/3.1.1.1.py @@ -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 diff --git a/VM/tests/intro/3.1.1.1.py.bytecode b/VM/tests/intro/3.1.1.1.py.bytecode new file mode 100644 index 000000000..34f812051 --- /dev/null +++ b/VM/tests/intro/3.1.1.1.py.bytecode @@ -0,0 +1 @@ +{"co_consts": [{"Int": 4}, {"Int": 20}, {"Float": 5.0}, {"Int": 5}, {"Float": 1.6}, {"NoneType": null}], "co_name": "", "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": []} diff --git a/VM/tests/intro/3.1.1.2.py b/VM/tests/intro/3.1.1.2.py new file mode 100644 index 000000000..f1ea276cf --- /dev/null +++ b/VM/tests/intro/3.1.1.2.py @@ -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 diff --git a/VM/tests/intro/3.1.1.2.py.bytecode b/VM/tests/intro/3.1.1.2.py.bytecode new file mode 100644 index 000000000..b46416c64 --- /dev/null +++ b/VM/tests/intro/3.1.1.2.py.bytecode @@ -0,0 +1 @@ +{"co_varnames": [], "co_names": ["AssertionError", "print"], "co_name": "", "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]]} diff --git a/VM/tests/intro/3.1.1.3.py b/VM/tests/intro/3.1.1.3.py new file mode 100644 index 000000000..1972e615c --- /dev/null +++ b/VM/tests/intro/3.1.1.3.py @@ -0,0 +1,3 @@ +assert 25 == 5 ** 2 # 5 squared + +assert 128 == 2 ** 7 # 2 to the power of 7 diff --git a/VM/tests/intro/3.1.1.3.py.bytecode b/VM/tests/intro/3.1.1.3.py.bytecode new file mode 100644 index 000000000..f9a9b1e40 --- /dev/null +++ b/VM/tests/intro/3.1.1.3.py.bytecode @@ -0,0 +1 @@ +{"co_consts": [{"Int": 25}, {"Int": 128}, {"NoneType": null}], "co_varnames": [], "co_name": "", "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]]} diff --git a/VM/tests/intro/3.1.1.4.py b/VM/tests/intro/3.1.1.4.py new file mode 100644 index 000000000..195713579 --- /dev/null +++ b/VM/tests/intro/3.1.1.4.py @@ -0,0 +1,4 @@ +width = 20 +height = 5 * 9 +assert 900 == width * height +print(width * height) diff --git a/VM/tests/intro/3.1.1.4.py.bytecode b/VM/tests/intro/3.1.1.4.py.bytecode new file mode 100644 index 000000000..6f4c39bdc --- /dev/null +++ b/VM/tests/intro/3.1.1.4.py.bytecode @@ -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": "", "co_varnames": [], "co_names": ["width", "height", "AssertionError", "print"]} diff --git a/VM/tests/intro/3.1.1.6.py b/VM/tests/intro/3.1.1.6.py new file mode 100644 index 000000000..7e898a793 --- /dev/null +++ b/VM/tests/intro/3.1.1.6.py @@ -0,0 +1,3 @@ +assert 7.5 == 3 * 3.75 / 1.5 + +assert 3.5 == 7.0 / 2 diff --git a/VM/tests/intro/3.1.1.6.py.bytecode b/VM/tests/intro/3.1.1.6.py.bytecode new file mode 100644 index 000000000..1c2d07ec0 --- /dev/null +++ b/VM/tests/intro/3.1.1.6.py.bytecode @@ -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": "", "co_names": ["AssertionError"], "co_varnames": []} diff --git a/VM/tests/intro/3.1.2.1.py b/VM/tests/intro/3.1.2.1.py new file mode 100644 index 000000000..9fd8ae6b0 --- /dev/null +++ b/VM/tests/intro/3.1.2.1.py @@ -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.' + diff --git a/VM/tests/intro/3.1.2.1.py.bytecode b/VM/tests/intro/3.1.2.1.py.bytecode new file mode 100644 index 000000000..2ca6594d1 --- /dev/null +++ b/VM/tests/intro/3.1.2.1.py.bytecode @@ -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": ""} diff --git a/VM/tests/intro/3.1.2.10.py b/VM/tests/intro/3.1.2.10.py new file mode 100644 index 000000000..b69c2029c --- /dev/null +++ b/VM/tests/intro/3.1.2.10.py @@ -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] diff --git a/VM/tests/intro/3.1.2.10.py.bytecode b/VM/tests/intro/3.1.2.10.py.bytecode new file mode 100644 index 000000000..c19148c92 --- /dev/null +++ b/VM/tests/intro/3.1.2.10.py.bytecode @@ -0,0 +1 @@ +{"co_name": "", "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]]} diff --git a/VM/tests/intro/3.1.2.3.py b/VM/tests/intro/3.1.2.3.py new file mode 100644 index 000000000..9e63ae92b --- /dev/null +++ b/VM/tests/intro/3.1.2.3.py @@ -0,0 +1 @@ +print('C:\some\name') diff --git a/VM/tests/intro/3.1.2.3.py.bytecode b/VM/tests/intro/3.1.2.3.py.bytecode new file mode 100644 index 000000000..b2bdbfc66 --- /dev/null +++ b/VM/tests/intro/3.1.2.3.py.bytecode @@ -0,0 +1 @@ +{"co_name": "", "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": []} diff --git a/VM/tests/intro/3.1.2.4.py b/VM/tests/intro/3.1.2.4.py new file mode 100644 index 000000000..8996ab7ad --- /dev/null +++ b/VM/tests/intro/3.1.2.4.py @@ -0,0 +1,5 @@ +print("""\ +Usage: thingy [OPTIONS] + -h Display this usage message + -H hostname Hostname to connect to +""") diff --git a/VM/tests/intro/3.1.2.4.py.bytecode b/VM/tests/intro/3.1.2.4.py.bytecode new file mode 100644 index 000000000..4b50afad4 --- /dev/null +++ b/VM/tests/intro/3.1.2.4.py.bytecode @@ -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": ""} diff --git a/VM/tests/intro/3.1.2.5.py b/VM/tests/intro/3.1.2.5.py new file mode 100644 index 000000000..64ed5b98a --- /dev/null +++ b/VM/tests/intro/3.1.2.5.py @@ -0,0 +1 @@ +assert 'unununium' == 3 * 'un' + 'ium' diff --git a/VM/tests/intro/3.1.2.5.py.bytecode b/VM/tests/intro/3.1.2.5.py.bytecode new file mode 100644 index 000000000..4a72c9294 --- /dev/null +++ b/VM/tests/intro/3.1.2.5.py.bytecode @@ -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": "", "co_consts": [{"Str": "unununium"}, {"NoneType": null}], "co_varnames": []} diff --git a/VM/tests/intro/3.1.2.6.py b/VM/tests/intro/3.1.2.6.py new file mode 100644 index 000000000..92e434d61 --- /dev/null +++ b/VM/tests/intro/3.1.2.6.py @@ -0,0 +1 @@ +assert 'Python' == 'Py' 'thon' diff --git a/VM/tests/intro/3.1.2.6.py.bytecode b/VM/tests/intro/3.1.2.6.py.bytecode new file mode 100644 index 000000000..26e852c49 --- /dev/null +++ b/VM/tests/intro/3.1.2.6.py.bytecode @@ -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": "", "co_names": ["AssertionError"]} diff --git a/VM/tests/list.py b/VM/tests/list.py new file mode 100644 index 000000000..de694b3df --- /dev/null +++ b/VM/tests/list.py @@ -0,0 +1,4 @@ +x = [1,2,3] +assert x[0] == 1 +assert x[1] == 2 +# assert x[7] diff --git a/VM/tests/list.py.bytecode b/VM/tests/list.py.bytecode new file mode 100644 index 000000000..d2fc63506 --- /dev/null +++ b/VM/tests/list.py.bytecode @@ -0,0 +1 @@ +{"co_names": ["x", "AssertionError"], "co_name": "", "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": []} diff --git a/VM/tests/loop.py b/VM/tests/loop.py new file mode 100644 index 000000000..d5c40b0c1 --- /dev/null +++ b/VM/tests/loop.py @@ -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 diff --git a/VM/tests/loop.py.bytecode b/VM/tests/loop.py.bytecode new file mode 100644 index 000000000..f4af806f6 --- /dev/null +++ b/VM/tests/loop.py.bytecode @@ -0,0 +1 @@ +{"co_name": "", "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": []} diff --git a/VM/tests/math.py b/VM/tests/math.py new file mode 100644 index 000000000..fb71da752 --- /dev/null +++ b/VM/tests/math.py @@ -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 + diff --git a/VM/tests/math.py.bytecode b/VM/tests/math.py.bytecode new file mode 100644 index 000000000..bff5e360c --- /dev/null +++ b/VM/tests/math.py.bytecode @@ -0,0 +1 @@ +{"co_varnames": [], "co_name": "", "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}]} diff --git a/VM/tests/minimum.py b/VM/tests/minimum.py new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/VM/tests/minimum.py @@ -0,0 +1 @@ +1 diff --git a/VM/tests/minimum.py.bytecode b/VM/tests/minimum.py.bytecode new file mode 100644 index 000000000..b08e25ee4 --- /dev/null +++ b/VM/tests/minimum.py.bytecode @@ -0,0 +1 @@ +{"co_name": "", "co_varnames": [], "co_names": [], "co_consts": [{"NoneType": null}], "co_code": [[3, "LOAD_CONST", 0], [1, "RETURN_VALUE", null]]} diff --git a/VM/tests/perf_add.py b/VM/tests/perf_add.py new file mode 100644 index 000000000..724c2fb26 --- /dev/null +++ b/VM/tests/perf_add.py @@ -0,0 +1,11 @@ +j = 0 +while j < 1000: + + total = 0 + i = 0 + while i < 100: + total += i + i += 1 + # print(total) + + j += 1 diff --git a/VM/tests/perf_add.py.bytecode b/VM/tests/perf_add.py.bytecode new file mode 100644 index 000000000..2003bccef --- /dev/null +++ b/VM/tests/perf_add.py.bytecode @@ -0,0 +1 @@ +{"co_name": "", "co_code": [[3, "LOAD_CONST", 0], [3, "STORE_NAME", 0], [3, "SETUP_LOOP", 77], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 1], [3, "COMPARE_OP", 0], [3, "POP_JUMP_IF_FALSE", 85], [3, "LOAD_CONST", 0], [3, "STORE_NAME", 1], [3, "LOAD_CONST", 0], [3, "STORE_NAME", 2], [3, "SETUP_LOOP", 36], [3, "LOAD_NAME", 2], [3, "LOAD_CONST", 2], [3, "COMPARE_OP", 0], [3, "POP_JUMP_IF_FALSE", 71], [3, "LOAD_NAME", 1], [3, "LOAD_NAME", 2], [1, "INPLACE_ADD", null], [3, "STORE_NAME", 1], [3, "LOAD_NAME", 2], [3, "LOAD_CONST", 3], [1, "INPLACE_ADD", null], [3, "STORE_NAME", 2], [3, "JUMP_ABSOLUTE", 36], [1, "POP_BLOCK", null], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 3], [1, "INPLACE_ADD", null], [3, "STORE_NAME", 0], [3, "JUMP_ABSOLUTE", 9], [1, "POP_BLOCK", null], [3, "LOAD_CONST", 4], [1, "RETURN_VALUE", null]], "co_names": ["j", "total", "i"], "co_consts": [{"Int": 0}, {"Int": 1000}, {"Int": 100}, {"Int": 1}, {"NoneType": null}], "co_varnames": []} diff --git a/VM/tests/perf_fib.py b/VM/tests/perf_fib.py new file mode 100644 index 000000000..8c508fd06 --- /dev/null +++ b/VM/tests/perf_fib.py @@ -0,0 +1,17 @@ +def fib(n): + # a, b = 1, 1 + a = 1 + b = 1 + for _ in range(n-1): + temp = b + b = a+b + a = temp + + #a, b = b, a+b + return b + +print(fib(1)) +print(fib(2)) +print(fib(3)) +print(fib(4)) +print(fib(5)) diff --git a/VM/tests/perf_fib.py.bytecode b/VM/tests/perf_fib.py.bytecode new file mode 100644 index 000000000..5d1303902 --- /dev/null +++ b/VM/tests/perf_fib.py.bytecode @@ -0,0 +1 @@ +{"co_names": ["fib", "print"], "co_consts": [{"Code": {"co_names": ["range"], "co_consts": [{"NoneType": null}, {"Int": 1}], "co_code": [[3, "LOAD_CONST", 1], [3, "STORE_FAST", 1], [3, "LOAD_CONST", 1], [3, "STORE_FAST", 2], [3, "SETUP_LOOP", 46], [3, "LOAD_GLOBAL", 0], [3, "LOAD_FAST", 0], [3, "LOAD_CONST", 1], [1, "BINARY_SUBTRACT", null], [3, "CALL_FUNCTION", 1], [1, "GET_ITER", null], [3, "FOR_ITER", 28], [3, "STORE_FAST", 3], [3, "LOAD_FAST", 2], [3, "STORE_FAST", 4], [3, "LOAD_FAST", 1], [3, "LOAD_FAST", 2], [1, "BINARY_ADD", null], [3, "STORE_FAST", 2], [3, "LOAD_FAST", 4], [3, "STORE_FAST", 1], [3, "JUMP_ABSOLUTE", 29], [1, "POP_BLOCK", null], [3, "LOAD_FAST", 2], [1, "RETURN_VALUE", null]], "co_name": "fib", "co_varnames": ["n", "a", "b", "_", "temp"]}}, {"Str": "fib"}, {"Int": 1}, {"Int": 2}, {"Int": 3}, {"Int": 4}, {"Int": 5}, {"NoneType": null}], "co_code": [[3, "LOAD_CONST", 0], [3, "LOAD_CONST", 1], [3, "MAKE_FUNCTION", 0], [3, "STORE_NAME", 0], [3, "LOAD_NAME", 1], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 2], [3, "CALL_FUNCTION", 1], [3, "CALL_FUNCTION", 1], [1, "POP_TOP", null], [3, "LOAD_NAME", 1], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 3], [3, "CALL_FUNCTION", 1], [3, "CALL_FUNCTION", 1], [1, "POP_TOP", null], [3, "LOAD_NAME", 1], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 4], [3, "CALL_FUNCTION", 1], [3, "CALL_FUNCTION", 1], [1, "POP_TOP", null], [3, "LOAD_NAME", 1], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 5], [3, "CALL_FUNCTION", 1], [3, "CALL_FUNCTION", 1], [1, "POP_TOP", null], [3, "LOAD_NAME", 1], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 6], [3, "CALL_FUNCTION", 1], [3, "CALL_FUNCTION", 1], [1, "POP_TOP", null], [3, "LOAD_CONST", 7], [1, "RETURN_VALUE", null]], "co_name": "", "co_varnames": []} diff --git a/VM/tests/print_const.py b/VM/tests/print_const.py new file mode 100644 index 000000000..816fea865 --- /dev/null +++ b/VM/tests/print_const.py @@ -0,0 +1 @@ +print(2 + 3) diff --git a/VM/tests/print_const.py.bytecode b/VM/tests/print_const.py.bytecode new file mode 100644 index 000000000..7ca5a4166 --- /dev/null +++ b/VM/tests/print_const.py.bytecode @@ -0,0 +1 @@ +{"co_name": "", "co_names": ["print"], "co_varnames": [], "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_consts": [{"Int": 5}, {"NoneType": null}]} diff --git a/VM/tests/tuple.py b/VM/tests/tuple.py new file mode 100644 index 000000000..16b961129 --- /dev/null +++ b/VM/tests/tuple.py @@ -0,0 +1,2 @@ +x = (1,2) +assert x[0] == 1 diff --git a/VM/tests/tuple.py.bytecode b/VM/tests/tuple.py.bytecode new file mode 100644 index 000000000..a53a7ce00 --- /dev/null +++ b/VM/tests/tuple.py.bytecode @@ -0,0 +1 @@ +{"co_code": [[3, "LOAD_CONST", 0], [3, "STORE_NAME", 0], [3, "LOAD_NAME", 0], [3, "LOAD_CONST", 1], [1, "BINARY_SUBSCR", 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_CONST", 3], [1, "RETURN_VALUE", null]], "co_varnames": [], "co_name": "", "co_names": ["x", "AssertionError"], "co_consts": [{"Tuple": [{"Int": 1}, {"Int": 2}]}, {"Int": 0}, {"Int": 1}, {"NoneType": null}]} diff --git a/VM/tests/types.py b/VM/tests/types.py new file mode 100644 index 000000000..48c7fa70d --- /dev/null +++ b/VM/tests/types.py @@ -0,0 +1 @@ +['a', 1] diff --git a/VM/tests/types.py.bytecode b/VM/tests/types.py.bytecode new file mode 100644 index 000000000..d223a7b57 --- /dev/null +++ b/VM/tests/types.py.bytecode @@ -0,0 +1 @@ +{"co_names": [], "co_varnames": [], "co_code": [[3, "LOAD_CONST", 0], [3, "LOAD_CONST", 1], [3, "BUILD_LIST", 2], [1, "POP_TOP", null], [3, "LOAD_CONST", 2], [1, "RETURN_VALUE", null]], "co_consts": [{"Str": "a"}, {"Int": 1}, {"NoneType": null}], "co_name": ""} diff --git a/VM/tests/variables.py b/VM/tests/variables.py new file mode 100644 index 000000000..2dc1403c7 --- /dev/null +++ b/VM/tests/variables.py @@ -0,0 +1,4 @@ +a = 8 +b = 7 +assert a+b == 15 +# print(a+b) diff --git a/VM/tests/variables.py.bytecode b/VM/tests/variables.py.bytecode new file mode 100644 index 000000000..f8e383b68 --- /dev/null +++ b/VM/tests/variables.py.bytecode @@ -0,0 +1 @@ +{"co_names": ["a", "b", "AssertionError"], "co_consts": [{"Int": 8}, {"Int": 7}, {"Int": 15}, {"NoneType": null}], "co_varnames": [], "co_name": "", "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", 3], [1, "RETURN_VALUE", null]]} diff --git a/VM/tests/xfail_3.1.2.17.py b/VM/tests/xfail_3.1.2.17.py new file mode 100644 index 000000000..fc956bef4 --- /dev/null +++ b/VM/tests/xfail_3.1.2.17.py @@ -0,0 +1,7 @@ +word = "Python" + +word[0] = "J" # Should raise a error, immutable +word[2:] = "Jy" # Should raise a error, immutable + + + diff --git a/VM/tests/xfail_3.1.2.17.py.bytecode b/VM/tests/xfail_3.1.2.17.py.bytecode new file mode 100644 index 000000000..b2a6a1574 --- /dev/null +++ b/VM/tests/xfail_3.1.2.17.py.bytecode @@ -0,0 +1 @@ +{"co_names": ["word"], "co_varnames": [], "co_consts": [{"Str": "Python"}, {"Str": "J"}, {"Int": 0}, {"Str": "Jy"}, {"Int": 2}, {"NoneType": null}], "co_name": "", "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]]} diff --git a/VM/tests/xfail_assert.py b/VM/tests/xfail_assert.py new file mode 100644 index 000000000..aa2a03c71 --- /dev/null +++ b/VM/tests/xfail_assert.py @@ -0,0 +1,2 @@ +assert 1 == 1 +assert 1 == 2 diff --git a/VM/tests/xfail_assert.py.bytecode b/VM/tests/xfail_assert.py.bytecode new file mode 100644 index 000000000..0c91ae68a --- /dev/null +++ b/VM/tests/xfail_assert.py.bytecode @@ -0,0 +1 @@ +{"co_varnames": [], "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", 0], [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_name": "", "co_consts": [{"Int": 1}, {"Int": 2}, {"NoneType": null}]}