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

⟦665aa80fa⟧ TextFile

    Length: 35723 (0x8b8b)
    Types: TextFile
    Names: »dm.c«

Derivation

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

TextFile


# line 2 "dm.y"
#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 ();

# line 122 "dm.y"
typedef union  { /* union of the data types the parser will deal with */
	int 	opr;
	char	*str;
	double	*num;
	ENODE	*ex;
	} YYSTYPE;
# define NUMBER 257
# define STRING 258
# define STRINDEX 259
# define IF 260
# define THEN 261
# define ELSE 262
# define NOR 263
# define NAND 264
# define EQ 265
# define NE 266
# define GE 267
# define GT 268
# define LE 269
# define LT 270
# define NOTIN 271
# define  C 67
# define UMINUS 272
# define  l 108
# define  L 76
# define  e 101
# define  a 97
# define  f 102
# define  c 99
#define yyclearin yychar = -1
#define yyerrok yyerrflag = 0
extern int yychar;
extern short yyerrflag;
#ifndef YYMAXDEPTH
#define YYMAXDEPTH 150
#endif
YYSTYPE yylval, yyval;
# define YYERRCODE 256

# line 386 "dm.y"


/*
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);
	}
short yyexca[] ={
-1, 1,
	0, -1,
	-2, 0,
-1, 66,
	265, 0,
	266, 0,
	267, 0,
	268, 0,
	269, 0,
	270, 0,
	-2, 15,
-1, 67,
	265, 0,
	266, 0,
	267, 0,
	268, 0,
	269, 0,
	270, 0,
	-2, 16,
-1, 68,
	265, 0,
	266, 0,
	267, 0,
	268, 0,
	269, 0,
	270, 0,
	-2, 19,
-1, 69,
	265, 0,
	266, 0,
	267, 0,
	268, 0,
	269, 0,
	270, 0,
	-2, 20,
-1, 70,
	265, 0,
	266, 0,
	267, 0,
	268, 0,
	269, 0,
	270, 0,
	-2, 23,
-1, 71,
	265, 0,
	266, 0,
	267, 0,
	268, 0,
	269, 0,
	270, 0,
	-2, 24,
	};
# define YYNPROD 45
# define YYLAST 396
short yyact[]={

  23,  32,  96,  19,  23,  22,  20,  44,  21,  22,
  24,  23,  32,  25,  24,  47,  22,  20,  46,  21,
  90,  24,  23,  32,   1,   0,  36,  22,  20,   0,
  21,  43,  24,  23,  32,   0,   0,  36,  22,  20,
   2,  21,   0,  24,   0,  23,  32,   0,  36,  88,
  22,  20,   0,  21,   0,  24,  93,  25,   0,  36,
   0,  25,   0,   0,  23,  32,   0,  92,  25,  22,
  20,  36,  21,   0,  24,   0,  23,  76,  91,  25,
   0,  22,  20,   0,  21,   0,  24,  34,   0,   0,
  25,  23,  32,   0,   0,   0,  22,  20,  34,  21,
   0,  24,  25,   0,   9,   0,  16,   0,   0,  34,
   0,   7,   0,   0,   0,   0,   8,  36,  23,   0,
  34,  25,   0,  22,  20,   0,  21,   0,  24,   0,
  94,  95,  34,  25,   0,   0,   0,  97,   0,   0,
   0,   0,   0,   0,   0,   0,   0,  11,  25,   0,
   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
   0,   0,   0,   0,   0,   0,   0,   0,  13,   0,
  15,   0,  12,  14,   0,  25,   0,   0,  34,  10,
   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
   0,   5,   6,   0,   0,   0,   0,   0,   0,   0,
   0,   0,   0,   0,   0,  37,  38,  40,  39,  42,
  41,  45,   0,   0,   0,   0,   0,   0,   0,   0,
   0,   0,   0,   0,   0,   0,  35,  33,  26,  27,
  29,  28,  31,  30,   0,   0,   0,  35,  33,  26,
  27,  29,  28,  31,  30,   0,   0,   0,  35,  33,
  26,  27,  29,  28,  31,  30,   0,  89,   0,  35,
  33,  26,  27,  29,  28,  31,  30,   0,   0,   0,
   0,  35,  33,  26,  27,  29,  28,  31,  30,   0,
   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
   0,  33,  26,  27,  29,  28,  31,  30,  49,   4,
   0,   0,   0,   0,  26,  27,  29,  28,  31,  30,
   0,   0,   0,   0,   0,  58,   0,  35,  33,  26,
  27,  29,  28,  31,  30,   0,   0,   0,  18,  19,
   0,  17,   0,   0,   0,   4,  77,  78,  79,  80,
  81,  82,   0,  84,  85,   0,   3,   0,   0,   0,
   0,   0,   0,   0,  48,  50,  51,  52,  53,  54,
  55,  56,  57,   0,  59,   0,   0,  60,  61,  62,
  63,  64,  65,  66,  67,  68,  69,  70,  71,  72,
  73,  74,  75,   0,   0,   0,   0,   0,   4,   4,
  83,   0,   0,  86,  87,   4 };
short yypact[]={

  71,-1000,-1000,  54, -60, -73, -76,  71,  71,  71,
  71,  71,  71,  71,  71,  71,-255,  71,-1000,-1000,
  71,  71,  71,  71,  71,  71,  71,  71,  71,  71,
  71,  71,  71,  71,  71,  71,  71,-255,-255,-255,
-255,-255,-255,  71,-255,-255,  71,  71,   8, -60,
-1000,  39,-1000,-1000,-1000,-1000,-1000,-1000,-1000,  -4,
 -33, -33, -81, -81, -81, -81,  81,  81,  81,  81,
  81,  81,  39,  39,  27,  27, -38,-1000,-1000,-1000,
-1000,-1000,-1000, -15,-1000,-1000, -26, -37,-1000,  71,
  71,-1000,-1000,-1000,-260,-1000,  71,-1000 };
short yypgo[]={

   0, 346, 298,  40,  24 };
short yyr1[]={

   0,   4,   3,   3,   1,   1,   1,   1,   1,   1,
   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,
   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,
   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,
   1,   1,   1,   1,   2 };
short yyr2[]={

   0,   1,   1,   1,   4,   4,   3,   3,   3,   3,
   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,
   3,   3,   3,   3,   3,   3,   3,   3,   3,   2,
   2,   2,   2,   2,   2,   2,   2,   2,   4,   3,
   3,   5,   6,   1,   1 };
short yychk[]={

-1000,  -4,  -3,  -1,  -2, 120, 121,  40,  45,  33,
 108,  76, 101,  97, 102,  99,  35, 260, 257, 258,
  43,  45,  42,  37,  47,  94, 265, 266, 268, 267,
 270, 269,  38, 264, 124, 263,  63, 265, 266, 268,
 267, 270, 269,  91,  67, 271,  91,  91,  -1,  -2,
  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -2,  -1,
  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
  -1,  -1,  -1,  -1,  -1,  -1,  -3,  -2,  -2,  -2,
  -2,  -2,  -2,  -1,  -2,  -2,  -1,  -1,  41, 261,
  58,  93,  93,  93,  -3,  -3, 262,  -3 };
short yydef[]={

   0,  -2,   1,   2,   3,   0,   0,   0,   0,   0,
   0,   0,   0,   0,   0,   0,   0,   0,  43,  44,
   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  29,  30,  31,  32,  33,  34,  35,  36,  37,   0,
   7,   8,   9,  10,  11,  12,  -2,  -2,  -2,  -2,
  -2,  -2,  25,  26,  27,  28,   0,  13,  14,  17,
  18,  21,  22,   0,  39,  40,   0,   0,   6,   0,
   0,  38,   4,   5,   0,  41,   0,  42 };
#ifndef lint
static char yaccpar_sccsid[] = "@(#)yaccpar	4.1	(Berkeley)	2/11/83";
#endif not lint

#
# define YYFLAG -1000
# define YYERROR goto yyerrlab
# define YYACCEPT return(0)
# define YYABORT return(1)

/*	parser for yacc output	*/

