%{
/*****
* camp.y
* Andy Hammerlindl 08/12/2002
*
* The grammar of the camp language.
*****/

#include "errormsg.h"
#include "exp.h"
#include "newexp.h"
#include "dec.h"
#include "fundec.h"
#include "stm.h"
#include "modifier.h"
#include "opsymbols.h"

// Avoid error messages with unpatched bison-1.875:
#ifndef __attribute__
#define __attribute__(x)
#endif

// Used when a position needs to be determined and no token is
// available.  Defined in camp.l.
position lexerPos();

bool lexerEOF();

int yylex(void); /* function prototype */

void yyerror(const char *s)
{
 if (!lexerEOF()) {
   em.error(lexerPos());
   em << s;
   em.cont();
 }
}

// Check if the symbol given is "keyword".  Returns true in this case and
// returns false and reports an error otherwise.
bool checkKeyword(position pos, symbol sym)
{
 if (sym != symbol::trans("keyword")) {
   em.error(pos);
   em << "expected 'keyword' here";

   return false;
 }
 return true;
}

namespace absyntax { file *root; }

using namespace absyntax;
using sym::symbol;
using mem::string;
%}

%union {
 position pos;
 bool boo;
 struct {
   position pos;
   sym::symbol sym;
 } ps;
 absyntax::name *n;
 absyntax::varinit *vi;
 absyntax::arrayinit *ai;
 absyntax::exp *e;
 absyntax::stringExp *stre;
 absyntax::specExp *se;
 absyntax::joinExp *j;
 absyntax::explist *elist;
 absyntax::argument arg;
 absyntax::arglist *alist;
 absyntax::slice *slice;
 absyntax::dimensions *dim;
 absyntax::astType  *t;
 absyntax::decid *di;
 absyntax::decidlist *dil;
 absyntax::decidstart *dis;
 absyntax::runnable *run;
 struct {
   position pos;
   trans::permission val;
 } perm;
 struct {
   position pos;
   trans::modifier val;
 } mod;
 absyntax::modifierList *ml;
 //absyntax::program *prog;
 absyntax::vardec *vd;
 //absyntax::vardecs *vds;
 absyntax::dec *d;
 absyntax::idpair *ip;
 absyntax::idpairlist *ipl;
 absyntax::stm *s;
 absyntax::block *b;
 absyntax::stmExpList *sel;
 //absyntax::funheader *fh;
 absyntax::formal *fl;
 absyntax::formals *fls;
 absyntax::typeParam *tp;
 absyntax::typeParamList *tps;
}

%token <ps> ID SELFOP
           DOTS COLONS DASHES INCR LONGDASH
           CONTROLS TENSION ATLEAST CURL
           COR CAND BAR AMPERSAND EQ NEQ LT LE GT GE CARETS
           '+' '-' '*' '/' '%' '#' '^' OPERATOR
%token <pos> LOOSE ASSIGN '?' ':'
            DIRTAG JOIN_PREC AND
            '{' '}' '(' ')' '.' ','  '[' ']' ';' ELLIPSIS
            ACCESS UNRAVEL IMPORT INCLUDE FROM QUOTE STRUCT TYPEDEF USING NEW
            IF ELSE WHILE DO FOR BREAK CONTINUE RETURN_
            THIS_TOK EXPLICIT
            GARBAGE
%token <e>   LIT
%token <stre> STRING
%token <perm> PERM
%token <mod> MODIFIER

%right ASSIGN SELFOP
%right '?' ':'
%left  COR
%left  CAND
%left  BAR
%left  AMPERSAND
%left  EQ NEQ
%left  LT LE GT GE
%left  OPERATOR

%left  CARETS
%left  JOIN_PREC DOTS COLONS DASHES INCR LONGDASH
%left  DIRTAG CONTROLS TENSION ATLEAST AND
%left  CURL '{' '}'

%left  '+' '-'
%left  '*' '/' '%' '#' LIT
%left  UNARY
%right '^'
%left  EXP_IN_PARENS_RULE
%left  '(' ')'

