You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Darwinism/LINQ/TestMain/Parser/Parser.vb

307 lines
14 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#Region "Microsoft.VisualBasic::ba8ff923e20521891e809f24b3f4c1bc, LINQ\TestMain\Parser\Parser.vb"
' Author:
'
' asuka (amethyst.asuka@gcmodeller.org)
' xie (genetics@smrucc.org)
' xieguigang (xie.guigang@live.com)
'
' Copyright (c) 2018 GPL3 Licensed
'
'
' GNU GENERAL PUBLIC LICENSE (GPL3)
'
'
' This program is free software: you can redistribute it and/or modify
' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or
' (at your option) any later version.
'
' This program 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 General Public License for more details.
'
' You should have received a copy of the GNU General Public License
' along with this program. If not, see <http://www.gnu.org/licenses/>.
' /********************************************************************************/
' Summaries:
' Class Parser
'
' Properties: Enums, Fields
'
' Function: ParseExpression, ReadExpression, ReadIdentifier
'
' Sub: (+2 Overloads) Dispose
'
'
' /********************************************************************************/
#End Region
Imports System.CodeDom
Imports System.Collections.Generic
Imports System.Collections.Specialized
Namespace Parser
''' <summary>
''' 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.
''' </summary>
Public Class Parser : Implements System.IDisposable
Private _Enums As Dictionary(Of String, CodeTypeReference) = New Dictionary(Of String, CodeTypeReference)
Private _Fields As StringCollection = New StringCollection
''' <summary>
''' A collection of identifiers that should be recognized as enums.
''' </summary>
Public ReadOnly Property Enums() As Dictionary(Of String, CodeTypeReference)
Get
Return _Enums
End Get
End Property
''' <summary>
''' A collection of names of fields.
''' </summary>
Public ReadOnly Property Fields() As StringCollection
Get
Return _Fields
End Get
End Property
''' <summary>
''' Parses an expression into a <see cref="CodeExpression"/>.
''' </summary>
''' <param name="exp">expression to parse</param>
''' <returns>CodeDom representing the expression</returns>
Public Function ParseExpression(exp As String) As CodeExpression
Dim t As New Tokenizer(exp)
If Not t.IsInvalid Then
t.GetNextToken()
Return ReadExpression(t, TokenPriority.None)
End If
Return Nothing
End Function
''' <summary>
''' Recursive method that reads an expression.
''' </summary>
''' <param name="t"></param>
''' <param name="priority"></param>
''' <returns></returns>
Private Function ReadExpression(t As Tokenizer, 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
Select Case t.Current.Type
Case TokenType.Primitive
left = New CodePrimitiveExpression(t.Current.ParsedObject)
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
''' <summary>
''' 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.
''' </summary>
''' <param name="t"></param>
''' <returns></returns>
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 ' <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ĵ<EFBFBD><C4B5><EFBFBD>
' IDisposable
Protected Overridable Sub Dispose(disposing As Boolean)
If Not Me.disposedValue Then
If disposing Then
' TODO: <20>ͷ<EFBFBD><CDB7>й<EFBFBD>״̬(<28>йܶ<D0B9><DCB6><EFBFBD>)<29><>
End If
' TODO: <20>ͷŷ<CDB7><C5B7>й<EFBFBD><D0B9><EFBFBD>Դ(<28><><EFBFBD>йܶ<D0B9><DCB6><EFBFBD>)<29><><EFBFBD><EFBFBD>д<EFBFBD><D0B4><EFBFBD><EFBFBD><EFBFBD> Finalize()<29><>
' TODO: <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֶ<EFBFBD><D6B6><EFBFBD><EFBFBD><EFBFBD>Ϊ null<6C><6C>
End If
Me.disposedValue = True
End Sub
' TODO: <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Dispose(ByVal disposing As Boolean)<29><><EFBFBD><EFBFBD><EFBFBD>ͷŷ<CDB7><C5B7>й<EFBFBD><D0B9><EFBFBD>Դ<EFBFBD>Ĵ<EFBFBD><C4B4><EFBFBD>ʱ<EFBFBD><CAB1>д Finalize()<29><>
'Protected Overrides Sub Finalize()
' ' <20><>Ҫ<EFBFBD><D2AA><EFBFBD>Ĵ˴<C4B4><CBB4><20><EFBFBD><EBBDAB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Dispose(ByVal disposing As Boolean)<29>С<EFBFBD>
' Dispose(False)
' MyBase.Finalize()
'End Sub
' Visual Basic <20><>Ӵ˴<D3B4><CBB4><EFBFBD><EFBFBD><EFBFBD>Ϊ<EFBFBD><CEAA><EFBFBD><EFBFBD>ȷʵ<C8B7>ֿɴ<D6BF><C9B4><EFBFBD>ģʽ<C4A3><CABD>
Public Sub Dispose() Implements IDisposable.Dispose
' <20><>Ҫ<EFBFBD><D2AA><EFBFBD>Ĵ˴<C4B4><CBB4><20><EFBFBD><EBBDAB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Dispose (disposing As Boolean)<29>С<EFBFBD>
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
#End Region
End Class
End Namespace