summaryrefslogtreecommitdiff
path: root/crimtag/src/parser.rs
diff options
context:
space:
mode:
authoreichlan <eichlan@jotunn.office.penny-arcade.com>2026-06-23 15:31:51 -0700
committereichlan <eichlan@jotunn.office.penny-arcade.com>2026-06-23 15:31:51 -0700
commit062048ef6e3e060bcf841ea2ec92e026f09d8da0 (patch)
tree22f7dad062b16f25fcfa1a28af13ac2937348ef0 /crimtag/src/parser.rs
parent7c299c86bbac76ec2ac199d68eb4dda093a64e3a (diff)
downloadcrimtag-062048ef6e3e060bcf841ea2ec92e026f09d8da0.tar.gz
crimtag-062048ef6e3e060bcf841ea2ec92e026f09d8da0.tar.bz2
crimtag-062048ef6e3e060bcf841ea2ec92e026f09d8da0.tar.xz
crimtag-062048ef6e3e060bcf841ea2ec92e026f09d8da0.zip
Roorganized into workspace and packages.
This is to accomidate a new proc_macro package, which can't have anything else in it.
Diffstat (limited to 'crimtag/src/parser.rs')
-rw-r--r--crimtag/src/parser.rs785
1 files changed, 785 insertions, 0 deletions
diff --git a/crimtag/src/parser.rs b/crimtag/src/parser.rs
new file mode 100644
index 0000000..4f7ff6a
--- /dev/null
+++ b/crimtag/src/parser.rs
@@ -0,0 +1,785 @@
1use crate::lexer::*;
2use crate::*;
3
4struct Context<'a> {
5 cur: [Option<Symbol<'a>>;2],
6 icur: usize,
7 ll: Lexer<'a>,
8 source: usize,
9}
10
11impl<'a> Context<'a> {
12 pub fn new( mut ll: Lexer<'a>, source: usize ) -> Self {
13 let cur = [ll.next(), ll.next()];
14 //println!(" - cur: {:?}, peek: {:?}", cur[0], cur[1] );
15 Self {
16 cur,
17 icur: 0,
18 ll,
19 source,
20 }
21 }
22
23 pub fn next(&mut self) -> Option<Symbol<'a>> {
24 self.cur[self.icur] = self.ll.next();
25 self.icur = (self.icur+1)%2;
26 //println!(" - cur: {:?}, peek: {:?}", self.cur(), self.peek() );
27 self.cur[self.icur]
28 }
29
30 pub fn cur(&self) -> Option<Symbol<'a>> {
31 self.cur[self.icur]
32 }
33
34 pub fn peek(&self) -> Option<Symbol<'a>> {
35 self.cur[(self.icur+1)%2]
36 }
37
38 /*
39 pub fn check_unwrapped<T: Fn( &SymbolType, &SymbolType ) -> bool>(&self, context: &str, f: T) -> CrimResult<bool> {
40 if self.cur().is_none() || self.peek().is_none() {
41 Err(CrimError::eos( context ))
42 } else {
43 Ok(f( self.cur().unwrap().symbol(), self.peek().unwrap().symbol()))
44 }
45 }
46 */
47
48 pub fn source(&self) -> usize {
49 self.source
50 }
51}
52
53trait SymbolHelper {
54 fn is<T: Fn( &SymbolType ) -> bool>(&self, context: &str, f: T ) -> CrimResult<bool>;
55 #[allow(dead_code)]
56 fn is_valid_tag_name(&self) -> CrimResult<bool>;
57}
58
59impl<'a> SymbolHelper for Option<Symbol<'a>> {
60 fn is<T: Fn( &SymbolType ) -> bool>(&self, context: &str, f: T ) -> CrimResult<bool> {
61 if let Some(st) = self {
62 Ok(f( &st.symbol() ))
63 } else {
64 Err(CrimError::eos( context ))
65 }
66 }
67
68 fn is_valid_tag_name(&self) -> CrimResult<bool> {
69 if let Some(st) = self {
70 Ok(match st.symbol() {
71 SymbolType::Token(_) |
72 SymbolType::View |
73 SymbolType::Output |
74 SymbolType::Loop |
75 SymbolType::If |
76 SymbolType::ElIf |
77 SymbolType::Else |
78 SymbolType::Show => true,
79 _ => false
80 })
81 } else {
82 Err(CrimError::parse(
83 Position::none(),
84 "Unexpeceted end of stream.".to_string()
85 ))
86 }
87 }
88}
89
90pub struct Parser {
91}
92
93/**
94 * input: input complete_tag
95 * | input text
96 * |
97 * ;
98 *
99 * tag: unary_tag
100 * | multinary_open_tag
101 * | multinary_mid_tag
102 * | multinary_close_tag
103 * ;
104 *
105 * unary_tag: '[|' tag_guts '|]'
106 * ;
107 *
108 * | '[|' tag_guts '|>'
109 * | '<|' tag_guts '|>'
110 * | '<|' tag_guts '|]'
111 * ;
112 *
113 * tag_pair: open_tag tag_body close_tag
114 * ;
115 *
116 * tag_body: tag_body tag
117 * | tag_body tag_pair
118 * | tag_body text
119 * |
120 * ;
121 *
122 * open_tag: '[|' tag_guts '|>'
123 * ;
124 *
125 * close_tag: '<|' token '|]'
126 * ;
127 *
128 * tag_guts: 'view' literal props
129 * | 'section' literal
130 * | 'output' literal
131 * ;
132 *
133 * literal: '"' [^"]* '"'
134 * ;
135 *
136 * props: props token '=' literal
137 * |
138 * ;
139 *
140 * disambiguate (tm):
141 *
142 * input: input tag
143 * | input text
144 * |
145 * ;
146 *
147 * tag: open_tag_base unary_tag
148 * | open_tag_base binary_tag
149 * ;
150 *
151 * open_tag_base: '[|' tag_guts
152 * ;
153 *
154 * unary_tag: '|]'
155 * ;
156 *
157 * binary_tag: '|>' tag_body close_tag
158 * ;
159 *
160 * tag_body: tag_body tag
161 * | tag_body text
162 * |
163 * ;
164 *
165 * close_tag: '<|' token '|]'
166 * ;
167 *
168 * tag_guts: 'view' literal props
169 * | 'section' literal
170 * | 'output' literal
171 * ;
172 *
173 * literal: '"' [^"]* '"'
174 * ;
175 *
176 * props: props token '=' literal
177 * |
178 * ;
179 */
180impl Parser {
181 pub fn new() -> Self {
182 Self {
183 }
184 }
185
186 fn lex_error<T>(&self, error: &Symbol) -> CrimResult<T> {
187 if let SymbolType::Error{what} = error.symbol() {
188 Err(CrimError::parse( *error.start(),
189 format!("What: {:?}", *what)
190 ))
191 } else {
192 Err(CrimError::parse( Position::none(), "Not an error?".into()))
193 }
194 }
195
196 pub fn parse(&self, src: &str, source: usize ) -> CrimResult<Vec<View>> {
197 let ll = Lexer::new( src );
198 let mut ctx = Context::new( ll, source );
199
200 // Parse the root of the file, the input context
201 self.p_input( &mut ctx )
202 }
203
204 fn p_input(&self, ctx: &mut Context ) -> CrimResult<Vec<View>> {
205 let mut tags = Vec::new();
206 loop {
207 if ctx.cur().is_none() {
208 break;
209 }
210 match ctx.cur().unwrap().symbol() {
211 SymbolType::Text(_) => { /* Skip top level text */ }
212 SymbolType::StartFlat => {
213 let tb = self.parse_tag( ctx )?;
214 let tb = self.parse_tag_set( ctx, tb )?;
215 tags.push( tb );
216 }
217 SymbolType::Error{..} => {
218 self.lex_error( &ctx.cur().unwrap() )?;
219 }
220 _ => {
221 return Err(CrimError::parse(
222 *ctx.cur().unwrap().start(),
223 format!("Unexpected symbol {:?} found looking for tags.", ctx.cur().unwrap().symbol() )
224 ));
225 }
226 }
227 ctx.next();
228 }
229
230 let mut views = Vec::new();
231 for tag in tags {
232 let start_pos = tag.start_pos();
233 let name = tag.name().clone();
234 if tag.is_name(&SymbolType::View) &&
235 let BuildResult::View(view) = tag.build( ctx )? {
236 views.push( view );
237 } else {
238 return Err(CrimError::parse(
239 start_pos,
240 format!("Expected view at root, found {:?}", name )
241 ));
242 }
243 }
244 Ok(views)
245 }
246
247 fn parse_tag<'a>(&self, ctx: &mut Context<'a>) -> CrimResult<TagBuilder<'a>> {
248 let start_sym = ctx.cur().unwrap();
249 let mut tb = TagBuilder::new(&start_sym);
250
251 if ctx.next().is_some() {
252 match ctx.cur().unwrap().symbol() {
253 SymbolType::View | SymbolType::Show | SymbolType::Loop |
254 SymbolType::If | SymbolType::ElIf | SymbolType::Else |
255 SymbolType::Output => {
256 let name_sym = ctx.cur().unwrap();
257 ctx.next();
258 tb.set_name( name_sym );
259 }
260 _ => {
261 return Err(CrimError::parse(
262 *ctx.cur().unwrap().start(),
263 "Unexpected symbol".to_string()
264 ));
265 }
266 }
267 } else {
268 return Err(CrimError::eos("tag type"));
269 }
270
271 if tb.can_have_params() || tb.can_have_expr() {
272 self.parse_tag_params( ctx, &mut tb )?;
273 }
274 if tb.can_have_props() {
275 self.parse_tag_props( ctx, &mut tb )?;
276 }
277
278 if let Some(end_sym) = ctx.cur() {
279 match end_sym.symbol() {
280 SymbolType::EndPoint | SymbolType::EndFlat => {
281 tb.set_end( &end_sym )?;
282 }
283 _ => {
284 return Err(CrimError::parse(
285 *end_sym.start(),
286 format!("Unexpected symbol {:?} looking for end of tag.", end_sym.symbol())
287 ));
288 }
289 }
290 }
291
292 ctx.next();
293
294 Ok(tb)
295 }
296
297 fn parse_tag_set<'a>(&self, ctx: &mut Context<'a>, mut base: TagBuilder<'a>) -> CrimResult<TagBuilder<'a>> {
298 if base.is_unary() {
299 return Ok(base);
300 }
301
302 loop {
303 if ctx.cur().is_none() {
304 return Err(CrimError::eos(format!("close tag for {:?}", base.name()).as_str()));
305 }
306 match ctx.cur().unwrap().symbol() {
307 SymbolType::Text(s) => {
308 base.add_child(Entry::Token(Token::Text(s.to_string())));
309 ctx.next();
310 }
311 SymbolType::StartFlat | SymbolType::StartPoint => {
312 let tb = self.parse_tag( ctx )?;
313
314 match tb.tag_type() {
315 TagType::MultinaryOpen => {
316 base.add_child(Entry::TagBuilder(self.parse_tag_set( ctx, tb )?));
317 }
318 TagType::MultinaryMid => {
319 base.add_chain(Entry::TagBuilder(self.parse_tag_set( ctx, tb )?));
320 return Ok(base);
321 }
322 TagType::MultinaryClose => {
323 if (tb.is_name(&SymbolType::If) &&
324 (base.is_name(&SymbolType::ElIf) ||
325 base.is_name(&SymbolType::Else))) ||
326 tb.is_name(base.name().unwrap().symbol()) {
327 return Ok(base);
328 } else {
329 return Err(CrimError::parse(
330 *tb.name().unwrap().start(),
331 format!("Found {:?} looking for close of {:?}", tb.name().unwrap().symbol(), base.name().unwrap().symbol())
332 ));
333 }
334 }
335 TagType::Unary => {
336 base.add_child(Entry::TagBuilder(tb));
337 }
338 TagType::Unknown => {
339 return Err(CrimError::broken(base.start_pos(), "Found unknown tag when looking for something else."));
340 }
341 }
342 }
343 SymbolType::Error{..} => {
344 return self.lex_error( &ctx.cur().unwrap() );
345 }
346 _ => {
347 return Err(CrimError::parse(
348 *ctx.cur().unwrap().start(),
349 format!("Unexpected token: {:?}", ctx.cur().unwrap().symbol()),
350 ));
351 }
352 }
353 }
354 }
355
356 fn parse_tag_params(&self, ctx: &mut Context, tb: &mut TagBuilder ) -> CrimResult<()> {
357 loop {
358 if ctx.cur().is_none() {
359 return Err(CrimError::eos("tag parameters"));
360 }
361 match ctx.cur().unwrap().symbol() {
362 SymbolType::Literal(s) => {
363 tb.add_param( ParamValue::Literal(s.to_string()) );
364 ctx.next();
365 }
366 SymbolType::Token(_) | SymbolType::Sharp => {
367 if ctx.peek().is("tag data", |s| *s == SymbolType::Equals)? {
368 break;
369 }
370 tb.add_param( self.parse_identifier( ctx )? );
371 }
372 _ => {
373 break;
374 }
375 }
376 }
377 Ok(())
378 }
379
380 fn parse_identifier(&self, ctx: &mut Context ) -> CrimResult<ParamValue> {
381 let mut id = Identifier::new();
382
383 if ctx.cur().is("identifier",|s| matches!( s, SymbolType::Sharp))? {
384 id.push( IdentifierValue::Root );
385 ctx.next();
386 }
387 loop {
388 if ctx.cur().is("identifier",|s| matches!( s, SymbolType::Token(_) ))? {
389 if let SymbolType::Token(s) = ctx.cur().unwrap().symbol() {
390 id.push( IdentifierValue::Name(s.to_string()) );
391 }
392 } else {
393 return Err(CrimError::parse( ctx.cur().unwrap().start().clone(), format!("Expeceted identifier, found {:?}", ctx.cur().unwrap().symbol() ) ) );
394 }
395
396 if !ctx.next().is("identifier seperator",|s| *s == SymbolType::Period )? {
397 break;
398 }
399 ctx.next();
400 }
401
402 Ok(ParamValue::Identifier(id))
403 }
404
405 fn parse_tag_props(&self, ctx: &mut Context, tb: &mut TagBuilder ) -> CrimResult<()> {
406 loop {
407 if ctx.cur().is_none() {
408 return Err(CrimError::eos("tag properties"));
409 }
410 if let SymbolType::Token(s) = ctx.cur().unwrap().symbol() {
411 if ctx.peek().is_some_and(|s|s.check_type(|t| matches!(t,SymbolType::Equals))) {
412 ctx.next();
413 if let Some(sym) = ctx.next() &&
414 let SymbolType::Literal(lv) = sym.symbol() {
415 tb.add_prop( s.to_string(), lv.to_string() );
416 } else {
417 return Err(CrimError::parse(Position::none(),"Expected quoted literal string".to_string()));
418 }
419 } else {
420 break;
421 }
422 } else {
423 break;
424 }
425 ctx.next();
426 }
427 Ok(())
428 }
429}
430
431#[derive(PartialEq,Copy,Clone,Debug)]
432enum TagType {
433 Unknown,
434 Unary,
435 MultinaryOpen,
436 MultinaryMid,
437 MultinaryClose,
438}
439
440#[derive(Debug)]
441enum Entry<'a>{
442 Token(Token),
443 TagBuilder(TagBuilder<'a>),
444}
445
446type EntryList<'a> = Vec<Entry<'a>>;
447
448trait EntryListConverter {
449 fn has_outputs(&self) -> bool;
450 fn to_outputs(&mut self, ctx: &mut Context ) -> CrimResult<Vec<Output>>;
451 fn to_tokens(&mut self, ctx: &mut Context) -> CrimResult<Vec<Token>>;
452}
453
454impl<'a> EntryListConverter for EntryList<'a> {
455 fn has_outputs(&self) -> bool {
456 self.iter().any(|e|
457 if let Entry::TagBuilder(tb) = e
458 && tb.is_name(&SymbolType::Output) {
459 true
460 } else {
461 false
462 }
463 )
464 }
465
466 fn to_outputs(&mut self, ctx: &mut Context) -> CrimResult<Vec<Output>> {
467 let mut outputs = Vec::new();
468
469 if self.has_outputs() {
470 // We have an explicit output, we cant have anything else
471 for e in self.drain(..) {
472 let tb = if let Entry::TagBuilder(tb) = e {
473 tb
474 } else {
475 continue;
476 };
477 if let BuildResult::Output(out) = tb.build(ctx)? {
478 outputs.push( out );
479 }
480 }
481 } else {
482 // No outputs, so we create one implicit output
483 outputs.push(Output {
484 name: "content".into(),
485 code: self.to_tokens(ctx)?
486 });
487 }
488
489 Ok(outputs)
490 }
491
492 fn to_tokens(&mut self, ctx: &mut Context) -> CrimResult<Vec<Token>> {
493 let mut tokens : Vec<Token> = Vec::new();
494 for e in self.drain(..) {
495 match e {
496 Entry::Token(token) => {
497 tokens.push( token );
498 }
499 Entry::TagBuilder(tb) => {
500 if let BuildResult::Token(token) = tb.build(ctx)? {
501 tokens.push( token );
502 } else {
503 return Err(CrimError::broken( Position::none(), "Non-token result built."));
504 }
505 }
506 }
507 }
508 Ok(tokens)
509 }
510}
511
512#[derive(Debug)]
513struct TagBuilder<'a> {
514 name: Option<Symbol<'a>>,
515 params: Vec<ParamValue>,
516 props: Properties,
517 children: Vec<Entry<'a>>,
518 tag_type: TagType,
519 start: Symbol<'a>,
520 chain: Vec<Entry<'a>>,
521}
522
523#[derive(Debug)]
524enum ParamValue {
525 Literal(String),
526 Identifier(Identifier),
527}
528
529enum BuildResult {
530 View(View),
531 Output(Output),
532 Token(Token),
533}
534
535impl<'a> TagBuilder<'a> {
536 pub fn new(start: &Symbol<'a>) -> TagBuilder<'a> {
537 TagBuilder {
538 name: None,
539 params: Vec::new(),
540 props: Properties::new(),
541 children: Vec::new(),
542 tag_type: TagType::Unknown,
543 start: start.clone(),
544 chain: Vec::new(),
545 }
546 }
547
548 pub fn set_end(&mut self, end: &Symbol) -> CrimResult<()> {
549 if *self.start.symbol() == SymbolType::StartFlat {
550 if *end.symbol() == SymbolType::EndFlat {
551 self.tag_type = TagType::Unary;
552 } else if *end.symbol() == SymbolType::EndPoint {
553 self.tag_type = TagType::MultinaryOpen;
554 } else {
555 return Err(CrimError::broken( *end.start(), "Invalid bracket token type."));
556 }
557 } else if *self.start.symbol() == SymbolType::StartPoint {
558 if *end.symbol() == SymbolType::EndFlat {
559 self.tag_type = TagType::MultinaryClose;
560 } else if *end.symbol() == SymbolType::EndPoint {
561 self.tag_type = TagType::MultinaryMid;
562 } else {
563 return Err(CrimError::broken( *end.start(), "Invalid bracket token type."));
564 }
565 } else {
566 return Err(CrimError::broken( *self.start.start(), "Invalid bracket token type."));
567 }
568 Ok(())
569 }
570
571 pub fn start_pos(&self) -> Position {
572 *self.start.start()
573 }
574
575 pub fn is_unary(&self) -> bool {
576 self.tag_type == TagType::Unary
577 }
578/*
579 pub fn is_multinary_open(&self) -> bool {
580 self.tag_type == TagType::MultinaryOpen
581 }
582*/
583 pub fn can_have_params(&self) -> bool {
584 match self.name.unwrap().symbol() {
585 SymbolType::View | SymbolType::Output | SymbolType::Loop => true,
586 SymbolType::Show | SymbolType::If | SymbolType::ElIf |
587 SymbolType::Else => false,
588 _ => false,
589 }
590 }
591
592 pub fn can_have_expr(&self) -> bool {
593 match self.name.unwrap().symbol() {
594 SymbolType::View | SymbolType::Output | SymbolType::Loop => false,
595 SymbolType::Show | SymbolType::If | SymbolType::ElIf |
596 SymbolType::Else => true,
597 _ => false,
598 }
599 }
600
601 pub fn can_have_props(&self) -> bool {
602 true
603 }
604/*
605 pub fn can_have_children(&self) -> bool {
606 if self.tag_type == TagType::MultinaryOpen ||
607 self.tag_type == TagType::MultinaryMid {
608 true
609 } else {
610 false
611 }
612 }
613*/
614 pub fn is_name(&self, name: &SymbolType<'a>) -> bool {
615 if let Some(a) = self.name {
616 a.symbol() == name
617 } else {
618 false
619 }
620 }
621
622 pub fn name(&self) -> Option<Symbol<'a>> {
623 self.name
624 }
625/*
626 pub fn params(&self) -> &Vec<ParamValue> {
627 &self.params
628 }
629*/
630 pub fn set_name(&mut self, name: Symbol<'a>) {
631 self.name = Some(name);
632 }
633
634 pub fn add_param(&mut self, param: ParamValue) {
635 self.params.push( param );
636 }
637
638 pub fn add_prop(&mut self, key: String, value: String) {
639 self.props.insert( key, value );
640 }
641
642 pub fn add_child(&mut self, tb: Entry<'a>) {
643 self.children.push( tb );
644 }
645/*
646 pub fn append_children(&mut self, children: &mut Vec::<Entry<'a>>) {
647 self.children.append( children );
648 }
649*/
650 pub fn add_chain(&mut self, tb: Entry<'a>) {
651 self.chain.push( tb );
652 }
653/*
654 pub fn set_type(&mut self, tag_type: TagType) {
655 self.tag_type = tag_type;
656 }
657*/
658 pub fn tag_type(&self) -> TagType {
659 self.tag_type
660 }
661
662 pub fn build(mut self, ctx: &mut Context ) -> CrimResult<BuildResult> {
663 if let Some(sym) = self.name {
664 match sym.symbol() {
665 SymbolType::View => {
666 let name = if let ParamValue::Literal(s) = self.params.swap_remove(0) {
667 s.to_string()
668 } else {
669 return Err(CrimError::parse(Position::none(), "Expected string literal for view name.".to_string()));
670 };
671 Ok(BuildResult::View(View {
672 name: name,
673 source: ctx.source(),
674 outputs: self.children.to_outputs(ctx)?,
675 //theme: Option<String>
676 layout: self.props.get("layout").cloned(),
677 }))
678 }
679 SymbolType::Output => {
680 let name = if let ParamValue::Literal(s) = self.params.swap_remove(0) {
681 s.to_string()
682 } else {
683 return Err(CrimError::parse(Position::none(), "Expected string literal for output name.".into()));
684 };
685 Ok(BuildResult::Output(Output {
686 name: name,
687 code: self.children.to_tokens(ctx)?,
688 }))
689 }
690 SymbolType::Loop => {
691 let id = if let ParamValue::Identifier(id)
692 = self.params.swap_remove(0) {
693 id
694 } else {
695 return Err(CrimError::parse(
696 Position::none(),
697 "Identifier for loop variable name.".into())
698 );
699 };
700 Ok(BuildResult::Token(
701 Token::Loop(id, self.children.to_tokens(ctx)?)
702 ))
703 }
704 SymbolType::Show => {
705 let id = if let ParamValue::Identifier(id)
706 = self.params.swap_remove(0) {
707 id
708 } else {
709 return Err(CrimError::parse(
710 Position::none(),
711 "Identifier for show variable name.".into())
712 );
713 };
714 Ok(BuildResult::Token(Token::Show(id, self.props)))
715 }
716 SymbolType::If => {
717 let id = if let ParamValue::Identifier(id)
718 = self.params.swap_remove(0) {
719 id
720 } else {
721 return Err(CrimError::parse(
722 Position::none(),
723 "Identifier for if variable name.".into())
724 );
725 };
726
727 let is_else = if self.chain.len() == 1 &&
728 let Entry::TagBuilder(tb) = &self.chain[0] &&
729 tb.is_name(&SymbolType::Else) &&
730 tb.params.len() == 0 {
731 true
732 } else {
733 false
734 };
735
736 if is_else &&
737 let Entry::TagBuilder(mut tb) = self.chain.swap_remove(0) {
738 self.chain.clear();
739 self.chain.append(&mut tb.children);
740 }
741 Ok(BuildResult::Token(Token::If(id, self.children.to_tokens(ctx)?, self.chain.to_tokens(ctx)?)))
742 }
743 SymbolType::ElIf => {
744 let id = if let ParamValue::Identifier(id) = self.params.swap_remove(0) {
745 id
746 } else {
747 return Err(CrimError::parse(
748 Position::none(),
749 "Identifier for elif variable name.".into())
750 );
751 };
752
753 // this is copied from if, since they're the same this
754 // should be encapsulated and moved into a function...
755 // ...but I can't do that right now.
756 let is_else = if self.chain.len() == 1 &&
757 let Entry::TagBuilder(tb) = &self.chain[0] &&
758 tb.is_name(&SymbolType::Else) &&
759 tb.params.len() == 0 {
760 true
761 } else {
762 false
763 };
764
765 if is_else &&
766 let Entry::TagBuilder(mut tb) = self.chain.swap_remove(0) {
767 self.chain.clear();
768 self.chain.append(&mut tb.children);
769 }
770
771
772 Ok(BuildResult::Token(Token::If(id, self.children.to_tokens(ctx)?, self.chain.to_tokens(ctx)?)))
773 }
774 SymbolType::Else => {
775 Ok(BuildResult::Token(Token::If(Identifier::new(), self.children.to_tokens(ctx)?, self.chain.to_tokens(ctx)?)))
776 }
777 _ => {
778 Err(CrimError::parse( self.start_pos(), "Bad tag type".into()))
779 }
780 }
781 } else {
782 Err(CrimError::parse(self.start_pos(), "Bad tag type".into()))
783 }
784 }
785}