Let an unbroken "while true" terminate a block
diff --git a/lang/ast/ast.go b/lang/ast/ast.go
index 363c9a7..78a7819 100644
--- a/lang/ast/ast.go
+++ b/lang/ast/ast.go
@@ -593,6 +593,11 @@
func (n *While) SetHasBreak() { n.flags |= FlagsHasBreak }
func (n *While) SetHasContinue() { n.flags |= FlagsHasContinue }
+func (n *While) IsWhileTrue() bool {
+ condition := n.mhs.AsExpr()
+ return (condition.Operator() == 0) && (condition.Ident() == t.IDTrue)
+}
+
func NewWhile(label t.ID, condition *Expr, asserts []*Node) *While {
return &While{
kind: KWhile,
@@ -1013,10 +1018,9 @@
// Terminates returns whether a block of statements terminates. In other words,
// whether the block is non-empty and its final statement is a "return",
-// "break", "continue" or an "if-else" chain where all branches terminate.
+// "break", "continue", a "while true" that doesn't break or an "if-else" chain
+// where all branches terminate.
func Terminates(body []*Node) bool {
- // TODO: strengthen this to include "while" statements? For inspiration,
- // the Go spec has https://golang.org/ref/spec#Terminating_statements
if len(body) == 0 {
return false
}
@@ -1041,6 +1045,9 @@
return true
case KRet:
return n.AsRet().Keyword() == t.IDReturn
+ case KWhile:
+ n := n.AsWhile()
+ return n.IsWhileTrue() && !n.HasBreak()
}
return false
}
diff --git a/lang/parse/parse.go b/lang/parse/parse.go
index ad1a227..994dd22 100644
--- a/lang/parse/parse.go
+++ b/lang/parse/parse.go
@@ -790,17 +790,17 @@
if err != nil {
return nil, err
}
- doubleCurly := p.peek1() == t.IDOpenDoubleCurly
- if doubleCurly && ((condition.Operator() != 0) || (condition.Ident() != t.IDTrue)) {
- return nil, fmt.Errorf(`parse: double {{ }} while loop condition isn't "true" at %s:%d`,
- p.filename, p.line())
- }
n := a.NewWhile(label, condition, asserts)
if !p.loops.Push(n) {
return nil, fmt.Errorf(`parse: duplicate loop label %s at %s:%d`,
label.Str(p.tm), p.filename, p.line())
}
+ doubleCurly := p.peek1() == t.IDOpenDoubleCurly
+ if doubleCurly && !n.IsWhileTrue() {
+ return nil, fmt.Errorf(`parse: double {{ }} while loop condition isn't "true" at %s:%d`,
+ p.filename, p.line())
+ }
body, err := p.parseBlock(doubleCurly)
if err != nil {
return nil, err