Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
go/src/cmd/compile/internal/syntax/parser.go
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
2314 lines (1981 sloc)
48.1 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright 2016 The Go Authors. All rights reserved. | |
// Use of this source code is governed by a BSD-style | |
// license that can be found in the LICENSE file. | |
package syntax | |
import ( | |
"fmt" | |
"io" | |
"strconv" | |
"strings" | |
) | |
const debug = false | |
const trace = false | |
type parser struct { | |
file *PosBase | |
errh ErrorHandler | |
mode Mode | |
pragh PragmaHandler | |
scanner | |
base *PosBase // current position base | |
first error // first error encountered | |
errcnt int // number of errors encountered | |
pragma Pragma // pragmas | |
fnest int // function nesting level (for error handling) | |
xnest int // expression nesting level (for complit ambiguity resolution) | |
indent []byte // tracing support | |
} | |
func (p *parser) init(file *PosBase, r io.Reader, errh ErrorHandler, pragh PragmaHandler, mode Mode) { | |
p.file = file | |
p.errh = errh | |
p.mode = mode | |
p.pragh = pragh | |
p.scanner.init( | |
r, | |
// Error and directive handler for scanner. | |
// Because the (line, col) positions passed to the | |
// handler is always at or after the current reading | |
// position, it is safe to use the most recent position | |
// base to compute the corresponding Pos value. | |
func(line, col uint, msg string) { | |
if msg[0] != '/' { | |
p.errorAt(p.posAt(line, col), msg) | |
return | |
} | |
// otherwise it must be a comment containing a line or go: directive. | |
// //line directives must be at the start of the line (column colbase). | |
// /*line*/ directives can be anywhere in the line. | |
text := commentText(msg) | |
if (col == colbase || msg[1] == '*') && strings.HasPrefix(text, "line ") { | |
var pos Pos // position immediately following the comment | |
if msg[1] == '/' { | |
// line comment (newline is part of the comment) | |
pos = MakePos(p.file, line+1, colbase) | |
} else { | |
// regular comment | |
// (if the comment spans multiple lines it's not | |
// a valid line directive and will be discarded | |
// by updateBase) | |
pos = MakePos(p.file, line, col+uint(len(msg))) | |
} | |
p.updateBase(pos, line, col+2+5, text[5:]) // +2 to skip over // or /* | |
return | |
} | |
// go: directive (but be conservative and test) | |
if pragh != nil && strings.HasPrefix(text, "go:") { | |
p.pragma = pragh(p.posAt(line, col+2), p.scanner.blank, text, p.pragma) // +2 to skip over // or /* | |
} | |
}, | |
directives, | |
) | |
p.base = file | |
p.first = nil | |
p.errcnt = 0 | |
p.pragma = nil | |
p.fnest = 0 | |
p.xnest = 0 | |
p.indent = nil | |
} | |
// takePragma returns the current parsed pragmas | |
// and clears them from the parser state. | |
func (p *parser) takePragma() Pragma { | |
prag := p.pragma | |
p.pragma = nil | |
return prag | |
} | |
// clearPragma is called at the end of a statement or | |
// other Go form that does NOT accept a pragma. | |
// It sends the pragma back to the pragma handler | |
// to be reported as unused. | |
func (p *parser) clearPragma() { | |
if p.pragma != nil { | |
p.pragh(p.pos(), p.scanner.blank, "", p.pragma) | |
p.pragma = nil | |
} | |
} | |
// updateBase sets the current position base to a new line base at pos. | |
// The base's filename, line, and column values are extracted from text | |
// which is positioned at (tline, tcol) (only needed for error messages). | |
func (p *parser) updateBase(pos Pos, tline, tcol uint, text string) { | |
i, n, ok := trailingDigits(text) | |
if i == 0 { | |
return // ignore (not a line directive) | |
} | |
// i > 0 | |
if !ok { | |
// text has a suffix :xxx but xxx is not a number | |
p.errorAt(p.posAt(tline, tcol+i), "invalid line number: "+text[i:]) | |
return | |
} | |
var line, col uint | |
i2, n2, ok2 := trailingDigits(text[:i-1]) | |
if ok2 { | |
//line filename:line:col | |
i, i2 = i2, i | |
line, col = n2, n | |
if col == 0 || col > PosMax { | |
p.errorAt(p.posAt(tline, tcol+i2), "invalid column number: "+text[i2:]) | |
return | |
} | |
text = text[:i2-1] // lop off ":col" | |
} else { | |
//line filename:line | |
line = n | |
} | |
if line == 0 || line > PosMax { | |
p.errorAt(p.posAt(tline, tcol+i), "invalid line number: "+text[i:]) | |
return | |
} | |
// If we have a column (//line filename:line:col form), | |
// an empty filename means to use the previous filename. | |
filename := text[:i-1] // lop off ":line" | |
if filename == "" && ok2 { | |
filename = p.base.Filename() | |
} | |
p.base = NewLineBase(pos, filename, line, col) | |
} | |
func commentText(s string) string { | |
if s[:2] == "/*" { | |
return s[2 : len(s)-2] // lop off /* and */ | |
} | |
// line comment (does not include newline) | |
// (on Windows, the line comment may end in \r\n) | |
i := len(s) | |
if s[i-1] == '\r' { | |
i-- | |
} | |
return s[2:i] // lop off //, and \r at end, if any | |
} | |
func trailingDigits(text string) (uint, uint, bool) { | |
// Want to use LastIndexByte below but it's not defined in Go1.4 and bootstrap fails. | |
i := strings.LastIndex(text, ":") // look from right (Windows filenames may contain ':') | |
if i < 0 { | |
return 0, 0, false // no ":" | |
} | |
// i >= 0 | |
n, err := strconv.ParseUint(text[i+1:], 10, 0) | |
return uint(i + 1), uint(n), err == nil | |
} | |
func (p *parser) got(tok token) bool { | |
if p.tok == tok { | |
p.next() | |
return true | |
} | |
return false | |
} | |
func (p *parser) want(tok token) { | |
if !p.got(tok) { | |
p.syntaxError("expecting " + tokstring(tok)) | |
p.advance() | |
} | |
} | |
// gotAssign is like got(_Assign) but it also accepts ":=" | |
// (and reports an error) for better parser error recovery. | |
func (p *parser) gotAssign() bool { | |
switch p.tok { | |
case _Define: | |
p.syntaxError("expecting =") | |
fallthrough | |
case _Assign: | |
p.next() | |
return true | |
} | |
return false | |
} | |
// ---------------------------------------------------------------------------- | |
// Error handling | |
// posAt returns the Pos value for (line, col) and the current position base. | |
func (p *parser) posAt(line, col uint) Pos { | |
return MakePos(p.base, line, col) | |
} | |
// error reports an error at the given position. | |
func (p *parser) errorAt(pos Pos, msg string) { | |
err := Error{pos, msg} | |
if p.first == nil { | |
p.first = err | |
} | |
p.errcnt++ | |
if p.errh == nil { | |
panic(p.first) | |
} | |
p.errh(err) | |
} | |
// syntaxErrorAt reports a syntax error at the given position. | |
func (p *parser) syntaxErrorAt(pos Pos, msg string) { | |
if trace { | |
p.print("syntax error: " + msg) | |
} | |
if p.tok == _EOF && p.first != nil { | |
return // avoid meaningless follow-up errors | |
} | |
// add punctuation etc. as needed to msg | |
switch { | |
case msg == "": | |
// nothing to do | |
case strings.HasPrefix(msg, "in "), strings.HasPrefix(msg, "at "), strings.HasPrefix(msg, "after "): | |
msg = " " + msg | |
case strings.HasPrefix(msg, "expecting "): | |
msg = ", " + msg | |
default: | |
// plain error - we don't care about current token | |
p.errorAt(pos, "syntax error: "+msg) | |
return | |
} | |
// determine token string | |
var tok string | |
switch p.tok { | |
case _Name, _Semi: | |
tok = p.lit | |
case _Literal: | |
tok = "literal " + p.lit | |
case _Operator: | |
tok = p.op.String() | |
case _AssignOp: | |
tok = p.op.String() + "=" | |
case _IncOp: | |
tok = p.op.String() | |
tok += tok | |
default: | |
tok = tokstring(p.tok) | |
} | |
p.errorAt(pos, "syntax error: unexpected "+tok+msg) | |
} | |
// tokstring returns the English word for selected punctuation tokens | |
// for more readable error messages. | |
func tokstring(tok token) string { | |
switch tok { | |
case _Comma: | |
return "comma" | |
case _Semi: | |
return "semicolon or newline" | |
} | |
return tok.String() | |
} | |
// Convenience methods using the current token position. | |
func (p *parser) pos() Pos { return p.posAt(p.line, p.col) } | |
func (p *parser) syntaxError(msg string) { p.syntaxErrorAt(p.pos(), msg) } | |
// The stopset contains keywords that start a statement. | |
// They are good synchronization points in case of syntax | |
// errors and (usually) shouldn't be skipped over. | |
const stopset uint64 = 1<<_Break | | |
1<<_Const | | |
1<<_Continue | | |
1<<_Defer | | |
1<<_Fallthrough | | |
1<<_For | | |
1<<_Go | | |
1<<_Goto | | |
1<<_If | | |
1<<_Return | | |
1<<_Select | | |
1<<_Switch | | |
1<<_Type | | |
1<<_Var | |
// Advance consumes tokens until it finds a token of the stopset or followlist. | |
// The stopset is only considered if we are inside a function (p.fnest > 0). | |
// The followlist is the list of valid tokens that can follow a production; | |
// if it is empty, exactly one (non-EOF) token is consumed to ensure progress. | |
func (p *parser) advance(followlist ...token) { | |
if trace { | |
p.print(fmt.Sprintf("advance %s", followlist)) | |
} | |
// compute follow set | |
// (not speed critical, advance is only called in error situations) | |
var followset uint64 = 1 << _EOF // don't skip over EOF | |
if len(followlist) > 0 { | |
if p.fnest > 0 { | |
followset |= stopset | |
} | |
for _, tok := range followlist { | |
followset |= 1 << tok | |
} | |
} | |
for !contains(followset, p.tok) { | |
if trace { | |
p.print("skip " + p.tok.String()) | |
} | |
p.next() | |
if len(followlist) == 0 { | |
break | |
} | |
} | |
if trace { | |
p.print("next " + p.tok.String()) | |
} | |
} | |
// usage: defer p.trace(msg)() | |
func (p *parser) trace(msg string) func() { | |
p.print(msg + " (") | |
const tab = ". " | |
p.indent = append(p.indent, tab...) | |
return func() { | |
p.indent = p.indent[:len(p.indent)-len(tab)] | |
if x := recover(); x != nil { | |
panic(x) // skip print_trace | |
} | |
p.print(")") | |
} | |
} | |
func (p *parser) print(msg string) { | |
fmt.Printf("%5d: %s%s\n", p.line, p.indent, msg) | |
} | |
// ---------------------------------------------------------------------------- | |
// Package files | |
// | |
// Parse methods are annotated with matching Go productions as appropriate. | |
// The annotations are intended as guidelines only since a single Go grammar | |
// rule may be covered by multiple parse methods and vice versa. | |
// | |
// Excluding methods returning slices, parse methods named xOrNil may return | |
// nil; all others are expected to return a valid non-nil node. | |
// SourceFile = PackageClause ";" { ImportDecl ";" } { TopLevelDecl ";" } . | |
func (p *parser) fileOrNil() *File { | |
if trace { | |
defer p.trace("file")() | |
} | |
f := new(File) | |
f.pos = p.pos() | |
// PackageClause | |
if !p.got(_Package) { | |
p.syntaxError("package statement must be first") | |
return nil | |
} | |
f.Pragma = p.takePragma() | |
f.PkgName = p.name() | |
p.want(_Semi) | |
// don't bother continuing if package clause has errors | |
if p.first != nil { | |
return nil | |
} | |
// { ImportDecl ";" } | |
for p.got(_Import) { | |
f.DeclList = p.appendGroup(f.DeclList, p.importDecl) | |
p.want(_Semi) | |
} | |
// { TopLevelDecl ";" } | |
for p.tok != _EOF { | |
switch p.tok { | |
case _Const: | |
p.next() | |
f.DeclList = p.appendGroup(f.DeclList, p.constDecl) | |
case _Type: | |
p.next() | |
f.DeclList = p.appendGroup(f.DeclList, p.typeDecl) | |
case _Var: | |
p.next() | |
f.DeclList = p.appendGroup(f.DeclList, p.varDecl) | |
case _Func: | |
p.next() | |
if d := p.funcDeclOrNil(); d != nil { | |
f.DeclList = append(f.DeclList, d) | |
} | |
default: | |
if p.tok == _Lbrace && len(f.DeclList) > 0 && isEmptyFuncDecl(f.DeclList[len(f.DeclList)-1]) { | |
// opening { of function declaration on next line | |
p.syntaxError("unexpected semicolon or newline before {") | |
} else { | |
p.syntaxError("non-declaration statement outside function body") | |
} | |
p.advance(_Const, _Type, _Var, _Func) | |
continue | |
} | |
// Reset p.pragma BEFORE advancing to the next token (consuming ';') | |
// since comments before may set pragmas for the next function decl. | |
p.clearPragma() | |
if p.tok != _EOF && !p.got(_Semi) { | |
p.syntaxError("after top level declaration") | |
p.advance(_Const, _Type, _Var, _Func) | |
} | |
} | |
// p.tok == _EOF | |
p.clearPragma() | |
f.Lines = p.line | |
return f | |
} | |
func isEmptyFuncDecl(dcl Decl) bool { | |
f, ok := dcl.(*FuncDecl) | |
return ok && f.Body == nil | |
} | |
// ---------------------------------------------------------------------------- | |
// Declarations | |
// list parses a possibly empty, sep-separated list, optionally | |
// followed by sep and enclosed by ( and ) or { and }. open is | |
// one of _Lparen, or _Lbrace, sep is one of _Comma or _Semi, | |
// and close is expected to be the (closing) opposite of open. | |
// For each list element, f is called. After f returns true, no | |
// more list elements are accepted. list returns the position | |
// of the closing token. | |
// | |
// list = "(" { f sep } ")" | | |
// "{" { f sep } "}" . // sep is optional before ")" or "}" | |
// | |
func (p *parser) list(open, sep, close token, f func() bool) Pos { | |
p.want(open) | |
var done bool | |
for p.tok != _EOF && p.tok != close && !done { | |
done = f() | |
// sep is optional before close | |
if !p.got(sep) && p.tok != close { | |
p.syntaxError(fmt.Sprintf("expecting %s or %s", tokstring(sep), tokstring(close))) | |
p.advance(_Rparen, _Rbrack, _Rbrace) | |
if p.tok != close { | |
// position could be better but we had an error so we don't care | |
return p.pos() | |
} | |
} | |
} | |
pos := p.pos() | |
p.want(close) | |
return pos | |
} | |
// appendGroup(f) = f | "(" { f ";" } ")" . // ";" is optional before ")" | |
func (p *parser) appendGroup(list []Decl, f func(*Group) Decl) []Decl { | |
if p.tok == _Lparen { | |
g := new(Group) | |
p.clearPragma() | |
p.list(_Lparen, _Semi, _Rparen, func() bool { | |
list = append(list, f(g)) | |
return false | |
}) | |
} else { | |
list = append(list, f(nil)) | |
} | |
if debug { | |
for _, d := range list { | |
if d == nil { | |
panic("nil list entry") | |
} | |
} | |
} | |
return list | |
} | |
// ImportSpec = [ "." | PackageName ] ImportPath . | |
// ImportPath = string_lit . | |
func (p *parser) importDecl(group *Group) Decl { | |
if trace { | |
defer p.trace("importDecl")() | |
} | |
d := new(ImportDecl) | |
d.pos = p.pos() | |
d.Group = group | |
d.Pragma = p.takePragma() | |
switch p.tok { | |
case _Name: | |
d.LocalPkgName = p.name() | |
case _Dot: | |
d.LocalPkgName = p.newName(".") | |
p.next() | |
} | |
d.Path = p.oliteral() | |
if d.Path == nil { | |
p.syntaxError("missing import path") | |
p.advance(_Semi, _Rparen) | |
return nil | |
} | |
return d | |
} | |
// ConstSpec = IdentifierList [ [ Type ] "=" ExpressionList ] . | |
func (p *parser) constDecl(group *Group) Decl { | |
if trace { | |
defer p.trace("constDecl")() | |
} | |
d := new(ConstDecl) | |
d.pos = p.pos() | |
d.Group = group | |
d.Pragma = p.takePragma() | |
d.NameList = p.nameList(p.name()) | |
if p.tok != _EOF && p.tok != _Semi && p.tok != _Rparen { | |
d.Type = p.typeOrNil() | |
if p.gotAssign() { | |
d.Values = p.exprList() | |
} | |
} | |
return d | |
} | |
// TypeSpec = identifier [ "=" ] Type . | |
func (p *parser) typeDecl(group *Group) Decl { | |
if trace { | |
defer p.trace("typeDecl")() | |
} | |
d := new(TypeDecl) | |
d.pos = p.pos() | |
d.Group = group | |
d.Pragma = p.takePragma() | |
d.Name = p.name() | |
d.Alias = p.gotAssign() | |
d.Type = p.typeOrNil() | |
if d.Type == nil { | |
d.Type = p.badExpr() | |
p.syntaxError("in type declaration") | |
p.advance(_Semi, _Rparen) | |
} | |
return d | |
} | |
// VarSpec = IdentifierList ( Type [ "=" ExpressionList ] | "=" ExpressionList ) . | |
func (p *parser) varDecl(group *Group) Decl { | |
if trace { | |
defer p.trace("varDecl")() | |
} | |
d := new(VarDecl) | |
d.pos = p.pos() | |
d.Group = group | |
d.Pragma = p.takePragma() | |
d.NameList = p.nameList(p.name()) | |
if p.gotAssign() { | |
d.Values = p.exprList() | |
} else { | |
d.Type = p.type_() | |
if p.gotAssign() { | |
d.Values = p.exprList() | |
} | |
} | |
return d | |
} | |
// FunctionDecl = "func" FunctionName ( Function | Signature ) . | |
// FunctionName = identifier . | |
// Function = Signature FunctionBody . | |
// MethodDecl = "func" Receiver MethodName ( Function | Signature ) . | |
// Receiver = Parameters . | |
func (p *parser) funcDeclOrNil() *FuncDecl { | |
if trace { | |
defer p.trace("funcDecl")() | |
} | |
f := new(FuncDecl) | |
f.pos = p.pos() | |
f.Pragma = p.takePragma() | |
if p.tok == _Lparen { | |
rcvr := p.paramList() | |
switch len(rcvr) { | |
case 0: | |
p.error("method has no receiver") | |
default: | |
p.error("method has multiple receivers") | |
fallthrough | |
case 1: | |
f.Recv = rcvr[0] | |
} | |
} | |
if p.tok != _Name { | |
p.syntaxError("expecting name or (") | |
p.advance(_Lbrace, _Semi) | |
return nil | |
} | |
f.Name = p.name() | |
f.Type = p.funcType() | |
if p.tok == _Lbrace { | |
f.Body = p.funcBody() | |
} | |
return f | |
} | |
func (p *parser) funcBody() *BlockStmt { | |
p.fnest++ | |
errcnt := p.errcnt | |
body := p.blockStmt("") | |
p.fnest-- | |
// Don't check branches if there were syntax errors in the function | |
// as it may lead to spurious errors (e.g., see test/switch2.go) or | |
// possibly crashes due to incomplete syntax trees. | |
if p.mode&CheckBranches != 0 && errcnt == p.errcnt { | |
checkBranches(body, p.errh) | |
} | |
return body | |
} | |
// ---------------------------------------------------------------------------- | |
// Expressions | |
func (p *parser) expr() Expr { | |
if trace { | |
defer p.trace("expr")() | |
} | |
return p.binaryExpr(0) | |
} | |
// Expression = UnaryExpr | Expression binary_op Expression . | |
func (p *parser) binaryExpr(prec int) Expr { | |
// don't trace binaryExpr - only leads to overly nested trace output | |
x := p.unaryExpr() | |
for (p.tok == _Operator || p.tok == _Star) && p.prec > prec { | |
t := new(Operation) | |
t.pos = p.pos() | |
t.Op = p.op | |
t.X = x | |
tprec := p.prec | |
p.next() | |
t.Y = p.binaryExpr(tprec) | |
x = t | |
} | |
return x | |
} | |
// UnaryExpr = PrimaryExpr | unary_op UnaryExpr . | |
func (p *parser) unaryExpr() Expr { | |
if trace { | |
defer p.trace("unaryExpr")() | |
} | |
switch p.tok { | |
case _Operator, _Star: | |
switch p.op { | |
case Mul, Add, Sub, Not, Xor: | |
x := new(Operation) | |
x.pos = p.pos() | |
x.Op = p.op | |
p.next() | |
x.X = p.unaryExpr() | |
return x | |
case And: | |
x := new(Operation) | |
x.pos = p.pos() | |
x.Op = And | |
p.next() | |
// unaryExpr may have returned a parenthesized composite literal | |
// (see comment in operand) - remove parentheses if any | |
x.X = unparen(p.unaryExpr()) | |
return x | |
} | |
case _Arrow: | |
// receive op (<-x) or receive-only channel (<-chan E) | |
pos := p.pos() | |
p.next() | |
// If the next token is _Chan we still don't know if it is | |
// a channel (<-chan int) or a receive op (<-chan int(ch)). | |
// We only know once we have found the end of the unaryExpr. | |
x := p.unaryExpr() | |
// There are two cases: | |
// | |
// <-chan... => <-x is a channel type | |
// <-x => <-x is a receive operation | |
// | |
// In the first case, <- must be re-associated with | |
// the channel type parsed already: | |
// | |
// <-(chan E) => (<-chan E) | |
// <-(chan<-E) => (<-chan (<-E)) | |
if _, ok := x.(*ChanType); ok { | |
// x is a channel type => re-associate <- | |
dir := SendOnly | |
t := x | |
for dir == SendOnly { | |
c, ok := t.(*ChanType) | |
if !ok { | |
break | |
} | |
dir = c.Dir | |
if dir == RecvOnly { | |
// t is type <-chan E but <-<-chan E is not permitted | |
// (report same error as for "type _ <-<-chan E") | |
p.syntaxError("unexpected <-, expecting chan") | |
// already progressed, no need to advance | |
} | |
c.Dir = RecvOnly | |
t = c.Elem | |
} | |
if dir == SendOnly { | |
// channel dir is <- but channel element E is not a channel | |
// (report same error as for "type _ <-chan<-E") | |
p.syntaxError(fmt.Sprintf("unexpected %s, expecting chan", String(t))) | |
// already progressed, no need to advance | |
} | |
return x | |
} | |
// x is not a channel type => we have a receive op | |
o := new(Operation) | |
o.pos = pos | |
o.Op = Recv | |
o.X = x | |
return o | |
} | |
// TODO(mdempsky): We need parens here so we can report an | |
// error for "(x) := true". It should be possible to detect | |
// and reject that more efficiently though. | |
return p.pexpr(true) | |
} | |
// callStmt parses call-like statements that can be preceded by 'defer' and 'go'. | |
func (p *parser) callStmt() *CallStmt { | |
if trace { | |
defer p.trace("callStmt")() | |
} | |
s := new(CallStmt) | |
s.pos = p.pos() | |
s.Tok = p.tok // _Defer or _Go | |
p.next() | |
x := p.pexpr(p.tok == _Lparen) // keep_parens so we can report error below | |
if t := unparen(x); t != x { | |
p.errorAt(x.Pos(), fmt.Sprintf("expression in %s must not be parenthesized", s.Tok)) | |
// already progressed, no need to advance | |
x = t | |
} | |
cx, ok := x.(*CallExpr) | |
if !ok { | |
p.errorAt(x.Pos(), fmt.Sprintf("expression in %s must be function call", s.Tok)) | |
// already progressed, no need to advance | |
cx = new(CallExpr) | |
cx.pos = x.Pos() | |
cx.Fun = x // assume common error of missing parentheses (function invocation) | |
} | |
s.Call = cx | |
return s | |
} | |
// Operand = Literal | OperandName | MethodExpr | "(" Expression ")" . | |
// Literal = BasicLit | CompositeLit | FunctionLit . | |
// BasicLit = int_lit | float_lit | imaginary_lit | rune_lit | string_lit . | |
// OperandName = identifier | QualifiedIdent. | |
func (p *parser) operand(keep_parens bool) Expr { | |
if trace { | |
defer p.trace("operand " + p.tok.String())() | |
} | |
switch p.tok { | |
case _Name: | |
return p.name() | |
case _Literal: | |
return p.oliteral() | |
case _Lparen: | |
pos := p.pos() | |
p.next() | |
p.xnest++ | |
x := p.expr() | |
p.xnest-- | |
p.want(_Rparen) | |
// Optimization: Record presence of ()'s only where needed | |
// for error reporting. Don't bother in other cases; it is | |
// just a waste of memory and time. | |
// Parentheses are not permitted on lhs of := . | |
// switch x.Op { | |
// case ONAME, ONONAME, OPACK, OTYPE, OLITERAL, OTYPESW: | |
// keep_parens = true | |
// } | |
// Parentheses are not permitted around T in a composite | |
// literal T{}. If the next token is a {, assume x is a | |
// composite literal type T (it may not be, { could be | |
// the opening brace of a block, but we don't know yet). | |
if p.tok == _Lbrace { | |
keep_parens = true | |
} | |
// Parentheses are also not permitted around the expression | |
// in a go/defer statement. In that case, operand is called | |
// with keep_parens set. | |
if keep_parens { | |
px := new(ParenExpr) | |
px.pos = pos | |
px.X = x | |
x = px | |
} | |
return x | |
case _Func: | |
pos := p.pos() | |
p.next() | |
t := p.funcType() | |
if p.tok == _Lbrace { | |
p.xnest++ | |
f := new(FuncLit) | |
f.pos = pos | |
f.Type = t | |
f.Body = p.funcBody() | |
p.xnest-- | |
return f | |
} | |
return t | |
case _Lbrack, _Chan, _Map, _Struct, _Interface: | |
return p.type_() // othertype | |
default: | |
x := p.badExpr() | |
p.syntaxError("expecting expression") | |
p.advance(_Rparen, _Rbrack, _Rbrace) | |
return x | |
} | |
// Syntactically, composite literals are operands. Because a complit | |
// type may be a qualified identifier which is handled by pexpr | |
// (together with selector expressions), complits are parsed there | |
// as well (operand is only called from pexpr). | |
} | |
// PrimaryExpr = | |
// Operand | | |
// Conversion | | |
// PrimaryExpr Selector | | |
// PrimaryExpr Index | | |
// PrimaryExpr Slice | | |
// PrimaryExpr TypeAssertion | | |
// PrimaryExpr Arguments . | |
// | |
// Selector = "." identifier . | |
// Index = "[" Expression "]" . | |
// Slice = "[" ( [ Expression ] ":" [ Expression ] ) | | |
// ( [ Expression ] ":" Expression ":" Expression ) | |
// "]" . | |
// TypeAssertion = "." "(" Type ")" . | |
// Arguments = "(" [ ( ExpressionList | Type [ "," ExpressionList ] ) [ "..." ] [ "," ] ] ")" . | |
func (p *parser) pexpr(keep_parens bool) Expr { | |
if trace { | |
defer p.trace("pexpr")() | |
} | |
x := p.operand(keep_parens) | |
loop: | |
for { | |
pos := p.pos() | |
switch p.tok { | |
case _Dot: | |
p.next() | |
switch p.tok { | |
case _Name: | |
// pexpr '.' sym | |
t := new(SelectorExpr) | |
t.pos = pos | |
t.X = x | |
t.Sel = p.name() | |
x = t | |
case _Lparen: | |
p.next() | |
if p.got(_Type) { | |
t := new(TypeSwitchGuard) | |
// t.Lhs is filled in by parser.simpleStmt | |
t.pos = pos | |
t.X = x | |
x = t | |
} else { | |
t := new(AssertExpr) | |
t.pos = pos | |
t.X = x | |
t.Type = p.type_() | |
x = t | |
} | |
p.want(_Rparen) | |
default: | |
p.syntaxError("expecting name or (") | |
p.advance(_Semi, _Rparen) | |
} | |
case _Lbrack: | |
p.next() | |
p.xnest++ | |
var i Expr | |
if p.tok != _Colon { | |
i = p.expr() | |
if p.got(_Rbrack) { | |
// x[i] | |
t := new(IndexExpr) | |
t.pos = pos | |
t.X = x | |
t.Index = i | |
x = t | |
p.xnest-- | |
break | |
} | |
} | |
// x[i:... | |
t := new(SliceExpr) | |
t.pos = pos | |
t.X = x | |
t.Index[0] = i | |
p.want(_Colon) | |
if p.tok != _Colon && p.tok != _Rbrack { | |
// x[i:j... | |
t.Index[1] = p.expr() | |
} | |
if p.got(_Colon) { | |
t.Full = true | |
// x[i:j:...] | |
if t.Index[1] == nil { | |
p.error("middle index required in 3-index slice") | |
} | |
if p.tok != _Rbrack { | |
// x[i:j:k... | |
t.Index[2] = p.expr() | |
} else { | |
p.error("final index required in 3-index slice") | |
} | |
} | |
p.want(_Rbrack) | |
x = t | |
p.xnest-- | |
case _Lparen: | |
t := new(CallExpr) | |
t.pos = pos | |
t.Fun = x | |
t.ArgList, t.HasDots = p.argList() | |
x = t | |
case _Lbrace: | |
// operand may have returned a parenthesized complit | |
// type; accept it but complain if we have a complit | |
t := unparen(x) | |
// determine if '{' belongs to a composite literal or a block statement | |
complit_ok := false | |
switch t.(type) { | |
case *Name, *SelectorExpr: | |
if p.xnest >= 0 { | |
// x is considered a composite literal type | |
complit_ok = true | |
} | |
case *ArrayType, *SliceType, *StructType, *MapType: | |
// x is a comptype | |
complit_ok = true | |
} | |
if !complit_ok { | |
break loop | |
} | |
if t != x { | |
p.syntaxError("cannot parenthesize type in composite literal") | |
// already progressed, no need to advance | |
} | |
n := p.complitexpr() | |
n.Type = x | |
x = n | |
default: | |
break loop | |
} | |
} | |
return x | |
} | |
// Element = Expression | LiteralValue . | |
func (p *parser) bare_complitexpr() Expr { | |
if trace { | |
defer p.trace("bare_complitexpr")() | |
} | |
if p.tok == _Lbrace { | |
// '{' start_complit braced_keyval_list '}' | |
return p.complitexpr() | |
} | |
return p.expr() | |
} | |
// LiteralValue = "{" [ ElementList [ "," ] ] "}" . | |
func (p *parser) complitexpr() *CompositeLit { | |
if trace { | |
defer p.trace("complitexpr")() | |
} | |
x := new(CompositeLit) | |
x.pos = p.pos() | |
p.xnest++ | |
x.Rbrace = p.list(_Lbrace, _Comma, _Rbrace, func() bool { | |
// value | |
e := p.bare_complitexpr() | |
if p.tok == _Colon { | |
// key ':' value | |
l := new(KeyValueExpr) | |
l.pos = p.pos() | |
p.next() | |
l.Key = e | |
l.Value = p.bare_complitexpr() | |
e = l | |
x.NKeys++ | |
} | |
x.ElemList = append(x.ElemList, e) | |
return false | |
}) | |
p.xnest-- | |
return x | |
} | |
// ---------------------------------------------------------------------------- | |
// Types | |
func (p *parser) type_() Expr { | |
if trace { | |
defer p.trace("type_")() | |
} | |
typ := p.typeOrNil() | |
if typ == nil { | |
typ = p.badExpr() | |
p.syntaxError("expecting type") | |
p.advance(_Comma, _Colon, _Semi, _Rparen, _Rbrack, _Rbrace) | |
} | |
return typ | |
} | |
func newIndirect(pos Pos, typ Expr) Expr { | |
o := new(Operation) | |
o.pos = pos | |
o.Op = Mul | |
o.X = typ | |
return o | |
} | |
// typeOrNil is like type_ but it returns nil if there was no type | |
// instead of reporting an error. | |
// | |
// Type = TypeName | TypeLit | "(" Type ")" . | |
// TypeName = identifier | QualifiedIdent . | |
// TypeLit = ArrayType | StructType | PointerType | FunctionType | InterfaceType | | |
// SliceType | MapType | Channel_Type . | |
func (p *parser) typeOrNil() Expr { | |
if trace { | |
defer p.trace("typeOrNil")() | |
} | |
pos := p.pos() | |
switch p.tok { | |
case _Star: | |
// ptrtype | |
p.next() | |
return newIndirect(pos, p.type_()) | |
case _Arrow: | |
// recvchantype | |
p.next() | |
p.want(_Chan) | |
t := new(ChanType) | |
t.pos = pos | |
t.Dir = RecvOnly | |
t.Elem = p.chanElem() | |
return t | |
case _Func: | |
// fntype | |
p.next() | |
return p.funcType() | |
case _Lbrack: | |
// '[' oexpr ']' ntype | |
// '[' _DotDotDot ']' ntype | |
p.next() | |
p.xnest++ | |
if p.got(_Rbrack) { | |
// []T | |
p.xnest-- | |
t := new(SliceType) | |
t.pos = pos | |
t.Elem = p.type_() | |
return t | |
} | |
// [n]T | |
t := new(ArrayType) | |
t.pos = pos | |
if !p.got(_DotDotDot) { | |
t.Len = p.expr() | |
} | |
p.want(_Rbrack) | |
p.xnest-- | |
t.Elem = p.type_() | |
return t | |
case _Chan: | |
// _Chan non_recvchantype | |
// _Chan _Comm ntype | |
p.next() | |
t := new(ChanType) | |
t.pos = pos | |
if p.got(_Arrow) { | |
t.Dir = SendOnly | |
} | |
t.Elem = p.chanElem() | |
return t | |
case _Map: | |
// _Map '[' ntype ']' ntype | |
p.next() | |
p.want(_Lbrack) | |
t := new(MapType) | |
t.pos = pos | |
t.Key = p.type_() | |
p.want(_Rbrack) | |
t.Value = p.type_() | |
return t | |
case _Struct: | |
return p.structType() | |
case _Interface: | |
return p.interfaceType() | |
case _Name: | |
return p.dotname(p.name()) | |
case _Lparen: | |
p.next() | |
t := p.type_() | |
p.want(_Rparen) | |
return t | |
} | |
return nil | |
} | |
func (p *parser) funcType() *FuncType { | |
if trace { | |
defer p.trace("funcType")() | |
} | |
typ := new(FuncType) | |
typ.pos = p.pos() | |
typ.ParamList = p.paramList() | |
typ.ResultList = p.funcResult() | |
return typ | |
} | |
func (p *parser) chanElem() Expr { | |
if trace { | |
defer p.trace("chanElem")() | |
} | |
typ := p.typeOrNil() | |
if typ == nil { | |
typ = p.badExpr() | |
p.syntaxError("missing channel element type") | |
// assume element type is simply absent - don't advance | |
} | |
return typ | |
} | |
func (p *parser) dotname(name *Name) Expr { | |
if trace { | |
defer p.trace("dotname")() | |
} | |
if p.tok == _Dot { | |
s := new(SelectorExpr) | |
s.pos = p.pos() | |
p.next() | |
s.X = name | |
s.Sel = p.name() | |
return s | |
} | |
return name | |
} | |
// StructType = "struct" "{" { FieldDecl ";" } "}" . | |
func (p *parser) structType() *StructType { | |
if trace { | |
defer p.trace("structType")() | |
} | |
typ := new(StructType) | |
typ.pos = p.pos() | |
p.want(_Struct) | |
p.list(_Lbrace, _Semi, _Rbrace, func() bool { | |
p.fieldDecl(typ) | |
return false | |
}) | |
return typ | |
} | |
// InterfaceType = "interface" "{" { MethodSpec ";" } "}" . | |
func (p *parser) interfaceType() *InterfaceType { | |
if trace { | |
defer p.trace("interfaceType")() | |
} | |
typ := new(InterfaceType) | |
typ.pos = p.pos() | |
p.want(_Interface) | |
p.list(_Lbrace, _Semi, _Rbrace, func() bool { | |
if m := p.methodDecl(); m != nil { | |
typ.MethodList = append(typ.MethodList, m) | |
} | |
return false | |
}) | |
return typ | |
} | |
// Result = Parameters | Type . | |
func (p *parser) funcResult() []*Field { | |
if trace { | |
defer p.trace("funcResult")() | |
} | |
if p.tok == _Lparen { | |
return p.paramList() | |
} | |
pos := p.pos() | |
if typ := p.typeOrNil(); typ != nil { | |
f := new(Field) | |
f.pos = pos | |
f.Type = typ | |
return []*Field{f} | |
} | |
return nil | |
} | |
func (p *parser) addField(styp *StructType, pos Pos, name *Name, typ Expr, tag *BasicLit) { | |
if tag != nil { | |
for i := len(styp.FieldList) - len(styp.TagList); i > 0; i-- { | |
styp.TagList = append(styp.TagList, nil) | |
} | |
styp.TagList = append(styp.TagList, tag) | |
} | |
f := new(Field) | |
f.pos = pos | |
f.Name = name | |
f.Type = typ | |
styp.FieldList = append(styp.FieldList, f) | |
if debug && tag != nil && len(styp.FieldList) != len(styp.TagList) { | |
panic("inconsistent struct field list") | |
} | |
} | |
// FieldDecl = (IdentifierList Type | AnonymousField) [ Tag ] . | |
// AnonymousField = [ "*" ] TypeName . | |
// Tag = string_lit . | |
func (p *parser) fieldDecl(styp *StructType) { | |
if trace { | |
defer p.trace("fieldDecl")() | |
} | |
pos := p.pos() | |
switch p.tok { | |
case _Name: | |
name := p.name() | |
if p.tok == _Dot || p.tok == _Literal || p.tok == _Semi || p.tok == _Rbrace { | |
// embed oliteral | |
typ := p.qualifiedName(name) | |
tag := p.oliteral() | |
p.addField(styp, pos, nil, typ, tag) | |
return | |
} | |
// new_name_list ntype oliteral | |
names := p.nameList(name) | |
typ := p.type_() | |
tag := p.oliteral() | |
for _, name := range names { | |
p.addField(styp, name.Pos(), name, typ, tag) | |
} | |
case _Lparen: | |
p.next() | |
if p.tok == _Star { | |
// '(' '*' embed ')' oliteral | |
pos := p.pos() | |
p.next() | |
typ := newIndirect(pos, p.qualifiedName(nil)) | |
p.want(_Rparen) | |
tag := p.oliteral() | |
p.addField(styp, pos, nil, typ, tag) | |
p.syntaxError("cannot parenthesize embedded type") | |
} else { | |
// '(' embed ')' oliteral | |
typ := p.qualifiedName(nil) | |
p.want(_Rparen) | |
tag := p.oliteral() | |
p.addField(styp, pos, nil, typ, tag) | |
p.syntaxError("cannot parenthesize embedded type") | |
} | |
case _Star: | |
p.next() | |
if p.got(_Lparen) { | |
// '*' '(' embed ')' oliteral | |
typ := newIndirect(pos, p.qualifiedName(nil)) | |
p.want(_Rparen) | |
tag := p.oliteral() | |
p.addField(styp, pos, nil, typ, tag) | |
p.syntaxError("cannot parenthesize embedded type") | |
} else { | |
// '*' embed oliteral | |
typ := newIndirect(pos, p.qualifiedName(nil)) | |
tag := p.oliteral() | |
p.addField(styp, pos, nil, typ, tag) | |
} | |
default: | |
p.syntaxError("expecting field name or embedded type") | |
p.advance(_Semi, _Rbrace) | |
} | |
} | |
func (p *parser) oliteral() *BasicLit { | |
if p.tok == _Literal { | |
b := new(BasicLit) | |
b.pos = p.pos() | |
b.Value = p.lit | |
b.Kind = p.kind | |
b.Bad = p.bad | |
p.next() | |
return b | |
} | |
return nil | |
} | |
// MethodSpec = MethodName Signature | InterfaceTypeName . | |
// MethodName = identifier . | |
// InterfaceTypeName = TypeName . | |
func (p *parser) methodDecl() *Field { | |
if trace { | |
defer p.trace("methodDecl")() | |
} | |
switch p.tok { | |
case _Name: | |
name := p.name() | |
// accept potential name list but complain | |
hasNameList := false | |
for p.got(_Comma) { | |
p.name() | |
hasNameList = true | |
} | |
if hasNameList { | |
p.syntaxError("name list not allowed in interface type") | |
// already progressed, no need to advance | |
} | |
f := new(Field) | |
f.pos = name.Pos() | |
if p.tok != _Lparen { | |
// packname | |
f.Type = p.qualifiedName(name) | |
return f | |
} | |
f.Name = name | |
f.Type = p.funcType() | |
return f | |
case _Lparen: | |
p.syntaxError("cannot parenthesize embedded type") | |
f := new(Field) | |
f.pos = p.pos() | |
p.next() | |
f.Type = p.qualifiedName(nil) | |
p.want(_Rparen) | |
return f | |
default: | |
p.syntaxError("expecting method or interface name") | |
p.advance(_Semi, _Rbrace) | |
return nil | |
} | |
} | |
// ParameterDecl = [ IdentifierList ] [ "..." ] Type . | |
func (p *parser) paramDeclOrNil() *Field { | |
if trace { | |
defer p.trace("paramDecl")() | |
} | |
f := new(Field) | |
f.pos = p.pos() | |
switch p.tok { | |
case _Name: | |
f.Name = p.name() | |
switch p.tok { | |
case _Name, _Star, _Arrow, _Func, _Lbrack, _Chan, _Map, _Struct, _Interface, _Lparen: | |
// sym name_or_type | |
f.Type = p.type_() | |
case _DotDotDot: | |
// sym dotdotdot | |
f.Type = p.dotsType() | |
case _Dot: | |
// name_or_type | |
// from dotname | |
f.Type = p.dotname(f.Name) | |
f.Name = nil | |
} | |
case _Arrow, _Star, _Func, _Lbrack, _Chan, _Map, _Struct, _Interface, _Lparen: | |
// name_or_type | |
f.Type = p.type_() | |
case _DotDotDot: | |
// dotdotdot | |
f.Type = p.dotsType() | |
default: | |
p.syntaxError("expecting )") | |
p.advance(_Comma, _Rparen) | |
return nil | |
} | |
return f | |
} | |
// ...Type | |
func (p *parser) dotsType() *DotsType { | |
if trace { | |
defer p.trace("dotsType")() | |
} | |
t := new(DotsType) | |
t.pos = p.pos() | |
p.want(_DotDotDot) | |
t.Elem = p.typeOrNil() | |
if t.Elem == nil { | |
t.Elem = p.badExpr() | |
p.syntaxError("final argument in variadic function missing type") | |
} | |
return t | |
} | |
// Parameters = "(" [ ParameterList [ "," ] ] ")" . | |
// ParameterList = ParameterDecl { "," ParameterDecl } . | |
func (p *parser) paramList() (list []*Field) { | |
if trace { | |
defer p.trace("paramList")() | |
} | |
pos := p.pos() | |
var named int // number of parameters that have an explicit name and type | |
p.list(_Lparen, _Comma, _Rparen, func() bool { | |
if par := p.paramDeclOrNil(); par != nil { | |
if debug && par.Name == nil && par.Type == nil { | |
panic("parameter without name or type") | |
} | |
if par.Name != nil && par.Type != nil { | |
named++ | |
} | |
list = append(list, par) | |
} | |
return false | |
}) | |
// distribute parameter types | |
if named == 0 { | |
// all unnamed => found names are named types | |
for _, par := range list { | |
if typ := par.Name; typ != nil { | |
par.Type = typ | |
par.Name = nil | |
} | |
} | |
} else if named != len(list) { | |
// some named => all must be named | |
ok := true | |
var typ Expr | |
for i := len(list) - 1; i >= 0; i-- { | |
if par := list[i]; par.Type != nil { | |
typ = par.Type | |
if par.Name == nil { | |
ok = false | |
n := p.newName("_") | |
n.pos = typ.Pos() // correct position | |
par.Name = n | |
} | |
} else if typ != nil { | |
par.Type = typ | |
} else { | |
// par.Type == nil && typ == nil => we only have a par.Name | |
ok = false | |
t := p.badExpr() | |
t.pos = par.Name.Pos() // correct position | |
par.Type = t | |
} | |
} | |
if !ok { | |
p.syntaxErrorAt(pos, "mixed named and unnamed function parameters") | |
} | |
} | |
return | |
} | |
func (p *parser) badExpr() *BadExpr { | |
b := new(BadExpr) | |
b.pos = p.pos() | |
return b | |
} | |
// ---------------------------------------------------------------------------- | |
// Statements | |
// We represent x++, x-- as assignments x += ImplicitOne, x -= ImplicitOne. | |
// ImplicitOne should not be used elsewhere. | |
var ImplicitOne = &BasicLit{Value: "1"} | |
// SimpleStmt = EmptyStmt | ExpressionStmt | SendStmt | IncDecStmt | Assignment | ShortVarDecl . | |
func (p *parser) simpleStmt(lhs Expr, keyword token) SimpleStmt { | |
if trace { | |
defer p.trace("simpleStmt")() | |
} | |
if keyword == _For && p.tok == _Range { | |
// _Range expr | |
if debug && lhs != nil { | |
panic("invalid call of simpleStmt") | |
} | |
return p.newRangeClause(nil, false) | |
} | |
if lhs == nil { | |
lhs = p.exprList() | |
} | |
if _, ok := lhs.(*ListExpr); !ok && p.tok != _Assign && p.tok != _Define { | |
// expr | |
pos := p.pos() | |
switch p.tok { | |
case _AssignOp: | |
// lhs op= rhs | |
op := p.op | |
p.next() | |
return p.newAssignStmt(pos, op, lhs, p.expr()) | |
case _IncOp: | |
// lhs++ or lhs-- | |
op := p.op | |
p.next() | |
return p.newAssignStmt(pos, op, lhs, ImplicitOne) | |
case _Arrow: | |
// lhs <- rhs | |
s := new(SendStmt) | |
s.pos = pos | |
p.next() | |
s.Chan = lhs | |
s.Value = p.expr() | |
return s | |
default: | |
// expr | |
s := new(ExprStmt) | |
s.pos = lhs.Pos() | |
s.X = lhs | |
return s | |
} | |
} | |
// expr_list | |
switch p.tok { | |
case _Assign, _Define: | |
pos := p.pos() | |
var op Operator | |
if p.tok == _Define { | |
op = Def | |
} | |
p.next() | |
if keyword == _For && p.tok == _Range { | |
// expr_list op= _Range expr | |
return p.newRangeClause(lhs, op == Def) | |
} | |
// expr_list op= expr_list | |
rhs := p.exprList() | |
if x, ok := rhs.(*TypeSwitchGuard); ok && keyword == _Switch && op == Def { | |
if lhs, ok := lhs.(*Name); ok { | |
// switch … lhs := rhs.(type) | |
x.Lhs = lhs | |
s := new(ExprStmt) | |
s.pos = x.Pos() | |
s.X = x | |
return s | |
} | |
} | |
return p.newAssignStmt(pos, op, lhs, rhs) | |
default: | |
p.syntaxError("expecting := or = or comma") | |
p.advance(_Semi, _Rbrace) | |
// make the best of what we have | |
if x, ok := lhs.(*ListExpr); ok { | |
lhs = x.ElemList[0] | |
} | |
s := new(ExprStmt) | |
s.pos = lhs.Pos() | |
s.X = lhs | |
return s | |
} | |
} | |
func (p *parser) newRangeClause(lhs Expr, def bool) *RangeClause { | |
r := new(RangeClause) | |
r.pos = p.pos() | |
p.next() // consume _Range | |
r.Lhs = lhs | |
r.Def = def | |
r.X = p.expr() | |
return r | |
} | |
func (p *parser) newAssignStmt(pos Pos, op Operator, lhs, rhs Expr) *AssignStmt { | |
a := new(AssignStmt) | |
a.pos = pos | |
a.Op = op | |
a.Lhs = lhs | |
a.Rhs = rhs | |
return a | |
} | |
func (p *parser) labeledStmtOrNil(label *Name) Stmt { | |
if trace { | |
defer p.trace("labeledStmt")() | |
} | |
s := new(LabeledStmt) | |
s.pos = p.pos() | |
s.Label = label | |
p.want(_Colon) | |
if p.tok == _Rbrace { | |
// We expect a statement (incl. an empty statement), which must be | |
// terminated by a semicolon. Because semicolons may be omitted before | |
// an _Rbrace, seeing an _Rbrace implies an empty statement. | |
e := new(EmptyStmt) | |
e.pos = p.pos() | |
s.Stmt = e | |
return s | |
} | |
s.Stmt = p.stmtOrNil() | |
if s.Stmt != nil { | |
return s | |
} | |
// report error at line of ':' token | |
p.syntaxErrorAt(s.pos, "missing statement after label") | |
// we are already at the end of the labeled statement - no need to advance | |
return nil // avoids follow-on errors (see e.g., fixedbugs/bug274.go) | |
} | |
// context must be a non-empty string unless we know that p.tok == _Lbrace. | |
func (p *parser) blockStmt(context string) *BlockStmt { | |
if trace { | |
defer p.trace("blockStmt")() | |
} | |
s := new(BlockStmt) | |
s.pos = p.pos() | |
// people coming from C may forget that braces are mandatory in Go | |
if !p.got(_Lbrace) { | |
p.syntaxError("expecting { after " + context) | |
p.advance(_Name, _Rbrace) | |
s.Rbrace = p.pos() // in case we found "}" | |
if p.got(_Rbrace) { | |
return s | |
} | |
} | |
s.List = p.stmtList() | |
s.Rbrace = p.pos() | |
p.want(_Rbrace) | |
return s | |
} | |
func (p *parser) declStmt(f func(*Group) Decl) *DeclStmt { | |
if trace { | |
defer p.trace("declStmt")() | |
} | |
s := new(DeclStmt) | |
s.pos = p.pos() | |
p.next() // _Const, _Type, or _Var | |
s.DeclList = p.appendGroup(nil, f) | |
return s | |
} | |
func (p *parser) forStmt() Stmt { | |
if trace { | |
defer p.trace("forStmt")() | |
} | |
s := new(ForStmt) | |
s.pos = p.pos() | |
s.Init, s.Cond, s.Post = p.header(_For) | |
s.Body = p.blockStmt("for clause") | |
return s | |
} | |
func (p *parser) header(keyword token) (init SimpleStmt, cond Expr, post SimpleStmt) { | |
p.want(keyword) | |
if p.tok == _Lbrace { | |
if keyword == _If { | |
p.syntaxError("missing condition in if statement") | |
} | |
return | |
} | |
// p.tok != _Lbrace | |
outer := p.xnest | |
p.xnest = -1 | |
if p.tok != _Semi { | |
// accept potential varDecl but complain | |
if p.got(_Var) { | |
p.syntaxError(fmt.Sprintf("var declaration not allowed in %s initializer", keyword.String())) | |
} | |
init = p.simpleStmt(nil, keyword) | |
// If we have a range clause, we are done (can only happen for keyword == _For). | |
if _, ok := init.(*RangeClause); ok { | |
p.xnest = outer | |
return | |
} | |
} | |
var condStmt SimpleStmt | |
var semi struct { | |
pos Pos | |
lit string // valid if pos.IsKnown() | |
} | |
if p.tok != _Lbrace { | |
if p.tok == _Semi { | |
semi.pos = p.pos() | |
semi.lit = p.lit | |
p.next() | |
} else { | |
// asking for a '{' rather than a ';' here leads to a better error message | |
p.want(_Lbrace) | |
if p.tok != _Lbrace { | |
p.advance(_Lbrace, _Rbrace) // for better synchronization (e.g., issue #22581) | |
} | |
} | |
if keyword == _For { | |
if p.tok != _Semi { | |
if p.tok == _Lbrace { | |
p.syntaxError("expecting for loop condition") | |
goto done | |
} | |
condStmt = p.simpleStmt(nil, 0 /* range not permitted */) | |
} | |
p.want(_Semi) | |
if p.tok != _Lbrace { | |
post = p.simpleStmt(nil, 0 /* range not permitted */) | |
if a, _ := post.(*AssignStmt); a != nil && a.Op == Def { | |
p.syntaxErrorAt(a.Pos(), "cannot declare in post statement of for loop") | |
} | |
} | |
} else if p.tok != _Lbrace { | |
condStmt = p.simpleStmt(nil, keyword) | |
} | |
} else { | |
condStmt = init | |
init = nil | |
} | |
done: | |
// unpack condStmt | |
switch s := condStmt.(type) { | |
case nil: | |
if keyword == _If && semi.pos.IsKnown() { | |
if semi.lit != "semicolon" { | |
p.syntaxErrorAt(semi.pos, fmt.Sprintf("unexpected %s, expecting { after if clause", semi.lit)) | |
} else { | |
p.syntaxErrorAt(semi.pos, "missing condition in if statement") | |
} | |
} | |
case *ExprStmt: | |
cond = s.X | |
default: | |
// A common syntax error is to write '=' instead of '==', | |
// which turns an expression into an assignment. Provide | |
// a more explicit error message in that case to prevent | |
// further confusion. | |
var str string | |
if as, ok := s.(*AssignStmt); ok && as.Op == 0 { | |
// Emphasize Lhs and Rhs of assignment with parentheses to highlight '='. | |
// Do it always - it's not worth going through the trouble of doing it | |
// only for "complex" left and right sides. | |
str = "assignment (" + String(as.Lhs) + ") = (" + String(as.Rhs) + ")" | |
} else { | |
str = String(s) | |
} | |
p.syntaxErrorAt(s.Pos(), fmt.Sprintf("cannot use %s as value", str)) | |
} | |
p.xnest = outer | |
return | |
} | |
func (p *parser) ifStmt() *IfStmt { | |
if trace { | |
defer p.trace("ifStmt")() | |
} | |
s := new(IfStmt) | |
s.pos = p.pos() | |
s.Init, s.Cond, _ = p.header(_If) | |
s.Then = p.blockStmt("if clause") | |
if p.got(_Else) { | |
switch p.tok { | |
case _If: | |
s.Else = p.ifStmt() | |
case _Lbrace: | |
s.Else = p.blockStmt("") | |
default: | |
p.syntaxError("else must be followed by if or statement block") | |
p.advance(_Name, _Rbrace) | |
} | |
} | |
return s | |
} | |
func (p *parser) switchStmt() *SwitchStmt { | |
if trace { | |
defer p.trace("switchStmt")() | |
} | |
s := new(SwitchStmt) | |
s.pos = p.pos() | |
s.Init, s.Tag, _ = p.header(_Switch) | |
if !p.got(_Lbrace) { | |
p.syntaxError("missing { after switch clause") | |
p.advance(_Case, _Default, _Rbrace) | |
} | |
for p.tok != _EOF && p.tok != _Rbrace { | |
s.Body = append(s.Body, p.caseClause()) | |
} | |
s.Rbrace = p.pos() | |
p.want(_Rbrace) | |
return s | |
} | |
func (p *parser) selectStmt() *SelectStmt { | |
if trace { | |
defer p.trace("selectStmt")() | |
} | |
s := new(SelectStmt) | |
s.pos = p.pos() | |
p.want(_Select) | |
if !p.got(_Lbrace) { | |
p.syntaxError("missing { after select clause") | |
p.advance(_Case, _Default, _Rbrace) | |
} | |
for p.tok != _EOF && p.tok != _Rbrace { | |
s.Body = append(s.Body, p.commClause()) | |
} | |
s.Rbrace = p.pos() | |
p.want(_Rbrace) | |
return s | |
} | |
func (p *parser) caseClause() *CaseClause { | |
if trace { | |
defer p.trace("caseClause")() | |
} | |
c := new(CaseClause) | |
c.pos = p.pos() | |
switch p.tok { | |
case _Case: | |
p.next() | |
c.Cases = p.exprList() | |
case _Default: | |
p.next() | |
default: | |
p.syntaxError("expecting case or default or }") | |
p.advance(_Colon, _Case, _Default, _Rbrace) | |
} | |
c.Colon = p.pos() | |
p.want(_Colon) | |
c.Body = p.stmtList() | |
return c | |
} | |
func (p *parser) commClause() *CommClause { | |
if trace { | |
defer p.trace("commClause")() | |
} | |
c := new(CommClause) | |
c.pos = p.pos() | |
switch p.tok { | |
case _Case: | |
p.next() | |
c.Comm = p.simpleStmt(nil, 0) | |
// The syntax restricts the possible simple statements here to: | |
// | |
// lhs <- x (send statement) | |
// <-x | |
// lhs = <-x | |
// lhs := <-x | |
// | |
// All these (and more) are recognized by simpleStmt and invalid | |
// syntax trees are flagged later, during type checking. | |
// TODO(gri) eventually may want to restrict valid syntax trees | |
// here. | |
case _Default: | |
p.next() | |
default: | |
p.syntaxError("expecting case or default or }") | |
p.advance(_Colon, _Case, _Default, _Rbrace) | |
} | |
c.Colon = p.pos() | |
p.want(_Colon) | |
c.Body = p.stmtList() | |
return c | |
} | |
// Statement = | |
// Declaration | LabeledStmt | SimpleStmt | | |
// GoStmt | ReturnStmt | BreakStmt | ContinueStmt | GotoStmt | | |
// FallthroughStmt | Block | IfStmt | SwitchStmt | SelectStmt | ForStmt | | |
// DeferStmt . | |
func (p *parser) stmtOrNil() Stmt { | |
if trace { | |
defer p.trace("stmt " + p.tok.String())() | |
} | |
// Most statements (assignments) start with an identifier; | |
// look for it first before doing anything more expensive. | |
if p.tok == _Name { | |
p.clearPragma() | |
lhs := p.exprList() | |
if label, ok := lhs.(*Name); ok && p.tok == _Colon { | |
return p.labeledStmtOrNil(label) | |
} | |
return p.simpleStmt(lhs, 0) | |
} | |
switch p.tok { | |
case _Var: | |
return p.declStmt(p.varDecl) | |
case _Const: | |
return p.declStmt(p.constDecl) | |
case _Type: | |
return p.declStmt(p.typeDecl) | |
} | |
p.clearPragma() | |
switch p.tok { | |
case _Lbrace: | |
return p.blockStmt("") | |
case _Operator, _Star: | |
switch p.op { | |
case Add, Sub, Mul, And, Xor, Not: | |
return p.simpleStmt(nil, 0) // unary operators | |
} | |
case _Literal, _Func, _Lparen, // operands | |
_Lbrack, _Struct, _Map, _Chan, _Interface, // composite types | |
_Arrow: // receive operator | |
return p.simpleStmt(nil, 0) | |
case _For: | |
return p.forStmt() | |
case _Switch: | |
return p.switchStmt() | |
case _Select: | |
return p.selectStmt() | |
case _If: | |
return p.ifStmt() | |
case _Fallthrough: | |
s := new(BranchStmt) | |
s.pos = p.pos() | |
p.next() | |
s.Tok = _Fallthrough | |
return s | |
case _Break, _Continue: | |
s := new(BranchStmt) | |
s.pos = p.pos() | |
s.Tok = p.tok | |
p.next() | |
if p.tok == _Name { | |
s.Label = p.name() | |
} | |
return s | |
case _Go, _Defer: | |
return p.callStmt() | |
case _Goto: | |
s := new(BranchStmt) | |
s.pos = p.pos() | |
s.Tok = _Goto | |
p.next() | |
s.Label = p.name() | |
return s | |
case _Return: | |
s := new(ReturnStmt) | |
s.pos = p.pos() | |
p.next() | |
if p.tok != _Semi && p.tok != _Rbrace { | |
s.Results = p.exprList() | |
} | |
return s | |
case _Semi: | |
s := new(EmptyStmt) | |
s.pos = p.pos() | |
return s | |
} | |
return nil | |
} | |
// StatementList = { Statement ";" } . | |
func (p *parser) stmtList() (l []Stmt) { | |
if trace { | |
defer p.trace("stmtList")() | |
} | |
for p.tok != _EOF && p.tok != _Rbrace && p.tok != _Case && p.tok != _Default { | |
s := p.stmtOrNil() | |
p.clearPragma() | |
if s == nil { | |
break | |
} | |
l = append(l, s) | |
// ";" is optional before "}" | |
if !p.got(_Semi) && p.tok != _Rbrace { | |
p.syntaxError("at end of statement") | |
p.advance(_Semi, _Rbrace, _Case, _Default) | |
p.got(_Semi) // avoid spurious empty statement | |
} | |
} | |
return | |
} | |
// Arguments = "(" [ ( ExpressionList | Type [ "," ExpressionList ] ) [ "..." ] [ "," ] ] ")" . | |
func (p *parser) argList() (list []Expr, hasDots bool) { | |
if trace { | |
defer p.trace("argList")() | |
} | |
p.xnest++ | |
p.list(_Lparen, _Comma, _Rparen, func() bool { | |
list = append(list, p.expr()) | |
hasDots = p.got(_DotDotDot) | |
return hasDots | |
}) | |
p.xnest-- | |
return | |
} | |
// ---------------------------------------------------------------------------- | |
// Common productions | |
func (p *parser) newName(value string) *Name { | |
n := new(Name) | |
n.pos = p.pos() | |
n.Value = value | |
return n | |
} | |
func (p *parser) name() *Name { | |
// no tracing to avoid overly verbose output | |
if p.tok == _Name { | |
n := p.newName(p.lit) | |
p.next() | |
return n | |
} | |
n := p.newName("_") | |
p.syntaxError("expecting name") | |
p.advance() | |
return n | |
} | |
// IdentifierList = identifier { "," identifier } . | |
// The first name must be provided. | |
func (p *parser) nameList(first *Name) []*Name { | |
if trace { | |
defer p.trace("nameList")() | |
} | |
if debug && first == nil { | |
panic("first name not provided") | |
} | |
l := []*Name{first} | |
for p.got(_Comma) { | |
l = append(l, p.name()) | |
} | |
return l | |
} | |
// The first name may be provided, or nil. | |
func (p *parser) qualifiedName(name *Name) Expr { | |
if trace { | |
defer p.trace("qualifiedName")() | |
} | |
switch { | |
case name != nil: | |
// name is provided | |
case p.tok == _Name: | |
name = p.name() | |
default: | |
name = p.newName("_") | |
p.syntaxError("expecting name") | |
p.advance(_Dot, _Semi, _Rbrace) | |
} | |
return p.dotname(name) | |
} | |
// ExpressionList = Expression { "," Expression } . | |
func (p *parser) exprList() Expr { | |
if trace { | |
defer p.trace("exprList")() | |
} | |
x := p.expr() | |
if p.got(_Comma) { | |
list := []Expr{x, p.expr()} | |
for p.got(_Comma) { | |
list = append(list, p.expr()) | |
} | |
t := new(ListExpr) | |
t.pos = x.Pos() | |
t.ElemList = list | |
x = t | |
} | |
return x | |
} | |
// unparen removes all parentheses around an expression. | |
func unparen(x Expr) Expr { | |
for { | |
p, ok := x.(*ParenExpr) | |
if !ok { | |
break | |
} | |
x = p.X | |
} | |
return x | |
} |