Skip to content

Commit 1eab916

Browse files
committed
Initial work on 'with' context manager logic
1 parent 903bf41 commit 1eab916

File tree

7 files changed

+82
-6
lines changed

7 files changed

+82
-6
lines changed

parser/src/ast.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ pub enum Statement {
7474
orelse: Option<Vec<LocatedStatement>>,
7575
},
7676
With {
77-
items: Expression,
77+
items: Vec<WithItem>,
7878
body: Vec<LocatedStatement>,
7979
},
8080
For {
@@ -106,6 +106,12 @@ pub enum Statement {
106106
},
107107
}
108108

109+
#[derive(Debug, PartialEq)]
110+
pub struct WithItem {
111+
pub context_expr: Expression,
112+
pub optional_vars: Option<Expression>,
113+
}
114+
109115
#[derive(Debug, PartialEq, Clone)]
110116
pub enum Expression {
111117
BoolOp {

parser/src/python.lalrpop

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -291,14 +291,28 @@ ExceptClause: ast::ExceptHandler = {
291291
};
292292

293293
WithStatement: ast::LocatedStatement = {
294-
<loc:@L> "with" <t:Test> "as" <_e:Expression> ":" <s:Suite> => {
294+
<loc:@L> "with" <i1:WithItem> <i2:("," WithItem)*> ":" <s:Suite> => {
295+
let mut items = vec![i1];
296+
for item in i2 {
297+
items.push(item.1);
298+
}
295299
ast::LocatedStatement {
296300
location: loc,
297-
node: ast::Statement::With { items: t, body: s },
301+
node: ast::Statement::With { items: items, body: s },
298302
}
299303
},
300304
};
301305

306+
WithItem: ast::WithItem = {
307+
<t:Test> <n:("as" Expression)?> => {
308+
let optional_vars = match n {
309+
Some(val) => Some(val.1),
310+
None => None,
311+
};
312+
ast::WithItem { context_expr: t, optional_vars }
313+
},
314+
};
315+
302316
FuncDef: ast::LocatedStatement = {
303317
<loc:@L> "def" <i:Identifier> <a:Parameters> ":" <s:Suite> => {
304318
ast::LocatedStatement {

tests/snippets/with.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
2+
ls = []
3+
4+
class ContextManager:
5+
def __enter__(self):
6+
print('Entrada')
7+
ls.append(1)
8+
return self
9+
10+
def __exit__(self, exc_type, exc_val, exc_tb):
11+
ls.append(2)
12+
print('Wiedersehen')
13+
14+
def __str__(self):
15+
ls.append(3)
16+
return "c'est moi!"
17+
18+
19+
with ContextManager() as c:
20+
print(c)
21+
22+
assert ls == [1, 3, 2]

vm/src/bytecode.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,9 @@ pub enum Instruction {
118118
SetupExcept {
119119
handler: Label,
120120
},
121+
SetupWith {
122+
end: Label,
123+
},
121124
PopBlock,
122125
Raise {
123126
argc: usize,

vm/src/compile.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,8 +199,21 @@ impl Compiler {
199199
});
200200
self.set_label(end_label);
201201
}
202-
ast::Statement::With { items: _, body: _ } => {
203-
// TODO
202+
ast::Statement::With { items, body } => {
203+
for item in items {
204+
self.compile_expression(&item.context_expr);
205+
match &item.optional_vars {
206+
Some(var) => {
207+
},
208+
None => {
209+
self.emit(Instruction::Pop);
210+
}
211+
}
212+
}
213+
let end_label = self.new_label();
214+
self.emit(Instruction::SetupWith { end: end_label });
215+
self.compile_statements(body);
216+
self.set_label(end_label);
204217
}
205218
ast::Statement::For {
206219
target,

vm/src/frame.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub enum Block {
1616
#[allow(dead_code)]
1717
// TODO: Implement try/except blocks
1818
TryExcept { handler: bytecode::Label },
19+
With { end: bytecode::Label },
1920
}
2021

2122
pub struct Frame {

vm/src/vm.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,12 +175,24 @@ impl VirtualMachine {
175175
self.current_frame().last_block()
176176
}
177177

178+
fn with_exit(&mut self) {
179+
// Assume top of stack is __exit__ method:
180+
let exit_method = self.pop_value();
181+
let args = PyFuncArgs {
182+
args: vec![],
183+
kwargs: vec![],
184+
};
185+
// TODO: what happens when we got an error during handling exception?
186+
self.invoke(exit_method, args).unwrap();
187+
}
188+
178189
fn unwind_loop(&mut self) -> Block {
179190
loop {
180191
let block = self.pop_block();
181192
match block {
182193
Some(Block::Loop { start: _, end: __ }) => break block.unwrap(),
183194
Some(Block::TryExcept { .. }) => {}
195+
Some(Block::With { .. }) => { self.with_exit(); }
184196
None => panic!("No block to break / continue"),
185197
}
186198
}
@@ -196,7 +208,8 @@ impl VirtualMachine {
196208
self.jump(&handler);
197209
return None;
198210
}
199-
Some(_) => {}
211+
Some(Block::With { .. }) => { self.with_exit(); }
212+
Some(Block::Loop { .. }) => {}
200213
None => break,
201214
}
202215
}
@@ -823,6 +836,10 @@ impl VirtualMachine {
823836
self.push_block(Block::TryExcept { handler: *handler });
824837
None
825838
}
839+
bytecode::Instruction::SetupWith { end } => {
840+
self.push_block(Block::With { end: *end });
841+
None
842+
}
826843
bytecode::Instruction::PopBlock => {
827844
self.pop_block();
828845
None

0 commit comments

Comments
 (0)