| // 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 | |
| // 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 | |
| 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 | |
| } | |
| type parserError string // for error recovery if no error handler was installed | |
| func (p *parser) init(src io.Reader, errh ErrorHandler, pragh PragmaHandler) { | |
| p.scanner.init(src, errh, pragh) | |
| p.fnest = 0 | |
| p.xnest = 0 | |
| p.indent = 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.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) { | |
| 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) { | |
| if trace { | |
| defer p.trace("syntax_error (" + msg + ")")() | |
| } | |
| if p.tok == _EOF && p.first != nil { | |
| return // avoid meaningless follow-up errors | |
| } | |
| // add punctuation etc. as needed to msg | |
| switch { | |
| case msg == "": | |
| // nothing to do | |
| case strings.HasPrefix(msg, "in"), strings.HasPrefix(msg, "at"), strings.HasPrefix(msg, "after"): | |
| msg = " " + msg | |
| case strings.HasPrefix(msg, "expecting"): | |
| msg = ", " + msg | |
| default: | |
| // plain error - we don't care about current token | |
| p.error_at(pos, line, "syntax error: "+msg) | |
| return | |
| } | |
| // determine token string | |
| var tok string | |
| switch p.tok { | |
| case _Name: | |
| 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.error_at(pos, line, "syntax error: unexpected "+tok+msg) | |
| } | |
| // The stopset contains keywords that start a statement. | |
| // They are good synchronization points in case of syntax | |
| // errors and (usually) shouldn't be skipped over. | |
| const stopset uint64 = 1<<_Break | | |
| 1<<_Const | | |
| 1<<_Continue | | |
| 1<<_Defer | | |
| 1<<_Fallthrough | | |
| 1<<_For | | |
| 1<<_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 | |
| if !p.got(_Package) { | |
| p.syntax_error("package statement must be first") | |
| return nil | |
| } | |
| f.PkgName = p.name() | |
| p.want(_Semi) | |
| // don't bother continuing if package clause has errors | |
| if p.first != nil { | |
| return nil | |
| } | |
| // { ImportDecl ";" } | |
| for p.got(_Import) { | |
| f.DeclList = p.appendGroup(f.DeclList, p.importDecl) | |
| p.want(_Semi) | |
| } | |
| // { TopLevelDecl ";" } | |
| for p.tok != _EOF { | |
| switch p.tok { | |
| case _Const: | |
| p.next() | |
| f.DeclList = p.appendGroup(f.DeclList, p.constDecl) | |
| case _Type: | |
| p.next() | |
| f.DeclList = p.appendGroup(f.DeclList, p.typeDecl) | |
| case _Var: | |
| p.next() | |
| f.DeclList = p.appendGroup(f.DeclList, p.varDecl) | |
| case _Func: | |
| p.next() | |
| 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 | |
| } | |
| // Reset p.pragma BEFORE advancing to the next token (consuming ';') | |
| // since comments before may set pragmas for the next function decl. | |
| p.pragma = 0 | |
| 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() | |
| } | |
| if p.tok == _Literal && (gcCompat || p.kind == StringLit) { | |
| d.Path = p.oliteral() | |
| } else { | |
| p.syntax_error("missing import path; require quoted string") | |
| p.advance(_Semi, _Rparen) | |
| } | |
| d.Group = group | |
| return d | |
| } | |
| // ConstSpec = IdentifierList [ [ Type ] "=" ExpressionList ] . | |
| func (p *parser) constDecl(group *Group) Decl { | |
| if trace { | |
| defer p.trace("constDecl")() | |
| } | |
| d := new(ConstDecl) | |
| d.init(p) | |
| d.NameList = p.nameList(p.name()) | |
| 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 | |
| } | |
| // TypeSpec = identifier Type . | |
| func (p *parser) typeDecl(group *Group) Decl { | |
| if trace { | |
| defer p.trace("typeDecl")() | |
| } | |
| d := new(TypeDecl) | |
| d.init(p) | |
| d.Name = p.name() | |
| d.Type = p.tryType() | |
| if d.Type == nil { | |
| p.syntax_error("in type declaration") | |
| p.advance(_Semi, _Rparen) | |
| } | |
| d.Group = group | |
| d.Pragma = p.pragma | |
| return d | |
| } | |
| // VarSpec = IdentifierList ( Type [ "=" ExpressionList ] | "=" ExpressionList ) . | |
| func (p *parser) varDecl(group *Group) Decl { | |
| if trace { | |
| defer p.trace("varDecl")() | |
| } | |
| d := new(VarDecl) | |
| d.init(p) | |
| d.NameList = p.nameList(p.name()) | |
| if p.got(_Assign) { | |
| d.Values = p.exprList() | |
| } else { | |
| d.Type = p.type_() | |
| if p.got(_Assign) { | |
| d.Values = p.exprList() | |
| } | |
| } | |
| d.Group = group | |
| if gcCompat { | |
| d.init(p) | |
| } | |
| return d | |
| } | |
| // FunctionDecl = "func" FunctionName ( Function | Signature ) . | |
| // FunctionName = identifier . | |
| // Function = Signature FunctionBody . | |
| // MethodDecl = "func" Receiver MethodName ( Function | Signature ) . | |
| // Receiver = Parameters . | |
| func (p *parser) funcDecl() *FuncDecl { | |
| if trace { | |
| defer p.trace("funcDecl")() | |
| } | |
| f := new(FuncDecl) | |
| f.init(p) | |
| badRecv := false | |
| if p.tok == _Lparen { | |
| rcvr := p.paramList() | |
| switch len(rcvr) { | |
| case 0: | |
| p.error("method has no receiver") | |
| badRecv = true | |
| case 1: | |
| f.Recv = rcvr[0] | |
| default: | |
| p.error("method has multiple receivers") | |
| badRecv = true | |
| } | |
| } | |
| if p.tok != _Name { | |
| p.syntax_error("expecting name or (") | |
| p.advance(_Lbrace, _Semi) | |
| return nil | |
| } | |
| // 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") | |
| // } | |
| // } | |
| f.Name = p.name() | |
| f.Type = p.funcType() | |
| if gcCompat { | |
| f.node = f.Type.node | |
| } | |
| f.Body = p.funcBody() | |
| f.Pragma = p.pragma | |
| f.EndLine = uint32(p.line) | |
| // TODO(gri) deal with function properties | |
| // if noescape && body != nil { | |
| // p.error("can only use //go:noescape with external func implementations") | |
| // } | |
| if badRecv { | |
| return nil // TODO(gri) better solution | |
| } | |
| 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) | |
| if gcCompat { | |
| t.init(p) | |
| } | |
| 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() | |
| if gcCompat { | |
| x.init(p) | |
| } | |
| 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 | |
| } | |
| case _Arrow: | |
| // 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)) | |
| if _, ok := x.(*ChanType); ok { | |
| // x is a channel type => re-associate <- | |
| dir := SendOnly | |
| t := x | |
| for dir == SendOnly { | |
| c, ok := t.(*ChanType) | |
| if !ok { | |
| break | |
| } | |
| dir = c.Dir | |
| if dir == RecvOnly { | |
| // t is type <-chan E but <-<-chan E is not permitted | |
| // (report same error as for "type _ <-<-chan E") | |
| p.syntax_error("unexpected <-, expecting chan") | |
| // already progressed, no need to advance | |
| } | |
| c.Dir = RecvOnly | |
| t = c.Elem | |
| } | |
| if dir == SendOnly { | |
| // channel dir is <- but channel element E is not a channel | |
| // (report same error as for "type _ <-chan<-E") | |
| p.syntax_error(fmt.Sprintf("unexpected %s, expecting chan", String(t))) | |
| // already progressed, no need to advance | |
| } | |
| return x | |
| } | |
| // x is not a channel type => we have a receive op | |
| return &Operation{Op: Recv, X: x} | |
| } | |
| // TODO(mdempsky): We need parens here so we can report an | |
| // error for "(x) := true". It should be possible to detect | |
| // and reject that more efficiently though. | |
| return p.pexpr(true) | |
| } | |
| // callStmt parses call-like statements that can be preceded by 'defer' and 'go'. | |
| func (p *parser) callStmt() *CallStmt { | |
| if trace { | |
| defer p.trace("callStmt")() | |
| } | |
| s := new(CallStmt) | |
| s.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 | |
| if gcCompat { | |
| s.node = x.node | |
| } | |
| 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() | |
| f.EndLine = uint32(p.line) | |
| 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) | |
| } | |
| if gcCompat { | |
| x.init(p) | |
| } | |
| 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) { | |
| t.Full = true | |
| // 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: | |
| x = p.call(x) | |
| 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() | |
| if gcCompat { | |
| l.init(p) | |
| } | |
| e = l | |
| x.NKeys++ | |
| } | |
| x.ElemList = append(x.ElemList, e) | |
| if !p.ocomma(_Rbrace) { | |
| break | |
| } | |
| } | |
| x.EndLine = uint32(p.line) | |
| 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_()) | |
| case _Arrow: | |
| // 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) | |
| if p.got(_Arrow) { | |
| t.Dir = SendOnly | |
| } | |
| t.Elem = p.chanElem() | |
| return t | |
| case _Map: | |
| // _Map '[' ntype ']' ntype | |
| p.next() | |
| p.want(_Lbrack) | |
| t := new(MapType) | |
| t.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() | |
| if gcCompat { | |
| typ.init(p) | |
| } | |
| 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 | |
| } | |
| } | |
| if gcCompat { | |
| typ.init(p) | |
| } | |
| 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 | |
| } | |
| } | |
| if gcCompat { | |
| typ.init(p) | |
| } | |
| 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) | |
| if gcCompat && name != nil { | |
| f.node = name.node | |
| } | |
| 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 { | |
| case _Name, _Star, _Arrow, _Func, _Lbrack, _Chan, _Map, _Struct, _Interface, _Lparen: | |
| // sym name_or_type | |
| f.Type = p.type_() | |
| case _DotDotDot: | |
| // sym dotdotdot | |
| f.Type = p.dotsType() | |
| case _Dot: | |
| // name_or_type | |
| // from dotname | |
| f.Type = p.dotname(f.Name) | |
| f.Name = nil | |
| } | |
| case _Arrow, _Star, _Func, _Lbrack, _Chan, _Map, _Struct, _Interface, _Lparen: | |
| // name_or_type | |
| f.Type = p.type_() | |
| case _DotDotDot: | |
| // dotdotdot | |
| f.Type = p.dotsType() | |
| default: | |
| p.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) | |
| case _Arrow: | |
| // lhs <- rhs | |
| p.next() | |
| s := new(SendStmt) | |
| s.init(p) | |
| s.Chan = lhs | |
| s.Value = p.expr() | |
| if gcCompat { | |
| s.init(p) | |
| } | |
| 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: | |
| var n node | |
| n.init(p) | |
| 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} | |
| } | |
| as := p.newAssignStmt(Def, lhs, rhs) | |
| if gcCompat { | |
| as.node = n | |
| } | |
| return as | |
| 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() | |
| if gcCompat { | |
| r.init(p) | |
| } | |
| 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")() | |
| } | |
| s := new(LabeledStmt) | |
| s.init(p) | |
| s.Label = label | |
| p.want(_Colon) | |
| if p.tok != _Rbrace && p.tok != _EOF { | |
| s.Stmt = p.stmt() | |
| if s.Stmt == missing_stmt { | |
| // report error at line of ':' token | |
| p.syntax_error_at(int(label.pos), int(label.line), "missing statement after label") | |
| // 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) | |
| if gcCompat { | |
| s.init(p) | |
| } | |
| 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 | |
| if forStmt && p.got(_Var) { | |
| p.error("var declaration not allowed in for initializer") | |
| } | |
| 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") | |
| } | |
| if gcCompat { | |
| s.init(p) | |
| } | |
| s.Then = p.stmtBody("if clause") | |
| if p.got(_Else) { | |
| switch p.tok { | |
| case _If: | |
| s.Else = p.ifStmt() | |
| case _Lbrace: | |
| s.Else = p.blockStmt() | |
| default: | |
| p.error("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")() | |
| } | |
| 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) | |
| } | |
| if gcCompat { | |
| c.init(p) | |
| } | |
| 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() | |
| if _, ok := lhs.(*ListExpr); !ok && p.tok == _Arrow { | |
| // 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) | |
| } | |
| if gcCompat { | |
| c.init(p) | |
| } | |
| p.want(_Colon) | |
| c.Body = p.stmtList() | |
| return c | |
| } | |
| // TODO(gri) find a better solution | |
| var missing_stmt Stmt = new(EmptyStmt) // = nod(OXXX, nil, nil) | |
| // 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() | |
| if label, ok := lhs.(*Name); ok && p.tok == _Colon { | |
| 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 | |
| _Arrow: // receive operator | |
| 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 | |
| // stmt := nod(OXFALL, nil, nil) | |
| // 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 | |
| // stmt := nod(OGOTO, p.new_name(p.name()), nil) | |
| // 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() | |
| } | |
| if gcCompat { | |
| s.init(p) | |
| } | |
| 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 ] ) [ "..." ] [ "," ] ] ")" . | |
| func (p *parser) call(fun Expr) *CallExpr { | |
| if trace { | |
| defer p.trace("call")() | |
| } | |
| // call or conversion | |
| // convtype '(' expr ocomma ')' | |
| c := new(CallExpr) | |
| c.init(p) | |
| c.Fun = fun | |
| p.want(_Lparen) | |
| p.xnest++ | |
| for p.tok != _EOF && p.tok != _Rparen { | |
| c.ArgList = append(c.ArgList, p.expr()) // expr_or_type | |
| c.HasDots = p.got(_DotDotDot) | |
| if !p.ocomma(_Rparen) || c.HasDots { | |
| break | |
| } | |
| } | |
| p.xnest-- | |
| if gcCompat { | |
| c.init(p) | |
| } | |
| p.want(_Rparen) | |
| return c | |
| } | |
| // ---------------------------------------------------------------------------- | |
| // 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 | |
| } |