mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-09 22:49:57 +09:00
Merge branch 'master' into sets-using-hashes
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -5,4 +5,6 @@ __pycache__
|
||||
**/*.pytest_cache
|
||||
.*sw*
|
||||
.repl_history.txt
|
||||
.vscode
|
||||
wasm-pack.log
|
||||
.idea/
|
||||
|
||||
11
Cargo.lock
generated
11
Cargo.lock
generated
@@ -1,3 +1,5 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.6.4"
|
||||
@@ -684,6 +686,7 @@ dependencies = [
|
||||
"rustpython_parser 0.0.1",
|
||||
"rustpython_vm 0.1.0",
|
||||
"rustyline 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -708,6 +711,7 @@ dependencies = [
|
||||
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-bigint 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-complex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -716,6 +720,7 @@ dependencies = [
|
||||
"serde_derive 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"statrs 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1128,6 +1133,11 @@ dependencies = [
|
||||
"winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xdg"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[metadata]
|
||||
"checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4"
|
||||
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||
@@ -1263,3 +1273,4 @@ dependencies = [
|
||||
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
"checksum wincolor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eeb06499a3a4d44302791052df005d5232b927ed1a9658146d842165c4de7767"
|
||||
"checksum wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "561ed901ae465d6185fa7864d63fbd5720d0ef718366c9a4dc83cf6170d7e9ba"
|
||||
"checksum xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57"
|
||||
|
||||
@@ -13,6 +13,7 @@ clap = "2.31.2"
|
||||
rustpython_parser = {path = "parser"}
|
||||
rustpython_vm = {path = "vm"}
|
||||
rustyline = "2.1.0"
|
||||
xdg = "2.2.0"
|
||||
|
||||
[profile.release]
|
||||
opt-level = "s"
|
||||
|
||||
@@ -56,7 +56,7 @@ If you wish to update the online documentation. Push directly to the `release` b
|
||||
- `obj`: python builtin types
|
||||
- `src`: using the other subcrates to bring rustpython to life.
|
||||
- `docs`: documentation (work in progress)
|
||||
- `py_code_object`: CPython bytecode to rustpython bytecode convertor (work in progress)
|
||||
- `py_code_object`: CPython bytecode to rustpython bytecode converter (work in progress)
|
||||
- `wasm`: Binary crate and resources for WebAssembly build
|
||||
- `tests`: integration test snippets
|
||||
|
||||
@@ -81,8 +81,8 @@ To test rustpython, there is a collection of python snippets located in the
|
||||
|
||||
```shell
|
||||
$ cd tests
|
||||
$ pipenv shell
|
||||
$ pytest -v
|
||||
$ pipenv install
|
||||
$ pipenv run pytest -v
|
||||
```
|
||||
|
||||
There also are some unittests, you can run those will cargo:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Byterun
|
||||
|
||||
* Builtins are exposted to frame.f_builtins
|
||||
* Builtins are exposed to frame.f_builtins
|
||||
* f_builtins is assigned during frame creation,
|
||||
self.f_builtins = f_locals['__builtins__']
|
||||
if hasattr(self.f_builtins, '__dict__'):
|
||||
@@ -21,10 +21,10 @@ 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
|
||||
* During frame creation, force push a native function `print` into the namespace
|
||||
* Modify LOAD_* so they can search for names in builtins
|
||||
|
||||
* Create a module type
|
||||
* In VM initialization, load the builtins module into locals
|
||||
* During frame creation, create a field that conatins the builtins dict
|
||||
* During frame creation, create a field that contains the builtins dict
|
||||
|
||||
|
||||
@@ -65,9 +65,9 @@ pub enum Statement {
|
||||
value: Expression,
|
||||
},
|
||||
AugAssign {
|
||||
target: Expression,
|
||||
target: Box<Expression>,
|
||||
op: Operator,
|
||||
value: Expression,
|
||||
value: Box<Expression>,
|
||||
},
|
||||
Expression {
|
||||
expression: Expression,
|
||||
@@ -221,7 +221,7 @@ pub enum Expression {
|
||||
|
||||
/*
|
||||
* In cpython this is called arguments, but we choose parameters to
|
||||
* distuingish between function parameters and actual call arguments.
|
||||
* distinguish between function parameters and actual call arguments.
|
||||
*/
|
||||
#[derive(Debug, PartialEq, Default)]
|
||||
pub struct Parameters {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//! This module takes care of lexing python source text. This means source
|
||||
//! code is translated into seperate tokens.
|
||||
//! code is translated into separate tokens.
|
||||
|
||||
pub use super::token::Tok;
|
||||
use num_bigint::BigInt;
|
||||
@@ -54,6 +54,7 @@ pub struct Lexer<T: Iterator<Item = char>> {
|
||||
#[derive(Debug)]
|
||||
pub enum LexicalError {
|
||||
StringError,
|
||||
NestingError,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
@@ -64,10 +65,7 @@ pub struct Location {
|
||||
|
||||
impl Location {
|
||||
pub fn new(row: usize, column: usize) -> Self {
|
||||
Location {
|
||||
row: row,
|
||||
column: column,
|
||||
}
|
||||
Location { row, column }
|
||||
}
|
||||
|
||||
pub fn get_row(&self) -> usize {
|
||||
@@ -126,8 +124,7 @@ pub type Spanned<Tok> = Result<(Location, Tok, Location), LexicalError>;
|
||||
pub fn make_tokenizer<'a>(source: &'a str) -> impl Iterator<Item = Spanned<Tok>> + 'a {
|
||||
let nlh = NewlineHandler::new(source.chars());
|
||||
let lch = LineContinationHandler::new(nlh);
|
||||
let lexer = Lexer::new(lch);
|
||||
lexer
|
||||
Lexer::new(lch)
|
||||
}
|
||||
|
||||
// The newline handler is an iterator which collapses different newline
|
||||
@@ -144,7 +141,7 @@ where
|
||||
{
|
||||
pub fn new(source: T) -> Self {
|
||||
let mut nlh = NewlineHandler {
|
||||
source: source,
|
||||
source,
|
||||
chr0: None,
|
||||
chr1: None,
|
||||
};
|
||||
@@ -200,7 +197,7 @@ where
|
||||
{
|
||||
pub fn new(source: T) -> Self {
|
||||
let mut nlh = LineContinationHandler {
|
||||
source: source,
|
||||
source,
|
||||
chr0: None,
|
||||
chr1: None,
|
||||
};
|
||||
@@ -313,7 +310,7 @@ where
|
||||
if keywords.contains_key(&name) {
|
||||
Ok((start_pos, keywords.remove(&name).unwrap(), end_pos))
|
||||
} else {
|
||||
Ok((start_pos, Tok::Name { name: name }, end_pos))
|
||||
Ok((start_pos, Tok::Name { name }, end_pos))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -358,7 +355,7 @@ where
|
||||
|
||||
let end_pos = self.get_pos();
|
||||
let value = BigInt::from_str_radix(&value_text, radix).unwrap();
|
||||
Ok((start_pos, Tok::Int { value: value }, end_pos))
|
||||
Ok((start_pos, Tok::Int { value }, end_pos))
|
||||
}
|
||||
|
||||
fn lex_normal_number(&mut self) -> Spanned<Tok> {
|
||||
@@ -410,7 +407,7 @@ where
|
||||
))
|
||||
} else {
|
||||
let end_pos = self.get_pos();
|
||||
Ok((start_pos, Tok::Float { value: value }, end_pos))
|
||||
Ok((start_pos, Tok::Float { value }, end_pos))
|
||||
}
|
||||
} else {
|
||||
// Parse trailing 'j':
|
||||
@@ -418,18 +415,11 @@ where
|
||||
self.next_char();
|
||||
let end_pos = self.get_pos();
|
||||
let imag = f64::from_str(&value_text).unwrap();
|
||||
Ok((
|
||||
start_pos,
|
||||
Tok::Complex {
|
||||
real: 0.0,
|
||||
imag: imag,
|
||||
},
|
||||
end_pos,
|
||||
))
|
||||
Ok((start_pos, Tok::Complex { real: 0.0, imag }, end_pos))
|
||||
} else {
|
||||
let end_pos = self.get_pos();
|
||||
let value = value_text.parse::<BigInt>().unwrap();
|
||||
Ok((start_pos, Tok::Int { value: value }, end_pos))
|
||||
Ok((start_pos, Tok::Int { value }, end_pos))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -439,9 +429,7 @@ where
|
||||
self.next_char();
|
||||
loop {
|
||||
match self.chr0 {
|
||||
Some('\n') => {
|
||||
return;
|
||||
}
|
||||
Some('\n') => return,
|
||||
Some(_) => {}
|
||||
None => return,
|
||||
}
|
||||
@@ -548,33 +536,33 @@ where
|
||||
}
|
||||
};
|
||||
|
||||
return Ok((start_pos, tok, end_pos));
|
||||
Ok((start_pos, tok, end_pos))
|
||||
}
|
||||
|
||||
fn is_char(&self) -> bool {
|
||||
match self.chr0 {
|
||||
Some('a'...'z') | Some('A'...'Z') | Some('_') | Some('0'...'9') => return true,
|
||||
_ => return false,
|
||||
Some('a'...'z') | Some('A'...'Z') | Some('_') | Some('0'...'9') => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_number(&self, radix: u32) -> bool {
|
||||
match radix {
|
||||
2 => match self.chr0 {
|
||||
Some('0'...'1') => return true,
|
||||
_ => return false,
|
||||
Some('0'...'1') => true,
|
||||
_ => false,
|
||||
},
|
||||
8 => match self.chr0 {
|
||||
Some('0'...'7') => return true,
|
||||
_ => return false,
|
||||
Some('0'...'7') => true,
|
||||
_ => false,
|
||||
},
|
||||
10 => match self.chr0 {
|
||||
Some('0'...'9') => return true,
|
||||
_ => return false,
|
||||
Some('0'...'9') => true,
|
||||
_ => false,
|
||||
},
|
||||
16 => match self.chr0 {
|
||||
Some('0'...'9') | Some('a'...'f') | Some('A'...'F') => return true,
|
||||
_ => return false,
|
||||
Some('0'...'9') | Some('a'...'f') | Some('A'...'F') => true,
|
||||
_ => false,
|
||||
},
|
||||
x => unimplemented!("Radix not implemented: {}", x),
|
||||
}
|
||||
@@ -915,6 +903,9 @@ where
|
||||
}
|
||||
Some(')') => {
|
||||
let result = self.eat_single_char(Tok::Rpar);
|
||||
if self.nesting == 0 {
|
||||
return Some(Err(LexicalError::NestingError));
|
||||
}
|
||||
self.nesting -= 1;
|
||||
return Some(result);
|
||||
}
|
||||
@@ -925,6 +916,9 @@ where
|
||||
}
|
||||
Some(']') => {
|
||||
let result = self.eat_single_char(Tok::Rsqb);
|
||||
if self.nesting == 0 {
|
||||
return Some(Err(LexicalError::NestingError));
|
||||
}
|
||||
self.nesting -= 1;
|
||||
return Some(result);
|
||||
}
|
||||
@@ -935,6 +929,9 @@ where
|
||||
}
|
||||
Some('}') => {
|
||||
let result = self.eat_single_char(Tok::Rbrace);
|
||||
if self.nesting == 0 {
|
||||
return Some(Err(LexicalError::NestingError));
|
||||
}
|
||||
self.nesting -= 1;
|
||||
return Some(result);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ extern crate num_traits;
|
||||
pub mod ast;
|
||||
pub mod lexer;
|
||||
pub mod parser;
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
mod python;
|
||||
pub mod token;
|
||||
|
||||
|
||||
@@ -105,7 +105,11 @@ ExpressionStatement: ast::LocatedStatement = {
|
||||
let rhs = e2.into_iter().next().unwrap();
|
||||
ast::LocatedStatement {
|
||||
location: loc,
|
||||
node: ast::Statement::AugAssign { target: expr, op: op, value: rhs },
|
||||
node: ast::Statement::AugAssign {
|
||||
target: Box::new(expr),
|
||||
op,
|
||||
value: Box::new(rhs)
|
||||
},
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@@ -56,7 +56,7 @@ impl VirtualMachine {
|
||||
}
|
||||
}
|
||||
|
||||
// Can we get rid of the code paramter?
|
||||
// Can we get rid of the code parameter?
|
||||
|
||||
fn make_frame(&self, code: PyCodeObject, callargs: HashMap<String, Rc<NativeType>>, globals: Option<HashMap<String, Rc<NativeType>>>) -> Frame {
|
||||
//populate the globals and locals
|
||||
@@ -345,7 +345,7 @@ impl VirtualMachine {
|
||||
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!("Invalid parameter for RAISE_VARARGS, must be between 0 to 3")
|
||||
};
|
||||
panic!("{:?}", exception);
|
||||
}
|
||||
|
||||
36
src/main.rs
36
src/main.rs
@@ -68,8 +68,8 @@ fn main() {
|
||||
handle_exception(&mut vm, result);
|
||||
}
|
||||
|
||||
fn _run_string(vm: &mut VirtualMachine, source: &str, source_path: Option<String>) -> PyResult {
|
||||
let code_obj = compile::compile(vm, source, compile::Mode::Exec, source_path)?;
|
||||
fn _run_string(vm: &mut VirtualMachine, source: &str, source_path: String) -> PyResult {
|
||||
let code_obj = compile::compile(vm, source, &compile::Mode::Exec, source_path)?;
|
||||
// trace!("Code object: {:?}", code_obj.borrow());
|
||||
let builtins = vm.get_builtin_scope();
|
||||
let vars = vm.context().new_scope(Some(builtins)); // Keep track of local variables
|
||||
@@ -91,7 +91,7 @@ fn run_command(vm: &mut VirtualMachine, mut source: String) -> PyResult {
|
||||
|
||||
// This works around https://github.com/RustPython/RustPython/issues/17
|
||||
source.push_str("\n");
|
||||
_run_string(vm, &source, None)
|
||||
_run_string(vm, &source, "<stdin>".to_string())
|
||||
}
|
||||
|
||||
fn run_module(vm: &mut VirtualMachine, module: &str) -> PyResult {
|
||||
@@ -105,7 +105,7 @@ fn run_script(vm: &mut VirtualMachine, script_file: &str) -> PyResult {
|
||||
// Parse an ast from it:
|
||||
let filepath = Path::new(script_file);
|
||||
match parser::read_file(filepath) {
|
||||
Ok(source) => _run_string(vm, &source, Some(filepath.to_str().unwrap().to_string())),
|
||||
Ok(source) => _run_string(vm, &source, filepath.to_str().unwrap().to_string()),
|
||||
Err(msg) => {
|
||||
error!("Parsing went horribly wrong: {}", msg);
|
||||
std::process::exit(1);
|
||||
@@ -114,7 +114,7 @@ fn run_script(vm: &mut VirtualMachine, script_file: &str) -> PyResult {
|
||||
}
|
||||
|
||||
fn shell_exec(vm: &mut VirtualMachine, source: &str, scope: PyObjectRef) -> bool {
|
||||
match compile::compile(vm, source, compile::Mode::Single, None) {
|
||||
match compile::compile(vm, source, &compile::Mode::Single, "<stdin>".to_string()) {
|
||||
Ok(code) => {
|
||||
match vm.run_code_obj(code, scope) {
|
||||
Ok(_value) => {
|
||||
@@ -142,6 +142,22 @@ fn shell_exec(vm: &mut VirtualMachine, source: &str, scope: PyObjectRef) -> bool
|
||||
true
|
||||
}
|
||||
|
||||
#[cfg(not(target_family = "unix"))]
|
||||
fn get_history_path() -> PathBuf {
|
||||
//Path buffer
|
||||
PathBuf::from(".repl_history.txt")
|
||||
}
|
||||
|
||||
#[cfg(target_family = "unix")]
|
||||
fn get_history_path() -> PathBuf {
|
||||
//work around for windows dependent builds. The xdg crate is unix specific
|
||||
//so access to the BaseDirectories struct breaks builds on python.
|
||||
extern crate xdg;
|
||||
|
||||
let xdg_dirs = xdg::BaseDirectories::with_prefix("rustpython").unwrap();
|
||||
xdg_dirs.place_cache_file("repl_history.txt").unwrap()
|
||||
}
|
||||
|
||||
fn run_shell(vm: &mut VirtualMachine) -> PyResult {
|
||||
println!(
|
||||
"Welcome to the magnificent Rust Python {} interpreter",
|
||||
@@ -154,14 +170,14 @@ fn run_shell(vm: &mut VirtualMachine) -> PyResult {
|
||||
let mut input = String::new();
|
||||
let mut rl = Editor::<()>::new();
|
||||
|
||||
// TODO: Store the history in a proper XDG directory
|
||||
let repl_history_path = ".repl_history.txt";
|
||||
if rl.load_history(repl_history_path).is_err() {
|
||||
//retrieve a history_path_str dependent to the os
|
||||
let repl_history_path_str = &get_history_path();
|
||||
if rl.load_history(repl_history_path_str).is_err() {
|
||||
println!("No previous history.");
|
||||
}
|
||||
|
||||
loop {
|
||||
// TODO: modules dont support getattr / setattr yet
|
||||
// TODO: modules don't support getattr / setattr yet
|
||||
//let prompt = match vm.get_attribute(vm.sys_module.clone(), "ps1") {
|
||||
// Ok(value) => objstr::get_value(&value),
|
||||
// Err(_) => ">>>>> ".to_string(),
|
||||
@@ -220,7 +236,7 @@ fn run_shell(vm: &mut VirtualMachine) -> PyResult {
|
||||
}
|
||||
};
|
||||
}
|
||||
rl.save_history(repl_history_path).unwrap();
|
||||
rl.save_history(repl_history_path_str).unwrap();
|
||||
|
||||
Ok(vm.get_none())
|
||||
}
|
||||
|
||||
@@ -44,7 +44,12 @@ assert int() == 0
|
||||
a = complex(2, 4)
|
||||
assert type(a) is complex
|
||||
assert type(a + a) is complex
|
||||
assert repr(a) == '(2+4j)'
|
||||
a = 10j
|
||||
assert repr(a) == '10j'
|
||||
|
||||
a = 1
|
||||
assert a.conjugate() == a
|
||||
|
||||
a = 12345
|
||||
|
||||
|
||||
@@ -46,3 +46,5 @@ assert True + True == 2
|
||||
assert False * 7 == 0
|
||||
assert True > 0
|
||||
assert int(True) == 1
|
||||
assert True.conjugate() == 1
|
||||
assert isinstance(True.conjugate(), int)
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
assert divmod(11, 3) == (3, 2)
|
||||
assert divmod(8,11) == (0, 8)
|
||||
assert divmod(0.873, 0.252) == (3.0, 0.11699999999999999)
|
||||
|
||||
try:
|
||||
divmod(5, 0)
|
||||
except ZeroDivisionError:
|
||||
pass
|
||||
else:
|
||||
assert False, "Expected divmod by zero to throw ZeroDivisionError"
|
||||
|
||||
try:
|
||||
divmod(5.0, 0.0)
|
||||
except ZeroDivisionError:
|
||||
pass
|
||||
else:
|
||||
assert False, "Expected divmod by zero to throw ZeroDivisionError"
|
||||
|
||||
23
tests/snippets/builtin_enumerate.py
Normal file
23
tests/snippets/builtin_enumerate.py
Normal file
@@ -0,0 +1,23 @@
|
||||
assert list(enumerate(['a', 'b', 'c'])) == [(0, 'a'), (1, 'b'), (2, 'c')]
|
||||
|
||||
assert type(enumerate([])) == enumerate
|
||||
|
||||
assert list(enumerate(['a', 'b', 'c'], -100)) == [(-100, 'a'), (-99, 'b'), (-98, 'c')]
|
||||
assert list(enumerate(['a', 'b', 'c'], 2**200)) == [(2**200, 'a'), (2**200 + 1, 'b'), (2**200 + 2, 'c')]
|
||||
|
||||
|
||||
# test infinite iterator
|
||||
class Counter(object):
|
||||
counter = 0
|
||||
|
||||
def __next__(self):
|
||||
self.counter += 1
|
||||
return self.counter
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
|
||||
it = enumerate(Counter())
|
||||
assert next(it) == (0, 1)
|
||||
assert next(it) == (1, 2)
|
||||
32
tests/snippets/builtin_filter.py
Normal file
32
tests/snippets/builtin_filter.py
Normal file
@@ -0,0 +1,32 @@
|
||||
assert list(filter(lambda x: ((x % 2) == 0), [0, 1, 2])) == [0, 2]
|
||||
|
||||
# None implies identity
|
||||
assert list(filter(None, [0, 1, 2])) == [1, 2]
|
||||
|
||||
assert type(filter(None, [])) == filter
|
||||
|
||||
|
||||
# test infinite iterator
|
||||
class Counter(object):
|
||||
counter = 0
|
||||
|
||||
def __next__(self):
|
||||
self.counter += 1
|
||||
return self.counter
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
|
||||
it = filter(lambda x: ((x % 2) == 0), Counter())
|
||||
assert next(it) == 2
|
||||
assert next(it) == 4
|
||||
|
||||
|
||||
def predicate(x):
|
||||
if x == 0:
|
||||
raise StopIteration()
|
||||
return True
|
||||
|
||||
|
||||
assert list(filter(predicate, [1, 2, 0, 4, 5])) == [1, 2]
|
||||
34
tests/snippets/builtin_map.py
Normal file
34
tests/snippets/builtin_map.py
Normal file
@@ -0,0 +1,34 @@
|
||||
a = list(map(str, [1, 2, 3]))
|
||||
assert a == ['1', '2', '3']
|
||||
|
||||
|
||||
b = list(map(lambda x, y: x + y, [1, 2, 4], [3, 5]))
|
||||
assert b == [4, 7]
|
||||
|
||||
assert type(map(lambda x: x, [])) == map
|
||||
|
||||
|
||||
# test infinite iterator
|
||||
class Counter(object):
|
||||
counter = 0
|
||||
|
||||
def __next__(self):
|
||||
self.counter += 1
|
||||
return self.counter
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
|
||||
it = map(lambda x: x+1, Counter())
|
||||
assert next(it) == 2
|
||||
assert next(it) == 3
|
||||
|
||||
|
||||
def mapping(x):
|
||||
if x == 0:
|
||||
raise StopIteration()
|
||||
return x
|
||||
|
||||
|
||||
assert list(map(mapping, [1, 2, 0, 4, 5])) == [1, 2]
|
||||
8
tests/snippets/builtin_open.py
Normal file
8
tests/snippets/builtin_open.py
Normal file
@@ -0,0 +1,8 @@
|
||||
fd = open('README.md')
|
||||
assert 'RustPython' in fd.read()
|
||||
|
||||
try:
|
||||
open('DoesNotExist')
|
||||
assert False
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
63
tests/snippets/builtin_range.py
Normal file
63
tests/snippets/builtin_range.py
Normal file
@@ -0,0 +1,63 @@
|
||||
def assert_raises(expr, exc_type):
|
||||
"""
|
||||
Helper function to assert `expr` raises an exception of type `exc_type`
|
||||
Args:
|
||||
expr: Callable
|
||||
exec_type: Exception
|
||||
Returns:
|
||||
None
|
||||
Raises:
|
||||
Assertion error on failure
|
||||
"""
|
||||
try:
|
||||
expr(None)
|
||||
except exc_type:
|
||||
assert True
|
||||
else:
|
||||
assert False
|
||||
|
||||
assert range(2**63+1)[2**63] == 9223372036854775808
|
||||
|
||||
# len tests
|
||||
assert len(range(10, 5)) == 0, 'Range with no elements should have length = 0'
|
||||
assert len(range(10, 5, -2)) == 3, 'Expected length 3, for elements: 10, 8, 6'
|
||||
assert len(range(5, 10, 2)) == 3, 'Expected length 3, for elements: 5, 7, 9'
|
||||
|
||||
# index tests
|
||||
assert range(10).index(6) == 6
|
||||
assert range(4, 10).index(6) == 2
|
||||
assert range(4, 10, 2).index(6) == 1
|
||||
assert range(10, 4, -2).index(8) == 1
|
||||
|
||||
# index raises value error on out of bounds
|
||||
assert_raises(lambda _: range(10).index(-1), ValueError)
|
||||
assert_raises(lambda _: range(10).index(10), ValueError)
|
||||
|
||||
# index raises value error if out of step
|
||||
assert_raises(lambda _: range(4, 10, 2).index(5), ValueError)
|
||||
|
||||
# index raises value error if needle is not an int
|
||||
assert_raises(lambda _: range(10).index('foo'), ValueError)
|
||||
|
||||
# __bool__
|
||||
assert bool(range(1))
|
||||
assert bool(range(1, 2))
|
||||
|
||||
assert not bool(range(0))
|
||||
assert not bool(range(1, 1))
|
||||
|
||||
# __contains__
|
||||
assert 6 in range(10)
|
||||
assert 6 in range(4, 10)
|
||||
assert 6 in range(4, 10, 2)
|
||||
assert 10 in range(10, 4, -2)
|
||||
assert 8 in range(10, 4, -2)
|
||||
|
||||
assert -1 not in range(10)
|
||||
assert 9 not in range(10, 4, -2)
|
||||
assert 4 not in range(10, 4, -2)
|
||||
assert 'foo' not in range(10)
|
||||
|
||||
# __reversed__
|
||||
assert list(reversed(range(5))) == [4, 3, 2, 1, 0]
|
||||
assert list(reversed(range(5, 0, -1))) == [1, 2, 3, 4, 5]
|
||||
1
tests/snippets/builtin_reversed.py
Normal file
1
tests/snippets/builtin_reversed.py
Normal file
@@ -0,0 +1 @@
|
||||
assert list(reversed(range(5))) == [4, 3, 2, 1, 0]
|
||||
77
tests/snippets/builtin_slice.py
Normal file
77
tests/snippets/builtin_slice.py
Normal file
@@ -0,0 +1,77 @@
|
||||
|
||||
a = []
|
||||
assert a[:] == []
|
||||
assert a[:2**100] == []
|
||||
assert a[-2**100:] == []
|
||||
assert a[::2**100] == []
|
||||
assert a[10:20] == []
|
||||
assert a[-20:-10] == []
|
||||
|
||||
b = [1, 2]
|
||||
|
||||
assert b[:] == [1, 2]
|
||||
assert b[:2**100] == [1, 2]
|
||||
assert b[-2**100:] == [1, 2]
|
||||
assert b[2**100:] == []
|
||||
assert b[::2**100] == [1]
|
||||
assert b[-10:1] == [1]
|
||||
assert b[0:0] == []
|
||||
assert b[1:0] == []
|
||||
|
||||
try:
|
||||
_ = b[::0]
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
assert False, "Zero step slice should raise ValueError"
|
||||
|
||||
assert b[::-1] == [2, 1]
|
||||
assert b[1::-1] == [2, 1]
|
||||
assert b[0::-1] == [1]
|
||||
assert b[0:-5:-1] == [1]
|
||||
assert b[:0:-1] == [2]
|
||||
assert b[5:0:-1] == [2]
|
||||
|
||||
c = list(range(10))
|
||||
|
||||
assert c[9:6:-3] == [9]
|
||||
assert c[9::-3] == [9, 6, 3, 0]
|
||||
assert c[9::-4] == [9, 5, 1]
|
||||
assert c[8::-2**100] == [8]
|
||||
|
||||
assert c[7:7:-2] == []
|
||||
assert c[7:8:-2] == []
|
||||
|
||||
d = "123456"
|
||||
|
||||
assert d[3::-1] == "4321"
|
||||
assert d[4::-3] == "52"
|
||||
|
||||
|
||||
slice_a = slice(5)
|
||||
assert slice_a.start is None
|
||||
assert slice_a.stop == 5
|
||||
assert slice_a.step is None
|
||||
|
||||
slice_b = slice(1, 5)
|
||||
assert slice_b.start == 1
|
||||
assert slice_b.stop == 5
|
||||
assert slice_b.step is None
|
||||
|
||||
slice_c = slice(1, 5, 2)
|
||||
assert slice_c.start == 1
|
||||
assert slice_c.stop == 5
|
||||
assert slice_c.step == 2
|
||||
|
||||
|
||||
class SubScript(object):
|
||||
def __getitem__(self, item):
|
||||
assert type(item) == slice
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
assert type(key) == slice
|
||||
|
||||
|
||||
ss = SubScript()
|
||||
_ = ss[:]
|
||||
ss[:1] = 1
|
||||
24
tests/snippets/builtin_zip.py
Normal file
24
tests/snippets/builtin_zip.py
Normal file
@@ -0,0 +1,24 @@
|
||||
assert list(zip(['a', 'b', 'c'], range(3), [9, 8, 7, 99])) == [('a', 0, 9), ('b', 1, 8), ('c', 2, 7)]
|
||||
|
||||
assert list(zip(['a', 'b', 'c'])) == [('a',), ('b',), ('c',)]
|
||||
assert list(zip()) == []
|
||||
|
||||
assert list(zip(*zip(['a', 'b', 'c'], range(1, 4)))) == [('a', 'b', 'c'), (1, 2, 3)]
|
||||
|
||||
|
||||
# test infinite iterator
|
||||
class Counter(object):
|
||||
def __init__(self, counter=0):
|
||||
self.counter = counter
|
||||
|
||||
def __next__(self):
|
||||
self.counter += 1
|
||||
return self.counter
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
|
||||
it = zip(Counter(), Counter(3))
|
||||
assert next(it) == (1, 4)
|
||||
assert next(it) == (2, 5)
|
||||
@@ -1,22 +1,12 @@
|
||||
|
||||
a = list(map(str, [1, 2, 3]))
|
||||
assert a == ['1', '2', '3']
|
||||
|
||||
x = sum(map(int, a))
|
||||
x = sum(map(int, ['1', '2', '3']))
|
||||
assert x == 6
|
||||
|
||||
assert callable(type)
|
||||
# TODO:
|
||||
# assert callable(callable)
|
||||
|
||||
assert list(enumerate(['a', 'b', 'c'])) == [(0, 'a'), (1, 'b'), (2, 'c')]
|
||||
|
||||
assert type(frozenset) is type
|
||||
|
||||
assert list(zip(['a', 'b', 'c'], range(3), [9, 8, 7, 99])) == [('a', 0, 9), ('b', 1, 8), ('c', 2, 7)]
|
||||
|
||||
assert list(filter(lambda x: ((x % 2) == 0), [0, 1, 2])) == [0, 2]
|
||||
|
||||
assert 3 == eval('1+2')
|
||||
|
||||
code = compile('5+3', 'x.py', 'eval')
|
||||
|
||||
31
tests/snippets/bytearray.py
Normal file
31
tests/snippets/bytearray.py
Normal file
@@ -0,0 +1,31 @@
|
||||
#__getitem__ not implemented yet
|
||||
#a = bytearray(b'abc')
|
||||
#assert a[0] == b'a'
|
||||
#assert a[1] == b'b'
|
||||
|
||||
assert len(bytearray([1,2,3])) == 3
|
||||
|
||||
assert bytearray(b'1a23').isalnum()
|
||||
assert not bytearray(b'1%a23').isalnum()
|
||||
|
||||
assert bytearray(b'abc').isalpha()
|
||||
assert not bytearray(b'abc1').isalpha()
|
||||
|
||||
# travis doesn't like this
|
||||
#assert bytearray(b'xyz').isascii()
|
||||
#assert not bytearray([128, 157, 32]).isascii()
|
||||
|
||||
assert bytearray(b'1234567890').isdigit()
|
||||
assert not bytearray(b'12ab').isdigit()
|
||||
|
||||
assert bytearray(b'lower').islower()
|
||||
assert not bytearray(b'Super Friends').islower()
|
||||
|
||||
assert bytearray(b' \n\t').isspace()
|
||||
assert not bytearray(b'\td\n').isspace()
|
||||
|
||||
assert bytearray(b'UPPER').isupper()
|
||||
assert not bytearray(b'tuPpEr').isupper()
|
||||
|
||||
assert bytearray(b'Is Title Case').istitle()
|
||||
assert not bytearray(b'is Not title casE').istitle()
|
||||
28
tests/snippets/code.py
Normal file
28
tests/snippets/code.py
Normal file
@@ -0,0 +1,28 @@
|
||||
c1 = compile("1 + 1", "", 'eval')
|
||||
|
||||
code_class = type(c1)
|
||||
|
||||
def f(x, y, *args, power=1, **kwargs):
|
||||
assert code_class == type(c1)
|
||||
z = x * y
|
||||
return z ** power
|
||||
|
||||
c2 = f.__code__
|
||||
# print(c2)
|
||||
assert type(c2) == code_class
|
||||
print(dir(c2))
|
||||
assert c2.co_argcount == 2
|
||||
# assert c2.co_cellvars == ()
|
||||
# assert isinstance(c2.co_code, bytes)
|
||||
# assert c2.co_consts == (None,)
|
||||
assert "code.py" in c2.co_filename
|
||||
assert c2.co_firstlineno == 5, str(c2.co_firstlineno)
|
||||
# assert isinstance(c2.co_flags, int) # 'OPTIMIZED, NEWLOCALS, NOFREE'
|
||||
# assert c2.co_freevars == (), str(c2.co_freevars)
|
||||
assert c2.co_kwonlyargcount == 1, (c2.co_kwonlyargcount)
|
||||
# assert c2.co_lnotab == 0, c2.co_lnotab # b'\x00\x01' # Line number table
|
||||
assert c2.co_name == 'f', c2.co_name
|
||||
# assert c2.co_names == ('code_class', 'type', 'c1', 'AssertionError'), c2.co_names # , c2.co_names
|
||||
# assert c2.co_nlocals == 4, c2.co_nlocals #
|
||||
# assert c2.co_stacksize == 2, 'co_stacksize',
|
||||
# assert c2.co_varnames == ('x', 'y', 'power', 'z'), c2.co_varnames
|
||||
55
tests/snippets/division_by_zero.py
Normal file
55
tests/snippets/division_by_zero.py
Normal file
@@ -0,0 +1,55 @@
|
||||
try:
|
||||
5 / 0
|
||||
except ZeroDivisionError:
|
||||
pass
|
||||
else:
|
||||
assert False, 'Expected ZeroDivisionError'
|
||||
|
||||
try:
|
||||
5 / -0.0
|
||||
except ZeroDivisionError:
|
||||
pass
|
||||
else:
|
||||
assert False, 'Expected ZeroDivisionError'
|
||||
|
||||
try:
|
||||
5 / (2-2)
|
||||
except ZeroDivisionError:
|
||||
pass
|
||||
else:
|
||||
assert False, 'Expected ZeroDivisionError'
|
||||
|
||||
try:
|
||||
5 % 0
|
||||
except ZeroDivisionError:
|
||||
pass
|
||||
else:
|
||||
assert False, 'Expected ZeroDivisionError'
|
||||
|
||||
try:
|
||||
5 // 0
|
||||
except ZeroDivisionError:
|
||||
pass
|
||||
else:
|
||||
assert False, 'Expected ZeroDivisionError'
|
||||
|
||||
try:
|
||||
5.3 // (-0.0)
|
||||
except ZeroDivisionError:
|
||||
pass
|
||||
else:
|
||||
assert False, 'Expected ZeroDivisionError'
|
||||
|
||||
try:
|
||||
divmod(5, 0)
|
||||
except ZeroDivisionError:
|
||||
pass
|
||||
else:
|
||||
assert False, 'Expected ZeroDivisionError'
|
||||
|
||||
try:
|
||||
raise ZeroDivisionError('Is an ArithmeticError subclass?')
|
||||
except ArithmeticError:
|
||||
pass
|
||||
else:
|
||||
assert False, 'Expected ZeroDivisionError'
|
||||
@@ -15,3 +15,6 @@ assert b >= a
|
||||
assert c >= a
|
||||
assert not a >= b
|
||||
|
||||
assert a + b == 2.5
|
||||
assert a - c == 0
|
||||
assert a / c == 1
|
||||
|
||||
@@ -16,6 +16,12 @@ assert import_target.Y == aliased_other_func()
|
||||
|
||||
assert STAR_IMPORT == '123'
|
||||
|
||||
try:
|
||||
from import_target import func, unknown_name
|
||||
raise AssertionError('`unknown_name` does not cause an exception')
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
# TODO: Once we can determine current directory, use that to construct this
|
||||
# path:
|
||||
#import sys
|
||||
|
||||
26
tests/snippets/index_overflow.py
Normal file
26
tests/snippets/index_overflow.py
Normal file
@@ -0,0 +1,26 @@
|
||||
import sys
|
||||
|
||||
|
||||
def expect_cannot_fit_index_error(s, index):
|
||||
try:
|
||||
s[index]
|
||||
except IndexError:
|
||||
pass
|
||||
# TODO: Replace current except block with commented
|
||||
# after solving https://github.com/RustPython/RustPython/issues/322
|
||||
# except IndexError as error:
|
||||
# assert str(error) == "cannot fit 'int' into an index-sized integer"
|
||||
else:
|
||||
assert False
|
||||
|
||||
|
||||
MAX_INDEX = sys.maxsize + 1
|
||||
MIN_INDEX = -(MAX_INDEX + 1)
|
||||
|
||||
test_str = "test"
|
||||
expect_cannot_fit_index_error(test_str, MIN_INDEX)
|
||||
expect_cannot_fit_index_error(test_str, MAX_INDEX)
|
||||
|
||||
test_list = [0, 1, 2, 3]
|
||||
expect_cannot_fit_index_error(test_list, MIN_INDEX)
|
||||
expect_cannot_fit_index_error(test_list, MAX_INDEX)
|
||||
@@ -10,6 +10,7 @@ y.extend(x)
|
||||
assert y == [2, 1, 2, 3, 1, 2, 3]
|
||||
|
||||
assert x * 0 == [], "list __mul__ by 0 failed"
|
||||
assert x * -1 == [], "list __mul__ by -1 failed"
|
||||
assert x * 2 == [1, 2, 3, 1, 2, 3], "list __mul__ by 2 failed"
|
||||
|
||||
assert ['a', 'b', 'c'].index('b') == 1
|
||||
@@ -20,3 +21,20 @@ except ValueError:
|
||||
pass
|
||||
else:
|
||||
assert False, "ValueError was not raised"
|
||||
|
||||
x = [[1,0,-3], 'a', 1]
|
||||
y = [[3,2,1], 'z', 2]
|
||||
assert x < y, "list __lt__ failed"
|
||||
|
||||
x = [5, 13, 31]
|
||||
y = [1, 10, 29]
|
||||
assert x > y, "list __gt__ failed"
|
||||
|
||||
|
||||
assert [1,2,'a'].pop() == 'a', "list pop failed"
|
||||
try:
|
||||
[].pop()
|
||||
except IndexError:
|
||||
pass
|
||||
else:
|
||||
assert False, "IndexError was not raised"
|
||||
|
||||
@@ -18,4 +18,12 @@ assert -a == -4
|
||||
assert +a == 4
|
||||
|
||||
# import math
|
||||
# print(math.cos(1.2))
|
||||
# assert(math.exp(2) == math.exp(2.0))
|
||||
# assert(math.exp(True) == math.exp(1.0))
|
||||
#
|
||||
# class Conversible():
|
||||
# def __float__(self):
|
||||
# print("Converting to float now!")
|
||||
# return 1.1111
|
||||
#
|
||||
# assert math.log(1.1111) == math.log(Conversible())
|
||||
|
||||
@@ -2,9 +2,43 @@ x = 5
|
||||
x.__init__(6)
|
||||
assert x == 5
|
||||
|
||||
|
||||
class A(int):
|
||||
pass
|
||||
|
||||
|
||||
x = A(7)
|
||||
assert x == 7
|
||||
assert type(x) is A
|
||||
|
||||
assert int(2).__index__() == 2
|
||||
assert int(2).__trunc__() == 2
|
||||
assert int(2).__ceil__() == 2
|
||||
assert int(2).__floor__() == 2
|
||||
assert int(2).__round__() == 2
|
||||
assert int(2).__round__(3) == 2
|
||||
assert int(-2).__index__() == -2
|
||||
assert int(-2).__trunc__() == -2
|
||||
assert int(-2).__ceil__() == -2
|
||||
assert int(-2).__floor__() == -2
|
||||
assert int(-2).__round__() == -2
|
||||
assert int(-2).__round__(3) == -2
|
||||
|
||||
assert round(10) == 10
|
||||
assert round(10, 2) == 10
|
||||
assert round(10, -1) == 10
|
||||
|
||||
assert int(2).__bool__() == True
|
||||
assert int(0.5).__bool__() == False
|
||||
assert int(-1).__bool__() == True
|
||||
|
||||
assert int(0).__invert__() == -1
|
||||
assert int(-3).__invert__() == 2
|
||||
assert int(4).__invert__() == -5
|
||||
|
||||
assert int(0).__rxor__(0) == 0
|
||||
assert int(1).__rxor__(0) == 1
|
||||
assert int(0).__rxor__(1) == 1
|
||||
assert int(1).__rxor__(1) == 0
|
||||
assert int(3).__rxor__(-3) == -2
|
||||
assert int(3).__rxor__(4) == 7
|
||||
|
||||
@@ -2,3 +2,9 @@ import os
|
||||
|
||||
assert os.open('README.md', 0) > 0
|
||||
|
||||
|
||||
try:
|
||||
os.open('DOES_NOT_EXIST', 0)
|
||||
assert False
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
26
tests/snippets/set.py
Normal file
26
tests/snippets/set.py
Normal file
@@ -0,0 +1,26 @@
|
||||
assert set([1,2]) == set([1,2])
|
||||
assert not set([1,2,3]) == set([1,2])
|
||||
|
||||
assert set([1,2,3]) >= set([1,2])
|
||||
assert set([1,2]) >= set([1,2])
|
||||
assert not set([1,3]) >= set([1,2])
|
||||
|
||||
assert set([1,2,3]).issuperset(set([1,2]))
|
||||
assert set([1,2]).issuperset(set([1,2]))
|
||||
assert not set([1,3]).issuperset(set([1,2]))
|
||||
|
||||
assert set([1,2,3]) > set([1,2])
|
||||
assert not set([1,2]) > set([1,2])
|
||||
assert not set([1,3]) > set([1,2])
|
||||
|
||||
assert set([1,2]) <= set([1,2,3])
|
||||
assert set([1,2]) <= set([1,2])
|
||||
assert not set([1,3]) <= set([1,2])
|
||||
|
||||
assert set([1,2]).issubset(set([1,2,3]))
|
||||
assert set([1,2]).issubset(set([1,2]))
|
||||
assert not set([1,3]).issubset(set([1,2]))
|
||||
|
||||
assert set([1,2]) < set([1,2,3])
|
||||
assert not set([1,2]) < set([1,2])
|
||||
assert not set([1,3]) < set([1,2])
|
||||
@@ -8,6 +8,9 @@ assert "\n" == """
|
||||
"""
|
||||
|
||||
assert len(""" " \" """) == 5
|
||||
assert len("é") == 1
|
||||
assert len("é") == 2
|
||||
assert len("あ") == 1
|
||||
|
||||
assert type("") is str
|
||||
assert type(b"") is bytes
|
||||
@@ -35,6 +38,7 @@ assert not a.endswith('on')
|
||||
assert a.zfill(8) == '000Hallo'
|
||||
assert a.isalnum()
|
||||
assert not a.isdigit()
|
||||
assert not a.isdecimal()
|
||||
assert not a.isnumeric()
|
||||
assert a.istitle()
|
||||
assert a.isalpha()
|
||||
@@ -65,6 +69,9 @@ assert '-'.join(['1', '2', '3']) == '1-2-3'
|
||||
assert 'HALLO'.isupper()
|
||||
assert "hello, my name is".partition("my ") == ('hello, ', 'my ', 'name is')
|
||||
assert "hello, my name is".rpartition("is") == ('hello, my name ', 'is', '')
|
||||
assert not ''.isdecimal()
|
||||
assert '123'.isdecimal()
|
||||
assert not '\u00B2'.isdecimal()
|
||||
|
||||
# String Formatting
|
||||
assert "{} {}".format(1,2) == "1 2"
|
||||
|
||||
37
tests/snippets/test_bitwise.py
Normal file
37
tests/snippets/test_bitwise.py
Normal file
@@ -0,0 +1,37 @@
|
||||
def assert_raises(expr, exc_type):
|
||||
"""
|
||||
Helper function to assert `expr` raises an exception of type `exc_type`
|
||||
Args:
|
||||
expr: Callable
|
||||
exec_type: Exception
|
||||
Returns:
|
||||
None
|
||||
Raises:
|
||||
Assertion error on failure
|
||||
"""
|
||||
try:
|
||||
expr(None)
|
||||
except exc_type:
|
||||
assert True
|
||||
else:
|
||||
assert False
|
||||
|
||||
#
|
||||
# Tests
|
||||
#
|
||||
assert 8 >> 3 == 1
|
||||
assert 8 << 3 == 64
|
||||
|
||||
# Left shift raises type error
|
||||
assert_raises(lambda _: 1 << 0.1, TypeError)
|
||||
assert_raises(lambda _: 1 << "abc", TypeError)
|
||||
|
||||
# Right shift raises type error
|
||||
assert_raises(lambda _: 1 >> 0.1, TypeError)
|
||||
assert_raises(lambda _: 1 >> "abc", TypeError)
|
||||
|
||||
# Left shift raises value error on negative
|
||||
assert_raises(lambda _: 1 << -1, ValueError)
|
||||
|
||||
# Right shift raises value error on negative
|
||||
assert_raises(lambda _: 1 >> -1, ValueError)
|
||||
@@ -5,3 +5,17 @@ assert x[0] == 1
|
||||
|
||||
y = (1,)
|
||||
assert y[0] == 1
|
||||
|
||||
assert x + y == (1, 2, 1)
|
||||
|
||||
assert x * 3 == (1, 2, 1, 2, 1, 2)
|
||||
# assert 3 * x == (1, 2, 1, 2, 1, 2)
|
||||
assert x * 0 == ()
|
||||
assert x * -1 == () # integers less than zero treated as 0
|
||||
|
||||
assert y < x, "tuple __lt__ failed"
|
||||
assert x > y, "tuple __gt__ failed"
|
||||
|
||||
|
||||
b = (1,2,3)
|
||||
assert b.index(2) == 1
|
||||
|
||||
33
tests/snippets/unicode_slicing.py
Normal file
33
tests/snippets/unicode_slicing.py
Normal file
@@ -0,0 +1,33 @@
|
||||
def test_slice_bounds(s):
|
||||
# End out of range
|
||||
assert s[0:100] == s
|
||||
assert s[0:-100] == ''
|
||||
# Start out of range
|
||||
assert s[100:1] == ''
|
||||
# Out of range both sides
|
||||
# This is the behaviour in cpython
|
||||
# assert s[-100:100] == s
|
||||
|
||||
def expect_index_error(s, index):
|
||||
try:
|
||||
s[index]
|
||||
except IndexError:
|
||||
pass
|
||||
else:
|
||||
assert False
|
||||
|
||||
unicode_str = "∀∂"
|
||||
assert unicode_str[0] == "∀"
|
||||
assert unicode_str[1] == "∂"
|
||||
assert unicode_str[-1] == "∂"
|
||||
|
||||
test_slice_bounds(unicode_str)
|
||||
expect_index_error(unicode_str, 100)
|
||||
expect_index_error(unicode_str, -100)
|
||||
|
||||
ascii_str = "hello world"
|
||||
test_slice_bounds(ascii_str)
|
||||
assert ascii_str[0] == "h"
|
||||
assert ascii_str[1] == "e"
|
||||
assert ascii_str[-1] == "d"
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
bool_expected_methods = [
|
||||
expected_methods = []
|
||||
|
||||
# TODO: using tuple could have been better
|
||||
expected_methods.append({'name': 'bool', 'methods': [
|
||||
'__abs__',
|
||||
'__add__',
|
||||
'__and__',
|
||||
@@ -69,9 +72,8 @@ bool_expected_methods = [
|
||||
'numerator',
|
||||
'real',
|
||||
'to_bytes',
|
||||
]
|
||||
|
||||
bytearray_expected_methods = [
|
||||
], 'type': bool})
|
||||
expected_methods.append({'name': 'bytearray', 'type': bytearray, 'methods': [
|
||||
'__add__',
|
||||
'__alloc__',
|
||||
'__class__',
|
||||
@@ -157,9 +159,8 @@ bytearray_expected_methods = [
|
||||
'translate',
|
||||
'upper',
|
||||
'zfill',
|
||||
]
|
||||
|
||||
bytes_expected_methods = [
|
||||
]})
|
||||
expected_methods.append({'name': 'bytes', 'type': bytes, 'methods': [
|
||||
'__add__',
|
||||
'__class__',
|
||||
'__contains__',
|
||||
@@ -233,9 +234,8 @@ bytes_expected_methods = [
|
||||
'translate',
|
||||
'upper',
|
||||
'zfill',
|
||||
]
|
||||
|
||||
complex_expected_methods = [
|
||||
]})
|
||||
expected_methods.append({'name': 'complex', 'type': complex, 'methods': [
|
||||
'__abs__',
|
||||
'__add__',
|
||||
'__bool__',
|
||||
@@ -285,9 +285,8 @@ complex_expected_methods = [
|
||||
'conjugate',
|
||||
'imag',
|
||||
'real',
|
||||
]
|
||||
|
||||
dict_expected_methods = [
|
||||
]})
|
||||
expected_methods.append({'name': 'dict', 'type': dict, 'methods': [
|
||||
'__class__',
|
||||
'__contains__',
|
||||
'__delattr__',
|
||||
@@ -328,9 +327,8 @@ dict_expected_methods = [
|
||||
'setdefault',
|
||||
'update',
|
||||
'values',
|
||||
]
|
||||
|
||||
float_expected_methods = [
|
||||
]})
|
||||
expected_methods.append({'name': 'float','type':float,'methods':[
|
||||
'__abs__',
|
||||
'__add__',
|
||||
'__bool__',
|
||||
@@ -388,9 +386,8 @@ float_expected_methods = [
|
||||
'imag',
|
||||
'is_integer',
|
||||
'real',
|
||||
]
|
||||
|
||||
frozenset_expected_methods = [
|
||||
]})
|
||||
expected_methods.append({'name': 'frozenset','type':frozenset, 'methods': [
|
||||
'__and__',
|
||||
'__class__',
|
||||
'__contains__',
|
||||
@@ -433,9 +430,8 @@ frozenset_expected_methods = [
|
||||
'issuperset',
|
||||
'symmetric_difference',
|
||||
'union',
|
||||
]
|
||||
|
||||
int_expected_methods = [
|
||||
]})
|
||||
expected_methods.append({'name': 'int', 'type':int, 'methods': [
|
||||
'__abs__',
|
||||
'__add__',
|
||||
'__and__',
|
||||
@@ -506,9 +502,8 @@ int_expected_methods = [
|
||||
'numerator',
|
||||
'real',
|
||||
'to_bytes',
|
||||
]
|
||||
|
||||
iter_expected_methods = [
|
||||
]})
|
||||
expected_methods.append({'name': 'iter','type':iter,'methods':[
|
||||
'__class__',
|
||||
'__delattr__',
|
||||
'__dir__',
|
||||
@@ -536,9 +531,8 @@ iter_expected_methods = [
|
||||
'__sizeof__',
|
||||
'__str__',
|
||||
'__subclasshook__',
|
||||
]
|
||||
|
||||
list_expected_methods = [
|
||||
]})
|
||||
expected_methods.append({'name': 'list','type':list,'methods':[
|
||||
'__add__',
|
||||
'__class__',
|
||||
'__contains__',
|
||||
@@ -585,9 +579,8 @@ list_expected_methods = [
|
||||
'remove',
|
||||
'reverse',
|
||||
'sort',
|
||||
]
|
||||
|
||||
memoryview_expected_methods = [
|
||||
]})
|
||||
expected_methods.append({'name': 'memoryview','type':memoryview,'methods':[
|
||||
'__class__',
|
||||
'__delattr__',
|
||||
'__delitem__',
|
||||
@@ -634,9 +627,8 @@ memoryview_expected_methods = [
|
||||
'suboffsets',
|
||||
'tobytes',
|
||||
'tolist',
|
||||
]
|
||||
|
||||
range_expected_methods = [
|
||||
]})
|
||||
expected_methods.append({'name': 'range','type':range,'methods':[
|
||||
'__bool__',
|
||||
'__class__',
|
||||
'__contains__',
|
||||
@@ -671,9 +663,8 @@ range_expected_methods = [
|
||||
'start',
|
||||
'step',
|
||||
'stop',
|
||||
]
|
||||
|
||||
set_expected_methods = [
|
||||
]})
|
||||
expected_methods.append({'name': 'set','type':set,'methods':[
|
||||
'__and__',
|
||||
'__class__',
|
||||
'__contains__',
|
||||
@@ -729,9 +720,8 @@ set_expected_methods = [
|
||||
'symmetric_difference_update',
|
||||
'union',
|
||||
'update',
|
||||
]
|
||||
|
||||
string_expected_methods = [
|
||||
]})
|
||||
expected_methods.append({'name': 'string','type':str,'methods':[
|
||||
'__add__',
|
||||
'__class__',
|
||||
'__contains__',
|
||||
@@ -810,9 +800,8 @@ string_expected_methods = [
|
||||
'translate',
|
||||
'upper',
|
||||
'zfill'
|
||||
]
|
||||
|
||||
tuple_expected_methods = [
|
||||
]})
|
||||
expected_methods.append({'name': 'tuple','type':tuple, 'methods': [
|
||||
'__add__',
|
||||
'__class__',
|
||||
'__contains__',
|
||||
@@ -846,116 +835,42 @@ tuple_expected_methods = [
|
||||
'__subclasshook__',
|
||||
'count',
|
||||
'index',
|
||||
]
|
||||
]})
|
||||
expected_methods.append({'name': 'object', 'type':object, 'methods':[
|
||||
'__repr__',
|
||||
'__hash__',
|
||||
'__str__',
|
||||
'__getattribute__',
|
||||
'__setattr__',
|
||||
'__delattr__',
|
||||
'__lt__',
|
||||
'__le__',
|
||||
'__eq__',
|
||||
'__ne__',
|
||||
'__gt__',
|
||||
'__ge__',
|
||||
'__init__',
|
||||
'__new__',
|
||||
'__reduce_ex__',
|
||||
'__reduce__',
|
||||
'__subclasshook__',
|
||||
'__init_subclass__',
|
||||
'__format__',
|
||||
'__sizeof__',
|
||||
'__dir__',
|
||||
'__class__',
|
||||
'__doc__'
|
||||
]})
|
||||
|
||||
not_implemented = []
|
||||
|
||||
for method in bool_expected_methods:
|
||||
try:
|
||||
if not hasattr(bool, method):
|
||||
not_implemented.append(("bool", method))
|
||||
except NameError:
|
||||
not_implemented.append(("bool", method))
|
||||
|
||||
for method in bytearray_expected_methods:
|
||||
try:
|
||||
if not hasattr(bytearray(), method):
|
||||
not_implemented.append(("bytearray", method))
|
||||
except NameError:
|
||||
not_implemented.append(("bytearray", method))
|
||||
|
||||
for method in bytes_expected_methods:
|
||||
try:
|
||||
if not hasattr(bytes, method):
|
||||
not_implemented.append(("bytes", method))
|
||||
except NameError:
|
||||
not_implemented.append(("bytes", method))
|
||||
|
||||
for method in complex_expected_methods:
|
||||
try:
|
||||
if not hasattr(complex, method):
|
||||
not_implemented.append(("complex", method))
|
||||
except NameError:
|
||||
not_implemented.append(("complex", method))
|
||||
|
||||
for method in dict_expected_methods:
|
||||
try:
|
||||
if not hasattr(dict, method):
|
||||
not_implemented.append(("dict", method))
|
||||
except NameError:
|
||||
not_implemented.append(("dict", method))
|
||||
|
||||
for method in float_expected_methods:
|
||||
try:
|
||||
if not hasattr(float, method):
|
||||
not_implemented.append(("float", method))
|
||||
except NameError:
|
||||
not_implemented.append(("float", method))
|
||||
|
||||
for method in frozenset_expected_methods:
|
||||
# TODO: uncomment this when frozenset is implemented
|
||||
# try:
|
||||
# if not hasattr(frozenset, method):
|
||||
# not_implemented.append(("frozenset", method))
|
||||
# except NameError:
|
||||
not_implemented.append(("frozenset", method))
|
||||
|
||||
for method in int_expected_methods:
|
||||
try:
|
||||
if not hasattr(int, method):
|
||||
not_implemented.append(("int", method))
|
||||
except NameError:
|
||||
not_implemented.append(("int", method))
|
||||
|
||||
for method in iter_expected_methods:
|
||||
try:
|
||||
if not hasattr(iter([]), method):
|
||||
not_implemented.append(("iter", method))
|
||||
except NameError:
|
||||
not_implemented.append(("iter", method))
|
||||
|
||||
for method in list_expected_methods:
|
||||
try:
|
||||
if not hasattr(list, method):
|
||||
not_implemented.append(("list", method))
|
||||
except NameError:
|
||||
not_implemented.append(("list", method))
|
||||
|
||||
for method in memoryview_expected_methods:
|
||||
# TODO: uncomment this when memoryview is implemented
|
||||
# try:
|
||||
# if not hasattr(memoryview, method):
|
||||
# not_implemented.append(("memoryview", method))
|
||||
# except NameError:
|
||||
not_implemented.append(("memoryview", method))
|
||||
|
||||
for method in range_expected_methods:
|
||||
try:
|
||||
if not hasattr(range, method):
|
||||
not_implemented.append(("range", method))
|
||||
except NameError:
|
||||
not_implemented.append(("range", method))
|
||||
|
||||
for method in set_expected_methods:
|
||||
try:
|
||||
if not hasattr(set, method):
|
||||
not_implemented.append(("set", method))
|
||||
except NameError:
|
||||
not_implemented.append(("set", method))
|
||||
|
||||
for method in string_expected_methods:
|
||||
try:
|
||||
if not hasattr(str, method):
|
||||
not_implemented.append(("string", method))
|
||||
except NameError:
|
||||
not_implemented.append(("string", method))
|
||||
|
||||
for method in tuple_expected_methods:
|
||||
try:
|
||||
if not hasattr(tuple, method):
|
||||
not_implemented.append(("tuple", method))
|
||||
except NameError:
|
||||
not_implemented.append(("tuple", method))
|
||||
for item in expected_methods:
|
||||
for method in item['methods']:
|
||||
try:
|
||||
if not hasattr(item['type'], method):
|
||||
not_implemented.append((item['name'], method))
|
||||
except NameError:
|
||||
not_implemented.append((item['name'], method))
|
||||
|
||||
for r in not_implemented:
|
||||
print(r[0], ".", r[1])
|
||||
|
||||
@@ -8,6 +8,7 @@ bitflags = "1.0.4"
|
||||
num-complex = "0.2"
|
||||
num-bigint = "0.2.1"
|
||||
num-traits = "0.2"
|
||||
num-integer = "0.1.39"
|
||||
rand = "0.5"
|
||||
log = "0.3"
|
||||
rustpython_parser = {path = "../parser"}
|
||||
@@ -17,4 +18,5 @@ serde_json = "1.0.26"
|
||||
byteorder = "1.2.6"
|
||||
regex = "1"
|
||||
statrs = "0.10.0"
|
||||
caseless = "0.2.1"
|
||||
caseless = "0.2.1"
|
||||
unicode-segmentation = "1.2.1"
|
||||
|
||||
@@ -21,8 +21,7 @@ use super::pyobject::{
|
||||
use super::stdlib::io::io_open;
|
||||
|
||||
use super::vm::VirtualMachine;
|
||||
use num_bigint::ToBigInt;
|
||||
use num_traits::{Signed, ToPrimitive, Zero};
|
||||
use num_traits::{Signed, ToPrimitive};
|
||||
|
||||
fn get_locals(vm: &mut VirtualMachine) -> PyObjectRef {
|
||||
let d = vm.new_dict();
|
||||
@@ -136,11 +135,11 @@ fn builtin_compile(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
|
||||
let mode = {
|
||||
let mode = objstr::get_value(mode);
|
||||
if mode == String::from("exec") {
|
||||
if mode == "exec" {
|
||||
compile::Mode::Exec
|
||||
} else if mode == "eval".to_string() {
|
||||
} else if mode == "eval" {
|
||||
compile::Mode::Eval
|
||||
} else if mode == "single".to_string() {
|
||||
} else if mode == "single" {
|
||||
compile::Mode::Single
|
||||
} else {
|
||||
return Err(
|
||||
@@ -151,7 +150,7 @@ fn builtin_compile(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
|
||||
let filename = objstr::get_value(filename);
|
||||
|
||||
compile::compile(vm, &source, mode, Some(filename))
|
||||
compile::compile(vm, &source, &mode, filename)
|
||||
}
|
||||
|
||||
fn builtin_delattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
@@ -180,29 +179,8 @@ fn builtin_divmod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
}
|
||||
}
|
||||
|
||||
fn builtin_enumerate(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [(iterable, None)],
|
||||
optional = [(start, None)]
|
||||
);
|
||||
let items = vm.extract_elements(iterable)?;
|
||||
let start = if let Some(start) = start {
|
||||
objint::get_value(start)
|
||||
} else {
|
||||
Zero::zero()
|
||||
};
|
||||
let mut new_items = vec![];
|
||||
for (i, item) in items.into_iter().enumerate() {
|
||||
let element = vm
|
||||
.ctx
|
||||
.new_tuple(vec![vm.ctx.new_int(i.to_bigint().unwrap() + &start), item]);
|
||||
new_items.push(element);
|
||||
}
|
||||
Ok(vm.ctx.new_list(new_items))
|
||||
}
|
||||
|
||||
/// Implements `eval`.
|
||||
/// See also: https://docs.python.org/3/library/functions.html#eval
|
||||
fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
@@ -222,7 +200,7 @@ fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
let source = objstr::get_value(source);
|
||||
// TODO: fix this newline bug:
|
||||
let source = format!("{}\n", source);
|
||||
compile::compile(vm, &source, mode, None)?
|
||||
compile::compile(vm, &source, &mode, "<string>".to_string())?
|
||||
} else {
|
||||
return Err(vm.new_type_error("code argument must be str or code object".to_string()));
|
||||
};
|
||||
@@ -236,7 +214,7 @@ fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
// TODO: handle optional globals
|
||||
// Construct new scope:
|
||||
let scope_inner = Scope {
|
||||
locals: locals,
|
||||
locals,
|
||||
parent: None,
|
||||
};
|
||||
let scope = PyObject {
|
||||
@@ -249,6 +227,8 @@ fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
vm.run_code_obj(code_obj.clone(), scope)
|
||||
}
|
||||
|
||||
/// Implements `exec`
|
||||
/// https://docs.python.org/3/library/functions.html#exec
|
||||
fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
@@ -266,7 +246,7 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
let source = objstr::get_value(source);
|
||||
// TODO: fix this newline bug:
|
||||
let source = format!("{}\n", source);
|
||||
compile::compile(vm, &source, mode, None)?
|
||||
compile::compile(vm, &source, &mode, "<string>".to_string())?
|
||||
} else if objtype::isinstance(source, &vm.ctx.code_type()) {
|
||||
source.clone()
|
||||
} else {
|
||||
@@ -284,7 +264,7 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
|
||||
// Construct new scope:
|
||||
let scope_inner = Scope {
|
||||
locals: locals,
|
||||
locals,
|
||||
parent: None,
|
||||
};
|
||||
let scope = PyObject {
|
||||
@@ -297,29 +277,6 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
vm.run_code_obj(code_obj, scope)
|
||||
}
|
||||
|
||||
fn builtin_filter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(function, None), (iterable, None)]);
|
||||
|
||||
// TODO: process one element at a time from iterators.
|
||||
let iterable = vm.extract_elements(iterable)?;
|
||||
|
||||
let mut new_items = vec![];
|
||||
for element in iterable {
|
||||
// apply function:
|
||||
let args = PyFuncArgs {
|
||||
args: vec![element.clone()],
|
||||
kwargs: vec![],
|
||||
};
|
||||
let result = vm.invoke(function.clone(), args)?;
|
||||
let result = objbool::boolval(vm, result)?;
|
||||
if result {
|
||||
new_items.push(element);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(vm.ctx.new_list(new_items))
|
||||
}
|
||||
|
||||
fn builtin_format(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
@@ -378,7 +335,7 @@ fn builtin_hex(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
fn builtin_id(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(obj, None)]);
|
||||
|
||||
Ok(vm.context().new_int(obj.get_id().to_bigint().unwrap()))
|
||||
Ok(vm.context().new_int(obj.get_id()))
|
||||
}
|
||||
|
||||
// builtin_input
|
||||
@@ -424,33 +381,6 @@ fn builtin_locals(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
Ok(vm.get_locals())
|
||||
}
|
||||
|
||||
fn builtin_map(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(function, None), (iter_target, None)]);
|
||||
let iterator = objiter::get_iter(vm, iter_target)?;
|
||||
let mut elements = vec![];
|
||||
loop {
|
||||
match vm.call_method(&iterator, "__next__", vec![]) {
|
||||
Ok(v) => {
|
||||
// Now apply function:
|
||||
let mapped_value = vm.invoke(
|
||||
function.clone(),
|
||||
PyFuncArgs {
|
||||
args: vec![v],
|
||||
kwargs: vec![],
|
||||
},
|
||||
)?;
|
||||
elements.push(mapped_value);
|
||||
}
|
||||
Err(_) => break,
|
||||
}
|
||||
}
|
||||
|
||||
trace!("Mapped elements: {:?}", elements);
|
||||
|
||||
// TODO: when iterators are implemented, we can improve this function.
|
||||
Ok(vm.ctx.new_list(elements))
|
||||
}
|
||||
|
||||
fn builtin_max(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
let candidates = if args.args.len() > 1 {
|
||||
args.args.clone()
|
||||
@@ -461,7 +391,7 @@ fn builtin_max(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
return Err(vm.new_type_error("Expected 1 or more arguments".to_string()));
|
||||
};
|
||||
|
||||
if candidates.len() == 0 {
|
||||
if candidates.is_empty() {
|
||||
let default = args.get_optional_kwarg("default");
|
||||
if default.is_none() {
|
||||
return Err(vm.new_value_error("max() arg is an empty sequence".to_string()));
|
||||
@@ -512,7 +442,7 @@ fn builtin_min(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
return Err(vm.new_type_error("Expected 1 or more arguments".to_string()));
|
||||
};
|
||||
|
||||
if candidates.len() == 0 {
|
||||
if candidates.is_empty() {
|
||||
let default = args.get_optional_kwarg("default");
|
||||
if default.is_none() {
|
||||
return Err(vm.new_value_error("min() arg is an empty sequence".to_string()));
|
||||
@@ -599,9 +529,7 @@ fn builtin_ord(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
)));
|
||||
}
|
||||
match string.chars().next() {
|
||||
Some(character) => Ok(vm
|
||||
.context()
|
||||
.new_int((character as i32).to_bigint().unwrap())),
|
||||
Some(character) => Ok(vm.context().new_int(character as i32)),
|
||||
None => Err(vm.new_type_error(
|
||||
"ord() could not guess the integer representing this character".to_string(),
|
||||
)),
|
||||
@@ -655,21 +583,42 @@ pub fn builtin_print(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
Ok(vm.get_none())
|
||||
}
|
||||
|
||||
fn builtin_range(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(range, Some(vm.ctx.int_type()))]);
|
||||
let value = objint::get_value(range);
|
||||
let range_elements: Vec<PyObjectRef> = (0..value.to_i32().unwrap())
|
||||
.map(|num| vm.context().new_int(num.to_bigint().unwrap()))
|
||||
.collect();
|
||||
Ok(vm.context().new_list(range_elements))
|
||||
}
|
||||
|
||||
fn builtin_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(obj, None)]);
|
||||
vm.to_repr(obj)
|
||||
}
|
||||
|
||||
fn builtin_reversed(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(obj, None)]);
|
||||
|
||||
match vm.get_method(obj.clone(), "__reversed__") {
|
||||
Ok(value) => vm.invoke(value, PyFuncArgs::default()),
|
||||
// TODO: fallback to using __len__ and __getitem__, if object supports sequence protocol
|
||||
Err(..) => Err(vm.new_type_error(format!(
|
||||
"'{}' object is not reversible",
|
||||
objtype::get_type_name(&obj.typ()),
|
||||
))),
|
||||
}
|
||||
}
|
||||
// builtin_reversed
|
||||
// builtin_round
|
||||
|
||||
fn builtin_round(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [(number, Some(vm.ctx.object()))],
|
||||
optional = [(ndigits, None)]
|
||||
);
|
||||
if let Some(ndigits) = ndigits {
|
||||
let ndigits = vm.call_method(ndigits, "__int__", vec![])?;
|
||||
let rounded = vm.call_method(number, "__round__", vec![ndigits])?;
|
||||
Ok(rounded)
|
||||
} else {
|
||||
// without a parameter, the result type is coerced to int
|
||||
let rounded = &vm.call_method(number, "__round__", vec![])?;
|
||||
Ok(vm.ctx.new_int(objint::get_value(rounded)))
|
||||
}
|
||||
}
|
||||
|
||||
fn builtin_setattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
@@ -690,7 +639,7 @@ fn builtin_sum(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
let items = vm.extract_elements(iterable)?;
|
||||
|
||||
// Start with zero and add at will:
|
||||
let mut sum = vm.ctx.new_int(Zero::zero());
|
||||
let mut sum = vm.ctx.new_int(0);
|
||||
for item in items {
|
||||
sum = vm._add(sum, item)?;
|
||||
}
|
||||
@@ -698,32 +647,6 @@ fn builtin_sum(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
}
|
||||
|
||||
// builtin_vars
|
||||
|
||||
fn builtin_zip(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
no_kwargs!(vm, args);
|
||||
|
||||
// TODO: process one element at a time from iterators.
|
||||
let mut iterables = vec![];
|
||||
for iterable in args.args.iter() {
|
||||
let iterable = vm.extract_elements(iterable)?;
|
||||
iterables.push(iterable);
|
||||
}
|
||||
|
||||
let minsize: usize = iterables.iter().map(|i| i.len()).min().unwrap_or(0);
|
||||
|
||||
let mut new_items = vec![];
|
||||
for i in 0..minsize {
|
||||
let items = iterables
|
||||
.iter()
|
||||
.map(|iterable| iterable[i].clone())
|
||||
.collect();
|
||||
let element = vm.ctx.new_tuple(items);
|
||||
new_items.push(element);
|
||||
}
|
||||
|
||||
Ok(vm.ctx.new_list(new_items))
|
||||
}
|
||||
|
||||
// builtin___import__
|
||||
|
||||
pub fn make_module(ctx: &PyContext) -> PyObjectRef {
|
||||
@@ -749,12 +672,12 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef {
|
||||
ctx.set_attr(&py_mod, "dict", ctx.dict_type());
|
||||
ctx.set_attr(&py_mod, "divmod", ctx.new_rustfunc(builtin_divmod));
|
||||
ctx.set_attr(&py_mod, "dir", ctx.new_rustfunc(builtin_dir));
|
||||
ctx.set_attr(&py_mod, "enumerate", ctx.new_rustfunc(builtin_enumerate));
|
||||
ctx.set_attr(&py_mod, "enumerate", ctx.enumerate_type());
|
||||
ctx.set_attr(&py_mod, "eval", ctx.new_rustfunc(builtin_eval));
|
||||
ctx.set_attr(&py_mod, "exec", ctx.new_rustfunc(builtin_exec));
|
||||
ctx.set_attr(&py_mod, "float", ctx.float_type());
|
||||
ctx.set_attr(&py_mod, "frozenset", ctx.frozenset_type());
|
||||
ctx.set_attr(&py_mod, "filter", ctx.new_rustfunc(builtin_filter));
|
||||
ctx.set_attr(&py_mod, "filter", ctx.filter_type());
|
||||
ctx.set_attr(&py_mod, "format", ctx.new_rustfunc(builtin_format));
|
||||
ctx.set_attr(&py_mod, "getattr", ctx.new_rustfunc(builtin_getattr));
|
||||
ctx.set_attr(&py_mod, "hasattr", ctx.new_rustfunc(builtin_hasattr));
|
||||
@@ -768,7 +691,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef {
|
||||
ctx.set_attr(&py_mod, "len", ctx.new_rustfunc(builtin_len));
|
||||
ctx.set_attr(&py_mod, "list", ctx.list_type());
|
||||
ctx.set_attr(&py_mod, "locals", ctx.new_rustfunc(builtin_locals));
|
||||
ctx.set_attr(&py_mod, "map", ctx.new_rustfunc(builtin_map));
|
||||
ctx.set_attr(&py_mod, "map", ctx.map_type());
|
||||
ctx.set_attr(&py_mod, "max", ctx.new_rustfunc(builtin_max));
|
||||
ctx.set_attr(&py_mod, "memoryview", ctx.memoryview_type());
|
||||
ctx.set_attr(&py_mod, "min", ctx.new_rustfunc(builtin_min));
|
||||
@@ -780,17 +703,20 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef {
|
||||
ctx.set_attr(&py_mod, "pow", ctx.new_rustfunc(builtin_pow));
|
||||
ctx.set_attr(&py_mod, "print", ctx.new_rustfunc(builtin_print));
|
||||
ctx.set_attr(&py_mod, "property", ctx.property_type());
|
||||
ctx.set_attr(&py_mod, "range", ctx.new_rustfunc(builtin_range));
|
||||
ctx.set_attr(&py_mod, "range", ctx.range_type());
|
||||
ctx.set_attr(&py_mod, "repr", ctx.new_rustfunc(builtin_repr));
|
||||
ctx.set_attr(&py_mod, "reversed", ctx.new_rustfunc(builtin_reversed));
|
||||
ctx.set_attr(&py_mod, "round", ctx.new_rustfunc(builtin_round));
|
||||
ctx.set_attr(&py_mod, "set", ctx.set_type());
|
||||
ctx.set_attr(&py_mod, "setattr", ctx.new_rustfunc(builtin_setattr));
|
||||
ctx.set_attr(&py_mod, "slice", ctx.slice_type());
|
||||
ctx.set_attr(&py_mod, "staticmethod", ctx.staticmethod_type());
|
||||
ctx.set_attr(&py_mod, "str", ctx.str_type());
|
||||
ctx.set_attr(&py_mod, "sum", ctx.new_rustfunc(builtin_sum));
|
||||
ctx.set_attr(&py_mod, "super", ctx.super_type());
|
||||
ctx.set_attr(&py_mod, "tuple", ctx.tuple_type());
|
||||
ctx.set_attr(&py_mod, "type", ctx.type_type());
|
||||
ctx.set_attr(&py_mod, "zip", ctx.new_rustfunc(builtin_zip));
|
||||
ctx.set_attr(&py_mod, "zip", ctx.zip_type());
|
||||
|
||||
// Exceptions:
|
||||
ctx.set_attr(
|
||||
@@ -799,6 +725,11 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef {
|
||||
ctx.exceptions.base_exception_type.clone(),
|
||||
);
|
||||
ctx.set_attr(&py_mod, "Exception", ctx.exceptions.exception_type.clone());
|
||||
ctx.set_attr(
|
||||
&py_mod,
|
||||
"ArithmeticError",
|
||||
ctx.exceptions.arithmetic_error.clone(),
|
||||
);
|
||||
ctx.set_attr(
|
||||
&py_mod,
|
||||
"AssertionError",
|
||||
@@ -810,6 +741,11 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef {
|
||||
ctx.exceptions.attribute_error.clone(),
|
||||
);
|
||||
ctx.set_attr(&py_mod, "NameError", ctx.exceptions.name_error.clone());
|
||||
ctx.set_attr(
|
||||
&py_mod,
|
||||
"OverflowError",
|
||||
ctx.exceptions.overflow_error.clone(),
|
||||
);
|
||||
ctx.set_attr(
|
||||
&py_mod,
|
||||
"RuntimeError",
|
||||
@@ -822,6 +758,23 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef {
|
||||
);
|
||||
ctx.set_attr(&py_mod, "TypeError", ctx.exceptions.type_error.clone());
|
||||
ctx.set_attr(&py_mod, "ValueError", ctx.exceptions.value_error.clone());
|
||||
ctx.set_attr(&py_mod, "IndexError", ctx.exceptions.index_error.clone());
|
||||
ctx.set_attr(&py_mod, "ImportError", ctx.exceptions.import_error.clone());
|
||||
ctx.set_attr(
|
||||
&py_mod,
|
||||
"FileNotFoundError",
|
||||
ctx.exceptions.file_not_found_error.clone(),
|
||||
);
|
||||
ctx.set_attr(
|
||||
&py_mod,
|
||||
"StopIteration",
|
||||
ctx.exceptions.stop_iteration.clone(),
|
||||
);
|
||||
ctx.set_attr(
|
||||
&py_mod,
|
||||
"ZeroDivisionError",
|
||||
ctx.exceptions.zero_division_error.clone(),
|
||||
);
|
||||
|
||||
py_mod
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ pub struct CodeObject {
|
||||
pub varargs: Option<Option<String>>, // *args or *
|
||||
pub kwonlyarg_names: Vec<String>,
|
||||
pub varkeywords: Option<Option<String>>, // **kwargs or **
|
||||
pub source_path: Option<String>,
|
||||
pub source_path: String,
|
||||
pub first_line_number: usize,
|
||||
pub obj_name: String, // Name of the object that created this code object
|
||||
pub is_generator: bool,
|
||||
}
|
||||
@@ -33,19 +34,21 @@ impl CodeObject {
|
||||
varargs: Option<Option<String>>,
|
||||
kwonlyarg_names: Vec<String>,
|
||||
varkeywords: Option<Option<String>>,
|
||||
source_path: Option<String>,
|
||||
source_path: String,
|
||||
first_line_number: usize,
|
||||
obj_name: String,
|
||||
) -> CodeObject {
|
||||
CodeObject {
|
||||
instructions: Vec::new(),
|
||||
label_map: HashMap::new(),
|
||||
locations: Vec::new(),
|
||||
arg_names: arg_names,
|
||||
varargs: varargs,
|
||||
kwonlyarg_names: kwonlyarg_names,
|
||||
varkeywords: varkeywords,
|
||||
source_path: source_path,
|
||||
obj_name: obj_name,
|
||||
arg_names,
|
||||
varargs,
|
||||
kwonlyarg_names,
|
||||
varkeywords,
|
||||
source_path,
|
||||
first_line_number,
|
||||
obj_name,
|
||||
is_generator: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
//! https://github.com/micropython/micropython/blob/master/py/compile.c
|
||||
|
||||
use super::bytecode::{self, CallType, CodeObject, Instruction};
|
||||
use super::pyobject::{PyObject, PyObjectPayload, PyResult};
|
||||
use super::pyobject::PyResult;
|
||||
use super::vm::VirtualMachine;
|
||||
use num_complex::Complex64;
|
||||
use rustpython_parser::{ast, parser};
|
||||
@@ -23,11 +23,11 @@ struct Compiler {
|
||||
pub fn compile(
|
||||
vm: &mut VirtualMachine,
|
||||
source: &str,
|
||||
mode: Mode,
|
||||
source_path: Option<String>,
|
||||
mode: &Mode,
|
||||
source_path: String,
|
||||
) -> PyResult {
|
||||
let mut compiler = Compiler::new();
|
||||
compiler.source_path = source_path.clone();
|
||||
compiler.source_path = Some(source_path.clone());
|
||||
compiler.push_new_code_object(source_path, "<module>".to_string());
|
||||
let syntax_error = vm.context().exceptions.syntax_error.clone();
|
||||
let result = match mode {
|
||||
@@ -45,17 +45,13 @@ pub fn compile(
|
||||
},
|
||||
};
|
||||
|
||||
match result {
|
||||
Err(msg) => return Err(vm.new_exception(syntax_error.clone(), msg)),
|
||||
_ => {}
|
||||
if let Err(msg) = result {
|
||||
return Err(vm.new_exception(syntax_error.clone(), msg));
|
||||
}
|
||||
|
||||
let code = compiler.pop_code_object();
|
||||
trace!("Compilation completed: {:?}", code);
|
||||
Ok(PyObject::new(
|
||||
PyObjectPayload::Code { code: code },
|
||||
vm.ctx.code_type(),
|
||||
))
|
||||
Ok(vm.ctx.new_code_object(code))
|
||||
}
|
||||
|
||||
pub enum Mode {
|
||||
@@ -82,13 +78,15 @@ impl Compiler {
|
||||
}
|
||||
}
|
||||
|
||||
fn push_new_code_object(&mut self, source_path: Option<String>, obj_name: String) {
|
||||
fn push_new_code_object(&mut self, source_path: String, obj_name: String) {
|
||||
let line_number = self.get_source_line_number();
|
||||
self.code_object_stack.push(CodeObject::new(
|
||||
Vec::new(),
|
||||
None,
|
||||
Vec::new(),
|
||||
None,
|
||||
source_path.clone(),
|
||||
line_number,
|
||||
obj_name,
|
||||
));
|
||||
}
|
||||
@@ -112,7 +110,7 @@ impl Compiler {
|
||||
|
||||
fn compile_program_single(&mut self, program: &ast::Program) -> Result<(), String> {
|
||||
for statement in &program.statements {
|
||||
if let &ast::Statement::Expression { ref expression } = &statement.node {
|
||||
if let ast::Statement::Expression { ref expression } = statement.node {
|
||||
self.compile_expression(expression)?;
|
||||
self.emit(Instruction::PrintExpr);
|
||||
} else {
|
||||
@@ -128,7 +126,7 @@ impl Compiler {
|
||||
|
||||
// Compile statement in eval mode:
|
||||
fn compile_statement_eval(&mut self, statement: &ast::LocatedStatement) -> Result<(), String> {
|
||||
if let &ast::Statement::Expression { ref expression } = &statement.node {
|
||||
if let ast::Statement::Expression { ref expression } = statement.node {
|
||||
self.compile_expression(expression)?;
|
||||
self.emit(Instruction::ReturnValue);
|
||||
Ok(())
|
||||
@@ -432,7 +430,7 @@ impl Compiler {
|
||||
|
||||
self.prepare_decorators(decorator_list)?;
|
||||
self.emit(Instruction::LoadConst {
|
||||
value: bytecode::Constant::Code { code: code },
|
||||
value: bytecode::Constant::Code { code },
|
||||
});
|
||||
self.emit(Instruction::LoadConst {
|
||||
value: bytecode::Constant::String {
|
||||
@@ -441,7 +439,7 @@ impl Compiler {
|
||||
});
|
||||
|
||||
// Turn code object into function object:
|
||||
self.emit(Instruction::MakeFunction { flags: flags });
|
||||
self.emit(Instruction::MakeFunction { flags });
|
||||
self.apply_decorators(decorator_list);
|
||||
|
||||
self.emit(Instruction::StoreName {
|
||||
@@ -457,12 +455,14 @@ impl Compiler {
|
||||
} => {
|
||||
self.prepare_decorators(decorator_list)?;
|
||||
self.emit(Instruction::LoadBuildClass);
|
||||
let line_number = self.get_source_line_number();
|
||||
self.code_object_stack.push(CodeObject::new(
|
||||
vec![String::from("__locals__")],
|
||||
None,
|
||||
vec![],
|
||||
None,
|
||||
self.source_path.clone(),
|
||||
self.source_path.clone().unwrap(),
|
||||
line_number,
|
||||
name.clone(),
|
||||
));
|
||||
self.emit(Instruction::LoadName {
|
||||
@@ -477,7 +477,7 @@ impl Compiler {
|
||||
|
||||
let code = self.pop_code_object();
|
||||
self.emit(Instruction::LoadConst {
|
||||
value: bytecode::Constant::Code { code: code },
|
||||
value: bytecode::Constant::Code { code },
|
||||
});
|
||||
self.emit(Instruction::LoadConst {
|
||||
value: bytecode::Constant::String {
|
||||
@@ -500,7 +500,7 @@ impl Compiler {
|
||||
self.compile_expression(base)?;
|
||||
}
|
||||
|
||||
if keywords.len() > 0 {
|
||||
if !keywords.is_empty() {
|
||||
let mut kwarg_names = vec![];
|
||||
for keyword in keywords {
|
||||
if let Some(name) = &keyword.name {
|
||||
@@ -592,7 +592,7 @@ impl Compiler {
|
||||
ast::Statement::Assign { targets, value } => {
|
||||
self.compile_expression(value)?;
|
||||
|
||||
for (i, target) in targets.into_iter().enumerate() {
|
||||
for (i, target) in targets.iter().enumerate() {
|
||||
if i + 1 != targets.len() {
|
||||
self.emit(Instruction::Duplicate);
|
||||
}
|
||||
@@ -627,7 +627,7 @@ impl Compiler {
|
||||
self.emit(Instruction::DeleteSubscript);
|
||||
}
|
||||
_ => {
|
||||
return Err(format!("Invalid delete statement"));
|
||||
return Err("Invalid delete statement".to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -641,10 +641,10 @@ impl Compiler {
|
||||
|
||||
fn enter_function(
|
||||
&mut self,
|
||||
name: &String,
|
||||
name: &str,
|
||||
args: &ast::Parameters,
|
||||
) -> Result<bytecode::FunctionOpArg, String> {
|
||||
let have_kwargs = args.defaults.len() > 0;
|
||||
let have_kwargs = !args.defaults.is_empty();
|
||||
if have_kwargs {
|
||||
// Construct a tuple:
|
||||
let size = args.defaults.len();
|
||||
@@ -657,31 +657,33 @@ impl Compiler {
|
||||
});
|
||||
}
|
||||
|
||||
let line_number = self.get_source_line_number();
|
||||
self.code_object_stack.push(CodeObject::new(
|
||||
args.args.clone(),
|
||||
args.vararg.clone(),
|
||||
args.kwonlyargs.clone(),
|
||||
args.kwarg.clone(),
|
||||
self.source_path.clone(),
|
||||
name.clone(),
|
||||
self.source_path.clone().unwrap(),
|
||||
line_number,
|
||||
name.to_string(),
|
||||
));
|
||||
|
||||
let mut flags = bytecode::FunctionOpArg::empty();
|
||||
if have_kwargs {
|
||||
flags = flags | bytecode::FunctionOpArg::HAS_DEFAULTS;
|
||||
flags |= bytecode::FunctionOpArg::HAS_DEFAULTS;
|
||||
}
|
||||
|
||||
Ok(flags)
|
||||
}
|
||||
|
||||
fn prepare_decorators(&mut self, decorator_list: &Vec<ast::Expression>) -> Result<(), String> {
|
||||
fn prepare_decorators(&mut self, decorator_list: &[ast::Expression]) -> Result<(), String> {
|
||||
for decorator in decorator_list {
|
||||
self.compile_expression(decorator)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn apply_decorators(&mut self, decorator_list: &Vec<ast::Expression>) {
|
||||
fn apply_decorators(&mut self, decorator_list: &[ast::Expression]) {
|
||||
// Apply decorators:
|
||||
for _ in decorator_list {
|
||||
self.emit(Instruction::CallFunction {
|
||||
@@ -780,7 +782,7 @@ impl Compiler {
|
||||
let f = false_label.unwrap_or_else(|| self.new_label());
|
||||
self.compile_test(a, None, Some(f), context)?;
|
||||
self.compile_test(b, true_label, false_label, context)?;
|
||||
if let None = false_label {
|
||||
if false_label.is_none() {
|
||||
self.set_label(f);
|
||||
}
|
||||
}
|
||||
@@ -788,7 +790,7 @@ impl Compiler {
|
||||
let t = true_label.unwrap_or_else(|| self.new_label());
|
||||
self.compile_test(a, Some(t), None, context)?;
|
||||
self.compile_test(b, true_label, false_label, context)?;
|
||||
if let None = true_label {
|
||||
if true_label.is_none() {
|
||||
self.set_label(t);
|
||||
}
|
||||
}
|
||||
@@ -838,21 +840,21 @@ impl Compiler {
|
||||
self.compile_test(expression, None, None, EvalContext::Expression)?
|
||||
}
|
||||
ast::Expression::Binop { a, op, b } => {
|
||||
self.compile_expression(&*a)?;
|
||||
self.compile_expression(&*b)?;
|
||||
self.compile_expression(a)?;
|
||||
self.compile_expression(b)?;
|
||||
|
||||
// Perform operation:
|
||||
self.compile_op(op);
|
||||
}
|
||||
ast::Expression::Subscript { a, b } => {
|
||||
self.compile_expression(&*a)?;
|
||||
self.compile_expression(&*b)?;
|
||||
self.compile_expression(a)?;
|
||||
self.compile_expression(b)?;
|
||||
self.emit(Instruction::BinaryOperation {
|
||||
op: bytecode::BinaryOperator::Subscript,
|
||||
});
|
||||
}
|
||||
ast::Expression::Unop { op, a } => {
|
||||
self.compile_expression(&*a)?;
|
||||
self.compile_expression(a)?;
|
||||
|
||||
// Perform operation:
|
||||
let i = match op {
|
||||
@@ -865,14 +867,14 @@ impl Compiler {
|
||||
self.emit(i);
|
||||
}
|
||||
ast::Expression::Attribute { value, name } => {
|
||||
self.compile_expression(&*value)?;
|
||||
self.compile_expression(value)?;
|
||||
self.emit(Instruction::LoadAttr {
|
||||
name: name.to_string(),
|
||||
});
|
||||
}
|
||||
ast::Expression::Compare { a, op, b } => {
|
||||
self.compile_expression(&*a)?;
|
||||
self.compile_expression(&*b)?;
|
||||
self.compile_expression(a)?;
|
||||
self.compile_expression(b)?;
|
||||
|
||||
let i = match op {
|
||||
ast::Comparison::Equal => bytecode::ComparisonOperator::Equal,
|
||||
@@ -905,7 +907,7 @@ impl Compiler {
|
||||
let size = elements.len();
|
||||
let must_unpack = self.gather_elements(elements)?;
|
||||
self.emit(Instruction::BuildList {
|
||||
size: size,
|
||||
size,
|
||||
unpack: must_unpack,
|
||||
});
|
||||
}
|
||||
@@ -913,7 +915,7 @@ impl Compiler {
|
||||
let size = elements.len();
|
||||
let must_unpack = self.gather_elements(elements)?;
|
||||
self.emit(Instruction::BuildTuple {
|
||||
size: size,
|
||||
size,
|
||||
unpack: must_unpack,
|
||||
});
|
||||
}
|
||||
@@ -921,7 +923,7 @@ impl Compiler {
|
||||
let size = elements.len();
|
||||
let must_unpack = self.gather_elements(elements)?;
|
||||
self.emit(Instruction::BuildSet {
|
||||
size: size,
|
||||
size,
|
||||
unpack: must_unpack,
|
||||
});
|
||||
}
|
||||
@@ -932,7 +934,7 @@ impl Compiler {
|
||||
self.compile_expression(value)?;
|
||||
}
|
||||
self.emit(Instruction::BuildMap {
|
||||
size: size,
|
||||
size,
|
||||
unpack: false,
|
||||
});
|
||||
}
|
||||
@@ -941,7 +943,7 @@ impl Compiler {
|
||||
for element in elements {
|
||||
self.compile_expression(element)?;
|
||||
}
|
||||
self.emit(Instruction::BuildSlice { size: size });
|
||||
self.emit(Instruction::BuildSlice { size });
|
||||
}
|
||||
ast::Expression::Yield { value } => {
|
||||
self.mark_generator();
|
||||
@@ -1003,13 +1005,13 @@ impl Compiler {
|
||||
self.emit(Instruction::ReturnValue);
|
||||
let code = self.pop_code_object();
|
||||
self.emit(Instruction::LoadConst {
|
||||
value: bytecode::Constant::Code { code: code },
|
||||
value: bytecode::Constant::Code { code },
|
||||
});
|
||||
self.emit(Instruction::LoadConst {
|
||||
value: bytecode::Constant::String { value: name },
|
||||
});
|
||||
// Turn code object into function object:
|
||||
self.emit(Instruction::MakeFunction { flags: flags });
|
||||
self.emit(Instruction::MakeFunction { flags });
|
||||
}
|
||||
ast::Expression::Comprehension { kind, generators } => {
|
||||
self.compile_comprehension(kind, generators)?;
|
||||
@@ -1036,8 +1038,8 @@ impl Compiler {
|
||||
fn compile_call(
|
||||
&mut self,
|
||||
function: &ast::Expression,
|
||||
args: &Vec<ast::Expression>,
|
||||
keywords: &Vec<ast::Keyword>,
|
||||
args: &[ast::Expression],
|
||||
keywords: &[ast::Keyword],
|
||||
) -> Result<(), String> {
|
||||
self.compile_expression(function)?;
|
||||
let count = args.len() + keywords.len();
|
||||
@@ -1054,7 +1056,7 @@ impl Compiler {
|
||||
});
|
||||
|
||||
// Create an optional map with kw-args:
|
||||
if keywords.len() > 0 {
|
||||
if !keywords.is_empty() {
|
||||
for keyword in keywords {
|
||||
if let Some(name) = &keyword.name {
|
||||
self.emit(Instruction::LoadConst {
|
||||
@@ -1090,7 +1092,7 @@ impl Compiler {
|
||||
}
|
||||
} else {
|
||||
// Keyword arguments:
|
||||
if keywords.len() > 0 {
|
||||
if !keywords.is_empty() {
|
||||
let mut kwarg_names = vec![];
|
||||
for keyword in keywords {
|
||||
if let Some(name) = &keyword.name {
|
||||
@@ -1123,7 +1125,7 @@ impl Compiler {
|
||||
|
||||
// Given a vector of expr / star expr generate code which gives either
|
||||
// a list of expressions on the stack, or a list of tuples.
|
||||
fn gather_elements(&mut self, elements: &Vec<ast::Expression>) -> Result<bool, String> {
|
||||
fn gather_elements(&mut self, elements: &[ast::Expression]) -> Result<bool, String> {
|
||||
// First determine if we have starred elements:
|
||||
let has_stars = elements.iter().any(|e| {
|
||||
if let ast::Expression::Starred { .. } = e {
|
||||
@@ -1153,10 +1155,10 @@ impl Compiler {
|
||||
fn compile_comprehension(
|
||||
&mut self,
|
||||
kind: &ast::ComprehensionKind,
|
||||
generators: &Vec<ast::Comprehension>,
|
||||
generators: &[ast::Comprehension],
|
||||
) -> Result<(), String> {
|
||||
// We must have at least one generator:
|
||||
assert!(generators.len() > 0);
|
||||
assert!(!generators.is_empty());
|
||||
|
||||
let name = match kind {
|
||||
ast::ComprehensionKind::GeneratorExpression { .. } => "<genexpr>",
|
||||
@@ -1166,13 +1168,15 @@ impl Compiler {
|
||||
}
|
||||
.to_string();
|
||||
|
||||
let line_number = self.get_source_line_number();
|
||||
// Create magnificent function <listcomp>:
|
||||
self.code_object_stack.push(CodeObject::new(
|
||||
vec![".0".to_string()],
|
||||
None,
|
||||
vec![],
|
||||
None,
|
||||
self.source_path.clone(),
|
||||
self.source_path.clone().unwrap(),
|
||||
line_number,
|
||||
name.clone(),
|
||||
));
|
||||
|
||||
@@ -1201,8 +1205,7 @@ impl Compiler {
|
||||
|
||||
let mut loop_labels = vec![];
|
||||
for generator in generators {
|
||||
let first = loop_labels.len() == 0;
|
||||
if first {
|
||||
if loop_labels.is_empty() {
|
||||
// Load iterator onto stack (passed as first argument):
|
||||
self.emit(Instruction::LoadName {
|
||||
name: String::from(".0"),
|
||||
@@ -1287,7 +1290,7 @@ impl Compiler {
|
||||
|
||||
// List comprehension code:
|
||||
self.emit(Instruction::LoadConst {
|
||||
value: bytecode::Constant::Code { code: code },
|
||||
value: bytecode::Constant::Code { code },
|
||||
});
|
||||
|
||||
// List comprehension function name:
|
||||
@@ -1343,6 +1346,10 @@ impl Compiler {
|
||||
self.current_source_location = location.clone();
|
||||
}
|
||||
|
||||
fn get_source_line_number(&mut self) -> usize {
|
||||
self.current_source_location.get_row()
|
||||
}
|
||||
|
||||
fn mark_generator(&mut self) {
|
||||
self.current_code_object().is_generator = true;
|
||||
}
|
||||
@@ -1357,7 +1364,7 @@ mod tests {
|
||||
use rustpython_parser::parser;
|
||||
fn compile_exec(source: &str) -> CodeObject {
|
||||
let mut compiler = Compiler::new();
|
||||
compiler.push_new_code_object(Option::None, "<module>".to_string());
|
||||
compiler.push_new_code_object("source_path".to_string(), "<module>".to_string());
|
||||
let ast = parser::parse_program(&source.to_string()).unwrap();
|
||||
compiler.compile_program(&ast).unwrap();
|
||||
compiler.pop_code_object()
|
||||
|
||||
230
vm/src/dictdatatype.rs
Normal file
230
vm/src/dictdatatype.rs
Normal file
@@ -0,0 +1,230 @@
|
||||
use super::obj::objbool;
|
||||
use super::obj::objint;
|
||||
use super::pyobject::{IdProtocol, PyObjectRef, PyResult};
|
||||
use super::vm::VirtualMachine;
|
||||
use num_traits::ToPrimitive;
|
||||
/// Ordered dictionary implementation.
|
||||
/// Inspired by: https://morepypy.blogspot.com/2015/01/faster-more-memory-efficient-and-more.html
|
||||
/// And: https://www.youtube.com/watch?v=p33CVV29OG8
|
||||
/// And: http://code.activestate.com/recipes/578375/
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub struct Dict {
|
||||
size: usize,
|
||||
indices: HashMap<usize, usize>,
|
||||
entries: Vec<Option<DictEntry>>,
|
||||
}
|
||||
|
||||
struct DictEntry {
|
||||
hash: usize,
|
||||
key: PyObjectRef,
|
||||
value: PyObjectRef,
|
||||
}
|
||||
|
||||
impl Dict {
|
||||
pub fn new() -> Self {
|
||||
Dict {
|
||||
size: 0,
|
||||
indices: HashMap::new(),
|
||||
entries: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Store a key
|
||||
pub fn insert(
|
||||
&mut self,
|
||||
vm: &mut VirtualMachine,
|
||||
key: &PyObjectRef,
|
||||
value: PyObjectRef,
|
||||
) -> Result<(), PyObjectRef> {
|
||||
match self.lookup(vm, key)? {
|
||||
LookupResult::Existing(index) => {
|
||||
// Update existing key
|
||||
if let Some(ref mut entry) = self.entries[index] {
|
||||
entry.value = value;
|
||||
Ok(())
|
||||
} else {
|
||||
panic!("Lookup returned invalid index into entries!");
|
||||
}
|
||||
}
|
||||
LookupResult::NewIndex {
|
||||
hash_index,
|
||||
hash_value,
|
||||
} => {
|
||||
// New key:
|
||||
let entry = DictEntry {
|
||||
hash: hash_value,
|
||||
key: key.clone(),
|
||||
value,
|
||||
};
|
||||
let index = self.entries.len();
|
||||
self.entries.push(Some(entry));
|
||||
self.indices.insert(hash_index, index);
|
||||
self.size += 1;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn contains(
|
||||
&self,
|
||||
vm: &mut VirtualMachine,
|
||||
key: &PyObjectRef,
|
||||
) -> Result<bool, PyObjectRef> {
|
||||
if let LookupResult::Existing(_index) = self.lookup(vm, key)? {
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve a key
|
||||
pub fn get(&self, vm: &mut VirtualMachine, key: &PyObjectRef) -> PyResult {
|
||||
if let LookupResult::Existing(index) = self.lookup(vm, key)? {
|
||||
if let Some(entry) = &self.entries[index] {
|
||||
Ok(entry.value.clone())
|
||||
} else {
|
||||
panic!("Lookup returned invalid index into entries!");
|
||||
}
|
||||
} else {
|
||||
let key_repr = vm.to_pystr(key)?;
|
||||
Err(vm.new_value_error(format!("Key not found: {}", key_repr)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete a key
|
||||
pub fn delete(
|
||||
&mut self,
|
||||
vm: &mut VirtualMachine,
|
||||
key: &PyObjectRef,
|
||||
) -> Result<(), PyObjectRef> {
|
||||
if let LookupResult::Existing(index) = self.lookup(vm, key)? {
|
||||
self.entries[index] = None;
|
||||
self.size -= 1;
|
||||
Ok(())
|
||||
} else {
|
||||
let key_repr = vm.to_pystr(key)?;
|
||||
Err(vm.new_value_error(format!("Key not found: {}", key_repr)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.size
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
pub fn get_items(&self) -> Vec<(PyObjectRef, PyObjectRef)> {
|
||||
self.entries
|
||||
.iter()
|
||||
.filter(|e| e.is_some())
|
||||
.map(|e| e.as_ref().unwrap())
|
||||
.map(|e| (e.key.clone(), e.value.clone()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Lookup the index for the given key.
|
||||
fn lookup(
|
||||
&self,
|
||||
vm: &mut VirtualMachine,
|
||||
key: &PyObjectRef,
|
||||
) -> Result<LookupResult, PyObjectRef> {
|
||||
let hash_value = calc_hash(vm, key)?;
|
||||
let perturb = hash_value;
|
||||
let mut hash_index: usize = hash_value;
|
||||
loop {
|
||||
if self.indices.contains_key(&hash_index) {
|
||||
// Now we have an index, lets check the key.
|
||||
let index = self.indices[&hash_index];
|
||||
if let Some(entry) = &self.entries[index] {
|
||||
// Okay, we have an entry at this place
|
||||
if entry.key.is(key) {
|
||||
// Literally the same object
|
||||
break Ok(LookupResult::Existing(index));
|
||||
} else if entry.hash == hash_value {
|
||||
if do_eq(vm, &entry.key, key)? {
|
||||
break Ok(LookupResult::Existing(index));
|
||||
} else {
|
||||
// entry mismatch.
|
||||
}
|
||||
} else {
|
||||
// entry mismatch.
|
||||
}
|
||||
} else {
|
||||
// Removed entry, continue search...
|
||||
}
|
||||
} else {
|
||||
// Hash not in table, we are at free slot now.
|
||||
break Ok(LookupResult::NewIndex {
|
||||
hash_value,
|
||||
hash_index,
|
||||
});
|
||||
}
|
||||
|
||||
// Update i to next probe location:
|
||||
hash_index = hash_index
|
||||
.wrapping_mul(5)
|
||||
.wrapping_add(perturb)
|
||||
.wrapping_add(1);
|
||||
// warn!("Perturb value: {}", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum LookupResult {
|
||||
NewIndex {
|
||||
hash_value: usize,
|
||||
hash_index: usize,
|
||||
}, // return not found, index into indices
|
||||
Existing(usize), // Existing record, index into entries
|
||||
}
|
||||
|
||||
fn calc_hash(vm: &mut VirtualMachine, key: &PyObjectRef) -> Result<usize, PyObjectRef> {
|
||||
let hash = vm.call_method(key, "__hash__", vec![])?;
|
||||
Ok(objint::get_value(&hash).to_usize().unwrap())
|
||||
}
|
||||
|
||||
/// Invoke __eq__ on two keys
|
||||
fn do_eq(
|
||||
vm: &mut VirtualMachine,
|
||||
key1: &PyObjectRef,
|
||||
key2: &PyObjectRef,
|
||||
) -> Result<bool, PyObjectRef> {
|
||||
let result = vm._eq(key1, key2.clone())?;
|
||||
Ok(objbool::get_value(&result))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Dict, VirtualMachine};
|
||||
|
||||
#[test]
|
||||
fn test_insert() {
|
||||
let mut vm = VirtualMachine::new();
|
||||
let mut dict = Dict::new();
|
||||
assert_eq!(0, dict.len());
|
||||
|
||||
let key1 = vm.new_bool(true);
|
||||
let value1 = vm.new_str("abc".to_string());
|
||||
dict.insert(&mut vm, &key1, value1.clone()).unwrap();
|
||||
assert_eq!(1, dict.len());
|
||||
|
||||
let key2 = vm.new_str("x".to_string());
|
||||
let value2 = vm.new_str("def".to_string());
|
||||
dict.insert(&mut vm, &key2, value2.clone()).unwrap();
|
||||
assert_eq!(2, dict.len());
|
||||
|
||||
dict.insert(&mut vm, &key1, value2.clone()).unwrap();
|
||||
assert_eq!(2, dict.len());
|
||||
|
||||
dict.delete(&mut vm, &key1).unwrap();
|
||||
assert_eq!(1, dict.len());
|
||||
|
||||
dict.insert(&mut vm, &key1, value2).unwrap();
|
||||
assert_eq!(2, dict.len());
|
||||
|
||||
assert_eq!(true, dict.contains(&mut vm, &key1).unwrap());
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,13 @@ use super::compile;
|
||||
use super::pyobject::{PyObjectRef, PyResult};
|
||||
use super::vm::VirtualMachine;
|
||||
|
||||
pub fn eval(vm: &mut VirtualMachine, source: &str, scope: PyObjectRef) -> PyResult {
|
||||
match compile::compile(vm, source, compile::Mode::Eval, None) {
|
||||
pub fn eval(
|
||||
vm: &mut VirtualMachine,
|
||||
source: &str,
|
||||
scope: PyObjectRef,
|
||||
source_path: &str,
|
||||
) -> PyResult {
|
||||
match compile::compile(vm, source, &compile::Mode::Eval, source_path.to_string()) {
|
||||
Ok(bytecode) => {
|
||||
debug!("Code object: {:?}", bytecode);
|
||||
vm.run_code_obj(bytecode, scope)
|
||||
@@ -24,7 +29,7 @@ mod tests {
|
||||
let source = String::from("print('Hello world')\n");
|
||||
let mut vm = VirtualMachine::new();
|
||||
let vars = vm.context().new_scope(None);
|
||||
let _result = eval(&mut vm, &source, vars);
|
||||
let _result = eval(&mut vm, &source, vars, "<unittest>");
|
||||
|
||||
// TODO: check result?
|
||||
//assert_eq!(
|
||||
|
||||
@@ -81,21 +81,27 @@ fn exception_str(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ExceptionZoo {
|
||||
pub base_exception_type: PyObjectRef,
|
||||
pub exception_type: PyObjectRef,
|
||||
pub syntax_error: PyObjectRef,
|
||||
pub arithmetic_error: PyObjectRef,
|
||||
pub assertion_error: PyObjectRef,
|
||||
pub attribute_error: PyObjectRef,
|
||||
pub base_exception_type: PyObjectRef,
|
||||
pub exception_type: PyObjectRef,
|
||||
pub file_not_found_error: PyObjectRef,
|
||||
pub import_error: PyObjectRef,
|
||||
pub index_error: PyObjectRef,
|
||||
pub key_error: PyObjectRef,
|
||||
pub module_not_found_error: PyObjectRef,
|
||||
pub name_error: PyObjectRef,
|
||||
pub runtime_error: PyObjectRef,
|
||||
pub not_implemented_error: PyObjectRef,
|
||||
pub os_error: PyObjectRef,
|
||||
pub overflow_error: PyObjectRef,
|
||||
pub permission_error: PyObjectRef,
|
||||
pub runtime_error: PyObjectRef,
|
||||
pub stop_iteration: PyObjectRef,
|
||||
pub syntax_error: PyObjectRef,
|
||||
pub type_error: PyObjectRef,
|
||||
pub value_error: PyObjectRef,
|
||||
pub import_error: PyObjectRef,
|
||||
pub module_not_found_error: PyObjectRef,
|
||||
pub zero_division_error: PyObjectRef,
|
||||
}
|
||||
|
||||
impl ExceptionZoo {
|
||||
@@ -104,69 +110,86 @@ impl ExceptionZoo {
|
||||
object_type: &PyObjectRef,
|
||||
dict_type: &PyObjectRef,
|
||||
) -> Self {
|
||||
// Sorted By Hierarchy then alphabetized.
|
||||
let base_exception_type =
|
||||
create_type("BaseException", &type_type, &object_type, &dict_type);
|
||||
|
||||
let exception_type = create_type("Exception", &type_type, &base_exception_type, &dict_type);
|
||||
let syntax_error = create_type("SyntaxError", &type_type, &exception_type, &dict_type);
|
||||
|
||||
let arithmetic_error =
|
||||
create_type("ArithmeticError", &type_type, &exception_type, &dict_type);
|
||||
let assertion_error =
|
||||
create_type("AssertionError", &type_type, &exception_type, &dict_type);
|
||||
let attribute_error = create_type(
|
||||
"AttributeError",
|
||||
&type_type,
|
||||
&exception_type.clone(),
|
||||
&dict_type,
|
||||
);
|
||||
let index_error = create_type(
|
||||
"IndexError",
|
||||
&type_type,
|
||||
&exception_type.clone(),
|
||||
&dict_type,
|
||||
);
|
||||
let key_error = create_type("KeyError", &type_type, &exception_type.clone(), &dict_type);
|
||||
let name_error = create_type("NameError", &type_type, &exception_type.clone(), &dict_type);
|
||||
let attribute_error =
|
||||
create_type("AttributeError", &type_type, &exception_type, &dict_type);
|
||||
let import_error = create_type("ImportError", &type_type, &exception_type, &dict_type);
|
||||
let index_error = create_type("IndexError", &type_type, &exception_type, &dict_type);
|
||||
let key_error = create_type("KeyError", &type_type, &exception_type, &dict_type);
|
||||
let name_error = create_type("NameError", &type_type, &exception_type, &dict_type);
|
||||
let os_error = create_type("OSError", &type_type, &exception_type, &dict_type);
|
||||
let runtime_error = create_type("RuntimeError", &type_type, &exception_type, &dict_type);
|
||||
let stop_iteration = create_type("StopIteration", &type_type, &exception_type, &dict_type);
|
||||
let syntax_error = create_type("SyntaxError", &type_type, &exception_type, &dict_type);
|
||||
let type_error = create_type("TypeError", &type_type, &exception_type, &dict_type);
|
||||
let value_error = create_type("ValueError", &type_type, &exception_type, &dict_type);
|
||||
|
||||
let overflow_error =
|
||||
create_type("OverflowError", &type_type, &arithmetic_error, &dict_type);
|
||||
let zero_division_error = create_type(
|
||||
"ZeroDivisionError",
|
||||
&type_type,
|
||||
&arithmetic_error,
|
||||
&dict_type,
|
||||
);
|
||||
|
||||
let module_not_found_error =
|
||||
create_type("ModuleNotFoundError", &type_type, &import_error, &dict_type);
|
||||
|
||||
let not_implemented_error = create_type(
|
||||
"NotImplementedError",
|
||||
&type_type,
|
||||
&runtime_error,
|
||||
&dict_type,
|
||||
);
|
||||
let stop_iteration = create_type("StopIteration", &type_type, &exception_type, &dict_type);
|
||||
let type_error = create_type("TypeError", &type_type, &exception_type, &dict_type);
|
||||
let value_error = create_type("ValueError", &type_type, &exception_type, &dict_type);
|
||||
let import_error = create_type("ImportError", &type_type, &exception_type, &dict_type);
|
||||
let module_not_found_error =
|
||||
create_type("ModuleNotFoundError", &type_type, &import_error, &dict_type);
|
||||
|
||||
let file_not_found_error =
|
||||
create_type("FileNotFoundError", &type_type, &os_error, &dict_type);
|
||||
let permission_error = create_type("PermissionError", &type_type, &os_error, &dict_type);
|
||||
|
||||
ExceptionZoo {
|
||||
base_exception_type: base_exception_type,
|
||||
exception_type: exception_type,
|
||||
syntax_error: syntax_error,
|
||||
assertion_error: assertion_error,
|
||||
attribute_error: attribute_error,
|
||||
index_error: index_error,
|
||||
key_error: key_error,
|
||||
name_error: name_error,
|
||||
runtime_error: runtime_error,
|
||||
not_implemented_error: not_implemented_error,
|
||||
stop_iteration: stop_iteration,
|
||||
type_error: type_error,
|
||||
value_error: value_error,
|
||||
import_error: import_error,
|
||||
module_not_found_error: module_not_found_error,
|
||||
arithmetic_error,
|
||||
assertion_error,
|
||||
attribute_error,
|
||||
base_exception_type,
|
||||
exception_type,
|
||||
file_not_found_error,
|
||||
import_error,
|
||||
index_error,
|
||||
key_error,
|
||||
module_not_found_error,
|
||||
name_error,
|
||||
not_implemented_error,
|
||||
os_error,
|
||||
overflow_error,
|
||||
permission_error,
|
||||
runtime_error,
|
||||
stop_iteration,
|
||||
syntax_error,
|
||||
type_error,
|
||||
value_error,
|
||||
zero_division_error,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
let ref base_exception_type = context.exceptions.base_exception_type;
|
||||
let base_exception_type = &context.exceptions.base_exception_type;
|
||||
context.set_attr(
|
||||
&base_exception_type,
|
||||
"__init__",
|
||||
context.new_rustfunc(exception_init),
|
||||
);
|
||||
let ref exception_type = context.exceptions.exception_type;
|
||||
let exception_type = &context.exceptions.exception_type;
|
||||
context.set_attr(
|
||||
&exception_type,
|
||||
"__str__",
|
||||
|
||||
@@ -87,7 +87,7 @@ fn parse_align(text: &str) -> (Option<FormatAlign>, &str) {
|
||||
|
||||
fn parse_fill_and_align(text: &str) -> (Option<char>, Option<FormatAlign>, &str) {
|
||||
let char_indices: Vec<(usize, char)> = text.char_indices().take(3).collect();
|
||||
if char_indices.len() == 0 {
|
||||
if char_indices.is_empty() {
|
||||
(None, None, text)
|
||||
} else if char_indices.len() == 1 {
|
||||
let (maybe_align, remaining) = parse_align(text);
|
||||
@@ -314,7 +314,7 @@ impl FormatSpec {
|
||||
}
|
||||
None => Ok(magnitude.to_str_radix(10)),
|
||||
};
|
||||
if !raw_magnitude_string_result.is_ok() {
|
||||
if raw_magnitude_string_result.is_err() {
|
||||
return raw_magnitude_string_result;
|
||||
}
|
||||
let magnitude_string = format!(
|
||||
@@ -438,14 +438,14 @@ impl FormatString {
|
||||
fn parse_literal(text: &str) -> Result<(FormatPart, &str), FormatParseError> {
|
||||
let mut cur_text = text;
|
||||
let mut result_string = String::new();
|
||||
while cur_text.len() > 0 {
|
||||
while !cur_text.is_empty() {
|
||||
match FormatString::parse_literal_single(cur_text) {
|
||||
Ok((next_char, remaining)) => {
|
||||
result_string.push(next_char);
|
||||
cur_text = remaining;
|
||||
}
|
||||
Err(err) => {
|
||||
if result_string.len() > 0 {
|
||||
if !result_string.is_empty() {
|
||||
return Ok((FormatPart::Literal(result_string.to_string()), cur_text));
|
||||
} else {
|
||||
return Err(err);
|
||||
@@ -467,7 +467,7 @@ impl FormatString {
|
||||
String::new()
|
||||
};
|
||||
|
||||
if arg_part.len() == 0 {
|
||||
if arg_part.is_empty() {
|
||||
return Ok(FormatPart::AutoSpec(format_spec));
|
||||
}
|
||||
|
||||
@@ -500,7 +500,7 @@ impl FormatString {
|
||||
pub fn from_str(text: &str) -> Result<FormatString, FormatParseError> {
|
||||
let mut cur_text: &str = text;
|
||||
let mut parts: Vec<FormatPart> = Vec::new();
|
||||
while cur_text.len() > 0 {
|
||||
while !cur_text.is_empty() {
|
||||
// Try to parse both literals and bracketed format parts util we
|
||||
// run out of text
|
||||
cur_text = FormatString::parse_literal(cur_text)
|
||||
|
||||
135
vm/src/frame.rs
135
vm/src/frame.rs
@@ -20,8 +20,7 @@ use super::pyobject::{
|
||||
PyResult, TypeProtocol,
|
||||
};
|
||||
use super::vm::VirtualMachine;
|
||||
use num_bigint::ToBigInt;
|
||||
use num_traits::ToPrimitive;
|
||||
use num_bigint::BigInt;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum Block {
|
||||
@@ -71,12 +70,12 @@ impl Frame {
|
||||
// locals.extend(callargs);
|
||||
|
||||
Frame {
|
||||
code: objcode::copy_code(&code),
|
||||
code: objcode::get_value(&code),
|
||||
stack: vec![],
|
||||
blocks: vec![],
|
||||
// save the callargs as locals
|
||||
// globals: locals.clone(),
|
||||
locals: locals,
|
||||
locals,
|
||||
lasti: 0,
|
||||
}
|
||||
}
|
||||
@@ -89,11 +88,7 @@ impl Frame {
|
||||
}
|
||||
|
||||
pub fn run_frame(&mut self, vm: &mut VirtualMachine) -> Result<ExecutionResult, PyObjectRef> {
|
||||
let filename = if let Some(source_path) = &self.code.source_path {
|
||||
source_path.to_string()
|
||||
} else {
|
||||
"<unknown>".to_string()
|
||||
};
|
||||
let filename = &self.code.source_path.to_string();
|
||||
|
||||
let prev_frame = mem::replace(&mut vm.current_frame, Some(vm.ctx.new_frame(self.clone())));
|
||||
|
||||
@@ -121,7 +116,7 @@ impl Frame {
|
||||
trace!("Adding to traceback: {:?} {:?}", traceback, lineno);
|
||||
let pos = vm.ctx.new_tuple(vec![
|
||||
vm.ctx.new_str(filename.clone()),
|
||||
vm.ctx.new_int(lineno.get_row().to_bigint().unwrap()),
|
||||
vm.ctx.new_int(lineno.get_row()),
|
||||
vm.ctx.new_str(run_obj_name.clone()),
|
||||
]);
|
||||
objlist::list_append(
|
||||
@@ -263,22 +258,22 @@ impl Frame {
|
||||
assert!(*size == 2 || *size == 3);
|
||||
let elements = self.pop_multiple(*size);
|
||||
|
||||
let mut out: Vec<Option<i32>> = elements
|
||||
let mut out: Vec<Option<BigInt>> = elements
|
||||
.into_iter()
|
||||
.map(|x| match x.borrow().payload {
|
||||
PyObjectPayload::Integer { ref value } => Some(value.to_i32().unwrap()),
|
||||
PyObjectPayload::Integer { ref value } => Some(value.clone()),
|
||||
PyObjectPayload::None => None,
|
||||
_ => panic!("Expect Int or None as BUILD_SLICE arguments, got {:?}", x),
|
||||
})
|
||||
.collect();
|
||||
|
||||
let start = out[0];
|
||||
let stop = out[1];
|
||||
let step = if out.len() == 3 { out[2] } else { None };
|
||||
let start = out[0].take();
|
||||
let stop = out[1].take();
|
||||
let step = if out.len() == 3 { out[2].take() } else { None };
|
||||
|
||||
let obj = PyObject::new(
|
||||
PyObjectPayload::Slice { start, stop, step },
|
||||
vm.ctx.type_type(),
|
||||
vm.ctx.slice_type(),
|
||||
);
|
||||
self.push_value(obj);
|
||||
Ok(None)
|
||||
@@ -440,7 +435,7 @@ impl Frame {
|
||||
bytecode::CallType::Positional(count) => {
|
||||
let args: Vec<PyObjectRef> = self.pop_multiple(*count);
|
||||
PyFuncArgs {
|
||||
args: args,
|
||||
args,
|
||||
kwargs: vec![],
|
||||
}
|
||||
}
|
||||
@@ -504,7 +499,7 @@ impl Frame {
|
||||
let exception = match argc {
|
||||
1 => self.pop_value(),
|
||||
0 | 2 | 3 => panic!("Not implemented!"),
|
||||
_ => panic!("Invalid paramter for RAISE_VARARGS, must be between 0 to 3"),
|
||||
_ => panic!("Invalid parameter for RAISE_VARARGS, must be between 0 to 3"),
|
||||
};
|
||||
if objtype::isinstance(&exception, &vm.ctx.exceptions.base_exception_type) {
|
||||
info!("Exception raised: {:?}", exception);
|
||||
@@ -522,7 +517,7 @@ impl Frame {
|
||||
|
||||
bytecode::Instruction::Break => {
|
||||
let block = self.unwind_loop(vm);
|
||||
if let Block::Loop { start: _, end } = block {
|
||||
if let Block::Loop { end, .. } = block {
|
||||
self.jump(end);
|
||||
}
|
||||
Ok(None)
|
||||
@@ -533,7 +528,7 @@ impl Frame {
|
||||
}
|
||||
bytecode::Instruction::Continue => {
|
||||
let block = self.unwind_loop(vm);
|
||||
if let Block::Loop { start, end: _ } = block {
|
||||
if let Block::Loop { start, .. } = block {
|
||||
self.jump(start);
|
||||
} else {
|
||||
assert!(false);
|
||||
@@ -611,7 +606,7 @@ impl Frame {
|
||||
.iter()
|
||||
.skip(*before)
|
||||
.take(middle)
|
||||
.map(|x| x.clone())
|
||||
.cloned()
|
||||
.collect();
|
||||
let t = vm.ctx.new_list(middle_elements);
|
||||
self.push_value(t);
|
||||
@@ -662,13 +657,10 @@ impl Frame {
|
||||
module: &str,
|
||||
symbol: &Option<String>,
|
||||
) -> FrameResult {
|
||||
let current_path = match &self.code.source_path {
|
||||
Some(source_path) => {
|
||||
let mut source_pathbuf = PathBuf::from(source_path);
|
||||
source_pathbuf.pop();
|
||||
source_pathbuf
|
||||
}
|
||||
None => PathBuf::from("."),
|
||||
let current_path = {
|
||||
let mut source_pathbuf = PathBuf::from(&self.code.source_path);
|
||||
source_pathbuf.pop();
|
||||
source_pathbuf
|
||||
};
|
||||
|
||||
let obj = import(vm, current_path, module, symbol)?;
|
||||
@@ -679,13 +671,10 @@ impl Frame {
|
||||
}
|
||||
|
||||
fn import_star(&mut self, vm: &mut VirtualMachine, module: &str) -> FrameResult {
|
||||
let current_path = match &self.code.source_path {
|
||||
Some(source_path) => {
|
||||
let mut source_pathbuf = PathBuf::from(source_path);
|
||||
source_pathbuf.pop();
|
||||
source_pathbuf
|
||||
}
|
||||
None => PathBuf::from("."),
|
||||
let current_path = {
|
||||
let mut source_pathbuf = PathBuf::from(&self.code.source_path);
|
||||
source_pathbuf.pop();
|
||||
source_pathbuf
|
||||
};
|
||||
|
||||
// Grab all the names from the module and put them in the context
|
||||
@@ -708,8 +697,7 @@ impl Frame {
|
||||
// TODO: execute finally handler
|
||||
}
|
||||
Some(Block::With {
|
||||
end: _,
|
||||
context_manager,
|
||||
context_manager, ..
|
||||
}) => {
|
||||
match self.with_exit(vm, &context_manager, None) {
|
||||
Ok(..) => {}
|
||||
@@ -728,13 +716,12 @@ impl Frame {
|
||||
loop {
|
||||
let block = self.pop_block();
|
||||
match block {
|
||||
Some(Block::Loop { start: _, end: __ }) => break block.unwrap(),
|
||||
Some(Block::Loop { .. }) => break block.unwrap(),
|
||||
Some(Block::TryExcept { .. }) => {
|
||||
// TODO: execute finally handler
|
||||
}
|
||||
Some(Block::With {
|
||||
end: _,
|
||||
context_manager,
|
||||
context_manager, ..
|
||||
}) => match self.with_exit(vm, &context_manager, None) {
|
||||
Ok(..) => {}
|
||||
Err(exc) => {
|
||||
@@ -889,25 +876,25 @@ impl Frame {
|
||||
) -> FrameResult {
|
||||
let b_ref = self.pop_value();
|
||||
let a_ref = self.pop_value();
|
||||
let value = match op {
|
||||
&bytecode::BinaryOperator::Subtract => vm._sub(a_ref, b_ref),
|
||||
&bytecode::BinaryOperator::Add => vm._add(a_ref, b_ref),
|
||||
&bytecode::BinaryOperator::Multiply => vm._mul(a_ref, b_ref),
|
||||
&bytecode::BinaryOperator::MatrixMultiply => {
|
||||
let value = match *op {
|
||||
bytecode::BinaryOperator::Subtract => vm._sub(a_ref, b_ref),
|
||||
bytecode::BinaryOperator::Add => vm._add(a_ref, b_ref),
|
||||
bytecode::BinaryOperator::Multiply => vm._mul(a_ref, b_ref),
|
||||
bytecode::BinaryOperator::MatrixMultiply => {
|
||||
vm.call_method(&a_ref, "__matmul__", vec![b_ref])
|
||||
}
|
||||
&bytecode::BinaryOperator::Power => vm._pow(a_ref, b_ref),
|
||||
&bytecode::BinaryOperator::Divide => vm._div(a_ref, b_ref),
|
||||
&bytecode::BinaryOperator::FloorDivide => {
|
||||
bytecode::BinaryOperator::Power => vm._pow(a_ref, b_ref),
|
||||
bytecode::BinaryOperator::Divide => vm._div(a_ref, b_ref),
|
||||
bytecode::BinaryOperator::FloorDivide => {
|
||||
vm.call_method(&a_ref, "__floordiv__", vec![b_ref])
|
||||
}
|
||||
&bytecode::BinaryOperator::Subscript => self.subscript(vm, a_ref, b_ref),
|
||||
&bytecode::BinaryOperator::Modulo => vm._modulo(a_ref, b_ref),
|
||||
&bytecode::BinaryOperator::Lshift => vm.call_method(&a_ref, "__lshift__", vec![b_ref]),
|
||||
&bytecode::BinaryOperator::Rshift => vm.call_method(&a_ref, "__rshift__", vec![b_ref]),
|
||||
&bytecode::BinaryOperator::Xor => vm._xor(a_ref, b_ref),
|
||||
&bytecode::BinaryOperator::Or => vm._or(a_ref, b_ref),
|
||||
&bytecode::BinaryOperator::And => vm._and(a_ref, b_ref),
|
||||
bytecode::BinaryOperator::Subscript => self.subscript(vm, a_ref, b_ref),
|
||||
bytecode::BinaryOperator::Modulo => vm._modulo(a_ref, b_ref),
|
||||
bytecode::BinaryOperator::Lshift => vm.call_method(&a_ref, "__lshift__", vec![b_ref]),
|
||||
bytecode::BinaryOperator::Rshift => vm.call_method(&a_ref, "__rshift__", vec![b_ref]),
|
||||
bytecode::BinaryOperator::Xor => vm._xor(a_ref, b_ref),
|
||||
bytecode::BinaryOperator::Or => vm._or(a_ref, b_ref),
|
||||
bytecode::BinaryOperator::And => vm._and(a_ref, b_ref),
|
||||
}?;
|
||||
|
||||
self.push_value(value);
|
||||
@@ -920,11 +907,11 @@ impl Frame {
|
||||
op: &bytecode::UnaryOperator,
|
||||
) -> FrameResult {
|
||||
let a = self.pop_value();
|
||||
let value = match op {
|
||||
&bytecode::UnaryOperator::Minus => vm.call_method(&a, "__neg__", vec![])?,
|
||||
&bytecode::UnaryOperator::Plus => vm.call_method(&a, "__pos__", vec![])?,
|
||||
&bytecode::UnaryOperator::Invert => vm.call_method(&a, "__invert__", vec![])?,
|
||||
&bytecode::UnaryOperator::Not => {
|
||||
let value = match *op {
|
||||
bytecode::UnaryOperator::Minus => vm.call_method(&a, "__neg__", vec![])?,
|
||||
bytecode::UnaryOperator::Plus => vm.call_method(&a, "__pos__", vec![])?,
|
||||
bytecode::UnaryOperator::Invert => vm.call_method(&a, "__invert__", vec![])?,
|
||||
bytecode::UnaryOperator::Not => {
|
||||
let value = objbool::boolval(vm, a)?;
|
||||
vm.ctx.new_bool(!value)
|
||||
}
|
||||
@@ -997,17 +984,17 @@ impl Frame {
|
||||
) -> FrameResult {
|
||||
let b = self.pop_value();
|
||||
let a = self.pop_value();
|
||||
let value = match op {
|
||||
&bytecode::ComparisonOperator::Equal => vm._eq(&a, b)?,
|
||||
&bytecode::ComparisonOperator::NotEqual => vm._ne(&a, b)?,
|
||||
&bytecode::ComparisonOperator::Less => vm._lt(&a, b)?,
|
||||
&bytecode::ComparisonOperator::LessOrEqual => vm._le(&a, b)?,
|
||||
&bytecode::ComparisonOperator::Greater => vm._gt(&a, b)?,
|
||||
&bytecode::ComparisonOperator::GreaterOrEqual => vm._ge(&a, b)?,
|
||||
&bytecode::ComparisonOperator::Is => vm.ctx.new_bool(self._is(a, b)),
|
||||
&bytecode::ComparisonOperator::IsNot => self._is_not(vm, a, b)?,
|
||||
&bytecode::ComparisonOperator::In => self._in(vm, a, b)?,
|
||||
&bytecode::ComparisonOperator::NotIn => self._not_in(vm, a, b)?,
|
||||
let value = match *op {
|
||||
bytecode::ComparisonOperator::Equal => vm._eq(&a, b)?,
|
||||
bytecode::ComparisonOperator::NotEqual => vm._ne(&a, b)?,
|
||||
bytecode::ComparisonOperator::Less => vm._lt(&a, b)?,
|
||||
bytecode::ComparisonOperator::LessOrEqual => vm._le(&a, b)?,
|
||||
bytecode::ComparisonOperator::Greater => vm._gt(&a, b)?,
|
||||
bytecode::ComparisonOperator::GreaterOrEqual => vm._ge(&a, b)?,
|
||||
bytecode::ComparisonOperator::Is => vm.ctx.new_bool(self._is(a, b)),
|
||||
bytecode::ComparisonOperator::IsNot => self._is_not(vm, a, b)?,
|
||||
bytecode::ComparisonOperator::In => self._in(vm, a, b)?,
|
||||
bytecode::ComparisonOperator::NotIn => self._not_in(vm, a, b)?,
|
||||
};
|
||||
|
||||
self.push_value(value);
|
||||
@@ -1038,15 +1025,13 @@ impl Frame {
|
||||
|
||||
fn unwrap_constant(&self, vm: &VirtualMachine, value: &bytecode::Constant) -> PyObjectRef {
|
||||
match *value {
|
||||
bytecode::Constant::Integer { ref value } => vm.ctx.new_int(value.to_bigint().unwrap()),
|
||||
bytecode::Constant::Integer { ref value } => vm.ctx.new_int(value.clone()),
|
||||
bytecode::Constant::Float { ref value } => vm.ctx.new_float(*value),
|
||||
bytecode::Constant::Complex { ref value } => vm.ctx.new_complex(*value),
|
||||
bytecode::Constant::String { ref value } => vm.new_str(value.clone()),
|
||||
bytecode::Constant::Bytes { ref value } => vm.ctx.new_bytes(value.clone()),
|
||||
bytecode::Constant::Boolean { ref value } => vm.new_bool(value.clone()),
|
||||
bytecode::Constant::Code { ref code } => {
|
||||
PyObject::new(PyObjectPayload::Code { code: code.clone() }, vm.get_type())
|
||||
}
|
||||
bytecode::Constant::Code { ref code } => vm.ctx.new_code_object(code.clone()),
|
||||
bytecode::Constant::Tuple { ref elements } => vm.ctx.new_tuple(
|
||||
elements
|
||||
.iter()
|
||||
|
||||
@@ -34,8 +34,8 @@ fn import_uncached_module(
|
||||
let code_obj = compile::compile(
|
||||
vm,
|
||||
&source,
|
||||
compile::Mode::Exec,
|
||||
Some(filepath.to_str().unwrap().to_string()),
|
||||
&compile::Mode::Exec,
|
||||
filepath.to_str().unwrap().to_string(),
|
||||
)?;
|
||||
// trace!("Code object: {:?}", code_obj);
|
||||
|
||||
@@ -71,11 +71,17 @@ pub fn import(
|
||||
let module = import_module(vm, current_path, module_name)?;
|
||||
// If we're importing a symbol, look it up and use it, otherwise construct a module and return
|
||||
// that
|
||||
let obj = match symbol {
|
||||
Some(symbol) => module.get_item(symbol).unwrap(),
|
||||
None => module,
|
||||
};
|
||||
Ok(obj)
|
||||
if let Some(symbol) = symbol {
|
||||
module.get_item(symbol).map_or_else(
|
||||
|| {
|
||||
let import_error = vm.context().exceptions.import_error.clone();
|
||||
Err(vm.new_exception(import_error, format!("cannot import name '{}'", symbol)))
|
||||
},
|
||||
|obj| Ok(obj),
|
||||
)
|
||||
} else {
|
||||
Ok(module)
|
||||
}
|
||||
}
|
||||
|
||||
fn find_source(vm: &VirtualMachine, current_path: PathBuf, name: &str) -> Result<PathBuf, String> {
|
||||
@@ -97,7 +103,7 @@ fn find_source(vm: &VirtualMachine, current_path: PathBuf, name: &str) -> Result
|
||||
}
|
||||
}
|
||||
|
||||
match filepaths.iter().filter(|p| p.exists()).next() {
|
||||
match filepaths.iter().find(|p| p.exists()) {
|
||||
Some(path) => Ok(path.to_path_buf()),
|
||||
None => Err(format!("No module named '{}'", name)),
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ extern crate log;
|
||||
// extern crate env_logger;
|
||||
extern crate num_bigint;
|
||||
extern crate num_complex;
|
||||
extern crate num_integer;
|
||||
extern crate num_traits;
|
||||
extern crate serde;
|
||||
extern crate serde_json;
|
||||
|
||||
@@ -6,6 +6,8 @@ pub mod objbytes;
|
||||
pub mod objcode;
|
||||
pub mod objcomplex;
|
||||
pub mod objdict;
|
||||
pub mod objenumerate;
|
||||
pub mod objfilter;
|
||||
pub mod objfloat;
|
||||
pub mod objframe;
|
||||
pub mod objfunction;
|
||||
@@ -13,12 +15,16 @@ pub mod objgenerator;
|
||||
pub mod objint;
|
||||
pub mod objiter;
|
||||
pub mod objlist;
|
||||
pub mod objmap;
|
||||
pub mod objmemory;
|
||||
pub mod objobject;
|
||||
pub mod objproperty;
|
||||
pub mod objrange;
|
||||
pub mod objsequence;
|
||||
pub mod objset;
|
||||
pub mod objslice;
|
||||
pub mod objstr;
|
||||
pub mod objsuper;
|
||||
pub mod objtuple;
|
||||
pub mod objtype;
|
||||
pub mod objzip;
|
||||
|
||||
@@ -20,6 +20,7 @@ pub fn boolval(vm: &mut VirtualMachine, obj: PyObjectRef) -> Result<bool, PyObje
|
||||
PyObjectPayload::Integer { ref value } => !value.is_zero(),
|
||||
_ => return Err(vm.new_type_error(String::from("TypeError"))),
|
||||
};
|
||||
|
||||
v
|
||||
} else {
|
||||
true
|
||||
@@ -30,9 +31,16 @@ pub fn boolval(vm: &mut VirtualMachine, obj: PyObjectRef) -> Result<bool, PyObje
|
||||
}
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
let ref bool_type = context.bool_type;
|
||||
let bool_doc = "bool(x) -> bool
|
||||
|
||||
Returns True when the argument x is true, False otherwise.
|
||||
The builtins True and False are the only two instances of the class bool.
|
||||
The class bool is a subclass of the class int, and cannot be subclassed.";
|
||||
|
||||
let bool_type = &context.bool_type;
|
||||
context.set_attr(&bool_type, "__new__", context.new_rustfunc(bool_new));
|
||||
context.set_attr(&bool_type, "__repr__", context.new_rustfunc(bool_repr));
|
||||
context.set_attr(&bool_type, "__doc__", context.new_str(bool_doc.to_string()));
|
||||
}
|
||||
|
||||
pub fn not(vm: &mut VirtualMachine, obj: &PyObjectRef) -> PyResult {
|
||||
@@ -74,7 +82,7 @@ fn bool_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
Ok(match val {
|
||||
Some(val) => {
|
||||
let bv = boolval(vm, val.clone())?;
|
||||
vm.new_bool(bv.clone())
|
||||
vm.new_bool(bv)
|
||||
}
|
||||
None => vm.context().new_bool(false),
|
||||
})
|
||||
|
||||
@@ -9,14 +9,27 @@ use super::objint;
|
||||
use super::super::vm::VirtualMachine;
|
||||
use super::objbytes::get_value;
|
||||
use super::objtype;
|
||||
use num_bigint::ToBigInt;
|
||||
use num_traits::ToPrimitive;
|
||||
|
||||
// Binary data support
|
||||
|
||||
/// Fill bytearray class methods dictionary.
|
||||
pub fn init(context: &PyContext) {
|
||||
let ref bytearray_type = context.bytearray_type;
|
||||
let bytearray_type = &context.bytearray_type;
|
||||
|
||||
let bytearray_doc =
|
||||
"bytearray(iterable_of_ints) -> bytearray\n\
|
||||
bytearray(string, encoding[, errors]) -> bytearray\n\
|
||||
bytearray(bytes_or_buffer) -> mutable copy of bytes_or_buffer\n\
|
||||
bytearray(int) -> bytes array of size given by the parameter initialized with null bytes\n\
|
||||
bytearray() -> empty bytes array\n\n\
|
||||
Construct a mutable bytearray object from:\n \
|
||||
- an iterable yielding integers in range(256)\n \
|
||||
- a text string encoded using the specified encoding\n \
|
||||
- a bytes or a buffer object\n \
|
||||
- any object implementing the buffer API.\n \
|
||||
- an integer";
|
||||
|
||||
context.set_attr(
|
||||
&bytearray_type,
|
||||
"__eq__",
|
||||
@@ -37,6 +50,51 @@ pub fn init(context: &PyContext) {
|
||||
"__len__",
|
||||
context.new_rustfunc(bytesarray_len),
|
||||
);
|
||||
context.set_attr(
|
||||
&bytearray_type,
|
||||
"__doc__",
|
||||
context.new_str(bytearray_doc.to_string()),
|
||||
);
|
||||
context.set_attr(
|
||||
&bytearray_type,
|
||||
"isalnum",
|
||||
context.new_rustfunc(bytearray_isalnum),
|
||||
);
|
||||
context.set_attr(
|
||||
&bytearray_type,
|
||||
"isalpha",
|
||||
context.new_rustfunc(bytearray_isalpha),
|
||||
);
|
||||
context.set_attr(
|
||||
&bytearray_type,
|
||||
"isascii",
|
||||
context.new_rustfunc(bytearray_isascii),
|
||||
);
|
||||
context.set_attr(
|
||||
&bytearray_type,
|
||||
"isdigit",
|
||||
context.new_rustfunc(bytearray_isdigit),
|
||||
);
|
||||
context.set_attr(
|
||||
&bytearray_type,
|
||||
"islower",
|
||||
context.new_rustfunc(bytearray_islower),
|
||||
);
|
||||
context.set_attr(
|
||||
&bytearray_type,
|
||||
"isspace",
|
||||
context.new_rustfunc(bytearray_isspace),
|
||||
);
|
||||
context.set_attr(
|
||||
&bytearray_type,
|
||||
"isupper",
|
||||
context.new_rustfunc(bytearray_isupper),
|
||||
);
|
||||
context.set_attr(
|
||||
&bytearray_type,
|
||||
"istitle",
|
||||
context.new_rustfunc(bytearray_istitle),
|
||||
);
|
||||
}
|
||||
|
||||
fn bytearray_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
@@ -63,18 +121,14 @@ fn bytearray_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
Ok(PyObject::new(
|
||||
PyObjectPayload::Bytes { value: value },
|
||||
cls.clone(),
|
||||
))
|
||||
Ok(PyObject::new(PyObjectPayload::Bytes { value }, cls.clone()))
|
||||
}
|
||||
|
||||
fn bytesarray_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(a, Some(vm.ctx.bytearray_type()))]);
|
||||
|
||||
let byte_vec = get_value(a).to_vec();
|
||||
let value = byte_vec.len().to_bigint();
|
||||
Ok(vm.ctx.new_int(value.unwrap()))
|
||||
Ok(vm.ctx.new_int(byte_vec.len()))
|
||||
}
|
||||
|
||||
fn bytearray_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
@@ -92,6 +146,100 @@ fn bytearray_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
Ok(vm.ctx.new_bool(result))
|
||||
}
|
||||
|
||||
fn bytearray_isalnum(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]);
|
||||
let bytes = get_value(zelf);
|
||||
Ok(vm.new_bool(!bytes.is_empty() && bytes.iter().all(|x| char::from(*x).is_alphanumeric())))
|
||||
}
|
||||
|
||||
fn bytearray_isalpha(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]);
|
||||
let bytes = get_value(zelf);
|
||||
Ok(vm.new_bool(!bytes.is_empty() && bytes.iter().all(|x| char::from(*x).is_alphabetic())))
|
||||
}
|
||||
|
||||
fn bytearray_isascii(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]);
|
||||
let bytes = get_value(zelf);
|
||||
Ok(vm.new_bool(!bytes.is_empty() && bytes.iter().all(|x| char::from(*x).is_ascii())))
|
||||
}
|
||||
|
||||
fn bytearray_isdigit(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]);
|
||||
let bytes = get_value(zelf);
|
||||
Ok(vm.new_bool(!bytes.is_empty() && bytes.iter().all(|x| char::from(*x).is_digit(10))))
|
||||
}
|
||||
|
||||
fn bytearray_islower(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]);
|
||||
let bytes = get_value(zelf);
|
||||
Ok(vm.new_bool(
|
||||
!bytes.is_empty()
|
||||
&& bytes
|
||||
.iter()
|
||||
.filter(|x| char::from(**x).is_whitespace())
|
||||
.all(|x| char::from(*x).is_lowercase()),
|
||||
))
|
||||
}
|
||||
|
||||
fn bytearray_isspace(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]);
|
||||
let bytes = get_value(zelf);
|
||||
Ok(vm.new_bool(!bytes.is_empty() && bytes.iter().all(|x| char::from(*x).is_whitespace())))
|
||||
}
|
||||
|
||||
fn bytearray_isupper(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]);
|
||||
let bytes = get_value(zelf);
|
||||
Ok(vm.new_bool(
|
||||
!bytes.is_empty()
|
||||
&& bytes
|
||||
.iter()
|
||||
.filter(|x| !char::from(**x).is_whitespace())
|
||||
.all(|x| char::from(*x).is_uppercase()),
|
||||
))
|
||||
}
|
||||
|
||||
fn bytearray_istitle(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(zelf, Some(vm.ctx.bytearray_type()))]);
|
||||
let bytes = get_value(zelf);
|
||||
|
||||
if bytes.is_empty() {
|
||||
Ok(vm.new_bool(false))
|
||||
} else {
|
||||
let mut iter = bytes.iter().peekable();
|
||||
let mut prev_cased = false;
|
||||
|
||||
while let Some(c) = iter.next() {
|
||||
let current = char::from(*c);
|
||||
let next = if let Some(k) = iter.peek() {
|
||||
char::from(**k)
|
||||
} else {
|
||||
if current.is_uppercase() {
|
||||
return Ok(vm.new_bool(!prev_cased));
|
||||
} else {
|
||||
return Ok(vm.new_bool(prev_cased));
|
||||
}
|
||||
};
|
||||
|
||||
if (is_cased(current) && next.is_uppercase() && !prev_cased)
|
||||
|| (!is_cased(current) && next.is_lowercase())
|
||||
{
|
||||
return Ok(vm.new_bool(false));
|
||||
}
|
||||
|
||||
prev_cased = is_cased(current);
|
||||
}
|
||||
|
||||
Ok(vm.new_bool(true))
|
||||
}
|
||||
}
|
||||
|
||||
// helper function for istitle
|
||||
fn is_cased(c: char) -> bool {
|
||||
c.to_uppercase().next().unwrap() != c || c.to_lowercase().next().unwrap() != c
|
||||
}
|
||||
|
||||
/*
|
||||
fn bytearray_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
|
||||
@@ -4,7 +4,6 @@ use super::super::pyobject::{
|
||||
use super::super::vm::VirtualMachine;
|
||||
use super::objint;
|
||||
use super::objtype;
|
||||
use num_bigint::ToBigInt;
|
||||
use num_traits::ToPrimitive;
|
||||
use std::cell::Ref;
|
||||
use std::hash::{Hash, Hasher};
|
||||
@@ -14,12 +13,30 @@ use std::ops::Deref;
|
||||
|
||||
// Fill bytes class methods:
|
||||
pub fn init(context: &PyContext) {
|
||||
let ref bytes_type = context.bytes_type;
|
||||
let bytes_type = &context.bytes_type;
|
||||
|
||||
let bytes_doc =
|
||||
"bytes(iterable_of_ints) -> bytes\n\
|
||||
bytes(string, encoding[, errors]) -> bytes\n\
|
||||
bytes(bytes_or_buffer) -> immutable copy of bytes_or_buffer\n\
|
||||
bytes(int) -> bytes object of size given by the parameter initialized with null bytes\n\
|
||||
bytes() -> empty bytes object\n\nConstruct an immutable array of bytes from:\n \
|
||||
- an iterable yielding integers in range(256)\n \
|
||||
- a text string encoded using the specified encoding\n \
|
||||
- any object implementing the buffer API.\n \
|
||||
- an integer";
|
||||
|
||||
context.set_attr(bytes_type, "__eq__", context.new_rustfunc(bytes_eq));
|
||||
context.set_attr(bytes_type, "__hash__", context.new_rustfunc(bytes_hash));
|
||||
context.set_attr(bytes_type, "__new__", context.new_rustfunc(bytes_new));
|
||||
context.set_attr(bytes_type, "__repr__", context.new_rustfunc(bytes_repr));
|
||||
context.set_attr(bytes_type, "__len__", context.new_rustfunc(bytes_len));
|
||||
context.set_attr(bytes_type, "__iter__", context.new_rustfunc(bytes_iter));
|
||||
context.set_attr(
|
||||
bytes_type,
|
||||
"__doc__",
|
||||
context.new_str(bytes_doc.to_string()),
|
||||
);
|
||||
}
|
||||
|
||||
fn bytes_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
@@ -47,10 +64,7 @@ fn bytes_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
vec![]
|
||||
};
|
||||
|
||||
Ok(PyObject::new(
|
||||
PyObjectPayload::Bytes { value: value },
|
||||
cls.clone(),
|
||||
))
|
||||
Ok(PyObject::new(PyObjectPayload::Bytes { value }, cls.clone()))
|
||||
}
|
||||
|
||||
fn bytes_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
@@ -72,8 +86,7 @@ fn bytes_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(a, Some(vm.ctx.bytes_type()))]);
|
||||
|
||||
let byte_vec = get_value(a).to_vec();
|
||||
let value = byte_vec.len().to_bigint();
|
||||
Ok(vm.ctx.new_int(value.unwrap()))
|
||||
Ok(vm.ctx.new_int(byte_vec.len()))
|
||||
}
|
||||
|
||||
fn bytes_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
@@ -82,7 +95,7 @@ fn bytes_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
let mut hasher = std::collections::hash_map::DefaultHasher::new();
|
||||
data.hash(&mut hasher);
|
||||
let hash = hasher.finish();
|
||||
Ok(vm.ctx.new_int(hash.to_bigint().unwrap()))
|
||||
Ok(vm.ctx.new_int(hash))
|
||||
}
|
||||
|
||||
pub fn get_value<'a>(obj: &'a PyObjectRef) -> impl Deref<Target = Vec<u8>> + 'a {
|
||||
@@ -101,3 +114,17 @@ fn bytes_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
let data = String::from_utf8(value.to_vec()).unwrap();
|
||||
Ok(vm.new_str(format!("b'{}'", data)))
|
||||
}
|
||||
|
||||
fn bytes_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(obj, Some(vm.ctx.bytes_type()))]);
|
||||
|
||||
let iter_obj = PyObject::new(
|
||||
PyObjectPayload::Iterator {
|
||||
position: 0,
|
||||
iterated_obj: obj.clone(),
|
||||
},
|
||||
vm.ctx.iter_type(),
|
||||
);
|
||||
|
||||
Ok(iter_obj)
|
||||
}
|
||||
|
||||
@@ -4,47 +4,94 @@
|
||||
|
||||
use super::super::bytecode;
|
||||
use super::super::pyobject::{
|
||||
PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol,
|
||||
IdProtocol, PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol,
|
||||
};
|
||||
use super::super::vm::VirtualMachine;
|
||||
use super::objtype;
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
let ref code_type = context.code_type;
|
||||
let code_type = &context.code_type;
|
||||
context.set_attr(code_type, "__new__", context.new_rustfunc(code_new));
|
||||
context.set_attr(code_type, "__repr__", context.new_rustfunc(code_repr));
|
||||
|
||||
for (name, f) in vec![
|
||||
(
|
||||
"co_argcount",
|
||||
code_co_argcount as fn(&mut VirtualMachine, PyFuncArgs) -> PyResult,
|
||||
),
|
||||
("co_filename", code_co_filename),
|
||||
("co_firstlineno", code_co_firstlineno),
|
||||
("co_kwonlyargcount", code_co_kwonlyargcount),
|
||||
("co_name", code_co_name),
|
||||
] {
|
||||
context.set_attr(code_type, name, context.new_member_descriptor(f))
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract rust bytecode object from a python code object.
|
||||
pub fn copy_code(code_obj: &PyObjectRef) -> bytecode::CodeObject {
|
||||
let code_obj = code_obj.borrow();
|
||||
if let PyObjectPayload::Code { ref code } = code_obj.payload {
|
||||
pub fn get_value(obj: &PyObjectRef) -> bytecode::CodeObject {
|
||||
if let PyObjectPayload::Code { code } = &obj.borrow().payload {
|
||||
code.clone()
|
||||
} else {
|
||||
panic!("Must be code obj");
|
||||
panic!("Inner error getting code {:?}", obj)
|
||||
}
|
||||
}
|
||||
|
||||
fn code_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(_cls, None)]);
|
||||
Err(vm.new_type_error(format!("Cannot directly create code object")))
|
||||
Err(vm.new_type_error("Cannot directly create code object".to_string()))
|
||||
}
|
||||
|
||||
fn code_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(o, Some(vm.ctx.code_type()))]);
|
||||
|
||||
// Fetch actual code:
|
||||
let code = copy_code(o);
|
||||
|
||||
let file = if let Some(source_path) = code.source_path {
|
||||
format!(", file {}", source_path)
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
// TODO: fetch proper line info from code object
|
||||
let line = format!(", line 1");
|
||||
|
||||
let repr = format!("<code object at .. {}{}>", file, line);
|
||||
let code = get_value(o);
|
||||
let repr = format!(
|
||||
"<code object {} at 0x{:x} file {:?}, line {}>",
|
||||
code.obj_name,
|
||||
o.get_id(),
|
||||
code.source_path,
|
||||
code.first_line_number
|
||||
);
|
||||
Ok(vm.new_str(repr))
|
||||
}
|
||||
|
||||
fn member_code_obj(
|
||||
vm: &mut VirtualMachine,
|
||||
args: PyFuncArgs,
|
||||
) -> Result<bytecode::CodeObject, PyObjectRef> {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [
|
||||
(zelf, Some(vm.ctx.code_type())),
|
||||
(_cls, Some(vm.ctx.type_type()))
|
||||
]
|
||||
);
|
||||
Ok(get_value(zelf))
|
||||
}
|
||||
|
||||
fn code_co_argcount(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
let code_obj = member_code_obj(vm, args)?;
|
||||
Ok(vm.ctx.new_int(code_obj.arg_names.len()))
|
||||
}
|
||||
|
||||
fn code_co_filename(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
let code_obj = member_code_obj(vm, args)?;
|
||||
let source_path = code_obj.source_path;
|
||||
Ok(vm.new_str(source_path))
|
||||
}
|
||||
|
||||
fn code_co_firstlineno(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
let code_obj = member_code_obj(vm, args)?;
|
||||
Ok(vm.ctx.new_int(code_obj.first_line_number))
|
||||
}
|
||||
|
||||
fn code_co_kwonlyargcount(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
let code_obj = member_code_obj(vm, args)?;
|
||||
Ok(vm.ctx.new_int(code_obj.kwonlyarg_names.len()))
|
||||
}
|
||||
|
||||
fn code_co_name(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
let code_obj = member_code_obj(vm, args)?;
|
||||
Ok(vm.new_str(code_obj.obj_name))
|
||||
}
|
||||
|
||||
@@ -7,9 +7,19 @@ use super::objtype;
|
||||
use num_complex::Complex64;
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
let ref complex_type = context.complex_type;
|
||||
let complex_type = &context.complex_type;
|
||||
|
||||
let complex_doc =
|
||||
"Create a complex number from a real part and an optional imaginary part.\n\n\
|
||||
This is equivalent to (real + imag*1j) where imag defaults to 0.";
|
||||
|
||||
context.set_attr(&complex_type, "__add__", context.new_rustfunc(complex_add));
|
||||
context.set_attr(&complex_type, "__new__", context.new_rustfunc(complex_new));
|
||||
context.set_attr(
|
||||
&complex_type,
|
||||
"__doc__",
|
||||
context.new_str(complex_doc.to_string()),
|
||||
);
|
||||
context.set_attr(
|
||||
&complex_type,
|
||||
"__repr__",
|
||||
@@ -85,5 +95,10 @@ fn complex_conjugate(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
fn complex_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(obj, Some(vm.ctx.complex_type()))]);
|
||||
let v = get_value(obj);
|
||||
Ok(vm.new_str(v.to_string()))
|
||||
let repr = if v.re == 0. {
|
||||
format!("{}j", v.im)
|
||||
} else {
|
||||
format!("({}+{}j)", v.re, v.im)
|
||||
};
|
||||
Ok(vm.new_str(repr))
|
||||
}
|
||||
|
||||
@@ -5,8 +5,7 @@ use super::super::vm::VirtualMachine;
|
||||
use super::objiter;
|
||||
use super::objstr;
|
||||
use super::objtype;
|
||||
use num_bigint::ToBigInt;
|
||||
use std::cell::{Ref, RefMut};
|
||||
use std::cell::{Ref, RefCell, RefMut};
|
||||
use std::collections::HashMap;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
@@ -106,10 +105,7 @@ pub fn contains_key_str(dict: &PyObjectRef, key: &str) -> bool {
|
||||
|
||||
pub fn content_contains_key_str(elements: &DictContentType, key: &str) -> bool {
|
||||
// TODO: let hash: usize = key;
|
||||
match elements.get(key) {
|
||||
Some(_) => true,
|
||||
None => false,
|
||||
}
|
||||
elements.get(key).is_some()
|
||||
}
|
||||
|
||||
// Python dict methods:
|
||||
@@ -140,7 +136,7 @@ fn dict_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
let elem_iter = objiter::get_iter(vm, &element)?;
|
||||
let needle = objiter::get_next_object(vm, &elem_iter)?.ok_or_else(|| err(vm))?;
|
||||
let value = objiter::get_next_object(vm, &elem_iter)?.ok_or_else(|| err(vm))?;
|
||||
if let Some(_) = objiter::get_next_object(vm, &elem_iter)? {
|
||||
if objiter::get_next_object(vm, &elem_iter)?.is_some() {
|
||||
return Err(err(vm));
|
||||
}
|
||||
set_item(&dict, &needle, &value);
|
||||
@@ -156,7 +152,7 @@ fn dict_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
fn dict_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(dict_obj, Some(vm.ctx.dict_type()))]);
|
||||
let elements = get_elements(dict_obj);
|
||||
Ok(vm.ctx.new_int(elements.len().to_bigint().unwrap()))
|
||||
Ok(vm.ctx.new_int(elements.len()))
|
||||
}
|
||||
|
||||
fn dict_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
@@ -277,14 +273,14 @@ fn dict_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
pub fn create_type(type_type: PyObjectRef, object_type: PyObjectRef, dict_type: PyObjectRef) {
|
||||
(*dict_type.borrow_mut()).payload = PyObjectPayload::Class {
|
||||
name: String::from("dict"),
|
||||
dict: new(dict_type.clone()),
|
||||
dict: RefCell::new(HashMap::new()),
|
||||
mro: vec![object_type],
|
||||
};
|
||||
(*dict_type.borrow_mut()).typ = Some(type_type.clone());
|
||||
}
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
let ref dict_type = context.dict_type;
|
||||
let dict_type = &context.dict_type;
|
||||
context.set_attr(&dict_type, "__len__", context.new_rustfunc(dict_len));
|
||||
context.set_attr(
|
||||
&dict_type,
|
||||
|
||||
69
vm/src/obj/objenumerate.rs
Normal file
69
vm/src/obj/objenumerate.rs
Normal file
@@ -0,0 +1,69 @@
|
||||
use super::super::pyobject::{
|
||||
PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol,
|
||||
};
|
||||
use super::super::vm::VirtualMachine;
|
||||
use super::objint;
|
||||
use super::objiter;
|
||||
use super::objtype; // Required for arg_check! to use isinstance
|
||||
use num_bigint::BigInt;
|
||||
use num_traits::Zero;
|
||||
use std::ops::AddAssign;
|
||||
|
||||
fn enumerate_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [(cls, Some(vm.ctx.type_type())), (iterable, None)],
|
||||
optional = [(start, Some(vm.ctx.int_type()))]
|
||||
);
|
||||
let counter = if let Some(x) = start {
|
||||
objint::get_value(x)
|
||||
} else {
|
||||
BigInt::zero()
|
||||
};
|
||||
let iterator = objiter::get_iter(vm, iterable)?;
|
||||
Ok(PyObject::new(
|
||||
PyObjectPayload::EnumerateIterator { counter, iterator },
|
||||
cls.clone(),
|
||||
))
|
||||
}
|
||||
|
||||
fn enumerate_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [(enumerate, Some(vm.ctx.enumerate_type()))]
|
||||
);
|
||||
|
||||
if let PyObjectPayload::EnumerateIterator {
|
||||
ref mut counter,
|
||||
ref mut iterator,
|
||||
} = enumerate.borrow_mut().payload
|
||||
{
|
||||
let next_obj = objiter::call_next(vm, iterator)?;
|
||||
let result = vm
|
||||
.ctx
|
||||
.new_tuple(vec![vm.ctx.new_int(counter.clone()), next_obj]);
|
||||
|
||||
AddAssign::add_assign(counter, 1);
|
||||
|
||||
Ok(result)
|
||||
} else {
|
||||
panic!("enumerate doesn't have correct payload");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
let enumerate_type = &context.enumerate_type;
|
||||
objiter::iter_type_init(context, enumerate_type);
|
||||
context.set_attr(
|
||||
enumerate_type,
|
||||
"__new__",
|
||||
context.new_rustfunc(enumerate_new),
|
||||
);
|
||||
context.set_attr(
|
||||
enumerate_type,
|
||||
"__next__",
|
||||
context.new_rustfunc(enumerate_next),
|
||||
);
|
||||
}
|
||||
63
vm/src/obj/objfilter.rs
Normal file
63
vm/src/obj/objfilter.rs
Normal file
@@ -0,0 +1,63 @@
|
||||
use super::super::pyobject::{
|
||||
IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult,
|
||||
TypeProtocol,
|
||||
};
|
||||
use super::super::vm::VirtualMachine;
|
||||
use super::objbool;
|
||||
use super::objiter;
|
||||
use super::objtype; // Required for arg_check! to use isinstance
|
||||
|
||||
fn filter_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [(cls, None), (function, None), (iterable, None)]
|
||||
);
|
||||
let iterator = objiter::get_iter(vm, iterable)?;
|
||||
Ok(PyObject::new(
|
||||
PyObjectPayload::FilterIterator {
|
||||
predicate: function.clone(),
|
||||
iterator,
|
||||
},
|
||||
cls.clone(),
|
||||
))
|
||||
}
|
||||
|
||||
fn filter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(filter, Some(vm.ctx.filter_type()))]);
|
||||
|
||||
if let PyObjectPayload::FilterIterator {
|
||||
ref mut predicate,
|
||||
ref mut iterator,
|
||||
} = filter.borrow_mut().payload
|
||||
{
|
||||
loop {
|
||||
let next_obj = objiter::call_next(vm, iterator)?;
|
||||
let predicate_value = if predicate.is(&vm.get_none()) {
|
||||
next_obj.clone()
|
||||
} else {
|
||||
// the predicate itself can raise StopIteration which does stop the filter
|
||||
// iteration
|
||||
vm.invoke(
|
||||
predicate.clone(),
|
||||
PyFuncArgs {
|
||||
args: vec![next_obj.clone()],
|
||||
kwargs: vec![],
|
||||
},
|
||||
)?
|
||||
};
|
||||
if objbool::boolval(vm, predicate_value)? {
|
||||
return Ok(next_obj);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
panic!("filter doesn't have correct payload");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
let filter_type = &context.filter_type;
|
||||
objiter::iter_type_init(context, filter_type);
|
||||
context.set_attr(&filter_type, "__new__", context.new_rustfunc(filter_new));
|
||||
context.set_attr(&filter_type, "__next__", context.new_rustfunc(filter_next));
|
||||
}
|
||||
@@ -172,9 +172,9 @@ fn float_divmod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
let args = PyFuncArgs::new(vec![i.clone(), i2.clone()], vec![]);
|
||||
if objtype::isinstance(i2, &vm.ctx.float_type()) || objtype::isinstance(i2, &vm.ctx.int_type())
|
||||
{
|
||||
let r1 = float_floordiv(vm, args.clone());
|
||||
let r2 = float_mod(vm, args.clone());
|
||||
Ok(vm.ctx.new_tuple(vec![r1.unwrap(), r2.unwrap()]))
|
||||
let r1 = float_floordiv(vm, args.clone())?;
|
||||
let r2 = float_mod(vm, args.clone())?;
|
||||
Ok(vm.ctx.new_tuple(vec![r1, r2]))
|
||||
} else {
|
||||
Err(vm.new_type_error(format!(
|
||||
"Cannot divmod power {} and {}",
|
||||
@@ -190,18 +190,26 @@ fn float_floordiv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
args,
|
||||
required = [(i, Some(vm.ctx.float_type())), (i2, None)]
|
||||
);
|
||||
if objtype::isinstance(i2, &vm.ctx.float_type()) {
|
||||
Ok(vm.ctx.new_float((get_value(i) / get_value(i2)).floor()))
|
||||
} else if objtype::isinstance(i2, &vm.ctx.int_type()) {
|
||||
Ok(vm
|
||||
.ctx
|
||||
.new_float((get_value(i) / objint::get_value(i2).to_f64().unwrap()).floor()))
|
||||
|
||||
let v1 = get_value(i);
|
||||
let v2 = if objtype::isinstance(i2, &vm.ctx.float_type) {
|
||||
get_value(i2)
|
||||
} else if objtype::isinstance(i2, &vm.ctx.int_type) {
|
||||
objint::get_value(i2)
|
||||
.to_f64()
|
||||
.ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_string()))?
|
||||
} else {
|
||||
Err(vm.new_type_error(format!(
|
||||
return Err(vm.new_type_error(format!(
|
||||
"Cannot floordiv {} and {}",
|
||||
i.borrow(),
|
||||
i2.borrow()
|
||||
)))
|
||||
)));
|
||||
};
|
||||
|
||||
if v2 != 0.0 {
|
||||
Ok(vm.ctx.new_float((v1 / v2).floor()))
|
||||
} else {
|
||||
Err(vm.new_zero_division_error("float floordiv by zero".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,14 +237,22 @@ fn float_mod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
args,
|
||||
required = [(i, Some(vm.ctx.float_type())), (i2, None)]
|
||||
);
|
||||
if objtype::isinstance(i2, &vm.ctx.float_type()) {
|
||||
Ok(vm.ctx.new_float(get_value(i) % get_value(i2)))
|
||||
} else if objtype::isinstance(i2, &vm.ctx.int_type()) {
|
||||
Ok(vm
|
||||
.ctx
|
||||
.new_float(get_value(i) % objint::get_value(i2).to_f64().unwrap()))
|
||||
|
||||
let v1 = get_value(i);
|
||||
let v2 = if objtype::isinstance(i2, &vm.ctx.float_type) {
|
||||
get_value(i2)
|
||||
} else if objtype::isinstance(i2, &vm.ctx.int_type) {
|
||||
objint::get_value(i2)
|
||||
.to_f64()
|
||||
.ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_string()))?
|
||||
} else {
|
||||
Err(vm.new_type_error(format!("Cannot mod {} and {}", i.borrow(), i2.borrow())))
|
||||
return Err(vm.new_type_error(format!("Cannot mod {} and {}", i.borrow(), i2.borrow())));
|
||||
};
|
||||
|
||||
if v2 != 0.0 {
|
||||
Ok(vm.ctx.new_float(v1 % v2))
|
||||
} else {
|
||||
Err(vm.new_zero_division_error("float mod by zero".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,8 +282,58 @@ fn float_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
}
|
||||
}
|
||||
|
||||
fn float_truediv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [(i, Some(vm.ctx.float_type())), (i2, None)]
|
||||
);
|
||||
|
||||
let v1 = get_value(i);
|
||||
let v2 = if objtype::isinstance(i2, &vm.ctx.float_type) {
|
||||
get_value(i2)
|
||||
} else if objtype::isinstance(i2, &vm.ctx.int_type) {
|
||||
objint::get_value(i2)
|
||||
.to_f64()
|
||||
.ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_string()))?
|
||||
} else {
|
||||
return Err(vm.new_type_error(format!("Cannot divide {} and {}", i.borrow(), i2.borrow())));
|
||||
};
|
||||
|
||||
if v2 != 0.0 {
|
||||
Ok(vm.ctx.new_float(v1 / v2))
|
||||
} else {
|
||||
Err(vm.new_zero_division_error("float division by zero".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
fn float_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [(i, Some(vm.ctx.float_type())), (i2, None)]
|
||||
);
|
||||
let v1 = get_value(i);
|
||||
if objtype::isinstance(i2, &vm.ctx.float_type) {
|
||||
Ok(vm.ctx.new_float(v1 * get_value(i2)))
|
||||
} else if objtype::isinstance(i2, &vm.ctx.int_type) {
|
||||
Ok(vm
|
||||
.ctx
|
||||
.new_float(v1 * objint::get_value(i2).to_f64().unwrap()))
|
||||
} else {
|
||||
Err(vm.new_type_error(format!(
|
||||
"Cannot multiply {} and {}",
|
||||
i.borrow(),
|
||||
i2.borrow()
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
let ref float_type = context.float_type;
|
||||
let float_type = &context.float_type;
|
||||
|
||||
let float_doc = "Convert a string or number to a floating point number, if possible.";
|
||||
|
||||
context.set_attr(&float_type, "__eq__", context.new_rustfunc(float_eq));
|
||||
context.set_attr(&float_type, "__lt__", context.new_rustfunc(float_lt));
|
||||
context.set_attr(&float_type, "__le__", context.new_rustfunc(float_le));
|
||||
@@ -291,4 +357,15 @@ pub fn init(context: &PyContext) {
|
||||
context.set_attr(&float_type, "__pow__", context.new_rustfunc(float_pow));
|
||||
context.set_attr(&float_type, "__sub__", context.new_rustfunc(float_sub));
|
||||
context.set_attr(&float_type, "__repr__", context.new_rustfunc(float_repr));
|
||||
context.set_attr(
|
||||
&float_type,
|
||||
"__doc__",
|
||||
context.new_str(float_doc.to_string()),
|
||||
);
|
||||
context.set_attr(
|
||||
&float_type,
|
||||
"__truediv__",
|
||||
context.new_rustfunc(float_truediv),
|
||||
);
|
||||
context.set_attr(&float_type, "__mul__", context.new_rustfunc(float_mul));
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ use super::super::vm::VirtualMachine;
|
||||
use super::objtype;
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
let ref frame_type = context.frame_type;
|
||||
let frame_type = &context.frame_type;
|
||||
context.set_attr(&frame_type, "__new__", context.new_rustfunc(frame_new));
|
||||
context.set_attr(&frame_type, "__repr__", context.new_rustfunc(frame_repr));
|
||||
context.set_attr(&frame_type, "f_locals", context.new_property(frame_flocals));
|
||||
@@ -19,12 +19,12 @@ pub fn init(context: &PyContext) {
|
||||
|
||||
fn frame_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(_cls, None)]);
|
||||
Err(vm.new_type_error(format!("Cannot directly create frame object")))
|
||||
Err(vm.new_type_error("Cannot directly create frame object".to_string()))
|
||||
}
|
||||
|
||||
fn frame_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(_frame, Some(vm.ctx.frame_type()))]);
|
||||
let repr = format!("<frame object at .. >");
|
||||
let repr = "<frame object at .. >".to_string();
|
||||
Ok(vm.new_str(repr))
|
||||
}
|
||||
|
||||
|
||||
@@ -1,22 +1,28 @@
|
||||
use super::super::pyobject::{
|
||||
AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef,
|
||||
PyResult, TypeProtocol,
|
||||
AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult,
|
||||
TypeProtocol,
|
||||
};
|
||||
use super::super::vm::VirtualMachine;
|
||||
use super::objtype;
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
let ref function_type = context.function_type;
|
||||
let function_type = &context.function_type;
|
||||
context.set_attr(&function_type, "__get__", context.new_rustfunc(bind_method));
|
||||
|
||||
let ref member_descriptor_type = context.member_descriptor_type;
|
||||
context.set_attr(
|
||||
&function_type,
|
||||
"__code__",
|
||||
context.new_member_descriptor(function_code),
|
||||
);
|
||||
|
||||
let member_descriptor_type = &context.member_descriptor_type;
|
||||
context.set_attr(
|
||||
&member_descriptor_type,
|
||||
"__get__",
|
||||
context.new_rustfunc(member_get),
|
||||
);
|
||||
|
||||
let ref classmethod_type = context.classmethod_type;
|
||||
let classmethod_type = &context.classmethod_type;
|
||||
context.set_attr(
|
||||
&classmethod_type,
|
||||
"__get__",
|
||||
@@ -28,7 +34,7 @@ pub fn init(context: &PyContext) {
|
||||
context.new_rustfunc(classmethod_new),
|
||||
);
|
||||
|
||||
let ref staticmethod_type = context.staticmethod_type;
|
||||
let staticmethod_type = &context.staticmethod_type;
|
||||
context.set_attr(
|
||||
staticmethod_type,
|
||||
"__get__",
|
||||
@@ -55,6 +61,13 @@ fn bind_method(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
}
|
||||
}
|
||||
|
||||
fn function_code(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
match args.args[0].borrow().payload {
|
||||
PyObjectPayload::Function { ref code, .. } => Ok(code.clone()),
|
||||
_ => Err(vm.new_type_error("no code".to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
fn member_get(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult {
|
||||
match args.shift().get_attr("function") {
|
||||
Some(function) => vm.invoke(function, args),
|
||||
@@ -97,12 +110,7 @@ fn classmethod_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
trace!("classmethod.__new__ {:?}", args.args);
|
||||
arg_check!(vm, args, required = [(cls, None), (callable, None)]);
|
||||
|
||||
let py_obj = PyObject::new(
|
||||
PyObjectPayload::Instance {
|
||||
dict: vm.ctx.new_dict(),
|
||||
},
|
||||
cls.clone(),
|
||||
);
|
||||
let py_obj = vm.ctx.new_instance(cls.clone(), None);
|
||||
vm.ctx.set_attr(&py_obj, "function", callable.clone());
|
||||
Ok(py_obj)
|
||||
}
|
||||
@@ -135,12 +143,7 @@ fn staticmethod_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
trace!("staticmethod.__new__ {:?}", args.args);
|
||||
arg_check!(vm, args, required = [(cls, None), (callable, None)]);
|
||||
|
||||
let py_obj = PyObject::new(
|
||||
PyObjectPayload::Instance {
|
||||
dict: vm.ctx.new_dict(),
|
||||
},
|
||||
cls.clone(),
|
||||
);
|
||||
let py_obj = vm.ctx.new_instance(cls.clone(), None);
|
||||
vm.ctx.set_attr(&py_obj, "function", callable.clone());
|
||||
Ok(py_obj)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ use super::super::vm::VirtualMachine;
|
||||
use super::objtype;
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
let ref generator_type = context.generator_type;
|
||||
let generator_type = &context.generator_type;
|
||||
context.set_attr(
|
||||
&generator_type,
|
||||
"__iter__",
|
||||
@@ -30,7 +30,7 @@ pub fn init(context: &PyContext) {
|
||||
|
||||
pub fn new_generator(vm: &mut VirtualMachine, frame: Frame) -> PyResult {
|
||||
let g = PyObject::new(
|
||||
PyObjectPayload::Generator { frame: frame },
|
||||
PyObjectPayload::Generator { frame },
|
||||
vm.ctx.generator_type.clone(),
|
||||
);
|
||||
Ok(g)
|
||||
|
||||
@@ -8,6 +8,7 @@ use super::objfloat;
|
||||
use super::objstr;
|
||||
use super::objtype;
|
||||
use num_bigint::{BigInt, ToBigInt};
|
||||
use num_integer::Integer;
|
||||
use num_traits::{Pow, Signed, ToPrimitive, Zero};
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
@@ -58,7 +59,7 @@ pub fn to_int(
|
||||
match i32::from_str_radix(&s, base) {
|
||||
Ok(v) => v.to_bigint().unwrap(),
|
||||
Err(err) => {
|
||||
trace!("Error occured during int conversion {:?}", err);
|
||||
trace!("Error occurred during int conversion {:?}", err);
|
||||
return Err(vm.new_value_error(format!(
|
||||
"invalid literal for int() with base {}: '{}'",
|
||||
base, s
|
||||
@@ -90,6 +91,20 @@ impl FromPyObjectRef for BigInt {
|
||||
}
|
||||
}
|
||||
|
||||
fn int_bool(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(zelf, Some(vm.ctx.int_type()))]);
|
||||
let result = !BigInt::from_pyobj(zelf).is_zero();
|
||||
Ok(vm.ctx.new_bool(result))
|
||||
}
|
||||
|
||||
fn int_invert(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(zelf, Some(vm.ctx.int_type()))]);
|
||||
|
||||
let result = !BigInt::from_pyobj(zelf);
|
||||
|
||||
Ok(vm.ctx.new_int(result))
|
||||
}
|
||||
|
||||
fn int_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
@@ -170,13 +185,73 @@ fn int_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
Ok(vm.ctx.new_bool(result))
|
||||
}
|
||||
|
||||
fn int_lshift(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [(i, Some(vm.ctx.int_type())), (i2, None)]
|
||||
);
|
||||
|
||||
if !objtype::isinstance(i2, &vm.ctx.int_type()) {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"unsupported operand type(s) for << '{}' and '{}'",
|
||||
objtype::get_type_name(&i.typ()),
|
||||
objtype::get_type_name(&i2.typ())
|
||||
)));
|
||||
}
|
||||
|
||||
if let Some(n_bits) = get_value(i2).to_usize() {
|
||||
return Ok(vm.ctx.new_int(get_value(i) << n_bits));
|
||||
}
|
||||
|
||||
// i2 failed `to_usize()` conversion
|
||||
match get_value(i2) {
|
||||
ref v if *v < BigInt::zero() => Err(vm.new_value_error("negative shift count".to_string())),
|
||||
ref v if *v > BigInt::from(usize::max_value()) => {
|
||||
// TODO: raise OverflowError
|
||||
panic!("Failed converting {} to rust usize", get_value(i2));
|
||||
}
|
||||
_ => panic!("Failed converting {} to rust usize", get_value(i2)),
|
||||
}
|
||||
}
|
||||
|
||||
fn int_rshift(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [(i, Some(vm.ctx.int_type())), (i2, None)]
|
||||
);
|
||||
|
||||
if !objtype::isinstance(i2, &vm.ctx.int_type()) {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"unsupported operand type(s) for >> '{}' and '{}'",
|
||||
objtype::get_type_name(&i.typ()),
|
||||
objtype::get_type_name(&i2.typ())
|
||||
)));
|
||||
}
|
||||
|
||||
if let Some(n_bits) = get_value(i2).to_usize() {
|
||||
return Ok(vm.ctx.new_int(get_value(i) >> n_bits));
|
||||
}
|
||||
|
||||
// i2 failed `to_usize()` conversion
|
||||
match get_value(i2) {
|
||||
ref v if *v < BigInt::zero() => Err(vm.new_value_error("negative shift count".to_string())),
|
||||
ref v if *v > BigInt::from(usize::max_value()) => {
|
||||
// TODO: raise OverflowError
|
||||
panic!("Failed converting {} to rust usize", get_value(i2));
|
||||
}
|
||||
_ => panic!("Failed converting {} to rust usize", get_value(i2)),
|
||||
}
|
||||
}
|
||||
|
||||
fn int_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(zelf, Some(vm.ctx.int_type()))]);
|
||||
let value = BigInt::from_pyobj(zelf);
|
||||
let mut hasher = std::collections::hash_map::DefaultHasher::new();
|
||||
value.hash(&mut hasher);
|
||||
let hash = hasher.finish();
|
||||
Ok(vm.ctx.new_int(hash.to_bigint().unwrap()))
|
||||
Ok(vm.ctx.new_int(hash))
|
||||
}
|
||||
|
||||
fn int_abs(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
@@ -215,7 +290,13 @@ fn int_floordiv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
required = [(i, Some(vm.ctx.int_type())), (i2, None)]
|
||||
);
|
||||
if objtype::isinstance(i2, &vm.ctx.int_type()) {
|
||||
Ok(vm.ctx.new_int(get_value(i) / get_value(i2)))
|
||||
let (v1, v2) = (get_value(i), get_value(i2));
|
||||
|
||||
if v2 != BigInt::zero() {
|
||||
Ok(vm.ctx.new_int(v1 / v2))
|
||||
} else {
|
||||
Err(vm.new_zero_division_error("integer floordiv by zero".to_string()))
|
||||
}
|
||||
} else {
|
||||
Err(vm.new_type_error(format!(
|
||||
"Cannot floordiv {} and {}",
|
||||
@@ -225,6 +306,21 @@ fn int_floordiv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
}
|
||||
}
|
||||
|
||||
fn int_round(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [(i, Some(vm.ctx.int_type()))],
|
||||
optional = [(_precision, None)]
|
||||
);
|
||||
Ok(vm.ctx.new_int(get_value(i)))
|
||||
}
|
||||
|
||||
fn int_pass_value(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(i, Some(vm.ctx.int_type()))]);
|
||||
Ok(vm.ctx.new_int(get_value(i)))
|
||||
}
|
||||
|
||||
fn int_format(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
@@ -292,17 +388,25 @@ fn int_truediv(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
args,
|
||||
required = [(i, Some(vm.ctx.int_type())), (i2, None)]
|
||||
);
|
||||
let v1 = get_value(i);
|
||||
if objtype::isinstance(i2, &vm.ctx.int_type()) {
|
||||
Ok(vm
|
||||
.ctx
|
||||
.new_float(v1.to_f64().unwrap() / get_value(i2).to_f64().unwrap()))
|
||||
|
||||
let v1 = get_value(i)
|
||||
.to_f64()
|
||||
.ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_string()))?;
|
||||
|
||||
let v2 = if objtype::isinstance(i2, &vm.ctx.int_type()) {
|
||||
get_value(i2)
|
||||
.to_f64()
|
||||
.ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_string()))?
|
||||
} else if objtype::isinstance(i2, &vm.ctx.float_type()) {
|
||||
Ok(vm
|
||||
.ctx
|
||||
.new_float(v1.to_f64().unwrap() / objfloat::get_value(i2)))
|
||||
objfloat::get_value(i2)
|
||||
} else {
|
||||
Err(vm.new_type_error(format!("Cannot divide {} and {}", i.borrow(), i2.borrow())))
|
||||
return Err(vm.new_type_error(format!("Cannot divide {} and {}", i.borrow(), i2.borrow())));
|
||||
};
|
||||
|
||||
if v2 == 0.0 {
|
||||
Err(vm.new_zero_division_error("integer division by zero".to_string()))
|
||||
} else {
|
||||
Ok(vm.ctx.new_float(v1 / v2))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -314,7 +418,13 @@ fn int_mod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
);
|
||||
let v1 = get_value(i);
|
||||
if objtype::isinstance(i2, &vm.ctx.int_type()) {
|
||||
Ok(vm.ctx.new_int(v1 % get_value(i2)))
|
||||
let v2 = get_value(i2);
|
||||
|
||||
if v2 != BigInt::zero() {
|
||||
Ok(vm.ctx.new_int(v1 % get_value(i2)))
|
||||
} else {
|
||||
Err(vm.new_zero_division_error("integer modulo by zero".to_string()))
|
||||
}
|
||||
} else {
|
||||
Err(vm.new_type_error(format!("Cannot modulo {} and {}", i.borrow(), i2.borrow())))
|
||||
}
|
||||
@@ -340,7 +450,7 @@ fn int_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
let v1 = get_value(i);
|
||||
if objtype::isinstance(i2, &vm.ctx.int_type()) {
|
||||
let v2 = get_value(i2).to_u32().unwrap();
|
||||
Ok(vm.ctx.new_int(v1.pow(v2).to_bigint().unwrap()))
|
||||
Ok(vm.ctx.new_int(v1.pow(v2)))
|
||||
} else if objtype::isinstance(i2, &vm.ctx.float_type()) {
|
||||
let v2 = objfloat::get_value(i2);
|
||||
Ok(vm.ctx.new_float((v1.to_f64().unwrap()).powf(v2)))
|
||||
@@ -359,11 +469,20 @@ fn int_divmod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
args,
|
||||
required = [(i, Some(vm.ctx.int_type())), (i2, None)]
|
||||
);
|
||||
let args = PyFuncArgs::new(vec![i.clone(), i2.clone()], vec![]);
|
||||
|
||||
if objtype::isinstance(i2, &vm.ctx.int_type()) {
|
||||
let r1 = int_floordiv(vm, args.clone());
|
||||
let r2 = int_mod(vm, args.clone());
|
||||
Ok(vm.ctx.new_tuple(vec![r1.unwrap(), r2.unwrap()]))
|
||||
let v1 = get_value(i);
|
||||
let v2 = get_value(i2);
|
||||
|
||||
if v2 != BigInt::zero() {
|
||||
let (r1, r2) = v1.div_rem(&v2);
|
||||
|
||||
Ok(vm
|
||||
.ctx
|
||||
.new_tuple(vec![vm.ctx.new_int(r1), vm.ctx.new_int(r2)]))
|
||||
} else {
|
||||
Err(vm.new_zero_division_error("integer divmod by zero".to_string()))
|
||||
}
|
||||
} else {
|
||||
Err(vm.new_type_error(format!(
|
||||
"Cannot divmod power {} and {}",
|
||||
@@ -388,6 +507,23 @@ fn int_xor(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
}
|
||||
}
|
||||
|
||||
fn int_rxor(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [(i, Some(vm.ctx.int_type())), (i2, None)]
|
||||
);
|
||||
|
||||
if objtype::isinstance(i2, &vm.ctx.int_type()) {
|
||||
let right_val = get_value(i);
|
||||
let left_val = get_value(i2);
|
||||
|
||||
Ok(vm.ctx.new_int(left_val ^ right_val))
|
||||
} else {
|
||||
Err(vm.new_type_error(format!("Cannot rxor {} and {}", i.borrow(), i2.borrow())))
|
||||
}
|
||||
}
|
||||
|
||||
fn int_or(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
@@ -422,11 +558,32 @@ fn int_bit_length(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(i, Some(vm.ctx.int_type()))]);
|
||||
let v = get_value(i);
|
||||
let bits = v.bits();
|
||||
Ok(vm.ctx.new_int(bits.to_bigint().unwrap()))
|
||||
Ok(vm.ctx.new_int(bits))
|
||||
}
|
||||
|
||||
fn int_conjugate(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(i, Some(vm.ctx.int_type()))]);
|
||||
let v = get_value(i);
|
||||
Ok(vm.ctx.new_int(v))
|
||||
}
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
let ref int_type = context.int_type;
|
||||
let int_doc = "int(x=0) -> integer
|
||||
int(x, base=10) -> integer
|
||||
|
||||
Convert a number or string to an integer, or return 0 if no arguments
|
||||
are given. If x is a number, return x.__int__(). For floating point
|
||||
numbers, this truncates towards zero.
|
||||
|
||||
If x is not a number or if base is given, then x must be a string,
|
||||
bytes, or bytearray instance representing an integer literal in the
|
||||
given base. The literal can be preceded by '+' or '-' and be surrounded
|
||||
by whitespace. The base defaults to 10. Valid bases are 0 and 2-36.
|
||||
Base 0 means to interpret the base from the string as an integer literal.
|
||||
>>> int('0b100', base=0)
|
||||
4";
|
||||
let int_type = &context.int_type;
|
||||
|
||||
context.set_attr(&int_type, "__eq__", context.new_rustfunc(int_eq));
|
||||
context.set_attr(&int_type, "__lt__", context.new_rustfunc(int_lt));
|
||||
context.set_attr(&int_type, "__le__", context.new_rustfunc(int_le));
|
||||
@@ -437,12 +594,20 @@ pub fn init(context: &PyContext) {
|
||||
context.set_attr(&int_type, "__and__", context.new_rustfunc(int_and));
|
||||
context.set_attr(&int_type, "__divmod__", context.new_rustfunc(int_divmod));
|
||||
context.set_attr(&int_type, "__float__", context.new_rustfunc(int_float));
|
||||
context.set_attr(&int_type, "__round__", context.new_rustfunc(int_round));
|
||||
context.set_attr(&int_type, "__ceil__", context.new_rustfunc(int_pass_value));
|
||||
context.set_attr(&int_type, "__floor__", context.new_rustfunc(int_pass_value));
|
||||
context.set_attr(&int_type, "__index__", context.new_rustfunc(int_pass_value));
|
||||
context.set_attr(&int_type, "__trunc__", context.new_rustfunc(int_pass_value));
|
||||
context.set_attr(&int_type, "__int__", context.new_rustfunc(int_pass_value));
|
||||
context.set_attr(
|
||||
&int_type,
|
||||
"__floordiv__",
|
||||
context.new_rustfunc(int_floordiv),
|
||||
);
|
||||
context.set_attr(&int_type, "__hash__", context.new_rustfunc(int_hash));
|
||||
context.set_attr(&int_type, "__lshift__", context.new_rustfunc(int_lshift));
|
||||
context.set_attr(&int_type, "__rshift__", context.new_rustfunc(int_rshift));
|
||||
context.set_attr(&int_type, "__new__", context.new_rustfunc(int_new));
|
||||
context.set_attr(&int_type, "__mod__", context.new_rustfunc(int_mod));
|
||||
context.set_attr(&int_type, "__mul__", context.new_rustfunc(int_mul));
|
||||
@@ -455,9 +620,14 @@ pub fn init(context: &PyContext) {
|
||||
context.set_attr(&int_type, "__format__", context.new_rustfunc(int_format));
|
||||
context.set_attr(&int_type, "__truediv__", context.new_rustfunc(int_truediv));
|
||||
context.set_attr(&int_type, "__xor__", context.new_rustfunc(int_xor));
|
||||
context.set_attr(&int_type, "__rxor__", context.new_rustfunc(int_rxor));
|
||||
context.set_attr(&int_type, "__bool__", context.new_rustfunc(int_bool));
|
||||
context.set_attr(&int_type, "__invert__", context.new_rustfunc(int_invert));
|
||||
context.set_attr(
|
||||
&int_type,
|
||||
"bit_length",
|
||||
context.new_rustfunc(int_bit_length),
|
||||
);
|
||||
context.set_attr(&int_type, "__doc__", context.new_str(int_doc.to_string()));
|
||||
context.set_attr(&int_type, "conjugate", context.new_rustfunc(int_conjugate));
|
||||
}
|
||||
|
||||
@@ -22,6 +22,10 @@ pub fn get_iter(vm: &mut VirtualMachine, iter_target: &PyObjectRef) -> PyResult
|
||||
// return Err(type_error);
|
||||
}
|
||||
|
||||
pub fn call_next(vm: &mut VirtualMachine, iter_obj: &PyObjectRef) -> PyResult {
|
||||
vm.call_method(iter_obj, "__next__", vec![])
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function to retrieve the next object (or none) from an iterator.
|
||||
*/
|
||||
@@ -29,7 +33,7 @@ pub fn get_next_object(
|
||||
vm: &mut VirtualMachine,
|
||||
iter_obj: &PyObjectRef,
|
||||
) -> Result<Option<PyObjectRef>, PyObjectRef> {
|
||||
let next_obj: PyResult = vm.call_method(iter_obj, "__next__", vec![]);
|
||||
let next_obj: PyResult = call_next(vm, iter_obj);
|
||||
|
||||
match next_obj {
|
||||
Ok(value) => Ok(Some(value)),
|
||||
@@ -60,6 +64,59 @@ pub fn get_all(
|
||||
Ok(elements)
|
||||
}
|
||||
|
||||
pub fn new_stop_iteration(vm: &mut VirtualMachine) -> PyObjectRef {
|
||||
let stop_iteration_type = vm.ctx.exceptions.stop_iteration.clone();
|
||||
vm.new_exception(stop_iteration_type, "End of iterator".to_string())
|
||||
}
|
||||
|
||||
fn contains(vm: &mut VirtualMachine, args: PyFuncArgs, iter_type: PyObjectRef) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [(iter, Some(iter_type)), (needle, None)]
|
||||
);
|
||||
loop {
|
||||
if let Some(element) = get_next_object(vm, iter)? {
|
||||
let equal = vm.call_method(needle, "__eq__", vec![element.clone()])?;
|
||||
if objbool::get_value(&equal) {
|
||||
return Ok(vm.new_bool(true));
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
return Ok(vm.new_bool(false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Common setup for iter types, adds __iter__ and __contains__ methods
|
||||
pub fn iter_type_init(context: &PyContext, iter_type: &PyObjectRef) {
|
||||
let contains_func = {
|
||||
let cloned_iter_type = iter_type.clone();
|
||||
move |vm: &mut VirtualMachine, args: PyFuncArgs| {
|
||||
contains(vm, args, cloned_iter_type.clone())
|
||||
}
|
||||
};
|
||||
context.set_attr(
|
||||
&iter_type,
|
||||
"__contains__",
|
||||
context.new_rustfunc(contains_func),
|
||||
);
|
||||
let iter_func = {
|
||||
let cloned_iter_type = iter_type.clone();
|
||||
move |vm: &mut VirtualMachine, args: PyFuncArgs| {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [(iter, Some(cloned_iter_type.clone()))]
|
||||
);
|
||||
// Return self:
|
||||
Ok(iter.clone())
|
||||
}
|
||||
};
|
||||
context.set_attr(&iter_type, "__iter__", context.new_rustfunc(iter_func));
|
||||
}
|
||||
|
||||
// Sequence iterator:
|
||||
fn iter_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(iter_target, None)]);
|
||||
@@ -67,44 +124,15 @@ fn iter_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
get_iter(vm, iter_target)
|
||||
}
|
||||
|
||||
fn iter_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(iter, Some(vm.ctx.iter_type()))]);
|
||||
// Return self:
|
||||
Ok(iter.clone())
|
||||
}
|
||||
|
||||
fn iter_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [(iter, Some(vm.ctx.iter_type())), (needle, None)]
|
||||
);
|
||||
loop {
|
||||
match vm.call_method(&iter, "__next__", vec![]) {
|
||||
Ok(element) => match vm.call_method(needle, "__eq__", vec![element.clone()]) {
|
||||
Ok(value) => {
|
||||
if objbool::get_value(&value) {
|
||||
return Ok(vm.new_bool(true));
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Err(_) => return Err(vm.new_type_error("".to_string())),
|
||||
},
|
||||
Err(_) => return Ok(vm.new_bool(false)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(iter, Some(vm.ctx.iter_type()))]);
|
||||
|
||||
if let PyObjectPayload::Iterator {
|
||||
ref mut position,
|
||||
iterated_obj: ref iterated_obj_ref,
|
||||
iterated_obj: ref mut iterated_obj_ref,
|
||||
} = iter.borrow_mut().payload
|
||||
{
|
||||
let iterated_obj = &*iterated_obj_ref.borrow_mut();
|
||||
let iterated_obj = iterated_obj_ref.borrow_mut();
|
||||
match iterated_obj.payload {
|
||||
PyObjectPayload::Sequence { ref elements } => {
|
||||
if *position < elements.len() {
|
||||
@@ -112,12 +140,29 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
*position += 1;
|
||||
Ok(obj_ref)
|
||||
} else {
|
||||
let stop_iteration_type = vm.ctx.exceptions.stop_iteration.clone();
|
||||
let stop_iteration =
|
||||
vm.new_exception(stop_iteration_type, "End of iterator".to_string());
|
||||
Err(stop_iteration)
|
||||
Err(new_stop_iteration(vm))
|
||||
}
|
||||
}
|
||||
|
||||
PyObjectPayload::Range { ref range } => {
|
||||
if let Some(int) = range.get(*position) {
|
||||
*position += 1;
|
||||
Ok(vm.ctx.new_int(int))
|
||||
} else {
|
||||
Err(new_stop_iteration(vm))
|
||||
}
|
||||
}
|
||||
|
||||
PyObjectPayload::Bytes { ref value } => {
|
||||
if *position < value.len() {
|
||||
let obj_ref = vm.ctx.new_int(value[*position]);
|
||||
*position += 1;
|
||||
Ok(obj_ref)
|
||||
} else {
|
||||
Err(new_stop_iteration(vm))
|
||||
}
|
||||
}
|
||||
|
||||
_ => {
|
||||
panic!("NOT IMPL");
|
||||
}
|
||||
@@ -128,13 +173,8 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
}
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
let ref iter_type = context.iter_type;
|
||||
context.set_attr(
|
||||
&iter_type,
|
||||
"__contains__",
|
||||
context.new_rustfunc(iter_contains),
|
||||
);
|
||||
context.set_attr(&iter_type, "__iter__", context.new_rustfunc(iter_iter));
|
||||
let iter_type = &context.iter_type;
|
||||
iter_type_init(context, iter_type);
|
||||
context.set_attr(&iter_type, "__new__", context.new_rustfunc(iter_new));
|
||||
context.set_attr(&iter_type, "__next__", context.new_rustfunc(iter_next));
|
||||
}
|
||||
|
||||
@@ -5,11 +5,11 @@ use super::super::vm::VirtualMachine;
|
||||
use super::objbool;
|
||||
use super::objint;
|
||||
use super::objsequence::{
|
||||
get_elements, get_item, get_mut_elements, seq_equal, PySliceableSequence,
|
||||
get_elements, get_item, get_mut_elements, seq_equal, seq_ge, seq_gt, seq_le, seq_lt, seq_mul,
|
||||
PySliceableSequence,
|
||||
};
|
||||
use super::objstr;
|
||||
use super::objtype;
|
||||
use num_bigint::ToBigInt;
|
||||
use num_traits::ToPrimitive;
|
||||
|
||||
// set_item:
|
||||
@@ -21,9 +21,12 @@ fn set_item(
|
||||
) -> PyResult {
|
||||
if objtype::isinstance(&idx, &vm.ctx.int_type()) {
|
||||
let value = objint::get_value(&idx).to_i32().unwrap();
|
||||
let pos_index = l.get_pos(value);
|
||||
l[pos_index] = obj;
|
||||
Ok(vm.get_none())
|
||||
if let Some(pos_index) = l.get_pos(value) {
|
||||
l[pos_index] = obj;
|
||||
Ok(vm.get_none())
|
||||
} else {
|
||||
Err(vm.new_index_error("list index out of range".to_string()))
|
||||
}
|
||||
} else {
|
||||
panic!(
|
||||
"TypeError: indexing type {:?} with index {:?} is not supported (yet?)",
|
||||
@@ -51,7 +54,7 @@ fn list_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
};
|
||||
|
||||
Ok(PyObject::new(
|
||||
PyObjectPayload::Sequence { elements: elements },
|
||||
PyObjectPayload::Sequence { elements },
|
||||
cls.clone(),
|
||||
))
|
||||
}
|
||||
@@ -73,6 +76,94 @@ fn list_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
Ok(vm.ctx.new_bool(result))
|
||||
}
|
||||
|
||||
fn list_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [(zelf, Some(vm.ctx.list_type())), (other, None)]
|
||||
);
|
||||
|
||||
let result = if objtype::isinstance(other, &vm.ctx.list_type()) {
|
||||
let zelf = get_elements(zelf);
|
||||
let other = get_elements(other);
|
||||
seq_lt(vm, &zelf, &other)?
|
||||
} else {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"Cannot compare {} and {} using '<'",
|
||||
zelf.borrow(),
|
||||
other.borrow()
|
||||
)));
|
||||
};
|
||||
|
||||
Ok(vm.ctx.new_bool(result))
|
||||
}
|
||||
|
||||
fn list_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [(zelf, Some(vm.ctx.list_type())), (other, None)]
|
||||
);
|
||||
|
||||
let result = if objtype::isinstance(other, &vm.ctx.list_type()) {
|
||||
let zelf = get_elements(zelf);
|
||||
let other = get_elements(other);
|
||||
seq_gt(vm, &zelf, &other)?
|
||||
} else {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"Cannot compare {} and {} using '>'",
|
||||
zelf.borrow(),
|
||||
other.borrow()
|
||||
)));
|
||||
};
|
||||
|
||||
Ok(vm.ctx.new_bool(result))
|
||||
}
|
||||
|
||||
fn list_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [(zelf, Some(vm.ctx.list_type())), (other, None)]
|
||||
);
|
||||
|
||||
let result = if objtype::isinstance(other, &vm.ctx.list_type()) {
|
||||
let zelf = get_elements(zelf);
|
||||
let other = get_elements(other);
|
||||
seq_ge(vm, &zelf, &other)?
|
||||
} else {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"Cannot compare {} and {} using '>='",
|
||||
zelf.borrow(),
|
||||
other.borrow()
|
||||
)));
|
||||
};
|
||||
|
||||
Ok(vm.ctx.new_bool(result))
|
||||
}
|
||||
|
||||
fn list_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [(zelf, Some(vm.ctx.list_type())), (other, None)]
|
||||
);
|
||||
|
||||
let result = if objtype::isinstance(other, &vm.ctx.list_type()) {
|
||||
let zelf = get_elements(zelf);
|
||||
let other = get_elements(other);
|
||||
seq_le(vm, &zelf, &other)?
|
||||
} else {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"Cannot compare {} and {} using '<='",
|
||||
zelf.borrow(),
|
||||
other.borrow()
|
||||
)));
|
||||
};
|
||||
|
||||
Ok(vm.ctx.new_bool(result))
|
||||
}
|
||||
|
||||
fn list_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
@@ -83,7 +174,7 @@ fn list_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
if objtype::isinstance(o2, &vm.ctx.list_type()) {
|
||||
let e1 = get_elements(o);
|
||||
let e2 = get_elements(o2);
|
||||
let elements = e1.iter().chain(e2.iter()).map(|e| e.clone()).collect();
|
||||
let elements = e1.iter().chain(e2.iter()).cloned().collect();
|
||||
Ok(vm.ctx.new_list(elements))
|
||||
} else {
|
||||
Err(vm.new_type_error(format!("Cannot add {} and {}", o.borrow(), o2.borrow())))
|
||||
@@ -135,10 +226,10 @@ fn list_count(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
for element in elements.iter() {
|
||||
let is_eq = vm._eq(element, value.clone())?;
|
||||
if objbool::boolval(vm, is_eq)? {
|
||||
count = count + 1;
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
Ok(vm.context().new_int(count.to_bigint().unwrap()))
|
||||
Ok(vm.context().new_int(count))
|
||||
}
|
||||
|
||||
pub fn list_extend(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
@@ -163,7 +254,7 @@ fn list_index(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
for (index, element) in get_elements(list).iter().enumerate() {
|
||||
let py_equal = vm.call_method(needle, "__eq__", vec![element.clone()])?;
|
||||
if objbool::get_value(&py_equal) {
|
||||
return Ok(vm.context().new_int(index.to_bigint().unwrap()));
|
||||
return Ok(vm.context().new_int(index));
|
||||
}
|
||||
}
|
||||
let needle_str = objstr::get_value(&vm.to_str(needle).unwrap());
|
||||
@@ -174,7 +265,7 @@ fn list_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
trace!("list.len called with: {:?}", args);
|
||||
arg_check!(vm, args, required = [(list, Some(vm.ctx.list_type()))]);
|
||||
let elements = get_elements(list);
|
||||
Ok(vm.context().new_int(elements.len().to_bigint().unwrap()))
|
||||
Ok(vm.context().new_int(elements.len()))
|
||||
}
|
||||
|
||||
fn list_reverse(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
@@ -259,21 +350,29 @@ fn list_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
]
|
||||
);
|
||||
|
||||
let counter = objint::get_value(&product).to_usize().unwrap();
|
||||
|
||||
let elements = get_elements(list);
|
||||
let current_len = elements.len();
|
||||
let mut new_elements = Vec::with_capacity(counter * current_len);
|
||||
|
||||
for _ in 0..counter {
|
||||
new_elements.extend(elements.clone());
|
||||
}
|
||||
let new_elements = seq_mul(&get_elements(list), product);
|
||||
|
||||
Ok(vm.ctx.new_list(new_elements))
|
||||
}
|
||||
|
||||
fn list_pop(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(zelf, Some(vm.ctx.list_type()))]);
|
||||
|
||||
let mut elements = get_mut_elements(zelf);
|
||||
if let Some(result) = elements.pop() {
|
||||
Ok(result)
|
||||
} else {
|
||||
Err(vm.new_index_error("pop from empty list".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
let ref list_type = context.list_type;
|
||||
let list_type = &context.list_type;
|
||||
|
||||
let list_doc = "Built-in mutable sequence.\n\n\
|
||||
If no argument is given, the constructor creates a new empty list.\n\
|
||||
The argument must be an iterable if specified.";
|
||||
|
||||
context.set_attr(&list_type, "__add__", context.new_rustfunc(list_add));
|
||||
context.set_attr(
|
||||
&list_type,
|
||||
@@ -281,6 +380,10 @@ pub fn init(context: &PyContext) {
|
||||
context.new_rustfunc(list_contains),
|
||||
);
|
||||
context.set_attr(&list_type, "__eq__", context.new_rustfunc(list_eq));
|
||||
context.set_attr(&list_type, "__lt__", context.new_rustfunc(list_lt));
|
||||
context.set_attr(&list_type, "__gt__", context.new_rustfunc(list_gt));
|
||||
context.set_attr(&list_type, "__le__", context.new_rustfunc(list_le));
|
||||
context.set_attr(&list_type, "__ge__", context.new_rustfunc(list_ge));
|
||||
context.set_attr(
|
||||
&list_type,
|
||||
"__getitem__",
|
||||
@@ -296,6 +399,7 @@ pub fn init(context: &PyContext) {
|
||||
context.set_attr(&list_type, "__len__", context.new_rustfunc(list_len));
|
||||
context.set_attr(&list_type, "__new__", context.new_rustfunc(list_new));
|
||||
context.set_attr(&list_type, "__repr__", context.new_rustfunc(list_repr));
|
||||
context.set_attr(&list_type, "__doc__", context.new_str(list_doc.to_string()));
|
||||
context.set_attr(&list_type, "append", context.new_rustfunc(list_append));
|
||||
context.set_attr(&list_type, "clear", context.new_rustfunc(list_clear));
|
||||
context.set_attr(&list_type, "count", context.new_rustfunc(list_count));
|
||||
@@ -303,4 +407,5 @@ pub fn init(context: &PyContext) {
|
||||
context.set_attr(&list_type, "index", context.new_rustfunc(list_index));
|
||||
context.set_attr(&list_type, "reverse", context.new_rustfunc(list_reverse));
|
||||
context.set_attr(&list_type, "sort", context.new_rustfunc(list_sort));
|
||||
context.set_attr(&list_type, "pop", context.new_rustfunc(list_pop));
|
||||
}
|
||||
|
||||
67
vm/src/obj/objmap.rs
Normal file
67
vm/src/obj/objmap.rs
Normal file
@@ -0,0 +1,67 @@
|
||||
use super::super::pyobject::{
|
||||
PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol,
|
||||
};
|
||||
use super::super::vm::VirtualMachine;
|
||||
use super::objiter;
|
||||
use super::objtype; // Required for arg_check! to use isinstance
|
||||
|
||||
fn map_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
no_kwargs!(vm, args);
|
||||
let cls = &args.args[0];
|
||||
if args.args.len() < 3 {
|
||||
Err(vm.new_type_error("map() must have at least two arguments.".to_owned()))
|
||||
} else {
|
||||
let function = &args.args[1];
|
||||
let iterables = &args.args[2..];
|
||||
let iterators = iterables
|
||||
.into_iter()
|
||||
.map(|iterable| objiter::get_iter(vm, iterable))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
Ok(PyObject::new(
|
||||
PyObjectPayload::MapIterator {
|
||||
mapper: function.clone(),
|
||||
iterators,
|
||||
},
|
||||
cls.clone(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn map_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(map, Some(vm.ctx.map_type()))]);
|
||||
|
||||
if let PyObjectPayload::MapIterator {
|
||||
ref mut mapper,
|
||||
ref mut iterators,
|
||||
} = map.borrow_mut().payload
|
||||
{
|
||||
let next_objs = iterators
|
||||
.iter()
|
||||
.map(|iterator| objiter::call_next(vm, iterator))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
// the mapper itself can raise StopIteration which does stop the map iteration
|
||||
vm.invoke(
|
||||
mapper.clone(),
|
||||
PyFuncArgs {
|
||||
args: next_objs,
|
||||
kwargs: vec![],
|
||||
},
|
||||
)
|
||||
} else {
|
||||
panic!("map doesn't have correct payload");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
let map_type = &context.map_type;
|
||||
|
||||
let map_doc = "map(func, *iterables) --> map object\n\n\
|
||||
Make an iterator that computes the function using arguments from\n\
|
||||
each of the iterables. Stops when the shortest iterable is exhausted.";
|
||||
|
||||
objiter::iter_type_init(context, map_type);
|
||||
context.set_attr(&map_type, "__new__", context.new_rustfunc(map_new));
|
||||
context.set_attr(&map_type, "__next__", context.new_rustfunc(map_next));
|
||||
context.set_attr(&map_type, "__doc__", context.new_str(map_doc.to_string()));
|
||||
}
|
||||
@@ -17,7 +17,7 @@ pub fn new_memory_view(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
}
|
||||
|
||||
pub fn init(ctx: &PyContext) {
|
||||
let ref memoryview_type = ctx.memoryview_type;
|
||||
let memoryview_type = &ctx.memoryview_type;
|
||||
ctx.set_attr(
|
||||
&memoryview_type,
|
||||
"__new__",
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
use super::super::pyobject::{
|
||||
AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef,
|
||||
PyResult, TypeProtocol,
|
||||
AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef, PyResult,
|
||||
TypeProtocol,
|
||||
};
|
||||
use super::super::vm::VirtualMachine;
|
||||
use super::objbool;
|
||||
use super::objdict;
|
||||
use super::objstr;
|
||||
use super::objtype;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub fn new_instance(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult {
|
||||
// more or less __new__ operator
|
||||
let type_ref = args.shift();
|
||||
let dict = vm.new_dict();
|
||||
let obj = PyObject::new(PyObjectPayload::Instance { dict: dict }, type_ref.clone());
|
||||
let obj = vm.ctx.new_instance(type_ref.clone(), None);
|
||||
Ok(obj)
|
||||
}
|
||||
|
||||
pub fn create_object(type_type: PyObjectRef, object_type: PyObjectRef, dict_type: PyObjectRef) {
|
||||
pub fn create_object(type_type: PyObjectRef, object_type: PyObjectRef, _dict_type: PyObjectRef) {
|
||||
(*object_type.borrow_mut()).payload = PyObjectPayload::Class {
|
||||
name: String::from("object"),
|
||||
dict: objdict::new(dict_type),
|
||||
dict: RefCell::new(HashMap::new()),
|
||||
mro: vec![],
|
||||
};
|
||||
(*object_type.borrow_mut()).typ = Some(type_type.clone());
|
||||
@@ -62,15 +62,14 @@ fn object_delattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
]
|
||||
);
|
||||
|
||||
// Get dict:
|
||||
let dict = match zelf.borrow().payload {
|
||||
PyObjectPayload::Class { ref dict, .. } => dict.clone(),
|
||||
PyObjectPayload::Instance { ref dict, .. } => dict.clone(),
|
||||
_ => return Err(vm.new_type_error("TypeError: no dictionary.".to_string())),
|
||||
};
|
||||
|
||||
// Delete attr from dict:
|
||||
vm.call_method(&dict, "__delitem__", vec![attr.clone()])
|
||||
match zelf.borrow().payload {
|
||||
PyObjectPayload::Class { ref dict, .. } | PyObjectPayload::Instance { ref dict, .. } => {
|
||||
let attr_name = objstr::get_value(attr);
|
||||
dict.borrow_mut().remove(&attr_name);
|
||||
Ok(vm.get_none())
|
||||
}
|
||||
_ => Err(vm.new_type_error("TypeError: no dictionary.".to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
fn object_str(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
@@ -86,7 +85,9 @@ fn object_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
}
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
let ref object = context.object;
|
||||
let object = &context.object;
|
||||
let object_doc = "The most base type";
|
||||
|
||||
context.set_attr(&object, "__new__", context.new_rustfunc(new_instance));
|
||||
context.set_attr(&object, "__init__", context.new_rustfunc(object_init));
|
||||
context.set_attr(&object, "__eq__", context.new_rustfunc(object_eq));
|
||||
@@ -105,6 +106,7 @@ pub fn init(context: &PyContext) {
|
||||
"__getattribute__",
|
||||
context.new_rustfunc(object_getattribute),
|
||||
);
|
||||
context.set_attr(&object, "__doc__", context.new_str(object_doc.to_string()));
|
||||
}
|
||||
|
||||
fn object_init(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult {
|
||||
@@ -113,8 +115,13 @@ fn object_init(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult {
|
||||
|
||||
fn object_dict(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
match args.args[0].borrow().payload {
|
||||
PyObjectPayload::Class { ref dict, .. } => Ok(dict.clone()),
|
||||
PyObjectPayload::Instance { ref dict, .. } => Ok(dict.clone()),
|
||||
PyObjectPayload::Class { ref dict, .. } | PyObjectPayload::Instance { ref dict, .. } => {
|
||||
let new_dict = vm.new_dict();
|
||||
for (attr, value) in dict.borrow().iter() {
|
||||
vm.ctx.set_item(&new_dict, &attr, value.clone());
|
||||
}
|
||||
Ok(new_dict)
|
||||
}
|
||||
_ => Err(vm.new_type_error("TypeError: no dictionary.".to_string())),
|
||||
}
|
||||
}
|
||||
@@ -151,21 +158,19 @@ fn object_getattribute(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
Ok(obj_attr)
|
||||
} else if let Some(attr) = cls.get_attr(&name) {
|
||||
vm.call_get_descriptor(attr, obj.clone())
|
||||
} else if let Some(getter) = cls.get_attr("__getattr__") {
|
||||
vm.invoke(
|
||||
getter,
|
||||
PyFuncArgs {
|
||||
args: vec![cls, name_str.clone()],
|
||||
kwargs: vec![],
|
||||
},
|
||||
)
|
||||
} else {
|
||||
if let Some(getter) = cls.get_attr("__getattr__") {
|
||||
vm.invoke(
|
||||
getter,
|
||||
PyFuncArgs {
|
||||
args: vec![cls, name_str.clone()],
|
||||
kwargs: vec![],
|
||||
},
|
||||
)
|
||||
} else {
|
||||
let attribute_error = vm.context().exceptions.attribute_error.clone();
|
||||
Err(vm.new_exception(
|
||||
attribute_error,
|
||||
format!("{} has no attribute '{}'", obj.borrow(), name),
|
||||
))
|
||||
}
|
||||
let attribute_error = vm.context().exceptions.attribute_error.clone();
|
||||
Err(vm.new_exception(
|
||||
attribute_error,
|
||||
format!("{} has no attribute '{}'", obj.borrow(), name),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,14 +2,41 @@
|
||||
|
||||
*/
|
||||
|
||||
use super::super::pyobject::{
|
||||
PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol,
|
||||
};
|
||||
use super::super::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol};
|
||||
use super::super::vm::VirtualMachine;
|
||||
use super::objtype;
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
let ref property_type = context.property_type;
|
||||
let property_type = &context.property_type;
|
||||
|
||||
let property_doc =
|
||||
"Property attribute.\n\n \
|
||||
fget\n \
|
||||
function to be used for getting an attribute value\n \
|
||||
fset\n \
|
||||
function to be used for setting an attribute value\n \
|
||||
fdel\n \
|
||||
function to be used for del\'ing an attribute\n \
|
||||
doc\n \
|
||||
docstring\n\n\
|
||||
Typical use is to define a managed attribute x:\n\n\
|
||||
class C(object):\n \
|
||||
def getx(self): return self._x\n \
|
||||
def setx(self, value): self._x = value\n \
|
||||
def delx(self): del self._x\n \
|
||||
x = property(getx, setx, delx, \"I\'m the \'x\' property.\")\n\n\
|
||||
Decorators make defining new properties or modifying existing ones easy:\n\n\
|
||||
class C(object):\n \
|
||||
@property\n \
|
||||
def x(self):\n \"I am the \'x\' property.\"\n \
|
||||
return self._x\n \
|
||||
@x.setter\n \
|
||||
def x(self, value):\n \
|
||||
self._x = value\n \
|
||||
@x.deleter\n \
|
||||
def x(self):\n \
|
||||
del self._x";
|
||||
|
||||
context.set_attr(
|
||||
&property_type,
|
||||
"__get__",
|
||||
@@ -20,6 +47,11 @@ pub fn init(context: &PyContext) {
|
||||
"__new__",
|
||||
context.new_rustfunc(property_new),
|
||||
);
|
||||
context.set_attr(
|
||||
&property_type,
|
||||
"__doc__",
|
||||
context.new_str(property_doc.to_string()),
|
||||
);
|
||||
// TODO: how to handle __set__ ?
|
||||
}
|
||||
|
||||
@@ -55,12 +87,7 @@ fn property_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
trace!("property.__new__ {:?}", args.args);
|
||||
arg_check!(vm, args, required = [(cls, None), (fget, None)]);
|
||||
|
||||
let py_obj = PyObject::new(
|
||||
PyObjectPayload::Instance {
|
||||
dict: vm.ctx.new_dict(),
|
||||
},
|
||||
cls.clone(),
|
||||
);
|
||||
let py_obj = vm.ctx.new_instance(cls.clone(), None);
|
||||
vm.ctx.set_attr(&py_obj, "fget", fget.clone());
|
||||
Ok(py_obj)
|
||||
}
|
||||
|
||||
368
vm/src/obj/objrange.rs
Normal file
368
vm/src/obj/objrange.rs
Normal file
@@ -0,0 +1,368 @@
|
||||
use super::super::pyobject::{
|
||||
PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol,
|
||||
};
|
||||
use super::super::vm::VirtualMachine;
|
||||
use super::objint;
|
||||
use super::objtype;
|
||||
use num_bigint::{BigInt, Sign};
|
||||
use num_integer::Integer;
|
||||
use num_traits::{One, Signed, ToPrimitive, Zero};
|
||||
use std::ops::Mul;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RangeType {
|
||||
// Unfortunately Rust's built in range type doesn't support things like indexing
|
||||
// or ranges where start > end so we need to roll our own.
|
||||
pub start: BigInt,
|
||||
pub end: BigInt,
|
||||
pub step: BigInt,
|
||||
}
|
||||
|
||||
impl RangeType {
|
||||
#[inline]
|
||||
pub fn try_len(&self) -> Option<usize> {
|
||||
match self.step.sign() {
|
||||
Sign::Plus if self.start < self.end => ((&self.end - &self.start - 1usize)
|
||||
/ &self.step)
|
||||
.to_usize()
|
||||
.map(|sz| sz + 1),
|
||||
Sign::Minus if self.start > self.end => ((&self.start - &self.end - 1usize)
|
||||
/ (-&self.step))
|
||||
.to_usize()
|
||||
.map(|sz| sz + 1),
|
||||
_ => Some(0),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.try_len().unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn offset(&self, value: &BigInt) -> Option<BigInt> {
|
||||
match self.step.sign() {
|
||||
Sign::Plus if value >= &self.start && value < &self.end => Some(value - &self.start),
|
||||
Sign::Minus if value <= &self.start && value > &self.end => Some(&self.start - value),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn contains(&self, value: &BigInt) -> bool {
|
||||
match self.offset(value) {
|
||||
Some(ref offset) => offset.is_multiple_of(&self.step),
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn index_of(&self, value: &BigInt) -> Option<BigInt> {
|
||||
match self.offset(value) {
|
||||
Some(ref offset) if offset.is_multiple_of(&self.step) => {
|
||||
Some((offset / &self.step).abs())
|
||||
}
|
||||
Some(_) | None => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
(self.start <= self.end && self.step.is_negative())
|
||||
|| (self.start >= self.end && self.step.is_positive())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn forward(&self) -> bool {
|
||||
self.start < self.end
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get<'a, T>(&'a self, index: T) -> Option<BigInt>
|
||||
where
|
||||
&'a BigInt: Mul<T, Output = BigInt>,
|
||||
{
|
||||
let result = &self.start + &self.step * index;
|
||||
|
||||
if (self.forward() && !self.is_empty() && result < self.end)
|
||||
|| (!self.forward() && !self.is_empty() && result > self.end)
|
||||
{
|
||||
Some(result)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn reversed(&self) -> Self {
|
||||
match self.step.sign() {
|
||||
Sign::Plus => RangeType {
|
||||
start: &self.end - 1,
|
||||
end: &self.start - 1,
|
||||
step: -&self.step,
|
||||
},
|
||||
Sign::Minus => RangeType {
|
||||
start: &self.end + 1,
|
||||
end: &self.start + 1,
|
||||
step: -&self.step,
|
||||
},
|
||||
Sign::NoSign => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn repr(&self) -> String {
|
||||
if self.step == BigInt::one() {
|
||||
format!("range({}, {})", self.start, self.end)
|
||||
} else {
|
||||
format!("range({}, {}, {})", self.start, self.end, self.step)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
let ref range_type = context.range_type;
|
||||
|
||||
let range_doc = "range(stop) -> range object\n\
|
||||
range(start, stop[, step]) -> range object\n\n\
|
||||
Return an object that produces a sequence of integers from start (inclusive)\n\
|
||||
to stop (exclusive) by step. range(i, j) produces i, i+1, i+2, ..., j-1.\n\
|
||||
start defaults to 0, and stop is omitted! range(4) produces 0, 1, 2, 3.\n\
|
||||
These are exactly the valid indices for a list of 4 elements.\n\
|
||||
When step is given, it specifies the increment (or decrement).";
|
||||
|
||||
context.set_attr(&range_type, "__new__", context.new_rustfunc(range_new));
|
||||
context.set_attr(&range_type, "__iter__", context.new_rustfunc(range_iter));
|
||||
context.set_attr(
|
||||
&range_type,
|
||||
"__reversed__",
|
||||
context.new_rustfunc(range_reversed),
|
||||
);
|
||||
context.set_attr(
|
||||
&range_type,
|
||||
"__doc__",
|
||||
context.new_str(range_doc.to_string()),
|
||||
);
|
||||
context.set_attr(&range_type, "__len__", context.new_rustfunc(range_len));
|
||||
context.set_attr(
|
||||
&range_type,
|
||||
"__getitem__",
|
||||
context.new_rustfunc(range_getitem),
|
||||
);
|
||||
context.set_attr(&range_type, "__repr__", context.new_rustfunc(range_repr));
|
||||
context.set_attr(&range_type, "__bool__", context.new_rustfunc(range_bool));
|
||||
context.set_attr(
|
||||
&range_type,
|
||||
"__contains__",
|
||||
context.new_rustfunc(range_contains),
|
||||
);
|
||||
context.set_attr(&range_type, "index", context.new_rustfunc(range_index));
|
||||
}
|
||||
|
||||
fn range_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [(cls, None), (first, Some(vm.ctx.int_type()))],
|
||||
optional = [
|
||||
(second, Some(vm.ctx.int_type())),
|
||||
(step, Some(vm.ctx.int_type()))
|
||||
]
|
||||
);
|
||||
|
||||
let start = if let Some(_) = second {
|
||||
objint::get_value(first)
|
||||
} else {
|
||||
BigInt::zero()
|
||||
};
|
||||
|
||||
let end = if let Some(pyint) = second {
|
||||
objint::get_value(pyint)
|
||||
} else {
|
||||
objint::get_value(first)
|
||||
};
|
||||
|
||||
let step = if let Some(pyint) = step {
|
||||
objint::get_value(pyint)
|
||||
} else {
|
||||
BigInt::one()
|
||||
};
|
||||
|
||||
if step.is_zero() {
|
||||
Err(vm.new_value_error("range with 0 step size".to_string()))
|
||||
} else {
|
||||
Ok(PyObject::new(
|
||||
PyObjectPayload::Range {
|
||||
range: RangeType { start, end, step },
|
||||
},
|
||||
cls.clone(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn range_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(range, Some(vm.ctx.range_type()))]);
|
||||
|
||||
Ok(PyObject::new(
|
||||
PyObjectPayload::Iterator {
|
||||
position: 0,
|
||||
iterated_obj: range.clone(),
|
||||
},
|
||||
vm.ctx.iter_type(),
|
||||
))
|
||||
}
|
||||
|
||||
fn range_reversed(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]);
|
||||
|
||||
let range = match zelf.borrow().payload {
|
||||
PyObjectPayload::Range { ref range } => range.reversed(),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
Ok(PyObject::new(
|
||||
PyObjectPayload::Iterator {
|
||||
position: 0,
|
||||
iterated_obj: PyObject::new(PyObjectPayload::Range { range }, vm.ctx.range_type()),
|
||||
},
|
||||
vm.ctx.iter_type(),
|
||||
))
|
||||
}
|
||||
|
||||
fn range_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]);
|
||||
|
||||
if let Some(len) = match zelf.borrow().payload {
|
||||
PyObjectPayload::Range { ref range } => range.try_len(),
|
||||
_ => unreachable!(),
|
||||
} {
|
||||
Ok(vm.ctx.new_int(len))
|
||||
} else {
|
||||
Err(vm.new_overflow_error("Python int too large to convert to Rust usize".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [(zelf, Some(vm.ctx.range_type())), (subscript, None)]
|
||||
);
|
||||
let zrange = if let PyObjectPayload::Range { ref range } = zelf.borrow().payload {
|
||||
range.clone()
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
match subscript.borrow().payload {
|
||||
PyObjectPayload::Integer { ref value } => {
|
||||
if let Some(int) = zrange.get(value) {
|
||||
Ok(vm.ctx.new_int(int))
|
||||
} else {
|
||||
Err(vm.new_index_error("range object index out of range".to_string()))
|
||||
}
|
||||
}
|
||||
PyObjectPayload::Slice {
|
||||
ref start,
|
||||
ref stop,
|
||||
ref step,
|
||||
} => {
|
||||
let new_start = if let Some(int) = start {
|
||||
if let Some(i) = zrange.get(int) {
|
||||
i
|
||||
} else {
|
||||
zrange.start.clone()
|
||||
}
|
||||
} else {
|
||||
zrange.start.clone()
|
||||
};
|
||||
|
||||
let new_end = if let Some(int) = stop {
|
||||
if let Some(i) = zrange.get(int) {
|
||||
i
|
||||
} else {
|
||||
zrange.end
|
||||
}
|
||||
} else {
|
||||
zrange.end
|
||||
};
|
||||
|
||||
let new_step = if let Some(int) = step {
|
||||
int * zrange.step
|
||||
} else {
|
||||
zrange.step
|
||||
};
|
||||
|
||||
Ok(PyObject::new(
|
||||
PyObjectPayload::Range {
|
||||
range: RangeType {
|
||||
start: new_start,
|
||||
end: new_end,
|
||||
step: new_step,
|
||||
},
|
||||
},
|
||||
vm.ctx.range_type(),
|
||||
))
|
||||
}
|
||||
|
||||
_ => Err(vm.new_type_error("range indices must be integer or slice".to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
fn range_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]);
|
||||
|
||||
let s = match zelf.borrow().payload {
|
||||
PyObjectPayload::Range { ref range } => range.repr(),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
Ok(vm.ctx.new_str(s))
|
||||
}
|
||||
|
||||
fn range_bool(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]);
|
||||
|
||||
let len = match zelf.borrow().payload {
|
||||
PyObjectPayload::Range { ref range } => range.len(),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
Ok(vm.ctx.new_bool(len > 0))
|
||||
}
|
||||
|
||||
fn range_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [(zelf, Some(vm.ctx.range_type())), (needle, None)]
|
||||
);
|
||||
|
||||
if let PyObjectPayload::Range { ref range } = zelf.borrow().payload {
|
||||
Ok(vm.ctx.new_bool(match needle.borrow().payload {
|
||||
PyObjectPayload::Integer { ref value } => range.contains(value),
|
||||
_ => false,
|
||||
}))
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
fn range_index(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [(zelf, Some(vm.ctx.range_type())), (needle, None)]
|
||||
);
|
||||
|
||||
if let PyObjectPayload::Range { ref range } = zelf.borrow().payload {
|
||||
match needle.borrow().payload {
|
||||
PyObjectPayload::Integer { ref value } => match range.index_of(value) {
|
||||
Some(idx) => Ok(vm.ctx.new_int(idx)),
|
||||
None => Err(vm.new_value_error(format!("{} is not in range", value))),
|
||||
},
|
||||
_ => Err(vm.new_value_error("sequence.index(x): x not in sequence".to_string())),
|
||||
}
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
@@ -1,48 +1,98 @@
|
||||
use super::super::pyobject::{PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol};
|
||||
use super::super::vm::VirtualMachine;
|
||||
use super::objbool;
|
||||
use num_traits::ToPrimitive;
|
||||
use super::objint;
|
||||
use num_bigint::BigInt;
|
||||
use num_traits::{One, Signed, ToPrimitive, Zero};
|
||||
use std::cell::{Ref, RefMut};
|
||||
use std::marker::Sized;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::ops::{Deref, DerefMut, Range};
|
||||
|
||||
pub trait PySliceableSequence {
|
||||
fn do_slice(&self, start: usize, stop: usize) -> Self;
|
||||
fn do_stepped_slice(&self, start: usize, stop: usize, step: usize) -> Self;
|
||||
fn do_slice(&self, range: Range<usize>) -> Self;
|
||||
fn do_slice_reverse(&self, range: Range<usize>) -> Self;
|
||||
fn do_stepped_slice(&self, range: Range<usize>, step: usize) -> Self;
|
||||
fn do_stepped_slice_reverse(&self, range: Range<usize>, step: usize) -> Self;
|
||||
fn empty() -> Self;
|
||||
fn len(&self) -> usize;
|
||||
fn get_pos(&self, p: i32) -> usize {
|
||||
fn get_pos(&self, p: i32) -> Option<usize> {
|
||||
if p < 0 {
|
||||
self.len() - ((-p) as usize)
|
||||
} else if p as usize > self.len() {
|
||||
// This is for the slicing case where the end element is greater than the length of the
|
||||
// sequence
|
||||
self.len()
|
||||
if -p as usize > self.len() {
|
||||
None
|
||||
} else {
|
||||
Some(self.len() - ((-p) as usize))
|
||||
}
|
||||
} else if p as usize >= self.len() {
|
||||
None
|
||||
} else {
|
||||
p as usize
|
||||
Some(p as usize)
|
||||
}
|
||||
}
|
||||
fn get_slice_items(&self, slice: &PyObjectRef) -> Self
|
||||
|
||||
fn get_slice_pos(&self, slice_pos: &BigInt) -> usize {
|
||||
if let Some(pos) = slice_pos.to_i32() {
|
||||
if let Some(index) = self.get_pos(pos) {
|
||||
// within bounds
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
if slice_pos.is_negative() {
|
||||
0
|
||||
} else {
|
||||
self.len()
|
||||
}
|
||||
}
|
||||
|
||||
fn get_slice_range(&self, start: &Option<BigInt>, stop: &Option<BigInt>) -> Range<usize> {
|
||||
let start = start.as_ref().map(|x| self.get_slice_pos(x)).unwrap_or(0);
|
||||
let stop = stop
|
||||
.as_ref()
|
||||
.map(|x| self.get_slice_pos(x))
|
||||
.unwrap_or(self.len());
|
||||
|
||||
start..stop
|
||||
}
|
||||
|
||||
fn get_slice_items(
|
||||
&self,
|
||||
vm: &mut VirtualMachine,
|
||||
slice: &PyObjectRef,
|
||||
) -> Result<Self, PyObjectRef>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
// TODO: we could potentially avoid this copy and use slice
|
||||
match &(slice.borrow()).payload {
|
||||
PyObjectPayload::Slice { start, stop, step } => {
|
||||
let start = match start {
|
||||
&Some(start) => self.get_pos(start),
|
||||
&None => 0,
|
||||
};
|
||||
let stop = match stop {
|
||||
&Some(stop) => self.get_pos(stop),
|
||||
&None => self.len() as usize,
|
||||
};
|
||||
match step {
|
||||
&None | &Some(1) => self.do_slice(start, stop),
|
||||
&Some(num) => {
|
||||
if num < 0 {
|
||||
unimplemented!("negative step indexing not yet supported")
|
||||
};
|
||||
self.do_stepped_slice(start, stop, num as usize)
|
||||
let step = step.clone().unwrap_or(BigInt::one());
|
||||
if step.is_zero() {
|
||||
Err(vm.new_value_error("slice step cannot be zero".to_string()))
|
||||
} else if step.is_positive() {
|
||||
let range = self.get_slice_range(start, stop);
|
||||
if range.start < range.end {
|
||||
match step.to_i32() {
|
||||
Some(1) => Ok(self.do_slice(range)),
|
||||
Some(num) => Ok(self.do_stepped_slice(range, num as usize)),
|
||||
None => Ok(self.do_slice(range.start..range.start + 1)),
|
||||
}
|
||||
} else {
|
||||
Ok(Self::empty())
|
||||
}
|
||||
} else {
|
||||
// calculate the range for the reverse slice, first the bounds needs to be made
|
||||
// exclusive around stop, the lower number
|
||||
let start = start.as_ref().map(|x| x + 1);
|
||||
let stop = stop.as_ref().map(|x| x + 1);
|
||||
let range = self.get_slice_range(&stop, &start);
|
||||
if range.start < range.end {
|
||||
match (-step).to_i32() {
|
||||
Some(1) => Ok(self.do_slice_reverse(range)),
|
||||
Some(num) => Ok(self.do_stepped_slice_reverse(range, num as usize)),
|
||||
None => Ok(self.do_slice(range.end - 1..range.end)),
|
||||
}
|
||||
} else {
|
||||
Ok(Self::empty())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,13 +101,29 @@ pub trait PySliceableSequence {
|
||||
}
|
||||
}
|
||||
|
||||
impl PySliceableSequence for Vec<PyObjectRef> {
|
||||
fn do_slice(&self, start: usize, stop: usize) -> Self {
|
||||
self[start..stop].to_vec()
|
||||
impl<T: Clone> PySliceableSequence for Vec<T> {
|
||||
fn do_slice(&self, range: Range<usize>) -> Self {
|
||||
self[range].to_vec()
|
||||
}
|
||||
fn do_stepped_slice(&self, start: usize, stop: usize, step: usize) -> Self {
|
||||
self[start..stop].iter().step_by(step).cloned().collect()
|
||||
|
||||
fn do_slice_reverse(&self, range: Range<usize>) -> Self {
|
||||
let mut slice = self[range].to_vec();
|
||||
slice.reverse();
|
||||
slice
|
||||
}
|
||||
|
||||
fn do_stepped_slice(&self, range: Range<usize>, step: usize) -> Self {
|
||||
self[range].iter().step_by(step).cloned().collect()
|
||||
}
|
||||
|
||||
fn do_stepped_slice_reverse(&self, range: Range<usize>, step: usize) -> Self {
|
||||
self[range].iter().rev().step_by(step).cloned().collect()
|
||||
}
|
||||
|
||||
fn empty() -> Self {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.len()
|
||||
}
|
||||
@@ -70,25 +136,24 @@ pub fn get_item(
|
||||
subscript: PyObjectRef,
|
||||
) -> PyResult {
|
||||
match &(subscript.borrow()).payload {
|
||||
PyObjectPayload::Integer { value } => {
|
||||
let value = value.to_i32().unwrap();
|
||||
let pos_index = elements.to_vec().get_pos(value);
|
||||
if pos_index < elements.len() {
|
||||
let obj = elements[pos_index].clone();
|
||||
Ok(obj)
|
||||
} else {
|
||||
let value_error = vm.context().exceptions.value_error.clone();
|
||||
Err(vm.new_exception(value_error, "Index out of bounds!".to_string()))
|
||||
PyObjectPayload::Integer { value } => match value.to_i32() {
|
||||
Some(value) => {
|
||||
if let Some(pos_index) = elements.to_vec().get_pos(value) {
|
||||
let obj = elements[pos_index].clone();
|
||||
Ok(obj)
|
||||
} else {
|
||||
Err(vm.new_index_error("Index out of bounds!".to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
PyObjectPayload::Slice {
|
||||
start: _,
|
||||
stop: _,
|
||||
step: _,
|
||||
} => Ok(PyObject::new(
|
||||
None => {
|
||||
Err(vm.new_index_error("cannot fit 'int' into an index-sized integer".to_string()))
|
||||
}
|
||||
},
|
||||
|
||||
PyObjectPayload::Slice { .. } => Ok(PyObject::new(
|
||||
match &(sequence.borrow()).payload {
|
||||
PyObjectPayload::Sequence { elements: _ } => PyObjectPayload::Sequence {
|
||||
elements: elements.to_vec().get_slice_items(&subscript),
|
||||
PyObjectPayload::Sequence { .. } => PyObjectPayload::Sequence {
|
||||
elements: elements.to_vec().get_slice_items(vm, &subscript)?,
|
||||
},
|
||||
ref payload => panic!("sequence get_item called for non-sequence: {:?}", payload),
|
||||
},
|
||||
@@ -103,8 +168,8 @@ pub fn get_item(
|
||||
|
||||
pub fn seq_equal(
|
||||
vm: &mut VirtualMachine,
|
||||
zelf: &Vec<PyObjectRef>,
|
||||
other: &Vec<PyObjectRef>,
|
||||
zelf: &[PyObjectRef],
|
||||
other: &[PyObjectRef],
|
||||
) -> Result<bool, PyObjectRef> {
|
||||
if zelf.len() == other.len() {
|
||||
for (a, b) in Iterator::zip(zelf.iter(), other.iter()) {
|
||||
@@ -120,6 +185,115 @@ pub fn seq_equal(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn seq_lt(
|
||||
vm: &mut VirtualMachine,
|
||||
zelf: &[PyObjectRef],
|
||||
other: &[PyObjectRef],
|
||||
) -> Result<bool, PyObjectRef> {
|
||||
if zelf.len() == other.len() {
|
||||
for (a, b) in Iterator::zip(zelf.iter(), other.iter()) {
|
||||
let lt = vm.call_method(&a.clone(), "__lt__", vec![b.clone()])?;
|
||||
let value = objbool::boolval(vm, lt)?;
|
||||
if !value {
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
Ok(true)
|
||||
} else {
|
||||
// This case is more complicated because it can still return true if
|
||||
// `zelf` is the head of `other` e.g. [1,2,3] < [1,2,3,4] should return true
|
||||
let mut head = true; // true if `zelf` is the head of `other`
|
||||
|
||||
for (a, b) in Iterator::zip(zelf.iter(), other.iter()) {
|
||||
let lt = vm.call_method(&a.clone(), "__lt__", vec![b.clone()])?;
|
||||
let eq = vm.call_method(&a.clone(), "__eq__", vec![b.clone()])?;
|
||||
let lt_value = objbool::boolval(vm, lt)?;
|
||||
let eq_value = objbool::boolval(vm, eq)?;
|
||||
|
||||
if !lt_value && !eq_value {
|
||||
return Ok(false);
|
||||
} else if !eq_value {
|
||||
head = false;
|
||||
}
|
||||
}
|
||||
|
||||
if head {
|
||||
Ok(zelf.len() < other.len())
|
||||
} else {
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn seq_gt(
|
||||
vm: &mut VirtualMachine,
|
||||
zelf: &[PyObjectRef],
|
||||
other: &[PyObjectRef],
|
||||
) -> Result<bool, PyObjectRef> {
|
||||
if zelf.len() == other.len() {
|
||||
for (a, b) in Iterator::zip(zelf.iter(), other.iter()) {
|
||||
let gt = vm.call_method(&a.clone(), "__gt__", vec![b.clone()])?;
|
||||
let value = objbool::boolval(vm, gt)?;
|
||||
if !value {
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
Ok(true)
|
||||
} else {
|
||||
let mut head = true; // true if `other` is the head of `zelf`
|
||||
for (a, b) in Iterator::zip(zelf.iter(), other.iter()) {
|
||||
// This case is more complicated because it can still return true if
|
||||
// `other` is the head of `zelf` e.g. [1,2,3,4] > [1,2,3] should return true
|
||||
let gt = vm.call_method(&a.clone(), "__gt__", vec![b.clone()])?;
|
||||
let eq = vm.call_method(&a.clone(), "__eq__", vec![b.clone()])?;
|
||||
let gt_value = objbool::boolval(vm, gt)?;
|
||||
let eq_value = objbool::boolval(vm, eq)?;
|
||||
|
||||
if !gt_value && !eq_value {
|
||||
return Ok(false);
|
||||
} else if !eq_value {
|
||||
head = false;
|
||||
}
|
||||
}
|
||||
|
||||
if head {
|
||||
Ok(zelf.len() > other.len())
|
||||
} else {
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn seq_ge(
|
||||
vm: &mut VirtualMachine,
|
||||
zelf: &[PyObjectRef],
|
||||
other: &[PyObjectRef],
|
||||
) -> Result<bool, PyObjectRef> {
|
||||
Ok(seq_gt(vm, zelf, other)? || seq_equal(vm, zelf, other)?)
|
||||
}
|
||||
|
||||
pub fn seq_le(
|
||||
vm: &mut VirtualMachine,
|
||||
zelf: &[PyObjectRef],
|
||||
other: &[PyObjectRef],
|
||||
) -> Result<bool, PyObjectRef> {
|
||||
Ok(seq_lt(vm, zelf, other)? || seq_equal(vm, zelf, other)?)
|
||||
}
|
||||
|
||||
pub fn seq_mul(elements: &[PyObjectRef], product: &PyObjectRef) -> Vec<PyObjectRef> {
|
||||
let counter = objint::get_value(&product).to_isize().unwrap();
|
||||
|
||||
let current_len = elements.len();
|
||||
let new_len = counter.max(0) as usize * current_len;
|
||||
let mut new_elements = Vec::with_capacity(new_len);
|
||||
|
||||
for _ in 0..counter {
|
||||
new_elements.extend(elements.clone().to_owned());
|
||||
}
|
||||
|
||||
new_elements
|
||||
}
|
||||
|
||||
pub fn get_elements<'a>(obj: &'a PyObjectRef) -> impl Deref<Target = Vec<PyObjectRef>> + 'a {
|
||||
Ref::map(obj.borrow(), |x| {
|
||||
if let PyObjectPayload::Sequence { ref elements } = x.payload {
|
||||
|
||||
@@ -11,7 +11,6 @@ use super::objiter;
|
||||
use super::objstr;
|
||||
use super::objtype;
|
||||
use num_bigint::BigInt;
|
||||
use num_bigint::ToBigInt;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub fn get_elements(obj: &PyObjectRef) -> HashMap<BigInt, PyObjectRef> {
|
||||
@@ -130,20 +129,15 @@ fn set_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
Some(iterable) => {
|
||||
let mut elements = HashMap::new();
|
||||
let iterator = objiter::get_iter(vm, iterable)?;
|
||||
loop {
|
||||
match vm.call_method(&iterator, "__next__", vec![]) {
|
||||
Ok(v) => {
|
||||
insert_into_set(vm, &mut elements, &v).unwrap();
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
while let Ok(v) = vm.call_method(&iterator, "__next__", vec![]) {
|
||||
insert_into_set(vm, &mut elements, &v).unwrap();
|
||||
}
|
||||
elements
|
||||
}
|
||||
};
|
||||
|
||||
Ok(PyObject::new(
|
||||
PyObjectPayload::Set { elements: elements },
|
||||
PyObjectPayload::Set { elements },
|
||||
cls.clone(),
|
||||
))
|
||||
}
|
||||
@@ -152,14 +146,14 @@ fn set_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
trace!("set.len called with: {:?}", args);
|
||||
arg_check!(vm, args, required = [(s, Some(vm.ctx.set_type()))]);
|
||||
let elements = get_elements(s);
|
||||
Ok(vm.context().new_int(elements.len().to_bigint().unwrap()))
|
||||
Ok(vm.context().new_int(elements.len()))
|
||||
}
|
||||
|
||||
fn set_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(o, Some(vm.ctx.set_type()))]);
|
||||
|
||||
let elements = get_elements(o);
|
||||
let s = if elements.len() == 0 {
|
||||
let s = if elements.is_empty() {
|
||||
"set()".to_string()
|
||||
} else {
|
||||
let mut str_parts = vec![];
|
||||
@@ -193,11 +187,104 @@ pub fn set_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
Ok(vm.new_bool(false))
|
||||
}
|
||||
|
||||
fn set_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
return set_compare_inner(
|
||||
vm,
|
||||
args,
|
||||
&|zelf: usize, other: usize| -> bool { zelf != other },
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
fn set_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
return set_compare_inner(
|
||||
vm,
|
||||
args,
|
||||
&|zelf: usize, other: usize| -> bool { zelf < other },
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
fn set_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
return set_compare_inner(
|
||||
vm,
|
||||
args,
|
||||
&|zelf: usize, other: usize| -> bool { zelf <= other },
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
fn set_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
return set_compare_inner(
|
||||
vm,
|
||||
args,
|
||||
&|zelf: usize, other: usize| -> bool { zelf < other },
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
fn set_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
return set_compare_inner(
|
||||
vm,
|
||||
args,
|
||||
&|zelf: usize, other: usize| -> bool { zelf <= other },
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
fn set_compare_inner(
|
||||
vm: &mut VirtualMachine,
|
||||
args: PyFuncArgs,
|
||||
size_func: &Fn(usize, usize) -> bool,
|
||||
swap: bool,
|
||||
) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [
|
||||
(zelf, Some(vm.ctx.set_type())),
|
||||
(other, Some(vm.ctx.set_type()))
|
||||
]
|
||||
);
|
||||
|
||||
let get_zelf = |swap: bool| -> &PyObjectRef {
|
||||
if swap {
|
||||
other
|
||||
} else {
|
||||
zelf
|
||||
}
|
||||
};
|
||||
let get_other = |swap: bool| -> &PyObjectRef {
|
||||
if swap {
|
||||
zelf
|
||||
} else {
|
||||
other
|
||||
}
|
||||
};
|
||||
|
||||
let zelf_elements = get_elements(get_zelf(swap));
|
||||
let other_elements = get_elements(get_other(swap));
|
||||
if size_func(zelf_elements.len(), other_elements.len()) {
|
||||
return Ok(vm.new_bool(false));
|
||||
}
|
||||
for element in other_elements.iter() {
|
||||
match vm.call_method(get_zelf(swap), "__contains__", vec![element.1.clone()]) {
|
||||
Ok(value) => {
|
||||
if !objbool::get_value(&value) {
|
||||
return Ok(vm.new_bool(false));
|
||||
}
|
||||
}
|
||||
Err(_) => return Err(vm.new_type_error("".to_string())),
|
||||
}
|
||||
}
|
||||
Ok(vm.new_bool(true))
|
||||
}
|
||||
|
||||
fn frozenset_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(o, Some(vm.ctx.frozenset_type()))]);
|
||||
|
||||
let elements = get_elements(o);
|
||||
let s = if elements.len() == 0 {
|
||||
let s = if elements.is_empty() {
|
||||
"frozenset()".to_string()
|
||||
} else {
|
||||
let mut str_parts = vec![];
|
||||
@@ -212,7 +299,12 @@ fn frozenset_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
}
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
let ref set_type = context.set_type;
|
||||
let set_type = &context.set_type;
|
||||
|
||||
let set_doc = "set() -> new empty set object\n\
|
||||
set(iterable) -> new set object\n\n\
|
||||
Build an unordered collection of unique elements.";
|
||||
|
||||
context.set_attr(
|
||||
&set_type,
|
||||
"__contains__",
|
||||
@@ -221,16 +313,34 @@ pub fn init(context: &PyContext) {
|
||||
context.set_attr(&set_type, "__len__", context.new_rustfunc(set_len));
|
||||
context.set_attr(&set_type, "__new__", context.new_rustfunc(set_new));
|
||||
context.set_attr(&set_type, "__repr__", context.new_rustfunc(set_repr));
|
||||
context.set_attr(&set_type, "__eq__", context.new_rustfunc(set_eq));
|
||||
context.set_attr(&set_type, "__ge__", context.new_rustfunc(set_ge));
|
||||
context.set_attr(&set_type, "__gt__", context.new_rustfunc(set_gt));
|
||||
context.set_attr(&set_type, "__le__", context.new_rustfunc(set_le));
|
||||
context.set_attr(&set_type, "__lt__", context.new_rustfunc(set_lt));
|
||||
context.set_attr(&set_type, "issubset", context.new_rustfunc(set_le));
|
||||
context.set_attr(&set_type, "issuperset", context.new_rustfunc(set_ge));
|
||||
context.set_attr(&set_type, "__doc__", context.new_str(set_doc.to_string()));
|
||||
context.set_attr(&set_type, "add", context.new_rustfunc(set_add));
|
||||
context.set_attr(&set_type, "remove", context.new_rustfunc(set_remove));
|
||||
|
||||
let ref frozenset_type = context.frozenset_type;
|
||||
let frozenset_type = &context.frozenset_type;
|
||||
|
||||
let frozenset_doc = "frozenset() -> empty frozenset object\n\
|
||||
frozenset(iterable) -> frozenset object\n\n\
|
||||
Build an immutable unordered collection of unique elements.";
|
||||
|
||||
context.set_attr(
|
||||
&frozenset_type,
|
||||
"__contains__",
|
||||
context.new_rustfunc(set_contains),
|
||||
);
|
||||
context.set_attr(&frozenset_type, "__len__", context.new_rustfunc(set_len));
|
||||
context.set_attr(
|
||||
&frozenset_type,
|
||||
"__doc__",
|
||||
context.new_str(frozenset_doc.to_string()),
|
||||
);
|
||||
context.set_attr(
|
||||
&frozenset_type,
|
||||
"__repr__",
|
||||
|
||||
95
vm/src/obj/objslice.rs
Normal file
95
vm/src/obj/objslice.rs
Normal file
@@ -0,0 +1,95 @@
|
||||
use super::super::pyobject::{
|
||||
PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol,
|
||||
};
|
||||
use super::super::vm::VirtualMachine;
|
||||
use super::objint;
|
||||
use super::objtype; // Required for arg_check! to use isinstance
|
||||
use num_bigint::BigInt;
|
||||
|
||||
fn slice_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
no_kwargs!(vm, args);
|
||||
let (cls, start, stop, step): (
|
||||
&PyObjectRef,
|
||||
Option<&PyObjectRef>,
|
||||
Option<&PyObjectRef>,
|
||||
Option<&PyObjectRef>,
|
||||
) = match args.args.len() {
|
||||
0 | 1 => Err(vm.new_type_error("slice() must have at least one arguments.".to_owned())),
|
||||
2 => {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [
|
||||
(cls, Some(vm.ctx.type_type())),
|
||||
(stop, Some(vm.ctx.int_type()))
|
||||
]
|
||||
);
|
||||
Ok((cls, None, Some(stop), None))
|
||||
}
|
||||
_ => {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [
|
||||
(cls, Some(vm.ctx.type_type())),
|
||||
(start, Some(vm.ctx.int_type())),
|
||||
(stop, Some(vm.ctx.int_type()))
|
||||
],
|
||||
optional = [(step, Some(vm.ctx.int_type()))]
|
||||
);
|
||||
Ok((cls, Some(start), Some(stop), step))
|
||||
}
|
||||
}?;
|
||||
Ok(PyObject::new(
|
||||
PyObjectPayload::Slice {
|
||||
start: start.map(|x| objint::get_value(x)),
|
||||
stop: stop.map(|x| objint::get_value(x)),
|
||||
step: step.map(|x| objint::get_value(x)),
|
||||
},
|
||||
cls.clone(),
|
||||
))
|
||||
}
|
||||
|
||||
fn get_property_value(vm: &mut VirtualMachine, value: &Option<BigInt>) -> PyResult {
|
||||
if let Some(value) = value {
|
||||
Ok(vm.ctx.new_int(value.clone()))
|
||||
} else {
|
||||
Ok(vm.get_none())
|
||||
}
|
||||
}
|
||||
|
||||
fn slice_start(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(slice, Some(vm.ctx.slice_type()))]);
|
||||
if let PyObjectPayload::Slice { start, .. } = &slice.borrow().payload {
|
||||
get_property_value(vm, start)
|
||||
} else {
|
||||
panic!("Slice has incorrect payload.");
|
||||
}
|
||||
}
|
||||
|
||||
fn slice_stop(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(slice, Some(vm.ctx.slice_type()))]);
|
||||
if let PyObjectPayload::Slice { stop, .. } = &slice.borrow().payload {
|
||||
get_property_value(vm, stop)
|
||||
} else {
|
||||
panic!("Slice has incorrect payload.");
|
||||
}
|
||||
}
|
||||
|
||||
fn slice_step(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(slice, Some(vm.ctx.slice_type()))]);
|
||||
if let PyObjectPayload::Slice { step, .. } = &slice.borrow().payload {
|
||||
get_property_value(vm, step)
|
||||
} else {
|
||||
panic!("Slice has incorrect payload.");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
let zip_type = &context.slice_type;
|
||||
|
||||
context.set_attr(zip_type, "__new__", context.new_rustfunc(slice_new));
|
||||
context.set_attr(zip_type, "start", context.new_property(slice_start));
|
||||
context.set_attr(zip_type, "stop", context.new_property(slice_stop));
|
||||
context.set_attr(zip_type, "step", context.new_property(slice_step));
|
||||
}
|
||||
@@ -6,14 +6,17 @@ use super::super::vm::VirtualMachine;
|
||||
use super::objint;
|
||||
use super::objsequence::PySliceableSequence;
|
||||
use super::objtype;
|
||||
use num_bigint::ToBigInt;
|
||||
use num_traits::ToPrimitive;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::Range;
|
||||
// rust's builtin to_lowercase isn't sufficient for casefold
|
||||
extern crate caseless;
|
||||
extern crate unicode_segmentation;
|
||||
|
||||
use self::unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
let ref str_type = context.str_type;
|
||||
let str_type = &context.str_type;
|
||||
context.set_attr(&str_type, "__add__", context.new_rustfunc(str_add));
|
||||
context.set_attr(&str_type, "__eq__", context.new_rustfunc(str_eq));
|
||||
context.set_attr(
|
||||
@@ -53,6 +56,7 @@ pub fn init(context: &PyContext) {
|
||||
context.set_attr(&str_type, "isalnum", context.new_rustfunc(str_isalnum));
|
||||
context.set_attr(&str_type, "isnumeric", context.new_rustfunc(str_isnumeric));
|
||||
context.set_attr(&str_type, "isdigit", context.new_rustfunc(str_isdigit));
|
||||
context.set_attr(&str_type, "isdecimal", context.new_rustfunc(str_isdecimal));
|
||||
context.set_attr(&str_type, "title", context.new_rustfunc(str_title));
|
||||
context.set_attr(&str_type, "swapcase", context.new_rustfunc(str_swapcase));
|
||||
context.set_attr(&str_type, "isalpha", context.new_rustfunc(str_isalpha));
|
||||
@@ -204,7 +208,7 @@ fn str_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
}
|
||||
|
||||
fn str_format(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
if args.args.len() == 0 {
|
||||
if args.args.is_empty() {
|
||||
return Err(
|
||||
vm.new_type_error("descriptor 'format' of 'str' object needs an argument".to_string())
|
||||
);
|
||||
@@ -234,9 +238,9 @@ fn str_format(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
fn call_object_format(
|
||||
vm: &mut VirtualMachine,
|
||||
argument: PyObjectRef,
|
||||
format_spec: &String,
|
||||
format_spec: &str,
|
||||
) -> PyResult {
|
||||
let returned_type = vm.ctx.new_str(format_spec.clone());
|
||||
let returned_type = vm.ctx.new_str(format_spec.to_string());
|
||||
let result = vm.call_method(&argument, "__format__", vec![returned_type])?;
|
||||
if !objtype::isinstance(&result, &vm.ctx.str_type()) {
|
||||
let result_type = result.typ();
|
||||
@@ -304,13 +308,13 @@ fn str_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
let mut hasher = std::collections::hash_map::DefaultHasher::new();
|
||||
value.hash(&mut hasher);
|
||||
let hash = hasher.finish();
|
||||
Ok(vm.ctx.new_int(hash.to_bigint().unwrap()))
|
||||
Ok(vm.ctx.new_int(hash))
|
||||
}
|
||||
|
||||
fn str_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]);
|
||||
let sv = get_value(s);
|
||||
Ok(vm.ctx.new_int(sv.len().to_bigint().unwrap()))
|
||||
Ok(vm.ctx.new_int(sv.chars().count()))
|
||||
}
|
||||
|
||||
fn str_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
@@ -446,12 +450,8 @@ fn str_isidentifier(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
&& !value.chars().nth(0).unwrap().is_digit(10)
|
||||
{
|
||||
for c in value.chars() {
|
||||
if c != "_".chars().nth(0).unwrap() {
|
||||
if !c.is_digit(10) {
|
||||
if !c.is_alphabetic() {
|
||||
is_identifier = false;
|
||||
}
|
||||
}
|
||||
if c != "_".chars().nth(0).unwrap() && !c.is_digit(10) && !c.is_alphabetic() {
|
||||
is_identifier = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -464,29 +464,37 @@ fn str_isidentifier(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
// which is why isspace is using is_ascii_whitespace. Same for isupper & islower
|
||||
fn str_isspace(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]);
|
||||
let is_whitespace = get_value(&s).chars().all(|c| c.is_ascii_whitespace());
|
||||
Ok(vm.ctx.new_bool(is_whitespace))
|
||||
let value = get_value(&s);
|
||||
Ok(vm
|
||||
.ctx
|
||||
.new_bool(!value.is_empty() && value.chars().all(|c| c.is_ascii_whitespace())))
|
||||
}
|
||||
|
||||
fn str_isupper(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]);
|
||||
let is_upper = get_value(&s)
|
||||
.chars()
|
||||
.filter(|x| !x.is_ascii_whitespace())
|
||||
.all(|c| c.is_uppercase());
|
||||
Ok(vm.ctx.new_bool(is_upper))
|
||||
let value = get_value(&s);
|
||||
Ok(vm.ctx.new_bool(
|
||||
!value.is_empty()
|
||||
&& value
|
||||
.chars()
|
||||
.filter(|x| !x.is_ascii_whitespace())
|
||||
.all(|c| c.is_uppercase()),
|
||||
))
|
||||
}
|
||||
|
||||
fn str_islower(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]);
|
||||
let is_lower = get_value(&s)
|
||||
.chars()
|
||||
.filter(|x| !x.is_ascii_whitespace())
|
||||
.all(|c| c.is_lowercase());
|
||||
Ok(vm.ctx.new_bool(is_lower))
|
||||
let value = get_value(&s);
|
||||
Ok(vm.ctx.new_bool(
|
||||
!value.is_empty()
|
||||
&& value
|
||||
.chars()
|
||||
.filter(|x| !x.is_ascii_whitespace())
|
||||
.all(|c| c.is_lowercase()),
|
||||
))
|
||||
}
|
||||
|
||||
// doesn't implement keep new line delimeter just yet
|
||||
// doesn't implement keep new line delimiter just yet
|
||||
fn str_splitlines(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]);
|
||||
let elements = get_value(&s)
|
||||
@@ -504,12 +512,11 @@ fn str_zfill(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
);
|
||||
let value = get_value(&s);
|
||||
let len = objint::get_value(&len).to_usize().unwrap();
|
||||
let new_str: String;
|
||||
if len <= value.len() {
|
||||
new_str = value;
|
||||
let new_str = if len <= value.len() {
|
||||
value
|
||||
} else {
|
||||
new_str = format!("{}{}", "0".repeat(len - value.len()), value);
|
||||
}
|
||||
format!("{}{}", "0".repeat(len - value.len()), value)
|
||||
};
|
||||
Ok(vm.ctx.new_str(new_str))
|
||||
}
|
||||
|
||||
@@ -546,7 +553,7 @@ fn str_count(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
Err(e) => return Err(vm.new_index_error(e)),
|
||||
};
|
||||
let num_occur: usize = value[start..end].matches(&sub).count();
|
||||
Ok(vm.ctx.new_int(num_occur.to_bigint().unwrap()))
|
||||
Ok(vm.ctx.new_int(num_occur))
|
||||
}
|
||||
|
||||
fn str_index(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
@@ -565,13 +572,13 @@ fn str_index(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
Ok((start, end)) => (start, end),
|
||||
Err(e) => return Err(vm.new_index_error(e)),
|
||||
};
|
||||
let ind: usize = match value[start..end + 1].find(&sub) {
|
||||
let ind: usize = match value[start..=end].find(&sub) {
|
||||
Some(num) => num,
|
||||
None => {
|
||||
return Err(vm.new_value_error("substring not found".to_string()));
|
||||
}
|
||||
};
|
||||
Ok(vm.ctx.new_int(ind.to_bigint().unwrap()))
|
||||
Ok(vm.ctx.new_int(ind))
|
||||
}
|
||||
|
||||
fn str_find(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
@@ -590,11 +597,11 @@ fn str_find(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
Ok((start, end)) => (start, end),
|
||||
Err(e) => return Err(vm.new_index_error(e)),
|
||||
};
|
||||
let ind: i128 = match value[start..end + 1].find(&sub) {
|
||||
let ind: i128 = match value[start..=end].find(&sub) {
|
||||
Some(num) => num as i128,
|
||||
None => -1 as i128,
|
||||
};
|
||||
Ok(vm.ctx.new_int(ind.to_bigint().unwrap()))
|
||||
Ok(vm.ctx.new_int(ind))
|
||||
}
|
||||
|
||||
// casefold is much more aggresive than lower
|
||||
@@ -785,10 +792,15 @@ fn str_istitle(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]);
|
||||
let value = get_value(&s);
|
||||
let mut is_titled = true;
|
||||
for word in value.split(" ") {
|
||||
if word != make_title(&word) {
|
||||
is_titled = false;
|
||||
break;
|
||||
|
||||
if value.is_empty() {
|
||||
is_titled = false;
|
||||
} else {
|
||||
for word in value.split(' ') {
|
||||
if word != make_title(&word) {
|
||||
is_titled = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(vm.ctx.new_bool(is_titled))
|
||||
@@ -845,14 +857,18 @@ fn str_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
|
||||
fn str_isalnum(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]);
|
||||
let is_alnum = get_value(&s).chars().all(|c| c.is_alphanumeric());
|
||||
Ok(vm.ctx.new_bool(is_alnum))
|
||||
let value = get_value(&s);
|
||||
Ok(vm
|
||||
.ctx
|
||||
.new_bool(!value.is_empty() && value.chars().all(|c| c.is_alphanumeric())))
|
||||
}
|
||||
|
||||
fn str_isascii(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]);
|
||||
let is_ascii = get_value(&s).chars().all(|c| c.is_ascii());
|
||||
Ok(vm.ctx.new_bool(is_ascii))
|
||||
let value = get_value(&s);
|
||||
Ok(vm
|
||||
.ctx
|
||||
.new_bool(!value.is_empty() && value.chars().all(|c| c.is_ascii())))
|
||||
}
|
||||
|
||||
fn str_rindex(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
@@ -871,13 +887,13 @@ fn str_rindex(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
Ok((start, end)) => (start, end),
|
||||
Err(e) => return Err(vm.new_index_error(e)),
|
||||
};
|
||||
let ind: i64 = match value[start..end + 1].rfind(&sub) {
|
||||
let ind: i64 = match value[start..=end].rfind(&sub) {
|
||||
Some(num) => num as i64,
|
||||
None => {
|
||||
return Err(vm.new_value_error("substring not found".to_string()));
|
||||
}
|
||||
};
|
||||
Ok(vm.ctx.new_int(ind.to_bigint().unwrap()))
|
||||
Ok(vm.ctx.new_int(ind))
|
||||
}
|
||||
|
||||
fn str_rfind(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
@@ -896,23 +912,27 @@ fn str_rfind(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
Ok((start, end)) => (start, end),
|
||||
Err(e) => return Err(vm.new_index_error(e)),
|
||||
};
|
||||
let ind = match value[start..end + 1].rfind(&sub) {
|
||||
let ind = match value[start..=end].rfind(&sub) {
|
||||
Some(num) => num as i128,
|
||||
None => -1 as i128,
|
||||
};
|
||||
Ok(vm.ctx.new_int(ind.to_bigint().unwrap()))
|
||||
Ok(vm.ctx.new_int(ind))
|
||||
}
|
||||
|
||||
fn str_isnumeric(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]);
|
||||
let is_numeric = get_value(&s).chars().all(|c| c.is_numeric());
|
||||
Ok(vm.ctx.new_bool(is_numeric))
|
||||
let value = get_value(&s);
|
||||
Ok(vm
|
||||
.ctx
|
||||
.new_bool(!value.is_empty() && value.chars().all(|c| c.is_numeric())))
|
||||
}
|
||||
|
||||
fn str_isalpha(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]);
|
||||
let is_alpha = get_value(&s).chars().all(|c| c.is_alphanumeric());
|
||||
Ok(vm.ctx.new_bool(is_alpha))
|
||||
let value = get_value(&s);
|
||||
Ok(vm
|
||||
.ctx
|
||||
.new_bool(!value.is_empty() && value.chars().all(|c| c.is_alphanumeric())))
|
||||
}
|
||||
|
||||
fn str_isdigit(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
@@ -923,21 +943,41 @@ fn str_isdigit(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
0x2070, 0x00B9, 0x00B2, 0x00B3, 0x2074, 0x2075, 0x2076, 0x2077, 0x2078, 0x2079,
|
||||
];
|
||||
let mut is_digit: bool = true;
|
||||
for c in value.chars() {
|
||||
if !c.is_digit(10) {
|
||||
// checking if char is exponent
|
||||
let char_as_uni: u16 = c as u16;
|
||||
if valid_unicodes.contains(&char_as_uni) {
|
||||
continue;
|
||||
} else {
|
||||
is_digit = false;
|
||||
break;
|
||||
|
||||
if value.is_empty() {
|
||||
is_digit = false;
|
||||
} else {
|
||||
for c in value.chars() {
|
||||
if !c.is_digit(10) {
|
||||
// checking if char is exponent
|
||||
let char_as_uni: u16 = c as u16;
|
||||
if valid_unicodes.contains(&char_as_uni) {
|
||||
continue;
|
||||
} else {
|
||||
is_digit = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(vm.ctx.new_bool(is_digit))
|
||||
}
|
||||
|
||||
fn str_isdecimal(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]);
|
||||
|
||||
let value = get_value(&s);
|
||||
|
||||
let is_decimal = if !value.is_empty() {
|
||||
value.chars().all(|c| c.is_ascii_digit())
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
Ok(vm.ctx.new_bool(is_decimal))
|
||||
}
|
||||
|
||||
fn str_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
@@ -964,30 +1004,84 @@ fn str_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
}
|
||||
|
||||
impl PySliceableSequence for String {
|
||||
fn do_slice(&self, start: usize, stop: usize) -> Self {
|
||||
self[start..stop].to_string()
|
||||
fn do_slice(&self, range: Range<usize>) -> Self {
|
||||
to_graphemes(self)
|
||||
.get(range)
|
||||
.map_or(String::default(), |c| c.join(""))
|
||||
}
|
||||
fn do_stepped_slice(&self, start: usize, stop: usize, step: usize) -> Self {
|
||||
self[start..stop].chars().step_by(step).collect()
|
||||
|
||||
fn do_slice_reverse(&self, range: Range<usize>) -> Self {
|
||||
to_graphemes(self)
|
||||
.get_mut(range)
|
||||
.map_or(String::default(), |slice| {
|
||||
slice.reverse();
|
||||
slice.join("")
|
||||
})
|
||||
}
|
||||
|
||||
fn do_stepped_slice(&self, range: Range<usize>, step: usize) -> Self {
|
||||
if let Some(s) = to_graphemes(self).get(range) {
|
||||
return s
|
||||
.iter()
|
||||
.cloned()
|
||||
.step_by(step)
|
||||
.collect::<Vec<String>>()
|
||||
.join("");
|
||||
}
|
||||
String::default()
|
||||
}
|
||||
|
||||
fn do_stepped_slice_reverse(&self, range: Range<usize>, step: usize) -> Self {
|
||||
if let Some(s) = to_graphemes(self).get(range) {
|
||||
return s
|
||||
.iter()
|
||||
.rev()
|
||||
.cloned()
|
||||
.step_by(step)
|
||||
.collect::<Vec<String>>()
|
||||
.join("");
|
||||
}
|
||||
String::default()
|
||||
}
|
||||
|
||||
fn empty() -> Self {
|
||||
String::default()
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.len()
|
||||
to_graphemes(self).len()
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a string-able `value` to a vec of graphemes
|
||||
/// represents the string according to user perceived characters
|
||||
fn to_graphemes<S: AsRef<str>>(value: S) -> Vec<String> {
|
||||
UnicodeSegmentation::graphemes(value.as_ref(), true)
|
||||
.map(String::from)
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn subscript(vm: &mut VirtualMachine, value: &str, b: PyObjectRef) -> PyResult {
|
||||
// let value = a
|
||||
if objtype::isinstance(&b, &vm.ctx.int_type()) {
|
||||
let pos = objint::get_value(&b).to_i32().unwrap();
|
||||
let idx = value.to_string().get_pos(pos);
|
||||
Ok(vm.new_str(value[idx..idx + 1].to_string()))
|
||||
match objint::get_value(&b).to_i32() {
|
||||
Some(pos) => {
|
||||
let graphemes = to_graphemes(value);
|
||||
if let Some(idx) = graphemes.get_pos(pos) {
|
||||
Ok(vm.new_str(graphemes[idx].to_string()))
|
||||
} else {
|
||||
Err(vm.new_index_error("string index out of range".to_string()))
|
||||
}
|
||||
}
|
||||
None => {
|
||||
Err(vm.new_index_error("cannot fit 'int' into an index-sized integer".to_string()))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match &(*b.borrow()).payload {
|
||||
&PyObjectPayload::Slice {
|
||||
start: _,
|
||||
stop: _,
|
||||
step: _,
|
||||
} => Ok(vm.new_str(value.to_string().get_slice_items(&b).to_string())),
|
||||
match (*b.borrow()).payload {
|
||||
PyObjectPayload::Slice { .. } => {
|
||||
let string = value.to_string().get_slice_items(vm, &b)?;
|
||||
Ok(vm.new_str(string))
|
||||
}
|
||||
_ => panic!(
|
||||
"TypeError: indexing type {:?} with index {:?} is not supported (yet?)",
|
||||
value, b
|
||||
@@ -1034,5 +1128,5 @@ fn make_title(s: &str) -> String {
|
||||
capitalize_char = true;
|
||||
}
|
||||
}
|
||||
return titled_str;
|
||||
titled_str
|
||||
}
|
||||
|
||||
@@ -11,8 +11,28 @@ use super::super::vm::VirtualMachine;
|
||||
use super::objtype;
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
let ref super_type = context.super_type;
|
||||
let super_type = &context.super_type;
|
||||
|
||||
let super_doc = "super() -> same as super(__class__, <first argument>)\n\
|
||||
super(type) -> unbound super object\n\
|
||||
super(type, obj) -> bound super object; requires isinstance(obj, type)\n\
|
||||
super(type, type2) -> bound super object; requires issubclass(type2, type)\n\
|
||||
Typical use to call a cooperative superclass method:\n\
|
||||
class C(B):\n \
|
||||
def meth(self, arg):\n \
|
||||
super().meth(arg)\n\
|
||||
This works for class methods too:\n\
|
||||
class C(B):\n \
|
||||
@classmethod\n \
|
||||
def cmeth(cls, arg):\n \
|
||||
super().cmeth(arg)\n";
|
||||
|
||||
context.set_attr(&super_type, "__init__", context.new_rustfunc(super_init));
|
||||
context.set_attr(
|
||||
&super_type,
|
||||
"__doc__",
|
||||
context.new_str(super_doc.to_string()),
|
||||
);
|
||||
}
|
||||
|
||||
fn super_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
@@ -53,9 +73,9 @@ fn super_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
|
||||
// Check obj type:
|
||||
if !(objtype::isinstance(&py_obj, &py_type) || objtype::issubclass(&py_obj, &py_type)) {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"super(type, obj): obj must be an instance or subtype of type"
|
||||
)));
|
||||
return Err(vm.new_type_error(
|
||||
"super(type, obj): obj must be an instance or subtype of type".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
// TODO: how to store those types?
|
||||
|
||||
@@ -4,12 +4,122 @@ use super::super::pyobject::{
|
||||
use super::super::vm::VirtualMachine;
|
||||
use super::objbool;
|
||||
use super::objint;
|
||||
use super::objsequence::{get_elements, get_item, seq_equal};
|
||||
use super::objsequence::{
|
||||
get_elements, get_item, seq_equal, seq_ge, seq_gt, seq_le, seq_lt, seq_mul,
|
||||
};
|
||||
use super::objstr;
|
||||
use super::objtype;
|
||||
use num_bigint::ToBigInt;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
fn tuple_lt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [(zelf, Some(vm.ctx.tuple_type())), (other, None)]
|
||||
);
|
||||
|
||||
let result = if objtype::isinstance(other, &vm.ctx.tuple_type()) {
|
||||
let zelf = get_elements(zelf);
|
||||
let other = get_elements(other);
|
||||
seq_lt(vm, &zelf, &other)?
|
||||
} else {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"Cannot compare {} and {} using '<'",
|
||||
zelf.borrow(),
|
||||
other.borrow()
|
||||
)));
|
||||
};
|
||||
|
||||
Ok(vm.ctx.new_bool(result))
|
||||
}
|
||||
|
||||
fn tuple_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [(zelf, Some(vm.ctx.tuple_type())), (other, None)]
|
||||
);
|
||||
|
||||
let result = if objtype::isinstance(other, &vm.ctx.tuple_type()) {
|
||||
let zelf = get_elements(zelf);
|
||||
let other = get_elements(other);
|
||||
seq_gt(vm, &zelf, &other)?
|
||||
} else {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"Cannot compare {} and {} using '>'",
|
||||
zelf.borrow(),
|
||||
other.borrow()
|
||||
)));
|
||||
};
|
||||
|
||||
Ok(vm.ctx.new_bool(result))
|
||||
}
|
||||
|
||||
fn tuple_ge(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [(zelf, Some(vm.ctx.tuple_type())), (other, None)]
|
||||
);
|
||||
|
||||
let result = if objtype::isinstance(other, &vm.ctx.tuple_type()) {
|
||||
let zelf = get_elements(zelf);
|
||||
let other = get_elements(other);
|
||||
seq_ge(vm, &zelf, &other)?
|
||||
} else {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"Cannot compare {} and {} using '>='",
|
||||
zelf.borrow(),
|
||||
other.borrow()
|
||||
)));
|
||||
};
|
||||
|
||||
Ok(vm.ctx.new_bool(result))
|
||||
}
|
||||
|
||||
fn tuple_le(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [(zelf, Some(vm.ctx.tuple_type())), (other, None)]
|
||||
);
|
||||
|
||||
let result = if objtype::isinstance(other, &vm.ctx.tuple_type()) {
|
||||
let zelf = get_elements(zelf);
|
||||
let other = get_elements(other);
|
||||
seq_le(vm, &zelf, &other)?
|
||||
} else {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"Cannot compare {} and {} using '<='",
|
||||
zelf.borrow(),
|
||||
other.borrow()
|
||||
)));
|
||||
};
|
||||
|
||||
Ok(vm.ctx.new_bool(result))
|
||||
}
|
||||
|
||||
fn tuple_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [(zelf, Some(vm.ctx.tuple_type())), (other, None)]
|
||||
);
|
||||
|
||||
if objtype::isinstance(other, &vm.ctx.tuple_type()) {
|
||||
let e1 = get_elements(zelf);
|
||||
let e2 = get_elements(other);
|
||||
let elements = e1.iter().chain(e2.iter()).cloned().collect();
|
||||
Ok(vm.ctx.new_tuple(elements))
|
||||
} else {
|
||||
Err(vm.new_type_error(format!(
|
||||
"Cannot add {} and {}",
|
||||
zelf.borrow(),
|
||||
other.borrow()
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
fn tuple_count(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
@@ -21,10 +131,10 @@ fn tuple_count(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
for element in elements.iter() {
|
||||
let is_eq = vm._eq(element, value.clone())?;
|
||||
if objbool::boolval(vm, is_eq)? {
|
||||
count = count + 1;
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
Ok(vm.context().new_int(count.to_bigint().unwrap()))
|
||||
Ok(vm.context().new_int(count))
|
||||
}
|
||||
|
||||
fn tuple_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
@@ -53,7 +163,7 @@ fn tuple_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
element_hash.hash(&mut hasher);
|
||||
}
|
||||
let hash = hasher.finish();
|
||||
Ok(vm.ctx.new_int(hash.to_bigint().unwrap()))
|
||||
Ok(vm.ctx.new_int(hash))
|
||||
}
|
||||
|
||||
fn tuple_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
@@ -73,7 +183,7 @@ fn tuple_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
fn tuple_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(zelf, Some(vm.ctx.tuple_type()))]);
|
||||
let elements = get_elements(zelf);
|
||||
Ok(vm.context().new_int(elements.len().to_bigint().unwrap()))
|
||||
Ok(vm.context().new_int(elements.len()))
|
||||
}
|
||||
|
||||
fn tuple_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
@@ -95,7 +205,7 @@ fn tuple_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
};
|
||||
|
||||
Ok(PyObject::new(
|
||||
PyObjectPayload::Sequence { elements: elements },
|
||||
PyObjectPayload::Sequence { elements },
|
||||
cls.clone(),
|
||||
))
|
||||
}
|
||||
@@ -119,6 +229,21 @@ fn tuple_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
Ok(vm.new_str(s))
|
||||
}
|
||||
|
||||
fn tuple_mul(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [
|
||||
(zelf, Some(vm.ctx.tuple_type())),
|
||||
(product, Some(vm.ctx.int_type()))
|
||||
]
|
||||
);
|
||||
|
||||
let new_elements = seq_mul(&get_elements(zelf), product);
|
||||
|
||||
Ok(vm.ctx.new_tuple(new_elements))
|
||||
}
|
||||
|
||||
fn tuple_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
@@ -128,6 +253,21 @@ fn tuple_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
get_item(vm, tuple, &get_elements(&tuple), needle.clone())
|
||||
}
|
||||
|
||||
pub fn tuple_index(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [(tuple, Some(vm.ctx.tuple_type())), (needle, None)]
|
||||
);
|
||||
for (index, element) in get_elements(tuple).iter().enumerate() {
|
||||
let py_equal = vm.call_method(needle, "__eq__", vec![element.clone()])?;
|
||||
if objbool::get_value(&py_equal) {
|
||||
return Ok(vm.context().new_int(index));
|
||||
}
|
||||
}
|
||||
Err(vm.new_value_error("tuple.index(x): x not in tuple".to_string()))
|
||||
}
|
||||
|
||||
pub fn tuple_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
@@ -149,7 +289,12 @@ pub fn tuple_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
}
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
let ref tuple_type = context.tuple_type;
|
||||
let tuple_type = &context.tuple_type;
|
||||
let tuple_doc = "tuple() -> empty tuple
|
||||
tuple(iterable) -> tuple initialized from iterable's items
|
||||
|
||||
If the argument is a tuple, the return value is the same object.";
|
||||
context.set_attr(&tuple_type, "__add__", context.new_rustfunc(tuple_add));
|
||||
context.set_attr(&tuple_type, "__eq__", context.new_rustfunc(tuple_eq));
|
||||
context.set_attr(
|
||||
&tuple_type,
|
||||
@@ -165,6 +310,17 @@ pub fn init(context: &PyContext) {
|
||||
context.set_attr(&tuple_type, "__iter__", context.new_rustfunc(tuple_iter));
|
||||
context.set_attr(&tuple_type, "__len__", context.new_rustfunc(tuple_len));
|
||||
context.set_attr(&tuple_type, "__new__", context.new_rustfunc(tuple_new));
|
||||
context.set_attr(&tuple_type, "__mul__", context.new_rustfunc(tuple_mul));
|
||||
context.set_attr(&tuple_type, "__repr__", context.new_rustfunc(tuple_repr));
|
||||
context.set_attr(&tuple_type, "count", context.new_rustfunc(tuple_count));
|
||||
context.set_attr(&tuple_type, "__lt__", context.new_rustfunc(tuple_lt));
|
||||
context.set_attr(&tuple_type, "__le__", context.new_rustfunc(tuple_le));
|
||||
context.set_attr(&tuple_type, "__gt__", context.new_rustfunc(tuple_gt));
|
||||
context.set_attr(&tuple_type, "__ge__", context.new_rustfunc(tuple_ge));
|
||||
context.set_attr(
|
||||
&tuple_type,
|
||||
"__doc__",
|
||||
context.new_str(tuple_doc.to_string()),
|
||||
);
|
||||
context.set_attr(&tuple_type, "index", context.new_rustfunc(tuple_index));
|
||||
}
|
||||
|
||||
@@ -1,28 +1,34 @@
|
||||
use super::super::pyobject::{
|
||||
AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef,
|
||||
PyResult, TypeProtocol,
|
||||
AttributeProtocol, IdProtocol, PyAttributes, PyContext, PyFuncArgs, PyObject, PyObjectPayload,
|
||||
PyObjectRef, PyResult, TypeProtocol,
|
||||
};
|
||||
use super::super::vm::VirtualMachine;
|
||||
use super::objdict;
|
||||
use super::objstr;
|
||||
use super::objtype; // Required for arg_check! to use isinstance
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
|
||||
/*
|
||||
* The magical type type
|
||||
*/
|
||||
|
||||
pub fn create_type(type_type: PyObjectRef, object_type: PyObjectRef, dict_type: PyObjectRef) {
|
||||
pub fn create_type(type_type: PyObjectRef, object_type: PyObjectRef, _dict_type: PyObjectRef) {
|
||||
(*type_type.borrow_mut()).payload = PyObjectPayload::Class {
|
||||
name: String::from("type"),
|
||||
dict: objdict::new(dict_type),
|
||||
dict: RefCell::new(PyAttributes::new()),
|
||||
mro: vec![object_type],
|
||||
};
|
||||
(*type_type.borrow_mut()).typ = Some(type_type.clone());
|
||||
}
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
let ref type_type = context.type_type;
|
||||
let type_type = &context.type_type;
|
||||
|
||||
let type_doc = "type(object_or_name, bases, dict)\n\
|
||||
type(object) -> the object's type\n\
|
||||
type(name, bases, dict) -> a new type";
|
||||
|
||||
context.set_attr(&type_type, "__call__", context.new_rustfunc(type_call));
|
||||
context.set_attr(&type_type, "__new__", context.new_rustfunc(type_new));
|
||||
context.set_attr(
|
||||
@@ -46,6 +52,7 @@ pub fn init(context: &PyContext) {
|
||||
"__getattribute__",
|
||||
context.new_rustfunc(type_getattribute),
|
||||
);
|
||||
context.set_attr(&type_type, "__doc__", context.new_str(type_doc.to_string()));
|
||||
}
|
||||
|
||||
fn type_mro(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
@@ -89,12 +96,7 @@ pub fn issubclass(typ: &PyObjectRef, cls: &PyObjectRef) -> bool {
|
||||
}
|
||||
|
||||
pub fn get_type_name(typ: &PyObjectRef) -> String {
|
||||
if let PyObjectPayload::Class {
|
||||
name,
|
||||
dict: _,
|
||||
mro: _,
|
||||
} = &typ.borrow().payload
|
||||
{
|
||||
if let PyObjectPayload::Class { name, .. } = &typ.borrow().payload {
|
||||
name.clone()
|
||||
} else {
|
||||
panic!("Cannot get type_name of non-type type {:?}", typ);
|
||||
@@ -124,12 +126,22 @@ pub fn type_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
let mut bases = vm.extract_elements(bases)?;
|
||||
bases.push(vm.context().object());
|
||||
let name = objstr::get_value(name);
|
||||
new(typ.clone(), &name, bases, dict.clone())
|
||||
new(typ.clone(), &name, bases, py_dict_to_attributes(dict))
|
||||
} else {
|
||||
Err(vm.new_type_error(format!(": type_new: {:?}", args)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Take a python dictionary and convert it to attributes.
|
||||
fn py_dict_to_attributes(dict: &PyObjectRef) -> PyAttributes {
|
||||
let mut attrs = PyAttributes::new();
|
||||
for (key, value) in objdict::get_key_value_pairs(dict) {
|
||||
let key = objstr::get_value(&key);
|
||||
attrs.insert(key, value);
|
||||
}
|
||||
attrs
|
||||
}
|
||||
|
||||
pub fn type_call(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult {
|
||||
debug!("type_call: {:?}", args);
|
||||
let cls = args.shift();
|
||||
@@ -192,42 +204,33 @@ pub fn type_getattribute(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult
|
||||
Ok(cls_attr)
|
||||
} else if let Some(attr) = mcl.get_attr(&name) {
|
||||
vm.call_get_descriptor(attr, cls.clone())
|
||||
} else if let Some(getter) = cls.get_attr("__getattr__") {
|
||||
vm.invoke(
|
||||
getter,
|
||||
PyFuncArgs {
|
||||
args: vec![mcl, name_str.clone()],
|
||||
kwargs: vec![],
|
||||
},
|
||||
)
|
||||
} else {
|
||||
if let Some(getter) = cls.get_attr("__getattr__") {
|
||||
vm.invoke(
|
||||
getter,
|
||||
PyFuncArgs {
|
||||
args: vec![mcl, name_str.clone()],
|
||||
kwargs: vec![],
|
||||
},
|
||||
)
|
||||
} else {
|
||||
let attribute_error = vm.context().exceptions.attribute_error.clone();
|
||||
Err(vm.new_exception(
|
||||
attribute_error,
|
||||
format!("{} has no attribute '{}'", cls.borrow(), name),
|
||||
))
|
||||
}
|
||||
let attribute_error = vm.context().exceptions.attribute_error.clone();
|
||||
Err(vm.new_exception(
|
||||
attribute_error,
|
||||
format!("{} has no attribute '{}'", cls.borrow(), name),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_attributes(obj: &PyObjectRef) -> HashMap<String, PyObjectRef> {
|
||||
pub fn get_attributes(obj: &PyObjectRef) -> PyAttributes {
|
||||
// Gather all members here:
|
||||
let mut attributes: HashMap<String, PyObjectRef> = HashMap::new();
|
||||
let mut attributes = PyAttributes::new();
|
||||
|
||||
// Get class attributes:
|
||||
let mut base_classes = objtype::base_classes(obj);
|
||||
base_classes.reverse();
|
||||
for bc in base_classes {
|
||||
if let PyObjectPayload::Class {
|
||||
name: _,
|
||||
dict,
|
||||
mro: _,
|
||||
} = &bc.borrow().payload
|
||||
{
|
||||
let elements = objdict::get_key_value_pairs(dict);
|
||||
for (name, value) in elements.iter() {
|
||||
let name = objstr::get_value(name);
|
||||
if let PyObjectPayload::Class { dict, .. } = &bc.borrow().payload {
|
||||
for (name, value) in dict.borrow().iter() {
|
||||
attributes.insert(name.to_string(), value.clone());
|
||||
}
|
||||
}
|
||||
@@ -235,9 +238,7 @@ pub fn get_attributes(obj: &PyObjectRef) -> HashMap<String, PyObjectRef> {
|
||||
|
||||
// Get instance attributes:
|
||||
if let PyObjectPayload::Instance { dict } = &obj.borrow().payload {
|
||||
let elements = objdict::get_key_value_pairs(dict);
|
||||
for (name, value) in elements.iter() {
|
||||
let name = objstr::get_value(name);
|
||||
for (name, value) in dict.borrow().iter() {
|
||||
attributes.insert(name.to_string(), value.clone());
|
||||
}
|
||||
}
|
||||
@@ -254,8 +255,8 @@ fn take_next_base(
|
||||
for base in &bases {
|
||||
let head = base[0].clone();
|
||||
if !(&bases)
|
||||
.into_iter()
|
||||
.any(|x| x[1..].into_iter().any(|x| x.get_id() == head.get_id()))
|
||||
.iter()
|
||||
.any(|x| x[1..].iter().any(|x| x.get_id() == head.get_id()))
|
||||
{
|
||||
next = Some(head);
|
||||
break;
|
||||
@@ -263,7 +264,7 @@ fn take_next_base(
|
||||
}
|
||||
|
||||
if let Some(head) = next {
|
||||
for ref mut item in &mut bases {
|
||||
for item in &mut bases {
|
||||
if item[0].get_id() == head.get_id() {
|
||||
item.remove(0);
|
||||
}
|
||||
@@ -277,7 +278,7 @@ fn linearise_mro(mut bases: Vec<Vec<PyObjectRef>>) -> Option<Vec<PyObjectRef>> {
|
||||
debug!("Linearising MRO: {:?}", bases);
|
||||
let mut result = vec![];
|
||||
loop {
|
||||
if (&bases).into_iter().all(|x| x.is_empty()) {
|
||||
if (&bases).iter().all(|x| x.is_empty()) {
|
||||
break;
|
||||
}
|
||||
match take_next_base(bases) {
|
||||
@@ -291,14 +292,19 @@ fn linearise_mro(mut bases: Vec<Vec<PyObjectRef>>) -> Option<Vec<PyObjectRef>> {
|
||||
Some(result)
|
||||
}
|
||||
|
||||
pub fn new(typ: PyObjectRef, name: &str, bases: Vec<PyObjectRef>, dict: PyObjectRef) -> PyResult {
|
||||
pub fn new(
|
||||
typ: PyObjectRef,
|
||||
name: &str,
|
||||
bases: Vec<PyObjectRef>,
|
||||
dict: HashMap<String, PyObjectRef>,
|
||||
) -> PyResult {
|
||||
let mros = bases.into_iter().map(|x| _mro(x).unwrap()).collect();
|
||||
let mro = linearise_mro(mros).unwrap();
|
||||
Ok(PyObject::new(
|
||||
PyObjectPayload::Class {
|
||||
name: String::from(name),
|
||||
dict: dict,
|
||||
mro: mro,
|
||||
dict: RefCell::new(dict),
|
||||
mro,
|
||||
},
|
||||
typ,
|
||||
))
|
||||
@@ -317,7 +323,7 @@ fn type_prepare(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{linearise_mro, new};
|
||||
use super::{IdProtocol, PyContext, PyObjectRef};
|
||||
use super::{HashMap, IdProtocol, PyContext, PyObjectRef};
|
||||
|
||||
fn map_ids(obj: Option<Vec<PyObjectRef>>) -> Option<Vec<usize>> {
|
||||
match obj {
|
||||
@@ -332,20 +338,8 @@ mod tests {
|
||||
let object = context.object;
|
||||
let type_type = context.type_type;
|
||||
|
||||
let a = new(
|
||||
type_type.clone(),
|
||||
"A",
|
||||
vec![object.clone()],
|
||||
type_type.clone(),
|
||||
)
|
||||
.unwrap();
|
||||
let b = new(
|
||||
type_type.clone(),
|
||||
"B",
|
||||
vec![object.clone()],
|
||||
type_type.clone(),
|
||||
)
|
||||
.unwrap();
|
||||
let a = new(type_type.clone(), "A", vec![object.clone()], HashMap::new()).unwrap();
|
||||
let b = new(type_type.clone(), "B", vec![object.clone()], HashMap::new()).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
map_ids(linearise_mro(vec![
|
||||
|
||||
46
vm/src/obj/objzip.rs
Normal file
46
vm/src/obj/objzip.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
use super::super::pyobject::{
|
||||
PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef, PyResult, TypeProtocol,
|
||||
};
|
||||
use super::super::vm::VirtualMachine;
|
||||
use super::objiter;
|
||||
use super::objtype; // Required for arg_check! to use isinstance
|
||||
|
||||
fn zip_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
no_kwargs!(vm, args);
|
||||
let cls = &args.args[0];
|
||||
let iterables = &args.args[1..];
|
||||
let iterators = iterables
|
||||
.into_iter()
|
||||
.map(|iterable| objiter::get_iter(vm, iterable))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
Ok(PyObject::new(
|
||||
PyObjectPayload::ZipIterator { iterators },
|
||||
cls.clone(),
|
||||
))
|
||||
}
|
||||
|
||||
fn zip_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(zip, Some(vm.ctx.zip_type()))]);
|
||||
|
||||
if let PyObjectPayload::ZipIterator { ref mut iterators } = zip.borrow_mut().payload {
|
||||
if iterators.is_empty() {
|
||||
Err(objiter::new_stop_iteration(vm))
|
||||
} else {
|
||||
let next_objs = iterators
|
||||
.iter()
|
||||
.map(|iterator| objiter::call_next(vm, iterator))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
Ok(vm.ctx.new_tuple(next_objs))
|
||||
}
|
||||
} else {
|
||||
panic!("zip doesn't have correct payload");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
let zip_type = &context.zip_type;
|
||||
objiter::iter_type_init(context, zip_type);
|
||||
context.set_attr(zip_type, "__new__", context.new_rustfunc(zip_new));
|
||||
context.set_attr(zip_type, "__next__", context.new_rustfunc(zip_next));
|
||||
}
|
||||
@@ -7,6 +7,8 @@ use super::obj::objbytes;
|
||||
use super::obj::objcode;
|
||||
use super::obj::objcomplex;
|
||||
use super::obj::objdict;
|
||||
use super::obj::objenumerate;
|
||||
use super::obj::objfilter;
|
||||
use super::obj::objfloat;
|
||||
use super::obj::objframe;
|
||||
use super::obj::objfunction;
|
||||
@@ -14,16 +16,21 @@ use super::obj::objgenerator;
|
||||
use super::obj::objint;
|
||||
use super::obj::objiter;
|
||||
use super::obj::objlist;
|
||||
use super::obj::objmap;
|
||||
use super::obj::objmemory;
|
||||
use super::obj::objobject;
|
||||
use super::obj::objproperty;
|
||||
use super::obj::objrange;
|
||||
use super::obj::objset;
|
||||
use super::obj::objslice;
|
||||
use super::obj::objstr;
|
||||
use super::obj::objsuper;
|
||||
use super::obj::objtuple;
|
||||
use super::obj::objtype;
|
||||
use super::obj::objzip;
|
||||
use super::vm::VirtualMachine;
|
||||
use num_bigint::BigInt;
|
||||
use num_bigint::ToBigInt;
|
||||
use num_complex::Complex64;
|
||||
use num_traits::{One, Zero};
|
||||
use std::cell::RefCell;
|
||||
@@ -66,6 +73,10 @@ pub type PyObjectWeakRef = Weak<RefCell<PyObject>>;
|
||||
/// since exceptions are also python objects.
|
||||
pub type PyResult = Result<PyObjectRef, PyObjectRef>; // A valid value, or an exception
|
||||
|
||||
/// For attributes we do not use a dict, but a hashmap. This is probably
|
||||
/// faster, unordered, and only supports strings as keys.
|
||||
pub type PyAttributes = HashMap<String, PyObjectRef>;
|
||||
|
||||
impl fmt::Display for PyObject {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use self::TypeProtocol;
|
||||
@@ -105,6 +116,8 @@ pub struct PyContext {
|
||||
pub classmethod_type: PyObjectRef,
|
||||
pub code_type: PyObjectRef,
|
||||
pub dict_type: PyObjectRef,
|
||||
pub enumerate_type: PyObjectRef,
|
||||
pub filter_type: PyObjectRef,
|
||||
pub float_type: PyObjectRef,
|
||||
pub frame_type: PyObjectRef,
|
||||
pub frozenset_type: PyObjectRef,
|
||||
@@ -115,6 +128,7 @@ pub struct PyContext {
|
||||
pub true_value: PyObjectRef,
|
||||
pub false_value: PyObjectRef,
|
||||
pub list_type: PyObjectRef,
|
||||
pub map_type: PyObjectRef,
|
||||
pub memoryview_type: PyObjectRef,
|
||||
pub none: PyObjectRef,
|
||||
pub tuple_type: PyObjectRef,
|
||||
@@ -122,7 +136,10 @@ pub struct PyContext {
|
||||
pub staticmethod_type: PyObjectRef,
|
||||
pub super_type: PyObjectRef,
|
||||
pub str_type: PyObjectRef,
|
||||
pub range_type: PyObjectRef,
|
||||
pub slice_type: PyObjectRef,
|
||||
pub type_type: PyObjectRef,
|
||||
pub zip_type: PyObjectRef,
|
||||
pub function_type: PyObjectRef,
|
||||
pub property_type: PyObjectRef,
|
||||
pub module_type: PyObjectRef,
|
||||
@@ -154,14 +171,9 @@ pub fn create_type(
|
||||
name: &str,
|
||||
type_type: &PyObjectRef,
|
||||
base: &PyObjectRef,
|
||||
dict_type: &PyObjectRef,
|
||||
_dict_type: &PyObjectRef,
|
||||
) -> PyObjectRef {
|
||||
let dict = PyObject::new(
|
||||
PyObjectPayload::Dict {
|
||||
elements: HashMap::new(),
|
||||
},
|
||||
dict_type.clone(),
|
||||
);
|
||||
let dict = PyAttributes::new();
|
||||
objtype::new(type_type.clone(), name, vec![base.clone()], dict).unwrap()
|
||||
}
|
||||
|
||||
@@ -198,9 +210,15 @@ impl PyContext {
|
||||
let bytearray_type = create_type("bytearray", &type_type, &object_type, &dict_type);
|
||||
let tuple_type = create_type("tuple", &type_type, &object_type, &dict_type);
|
||||
let iter_type = create_type("iter", &type_type, &object_type, &dict_type);
|
||||
let enumerate_type = create_type("enumerate", &type_type, &object_type, &dict_type);
|
||||
let filter_type = create_type("filter", &type_type, &object_type, &dict_type);
|
||||
let map_type = create_type("map", &type_type, &object_type, &dict_type);
|
||||
let zip_type = create_type("zip", &type_type, &object_type, &dict_type);
|
||||
let bool_type = create_type("bool", &type_type, &int_type, &dict_type);
|
||||
let memoryview_type = create_type("memoryview", &type_type, &object_type, &dict_type);
|
||||
let code_type = create_type("code", &type_type, &int_type, &dict_type);
|
||||
let range_type = create_type("range", &type_type, &object_type, &dict_type);
|
||||
let slice_type = create_type("slice", &type_type, &object_type, &dict_type);
|
||||
let exceptions = exceptions::ExceptionZoo::new(&type_type, &object_type, &dict_type);
|
||||
|
||||
let none = PyObject::new(
|
||||
@@ -219,37 +237,43 @@ impl PyContext {
|
||||
bool_type.clone(),
|
||||
);
|
||||
let context = PyContext {
|
||||
bool_type: bool_type,
|
||||
memoryview_type: memoryview_type,
|
||||
bytearray_type: bytearray_type,
|
||||
bytes_type: bytes_type,
|
||||
code_type: code_type,
|
||||
complex_type: complex_type,
|
||||
classmethod_type: classmethod_type,
|
||||
int_type: int_type,
|
||||
float_type: float_type,
|
||||
frame_type: frame_type,
|
||||
staticmethod_type: staticmethod_type,
|
||||
list_type: list_type,
|
||||
set_type: set_type,
|
||||
frozenset_type: frozenset_type,
|
||||
true_value: true_value,
|
||||
false_value: false_value,
|
||||
tuple_type: tuple_type,
|
||||
iter_type: iter_type,
|
||||
dict_type: dict_type,
|
||||
none: none,
|
||||
str_type: str_type,
|
||||
bool_type,
|
||||
memoryview_type,
|
||||
bytearray_type,
|
||||
bytes_type,
|
||||
code_type,
|
||||
complex_type,
|
||||
classmethod_type,
|
||||
int_type,
|
||||
float_type,
|
||||
frame_type,
|
||||
staticmethod_type,
|
||||
list_type,
|
||||
set_type,
|
||||
frozenset_type,
|
||||
true_value,
|
||||
false_value,
|
||||
tuple_type,
|
||||
iter_type,
|
||||
enumerate_type,
|
||||
filter_type,
|
||||
map_type,
|
||||
zip_type,
|
||||
dict_type,
|
||||
none,
|
||||
str_type,
|
||||
range_type,
|
||||
slice_type,
|
||||
object: object_type,
|
||||
function_type: function_type,
|
||||
super_type: super_type,
|
||||
property_type: property_type,
|
||||
generator_type: generator_type,
|
||||
module_type: module_type,
|
||||
bound_method_type: bound_method_type,
|
||||
member_descriptor_type: member_descriptor_type,
|
||||
type_type: type_type,
|
||||
exceptions: exceptions,
|
||||
function_type,
|
||||
super_type,
|
||||
property_type,
|
||||
generator_type,
|
||||
module_type,
|
||||
bound_method_type,
|
||||
member_descriptor_type,
|
||||
type_type,
|
||||
exceptions,
|
||||
};
|
||||
objtype::init(&context);
|
||||
objlist::init(&context);
|
||||
@@ -267,9 +291,15 @@ impl PyContext {
|
||||
objproperty::init(&context);
|
||||
objmemory::init(&context);
|
||||
objstr::init(&context);
|
||||
objrange::init(&context);
|
||||
objslice::init(&context);
|
||||
objsuper::init(&context);
|
||||
objtuple::init(&context);
|
||||
objiter::init(&context);
|
||||
objenumerate::init(&context);
|
||||
objfilter::init(&context);
|
||||
objmap::init(&context);
|
||||
objzip::init(&context);
|
||||
objbool::init(&context);
|
||||
objcode::init(&context);
|
||||
objframe::init(&context);
|
||||
@@ -317,6 +347,14 @@ impl PyContext {
|
||||
self.set_type.clone()
|
||||
}
|
||||
|
||||
pub fn range_type(&self) -> PyObjectRef {
|
||||
self.range_type.clone()
|
||||
}
|
||||
|
||||
pub fn slice_type(&self) -> PyObjectRef {
|
||||
self.slice_type.clone()
|
||||
}
|
||||
|
||||
pub fn frozenset_type(&self) -> PyObjectRef {
|
||||
self.frozenset_type.clone()
|
||||
}
|
||||
@@ -337,6 +375,22 @@ impl PyContext {
|
||||
self.iter_type.clone()
|
||||
}
|
||||
|
||||
pub fn enumerate_type(&self) -> PyObjectRef {
|
||||
self.enumerate_type.clone()
|
||||
}
|
||||
|
||||
pub fn filter_type(&self) -> PyObjectRef {
|
||||
self.filter_type.clone()
|
||||
}
|
||||
|
||||
pub fn map_type(&self) -> PyObjectRef {
|
||||
self.map_type.clone()
|
||||
}
|
||||
|
||||
pub fn zip_type(&self) -> PyObjectRef {
|
||||
self.zip_type.clone()
|
||||
}
|
||||
|
||||
pub fn str_type(&self) -> PyObjectRef {
|
||||
self.str_type.clone()
|
||||
}
|
||||
@@ -383,16 +437,16 @@ impl PyContext {
|
||||
}
|
||||
|
||||
pub fn new_object(&self) -> PyObjectRef {
|
||||
PyObject::new(
|
||||
PyObjectPayload::Instance {
|
||||
dict: self.new_dict(),
|
||||
},
|
||||
self.object(),
|
||||
)
|
||||
self.new_instance(self.object(), None)
|
||||
}
|
||||
|
||||
pub fn new_int(&self, i: BigInt) -> PyObjectRef {
|
||||
PyObject::new(PyObjectPayload::Integer { value: i }, self.int_type())
|
||||
pub fn new_int<T: ToBigInt>(&self, i: T) -> PyObjectRef {
|
||||
PyObject::new(
|
||||
PyObjectPayload::Integer {
|
||||
value: i.to_bigint().unwrap(),
|
||||
},
|
||||
self.int_type(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_float(&self, i: f64) -> PyObjectRef {
|
||||
@@ -427,17 +481,11 @@ impl PyContext {
|
||||
}
|
||||
|
||||
pub fn new_tuple(&self, elements: Vec<PyObjectRef>) -> PyObjectRef {
|
||||
PyObject::new(
|
||||
PyObjectPayload::Sequence { elements: elements },
|
||||
self.tuple_type(),
|
||||
)
|
||||
PyObject::new(PyObjectPayload::Sequence { elements }, self.tuple_type())
|
||||
}
|
||||
|
||||
pub fn new_list(&self, elements: Vec<PyObjectRef>) -> PyObjectRef {
|
||||
PyObject::new(
|
||||
PyObjectPayload::Sequence { elements: elements },
|
||||
self.list_type(),
|
||||
)
|
||||
PyObject::new(PyObjectPayload::Sequence { elements }, self.list_type())
|
||||
}
|
||||
|
||||
pub fn new_set(&self) -> PyObjectRef {
|
||||
@@ -457,17 +505,14 @@ impl PyContext {
|
||||
}
|
||||
|
||||
pub fn new_class(&self, name: &str, base: PyObjectRef) -> PyObjectRef {
|
||||
objtype::new(self.type_type(), name, vec![base], self.new_dict()).unwrap()
|
||||
objtype::new(self.type_type(), name, vec![base], PyAttributes::new()).unwrap()
|
||||
}
|
||||
|
||||
pub fn new_scope(&self, parent: Option<PyObjectRef>) -> PyObjectRef {
|
||||
let locals = self.new_dict();
|
||||
let scope = Scope {
|
||||
locals: locals,
|
||||
parent: parent,
|
||||
};
|
||||
let scope = Scope { locals, parent };
|
||||
PyObject {
|
||||
payload: PyObjectPayload::Scope { scope: scope },
|
||||
payload: PyObjectPayload::Scope { scope },
|
||||
typ: None,
|
||||
}
|
||||
.into_ref()
|
||||
@@ -506,7 +551,7 @@ impl PyContext {
|
||||
}
|
||||
|
||||
pub fn new_frame(&self, frame: Frame) -> PyObjectRef {
|
||||
PyObject::new(PyObjectPayload::Frame { frame: frame }, self.frame_type())
|
||||
PyObject::new(PyObjectPayload::Frame { frame }, self.frame_type())
|
||||
}
|
||||
|
||||
pub fn new_property<F: 'static + Fn(&mut VirtualMachine, PyFuncArgs) -> PyResult>(
|
||||
@@ -514,12 +559,7 @@ impl PyContext {
|
||||
function: F,
|
||||
) -> PyObjectRef {
|
||||
let fget = self.new_rustfunc(function);
|
||||
let py_obj = PyObject::new(
|
||||
PyObjectPayload::Instance {
|
||||
dict: self.new_dict(),
|
||||
},
|
||||
self.property_type(),
|
||||
);
|
||||
let py_obj = self.new_instance(self.property_type(), None);
|
||||
self.set_attr(&py_obj, "fget", fget.clone());
|
||||
py_obj
|
||||
}
|
||||
@@ -537,8 +577,8 @@ impl PyContext {
|
||||
PyObject::new(
|
||||
PyObjectPayload::Function {
|
||||
code: code_obj,
|
||||
scope: scope,
|
||||
defaults: defaults,
|
||||
scope,
|
||||
defaults,
|
||||
},
|
||||
self.function_type(),
|
||||
)
|
||||
@@ -546,10 +586,7 @@ impl PyContext {
|
||||
|
||||
pub fn new_bound_method(&self, function: PyObjectRef, object: PyObjectRef) -> PyObjectRef {
|
||||
PyObject::new(
|
||||
PyObjectPayload::BoundMethod {
|
||||
function: function,
|
||||
object: object,
|
||||
},
|
||||
PyObjectPayload::BoundMethod { function, object },
|
||||
self.bound_method_type(),
|
||||
)
|
||||
}
|
||||
@@ -558,13 +595,23 @@ impl PyContext {
|
||||
&self,
|
||||
function: F,
|
||||
) -> PyObjectRef {
|
||||
let dict = self.new_dict();
|
||||
self.set_item(&dict, "function", self.new_rustfunc(function));
|
||||
self.new_instance(dict, self.member_descriptor_type())
|
||||
let mut dict = PyAttributes::new();
|
||||
dict.insert("function".to_string(), self.new_rustfunc(function));
|
||||
self.new_instance(self.member_descriptor_type(), Some(dict))
|
||||
}
|
||||
|
||||
pub fn new_instance(&self, dict: PyObjectRef, class: PyObjectRef) -> PyObjectRef {
|
||||
PyObject::new(PyObjectPayload::Instance { dict: dict }, class)
|
||||
pub fn new_instance(&self, class: PyObjectRef, dict: Option<PyAttributes>) -> PyObjectRef {
|
||||
let dict = if let Some(dict) = dict {
|
||||
dict
|
||||
} else {
|
||||
PyAttributes::new()
|
||||
};
|
||||
PyObject::new(
|
||||
PyObjectPayload::Instance {
|
||||
dict: RefCell::new(dict),
|
||||
},
|
||||
class,
|
||||
)
|
||||
}
|
||||
|
||||
// Item set/get:
|
||||
@@ -574,10 +621,7 @@ impl PyContext {
|
||||
let key = self.new_str(key.to_string());
|
||||
objdict::set_item_in_content(elements, &key, &v);
|
||||
}
|
||||
PyObjectPayload::Module {
|
||||
name: _,
|
||||
ref mut dict,
|
||||
} => self.set_item(dict, key, v),
|
||||
PyObjectPayload::Module { ref mut dict, .. } => self.set_item(dict, key, v),
|
||||
PyObjectPayload::Scope { ref mut scope } => {
|
||||
self.set_item(&scope.locals, key, v);
|
||||
}
|
||||
@@ -594,13 +638,10 @@ impl PyContext {
|
||||
|
||||
pub fn set_attr(&self, obj: &PyObjectRef, attr_name: &str, value: PyObjectRef) {
|
||||
match obj.borrow().payload {
|
||||
PyObjectPayload::Module { name: _, ref dict } => self.set_item(dict, attr_name, value),
|
||||
PyObjectPayload::Instance { ref dict } => self.set_item(dict, attr_name, value),
|
||||
PyObjectPayload::Class {
|
||||
name: _,
|
||||
ref dict,
|
||||
mro: _,
|
||||
} => self.set_item(dict, attr_name, value),
|
||||
PyObjectPayload::Module { ref dict, .. } => self.set_item(dict, attr_name, value),
|
||||
PyObjectPayload::Instance { ref dict } | PyObjectPayload::Class { ref dict, .. } => {
|
||||
dict.borrow_mut().insert(attr_name.to_string(), value);
|
||||
}
|
||||
ref payload => unimplemented!("set_attr unimplemented for: {:?}", payload),
|
||||
};
|
||||
}
|
||||
@@ -661,10 +702,7 @@ pub trait ParentProtocol {
|
||||
impl ParentProtocol for PyObjectRef {
|
||||
fn has_parent(&self) -> bool {
|
||||
match self.borrow().payload {
|
||||
PyObjectPayload::Scope { ref scope } => match scope.parent {
|
||||
Some(_) => true,
|
||||
None => false,
|
||||
},
|
||||
PyObjectPayload::Scope { ref scope } => scope.parent.is_some(),
|
||||
_ => panic!("Only scopes have parent (not {:?}", self),
|
||||
}
|
||||
}
|
||||
@@ -688,7 +726,7 @@ pub trait AttributeProtocol {
|
||||
fn class_get_item(class: &PyObjectRef, attr_name: &str) -> Option<PyObjectRef> {
|
||||
let class = class.borrow();
|
||||
match class.payload {
|
||||
PyObjectPayload::Class { ref dict, .. } => dict.get_item(attr_name),
|
||||
PyObjectPayload::Class { ref dict, .. } => dict.borrow().get(attr_name).map(|v| v.clone()),
|
||||
_ => panic!("Only classes should be in MRO!"),
|
||||
}
|
||||
}
|
||||
@@ -696,7 +734,7 @@ fn class_get_item(class: &PyObjectRef, attr_name: &str) -> Option<PyObjectRef> {
|
||||
fn class_has_item(class: &PyObjectRef, attr_name: &str) -> bool {
|
||||
let class = class.borrow();
|
||||
match class.payload {
|
||||
PyObjectPayload::Class { ref dict, .. } => dict.contains_key(attr_name),
|
||||
PyObjectPayload::Class { ref dict, .. } => dict.borrow().contains_key(attr_name),
|
||||
_ => panic!("Only classes should be in MRO!"),
|
||||
}
|
||||
}
|
||||
@@ -710,14 +748,16 @@ impl AttributeProtocol for PyObjectRef {
|
||||
if let Some(item) = class_get_item(self, attr_name) {
|
||||
return Some(item);
|
||||
}
|
||||
for ref class in mro {
|
||||
for class in mro {
|
||||
if let Some(item) = class_get_item(class, attr_name) {
|
||||
return Some(item);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
PyObjectPayload::Instance { ref dict } => dict.get_item(attr_name),
|
||||
PyObjectPayload::Instance { ref dict } => {
|
||||
dict.borrow().get(attr_name).map(|v| v.clone())
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -725,12 +765,11 @@ impl AttributeProtocol for PyObjectRef {
|
||||
fn has_attr(&self, attr_name: &str) -> bool {
|
||||
let obj = self.borrow();
|
||||
match obj.payload {
|
||||
PyObjectPayload::Module { name: _, ref dict } => dict.contains_key(attr_name),
|
||||
PyObjectPayload::Module { ref dict, .. } => dict.contains_key(attr_name),
|
||||
PyObjectPayload::Class { ref mro, .. } => {
|
||||
class_has_item(self, attr_name)
|
||||
|| mro.into_iter().any(|d| class_has_item(d, attr_name))
|
||||
class_has_item(self, attr_name) || mro.iter().any(|d| class_has_item(d, attr_name))
|
||||
}
|
||||
PyObjectPayload::Instance { ref dict } => dict.contains_key(attr_name),
|
||||
PyObjectPayload::Instance { ref dict } => dict.borrow().contains_key(attr_name),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@@ -748,7 +787,7 @@ impl DictProtocol for PyObjectRef {
|
||||
PyObjectPayload::Dict { ref elements } => {
|
||||
objdict::content_contains_key_str(elements, k)
|
||||
}
|
||||
PyObjectPayload::Module { name: _, ref dict } => dict.contains_key(k),
|
||||
PyObjectPayload::Module { ref dict, .. } => dict.contains_key(k),
|
||||
PyObjectPayload::Scope { ref scope } => scope.locals.contains_key(k),
|
||||
ref payload => unimplemented!("TODO {:?}", payload),
|
||||
}
|
||||
@@ -757,7 +796,7 @@ impl DictProtocol for PyObjectRef {
|
||||
fn get_item(&self, k: &str) -> Option<PyObjectRef> {
|
||||
match self.borrow().payload {
|
||||
PyObjectPayload::Dict { ref elements } => objdict::content_get_key_str(elements, k),
|
||||
PyObjectPayload::Module { name: _, ref dict } => dict.get_item(k),
|
||||
PyObjectPayload::Module { ref dict, .. } => dict.get_item(k),
|
||||
PyObjectPayload::Scope { ref scope } => scope.locals.get_item(k),
|
||||
_ => panic!("TODO"),
|
||||
}
|
||||
@@ -765,8 +804,8 @@ impl DictProtocol for PyObjectRef {
|
||||
|
||||
fn get_key_value_pairs(&self) -> Vec<(PyObjectRef, PyObjectRef)> {
|
||||
match self.borrow().payload {
|
||||
PyObjectPayload::Dict { elements: _ } => objdict::get_key_value_pairs(self),
|
||||
PyObjectPayload::Module { name: _, ref dict } => dict.get_key_value_pairs(),
|
||||
PyObjectPayload::Dict { .. } => objdict::get_key_value_pairs(self),
|
||||
PyObjectPayload::Module { ref dict, .. } => dict.get_key_value_pairs(),
|
||||
PyObjectPayload::Scope { ref scope } => scope.locals.get_key_value_pairs(),
|
||||
_ => panic!("TODO"),
|
||||
}
|
||||
@@ -808,10 +847,7 @@ impl PyFuncArgs {
|
||||
for name in kwarg_names.iter().rev() {
|
||||
kwargs.push((name.clone(), args.pop().unwrap()));
|
||||
}
|
||||
PyFuncArgs {
|
||||
args: args,
|
||||
kwargs: kwargs,
|
||||
}
|
||||
PyFuncArgs { args, kwargs }
|
||||
}
|
||||
|
||||
pub fn insert(&self, item: PyObjectRef) -> PyFuncArgs {
|
||||
@@ -820,7 +856,7 @@ impl PyFuncArgs {
|
||||
kwargs: self.kwargs.clone(),
|
||||
};
|
||||
args.args.insert(0, item);
|
||||
return args;
|
||||
args
|
||||
}
|
||||
|
||||
pub fn shift(&mut self) -> PyObjectRef {
|
||||
@@ -879,10 +915,28 @@ pub enum PyObjectPayload {
|
||||
position: usize,
|
||||
iterated_obj: PyObjectRef,
|
||||
},
|
||||
EnumerateIterator {
|
||||
counter: BigInt,
|
||||
iterator: PyObjectRef,
|
||||
},
|
||||
FilterIterator {
|
||||
predicate: PyObjectRef,
|
||||
iterator: PyObjectRef,
|
||||
},
|
||||
MapIterator {
|
||||
mapper: PyObjectRef,
|
||||
iterators: Vec<PyObjectRef>,
|
||||
},
|
||||
ZipIterator {
|
||||
iterators: Vec<PyObjectRef>,
|
||||
},
|
||||
Slice {
|
||||
start: Option<i32>,
|
||||
stop: Option<i32>,
|
||||
step: Option<i32>,
|
||||
start: Option<BigInt>,
|
||||
stop: Option<BigInt>,
|
||||
step: Option<BigInt>,
|
||||
},
|
||||
Range {
|
||||
range: objrange::RangeType,
|
||||
},
|
||||
MemoryView {
|
||||
obj: PyObjectRef,
|
||||
@@ -915,14 +969,14 @@ pub enum PyObjectPayload {
|
||||
None,
|
||||
Class {
|
||||
name: String,
|
||||
dict: PyObjectRef,
|
||||
dict: RefCell<PyAttributes>,
|
||||
mro: Vec<PyObjectRef>,
|
||||
},
|
||||
WeakRef {
|
||||
referent: PyObjectWeakRef,
|
||||
},
|
||||
Instance {
|
||||
dict: PyObjectRef,
|
||||
dict: RefCell<PyAttributes>,
|
||||
},
|
||||
RustFunction {
|
||||
function: Box<Fn(&mut VirtualMachine, PyFuncArgs) -> PyResult>,
|
||||
@@ -932,43 +986,37 @@ pub enum PyObjectPayload {
|
||||
impl fmt::Debug for PyObjectPayload {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&PyObjectPayload::String { ref value } => write!(f, "str \"{}\"", value),
|
||||
&PyObjectPayload::Integer { ref value } => write!(f, "int {}", value),
|
||||
&PyObjectPayload::Float { ref value } => write!(f, "float {}", value),
|
||||
&PyObjectPayload::Complex { ref value } => write!(f, "complex {}", value),
|
||||
&PyObjectPayload::Bytes { ref value } => write!(f, "bytes/bytearray {:?}", value),
|
||||
&PyObjectPayload::MemoryView { ref obj } => write!(f, "bytes/bytearray {:?}", obj),
|
||||
&PyObjectPayload::Sequence { elements: _ } => write!(f, "list or tuple"),
|
||||
&PyObjectPayload::Dict { elements: _ } => write!(f, "dict"),
|
||||
&PyObjectPayload::Set { elements: _ } => write!(f, "set"),
|
||||
&PyObjectPayload::WeakRef { .. } => write!(f, "weakref"),
|
||||
&PyObjectPayload::Iterator {
|
||||
position: _,
|
||||
iterated_obj: _,
|
||||
} => write!(f, "iterator"),
|
||||
&PyObjectPayload::Slice {
|
||||
start: _,
|
||||
stop: _,
|
||||
step: _,
|
||||
} => write!(f, "slice"),
|
||||
&PyObjectPayload::Code { ref code } => write!(f, "code: {:?}", code),
|
||||
&PyObjectPayload::Function { .. } => write!(f, "function"),
|
||||
&PyObjectPayload::Generator { .. } => write!(f, "generator"),
|
||||
&PyObjectPayload::BoundMethod {
|
||||
PyObjectPayload::String { ref value } => write!(f, "str \"{}\"", value),
|
||||
PyObjectPayload::Integer { ref value } => write!(f, "int {}", value),
|
||||
PyObjectPayload::Float { ref value } => write!(f, "float {}", value),
|
||||
PyObjectPayload::Complex { ref value } => write!(f, "complex {}", value),
|
||||
PyObjectPayload::Bytes { ref value } => write!(f, "bytes/bytearray {:?}", value),
|
||||
PyObjectPayload::MemoryView { ref obj } => write!(f, "bytes/bytearray {:?}", obj),
|
||||
PyObjectPayload::Sequence { .. } => write!(f, "list or tuple"),
|
||||
PyObjectPayload::Dict { .. } => write!(f, "dict"),
|
||||
PyObjectPayload::Set { .. } => write!(f, "set"),
|
||||
PyObjectPayload::WeakRef { .. } => write!(f, "weakref"),
|
||||
PyObjectPayload::Range { .. } => write!(f, "range"),
|
||||
PyObjectPayload::Iterator { .. } => write!(f, "iterator"),
|
||||
PyObjectPayload::EnumerateIterator { .. } => write!(f, "enumerate"),
|
||||
PyObjectPayload::FilterIterator { .. } => write!(f, "filter"),
|
||||
PyObjectPayload::MapIterator { .. } => write!(f, "map"),
|
||||
PyObjectPayload::ZipIterator { .. } => write!(f, "zip"),
|
||||
PyObjectPayload::Slice { .. } => write!(f, "slice"),
|
||||
PyObjectPayload::Code { ref code } => write!(f, "code: {:?}", code),
|
||||
PyObjectPayload::Function { .. } => write!(f, "function"),
|
||||
PyObjectPayload::Generator { .. } => write!(f, "generator"),
|
||||
PyObjectPayload::BoundMethod {
|
||||
ref function,
|
||||
ref object,
|
||||
} => write!(f, "bound-method: {:?} of {:?}", function, object),
|
||||
&PyObjectPayload::Module { name: _, dict: _ } => write!(f, "module"),
|
||||
&PyObjectPayload::Scope { scope: _ } => write!(f, "scope"),
|
||||
&PyObjectPayload::None => write!(f, "None"),
|
||||
&PyObjectPayload::Class {
|
||||
ref name,
|
||||
dict: _,
|
||||
mro: _,
|
||||
} => write!(f, "class {:?}", name),
|
||||
&PyObjectPayload::Instance { dict: _ } => write!(f, "instance"),
|
||||
&PyObjectPayload::RustFunction { function: _ } => write!(f, "rust function"),
|
||||
&PyObjectPayload::Frame { .. } => write!(f, "frame"),
|
||||
PyObjectPayload::Module { .. } => write!(f, "module"),
|
||||
PyObjectPayload::Scope { .. } => write!(f, "scope"),
|
||||
PyObjectPayload::None => write!(f, "None"),
|
||||
PyObjectPayload::Class { ref name, .. } => write!(f, "class {:?}", name),
|
||||
PyObjectPayload::Instance { .. } => write!(f, "instance"),
|
||||
PyObjectPayload::RustFunction { .. } => write!(f, "rust function"),
|
||||
PyObjectPayload::Frame { .. } => write!(f, "frame"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -979,7 +1027,7 @@ impl PyObject {
|
||||
/* dict: PyObjectRef,*/ typ: PyObjectRef,
|
||||
) -> PyObjectRef {
|
||||
PyObject {
|
||||
payload: payload,
|
||||
payload,
|
||||
typ: Some(typ),
|
||||
// dict: HashMap::new(), // dict,
|
||||
}
|
||||
@@ -1024,22 +1072,23 @@ impl PyObject {
|
||||
PyObjectPayload::Class {
|
||||
ref name,
|
||||
dict: ref _dict,
|
||||
mro: _,
|
||||
..
|
||||
} => format!("<class '{}'>", name),
|
||||
PyObjectPayload::Instance { dict: _ } => format!("<instance>"),
|
||||
PyObjectPayload::Code { code: _ } => format!("<code>"),
|
||||
PyObjectPayload::Function { .. } => format!("<func>"),
|
||||
PyObjectPayload::Generator { .. } => format!("<generator>"),
|
||||
PyObjectPayload::Frame { .. } => format!("<frame>"),
|
||||
PyObjectPayload::BoundMethod { .. } => format!("<bound-method>"),
|
||||
PyObjectPayload::RustFunction { function: _ } => format!("<rustfunc>"),
|
||||
PyObjectPayload::Module { ref name, dict: _ } => format!("<module '{}'>", name),
|
||||
PyObjectPayload::Instance { .. } => "<instance>".to_string(),
|
||||
PyObjectPayload::Code { .. } => "<code>".to_string(),
|
||||
PyObjectPayload::Function { .. } => "<func>".to_string(),
|
||||
PyObjectPayload::Generator { .. } => "<generator>".to_string(),
|
||||
PyObjectPayload::Frame { .. } => "<frame>".to_string(),
|
||||
PyObjectPayload::BoundMethod { .. } => "<bound-method>".to_string(),
|
||||
PyObjectPayload::RustFunction { .. } => "<rustfunc>".to_string(),
|
||||
PyObjectPayload::Module { ref name, .. } => format!("<module '{}'>", name),
|
||||
PyObjectPayload::Scope { ref scope } => format!("<scope '{:?}'>", scope),
|
||||
PyObjectPayload::Slice {
|
||||
ref start,
|
||||
ref stop,
|
||||
ref step,
|
||||
} => format!("<slice '{:?}:{:?}:{:?}'>", start, stop, step),
|
||||
PyObjectPayload::Range { ref range } => format!("<range '{:?}'>", range),
|
||||
PyObjectPayload::Iterator {
|
||||
ref position,
|
||||
ref iterated_obj,
|
||||
@@ -1048,6 +1097,10 @@ impl PyObject {
|
||||
position,
|
||||
iterated_obj.borrow_mut().str()
|
||||
),
|
||||
PyObjectPayload::EnumerateIterator { .. } => format!("<enumerate>"),
|
||||
PyObjectPayload::FilterIterator { .. } => format!("<filter>"),
|
||||
PyObjectPayload::MapIterator { .. } => format!("<map>"),
|
||||
PyObjectPayload::ZipIterator { .. } => format!("<zip>"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,9 +9,7 @@ use self::rustpython_parser::{ast, parser};
|
||||
use super::super::obj::{objstr, objtype};
|
||||
use super::super::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol};
|
||||
use super::super::VirtualMachine;
|
||||
use num_bigint::ToBigInt;
|
||||
use num_complex::Complex64;
|
||||
use num_traits::One;
|
||||
use std::ops::Deref;
|
||||
|
||||
/*
|
||||
@@ -47,8 +45,7 @@ fn program_to_ast(ctx: &PyContext, program: &ast::Program) -> PyObjectRef {
|
||||
fn create_node(ctx: &PyContext, _name: &str) -> PyObjectRef {
|
||||
// TODO: instantiate a class of type given by name
|
||||
// TODO: lookup in the current module?
|
||||
let node = ctx.new_object();
|
||||
node
|
||||
ctx.new_object()
|
||||
}
|
||||
|
||||
fn statements_to_ast(ctx: &PyContext, statements: &[ast::LocatedStatement]) -> PyObjectRef {
|
||||
@@ -64,9 +61,8 @@ fn statement_to_ast(ctx: &PyContext, statement: &ast::LocatedStatement) -> PyObj
|
||||
ast::Statement::ClassDef {
|
||||
name,
|
||||
body,
|
||||
bases: _,
|
||||
keywords: _,
|
||||
decorator_list,
|
||||
..
|
||||
} => {
|
||||
let node = create_node(ctx, "ClassDef");
|
||||
|
||||
@@ -102,18 +98,9 @@ fn statement_to_ast(ctx: &PyContext, statement: &ast::LocatedStatement) -> PyObj
|
||||
ctx.set_attr(&node, "decorator_list", py_decorator_list);
|
||||
node
|
||||
}
|
||||
ast::Statement::Continue => {
|
||||
let node = create_node(ctx, "Continue");
|
||||
node
|
||||
}
|
||||
ast::Statement::Break => {
|
||||
let node = create_node(ctx, "Break");
|
||||
node
|
||||
}
|
||||
ast::Statement::Pass => {
|
||||
let node = create_node(ctx, "Pass");
|
||||
node
|
||||
}
|
||||
ast::Statement::Continue => create_node(ctx, "Continue"),
|
||||
ast::Statement::Break => create_node(ctx, "Break"),
|
||||
ast::Statement::Pass => create_node(ctx, "Pass"),
|
||||
ast::Statement::Assert { test, msg } => {
|
||||
let node = create_node(ctx, "Pass");
|
||||
|
||||
@@ -130,12 +117,8 @@ fn statement_to_ast(ctx: &PyContext, statement: &ast::LocatedStatement) -> PyObj
|
||||
ast::Statement::Delete { targets } => {
|
||||
let node = create_node(ctx, "Delete");
|
||||
|
||||
let py_targets = ctx.new_tuple(
|
||||
targets
|
||||
.into_iter()
|
||||
.map(|v| expression_to_ast(ctx, v))
|
||||
.collect(),
|
||||
);
|
||||
let py_targets =
|
||||
ctx.new_tuple(targets.iter().map(|v| expression_to_ast(ctx, v)).collect());
|
||||
ctx.set_attr(&node, "targets", py_targets);
|
||||
|
||||
node
|
||||
@@ -144,12 +127,7 @@ fn statement_to_ast(ctx: &PyContext, statement: &ast::LocatedStatement) -> PyObj
|
||||
let node = create_node(ctx, "Return");
|
||||
|
||||
let py_value = if let Some(value) = value {
|
||||
ctx.new_tuple(
|
||||
value
|
||||
.into_iter()
|
||||
.map(|v| expression_to_ast(ctx, v))
|
||||
.collect(),
|
||||
)
|
||||
ctx.new_tuple(value.iter().map(|v| expression_to_ast(ctx, v)).collect())
|
||||
} else {
|
||||
ctx.none()
|
||||
};
|
||||
@@ -233,13 +211,13 @@ fn statement_to_ast(ctx: &PyContext, statement: &ast::LocatedStatement) -> PyObj
|
||||
};
|
||||
|
||||
// set lineno on node:
|
||||
let lineno = ctx.new_int(statement.location.get_row().to_bigint().unwrap());
|
||||
let lineno = ctx.new_int(statement.location.get_row());
|
||||
ctx.set_attr(&node, "lineno", lineno);
|
||||
|
||||
node
|
||||
}
|
||||
|
||||
fn expressions_to_ast(ctx: &PyContext, expressions: &Vec<ast::Expression>) -> PyObjectRef {
|
||||
fn expressions_to_ast(ctx: &PyContext, expressions: &[ast::Expression]) -> PyObjectRef {
|
||||
let mut py_expression_nodes = vec![];
|
||||
for expression in expressions {
|
||||
py_expression_nodes.push(expression_to_ast(ctx, expression));
|
||||
@@ -249,11 +227,7 @@ fn expressions_to_ast(ctx: &PyContext, expressions: &Vec<ast::Expression>) -> Py
|
||||
|
||||
fn expression_to_ast(ctx: &PyContext, expression: &ast::Expression) -> PyObjectRef {
|
||||
let node = match &expression {
|
||||
ast::Expression::Call {
|
||||
function,
|
||||
args,
|
||||
keywords: _,
|
||||
} => {
|
||||
ast::Expression::Call { function, args, .. } => {
|
||||
let node = create_node(ctx, "Call");
|
||||
|
||||
let py_func_ast = expression_to_ast(ctx, function);
|
||||
@@ -390,7 +364,7 @@ fn expression_to_ast(ctx: &PyContext, expression: &ast::Expression) -> PyObjectR
|
||||
let node = create_node(ctx, "Num");
|
||||
|
||||
let py_n = match value {
|
||||
ast::Number::Integer { value } => ctx.new_int(value.to_bigint().unwrap()),
|
||||
ast::Number::Integer { value } => ctx.new_int(value.clone()),
|
||||
ast::Number::Float { value } => ctx.new_float(*value),
|
||||
ast::Number::Complex { real, imag } => {
|
||||
ctx.new_complex(Complex64::new(*real, *imag))
|
||||
@@ -555,7 +529,7 @@ fn expression_to_ast(ctx: &PyContext, expression: &ast::Expression) -> PyObjectR
|
||||
};
|
||||
|
||||
// TODO: retrieve correct lineno:
|
||||
let lineno = ctx.new_int(One::one());
|
||||
let lineno = ctx.new_int(1);
|
||||
ctx.set_attr(&node, "lineno", lineno);
|
||||
|
||||
node
|
||||
|
||||
@@ -2,23 +2,23 @@
|
||||
* I/O core tools.
|
||||
*/
|
||||
|
||||
//library imports
|
||||
use std::collections::HashSet;
|
||||
|
||||
use std::io::prelude::*;
|
||||
use std::os::unix::io::{FromRawFd, IntoRawFd};
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std::io::BufReader;
|
||||
|
||||
//3rd party imports
|
||||
use num_bigint::ToBigInt;
|
||||
use num_traits::ToPrimitive;
|
||||
|
||||
//custom imports
|
||||
use super::super::obj::objbytes;
|
||||
use super::super::obj::objint;
|
||||
use super::super::obj::objstr;
|
||||
use super::super::obj::objtype;
|
||||
use super::os;
|
||||
|
||||
use num_bigint::ToBigInt;
|
||||
use num_traits::ToPrimitive;
|
||||
|
||||
use super::super::pyobject::{
|
||||
AttributeProtocol, BufferProtocol, PyContext, PyFuncArgs, PyObjectPayload, PyObjectRef,
|
||||
PyResult, TypeProtocol,
|
||||
@@ -26,8 +26,8 @@ use super::super::pyobject::{
|
||||
|
||||
use super::super::vm::VirtualMachine;
|
||||
|
||||
fn compute_c_flag(mode: &String) -> u16 {
|
||||
match mode.as_ref() {
|
||||
fn compute_c_flag(mode: &str) -> u16 {
|
||||
match mode {
|
||||
"w" => 512,
|
||||
"x" => 512,
|
||||
"a" => 8,
|
||||
@@ -81,17 +81,12 @@ fn buffered_reader_read(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
//bytes are returned (when the end of the file is reached).
|
||||
while length == buff_size {
|
||||
let raw_read = vm.get_method(raw.clone(), &"readinto".to_string()).unwrap();
|
||||
match vm.invoke(raw_read, PyFuncArgs::new(vec![buffer.clone()], vec![])) {
|
||||
Ok(_) => {}
|
||||
Err(_) => return Err(vm.new_value_error("IO Error".to_string())),
|
||||
}
|
||||
vm.invoke(raw_read, PyFuncArgs::new(vec![buffer.clone()], vec![]))
|
||||
.map_err(|_| vm.new_value_error("IO Error".to_string()))?;
|
||||
|
||||
//Copy bytes from the buffer vector into the results vector
|
||||
match buffer.borrow_mut().payload {
|
||||
PyObjectPayload::Bytes { ref mut value } => {
|
||||
result.extend(value.iter().cloned());
|
||||
}
|
||||
_ => {}
|
||||
if let PyObjectPayload::Bytes { ref mut value } = buffer.borrow_mut().payload {
|
||||
result.extend(value.iter().cloned());
|
||||
};
|
||||
|
||||
let len = vm.get_method(buffer.clone(), &"__len__".to_string());
|
||||
@@ -110,18 +105,18 @@ fn file_io_init(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
optional = [(mode, Some(vm.ctx.str_type()))]
|
||||
);
|
||||
|
||||
let rust_mode = match mode {
|
||||
Some(m) => objstr::get_value(m),
|
||||
None => "r".to_string(),
|
||||
};
|
||||
let rust_mode = mode.map_or("r".to_string(), |m| objstr::get_value(m));
|
||||
|
||||
match compute_c_flag(&rust_mode).to_bigint() {
|
||||
Some(os_mode) => {
|
||||
let args = vec![name.clone(), vm.ctx.new_int(os_mode)];
|
||||
let fileno = os::os_open(vm, PyFuncArgs::new(args, vec![]));
|
||||
let file_no = os::os_open(vm, PyFuncArgs::new(args, vec![]))?;
|
||||
|
||||
vm.ctx.set_attr(&file_io, "name", name.clone());
|
||||
vm.ctx.set_attr(&file_io, "fileno", fileno.unwrap());
|
||||
vm.ctx.set_attr(&file_io, "fileno", file_no);
|
||||
vm.ctx.set_attr(&file_io, "closefd", vm.new_bool(false));
|
||||
vm.ctx.set_attr(&file_io, "closed", vm.new_bool(false));
|
||||
|
||||
Ok(vm.get_none())
|
||||
}
|
||||
None => Err(vm.new_type_error(format!("invalid mode {}", rust_mode))),
|
||||
@@ -166,30 +161,25 @@ fn file_io_readinto(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
let py_length = vm.invoke(len_method.unwrap(), PyFuncArgs::default());
|
||||
let length = objint::get_value(&py_length.unwrap()).to_u64().unwrap();
|
||||
|
||||
let fileno = file_io.get_attr("fileno").unwrap();
|
||||
let raw_fd = objint::get_value(&fileno).to_i32().unwrap();
|
||||
let file_no = file_io.get_attr("fileno").unwrap();
|
||||
let raw_fd = objint::get_value(&file_no).to_i64().unwrap();
|
||||
|
||||
//unsafe block - creates file handle from the UNIX file descriptor
|
||||
//raw_fd is supported on UNIX only. This will need to be extended
|
||||
//to support windows - i.e. raw file_handles
|
||||
let handle = unsafe { File::from_raw_fd(raw_fd) };
|
||||
//extract unix file descriptor.
|
||||
let handle = os::rust_file(raw_fd);
|
||||
|
||||
let mut f = handle.take(length);
|
||||
match obj.borrow_mut().payload {
|
||||
if let PyObjectPayload::Bytes { ref mut value } = obj.borrow_mut().payload {
|
||||
//TODO: Implement for MemoryView
|
||||
PyObjectPayload::Bytes { ref mut value } => {
|
||||
value.clear();
|
||||
match f.read_to_end(&mut *value) {
|
||||
Ok(_) => {}
|
||||
Err(_) => return Err(vm.new_value_error("Error reading from Take".to_string())),
|
||||
}
|
||||
|
||||
value.clear();
|
||||
match f.read_to_end(&mut *value) {
|
||||
Ok(_) => {}
|
||||
Err(_) => return Err(vm.new_value_error("Error reading from Take".to_string())),
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
let new_handle = f.into_inner().into_raw_fd().to_bigint();
|
||||
vm.ctx
|
||||
.set_attr(&file_io, "fileno", vm.ctx.new_int(new_handle.unwrap()));
|
||||
let updated = os::raw_file_number(f.into_inner());
|
||||
vm.ctx.set_attr(&file_io, "fileno", vm.ctx.new_int(updated));
|
||||
Ok(vm.get_none())
|
||||
}
|
||||
|
||||
@@ -200,25 +190,24 @@ fn file_io_write(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
required = [(file_io, None), (obj, Some(vm.ctx.bytes_type()))]
|
||||
);
|
||||
|
||||
let fileno = file_io.get_attr("fileno").unwrap();
|
||||
let raw_fd = objint::get_value(&fileno).to_i32().unwrap();
|
||||
let file_no = file_io.get_attr("fileno").unwrap();
|
||||
let raw_fd = objint::get_value(&file_no).to_i64().unwrap();
|
||||
|
||||
//unsafe block - creates file handle from the UNIX file descriptor
|
||||
//raw_fd is supported on UNIX only. This will need to be extended
|
||||
//to support windows - i.e. raw file_handles
|
||||
let mut handle = unsafe { File::from_raw_fd(raw_fd) };
|
||||
let mut handle = os::rust_file(raw_fd);
|
||||
|
||||
match obj.borrow_mut().payload {
|
||||
PyObjectPayload::Bytes { ref mut value } => {
|
||||
match handle.write(&value[..]) {
|
||||
Ok(len) => {
|
||||
//reset raw fd on the FileIO object
|
||||
let new_handle = handle.into_raw_fd().to_bigint();
|
||||
vm.ctx
|
||||
.set_attr(&file_io, "fileno", vm.ctx.new_int(new_handle.unwrap()));
|
||||
let updated = os::raw_file_number(handle);
|
||||
vm.ctx.set_attr(&file_io, "fileno", vm.ctx.new_int(updated));
|
||||
|
||||
//return number of bytes written
|
||||
Ok(vm.ctx.new_int(len.to_bigint().unwrap()))
|
||||
Ok(vm.ctx.new_int(len))
|
||||
}
|
||||
Err(_) => Err(vm.new_value_error("Error Writing Bytes to Handle".to_string())),
|
||||
}
|
||||
@@ -280,15 +269,11 @@ pub fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
let module = mk_module(&vm.ctx);
|
||||
|
||||
//mode is optional: 'rt' is the default mode (open from reading text)
|
||||
let rust_mode = if let Some(m) = mode {
|
||||
objstr::get_value(m)
|
||||
} else {
|
||||
"rt".to_string()
|
||||
};
|
||||
let rust_mode = mode.map_or("rt".to_string(), |m| objstr::get_value(m));
|
||||
|
||||
let mut raw_modes = HashSet::new();
|
||||
|
||||
// Add some books.
|
||||
//add raw modes
|
||||
raw_modes.insert("a".to_string());
|
||||
raw_modes.insert("r".to_string());
|
||||
raw_modes.insert("x".to_string());
|
||||
@@ -303,7 +288,7 @@ pub fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
.filter(|a| raw_modes.contains(&a.to_string()))
|
||||
.collect();
|
||||
|
||||
if modes.len() == 0 || modes.len() > 1 {
|
||||
if modes.is_empty() || modes.len() > 1 {
|
||||
return Err(vm.new_value_error("Invalid Mode".to_string()));
|
||||
}
|
||||
|
||||
@@ -322,13 +307,13 @@ pub fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
vec![file.clone(), vm.ctx.new_str(modes[0].to_string())],
|
||||
vec![],
|
||||
);
|
||||
let file_io = vm.invoke(file_io_class, file_args).unwrap();
|
||||
let file_io = vm.invoke(file_io_class, file_args)?;
|
||||
|
||||
//Create Buffered class to consume FileIO. The type of buffered class depends on
|
||||
//the operation in the mode.
|
||||
//There are 3 possible classes here, each inheriting from the RawBaseIO
|
||||
// creating || writing || appending => BufferedWriter
|
||||
let buffered = if rust_mode.contains("w") {
|
||||
let buffered = if rust_mode.contains('w') {
|
||||
vm.invoke(
|
||||
buffered_writer_class,
|
||||
PyFuncArgs::new(vec![file_io.clone()], vec![]),
|
||||
@@ -342,7 +327,7 @@ pub fn io_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
//TODO: updating => PyBufferedRandom
|
||||
};
|
||||
|
||||
if rust_mode.contains("t") {
|
||||
if rust_mode.contains('t') {
|
||||
//If the mode is text this buffer type is consumed on construction of
|
||||
//a TextIOWrapper which is subsequently returned.
|
||||
vm.invoke(
|
||||
|
||||
@@ -11,7 +11,6 @@ use super::super::pyobject::{
|
||||
TypeProtocol,
|
||||
};
|
||||
use super::super::VirtualMachine;
|
||||
use num_bigint::ToBigInt;
|
||||
use num_traits::cast::ToPrimitive;
|
||||
|
||||
// We need to have a VM available to serialise a PyObject based on its subclass, so we implement
|
||||
@@ -52,12 +51,11 @@ impl<'s> serde::Serialize for PyObjectSerializer<'s> {
|
||||
} else if objtype::isinstance(self.pyobject, &self.ctx.int_type()) {
|
||||
let v = objint::get_value(self.pyobject);
|
||||
serializer.serialize_i64(v.to_i64().unwrap())
|
||||
// Allthough this may seem nice, it does not give the right result:
|
||||
// Although this may seem nice, it does not give the right result:
|
||||
// v.serialize(serializer)
|
||||
} else if objtype::isinstance(self.pyobject, &self.ctx.list_type()) {
|
||||
let elements = objsequence::get_elements(self.pyobject);
|
||||
serialize_seq_elements(serializer, &elements)
|
||||
} else if objtype::isinstance(self.pyobject, &self.ctx.tuple_type()) {
|
||||
} else if objtype::isinstance(self.pyobject, &self.ctx.list_type())
|
||||
|| objtype::isinstance(self.pyobject, &self.ctx.tuple_type())
|
||||
{
|
||||
let elements = objsequence::get_elements(self.pyobject);
|
||||
serialize_seq_elements(serializer, &elements)
|
||||
} else if objtype::isinstance(self.pyobject, &self.ctx.dict_type()) {
|
||||
@@ -119,7 +117,7 @@ impl<'de> serde::de::DeserializeSeed<'de> for PyObjectDeserializer<'de> {
|
||||
{
|
||||
// The JSON deserialiser always uses the i64/u64 deserialisers, so we only need to
|
||||
// implement those for now
|
||||
Ok(self.ctx.new_int(value.to_bigint().unwrap()))
|
||||
Ok(self.ctx.new_int(value))
|
||||
}
|
||||
|
||||
fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
|
||||
@@ -128,7 +126,7 @@ impl<'de> serde::de::DeserializeSeed<'de> for PyObjectDeserializer<'de> {
|
||||
{
|
||||
// The JSON deserialiser always uses the i64/u64 deserialisers, so we only need to
|
||||
// implement those for now
|
||||
Ok(self.ctx.new_int(value.to_bigint().unwrap()))
|
||||
Ok(self.ctx.new_int(value))
|
||||
}
|
||||
|
||||
fn visit_f64<E>(self, value: f64) -> Result<Self::Value, E>
|
||||
@@ -223,10 +221,8 @@ fn loads(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
.get_item("JSONDecodeError")
|
||||
.unwrap();
|
||||
let exc = vm.new_exception(json_decode_error, format!("{}", err));
|
||||
vm.ctx
|
||||
.set_item(&exc, "lineno", vm.ctx.new_int(err.line().into()));
|
||||
vm.ctx
|
||||
.set_item(&exc, "colno", vm.ctx.new_int(err.column().into()));
|
||||
vm.ctx.set_item(&exc, "lineno", vm.ctx.new_int(err.line()));
|
||||
vm.ctx.set_item(&exc, "colno", vm.ctx.new_int(err.column()));
|
||||
exc
|
||||
})
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@ use std;
|
||||
macro_rules! make_math_func {
|
||||
( $fname:ident, $fun:ident ) => {
|
||||
fn $fname(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(value, Some(vm.ctx.float_type()))]);
|
||||
let value = objfloat::get_value(value);
|
||||
arg_check!(vm, args, required = [(value, None)]);
|
||||
let value = objfloat::make_float(vm, value)?;
|
||||
let value = value.$fun();
|
||||
let value = vm.ctx.new_float(value);
|
||||
Ok(value)
|
||||
@@ -27,20 +27,20 @@ macro_rules! make_math_func {
|
||||
make_math_func!(math_fabs, abs);
|
||||
|
||||
fn math_isfinite(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(value, Some(vm.ctx.float_type()))]);
|
||||
let value = objfloat::get_value(value).is_finite();
|
||||
arg_check!(vm, args, required = [(value, None)]);
|
||||
let value = objfloat::make_float(vm, value)?.is_finite();
|
||||
Ok(vm.ctx.new_bool(value))
|
||||
}
|
||||
|
||||
fn math_isinf(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(value, Some(vm.ctx.float_type()))]);
|
||||
let value = objfloat::get_value(value).is_infinite();
|
||||
arg_check!(vm, args, required = [(value, None)]);
|
||||
let value = objfloat::make_float(vm, value)?.is_infinite();
|
||||
Ok(vm.ctx.new_bool(value))
|
||||
}
|
||||
|
||||
fn math_isnan(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(value, Some(vm.ctx.float_type()))]);
|
||||
let value = objfloat::get_value(value).is_nan();
|
||||
arg_check!(vm, args, required = [(value, None)]);
|
||||
let value = objfloat::make_float(vm, value)?.is_nan();
|
||||
Ok(vm.ctx.new_bool(value))
|
||||
}
|
||||
|
||||
@@ -49,25 +49,20 @@ make_math_func!(math_exp, exp);
|
||||
make_math_func!(math_expm1, exp_m1);
|
||||
|
||||
fn math_log(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [(x, Some(vm.ctx.float_type()))],
|
||||
optional = [(base, Some(vm.ctx.float_type()))]
|
||||
);
|
||||
let x = objfloat::get_value(x);
|
||||
arg_check!(vm, args, required = [(x, None)], optional = [(base, None)]);
|
||||
let x = objfloat::make_float(vm, x)?;
|
||||
match base {
|
||||
None => Ok(vm.ctx.new_float(x.ln())),
|
||||
Some(base) => {
|
||||
let base = objfloat::get_value(base);
|
||||
let base = objfloat::make_float(vm, base)?;
|
||||
Ok(vm.ctx.new_float(x.log(base)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn math_log1p(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(x, Some(vm.ctx.float_type()))]);
|
||||
let x = objfloat::get_value(x);
|
||||
arg_check!(vm, args, required = [(x, None)]);
|
||||
let x = objfloat::make_float(vm, x)?;
|
||||
Ok(vm.ctx.new_float((x + 1.0).ln()))
|
||||
}
|
||||
|
||||
@@ -75,16 +70,9 @@ make_math_func!(math_log2, log2);
|
||||
make_math_func!(math_log10, log10);
|
||||
|
||||
fn math_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [
|
||||
(x, Some(vm.ctx.float_type())),
|
||||
(y, Some(vm.ctx.float_type()))
|
||||
]
|
||||
);
|
||||
let x = objfloat::get_value(x);
|
||||
let y = objfloat::get_value(y);
|
||||
arg_check!(vm, args, required = [(x, None), (y, None)]);
|
||||
let x = objfloat::make_float(vm, x)?;
|
||||
let y = objfloat::make_float(vm, y)?;
|
||||
Ok(vm.ctx.new_float(x.powf(y)))
|
||||
}
|
||||
|
||||
@@ -96,32 +84,18 @@ make_math_func!(math_asin, asin);
|
||||
make_math_func!(math_atan, atan);
|
||||
|
||||
fn math_atan2(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [
|
||||
(y, Some(vm.ctx.float_type())),
|
||||
(x, Some(vm.ctx.float_type()))
|
||||
]
|
||||
);
|
||||
let y = objfloat::get_value(y);
|
||||
let x = objfloat::get_value(x);
|
||||
arg_check!(vm, args, required = [(y, None), (x, None)]);
|
||||
let y = objfloat::make_float(vm, y)?;
|
||||
let x = objfloat::make_float(vm, x)?;
|
||||
Ok(vm.ctx.new_float(y.atan2(x)))
|
||||
}
|
||||
|
||||
make_math_func!(math_cos, cos);
|
||||
|
||||
fn math_hypot(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
args,
|
||||
required = [
|
||||
(x, Some(vm.ctx.float_type())),
|
||||
(y, Some(vm.ctx.float_type()))
|
||||
]
|
||||
);
|
||||
let x = objfloat::get_value(x);
|
||||
let y = objfloat::get_value(y);
|
||||
arg_check!(vm, args, required = [(x, None), (y, None)]);
|
||||
let x = objfloat::make_float(vm, x)?;
|
||||
let y = objfloat::make_float(vm, y)?;
|
||||
Ok(vm.ctx.new_float(x.hypot(y)))
|
||||
}
|
||||
|
||||
@@ -129,14 +103,14 @@ make_math_func!(math_sin, sin);
|
||||
make_math_func!(math_tan, tan);
|
||||
|
||||
fn math_degrees(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(value, Some(vm.ctx.float_type()))]);
|
||||
let x = objfloat::get_value(value);
|
||||
arg_check!(vm, args, required = [(value, None)]);
|
||||
let x = objfloat::make_float(vm, value)?;
|
||||
Ok(vm.ctx.new_float(x * (180.0 / std::f64::consts::PI)))
|
||||
}
|
||||
|
||||
fn math_radians(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(value, Some(vm.ctx.float_type()))]);
|
||||
let x = objfloat::get_value(value);
|
||||
arg_check!(vm, args, required = [(value, None)]);
|
||||
let x = objfloat::make_float(vm, value)?;
|
||||
Ok(vm.ctx.new_float(x * (std::f64::consts::PI / 180.0)))
|
||||
}
|
||||
|
||||
@@ -150,8 +124,8 @@ make_math_func!(math_tanh, tanh);
|
||||
|
||||
// Special functions:
|
||||
fn math_erf(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(value, Some(vm.ctx.float_type()))]);
|
||||
let x = objfloat::get_value(value);
|
||||
arg_check!(vm, args, required = [(value, None)]);
|
||||
let x = objfloat::make_float(vm, value)?;
|
||||
|
||||
if x.is_nan() {
|
||||
Ok(vm.ctx.new_float(x))
|
||||
@@ -161,8 +135,8 @@ fn math_erf(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
}
|
||||
|
||||
fn math_erfc(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(value, Some(vm.ctx.float_type()))]);
|
||||
let x = objfloat::get_value(value);
|
||||
arg_check!(vm, args, required = [(value, None)]);
|
||||
let x = objfloat::make_float(vm, value)?;
|
||||
|
||||
if x.is_nan() {
|
||||
Ok(vm.ctx.new_float(x))
|
||||
@@ -172,32 +146,28 @@ fn math_erfc(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
}
|
||||
|
||||
fn math_gamma(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(value, Some(vm.ctx.float_type()))]);
|
||||
let x = objfloat::get_value(value);
|
||||
arg_check!(vm, args, required = [(value, None)]);
|
||||
let x = objfloat::make_float(vm, value)?;
|
||||
|
||||
if x.is_finite() {
|
||||
Ok(vm.ctx.new_float(gamma(x)))
|
||||
} else if x.is_nan() || x.is_sign_positive() {
|
||||
Ok(vm.ctx.new_float(x))
|
||||
} else {
|
||||
if x.is_nan() || x.is_sign_positive() {
|
||||
Ok(vm.ctx.new_float(x))
|
||||
} else {
|
||||
Ok(vm.ctx.new_float(std::f64::NAN))
|
||||
}
|
||||
Ok(vm.ctx.new_float(std::f64::NAN))
|
||||
}
|
||||
}
|
||||
|
||||
fn math_lgamma(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(value, Some(vm.ctx.float_type()))]);
|
||||
let x = objfloat::get_value(value);
|
||||
arg_check!(vm, args, required = [(value, None)]);
|
||||
let x = objfloat::make_float(vm, value)?;
|
||||
|
||||
if x.is_finite() {
|
||||
Ok(vm.ctx.new_float(ln_gamma(x)))
|
||||
} else if x.is_nan() {
|
||||
Ok(vm.ctx.new_float(x))
|
||||
} else {
|
||||
if x.is_nan() {
|
||||
Ok(vm.ctx.new_float(x))
|
||||
} else {
|
||||
Ok(vm.ctx.new_float(std::f64::INFINITY))
|
||||
}
|
||||
Ok(vm.ctx.new_float(std::f64::INFINITY))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,65 @@
|
||||
//library imports
|
||||
use std::fs::File;
|
||||
use std::fs::OpenOptions;
|
||||
use std::os::unix::io::IntoRawFd;
|
||||
use std::io::ErrorKind;
|
||||
|
||||
use num_bigint::ToBigInt;
|
||||
//3rd party imports
|
||||
use num_traits::cast::ToPrimitive;
|
||||
|
||||
//custom imports
|
||||
use super::super::obj::objint;
|
||||
use super::super::obj::objstr;
|
||||
use super::super::obj::objtype;
|
||||
|
||||
use super::super::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol};
|
||||
|
||||
use super::super::vm::VirtualMachine;
|
||||
|
||||
#[cfg(target_family = "unix")]
|
||||
pub fn raw_file_number(handle: File) -> i64 {
|
||||
use std::os::unix::io::IntoRawFd;
|
||||
|
||||
handle.into_raw_fd() as i64
|
||||
}
|
||||
|
||||
#[cfg(target_family = "unix")]
|
||||
pub fn rust_file(raw_fileno: i64) -> File {
|
||||
use std::os::unix::io::FromRawFd;
|
||||
|
||||
unsafe { File::from_raw_fd(raw_fileno as i32) }
|
||||
}
|
||||
|
||||
#[cfg(target_family = "windows")]
|
||||
pub fn raw_file_number(handle: File) -> i64 {
|
||||
use std::os::windows::io::IntoRawHandle;
|
||||
|
||||
handle.into_raw_handle() as i64
|
||||
}
|
||||
|
||||
#[cfg(target_family = "windows")]
|
||||
pub fn rust_file(raw_fileno: i64) -> File {
|
||||
use std::ffi::c_void;
|
||||
use std::os::windows::io::FromRawHandle;
|
||||
|
||||
//TODO: This is untested and (very) unsafe handling or
|
||||
//raw pointers - This should be patched urgently by
|
||||
//comparison to the cpython handling of the equivalent fileno
|
||||
//fields for windows
|
||||
unsafe { File::from_raw_handle(raw_fileno as *mut c_void) }
|
||||
}
|
||||
|
||||
pub fn os_close(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(fileno, Some(vm.ctx.int_type()))]);
|
||||
|
||||
let raw_fileno = objint::get_value(&fileno);
|
||||
|
||||
//The File type automatically closes when it goes out of scope.
|
||||
//To enable us to close these file descriptors (and hence prevent leaks)
|
||||
//we seek to create the relevant File and simply let it pass out of scope!
|
||||
rust_file(raw_fileno.to_i64().unwrap());
|
||||
|
||||
Ok(vm.get_none())
|
||||
}
|
||||
|
||||
pub fn os_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(
|
||||
vm,
|
||||
@@ -22,35 +70,39 @@ pub fn os_open(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
]
|
||||
);
|
||||
|
||||
let handle = match objint::get_value(mode).to_u16().unwrap() {
|
||||
0 => OpenOptions::new().read(true).open(objstr::get_value(&name)),
|
||||
1 => OpenOptions::new()
|
||||
.write(true)
|
||||
.open(objstr::get_value(&name)),
|
||||
512 => OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(objstr::get_value(&name)),
|
||||
_ => OpenOptions::new().read(true).open(objstr::get_value(&name)),
|
||||
};
|
||||
let fname = objstr::get_value(&name);
|
||||
|
||||
//raw_fd is supported on UNIX only. This will need to be extended
|
||||
//to support windows - i.e. raw file_handles
|
||||
if let Ok(f) = handle {
|
||||
Ok(vm.ctx.new_int(f.into_raw_fd().to_bigint().unwrap()))
|
||||
} else {
|
||||
Err(vm.new_value_error("Bad file descriptor".to_string()))
|
||||
let handle = match objint::get_value(mode).to_u16().unwrap() {
|
||||
0 => OpenOptions::new().read(true).open(&fname),
|
||||
1 => OpenOptions::new().write(true).open(&fname),
|
||||
512 => OpenOptions::new().write(true).create(true).open(&fname),
|
||||
_ => OpenOptions::new().read(true).open(&fname),
|
||||
}
|
||||
.map_err(|err| match err.kind() {
|
||||
ErrorKind::NotFound => {
|
||||
let exc_type = vm.ctx.exceptions.file_not_found_error.clone();
|
||||
vm.new_exception(exc_type, format!("No such file or directory: {}", &fname))
|
||||
}
|
||||
ErrorKind::PermissionDenied => {
|
||||
let exc_type = vm.ctx.exceptions.permission_error.clone();
|
||||
vm.new_exception(exc_type, format!("Permission denied: {}", &fname))
|
||||
}
|
||||
_ => vm.new_value_error("Unhandled file IO error".to_string()),
|
||||
})?;
|
||||
|
||||
Ok(vm.ctx.new_int(raw_file_number(handle)))
|
||||
}
|
||||
|
||||
pub fn mk_module(ctx: &PyContext) -> PyObjectRef {
|
||||
let py_mod = ctx.new_module(&"io".to_string(), ctx.new_scope(None));
|
||||
ctx.set_attr(&py_mod, "open", ctx.new_rustfunc(os_open));
|
||||
ctx.set_attr(&py_mod, "O_RDONLY", ctx.new_int(0.to_bigint().unwrap()));
|
||||
ctx.set_attr(&py_mod, "O_WRONLY", ctx.new_int(1.to_bigint().unwrap()));
|
||||
ctx.set_attr(&py_mod, "O_RDWR", ctx.new_int(2.to_bigint().unwrap()));
|
||||
ctx.set_attr(&py_mod, "O_NONBLOCK", ctx.new_int(4.to_bigint().unwrap()));
|
||||
ctx.set_attr(&py_mod, "O_APPEND", ctx.new_int(8.to_bigint().unwrap()));
|
||||
ctx.set_attr(&py_mod, "O_CREAT", ctx.new_int(512.to_bigint().unwrap()));
|
||||
ctx.set_attr(&py_mod, "close", ctx.new_rustfunc(os_close));
|
||||
|
||||
ctx.set_attr(&py_mod, "O_RDONLY", ctx.new_int(0));
|
||||
ctx.set_attr(&py_mod, "O_WRONLY", ctx.new_int(1));
|
||||
ctx.set_attr(&py_mod, "O_RDWR", ctx.new_int(2));
|
||||
ctx.set_attr(&py_mod, "O_NONBLOCK", ctx.new_int(4));
|
||||
ctx.set_attr(&py_mod, "O_APPEND", ctx.new_int(8));
|
||||
ctx.set_attr(&py_mod, "O_CREAT", ctx.new_int(512));
|
||||
py_mod
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ use self::byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
use super::super::obj::{objbool, objbytes, objfloat, objint, objstr, objtype};
|
||||
use super::super::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol};
|
||||
use super::super::VirtualMachine;
|
||||
use num_bigint::{BigInt, ToBigInt};
|
||||
use num_bigint::BigInt;
|
||||
use num_traits::ToPrimitive;
|
||||
use std::io::{Cursor, Read, Write};
|
||||
|
||||
@@ -76,7 +76,7 @@ fn pack_bool(
|
||||
data.write_u8(v).unwrap();
|
||||
Ok(())
|
||||
} else {
|
||||
Err(vm.new_type_error(format!("Expected boolean")))
|
||||
Err(vm.new_type_error("Expected boolean".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ fn pack_f32(
|
||||
data.write_f32::<LittleEndian>(v).unwrap();
|
||||
Ok(())
|
||||
} else {
|
||||
Err(vm.new_type_error(format!("Expected float")))
|
||||
Err(vm.new_type_error("Expected float".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,12 +164,12 @@ fn pack_f64(
|
||||
data.write_f64::<LittleEndian>(v).unwrap();
|
||||
Ok(())
|
||||
} else {
|
||||
Err(vm.new_type_error(format!("Expected float")))
|
||||
Err(vm.new_type_error("Expected float".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
fn struct_pack(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
if args.args.len() < 1 {
|
||||
if args.args.is_empty() {
|
||||
Err(vm.new_type_error(format!(
|
||||
"Expected at least 1 argument (got: {})",
|
||||
args.args.len()
|
||||
@@ -216,7 +216,7 @@ fn struct_pack(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
)))
|
||||
}
|
||||
} else {
|
||||
Err(vm.new_type_error(format!("First argument must be of str type")))
|
||||
Err(vm.new_type_error("First argument must be of str type".to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -224,14 +224,14 @@ fn struct_pack(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
fn unpack_i8(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult {
|
||||
match rdr.read_i8() {
|
||||
Err(err) => panic!("Error in reading {:?}", err),
|
||||
Ok(v) => Ok(vm.ctx.new_int(v.to_bigint().unwrap())),
|
||||
Ok(v) => Ok(vm.ctx.new_int(v)),
|
||||
}
|
||||
}
|
||||
|
||||
fn unpack_u8(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult {
|
||||
match rdr.read_u8() {
|
||||
Err(err) => panic!("Error in reading {:?}", err),
|
||||
Ok(v) => Ok(vm.ctx.new_int(v.to_bigint().unwrap())),
|
||||
Ok(v) => Ok(vm.ctx.new_int(v)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,42 +245,42 @@ fn unpack_bool(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult {
|
||||
fn unpack_i16(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult {
|
||||
match rdr.read_i16::<LittleEndian>() {
|
||||
Err(err) => panic!("Error in reading {:?}", err),
|
||||
Ok(v) => Ok(vm.ctx.new_int(v.to_bigint().unwrap())),
|
||||
Ok(v) => Ok(vm.ctx.new_int(v)),
|
||||
}
|
||||
}
|
||||
|
||||
fn unpack_u16(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult {
|
||||
match rdr.read_u16::<LittleEndian>() {
|
||||
Err(err) => panic!("Error in reading {:?}", err),
|
||||
Ok(v) => Ok(vm.ctx.new_int(v.to_bigint().unwrap())),
|
||||
Ok(v) => Ok(vm.ctx.new_int(v)),
|
||||
}
|
||||
}
|
||||
|
||||
fn unpack_i32(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult {
|
||||
match rdr.read_i32::<LittleEndian>() {
|
||||
Err(err) => panic!("Error in reading {:?}", err),
|
||||
Ok(v) => Ok(vm.ctx.new_int(v.to_bigint().unwrap())),
|
||||
Ok(v) => Ok(vm.ctx.new_int(v)),
|
||||
}
|
||||
}
|
||||
|
||||
fn unpack_u32(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult {
|
||||
match rdr.read_u32::<LittleEndian>() {
|
||||
Err(err) => panic!("Error in reading {:?}", err),
|
||||
Ok(v) => Ok(vm.ctx.new_int(v.to_bigint().unwrap())),
|
||||
Ok(v) => Ok(vm.ctx.new_int(v)),
|
||||
}
|
||||
}
|
||||
|
||||
fn unpack_i64(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult {
|
||||
match rdr.read_i64::<LittleEndian>() {
|
||||
Err(err) => panic!("Error in reading {:?}", err),
|
||||
Ok(v) => Ok(vm.ctx.new_int(v.to_bigint().unwrap())),
|
||||
Ok(v) => Ok(vm.ctx.new_int(v)),
|
||||
}
|
||||
}
|
||||
|
||||
fn unpack_u64(vm: &mut VirtualMachine, rdr: &mut Read) -> PyResult {
|
||||
match rdr.read_u64::<LittleEndian>() {
|
||||
Err(err) => panic!("Error in reading {:?}", err),
|
||||
Ok(v) => Ok(vm.ctx.new_int(v.to_bigint().unwrap())),
|
||||
Ok(v) => Ok(vm.ctx.new_int(v)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
*/
|
||||
|
||||
use super::super::obj::{objsequence, objstr, objtype};
|
||||
use super::super::pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol};
|
||||
use super::super::pyobject::{
|
||||
PyAttributes, PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol,
|
||||
};
|
||||
use super::super::VirtualMachine;
|
||||
|
||||
fn types_new_class(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
@@ -15,7 +17,6 @@ fn types_new_class(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
);
|
||||
|
||||
let name = objstr::get_value(name);
|
||||
let dict = vm.ctx.new_dict();
|
||||
|
||||
let bases = match bases {
|
||||
Some(b) => {
|
||||
@@ -28,7 +29,7 @@ fn types_new_class(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
None => vec![vm.ctx.object()],
|
||||
};
|
||||
|
||||
objtype::new(vm.ctx.type_type(), &name, bases, dict)
|
||||
objtype::new(vm.ctx.type_type(), &name, bases, PyAttributes::new())
|
||||
}
|
||||
|
||||
pub fn mk_module(ctx: &PyContext) -> PyObjectRef {
|
||||
|
||||
@@ -28,7 +28,7 @@ fn ref_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(cls, None), (referent, None)]);
|
||||
let referent = Rc::downgrade(referent);
|
||||
Ok(PyObject::new(
|
||||
PyObjectPayload::WeakRef { referent: referent },
|
||||
PyObjectPayload::WeakRef { referent },
|
||||
cls.clone(),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use num_bigint::ToBigInt;
|
||||
use obj::objtype;
|
||||
use pyobject::{PyContext, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol};
|
||||
use std::rc::Rc;
|
||||
@@ -26,14 +25,14 @@ fn getframe(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult {
|
||||
fn sys_getrefcount(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(object, None)]);
|
||||
let size = Rc::strong_count(&object);
|
||||
Ok(vm.ctx.new_int(size.to_bigint().unwrap()))
|
||||
Ok(vm.ctx.new_int(size))
|
||||
}
|
||||
|
||||
fn sys_getsizeof(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(object, None)]);
|
||||
// TODO: implement default optional argument.
|
||||
let size = mem::size_of_val(&object.borrow());
|
||||
Ok(vm.ctx.new_int(size.to_bigint().unwrap()))
|
||||
Ok(vm.ctx.new_int(size))
|
||||
}
|
||||
|
||||
pub fn mk_module(ctx: &PyContext) -> PyObjectRef {
|
||||
@@ -54,6 +53,75 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef {
|
||||
let modules = ctx.new_dict();
|
||||
|
||||
let sys_name = "sys";
|
||||
let sys_doc = "This module provides access to some objects used or maintained by the
|
||||
interpreter and to functions that interact strongly with the interpreter.
|
||||
|
||||
Dynamic objects:
|
||||
|
||||
argv -- command line arguments; argv[0] is the script pathname if known
|
||||
path -- module search path; path[0] is the script directory, else ''
|
||||
modules -- dictionary of loaded modules
|
||||
|
||||
displayhook -- called to show results in an interactive session
|
||||
excepthook -- called to handle any uncaught exception other than SystemExit
|
||||
To customize printing in an interactive session or to install a custom
|
||||
top-level exception handler, assign other functions to replace these.
|
||||
|
||||
stdin -- standard input file object; used by input()
|
||||
stdout -- standard output file object; used by print()
|
||||
stderr -- standard error object; used for error messages
|
||||
By assigning other file objects (or objects that behave like files)
|
||||
to these, it is possible to redirect all of the interpreter's I/O.
|
||||
|
||||
last_type -- type of last uncaught exception
|
||||
last_value -- value of last uncaught exception
|
||||
last_traceback -- traceback of last uncaught exception
|
||||
These three are only available in an interactive session after a
|
||||
traceback has been printed.
|
||||
|
||||
Static objects:
|
||||
|
||||
builtin_module_names -- tuple of module names built into this interpreter
|
||||
copyright -- copyright notice pertaining to this interpreter
|
||||
exec_prefix -- prefix used to find the machine-specific Python library
|
||||
executable -- absolute path of the executable binary of the Python interpreter
|
||||
float_info -- a struct sequence with information about the float implementation.
|
||||
float_repr_style -- string indicating the style of repr() output for floats
|
||||
hash_info -- a struct sequence with information about the hash algorithm.
|
||||
hexversion -- version information encoded as a single integer
|
||||
implementation -- Python implementation information.
|
||||
int_info -- a struct sequence with information about the int implementation.
|
||||
maxsize -- the largest supported length of containers.
|
||||
maxunicode -- the value of the largest Unicode code point
|
||||
platform -- platform identifier
|
||||
prefix -- prefix used to find the Python library
|
||||
thread_info -- a struct sequence with information about the thread implementation.
|
||||
version -- the version of this interpreter as a string
|
||||
version_info -- version information as a named tuple
|
||||
__stdin__ -- the original stdin; don't touch!
|
||||
__stdout__ -- the original stdout; don't touch!
|
||||
__stderr__ -- the original stderr; don't touch!
|
||||
__displayhook__ -- the original displayhook; don't touch!
|
||||
__excepthook__ -- the original excepthook; don't touch!
|
||||
|
||||
Functions:
|
||||
|
||||
displayhook() -- print an object to the screen, and save it in builtins._
|
||||
excepthook() -- print an exception and its traceback to sys.stderr
|
||||
exc_info() -- return thread-safe information about the current exception
|
||||
exit() -- exit the interpreter by raising SystemExit
|
||||
getdlopenflags() -- returns flags to be used for dlopen() calls
|
||||
getprofile() -- get the global profiling function
|
||||
getrefcount() -- return the reference count for an object (plus one :-)
|
||||
getrecursionlimit() -- return the max recursion depth for the interpreter
|
||||
getsizeof() -- return the size of an object in bytes
|
||||
gettrace() -- get the global debug tracing function
|
||||
setcheckinterval() -- control how often the interpreter checks for events
|
||||
setdlopenflags() -- set the flags to be used for dlopen() calls
|
||||
setprofile() -- set the global profiling function
|
||||
setrecursionlimit() -- set the max recursion depth for the interpreter
|
||||
settrace() -- set the global debug tracing function
|
||||
";
|
||||
let sys_mod = ctx.new_module(&sys_name, ctx.new_scope(None));
|
||||
|
||||
ctx.set_item(&modules, sys_name, sys_mod.clone());
|
||||
@@ -62,14 +130,11 @@ pub fn mk_module(ctx: &PyContext) -> PyObjectRef {
|
||||
ctx.set_item(&sys_mod, "argv", argv(ctx));
|
||||
ctx.set_item(&sys_mod, "getrefcount", ctx.new_rustfunc(sys_getrefcount));
|
||||
ctx.set_item(&sys_mod, "getsizeof", ctx.new_rustfunc(sys_getsizeof));
|
||||
ctx.set_item(
|
||||
&sys_mod,
|
||||
"maxsize",
|
||||
ctx.new_int(std::usize::MAX.to_bigint().unwrap()),
|
||||
);
|
||||
ctx.set_item(&sys_mod, "maxsize", ctx.new_int(std::usize::MAX));
|
||||
ctx.set_item(&sys_mod, "path", path);
|
||||
ctx.set_item(&sys_mod, "ps1", ctx.new_str(">>>>> ".to_string()));
|
||||
ctx.set_item(&sys_mod, "ps2", ctx.new_str("..... ".to_string()));
|
||||
ctx.set_item(&sys_mod, "__doc__", ctx.new_str(sys_doc.to_string()));
|
||||
ctx.set_item(&sys_mod, "_getframe", ctx.new_rustfunc(getframe));
|
||||
|
||||
sys_mod
|
||||
|
||||
66
vm/src/vm.rs
66
vm/src/vm.rs
@@ -11,7 +11,7 @@ use std::collections::hash_map::HashMap;
|
||||
use super::builtins;
|
||||
use super::bytecode;
|
||||
use super::frame::Frame;
|
||||
use super::obj::objcode::copy_code;
|
||||
use super::obj::objcode;
|
||||
use super::obj::objgenerator;
|
||||
use super::obj::objiter;
|
||||
use super::obj::objsequence;
|
||||
@@ -53,10 +53,10 @@ impl VirtualMachine {
|
||||
|
||||
let stdlib_inits = stdlib::get_module_inits();
|
||||
VirtualMachine {
|
||||
builtins: builtins,
|
||||
builtins,
|
||||
sys_module: sysmod,
|
||||
stdlib_inits,
|
||||
ctx: ctx,
|
||||
ctx,
|
||||
current_frame: None,
|
||||
}
|
||||
}
|
||||
@@ -86,13 +86,12 @@ impl VirtualMachine {
|
||||
let pymsg = self.new_str(msg);
|
||||
let args: Vec<PyObjectRef> = vec![pymsg];
|
||||
let args = PyFuncArgs {
|
||||
args: args,
|
||||
args,
|
||||
kwargs: vec![],
|
||||
};
|
||||
|
||||
// Call function:
|
||||
let exception = self.invoke(exc_type, args).unwrap();
|
||||
exception
|
||||
self.invoke(exc_type, args).unwrap()
|
||||
}
|
||||
|
||||
pub fn new_type_error(&mut self, msg: String) -> PyObjectRef {
|
||||
@@ -100,6 +99,16 @@ impl VirtualMachine {
|
||||
self.new_exception(type_error, msg)
|
||||
}
|
||||
|
||||
pub fn new_os_error(&mut self, msg: String) -> PyObjectRef {
|
||||
let os_error = self.ctx.exceptions.os_error.clone();
|
||||
self.new_exception(os_error, msg)
|
||||
}
|
||||
|
||||
pub fn new_overflow_error(&mut self, msg: String) -> PyObjectRef {
|
||||
let overflow_error = self.ctx.exceptions.overflow_error.clone();
|
||||
self.new_exception(overflow_error, msg)
|
||||
}
|
||||
|
||||
/// Create a new python ValueError object. Useful for raising errors from
|
||||
/// python functions implemented in rust.
|
||||
pub fn new_value_error(&mut self, msg: String) -> PyObjectRef {
|
||||
@@ -118,8 +127,13 @@ impl VirtualMachine {
|
||||
}
|
||||
|
||||
pub fn new_not_implemented_error(&mut self, msg: String) -> PyObjectRef {
|
||||
let value_error = self.ctx.exceptions.not_implemented_error.clone();
|
||||
self.new_exception(value_error, msg)
|
||||
let not_implemented_error = self.ctx.exceptions.not_implemented_error.clone();
|
||||
self.new_exception(not_implemented_error, msg)
|
||||
}
|
||||
|
||||
pub fn new_zero_division_error(&mut self, msg: String) -> PyObjectRef {
|
||||
let zero_division_error = self.ctx.exceptions.zero_division_error.clone();
|
||||
self.new_exception(zero_division_error, msg)
|
||||
}
|
||||
|
||||
pub fn new_scope(&mut self, parent_scope: Option<PyObjectRef>) -> PyObjectRef {
|
||||
@@ -159,7 +173,7 @@ impl VirtualMachine {
|
||||
pub fn get_builtin_scope(&mut self) -> PyObjectRef {
|
||||
let a2 = &*self.builtins.borrow();
|
||||
match a2.payload {
|
||||
PyObjectPayload::Module { name: _, ref dict } => dict.clone(),
|
||||
PyObjectPayload::Module { ref dict, .. } => dict.clone(),
|
||||
_ => {
|
||||
panic!("OMG");
|
||||
}
|
||||
@@ -206,7 +220,7 @@ impl VirtualMachine {
|
||||
obj,
|
||||
method_name,
|
||||
PyFuncArgs {
|
||||
args: args,
|
||||
args,
|
||||
kwargs: vec![],
|
||||
},
|
||||
)
|
||||
@@ -245,11 +259,7 @@ impl VirtualMachine {
|
||||
ref scope,
|
||||
ref defaults,
|
||||
} => self.invoke_python_function(code, scope, defaults, args),
|
||||
PyObjectPayload::Class {
|
||||
name: _,
|
||||
dict: _,
|
||||
mro: _,
|
||||
} => self.call_method_pyargs(&func_ref, "__call__", args),
|
||||
PyObjectPayload::Class { .. } => self.call_method_pyargs(&func_ref, "__call__", args),
|
||||
PyObjectPayload::BoundMethod {
|
||||
ref function,
|
||||
ref object,
|
||||
@@ -272,7 +282,7 @@ impl VirtualMachine {
|
||||
defaults: &PyObjectRef,
|
||||
args: PyFuncArgs,
|
||||
) -> PyResult {
|
||||
let code_object = copy_code(code);
|
||||
let code_object = objcode::get_value(code);
|
||||
let scope = self.ctx.new_scope(Some(scope.clone()));
|
||||
self.fill_scope_from_args(&code_object, &scope, args, defaults)?;
|
||||
|
||||
@@ -439,9 +449,9 @@ impl VirtualMachine {
|
||||
value: &PyObjectRef,
|
||||
) -> Result<Vec<PyObjectRef>, PyObjectRef> {
|
||||
// Extract elements from item, if possible:
|
||||
let elements = if objtype::isinstance(value, &self.ctx.tuple_type()) {
|
||||
objsequence::get_elements(value).to_vec()
|
||||
} else if objtype::isinstance(value, &self.ctx.list_type()) {
|
||||
let elements = if objtype::isinstance(value, &self.ctx.tuple_type())
|
||||
|| objtype::isinstance(value, &self.ctx.list_type())
|
||||
{
|
||||
objsequence::get_elements(value).to_vec()
|
||||
} else {
|
||||
let iter = objiter::get_iter(self, value)?;
|
||||
@@ -549,27 +559,27 @@ impl VirtualMachine {
|
||||
}
|
||||
|
||||
pub fn _add(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult {
|
||||
self.call_method(&a, "__add__", vec![b])
|
||||
self.call_or_unsupported(a, b, "__add__", "__radd__", "+")
|
||||
}
|
||||
|
||||
pub fn _mul(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult {
|
||||
self.call_method(&a, "__mul__", vec![b])
|
||||
self.call_or_unsupported(a, b, "__mul__", "__rmul__", "*")
|
||||
}
|
||||
|
||||
pub fn _div(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult {
|
||||
self.call_method(&a, "__truediv__", vec![b])
|
||||
self.call_or_unsupported(a, b, "__truediv__", "__rtruediv__", "/")
|
||||
}
|
||||
|
||||
pub fn _pow(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult {
|
||||
self.call_method(&a, "__pow__", vec![b])
|
||||
self.call_or_unsupported(a, b, "__pow__", "__rpow__", "**")
|
||||
}
|
||||
|
||||
pub fn _modulo(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult {
|
||||
self.call_method(&a, "__mod__", vec![b])
|
||||
self.call_or_unsupported(a, b, "__mod__", "__rmod__", "%")
|
||||
}
|
||||
|
||||
pub fn _xor(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult {
|
||||
self.call_method(&a, "__xor__", vec![b])
|
||||
self.call_or_unsupported(a, b, "__xor__", "__rxor__", "^")
|
||||
}
|
||||
|
||||
pub fn _or(&mut self, a: PyObjectRef, b: PyObjectRef) -> PyResult {
|
||||
@@ -614,8 +624,8 @@ mod tests {
|
||||
#[test]
|
||||
fn test_add_py_integers() {
|
||||
let mut vm = VirtualMachine::new();
|
||||
let a = vm.ctx.new_int(33_i32.to_bigint().unwrap());
|
||||
let b = vm.ctx.new_int(12_i32.to_bigint().unwrap());
|
||||
let a = vm.ctx.new_int(33_i32);
|
||||
let b = vm.ctx.new_int(12_i32);
|
||||
let res = vm._add(a, b).unwrap();
|
||||
let value = objint::get_value(&res);
|
||||
assert_eq!(value, 45_i32.to_bigint().unwrap());
|
||||
@@ -625,7 +635,7 @@ mod tests {
|
||||
fn test_multiply_str() {
|
||||
let mut vm = VirtualMachine::new();
|
||||
let a = vm.ctx.new_str(String::from("Hello "));
|
||||
let b = vm.ctx.new_int(4_i32.to_bigint().unwrap());
|
||||
let b = vm.ctx.new_int(4_i32);
|
||||
let res = vm._mul(a, b).unwrap();
|
||||
let value = objstr::get_value(&res);
|
||||
assert_eq!(value, String::from("Hello Hello Hello Hello "))
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<body>
|
||||
<h1>RustPython Demo</h1>
|
||||
<p>
|
||||
RustPython is a Python interpreter writter in Rust. This demo is
|
||||
RustPython is a Python interpreter written in Rust. This demo is
|
||||
compiled from Rust to WebAssembly so it runs in the browser.<br>
|
||||
Please input your Python code below and click <kbd>Run</kbd>, or you
|
||||
can open up your browser's devtools and play with
|
||||
@@ -44,7 +44,7 @@ while count < until:
|
||||
<ul>
|
||||
<li>
|
||||
<code>stdout</code>: either a string with a css selector
|
||||
to a textarea element or a function that recieves a
|
||||
to a textarea element or a function that receives a
|
||||
string when the <code>print</code> function is called in
|
||||
python. The default value is <code>console.log</code>.
|
||||
</li>
|
||||
@@ -57,7 +57,7 @@ while count < until:
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
JS functions that get passed to python will recieve positional
|
||||
JS functions that get passed to python will receive positional
|
||||
args as positional args and kwargs as the
|
||||
<code>this</code> argument
|
||||
</li>
|
||||
|
||||
@@ -28,7 +28,7 @@ pyEval(code, options?);
|
||||
|
||||
- `vars?`: `{ [key: string]: any }`: Variables passed to the VM that can be
|
||||
accessed in Python with the variable `js_vars`. Functions do work, and
|
||||
recieve the Python kwargs as the `this` argument.
|
||||
receive the Python kwargs as the `this` argument.
|
||||
- `stdout?`: `(out: string) => void`: A function to replace the native print
|
||||
function, by default `console.log`.
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ use rustpython_vm::pyobject::{self, PyFuncArgs, PyObjectRef, PyResult};
|
||||
use rustpython_vm::VirtualMachine;
|
||||
use wasm_bindgen::{prelude::*, JsCast};
|
||||
|
||||
// Hack to comment out wasm-bindgen's typescript definitons
|
||||
// Hack to comment out wasm-bindgen's typescript definitions
|
||||
#[wasm_bindgen(typescript_custom_section)]
|
||||
const TS_CMT_START: &'static str = "/*";
|
||||
|
||||
@@ -123,7 +123,7 @@ fn eval(vm: &mut VirtualMachine, source: &str, vars: PyObjectRef) -> PyResult {
|
||||
source.push('\n');
|
||||
}
|
||||
|
||||
let code_obj = compile::compile(vm, &source, compile::Mode::Exec, None)?;
|
||||
let code_obj = compile::compile(vm, &source, &compile::Mode::Exec, "<string>".to_string())?;
|
||||
|
||||
vm.run_code_obj(code_obj, vars)
|
||||
}
|
||||
@@ -141,7 +141,7 @@ fn eval(vm: &mut VirtualMachine, source: &str, vars: PyObjectRef) -> PyResult {
|
||||
///
|
||||
/// - `vars?`: `{ [key: string]: any }`: Variables passed to the VM that can be
|
||||
/// accessed in Python with the variable `js_vars`. Functions do work, and
|
||||
/// recieve the Python kwargs as the `this` argument.
|
||||
/// receive the Python kwargs as the `this` argument.
|
||||
/// - `stdout?`: `(out: string) => void`: A function to replace the native print
|
||||
/// function, by default `console.log`.
|
||||
pub fn eval_py(source: &str, options: Option<Object>) -> Result<JsValue, JsValue> {
|
||||
|
||||
Reference in New Issue
Block a user