%{ /***** * 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 ID SELFOP DOTS COLONS DASHES INCR LONGDASH CONTROLS TENSION ATLEAST CURL COR CAND BAR AMPERSAND EQ NEQ LT LE GT GE CARETS '+' '-' '*' '/' '%' '#' '^' OPERATOR %token LOOSE ASSIGN '?' ':' DIRTAG JOIN_PREC AND '{' '}' '(' ')' '.' ',' '[' ']' ';' ELLIPSIS ACCESS UNRAVEL IMPORT INCLUDE FROM QUOTE STRUCT TYPEDEF NEW IF ELSE WHILE DO FOR BREAK CONTINUE RETURN_ THIS_TOK EXPLICIT GARBAGE %token LIT %token STRING %token PERM %token 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 fileblock bareblock block %type name %type runnable %type modifiers %type dec fundec typedec %type strid %type idpair stridpair %type idpairlist stridpairlist %type vardec barevardec %type type celltype %type dims %type decidlist %type decid %type decidstart %type varinit %type arrayinit basearrayinit varinits %type formal decdec %type formals decdeclist %type value exp fortest %type argument %type slice %type join basicjoin %type tension controls %type dir %type dimexps %type arglist tuple %type stm stmexp blockstm %type forinit %type forupdate stmexplist %type explicitornot %type typeparam %type 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();'"); } /* 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 () as ;'." ); } | 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); } ; 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); } ;