| // 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) error(msg string) { p.errorAt(p.pos(), msg) } |
| 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.tok == _Colon { |
| t.Full = true |
| // x[i:j:...] |
| if t.Index[1] == nil { |
| p.error("middle index required in 3-index slice") |
| t.Index[1] = p.badExpr() |
| } |
| p.next() |
| if p.tok != _Rbrack { |
| // x[i:j:k... |
| t.Index[2] = p.expr() |
| } else { |
| p.error("final index required in 3-index slice") |
| t.Index[2] = p.badExpr() |
| } |
| } |
| 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") |
| cond = p.badExpr() |
| } |
| 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") |
| } |
| b := new(BadExpr) |
| b.pos = semi.pos |
| cond = b |
| } |
| 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 |
| } |