%type  <b>   fileblock bareblock block
%type  <n>   name
%type  <run> runnable
%type  <ml>  modifiers
%type  <d>   dec fundec typedec
%type  <ps>  strid
%type  <ip>  idpair stridpair
%type  <ipl> idpairlist stridpairlist
%type  <vd>  vardec barevardec
%type  <t>   type celltype
%type  <dim> dims
%type  <dil> decidlist
%type  <di>  decid
%type  <dis> decidstart
%type  <vi>  varinit
%type  <ai>  arrayinit basearrayinit varinits
%type  <fl>  formal decdec
%type  <fls> formals decdeclist
%type  <e>   value exp fortest
%type  <arg> argument
%type  <slice> slice
%type  <j>   join basicjoin
%type  <e>   tension controls
%type  <se>  dir
%type  <elist> dimexps
%type  <alist> arglist tuple
%type  <s>   stm stmexp blockstm
%type  <run> forinit
%type  <sel> forupdate stmexplist
%type  <boo> explicitornot
%type <tp> typeparam
%type <tps> typeparamlist

/* There are four shift/reduce conflicts:
*   the dangling ELSE in IF (exp) IF (exp) stm ELSE stm
*   new ID
*   the argument id=exp is taken as an argument instead of an assignExp
*   explicit cast
*/
%expect 4

/* Enable grammar debugging. */
/*%debug*/

%%

file:
 fileblock        { absyntax::root = $1; }
;

fileblock:
 /* empty */      { $$ = new file(lexerPos(), false); }
| fileblock runnable
                  { $$ = $1; $$->add($2); }
;

bareblock:
 /* empty */      { $$ = new block(lexerPos(), true); }
| bareblock runnable
                  { $$ = $1; $$->add($2); }
;

name:
 ID               { $$ = new simpleName($1.pos, $1.sym); }
| name '.' ID      { $$ = new qualifiedName($2, $1, $3.sym); }
| '%'              { $$ = new simpleName($1.pos,
                                 symbol::trans("operator answer")); }
;

runnable:
 dec              { $$ = $1; }
| stm              { $$ = $1; }
| modifiers dec
                  { $$ = new modifiedRunnable($1->getPos(), $1, $2); }
| modifiers stm
                  { $$ = new modifiedRunnable($1->getPos(), $1, $2); }
;

modifiers:
 MODIFIER         { $$ = new modifierList($1.pos); $$->add($1.val); }
| PERM             { $$ = new modifierList($1.pos); $$->add($1.val); }
| modifiers MODIFIER
                  { $$ = $1; $$->add($2.val); }
| modifiers PERM
                  { $$ = $1; $$->add($2.val); }
;

dec:
 vardec           { $$ = $1; }
| fundec           { $$ = $1; }
| typedec          { $$ = $1; }
| ACCESS stridpairlist ';'
                  { $$ = new accessdec($1, $2); }
| FROM name UNRAVEL idpairlist ';'
                  { $$ = new unraveldec($1, $2, $4); }
| FROM name UNRAVEL '*' ';'
                  { $$ = new unraveldec($1, $2, WILDCARD); }
| UNRAVEL name ';' { $$ = new unraveldec($1, $2, WILDCARD); }
| FROM strid ACCESS idpairlist ';'
                  { $$ = new fromaccessdec($1, $2.sym, $4); }
| FROM strid ACCESS '*' ';'
                  { $$ = new fromaccessdec($1, $2.sym, WILDCARD); }
| IMPORT stridpair ';'
                  { $$ = new importdec($1, $2); }
| INCLUDE ID ';'   { $$ = new includedec($1, $2.sym); }
| INCLUDE STRING ';'
                  { $$ = new includedec($1, $2->getString()); }

// Experimental - templated imports.
| TYPEDEF IMPORT '(' typeparamlist ')' ';'
                  { $$ = new receiveTypedefDec($1, $4); }
