From: Robert Griesemer Date: Wed, 4 Nov 2015 17:21:49 +0000 (-0800) Subject: cmd/compile/internal/gc: recursive-descent parser X-Git-Tag: go1.6beta1~464 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=b569b87ca1f6905ed38cafc2bae6e26f4ec416b7;p=gostls13.git cmd/compile/internal/gc: recursive-descent parser This is a translation of the yacc-based parser with adjustements to make the grammar work for a recursive-descent parser followed by cleanups and simplifications. The yacc actions were mostly literally copied for correctness with better temporary names. A few of the syntax tests were adjusted for slightly different error messages (it is very difficult to match the yacc-based error messages in all cases, and sometimes the new parser could produce better errors). The new parser is enabled by default. To switch back to the yacc-based parser, set -oldparser. To hardwire the switch back, uncomment "oldparser = 1" in lex.go. - passes all.bash - ~18% reduced parse time per file on average for make.bash - ~3% reduced compile time for building cmd/compile Change-Id: Icb5651bb9d8b9f66261762d2c94a03793050d4ce Reviewed-on: https://go-review.googlesource.com/16665 Run-TryBot: Robert Griesemer TryBot-Result: Gobot Gobot Reviewed-by: Russ Cox --- diff --git a/src/cmd/compile/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go index 64c3e4772f..29747d5478 100644 --- a/src/cmd/compile/internal/gc/go.go +++ b/src/cmd/compile/internal/gc/go.go @@ -628,7 +628,7 @@ var Widthint int var Widthreg int -var typesw *Node +var typesw *Node // TODO(gri) remove when yacc-based parser is gone var nblank *Node diff --git a/src/cmd/compile/internal/gc/lex.go b/src/cmd/compile/internal/gc/lex.go index 9fe0f6cbc1..b1e8f11b92 100644 --- a/src/cmd/compile/internal/gc/lex.go +++ b/src/cmd/compile/internal/gc/lex.go @@ -202,7 +202,8 @@ func Main() { obj.Flagcount("live", "debug liveness analysis", &debuglive) obj.Flagcount("m", "print optimization decisions", &Debug['m']) obj.Flagcount("msan", "build code compatible with C/C++ memory sanitizer", &flag_msan) - obj.Flagcount("newexport", "use new export format", &newexport) // TODO remove eventually + obj.Flagcount("newexport", "use new export format", &newexport) // TODO(gri) remove eventually + obj.Flagcount("oldparser", "use old parser", &oldparser) // TODO(gri) remove eventually obj.Flagcount("nolocalimports", "reject local (relative) imports", &nolocalimports) obj.Flagstr("o", "write output to `file`", &outfile) obj.Flagstr("p", "set expected package import `path`", &myimportpath) @@ -317,7 +318,19 @@ func Main() { lexlineno = 1 const BOM = 0xFEFF + // Uncomment the line below to temporarily switch the compiler back + // to the yacc-based parser. Short-term work-around for issues with + // the new recursive-descent parser for which setting -oldparser is + // not sufficient. + // TODO(gri) remove this eventually + // + // oldparser = 1 + for _, infile = range flag.Args() { + if trace && Debug['x'] != 0 && oldparser == 0 { + fmt.Printf("--- %s ---\n", infile) + } + linehistpush(infile) curio.infile = infile @@ -831,6 +844,10 @@ func importfile(f *Val, line int) { curio.nlsemi = false typecheckok = true + if oldparser == 0 { + push_parser() + } + case 'B': // new export format obj.Bgetc(imp) // skip \n after $$B @@ -850,6 +867,10 @@ func importfile(f *Val, line int) { } func unimportfile() { + if oldparser == 0 { + pop_parser() + } + if curio.bin != nil { obj.Bterm(curio.bin) curio.bin = nil @@ -879,6 +900,10 @@ func cannedimports(file string, cp string) { typecheckok = true incannedimport = 1 + + if oldparser == 0 { + push_parser() + } } func isSpace(c int) bool { @@ -1358,8 +1383,10 @@ l0: // a '{' with loophack == true becomes LBODY and disables loophack. // // I said it was clumsy. + // + // We only need the loophack when running with -oldparser. case '(', '[': - if loophack || _yylex_lstk != nil { + if oldparser != 0 && (loophack || _yylex_lstk != nil) { h = new(Loophack) if h == nil { Flusherrors() @@ -1376,7 +1403,7 @@ l0: goto lx case ')', ']': - if _yylex_lstk != nil { + if oldparser != 0 && _yylex_lstk != nil { h = _yylex_lstk loophack = h.v _yylex_lstk = h.next @@ -1385,7 +1412,7 @@ l0: goto lx case '{': - if loophack { + if oldparser != 0 && loophack { if Debug['x'] != 0 { fmt.Printf("%v lex: LBODY\n", Ctxt.Line(int(lexlineno))) } @@ -1460,7 +1487,9 @@ talph: goto l0 case LFOR, LIF, LSWITCH, LSELECT: - loophack = true // see comment about loophack above + if oldparser != 0 { + loophack = true // see comment about loophack above + } } if Debug['x'] != 0 { @@ -1902,13 +1931,18 @@ func (yy) Error(msg string) { Yyerror("%s", msg) } +var oldparser int // if set, theparser is used (otherwise we use the recursive-descent parser) var theparser yyParser var parsing bool func yyparse() { - theparser = yyNewParser() parsing = true - theparser.Parse(yy{}) + if oldparser != 0 { + theparser = yyNewParser() + theparser.Parse(yy{}) + } else { + parse_file() + } parsing = false } diff --git a/src/cmd/compile/internal/gc/parser.go b/src/cmd/compile/internal/gc/parser.go new file mode 100644 index 0000000000..4f0a6ea924 --- /dev/null +++ b/src/cmd/compile/internal/gc/parser.go @@ -0,0 +1,3438 @@ +// Copyright 2015 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 gc + +import ( + "fmt" + "strconv" + "strings" +) + +const trace = true // if set, parse tracing can be enabled with -x + +// TODO(gri) Once we handle imports w/o redirecting the underlying +// source of the lexer we can get rid of these. They are here for +// compatibility with the existing yacc-based parser setup. +var thenewparser parser // the parser in use +var savedstate []parser // saved parser state, used during import + +func push_parser() { + savedstate = append(savedstate, thenewparser) + thenewparser = parser{} + thenewparser.next() +} + +func pop_parser() { + n := len(savedstate) - 1 + thenewparser = savedstate[n] + savedstate = savedstate[:n] +} + +func parse_file() { + // This doesn't quite work w/ the trybots. Fun experiment but we need to find a better way. + // go func() { + // prev := lexlineno + // for { + // time.Sleep(5 * time.Second) + // t := lexlineno // racy but we don't care - any new value will do + // if prev == t { + // // If lexlineno doesn't change anymore we probably have an endless + // // loop somewhere. Terminate before process becomes unresponsive. + // Yyerror("internal error: compiler makes no progress (workaround: -oldparser)") + // errorexit() + // } + // prev = t + // } + // }() + + thenewparser = parser{} + thenewparser.loadsys() + thenewparser.next() + thenewparser.file() +} + +// This loads the definitions for the low-level runtime functions, +// so that the compiler can generate calls to them, +// but does not make the name "runtime" visible as a package. +// +// go.y:loadsys +func (p *parser) loadsys() { + if trace && Debug['x'] != 0 { + defer p.trace("loadsys")() + } + + importpkg = Runtimepkg + + if Debug['A'] != 0 { + cannedimports("runtime.Builtin", "package runtime\n\n$$\n\n") + } else { + cannedimports("runtime.Builtin", runtimeimport) + } + curio.importsafe = true + + p.import_package() + p.import_there() + + importpkg = nil +} + +type parser struct { + tok int32 // next token (one-token look-ahead) + op Op // valid if tok == LASOP + val Val // valid if tok == LLITERAL + sym_ *Sym // valid if tok == LNAME + nest int // expression nesting level (for complit ambiguity resolution) + yy yySymType // for temporary use by next + indent int // tracing support +} + +func (p *parser) next() { + p.tok = yylex(&p.yy) + p.op = Op(p.yy.i) + p.val = p.yy.val + p.sym_ = p.yy.sym +} + +func (p *parser) got(tok int32) bool { + if p.tok == tok { + p.next() + return true + } + return false +} + +func (p *parser) want(tok int32) { + if p.tok != EOF && !p.got(tok) { + p.error("") + } +} + +// ---------------------------------------------------------------------------- +// Syntax error handling + +// TODO(gri) Approach this more systematically. For now it passes all tests. + +func syntax_error(msg string) { + Yyerror("syntax error: " + msg) +} + +func (p *parser) error(context string) { + if p.tok == EOF { + return + } + syntax_error("unexpected " + tokstring(p.tok) + context) + // TODO(gri) keep also track of nesting below + switch p.tok { + case '(': + // skip to closing ')' + for p.tok != EOF && p.tok != ')' { + p.next() + } + case '{': + // skip to closing '}' + for p.tok != EOF && p.tok != '}' { + p.next() + } + } + p.next() // make progress +} + +func tokstring(tok int32) string { + switch tok { + case EOF: + return "EOF" + case ',': + return "comma" + case ';': + return "semicolon or newline" + } + if 0 <= tok && tok < 128 { + // get invisibles properly backslashed + s := strconv.QuoteRune(tok) + if n := len(s); n > 0 && s[0] == '\'' && s[n-1] == '\'' { + s = s[1 : n-1] + } + return s + } + if s := tokstrings[tok]; s != "" { + return s + } + // catchall + return yyTokname(int(tok)) +} + +// TODO(gri) figure out why yyTokname doesn't work for us as expected +var tokstrings = map[int32]string{ + LLITERAL: "LLITERAL", + LASOP: "op=", + LCOLAS: ":=", + LBREAK: "break", + LCASE: "case", + LCHAN: "chan", + LCONST: "const", + LCONTINUE: "continue", + LDDD: "...", + LDEFAULT: "default", + LDEFER: "defer", + LELSE: "else", + LFALL: "fallthrough", + LFOR: "for", + LFUNC: "func", + LGO: "go", + LGOTO: "goto", + LIF: "if", + LIMPORT: "import", + LINTERFACE: "interface", + LMAP: "map", + LNAME: "", + LPACKAGE: "package", + LRANGE: "range", + LRETURN: "return", + LSELECT: "select", + LSTRUCT: "struct", + LSWITCH: "switch", + LTYPE: "type", + LVAR: "var", + LANDAND: "&&", + LANDNOT: "&^", + LBODY: "LBODY", // we should never see this one + LCOMM: "<-", + LDEC: "--", + LEQ: "==", + LGE: ">=", + LGT: ">", + LIGNORE: "LIGNORE", // we should never see this one + LINC: "++", + LLE: "<=", + LLSH: "<<", + LLT: "<", + LNE: "!=", + LOROR: "||", + LRSH: ">>", + NotPackage: "NotPackage", // we should never see this one + NotParen: "NotParen", // we should never see this one + PreferToRightParen: "PreferToRightParen", // we should never see this one +} + +func (p *parser) print_trace(msg ...interface{}) { + const dots = ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . " + const n = len(dots) + fmt.Printf("%5d: ", lineno) + + // TODO(gri) imports screw up p.indent - fix this + if p.indent < 0 { + p.indent = 0 + } + + i := 2 * p.indent + for i > n { + fmt.Print(dots) + i -= n + } + // i <= n + fmt.Print(dots[0:i]) + fmt.Println(msg...) +} + +// usage: defer p.trace(msg)() +func (p *parser) trace(msg string) func() { + p.print_trace(msg, "(") + p.indent++ + return func() { + p.indent-- + if x := recover(); x != nil { + panic(x) // skip print_trace + } + p.print_trace(")") + } +} + +// ---------------------------------------------------------------------------- +// Parsing package files + +// go.y:file +func (p *parser) file() { + if trace && Debug['x'] != 0 { + defer p.trace("file")() + } + + p.package_() + + //go.y:imports + for p.tok == LIMPORT { + p.import_() + p.want(';') + } + + xtop = concat(xtop, p.xdcl_list()) +} + +// go.y:package +func (p *parser) package_() { + if trace && Debug['x'] != 0 { + defer p.trace("package_")() + } + + if p.got(LPACKAGE) { + mkpackage(p.sym().Name) + p.want(';') + } else { + prevlineno = lineno // TODO(gri) do we still need this? (e.g., not needed for test/fixedbugs/bug050.go) + Yyerror("package statement must be first") + errorexit() + } +} + +// import: +// LIMPORT import_stmt +// | LIMPORT '(' import_stmt_list osemi ')' +// | LIMPORT '(' ')' + +func (p *parser) import_() { + if trace && Debug['x'] != 0 { + defer p.trace("import_")() + } + + p.want(LIMPORT) + if p.got('(') { + for p.tok != EOF && p.tok != ')' { + p.import_stmt() + p.osemi() + } + p.want(')') + } else { + p.import_stmt() + } +} + +// go.y:import_stmt +func (p *parser) import_stmt() { + if trace && Debug['x'] != 0 { + defer p.trace("import_stmt")() + } + + line := int32(p.import_here()) + if p.tok == LPACKAGE { + p.import_package() + p.import_there() + + ipkg := importpkg + my := importmyname + importpkg = nil + importmyname = nil + + if my == nil { + my = Lookup(ipkg.Name) + } + + pack := Nod(OPACK, nil, nil) + pack.Sym = my + pack.Name.Pkg = ipkg + pack.Lineno = line + + if strings.HasPrefix(my.Name, ".") { + importdot(ipkg, pack) + return + } + if my.Name == "init" { + lineno = line + Yyerror("cannot import package as init - init must be a func") + return + } + if my.Name == "_" { + return + } + if my.Def != nil { + lineno = line + redeclare(my, "as imported package name") + } + my.Def = pack + my.Lastlineno = line + my.Block = 1 // at top level + + return + } + + p.import_there() + // When an invalid import path is passed to importfile, + // it calls Yyerror and then sets up a fake import with + // no package statement. This allows us to test more + // than one invalid import statement in a single file. + if nerrors == 0 { + Fatalf("phase error in import") + } +} + +// go.y:import_here +func (p *parser) import_here() int { + if trace && Debug['x'] != 0 { + defer p.trace("import_here")() + } + + importmyname = nil + switch p.tok { + case LNAME, '@', '?': + // import with given name + importmyname = p.sym() + + case '.': + // import into my name space + importmyname = Lookup(".") + p.next() + } + + var path Val + if p.tok == LLITERAL { + path = p.val + p.next() + } else { + syntax_error("missing import path; require quoted string") + } + + line := parserline() // TODO(gri) check correct placement of this + importfile(&path, line) + return line +} + +// go.y:import_package +func (p *parser) import_package() { + if trace && Debug['x'] != 0 { + defer p.trace("import_package")() + } + + p.want(LPACKAGE) + var name string + if p.tok == LNAME { + name = p.sym_.Name + p.next() + } else { + p.import_error() + } + + // go.y:import_safety + if p.tok == LNAME { + if p.sym_.Name == "safe" { + curio.importsafe = true + } + p.next() + } + p.want(';') + + if importpkg.Name == "" { + importpkg.Name = name + numImport[name]++ + } else if importpkg.Name != name { + Yyerror("conflicting names %s and %s for package %q", importpkg.Name, name, importpkg.Path) + } + if incannedimport == 0 { + importpkg.Direct = true + } + importpkg.Safe = curio.importsafe + + if safemode != 0 && !curio.importsafe { + Yyerror("cannot import unsafe package %q", importpkg.Path) + } +} + +// go.y:import_there +func (p *parser) import_there() { + if trace && Debug['x'] != 0 { + defer p.trace("import_there")() + } + + defercheckwidth() + + p.hidden_import_list() + p.want('$') + // don't read past 2nd '$' + if p.tok != '$' { + p.import_error() + } + + resumecheckwidth() + unimportfile() +} + +// go.y:common_dcl +func (p *parser) common_dcl() *NodeList { + if trace && Debug['x'] != 0 { + defer p.trace("common_dcl")() + } + + var dcl func() *NodeList + switch p.tok { + case LVAR: + dcl = p.vardcl + + case LCONST: + iota_ = 0 + dcl = p.constdcl + + case LTYPE: + dcl = p.typedcl + + default: + panic("unreachable") + } + + p.next() + var l *NodeList + if p.got('(') { + for p.tok != EOF && p.tok != ')' { + l = concat(l, dcl()) + p.osemi() + } + p.want(')') + } else { + l = dcl() + } + + iota_ = -100000 + lastconst = nil + + return l +} + +// go.y:vardcl +func (p *parser) vardcl() *NodeList { + if trace && Debug['x'] != 0 { + defer p.trace("vardcl")() + } + + names := p.dcl_name_list() + var typ *Node + var exprs *NodeList + if p.got('=') { + exprs = p.expr_list() + } else { + typ = p.ntype() + if p.got('=') { + exprs = p.expr_list() + } + } + + return variter(names, typ, exprs) +} + +// go.y:constdcl +func (p *parser) constdcl() *NodeList { + if trace && Debug['x'] != 0 { + defer p.trace("constdcl")() + } + + names := p.dcl_name_list() + var typ *Node + var exprs *NodeList + if p.tok != EOF && p.tok != ';' && p.tok != ')' { + if p.tok != '=' { + typ = p.ntype() + } + if p.got('=') { + exprs = p.expr_list() + } + } + + return constiter(names, typ, exprs) +} + +// go.y:typedcl +func (p *parser) typedcl() *NodeList { + if trace && Debug['x'] != 0 { + defer p.trace("typedcl")() + } + + name := typedcl0(p.sym()) + + // handle case where type is missing + var typ *Node + if p.tok != ';' { + typ = p.ntype() + } else { + p.error(" in type declaration") + } + + return list1(typedcl1(name, typ, true)) +} + +// go.y:simple_stmt +// may return missing_stmt if labelOk is set +func (p *parser) simple_stmt(labelOk, rangeOk bool) *Node { + if trace && Debug['x'] != 0 { + defer p.trace("simple_stmt")() + } + + if rangeOk && p.got(LRANGE) { + // LRANGE expr + r := Nod(ORANGE, nil, p.expr()) + r.Etype = 0 // := flag + return r + } + + lhs := p.expr_list() + + if count(lhs) == 1 && p.tok != '=' && p.tok != LCOLAS && p.tok != LRANGE { + // expr + lhs := lhs.N + switch p.tok { + case LASOP: + // expr LASOP expr + op := p.op + p.next() + rhs := p.expr() + + stmt := Nod(OASOP, lhs, rhs) + stmt.Etype = EType(op) // rathole to pass opcode + return stmt + + case LINC: + // expr LINC + p.next() + + stmt := Nod(OASOP, lhs, Nodintconst(1)) + stmt.Implicit = true + stmt.Etype = EType(OADD) + return stmt + + case LDEC: + // expr LDEC + p.next() + + stmt := Nod(OASOP, lhs, Nodintconst(1)) + stmt.Implicit = true + stmt.Etype = EType(OSUB) + return stmt + + case ':': + // labelname ':' stmt + if labelOk { + // If we have a labelname, it was parsed by operand + // (calling p.name()) and given an ONAME, ONONAME, or OTYPE node. + if lhs.Op == ONAME || lhs.Op == ONONAME || lhs.Op == OTYPE { + lhs = newname(lhs.Sym) + } else { + p.error(", expecting semicolon or newline or }") + } + lhs := Nod(OLABEL, lhs, nil) + lhs.Sym = dclstack // context, for goto restrictions + p.next() // consume ':' after making label node for correct lineno + return p.labeled_stmt(lhs) + } + fallthrough + + default: + // expr + // These nodes do not carry line numbers. + // Since a bare name used as an expression is an error, + // introduce a wrapper node to give the correct line. + switch lhs.Op { + case ONAME, ONONAME, OTYPE, OPACK, OLITERAL: + lhs = Nod(OPAREN, lhs, nil) + lhs.Implicit = true + } + return lhs + } + } + + // expr_list + switch p.tok { + case '=': + p.next() + if rangeOk && p.got(LRANGE) { + // expr_list '=' LRANGE expr + r := Nod(ORANGE, nil, p.expr()) + r.List = lhs + r.Etype = 0 // := flag + return r + } + + // expr_list '=' expr_list + rhs := p.expr_list() + + if lhs.Next == nil && rhs.Next == nil { + // simple + return Nod(OAS, lhs.N, rhs.N) + } + // multiple + stmt := Nod(OAS2, nil, nil) + stmt.List = lhs + stmt.Rlist = rhs + return stmt + + case LCOLAS: + line := lineno + p.next() + + if rangeOk && p.got(LRANGE) { + // expr_list LCOLAS LRANGE expr + r := Nod(ORANGE, nil, p.expr()) + r.List = lhs + r.Colas = true + colasdefn(lhs, r) + return r + } + + // expr_list LCOLAS expr_list + rhs := p.expr_list() + + if rhs.N.Op == OTYPESW { + ss := Nod(OTYPESW, nil, rhs.N.Right) + if rhs.Next != nil { + Yyerror("expr.(type) must be alone in list") + } + if lhs.Next != nil { + Yyerror("argument count mismatch: %d = %d", count(lhs), 1) + } else if (lhs.N.Op != ONAME && lhs.N.Op != OTYPE && lhs.N.Op != ONONAME && (lhs.N.Op != OLITERAL || lhs.N.Name == nil)) || isblank(lhs.N) { + Yyerror("invalid variable name %s in type switch", lhs.N) + } else { + ss.Left = dclname(lhs.N.Sym) + } // it's a colas, so must not re-use an oldname. + return ss + } + return colas(lhs, rhs, int32(line)) + + default: + p.error(", expecting := or = or comma") + return nil + } +} + +// may return missing_stmt +func (p *parser) labeled_stmt(label *Node) *Node { + if trace && Debug['x'] != 0 { + defer p.trace("labeled_stmt")() + } + + var ls *Node // labeled statement + if p.tok != '}' && p.tok != EOF { + ls = p.stmt() + if ls == missing_stmt { + // report error at line of ':' token + saved := lexlineno + lexlineno = prevlineno + syntax_error("missing statement after label") + lexlineno = saved + return missing_stmt + } + } + + label.Name.Defn = ls + l := list1(label) + if ls != nil { + l = list(l, ls) + } + return liststmt(l) +} + +// go.y:case +func (p *parser) case_(tswitch *Node) *Node { + if trace && Debug['x'] != 0 { + defer p.trace("case_")() + } + + switch p.tok { + case LCASE: + p.next() + cases := p.expr_list() // expr_or_type_list + switch p.tok { + case ':': + // LCASE expr_or_type_list ':' + + // will be converted to OCASE + // right will point to next case + // done in casebody() + markdcl() + stmt := Nod(OXCASE, nil, nil) + stmt.List = cases + if tswitch != nil { + if n := tswitch.Left; n != nil { + // type switch - declare variable + nn := newname(n.Sym) + declare(nn, dclcontext) + stmt.Rlist = list1(nn) + + // keep track of the instances for reporting unused + nn.Name.Defn = tswitch + } + } + + p.next() // consume ':' after declaring type switch var for correct lineno + return stmt + + case '=': + // LCASE expr_or_type_list '=' expr ':' + p.next() + rhs := p.expr() + + // will be converted to OCASE + // right will point to next case + // done in casebody() + markdcl() + stmt := Nod(OXCASE, nil, nil) + var n *Node + if cases.Next == nil { + n = Nod(OAS, cases.N, rhs) + } else { + n = Nod(OAS2, nil, nil) + n.List = cases + n.Rlist = list1(rhs) + } + stmt.List = list1(n) + + p.want(':') // consume ':' after declaring select cases for correct lineno + return stmt + + case LCOLAS: + // LCASE expr_or_type_list LCOLAS expr ':' + p.next() + rhs := p.expr() + + // will be converted to OCASE + // right will point to next case + // done in casebody() + markdcl() + stmt := Nod(OXCASE, nil, nil) + stmt.List = list1(colas(cases, list1(rhs), int32(p.op))) + + p.want(':') // consume ':' after declaring select cases for correct lineno + return stmt + + default: + p.error(", expecting := or = or : or comma") + return nil + } + + case LDEFAULT: + // LDEFAULT ':' + p.next() + + markdcl() + stmt := Nod(OXCASE, nil, nil) + if tswitch != nil { + if n := tswitch.Left; n != nil { + // type switch - declare variable + nn := newname(n.Sym) + declare(nn, dclcontext) + stmt.Rlist = list1(nn) + + // keep track of the instances for reporting unused + nn.Name.Defn = tswitch + } + } + + p.want(':') // consume ':' after declaring type switch var for correct lineno + return stmt + + default: + p.error(", expecting case or default or }") + return nil + } +} + +// go.y:compound_stmt +func (p *parser) compound_stmt(else_clause bool) *Node { + if trace && Debug['x'] != 0 { + defer p.trace("compound_stmt")() + } + + if p.tok == '{' { + markdcl() + p.next() // consume ';' after markdcl() for correct lineno + } else if else_clause { + syntax_error("else must be followed by if or statement block") + // skip through closing } + for p.tok != EOF && p.tok != '}' { + p.next() + } + p.next() + return nil + } else { + panic("unreachable") + } + + l := p.stmt_list() + + var stmt *Node + if l == nil { + stmt = Nod(OEMPTY, nil, nil) + } else { + stmt = liststmt(l) + } + popdcl() + + p.want('}') // TODO(gri) is this correct location w/ respect to popdcl()? + + return stmt +} + +// go.y:caseblock +func (p *parser) caseblock(tswitch *Node) *Node { + if trace && Debug['x'] != 0 { + defer p.trace("caseblock")() + } + + stmt := p.case_(tswitch) + + // If the last token read by the lexer was consumed + // as part of the case, clear it (parser has cleared yychar). + // If the last token read by the lexer was the lookahead + // leave it alone (parser has it cached in yychar). + // This is so that the stmt_list action doesn't look at + // the case tokens if the stmt_list is empty. + //yylast = yychar; + stmt.Xoffset = int64(block) + + stmt.Nbody = p.stmt_list() + + // TODO(gri) what do we need to do here? + // // This is the only place in the language where a statement + // // list is not allowed to drop the final semicolon, because + // // it's the only place where a statement list is not followed + // // by a closing brace. Handle the error for pedantry. + + // // Find the final token of the statement list. + // // yylast is lookahead; yyprev is last of stmt_list + // last := yyprev; + + // if last > 0 && last != ';' && yychar != '}' { + // Yyerror("missing statement after label"); + // } + + popdcl() + + return stmt +} + +// go.y:caseblock_list +func (p *parser) caseblock_list(tswitch *Node) (l *NodeList) { + if trace && Debug['x'] != 0 { + defer p.trace("caseblock_list")() + } + + if !p.got('{') { + syntax_error("missing { after switch clause") + // skip through closing } + for p.tok != EOF && p.tok != '}' { + p.next() + } + p.next() + return nil + } + + for p.tok != '}' { + l = list(l, p.caseblock(tswitch)) + } + p.want('}') + return +} + +// go.y:loop_body +func (p *parser) loop_body(context string) *NodeList { + if trace && Debug['x'] != 0 { + defer p.trace("loop_body")() + } + + if p.tok == '{' { + markdcl() + p.next() // consume ';' after markdcl() for correct lineno + } else { + syntax_error("missing { after " + context) + // skip through closing } + for p.tok != EOF && p.tok != '}' { + p.next() + } + p.next() + return nil + } + + body := p.stmt_list() + popdcl() + p.want('}') + + return body +} + +// go.y:for_header +func (p *parser) for_header() *Node { + if trace && Debug['x'] != 0 { + defer p.trace("for_header")() + } + + init, cond, post := p.header(true) + + if init != nil || post != nil { + // init ; test ; incr + if post != nil && post.Colas { + Yyerror("cannot declare in the for-increment") + } + h := Nod(OFOR, nil, nil) + if init != nil { + h.Ninit = list1(init) + } + h.Left = cond + h.Right = post + return h + } + + if cond != nil && cond.Op == ORANGE { + // range_stmt - handled by pexpr + return cond + } + + // normal test + h := Nod(OFOR, nil, nil) + h.Left = cond + return h +} + +// go.y:for_body +func (p *parser) for_body() *Node { + if trace && Debug['x'] != 0 { + defer p.trace("for_body")() + } + + stmt := p.for_header() + body := p.loop_body("for clause") + + stmt.Nbody = concat(stmt.Nbody, body) + return stmt +} + +// go.y:for_stmt +func (p *parser) for_stmt() *Node { + if trace && Debug['x'] != 0 { + defer p.trace("for_stmt")() + } + + p.want(LFOR) + markdcl() + body := p.for_body() + popdcl() + + return body +} + +func (p *parser) header(for_stmt bool) (init, cond, post *Node) { + if p.tok == '{' { + return + } + + nest := p.nest + p.nest = -1 + + if p.tok != ';' { + // accept potential vardcl but complain + // (for test/syntax/forvar.go) + if for_stmt && p.tok == LVAR { + Yyerror("var declaration not allowed in for initializer") + p.next() + } + init = p.simple_stmt(false, for_stmt) + // If we have a range clause, we are done. + if for_stmt && init.Op == ORANGE { + cond = init + init = nil + + p.nest = nest + return + } + } + if p.got(';') { + if for_stmt { + if p.tok != ';' { + cond = p.simple_stmt(false, false) + } + p.want(';') + if p.tok != '{' { + post = p.simple_stmt(false, false) + } + } else if p.tok != '{' { + cond = p.simple_stmt(false, false) + } + } else { + cond = init + init = nil + } + + p.nest = nest + + return +} + +// go.y:if_header +func (p *parser) if_header() *Node { + if trace && Debug['x'] != 0 { + defer p.trace("if_header")() + } + + init, cond, _ := p.header(false) + h := Nod(OIF, nil, nil) + h.Ninit = list1(init) + h.Left = cond + return h +} + +// go.y:if_stmt +func (p *parser) if_stmt() *Node { + if trace && Debug['x'] != 0 { + defer p.trace("if_stmt")() + } + + p.want(LIF) + + markdcl() + + stmt := p.if_header() + if stmt.Left == nil { + Yyerror("missing condition in if statement") + } + + stmt.Nbody = p.loop_body("if clause") + + l := p.elseif_list_else() + + n := stmt + popdcl() + for nn := l; nn != nil; nn = nn.Next { + if nn.N.Op == OIF { + popdcl() + } + n.Rlist = list1(nn.N) + n = nn.N + } + + return stmt +} + +// go.y:elsif +func (p *parser) elseif() *NodeList { + if trace && Debug['x'] != 0 { + defer p.trace("elseif")() + } + + // LELSE LIF already consumed + markdcl() + + stmt := p.if_header() + if stmt.Left == nil { + Yyerror("missing condition in if statement") + } + + stmt.Nbody = p.loop_body("if clause") + + return list1(stmt) +} + +// go.y:elsif_list +// go.y:else +func (p *parser) elseif_list_else() (l *NodeList) { + if trace && Debug['x'] != 0 { + defer p.trace("elseif_list_else")() + } + + for p.got(LELSE) { + if p.got(LIF) { + l = concat(l, p.elseif()) + } else { + l = concat(l, p.else_()) + break + } + } + + return l +} + +// go.y:else +func (p *parser) else_() *NodeList { + if trace && Debug['x'] != 0 { + defer p.trace("else")() + } + + l := &NodeList{N: p.compound_stmt(true)} + l.End = l + return l + +} + +// go.y:switch_stmt +func (p *parser) switch_stmt() *Node { + if trace && Debug['x'] != 0 { + defer p.trace("switch_stmt")() + } + + p.want(LSWITCH) + markdcl() + + hdr := p.if_header() + hdr.Op = OSWITCH + + tswitch := hdr.Left + if tswitch != nil && tswitch.Op != OTYPESW { + tswitch = nil + } + + hdr.List = p.caseblock_list(tswitch) + popdcl() + + return hdr +} + +// go.y:select_stmt +func (p *parser) select_stmt() *Node { + if trace && Debug['x'] != 0 { + defer p.trace("select_stmt")() + } + + p.want(LSELECT) + hdr := Nod(OSELECT, nil, nil) + hdr.List = p.caseblock_list(nil) + return hdr +} + +// TODO(gri) should have lexer return this info - no need for separate lookup +var prectab = map[int32]struct { + prec int // > 0 (0 indicates not found) + op Op +}{ + // not an expression anymore, but left in so we can give a good error + // message when used in expression context + LCOMM: {1, OSEND}, + + LOROR: {2, OOROR}, + + LANDAND: {3, OANDAND}, + + LEQ: {4, OEQ}, + LNE: {4, ONE}, + LLE: {4, OLE}, + LGE: {4, OGE}, + LLT: {4, OLT}, + LGT: {4, OGT}, + + '+': {5, OADD}, + '-': {5, OSUB}, + '|': {5, OOR}, + '^': {5, OXOR}, + + '*': {6, OMUL}, + '/': {6, ODIV}, + '%': {6, OMOD}, + '&': {6, OAND}, + LLSH: {6, OLSH}, + LRSH: {6, ORSH}, + LANDNOT: {6, OANDNOT}, +} + +func (p *parser) bexpr(prec int) *Node { + if trace && Debug['x'] != 0 { + defer p.trace("expr")() + } + + x := p.uexpr() + t := prectab[p.tok] + for tprec := t.prec; tprec >= prec; tprec-- { + for tprec == prec { + p.next() + y := p.bexpr(t.prec + 1) + x = Nod(t.op, x, y) + t = prectab[p.tok] + tprec = t.prec + } + } + return x +} + +// go.y:expr +func (p *parser) expr() *Node { + return p.bexpr(1) +} + +// go.y:uexpr +func (p *parser) uexpr() *Node { + if trace && Debug['x'] != 0 { + defer p.trace("uexpr")() + } + + var op Op + switch p.tok { + case '*': + op = OIND + + case '&': + p.next() + x := p.uexpr() + if x.Op == OCOMPLIT { + // Special case for &T{...}: turn into (*T){...}. + x.Right = Nod(OIND, x.Right, nil) + x.Right.Implicit = true + } else { + x = Nod(OADDR, x, nil) + } + return x + + case '+': + op = OPLUS + + case '-': + op = OMINUS + + case '!': + op = ONOT + + case '~': + // TODO(gri) do this in the lexer instead + p.next() + x := p.uexpr() + Yyerror("the bitwise complement operator is ^") + return Nod(OCOM, x, nil) + + case '^': + op = OCOM + + case LCOMM: + // receive operation (<-s2) or receive-only channel type (<-chan s3) + p.next() + if p.got(LCHAN) { + // <-chan T + t := Nod(OTCHAN, p.chan_elem(), nil) + t.Etype = Crecv + return t + } + return Nod(ORECV, p.uexpr(), nil) + + default: + return p.pexpr(false) + } + + // simple uexpr + p.next() + return Nod(op, p.uexpr(), nil) +} + +// call-like statements that can be preceded by 'defer' and 'go' +// +// go.y:pseudocall +func (p *parser) pseudocall() *Node { + if trace && Debug['x'] != 0 { + defer p.trace("pseudocall")() + } + + x := p.pexpr(true) + if x.Op != OCALL { + Yyerror("argument to go/defer must be function call") + } + return x +} + +// go.y:pexpr (partial) +func (p *parser) operand(keep_parens bool) *Node { + if trace && Debug['x'] != 0 { + defer p.trace("operand")() + } + + switch p.tok { + case LLITERAL: + x := nodlit(p.val) + p.next() + return x + + case LNAME, '@', '?': + return p.name() + + case '(': + p.next() + p.nest++ + x := p.expr() // expr_or_type + p.nest-- + p.want(')') + + // Need to know on lhs of := whether there are ( ). + // Don't bother with the OPAREN in other cases: + // it's just a waste of memory and time. + // + // But if the next token is a { , introduce OPAREN since + // we may have a composite literal and we need to know + // if there were ()'s'. + // + // TODO(gri) could simplify this if we parse complits + // in operand (see respective comment in pexpr). + if keep_parens || p.tok == '{' { + return Nod(OPAREN, x, nil) + } + switch x.Op { + case ONAME, ONONAME, OPACK, OTYPE, OLITERAL, OTYPESW: + return Nod(OPAREN, x, nil) + } + return x + + case LFUNC: + t := p.fntype() + if p.tok == '{' { + // fnlitdcl + closurehdr(t) + // fnliteral + p.next() // consume '{' + p.nest++ + body := p.stmt_list() + p.nest-- + p.want('}') + return closurebody(body) + } + return t + + case '[', LCHAN, LMAP, LSTRUCT, LINTERFACE: + return p.othertype() + + case '{': + // common case: p.header is missing simple_stmt before { in if, for, switch + syntax_error("missing operand") + // '{' will be consumed in pexpr - no need to consume it here + return nil + + default: + p.error(" in operand") + return nil + } +} + +// go.y:pexpr, pexpr_no_paren +func (p *parser) pexpr(keep_parens bool) *Node { + if trace && Debug['x'] != 0 { + defer p.trace("pexpr")() + } + + x := p.operand(keep_parens) + +loop: + for { + switch p.tok { + case '.': + p.next() + switch p.tok { + case LNAME, '@', '?': + // pexpr '.' sym + sel := p.sym() + + if x.Op == OPACK { + s := restrictlookup(sel.Name, x.Name.Pkg) + x.Used = true + x = oldname(s) + break + } + x = Nod(OXDOT, x, newname(sel)) + + case '(': + p.next() + switch p.tok { + default: + // pexpr '.' '(' expr_or_type ')' + t := p.expr() // expr_or_type + p.want(')') + x = Nod(ODOTTYPE, x, t) + + case LTYPE: + // pexpr '.' '(' LTYPE ')' + p.next() + p.want(')') + x = Nod(OTYPESW, nil, x) + } + + default: + p.error(", expecting name or (") + } + + case '[': + p.next() + p.nest++ + var index [3]*Node + if p.tok != ':' { + index[0] = p.expr() + } + ncol := 0 + for ncol < len(index)-1 && p.got(':') { + ncol++ + if p.tok != EOF && p.tok != ':' && p.tok != ']' { + index[ncol] = p.expr() + } + } + p.nest-- + p.want(']') + + switch ncol { + case 0: + i := index[0] + if i == nil { + Yyerror("missing index in index expression") + } + x = Nod(OINDEX, x, i) + case 1: + i := index[0] + j := index[1] + x = Nod(OSLICE, x, Nod(OKEY, i, j)) + case 2: + i := index[0] + j := index[1] + k := index[2] + if j == nil { + Yyerror("middle index required in 3-index slice") + } + if k == nil { + Yyerror("final index required in 3-index slice") + } + x = Nod(OSLICE3, x, Nod(OKEY, i, Nod(OKEY, j, k))) + + default: + panic("unreachable") + } + + case '(': + // convtype '(' expr ocomma ')' + p.next() + p.nest++ + args, ddd := p.arg_list() + p.nest-- + p.want(')') + + // call or conversion + x = Nod(OCALL, x, nil) + x.List = args + x.Isddd = ddd + + case '{': + // TODO(gri) should this (complit acceptance) be in operand? + // accept ()'s around the complit type but complain if we have a complit + t := x + for t.Op == OPAREN { + t = t.Left + } + // determine if '{' belongs to a complit or a compound_stmt + complit_ok := false + switch t.Op { + case ONAME, ONONAME, OTYPE, OPACK, OXDOT, ODOT: + if p.nest >= 0 { + // x is considered a comptype + complit_ok = true + } + case OTARRAY, OTSTRUCT, OTMAP: + // x is a comptype + complit_ok = true + } + if !complit_ok { + break loop + } + if t != x { + syntax_error("cannot parenthesize type in composite literal") + } + n := p.complitexpr() + n.Right = x + x = n + + default: + break loop + } + } + + return x +} + +// go.y:keyval +func (p *parser) keyval() *Node { + if trace && Debug['x'] != 0 { + defer p.trace("keyval")() + } + + x := p.bare_complitexpr() + if p.got(':') { + x = Nod(OKEY, x, p.bare_complitexpr()) + } + return x +} + +// go.y:bare_complitexpr +func (p *parser) bare_complitexpr() *Node { + if trace && Debug['x'] != 0 { + defer p.trace("bare_complitexpr")() + } + + if p.tok == '{' { + // '{' start_complit braced_keyval_list '}' + return p.complitexpr() + } + + x := p.expr() + + // These nodes do not carry line numbers. + // Since a composite literal commonly spans several lines, + // the line number on errors may be misleading. + // Introduce a wrapper node to give the correct line. + + // TODO(gri) This is causing trouble when used for keys. Need to fix complit parsing. + // switch x.Op { + // case ONAME, ONONAME, OTYPE, OPACK, OLITERAL: + // x = Nod(OPAREN, x, nil) + // x.Implicit = true + // } + return x +} + +// go.y:complitexpr +func (p *parser) complitexpr() *Node { + if trace && Debug['x'] != 0 { + defer p.trace("complitexpr")() + } + + // make node early so we get the right line number + n := Nod(OCOMPLIT, nil, nil) + + p.want('{') + p.nest++ + + var l *NodeList + for p.tok != EOF && p.tok != '}' { + l = list(l, p.keyval()) + p.ocomma("composite literal") + } + + p.nest-- + p.want('}') + + n.List = l + return n +} + +// names and types +// newname is used before declared +// oldname is used after declared +// +// go.y:new_name: +func (p *parser) new_name(sym *Sym) *Node { + if trace && Debug['x'] != 0 { + defer p.trace("new_name")() + } + + if sym != nil { + return newname(sym) + } + return nil +} + +// go.y:onew_name: +func (p *parser) onew_name() *Node { + if trace && Debug['x'] != 0 { + defer p.trace("onew_name")() + } + + switch p.tok { + case LNAME, '@', '?': + return p.new_name(p.sym()) + } + return nil +} + +// go.y:sym +func (p *parser) sym() *Sym { + switch p.tok { + case LNAME: + s := p.sym_ + p.next() + // during imports, unqualified non-exported identifiers are from builtinpkg + if importpkg != nil && !exportname(s.Name) { + s = Pkglookup(s.Name, builtinpkg) + } + return s + + case '@': + return p.hidden_importsym() + + case '?': + p.next() + return nil + + default: + p.error("") + return new(Sym) + } +} + +func mkname(sym *Sym) *Node { + n := oldname(sym) + if n.Name != nil && n.Name.Pack != nil { + n.Name.Pack.Used = true + } + return n +} + +// go.y:name +func (p *parser) name() *Node { + if trace && Debug['x'] != 0 { + defer p.trace("name")() + } + + return mkname(p.sym()) +} + +// go.y:dotdotdot +func (p *parser) dotdotdot() *Node { + if trace && Debug['x'] != 0 { + defer p.trace("dotdotdot")() + } + + p.want(LDDD) + switch p.tok { + case LCOMM, LFUNC, '[', LCHAN, LMAP, LSTRUCT, LINTERFACE, '*', LNAME, '@', '?', '(': + return Nod(ODDD, p.ntype(), nil) + } + + Yyerror("final argument in variadic function missing type") + return Nod(ODDD, typenod(typ(TINTER)), nil) +} + +// go.y:ntype +func (p *parser) ntype() *Node { + if trace && Debug['x'] != 0 { + defer p.trace("ntype")() + } + + switch p.tok { + case LCOMM: + return p.recvchantype() + + case LFUNC: + return p.fntype() + + case '[', LCHAN, LMAP, LSTRUCT, LINTERFACE: + return p.othertype() + + case '*': + return p.ptrtype() + + case LNAME, '@', '?': + return p.dotname() + + case '(': + p.next() + t := p.ntype() + p.want(')') + return t + + case LDDD: + // permit ...T but complain + // TODO(gri) introduced for test/fixedbugs/bug228.go - maybe adjust bug or find better solution + p.error(" in type") + return p.ntype() + + default: + p.error(" in type") + return nil + } +} + +func (p *parser) chan_elem() *Node { + if trace && Debug['x'] != 0 { + defer p.trace("chan_elem")() + } + + switch p.tok { + case LCOMM, LFUNC, + '[', LCHAN, LMAP, LSTRUCT, LINTERFACE, + '*', + LNAME, '@', '?', + '(', + LDDD: + return p.ntype() + default: + syntax_error("missing channel element type") + return nil + } +} + +// go.y:fnret_type +func (p *parser) fnret_type() *Node { + if trace && Debug['x'] != 0 { + defer p.trace("fnret_type")() + } + + switch p.tok { + case LCOMM: + return p.recvchantype() + + case LFUNC: + return p.fntype() + + case '[', LCHAN, LMAP, LSTRUCT, LINTERFACE: + return p.othertype() + + case '*': + return p.ptrtype() + + default: + return p.dotname() + } +} + +// go.y:dotname +func (p *parser) dotname() *Node { + if trace && Debug['x'] != 0 { + defer p.trace("dotname")() + } + + s1 := p.name() + + switch p.tok { + default: + return s1 + + case '.': + p.next() + s3 := p.sym() + + if s1.Op == OPACK { + var s *Sym + s = restrictlookup(s3.Name, s1.Name.Pkg) + s1.Used = true + return oldname(s) + } + return Nod(OXDOT, s1, newname(s3)) + } +} + +// go.y:othertype +func (p *parser) othertype() *Node { + if trace && Debug['x'] != 0 { + defer p.trace("othertype")() + } + + switch p.tok { + case '[': + // '[' oexpr ']' ntype + // '[' LDDD ']' ntype + p.next() + p.nest++ + var len *Node + if p.tok != ']' { + if p.got(LDDD) { + len = Nod(ODDD, nil, nil) + } else { + len = p.expr() + } + } + p.nest-- + p.want(']') + return Nod(OTARRAY, len, p.ntype()) + + case LCHAN: + // LCHAN non_recvchantype + // LCHAN LCOMM ntype + p.next() + var dir EType = Cboth + if p.got(LCOMM) { + dir = Csend + } + t := Nod(OTCHAN, p.chan_elem(), nil) + t.Etype = dir + return t + + case LMAP: + // LMAP '[' ntype ']' ntype + p.next() + p.want('[') + key := p.ntype() + p.want(']') + val := p.ntype() + return Nod(OTMAP, key, val) + + case LSTRUCT: + // structtype + return p.structtype() + + case LINTERFACE: + // interfacetype + return p.interfacetype() + + default: + panic("unreachable") + } +} + +// go.y:ptrtype +func (p *parser) ptrtype() *Node { + if trace && Debug['x'] != 0 { + defer p.trace("ptrtype")() + } + + p.want('*') + return Nod(OIND, p.ntype(), nil) +} + +// go.y:recvchantype +func (p *parser) recvchantype() *Node { + if trace && Debug['x'] != 0 { + defer p.trace("recvchantype")() + } + + p.want(LCOMM) + p.want(LCHAN) + t := Nod(OTCHAN, p.chan_elem(), nil) + t.Etype = Crecv + return t +} + +// go.y:structtype +func (p *parser) structtype() *Node { + if trace && Debug['x'] != 0 { + defer p.trace("structtype")() + } + + p.want(LSTRUCT) + p.want('{') + var l *NodeList + for p.tok != EOF && p.tok != '}' { + l = concat(l, p.structdcl()) + p.osemi() + } + p.want('}') + + t := Nod(OTSTRUCT, nil, nil) + t.List = l + return t +} + +// go.y:interfacetype +func (p *parser) interfacetype() *Node { + if trace && Debug['x'] != 0 { + defer p.trace("interfacetype")() + } + + p.want(LINTERFACE) + p.want('{') + var l *NodeList + for p.tok != EOF && p.tok != '}' { + l = list(l, p.interfacedcl()) + p.osemi() + } + p.want('}') + + t := Nod(OTINTER, nil, nil) + t.List = l + return t +} + +// Function stuff. +// All in one place to show how crappy it all is. +// +// go.y:xfndcl +func (p *parser) xfndcl() *Node { + if trace && Debug['x'] != 0 { + defer p.trace("xfndcl")() + } + + p.want(LFUNC) + f := p.fndcl() + body := p.fnbody() + + if f == nil { + return nil + } + if noescape && body != nil { + Yyerror("can only use //go:noescape with external func implementations") + } + + f.Nbody = body + f.Func.Endlineno = lineno + f.Noescape = noescape + f.Func.Norace = norace + f.Func.Nosplit = nosplit + f.Func.Noinline = noinline + f.Func.Nowritebarrier = nowritebarrier + f.Func.Nowritebarrierrec = nowritebarrierrec + f.Func.Systemstack = systemstack + funcbody(f) + + return f +} + +// go.y:fndcl +func (p *parser) fndcl() *Node { + if trace && Debug['x'] != 0 { + defer p.trace("fndcl")() + } + + switch p.tok { + case LNAME, '@', '?': + // sym '(' oarg_type_list_ocomma ')' fnres + name := p.sym() + params := p.param_list() + result := p.fnres() + + params = checkarglist(params, 1) + + if name.Name == "init" { + name = renameinit() + if params != nil || result != nil { + Yyerror("func init must have no arguments and no return values") + } + } + + if localpkg.Name == "main" && name.Name == "main" { + if params != nil || result != nil { + Yyerror("func main must have no arguments and no return values") + } + } + + t := Nod(OTFUNC, nil, nil) + t.List = params + t.Rlist = result + + f := Nod(ODCLFUNC, nil, nil) + f.Func.Nname = newfuncname(name) + f.Func.Nname.Name.Defn = f + f.Func.Nname.Name.Param.Ntype = t // TODO: check if nname already has an ntype + declare(f.Func.Nname, PFUNC) + + funchdr(f) + return f + + case '(': + // '(' oarg_type_list_ocomma ')' sym '(' oarg_type_list_ocomma ')' fnres + rparam := p.param_list() + name := p.sym() + params := p.param_list() + result := p.fnres() + + rparam = checkarglist(rparam, 0) + params = checkarglist(params, 1) + + if rparam == nil { + Yyerror("method has no receiver") + return nil + } + + if rparam.Next != nil { + Yyerror("method has multiple receivers") + return nil + } + + rcvr := rparam.N + if rcvr.Op != ODCLFIELD { + Yyerror("bad receiver in method") + return nil + } + + t := Nod(OTFUNC, rcvr, nil) + t.List = params + t.Rlist = result + + f := Nod(ODCLFUNC, nil, nil) + f.Func.Shortname = newfuncname(name) + f.Func.Nname = methodname1(f.Func.Shortname, rcvr.Right) + f.Func.Nname.Name.Defn = f + f.Func.Nname.Name.Param.Ntype = t + f.Func.Nname.Nointerface = nointerface + declare(f.Func.Nname, PFUNC) + + funchdr(f) + return f + + default: + p.error(", expecting name or (") + return nil + } +} + +// go.y:hidden_fndcl +func (p *parser) hidden_fndcl() *Node { + if trace && Debug['x'] != 0 { + defer p.trace("hidden_fndcl")() + } + + switch p.tok { + default: + // hidden_pkg_importsym '(' ohidden_funarg_list ')' ohidden_funres + s1 := p.hidden_pkg_importsym() + p.want('(') + s3 := p.ohidden_funarg_list() + p.want(')') + s5 := p.ohidden_funres() + + s := s1 + t := functype(nil, s3, s5) + + importsym(s, ONAME) + if s.Def != nil && s.Def.Op == ONAME { + if Eqtype(t, s.Def.Type) { + dclcontext = PDISCARD // since we skip funchdr below + return nil + } + Yyerror("inconsistent definition for func %v during import\n\t%v\n\t%v", s, s.Def.Type, t) + } + + ss := newfuncname(s) + ss.Type = t + declare(ss, PFUNC) + + funchdr(ss) + return ss + + case '(': + // '(' hidden_funarg_list ')' sym '(' ohidden_funarg_list ')' ohidden_funres + p.next() + s2 := p.hidden_funarg_list() + p.want(')') + s4 := p.sym() + p.want('(') + s6 := p.ohidden_funarg_list() + p.want(')') + s8 := p.ohidden_funres() + + ss := methodname1(newname(s4), s2.N.Right) + ss.Type = functype(s2.N, s6, s8) + + checkwidth(ss.Type) + addmethod(s4, ss.Type, false, nointerface) + nointerface = false + funchdr(ss) + + // inl.C's inlnode in on a dotmeth node expects to find the inlineable body as + // (dotmeth's type).Nname.Inl, and dotmeth's type has been pulled + // out by typecheck's lookdot as this $$.ttype. So by providing + // this back link here we avoid special casing there. + ss.Type.Nname = ss + return ss + } +} + +// go.y:fntype +func (p *parser) fntype() *Node { + if trace && Debug['x'] != 0 { + defer p.trace("fntype")() + } + + p.want(LFUNC) + params := p.param_list() + result := p.fnres() + + params = checkarglist(params, 1) + t := Nod(OTFUNC, nil, nil) + t.List = params + t.Rlist = result + + return t +} + +// go.y:fnbody +func (p *parser) fnbody() *NodeList { + if trace && Debug['x'] != 0 { + defer p.trace("fnbody")() + } + + if p.got('{') { + body := p.stmt_list() + p.want('}') + if body == nil { + body = list1(Nod(OEMPTY, nil, nil)) + } + return body + } + + return nil +} + +// go.y:fnres +func (p *parser) fnres() *NodeList { + if trace && Debug['x'] != 0 { + defer p.trace("fnres")() + } + + switch p.tok { + default: + return nil + + case LCOMM, LFUNC, '[', LCHAN, LMAP, LSTRUCT, LINTERFACE, '*', LNAME, '@', '?': + result := p.fnret_type() + return list1(Nod(ODCLFIELD, nil, result)) + + case '(': + result := p.param_list() + return checkarglist(result, 0) + } +} + +// go.y:xdcl_list +func (p *parser) xdcl_list() (l *NodeList) { + if trace && Debug['x'] != 0 { + defer p.trace("xdcl_list")() + } + +loop: + for p.tok != EOF { + switch p.tok { + case LVAR, LCONST, LTYPE: + l = concat(l, p.common_dcl()) + + case LFUNC: + l = list(l, p.xfndcl()) + + default: + if p.tok == '{' && l != nil && l.End.N.Op == ODCLFUNC && l.End.N.Nbody == nil { + // opening { of function declaration on next line + syntax_error("unexpected semicolon or newline before {") + } else { + syntax_error("non-declaration statement outside function body") + } + // skip over tokens until we find a new top-level declaration + // TODO(gri) keep track of {} nesting as well? + for { + p.next() + switch p.tok { + case LVAR, LCONST, LTYPE, LFUNC, EOF: + continue loop + } + } + + } + + if nsyntaxerrors == 0 { + testdclstack() + } + + noescape = false + noinline = false + nointerface = false + norace = false + nosplit = false + nowritebarrier = false + nowritebarrierrec = false + systemstack = false + + // Consume ';' AFTER resetting the above flags since + // it may read the subsequent comment line which may + // set the flags for the next function declaration. + if p.tok != EOF && !p.got(';') { + p.error(" after top level declaration") + // TODO(gri) same code above - factor! + for { + p.next() + switch p.tok { + case LVAR, LCONST, LTYPE, LFUNC, EOF: + continue loop + } + } + } + } + return +} + +// go.y:structdcl +func (p *parser) structdcl() *NodeList { + if trace && Debug['x'] != 0 { + defer p.trace("structdcl")() + } + + var sym *Sym + switch p.tok { + case LNAME: + sym = p.sym_ + p.next() + if sym == nil { + panic("unreachable") // we must have a sym for LNAME + } + if p.tok == '.' || p.tok == LLITERAL || p.tok == ';' || p.tok == '}' { + // embed oliteral + field := p.embed(sym) + tag := p.oliteral() + + field.SetVal(tag) + return list1(field) + } + + // LNAME belongs to first *Sym of new_name_list + // + // during imports, unqualified non-exported identifiers are from builtinpkg + if importpkg != nil && !exportname(sym.Name) { + sym = Pkglookup(sym.Name, builtinpkg) + if sym == nil { + p.import_error() + } + } + fallthrough + + case '@', '?': + // new_name_list ntype oliteral + fields := p.new_name_list(sym) + typ := p.ntype() + tag := p.oliteral() + + if l := fields; l == nil || l.N.Sym.Name == "?" { + // ? symbol, during import (list1(nil) == nil) + n := typ + if n.Op == OIND { + n = n.Left + } + n = embedded(n.Sym, importpkg) + n.Right = typ + n.SetVal(tag) + return list1(n) + } + + for l := fields; l != nil; l = l.Next { + l.N = Nod(ODCLFIELD, l.N, typ) + l.N.SetVal(tag) + } + return fields + + case '(': + p.next() + if p.got('*') { + // '(' '*' embed ')' oliteral + field := p.embed(nil) + p.want(')') + tag := p.oliteral() + + field.Right = Nod(OIND, field.Right, nil) + field.SetVal(tag) + Yyerror("cannot parenthesize embedded type") + return list1(field) + + } else { + // '(' embed ')' oliteral + field := p.embed(nil) + p.want(')') + tag := p.oliteral() + + field.SetVal(tag) + Yyerror("cannot parenthesize embedded type") + return list1(field) + } + + case '*': + p.next() + if p.got('(') { + // '*' '(' embed ')' oliteral + field := p.embed(nil) + p.want(')') + tag := p.oliteral() + + field.Right = Nod(OIND, field.Right, nil) + field.SetVal(tag) + Yyerror("cannot parenthesize embedded type") + return list1(field) + + } else { + // '*' embed oliteral + field := p.embed(nil) + tag := p.oliteral() + + field.Right = Nod(OIND, field.Right, nil) + field.SetVal(tag) + return list1(field) + } + + default: + p.error(", expecting field name or embedded type") + return nil + } +} + +// go.y:oliteral +func (p *parser) oliteral() (v Val) { + if p.tok == LLITERAL { + v = p.val + p.next() + } + return +} + +// go.y:packname +func (p *parser) packname(name *Sym) *Sym { + if trace && Debug['x'] != 0 { + defer p.trace("embed")() + } + + if name != nil { + // LNAME was already consumed and is coming in as name + } else if p.tok == LNAME { + name = p.sym_ + p.next() + } else { + p.error(", expecting name") + name = new(Sym) + } + + if p.got('.') { + // LNAME '.' sym + s := p.sym() + + var pkg *Pkg + if name.Def == nil || name.Def.Op != OPACK { + Yyerror("%v is not a package", name) + pkg = localpkg + } else { + name.Def.Used = true + pkg = name.Def.Name.Pkg + } + return restrictlookup(s.Name, pkg) + } + + // LNAME + if n := oldname(name); n.Name != nil && n.Name.Pack != nil { + n.Name.Pack.Used = true + } + return name +} + +// go.y:embed +func (p *parser) embed(sym *Sym) *Node { + if trace && Debug['x'] != 0 { + defer p.trace("embed")() + } + + pkgname := p.packname(sym) + return embedded(pkgname, localpkg) +} + +// go.y: interfacedcl +func (p *parser) interfacedcl() *Node { + if trace && Debug['x'] != 0 { + defer p.trace("interfacedcl")() + } + + switch p.tok { + case LNAME: + sym := p.sym_ + p.next() + + // accept potential name list but complain + hasNameList := false + for p.got(',') { + p.sym() + hasNameList = true + } + if hasNameList { + syntax_error("name list not allowed in interface type") + } + + if p.tok != '(' { + // packname + pname := p.packname(sym) + return Nod(ODCLFIELD, nil, oldname(pname)) + } + + // newname indcl + mname := newname(sym) + sig := p.indcl() + + meth := Nod(ODCLFIELD, mname, sig) + ifacedcl(meth) + return meth + + case '(': + p.next() + pname := p.packname(nil) + p.want(')') + n := Nod(ODCLFIELD, nil, oldname(pname)) + Yyerror("cannot parenthesize embedded type") + return n + + default: + p.error("") + return nil + } +} + +// go.y:indcl +func (p *parser) indcl() *Node { + if trace && Debug['x'] != 0 { + defer p.trace("indcl")() + } + + params := p.param_list() + result := p.fnres() + + // without func keyword + params = checkarglist(params, 1) + t := Nod(OTFUNC, fakethis(), nil) + t.List = params + t.Rlist = result + + return t +} + +// go.y:arg_type +func (p *parser) arg_type() *Node { + if trace && Debug['x'] != 0 { + defer p.trace("arg_type")() + } + + switch p.tok { + case LNAME, '@', '?': + s1 := p.sym() + switch p.tok { + case LCOMM, LFUNC, '[', LCHAN, LMAP, LSTRUCT, LINTERFACE, '*', LNAME, '@', '?', '(': + // sym name_or_type + s2 := p.ntype() + ss := Nod(ONONAME, nil, nil) + ss.Sym = s1 + return Nod(OKEY, ss, s2) + + case LDDD: + // sym dotdotdot + s2 := p.dotdotdot() + ss := Nod(ONONAME, nil, nil) + ss.Sym = s1 + return Nod(OKEY, ss, s2) + + default: + // name_or_type + s1 := mkname(s1) + // from dotname + if p.got('.') { + s3 := p.sym() + + if s1.Op == OPACK { + var s *Sym + s = restrictlookup(s3.Name, s1.Name.Pkg) + s1.Used = true + return oldname(s) + } + return Nod(OXDOT, s1, newname(s3)) + } + return s1 + } + + case LDDD: + // dotdotdot + return p.dotdotdot() + + case LCOMM, LFUNC, '[', LCHAN, LMAP, LSTRUCT, LINTERFACE, '*', '(': + // name_or_type + return p.ntype() + + default: + p.error(", expecting )") + return nil + } +} + +// go.y:oarg_type_list_ocomma + surrounding ()'s +func (p *parser) param_list() (l *NodeList) { + if trace && Debug['x'] != 0 { + defer p.trace("param_list")() + } + + p.want('(') + for p.tok != EOF && p.tok != ')' { + l = list(l, p.arg_type()) + p.ocomma("parameter list") + } + p.want(')') + return +} + +var missing_stmt = Nod(OXXX, nil, nil) + +// go.y:stmt +// maty return missing_stmt +func (p *parser) stmt() *Node { + if trace && Debug['x'] != 0 { + defer p.trace("stmt")() + } + + switch p.tok { + case '{': + return p.compound_stmt(false) + + case LVAR, LCONST, LTYPE: + return liststmt(p.common_dcl()) + + case LNAME, '@', '?', LLITERAL, LFUNC, '(', // operands + '[', LSTRUCT, LMAP, LCHAN, LINTERFACE, // composite types + '+', '-', '*', '&', '^', '~', LCOMM, '!': // unary operators + // simple_stmt + fallthrough + + case LFOR, LSWITCH, LSELECT, LIF, LFALL, LBREAK, LCONTINUE, LGO, LDEFER, LGOTO, LRETURN: + return p.non_dcl_stmt() + + case ';': + return nil + + default: + return missing_stmt + } +} + +// TODO(gri) inline non_dcl_stmt into stmt +// go.y:non_dcl_stmt +func (p *parser) non_dcl_stmt() *Node { + if trace && Debug['x'] != 0 { + defer p.trace("non_dcl_stmt")() + } + + switch p.tok { + case LNAME, '@', '?', LLITERAL, LFUNC, '(', // operands + '[', LSTRUCT, LMAP, LCHAN, LINTERFACE, // composite types + '+', '-', '*', '&', '^', '~', LCOMM, '!': // unary operators + return p.simple_stmt(true, false) + + case LFOR: + return p.for_stmt() + + case LSWITCH: + return p.switch_stmt() + + case LSELECT: + return p.select_stmt() + + case LIF: + return p.if_stmt() + + case LFALL: + p.next() + + // will be converted to OFALL + ss := Nod(OXFALL, nil, nil) + ss.Xoffset = int64(block) + return ss + + case LBREAK: + p.next() + s2 := p.onew_name() + + return Nod(OBREAK, s2, nil) + + case LCONTINUE: + p.next() + s2 := p.onew_name() + + return Nod(OCONTINUE, s2, nil) + + case LGO: + p.next() + s2 := p.pseudocall() + + return Nod(OPROC, s2, nil) + + case LDEFER: + p.next() + s2 := p.pseudocall() + + return Nod(ODEFER, s2, nil) + + case LGOTO: + p.next() + s2 := p.new_name(p.sym()) + + ss := Nod(OGOTO, s2, nil) + ss.Sym = dclstack // context, for goto restrictions + return ss + + case LRETURN: + p.next() + var s2 *NodeList + if p.tok != ';' && p.tok != '}' { + s2 = p.expr_list() + } + + ss := Nod(ORETURN, nil, nil) + ss.List = s2 + if ss.List == nil && Curfn != nil { + var l *NodeList + + for l = Curfn.Func.Dcl; l != nil; l = l.Next { + if l.N.Class == PPARAM { + continue + } + if l.N.Class != PPARAMOUT { + break + } + if l.N.Sym.Def != l.N { + Yyerror("%s is shadowed during return", l.N.Sym.Name) + } + } + } + + return ss + + default: + panic("unreachable") + } +} + +// go.y:stmt_list +func (p *parser) stmt_list() (l *NodeList) { + if trace && Debug['x'] != 0 { + defer p.trace("stmt_list")() + } + + for p.tok != EOF && p.tok != '}' && p.tok != LCASE && p.tok != LDEFAULT { + s := p.stmt() + if s == missing_stmt { + break + } + l = list(l, s) + // customized version of osemi: + // ';' is optional before a closing ')' or '}' + if p.tok == ')' || p.tok == '}' { + continue + } + if !p.got(';') { + p.error(" at end of statement") + } + } + return +} + +// go.y:new_name_list +// if first != nil we have the first symbol already +func (p *parser) new_name_list(first *Sym) *NodeList { + if trace && Debug['x'] != 0 { + defer p.trace("new_name_list")() + } + + if first == nil { + first = p.sym() // may still be nil + } + l := list1(p.new_name(first)) + for p.got(',') { + l = list(l, p.new_name(p.sym())) + } + return l +} + +// go.y:dcl_name_list +func (p *parser) dcl_name_list() *NodeList { + if trace && Debug['x'] != 0 { + defer p.trace("dcl_name_list")() + } + + l := list1(dclname(p.sym())) + for p.got(',') { + l = list(l, dclname(p.sym())) + } + return l +} + +// go.y:expr_list +func (p *parser) expr_list() *NodeList { + if trace && Debug['x'] != 0 { + defer p.trace("expr_list")() + } + + l := list1(p.expr()) + for p.got(',') { + l = list(l, p.expr()) + } + return l +} + +// go.y:expr_or_type_list +func (p *parser) arg_list() (l *NodeList, ddd bool) { + if trace && Debug['x'] != 0 { + defer p.trace("arg_list")() + } + + // TODO(gri) make this more tolerant in the presence of LDDD + // that is not at the end. + + for p.tok != EOF && p.tok != ')' && !ddd { + l = list(l, p.expr()) // expr_or_type + ddd = p.got(LDDD) + p.ocomma("argument list") + } + + return +} + +// go.y:osemi +func (p *parser) osemi() { + // ';' is optional before a closing ')' or '}' + if p.tok == ')' || p.tok == '}' { + return + } + p.want(';') +} + +// go.y:ocomma +func (p *parser) ocomma(context string) { + switch p.tok { + case ')', '}': + // ',' is optional before a closing ')' or '}' + return + case ';': + syntax_error("need trailing comma before newline in " + context) + p.next() + return + } + p.want(',') +} + +// ---------------------------------------------------------------------------- +// Importing packages + +func (p *parser) import_error() { + p.error(" in export data of imported package") +} + +// The methods below reflect a 1:1 translation of the corresponding go.y yacc +// productions They could be simplified significantly and also use better +// variable names. However, we will be able to delete them once we enable the +// new export format by default, so it's not worth the effort. + +// go.y:hidden_importsym: +func (p *parser) hidden_importsym() *Sym { + if trace && Debug['x'] != 0 { + defer p.trace("hidden_importsym")() + } + + p.want('@') + var s2 Val + if p.tok == LLITERAL { + s2 = p.val + p.next() + } else { + p.import_error() + } + p.want('.') + + switch p.tok { + case LNAME: + s4 := p.sym_ + p.next() + + var p *Pkg + + if s2.U.(string) == "" { + p = importpkg + } else { + if isbadimport(s2.U.(string)) { + errorexit() + } + p = mkpkg(s2.U.(string)) + } + return Pkglookup(s4.Name, p) + + case '?': + p.next() + + var p *Pkg + + if s2.U.(string) == "" { + p = importpkg + } else { + if isbadimport(s2.U.(string)) { + errorexit() + } + p = mkpkg(s2.U.(string)) + } + return Pkglookup("?", p) + + default: + p.import_error() + return nil + } +} + +// go.y:ohidden_funarg_list +func (p *parser) ohidden_funarg_list() *NodeList { + if trace && Debug['x'] != 0 { + defer p.trace("ohidden_funarg_list")() + } + + var ss *NodeList + if p.tok != ')' { + ss = p.hidden_funarg_list() + } + return ss +} + +// go.y:ohidden_structdcl_list +func (p *parser) ohidden_structdcl_list() *NodeList { + if trace && Debug['x'] != 0 { + defer p.trace("ohidden_structdcl_list")() + } + + var ss *NodeList + if p.tok != '}' { + ss = p.hidden_structdcl_list() + } + return ss +} + +// go.y:ohidden_interfacedcl_list +func (p *parser) ohidden_interfacedcl_list() *NodeList { + if trace && Debug['x'] != 0 { + defer p.trace("ohidden_interfacedcl_list")() + } + + var ss *NodeList + if p.tok != '}' { + ss = p.hidden_interfacedcl_list() + } + return ss +} + +// import syntax from package header +// +// go.y:hidden_import +func (p *parser) hidden_import() { + if trace && Debug['x'] != 0 { + defer p.trace("hidden_import")() + } + + switch p.tok { + case LIMPORT: + // LIMPORT LNAME LLITERAL ';' + p.next() + var s2 *Sym + if p.tok == LNAME { + s2 = p.sym_ + p.next() + } else { + p.import_error() + } + var s3 Val + if p.tok == LLITERAL { + s3 = p.val + p.next() + } else { + p.import_error() + } + p.want(';') + + importimport(s2, s3.U.(string)) + + case LVAR: + // LVAR hidden_pkg_importsym hidden_type ';' + p.next() + s2 := p.hidden_pkg_importsym() + s3 := p.hidden_type() + p.want(';') + + importvar(s2, s3) + + case LCONST: + // LCONST hidden_pkg_importsym '=' hidden_constant ';' + // LCONST hidden_pkg_importsym hidden_type '=' hidden_constant ';' + p.next() + s2 := p.hidden_pkg_importsym() + var s3 *Type = Types[TIDEAL] + if p.tok != '=' { + s3 = p.hidden_type() + } + p.want('=') + s4 := p.hidden_constant() + p.want(';') + + importconst(s2, s3, s4) + + case LTYPE: + // LTYPE hidden_pkgtype hidden_type ';' + p.next() + s2 := p.hidden_pkgtype() + s3 := p.hidden_type() + p.want(';') + + importtype(s2, s3) + + case LFUNC: + // LFUNC hidden_fndcl fnbody ';' + p.next() + s2 := p.hidden_fndcl() + s3 := p.fnbody() + p.want(';') + + if s2 == nil { + dclcontext = PEXTERN // since we skip the funcbody below + return + } + + s2.Func.Inl = s3 + + funcbody(s2) + importlist = append(importlist, s2) + + if Debug['E'] > 0 { + fmt.Printf("import [%q] func %v \n", importpkg.Path, s2) + if Debug['m'] > 2 && s2.Func.Inl != nil { + fmt.Printf("inl body:%v\n", s2.Func.Inl) + } + } + + default: + p.import_error() + } +} + +// go.y:hidden_pkg_importsym +func (p *parser) hidden_pkg_importsym() *Sym { + if trace && Debug['x'] != 0 { + defer p.trace("hidden_pkg_importsym")() + } + + s1 := p.hidden_importsym() + + ss := s1 + structpkg = ss.Pkg + + return ss +} + +// go.y:hidden_pkgtype +func (p *parser) hidden_pkgtype() *Type { + if trace && Debug['x'] != 0 { + defer p.trace("hidden_pkgtype")() + } + + s1 := p.hidden_pkg_importsym() + + ss := pkgtype(s1) + importsym(s1, OTYPE) + + return ss +} + +// ---------------------------------------------------------------------------- +// Importing types + +// go.y:hidden_type +func (p *parser) hidden_type() *Type { + if trace && Debug['x'] != 0 { + defer p.trace("hidden_type")() + } + + switch p.tok { + default: + return p.hidden_type_misc() + case LCOMM: + return p.hidden_type_recv_chan() + case LFUNC: + return p.hidden_type_func() + } +} + +// go.y:hidden_type_non_recv_chan +func (p *parser) hidden_type_non_recv_chan() *Type { + if trace && Debug['x'] != 0 { + defer p.trace("hidden_type_non_recv_chan")() + } + + switch p.tok { + default: + return p.hidden_type_misc() + case LFUNC: + return p.hidden_type_func() + } +} + +// go.y:hidden_type_misc +func (p *parser) hidden_type_misc() *Type { + if trace && Debug['x'] != 0 { + defer p.trace("hidden_type_misc")() + } + + switch p.tok { + case '@': + // hidden_importsym + s1 := p.hidden_importsym() + return pkgtype(s1) + + case LNAME: + // LNAME + s1 := p.sym_ + p.next() + + // predefined name like uint8 + s1 = Pkglookup(s1.Name, builtinpkg) + if s1.Def == nil || s1.Def.Op != OTYPE { + Yyerror("%s is not a type", s1.Name) + return nil + } else { + return s1.Def.Type + } + + case '[': + // '[' ']' hidden_type + // '[' LLITERAL ']' hidden_type + p.next() + var s2 *Node + if p.tok == LLITERAL { + s2 = nodlit(p.val) + p.next() + } + p.want(']') + s4 := p.hidden_type() + + return aindex(s2, s4) + + case LMAP: + // LMAP '[' hidden_type ']' hidden_type + p.next() + p.want('[') + s3 := p.hidden_type() + p.want(']') + s5 := p.hidden_type() + + return maptype(s3, s5) + + case LSTRUCT: + // LSTRUCT '{' ohidden_structdcl_list '}' + p.next() + p.want('{') + s3 := p.ohidden_structdcl_list() + p.want('}') + + return tostruct(s3) + + case LINTERFACE: + // LINTERFACE '{' ohidden_interfacedcl_list '}' + p.next() + p.want('{') + s3 := p.ohidden_interfacedcl_list() + p.want('}') + + return tointerface(s3) + + case '*': + // '*' hidden_type + p.next() + s2 := p.hidden_type() + return Ptrto(s2) + + case LCHAN: + p.next() + switch p.tok { + default: + // LCHAN hidden_type_non_recv_chan + s2 := p.hidden_type_non_recv_chan() + ss := typ(TCHAN) + ss.Type = s2 + ss.Chan = Cboth + return ss + + case '(': + // LCHAN '(' hidden_type_recv_chan ')' + p.next() + s3 := p.hidden_type_recv_chan() + p.want(')') + ss := typ(TCHAN) + ss.Type = s3 + ss.Chan = Cboth + return ss + + case LCOMM: + // LCHAN hidden_type + p.next() + s3 := p.hidden_type() + ss := typ(TCHAN) + ss.Type = s3 + ss.Chan = Csend + return ss + } + + default: + p.import_error() + return nil + } +} + +// go.y:hidden_type_recv_chan +func (p *parser) hidden_type_recv_chan() *Type { + if trace && Debug['x'] != 0 { + defer p.trace("hidden_type_recv_chan")() + } + + p.want(LCOMM) + p.want(LCHAN) + s3 := p.hidden_type() + + ss := typ(TCHAN) + ss.Type = s3 + ss.Chan = Crecv + return ss +} + +// go.y:hidden_type_func +func (p *parser) hidden_type_func() *Type { + if trace && Debug['x'] != 0 { + defer p.trace("hidden_type_func")() + } + + p.want(LFUNC) + p.want('(') + s3 := p.ohidden_funarg_list() + p.want(')') + s5 := p.ohidden_funres() + + return functype(nil, s3, s5) +} + +// go.y:hidden_funarg +func (p *parser) hidden_funarg() *Node { + if trace && Debug['x'] != 0 { + defer p.trace("hidden_funarg")() + } + + s1 := p.sym() + switch p.tok { + default: + s2 := p.hidden_type() + s3 := p.oliteral() + + ss := Nod(ODCLFIELD, nil, typenod(s2)) + if s1 != nil { + ss.Left = newname(s1) + } + ss.SetVal(s3) + return ss + + case LDDD: + p.next() + s3 := p.hidden_type() + s4 := p.oliteral() + + var t *Type + + t = typ(TARRAY) + t.Bound = -1 + t.Type = s3 + + ss := Nod(ODCLFIELD, nil, typenod(t)) + if s1 != nil { + ss.Left = newname(s1) + } + ss.Isddd = true + ss.SetVal(s4) + + return ss + } +} + +// go.y:hidden_structdcl +func (p *parser) hidden_structdcl() *Node { + if trace && Debug['x'] != 0 { + defer p.trace("hidden_structdcl")() + } + + s1 := p.sym() + s2 := p.hidden_type() + s3 := p.oliteral() + + var s *Sym + var pkg *Pkg + + var ss *Node + if s1 != nil && s1.Name != "?" { + ss = Nod(ODCLFIELD, newname(s1), typenod(s2)) + ss.SetVal(s3) + } else { + s = s2.Sym + if s == nil && Isptr[s2.Etype] { + s = s2.Type.Sym + } + pkg = importpkg + if s1 != nil { + pkg = s1.Pkg + } + ss = embedded(s, pkg) + ss.Right = typenod(s2) + ss.SetVal(s3) + } + + return ss +} + +// go.y:hidden_interfacedcl +func (p *parser) hidden_interfacedcl() *Node { + if trace && Debug['x'] != 0 { + defer p.trace("hidden_interfacedcl")() + } + + // TODO(gri) possible conflict here: both cases may start with '@' per grammar + switch p.tok { + case LNAME, '@', '?': + s1 := p.sym() + p.want('(') + s3 := p.ohidden_funarg_list() + p.want(')') + s5 := p.ohidden_funres() + + return Nod(ODCLFIELD, newname(s1), typenod(functype(fakethis(), s3, s5))) + + default: + s1 := p.hidden_type() + + return Nod(ODCLFIELD, nil, typenod(s1)) + } +} + +// go.y:ohidden_funres +func (p *parser) ohidden_funres() *NodeList { + if trace && Debug['x'] != 0 { + defer p.trace("ohidden_funres")() + } + + switch p.tok { + default: + return nil + + case '(', '@', LNAME, '[', LMAP, LSTRUCT, LINTERFACE, '*', LCHAN, LCOMM, LFUNC: + return p.hidden_funres() + } +} + +// go.y:hidden_funres +func (p *parser) hidden_funres() *NodeList { + if trace && Debug['x'] != 0 { + defer p.trace("hidden_funres")() + } + + switch p.tok { + case '(': + p.next() + s2 := p.ohidden_funarg_list() + p.want(')') + return s2 + + default: + s1 := p.hidden_type() + return list1(Nod(ODCLFIELD, nil, typenod(s1))) + } +} + +// ---------------------------------------------------------------------------- +// Importing constants + +// go.y:hidden_literal +func (p *parser) hidden_literal() *Node { + if trace && Debug['x'] != 0 { + defer p.trace("hidden_literal")() + } + + switch p.tok { + case LLITERAL: + ss := nodlit(p.val) + p.next() + return ss + + case '-': + p.next() + if p.tok == LLITERAL { + ss := nodlit(p.val) + p.next() + switch ss.Val().Ctype() { + case CTINT, CTRUNE: + mpnegfix(ss.Val().U.(*Mpint)) + break + case CTFLT: + mpnegflt(ss.Val().U.(*Mpflt)) + break + case CTCPLX: + mpnegflt(&ss.Val().U.(*Mpcplx).Real) + mpnegflt(&ss.Val().U.(*Mpcplx).Imag) + break + default: + Yyerror("bad negated constant") + } + return ss + } else { + p.import_error() + return nil + } + + case LNAME, '@', '?': + s1 := p.sym() + ss := oldname(Pkglookup(s1.Name, builtinpkg)) + if ss.Op != OLITERAL { + Yyerror("bad constant %v", ss.Sym) + } + return ss + + default: + p.import_error() + return nil + } +} + +// go.y:hidden_constant +func (p *parser) hidden_constant() *Node { + if trace && Debug['x'] != 0 { + defer p.trace("hidden_constant")() + } + + switch p.tok { + default: + return p.hidden_literal() + case '(': + p.next() + s2 := p.hidden_literal() + p.want('+') + s4 := p.hidden_literal() + p.want(')') + + if s2.Val().Ctype() == CTRUNE && s4.Val().Ctype() == CTINT { + ss := s2 + mpaddfixfix(s2.Val().U.(*Mpint), s4.Val().U.(*Mpint), 0) + return ss + } + s4.Val().U.(*Mpcplx).Real = s4.Val().U.(*Mpcplx).Imag + Mpmovecflt(&s4.Val().U.(*Mpcplx).Imag, 0.0) + return nodcplxlit(s2.Val(), s4.Val()) + } +} + +// go.y:hidden_import_list +func (p *parser) hidden_import_list() { + if trace && Debug['x'] != 0 { + defer p.trace("hidden_import_list")() + } + + for p.tok != '$' { + p.hidden_import() + } +} + +// go.y:hidden_funarg_list +func (p *parser) hidden_funarg_list() *NodeList { + if trace && Debug['x'] != 0 { + defer p.trace("hidden_funarg_list")() + } + + s1 := p.hidden_funarg() + ss := list1(s1) + for p.got(',') { + s3 := p.hidden_funarg() + ss = list(ss, s3) + } + return ss +} + +// go.y:hidden_structdcl_list +func (p *parser) hidden_structdcl_list() *NodeList { + if trace && Debug['x'] != 0 { + defer p.trace("hidden_structdcl_list")() + } + + s1 := p.hidden_structdcl() + ss := list1(s1) + for p.got(';') { + s3 := p.hidden_structdcl() + ss = list(ss, s3) + } + return ss +} + +// go.y:hidden_interfacedcl_list +func (p *parser) hidden_interfacedcl_list() *NodeList { + if trace && Debug['x'] != 0 { + defer p.trace("hidden_interfacedcl_list")() + } + + s1 := p.hidden_interfacedcl() + ss := list1(s1) + for p.got(';') { + s3 := p.hidden_interfacedcl() + ss = list(ss, s3) + } + return ss +} diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go index 6bc4bc8d01..573a6e4c8a 100644 --- a/src/cmd/compile/internal/gc/subr.go +++ b/src/cmd/compile/internal/gc/subr.go @@ -34,7 +34,7 @@ func errorexit() { } func parserline() int { - if parsing && theparser.Lookahead() > 0 { + if oldparser != 0 && parsing && theparser.Lookahead() > 0 { // parser has one symbol lookahead return int(prevlineno) } diff --git a/test/fixedbugs/bug358.go b/test/fixedbugs/bug358.go index 063c2e0bf8..6f9d73cf1f 100644 --- a/test/fixedbugs/bug358.go +++ b/test/fixedbugs/bug358.go @@ -10,13 +10,14 @@ package main import ( - "io/ioutil" // GCCGO_ERROR "imported and not used" + // avoid imported and not used errors + // "io/ioutil" "net/http" - "os" // GCCGO_ERROR "imported and not used" + // "os" ) func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) // ERROR "syntax error|invalid use of type" + return func(w http.ResponseWriter, r *http.Request) // ERROR "syntax error|not an expression|invalid use of type" } type Page struct { diff --git a/test/syntax/chan.go b/test/syntax/chan.go index 3b68bda35f..b016790fe7 100644 --- a/test/syntax/chan.go +++ b/test/syntax/chan.go @@ -8,10 +8,10 @@ package main type xyz struct { ch chan -} // ERROR "unexpected .*}.* in channel type" +} // ERROR "unexpected .*}.* in channel type|missing channel element type" -func Foo(y chan) { // ERROR "unexpected .*\).* in channel type" +func Foo(y chan) { // ERROR "unexpected .*\).* in channel type|missing channel element type" } -func Bar(x chan, y int) { // ERROR "unexpected comma in channel type" +func Bar(x chan, y int) { // ERROR "unexpected comma in channel type|missing channel element type" } diff --git a/test/syntax/forvar.go b/test/syntax/forvar.go index dc592d2b64..043c299040 100644 --- a/test/syntax/forvar.go +++ b/test/syntax/forvar.go @@ -7,4 +7,5 @@ package main func main() { + var x int // avoid undefined: x error below with recursive-descent parser for var x = 0; x < 10; x++ { // ERROR "var declaration not allowed in for initializer" diff --git a/test/syntax/semi4.go b/test/syntax/semi4.go index 99c2d22561..1f4e679cf8 100644 --- a/test/syntax/semi4.go +++ b/test/syntax/semi4.go @@ -8,7 +8,7 @@ package main func main() { for x // GCCGO_ERROR "undefined" - { // ERROR "missing .*{.* after for clause" + { // ERROR "missing .*{.* after for clause|missing operand" z // GCCGO_ERROR "undefined" diff --git a/test/syntax/semi6.go b/test/syntax/semi6.go index c1e1cc363a..1b51d8ba7e 100644 --- a/test/syntax/semi6.go +++ b/test/syntax/semi6.go @@ -7,7 +7,5 @@ package main type T // ERROR "unexpected semicolon or newline in type declaration" -{ - - - +// line below uncommented to avoid follow-up error +// { \ No newline at end of file diff --git a/test/syntax/semi7.go b/test/syntax/semi7.go index 6c9ade8bc2..357352dea9 100644 --- a/test/syntax/semi7.go +++ b/test/syntax/semi7.go @@ -8,7 +8,7 @@ package main func main() { if x { } // GCCGO_ERROR "undefined" - else { } // ERROR "unexpected semicolon or newline before .?else.?" + else { } // ERROR "unexpected semicolon or newline before .?else.?|unexpected else" } diff --git a/test/syntax/vareq1.go b/test/syntax/vareq1.go index e900eabebe..d4952fedd7 100644 --- a/test/syntax/vareq1.go +++ b/test/syntax/vareq1.go @@ -6,5 +6,5 @@ package main -var x map[string]string{"a":"b"} // ERROR "unexpected { at end of statement|expected ';' or newline after top level declaration" +var x map[string]string{"a":"b"} // ERROR "unexpected { at end of statement|unexpected { after top level declaration|expected ';' or newline after top level declaration"