@@ -88,80 +88,82 @@ private extension HTMLElement {
88
88
isVoid = elementType. isVoid
89
89
children = childs. prefix ( childs. count)
90
90
}
91
- let data : ElementData = parse_arguments ( context: context, elementType: elementType, children: children)
92
- var string : String = ( tag == " html " ? " <!DOCTYPE html> " : " " ) + " < " + tag + data . attributes + " > " + data . innerHTML
91
+ let ( attributes , innerHTML ) : ( String , String ) = parse_arguments ( context: context, elementType: elementType, children: children)
92
+ var string : String = ( tag == " html " ? " <!DOCTYPE html> " : " " ) + " < " + tag + attributes + " > " + innerHTML
93
93
if !isVoid {
94
94
string += " </ " + tag + " > "
95
95
}
96
96
return string
97
97
}
98
98
// MARK: Parse Arguments
99
- static func parse_arguments( context: some MacroExpansionContext , elementType: HTMLElementType , children: Slice < SyntaxChildren > ) -> ElementData {
100
- var attributes : [ String ] = [ ] , innerHTML : [ String ] = [ ]
99
+ static func parse_arguments( context: some MacroExpansionContext , elementType: HTMLElementType , children: Slice < SyntaxChildren > ) -> ( attributes : String , innerHTML : String ) {
100
+ var attributes : String = " " , innerHTML : String = " "
101
101
for element in children {
102
102
if let child: LabeledExprSyntax = element. labeled {
103
103
if var key: String = child. label? . text {
104
- if key == " dataType " { // HTMLDataRepresentation; we don't care
105
- } else if key == " attributes " {
106
- attributes. append ( contentsOf: parse_global_attributes ( context: context, elementType: elementType, array: child. expression. array!) )
104
+ if key == " attributes " {
105
+ for attribute in parse_global_attributes ( context: context, elementType: elementType, array: child. expression. array!. elements) {
106
+ attributes += attribute + " "
107
+ }
107
108
} else {
108
109
if key == " acceptCharset " {
109
110
key = " accept-charset "
110
111
}
111
- if let string: String = parse_attribute ( context: context, elementType: elementType, key: key, argument : child) {
112
- attributes. append ( key + ( string. isEmpty ? " " : " = \\ \" " + string + " \\ \" " ) )
112
+ if let string: String = parse_attribute ( context: context, elementType: elementType, key: key, expression : child. expression ) {
113
+ attributes += key + ( string. isEmpty ? " " : " = \\ \" " + string + " \\ \" " ) + " "
113
114
}
114
115
}
115
116
// inner html
116
117
} else if let inner_html: String = parse_inner_html ( context: context, elementType: elementType, child: child) {
117
- innerHTML. append ( inner_html)
118
+ innerHTML += inner_html
118
119
}
119
120
}
120
121
}
121
- return ElementData ( attributes: attributes, innerHTML: innerHTML)
122
+ attributes. removeLast ( )
123
+ return ( attributes, innerHTML)
122
124
}
123
125
// MARK: Parse Global Attributes
124
- static func parse_global_attributes( context: some MacroExpansionContext , elementType: HTMLElementType , array: ArrayExprSyntax ) -> [ String ] {
126
+ static func parse_global_attributes( context: some MacroExpansionContext , elementType: HTMLElementType , array: ArrayElementListSyntax ) -> [ String ] {
125
127
var keys : Set < String > = [ ] , attributes : [ String ] = [ ]
126
- for element in array. elements {
127
- let function : FunctionCallExprSyntax = element. expression. as ( FunctionCallExprSyntax . self ) !, key_argument : LabeledExprSyntax = function. arguments. first!, key_element : ExprSyntax = key_argument . expression
128
- var key : String = function. calledExpression. memberAccess!. declName. baseName. text, value : String ? = nil
128
+ for element in array {
129
+ let function : FunctionCallExprSyntax = element. expression. functionCall !, first_expression : ExprSyntax = function. arguments. first!. expression
130
+ var key : String = function. calledExpression. memberAccess!. declName. baseName. text, value : String ! = nil
129
131
switch key {
130
132
case " custom " , " data " :
131
- var ( literalValue, returnType) : ( String , LiteralReturnType ) = parse_literal_value ( context: context, elementType: elementType, key: key, expression: function. arguments. last!. expression) !
133
+ var returnType : LiteralReturnType = . string
134
+ ( value, returnType) = parse_literal_value ( context: context, elementType: elementType, key: key, expression: function. arguments. last!. expression) !
132
135
if returnType == . string {
133
- literalValue . escapeHTML ( escapeAttributes: true )
136
+ value . escapeHTML ( escapeAttributes: true )
134
137
}
135
- value = literalValue
136
138
if key == " custom " {
137
- key = key_element . stringLiteral!. string
139
+ key = first_expression . stringLiteral!. string
138
140
} else {
139
- key += " - \( key_element . stringLiteral!. string) "
141
+ key += " - \( first_expression . stringLiteral!. string) "
140
142
}
141
143
break
142
144
case " event " :
143
- key = " on " + key_element . memberAccess!. declName. baseName. text
144
- if var result : ( String , LiteralReturnType ) = parse_literal_value ( context: context, elementType: elementType, key: key, expression: function. arguments. last!. expression) {
145
- if result . 1 == . string {
146
- result . 0 . escapeHTML ( escapeAttributes: true )
145
+ key = " on " + first_expression . memberAccess!. declName. baseName. text
146
+ if var ( string , returnType ) : ( String , LiteralReturnType ) = parse_literal_value ( context: context, elementType: elementType, key: key, expression: function. arguments. last!. expression) {
147
+ if returnType == . string {
148
+ string . escapeHTML ( escapeAttributes: true )
147
149
}
148
- value = result . 0
150
+ value = string
149
151
} else {
150
152
unallowed_expression ( context: context, node: function. arguments. last!)
151
153
return [ ]
152
154
}
153
155
break
154
156
default :
155
- if let string: String = parse_attribute ( context: context, elementType: elementType, key: key, argument : key_argument ) {
157
+ if let string: String = parse_attribute ( context: context, elementType: elementType, key: key, expression : first_expression ) {
156
158
value = string
157
159
}
158
160
break
159
161
}
160
162
if key. contains ( " " ) {
161
- context. diagnose ( Diagnostic ( node: key_element , message: DiagnosticMsg ( id: " spacesNotAllowedInAttributeDeclaration " , message: " Spaces are not allowed in attribute declaration. " ) ) )
163
+ context. diagnose ( Diagnostic ( node: first_expression , message: DiagnosticMsg ( id: " spacesNotAllowedInAttributeDeclaration " , message: " Spaces are not allowed in attribute declaration. " ) ) )
162
164
} else if let value: String = value {
163
165
if keys. contains ( key) {
164
- context. diagnose ( Diagnostic ( node: key_element , message: DiagnosticMsg ( id: " globalAttributeAlreadyDefined " , message: " Global attribute \" " + key + " \" is already defined. " ) ) )
166
+ context. diagnose ( Diagnostic ( node: first_expression , message: DiagnosticMsg ( id: " globalAttributeAlreadyDefined " , message: " Global attribute \" " + key + " \" is already defined. " ) ) )
165
167
} else {
166
168
attributes. append ( key + ( value. isEmpty ? " " : " = \\ \" " + value + " \\ \" " ) )
167
169
keys. insert ( key)
@@ -196,15 +198,6 @@ private extension HTMLElement {
196
198
] )
197
199
] ) )
198
200
}
199
-
200
- struct ElementData {
201
- let attributes : String , innerHTML : String
202
-
203
- init ( attributes: [ String ] , innerHTML: [ String ] ) {
204
- self . attributes = attributes. isEmpty ? " " : " " + attributes. joined ( separator: " " )
205
- self . innerHTML = innerHTML. joined ( )
206
- }
207
- }
208
201
209
202
static func enumName( elementType: HTMLElementType , key: String ) -> String {
210
203
switch elementType. rawValue + key {
@@ -217,24 +210,22 @@ private extension HTMLElement {
217
210
}
218
211
219
212
// MARK: Parse Attribute
220
- static func parse_attribute( context: some MacroExpansionContext , elementType: HTMLElementType , key: String , argument: LabeledExprSyntax ) -> String ? {
221
- let expression : ExprSyntax = argument. expression
222
- if var result: ( String , LiteralReturnType ) = parse_literal_value ( context: context, elementType: elementType, key: key, expression: expression) {
223
- switch result. 1 {
224
- case . boolean: return result. 0 . elementsEqual ( " true " ) ? " " : nil
225
- case . string:
226
- result. 0 . escapeHTML ( escapeAttributes: true )
227
- return result. 0
228
- case . interpolation: return result. 0
213
+ static func parse_attribute( context: some MacroExpansionContext , elementType: HTMLElementType , key: String , expression: ExprSyntax ) -> String ? {
214
+ if var ( string, returnType) : ( String , LiteralReturnType ) = parse_literal_value ( context: context, elementType: elementType, key: key, expression: expression) {
215
+ switch returnType {
216
+ case . boolean: return string. elementsEqual ( " true " ) ? " " : nil
217
+ case . string, . enumCase:
218
+ if returnType == . string && string. isEmpty {
219
+ return nil
220
+ }
221
+ string. escapeHTML ( escapeAttributes: true )
222
+ return string
223
+ case . interpolation: return string
229
224
}
230
225
}
231
- func member( _ value: String ) -> String {
232
- var string : String = String ( value [ value. index ( after: value. startIndex) ... ] )
233
- string = HTMLElementAttribute . Extra. htmlValue ( enumName: enumName ( elementType: elementType, key: key) , for: string)
234
- return string
235
- }
236
- if let function: FunctionCallExprSyntax = expression. as ( FunctionCallExprSyntax . self) {
237
- return member ( " \( function) " )
226
+ if let function: FunctionCallExprSyntax = expression. functionCall {
227
+ let string : String = " \( function) "
228
+ return HTMLElementAttribute . Extra. htmlValue ( enumName: enumName ( elementType: elementType, key: key) , for: String ( string [ string. index ( after: string. startIndex) ... ] ) )
238
229
}
239
230
return nil
240
231
}
@@ -247,13 +238,13 @@ private extension HTMLElement {
247
238
if let string: String = expression. stringLiteral? . string ?? expression. integerLiteral? . literal. text ?? expression. floatLiteral? . literal. text {
248
239
return ( string, . string)
249
240
}
250
- if let function: FunctionCallExprSyntax = expression. as ( FunctionCallExprSyntax . self ) {
241
+ if let function: FunctionCallExprSyntax = expression. functionCall {
251
242
switch key {
252
243
case " height " , " width " :
253
244
var value : String = " \( function) "
254
245
value = String ( value [ value. index ( after: value. startIndex) ... ] )
255
246
value = HTMLElementAttribute . Extra. htmlValue ( enumName: enumName ( elementType: elementType, key: key) , for: value)
256
- return ( value, . string )
247
+ return ( value, . enumCase )
257
248
default :
258
249
if function. calledExpression. as ( DeclReferenceExprSyntax . self) ? . baseName. text == " StaticString " {
259
250
return ( function. arguments. first!. expression. stringLiteral!. string, . string)
@@ -262,15 +253,13 @@ private extension HTMLElement {
262
253
}
263
254
}
264
255
if let member: MemberAccessExprSyntax = expression. memberAccess {
265
- let decl : String = member. declName. baseName. text
266
256
if let _: ExprSyntax = member. base {
267
257
return ( " \( member) " , . interpolation)
268
- } else {
269
- return ( HTMLElementAttribute . Extra. htmlValue ( enumName: enumName ( elementType: elementType, key: key) , for: decl) , . string)
270
258
}
259
+ return ( HTMLElementAttribute . Extra. htmlValue ( enumName: enumName ( elementType: elementType, key: key) , for: member. declName. baseName. text) , . enumCase)
271
260
}
272
261
if let array: ArrayExprSyntax = expression. array {
273
- let separator : String
262
+ let separator : Character , separator_string : String
274
263
switch key {
275
264
case " accept " , " coords " , " exportparts " , " imagesizes " , " imagesrcset " , " sizes " , " srcset " :
276
265
separator = " , "
@@ -279,22 +268,30 @@ private extension HTMLElement {
279
268
separator = " "
280
269
break
281
270
}
282
- return ( array. elements. compactMap ( {
283
- if let string: String = $0. expression. stringLiteral? . string {
271
+ separator_string = String ( separator)
272
+ var result : String = " "
273
+ for element in array. elements {
274
+ if let string: String = element. expression. stringLiteral? . string {
284
275
if string. contains ( separator) {
285
- context. diagnose ( Diagnostic ( node: $0 . expression, message: DiagnosticMsg ( id: " characterNotAllowedInDeclaration " , message: " Character \" " + separator + " \" is not allowed when declaring values for \" " + key + " \" . " ) ) )
276
+ context. diagnose ( Diagnostic ( node: element . expression, message: DiagnosticMsg ( id: " characterNotAllowedInDeclaration " , message: " Character \" \( separator) \" is not allowed when declaring values for \" " + key + " \" . " ) ) )
286
277
return nil
287
278
}
288
- return string
279
+ result += string + separator_string
289
280
}
290
- if let string: String = $0 . expression. integerLiteral? . literal. text ?? $0 . expression. floatLiteral? . literal. text {
291
- return string
281
+ if let string: String = element . expression. integerLiteral? . literal. text ?? element . expression. floatLiteral? . literal. text {
282
+ result += string + separator_string
292
283
}
293
- if let string: String = $0 . expression. memberAccess? . declName. baseName. text {
294
- return HTMLElementAttribute . Extra. htmlValue ( enumName: enumName ( elementType: elementType, key: key) , for: string)
284
+ if let string: String = element . expression. memberAccess? . declName. baseName. text {
285
+ result += HTMLElementAttribute . Extra. htmlValue ( enumName: enumName ( elementType: elementType, key: key) , for: string) + separator_string
295
286
}
296
- return nil
297
- } ) . joined ( separator: separator) , . string)
287
+ }
288
+ if !result. isEmpty {
289
+ result. removeLast ( )
290
+ }
291
+ return ( result, . string)
292
+ }
293
+ if let _: DeclReferenceExprSyntax = expression. as ( DeclReferenceExprSyntax . self) {
294
+ context. diagnose ( Diagnostic ( node: expression, message: DiagnosticMsg ( id: " runtimeValueNotAllowed " , message: " Runtime value not allowed here. " ) ) )
298
295
}
299
296
return nil
300
297
}
@@ -362,7 +359,7 @@ private extension HTMLElement {
362
359
}
363
360
364
361
enum LiteralReturnType {
365
- case boolean, string, interpolation
362
+ case boolean, string, enumCase , interpolation
366
363
}
367
364
368
365
// MARK: HTMLElementType
@@ -530,6 +527,7 @@ extension SyntaxProtocol {
530
527
var array : ArrayExprSyntax ? { self . as ( ArrayExprSyntax . self) }
531
528
var memberAccess : MemberAccessExprSyntax ? { self . as ( MemberAccessExprSyntax . self) }
532
529
var macroExpansion : MacroExpansionExprSyntax ? { self . as ( MacroExpansionExprSyntax . self) }
530
+ var functionCall : FunctionCallExprSyntax ? { self . as ( FunctionCallExprSyntax . self) }
533
531
}
534
532
extension SyntaxChildren . Element {
535
533
var labeled : LabeledExprSyntax ? { self . as ( LabeledExprSyntax . self) }
0 commit comments