JBoolean
JIsNamedConstant
	(
	const JCharacter*	origExpr,
	const JSize			origLength,
	JFunction**			theFunction
	)
{
	*theFunction = NULL;

	// remove enclosing parentheses

	const JCharacter* expr = origExpr;
	const JSize length     = JStripParentheses(&expr, origLength);

	// check if expr is a named constant

	const JSize nameCount           = JPGetNamedConstCount();
	const JNamedConstInfo* nameInfo = JPGetNamedConstInfo();

	for (JSize i=1; i<=nameCount; i++)
		{
		if (JStringsEqual(expr, length, nameInfo[i-1].name))
			{
			*theFunction = new JNamedConstant(nameInfo[i-1].index);
			assert( *theFunction != NULL );
			return kJTrue;
			}
		}
	return kJFalse;
}
JBoolean
JIsBoolConstant
	(
	const JCharacter*	origExpr,
	const JSize			origLength,
	JDecision**			theDecision
	)
{
	*theDecision = NULL;

	// remove enclosing parentheses

	const JCharacter* expr = origExpr;
	const JSize length     = JStripParentheses(&expr, origLength);

	// check if expr is a known constant

	const JSize boolCount          = JPGetBoolConstCount();
	const JBoolConstInfo* boolInfo = JPGetBoolConstInfo();

	for (JSize i=1; i<=boolCount; i++)
		{
		if (JStringsEqual(expr, length, boolInfo[i-1].name))
			{
			*theDecision = new JBooleanConstant(boolInfo[i-1].value);
			assert( *theDecision != NULL );
			return kJTrue;
			}
		}

	return kJFalse;
}
JBoolean
JVariableList::ParseVariableName
	(
	const JCharacter*	expr,
	const JSize			exprLength,
	JIndex*				index
	)
	const
{
	const JSize count = GetElementCount();
	for (JIndex i=1; i<=count; i++)
		{
		const JString& name = GetVariableName(i);
		if (JStringsEqual(expr, exprLength, name))
			{
			*index = i;
			return kJTrue;
			}
		}

	return kJFalse;
}
JBoolean
JVariableList::ParseDiscreteValue
	(
	const JCharacter*	expr,
	const JSize			exprLength,
	const JIndex&		variableIndex,
	JIndex*				valueIndex
	)
	const
{
	const JSize count = GetDiscreteValueCount(variableIndex);

	for (JIndex i=1; i<=count; i++)
		{
		const JString& name = GetDiscreteValueName(variableIndex, i);
		if (JStringsEqual(expr, exprLength, name))
			{
			*valueIndex = i;
			return kJTrue;
			}
		}

	return kJFalse;
}
JBoolean
JRecurseFunction
	(
	const JCharacter*		origExpr,
	const JSize				origLength,
	const JVariableList*	theVariableList,
	JFunction**				theFunction,
	const JBoolean			allowUIF
	)
{
	*theFunction = NULL;

	// remove enclosing parentheses

	const JCharacter* expr = origExpr;
	const JSize length     = JStripParentheses(&expr, origLength);

	if (length == 0)
		{
		(JGetUserNotification())->ReportError("You specified an empty function");
		return kJFalse;
		}

	// check for summation (+,-)

	JParseResult result = JParseAsSummation(expr, length, theVariableList,
											theFunction, allowUIF);
	if (result == kJParsedOK)
		{
		return kJTrue;
		}
	else if (result == kJParseError)
		{
		return kJFalse;
		}

	// check for product before division
	// so we correctly parse x/y*z as (x/y)*z

	result = JParseAsProduct(expr, length, theVariableList, theFunction, allowUIF);
	if (result == kJParsedOK)
		{
		return kJTrue;
		}
	else if (result == kJParseError)
		{
		return kJFalse;
		}

	// check for division

	result = JParseAsDivision(expr, length, theVariableList, theFunction, allowUIF);
	if (result == kJParsedOK)
		{
		return kJTrue;
		}
	else if (result == kJParseError)
		{
		return kJFalse;
		}

	// check for exponentiation

	result = JParseAsExponentiation(expr, length, theVariableList, theFunction, allowUIF);
	if (result == kJParsedOK)
		{
		return kJTrue;
		}
	else if (result == kJParseError)
		{
		return kJFalse;
		}

	// check for standard function

	result = JParseAsFunctionWithArgs(expr, length, theVariableList, theFunction, allowUIF);
	if (result == kJParsedOK)
		{
		return kJTrue;
		}
	else if (result == kJParseError)
		{
		return kJFalse;
		}

	// check for function of discrete variable

	result = JParseAsFunctionOfDiscrete(expr, length, theVariableList, theFunction, allowUIF);
	if (result == kJParsedOK)
		{
		return kJTrue;
		}
	else if (result == kJParseError)
		{
		return kJFalse;
		}

	// check for named constant

	if (JIsNamedConstant(expr, length, theFunction))
		{
		return kJTrue;
		}

	// check for variable name

	result = JParseAsNumericVariable(expr, length, theVariableList, theFunction, allowUIF);
	if (result == kJParsedOK)
		{
		return kJTrue;
		}
	else if (result == kJParseError)
		{
		return kJFalse;
		}

	// check for empty JUserInputFunction

	if (allowUIF && JStringsEqual(expr, length, JUserInputFunction::GetEmptyString()))
		{
		*theFunction = new JUserInputFunction(theVariableList,
											  JGetCurrFontManager(),
											  JGetCurrColormap());
		assert( *theFunction != NULL );
		return kJTrue;
		}

	// check for constant, must occupy entire string

	JString valueStr(expr, length);
	JBoolean isPercentage = kJFalse;
	if (valueStr.GetLastCharacter() == '%')
		{
		isPercentage = kJTrue;
		valueStr.RemoveSubstring(valueStr.GetLength(), valueStr.GetLength());
		valueStr.TrimWhitespace();
		}

	JFloat value;
	if (valueStr.ConvertToFloat(&value))
		{
		if (isPercentage)
			{
			value /= 100.0;
			}

		*theFunction = new JConstantValue(value);
		assert( *theFunction != NULL );
		return kJTrue;
		}
	else if (jerrno() == ERANGE)
		{
		JString errorStr(expr, length);
		errorStr.Prepend("\"");
		errorStr += "\" is too large to be represented.";
		(JGetUserNotification())->ReportError(errorStr);
		return kJFalse;
		}

	// this string is not a function

	JString errorStr(expr, length);
	errorStr.Prepend("\"");
	errorStr += "\" is not a valid function.";
	(JGetUserNotification())->ReportError(errorStr);
	return kJFalse;
}
JParseResult
JParseAsDiscreteComparison
	(
	const JCharacter*		origExpr,
	const JSize				origLength,
	const JVariableList*	theVariableList,
	JDecision**				theDecision
	)
{
	*theDecision = NULL;

	// remove enclosing parentheses

	const JCharacter* expr = origExpr;
	const JSize length     = JStripParentheses(&expr, origLength);

	// search for equal sign

	JSize offset;
	if (!JStringContains(expr, length, JPGetEqualityString(), &offset))
		{
		return kJNotMyProblem;
		}

	// check front part for a single variable name
	// if it doesn't work, we assume it's a function

	JIndex varIndex;
	JFunction* arrayIndex = NULL;

	const JParseResult result = JParseVariable(expr, offset, theVariableList,
											   &varIndex, &arrayIndex);
	if (result != kJParsedOK)
		{
		return result;
		}

	// check last part for a legal variable value

	const JSize valueOffset      = offset + JPGetEqualityStringLength();
	const JCharacter* valueStart = expr + valueOffset;
	const JSize valueLength      = JStripParentheses(&valueStart, length - valueOffset);

	JIndex value;
	if (theVariableList->IsNumeric(varIndex))
		{
		// check for comparison with appropriate equivalent of "unknown"

		const JString& unknownName = theVariableList->GetUnknownValueSymbol(varIndex);
		if (JStringsEqual(valueStart, valueLength, unknownName))
			{
			// return JValueUnknown object

			*theDecision = new JValueUnknown(theVariableList, varIndex, arrayIndex);
			assert( *theDecision != NULL );
			return kJParsedOK;
			}
		else
			{
			delete arrayIndex;
			return kJNotMyProblem;
			}
		}
	else if (theVariableList->ParseDiscreteValue(valueStart, valueLength, varIndex, &value))
		{
		// return JDiscreteEquality object

		*theDecision = new JDiscreteEquality(theVariableList, varIndex, arrayIndex, value);
		assert( *theDecision != NULL );
		return kJParsedOK;
		}

	// check for another discrete variable

	JIndex varIndex2;
	JFunction* arrayIndex2 = NULL;

	const JParseResult result2 = JParseVariable(valueStart, valueLength, theVariableList,
											    &varIndex2, &arrayIndex2);
	if (result2 == kJParsedOK && theVariableList->IsDiscrete(varIndex2) &&
		theVariableList->HaveSameValues(varIndex, varIndex2))
		{
		// return JDiscreteVarEquality object

		*theDecision = new JDiscreteVarEquality(theVariableList, varIndex, arrayIndex,
												varIndex2, arrayIndex2);
		assert( *theDecision != NULL );
		return kJParsedOK;
		}

	delete arrayIndex;
	delete arrayIndex2;

	if (result2 == kJParsedOK)
		{
		// it parsed, but the variables can't be compared

		JString errorStr = theVariableList->GetVariableName(varIndex);
		errorStr += " cannot be compared with ";
		errorStr += JString(valueStart, valueLength);
		(JGetUserNotification())->ReportError(errorStr);
		return kJParseError;
		}
	else if (result2 == kJParseError)
		{
		return kJParseError;
		}
	else
		{
		// we couldn't make sense of the second half, so it's an error

		JString errorStr(valueStart, valueLength);
		errorStr.Prepend("\"");
		errorStr += "\" is not a possible value of ";
		errorStr += theVariableList->GetVariableName(varIndex);
		(JGetUserNotification())->ReportError(errorStr);
		return kJParseError;
		}
}