@@ -74,11 +74,62 @@ fileprivate extension String {
7474 ///
7575 /// See the proposal SOAR-0001 for details.
7676 ///
77- /// In addition to replacing illegal characters with an underscores, also
77+ /// For example, the string `$nake…` would be returned as `_dollar_nake_x2026_`, because
78+ /// both the dollar and ellipsis sign are not valid characters in a Swift identifier.
79+ /// So, it replaces such characters with their html entity equivalents or unicode hex representation,
80+ /// in case it's not present in the `specialCharsMap`. It marks this replacement with `_` as a delimiter.
81+ ///
82+ /// In addition to replacing illegal characters, it also
7883 /// ensures that the identifier starts with a letter and not a number.
7984 var proposedSafeForSwiftCode : String {
80- // TODO: New logic proposed in SOAR-0001 goes here.
81- return " "
85+ guard !isEmpty else {
86+ return " _empty "
87+ }
88+
89+ let firstCharSet : CharacterSet = . letters. union ( . init( charactersIn: " _ " ) )
90+ let numbers : CharacterSet = . decimalDigits
91+ let otherCharSet : CharacterSet = . alphanumerics. union ( . init( charactersIn: " _ " ) )
92+
93+ var sanitizedScalars : [ Unicode . Scalar ] = [ ]
94+ for (index, scalar) in unicodeScalars. enumerated ( ) {
95+ let allowedSet = index == 0 ? firstCharSet : otherCharSet
96+ let outScalar : Unicode . Scalar
97+ if allowedSet. contains ( scalar) {
98+ outScalar = scalar
99+ } else if index == 0 && numbers. contains ( scalar) {
100+ sanitizedScalars. append ( " _ " )
101+ outScalar = scalar
102+ } else {
103+ sanitizedScalars. append ( " _ " )
104+ if let entityName = Self . specialCharsMap [ scalar] {
105+ for char in entityName. unicodeScalars {
106+ sanitizedScalars. append ( char)
107+ }
108+ } else {
109+ sanitizedScalars. append ( " x " )
110+ let hexString = String ( scalar. value, radix: 16 , uppercase: true )
111+ for char in hexString. unicodeScalars {
112+ sanitizedScalars. append ( char)
113+ }
114+ }
115+ sanitizedScalars. append ( " _ " )
116+ continue
117+ }
118+ sanitizedScalars. append ( outScalar)
119+ }
120+
121+ let validString = String ( UnicodeScalarView ( sanitizedScalars) )
122+
123+ //Special case for a single underscore.
124+ //We can't add it to the map as its a valid swift identifier in other cases.
125+ if validString == " _ " {
126+ return " _underscore_ "
127+ }
128+
129+ guard Self . keywords. contains ( validString) else {
130+ return validString
131+ }
132+ return " _ \( validString) "
82133 }
83134
84135 /// A list of Swift keywords.
@@ -138,62 +189,6 @@ fileprivate extension String {
138189 " true " ,
139190 " try " ,
140191 " throws " ,
141- " __FILE__ " ,
142- " __LINE__ " ,
143- " __COLUMN__ " ,
144- " __FUNCTION__ " ,
145- " __DSO_HANDLE__ " ,
146- " _ " ,
147- " ( " ,
148- " ) " ,
149- " { " ,
150- " } " ,
151- " [ " ,
152- " ] " ,
153- " < " ,
154- " > " ,
155- " . " ,
156- " . " ,
157- " , " ,
158- " ... " ,
159- " : " ,
160- " ; " ,
161- " = " ,
162- " @ " ,
163- " # " ,
164- " & " ,
165- " -> " ,
166- " ` " ,
167- " \\ " ,
168- " ! " ,
169- " ? " ,
170- " ? " ,
171- " \" " ,
172- " \' " ,
173- " \" \" \" " ,
174- " #keyPath " ,
175- " #line " ,
176- " #selector " ,
177- " #file " ,
178- " #fileID " ,
179- " #filePath " ,
180- " #column " ,
181- " #function " ,
182- " #dsohandle " ,
183- " #assert " ,
184- " #sourceLocation " ,
185- " #warning " ,
186- " #error " ,
187- " #if " ,
188- " #else " ,
189- " #elseif " ,
190- " #endif " ,
191- " #available " ,
192- " #unavailable " ,
193- " #fileLiteral " ,
194- " #imageLiteral " ,
195- " #colorLiteral " ,
196- " ) " ,
197192 " yield " ,
198193 " String " ,
199194 " Error " ,
@@ -205,4 +200,40 @@ fileprivate extension String {
205200 " Protocol " ,
206201 " await " ,
207202 ]
203+
204+ /// A map of ASCII printable characters to their HTML entity names. Used to reduce collisions in generated names.
205+ private static let specialCharsMap : [ Unicode . Scalar : String ] = [
206+ " " : " space " ,
207+ " ! " : " excl " ,
208+ " \" " : " quot " ,
209+ " # " : " num " ,
210+ " $ " : " dollar " ,
211+ " % " : " percnt " ,
212+ " & " : " amp " ,
213+ " ' " : " apos " ,
214+ " ( " : " lpar " ,
215+ " ) " : " rpar " ,
216+ " * " : " ast " ,
217+ " + " : " plus " ,
218+ " , " : " comma " ,
219+ " - " : " hyphen " ,
220+ " . " : " period " ,
221+ " / " : " sol " ,
222+ " : " : " colon " ,
223+ " ; " : " semi " ,
224+ " < " : " lt " ,
225+ " = " : " equals " ,
226+ " > " : " gt " ,
227+ " ? " : " quest " ,
228+ " @ " : " commat " ,
229+ " [ " : " lbrack " ,
230+ " \\ " : " bsol " ,
231+ " ] " : " rbrack " ,
232+ " ^ " : " hat " ,
233+ " ` " : " grave " ,
234+ " { " : " lcub " ,
235+ " | " : " verbar " ,
236+ " } " : " rcub " ,
237+ " ~ " : " tilde " ,
238+ ]
208239}
0 commit comments