| IMPORT TYPEDEF '(' typeparamlist ')' ';'
                  { $$ = new badDec($1, $1,
                    "Expected 'typedef import(<types>);'");
                  }
/* ACCESS strid '(' decdeclist ')' 'as' ID */
| ACCESS strid '(' decdeclist ')' ID ID ';'
                  { $$ = new templateAccessDec(
                       $1, $2.sym, $4, $6.sym, $7.sym, $6.pos
                     ); }
| ACCESS strid '(' decdeclist ')' ';'
                  { $$ = new badDec($1, $6, "expected 'as'"); }
| IMPORT strid '(' decdeclist ')' ';'
                  { $$ = new badDec($1, $1,
                       "Parametrized imports disallowed to reduce naming "
                       "conflicts. Try "
                       "'access <module>(<type parameters>) as <newname>;'."
                    ); }
| FROM strid '(' decdeclist ')' ACCESS idpairlist ';'
                  { $$ = new fromaccessdec($1, $2.sym, $7, $4); }
;

// List mapping dec to dec as in "Key=string, Value=int"
decdec:
   ID ASSIGN type
                  { $$ = new formal(
                       $1.pos, $3, new decidstart($1.pos, $1.sym)
                     ); }
| type { $$ = new formal($1->getPos(), $1, nullptr); }  // ultimately log error
;

decdeclist:
   decdec
                  { $$ = new formals($1->getPos()); $$->add($1); }
|   decdeclist ',' decdec
                  { $$ = $1; $$->add($3); }
;

typeparam:
   ID { $$ = new typeParam($1.pos, $1.sym); }
;

typeparamlist:
 typeparam
                  { $$ = new typeParamList($1->getPos()); $$->add($1); }
| typeparamlist ',' typeparam
                  { $$ = $1; $$->add($3); }
;

idpair:
 ID               { $$ = new idpair($1.pos, $1.sym); }
/* ID 'as' ID */
| ID ID ID         { $$ = new idpair($1.pos, $1.sym, $2.sym , $3.sym); }
;

idpairlist:
 idpair           { $$ = new idpairlist(); $$->add($1); }
| idpairlist ',' idpair
                  { $$ = $1; $$->add($3); }
;

strid:
 ID               { $$ = $1; }
| STRING           { $$.pos = $1->getPos();
                    $$.sym = symbol::literalTrans($1->getString()); }
;

stridpair:
 ID               { $$ = new idpair($1.pos, $1.sym); }
/* strid 'as' ID */
| strid ID ID      { $$ = new idpair($1.pos, $1.sym, $2.sym , $3.sym); }
;

stridpairlist:
 stridpair        { $$ = new idpairlist(); $$->add($1); }
| stridpairlist ',' stridpair
                  { $$ = $1; $$->add($3); }
;

vardec:
 barevardec ';'   { $$ = $1; }
;

barevardec:
 type decidlist   { $$ = new vardec($1->getPos(), $1, $2); }
;

type:
 celltype         { $$ = $1; }
| name dims        { $$ = new arrayTy($1, $2); }
;

celltype:
 name             { $$ = new nameTy($1); }
;

dims:
'[' ']'           { $$ = new dimensions($1); }
| dims '[' ']'     { $$ = $1; $$->increase(); }
;

dimexps:
 '[' exp ']'      { $$ = new explist($1); $$->add($2); }
| dimexps '[' exp ']'
                  { $$ = $1; $$->add($3); }
;

decidlist:
 decid            { $$ = new decidlist($1->getPos()); $$->add($1); }
| decidlist ',' decid
                  { $$ = $1; $$->add($3); }
;

decid:
 decidstart       { $$ = new decid($1->getPos(), $1); }
| decidstart ASSIGN varinit
                  { $$ = new decid($1->getPos(), $1, $3); }
;

decidstart:
 ID               { $$ = new decidstart($1.pos, $1.sym); }
