DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - download
Index: ┃ T d

⟦b962bf620⟧ TextFile

    Length: 26794 (0x68aa)
    Types: TextFile
    Names: »dm.y«

Derivation

└─⟦87ddcff64⟧ Bits:30001253 CPHDIST85 Tape, 1985 Autumn Conference Copenhagen
    └─ ⟦this⟧ »cph85dist/stat/src/dm.y« 

TextFile

%{
#include "unixstat.h"
PGM(dm,Data Manipulation,5.0,3/2/85)
/* Copyright (c) 1982 Gary Perlman (see Copyright file) */

/*
	MSDOS note: in dm.c (the output from yacc on dm.y),
	you must change the line:
		yyval = yypv[1];
	to:
		yyval.ex = yypv[1].ex;
	The reason is that the Lattice C compiler cannot do union assignments.
*/

/*
dm is a data manipulator designed to manipulate files of columns of number and
strings.  The major components of this program are:
	(1) a parser, built by yacc, called yyparse.
	(2) a scanner, yylex, called by yyparse.
	(3) a parse tree node function, called by yyparse.
	(4) a function, eval, that evaluates the parse trees.
	(5) a main that calls I/O routines and the control loop.
The following section is a bunch of global declarations that will be put
literally into the program, y.tab.c by yacc.
*/

#define MAXEXPR      100             /* maximum number of expressions */
#define MAXSTRING    32              /* maximum length of input string */
#define MAXCOL       100             /* maximum number of input columns */
#define MAXCONST     100             /* maximum number of constants */
#define LOGe10       2.302585092994  /* used for L function */
#define FLOATPTR     0               /* codes for parse tree node types */
#define OPERATOR     1
#define STRINGOP     2
#define STRINGPTR    3
typedef	int lgl;                     /* no lgl  type in C */
#define FALSE        0
#define TRUE         1
#define PARSERROR    1               /* returned by yyparse on error */
/*
The following few numbers are reserved by dm to signal special conditions by
being returned by various routines.  They are hopefully numbers that no
expressions would ever evaluate to.
*/
#define	LARGE         9999999999999.0      /* a large number */
#define	SUPPRESS     -1125899906842624.    /* suppress output */
#define	STRINGFLAG   -8888888888888777.0   /* returned by eval */
#define	NIL          -998888677484837274.  /* cause nil output */
#define	EXITFLAG     -99999999999999.9     /* cause exit */

char	*N_KILL     = "KILL";
char	*N_EXIT     = "EXIT";
char	*N_NIL      = "NIL";
char	*N_INLINE   = "INLINE";
char	*N_OUTLINE  = "OUTLINE";
char	*N_INPUT    = "INPUT";
char	*N_N        = "N";
char	*N_SUM      = "SUM";
char	*N_RAND     = "RAND";
char	*N_ABS      = "abs";
char	*N_FLOOR    = "floor";
char	*N_CEIL     = "ceil";
char	*N_EXP      = "exp";
char	*N_LOG      = "log";
char	*N_LOG10    = "Log";

char	Outpipe = 0;                       /* true if output is piped */
FILE	*Infile;                           /* data read from here */
char	Inputline[BUFSIZ];                 /* INPUT read into here */
FILE	*Outfile;                          /* output from dm */
char	*Evalstr[MAXCOL+1];                /* ptrs to strings from eval */
char	Str[MAXCOL+1][MAXSTRING];          /* columns from each dataline */
char	*Expra;                            /* ptr to each expression */
typedef	union
	{
	int 	opr;        /* if operator or stringop */
	double	*num;       /* if FLOATPTR */
	char	*str;       /* if STRINGOP */
	} STUFF;
STUFF	Tmp1, Tmp2;  /* used in the parser to cast operators */
typedef struct enode        /* expression node in tree */
	{
	int 	etype;          /* type of node */
	STUFF	contents;
	struct	enode *lchild;
	struct	enode *rchild;
	} ENODE;
#define	ENULL ((ENODE *) NULL)
ENODE	*Expr[MAXEXPR+1];           /* ptr to each parse tree */
double	Input[MAXCOL+1];            /* input numbers */
#define	INLINE (Input[0])           /* input line number stored here */
lgl 	Usenumber[MAXCOL+1];        /* true is Input[i] is used */
int 	Usecols;                    /* maximum col number accessed */
double	Output[MAXEXPR+1];          /* output numbers */
#define	OUTLINE (Output[0])         /* output line number stored here */
double	Const[MAXCONST];            /* constants stored here */
int 	Nconst;                     /* number of constants */
double	Nil = NIL;                  /* flagged by NIL */
double	Suppress  = SUPPRESS;       /* flagged by KILL */
double	Stringflag = STRINGFLAG;    /* eval returns string */
double	Exitflag = EXITFLAG;        /* flagged by EXIT */
double	Randu;                      /* uniform rand num */
extern	double	Maxrand;            /* set by initrand */
lgl 	Userand;                    /* true if Randu is used */
double	N;                          /* number of input cols */
double	Sum;                        /* sum of input cols */
int 	Should_output[MAXEXPR+1];   /* used with X */
int 	Exprno;                     /* expression number */
int 	Nexpr;                      /* number of expressions */
char	*malloc (), *strcpy ();
%}

