/src/cmd/gc/go.y
Happy | 2089 lines | 1896 code | 193 blank | 0 comment | 0 complexity | 7fd0f82143240a549b03a98c1c70130c MD5 | raw file
Possible License(s): BSD-3-Clause
- // 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.
- */
- %{
- #include <u.h>
- #include <stdio.h> /* if we don't, bison will, and go.h re-#defines getc */
- #include <libc.h>
- #include "go.h"
- static void fixlbrace(int);
- %}
- %union {
- Node* node;
- NodeList* list;
- Type* type;
- Sym* sym;
- struct Val val;
- int i;
- }
- // |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
- %type <node> expr_or_type
- %type <node> fndcl hidden_fndcl fnliteral
- %type <node> for_body for_header for_stmt if_header if_stmt else 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 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 <type> hidden_type hidden_type_misc hidden_pkgtype
- %type <type> hidden_type_func
- %type <type> 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
- %error-verbose
- %%
- file:
- loadsys
- package
- imports
- xdcl_list
- {
- xtop = concat(xtop, $4);
- }
- package:
- %prec NotPackage
- {
- prevlineno = lineno;
- yyerror("package statement must be first");
- flusherrors();
- mkpackage("main");
- }
- | 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'])
- cannedimports("runtime.builtin", "package runtime\n\n$$\n\n");
- else
- cannedimports("runtime.builtin", runtimeimport);
- curio.importsafe = 1;
- }
- 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
- {
- Pkg *ipkg;
- Sym *my;
- Node *pack;
-
- ipkg = importpkg;
- my = importmyname;
- importpkg = nil;
- importmyname = S;
- if(my == nil)
- my = lookup(ipkg->name);
- pack = nod(OPACK, N, N);
- pack->sym = my;
- pack->pkg = ipkg;
- pack->lineno = $1;
- if(my->name[0] == '.') {
- importdot(ipkg, pack);
- break;
- }
- if(my->name[0] == '_' && my->name[1] == '\0')
- break;
- if(my->def) {
- lineno = $1;
- redeclare(my, "as imported package name");
- }
- my->def = pack;
- my->lastlineno = $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 = S;
- 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 == nil) {
- importpkg->name = $2->name;
- pkglookup($2->name, nil)->npkg++;
- } else if(strcmp(importpkg->name, $2->name) != 0)
- yyerror("conflicting names %s and %s for package \"%Z\"", importpkg->name, $2->name, importpkg->path);
- importpkg->direct = 1;
-
- if(safemode && !curio.importsafe)
- yyerror("cannot import unsafe package \"%Z\"", importpkg->path);
- }
- import_safety:
- | LNAME
- {
- if(strcmp($1->name, "safe") == 0)
- curio.importsafe = 1;
- }
- 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, N, $3);
- }
- constdcl1:
- constdcl
- | dcl_name_list ntype
- {
- $$ = constiter($1, $2, nil);
- }
- | dcl_name_list
- {
- $$ = constiter($1, N, 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;
- }
- | expr LASOP expr
- {
- $$ = nod(OASOP, $1, $3);
- $$->etype = $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, N, N);
- $$->list = $1;
- $$->rlist = $3;
- }
- | expr_list LCOLAS expr_list
- {
- if($3->n->op == OTYPESW) {
- $$ = nod(OTYPESW, N, $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 %N in type switch", $1->n);
- else
- $$->left = dclname($1->n->sym); // it's a colas, so must not re-use an oldname.
- break;
- }
- $$ = colas($1, $3, $2);
- }
- | expr LINC
- {
- $$ = nod(OASOP, $1, nodintconst(1));
- $$->etype = OADD;
- }
- | expr LDEC
- {
- $$ = nod(OASOP, $1, nodintconst(1));
- $$->etype = OSUB;
- }
- case:
- LCASE expr_or_type_list ':'
- {
- Node *n, *nn;
- // will be converted to OCASE
- // right will point to next case
- // done in casebody()
- markdcl();
- $$ = nod(OXCASE, N, N);
- $$->list = $2;
- if(typesw != N && typesw->right != N && (n=typesw->right->left) != N) {
- // 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 ':'
- {
- Node *n;
- // will be converted to OCASE
- // right will point to next case
- // done in casebody()
- markdcl();
- $$ = nod(OXCASE, N, N);
- if($2->next == nil)
- n = nod(OAS, $2->n, $4);
- else {
- n = nod(OAS2, N, N);
- 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, N, N);
- $$->list = list1(colas($2, list1($4), $3));
- }
- | LDEFAULT ':'
- {
- Node *n, *nn;
- markdcl();
- $$ = nod(OXCASE, N, N);
- if(typesw != N && typesw->right != N && (n=typesw->right->left) != N) {
- // 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 '}'
- {
- $$ = 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;
- }
- stmt_list
- {
- int last;
- // 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, N, $4);
- $$->list = $1;
- $$->etype = 0; // := flag
- }
- | expr_list LCOLAS LRANGE expr
- {
- $$ = nod(ORANGE, N, $4);
- $$->list = $1;
- $$->colas = 1;
- colasdefn($1, $$);
- }
- for_header:
- osimple_stmt ';' osimple_stmt ';' osimple_stmt
- {
- // init ; test ; incr
- if($5 != N && $5->colas != 0)
- yyerror("cannot declare in the for-increment");
- $$ = nod(OFOR, N, N);
- if($1 != N)
- $$->ninit = list1($1);
- $$->ntest = $3;
- $$->nincr = $5;
- }
- | osimple_stmt
- {
- // normal test
- $$ = nod(OFOR, N, N);
- $$->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, N, N);
- $$->ntest = $1;
- }
- | osimple_stmt ';' osimple_stmt
- {
- // init ; test
- $$ = nod(OIF, N, N);
- if($1 != N)
- $$->ninit = list1($1);
- $$->ntest = $3;
- }
- /* IF cond body (ELSE IF cond body)* (ELSE block)? */
- if_stmt:
- LIF
- {
- markdcl();
- }
- if_header
- {
- if($3->ntest == N)
- yyerror("missing condition in if statement");
- }
- loop_body
- {
- $3->nbody = $5;
- }
- else
- {
- popdcl();
- $$ = $3;
- if($7 != N)
- $$->nelse = list1($7);
- }
- else:
- {
- $$ = N;
- }
- | LELSE if_stmt
- {
- $$ = $2;
- }
- | LELSE compound_stmt
- {
- $$ = $2;
- }
- switch_stmt:
- LSWITCH
- {
- markdcl();
- }
- if_header
- {
- Node *n;
- n = $3->ntest;
- if(n != N && n->op != OTYPESW)
- n = N;
- typesw = nod(OXXX, typesw, n);
- }
- LBODY caseblock_list '}'
- {
- $$ = $3;
- $$->op = OSWITCH;
- $$->list = $6;
- typesw = typesw->left;
- popdcl();
- }
- select_stmt:
- LSELECT
- {
- typesw = nod(OXXX, typesw, N);
- }
- LBODY caseblock_list '}'
- {
- $$ = nod(OSELECT, N, N);
- $$->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, N);
- }
- | '&' uexpr
- {
- if($2->op == OCOMPLIT) {
- // Special case for &T{...}: turn into (*T){...}.
- $$ = $2;
- $$->right = nod(OIND, $$->right, N);
- $$->right->implicit = 1;
- } else {
- $$ = nod(OADDR, $2, N);
- }
- }
- | '+' uexpr
- {
- $$ = nod(OPLUS, $2, N);
- }
- | '-' uexpr
- {
- $$ = nod(OMINUS, $2, N);
- }
- | '!' uexpr
- {
- $$ = nod(ONOT, $2, N);
- }
- | '~' uexpr
- {
- yyerror("the bitwise complement operator is ^");
- $$ = nod(OCOM, $2, N);
- }
- | '^' uexpr
- {
- $$ = nod(OCOM, $2, N);
- }
- | LCOMM uexpr
- {
- $$ = nod(ORECV, $2, N);
- }
- /*
- * call-like statements that
- * can be preceded by 'defer' and 'go'
- */
- pseudocall:
- pexpr '(' ')'
- {
- $$ = nod(OCALL, $1, N);
- }
- | pexpr '(' expr_or_type_list ocomma ')'
- {
- $$ = nod(OCALL, $1, N);
- $$->list = $3;
- }
- | pexpr '(' expr_or_type_list LDDD ocomma ')'
- {
- $$ = nod(OCALL, $1, N);
- $$->list = $3;
- $$->isddd = 1;
- }
- pexpr_no_paren:
- LLITERAL
- {
- $$ = nodlit($1);
- }
- | name
- | pexpr '.' sym
- {
- if($1->op == OPACK) {
- Sym *s;
- 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, N, $1);
- }
- | pexpr '[' expr ']'
- {
- $$ = nod(OINDEX, $1, $3);
- }
- | pexpr '[' oexpr ':' oexpr ']'
- {
- $$ = nod(OSLICE, $1, nod(OKEY, $3, $5));
- }
- | pseudocall
- | convtype '(' expr ')'
- {
- // conversion
- $$ = nod(OCALL, $1, N);
- $$->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, N, N);
- }
- keyval:
- expr ':' complitexpr
- {
- $$ = nod(OKEY, $1, $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:
- case ONONAME:
- case OPACK:
- case OTYPE:
- case OLITERAL:
- $$ = nod(OPAREN, $$, N);
- }
- }
- 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 == S)
- $$ = N;
- else
- $$ = newname($1);
- }
- dcl_name:
- sym
- {
- $$ = dclname($1);
- }
- onew_name:
- {
- $$ = N;
- }
- | 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
- | '?'
- {
- $$ = S;
- }
- hidden_importsym:
- '@' LLITERAL '.' LNAME
- {
- if($2.u.sval->len == 0)
- $$ = pkglookup($4->name, importpkg);
- else
- $$ = pkglookup($4->name, mkpkg($2.u.sval));
- }
- name:
- sym %prec NotParen
- {
- $$ = oldname($1);
- if($$->pack != N)
- $$->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)), N);
- }
- | LDDD ntype
- {
- $$ = nod(ODDD, $2, N);
- }
- ntype:
- recvchantype
- | fntype
- | othertype
- | ptrtype
- | dotname
- | '(' ntype ')'
- {
- $$ = nod(OTPAREN, $2, N);
- }
- non_expr_type:
- recvchantype
- | fntype
- | othertype
- | '*' non_expr_type
- {
- $$ = nod(OIND, $2, N);
- }
- non_recvchantype:
- fntype
- | othertype
- | ptrtype
- | dotname
- | '(' ntype ')'
- {
- $$ = nod(OTPAREN, $2, N);
- }
- convtype:
- fntype
- | othertype
- comptype:
- othertype
- fnret_type:
- recvchantype
- | fntype
- | othertype
- | ptrtype
- | dotname
- dotname:
- name
- | name '.' sym
- {
- if($1->op == OPACK) {
- Sym *s;
- 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, N, N), $4);
- }
- | LCHAN non_recvchantype
- {
- $$ = nod(OTCHAN, $2, N);
- $$->etype = Cboth;
- }
- | LCHAN LCOMM ntype
- {
- $$ = nod(OTCHAN, $3, N);
- $$->etype = Csend;
- }
- | LMAP '[' ntype ']' ntype
- {
- $$ = nod(OTMAP, $3, $5);
- }
- | structtype
- | interfacetype
- ptrtype:
- '*' ntype
- {
- $$ = nod(OIND, $2, N);
- }
- recvchantype:
- LCOMM LCHAN ntype
- {
- $$ = nod(OTCHAN, $3, N);
- $$->etype = Crecv;
- }
- structtype:
- LSTRUCT lbrace structdcl_list osemi '}'
- {
- $$ = nod(OTSTRUCT, N, N);
- $$->list = $3;
- fixlbrace($2);
- }
- | LSTRUCT lbrace '}'
- {
- $$ = nod(OTSTRUCT, N, N);
- fixlbrace($2);
- }
- interfacetype:
- LINTERFACE lbrace interfacedcl_list osemi '}'
- {
- $$ = nod(OTINTER, N, N);
- $$->list = $3;
- fixlbrace($2);
- }
- | LINTERFACE lbrace '}'
- {
- $$ = nod(OTINTER, N, N);
- fixlbrace($2);
- }
- /*
- * function stuff
- * all in one place to show how crappy it all is
- */
- xfndcl:
- LFUNC fndcl fnbody
- {
- $$ = $2;
- if($$ == N)
- break;
- $$->nbody = $3;
- $$->endlineno = lineno;
- funcbody($$);
- }
- fndcl:
- sym '(' oarg_type_list_ocomma ')' fnres
- {
- Node *t;
- $$ = N;
- $3 = checkarglist($3, 1);
- if(strcmp($1->name, "init") == 0) {
- $1 = renameinit();
- if($3 != nil || $5 != nil)
- yyerror("func init must have no arguments and no return values");
- }
- if(strcmp(localpkg->name, "main") == 0 && strcmp($1->name, "main") == 0) {
- if($3 != nil || $5 != nil)
- yyerror("func main must have no arguments and no return values");
- }
- t = nod(OTFUNC, N, N);
- t->list = $3;
- t->rlist = $5;
- $$ = nod(ODCLFUNC, N, N);
- $$->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
- {
- Node *rcvr, *t;
- $$ = N;
- $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;
- }
- if(rcvr->right->op == OTPAREN || (rcvr->right->op == OIND && rcvr->right->left->op == OTPAREN))
- yyerror("cannot parenthesize receiver type");
- t = nod(OTFUNC, rcvr, N);
- t->list = $6;
- t->rlist = $8;
- $$ = nod(ODCLFUNC, N, N);
- $$->shortname = newname($4);
- $$->nname = methodname1($$->shortname, rcvr->right);
- $$->nname->defn = $$;
- $$->nname->ntype = t;
- declare($$->nname, PFUNC);
- funchdr($$);
- }
- hidden_fndcl:
- hidden_pkg_importsym '(' ohidden_funarg_list ')' ohidden_funres
- {
- Sym *s;
- Type *t;
- $$ = N;
- s = $1;
- t = functype(N, $3, $5);
- importsym(s, ONAME);
- if(s->def != N && s->def->op == ONAME) {
- if(eqtype(t, s->def->type))
- break;
- yyerror("inconsistent definition for func %S during import\n\t%T\n\t%T", s, s->def->type, t);
- }
- $$ = 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, 0);
- 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, N, N);
- $$->list = $3;
- $$->rlist = $5;
- }
- fnbody:
- {
- $$ = nil;
- }
- | '{' stmt_list '}'
- {
- $$ = $2;
- if($$ == nil)
- $$ = list1(nod(OEMPTY, N, N));
- }
- fnres:
- %prec NotParen
- {
- $$ = nil;
- }
- | fnret_type
- {
- $$ = list1(nod(ODCLFIELD, N, $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();
- }
- 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
- {
- NodeList *l;
- Node *n;
- l = $1;
- if(l != nil && l->next == nil && l->n == nil) {
- // ? symbol, during import
- n = $2;
- if(n->op == OIND)
- n = n->left;
- n = embedded(n->sym);
- n->right = $2;
- n->val = $3;
- $$ = list1(n);
- break;
- }
- for(l=$1; l; 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, N);
- $2->val = $3;
- $$ = list1($2);
- }
- | '(' '*' embed ')' oliteral
- {
- $3->right = nod(OIND, $3->right, N);
- $3->val = $5;
- $$ = list1($3);
- yyerror("cannot parenthesize embedded type");
- }
- | '*' '(' embed ')' oliteral
- {
- $3->right = nod(OIND, $3->right, N);
- $3->val = $5;
- $$ = list1($3);
- yyerror("cannot parenthesize embedded type");
- }
- packname:
- LNAME
- {
- Node *n;
- $$ = $1;
- n = oldname($1);
- if(n->pack != N)
- n->pack->used = 1;
- }
- | LNAME '.' sym
- {
- Pkg *pkg;
- if($1->def == N || $1->def->op != OPACK) {
- yyerror("%S is not a package", $1);
- pkg = localpkg;
- } else {
- $1->def->used = 1;
- pkg = $1->def->pkg;
- }
- $$ = restrictlookup($3->name, pkg);
- }
- embed:
- packname
- {
- $$ = embedded($1);
- }
- interfacedcl:
- new_name indcl
- {
- $$ = nod(ODCLFIELD, $1, $2);
- ifacedcl($$);
- }
- | packname
- {
- $$ = nod(ODCLFIELD, N, oldname($1));
- }
- | '(' packname ')'
- {
- $$ = nod(ODCLFIELD, N, oldname($2));
- yyerror("cannot parenthesize embedded type");
- }
- indcl:
- '(' oarg_type_list_ocomma ')' fnres
- {
- // without func keyword
- $2 = checkarglist($2, 1);
- $$ = nod(OTFUNC, fakethis(), N);
- $$->list = $2;
- $$->rlist = $4;
- }
- /*
- * function arguments.
- */
- arg_type:
- name_or_type
- | sym name_or_type
- {
- $$ = nod(ONONAME, N, N);
- $$->sym = $1;
- $$ = nod(OKEY, $$, $2);
- }
- | sym dotdotdot
- {
- $$ = nod(ONONAME, N, N);
- $$->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:
- {
- $$ = N;
- }
- | compound_stmt
- | common_dcl
- {
- $$ = liststmt($1);
- }
- | non_dcl_stmt
- | error
- {
- $$ = N;
- }
- non_dcl_stmt:
- simple_stmt
- | for_stmt
- | switch_stmt
- | select_stmt
- | if_stmt
- | labelname ':'
- {
- $1 = nod(OLABEL, $1, N);
- $1->sym = dclstack; // context, for goto restrictions
- }
- stmt
- {
- NodeList *l;
- $1->defn = $4;
- l = list1($1);
- if($4)
- l = list(l, $4);
- $$ = liststmt(l);
- }
- | LFALL
- {
- // will be converted to OFALL
- $$ = nod(OXFALL, N, N);
- }
- | LBREAK onew_name
- {
- $$ = nod(OBREAK, $2, N);
- }
- | LCONTINUE onew_name
- {
- $$ = nod(OCONTINUE, $2, N);
- }
- | LGO pseudocall
- {
- $$ = nod(OPROC, $2, N);
- }
- | LDEFER pseudocall
- {
- $$ = nod(ODEFER, $2, N);
- }
- | LGOTO new_name
- {
- $$ = nod(OGOTO, $2, N);
- $$->sym = dclstack; // context, for goto restrictions
- }
- | LRETURN oexpr_list
- {
- $$ = nod(ORETURN, N, N);
- $$->list = $2;
- if($$->list == nil && curfn != N) {
- NodeList *l;
- for(l=curfn->dcl; l; 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 != N)
- $$ = list1($1);
- }
- | stmt_list ';' stmt
- {
- $$ = $1;
- if($3 != N)
- $$ = 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);
- }
- | complitexpr
- {
- $$ = list1($1);
- }
- | keyval_list ',' keyval
- {
- $$ = list($1, $3);
- }
- | keyval_list ',' complitexpr
- {
- $$ = list($1, $3);
- }
- braced_keyval_list:
- {
- $$ = nil;
- }
- | keyval_list ocomma
- {
- $$ = $1;
- }
- /*
- * optional things
- */
- osemi:
- | ';'
- ocomma:
- | ','
- oexpr:
- {
- $$ = N;
- }
- | expr
- oexpr_list:
- {
- $$ = nil;
- }
- | expr_list
- osimple_stmt:
- {
- $$ = N;
- }
- | 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 == N)
- break;
- $2->inl = $3;
- funcbody($2);
- importlist = list(importlist, $2);
- if(debug['E']) {
- print("import [%Z] func %lN \n", importpkg->path, $2);
- if(debug['l'] > 2 && $2->inl)
- 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 == N || $1->def->op != OTYPE) {
- yyerror("%s is not a type", $1->name);
- $$ = T;
- } else
- $$ = $1->def->type;
- }
- | '[' ']' hidden_type
- {
- $$ = aindex(N, $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, N, typenod($2));
- if($1)
- $$->left = newname($1);
- $$->val = $3;
- }
- | sym LDDD hidden_type oliteral
- {
- Type *t;
-
- t = typ(TARRAY);
- t->bound = -1;
- t->type = $3;
- $$ = nod(ODCLFIELD, N, typenod(t));
- if($1)
- $$->left = newname($1);
- $$->isddd = 1;
- $$->val = $4;
- }
- hidden_structdcl:
- sym hidden_type oliteral
- {
- Sym *s;
- if($1 != S) {
- $$ = nod(ODCLFIELD, newname($1), typenod($2));
- $$->val = $3;
- } else {
- s = $2->sym;
- if(s == S && isptr[$2->etype])
- s = $2->type->sym;
- $$ = embedded(s);
- $$->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, N, typenod($1));
- }
- ohidden_funres:
- {
- $$ = nil;
- }
- | hidden_funres
- hidden_funres:
- '(' ohidden_funarg_list ')'
- {
- $$ = $2;
- }
- | hidden_type
- {
- $$ = list1(nod(ODCLFIELD, N, typenod($1)));
- }
- /*
- * importing constants
- */
- hidden_literal:
- LLITERAL
- {
- $$ = nodlit($1);
- }
- | '-' LLITERAL
- {
- $$ = nodlit($2);
- switch($$->val.ctype){
- case CTINT:
- case CTRUNE:
- mpnegfix($$->val.u.xval);
- break;
- case CTFLT:
- mpnegflt($$->val.u.fval);
- break;
- default:
- yyerror("bad negated constant");
- }
- }
- | sym
- {
- $$ = oldname(pkglookup($1->name, builtinpkg));
- if($$->op != OLITERAL)
- yyerror("bad constant %S", $$->sym);
- }
- 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;
- }
- $$ = 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);
- }
- %%
- static void
- fixlbrace(int lbr)
- {
- // 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;
- }