2016-03-04 17:09:08 -08:00
|
|
|
// Copyright 2016 The Go Authors. All rights reserved.
|
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
|
|
package syntax
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"io"
|
|
|
|
|
"strings"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const debug = false
|
|
|
|
|
const trace = false
|
|
|
|
|
|
2016-06-06 17:59:05 -07:00
|
|
|
// The old gc parser assigned line numbers very inconsistently depending
|
|
|
|
|
// on when it happened to construct AST nodes. To make transitioning to the
|
|
|
|
|
// new AST easier, we try to mimick the behavior as much as possible.
|
|
|
|
|
const gcCompat = true
|
|
|
|
|
|
2016-03-04 17:09:08 -08:00
|
|
|
type parser struct {
|
|
|
|
|
scanner
|
|
|
|
|
|
|
|
|
|
fnest int // function nesting level (for error handling)
|
|
|
|
|
xnest int // expression nesting level (for complit ambiguity resolution)
|
|
|
|
|
indent []byte // tracing support
|
|
|
|
|
|
|
|
|
|
nerrors int // error count
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-30 16:31:53 -07:00
|
|
|
func (p *parser) init(src io.Reader, errh ErrorHandler, pragh PragmaHandler) {
|
2016-03-04 17:09:08 -08:00
|
|
|
p.scanner.init(src, func(pos, line int, msg string) {
|
|
|
|
|
p.nerrors++
|
|
|
|
|
if !debug && errh != nil {
|
|
|
|
|
errh(pos, line, msg)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
panic(fmt.Sprintf("%d: %s\n", line, msg))
|
2016-08-30 16:31:53 -07:00
|
|
|
}, pragh)
|
2016-03-04 17:09:08 -08:00
|
|
|
|
|
|
|
|
p.fnest = 0
|
|
|
|
|
p.xnest = 0
|
|
|
|
|
p.indent = nil
|
|
|
|
|
|
|
|
|
|
p.nerrors = 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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.syntax_error("expecting " + tok.String())
|
|
|
|
|
p.advance()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
// Error handling
|
|
|
|
|
|
|
|
|
|
// syntax_error reports a syntax error at the current line.
|
|
|
|
|
func (p *parser) syntax_error(msg string) {
|
2016-06-06 17:59:05 -07:00
|
|
|
p.syntax_error_at(p.pos, p.line, msg)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Like syntax_error, but reports error at given line rather than current lexer line.
|
|
|
|
|
func (p *parser) syntax_error_at(pos, line int, msg string) {
|
2016-03-04 17:09:08 -08:00
|
|
|
if trace {
|
|
|
|
|
defer p.trace("syntax_error (" + msg + ")")()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if p.tok == _EOF && p.nerrors > 0 {
|
|
|
|
|
return // avoid meaningless follow-up errors
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// add punctuation etc. as needed to msg
|
|
|
|
|
switch {
|
|
|
|
|
case msg == "":
|
|
|
|
|
// nothing to do
|
|
|
|
|
case strings.HasPrefix(msg, "in"), strings.HasPrefix(msg, "at"), strings.HasPrefix(msg, "after"):
|
|
|
|
|
msg = " " + msg
|
|
|
|
|
case strings.HasPrefix(msg, "expecting"):
|
|
|
|
|
msg = ", " + msg
|
|
|
|
|
default:
|
|
|
|
|
// plain error - we don't care about current token
|
2016-06-06 17:59:05 -07:00
|
|
|
p.error_at(pos, line, "syntax error: "+msg)
|
2016-03-04 17:09:08 -08:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// determine token string
|
|
|
|
|
var tok string
|
|
|
|
|
switch p.tok {
|
2016-06-06 17:59:05 -07:00
|
|
|
case _Name:
|
2016-03-04 17:09:08 -08:00
|
|
|
tok = p.lit
|
2016-06-06 17:59:05 -07:00
|
|
|
case _Literal:
|
|
|
|
|
tok = "literal " + p.lit
|
2016-03-04 17:09:08 -08:00
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-06 17:59:05 -07:00
|
|
|
p.error_at(pos, line, "syntax error: unexpected "+tok+msg)
|
2016-03-04 17:09:08 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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<<_Func |
|
|
|
|
|
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 token is consumed to ensure progress.
|
|
|
|
|
func (p *parser) advance(followlist ...token) {
|
|
|
|
|
if len(followlist) == 0 {
|
|
|
|
|
p.next()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// compute follow set
|
|
|
|
|
// TODO(gri) the args are constants - do as constant expressions?
|
|
|
|
|
var followset uint64 = 1 << _EOF // never skip over EOF
|
|
|
|
|
for _, tok := range followlist {
|
|
|
|
|
followset |= 1 << tok
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for !(contains(followset, p.tok) || p.fnest > 0 && contains(stopset, p.tok)) {
|
|
|
|
|
p.next()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func tokstring(tok token) string {
|
|
|
|
|
switch tok {
|
|
|
|
|
case _EOF:
|
|
|
|
|
return "EOF"
|
|
|
|
|
case _Comma:
|
|
|
|
|
return "comma"
|
|
|
|
|
case _Semi:
|
|
|
|
|
return "semicolon or newline"
|
|
|
|
|
}
|
|
|
|
|
return tok.String()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// usage: defer p.trace(msg)()
|
|
|
|
|
func (p *parser) trace(msg string) func() {
|
|
|
|
|
fmt.Printf("%5d: %s%s (\n", p.line, p.indent, 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
|
|
|
|
|
}
|
|
|
|
|
fmt.Printf("%5d: %s)\n", p.line, p.indent)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
// 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.
|
|
|
|
|
|
|
|
|
|
// SourceFile = PackageClause ";" { ImportDecl ";" } { TopLevelDecl ";" } .
|
|
|
|
|
func (p *parser) file() *File {
|
|
|
|
|
if trace {
|
|
|
|
|
defer p.trace("file")()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
f := new(File)
|
|
|
|
|
f.init(p)
|
|
|
|
|
|
|
|
|
|
// PackageClause
|
2016-06-06 17:59:05 -07:00
|
|
|
if !p.got(_Package) {
|
|
|
|
|
p.syntax_error("package statement must be first")
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2016-03-04 17:09:08 -08:00
|
|
|
f.PkgName = p.name()
|
|
|
|
|
p.want(_Semi)
|
|
|
|
|
|
|
|
|
|
// don't bother continuing if package clause has errors
|
|
|
|
|
if p.nerrors > 0 {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// { ImportDecl ";" }
|
|
|
|
|
for p.got(_Import) {
|
|
|
|
|
f.DeclList = p.appendGroup(f.DeclList, p.importDecl)
|
|
|
|
|
p.want(_Semi)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// { TopLevelDecl ";" }
|
|
|
|
|
for p.tok != _EOF {
|
|
|
|
|
switch p.tok {
|
|
|
|
|
case _Const:
|
|
|
|
|
p.next()
|
|
|
|
|
f.DeclList = p.appendGroup(f.DeclList, p.constDecl)
|
|
|
|
|
|
|
|
|
|
case _Type:
|
|
|
|
|
p.next()
|
|
|
|
|
f.DeclList = p.appendGroup(f.DeclList, p.typeDecl)
|
|
|
|
|
|
|
|
|
|
case _Var:
|
|
|
|
|
p.next()
|
|
|
|
|
f.DeclList = p.appendGroup(f.DeclList, p.varDecl)
|
|
|
|
|
|
|
|
|
|
case _Func:
|
|
|
|
|
p.next()
|
|
|
|
|
f.DeclList = append(f.DeclList, p.funcDecl())
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
if p.tok == _Lbrace && len(f.DeclList) > 0 && emptyFuncDecl(f.DeclList[len(f.DeclList)-1]) {
|
|
|
|
|
// opening { of function declaration on next line
|
|
|
|
|
p.syntax_error("unexpected semicolon or newline before {")
|
|
|
|
|
} else {
|
|
|
|
|
p.syntax_error("non-declaration statement outside function body")
|
|
|
|
|
}
|
|
|
|
|
p.advance(_Const, _Type, _Var, _Func)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-30 16:31:53 -07:00
|
|
|
// Reset p.pragma BEFORE advancing to the next token (consuming ';')
|
|
|
|
|
// since comments before may set pragmas for the next function decl.
|
|
|
|
|
p.pragma = 0
|
|
|
|
|
|
2016-03-04 17:09:08 -08:00
|
|
|
if p.tok != _EOF && !p.got(_Semi) {
|
|
|
|
|
p.syntax_error("after top level declaration")
|
|
|
|
|
p.advance(_Const, _Type, _Var, _Func)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// p.tok == _EOF
|
|
|
|
|
|
|
|
|
|
f.Lines = p.source.line
|
|
|
|
|
|
|
|
|
|
return f
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func emptyFuncDecl(dcl Decl) bool {
|
|
|
|
|
f, ok := dcl.(*FuncDecl)
|
|
|
|
|
return ok && f.Body == nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
// Declarations
|
|
|
|
|
|
|
|
|
|
// appendGroup(f) = f | "(" { f ";" } ")" .
|
|
|
|
|
func (p *parser) appendGroup(list []Decl, f func(*Group) Decl) []Decl {
|
|
|
|
|
if p.got(_Lparen) {
|
|
|
|
|
g := new(Group)
|
|
|
|
|
for p.tok != _EOF && p.tok != _Rparen {
|
|
|
|
|
list = append(list, f(g))
|
|
|
|
|
if !p.osemi(_Rparen) {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
p.want(_Rparen)
|
|
|
|
|
return list
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return append(list, f(nil))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *parser) importDecl(group *Group) Decl {
|
|
|
|
|
if trace {
|
|
|
|
|
defer p.trace("importDecl")()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
d := new(ImportDecl)
|
|
|
|
|
d.init(p)
|
|
|
|
|
|
|
|
|
|
switch p.tok {
|
|
|
|
|
case _Name:
|
|
|
|
|
d.LocalPkgName = p.name()
|
|
|
|
|
case _Dot:
|
|
|
|
|
n := new(Name)
|
|
|
|
|
n.init(p)
|
|
|
|
|
n.Value = "."
|
|
|
|
|
d.LocalPkgName = n
|
|
|
|
|
p.next()
|
|
|
|
|
}
|
2016-06-06 17:59:05 -07:00
|
|
|
if p.tok == _Literal && (gcCompat || p.kind == StringLit) {
|
2016-03-04 17:09:08 -08:00
|
|
|
d.Path = p.oliteral()
|
|
|
|
|
} else {
|
|
|
|
|
p.syntax_error("missing import path; require quoted string")
|
|
|
|
|
p.advance(_Semi, _Rparen)
|
|
|
|
|
}
|
|
|
|
|
d.Group = group
|
|
|
|
|
|
|
|
|
|
return d
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-15 17:40:26 -07:00
|
|
|
// AliasSpec = identifier "=>" [ PackageName "." ] identifier .
|
|
|
|
|
func (p *parser) aliasDecl(tok token, name *Name, group *Group) Decl {
|
|
|
|
|
// no tracing since this is already called from a const/type/var/funcDecl
|
|
|
|
|
|
|
|
|
|
d := new(AliasDecl)
|
|
|
|
|
d.initFrom(&name.node)
|
|
|
|
|
|
2016-10-21 17:23:01 -07:00
|
|
|
// lhs identifier and "=>" have been consumed already
|
|
|
|
|
|
2016-09-15 17:40:26 -07:00
|
|
|
d.Tok = tok
|
|
|
|
|
d.Name = name
|
|
|
|
|
d.Orig = p.dotname(p.name())
|
|
|
|
|
d.Group = group
|
|
|
|
|
|
|
|
|
|
return d
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ConstSpec = IdentifierList [ [ Type ] "=" ExpressionList ] | AliasSpec .
|
2016-03-04 17:09:08 -08:00
|
|
|
func (p *parser) constDecl(group *Group) Decl {
|
|
|
|
|
if trace {
|
|
|
|
|
defer p.trace("constDecl")()
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-15 17:40:26 -07:00
|
|
|
name := p.name()
|
2016-10-21 17:23:01 -07:00
|
|
|
if p.got(_Rarrow) {
|
2016-09-15 17:40:26 -07:00
|
|
|
return p.aliasDecl(Const, name, group)
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-04 17:09:08 -08:00
|
|
|
d := new(ConstDecl)
|
2016-09-15 17:40:26 -07:00
|
|
|
d.initFrom(&name.node)
|
2016-03-04 17:09:08 -08:00
|
|
|
|
2016-09-15 17:40:26 -07:00
|
|
|
d.NameList = p.nameList(name)
|
2016-03-04 17:09:08 -08:00
|
|
|
if p.tok != _EOF && p.tok != _Semi && p.tok != _Rparen {
|
|
|
|
|
d.Type = p.tryType()
|
|
|
|
|
if p.got(_Assign) {
|
|
|
|
|
d.Values = p.exprList()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
d.Group = group
|
|
|
|
|
|
|
|
|
|
return d
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-15 17:40:26 -07:00
|
|
|
// TypeSpec = identifier Type | AliasSpec .
|
2016-03-04 17:09:08 -08:00
|
|
|
func (p *parser) typeDecl(group *Group) Decl {
|
|
|
|
|
if trace {
|
|
|
|
|
defer p.trace("typeDecl")()
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-15 17:40:26 -07:00
|
|
|
name := p.name()
|
2016-10-27 11:20:20 -07:00
|
|
|
if p.got(_Rarrow) {
|
2016-09-15 17:40:26 -07:00
|
|
|
return p.aliasDecl(Type, name, group)
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-04 17:09:08 -08:00
|
|
|
d := new(TypeDecl)
|
2016-09-15 17:40:26 -07:00
|
|
|
d.initFrom(&name.node)
|
2016-03-04 17:09:08 -08:00
|
|
|
|
2016-09-15 17:40:26 -07:00
|
|
|
d.Name = name
|
2016-03-04 17:09:08 -08:00
|
|
|
d.Type = p.tryType()
|
|
|
|
|
if d.Type == nil {
|
|
|
|
|
p.syntax_error("in type declaration")
|
|
|
|
|
p.advance(_Semi, _Rparen)
|
|
|
|
|
}
|
|
|
|
|
d.Group = group
|
cmd/compile: add go:notinheap type pragma
This adds a //go:notinheap pragma for declarations of types that must
not be heap allocated. We ensure these rules by disallowing new(T),
make([]T), append([]T), or implicit allocation of T, by disallowing
conversions to notinheap types, and by propagating notinheap to any
struct or array that contains notinheap elements.
The utility of this pragma is that we can eliminate write barriers for
writes to pointers to go:notinheap types, since the write barrier is
guaranteed to be a no-op. This will let us mark several scheduler and
memory allocator structures as go:notinheap, which will let us
disallow write barriers in the scheduler and memory allocator much
more thoroughly and also eliminate some problematic hybrid write
barriers.
This also makes go:nowritebarrierrec and go:yeswritebarrierrec much
more powerful. Currently we use go:nowritebarrier all over the place,
but it's almost never what you actually want: when write barriers are
illegal, they're typically illegal for a whole dynamic scope. Partly
this is because go:nowritebarrier has been around longer, but it's
also because go:nowritebarrierrec couldn't be used in situations that
had no-op write barriers or where some nested scope did allow write
barriers. go:notinheap eliminates many no-op write barriers and
go:yeswritebarrierrec makes it possible to opt back in to write
barriers, so these two changes will let us use go:nowritebarrierrec
far more liberally.
This updates #13386, which is about controlling pointers from non-GC'd
memory to GC'd memory. That would require some additional pragma (or
pragmas), but could build on this pragma.
Change-Id: I6314f8f4181535dd166887c9ec239977b54940bd
Reviewed-on: https://go-review.googlesource.com/30939
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2016-10-11 22:53:27 -04:00
|
|
|
d.Pragma = p.pragma
|
2016-03-04 17:09:08 -08:00
|
|
|
|
|
|
|
|
return d
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-15 17:40:26 -07:00
|
|
|
// VarSpec = IdentifierList ( Type [ "=" ExpressionList ] | "=" ExpressionList ) | AliasSpec .
|
2016-03-04 17:09:08 -08:00
|
|
|
func (p *parser) varDecl(group *Group) Decl {
|
|
|
|
|
if trace {
|
|
|
|
|
defer p.trace("varDecl")()
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-15 17:40:26 -07:00
|
|
|
name := p.name()
|
2016-10-21 17:23:01 -07:00
|
|
|
if p.got(_Rarrow) {
|
2016-09-15 17:40:26 -07:00
|
|
|
return p.aliasDecl(Var, name, group)
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-04 17:09:08 -08:00
|
|
|
d := new(VarDecl)
|
2016-09-15 17:40:26 -07:00
|
|
|
d.initFrom(&name.node)
|
2016-03-04 17:09:08 -08:00
|
|
|
|
2016-09-15 17:40:26 -07:00
|
|
|
d.NameList = p.nameList(name)
|
2016-03-04 17:09:08 -08:00
|
|
|
if p.got(_Assign) {
|
|
|
|
|
d.Values = p.exprList()
|
|
|
|
|
} else {
|
|
|
|
|
d.Type = p.type_()
|
|
|
|
|
if p.got(_Assign) {
|
|
|
|
|
d.Values = p.exprList()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
d.Group = group
|
2016-08-30 16:31:53 -07:00
|
|
|
if gcCompat {
|
|
|
|
|
d.init(p)
|
|
|
|
|
}
|
2016-03-04 17:09:08 -08:00
|
|
|
|
|
|
|
|
return d
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-15 17:40:26 -07:00
|
|
|
var badRecv = new(Field) // to signal invalid receiver in funcDecl
|
|
|
|
|
|
|
|
|
|
// FunctionDecl = "func" FunctionName ( Function | Signature ) | "func" AliasSpec .
|
2016-03-04 17:09:08 -08:00
|
|
|
// FunctionName = identifier .
|
|
|
|
|
// Function = Signature FunctionBody .
|
|
|
|
|
// MethodDecl = "func" Receiver MethodName ( Function | Signature ) .
|
|
|
|
|
// Receiver = Parameters .
|
2016-09-15 17:40:26 -07:00
|
|
|
func (p *parser) funcDecl() Decl {
|
2016-03-04 17:09:08 -08:00
|
|
|
if trace {
|
|
|
|
|
defer p.trace("funcDecl")()
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-15 17:40:26 -07:00
|
|
|
var recv *Field
|
2016-03-04 17:09:08 -08:00
|
|
|
if p.tok == _Lparen {
|
2016-09-15 17:40:26 -07:00
|
|
|
recv = badRecv
|
|
|
|
|
switch list := p.paramList(); len(list) {
|
2016-03-04 17:09:08 -08:00
|
|
|
case 0:
|
|
|
|
|
p.error("method has no receiver")
|
|
|
|
|
case 1:
|
2016-09-15 17:40:26 -07:00
|
|
|
recv = list[0]
|
2016-03-04 17:09:08 -08:00
|
|
|
default:
|
|
|
|
|
p.error("method has multiple receivers")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if p.tok != _Name {
|
|
|
|
|
p.syntax_error("expecting name or (")
|
|
|
|
|
p.advance(_Lbrace, _Semi)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-15 17:40:26 -07:00
|
|
|
name := p.name()
|
2016-10-21 17:23:01 -07:00
|
|
|
if recv == nil && p.got(_Rarrow) {
|
2016-09-15 17:40:26 -07:00
|
|
|
return p.aliasDecl(Func, name, nil)
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-04 17:09:08 -08:00
|
|
|
// TODO(gri) check for regular functions only
|
|
|
|
|
// if name.Sym.Name == "init" {
|
|
|
|
|
// name = renameinit()
|
|
|
|
|
// if params != nil || result != nil {
|
|
|
|
|
// p.error("func init must have no arguments and no return values")
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// if localpkg.Name == "main" && name.Name == "main" {
|
|
|
|
|
// if params != nil || result != nil {
|
|
|
|
|
// p.error("func main must have no arguments and no return values")
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
|
2016-09-15 17:40:26 -07:00
|
|
|
f := new(FuncDecl)
|
|
|
|
|
f.initFrom(&name.node) // TODO(gri) is this the correct position for methods?
|
|
|
|
|
|
|
|
|
|
f.Recv = recv
|
|
|
|
|
f.Name = name
|
2016-03-04 17:09:08 -08:00
|
|
|
f.Type = p.funcType()
|
2016-08-30 16:31:53 -07:00
|
|
|
if gcCompat {
|
|
|
|
|
f.node = f.Type.node
|
|
|
|
|
}
|
2016-03-04 17:09:08 -08:00
|
|
|
f.Body = p.funcBody()
|
|
|
|
|
|
2016-08-30 16:31:53 -07:00
|
|
|
f.Pragma = p.pragma
|
2016-08-16 13:33:29 -07:00
|
|
|
f.EndLine = uint32(p.line)
|
|
|
|
|
|
2016-03-04 17:09:08 -08:00
|
|
|
// TODO(gri) deal with function properties
|
|
|
|
|
// if noescape && body != nil {
|
|
|
|
|
// p.error("can only use //go:noescape with external func implementations")
|
|
|
|
|
// }
|
|
|
|
|
|
2016-09-15 17:40:26 -07:00
|
|
|
if recv == badRecv {
|
2016-06-06 17:59:05 -07:00
|
|
|
return nil // TODO(gri) better solution
|
|
|
|
|
}
|
2016-03-04 17:09:08 -08:00
|
|
|
return f
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
// Expressions
|
|
|
|
|
|
|
|
|
|
func (p *parser) expr() Expr {
|
|
|
|
|
if trace {
|
|
|
|
|
defer p.trace("expr")()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return p.binaryExpr(0)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Expression = UnaryExpr | Expression binary_op Expression .
|
|
|
|
|
func (p *parser) binaryExpr(prec int) Expr {
|
|
|
|
|
// don't trace binaryExpr - only leads to overly nested trace output
|
|
|
|
|
|
|
|
|
|
x := p.unaryExpr()
|
|
|
|
|
for (p.tok == _Operator || p.tok == _Star) && p.prec > prec {
|
|
|
|
|
t := new(Operation)
|
|
|
|
|
t.init(p)
|
|
|
|
|
t.Op = p.op
|
|
|
|
|
t.X = x
|
|
|
|
|
tprec := p.prec
|
|
|
|
|
p.next()
|
|
|
|
|
t.Y = p.binaryExpr(tprec)
|
2016-08-30 16:31:53 -07:00
|
|
|
if gcCompat {
|
|
|
|
|
t.init(p)
|
|
|
|
|
}
|
2016-03-04 17:09:08 -08:00
|
|
|
x = t
|
|
|
|
|
}
|
|
|
|
|
return x
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// UnaryExpr = PrimaryExpr | unary_op UnaryExpr .
|
|
|
|
|
func (p *parser) unaryExpr() Expr {
|
|
|
|
|
if trace {
|
|
|
|
|
defer p.trace("unaryExpr")()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch p.tok {
|
|
|
|
|
case _Operator, _Star:
|
|
|
|
|
switch p.op {
|
|
|
|
|
case Mul, Add, Sub, Not, Xor:
|
|
|
|
|
x := new(Operation)
|
|
|
|
|
x.init(p)
|
|
|
|
|
x.Op = p.op
|
|
|
|
|
p.next()
|
|
|
|
|
x.X = p.unaryExpr()
|
2016-08-30 16:31:53 -07:00
|
|
|
if gcCompat {
|
|
|
|
|
x.init(p)
|
|
|
|
|
}
|
2016-03-04 17:09:08 -08:00
|
|
|
return x
|
|
|
|
|
|
|
|
|
|
case And:
|
|
|
|
|
p.next()
|
|
|
|
|
x := new(Operation)
|
|
|
|
|
x.init(p)
|
|
|
|
|
x.Op = And
|
|
|
|
|
// unaryExpr may have returned a parenthesized composite literal
|
|
|
|
|
// (see comment in operand) - remove parentheses if any
|
|
|
|
|
x.X = unparen(p.unaryExpr())
|
|
|
|
|
return x
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-15 17:40:26 -07:00
|
|
|
case _Larrow:
|
2016-03-04 17:09:08 -08:00
|
|
|
// receive op (<-x) or receive-only channel (<-chan E)
|
|
|
|
|
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))
|
|
|
|
|
|
2016-06-06 17:59:05 -07:00
|
|
|
if _, ok := x.(*ChanType); ok {
|
2016-03-04 17:09:08 -08:00
|
|
|
// x is a channel type => re-associate <-
|
|
|
|
|
dir := SendOnly
|
|
|
|
|
t := x
|
2016-06-06 17:59:05 -07:00
|
|
|
for dir == SendOnly {
|
|
|
|
|
c, ok := t.(*ChanType)
|
|
|
|
|
if !ok {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
dir = c.Dir
|
2016-03-04 17:09:08 -08:00
|
|
|
if dir == RecvOnly {
|
|
|
|
|
// t is type <-chan E but <-<-chan E is not permitted
|
|
|
|
|
// (report same error as for "type _ <-<-chan E")
|
|
|
|
|
p.syntax_error("unexpected <-, expecting chan")
|
|
|
|
|
// already progressed, no need to advance
|
|
|
|
|
}
|
2016-06-06 17:59:05 -07:00
|
|
|
c.Dir = RecvOnly
|
|
|
|
|
t = c.Elem
|
2016-03-04 17:09:08 -08:00
|
|
|
}
|
|
|
|
|
if dir == SendOnly {
|
|
|
|
|
// channel dir is <- but channel element E is not a channel
|
|
|
|
|
// (report same error as for "type _ <-chan<-E")
|
2016-06-06 17:59:05 -07:00
|
|
|
p.syntax_error(fmt.Sprintf("unexpected %s, expecting chan", String(t)))
|
2016-03-04 17:09:08 -08:00
|
|
|
// already progressed, no need to advance
|
|
|
|
|
}
|
|
|
|
|
return x
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// x is not a channel type => we have a receive op
|
|
|
|
|
return &Operation{Op: Recv, X: x}
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-06 17:59:05 -07:00
|
|
|
// TODO(mdempsky): We need parens here so we can report an
|
|
|
|
|
// error for "(x) := true". It should be possible to detect
|
|
|
|
|
// and reject that more efficiently though.
|
|
|
|
|
return p.pexpr(true)
|
2016-03-04 17:09:08 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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.init(p)
|
|
|
|
|
s.Tok = p.tok
|
|
|
|
|
p.next()
|
|
|
|
|
|
|
|
|
|
x := p.pexpr(p.tok == _Lparen) // keep_parens so we can report error below
|
|
|
|
|
switch x := x.(type) {
|
|
|
|
|
case *CallExpr:
|
|
|
|
|
s.Call = x
|
2016-06-06 17:59:05 -07:00
|
|
|
if gcCompat {
|
|
|
|
|
s.node = x.node
|
|
|
|
|
}
|
2016-03-04 17:09:08 -08:00
|
|
|
case *ParenExpr:
|
|
|
|
|
p.error(fmt.Sprintf("expression in %s must not be parenthesized", s.Tok))
|
|
|
|
|
// already progressed, no need to advance
|
|
|
|
|
default:
|
|
|
|
|
p.error(fmt.Sprintf("expression in %s must be function call", s.Tok))
|
|
|
|
|
// already progressed, no need to advance
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return s // TODO(gri) should we return nil in case of failure?
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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:
|
|
|
|
|
p.next()
|
|
|
|
|
p.xnest++
|
|
|
|
|
x := p.expr() // expr_or_type
|
|
|
|
|
p.xnest--
|
|
|
|
|
p.want(_Rparen)
|
|
|
|
|
|
|
|
|
|
// Optimization: Record presence of ()'s only where needed
|
|
|
|
|
// for error reporting. Don't bother in other cases; it is
|
|
|
|
|
// just a waste of memory and time.
|
|
|
|
|
|
|
|
|
|
// Parentheses are not permitted on lhs of := .
|
|
|
|
|
// switch x.Op {
|
|
|
|
|
// case ONAME, ONONAME, OPACK, OTYPE, OLITERAL, OTYPESW:
|
|
|
|
|
// keep_parens = true
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// Parentheses are not permitted around T in a composite
|
|
|
|
|
// literal T{}. If the next token is a {, assume x is a
|
|
|
|
|
// composite literal type T (it may not be, { could be
|
|
|
|
|
// the opening brace of a block, but we don't know yet).
|
|
|
|
|
if p.tok == _Lbrace {
|
|
|
|
|
keep_parens = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Parentheses are also not permitted around the expression
|
|
|
|
|
// in a go/defer statement. In that case, operand is called
|
|
|
|
|
// with keep_parens set.
|
|
|
|
|
if keep_parens {
|
|
|
|
|
x = &ParenExpr{X: x}
|
|
|
|
|
}
|
|
|
|
|
return x
|
|
|
|
|
|
|
|
|
|
case _Func:
|
|
|
|
|
p.next()
|
|
|
|
|
t := p.funcType()
|
|
|
|
|
if p.tok == _Lbrace {
|
|
|
|
|
p.fnest++
|
|
|
|
|
p.xnest++
|
|
|
|
|
f := new(FuncLit)
|
|
|
|
|
f.init(p)
|
|
|
|
|
f.Type = t
|
|
|
|
|
f.Body = p.funcBody()
|
2016-08-16 13:33:29 -07:00
|
|
|
f.EndLine = uint32(p.line)
|
2016-03-04 17:09:08 -08:00
|
|
|
p.xnest--
|
|
|
|
|
p.fnest--
|
|
|
|
|
return f
|
|
|
|
|
}
|
|
|
|
|
return t
|
|
|
|
|
|
|
|
|
|
case _Lbrack, _Chan, _Map, _Struct, _Interface:
|
|
|
|
|
return p.type_() // othertype
|
|
|
|
|
|
|
|
|
|
case _Lbrace:
|
|
|
|
|
// common case: p.header is missing simpleStmt before { in if, for, switch
|
|
|
|
|
p.syntax_error("missing operand")
|
|
|
|
|
// '{' will be consumed in pexpr - no need to consume it here
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
p.syntax_error("expecting expression")
|
|
|
|
|
p.advance()
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Syntactically, composite literals are operands. Because a complit
|
|
|
|
|
// type may be a qualified identifier which is handled by pexpr
|
|
|
|
|
// (together with selector expressions), complits are parsed there
|
|
|
|
|
// as well (operand is only called from pexpr).
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// PrimaryExpr =
|
|
|
|
|
// Operand |
|
|
|
|
|
// Conversion |
|
|
|
|
|
// PrimaryExpr Selector |
|
|
|
|
|
// PrimaryExpr Index |
|
|
|
|
|
// PrimaryExpr Slice |
|
|
|
|
|
// PrimaryExpr TypeAssertion |
|
|
|
|
|
// PrimaryExpr Arguments .
|
|
|
|
|
//
|
|
|
|
|
// Selector = "." identifier .
|
|
|
|
|
// Index = "[" Expression "]" .
|
|
|
|
|
// Slice = "[" ( [ Expression ] ":" [ Expression ] ) |
|
|
|
|
|
// ( [ Expression ] ":" Expression ":" Expression )
|
|
|
|
|
// "]" .
|
|
|
|
|
// TypeAssertion = "." "(" Type ")" .
|
|
|
|
|
// Arguments = "(" [ ( ExpressionList | Type [ "," ExpressionList ] ) [ "..." ] [ "," ] ] ")" .
|
|
|
|
|
func (p *parser) pexpr(keep_parens bool) Expr {
|
|
|
|
|
if trace {
|
|
|
|
|
defer p.trace("pexpr")()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
x := p.operand(keep_parens)
|
|
|
|
|
|
|
|
|
|
loop:
|
|
|
|
|
for {
|
|
|
|
|
switch p.tok {
|
|
|
|
|
case _Dot:
|
|
|
|
|
p.next()
|
|
|
|
|
switch p.tok {
|
|
|
|
|
case _Name:
|
|
|
|
|
// pexpr '.' sym
|
|
|
|
|
t := new(SelectorExpr)
|
|
|
|
|
t.init(p)
|
|
|
|
|
t.X = x
|
|
|
|
|
t.Sel = p.name()
|
|
|
|
|
x = t
|
|
|
|
|
|
|
|
|
|
case _Lparen:
|
|
|
|
|
p.next()
|
|
|
|
|
if p.got(_Type) {
|
|
|
|
|
t := new(TypeSwitchGuard)
|
|
|
|
|
t.init(p)
|
|
|
|
|
t.X = x
|
|
|
|
|
x = t
|
|
|
|
|
} else {
|
|
|
|
|
t := new(AssertExpr)
|
|
|
|
|
t.init(p)
|
|
|
|
|
t.X = x
|
|
|
|
|
t.Type = p.expr()
|
|
|
|
|
x = t
|
|
|
|
|
}
|
|
|
|
|
p.want(_Rparen)
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
p.syntax_error("expecting name or (")
|
|
|
|
|
p.advance(_Semi, _Rparen)
|
|
|
|
|
}
|
2016-08-30 16:31:53 -07:00
|
|
|
if gcCompat {
|
|
|
|
|
x.init(p)
|
|
|
|
|
}
|
2016-03-04 17:09:08 -08:00
|
|
|
|
|
|
|
|
case _Lbrack:
|
|
|
|
|
p.next()
|
|
|
|
|
p.xnest++
|
|
|
|
|
|
|
|
|
|
var i Expr
|
|
|
|
|
if p.tok != _Colon {
|
|
|
|
|
i = p.expr()
|
|
|
|
|
if p.got(_Rbrack) {
|
|
|
|
|
// x[i]
|
|
|
|
|
t := new(IndexExpr)
|
|
|
|
|
t.init(p)
|
|
|
|
|
t.X = x
|
|
|
|
|
t.Index = i
|
|
|
|
|
x = t
|
|
|
|
|
p.xnest--
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// x[i:...
|
|
|
|
|
t := new(SliceExpr)
|
|
|
|
|
t.init(p)
|
|
|
|
|
t.X = x
|
|
|
|
|
t.Index[0] = i
|
|
|
|
|
p.want(_Colon)
|
|
|
|
|
if p.tok != _Colon && p.tok != _Rbrack {
|
|
|
|
|
// x[i:j...
|
|
|
|
|
t.Index[1] = p.expr()
|
|
|
|
|
}
|
|
|
|
|
if p.got(_Colon) {
|
2016-08-16 13:33:29 -07:00
|
|
|
t.Full = true
|
2016-03-04 17:09:08 -08:00
|
|
|
// x[i:j:...]
|
|
|
|
|
if t.Index[1] == nil {
|
|
|
|
|
p.error("middle index required in 3-index slice")
|
|
|
|
|
}
|
|
|
|
|
if p.tok != _Rbrack {
|
|
|
|
|
// x[i:j:k...
|
|
|
|
|
t.Index[2] = p.expr()
|
|
|
|
|
} else {
|
|
|
|
|
p.error("final index required in 3-index slice")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
p.want(_Rbrack)
|
|
|
|
|
|
|
|
|
|
x = t
|
|
|
|
|
p.xnest--
|
|
|
|
|
|
|
|
|
|
case _Lparen:
|
2016-06-06 17:59:05 -07:00
|
|
|
x = p.call(x)
|
2016-03-04 17:09:08 -08:00
|
|
|
|
|
|
|
|
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 complit or a compound_stmt
|
|
|
|
|
complit_ok := false
|
|
|
|
|
switch t.(type) {
|
|
|
|
|
case *Name, *SelectorExpr:
|
|
|
|
|
if p.xnest >= 0 {
|
|
|
|
|
// x is considered a comptype
|
|
|
|
|
complit_ok = true
|
|
|
|
|
}
|
|
|
|
|
case *ArrayType, *SliceType, *StructType, *MapType:
|
|
|
|
|
// x is a comptype
|
|
|
|
|
complit_ok = true
|
|
|
|
|
}
|
|
|
|
|
if !complit_ok {
|
|
|
|
|
break loop
|
|
|
|
|
}
|
|
|
|
|
if t != x {
|
|
|
|
|
p.syntax_error("cannot parenthesize type in composite literal")
|
|
|
|
|
// already progressed, no need to advance
|
|
|
|
|
}
|
|
|
|
|
n := p.complitexpr()
|
|
|
|
|
n.Type = x
|
|
|
|
|
x = n
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break loop
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return x
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Element = Expression | LiteralValue .
|
|
|
|
|
func (p *parser) bare_complitexpr() Expr {
|
|
|
|
|
if trace {
|
|
|
|
|
defer p.trace("bare_complitexpr")()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if p.tok == _Lbrace {
|
|
|
|
|
// '{' start_complit braced_keyval_list '}'
|
|
|
|
|
return p.complitexpr()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return p.expr()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// LiteralValue = "{" [ ElementList [ "," ] ] "}" .
|
|
|
|
|
func (p *parser) complitexpr() *CompositeLit {
|
|
|
|
|
if trace {
|
|
|
|
|
defer p.trace("complitexpr")()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
x := new(CompositeLit)
|
|
|
|
|
x.init(p)
|
|
|
|
|
|
|
|
|
|
p.want(_Lbrace)
|
|
|
|
|
p.xnest++
|
|
|
|
|
|
|
|
|
|
for p.tok != _EOF && p.tok != _Rbrace {
|
|
|
|
|
// value
|
|
|
|
|
e := p.bare_complitexpr()
|
|
|
|
|
if p.got(_Colon) {
|
|
|
|
|
// key ':' value
|
|
|
|
|
l := new(KeyValueExpr)
|
|
|
|
|
l.init(p)
|
|
|
|
|
l.Key = e
|
|
|
|
|
l.Value = p.bare_complitexpr()
|
2016-08-30 16:31:53 -07:00
|
|
|
if gcCompat {
|
|
|
|
|
l.init(p)
|
|
|
|
|
}
|
2016-03-04 17:09:08 -08:00
|
|
|
e = l
|
|
|
|
|
x.NKeys++
|
|
|
|
|
}
|
|
|
|
|
x.ElemList = append(x.ElemList, e)
|
|
|
|
|
if !p.ocomma(_Rbrace) {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-30 16:31:53 -07:00
|
|
|
x.EndLine = uint32(p.line)
|
2016-03-04 17:09:08 -08:00
|
|
|
p.xnest--
|
|
|
|
|
p.want(_Rbrace)
|
|
|
|
|
|
|
|
|
|
return x
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
// Types
|
|
|
|
|
|
|
|
|
|
func (p *parser) type_() Expr {
|
|
|
|
|
if trace {
|
|
|
|
|
defer p.trace("type_")()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if typ := p.tryType(); typ != nil {
|
|
|
|
|
return typ
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p.syntax_error("")
|
|
|
|
|
p.advance()
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func indirect(typ Expr) Expr {
|
|
|
|
|
return &Operation{Op: Mul, X: typ}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// tryType 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) tryType() Expr {
|
|
|
|
|
if trace {
|
|
|
|
|
defer p.trace("tryType")()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch p.tok {
|
|
|
|
|
case _Star:
|
|
|
|
|
// ptrtype
|
|
|
|
|
p.next()
|
|
|
|
|
return indirect(p.type_())
|
|
|
|
|
|
2016-09-15 17:40:26 -07:00
|
|
|
case _Larrow:
|
2016-03-04 17:09:08 -08:00
|
|
|
// recvchantype
|
|
|
|
|
p.next()
|
|
|
|
|
p.want(_Chan)
|
|
|
|
|
t := new(ChanType)
|
|
|
|
|
t.init(p)
|
|
|
|
|
t.Dir = RecvOnly
|
|
|
|
|
t.Elem = p.chanElem()
|
|
|
|
|
return t
|
|
|
|
|
|
|
|
|
|
case _Func:
|
|
|
|
|
// fntype
|
|
|
|
|
p.next()
|
|
|
|
|
return p.funcType()
|
|
|
|
|
|
|
|
|
|
case _Lbrack:
|
|
|
|
|
// '[' oexpr ']' ntype
|
|
|
|
|
// '[' _DotDotDot ']' ntype
|
|
|
|
|
p.next()
|
|
|
|
|
p.xnest++
|
|
|
|
|
if p.got(_Rbrack) {
|
|
|
|
|
// []T
|
|
|
|
|
p.xnest--
|
|
|
|
|
t := new(SliceType)
|
|
|
|
|
t.init(p)
|
|
|
|
|
t.Elem = p.type_()
|
|
|
|
|
return t
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// [n]T
|
|
|
|
|
t := new(ArrayType)
|
|
|
|
|
t.init(p)
|
|
|
|
|
if !p.got(_DotDotDot) {
|
|
|
|
|
t.Len = p.expr()
|
|
|
|
|
}
|
|
|
|
|
p.want(_Rbrack)
|
|
|
|
|
p.xnest--
|
|
|
|
|
t.Elem = p.type_()
|
|
|
|
|
return t
|
|
|
|
|
|
|
|
|
|
case _Chan:
|
|
|
|
|
// _Chan non_recvchantype
|
|
|
|
|
// _Chan _Comm ntype
|
|
|
|
|
p.next()
|
|
|
|
|
t := new(ChanType)
|
|
|
|
|
t.init(p)
|
2016-09-15 17:40:26 -07:00
|
|
|
if p.got(_Larrow) {
|
2016-03-04 17:09:08 -08:00
|
|
|
t.Dir = SendOnly
|
|
|
|
|
}
|
|
|
|
|
t.Elem = p.chanElem()
|
|
|
|
|
return t
|
|
|
|
|
|
|
|
|
|
case _Map:
|
|
|
|
|
// _Map '[' ntype ']' ntype
|
|
|
|
|
p.next()
|
|
|
|
|
p.want(_Lbrack)
|
|
|
|
|
t := new(MapType)
|
|
|
|
|
t.init(p)
|
|
|
|
|
t.Key = p.type_()
|
|
|
|
|
p.want(_Rbrack)
|
|
|
|
|
t.Value = p.type_()
|
|
|
|
|
return t
|
|
|
|
|
|
|
|
|
|
case _Struct:
|
|
|
|
|
return p.structType()
|
|
|
|
|
|
|
|
|
|
case _Interface:
|
|
|
|
|
return p.interfaceType()
|
|
|
|
|
|
|
|
|
|
case _Name:
|
|
|
|
|
return p.dotname(p.name())
|
|
|
|
|
|
|
|
|
|
case _Lparen:
|
|
|
|
|
p.next()
|
|
|
|
|
t := p.type_()
|
|
|
|
|
p.want(_Rparen)
|
|
|
|
|
return t
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *parser) funcType() *FuncType {
|
|
|
|
|
if trace {
|
|
|
|
|
defer p.trace("funcType")()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
typ := new(FuncType)
|
|
|
|
|
typ.init(p)
|
|
|
|
|
typ.ParamList = p.paramList()
|
|
|
|
|
typ.ResultList = p.funcResult()
|
2016-08-30 16:31:53 -07:00
|
|
|
if gcCompat {
|
|
|
|
|
typ.init(p)
|
|
|
|
|
}
|
2016-03-04 17:09:08 -08:00
|
|
|
return typ
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *parser) chanElem() Expr {
|
|
|
|
|
if trace {
|
|
|
|
|
defer p.trace("chanElem")()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if typ := p.tryType(); typ != nil {
|
|
|
|
|
return typ
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p.syntax_error("missing channel element type")
|
|
|
|
|
// assume element type is simply absent - don't advance
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *parser) dotname(name *Name) Expr {
|
|
|
|
|
if trace {
|
|
|
|
|
defer p.trace("dotname")()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if p.got(_Dot) {
|
|
|
|
|
s := new(SelectorExpr)
|
|
|
|
|
s.init(p)
|
|
|
|
|
s.X = name
|
|
|
|
|
s.Sel = p.name()
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
return name
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// StructType = "struct" "{" { FieldDecl ";" } "}" .
|
|
|
|
|
func (p *parser) structType() *StructType {
|
|
|
|
|
if trace {
|
|
|
|
|
defer p.trace("structType")()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
typ := new(StructType)
|
|
|
|
|
typ.init(p)
|
|
|
|
|
|
|
|
|
|
p.want(_Struct)
|
|
|
|
|
p.want(_Lbrace)
|
|
|
|
|
for p.tok != _EOF && p.tok != _Rbrace {
|
|
|
|
|
p.fieldDecl(typ)
|
|
|
|
|
if !p.osemi(_Rbrace) {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-06-06 17:59:05 -07:00
|
|
|
if gcCompat {
|
|
|
|
|
typ.init(p)
|
|
|
|
|
}
|
2016-03-04 17:09:08 -08:00
|
|
|
p.want(_Rbrace)
|
|
|
|
|
|
|
|
|
|
return typ
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// InterfaceType = "interface" "{" { MethodSpec ";" } "}" .
|
|
|
|
|
func (p *parser) interfaceType() *InterfaceType {
|
|
|
|
|
if trace {
|
|
|
|
|
defer p.trace("interfaceType")()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
typ := new(InterfaceType)
|
|
|
|
|
typ.init(p)
|
|
|
|
|
|
|
|
|
|
p.want(_Interface)
|
|
|
|
|
p.want(_Lbrace)
|
|
|
|
|
for p.tok != _EOF && p.tok != _Rbrace {
|
|
|
|
|
if m := p.methodDecl(); m != nil {
|
|
|
|
|
typ.MethodList = append(typ.MethodList, m)
|
|
|
|
|
}
|
|
|
|
|
if !p.osemi(_Rbrace) {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-06-06 17:59:05 -07:00
|
|
|
if gcCompat {
|
|
|
|
|
typ.init(p)
|
|
|
|
|
}
|
2016-03-04 17:09:08 -08:00
|
|
|
p.want(_Rbrace)
|
|
|
|
|
|
|
|
|
|
return typ
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// FunctionBody = Block .
|
|
|
|
|
func (p *parser) funcBody() []Stmt {
|
|
|
|
|
if trace {
|
|
|
|
|
defer p.trace("funcBody")()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if p.got(_Lbrace) {
|
|
|
|
|
p.fnest++
|
|
|
|
|
body := p.stmtList()
|
|
|
|
|
p.fnest--
|
|
|
|
|
p.want(_Rbrace)
|
|
|
|
|
if body == nil {
|
|
|
|
|
body = []Stmt{new(EmptyStmt)}
|
|
|
|
|
}
|
|
|
|
|
return body
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Result = Parameters | Type .
|
|
|
|
|
func (p *parser) funcResult() []*Field {
|
|
|
|
|
if trace {
|
|
|
|
|
defer p.trace("funcResult")()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if p.tok == _Lparen {
|
|
|
|
|
return p.paramList()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if result := p.tryType(); result != nil {
|
|
|
|
|
f := new(Field)
|
|
|
|
|
f.init(p)
|
|
|
|
|
f.Type = result
|
|
|
|
|
return []*Field{f}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *parser) addField(styp *StructType, 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.init(p)
|
|
|
|
|
f.Name = name
|
|
|
|
|
f.Type = typ
|
|
|
|
|
styp.FieldList = append(styp.FieldList, f)
|
|
|
|
|
|
2016-08-30 16:31:53 -07:00
|
|
|
if gcCompat && name != nil {
|
|
|
|
|
f.node = name.node
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-04 17:09:08 -08:00
|
|
|
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")()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var name *Name
|
|
|
|
|
switch p.tok {
|
|
|
|
|
case _Name:
|
|
|
|
|
name = p.name()
|
|
|
|
|
if p.tok == _Dot || p.tok == _Literal || p.tok == _Semi || p.tok == _Rbrace {
|
|
|
|
|
// embed oliteral
|
|
|
|
|
typ := p.qualifiedName(name)
|
|
|
|
|
tag := p.oliteral()
|
|
|
|
|
p.addField(styp, nil, typ, tag)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// new_name_list ntype oliteral
|
|
|
|
|
names := p.nameList(name)
|
|
|
|
|
typ := p.type_()
|
|
|
|
|
tag := p.oliteral()
|
|
|
|
|
|
|
|
|
|
for _, name := range names {
|
|
|
|
|
p.addField(styp, name, typ, tag)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case _Lparen:
|
|
|
|
|
p.next()
|
|
|
|
|
if p.tok == _Star {
|
|
|
|
|
// '(' '*' embed ')' oliteral
|
|
|
|
|
p.next()
|
|
|
|
|
typ := indirect(p.qualifiedName(nil))
|
|
|
|
|
p.want(_Rparen)
|
|
|
|
|
tag := p.oliteral()
|
|
|
|
|
p.addField(styp, nil, typ, tag)
|
|
|
|
|
p.error("cannot parenthesize embedded type")
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
// '(' embed ')' oliteral
|
|
|
|
|
typ := p.qualifiedName(nil)
|
|
|
|
|
p.want(_Rparen)
|
|
|
|
|
tag := p.oliteral()
|
|
|
|
|
p.addField(styp, nil, typ, tag)
|
|
|
|
|
p.error("cannot parenthesize embedded type")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case _Star:
|
|
|
|
|
p.next()
|
|
|
|
|
if p.got(_Lparen) {
|
|
|
|
|
// '*' '(' embed ')' oliteral
|
|
|
|
|
typ := indirect(p.qualifiedName(nil))
|
|
|
|
|
p.want(_Rparen)
|
|
|
|
|
tag := p.oliteral()
|
|
|
|
|
p.addField(styp, nil, typ, tag)
|
|
|
|
|
p.error("cannot parenthesize embedded type")
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
// '*' embed oliteral
|
|
|
|
|
typ := indirect(p.qualifiedName(nil))
|
|
|
|
|
tag := p.oliteral()
|
|
|
|
|
p.addField(styp, nil, typ, tag)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
p.syntax_error("expecting field name or embedded type")
|
|
|
|
|
p.advance(_Semi, _Rbrace)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *parser) oliteral() *BasicLit {
|
|
|
|
|
if p.tok == _Literal {
|
|
|
|
|
b := new(BasicLit)
|
|
|
|
|
b.init(p)
|
|
|
|
|
b.Value = p.lit
|
|
|
|
|
b.Kind = p.kind
|
|
|
|
|
p.next()
|
|
|
|
|
return b
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MethodSpec = MethodName Signature | InterfaceTypeName .
|
|
|
|
|
// MethodName = identifier .
|
|
|
|
|
// InterfaceTypeName = TypeName .
|
|
|
|
|
func (p *parser) methodDecl() *Field {
|
|
|
|
|
if trace {
|
|
|
|
|
defer p.trace("methodDecl")()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch p.tok {
|
|
|
|
|
case _Name:
|
|
|
|
|
name := p.name()
|
|
|
|
|
|
|
|
|
|
// accept potential name list but complain
|
|
|
|
|
hasNameList := false
|
|
|
|
|
for p.got(_Comma) {
|
|
|
|
|
p.name()
|
|
|
|
|
hasNameList = true
|
|
|
|
|
}
|
|
|
|
|
if hasNameList {
|
|
|
|
|
p.syntax_error("name list not allowed in interface type")
|
|
|
|
|
// already progressed, no need to advance
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
f := new(Field)
|
|
|
|
|
f.init(p)
|
|
|
|
|
if p.tok != _Lparen {
|
|
|
|
|
// packname
|
|
|
|
|
f.Type = p.qualifiedName(name)
|
|
|
|
|
return f
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
f.Name = name
|
|
|
|
|
f.Type = p.funcType()
|
|
|
|
|
return f
|
|
|
|
|
|
|
|
|
|
case _Lparen:
|
|
|
|
|
p.next()
|
|
|
|
|
f := new(Field)
|
|
|
|
|
f.init(p)
|
|
|
|
|
f.Type = p.qualifiedName(nil)
|
|
|
|
|
p.want(_Rparen)
|
|
|
|
|
p.error("cannot parenthesize embedded type")
|
|
|
|
|
return f
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
p.syntax_error("")
|
|
|
|
|
p.advance(_Semi, _Rbrace)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ParameterDecl = [ IdentifierList ] [ "..." ] Type .
|
|
|
|
|
func (p *parser) paramDecl() *Field {
|
|
|
|
|
if trace {
|
|
|
|
|
defer p.trace("paramDecl")()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
f := new(Field)
|
|
|
|
|
f.init(p)
|
|
|
|
|
|
|
|
|
|
switch p.tok {
|
|
|
|
|
case _Name:
|
|
|
|
|
f.Name = p.name()
|
|
|
|
|
switch p.tok {
|
2016-09-15 17:40:26 -07:00
|
|
|
case _Name, _Star, _Larrow, _Func, _Lbrack, _Chan, _Map, _Struct, _Interface, _Lparen:
|
2016-03-04 17:09:08 -08:00
|
|
|
// sym name_or_type
|
|
|
|
|
f.Type = p.type_()
|
|
|
|
|
|
|
|
|
|
case _DotDotDot:
|
|
|
|
|
// sym dotdotdot
|
|
|
|
|
f.Type = p.dotsType()
|
|
|
|
|
|
|
|
|
|
case _Dot:
|
|
|
|
|
// name_or_type
|
|
|
|
|
// from dotname
|
|
|
|
|
f.Type = p.dotname(f.Name)
|
|
|
|
|
f.Name = nil
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-15 17:40:26 -07:00
|
|
|
case _Larrow, _Star, _Func, _Lbrack, _Chan, _Map, _Struct, _Interface, _Lparen:
|
2016-03-04 17:09:08 -08:00
|
|
|
// name_or_type
|
|
|
|
|
f.Type = p.type_()
|
|
|
|
|
|
|
|
|
|
case _DotDotDot:
|
|
|
|
|
// dotdotdot
|
|
|
|
|
f.Type = p.dotsType()
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
p.syntax_error("expecting )")
|
|
|
|
|
p.advance(_Comma, _Rparen)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return f
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ...Type
|
|
|
|
|
func (p *parser) dotsType() *DotsType {
|
|
|
|
|
if trace {
|
|
|
|
|
defer p.trace("dotsType")()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
t := new(DotsType)
|
|
|
|
|
t.init(p)
|
|
|
|
|
|
|
|
|
|
p.want(_DotDotDot)
|
|
|
|
|
t.Elem = p.tryType()
|
|
|
|
|
if t.Elem == nil {
|
|
|
|
|
p.error("final argument in variadic function missing type")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return t
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Parameters = "(" [ ParameterList [ "," ] ] ")" .
|
|
|
|
|
// ParameterList = ParameterDecl { "," ParameterDecl } .
|
|
|
|
|
func (p *parser) paramList() (list []*Field) {
|
|
|
|
|
if trace {
|
|
|
|
|
defer p.trace("paramList")()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p.want(_Lparen)
|
|
|
|
|
|
|
|
|
|
var named int // number of parameters that have an explicit name and type
|
|
|
|
|
for p.tok != _EOF && p.tok != _Rparen {
|
|
|
|
|
if par := p.paramDecl(); par != nil {
|
|
|
|
|
if debug && par.Name == nil && par.Type == nil {
|
|
|
|
|
panic("parameter without name or type")
|
|
|
|
|
}
|
|
|
|
|
if par.Name != nil && par.Type != nil {
|
|
|
|
|
named++
|
|
|
|
|
}
|
|
|
|
|
list = append(list, par)
|
|
|
|
|
}
|
|
|
|
|
if !p.ocomma(_Rparen) {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// distribute parameter types
|
|
|
|
|
if named == 0 {
|
|
|
|
|
// all unnamed => found names are named types
|
|
|
|
|
for _, par := range list {
|
|
|
|
|
if typ := par.Name; typ != nil {
|
|
|
|
|
par.Type = typ
|
|
|
|
|
par.Name = nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if named != len(list) {
|
|
|
|
|
// some named => all must be named
|
|
|
|
|
var typ Expr
|
|
|
|
|
for i := len(list) - 1; i >= 0; i-- {
|
|
|
|
|
if par := list[i]; par.Type != nil {
|
|
|
|
|
typ = par.Type
|
|
|
|
|
if par.Name == nil {
|
|
|
|
|
typ = nil // error
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
par.Type = typ
|
|
|
|
|
}
|
|
|
|
|
if typ == nil {
|
|
|
|
|
p.syntax_error("mixed named and unnamed function parameters")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p.want(_Rparen)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
// Statements
|
|
|
|
|
|
|
|
|
|
// We represent x++, x-- as assignments x += ImplicitOne, x -= ImplicitOne.
|
|
|
|
|
// ImplicitOne should not be used elsewhere.
|
|
|
|
|
var ImplicitOne = &BasicLit{Value: "1"}
|
|
|
|
|
|
|
|
|
|
// SimpleStmt = EmptyStmt | ExpressionStmt | SendStmt | IncDecStmt | Assignment | ShortVarDecl .
|
|
|
|
|
//
|
|
|
|
|
// simpleStmt may return missing_stmt if labelOk is set.
|
|
|
|
|
func (p *parser) simpleStmt(lhs Expr, rangeOk bool) SimpleStmt {
|
|
|
|
|
if trace {
|
|
|
|
|
defer p.trace("simpleStmt")()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if rangeOk && p.got(_Range) {
|
|
|
|
|
// _Range expr
|
|
|
|
|
if debug && lhs != nil {
|
|
|
|
|
panic("invalid call of simpleStmt")
|
|
|
|
|
}
|
|
|
|
|
return p.rangeClause(nil, false)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if lhs == nil {
|
|
|
|
|
lhs = p.exprList()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if _, ok := lhs.(*ListExpr); !ok && p.tok != _Assign && p.tok != _Define {
|
|
|
|
|
// expr
|
|
|
|
|
switch p.tok {
|
|
|
|
|
case _AssignOp:
|
|
|
|
|
// lhs op= rhs
|
|
|
|
|
op := p.op
|
|
|
|
|
p.next()
|
|
|
|
|
return p.newAssignStmt(op, lhs, p.expr())
|
|
|
|
|
|
|
|
|
|
case _IncOp:
|
|
|
|
|
// lhs++ or lhs--
|
|
|
|
|
op := p.op
|
|
|
|
|
p.next()
|
|
|
|
|
return p.newAssignStmt(op, lhs, ImplicitOne)
|
|
|
|
|
|
2016-09-15 17:40:26 -07:00
|
|
|
case _Larrow:
|
2016-03-04 17:09:08 -08:00
|
|
|
// lhs <- rhs
|
|
|
|
|
p.next()
|
|
|
|
|
s := new(SendStmt)
|
|
|
|
|
s.init(p)
|
|
|
|
|
s.Chan = lhs
|
|
|
|
|
s.Value = p.expr()
|
2016-08-30 16:31:53 -07:00
|
|
|
if gcCompat {
|
|
|
|
|
s.init(p)
|
|
|
|
|
}
|
2016-03-04 17:09:08 -08:00
|
|
|
return s
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
// expr
|
|
|
|
|
return &ExprStmt{X: lhs}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// expr_list
|
|
|
|
|
switch p.tok {
|
|
|
|
|
case _Assign:
|
|
|
|
|
p.next()
|
|
|
|
|
|
|
|
|
|
if rangeOk && p.got(_Range) {
|
|
|
|
|
// expr_list '=' _Range expr
|
|
|
|
|
return p.rangeClause(lhs, false)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// expr_list '=' expr_list
|
|
|
|
|
return p.newAssignStmt(0, lhs, p.exprList())
|
|
|
|
|
|
|
|
|
|
case _Define:
|
2016-06-06 17:59:05 -07:00
|
|
|
var n node
|
|
|
|
|
n.init(p)
|
2016-03-04 17:09:08 -08:00
|
|
|
p.next()
|
|
|
|
|
|
|
|
|
|
if rangeOk && p.got(_Range) {
|
|
|
|
|
// expr_list ':=' range expr
|
|
|
|
|
return p.rangeClause(lhs, true)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// expr_list ':=' expr_list
|
|
|
|
|
rhs := p.exprList()
|
|
|
|
|
|
|
|
|
|
if x, ok := rhs.(*TypeSwitchGuard); ok {
|
|
|
|
|
switch lhs := lhs.(type) {
|
|
|
|
|
case *Name:
|
|
|
|
|
x.Lhs = lhs
|
|
|
|
|
case *ListExpr:
|
|
|
|
|
p.error(fmt.Sprintf("argument count mismatch: %d = %d", len(lhs.ElemList), 1))
|
|
|
|
|
default:
|
|
|
|
|
// TODO(mdempsky): Have Expr types implement Stringer?
|
|
|
|
|
p.error(fmt.Sprintf("invalid variable name %s in type switch", lhs))
|
|
|
|
|
}
|
|
|
|
|
return &ExprStmt{X: x}
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-06 17:59:05 -07:00
|
|
|
as := p.newAssignStmt(Def, lhs, rhs)
|
|
|
|
|
if gcCompat {
|
|
|
|
|
as.node = n
|
|
|
|
|
}
|
|
|
|
|
return as
|
2016-03-04 17:09:08 -08:00
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
p.syntax_error("expecting := or = or comma")
|
|
|
|
|
p.advance(_Semi, _Rbrace)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *parser) rangeClause(lhs Expr, def bool) *RangeClause {
|
|
|
|
|
r := new(RangeClause)
|
|
|
|
|
r.init(p)
|
|
|
|
|
r.Lhs = lhs
|
|
|
|
|
r.Def = def
|
|
|
|
|
r.X = p.expr()
|
2016-08-30 16:31:53 -07:00
|
|
|
if gcCompat {
|
|
|
|
|
r.init(p)
|
|
|
|
|
}
|
2016-03-04 17:09:08 -08:00
|
|
|
return r
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *parser) newAssignStmt(op Operator, lhs, rhs Expr) *AssignStmt {
|
|
|
|
|
a := new(AssignStmt)
|
|
|
|
|
a.init(p)
|
|
|
|
|
a.Op = op
|
|
|
|
|
a.Lhs = lhs
|
|
|
|
|
a.Rhs = rhs
|
|
|
|
|
return a
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *parser) labeledStmt(label *Name) Stmt {
|
|
|
|
|
if trace {
|
|
|
|
|
defer p.trace("labeledStmt")()
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-06 17:59:05 -07:00
|
|
|
s := new(LabeledStmt)
|
|
|
|
|
s.init(p)
|
|
|
|
|
s.Label = label
|
|
|
|
|
|
|
|
|
|
p.want(_Colon)
|
|
|
|
|
|
2016-03-04 17:09:08 -08:00
|
|
|
if p.tok != _Rbrace && p.tok != _EOF {
|
2016-06-06 17:59:05 -07:00
|
|
|
s.Stmt = p.stmt()
|
|
|
|
|
if s.Stmt == missing_stmt {
|
2016-03-04 17:09:08 -08:00
|
|
|
// report error at line of ':' token
|
2016-06-06 17:59:05 -07:00
|
|
|
p.syntax_error_at(int(label.pos), int(label.line), "missing statement after label")
|
2016-03-04 17:09:08 -08:00
|
|
|
// we are already at the end of the labeled statement - no need to advance
|
|
|
|
|
return missing_stmt
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *parser) blockStmt() *BlockStmt {
|
|
|
|
|
if trace {
|
|
|
|
|
defer p.trace("blockStmt")()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s := new(BlockStmt)
|
|
|
|
|
s.init(p)
|
|
|
|
|
p.want(_Lbrace)
|
|
|
|
|
s.Body = p.stmtList()
|
|
|
|
|
p.want(_Rbrace)
|
|
|
|
|
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *parser) declStmt(f func(*Group) Decl) *DeclStmt {
|
|
|
|
|
if trace {
|
|
|
|
|
defer p.trace("declStmt")()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s := new(DeclStmt)
|
|
|
|
|
s.init(p)
|
|
|
|
|
|
|
|
|
|
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.init(p)
|
|
|
|
|
|
|
|
|
|
p.want(_For)
|
|
|
|
|
s.Init, s.Cond, s.Post = p.header(true)
|
2016-08-30 16:31:53 -07:00
|
|
|
if gcCompat {
|
|
|
|
|
s.init(p)
|
|
|
|
|
}
|
2016-03-04 17:09:08 -08:00
|
|
|
s.Body = p.stmtBody("for clause")
|
|
|
|
|
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// stmtBody parses if and for statement bodies.
|
|
|
|
|
func (p *parser) stmtBody(context string) []Stmt {
|
|
|
|
|
if trace {
|
|
|
|
|
defer p.trace("stmtBody")()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !p.got(_Lbrace) {
|
|
|
|
|
p.syntax_error("missing { after " + context)
|
|
|
|
|
p.advance(_Name, _Rbrace)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
body := p.stmtList()
|
|
|
|
|
p.want(_Rbrace)
|
|
|
|
|
|
|
|
|
|
return body
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *parser) header(forStmt bool) (init SimpleStmt, cond Expr, post SimpleStmt) {
|
|
|
|
|
if p.tok == _Lbrace {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
outer := p.xnest
|
|
|
|
|
p.xnest = -1
|
|
|
|
|
|
|
|
|
|
if p.tok != _Semi {
|
|
|
|
|
// accept potential varDecl but complain
|
2016-06-06 17:59:05 -07:00
|
|
|
if forStmt && p.got(_Var) {
|
|
|
|
|
p.error("var declaration not allowed in for initializer")
|
2016-03-04 17:09:08 -08:00
|
|
|
}
|
|
|
|
|
init = p.simpleStmt(nil, forStmt)
|
|
|
|
|
// If we have a range clause, we are done.
|
|
|
|
|
if _, ok := init.(*RangeClause); ok {
|
|
|
|
|
p.xnest = outer
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var condStmt SimpleStmt
|
|
|
|
|
if p.got(_Semi) {
|
|
|
|
|
if forStmt {
|
|
|
|
|
if p.tok != _Semi {
|
|
|
|
|
condStmt = p.simpleStmt(nil, false)
|
|
|
|
|
}
|
|
|
|
|
p.want(_Semi)
|
|
|
|
|
if p.tok != _Lbrace {
|
|
|
|
|
post = p.simpleStmt(nil, false)
|
|
|
|
|
}
|
|
|
|
|
} else if p.tok != _Lbrace {
|
|
|
|
|
condStmt = p.simpleStmt(nil, false)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
condStmt = init
|
|
|
|
|
init = nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// unpack condStmt
|
|
|
|
|
switch s := condStmt.(type) {
|
|
|
|
|
case nil:
|
|
|
|
|
// nothing to do
|
|
|
|
|
case *ExprStmt:
|
|
|
|
|
cond = s.X
|
|
|
|
|
default:
|
|
|
|
|
p.error("invalid condition, tag, or type switch guard")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p.xnest = outer
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *parser) ifStmt() *IfStmt {
|
|
|
|
|
if trace {
|
|
|
|
|
defer p.trace("ifStmt")()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s := new(IfStmt)
|
|
|
|
|
s.init(p)
|
|
|
|
|
|
|
|
|
|
p.want(_If)
|
|
|
|
|
s.Init, s.Cond, _ = p.header(false)
|
|
|
|
|
if s.Cond == nil {
|
|
|
|
|
p.error("missing condition in if statement")
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-30 16:31:53 -07:00
|
|
|
if gcCompat {
|
|
|
|
|
s.init(p)
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-04 17:09:08 -08:00
|
|
|
s.Then = p.stmtBody("if clause")
|
|
|
|
|
|
|
|
|
|
if p.got(_Else) {
|
2016-06-06 17:59:05 -07:00
|
|
|
switch p.tok {
|
|
|
|
|
case _If:
|
2016-03-04 17:09:08 -08:00
|
|
|
s.Else = p.ifStmt()
|
2016-06-06 17:59:05 -07:00
|
|
|
case _Lbrace:
|
2016-03-04 17:09:08 -08:00
|
|
|
s.Else = p.blockStmt()
|
2016-06-06 17:59:05 -07:00
|
|
|
default:
|
|
|
|
|
p.error("else must be followed by if or statement block")
|
|
|
|
|
p.advance(_Name, _Rbrace)
|
2016-03-04 17:09:08 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *parser) switchStmt() *SwitchStmt {
|
|
|
|
|
if trace {
|
|
|
|
|
defer p.trace("switchStmt")()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p.want(_Switch)
|
|
|
|
|
s := new(SwitchStmt)
|
|
|
|
|
s.init(p)
|
|
|
|
|
|
|
|
|
|
s.Init, s.Tag, _ = p.header(false)
|
|
|
|
|
|
|
|
|
|
if !p.got(_Lbrace) {
|
|
|
|
|
p.syntax_error("missing { after switch clause")
|
|
|
|
|
p.advance(_Case, _Default, _Rbrace)
|
|
|
|
|
}
|
|
|
|
|
for p.tok != _EOF && p.tok != _Rbrace {
|
|
|
|
|
s.Body = append(s.Body, p.caseClause())
|
|
|
|
|
}
|
|
|
|
|
p.want(_Rbrace)
|
|
|
|
|
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *parser) selectStmt() *SelectStmt {
|
|
|
|
|
if trace {
|
|
|
|
|
defer p.trace("selectStmt")()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p.want(_Select)
|
|
|
|
|
s := new(SelectStmt)
|
|
|
|
|
s.init(p)
|
|
|
|
|
|
|
|
|
|
if !p.got(_Lbrace) {
|
|
|
|
|
p.syntax_error("missing { after select clause")
|
|
|
|
|
p.advance(_Case, _Default, _Rbrace)
|
|
|
|
|
}
|
|
|
|
|
for p.tok != _EOF && p.tok != _Rbrace {
|
|
|
|
|
s.Body = append(s.Body, p.commClause())
|
|
|
|
|
}
|
|
|
|
|
p.want(_Rbrace)
|
|
|
|
|
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *parser) caseClause() *CaseClause {
|
|
|
|
|
if trace {
|
|
|
|
|
defer p.trace("caseClause")()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c := new(CaseClause)
|
|
|
|
|
c.init(p)
|
|
|
|
|
|
|
|
|
|
switch p.tok {
|
|
|
|
|
case _Case:
|
|
|
|
|
p.next()
|
|
|
|
|
c.Cases = p.exprList()
|
|
|
|
|
|
|
|
|
|
case _Default:
|
|
|
|
|
p.next()
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
p.syntax_error("expecting case or default or }")
|
|
|
|
|
p.advance(_Case, _Default, _Rbrace)
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-06 17:59:05 -07:00
|
|
|
if gcCompat {
|
|
|
|
|
c.init(p)
|
|
|
|
|
}
|
2016-03-04 17:09:08 -08:00
|
|
|
p.want(_Colon)
|
|
|
|
|
c.Body = p.stmtList()
|
|
|
|
|
|
|
|
|
|
return c
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *parser) commClause() *CommClause {
|
|
|
|
|
if trace {
|
|
|
|
|
defer p.trace("commClause")()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c := new(CommClause)
|
|
|
|
|
c.init(p)
|
|
|
|
|
|
|
|
|
|
switch p.tok {
|
|
|
|
|
case _Case:
|
|
|
|
|
p.next()
|
|
|
|
|
lhs := p.exprList()
|
|
|
|
|
|
2016-09-15 17:40:26 -07:00
|
|
|
if _, ok := lhs.(*ListExpr); !ok && p.tok == _Larrow {
|
2016-03-04 17:09:08 -08:00
|
|
|
// lhs <- x
|
|
|
|
|
} else {
|
|
|
|
|
// lhs
|
|
|
|
|
// lhs = <-x
|
|
|
|
|
// lhs := <-x
|
|
|
|
|
if p.tok == _Assign || p.tok == _Define {
|
|
|
|
|
// TODO(gri) check that lhs has at most 2 entries
|
|
|
|
|
} else if p.tok == _Colon {
|
|
|
|
|
// TODO(gri) check that lhs has at most 1 entry
|
|
|
|
|
} else {
|
|
|
|
|
panic("unimplemented")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c.Comm = p.simpleStmt(lhs, false)
|
|
|
|
|
|
|
|
|
|
case _Default:
|
|
|
|
|
p.next()
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
p.syntax_error("expecting case or default or }")
|
|
|
|
|
p.advance(_Case, _Default, _Rbrace)
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-06 17:59:05 -07:00
|
|
|
if gcCompat {
|
|
|
|
|
c.init(p)
|
|
|
|
|
}
|
2016-03-04 17:09:08 -08:00
|
|
|
p.want(_Colon)
|
|
|
|
|
c.Body = p.stmtList()
|
|
|
|
|
|
|
|
|
|
return c
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO(gri) find a better solution
|
2016-09-16 11:00:54 +10:00
|
|
|
var missing_stmt Stmt = new(EmptyStmt) // = nod(OXXX, nil, nil)
|
2016-03-04 17:09:08 -08:00
|
|
|
|
|
|
|
|
// Statement =
|
|
|
|
|
// Declaration | LabeledStmt | SimpleStmt |
|
|
|
|
|
// GoStmt | ReturnStmt | BreakStmt | ContinueStmt | GotoStmt |
|
|
|
|
|
// FallthroughStmt | Block | IfStmt | SwitchStmt | SelectStmt | ForStmt |
|
|
|
|
|
// DeferStmt .
|
|
|
|
|
//
|
|
|
|
|
// stmt may return missing_stmt.
|
|
|
|
|
func (p *parser) stmt() 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 {
|
|
|
|
|
lhs := p.exprList()
|
2016-06-06 17:59:05 -07:00
|
|
|
if label, ok := lhs.(*Name); ok && p.tok == _Colon {
|
2016-03-04 17:09:08 -08:00
|
|
|
return p.labeledStmt(label)
|
|
|
|
|
}
|
|
|
|
|
return p.simpleStmt(lhs, false)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch p.tok {
|
|
|
|
|
case _Lbrace:
|
|
|
|
|
return p.blockStmt()
|
|
|
|
|
|
|
|
|
|
case _Var:
|
|
|
|
|
return p.declStmt(p.varDecl)
|
|
|
|
|
|
|
|
|
|
case _Const:
|
|
|
|
|
return p.declStmt(p.constDecl)
|
|
|
|
|
|
|
|
|
|
case _Type:
|
|
|
|
|
return p.declStmt(p.typeDecl)
|
|
|
|
|
|
|
|
|
|
case _Operator, _Star:
|
|
|
|
|
switch p.op {
|
|
|
|
|
case Add, Sub, Mul, And, Xor, Not:
|
|
|
|
|
return p.simpleStmt(nil, false) // unary operators
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case _Literal, _Func, _Lparen, // operands
|
|
|
|
|
_Lbrack, _Struct, _Map, _Chan, _Interface, // composite types
|
2016-09-15 17:40:26 -07:00
|
|
|
_Larrow: // receive operator
|
2016-03-04 17:09:08 -08:00
|
|
|
return p.simpleStmt(nil, false)
|
|
|
|
|
|
|
|
|
|
case _For:
|
|
|
|
|
return p.forStmt()
|
|
|
|
|
|
|
|
|
|
case _Switch:
|
|
|
|
|
return p.switchStmt()
|
|
|
|
|
|
|
|
|
|
case _Select:
|
|
|
|
|
return p.selectStmt()
|
|
|
|
|
|
|
|
|
|
case _If:
|
|
|
|
|
return p.ifStmt()
|
|
|
|
|
|
|
|
|
|
case _Fallthrough:
|
|
|
|
|
p.next()
|
|
|
|
|
s := new(BranchStmt)
|
|
|
|
|
s.init(p)
|
|
|
|
|
s.Tok = _Fallthrough
|
|
|
|
|
return s
|
|
|
|
|
// // will be converted to OFALL
|
2016-09-16 11:00:54 +10:00
|
|
|
// stmt := nod(OXFALL, nil, nil)
|
2016-03-04 17:09:08 -08:00
|
|
|
// stmt.Xoffset = int64(block)
|
|
|
|
|
// return stmt
|
|
|
|
|
|
|
|
|
|
case _Break, _Continue:
|
|
|
|
|
tok := p.tok
|
|
|
|
|
p.next()
|
|
|
|
|
s := new(BranchStmt)
|
|
|
|
|
s.init(p)
|
|
|
|
|
s.Tok = tok
|
|
|
|
|
if p.tok == _Name {
|
|
|
|
|
s.Label = p.name()
|
|
|
|
|
}
|
|
|
|
|
return s
|
|
|
|
|
|
|
|
|
|
case _Go, _Defer:
|
|
|
|
|
return p.callStmt()
|
|
|
|
|
|
|
|
|
|
case _Goto:
|
|
|
|
|
p.next()
|
|
|
|
|
s := new(BranchStmt)
|
|
|
|
|
s.init(p)
|
|
|
|
|
s.Tok = _Goto
|
|
|
|
|
s.Label = p.name()
|
|
|
|
|
return s
|
2016-09-16 11:00:54 +10:00
|
|
|
// stmt := nod(OGOTO, p.new_name(p.name()), nil)
|
2016-03-04 17:09:08 -08:00
|
|
|
// stmt.Sym = dclstack // context, for goto restrictions
|
|
|
|
|
// return stmt
|
|
|
|
|
|
|
|
|
|
case _Return:
|
|
|
|
|
p.next()
|
|
|
|
|
s := new(ReturnStmt)
|
|
|
|
|
s.init(p)
|
|
|
|
|
if p.tok != _Semi && p.tok != _Rbrace {
|
|
|
|
|
s.Results = p.exprList()
|
|
|
|
|
}
|
2016-08-30 16:31:53 -07:00
|
|
|
if gcCompat {
|
|
|
|
|
s.init(p)
|
|
|
|
|
}
|
2016-03-04 17:09:08 -08:00
|
|
|
return s
|
|
|
|
|
|
|
|
|
|
case _Semi:
|
|
|
|
|
s := new(EmptyStmt)
|
|
|
|
|
s.init(p)
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return missing_stmt
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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.stmt()
|
|
|
|
|
if s == missing_stmt {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
l = append(l, s)
|
|
|
|
|
// customized version of osemi:
|
|
|
|
|
// ';' is optional before a closing ')' or '}'
|
|
|
|
|
if p.tok == _Rparen || p.tok == _Rbrace {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if !p.got(_Semi) {
|
|
|
|
|
p.syntax_error("at end of statement")
|
|
|
|
|
p.advance(_Semi, _Rbrace)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Arguments = "(" [ ( ExpressionList | Type [ "," ExpressionList ] ) [ "..." ] [ "," ] ] ")" .
|
2016-06-06 17:59:05 -07:00
|
|
|
func (p *parser) call(fun Expr) *CallExpr {
|
2016-03-04 17:09:08 -08:00
|
|
|
if trace {
|
2016-06-06 17:59:05 -07:00
|
|
|
defer p.trace("call")()
|
2016-03-04 17:09:08 -08:00
|
|
|
}
|
|
|
|
|
|
2016-06-06 17:59:05 -07:00
|
|
|
// call or conversion
|
|
|
|
|
// convtype '(' expr ocomma ')'
|
|
|
|
|
c := new(CallExpr)
|
|
|
|
|
c.init(p)
|
|
|
|
|
c.Fun = fun
|
|
|
|
|
|
2016-03-04 17:09:08 -08:00
|
|
|
p.want(_Lparen)
|
|
|
|
|
p.xnest++
|
|
|
|
|
|
|
|
|
|
for p.tok != _EOF && p.tok != _Rparen {
|
2016-06-06 17:59:05 -07:00
|
|
|
c.ArgList = append(c.ArgList, p.expr()) // expr_or_type
|
|
|
|
|
c.HasDots = p.got(_DotDotDot)
|
|
|
|
|
if !p.ocomma(_Rparen) || c.HasDots {
|
2016-03-04 17:09:08 -08:00
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p.xnest--
|
2016-06-06 17:59:05 -07:00
|
|
|
if gcCompat {
|
|
|
|
|
c.init(p)
|
|
|
|
|
}
|
2016-03-04 17:09:08 -08:00
|
|
|
p.want(_Rparen)
|
|
|
|
|
|
2016-06-06 17:59:05 -07:00
|
|
|
return c
|
2016-03-04 17:09:08 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
// Common productions
|
|
|
|
|
|
|
|
|
|
func (p *parser) name() *Name {
|
|
|
|
|
// no tracing to avoid overly verbose output
|
|
|
|
|
|
|
|
|
|
n := new(Name)
|
|
|
|
|
n.init(p)
|
|
|
|
|
|
|
|
|
|
if p.tok == _Name {
|
|
|
|
|
n.Value = p.lit
|
|
|
|
|
p.next()
|
|
|
|
|
} else {
|
|
|
|
|
n.Value = "_"
|
|
|
|
|
p.syntax_error("expecting name")
|
|
|
|
|
p.advance()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return n
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// IdentifierList = identifier { "," identifier } .
|
|
|
|
|
// The first name must be provided.
|
|
|
|
|
func (p *parser) nameList(first *Name) []*Name {
|
|
|
|
|
if trace {
|
|
|
|
|
defer p.trace("nameList")()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if debug && first == nil {
|
|
|
|
|
panic("first name not provided")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
l := []*Name{first}
|
|
|
|
|
for p.got(_Comma) {
|
|
|
|
|
l = append(l, p.name())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return l
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The first name may be provided, or nil.
|
|
|
|
|
func (p *parser) qualifiedName(name *Name) Expr {
|
|
|
|
|
if trace {
|
|
|
|
|
defer p.trace("qualifiedName")()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch {
|
|
|
|
|
case name != nil:
|
|
|
|
|
// name is provided
|
|
|
|
|
case p.tok == _Name:
|
|
|
|
|
name = p.name()
|
|
|
|
|
default:
|
|
|
|
|
name = new(Name)
|
|
|
|
|
name.init(p)
|
|
|
|
|
p.syntax_error("expecting name")
|
|
|
|
|
p.advance(_Dot, _Semi, _Rbrace)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return p.dotname(name)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExpressionList = Expression { "," Expression } .
|
|
|
|
|
func (p *parser) exprList() Expr {
|
|
|
|
|
if trace {
|
|
|
|
|
defer p.trace("exprList")()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
x := p.expr()
|
|
|
|
|
if p.got(_Comma) {
|
|
|
|
|
list := []Expr{x, p.expr()}
|
|
|
|
|
for p.got(_Comma) {
|
|
|
|
|
list = append(list, p.expr())
|
|
|
|
|
}
|
|
|
|
|
t := new(ListExpr)
|
|
|
|
|
t.init(p) // TODO(gri) what is the correct thing here?
|
|
|
|
|
t.ElemList = list
|
|
|
|
|
x = t
|
|
|
|
|
}
|
|
|
|
|
return x
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// osemi parses an optional semicolon.
|
|
|
|
|
func (p *parser) osemi(follow token) bool {
|
|
|
|
|
switch p.tok {
|
|
|
|
|
case _Semi:
|
|
|
|
|
p.next()
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
|
|
case _Rparen, _Rbrace:
|
|
|
|
|
// semicolon is optional before ) or }
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p.syntax_error("expecting semicolon, newline, or " + tokstring(follow))
|
|
|
|
|
p.advance(follow)
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ocomma parses an optional comma.
|
|
|
|
|
func (p *parser) ocomma(follow token) bool {
|
|
|
|
|
switch p.tok {
|
|
|
|
|
case _Comma:
|
|
|
|
|
p.next()
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
|
|
case _Rparen, _Rbrace:
|
|
|
|
|
// comma is optional before ) or }
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p.syntax_error("expecting comma or " + tokstring(follow))
|
|
|
|
|
p.advance(follow)
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// unparen removes all parentheses around an expression.
|
|
|
|
|
func unparen(x Expr) Expr {
|
|
|
|
|
for {
|
|
|
|
|
p, ok := x.(*ParenExpr)
|
|
|
|
|
if !ok {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
x = p.X
|
|
|
|
|
}
|
|
|
|
|
return x
|
|
|
|
|
}
|