Skip to content

Commit 99af466

Browse files
committed
Move max function test cases to snippet dir. Implement key and default argument on max function.
1 parent 4acea45 commit 99af466

File tree

13 files changed

+191
-41
lines changed

13 files changed

+191
-41
lines changed

parser/src/lexer.rs

+3
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,9 @@ where
197197
// Skip backslash and newline
198198
self.shift();
199199
self.shift();
200+
// Idea: insert trailing newline here:
201+
// } else if self.chr0 != Some('\n') && self.chr1.is_none() {
202+
// self.chr1 = Some('\n');
200203
} else {
201204
break;
202205
}
File renamed without changes.

tests/snippets/builtins.py

+4
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,7 @@
1717

1818
assert list(filter(lambda x: ((x % 2) == 0), [0, 1, 2])) == [0, 2]
1919

20+
assert 3 == eval('1+2')
21+
22+
code = compile('5+3', 'x.py', 'eval')
23+
assert eval(code) == 8

vm/src/builtins.rs

+87-15
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ fn builtin_compile(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
132132
]
133133
);
134134
let source = objstr::get_value(source);
135+
// TODO: fix this newline bug:
136+
let source = format!("{}\n", source);
135137

136138
let mode = {
137139
let mode = objstr::get_value(mode);
@@ -206,19 +208,36 @@ fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
206208
arg_check!(
207209
vm,
208210
args,
209-
required = [
210-
(source, None), // TODO: Use more specific type
211+
required = [(source, None)],
212+
optional = [
211213
(_globals, Some(vm.ctx.dict_type())),
212214
(locals, Some(vm.ctx.dict_type()))
213215
]
214216
);
215-
// TODO: handle optional global and locals
216217

217-
let code_obj = source; // if source.borrow().kind
218+
// Determine code object:
219+
let code_obj = if objtype::isinstance(source, &vm.ctx.code_type()) {
220+
source.clone()
221+
} else if objtype::isinstance(source, &vm.ctx.str_type()) {
222+
let mode = compile::Mode::Eval;
223+
let source = objstr::get_value(source);
224+
// TODO: fix this newline bug:
225+
let source = format!("{}\n", source);
226+
compile::compile(vm, &source, mode, None)?
227+
} else {
228+
return Err(vm.new_type_error("code argument must be str or code object".to_string()));
229+
};
230+
231+
let locals = if let Some(locals) = locals {
232+
locals.clone()
233+
} else {
234+
vm.new_dict()
235+
};
218236

237+
// TODO: handle optional globals
219238
// Construct new scope:
220239
let scope_inner = Scope {
221-
locals: locals.clone(),
240+
locals: locals,
222241
parent: None,
223242
};
224243
let scope = PyObject {
@@ -235,26 +254,38 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
235254
arg_check!(
236255
vm,
237256
args,
238-
required = [
239-
(source, None),
257+
required = [(source, None)],
258+
optional = [
240259
(_globals, Some(vm.ctx.dict_type())),
241260
(locals, Some(vm.ctx.dict_type()))
242261
]
243262
);
244-
// TODO: handle optional global and locals
245263

246264
// Determine code object:
247265
let code_obj = if objtype::isinstance(source, &vm.ctx.str_type()) {
248266
let mode = compile::Mode::Exec;
249267
let source = objstr::get_value(source);
268+
// TODO: fix this newline bug:
269+
let source = format!("{}\n", source);
250270
compile::compile(vm, &source, mode, None)?
251-
} else {
271+
} else if objtype::isinstance(source, &vm.ctx.code_type()) {
252272
source.clone()
273+
} else {
274+
return Err(vm.new_type_error("source argument must be str or code object".to_string()));
253275
};
254276

277+
// handle optional global and locals
278+
let locals = if let Some(locals) = locals {
279+
locals.clone()
280+
} else {
281+
vm.new_dict()
282+
};
283+
284+
// TODO: use globals
285+
255286
// Construct new scope:
256287
let scope_inner = Scope {
257-
locals: locals.clone(),
288+
locals: locals,
258289
parent: None,
259290
};
260291
let scope = PyObject {
@@ -416,15 +447,56 @@ fn builtin_map(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
416447
}
417448

418449
fn builtin_max(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
419-
arg_check!(vm, args, required = [(x, None), (y, None)]);
450+
// arg_check!(vm, args, required = [(x, None), (y, None)]);
420451

421-
let order = vm.call_method(x, "__gt__", vec![y.clone()])?;
452+
let candidates = if args.args.len() > 1 {
453+
args.args.clone()
454+
} else if args.args.len() == 1 {
455+
vm.extract_elements(&args.args[0])?
456+
} else {
457+
// zero arguments means type error:
458+
return Err(vm.new_type_error("Expected 1 or more arguments".to_string()));
459+
};
422460

423-
if objbool::get_value(&order) {
424-
Ok(x.clone())
461+
if candidates.len() == 0 {
462+
let default = args.get_optional_kwarg("default");
463+
if default.is_none() {
464+
return Err(vm.new_value_error("max() arg is an empty sequence".to_string()));
465+
} else {
466+
return Ok(default.unwrap());
467+
}
468+
}
469+
470+
let key_func = args.get_optional_kwarg("key");
471+
472+
// Start with first assumption:
473+
let mut candidates_iter = candidates.into_iter();
474+
let mut x = candidates_iter.next().unwrap();
475+
// TODO: this key function looks pretty duplicate. Maybe we can create
476+
// a local function?
477+
let mut x_key = if let Some(f) = &key_func {
478+
let args = PyFuncArgs::new(vec![x.clone()], vec![]);
479+
vm.invoke(f.clone(), args)?
425480
} else {
426-
Ok(y.clone())
481+
x.clone()
482+
};
483+
484+
for y in candidates_iter {
485+
let y_key = if let Some(f) = &key_func {
486+
let args = PyFuncArgs::new(vec![y.clone()], vec![]);
487+
vm.invoke(f.clone(), args)?
488+
} else {
489+
y.clone()
490+
};
491+
let order = vm.call_method(&x_key, "__gt__", vec![y_key.clone()])?;
492+
493+
if !objbool::get_value(&order) {
494+
x = y.clone();
495+
x_key = y_key;
496+
}
427497
}
498+
499+
Ok(x)
428500
}
429501

430502
// builtin_memoryview

vm/src/obj/objcode.rs

+6
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use super::objtype;
1111

1212
pub fn init(context: &PyContext) {
1313
let ref code_type = context.code_type;
14+
code_type.set_attr("__new__", context.new_rustfunc(code_new));
1415
code_type.set_attr("__repr__", context.new_rustfunc(code_repr));
1516
}
1617

@@ -24,6 +25,11 @@ pub fn copy_code(code_obj: &PyObjectRef) -> bytecode::CodeObject {
2425
}
2526
}
2627

28+
fn code_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
29+
arg_check!(vm, args, required = [(_cls, None)]);
30+
Err(vm.new_type_error(format!("Cannot directly create code object")))
31+
}
32+
2733
fn code_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
2834
arg_check!(vm, args, required = [(o, Some(vm.ctx.code_type()))]);
2935

vm/src/obj/objdict.rs

+22
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,27 @@ fn dict_delitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
109109
}
110110
}
111111

112+
/// When iterating over a dictionary, we iterate over the keys of it.
113+
fn dict_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
114+
arg_check!(vm, args, required = [(dict, Some(vm.ctx.dict_type()))]);
115+
116+
let keys = get_elements(dict)
117+
.keys()
118+
.map(|k| vm.ctx.new_str(k.to_string()))
119+
.collect();
120+
let key_list = vm.ctx.new_list(keys);
121+
122+
let iter_obj = PyObject::new(
123+
PyObjectKind::Iterator {
124+
position: 0,
125+
iterated_obj: key_list,
126+
},
127+
vm.ctx.iter_type(),
128+
);
129+
130+
Ok(iter_obj)
131+
}
132+
112133
fn dict_setitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
113134
arg_check!(
114135
vm,
@@ -165,6 +186,7 @@ pub fn init(context: &PyContext) {
165186
dict_type.set_attr("__contains__", context.new_rustfunc(dict_contains));
166187
dict_type.set_attr("__delitem__", context.new_rustfunc(dict_delitem));
167188
dict_type.set_attr("__getitem__", context.new_rustfunc(dict_getitem));
189+
dict_type.set_attr("__iter__", context.new_rustfunc(dict_iter));
168190
dict_type.set_attr("__new__", context.new_rustfunc(dict_new));
169191
dict_type.set_attr("__repr__", context.new_rustfunc(dict_repr));
170192
dict_type.set_attr("__setitem__", context.new_rustfunc(dict_setitem));

vm/src/obj/objframe.rs

+6
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,15 @@ use super::objtype;
1010

1111
pub fn init(context: &PyContext) {
1212
let ref frame_type = context.frame_type;
13+
frame_type.set_attr("__new__", context.new_rustfunc(frame_new));
1314
frame_type.set_attr("__repr__", context.new_rustfunc(frame_repr));
1415
}
1516

17+
fn frame_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
18+
arg_check!(vm, args, required = [(_cls, None)]);
19+
Err(vm.new_type_error(format!("Cannot directly create frame object")))
20+
}
21+
1622
fn frame_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
1723
arg_check!(vm, args, required = [(_frame, Some(vm.ctx.frame_type()))]);
1824
let repr = format!("<frame object at .. >");

vm/src/obj/objiter.rs

+5-26
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
*/
44

55
use super::super::pyobject::{
6-
AttributeProtocol, PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, PyResult,
7-
TypeProtocol,
6+
AttributeProtocol, PyContext, PyFuncArgs, PyObjectKind, PyObjectRef, PyResult, TypeProtocol,
87
};
98
use super::super::vm::VirtualMachine;
109
use super::objbool;
@@ -17,30 +16,10 @@ use super::objtype; // Required for arg_check! to use isinstance
1716
* function 'iter' is called.
1817
*/
1918
pub fn get_iter(vm: &mut VirtualMachine, iter_target: &PyObjectRef) -> PyResult {
20-
// Check what we are going to iterate over:
21-
let iterated_obj = if objtype::isinstance(iter_target, &vm.ctx.iter_type()) {
22-
// If object is already an iterator, return that one.
23-
return Ok(iter_target.clone());
24-
} else if objtype::isinstance(iter_target, &vm.ctx.list_type()) {
25-
iter_target.clone()
26-
// } else if hasattr(iter_target, "__iter__") {
27-
} else {
28-
return vm.call_method(iter_target, "__iter__", vec![]);
29-
// let type_str = objstr::get_value(&vm.to_str(iter_target.typ()).unwrap());
30-
// let type_error = vm.new_type_error(format!("Cannot iterate over {}", type_str));
31-
// return Err(type_error);
32-
};
33-
34-
let iter_obj = PyObject::new(
35-
PyObjectKind::Iterator {
36-
position: 0,
37-
iterated_obj: iterated_obj,
38-
},
39-
vm.ctx.iter_type(),
40-
);
41-
42-
// We are all good here:
43-
Ok(iter_obj)
19+
vm.call_method(iter_target, "__iter__", vec![])
20+
// let type_str = objstr::get_value(&vm.to_str(iter_target.typ()).unwrap());
21+
// let type_error = vm.new_type_error(format!("Cannot iterate over {}", type_str));
22+
// return Err(type_error);
4423
}
4524

4625
/*

vm/src/obj/objlist.rs

+16
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,21 @@ fn list_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
183183
get_item(vm, list, &get_elements(list), needle.clone())
184184
}
185185

186+
fn list_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
187+
arg_check!(vm, args, required = [(list, Some(vm.ctx.list_type()))]);
188+
189+
let iter_obj = PyObject::new(
190+
PyObjectKind::Iterator {
191+
position: 0,
192+
iterated_obj: list.clone(),
193+
},
194+
vm.ctx.iter_type(),
195+
);
196+
197+
// We are all good here:
198+
Ok(iter_obj)
199+
}
200+
186201
fn list_setitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
187202
arg_check!(
188203
vm,
@@ -199,6 +214,7 @@ pub fn init(context: &PyContext) {
199214
list_type.set_attr("__contains__", context.new_rustfunc(list_contains));
200215
list_type.set_attr("__eq__", context.new_rustfunc(list_eq));
201216
list_type.set_attr("__getitem__", context.new_rustfunc(list_getitem));
217+
list_type.set_attr("__iter__", context.new_rustfunc(list_iter));
202218
list_type.set_attr("__setitem__", context.new_rustfunc(list_setitem));
203219
list_type.set_attr("__len__", context.new_rustfunc(list_len));
204220
list_type.set_attr("__new__", context.new_rustfunc(list_new));

vm/src/obj/objstr.rs

+16
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ pub fn init(context: &PyContext) {
1414
str_type.set_attr("__eq__", context.new_rustfunc(str_eq));
1515
str_type.set_attr("__contains__", context.new_rustfunc(str_contains));
1616
str_type.set_attr("__getitem__", context.new_rustfunc(str_getitem));
17+
str_type.set_attr("__gt__", context.new_rustfunc(str_gt));
1718
str_type.set_attr("__len__", context.new_rustfunc(str_len));
1819
str_type.set_attr("__mul__", context.new_rustfunc(str_mul));
1920
str_type.set_attr("__new__", context.new_rustfunc(str_new));
@@ -53,6 +54,21 @@ fn str_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
5354
Ok(vm.ctx.new_bool(result))
5455
}
5556

57+
fn str_gt(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
58+
arg_check!(
59+
vm,
60+
args,
61+
required = [
62+
(zelf, Some(vm.ctx.str_type())),
63+
(other, Some(vm.ctx.str_type()))
64+
]
65+
);
66+
let zelf = get_value(zelf);
67+
let other = get_value(other);
68+
let result = zelf > other;
69+
Ok(vm.ctx.new_bool(result))
70+
}
71+
5672
fn str_str(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
5773
arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]);
5874
Ok(s.clone())

vm/src/obj/objtuple.rs

+15
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,20 @@ fn tuple_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
4848
Ok(vm.ctx.new_int(x.to_bigint().unwrap()))
4949
}
5050

51+
fn tuple_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
52+
arg_check!(vm, args, required = [(tuple, Some(vm.ctx.tuple_type()))]);
53+
54+
let iter_obj = PyObject::new(
55+
PyObjectKind::Iterator {
56+
position: 0,
57+
iterated_obj: tuple.clone(),
58+
},
59+
vm.ctx.iter_type(),
60+
);
61+
62+
Ok(iter_obj)
63+
}
64+
5165
fn tuple_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
5266
arg_check!(vm, args, required = [(zelf, Some(vm.ctx.tuple_type()))]);
5367
let elements = get_elements(zelf);
@@ -132,6 +146,7 @@ pub fn init(context: &PyContext) {
132146
tuple_type.set_attr("__contains__", context.new_rustfunc(tuple_contains));
133147
tuple_type.set_attr("__getitem__", context.new_rustfunc(tuple_getitem));
134148
tuple_type.set_attr("__hash__", context.new_rustfunc(tuple_hash));
149+
tuple_type.set_attr("__iter__", context.new_rustfunc(tuple_iter));
135150
tuple_type.set_attr("__len__", context.new_rustfunc(tuple_len));
136151
tuple_type.set_attr("__new__", context.new_rustfunc(tuple_new));
137152
tuple_type.set_attr("__repr__", context.new_rustfunc(tuple_repr));

vm/src/pyobject.rs

+9
Original file line numberDiff line numberDiff line change
@@ -728,6 +728,15 @@ impl PyFuncArgs {
728728
}
729729
default.clone()
730730
}
731+
732+
pub fn get_optional_kwarg(&self, key: &str) -> Option<PyObjectRef> {
733+
for (arg_name, arg_value) in self.kwargs.iter() {
734+
if arg_name == key {
735+
return Some(arg_value.clone());
736+
}
737+
}
738+
None
739+
}
731740
}
732741

733742
type RustPyFunc = fn(vm: &mut VirtualMachine, PyFuncArgs) -> PyResult;

0 commit comments

Comments
 (0)