Skip to content

Commit 45dc24c

Browse files
committed
Implement for loops.
1 parent c369776 commit 45dc24c

File tree

7 files changed

+290
-97
lines changed

7 files changed

+290
-97
lines changed

functional_tests/for_loop.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
mytuple = (1, 2, 4, 8, 16, 42)
2+
for n in mytuple:
3+
print(n)
4+
5+
print('end')

src/objects/mod.rs

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ use std::collections::HashMap;
33
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
44
use std::fmt;
55
use self::itertools::Itertools;
6+
use super::state::State;
7+
use super::sandbox::EnvProxy;
68

79
#[derive(Debug)]
810
#[derive(Clone)]
@@ -53,25 +55,44 @@ pub enum ObjectContent {
5355
PrimitiveNamespace, // __primitives__
5456
PrimitiveFunction(String),
5557
Class(Option<ObjectRef>),
58+
RandomAccessIterator(ObjectRef, usize, u64), // container, index, container version
5659
OtherObject,
5760
}
5861

62+
static CURRENT_VERSION: AtomicUsize = ATOMIC_USIZE_INIT;
63+
5964
#[derive(Debug)]
6065
#[derive(Clone)]
6166
pub struct Object {
67+
pub version: u64,
6268
pub name: Option<String>,
6369
pub content: ObjectContent,
6470
pub class: ObjectRef,
6571
pub bases: Option<Vec<ObjectRef>>, // superclasses
6672
}
6773

6874
impl Object {
75+
fn new_version() -> u64 {
76+
CURRENT_VERSION.fetch_add(1, Ordering::SeqCst) as u64 // TODO: avoid cast
77+
}
6978
pub fn new_instance(name: Option<String>, class: ObjectRef, content: ObjectContent) -> Object {
70-
Object { name: name, content: content, class: class, bases: None }
79+
Object {
80+
version: Object::new_version(),
81+
name: name,
82+
content: content,
83+
class: class,
84+
bases: None,
85+
}
7186
}
7287

7388
pub fn new_class(name: String, code: Option<ObjectRef>, metaclass: ObjectRef, bases: Vec<ObjectRef>) -> Object {
74-
Object { name: Some(name), content: ObjectContent::Class(code), class: metaclass, bases: Some(bases) }
89+
Object {
90+
version: Object::new_version(),
91+
name: Some(name),
92+
content: ObjectContent::Class(code),
93+
class: metaclass,
94+
bases: Some(bases),
95+
}
7596
}
7697
}
7798

@@ -135,6 +156,9 @@ impl ObjectRef {
135156
Some(ref s) => format!("<module {}", s),
136157
}
137158
},
159+
ObjectContent::RandomAccessIterator(ref container, ref index, ref version) => {
160+
format!("<iterator on {} at index {}>", store.deref(container).class.repr(store), version)
161+
}
138162
ObjectContent::OtherObject => format!("<{} instance>", obj.class.repr(store)),
139163
}
140164
}
@@ -148,6 +172,13 @@ impl ObjectRef {
148172
_ => panic!(format!("Not a function/module: {:?}", func)),
149173
}
150174
}
175+
176+
pub fn iter<EP: EnvProxy>(&self, state: &mut State<EP>) -> ObjectRef {
177+
// TODO: check it's a list or a tuple
178+
let obj_version = state.store.deref(self).version;
179+
let iterator = Object::new_instance(None, state.primitive_objects.iterator_type.clone(), ObjectContent::RandomAccessIterator(self.clone(), 0, obj_version));
180+
state.store.allocate(iterator)
181+
}
151182
}
152183

153184

