%{
#include "symbol.h"
#include "code.h"
#define code(c1)	Ft_code(c1)
#define code2(c1, c2) code(c1); code(c2)
#define code3(c1, c2, c3) code(c1); code(c2); code(c3)

#undef DEBUG

#ifdef DEBUG
#include <stdio.h>
#define PNUM(a)    fprintf(stderr, "Parse: at address %d\n", a);
#define ARGNO(a)   fprintf(stderr, "Parse: argument no %d\n", a);
#define CODE(a)    fprintf(stderr, "Parse: %s\n", a)
#define CODE2(a, b)    CODE(a); CODE(b);
#define CODE3(a, b, c) CODE(a); CODE(b); CODE(c)
#else
#define PNUM(a)
#define ARGNO(a)
#define CODE(a)
#define CODE2(a, b)
#define CODE3(a, b, c)
#endif
int yyerror(char *);
int yylex(void);
extern int Ft_autosymremove(int);
extern int Ft_more_input(int, char *);
extern int Ft_Inbrace;
extern int Ft_Indef;
extern int Ft_Inproto;
extern int Ft_Inauto;
static char *interprompt = 0;
%}
%union {
    Symbol *sym;
    Inst *inst;
    int narg;
    double val;
}
%token  <sym>   VAR BLTINVAR
%token  <sym>   CONST BLTINCONST
%token  <sym>   STRVAR BLTINSTRVAR
%token  <sym>   STRCONST BLTINSTRCONST
%token  <sym>   STRING
%token  <sym>	UNDEFVEC UNDEFVAR UNDEFSTRVAR
%token  <sym>   VEC AUTOVEC
%token  <sym>   PARAM
%token  <sym>   PRINT WHILE IF ELSE
%token  <sym>   CONTINUE BREAK FOR RETURN FUNC PROC
%token  <sym>	FUNCSYM PROCSYM EFUNCSYM EPROCSYM
%token  <sym>   AUTO ARG AUTOSTRVAR
%token  <sym>   BLTIN0 BLTIN1 BLTIN2 STRBLTIN2 BLTIN1VEC
%token  <sym>   BLTIN0STR BLTIN1STR BLTIN2STR
%token  <narg>  VARARG STRVARARG VECARG PARARG
%token  <val>   NUMBER
%type   <inst>  stmtline dontprint value strvalue
%type   <inst>  expr exprl exprlist stmt varincr prlist
%type   <inst>  asgn asgnl asgnlist forcond strasgn
%type   <inst>  parloop vecloop vecasgn parasgn
%type   <inst>  cond while if begin end for string
%type   <inst>  argstr argvec argexpr opbrace nicevec argpar
%type   <sym>   procname element
%type   <narg>  autol autolist autolines
%type   <narg>  arglist argelement
%right  '=' DIVASS MULASS ADDASS SUBASS
%left   OR
%left   AND
%left   GT GE LT LE EQ NE
%left   '+' '-'
%left   '*' '/' '%'
%left   UNARYMINUS NOT
%right  '^'
%right  INCR DECR
%%
list:
    | list '\n'
    | list defn '\n'
    | list stmtline '\n'    {
            CODE("STOP");
            code(STOP); Ft_execute(Ft_Progbase); return(1); }
    | list error '\n' { yyerrok; }
    ;
stmtline: begin stmt
    |    stmtline ';' stmt
    ;
asgn:    VAR '=' expr     {
            CODE3("varpush", $1->name, "assign");
            code3(Ft_varpush, (Inst)$1, Ft_assign); $$=$3; }
    |    VAR ADDASS expr    {
            CODE3("varpush", $1->name, "addassign");
            code3(Ft_varpush, (Inst)$1, Ft_addassign); $$=$3; }
    |    VAR SUBASS expr    {
            CODE3("varpush", $1->name, "subassign");
            code3(Ft_varpush, (Inst)$1, Ft_subassign); $$=$3; }
    |    VAR MULASS expr    {
            CODE3("varpush", $1->name, "mulassign");
            code3(Ft_varpush, (Inst)$1, Ft_mulassign); $$=$3; }
    |    VAR DIVASS expr    {
            CODE3("varpush", $1->name, "divassign");
            code3(Ft_varpush, (Inst)$1, Ft_divassign); $$=$3; }
    |    VARARG '=' expr     {
            CODE("argassign"); ARGNO($1);
            code2(Ft_argassign, (Inst)$1); $$=$3; }
    |    VARARG ADDASS expr    {
            CODE("argaddassign"); ARGNO($1);
            code2(Ft_argaddassign, (Inst)$1); $$=$3; }
    |    VARARG SUBASS expr    {
            CODE("argsubassign"); ARGNO($1);
            code2(Ft_argsubassign, (Inst)$1); $$=$3; }
    |    VARARG MULASS expr    {
            CODE("argmulassign"); ARGNO($1);
            code2(Ft_argmulassign, (Inst)$1); $$=$3; }
    |    VARARG DIVASS expr    {
            CODE("argdivassign"); ARGNO($1);
            code2(Ft_argdivassign, (Inst)$1); $$=$3; }
    |    argelement '=' expr     {
            CODE("argvarpush"); ARGNO($1); CODE("eassign");
            code3(Ft_argvarpush, (Inst)$1, Ft_eassign); $$=$3; }
    |    argelement ADDASS expr    {
            CODE("argvarpush"); ARGNO($1); CODE("eaddassign");
            code3(Ft_argvarpush, (Inst)$1, Ft_eaddassign); $$=$3; }
    |    argelement SUBASS expr    {
            CODE("argvarpush"); ARGNO($1); CODE("esubassign");
            code3(Ft_argvarpush, (Inst)$1, Ft_esubassign); $$=$3; }
    |    argelement MULASS expr    {
            CODE("argvarpush"); ARGNO($1); CODE("emulassign");
            code3(Ft_argvarpush, (Inst)$1, Ft_emulassign); $$=$3; }
    |    argelement DIVASS expr    {
            CODE("argvarpush"); ARGNO($1); CODE("edivassign");
            code3(Ft_argvarpush, (Inst)$1, Ft_edivassign); $$=$3; }
    |    element '=' expr     {
            CODE3("varpush", $1->name, "eassign");
            code3(Ft_varpush, (Inst)$1, Ft_eassign); $$=$3; }
    |    element ADDASS expr    {
            CODE3("varpush", $1->name, "eaddassign");
            code3(Ft_varpush, (Inst)$1, Ft_eaddassign); $$=$3; }
    |    element SUBASS expr    {
            CODE3("varpush", $1->name, "esubassign");
            code3(Ft_varpush, (Inst)$1, Ft_esubassign); $$=$3; }
    |    element MULASS expr    {
            CODE3("varpush", $1->name, "emulassign");
            code3(Ft_varpush, (Inst)$1, Ft_emulassign); $$=$3; }
    |    element DIVASS expr    {
            CODE3("varpush", $1->name, "edivassign");
            code3(Ft_varpush, (Inst)$1, Ft_edivassign); $$=$3; }
    ;
strasgn: STRVAR '=' string     {
            CODE3("varpush", $1->name, "strassign");
            code3(Ft_varpush, (Inst)$1, Ft_strassign); $$=$3; }
    |    STRVARARG '=' string     {
            CODE("argvarpush"); ARGNO($1); CODE("strassign");
            code3(Ft_argvarpush, (Inst)$1, Ft_strassign); $$=$3; }
    ;
vecasgn: VEC '=' vecloop expr     {
            CODE3("varpush", $1->name, "assign");
            CODE2("nullpop", "STOP");
            code3(Ft_varpush, (Inst)$1, Ft_assign);
            code2(Ft_nullpop, STOP); }
    |    VEC ADDASS vecloop expr    {
            CODE3("varpush", $1->name, "addassign");
            CODE2("nullpop", "STOP");
            code3(Ft_varpush, (Inst)$1, Ft_addassign);
            code2(Ft_nullpop, STOP); }
    |    VEC SUBASS vecloop expr    {
            CODE3("varpush", $1->name, "subassign");
            CODE2("nullpop", "STOP");
            code3(Ft_varpush, (Inst)$1, Ft_subassign);
            code2(Ft_nullpop, STOP); }
    |    VEC MULASS vecloop expr    {
            CODE3("varpush", $1->name, "mulassign");
            CODE2("nullpop", "STOP");
            code3(Ft_varpush, (Inst)$1, Ft_mulassign);
            code2(Ft_nullpop, STOP); }
    |    VEC DIVASS vecloop expr    {
            CODE3("varpush", $1->name, "divassign");
            CODE2("nullpop", "STOP");
            code3(Ft_varpush, (Inst)$1, Ft_divassign);
            code2(Ft_nullpop, STOP); }
    |    VECARG '=' vecloop expr     {
            CODE("argvarpush"); ARGNO($1); CODE("assign");
            CODE2("nullpop", "STOP");
            code3(Ft_argvarpush, (Inst)$1, Ft_assign);
            code2(Ft_nullpop, STOP); }
    |    VECARG ADDASS vecloop expr    {
            CODE("argvarpush"); ARGNO($1); CODE("addassign");
            CODE2("nullpop", "STOP");
            code3(Ft_argvarpush, (Inst)$1, Ft_addassign);
            code2(Ft_nullpop, STOP); }
    |    VECARG SUBASS vecloop expr    {
            CODE("argvarpush"); ARGNO($1); CODE("subassign");
            CODE2("nullpop", "STOP");
            code3(Ft_argvarpush, (Inst)$1, Ft_subassign);
            code2(Ft_nullpop, STOP); }
    |    VECARG MULASS vecloop expr    {
            CODE("argvarpush"); ARGNO($1); CODE("mulassign");
            CODE2("nullpop", "STOP");
            code3(Ft_argvarpush, (Inst)$1, Ft_mulassign);
            code2(Ft_nullpop, STOP); }
    |    VECARG DIVASS vecloop expr    {
            CODE("argvarpush"); ARGNO($1); CODE("divassign");
            CODE2("nullpop", "STOP");
            code3(Ft_argvarpush, (Inst)$1, Ft_divassign);
            code2(Ft_nullpop, STOP); }
    ;
parasgn: PARAM '=' parloop expr     {
            CODE3("varpush", $1->name, "assign");
            CODE2("nullpop", "STOP");
            code3(Ft_varpush, (Inst)$1, Ft_assign);
            code2(Ft_nullpop, STOP); }
    |    PARAM ADDASS parloop expr    {
            CODE3("varpush", $1->name, "addassign");
            CODE2("nullpop", "STOP");
            code3(Ft_varpush, (Inst)$1, Ft_addassign);
            code2(Ft_nullpop, STOP); }
    |    PARAM SUBASS parloop expr    {
            CODE3("varpush", $1->name, "subassign");
            CODE2("nullpop", "STOP");
            code3(Ft_varpush, (Inst)$1, Ft_subassign);
            code2(Ft_nullpop, STOP); }
    |    PARAM MULASS parloop expr    {
            CODE3("varpush", $1->name, "mulassign");
            CODE2("nullpop", "STOP");
            code3(Ft_varpush, (Inst)$1, Ft_mulassign);
            code2(Ft_nullpop, STOP); }
    |    PARAM DIVASS parloop expr    {
            CODE3("varpush", $1->name, "divassign");
            CODE2("nullpop", "STOP");
            code3(Ft_varpush, (Inst)$1, Ft_divassign);
            code2(Ft_nullpop, STOP); }
    |    PARARG '=' parloop expr     {
            CODE("argvarpush"); ARGNO($1); CODE("assign");
            CODE2("nullpop", "STOP");
            code3(Ft_argvarpush, (Inst)$1, Ft_assign);
            code2(Ft_nullpop, STOP); }
    |    PARARG ADDASS parloop expr    {
            CODE("argvarpush"); ARGNO($1); CODE("addassign");
            CODE2("nullpop", "STOP");
            code3(Ft_argvarpush, (Inst)$1, Ft_addassign);
            code2(Ft_nullpop, STOP); }
    |    PARARG SUBASS parloop expr    {
            CODE("argvarpush"); ARGNO($1); CODE("subassign");
            CODE2("nullpop", "STOP");
            code3(Ft_argvarpush, (Inst)$1, Ft_subassign);
            code2(Ft_nullpop, STOP); }
    |    PARARG MULASS parloop expr    {
            CODE("argvarpush"); ARGNO($1); CODE("mulassign");
            CODE2("nullpop", "STOP");
            code3(Ft_argvarpush, (Inst)$1, Ft_mulassign);
            code2(Ft_nullpop, STOP); }
    |    PARARG DIVASS parloop expr    {
            CODE("argvarpush"); ARGNO($1); CODE("divassign");
            CODE2("nullpop", "STOP");
            code3(Ft_argvarpush, (Inst)$1, Ft_divassign);
            code2(Ft_nullpop, STOP); }
    ;
argelement:    VECARG index     { $$=$1; }
	|	PARARG index     { $$=$1; }
    ;
element: VEC index     { $$=$1; }
    |    PARAM index    { $$=$1; }
    ;
index: '[' expr ']'
    ;
vecloop:    {
            CODE("vecloop");
            $$ = code(Ft_vecloop); }
    ;
parloop:    {
            CODE("parloop");
            $$ = code(Ft_parloop); }
    ;
stmt: strasgn        {
            CODE("nullpop");
            code(Ft_nullpop); }
    | dontprint {
            CODE("nullpop");
            code(Ft_nullpop); }
    | vecasgn
    | parasgn
    | linprlist    {
            CODE("linprnl");
            code(Ft_linprnl); }
    | PRINT prlist     { $$ = $2;}
    | RETURN {
            CODE("procret");
            Ft_defnonly(PROC, "return"); $$ = code(Ft_procret); }
    | RETURN '(' expr ')'    {
            CODE("funcret");
            Ft_defnonly(FUNC, "return"); $$ = $3; code(Ft_funcret); }
    | CONTINUE         {
            CODE("STOP");
            Ft_defnonly(WHILE, "continue"); $$ = code(STOP); }
    | BREAK               {
            CODE("breakit");
            Ft_defnonly(WHILE, "break"); $$ = code(Ft_breakit); }
    | EPROCSYM begin '(' arglist ')'    {
            CODE2("extcall", $1->name); ARGNO($4);
            $$=$2; code3(Ft_extcall, (Inst)$1, (Inst)$4); }
    | PROCSYM begin '(' arglist ')'    {
            CODE2("call", $1->name); ARGNO($4);
            $$=$2; code3(Ft_call, (Inst)$1, (Inst)$4); }
    | while cond newline stmtline end {
            CODE("Replace while[1]"); PNUM($4-prog);
              ($1)[1] = (Inst) $4;
            CODE("Replace while[2]"); PNUM($5-prog);
              ($1)[2] = (Inst) $5; }
    | if cond newline stmtline end newline {
            CODE("Replace if[1]"); PNUM($4-prog);
              ($1)[1] = (Inst) $4;
            CODE("Replace if[3]"); PNUM($5-prog);
              ($1)[3] = (Inst) $5; }
    | if cond newline stmtline end newline else newline stmtline end {
            CODE("Replace if[1]"); PNUM($4-prog);
              ($1)[1] = (Inst) $4;
            CODE("Replace if[2]"); PNUM($9-prog);
              ($1)[2] = (Inst) $9;
            CODE("Replace if[3]"); PNUM($10-prog);
              ($1)[3] = (Inst) $10; }
    | for asgnlist forcond exprlist newline stmtline end {
            CODE("Replace for[1]"); PNUM($2-prog);
              ($1)[1] = (Inst) $2;
            CODE("Replace for[2]"); PNUM($3-prog);
              ($1)[2] = (Inst) $3;
            CODE("Replace for[3]"); PNUM($6-prog);
              ($1)[3] = (Inst) $6;
            CODE("Replace for[4]"); PNUM($7-prog);
              ($1)[4] = (Inst) $7; }
    | opbrace autolines		{
			if ($2) {
				CODE("boost"); ARGNO($2);
				code2(Ft_boost, (Inst)$2);
			};						}
	  stmtlist clbrace    { ; }
    ;
newline:
	|	'\n'	 { if (!Ft_more_input(Ft_Inbrace, interprompt)) {
					Ft_matherror("Incomplete statement.", 0, 0);
				   };						}
	;
opbrace: '{'  { Ft_Inbrace++; $$ = Ft_Progp; }
	;
clbrace: '}'  {
				{ 	int num = Ft_autosymremove(Ft_Inbrace--);
					if (num) {
						CODE("restore"); ARGNO(num);
						code2(Ft_restore, (Inst)num);
					};
				}; 						}
	;
stmtlist: 
    |   stmtlist '\n'	{ if (!Ft_more_input(Ft_Inbrace, 0)) {
							Ft_matherror("Unmatched brace.", 0, 0);
				   		};						}
	|	stmtlist stmtline
    ;
autolines:					{ $$ = 0; }
	|  autol				{ $$ = $1; }
	|  autolines ';'		{ $$ = $1; }
	|  autolines '\n'   	{ $$ = $1; 
							if (!Ft_more_input(Ft_Inbrace, 0)) {
								Ft_matherror("Unmatched brace.", 0, 0);
				    		};						}
	|  autolines autol		{ $$ = $1 + $2; }
	;
autol:  AUTO             	{ Ft_Inauto = 1; }
		autolist    		{ Ft_Inauto = 0; $$ = $3; }
	;
autolist: 	VARARG 				{
			CODE2("pushnull", "pushexprtype");
			code2(Ft_pushnull, Ft_pushexprtype); $$ = 1; }
	|	VARARG '=' 			{ Ft_Inauto = 0; }
		expr				{
			CODE("pushexprtype");
			code(Ft_pushexprtype); $$ = 1; Ft_Inauto = 1; }
	|	STRVARARG			{
			CODE2("strmake", "pushastrtype");
			code2(Ft_strmake, Ft_pushastrtype); $$ = 1; }
	|	VECARG 				{
			CODE2("vecmake", "pushavectype");
			code2(Ft_vecmake, Ft_pushavectype); $$ = 1; }
	|	autolist ',' VARARG	{
			CODE2("pushnull", "pushexprtype");
			code2(Ft_pushnull, Ft_pushexprtype); $$ = $1 + 1; }
	|	autolist ',' VARARG '=' 		{ Ft_Inauto = 0; }
		expr				{
			CODE("pushexprtype");
			code(Ft_pushexprtype); Ft_Inauto = 1; $$ = $1 + 1; }
	|	autolist ',' VECARG	{
			CODE2("vecmake", "pushavectype");
			code2(Ft_vecmake, Ft_pushavectype); $$ = $1 + 1; }
	|	autolist ',' STRVARARG	 {
			CODE2("stramke", "pushastrtype");
			code2(Ft_strmake, Ft_pushastrtype); $$ = $1 + 1; }
	;
cond: '(' expr ')'      {
            CODE("STOP");
            code(STOP); $$=$2; }
    ;
asgnlist:    '(' asgnl     {
            CODE("STOP");
            code(STOP); $$=Ft_Progp; }
    ;
asgnl:   asgn    {
            CODE("nullpop");
            code(Ft_nullpop); }
    |    strasgn    {
            CODE("nullpop");
            code(Ft_nullpop); }
    |    vecasgn
    |    parasgn
    |    asgnl ',' asgn    {
            CODE("nullpop");
            code(Ft_nullpop); }
    |    asgnl ',' strasgn    {
            CODE("nullpop");
            code(Ft_nullpop); }
    |    asgnl ',' vecasgn
    |    asgnl ',' parasgn
    ;
forcond:     ';' expr ';'    {
            CODE("STOP");
            code(STOP);  $$=Ft_Progp; }
        ;
exprlist:    exprl ')'    {
            CODE("STOP");
            code(STOP); }
    ;
exprl:   expr    {
            CODE("nullpop");
            code(Ft_nullpop); }
    |    string  {
            CODE("nullpop");
            code(Ft_nullpop); }
	|	 vecasgn
	|	 parasgn
    |    exprl ',' expr    {
            CODE("nullpop");
            code(Ft_nullpop); }
    |    exprl ',' string  {
            CODE("nullpop");
            code(Ft_nullpop); }
    |    exprl ',' vecasgn
    |    exprl ',' parasgn
    ;
while:   WHILE             {
            CODE3("whilecode", "STOP", "STOP");
            $$ = code3(Ft_whilecode, STOP, STOP); 
			interprompt = "while? "; }
    ;
if:      IF                 {
            CODE("ifcode"); CODE3("STOP", "STOP", "STOP");
            $$ = code(Ft_ifcode); code3(STOP, STOP, STOP);
			interprompt = "if? "; }
    ;
else:	 ELSE				{
			interprompt = "else? "; }
	;
for:     FOR                {
            CODE("forcode"); CODE("STOP"); CODE3("STOP", "STOP", "STOP");
            $$ = code(Ft_forcode); code(STOP); code3(STOP, STOP, STOP);
			interprompt = "for? "; }
    ;
begin:                  { $$ = Ft_Progp; }
    ;
end:                    {
            CODE("STOP");
            code(STOP); $$ = Ft_Progp; }
    ;
dontprint: varincr
    | asgn
    ;
expr: argexpr
    | VEC        {
            CODE3("varpush", $1->name, "eval");
            $$ = code3(Ft_varpush, (Inst)$1, Ft_eval); }
    | VECARG     {
            CODE("argvarpush"); ARGNO($1); CODE("eval");
            $$ = code3(Ft_argvarpush, (Inst)$1, Ft_eval); }
    ;
argexpr: value
    | dontprint
    ;
argstr: STRVAR        {
            CODE2("varpush", $1->name);
            $$ = code2(Ft_varpush, (Inst)$1); }
    | UNDEFSTRVAR        {
            CODE2("varpush", $1->name);
            $$ = code2(Ft_varpush, (Inst)$1); }
    ;
nicevec: VEC           {
            CODE2("varpush", $1->name);
            $$ = code2(Ft_varpush, (Inst)$1); }
	;
argvec: nicevec
    | UNDEFVEC        {
            CODE2("varpush", $1->name);
            $$ = code2(Ft_varpush, (Inst)$1); }
    ;
argpar: PARAM          {
            CODE2("varpush", $1->name);
            $$ = code2(Ft_varpush, (Inst)$1); }
	;
varincr:INCR VAR %prec INCR {
            CODE3("varpush", $2->name, "preieval");
            $$ = code3(Ft_varpush, (Inst)$2, Ft_preieval); }
    |    VAR INCR %prec INCR {
            CODE3("varpush", $1->name, "postieval");
            $$ = code3(Ft_varpush, (Inst)$1, Ft_postieval); }
    |    DECR VAR %prec INCR {
            CODE3("varpush", $2->name, "predeval");
            $$ = code3(Ft_varpush, (Inst)$2, Ft_predeval); }
    |    VAR DECR %prec INCR {
            CODE3("varpush", $1->name, "postdeval");
            $$ = code3(Ft_varpush, (Inst)$1, Ft_postdeval); }
	|	INCR VARARG %prec INCR {
            CODE("preiargpush"); ARGNO($2);
            $$ = code2(Ft_preiargpush, (Inst)$2); }
    |    VARARG INCR %prec INCR {
            CODE("postiargpush"); ARGNO($1);
            $$ = code2(Ft_postiargpush, (Inst)$1); }
    |    DECR VARARG %prec INCR {
            CODE("predargpush"); ARGNO($2);
            $$ = code2(Ft_predargpush, (Inst)$2); }
    |    VARARG DECR %prec INCR {
            CODE("postdargpush"); ARGNO($1);
            $$ = code2(Ft_postdargpush, (Inst)$1); }
    ;
value: NUMBER    {
            CODE2("constpush", "number");
            $$ = code(Ft_constpush); Ft_dblcode($1); }
    | VARARG        {
            CODE("argpush"); ARGNO($1);
            $$ = code2(Ft_argpush, (Inst)$1); }
    | VAR        {
            CODE3("varpush", $1->name, "eval");
            $$ = code3(Ft_varpush, (Inst)$1, Ft_eval); }
    | CONST        {
            CODE3("varpush", $1->name, "eval");
            $$ = code3(Ft_varpush, (Inst)$1, Ft_eval); }
    | EFUNCSYM begin '(' arglist ')'    {
            CODE2("extcall", $1->name); ARGNO($4);
            $$ = $2; code3(Ft_extcall, (Inst)$1, (Inst)$4); }
    | FUNCSYM begin '(' arglist ')'    {
            CODE2("call", $1->name); ARGNO($4);
            $$ = $2; code3(Ft_call, (Inst)$1, (Inst)$4); }
    | BLTIN0  '(' ')' {
            CODE2("builtin0", $1->name);
            $$ = code2(Ft_bltin0, (Inst)$1->u.ptr); }
    | BLTIN1VEC '(' nicevec ')' {
            CODE2("builtin1vec", $1->name);
            $$ = code2(Ft_bltin1vec, (Inst)$1->u.ptr); }
    | BLTIN1  '(' expr ')' {
            CODE2("builtin1", $1->name);
            $$ = code2(Ft_bltin1, (Inst)$1->u.ptr); }
    | BLTIN2  '(' expr ',' expr ')' {
            CODE2("builtin2", $1->name);
            $$ = code2(Ft_bltin2, (Inst)$1->u.ptr); }
    | STRBLTIN2  '(' string ',' string ')' {
            CODE2("strbltin2", $1->u.str);
            $$ = code2(Ft_strbltin2, (Inst)$1->u.str); }
    | element         {
            CODE3("varpush", $1->name, "eeval");
            $$ = code3(Ft_varpush, (Inst)$1, Ft_eeval); }
    | argelement      {
            CODE("argvarpush"); ARGNO($1); CODE("eeval");
            $$ = code3(Ft_argvarpush, (Inst)$1, Ft_eeval); }
    | '(' expr ')'     { $$ = $2;    }
    | expr '+' expr {
            CODE("add");
            code(Ft_add); }
    | expr '-' expr {
            CODE("sub");
            code(Ft_sub); }
    | expr '/' expr {
            CODE("div");
            code(Ft_div); }
    | expr '%' expr {
            CODE("modulo");
            code(Ft_modulo); }
    | expr '*' expr {
            CODE("mul");
            code(Ft_mul); }
    | expr '^' expr {
            CODE("power");
            code(Ft_power); }
    | '-' expr %prec UNARYMINUS {
            CODE("negate");
            $$ = $2; code(Ft_negate); }
    | string EQ string  {
            CODE("streq");
            code(Ft_streq);  }
    | string NE string  {
            CODE("strne");
            code(Ft_strne);  }
    | expr GT expr  {
            CODE("gt");
            code(Ft_gt); }
    | expr GE expr {
            CODE("ge");
            code(Ft_ge); }
    | expr LT expr  {
            CODE("lt");
            code(Ft_lt);  }
    | expr LE expr  {
            CODE("le");
            code(Ft_le);  }
    | expr EQ expr  {
            CODE("eq");
            code(Ft_eq);  }
    | expr NE expr  {
            CODE("ne");
            code(Ft_ne);  }
    | expr AND expr {
            CODE("and");
            code(Ft_and); }
    | expr OR expr  {
            CODE("or");
            code(Ft_or);  }
    | NOT expr      {
            CODE("not");
            $$ = $2; code(Ft_not); }
    ;
string: strvalue
    | strasgn
    ;