/*                    G R A M M A R                       */
/*
First is the set of precedences and associativities, followed by the grammar
Actions are associated with each part of the grammar matched.  These amount
to creating an enode representing the expression parsed, created by node.
One notable feature about the grammar is that it uses the same symbols for
comparing both strings and numbers, the difference being made explicit in
the actions for the string comparison functions.
*/
%union { /* union of the data types the parser will deal with */
	int 	opr;
	char	*str;
	double	*num;
	ENODE	*ex;
	}
%type <ex> expr string stringexpr
%token <num> NUMBER                          /* returned by yylex for numbers */
%token <str> STRING                          /* returned by yylex for strings */
%token <opr> STRINDEX                        /* string index operator flag */
%left <opr> '?' ':' IF THEN ELSE             /* if then else */
%left <opr> '|' NOR                          /* logical or */
%left <opr> '&' NAND                         /* logical and */
%nonassoc <opr> '!'                          /* logical not */
%nonassoc <opr> EQ NE GE GT LE LT NOTIN 'C'  /* comparators */
%left  <opr> '+' '-'                         /* plus and minus */
%left  <opr> '*' '/' '%'                     /* mult, div, mod */
%right <opr> '^'                             /* exponentiation */
%nonassoc <opr> UMINUS                       /* unary minus */
%nonassoc <opr> '#' 'l' 'L' 'e' 'a' 'f' 'c'  /* unary functions */
%%
/*                     P R O D U C T I O N S                         */
start:
	stringexpr
		{
		Expr[Exprno] = $1;
		};
stringexpr :
	expr
		{
		$$ = $1;
		}|
	string
		{
		$$ = $1;
		};
