Welcome to
aleprojects.com

Using parser - operators

Operator is a symbol specifying operation that applied to one or two operands in an expression. Operator written between its operands is an infix binary operator; the one written before operand is a prefix unary operator, and the one written after is a postfix unary operator. Operator has the precedence and associativity. The precedence defines order of operator application in relation to other operators in expression which is not bracketed. The associativity defines the order of application of two operators with the same precedence in unbracketed expression.

The AleOperation class describes operators. Use this constructor to create operator definition:

public AleOperation(string name, int precedence, uint associativity, AleOperationType type = = AleOperationType.Evaluation)

where name is an operator symbol. Operator symbol can't start with digit and may have any character except brackets and quotation marks of any kind. Operator symbol may consist of multiple parts separated by whitespaces. In expressions symbol may have any whitespaces in any count between parts, but in description only one space character ('\u0020') must separate parts of operator symbol.

The precedence parameters specifies precedence of operator. Higher value means higher precedence.

The associativity parameter specifies associativity of operator and must be one of the following constants or their combination:

public const uint OPERATOR_FX = 0x00000001;
public const uint OPERATOR_FY = 0x00000002;
public const uint OPERATOR_YFX = 0x00000004;
public const uint OPERATOR_XFY = 0x000000010;
public const uint OPERATOR_YF = 0x000000020;
public const uint OPERATOR_XF = 0x000000040;

Left associative operators are denoted by constants OPERATOR_YFX, OPERATOR_YF and OPERATOR_FX. The OPERATOR_YFX constant denotes infix binary operator. The OPERATOR_YF constant denotes postfix unary operator. The OPERATOR_XF constant denotes prefix unary operator.

Right associative operators are denoted by constants OPERATOR_XFY, OPERATOR_XF and OPERATOR_FY. The OPERATOR_XFY constant denotes infix binary operator. The OPERATOR_XF constant denotes postfix unary operator. The OPERATOR_FY constant denotes prefix unary operator.

Following combination of constants are allowed: one or less constant denoting prefix operator + one or less constant denoting binary operator + one or less constant denoting postfix operator. Zero value of associativity is an error.

The type parameter is a member of AleOperationType enumeration and specifies that operator is of special kind like assignment, increment or member access (class) operator.

public enum AleOperationType
{
	Evaluation, Assignment, CompoundAssignment, Increment, MemberAccess, ElementAccess, 
	Index, InitList, ObjectConst, KeyValue, PropertyValue
}

This information is used for semantic checking of expression. The type can be one of the following values: Evaluation, Assignment, CompoundAssignment, Increment, MemberAccess. Other values are not allowed in operator constructor.

The AleOperation class has property Parameters. For operators this property must be null.

Evaluation of operators

The AleOperation.Evaluator property is a delegate that evaluates operator.

public delegate bool EvaluateOperation(AleTerm term, ref OperationEvalParameters parameters, ref AleTermResult result);

The term parameter is an expression tree node corresponding to the operator being evaluated. The AleTerm class has indexer returning child nodes that are also instances of the AleTerm class. For operators there is one or two child nodes, which are operands. Delegate is responsible for evaluation of the operator operands.

protected bool SubstractOperation(AleTerm term, ref OperationEvalParameters parameters, ref AleTermResult result)
{
	AleTerm t1 = term[0];
	
	AleTermResult a;
	if (!t1.Evaluate(out a, parameters.userEvaluate, parameters.userAssign) && 
		result.SetError(a.ErrorCode, a.ErrorPos)) return false;
	
	AleTermResult b;
	if (term.Count == 2) // binary operator
	{
		AleTerm t2 = term[1];
		if (!t2.Evaluate(out b, parameters.userEvaluate, parameters.userAssign) && 
			result.SetError(b.ErrorCode, b.ErrorPos)) return false;
	}
	else
	{
		b = a;
		a.Value = 0;
	}
	
	TypeCode optype = AleTerm.OperationType(a.Value, b.Value);
	
	switch (optype)
	{
		case TypeCode.Double:
			result.Value = Convert.ToDouble(a.Value) - Convert.ToDouble(b.Value);
			return true;
		case TypeCode.Decimal:
			result.Value = Convert.ToDecimal(a.Value) - Convert.ToDecimal(b.Value);
			return true;
		case TypeCode.UInt64:
			result.Value = Convert.ToUInt64(a.Value) - Convert.ToUInt64(b.Value);
			return true;
		case TypeCode.Int64:
			result.Value = Convert.ToInt64(a.Value) - Convert.ToInt64(b.Value);
			return true;
		case TypeCode.UInt32:
			result.Value = Convert.ToUInt32(a.Value) - Convert.ToUInt32(b.Value);
			return true;
		case TypeCode.Int32:
			result.Value = Convert.ToInt32(a.Value) - Convert.ToInt32(b.Value);
			return true;
		default:
			result.SetError(AleTermResult.ERROR_INCOMPATIBLETYPES, term.Token.StartInOrigin);
			return false;
	}
}

This is an example of delegate that performs substraction operation (minus '-' operator). Minus operator can be unary or binary. When it is unary, Count property of the term parameter is equal to 1 (only one operand). When it is binary, Count property is 2. Operands are evaluated by calling their Evaluate methods. Delegates for evaluating user variables and user operations and for assigning values for variables are passed in userEvaluate and userAssign properties of the parameters parameter. Method SetError of structure AleTermResult sets error status and always returns true.

Evaluated values of operands might be of different types. Static method OperationType of class AleTerm has two parameters of type object with values of operands and returns type code to which both operands can be safely typecasted.

As was mentioned before, the result of tokenization is a linear list of tokens that are instances of the AleToken class. In fact, the operator in this list is represented by the AleOperationToken class that is derived from AleToken. The AleOperationToken class has property Associativity that unambiguously defines associativity and position of operator in its context. Operator token can be accessed by the Token property of the term parameter.

protected bool MyOperatorEvaluator(AleTerm term, ref OperationEvalParameters parameters, ref AleTermResult result)
{
	AleTerm t1 = term[0];
	
	if (((term.Token as AleOperatorToken).Associativity & (AleOperation.OPERATOR_FX + AleOperation.OPERATOR_FY)) != 0)
	{
		// prefix operator
		
	} else if (((term.Token as AleOperatorToken).Associativity & (AleOperation.OPERATOR_XF + AleOperation.OPERATOR_YF)) != 0)
	{
		// postfix operator
		
	} else 
	{
		// binary operator
		
		AleTerm t2 = term[1];
	}
	
	...
}

To add operator to the parser set of operations, use the AleExpressionParser.AddOperation method.

public bool AddOperation(AleOperation operation)

If operator with such symbol already exists, the method replaces it with a new one.

To remove operator from the parser set of operations, use the AleExpressionParser.RemoveOperation method.

public void RemoveOperation(string name, int paramCount, bool isClassMethod = false)

The name parameters is an operator symbol. The paramCount parameter must be -1. The isClassMethod parameter is ignored.