@@ -88,80 +88,82 @@ private extension HTMLElement {
8888 isVoid = elementType. isVoid
8989 children = childs. prefix ( childs. count)
9090 }
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
9393 if !isVoid {
9494 string += " </ " + tag + " > "
9595 }
9696 return string
9797 }
9898 // 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 = " "
101101 for element in children {
102102 if let child: LabeledExprSyntax = element. labeled {
103103 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+ }
107108 } else {
108109 if key == " acceptCharset " {
109110 key = " accept-charset "
110111 }
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 + " \\ \" " ) + " "
113114 }
114115 }
115116 // inner html
116117 } else if let inner_html: String = parse_inner_html ( context: context, elementType: elementType, child: child) {
117- innerHTML. append ( inner_html)
118+ innerHTML += inner_html
118119 }
119120 }
120121 }
121- return ElementData ( attributes: attributes, innerHTML: innerHTML)
122+ attributes. removeLast ( )
123+ return ( attributes, innerHTML)
122124 }
123125 // 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 ] {
125127 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
129131 switch key {
130132 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) !
132135 if returnType == . string {
133- literalValue . escapeHTML ( escapeAttributes: true )
136+ value . escapeHTML ( escapeAttributes: true )
134137 }
135- value = literalValue
136138 if key == " custom " {
137- key = key_element . stringLiteral!. string
139+ key = first_expression . stringLiteral!. string
138140 } else {
139- key += " - \( key_element . stringLiteral!. string) "
141+ key += " - \( first_expression . stringLiteral!. string) "
140142 }
141143 break
142144 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 )
147149 }
148- value = result . 0
150+ value = string
149151 } else {
150152 unallowed_expression ( context: context, node: function. arguments. last!)
151153 return [ ]
152154 }
153155 break
154156 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 ) {
156158 value = string
157159 }
158160 break
159161 }
160162 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. " ) ) )
162164 } else if let value: String = value {
163165 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. " ) ) )
165167 } else {
166168 attributes. append ( key + ( value. isEmpty ? " " : " = \\ \" " + value + " \\ \" " ) )
167169 keys. insert ( key)
@@ -196,15 +198,6 @@ private extension HTMLElement {
196198 ] )
197199 ] ) )
198200 }
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- }
208201
209202 static func enumName( elementType: HTMLElementType , key: String ) -> String {
210203 switch elementType. rawValue + key {
@@ -217,24 +210,22 @@ private extension HTMLElement {
217210 }
218211
219212 // 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
229224 }
230225 }
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) ... ] ) )
238229 }
239230 return nil
240231 }
@@ -247,13 +238,13 @@ private extension HTMLElement {
247238 if let string: String = expression. stringLiteral? . string ?? expression. integerLiteral? . literal. text ?? expression. floatLiteral? . literal. text {
248239 return ( string, . string)
249240 }
250- if let function: FunctionCallExprSyntax = expression. as ( FunctionCallExprSyntax . self ) {
241+ if let function: FunctionCallExprSyntax = expression. functionCall {
251242 switch key {
252243 case " height " , " width " :
253244 var value : String = " \( function) "
254245 value = String ( value [ value. index ( after: value. startIndex) ... ] )
255246 value = HTMLElementAttribute . Extra. htmlValue ( enumName: enumName ( elementType: elementType, key: key) , for: value)
256- return ( value, . string )
247+ return ( value, . enumCase )
257248 default :
258249 if function. calledExpression. as ( DeclReferenceExprSyntax . self) ? . baseName. text == " StaticString " {
259250 return ( function. arguments. first!. expression. stringLiteral!. string, . string)
@@ -262,15 +253,13 @@ private extension HTMLElement {
262253 }
263254 }
264255 if let member: MemberAccessExprSyntax = expression. memberAccess {
265- let decl : String = member. declName. baseName. text
266256 if let _: ExprSyntax = member. base {
267257 return ( " \( member) " , . interpolation)
268- } else {
269- return ( HTMLElementAttribute . Extra. htmlValue ( enumName: enumName ( elementType: elementType, key: key) , for: decl) , . string)
270258 }
259+ return ( HTMLElementAttribute . Extra. htmlValue ( enumName: enumName ( elementType: elementType, key: key) , for: member. declName. baseName. text) , . enumCase)
271260 }
272261 if let array: ArrayExprSyntax = expression. array {
273- let separator : String
262+ let separator : Character , separator_string : String
274263 switch key {
275264 case " accept " , " coords " , " exportparts " , " imagesizes " , " imagesrcset " , " sizes " , " srcset " :
276265 separator = " , "
@@ -279,22 +268,30 @@ private extension HTMLElement {
279268 separator = " "
280269 break
281270 }
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 {
284275 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 + " \" . " ) ) )
286277 return nil
287278 }
288- return string
279+ result += string + separator_string
289280 }
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
292283 }
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
295286 }
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. " ) ) )
298295 }
299296 return nil
300297 }
@@ -362,7 +359,7 @@ private extension HTMLElement {
362359}
363360
364361enum LiteralReturnType {
365- case boolean, string, interpolation
362+ case boolean, string, enumCase , interpolation
366363}
367364
368365// MARK: HTMLElementType
@@ -530,6 +527,7 @@ extension SyntaxProtocol {
530527 var array : ArrayExprSyntax ? { self . as ( ArrayExprSyntax . self) }
531528 var memberAccess : MemberAccessExprSyntax ? { self . as ( MemberAccessExprSyntax . self) }
532529 var macroExpansion : MacroExpansionExprSyntax ? { self . as ( MacroExpansionExprSyntax . self) }
530+ var functionCall : FunctionCallExprSyntax ? { self . as ( FunctionCallExprSyntax . self) }
533531}
534532extension SyntaxChildren . Element {
535533 var labeled : LabeledExprSyntax ? { self . as ( LabeledExprSyntax . self) }
0 commit comments