From 3e60b2746bb95cfcd9b7fa4b7ed9e1c72259fb23 Mon Sep 17 00:00:00 2001 From: Mike Buland Date: Mon, 8 Jun 2026 12:32:06 -0700 Subject: It parses correctly for everything defined. I think I'll change fragment to view, it's nicer and shorter. --- src/lexer.rs | 2 +- src/lib.rs | 18 ++++- src/parser.rs | 236 ++++++++++++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 223 insertions(+), 33 deletions(-) diff --git a/src/lexer.rs b/src/lexer.rs index 4225b32..0667d07 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -17,8 +17,8 @@ pub enum SymbolType<'a> { EndFlat, EndPoint, Fragment, - Section, Output, + Show, Equals, Token(&'a str), Literal(&'a str), diff --git a/src/lib.rs b/src/lib.rs index 25d9be1..dec06a5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,6 +50,7 @@ struct Output { type Properties = HashMap; +#[derive(Debug)] enum Token { Root(Vec), Fragment(String,Properties,Vec), @@ -111,7 +112,7 @@ impl Crimtag { pub fn load_static(&mut self, data: &str) -> Result<(),Box::> { let mut p = parser::Parser::new(); - p.parse( &data ); + println!("Parsed token tree: {:?}", p.parse( &data )? ); Ok(()) } @@ -146,7 +147,20 @@ mod tests { let mut ct = Crimtag::new(); if let Err(e) = ct.load_static(r#"Here is a sample [|fragment "index" theme="standard"|>Hi<|fragment|] -That was fun!"#) { +That was fun! now a placeholder: [|fragment "placeholder"|] and now one with an implicit [|fragment "simple"|>Body [|show "content"|]<|fragment|] aoeu + +Now with explicit outputs: +[|fragment "complex"|> + [|output "content"|> + Here's a [|show "name"|]. + <|output|] + [|output "sidebar"|> + What's up world? + <|output|] + [|output "footer"|> + Same, I guess! + <|output|] +<|fragment|]"#) { println!("Error: {:?}", e ); } else { println!("It finished"); diff --git a/src/parser.rs b/src/parser.rs index 368a246..1591c8b 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -51,12 +51,45 @@ impl<'a> Context<'a> { 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 Option { - pub fn is bool>(&self, f: T ) -> ParseError { +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(f( &self.symbol )) + Ok(match st.symbol() { + SymbolType::Token(_) | + SymbolType::Fragment | + SymbolType::Output | + SymbolType::Show => true, + _ => false + }) } else { Err(ParseError{ position: Position::new(0,0), @@ -166,15 +199,15 @@ impl Parser { } } - pub fn parse(&mut self, src: &str ) -> ParseResult { - let mut ll = Lexer::new( src ); + 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(&mut self, ctx: &mut Context ) -> ParseResult { + fn p_input(&self, ctx: &mut Context ) -> ParseResult { let mut children = Vec::new(); loop { if ctx.cur().is_none() { @@ -183,7 +216,7 @@ impl Parser { match ctx.cur().unwrap().symbol() { SymbolType::Text(_) => { /* Skip top level text */ } SymbolType::StartFlat => { - children.push( self.parse_tag( ctx )? ); + children.push( self.parse_tag_fragment( ctx )? ); } SymbolType::Error{..} => { return self.lex_error( &ctx.cur().unwrap() ); @@ -197,12 +230,99 @@ impl Parser { Ok(Token::Root(children)) } - fn parse_tag(&mut self, ctx: &mut Context) -> ParseResult { - let mut tb = TagBuilder::new(); + fn parse_tag_fragment(&self, ctx: &mut Context) -> ParseResult { + let mut tb = self.parse_open_tag( ctx )?; + if !tb.is_name( &SymbolType::Fragment ) { + 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-fragment-- 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> { println!("--> Begin parse_tag <--"); let start_sym = ctx.cur().unwrap(); + let mut tb = TagBuilder::new(&start_sym); println!("--> Start sym: {:?}", start_sym ); @@ -239,19 +359,40 @@ impl Parser { } } + ctx.next(); + Ok(tb) } - fn parse_end_tag(&mut self, ctx: &mut Context ) -> &'a str { - if !ctx.cur().is(|s| matches!(s, Symbol::StartPoint))? { + 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(). + position: *ctx.cur().unwrap().start(), + what: "Tag should be <| |] style end tag.".to_string(), }); } - ctx.next() + Ok(name) } - fn parse_tag_body(&mut self, ctx: &mut Context, tb: &mut TagBuilder ) -> ParseResult<()> { + 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()}); @@ -259,28 +400,37 @@ impl Parser { 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( ctx )? ); + tb.add_child( self.parse_tag_body( ctx )? ); + println!("Following tag body: {:?} {:?}", ctx.cur(), ctx.peek() ); } SymbolType::StartPoint => { - let end_tag = self.parse_end_tag( ctx )?; - + let end = self.parse_end_tag( ctx )?; + println!("End tag: {:?}, open tag: {:?}", end, tb.name() ); + 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{..} => { - return self.lex_error( &ctx.cur().unwrap() ); + self.lex_error( &ctx.cur().unwrap() )?; } _ => { } } - ctx.next(); } - Ok(Token::Root(children)) - Ok(()) } - fn parse_tag_params(&mut self, ctx: &mut Context, tb: &mut TagBuilder ) -> ParseResult<()> { + fn parse_tag_params(&self, ctx: &mut Context, tb: &mut TagBuilder ) -> ParseResult<()> { loop { if ctx.cur().is_none() { @@ -299,7 +449,7 @@ impl Parser { Ok(()) } - fn parse_tag_props(&mut self, ctx: &mut Context, tb: &mut TagBuilder ) -> ParseResult<()> { + 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()}); @@ -325,7 +475,7 @@ impl Parser { } } -#[derive(PartialEq)] +#[derive(PartialEq,Copy,Clone,Debug)] enum TagType { Unknown, Unary, @@ -339,20 +489,34 @@ struct TagBuilder<'a> { props: Properties, children: Vec, tag_type: TagType, + start_pos: Position, } impl<'a> TagBuilder<'a> { - pub fn new() -> 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 has_children(&self) -> bool { + 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 { @@ -360,14 +524,18 @@ impl<'a> TagBuilder<'a> { } } - pub fn is_same_name(&self, name: Symbol<'a>) -> bool { + pub fn is_name(&self, name: &SymbolType<'a>) -> bool { if let Some(a) = self.name { - a.symbol() == name.symbol() + 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); println!("Tag name: {:?}", self.name ); @@ -387,10 +555,18 @@ impl<'a> TagBuilder<'a> { 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() { @@ -406,14 +582,14 @@ impl<'a> TagBuilder<'a> { _ => { println!("Unkown tag type"); Err(ParseError { - position: Position::new(0,0), + position: self.start_pos, what: "Bad tag type".to_string(), }) } } } else { Err(ParseError { - position: Position::new(0,0), + position: self.start_pos, what: "Bad tag type".to_string(), }) } -- cgit v1.2.3