/*
 * Bawk C actions builtin functions, variable declaration, and
 * stack management routines.
 */
#include <stdio.h>
#include "bawk.h"

#define MAXARGS		10	/* max # of arguments to a builtin func */
#define F_PRINTF	1
#define F_GETLINE	2
#define F_STRLEN	3
#define F_STRCPY	4
#define F_STRCMP	5
#define F_TOUPPER	6
#define F_TOLOWER	7
#define F_MATCH		8
#define F_NEXTFILE	9

isfunction( s )
char *s;
{
	/*
	 * Compare the string "s" to a list of builtin functions
	 * and return its (non-zero) token number.
	 * Return zero if "s" is not a function.
	 */
	if ( !strcmp( s, "printf" ) )
		return F_PRINTF;
	if ( !strcmp( s, "getline" ) )
		return F_GETLINE;
	if ( !strcmp( s, "strlen" ) )
		return F_STRLEN;
	if ( !strcmp( s, "strcpy" ) )
		return F_STRCPY;
	if ( !strcmp( s, "strcmp" ) )
		return F_STRCMP;
	if ( !strcmp( s, "toupper" ) )
		return F_TOUPPER;
	if ( !strcmp( s, "tolower" ) )
		return F_TOLOWER;
	if ( !strcmp( s, "match" ) )
		return F_MATCH;
	if ( !strcmp( s, "nextfile" ) )
		return F_NEXTFILE;
	return 0;
}

iskeyword( s )
char *s;
{
	/*
	 * Compare the string "s" to a list of keywords and return its
	 * (non-zero) token number.  Return zero if "s" is not a keyword.
	 */
	if ( !strcmp( s, "char" ) )
		return T_CHAR;
	if ( !strcmp( s, "int" ) )
		return T_INT;
	if ( !strcmp( s, "if" ) )
		return T_IF;
	if ( !strcmp( s, "else" ) )
		return T_ELSE;
	if ( !strcmp( s, "while" ) )
		return T_WHILE;
	if ( !strcmp( s, "break" ) )
		return T_BREAK;

	if ( !strcmp( s, "NF" ) )
		return T_NF;
	if ( !strcmp( s, "NR" ) )
		return T_NR;
	if ( !strcmp( s, "FS" ) )
		return T_FS;
	if ( !strcmp( s, "RS" ) )
		return T_RS;
	if ( !strcmp( s, "FILENAME" ) )
		return T_FILENAME;
	if ( !strcmp( s, "BEGIN" ) )
		return T_BEGIN;
	if ( !strcmp( s, "END" ) )
		return T_END;
	return 0;
}

function( funcnum )
{
	int argc, args[ MAXARGS ];
	char lpar;

	argc = 0;
	if ( Token==T_LPAREN )
	{
		lpar = 1;
		getoken();
	}
	else
		lpar = 0;
	/*
	 * If there are any arguments, evaluate them and copy their values
	 * to a local array.
	 */
	if ( Token!=T_RPAREN && Token!=T_EOF )
	{
		for ( ;; )
		{
			expression();
			if ( argc<MAXARGS )
				args[ argc++ ] = popint();
			else
				popint();
			if ( Token==T_COMMA )
				getoken();
			else
				break;
		}
	}
	if ( lpar && Token!=T_RPAREN )
		error( "missing ')'", ACT_ERROR );
	else if ( Token==T_RPAREN )
		getoken();

	switch ( funcnum )
	{
	case F_PRINTF:	/* just like the real printf() function */
		pushint( printf( args[0], args[1], args[2], args[3], args[4],
			 args[5], args[6], args[7], args[8], args[9] ) );
		break;
	case F_GETLINE:
		/*
		 * Get the next line of input from the current input file
		 * and parse according to the current field seperator.
		 * Don't forget to free up the previous line's words first...
		 */
		while ( Fieldcount )
			free( Fields[ --Fieldcount ] );
		pushint( getline() );
		Fieldcount = parse( Linebuf, Fields, Fieldsep );
		break;
	case F_STRLEN:	/* calculate length of string argument */
		pushint( strlen( args[0] ) );
		break;
	case F_STRCPY:	/* copy second string argument to first string */
		pushint( strcpy( args[0], args[1] ) );
		break;
	case F_STRCMP:	/* compare two strings */
		pushint( strcmp( args[0], args[1] ) );
		break;
	case F_TOUPPER:	/* convert the character argument to upper case */
		pushint( toupper( args[0] ) );
		break;
	case F_TOLOWER:	/* convert the character argument to lower case */
		pushint( tolower( args[0] ) );
		break;
	case F_MATCH:	/* match a string argument to a regular expression */
		pushint( match( args[0], args[1] ) );
		break;
	case F_NEXTFILE:/* close current input file and process next file */
		pushint( endfile() );
		break;
	default:	/* oops! */
		error( "bad function call", ACT_ERROR );
	}
}

VARIABLE *
findvar( s )
char *s;
{
	/*
	 * Search the symbol table for a variable whose name is "s".
	 */
	VARIABLE *pvar;
	int i;
	char name[ MAXVARLEN ];

	i = 0;
	while ( i < MAXVARLEN && alphanum( *s ) )
		name[i++] = *s++;
	if ( i<MAXVARLEN )
		name[i] = 0;

	for ( pvar = Vartab; pvar<Nextvar; ++pvar )
	{
		if ( !strncmp( pvar->vname, name, MAXVARLEN ) )
			return pvar;
	}
	return NULL;
}

