Merge branch 'master' into map_doc

This commit is contained in:
Windel Bouwman
2019-02-09 18:58:37 +01:00
committed by GitHub
52 changed files with 1595 additions and 338 deletions

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

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;

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,7 +68,7 @@ fn main() {
handle_exception(&mut vm, result);
}
fn _run_string(vm: &mut VirtualMachine, source: &str, source_path: Option<String>) -> PyResult {
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();
@@ -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) => {
@@ -177,7 +177,7 @@ fn run_shell(vm: &mut VirtualMachine) -> PyResult {
}
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(),

View File

@@ -40,8 +40,6 @@ assert (True and True)
assert not (False and fake)
assert (True and 5) == 5
assert bool.__doc__ == "bool(x) -> bool\n\nReturns True when the argument x is true, False otherwise.\nThe builtins True and False are the only two instances of the class bool.\nThe class bool is a subclass of the class int, and cannot be subclassed."
# Bools are also ints.
assert isinstance(True, int)
assert True + True == 2

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,8 @@
fd = open('README.md')
assert 'RustPython' in fd.read()
try:
open('DoesNotExist')
assert False
except FileNotFoundError:
pass

View File

@@ -18,6 +18,11 @@ def assert_raises(expr, exc_type):
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

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

@@ -5,12 +5,8 @@ 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 3 == eval('1+2')
code = compile('5+3', 'x.py', 'eval')

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

@@ -2,8 +2,6 @@ x = 5
x.__init__(6)
assert x == 5
assert int.__doc__ == "int(x=0) -> integer\nint(x, base=10) -> integer\n\nConvert a number or string to an integer, or return 0 if no arguments\nare given. If x is a number, return x.__int__(). For floating point\nnumbers, this truncates towards zero.\n\nIf x is not a number or if base is given, then x must be a string,\nbytes, or bytearray instance representing an integer literal in the\ngiven base. The literal can be preceded by '+' or '-' and be surrounded\nby whitespace. The base defaults to 10. Valid bases are 0 and 2-36.\nBase 0 means to interpret the base from the string as an integer literal.\n>>> int('0b100', base=0)\n4"
class A(int):
pass
@@ -13,6 +11,23 @@ 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

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

@@ -19,5 +19,3 @@ assert x > y, "tuple __gt__ failed"
b = (1,2,3)
assert b.index(2) == 1
assert b.__doc__ == "tuple() -> empty tuple\ntuple(iterable) -> tuple initialized from iterable's items\n\nIf the argument is a tuple, the return value is the same object."

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();
@@ -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,6 @@ 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 {
@@ -224,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()));
};
@@ -270,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 {
@@ -612,7 +588,24 @@ fn builtin_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
vm.to_repr(obj)
}
// 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!(
@@ -641,32 +634,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 {
@@ -692,7 +659,7 @@ 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());
@@ -725,15 +692,17 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef {
ctx.set_attr(&py_mod, "property", ctx.property_type());
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, "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(
@@ -742,6 +711,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",
@@ -753,6 +727,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",
@@ -767,11 +746,21 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef {
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,7 +34,8 @@ 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 {
@@ -45,6 +47,7 @@ impl CodeObject {
kwonlyarg_names,
varkeywords,
source_path,
first_line_number,
obj_name,
is_generator: false,
}

View File

@@ -24,10 +24,10 @@ pub fn compile(
vm: &mut VirtualMachine,
source: &str,
mode: &Mode,
source_path: Option<String>,
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 {
@@ -78,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,
));
}
@@ -453,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 {
@@ -653,12 +657,14 @@ 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(),
self.source_path.clone().unwrap(),
line_number,
name.to_string(),
));
@@ -1162,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(),
));
@@ -1338,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;
}
@@ -1352,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,6 +81,7 @@ fn exception_str(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
#[derive(Debug)]
pub struct ExceptionZoo {
pub arithmetic_error: PyObjectRef,
pub assertion_error: PyObjectRef,
pub attribute_error: PyObjectRef,
pub base_exception_type: PyObjectRef,
@@ -93,12 +94,14 @@ pub struct ExceptionZoo {
pub name_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 zero_division_error: PyObjectRef,
}
impl ExceptionZoo {
@@ -113,6 +116,8 @@ impl ExceptionZoo {
let exception_type = create_type("Exception", &type_type, &base_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 =
@@ -128,8 +133,18 @@ impl ExceptionZoo {
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,
@@ -142,6 +157,7 @@ impl ExceptionZoo {
let permission_error = create_type("PermissionError", &type_type, &os_error, &dict_type);
ExceptionZoo {
arithmetic_error,
assertion_error,
attribute_error,
base_exception_type,
@@ -154,12 +170,14 @@ impl ExceptionZoo {
name_error,
not_implemented_error,
os_error,
overflow_error,
permission_error,
runtime_error,
stop_iteration,
syntax_error,
type_error,
value_error,
zero_division_error,
}
}
}

View File

@@ -20,7 +20,7 @@ use super::pyobject::{
PyResult, TypeProtocol,
};
use super::vm::VirtualMachine;
use num_traits::ToPrimitive;
use num_bigint::BigInt;
#[derive(Clone, Debug)]
enum Block {
@@ -70,7 +70,7 @@ 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
@@ -88,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())));
@@ -262,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)
@@ -503,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);
@@ -661,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)?;
@@ -678,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

View File

@@ -35,7 +35,7 @@ fn import_uncached_module(
vm,
&source,
&compile::Mode::Exec,
Some(filepath.to_str().unwrap().to_string()),
filepath.to_str().unwrap().to_string(),
)?;
// trace!("Code object: {:?}", code_obj);

View File

@@ -6,6 +6,7 @@ pub mod objbytes;
pub mod objcode;
pub mod objcomplex;
pub mod objdict;
pub mod objenumerate;
pub mod objfilter;
pub mod objfloat;
pub mod objframe;
@@ -21,7 +22,9 @@ 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

@@ -16,6 +16,20 @@ use num_traits::ToPrimitive;
/// Fill bytearray class methods dictionary.
pub fn init(context: &PyContext) {
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__",
@@ -36,6 +50,11 @@ 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",
@@ -203,11 +222,12 @@ fn bytearray_istitle(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
}
};
if is_cased(current) && next.is_uppercase() && !prev_cased {
return Ok(vm.new_bool(false));
} else if !is_cased(current) && next.is_lowercase() {
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);
}