| ID dims          { $$ = new decidstart($1.pos, $1.sym, $2); }
| ID '(' ')'       { $$ = new fundecidstart($1.pos, $1.sym, 0,
                                           new formals($2)); }
| ID '(' formals ')'
                  { $$ = new fundecidstart($1.pos, $1.sym, 0, $3); }
;

varinit:
 exp              { $$ = $1; }
| arrayinit        { $$ = $1; }
;

block:
 '{' bareblock '}'
                  { $$ = $2; }
;

arrayinit:
 '{' '}'          { $$ = new arrayinit($1); }
| '{' ELLIPSIS varinit '}'
                  { $$ = new arrayinit($1); $$->addRest($3); }
| '{' basearrayinit '}'
                  { $$ = $2; }
| '{' basearrayinit ELLIPSIS varinit '}'
                  { $$ = $2; $$->addRest($4); }
;

basearrayinit:
 ','              { $$ = new arrayinit($1); }
| varinits         { $$ = $1; }
| varinits ','     { $$ = $1; }
;

varinits:
 varinit          { $$ = new arrayinit($1->getPos());
                    $$->add($1);}
| varinits ',' varinit
                  { $$ = $1; $$->add($3); }
;

formals:
 formal           { $$ = new formals($1->getPos()); $$->add($1); }
| ELLIPSIS formal  { $$ = new formals($1); $$->addRest($2); }
| formals ',' formal
                  { $$ = $1; $$->add($3); }
| formals ELLIPSIS formal
                  { $$ = $1; $$->addRest($3); }
;

explicitornot:
 EXPLICIT         { $$ = true; }
|                  { $$ = false; }
;

formal:
 explicitornot type
                  { $$ = new formal($2->getPos(), $2, 0, 0, $1, 0); }
| explicitornot type decidstart
                  { $$ = new formal($2->getPos(), $2, $3, 0, $1, 0); }
| explicitornot type decidstart ASSIGN varinit
                  { $$ = new formal($2->getPos(), $2, $3, $5, $1, 0); }
/* The uses of ID below are 'keyword' qualifiers before the parameter name. */
| explicitornot type ID decidstart
                  { bool k = checkKeyword($3.pos, $3.sym);
                    $$ = new formal($2->getPos(), $2, $4, 0, $1, k); }
| explicitornot type ID decidstart ASSIGN varinit
                  { bool k = checkKeyword($3.pos, $3.sym);
                    $$ = new formal($2->getPos(), $2, $4, $6, $1, k); }
;

fundec:
 type ID '(' ')' blockstm
                  { $$ = new fundec($3, $1, $2.sym, new formals($3), $5); }
| type ID '(' formals ')' blockstm
                  { $$ = new fundec($3, $1, $2.sym, $4, $6); }
;

typedec:
 STRUCT ID block  { $$ = new recorddec($1, $2.sym, $3); }
| TYPEDEF vardec   { $$ = new typedec($1, $2); }
// See definition for decidstart. Following C++, "The syntax of the type-id
// that names type T is exactly the syntax of a declaration of a variable or
// function of type T, with the identifier omitted."
// http://en.cppreference.com/w/cpp/language/type#Type_naming
| USING ID ASSIGN type ';'
                  { decidstart *dis = new decidstart($2.pos, $2.sym);
                    $$ = new typedec($1, dis, $4); }
| USING ID ASSIGN type '(' ')' ';'
                  { decidstart *dis = new fundecidstart($2.pos, $2.sym,
                                                        0, new formals($5));
                    $$ = new typedec($1, dis, $4); }
| USING ID ASSIGN type '(' formals ')' ';'
                  { decidstart *dis = new fundecidstart($2.pos, $2.sym, 0, $6);
                    $$ = new typedec($1, dis, $4); }
;

slice:
 ':'              { $$ = new slice($1, 0, 0); }
| exp ':'          { $$ = new slice($2, $1, 0); }
| ':' exp          { $$ = new slice($1, 0, $2); }
| exp ':' exp      { $$ = new slice($2, $1, $3); }
;

