/*
 * $Id: expr2.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $
 * math.c - mathematical expression evaluation
 * This file is based on 'math.c', which is part of zsh, the Z shell.
 *
 * Copyright (c) 1992-1997 Paul Falstad, All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the copyright notice,
 *    this list of conditions and the two following paragraphs.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimers in the
 *    documentation and/or other materials provided with the distribution
 * 3. The names of the author(s) may not be used to endorse or promote
 *    products derived from this software without specific prior written
 *    permission.
 *
 * In no event shall Paul Falstad or the Zsh Development Group be liable
 * to any party for direct, indirect, special, incidental, or consequential
 * damages arising out of the use of this software and its documentation,
 * even if Paul Falstad and the Zsh Development Group have been advised of
 * the possibility of such damage.
 *
 * Paul Falstad and the Zsh Development Group specifically disclaim any
 * warranties, including, but not limited to, the implied warranties of
 * merchantability and fitness for a particular purpose.  The software
 * provided hereunder is on an "as is" basis, and Paul Falstad and the
 * Zsh Development Group have no obligation to provide maintenance,
 * support, updates, enhancements, or modifications.
 *
 */
/*
 * Significant modifications by Jeremy Nelson
 * Coypright 1998 EPIC Software Labs, All rights reserved.
 *
 * You may distribute this file under the same terms as the above, by 
 * including the parties "Jeremy Nelson" and "EPIC Software Labs" to the 
 * limitations of liability and the express disclaimer of all warranties.  
 * This software is provided "AS IS".
 *
 * $Id: expr2.c,v 1.1.1.1 2003/04/11 01:09:07 dan Exp $
 */

#include "irc.h"

#include <math.h>
#include "debug.h"

/* XXX in expr.c */
#if 0
static char *alias_special_char(char **, char *, const char *, char *, int *);
#endif

#define STACKSZ 	100
#define TOKENCOUNT	256
#define MAGIC_TOKEN	-14

/*
 * One thing of note is that while the original did only ints, we really
 * only do strings.  We convert to and from ints as neccesary.  Icky,
 * but given the semantics we require its the only way.
 */
/*
 * All the information for each expression is stored in a struct.  This
 * is done so that there are no global variables in use (theyre all collected
 * making them easier to handle), and makes re-entrancy much easier since 
 * i dont have to ask myself "have i accounted for the old state of all the
 * global variables?"
 */

/*
 * When we want to refer symbolically to a token, we just sling around
 * the integer index to the token table.  This serves two purposes:  We
 * dont have to worry about whether something is malloced or not, or who
 * is resopnsible to free, or anything like that.  If you want to keep 
 * something around, you tokenize() it and that returns a "handle" to the
 * token and then you pass that handle around.  So the pair (context,handle)
 * refers to a unique, usable token. 
 */
typedef int	TOKEN;

/*
 * This sets up whether we do floating point math or integer math
 */
#ifdef FLOATING_POINT_MATH			/* XXXX This doesnt work yet */
  typedef	double		NUMBER;
  typedef 	long		BooL;
# define STON atof
# define NTOS ftoa
#else
  typedef	unsigned long	NUMBER;
  typedef 	long		BooL;
# define STON atol
# define NTOS ltoa
#endif

typedef struct
{
	/* POSITION AND STATE INFORMATION */
	/*
	 * This is the current position in the lexing.
	 */
	char	*ptr;

	/*
	 * When set, the expression is lexed, but nothing that may have a side
	 * effect (function calls, assignments, etc) are actually performed.
	 * Dummy values are instead substituted.
	 */
	int	noeval;

	/* 
	 * When set, this means the next token may either be a prefix operator
	 * or an operand.  When clear, it means the next operator must be a
	 * non-prefix operator.
	 */
	int	operand;


	/* TOKEN TABLE */
	/*
	 * Each registered 'token' is given a TOKEN id.  The idea is
	 * that we want TOKEN to be an opaque type to be used to refer
	 * to a token in a generic way, but in practice its just an integer
	 * offset into a char ** table.  We register all tokens sequentially,
	 * so this just gets incremented when we want to register a new token.
	 */
	TOKEN	token;

	/*
	 * This is the list of operand (string) tokens we have extracted
	 * so far from the expression.  Offsets into this array are stored
	 * into the parsing stack.
	 */
	char *	tokens[TOKENCOUNT + 1];


	/* OPERAND STACK */
	/*
	 * This is the operand shift stack.  These are the operands that
	 * are currently awaiting reduction.  Note that rather than keeping
	 * track of the lvals and rvals here, we simply keep track of offsets
	 * to the 'tokens' table that actually stores all the relevant data.
	 * Then we can just call the token-class functions to get that data.
	 * This is more efficient because it allows us to recycle tokens
	 * more reasonably without wasteful malloc-copies.
	 */
	TOKEN 	stack[STACKSZ + 1];

	/* Current index to the operand stack */
	int	sp;

	/* This is the last token that was lexed. */
	TOKEN	mtok;

	/* This is set when an error happens */
	int	errflag;

	TOKEN	last_token;

	const char	*args;
	int	*args_flag;
} expr_info;

__inline static	TOKEN	tokenize (expr_info *c, const char *t);
	 static char *	after_expando_special (expr_info *c);

void	setup_expr_info (expr_info *c)
{
	int	i;

	c->ptr = NULL;
	c->noeval = 0;
	c->operand = 1;
	c->token = 0;
	for (i = 0; i <= TOKENCOUNT; i++)
		c->tokens[i] = NULL;
	for (i = 0; i <= STACKSZ; i++)
		c->stack[i] = 0;
	c->sp = -1;
	c->mtok = 0;
	c->errflag = 0;
	c->last_token = 0;
	tokenize(c, empty_string);	/* Always token 0 */
}

void 	destroy_expr_info (expr_info *c)
{
	int	i;

	c->ptr = NULL;
	c->noeval = -1;
	c->operand = -1;
	for (i = 0; i < c->token; i++)
		new_free(&c->tokens[i]);
	c->token = -1;
	for (i = 0; i <= STACKSZ; i++)
		c->stack[i] = -1;
	c->sp = -1;
	c->mtok = -1;
	c->errflag = -1;
	c->last_token = -1;
}


/* 
 * LR = left-to-right associativity
 * RL = right-to-left associativity
 * BOO = short-circuiting boolean
 */
#define LR 		0
#define RL 		1
#define BOOL 		2

/*
 * These are all the token-types.  Each of the operators is represented,
 * as is the generic operand type
 */