View File

@@ -4,7 +4,7 @@
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;
@@ -13,15 +13,26 @@ pub fn init(context: &PyContext) {
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)
}
}
@@ -33,18 +44,54 @@ fn code_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
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 = ", line 1".to_string();
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

@@ -8,8 +8,18 @@ use num_complex::Complex64;
pub fn init(context: &PyContext) {
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__",

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

View File

@@ -7,7 +7,7 @@ use super::objbool;
use super::objiter;
use super::objtype; // Required for arg_check! to use isinstance
pub fn filter_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
fn filter_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
args,
@@ -23,21 +23,6 @@ pub fn filter_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
))
}
fn filter_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(filter, Some(vm.ctx.filter_type()))]);
// Return self:
Ok(filter.clone())
}
fn filter_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
args,
required = [(filter, Some(vm.ctx.filter_type())), (needle, None)]
);
objiter::contains(vm, filter, needle)
}
fn filter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(filter, Some(vm.ctx.filter_type()))]);
@@ -72,12 +57,7 @@ fn filter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
pub fn init(context: &PyContext) {
let filter_type = &context.filter_type;
context.set_attr(
&filter_type,
"__contains__",
context.new_rustfunc(filter_contains),
);
context.set_attr(&filter_type, "__iter__", context.new_rustfunc(filter_iter));
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,6 +282,53 @@ 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 float_type = &context.float_type;
@@ -299,4 +362,10 @@ pub fn init(context: &PyContext) {
"__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

@@ -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
@@ -289,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 {}",
@@ -299,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,
@@ -366,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))
}
}
@@ -388,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())))
}
@@ -433,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 {}",
@@ -549,6 +594,12 @@ Base 0 means to interpret the base from the string as an integer literal.
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__",

View File

