home *** CD-ROM | disk | FTP | other *** search
- /*
- * icalc - complex-expression parser
- *
- * Yacc parser definition for icalc, a complex-number expression
- * parser, featuring arbitrarily named variables, user-defined
- * functions, startup files and special commands to ease certain tasks.
- *
- * (C) Martin W Scott, 1991.
- */
- %{
- #include <stdio.h>
- #include <ctype.h>
- #include "complex.h"
- #include "constant.h"
- #include "memory.h"
-
- RemKey *remkey;
-
- Symbol *ans; /* set by init.c */
- Symbol *multi; /* ditto */
- Complex treeval; /* what tree evaluates to */
- UserFunc *userfunc; /* symbol for user-function definition */
- int noprompt; /* if separator a ';', prompt disabled */
- int inparamlist; /* are we at a functions parameter-list? */
- int indefn; /* are we in function definition? */
- int silent; /* should we display results? */
- char *infile; /* file currently being read */
- %}
-
- %union {
- double rval;
- Symbol *sym;
- Node *node;
- SymList *slist;
- ArgList *alist;
- }
- %start list
-
- %token <rval> NUMBER
- %token <sym> VAR CONST C_BLTIN FUNCDEF UFUNC SFUNC COMMAND UNDEF
- %token <sym> PARAMETER
-
- %type <node> expr
- %type <sym> symbol
- %type <slist> parlist
- %type <alist> arglist
-
- %right '=' /* assignment */
- %left '+' '-' /* standard arithmetic operators */
- %left '*' '/'
- %left UMINUS /* unary minus */
- %right '^' /* exponentation */
- %left '\'' /* conjugate operator z' = conj(z) */
- %%
-
- list: /* nothing */
- | list separator { prompt(); }
- | list FUNCDEF symbol
- {
- if ($3->type == UFUNC)
- {
- clear_ufunc(&$3->u.ufunc);
- }
- else if ($3->type != UNDEF)
- {
- execerror($3->name, "already defined");
- }
- inparamlist = 1;
- rem_freeall();
- $3->type = UFUNC;
- userfunc = &$3->u.ufunc;
- init_ufunc(userfunc);
- (void)rem_setkey(&userfunc->remkey);
- }
- '(' parlist ')'
- {
- inparamlist = 0;
- indefn = 1;
- userfunc->params = $6;
- }
- '=' expr separator
- {
- userfunc->tree = $10;
- (void)rem_setkey(&remkey);
- indefn = 0;
- if (!silent)
- {
- fprintf(stdout, "\tfunction %s defined\n", $3->name);
- }
- prompt();
- }
-
- | list COMMAND separator { (*($2->u.vptr))(); prompt(); }
- | list expr separator
- {
- treeval = eval_tree($2);
-
- if (!silent)
- cprin(stdout, "\t", "\n", treeval);
-
- if (ans) /* allocated successfully */
- ans->u.val = treeval;/* set 'last answer' const */
-
- /* free all mem associated with this tree */
- rem_freeall();
- prompt();
- }
- | list error separator
- {
- if (indefn)
- {
- indefn = 0;
- rem_setkey(&remkey);
- }
- inparamlist = 0;
- yyerrok;
- prompt();
- }
- ;
-
- parlist: /* none */ { $$ = NULL; }
- | PARAMETER { $$ = addparam(NULL, $1); }
- | parlist ',' PARAMETER { $$ = addparam($1, $3); }
- ;
-
- arglist: /* nothing */ { $$ = NULL; }
- | expr { $$ = addarg(NULL, $1); }
- | arglist ',' expr { $$ = addarg($1, $3); }
- ;
-
- symbol: VAR
- | UFUNC
- ;
-
- separator: '\n' { noprompt = 0; }
- | ';' { noprompt = 1; }
- ;
-
- expr: NUMBER { $$ = n_number($1, 0.0); }
- | NUMBER 'i' { $$ = n_number(0.0, $1); }
- | 'i' { $$ = n_number(0.0, 1.0); }
- | CONST { $$ = n_symbol(CONST, $1); }
- | CONST '=' expr { execerror("invalid assignment to constant", $1->name); }
- | VAR { if ($1->type == UNDEF)
- warning("using zero for undefined symbol", $1->name);
- $$ = n_symbol(VAR, $1);
- }
- | PARAMETER { $$ = n_symbol(PARAMETER, $1); }
- | VAR '=' expr { $1->type = VAR; $$ = n_asgn($1, $3); }
- | C_BLTIN '(' expr ')' { $$ = n_func(C_BLTIN, $1, $3); }
- | UFUNC '(' arglist ')' { $$ = n_func(UFUNC, $1, (Node *)$3); }
- | SFUNC '(' arglist ')' { $$ = n_func(SFUNC, $1, (Node *)$3); }
- | expr '+' expr { $$ = n_binop('+', $1, $3); }
- | expr '-' expr { $$ = n_binop('-', $1, $3); }
- | expr '*' expr { $$ = n_binop('*', $1, $3); }
- | expr '/' expr { $$ = n_binop('/', $1, $3); }
- | expr '^' expr { $$ = n_binop('^', $1, $3); }
- | expr '\'' { $$ = n_unop('\'', $1); }
- | '(' expr ')' { $$ = n_unop('(', $2); }
- | '-' expr %prec UMINUS { $$ = n_unop(UMINUS , $2); }
- ;
-
- %%
-
- #include <signal.h>
- #include <setjmp.h>
-
- #ifdef AMIGA
-
- #include <libraries/dos.h>
- #include <workbench/startup.h>
- #include <proto/dos.h>
-
- #define STARTUP_FILE "s:icalc.init"
-
- extern struct WBStartup *WBenchMsg;
- struct WBArg *wbargv;
- int wbargc;
-
- char *nextarg(void);
-
- #endif
-
- void processfile(char *name);
-
- #define BANNER "\033[1micalc v1.1a (c) Martin W Scott, 1992\033[0m\n"
- #define PROMPT "\033[33micalc> \033[31m"
-
- jmp_buf begin; /* error start */
- int lineno; /* current line-number of input file */
- FILE *fin; /* current input file */
-
- yylex() /* lexical analyser - cumbersome, but does the job (just) */
- {
- int c;
-
- while ((c = getc(fin)) == ' ' || c == '\t') /* skip blanks */
- ;
-
- if (c == EOF) /* end of input */
- return 0;
-
- if (c == '.' || isdigit(c)) /* number */
- {
- ungetc(c, fin);
- fscanf(fin, "%lf", &yylval.rval);
- return NUMBER;
- }
- if (c == 'i') /* possibly imaginary part */
- {
- if (!isalnum(c = getc(fin)) && c != '_') /* yes, it is */
- {
- ungetc(c, fin);
- return 'i';
- }
- ungetc(c, fin); /* no, fall through to next */
- c = 'i'; /* restore c to old value */
- }
- if (isalpha(c) || c == '_') /* constant, var or builtin */
- {
- Symbol *s;
- char sbuf[100], *p = sbuf;
-
- do {
- *p++ = c;
- } while ((c = getc(fin)) != EOF && (isalnum(c) || c == '_'));
- ungetc(c, fin);
- *p = '\0';
-
- if (inparamlist)
- s = allocsym(sbuf, PARAMETER);
- else
- {
- /* if in function definition, check it's argument
- list for variable references BEFORE symtree */
- if (indefn && (s = findsym(userfunc->params, sbuf)))
- ;
- else if (!(s = lookup(sbuf)))
- s = install(sbuf, UNDEF, zero);
- }
-
- yylval.sym = s;
-
- return s->type == UNDEF ? VAR : s->type;
- }
- if (c == '\\') /* expression continued on next line */
- {
- while ((c = getc(fin)) != '\n' && c != EOF)
- ;
- if (c == '\n')
- {
- lineno++;
- return yylex(); /* parse next line */
- }
- if (c == EOF) /* end of input */
- return 0;
- }
- if (c == '#') /* comment line */
- {
- while ((c = getc(fin)) != '\n' && c != EOF)
- ;
- if (c == EOF) /* end of input */
- return 0;
- }
- if (c == '\n')
- lineno++;
-
- return c;
- }
-
- void warning(s, t) /* print warning messages s and t (t can be NULL) */
- char *s, *t;
- {
-
- fprintf(stderr,"icalc: %s", s);
- if (t)
- fprintf(stderr," %s", t);
- if (infile)
- {
- fprintf(stderr," in %s,", infile);
- fprintf(stderr," near line %d\n", lineno);
- }
- fprintf(stderr,"\n");
- }
-
- void yyerror(s) /* standard parse error notification */
- char *s;
- {
- warning(s, NULL);
- }
-
- void execerror(s, t) /* complete evaluation failure; advise, and restart */
- char *s;
- char *t;
- {
- warning(s, t);
- longjmp(begin, 0);
- }
-
-
- void prompt() /* display a prompt if circumstances are right */
- {
- if (!noprompt && !infile && !silent) /* interactive session */
- fprintf(stderr, PROMPT);
- }
-
-
- void fpecatch() /* catch floating-point errors */
- {
- execerror("floating point exception", NULL);
- }
-
-
- void main(argc,argv) /* main program entry - parse arguments */
- char **argv;
- {
- fprintf(stderr,BANNER); /* hello... */
-
- (void)rem_setkey(&remkey); /* set 'remember' key */
- init(); /* build initial symbol tree */
-
- #ifdef AMIGA
- if (!access(STARTUP_FILE,0)) /* read startup file */
- processfile(STARTUP_FILE);
- #endif
-
- #ifdef AMIGA /* process arguments - the Workbench way... */
-
- if (argc == 0) /* ran from workbench */
- {
- char *name;
-
- wbargc = WBenchMsg->sm_NumArgs-1;
- wbargv = &(WBenchMsg->sm_ArgList)[1];
-
- while (name = nextarg())
- processfile(name);
- }
- else
- #endif
- if (argc == 1) /* stdin only - fake argument list */
- processfile("-");
- else
- {
- while (--argc)
- processfile(*++argv);
- }
- }
-
- void processfile(name) /* open 'name' and set as curently scanned file */
- char *name;
- {
- if (!strcmp(name, "-")) /* stdin */
- {
- fin = stdin;
- infile = NULL;
- }
- else if (!(fin = fopen(name, "r"))) /* specified file */
- {
- fprintf(stderr, "icalc: can't open %s\n", name);
- return;
- }
- else infile = name;
-
- lineno = 1; /* initialise line-count */
-
- setjmp(begin); /* where to come back to */
- signal(SIGFPE, fpecatch); /* catch math errors */
- prompt();
- yyparse(); /* start parsing */
-
- if (!infile) fclose(fin); /* close opened file */
- }
-
-
- #ifdef AMIGA
-
- char *nextarg() /* get next selected Workbench argument (icon) */
- {
- static BPTR olddir = -1L;
-
- if (wbargc > 0)
- {
- olddir = CurrentDir(wbargv->wa_Lock);
- wbargc--;
- return (wbargv++)->wa_Name;
- }
- else if (wbargc-- == 0)
- {
- if (olddir != (-1L))
- CurrentDir(olddir);
- return "-";
- }
- else return NULL;
- }
-
- #endif
-