1 #Region "Microsoft.VisualBasic::e9c1f8e8c99d56f8e8b67277f9c1f934, Microsoft.VisualBasic.Core\CommandLine\InteropService\SharedORM\API.vb"
2
3     ' Author:
4     
5     '       asuka (amethyst.asuka@gcmodeller.org)
6     '       xie (genetics@smrucc.org)
7     '       xieguigang (xie.guigang@live.com)
8     
9     ' Copyright (c) 2018 GPL3 Licensed
10     
11     
12     ' GNU GENERAL PUBLIC LICENSE (GPL3)
13     
14     
15     ' This program is free software: you can redistribute it and/or modify
16     ' it under the terms of the GNU General Public License as published by
17     ' the Free Software Foundation, either version 3 of the License, or
18     ' (at your option) any later version.
19     
20     ' This program is distributed in the hope that it will be useful,
21     ' but WITHOUT ANY WARRANTY; without even the implied warranty of
22     ' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23     ' GNU General Public License for more details.
24     
25     ' You should have received a copy of the GNU General Public License
26     ' along with this program. If not, see <http://www.gnu.org/licenses/>.
27
28
29
30     ' /********************************************************************************/
31
32     ' Summaries:
33
34     '     Module API
35     
36     '         Function: BuildArguments, CommandLineModel, Tokenize
37     
38     '         SubTokenize
39     
40     
41     ' /********************************************************************************/
42
43 #End Region
44
45 Imports System.Runtime.CompilerServices
46 Imports System.Text.RegularExpressions
47 Imports Microsoft.VisualBasic.CommandLine.Reflection
48 Imports Microsoft.VisualBasic.ComponentModel.DataSourceModel
49 Imports Microsoft.VisualBasic.Emit.Marshal
50 Imports Microsoft.VisualBasic.Language
51 Imports Microsoft.VisualBasic.Serialization.JSON
52 Imports Microsoft.VisualBasic.Text
53
54 Namespace CommandLine.InteropService.SharedORM
55
56     ''' <summary>
57     ''' CommandLine source code mapper API
58     ''' </summary>
59     Public Module API
60
61         ' Usage:= /command1 /param1 <value1, means a string value?> /param2 <value2> [/boolean1 /boolean2 /opt <blabla, default=value233>]
62
63         ''' <summary>
64         ''' 从<see cref="ExportAPIAttribute.Usage"/>之中解析出命令行的模型
65         ''' </summary>
66         ''' <param name="usage$">
67         ''' + 由于习惯于在命令行的usage说明之中使用``&lt;>``括号对来包裹参数值,所以usage字符串数据还不能直接使用普通的命令行函数进行解析
68         ''' + ``[]``方括号所包裹的参数对象都是可选参数
69         ''' + 对于可选参数的默认值,默认值的解析形式为``default=...``,如果没有这个表达式,则默认为Nothing空值为默认值
70         ''' </param>
71         ''' <returns></returns>
72         <Extension> Public Function CommandLineModel(usage As StringAs CommandLine
73             Dim name$ = Nothing
74             Dim arguments$() = Nothing
75             Dim optionals$() = Nothing
76
77             usage = usage.Trim
78             usage.Tokenize(name, arguments, optionals)
79
80             ' 逻辑符号只存在于optional之中
81             Dim booleans$() = Nothing
82             Dim params As NamedValue(Of String)()
83
84             Try
85                 params = arguments.BuildArguments(optionals, booleans)
86             Catch ex As Exception
87                 Dim msg$ = $"Invalid commandline usage({usage})!" & vbCrLf & vbCrLf
88                 Dim details = New Dictionary(Of StringString()) From {
89                     {NameOf(arguments), arguments},
90                     {NameOf(optionals), optionals},
91                     {NameOf(booleans), booleans}
92                 }.GetJson
93
94                 msg = msg & details
95
96                 Throw New ArgumentException(msg, ex)
97             End Try
98
99             Dim model As New CommandLine With {
100                 .Name = name,
101                 .__arguments = params.AsList,
102                 .BoolFlags = booleans.Select(AddressOf LCase).ToArray,
103                 .cliCommandArgvs = usage
104             }
105
106             Return model
107         End Function
108
109         ''' <summary>
110         ''' 
111         ''' </summary>
112         ''' <param name="args$">因为逻辑变量只存在于可选参数之中,所以在这里必须参数直接split2即可</param>
113         ''' <param name="optionals$"></param>
114         ''' <param name="booleans$"></param>
115         ''' <returns></returns>
116         <Extension>
117         Public Function BuildArguments(args$(), optionals$(), ByRef booleans$()) As NamedValue(Of String)()
118             Dim out As List(Of NamedValue(Of String)) = args _
119                 .Split(2) _
120                 .Select(Function(a)
121                             Return New NamedValue(Of StringWith {
122                                 .Name = a(0),
123                                 .Value = a(1)
124                             }
125                         End Function) _
126                 .AsList
127
128             booleans = GetLogicalFlags(optionals, Nothing)
129             out += optionals _
130                 .CreateParameterValues(False, note:=NameOf(optionals)) _
131                 .ToArray
132
133             Return out
134         End Function
135
136         <Extension>
137         Public Sub Tokenize(usage$, ByRef name$, ByRef arguments$(), ByRef optionals$())
138             Dim opts$ = Regex.Match(usage, "\[.+\]").Value
139
140             If opts.Length > 0 Then
141                 usage = usage.Replace(opts, "")
142                 opts = opts.GetStackValue("[""]")
143             End If
144
145             ' 命令的名称肯定是没有空格的,所以在里直接split取第一个元素
146             name = usage.Split.First
147             usage = Mid(usage, name.Length + 1).Trim
148
149             ' 在下面将arguments和optionals参数进行分词就行了
150             ' 因为在usage之中并不会使用双引号来分割value值,而是使用尖括号
151             ' 所以在这里需要额外注意下
152             arguments = usage.Tokenize
153             optionals = opts.Tokenize
154         End Sub
155
156         ''' <summary>
157         ''' 使用空格分隔,但是需要对value额外注意
158         ''' 
159         ''' + 在这里面分隔符为空格
160         ''' + 可以使用双引号包裹值,则双引号之中的字符串在可选参数之中都被看作为可选参数值
161         ''' + 可以使用尖括号包裹值,则尖括号之中的default=表达式则是可选参数之中的默认参数值
162         ''' </summary>
163         ''' <param name="s$"></param>
164         ''' <returns></returns>
165         <Extension> Public Function Tokenize(s As StringAs String()
166             Dim t As New Pointer(Of Char)(s)
167             Dim c As Char
168             Dim valueEscape As Boolean = False
169             Dim tmp As New List(Of Char)
170             Dim out As New List(Of String)
171             Dim escapeType As Char ' 转义的起始只有双引号或者左边的尖括号
172             Dim isValueEnd =
173                 Function()
174                     Dim last As Char = tmp.LastOrDefault
175                     Return (escapeType = "<"AndAlso last = ">"c) OrElse
176                            (escapeType = ASCII.Quot AndAlso last = ASCII.Quot)
177                 End Function
178
179             Do While Not t.EndRead
180                 c = ++t
181
182                 If c = " "Then
183                     If Not valueEscape Then
184                         out += New String(tmp)
185                         tmp *= 0
186                     Else
187                         ' 检查上一个字符是不是value的结束符号: >
188                         If isValueEnd() Then
189
190                             ' 则这个空格表示value的结束
191                             out += New String(tmp)
192                             valueEscape = False
193                             tmp *= 0
194                             escapeType = " "' 重置转义类型
195
196                         Else
197                             ' 这个空格是value值之中的一部分,则添加到临时列表
198                             tmp += c
199                         End If
200                     End If
201                 Else
202                     ' 检查是否是value的开始符号: <或者双引号
203                     If (c = "<"OrElse c = ASCII.Quot) AndAlso tmp = 0 Then
204                         valueEscape = True
205                         escapeType = c
206                     End If
207
208                     tmp += c
209                 End If
210             Loop
211
212             If tmp.Count > 0 Then
213                 Return out + New String(tmp)
214             Else
215                 Return out
216             End If
217         End Function
218     End Module
219 End Namespace