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