expr :
	'x' '[' expr ']'
		{
		Tmp1.opr = 'x';
		$$ = node (&Tmp1, OPERATOR, ENULL, $3);
		}|
	'y' '[' expr ']'
		{
		Tmp1.opr = 'y';
		$$ = node (&Tmp1, OPERATOR, ENULL, $3);
		}|
	'('  expr ')'
		{
		$$ = $2;
		}|
	expr '+' expr
		{
		Tmp1.opr = '+';
		$$ = node (&Tmp1, OPERATOR, $1, $3);
		}|
	expr '-' expr
		{
		Tmp1.opr = '-';
		$$ = node (&Tmp1, OPERATOR, $1, $3);
		}|
	expr '*' expr
		{
		Tmp1.opr = '*';
		$$ = node (&Tmp1, OPERATOR, $1, $3);
		}|
	expr '%' expr
		{
		Tmp1.opr = '%';
		$$ = node (&Tmp1, OPERATOR, $1, $3);
		}|
	expr '/' expr
		{
		Tmp1.opr = '/';
		$$ = node (&Tmp1, OPERATOR, $1, $3);
		}|
	expr '^' expr
		{
		Tmp1.opr = '^';
		$$ = node (&Tmp1, OPERATOR, $1, $3);
		}|
	string EQ string
		{
		Tmp1.opr = '=';
		$$ = node (&Tmp1, STRINGOP, $1, $3);
		}|
	string NE string
		{
		Tmp1.opr = '!';
		Tmp2.opr = '=';
		$$ = node (&Tmp1, OPERATOR, ENULL,
			node (&Tmp2, STRINGOP, $1, $3));
		}|
	expr EQ expr
		{
		Tmp1.opr = '=';
		$$ = node (&Tmp1, OPERATOR, $1, $3);
		}|
	expr NE expr
		{
		Tmp1.opr = '!';
		Tmp2.opr = '=';
		$$ = node (&Tmp1, OPERATOR, ENULL,
			node (&Tmp2, OPERATOR, $1, $3));
		}|
	string GT string
		{
		Tmp1.opr = '>';
		$$ = node (&Tmp1, STRINGOP, $1, $3);
		}|
	string GE string
		{
		Tmp1.opr = '!';
		Tmp2.opr = '<';
		$$ = node (&Tmp1, OPERATOR, ENULL,
			node (&Tmp2, STRINGOP, $1, $3));
		}|
	expr GT expr
		{
		Tmp1.opr = '>';
		$$ = node (&Tmp1, OPERATOR, $1, $3);
		}|
	expr GE expr
		{
		Tmp1.opr = '!';
		Tmp2.opr = '<';
		$$ = node (&Tmp1, OPERATOR, ENULL,
			node (&Tmp2, OPERATOR, $1, $3));
		}|
	string LT string
		{
		Tmp1.opr = '<';
		$$ = node (&Tmp1, STRINGOP, $1, $3);
		}|
	string LE string
		{
		Tmp1.opr = '!';
		Tmp2.opr = '>';
		$$ = node (&Tmp1, OPERATOR, ENULL,
			node (&Tmp2, STRINGOP, $1, $3));
		}|
	expr LT expr
		{
		Tmp1.opr = '<';
		$$ = node (&Tmp1, OPERATOR, $1, $3);
		}|
	expr LE expr
		{
		Tmp1.opr = '!';
		Tmp2.opr = '>';
		$$ = node (&Tmp1, OPERATOR, ENULL,
			node (&Tmp2, OPERATOR, $1, $3));
		}|
	expr '&' expr
		{
		Tmp1.opr = '&';
		$$ = node (&Tmp1, OPERATOR, $1, $3);
		}|
	expr NAND expr
		{
		Tmp1.opr = '!';
		Tmp2.opr = '&';
		$$ = node (&Tmp1, OPERATOR, ENULL,
			node (&Tmp2, OPERATOR, $1, $3));
		}|
	expr '|' expr
		{
		Tmp1.opr = '|';
		$$ = node (&Tmp1, OPERATOR, $1, $3);
		}|
	expr NOR expr
		{
		Tmp1.opr = '!';
		Tmp2.opr = '|';
		$$ = node (&Tmp1, OPERATOR, ENULL,
			node (&Tmp2, OPERATOR, $1, $3));
		}|
	'-' expr %prec UMINUS
		{
		Tmp1.opr = '_';
		$$ = node (&Tmp1, OPERATOR, ENULL, $2);
		}|
	'!' expr
		{
		Tmp1.opr = '!';
		$$ = node (&Tmp1, OPERATOR, ENULL, $2);
		}|
	'l' expr
		{
		Tmp1.opr = 'l';
		$$ = node (&Tmp1, OPERATOR, ENULL, $2);
		}|
	'L' expr
		{
		Tmp1.opr = 'L';
		$$ = node (&Tmp1, OPERATOR, ENULL, $2);
		}|
	'e' expr
		{
		Tmp1.opr = 'e';
		$$ = node (&Tmp1, OPERATOR, ENULL, $2);
		}|
	'a' expr
		{
		Tmp1.opr = 'a';
		$$ = node (&Tmp1, OPERATOR, ENULL, $2);
		}|
	'f' expr
		{
		Tmp1.opr = 'f';
		$$ = node (&Tmp1, OPERATOR, ENULL, $2);
		}|
	'c' expr
		{
		Tmp1.opr = 'c';
		$$ = node (&Tmp1, OPERATOR, ENULL, $2);
		}|
	'#' string
		{
		Tmp1.opr = '#';
		$$ = node (&Tmp1, STRINGOP, ENULL, $2);
		}|
	string '[' expr ']'
		{
		Tmp1.opr = '[';
		$$ = node (&Tmp1, STRINGOP, $1, $3);
		}|
	string 'C' string
		{
		Tmp1.opr = 'C';
		$$ = node (&Tmp1, STRINGOP, $1, $3);
		}|
	string NOTIN string
		{
		Tmp1.opr = '!';
		Tmp2.opr = 'C';
		$$ = node (&Tmp1, OPERATOR, ENULL,
			node (&Tmp2, STRINGOP, $1, $3));
		}|
	expr '?' stringexpr ':' stringexpr
		{
		Tmp1.opr = '?';
		Tmp2.opr = ':';
		$$ = node (&Tmp1, OPERATOR, $1,
			node (&Tmp2, OPERATOR,  $3, $5));
		}|
	IF expr THEN stringexpr ELSE stringexpr 
		{
		Tmp1.opr = '?';
		Tmp2.opr = ':';
		$$ = node (&Tmp1, OPERATOR, $2,
			node (&Tmp2, OPERATOR, $4, $6));
		}|
	NUMBER
		{
		Tmp1.num = $1;
		$$ = node (&Tmp1, FLOATPTR, ENULL, ENULL);
		};
