Commit d3a2f587 authored by Robert Griesemer's avatar Robert Griesemer

go/*: permit "for range x"

LGTM=rsc
R=rsc
CC=golang-codereviews
https://golang.org/cl/112970044
parent 81e661aa
...@@ -699,9 +699,9 @@ type ( ...@@ -699,9 +699,9 @@ type (
// A RangeStmt represents a for statement with a range clause. // A RangeStmt represents a for statement with a range clause.
RangeStmt struct { RangeStmt struct {
For token.Pos // position of "for" keyword For token.Pos // position of "for" keyword
Key, Value Expr // Value may be nil Key, Value Expr // Key, Value may be nil
TokPos token.Pos // position of Tok TokPos token.Pos // position of Tok; invalid if Key == nil
Tok token.Token // ASSIGN, DEFINE Tok token.Token // ILLEGAL if Key == nil, ASSIGN, DEFINE
X Expr // value to range over X Expr // value to range over
Body *BlockStmt Body *BlockStmt
} }
......
...@@ -2041,8 +2041,17 @@ func (p *parser) parseForStmt() ast.Stmt { ...@@ -2041,8 +2041,17 @@ func (p *parser) parseForStmt() ast.Stmt {
prevLev := p.exprLev prevLev := p.exprLev
p.exprLev = -1 p.exprLev = -1
if p.tok != token.SEMICOLON { if p.tok != token.SEMICOLON {
if p.tok == token.RANGE {
// "for range x" (nil lhs in assignment)
pos := p.pos
p.next()
y := []ast.Expr{&ast.UnaryExpr{OpPos: pos, Op: token.RANGE, X: p.parseRhs()}}
s2 = &ast.AssignStmt{Rhs: y}
isRange = true
} else {
s2, isRange = p.parseSimpleStmt(rangeOk) s2, isRange = p.parseSimpleStmt(rangeOk)
} }
}
if !isRange && p.tok == token.SEMICOLON { if !isRange && p.tok == token.SEMICOLON {
p.next() p.next()
s1 = s2 s1 = s2
...@@ -2066,12 +2075,14 @@ func (p *parser) parseForStmt() ast.Stmt { ...@@ -2066,12 +2075,14 @@ func (p *parser) parseForStmt() ast.Stmt {
// check lhs // check lhs
var key, value ast.Expr var key, value ast.Expr
switch len(as.Lhs) { switch len(as.Lhs) {
case 2: case 0:
key, value = as.Lhs[0], as.Lhs[1] // nothing to do
case 1: case 1:
key = as.Lhs[0] key = as.Lhs[0]
case 2:
key, value = as.Lhs[0], as.Lhs[1]
default: default:
p.errorExpected(as.Lhs[0].Pos(), "1 or 2 expressions") p.errorExpected(as.Lhs[len(as.Lhs)-1].Pos(), "at most 2 expressions")
return &ast.BadStmt{From: pos, To: p.safePos(body.End())} return &ast.BadStmt{From: pos, To: p.safePos(body.End())}
} }
// parseSimpleStmt returned a right-hand side that // parseSimpleStmt returned a right-hand side that
......
...@@ -38,6 +38,7 @@ var valids = []string{ ...@@ -38,6 +38,7 @@ var valids = []string{
`package p; func ((T),) m() {}`, `package p; func ((T),) m() {}`,
`package p; func ((*T),) m() {}`, `package p; func ((*T),) m() {}`,
`package p; func (*(T),) m() {}`, `package p; func (*(T),) m() {}`,
`package p; func _(x []int) { for range x {} }`,
} }
func TestValid(t *testing.T) { func TestValid(t *testing.T) {
......
...@@ -1216,6 +1216,7 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool) { ...@@ -1216,6 +1216,7 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool) {
case *ast.RangeStmt: case *ast.RangeStmt:
p.print(token.FOR, blank) p.print(token.FOR, blank)
if s.Key != nil {
p.expr(s.Key) p.expr(s.Key)
if s.Value != nil { if s.Value != nil {
// use position of value following the comma as // use position of value following the comma as
...@@ -1223,7 +1224,9 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool) { ...@@ -1223,7 +1224,9 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool) {
p.print(s.Value.Pos(), token.COMMA, blank) p.print(s.Value.Pos(), token.COMMA, blank)
p.expr(s.Value) p.expr(s.Value)
} }
p.print(blank, s.TokPos, s.Tok, blank, token.RANGE, blank) p.print(blank, s.TokPos, s.Tok, blank)
}
p.print(token.RANGE, blank)
p.expr(stripParens(s.X)) p.expr(stripParens(s.X))
p.print(blank) p.print(blank)
p.block(s.Body, 1) p.block(s.Body, 1)
......
...@@ -309,6 +309,9 @@ func _() { ...@@ -309,6 +309,9 @@ func _() {
for x := expr; expr; expr = false { for x := expr; expr; expr = false {
use(x) use(x)
} }
for range []int{} {
println("foo")
}
for x := range []int{} { for x := range []int{} {
use(x) use(x)
} }
...@@ -338,6 +341,12 @@ func _() { ...@@ -338,6 +341,12 @@ func _() {
a[i] = i a[i] = i
} // multiple lines } // multiple lines
for range a {
}
for _ = range a {
}
for _, _ = range a {
}
for i := range a { for i := range a {
} }
for i := range a { for i := range a {
......
...@@ -269,6 +269,8 @@ func _() { ...@@ -269,6 +269,8 @@ func _() {
for x := expr;expr;expr = false { for x := expr;expr;expr = false {
use(x) use(x)
} }
for range []int{} {
println("foo")}
for x := range []int{} { for x := range []int{} {
use(x) } use(x) }
for x := range (([]int{})) { for x := range (([]int{})) {
...@@ -289,6 +291,9 @@ func _() { ...@@ -289,6 +291,9 @@ func _() {
for i := 0; i < len(a); 1++ { a[i] = i for i := 0; i < len(a); 1++ { a[i] = i
} // multiple lines } // multiple lines
for range a{}
for _ = range a{}
for _, _ = range a{}
for i := range a {} for i := range a {}
for i := range a { a[i] = i } for i := range a { a[i] = i }
for i := range a { a[i] = i for i := range a { a[i] = i
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment