%{
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <mach.h>
#define Extern extern
#include "acid.h"
%}

%union
{
       Node    *node;
       Lsym    *sym;
       uvlong  ival;
       float   fval;
       String  *string;
}

%type <node> expr monexpr term stmnt name args zexpr slist
%type <node> member members mname castexpr idlist
%type <sym> zname

%left   ';'
%right  '='
%left   Tfmt
%left   Toror
%left   Tandand
%left   '|'
%left   '^'
%left   '&'
%left   Teq Tneq
%left   '<' '>' Tleq Tgeq
%left   Tlsh Trsh
%left   '+' '-'
%left   '*' '/' '%'
%right  Tdec Tinc Tindir '.' '[' '('

%token <sym>    Tid
%token <ival>   Tconst Tfmt
%token <fval>   Tfconst
%token <string> Tstring
%token Tif Tdo Tthen Telse Twhile Tloop Thead Ttail Tappend Tfn Tret Tlocal
%token Tcomplex Twhat Tdelete Teval Tbuiltin

%%

prog            :
               | prog bigstmnt
               ;

bigstmnt        : stmnt
               {
                       execrec($1);
                       if(interactive)
                               Bprint(bout, "acid: ");
               }
               | Tfn Tid '(' args ')' zsemi '{' slist '}'
               {
                       $2->proc = an(OLIST, $4, $8);
               }
               | Tfn Tid
               {
                       $2->proc = nil;
               }
               | Tcomplex name '{' members '}' ';'
               {
                       defcomplex($2, $4);
               }
               ;

zsemi           :
               | ';' zsemi

members         : member
               | members member
               {
                       $$ = an(OLIST, $1, $2);
               }
               ;

mname           : Tid
               {
                       $$ = an(ONAME, ZN, ZN);
                       $$->sym = $1;
               }
               ;

member          : Tconst Tconst mname ';'
               {
                       $3->ival = $2;
                       $3->fmt = $1;
                       $$ = $3;
               }
               | Tconst mname Tconst mname ';'
               {
                       $4->ival = $3;
                       $4->fmt = $1;
                       $4->right = $2;
                       $$ = $4;
               }
               | mname Tconst mname ';'
               {
                       $3->ival = $2;
                       $3->left = $1;
                       $$ = $3;
               }
               | '{' members '}' ';'
               {
                       $$ = an(OCTRUCT, $2, ZN);
               }
               ;

zname           :
               { $$ = 0; }
               | Tid
               ;

slist           : stmnt
               | slist stmnt
               {
                       $$ = an(OLIST, $1, $2);
               }
               ;

stmnt           : zexpr ';'
               | '{' slist '}'
               {
                       $$ = $2;
               }
               | Tif expr Tthen stmnt
               {
                       $$ = an(OIF, $2, $4);
               }
               | Tif expr Tthen stmnt Telse stmnt
               {
                       $$ = an(OIF, $2, an(OELSE, $4, $6));
               }
               | Tloop expr ',' expr Tdo stmnt
               {
                       $$ = an(ODO, an(OLIST, $2, $4), $6);
               }
               | Twhile expr Tdo stmnt
               {
                       $$ = an(OWHILE, $2, $4);
               }
               | Tret expr ';'
               {
                       $$ = an(ORET, $2, ZN);
               }
               | Tlocal idlist
               {
                       $$ = an(OLOCAL, $2, ZN);
               }
               | Tcomplex Tid name ';'
               {
                       $$ = an(OCOMPLEX, $3, ZN);
                       $$->sym = $2;
               }
               ;

idlist          : Tid
               {
                       $$ = an(ONAME, ZN, ZN);
                       $$->sym = $1;
               }
               | idlist ',' Tid
               {
                       $$ = an(ONAME, $1, ZN);
                       $$->sym = $3;
               }
               ;

zexpr           :
               { $$ = 0; }
               | expr
               ;

