1 | #Region "Microsoft.VisualBasic::3144cbda496e0193f901526dfec42493, Microsoft.VisualBasic.Core\CommandLine\Interpreters\Interpreter.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 Interpreter |
35 | ' |
36 | ' Properties: APIList, APINameList, Count, ExecuteEmptyCli, ExecuteFile |
37 | ' ExecuteNotFound, Info, IsReadOnly, ListCommandInfo, Stack |
38 | ' Type |
39 | ' |
40 | ' Constructor: (+1 Overloads) Sub New |
41 | ' |
42 | ' Function: __executeEmpty, __getsAllCommands, apiInvoke, Contains, CreateEmptyCLIObject |
43 | ' (+3 Overloads) CreateInstance, (+3 Overloads) Execute, ExistsCommand, GetAllCommands, getAPI |
44 | ' GetEnumerator, GetEnumerator1, GetPossibleCommand, Help, ListingRelated |
45 | ' (+2 Overloads) Remove, SDKdocs, ToDictionary, ToString, TryGetValue |
46 | ' |
47 | ' Sub: (+2 Overloads) Add, AddCommand, Clear, CopyTo, (+2 Overloads) Dispose |
48 | ' |
49 | ' |
50 | ' /********************************************************************************/ |
51 | |
52 | #End Region |
53 | |
54 | Imports System.Reflection |
55 | Imports System.Runtime.CompilerServices |
56 | Imports System.Text |
57 | Imports Microsoft.VisualBasic.ApplicationServices.Debugging |
58 | Imports Microsoft.VisualBasic.CommandLine.ManView |
59 | Imports Microsoft.VisualBasic.CommandLine.Reflection |
60 | Imports Microsoft.VisualBasic.CommandLine.Reflection.EntryPoints |
61 | Imports Microsoft.VisualBasic.ComponentModel.Settings |
62 | Imports Microsoft.VisualBasic.Language |
63 | Imports Microsoft.VisualBasic.Language.UnixBash |
64 | Imports Microsoft.VisualBasic.Linq.Extensions |
65 | Imports Microsoft.VisualBasic.Serialization.JSON |
66 | Imports Microsoft.VisualBasic.Text |
67 | Imports Microsoft.VisualBasic.Text.Levenshtein |
68 | Imports VB = Microsoft.VisualBasic.CommandLine.InteropService.SharedORM.VisualBasic |
69 | |
70 | #Const NET_45 = 0 |
71 | |
72 | Namespace CommandLine |
73 | |
74 | ''' <summary> |
75 | ''' Command line interpreter for your **CLI** program. |
76 | ''' (命令行解释器,请注意,在调试模式之下,命令行解释器会在运行完命令之后暂停,而Release模式之下则不会。 |
77 | ''' 假若在调试模式之下发现程序有很长一段时间处于cpu占用为零的静止状态,则很有可能已经运行完命令并且等待 |
78 | ''' 回车退出) |
79 | ''' </summary> |
80 | ''' <remarks></remarks> |
81 | ''' |
82 | <[Namespace]("Interpreter")> Public Class Interpreter |
83 | |
84 | Implements IDisposable |
85 | Implements IDictionary(Of String, APIEntryPoint) |
86 | |
87 | ''' <summary> |
88 | ''' 在添加之前请确保键名是小写的字符串 |
89 | ''' </summary> |
90 | Protected Friend __API_table As New Dictionary(Of String, APIEntryPoint) |
91 | Protected __rootNamespace$ |
92 | |
93 | #Region "Optional delegates" |
94 | |
95 | ''' <summary> |
96 | ''' Public Delegate Function __ExecuteFile(path As String, args As String()) As Integer, |
97 | ''' (<seealso cref="VisualBasic.CommandLine.ExecuteFile"/>: 假若所传入的命令行的name是文件路径,解释器就会执行这个函数指针) |
98 | ''' 这个函数指针一般是用作于执行脚本程序的 |
99 | ''' </summary> |
100 | ''' <returns></returns> |
101 | Public Property ExecuteFile As ExecuteFile |
102 | ''' <summary> |
103 | ''' Public Delegate Function __ExecuteEmptyCli() As Integer, |
104 | ''' (<seealso cref="VisualBasic.CommandLine.ExecuteEmptyCLI"/>: 假若所传入的命令行是空的,就会执行这个函数指针) |
105 | ''' </summary> |
106 | ''' <returns></returns> |
107 | Public Property ExecuteEmptyCli As ExecuteEmptyCLI |
108 | Public Property ExecuteNotFound As ExecuteNotFound |
109 | #End Region |
110 | |
111 | ''' <summary> |
112 | ''' Gets the dictionary data which contains all of the available command information in this assembly module. |
113 | ''' (获取从本模块之中获取得到的所有的命令行信息) |
114 | ''' </summary> |
115 | ''' <returns></returns> |
116 | ''' <remarks></remarks> |
117 | ''' |
118 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
119 | Public Function ToDictionary() As Dictionary(Of String, APIEntryPoint) |
120 | Return __API_table |
121 | End Function |
122 | |
123 | Public Overrides Function ToString() As String |
124 | Return "CLI://" & __rootNamespace |
125 | End Function |
126 | |
127 | ''' <summary> |
128 | ''' Execute the specific command line using this interpreter. |
129 | ''' </summary> |
130 | ''' <param name="args">The user input command line string.</param> |
131 | ''' <returns></returns> |
132 | ''' <remarks></remarks> |
133 | Public Overridable Function Execute(args As CommandLine) As Integer |
134 | If Not args.IsNullOrEmpty Then |
135 | Dim i As Integer = apiInvoke(args.Name.ToLower, {args}, args.Parameters) |
136 | #If DEBUG Then |
137 | |
138 | #Else |
139 | If Stack.TextEquals("Main") Then |
140 | If DebuggerArgs.AutoPaused Then |
141 | Call Pause() |
142 | End If |
143 | End If |
144 | #End If |
145 | Return i |
146 | Else |
147 | Return __executeEmpty() ' 命令行是空的 |
148 | End If |
149 | End Function |
150 | |
151 | ''' <summary> |
152 | ''' 命令行是空的话,假若<see cref="ExecuteEmptyCli"/>不是空值的话,会优先执行<see cref="ExecuteEmptyCli"/>函数指针 |
153 | ''' 否则打印出所有的命令名称信息 |
154 | ''' </summary> |
155 | ''' <returns></returns> |
156 | Private Function __executeEmpty() As Integer |
157 | If Not ExecuteEmptyCli Is Nothing Then |
158 | Try |
159 | Return _ExecuteEmptyCli() |
160 | Catch ex As Exception |
161 | Call App.LogException(ex) |
162 | Call ex.PrintException |
163 | End Try |
164 | |
165 | Return -100 |
166 | Else |
167 | ' 当用户什么也不输入的时候,打印出所有的命令名称帮助信息 |
168 | Return Help("") |
169 | End If |
170 | End Function |
171 | |
172 | ''' <summary> |
173 | ''' The interpreter runs all of the command from here.(所有的命令行都从这里开始执行) |
174 | ''' </summary> |
175 | ''' <param name="commandName"></param> |
176 | ''' <param name="argvs">就只有一个命令行对象</param> |
177 | ''' <param name="help_argvs"></param> |
178 | ''' <returns></returns> |
179 | Private Function apiInvoke(commandName$, argvs As Object(), help_argvs$()) As Integer |
180 | Dim CLI As CommandLine = DirectCast(argvs(Scan0), CommandLine) |
181 | |
182 | If __API_table.ContainsKey(commandName) Then _ |
183 | Return __API_table(commandName).Execute(argvs) |
184 | |
185 | If "??vars".TextEquals(commandName) Then |
186 | Dim vars = App.GetAppVariables |
187 | |
188 | Call Console.WriteLine() |
189 | Call Console.WriteLine(PS1.Fedora12.ToString) |
190 | Call Console.WriteLine() |
191 | Call Console.WriteLine($"Print environment variables for {GetType(App).FullName}:") |
192 | Call Console.WriteLine(ConfigEngine.Prints(vars)) |
193 | |
194 | Return 0 |
195 | |
196 | ElseIf "??history".TextEquals(commandName) Then |
197 | |
198 | Dim logs$ = (App.LogErrDIR.ParentPath & "/.shells.log") |
199 | |
200 | With DirectCast(argvs(Scan0), CommandLine) |
201 | If .Parameters.IsNullOrEmpty Then |
202 | Call Console.WriteLine() |
203 | Call logs.ReadAllText.EchoLine |
204 | Call Console.WriteLine() |
205 | Else |
206 | With .ParameterList.First |
207 | Select Case .Name.ToLower |
208 | Case "/clear" |
209 | Call New Byte() {}.FlushStream(logs) |
210 | Case "/search" |
211 | |
212 | Dim term$ = .Value |
213 | |
214 | Call Console.WriteLine() |
215 | Call logs.IterateAllLines _ |
216 | .Where(Function(line) |
217 | Return InStr(line, term, CompareMethod.Text) > 0 |
218 | End Function) _ |
219 | .JoinBy(vbCrLf) _ |
220 | .EchoLine |
221 | Call Console.WriteLine() |
222 | |
223 | Case Else |
224 | Console.WriteLine("Unknown command!") |
225 | End Select |
226 | End With |
227 | End If |
228 | End With |
229 | |
230 | Return 0 |
231 | |
232 | ElseIf String.Equals(commandName, "?") OrElse commandName = "??" OrElse commandName.TextEquals("--help") Then |
233 | If help_argvs.IsNullOrEmpty Then |
234 | Return Help("") |
235 | Else |
236 | Return Help(help_argvs.First) |
237 | End If |
238 | |
239 | ElseIf InStr(commandName, "??") = 1 Then ' 支持类似于R语言里面的 ??帮助命令 |
240 | commandName = Mid(commandName, 3) ' 去除前面的两个??问号,得到查询的term |
241 | Return Help(commandName) |
242 | |
243 | ElseIf String.Equals(commandName, "~") Then |
244 | ' 打印出应用程序的位置,linux里面的HOME |
245 | Call Console.WriteLine(App.ExecutablePath) |
246 | |
247 | Return 0 |
248 | |
249 | ElseIf String.Equals(commandName, "man") Then |
250 | ' 默认是分段打印帮助信息,假若加上了 --print参数的话,则才会一次性的打印所有的信息出来 |
251 | Dim doc As String = SDKdocs() |
252 | Dim output$ = CLI("/out") Or "./" |
253 | |
254 | If Not CLI.GetBoolean("--file") Then |
255 | If CLI.GetBoolean("--print") Then |
256 | Call Console.WriteLine(doc) |
257 | Else |
258 | Call SDKManual.LaunchManual(CLI:=Me) |
259 | End If |
260 | Else |
261 | ' 只会写文件而不会在终端打开帮助窗口 |
262 | End If |
263 | |
264 | Return doc.SaveTo($"{output}/{App.AssemblyName}.md", Encoding.UTF8).CLICode |
265 | |
266 | ElseIf String.Equals(commandName, "/linux-bash", StringComparison.OrdinalIgnoreCase) Then |
267 | Return BashShell() |
268 | |
269 | ElseIf String.Equals(commandName, "/CLI.dev", StringComparison.OrdinalIgnoreCase) Then |
270 | Dim namespace$ = CLI("/namespace") Or "CLI" |
271 | Dim vb$ = New VB(App:=Me, [namespace]:=[namespace]).GetSourceCode |
272 | |
273 | If CLI.IsTrue("---echo") Then |
274 | Console.WriteLine(vb) |
275 | Return 0 |
276 | Else |
277 | Dim sourcefile$ = $"{App.HOME}/{Type.Assembly.CodeBase.BaseName}.vb" |
278 | Return vb _ |
279 | .SaveTo(sourcefile, Encodings.UTF8WithoutBOM.CodePage) _ |
280 | .CLICode |
281 | End If |
282 | |
283 | Else |
284 | ' 命令行的名称和上面的都不符合,但是可以在文件系统之中找得到一个相应的文件,则执行文件句柄 |
285 | If (commandName.FileExists OrElse commandName.DirectoryExists) AndAlso Not Me.ExecuteFile Is Nothing Then |
286 | Try |
287 | App.InputFile = commandName |
288 | Return ExecuteFile()(path:=commandName, args:=DirectCast(argvs(Scan0), CommandLine)) |
289 | Catch ex As Exception |
290 | ex = New Exception("Execute file failure!", ex) |
291 | ex = New Exception(argvs(Scan0).ToString, ex) |
292 | Call App.LogException(ex) |
293 | Call ex.PrintException |
294 | End Try |
295 | |
296 | Return -120 |
297 | ElseIf Not ExecuteNotFound Is Nothing Then |
298 | Try |
299 | Return ExecuteNotFound()(DirectCast(argvs(Scan0), CommandLine)) |
300 | Catch ex As Exception |
301 | ex = New Exception("Execute not found failure!", ex) |
302 | ex = New Exception(argvs(Scan0).ToString, ex) |
303 | Call App.LogException(ex) |
304 | Call ex.PrintException |
305 | End Try |
306 | |
307 | Return -1000 |
308 | Else |
309 | Dim list$() = Me.ListingRelated(commandName) |
310 | |
311 | If list.IsNullOrEmpty Then |
312 | |
313 | Call Console.WriteLine(BAD_COMMAND_NAME, commandName) |
314 | Call Console.WriteLine() |
315 | Call Console.WriteLine(PS1.Fedora12.ToString & " " & DirectCast(argvs(Scan0), CommandLine).ToString) |
316 | |
317 | Else |
318 | Call listingCommands(list, commandName) |
319 | End If |
320 | End If |
321 | End If |
322 | |
323 | Return -1 |
324 | End Function |
325 | |
326 | Const BAD_COMMAND_NAME$ = "Bad command, no such a command named ""{0}"", ? for command list or ""man"" for all of the commandline detail informations." |
327 | |
328 | ''' <summary> |
329 | ''' Generate the sdk document for the target program assembly.(生成目标应用程序的命令行帮助文档,markdown格式的) |
330 | ''' </summary> |
331 | ''' <returns></returns> |
332 | ''' <remarks></remarks> |
333 | ''' |
334 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
335 | Public Function SDKdocs() As String |
336 | Return Me.MarkdownDoc |
337 | End Function |
338 | |
339 | ''' <summary> |
340 | ''' Process the command option arguments of the main function: |
341 | ''' <code>Public Function Main(argvs As String()) As Integer |
342 | ''' </code> |
343 | ''' </summary> |
344 | ''' <param name="CommandLineArgs">The cli command line parameter string value collection.</param> |
345 | ''' <returns></returns> |
346 | ''' <remarks></remarks> |
347 | Public Function Execute(CommandLineArgs As String()) As Integer |
348 | Dim CommandName As String = CommandLineArgs.First |
349 | Dim argvs As String() = CommandLineArgs.Skip(1).ToArray |
350 | Dim i As Integer = apiInvoke(CommandName, argvs, help_argvs:=argvs) |
351 | |
352 | #If DEBUG Then |
353 | If Not App.GetVariable("pause.disable").ParseBoolean = True Then |
354 | Call Pause() |
355 | End If |
356 | #Else |
357 | If Stack.TextEquals("Main") Then |
358 | If AutoPaused Then |
359 | Call Pause() |
360 | End If |
361 | End If |
362 | #End If |
363 | Return i |
364 | End Function |
365 | |
366 | Public Function Execute(CommandName As String, args As String()) As Integer |
367 | Return apiInvoke(CommandName.ToLower, args, help_argvs:=args) |
368 | End Function |
369 | |
370 | ''' <summary> |
371 | ''' Add a command in current cli interpreter.(x向当前的这个CLI命令行解释器之中添加一个命令) |
372 | ''' </summary> |
373 | ''' <param name="Command"></param> |
374 | ''' <remarks></remarks> |
375 | Public Sub AddCommand(Command As APIEntryPoint) |
376 | Dim key$ = Command.Name.ToLower |
377 | |
378 | If Not __API_table.ContainsKey(key) Then |
379 | Call __API_table.Add(key, Command) |
380 | End If |
381 | End Sub |
382 | |
383 | ''' <summary> |
384 | ''' Gets the help information of a specific command using its name property value.(获取某一个命令的帮助信息) |
385 | ''' </summary> |
386 | ''' <param name="CommandName">If the paramteer command name value is a empty string then this function |
387 | ''' will list all of the commands' help information.(假若本参数为空则函数会列出所有的命令的帮助信息)</param> |
388 | ''' <returns>Error code, ZERO for no error</returns> |
389 | ''' <remarks></remarks> |
390 | <ExportAPI("?", Usage:="? [CommandName]", Info:="Show Application help", Example:="? example_commandName")> |
391 | Public Function Help(CommandName As String) As Integer |
392 | If String.IsNullOrEmpty(CommandName) Then |
393 | ' List all commands when command name is empty. |
394 | Call Console.WriteLine(HelpSummary(False)) |
395 | Else |
396 | ' listing the help for specific command name |
397 | Call PrintCommandHelp(CommandName) |
398 | End If |
399 | |
400 | Return 0 |
401 | End Function |
402 | |
403 | ''' <summary> |
404 | ''' Returns the command entry info list array. |
405 | ''' </summary> |
406 | ''' <value></value> |
407 | ''' <returns></returns> |
408 | ''' <remarks></remarks> |
409 | Public ReadOnly Property ListCommandInfo As EntryPoints.APIEntryPoint() |
410 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
411 | Get |
412 | Return __API_table.Values.ToArray |
413 | End Get |
414 | End Property |
415 | |
416 | Public ReadOnly Property Stack As String |
417 | Public ReadOnly Property Info As [Namespace] |
418 | |
419 | ''' <summary> |
420 | ''' |
421 | ''' </summary> |
422 | ''' <param name="type">A module or a class which contains some shared method for the command entry. |
423 | ''' (包含有若干使用<see cref="Reflection.ExportAPIAttribute"></see>进行标记的命令行执行入口点的Module或者Class对象类型, |
424 | ''' 可以使用 Object.GetType/GetType 关键词操作来获取所需要的类型信息)</param> |
425 | ''' <remarks></remarks> |
426 | Sub New(type As Type, <CallerMemberName> Optional caller As String = Nothing) |
427 | For Each cInfo As APIEntryPoint In __getsAllCommands(type, False) |
428 | If __API_table.ContainsKey(cInfo.Name.ToLower) Then |
429 | Throw New Exception(cInfo.Name & " is duplicated with other command!") |
430 | Else |
431 | Call __API_table.Add(cInfo.Name.ToLower, cInfo) |
432 | End If |
433 | Next |
434 | |
435 | Me.__rootNamespace = type.Namespace |
436 | Me._Stack = caller |
437 | Me._Type = type |
438 | Me._Info = type.NamespaceEntry(True) |
439 | End Sub |
440 | |
441 | ''' <summary> |
442 | ''' The CLI API container Module/Class type information.(申明这个解释器的命令行API容器类型) |
443 | ''' </summary> |
444 | ''' <returns></returns> |
445 | Public ReadOnly Property Type As Type |
446 | |
447 | ''' <summary> |
448 | ''' 导出所有符合条件的静态方法 |
449 | ''' </summary> |
450 | ''' <param name="Type"></param> |
451 | ''' <param name="[Throw]"></param> |
452 | ''' <returns></returns> |
453 | Protected Overridable Function __getsAllCommands(Type As Type, Optional [Throw] As Boolean = True) As List(Of EntryPoints.APIEntryPoint) |
454 | Return GetAllCommands(Type, [Throw]) |
455 | End Function |
456 | |
457 | ''' <summary> |
458 | ''' 导出所有符合条件的静态方法,请注意,在这里已经将外部的属性标记和所属的函数的入口点进行连接了 |
459 | ''' </summary> |
460 | ''' <param name="type"></param> |
461 | ''' <param name="[Throw]"></param> |
462 | ''' <returns></returns> |
463 | Public Shared Function GetAllCommands(type As Type, Optional [Throw] As Boolean = True) As List(Of EntryPoints.APIEntryPoint) |
464 | If type Is Nothing Then |
465 | Return New List(Of APIEntryPoint) |
466 | End If |
467 | |
468 | Dim methods As MethodInfo() = type.GetMethods(BindingFlags.Public Or BindingFlags.Static) |
469 | Dim commandAttribute As Type = GetType(ExportAPIAttribute) |
470 | Dim commandsInfo = LinqAPI.MakeList(Of APIEntryPoint) <= |
471 | _ |
472 | From methodInfo As MethodInfo |
473 | In methods |
474 | Let commandInfo As APIEntryPoint = |
475 | getAPI(methodInfo, commandAttribute, [Throw]) |
476 | Where Not commandInfo Is Nothing |
477 | Select commandInfo |
478 | Order By commandInfo.Name Ascending |
479 | |
480 | Return commandsInfo |
481 | End Function |
482 | |
483 | ''' <summary> |
484 | ''' 从方法定义<see cref="MethodInfo"/>之中解析出命令行的定义 |
485 | ''' </summary> |
486 | ''' <param name="methodInfo"></param> |
487 | ''' <param name="commandAttribute"></param> |
488 | ''' <param name="[throw]"></param> |
489 | ''' <returns></returns> |
490 | Private Shared Function getAPI(methodInfo As MethodInfo, commandAttribute As Type, [throw] As Boolean) As APIEntryPoint |
491 | Dim cmdAttr As ExportAPIAttribute = Nothing |
492 | Dim commandInfo As APIEntryPoint |
493 | |
494 | Try |
495 | Dim attrs As Object() = methodInfo.GetCustomAttributes(commandAttribute, False) |
496 | If attrs.IsNullOrEmpty Then |
497 | Return Nothing |
498 | End If |
499 | |
500 | cmdAttr = DirectCast(attrs(0), ExportAPIAttribute) |
501 | commandInfo = New APIEntryPoint(cmdAttr, methodInfo, [throw]) ' 在这里将外部的属性标记和所属的函数的入口点进行连接 |
502 | If cmdAttr.Info.StringEmpty Then |
503 | cmdAttr.Info = methodInfo.Description ' 帮助信息的获取兼容系统的Description方法 |
504 | End If |
505 | If cmdAttr.Usage.StringEmpty Then |
506 | cmdAttr.Usage = methodInfo.Usage |
507 | End If |
508 | If cmdAttr.Example.StringEmpty Then |
509 | cmdAttr.Example = methodInfo.ExampleInfo |
510 | End If |
511 | |
512 | Return commandInfo |
513 | Catch ex As Exception |
514 | If Not cmdAttr Is Nothing Then |
515 | ex = New Exception("This command API can not be imports: " & cmdAttr.GetJson, ex) |
516 | ex = New Exception(CheckNotice, ex) |
517 | End If |
518 | Call App.LogException(New Exception(methodInfo.FullName(True), ex)) |
519 | Call ex.PrintException |
520 | |
521 | Return Nothing |
522 | End Try |
523 | End Function |
524 | |
525 | Const CheckNotice As String = "Please checks for the export api definition on your CLI interface function." |
526 | |
527 | ''' <summary> |
528 | ''' Create an empty cli command line interpreter object which contains no commands entry. |
529 | ''' (创建一个没有包含有任何命令入口点的空的CLI命令行解释器) |
530 | ''' </summary> |
531 | ''' <returns></returns> |
532 | ''' <remarks></remarks> |
533 | ''' |
534 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
535 | Public Shared Function CreateEmptyCLIObject() As Interpreter |
536 | Return New Interpreter(GetType(Interpreter)) |
537 | End Function |
538 | |
539 | ''' <summary> |
540 | ''' Create a new interpreter instance from a specific type information. |
541 | ''' (从目标类型之中构造出一个命令行解释器) |
542 | ''' </summary> |
543 | ''' <param name="Type"></param> |
544 | ''' <returns></returns> |
545 | ''' <remarks></remarks> |
546 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
547 | <ExportAPI("CreateObject")> |
548 | Public Shared Function CreateInstance(type As Type) As Interpreter |
549 | Return New Interpreter(type) |
550 | End Function |
551 | |
552 | ''' <summary> |
553 | ''' Create a new interpreter instance using the specific type information. |
554 | ''' (使用所制定的目标类型信息构造出一个CLI命令行解释器) |
555 | ''' </summary> |
556 | ''' <typeparam name="T"></typeparam> |
557 | ''' <returns></returns> |
558 | ''' <remarks></remarks> |
559 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
560 | Public Shared Function CreateInstance(Of T As Class)() As Interpreter |
561 | Return New Interpreter(type:=GetType(Type)) |
562 | End Function |
563 | |
564 | #If NET_40 = 0 Then |
565 | |
566 | ''' <summary> |
567 | ''' Create a new interpreter instance from a specific dll/exe path, this program assembly file should be a standard .NET assembly. |
568 | ''' (从一个标准的.NET程序文件之中构建出一个命令行解释器) |
569 | ''' </summary> |
570 | ''' <param name="assmPath">DLL/EXE file path.(标准的.NET程序集文件的文件路径)</param> |
571 | ''' <returns></returns> |
572 | ''' <remarks></remarks> |
573 | ''' |
574 | <ExportAPI("rundll")> |
575 | Public Shared Function CreateInstance(assmPath As String) As Interpreter |
576 | Dim assembly As Assembly = Assembly.LoadFrom(assmPath) |
577 | Dim dllMain As Type = GetType(RunDllEntryPoint) |
578 | Dim main As Type = LinqAPI.DefaultFirst(Of Type) _ |
579 | _ |
580 | () <= From [mod] As Type |
581 | In assembly.DefinedTypes |
582 | Let attributes As Object() = [mod].GetCustomAttributes(dllMain, inherit:=False) |
583 | Where Not attributes Is Nothing AndAlso |
584 | attributes.Length = 1 |
585 | Select [mod] |
586 | |
587 | If main Is Nothing Then |
588 | Return Nothing ' 没有找到执行入口点 |
589 | Else |
590 | Return New Interpreter(main) |
591 | End If |
592 | End Function |
593 | #End If |
594 | |
595 | #Region "IDisposable Support" |
596 | Private disposedValue As Boolean ' To detect redundant calls |
597 | |
598 | ' IDisposable |
599 | Protected Sub Dispose(disposing As Boolean) |
600 | If Not Me.disposedValue Then |
601 | If disposing Then |
602 | ' TODO: dispose managed state (managed objects). |
603 | End If |
604 | |
605 | ' TODO: free unmanaged resources (unmanaged objects) and override Finalize() below. |
606 | ' TODO: set large fields to null. |
607 | End If |
608 | Me.disposedValue = True |
609 | End Sub |
610 | |
611 | ' TODO: override Finalize() only if Dispose( disposing As Boolean) above has code to free unmanaged resources. |
612 | 'Protected Overrides Sub Finalize() |
613 | ' ' Do not change this code. Put cleanup code in Dispose( disposing As Boolean) above. |
614 | ' Dispose(False) |
615 | ' MyBase.Finalize() |
616 | 'End Sub |
617 | |
618 | ' This code added by Visual Basic to correctly implement the disposable pattern. |
619 | Public Sub Dispose() Implements IDisposable.Dispose |
620 | ' Do not change this code. Put cleanup code in Dispose(disposing As Boolean) above. |
621 | Dispose(True) |
622 | GC.SuppressFinalize(Me) |
623 | End Sub |
624 | #End Region |
625 | |
626 | #Region "Implements System.Collections.Generic.IReadOnlyDictionary(Of String, CommandInfo)" |
627 | |
628 | Public Iterator Function GetEnumerator() As IEnumerator(Of KeyValuePair(Of String, EntryPoints.APIEntryPoint)) Implements IEnumerable(Of KeyValuePair(Of String, EntryPoints.APIEntryPoint)).GetEnumerator |
629 | For Each key As String In Me.__API_table.Keys |
630 | Yield New KeyValuePair(Of String, EntryPoints.APIEntryPoint)(key, Me.__API_table(key)) |
631 | Next |
632 | End Function |
633 | |
634 | Public Iterator Function GetEnumerator1() As IEnumerator Implements IEnumerable.GetEnumerator |
635 | Yield Me.GetEnumerator |
636 | End Function |
637 | |
638 | Public Sub Add(item As KeyValuePair(Of String, EntryPoints.APIEntryPoint)) Implements ICollection(Of KeyValuePair(Of String, EntryPoints.APIEntryPoint)).Add |
639 | Call __API_table.Add(item.Key, item.Value) |
640 | End Sub |
641 | |
642 | ''' <summary> |
643 | ''' Clear the hash table of the cli command line interpreter command entry points.(清除本CLI解释器之中的所有的命令行执行入口点的哈希数据信息) |
644 | ''' </summary> |
645 | ''' <remarks></remarks> |
646 | Public Sub Clear() Implements ICollection(Of KeyValuePair(Of String, EntryPoints.APIEntryPoint)).Clear |
647 | Call __API_table.Clear() |
648 | End Sub |
649 | |
650 | Public Function Contains(item As KeyValuePair(Of String, EntryPoints.APIEntryPoint)) As Boolean Implements ICollection(Of KeyValuePair(Of String, EntryPoints.APIEntryPoint)).Contains |
651 | Return __API_table.Contains(item) |
652 | End Function |
653 | |
654 | Public Sub CopyTo(array() As KeyValuePair(Of String, EntryPoints.APIEntryPoint), arrayIndex As Integer) Implements ICollection(Of KeyValuePair(Of String, EntryPoints.APIEntryPoint)).CopyTo |
655 | Call __API_table.ToArray.CopyTo(array, arrayIndex) |
656 | End Sub |
657 | |
658 | ''' <summary> |
659 | ''' Gets the command counts in current cli interpreter.(返回本CLI命令行解释器之中所包含有的命令的数目) |
660 | ''' </summary> |
661 | ''' <value></value> |
662 | ''' <returns></returns> |
663 | ''' <remarks></remarks> |
664 | Public ReadOnly Property Count As Integer Implements ICollection(Of KeyValuePair(Of String, EntryPoints.APIEntryPoint)).Count |
665 | Get |
666 | Return Me.__API_table.Count |
667 | End Get |
668 | End Property |
669 | |
670 | Public ReadOnly Property IsReadOnly As Boolean Implements ICollection(Of KeyValuePair(Of String, EntryPoints.APIEntryPoint)).IsReadOnly |
671 | Get |
672 | Return False |
673 | End Get |
674 | End Property |
675 | |
676 | Public Function Remove(item As KeyValuePair(Of String, EntryPoints.APIEntryPoint)) As Boolean Implements ICollection(Of KeyValuePair(Of String, EntryPoints.APIEntryPoint)).Remove |
677 | Return __API_table.Remove(item.Key) |
678 | End Function |
679 | |
680 | Public Sub Add(key As String, value As EntryPoints.APIEntryPoint) Implements IDictionary(Of String, EntryPoints.APIEntryPoint).Add |
681 | Call __API_table.Add(key, value) |
682 | End Sub |
683 | |
684 | ''' <summary> |
685 | ''' The target command line command is exists in this cli interpreter using it name property?(判断目标命令行命令是否存在于本CLI命令行解释器之中) |
686 | ''' </summary> |
687 | ''' <param name="CommandName">The command name value is not case sensitive.(命令的名称对大小写不敏感的)</param> |
688 | ''' <returns></returns> |
689 | ''' <remarks></remarks> |
690 | Public Function ExistsCommand(CommandName As String) As Boolean Implements IDictionary(Of String, EntryPoints.APIEntryPoint).ContainsKey |
691 | Return Me.__API_table.ContainsKey(CommandName.ToLower) |
692 | End Function |
693 | |
694 | ''' <summary> |
695 | ''' |
696 | ''' </summary> |
697 | ''' <param name="key">调用前需要转换为小写字母的形式</param> |
698 | ''' <returns></returns> |
699 | Default Public Overloads Property Item(key As String) As EntryPoints.APIEntryPoint Implements IDictionary(Of String, EntryPoints.APIEntryPoint).Item |
700 | Get |
701 | Return Me.__API_table(key) |
702 | End Get |
703 | Set(value As EntryPoints.APIEntryPoint) |
704 | 'DO NOTHING |
705 | End Set |
706 | End Property |
707 | |
708 | Public Function GetPossibleCommand(name As Value(Of String)) As APIEntryPoint |
709 | Dim commands = ListingRelated(name) |
710 | |
711 | If commands.Length = 0 Then |
712 | Return Nothing |
713 | Else |
714 | Return __API_table(commands.First.ToLower) |
715 | End If |
716 | End Function |
717 | |
718 | ''' <summary> |
719 | ''' 列举出所有可能的命令 |
720 | ''' </summary> |
721 | ''' <param name="query">模糊匹配</param> |
722 | ''' <returns></returns> |
723 | Public Function ListingRelated(query$) As String() |
724 | Dim key As New LevenshteinString(query.ToLower) |
725 | Dim LQuery = From x As String |
726 | In __API_table.Keys.AsParallel |
727 | Let compare = key Like x |
728 | Where Not compare Is Nothing AndAlso |
729 | compare.Score > 0.3 |
730 | Select compare.Score, |
731 | x |
732 | Order By Score Descending |
733 | |
734 | Dim levenshteins = LQuery _ |
735 | .Select(Function(x) x.x) _ |
736 | .AsList |
737 | |
738 | levenshteins += __API_table _ |
739 | .Keys _ |
740 | .Where(Function(s) |
741 | Return InStr(s, query, CompareMethod.Text) > 0 OrElse |
742 | InStr(query, s, CompareMethod.Text) > 0 |
743 | End Function) |
744 | |
745 | Return levenshteins _ |
746 | .Distinct _ |
747 | .Select(Function(name) |
748 | Return __API_table(name).Name |
749 | End Function) _ |
750 | .ToArray |
751 | End Function |
752 | |
753 | ''' <summary> |
754 | ''' List all of the command line entry point name which were contains in this cli interpreter. |
755 | ''' (列举出本CLI命令行解释器之中的所有的命令行执行入口点的名称) |
756 | ''' </summary> |
757 | ''' <value></value> |
758 | ''' <returns></returns> |
759 | ''' <remarks></remarks> |
760 | Public ReadOnly Property APINameList As ICollection(Of String) Implements IDictionary(Of String, EntryPoints.APIEntryPoint).Keys |
761 | Get |
762 | Return Me.__API_table.Keys |
763 | End Get |
764 | End Property |
765 | |
766 | Public Function Remove(CommandName As String) As Boolean Implements IDictionary(Of String, EntryPoints.APIEntryPoint).Remove |
767 | Return __API_table.Remove(CommandName) |
768 | End Function |
769 | |
770 | Public Function TryGetValue(key As String, ByRef value As EntryPoints.APIEntryPoint) As Boolean Implements IDictionary(Of String, EntryPoints.APIEntryPoint).TryGetValue |
771 | Return Me.__API_table.TryGetValue(key, value) |
772 | End Function |
773 | |
774 | ''' <summary> |
775 | ''' 当前的解释器内所容纳的所有的CLI API列表 |
776 | ''' </summary> |
777 | ''' <returns></returns> |
778 | Public ReadOnly Property APIList As ICollection(Of EntryPoints.APIEntryPoint) Implements IDictionary(Of String, EntryPoints.APIEntryPoint).Values |
779 | Get |
780 | Return Me.__API_table.Values |
781 | End Get |
782 | End Property |
783 | #End Region |
784 | End Class |
785 | End Namespace |