1 #Region "Microsoft.VisualBasic::345a0252b689d3c891f91c1810aca5fb, Microsoft.VisualBasic.Core\CommandLine\InteropService\SharedORM\VisualBasic.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     '     Class VisualBasic
35     
36     '         Constructor: (+2 OverloadsSub New
37     
38     '         Function: __CLI, __defaultValue, __normalizedAsIdentifier, __vbParameters, __xmlComments
39     '                   GetSourceCode
40     
41     '         Sub: __calls
42     
43     
44     ' /********************************************************************************/
45
46 #End Region
47
48 Imports System.Text
49 Imports Microsoft.VisualBasic.ComponentModel.DataSourceModel
50 Imports Microsoft.VisualBasic.Language
51 Imports Microsoft.VisualBasic.Scripting.SymbolBuilder
52 Imports Microsoft.VisualBasic.Text
53 Imports Microsoft.VisualBasic.Text.Xml
54
55 Namespace CommandLine.InteropService.SharedORM
56
57     Public Class VisualBasic : Inherits CodeGenerator
58
59         Dim namespace$
60
61         Public Sub New(CLI As Type, namespace$)
62             MyBase.New(CLI)
63             Me.namespace = [namespace]
64         End Sub
65
66         Sub New(App As Interpreter)
67             Call MyBase.New(App)
68             Me.namespace = App.Type.Name
69         End Sub
70
71         Public Overrides Function GetSourceCode() As String
72             Dim vb As New StringBuilder
73             Dim className$ = MyBase.exe _
74                 .NormalizePathString(OnlyASCII:=True) _
75                 .Replace(" ""_")
76             Dim rel$ = ProgramPathSearchTool.RelativePath(App.Type.Assembly.Location.GetFullPath)
77             Dim info$ = App.Type.NamespaceEntry.Description
78
79             Call vb.AppendLine("Imports " & GetType(StringBuilder).Namespace)
80             Call vb.AppendLine("Imports " & GetType(IIORedirectAbstract).Namespace)
81             Call vb.AppendLine("Imports " & GetType(InteropService).Namespace)
82             Call vb.AppendLine("Imports Microsoft.VisualBasic.ApplicationServices")
83             Call vb.AppendLine()
84             Call vb.AppendLine("' Microsoft VisualBasic CommandLine Code AutoGenerator")
85             Call vb.AppendLine("' assembly: " & rel)
86             Call vb.AppendLine()
87             Call vb.AppendLine(GetManualPage.LineTokens.Select(Function(l) "' " & l).JoinBy(vbCrLf))
88             Call vb.AppendLine()
89             Call vb.AppendLine("Namespace " & [namespace])
90             Call vb.AppendLine()
91             Call vb.AppendLine(__xmlComments(XmlEntity.EscapingXmlEntity(info)))
92             Call vb.AppendLine($"Public Class {VBLanguage.AutoEscapeVBKeyword(className)} : Inherits {GetType(InteropService).Name}")
93             Call vb.AppendLine()
94             Call vb.AppendLine($"    Public Const App$ = ""{exe}.exe""")
95             Call vb.AppendLine()
96             Call vb.AppendLine("    Sub New(App$)")
97             Call vb.AppendLine($"        MyBase.{NameOf(InteropService._executableAssembly)} = App$")
98             Call vb.AppendLine("    End Sub")
99
100             For Each API In Me.EnumeratesAPI
101                 Call __calls(vb, API.CLI, incompatible:=Not InCompatibleAttribute.CLRProcessCompatible(API.API))
102             Next
103
104             Call vb.AppendLine("End Class")
105             Call vb.AppendLine("End Namespace")
106
107             Return vb.ToString
108         End Function
109
110         Private Shared Function __xmlComments(description$) As String
111             If description.StringEmpty Then
112                 description = "<span class="xml_comment">'''"</span>
113             Else
114                 description = description _
115                     .LineTokens _
116                     .Select(Function(s) "''' " & s.Trim(" "c, ASCII.TAB)) _
117                     .JoinBy(vbCrLf)
118             End If
119
120             Return $"
121 <span class="xml_comment">''' <summary></span>
122 {description}
123 <span class="xml_comment">''' </summary></span>
124 <span class="xml_comment">'''"</span>
125         End Function
126
127         <span class="xml_comment">''' <summary></span>
128         <span class="xml_comment">''' </span>
129         <span class="xml_comment">''' </summary></span>
130         <span class="xml_comment">''' <param name="vb"></param></span>
131         <span class="xml_comment">''' <param name="API"></param></span>
132         <span class="xml_comment">''' <remarks></span>
133         <span class="xml_comment">''' </remarks></span>
134         Private Sub __calls(vb As StringBuilder, API As NamedValue(Of CommandLine), incompatible As Boolean)
135
136 #Region "Code template"
137
138             Public Function CommandName(args$,....Optional args$....) As Integer
139             '     Dim CLI$ = "commandname arguments"
140             '     Dim proc As IIORedirectAbstract = RunDotNetApp(CLI$)
141             '
142             '     Return proc.Run()
143             End Function
144 #End Region
145
146             ' 直接使用函数原型的名字了,会比较容易辨别一些
147             Dim func$ = API.Name
148             ' Xml comment 已经是经过转义了的,所以不需要再做xml entity的转义了
149             Dim xmlComments$ = __xmlComments(API.Description)
150             Dim params$()
151
152             Try
153                 If func.First <= "9AndAlso func.First >= "0"c Then
154                     func = "_" & func  ' 有些命令行开关是以数字开头的?
155                 Else
156                     ' 不是以数字开头的,则尝试解决关键词的问题
157                     func = VBLanguage.AutoEscapeVBKeyword(func)
158                 End If
159                 params = __vbParameters(API.Value)
160             Catch ex As Exception
161                 ex = New Exception("Check for your CLI Usage definition: " & API.Value.ToString, ex)
162                 Throw ex
163             End Try
164
165             Call vb.AppendLine(xmlComments)
166
167             Call vb.AppendLine($"Public Function {func}({params.JoinBy(", ")}) As Integer")
168             Call vb.AppendLine($"    Dim CLI As New StringBuilder(""{API.Value.Name}"")")
169             Call vb.AppendLine("    Call CLI.Append("" "")") ' 插入命令名称和参数值之间的一个必须的空格
170             Call vb.AppendLine(__CLI(+API))
171             Call vb.AppendLine()
172
173             If incompatible Then
174                 ' 这个CLI是不兼容的方法
175                 Call vb.AppendLine($"    Dim proc As {NameOf(IIORedirectAbstract)} = {NameOf(InteropService.RunProgram)}(CLI.ToString(), Nothing)")
176             Else
177                 ' 兼容的
178                 Call vb.AppendLine($"    Dim proc As {NameOf(IIORedirectAbstract)} = {NameOf(InteropService.RunDotNetApp)}(CLI.ToString())")
179             End If
180
181             Call vb.AppendLine($"    Return proc.{NameOf(IIORedirectAbstract.Run)}()")
182             Call vb.AppendLine("End Function")
183         End Sub
184
185
186         <span class="xml_comment">''' <summary></span>
187         <span class="xml_comment">''' 在这个函数之中会生成函数的参数列表</span>
188         <span class="xml_comment">''' </summary></span>
189         <span class="xml_comment">''' <param name="API"></param></span>
190         <span class="xml_comment">''' <returns></returns></span>
191         Private Shared Function __vbParameters(API As CommandLine) As String()
192             Dim out As New List(Of String)
193             Dim param$
194
195             For Each arg As NamedValue(Of StringIn API.ParameterList
196                 param = $"{VisualBasic.__normalizedAsIdentifier(arg.Name)} As String"
197
198                 If Not arg.Description.StringEmpty Then
199                     ' 可选参数
200                     param = $"Optional {param} = ""{__defaultValue(arg.Value)}"""
201                 End If
202
203                 out += param
204             Next
205             For Each bool In API.BoolFlags
206                 out += $"Optional {VisualBasic.__normalizedAsIdentifier(bool)} As Boolean = False"
207             Next
208
209             Return out
210         End Function
211
212         ''' <summary>
213         ''' 必须是以``default=``来作为前缀的,否则默认使用空字符串
214         ''' </summary>
215         ''' <param name="value$"></param>
216         ''' <returns></returns>
217         Private Shared Function __defaultValue(value$) As String
218             If value.First = """"AndAlso value.Last = """"Then
219                 ' 如果是直接使用双引号包裹而不是使用<>尖括号进行包裹,则认为双引号所包裹的值都是默认值
220                 value = value.GetStackValue(ASCII.Quot, ASCII.Quot)
221             ElseIf value.First = "<"c AndAlso value.Last = ">"c Then
222                 ' 而如果是使用尖括号的时候,则判断是否存在default=表达式,不存在则是空值
223                 value = value.GetStackValue("<", ">")
224
225                 If InStr(value, "default=") > 0 Then
226                     value = Strings.Split(value, "default=").Last.Trim(""""c)
227                 Else
228                     value = "" ' 没有表达式前缀,则使用默认的空字符串
229                 End If
230             Else
231                 ' 其他情况都认为是使用空值为默认值
232                 value = ""
233             End If
234
235             value = value.Replace(""""c, New String(ASCII.Quot, 2))
236
237             Return value
238         End Function
239
240         Private Shared Function __CLI(API As CommandLine) As String
241             Dim CLI As New StringBuilder
242             Dim vbcode$
243
244             For Each param In API.ParameterList
245                 Dim var$ = __normalizedAsIdentifier(param.Name)
246
247                 ' 注意:在这句代码的最后有一个空格,是间隔参数所必需的,不可以删除
248                 vbcode = $"    Call CLI.Append(""{param.Name} "" & """""""" & {var} & """""" "")"
249
250                 If param.Description.StringEmpty Then
251                     ' 必须参数不需要进一步判断,直接添加                    
252                     Call CLI.AppendLine(vbcode)
253                 Else
254                     ' 可选参数还需要IF判断是否存在                  
255                     Call CLI.AppendLine($"    If Not {var}.{NameOf(StringEmpty)} Then")
256                     Call CLI.AppendLine("        " & vbcode)
257                     Call CLI.AppendLine("    End If")
258                 End If
259             Next
260
261             For Each b In API.BoolFlags
262                 Dim var$ = __normalizedAsIdentifier(b)
263
264                 Call CLI.AppendLine($"    If {var} Then")
265                 Call CLI.AppendLine($"        Call CLI.Append(""{b} "")") ' 逻辑参数后面有一个空格,是正确的生成CLI所必需的
266                 Call CLI.AppendLine("    End If")
267             Next
268
269             Return CLI.ToString
270         End Function
271
272         Const SyntaxError$ = "'<' or '>' is using for the IO redirect in your terminal, unavailable for your commandline argument name!"
273
274         <span class="xml_comment">''' <summary></span>
275         <span class="xml_comment">''' 将命令行参数的名称转义为VB之中有效的对象标识符</span>
276         <span class="xml_comment">''' </summary></span>
277         <span class="xml_comment">''' <param name="arg$"></param></span>
278         <span class="xml_comment">''' <returns></returns></span>
279         Private Shared Function __normalizedAsIdentifier(arg$) As String
280             ' 在命令行的参数名称前面一般都会有/-之类的控制符前缀,在这里去掉
281             Dim name$ = arg.Trim("/"c, "\"c, "-"c)
282             Dim s As Char() = name.ToArray
283             Dim upper As Char() = name.ToUpper.ToArray
284             Dim c As Char
285
286             If s.First = "<"c OrElse s.Last = ">"c Then
287                 Throw New SyntaxErrorException(SyntaxError)
288             End If
289
290             For i As Integer = 0 To s.Length - 1
291                 c = upper(i)
292
293                 If (c >= "A"c AndAlso c <= "Z"c) OrElse (c >= "0"c AndAlso c <= "9"c) OrElse (c = "_"Then
294                     ' 合法的字符,不做处理
295                 Else
296                     ' 非法字符串都被替换为下划线
297                     s(i) = "_"c
298                 End If
299             Next
300
301             If s.First >= "0"c AndAlso s.First <= "9"c Then
302                 Return "_" & New String(s)
303             Else
304                 ' 可能会存在in, byref, class这类的名字,需要在这里转义一下
305                 Return VBLanguage.AutoEscapeVBKeyword(New String(s))
306             End If
307         End Function
308     End Class
309 End Namespace