' 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 Ciloci.Flee.CalcEngine Imports System.IO ''' Public NotInheritable Class ExpressionContext #Region "Fields" Private MyProperties As PropertyDictionary Private MySyncRoot As New Object ''' Keep variables as a field to make access fast Private MyVariables As VariableCollection #End Region #Region "Constructor" ''' Public Sub New() Me.New(DefaultExpressionOwner.Instance) End Sub ''' Public Sub New(ByVal expressionOwner As Object) Utility.AssertNotNull(expressionOwner, "expressionOwner") MyProperties = New PropertyDictionary() MyProperties.SetValue("CalculationEngine", Nothing) MyProperties.SetValue("CalcEngineExpressionName", Nothing) MyProperties.SetValue("IdentifierParser", Nothing) MyProperties.SetValue("ExpressionOwner", expressionOwner) MyProperties.SetValue("ParserOptions", New ExpressionParserOptions(Me)) MyProperties.SetValue("Options", New ExpressionOptions(Me)) MyProperties.SetValue("Imports", New ExpressionImports()) Me.Imports.SetContext(Me) MyVariables = New VariableCollection(Me) MyProperties.SetToDefault(Of Boolean)("NoClone") Me.RecreateParser() End Sub #End Region #Region "Methods - Private" Private Sub AssertTypeIsAccessibleInternal(ByVal t As Type) Dim isPublic As Boolean = t.IsPublic If t.IsNested = True Then isPublic = t.IsNestedPublic End If Dim isSameModuleAsOwner As Boolean = t.Module Is Me.ExpressionOwner.GetType().Module ' Public types are always accessible. Otherwise they have to be in the same module as the owner Dim isAccessible As Boolean = isPublic Or isSameModuleAsOwner If isAccessible = False Then Dim msg As String = Utility.GetGeneralErrorMessage(GeneralErrorResourceKeys.TypeNotAccessibleToExpression, t.Name) Throw New ArgumentException(msg) End If End Sub Private Sub AssertNestedTypeIsAccessible(ByVal t As Type) While Not t Is Nothing AssertTypeIsAccessibleInternal(t) t = t.DeclaringType End While End Sub #End Region #Region "Methods - Internal" Friend Function CloneInternal(ByVal cloneVariables As Boolean) As ExpressionContext Dim context As ExpressionContext = Me.MemberwiseClone() context.MyProperties = MyProperties.Clone() context.MyProperties.SetValue("Options", Me.Options.Clone()) context.MyProperties.SetValue("ParserOptions", Me.ParserOptions.Clone()) context.MyProperties.SetValue("Imports", Me.Imports.Clone()) context.Imports.SetContext(context) If cloneVariables = True Then context.MyVariables = New VariableCollection(Me) Me.Variables.Copy(context.MyVariables) End If Return context End Function Friend Sub AssertTypeIsAccessible(ByVal t As Type) If t.IsNested = True Then AssertNestedTypeIsAccessible(t) Else AssertTypeIsAccessibleInternal(t) End If End Sub ' Does the actual parsing of an expression. Thead-safe. Friend Function Parse(ByVal expression As String, ByVal services As IServiceProvider) As ExpressionElement SyncLock MySyncRoot Dim sr As New System.IO.StringReader(expression) Dim parser As ExpressionParser = Me.Parser parser.Reset() parser.Tokenizer.Reset(sr) Dim analyzer As FleeExpressionAnalyzer = parser.Analyzer analyzer.SetServices(services) Dim rootNode As PerCederberg.Grammatica.Runtime.Node = DoParse() analyzer.Reset() Dim topElement As ExpressionElement = rootNode.Values.Item(0) Return topElement End SyncLock End Function Friend Sub RecreateParser() SyncLock MySyncRoot Dim analyzer As New FleeExpressionAnalyzer Dim parser As ExpressionParser = New ExpressionParser(System.IO.StringReader.Null, analyzer, Me) MyProperties.SetValue("ExpressionParser", parser) End SyncLock End Sub Friend Function DoParse() As PerCederberg.Grammatica.Runtime.Node Try Return Me.Parser.Parse() Catch ex As PerCederberg.Grammatica.Runtime.ParserLogException ' Syntax error; wrap it in our exception and rethrow Throw New ExpressionCompileException(ex) End Try End Function Friend Sub SetCalcEngine(ByVal engine As CalculationEngine, ByVal calcEngineExpressionName As String) MyProperties.SetValue("CalculationEngine", engine) MyProperties.SetValue("CalcEngineExpressionName", calcEngineExpressionName) End Sub Friend Function ParseIdentifiers(ByVal expression As String) As IdentifierAnalyzer Dim parser As ExpressionParser = Me.IdentifierParser Dim sr As New StringReader(expression) parser.Reset() parser.Tokenizer.Reset(sr) Dim analyzer As IdentifierAnalyzer = parser.Analyzer analyzer.Reset() parser.Parse() Return parser.Analyzer End Function #End Region #Region "Methods - Public" ''' Public Function Clone() As ExpressionContext Return Me.CloneInternal(True) End Function ''' Public Function CompileDynamic(ByVal expression As String) As IDynamicExpression Return New Expression(Of Object)(expression, Me, False) End Function ''' Public Function CompileGeneric(Of TResultType)(ByVal expression As String) As IGenericExpression(Of TResultType) Return New Expression(Of TResultType)(expression, Me, True) End Function #End Region #Region "Properties - Private" Private ReadOnly Property IdentifierParser() As ExpressionParser Get Dim parser As ExpressionParser = MyProperties.GetValue(Of ExpressionParser)("IdentifierParser") If parser Is Nothing Then Dim analyzer As New IdentifierAnalyzer() parser = New ExpressionParser(System.IO.StringReader.Null, analyzer, Me) MyProperties.SetValue("IdentifierParser", parser) End If Return parser End Get End Property #End Region #Region "Properties - Internal" Friend Property NoClone() As Boolean Get Return MyProperties.GetValue(Of Boolean)("NoClone") End Get Set(ByVal value As Boolean) MyProperties.SetValue("NoClone", value) End Set End Property Friend ReadOnly Property ExpressionOwner() As Object Get Return MyProperties.GetValue(Of Object)("ExpressionOwner") End Get End Property Friend ReadOnly Property CalcEngineExpressionName() As String Get Return MyProperties.GetValue(Of String)("CalcEngineExpressionName") End Get End Property Friend ReadOnly Property Parser() As ExpressionParser Get Return MyProperties.GetValue(Of ExpressionParser)("ExpressionParser") End Get End Property #End Region #Region "Properties - Public" ''' Public ReadOnly Property Options() As ExpressionOptions Get Return MyProperties.GetValue(Of ExpressionOptions)("Options") End Get End Property ''' Public ReadOnly Property [Imports]() As ExpressionImports Get Return MyProperties.GetValue(Of ExpressionImports)("Imports") End Get End Property ''' Public ReadOnly Property Variables() As VariableCollection Get Return MyVariables End Get End Property ''' Public ReadOnly Property CalculationEngine() As CalculationEngine Get Return MyProperties.GetValue(Of CalculationEngine)("CalculationEngine") End Get End Property ''' Public ReadOnly Property ParserOptions() As ExpressionParserOptions Get Return MyProperties.GetValue(Of ExpressionParserOptions)("ParserOptions") End Get End Property #End Region End Class