value:
 value '.' ID     { $$ = new fieldExp($2, $1, $3.sym); }
| name '[' exp ']' { $$ = new subscriptExp($2,
                             new nameExp($1->getPos(), $1), $3); }
| value '[' exp ']'{ $$ = new subscriptExp($2, $1, $3); }
| name '[' slice ']' { $$ = new sliceExp($2,
                             new nameExp($1->getPos(), $1), $3); }
| value '[' slice ']'{ $$ = new sliceExp($2, $1, $3); }
| name '(' ')'     { $$ = new callExp($2,
                                     new nameExp($1->getPos(), $1),
                                     new arglist()); }
| name '(' arglist ')'
                  { $$ = new callExp($2,
                                     new nameExp($1->getPos(), $1),
                                     $3); }
| value '(' ')'    { $$ = new callExp($2, $1, new arglist()); }
| value '(' arglist ')'
                  { $$ = new callExp($2, $1, $3); }
| '(' exp ')' %prec EXP_IN_PARENS_RULE
                  { $$ = $2; }
| '(' name ')' %prec EXP_IN_PARENS_RULE
                  { $$ = new nameExp($2->getPos(), $2); }
| THIS_TOK         { $$ = new thisExp($1); }
;

argument:
 exp              { $$.name = symbol::nullsym; $$.val=$1; }
| ID ASSIGN exp    { $$.name = $1.sym; $$.val=$3; }
;

arglist:
 argument         { $$ = new arglist(); $$->add($1); }
| ELLIPSIS argument
                  { $$ = new arglist(); $$->addRest($2); }
| arglist ',' argument
                  { $$ = $1; $$->add($3); }
| arglist ELLIPSIS argument
                  { $$ = $1; $$->addRest($3); }
;

/* A list of two or more expressions, separated by commas. */
tuple:
 exp ',' exp      { $$ = new arglist(); $$->add($1); $$->add($3); }
| tuple ',' exp    { $$ = $1; $$->add($3); }
;

exp:
 name             { $$ = new nameExp($1->getPos(), $1); }
| value            { $$ = $1; }
| LIT              { $$ = $1; }
| STRING           { $$ = $1; }
/* This is for scaling expressions such as 105cm */
| LIT exp          { $$ = new scaleExp($1->getPos(), $1, $2); }
| '(' name ')' exp
                  { $$ = new castExp($2->getPos(), new nameTy($2), $4); }
| '(' name dims ')' exp
                  { $$ = new castExp($2->getPos(), new arrayTy($2, $3), $5); }
| '+' exp %prec UNARY
                  { $$ = new unaryExp($1.pos, $2, $1.sym); }
| '-' exp %prec UNARY
                  { $$ = new unaryExp($1.pos, $2, $1.sym); }
| OPERATOR exp     { $$ = new unaryExp($1.pos, $2, $1.sym); }
| exp '+' exp      { $$ = new binaryExp($2.pos, $1, $2.sym, $3); }
| exp '-' exp      { $$ = new binaryExp($2.pos, $1, $2.sym, $3); }
| exp '*' exp      { $$ = new binaryExp($2.pos, $1, $2.sym, $3); }
| exp '/' exp      { $$ = new binaryExp($2.pos, $1, $2.sym, $3); }
| exp '%' exp      { $$ = new binaryExp($2.pos, $1, $2.sym, $3); }
| exp '#' exp      { $$ = new binaryExp($2.pos, $1, $2.sym, $3); }
| exp '^' exp      { $$ = new binaryExp($2.pos, $1, $2.sym, $3); }
| exp LT exp       { $$ = new binaryExp($2.pos, $1, $2.sym, $3); }
| exp LE exp       { $$ = new binaryExp($2.pos, $1, $2.sym, $3); }
| exp GT exp       { $$ = new binaryExp($2.pos, $1, $2.sym, $3); }
| exp GE exp       { $$ = new binaryExp($2.pos, $1, $2.sym, $3); }
| exp EQ exp       { $$ = new equalityExp($2.pos, $1, $2.sym, $3); }
| exp NEQ exp      { $$ = new equalityExp($2.pos, $1, $2.sym, $3); }
| exp CAND exp     { $$ = new andExp($2.pos, $1, $2.sym, $3); }
| exp COR exp      { $$ = new orExp($2.pos, $1, $2.sym, $3); }
| exp CARETS exp   { $$ = new binaryExp($2.pos, $1, $2.sym, $3); }
| exp AMPERSAND exp{ $$ = new binaryExp($2.pos, $1, $2.sym, $3); }
| exp BAR       exp{ $$ = new binaryExp($2.pos, $1, $2.sym, $3); }
| exp OPERATOR exp { $$ = new binaryExp($2.pos, $1, $2.sym, $3); }
| exp INCR exp     { $$ = new binaryExp($2.pos, $1, $2.sym, $3); }
| NEW celltype
                  { $$ = new newRecordExp($1, $2); }