string :
	STRING
		{
		Tmp1.str = $1;
		$$ = node (&Tmp1, STRINGPTR, ENULL, ENULL);
		};
%%

/*
Next is the scanner that will be repeatedly called by yyparse, yylex.
This simple program reads from a global char *Expra, set by main.  Variables
are handled by returning NUMBER or STRING, tokens defined in above grammar.
*/
#define	CONST	(*Expra=='.'||(*Expra>='0'&&*Expra<='9'))
yylex ()
	{
	extern	YYSTYPE yylval;
	char	*strsave ();
	int 	column;
	while (isspace (*Expra)) Expra++;
	if (CONST)
		{
		if (Nconst == MAXCONST)
			ERRMSG1 (Maximum number of constants is %d, MAXCONST)
		Const[Nconst] = atof (Expra);
		yylval.num = &Const[Nconst++];
		while (CONST) Expra++;
		return (NUMBER);
		}
	switch (*Expra)
		{
		case '"':
		case '\'':
			yylval.str = strsave ();
			return (STRING);
		case 's':
			column = atoi (++Expra);
			if (column > Usecols) Usecols = column;
			yylval.str = Str[column];
			while (CONST) Expra++;
			return (STRING);
		case 'x':
			if (Expra[1] == '[')
				{
				Expra++;
				return ('x');
				}
			column = atoi (++Expra);
			if (column > Usecols) Usecols = column;
			Usenumber[column] = TRUE;
			yylval.num = &Input[column];
			while (CONST) Expra++;
			return (NUMBER);
		case 'y':
			if (Expra[1] == '[')
				{
				Expra++;
				return ('y');
				}
			column = atoi (++Expra);
			if (column > Usecols) Usecols = column;
			yylval.num = &Output[column];
			while (CONST) Expra++;
			return (NUMBER);
		case 'K':
			if (begins (N_KILL, Expra))
				Expra += strlen (N_KILL);
			else Expra++;
			yylval.num = &Suppress;
			return (NUMBER);
		case 'E':
			if (begins (N_EXIT, Expra))
				Expra += strlen (N_EXIT);
			else Expra++;
			yylval.num = &Exitflag;
			return (NUMBER);
		case 'N':
			if (begins (N_NIL, Expra))
				{
				Expra += strlen (N_NIL);
				yylval.num = &Nil;
				}
			else if (begins ("NEXT", Expra))
				{
				Expra += 4;
				yylval.num = &Suppress;
				}
			else
				{
				Expra++;
				yylval.num = &N;
				}
			return (NUMBER);
		case 'S':
			if (begins ("SKIP", Expra))
				{
				Expra += 4;
				yylval.num = &Suppress;
				}
			else
				{
				if (begins (N_SUM, Expra))
					Expra += strlen (N_SUM);
				else Expra++;
				yylval.num = &Sum;
				}
			return (NUMBER);
		case 'R':
			if (begins (N_RAND, Expra)) Expra += strlen (N_RAND); else Expra++;
			Userand = TRUE;
			yylval.num = &Randu;
			return (NUMBER);
		case 'I':
			if (begins (N_INLINE, Expra))
				{
				Expra += strlen (N_INLINE);
				yylval.num = &INLINE;
				return (NUMBER);
				}
			if (begins (N_INPUT, Expra))
				Expra += strlen (N_INPUT);
			else Expra++;
			yylval.str = Inputline;
			return (STRING);
		case 'O':
			if (begins (N_OUTLINE, Expra))
				{
				Expra += strlen (N_OUTLINE);
				yylval.num = &OUTLINE;
				return (NUMBER);
				}
			break;
		case '=': if (Expra[1] == '=') Expra += 2;
			else Expra++;
			return (EQ);
		case '<': if (Expra[1] == '=')
			{
			Expra += 2;
			return (LE);
			}
			Expra++;
			return (LT);
		case '>': if (Expra[1] == '=')
			{
			Expra += 2;
			return (GE);
			}
			Expra++;
			return (GT);
		case '!': switch (Expra[1])
				{
				case '=': Expra += 2; return (NE);
				case 'C': Expra += 2; return (NOTIN);
				case '&': Expra += 2; return (NAND);
				case '|': Expra += 2; return (NOR);
				default: Expra++; return ('!');
				}
		case '&': if (Expra[1] == '&') Expra++; break;
		case '|': if (Expra[1] == '|') Expra++; break;
		case 'i': if (begins ("if", Expra))
				{Expra += 2; return (IF);} break;
		case 't': if (begins ("then", Expra))
				{Expra += 4; return (THEN);} break;
		case 'e': if (begins ("else", Expra))
				{Expra += 4; return (ELSE);}
			else if (begins (N_EXP, Expra))
				{Expra += strlen (N_EXP); return ('e');}
			break;
		case 'a': if (begins (N_ABS, Expra))
				{Expra += strlen (N_ABS); return ('a');}
			break;
		case 'f': if (begins (N_FLOOR, Expra))
				{Expra += strlen (N_FLOOR); return ('f');}
			break;
		case 'c': if (begins (N_CEIL, Expra))
				{Expra += strlen (N_CEIL); return ('c');}
			break;
		case 'l': if (begins (N_LOG, Expra))
				{Expra += strlen (N_LOG); return ('l');}
			break;
		case 'L': if (begins (N_LOG10, Expra))
				{Expra += strlen (N_LOG10); return ('L');}
			break;
		}
	return ((int) *Expra++);
	}

