| // Copyright 2009 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. |
| |
| /* |
| * Go language grammar. |
| * |
| * The Go semicolon rules are: |
| * |
| * 1. all statements and declarations are terminated by semicolons. |
| * 2. semicolons can be omitted before a closing ) or }. |
| * 3. semicolons are inserted by the lexer before a newline |
| * following a specific list of tokens. |
| * |
| * Rules #1 and #2 are accomplished by writing the lists as |
| * semicolon-separated lists with an optional trailing semicolon. |
| * Rule #3 is implemented in yylex. |
| */ |
| |
| %{ |
| package gc |
| |
| import ( |
| "strings" |
| ) |
| %} |
| %union { |
| node *Node |
| list *NodeList |
| typ *Type |
| sym *Sym |
| val Val |
| i int |
| } |
| |
| // |sed 's/.* //' |9 fmt -l1 |sort |9 fmt -l50 | sed 's/^/%xxx /' |
| |
| %token <val> LLITERAL |
| %token <i> LASOP LCOLAS |
| %token <sym> LBREAK LCASE LCHAN LCONST LCONTINUE LDDD |
| %token <sym> LDEFAULT LDEFER LELSE LFALL LFOR LFUNC LGO LGOTO |
| %token <sym> LIF LIMPORT LINTERFACE LMAP LNAME |
| %token <sym> LPACKAGE LRANGE LRETURN LSELECT LSTRUCT LSWITCH |
| %token <sym> LTYPE LVAR |
| |
| %token LANDAND LANDNOT LBODY LCOMM LDEC LEQ LGE LGT |
| %token LIGNORE LINC LLE LLSH LLT LNE LOROR LRSH |
| |
| %type <i> lbrace import_here |
| %type <sym> sym packname |
| %type <val> oliteral |
| |
| %type <node> stmt ntype |
| %type <node> arg_type |
| %type <node> case caseblock |
| %type <node> compound_stmt dotname embed expr complitexpr bare_complitexpr |
| %type <node> expr_or_type |
| %type <node> fndcl hidden_fndcl fnliteral |
| %type <node> for_body for_header for_stmt if_header if_stmt non_dcl_stmt |
| %type <node> interfacedcl keyval labelname name |
| %type <node> name_or_type non_expr_type |
| %type <node> new_name dcl_name oexpr typedclname |
| %type <node> onew_name |
| %type <node> osimple_stmt pexpr pexpr_no_paren |
| %type <node> pseudocall range_stmt select_stmt |
| %type <node> simple_stmt |
| %type <node> switch_stmt uexpr |
| %type <node> xfndcl typedcl start_complit |
| |
| %type <list> xdcl fnbody fnres loop_body dcl_name_list |
| %type <list> new_name_list expr_list keyval_list braced_keyval_list expr_or_type_list xdcl_list |
| %type <list> oexpr_list caseblock_list elseif elseif_list else stmt_list oarg_type_list_ocomma arg_type_list |
| %type <list> interfacedcl_list vardcl vardcl_list structdcl structdcl_list |
| %type <list> common_dcl constdcl constdcl1 constdcl_list typedcl_list |
| |
| %type <node> convtype comptype dotdotdot |
| %type <node> indcl interfacetype structtype ptrtype |
| %type <node> recvchantype non_recvchantype othertype fnret_type fntype |
| |
| %type <sym> hidden_importsym hidden_pkg_importsym |
| |
| %type <node> hidden_constant hidden_literal hidden_funarg |
| %type <node> hidden_interfacedcl hidden_structdcl |
| |
| %type <list> hidden_funres |
| %type <list> ohidden_funres |
| %type <list> hidden_funarg_list ohidden_funarg_list |
| %type <list> hidden_interfacedcl_list ohidden_interfacedcl_list |
| %type <list> hidden_structdcl_list ohidden_structdcl_list |
| |
| %type <typ> hidden_type hidden_type_misc hidden_pkgtype |
| %type <typ> hidden_type_func |
| %type <typ> hidden_type_recv_chan hidden_type_non_recv_chan |
| |
| %left LCOMM /* outside the usual hierarchy; here for good error messages */ |
| |
| %left LOROR |
| %left LANDAND |
| %left LEQ LNE LLE LGE LLT LGT |
| %left '+' '-' '|' '^' |
| %left '*' '/' '%' '&' LLSH LRSH LANDNOT |
| |
| /* |
| * manual override of shift/reduce conflicts. |
| * the general form is that we assign a precedence |
| * to the token being shifted and then introduce |
| * NotToken with lower precedence or PreferToToken with higher |
| * and annotate the reducing rule accordingly. |
| */ |
| %left NotPackage |
| %left LPACKAGE |
| |
| %left NotParen |
| %left '(' |
| |
| %left ')' |
| %left PreferToRightParen |
| |
| // TODO(rsc): Add %error-verbose |
| |
| %% |
| file: |
| loadsys |
| package |
| imports |
| xdcl_list |
| { |
| xtop = concat(xtop, $4); |
| } |
| |
| package: |
| %prec NotPackage |
| { |
| prevlineno = lineno; |
| Yyerror("package statement must be first"); |
| errorexit(); |
| } |
| | LPACKAGE sym ';' |
| { |
| mkpackage($2.Name); |
| } |
| |
| /* |
| * 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. |
| */ |
| loadsys: |
| { |
| importpkg = Runtimepkg; |
| |
| if Debug['A'] != 0 { |
| cannedimports("runtime.Builtin", "package runtime\n\n$$\n\n"); |
| } else { |
| cannedimports("runtime.Builtin", runtimeimport); |
| } |
| curio.importsafe = true |
| } |
| import_package |
| import_there |
| { |
| importpkg = nil; |
| } |
| |
| imports: |
| | imports import ';' |
| |
| import: |
| LIMPORT import_stmt |
| | LIMPORT '(' import_stmt_list osemi ')' |
| | LIMPORT '(' ')' |
| |
| import_stmt: |
| import_here import_package 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.Pkg = ipkg; |
| pack.Lineno = int32($1); |
| |
| if strings.HasPrefix(my.Name, ".") { |
| importdot(ipkg, pack); |
| break; |
| } |
| if my.Name == "init" { |
| Yyerror("cannot import package as init - init must be a func"); |
| break; |
| } |
| if my.Name == "_" { |
| break; |
| } |
| if my.Def != nil { |
| lineno = int32($1); |
| redeclare(my, "as imported package name"); |
| } |
| my.Def = pack; |
| my.Lastlineno = int32($1); |
| my.Block = 1; // at top level |
| } |
| | import_here 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 { |
| Fatal("phase error in import"); |
| } |
| } |
| |
| import_stmt_list: |
| import_stmt |
| | import_stmt_list ';' import_stmt |
| |
| import_here: |
| LLITERAL |
| { |
| // import with original name |
| $$ = parserline(); |
| importmyname = nil; |
| importfile(&$1, $$); |
| } |
| | sym LLITERAL |
| { |
| // import with given name |
| $$ = parserline(); |
| importmyname = $1; |
| importfile(&$2, $$); |
| } |
| | '.' LLITERAL |
| { |
| // import into my name space |
| $$ = parserline(); |
| importmyname = Lookup("."); |
| importfile(&$2, $$); |
| } |
| |
| import_package: |
| LPACKAGE LNAME import_safety ';' |
| { |
| if importpkg.Name == "" { |
| importpkg.Name = $2.Name; |
| Pkglookup($2.Name, nil).Npkg++; |
| } else if importpkg.Name != $2.Name { |
| Yyerror("conflicting names %s and %s for package \"%v\"", importpkg.Name, $2.Name, Zconv(importpkg.Path, 0)); |
| } |
| importpkg.Direct = 1; |
| importpkg.Safe = curio.importsafe |
| |
| if safemode != 0 && !curio.importsafe { |
| Yyerror("cannot import unsafe package \"%v\"", Zconv(importpkg.Path, 0)); |
| } |
| } |
| |
| import_safety: |
| | LNAME |
| { |
| if $1.Name == "safe" { |
| curio.importsafe = true |
| } |
| } |
| |
| import_there: |
| { |
| defercheckwidth(); |
| } |
| hidden_import_list '$' '$' |
| { |
| resumecheckwidth(); |
| unimportfile(); |
| } |
| |
| /* |
| * declarations |
| */ |
| xdcl: |
| { |
| Yyerror("empty top-level declaration"); |
| $$ = nil; |
| } |
| | common_dcl |
| | xfndcl |
| { |
| $$ = list1($1); |
| } |
| | non_dcl_stmt |
| { |
| Yyerror("non-declaration statement outside function body"); |
| $$ = nil; |
| } |
| | error |
| { |
| $$ = nil; |
| } |
| |
| common_dcl: |
| LVAR vardcl |
| { |
| $$ = $2; |
| } |
| | LVAR '(' vardcl_list osemi ')' |
| { |
| $$ = $3; |
| } |
| | LVAR '(' ')' |
| { |
| $$ = nil; |
| } |
| | lconst constdcl |
| { |
| $$ = $2; |
| iota_ = -100000; |
| lastconst = nil; |
| } |
| | lconst '(' constdcl osemi ')' |
| { |
| $$ = $3; |
| iota_ = -100000; |
| lastconst = nil; |
| } |
| | lconst '(' constdcl ';' constdcl_list osemi ')' |
| { |
| $$ = concat($3, $5); |
| iota_ = -100000; |
| lastconst = nil; |
| } |
| | lconst '(' ')' |
| { |
| $$ = nil; |
| iota_ = -100000; |
| } |
| | LTYPE typedcl |
| { |
| $$ = list1($2); |
| } |
| | LTYPE '(' typedcl_list osemi ')' |
| { |
| $$ = $3; |
| } |
| | LTYPE '(' ')' |
| { |
| $$ = nil; |
| } |
| |
| lconst: |
| LCONST |
| { |
| iota_ = 0; |
| } |
| |
| vardcl: |
| dcl_name_list ntype |
| { |
| $$ = variter($1, $2, nil); |
| } |
| | dcl_name_list ntype '=' expr_list |
| { |
| $$ = variter($1, $2, $4); |
| } |
| | dcl_name_list '=' expr_list |
| { |
| $$ = variter($1, nil, $3); |
| } |
| |
| constdcl: |
| dcl_name_list ntype '=' expr_list |
| { |
| $$ = constiter($1, $2, $4); |
| } |
| | dcl_name_list '=' expr_list |
| { |
| $$ = constiter($1, nil, $3); |
| } |
| |
| constdcl1: |
| constdcl |
| | dcl_name_list ntype |
| { |
| $$ = constiter($1, $2, nil); |
| } |
| | dcl_name_list |
| { |
| $$ = constiter($1, nil, nil); |
| } |
| |
| typedclname: |
| sym |
| { |
| // different from dclname because the name |
| // becomes visible right here, not at the end |
| // of the declaration. |
| $$ = typedcl0($1); |
| } |
| |
| typedcl: |
| typedclname ntype |
| { |
| $$ = typedcl1($1, $2, 1); |
| } |
| |
| simple_stmt: |
| expr |
| { |
| $$ = $1; |
| |
| // 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($$.Op) { |
| case ONAME, ONONAME, OTYPE, OPACK, OLITERAL: |
| $$ = Nod(OPAREN, $$, nil); |
| $$.Implicit = 1; |
| break; |
| } |
| } |
| | expr LASOP expr |
| { |
| $$ = Nod(OASOP, $1, $3); |
| $$.Etype = uint8($2); // rathole to pass opcode |
| } |
| | expr_list '=' expr_list |
| { |
| if $1.Next == nil && $3.Next == nil { |
| // simple |
| $$ = Nod(OAS, $1.N, $3.N); |
| break; |
| } |
| // multiple |
| $$ = Nod(OAS2, nil, nil); |
| $$.List = $1; |
| $$.Rlist = $3; |
| } |
| | expr_list LCOLAS expr_list |
| { |
| if $3.N.Op == OTYPESW { |
| $$ = Nod(OTYPESW, nil, $3.N.Right); |
| if $3.Next != nil { |
| Yyerror("expr.(type) must be alone in list"); |
| } |
| if $1.Next != nil { |
| Yyerror("argument count mismatch: %d = %d", count($1), 1); |
| } else if ($1.N.Op != ONAME && $1.N.Op != OTYPE && $1.N.Op != ONONAME) || isblank($1.N) { |
| Yyerror("invalid variable name %s in type switch", Nconv($1.N, 0)); |
| } else { |
| $$.Left = dclname($1.N.Sym); |
| } // it's a colas, so must not re-use an oldname. |
| break; |
| } |
| $$ = colas($1, $3, int32($2)); |
| } |
| | expr LINC |
| { |
| $$ = Nod(OASOP, $1, Nodintconst(1)); |
| $$.Implicit = 1; |
| $$.Etype = OADD; |
| } |
| | expr LDEC |
| { |
| $$ = Nod(OASOP, $1, Nodintconst(1)); |
| $$.Implicit = 1; |
| $$.Etype = OSUB; |
| } |
| |
| case: |
| LCASE expr_or_type_list ':' |
| { |
| var n, nn *Node |
| |
| // will be converted to OCASE |
| // right will point to next case |
| // done in casebody() |
| markdcl(); |
| $$ = Nod(OXCASE, nil, nil); |
| $$.List = $2; |
| if typesw != nil && typesw.Right != nil { |
| n = typesw.Right.Left |
| if n != nil { |
| // type switch - declare variable |
| nn = newname(n.Sym); |
| declare(nn, dclcontext); |
| $$.Nname = nn; |
| |
| // keep track of the instances for reporting unused |
| nn.Defn = typesw.Right; |
| } |
| } |
| } |
| | LCASE expr_or_type_list '=' expr ':' |
| { |
| var n *Node |
| |
| // will be converted to OCASE |
| // right will point to next case |
| // done in casebody() |
| markdcl(); |
| $$ = Nod(OXCASE, nil, nil); |
| if $2.Next == nil { |
| n = Nod(OAS, $2.N, $4); |
| } else { |
| n = Nod(OAS2, nil, nil); |
| n.List = $2; |
| n.Rlist = list1($4); |
| } |
| $$.List = list1(n); |
| } |
| | LCASE expr_or_type_list LCOLAS expr ':' |
| { |
| // will be converted to OCASE |
| // right will point to next case |
| // done in casebody() |
| markdcl(); |
| $$ = Nod(OXCASE, nil, nil); |
| $$.List = list1(colas($2, list1($4), int32($3))); |
| } |
| | LDEFAULT ':' |
| { |
| var n, nn *Node |
| |
| markdcl(); |
| $$ = Nod(OXCASE, nil, nil); |
| if typesw != nil && typesw.Right != nil { |
| n = typesw.Right.Left |
| if n != nil { |
| // type switch - declare variable |
| nn = newname(n.Sym); |
| declare(nn, dclcontext); |
| $$.Nname = nn; |
| |
| // keep track of the instances for reporting unused |
| nn.Defn = typesw.Right; |
| } |
| } |
| } |
| |
| compound_stmt: |
| '{' |
| { |
| markdcl(); |
| } |
| stmt_list '}' |
| { |
| if $3 == nil { |
| $$ = Nod(OEMPTY, nil, nil); |
| } else { |
| $$ = liststmt($3); |
| } |
| popdcl(); |
| } |
| |
| caseblock: |
| case |
| { |
| // 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; |
| $1.Xoffset = int64(block); |
| } |
| stmt_list |
| { |
| // 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"); |
| } |
| $$ = $1; |
| $$.Nbody = $3; |
| popdcl(); |
| } |
| |
| caseblock_list: |
| { |
| $$ = nil; |
| } |
| | caseblock_list caseblock |
| { |
| $$ = list($1, $2); |
| } |
| |
| loop_body: |
| LBODY |
| { |
| markdcl(); |
| } |
| stmt_list '}' |
| { |
| $$ = $3; |
| popdcl(); |
| } |
| |
| range_stmt: |
| expr_list '=' LRANGE expr |
| { |
| $$ = Nod(ORANGE, nil, $4); |
| $$.List = $1; |
| $$.Etype = 0; // := flag |
| } |
| | expr_list LCOLAS LRANGE expr |
| { |
| $$ = Nod(ORANGE, nil, $4); |
| $$.List = $1; |
| $$.Colas = 1; |
| colasdefn($1, $$); |
| } |
| | LRANGE expr |
| { |
| $$ = Nod(ORANGE, nil, $2); |
| $$.Etype = 0; // := flag |
| } |
| |
| for_header: |
| osimple_stmt ';' osimple_stmt ';' osimple_stmt |
| { |
| // init ; test ; incr |
| if $5 != nil && $5.Colas != 0 { |
| Yyerror("cannot declare in the for-increment"); |
| } |
| $$ = Nod(OFOR, nil, nil); |
| if $1 != nil { |
| $$.Ninit = list1($1); |
| } |
| $$.Ntest = $3; |
| $$.Nincr = $5; |
| } |
| | osimple_stmt |
| { |
| // normal test |
| $$ = Nod(OFOR, nil, nil); |
| $$.Ntest = $1; |
| } |
| | range_stmt |
| |
| for_body: |
| for_header loop_body |
| { |
| $$ = $1; |
| $$.Nbody = concat($$.Nbody, $2); |
| } |
| |
| for_stmt: |
| LFOR |
| { |
| markdcl(); |
| } |
| for_body |
| { |
| $$ = $3; |
| popdcl(); |
| } |
| |
| if_header: |
| osimple_stmt |
| { |
| // test |
| $$ = Nod(OIF, nil, nil); |
| $$.Ntest = $1; |
| } |
| | osimple_stmt ';' osimple_stmt |
| { |
| // init ; test |
| $$ = Nod(OIF, nil, nil); |
| if $1 != nil { |
| $$.Ninit = list1($1); |
| } |
| $$.Ntest = $3; |
| } |
| |
| /* IF cond body (ELSE IF cond body)* (ELSE block)? */ |
| if_stmt: |
| LIF |
| { |
| markdcl(); |
| } |
| if_header |
| { |
| if $3.Ntest == nil { |
| Yyerror("missing condition in if statement"); |
| } |
| } |
| loop_body |
| { |
| $3.Nbody = $5; |
| } |
| elseif_list else |
| { |
| var n *Node |
| var nn *NodeList |
| |
| $$ = $3; |
| n = $3; |
| popdcl(); |
| for nn = concat($7, $8); nn != nil; nn = nn.Next { |
| if nn.N.Op == OIF { |
| popdcl(); |
| } |
| n.Nelse = list1(nn.N); |
| n = nn.N; |
| } |
| } |
| |
| elseif: |
| LELSE LIF |
| { |
| markdcl(); |
| } |
| if_header loop_body |
| { |
| if $4.Ntest == nil { |
| Yyerror("missing condition in if statement"); |
| } |
| $4.Nbody = $5; |
| $$ = list1($4); |
| } |
| |
| elseif_list: |
| { |
| $$ = nil; |
| } |
| | elseif_list elseif |
| { |
| $$ = concat($1, $2); |
| } |
| |
| else: |
| { |
| $$ = nil; |
| } |
| | LELSE compound_stmt |
| { |
| l := &NodeList{N: $2} |
| l.End = l |
| $$ = l; |
| } |
| |
| switch_stmt: |
| LSWITCH |
| { |
| markdcl(); |
| } |
| if_header |
| { |
| var n *Node |
| n = $3.Ntest; |
| if n != nil && n.Op != OTYPESW { |
| n = nil; |
| } |
| typesw = Nod(OXXX, typesw, n); |
| } |
| LBODY caseblock_list '}' |
| { |
| $$ = $3; |
| $$.Op = OSWITCH; |
| $$.List = $6; |
| typesw = typesw.Left; |
| popdcl(); |
| } |
| |
| select_stmt: |
| LSELECT |
| { |
| typesw = Nod(OXXX, typesw, nil); |
| } |
| LBODY caseblock_list '}' |
| { |
| $$ = Nod(OSELECT, nil, nil); |
| $$.Lineno = typesw.Lineno; |
| $$.List = $4; |
| typesw = typesw.Left; |
| } |
| |
| /* |
| * expressions |
| */ |
| expr: |
| uexpr |
| | expr LOROR expr |
| { |
| $$ = Nod(OOROR, $1, $3); |
| } |
| | expr LANDAND expr |
| { |
| $$ = Nod(OANDAND, $1, $3); |
| } |
| | expr LEQ expr |
| { |
| $$ = Nod(OEQ, $1, $3); |
| } |
| | expr LNE expr |
| { |
| $$ = Nod(ONE, $1, $3); |
| } |
| | expr LLT expr |
| { |
| $$ = Nod(OLT, $1, $3); |
| } |
| | expr LLE expr |
| { |
| $$ = Nod(OLE, $1, $3); |
| } |
| | expr LGE expr |
| { |
| $$ = Nod(OGE, $1, $3); |
| } |
| | expr LGT expr |
| { |
| $$ = Nod(OGT, $1, $3); |
| } |
| | expr '+' expr |
| { |
| $$ = Nod(OADD, $1, $3); |
| } |
| | expr '-' expr |
| { |
| $$ = Nod(OSUB, $1, $3); |
| } |
| | expr '|' expr |
| { |
| $$ = Nod(OOR, $1, $3); |
| } |
| | expr '^' expr |
| { |
| $$ = Nod(OXOR, $1, $3); |
| } |
| | expr '*' expr |
| { |
| $$ = Nod(OMUL, $1, $3); |
| } |
| | expr '/' expr |
| { |
| $$ = Nod(ODIV, $1, $3); |
| } |
| | expr '%' expr |
| { |
| $$ = Nod(OMOD, $1, $3); |
| } |
| | expr '&' expr |
| { |
| $$ = Nod(OAND, $1, $3); |
| } |
| | expr LANDNOT expr |
| { |
| $$ = Nod(OANDNOT, $1, $3); |
| } |
| | expr LLSH expr |
| { |
| $$ = Nod(OLSH, $1, $3); |
| } |
| | expr LRSH expr |
| { |
| $$ = Nod(ORSH, $1, $3); |
| } |
| /* not an expression anymore, but left in so we can give a good error */ |
| | expr LCOMM expr |
| { |
| $$ = Nod(OSEND, $1, $3); |
| } |
| |
| uexpr: |
| pexpr |
| | '*' uexpr |
| { |
| $$ = Nod(OIND, $2, nil); |
| } |
| | '&' uexpr |
| { |
| if $2.Op == OCOMPLIT { |
| // Special case for &T{...}: turn into (*T){...}. |
| $$ = $2; |
| $$.Right = Nod(OIND, $$.Right, nil); |
| $$.Right.Implicit = 1; |
| } else { |
| $$ = Nod(OADDR, $2, nil); |
| } |
| } |
| | '+' uexpr |
| { |
| $$ = Nod(OPLUS, $2, nil); |
| } |
| | '-' uexpr |
| { |
| $$ = Nod(OMINUS, $2, nil); |
| } |
| | '!' uexpr |
| { |
| $$ = Nod(ONOT, $2, nil); |
| } |
| | '~' uexpr |
| { |
| Yyerror("the bitwise complement operator is ^"); |
| $$ = Nod(OCOM, $2, nil); |
| } |
| | '^' uexpr |
| { |
| $$ = Nod(OCOM, $2, nil); |
| } |
| | LCOMM uexpr |
| { |
| $$ = Nod(ORECV, $2, nil); |
| } |
| |
| /* |
| * call-like statements that |
| * can be preceded by 'defer' and 'go' |
| */ |
| pseudocall: |
| pexpr '(' ')' |
| { |
| $$ = Nod(OCALL, $1, nil); |
| } |
| | pexpr '(' expr_or_type_list ocomma ')' |
| { |
| $$ = Nod(OCALL, $1, nil); |
| $$.List = $3; |
| } |
| | pexpr '(' expr_or_type_list LDDD ocomma ')' |
| { |
| $$ = Nod(OCALL, $1, nil); |
| $$.List = $3; |
| $$.Isddd = 1; |
| } |
| |
| pexpr_no_paren: |
| LLITERAL |
| { |
| $$ = nodlit($1); |
| } |
| | name |
| | pexpr '.' sym |
| { |
| if $1.Op == OPACK { |
| var s *Sym |
| s = restrictlookup($3.Name, $1.Pkg); |
| $1.Used = 1; |
| $$ = oldname(s); |
| break; |
| } |
| $$ = Nod(OXDOT, $1, newname($3)); |
| } |
| | pexpr '.' '(' expr_or_type ')' |
| { |
| $$ = Nod(ODOTTYPE, $1, $4); |
| } |
| | pexpr '.' '(' LTYPE ')' |
| { |
| $$ = Nod(OTYPESW, nil, $1); |
| } |
| | pexpr '[' expr ']' |
| { |
| $$ = Nod(OINDEX, $1, $3); |
| } |
| | pexpr '[' oexpr ':' oexpr ']' |
| { |
| $$ = Nod(OSLICE, $1, Nod(OKEY, $3, $5)); |
| } |
| | pexpr '[' oexpr ':' oexpr ':' oexpr ']' |
| { |
| if $5 == nil { |
| Yyerror("middle index required in 3-index slice"); |
| } |
| if $7 == nil { |
| Yyerror("final index required in 3-index slice"); |
| } |
| $$ = Nod(OSLICE3, $1, Nod(OKEY, $3, Nod(OKEY, $5, $7))); |
| } |
| | pseudocall |
| | convtype '(' expr ocomma ')' |
| { |
| // conversion |
| $$ = Nod(OCALL, $1, nil); |
| $$.List = list1($3); |
| } |
| | comptype lbrace start_complit braced_keyval_list '}' |
| { |
| $$ = $3; |
| $$.Right = $1; |
| $$.List = $4; |
| fixlbrace($2); |
| } |
| | pexpr_no_paren '{' start_complit braced_keyval_list '}' |
| { |
| $$ = $3; |
| $$.Right = $1; |
| $$.List = $4; |
| } |
| | '(' expr_or_type ')' '{' start_complit braced_keyval_list '}' |
| { |
| Yyerror("cannot parenthesize type in composite literal"); |
| $$ = $5; |
| $$.Right = $2; |
| $$.List = $6; |
| } |
| | fnliteral |
| |
| start_complit: |
| { |
| // composite expression. |
| // make node early so we get the right line number. |
| $$ = Nod(OCOMPLIT, nil, nil); |
| } |
| |
| keyval: |
| expr ':' complitexpr |
| { |
| $$ = Nod(OKEY, $1, $3); |
| } |
| |
| bare_complitexpr: |
| 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. |
| $$ = $1; |
| switch($$.Op) { |
| case ONAME, ONONAME, OTYPE, OPACK, OLITERAL: |
| $$ = Nod(OPAREN, $$, nil); |
| $$.Implicit = 1; |
| } |
| } |
| | '{' start_complit braced_keyval_list '}' |
| { |
| $$ = $2; |
| $$.List = $3; |
| } |
| |
| complitexpr: |
| expr |
| | '{' start_complit braced_keyval_list '}' |
| { |
| $$ = $2; |
| $$.List = $3; |
| } |
| |
| pexpr: |
| pexpr_no_paren |
| | '(' expr_or_type ')' |
| { |
| $$ = $2; |
| |
| // 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. |
| switch($$.Op) { |
| case ONAME, ONONAME, OPACK, OTYPE, OLITERAL, OTYPESW: |
| $$ = Nod(OPAREN, $$, nil); |
| } |
| } |
| |
| expr_or_type: |
| expr |
| | non_expr_type %prec PreferToRightParen |
| |
| name_or_type: |
| ntype |
| |
| lbrace: |
| LBODY |
| { |
| $$ = LBODY; |
| } |
| | '{' |
| { |
| $$ = '{'; |
| } |
| |
| /* |
| * names and types |
| * newname is used before declared |
| * oldname is used after declared |
| */ |
| new_name: |
| sym |
| { |
| if $1 == nil { |
| $$ = nil; |
| } else { |
| $$ = newname($1); |
| } |
| } |
| |
| dcl_name: |
| sym |
| { |
| $$ = dclname($1); |
| } |
| |
| onew_name: |
| { |
| $$ = nil; |
| } |
| | new_name |
| |
| sym: |
| LNAME |
| { |
| $$ = $1; |
| // during imports, unqualified non-exported identifiers are from builtinpkg |
| if importpkg != nil && !exportname($1.Name) { |
| $$ = Pkglookup($1.Name, builtinpkg); |
| } |
| } |
| | hidden_importsym |
| | '?' |
| { |
| $$ = nil; |
| } |
| |
| hidden_importsym: |
| '@' LLITERAL '.' LNAME |
| { |
| var p *Pkg |
| |
| if $2.U.Sval.S == "" { |
| p = importpkg; |
| } else { |
| if isbadimport($2.U.Sval) { |
| errorexit(); |
| } |
| p = mkpkg($2.U.Sval); |
| } |
| $$ = Pkglookup($4.Name, p); |
| } |
| | '@' LLITERAL '.' '?' |
| { |
| var p *Pkg |
| |
| if $2.U.Sval.S == "" { |
| p = importpkg; |
| } else { |
| if isbadimport($2.U.Sval) { |
| errorexit(); |
| } |
| p = mkpkg($2.U.Sval); |
| } |
| $$ = Pkglookup("?", p); |
| } |
| |
| name: |
| sym %prec NotParen |
| { |
| $$ = oldname($1); |
| if $$.Pack != nil { |
| $$.Pack.Used = 1; |
| } |
| } |
| |
| labelname: |
| new_name |
| |
| /* |
| * to avoid parsing conflicts, type is split into |
| * channel types |
| * function types |
| * parenthesized types |
| * any other type |
| * the type system makes additional restrictions, |
| * but those are not implemented in the grammar. |
| */ |
| dotdotdot: |
| LDDD |
| { |
| Yyerror("final argument in variadic function missing type"); |
| $$ = Nod(ODDD, typenod(typ(TINTER)), nil); |
| } |
| | LDDD ntype |
| { |
| $$ = Nod(ODDD, $2, nil); |
| } |
| |
| ntype: |
| recvchantype |
| | fntype |
| | othertype |
| | ptrtype |
| | dotname |
| | '(' ntype ')' |
| { |
| $$ = $2; |
| } |
| |
| non_expr_type: |
| recvchantype |
| | fntype |
| | othertype |
| | '*' non_expr_type |
| { |
| $$ = Nod(OIND, $2, nil); |
| } |
| |
| non_recvchantype: |
| fntype |
| | othertype |
| | ptrtype |
| | dotname |
| | '(' ntype ')' |
| { |
| $$ = $2; |
| } |
| |
| convtype: |
| fntype |
| | othertype |
| |
| comptype: |
| othertype |
| |
| fnret_type: |
| recvchantype |
| | fntype |
| | othertype |
| | ptrtype |
| | dotname |
| |
| dotname: |
| name |
| | name '.' sym |
| { |
| if $1.Op == OPACK { |
| var s *Sym |
| s = restrictlookup($3.Name, $1.Pkg); |
| $1.Used = 1; |
| $$ = oldname(s); |
| break; |
| } |
| $$ = Nod(OXDOT, $1, newname($3)); |
| } |
| |
| othertype: |
| '[' oexpr ']' ntype |
| { |
| $$ = Nod(OTARRAY, $2, $4); |
| } |
| | '[' LDDD ']' ntype |
| { |
| // array literal of nelem |
| $$ = Nod(OTARRAY, Nod(ODDD, nil, nil), $4); |
| } |
| | LCHAN non_recvchantype |
| { |
| $$ = Nod(OTCHAN, $2, nil); |
| $$.Etype = Cboth; |
| } |
| | LCHAN LCOMM ntype |
| { |
| $$ = Nod(OTCHAN, $3, nil); |
| $$.Etype = Csend; |
| } |
| | LMAP '[' ntype ']' ntype |
| { |
| $$ = Nod(OTMAP, $3, $5); |
| } |
| | structtype |
| | interfacetype |
| |
| ptrtype: |
| '*' ntype |
| { |
| $$ = Nod(OIND, $2, nil); |
| } |
| |
| recvchantype: |
| LCOMM LCHAN ntype |
| { |
| $$ = Nod(OTCHAN, $3, nil); |
| $$.Etype = Crecv; |
| } |
| |
| structtype: |
| LSTRUCT lbrace structdcl_list osemi '}' |
| { |
| $$ = Nod(OTSTRUCT, nil, nil); |
| $$.List = $3; |
| fixlbrace($2); |
| } |
| | LSTRUCT lbrace '}' |
| { |
| $$ = Nod(OTSTRUCT, nil, nil); |
| fixlbrace($2); |
| } |
| |
| interfacetype: |
| LINTERFACE lbrace interfacedcl_list osemi '}' |
| { |
| $$ = Nod(OTINTER, nil, nil); |
| $$.List = $3; |
| fixlbrace($2); |
| } |
| | LINTERFACE lbrace '}' |
| { |
| $$ = Nod(OTINTER, nil, nil); |
| fixlbrace($2); |
| } |
| |
| /* |
| * function stuff |
| * all in one place to show how crappy it all is |
| */ |
| xfndcl: |
| LFUNC fndcl fnbody |
| { |
| $$ = $2; |
| if $$ == nil { |
| break; |
| } |
| if noescape && $3 != nil { |
| Yyerror("can only use //go:noescape with external func implementations"); |
| } |
| $$.Nbody = $3; |
| $$.Endlineno = lineno; |
| $$.Noescape = noescape; |
| $$.Nosplit = nosplit; |
| $$.Nowritebarrier = nowritebarrier; |
| funcbody($$); |
| } |
| |
| fndcl: |
| sym '(' oarg_type_list_ocomma ')' fnres |
| { |
| var t *Node |
| |
| $$ = nil; |
| $3 = checkarglist($3, 1); |
| |
| if $1.Name == "init" { |
| $1 = renameinit(); |
| if $3 != nil || $5 != nil { |
| Yyerror("func init must have no arguments and no return values"); |
| } |
| } |
| if localpkg.Name == "main" && $1.Name == "main" { |
| if $3 != nil || $5 != nil { |
| Yyerror("func main must have no arguments and no return values"); |
| } |
| } |
| |
| t = Nod(OTFUNC, nil, nil); |
| t.List = $3; |
| t.Rlist = $5; |
| |
| $$ = Nod(ODCLFUNC, nil, nil); |
| $$.Nname = newname($1); |
| $$.Nname.Defn = $$; |
| $$.Nname.Ntype = t; // TODO: check if nname already has an ntype |
| declare($$.Nname, PFUNC); |
| |
| funchdr($$); |
| } |
| | '(' oarg_type_list_ocomma ')' sym '(' oarg_type_list_ocomma ')' fnres |
| { |
| var rcvr, t *Node |
| |
| $$ = nil; |
| $2 = checkarglist($2, 0); |
| $6 = checkarglist($6, 1); |
| |
| if $2 == nil { |
| Yyerror("method has no receiver"); |
| break; |
| } |
| if $2.Next != nil { |
| Yyerror("method has multiple receivers"); |
| break; |
| } |
| rcvr = $2.N; |
| if rcvr.Op != ODCLFIELD { |
| Yyerror("bad receiver in method"); |
| break; |
| } |
| |
| t = Nod(OTFUNC, rcvr, nil); |
| t.List = $6; |
| t.Rlist = $8; |
| |
| $$ = Nod(ODCLFUNC, nil, nil); |
| $$.Shortname = newname($4); |
| $$.Nname = methodname1($$.Shortname, rcvr.Right); |
| $$.Nname.Defn = $$; |
| $$.Nname.Ntype = t; |
| $$.Nname.Nointerface = nointerface; |
| declare($$.Nname, PFUNC); |
| |
| funchdr($$); |
| } |
| |
| hidden_fndcl: |
| hidden_pkg_importsym '(' ohidden_funarg_list ')' ohidden_funres |
| { |
| var s *Sym |
| var t *Type |
| |
| $$ = nil; |
| |
| s = $1; |
| t = functype(nil, $3, $5); |
| |
| importsym(s, ONAME); |
| if s.Def != nil && s.Def.Op == ONAME { |
| if Eqtype(t, s.Def.Type) { |
| dclcontext = PDISCARD; // since we skip funchdr below |
| break; |
| } |
| Yyerror("inconsistent definition for func %v during import\n\t%v\n\t%v", Sconv(s, 0), Tconv(s.Def.Type, 0), Tconv(t, 0)); |
| } |
| |
| $$ = newname(s); |
| $$.Type = t; |
| declare($$, PFUNC); |
| |
| funchdr($$); |
| } |
| | '(' hidden_funarg_list ')' sym '(' ohidden_funarg_list ')' ohidden_funres |
| { |
| $$ = methodname1(newname($4), $2.N.Right); |
| $$.Type = functype($2.N, $6, $8); |
| |
| checkwidth($$.Type); |
| addmethod($4, $$.Type, false, nointerface); |
| nointerface = false |
| funchdr($$); |
| |
| // 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. |
| $$.Type.Nname = $$; |
| } |
| |
| fntype: |
| LFUNC '(' oarg_type_list_ocomma ')' fnres |
| { |
| $3 = checkarglist($3, 1); |
| $$ = Nod(OTFUNC, nil, nil); |
| $$.List = $3; |
| $$.Rlist = $5; |
| } |
| |
| fnbody: |
| { |
| $$ = nil; |
| } |
| | '{' stmt_list '}' |
| { |
| $$ = $2; |
| if $$ == nil { |
| $$ = list1(Nod(OEMPTY, nil, nil)); |
| } |
| } |
| |
| fnres: |
| %prec NotParen |
| { |
| $$ = nil; |
| } |
| | fnret_type |
| { |
| $$ = list1(Nod(ODCLFIELD, nil, $1)); |
| } |
| | '(' oarg_type_list_ocomma ')' |
| { |
| $2 = checkarglist($2, 0); |
| $$ = $2; |
| } |
| |
| fnlitdcl: |
| fntype |
| { |
| closurehdr($1); |
| } |
| |
| fnliteral: |
| fnlitdcl lbrace stmt_list '}' |
| { |
| $$ = closurebody($3); |
| fixlbrace($2); |
| } |
| | fnlitdcl error |
| { |
| $$ = closurebody(nil); |
| } |
| |
| /* |
| * lists of things |
| * note that they are left recursive |
| * to conserve yacc stack. they need to |
| * be reversed to interpret correctly |
| */ |
| xdcl_list: |
| { |
| $$ = nil; |
| } |
| | xdcl_list xdcl ';' |
| { |
| $$ = concat($1, $2); |
| if nsyntaxerrors == 0 { |
| testdclstack(); |
| } |
| nointerface = false |
| noescape = false |
| nosplit = false |
| nowritebarrier = false |
| } |
| |
| vardcl_list: |
| vardcl |
| | vardcl_list ';' vardcl |
| { |
| $$ = concat($1, $3); |
| } |
| |
| constdcl_list: |
| constdcl1 |
| | constdcl_list ';' constdcl1 |
| { |
| $$ = concat($1, $3); |
| } |
| |
| typedcl_list: |
| typedcl |
| { |
| $$ = list1($1); |
| } |
| | typedcl_list ';' typedcl |
| { |
| $$ = list($1, $3); |
| } |
| |
| structdcl_list: |
| structdcl |
| | structdcl_list ';' structdcl |
| { |
| $$ = concat($1, $3); |
| } |
| |
| interfacedcl_list: |
| interfacedcl |
| { |
| $$ = list1($1); |
| } |
| | interfacedcl_list ';' interfacedcl |
| { |
| $$ = list($1, $3); |
| } |
| |
| structdcl: |
| new_name_list ntype oliteral |
| { |
| var l *NodeList |
| |
| var n *Node |
| l = $1; |
| if l == nil { |
| // ? symbol, during import (list1(nil) == nil) |
| n = $2; |
| if n.Op == OIND { |
| n = n.Left; |
| } |
| n = embedded(n.Sym, importpkg); |
| n.Right = $2; |
| n.Val = $3; |
| $$ = list1(n); |
| break; |
| } |
| |
| for l=$1; l != nil; l=l.Next { |
| l.N = Nod(ODCLFIELD, l.N, $2); |
| l.N.Val = $3; |
| } |
| } |
| | embed oliteral |
| { |
| $1.Val = $2; |
| $$ = list1($1); |
| } |
| | '(' embed ')' oliteral |
| { |
| $2.Val = $4; |
| $$ = list1($2); |
| Yyerror("cannot parenthesize embedded type"); |
| } |
| | '*' embed oliteral |
| { |
| $2.Right = Nod(OIND, $2.Right, nil); |
| $2.Val = $3; |
| $$ = list1($2); |
| } |
| | '(' '*' embed ')' oliteral |
| { |
| $3.Right = Nod(OIND, $3.Right, nil); |
| $3.Val = $5; |
| $$ = list1($3); |
| Yyerror("cannot parenthesize embedded type"); |
| } |
| | '*' '(' embed ')' oliteral |
| { |
| $3.Right = Nod(OIND, $3.Right, nil); |
| $3.Val = $5; |
| $$ = list1($3); |
| Yyerror("cannot parenthesize embedded type"); |
| } |
| |
| packname: |
| LNAME |
| { |
| var n *Node |
| |
| $$ = $1; |
| n = oldname($1); |
| if n.Pack != nil { |
| n.Pack.Used = 1; |
| } |
| } |
| | LNAME '.' sym |
| { |
| var pkg *Pkg |
| |
| if $1.Def == nil || $1.Def.Op != OPACK { |
| Yyerror("%v is not a package", Sconv($1, 0)); |
| pkg = localpkg; |
| } else { |
| $1.Def.Used = 1; |
| pkg = $1.Def.Pkg; |
| } |
| $$ = restrictlookup($3.Name, pkg); |
| } |
| |
| embed: |
| packname |
| { |
| $$ = embedded($1, localpkg); |
| } |
| |
| interfacedcl: |
| new_name indcl |
| { |
| $$ = Nod(ODCLFIELD, $1, $2); |
| ifacedcl($$); |
| } |
| | packname |
| { |
| $$ = Nod(ODCLFIELD, nil, oldname($1)); |
| } |
| | '(' packname ')' |
| { |
| $$ = Nod(ODCLFIELD, nil, oldname($2)); |
| Yyerror("cannot parenthesize embedded type"); |
| } |
| |
| indcl: |
| '(' oarg_type_list_ocomma ')' fnres |
| { |
| // without func keyword |
| $2 = checkarglist($2, 1); |
| $$ = Nod(OTFUNC, fakethis(), nil); |
| $$.List = $2; |
| $$.Rlist = $4; |
| } |
| |
| /* |
| * function arguments. |
| */ |
| arg_type: |
| name_or_type |
| | sym name_or_type |
| { |
| $$ = Nod(ONONAME, nil, nil); |
| $$.Sym = $1; |
| $$ = Nod(OKEY, $$, $2); |
| } |
| | sym dotdotdot |
| { |
| $$ = Nod(ONONAME, nil, nil); |
| $$.Sym = $1; |
| $$ = Nod(OKEY, $$, $2); |
| } |
| | dotdotdot |
| |
| arg_type_list: |
| arg_type |
| { |
| $$ = list1($1); |
| } |
| | arg_type_list ',' arg_type |
| { |
| $$ = list($1, $3); |
| } |
| |
| oarg_type_list_ocomma: |
| { |
| $$ = nil; |
| } |
| | arg_type_list ocomma |
| { |
| $$ = $1; |
| } |
| |
| /* |
| * statement |
| */ |
| stmt: |
| { |
| $$ = nil; |
| } |
| | compound_stmt |
| | common_dcl |
| { |
| $$ = liststmt($1); |
| } |
| | non_dcl_stmt |
| | error |
| { |
| $$ = nil; |
| } |
| |
| non_dcl_stmt: |
| simple_stmt |
| | for_stmt |
| | switch_stmt |
| | select_stmt |
| | if_stmt |
| | labelname ':' |
| { |
| $1 = Nod(OLABEL, $1, nil); |
| $1.Sym = dclstack; // context, for goto restrictions |
| } |
| stmt |
| { |
| var l *NodeList |
| |
| $1.Defn = $4; |
| l = list1($1); |
| if $4 != nil { |
| l = list(l, $4); |
| } |
| $$ = liststmt(l); |
| } |
| | LFALL |
| { |
| // will be converted to OFALL |
| $$ = Nod(OXFALL, nil, nil); |
| $$.Xoffset = int64(block); |
| } |
| | LBREAK onew_name |
| { |
| $$ = Nod(OBREAK, $2, nil); |
| } |
| | LCONTINUE onew_name |
| { |
| $$ = Nod(OCONTINUE, $2, nil); |
| } |
| | LGO pseudocall |
| { |
| $$ = Nod(OPROC, $2, nil); |
| } |
| | LDEFER pseudocall |
| { |
| $$ = Nod(ODEFER, $2, nil); |
| } |
| | LGOTO new_name |
| { |
| $$ = Nod(OGOTO, $2, nil); |
| $$.Sym = dclstack; // context, for goto restrictions |
| } |
| | LRETURN oexpr_list |
| { |
| $$ = Nod(ORETURN, nil, nil); |
| $$.List = $2; |
| if $$.List == nil && Curfn != nil { |
| var l *NodeList |
| |
| for l=Curfn.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); |
| } |
| } |
| } |
| } |
| |
| stmt_list: |
| stmt |
| { |
| $$ = nil; |
| if $1 != nil { |
| $$ = list1($1); |
| } |
| } |
| | stmt_list ';' stmt |
| { |
| $$ = $1; |
| if $3 != nil { |
| $$ = list($$, $3); |
| } |
| } |
| |
| new_name_list: |
| new_name |
| { |
| $$ = list1($1); |
| } |
| | new_name_list ',' new_name |
| { |
| $$ = list($1, $3); |
| } |
| |
| dcl_name_list: |
| dcl_name |
| { |
| $$ = list1($1); |
| } |
| | dcl_name_list ',' dcl_name |
| { |
| $$ = list($1, $3); |
| } |
| |
| expr_list: |
| expr |
| { |
| $$ = list1($1); |
| } |
| | expr_list ',' expr |
| { |
| $$ = list($1, $3); |
| } |
| |
| expr_or_type_list: |
| expr_or_type |
| { |
| $$ = list1($1); |
| } |
| | expr_or_type_list ',' expr_or_type |
| { |
| $$ = list($1, $3); |
| } |
| |
| /* |
| * list of combo of keyval and val |
| */ |
| keyval_list: |
| keyval |
| { |
| $$ = list1($1); |
| } |
| | bare_complitexpr |
| { |
| $$ = list1($1); |
| } |
| | keyval_list ',' keyval |
| { |
| $$ = list($1, $3); |
| } |
| | keyval_list ',' bare_complitexpr |
| { |
| $$ = list($1, $3); |
| } |
| |
| braced_keyval_list: |
| { |
| $$ = nil; |
| } |
| | keyval_list ocomma |
| { |
| $$ = $1; |
| } |
| |
| /* |
| * optional things |
| */ |
| osemi: |
| | ';' |
| |
| ocomma: |
| | ',' |
| |
| oexpr: |
| { |
| $$ = nil; |
| } |
| | expr |
| |
| oexpr_list: |
| { |
| $$ = nil; |
| } |
| | expr_list |
| |
| osimple_stmt: |
| { |
| $$ = nil; |
| } |
| | simple_stmt |
| |
| ohidden_funarg_list: |
| { |
| $$ = nil; |
| } |
| | hidden_funarg_list |
| |
| ohidden_structdcl_list: |
| { |
| $$ = nil; |
| } |
| | hidden_structdcl_list |
| |
| ohidden_interfacedcl_list: |
| { |
| $$ = nil; |
| } |
| | hidden_interfacedcl_list |
| |
| oliteral: |
| { |
| $$.Ctype = CTxxx; |
| } |
| | LLITERAL |
| |
| /* |
| * import syntax from package header |
| */ |
| hidden_import: |
| LIMPORT LNAME LLITERAL ';' |
| { |
| importimport($2, $3.U.Sval); |
| } |
| | LVAR hidden_pkg_importsym hidden_type ';' |
| { |
| importvar($2, $3); |
| } |
| | LCONST hidden_pkg_importsym '=' hidden_constant ';' |
| { |
| importconst($2, Types[TIDEAL], $4); |
| } |
| | LCONST hidden_pkg_importsym hidden_type '=' hidden_constant ';' |
| { |
| importconst($2, $3, $5); |
| } |
| | LTYPE hidden_pkgtype hidden_type ';' |
| { |
| importtype($2, $3); |
| } |
| | LFUNC hidden_fndcl fnbody ';' |
| { |
| if $2 == nil { |
| dclcontext = PEXTERN; // since we skip the funcbody below |
| break; |
| } |
| |
| $2.Inl = $3; |
| |
| funcbody($2); |
| importlist = list(importlist, $2); |
| |
| if Debug['E'] > 0 { |
| print("import [%v] func %lN \n", Zconv(importpkg.Path, 0), $2); |
| if Debug['m'] > 2 && $2.Inl != nil { |
| print("inl body:%+H\n", $2.Inl); |
| } |
| } |
| } |
| |
| hidden_pkg_importsym: |
| hidden_importsym |
| { |
| $$ = $1; |
| structpkg = $$.Pkg; |
| } |
| |
| hidden_pkgtype: |
| hidden_pkg_importsym |
| { |
| $$ = pkgtype($1); |
| importsym($1, OTYPE); |
| } |
| |
| /* |
| * importing types |
| */ |
| |
| hidden_type: |
| hidden_type_misc |
| | hidden_type_recv_chan |
| | hidden_type_func |
| |
| hidden_type_non_recv_chan: |
| hidden_type_misc |
| | hidden_type_func |
| |
| hidden_type_misc: |
| hidden_importsym |
| { |
| $$ = pkgtype($1); |
| } |
| | LNAME |
| { |
| // predefined name like uint8 |
| $1 = Pkglookup($1.Name, builtinpkg); |
| if $1.Def == nil || $1.Def.Op != OTYPE { |
| Yyerror("%s is not a type", $1.Name); |
| $$ = nil; |
| } else { |
| $$ = $1.Def.Type; |
| } |
| } |
| | '[' ']' hidden_type |
| { |
| $$ = aindex(nil, $3); |
| } |
| | '[' LLITERAL ']' hidden_type |
| { |
| $$ = aindex(nodlit($2), $4); |
| } |
| | LMAP '[' hidden_type ']' hidden_type |
| { |
| $$ = maptype($3, $5); |
| } |
| | LSTRUCT '{' ohidden_structdcl_list '}' |
| { |
| $$ = tostruct($3); |
| } |
| | LINTERFACE '{' ohidden_interfacedcl_list '}' |
| { |
| $$ = tointerface($3); |
| } |
| | '*' hidden_type |
| { |
| $$ = Ptrto($2); |
| } |
| | LCHAN hidden_type_non_recv_chan |
| { |
| $$ = typ(TCHAN); |
| $$.Type = $2; |
| $$.Chan = Cboth; |
| } |
| | LCHAN '(' hidden_type_recv_chan ')' |
| { |
| $$ = typ(TCHAN); |
| $$.Type = $3; |
| $$.Chan = Cboth; |
| } |
| | LCHAN LCOMM hidden_type |
| { |
| $$ = typ(TCHAN); |
| $$.Type = $3; |
| $$.Chan = Csend; |
| } |
| |
| hidden_type_recv_chan: |
| LCOMM LCHAN hidden_type |
| { |
| $$ = typ(TCHAN); |
| $$.Type = $3; |
| $$.Chan = Crecv; |
| } |
| |
| hidden_type_func: |
| LFUNC '(' ohidden_funarg_list ')' ohidden_funres |
| { |
| $$ = functype(nil, $3, $5); |
| } |
| |
| hidden_funarg: |
| sym hidden_type oliteral |
| { |
| $$ = Nod(ODCLFIELD, nil, typenod($2)); |
| if $1 != nil { |
| $$.Left = newname($1); |
| } |
| $$.Val = $3; |
| } |
| | sym LDDD hidden_type oliteral |
| { |
| var t *Type |
| |
| t = typ(TARRAY); |
| t.Bound = -1; |
| t.Type = $3; |
| |
| $$ = Nod(ODCLFIELD, nil, typenod(t)); |
| if $1 != nil { |
| $$.Left = newname($1); |
| } |
| $$.Isddd = 1; |
| $$.Val = $4; |
| } |
| |
| hidden_structdcl: |
| sym hidden_type oliteral |
| { |
| var s *Sym |
| var p *Pkg |
| |
| if $1 != nil && $1.Name != "?" { |
| $$ = Nod(ODCLFIELD, newname($1), typenod($2)); |
| $$.Val = $3; |
| } else { |
| s = $2.Sym; |
| if s == nil && Isptr[$2.Etype] != 0 { |
| s = $2.Type.Sym; |
| } |
| p = importpkg; |
| if $1 != nil { |
| p = $1.Pkg; |
| } |
| $$ = embedded(s, p); |
| $$.Right = typenod($2); |
| $$.Val = $3; |
| } |
| } |
| |
| hidden_interfacedcl: |
| sym '(' ohidden_funarg_list ')' ohidden_funres |
| { |
| $$ = Nod(ODCLFIELD, newname($1), typenod(functype(fakethis(), $3, $5))); |
| } |
| | hidden_type |
| { |
| $$ = Nod(ODCLFIELD, nil, typenod($1)); |
| } |
| |
| ohidden_funres: |
| { |
| $$ = nil; |
| } |
| | hidden_funres |
| |
| hidden_funres: |
| '(' ohidden_funarg_list ')' |
| { |
| $$ = $2; |
| } |
| | hidden_type |
| { |
| $$ = list1(Nod(ODCLFIELD, nil, typenod($1))); |
| } |
| |
| /* |
| * importing constants |
| */ |
| |
| hidden_literal: |
| LLITERAL |
| { |
| $$ = nodlit($1); |
| } |
| | '-' LLITERAL |
| { |
| $$ = nodlit($2); |
| switch($$.Val.Ctype){ |
| case CTINT, CTRUNE: |
| mpnegfix($$.Val.U.Xval); |
| break; |
| case CTFLT: |
| mpnegflt($$.Val.U.Fval); |
| break; |
| case CTCPLX: |
| mpnegflt(&$$.Val.U.Cval.Real); |
| mpnegflt(&$$.Val.U.Cval.Imag); |
| break; |
| default: |
| Yyerror("bad negated constant"); |
| } |
| } |
| | sym |
| { |
| $$ = oldname(Pkglookup($1.Name, builtinpkg)); |
| if $$.Op != OLITERAL { |
| Yyerror("bad constant %v", Sconv($$.Sym, 0)); |
| } |
| } |
| |
| hidden_constant: |
| hidden_literal |
| | '(' hidden_literal '+' hidden_literal ')' |
| { |
| if $2.Val.Ctype == CTRUNE && $4.Val.Ctype == CTINT { |
| $$ = $2; |
| mpaddfixfix($2.Val.U.Xval, $4.Val.U.Xval, 0); |
| break; |
| } |
| $4.Val.U.Cval.Real = $4.Val.U.Cval.Imag; |
| Mpmovecflt(&$4.Val.U.Cval.Imag, 0.0); |
| $$ = nodcplxlit($2.Val, $4.Val); |
| } |
| |
| hidden_import_list: |
| | hidden_import_list hidden_import |
| |
| hidden_funarg_list: |
| hidden_funarg |
| { |
| $$ = list1($1); |
| } |
| | hidden_funarg_list ',' hidden_funarg |
| { |
| $$ = list($1, $3); |
| } |
| |
| hidden_structdcl_list: |
| hidden_structdcl |
| { |
| $$ = list1($1); |
| } |
| | hidden_structdcl_list ';' hidden_structdcl |
| { |
| $$ = list($1, $3); |
| } |
| |
| hidden_interfacedcl_list: |
| hidden_interfacedcl |
| { |
| $$ = list1($1); |
| } |
| | hidden_interfacedcl_list ';' hidden_interfacedcl |
| { |
| $$ = list($1, $3); |
| } |
| |
| %% |
| |
| func fixlbrace(lbr int) { |
| // If the opening brace was an LBODY, |
| // set up for another one now that we're done. |
| // See comment in lex.C about loophack. |
| if lbr == LBODY { |
| loophack = 1 |
| } |
| } |
| |