| | |
| | |
| | |
| |
|
| | 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 |
| | first error |
| | errcnt int |
| | pragma Pragma |
| | goVersion string |
| |
|
| | top bool |
| | fnest int |
| | xnest int |
| | indent []byte |
| | } |
| |
|
| | 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, |
| | |
| | |
| | |
| | |
| | |
| | func(line, col uint, msg string) { |
| | if msg[0] != '/' { |
| | p.errorAt(p.posAt(line, col), msg) |
| | return |
| | } |
| |
|
| | |
| | |
| | |
| | text := commentText(msg) |
| | if (col == colbase || msg[1] == '*') && strings.HasPrefix(text, "line ") { |
| | var pos Pos |
| | if msg[1] == '/' { |
| | |
| | pos = MakePos(p.file, line+1, colbase) |
| | } else { |
| | |
| | |
| | |
| | |
| | pos = MakePos(p.file, line, col+uint(len(msg))) |
| | } |
| | p.updateBase(pos, line, col+2+5, text[5:]) |
| | return |
| | } |
| |
|
| | |
| | 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) |
| | } |
| | } |
| | }, |
| | directives, |
| | ) |
| |
|
| | p.base = file |
| | p.first = nil |
| | p.errcnt = 0 |
| | p.pragma = nil |
| |
|
| | p.fnest = 0 |
| | p.xnest = 0 |
| | p.indent = nil |
| | } |
| |
|
| | |
| | |
| | func (p *parser) takePragma() Pragma { |
| | prag := p.pragma |
| | p.pragma = nil |
| | return prag |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | func (p *parser) clearPragma() { |
| | if p.pragma != nil { |
| | p.pragh(p.pos(), p.scanner.blank, "", p.pragma) |
| | p.pragma = nil |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | func (p *parser) updateBase(pos Pos, tline, tcol uint, text string) { |
| | i, n, ok := trailingDigits(text) |
| | if i == 0 { |
| | return |
| | } |
| | |
| |
|
| | if !ok { |
| | |
| | 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 { |
| | |
| | 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] |
| | } else { |
| | |
| | line = n |
| | } |
| |
|
| | if line == 0 || line > PosMax { |
| | p.errorAt(p.posAt(tline, tcol+i), "invalid line number: "+text[i:]) |
| | return |
| | } |
| |
|
| | |
| | |
| | filename := text[:i-1] |
| | 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] |
| | } |
| |
|
| | |
| | |
| | i := len(s) |
| | if s[i-1] == '\r' { |
| | i-- |
| | } |
| | return s[2:i] |
| | } |
| |
|
| | func trailingDigits(text string) (uint, uint, bool) { |
| | i := strings.LastIndexByte(text, ':') |
| | if i < 0 { |
| | return 0, 0, false |
| | } |
| | |
| | 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() |
| | } |
| | } |
| |
|
| | |
| | |
| | func (p *parser) gotAssign() bool { |
| | switch p.tok { |
| | case _Define: |
| | p.syntaxError("expected =") |
| | fallthrough |
| | case _Assign: |
| | p.next() |
| | return true |
| | } |
| | return false |
| | } |
| |
|
| | |
| | |
| |
|
| | |
| | func (p *parser) posAt(line, col uint) Pos { |
| | return MakePos(p.base, line, col) |
| | } |
| |
|
| | |
| | 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) |
| | } |
| |
|
| | |
| | func (p *parser) syntaxErrorAt(pos Pos, msg string) { |
| | if trace { |
| | p.print("syntax error: " + msg) |
| | } |
| |
|
| | if p.tok == _EOF && p.first != nil { |
| | return |
| | } |
| |
|
| | |
| | switch { |
| | case msg == "": |
| | |
| | case strings.HasPrefix(msg, "in "), strings.HasPrefix(msg, "at "), strings.HasPrefix(msg, "after "): |
| | msg = " " + msg |
| | case strings.HasPrefix(msg, "expected "): |
| | msg = ", " + msg |
| | default: |
| | |
| | p.errorAt(pos, "syntax error: "+msg) |
| | return |
| | } |
| |
|
| | |
| | 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) |
| | } |
| |
|
| | |
| | |
| | p.errorAt(pos, "syntax error: unexpected "+tok+msg) |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | func tokstring(tok token) string { |
| | switch tok { |
| | case _Comma: |
| | return "comma" |
| | case _Semi: |
| | return "semicolon or newline" |
| | } |
| | s := tok.String() |
| | if _Break <= tok && tok <= _Var { |
| | return "keyword " + s |
| | } |
| | return s |
| | } |
| |
|
| | |
| | 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) } |
| |
|
| | |
| | |
| | |
| | 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 |
| |
|
| | |
| | |
| | |
| | |
| | func (p *parser) advance(followlist ...token) { |
| | if trace { |
| | p.print(fmt.Sprintf("advance %s", followlist)) |
| | } |
| |
|
| | |
| | |
| | var followset uint64 = 1 << _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()) |
| | } |
| | } |
| |
|
| | |
| | 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) |
| | } |
| | p.print(")") |
| | } |
| | } |
| |
|
| | func (p *parser) print(msg string) { |
| | fmt.Printf("%5d: %s%s\n", p.line, p.indent, msg) |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | |
| | func (p *parser) fileOrNil() *File { |
| | if trace { |
| | defer p.trace("file")() |
| | } |
| |
|
| | f := new(File) |
| | f.pos = p.pos() |
| |
|
| | |
| | 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) |
| |
|
| | |
| | if p.first != nil { |
| | return nil |
| | } |
| |
|
| | |
| | |
| | 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]) { |
| | |
| | p.syntaxError("unexpected semicolon or newline before {") |
| | } else { |
| | p.syntaxError("non-declaration statement outside function body") |
| | } |
| | p.advance(_Import, _Const, _Type, _Var, _Func) |
| | continue |
| | } |
| |
|
| | |
| | |
| | p.clearPragma() |
| |
|
| | if p.tok != _EOF && !p.got(_Semi) { |
| | p.syntaxError("after top level declaration") |
| | p.advance(_Import, _Const, _Type, _Var, _Func) |
| | } |
| | } |
| | |
| |
|
| | p.clearPragma() |
| | f.EOF = p.pos() |
| |
|
| | return f |
| | } |
| |
|
| | func isEmptyFuncDecl(dcl Decl) bool { |
| | f, ok := dcl.(*FuncDecl) |
| | return ok && f.Body == nil |
| | } |
| |
|
| | |
| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | 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() |
| | |
| | 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 { |
| | |
| | return p.pos() |
| | } |
| | } |
| | } |
| |
|
| | pos := p.pos() |
| | p.want(close) |
| | return pos |
| | } |
| |
|
| | |
| | func (p *parser) appendGroup(list []Decl, f func(*Group) Decl) []Decl { |
| | if p.tok == _Lparen { |
| | g := new(Group) |
| | p.clearPragma() |
| | p.next() |
| | 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 |
| | } |
| |
|
| | |
| | |
| | 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 |
| | } |
| | |
| |
|
| | return d |
| | } |
| |
|
| | |
| | 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 |
| | } |
| |
|
| | |
| | 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 { |
| | |
| | |
| | pos := p.pos() |
| | p.next() |
| | switch p.tok { |
| | case _Name: |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | var x Expr = p.name() |
| | if p.tok != _Lbrack { |
| | |
| | |
| | |
| | p.xnest++ |
| | x = p.binaryExpr(p.pexpr(x, false), 0) |
| | p.xnest-- |
| | } |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | if pname, ptype := extractName(x, p.tok == _Comma); pname != nil && (ptype != nil || p.tok != _Rbrack) { |
| | |
| | |
| | |
| | d.TParamList = p.paramList(pname, ptype, _Rbrack, true, false) |
| | d.Alias = p.gotAssign() |
| | d.Type = p.typeOrNil() |
| | } else { |
| | |
| | |
| | d.Type = p.arrayType(pos, x) |
| | } |
| | case _Rbrack: |
| | |
| | p.next() |
| | d.Type = p.sliceType(pos) |
| | default: |
| | |
| | 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 |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func extractName(x Expr, force bool) (*Name, Expr) { |
| | switch x := x.(type) { |
| | case *Name: |
| | return x, nil |
| | case *Operation: |
| | if x.Y == nil { |
| | break |
| | } |
| | switch x.Op { |
| | case Mul: |
| | if name, _ := x.X.(*Name); name != nil && (force || isTypeElem(x.Y)) { |
| | |
| | op := *x |
| | op.X, op.Y = op.Y, nil |
| | return name, &op |
| | } |
| | case Or: |
| | if name, lhs := extractName(x.X, force || isTypeElem(x.Y)); name != nil && lhs != nil { |
| | |
| | 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])) { |
| | |
| | |
| | |
| | const keep_parens = false |
| | if keep_parens { |
| | |
| | px := new(ParenExpr) |
| | px.pos = x.pos |
| | px.X = x.ArgList[0] |
| | return name, px |
| | } else { |
| | |
| | return name, Unparen(x.ArgList[0]) |
| | } |
| | } |
| | } |
| | } |
| | return nil, x |
| | } |
| |
|
| | |
| | |
| | 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 |
| | } |
| |
|
| | |
| | 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 |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | 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, 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-- |
| |
|
| | |
| | |
| | |
| | if p.mode&CheckBranches != 0 && errcnt == p.errcnt { |
| | checkBranches(body, p.errh) |
| | } |
| |
|
| | return body |
| | } |
| |
|
| | |
| | |
| |
|
| | func (p *parser) expr() Expr { |
| | if trace { |
| | defer p.trace("expr")() |
| | } |
| |
|
| | return p.binaryExpr(nil, 0) |
| | } |
| |
|
| | |
| | func (p *parser) binaryExpr(x Expr, prec int) Expr { |
| | |
| |
|
| | 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 |
| | } |
| |
|
| | |
| | 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() |
| | |
| | |
| | x.X = Unparen(p.unaryExpr()) |
| | return x |
| | } |
| |
|
| | case _Arrow: |
| | |
| | pos := p.pos() |
| | p.next() |
| |
|
| | |
| | |
| | |
| |
|
| | x := p.unaryExpr() |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | if _, ok := x.(*ChanType); ok { |
| | |
| | dir := SendOnly |
| | t := x |
| | for dir == SendOnly { |
| | c, ok := t.(*ChanType) |
| | if !ok { |
| | break |
| | } |
| | dir = c.Dir |
| | if dir == RecvOnly { |
| | |
| | |
| | p.syntaxError("unexpected <-, expected chan") |
| | |
| | } |
| | c.Dir = RecvOnly |
| | t = c.Elem |
| | } |
| | if dir == SendOnly { |
| | |
| | |
| | p.syntaxError(fmt.Sprintf("unexpected %s, expected chan", String(t))) |
| | |
| | } |
| | return x |
| | } |
| |
|
| | |
| | o := new(Operation) |
| | o.pos = pos |
| | o.Op = Recv |
| | o.X = x |
| | return o |
| | } |
| |
|
| | |
| | |
| | |
| | return p.pexpr(nil, true) |
| | } |
| |
|
| | |
| | func (p *parser) callStmt() *CallStmt { |
| | if trace { |
| | defer p.trace("callStmt")() |
| | } |
| |
|
| | s := new(CallStmt) |
| | s.pos = p.pos() |
| | s.Tok = p.tok |
| | p.next() |
| |
|
| | x := p.pexpr(nil, p.tok == _Lparen) |
| | if t := Unparen(x); t != x { |
| | p.errorAt(x.Pos(), fmt.Sprintf("expression in %s must not be parenthesized", s.Tok)) |
| | |
| | x = t |
| | } |
| |
|
| | s.Call = x |
| | return s |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | 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) |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | if p.tok == _Lbrace { |
| | keep_parens = true |
| | } |
| |
|
| | |
| | |
| | |
| | if keep_parens { |
| | px := new(ParenExpr) |
| | px.pos = pos |
| | px.X = x |
| | x = px |
| | } |
| | return x |
| |
|
| | case _Func: |
| | pos := p.pos() |
| | p.next() |
| | _, ftyp := p.funcType("function type") |
| | if p.tok == _Lbrace { |
| | p.xnest++ |
| |
|
| | f := new(FuncLit) |
| | f.pos = pos |
| | f.Type = ftyp |
| | f.Body = p.funcBody() |
| |
|
| | p.xnest-- |
| | return f |
| | } |
| | return ftyp |
| |
|
| | case _Lbrack, _Chan, _Map, _Struct, _Interface: |
| | return p.type_() |
| |
|
| | default: |
| | x := p.badExpr() |
| | p.syntaxError("expected expression") |
| | p.advance(_Rparen, _Rbrack, _Rbrace) |
| | return x |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | 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: |
| | |
| | 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.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 { |
| | |
| | p.syntaxError("expected operand") |
| | i = p.badExpr() |
| | } else { |
| | i, comma = p.typeList(false) |
| | } |
| | if comma || p.tok == _Rbrack { |
| | p.want(_Rbrack) |
| | |
| | t := new(IndexExpr) |
| | t.pos = pos |
| | t.X = x |
| | t.Index = i |
| | x = t |
| | break |
| | } |
| | } |
| |
|
| | |
| | |
| | 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 { |
| | |
| | t.Index[1] = p.expr() |
| | } |
| | if p.tok == _Colon { |
| | t.Full = true |
| | |
| | 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 { |
| | |
| | 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: |
| | |
| | |
| | t := Unparen(x) |
| | |
| | complit_ok := false |
| | switch t.(type) { |
| | case *Name, *SelectorExpr: |
| | if p.xnest >= 0 { |
| | |
| | complit_ok = true |
| | } |
| | case *IndexExpr: |
| | if p.xnest >= 0 && !isValue(t) { |
| | |
| | complit_ok = true |
| | } |
| | case *ArrayType, *SliceType, *StructType, *MapType: |
| | |
| | complit_ok = true |
| | } |
| | if !complit_ok { |
| | break loop |
| | } |
| | if t != x { |
| | p.syntaxError("cannot parenthesize type in composite literal") |
| | |
| | } |
| | n := p.complitexpr() |
| | n.Type = x |
| | x = n |
| |
|
| | default: |
| | break loop |
| | } |
| | } |
| |
|
| | return x |
| | } |
| |
|
| | |
| | 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 |
| | case *ParenExpr: |
| | return isValue(x.X) |
| | case *IndexExpr: |
| | return isValue(x.X) || isValue(x.Index) |
| | } |
| | return false |
| | } |
| |
|
| | |
| | func (p *parser) bare_complitexpr() Expr { |
| | if trace { |
| | defer p.trace("bare_complitexpr")() |
| | } |
| |
|
| | if p.tok == _Lbrace { |
| | |
| | return p.complitexpr() |
| | } |
| |
|
| | return p.expr() |
| | } |
| |
|
| | |
| | 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 { |
| | |
| | e := p.bare_complitexpr() |
| | if p.tok == _Colon { |
| | |
| | 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 |
| | } |
| |
|
| | |
| | |
| |
|
| | 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 |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func (p *parser) typeOrNil() Expr { |
| | if trace { |
| | defer p.trace("typeOrNil")() |
| | } |
| |
|
| | pos := p.pos() |
| | switch p.tok { |
| | case _Star: |
| | |
| | p.next() |
| | return newIndirect(pos, p.type_()) |
| |
|
| | case _Arrow: |
| | |
| | p.next() |
| | p.want(_Chan) |
| | t := new(ChanType) |
| | t.pos = pos |
| | t.Dir = RecvOnly |
| | t.Elem = p.chanElem() |
| | return t |
| |
|
| | case _Func: |
| | |
| | p.next() |
| | _, t := p.funcType("function type") |
| | return t |
| |
|
| | case _Lbrack: |
| | |
| | |
| | p.next() |
| | if p.got(_Rbrack) { |
| | return p.sliceType(pos) |
| | } |
| | return p.arrayType(pos, nil) |
| |
|
| | case _Chan: |
| | |
| | |
| | p.next() |
| | t := new(ChanType) |
| | t.pos = pos |
| | if p.got(_Arrow) { |
| | t.Dir = SendOnly |
| | } |
| | t.Elem = p.chanElem() |
| | return t |
| |
|
| | case _Map: |
| | |
| | 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) |
| | |
| | |
| | |
| | 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 |
| | } |
| |
|
| | |
| | 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 != "" { |
| | |
| | p.syntaxErrorAt(typ.pos, context+" must have no type parameters") |
| | } |
| | if p.tok == _Rbrack { |
| | p.syntaxError("empty type parameter list") |
| | p.next() |
| | } else { |
| | tparamList = p.paramList(nil, nil, _Rbrack, true, false) |
| | } |
| | } |
| |
|
| | p.want(_Lparen) |
| | typ.ParamList = p.paramList(nil, nil, _Rparen, false, true) |
| | typ.ResultList = p.funcResult() |
| |
|
| | return tparamList, typ |
| | } |
| |
|
| | |
| | |
| | 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 { |
| | |
| | |
| | |
| | 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 |
| | } |
| |
|
| | |
| | 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") |
| | |
| | } |
| |
|
| | return typ |
| | } |
| |
|
| | |
| | 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 |
| | } |
| |
|
| | |
| | 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 |
| | } |
| |
|
| | |
| | func (p *parser) funcResult() []*Field { |
| | if trace { |
| | defer p.trace("funcResult")() |
| | } |
| |
|
| | if p.got(_Lparen) { |
| | return p.paramList(nil, nil, _Rparen, false, false) |
| | } |
| |
|
| | pos := p.pos() |
| | if typ := p.typeOrNil(); typ != nil { |
| | f := new(Field) |
| | f.pos = pos |
| | f.Type = typ |
| | return []*Field{f} |
| | } |
| |
|
| | return nil |
| | } |
| |
|
| | func (p *parser) addField(styp *StructType, pos Pos, name *Name, typ Expr, tag *BasicLit) { |
| | if tag != nil { |
| | for i := len(styp.FieldList) - len(styp.TagList); i > 0; i-- { |
| | styp.TagList = append(styp.TagList, nil) |
| | } |
| | styp.TagList = append(styp.TagList, tag) |
| | } |
| |
|
| | f := new(Field) |
| | f.pos = pos |
| | f.Name = name |
| | f.Type = typ |
| | styp.FieldList = append(styp.FieldList, f) |
| |
|
| | if debug && tag != nil && len(styp.FieldList) != len(styp.TagList) { |
| | panic("inconsistent struct field list") |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | 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 { |
| | |
| | typ := p.qualifiedName(name) |
| | tag := p.oliteral() |
| | p.addField(styp, pos, nil, typ, tag) |
| | break |
| | } |
| |
|
| | |
| | names := p.nameList(name) |
| | var typ Expr |
| |
|
| | |
| | |
| | if len(names) == 1 && p.tok == _Lbrack { |
| | typ = p.arrayOrTArgs() |
| | if typ, ok := typ.(*IndexExpr); ok { |
| | |
| | typ.X = name |
| | tag := p.oliteral() |
| | p.addField(styp, pos, nil, typ, tag) |
| | break |
| | } |
| | } else { |
| | |
| | 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 { |
| | |
| | p.syntaxError("cannot parenthesize embedded type") |
| | p.next() |
| | typ = p.qualifiedName(nil) |
| | p.got(_Rparen) |
| | } else { |
| | |
| | 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 { |
| | |
| | pos := p.pos() |
| | p.next() |
| | typ = newIndirect(pos, p.qualifiedName(nil)) |
| | } else { |
| | |
| | typ = p.qualifiedName(nil) |
| | } |
| | p.got(_Rparen) |
| | 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) |
| | } |
| |
|
| | |
| | n, comma := p.typeList(false) |
| | p.want(_Rbrack) |
| | if !comma { |
| | if elem := p.typeOrNil(); elem != nil { |
| | |
| | t := new(ArrayType) |
| | t.pos = pos |
| | t.Len = n |
| | t.Elem = elem |
| | return t |
| | } |
| | } |
| |
|
| | |
| | t := new(IndexExpr) |
| | t.pos = pos |
| | |
| | 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 |
| | } |
| |
|
| | |
| | |
| | |
| | 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: |
| | |
| | f.Name = name |
| | _, f.Type = p.funcType(context) |
| |
|
| | case _Lbrack: |
| | |
| | |
| | |
| | pos := p.pos() |
| | p.next() |
| |
|
| | |
| | |
| | if p.tok == _Rbrack { |
| | |
| | pos := p.pos() |
| | p.next() |
| | if p.tok == _Lparen { |
| | |
| | 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 |
| | } |
| |
|
| | |
| | |
| | list := p.paramList(nil, nil, _Rbrack, false, false) |
| | if len(list) == 0 { |
| | |
| | |
| | |
| | if p.tok == _Lparen { |
| | f.Name = name |
| | _, f.Type = p.funcType(context) |
| | } else { |
| | f.Type = name |
| | } |
| | break |
| | } |
| |
|
| | |
| | if list[0].Name != nil { |
| | |
| | f.Name = name |
| | _, f.Type = p.funcType(context) |
| | p.errorAt(pos, "interface method must have no type parameters") |
| | break |
| | } |
| |
|
| | |
| | t := new(IndexExpr) |
| | t.pos = pos |
| | t.X = name |
| | if len(list) == 1 { |
| | t.Index = list[0].Type |
| | } else { |
| | |
| | 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: |
| | |
| | f.Type = p.qualifiedName(name) |
| | } |
| |
|
| | return f |
| | } |
| |
|
| | |
| | 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 |
| | } |
| |
|
| | |
| | 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 |
| | } |
| |
|
| | |
| | func (p *parser) paramDeclOrNil(name *Name, follow token) *Field { |
| | if trace { |
| | defer p.trace("paramDeclOrNil")() |
| | } |
| |
|
| | |
| | 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 { |
| | |
| | if name == nil { |
| | name = p.name() |
| | } |
| |
|
| | if p.tok == _Lbrack { |
| | |
| | f.Type = p.arrayOrTArgs() |
| | if typ, ok := f.Type.(*IndexExpr); ok { |
| | |
| | typ.X = name |
| | } else { |
| | |
| | f.Name = name |
| | } |
| | if typeSetsOk && p.tok == _Operator && p.op == Or { |
| | |
| | |
| | f = p.embeddedElem(f) |
| | } |
| | return f |
| | } |
| |
|
| | if p.tok == _Dot { |
| | |
| | f.Type = p.qualifiedName(name) |
| | if typeSetsOk && p.tok == _Operator && p.op == Or { |
| | |
| | f = p.embeddedElem(f) |
| | } |
| | return f |
| | } |
| |
|
| | if typeSetsOk && p.tok == _Operator && p.op == Or { |
| | |
| | f.Type = name |
| | return p.embeddedElem(f) |
| | } |
| |
|
| | f.Name = name |
| | } |
| |
|
| | if p.tok == _DotDotDot { |
| | |
| | t := new(DotsType) |
| | t.pos = p.pos() |
| | p.next() |
| | t.Elem = p.typeOrNil() |
| | if t.Elem == nil { |
| | f.Type = p.badExpr() |
| | p.syntaxError("... is missing type") |
| | } else { |
| | f.Type = t |
| | } |
| | return f |
| | } |
| |
|
| | if typeSetsOk && p.tok == _Operator && p.op == Tilde { |
| | |
| | f.Type = p.embeddedElem(nil).Type |
| | return f |
| | } |
| |
|
| | f.Type = p.typeOrNil() |
| | if typeSetsOk && p.tok == _Operator && p.op == Or && f.Type != nil { |
| | |
| | f = p.embeddedElem(f) |
| | } |
| | if f.Name != nil || f.Type != nil { |
| | return f |
| | } |
| |
|
| | p.syntaxError("expected " + tokstring(follow)) |
| | p.advance(_Comma, follow) |
| | return nil |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | func (p *parser) paramList(name *Name, typ Expr, close token, requireNames, dddok bool) (list []*Field) { |
| | if trace { |
| | defer p.trace("paramList")() |
| | } |
| |
|
| | |
| | |
| | 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 |
| | var typed int |
| | 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 |
| | typ = nil |
| | 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 |
| | } |
| |
|
| | |
| | if named == 0 && !requireNames { |
| | |
| | for _, par := range list { |
| | if typ := par.Name; typ != nil { |
| | par.Type = typ |
| | par.Name = nil |
| | } |
| | } |
| | } else if named != len(list) { |
| | |
| | var errPos Pos |
| | var typ Expr |
| | 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 { |
| | |
| | errPos = par.Name.Pos() |
| | t := p.badExpr() |
| | t.pos = errPos |
| | par.Type = t |
| | } |
| | } |
| | if errPos.IsKnown() { |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | var msg string |
| | if named == typed { |
| | errPos = end |
| | if requireNames { |
| | msg = "missing type constraint" |
| | } else { |
| | msg = "missing parameter type" |
| | } |
| | } else { |
| | if requireNames { |
| | msg = "missing type parameter name" |
| | |
| | if len(list) == 1 { |
| | msg += " or invalid array length" |
| | } |
| | } else { |
| | msg = "missing parameter name" |
| | } |
| | } |
| | p.syntaxErrorAt(errPos, msg) |
| | } |
| | } |
| |
|
| | |
| | first := true |
| | for i, f := range list { |
| | if t, _ := f.Type.(*DotsType); t != nil && (!dddok || i+1 < len(list)) { |
| | if first { |
| | first = false |
| | if dddok { |
| | p.errorAt(t.pos, "can only use ... with final parameter") |
| | } else { |
| | p.errorAt(t.pos, "invalid use of ...") |
| | } |
| | } |
| | |
| | f.Type = t.Elem |
| | } |
| | } |
| |
|
| | return |
| | } |
| |
|
| | func (p *parser) badExpr() *BadExpr { |
| | b := new(BadExpr) |
| | b.pos = p.pos() |
| | return b |
| | } |
| |
|
| | |
| | |
| |
|
| | |
| | func (p *parser) simpleStmt(lhs Expr, keyword token) SimpleStmt { |
| | if trace { |
| | defer p.trace("simpleStmt")() |
| | } |
| |
|
| | if keyword == _For && p.tok == _Range { |
| | |
| | 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 { |
| | |
| | pos := p.pos() |
| | switch p.tok { |
| | case _AssignOp: |
| | |
| | op := p.op |
| | p.next() |
| | return p.newAssignStmt(pos, op, lhs, p.expr()) |
| |
|
| | case _IncOp: |
| | |
| | op := p.op |
| | p.next() |
| | return p.newAssignStmt(pos, op, lhs, nil) |
| |
|
| | case _Arrow: |
| | |
| | s := new(SendStmt) |
| | s.pos = pos |
| | p.next() |
| | s.Chan = lhs |
| | s.Value = p.expr() |
| | return s |
| |
|
| | default: |
| | |
| | s := new(ExprStmt) |
| | s.pos = lhs.Pos() |
| | s.X = lhs |
| | return s |
| | } |
| | } |
| |
|
| | |
| | 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 { |
| | |
| | return p.newRangeClause(lhs, op == Def) |
| | } |
| |
|
| | |
| | rhs := p.exprList() |
| |
|
| | if x, ok := rhs.(*TypeSwitchGuard); ok && keyword == _Switch && op == Def { |
| | if lhs, ok := lhs.(*Name); ok { |
| | |
| | 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) |
| | |
| | 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() |
| | 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 { |
| | |
| | |
| | |
| | e := new(EmptyStmt) |
| | e.pos = p.pos() |
| | s.Stmt = e |
| | return s |
| | } |
| |
|
| | s.Stmt = p.stmtOrNil() |
| | if s.Stmt != nil { |
| | return s |
| | } |
| |
|
| | |
| | p.syntaxErrorAt(s.pos, "missing statement after label") |
| | |
| | return nil |
| | } |
| |
|
| | |
| | func (p *parser) blockStmt(context string) *BlockStmt { |
| | if trace { |
| | defer p.trace("blockStmt")() |
| | } |
| |
|
| | s := new(BlockStmt) |
| | s.pos = p.pos() |
| |
|
| | |
| | if !p.got(_Lbrace) { |
| | p.syntaxError("expected { after " + context) |
| | p.advance(_Name, _Rbrace) |
| | s.Rbrace = p.pos() |
| | 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() |
| | 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 |
| | } |
| | |
| |
|
| | outer := p.xnest |
| | p.xnest = -1 |
| |
|
| | if p.tok != _Semi { |
| | |
| | if p.got(_Var) { |
| | p.syntaxError(fmt.Sprintf("var declaration not allowed in %s initializer", keyword.String())) |
| | } |
| | init = p.simpleStmt(nil, keyword) |
| | |
| | if _, ok := init.(*RangeClause); ok { |
| | p.xnest = outer |
| | return |
| | } |
| | } |
| |
|
| | var condStmt SimpleStmt |
| | var semi struct { |
| | pos Pos |
| | lit string |
| | } |
| | if p.tok != _Lbrace { |
| | if p.tok == _Semi { |
| | semi.pos = p.pos() |
| | semi.lit = p.lit |
| | p.next() |
| | } else { |
| | |
| | p.want(_Lbrace) |
| | if p.tok != _Lbrace { |
| | p.advance(_Lbrace, _Rbrace) |
| | } |
| | } |
| | if keyword == _For { |
| | if p.tok != _Semi { |
| | if p.tok == _Lbrace { |
| | p.syntaxError("expected for loop condition") |
| | goto done |
| | } |
| | condStmt = p.simpleStmt(nil, 0 ) |
| | } |
| | p.want(_Semi) |
| | if p.tok != _Lbrace { |
| | post = p.simpleStmt(nil, 0 ) |
| | 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: |
| | |
| | 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: |
| | |
| | |
| | |
| | |
| | var str string |
| | if as, ok := s.(*AssignStmt); ok && as.Op == 0 { |
| | |
| | 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 |
| | } |
| |
|
| | |
| | |
| | func emphasize(x Expr) string { |
| | s := String(x) |
| | if op, _ := x.(*Operation); op != nil && op.Y != nil { |
| | |
| | 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) |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | 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) stmtOrNil() Stmt { |
| | if trace { |
| | defer p.trace("stmt " + p.tok.String())() |
| | } |
| |
|
| | |
| | |
| | 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) |
| | } |
| |
|
| | case _Literal, _Func, _Lparen, |
| | _Lbrack, _Struct, _Map, _Chan, _Interface, |
| | _Arrow: |
| | 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 |
| | } |
| |
|
| | |
| | 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) |
| | |
| | if !p.got(_Semi) && p.tok != _Rbrace { |
| | p.syntaxError("at end of statement") |
| | p.advance(_Semi, _Rbrace, _Case, _Default) |
| | p.got(_Semi) |
| | } |
| | } |
| | return |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | 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 |
| | } |
| |
|
| | |
| | |
| |
|
| | func (p *parser) name() *Name { |
| | |
| |
|
| | 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 |
| | } |
| |
|
| | |
| | |
| | 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 |
| | } |
| |
|
| | |
| | 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 |
| | } |
| |
|
| | |
| | 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 |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | 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() |
| | l.ElemList = list |
| | x = l |
| | } |
| | } |
| | p.xnest-- |
| | return |
| | } |
| |
|
| | |
| | func Unparen(x Expr) Expr { |
| | for { |
| | p, ok := x.(*ParenExpr) |
| | if !ok { |
| | break |
| | } |
| | x = p.X |
| | } |
| | return x |
| | } |
| |
|
| | |
| | func UnpackListExpr(x Expr) []Expr { |
| | switch x := x.(type) { |
| | case nil: |
| | return nil |
| | case *ListExpr: |
| | return x.ElemList |
| | default: |
| | return []Expr{x} |
| | } |
| | } |
| |
|