yyerror (msg)
char	*msg;
	{
	if (msg && *msg) fprintf (stderr, "\007dm: %s\n", msg);
	fprintf (stderr,
		"\007dm: Failure occured with this left in input: (%s)\n", Expra-1);
#ifdef PTREE
	ptree (Expr[Exprno]);
	putchar ('\n');
#endif
	}

char *
strsave ()
	{
	char	buf[BUFSIZ], *bptr = buf;
	char	*p;
	char	quotechar = *Expra++;
	while (*Expra && *Expra != quotechar)
		*bptr++ = *Expra++;
	if (*Expra == quotechar) Expra++;
	*bptr = '\0';
	if ((p = malloc ((unsigned) strlen (buf) + 1) ) == NULL)
		ERRSPACE (saving strings)
	return (strcpy (p, buf));
	}

ENODE *
node (datum, dtype, lson, rson)
STUFF	*datum;                /* string, number, or operator */
int 	dtype;                 /* STRINGPTR, FLOATPTR, OPERATOR, STRINGOP */
ENODE	*lson;
ENODE	*rson;
	{
	ENODE	*newnode;
	newnode = (ENODE *) malloc (sizeof (ENODE));
	if (newnode == NULL)
		ERRSPACE(expressions)
	newnode->etype = dtype;
	switch (dtype)
		{
		case FLOATPTR: newnode->contents.num = datum->num; break;
		case STRINGPTR: newnode->contents.str = datum->str; break;
		case STRINGOP:
		case OPERATOR: newnode->contents.opr = datum->opr; break;
		default: fprintf (stderr, "\007dm/enode: unknown data type.\n");
		}
	newnode->lchild = lson;
	newnode->rchild = rson;
	return (newnode);
	}

main (argc, argv) int argc; char *argv[];
	{
	ARGV0;
	initial (argc, argv);
	loop ();
	exit (0);
	}