| NEW celltype dimexps
                  { $$ = new newArrayExp($1, $2, $3, 0, 0); }
| NEW celltype dimexps dims
                  { $$ = new newArrayExp($1, $2, $3, $4, 0); }
| NEW celltype dims
                  { $$ = new newArrayExp($1, $2, 0, $3, 0); }
| NEW celltype dims arrayinit
                  { $$ = new newArrayExp($1, $2, 0, $3, $4); }
| NEW celltype '(' ')' blockstm
                  { $$ = new newFunctionExp($1, $2, new formals($3), $5); }
| NEW celltype dims '(' ')' blockstm
                  { $$ = new newFunctionExp($1,
                                            new arrayTy($2->getPos(), $2, $3),
                                            new formals($4),
                                            $6); }
| NEW celltype '(' formals ')' blockstm
                  { $$ = new newFunctionExp($1, $2, $4, $6); }
| NEW celltype dims '(' formals ')' blockstm
                  { $$ = new newFunctionExp($1,
                                            new arrayTy($2->getPos(), $2, $3),
                                            $5,
                                            $7); }
| exp '?' exp ':' exp
                  { $$ = new conditionalExp($2, $1, $3, $5); }
| exp ASSIGN exp   { $$ = new assignExp($2, $1, $3); }
| '(' tuple ')'    { $$ = new callExp($1, new nameExp($1, SYM_TUPLE), $2); }
| exp join exp %prec JOIN_PREC
                  { $2->pushFront($1); $2->pushBack($3); $$ = $2; }
| exp dir %prec DIRTAG
                  { $2->setSide(camp::OUT);
                    joinExp *jexp =
                        new joinExp($2->getPos(), SYM_DOTS);
                    $$=jexp;
                    jexp->pushBack($1); jexp->pushBack($2); }
| INCR exp %prec UNARY
                  { $$ = new prefixExp($1.pos, $2, SYM_PLUS); }
| DASHES exp %prec UNARY
                  { $$ = new prefixExp($1.pos, $2, SYM_MINUS); }
/* Illegal - will be caught during translation. */
| exp INCR %prec UNARY
                  { $$ = new postfixExp($2.pos, $1, SYM_PLUS); }
| exp SELFOP exp   { $$ = new selfExp($2.pos, $1, $2.sym, $3); }
| QUOTE '{' fileblock '}'
                  { $$ = new quoteExp($1, $3); }
;

// This verbose definition is because leaving empty as an expansion for dir
// made a whack of reduce/reduce errors.
join:
 DASHES           { $$ = new joinExp($1.pos,$1.sym); }
| basicjoin %prec JOIN_PREC
                  { $$ = $1; }
| dir basicjoin %prec JOIN_PREC
                  { $1->setSide(camp::OUT);
                    $$ = $2; $$->pushFront($1); }
| basicjoin dir %prec JOIN_PREC
                  { $2->setSide(camp::IN);
                    $$ = $1; $$->pushBack($2); }
