From 92dbff3cda66a2a677f0c2306bb8508c64884445 Mon Sep 17 00:00:00 2001 From: Mike Buland Date: Thu, 11 Jun 2026 10:43:24 -0700 Subject: Broke out error and renamed it. I like the new name, now it could be for all sorts. --- src/error.rs | 45 ++++++++++++ src/lexer.rs | 6 ++ src/lib.rs | 27 ++++--- src/parser.rs | 221 ++++++++++++++++++++++++---------------------------------- 4 files changed, 158 insertions(+), 141 deletions(-) create mode 100644 src/error.rs diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..a2b6d96 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,45 @@ + +use core::error::Error; +use std::fmt; + +use crate::position::*; + +#[derive(Debug)] +pub struct CrimError { + position: Position, + what: String, +} + +impl CrimError { + pub fn new( position: Position, what: String ) -> Self { + CrimError { + position, + what, + } + } + + pub fn eos( context: &str ) -> Self { + CrimError { + position: Position::none(), + what: format!("Premature end of stream while looking for {}", context), + } + } + + pub fn what(&self) -> &str { + &self.what + } + + pub fn start(&self) -> &Position { + &self.position + } +} + +impl fmt::Display for CrimError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Error parsing input: {}", "yeah") + } +} + +impl Error for CrimError { } + +pub type CrimResult = Result; diff --git a/src/lexer.rs b/src/lexer.rs index 7228866..2fa29ce 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -22,6 +22,9 @@ pub enum SymbolType<'a> { Loop, Equals, Period, + If, + ElseIf, + Else, Token(&'a str), Literal(&'a str), Text(&'a str), @@ -246,6 +249,9 @@ impl<'a> Lexer<'a> { "show" => SymbolType::Show, "output" => SymbolType::Output, "loop" => SymbolType::Loop, + "if" => SymbolType::If, + "elseif" => SymbolType::ElseIf, + "else" => SymbolType::Else, _ => SymbolType::Token(s) }, start_pos, end_pos )) } diff --git a/src/lib.rs b/src/lib.rs index feb00f0..9b838f3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,8 +6,9 @@ use std::time::SystemTime; mod lexer; mod parser; mod position; +mod error; -pub use parser::ParseError; +pub use error::{CrimError,CrimResult}; pub use position::*; #[derive(Debug)] @@ -38,7 +39,7 @@ struct View { } impl View { - fn process(&self, input: &impl MappedStructure) -> Result { + fn process(&self, input: &impl MappedStructure) -> Result { let mut out_vars = Context::new(); for output in &self.outputs { let mut buf = String::new(); @@ -61,6 +62,9 @@ type Properties = HashMap; enum Token { Loop(Identifier, Vec), Show(Identifier,Properties), + If(Identifier, Vec), + ElseIf(Identifier, Vec), + Else(Vec), Text(String), } @@ -165,13 +169,13 @@ impl Crimtag { Ok(()) } - pub fn load_static(&mut self, data: &str) -> Result<(),ParseError> { + pub fn load_static(&mut self, data: &str) -> Result<(),CrimError> { let source_id = self.id_source( &ViewSource::Static ); let p = parser::Parser::new(); self.register_views( p.parse( &data, source_id )? ) } - pub fn load_external(&mut self, key: &str, data: &str) -> Result<(),ParseError> { + pub fn load_external(&mut self, key: &str, data: &str) -> Result<(),CrimError> { let source_id = self.id_source( &ViewSource::External{ key: key.to_string()} ); @@ -182,7 +186,7 @@ impl Crimtag { ) } - fn register_views(&mut self, views: Vec) -> Result<(),ParseError> { + fn register_views(&mut self, views: Vec) -> Result<(),CrimError> { for view in views { self.views.insert( view.name.clone(), view ); } @@ -201,15 +205,15 @@ impl Crimtag { } } - pub fn render_partial(&self, view: &str, input: &impl MappedStructure ) -> Result { + pub fn render_partial(&self, view: &str, input: &impl MappedStructure ) -> Result { if let Some(view) = self.views.get(view) { return view.process( input ) } else { - return Err(ParseError::new(Position::new(0,0),"No such view found")); + return Err(CrimError::new(Position::none(),"No such view found".into())); } } - pub fn render(&self, view: &str, input: &impl MappedStructure ) -> Result { + pub fn render(&self, view: &str, input: &impl MappedStructure ) -> Result { if let Some(view) = self.views.get( view ) { let mut output = view.process( input )?; if let Some(l) = &view.layout { @@ -220,17 +224,17 @@ impl Crimtag { Ok(s) } else { - Err(ParseError::new(Position::new(0,0),"No content found in root layout.")) + Err(CrimError::new(Position::none(), "No content found in root layout.".into())) } } } else { - Err(ParseError::new(Position::new(0,0),"No such view found")) + Err(CrimError::new(Position::none(),"No such view found".into())) } } } -fn exec(input: &impl MappedStructure, tokens: &Vec, buf: &mut String) -> Result<(),ParseError> { +fn exec(input: &impl MappedStructure, tokens: &Vec, buf: &mut String) -> Result<(),CrimError> { for token in tokens { match token { Token::Loop(ident,code) => { @@ -279,6 +283,7 @@ fn exec(input: &impl MappedStructure, tokens: &Vec, buf: &mut String) -> Token::Text(s) => { buf.push_str( s ); } + _ => {} } } Ok(()) diff --git a/src/parser.rs b/src/parser.rs index 07bbd93..83144fb 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,41 +1,6 @@ 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(), - } - } - - pub fn eos( context: &str ) -> Self { - ParseError { - position: Position::none(), - what: format!("Premature end of stream while looking for {}", context), - } - } -} - -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, @@ -70,9 +35,9 @@ impl<'a> Context<'a> { self.cur[(self.icur+1)%2] } - pub fn check_unwrapped bool>(&self, context: &str, f: T) -> ParseResult { + pub fn check_unwrapped bool>(&self, context: &str, f: T) -> CrimResult { if self.cur().is_none() || self.peek().is_none() { - Err(ParseError::eos( context )) + Err(CrimError::eos( context )) } else { Ok(f( self.cur().unwrap().symbol(), self.peek().unwrap().symbol())) } @@ -84,34 +49,37 @@ impl<'a> Context<'a> { } trait SymbolHelper { - fn is bool>(&self, context: &str, f: T ) -> ParseResult; - fn is_valid_tag_name(&self) -> ParseResult; + 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 ) -> ParseResult { + fn is bool>(&self, context: &str, f: T ) -> CrimResult { if let Some(st) = self { Ok(f( &st.symbol() )) } else { - Err(ParseError::eos( context )) + Err(CrimError::eos( context )) } } - fn is_valid_tag_name(&self) -> ParseResult { + 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::ElseIf | + SymbolType::Else | SymbolType::Show => true, _ => false }) } else { - Err(ParseError{ - position: Position::new(0,0), - what: "Unexpeceted end of stream.".to_string() - }) + Err(CrimError::new( + Position::none(), + "Unexpeceted end of stream.".to_string() + )) } } } @@ -202,21 +170,17 @@ impl Parser { } } - fn lex_error(&self, error: &Symbol) -> ParseResult { + fn lex_error(&self, error: &Symbol) -> CrimResult { if let SymbolType::Error{what} = error.symbol() { - Err(ParseError { - position: error.start().clone(), - what: format!("What: {:?}", *what) - }) + Err(CrimError::new( *error.start(), + format!("What: {:?}", *what) + )) } else { - Err(ParseError { - position: Position::new(0,0), - what: "Not an error?".to_string(), - }) + Err(CrimError::new( Position::none(), "Not an error?".into())) } } - pub fn parse(&self, src: &str, source: usize ) -> ParseResult> { + pub fn parse(&self, src: &str, source: usize ) -> CrimResult> { let ll = Lexer::new( src ); let mut ctx = Context::new( ll, source ); @@ -224,7 +188,7 @@ impl Parser { self.p_input( &mut ctx ) } - fn p_input(&self, ctx: &mut Context ) -> ParseResult> { + fn p_input(&self, ctx: &mut Context ) -> CrimResult> { let mut children = Vec::new(); loop { if ctx.cur().is_none() { @@ -247,13 +211,12 @@ impl Parser { Ok(children) } - fn parse_tag_view(&self, ctx: &mut Context) -> ParseResult { + fn parse_tag_view(&self, ctx: &mut Context) -> CrimResult { let 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(), - }); + return Err(CrimError::new(tb.start_pos(), + "Unexpected tag type, only frament allowed at root".into() + )); } if tb.is_unary() { @@ -265,7 +228,7 @@ impl Parser { //println!("--parse-tag-view-- tag parsed, next token: {:?}", ctx.cur() ); loop { if ctx.cur().is_none() { - return Err(ParseError::eos(format!("close tag for {:?}", tb.name()).as_str())) + return Err(CrimError::eos(format!("close tag for {:?}", tb.name()).as_str())) } match ctx.cur().unwrap().symbol() { SymbolType::Text(s) => { @@ -297,52 +260,56 @@ impl Parser { }); } else { if !all_text { - return Err(ParseError{ - position: *end.start(), - what: "You cannot mix non-output and output tags in a view.".to_string() - }); + return Err(CrimError::new( + *end.start(), + "You cannot mix non-output and output tags in a view.".to_string() + )); } } return tb.build_view(ctx,outputs); } else { // They don't match, complain. - return Err(ParseError{ - position: *end.start(), - what: "Mismatched open and closing tags.".to_string() - }); + return Err(CrimError::new( + *end.start(), + "Mismatched open and closing tags.".to_string() + )); } } SymbolType::Error{..} => { return self.lex_error( &ctx.cur().unwrap() ); } _ => { - return Err(ParseError{ - position: *ctx.cur().unwrap().start(), - what: format!("Unexpected tag in view root: {:?}", ctx.cur().unwrap().symbol()) - }); + return Err(CrimError::new( + *ctx.cur().unwrap().start(), + format!("Unexpected tag in view root: {:?}", ctx.cur().unwrap().symbol()) + )); } } } } - fn parse_open_tag<'a>(&self, ctx: &mut Context<'a>) -> ParseResult> { + fn parse_open_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::ElseIf | SymbolType::Else | 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: "Unexpected symbol".to_string()}); + return Err(CrimError::new( + *ctx.cur().unwrap().start(), + "Unexpected symbol".to_string() + )); } } } else { - return Err(ParseError::eos("tag type")); + return Err(CrimError::eos("tag type")); } self.parse_tag_params( ctx, &mut tb )?; @@ -357,9 +324,9 @@ impl Parser { tb.set_type( TagType::Unary ); } _ => { - return Err(ParseError::new( - end_sym.start().clone(), - format!("Unexpected symbol {:?} looking for end of tag.", end_sym.symbol()).as_str() + return Err(CrimError::new( + *end_sym.start(), + format!("Unexpected symbol {:?} looking for end of tag.", end_sym.symbol()) )); } } @@ -370,31 +337,31 @@ impl Parser { Ok(tb) } - fn parse_end_tag<'a>(&self, ctx: &mut Context<'a> ) -> ParseResult> { + fn parse_end_tag<'a>(&self, ctx: &mut Context<'a> ) -> CrimResult> { if !ctx.cur().is("end tag",|s| *s == SymbolType::StartPoint )? { - return Err(ParseError{ - position: *ctx.cur().unwrap().start(), - what: "Invalid end tag?".to_string(), - }); + return Err(CrimError::new( + *ctx.cur().unwrap().start(), + "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(), - }); + return Err(CrimError::new( + *ctx.cur().unwrap().start(), + "Invalid tag name".to_string() + )); } let name = ctx.cur().unwrap(); if !ctx.next().is("end of end tag", |s| *s == SymbolType::EndFlat )? { - return Err(ParseError{ - position: *ctx.cur().unwrap().start(), - what: "Tag should be <| |] style end tag.".to_string(), - }); + return Err(CrimError::new( + *ctx.cur().unwrap().start(), + "Tag should be <| |] style end tag.".to_string(), + )); } ctx.next(); Ok(name) } - fn parse_tag_body<'a>(&self, ctx: &mut Context<'a> ) -> ParseResult> { + fn parse_tag_body<'a>(&self, ctx: &mut Context<'a> ) -> CrimResult> { let mut tb = self.parse_open_tag( ctx )?; if tb.is_unary() { return Ok(tb); @@ -402,7 +369,7 @@ impl Parser { loop { if ctx.cur().is_none() { - return Err(ParseError::eos(format!("close tag for {:?}", tb.name()).as_str())); + return Err(CrimError::eos(format!("close tag for {:?}", tb.name()).as_str())); } match ctx.cur().unwrap().symbol() { SymbolType::Text(s) => { @@ -419,29 +386,29 @@ impl Parser { return Ok(tb); } else { // They don't match, complain. - return Err(ParseError{ - position: *end.start(), - what: "Mismatched open and closing tags.".to_string() - }); + return Err(CrimError::new( + *end.start(), + "Mismatched open and closing tags.".to_string() + )); } } SymbolType::Error{..} => { return self.lex_error( &ctx.cur().unwrap() ); } _ => { - return Err(ParseError{ - position: ctx.cur().unwrap().start().clone(), - what: format!("Unexpected token: {:?}", ctx.cur().unwrap().symbol()), - }); + return Err(CrimError::new( + *ctx.cur().unwrap().start(), + format!("Unexpected token: {:?}", ctx.cur().unwrap().symbol()), + )); } } } } - fn parse_tag_params(&self, ctx: &mut Context, tb: &mut TagBuilder ) -> ParseResult<()> { + fn parse_tag_params(&self, ctx: &mut Context, tb: &mut TagBuilder ) -> CrimResult<()> { loop { if ctx.cur().is_none() { - return Err(ParseError::eos("tag parameters")); + return Err(CrimError::eos("tag parameters")); } match ctx.cur().unwrap().symbol() { SymbolType::Literal(s) => { @@ -462,7 +429,7 @@ impl Parser { Ok(()) } - fn parse_identifier(&self, ctx: &mut Context ) -> ParseResult { + fn parse_identifier(&self, ctx: &mut Context ) -> CrimResult { let mut id = Identifier::new(); loop { @@ -471,7 +438,7 @@ impl Parser { id.push( IdentifierValue::Name(s.to_string()) ); } } else { - return Err(ParseError::new( ctx.cur().unwrap().start().clone(), &format!("Expeceted identifier, found {:?}", ctx.cur().unwrap().symbol() ) ) ); + return Err(CrimError::new( ctx.cur().unwrap().start().clone(), format!("Expeceted identifier, found {:?}", ctx.cur().unwrap().symbol() ) ) ); } if !ctx.next().is("identifier seperator",|s| *s == SymbolType::Period )? { @@ -483,10 +450,10 @@ impl Parser { Ok(ParamValue::Identifier(id)) } - fn parse_tag_props(&self, ctx: &mut Context, tb: &mut TagBuilder ) -> ParseResult<()> { + fn parse_tag_props(&self, ctx: &mut Context, tb: &mut TagBuilder ) -> CrimResult<()> { loop { if ctx.cur().is_none() { - return Err(ParseError::eos("tag properties")); + 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))) { @@ -495,7 +462,7 @@ impl Parser { 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()}); + return Err(CrimError::new(Position::none(),"Expected quoted literal string".to_string())); } } else { break; @@ -607,13 +574,13 @@ impl<'a> TagBuilder<'a> { self.tag_type } - pub fn build_view(mut self, ctx: &Context, outputs: Vec) -> ParseResult { + pub fn build_view(mut self, ctx: &Context, outputs: Vec) -> CrimResult { if let Some(sym) = self.name { if *sym.symbol() == SymbolType::View { let name = if let ParamValue::Literal(s) = self.params.swap_remove(0) { s.to_string() } else { - return Err(ParseError::new(Position::none(), "Expected string literal for view name.")); + return Err(CrimError::new(Position::none(), "Expected string literal for view name.".to_string())); }; Ok(View { name: name, @@ -623,41 +590,41 @@ impl<'a> TagBuilder<'a> { layout: self.props.get("layout").cloned(), }) } else { - Err(ParseError::new(self.start_pos, &format!("Expected tag type view, found {:?}", sym.symbol()))) + Err(CrimError::new(self.start_pos, format!("Expected tag type view, found {:?}", sym.symbol()))) } } else { - Err(ParseError::new(self.start_pos, "Broken Parser? No tag type found when building view.")) + Err(CrimError::new(self.start_pos, "Broken Parser? No tag type found when building view.".to_string())) } } - pub fn build_output(mut self) -> ParseResult { + pub fn build_output(mut self) -> CrimResult { if let Some(sym) = self.name { if *sym.symbol() == SymbolType::Output { let name = if let ParamValue::Literal(s) = self.params.swap_remove(0) { s.to_string() } else { - return Err(ParseError::new(Position::none(), "Expected string literal for output name.")); + return Err(CrimError::new(Position::none(), "Expected string literal for output name.".into())); }; Ok(Output { name: name, code: self.children, }) } else { - Err(ParseError::new(self.start_pos, &format!("Expected tag type view, found {:?}", sym.symbol()))) + Err(CrimError::new(self.start_pos, format!("Expected tag type view, found {:?}", sym.symbol()))) } } else { - Err(ParseError::new(self.start_pos, "Broken Parser? No tag type found when building view.")) + Err(CrimError::new(self.start_pos, "Broken Parser? No tag type found when building view.".into())) } } - pub fn build(mut self) -> ParseResult { + pub fn build(mut self) -> CrimResult { if let Some(sym) = self.name { match sym.symbol() { SymbolType::Loop => { let id = if let ParamValue::Identifier(id) = self.params.swap_remove(0) { id } else { - return Err(ParseError::new(Position::none(), "Identifier for loop variable name.")); + return Err(CrimError::new(Position::none(), "Identifier for loop variable name.".into())); }; Ok(Token::Loop(id, self.children)) } @@ -665,22 +632,16 @@ impl<'a> TagBuilder<'a> { let id = if let ParamValue::Identifier(id) = self.params.swap_remove(0) { id } else { - return Err(ParseError::new(Position::none(), "Identifier for show variable name.")); + return Err(CrimError::new(Position::none(), "Identifier for show variable name.".into())); }; Ok(Token::Show(id, self.props)) } _ => { - Err(ParseError { - position: self.start_pos, - what: "Bad tag type".to_string(), - }) + Err(CrimError::new( self.start_pos, "Bad tag type".into())) } } } else { - Err(ParseError { - position: self.start_pos, - what: "Bad tag type".to_string(), - }) + Err(CrimError::new(self.start_pos, "Bad tag type".into())) } } } -- cgit v1.2.3