diff --git a/parser/src/lexer.rs b/parser/src/lexer.rs
index dbde9b56b..da063fffc 100644
--- a/parser/src/lexer.rs
+++ b/parser/src/lexer.rs
@@ -197,6 +197,9 @@ where
// Skip backslash and newline
self.shift();
self.shift();
+ // Idea: insert trailing newline here:
+ // } else if self.chr0 != Some('\n') && self.chr1.is_none() {
+ // self.chr1 = Some('\n');
} else {
break;
}
diff --git a/tests/tobefixed/builtin_max.py b/tests/snippets/builtin_max.py
similarity index 100%
rename from tests/tobefixed/builtin_max.py
rename to tests/snippets/builtin_max.py
diff --git a/tests/snippets/builtins.py b/tests/snippets/builtins.py
index 5033cb633..bbf116abc 100644
--- a/tests/snippets/builtins.py
+++ b/tests/snippets/builtins.py
@@ -17,3 +17,7 @@ assert list(zip(['a', 'b', 'c'], range(3), [9, 8, 7, 99])) == [('a', 0, 9), ('b'
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')
+assert eval(code) == 8
diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs
index aeedff400..845647033 100644
--- a/vm/src/builtins.rs
+++ b/vm/src/builtins.rs
@@ -132,6 +132,8 @@ fn builtin_compile(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
]
);
let source = objstr::get_value(source);
+ // TODO: fix this newline bug:
+ let source = format!("{}\n", source);
let mode = {
let mode = objstr::get_value(mode);
@@ -206,19 +208,36 @@ fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
args,
- required = [
- (source, None), // TODO: Use more specific type
+ required = [(source, None)],
+ optional = [
(_globals, Some(vm.ctx.dict_type())),
(locals, Some(vm.ctx.dict_type()))
]
);
- // TODO: handle optional global and locals
- let code_obj = source; // if source.borrow().kind
+ // Determine code object:
+ let code_obj = if objtype::isinstance(source, &vm.ctx.code_type()) {
+ source.clone()
+ } else if objtype::isinstance(source, &vm.ctx.str_type()) {
+ let mode = compile::Mode::Eval;
+ let source = objstr::get_value(source);
+ // TODO: fix this newline bug:
+ let source = format!("{}\n", source);
+ compile::compile(vm, &source, mode, None)?
+ } else {
+ return Err(vm.new_type_error("code argument must be str or code object".to_string()));
+ };
+ let locals = if let Some(locals) = locals {
+ locals.clone()
+ } else {
+ vm.new_dict()
+ };
+
+ // TODO: handle optional globals
// Construct new scope:
let scope_inner = Scope {
- locals: locals.clone(),
+ locals: locals,
parent: None,
};
let scope = PyObject {
@@ -235,26 +254,38 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
args,
- required = [
- (source, None),
+ required = [(source, None)],
+ optional = [
(_globals, Some(vm.ctx.dict_type())),
(locals, Some(vm.ctx.dict_type()))
]
);
- // TODO: handle optional global and locals
// Determine code object:
let code_obj = if objtype::isinstance(source, &vm.ctx.str_type()) {
let mode = compile::Mode::Exec;
let source = objstr::get_value(source);
+ // TODO: fix this newline bug:
+ let source = format!("{}\n", source);
compile::compile(vm, &source, mode, None)?
- } else {
+ } else if objtype::isinstance(source, &vm.ctx.code_type()) {
source.clone()
+ } else {
+ return Err(vm.new_type_error("source argument must be str or code object".to_string()));
};
+ // handle optional global and locals
+ let locals = if let Some(locals) = locals {
+ locals.clone()
+ } else {
+ vm.new_dict()
+ };
+
+ // TODO: use globals
+
// Construct new scope:
let scope_inner = Scope {
- locals: locals.clone(),
+ locals: locals,
parent: None,
};
let scope = PyObject {
@@ -416,15 +447,56 @@ fn builtin_map(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
}
fn builtin_max(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
- arg_check!(vm, args, required = [(x, None), (y, None)]);
+ // arg_check!(vm, args, required = [(x, None), (y, None)]);
- let order = vm.call_method(x, "__gt__", vec![y.clone()])?;
-
- if objbool::get_value(&order) {
- Ok(x.clone())
+ let candidates = if args.args.len() > 1 {
+ args.args.clone()
+ } else if args.args.len() == 1 {
+ vm.extract_elements(&args.args[0])?
} else {
- Ok(y.clone())
+ // zero arguments means type error:
+ return Err(vm.new_type_error("Expected 1 or more arguments".to_string()));
+ };
+
+ if candidates.len() == 0 {
+ 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()));
+ } else {
+ return Ok(default.unwrap());
+ }
}
+
+ let key_func = args.get_optional_kwarg("key");
+
+ // Start with first assumption:
+ let mut candidates_iter = candidates.into_iter();
+ let mut x = candidates_iter.next().unwrap();
+ // TODO: this key function looks pretty duplicate. Maybe we can create
+ // a local function?
+ let mut x_key = if let Some(f) = &key_func {
+ let args = PyFuncArgs::new(vec![x.clone()], vec![]);
+ vm.invoke(f.clone(), args)?
+ } else {
+ x.clone()
+ };
+
+ for y in candidates_iter {
+ let y_key = if let Some(f) = &key_func {
+ let args = PyFuncArgs::new(vec![y.clone()], vec![]);
+ vm.invoke(f.clone(), args)?
+ } else {
+ y.clone()
+ };
+ let order = vm.call_method(&x_key, "__gt__", vec![y_key.clone()])?;
+
+ if !objbool::get_value(&order) {
+ x = y.clone();
+ x_key = y_key;
+ }
+ }
+
+ Ok(x)
}
// builtin_memoryview
diff --git a/vm/src/obj/objcode.rs b/vm/src/obj/objcode.rs
index 1776c5a3b..53b73d09c 100644
--- a/vm/src/obj/objcode.rs
+++ b/vm/src/obj/objcode.rs
@@ -11,6 +11,7 @@ use super::objtype;
pub fn init(context: &PyContext) {
let ref code_type = context.code_type;
+ code_type.set_attr("__new__", context.new_rustfunc(code_new));
code_type.set_attr("__repr__", context.new_rustfunc(code_repr));
}
@@ -24,6 +25,11 @@ pub fn copy_code(code_obj: &PyObjectRef) -> bytecode::CodeObject {
}
}
+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")))
+}
+
fn code_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(o, Some(vm.ctx.code_type()))]);
diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs
index 0ca4baea9..80440e9d7 100644
--- a/vm/src/obj/objdict.rs
+++ b/vm/src/obj/objdict.rs
@@ -109,6 +109,27 @@ fn dict_delitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
}
}
+/// When iterating over a dictionary, we iterate over the keys of it.
+fn dict_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
+ arg_check!(vm, args, required = [(dict, Some(vm.ctx.dict_type()))]);
+
+ let keys = get_elements(dict)
+ .keys()
+ .map(|k| vm.ctx.new_str(k.to_string()))
+ .collect();
+ let key_list = vm.ctx.new_list(keys);
+
+ let iter_obj = PyObject::new(
+ PyObjectKind::Iterator {
+ position: 0,
+ iterated_obj: key_list,
+ },
+ vm.ctx.iter_type(),
+ );
+
+ Ok(iter_obj)
+}
+
fn dict_setitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
@@ -165,6 +186,7 @@ pub fn init(context: &PyContext) {
dict_type.set_attr("__contains__", context.new_rustfunc(dict_contains));
dict_type.set_attr("__delitem__", context.new_rustfunc(dict_delitem));
dict_type.set_attr("__getitem__", context.new_rustfunc(dict_getitem));
+ dict_type.set_attr("__iter__", context.new_rustfunc(dict_iter));
dict_type.set_attr("__new__", context.new_rustfunc(dict_new));
dict_type.set_attr("__repr__", context.new_rustfunc(dict_repr));
dict_type.set_attr("__setitem__", context.new_rustfunc(dict_setitem));
diff --git a/vm/src/obj/objframe.rs b/vm/src/obj/objframe.rs
index 04c309613..511888b31 100644
--- a/vm/src/obj/objframe.rs
+++ b/vm/src/obj/objframe.rs
@@ -10,9 +10,15 @@ use super::objtype;
pub fn init(context: &PyContext) {
let ref frame_type = context.frame_type;
+ frame_type.set_attr("__new__", context.new_rustfunc(frame_new));
frame_type.set_attr("__repr__", context.new_rustfunc(frame_repr));
}
+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")))
+}
+
fn frame_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(_frame, Some(vm.ctx.frame_type()))]);
let repr = format!("");
diff --git a/vm/src/obj/objiter.rs b/vm/src/obj/objiter.rs
index 1e22a26e7..796d42282 100644
--- a/vm/src/obj/objiter.rs
+++ b/vm/src/obj/objiter.rs
@@ -3,8 +3,7 @@
*/
use super::super::pyobject::{
- AttributeProtocol, PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, PyResult,
- TypeProtocol,
+ AttributeProtocol, PyContext, PyFuncArgs, PyObjectKind, PyObjectRef, PyResult, TypeProtocol,
};
use super::super::vm::VirtualMachine;
use super::objbool;
@@ -17,30 +16,10 @@ use super::objtype; // Required for arg_check! to use isinstance
* function 'iter' is called.
*/
pub fn get_iter(vm: &mut VirtualMachine, iter_target: &PyObjectRef) -> PyResult {
- // Check what we are going to iterate over:
- let iterated_obj = if objtype::isinstance(iter_target, &vm.ctx.iter_type()) {
- // If object is already an iterator, return that one.
- return Ok(iter_target.clone());
- } else if objtype::isinstance(iter_target, &vm.ctx.list_type()) {
- iter_target.clone()
- // } else if hasattr(iter_target, "__iter__") {
- } else {
- return vm.call_method(iter_target, "__iter__", vec![]);
- // let type_str = objstr::get_value(&vm.to_str(iter_target.typ()).unwrap());
- // let type_error = vm.new_type_error(format!("Cannot iterate over {}", type_str));
- // return Err(type_error);
- };
-
- let iter_obj = PyObject::new(
- PyObjectKind::Iterator {
- position: 0,
- iterated_obj: iterated_obj,
- },
- vm.ctx.iter_type(),
- );
-
- // We are all good here:
- Ok(iter_obj)
+ vm.call_method(iter_target, "__iter__", vec![])
+ // let type_str = objstr::get_value(&vm.to_str(iter_target.typ()).unwrap());
+ // let type_error = vm.new_type_error(format!("Cannot iterate over {}", type_str));
+ // return Err(type_error);
}
/*
diff --git a/vm/src/obj/objlist.rs b/vm/src/obj/objlist.rs
index 16f665fd3..6f1fd0908 100644
--- a/vm/src/obj/objlist.rs
+++ b/vm/src/obj/objlist.rs
@@ -183,6 +183,21 @@ fn list_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
get_item(vm, list, &get_elements(list), needle.clone())
}
+fn list_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
+ arg_check!(vm, args, required = [(list, Some(vm.ctx.list_type()))]);
+
+ let iter_obj = PyObject::new(
+ PyObjectKind::Iterator {
+ position: 0,
+ iterated_obj: list.clone(),
+ },
+ vm.ctx.iter_type(),
+ );
+
+ // We are all good here:
+ Ok(iter_obj)
+}
+
fn list_setitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
@@ -199,6 +214,7 @@ pub fn init(context: &PyContext) {
list_type.set_attr("__contains__", context.new_rustfunc(list_contains));
list_type.set_attr("__eq__", context.new_rustfunc(list_eq));
list_type.set_attr("__getitem__", context.new_rustfunc(list_getitem));
+ list_type.set_attr("__iter__", context.new_rustfunc(list_iter));
list_type.set_attr("__setitem__", context.new_rustfunc(list_setitem));
list_type.set_attr("__len__", context.new_rustfunc(list_len));
list_type.set_attr("__new__", context.new_rustfunc(list_new));
diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs
index b31648b3f..7a6c7e780 100644
--- a/vm/src/obj/objstr.rs
+++ b/vm/src/obj/objstr.rs
@@ -14,6 +14,7 @@ pub fn init(context: &PyContext) {
str_type.set_attr("__eq__", context.new_rustfunc(str_eq));
str_type.set_attr("__contains__", context.new_rustfunc(str_contains));
str_type.set_attr("__getitem__", context.new_rustfunc(str_getitem));
+ str_type.set_attr("__gt__", context.new_rustfunc(str_gt));
str_type.set_attr("__len__", context.new_rustfunc(str_len));
str_type.set_attr("__mul__", context.new_rustfunc(str_mul));
str_type.set_attr("__new__", context.new_rustfunc(str_new));
@@ -53,6 +54,21 @@ fn str_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
Ok(vm.ctx.new_bool(result))
}
+fn str_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
+ arg_check!(
+ vm,
+ args,
+ required = [
+ (zelf, Some(vm.ctx.str_type())),
+ (other, Some(vm.ctx.str_type()))
+ ]
+ );
+ let zelf = get_value(zelf);
+ let other = get_value(other);
+ let result = zelf > other;
+ Ok(vm.ctx.new_bool(result))
+}
+
fn str_str(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]);
Ok(s.clone())
diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs
index 1d1a8bff9..9a7b64395 100644
--- a/vm/src/obj/objtuple.rs
+++ b/vm/src/obj/objtuple.rs
@@ -48,6 +48,20 @@ fn tuple_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
Ok(vm.ctx.new_int(x.to_bigint().unwrap()))
}
+fn tuple_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
+ arg_check!(vm, args, required = [(tuple, Some(vm.ctx.tuple_type()))]);
+
+ let iter_obj = PyObject::new(
+ PyObjectKind::Iterator {
+ position: 0,
+ iterated_obj: tuple.clone(),
+ },
+ vm.ctx.iter_type(),
+ );
+
+ Ok(iter_obj)
+}
+
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);
@@ -132,6 +146,7 @@ pub fn init(context: &PyContext) {
tuple_type.set_attr("__contains__", context.new_rustfunc(tuple_contains));
tuple_type.set_attr("__getitem__", context.new_rustfunc(tuple_getitem));
tuple_type.set_attr("__hash__", context.new_rustfunc(tuple_hash));
+ tuple_type.set_attr("__iter__", context.new_rustfunc(tuple_iter));
tuple_type.set_attr("__len__", context.new_rustfunc(tuple_len));
tuple_type.set_attr("__new__", context.new_rustfunc(tuple_new));
tuple_type.set_attr("__repr__", context.new_rustfunc(tuple_repr));
diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs
index b65757714..bfdd4d668 100644
--- a/vm/src/pyobject.rs
+++ b/vm/src/pyobject.rs
@@ -728,6 +728,15 @@ impl PyFuncArgs {
}
default.clone()
}
+
+ pub fn get_optional_kwarg(&self, key: &str) -> Option {
+ for (arg_name, arg_value) in self.kwargs.iter() {
+ if arg_name == key {
+ return Some(arg_value.clone());
+ }
+ }
+ None
+ }
}
type RustPyFunc = fn(vm: &mut VirtualMachine, PyFuncArgs) -> PyResult;
diff --git a/vm/src/vm.rs b/vm/src/vm.rs
index 41946a2de..9435db199 100644
--- a/vm/src/vm.rs
+++ b/vm/src/vm.rs
@@ -35,6 +35,7 @@ pub struct VirtualMachine {
pub sys_module: PyObjectRef,
pub stdlib_inits: HashMap,
pub ctx: PyContext,
+ pub current_frame: Option,
}
impl VirtualMachine {
@@ -51,6 +52,7 @@ impl VirtualMachine {
sys_module: sysmod,
stdlib_inits,
ctx: ctx,
+ current_frame: None,
}
}