summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lib.rs43
-rw-r--r--src/parser.rs432
2 files changed, 217 insertions, 258 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 2b53828..84a5a43 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -171,6 +171,7 @@ impl Crimtag {
171 171
172 pub fn render(&self, view: &str, input: &impl MappedStructure ) -> CrimResult<String> { 172 pub fn render(&self, view: &str, input: &impl MappedStructure ) -> CrimResult<String> {
173 if let Some(view) = self.views.get( view ) { 173 if let Some(view) = self.views.get( view ) {
174 //println!("::Token tree::\n{:?}", view );
174 let mut output = view.process( input )?; 175 let mut output = view.process( input )?;
175 if let Some(l) = &view.layout { 176 if let Some(l) = &view.layout {
176 self.render( l, &output ) 177 self.render( l, &output )
@@ -203,12 +204,14 @@ fn exec(input: &impl MappedStructure, tokens: &Vec<Token>, buf: &mut String) ->
203 } 204 }
204 } 205 }
205 } 206 }
206 Token::If(ident,code) => { 207 Token::If(ident,code,other) => {
207 if let Some(value) = input.get_value( &ident ) && 208 if let Some(value) = input.get_value( &ident ) &&
208 let Value::Bool(b) = value && 209 let Value::Bool(b) = value &&
209 b { 210 b {
210 211
211 exec(input, code, buf)? 212 exec(input, code, buf)?
213 } else {
214 exec(input, other, buf)?
212 } 215 }
213 } 216 }
214 Token::Show(ident,p) => { 217 Token::Show(ident,p) => {
@@ -322,16 +325,13 @@ Now with explicit outputs:
322 } 325 }
323 326
324 #[test] 327 #[test]
325 fn loops() { 328 fn loops() -> Result<(),CrimError> {
326 let mut ct = Crimtag::new(); 329 let mut ct = Crimtag::new();
327 if let Err(e) = ct.load_static(r#"Looping code: 330 ct.load_static(r#"Looping code:
328[|view "index"|>We will enumerate people here:[|loop people|> 331[|view "index"|>We will enumerate people here:[|loop people|>
329 - [|show last_name|], [|show first_name|][|if show_title |>: [|show title|] <|if|] [|loop tags|> [|show tag|]<|loop|] <|loop|] 332 - [|show last_name|], [|show first_name|][|if show_title |>: [|show title|] <|else|> **title redacted** <|if|] [|loop tags|> [|show tag|]<|loop|] <|loop|]
330<|view|] 333<|view|]
331"#) { 334"#)?;
332 println!("Error: {:?}", e );
333 return;
334 }
335 335
336 let ctx = Context::from([ 336 let ctx = Context::from([
337 ("people".to_string(), vec![ 337 ("people".to_string(), vec![
@@ -361,9 +361,28 @@ Now with explicit outputs:
361 ].into(), 361 ].into(),
362 ].into()), 362 ].into()),
363 ]); 363 ]);
364 match ct.render("index", &ctx) { 364
365 Ok(s) => println!("View result: {}", s ), 365 println!("View result: {}", ct.render("index", &ctx)? );
366 Err(e) => println!("Error: {:?}", e ), 366
367 } 367 Ok(())
368 }
369
370 #[test]
371 fn conditional() -> Result<(),CrimError> {
372 let mut ct = Crimtag::new();
373 ct.load_static(r#"Hi there
374[|view "index"|>
375 Color: [|if is_red|> red <|elif is_blue |> blue <|else|> green <|if|]
376<|view|]"#)?;
377
378 let ctx = Context::from([
379 ("is_red".into(), false.into()),
380 ("is_blue".into(), false.into()),
381 ("color".into(), "purple".into()),
382 ]);
383
384 println!("View result: {}", ct.render("index", &ctx)? );
385
386 Ok(())
368 } 387 }
369} 388}
diff --git a/src/parser.rs b/src/parser.rs
index b681fce..fff00b1 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -199,7 +199,7 @@ impl Parser {
199 } 199 }
200 200
201 fn p_input(&self, ctx: &mut Context ) -> CrimResult<Vec<View>> { 201 fn p_input(&self, ctx: &mut Context ) -> CrimResult<Vec<View>> {
202 let mut children = Vec::new(); 202 let mut tags = Vec::new();
203 loop { 203 loop {
204 if ctx.cur().is_none() { 204 if ctx.cur().is_none() {
205 break; 205 break;
@@ -207,21 +207,41 @@ impl Parser {
207 match ctx.cur().unwrap().symbol() { 207 match ctx.cur().unwrap().symbol() {
208 SymbolType::Text(_) => { /* Skip top level text */ } 208 SymbolType::Text(_) => { /* Skip top level text */ }
209 SymbolType::StartFlat => { 209 SymbolType::StartFlat => {
210 children.push( self.parse_tag_view( ctx )? ); 210 let tb = self.parse_tag( ctx )?;
211 let tb = self.parse_tag_set( ctx, tb )?;
212 tags.push( tb );
211 } 213 }
212 SymbolType::Error{..} => { 214 SymbolType::Error{..} => {
213 self.lex_error( &ctx.cur().unwrap() )?; 215 self.lex_error( &ctx.cur().unwrap() )?;
214 } 216 }
215 _ => { 217 _ => {
216 218 return Err(CrimError::parse(
219 *ctx.cur().unwrap().start(),
220 format!("Unexpected symbol {:?} found looking for tags.", ctx.cur().unwrap().symbol() )
221 ));
217 } 222 }
218 } 223 }
219 ctx.next(); 224 ctx.next();
220 } 225 }
221 Ok(children) 226
227 let mut views = Vec::new();
228 for tag in tags {
229 let start_pos = tag.start_pos();
230 let name = tag.name().clone();
231 if tag.is_name(&SymbolType::View) &&
232 let BuildResult::View(view) = tag.build( ctx )? {
233 views.push( view );
234 } else {
235 return Err(CrimError::parse(
236 start_pos,
237 format!("Expected view at root, found {:?}", name )
238 ));
239 }
240 }
241 Ok(views)
222 } 242 }
223 243
224 fn parse_tag(&self, ctx: &mut Context) -> CrimResult<TagBuilder> { 244 fn parse_tag<'a>(&self, ctx: &mut Context<'a>) -> CrimResult<TagBuilder<'a>> {
225 let start_sym = ctx.cur().unwrap(); 245 let start_sym = ctx.cur().unwrap();
226 let mut tb = TagBuilder::new(&start_sym); 246 let mut tb = TagBuilder::new(&start_sym);
227 247
@@ -255,7 +275,7 @@ impl Parser {
255 if let Some(end_sym) = ctx.cur() { 275 if let Some(end_sym) = ctx.cur() {
256 match end_sym.symbol() { 276 match end_sym.symbol() {
257 SymbolType::EndPoint | SymbolType::EndFlat => { 277 SymbolType::EndPoint | SymbolType::EndFlat => {
258 tb.set_end( end_sym )?; 278 tb.set_end( &end_sym )?;
259 } 279 }
260 _ => { 280 _ => {
261 return Err(CrimError::parse( 281 return Err(CrimError::parse(
@@ -271,12 +291,9 @@ impl Parser {
271 Ok(tb) 291 Ok(tb)
272 } 292 }
273 293
274 fn parse_tag_set<'a>(&self, ctx: &mut Context; base: &mut TagBuilder) -> CrimResult<Token> { 294 fn parse_tag_set<'a>(&self, ctx: &mut Context<'a>, mut base: TagBuilder<'a>) -> CrimResult<TagBuilder<'a>> {
275 if base.is_unary() { 295 if base.is_unary() {
276 return Ok(base.build()); 296 return Ok(base);
277 }
278 if !base.is_multinary_open() {
279 return Err(CrimError::broken("Found close/mid tag instead of opening"));
280 } 297 }
281 298
282 loop { 299 loop {
@@ -285,226 +302,39 @@ impl Parser {
285 } 302 }
286 match ctx.cur().unwrap().symbol() { 303 match ctx.cur().unwrap().symbol() {
287 SymbolType::Text(s) => { 304 SymbolType::Text(s) => {
288 base.add_child(Token::Text(s.to_string())); 305 base.add_child(Entry::Token(Token::Text(s.to_string())));
289 ctx.next(); 306 ctx.next();
290 } 307 }
291 SymbolType::StartFlat | SymbolType::StartPoint => { 308 SymbolType::StartFlat | SymbolType::StartPoint => {
292 let mut tb = self.parse_tag( ctx )?; 309 let tb = self.parse_tag( ctx )?;
293 310
294 match tb.tag_type() { 311 match tb.tag_type() {
295 TagType::MultinaryOpen => { 312 TagType::MultinaryOpen => {
296 base.add_child( parse_tag_set( ctx, tb )? ); 313 base.add_child(Entry::TagBuilder(self.parse_tag_set( ctx, tb )?));
297 } 314 }
298 TagType::MultinaryMid => { 315 TagType::MultinaryMid => {
299 base.add_chain( parse_tag_set( ctx, tb )? ); 316 base.add_chain(Entry::TagBuilder(self.parse_tag_set( ctx, tb )?));
317 return Ok(base);
300 } 318 }
301 TagType::MultinaryClose => { 319 TagType::MultinaryClose => {
302 if (tb.is_name(SymbolToken::If) && 320 if (tb.is_name(&SymbolType::If) &&
303 (base.is_name(SymbolToken::Elif) || 321 (base.is_name(&SymbolType::ElIf) ||
304 base.is_name(SymbolToken::Else))) || 322 base.is_name(&SymbolType::Else))) ||
305 tb.is_name(base.name().symbol()) { 323 tb.is_name(base.name().unwrap().symbol()) {
306 324 return Ok(base);
307 } else { 325 } else {
308 return Err(CrimError::parse( 326 return Err(CrimError::parse(
309 *tb.name().start(), 327 *tb.name().unwrap().start(),
310 format!("Found {:?} looking for close of {:?}", tb.name().symbol(), base.name().symbol()) 328 format!("Found {:?} looking for close of {:?}", tb.name().unwrap().symbol(), base.name().unwrap().symbol())
311 )); 329 ));
312 } 330 }
313 } 331 }
314 } 332 TagType::Unary => {
315 } 333 base.add_child(Entry::TagBuilder(tb));
316 SymbolType::Error{..} => { 334 }
317 return self.lex_error( &ctx.cur().unwrap() ); 335 TagType::Unknown => {
318 } 336 return Err(CrimError::broken(base.start_pos(), "Found unknown tag when looking for something else."));
319 _ => {
320 return Err(CrimError::parse(
321 *ctx.cur().unwrap().start(),
322 format!("Unexpected token: {:?}", ctx.cur().unwrap().symbol()),
323 ));
324 }
325 }
326 }
327 }
328
329 fn parse_tag_view(&self, ctx: &mut Context) -> CrimResult<View> {
330 let tb = self.parse_open_tag( ctx )?;
331 if !tb.is_name( &SymbolType::View ) {
332 return Err(CrimError::parse(tb.start_pos(),
333 "Unexpected tag type, only view allowed at root".into()
334 ));
335 }
336
337 if tb.is_unary() {
338 return tb.build_view(ctx, Vec::new());
339 }
340 let mut children = Vec::new();
341 let mut outputs = Vec::new();
342
343 //println!("--parse-tag-view-- tag parsed, next token: {:?}", ctx.cur() );
344 loop {
345 if ctx.cur().is_none() {
346 return Err(CrimError::eos(format!("close tag for {:?}", tb.name()).as_str()))
347 }
348 match ctx.cur().unwrap().symbol() {
349 SymbolType::Text(s) => {
350 children.push( Token::Text(s.to_string()) );
351 ctx.next();
352 }
353 SymbolType::StartFlat => {
354 let subtb = self.parse_tag( ctx )?;
355 if subtb.is_name(&SymbolType::Output) {
356 outputs.push(subtb.build_output()?);
357 } else {
358 children.push(subtb.build()?);
359 }
360 }
361 SymbolType::StartPoint => {
362 let end = self.parse_end_tag( ctx )?;
363 //println!("End tag: {:?}, open tag: {:?}", end, tb.name() );
364 if tb.is_name(end.symbol()) {
365 // They match, time to decide what we're doing.
366 let any_outputs = outputs.len() > 0;
367 let all_text = children.iter().all(
368 |t| matches!(t, Token::Text(..))
369 );
370 if !any_outputs {
371 // Create an implicit output called "content"
372 outputs.push(Output{
373 name: "content".to_string(),
374 code: children,
375 });
376 } else {
377 if !all_text {
378 return Err(CrimError::parse(
379 *end.start(),
380 "You cannot mix non-output and output tags in a view.".to_string()
381 ));
382 }
383 } 337 }
384 return tb.build_view(ctx,outputs);
385 } else {
386 // They don't match, complain.
387 return Err(CrimError::parse(
388 *end.start(),
389 "Mismatched open and closing tags.".to_string()
390 ));
391 }
392 }
393 SymbolType::Error{..} => {
394 return self.lex_error( &ctx.cur().unwrap() );
395 }
396 _ => {
397 return Err(CrimError::parse(
398 *ctx.cur().unwrap().start(),
399 format!("Unexpected tag in view root: {:?}", ctx.cur().unwrap().symbol())
400 ));
401 }
402 }
403 }
404 }
405
406 fn parse_open_tag<'a>(&self, ctx: &mut Context<'a>) -> CrimResult<TagBuilder<'a>> {
407 let start_sym = ctx.cur().unwrap();
408 let mut tb = TagBuilder::new(&start_sym);
409
410 if ctx.next().is_some() {
411 match ctx.cur().unwrap().symbol() {
412 SymbolType::View | SymbolType::Show | SymbolType::Loop |
413 SymbolType::If | SymbolType::ElIf | SymbolType::Else |
414 SymbolType::Output => {
415 let name_sym = ctx.cur().unwrap();
416 ctx.next();
417 tb.set_name( name_sym );
418 }
419 _ => {
420 return Err(CrimError::parse(
421 *ctx.cur().unwrap().start(),
422 "Unexpected symbol".to_string()
423 ));
424 }
425 }
426 } else {
427 return Err(CrimError::eos("tag type"));
428 }
429
430 self.parse_tag_params( ctx, &mut tb )?;
431 self.parse_tag_props( ctx, &mut tb )?;
432
433 if let Some(end_sym) = ctx.cur() {
434 match end_sym.symbol() {
435 SymbolType::EndPoint => {
436 tb.set_type( TagType::MultinaryOpen );
437 }
438 SymbolType::EndFlat => {
439 tb.set_type( TagType::Unary );
440 }
441 _ => {
442 return Err(CrimError::parse(
443 *end_sym.start(),
444 format!("Unexpected symbol {:?} looking for end of tag.", end_sym.symbol())
445 ));
446 }
447 }
448 }
449
450 ctx.next();
451
452 Ok(tb)
453 }
454
455 fn parse_end_tag<'a>(&self, ctx: &mut Context<'a> ) -> CrimResult<Symbol<'a>> {
456 if !ctx.cur().is("end tag",|s| *s == SymbolType::StartPoint )? {
457 return Err(CrimError::parse(
458 *ctx.cur().unwrap().start(),
459 "Invalid end tag?".to_string()
460 ));
461 }
462 if !ctx.next().is_valid_tag_name()? {
463 return Err(CrimError::parse(
464 *ctx.cur().unwrap().start(),
465 "Invalid tag name".to_string()
466 ));
467 }
468 let name = ctx.cur().unwrap();
469 if !ctx.next().is("end of end tag", |s| *s == SymbolType::EndFlat )? {
470 return Err(CrimError::parse(
471 *ctx.cur().unwrap().start(),
472 "Tag should be <| |] style end tag.".to_string(),
473 ));
474 }
475 ctx.next();
476 Ok(name)
477 }
478
479 fn parse_tag_body<'a>(&self, ctx: &mut Context<'a> ) -> CrimResult<TagBuilder<'a>> {
480 let mut tb = self.parse_open_tag( ctx )?;
481 if tb.is_unary() {
482 return Ok(tb);
483 }
484
485 loop {
486 if ctx.cur().is_none() {
487 return Err(CrimError::eos(format!("close tag for {:?}", tb.name()).as_str()));
488 }
489 match ctx.cur().unwrap().symbol() {
490 SymbolType::Text(s) => {
491 tb.add_child(Token:: Text(s.to_string()));
492 ctx.next();
493 }
494 SymbolType::StartFlat => {
495 tb.add_child( self.parse_tag_body( ctx )?.build()? );
496 }
497 SymbolType::StartPoint => {
498 let end = self.parse_end_tag( ctx )?;
499 if tb.is_name(end.symbol()) {
500 // They match, end.
501 return Ok(tb);
502 } else {
503 // They don't match, complain.
504 return Err(CrimError::parse(
505 *end.start(),
506 "Mismatched open and closing tags.".to_string()
507 ));
508 } 338 }
509 } 339 }
510 SymbolType::Error{..} => { 340 SymbolType::Error{..} => {
@@ -600,14 +430,87 @@ enum TagType {
600 MultinaryClose, 430 MultinaryClose,
601} 431}
602 432
433#[derive(Debug)]
434enum Entry<'a>{
435 Token(Token),
436 TagBuilder(TagBuilder<'a>),
437}
438
439type EntryList<'a> = Vec<Entry<'a>>;
440
441trait EntryListConverter {
442 fn has_outputs(&self) -> bool;
443 fn to_outputs(&mut self, ctx: &mut Context ) -> CrimResult<Vec<Output>>;
444 fn to_tokens(&mut self, ctx: &mut Context) -> CrimResult<Vec<Token>>;
445}
446
447impl<'a> EntryListConverter for EntryList<'a> {
448 fn has_outputs(&self) -> bool {
449 self.iter().any(|e|
450 if let Entry::TagBuilder(tb) = e
451 && tb.is_name(&SymbolType::Output) {
452 true
453 } else {
454 false
455 }
456 )
457 }
458
459 fn to_outputs(&mut self, ctx: &mut Context) -> CrimResult<Vec<Output>> {
460 let mut outputs = Vec::new();
461
462 if self.has_outputs() {
463 // We have an explicit output, we cant have anything else
464 for e in self.drain(..) {
465 let tb = if let Entry::TagBuilder(tb) = e {
466 tb
467 } else {
468 continue;
469 };
470 if let BuildResult::Output(out) = tb.build(ctx)? {
471 outputs.push( out );
472 }
473 }
474 } else {
475 // No outputs, so we create one implicit output
476 outputs.push(Output {
477 name: "content".into(),
478 code: self.to_tokens(ctx)?
479 });
480 }
481
482 Ok(outputs)
483 }
484
485 fn to_tokens(&mut self, ctx: &mut Context) -> CrimResult<Vec<Token>> {
486 let mut tokens : Vec<Token> = Vec::new();
487 for e in self.drain(..) {
488 match e {
489 Entry::Token(token) => {
490 tokens.push( token );
491 }
492 Entry::TagBuilder(tb) => {
493 if let BuildResult::Token(token) = tb.build(ctx)? {
494 tokens.push( token );
495 } else {
496 return Err(CrimError::broken( Position::none(), "Non-token result built."));
497 }
498 }
499 }
500 }
501 Ok(tokens)
502 }
503}
504
505#[derive(Debug)]
603struct TagBuilder<'a> { 506struct TagBuilder<'a> {
604 name: Option<Symbol<'a>>, 507 name: Option<Symbol<'a>>,
605 params: Vec<ParamValue>, 508 params: Vec<ParamValue>,
606 props: Properties, 509 props: Properties,
607 children: Vec<Token>, 510 children: Vec<Entry<'a>>,
608 tag_type: TagType, 511 tag_type: TagType,
609 start: Symbol<'a>, 512 start: Symbol<'a>,
610 chain: Vec<Token>, 513 chain: Vec<Entry<'a>>,
611} 514}
612 515
613#[derive(Debug)] 516#[derive(Debug)]
@@ -636,29 +539,30 @@ impl<'a> TagBuilder<'a> {
636 } 539 }
637 540
638 pub fn set_end(&mut self, end: &Symbol) -> CrimResult<()> { 541 pub fn set_end(&mut self, end: &Symbol) -> CrimResult<()> {
639 if start.symbol() == SymbolType::StartFlat { 542 if *self.start.symbol() == SymbolType::StartFlat {
640 if end.symbol() == SymbolType::EndFlat { 543 if *end.symbol() == SymbolType::EndFlat {
641 self.tag_type = TagType::Unary; 544 self.tag_type = TagType::Unary;
642 } else if end.symbol() == SymbolType::EndPoint { 545 } else if *end.symbol() == SymbolType::EndPoint {
643 self.tag_type = TagType::MultinaryOpen; 546 self.tag_type = TagType::MultinaryOpen;
644 } else { 547 } else {
645 return Err(CrimError::broken( *self.end.start(), "Invalid bracket token type.")); 548 return Err(CrimError::broken( *end.start(), "Invalid bracket token type."));
646 } 549 }
647 } else if start.symbol() == SymbolType::StartPoint { 550 } else if *self.start.symbol() == SymbolType::StartPoint {
648 if end.symbol() == SymbolType::EndFlat { 551 if *end.symbol() == SymbolType::EndFlat {
649 self.tag_type = TagType::MultinaryClose; 552 self.tag_type = TagType::MultinaryClose;
650 } else if end.symbol() == SymbolType::EndPoint { 553 } else if *end.symbol() == SymbolType::EndPoint {
651 self.tag_type = TagType::MultinaryMid; 554 self.tag_type = TagType::MultinaryMid;
652 } else { 555 } else {
653 return Err(CrimError::broken( *self.end.start(), "Invalid bracket token type.")); 556 return Err(CrimError::broken( *end.start(), "Invalid bracket token type."));
654 } 557 }
655 } else { 558 } else {
656 return Err(CrimError::broken( *self.start.start(), "Invalid bracket token type.")); 559 return Err(CrimError::broken( *self.start.start(), "Invalid bracket token type."));
657 } 560 }
561 Ok(())
658 } 562 }
659 563
660 pub fn start_pos(&self) -> Position { 564 pub fn start_pos(&self) -> Position {
661 self.start.start() 565 *self.start.start()
662 } 566 }
663 567
664 pub fn is_unary(&self) -> bool { 568 pub fn is_unary(&self) -> bool {
@@ -670,18 +574,20 @@ impl<'a> TagBuilder<'a> {
670 } 574 }
671 575
672 pub fn can_have_params(&self) -> bool { 576 pub fn can_have_params(&self) -> bool {
673 match self.name.symbol() { 577 match self.name.unwrap().symbol() {
674 SymbolType::View | SymbolType::Output | SymbolType::Loop => true, 578 SymbolType::View | SymbolType::Output | SymbolType::Loop => true,
675 SymbolType::Show | SymbolType::If | SymbolType::ElIf | 579 SymbolType::Show | SymbolType::If | SymbolType::ElIf |
676 SymbolType::Else => false, 580 SymbolType::Else => false,
581 _ => false,
677 } 582 }
678 } 583 }
679 584
680 pub fn can_have_expr(&self) -> bool { 585 pub fn can_have_expr(&self) -> bool {
681 match self.name.symbol() { 586 match self.name.unwrap().symbol() {
682 SymbolType::View | SymbolType::Output | SymbolType::Loop => false, 587 SymbolType::View | SymbolType::Output | SymbolType::Loop => false,
683 SymbolType::Show | SymbolType::If | SymbolType::ElIf | 588 SymbolType::Show | SymbolType::If | SymbolType::ElIf |
684 SymbolType::Else => true, 589 SymbolType::Else => true,
590 _ => false,
685 } 591 }
686 } 592 }
687 593
@@ -726,14 +632,18 @@ impl<'a> TagBuilder<'a> {
726 self.props.insert( key, value ); 632 self.props.insert( key, value );
727 } 633 }
728 634
729 pub fn add_child(&mut self, token: Token) { 635 pub fn add_child(&mut self, tb: Entry<'a>) {
730 self.children.push( token ); 636 self.children.push( tb );
731 } 637 }
732 638
733 pub fn append_children(&mut self, children: &mut Vec::<Token>) { 639 pub fn append_children(&mut self, children: &mut Vec::<Entry<'a>>) {
734 self.children.append( children ); 640 self.children.append( children );
735 } 641 }
736 642
643 pub fn add_chain(&mut self, tb: Entry<'a>) {
644 self.chain.push( tb );
645 }
646
737 pub fn set_type(&mut self, tag_type: TagType) { 647 pub fn set_type(&mut self, tag_type: TagType) {
738 self.tag_type = tag_type; 648 self.tag_type = tag_type;
739 } 649 }
@@ -742,7 +652,7 @@ impl<'a> TagBuilder<'a> {
742 self.tag_type 652 self.tag_type
743 } 653 }
744 654
745 pub fn build(mut self) -> CrimResult<BuildResult> { 655 pub fn build(mut self, ctx: &mut Context ) -> CrimResult<BuildResult> {
746 if let Some(sym) = self.name { 656 if let Some(sym) = self.name {
747 match sym.symbol() { 657 match sym.symbol() {
748 SymbolType::View => { 658 SymbolType::View => {
@@ -754,7 +664,7 @@ impl<'a> TagBuilder<'a> {
754 Ok(BuildResult::View(View { 664 Ok(BuildResult::View(View {
755 name: name, 665 name: name,
756 source: ctx.source(), 666 source: ctx.source(),
757 outputs: outputs, 667 outputs: self.children.to_outputs(ctx)?,
758 //theme: Option<String> 668 //theme: Option<String>
759 layout: self.props.get("layout").cloned(), 669 layout: self.props.get("layout").cloned(),
760 })) 670 }))
@@ -767,7 +677,7 @@ impl<'a> TagBuilder<'a> {
767 }; 677 };
768 Ok(BuildResult::Output(Output { 678 Ok(BuildResult::Output(Output {
769 name: name, 679 name: name,
770 code: self.children, 680 code: self.children.to_tokens(ctx)?,
771 })) 681 }))
772 } 682 }
773 SymbolType::Loop => { 683 SymbolType::Loop => {
@@ -780,7 +690,9 @@ impl<'a> TagBuilder<'a> {
780 "Identifier for loop variable name.".into()) 690 "Identifier for loop variable name.".into())
781 ); 691 );
782 }; 692 };
783 Ok(BuildResult::Token(Token::Loop(id, self.children))) 693 Ok(BuildResult::Token(
694 Token::Loop(id, self.children.to_tokens(ctx)?)
695 ))
784 } 696 }
785 SymbolType::Show => { 697 SymbolType::Show => {
786 let id = if let ParamValue::Identifier(id) 698 let id = if let ParamValue::Identifier(id)
@@ -804,17 +716,25 @@ impl<'a> TagBuilder<'a> {
804 "Identifier for if variable name.".into()) 716 "Identifier for if variable name.".into())
805 ); 717 );
806 }; 718 };
807 if self.chain.len() == 1 && 719
808 let Token::If(id, mut children, chain) = self.chain[0] && 720 let is_else = if self.chain.len() == 1 &&
809 id.len() == 0 { 721 let Entry::TagBuilder(tb) = &self.chain[0] &&
722 tb.is_name(&SymbolType::Else) &&
723 tb.params.len() == 0 {
724 true
725 } else {
726 false
727 };
728
729 if is_else &&
730 let Entry::TagBuilder(mut tb) = self.chain.swap_remove(0) {
810 self.chain.clear(); 731 self.chain.clear();
811 self.chain.append(children); 732 self.chain.append(&mut tb.children);
812 } 733 }
813 Ok(BuildResult::Token(Token::If(id, self.children, self.chain))) 734 Ok(BuildResult::Token(Token::If(id, self.children.to_tokens(ctx)?, self.chain.to_tokens(ctx)?)))
814 } 735 }
815 SymbolType::ElIf => { 736 SymbolType::ElIf => {
816 let id = if let ParamValue::Identifier(id) 737 let id = if let ParamValue::Identifier(id) = self.params.swap_remove(0) {
817 = self.params.swap_remove(0) {
818 id 738 id
819 } else { 739 } else {
820 return Err(CrimError::parse( 740 return Err(CrimError::parse(
@@ -822,17 +742,37 @@ impl<'a> TagBuilder<'a> {
822 "Identifier for elif variable name.".into()) 742 "Identifier for elif variable name.".into())
823 ); 743 );
824 }; 744 };
825 Ok(BuildResult::Token(Token::If(Identifier::new(), self.children, self.chain))) 745
746 // this is copied from if, since they're the same this
747 // should be encapsulated and moved into a function...
748 // ...but I can't do that right now.
749 let is_else = if self.chain.len() == 1 &&
750 let Entry::TagBuilder(tb) = &self.chain[0] &&
751 tb.is_name(&SymbolType::Else) &&
752 tb.params.len() == 0 {
753 true
754 } else {
755 false
756 };
757
758 if is_else &&
759 let Entry::TagBuilder(mut tb) = self.chain.swap_remove(0) {
760 self.chain.clear();
761 self.chain.append(&mut tb.children);
762 }
763
764
765 Ok(BuildResult::Token(Token::If(id, self.children.to_tokens(ctx)?, self.chain.to_tokens(ctx)?)))
826 } 766 }
827 SymbolType::Else => { 767 SymbolType::Else => {
828 Ok(BuildResult::Token(Token::If(Identifier::new(), self.children, self.chain))) 768 Ok(BuildResult::Token(Token::If(Identifier::new(), self.children.to_tokens(ctx)?, self.chain.to_tokens(ctx)?)))
829 } 769 }
830 _ => { 770 _ => {
831 Err(CrimError::parse( self.start_pos, "Bad tag type".into())) 771 Err(CrimError::parse( self.start_pos(), "Bad tag type".into()))
832 } 772 }
833 } 773 }
834 } else { 774 } else {
835 Err(CrimError::parse(self.start_pos, "Bad tag type".into())) 775 Err(CrimError::parse(self.start_pos(), "Bad tag type".into()))
836 } 776 }
837 } 777 }
838} 778}