diff --git a/LINQ/LINQ/LDM/Expression/WhereClosure.vb b/LINQ/LINQ/LDM/Expression/WhereClosure.vb index 04f1350..cfbc999 100644 --- a/LINQ/LINQ/LDM/Expression/WhereClosure.vb +++ b/LINQ/LINQ/LDM/Expression/WhereClosure.vb @@ -55,8 +55,22 @@ Namespace LDM.Expression Protected Overrides Function __parsing() As CodeExpression Dim expr As Func(Of Tokens) = MyBase._source.Source.ParsingStack.Args.First ' 只能够有一个测试语句 + Dim code As New CodeExpression + + For Each c In expr.Caller + If c.InnerStack Is Nothing Then + If c.obj.TokenName = Tokens.varRef Then + + End If + Else + + End If + Next + + Return code End Function + Public Const ARGV As String = "obj" Public Const TestMethod As String = "___test" Private Function __buildFunc() As CodeMemberMethod diff --git a/LINQ/LINQ/LDM/Parser/Parser.vb b/LINQ/LINQ/LDM/Parser/Parser.vb new file mode 100644 index 0000000..02b8bac --- /dev/null +++ b/LINQ/LINQ/LDM/Parser/Parser.vb @@ -0,0 +1,259 @@ +Imports System.CodeDom +Imports System.Collections.Generic +Imports System.Collections.Specialized +Imports Microsoft.VisualBasic.Linq.Statements.TokenIcer +Imports Microsoft.VisualBasic.Scripting.TokenIcer + +Namespace LDM.Parser + + ''' + ''' Parses expressions written in strings into CodeDom expressions. There is a certain + ''' amount of context that the parser may need to be familiar with. This is why the + ''' parsing methods are not exposed as static. + ''' + Public Class Parser : Implements System.IDisposable + + Private _Enums As Dictionary(Of String, CodeTypeReference) = New Dictionary(Of String, CodeTypeReference) + Private _Fields As StringCollection = New StringCollection + + ''' + ''' A collection of identifiers that should be recognized as enums. + ''' + Public ReadOnly Property Enums() As Dictionary(Of String, CodeTypeReference) + Get + Return _Enums + End Get + End Property + + ''' + ''' A collection of names of fields. + ''' + Public ReadOnly Property Fields() As StringCollection + Get + Return _Fields + End Get + End Property + + ''' + ''' Parses an expression into a . + ''' + ''' expression to parse + ''' CodeDom representing the expression + Public Function ParseExpression(exp As Token(Of Tokens)()) As CodeExpression + Return ReadExpression(exp, Scan0, TokenPriority.None) + End Function + + ''' + ''' Recursive method that reads an expression. + ''' + ''' + ''' + ''' + Private Function ReadExpression(t As Token(Of Tokens)(), [next] As Integer, priority As TokenPriority) As CodeExpression + Dim left As CodeExpression = Nothing, right As CodeExpression = Nothing + Dim cont As Boolean = True, applyNot As Boolean = False, applyNegative As Boolean = False + While cont + Dim current As Token(Of Tokens) = t([next].MoveNext) + + Select Case current.TokenName + Case Tokens.Float OrElse Tokens.Integer OrElse Tokens.String + left = New CodePrimitiveExpression(current.TryCast) + t.GetNextToken() + cont = False + Case TokenType.[Operator] + ' An operator here is considered a unary operator. + Select Case t.Current.Text + Case "-" + applyNegative = True + Case "!" + applyNot = True + Case Else + Throw New Exception("Unexpected operator: " & t.Current.Text) + End Select + t.GetNextToken() + Continue While + Case TokenType.Identifier + left = ReadIdentifier(t) + cont = False + Case TokenType.OpenParens + t.GetNextToken() + left = ReadExpression(t, TokenPriority.None) + t.GetNextToken() + If TypeOf left Is CodeTypeReferenceExpression Then + left = New CodeCastExpression(TryCast(left, CodeTypeReferenceExpression).Type, ReadExpression(t, TokenPriority.None)) + End If + cont = False + End Select + If t.IsInvalid Then + cont = False + End If + End While + If left Is Nothing Then + Throw New Exception("No expression found.") + End If + If applyNot Then + left = New CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.ValueEquality, New CodePrimitiveExpression(False)) + ElseIf applyNegative Then + left = New CodeBinaryOperatorExpression(New CodePrimitiveExpression(0), CodeBinaryOperatorType.Subtract, left) + End If + If t.IsInvalid OrElse t.Current.Type = TokenType.CloseParens OrElse t.Current.Type = TokenType.Comma OrElse t.Current.Type = TokenType.CloseBracket Then + Return left + End If + cont = True + While cont AndAlso Not t.IsInvalid + Dim token As Token = t.Current + Select Case token.Type + Case TokenType.[Operator] + If t.Current.Priority < priority Then + cont = False + Else + ' In the case we have an operator, we'll assume it's a binary operator. + Dim binOp As CodeBinaryOperatorType + Dim notEquals As Boolean = False + Select Case token.Text + Case ">" + binOp = CodeBinaryOperatorType.GreaterThan + Case ">=" + binOp = CodeBinaryOperatorType.GreaterThanOrEqual + Case "<" + binOp = CodeBinaryOperatorType.LessThan + Case "<=" + binOp = CodeBinaryOperatorType.LessThanOrEqual + Case "=", "==" + binOp = CodeBinaryOperatorType.ValueEquality + Case "!=" + binOp = CodeBinaryOperatorType.ValueEquality + notEquals = True + Case "|" + binOp = CodeBinaryOperatorType.BitwiseOr + Case "||" + binOp = CodeBinaryOperatorType.BooleanOr + Case "&" + binOp = CodeBinaryOperatorType.BitwiseAnd + Case "&&" + binOp = CodeBinaryOperatorType.BooleanAnd + Case "-" + binOp = CodeBinaryOperatorType.Subtract + Case "+" + binOp = CodeBinaryOperatorType.Add + Case "/" + binOp = CodeBinaryOperatorType.Divide + Case "%" + binOp = CodeBinaryOperatorType.Modulus + Case "*" + binOp = CodeBinaryOperatorType.Multiply + Case Else + Throw New Exception("Unrecognized operator: " & t.Current.Text) + End Select + If t.IsInvalid Then + Throw New Exception("Expected token for right side of binary expression.") + End If + t.GetNextToken() + right = ReadExpression(t, token.Priority) + left = New CodeBinaryOperatorExpression(left, binOp, right) + + ' If the operator was the not equals operator, we just negate the previous binary expression. + If notEquals Then + left = New CodeBinaryOperatorExpression(left, binOp, New CodePrimitiveExpression(False)) + End If + End If + Case TokenType.CloseParens + 't.GetNextToken(); + cont = False + Case TokenType.Dot + ' A dot could appear after some parentheses. In this case we need to parse + ' what's after the dot as an identifier. + t.GetNextToken() + right = ReadIdentifier(t) + Dim ceTemp As CodeExpression = right + While True + If TypeOf ceTemp Is CodeVariableReferenceExpression Then + left = New CodePropertyReferenceExpression(left, TryCast(ceTemp, CodeVariableReferenceExpression).VariableName) + Exit While + ElseIf TypeOf ceTemp Is CodePropertyReferenceExpression Then + Dim cpre As CodePropertyReferenceExpression = TryCast(ceTemp, CodePropertyReferenceExpression) + If TypeOf cpre.TargetObject Is CodeThisReferenceExpression Then + cpre.TargetObject = left + left = cpre + Exit While + Else + ceTemp = cpre.TargetObject + End If + ElseIf TypeOf ceTemp Is CodeFieldReferenceExpression Then + Dim cfre As CodeFieldReferenceExpression = TryCast(ceTemp, CodeFieldReferenceExpression) + If TypeOf cfre.TargetObject Is CodeThisReferenceExpression Then + cfre.TargetObject = left + left = cfre + Exit While + End If + ElseIf TypeOf ceTemp Is CodeMethodInvokeExpression Then + Dim cmie As CodeMethodInvokeExpression = TryCast(ceTemp, CodeMethodInvokeExpression) + If TypeOf cmie.Method.TargetObject Is CodeThisReferenceExpression Then + cmie.Method.TargetObject = left + left = cmie + Exit While + Else + ceTemp = cmie.Method.TargetObject + End If + Else + Throw New Exception("Unexpected identifier found after .") + End If + End While + cont = False + Case Else + Throw New Exception("Token not expected: " & token.Text) + End Select + End While + Return left + End Function + + ''' + ''' When an identifier is encountered, it could be a number of things. A single identifer by itself + ''' is considered a variable. The pattern identifier[.identifier]+ will consider the + ''' first identifier as a variable and the others as properties. Any identifier that is followed + ''' by an open parenthesis is considered to be a function call. Indexes are not handled yet, but + ''' should be handled in the future. If the identifier is "this" then a this reference is used. + ''' + ''' + ''' + Private Function ReadIdentifier(t As Tokenizer) As CodeExpression + Dim ce As CodeExpression = Nothing + Dim token As Token = t.Current + ce = New CodeVariableReferenceExpression(token.Text) + token = t.GetNextToken() + Return ce + End Function + +#Region "IDisposable Support" + Private disposedValue As Boolean ' ĵ + + ' IDisposable + Protected Overridable Sub Dispose(disposing As Boolean) + If Not Me.disposedValue Then + If disposing Then + ' TODO: ͷй״̬(йܶ) + End If + + ' TODO: ͷŷйԴ(йܶ)д Finalize() + ' TODO: ֶΪ null + End If + Me.disposedValue = True + End Sub + + ' TODO: Dispose(ByVal disposing As Boolean)ͷŷйԴĴʱд Finalize() + 'Protected Overrides Sub Finalize() + ' ' ҪĴ˴롣 뽫 Dispose(ByVal disposing As Boolean)С + ' Dispose(False) + ' MyBase.Finalize() + 'End Sub + + ' Visual Basic Ӵ˴Ϊȷʵֿɴģʽ + Public Sub Dispose() Implements IDisposable.Dispose + ' ҪĴ˴롣 뽫 Dispose (disposing As Boolean)С + Dispose(True) + GC.SuppressFinalize(Me) + End Sub +#End Region + + End Class +End Namespace \ No newline at end of file diff --git a/LINQ/LINQ/LDM/Parser/TokenPriority.vb b/LINQ/LINQ/LDM/Parser/TokenPriority.vb new file mode 100644 index 0000000..69b0354 --- /dev/null +++ b/LINQ/LINQ/LDM/Parser/TokenPriority.vb @@ -0,0 +1,52 @@ +Namespace LDM.Parser + + ''' + ''' Indicates priority in order of operations. + ''' + Public Enum TokenPriority + ''' + ''' Default + ''' + None + + ''' + ''' Bitwise or + ''' + [Or] + + ''' + ''' Bitwise and + ''' + [And] + + ''' + ''' Bitwise not + ''' + [Not] + + ''' + ''' Equality comparisons like >, <=, ==, etc. + ''' + Equality + + ''' + ''' Plus or minus + ''' + PlusMinus + + ''' + ''' Modulus + ''' + [Mod] + + ''' + ''' Multiply or divide + ''' + MulDiv + + ''' + ''' Unary minus + ''' + UnaryMinus + End Enum +End Namespace \ No newline at end of file diff --git a/LINQ/LINQ/LINQ.vbproj b/LINQ/LINQ/LINQ.vbproj index 38803e2..1d39b45 100644 --- a/LINQ/LINQ/LINQ.vbproj +++ b/LINQ/LINQ/LINQ.vbproj @@ -126,8 +126,10 @@ + +