strvalue: STRING    {
            CODE2("strpush", "string");
            $$ = code2(Ft_strpush, (Inst)$1); }
    | STRVAR        {
            CODE3("varpush", $1->name, "streval");
            $$ = code3(Ft_varpush, (Inst)$1, Ft_streval); }
    | STRVARARG     {
            CODE("argvarpush"); ARGNO($1); CODE("streval");
            $$ = code3(Ft_argvarpush, (Inst)$1, Ft_streval); }
    | STRCONST      {
            CODE3("varpush", $1->name, "streval");
            $$ = code3(Ft_varpush, (Inst)$1, Ft_streval); }
    | BLTIN2STR '(' string ',' string ')'    {
            CODE2("builtin2str", $1->name);
            code2(Ft_bltin2str, (Inst)$1->u.ptr); }
    | BLTIN1STR '(' string ')' {
            CODE2("builtin1str", $1->name);
            code2(Ft_bltin1str, (Inst)$1->u.ptr); }
    | BLTIN0STR '(' ')'        {
            CODE2("builtin0str", $1->name);
            code2(Ft_bltin0str, (Inst)$1->u.ptr); }
    | string '-' string        {
        CODE("strsub");
        code(Ft_strsub); }
    | string '+' string        {
        CODE("stradd");
        code(Ft_stradd); }
    | '(' string ')'     { $$ = $2; }
    ;
defn:    FUNC procname { Ft_chkfunc(FUNCSYM, $2); Ft_Indef=1; }
        '(' { Ft_Inproto = 1; Ft_autosymremove(0); }
        protolist ')' { Ft_Inproto = 0; CODE("STOP"); code(STOP); }
        stmt    {
            CODE("procret");
            code(Ft_procret); Ft_define($2); Ft_Indef = 0;
		    Ft_autosymremove(0); }
    |    PROC procname { Ft_chkfunc(PROCSYM, $2); Ft_Indef=1; }
        '(' { Ft_Inproto = 1; Ft_autosymremove(0); }
        protolist ')' { Ft_Inproto = 0; CODE("STOP"); code(STOP); }
        stmt    {
            CODE("procret");
            code(Ft_procret); Ft_define($2); Ft_Indef = 0;
            Ft_autosymremove(0); }
    ;
procname: VAR
        | FUNCSYM
        | PROCSYM
        ;
protolist:
    | VARARG { CODE("NUMBER"); code((Inst)NUMBER); }
    | VECARG { CODE("VEC"); code((Inst)VEC); }
    | PARARG { CODE("PARAM"); code((Inst)PARAM); }
    | STRVARARG { CODE("STRVAR"); code((Inst)STRVAR); }
    | protolist ',' VARARG { CODE("NUMBER"); code((Inst)NUMBER); }
    | protolist ',' VECARG { CODE("VEC"); code((Inst)VEC); }
    | protolist ',' PARARG { CODE("PARAM"); code((Inst)PARAM); }
    | protolist ',' STRVARARG { CODE("STRVAR"); code((Inst)STRVAR); }
    ;
arglist:                { $$ = 0; }
    | argpar            { CODE("pushpartype"); code(Ft_pushpartype); $$ = 1; }
    | argvec            { CODE("pushvectype"); code(Ft_pushvectype); $$ = 1; }
    | argstr            { CODE("pushstrtype"); code(Ft_pushstrtype); $$ = 1; }
    | argexpr           { CODE("pushexprtype"); code(Ft_pushexprtype); $$ = 1; }
    | arglist ',' argpar  { CODE("pushpartype"); code(Ft_pushpartype);
                            $$ = $1 + 1; }
    | arglist ',' argvec  { CODE("pushvectype"); code(Ft_pushvectype);
                            $$ = $1 + 1; }
    | arglist ',' argstr  { CODE("pushstrtype"); code(Ft_pushstrtype);
                            $$ = $1 + 1; }
    | arglist ',' argexpr { CODE("pushexprtype"); code(Ft_pushexprtype);
                            $$ = $1 + 1; }
    ;
linprlist:    value          {
            CODE("linprexpr");
            code(Ft_linprexpr); }
    | strvalue               {
            CODE("linprstr");
            code(Ft_linprstr);  }
    | linprlist ',' value    {
            CODE("linprexpr");
            code(Ft_linprexpr); }
    | linprlist ',' strvalue {
            CODE("linprstr");
            code(Ft_linprstr);  }
    ;
prlist:     expr          {
            CODE("prexpr");
            code(Ft_prexpr); }
    | string              {
            CODE("prstr");
            code(Ft_prstr);  }
    | prlist ',' expr     {
            CODE("prexpr");
            code(Ft_prexpr); }
    | prlist ',' string   {
            CODE("prstr");
            code(Ft_prstr);  }
    ;
%%

