From a9172970548805db45188a352b2aafbc0e7b32e3 Mon Sep 17 00:00:00 2001 From: Mike Buland Date: Mon, 8 Jun 2026 15:07:12 -0700 Subject: It works! Lots to do, but it works. We have a lot of cleanup to do, remove debugging prints, and make it easier to use other structures and complex variable references, etc. --- src/lexer.rs | 4 +- src/lib.rs | 200 ++++++++++++++++++++++++++++++++++++++++++++++++++-------- src/parser.rs | 31 +++++---- 3 files changed, 197 insertions(+), 38 deletions(-) diff --git a/src/lexer.rs b/src/lexer.rs index 0667d07..5ce7929 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -16,7 +16,7 @@ pub enum SymbolType<'a> { StartPoint, EndFlat, EndPoint, - Fragment, + View, Output, Show, Equals, @@ -228,7 +228,7 @@ impl<'a> Lexer<'a> { None } else { Some(Symbol::new( match s { - "fragment" => SymbolType::Fragment, + "view" => SymbolType::View, "show" => SymbolType::Show, "output" => SymbolType::Output, _ => SymbolType::Token(s) diff --git a/src/lib.rs b/src/lib.rs index dec06a5..e07f85b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,8 @@ use std::time::SystemTime; mod lexer; mod parser; +pub use parser::ParseError; + #[derive(Debug,Copy,Clone)] pub struct Position { pub line: u32, @@ -20,13 +22,14 @@ impl Position { } } +#[derive(Debug)] pub struct Crimtag { - fragments: HashMap, - sources: Vec, + views: HashMap, + sources: Vec, } #[derive(PartialEq,Eq,Debug,Clone)] -enum FragmentSource { +enum ViewSource { File { path: PathBuf, loaded: SystemTime, @@ -37,12 +40,29 @@ enum FragmentSource { }, } -struct Fragment { - name: String, +#[derive(Debug)] +struct View { source: usize, - sections: HashMap, + outputs: Vec, +// theme: Option + layout: Option, } +impl View { + fn process(&self, input: &Value) -> Result { + let mut out_vars = HashMap::new(); + for output in &self.outputs { + if let Token::Output(_,_,tokens) = &output.ast_root { + let mut buf = String::new(); + exec( input, tokens, &mut buf )?; + out_vars.insert( output.name.clone(), Value::String(buf) ); + } + } + Ok(Value::Dictionary(out_vars)) + } +} + +#[derive(Debug)] struct Output { name: String, ast_root: Token, @@ -53,7 +73,7 @@ type Properties = HashMap; #[derive(Debug)] enum Token { Root(Vec), - Fragment(String,Properties,Vec), + View(String,Properties,Vec), Output(String,Properties,Vec), Show(String,Properties), Text(String), @@ -67,18 +87,24 @@ enum Token { },*/ } -struct State { +#[derive(Debug)] +pub enum Value { + Dictionary(HashMap), + List(Vec), + String(String), + Int(i64), + Float(f64), } impl Crimtag { pub fn new() -> Self { Self { - fragments: HashMap::new(), - sources: vec![FragmentSource::Static], + views: HashMap::new(), + sources: vec![ViewSource::Static], } } - fn id_source(&mut self, src: &FragmentSource ) -> usize { + fn id_source(&mut self, src: &ViewSource ) -> usize { for idx in 0..self.sources.len() { if self.sources[idx] == *src { return idx; @@ -104,23 +130,133 @@ impl Crimtag { SystemTime::now() }; - let mut p = parser::Parser::new(); - p.parse( &String::from_utf8( std::fs::read( path )? )? ); - + let p = parser::Parser::new(); + self.register_views( + p.parse( &String::from_utf8( std::fs::read( path )? )? )?, + ViewSource::File { + path: path.to_path_buf(), + loaded: time, + })?; Ok(()) } - pub fn load_static(&mut self, data: &str) -> Result<(),Box::> { - let mut p = parser::Parser::new(); - println!("Parsed token tree: {:?}", p.parse( &data )? ); + pub fn load_static(&mut self, data: &str) -> Result<(),ParseError> { + let p = parser::Parser::new(); + self.register_views( p.parse( &data )?, ViewSource::Static ) + } - Ok(()) + pub fn load_external(&mut self, key: &str, data: &str) -> Result<(),ParseError> { + let p = parser::Parser::new(); + self.register_views( + p.parse( &data )?, + ViewSource::External{ key: key.to_string()} + ) } - pub fn load_external(&mut self, key: &str, data: &str) -> Result<(),Box::> { - Ok(()) + fn register_views(&mut self, token: Token, source: ViewSource) -> Result<(),ParseError> { + let source_id = self.id_source( &source ); + if let Token::Root(views) = token { + for token in views { + if let Token::View(name,props,output_tokens) = token { + let mut outputs = Vec::new(); + for ot in output_tokens { + let output_name = if let Token::Output(on,..) = &ot { + on.clone() + } else { + return Err(ParseError::new( + Position::new(0,0), + "Broken parser? Item in view is not an output." + )); + }; + outputs.push( + Output { + name: output_name, + ast_root: ot, + } + ); + } + + let layout = if let Some(l) = props.get("layout") { + Some(l.clone()) + } else { + None + }; + + self.views.insert( name, View { + source: source_id, + outputs: outputs, + layout: layout, + }); + } else { + return Err(ParseError::new( + Position::new(0,0), + "Broken parser? Item in root is not a view." + )); + } + } + Ok(()) + } else { + Err(ParseError::new( + Position::new(0,0), + "Broken parser? Root is not a root token." + )) + } + } + + pub fn view_partial(&self, view: &str, input: &Value ) -> 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")); + } + } + + pub fn view(&self, view: &str, input: &Value ) -> Result { + if let Some(view) = self.views.get( view ) { + let mut output = view.process( input )?; + if let Some(l) = &view.layout { + println!("Layout: {}", l ); + self.view( l, &output ) + } else { + println!("No layout, at the top"); + if let Value::Dictionary(mut d) = output && + let Some(content) = d.remove("content") && + let Value::String(s) = content { + + Ok(s) + } else { + Err(ParseError::new(Position::new(0,0),"No content found in root layout.")) + } + } + + } else { + Err(ParseError::new(Position::new(0,0),"No such view found")) + } } } +fn exec(input: &Value, tokens: &Vec, buf: &mut String) -> Result<(),ParseError> { + for token in tokens { + match token { + Token::Root(..) | Token::View(..) | Token::Output(..) => { + println!("Too high!?"); + // Error? + } + Token::Show(s,_) => { + if let Value::Dictionary(d) = input && + let Some(Value::String(s)) = d.get(s) { + buf.push_str(s); + } + } + Token::Text(s) => { + buf.push_str( s ); + } + Token::Literal(..) => { + println!("Literal!?"); + } + } + } + Ok(()) +} #[cfg(test)] mod tests { @@ -130,7 +266,7 @@ mod tests { #[test] fn lexing() { - let data = r#"Leading comment: [|fragment "basic" something="yeup"|>Hello there <|fragment|] Trailing text"#; + let data = r#"Leading comment: [|view "basic" something="yeup"|>Hello there <|view|] Trailing text"#; let ll = lexer::Lexer::new( &data ); for sym in ll { if let SymbolType::Error{what} = sym.symbol() { @@ -146,11 +282,13 @@ mod tests { fn parsing() { 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! now a placeholder: [|fragment "placeholder"|] and now one with an implicit [|fragment "simple"|>Body [|show "content"|]<|fragment|] aoeu +[|view "index" theme="standard"|>Hi<|view|] +That was fun! now a placeholder: [|view "placeholder"|] and now one with an implicit [|view "simple"|>Body [|show "content"|] and end<|view|] aoeu + +[|view "index" layout="simple"|>Whatever man!<|view|] Now with explicit outputs: -[|fragment "complex"|> +[|view "complex"|> [|output "content"|> Here's a [|show "name"|]. <|output|] @@ -160,10 +298,22 @@ Now with explicit outputs: [|output "footer"|> Same, I guess! <|output|] -<|fragment|]"#) { +<|view|]"#) { println!("Error: {:?}", e ); } else { println!("It finished"); + if let Ok(v) = ct.view( + "index", + &Value::Dictionary( + HashMap::from([ + ("hi".to_string(),Value::String("hi".to_string())) + ]) + ) + ) { + println!("View result: {:?}", v ); + } else { + println!("Error?"); + } } } } diff --git a/src/parser.rs b/src/parser.rs index 1591c8b..aff64f6 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -10,6 +10,15 @@ pub struct ParseError { what: String, } +impl ParseError { + pub fn new( position: Position, what: &str ) -> Self { + ParseError { + position, + what: what.to_string(), + } + } +} + impl fmt::Display for ParseError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Error parsing input: {}", "yeah") @@ -85,7 +94,7 @@ impl<'a> SymbolHelper for Option> { if let Some(st) = self { Ok(match st.symbol() { SymbolType::Token(_) | - SymbolType::Fragment | + SymbolType::View | SymbolType::Output | SymbolType::Show => true, _ => false @@ -127,7 +136,7 @@ pub struct Parser { * close_tag: '<|' token '|]' * ; * - * tag_guts: 'fragment' literal props + * tag_guts: 'view' literal props * | 'section' literal * | 'output' literal * ; @@ -167,7 +176,7 @@ pub struct Parser { * close_tag: '<|' token '|]' * ; * - * tag_guts: 'fragment' literal props + * tag_guts: 'view' literal props * | 'section' literal * | 'output' literal * ; @@ -216,7 +225,7 @@ impl Parser { match ctx.cur().unwrap().symbol() { SymbolType::Text(_) => { /* Skip top level text */ } SymbolType::StartFlat => { - children.push( self.parse_tag_fragment( ctx )? ); + children.push( self.parse_tag_view( ctx )? ); } SymbolType::Error{..} => { return self.lex_error( &ctx.cur().unwrap() ); @@ -230,9 +239,9 @@ impl Parser { Ok(Token::Root(children)) } - fn parse_tag_fragment(&self, ctx: &mut Context) -> ParseResult { + fn parse_tag_view(&self, ctx: &mut Context) -> ParseResult { let mut tb = self.parse_open_tag( ctx )?; - if !tb.is_name( &SymbolType::Fragment ) { + if !tb.is_name( &SymbolType::View ) { return Err(ParseError{ position: tb.start_pos(), what: "Unexpected tag type, only frament allowed at root".to_string(), @@ -244,7 +253,7 @@ impl Parser { } let mut children = Vec::new(); - println!("--parse-tag-fragment-- tag parsed, next token: {:?}", ctx.cur() ); + println!("--parse-tag-view-- tag parsed, next token: {:?}", ctx.cur() ); loop { if ctx.cur().is_none() { return Err(ParseError{ @@ -328,7 +337,7 @@ impl Parser { if ctx.next().is_some() { match ctx.cur().unwrap().symbol() { - SymbolType::Fragment | SymbolType::Show | + SymbolType::View | SymbolType::Show | SymbolType::Output => { let name_sym = ctx.cur().unwrap(); ctx.next(); @@ -532,7 +541,7 @@ impl<'a> TagBuilder<'a> { } } - pub fn name(&self) -> Option { + pub fn name(&self) -> Option> { self.name } @@ -570,8 +579,8 @@ impl<'a> TagBuilder<'a> { pub fn build(mut self) -> ParseResult { if let Some(sym) = self.name { match sym.symbol() { - SymbolType::Fragment => { - Ok(Token::Fragment(self.params.swap_remove(0), self.props, self.children)) + SymbolType::View => { + Ok(Token::View(self.params.swap_remove(0), self.props, self.children)) } SymbolType::Output => { Ok(Token::Output(self.params.swap_remove(0), self.props, self.children)) -- cgit v1.2.3