| dir basicjoin dir %prec JOIN_PREC
                  { $1->setSide(camp::OUT); $3->setSide(camp::IN);
                    $$ = $2; $$->pushFront($1); $$->pushBack($3); }
;

dir:
 '{' CURL exp '}' { $$ = new specExp($2.pos, $2.sym, $3); }
| '{' exp '}'      { $$ = new specExp($1, symbol::opTrans("spec"), $2); }
| '{' exp ',' exp '}'
                  { $$ = new specExp($1, symbol::opTrans("spec"),
                                     new pairExp($3, $2, $4)); }
| '{' exp ',' exp ',' exp '}'
                  { $$ = new specExp($1, symbol::opTrans("spec"),
                                     new tripleExp($3, $2, $4, $6)); }
;

basicjoin:
 DOTS             { $$ = new joinExp($1.pos, $1.sym); }
| DOTS tension DOTS
                  { $$ = new joinExp($1.pos, $1.sym); $$->pushBack($2); }
| DOTS controls DOTS
                  { $$ = new joinExp($1.pos, $1.sym); $$->pushBack($2); }
| COLONS           { $$ = new joinExp($1.pos, $1.sym); }
| LONGDASH         { $$ = new joinExp($1.pos, $1.sym); }
;

tension:
 TENSION exp      { $$ = new binaryExp($1.pos, $2, $1.sym,
                             new booleanExp($1.pos, false)); }
| TENSION exp AND exp
                  { $$ = new ternaryExp($1.pos, $2, $1.sym, $4,
                             new booleanExp($1.pos, false)); }
| TENSION ATLEAST exp
                  { $$ = new binaryExp($1.pos, $3, $1.sym,
                             new booleanExp($2.pos, true)); }
| TENSION ATLEAST exp AND exp
                  { $$ = new ternaryExp($1.pos, $3, $1.sym, $5,
                             new booleanExp($2.pos, true)); }
;

controls:
 CONTROLS exp     { $$ = new unaryExp($1.pos, $2, $1.sym); }
| CONTROLS exp AND exp
                  { $$ = new binaryExp($1.pos, $2, $1.sym, $4); }
;

stm:
 ';'              { $$ = new emptyStm($1); }
| blockstm         { $$ = $1; }
| stmexp ';'       { $$ = $1; }
| IF '(' exp ')' stm
                  { $$ = new ifStm($1, $3, $5); }
| IF '(' exp ')' stm ELSE stm
                  { $$ = new ifStm($1, $3, $5, $7); }
| WHILE '(' exp ')' stm
                  { $$ = new whileStm($1, $3, $5); }
| DO stm WHILE '(' exp ')' ';'
                  { $$ = new doStm($1, $2, $5); }
| FOR '(' forinit ';' fortest ';' forupdate ')' stm
                  { $$ = new forStm($1, $3, $5, $7, $9); }
| FOR '(' type ID ':' exp ')' stm
                  { $$ = new extendedForStm($1, $3, $4.sym, $6, $8); }
| BREAK ';'        { $$ = new breakStm($1); }
| CONTINUE ';'     { $$ = new continueStm($1); }
| RETURN_ ';'       { $$ = new returnStm($1); }
| RETURN_ exp ';'   { $$ = new returnStm($1, $2); }
;

stmexp:
 exp              { $$ = new expStm($1->getPos(), $1); }
;

blockstm:
 block            { $$ = new blockStm($1->getPos(), $1); }
;

forinit:
 /* empty */      { $$ = 0; }
| stmexplist       { $$ = $1; }
| barevardec       { $$ = $1; }
;

fortest:
 /* empty */      { $$ = 0; }
| exp              { $$ = $1; }
;

forupdate:
 /* empty */      { $$ = 0; }
| stmexplist       { $$ = $1; }
;

stmexplist:
 stmexp           { $$ = new stmExpList($1->getPos()); $$->add($1); }
| stmexplist ',' stmexp
                  { $$ = $1; $$->add($3); }
;