/*
	initial does the following:
		1) inits the random number generator
		2) reads in expressions from file, user, or argv[i].
		3) parses expressions.
		4) opens input and output files.
*/
initial (argc, argv) int argc; char **argv;
	{
	lgl 	interactive = FALSE;    /* if true, input in interactive mode */
	lgl 	input_by_hand = FALSE;  /* true if expressions input by hand */
	char	exprline[BUFSIZ];       /* expressions read into here */
	FILE	*exprfile;              /* expressions read from here */
	FILE	*getfile ();            /* gets a file open */
	argc--;
	if (argc) checkstdin (Argv0);
	if (argc == 0)
		{
		interactive = TRUE;
		exprfile = getfile ("Expression file? ", "r");
		if (exprfile == NULL)
			{
			input_by_hand = TRUE;
			exprfile = stdin;
			printf ("Enter ONE expression per line.\n");
			printf ("End with an empty line.\n");
			}
		}
	else if (argv[1][0] == 'E') /*Expra file flag */
		{
		if ((exprfile = fopen (&argv[1][1], "r")) == NULL)
			ERROPEN (&argv[1][1])
		}
	else exprfile = NULL;
	for (;;) /* PARSE expressions until done */
		{
	readexpr:
		if (exprfile == NULL) /*read expras from argv[i] */
			{
			if (++Exprno > argc) break;
			Expra = argv[Exprno];
			}
		else /*read Expras from exprfile */
			{
			++Exprno;
			if (input_by_hand) printf ("expression[%d]: ", Exprno);
			if (getline (exprline, BUFSIZ, exprfile) <= 0) break;
			Expra = exprline;
			}

		while (isspace (*Expra)) Expra++;
		if (*Expra == 'X')
			{
			Should_output[Exprno] = FALSE;
			Expra++;
			}
		else Should_output[Exprno] = TRUE;
		if (yyparse() == PARSERROR) /* call parser */
			{
			fprintf (stderr, "\007dm: error in parsing expr[%d].\n",
				Exprno--);
			if (input_by_hand) goto readexpr; else exit (1);
			}
#ifdef PTREE
		if (interactive)
			{
			printf ("e%d: ", Exprno);
			ptree (Expr[Exprno]);
			putchar ('\n');
			}
#endif
		}
		Nexpr = Exprno - 1;
		if (Nexpr == 0)
			{
			Exprno = 0;
			fprintf (stderr, "dm: \007No expressions were read in\n");
			if (input_by_hand) goto readexpr;
			else exit (1);
			}
		/* OPEN I/O files */
		if (interactive)
			{
			if ((Infile = getfile ("Input file? ", "r")) == NULL)
				Infile = stdin;
			if ((Outfile = getfile ("Output file or pipe? ", "w")) == NULL)
				Outfile = stdout;
			}
		else
			{
			Infile = stdin;
			Outfile = stdout;
			}
	if (Userand) initrand ();
	}


/*	loop runs the process on the input to produce the output */
loop ()
	{
	double	eval ();
	lgl 	skip = FALSE;

	while (getinput () != EOF)
		{
		skip = FALSE;
		for (Exprno = 1; Exprno <= Nexpr; Exprno++)
			if ((Output[Exprno] = eval(Expr[Exprno])) == Suppress)
				{skip = TRUE; break;}
			else if (Output[Exprno] == Exitflag) exit (0);
		if (skip == TRUE) continue;
		OUTLINE += 1.0;
		for (Exprno = 1; Exprno <= Nexpr; Exprno++)
			if (Should_output[Exprno])
				{
				if (Output[Exprno] == Stringflag)
				fprintf (Outfile, "%s",Evalstr[Exprno]);
				else if (Output[Exprno] == Nil) continue;
				else fprintf (Outfile,"%.6g",Output[Exprno]);
				if (Exprno < Nexpr) putc ('\t', Outfile);
				}
		putc ('\n', Outfile);
		/* fflush (Outfile);  why was this needed? */
		}
#ifndef MSDOS /* no popen on MSDOS */
	if (Outpipe) VOID pclose (Outfile);
#endif
	}

int
getinput ()
	{
	int ncols;
	register int col;

	if (getline (Inputline, BUFSIZ, Infile) == EOF) return (EOF);
	if (Userand) Randu = rand()/Maxrand;
	Sum = 0.0;
	INLINE += 1.0;
	for (col = 1; col <= Usecols; col++)
		Str[col][0] = '\0';
	ncols = sstrings (Inputline, Str[1], MAXCOL, MAXSTRING);
	for (col = 1; col <= ncols; col++)
		if (number (Str[col]))
			Sum += Input[col] = atof(Str[col]);
		else
		if (Usenumber[col])
			ERRMSG2 (number expected in column %d on line %.0f, col, INLINE)
	N = ncols;
	return (ncols);
	}


