diff options
Diffstat (limited to 'saga/expr.cpp')
| -rw-r--r-- | saga/expr.cpp | 464 | 
1 files changed, 464 insertions, 0 deletions
diff --git a/saga/expr.cpp b/saga/expr.cpp new file mode 100644 index 0000000000..32aca92b7f --- /dev/null +++ b/saga/expr.cpp @@ -0,0 +1,464 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2004 The ScummVM project + * + * The ReInherit Engine is (C)2000-2003 by Daniel Balsom. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. + * + * $Header$ + * + */ +/* + Description:    +  +    Expression parsing module, and string handling functions + + Notes:  + +    EXPR_ParseArgs() lifted wholesale from SDL win32 initialization code by +    Sam Lantinga +*/ + +#include "reinherit.h" + +/* +   Uses the following modules: +\*--------------------------------------------------------------------------*/ +#include "cvar_mod.h" + +/* +   Begin module +\*--------------------------------------------------------------------------*/ +#include "expr.h" + +namespace Saga { + +char *EXPR_ErrMsg[] = { + +	"Invalid error state.", +	"No Error", +	"Memory allocation failed", +	"Illegal variable name", +	"Expected \'=\' or \'(\' in expression", +	"Expected \'(\' in function call", +	"Illegal \'(\', identifier is not function", +	"Expected a value to assign", +	"Unterminated string literal", +	"Unmatched parenthesis in function call", +	"Error reading value string", +	"Expected a number or boolean", +	"Unknown variable or function" +}; + +enum EXPR_Errors { + +	EXERR_ASSERT, +	EXERR_NONE, +	EXERR_MEM, +	EXERR_ILLEGAL, +	EXERR_EXPR, +	EXERR_FUNC, +	EXERR_NOTFUNC, +	EXERR_RVALUE, +	EXERR_LITERAL, +	EXERR_PAREN, +	EXERR_STRING, +	EXERR_NUMBER, +	EXERR_NOTFOUND +}; + +static enum EXPR_Errors EXPR_ErrorState; + +int EXPR_GetError(char **err_str) +/*--------------------------------------------------------------------------*\ + Returns the appropriate expression parser error string given an error code. +\*--------------------------------------------------------------------------*/ +{ + +	*err_str = EXPR_ErrMsg[EXPR_ErrorState]; + +	return EXPR_ErrorState; +} + +int +EXPR_Parse(const char **exp_pp, int *len, R_CVAR_P * expr_cvar, char **rvalue) +/*--------------------------------------------------------------------------*\ + Parses an interactive expression. + Sets 'expr_cvar' to the cvar/cfunction identifier input by the user, and + 'rvalue' to the corresponding rvalue ( in an expression ) or argument string + ( in a function call ). + + Memory pointed to by rvalue after return must be explicitly freed by the + caller. +\*--------------------------------------------------------------------------*/ +{ + +	int i; +	int in_char; +	int equ_offset; +	int rvalue_offset; + +	char *lvalue_str; +	int lvalue_len; + +	char *rvalue_str; +	int rvalue_len; + +	const char *scan_p; +	int scan_len; + +	const char *expr_p; +	int expr_len; +	int test_char = '\0'; +	int have_func = 0; + +	R_CVAR_P lvalue_cvar; + +	expr_p = *exp_pp; +	expr_len = strlen(*exp_pp); + +	scan_p = *exp_pp; +	scan_len = expr_len; + +    /**lvalue = NULL;*/ +	*rvalue = NULL; + +	EXPR_ErrorState = EXERR_ASSERT; + +	for (i = 0; i <= scan_len; i++, scan_p++) { + +		in_char = *scan_p; + +		if ((i == 0) && isdigit(in_char)) { +			/* First character of a valid identifier cannot be a digit */ +			EXPR_ErrorState = EXERR_ILLEGAL; +			return R_FAILURE; +		} + +		/* If we reach a character that isn't valid in an identifier... */ +		if ((!isalnum(in_char)) && ((in_char != '_'))) { + +			/* then eat remaining whitespace, if any */ +			equ_offset = strspn(scan_p, R_EXPR_WHITESPACE); + +			test_char = scan_p[equ_offset]; +			/* and test for the only valid characters after an identifier */ +			if ((test_char != '=') && +			    (test_char != '\0') && (test_char != '(')) { + +				if ((equ_offset == 0) +				    && ((scan_p - expr_p) != expr_len)) { +					EXPR_ErrorState = EXERR_ILLEGAL; +				} else { +					EXPR_ErrorState = EXERR_EXPR; +				} +				return R_FAILURE; +			} + +			break; +		} +	} + +	lvalue_len = (scan_p - expr_p); +	lvalue_str = (char *)malloc(lvalue_len + 1); + +	if (lvalue_str == NULL) { +		EXPR_ErrorState = EXERR_MEM; +		return R_FAILURE; +	} + +	strncpy(lvalue_str, expr_p, lvalue_len); +	lvalue_str[lvalue_len] = 0; + +	/* We now have the lvalue, so attempt to find it */ +	lvalue_cvar = CVAR_Find(lvalue_str); +	if (lvalue_cvar == NULL) { +		EXPR_ErrorState = EXERR_NOTFOUND; +		return R_FAILURE; +	} +	if (lvalue_str) { +		free(lvalue_str); +		lvalue_str = NULL; +	} + +	/* Skip parsed character, if any */ +	scan_p += equ_offset + 1; +	scan_len = (scan_p - expr_p); + +	/* Check if the 'cvar' is really a function */ +	have_func = CVAR_IsFunc(lvalue_cvar); + +	if (test_char == '(') { + +		if (have_func) { + +			rvalue_str = +			    EXPR_ReadString(&scan_p, &rvalue_len, ')'); +			if (rvalue_str != NULL) { +				/* Successfully read string */ +				/*CON_Print( "Read function parameters \"%s\".", rvalue_str ); */ +				*expr_cvar = lvalue_cvar; +				*rvalue = rvalue_str; + +				scan_len = (scan_p - expr_p); + +				*exp_pp = scan_p; +				*len -= scan_len; + +				EXPR_ErrorState = EXERR_NONE; +				return R_SUCCESS; +			} else { +				EXPR_ErrorState = EXERR_PAREN; +				return R_FAILURE; +			} +		} else { +			EXPR_ErrorState = EXERR_NOTFUNC; +			return R_FAILURE; +		} + +	} + +	/* Eat more whitespace */ +	rvalue_offset = strspn(scan_p, R_EXPR_WHITESPACE); + +	if (rvalue_offset + i == expr_len) { +		/* Only found single lvalue */ +		*expr_cvar = lvalue_cvar; +		*exp_pp = scan_p; +		*len -= scan_len; +		return R_SUCCESS; +	} + +	scan_p += rvalue_offset; +	scan_len = (scan_p - expr_p) + 1; + +	in_char = *scan_p; + +	in_char = toupper(in_char); + +	switch (in_char) { + +	case '\"': +		scan_p++; +		scan_len--; +		rvalue_str = EXPR_ReadString(&scan_p, &rvalue_len, '\"'); + +		if (rvalue_str != NULL) { +			/* Successfully read string */ +			break; +		} else { +			EXPR_ErrorState = EXERR_LITERAL; +			return R_FAILURE; +		} +		break; + +#if 0 +	case 'Y':		/* Y[es] */ +	case 'T':		/* T[rue] */ + +		break; + +	case 'N':		/* N[o] */ +	case 'F':		/* F[alse] */ + +		break; +#endif + +	default: + +		if (isdigit(in_char) || (in_char == '-') || (in_char == '+')) { + +			rvalue_str = EXPR_ReadString(&scan_p, &rvalue_len, 0); + +			if (rvalue_str != NULL) { +				/* Successfully read string */ +				break; +			} else { +				EXPR_ErrorState = EXERR_STRING; +				return R_FAILURE; +			} +		} else { +			EXPR_ErrorState = EXERR_NUMBER; +			return R_FAILURE; +		} + +		break; + +	} + +	*expr_cvar = lvalue_cvar; +	*rvalue = rvalue_str; + +	scan_len = (scan_p - expr_p); + +	*exp_pp = scan_p; +	*len -= scan_len; + +	EXPR_ErrorState = EXERR_NONE; +	return R_SUCCESS; + +} + +char *EXPR_ReadString(const char **string_p, int *len, int term_char) +/****************************************************************************\ + Reads in a string of characters from '*string_p' until 'term_char' is  + encountered. If 'term_char' == 0, the function reads characters until + whitespace is encountered.  + Upon reading a string, the function modifies *string_p and len based on  + the number of characters read. +\****************************************************************************/ +{ + +	int string_len; +	char *str_p; +	char *term_p; + +	const char *scan_p; +	int in_char; + +	if (term_char > 0) { + +		term_p = strchr(*string_p, term_char); + +		if (term_p == NULL) { +			return NULL; +		} + +		string_len = (int)(term_p - *string_p); + +		str_p = (char *)malloc(string_len + 1); + +		if (str_p == NULL) { +			return NULL; +		} + +		strncpy(str_p, *string_p, string_len); +		str_p[string_len] = 0; + +		*string_p += (string_len + 1);	/* Add 1 for terminating char */ +		*len -= (string_len + 1); + +	} else { + +		scan_p = *string_p; +		string_len = 0; + +		while (scan_p) { + +			in_char = *scan_p++; + +			if (!isspace(in_char)) { +				string_len++; +			} else if (string_len) { + +			  str_p = (char *)malloc(string_len + 1); + +				if (str_p == NULL) { +					return NULL; +				} + +				strncpy(str_p, *string_p, string_len); +				str_p[string_len] = 0; + +				*string_p += string_len; +				*len -= string_len; +				break; + +			} else { +				return NULL; +			} +		} + +	} + +	return str_p; +} + +int EXPR_GetArgs(char *cmd_str, char ***expr_argv) +/****************************************************************************\ + Parses the string 'cmd_str' into argc/argv format, returning argc. + The resulting argv pointers point into the 'cmd_str' string, so any argv + entries should not be used after cmd_str is deallocated. + + Memory pointed to by expr_argv must be explicitly freed by the caller. +\****************************************************************************/ +{ + +	int expr_argc; + +	expr_argc = EXPR_ParseArgs(cmd_str, NULL); +	*expr_argv = (char **)malloc((expr_argc + 1) * sizeof(**expr_argv)); + +	if (expr_argv == NULL) { +		return R_FAILURE; +	} + +	EXPR_ParseArgs(cmd_str, *expr_argv); + +	return expr_argc; + +} + +int EXPR_ParseArgs(char *cmd_str, char **argv) +{ + +	char *bufp; +	int argc; + +	argc = 0; +	for (bufp = cmd_str; *bufp;) { +		/* Skip leading whitespace */ +		while (isspace(*bufp)) { +			++bufp; +		} +		/* Skip over argument */ +		if (*bufp == '"') { +			++bufp; +			if (*bufp) { +				if (argv) { +					argv[argc] = bufp; +				} +				++argc; +			} +			/* Skip over word */ +			while (*bufp && (*bufp != '"')) { +				++bufp; +			} +		} else { +			if (*bufp) { +				if (argv) { +					argv[argc] = bufp; +				} +				++argc; +			} +			/* Skip over word */ +			while (*bufp && !isspace(*bufp)) { +				++bufp; +			} +		} +		if (*bufp) { +			if (argv) { +				*bufp = '\0'; +			} +			++bufp; +		} +	} +	if (argv) { +		argv[argc] = NULL; +	} +	return (argc); +} + +} // End of namespace Saga  | 
