Skip to content

Commit 8072a76

Browse files
committed
compile: implement try/except/finally
1 parent 54e8dcb commit 8072a76

File tree

3 files changed

+299
-40
lines changed

3 files changed

+299
-40
lines changed

compile/compile.go

Lines changed: 189 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -347,13 +347,6 @@ func (c *compiler) Jump(Op byte, Dest *Label) {
347347
}
348348
}
349349

350-
// Compile statements
351-
func (c *compiler) Stmts(stmts []ast.Stmt) {
352-
for _, stmt := range stmts {
353-
c.Stmt(stmt)
354-
}
355-
}
356-
357350
/* The test for LOCAL must come before the test for FREE in order to
358351
handle classes where name is both local and free. The local var is
359352
a method and the free var is a free var referenced within a method.
@@ -494,9 +487,7 @@ func (c *compiler) compileFunc(compilerScope compilerScopeType, Ast ast.Ast, Arg
494487
code.Kwonlyargcount = int32(len(Args.Kwonlyargs))
495488

496489
// Defaults
497-
for _, expr := range Args.Defaults {
498-
c.Expr(expr)
499-
}
490+
c.Exprs(Args.Defaults)
500491

501492
// KwDefaults
502493
if len(Args.Kwonlyargs) != len(Args.KwDefaults) {
@@ -532,9 +523,7 @@ func (c *compiler) compileFunc(compilerScope compilerScopeType, Ast ast.Ast, Arg
532523
}
533524

534525
// Load decorators onto stack
535-
for _, expr := range DecoratorList {
536-
c.Expr(expr)
537-
}
526+
c.Exprs(DecoratorList)
538527

539528
// Make function or closure, leaving it on the stack
540529
posdefaults := uint32(len(Args.Defaults))
@@ -551,9 +540,7 @@ func (c *compiler) compileFunc(compilerScope compilerScopeType, Ast ast.Ast, Arg
551540
// Compile class definition
552541
func (c *compiler) class(Ast ast.Ast, class *ast.ClassDef) {
553542
// Load decorators onto stack
554-
for _, expr := range class.DecoratorList {
555-
c.Expr(expr)
556-
}
543+
c.Exprs(class.DecoratorList)
557544

558545
/* ultimately generate code for:
559546
<name> = __build_class__(<func>, <name>, *<bases>, **<keywords>)
@@ -643,9 +630,7 @@ func (c *compiler) with(node *ast.With, pos int) {
643630
pos++
644631
if pos == len(node.Items) {
645632
/* BLOCK code */
646-
for _, stmt := range node.Body {
647-
c.Stmt(stmt)
648-
}
633+
c.Stmts(node.Body)
649634
} else {
650635
c.with(node, pos)
651636
}
@@ -664,6 +649,176 @@ func (c *compiler) with(node *ast.With, pos int) {
664649
c.Op(vm.END_FINALLY)
665650
}
666651

