mirror of
https://github.com/golang/go.git
synced 2025-05-05 15:43:04 +00:00
Fixes #69506. Change-Id: I18215e11f214b12d5f65be1d1740181e427f8817 Reviewed-on: https://go-review.googlesource.com/c/go/+/617015 Reviewed-by: Alan Donovan <adonovan@google.com> Reviewed-by: Robert Griesemer <gri@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2879 lines
64 KiB
Go
2879 lines
64 KiB
Go
// 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"
|
|
"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()
|
|
}
|
|
|
|
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"
|
|
}
|
|
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
|
|
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) // 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()
|
|
|
|
var context string
|
|
if p.got(_Lparen) {
|
|
context = "method"
|
|
rcvr := p.paramList(nil, nil, _Rparen, 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(context)
|
|
} else {
|
|
f.Name = NewName(p.pos(), "_")
|
|
f.Type = new(FuncType)
|
|
f.Type.pos = p.pos()
|
|
msg := "expected name or ("
|
|
if context != "" {
|
|
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 | 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 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_() // othertype
|
|
|
|
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.complitexpr()
|
|
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
|
|
}
|
|
|
|
// 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++
|
|
p.want(_Lbrace)
|
|
x.Rbrace = p.list("composite literal", _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("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)
|
|
}
|
|
}
|
|
|
|
p.want(_Lparen)
|
|
typ.ParamList = p.paramList(nil, nil, _Rparen, false)
|
|
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)
|
|
}
|
|
|
|
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)
|
|
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 {
|
|
t.Elem = p.badExpr()
|
|
p.syntaxError("... is missing type")
|
|
}
|
|
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 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)
|
|
}
|
|
}
|
|
|
|
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", tokstring(keyword)))
|
|
}
|
|
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() (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
|
|
}
|
|
|
|
// 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}
|
|
}
|
|
}
|