Click here to Skip to main content
15,892,059 members
Articles / Programming Languages / Visual Basic

Flee - Fast Lightweight Expression Evaluator

Rate me:
Please Sign up or sign in to vote.
4.91/5 (47 votes)
11 Oct 2007LGPL310 min read 196.1K   3.7K   108  
A .NET expression evaluator that compiles to IL and is designed for speed.
' This library is free software; you can redistribute it and/or
' modify it under the terms of the GNU Lesser General Public License
' as published by the Free Software Foundation; either version 2.1
' of the License, or (at your option) any later version.
' 
' This library 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
' Lesser General Public License for more details.
' 
' You should have received a copy of the GNU Lesser General Public
' License along with this library; if not, write to the Free
' Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
' MA 02111-1307, USA.
' 
' Flee - Fast Lightweight Expression Evaluator
' Copyright � 2007 Eugene Ciloci
'

Imports System.Reflection.Emit
Imports System.Reflection
Imports System.ComponentModel.Design

' Element that compares values and generates a boolean result
Friend Class CompareElement
	Inherits BinaryExpressionElement

	Private MyOperation As LogicalCompareOperation

	Public Sub New()

	End Sub

	Protected Overrides Sub GetOperation(ByVal operation As Object)
		MyOperation = DirectCast(operation, LogicalCompareOperation)
	End Sub

	Protected Overrides Function GetResultType(ByVal leftType As System.Type, ByVal rightType As System.Type) As System.Type
		Dim binaryResultType As Type = ImplicitConverter.GetBinaryResultType(leftType, rightType)
		Dim overloadedOperator As MethodInfo = Me.GetOverloadedCompareOperator()

		' Use our string equality instead of overloaded operator
		If leftType Is GetType(String) And rightType Is GetType(String) And Me.IsOpTypeEqualOrNotEqual(MyOperation) = True Then
			' String equality
			Return GetType(Boolean)
		ElseIf Not overloadedOperator Is Nothing Then
			Return overloadedOperator.ReturnType
		ElseIf Not binaryResultType Is Nothing Then
			' Comparison of numeric operands
			Return GetType(Boolean)
		ElseIf leftType Is GetType(Boolean) And rightType Is GetType(Boolean) And Me.IsOpTypeEqualOrNotEqual(MyOperation) = True Then
			' Boolean equality
			Return GetType(Boolean)
		Else
			' Invalid operands
			Return Nothing
		End If
	End Function

	Private Function GetOverloadedCompareOperator() As MethodInfo
		Dim name As String = Me.GetCompareOperatorName(MyOperation)
		Return MyBase.GetOverloadedBinaryOperator(name, MyOperation)
	End Function

	Private Function GetCompareOperatorName(ByVal op As LogicalCompareOperation) As String
		Select Case op
			Case LogicalCompareOperation.Equal
				Return "Equality"
			Case LogicalCompareOperation.NotEqual
				Return "Inequality"
			Case LogicalCompareOperation.GreaterThan
				Return "GreaterThan"
			Case LogicalCompareOperation.LessThan
				Return "LessThan"
			Case LogicalCompareOperation.GreaterThanOrEqual
				Return "GreaterThanOrEqual"
			Case LogicalCompareOperation.LessThanOrEqual
				Return "LessThanOrEqual"
			Case Else
				Debug.Assert(False, "unknown compare type")
				Return Nothing
		End Select
	End Function

	Public Overrides Sub Emit(ByVal ilg As System.Reflection.Emit.ILGenerator, ByVal services As IServiceContainer)
		Dim binaryResultType As Type = ImplicitConverter.GetBinaryResultType(MyLeftChild.ResultType, MyRightChild.ResultType)
		Dim overloadedOperator As MethodInfo = Me.GetOverloadedCompareOperator()

		If Me.AreBothChildrenOfType(GetType(String)) Then
			' String equality
			MyLeftChild.Emit(ilg, services)
			MyRightChild.Emit(ilg, services)
			Me.EmitStringEquality(ilg, MyOperation, services)
		ElseIf Not overloadedOperator Is Nothing Then
			MyBase.EmitOverloadedOperatorCall(overloadedOperator, ilg, services)
		ElseIf Not binaryResultType Is Nothing Then
			' Emit a compare of numeric operands
			Me.EmitChildWithConvert(MyLeftChild, binaryResultType, ilg, services)
			Me.EmitChildWithConvert(MyRightChild, binaryResultType, ilg, services)
			Me.EmitCompareOperation(ilg, MyOperation)
		ElseIf Me.AreBothChildrenOfType(GetType(Boolean)) Then
			' Boolean equality
			MyLeftChild.Emit(ilg, services)
			MyRightChild.Emit(ilg, services)
			Me.EmitCompareOperation(ilg, MyOperation)
		Else
			Throw New InvalidOperationException("unknown operand types")
		End If
	End Sub

	Private Sub EmitStringEquality(ByVal ilg As ILGenerator, ByVal op As LogicalCompareOperation, ByVal services As IServiceContainer)
		' Get the StringComparison from the options
		Dim options As ExpressionOptions = services.GetService(GetType(ExpressionOptions))
		Dim ic As New Int32Constant(options.StringComparison)

		ic.Emit(ilg, services)

		' and emit the method call
		Dim mi As System.Reflection.MethodInfo = GetType(String).GetMethod("Equals", Reflection.BindingFlags.Public Or Reflection.BindingFlags.Static, Nothing, New Type() {GetType(String), GetType(String), GetType(StringComparison)}, Nothing)
		ilg.EmitCall(OpCodes.Call, mi, Nothing)

		If op = LogicalCompareOperation.NotEqual Then
			ilg.Emit(OpCodes.Ldc_I4_0)
			ilg.Emit(OpCodes.Ceq)
		End If
	End Sub

	Private Function IsOpTypeEqualOrNotEqual(ByVal op As LogicalCompareOperation) As Boolean
		Return op = LogicalCompareOperation.Equal Or op = LogicalCompareOperation.NotEqual
	End Function

	' Emit the actual compare
	Private Sub EmitCompareOperation(ByVal ilg As ILGenerator, ByVal op As LogicalCompareOperation)
		Dim ltOpcode As OpCode = Me.GetCompareGTLTOpcode(False)
		Dim gtOpcode As OpCode = Me.GetCompareGTLTOpcode(True)

		Select Case op
			Case LogicalCompareOperation.Equal
				ilg.Emit(OpCodes.Ceq)
			Case LogicalCompareOperation.LessThan
				ilg.Emit(ltOpcode)
			Case LogicalCompareOperation.GreaterThan
				ilg.Emit(gtOpcode)
			Case LogicalCompareOperation.NotEqual
				ilg.Emit(OpCodes.Ceq)
				ilg.Emit(OpCodes.Ldc_I4_0)
				ilg.Emit(OpCodes.Ceq)
			Case LogicalCompareOperation.LessThanOrEqual
				ilg.Emit(gtOpcode)
				ilg.Emit(OpCodes.Ldc_I4_0)
				ilg.Emit(OpCodes.Ceq)
			Case LogicalCompareOperation.GreaterThanOrEqual
				ilg.Emit(ltOpcode)
				ilg.Emit(OpCodes.Ldc_I4_0)
				ilg.Emit(OpCodes.Ceq)
			Case Else
				Throw New ArgumentException("Unknown op type")
		End Select
	End Sub

	' Get the correct greater/less than opcode
	Private Function GetCompareGTLTOpcode(ByVal greaterThan As Boolean) As OpCode
		Dim leftType As Type = MyLeftChild.ResultType

		If leftType Is MyRightChild.ResultType Then
			If leftType Is GetType(UInt32) Or leftType Is GetType(UInt64) Then
				If greaterThan = True Then
					Return OpCodes.Cgt_Un
				Else
					Return OpCodes.Clt_Un
				End If
			Else
				Return Me.GetCompareOpcode(greaterThan)
			End If
		Else
			Return Me.GetCompareOpcode(greaterThan)
		End If
	End Function

	Private Function GetCompareOpcode(ByVal greaterThan As Boolean) As OpCode
		If greaterThan = True Then
			Return OpCodes.Cgt
		Else
			Return OpCodes.Clt
		End If
	End Function

	Protected Overrides ReadOnly Property Name() As String
		Get
			Return "Compare Operator"
		End Get
	End Property
End Class

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)


Written By
Web Developer
Canada Canada
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions