From 11f5bc0c5a8bcfb6ac8fef8abc33297c4addc81b Mon Sep 17 00:00:00 2001 From: Mike Buland Date: Fri, 5 Jun 2026 11:28:07 -0700 Subject: Started on the parser. Thinking about some tweaks. - Creating a LookaheadIterator as a general class that could be used. - Wrap symbols in a symbol struct that tracks symbol start and end position for error reporting. - Make a struct for position in a file as well. --- src/parser.rs | 280 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 280 insertions(+) create mode 100644 src/parser.rs (limited to 'src/parser.rs') diff --git a/src/parser.rs b/src/parser.rs new file mode 100644 index 0000000..54b77ac --- /dev/null +++ b/src/parser.rs @@ -0,0 +1,280 @@ +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; + } +} -- cgit v1.2.3