diff options
| author | Mike Buland <mike@xagasoft.com> | 2026-06-07 16:37:28 -0700 |
|---|---|---|
| committer | Mike Buland <mike@xagasoft.com> | 2026-06-07 16:37:28 -0700 |
| commit | 83ab8c67beecfc05684c9400e29f7d77225d3af9 (patch) | |
| tree | 2ec5042814bfb8a485797672cfef66fea04fd85f | |
| parent | 3dd9ca69340512e42867b43db689e494bada2000 (diff) | |
| download | crimtag-83ab8c67beecfc05684c9400e29f7d77225d3af9.tar.gz crimtag-83ab8c67beecfc05684c9400e29f7d77225d3af9.tar.bz2 crimtag-83ab8c67beecfc05684c9400e29f7d77225d3af9.tar.xz crimtag-83ab8c67beecfc05684c9400e29f7d77225d3af9.zip | |
Cleaned up a lot of parsing code.
| -rw-r--r-- | src/lexer.rs | 13 | ||||
| -rw-r--r-- | src/lib.rs | 24 | ||||
| -rw-r--r-- | src/parser.rs | 142 |
3 files changed, 137 insertions, 42 deletions
diff --git a/src/lexer.rs b/src/lexer.rs index 04be350..902bb64 100644 --- a/src/lexer.rs +++ b/src/lexer.rs | |||
| @@ -1,15 +1,16 @@ | |||
| 1 | use std::iter::Iterator; | 1 | use std::iter::Iterator; |
| 2 | //use core::error::Error; | 2 | //use core::error::Error; |
| 3 | use std::str::CharIndices; | 3 | use std::str::CharIndices; |
| 4 | use std::fmt; | ||
| 4 | 5 | ||
| 5 | use crate::Position; | 6 | use crate::Position; |
| 6 | 7 | ||
| 7 | #[derive(Debug,Copy,Clone)] | 8 | #[derive(Debug,Copy,Clone,PartialEq)] |
| 8 | pub enum ErrorType { | 9 | pub enum ErrorType { |
| 9 | UnexpectedChar(char), | 10 | UnexpectedChar(char), |
| 10 | } | 11 | } |
| 11 | 12 | ||
| 12 | #[derive(Debug,Copy,Clone)] | 13 | #[derive(Debug,Copy,Clone,PartialEq)] |
| 13 | pub enum SymbolType<'a> { | 14 | pub enum SymbolType<'a> { |
| 14 | StartFlat, | 15 | StartFlat, |
| 15 | StartPoint, | 16 | StartPoint, |
| @@ -26,13 +27,19 @@ pub enum SymbolType<'a> { | |||
| 26 | EOS, | 27 | EOS, |
| 27 | } | 28 | } |
| 28 | 29 | ||
| 29 | #[derive(Debug,Copy,Clone)] | 30 | #[derive(Copy,Clone)] |
| 30 | pub struct Symbol<'a> { | 31 | pub struct Symbol<'a> { |
| 31 | symbol: SymbolType<'a>, | 32 | symbol: SymbolType<'a>, |
| 32 | start: Position, | 33 | start: Position, |
| 33 | end: Position, | 34 | end: Position, |
| 34 | } | 35 | } |
| 35 | 36 | ||
| 37 | impl<'a> fmt::Debug for Symbol<'a> { | ||
| 38 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| 39 | write!(f, "{:?} @ {}:{}", self.symbol, self.start.line, self.start.column ) | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 36 | impl<'a> Symbol<'a> { | 43 | impl<'a> Symbol<'a> { |
| 37 | pub fn new( symbol: SymbolType<'a>, start: Position, end: Position ) -> Self { | 44 | pub fn new( symbol: SymbolType<'a>, start: Position, end: Position ) -> Self { |
| 38 | Self { | 45 | Self { |
| @@ -2,8 +2,6 @@ use std::path::{PathBuf,Path}; | |||
| 2 | use std::collections::HashMap; | 2 | use std::collections::HashMap; |
| 3 | use core::error::Error; | 3 | use core::error::Error; |
| 4 | use std::time::SystemTime; | 4 | use std::time::SystemTime; |
| 5 | use std::fs::File; | ||
| 6 | use std::io::Read; | ||
| 7 | 5 | ||
| 8 | mod lexer; | 6 | mod lexer; |
| 9 | mod parser; | 7 | mod parser; |
| @@ -57,6 +55,14 @@ enum Token { | |||
| 57 | Fragment(String,Properties,Vec<Token>), | 55 | Fragment(String,Properties,Vec<Token>), |
| 58 | Section(String,Properties,Vec<Token>), | 56 | Section(String,Properties,Vec<Token>), |
| 59 | Text(String), | 57 | Text(String), |
| 58 | Literal(String), | ||
| 59 | /* | ||
| 60 | Tag{ | ||
| 61 | name: String, | ||
| 62 | params: Vec<String>, | ||
| 63 | props: Properties, | ||
| 64 | children: Vec<Token> | ||
| 65 | },*/ | ||
| 60 | } | 66 | } |
| 61 | 67 | ||
| 62 | struct State { | 68 | struct State { |
| @@ -97,14 +103,14 @@ impl Crimtag { | |||
| 97 | }; | 103 | }; |
| 98 | 104 | ||
| 99 | let mut p = parser::Parser::new(); | 105 | let mut p = parser::Parser::new(); |
| 100 | p.parse( self, &String::from_utf8( std::fs::read( path )? )? ); | 106 | p.parse( &String::from_utf8( std::fs::read( path )? )? ); |
| 101 | 107 | ||
| 102 | Ok(()) | 108 | Ok(()) |
| 103 | } | 109 | } |
| 104 | 110 | ||
| 105 | pub fn load_static(&mut self, data: &str) -> Result<(),Box::<dyn Error>> { | 111 | pub fn load_static(&mut self, data: &str) -> Result<(),Box::<dyn Error>> { |
| 106 | let mut p = parser::Parser::new(); | 112 | let mut p = parser::Parser::new(); |
| 107 | p.parse( self, &data ); | 113 | p.parse( &data ); |
| 108 | 114 | ||
| 109 | Ok(()) | 115 | Ok(()) |
| 110 | } | 116 | } |
| @@ -137,8 +143,12 @@ mod tests { | |||
| 137 | #[test] | 143 | #[test] |
| 138 | fn parsing() { | 144 | fn parsing() { |
| 139 | let mut ct = Crimtag::new(); | 145 | let mut ct = Crimtag::new(); |
| 140 | ct.load_static(r#"Here is a sample | 146 | if let Err(e) = ct.load_static(r#"Here is a sample |
| 141 | [|frament "index" theme="standard"|><html><body>Hi</body></html><|fragment|] | 147 | [|fragment "index" theme="standard"|><html><body>Hi</body></html><|fragment|] |
| 142 | That was fun!"#); | 148 | That was fun!"#) { |
| 149 | println!("Error: {:?}", e ); | ||
| 150 | } else { | ||
| 151 | println!("It finished"); | ||
| 152 | } | ||
| 143 | } | 153 | } |
| 144 | } | 154 | } |
diff --git a/src/parser.rs b/src/parser.rs index e5ea1f5..2d845cd 100644 --- a/src/parser.rs +++ b/src/parser.rs | |||
| @@ -27,6 +27,7 @@ struct Context<'a> { | |||
| 27 | impl<'a> Context<'a> { | 27 | impl<'a> Context<'a> { |
| 28 | pub fn new( mut ll: Lexer<'a> ) -> Self { | 28 | pub fn new( mut ll: Lexer<'a> ) -> Self { |
| 29 | let cur = [ll.next(), ll.next()]; | 29 | let cur = [ll.next(), ll.next()]; |
| 30 | //println!(" - cur: {:?}, peek: {:?}", cur[0], cur[1] ); | ||
| 30 | Self { | 31 | Self { |
| 31 | cur, | 32 | cur, |
| 32 | icur: 0, | 33 | icur: 0, |
| @@ -37,7 +38,7 @@ impl<'a> Context<'a> { | |||
| 37 | pub fn next(&mut self) -> Option<Symbol<'a>> { | 38 | pub fn next(&mut self) -> Option<Symbol<'a>> { |
| 38 | self.cur[self.icur] = self.ll.next(); | 39 | self.cur[self.icur] = self.ll.next(); |
| 39 | self.icur = (self.icur+1)%2; | 40 | self.icur = (self.icur+1)%2; |
| 40 | println!(" - cur: {:?}, peek: {:?}", self.cur(), self.peek() ); | 41 | //println!(" - cur: {:?}, peek: {:?}", self.cur(), self.peek() ); |
| 41 | self.cur[self.icur] | 42 | self.cur[self.icur] |
| 42 | } | 43 | } |
| 43 | 44 | ||
| @@ -136,7 +137,7 @@ impl Parser { | |||
| 136 | } | 137 | } |
| 137 | } | 138 | } |
| 138 | 139 | ||
| 139 | fn lex_error(&self, error: &Symbol) -> Result<(),Box<dyn Error>> { | 140 | fn lex_error(&self, error: &Symbol) -> Result<Token,Box<dyn Error>> { |
| 140 | if let SymbolType::Error{what} = error.symbol() { | 141 | if let SymbolType::Error{what} = error.symbol() { |
| 141 | Err(Box::new(ParseError { | 142 | Err(Box::new(ParseError { |
| 142 | position: error.start().clone(), | 143 | position: error.start().clone(), |
| @@ -150,17 +151,16 @@ impl Parser { | |||
| 150 | } | 151 | } |
| 151 | } | 152 | } |
| 152 | 153 | ||
| 153 | pub fn parse(&mut self, crim: &mut Crimtag, src: &str ) -> Result<(),Box<dyn Error>> { | 154 | pub fn parse(&mut self, src: &str ) -> Result<Token,Box<dyn Error>> { |
| 154 | let mut ll = Lexer::new( src ); | 155 | let mut ll = Lexer::new( src ); |
| 155 | let mut ctx = Context::new( ll ); | 156 | let mut ctx = Context::new( ll ); |
| 156 | 157 | ||
| 157 | // Parse the root of the file, the input context | 158 | // Parse the root of the file, the input context |
| 158 | self.p_input( &mut ctx )?; | 159 | self.p_input( &mut ctx ) |
| 159 | |||
| 160 | Ok(()) | ||
| 161 | } | 160 | } |
| 162 | 161 | ||
| 163 | fn p_input(&mut self, ctx: &mut Context ) -> Result<(), Box<dyn Error>> { | 162 | fn p_input(&mut self, ctx: &mut Context ) -> Result<Token, Box<dyn Error>> { |
| 163 | let mut children = Vec::new(); | ||
| 164 | loop { | 164 | loop { |
| 165 | if ctx.cur().is_none() { | 165 | if ctx.cur().is_none() { |
| 166 | break; | 166 | break; |
| @@ -168,7 +168,7 @@ impl Parser { | |||
| 168 | match ctx.cur().unwrap().symbol() { | 168 | match ctx.cur().unwrap().symbol() { |
| 169 | SymbolType::Text(_) => { /* Skip top level text */ } | 169 | SymbolType::Text(_) => { /* Skip top level text */ } |
| 170 | SymbolType::StartFlat => { | 170 | SymbolType::StartFlat => { |
| 171 | self.parse_tag( ctx ); | 171 | children.push( self.parse_tag( ctx )? ); |
| 172 | } | 172 | } |
| 173 | SymbolType::Error{..} => { | 173 | SymbolType::Error{..} => { |
| 174 | return self.lex_error( &ctx.cur().unwrap() ); | 174 | return self.lex_error( &ctx.cur().unwrap() ); |
| @@ -179,28 +179,57 @@ impl Parser { | |||
| 179 | } | 179 | } |
| 180 | ctx.next(); | 180 | ctx.next(); |
| 181 | } | 181 | } |
| 182 | Ok(()) | 182 | Ok(Token::Root(children)) |
| 183 | } | 183 | } |
| 184 | 184 | ||
| 185 | fn parse_tag(&mut self, ctx: &mut Context) -> Result<(), Box<dyn Error>> { | 185 | fn parse_tag(&mut self, ctx: &mut Context) -> Result<Token, Box<dyn Error>> { |
| 186 | let mut tb = TagBuilder::new(); | 186 | let mut tb = TagBuilder::new(); |
| 187 | |||
| 188 | println!("--> Begin parse_tag <--"); | ||
| 187 | 189 | ||
| 188 | let start_sym = ctx.cur().unwrap(); | 190 | let start_sym = ctx.cur().unwrap(); |
| 189 | 191 | ||
| 192 | println!("--> Start sym: {:?}", start_sym ); | ||
| 193 | |||
| 190 | if ctx.next().is_some() { | 194 | if ctx.next().is_some() { |
| 191 | if let SymbolType::Token(s) = ctx.next().unwrap().symbol() { | 195 | match ctx.cur().unwrap().symbol() { |
| 192 | tb.set_name( s.to_string() ); | 196 | SymbolType::Fragment | SymbolType::Section | |
| 193 | } else { | 197 | SymbolType::Output => { |
| 194 | return Err(Box::new(ParseError{position: ctx.cur().unwrap().start().clone(), what: "Unexpecetd symbol".to_string()})); | 198 | let name_sym = ctx.cur().unwrap(); |
| 199 | ctx.next(); | ||
| 200 | tb.set_name( name_sym ); | ||
| 201 | } | ||
| 202 | _ => { | ||
| 203 | return Err(Box::new(ParseError{position: ctx.cur().unwrap().start().clone(), what: "Unexpecetd symbol".to_string()})); | ||
| 204 | } | ||
| 195 | } | 205 | } |
| 196 | } else { | 206 | } else { |
| 197 | return Err(Box::new(ParseError{position: Position::new(0,0), what: "Unexpecetd end of stream".to_string()})); | 207 | return Err(Box::new(ParseError{position: Position::new(0,0), what: "Unexpecetd end of stream".to_string()})); |
| 198 | } | 208 | } |
| 199 | 209 | ||
| 210 | println!("--> Begin parse_tag_params <--"); | ||
| 200 | self.parse_tag_params( ctx, &mut tb )?; | 211 | self.parse_tag_params( ctx, &mut tb )?; |
| 212 | println!("--> End parse_tag_params <--"); | ||
| 213 | println!("--> Begin parse_tag_props <--"); | ||
| 201 | self.parse_tag_props( ctx, &mut tb )?; | 214 | self.parse_tag_props( ctx, &mut tb )?; |
| 215 | println!("--> End parse_tag_props <--"); | ||
| 202 | 216 | ||
| 203 | Ok(()) | 217 | println!("---> {:?}", ctx.cur() ); |
| 218 | if let Some(end_sym) = ctx.cur() { | ||
| 219 | match end_sym.symbol() { | ||
| 220 | SymbolType::EndPoint => { | ||
| 221 | tb.set_type( TagType::BinaryOpen ); | ||
| 222 | } | ||
| 223 | SymbolType::EndFlat => { | ||
| 224 | tb.set_type( TagType::Unary ); | ||
| 225 | } | ||
| 226 | _ => { | ||
| 227 | return Err(Box::new(ParseError{position: Position::new(0,0), what: "Unexpecetd end of stream".to_string()})); | ||
| 228 | } | ||
| 229 | } | ||
| 230 | } | ||
| 231 | |||
| 232 | tb.build() | ||
| 204 | } | 233 | } |
| 205 | 234 | ||
| 206 | fn parse_tag_params(&mut self, ctx: &mut Context, tb: &mut TagBuilder ) -> Result<(), Box<dyn Error>> { | 235 | fn parse_tag_params(&mut self, ctx: &mut Context, tb: &mut TagBuilder ) -> Result<(), Box<dyn Error>> { |
| @@ -209,15 +238,13 @@ impl Parser { | |||
| 209 | 238 | ||
| 210 | return Err(Box::new(ParseError{position: Position::new(0,0), what: "Unexpecetd end of stream".to_string()})); | 239 | return Err(Box::new(ParseError{position: Position::new(0,0), what: "Unexpecetd end of stream".to_string()})); |
| 211 | } | 240 | } |
| 212 | if let SymbolType::Token(s) = ctx.cur().unwrap().symbol() { | 241 | match ctx.cur().unwrap().symbol() { |
| 213 | if let Some(p) = ctx.peek() { | 242 | SymbolType::Literal(s) => { |
| 214 | if p.check_type(|t| matches!(t, SymbolType::Equals)) { | ||
| 215 | break; | ||
| 216 | } | ||
| 217 | } | ||
| 218 | else { | ||
| 219 | tb.add_param( s.to_string() ); | 243 | tb.add_param( s.to_string() ); |
| 220 | } | 244 | } |
| 245 | _ => { | ||
| 246 | break; | ||
| 247 | } | ||
| 221 | } | 248 | } |
| 222 | ctx.next(); | 249 | ctx.next(); |
| 223 | } | 250 | } |
| @@ -250,6 +277,7 @@ impl Parser { | |||
| 250 | } | 277 | } |
| 251 | } | 278 | } |
| 252 | 279 | ||
| 280 | #[derive(PartialEq)] | ||
| 253 | enum TagType { | 281 | enum TagType { |
| 254 | Unknown, | 282 | Unknown, |
| 255 | Unary, | 283 | Unary, |
| @@ -257,36 +285,86 @@ enum TagType { | |||
| 257 | BinaryClose, | 285 | BinaryClose, |
| 258 | } | 286 | } |
| 259 | 287 | ||
| 260 | struct TagBuilder { | 288 | struct TagBuilder<'a> { |
| 261 | name: String, | 289 | name: Option<Symbol<'a>>, |
| 262 | params: Vec<String>, | 290 | params: Vec<String>, |
| 263 | props: Vec<(String,String)>, | 291 | props: Properties, |
| 292 | children: Vec<Token>, | ||
| 264 | tag_type: TagType, | 293 | tag_type: TagType, |
| 265 | } | 294 | } |
| 266 | 295 | ||
| 267 | impl TagBuilder { | 296 | impl<'a> TagBuilder<'a> { |
| 268 | pub fn new() -> TagBuilder { | 297 | pub fn new() -> TagBuilder<'a> { |
| 269 | TagBuilder { | 298 | TagBuilder { |
| 270 | name: String::new(), | 299 | name: None, |
| 271 | params: Vec::new(), | 300 | params: Vec::new(), |
| 272 | props: Vec::new(), | 301 | props: Properties::new(), |
| 302 | children: Vec::new(), | ||
| 273 | tag_type: TagType::Unknown, | 303 | tag_type: TagType::Unknown, |
| 274 | } | 304 | } |
| 275 | } | 305 | } |
| 276 | 306 | ||
| 277 | pub fn set_name(&mut self, name: String) { | 307 | pub fn has_children(&self) -> bool { |
| 278 | self.name = name; | 308 | if self.tag_type == TagType::BinaryOpen { |
| 309 | true | ||
| 310 | } else { | ||
| 311 | false | ||
| 312 | } | ||
| 313 | } | ||
| 314 | |||
| 315 | pub fn is_same_name(&self, name: Symbol<'a>) -> bool { | ||
| 316 | if let Some(a) = self.name { | ||
| 317 | a.symbol() == name.symbol() | ||
| 318 | } else { | ||
| 319 | false | ||
| 320 | } | ||
| 321 | } | ||
| 322 | |||
| 323 | pub fn set_name(&mut self, name: Symbol<'a>) { | ||
| 324 | self.name = Some(name); | ||
| 325 | println!("Tag name: {:?}", self.name ); | ||
| 279 | } | 326 | } |
| 280 | 327 | ||
| 281 | pub fn add_param(&mut self, param: String) { | 328 | pub fn add_param(&mut self, param: String) { |
| 329 | println!("Add param: {:?}", param ); | ||
| 282 | self.params.push( param ); | 330 | self.params.push( param ); |
| 283 | } | 331 | } |
| 284 | 332 | ||
| 285 | pub fn add_prop(&mut self, key: String, value: String) { | 333 | pub fn add_prop(&mut self, key: String, value: String) { |
| 286 | self.props.push( (key, value) ); | 334 | println!("Add prop: {:?} = {:?}", key, value ); |
| 335 | self.props.insert( key, value ); | ||
| 336 | } | ||
| 337 | |||
| 338 | pub fn add_child(&mut self, token: Token) { | ||
| 339 | self.children.push( token ); | ||
| 287 | } | 340 | } |
| 288 | 341 | ||
| 289 | pub fn set_type(&mut self, tag_type: TagType) { | 342 | pub fn set_type(&mut self, tag_type: TagType) { |
| 290 | self.tag_type = tag_type; | 343 | self.tag_type = tag_type; |
| 291 | } | 344 | } |
| 345 | |||
| 346 | pub fn build(mut self) -> Result<Token,Box<dyn Error>> { | ||
| 347 | if let Some(sym) = self.name { | ||
| 348 | match sym.symbol() { | ||
| 349 | SymbolType::Fragment => { | ||
| 350 | Ok(Token::Fragment(self.params.swap_remove(0), self.props, self.children)) | ||
| 351 | } | ||
| 352 | SymbolType::Output => { | ||
| 353 | Ok(Token::Section(self.params.swap_remove(0), self.props, self.children)) | ||
| 354 | } | ||
| 355 | _ => { | ||
| 356 | println!("Unkown tag type"); | ||
| 357 | Err(Box::new(ParseError { | ||
| 358 | position: Position::new(0,0), | ||
| 359 | what: "Bad tag type".to_string(), | ||
| 360 | })) | ||
| 361 | } | ||
| 362 | } | ||
| 363 | } else { | ||
| 364 | Err(Box::new(ParseError { | ||
| 365 | position: Position::new(0,0), | ||
| 366 | what: "Bad tag type".to_string(), | ||
| 367 | })) | ||
| 368 | } | ||
| 369 | } | ||
| 292 | } | 370 | } |