@@ -214,6 +245,8 @@ pub struct PrimitiveObjects {
214245
pub bytes_type: ObjectRef,
215246
pub str_type: ObjectRef,
216247

248+
pub iterator_type: ObjectRef,
249+
217250
pub function_type: ObjectRef,
218251
pub code_type: ObjectRef,
219252

@@ -226,6 +259,7 @@ pub struct PrimitiveObjects {
226259
pub nameerror: ObjectRef,
227260
pub attributeerror: ObjectRef,
228261
pub typeerror: ObjectRef,
262+
pub stopiteration: ObjectRef,
229263

230264
pub lookuperror: ObjectRef,
231265
pub keyerror: ObjectRef,
@@ -237,8 +271,20 @@ impl PrimitiveObjects {
237271
pub fn new(store: &mut ObjectStore) -> PrimitiveObjects {
238272
let obj_ref = ObjectRef::new();
239273
let type_ref = ObjectRef::new();
240-
let obj = Object { name: Some("object".to_string()), content: ObjectContent::OtherObject, bases: Some(vec![]), class: type_ref.clone() };
241-
let type_ = Object { name: Some("type".to_string()), content: ObjectContent::OtherObject, bases: Some(vec![obj_ref.clone()]), class: type_ref.clone() };
274+
let obj = Object {
275+
version: Object::new_version(),
276+
name: Some("object".to_string()),
277+
content: ObjectContent::OtherObject,
278+
bases: Some(vec![]),
279+
class: type_ref.clone()
280+
};
281+
let type_ = Object {
282+
version: Object::new_version(),
283+
name: Some("type".to_string()),
284+
content: ObjectContent::OtherObject,
285+
bases: Some(vec![obj_ref.clone()]),
286+
class: type_ref.clone()
287+
};
242288
store.allocate_at(obj_ref.clone(), obj);
243289
store.allocate_at(type_ref.clone(), type_);
244290

@@ -256,6 +302,7 @@ impl PrimitiveObjects {
256302
let frozenset_type = store.allocate(Object::new_class("frozenset".to_string(), None, type_ref.clone(), vec![obj_ref.clone()]));
257303
let bytes_type = store.allocate(Object::new_class("bytes".to_string(), None, type_ref.clone(), vec![obj_ref.clone()]));
258304
let str_type = store.allocate(Object::new_class("str".to_string(), None, type_ref.clone(), vec![obj_ref.clone()]));
305+
let iterator_type = store.allocate(Object::new_class("iterator".to_string(), None, type_ref.clone(), vec![obj_ref.clone()]));
259306

260307
let function_type = store.allocate(Object::new_class("function".to_string(), None, type_ref.clone(), vec![obj_ref.clone()]));
261308
let code_type = store.allocate(Object::new_class("code".to_string(), None, type_ref.clone(), vec![obj_ref.clone()]));
@@ -269,6 +316,7 @@ impl PrimitiveObjects {
269316
let nameerror = store.allocate(Object::new_class("NameError".to_string(), None, type_ref.clone(), vec![exception.clone()]));
270317
let attributeerror = store.allocate(Object::new_class("AttributeError".to_string(), None, type_ref.clone(), vec![exception.clone()]));
271318
let typeerror = store.allocate(Object::new_class("TypeError".to_string(), None, type_ref.clone(), vec![exception.clone()]));
319+
let stopiteration = store.allocate(Object::new_class("StopIteration".to_string(), None, type_ref.clone(), vec![exception.clone()]));
272320

273321
let lookuperror = store.allocate(Object::new_class("LookupError".to_string(), None, type_ref.clone(), vec![exception.clone()]));
274322
let keyerror = store.allocate(Object::new_class("KeyError".to_string(), None, type_ref.clone(), vec![lookuperror.clone()]));
@@ -301,6 +349,7 @@ impl PrimitiveObjects {
301349
map.insert("NameError".to_string(), nameerror.clone());
302350
map.insert("AttributeError".to_string(), attributeerror.clone());
303351
map.insert("TypeError".to_string(), typeerror.clone());
352+
map.insert("StopIteration".to_string(), stopiteration.clone());
304353

305354
map.insert("LookupError".to_string(), lookuperror.clone());
306355
map.insert("KeyError".to_string(), keyerror.clone());
@@ -312,9 +361,10 @@ impl PrimitiveObjects {
312361
tuple_type: tuple_type, list_type: list_type,
313362
set_type: set_type, frozenset_type: frozenset_type,
314363
bytes_type: bytes_type, str_type: str_type,
364+
iterator_type: iterator_type,
315365
function_type: function_type, code_type: code_type,
316366
baseexception: baseexception, processorerror: processorerror, exception: exception,
317-
nameerror: nameerror, attributeerror: attributeerror, typeerror: typeerror,
367+
nameerror: nameerror, attributeerror: attributeerror, typeerror: typeerror, stopiteration: stopiteration,
318368
lookuperror: lookuperror, keyerror: keyerror,
319369
module: module,
320370
names_map: map,

src/primitives/mod.rs

Lines changed: 79 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ use std::collections::{HashMap, HashSet};
22
use std::io::Write;
33
use std::collections::linked_list::LinkedList;
44
use super::sandbox::EnvProxy;
5-
use super::state::{State, PyResult, PyFunction};
6-
use super::objects::{ObjectRef, ObjectContent, Object};
5+
use super::state::{State, PyResult, PyFunction, raise, return_value};
6+
use super::objects::{ObjectRef, ObjectContent, Object, ObjectStore};
7+
use super::processor::frame::Frame;
78

89
macro_rules! parse_first_arguments {
910
( $funcname:expr, $store:expr, $args:ident, $args_iter:ident, $( $argname:tt $argexpected:tt : { $($argpattern:pat => $argcode:block,)* } ),* ) => {{
@@ -31,7 +32,7 @@ macro_rules! parse_arguments {
3132
}};
3233
}
3334

34-
fn write_stdout<EP: EnvProxy>(processor: &mut State<EP>, args: Vec<ObjectRef>) -> PyResult {
35+
fn write_stdout<EP: EnvProxy>(processor: &mut State<EP>, call_stack: &mut Vec<Frame>, args: Vec<ObjectRef>) {
3536
parse_arguments!("__primitives__.write_stdout", processor.store, args,
3637
"value" "a string, boolean, or integer": {
3738
ObjectContent::String(ref s) => {
@@ -48,10 +49,10 @@ fn write_stdout<EP: EnvProxy>(processor: &mut State<EP>, args: Vec<ObjectRef>) -
4849
},
4950
}
5051
);
51-
PyResult::Return(processor.primitive_objects.none.clone())
52+
return_value(call_stack, processor.primitive_objects.none.clone())
5253
}
5354

54-
fn build_class<EP: EnvProxy>(processor: &mut State<EP>, args: Vec<ObjectRef>) -> PyResult {
55+
fn build_class<EP: EnvProxy>(processor: &mut State<EP>, call_stack: &mut Vec<Frame>, args: Vec<ObjectRef>) {
5556
let name;
5657
let code;
5758
let mut args_iter = args.into_iter();
@@ -72,15 +73,10 @@ fn build_class<EP: EnvProxy>(processor: &mut State<EP>, args: Vec<ObjectRef>) ->
7273
else {
7374
bases
7475
};
75-
PyResult::Return(processor.store.allocate(Object::new_class(name, Some(code), processor.primitive_objects.type_.clone(), bases)))
76+
return_value(call_stack, processor.store.allocate(Object::new_class(name, Some(code), processor.primitive_objects.type_.clone(), bases)))
7677
}
7778

78-
fn issubclass<EP: EnvProxy>(processor: &mut State<EP>, args: Vec<ObjectRef>) -> PyResult {
79-
if args.len() != 2 {
80-
panic!(format!("__primitives__.issubclass takes 2 arguments, not {}", args.len()))
81-
}
82-
let first = args.get(0).unwrap();
83-
let second = args.get(1).unwrap();
79+
pub fn native_issubclass(store: &ObjectStore, first: &ObjectRef, second: &ObjectRef) -> bool {
8480
let mut visited = HashSet::new();
8581
let mut to_visit = LinkedList::new();
8682
to_visit.push_back(first.clone());
@@ -90,9 +86,9 @@ fn issubclass<EP: EnvProxy>(processor: &mut State<EP>, args: Vec<ObjectRef>) ->
9086
continue
9187
};
9288
if candidate.is(second) {
93-
return PyResult::Return(processor.primitive_objects.true_obj.clone())
89+
return true
9490
};
95-
match processor.store.deref(&candidate).bases {
91+
match store.deref(&candidate).bases {
9692
None => (),
9793
Some(ref bases) => {
9894
for base in bases.iter() {
@@ -101,17 +97,81 @@ fn issubclass<EP: EnvProxy>(processor: &mut State<EP>, args: Vec<ObjectRef>) ->
10197
}
10298
};
10399
}
104-
PyResult::Return(processor.primitive_objects.false_obj.clone())
100+
false
105101
}
106102

107-
fn isinstance<EP: EnvProxy>(processor: &mut State<EP>, mut args: Vec<ObjectRef>) -> PyResult {
103+
fn issubclass<EP: EnvProxy>(state: &mut State<EP>, call_stack: &mut Vec<Frame>, args: Vec<ObjectRef>) {
104+
if args.len() != 2 {
105+
panic!(format!("__primitives__.issubclass takes 2 arguments, not {}", args.len()))
106+
}
107+
let first = args.get(0).unwrap();
108+
let second = args.get(1).unwrap();
109+
let res = native_issubclass(&state.store, first, second);
110+
if res {
111+
return_value(call_stack, state.primitive_objects.true_obj.clone())
112+
}
113+
else {
114+
return_value(call_stack, state.primitive_objects.false_obj.clone())
115+
}
116+
}
117+
118+
pub fn native_isinstance(store: &ObjectStore, first: &ObjectRef, second: &ObjectRef) -> bool {
119+
native_issubclass(store, &store.deref(&first).class, second)
120+
}
121+
122+
fn isinstance<EP: EnvProxy>(state: &mut State<EP>, call_stack: &mut Vec<Frame>, mut args: Vec<ObjectRef>) {
108123
if args.len() != 2 {
109124
panic!(format!("__primitives__.isinstance takes 2 arguments, not {}", args.len()))
110125
}
111126
let second = args.pop().unwrap();
112127
let first = args.pop().unwrap();
113-
let new_args = vec![processor.store.deref(&first).class.clone(), second];
114-
issubclass(processor, new_args)
128+
let res = native_isinstance(&state.store, &first, &second);
129+
if res {
130+
return_value(call_stack, state.primitive_objects.true_obj.clone())
131+
}
132+
else {
133+
return_value(call_stack, state.primitive_objects.false_obj.clone())
134+
}
135+
}
136+
137+
fn iter<EP: EnvProxy>(state: &mut State<EP>, call_stack: &mut Vec<Frame>, mut args: Vec<ObjectRef>) {
138+
if args.len() != 1 {
139+
panic!(format!("__primitives__.iter takes 1 arguments, not {}", args.len()))
140+
}
141+
let iterator_ref = args.last().unwrap();
142+
let iterator = state.store.deref(iterator_ref).clone();
143+
match iterator.content {
144+
ObjectContent::RandomAccessIterator(container_ref, index, container_version) => {
145+
let value = {
146+
let container = state.store.deref(&container_ref);
147+
if container.version != container_version {
148+
panic!("Container changed while iterating.")
149+
};
150+
match container.content {
151+
ObjectContent::List(ref v) | ObjectContent::Tuple(ref v) => v.get(index).map(|r| r.clone()),
152+
ref c => panic!(format!("RandomAccessIterator does not support {}", container_ref.repr(&state.store)))
153+
}
154+
};
155+
match value {
156+
Some(value) => {
157+
let mut iterator = state.store.deref_mut(iterator_ref);
158+
iterator.content = ObjectContent::RandomAccessIterator(container_ref, index+1, container_version);
159+
return_value(call_stack, value.clone())
160+
}
161+
None => {
162+
let stopiteration = state.primitive_objects.stopiteration.clone();
163+
164+
return raise(state, call_stack, stopiteration, "StopIteration instance".to_string())
165+
}
166+
}
167+
}
168+
ref c => {
169+
let repr = iterator_ref.repr(&state.store);
170+
let exc = Object::new_instance(None, state.primitive_objects.typeerror.clone(), ObjectContent::OtherObject);
171+
let exc = state.store.allocate(exc);
172+
raise(state, call_stack, exc, format!("{} is not an iterator", repr));
173+
}
174+
}
115175
}
116176

117177

@@ -121,5 +181,6 @@ pub fn get_default_primitives<EP: EnvProxy>() -> HashMap<String, PyFunction<EP>>
121181
builtins.insert("build_class".to_string(), build_class);
122182
builtins.insert("issubclass".to_string(), issubclass);
123183
builtins.insert("isinstance".to_string(), isinstance);
184+
builtins.insert("iter".to_string(), iter);
124185
builtins
125186
}

src/processor/frame.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use super::instructions::{Instruction, InstructionDecoder};
99
pub enum Block {
1010
Loop(usize, usize), // begin, end
1111
TryExcept(usize, usize), // begin, end
12+
ExceptPopGoto(ObjectRef, usize, usize), // If an exception matchs the first arg matches, pop n elements from the stack and set the PC to the second arg
1213
}
1314

1415
#[derive(Debug)]

src/processor/instructions.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,19 +42,22 @@ pub enum Instruction {
4242
DupTop,
4343
Nop,
4444
BinarySubscr,
45+
GetIter,
4546
LoadBuildClass,
4647
ReturnValue,
4748
PopBlock,
4849
EndFinally,
4950
PopExcept,
5051
StoreName(usize),
52+
ForIter(usize),
5153
LoadConst(usize),
5254
LoadName(usize),
5355
LoadAttr(usize),
5456
SetupLoop(usize),
5557
SetupExcept(usize),
5658
CompareOp(CmpOperator),
5759
JumpForward(usize),
60+
JumpAbsolute(usize),
5861
PopJumpIfFalse(usize),
5962
LoadFast(usize),
6063
LoadGlobal(usize),
@@ -108,17 +111,20 @@ impl<'a, I> Iterator for InstructionDecoder<I> where I: Iterator<Item=&'a u8> {
108111
1 => Instruction::PopTop,
109112
4 => Instruction::DupTop,
110113
25 => Instruction::BinarySubscr,
114+
68 => Instruction::GetIter,
111115
71 => Instruction::LoadBuildClass,
112116
83 => Instruction::ReturnValue,
113117
87 => Instruction::PopBlock,
114118
88 => Instruction::EndFinally,
115119
89 => Instruction::PopExcept,
116120
90 => Instruction::StoreName(self.read_argument() as usize),
121+
93 => Instruction::ForIter(self.read_argument() as usize),
117122
100 => Instruction::LoadConst(self.read_argument() as usize),
118123
101 => Instruction::LoadName(self.read_argument() as usize),
119124
106 => Instruction::LoadAttr(self.read_argument() as usize),
120125
107 => Instruction::CompareOp(CmpOperator::from_bytecode(self.read_argument())),
121126
110 => Instruction::JumpForward(self.read_argument() as usize + 2), // +2, because JumpForward takes 3 bytes, and the relative address is computed from the next instruction.
127+
113 => Instruction::JumpAbsolute(self.read_argument() as usize),
122128
114 => Instruction::PopJumpIfFalse(self.read_argument() as usize),
123129
116 => Instruction::LoadGlobal(self.read_argument() as usize),
124130
120 => Instruction::SetupLoop(self.read_argument() as usize + 2),

0 commit comments

Comments
 (0)