| // 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" | |
| "go/build/constraint" | |
| "io" | |
| "path/filepath" | |
| "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 | |
| goVersion string // Go version from //go:build line | |
| top bool // in top of file (before package clause) | |
| 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.top = true | |
| 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 strings.HasPrefix(text, "go:") { | |
| if p.top && strings.HasPrefix(msg, "//go:build") { | |
| if x, err := constraint.Parse(msg); err == nil { | |
| p.goVersion = constraint.GoVersion(x) | |
| } | |
| } | |
| if pragh != nil { | |
| 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" | |
| trimmed := false | |
| if filename == "" && ok2 { | |
| filename = p.base.Filename() | |
| trimmed = p.base.Trimmed() | |
| } else if filename != "" { | |
| filename = filepath.Clean(filename) | |
| if !filepath.IsAbs(filename) { | |
| if dir := filepath.Dir(p.file.Filename()); dir != "." { | |
| filename = filepath.Join(dir, filename) | |
| } | |
| } | |
| } | |
| p.base = NewLineBase(pos, filename, trimmed, 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) { | |
| i := strings.LastIndexByte(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("expected " + 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("expected =") | |
| 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) | |
| } | |
| // errorAt 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, "expected "): | |
| 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: | |
| tok = "name " + p.lit | |
| case _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) | |
| } | |
| // TODO(gri) This may print "unexpected X, expected Y". | |
| // Consider "got X, expected Y" in this case. | |
| p.errorAt(pos, "syntax error: unexpected "+tok+msg) | |
| } | |
| // tokstring returns the English word for selected punctuation tokens | |
| // for more readable error messages. Use tokstring (not tok.String()) | |
| // for user-facing (error) messages; use tok.String() for debugging | |
| // output. | |
| func tokstring(tok token) string { | |
| switch tok { | |
| case _Comma: | |
| return "comma" | |
| case _Semi: | |
| return "semicolon or newline" | |
| } | |
| s := tok.String() | |
| if _Break <= tok && tok <= _Var { | |
| return "keyword " + s | |
| } | |
| return s | |
| } | |
| // 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 | |
| f.GoVersion = p.goVersion | |
| p.top = false | |
| 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 | |
| } | |
| // Accept import declarations anywhere for error tolerance, but complain. | |
| // { ( ImportDecl | TopLevelDecl ) ";" } | |
| prev := _Import | |
| for p.tok != _EOF { | |
| if p.tok == _Import && prev != _Import { | |
| p.syntaxError("imports must appear before other declarations") | |
| } | |
| prev = p.tok | |
| switch p.tok { | |
| case _Import: | |
| p.next() | |
| f.DeclList = p.appendGroup(f.DeclList, p.importDecl) | |
| 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(_Import, _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(_Import, _Const, _Type, _Var, _Func) | |
| } | |
| } | |
| // p.tok == _EOF | |
| p.clearPragma() | |
| f.EOF = p.pos() | |
| 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 of elements, optionally | |
| // followed by sep, and closed by close (or EOF). sep must be one of _Comma | |
| // or _Semi, and close must be one of _Rparen, _Rbrace, or _Rbrack. | |
| // | |
| // For each list element, f is called. Specifically, unless we're at close | |
| // (or EOF), f is called at least once. After f returns true, no more list | |
| // elements are accepted. list returns the position of the closing token. | |
| // | |
| // list = [ f { sep f } [sep] ] close . | |
| func (p *parser) list(context string, sep, close token, f func() bool) Pos { | |
| if debug && (sep != _Comma && sep != _Semi || close != _Rparen && close != _Rbrace && close != _Rbrack) { | |
| panic("invalid sep or close argument for list") | |
| } | |
| done := false | |
| 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("in %s; possibly missing %s or %s", context, 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.next() // must consume "(" after calling clearPragma! | |
| p.list("grouped declaration", _Semi, _Rparen, func() bool { | |
| if x := f(g); x != nil { | |
| list = append(list, x) | |
| } | |
| return false | |
| }) | |
| } else { | |
| if x := f(nil); x != nil { | |
| list = append(list, x) | |
| } | |
| } | |
| 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 = NewName(p.pos(), ".") | |
| p.next() | |
| } | |
| d.Path = p.oliteral() | |
| if d.Path == nil { | |
| p.syntaxError("missing import path") | |
| p.advance(_Semi, _Rparen) | |
| return d | |
| } | |
| if !d.Path.Bad && d.Path.Kind != StringLit { | |
| p.syntaxErrorAt(d.Path.Pos(), "import path must be a string") | |
| d.Path.Bad = true | |
| } | |
| // d.Path.Bad || d.Path.Kind == StringLit | |
| 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 [ TypeParams ] [ "=" ] 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() | |
| if p.tok == _Lbrack { | |
| // d.Name "[" ... | |
| // array/slice type or type parameter list | |
| pos := p.pos() | |
| p.next() | |
| switch p.tok { | |
| case _Name: | |
| // We may have an array type or a type parameter list. | |
| // In either case we expect an expression x (which may | |
| // just be a name, or a more complex expression) which | |
| // we can analyze further. | |
| // | |
| // A type parameter list may have a type bound starting | |
| // with a "[" as in: P []E. In that case, simply parsing | |
| // an expression would lead to an error: P[] is invalid. | |
| // But since index or slice expressions are never constant | |
| // and thus invalid array length expressions, if the name | |
| // is followed by "[" it must be the start of an array or | |
| // slice constraint. Only if we don't see a "[" do we | |
| // need to parse a full expression. Notably, name <- x | |
| // is not a concern because name <- x is a statement and | |
| // not an expression. | |
| var x Expr = p.name() | |
| if p.tok != _Lbrack { | |
| // To parse the expression starting with name, expand | |
| // the call sequence we would get by passing in name | |
| // to parser.expr, and pass in name to parser.pexpr. | |
| p.xnest++ | |
| x = p.binaryExpr(p.pexpr(x, false), 0) | |
| p.xnest-- | |
| } | |
| // Analyze expression x. If we can split x into a type parameter | |
| // name, possibly followed by a type parameter type, we consider | |
| // this the start of a type parameter list, with some caveats: | |
| // a single name followed by "]" tilts the decision towards an | |
| // array declaration; a type parameter type that could also be | |
| // an ordinary expression but which is followed by a comma tilts | |
| // the decision towards a type parameter list. | |
| if pname, ptype := extractName(x, p.tok == _Comma); pname != nil && (ptype != nil || p.tok != _Rbrack) { | |
| // d.Name "[" pname ... | |
| // d.Name "[" pname ptype ... | |
| // d.Name "[" pname ptype "," ... | |
| d.TParamList = p.paramList(pname, ptype, _Rbrack, true, false) // ptype may be nil | |
| d.Alias = p.gotAssign() | |
| d.Type = p.typeOrNil() | |
| } else { | |
| // d.Name "[" pname "]" ... | |
| // d.Name "[" x ... | |
| d.Type = p.arrayType(pos, x) | |
| } | |
| case _Rbrack: | |
| // d.Name "[" "]" ... | |
| p.next() | |
| d.Type = p.sliceType(pos) | |
| default: | |
| // d.Name "[" ... | |
| d.Type = p.arrayType(pos, nil) | |
| } | |
| } else { | |
| 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 | |
| } | |
| // extractName splits the expression x into (name, expr) if syntactically | |
| // x can be written as name expr. The split only happens if expr is a type | |
| // element (per the isTypeElem predicate) or if force is set. | |
| // If x is just a name, the result is (name, nil). If the split succeeds, | |
| // the result is (name, expr). Otherwise the result is (nil, x). | |
| // Examples: | |
| // | |
| // x force name expr | |
| // ------------------------------------ | |
| // P*[]int T/F P *[]int | |
| // P*E T P *E | |
| // P*E F nil P*E | |
| // P([]int) T/F P []int | |
| // P(E) T P E | |
| // P(E) F nil P(E) | |
| // P*E|F|~G T/F P *E|F|~G | |
| // P*E|F|G T P *E|F|G | |
| // P*E|F|G F nil P*E|F|G | |
| func extractName(x Expr, force bool) (*Name, Expr) { | |
| switch x := x.(type) { | |
| case *Name: | |
| return x, nil | |
| case *Operation: | |
| if x.Y == nil { | |
| break // unary expr | |
| } | |
| switch x.Op { | |
| case Mul: | |
| if name, _ := x.X.(*Name); name != nil && (force || isTypeElem(x.Y)) { | |
| // x = name *x.Y | |
| op := *x | |
| op.X, op.Y = op.Y, nil // change op into unary *op.Y | |
| return name, &op | |
| } | |
| case Or: | |
| if name, lhs := extractName(x.X, force || isTypeElem(x.Y)); name != nil && lhs != nil { | |
| // x = name lhs|x.Y | |
| op := *x | |
| op.X = lhs | |
| return name, &op | |
| } | |
| } | |
| case *CallExpr: | |
| if name, _ := x.Fun.(*Name); name != nil { | |
| if len(x.ArgList) == 1 && !x.HasDots && (force || isTypeElem(x.ArgList[0])) { | |
| // The parser doesn't keep unnecessary parentheses. | |
| // Set the flag below to keep them, for testing | |
| // (see go.dev/issues/69206). | |
| const keep_parens = false | |
| if keep_parens { | |
| // x = name (x.ArgList[0]) | |
| px := new(ParenExpr) | |
| px.pos = x.pos // position of "(" in call | |
| px.X = x.ArgList[0] | |
| return name, px | |
| } else { | |
| // x = name x.ArgList[0] | |
| return name, Unparen(x.ArgList[0]) | |
| } | |
| } | |
| } | |
| } | |
| return nil, x | |
| } | |
| // isTypeElem reports whether x is a (possibly parenthesized) type element expression. | |
| // The result is false if x could be a type element OR an ordinary (value) expression. | |
| func isTypeElem(x Expr) bool { | |
| switch x := x.(type) { | |
| case *ArrayType, *StructType, *FuncType, *InterfaceType, *SliceType, *MapType, *ChanType: | |
| return true | |
| case *Operation: | |
| return isTypeElem(x.X) || (x.Y != nil && isTypeElem(x.Y)) || x.Op == Tilde | |
| case *ParenExpr: | |
| return isTypeElem(x.X) | |
| } | |
| return false | |
| } | |
| // 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 [ TypeParams ] ( 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() | |
| hasRecv := false | |
| if p.got(_Lparen) { | |
| hasRecv = true | |
| rcvr := p.paramList(nil, nil, _Rparen, false, false) | |
| 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 { | |
| f.Name = p.name() | |
| f.TParamList, f.Type = p.funcType("") | |
| } else { | |
| f.Name = NewName(p.pos(), "_") | |
| f.Type = new(FuncType) | |
| f.Type.pos = p.pos() | |
| msg := "expected name or (" | |
| if hasRecv { | |
| msg = "expected name" | |
| } | |
| p.syntaxError(msg) | |
| p.advance(_Lbrace, _Semi) | |
| } | |
| 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(nil, 0) | |
| } | |
| // Expression = UnaryExpr | Expression binary_op Expression . | |
| func (p *parser) binaryExpr(x Expr, prec int) Expr { | |
| // don't trace binaryExpr - only leads to overly nested trace output | |
| if x == nil { | |
| x = p.unaryExpr() | |
| } | |
| for (p.tok == _Operator || p.tok == _Star) && p.prec > prec { | |
| t := new(Operation) | |
| t.pos = p.pos() | |
| t.Op = p.op | |
| tprec := p.prec | |
| p.next() | |
| t.X = x | |
| t.Y = p.binaryExpr(nil, 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, Tilde: | |
| 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 <-, expected 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, expected 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(nil, 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(nil, 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 | |
| } | |
| s.Call = x | |
| return s | |
| } | |
| // Operand = Literal | OperandName [ TypeArgs ] | "(" 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 _Lbrace: | |
| return p.literalVal() | |
| 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 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() | |
| _, ftyp := p.funcType("function type") | |
| if p.tok == _Lbrace { | |
| p.xnest++ | |
| f := new(FuncLit) | |
| f.pos = pos | |
| f.Type = ftyp | |
| f.Body = p.funcBody() | |
| p.xnest-- | |
| return f | |
| } | |
| return ftyp | |
| case _Lbrack, _Chan, _Map, _Struct, _Interface: | |
| return p.type_() | |
| default: | |
| x := p.badExpr() | |
| p.syntaxError("expected 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). | |
| } | |
| // pexpr parses a PrimaryExpr. | |
| // | |
| // 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(x Expr, keep_parens bool) Expr { | |
| if trace { | |
| defer p.trace("pexpr")() | |
| } | |
| if x == nil { | |
| 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("expected name or (") | |
| p.advance(_Semi, _Rparen) | |
| } | |
| case _Lbrack: | |
| p.next() | |
| var i Expr | |
| if p.tok != _Colon { | |
| var comma bool | |
| if p.tok == _Rbrack { | |
| // invalid empty instance, slice or index expression; accept but complain | |
| p.syntaxError("expected operand") | |
| i = p.badExpr() | |
| } else { | |
| i, comma = p.typeList(false) | |
| } | |
| if comma || p.tok == _Rbrack { | |
| p.want(_Rbrack) | |
| // x[], x[i,] or x[i, j, ...] | |
| t := new(IndexExpr) | |
| t.pos = pos | |
| t.X = x | |
| t.Index = i | |
| x = t | |
| break | |
| } | |
| } | |
| // x[i:... | |
| // For better error message, don't simply use p.want(_Colon) here (go.dev/issue/47704). | |
| if !p.got(_Colon) { | |
| p.syntaxError("expected comma, : or ]") | |
| p.advance(_Comma, _Colon, _Rbrack) | |
| } | |
| p.xnest++ | |
| t := new(SliceExpr) | |
| t.pos = pos | |
| t.X = x | |
| t.Index[0] = i | |
| 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.xnest-- | |
| p.want(_Rbrack) | |
| x = t | |
| case _Lparen: | |
| t := new(CallExpr) | |
| t.pos = pos | |
| p.next() | |
| 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 possibly a composite literal type | |
| complit_ok = true | |
| } | |
| case *IndexExpr: | |
| if p.xnest >= 0 && !isValue(t) { | |
| // x is possibly 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.literalVal() | |
| n.Type = x | |
| x = n | |
| default: | |
| break loop | |
| } | |
| } | |
| return x | |
| } | |
| // isValue reports whether x syntactically must be a value (and not a type) expression. | |
| func isValue(x Expr) bool { | |
| switch x := x.(type) { | |
| case *BasicLit, *CompositeLit, *FuncLit, *SliceExpr, *AssertExpr, *TypeSwitchGuard, *CallExpr: | |
| return true | |
| case *Operation: | |
| return x.Op != Mul || x.Y != nil // *T may be a type | |
| case *ParenExpr: | |
| return isValue(x.X) | |
| case *IndexExpr: | |
| return isValue(x.X) || isValue(x.Index) | |
| } | |
| return false | |
| } | |
| // LiteralValue = "{" [ ElementList [ "," ] ] "}" . | |
| // ElementList = [ Expression ":" ] Expression { "," [ Expression ":" ] Expression } . | |
| func (p *parser) literalVal() *CompositeLit { | |
| if trace { | |
| defer p.trace("compositeLit")() | |
| } | |
| x := new(CompositeLit) | |
| x.pos = p.pos() | |
| p.xnest++ | |
| p.want(_Lbrace) | |
| x.Rbrace = p.list("composite literal", _Comma, _Rbrace, func() bool { | |
| // value | |
| e := p.expr() | |
| if p.tok == _Colon { | |
| // key ':' value | |
| l := new(KeyValueExpr) | |
| l.pos = p.pos() | |
| p.next() | |
| l.Key = e | |
| l.Value = p.expr() | |
| 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("expected 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() | |
| _, t := p.funcType("function type") | |
| return t | |
| case _Lbrack: | |
| // '[' oexpr ']' ntype | |
| // '[' _DotDotDot ']' ntype | |
| p.next() | |
| if p.got(_Rbrack) { | |
| return p.sliceType(pos) | |
| } | |
| return p.arrayType(pos, nil) | |
| 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.qualifiedName(nil) | |
| case _Lparen: | |
| p.next() | |
| t := p.type_() | |
| p.want(_Rparen) | |
| // The parser doesn't keep unnecessary parentheses. | |
| // Set the flag below to keep them, for testing | |
| // (see e.g. tests for go.dev/issue/68639). | |
| const keep_parens = false | |
| if keep_parens { | |
| px := new(ParenExpr) | |
| px.pos = pos | |
| px.X = t | |
| t = px | |
| } | |
| return t | |
| } | |
| return nil | |
| } | |
| func (p *parser) typeInstance(typ Expr) Expr { | |
| if trace { | |
| defer p.trace("typeInstance")() | |
| } | |
| pos := p.pos() | |
| p.want(_Lbrack) | |
| x := new(IndexExpr) | |
| x.pos = pos | |
| x.X = typ | |
| if p.tok == _Rbrack { | |
| p.syntaxError("expected type argument list") | |
| x.Index = p.badExpr() | |
| } else { | |
| x.Index, _ = p.typeList(true) | |
| } | |
| p.want(_Rbrack) | |
| return x | |
| } | |
| // If context != "", type parameters are not permitted. | |
| func (p *parser) funcType(context string) ([]*Field, *FuncType) { | |
| if trace { | |
| defer p.trace("funcType")() | |
| } | |
| typ := new(FuncType) | |
| typ.pos = p.pos() | |
| var tparamList []*Field | |
| if p.got(_Lbrack) { | |
| if context != "" { | |
| // accept but complain | |
| p.syntaxErrorAt(typ.pos, context+" must have no type parameters") | |
| } | |
| if p.tok == _Rbrack { | |
| p.syntaxError("empty type parameter list") | |
| p.next() | |
| } else { | |
| tparamList = p.paramList(nil, nil, _Rbrack, true, false) | |
| } | |
| } | |
| p.want(_Lparen) | |
| typ.ParamList = p.paramList(nil, nil, _Rparen, false, true) | |
| typ.ResultList = p.funcResult() | |
| return tparamList, typ | |
| } | |
| // "[" has already been consumed, and pos is its position. | |
| // If len != nil it is the already consumed array length. | |
| func (p *parser) arrayType(pos Pos, len Expr) Expr { | |
| if trace { | |
| defer p.trace("arrayType")() | |
| } | |
| if len == nil && !p.got(_DotDotDot) { | |
| p.xnest++ | |
| len = p.expr() | |
| p.xnest-- | |
| } | |
| if p.tok == _Comma { | |
| // Trailing commas are accepted in type parameter | |
| // lists but not in array type declarations. | |
| // Accept for better error handling but complain. | |
| p.syntaxError("unexpected comma; expected ]") | |
| p.next() | |
| } | |
| p.want(_Rbrack) | |
| t := new(ArrayType) | |
| t.pos = pos | |
| t.Len = len | |
| t.Elem = p.type_() | |
| return t | |
| } | |
| // "[" and "]" have already been consumed, and pos is the position of "[". | |
| func (p *parser) sliceType(pos Pos) Expr { | |
| t := new(SliceType) | |
| t.pos = pos | |
| t.Elem = p.type_() | |
| return t | |
| } | |
| 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 | |
| } | |
| // 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.want(_Lbrace) | |
| p.list("struct type", _Semi, _Rbrace, func() bool { | |
| p.fieldDecl(typ) | |
| return false | |
| }) | |
| return typ | |
| } | |
| // InterfaceType = "interface" "{" { ( MethodDecl | EmbeddedElem ) ";" } "}" . | |
| func (p *parser) interfaceType() *InterfaceType { | |
| if trace { | |
| defer p.trace("interfaceType")() | |
| } | |
| typ := new(InterfaceType) | |
| typ.pos = p.pos() | |
| p.want(_Interface) | |
| p.want(_Lbrace) | |
| p.list("interface type", _Semi, _Rbrace, func() bool { | |
| var f *Field | |
| if p.tok == _Name { | |
| f = p.methodDecl() | |
| } | |
| if f == nil || f.Name == nil { | |
| f = p.embeddedElem(f) | |
| } | |
| typ.MethodList = append(typ.MethodList, f) | |
| return false | |
| }) | |
| return typ | |
| } | |
| // Result = Parameters | Type . | |
| func (p *parser) funcResult() []*Field { | |
| if trace { | |
| defer p.trace("funcResult")() | |
| } | |
| if p.got(_Lparen) { | |
| return p.paramList(nil, nil, _Rparen, false, false) | |
| } | |
| 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 { | |
| // embedded type | |
| typ := p.qualifiedName(name) | |
| tag := p.oliteral() | |
| p.addField(styp, pos, nil, typ, tag) | |
| break | |
| } | |
| // name1, name2, ... Type [ tag ] | |
| names := p.nameList(name) | |
| var typ Expr | |
| // Careful dance: We don't know if we have an embedded instantiated | |
| // type T[P1, P2, ...] or a field T of array/slice type [P]E or []E. | |
| if len(names) == 1 && p.tok == _Lbrack { | |
| typ = p.arrayOrTArgs() | |
| if typ, ok := typ.(*IndexExpr); ok { | |
| // embedded type T[P1, P2, ...] | |
| typ.X = name // name == names[0] | |
| tag := p.oliteral() | |
| p.addField(styp, pos, nil, typ, tag) | |
| break | |
| } | |
| } else { | |
| // T P | |
| typ = p.type_() | |
| } | |
| tag := p.oliteral() | |
| for _, name := range names { | |
| p.addField(styp, name.Pos(), name, typ, tag) | |
| } | |
| case _Star: | |
| p.next() | |
| var typ Expr | |
| if p.tok == _Lparen { | |
| // *(T) | |
| p.syntaxError("cannot parenthesize embedded type") | |
| p.next() | |
| typ = p.qualifiedName(nil) | |
| p.got(_Rparen) // no need to complain if missing | |
| } else { | |
| // *T | |
| typ = p.qualifiedName(nil) | |
| } | |
| tag := p.oliteral() | |
| p.addField(styp, pos, nil, newIndirect(pos, typ), tag) | |
| case _Lparen: | |
| p.syntaxError("cannot parenthesize embedded type") | |
| p.next() | |
| var typ Expr | |
| if p.tok == _Star { | |
| // (*T) | |
| pos := p.pos() | |
| p.next() | |
| typ = newIndirect(pos, p.qualifiedName(nil)) | |
| } else { | |
| // (T) | |
| typ = p.qualifiedName(nil) | |
| } | |
| p.got(_Rparen) // no need to complain if missing | |
| tag := p.oliteral() | |
| p.addField(styp, pos, nil, typ, tag) | |
| default: | |
| p.syntaxError("expected field name or embedded type") | |
| p.advance(_Semi, _Rbrace) | |
| } | |
| } | |
| func (p *parser) arrayOrTArgs() Expr { | |
| if trace { | |
| defer p.trace("arrayOrTArgs")() | |
| } | |
| pos := p.pos() | |
| p.want(_Lbrack) | |
| if p.got(_Rbrack) { | |
| return p.sliceType(pos) | |
| } | |
| // x [n]E or x[n,], x[n1, n2], ... | |
| n, comma := p.typeList(false) | |
| p.want(_Rbrack) | |
| if !comma { | |
| if elem := p.typeOrNil(); elem != nil { | |
| // x [n]E | |
| t := new(ArrayType) | |
| t.pos = pos | |
| t.Len = n | |
| t.Elem = elem | |
| return t | |
| } | |
| } | |
| // x[n,], x[n1, n2], ... | |
| t := new(IndexExpr) | |
| t.pos = pos | |
| // t.X will be filled in by caller | |
| t.Index = n | |
| return t | |
| } | |
| 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")() | |
| } | |
| f := new(Field) | |
| f.pos = p.pos() | |
| name := p.name() | |
| const context = "interface method" | |
| switch p.tok { | |
| case _Lparen: | |
| // method | |
| f.Name = name | |
| _, f.Type = p.funcType(context) | |
| case _Lbrack: | |
| // Careful dance: We don't know if we have a generic method m[T C](x T) | |
| // or an embedded instantiated type T[P1, P2] (we accept generic methods | |
| // for generality and robustness of parsing but complain with an error). | |
| pos := p.pos() | |
| p.next() | |
| // Empty type parameter or argument lists are not permitted. | |
| // Treat as if [] were absent. | |
| if p.tok == _Rbrack { | |
| // name[] | |
| pos := p.pos() | |
| p.next() | |
| if p.tok == _Lparen { | |
| // name[]( | |
| p.errorAt(pos, "empty type parameter list") | |
| f.Name = name | |
| _, f.Type = p.funcType(context) | |
| } else { | |
| p.errorAt(pos, "empty type argument list") | |
| f.Type = name | |
| } | |
| break | |
| } | |
| // A type argument list looks like a parameter list with only | |
| // types. Parse a parameter list and decide afterwards. | |
| list := p.paramList(nil, nil, _Rbrack, false, false) | |
| if len(list) == 0 { | |
| // The type parameter list is not [] but we got nothing | |
| // due to other errors (reported by paramList). Treat | |
| // as if [] were absent. | |
| if p.tok == _Lparen { | |
| f.Name = name | |
| _, f.Type = p.funcType(context) | |
| } else { | |
| f.Type = name | |
| } | |
| break | |
| } | |
| // len(list) > 0 | |
| if list[0].Name != nil { | |
| // generic method | |
| f.Name = name | |
| _, f.Type = p.funcType(context) | |
| p.errorAt(pos, "interface method must have no type parameters") | |
| break | |
| } | |
| // embedded instantiated type | |
| t := new(IndexExpr) | |
| t.pos = pos | |
| t.X = name | |
| if len(list) == 1 { | |
| t.Index = list[0].Type | |
| } else { | |
| // len(list) > 1 | |
| l := new(ListExpr) | |
| l.pos = list[0].Pos() | |
| l.ElemList = make([]Expr, len(list)) | |
| for i := range list { | |
| l.ElemList[i] = list[i].Type | |
| } | |
| t.Index = l | |
| } | |
| f.Type = t | |
| default: | |
| // embedded type | |
| f.Type = p.qualifiedName(name) | |
| } | |
| return f | |
| } | |
| // EmbeddedElem = MethodSpec | EmbeddedTerm { "|" EmbeddedTerm } . | |
| func (p *parser) embeddedElem(f *Field) *Field { | |
| if trace { | |
| defer p.trace("embeddedElem")() | |
| } | |
| if f == nil { | |
| f = new(Field) | |
| f.pos = p.pos() | |
| f.Type = p.embeddedTerm() | |
| } | |
| for p.tok == _Operator && p.op == Or { | |
| t := new(Operation) | |
| t.pos = p.pos() | |
| t.Op = Or | |
| p.next() | |
| t.X = f.Type | |
| t.Y = p.embeddedTerm() | |
| f.Type = t | |
| } | |
| return f | |
| } | |
| // EmbeddedTerm = [ "~" ] Type . | |
| func (p *parser) embeddedTerm() Expr { | |
| if trace { | |
| defer p.trace("embeddedTerm")() | |
| } | |
| if p.tok == _Operator && p.op == Tilde { | |
| t := new(Operation) | |
| t.pos = p.pos() | |
| t.Op = Tilde | |
| p.next() | |
| t.X = p.type_() | |
| return t | |
| } | |
| t := p.typeOrNil() | |
| if t == nil { | |
| t = p.badExpr() | |
| p.syntaxError("expected ~ term or type") | |
| p.advance(_Operator, _Semi, _Rparen, _Rbrack, _Rbrace) | |
| } | |
| return t | |
| } | |
| // ParameterDecl = [ IdentifierList ] [ "..." ] Type . | |
| func (p *parser) paramDeclOrNil(name *Name, follow token) *Field { | |
| if trace { | |
| defer p.trace("paramDeclOrNil")() | |
| } | |
| // type set notation is ok in type parameter lists | |
| typeSetsOk := follow == _Rbrack | |
| pos := p.pos() | |
| if name != nil { | |
| pos = name.pos | |
| } else if typeSetsOk && p.tok == _Operator && p.op == Tilde { | |
| // "~" ... | |
| return p.embeddedElem(nil) | |
| } | |
| f := new(Field) | |
| f.pos = pos | |
| if p.tok == _Name || name != nil { | |
| // name | |
| if name == nil { | |
| name = p.name() | |
| } | |
| if p.tok == _Lbrack { | |
| // name "[" ... | |
| f.Type = p.arrayOrTArgs() | |
| if typ, ok := f.Type.(*IndexExpr); ok { | |
| // name "[" ... "]" | |
| typ.X = name | |
| } else { | |
| // name "[" n "]" E | |
| f.Name = name | |
| } | |
| if typeSetsOk && p.tok == _Operator && p.op == Or { | |
| // name "[" ... "]" "|" ... | |
| // name "[" n "]" E "|" ... | |
| f = p.embeddedElem(f) | |
| } | |
| return f | |
| } | |
| if p.tok == _Dot { | |
| // name "." ... | |
| f.Type = p.qualifiedName(name) | |
| if typeSetsOk && p.tok == _Operator && p.op == Or { | |
| // name "." name "|" ... | |
| f = p.embeddedElem(f) | |
| } | |
| return f | |
| } | |
| if typeSetsOk && p.tok == _Operator && p.op == Or { | |
| // name "|" ... | |
| f.Type = name | |
| return p.embeddedElem(f) | |
| } | |
| f.Name = name | |
| } | |
| if p.tok == _DotDotDot { | |
| // [name] "..." ... | |
| t := new(DotsType) | |
| t.pos = p.pos() | |
| p.next() | |
| t.Elem = p.typeOrNil() | |
| if t.Elem == nil { | |
| f.Type = p.badExpr() | |
| p.syntaxError("... is missing type") | |
| } else { | |
| f.Type = t | |
| } | |
| return f | |
| } | |
| if typeSetsOk && p.tok == _Operator && p.op == Tilde { | |
| // [name] "~" ... | |
| f.Type = p.embeddedElem(nil).Type | |
| return f | |
| } | |
| f.Type = p.typeOrNil() | |
| if typeSetsOk && p.tok == _Operator && p.op == Or && f.Type != nil { | |
| // [name] type "|" | |
| f = p.embeddedElem(f) | |
| } | |
| if f.Name != nil || f.Type != nil { | |
| return f | |
| } | |
| p.syntaxError("expected " + tokstring(follow)) | |
| p.advance(_Comma, follow) | |
| return nil | |
| } | |
| // Parameters = "(" [ ParameterList [ "," ] ] ")" . | |
| // ParameterList = ParameterDecl { "," ParameterDecl } . | |
| // "(" or "[" has already been consumed. | |
| // If name != nil, it is the first name after "(" or "[". | |
| // If typ != nil, name must be != nil, and (name, typ) is the first field in the list. | |
| // In the result list, either all fields have a name, or no field has a name. | |
| func (p *parser) paramList(name *Name, typ Expr, close token, requireNames, dddok bool) (list []*Field) { | |
| if trace { | |
| defer p.trace("paramList")() | |
| } | |
| // p.list won't invoke its function argument if we're at the end of the | |
| // parameter list. If we have a complete field, handle this case here. | |
| if name != nil && typ != nil && p.tok == close { | |
| p.next() | |
| par := new(Field) | |
| par.pos = name.pos | |
| par.Name = name | |
| par.Type = typ | |
| return []*Field{par} | |
| } | |
| var named int // number of parameters that have an explicit name and type | |
| var typed int // number of parameters that have an explicit type | |
| end := p.list("parameter list", _Comma, close, func() bool { | |
| var par *Field | |
| if typ != nil { | |
| if debug && name == nil { | |
| panic("initial type provided without name") | |
| } | |
| par = new(Field) | |
| par.pos = name.pos | |
| par.Name = name | |
| par.Type = typ | |
| } else { | |
| par = p.paramDeclOrNil(name, close) | |
| } | |
| name = nil // 1st name was consumed if present | |
| typ = nil // 1st type was consumed if present | |
| if par != nil { | |
| if debug && par.Name == nil && par.Type == nil { | |
| panic("parameter without name or type") | |
| } | |
| if par.Name != nil && par.Type != nil { | |
| named++ | |
| } | |
| if par.Type != nil { | |
| typed++ | |
| } | |
| list = append(list, par) | |
| } | |
| return false | |
| }) | |
| if len(list) == 0 { | |
| return | |
| } | |
| // distribute parameter types (len(list) > 0) | |
| if named == 0 && !requireNames { | |
| // all unnamed and we're not in a type parameter list => 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 or we're in a type parameter list => all must be named | |
| var errPos Pos // left-most error position (or unknown) | |
| var typ Expr // current type (from right to left) | |
| for i := len(list) - 1; i >= 0; i-- { | |
| par := list[i] | |
| if par.Type != nil { | |
| typ = par.Type | |
| if par.Name == nil { | |
| errPos = StartPos(typ) | |
| par.Name = NewName(errPos, "_") | |
| } | |
| } else if typ != nil { | |
| par.Type = typ | |
| } else { | |
| // par.Type == nil && typ == nil => we only have a par.Name | |
| errPos = par.Name.Pos() | |
| t := p.badExpr() | |
| t.pos = errPos // correct position | |
| par.Type = t | |
| } | |
| } | |
| if errPos.IsKnown() { | |
| // Not all parameters are named because named != len(list). | |
| // If named == typed, there must be parameters that have no types. | |
| // They must be at the end of the parameter list, otherwise types | |
| // would have been filled in by the right-to-left sweep above and | |
| // there would be no error. | |
| // If requireNames is set, the parameter list is a type parameter | |
| // list. | |
| var msg string | |
| if named == typed { | |
| errPos = end // position error at closing token ) or ] | |
| if requireNames { | |
| msg = "missing type constraint" | |
| } else { | |
| msg = "missing parameter type" | |
| } | |
| } else { | |
| if requireNames { | |
| msg = "missing type parameter name" | |
| // go.dev/issue/60812 | |
| if len(list) == 1 { | |
| msg += " or invalid array length" | |
| } | |
| } else { | |
| msg = "missing parameter name" | |
| } | |
| } | |
| p.syntaxErrorAt(errPos, msg) | |
| } | |
| } | |
| // check use of ... | |
| first := true // only report first occurrence | |
| for i, f := range list { | |
| if t, _ := f.Type.(*DotsType); t != nil && (!dddok || i+1 < len(list)) { | |
| if first { | |
| first = false | |
| if dddok { | |
| p.errorAt(t.pos, "can only use ... with final parameter") | |
| } else { | |
| p.errorAt(t.pos, "invalid use of ...") | |
| } | |
| } | |
| // use T instead of invalid ...T | |
| f.Type = t.Elem | |
| } | |
| } | |
| return | |
| } | |
| func (p *parser) badExpr() *BadExpr { | |
| b := new(BadExpr) | |
| b.pos = p.pos() | |
| return b | |
| } | |
| // ---------------------------------------------------------------------------- | |
| // Statements | |
| // 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, nil) | |
| 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("expected := 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("expected { 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., go.dev/issue/22581) | |
| } | |
| } | |
| if keyword == _For { | |
| if p.tok != _Semi { | |
| if p.tok == _Lbrace { | |
| p.syntaxError("expected 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, expected { 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 complex Lhs and Rhs of assignment with parentheses to highlight '='. | |
| str = "assignment " + emphasize(as.Lhs) + " = " + emphasize(as.Rhs) | |
| } else { | |
| str = String(s) | |
| } | |
| p.syntaxErrorAt(s.Pos(), fmt.Sprintf("cannot use %s as value", str)) | |
| } | |
| p.xnest = outer | |
| return | |
| } | |
| // emphasize returns a string representation of x, with (top-level) | |
| // binary expressions emphasized by enclosing them in parentheses. | |
| func emphasize(x Expr) string { | |
| s := String(x) | |
| if op, _ := x.(*Operation); op != nil && op.Y != nil { | |
| // binary expression | |
| return "(" + s + ")" | |
| } | |
| return s | |
| } | |
| 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("expected 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. | |
| case _Default: | |
| p.next() | |
| default: | |
| p.syntaxError("expected case or default or }") | |
| p.advance(_Colon, _Case, _Default, _Rbrace) | |
| } | |
| c.Colon = p.pos() | |
| p.want(_Colon) | |
| c.Body = p.stmtList() | |
| return c | |
| } | |
| // stmtOrNil parses a statement if one is present, or else returns nil. | |
| // | |
| // 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() (list []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 | |
| } | |
| list = append(list, 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 | |
| } | |
| // argList parses a possibly empty, comma-separated list of arguments, | |
| // optionally followed by a comma (if not empty), and closed by ")". | |
| // The last argument may be followed by "...". | |
| // | |
| // argList = [ arg { "," arg } [ "..." ] [ "," ] ] ")" . | |
| func (p *parser) argList() (list []Expr, hasDots bool) { | |
| if trace { | |
| defer p.trace("argList")() | |
| } | |
| p.xnest++ | |
| p.list("argument list", _Comma, _Rparen, func() bool { | |
| list = append(list, p.expr()) | |
| hasDots = p.got(_DotDotDot) | |
| return hasDots | |
| }) | |
| p.xnest-- | |
| return | |
| } | |
| // ---------------------------------------------------------------------------- | |
| // Common productions | |
| func (p *parser) name() *Name { | |
| // no tracing to avoid overly verbose output | |
| if p.tok == _Name { | |
| n := NewName(p.pos(), p.lit) | |
| p.next() | |
| return n | |
| } | |
| n := NewName(p.pos(), "_") | |
| p.syntaxError("expected 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")() | |
| } | |
| var x Expr | |
| switch { | |
| case name != nil: | |
| x = name | |
| case p.tok == _Name: | |
| x = p.name() | |
| default: | |
| x = NewName(p.pos(), "_") | |
| p.syntaxError("expected name") | |
| p.advance(_Dot, _Semi, _Rbrace) | |
| } | |
| if p.tok == _Dot { | |
| s := new(SelectorExpr) | |
| s.pos = p.pos() | |
| p.next() | |
| s.X = x | |
| s.Sel = p.name() | |
| x = s | |
| } | |
| if p.tok == _Lbrack { | |
| x = p.typeInstance(x) | |
| } | |
| return x | |
| } | |
| // 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 | |
| } | |
| // typeList parses a non-empty, comma-separated list of types, | |
| // optionally followed by a comma. If strict is set to false, | |
| // the first element may also be a (non-type) expression. | |
| // If there is more than one argument, the result is a *ListExpr. | |
| // The comma result indicates whether there was a (separating or | |
| // trailing) comma. | |
| // | |
| // typeList = arg { "," arg } [ "," ] . | |
| func (p *parser) typeList(strict bool) (x Expr, comma bool) { | |
| if trace { | |
| defer p.trace("typeList")() | |
| } | |
| p.xnest++ | |
| if strict { | |
| x = p.type_() | |
| } else { | |
| x = p.expr() | |
| } | |
| if p.got(_Comma) { | |
| comma = true | |
| if t := p.typeOrNil(); t != nil { | |
| list := []Expr{x, t} | |
| for p.got(_Comma) { | |
| if t = p.typeOrNil(); t == nil { | |
| break | |
| } | |
| list = append(list, t) | |
| } | |
| l := new(ListExpr) | |
| l.pos = x.Pos() // == list[0].Pos() | |
| l.ElemList = list | |
| x = l | |
| } | |
| } | |
| p.xnest-- | |
| return | |
| } | |
| // Unparen returns e with any enclosing parentheses stripped. | |
| func Unparen(x Expr) Expr { | |
| for { | |
| p, ok := x.(*ParenExpr) | |
| if !ok { | |
| break | |
| } | |
| x = p.X | |
| } | |
| return x | |
| } | |
| // UnpackListExpr unpacks a *ListExpr into a []Expr. | |
| func UnpackListExpr(x Expr) []Expr { | |
| switch x := x.(type) { | |
| case nil: | |
| return nil | |
| case *ListExpr: | |
| return x.ElemList | |
| default: | |
| return []Expr{x} | |
| } | |
| } |