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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy