use crate::lexer::*; use crate::*; struct Context<'a> { cur: [Option>;2], icur: usize, ll: Lexer<'a>, source: usize, } impl<'a> Context<'a> { pub fn new( mut ll: Lexer<'a>, source: usize ) -> Self { let cur = [ll.next(), ll.next()]; //println!(" - cur: {:?}, peek: {:?}", cur[0], cur[1] ); Self { cur, icur: 0, ll, source, } } pub fn next(&mut self) -> Option> { self.cur[self.icur] = self.ll.next(); self.icur = (self.icur+1)%2; //println!(" - cur: {:?}, peek: {:?}", self.cur(), self.peek() ); self.cur[self.icur] } pub fn cur(&self) -> Option> { self.cur[self.icur] } pub fn peek(&self) -> Option> { self.cur[(self.icur+1)%2] } pub fn check_unwrapped bool>(&self, context: &str, f: T) -> CrimResult { if self.cur().is_none() || self.peek().is_none() { Err(CrimError::eos( context )) } else { Ok(f( self.cur().unwrap().symbol(), self.peek().unwrap().symbol())) } } pub fn source(&self) -> usize { self.source } } trait SymbolHelper { fn is bool>(&self, context: &str, f: T ) -> CrimResult; fn is_valid_tag_name(&self) -> CrimResult; } impl<'a> SymbolHelper for Option> { fn is bool>(&self, context: &str, f: T ) -> CrimResult { if let Some(st) = self { Ok(f( &st.symbol() )) } else { Err(CrimError::eos( context )) } } fn is_valid_tag_name(&self) -> CrimResult { if let Some(st) = self { Ok(match st.symbol() { SymbolType::Token(_) | SymbolType::View | SymbolType::Output | SymbolType::Loop | SymbolType::If | SymbolType::ElIf | SymbolType::Else | SymbolType::Show => true, _ => false }) } else { Err(CrimError::parse( Position::none(), "Unexpeceted end of stream.".to_string() )) } } } pub struct Parser { } /** * input: input complete_tag * | input text * | * ; * * tag: unary_tag * | multinary_open_tag * | multinary_mid_tag * | multinary_close_tag * ; * * unary_tag: '[|' tag_guts '|]' * ; * * | '[|' tag_guts '|>' * | '<|' tag_guts '|>' * | '<|' tag_guts '|]' * ; * * tag_pair: open_tag tag_body close_tag * ; * * tag_body: tag_body tag * | tag_body tag_pair * | tag_body text * | * ; * * open_tag: '[|' tag_guts '|>' * ; * * close_tag: '<|' token '|]' * ; * * tag_guts: 'view' literal props * | 'section' literal * | 'output' literal * ; * * literal: '"' [^"]* '"' * ; * * props: props token '=' literal * | * ; * * disambiguate (tm): * * input: input tag * | input text * | * ; * * tag: open_tag_base unary_tag * | open_tag_base binary_tag * ; * * open_tag_base: '[|' tag_guts * ; * * unary_tag: '|]' * ; * * binary_tag: '|>' tag_body close_tag * ; * * tag_body: tag_body tag * | tag_body text * | * ; * * close_tag: '<|' token '|]' * ; * * tag_guts: 'view' literal props * | 'section' literal * | 'output' literal * ; * * literal: '"' [^"]* '"' * ; * * props: props token '=' literal * | * ; */ impl Parser { pub fn new() -> Self { Self { } } fn lex_error(&self, error: &Symbol) -> CrimResult { if let SymbolType::Error{what} = error.symbol() { Err(CrimError::parse( *error.start(), format!("What: {:?}", *what) )) } else { Err(CrimError::parse( Position::none(), "Not an error?".into())) } } pub fn parse(&self, src: &str, source: usize ) -> CrimResult> { let ll = Lexer::new( src ); let mut ctx = Context::new( ll, source ); // Parse the root of the file, the input context self.p_input( &mut ctx ) } fn p_input(&self, ctx: &mut Context ) -> CrimResult> { let mut tags = Vec::new(); loop { if ctx.cur().is_none() { break; } match ctx.cur().unwrap().symbol() { SymbolType::Text(_) => { /* Skip top level text */ } SymbolType::StartFlat => { let tb = self.parse_tag( ctx )?; let tb = self.parse_tag_set( ctx, tb )?; tags.push( tb ); } SymbolType::Error{..} => { self.lex_error( &ctx.cur().unwrap() )?; } _ => { return Err(CrimError::parse( *ctx.cur().unwrap().start(), format!("Unexpected symbol {:?} found looking for tags.", ctx.cur().unwrap().symbol() ) )); } } ctx.next(); } let mut views = Vec::new(); for tag in tags { let start_pos = tag.start_pos(); let name = tag.name().clone(); if tag.is_name(&SymbolType::View) && let BuildResult::View(view) = tag.build( ctx )? { views.push( view ); } else { return Err(CrimError::parse( start_pos, format!("Expected view at root, found {:?}", name ) )); } } Ok(views) } fn parse_tag<'a>(&self, ctx: &mut Context<'a>) -> CrimResult> { let start_sym = ctx.cur().unwrap(); let mut tb = TagBuilder::new(&start_sym); if ctx.next().is_some() { match ctx.cur().unwrap().symbol() { SymbolType::View | SymbolType::Show | SymbolType::Loop | SymbolType::If | SymbolType::ElIf | SymbolType::Else | SymbolType::Output => { let name_sym = ctx.cur().unwrap(); ctx.next(); tb.set_name( name_sym ); } _ => { return Err(CrimError::parse( *ctx.cur().unwrap().start(), "Unexpected symbol".to_string() )); } } } else { return Err(CrimError::eos("tag type")); } if tb.can_have_params() || tb.can_have_expr() { self.parse_tag_params( ctx, &mut tb )?; } if tb.can_have_props() { self.parse_tag_props( ctx, &mut tb )?; } if let Some(end_sym) = ctx.cur() { match end_sym.symbol() { SymbolType::EndPoint | SymbolType::EndFlat => { tb.set_end( &end_sym )?; } _ => { return Err(CrimError::parse( *end_sym.start(), format!("Unexpected symbol {:?} looking for end of tag.", end_sym.symbol()) )); } } } ctx.next(); Ok(tb) } fn parse_tag_set<'a>(&self, ctx: &mut Context<'a>, mut base: TagBuilder<'a>) -> CrimResult> { if base.is_unary() { return Ok(base); } loop { if ctx.cur().is_none() { return Err(CrimError::eos(format!("close tag for {:?}", base.name()).as_str())); } match ctx.cur().unwrap().symbol() { SymbolType::Text(s) => { base.add_child(Entry::Token(Token::Text(s.to_string()))); ctx.next(); } SymbolType::StartFlat | SymbolType::StartPoint => { let tb = self.parse_tag( ctx )?; match tb.tag_type() { TagType::MultinaryOpen => { base.add_child(Entry::TagBuilder(self.parse_tag_set( ctx, tb )?)); } TagType::MultinaryMid => { base.add_chain(Entry::TagBuilder(self.parse_tag_set( ctx, tb )?)); return Ok(base); } TagType::MultinaryClose => { if (tb.is_name(&SymbolType::If) && (base.is_name(&SymbolType::ElIf) || base.is_name(&SymbolType::Else))) || tb.is_name(base.name().unwrap().symbol()) { return Ok(base); } else { return Err(CrimError::parse( *tb.name().unwrap().start(), format!("Found {:?} looking for close of {:?}", tb.name().unwrap().symbol(), base.name().unwrap().symbol()) )); } } TagType::Unary => { base.add_child(Entry::TagBuilder(tb)); } TagType::Unknown => { return Err(CrimError::broken(base.start_pos(), "Found unknown tag when looking for something else.")); } } } SymbolType::Error{..} => { return self.lex_error( &ctx.cur().unwrap() ); } _ => { return Err(CrimError::parse( *ctx.cur().unwrap().start(), format!("Unexpected token: {:?}", ctx.cur().unwrap().symbol()), )); } } } } fn parse_tag_params(&self, ctx: &mut Context, tb: &mut TagBuilder ) -> CrimResult<()> { loop { if ctx.cur().is_none() { return Err(CrimError::eos("tag parameters")); } match ctx.cur().unwrap().symbol() { SymbolType::Literal(s) => { tb.add_param( ParamValue::Literal(s.to_string()) ); ctx.next(); } SymbolType::Token(_) | SymbolType::Sharp => { if ctx.peek().is("tag data", |s| *s == SymbolType::Equals)? { break; } tb.add_param( self.parse_identifier( ctx )? ); } _ => { break; } } } Ok(()) } fn parse_identifier(&self, ctx: &mut Context ) -> CrimResult { let mut id = Identifier::new(); if ctx.cur().is("identifier",|s| matches!( s, SymbolType::Sharp))? { id.push( IdentifierValue::Root ); ctx.next(); } loop { if ctx.cur().is("identifier",|s| matches!( s, SymbolType::Token(_) ))? { if let SymbolType::Token(s) = ctx.cur().unwrap().symbol() { id.push( IdentifierValue::Name(s.to_string()) ); } } else { return Err(CrimError::parse( ctx.cur().unwrap().start().clone(), format!("Expeceted identifier, found {:?}", ctx.cur().unwrap().symbol() ) ) ); } if !ctx.next().is("identifier seperator",|s| *s == SymbolType::Period )? { break; } ctx.next(); } Ok(ParamValue::Identifier(id)) } fn parse_tag_props(&self, ctx: &mut Context, tb: &mut TagBuilder ) -> CrimResult<()> { loop { if ctx.cur().is_none() { return Err(CrimError::eos("tag properties")); } if let SymbolType::Token(s) = ctx.cur().unwrap().symbol() { if ctx.peek().is_some_and(|s|s.check_type(|t| matches!(t,SymbolType::Equals))) { ctx.next(); if let Some(sym) = ctx.next() && let SymbolType::Literal(lv) = sym.symbol() { tb.add_prop( s.to_string(), lv.to_string() ); } else { return Err(CrimError::parse(Position::none(),"Expected quoted literal string".to_string())); } } else { break; } } else { break; } ctx.next(); } Ok(()) } } #[derive(PartialEq,Copy,Clone,Debug)] enum TagType { Unknown, Unary, MultinaryOpen, MultinaryMid, MultinaryClose, } #[derive(Debug)] enum Entry<'a>{ Token(Token), TagBuilder(TagBuilder<'a>), } type EntryList<'a> = Vec>; trait EntryListConverter { fn has_outputs(&self) -> bool; fn to_outputs(&mut self, ctx: &mut Context ) -> CrimResult>; fn to_tokens(&mut self, ctx: &mut Context) -> CrimResult>; } impl<'a> EntryListConverter for EntryList<'a> { fn has_outputs(&self) -> bool { self.iter().any(|e| if let Entry::TagBuilder(tb) = e && tb.is_name(&SymbolType::Output) { true } else { false } ) } fn to_outputs(&mut self, ctx: &mut Context) -> CrimResult> { let mut outputs = Vec::new(); if self.has_outputs() { // We have an explicit output, we cant have anything else for e in self.drain(..) { let tb = if let Entry::TagBuilder(tb) = e { tb } else { continue; }; if let BuildResult::Output(out) = tb.build(ctx)? { outputs.push( out ); } } } else { // No outputs, so we create one implicit output outputs.push(Output { name: "content".into(), code: self.to_tokens(ctx)? }); } Ok(outputs) } fn to_tokens(&mut self, ctx: &mut Context) -> CrimResult> { let mut tokens : Vec = Vec::new(); for e in self.drain(..) { match e { Entry::Token(token) => { tokens.push( token ); } Entry::TagBuilder(tb) => { if let BuildResult::Token(token) = tb.build(ctx)? { tokens.push( token ); } else { return Err(CrimError::broken( Position::none(), "Non-token result built.")); } } } } Ok(tokens) } } #[derive(Debug)] struct TagBuilder<'a> { name: Option>, params: Vec, props: Properties, children: Vec>, tag_type: TagType, start: Symbol<'a>, chain: Vec>, } #[derive(Debug)] enum ParamValue { Literal(String), Identifier(Identifier), } enum BuildResult { View(View), Output(Output), Token(Token), } impl<'a> TagBuilder<'a> { pub fn new(start: &Symbol<'a>) -> TagBuilder<'a> { TagBuilder { name: None, params: Vec::new(), props: Properties::new(), children: Vec::new(), tag_type: TagType::Unknown, start: start.clone(), chain: Vec::new(), } } pub fn set_end(&mut self, end: &Symbol) -> CrimResult<()> { if *self.start.symbol() == SymbolType::StartFlat { if *end.symbol() == SymbolType::EndFlat { self.tag_type = TagType::Unary; } else if *end.symbol() == SymbolType::EndPoint { self.tag_type = TagType::MultinaryOpen; } else { return Err(CrimError::broken( *end.start(), "Invalid bracket token type.")); } } else if *self.start.symbol() == SymbolType::StartPoint { if *end.symbol() == SymbolType::EndFlat { self.tag_type = TagType::MultinaryClose; } else if *end.symbol() == SymbolType::EndPoint { self.tag_type = TagType::MultinaryMid; } else { return Err(CrimError::broken( *end.start(), "Invalid bracket token type.")); } } else { return Err(CrimError::broken( *self.start.start(), "Invalid bracket token type.")); } Ok(()) } pub fn start_pos(&self) -> Position { *self.start.start() } pub fn is_unary(&self) -> bool { self.tag_type == TagType::Unary } pub fn is_multinary_open(&self) -> bool { self.tag_type == TagType::MultinaryOpen } pub fn can_have_params(&self) -> bool { match self.name.unwrap().symbol() { SymbolType::View | SymbolType::Output | SymbolType::Loop => true, SymbolType::Show | SymbolType::If | SymbolType::ElIf | SymbolType::Else => false, _ => false, } } pub fn can_have_expr(&self) -> bool { match self.name.unwrap().symbol() { SymbolType::View | SymbolType::Output | SymbolType::Loop => false, SymbolType::Show | SymbolType::If | SymbolType::ElIf | SymbolType::Else => true, _ => false, } } pub fn can_have_props(&self) -> bool { true } pub fn can_have_children(&self) -> bool { if self.tag_type == TagType::MultinaryOpen || self.tag_type == TagType::MultinaryMid { true } else { false } } pub fn is_name(&self, name: &SymbolType<'a>) -> bool { if let Some(a) = self.name { a.symbol() == name } else { false } } pub fn name(&self) -> Option> { self.name } pub fn params(&self) -> &Vec { &self.params } pub fn set_name(&mut self, name: Symbol<'a>) { self.name = Some(name); } pub fn add_param(&mut self, param: ParamValue) { self.params.push( param ); } pub fn add_prop(&mut self, key: String, value: String) { self.props.insert( key, value ); } pub fn add_child(&mut self, tb: Entry<'a>) { self.children.push( tb ); } pub fn append_children(&mut self, children: &mut Vec::>) { self.children.append( children ); } pub fn add_chain(&mut self, tb: Entry<'a>) { self.chain.push( tb ); } pub fn set_type(&mut self, tag_type: TagType) { self.tag_type = tag_type; } pub fn tag_type(&self) -> TagType { self.tag_type } pub fn build(mut self, ctx: &mut Context ) -> CrimResult { if let Some(sym) = self.name { match sym.symbol() { SymbolType::View => { let name = if let ParamValue::Literal(s) = self.params.swap_remove(0) { s.to_string() } else { return Err(CrimError::parse(Position::none(), "Expected string literal for view name.".to_string())); }; Ok(BuildResult::View(View { name: name, source: ctx.source(), outputs: self.children.to_outputs(ctx)?, //theme: Option layout: self.props.get("layout").cloned(), })) } SymbolType::Output => { let name = if let ParamValue::Literal(s) = self.params.swap_remove(0) { s.to_string() } else { return Err(CrimError::parse(Position::none(), "Expected string literal for output name.".into())); }; Ok(BuildResult::Output(Output { name: name, code: self.children.to_tokens(ctx)?, })) } SymbolType::Loop => { let id = if let ParamValue::Identifier(id) = self.params.swap_remove(0) { id } else { return Err(CrimError::parse( Position::none(), "Identifier for loop variable name.".into()) ); }; Ok(BuildResult::Token( Token::Loop(id, self.children.to_tokens(ctx)?) )) } SymbolType::Show => { let id = if let ParamValue::Identifier(id) = self.params.swap_remove(0) { id } else { return Err(CrimError::parse( Position::none(), "Identifier for show variable name.".into()) ); }; Ok(BuildResult::Token(Token::Show(id, self.props))) } SymbolType::If => { let id = if let ParamValue::Identifier(id) = self.params.swap_remove(0) { id } else { return Err(CrimError::parse( Position::none(), "Identifier for if variable name.".into()) ); }; let is_else = if self.chain.len() == 1 && let Entry::TagBuilder(tb) = &self.chain[0] && tb.is_name(&SymbolType::Else) && tb.params.len() == 0 { true } else { false }; if is_else && let Entry::TagBuilder(mut tb) = self.chain.swap_remove(0) { self.chain.clear(); self.chain.append(&mut tb.children); } Ok(BuildResult::Token(Token::If(id, self.children.to_tokens(ctx)?, self.chain.to_tokens(ctx)?))) } SymbolType::ElIf => { let id = if let ParamValue::Identifier(id) = self.params.swap_remove(0) { id } else { return Err(CrimError::parse( Position::none(), "Identifier for elif variable name.".into()) ); }; // this is copied from if, since they're the same this // should be encapsulated and moved into a function... // ...but I can't do that right now. let is_else = if self.chain.len() == 1 && let Entry::TagBuilder(tb) = &self.chain[0] && tb.is_name(&SymbolType::Else) && tb.params.len() == 0 { true } else { false }; if is_else && let Entry::TagBuilder(mut tb) = self.chain.swap_remove(0) { self.chain.clear(); self.chain.append(&mut tb.children); } Ok(BuildResult::Token(Token::If(id, self.children.to_tokens(ctx)?, self.chain.to_tokens(ctx)?))) } SymbolType::Else => { Ok(BuildResult::Token(Token::If(Identifier::new(), self.children.to_tokens(ctx)?, self.chain.to_tokens(ctx)?))) } _ => { Err(CrimError::parse( self.start_pos(), "Bad tag type".into())) } } } else { Err(CrimError::parse(self.start_pos(), "Bad tag type".into())) } } }