1
0
mirror of https://github.com/golang/go synced 2024-11-25 06:47:56 -07:00

go/ast: reflect communication operator changes accurately in ast

- go/ast: introduce SendStmt; adjust SelectStmt
- go/parser: accept new communication syntax, minor
  unrelated cleanups
- go/printer: adjustments for new ast, fewer binary
  expression precedences
- go/token: remove one binary precedence

Adjusted dependent code. gofmt -w src -misc. Ran all tests.

R=rsc, gri
CC=golang-dev
https://golang.org/cl/3989056
This commit is contained in:
Robert Griesemer 2011-02-01 13:47:51 -08:00
parent 7fc4e37853
commit 288a39c86b
9 changed files with 134 additions and 96 deletions

View File

@ -23,7 +23,7 @@ func link(left chan<- int, right <-chan int) {
for { for {
v := <-right v := <-right
stdio.Stdout.WriteString(strconv.Itoa(v) + "\n") stdio.Stdout.WriteString(strconv.Itoa(v) + "\n")
left <- 1+v left <- 1 + v
} }
} }

View File

@ -305,6 +305,9 @@ func (f *File) walk(x interface{}, context string, visit func(*File, interface{}
f.walk(n.Stmt, "stmt", visit) f.walk(n.Stmt, "stmt", visit)
case *ast.ExprStmt: case *ast.ExprStmt:
f.walk(&n.X, "expr", visit) f.walk(&n.X, "expr", visit)
case *ast.SendStmt:
f.walk(&n.Chan, "expr", visit)
f.walk(&n.Value, "expr", visit)
case *ast.IncDecStmt: case *ast.IncDecStmt:
f.walk(&n.X, "expr", visit) f.walk(&n.X, "expr", visit)
case *ast.AssignStmt: case *ast.AssignStmt:
@ -343,8 +346,7 @@ func (f *File) walk(x interface{}, context string, visit func(*File, interface{}
f.walk(n.Assign, "stmt", visit) f.walk(n.Assign, "stmt", visit)
f.walk(n.Body, "stmt", visit) f.walk(n.Body, "stmt", visit)
case *ast.CommClause: case *ast.CommClause:
f.walk(n.Lhs, "expr", visit) f.walk(n.Comm, "stmt", visit)
f.walk(n.Rhs, "expr", visit)
f.walk(n.Body, "stmt", visit) f.walk(n.Body, "stmt", visit)
case *ast.SelectStmt: case *ast.SelectStmt:
f.walk(n.Body, "stmt", visit) f.walk(n.Body, "stmt", visit)

View File

@ -535,6 +535,13 @@ type (
X Expr // expression X Expr // expression
} }
// A SendStmt node represents a send statement.
SendStmt struct {
Chan Expr
Arrow token.Pos // position of "<-"
Value Expr
}
// An IncDecStmt node represents an increment or decrement statement. // An IncDecStmt node represents an increment or decrement statement.
IncDecStmt struct { IncDecStmt struct {
X Expr X Expr
@ -629,11 +636,10 @@ type (
// A CommClause node represents a case of a select statement. // A CommClause node represents a case of a select statement.
CommClause struct { CommClause struct {
Case token.Pos // position of "case" or "default" keyword Case token.Pos // position of "case" or "default" keyword
Tok token.Token // ASSIGN or DEFINE (valid only if Lhs != nil) Comm Stmt // send or receive statement; nil means default case
Lhs, Rhs Expr // Rhs == nil means default case Colon token.Pos // position of ":"
Colon token.Pos // position of ":" Body []Stmt // statement list; or nil
Body []Stmt // statement list; or nil
} }
// An SelectStmt node represents a select statement. // An SelectStmt node represents a select statement.
@ -670,6 +676,7 @@ func (s *DeclStmt) Pos() token.Pos { return s.Decl.Pos() }
func (s *EmptyStmt) Pos() token.Pos { return s.Semicolon } func (s *EmptyStmt) Pos() token.Pos { return s.Semicolon }
func (s *LabeledStmt) Pos() token.Pos { return s.Label.Pos() } func (s *LabeledStmt) Pos() token.Pos { return s.Label.Pos() }
func (s *ExprStmt) Pos() token.Pos { return s.X.Pos() } func (s *ExprStmt) Pos() token.Pos { return s.X.Pos() }
func (s *SendStmt) Pos() token.Pos { return s.Chan.Pos() }
func (s *IncDecStmt) Pos() token.Pos { return s.X.Pos() } func (s *IncDecStmt) Pos() token.Pos { return s.X.Pos() }
func (s *AssignStmt) Pos() token.Pos { return s.Lhs[0].Pos() } func (s *AssignStmt) Pos() token.Pos { return s.Lhs[0].Pos() }
func (s *GoStmt) Pos() token.Pos { return s.Go } func (s *GoStmt) Pos() token.Pos { return s.Go }
@ -695,6 +702,7 @@ func (s *EmptyStmt) End() token.Pos {
} }
func (s *LabeledStmt) End() token.Pos { return s.Stmt.End() } func (s *LabeledStmt) End() token.Pos { return s.Stmt.End() }
func (s *ExprStmt) End() token.Pos { return s.X.End() } func (s *ExprStmt) End() token.Pos { return s.X.End() }
func (s *SendStmt) End() token.Pos { return s.Value.End() }
func (s *IncDecStmt) End() token.Pos { func (s *IncDecStmt) End() token.Pos {
return s.TokPos + 2 /* len("++") */ return s.TokPos + 2 /* len("++") */
} }
@ -753,6 +761,7 @@ func (s *DeclStmt) stmtNode() {}
func (s *EmptyStmt) stmtNode() {} func (s *EmptyStmt) stmtNode() {}
func (s *LabeledStmt) stmtNode() {} func (s *LabeledStmt) stmtNode() {}
func (s *ExprStmt) stmtNode() {} func (s *ExprStmt) stmtNode() {}
func (s *SendStmt) stmtNode() {}
func (s *IncDecStmt) stmtNode() {} func (s *IncDecStmt) stmtNode() {}
func (s *AssignStmt) stmtNode() {} func (s *AssignStmt) stmtNode() {}
func (s *GoStmt) stmtNode() {} func (s *GoStmt) stmtNode() {}

View File

@ -258,11 +258,8 @@ func Walk(v Visitor, node Node) {
Walk(v, n.Body) Walk(v, n.Body)
case *CommClause: case *CommClause:
if n.Lhs != nil { if n.Comm != nil {
Walk(v, n.Lhs) Walk(v, n.Comm)
}
if n.Rhs != nil {
Walk(v, n.Rhs)
} }
walkStmtList(v, n.Body) walkStmtList(v, n.Body)

View File

@ -1193,18 +1193,6 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt {
x := p.parseExprList() x := p.parseExprList()
switch p.tok { switch p.tok {
case token.COLON:
// labeled statement
colon := p.pos
p.next()
if labelOk && len(x) == 1 {
if label, isIdent := x[0].(*ast.Ident); isIdent {
return &ast.LabeledStmt{label, colon, p.parseStmt()}
}
}
p.error(x[0].Pos(), "illegal label declaration")
return &ast.BadStmt{x[0].Pos(), colon + 1}
case case
token.DEFINE, token.ASSIGN, token.ADD_ASSIGN, token.DEFINE, token.ASSIGN, token.ADD_ASSIGN,
token.SUB_ASSIGN, token.MUL_ASSIGN, token.QUO_ASSIGN, token.SUB_ASSIGN, token.MUL_ASSIGN, token.QUO_ASSIGN,
@ -1218,11 +1206,29 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt {
} }
if len(x) > 1 { if len(x) > 1 {
p.error(x[0].Pos(), "only one expression allowed") p.errorExpected(x[0].Pos(), "1 expression")
// continue with first expression // continue with first expression
} }
if p.tok == token.INC || p.tok == token.DEC { switch p.tok {
case token.COLON:
// labeled statement
colon := p.pos
p.next()
if label, isIdent := x[0].(*ast.Ident); labelOk && isIdent {
return &ast.LabeledStmt{label, colon, p.parseStmt()}
}
p.error(x[0].Pos(), "illegal label declaration")
return &ast.BadStmt{x[0].Pos(), colon + 1}
case token.ARROW:
// send statement
arrow := p.pos
p.next() // consume "<-"
y := p.parseExpr()
return &ast.SendStmt{x[0], arrow, y}
case token.INC, token.DEC:
// increment or decrement // increment or decrement
s := &ast.IncDecStmt{x[0], p.pos, p.tok} s := &ast.IncDecStmt{x[0], p.pos, p.tok}
p.next() // consume "++" or "--" p.next() // consume "++" or "--"
@ -1486,28 +1492,52 @@ func (p *parser) parseCommClause() *ast.CommClause {
// CommCase // CommCase
pos := p.pos pos := p.pos
var tok token.Token var comm ast.Stmt
var lhs, rhs ast.Expr
if p.tok == token.CASE { if p.tok == token.CASE {
p.next() p.next()
lhs := p.parseExprList()
if p.tok == token.ARROW { if p.tok == token.ARROW {
// RecvExpr without assignment // SendStmt
rhs = p.parseExpr() if len(lhs) > 1 {
} else { p.errorExpected(lhs[0].Pos(), "1 expression")
// SendExpr or RecvExpr // continue with first expression
rhs = p.parseExpr() }
if p.tok == token.ASSIGN || p.tok == token.DEFINE { arrow := p.pos
// RecvExpr with assignment p.next()
tok = p.tok rhs := p.parseExpr()
p.next() comm = &ast.SendStmt{lhs[0], arrow, rhs}
lhs = rhs } else {
if p.tok == token.ARROW { // RecvStmt
rhs = p.parseExpr() pos := p.pos
} else { tok := p.tok
p.expect(token.ARROW) // use expect() error handling var rhs ast.Expr
} if p.tok == token.ASSIGN || p.tok == token.DEFINE {
// RecvStmt with assignment
if len(lhs) > 2 {
p.errorExpected(lhs[0].Pos(), "1 or 2 expressions")
// continue with first two expressions
lhs = lhs[0:2]
}
p.next()
rhs = p.parseExpr()
} else {
// rhs must be single receive operation
if len(lhs) > 1 {
p.errorExpected(lhs[0].Pos(), "1 expression")
// continue with first expression
}
rhs = lhs[0]
lhs = nil // there is no lhs
}
if x, isUnary := rhs.(*ast.UnaryExpr); !isUnary || x.Op != token.ARROW {
p.errorExpected(rhs.Pos(), "send or receive operation")
rhs = &ast.BadExpr{rhs.Pos(), rhs.End()}
}
if lhs != nil {
comm = &ast.AssignStmt{lhs, pos, tok, []ast.Expr{rhs}}
} else {
comm = &ast.ExprStmt{rhs}
} }
// else SendExpr
} }
} else { } else {
p.expect(token.DEFAULT) p.expect(token.DEFAULT)
@ -1516,7 +1546,7 @@ func (p *parser) parseCommClause() *ast.CommClause {
colon := p.expect(token.COLON) colon := p.expect(token.COLON)
body := p.parseStmtList() body := p.parseStmtList()
return &ast.CommClause{pos, tok, lhs, rhs, colon, body} return &ast.CommClause{pos, comm, colon, body}
} }
@ -1568,7 +1598,7 @@ func (p *parser) parseForStmt() ast.Stmt {
} }
// check rhs // check rhs
if len(as.Rhs) != 1 { if len(as.Rhs) != 1 {
p.errorExpected(as.Rhs[0].Pos(), "1 expressions") p.errorExpected(as.Rhs[0].Pos(), "1 expression")
return &ast.BadStmt{pos, body.End()} return &ast.BadStmt{pos, body.End()}
} }
if rhs, isUnary := as.Rhs[0].(*ast.UnaryExpr); isUnary && rhs.Op == token.RANGE { if rhs, isUnary := as.Rhs[0].(*ast.UnaryExpr); isUnary && rhs.Op == token.RANGE {

View File

@ -46,6 +46,7 @@ var validPrograms = []interface{}{
`package main; type T []int; func g(int) bool { return true }; func f() { if g(T{42}[0]) {} };`, `package main; type T []int; func g(int) bool { return true }; func f() { if g(T{42}[0]) {} };`,
`package main; type T []int; func f() { for _ = range []int{T{42}[0]} {} };`, `package main; type T []int; func f() { for _ = range []int{T{42}[0]} {} };`,
`package main; var a = T{{1, 2}, {3, 4}}`, `package main; var a = T{{1, 2}, {3, 4}}`,
`package main; func f() { select { case <- c: case c <- d: case c <- <- d: case <-c <- d: } };`,
} }

View File

@ -506,12 +506,12 @@ const (
) )
func walkBinary(e *ast.BinaryExpr) (has5, has6 bool, maxProblem int) { func walkBinary(e *ast.BinaryExpr) (has4, has5 bool, maxProblem int) {
switch e.Op.Precedence() { switch e.Op.Precedence() {
case 4:
has4 = true
case 5: case 5:
has5 = true has5 = true
case 6:
has6 = true
} }
switch l := e.X.(type) { switch l := e.X.(type) {
@ -521,9 +521,9 @@ func walkBinary(e *ast.BinaryExpr) (has5, has6 bool, maxProblem int) {
// pretend this is an *ast.ParenExpr and do nothing. // pretend this is an *ast.ParenExpr and do nothing.
break break
} }
h5, h6, mp := walkBinary(l) h4, h5, mp := walkBinary(l)
has4 = has4 || h4
has5 = has5 || h5 has5 = has5 || h5
has6 = has6 || h6
if maxProblem < mp { if maxProblem < mp {
maxProblem = mp maxProblem = mp
} }
@ -536,25 +536,25 @@ func walkBinary(e *ast.BinaryExpr) (has5, has6 bool, maxProblem int) {
// pretend this is an *ast.ParenExpr and do nothing. // pretend this is an *ast.ParenExpr and do nothing.
break break
} }
h5, h6, mp := walkBinary(r) h4, h5, mp := walkBinary(r)
has4 = has4 || h4
has5 = has5 || h5 has5 = has5 || h5
has6 = has6 || h6
if maxProblem < mp { if maxProblem < mp {
maxProblem = mp maxProblem = mp
} }
case *ast.StarExpr: case *ast.StarExpr:
if e.Op.String() == "/" { if e.Op.String() == "/" {
maxProblem = 6 maxProblem = 5
} }
case *ast.UnaryExpr: case *ast.UnaryExpr:
switch e.Op.String() + r.Op.String() { switch e.Op.String() + r.Op.String() {
case "/*", "&&", "&^": case "/*", "&&", "&^":
maxProblem = 6 maxProblem = 5
case "++", "--": case "++", "--":
if maxProblem < 5 { if maxProblem < 4 {
maxProblem = 5 maxProblem = 4
} }
} }
} }
@ -563,20 +563,20 @@ func walkBinary(e *ast.BinaryExpr) (has5, has6 bool, maxProblem int) {
func cutoff(e *ast.BinaryExpr, depth int) int { func cutoff(e *ast.BinaryExpr, depth int) int {
has5, has6, maxProblem := walkBinary(e) has4, has5, maxProblem := walkBinary(e)
if maxProblem > 0 { if maxProblem > 0 {
return maxProblem + 1 return maxProblem + 1
} }
if has5 && has6 { if has4 && has5 {
if depth == 1 { if depth == 1 {
return 6 return 5
} }
return 5 return 4
} }
if depth == 1 { if depth == 1 {
return 7 return 6
} }
return 5 return 4
} }
@ -603,15 +603,14 @@ func reduceDepth(depth int) int {
// (Algorithm suggestion by Russ Cox.) // (Algorithm suggestion by Russ Cox.)
// //
// The precedences are: // The precedences are:
// 6 * / % << >> & &^ // 5 * / % << >> & &^
// 5 + - | ^ // 4 + - | ^
// 4 == != < <= > >= // 3 == != < <= > >=
// 3 <-
// 2 && // 2 &&
// 1 || // 1 ||
// //
// The only decision is whether there will be spaces around levels 5 and 6. // The only decision is whether there will be spaces around levels 4 and 5.
// There are never spaces at level 7 (unary), and always spaces at levels 4 and below. // There are never spaces at level 6 (unary), and always spaces at levels 3 and below.
// //
// To choose the cutoff, look at the whole expression but excluding primary // To choose the cutoff, look at the whole expression but excluding primary
// expressions (function calls, parenthesized exprs), and apply these rules: // expressions (function calls, parenthesized exprs), and apply these rules:
@ -619,21 +618,21 @@ func reduceDepth(depth int) int {
// 1) If there is a binary operator with a right side unary operand // 1) If there is a binary operator with a right side unary operand
// that would clash without a space, the cutoff must be (in order): // that would clash without a space, the cutoff must be (in order):
// //
// /* 7 // /* 6
// && 7 // && 6
// &^ 7 // &^ 6
// ++ 6 // ++ 5
// -- 6 // -- 5
// //
// (Comparison operators always have spaces around them.) // (Comparison operators always have spaces around them.)
// //
// 2) If there is a mix of level 6 and level 5 operators, then the cutoff // 2) If there is a mix of level 5 and level 4 operators, then the cutoff
// is 6 (use spaces to distinguish precedence) in Normal mode // is 5 (use spaces to distinguish precedence) in Normal mode
// and 5 (never use spaces) in Compact mode. // and 4 (never use spaces) in Compact mode.
// //
// 3) If there are no level 5 operators or no level 6 operators, then the // 3) If there are no level 4 operators or no level 5 operators, then the
// cutoff is 7 (always use spaces) in Normal mode // cutoff is 6 (always use spaces) in Normal mode
// and 5 (never use spaces) in Compact mode. // and 4 (never use spaces) in Compact mode.
// //
// Sets multiLine to true if the binary expression spans multiple lines. // Sets multiLine to true if the binary expression spans multiple lines.
func (p *printer) binaryExpr(x *ast.BinaryExpr, prec1, cutoff, depth int, multiLine *bool) { func (p *printer) binaryExpr(x *ast.BinaryExpr, prec1, cutoff, depth int, multiLine *bool) {
@ -1083,6 +1082,12 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool, multiLine *bool) {
const depth = 1 const depth = 1
p.expr0(s.X, depth, multiLine) p.expr0(s.X, depth, multiLine)
case *ast.SendStmt:
const depth = 1
p.expr0(s.Chan, depth, multiLine)
p.print(blank, s.Arrow, token.ARROW, blank)
p.expr0(s.Value, depth, multiLine)
case *ast.IncDecStmt: case *ast.IncDecStmt:
const depth = 1 const depth = 1
p.expr0(s.X, depth+1, multiLine) p.expr0(s.X, depth+1, multiLine)
@ -1179,13 +1184,9 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool, multiLine *bool) {
*multiLine = true *multiLine = true
case *ast.CommClause: case *ast.CommClause:
if s.Rhs != nil { if s.Comm != nil {
p.print(token.CASE, blank) p.print(token.CASE, blank)
if s.Lhs != nil { p.stmt(s.Comm, false, ignoreMultiLine)
p.expr(s.Lhs, multiLine)
p.print(blank, s.Tok, blank)
}
p.expr(s.Rhs, multiLine)
} else { } else {
p.print(token.DEFAULT) p.print(token.DEFAULT)
} }

View File

@ -252,8 +252,8 @@ func (tok Token) String() string {
// //
const ( const (
LowestPrec = 0 // non-operators LowestPrec = 0 // non-operators
UnaryPrec = 7 UnaryPrec = 6
HighestPrec = 8 HighestPrec = 7
) )
@ -267,14 +267,12 @@ func (op Token) Precedence() int {
return 1 return 1
case LAND: case LAND:
return 2 return 2
case ARROW:
return 3
case EQL, NEQ, LSS, LEQ, GTR, GEQ: case EQL, NEQ, LSS, LEQ, GTR, GEQ:
return 4 return 3
case ADD, SUB, OR, XOR: case ADD, SUB, OR, XOR:
return 5 return 4
case MUL, QUO, REM, SHL, SHR, AND, AND_NOT: case MUL, QUO, REM, SHL, SHR, AND, AND_NOT:
return 6 return 5
} }
return LowestPrec return LowestPrec
} }

View File

@ -23,7 +23,7 @@ func exportSend(exp *Exporter, n int, t *testing.T, done chan bool) {
} }
go func() { go func() {
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
ch <- base+i ch <- base + i
} }
close(ch) close(ch)
if done != nil { if done != nil {
@ -61,7 +61,7 @@ func importSend(imp *Importer, n int, t *testing.T, done chan bool) {
} }
go func() { go func() {
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
ch <- base+i ch <- base + i
} }
close(ch) close(ch)
if done != nil { if done != nil {