652+
/* Code generated for "try: <body> finally: <finalbody>" is as follows:
653+
654+
SETUP_FINALLY L
655+
<code for body>
656+
POP_BLOCK
657+
LOAD_CONST <None>
658+
L: <code for finalbody>
659+
END_FINALLY
660+
661+
The special instructions use the block stack. Each block
662+
stack entry contains the instruction that created it (here
663+
SETUP_FINALLY), the level of the value stack at the time the
664+
block stack entry was created, and a label (here L).
665+
666+
SETUP_FINALLY:
667+
Pushes the current value stack level and the label
668+
onto the block stack.
669+
POP_BLOCK:
670+
Pops en entry from the block stack, and pops the value
671+
stack until its level is the same as indicated on the
672+
block stack. (The label is ignored.)
673+
END_FINALLY:
674+
Pops a variable number of entries from the *value* stack
675+
and re-raises the exception they specify. The number of
676+
entries popped depends on the (pseudo) exception type.
677+
678+
The block stack is unwound when an exception is raised:
679+
when a SETUP_FINALLY entry is found, the exception is pushed
680+
onto the value stack (and the exception condition is cleared),
681+
and the interpreter jumps to the label gotten from the block
682+
stack.
683+
*/
684+
func (c *compiler) tryFinally(node *ast.Try) {
685+
end := new(Label)
686+
c.Jump(vm.SETUP_FINALLY, end)
687+
if len(node.Handlers) > 0 {
688+
c.tryExcept(node)
689+
} else {
690+
c.Stmts(node.Body)
691+
}
692+
c.Op(vm.POP_BLOCK)
693+
c.LoadConst(py.None)
694+
c.Label(end)
695+
c.Stmts(node.Finalbody)
696+
c.Op(vm.END_FINALLY)
697+
}
698+
699+
/*
700+
Code generated for "try: S except E1 as V1: S1 except E2 as V2: S2 ...":
701+
(The contents of the value stack is shown in [], with the top
702+
at the right; 'tb' is trace-back info, 'val' the exception's
703+
associated value, and 'exc' the exception.)
704+
705+
Value stack Label Instruction Argument
706+
[] SETUP_EXCEPT L1
707+
[] <code for S>
708+
[] POP_BLOCK
709+
[] JUMP_FORWARD L0
710+
711+
[tb, val, exc] L1: DUP )
712+
[tb, val, exc, exc] <evaluate E1> )
713+
[tb, val, exc, exc, E1] COMPARE_OP EXC_MATCH ) only if E1
714+
[tb, val, exc, 1-or-0] POP_JUMP_IF_FALSE L2 )
715+
[tb, val, exc] POP
716+
[tb, val] <assign to V1> (or POP if no V1)
717+
[tb] POP
718+
[] <code for S1>
719+
JUMP_FORWARD L0
720+
721+
[tb, val, exc] L2: DUP
722+
.............................etc.......................
723+
724+
[tb, val, exc] Ln+1: END_FINALLY # re-raise exception
725+
726+
[] L0: <next statement>
727+
728+
Of course, parts are not generated if Vi or Ei is not present.
729+
*/
730+
func (c *compiler) tryExcept(node *ast.Try) {
731+
except := new(Label)
732+
orelse := new(Label)
733+
end := new(Label)
734+
c.Jump(vm.SETUP_EXCEPT, except)
735+
c.Stmts(node.Body)
736+
c.Op(vm.POP_BLOCK)
737+
c.Jump(vm.JUMP_FORWARD, orelse)
738+
n := len(node.Handlers)
739+
c.Label(except)
740+
for i, handler := range node.Handlers {
741+
if handler.ExprType == nil && i < n-1 {
742+
panic(py.ExceptionNewf(py.SyntaxError, "default 'except:' must be last"))
743+
}
744+
// FIXME c.u.u_lineno_set = 0
745+
// c.u.u_lineno = handler.lineno
746+
// c.u.u_col_offset = handler.col_offset
747+
except := new(Label)
748+
if handler.ExprType != nil {
749+
c.Op(vm.DUP_TOP)
750+
c.Expr(handler.ExprType)
751+
c.OpArg(vm.COMPARE_OP, vm.PyCmp_EXC_MATCH)
752+
c.Jump(vm.POP_JUMP_IF_FALSE, except)
753+
}
754+
c.Op(vm.POP_TOP)
755+
if handler.Name != "" {
756+
cleanup_end := new(Label)
757+
c.NameOp(string(handler.Name), ast.Store)
758+
c.Op(vm.POP_TOP)
759+
760+
/*
761+
try:
762+
# body
763+
except type as name:
764+
try:
765+
# body
766+
finally:
767+
name = None
768+
del name
769+
*/
770+
771+
/* second try: */
772+
c.Jump(vm.SETUP_FINALLY, cleanup_end)
773+
774+
/* second # body */
775+
c.Stmts(handler.Body)
776+
c.Op(vm.POP_BLOCK)
777+
c.Op(vm.POP_EXCEPT)
778+
779+
/* finally: */
780+
c.LoadConst(py.None)
781+
c.Label(cleanup_end)
782+
783+
/* name = None */
784+
c.LoadConst(py.None)
785+
c.NameOp(string(handler.Name), ast.Store)
786+
787+
/* del name */
788+
c.NameOp(string(handler.Name), ast.Del)
789+
790+
c.Op(vm.END_FINALLY)
791+
} else {
792+
c.Op(vm.POP_TOP)
793+
c.Op(vm.POP_TOP)
794+
c.Stmts(handler.Body)
795+
c.Op(vm.POP_EXCEPT)
796+
}
797+
c.Jump(vm.JUMP_FORWARD, end)
798+
c.Label(except)
799+
}
800+
c.Op(vm.END_FINALLY)
801+
c.Label(orelse)
802+
c.Stmts(node.Orelse)
803+
c.Label(end)
804+
}
805+
806+
// Compile a try statement
807+
func (c *compiler) try(node *ast.Try) {
808+
if len(node.Finalbody) > 0 {
809+
c.tryFinally(node)
810+
} else {
811+
c.tryExcept(node)
812+
}
813+
}
814+
815+
// Compile statements
816+
func (c *compiler) Stmts(stmts []ast.Stmt) {
817+
for _, stmt := range stmts {
818+
c.Stmt(stmt)
819+
}
820+
}
821+
667822
// Compile statement
668823
func (c *compiler) Stmt(stmt ast.Stmt) {
669824
switch node := stmt.(type) {
@@ -766,16 +921,12 @@ func (c *compiler) Stmt(stmt ast.Stmt) {
766921
c.loops.Push(loop{Start: forloop, End: endpopblock, IsForLoop: true})
767922
c.Jump(vm.FOR_ITER, endfor)
768923
c.Expr(node.Target)
769-
for _, stmt := range node.Body {
770-
c.Stmt(stmt)
771-
}
924+
c.Stmts(node.Body)
772925
c.Jump(vm.JUMP_ABSOLUTE, forloop)
773926
c.Label(endfor)
774927
c.Op(vm.POP_BLOCK)
775928
c.loops.Pop()
776-
for _, stmt := range node.Orelse {
777-
c.Stmt(stmt)
778-
}
929+
c.Stmts(node.Orelse)
779930
c.Label(endpopblock)
780931
case *ast.While:
781932
// Test Expr
@@ -788,16 +939,12 @@ func (c *compiler) Stmt(stmt ast.Stmt) {
788939
c.loops.Push(loop{Start: while, End: endpopblock})
789940
c.Expr(node.Test)
790941
c.Jump(vm.POP_JUMP_IF_FALSE, endwhile)
791-
for _, stmt := range node.Body {
792-
c.Stmt(stmt)
793-
}
942+
c.Stmts(node.Body)
794943
c.Jump(vm.JUMP_ABSOLUTE, while)
795944
c.Label(endwhile)
796945
c.Op(vm.POP_BLOCK)
797946
c.loops.Pop()
798-
for _, stmt := range node.Orelse {
799-
c.Stmt(stmt)
800-
}
947+
c.Stmts(node.Orelse)
801948
c.Label(endpopblock)
802949
case *ast.If:
803950
// Test Expr
@@ -807,17 +954,13 @@ func (c *compiler) Stmt(stmt ast.Stmt) {
807954
endif := new(Label)
808955
c.Expr(node.Test)
809956
c.Jump(vm.POP_JUMP_IF_FALSE, orelse)
810-
for _, stmt := range node.Body {
811-
c.Stmt(stmt)
812-
}
957+
c.Stmts(node.Body)
813958
// FIXME this puts a JUMP_FORWARD in when not
814959
// necessary (when no Orelse statements) but it
815960
// matches python3.4 (this is fixed in py3.5)
816961
c.Jump(vm.JUMP_FORWARD, endif)
817962
c.Label(orelse)
818-
for _, stmt := range node.Orelse {
819-
c.Stmt(stmt)
820-
}
963+
c.Stmts(node.Orelse)
821964
c.Label(endif)
822965
case *ast.With:
823966
// Items []*WithItem
@@ -841,7 +984,7 @@ func (c *compiler) Stmt(stmt ast.Stmt) {
841984
// Handlers []*ExceptHandler
842985
// Orelse []Stmt
843986
// Finalbody []Stmt
844-
panic("FIXME compile: Try not implemented")
987+
c.try(node)
845988
case *ast.Assert:
846989
// Test Expr
847990
// Msg Expr
@@ -913,7 +1056,7 @@ func (c *compiler) NameOp(name string, ctx ast.ExprContext) {
9131056
// PyObject *mangled;
9141057
/* XXX AugStore isn't used anywhere! */
9151058

916-
// FIXME mangled = _Py_Mangle(c->u->u_private, name);
1059+
// FIXME mangled = _Py_Mangle(c.u.u_private, name);
9171060
mangled := name
9181061

9191062
if name == "None" || name == "True" || name == "False" {
@@ -1143,6 +1286,13 @@ func (c *compiler) comprehension(expr ast.Expr, generators []ast.Comprehension)
11431286
}
11441287

11451288
// Compile expressions
1289+
func (c *compiler) Exprs(exprs []ast.Expr) {
1290+
for _, expr := range exprs {
1291+
c.Expr(expr)
1292+
}
1293+
}
1294+
1295+
// Compile and expression
11461296
func (c *compiler) Expr(expr ast.Expr) {
11471297
switch node := expr.(type) {
11481298
case *ast.BoolOp:

compile/compile_data_test.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3483,4 +3483,73 @@ var compileTestData = []struct {
34833483
Firstlineno: 1,
34843484
Lnotab: "\x0c\x01\x0c\x01",
34853485
}, nil, ""},
3486+
{"try:\n f()\nexcept Exception:\n h()\n", "exec", &py.Code{
3487+
Argcount: 0,
3488+
Kwonlyargcount: 0,
3489+
Nlocals: 0,
3490+
Stacksize: 11,
3491+
Flags: 64,
3492+
Code: "\x79\x0b\x00\x65\x00\x00\x83\x00\x00\x01\x57\x6e\x19\x00\x04\x65\x01\x00\x6b\x0a\x00\x72\x26\x00\x01\x01\x01\x65\x02\x00\x83\x00\x00\x01\x59\x6e\x01\x00\x58\x64\x00\x00\x53",
3493+
Consts: []py.Object{py.None},
3494+
Names: []string{"f", "Exception", "h"},
3495+
Varnames: []string{},
3496+
Freevars: []string{},
3497+
Cellvars: []string{},
3498+
Filename: "<string>",
3499+
Name: "<module>",
3500+
Firstlineno: 1,
3501+
Lnotab: "\x03\x01\x0b\x01\x0d\x01",
3502+
}, nil, ""},
3503+
{"try:\n f()\nexcept Exception as e:\n h(e)\nexcept (Exception1, Exception2) as e:\n i(e)\nexcept:\n j()\nelse:\n potato()\n", "exec", &py.Code{
3504+
Argcount: 0,
3505+
Kwonlyargcount: 0,
3506+
Nlocals: 0,
3507+
Stacksize: 16,
3508+
Flags: 64,
3509+
Code: "\x79\x0b\x00\x65\x00\x00\x83\x00\x00\x01\x57\x6e\x71\x00\x04\x65\x01\x00\x6b\x0a\x00\x72\x3c\x00\x01\x5a\x02\x00\x01\x7a\x0f\x00\x65\x03\x00\x65\x02\x00\x83\x01\x00\x01\x57\x59\x64\x00\x00\x64\x00\x00\x5a\x02\x00\x5b\x02\x00\x58\x6e\x4a\x00\x04\x65\x04\x00\x65\x05\x00\x66\x02\x00\x6b\x0a\x00\x72\x70\x00\x01\x5a\x02\x00\x01\x7a\x0f\x00\x65\x06\x00\x65\x02\x00\x83\x01\x00\x01\x57\x59\x64\x00\x00\x64\x00\x00\x5a\x02\x00\x5b\x02\x00\x58\x6e\x16\x00\x01\x01\x01\x65\x07\x00\x83\x00\x00\x01\x59\x6e\x08\x00\x58\x65\x08\x00\x83\x00\x00\x01\x64\x00\x00\x53",
3510+
Consts: []py.Object{py.None},
3511+
Names: []string{"f", "Exception", "e", "h", "Exception1", "Exception2", "i", "j", "potato"},
3512+
Varnames: []string{},
3513+
Freevars: []string{},
3514+
Cellvars: []string{},
3515+
Filename: "<string>",
3516+
Name: "<module>",
3517+
Firstlineno: 1,
3518+
Lnotab: "\x03\x01\x0b\x01\x12\x01\x1c\x01\x18\x01\x1c\x01\x03\x01\x0c\x02",
3519+
}, nil, ""},
3520+
{"try:\n f()\nexcept:\n j()\nexcept Exception as e:\n h(e)\n ", "exec", nil, py.SyntaxError, "default 'except:' must be last"},
3521+
{"try:\n f()\nfinally:\n j()\n ", "exec", &py.Code{
3522+
Argcount: 0,
3523+
Kwonlyargcount: 0,
3524+
Nlocals: 0,
3525+
Stacksize: 10,
3526+
Flags: 64,
3527+
Code: "\x7a\x0b\x00\x65\x00\x00\x83\x00\x00\x01\x57\x64\x00\x00\x65\x01\x00\x83\x00\x00\x01\x58\x64\x00\x00\x53",
3528+
Consts: []py.Object{py.None},
3529+
Names: []string{"f", "j"},
3530+
Varnames: []string{},
3531+
Freevars: []string{},
3532+
Cellvars: []string{},
3533+
Filename: "<string>",
3534+
Name: "<module>",
3535+
Firstlineno: 1,
3536+
Lnotab: "\x03\x01\x0b\x02",
3537+
}, nil, ""},
3538+
{"try:\n f()\nexcept Exception as e:\n h(e)\nfinally:\n j()\n ", "exec", &py.Code{
3539+
Argcount: 0,
3540+
Kwonlyargcount: 0,
3541+
Nlocals: 0,
3542+
Stacksize: 22,
3543+
Flags: 64,
3544+
Code: "\x7a\x41\x00\x79\x0b\x00\x65\x00\x00\x83\x00\x00\x01\x57\x6e\x2f\x00\x04\x65\x01\x00\x6b\x0a\x00\x72\x3f\x00\x01\x5a\x02\x00\x01\x7a\x0f\x00\x65\x03\x00\x65\x02\x00\x83\x01\x00\x01\x57\x59\x64\x00\x00\x64\x00\x00\x5a\x02\x00\x5b\x02\x00\x58\x6e\x01\x00\x58\x57\x64\x00\x00\x65\x04\x00\x83\x00\x00\x01\x58\x64\x00\x00\x53",
3545+
Consts: []py.Object{py.None},
3546+
Names: []string{"f", "Exception", "e", "h", "j"},
3547+
Varnames: []string{},
3548+
Freevars: []string{},
3549+
Cellvars: []string{},
3550+
Filename: "<string>",
3551+
Name: "<module>",
3552+
Firstlineno: 1,
3553+
Lnotab: "\x06\x01\x0b\x01\x12\x01\x21\x02",
3554+
}, nil, ""},
34863555
}

0 commit comments

Comments
 (0)