enum LEX {
	M_INPAR,
	NOT, 		COMP, 		PREMINUS,	PREPLUS,
			UPLUS,		UMINUS,		STRLEN,
			WORDC,		DEREF,
	POWER,
	MUL,		DIV,		MOD,
	PLUS,		MINUS,		STRCAT,
	SHLEFT,		SHRIGHT,
	LES,		LEQ,		GRE,		GEQ,
	MATCH,		NOMATCH,
	DEQ,		NEQ,
	AND,
	XOR,
	OR,
	DAND,
	DXOR,
	DOR,
	QUEST,		COLON,
	EQ,		PLUSEQ,		MINUSEQ,	MULEQ,		DIVEQ,
			MODEQ,		ANDEQ,		XOREQ,		OREQ,
			SHLEFTEQ,	SHRIGHTEQ,	DANDEQ,		DOREQ,
			DXOREQ,		POWEREQ,	STRCATEQ,    STRPREEQ,
			SWAP,
	COMMA,
	POSTMINUS,	POSTPLUS,
	ID,
	M_OUTPAR,
	EERROR,
	EOI,
	TOKCOUNT
};


/*
 * Precedence table:  Operators with a lower precedence VALUE have a higher
 * precedence.  The theory behind infix notation (algebraic notation) is that
 * you have a sequence of operands seperated by (typically binary) operators.
 * The problem of precedence is that each operand is surrounded by two
 * operators, so it is ambiguous which operator the operand "binds" to.  This
 * is resolved by "precedence rules" which state that given two operators,
 * which one is allowed to "reduce" (operate on) the operand.  For a simple
 * explanation, take the expression  (3+4*5).  Now the middle operand is a
 * '4', but we dont know if it should be reduced via the plus, or via the
 * multiply.  If we look up both operators in the prec table, we see that
 * multiply has the lower value -- therefore the 4 is reduced via the multiply
 * and then the result of the multiply is reduced by the addition.
 */
static	int	prec[TOKCOUNT] = 
{
	1,
	2,		2,		2,		2,
			2,		2,		2,
			2,		2,
	3,
	4,		4,		4,
	5,		5,		5,
	6,		6,
	7,		7,		7,		7,
	8,		8,
	9,		9,
	10,
	11,
	12,
	13,
	14,
	15,
	16,		16,
	17,		17,		17,		17,		17,
			17,		17,		17,		17,
			17,		17,		17,		17,
			17,		17,		17,		17,
			17,
	18,
	2,		2,
	0,
	137,
	156,
	200
};
#define TOPPREC 21


/*
 * Associativity table: But precedence isnt enough.  What happens when you
 * have two identical operations to determine between?  Well, the easy way
 * is to say that the first operation is always done first.  But some
 * operators dont work that way (like the assignment operator) and always
 * reduce the LAST (or rightmost) operation first.  For example:
 *	(3+4+5)	    ((4+3)+5)    (7+5)    (12)
 *      (v1=v2=3)   (v1=(v2=3))  (v1=3)   (3)
 * So for each operator we need to specify how to determine the precedence
 * of the same operator against itself.  This is called "associativity".
 * Left-to-right associativity means that the operations occur left-to-right,
 * or first-operator, first-reduced.  Right-to-left associativity means
 * that the operations occur right-to-left, or last-operator, first-reduced.
 * 
 * We have a special case of associativity called BOOL, which is just a 
 * special type of left-to-right associtivity whereby the right hand side
 * of the operand is not automatically parsed. (not really, but its the 
 * theory that counts.)
 */
static 	int 	assoc[TOKCOUNT] =
{
	LR,
	RL,		RL,		RL,		RL,
			RL,		RL,		RL,
			RL,		RL,
	RL,
	LR,		LR,		LR,
	LR,		LR,		LR,
	LR,		LR,
	LR,		LR,		LR,		LR,
	LR,		LR,
	LR,		LR,
	LR,
	LR,
	LR,
	BOOL,
	BOOL,
	BOOL,
	RL,		RL,
	RL,		RL,		RL,		RL,		RL,
			RL,		RL,		RL,		RL,
			RL,		RL,		RL,		RL,
			RL,		RL,		RL,		RL,
			RL,
	LR,
	RL,		RL,
	LR,
	LR,
	LR,
	LR
};


/* ********************* OPERAND TOKEN REPOSITORY ********************** */
/*
 * This is where we store our lvalues, kind of.  What we really store here
 * are all of the string operands.  Actually, we store all of the operands
 * here.  When an operand is parsed, its converted to a string and put in
 * here and the index into the 'token' table is returned.
 */
/* THIS FUNCTION MAKES A NEW COPY OF 'T'.  YOU MUST DISPOSE OF 'T' YOURSELF */
__inline static	TOKEN		tokenize (expr_info *c, const char *t)
{
	if (c->token >= TOKENCOUNT)
	{
		error("Too many tokens for this expression");
		return -1;
	}
	c->tokens[c->token] = m_strdup(t);
	return c->token++;
}

/* get_token gets the ``lvalue'', or original text, of a token */
/* YOU MUST _NOT_ FREE THE RETURN VALUE FROM THIS FUNCTION! */
__inline static	const char *		get_token (expr_info *c, TOKEN v)
{
	if (v == MAGIC_TOKEN)	/* Magic token */
		return c->args;

	if (v < 0 || v >= c->token)
	{
		error("Token index [%d] is out of range", v);
		return get_token(c, 0);	/* The empty token */
	}
	return c->tokens[v];
}



/*
 * These three functions ``getsval'', ``getival'', and ``getbval'' take
 * as arguments token-indexes, and return the appropriate rvalue for those
 * tokens.  For literal text strings, they are simply expanded and returned.
 * For function calls, the function is called, and the return value is 
 * returned.  For variable references, the value is looked up and returned.
 * Furthermore, ``getival'' takes this value and converts it into a long
 * int and returns that.  ``getbval'' takes the value and checks it for its
 * truth or falseness (as determined by check_val()).
 */
/* YOU MUST FREE THE RETURN VALUE FROM THIS FUNCTION! */
static	char *	getsval2 (expr_info *c, TOKEN s);
static char * getsval (expr_info *c, TOKEN s)
{
	char *		retval;
	const char *	t;

	t = get_token(c, s);
	if (x_debug & DEBUG_NEW_MATH_DEBUG)
		debugyell(">>> Expanding token [%d]: [%s]", s, t);
	retval = getsval2(c, s);
	if (x_debug & DEBUG_NEW_MATH_DEBUG)
		debugyell("<<< Expanded token [%d]: [%s] to: [%s]", s, t, retval);
	return retval;
}