/*
eval is a recursive function that takes a parse tree of an expression,
and returns its value.  The major kluge in this program is how it handles
strings.  Since it wants to return a double, it cannot return a string, so
the use of strings is somewhat restricted.  When eval evals to a string, it
returns STRINGFLAG after setting a global char *Evalstr[Exprno] to the str
MAIN will look for this flag and switch its output to Evalstr[Exprno] rather
than Output[Exprno].
*/
double
eval (expression) ENODE *expression;
	{
	int 	comp;                 /*for string comparisons*/
	int 	sindex, character;    /*for STRINDEX function */
	char	*string_2b_indexed;   /*for STRINDEX function */
	double	tmp1, tmp2;
	char	operator;
	if (expression == NULL) return (0.0);
	if (expression->etype == FLOATPTR)
		return (*expression->contents.num);
	if (expression->etype == STRINGPTR) 
		{
		Evalstr[Exprno] = expression->contents.str;
		return (Stringflag);
		}
	if (expression->etype == STRINGOP)
		{
		switch (expression->contents.opr)
			{
			case '=': /*string compare*/
				comp = strcmp (expression->lchild->contents.str,
					expression->rchild->contents.str);
				return (comp ? 0.0 : 1.0);
			case '>': /*string compare*/
				comp = strcmp (expression->lchild->contents.str,
					expression->rchild->contents.str);
				return (comp > 0 ? 1.0 : 0.0);
			case '<': /*string compare*/
				comp = strcmp (expression->lchild->contents.str,
					expression->rchild->contents.str);
				return (comp < 0 ? 1.0 : 0.0);
			case 'C': /*true is s1 is in s2 */
				comp = substr (expression->lchild->contents.str,
					expression->rchild->contents.str);
				return (comp ? 1.0 : 0.0);
			case '#':
				return ((double) strlen (expression->rchild->contents.str));
			case '[': /* string index */
				sindex = eval (expression->rchild);
				string_2b_indexed = expression->lchild->contents.str;
				character = string_2b_indexed[sindex-1];
				return (1.0 * character);
			}
		}
	operator = expression->contents.opr;
	if (operator == ':') return (0.0); /*dummy for conditional */
	tmp1 = eval (expression->lchild);
	tmp2 = eval (expression->rchild);
	switch (operator)
	{
	case 'x':
		sindex = (int) tmp2;
		if (sindex < 0 || sindex > N)
			ERRMSG1 (computed index for x (%d) is out of range, sindex)
		if (Usenumber[sindex]) return (Input[sindex]);
		if (number (Str[sindex])) return (atof (Str[sindex]));
			ERRMSG2 (number expected in column %d of line %.0f, sindex, INLINE)
	case 'y':
		sindex = (int) tmp2;
		if (sindex >= 0 && sindex <= Nexpr)
		return (Output[sindex]);
		ERRMSG1 (computed index for y (%d) is  out of range, sindex)
	case '_': return (-tmp2);
	case '!': return (fzero (tmp2) ? 1.0 : 0.0);
	case 'l': if (tmp2 <= 0.0)
		ERRMSG3 (log undefined for %f on line %.0f  expr[%d], tmp2, INLINE,Exprno)
		return (log (tmp2));
	case 'L': if (tmp2 <= 0.0)
		ERRMSG3(Log undefined for %f Input line %.0f  expr[%d], tmp2,INLINE,Exprno)
		return (log (tmp2) / LOGe10);
	case 'e': return (exp (tmp2));
	case 'a': return (fabs (tmp2));
	case 'c': return (ceil (tmp2));
	case 'f': return (floor (tmp2));
	case '+': return (tmp1 + tmp2);
	case '-': return (tmp1 - tmp2);
	case '*': return (tmp1 * tmp2);
	case '%': if (fzero (tmp2))
		ERRMSG2 (division by zero. input line %.0f  expr[%d], INLINE,Exprno)
		return ((double) (((int) tmp1) % ((int) tmp2)));
	case '/': if (fzero (tmp2))
		ERRMSG2 (division by zero. input line %.0f  expr[%d], INLINE,Exprno)
		return (tmp1/tmp2);
	case '^':
		if (tmp1 < 0.0 && (floor (tmp2) != tmp2))
			ERRMSG1 (power failure at line %.0f, INLINE)
		return (pow (tmp1, tmp2));
	case '>': return (tmp1 > tmp2 ? 1.0 : 0.0);
	case '<': return (tmp1 < tmp2 ? 1.0 : 0.0);
	case '=': return (fzero (tmp1 - tmp2) ? 1.0 : 0.0);
	case '&': return ((!fzero (tmp1) && !fzero (tmp2)) ? 1.0 : 0.0);
	case '|': return ((!fzero (tmp1) || !fzero (tmp2)) ? 1.0 : 0.0);
	case '?': if (!fzero (tmp1))
		return (eval (expression->rchild->lchild));
		return (eval (expression->rchild->rchild));
	default:
		ERRMSG1 (Unknown operator '%c', operator)
	}
	return (Exitflag);
	}

