// 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 (
	"fmt"
	"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

%error loadsys package LIMPORT '(' LLITERAL import_package import_there ',':
	"unexpected comma during import block"

%error loadsys package LIMPORT LNAME ';':
	"missing import path; require quoted string"

%error loadsys package imports LFUNC LNAME '(' ')' '{' LIF if_header ';':
	"missing { after if clause"

%error loadsys package imports LFUNC LNAME '(' ')' '{' LSWITCH if_header ';':
	"missing { after switch clause"

%error loadsys package imports LFUNC LNAME '(' ')' '{' LFOR for_header ';':
	"missing { after for clause"

%error loadsys package imports LFUNC LNAME '(' ')' '{' LFOR ';' LBODY:
	"missing { after for clause"

%error loadsys package imports LFUNC LNAME '(' ')' ';' '{':
	"unexpected semicolon or newline before {"

%error loadsys package imports LTYPE LNAME ';':
	"unexpected semicolon or newline in type declaration"

%error loadsys package imports LCHAN '}':
	"unexpected } in channel type"

%error loadsys package imports LCHAN ')':
	"unexpected ) in channel type"

%error loadsys package imports LCHAN ',':
	"unexpected comma in channel type"

%error loadsys package imports LFUNC LNAME '(' ')' '{' if_stmt ';' LELSE:
	"unexpected semicolon or newline before else"

%error loadsys package imports LTYPE LNAME LINTERFACE '{' LNAME ',' LNAME:
	"name list not allowed in interface type"

%error loadsys package imports LFUNC LNAME '(' ')' '{' LFOR LVAR LNAME '=' LNAME:
	"var declaration not allowed in for initializer"

%error loadsys package imports LVAR LNAME '[' ']' LNAME '{':
	"unexpected { at end of statement"

%error loadsys package imports LFUNC LNAME '(' ')' '{' LVAR LNAME '[' ']' LNAME '{':
	"unexpected { at end of statement"

%error loadsys package imports LFUNC LNAME '(' ')' '{' LDEFER LNAME ';':
	"argument to go/defer must be function call"

%error loadsys package imports LVAR LNAME '=' LNAME '{' LNAME ';':
	"need trailing comma before newline in composite literal"

%error loadsys package imports LVAR LNAME '=' comptype '{' LNAME ';':
	"need trailing comma before newline in composite literal"

%error loadsys package imports LFUNC LNAME '(' ')' '{' LFUNC LNAME:
	"nested func not allowed"

%error loadsys package imports LFUNC LNAME '(' ')' '{' LIF if_header loop_body LELSE ';':
	"else must be followed by if or statement block"

%%
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.Name.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;
			numImport[$2.Name]++
		} else if importpkg.Name != $2.Name {
			Yyerror("conflicting names %s and %s for package %q", importpkg.Name, $2.Name, importpkg.Path);
		}
		importpkg.Direct = 1;
		importpkg.Safe = curio.importsafe

		if safemode != 0 && !curio.importsafe {
			Yyerror("cannot import unsafe package %q", importpkg.Path);
		}
	}

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, true);
	}

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 = true;
			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", $1.N);
			} 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 = true;
		$$.Etype = OADD;
	}
|	expr LDEC
	{
		$$ = Nod(OASOP, $1, Nodintconst(1));
		$$.Implicit = true;
		$$.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);
				$$.Rlist = list1(nn);
	
				// keep track of the instances for reporting unused
				nn.Name.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);
				$$.Rlist = list1(nn);
	
				// keep track of the instances for reporting unused
				nn.Name.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 = true;
		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 {
			Yyerror("cannot declare in the for-increment");
		}
		$$ = Nod(OFOR, nil, nil);
		if $1 != nil {
			$$.Ninit = list1($1);
		}
		$$.Left = $3;
		$$.Right = $5;
	}
|	osimple_stmt
	{
		// normal test
		$$ = Nod(OFOR, nil, nil);
		$$.Left = $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);
		$$.Left = $1;
	}
|	osimple_stmt ';' osimple_stmt
	{
		// init ; test
		$$ = Nod(OIF, nil, nil);
		if $1 != nil {
			$$.Ninit = list1($1);
		}
		$$.Left = $3;
	}

/* IF cond body (ELSE IF cond body)* (ELSE block)? */
if_stmt:
	LIF
	{
		markdcl();
	}
	if_header
	{
		if $3.Left == 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.Rlist = list1(nn.N);
			n = nn.N;
		}
	}

elseif:
	LELSE LIF 
	{
		markdcl();
	}
	if_header loop_body
	{
		if $4.Left == 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.Left;
		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 = true;
		} 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 = true;
	}

pexpr_no_paren:
	LLITERAL
	{
		$$ = nodlit($1);
	}
|	name
|	pexpr '.' sym
	{
		if $1.Op == OPACK {
			var s *Sym
			s = restrictlookup($3.Name, $1.Name.Pkg);
			$1.Used = true;
			$$ = 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:
	complitexpr ':' 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 = true;
		}
	}
|	'{' 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.(string) == "" {
			p = importpkg;
		} else {
			if isbadimport($2.U.(string)) {
				errorexit();
			}
			p = mkpkg($2.U.(string));
		}
		$$ = Pkglookup($4.Name, p);
	}
|	'@' LLITERAL '.' '?'
	{
		var p *Pkg

		if $2.U.(string) == "" {
			p = importpkg;
		} else {
			if isbadimport($2.U.(string)) {
				errorexit();
			}
			p = mkpkg($2.U.(string));
		}
		$$ = Pkglookup("?", p);
	}

