microsoft-visualbasic-runtime/Scripting/TextGrepScriptEngine.vb

354 lines
13 KiB
VB.net
Raw Normal View History

2019-07-29 02:05:05 +08:00
#Region "Microsoft.VisualBasic::1f3e77cee1487311faf5ba5ef90dcc0f, Microsoft.VisualBasic.Core\Scripting\TextGrepScriptEngine.vb"
2018-11-08 20:21:25 +08:00
2020-01-04 14:44:02 +08:00
' 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:
' Delegate Function
'
'
' Delegate Function
'
'
' Class TextGrepScriptEngine
'
' Properties: DoNothing, IsDoNothing, MethodPointers, PipelinePointer
'
' Constructor: (+1 Overloads) Sub New
' Function: Compile, CompileInternal, EnsureNotEmpty, Explains, Grep
' Match, MidString, NoOperation, Replace, Reverse
' Tokens, ToString
'
'
'
'
'
' /********************************************************************************/
2018-08-02 20:14:48 +08:00
#End Region
2020-01-04 14:44:02 +08:00
Imports System.ComponentModel
2018-08-02 20:14:48 +08:00
Imports System.Runtime.CompilerServices
Imports System.Text
Imports System.Text.RegularExpressions
Imports Microsoft.VisualBasic.CommandLine
Imports Microsoft.VisualBasic.CommandLine.Reflection
Imports Microsoft.VisualBasic.Language
2018-10-20 20:29:57 +08:00
Imports Microsoft.VisualBasic.Language.Default
2018-11-03 02:47:48 +08:00
Imports r = System.Text.RegularExpressions.Regex
2020-01-04 14:44:02 +08:00
Imports Token = System.Collections.Generic.KeyValuePair(Of String(), Microsoft.VisualBasic.Scripting.TextGrepMethodToken)
2018-08-02 20:14:48 +08:00
Namespace Scripting
''' <summary>
'''
''' </summary>
''' <param name="source">文本源</param>
''' <param name="args">脚本命令的参数</param>
''' <returns></returns>
''' <remarks></remarks>
Public Delegate Function TextGrepMethodToken(source$, args$()) As String
2019-03-06 20:07:16 +08:00
''' <summary>
''' 从目标源文本字符串之中进行字符串解析的操作
''' </summary>
''' <param name="source"></param>
''' <returns></returns>
2018-08-02 20:14:48 +08:00
Public Delegate Function TextGrepMethod(source As String) As String
''' <summary>
''' A script object for grep the gene id in the blast output query and subject title.
''' (用于解析基因名称的脚本类,这个对象是在项目的初始阶段,为了方便命令行操作而设置的)
''' </summary>
''' <remarks></remarks>
Public NotInheritable Class TextGrepScriptEngine
Public Shared ReadOnly Property MethodPointers As New SortedDictionary(Of String, TextGrepMethodToken) From {
_
{"tokens", AddressOf TextGrepScriptEngine.Tokens},
{"match", AddressOf TextGrepScriptEngine.Match},
{"-", AddressOf TextGrepScriptEngine.NoOperation},
{"replace", AddressOf TextGrepScriptEngine.Replace},
{"mid", AddressOf TextGrepScriptEngine.MidString},
{"reverse", AddressOf TextGrepScriptEngine.Reverse}
}
Public Shared Function EnsureNotEmpty(ptr As TextGrepMethod) As TextGrepMethod
Return Function(str) As String
Dim gs$ = ptr(str)
If gs.StringEmpty Then
Return str
Else
Return gs
End If
End Function
End Function
''' <summary>
''' Source,Script,ReturnValue
''' </summary>
''' <remarks></remarks>
Dim _operations As Token()
2018-11-03 02:47:48 +08:00
Dim _script$()
2018-08-02 20:14:48 +08:00
2018-11-04 20:46:32 +08:00
''' <summary>
''' 当前的这个脚本是否不进行任何操作
''' </summary>
''' <returns></returns>
Public ReadOnly Property IsDoNothing As Boolean
Get
Dim emptyScript = _script.IsNullOrEmpty
Dim emptyOperation = _script.Length = 1 AndAlso (_script.First = "-" OrElse _script.First.StringEmpty)
Return emptyScript OrElse emptyOperation
End Get
End Property
2018-08-02 20:14:48 +08:00
''' <summary>
''' 对用户所输入的脚本进行编译,对于内部的空格,请使用单引号``'``进行分割
''' </summary>
2018-10-20 20:29:57 +08:00
''' <param name="scriptText">
2020-03-14 14:18:38 +08:00
''' The script line should be this format:
''' ```
''' script_tokens1;script_tokens2;....
''' ```
''' if there is any space in the script line, then the space should wrapped
''' by the ``'`` character.
'''
''' (如果这个参数传递的是一个空字符串,那么这个函数将会直接返回<see cref="DoNothing"/>脚本)
2018-10-20 20:29:57 +08:00
''' </param>
2018-08-02 20:14:48 +08:00
''' <returns></returns>
''' <remarks></remarks>
2020-01-04 14:44:02 +08:00
<ExportAPI("compile")>
<Usage("script_tokens1;script_tokens2;....")>
2018-08-02 20:14:48 +08:00
Public Shared Function Compile(scriptText As String) As TextGrepScriptEngine
2018-10-20 20:29:57 +08:00
If scriptText.StringEmpty Then
Return DoNothing
2018-11-05 19:51:52 +08:00
ElseIf scriptText = "-" Then
Return DoNothing
2018-10-20 20:29:57 +08:00
Else
Return CompileInternal(scriptText)
End If
End Function
Private Shared Function CompileInternal(scriptText As String) As TextGrepScriptEngine
2018-10-03 13:28:23 +08:00
Dim script$() = TryParse(scriptText, TokenDelimited:=";", InnerDelimited:="'"c)
2018-08-02 20:14:48 +08:00
Dim builder = LinqAPI.Exec(Of Token) <=
_
From sToken As String
2018-10-03 13:28:23 +08:00
In script
2018-08-02 20:14:48 +08:00
Let tokens As String() = TryParse(sToken, TokenDelimited:=" ", InnerDelimited:="'"c)
2018-10-03 13:28:23 +08:00
Let entryPoint As String = sToken.Split.First.ToLower
Where MethodPointers.ContainsKey(entryPoint)
Select New Token(tokens, _MethodPointers(entryPoint))
2018-08-02 20:14:48 +08:00
2018-10-03 13:28:23 +08:00
If script.Length > builder.Length Then
2018-08-02 20:14:48 +08:00
' 有非法的命令短语,则为了保护数据的一致性,这个
' 含有错误的语法的脚本是不能够用于操作的,
' 则函数返回空指针
Return Nothing
Else
Return New TextGrepScriptEngine With {
2018-11-03 02:47:48 +08:00
._script = script,
2018-08-02 20:14:48 +08:00
._operations = builder
}
End If
End Function
2018-10-20 20:29:57 +08:00
''' <summary>
''' Source in and then source out, no changes
''' </summary>
''' <returns></returns>
2019-05-22 19:02:42 +08:00
Public Shared ReadOnly Property DoNothing As [Default](Of TextGrepScriptEngine)
2018-10-20 20:29:57 +08:00
Get
Static opNothing As New TextGrepScriptEngine With {
._operations = {},
2018-11-03 02:47:48 +08:00
._script = {"-"}
2018-10-20 20:29:57 +08:00
}
Return opNothing
End Get
End Property
2018-08-02 20:14:48 +08:00
''' <summary>
''' <see cref="Grep"/>
''' 字符串剪裁操作的函数指针
''' </summary>
''' <returns></returns>
Public ReadOnly Property PipelinePointer As TextGrepMethod
<MethodImpl(MethodImplOptions.AggressiveInlining)>
Get
2018-10-20 20:29:57 +08:00
Return AddressOf Grep
2018-08-02 20:14:48 +08:00
End Get
End Property
2018-11-03 02:47:48 +08:00
Public Iterator Function Explains() As IEnumerable(Of String)
For Each op As Token In _operations
Dim args$() = op.Key.Skip(1).Join("*".Replicate(5)).ToArray
Dim description As DescriptionAttribute = op.Value _
.Method _
.GetCustomAttributes(GetType(DescriptionAttribute), True) _
.First
Dim explain$ = String.Format(description.Description, args)
Yield explain
Next
End Function
2018-08-02 20:14:48 +08:00
''' <summary>
''' 修整目标字符串,按照脚本之中的方法取出所需要的字符串信息
''' </summary>
''' <param name="source"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Function Grep(source As String) As String
2018-10-20 20:29:57 +08:00
Dim doGrep As Func(Of String, Token, Integer) =
2018-08-02 20:14:48 +08:00
Function(sourceText As String, method As Token) As Integer
source = method.Value()(sourceText, method.Key) '迭代解析
Return Len(source)
End Function
' 这里是迭代计算,所以请不要使用并行拓展
For Each operation As Token In _operations
2018-10-20 20:29:57 +08:00
Call doGrep(source, operation)
2018-08-02 20:14:48 +08:00
Next
Return source
End Function
<MethodImpl(MethodImplOptions.AggressiveInlining)>
Public Overrides Function ToString() As String
2018-11-03 02:47:48 +08:00
Return _script.JoinBy(" -> ")
2018-08-02 20:14:48 +08:00
End Function
Protected Friend Sub New()
End Sub
2018-11-03 02:47:48 +08:00
#Region "API supports"
2020-01-04 14:44:02 +08:00
''' <summary>
''' DO_NOTHING
''' </summary>
''' <param name="source"></param>
''' <param name="script"></param>
''' <returns></returns>
2018-08-02 20:14:48 +08:00
<MethodImpl(MethodImplOptions.AggressiveInlining)>
2020-01-04 14:44:02 +08:00
<ExportAPI("-")>
2018-11-03 02:47:48 +08:00
<Description("Do nothing with the source input")>
2018-08-02 20:14:48 +08:00
Private Shared Function NoOperation(source As String, script As String()) As String
Return source
End Function
<MethodImpl(MethodImplOptions.AggressiveInlining)>
<ExportAPI("Reverse")>
2018-11-03 02:47:48 +08:00
<Description("Reverse the input source text")>
2018-08-02 20:14:48 +08:00
Private Shared Function Reverse(source As String, Script As String()) As String
Return source.Reverse.ToArray
End Function
''' <summary>
'''
''' </summary>
''' <param name="Source"></param>
''' <param name="Script"></param>
''' <returns></returns>
''' <remarks></remarks>
2020-01-04 14:44:02 +08:00
<ExportAPI("Tokens")>
<Usage("tokens p_str pointer")>
2018-10-03 13:28:23 +08:00
<Argument("pointer", False, Description:="pointer must be a zero base integer number which is smaller than
the tokens array's length; pointer can also be assign of a specific string ""last"" to get the last
element and ""first"" to get the first element in the tokens array.")>
2018-11-03 02:47:48 +08:00
<Description("Split source text with delimiter [{0}], and get the token at position [{1}]")>
2018-08-02 20:14:48 +08:00
Private Shared Function Tokens(source As String, script As String()) As String
2018-11-03 02:47:48 +08:00
Dim delimiter As String = script(1)
Dim Tstr As String() = Strings.Split(source, delimiter)
2018-08-02 20:14:48 +08:00
If String.Equals(script(2), "last", StringComparison.OrdinalIgnoreCase) Then
Return If(Tstr.IsNullOrEmpty, "", Tstr.Last)
Else
2018-10-03 13:28:23 +08:00
' first指示符被计算为0所以在这里不需要为first进行额外的处理
Dim p As Integer = CInt(Val(script(2)))
2018-08-02 20:14:48 +08:00
If Tstr.Length - 1 < p OrElse p < 0 Then
Return ""
Else
Return If(Tstr.IsNullOrEmpty, "", Tstr(p))
End If
End If
End Function
2020-01-04 14:44:02 +08:00
<ExportAPI("match")>
<Usage("match pattern")>
2018-11-03 02:47:48 +08:00
<Description("Get the text token which match pattern [{0}]")>
2018-08-02 20:14:48 +08:00
Private Shared Function Match(source As String, script As String()) As String
Dim pattern As String = script.Last
2018-11-03 02:47:48 +08:00
Return r.Match(source, pattern, RegexOptions.IgnoreCase).Value
2018-08-02 20:14:48 +08:00
End Function
''' <summary>
'''
''' </summary>
''' <param name="source"></param>
2018-11-03 02:47:48 +08:00
''' <param name="script">
2018-10-03 13:28:23 +08:00
''' 向量之中的第一个元素为命令的名字第二个元素为Mid函数的Start参数第三个元素为Mid函数的Length参数可以被忽略掉
''' </param>
2018-08-02 20:14:48 +08:00
''' <returns></returns>
''' <remarks></remarks>
2020-01-04 14:44:02 +08:00
<ExportAPI("mid")>
2018-11-03 02:47:48 +08:00
<Description("Substring a region [{0}, {1}] from the input text source.")>
Private Shared Function MidString(source As String, script As String()) As String
Dim start As Integer = CInt(Val(script(1)))
2018-08-02 20:14:48 +08:00
2018-11-03 02:47:48 +08:00
If script.Length > 2 Then
Dim length As Integer = CInt(Val(script(2)))
Return Mid(source, start, length)
2018-08-02 20:14:48 +08:00
Else
2018-11-03 02:47:48 +08:00
Return Mid(source, start)
2018-08-02 20:14:48 +08:00
End If
End Function
2020-01-04 14:44:02 +08:00
<ExportAPI("replace")>
<Usage("replace <regx_text> <replace_value>")>
2018-11-03 02:47:48 +08:00
<Description("replace source with [{1}] where match pattern [{0}]")>
2018-08-02 20:14:48 +08:00
Private Shared Function Replace(source As String, script As String()) As String
2018-11-03 02:47:48 +08:00
Dim regexp As New Regex(script(1))
Dim matchs = regexp.Matches(source)
2018-08-02 20:14:48 +08:00
Dim sBuilder As New StringBuilder(source)
2018-11-03 02:47:48 +08:00
Dim newValue = script(2)
2018-08-02 20:14:48 +08:00
2018-11-03 02:47:48 +08:00
For Each m As Match In matchs
Call sBuilder.Replace(m.Value, newValue)
2018-08-02 20:14:48 +08:00
Next
Return sBuilder.ToString
End Function
2018-11-03 02:47:48 +08:00
#End Region
2018-08-02 20:14:48 +08:00
End Class
End Namespace