blob: 91f939219ccc78b459c9acf19d05066ca8bfa897 [file] [log] [blame]
// 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
%token <sym> LBREAK LCASE LCHAN LCOLAS 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_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);
}
| 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)));
}
| 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;
}