Skip to content

Commit 08f7c83

Browse files
committed
compile: Fix continue and loops in general
1 parent 3965a79 commit 08f7c83

File tree

3 files changed

+214
-13
lines changed

3 files changed

+214
-13
lines changed

compile/compile.go

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,21 @@ import (
1414
"github.com/ncw/gpython/vm"
1515
)
1616

17-
// Loop
17+
type loopType byte
18+
19+
// type of loop
20+
const (
21+
loopLoop loopType = iota
22+
exceptLoop
23+
finallyTryLoop
24+
finallyEndLoop
25+
)
26+
27+
// Loop - used to track loops, try/except and try/finally
1828
type loop struct {
19-
Start *Label
20-
End *Label
21-
IsForLoop bool
29+
Start *Label
30+
End *Label
31+
Type loopType
2232
}
2333

2434
// Loopstack
@@ -697,12 +707,16 @@ func (c *compiler) tryFinally(node *ast.Try) {
697707
if len(node.Handlers) > 0 {
698708
c.tryExcept(node)
699709
} else {
710+
c.loops.Push(loop{Type: finallyTryLoop})
700711
c.Stmts(node.Body)
712+
c.loops.Pop()
701713
}
702714
c.Op(vm.POP_BLOCK)
703715
c.LoadConst(py.None)
704716
c.Label(end)
717+
c.loops.Push(loop{Type: finallyEndLoop})
705718
c.Stmts(node.Finalbody)
719+
c.loops.Pop()
706720
c.Op(vm.END_FINALLY)
707721
}
708722

