From 0f2bd09d269862105e603a9865201f521f49490b Mon Sep 17 00:00:00 2001 From: Mike Buland Date: Mon, 8 Jun 2026 21:45:30 -0700 Subject: It works again! and more! About to change a few things: - symbols and errors will have a range not a position - remove view/output token type, those should just be View and Output structs - Figure out some formatting stuff? (add a dep? I dunno) - Move to non-quoted dot-delimeted variable references. - Add loops. - Add conditionals? - Add more! --- src/lexer.rs | 5 +-- src/lib.rs | 80 +++++++++++++++++++++++++++-------------------- src/parser.rs | 57 +++++++++++++++++----------------- src/position.rs | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 173 insertions(+), 65 deletions(-) create mode 100644 src/position.rs diff --git a/src/lexer.rs b/src/lexer.rs index 5ce7929..c7249b7 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -24,7 +24,6 @@ pub enum SymbolType<'a> { Literal(&'a str), Text(&'a str), Error{ what: ErrorType }, - EOS, } #[derive(Copy,Clone)] @@ -36,7 +35,7 @@ pub struct Symbol<'a> { impl<'a> fmt::Debug for Symbol<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?} @ {}:{}", self.symbol, self.start.line, self.start.column ) + write!(f, "{:?} @ {:?}-{:?}", self.symbol, self.start, self.end ) } } @@ -64,6 +63,7 @@ impl<'a> Symbol<'a> { } } +#[derive(Debug)] enum Mode { Text, InTag, @@ -300,6 +300,7 @@ impl<'a> Lexer<'a> { } fn parse_end_tag(&mut self) -> Option> { + self.skip_ws(); let start_pos = self.pos.clone(); if let Some(chr) = self.cur() && chr == '|' { match self.peek() { diff --git a/src/lib.rs b/src/lib.rs index 5dbb379..208cea1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,22 +5,10 @@ use std::time::SystemTime; mod lexer; mod parser; +mod position; pub use parser::ParseError; - -#[derive(Debug,Copy,Clone)] -pub struct Position { - pub line: u32, - pub column: u32, -} - -impl Position { - pub fn new( line: u32, column: u32 ) -> Self { - Self { - line, column, - } - } -} +pub use position::*; #[derive(Debug)] pub struct Crimtag { @@ -29,7 +17,7 @@ pub struct Crimtag { } #[derive(PartialEq,Eq,Debug,Clone)] -enum ViewSource { +pub enum ViewSource { File { path: PathBuf, loaded: SystemTime, @@ -52,7 +40,7 @@ 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 { + 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) ); @@ -74,17 +62,9 @@ type Properties = HashMap; enum Token { Root(Vec), View(String,Properties,Vec), - Output(String,Properties,Vec), + Output(String,/*Properties,*/ Vec), Show(String,Properties), Text(String), - Literal(String), - /* - Tag{ - name: String, - params: Vec, - props: Properties, - children: Vec - },*/ } #[derive(Debug)] @@ -203,6 +183,18 @@ impl Crimtag { } } + pub fn get_view_source(&self, view: &str) -> Option { + if let Some(view) = self.views.get(view) { + if view.source < self.sources.len() { + Some(self.sources[view.source].clone()) + } else { + None + } + } else { + None + } + } + pub fn view_partial(&self, view: &str, input: &Value ) -> Result { if let Some(view) = self.views.get(view) { return view.process( input ) @@ -213,12 +205,10 @@ impl Crimtag { pub fn view(&self, view: &str, input: &Value ) -> Result { if let Some(view) = self.views.get( view ) { - let mut output = view.process( input )?; + let 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 { @@ -242,18 +232,40 @@ fn exec(input: &Value, tokens: &Vec, buf: &mut String) -> Result<(),Parse println!("Too high!?"); // Error? } - Token::Show(s,_) => { + Token::Show(s,p) => { + let format = if let Some(s) = p.get("format") { + s + } else { + &"".to_string() + }; if let Value::Dictionary(d) = input && - let Some(Value::String(s)) = d.get(s) { - buf.push_str(s); + let Some(value) = d.get(s) { + match value { + Value::Dictionary(_) => { + buf.push_str(""); + } + Value::List(_) => { + buf.push_str(""); + } + Value::String(s) => { + buf.push_str(s); + } + Value::Int(i) => { + buf.push_str(&i.to_string()); + } + Value::Float(f) => { + if format == "" { + buf.push_str(&f.to_string()); + } else { + buf.push_str(&f.to_string()); + } + } + } } } Token::Text(s) => { buf.push_str( s ); } - Token::Literal(..) => { - println!("Literal!?"); - } } } Ok(()) diff --git a/src/parser.rs b/src/parser.rs index 13e89aa..c7a8abe 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -17,6 +17,13 @@ impl ParseError { 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 { @@ -61,12 +68,9 @@ impl<'a> Context<'a> { self.cur[(self.icur+1)%2] } - pub fn check_unwrapped bool>(&self, f: T) -> ParseResult { + pub fn check_unwrapped bool>(&self, context: &str, 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() - }) + Err(ParseError::eos( context )) } else { Ok(f( self.cur().unwrap().symbol(), self.peek().unwrap().symbol())) } @@ -74,19 +78,16 @@ impl<'a> Context<'a> { } trait SymbolHelper { - fn is bool>(&self, f: T ) -> ParseResult; + fn is bool>(&self, context: &str, f: T ) -> ParseResult; fn is_valid_tag_name(&self) -> ParseResult; } impl<'a> SymbolHelper for Option> { - fn is bool>(&self, f: T ) -> ParseResult { + fn is bool>(&self, context: &str, 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() - }) + Err(ParseError::eos( context )) } } @@ -256,10 +257,7 @@ impl Parser { //println!("--parse-tag-view-- 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() - }); + return Err(ParseError::eos(format!("close tag for {:?}", tb.name()).as_str())) } match ctx.cur().unwrap().symbol() { SymbolType::Text(s) => { @@ -282,7 +280,7 @@ impl Parser { // 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 )); + tb.add_child(Token::Output("content".to_string(), /*Properties::new(),*/ children )); } else { // Standard case, outputs are included, text is @@ -303,7 +301,7 @@ impl Parser { }); } else { // No outputs at all, we create an implict output - tb.add_child(Token::Output("content".to_string(), Properties::new(), children )); + tb.add_child(Token::Output("content".to_string(), /*Properties::new(), */children )); } } @@ -339,11 +337,11 @@ impl Parser { tb.set_name( name_sym ); } _ => { - return Err(ParseError{position: ctx.cur().unwrap().start().clone(), what: "Unexpecetd symbol".to_string()}); + return Err(ParseError{position: ctx.cur().unwrap().start().clone(), what: "Unexpected symbol".to_string()}); } } } else { - return Err(ParseError{position: Position::new(0,0), what: "Unexpecetd end of stream".to_string()}); + return Err(ParseError::eos("tag type")); } self.parse_tag_params( ctx, &mut tb )?; @@ -358,7 +356,10 @@ impl Parser { tb.set_type( TagType::Unary ); } _ => { - return Err(ParseError{position: Position::new(0,0), what: "Unexpecetd end of stream".to_string()}); + return Err(ParseError::new( + end_sym.start().clone(), + format!("Unexpected symbol {:?} looking for end of tag.", end_sym.symbol()).as_str() + )); } } } @@ -369,7 +370,7 @@ impl Parser { } fn parse_end_tag<'a>(&self, ctx: &mut Context<'a> ) -> ParseResult> { - if !ctx.cur().is(|s| *s == SymbolType::StartPoint )? { + if !ctx.cur().is("end tag",|s| *s == SymbolType::StartPoint )? { return Err(ParseError{ position: *ctx.cur().unwrap().start(), what: "Invalid end tag?".to_string(), @@ -382,7 +383,7 @@ impl Parser { }); } let name = ctx.cur().unwrap(); - if !ctx.next().is(|s| *s == SymbolType::EndFlat )? { + 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(), @@ -399,7 +400,7 @@ impl Parser { loop { if ctx.cur().is_none() { - return Err(ParseError{position: Position::new(0,0), what: "Unexpecetd end of stream".to_string()}); + return Err(ParseError::eos(format!("close tag for {:?}", tb.name()).as_str())); } match ctx.cur().unwrap().symbol() { SymbolType::Text(s) => { @@ -435,8 +436,7 @@ impl Parser { fn parse_tag_params(&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()}); + return Err(ParseError::eos("tag parameters")); } match ctx.cur().unwrap().symbol() { SymbolType::Literal(s) => { @@ -454,7 +454,7 @@ impl Parser { 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()}); + return Err(ParseError::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))) { @@ -466,7 +466,7 @@ impl Parser { return Err(ParseError{position: Position::new(0,0), what: "Expected quoted literal string".to_string()}); } } else { - return Err(ParseError{position:Position::new(0,0), what: "Expected = ".to_string()}); + break; } } else { break; @@ -482,7 +482,6 @@ enum TagType { Unknown, Unary, BinaryOpen, - BinaryClose, } struct TagBuilder<'a> { @@ -573,7 +572,7 @@ impl<'a> TagBuilder<'a> { 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)) + Ok(Token::Output(self.params.swap_remove(0), /*self.props,*/ self.children)) } SymbolType::Show => { Ok(Token::Show(self.params.swap_remove(0), self.props)) diff --git a/src/position.rs b/src/position.rs new file mode 100644 index 0000000..24f5309 --- /dev/null +++ b/src/position.rs @@ -0,0 +1,96 @@ +use std::fmt; +use std::cmp::Ordering; + +#[derive(Copy,Clone)] +pub struct Position { + pub line: u32, + pub column: u32, +} + +impl Position { + pub fn new( line: u32, column: u32 ) -> Self { + Self { + line, column, + } + } + + pub fn none() -> Self { + Self { + line: 0, + column: 0, + } + } + + pub fn is_none(&self) -> bool { + self.line == 0 || self.column == 0 + } +} + +impl fmt::Debug for Position { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.is_none() { + write!(f, "none") + } else { + write!(f, "{}:{}", self.line, self.column ) + } + } +} + +impl PartialOrd for Position { + fn partial_cmp(&self, other: &Self) -> Option { + let ord = self.line.partial_cmp( &other.line ); + if Some(Ordering::Equal) == ord { + self.column.partial_cmp( &other.column ) + } else { + ord + } + } +} + +impl Ord for Position { + fn cmp(&self, other: &Self) -> Ordering { + let ord = self.line.cmp( &other.line ); + if Ordering::Equal == ord { + self.column.cmp( &other.column ) + } else { + ord + } + } +} + +impl PartialEq for Position { + fn eq(&self, other: &Self) -> bool { + self.line == other.line && self.column == other.column + } +} + +impl Eq for Position {} + +#[derive(Copy,Clone)] +pub struct Range { + start: Position, + end: Position, +} + +impl Range { + pub fn new( start: Position, end: Position ) -> Self { + Self { + start, end, + } + } + + pub fn include(&mut self, pos: &Position ) { + if *pos < self.start { + self.start = pos.clone(); + } + if *pos > self.end { + self.end = pos.clone(); + } + } +} + +impl fmt::Debug for Range { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}-{:?}", self.start, self.end ) + } +} -- cgit v1.2.3