VARIABLE *
addvar( name )
char *name;
{
	/*
	 * Add a new variable to symbol table and assign it default
	 * attributes (int name;)
	 */
	int i;

	if ( Nextvar <= Vartab + MAXVARTABSZ )
	{
		i = 0;
		while ( i<MAXVARLEN && alphanum( *name ) )
			Nextvar->vname[i++] = *name++;
		if ( i<MAXVARLEN )
			Nextvar->vname[i] = 0;

		Nextvar->vclass = 0;
		Nextvar->vsize = WORD;
		Nextvar->vlen = 0;
		/*
		 * Allocate some new room
		 */
		Nextvar->vptr = getmem( WORD );
		fillmem( Nextvar->vptr, WORD, 0 );
	}
	else
		error( "symbol table overflow", MEM_ERROR );

	return Nextvar++;
}

declist()
{
	/*
	 * Parse a "char" or "int" statement.
	 */
	char type;

	type = Token;
	getoken();
	decl( type );
	while ( Token==T_COMMA )
	{
		getoken();
		decl( type );
	}
	if ( Token==T_SEMICOLON )
		getoken();
}

VARIABLE *
decl( type )
{
	/*
	 * Parse an element of a "char" or "int" declaration list.
	 * The function stmt_compile() has already entered the variable
	 * into the symbol table as an integer, this routine simply changes
	 * the symbol's class, size or length according to the declaraction.
	 * WARNING: The interpreter depends on the fact that pointers are
	 * the same length as int's.  If your machine uses long's for
	 * pointers either change the code or #define int long (or whatever).
	 */
	char class, size;
	int len;
	unsigned oldsize, newsize;
	VARIABLE *pvar;

	if ( Token==T_MUL )
	{
		/*
		 * it's a pointer
		 */
		getoken();
		pvar = decl( type );
		++pvar->vclass;
	}
	else if ( Token==T_VARIABLE )
	{
		/*
		 * Simple variable so far.  The token value (in the global
		 * "Value" variable) is a pointer to the variable's symbol
		 * table entry.
		 */
		pvar = Value.dptr;
		getoken();
		class = 0;
		/*
		 * Compute its length
		 */
		if ( Token==T_LBRACKET )
		{
			/*
			 * It's an array.
			 */
			getoken();
			++class;
			/*
			 * Compute the dimension
			 */
			expression();
			if ( Token!=T_RBRACKET )
				error( "missing ']'", ACT_ERROR );
			getoken();
			len = popint();
		}
		else
			/*
			 * It's a simple variable - array length is zero.
			 */
			len = 0;

		size = (type==T_CHAR) ? BYTE : WORD;

		newsize = (len ? len : 1) * size;
		oldsize = (pvar->vlen ? pvar->vlen : 1) * pvar->vsize;
		if ( newsize != oldsize )
		{
			/*
			 * The amount of storage needed for the variable
			 * has changed - free up memory allocated initially
			 * and reallocate for new size.
			 */
			free( pvar->vptr );
			pvar->vptr = getmem( newsize );
		}
		/*
		 * Now change the variable's attributes.
		 */
		pvar->vclass = class;
		pvar->vsize = size;
		pvar->vlen = len;
	}
	else
		syntaxerror();

	return pvar;
}

assignment()
{
	/*
	 * Perform an assignment
	 */
	int ival;

	ival = popint();
	/*
	 * make sure we've got an lvalue
	 */
	if ( Stackptr->lvalue )
	{
		if ( Stackptr->class )
			movemem( &ival, Stackptr->value.dptr, WORD );
		else
			movemem(&ival, Stackptr->value.dptr, Stackptr->size);
		pop();
		pushint( ival );
	}
	else
		error( "'=' needs an lvalue", ACT_ERROR );
}

pop()
{
	/*
	 * Pop the stack and return the integer value
	 */
	if ( Stackptr >= Stackbtm )
		return (Stackptr--)->value.ival;
	return error( "stack underflow", ACT_ERROR );
}

push( pclass, plvalue, psize, pdatum )
char pclass, plvalue, psize;
DATUM *pdatum;
{
	/*
	 * Push item parts onto the stack
	 */
	if ( ++Stackptr <= Stacktop )
	{
		Stackptr->lvalue = plvalue;
		Stackptr->size = psize;
		if ( !(Stackptr->class = pclass)  &&  !plvalue )
			Stackptr->value.ival = pdatum->ival;
		else
			Stackptr->value.dptr = pdatum->dptr;
	}
	else
		error( "stack overflow", MEM_ERROR );
}

pushint( intvalue )
int intvalue;
{
	/*
	 * push an integer onto the stack
	 */
	if ( ++Stackptr <= Stacktop )
	{
		Stackptr->lvalue =
		Stackptr->class = 0;
		Stackptr->size = WORD;
		Stackptr->value.ival = intvalue;
	}
	else
		error( "stack overflow", MEM_ERROR );
}

popint()
{
	/*
	 * Resolve the item on the top of the stack and return it
	 */
	int intvalue;

	if ( Stackptr->lvalue )
	{
		/*
		 * if it's a byte indirect, sign extend it
		 */
		if ( Stackptr->size == BYTE && !Stackptr->class )
			intvalue = *Stackptr->value.dptr;
		else
		{
			/*
			 * otherwise, it's an unsigned int
			 */
			intvalue = *Stackptr->value.ptrptr;
		}
		pop();
		return intvalue;
	}
	else
	{
		/*
		 * else it's an ACTUAL, just pop it
		 */
		return pop();
	}
}

