1 #Region "Microsoft.VisualBasic::942bb8d88391cf07f45ae91051f76032, Microsoft.VisualBasic.Core\Scripting\Expressions\StringInterpolation.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 StringInterpolation
35     
36     '         FunctionGetValue, (+2 OverloadsInterpolate
37     
38     '         SubInterpolate
39     
40     
41     ' /********************************************************************************/
42
43 #End Region
44
45 Imports System.Runtime.CompilerServices
46 Imports System.Text
47 Imports System.Text.RegularExpressions
48 Imports Microsoft.VisualBasic.Scripting.SymbolBuilder
49
50 Namespace Scripting.Expressions
51
52     ''' <summary>
53     ''' 简单的字符串插值引擎,可以用来调试字符串表达式的处理结果
54     ''' </summary>
55     Public Module StringInterpolation
56
57         ' "abcdefg$h$i is $k \$a"
58
59         Const VB_str$ = "&VB_str"
60
61         ''' <summary>
62         ''' 允许下换线,点号,ASCII字母,以及数字作为标识符
63         ''' 
64         ''' ```
65         ''' $var
66         ''' $obj.property
67         ''' $obj.property.value.method.etc
68         ''' ```
69         ''' </summary>
70         Const VariablePattern$ = "[$]" & VBLanguage.IdentiferPattern & "(\." & VBLanguage.IdentiferPattern & ")*"
71
72         ''' <summary>
73         ''' 不存在的键名会自动返回空字符串
74         ''' </summary>
75         ''' <param name="resource"></param>
76         ''' <returns></returns>
77         <MethodImpl(MethodImplOptions.AggressiveInlining)>
78         <Extension>
79         Public Function GetValue(resource As Dictionary(Of StringString)) As Func(Of StringString)
80             Return Function(name$)
81                        If resource.ContainsKey(name) Then
82                            Return resource(name$)
83                        Else
84                            Return Nothing
85                        End If
86                    End Function
87         End Function
88
89         ''' <summary>
90         ''' 对于<paramref name="getValue"/>方法而言,是不需要``$``前缀了的
91         ''' </summary>
92         ''' <param name="expr$">
93         ''' 只有当变量的值不为空值的时候才会进行替换,但是当<paramref name="nullAsEmpty"/>为真的时候会被强行替换为空字符串进行替换
94         ''' </param>
95         ''' <param name="getValue">Get string value of the variable in the expression.</param>
96         ''' <param name="escape">
97         ''' 是否需要进行对\t\n这类字符的转义操作?假若是路径字符串,则不推荐开启这个选项了,因为路径的分隔符很容易引起误转义...
98         ''' </param>
99         ''' <returns></returns>
100         <Extension>
101         Public Function Interpolate(expr$, getValue As Func(Of StringString),
102                                     Optional nullAsEmpty As Boolean = False,
103                                     Optional escape As Boolean = TrueAs String
104
105             With New StringBuilder(expr)
106                 Call .Interpolate(getValue, nullAsEmpty, escape)
107                 Return .ToString
108             End With
109         End Function
110
111         ''' <summary>
112         ''' 在解析出variable之后,variable前面的``$``是会被清除掉的,所以variable的source <paramref name="getValue"/>里面的变量名称应该是没有``$``前缀的
113         ''' </summary>
114         ''' <param name="sb"></param>
115         ''' <param name="getValue"></param>
116         ''' <param name="nullAsEmpty">只有当变量的值不为空值的时候才会进行替换,但是当<paramref name="nullAsEmpty"/>为真的时候会被强行替换为空字符串进行替换</param>
117         ''' <param name="escape"></param>
118         <Extension>
119         Public Sub Interpolate(ByRef sb As StringBuilder, getValue As Func(Of StringString),
120                                Optional nullAsEmpty As Boolean = False,
121                                Optional escape As Boolean = True)
122
123             Call sb.Replace("\$", VB_str)
124
125             For Each v$ In Regex _
126                 .Matches(sb.ToString, VariablePattern, RegexICSng) _
127                 .ToArray _
128                 .OrderByDescending(Function(s) s.Length) _
129                 .ToArray
130
131                 Dim value$ = getValue(Mid(v, 2))
132
133                 If value Is Nothing AndAlso nullAsEmpty Then
134                     value = ""
135                 End If
136
137                 ' 只对非空值进行替换
138                 If Not value Is Nothing Then
139                     Call sb.Replace(v, value)
140                 End If
141             Next
142
143             With sb
144                 ' 这个必须要转义
145                 .Replace(VB_str, "$")
146
147                 ' 根据需要来进行转义,对于Windows文件路径而言,不推荐转义
148                 ' 因为Windows的文件路径分隔符为\,很容易引起误解,例如C:\tsv会被误转义为C:<TAB>sv而导致错误
149                 If escape Then
150                     Call .Replace("\n", vbLf)
151                     Call .Replace("\t", vbTab)
152                 End If
153             End With
154         End Sub
155
156         <MethodImpl(MethodImplOptions.AggressiveInlining)>
157         <Extension>
158         Public Function Interpolate(expr$, table As Dictionary(Of StringString), Optional nullAsEmpty As Boolean = FalseAs String
159             Return expr.Interpolate(table.GetValue, nullAsEmpty)
160         End Function
161     End Module
162 End Namespace