#ifdef YYDEBUG
int yydebug = 0; /* 1 for debugging */
#endif
YYSTYPE yyv[YYMAXDEPTH]; /* where the values are stored */
int yychar = -1; /* current input token number */
int yynerrs = 0;  /* number of errors */
short yyerrflag = 0;  /* error recovery flag */

yyparse() {

	short yys[YYMAXDEPTH];
	short yyj, yym;
	register YYSTYPE *yypvt;
	register short yystate, *yyps, yyn;
	register YYSTYPE *yypv;
	register short *yyxi;

	yystate = 0;
	yychar = -1;
	yynerrs = 0;
	yyerrflag = 0;
	yyps= &yys[-1];
	yypv= &yyv[-1];

 yystack:    /* put a state and value onto the stack */

#ifdef YYDEBUG
	if( yydebug  ) printf( "state %d, char 0%o\n", yystate, yychar );
#endif
		if( ++yyps> &yys[YYMAXDEPTH] ) { yyerror( "yacc stack overflow" ); return(1); }
		*yyps = yystate;
		++yypv;
		*yypv = yyval;

 yynewstate:

	yyn = yypact[yystate];

	if( yyn<= YYFLAG ) goto yydefault; /* simple state */

	if( yychar<0 ) if( (yychar=yylex())<0 ) yychar=0;
	if( (yyn += yychar)<0 || yyn >= YYLAST ) goto yydefault;

	if( yychk[ yyn=yyact[ yyn ] ] == yychar ){ /* valid shift */
		yychar = -1;
		yyval = yylval;
		yystate = yyn;
		if( yyerrflag > 0 ) --yyerrflag;
		goto yystack;
		}

 yydefault:
	/* default state action */

	if( (yyn=yydef[yystate]) == -2 ) {
		if( yychar<0 ) if( (yychar=yylex())<0 ) yychar = 0;
		/* look through exception table */

		for( yyxi=yyexca; (*yyxi!= (-1)) || (yyxi[1]!=yystate) ; yyxi += 2 ) ; /* VOID */

		while( *(yyxi+=2) >= 0 ){
			if( *yyxi == yychar ) break;
			}
		if( (yyn = yyxi[1]) < 0 ) return(0);   /* accept */
		}

	if( yyn == 0 ){ /* error */
		/* error ... attempt to resume parsing */

		switch( yyerrflag ){

		case 0:   /* brand new error */

			yyerror( "syntax error" );
		yyerrlab:
			++yynerrs;

		case 1:
		case 2: /* incompletely recovered error ... try again */

			yyerrflag = 3;

			/* find a state where "error" is a legal shift action */

			while ( yyps >= yys ) {
			   yyn = yypact[*yyps] + YYERRCODE;
			   if( yyn>= 0 && yyn < YYLAST && yychk[yyact[yyn]] == YYERRCODE ){
			      yystate = yyact[yyn];  /* simulate a shift of "error" */
			      goto yystack;
			      }
			   yyn = yypact[*yyps];

			   /* the current yyps has no shift onn "error", pop stack */

#ifdef YYDEBUG
			   if( yydebug ) printf( "error recovery pops state %d, uncovers %d\n", *yyps, yyps[-1] );
#endif
			   --yyps;
			   --yypv;
			   }

			/* there is no state on the stack with an error shift ... abort */

	yyabort:
			return(1);


		case 3:  /* no shift yet; clobber input char */

#ifdef YYDEBUG
			if( yydebug ) printf( "error recovery discards char %d\n", yychar );
#endif

			if( yychar == 0 ) goto yyabort; /* don't discard EOF, quit */
			yychar = -1;
			goto yynewstate;   /* try again in the same state */

			}

		}

	/* reduction by production yyn */

#ifdef YYDEBUG
		if( yydebug ) printf("reduce %d\n",yyn);
#endif
		yyps -= yyr2[yyn];
		yypvt = yypv;
		yypv -= yyr2[yyn];
		yyval = yypv[1];
		yym=yyn;
			/* consult goto table to find next state */
		yyn = yyr1[yyn];
		yyj = yypgo[yyn] + *yyps + 1;
		if( yyj>=YYLAST || yychk[ yystate = yyact[yyj] ] != -yyn ) yystate = yyact[yypgo[yyn]];
		switch(yym){
			
case 1:
# line 146 "dm.y"
{
		Expr[Exprno] = yypvt[-0].ex;
		} break;
case 2:
# line 151 "dm.y"
{
		yyval.ex = yypvt[-0].ex;
		} break;
case 3:
# line 155 "dm.y"
{
		yyval.ex = yypvt[-0].ex;
		} break;
case 4:
# line 160 "dm.y"
{
		Tmp1.opr = 'x';
		yyval.ex = node (&Tmp1, OPERATOR, ENULL, yypvt[-1].ex);
		} break;
case 5:
# line 165 "dm.y"
{
		Tmp1.opr = 'y';
		yyval.ex = node (&Tmp1, OPERATOR, ENULL, yypvt[-1].ex);
		} break;
case 6:
# line 170 "dm.y"
{
		yyval.ex = yypvt[-1].ex;
		} break;
case 7:
# line 174 "dm.y"
{
		Tmp1.opr = '+';
		yyval.ex = node (&Tmp1, OPERATOR, yypvt[-2].ex, yypvt[-0].ex);
		} break;
case 8:
# line 179 "dm.y"
{
		Tmp1.opr = '-';
		yyval.ex = node (&Tmp1, OPERATOR, yypvt[-2].ex, yypvt[-0].ex);
		} break;
case 9:
# line 184 "dm.y"
{
		Tmp1.opr = '*';
		yyval.ex = node (&Tmp1, OPERATOR, yypvt[-2].ex, yypvt[-0].ex);
		} break;
case 10:
# line 189 "dm.y"
{
		Tmp1.opr = '%';
		yyval.ex = node (&Tmp1, OPERATOR, yypvt[-2].ex, yypvt[-0].ex);
		} break;
case 11:
# line 194 "dm.y"
{
		Tmp1.opr = '/';
		yyval.ex = node (&Tmp1, OPERATOR, yypvt[-2].ex, yypvt[-0].ex);
		} break;
case 12:
# line 199 "dm.y"
{
		Tmp1.opr = '^';
		yyval.ex = node (&Tmp1, OPERATOR, yypvt[-2].ex, yypvt[-0].ex);
		} break;
case 13:
# line 204 "dm.y"
{
		Tmp1.opr = '=';
		yyval.ex = node (&Tmp1, STRINGOP, yypvt[-2].ex, yypvt[-0].ex);
		} break;
case 14:
# line 209 "dm.y"
{
		Tmp1.opr = '!';
		Tmp2.opr = '=';
		yyval.ex = node (&Tmp1, OPERATOR, ENULL,
			node (&Tmp2, STRINGOP, yypvt[-2].ex, yypvt[-0].ex));
		} break;
case 15:
# line 216 "dm.y"
{
		Tmp1.opr = '=';
		yyval.ex = node (&Tmp1, OPERATOR, yypvt[-2].ex, yypvt[-0].ex);
		} break;
case 16:
# line 221 "dm.y"
{
		Tmp1.opr = '!';
		Tmp2.opr = '=';
		yyval.ex = node (&Tmp1, OPERATOR, ENULL,
			node (&Tmp2, OPERATOR, yypvt[-2].ex, yypvt[-0].ex));
		} break;
case 17:
# line 228 "dm.y"
{
		Tmp1.opr = '>';
		yyval.ex = node (&Tmp1, STRINGOP, yypvt[-2].ex, yypvt[-0].ex);
		} break;
case 18:
# line 233 "dm.y"
{
		Tmp1.opr = '!';
		Tmp2.opr = '<';
		yyval.ex = node (&Tmp1, OPERATOR, ENULL,
			node (&Tmp2, STRINGOP, yypvt[-2].ex, yypvt[-0].ex));
		} break;
case 19:
# line 240 "dm.y"
{
		Tmp1.opr = '>';
		yyval.ex = node (&Tmp1, OPERATOR, yypvt[-2].ex, yypvt[-0].ex);
		} break;
case 20:
# line 245 "dm.y"
{
		Tmp1.opr = '!';
		Tmp2.opr = '<';
		yyval.ex = node (&Tmp1, OPERATOR, ENULL,
			node (&Tmp2, OPERATOR, yypvt[-2].ex, yypvt[-0].ex));
		} break;
case 21:
# line 252 "dm.y"
{
		Tmp1.opr = '<';
		yyval.ex = node (&Tmp1, STRINGOP, yypvt[-2].ex, yypvt[-0].ex);
		} break;
case 22:
# line 257 "dm.y"
{
		Tmp1.opr = '!';
		Tmp2.opr = '>';
		yyval.ex = node (&Tmp1, OPERATOR, ENULL,
			node (&Tmp2, STRINGOP, yypvt[-2].ex, yypvt[-0].ex));
		} break;
case 23:
# line 264 "dm.y"
{
		Tmp1.opr = '<';
		yyval.ex = node (&Tmp1, OPERATOR, yypvt[-2].ex, yypvt[-0].ex);
		} break;
case 24:
# line 269 "dm.y"
{
		Tmp1.opr = '!';
		Tmp2.opr = '>';
		yyval.ex = node (&Tmp1, OPERATOR, ENULL,
			node (&Tmp2, OPERATOR, yypvt[-2].ex, yypvt[-0].ex));
		} break;
case 25:
# line 276 "dm.y"
{
		Tmp1.opr = '&';
		yyval.ex = node (&Tmp1, OPERATOR, yypvt[-2].ex, yypvt[-0].ex);
		} break;
case 26:
# line 281 "dm.y"
{
		Tmp1.opr = '!';
		Tmp2.opr = '&';
		yyval.ex = node (&Tmp1, OPERATOR, ENULL,
			node (&Tmp2, OPERATOR, yypvt[-2].ex, yypvt[-0].ex));
		} break;
case 27:
# line 288 "dm.y"
{
		Tmp1.opr = '|';
		yyval.ex = node (&Tmp1, OPERATOR, yypvt[-2].ex, yypvt[-0].ex);
		} break;
case 28:
# line 293 "dm.y"
{
		Tmp1.opr = '!';
		Tmp2.opr = '|';
		yyval.ex = node (&Tmp1, OPERATOR, ENULL,
			node (&Tmp2, OPERATOR, yypvt[-2].ex, yypvt[-0].ex));
		} break;
case 29:
# line 300 "dm.y"
{
		Tmp1.opr = '_';
		yyval.ex = node (&Tmp1, OPERATOR, ENULL, yypvt[-0].ex);
		} break;
case 30:
# line 305 "dm.y"
{
		Tmp1.opr = '!';
		yyval.ex = node (&Tmp1, OPERATOR, ENULL, yypvt[-0].ex);
		} break;
case 31:
# line 310 "dm.y"
{
		Tmp1.opr = 'l';
		yyval.ex = node (&Tmp1, OPERATOR, ENULL, yypvt[-0].ex);
		} break;
case 32:
# line 315 "dm.y"
{
		Tmp1.opr = 'L';
		yyval.ex = node (&Tmp1, OPERATOR, ENULL, yypvt[-0].ex);
		} break;
case 33:
# line 320 "dm.y"
{
		Tmp1.opr = 'e';
		yyval.ex = node (&Tmp1, OPERATOR, ENULL, yypvt[-0].ex);
		} break;
case 34:
# line 325 "dm.y"
{
		Tmp1.opr = 'a';
		yyval.ex = node (&Tmp1, OPERATOR, ENULL, yypvt[-0].ex);
		} break;
case 35:
# line 330 "dm.y"
{
		Tmp1.opr = 'f';
		yyval.ex = node (&Tmp1, OPERATOR, ENULL, yypvt[-0].ex);
		} break;
case 36:
# line 335 "dm.y"
{
		Tmp1.opr = 'c';
		yyval.ex = node (&Tmp1, OPERATOR, ENULL, yypvt[-0].ex);
		} break;
case 37:
# line 340 "dm.y"
{
		Tmp1.opr = '#';
		yyval.ex = node (&Tmp1, STRINGOP, ENULL, yypvt[-0].ex);
		} break;
case 38:
# line 345 "dm.y"
{
		Tmp1.opr = '[';
		yyval.ex = node (&Tmp1, STRINGOP, yypvt[-3].ex, yypvt[-1].ex);
		} break;
case 39:
# line 350 "dm.y"
{
		Tmp1.opr = 'C';
		yyval.ex = node (&Tmp1, STRINGOP, yypvt[-2].ex, yypvt[-0].ex);
		} break;
case 40:
# line 355 "dm.y"
{
		Tmp1.opr = '!';
		Tmp2.opr = 'C';
		yyval.ex = node (&Tmp1, OPERATOR, ENULL,
			node (&Tmp2, STRINGOP, yypvt[-2].ex, yypvt[-0].ex));
		} break;
case 41:
# line 362 "dm.y"
{
		Tmp1.opr = '?';
		Tmp2.opr = ':';
		yyval.ex = node (&Tmp1, OPERATOR, yypvt[-4].ex,
			node (&Tmp2, OPERATOR,  yypvt[-2].ex, yypvt[-0].ex));
		} break;
case 42:
# line 369 "dm.y"
{
		Tmp1.opr = '?';
		Tmp2.opr = ':';
		yyval.ex = node (&Tmp1, OPERATOR, yypvt[-4].ex,
			node (&Tmp2, OPERATOR, yypvt[-2].ex, yypvt[-0].ex));
		} break;
case 43:
# line 376 "dm.y"
{
		Tmp1.num = yypvt[-0].num;
		yyval.ex = node (&Tmp1, FLOATPTR, ENULL, ENULL);
		} break;
case 44:
# line 382 "dm.y"
{
		Tmp1.str = yypvt[-0].str;
		yyval.ex = node (&Tmp1, STRINGPTR, ENULL, ENULL);
		} break; 
		}
		goto yystack;  /* stack new state and value */

	}