package:
    %prec NotPackage
    {
        prevlineno = lineno;
        Yyerror("package statement must be first");
        errorexit();
    }


/*
 * 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;
    }


import_stmt:
|   import_here import_package import_there { }
|   import_here import_there


|   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.
        *)
    }

import_there:
    hidden_import_list '$' '$'
    {
        resumecheckwidth();
        unimportfile();
    }

import_package:
    LPACKAGE LNAME import_safety LSEMICOLON
    {
    }

import_safety:
|   LNAME
    {
    }


xdcl:
|   non_dcl_stmt
    {
        Yyerror("non-declaration statement outside function body");
        $$ = nil;
    }
|   error
    {
        $$ = nil;
    }

start_complit:
    {
        // composite expression.
        // make node early so we get the right line number.
        $$ = Nod(OCOMPLIT, nil, nil);
    }


sym:
|   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);
    }


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 = $$;
    }


fnliteral:
|   fnlitdcl error
    {
        $$ = closurebody(nil);
    }

stmt:
|   error
    {
        $$ = nil;
    }

ohidden_funarg_list:
    {
        $$ = nil;
    }
|   hidden_funarg_list

ohidden_structdcl_list:
    {
        $$ = nil;
    }
|   hidden_structdcl_list

ohidden_interfacedcl_list:
    {
        $$ = nil;
    }
|   hidden_interfacedcl_list



/*
 * 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 = append(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);
    }
