From 9083d5843f5a39fafacde81242b817021f9cb818 Mon Sep 17 00:00:00 2001 From: Mike Buland Date: Thu, 11 Jun 2026 15:20:49 -0700 Subject: Halfway there. About half the code is going away. I'm learning better how to take advantage of features of rust to simplify the code a lot. It's actually really cool. --- src/error.rs | 7 ++ src/lexer.rs | 6 +- src/lib.rs | 4 +- src/parser.rs | 297 +++++++++++++++++++++++++++++++++++++++++++++------------- 4 files changed, 246 insertions(+), 68 deletions(-) diff --git a/src/error.rs b/src/error.rs index a2349ee..bbb5d6f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -25,6 +25,13 @@ impl CrimError { } } + pub fn broken( position: Position, what: &str ) -> Self { + Self { + position, + what: format!("Broken parser? {}", what), + } + } + pub fn eos( context: &str ) -> Self { Self { position: Position::none(), diff --git a/src/lexer.rs b/src/lexer.rs index 2bf7e9e..afff5a8 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -16,15 +16,17 @@ pub enum SymbolType<'a> { StartPoint, EndFlat, EndPoint, + View, Output, Show, Loop, - Equals, - Period, If, ElIf, Else, + + Equals, + Period, Token(&'a str), Literal(&'a str), Text(&'a str), diff --git a/src/lib.rs b/src/lib.rs index 8c91686..2b53828 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -64,9 +64,7 @@ type Properties = HashMap; enum Token { Loop(Identifier, Vec), Show(Identifier,Properties), - If(Identifier, Vec), - ElIf(Identifier, Vec), - Else(Vec), + If(Identifier, Vec, Vec), Text(String), } diff --git a/src/parser.rs b/src/parser.rs index 6ba6cfe..b681fce 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -221,11 +221,116 @@ impl Parser { Ok(children) } + fn parse_tag(&self, ctx: &mut Context) -> 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; base: &mut TagBuilder) -> CrimResult { + if base.is_unary() { + return Ok(base.build()); + } + if !base.is_multinary_open() { + return Err(CrimError::broken("Found close/mid tag instead of opening")); + } + + 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(Token::Text(s.to_string())); + ctx.next(); + } + SymbolType::StartFlat | SymbolType::StartPoint => { + let mut tb = self.parse_tag( ctx )?; + + match tb.tag_type() { + TagType::MultinaryOpen => { + base.add_child( parse_tag_set( ctx, tb )? ); + } + TagType::MultinaryMid => { + base.add_chain( parse_tag_set( ctx, tb )? ); + } + TagType::MultinaryClose => { + if (tb.is_name(SymbolToken::If) && + (base.is_name(SymbolToken::Elif) || + base.is_name(SymbolToken::Else))) || + tb.is_name(base.name().symbol()) { + + } else { + return Err(CrimError::parse( + *tb.name().start(), + format!("Found {:?} looking for close of {:?}", tb.name().symbol(), base.name().symbol()) + )); + } + } + } + } + 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_view(&self, ctx: &mut Context) -> CrimResult { let tb = self.parse_open_tag( ctx )?; if !tb.is_name( &SymbolType::View ) { return Err(CrimError::parse(tb.start_pos(), - "Unexpected tag type, only frament allowed at root".into() + "Unexpected tag type, only view allowed at root".into() )); } @@ -246,7 +351,7 @@ impl Parser { ctx.next(); } SymbolType::StartFlat => { - let subtb = self.parse_tag_body( ctx )?; + let subtb = self.parse_tag( ctx )?; if subtb.is_name(&SymbolType::Output) { outputs.push(subtb.build_output()?); } else { @@ -492,6 +597,7 @@ enum TagType { Unary, MultinaryOpen, MultinaryMid, + MultinaryClose, } struct TagBuilder<'a> { @@ -500,7 +606,8 @@ struct TagBuilder<'a> { props: Properties, children: Vec, tag_type: TagType, - start_pos: Position, + start: Symbol<'a>, + chain: Vec, } #[derive(Debug)] @@ -509,30 +616,79 @@ enum ParamValue { Identifier(Identifier), } +enum BuildResult { + View(View), + Output(Output), + Token(Token), +} + impl<'a> TagBuilder<'a> { - pub fn new(start: &Symbol) -> 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_pos: *start.start(), + start: start.clone(), + chain: Vec::new(), + } + } + + pub fn set_end(&mut self, end: &Symbol) -> CrimResult<()> { + if 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( *self.end.start(), "Invalid bracket token type.")); + } + } else if 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( *self.end.start(), "Invalid bracket token type.")); + } + } else { + return Err(CrimError::broken( *self.start.start(), "Invalid bracket token type.")); } } pub fn start_pos(&self) -> Position { - self.start_pos + self.start.start() } pub fn is_unary(&self) -> bool { - if self.tag_type == TagType::Unary { - true - } else { - false + 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.symbol() { + SymbolType::View | SymbolType::Output | SymbolType::Loop => true, + SymbolType::Show | SymbolType::If | SymbolType::ElIf | + SymbolType::Else => false, } } + pub fn can_have_expr(&self) -> bool { + match self.name.symbol() { + SymbolType::View | SymbolType::Output | SymbolType::Loop => false, + SymbolType::Show | SymbolType::If | SymbolType::ElIf | + SymbolType::Else => true, + } + } + + 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 { @@ -586,75 +742,90 @@ impl<'a> TagBuilder<'a> { self.tag_type } - 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(CrimError::parse(Position::none(), "Expected string literal for view name.".to_string())); - }; - Ok(View { - name: name, - source: ctx.source(), - outputs: outputs, - //theme: Option - layout: self.props.get("layout").cloned(), - }) - } else { - Err(CrimError::parse(self.start_pos, format!("Expected tag type view, found {:?}", sym.symbol()))) - } - } else { - Err(CrimError::parse(self.start_pos, "Broken Parser? No tag type found when building view.".to_string())) - } - } - - 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(CrimError::parse(Position::none(), "Expected string literal for output name.".into())); - }; - Ok(Output { - name: name, - code: self.children, - }) - } else { - Err(CrimError::parse(self.start_pos, format!("Expected tag type view, found {:?}", sym.symbol()))) - } - } else { - Err(CrimError::parse(self.start_pos, "Broken Parser? No tag type found when building view.".into())) - } - } - - pub fn build(mut self) -> CrimResult { + pub fn build(mut self) -> 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: outputs, + //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, + })) + } SymbolType::Loop => { - let id = if let ParamValue::Identifier(id) = self.params.swap_remove(0) { + 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())); + return Err(CrimError::parse( + Position::none(), + "Identifier for loop variable name.".into()) + ); }; - Ok(Token::Loop(id, self.children)) + Ok(BuildResult::Token(Token::Loop(id, self.children))) } SymbolType::Show => { - let id = if let ParamValue::Identifier(id) = self.params.swap_remove(0) { + 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())); + return Err(CrimError::parse( + Position::none(), + "Identifier for show variable name.".into()) + ); }; - Ok(Token::Show(id, self.props)) + Ok(BuildResult::Token(Token::Show(id, self.props))) } SymbolType::If => { - let id = if let ParamValue::Identifier(id) = self.params.swap_remove(0) { + 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()) + ); + }; + if self.chain.len() == 1 && + let Token::If(id, mut children, chain) = self.chain[0] && + id.len() == 0 { + self.chain.clear(); + self.chain.append(children); + } + Ok(BuildResult::Token(Token::If(id, self.children, self.chain))) + } + SymbolType::ElIf => { + 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())); + return Err(CrimError::parse( + Position::none(), + "Identifier for elif variable name.".into()) + ); }; - Ok(Token::If(id, self.children)) + Ok(BuildResult::Token(Token::If(Identifier::new(), self.children, self.chain))) + } + SymbolType::Else => { + Ok(BuildResult::Token(Token::If(Identifier::new(), self.children, self.chain))) } _ => { Err(CrimError::parse( self.start_pos, "Bad tag type".into())) -- cgit v1.2.3