/* XXX Ick. :-D */
static	char *	getsval2 (expr_info *c, TOKEN s)
{
	const	char	*t;

	if (c->noeval || s == 0)
		return m_strdup(get_token(c, 0));

	/* XXXX Bleh */
	if (s == MAGIC_TOKEN)
	{
		*c->args_flag = 1;
		return m_strdup(c->args);
	}

	t = get_token(c, s);

	/*
	 * Handle [string] token types.
	 */
	if (*t == '[')
	{
		t++;

		/*
	 	 * Attempt to handle $0 and friends here.  Also handle
		 * Things like $1-, and also $*.
		 */
		if (*t == '$')
		{
		   if (t[1] == '*' && !t[2])
			return m_strdup(c->args);
		   else
		   {
			char *	end = NULL;
			long 	j = strtol(t + 1, &end, 10);

			/* Handle [$X] */
			if (end && !*end)
			{
				*c->args_flag = 1;

				if (j < 0)
					return extract2(c->args, SOS, -j);
				else
					return extract2(c->args, j, j);
			}

			/* Gracefully handle [$X-] as well */
			else if (*end == '-' && !end[1])
			{
				*c->args_flag = 1;
				return extract2(c->args, j, EOS);
			}

			/* Anything else we dont grok */
			else
				return expand_alias(t, c->args, 
							c->args_flag, NULL);
		   }
		}

		/* Handle [plain text] */
		else if (!strchr(t, '$') && !strchr(t, '\\'))
			return m_strdup(t);

		/* Everything else gets expanded per normal */
		else
			return expand_alias(t, c->args, c->args_flag, NULL);
	}

	/* Do this first, its cheap */
	else if (is_number(t))
		return m_strdup(t);

	/* Figure out if its a variable reference or a function call */
	else
	{
		char	*after,
			*ptr,
			*w,
			saver = 0;
		int	func = 0;

		w = LOCAL_COPY(t);
		after = after_expando(w, 0, &func);
		if (after)
		{
			saver = *after;
			*after = 0;
		}

		if (func)
			ptr = call_function(w, c->args, c->args_flag);
		else
			ptr = get_variable_with_args(w, c->args, c->args_flag);

		if (!ptr)
			return m_strdup(empty_string);
		if (after)
			*after = saver;
		return ptr;
	}

	return NULL /* <whatever> */;
}

__inline static  NUMBER	getnval (expr_info *c, TOKEN s)
{
	char	*t;
	NUMBER	retval;

	if (c->noeval)
		return 0;

	if (!(t = getsval(c, s)))
		return 0;

	retval = STON(t);
	new_free(&t);
	return retval; 
}

#ifdef notused
__inline static  BooL	getbval (expr_info *c, TOKEN s)
{
	char	*t;
	long	retval;

	if (c->noeval)
		return 0;

	if (!(t = getsval(c, s)))
		return 0;

	retval = check_val(t);
	new_free(&t);
	return retval;
}
#endif


/* ******************** ASSIGNING TO VARIABLES ************************** */
/*
 * When you have an lvalue (left hand side of an assignment) that needs to
 * be assigned to, then you can call these functions to assign to it the
 * appropriate type.  The basic operation is to assign and rvalue token
 * to an lvalue token.  But some times you dont always have a tokenized
 * rvalue, so you can just pass in a raw value and we will tokenize it for
 * you and go from there.  Note that the "result" of an assignment is the
 * rvalue token.  This is then pushed back onto the stack.
 */
__inline static	TOKEN	setvar (expr_info *c, TOKEN l, TOKEN r)
{
	char *t = expand_alias(get_token(c, l), c->args, c->args_flag, NULL);
	char *u = getsval(c, r);
	char *s;

	if (!c->noeval)
	{
		int 	old_window_display = window_display;
		window_display = 0;
		add_var_alias(t, u);
		window_display = old_window_display;
	}

	s = alloca(strlen(u) + 3);
	s[0] = '[';
	strcpy(s+1, u);

	new_free(&t);
	new_free(&u);
	return tokenize(c, s);
}

__inline static TOKEN 	setnvar (expr_info *c, TOKEN l, NUMBER v) 
{
	 return setvar(c, l, tokenize(c, NTOS(v)));
}

__inline static	TOKEN	setsvar (expr_info *c, TOKEN l, char *v)
{
	char	*s;

	s = alloca(strlen(v) + 3);
	s[0] = '[';
	strcpy(s+1, v);
	return setvar(c, l, tokenize(c, s));
}




/* *************************** OPERAND STACK ************************** */


/*
 * Adding (shifting) and Removing (reducing) operands from the stack is a 
 * fairly straightforward process.  The general way to add an token to
 * the stack is to pass in its TOKEN index.  However, there are some times
 * when you want to shift a value that has not been tokenized.  So you call
 * one of the other functions that will do this for you.
 */
__inline static	TOKEN	pusht (expr_info *c, TOKEN t)
{
	if (c->sp == STACKSZ - 1)
	{
		error("Expressions may not have more than 99 operands");
		return -1;
	}
	else
		c->sp++;

	if (x_debug & DEBUG_NEW_MATH_DEBUG)
		debugyell("Pushing token [%d] [%s]", t, get_token(c, t));
	return ((c->stack[c->sp] = t));
}

__inline static	TOKEN	pushn (expr_info *c, NUMBER val)
{ 
	return pusht(c, tokenize(c, NTOS(val)));
}

__inline static	TOKEN	pushs (expr_info *c, char *val)
{
	char	*blah;
	blah = alloca(strlen(val) + 2);
	sprintf(blah, "[%s", val);
	return pusht(c, tokenize(c, blah)); 
}

__inline static TOKEN	top (expr_info *c)
{
	if (c->sp < 0)
	{
		error("No operands.");
		return -1;
	}
	else
		return c->stack[c->sp];
}

__inline static	TOKEN	pop (expr_info *c)
{
	if (c->sp < 0)
	{
		/* 
		 * Attempting to pop more operands than are available
		 * Yeilds empty values.  Thats probably the most reasonable
		 * course of action.
		 */
		error("Cannot pop operand: no more operands");
		return 0;
	}
	else
		return c->stack[c->sp--];
}

__inline static	double	popn (expr_info *c)
{
	char *	x = getsval(c, pop(c));
	NUMBER	i = atof(x);

	new_free(&x);
	return i;
}

/* YOU MUST FREE THE RETURN VALUE FROM THIS FUNCTION */
__inline static	char *	pops (expr_info *c)
{
	return getsval(c, pop(c));
}