@@ -9,7 +9,6 @@ use super::super::vm::VirtualMachine;
use super::objbool;
// use super::objstr;
use super::objtype; // Required for arg_check! to use isinstance
use num_bigint::BigInt;
/*
* This helper function is called at multiple places. First, it is called
@@ -65,7 +64,17 @@ pub fn get_all(
Ok(elements)
}
pub fn contains(vm: &mut VirtualMachine, iter: &PyObjectRef, needle: &PyObjectRef) -> PyResult {
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()])?;
@@ -80,6 +89,34 @@ pub fn contains(vm: &mut VirtualMachine, iter: &PyObjectRef, needle: &PyObjectRe
}
}
/// 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)]);
@@ -87,21 +124,6 @@ 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)]
);
contains(vm, iter, needle)
}
fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(iter, Some(vm.ctx.iter_type()))]);
@@ -118,22 +140,16 @@ 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(BigInt::from(*position)) {
if let Some(int) = range.get(*position) {
*position += 1;
Ok(vm.ctx.new_int(int))
} 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))
}
}
@@ -143,10 +159,7 @@ 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))
}
}
@@ -161,12 +174,7 @@ fn iter_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
pub fn init(context: &PyContext) {
let 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));
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

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

View File

@@ -5,7 +5,7 @@ use super::super::vm::VirtualMachine;
use super::objiter;
use super::objtype; // Required for arg_check! to use isinstance
pub fn map_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
fn map_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
no_kwargs!(vm, args);
let cls = &args.args[0];
if args.args.len() < 3 {
@@ -27,21 +27,6 @@ pub fn map_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
}
}
fn map_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(map, Some(vm.ctx.map_type()))]);
// Return self:
Ok(map.clone())
}
fn map_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
args,
required = [(map, Some(vm.ctx.map_type())), (needle, None)]
);
objiter::contains(vm, map, needle)
}
fn map_next(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(map, Some(vm.ctx.map_type()))]);
@@ -75,12 +60,7 @@ pub fn init(context: &PyContext) {
Make an iterator that computes the function using arguments from\n\
each of the iterables. Stops when the shortest iterable is exhausted.";
context.set_attr(
&map_type,
"__contains__",
context.new_rustfunc(map_contains),
);
context.set_attr(&map_type, "__iter__", context.new_rustfunc(map_iter));
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

@@ -8,6 +8,35 @@ use super::objtype;
pub fn init(context: &PyContext) {
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__",
@@ -18,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__ ?
}

View File

@@ -7,6 +7,7 @@ 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 {
@@ -18,12 +19,24 @@ pub struct RangeType {
}
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.end.clone() - self.start.clone()) / self.step.clone())
.abs()
.to_usize()
.unwrap()
self.try_len().unwrap()
}
#[inline]
@@ -65,29 +78,56 @@ impl RangeType {
}
#[inline]
pub fn get(&self, index: BigInt) -> Option<BigInt> {
let result = self.start.clone() + self.step.clone() * index;
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 {
Some(result)
} else if !self.forward() && !self.is_empty() && result > self.end {
if (self.forward() && !self.is_empty() && result < self.end)
|| (!self.forward() && !self.is_empty() && result > self.end)
{
Some(result)
} else {
None
}
}
#[inline]
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,
"__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,
@@ -153,12 +193,14 @@ fn range_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
fn range_len(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(),
if let Some(len) = match zelf.borrow().payload {
PyObjectPayload::Range { ref range } => range.try_len(),
_ => unreachable!(),
};
Ok(vm.ctx.new_int(len))
} {
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 {
@@ -175,15 +217,19 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
match subscript.borrow().payload {
PyObjectPayload::Integer { ref value } => {
if let Some(int) = zrange.get(value.clone()) {
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 { start, stop, step } => {
PyObjectPayload::Slice {
ref start,
ref stop,
ref step,
} => {
let new_start = if let Some(int) = start {
if let Some(i) = zrange.get(int.into()) {
if let Some(i) = zrange.get(int) {
i
} else {
zrange.start.clone()
@@ -193,7 +239,7 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
};
let new_end = if let Some(int) = stop {
if let Some(i) = zrange.get(int.into()) {
if let Some(i) = zrange.get(int) {
i
} else {
zrange.end
@@ -203,7 +249,7 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
};
let new_step = if let Some(int) = step {
(int as i64) * zrange.step
int * zrange.step
} else {
zrange.step
};
@@ -224,6 +270,17 @@ fn range_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
}
}
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()))]);

View File

@@ -2,53 +2,97 @@ use super::super::pyobject::{PyObject, PyObjectPayload, PyObjectRef, PyResult, T
use super::super::vm::VirtualMachine;
use super::objbool;
use super::objint;
use num_traits::ToPrimitive;
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 {
if -p as usize > self.len() {
// return something that is out of bounds so `get_item` raises an IndexError
self.len() + 1
None
} else {
self.len() - ((-p) as usize)
Some(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()
} 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())
}
}
}
@@ -58,12 +102,28 @@ pub trait PySliceableSequence {
}
impl<T: Clone> PySliceableSequence for Vec<T> {
fn do_slice(&self, start: usize, stop: usize) -> Self {
self[start..stop].to_vec()
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()
}
@@ -78,8 +138,7 @@ pub fn get_item(
match &(subscript.borrow()).payload {
PyObjectPayload::Integer { value } => match value.to_i32() {
Some(value) => {
let pos_index = elements.to_vec().get_pos(value);
if pos_index < elements.len() {
if let Some(pos_index) = elements.to_vec().get_pos(value) {
let obj = elements[pos_index].clone();
Ok(obj)
} else {
@@ -94,7 +153,7 @@ pub fn get_item(
PyObjectPayload::Slice { .. } => Ok(PyObject::new(
match &(sequence.borrow()).payload {
PyObjectPayload::Sequence { .. } => PyObjectPayload::Sequence {
elements: elements.to_vec().get_slice_items(&subscript),
elements: elements.to_vec().get_slice_items(vm, &subscript)?,
},
ref payload => panic!("sequence get_item called for non-sequence: {:?}", payload),
},

View File

@@ -126,6 +126,99 @@ 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()))]);
@@ -159,6 +252,13 @@ 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));

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

@@ -8,6 +8,7 @@ use super::objsequence::PySliceableSequence;
use super::objtype;
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;
@@ -493,7 +494,7 @@ fn str_islower(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
))
}
// 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)
@@ -1003,14 +1004,23 @@ fn str_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
}
impl PySliceableSequence for String {
fn do_slice(&self, start: usize, stop: usize) -> Self {
fn do_slice(&self, range: Range<usize>) -> Self {
to_graphemes(self)
.get(start..stop)
.get(range)
.map_or(String::default(), |c| c.join(""))
}
fn do_stepped_slice(&self, start: usize, stop: usize, step: usize) -> Self {
if let Some(s) = to_graphemes(self).get(start..stop) {
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()
@@ -1021,6 +1031,23 @@ impl PySliceableSequence for String {
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 {
to_graphemes(self).len()
}
@@ -1039,11 +1066,11 @@ pub fn subscript(vm: &mut VirtualMachine, value: &str, b: PyObjectRef) -> PyResu
match objint::get_value(&b).to_i32() {
Some(pos) => {
let graphemes = to_graphemes(value);
let idx = graphemes.get_pos(pos);
graphemes
.get(idx)
.map(|c| vm.new_str(c.to_string()))
.ok_or(vm.new_index_error("string index out of range".to_string()))
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()))
@@ -1052,7 +1079,8 @@ pub fn subscript(vm: &mut VirtualMachine, value: &str, b: PyObjectRef) -> PyResu
} else {
match (*b.borrow()).payload {
PyObjectPayload::Slice { .. } => {
Ok(vm.new_str(value.to_string().get_slice_items(&b).to_string()))
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?)",

View File

@@ -12,7 +12,27 @@ use super::objtype;
pub fn init(context: &PyContext) {
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 {

View File

@@ -24,6 +24,11 @@ pub fn create_type(type_type: PyObjectRef, object_type: PyObjectRef, _dict_type:
pub fn init(context: &PyContext) {
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(
@@ -47,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 {

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,7 @@ 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;
@@ -21,10 +22,12 @@ 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;
@@ -113,6 +116,7 @@ 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,
@@ -133,7 +137,9 @@ pub struct PyContext {
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,
@@ -204,12 +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(
@@ -246,12 +255,15 @@ impl PyContext {
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,
super_type,
@@ -280,11 +292,14 @@ impl PyContext {
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);
@@ -336,6 +351,10 @@ impl PyContext {
self.range_type.clone()
}
pub fn slice_type(&self) -> PyObjectRef {
self.slice_type.clone()
}
pub fn frozenset_type(&self) -> PyObjectRef {
self.frozenset_type.clone()
}
@@ -356,6 +375,10 @@ 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()
}
@@ -364,6 +387,10 @@ impl PyContext {
self.map_type.clone()
}
pub fn zip_type(&self) -> PyObjectRef {
self.zip_type.clone()
}
pub fn str_type(&self) -> PyObjectRef {
self.str_type.clone()
}
@@ -886,6 +913,10 @@ pub enum PyObjectPayload {
position: usize,
iterated_obj: PyObjectRef,
},
EnumerateIterator {
counter: BigInt,
iterator: PyObjectRef,
},
FilterIterator {
predicate: PyObjectRef,
iterator: PyObjectRef,
@@ -894,10 +925,13 @@ pub enum PyObjectPayload {
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,
@@ -962,8 +996,10 @@ impl fmt::Debug for PyObjectPayload {
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"),
@@ -1059,8 +1095,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

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

View File

@@ -53,7 +53,7 @@ pub fn os_close(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
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 desciptors (and hence prevent leaks)
//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());

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;
@@ -104,6 +104,11 @@ impl VirtualMachine {
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 {
@@ -122,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 {
@@ -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)?;

View File

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