@@ -86,82 +86,113 @@ impl Parser {
8686 let total_length = token. length ( ) . unwrap_or ( 0 ) ;
8787 let span = Span :: new ( start_pos, total_length) ;
8888
89- // Parse the tag name and any assignments
90- let mut bits = content. split_whitespace ( ) ;
91- let tag_name = bits. next ( ) . unwrap_or_default ( ) . to_string ( ) ;
92- let bits_vec: Vec < String > = bits. map ( |s| s. to_string ( ) ) . collect ( ) ;
93-
94- // Check for assignment syntax
95- let mut assignments = Vec :: new ( ) ;
96- let mut assignment = None ;
97- if bits_vec. len ( ) > 2 && bits_vec[ 1 ] == "as" {
98- assignment = Some ( bits_vec[ 2 ] . clone ( ) ) ;
99- assignments. push ( Assignment {
100- target : bits_vec[ 2 ] . clone ( ) ,
101- value : bits_vec[ 3 ..] . join ( " " ) ,
102- } ) ;
103- }
89+ let bits: Vec < String > = content. split_whitespace ( ) . map ( String :: from) . collect ( ) ;
90+ let tag_name = bits. first ( ) . ok_or ( ParserError :: EmptyTag ) ?. clone ( ) ;
10491
10592 let tag = Tag {
10693 name : tag_name. clone ( ) ,
107- bits : content . split_whitespace ( ) . map ( |s| s . to_string ( ) ) . collect ( ) ,
94+ bits : bits . clone ( ) ,
10895 span,
10996 tag_span : span,
110- assignment,
97+ assignment : None ,
11198 } ;
11299
113- // Check if this is a closing tag
114- if tag_name. starts_with ( "end" ) {
115- return Ok ( Node :: Block ( Block :: Closing { tag } ) ) ;
116- }
117-
118- // Load tag specs
119100 let specs = TagSpec :: load_builtin_specs ( ) ?;
120101 let spec = match specs. get ( & tag_name) {
121102 Some ( spec) => spec,
122103 None => return Ok ( Node :: Block ( Block :: Tag { tag } ) ) ,
123104 } ;
124105
125- match spec. tag_type {
106+ let block = match spec. tag_type {
126107 TagType :: Block => {
127108 let mut nodes = Vec :: new ( ) ;
128-
129- // Parse child nodes until we find the closing tag
130- while let Ok ( node) = self . next_node ( ) {
131- if let Node :: Block ( Block :: Closing { tag : closing_tag } ) = & node {
132- if let Some ( expected_closing) = & spec. closing {
133- if closing_tag. name == * expected_closing {
134- return Ok ( Node :: Block ( Block :: Block {
135- tag,
136- nodes,
137- closing : Some ( Box :: new ( Block :: Closing {
138- tag : closing_tag. clone ( ) ,
139- } ) ) ,
140- assignments : Some ( assignments) ,
141- } ) ) ;
109+ let mut closing = None ;
110+
111+ while !self . is_at_end ( ) {
112+ match self . next_node ( ) {
113+ Ok ( Node :: Block ( Block :: Tag { tag } ) ) => {
114+ if let Some ( expected_closing) = & spec. closing {
115+ if tag. name == * expected_closing {
116+ closing = Some ( Box :: new ( Block :: Closing { tag } ) ) ;
117+ break ;
118+ }
119+ }
120+ // If we get here, either there was no expected closing tag or it didn't match
121+ if let Some ( branches) = & spec. branches {
122+ if branches. iter ( ) . any ( |b| b. name == tag. name ) {
123+ let mut branch_tag = tag. clone ( ) ;
124+ let mut branch_nodes = Vec :: new ( ) ;
125+ let mut found_closing = false ;
126+ while let Ok ( node) = self . next_node ( ) {
127+ match & node {
128+ Node :: Block ( Block :: Tag { tag : next_tag } ) => {
129+ if let Some ( expected_closing) = & spec. closing {
130+ if next_tag. name == * expected_closing {
131+ // Found the closing tag
132+ nodes. push ( Node :: Block ( Block :: Branch {
133+ tag : branch_tag. clone ( ) ,
134+ nodes : branch_nodes. clone ( ) ,
135+ } ) ) ;
136+ closing = Some ( Box :: new ( Block :: Closing { tag : next_tag. clone ( ) } ) ) ;
137+ found_closing = true ;
138+ break ;
139+ }
140+ }
141+ // Check if this is another branch tag
142+ if branches. iter ( ) . any ( |b| b. name == next_tag. name ) {
143+ // Push the current branch and start a new one
144+ nodes. push ( Node :: Block ( Block :: Branch {
145+ tag : branch_tag. clone ( ) ,
146+ nodes : branch_nodes. clone ( ) ,
147+ } ) ) ;
148+ branch_nodes = Vec :: new ( ) ;
149+ branch_tag = next_tag. clone ( ) ;
150+ continue ;
151+ }
152+ branch_nodes. push ( node) ;
153+ }
154+ _ => branch_nodes. push ( node) ,
155+ }
156+ }
157+ if !found_closing {
158+ // Push the last branch if we didn't find a closing tag
159+ nodes. push ( Node :: Block ( Block :: Branch {
160+ tag : branch_tag,
161+ nodes : branch_nodes,
162+ } ) ) ;
163+ }
164+ if found_closing {
165+ break ;
166+ }
167+ continue ;
168+ }
142169 }
170+ nodes. push ( Node :: Block ( Block :: Tag { tag } ) ) ;
171+ }
172+ Ok ( node) => nodes. push ( node) ,
173+ Err ( e) => {
174+ self . errors . push ( e) ;
175+ break ;
143176 }
144177 }
145- nodes. push ( node) ;
146178 }
147179
148- // Add error for unclosed tag
149- self . errors . push ( ParserError :: Ast ( AstError :: UnclosedTag ( tag_name. clone ( ) ) ) ) ;
150-
151- Ok ( Node :: Block ( Block :: Block {
180+ Block :: Block {
152181 tag,
153182 nodes,
154- closing : None ,
155- assignments : Some ( assignments ) ,
156- } ) )
183+ closing,
184+ assignments : None ,
185+ }
157186 }
158- TagType :: Tag => Ok ( Node :: Block ( Block :: Tag { tag } ) ) ,
159- TagType :: Variable => Ok ( Node :: Block ( Block :: Variable { tag } ) ) ,
187+ TagType :: Tag => Block :: Tag { tag } ,
188+ TagType :: Variable => Block :: Variable { tag } ,
160189 TagType :: Inclusion => {
161- let template_name = bits_vec . get ( 1 ) . cloned ( ) . unwrap_or_default ( ) ;
162- Ok ( Node :: Block ( Block :: Inclusion { tag, template_name } ) )
190+ let template_name = bits . get ( 1 ) . cloned ( ) . unwrap_or_default ( ) ;
191+ Block :: Inclusion { tag, template_name }
163192 }
164- }
193+ } ;
194+
195+ Ok ( Node :: Block ( block) )
165196 }
166197
167198 fn parse_django_variable ( & mut self , content : & str ) -> Result < Node , ParserError > {
@@ -356,6 +387,8 @@ pub enum ParserError {
356387 ErrorSignal ( Signal ) ,
357388 #[ error( "{0}" ) ]
358389 Other ( #[ from] anyhow:: Error ) ,
390+ #[ error( "empty tag" ) ]
391+ EmptyTag ,
359392}
360393
361394impl ParserError {
@@ -544,7 +577,9 @@ mod tests {
544577 let ( ast, errors) = parser. parse ( ) . unwrap ( ) ;
545578 insta:: assert_yaml_snapshot!( ast) ;
546579 assert_eq ! ( errors. len( ) , 1 ) ;
547- assert ! ( matches!( & errors[ 0 ] , ParserError :: Ast ( AstError :: UnclosedTag ( tag) ) if tag == "if" ) ) ;
580+ assert ! (
581+ matches!( & errors[ 0 ] , ParserError :: Ast ( AstError :: UnclosedTag ( tag) ) if tag == "if" )
582+ ) ;
548583 }
549584 #[ test]
550585 fn test_parse_unclosed_django_for ( ) {
@@ -554,7 +589,9 @@ mod tests {
554589 let ( ast, errors) = parser. parse ( ) . unwrap ( ) ;
555590 insta:: assert_yaml_snapshot!( ast) ;
556591 assert_eq ! ( errors. len( ) , 1 ) ;
557- assert ! ( matches!( & errors[ 0 ] , ParserError :: Ast ( AstError :: UnclosedTag ( tag) ) if tag == "for" ) ) ;
592+ assert ! (
593+ matches!( & errors[ 0 ] , ParserError :: Ast ( AstError :: UnclosedTag ( tag) ) if tag == "for" )
594+ ) ;
558595 }
559596 #[ test]
560597 fn test_parse_unclosed_script ( ) {
@@ -593,7 +630,9 @@ mod tests {
593630 let ( ast, errors) = parser. parse ( ) . unwrap ( ) ;
594631 insta:: assert_yaml_snapshot!( ast) ;
595632 assert_eq ! ( errors. len( ) , 1 ) ;
596- assert ! ( matches!( & errors[ 0 ] , ParserError :: Ast ( AstError :: UnclosedTag ( tag) ) if tag == "if" ) ) ;
633+ assert ! (
634+ matches!( & errors[ 0 ] , ParserError :: Ast ( AstError :: UnclosedTag ( tag) ) if tag == "if" )
635+ ) ;
597636 }
598637 }
599638
0 commit comments