%{ #define YACC #include "m.h" static char buf[BUFSIZ]; static int argtype; static int fntype; static int argclass; static Symbolptr fnptr; int echo = 0; int inmat = 0; int inloop = 0; int instmt = 0; int insw = 0; int indef = 0; int yylex( void); %} %union { Symbolptr sym; Instptr inst; int narg; } %token NUMBER STRING VAR FUNCTION PROCEDURE FN PROC CMD WHILE IF ELSE FOR BREAK CONTINUE CASE DEFAULT SWITCH DO RETURN String matrix vector DOUBLE INT VOID EXTERN STATIC AUTO CONST %token RIGHT_OP %type name sname type vtype class io_opt %type asgn cexpr expr expr_stmt stmt begin optexpr opt while cond end endlp mat beginmat if for else stmtlist colon Q do dowhile and or switch case default const optcond optc comp_stmt fn_stmt cmd %type arglist matlist fn_args fn_arglist dec declist namelist cmdlist varlist %left ',' /* = += -= *= /= %= */ %right '=' ADD_ASSIGN SUB_ASSIGN MUL_ASSIGN DIV_ASSIGN MOD_ASSIGN /* ^= .*= +/ -/ * / // +\ *\ */ EXPE DOTME PS MS TS DS PBS TBS %right '?' %left ':' %left OR_OP /* || */ %left AND_OP /* && */ %left EQ_OP NE_OP /* == != */ %left '<' LE_OP '>' GE_OP /* < <= > >= */ %left '+' '-' %left '*' '/' '%' DOTM /* * / % .* */ %right '!' UNARYMINUS INC_OP DEC_OP /* ! - ++ -- */ %right '^' %left '\'' %% list: /* nothing */ { if( fin == stdin && !batch) prompt(); } | list defn { cleanup(1); initcode(); } | list stmt { if( pp != prog) { code(NULL); YYABORT; } else if( fin == stdin && !batch) prompt(); } | list expr ';' { code2( m_print, NULL); YYABORT; } | list asgn ';' { code2( (Inst)pop, NULL); YYABORT; } | list error ';' { yyerrok; cleanup(1); initcode(); if( fin == stdin && !batch) prompt(); } ; class: EXTERN { argclass = EXTERN; } | STATIC { argclass = STATIC; } | AUTO { argclass = AUTO; } ; type: matrix { argtype = MATRIX; } | String { argtype = STRING; } | vector { argtype = VECTOR; } | INT { argtype = INT; } | DOUBLE { argtype = DOUBLE; } ; vtype: type | VOID { argtype = VOID; } ; defn: vtype name { code(NULL); /* space for pointer to defn */ if( $2->type == FN || $2->type == PROC || $2->type == CMD) $2 = install( $2->name, UNDEF, NEW, 0, 0); if( $2->type == PROCEDURE || $2->type == FUNCTION) fnptr = install( $2->name, UNDEF, NEW, 0, 0); else { clear($2); fnptr = $2; } if( (fntype = argtype) == VOID) fnptr->type = PROCEDURE; else fnptr->type = FUNCTION; fnptr->class = fntype; indef = ++instmt; fn_start(); /* io.c saves start position of defn */ argtype = MATRIX; } '(' fn_args ')' { Symbolptr s; if( (s=lookup("narg")) == NULL) s = install( "narg", UNDEF, NEW, 0, 0); fnarg( INT, s); --pp; /* fnarg() codes a useless type instruction */ } fn_stmt { int i; Symbolptr s; for( i = 0; i <= $5; ++i) { s = pop(); free(s->name); s->name = NULL; } code2( proc_ret, NULL); if( fnptr == $2) define( $2, $5); else { if( pp - prog - $5 - 3 > 0) { /* size > 0 */ clear($2); $2->type = fnptr->type; $2->class = fnptr->class; fnptr->name = NULL; define( $2, $5); } free( fnptr->name); fnptr->name = NULL; fnptr->type = UNDEF; fnptr->class = CONST; } --instmt; indef = 0; } ; asgn: VAR '=' expr { varpush($1); code( m_copy); $$=$3; } | VAR begin varlist '=' expr { varpush($1); code2( m_assign, (Inst)$3); $$=$2; } /* | VAR begin varlist ADD_ASSIGN expr { $$ = $2; } */ | VAR ADD_ASSIGN expr { varpush($1); code2( m_dyadic, (Inst)'+'); varpush($1); code( m_copy); $$=$3; } | VAR SUB_ASSIGN expr { varpush($1); code3(swap, m_dyadic, (Inst)'-'); varpush($1); code( m_copy); $$=$3; } | VAR DOTME expr { varpush($1); code2( m_dyadic, (Inst)'*'); varpush($1); code( m_copy); $$=$3; } | VAR DIV_ASSIGN expr { varpush($1); code3(swap, m_dyadic, (Inst)'/'); varpush($1); code( m_copy); $$=$3; } | VAR EXPE expr { varpush($1); code3(swap, m_dyadic, (Inst)'^'); varpush($1); code( m_copy); $$=$3; } | VAR MUL_ASSIGN expr { varpush($1); code2( swap, m_mul); varpush($1); code( m_copy); $$=$3; } | VAR MOD_ASSIGN expr { varpush($1); code3(swap, m_dyadic, (Inst)'%'); varpush($1); code( m_copy); $$=$3; } ; name: VAR | FN | PROC | FUNCTION | PROCEDURE | CMD ; sname: name | STRING | AUTO | BREAK | CASE | CONTINUE | DEFAULT | DO | DOUBLE | ELSE | EXTERN | FOR | IF | INT | matrix | RETURN | STATIC | String | SWITCH | vector | VOID | WHILE ; io_opt: '<' { $$ = <sym; } | '>' { $$ = >sym; } | RIGHT_OP { $$ = >GTsym; } ; namelist: name { $$ = 1; mklocal($1,argclass,NULL); } | name '=' expr { $$ = 1; mklocal($1,argclass,$3); } | namelist ',' name { $$ = $1 + 1; mklocal($3,argclass,NULL); } | namelist ',' name '=' expr { $$ = $1 + 1; mklocal($3,argclass,$5); } ; dec: type namelist { $$ = $2; } | class type namelist { $$ = $3; } ; fn_args: /* nothing */ { $$ = 0; } | VOID { $$ = 0; } | fn_arglist { $$ = $1; } ; fn_arglist: name { $$ = 1; fnarg(argtype,$1); } | type name { $$ = 1; fnarg(argtype,$2); } | fn_arglist ',' name { $$ = $1 + 1; fnarg(argtype,$3); } | fn_arglist ',' type name { $$ = $1 + 1; fnarg(argtype,$4); } ; declist: /* nothing */ { $$ = 0; argclass = AUTO; } | declist dec ';' { $$ = $1 + $2; argclass = AUTO; } ; stmtlist: /* nothing */ { $$ = pp; } | stmtlist stmt ; fn_stmt: ';' { $$ = pp; } | comp_stmt ; comp_stmt: '{' begin declist stmtlist '}' /* compound statement */ { $$ = $2; if( $3 > 0) { int i, count = 0; Symbolptr s; for( i = 0; i < $3; ++i) { s = pop(); if( s->class == STATIC) { free(s->name); s->name = NULL; } else if( s->class == AUTO) { ++count; free(s->name); s->name = NULL; } } if( count) code2( m_uninstall, (Inst)count); } } ; cmdlist: sname { $$ = 1; varpush($1); } | cmdlist sname { $$ = $1 + 1; varpush($2); } | io_opt sname { $$ = 2; code2( sympush, d2i($1)); varpush($2); } | cmdlist io_opt sname { $$ = $1 + 2; code2( sympush, d2i($2)); varpush($3); } ; cmd: CMD { $$ = code3( fn, (Inst)$1->u.fn, (Inst)0); } | CMD begin cmdlist { $$ = $2; code3( fn, (Inst)$1->u.fn, (Inst)$3); } ; stmt: cmd ';' | expr_stmt | comp_stmt | do stmt dowhile cond ';' endlp { ($1)[1] = (Inst)($4-$1-1); /* condition */ ($1)[2] = (Inst)($6-$1-1); /* next stmt */ } | while cond stmt endlp { ($1)[1] = (Inst)($3-$1-1); /* body */ ($1)[2] = (Inst)($4-$1-1); /* next stmt */ } | if cond stmt end { ($1)[1] = (Inst)($3-$1-1); /* then part */ ($1)[3] = (Inst)($4-$1-1); /* next stmt */ --instmt; } | if cond stmt end else stmt end { ($1)[1] = (Inst)($3-$1-1); /* then part */ ($1)[2] = (Inst)($6-$1-1); /* else part */ ($1)[3] = (Inst)($7-$1-1); /* next stmt */ --instmt; } | for '(' optexpr ';' optcond ';' optexpr ')' stmt endlp { ($1)[1] = (Inst)($5-$1-1); /* condition */ ($1)[2] = (Inst)($7-$1-1); /* ending */ ($1)[3] = (Inst)($9-$1-1); /* body */ ($1)[4] = (Inst)($10-$1-1); /* next */ } | BREAK ';' { $$ = code2( m_break, NULL); if( !inloop && !insw) { yyerror( "syntax error: `break' not in loop or switch"); YYERROR; } } | CONTINUE ';' { $$ = code2( m_continue, NULL); if( !inloop) { yyerror( "syntax error: `continue' not in loop"); YYERROR; } } | switch cond stmt end { --instmt; --insw; ($1)[1] = (Inst)($3-$1-1); /* body */ ($1)[2] = (Inst)($4-$1-1); /* next stmt */ if( *($3) != m_case && *($3) != m_default) yyerror("warning: initial non-case/default statement in switch WILL be executed"); } | case const ':' stmt { if( !insw) { yyerror( "syntax error: `case' not in switch"); YYERROR; } } | default ':' stmt { if( !insw) { yyerror( "syntax error: `default' not in switch"); YYERROR; } } | RETURN ';' { $$ = code2( proc_ret, NULL); if( fntype != VOID) { yyerror("syntax error: non-void function must return a value"); YYERROR; } if( !indef) { yyerror( "syntax error: `return' not in fn/proc"); YYERROR; } } | RETURN begin cexpr ';' { $$ = $2; code2( func_ret, NULL); if( fntype == VOID) { yyerror("syntax error: void function cannot return a value"); YYERROR; } if( !indef) { yyerror( "syntax error: `return' not in fn/proc"); YYERROR; } } ; switch: SWITCH { ++instmt; ++insw; $$ = code3( m_switch, NULL, NULL); } ; case: CASE { $$ = code( m_case); } ; default: DEFAULT { $$ = code( m_default); } ; cond: '(' cexpr ')' { code(NULL); $$ = $2; } ; cexpr: expr /* comma operator kludge */ | cexpr ',' { code( (Inst)pop); } expr ; const: NUMBER { $$ = code( d2i($1)); } | '-' NUMBER { $$ = code( d2i($2)); VAL($2) = - VAL($2); } ; do: DO { ++instmt; ++inloop; $$ = code3( m_do, NULL, NULL); } ; while: WHILE { ++instmt; ++inloop; $$ = code3( m_while, NULL, NULL); } ; dowhile: WHILE { $$ = code(NULL); } ; if: IF { ++instmt; $$ = code4( m_if, NULL, NULL, NULL); } ; else: ELSE { ++instmt; $$ = pp; } ; for: FOR { ++instmt; ++inloop; $$ = code( m_for); code4( NULL, NULL, NULL, NULL); } ; optexpr: opt { code(NULL); } ; opt: /* nothing */ { $$ = pp; } | cexpr { code( (Inst)pop); } ; optcond: optc { code(NULL); } ; optc: /* nothing */ { $$ = pp; } | cexpr ; end: /* nothing */ { code(NULL); $$ = pp; } ; endlp: /* nothing */ { --instmt; --inloop; code(NULL); $$ = pp; } ; begin: /* nothing */ { $$ = pp; } ; expr_stmt: ';' { $$ = pp; } | cexpr ';' { code( (Inst)pop); } | '(' VOID ')' expr ';' { code( (Inst)pop); $$ = $4; } | PROC ';' { $$ = code3( fn, (Inst)$1->u.fn, (Inst)0); } | PROC begin '(' arglist ')' ';' { code3( fn, (Inst)$1->u.fn, (Inst)$4); $$=$2; } | PROCEDURE ';' { $$ = code3( call, d2i($1), (Inst)0); } | PROCEDURE begin '(' arglist ')' ';' { code3( call, d2i($1), (Inst)$4); $$=$2; } ; expr: asgn | VAR { $$ = varpush($1); } | VAR begin varlist { varpush($1); code2( m_index, (Inst)$3); $$ = $2; } | INC_OP VAR { $$ = varpush($2); code2( m_pp, (Inst)1); } | INC_OP VAR begin varlist { $$ = $3; varpush($2); code3( m_pp2, (Inst)1, (Inst)$4); } | DEC_OP VAR { $$ = varpush($2); code2( m_pp, (Inst)-1); } | DEC_OP VAR begin varlist { $$ = $3; varpush($2); code3( m_pp2, (Inst)-1, (Inst)$4); } | VAR INC_OP { $$ = varpush($1); code3( sympush, NULL, m_copy); varpush($1); code3( m_pp, (Inst)1, (Inst)pop); } | VAR DEC_OP { $$ = varpush($1); code3( sympush, NULL, m_copy); varpush($1); code3( m_pp, (Inst)-1, (Inst)pop); } | FN { $$ = code3( fn, (Inst)$1->u.fn, (Inst)0); } | FN begin '(' arglist ')' { code3( fn, (Inst)$1->u.fn, (Inst)$4); $$=$2; } | FUNCTION { $$ = code3( call, d2i($1), (Inst)0); } | FUNCTION begin '(' arglist ')' { code3( call, d2i($1), (Inst)$4); $$ = $2; } | STRING { $$ = code2( sympush, d2i($1)); } | NUMBER { $$ = code2( sympush, d2i($1)); } | mat { code( matpop); } | '(' matrix ')' expr /* casts */ %prec UNARYMINUS { $$ = $4; code( m_matrix); } | '(' vector ')' expr %prec UNARYMINUS { $$ = $4; code( m_matrix); } | '(' DOUBLE ')' expr %prec UNARYMINUS { $$ = $4; code( m_matrix); } | '(' String ')' expr %prec UNARYMINUS { $$ = $4; code( m_string); } | '(' INT ')' expr %prec UNARYMINUS { $$ = $4; code2( m_unary, (Inst)INT); } | PS expr { code2( m_reduce, (Inst)PS); $$ = $2; } | MS expr { code2( m_reduce, (Inst)MS); $$ = $2; } | TS expr { code2( m_reduce, (Inst)TS); $$ = $2; } | DS expr { code2( m_reduce, (Inst)DS); $$ = $2; } | PBS expr { code( m_pbs); $$ = $2; } | TBS expr { code( m_tbs); $$ = $2; } /* unary ops */ | '-' expr %prec UNARYMINUS { Symbolptr a = i2d(($2)[1]); if( pp - $2 == 2 && *($2) == sympush && !a->name && SCALAR(a)) VAL(a) = - VAL(a); /* NUMBER */ else code2( m_unary, (Inst)'-'); $$ = $2; } | '!' expr { code2( m_unary, (Inst)'!'); $$ = $2; } | expr '\'' { code( m_transpose); } | expr '*' expr { code( m_mul); } /* dyadic ops */ | expr '+' expr { code2( m_dyadic, (Inst)'+'); } | expr '-' expr { code2( m_dyadic, (Inst)'-'); } | expr '/' expr { code2( m_dyadic, (Inst)'/'); } | expr '^' expr { code2( m_dyadic, (Inst)'^'); } | expr '%' expr { code2( m_dyadic, (Inst)'%'); } | expr DOTM expr { code2( m_dyadic, (Inst)'*'); } | expr EQ_OP expr { code2( m_dyadic, (Inst)EQ_OP); } | expr NE_OP expr { code2( m_dyadic, (Inst)NE_OP); } | expr '<' expr { code2( m_dyadic, (Inst)'<'); } | expr LE_OP expr { code2( m_dyadic, (Inst)LE_OP); } | expr '>' expr { code2( m_dyadic, (Inst)'>'); } | expr GE_OP expr { code2( m_dyadic, (Inst)GE_OP); } | '(' expr ')' { $$ = $2; } | expr or expr %prec OR_OP { code(NULL); ($2)[1] = (Inst)(pp-$2-1); } | expr and expr %prec AND_OP { code(NULL); ($2)[1] = (Inst)(pp-$2-1); } | expr Q expr ':' end expr %prec '?' { code(NULL); ($2)[1] = (Inst)($6-$2-1); ($2)[2] = (Inst)(pp-$2-1); } ; and: AND_OP { $$ = code2( m_and, NULL); } ; or: OR_OP { $$ = code2( m_or, NULL); } ; Q: '?' { $$ = code3( m_cond, NULL, NULL); } ; mat: '[' beginmat matlist ']' { Symbolptr s; --inmat; $$ = $2; s = install( NULL, NUMBER, indef ? DEFN : (inloop ? LOOP : CONST), 1, 1); VAL(s) = $3; code2( sympush, d2i(s)); } ; beginmat: /* nothing */ { ++inmat; $$ = pp; } ; matlist: arglist { $$ = $1; } | matlist rowmark arglist { $$ = $1 + $3 + 1; } ; rowmark: ';' { code2( sympush, d2i(&rowend)); } ; arglist: /* nothing */ { $$ = 0; } | expr { $$ = 1; } | colon { $$ = 1; } | arglist ',' expr { $$ = $1 + 1; } | arglist ',' colon { $$ = $1 + 1; } ; colon: ':' { $$ = code2( sympush, NULL); } | expr ':' expr { code( m_colon); } /* | expr ':' | ':' expr { $$ = $2; } */ ; varlist: '(' arglist ')' { $$ = $2; if( $2 > 2) { yyerror( "syntax error: too many indices for variable"); YYERROR; } } ; %% #include static int follow( expect, ifyes, ifno) int expect, ifyes, ifno; { int c = m_getc(); if( c == expect) return ifyes; m_ungetc( c); return ifno; } void skip(); int prevc = 0; int yylex( void) { int c, d = ','; while( (c=m_getc()) == ' ' || c == '\t') d = c; if( c == EOF) { if( fin == stdin) return prevc = 0; else { fclose(fin); fin = nextfile(); if( fin == stdin && !batch) prompt(); return prevc = yylex(); } } if( inmat && isspace(d) && c != ']' && c != ';' && prevc != ',' && prevc != '[' && prevc != ';') { m_ungetc( c); return prevc = ','; } if( c == '"') { /* STRING */ char *p; for( p = buf; (c=m_getc()) != '"'; p++) { if( c == EOF) error("missing quote",""); if( p >= buf + sizeof(buf) - 1) error("string too long",""); *p = c; if( c == '\\') switch( d=m_getc()) { case 'n': *p = '\n'; break; case '\\': *p = '\\'; break; case 't': *p = '\t'; break; case '"': *p = '"'; break; default: m_ungetc(d); break; } } *p = '\0'; yylval.sym = install( NULL, STRING, indef ? DEFN : (inloop ? LOOP : CONST), 1, strlen(buf)); if( (STR(yylval.sym) = strdup(buf)) == NULL) error("strdup() failed in yylex()",""); return prevc = STRING; } if( c == '.') { if( (d=m_getc()) == '*') return prevc = follow( '=', DOTME, DOTM); else m_ungetc(d); } if( c == '.' || isdigit(c)) { /* number */ double x; m_ungetc( c); if( m_scanf( &x) != 1) { m_gets(); error("bad number",""); } yylval.sym = install( NULL, NUMBER, indef ? DEFN : (inloop ? LOOP : CONST), 1, 1); VAL(yylval.sym) = x; return prevc = NUMBER; } if( isalpha(c) || c == '_' || c == '$') { /* name */ Symbolptr s; char *p = buf; do { *p++ = c; } while( (c=m_getc())!=EOF && (isalnum(c) || c == '_' || c == '$')); m_ungetc(c); *p = '\0'; if( (s=lookup(buf)) == NULL) s = install( buf, UNDEF, NEW, 0, 0); yylval.sym = s; d = s->type; return prevc = (d == UNDEF || d == NUMBER || d == STRING || d == MATRIX) ? VAR : d; } switch(c) { case '+': switch(d = m_getc()) { case '+': return prevc = INC_OP; case '=': return prevc = ADD_ASSIGN; case '/': return prevc = PS; case '\\': return prevc = PBS; default: m_ungetc(d); return prevc = c; }; break; case '-': switch(d = m_getc()) { case '-': return prevc = DEC_OP; case '=': return prevc = SUB_ASSIGN; case '/': return prevc = MS; default: m_ungetc(d); return prevc = c; }; break; case '*': switch(d = m_getc()) { case '=': return prevc = MUL_ASSIGN; case '/': return prevc = TS; case '\\': return prevc = TBS; default: m_ungetc(d); return prevc = c; }; break; case '/': switch(d = m_getc()) { case '=': return prevc = DIV_ASSIGN; case '/': return prevc = DS; case '*': skip(); return prevc = yylex(); default: m_ungetc(d); return prevc = c; }; break; case '%': return prevc = follow( '=', MOD_ASSIGN, '%'); case '^': return prevc = follow( '=', EXPE, '^'); case '|': return prevc = follow( '|', OR_OP, '|'); case '&': return prevc = follow( '&', AND_OP, '&'); case '=': return prevc = follow( '=', EQ_OP, '='); case '!': return prevc = follow( '=', NE_OP, '!'); case '<': return prevc = follow( '=', LE_OP, '<'); case '>': switch( d=m_getc()) { case '=': return prevc = GE_OP; case '>': return prevc = RIGHT_OP; default: m_ungetc(d); return prevc = '>'; }; break; /* return prevc = follow( '=', GE_OP, '>'); */ case '{': ++instmt; return prevc = c; case '}': --instmt; return prevc = c; case '\n': if(inmat) return prevc = ';'; else if(instmt) return prevc = yylex(); else return prevc = ';'; default: return prevc = c; } /*NOTREACHED*/ } void skip() /* skip comments */ { int c, d; c = 0; while( (d = m_getc()) != EOF) if( c == '*' && d == '/') return; else c = d; error("EOF before end of comment",""); } #if YYDEBUG && YYBISON const char *bison_translate( x) int x; { int yychar1; yychar1 = YYTRANSLATE( x); if( yychar1 == 2) /* illegal */ return NULL; else return yytname[yychar1]; } #endif