home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
World of A1200
/
World_Of_A1200.iso
/
programs
/
misc
/
eval
/
source
/
src.lha
/
parse.c
< prev
Wrap
C/C++ Source or Header
|
1993-04-13
|
16KB
|
618 lines
/*
**
** PARSE.C Divides an input string into tokens and evaluates an
** expression.
**
** Originally written 5/89 in ANSI C
**
** Eval is a floating point expression evaluator.
** This file last updated in version 1.10
** For the version number, see eval.h
** Copyright (C) 1993 Will Menninger
**
** This program is free software; you can redistribute it and/or modify it
** under the terms of the GNU General Public License as published by the
** Free Software Foundation; either version 2 of the License, or any
** later version.
**
** This program is distributed in the hope that it will be useful, but
** WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** General Public License for more details.
**
** You should have received a copy of the GNU General Public License along
** with this program; if not, write to the Free Software Foundation, Inc.,
** 675 Mass Ave, Cambridge, MA 02139, USA.
**
** The author until 9/93 can be contacted at:
** e-mail: willus@ilm.pfc.mit.edu
** U.S. mail: Will Menninger, 45 River St., #2, Boston, MA 02108-1124
**
**
*/
#include "eval.h"
/* modes */
#define M_UNARY 1
#define M_BINARY 2
#define M_FUNCTION 3
static double last_value=0.0;
static int base;
static int is_binary (char *s);
static int is_unary (char c);
static int instr (char c,char *s);
static BOOLEAN legal (char *s);
static BOOLEAN is_digit (char c);
static BOOLEAN is_exp (char c);
static BOOLEAN get_token (int mode,char *s,int *n,TOKENPTR t,VARPTR vlist,
VARPTR clist,char *newname);
static BOOLEAN assign_as_var (char *s,int *n,TOKENPTR t,VARPTR vlist,
VARPTR clist,char *newname);
static BOOLEAN assign_as_num (char *s,int *n,TOKENPTR t,int max,int base);
static BOOLEAN get_value (char *s,VARPTR vlist,VARPTR clist,
double *ret_val);
static int base_override (char *s,int *n);
static int rank (int operator);
static int is_binary(char *s)
{
if (s[0]=='<' && s[1]=='<')
return(SHLEFT);
if (s[0]=='>' && s[1]=='>')
return(SHRIGHT);
return(instr(s[0],BSTRING));
}
static int is_unary(char c)
{
return(instr(c,USTRING));
}
static int instr(char c,char *s)
{
int i;
for (i=0;s[i]!=EOS && s[i]!=c;i++);
return(s[i]==EOS ? 0 : i+1);
}
static BOOLEAN legal(char *s)
{
int c;
c=s[0];
return(c!=EOS && c!='(' && c!=')' && c!=','
&& c!='=' && !isspace(c) && !is_binary(s));
}
static BOOLEAN is_digit(char c)
{
c=tolower((int)c);
if (base>10)
return((c>='0' && c<='9') || (c>='a' && c<='a'+base-11));
return(c>='0' && c<='0'+base-1);
}
static BOOLEAN is_exp(char c)
{
return((base > 14) ? (c=='\\') : (c=='\\' || c=='e' || c=='E'));
}
/*
** get_token(int mode,char *s,int *n,TOKENPTR t,VARPTR vlist,
** VARPTR clist,char *newname)
**
** Figures out what the next token in the string s is, starting at (*n).
** (*n) is returned pointing to the part of the string just after the
** evaluated token. vlist and clist are used to search through in case
** the token is a variable or constant. newname is assigned if the token
** is a variable name not found in any of the lists.
**
** Returns 0 for end of string, 1 for valid token found
**
*/
static BOOLEAN get_token(int mode,char *s,int *n,TOKENPTR t,VARPTR vlist,
VARPTR clist,char *newname)
{
int i,c,h;
t->type=0;
t->code=0;
t->value=0;
for (i=(*n);isspace(s[i]);i++);
(*n)=i;
if (s[i]==EOS)
{
(*n)=i;
return(0);
}
t->type=0;
if (mode==M_UNARY && is_unary(s[i]))
{
t->type=UNARY;
t->code=is_unary(s[i]);
(*n)=(*n)+1;
return(1);
}
if (is_binary(&s[i]) && (mode!=M_UNARY || s[i]!='&'))
{
t->type=BINARY;
t->code=is_binary(&s[i]);
if (t->code==SHLEFT || t->code==SHRIGHT)
(*n)=(*n)+1;
}
else
{
t->type=instr(s[i],"(),=");
if (t->type>0)
t->type+=LEFT_PAREN-1;
}
if (t->type!=0)
{
(*n)=(*n)+1;
return(1);
}
if (s[i]=='!')
return(assign_as_var(s,n,t,vlist,clist,newname));
base=getibase();
c=base_override(&s[i],&h);
if (c)
{
base=c;
i+=h;
}
if (!is_digit(s[i]) && s[i]!='.')
return(assign_as_var(s,n,t,vlist,clist,newname));
if (s[i]=='.' && !is_digit(s[i+1]))
return(assign_as_var(s,n,t,vlist,clist,newname));
for (;legal(&s[i]);i++)
{
if (s[i]=='.' || is_exp(s[i]))
break;
if (!is_digit(s[i]))
return(assign_as_var(s,n,t,vlist,clist,newname));
}
if (!legal(&s[i]))
{
(*n)=(*n)+h;
return(assign_as_num(s,n,t,i,base));
}
if (s[i]=='.')
for (i++;legal(&s[i]);i++)
{
if (is_exp(s[i]))
break;
if (!is_digit(s[i]))
return(assign_as_var(s,n,t,vlist,clist,newname));
}
if (!legal(&s[i]))
{
(*n)=(*n)+h;
return(assign_as_num(s,n,t,i,base));
}
if (s[i+1]=='+' || s[i+1]=='-')
i++;
if (!is_digit(s[i+1]))
return(assign_as_var(s,n,t,vlist,clist,newname));
for (i++;legal(&s[i]);i++)
{
if (!is_digit(s[i]))
return(assign_as_var(s,n,t,vlist,clist,newname));
}
(*n)=(*n)+h;
return(assign_as_num(s,n,t,i,base));
}
static BOOLEAN assign_as_var(char *s,int *n,TOKENPTR t,VARPTR vlist,
VARPTR clist,char *newname)
{
VAR v;
char vname[MAXINPUT+1];
int l,i,j;
i=(*n);
l=(s[i]=='!');
if (l)
i++;
for (j=0;legal(&s[i]) || (!j && s[i]=='&');i++,j++)
vname[j]=s[i];
if (!j && l)
vname[j++]='!';
(*n)=i;
vname[j]=EOS;
if (strlen(vname)>MAXNAMELEN)
vname[MAXNAMELEN]=EOS;
strcpy(newname,vname);
if (vname[0]=='\"' && vname[1]==EOS)
{
t->value=last_value;
t->type=QUOTE;
return(1);
}
if (strlen(vname)<=MAXFLEN && (i=func_code(vname))!=0)
{
t->code=i;
t->type=FUNCTION;
}
else
{
strcpy(v.name,vname);
if (search_varlist(&v,clist,&i,MAXC))
{
t->code=i;
t->type=CONSTANT;
t->value=clist[i].value;
}
else
{
t->type=VARIABLE;
if (search_varlist(&v,vlist,&i,MAXV))
{
t->code=i;
t->value=vlist[i].value;
}
else
t->code=-1;
}
}
return(1);
}
static BOOLEAN assign_as_num(char *s,int *n,TOKENPTR t,int max,int base)
{
char num[MAXINPUT+1];
int i,j;
for (i=(*n),j=0;i<max;i++,j++)
num[j]=s[i];
(*n)=i;
num[j]=EOS;
t->type=NUMBER;
t->value=asciiconv(base,num);
return(1);
}
/*
** evaluate(char *s,int showout,VARPTR vlist,VARPTR clist)
**
** Evaluate parses the input string looking for help queries or an "equals"
** sign that divides the string into an assignment variable and its
** assigned expression. To evaluate the actual expression, get_value is
** called.
**
** Returns nothing
**
*/
void evaluate(char *s,int showout,VARPTR vlist,VARPTR clist)
{
char varname[MAXINPUT+1];
int i,j,n,vn;
VAR x;
TOKEN t;
int checked,outbase,oldbase;
char bigbuf[MAXOUTLEN];
oldbase=getobase();
outbase=base_override(s,&j);
checked=0;
if (outbase && isspace(s[j]))
{
j++;
checked=1;
}
else
{
j=0;
outbase=oldbase;
}
for (i=j;s[i]!=EOS && s[i]!='=';i++);
if (s[i]!=EOS)
{
strcpy(varname,&s[j]);
varname[i++]=EOS;
fixup(varname);
n=0;
j=get_token(M_UNARY,varname,&n,&t,vlist,clist,x.name);
if (!j)
{
printf("There must be valid variable name on the left side of the '='.\n");
return;
}
if (varname[n]!=EOS || t.type!=VARIABLE && t.type!=CONSTANT)
{
printf("\"%s\" is not a valid variable name.\n",varname);
return;
}
if (t.type==CONSTANT)
{
printf("\"%s\" is a pre-assigned constant. It cannot be reassigned.\n",x.name);
return;
}
vn=(t.type==VARIABLE);
}
else
{
vn=0;
i=j;
}
if (!checked)
{
outbase=base_override(&s[i],&j);
if (outbase && isspace(s[i+j]))
j++;
else
{
j=0;
outbase=oldbase;
}
i+=j;
}
if (!get_value(&s[i],vlist,clist,&x.value))
return;
last_value=x.value;
if (outbase!=oldbase)
setobase(outbase);
baseconv(x.value,bigbuf);
if (showout)
printf("%s\n",bigbuf);
if (outbase!=oldbase)
setobase(oldbase);
if (vn)
{
if (!insert_var(&x,vlist))
printf("No more variables can be assigned! Limit = %d.\n",MAXV);
}
}
/*
** get_value(char *s,VARPTR vlist,VARPTR clist)
**
** get_value evaluates an expression string by parsing it into tokens
** (using get_token) and converting the string of tokens to a table that
** contains tokens in reverse polish order. table_value is then called
** to evaluate the final value of the reverse polish table of tokens.
** That value is returned by get_value.
**
*/
static BOOLEAN get_value(char *s,VARPTR vlist,VARPTR clist,double *ret_val)
{
char nn[MAXNAMELEN+1];
TOKEN t,t2,t3;
int spos,mode;
char argcount[MAXINPUT+1];
int argptr;
clear_stack();
clear_table();
spos=0;
argptr=-1;
mode=M_UNARY;
while (get_token(mode,s,&spos,&t,vlist,clist,nn))
{
if (t.type==ILLEGAL)
return(eerror("The '=' character is not allowed in expressions."));
if (mode==M_UNARY && (t.type==BINARY || t.type==COMMA ||
t.type==RIGHT_PAREN))
return(eerror("Operand expected in expression."));
if (mode==M_BINARY && (t.type==UNARY || t.type==CONSTANT ||
t.type==VARIABLE || t.type==FUNCTION ||
t.type==NUMBER || t.type==LEFT_PAREN ||
t.type==QUOTE))
return(eerror("Operator expected in expression."));
if (t.type==VARIABLE && t.code<0)
{
printf("\"%s\" is an unassigned variable.\n",nn);
return(0);
}
if (mode==M_FUNCTION && t.type!=LEFT_PAREN)
return(eerror("Function names must be immediately followed "
"by a left parenthesis."));
switch(t.type)
{
case NUMBER:
case VARIABLE:
case CONSTANT:
case QUOTE:
if (!add_token(&t))
return(0);
mode=M_BINARY;
break;
case LEFT_PAREN:
if (!push_token(&t))
return(0);
mode=M_UNARY;
break;
case UNARY:
if (!push_token(&t))
return(0);
mode=M_UNARY;
break;
case BINARY:
while (top_of_stack(&t2))
{
if (t2.type==BINARY && rank(t.code) > rank(t2.code))
break;
if (t2.type==LEFT_PAREN)
break;
pop_token(&t2);
if (!add_token(&t2))
return(0);
}
if (!push_token(&t))
return(0);
mode=M_UNARY;
break;
case FUNCTION:
if (!push_token(&t))
return(0);
argptr++;
argcount[argptr]=0;
mode=M_FUNCTION;
break;
case RIGHT_PAREN:
while (pop_token(&t2) && t2.type!=LEFT_PAREN)
if (!add_token(&t2))
return(0);
if (t2.type!=LEFT_PAREN)
return(eerror("Unmatched parentheses in expression."));
if (top_of_stack(&t2) && t2.type==FUNCTION)
{
pop_token(&t2);
if (!add_token(&t2))
return(0);
if (func_nargs(t2.code)!=argcount[argptr]+1)
{
printf("Incorrect number of arguments "
"specified for function %s.\n",
func_name(t2.code));
return(0);
}
argptr--;
}
mode=M_BINARY;
break;
case COMMA:
while (pop_token(&t2) && t2.type!=LEFT_PAREN)
if (!add_token(&t2))
return(0);
if (t2.type!=LEFT_PAREN || !top_of_stack(&t3) ||
t3.type!=FUNCTION)
return(eerror("Misplaced comma in expression."));
if (!push_token(&t2))
return(0);
mode=M_UNARY;
argcount[argptr]++;
break;
}
}
if (mode==M_UNARY)
return(eerror("Expression is improperly terminated."));
while (pop_token(&t2))
{
if (t2.type==LEFT_PAREN)
return(eerror("Unmatched parentheses in expression."));
if (!add_token(&t2))
return(0);
}
return(table_value(ret_val));
}
BOOLEAN eerror(char *message)
{
printf("%s\n",message);
return(0);
}
static int rank(int operator)
{
switch (operator)
{
case POWER:
return(10);
case DIVIDE:
case MULTIPLY:
case MOD:
return(9);
case ADD:
case SUBTRACT:
return(8);
case SHRIGHT:
case SHLEFT:
return(7);
case AND:
return(6);
case XOR:
return(5);
case OR:
return(4);
}
return(0);
}
static int base_override(char *s,int *n)
{
int base;
int c;
(*n)=0;
base=0;
if (s[0]=='&')
{
if (s[1]!=EOS)
{
c=tolower((int)s[1]);
if (c=='h' || c=='o' || c=='b' || c=='d')
{
base= c=='h' ? 16 : (c=='o' ? 8 :(c=='b' ? 2 : 10));
(*n)=2;
}
}
}
else if (s[0]=='0' && (s[1]=='x' || s[1]=='X'))
{
base=16;
(*n)=2;
}
/* Assuming base 8 for a leading zero removed because of
confusion with floating point numbers */
else if (s[0]=='\\')
{
c=tolower((int)s[1]);
if (c=='0' || (c>='2' && c<='9') || (c>='a' && c<='z'))
{
base= c=='0' ? 10:((c>='2'&&c<='9')?c-'0':c-'a'+11);
(*n)=2;
}
}
else if (s[0]=='$')
{
base=16;
(*n)=1;
}
return(base);
}
void tokcpy(TOKENPTR dest,TOKENPTR source)
{
dest->type=source->type;
dest->code=source->code;
dest->value=source->value;
}