/* $XConsortium: looper.yacc,v 5.5 94/04/17 20:46:22 hersh Exp $ */ /***************************************************************** Copyright (c) 1989, 1990, 1991 X Consortium Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of the X Consortium shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the X Consortium. Copyright (c) 1989, 1990, 1991 by Sun Microsystems, Inc. All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the names of Sun Microsystems, and the X Consortium, not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ******************************************************************/ /* * looper.grammar: * Yacc grammar, with actions, for the looper end of Newt. * The lexical analyzer is specified in looper.lex. * * Also contains the functions for the building and execution of the * structures corresponding to looper functions. */ %{ #ifndef lint static char sccsid[] = "@(#)looper.yacc 5.2 2/13/91 Copyr. Sun Microsystems"; #endif #include #include #ifdef SYSV #define index strchr #define rindex strrchr #include #else #include #include #endif #include "looper.h" #include "debugger.h" #include "var.h" #include "interpret.h" #define YYMAXDEPTH 500 /* stack size for parsing */ #define MAXLINELEN 1024 /* maximum size of predicate output line */ extern char *optarg; /* for getopt */ extern int optind, opterr; /* *execution modes: */ static int interactive = 0; /* nonzero value indicates interactive mode */ static int printonly = 0; /* nonzero means just print looper output, */ /* rather than interpret. */ static int trace = 0; /* nonzero means print out interpreted */ /* commands before passing on to interpreter*/ int N_translate = 0; /* translate-to-C mode */ static MACROVARLIST *macro_vars = NULL; /* list of variables being used */ /* for macro definition */ static char *macro_name = NULL; /* name of macro currently being defined */ static FUNC *macro_list = NULL; /* list of currently defined macros */ char *N_filepath; /* path of directories in which to search for files */ static char *tmpstr1, *tmpstr2; /* needed to workaround sun4 compiler prob */ /* we keep a stack of recursively-invoked source files.*/ #define MAXFILES 16 static FILE *filestack[MAXFILES]; static char *filenamestack[MAXFILES]; static int filelinestack[MAXFILES] = {0}; static int fileindex = 0; static char *filename = "standard input"; /* current file being parsed */ static int lineno = 1; /* line number in file being parsed */ static char *expand_arg(), *copy_string(); /* for debugging mode */ static int debug=0; /* 1 if debug is ON, 0 otherwise */ static int syntaxerr=0; /* 1 if syntax error occured, 0 otherwise */ FUNC *N_funchead; /* pointer to start of function structure */ static FUNC *functail; /* pointer to end of function structure */ int N_2process; /* 2 process debugging mode */ #define IFSTACK 0 #define FORSTACK 1 #define FOREACHSTACK 2 static char *curr_debug_file; /* file currently being debugged */ /* for translation mode */ static int countervars = 0; /* number of counter variables to declare */ static int prepend_underscore = 0; /* 1 means add "_" before predicates */ static char *headertext_file = NULL; /* file to insert at head of C prog */ %} /* union defining the possible value types */ %union { char *strval; FUNC *funval; ARGTYPE *argval; } %token LEFT RIGHT SEMI COMMA EQUAL FOREACH FOR END IF THEN ELSE ENDIF %token MACRO ENDMACRO READFILE RESUME %token VAR ARG IDENT SHCOMMENT %type fun foreachloop forloop ifstmnt funlist %type varlist var macvarlist macvar looparglist arglist arg %type ident optcomment %% immed_funlist : /* empty */ | immed_funlist immed_fun optsemi ; immed_fun : fun { if (interactive) N_execute($1,EXECUTE_NORMAL); else { functail->next = $1; functail = functail->next; } if (interactive && fileindex==0) fprintf(stderr,">"); fflush(stdout); } | error { static char ln[256]; syntaxerr = 1; fgets(ln, 256, yyin); fprintf(stderr, "looper syntax error, line %d of %s, at or near '%s' -- continuing\n", lineno, filename, yytext);} ; fun: foreachloop {$$ = $1;} | forloop {$$ = $1;} | ifstmnt {$$ = $1;} | ident LEFT arglist RIGHT {$$ = pred(NULL, $1, $3);} | ident EQUAL ident LEFT arglist RIGHT {$$ = pred($1, $3, $5);} | var EQUAL ident LEFT arglist RIGHT {$$ = pred($1->name, $3, $5);} | MACRO ident { start_macro($2);} LEFT macvarlist RIGHT funlist ENDMACRO {$$ = end_macro($2, $5, $7, filename);} | var EQUAL arg {$$ = assign($1, $3);} | READFILE arg { if (fileindex==MAXFILES-1) fatal("Too many files"); filestack[fileindex]=yyin; if ((yyin=N_fopen_from_path($2->name, "r", N_filepath))==NULL) { yyin=filestack[fileindex]; fprintf(stderr,"looper error: could not open file %s -- continuing\n", $2->name); YYERROR; } else { filenamestack[fileindex] = filename; filename = $2->name; filelinestack[fileindex++]=lineno; lineno=1; if (debug) N_debuguse($2->name); } } funlist RESUME { /* note that stack popping occurs in looper.lex.*/ $$ = source($2, $4); } | SHCOMMENT { $$ = comment(yylval.strval); } ; foreachloop: FOREACH varlist LEFT looparglist RIGHT funlist END {$$ = foreach($2, $4, $6);} ; forloop: FOR LEFT var SEMI arg SEMI arg SEMI arg RIGHT funlist END {$$ = forloop($3, $5, $7, $9, $11);} | FOR LEFT var SEMI arg SEMI arg RIGHT funlist END { /* increment left implicit */ $$ = forloop($3, $5, $7, NULL, $9); } | FOR LEFT var SEMI arg RIGHT funlist END { /* start and increment left implicit */ $$ = forloop($3, NULL, $5, NULL, $7); } ; ifstmnt: IF LEFT arg RIGHT optthen funlist ELSE funlist ENDIF {$$ = ifstatement($3, NULL, NULL, $6, $8);} | IF LEFT arg RIGHT optthen funlist ENDIF {$$ = ifstatement($3, NULL, NULL, $6, NULL);} | IF LEFT arg arg arg RIGHT optthen funlist ELSE funlist ENDIF {if (!is_binary_oper($4->name)) { fprintf(stderr,"looper error, line %d of %d: %s is not a valid operator for if-statement condition\n", lineno, filename, $4->name); YYERROR; } else { $$ = ifstatement($3, $4, $5, $8, $10); } } | IF LEFT arg arg arg RIGHT optthen funlist ENDIF {if (!is_binary_oper($4->name)) { fprintf(stderr,"looper error, line %d of %d: %s is not a valid operator for if-statement condition\n", lineno, filename, $4->name); YYERROR; } else { $$ = ifstatement($3, $4, $5, $8, NULL); } } ; funlist: fun optsemi funlist {append_fun($1, $3); $$ = $1;} | fun optsemi {$$ = $1;} ; varlist: var varlist {append_arg($1, $2); $$ = $1;} | var {$$ = $1;} ; macvarlist: /* empty - macro may have no arguments */ {$$ = NULL;} | macvar COMMA macvarlist {append_arg($1, $3); $$ = $1;} | macvar {$$ = $1;} ; var: VAR {$$ = arg(yylval.strval);} ; macvar: VAR { $$ = new_macvar(yylval.strval); } ; looparglist: /* empty */ {$$ = NULL;} | arg SEMI optcomment looparglist {$1->cmt=$3; append_arg($1, $4); $$ = $1;} | arg optcomment {$1->cmt=$2; $$=$1;} ; arglist: /* empty */ {$$ = NULL;} | arg COMMA arglist {append_arg($1, $3); $$ = $1;} | arg {$$=$1;} ; arg: ARG {$$ = arg(yylval.strval);} | var {$$ = $1;} | ident {$$ = arg($1);} ; ident: IDENT {tmpstr1 = (char *) malloc(strlen(yylval.strval)+1); strcpy(tmpstr1, yylval.strval); $$ = tmpstr1;} optcomment: /* empty */ {$$ = NULL;} | SHCOMMENT {tmpstr1 = (char *) malloc(strlen(yylval.strval)+1); strcpy(tmpstr1, yylval.strval); $$ = tmpstr1;} ; optsemi: /* empty */ | SEMI ; optthen: /* empty */ | THEN ; %% #include "lex.yy.c" /* * N_execute() - the main execution routine. * Execute the function structure list pointed to by func, in the manner * appropriate to its type. The execution of the first function is * carried out, and then N_execute is called recursively on the rest of * the list. */ void N_execute(func,exeflag) FUNC *func; int exeflag; { ARGTYPE *arg, *val, *macvar; char *valstring, *varstring, *tmpstr; char outstring[MAXLINELEN], *outptr; FUNC *mac; int curflag=exeflag; int bool; if (debug) { if (!N_isskip || (N_isskip && func->lineno >= N_resumeat)) { if (N_isskip) N_isskip = 0; if (func->lineno) { /* is this line breakable? */ if ((N_getflag(N_fstr->userbreakpt,func->lineno) && exeflag == EXECUTE_NORMAL) || N_isstep || (N_isnext && exeflag == EXECUTE_NORMAL) ) { if (N_isstep && N_stepcount >1) { N_stepcount--; } else if (N_isnext && N_nextcount >1) { N_nextcount--; } else { printf("Debugger: stopped at line %d in file %s\n", func->lineno,N_fstr->name); printf("\t%d %s", func->lineno,N_fstr->fileimage[func->lineno-1]); N_debugger(func->lineno+1); if (N_isskip && func->lineno >= N_resumeat) N_isskip = 0; if (N_isnext) curflag = EXECUTE_BYPASS; } } } } } if (!N_isskip) switch (func->ftype) { case FOREACHLOOP: val = func->fun.fl.values; /* loop runs until we're out of loop variable values */ while(val != NULL) { /* set a value for each loop variable */ for (arg=func->fun.fl.loopvars; arg!=NULL; arg=arg->next) { if (val==NULL) { fatal("wrong number of loopvars\n"); } set_val(arg->name, val->name); val = val->next; } N_execute(func->fun.fl.body,curflag); } break; case FORLOOP: { int i, end, increment; char numstring[12]; /* get start value */ if (func->fun.forl.start==NULL) {i = 1;} else { if((valstring=value(func->fun.forl.start->name))==NULL){ fprintf(stderr, "Looper error: forloop start, %s, has no value\n", func->fun.forl.start->name); exit(1); } i = atoi(valstring); } /* get end value */ if((valstring=value(func->fun.forl.end->name))==NULL){ fprintf(stderr, "Looper error: forloop end, %s, has no value\n", func->fun.forl.start->name); exit(1); } end = atoi(valstring); /* get increment value */ if (func->fun.forl.incr==NULL) {increment = 1;} else { if((valstring=value(func->fun.forl.incr->name))==NULL){ fprintf(stderr, "Looper error: forloop increment, %s, has no value\n", func->fun.forl.start->name); exit(1); } increment = atoi(valstring); } /* iterate over the loop */ for ( ; i <= end; i+=increment) { sprintf(numstring, "%d", i); set_val(func->fun.forl.loopvar->name,numstring); N_execute(func->fun.forl.body,curflag); } } break; case IFSTMNT: if (func->fun.ifstr.oper==NULL) /* the if statement has a unary condition */ bool = eval_condition(func->fun.ifstr.cond); else /* the if statement has a binary operator condition */ bool = eval_binary_oper(func->fun.ifstr.oper->name, func->fun.ifstr.cond, func->fun.ifstr.cond2); if (bool) { if (func->fun.ifstr.thenbody==NULL) fatal("empty if-then body\n"); N_execute(func->fun.ifstr.thenbody, curflag); } else if (func->fun.ifstr.elsebody!=NULL) { N_execute(func->fun.ifstr.elsebody, curflag); } break; case PRED: if (func->fun.pred.dest!=NULL) { /* function assignment can't be a macro */ mac=NULL; } else { /* see whether predicate is a macro invocation */ for(mac=macro_list; mac!=NULL && strcmp(mac->fun.macro.name,func->fun.pred.name)!=0; mac=mac->fun.macro.next_macro); } if (mac!=NULL) { /* is a macro -- must first set vars */ for (arg=func->fun.pred.args, macvar=mac->fun.macro.macvars; (arg != NULL) && (macvar!=NULL); arg=arg->next, macvar=macvar->next) { set_val(macvar->name, arg->name); } if (arg==NULL && macvar!=NULL) { fprintf(stderr, "macro %s: not enough args\n", func->fun.pred.name); exit(1); } if (arg!=NULL && macvar==NULL) { fprintf(stderr, "macro %s: extra args ignored\n", func->fun.pred.name); } /* I think I'm safe using != instead of strcmp...*/ if (debug && curr_debug_file!=mac->fun.macro.fname) { tmpstr = curr_debug_file; N_debuguse(curr_debug_file = mac->fun.macro.fname); N_execute(mac->fun.macro.body,curflag); N_debuguse(curr_debug_file = tmpstr); } else { N_execute(mac->fun.macro.body,curflag); } } else { /* is not a macro invocation -- assemble output line */ outptr = copy_string(outstring, func->fun.pred.name); outptr = copy_string(outptr, "("); for (arg=func->fun.pred.args; arg != NULL; arg=arg->next) { valstring = value(arg->name); if (valstring == NULL) { fprintf(stderr, "Looper error: unknown variable, %s\n", arg->name); exit(1); } if (index(valstring,'@')==NULL) { outptr = copy_string(outptr, valstring); } else { /* must expand variables in arg */ outptr = expand_arg(outptr, valstring); } if (arg->next != NULL) { outptr = copy_string(outptr,", "); } } outptr = copy_string(outptr,")"); if (trace || printonly) { if (func->fun.pred.dest!=NULL) printf("%s = %s\n", value(func->fun.pred.dest), outstring); else printf("%s\n", outstring); } if(!printonly) N_interpret(outstring, value(func->fun.pred.dest)); } break; case MACRODEF: /* put macro at head of macro list */ if (macro_list!=func) { /* Isn't the first macro in list. */ /* Remove from curr. place in macro list if already there*/ if (func->fun.macro.prev_macro!=NULL) func->fun.macro.prev_macro->fun.macro.next_macro= func->fun.macro.next_macro; if (func->fun.macro.next_macro!=NULL) func->fun.macro.next_macro->fun.macro.prev_macro= func->fun.macro.prev_macro; /* insert at head of list of defined macros */ func->fun.macro.next_macro = macro_list; func->fun.macro.prev_macro = NULL; if (macro_list!=NULL) macro_list->fun.macro.prev_macro=func; macro_list = func; } break; case ASSIGN: varstring = func->fun.assign.var->name; valstring = func->fun.assign.val->name; set_val(varstring, valstring); break; case SOURCE: if (debug) { tmpstr = curr_debug_file; N_debuguse(curr_debug_file = func->fun.source.fname); } N_execute(func->fun.source.body, curflag); if (debug) N_debuguse(curr_debug_file = tmpstr); break; case COMMENT: /* do nothing */ break; default: fatal("(internal) -- unknown function type\n"); break; } if (func->next != NULL) { N_execute(func->next,exeflag); } } /* generate_C_file: * generate the C code from the parsed structure supplied, * and output the result into a file whose name is derived from the supplied * filename (or to stdout if filename is null). * two passes are made through the data structure -- the first * passes information to the interpreter in order to derive * variable declaration information, and the second outputs the * actual code. */ static int generate_C_file(func, filename) FUNC *func; char *filename; { FILE *outfile, *hfile; char Cfilename[64], *period; int i, character; if (filename==NULL) outfile=stdout; else { strcpy(Cfilename,filename); if ((period=rindex(Cfilename,'.'))==NULL) strcat(Cfilename,".c"); else strcpy(period,".c"); outfile = fdopen(creat(Cfilename, 0666),"w+"); if (outfile==NULL) { fprintf(stderr, "ERROR - could not create file %s\n",Cfilename); exit(1); } } fprintf(outfile, "/* Automatically-generated translation of %s */\n\n",filename); if (headertext_file) { /* include the file's text in the translation */ if ((hfile=fopen(headertext_file,"r"))==NULL) { fprintf(stderr,"ERROR - could not open header file %s\n", headertext_file); exit(1); } while ((character=getc(hfile))!=EOF) putc((char)character,outfile); fclose(hfile); fflush(outfile); } fprintf(outfile, "\nmain()\n{\n"); /* first pass through the data structure just passes information * to the interpreter in order to get variable declaration info. */ execute_for_trans(func); /* * insert interpreter's declarations, and declaration of loop * counters. */ N_generate_C_declaration(outfile); if (countervars) { fprintf(outfile, "int ctr1"); if (countervars>1) for(i=2; i<=countervars; i++) fprintf(outfile, ", ctr%i", i); fprintf(outfile, ";\n\n"); } /* * generate the program body and closing brace */ countervars=0; translate_func(func, outfile); fprintf(outfile, "}\n"); fclose(outfile); } /* * execute_for_trans: * traverse the function structure to allow the interpreter * to generate a list of the variables that will have to * be declared in order to generate the C translation. */ static int execute_for_trans(func) FUNC *func; { ARGTYPE *arg, *val, *macvar; char *valstring, *varstring, *tmpstr; char outstring[MAXLINELEN], *outptr; FUNC *mac; int i; switch (func->ftype) { case FOREACHLOOP: val = func->fun.fl.values; countervars++; /* * execute with values from value list, to * get declarations for any vars named there. */ for (i=0; val != NULL; i++) { /* set a value for each loop variable */ for (arg=func->fun.fl.loopvars; arg!=NULL; arg=arg->next,val=val->next) { if (val==NULL) { fatal("wrong number of loopvars\n"); } set_val(arg->name, val->name); } N_execute(func->fun.fl.body,EXECUTE_NORMAL); } /* * assign C variable names to looper variables, * traverse the body */ for (arg=func->fun.fl.loopvars; arg!=NULL; arg=arg->next) { set_val(arg->name, arg->name+1); } execute_for_trans(func->fun.fl.body); break; case FORLOOP: { set_val(func->fun.forl.loopvar->name, func->fun.forl.loopvar->name + 1); execute_for_trans(func->fun.forl.body); } break; case IFSTMNT: execute_for_trans(func->fun.ifstr.thenbody); if (func->fun.ifstr.elsebody!=NULL) { execute_for_trans(func->fun.ifstr.elsebody); } break; case PRED: if (func->fun.pred.dest!=NULL) { /* function assignment can't be a macro */ mac=NULL; } else { /* see whether predicate is a macro invocation */ for(mac=macro_list; mac!=NULL && strcmp(mac->fun.macro.name,func->fun.pred.name)!=0; mac=mac->fun.macro.next_macro); } if (mac!=NULL) { /* is a macro -- must first set vars */ for (arg=func->fun.pred.args, macvar=mac->fun.macro.macvars; (arg != NULL) && (macvar!=NULL); arg=arg->next, macvar=macvar->next) { set_val(macvar->name, arg->name); } if (arg==NULL && macvar!=NULL) { fprintf(stderr, "macro %s: not enough args\n", func->fun.pred.name); exit(1); } if (arg!=NULL && macvar==NULL) { fprintf(stderr, "macro %s: extra args ignored\n", func->fun.pred.name); } execute_for_trans(mac->fun.macro.body); } else { /* is not a macro invocation -- assemble output line */ outptr = copy_string(outstring, func->fun.pred.name); outptr = copy_string(outptr, "("); for (arg=func->fun.pred.args; arg != NULL; arg=arg->next) { valstring = value(arg->name); if (valstring == NULL) { fprintf(stderr, "Looper error: unknown variable, %s\n", arg->name); exit(1); } if (index(valstring,'@')==NULL) { outptr = copy_string(outptr, valstring); } else { /* must expand variables in arg */ outptr = expand_arg(outptr, valstring); } if (arg->next != NULL) { outptr = copy_string(outptr,", "); } } outptr = copy_string(outptr,")"); /* interpret to get variable declaration */ N_interpret(outstring,value(func->fun.pred.dest)); } break; case MACRODEF: /* put macro at head of macro list */ if (macro_list!=func) { /* Isn't the first macro in list. */ /* Remove from curr. place in macro list if already there*/ if (func->fun.macro.prev_macro!=NULL) func->fun.macro.prev_macro->fun.macro.next_macro= func->fun.macro.next_macro; if (func->fun.macro.next_macro!=NULL) func->fun.macro.next_macro->fun.macro.prev_macro= func->fun.macro.prev_macro; /* insert at head of list of defined macros */ func->fun.macro.next_macro = macro_list; func->fun.macro.prev_macro = NULL; if (macro_list!=NULL) macro_list->fun.macro.prev_macro=func; macro_list = func; } break; case ASSIGN: varstring = func->fun.assign.var->name; valstring = func->fun.assign.val->name; set_val(varstring, valstring); break; case SOURCE: execute_for_trans(func->fun.source.body); break; case COMMENT: /* do nothing */ break; default: fatal("(internal) -- unknown function type\n"); break; } if (func->next != NULL) { execute_for_trans(func->next); } } /* translate_func: * generate the C code for the given function, writing it into * the supplied file. */ static int translate_func(func,outfile) FUNC *func; FILE *outfile; { ARGTYPE *arg, *val, *macvar; char *valstring, *varstring, *tmpstr; char outstring[MAXLINELEN], *outptr; FUNC *mac; int i; switch (func->ftype) { case FOREACHLOOP: /* translate into a forloop & switch combination */ /* * first, generate header */ val = func->fun.fl.values; countervars++; fprintf(outfile, "for (ctr%d=0; ctr%d<%d; ctr%d++) {\n", countervars, countervars, arglist_length(val)/arglist_length(func->fun.fl.loopvars), countervars); fprintf(outfile, " switch (ctr%d) {\n", countervars); /* * go through the value list, generating switch cases */ for (i=0; val != NULL; i++) { fprintf(outfile, "\tcase %d:\n",i); for (arg=func->fun.fl.loopvars; arg!=NULL; arg=arg->next,val=val->next) { if (val==NULL) { fatal("wrong number of loopvars\n"); } if (val->cmt != NULL) fprintf(outfile, "\t %s = %s; /* %s */\n", arg->name+1,value(val->name), val->cmt+1); else fprintf(outfile, "\t %s = %s;\n", arg->name+1,value(val->name)); } fprintf(outfile, "\tbreak;\n"); } fprintf(outfile, " } \n"); /* * assign C variable names to looper variables, * translate body, close for loop. */ for (arg=func->fun.fl.loopvars; arg!=NULL; arg=arg->next) { set_val(arg->name, arg->name+1); } translate_func(func->fun.fl.body, outfile); fprintf(outfile, "} \n"); break; case FORLOOP: { char *loopvar, *start, *end, *increment; char numstring[12]; /* get start value */ if (func->fun.forl.start==NULL) start = "1"; else { if((start=value(func->fun.forl.start->name))==NULL){ fprintf(stderr, "Looper error: forloop start, %s, has no value\n", func->fun.forl.start->name); exit(1); } } /* get end value */ if((end=value(func->fun.forl.end->name))==NULL){ fprintf(stderr, "Looper error: forloop end, %s, has no value\n", func->fun.forl.start->name); exit(1); } /* get increment value */ if (func->fun.forl.incr==NULL) {increment = "1";} else { if((increment=value(func->fun.forl.incr->name))==NULL){ fprintf(stderr, "Looper error: forloop increment, %s, has no value\n", func->fun.forl.start->name); exit(1); } } /* Generate a for-loop with these values */ loopvar = func->fun.forl.loopvar->name + 1; fprintf(outfile, "for(%s=%s; %s<=%s; %s+=%s) {\n", loopvar,start,loopvar,end,loopvar,increment); set_val(func->fun.forl.loopvar->name,loopvar); translate_func(func->fun.forl.body,outfile); fprintf(outfile, "}\n"); } break; case IFSTMNT: fprintf(outfile, "if (%s", value(func->fun.ifstr.cond->name)); if (func->fun.ifstr.oper != NULL) { fprintf(outfile, " %s %s", func->fun.ifstr.oper->name, value(func->fun.ifstr.cond2->name)); } fprintf(outfile, ") {\n"); translate_func(func->fun.ifstr.thenbody, outfile); if (func->fun.ifstr.elsebody!=NULL) { fprintf(outfile, "} else {\n"); translate_func(func->fun.ifstr.elsebody, outfile); } fprintf(outfile, "}\n"); break; case PRED: if (func->fun.pred.dest!=NULL) { /* function assignment can't be a macro */ mac=NULL; } else { /* see whether predicate is a macro invocation */ for(mac=macro_list; mac!=NULL && strcmp(mac->fun.macro.name,func->fun.pred.name)!=0; mac=mac->fun.macro.next_macro); } if (mac!=NULL) { /* is a macro -- must first set vars */ for (arg=func->fun.pred.args, macvar=mac->fun.macro.macvars; (arg != NULL) && (macvar!=NULL); arg=arg->next, macvar=macvar->next) { set_val(macvar->name, arg->name); } if (arg==NULL && macvar!=NULL) { fprintf(stderr, "macro %s: not enough args\n", func->fun.pred.name); exit(1); } if (arg!=NULL && macvar==NULL) { fprintf(stderr, "macro %s: extra args ignored\n", func->fun.pred.name); } translate_func(mac->fun.macro.body,outfile); } else { /* is not a macro invocation -- assemble output line */ outptr = outstring; if (prepend_underscore) outptr = copy_string(outptr, "_"); outptr = copy_string(outptr, func->fun.pred.name); outptr = copy_string(outptr, "("); for (arg=func->fun.pred.args; arg != NULL; arg=arg->next) { valstring = value(arg->name); if (valstring == NULL) { fprintf(stderr, "Looper error: unknown variable, %s\n", arg->name); exit(1); } if (index(valstring,'@')==NULL) { outptr = copy_string(outptr, valstring); } else { /* must expand variables in arg */ outptr = expand_arg(outptr, valstring); } if (arg->next != NULL) { outptr = copy_string(outptr,", "); } } outptr = copy_string(outptr,")"); if (func->fun.pred.dest!=NULL) fprintf(outfile, " %s = %s;\n", value(func->fun.pred.dest), outstring); else fprintf(outfile, " %s;\n",outstring); } break; case MACRODEF: /* put macro at head of macro list */ if (macro_list!=func) { /* Isn't the first macro in list. */ /* Remove from curr. place in macro list if already there*/ if (func->fun.macro.prev_macro!=NULL) func->fun.macro.prev_macro->fun.macro.next_macro= func->fun.macro.next_macro; if (func->fun.macro.next_macro!=NULL) func->fun.macro.next_macro->fun.macro.prev_macro= func->fun.macro.prev_macro; /* insert at head of list of defined macros */ func->fun.macro.next_macro = macro_list; func->fun.macro.prev_macro = NULL; if (macro_list!=NULL) macro_list->fun.macro.prev_macro=func; macro_list = func; } break; case ASSIGN: varstring = func->fun.assign.var->name; valstring = func->fun.assign.val->name; set_val(varstring, valstring); break; case SOURCE: translate_func(func->fun.source.body, outfile); break; case COMMENT: /* do nothing */ fprintf(outfile, "/* %s */\n", func->fun.cmt.text+1); break; default: fatal("(internal) -- unknown function type\n"); break; } if (func->next != NULL) { translate_func(func->next,outfile); } } /* create a function structure for a predicate with the given destination, name * and argument list, return a pointer to it */ static FUNC * pred(dest, name, args) char *dest, *name; ARGTYPE *args; { FUNC *func; func = (FUNC *) malloc(sizeof(FUNC)); func->ftype = PRED; if (debug) { func->lineno = lineno; N_setflag(N_fstr->breakablept,lineno); } func->next = NULL; func->fun.pred.dest = dest; func->fun.pred.name = name; func->fun.pred.args = args; return(func); } /* create a function structure for a comment */ static FUNC * comment(text) char *text; { FUNC *func; func = (FUNC *) malloc(sizeof(FUNC)); func->ftype = COMMENT; if (debug) { func->lineno = 0; /*zero means non stoppable for the debugger*/ } func->next = NULL; func->fun.cmt.text = (char *)malloc(strlen(text)+1); strcpy(func->fun.cmt.text, text); return(func); } /* initialize an argument structure, with a copy of the given name and a * null next field (substituting long name if arg is a macro variable) */ static ARGTYPE * arg(argname) char *argname; { ARGTYPE *argstruct; MACROVARLIST *mv; argstruct = (ARGTYPE *) malloc(sizeof(ARGTYPE)); argstruct->next = NULL; argstruct->cmt = NULL; if (macro_vars != NULL && is_var(argname)) { /* must see whether argname should be turned into long var */ for(mv=macro_vars; mv!=NULL; mv=mv->next) { if (strcmp(argname+1,mv->shortname)==0) { argstruct->name = (mv->longname); return(argstruct); } } } /* if we got this far, is not a macro variable */ argstruct->name = (char *) malloc(strlen(argname)+1); strcpy(argstruct->name, argname); return(argstruct); } /* append an argument list after arg1 */ static int append_arg(arg1, arglist) ARGTYPE *arg1, *arglist; { arg1->next = arglist; } static int arglist_length(list) ARGTYPE *list; { ARGTYPE *p; int i; for(p=list,i=0; p!=NULL; p=p->next,i++); return(i); } /* append a function list after fun1 */ static int append_fun(fun1, funlist) FUNC *fun1, *funlist; { fun1->next = funlist; } /* allocate and initialize a foreach-loop function structure with the given * arguments */ static FUNC * foreach(vars, vals, funlist) ARGTYPE *vars, *vals; FUNC *funlist; { FUNC *forstruct; forstruct = (FUNC *) malloc(sizeof(FUNC)); forstruct->ftype = FOREACHLOOP; if (debug) { forstruct->lineno = pop(FOREACHSTACK); N_setflag(N_fstr->breakablept,forstruct->lineno); } forstruct->next = NULL; forstruct->fun.fl.loopvars = vars; forstruct->fun.fl.values = vals; forstruct->fun.fl.body = funlist; return(forstruct); } /* allocate and initialize a for-loop function structure with the given * arguments */ static FUNC * forloop(var, start, end, incr, funlist) ARGTYPE *var, *start, *end, *incr; FUNC *funlist; { FUNC *forstruct; forstruct = (FUNC *) malloc(sizeof(FUNC)); forstruct->ftype = FORLOOP; if (debug) { forstruct->lineno = pop(FORSTACK); N_setflag(N_fstr->breakablept,forstruct->lineno); } forstruct->next = NULL; forstruct->fun.forl.loopvar = var; forstruct->fun.forl.start = start; forstruct->fun.forl.end = end; forstruct->fun.forl.incr = incr; forstruct->fun.forl.body = funlist; return(forstruct); } /* allocate and initialize an if-statement structure */ static FUNC * ifstatement(cond,oper,cond2,thenbody,elsebody) ARGTYPE *cond, *oper, *cond2; FUNC *thenbody, *elsebody; { FUNC *ifstruct; ifstruct = (FUNC *) malloc(sizeof(FUNC)); ifstruct->ftype = IFSTMNT; if (debug) { ifstruct->lineno = pop(IFSTACK); N_setflag(N_fstr->breakablept,ifstruct->lineno); } ifstruct->next = NULL; ifstruct->fun.ifstr.cond = cond; ifstruct->fun.ifstr.oper = oper; ifstruct->fun.ifstr.cond2 = cond2; ifstruct->fun.ifstr.thenbody = thenbody; ifstruct->fun.ifstr.elsebody = elsebody; return(ifstruct); } /* build an assigment structure, consisting of variable and value arg */ static FUNC * assign(var, val) ARGTYPE *var, *val; { FUNC *func; func = (FUNC *) malloc(sizeof(FUNC)); func->ftype = ASSIGN; if (debug) { func->lineno = lineno; N_setflag(N_fstr->breakablept,lineno); } func->next = NULL; func->fun.assign.var = var; func->fun.assign.val = val; return(func); } static FUNC * source(file, body) ARGTYPE *file; FUNC *body; { FUNC *func; func = (FUNC *) malloc(sizeof(FUNC)); func->ftype = SOURCE; if (debug) { func->lineno = lineno; N_setflag(N_fstr->breakablept,lineno); } func->next = NULL; func->fun.source.fname = file->name; func->fun.source.body = body; return(func); } /* initiate definition of a macro. */ static start_macro(name) char *name; { /* set global variable indicating name of current macro */ /* (this name will be prepended to variable names) */ macro_name = name; macro_vars = NULL; } /* * new_macvar(): create from a macro variable an expanded variable * name, of the form @MACRO_foo_x, where "foo" is the current macro's * name and x is the original variable name (minus the "@"). This * expanded name, and a pointer to where the original name is within the * expanded string, are stored in a list so that occurrences of the * short name within the macro body are replaced with the long * variable name. */ static ARGTYPE * new_macvar(varname) char *varname; { ARGTYPE *argptr; char *longname; MACROVARLIST *newvar; longname = (char *) malloc(strlen(macro_name)+strlen(varname)+8); strcpy(longname,"@MACRO_"); strcat(longname,macro_name); strcat(longname,"_"); strcat(longname,varname+1); /* leave off leading "@" */ /* add to macro variable list */ newvar = (MACROVARLIST *) malloc(sizeof(MACROVARLIST)); newvar->longname = longname; /* shortname is at end of variable's longname! */ newvar->shortname = longname + strlen(macro_name) + 8; /* put new variable at head of macro variable list */ newvar->next = macro_vars; macro_vars = newvar; /* create and fill argument structure */ argptr = (ARGTYPE *) malloc(sizeof(ARGTYPE)); argptr->name = longname; argptr->next = NULL; return(argptr); } /* Create macro FUNC struct for macro just defined */ static FUNC * end_macro(name, varlist, body, fname) char *name; ARGTYPE *varlist; FUNC *body; char *fname; { FUNC *func; func = (FUNC *) malloc(sizeof(FUNC)); func->ftype = MACRODEF; if (debug) func->lineno = 0; func->next = NULL; func->fun.macro.name = name; func->fun.macro.fname = fname; func->fun.macro.macvars = varlist; func->fun.macro.body = body; func->fun.macro.next_macro = NULL; func->fun.macro.prev_macro = NULL; macro_name = NULL; /* no longer defining a macro */ macro_vars = NULL; /* (could reclaim this storage?) */ return(func); } /* * copy into deststr the expanded form of an argument containing variables, * and return a pointer to the end of the resulting string (the null * terminator). * A variable may be placed first in extended argument if the * "@" is escaped with a preceding "\". */ static char * expand_arg(deststr, orig) char *deststr, *orig; { char tempstr[MAXLINELEN], *curr, /* current position in original string */ *val; /* return value from variable lookup */ int n; /* handle escaped "@" in extended argument */ if (orig[0]=='\\' && orig[1]=='@') { curr = orig+1; } else { curr = orig; } while (*curr != '\0') { if (*curr == '@') { /* extract variable name, look up value, output */ n =strspn(curr+1, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"); strncpy(tempstr,curr,n+1); tempstr[n+1]='\0'; if((val=value(tempstr))==NULL) { fprintf(stderr, "looper error: unknown variable %s in argument %s\n", tempstr, orig); exit(1); } else { if (index(val,'@')==NULL) { deststr = copy_string(deststr,val); } else { /* variable has embedded variables*/ deststr = expand_arg(deststr, val); } } curr += n+1; } else { /* output contents up to '@' or end */ n=strcspn(curr,"@"); strncpy(deststr,curr,n); deststr[n]='\0'; deststr += n; curr += n; } } return (deststr); } /* * return a value of the supplied argument appropriate for * true/false evaluation */ static int eval_condition(arg) struct ARG_STRUCT *arg; { int n, i; float f; char *val; long l; VLIST *vstr; /* instantiate any variables in arg */ if ((val = value(arg->name))==NULL) fatal("if-condition has unknown value\n"); /* figure out what it is */ if (n=sscanf(val, "%i", &i)==1) { /* successfully read an integer */ return(i); } else if (n=sscanf(arg->name, "%f", &f)==1) { /* successfully read a float */ return(f); } else if (printonly) { printf("looper: if-cond unknown due to printonly option; assuming true, continuing\n"); return(1); } else if (N_get_interpreter_const(val, &l)) { return(l); } else if (N_get_interpreter_var(val, &vstr)) { switch(vstr->size) { case 1: return(*((char*)(vstr->addr))); break; case 2: return(*((short*)(vstr->addr))); break; case 4: return(*((long*)(vstr->addr))); break; default: fatal("if-statement condition variable is bad type\n"); } } else fatal("if-statement condition is unknown\n"); } /* * return true iff the string specified is a supported binary operator * for if statements. */ static int is_binary_oper(string) char *string; { return (strcmp(string,"==")==0 || strcmp(string,"!=")==0 || strcmp(string,"<")==0 || strcmp(string,">")==0 || strcmp(string,"||")==0 || strcmp(string,"&&")==0 || strcmp(string,"|")==0 || strcmp(string,"&")==0); } /* * evaluate the binary operator applied to the two supplied arguments * and return the boolean result */ static int eval_binary_oper(oper, arg1, arg2) char *oper; struct ARG_STRUCT *arg1, *arg2; { int val1, val2; val1 = eval_condition(arg1); val2 = eval_condition(arg2); if (strcmp(oper,"==") == 0) return (val1 == val2); else if (strcmp(oper,"!=") == 0) return (val1 != val2); else if (strcmp(oper,"<") == 0) return (val1 < val2); else if (strcmp(oper,">") == 0) return (val1 > val2); else if (strcmp(oper,"||") == 0) return (val1 || val2); else if (strcmp(oper,"&&") == 0) return (val1 && val2); else if (strcmp(oper,"|") == 0) return (val1 | val2); else if (strcmp(oper,"&") == 0) return (val1 & val2); else fatal("invalid binary operator in if-statement\n"); } /* N_fopen_from_path(name, type, path) - search through the * directories in colon-separated path "path" for a file of * name "name", open it with fopen, and return the stream * pointer. If path is null, look for it in current directory. */ FILE * N_fopen_from_path(name, type, path) char *name, *type, *path; { char *path_copy; /* copy of path (strtok munges it) */ char *dir; char full_filename[256]; FILE *stream; if (path==NULL) { /* look for it in current dir */ return fopen(name, type); } else { /* go through dirs in path */ path_copy = strcpy (malloc (strlen (path) + 1), path); for (dir=strtok(path_copy, ":"), stream = NULL; dir != NULL && stream==NULL; dir=strtok(NULL, ":")) { sprintf(full_filename, "%s/%s", dir, name); stream=fopen(full_filename, type); } /* either we ran out of dirs, or found the file */ free(path_copy); return stream; } } /* * Copy the source string into the destination string, returning * a pointer to the end (null terminator) of destination string. */ static char * copy_string(dest,src) char *dest, *src; { while (*dest = *src++) dest++; return dest; } /* for a fatal error: print message, exit */ static fatal(msg) char *msg; { fprintf(stderr, "looper error: %s", msg); exit(1); } main(argc, argv) int argc; char *argv[]; { int c; int status; /* * MACROPATH can contain a list of directories in which * to search for directories. */ N_filepath = (char*) getenv("MACROPATH"); /* process command line options */ while ((c = getopt(argc,argv,"iptDdCuh:")) != EOF) { switch(c) { case 'i': interactive = 1; break; case 'p': printonly = 1; break; case 't': trace = 1; break; case 'd': debug = 1; N_2process = 1; filename=optarg; break; case 'D': debug = 1; N_2process = 0; filename=optarg; break; case 'C': N_translate = 1; break; case 'u': prepend_underscore = 1; break; case 'h': headertext_file = optarg; break; case '?': printf("Legal interpreter options:\n"); printf("-i interactive mode\n"); printf("-p printonly mode\n"); printf("-t trace mode - prints commands before executing\n"); printf("-d two process debug mode (file required)\n"); printf("-D single process debug mode (file required)\n"); printf(" file to read from\n"); printf("-C translation to C mode\n"); printf(" -u prepend underscore when translating\n"); printf(" -h insert file at head of translated program\n"); exit(1); } } if (N_translate && (trace||debug||interactive)) fatal("C translation option not allowed with trace, debug, or interactive options\n"); if (debug) { if (interactive) fatal("invalid combination of options, debug and interactive\n"); if (optind == argc) fatal("filename required for debug option.\n"); filename = argv[optind]; yyin = fopen(filename, "r"); if (yyin == NULL) { fprintf(stderr, "looper error - could not open file %s\n", filename); exit(1); } functail = N_funchead = (FUNC*) malloc(sizeof(FUNC)); N_debuguse(filename); status = yyparse(); N_debuguse(filename); curr_debug_file = filename; if (!syntaxerr) N_debugger(1); return(status); } else if (interactive) { if (optind> "); return( yyparse() ); } else if (optind == argc) { /* take input from stdin */ functail = N_funchead = (FUNC*) malloc(sizeof(FUNC)); status = yyparse(); if (!syntaxerr) { if (N_translate) generate_C_file(N_funchead->next, NULL); else N_execute(N_funchead->next,EXECUTE_NORMAL); } return(status); } else { /* separately execute each named file */ for (; optind < argc && !syntaxerr; optind++) { yyin = fopen(argv[optind], "r"); if (yyin == NULL) { fprintf(stderr, "looper error - could not open file %s\n", argv[optind]); exit(1); } filename=argv[optind]; functail = N_funchead = (FUNC*) malloc(sizeof(FUNC)); status = yyparse(); if (!syntaxerr) { if (N_translate) generate_C_file(N_funchead->next,filename); else N_execute(N_funchead->next,EXECUTE_NORMAL); } } return(status); } } /* required by yacc */ yyerror(s) char *s; { fprintf(stderr, " %s\n",s); } /* * maintain the stack of starting lines for the "if", "for" and "foreach" */ #define MAXSTACK 64 static int ifstack[MAXSTACK]; static int forstack[MAXSTACK]; static int foreachstack[MAXSTACK]; static int ifstackp=0; static int forstackp=0; static int foreachstackp=0; static push(type,val) int type,val; { switch (type) { case IFSTACK: ifstack[ifstackp] = val; if (ifstackp++ >= MAXSTACK) puts("looper error: push() stack overflow"); break; case FORSTACK: forstack[forstackp] = val; if (forstackp++ >= MAXSTACK) puts("looper error: push() stack overflow"); break; case FOREACHSTACK: foreachstack[foreachstackp] = val; if (foreachstackp++ >= MAXSTACK) puts("looper error: push() stack overflow"); break; default: puts("looper error: push() invalid type"); } } static pop(type) int type; { switch (type) { case IFSTACK: if (--ifstackp < 0) puts("looper error: pop() stack underflow"); return (ifstack[ifstackp]); break; case FORSTACK: if (--forstackp < 0) puts("looper error: pop() stack underflow"); return (forstack[forstackp]); break; case FOREACHSTACK: if (--foreachstackp < 0) puts("looper error: pop() stack underflow"); return (foreachstack[foreachstackp]); break; default: puts("looper error: pop() invalid type"); } }