home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Otherware
/
Otherware_1_SB_Development.iso
/
amiga
/
misc
/
icalc.lzh
/
icalc
/
src
/
complex.y
< prev
next >
Wrap
Text File
|
1992-01-26
|
8KB
|
398 lines
/*
* 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