diff options
| author | Mike Buland <mike@xagasoft.com> | 2026-06-08 12:32:06 -0700 |
|---|---|---|
| committer | Mike Buland <mike@xagasoft.com> | 2026-06-08 12:32:06 -0700 |
| commit | 3e60b2746bb95cfcd9b7fa4b7ed9e1c72259fb23 (patch) | |
| tree | 3034717e3bcaeb4f93b03dc89ce81f77feda9bc7 /src/parser.rs | |
| parent | 55750a223008b256fddd8636ff4d474f959c8a23 (diff) | |
| download | crimtag-3e60b2746bb95cfcd9b7fa4b7ed9e1c72259fb23.tar.gz crimtag-3e60b2746bb95cfcd9b7fa4b7ed9e1c72259fb23.tar.bz2 crimtag-3e60b2746bb95cfcd9b7fa4b7ed9e1c72259fb23.tar.xz crimtag-3e60b2746bb95cfcd9b7fa4b7ed9e1c72259fb23.zip | |
It parses correctly for everything defined.
I think I'll change fragment to view, it's nicer and shorter.
Diffstat (limited to 'src/parser.rs')
| -rw-r--r-- | src/parser.rs | 236 |
1 files changed, 206 insertions, 30 deletions
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> { | |||
| 51 | pub fn peek(&self) -> Option<Symbol<'a>> { | 51 | pub fn peek(&self) -> Option<Symbol<'a>> { |
| 52 | self.cur[(self.icur+1)%2] | 52 | self.cur[(self.icur+1)%2] |
| 53 | } | 53 | } |
| 54 | |||
| 55 | pub fn check_unwrapped<T: Fn( &SymbolType, &SymbolType ) -> bool>(&self, f: T) -> ParseResult<bool> { | ||
| 56 | if self.cur().is_none() || self.peek().is_none() { | ||
| 57 | Err(ParseError{ | ||
| 58 | position: Position::new(0,0), | ||
| 59 | what: "Unexpected end of stream.".to_string() | ||
| 60 | }) | ||
| 61 | } else { | ||
| 62 | Ok(f( self.cur().unwrap().symbol(), self.peek().unwrap().symbol())) | ||
| 63 | } | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | trait SymbolHelper { | ||
| 68 | fn is<T: Fn( &SymbolType ) -> bool>(&self, f: T ) -> ParseResult<bool>; | ||
| 69 | fn is_valid_tag_name(&self) -> ParseResult<bool>; | ||
| 54 | } | 70 | } |
| 55 | 71 | ||
| 56 | impl Option<Symbol> { | 72 | impl<'a> SymbolHelper for Option<Symbol<'a>> { |
| 57 | pub fn is<T: Fn( &SymbolType ) -> bool>(&self, f: T ) -> ParseError<bool> { | 73 | fn is<T: Fn( &SymbolType ) -> bool>(&self, f: T ) -> ParseResult<bool> { |
| 74 | if let Some(st) = self { | ||
| 75 | Ok(f( &st.symbol() )) | ||
| 76 | } else { | ||
| 77 | Err(ParseError{ | ||
| 78 | position: Position::new(0,0), | ||
| 79 | what: "Unexpected end of stream.".to_string() | ||
| 80 | }) | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | fn is_valid_tag_name(&self) -> ParseResult<bool> { | ||
| 58 | if let Some(st) = self { | 85 | if let Some(st) = self { |
| 59 | Ok(f( &self.symbol )) | 86 | Ok(match st.symbol() { |
| 87 | SymbolType::Token(_) | | ||
| 88 | SymbolType::Fragment | | ||
| 89 | SymbolType::Output | | ||
| 90 | SymbolType::Show => true, | ||
| 91 | _ => false | ||
| 92 | }) | ||
| 60 | } else { | 93 | } else { |
| 61 | Err(ParseError{ | 94 | Err(ParseError{ |
| 62 | position: Position::new(0,0), | 95 | position: Position::new(0,0), |
| @@ -166,15 +199,15 @@ impl Parser { | |||
| 166 | } | 199 | } |
| 167 | } | 200 | } |
| 168 | 201 | ||
| 169 | pub fn parse(&mut self, src: &str ) -> ParseResult<Token> { | 202 | pub fn parse(&self, src: &str ) -> ParseResult<Token> { |
| 170 | let mut ll = Lexer::new( src ); | 203 | let ll = Lexer::new( src ); |
| 171 | let mut ctx = Context::new( ll ); | 204 | let mut ctx = Context::new( ll ); |
| 172 | 205 | ||
| 173 | // Parse the root of the file, the input context | 206 | // Parse the root of the file, the input context |
| 174 | self.p_input( &mut ctx ) | 207 | self.p_input( &mut ctx ) |
| 175 | } | 208 | } |
| 176 | 209 | ||
| 177 | fn p_input(&mut self, ctx: &mut Context ) -> ParseResult<Token> { | 210 | fn p_input(&self, ctx: &mut Context ) -> ParseResult<Token> { |
| 178 | let mut children = Vec::new(); | 211 | let mut children = Vec::new(); |
| 179 | loop { | 212 | loop { |
| 180 | if ctx.cur().is_none() { | 213 | if ctx.cur().is_none() { |
| @@ -183,7 +216,7 @@ impl Parser { | |||
| 183 | match ctx.cur().unwrap().symbol() { | 216 | match ctx.cur().unwrap().symbol() { |
| 184 | SymbolType::Text(_) => { /* Skip top level text */ } | 217 | SymbolType::Text(_) => { /* Skip top level text */ } |
| 185 | SymbolType::StartFlat => { | 218 | SymbolType::StartFlat => { |
| 186 | children.push( self.parse_tag( ctx )? ); | 219 | children.push( self.parse_tag_fragment( ctx )? ); |
| 187 | } | 220 | } |
| 188 | SymbolType::Error{..} => { | 221 | SymbolType::Error{..} => { |
| 189 | return self.lex_error( &ctx.cur().unwrap() ); | 222 | return self.lex_error( &ctx.cur().unwrap() ); |
| @@ -197,12 +230,99 @@ impl Parser { | |||
| 197 | Ok(Token::Root(children)) | 230 | Ok(Token::Root(children)) |
| 198 | } | 231 | } |
| 199 | 232 | ||
| 200 | fn parse_tag(&mut self, ctx: &mut Context) -> ParseResult<TagBuilder> { | 233 | fn parse_tag_fragment(&self, ctx: &mut Context) -> ParseResult<Token> { |
| 201 | let mut tb = TagBuilder::new(); | 234 | let mut tb = self.parse_open_tag( ctx )?; |
| 235 | if !tb.is_name( &SymbolType::Fragment ) { | ||
| 236 | return Err(ParseError{ | ||
| 237 | position: tb.start_pos(), | ||
| 238 | what: "Unexpected tag type, only frament allowed at root".to_string(), | ||
| 239 | }); | ||
| 240 | } | ||
| 241 | |||
| 242 | if tb.is_unary() { | ||
| 243 | return tb.build(); | ||
| 244 | } | ||
| 245 | let mut children = Vec::new(); | ||
| 246 | |||
| 247 | println!("--parse-tag-fragment-- tag parsed, next token: {:?}", ctx.cur() ); | ||
| 248 | loop { | ||
| 249 | if ctx.cur().is_none() { | ||
| 250 | return Err(ParseError{ | ||
| 251 | position: Position::new(0,0), | ||
| 252 | what: "Unexpected end of stream.".to_string() | ||
| 253 | }); | ||
| 254 | } | ||
| 255 | match ctx.cur().unwrap().symbol() { | ||
| 256 | SymbolType::Text(s) => { | ||
| 257 | children.push( Token::Text(s.to_string()) ); | ||
| 258 | ctx.next(); | ||
| 259 | } | ||
| 260 | SymbolType::StartFlat => { | ||
| 261 | //child.is_name(SymbolType::Output) | ||
| 262 | children.push( self.parse_tag_body( ctx )? ); | ||
| 263 | } | ||
| 264 | SymbolType::StartPoint => { | ||
| 265 | let end = self.parse_end_tag( ctx )?; | ||
| 266 | println!("End tag: {:?}, open tag: {:?}", end, tb.name() ); | ||
| 267 | if tb.is_name(end.symbol()) { | ||
| 268 | // They match, time to decide what we're doing. | ||
| 269 | let any_outputs = children.iter().any( | ||
| 270 | |t| matches!(t, Token::Output(..)) | ||
| 271 | ); | ||
| 272 | if children.iter().all(|t| matches!(t, Token::Text(..)) || matches!(t, Token::Output(..))) { | ||
| 273 | // Everything is either text or output | ||
| 274 | if !any_outputs { | ||
| 275 | // Special case, if it's only text then we create an implicit output. | ||
| 276 | tb.add_child(Token::Output("content".to_string(), Properties::new(), children )); | ||
| 277 | } | ||
| 278 | else { | ||
| 279 | // Standard case, outputs are included, text is | ||
| 280 | // skipped. | ||
| 281 | for token in children { | ||
| 282 | if matches!(token, Token::Output(..)) { | ||
| 283 | tb.add_child( token ); | ||
| 284 | } | ||
| 285 | } | ||
| 286 | } | ||
| 287 | } else { | ||
| 288 | // We have another mix, now check to see if there | ||
| 289 | // are any outptus, if so this is an error. | ||
| 290 | if any_outputs { | ||
| 291 | return Err(ParseError{ | ||
| 292 | position: *end.start(), | ||
| 293 | what: "You cannot mix non-output and output tags in a view.".to_string() | ||
| 294 | }); | ||
| 295 | } else { | ||
| 296 | // No outputs at all, we create an implict output | ||
| 297 | tb.add_child(Token::Output("content".to_string(), Properties::new(), children )); | ||
| 298 | } | ||
| 299 | } | ||
| 300 | |||
| 301 | return tb.build(); | ||
| 302 | } else { | ||
| 303 | // They don't match, complain. | ||
| 304 | return Err(ParseError{ | ||
| 305 | position: *end.start(), | ||
| 306 | what: "Mismatched open and closing tags.".to_string() | ||
| 307 | }); | ||
| 308 | } | ||
| 309 | } | ||
| 310 | SymbolType::Error{..} => { | ||
| 311 | return self.lex_error( &ctx.cur().unwrap() ); | ||
| 312 | } | ||
| 313 | _ => { | ||
| 314 | ctx.next(); | ||
| 315 | } | ||
| 316 | } | ||
| 317 | } | ||
| 318 | } | ||
| 319 | |||
| 320 | fn parse_open_tag<'a>(&self, ctx: &mut Context<'a>) -> ParseResult<TagBuilder<'a>> { | ||
| 202 | 321 | ||
| 203 | println!("--> Begin parse_tag <--"); | 322 | println!("--> Begin parse_tag <--"); |
| 204 | 323 | ||
| 205 | let start_sym = ctx.cur().unwrap(); | 324 | let start_sym = ctx.cur().unwrap(); |
| 325 | let mut tb = TagBuilder::new(&start_sym); | ||
| 206 | 326 | ||
| 207 | println!("--> Start sym: {:?}", start_sym ); | 327 | println!("--> Start sym: {:?}", start_sym ); |
| 208 | 328 | ||
| @@ -239,19 +359,40 @@ impl Parser { | |||
| 239 | } | 359 | } |
| 240 | } | 360 | } |
| 241 | 361 | ||
| 362 | ctx.next(); | ||
| 363 | |||
| 242 | Ok(tb) | 364 | Ok(tb) |
| 243 | } | 365 | } |
| 244 | 366 | ||
| 245 | fn parse_end_tag(&mut self, ctx: &mut Context ) -> &'a str { | 367 | fn parse_end_tag<'a>(&self, ctx: &mut Context<'a> ) -> ParseResult<Symbol<'a>> { |
| 246 | if !ctx.cur().is(|s| matches!(s, Symbol::StartPoint))? { | 368 | if !ctx.cur().is(|s| *s == SymbolType::StartPoint )? { |
| 369 | return Err(ParseError{ | ||
| 370 | position: *ctx.cur().unwrap().start(), | ||
| 371 | what: "Invalid end tag?".to_string(), | ||
| 372 | }); | ||
| 373 | } | ||
| 374 | if !ctx.next().is_valid_tag_name()? { | ||
| 375 | return Err(ParseError{ | ||
| 376 | position: *ctx.cur().unwrap().start(), | ||
| 377 | what: "Invalid tag name".to_string(), | ||
| 378 | }); | ||
| 379 | } | ||
| 380 | let name = ctx.cur().unwrap(); | ||
| 381 | if !ctx.next().is(|s| *s == SymbolType::EndFlat )? { | ||
| 247 | return Err(ParseError{ | 382 | return Err(ParseError{ |
| 248 | position: ctx.cur(). | 383 | position: *ctx.cur().unwrap().start(), |
| 384 | what: "Tag should be <| |] style end tag.".to_string(), | ||
| 249 | }); | 385 | }); |
| 250 | } | 386 | } |
| 251 | ctx.next() | 387 | Ok(name) |
| 252 | } | 388 | } |
| 253 | 389 | ||
| 254 | fn parse_tag_body(&mut self, ctx: &mut Context, tb: &mut TagBuilder ) -> ParseResult<()> { | 390 | fn parse_tag_body(&self, ctx: &mut Context ) -> ParseResult<Token> { |
| 391 | let mut tb = self.parse_open_tag( ctx )?; | ||
| 392 | if tb.is_unary() { | ||
| 393 | return tb.build(); | ||
| 394 | } | ||
| 395 | |||
| 255 | loop { | 396 | loop { |
| 256 | if ctx.cur().is_none() { | 397 | if ctx.cur().is_none() { |
| 257 | return Err(ParseError{position: Position::new(0,0), what: "Unexpecetd end of stream".to_string()}); | 398 | return Err(ParseError{position: Position::new(0,0), what: "Unexpecetd end of stream".to_string()}); |
| @@ -259,28 +400,37 @@ impl Parser { | |||
| 259 | match ctx.cur().unwrap().symbol() { | 400 | match ctx.cur().unwrap().symbol() { |
| 260 | SymbolType::Text(s) => { | 401 | SymbolType::Text(s) => { |
| 261 | tb.add_child(Token:: Text(s.to_string())); | 402 | tb.add_child(Token:: Text(s.to_string())); |
| 403 | ctx.next(); | ||
| 262 | } | 404 | } |
| 263 | SymbolType::StartFlat => { | 405 | SymbolType::StartFlat => { |
| 264 | tb.add_child( self.parse_tag( ctx )? ); | 406 | tb.add_child( self.parse_tag_body( ctx )? ); |
| 407 | println!("Following tag body: {:?} {:?}", ctx.cur(), ctx.peek() ); | ||
| 265 | } | 408 | } |
| 266 | SymbolType::StartPoint => { | 409 | SymbolType::StartPoint => { |
| 267 | let end_tag = self.parse_end_tag( ctx )?; | 410 | let end = self.parse_end_tag( ctx )?; |
| 268 | 411 | println!("End tag: {:?}, open tag: {:?}", end, tb.name() ); | |
| 412 | if tb.is_name(end.symbol()) { | ||
| 413 | // They match, end. | ||
| 414 | return tb.build(); | ||
| 415 | } else { | ||
| 416 | // They don't match, complain. | ||
| 417 | return Err(ParseError{ | ||
| 418 | position: *end.start(), | ||
| 419 | what: "Mismatched open and closing tags.".to_string() | ||
| 420 | }); | ||
| 421 | } | ||
| 269 | } | 422 | } |
| 270 | SymbolType::Error{..} => { | 423 | SymbolType::Error{..} => { |
| 271 | return self.lex_error( &ctx.cur().unwrap() ); | 424 | self.lex_error( &ctx.cur().unwrap() )?; |
| 272 | } | 425 | } |
| 273 | _ => { | 426 | _ => { |
| 274 | 427 | ||
| 275 | } | 428 | } |
| 276 | } | 429 | } |
| 277 | ctx.next(); | ||
| 278 | } | 430 | } |
| 279 | Ok(Token::Root(children)) | ||
| 280 | Ok(()) | ||
| 281 | } | 431 | } |
| 282 | 432 | ||
| 283 | fn parse_tag_params(&mut self, ctx: &mut Context, tb: &mut TagBuilder ) -> ParseResult<()> { | 433 | fn parse_tag_params(&self, ctx: &mut Context, tb: &mut TagBuilder ) -> ParseResult<()> { |
| 284 | loop { | 434 | loop { |
| 285 | if ctx.cur().is_none() { | 435 | if ctx.cur().is_none() { |
| 286 | 436 | ||
| @@ -299,7 +449,7 @@ impl Parser { | |||
| 299 | Ok(()) | 449 | Ok(()) |
| 300 | } | 450 | } |
| 301 | 451 | ||
| 302 | fn parse_tag_props(&mut self, ctx: &mut Context, tb: &mut TagBuilder ) -> ParseResult<()> { | 452 | fn parse_tag_props(&self, ctx: &mut Context, tb: &mut TagBuilder ) -> ParseResult<()> { |
| 303 | loop { | 453 | loop { |
| 304 | if ctx.cur().is_none() { | 454 | if ctx.cur().is_none() { |
| 305 | return Err(ParseError{position: Position::new(0,0), what: "Unexpecetd end of stream".to_string()}); | 455 | return Err(ParseError{position: Position::new(0,0), what: "Unexpecetd end of stream".to_string()}); |
| @@ -325,7 +475,7 @@ impl Parser { | |||
| 325 | } | 475 | } |
| 326 | } | 476 | } |
| 327 | 477 | ||
| 328 | #[derive(PartialEq)] | 478 | #[derive(PartialEq,Copy,Clone,Debug)] |
| 329 | enum TagType { | 479 | enum TagType { |
| 330 | Unknown, | 480 | Unknown, |
| 331 | Unary, | 481 | Unary, |
| @@ -339,20 +489,34 @@ struct TagBuilder<'a> { | |||
| 339 | props: Properties, | 489 | props: Properties, |
| 340 | children: Vec<Token>, | 490 | children: Vec<Token>, |
| 341 | tag_type: TagType, | 491 | tag_type: TagType, |
| 492 | start_pos: Position, | ||
| 342 | } | 493 | } |
| 343 | 494 | ||
| 344 | impl<'a> TagBuilder<'a> { | 495 | impl<'a> TagBuilder<'a> { |
| 345 | pub fn new() -> TagBuilder<'a> { | 496 | pub fn new(start: &Symbol) -> TagBuilder<'a> { |
| 346 | TagBuilder { | 497 | TagBuilder { |
| 347 | name: None, | 498 | name: None, |
| 348 | params: Vec::new(), | 499 | params: Vec::new(), |
| 349 | props: Properties::new(), | 500 | props: Properties::new(), |
| 350 | children: Vec::new(), | 501 | children: Vec::new(), |
| 351 | tag_type: TagType::Unknown, | 502 | tag_type: TagType::Unknown, |
| 503 | start_pos: *start.start(), | ||
| 352 | } | 504 | } |
| 353 | } | 505 | } |
| 354 | 506 | ||
| 355 | pub fn has_children(&self) -> bool { | 507 | pub fn start_pos(&self) -> Position { |
| 508 | self.start_pos | ||
| 509 | } | ||
| 510 | |||
| 511 | pub fn is_unary(&self) -> bool { | ||
| 512 | if self.tag_type == TagType::Unary { | ||
| 513 | true | ||
| 514 | } else { | ||
| 515 | false | ||
| 516 | } | ||
| 517 | } | ||
| 518 | |||
| 519 | pub fn can_have_children(&self) -> bool { | ||
| 356 | if self.tag_type == TagType::BinaryOpen { | 520 | if self.tag_type == TagType::BinaryOpen { |
| 357 | true | 521 | true |
| 358 | } else { | 522 | } else { |
| @@ -360,14 +524,18 @@ impl<'a> TagBuilder<'a> { | |||
| 360 | } | 524 | } |
| 361 | } | 525 | } |
| 362 | 526 | ||
| 363 | pub fn is_same_name(&self, name: Symbol<'a>) -> bool { | 527 | pub fn is_name(&self, name: &SymbolType<'a>) -> bool { |
| 364 | if let Some(a) = self.name { | 528 | if let Some(a) = self.name { |
| 365 | a.symbol() == name.symbol() | 529 | a.symbol() == name |
| 366 | } else { | 530 | } else { |
| 367 | false | 531 | false |
| 368 | } | 532 | } |
| 369 | } | 533 | } |
| 370 | 534 | ||
| 535 | pub fn name(&self) -> Option<Symbol> { | ||
| 536 | self.name | ||
| 537 | } | ||
| 538 | |||
| 371 | pub fn set_name(&mut self, name: Symbol<'a>) { | 539 | pub fn set_name(&mut self, name: Symbol<'a>) { |
| 372 | self.name = Some(name); | 540 | self.name = Some(name); |
| 373 | println!("Tag name: {:?}", self.name ); | 541 | println!("Tag name: {:?}", self.name ); |
| @@ -387,10 +555,18 @@ impl<'a> TagBuilder<'a> { | |||
| 387 | self.children.push( token ); | 555 | self.children.push( token ); |
| 388 | } | 556 | } |
| 389 | 557 | ||
| 558 | pub fn append_children(&mut self, children: &mut Vec::<Token>) { | ||
| 559 | self.children.append( children ); | ||
| 560 | } | ||
| 561 | |||
| 390 | pub fn set_type(&mut self, tag_type: TagType) { | 562 | pub fn set_type(&mut self, tag_type: TagType) { |
| 391 | self.tag_type = tag_type; | 563 | self.tag_type = tag_type; |
| 392 | } | 564 | } |
| 393 | 565 | ||
| 566 | pub fn tag_type(&self) -> TagType { | ||
| 567 | self.tag_type | ||
| 568 | } | ||
| 569 | |||
| 394 | pub fn build(mut self) -> ParseResult<Token> { | 570 | pub fn build(mut self) -> ParseResult<Token> { |
| 395 | if let Some(sym) = self.name { | 571 | if let Some(sym) = self.name { |
| 396 | match sym.symbol() { | 572 | match sym.symbol() { |
| @@ -406,14 +582,14 @@ impl<'a> TagBuilder<'a> { | |||
| 406 | _ => { | 582 | _ => { |
| 407 | println!("Unkown tag type"); | 583 | println!("Unkown tag type"); |
| 408 | Err(ParseError { | 584 | Err(ParseError { |
| 409 | position: Position::new(0,0), | 585 | position: self.start_pos, |
| 410 | what: "Bad tag type".to_string(), | 586 | what: "Bad tag type".to_string(), |
| 411 | }) | 587 | }) |
| 412 | } | 588 | } |
| 413 | } | 589 | } |
| 414 | } else { | 590 | } else { |
| 415 | Err(ParseError { | 591 | Err(ParseError { |
| 416 | position: Position::new(0,0), | 592 | position: self.start_pos, |
| 417 | what: "Bad tag type".to_string(), | 593 | what: "Bad tag type".to_string(), |
| 418 | }) | 594 | }) |
| 419 | } | 595 | } |