__inline static BooL	popb (expr_info *c)
{
	char *	x = getsval(c, pop(c));
	BooL	i = check_val(x);

	new_free(&x);
	return i;
}

__inline static void	pop2 (expr_info *c, TOKEN *t1, TOKEN *t2)
{
	*t2 = pop(c);
	*t1 = pop(c);
}

__inline static	void	pop2n (expr_info *c, NUMBER *a, NUMBER *b)
{
	TOKEN	t1, t2;
	char	*x, *y;

	pop2(c, &t1, &t2);
	x = getsval(c, t1);
	y = getsval(c, t2);
	*a = STON(x);
	*b = STON(y);
	new_free(&x);
	new_free(&y);
}

__inline static void	pop2s (expr_info *c, char **s, char **t)
{
	TOKEN	t1, t2;
	char	*x, *y;

	pop2(c, &t1, &t2);
	x = getsval(c, t1);
	y = getsval(c, t2);
	*s = x;
	*t = y;
}

__inline static void	pop2b (expr_info *c, BooL *a, BooL *b)
{
	TOKEN	t1, t2;
	char	*x, *y;

	pop2(c, &t1, &t2);
	x = getsval(c, t1);
	y = getsval(c, t2);
	*a = check_val(x);
	*b = check_val(y);
	new_free(&x);
	new_free(&y);
}

__inline static	void	pop2n_a (expr_info *c, NUMBER *a, NUMBER *b, TOKEN *v)
{
	TOKEN	t1, t2;
	char	*x, *y;

	pop2(c, &t1, &t2);
	x = getsval(c, t1);
	y = getsval(c, t2);
	*a = STON(x);
	*b = STON(y);
	*v = t1;
	new_free(&x);
	new_free(&y);
}

__inline static	void	pop2s_a (expr_info *c, char **s, char **t, TOKEN *v)
{
	TOKEN	t1, t2;
	char	*x, *y;

	pop2(c, &t1, &t2);
	x = getsval(c, t1);
	y = getsval(c, t2);
	*s = x;
	*t = y;
	*v = t1;
}

#if notused
__inline static void	pop2b_a (expr_info *c, BooL *a, BooL *b, TOKEN *v)
{
	TOKEN	t1, t2;
	char	*x, *y;

	pop2(c, &t1, &t2);
	x = getsval(c, t1);
	y = getsval(c, t2);
	*a = check_val(x);
	*b = check_val(y);
	*v = t1;
	new_free(&x);
	new_free(&y);
}
#endif

__inline static	void	pop3 (expr_info *c, NUMBER *a, TOKEN *v, TOKEN *w)
{
	TOKEN	t1, t2, t3;
	char	*x;

	t3 = pop(c);
	t2 = pop(c);
	t1 = pop(c);

	x = getsval(c, t1);
	*a = STON(x);
	*v = t2;
	*w = t3;
	new_free(&x);
}



/*
 * This is the reducer.  It takes the relevant arguments off the argument
 * stack and then performs the neccesary operation on them.
 */