name:
	sym	%prec NotParen
	{
		$$ = oldname($1);
		if $$.Name != nil && $$.Name.Pack != nil {
			$$.Name.Pack.Used = true;
		}
	}

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.Name.Pkg);
			$1.Used = true;
			$$ = 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;
		$$.Func.Endlineno = lineno;
		$$.Noescape = noescape;
		$$.Func.Nosplit = nosplit;
		$$.Func.Nowritebarrier = nowritebarrier;
		$$.Func.Systemstack = systemstack;
		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);
		$$.Func.Nname = newfuncname($1);
		$$.Func.Nname.Name.Defn = $$;
		$$.Func.Nname.Name.Param.Ntype = t;		// TODO: check if nname already has an ntype
		declare($$.Func.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);
		$$.Func.Shortname = newfuncname($4);
		$$.Func.Nname = methodname1($$.Func.Shortname, rcvr.Right);
		$$.Func.Nname.Name.Defn = $$;
		$$.Func.Nname.Name.Param.Ntype = t;
		$$.Func.Nname.Nointerface = nointerface;
		declare($$.Func.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", s, s.Def.Type, t);
		}

		$$ = newfuncname(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
		systemstack = 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 || l.N.Sym.Name == "?" {
			// ? symbol, during import (list1(nil) == nil)
			n = $2;
			if n.Op == OIND {
				n = n.Left;
			}
			n = embedded(n.Sym, importpkg);
			n.Right = $2;
			n.SetVal($3)
			$$ = list1(n);
			break;
		}

		for l=$1; l != nil; l=l.Next {
			l.N = Nod(ODCLFIELD, l.N, $2);
			l.N.SetVal($3)
		}
	}
|	embed oliteral
	{
		$1.SetVal($2)
		$$ = list1($1);
	}
|	'(' embed ')' oliteral
	{
		$2.SetVal($4)
		$$ = list1($2);
		Yyerror("cannot parenthesize embedded type");
	}
|	'*' embed oliteral
	{
		$2.Right = Nod(OIND, $2.Right, nil);
		$2.SetVal($3)
		$$ = list1($2);
	}
|	'(' '*' embed ')' oliteral
	{
		$3.Right = Nod(OIND, $3.Right, nil);
		$3.SetVal($5)
		$$ = list1($3);
		Yyerror("cannot parenthesize embedded type");
	}
|	'*' '(' embed ')' oliteral
	{
		$3.Right = Nod(OIND, $3.Right, nil);
		$3.SetVal($5)
		$$ = list1($3);
		Yyerror("cannot parenthesize embedded type");
	}

packname:
	LNAME
	{
		var n *Node

		$$ = $1;
		n = oldname($1);
		if n.Name != nil && n.Name.Pack != nil {
			n.Name.Pack.Used = true;
		}
	}
|	LNAME '.' sym
	{
		var pkg *Pkg

		if $1.Def == nil || $1.Def.Op != OPACK {
			Yyerror("%v is not a package", $1);
			pkg = localpkg;
		} else {
			$1.Def.Used = true;
			pkg = $1.Def.Name.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.Name.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.Func.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:
	{
		$$.U = nil
	}
|	LLITERAL

/*
 * import syntax from package header
 */
hidden_import:
	LIMPORT LNAME LLITERAL ';'
	{
		importimport($2, $3.U.(string));
	}
|	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.Func.Inl = $3;

		funcbody($2);
		importlist = list(importlist, $2);

		if Debug['E'] > 0 {
			fmt.Printf("import [%q] func %v \n", importpkg.Path, $2)
			if Debug['m'] > 2 && $2.Func.Inl != nil {
				fmt.Printf("inl body:%v\n", $2.Func.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);
		}
		$$.SetVal($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 = true;
		$$.SetVal($4)
	}

hidden_structdcl:
	sym hidden_type oliteral
	{
		var s *Sym
		var p *Pkg

		if $1 != nil && $1.Name != "?" {
			$$ = Nod(ODCLFIELD, newname($1), typenod($2));
			$$.SetVal($3)
		} else {
			s = $2.Sym;
			if s == nil && Isptr[$2.Etype] {
				s = $2.Type.Sym;
			}
			p = importpkg;
			if $1 != nil {
				p = $1.Pkg;
			}
			$$ = embedded(s, p);
			$$.Right = typenod($2);
			$$.SetVal($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.(*Mpint));
			break;
		case CTFLT:
			mpnegflt($$.Val().U.(*Mpflt));
			break;
		case CTCPLX:
			mpnegflt(&$$.Val().U.(*Mpcplx).Real);
			mpnegflt(&$$.Val().U.(*Mpcplx).Imag);
			break;
		default:
			Yyerror("bad negated constant");
		}
	}
|	sym
	{
		$$ = oldname(Pkglookup($1.Name, builtinpkg));
		if $$.Op != OLITERAL {
			Yyerror("bad constant %v", $$.Sym);
		}
	}

hidden_constant:
	hidden_literal
|	'(' hidden_literal '+' hidden_literal ')'
	{
		if $2.Val().Ctype() == CTRUNE && $4.Val().Ctype() == CTINT {
			$$ = $2;
			mpaddfixfix($2.Val().U.(*Mpint), $4.Val().U.(*Mpint), 0);
			break;
		}
		$4.Val().U.(*Mpcplx).Real = $4.Val().U.(*Mpcplx).Imag;
		Mpmovecflt(&$4.Val().U.(*Mpcplx).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
	}
}
