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/lib.rs | 200 +++++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 175 insertions(+), 25 deletions(-) (limited to 'src/lib.rs') 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?"); + } } } } -- cgit v1.2.3