Skip to content

Commit 54e8dcb

Browse files
committed
compile: with statement
1 parent ebc723a commit 54e8dcb

File tree

3 files changed

+150
-1
lines changed

3 files changed

+150
-1
lines changed

compile/compile.go

+64-1
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,69 @@ func (c *compiler) class(Ast ast.Ast, class *ast.ClassDef) {
601601
c.NameOp(string(class.Name), ast.Store)
602602
}
603603

604+
/*
605+
Implements the with statement from PEP 343.
606+
607+
The semantics outlined in that PEP are as follows:
608+
609+
with EXPR as VAR:
610+
BLOCK
611+
612+
It is implemented roughly as:
613+
614+
context = EXPR
615+
exit = context.__exit__ # not calling it
616+
value = context.__enter__()
617+
try:
618+
VAR = value # if VAR present in the syntax
619+
BLOCK
620+
finally:
621+
if an exception was raised:
622+
exc = copy of (exception, instance, traceback)
623+
else:
624+
exc = (None, None, None)
625+
exit(*exc)
626+
*/
627+
func (c *compiler) with(node *ast.With, pos int) {
628+
item := node.Items[pos]
629+
finally := new(Label)
630+
631+
/* Evaluate EXPR */
632+
c.Expr(item.ContextExpr)
633+
c.Jump(vm.SETUP_WITH, finally)
634+
635+
/* SETUP_WITH pushes a finally block. */
636+
if item.OptionalVars != nil {
637+
c.Expr(item.OptionalVars)
638+
} else {
639+
/* Discard result from context.__enter__() */
640+
c.Op(vm.POP_TOP)
641+
}
642+
643+
pos++
644+
if pos == len(node.Items) {
645+
/* BLOCK code */
646+
for _, stmt := range node.Body {
647+
c.Stmt(stmt)
648+
}
649+
} else {
650+
c.with(node, pos)
651+
}
652+
653+
/* End of try block; start the finally block */
654+
c.Op(vm.POP_BLOCK)
655+
c.LoadConst(py.None)
656+
657+
/* Finally block starts; context.__exit__ is on the stack under
658+
the exception or return information. Just issue our magic
659+
opcode. */
660+
c.Label(finally)
661+
c.Op(vm.WITH_CLEANUP)
662+
663+
/* Finally block ends. */
664+
c.Op(vm.END_FINALLY)
665+
}
666+
604667
// Compile statement
605668
func (c *compiler) Stmt(stmt ast.Stmt) {
606669
switch node := stmt.(type) {
@@ -759,7 +822,7 @@ func (c *compiler) Stmt(stmt ast.Stmt) {
759822
case *ast.With:
760823
// Items []*WithItem
761824
// Body []Stmt
762-
panic("FIXME compile: With not implemented")
825+
c.with(node, 0)
763826
case *ast.Raise:
764827
// Exc Expr
765828
// Cause Expr

compile/compile_data_test.go

+68
Original file line numberDiff line numberDiff line change
@@ -3415,4 +3415,72 @@ var compileTestData = []struct {
34153415
Firstlineno: 1,
34163416
Lnotab: "",
34173417
}, nil, ""},
3418+
{"with a:\n f()\n", "exec", &py.Code{
3419+
Argcount: 0,
3420+
Kwonlyargcount: 0,
3421+
Nlocals: 0,
3422+
Stacksize: 8,
3423+
Flags: 64,
3424+
Code: "\x65\x00\x00\x8f\x0c\x00\x01\x65\x01\x00\x83\x00\x00\x01\x57\x64\x00\x00\x51\x58\x64\x00\x00\x53",
3425+
Consts: []py.Object{py.None},
3426+
Names: []string{"a", "f"},
3427+
Varnames: []string{},
3428+
Freevars: []string{},
3429+
Cellvars: []string{},
3430+
Filename: "<string>",
3431+
Name: "<module>",
3432+
Firstlineno: 1,
3433+
Lnotab: "\x07\x01",
3434+
}, nil, ""},
3435+
{"with a() as b:\n f(b)\n", "exec", &py.Code{
3436+
Argcount: 0,
3437+
Kwonlyargcount: 0,
3438+
Nlocals: 0,
3439+
Stacksize: 9,
3440+
Flags: 64,
3441+
Code: "\x65\x00\x00\x83\x00\x00\x8f\x11\x00\x5a\x01\x00\x65\x02\x00\x65\x01\x00\x83\x01\x00\x01\x57\x64\x00\x00\x51\x58\x64\x00\x00\x53",
3442+
Consts: []py.Object{py.None},
3443+
Names: []string{"a", "b", "f"},
3444+
Varnames: []string{},
3445+
Freevars: []string{},
3446+
Cellvars: []string{},
3447+
Filename: "<string>",
3448+
Name: "<module>",
3449+
Firstlineno: 1,
3450+
Lnotab: "\x0c\x01",
3451+
}, nil, ""},
3452+
{"with A() as a, B() as b:\n f(a,b)\n", "exec", &py.Code{
3453+
Argcount: 0,
3454+
Kwonlyargcount: 0,
3455+
Nlocals: 0,
3456+
Stacksize: 17,
3457+
Flags: 64,
3458+
Code: "\x65\x00\x00\x83\x00\x00\x8f\x26\x00\x5a\x01\x00\x65\x02\x00\x83\x00\x00\x8f\x14\x00\x5a\x03\x00\x65\x04\x00\x65\x01\x00\x65\x03\x00\x83\x02\x00\x01\x57\x64\x00\x00\x51\x58\x57\x64\x00\x00\x51\x58\x64\x00\x00\x53",
3459+
Consts: []py.Object{py.None},
3460+
Names: []string{"A", "a", "B", "b", "f"},
3461+
Varnames: []string{},
3462+
Freevars: []string{},
3463+
Cellvars: []string{},
3464+
Filename: "<string>",
3465+
Name: "<module>",
3466+
Firstlineno: 1,
3467+
Lnotab: "\x18\x01",
3468+
}, nil, ""},
3469+
{"with A() as a:\n with B() as b:\n f(a,b)\n", "exec", &py.Code{
3470+
Argcount: 0,
3471+
Kwonlyargcount: 0,
3472+
Nlocals: 0,
3473+
Stacksize: 17,
3474+
Flags: 64,
3475+
Code: "\x65\x00\x00\x83\x00\x00\x8f\x26\x00\x5a\x01\x00\x65\x02\x00\x83\x00\x00\x8f\x14\x00\x5a\x03\x00\x65\x04\x00\x65\x01\x00\x65\x03\x00\x83\x02\x00\x01\x57\x64\x00\x00\x51\x58\x57\x64\x00\x00\x51\x58\x64\x00\x00\x53",
3476+
Consts: []py.Object{py.None},
3477+
Names: []string{"A", "a", "B", "b", "f"},
3478+
Varnames: []string{},
3479+
Freevars: []string{},
3480+
Cellvars: []string{},
3481+
Filename: "<string>",
3482+
Name: "<module>",
3483+
Firstlineno: 1,
3484+
Lnotab: "\x0c\x01\x0c\x01",
3485+
}, nil, ""},
34183486
}

compile/make_compile_test.py

+18
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,24 @@ def method(self, c):
274274
('''{ (x,y,z) for x in xs for y in ys if a if b for z in zs if c if d }''', "eval"),
275275
('''{ x:(y,z) for x in xs for y in ys for z in zs }''', "eval"),
276276
('''( (x,y,z) for x in xs for y in ys if a if b for z in zs if c if d )''', "eval"),
277+
# with
278+
('''\
279+
with a:
280+
f()
281+
''', "exec"),
282+
('''\
283+
with a() as b:
284+
f(b)
285+
''', "exec"),
286+
('''\
287+
with A() as a, B() as b:
288+
f(a,b)
289+
''', "exec"),
290+
('''\
291+
with A() as a:
292+
with B() as b:
293+
f(a,b)
294+
''', "exec"),
277295

278296

279297
]

0 commit comments

Comments
 (0)