#ifdef PTREE
ptree (tree) ENODE *tree;
	{
	if (tree == NULL) return;
	if (tree->etype == FLOATPTR)
		if (*tree->contents.num < -LARGE)
			{
			double	*nptr = tree->contents.num;
			if (nptr == &Suppress)
				printf (N_KILL);
			else if (nptr == &Exitflag)
				printf (N_EXIT);
			else if (nptr == &Nil)
				printf (N_NIL);
			else printf ("CONTROL");
			}
		else /* regular number */
			{
			double	*dptr = tree->contents.num;
			if (dptr > Input && dptr <= &Input[MAXCOL])
				printf ("x%d", dptr - Input);
			else if (dptr == &INLINE)
				printf (N_INLINE);
			else if (dptr > Output && dptr <= &Output[MAXCOL])
				printf ("y%d", dptr - Output);
			else if (dptr == &OUTLINE)
				printf (N_OUTLINE);
			else if (dptr == &N)
				printf (N_N);
			else if (dptr == &Sum)
				printf (N_SUM);
			else if (dptr == &Randu)
				printf (N_RAND);
			else	printf ("%g", *dptr);
			}
	else if (tree->etype == STRINGPTR)
		{
		char	*sptr = tree->contents.str;
		if (sptr == Inputline)
			printf (N_INPUT);
		else if (sptr >= Str[0] && sptr < Str[MAXCOL])
			printf ("s%d", (sptr - Str[0])/MAXSTRING);
		else printf ("'%s'", sptr);
		}
	else if (tree->etype == OPERATOR || tree->etype == STRINGOP)
		{
		int 	op = tree->contents.opr;
		printf ("(");
		ptree (tree->lchild);
		switch (op)
			{
			case 'l': printf ("%s ", N_LOG); break;
			case 'L': printf ("%s ", N_LOG10); break;
			case 'e': printf ("%s ", N_EXP); break;
			case 'c': printf ("%s ", N_CEIL); break;
			case 'f': printf ("%s ", N_FLOOR); break;
			case 'a': printf ("%s ", N_ABS); break;
			default:
				printf (" %c ", op);
			}
		ptree (tree->rchild);
		printf (")");
		}
	else printf ("(bad node type %d)", tree->etype);
	}
#endif

FILE *
getfile (prompt, mode) char *prompt, *mode;
	{
#ifndef MSDOS /* no popen on MSDOS */
	FILE	*popen ();
#endif
	FILE	*fopen (), *ioptr;
	char	filename[BUFSIZ];
	char	*ptr = filename;
  newfile:
	printf ("%s", prompt);
	if (getline (filename, MAXSTRING, stdin) <= 0) return (NULL);
	while (isspace (*ptr)) ptr++;
	if (*ptr == NULL) return (NULL);
	if (*mode == 'w')
		if (*ptr == '|') Outpipe = 1;
		else if (!canwrite (filename)) goto newfile;
	if (Outpipe)
		{
#ifndef MSDOS /* no popen on MSDOS */
		if ((ioptr = popen (ptr+1, "w")) == NULL)
#endif
			{
			fprintf (stderr, "Cannot create pipe.\n");
			Outpipe = 0;
			goto newfile;
			}
		}
	else if ((ioptr = fopen (filename, mode)) == NULL)
		{
		printf ("Cannot open '%s'.\n", filename);
		goto newfile;
		}
	return (ioptr);
	}

int
getline (string, maxlen, ioptr)
char	*string;
int 	maxlen;
FILE	*ioptr;
	{
	register	int 	inchar;
	register	int 	len = 0;
	while ((inchar = getc (ioptr)) != EOF)
		{
		if (inchar == '\n') break;
		else if (inchar == '\\')
			if ((inchar = getc (ioptr)) == EOF) inchar = '\\';
		string[len] = inchar;
		if (++len == maxlen) break;
		}
	string[len] = '\0';
	if (len == 0 && feof (ioptr)) return (EOF);
	return (len);
	}


begins (s1, s2) char *s1, *s2;
	{
	while (*s1)
		if (*s1++ != *s2++) return (0);
	return (1);
	/* return (isalpha (*s2) ? 0 : 1);  can't be used because of s1 C s2 */
	}

substr (s1, s2) char *s1, *s2;
	{
	while (*s2)
		if (begins (s1, s2)) return (1);
		else s2++;
	return (0);
	}