Merge branch 'master' into sets-using-hashes

This commit is contained in:
Matthew Constable
2019-02-09 21:04:33 +00:00
94 changed files with 4219 additions and 1416 deletions

2
.gitignore vendored
View File

@@ -5,4 +5,6 @@ __pycache__
**/*.pytest_cache
.*sw*
.repl_history.txt
.vscode
wasm-pack.log
.idea/

11
Cargo.lock generated
View File

@@ -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"

View File

@@ -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"

View File

@@ -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:

View File

@@ -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

View File

@@ -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 {

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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)
},
}
},
};

View File

@@ -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);
}

View File

@@ -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())
}

View File

@@ -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

View File

@@ -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)

View File

@@ -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"

View 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)

View 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]

View 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]

View File

@@ -0,0 +1,8 @@
fd = open('README.md')
assert 'RustPython' in fd.read()
try:
open('DoesNotExist')
assert False
except FileNotFoundError:
pass

View 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]

View File

@@ -0,0 +1 @@
assert list(reversed(range(5))) == [4, 3, 2, 1, 0]

View 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

View 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)

View File

@@ -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')

View 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
View 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

View 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'

View File

@@ -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

View File

@@ -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

View 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)

View File

@@ -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"

View File

@@ -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())

View File

@@ -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

View File

@@ -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
View 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])

View File

@@ -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"

View 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)

View File

@@ -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

View 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"

View File

@@ -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])

View File

@@ -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"

View File

@@ -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
}

View File

@@ -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,
}
}

View File

@@ -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
View 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());
}
}

View File

@@ -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!(

View File

@@ -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__",

View File

@@ -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)

View File

@@ -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()

View File

@@ -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)),
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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),
})

View File

@@ -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!(

View File

@@ -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)
}

View File

@@ -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))
}

View File

@@ -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))
}

View File

@@ -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,

View 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
View 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));
}

View File

@@ -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));
}

View File

@@ -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))
}

View File

@@ -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)
}

View File

@@ -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)

View File

@@ -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));
}

View File

@@ -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));
}

View File

@@ -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
View 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()));
}

View File

@@ -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__",

View File

@@ -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),
))
}
}

View File

@@ -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
View 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!()
}
}

View File

@@ -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 {

View File

@@ -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
View 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));
}

View File

@@ -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
}

View File

@@ -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?

View File

@@ -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));
}

View File

@@ -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
View 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));
}

View File

@@ -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>"),
}
}

View File

@@ -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

View File

@@ -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(

View File

@@ -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
})
}

View File

@@ -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))
}
}

View File

@@ -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
}

View File

@@ -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)),
}
}

View File

@@ -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 {

View File

@@ -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(),
))
}

View File

@@ -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

View File

@@ -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 "))

View File

@@ -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>

View File

@@ -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`.

View File

@@ -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> {