void	op (expr_info *cx, int what)
{
	NUMBER	a, b;
	BooL	c, d;
	char	*s, *t;
	TOKEN	v, w;

	if (x_debug & DEBUG_NEW_MATH_DEBUG)
		debugyell("Reducing last operation...");

	if (cx->sp < 0) {
		error("An operator is missing a required operand");
		return;
	}

	if (cx->errflag)
		return;		/* Dont parse on an error */

#define BINARY(x) \
	{ \
		pop2n(cx, &a, &b); \
		pushn(cx, (x)); \
		if (x_debug & DEBUG_NEW_MATH_DEBUG) \
			debugyell("O: %s (%ld %ld) -> %ld", #x, a, b, (long)x); \
		break; \
	}
#define BINARY_BOOLEAN(x) \
	{ \
		pop2b(cx, &c, &d); \
		pushn(cx, (x)); \
		if (x_debug & DEBUG_NEW_MATH_DEBUG) \
			debugyell("O: %s (%ld %ld) -> %ld", #x, c, d, (long)x); \
		break; \
	}

#define BINARY_NOZERO(x) \
	{ \
		pop2n(cx, &a, &b); \
		if (b == 0) { \
			if (x_debug & DEBUG_NEW_MATH_DEBUG) \
				debugyell("O: %s (%ld %ld) -> 0", #x, a, b); \
			error("Division by zero"); \
			pushn(cx, 0); \
		} \
		else \
		{ \
			if (x_debug & DEBUG_NEW_MATH_DEBUG) \
				debugyell("O: %s (%ld %ld) -> %ld", #x, a, b, (long)x); \
			pushn(cx, (x)); \
		} \
		break; \
	}
#define IMPLIED(x) \
	{ \
		pop2n_a(cx, &a, &b, &v); \
		if (x_debug & DEBUG_NEW_MATH_DEBUG) \
			debugyell("O: %s = %s (%ld %ld) -> %ld",  \
				get_token(cx, v), #x, a, b, x); \
		pushn(cx, setnvar(cx, v, (x))); \
		break; \
	}
#define IMPLIED_NOZERO(x) \
	{ \
		pop2n_a(cx, &a, &b, &v); \
		if (b == 0) { \
			if (x_debug & DEBUG_NEW_MATH_DEBUG) \
				debugyell("O: %s = %s (%ld %ld) -> 0",  \
					get_token(cx, v), #x, a, b); \
			error("Division by zero"); \
			pushn(cx, setnvar(cx, v, 0)); \
		} \
		if (x_debug & DEBUG_NEW_MATH_DEBUG) \
			debugyell("O: %s =  %s (%ld %ld) -> %ld",  \
				get_token(cx, v), #x, a, b, x); \
		pushn(cx, setnvar(cx, v, (x))); \
		break; \
	}
#define AUTO_UNARY(x, y) \
	{ \
		v = pop(cx); \
		b = getnval(cx, v); \
		if (x_debug & DEBUG_NEW_MATH_DEBUG) \
			debugyell("O: %s (%s %ld) -> %ld", \
				#x, get_token(cx, v), b, (x)); \
		setnvar(cx, v, (x)); \
		pushn(cx, (y)); \
		break; \
	}

#define dpushn(x1,x2,y1) \
	{ \
		if (x_debug & DEBUG_NEW_MATH_DEBUG) \
		{ \
			debugyell("O: COMPARE"); \
			debugyell("O: %s -> %d", #x2, (x2)); \
		} \
		pushn(x1,y1);  \
	} 
#define COMPARE(x, y) \
	{ \
		pop2s(cx, &s, &t); \
		if ((a = STON(s)) && (b = STON(t))) \
		{ \
			if (x_debug & DEBUG_NEW_MATH_DEBUG) \
				debugyell("O: %s (%ld %ld) -> %d", #x, a, b, (x)); \
			if ((x))		dpushn(cx, x, 1) \
			else			dpushn(cx, x, 0) \
		} \
		else \
		{ \
			if (x_debug & DEBUG_NEW_MATH_DEBUG) \
				debugyell("O: %s (%s %s) -> %d", #x, s, t, (y)); \
			if ((y))			dpushn(cx, y, 1) \
			else				dpushn(cx, y, 0) \
		} \
		new_free(&s); \
		new_free(&t); \
		break; \
	}

	switch (what) 
	{
		/* Simple unary prefix operators */
		case NOT:
			c = popb(cx);
			if (x_debug & DEBUG_NEW_MATH_DEBUG)
				debugyell("O: !%ld -> %d", c, !c);
			pushn(cx, !c);
			break;
		case COMP:
			a = popn(cx);
			if (x_debug & DEBUG_NEW_MATH_DEBUG)
				debugyell(": ~%ld -> %ld", a, ~a);
			pushn(cx, ~a);
			break;
		case UPLUS:
			a = popn(cx);
			if (x_debug & DEBUG_NEW_MATH_DEBUG)
				debugyell("O: +%ld -> %ld", a, a);
			pushn(cx, a);
			break;
		case UMINUS:
			a = popn(cx);
			if (x_debug & DEBUG_NEW_MATH_DEBUG)
				debugyell("O: -%ld -> %ld", a, -a);
			pushn(cx, -a);
			break;
		case STRLEN:
			s = pops(cx);
			a = strlen(s);
			if (x_debug & DEBUG_NEW_MATH_DEBUG)
				debugyell("O: @(%s) -> %ld", s, a);
			pushn(cx, a);
			new_free(&s);
			break;
		case WORDC:
			s = pops(cx);
			a = word_count(s);
			if (x_debug & DEBUG_NEW_MATH_DEBUG)
				debugyell("O: #(%s) -> %ld", s, a);
			pushn(cx, a);
			new_free(&s);
			break;
		case DEREF:
		{
			char	*buffer = NULL,
				*tmp;

			if (top(cx) == MAGIC_TOKEN)
				break;		/* Dont do anything */

			s = pops(cx);
			tmp = expand_alias(s, cx->args, cx->args_flag, NULL);
			alias_special_char(&buffer, tmp, cx->args, 
						NULL, cx->args_flag);
			if (buffer == NULL)
				buffer = m_strdup(empty_string);
			*cx->args_flag = 1;
			pushs(cx, buffer);

			new_free(&buffer);
			new_free(&tmp);
			break;
		}

		/* (pre|post)(in|de)crement operators. */
		case PREPLUS:   AUTO_UNARY(b + 1, b + 1)
		case PREMINUS:  AUTO_UNARY(b - 1, b - 1)
		case POSTPLUS:	AUTO_UNARY(b + 1, b)
		case POSTMINUS: AUTO_UNARY(b - 1, b)
		
		/* Simple binary operators */
		case AND:	BINARY(a & b)
		case XOR:	BINARY(a ^ b)
		case OR:	BINARY(a | b)
		case PLUS:	BINARY(a + b)
		case MINUS:	BINARY(a - b)
		case MUL:	BINARY(a * b)
		case POWER:	BINARY(pow(a, b))
		case SHLEFT:	BINARY(a << b)
		case SHRIGHT:	BINARY(a >> b)
		case DIV:	BINARY_NOZERO(a / b)
		case MOD:	BINARY_NOZERO(a % b)
		case DAND:	BINARY_BOOLEAN((long)(c && d))
		case DOR:	BINARY_BOOLEAN((long)(c || d))
		case DXOR:	BINARY_BOOLEAN((long)((c && !d) || (!c && d)))
		case STRCAT:	
			pop2s(cx, &s, &t);
			if (x_debug & DEBUG_NEW_MATH_DEBUG)
				debugyell("O: (%s) ## (%s) -> %s%s", s, t, s, t);
			malloc_strcat(&s, t);
			pushs(cx, s);
			new_free(&s);
			new_free(&t);
			break;

		/* Assignment operators */
		case PLUSEQ:	IMPLIED(a + b)
		case MINUSEQ:	IMPLIED(a - b)
		case MULEQ:	IMPLIED(a * b)
		case POWEREQ:	IMPLIED((long)pow(a, b))
		case DIVEQ:	IMPLIED_NOZERO(a / b)
		case MODEQ:	IMPLIED_NOZERO(a % b)
		case ANDEQ:	IMPLIED(a & b)
		case XOREQ:	IMPLIED(a ^ b)
		case OREQ:	IMPLIED(a | b)
		case SHLEFTEQ:	IMPLIED(a << b)
		case SHRIGHTEQ: IMPLIED(a >> b)
		case DANDEQ:	IMPLIED((long)(c && d))
		case DOREQ:	IMPLIED((long)(c || d))
		case DXOREQ:	IMPLIED((long)((c && !d) || (!c && d)))
		case STRCATEQ:
			pop2s_a(cx, &s, &t, &v);
			if (x_debug & DEBUG_NEW_MATH_DEBUG) 
				debugyell("O: %s = (%s ## %s) -> %s%s", 
					get_token(cx, v), s, t, s, t);
			malloc_strcat(&s, t);
			pusht(cx, setsvar(cx, v, s));
			new_free(&s);
			new_free(&t);
			break;
		case STRPREEQ:
			pop2s_a(cx, &s, &t, &v);
			if (x_debug & DEBUG_NEW_MATH_DEBUG) 
				debugyell("O: %s = (%s ## %s) -> %s%s", 
					get_token(cx, v), t, s, t, s);
			malloc_strcat(&t, s);
			pusht(cx, setsvar(cx, v, t));
			new_free(&s);
			new_free(&t);
			break;
		case EQ:
			pop2(cx, &v, &w);
			if (x_debug & DEBUG_NEW_MATH_DEBUG)
				debugyell("O: %s = (%s)",
					get_token(cx, v), get_token(cx, w));
			pusht(cx, setvar(cx, v, w));
			break;
		case SWAP:
		{
			char *vval, *wval;

			pop2(cx, &v, &w);
			if (x_debug & DEBUG_NEW_MATH_DEBUG)
				debugyell("O: %s <=> %s",
					get_token(cx, v), get_token(cx, w));
			vval = getsval(cx, v);	/* lhs variable */
			wval = getsval(cx, w);	/* rhs variable */
			setsvar(cx, w, vval);
			pusht(cx, setsvar(cx, v, wval));
			new_free(&vval);
			new_free(&wval);
			break;
		}
		/* Comparison operators */
		case DEQ:
			pop2s(cx, &s, &t);
			if (x_debug & DEBUG_NEW_MATH_DEBUG)
				debugyell("O: %s == %s -> %d", s, t,
					!!my_stricmp(s, t));
			if (my_stricmp(s, t) == 0)	pushn(cx, 1);
			else				pushn(cx, 0);
			new_free(&s);
			new_free(&t);
			break;
		case NEQ:
			pop2s(cx, &s, &t);
			if (x_debug & DEBUG_NEW_MATH_DEBUG)
				debugyell("O: %s != %s -> %d", s, t,
					!my_stricmp(s, t));
			if (my_stricmp(s, t) != 0)	pushn(cx, 1);
			else				pushn(cx, 0);
			new_free(&s);
			new_free(&t);
			break;
		case MATCH:
			pop2s(cx, &s, &t);
			a = !!wild_match(t, s);
			if (x_debug & DEBUG_NEW_MATH_DEBUG)
				debugyell("O: %s =~ %s -> %ld", s, t, a);
			pushn(cx, a);
			new_free(&s);
			new_free(&t);
			break;
		case NOMATCH:
			pop2s(cx, &s, &t);
			a = !wild_match(t, s);
			if (x_debug & DEBUG_NEW_MATH_DEBUG)
				debugyell("O: %s !~ %s -> %ld", s, t, a);
			pushn(cx, a);
			new_free(&s);
			new_free(&t);
			break;
	

		case LES:	COMPARE(a < b,  my_stricmp(s, t) < 0)
		case LEQ:	COMPARE(a <= b, my_stricmp(s, t) <= 0)
		case GRE:	COMPARE(a > b,  my_stricmp(s, t) > 0)
		case GEQ:	COMPARE(a >= b, my_stricmp(s, t) >= 0)


		/* Miscelaneous operators */
		case QUEST:
			pop3(cx, &a, &v, &w);
			if (x_debug & DEBUG_NEW_MATH_DEBUG)
				debugyell("O: %ld ? %s : %s -> %s", a,
					get_token(cx, v), get_token(cx, w),
					(a) ? get_token(cx, v) : 
						get_token(cx, w));
			pusht(cx, (a) ? v : w);
			break;

		case COLON:
			break;

		case COMMA:
			v = pop(cx);
			w = pop(cx);
			if (x_debug & DEBUG_NEW_MATH_DEBUG)
				debugyell("O: %s , %s -> %s", 
					get_token(cx, w),
					get_token(cx, v), 
					get_token(cx, v));
			pusht(cx, v);
			break;

		default:
			error("Unknown operator or out of operators");
			return;
	}
}


/***************************************************************************/
/* 
 * THE LEXER
 */
static	int	dummy = 1;

int	lexerr (expr_info *c, char *format, ...)
{
	char 	buffer[BIG_BUFFER_SIZE + 1];
	va_list	a;

	va_start(a, format);
	vsnprintf(buffer, BIG_BUFFER_SIZE, format, a);
	va_end(a);

	error("%s", buffer);
	c->errflag = 1;
	return EOI;
}

/*
 * 'operand' is state information that tells us about what the next token
 * is expected to be.  When a binary operator is lexed, then the next token
 * is expected to be either a unary operator or an operand.  So in this
 * case 'operand' is set to 1.  When an operand is lexed, then the next token
 * is expected to be a binary operator, so 'operand' is set to 0. 
 */
__inline int	check_implied_arg (expr_info *c)
{
	if (c->operand == 2)
	{
		pusht(c, MAGIC_TOKEN);	/* XXXX Bleh */
		c->operand = 0;
		*c->args_flag = 1;
		return 0;
	}

	return c->operand;
}

__inline TOKEN 	operator (expr_info *c, char *x, int y, TOKEN z)
{
	check_implied_arg(c);
	if (c->operand)
		return lexerr(c, "A binary operator (%s) was found "
				 "where an operand was expected", x);
	c->ptr += y;
	c->operand = 1;
	return z;
}

__inline TOKEN 	unary (expr_info *c, char *x, int y, TOKEN z)
{
	if (!c->operand)
		return lexerr(c, "An operator (%s) was found where "
				 "an operand was expected", x);
	c->ptr += y;
	c->operand = dummy;
	return z;
}


/*
 * This finds and extracts the next token in the expression
 */
static int	zzlex (expr_info *c)
{
	char	*start = c->ptr;

#define OPERATOR(x, y, z) return operator(c, x, y, z);
#define UNARY(x, y, z) return unary(c, x, y, z);

	dummy = 1;
	if (x_debug & DEBUG_NEW_MATH_DEBUG)
		debugyell("Parsing next token from: [%s]", c->ptr);

	for (;;)
	{
	    switch (*(c->ptr++)) 
	    {
		case '(':
			c->operand = 1;
			return M_INPAR;
		case ')':
			/*
			 * If we get a close paren and the lexer is expecting
			 * an operand, then obviously thats a syntax error.
			 * But we gently just insert the empty value as the
			 * rhs for the last operand and hope it all works out.
			 */
			if (check_implied_arg(c))
				pusht(c, 0);
			c->operand = 0;
			return M_OUTPAR;

		case '+':
		{
			/*
			 * Note:  In general, any operand that depends on 
			 * whether it is a unary or binary operator based
			 * upon the context is required to call the func
			 * 'check_implied_arg' to solidify the context.
			 * That is because some operators are ambiguous,
			 * And if you see   (# + 4), it can only be determined
			 * on the fly how to lex that.
			 */
			check_implied_arg(c);
			if (*c->ptr == '+' && (c->operand || !isalnum((unsigned char)*c->ptr)))
			{
				c->ptr++;
				return c->operand ? PREPLUS : POSTPLUS;
			}
			else if (*c->ptr == '=') 
				OPERATOR("+=", 1, PLUSEQ)
			else if (c->operand)
				UNARY("+", 0, UPLUS)
			else
				OPERATOR("+", 0, PLUS)
		}
		case '-':
		{
			check_implied_arg(c);
			if (*c->ptr == '-' && (c->operand || !isalnum((unsigned char)*c->ptr)))
			{
				c->ptr++;
				return (c->operand) ? PREMINUS : POSTMINUS;
			}
			else if (*c->ptr == '=') 
				OPERATOR("-=", 1, MINUSEQ)
			else if (c->operand)
				UNARY("-", 0, UMINUS)
			else
				OPERATOR("-", 0, MINUS)
		}
		case '*':
		{
			if (*c->ptr == '*') 
			{
				c->ptr++;
				if (*c->ptr == '=') 
					OPERATOR("**=", 1, POWEREQ)
				else
					OPERATOR("**", 0, POWER)
			}
			else if (*c->ptr == '=') 
				OPERATOR("*=", 1, MULEQ)
			else if (c->operand)
			{
				dummy = 2;
				UNARY("*", 0, DEREF)
			}
			else
				OPERATOR("*", 0, MUL)
		}
		case '/':
		{
			if (*c->ptr == '=') 
				OPERATOR("/=", 1, DIVEQ)
			else
				OPERATOR("/", 0, DIV)
		}
		case '%':
		{
			if (*c->ptr == '=')
				OPERATOR("%=", 1, MODEQ)
			else
				OPERATOR("%", 0, MOD)
		}

		case '!':
		{
			if (*c->ptr == '=')
				OPERATOR("!=", 1, NEQ)
			else if (*c->ptr == '~')
				OPERATOR("!~", 1, NOMATCH)
			else
				UNARY("!", 0, NOT)
		}
		case '~':
			UNARY("~", 0, COMP)

		case '&':
		{
			if (*c->ptr == '&') 
			{
				c->ptr++;
				if (*c->ptr == '=')
					OPERATOR("&&=", 1, DANDEQ)
				else
					OPERATOR("&&", 0, DAND)
			} 
			else if (*c->ptr == '=') 
				OPERATOR("&=", 1, ANDEQ)
			else
				OPERATOR("&", 0, AND)
		}
		case '|':
		{
			if (*c->ptr == '|') 
			{
				c->ptr++;
				if (*c->ptr == '=') 
					OPERATOR("||=", 1, DOREQ)
				else
					OPERATOR("||", 0, DOR)
			} 
			else if (*c->ptr == '=')
				OPERATOR("|=", 1, OREQ)
			else
				OPERATOR("|", 0, OR)
		}
		case '^':
		{
			if (*c->ptr == '^')
			{
				c->ptr++;
				if (*c->ptr == '=')
					OPERATOR("^^=", 1, DXOREQ)
				else
					OPERATOR("^^", 0, DXOR)
			}
			else if (*c->ptr == '=')
				OPERATOR("^=", 1, XOREQ)
			else
				OPERATOR("^", 0, XOR)
		}
		case '#':
		{
			check_implied_arg(c);
			if (*c->ptr == '#') 
			{
				c->ptr++;
				if (*c->ptr == '=')
					OPERATOR("##=", 1, STRCATEQ)
				else
					OPERATOR("##", 0, STRCAT)
			}
			else if (*c->ptr == '=') 
				OPERATOR("#=", 1, STRCATEQ)
			else if (*c->ptr == '~')
				OPERATOR("#~", 1, STRPREEQ)
			else if (c->operand)
			{
				dummy = 2;
				UNARY("#", 0, WORDC)
			}
			else
				OPERATOR("#", 0, STRCAT)
		}

		case '@':
			dummy = 2;
			UNARY("@", 0, STRLEN)

		case '<':
		{
			if (*c->ptr == '<') 
			{
				c->ptr++;
				if (*c->ptr == '=')
					OPERATOR("<<=", 1, SHLEFTEQ)
				else
					OPERATOR("<<", 0, SHLEFT)
			}
			else if (*c->ptr == '=')
			{
				c->ptr++;
				if (*c->ptr == '>')
					OPERATOR("<=>", 1, SWAP)
				else
					OPERATOR("<=", 0, LEQ)
			}
			else
				OPERATOR("<", 0, LES)
		}
		case '>':
		{
			if (*c->ptr == '>') 
			{
				c->ptr++;
				if (*c->ptr == '=')
					OPERATOR(">>=", 1, SHRIGHTEQ)
				else
					OPERATOR(">>", 0, SHRIGHT)
			} 
			else if (*c->ptr == '=') 
				OPERATOR(">=", 1, GEQ)
			else
				OPERATOR(">", 0, GRE)
		}

		case '=':
			if (*c->ptr == '=') 
				OPERATOR("==", 1, DEQ)
			else if (*c->ptr == '~')
				OPERATOR("=~", 1, MATCH)
			else
				OPERATOR("=", 0, EQ)

		case '?':
			c->operand = 1;
			return QUEST;
		case ':':
			/*
			 * I dont want to hear anything from you anti-goto
			 * bigots out there. ;-)  If you can't figure out
			 * what this does, you ought to give up programming.
			 * And a big old :p to everyone who insisted that
			 * i support this horrid hack.
			 */
			if (c->operand)
				goto handle_expando;

			c->operand = 1;
			return COLON;

		case ',':
			/* Same song, second verse. */
			if (c->operand)
				goto handle_expando;

			c->operand = 1;
			return COMMA;

		case '\0':
			check_implied_arg(c);
			c->operand = 1;
			c->ptr--;
			return EOI;

		case '[':
		{
			char *p = c->ptr - 1;
			char oc = 0;

			if (!c->operand)
				return lexerr(c, "Misplaced [ token");

			if ((c->ptr = MatchingBracket(p + 1, '[', ']')))
			{
				oc = *c->ptr;
				*c->ptr = 0;
			}
			else
				c->ptr = empty_string;

			c->last_token = tokenize(c, p);
			if (oc)
				*c->ptr++ = oc;
			c->operand = 0;
			return ID;
		}
		case ' ':
		case '\t':
		case '\n':
			start++;
			break;

		/*
		 * Handle literal numbers
		 */
		case '0': case '1': case '2': case '3': case '4':
		case '5': case '6': case '7': case '8': case '9':
		{
			char 	*end;
			char 	endc;

			c->operand = 0;
			c->ptr--;
			strtod(c->ptr, &end);
			endc = *end;
			*end = 0;
			c->last_token = tokenize(c, c->ptr);
			*end = endc;
			c->ptr = end;
			return ID;
		}

		/*
		 * Handle those weirdo $-values
		 */
		case '$':
			continue;

		/*
		 * Handle generic lvalue operands
		 */
		default:
handle_expando:
		{
			char 	*end;
			char	endc;

			c->operand = 0;
			c->ptr--;
			if ((end = after_expando_special(c)))
			{
				endc = *end;
				*end = 0;
				c->last_token = tokenize(c, start);
				*end = endc;
				c->ptr = end;
			}
			else
			{
				c->last_token = 0; /* Empty token */
				c->ptr = empty_string;
			}

			if (x_debug & DEBUG_NEW_MATH_DEBUG)
				debugyell("After token: [%s]", c->ptr);
			return ID;
		}
	    }
	}
}

/*
 * mathparse -- this is the state machine that actually parses the
 * expression.   The parsing is done through a shift-reduce mechanism,
 * and all the precedence levels lower than 'pc' are evaluated.
 */
static void	mathparse (expr_info *c, int pc)
{
	int	otok, 
		onoeval;

	/*
	 * Drop out of parsing if an error has occured
	 */
	if (c->errflag)
		return;

	/*
	 * Get the next token in the expression
	 */
	c->mtok = zzlex(c);

	/*
	 * For as long as the next operator indicates a shift operation...
	 */
	while (prec[c->mtok] <= pc) 
	{
		/* Drop out if an error has occured */
		if (c->errflag)
			return;

		/*
		 * Figure out what to do with this token that needs
		 * to be shifted.
		 */
		switch (c->mtok) 
		{
			case ID:
				if (x_debug & DEBUG_NEW_MATH_DEBUG)
					debugyell("Parsed identifier token [%s]", get_token(c, c->last_token));
				if (c->noeval)
					pusht(c, 0);
				else
					pusht(c, c->last_token);
				break;

			/*
			 * An open-parenthesis indicates that we should
			 * recursively evaluate the inside of the paren-set.
			 */
			case M_INPAR:
			{
				if (x_debug & DEBUG_NEW_MATH_DEBUG)
					debugyell("Parsed open paren");
				mathparse(c, TOPPREC);

				/*
				 * Of course if the expression ends without
				 * a matching rparen, then we whine about it.
				 */
				if (c->mtok != M_OUTPAR) 
				{
					if (!c->errflag)
					    error("')' expected");
					return;
				}
				break;
			}

			/*
			 * A question mark requires that we check for short
			 * circuiting.  We check the lhs, and if it is true,
			 * then we evaluate the lhs of the colon.  If it is
			 * false then we just parse the lhs of the colon and
			 * evaluate the rhs of the colon.
			 */
			case QUEST:
			{
				long u = popb(c);

				pushn(c, u);
				if (!u)
					c->noeval++;
				mathparse(c, prec[QUEST] - 1);
				if (!u)
					c->noeval--;
				else
					c->noeval++;
				mathparse(c, prec[QUEST]);
				if (u)
					c->noeval--;
				op(c, QUEST);

				continue;
			}

			/*
			 * All other operators handle normally
			 */
			default:
			{
				/* Save state */
				otok = c->mtok;
				onoeval = c->noeval;

				/*
				 * Check for short circuiting.
				 */
				if (assoc[otok] == BOOL)
				{
				    if (x_debug & DEBUG_NEW_MATH_DEBUG)
					debugyell("Parsed short circuit operator");
				    switch (otok)
				    {
					case DAND:
					case DANDEQ:
					{
						long u = popb(c);
						pushn(c, u);
						if (!u)
							c->noeval++;
						break;
					}
					case DOR:
					case DOREQ:
					{
						long u = popb(c);
						pushn(c, u);
						if (u)
							c->noeval++;
						break;
					}
				    }
				}

			 	if (x_debug & DEBUG_NEW_MATH_DEBUG)
				    debugyell("Parsed operator of type [%d]", otok);

				/*
				 * Parse the right hand side through
				 * recursion if we're doing things R->L.
				 */
				mathparse(c, prec[otok] - (assoc[otok] != RL));

				/*
				 * Then reduce this operation.
				 */
				c->noeval = onoeval;
				op(c, otok);
				continue;
			}
		}

		/*
		 * Grab the next token
		 */
		c->mtok = zzlex(c);
	}
}

/*
 * This is the new math parser.  It sets up an execution context, which
 * contains sundry information like all the extracted tokens, intermediate
 * tokens, shifted tokens, and the like.  The expression context is passed
 * around from function to function, each function is totaly independant
 * of state information stored in global variables.  Therefore, this math
 * parser is re-entrant safe.
 */
static char *	matheval (char *s, const char *args, int *args_flag)
{
	expr_info	context;
	char *		ret;

	/* Sanity check */
	if (!s || !*s)
		return m_strdup(empty_string);

	/* Create new state */
	setup_expr_info(&context);
	context.ptr = s;
	context.args = args;
	context.args_flag = args_flag;

	/* Actually do the parsing */
	mathparse(&context, TOPPREC);

	/* Check for error */
	if (context.errflag)
	{
		ret = m_strdup(empty_string);
		goto cleanup;
	}

	/* Check for leftover operands */
	if (context.sp)
		error("The expression has too many operands");

	if (x_debug & DEBUG_NEW_MATH_DEBUG)
	{
		int i;
		debugyell("Terms left: %d", context.sp);
		for (i = 0; i <= context.sp; i++)
			debugyell("Term [%d]: [%s]", i, 
				get_token(&context, context.stack[i]));
	}

	/* Get the return value */
	ret = getsval(&context, pop(&context));

cleanup:
	/* Clean up and restore order */
	destroy_expr_info(&context);

	if (internal_debug & DEBUG_EXPANSIONS && !in_debug_yell)
		debugyell("Returning [%s]", ret);

	/* Return the result */
	return ret;
}


/*
 * after_expando_special: This is a special version of after_expando that
 * can handle parsing out lvalues in expressions.  Due to the eclectic nature
 * of lvalues in expressions, this is quite a bit different than the normal
 * after_expando, requiring a different function. Ugh.
 *
 * This replaces some much more complicated logic strewn
 * here and there that attempted to figure out just how long an expando 
 * name was supposed to be.  Well, now this changes that.  This will slurp
 * up everything in 'start' that could possibly be put after a $ that could
 * result in a syntactically valid expando.  All you need to do is tell it
 * if the expando is an rvalue or an lvalue (it *does* make a difference)
 */
static  char *	after_expando_special (expr_info *c)
{
	char	*start;
	char	*rest;
	int	call;

	if (!(start = c->ptr))
		return c->ptr;

	for (;;)
	{
		rest = after_expando(start, 0, &call);
		if (*rest != '$')
			break;
		start = rest + 1;
	}

	/*
	 * All done!
	 */
	return rest;
}