@@ -738,6 +752,7 @@ func (c *compiler) tryFinally(node *ast.Try) {
738752
Of course, parts are not generated if Vi or Ei is not present.
739753
*/
740754
func (c *compiler) tryExcept(node *ast.Try) {
755+
c.loops.Push(loop{Type: exceptLoop})
741756
except := new(Label)
742757
orelse := new(Label)
743758
end := new(Label)
@@ -811,6 +826,7 @@ func (c *compiler) tryExcept(node *ast.Try) {
811826
c.Label(orelse)
812827
c.Stmts(node.Orelse)
813828
c.Label(end)
829+
c.loops.Pop()
814830
}
815831

816832
// Compile a try statement
@@ -1006,7 +1022,7 @@ func (c *compiler) Stmt(stmt ast.Stmt) {
10061022
c.Expr(node.Iter)
10071023
c.Op(vm.GET_ITER)
10081024
forloop := c.NewLabel()
1009-
c.loops.Push(loop{Start: forloop, End: endpopblock, IsForLoop: true})
1025+
c.loops.Push(loop{Start: forloop, End: endpopblock, Type: loopLoop})
10101026
c.Jump(vm.FOR_ITER, endfor)
10111027
c.Expr(node.Target)
10121028
c.Stmts(node.Body)
@@ -1024,7 +1040,7 @@ func (c *compiler) Stmt(stmt ast.Stmt) {
10241040
endpopblock := new(Label)
10251041
c.Jump(vm.SETUP_LOOP, endpopblock)
10261042
while := c.NewLabel()
1027-
c.loops.Push(loop{Start: while, End: endpopblock})
1043+
c.loops.Push(loop{Start: while, End: endpopblock, Type: loopLoop})
10281044
c.Expr(node.Test)
10291045
c.Jump(vm.POP_JUMP_IF_FALSE, endwhile)
10301046
c.Stmts(node.Body)
@@ -1113,16 +1129,35 @@ func (c *compiler) Stmt(stmt ast.Stmt) {
11131129
}
11141130
c.Op(vm.BREAK_LOOP)
11151131
case *ast.Continue:
1132+
const loopError = "'continue' not properly in loop"
1133+
const inFinallyError = "'continue' not supported inside 'finally' clause"
11161134
l := c.loops.Top()
11171135
if l == nil {
1118-
panic(py.ExceptionNewf(py.SyntaxError, "'continue' not properly in loop"))
1136+
panic(py.ExceptionNewf(py.SyntaxError, loopError))
11191137
}
1120-
if l.IsForLoop {
1121-
// FIXME when do we use CONTINUE_LOOP? - need to port the code from compile.c
1122-
c.Jump(vm.JUMP_ABSOLUTE, l.Start)
1123-
//c.Jump(vm.CONTINUE_LOOP, l.Start)
1124-
} else {
1138+
switch l.Type {
1139+
case loopLoop:
11251140
c.Jump(vm.JUMP_ABSOLUTE, l.Start)
1141+
case exceptLoop, finallyTryLoop:
1142+
i := len(c.loops) - 2 // next loop out
1143+
for ; i >= 0; i-- {
1144+
l = &c.loops[i]
1145+
if l.Type == loopLoop {
1146+
break
1147+
}
1148+
// Prevent continue anywhere under a finally even if hidden in a sub-try or except.
1149+
if l.Type == finallyEndLoop {
1150+
panic(py.ExceptionNewf(py.SyntaxError, inFinallyError))
1151+
}
1152+
}
1153+
if i == -1 {
1154+
panic(py.ExceptionNewf(py.SyntaxError, loopError))
1155+
}
1156+
c.Jump(vm.CONTINUE_LOOP, l.Start)
1157+
case finallyEndLoop:
1158+
panic(py.ExceptionNewf(py.SyntaxError, inFinallyError))
1159+
default:
1160+
panic("unknown loop type")
11261161
}
11271162
default:
11281163
panic(py.ExceptionNewf(py.SyntaxError, "Unknown StmtBase: %v", stmt))

compile/compile_data_test.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5324,4 +5324,80 @@ var compileTestData = []struct {
53245324
Firstlineno: 1,
53255325
Lnotab: "",
53265326
}, nil, ""},
5327+
{"try:\n continue\nexcept:\n pass\n ", "exec", nil, py.SyntaxError, "'continue' not properly in loop"},
5328+
{"try:\n pass\nexcept:\n continue\n ", "exec", nil, py.SyntaxError, "'continue' not properly in loop"},
5329+
{"for x in xs:\n try:\n f()\n except:\n continue\n f()\n ", "exec", &py.Code{
5330+
Argcount: 0,
5331+
Kwonlyargcount: 0,
5332+
Nlocals: 0,
5333+
Stacksize: 10,
5334+
Flags: 64,
5335+
Code: "\x78\x2e\x00\x65\x00\x00\x44\x5d\x26\x00\x5a\x01\x00\x79\x0b\x00\x65\x02\x00\x83\x00\x00\x01\x57\x6e\x0b\x00\x01\x01\x01\x77\x07\x00\x59\x6e\x01\x00\x58\x65\x02\x00\x83\x00\x00\x01\x71\x07\x00\x57\x64\x00\x00\x53",
5336+
Consts: []py.Object{py.None},
5337+
Names: []string{"xs", "x", "f"},
5338+
Varnames: []string{},
5339+
Freevars: []string{},
5340+
Cellvars: []string{},
5341+
Filename: "<string>",
5342+
Name: "<module>",
5343+
Firstlineno: 1,
5344+
Lnotab: "\x0d\x01\x03\x01\x0b\x01\x03\x01\x08\x01",
5345+
}, nil, ""},
5346+
{"for x in xs:\n try:\n f()\n continue\n finally:\n f()\n ", "exec", &py.Code{
5347+
Argcount: 0,
5348+
Kwonlyargcount: 0,
5349+
Nlocals: 0,
5350+
Stacksize: 11,
5351+
Flags: 64,
5352+
Code: "\x78\x27\x00\x65\x00\x00\x44\x5d\x1f\x00\x5a\x01\x00\x7a\x0e\x00\x65\x02\x00\x83\x00\x00\x01\x77\x07\x00\x57\x64\x00\x00\x65\x02\x00\x83\x00\x00\x01\x58\x71\x07\x00\x57\x64\x00\x00\x53",
5353+
Consts: []py.Object{py.None},
5354+
Names: []string{"xs", "x", "f"},
5355+
Varnames: []string{},
5356+
Freevars: []string{},
5357+
Cellvars: []string{},
5358+
Filename: "<string>",
5359+
Name: "<module>",
5360+
Firstlineno: 1,
5361+
Lnotab: "\x0d\x01\x03\x01\x07\x01\x07\x02",
5362+
}, nil, ""},
5363+
{"for x in xs:\n try:\n f()\n finally:\n continue\n ", "exec", nil, py.SyntaxError, "'continue' not supported inside 'finally' clause"},
5364+
{"for x in xs:\n try:\n f()\n finally:\n try:\n continue\n except:\n pass\n ", "exec", nil, py.SyntaxError, "'continue' not supported inside 'finally' clause"},
5365+
{"try:\n continue\nexcept:\n pass\n ", "exec", nil, py.SyntaxError, "'continue' not properly in loop"},
5366+
{"try:\n pass\nexcept:\n continue\n ", "exec", nil, py.SyntaxError, "'continue' not properly in loop"},
5367+
{"while truth():\n try:\n f()\n except:\n continue\n f()\n ", "exec", &py.Code{
5368+
Argcount: 0,
5369+
Kwonlyargcount: 0,
5370+
Nlocals: 0,
5371+
Stacksize: 9,
5372+
Flags: 64,
5373+
Code: "\x78\x2d\x00\x65\x00\x00\x83\x00\x00\x72\x2f\x00\x79\x0b\x00\x65\x01\x00\x83\x00\x00\x01\x57\x6e\x0b\x00\x01\x01\x01\x77\x03\x00\x59\x6e\x01\x00\x58\x65\x01\x00\x83\x00\x00\x01\x71\x03\x00\x57\x64\x00\x00\x53",
5374+
Consts: []py.Object{py.None},
5375+
Names: []string{"truth", "f"},
5376+
Varnames: []string{},
5377+
Freevars: []string{},
5378+
Cellvars: []string{},
5379+
Filename: "<string>",
5380+
Name: "<module>",
5381+
Firstlineno: 1,
5382+
Lnotab: "\x0c\x01\x03\x01\x0b\x01\x03\x01\x08\x01",
5383+
}, nil, ""},
5384+
{"while truth():\n try:\n f()\n continue\n finally:\n f()\n ", "exec", &py.Code{
5385+
Argcount: 0,
5386+
Kwonlyargcount: 0,
5387+
Nlocals: 0,
5388+
Stacksize: 10,
5389+
Flags: 64,
5390+
Code: "\x78\x26\x00\x65\x00\x00\x83\x00\x00\x72\x28\x00\x7a\x0e\x00\x65\x01\x00\x83\x00\x00\x01\x77\x03\x00\x57\x64\x00\x00\x65\x01\x00\x83\x00\x00\x01\x58\x71\x03\x00\x57\x64\x00\x00\x53",
5391+
Consts: []py.Object{py.None},
5392+
Names: []string{"truth", "f"},
5393+
Varnames: []string{},
5394+
Freevars: []string{},
5395+
Cellvars: []string{},
5396+
Filename: "<string>",
5397+
Name: "<module>",
5398+
Firstlineno: 1,
5399+
Lnotab: "\x0c\x01\x03\x01\x07\x01\x07\x02",
5400+
}, nil, ""},
5401+
{"while truth():\n try:\n f()\n finally:\n continue\n ", "exec", nil, py.SyntaxError, "'continue' not supported inside 'finally' clause"},
5402+
{"while truth():\n try:\n f()\n finally:\n try:\n continue\n except:\n pass\n ", "exec", nil, py.SyntaxError, "'continue' not supported inside 'finally' clause"},
53275403
}

compile/make_compile_test.py

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,97 @@ def f():
455455
("del x[a, b, c]", "exec"),
456456
("del x[a, b:c, ::d]", "exec"),
457457
("del x[0, 1:2, ::5, ...]", "exec"),
458-
458+
# continue
459+
('''\
460+
try:
461+
continue
462+
except:
463+
pass
464+
''', "exec", SyntaxError),
465+
('''\
466+
try:
467+
pass
468+
except:
469+
continue
470+
''', "exec", SyntaxError),
471+
('''\
472+
for x in xs:
473+
try:
474+
f()
475+
except:
476+
continue
477+
f()
478+
''', "exec"),
479+
('''\
480+
for x in xs:
481+
try:
482+
f()
483+
continue
484+
finally:
485+
f()
486+
''', "exec"),
487+
('''\
488+
for x in xs:
489+
try:
490+
f()
491+
finally:
492+
continue
493+
''', "exec", SyntaxError),
494+
('''\
495+
for x in xs:
496+
try:
497+
f()
498+
finally:
499+
try:
500+
continue
501+
except:
502+
pass
503+
''', "exec", SyntaxError),
504+
('''\
505+
try:
506+
continue
507+
except:
508+
pass
509+
''', "exec", SyntaxError),
510+
('''\
511+
try:
512+
pass
513+
except:
514+
continue
515+
''', "exec", SyntaxError),
516+
('''\
517+
while truth():
518+
try:
519+
f()
520+
except:
521+
continue
522+
f()
523+
''', "exec"),
524+
('''\
525+
while truth():
526+
try:
527+
f()
528+
continue
529+
finally:
530+
f()
531+
''', "exec"),
532+
('''\
533+
while truth():
534+
try:
535+
f()
536+
finally:
537+
continue
538+
''', "exec", SyntaxError),
539+
('''\
540+
while truth():
541+
try:
542+
f()
543+
finally:
544+
try:
545+
continue
546+
except:
547+
pass
548+
''', "exec", SyntaxError),
459549
]
460550

461551
def string(s):

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