use crate::lexer::*; use crate::*; use core::error::Error; use std::fmt; #[derive(Debug)] struct ParseError { line: u32, row: u32, what: 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 { } 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()]; 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 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: 'fragment' 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: 'fragment' literal props * | 'section' literal * | 'output' literal * ; * * literal: '"' [^"]* '"' * ; * * props: props token '=' literal * | * ; */ impl Parser { pub fn new() -> Self { Self { } } fn lex_error(&self, error: &Symbol) -> Result<(),Box> { if let Symbol::Error{line,row,what} = error { Err(Box::new(ParseError { line: *line, row: *row, what: format!("What: {:?}", *what) })) } else { Err(Box::new(ParseError { line: 0, row: 0, what: "Not an error?".to_string() })) } } pub fn parse(&mut self, crim: &mut Crimtag, src: &str ) -> Result<(),Box> { let mut ll = Lexer::new( src ); let mut ctx = Context::new( ll ); // Parse the root of the file, the input context self.p_input( &mut ctx )?; Ok(()) } fn p_input(&mut self, ctx: &mut Context ) -> Result<(), Box> { loop { if ctx.cur().is_none() { break; } match ctx.cur() { Some(Symbol::Text(_)) => { /* Skip top level text */ } Some(Symbol::StartFlat) => { self.parse_tag( ctx ); } Some(Symbol::Error{..}) => { return self.lex_error( &ctx.cur().unwrap() ); } _ => { } } ctx.next(); } Ok(()) } fn parse_tag(&mut self, ctx: &mut Context) -> Result<(), Box> { let mut tb = TagBuilder::new(); let start_sym = ctx.cur().unwrap(); if let Some(Symbol::Token(s)) = ctx.next() { tb.set_name( s.to_string() ); } else { return Err(Box::new(ParseError{line: 0, row: 0, what: "Unexpecetd symbol".to_string()})); } self.parse_tag_params( ctx, &mut tb )?; self.parse_tag_props( ctx, &mut tb )?; Ok(()) } fn parse_tag_params(&mut self, ctx: &mut Context, tb: &mut TagBuilder ) -> Result<(), Box> { loop { if let Some(Symbol::Token(s)) = ctx.cur() { if matches!(ctx.peek(), Some(Symbol::Equals)) { // Done with parameters break; } else { tb.add_param( s.to_string() ); } } ctx.next(); } Ok(()) } fn parse_tag_props(&mut self, ctx: &mut Context, tb: &mut TagBuilder ) -> Result<(), Box> { loop { if let Some(Symbol::Token(s)) = ctx.cur() { if matches!(ctx.peek(), Some(Symbol::Equals)) { ctx.next(); if let Some(Symbol::Literal(lv)) = ctx.next() { tb.add_prop( s.to_string(), lv.to_string() ); } else { return Err(Box::new(ParseError{line: 0, row: 0, what: "Expected quoted literal string".to_string()})); } } else { return Err(Box::new(ParseError{line: 0, row: 0, what: "Expected = ".to_string()})); } } else { break; } ctx.next(); } Ok(()) } } enum TagType { Unknown, Unary, BinaryOpen, BinaryClose, } struct TagBuilder { name: String, params: Vec, props: Vec<(String,String)>, tag_type: TagType, } impl TagBuilder { pub fn new() -> TagBuilder { TagBuilder { name: String::new(), params: Vec::new(), props: Vec::new(), tag_type: TagType::Unknown, } } pub fn set_name(&mut self, name: String) { self.name = 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.push( (key, value) ); } pub fn set_type(&mut self, tag_type: TagType) { self.tag_type = tag_type; } }