mirror of
https://github.com/golang/go
synced 2024-11-20 11:24:47 -07:00
3d40062c68
This is an experiment in static analysis of Go programs to understand which struct fields a program might use. It is not part of the Go language specification, it must be enabled explicitly when building the toolchain, and it may be removed at any time. After building the toolchain with GOEXPERIMENT=fieldtrack, a specific field can be marked for tracking by including `go:"track"` in the field tag: package pkg type T struct { F int `go:"track"` G int // untracked } To simplify usage, only named struct types can have tracked fields, and only exported fields can be tracked. The implementation works by making each function begin with a sequence of no-op USEFIELD instructions declaring which tracked fields are accessed by a specific function. After the linker's dead code elimination removes unused functions, the fields referred to by the remaining USEFIELD instructions are the ones reported as used by the binary. The -k option to the linker specifies the fully qualified symbol name (such as my/pkg.list) of a string variable that should be initialized with the field tracking information for the program. The field tracking string is a sequence of lines, each terminated by a \n and describing a single tracked field referred to by the program. Each line is made up of one or more tab-separated fields. The first field is the name of the tracked field, fully qualified, as in "my/pkg.T.F". Subsequent fields give a shortest path of reverse references from that field to a global variable or function, corresponding to one way in which the program might reach that field. A common source of false positives in field tracking is types with large method sets, because a reference to the type descriptor carries with it references to all methods. To address this problem, the CL also introduces a comment annotation //go:nointerface that marks an upcoming method declaration as unavailable for use in satisfying interfaces, both statically and dynamically. Such a method is also invisible to package reflect. Again, all of this is disabled by default. It only turns on if you have GOEXPERIMENT=fieldtrack set during make.bash. R=iant, ken CC=golang-dev https://golang.org/cl/6749064
2105 lines
33 KiB
Plaintext
2105 lines
33 KiB
Plaintext
// 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
|
|
{
|
|
Pkg *p;
|
|
|
|
if($2.u.sval->len == 0)
|
|
p = importpkg;
|
|
else {
|
|
if(isbadimport($2.u.sval))
|
|
errorexit();
|
|
p = mkpkg($2.u.sval);
|
|
}
|
|
$$ = pkglookup($4->name, p);
|
|
}
|
|
|
|
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;
|
|
$$->nname->nointerface = nointerface;
|
|
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)) {
|
|
dclcontext = PDISCARD; // since we skip funchdr below
|
|
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, nointerface);
|
|
nointerface = 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();
|
|
nointerface = 0;
|
|
}
|
|
|
|
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) {
|
|
dclcontext = PEXTERN; // since we skip the funcbody below
|
|
break;
|
|
}
|
|
|
|
$2->inl = $3;
|
|
|
|
funcbody($2);
|
|
importlist = list(importlist, $2);
|
|
|
|
if(debug['E']) {
|
|
print("import [%Z] func %lN \n", importpkg->path, $2);
|
|
if(debug['m'] > 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;
|
|
}
|
|
$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);
|
|
}
|
|
|
|
%%
|
|
|
|
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;
|
|
}
|
|
|