expr            : castexpr
               | expr '*' expr
               {
                       $$ = an(OMUL, $1, $3);
               }
               | expr '/' expr
               {
                       $$ = an(ODIV, $1, $3);
               }
               | expr '%' expr
               {
                       $$ = an(OMOD, $1, $3);
               }
               | expr '+' expr
               {
                       $$ = an(OADD, $1, $3);
               }
               | expr '-' expr
               {
                       $$ = an(OSUB, $1, $3);
               }
               | expr Trsh expr
               {
                       $$ = an(ORSH, $1, $3);
               }
               | expr Tlsh expr
               {
                       $$ = an(OLSH, $1, $3);
               }
               | expr '<' expr
               {
                       $$ = an(OLT, $1, $3);
               }
               | expr '>' expr
               {
                       $$ = an(OGT, $1, $3);
               }
               | expr Tleq expr
               {
                       $$ = an(OLEQ, $1, $3);
               }
               | expr Tgeq expr
               {
                       $$ = an(OGEQ, $1, $3);
               }
               | expr Teq expr
               {
                       $$ = an(OEQ, $1, $3);
               }
               | expr Tneq expr
               {
                       $$ = an(ONEQ, $1, $3);
               }
               | expr '&' expr
               {
                       $$ = an(OLAND, $1, $3);
               }
               | expr '^' expr
               {
                       $$ = an(OXOR, $1, $3);
               }
               | expr '|' expr
               {
                       $$ = an(OLOR, $1, $3);
               }
               | expr Tandand expr
               {
                       $$ = an(OCAND, $1, $3);
               }
               | expr Toror expr
               {
                       $$ = an(OCOR, $1, $3);
               }
               | expr '=' expr
               {
                       $$ = an(OASGN, $1, $3);
               }
               | expr Tfmt
               {
                       $$ = an(OFMT, $1, con($2));
               }
               ;

castexpr        : monexpr
               | '(' Tid ')' monexpr
               {
                       $$ = an(OCAST, $4, ZN);
                       $$->sym = $2;
               }
               ;

monexpr         : term
               | '*' monexpr
               {
                       $$ = an(OINDM, $2, ZN);
               }
               | '@' monexpr
               {
                       $$ = an(OINDC, $2, ZN);
               }
               | '+' monexpr
               {
                       $$ = an(OADD, $2, ZN);
               }
               | '-' monexpr
               {
                       $$ = con(0);
                       $$ = an(OSUB, $$, $2);
               }
               | Tdec monexpr
               {
                       $$ = an(OEDEC, $2, ZN);
               }
               | Tinc monexpr
               {
                       $$ = an(OEINC, $2, ZN);
               }
               | Thead monexpr
               {
                       $$ = an(OHEAD, $2, ZN);
               }
               | Ttail monexpr
               {
                       $$ = an(OTAIL, $2, ZN);
               }
               | Tappend monexpr ',' monexpr
               {
                       $$ = an(OAPPEND, $2, $4);
               }
               | Tdelete monexpr ',' monexpr
               {
                       $$ = an(ODELETE, $2, $4);
               }
               | '!' monexpr
               {
                       $$ = an(ONOT, $2, ZN);
               }
               | '~' monexpr
               {
                       $$ = an(OXOR, $2, con(-1));
               }
               | Teval monexpr
               {
                       $$ = an(OEVAL, $2, ZN);
               }
               ;

term            : '(' expr ')'
               {
                       $$ = $2;
               }
               | '{' args '}'
               {
                       $$ = an(OCTRUCT, $2, ZN);
               }
               | term '[' expr ']'
               {
                       $$ = an(OINDEX, $1, $3);
               }
               | term Tdec
               {
                       $$ = an(OPDEC, $1, ZN);
               }
               | term '.' Tid
               {
                       $$ = an(ODOT, $1, ZN);
                       $$->sym = $3;
               }
               | term Tindir Tid
               {
                       $$ = an(ODOT, an(OINDM, $1, ZN), ZN);
                       $$->sym = $3;
               }
               | term Tinc
               {
                       $$ = an(OPINC, $1, ZN);
               }
               | name '(' args ')'
               {
                       $$ = an(OCALL, $1, $3);
               }
               | Tbuiltin name '(' args ')'
               {
                       $$ = an(OCALL, $2, $4);
                       $$->builtin = 1;
               }
               | name
               | Tconst
               {
                       $$ = con($1);
               }
               | Tfconst
               {
                       $$ = an(OCONST, ZN, ZN);
                       $$->type = TFLOAT;
                       $$->fmt = 'f';
                       $$->fval = $1;
               }
               | Tstring
               {
                       $$ = an(OCONST, ZN, ZN);
                       $$->type = TSTRING;
                       $$->string = $1;
                       $$->fmt = 's';
               }
               | Twhat zname
               {
                       $$ = an(OWHAT, ZN, ZN);
                       $$->sym = $2;
               }
               ;

name            : Tid
               {
                       $$ = an(ONAME, ZN, ZN);
                       $$->sym = $1;
               }
               | Tid ':' name
               {
                       $$ = an(OFRAME, $3, ZN);
                       $$->sym = $1;
               }
               ;

args            : zexpr
               | args ','  zexpr
               {
                       $$ = an(OLIST, $1, $3);
               }
               ;