use crate::lexer::*; use crate::*; use core::error::Error; use std::fmt; #[derive(Debug)] pub struct ParseError { position: Position, what: String, } impl ParseError { pub fn new( position: Position, what: &str ) -> Self { ParseError { position, what: what.to_string(), } } } impl fmt::Display for ParseError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Error parsing input: {}", "yeah") } } impl Error for ParseError { } pub type ParseResult = Result; struct Context<'a> { cur: [Option>;2], icur: usize, ll: Lexer<'a>, } impl<'a> Context<'a> { pub fn new( mut ll: Lexer<'a> ) -> Self { let cur = [ll.next(), ll.next()]; //println!(" - cur: {:?}, peek: {:?}", cur[0], cur[1] ); Self { cur, icur: 0, ll } } 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, f: T) -> ParseResult { if self.cur().is_none() || self.peek().is_none() { Err(ParseError{ position: Position::new(0,0), what: "Unexpected end of stream.".to_string() }) } else { Ok(f( self.cur().unwrap().symbol(), self.peek().unwrap().symbol())) } } } trait SymbolHelper { fn is bool>(&self, f: T ) -> ParseResult; fn is_valid_tag_name(&self) -> ParseResult; } impl<'a> SymbolHelper for Option> { fn is bool>(&self, f: T ) -> ParseResult { if let Some(st) = self { Ok(f( &st.symbol() )) } else { Err(ParseError{ position: Position::new(0,0), what: "Unexpected end of stream.".to_string() }) } } fn is_valid_tag_name(&self) -> ParseResult { if let Some(st) = self { Ok(match st.symbol() { SymbolType::Token(_) | SymbolType::View | SymbolType::Output | SymbolType::Show => true, _ => false }) } else { Err(ParseError{ position: Position::new(0,0), what: "Unexpeceted end of stream.".to_string() }) } } } pub struct Parser { } /** * input: input tag * | input tag_pair * | input text * | * ; * * tag: '[|' 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) -> ParseResult { if let SymbolType::Error{what} = error.symbol() { Err(ParseError { position: error.start().clone(), what: format!("What: {:?}", *what) }) } else { Err(ParseError { position: Position::new(0,0), what: "Not an error?".to_string(), }) } } pub fn parse(&self, src: &str ) -> ParseResult { let ll = Lexer::new( src ); let mut ctx = Context::new( ll ); // Parse the root of the file, the input context self.p_input( &mut ctx ) } fn p_input(&self, ctx: &mut Context ) -> ParseResult { let mut children = Vec::new(); loop { if ctx.cur().is_none() { break; } match ctx.cur().unwrap().symbol() { SymbolType::Text(_) => { /* Skip top level text */ } SymbolType::StartFlat => { children.push( self.parse_tag_view( ctx )? ); } SymbolType::Error{..} => { return self.lex_error( &ctx.cur().unwrap() ); } _ => { } } ctx.next(); } Ok(Token::Root(children)) } fn parse_tag_view(&self, ctx: &mut Context) -> ParseResult { let mut tb = self.parse_open_tag( ctx )?; if !tb.is_name( &SymbolType::View ) { return Err(ParseError{ position: tb.start_pos(), what: "Unexpected tag type, only frament allowed at root".to_string(), }); } if tb.is_unary() { return tb.build(); } let mut children = Vec::new(); //println!("--parse-tag-view-- tag parsed, next token: {:?}", ctx.cur() ); loop { if ctx.cur().is_none() { return Err(ParseError{ position: Position::new(0,0), what: "Unexpected end of stream.".to_string() }); } match ctx.cur().unwrap().symbol() { SymbolType::Text(s) => { children.push( Token::Text(s.to_string()) ); ctx.next(); } SymbolType::StartFlat => { //child.is_name(SymbolType::Output) children.push( self.parse_tag_body( ctx )? ); } SymbolType::StartPoint => { let end = self.parse_end_tag( ctx )?; //println!("End tag: {:?}, open tag: {:?}", end, tb.name() ); if tb.is_name(end.symbol()) { // They match, time to decide what we're doing. let any_outputs = children.iter().any( |t| matches!(t, Token::Output(..)) ); if children.iter().all(|t| matches!(t, Token::Text(..)) || matches!(t, Token::Output(..))) { // Everything is either text or output if !any_outputs { // Special case, if it's only text then we create an implicit output. tb.add_child(Token::Output("content".to_string(), Properties::new(), children )); } else { // Standard case, outputs are included, text is // skipped. for token in children { if matches!(token, Token::Output(..)) { tb.add_child( token ); } } } } else { // We have another mix, now check to see if there // are any outptus, if so this is an error. if any_outputs { return Err(ParseError{ position: *end.start(), what: "You cannot mix non-output and output tags in a view.".to_string() }); } else { // No outputs at all, we create an implict output tb.add_child(Token::Output("content".to_string(), Properties::new(), children )); } } return tb.build(); } else { // They don't match, complain. return Err(ParseError{ position: *end.start(), what: "Mismatched open and closing tags.".to_string() }); } } SymbolType::Error{..} => { return self.lex_error( &ctx.cur().unwrap() ); } _ => { ctx.next(); } } } } fn parse_open_tag<'a>(&self, ctx: &mut Context<'a>) -> ParseResult> { 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::Output => { let name_sym = ctx.cur().unwrap(); ctx.next(); tb.set_name( name_sym ); } _ => { return Err(ParseError{position: ctx.cur().unwrap().start().clone(), what: "Unexpecetd symbol".to_string()}); } } } else { return Err(ParseError{position: Position::new(0,0), what: "Unexpecetd end of stream".to_string()}); } self.parse_tag_params( ctx, &mut tb )?; self.parse_tag_props( ctx, &mut tb )?; if let Some(end_sym) = ctx.cur() { match end_sym.symbol() { SymbolType::EndPoint => { tb.set_type( TagType::BinaryOpen ); } SymbolType::EndFlat => { tb.set_type( TagType::Unary ); } _ => { return Err(ParseError{position: Position::new(0,0), what: "Unexpecetd end of stream".to_string()}); } } } ctx.next(); Ok(tb) } fn parse_end_tag<'a>(&self, ctx: &mut Context<'a> ) -> ParseResult> { if !ctx.cur().is(|s| *s == SymbolType::StartPoint )? { return Err(ParseError{ position: *ctx.cur().unwrap().start(), what: "Invalid end tag?".to_string(), }); } if !ctx.next().is_valid_tag_name()? { return Err(ParseError{ position: *ctx.cur().unwrap().start(), what: "Invalid tag name".to_string(), }); } let name = ctx.cur().unwrap(); if !ctx.next().is(|s| *s == SymbolType::EndFlat )? { return Err(ParseError{ position: *ctx.cur().unwrap().start(), what: "Tag should be <| |] style end tag.".to_string(), }); } Ok(name) } fn parse_tag_body(&self, ctx: &mut Context ) -> ParseResult { let mut tb = self.parse_open_tag( ctx )?; if tb.is_unary() { return tb.build(); } loop { if ctx.cur().is_none() { return Err(ParseError{position: Position::new(0,0), what: "Unexpecetd end of stream".to_string()}); } match ctx.cur().unwrap().symbol() { SymbolType::Text(s) => { tb.add_child(Token:: Text(s.to_string())); ctx.next(); } SymbolType::StartFlat => { tb.add_child( self.parse_tag_body( ctx )? ); } SymbolType::StartPoint => { let end = self.parse_end_tag( ctx )?; if tb.is_name(end.symbol()) { // They match, end. return tb.build(); } else { // They don't match, complain. return Err(ParseError{ position: *end.start(), what: "Mismatched open and closing tags.".to_string() }); } } SymbolType::Error{..} => { self.lex_error( &ctx.cur().unwrap() )?; } _ => { } } } } fn parse_tag_params(&self, ctx: &mut Context, tb: &mut TagBuilder ) -> ParseResult<()> { loop { if ctx.cur().is_none() { return Err(ParseError{position: Position::new(0,0), what: "Unexpecetd end of stream".to_string()}); } match ctx.cur().unwrap().symbol() { SymbolType::Literal(s) => { tb.add_param( s.to_string() ); } _ => { break; } } ctx.next(); } Ok(()) } fn parse_tag_props(&self, ctx: &mut Context, tb: &mut TagBuilder ) -> ParseResult<()> { loop { if ctx.cur().is_none() { return Err(ParseError{position: Position::new(0,0), what: "Unexpecetd end of stream".to_string()}); } 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(ParseError{position: Position::new(0,0), what: "Expected quoted literal string".to_string()}); } } else { return Err(ParseError{position:Position::new(0,0), what: "Expected = ".to_string()}); } } else { break; } ctx.next(); } Ok(()) } } #[derive(PartialEq,Copy,Clone,Debug)] enum TagType { Unknown, Unary, BinaryOpen, BinaryClose, } struct TagBuilder<'a> { name: Option>, params: Vec, props: Properties, children: Vec, tag_type: TagType, start_pos: Position, } impl<'a> TagBuilder<'a> { pub fn new(start: &Symbol) -> TagBuilder<'a> { TagBuilder { name: None, params: Vec::new(), props: Properties::new(), children: Vec::new(), tag_type: TagType::Unknown, start_pos: *start.start(), } } pub fn start_pos(&self) -> Position { self.start_pos } pub fn is_unary(&self) -> bool { if self.tag_type == TagType::Unary { true } else { false } } pub fn can_have_children(&self) -> bool { if self.tag_type == TagType::BinaryOpen { 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 set_name(&mut self, name: Symbol<'a>) { self.name = Some(name); } pub fn add_param(&mut self, param: String) { 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, token: Token) { self.children.push( token ); } pub fn append_children(&mut self, children: &mut Vec::) { self.children.append( children ); } 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) -> ParseResult { if let Some(sym) = self.name { match sym.symbol() { SymbolType::View => { Ok(Token::View(self.params.swap_remove(0), self.props, self.children)) } SymbolType::Output => { Ok(Token::Output(self.params.swap_remove(0), self.props, self.children)) } SymbolType::Show => { Ok(Token::Show(self.params.swap_remove(0), self.props)) } _ => { Err(ParseError { position: self.start_pos, what: "Bad tag type".to_string(), }) } } } else { Err(ParseError { position: self.start_pos, what: "Bad tag type".to_string(), }) } } }