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
|
|
|
|
|
|
|
|
|
|
' 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
|
|
|
|
|
|
|
|
|
|
|
|
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-08-02 20:14:48 +08:00
|
|
|
|
Imports Token = System.Collections.Generic.KeyValuePair(Of String(), Microsoft.VisualBasic.Scripting.TextGrepMethodToken)
|
2018-11-03 02:47:48 +08:00
|
|
|
|
Imports r = System.Text.RegularExpressions.Regex
|
|
|
|
|
|
Imports System.ComponentModel
|
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">
|
|
|
|
|
|
''' 如果这个参数传递的是一个空字符串,那么这个函数将会直接返回<see cref="DoNothing"/>脚本
|
|
|
|
|
|
''' </param>
|
2018-08-02 20:14:48 +08:00
|
|
|
|
''' <returns></returns>
|
|
|
|
|
|
''' <remarks></remarks>
|
|
|
|
|
|
<ExportAPI("compile", Info:="", Usage:="script_tokens1;script_tokens2;....", Example:="")>
|
|
|
|
|
|
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"
|
|
|
|
|
|
|
2018-08-02 20:14:48 +08:00
|
|
|
|
<MethodImpl(MethodImplOptions.AggressiveInlining)>
|
|
|
|
|
|
<ExportAPI("-", Info:="DO_NOTHING")>
|
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>
|
|
|
|
|
|
<ExportAPI("Tokens", Info:="", Usage:="tokens p_str pointer", Example:="")>
|
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
|
|
|
|
|
|
|
|
|
|
|
|
<ExportAPI("match", Info:="", Usage:="match pattern", Example:="")>
|
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>
|
|
|
|
|
|
<ExportAPI("mid", Info:="Substring a token from the input text source.")>
|
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
|
|
|
|
|
|
|
|
|
|
|
|
<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
|