Handle several anchor edge cases

This commit is contained in:
Juno Takano 2026-01-03 16:02:11 -03:00
commit 9f04a4606c
3 changed files with 164 additions and 61 deletions

View file

@ -1,5 +1,3 @@
use std::{iter::Peekable, slice::Iter};
use crate::{
prelude::*,
syntax::content::parser::{
@ -11,61 +9,89 @@ use crate::{
///
/// This function is only called if the current inline context is Anchor.
///
/// A return kind of true will trigger a continue in the outer parser,
/// A return of `true` will trigger a continue in the outer parser,
/// skipping any further parsing of the current lexeme.
///
/// # Panics
/// This function will panic if can't determine the destination of an anchor.
pub fn parse(
lexeme: &Lexeme,
iterator: &mut Peekable<Iter<'_, Lexeme>>,
state: &mut State,
tokens: &mut Vec<Token>,
) -> bool {
log!("Resolving open context: {:#?}", state.clone().buffers.anchor);
log!(
"Resolving open context: {:#?}",
state.clone().buffers.anchor
);
let buffer = &mut state.buffers.anchor;
let candidate = &mut buffer.candidate;
// This is only true if the anchor is leading, otherwise the outer parser
// would already have set its text to the word before the first pipe
if candidate.text.is_empty() {
log!("Seeking text at {:#?} -> {:#?}", lexeme.text(), lexeme.next());
log!(
"Seeking end of text at {:#?} -> {:#?}",
lexeme.text(),
lexeme.next()
);
if lexeme.next() == "|" {
log!("End: Next lexeme is a pipe");
buffer.text.push_str(&lexeme.text());
candidate.text.clone_from(&buffer.text);
log!("End: {:#?}", lexeme.text());
return true;
} else {
log!("Pushing non-terminal {:#?} into buffer {:#?}",
lexeme.text(), buffer.text);
log!(
"Pushing non-terminal {:#?} into buffer {:#?}",
lexeme.text(),
buffer.text
);
buffer.text.push_str(&lexeme.text());
return true;
}
return true;
}
if candidate.destination.is_none() {
log!(
"Seeking end of destination at {:#?} -> {:#?}",
lexeme.text(),
lexeme.next()
);
log!("Seeking destination at {:#?} -> {:#?}",
lexeme.text(), lexeme.next());
// Conditions to this decision tree should match the destination end
if lexeme.last(){
log!("End: no more input");
// Conditions in this decision tree should match the destination end
if lexeme.match_as_char('s')
&& lexeme.is_next_boundary()
&& !lexeme.match_next_as_char('|')
{
log!("End: Plural anchor");
candidate.destination = Some(candidate.text.clone());
candidate.text.push('s');
if lexeme.last() {
tokens.push(Token::Anchor(candidate.clone()));
state.context.inline = Inline::None;
}
return true;
} else if lexeme.match_as_char('|') && lexeme.is_next_boundary() {
log!("End: Pipe followed by boundary");
if buffer.destination.is_empty() {
candidate.destination = Some(candidate.text.clone());
} else {
candidate.destination = Some(buffer.destination.clone());
return true
}
} else if lexeme.match_as_char('|') {
log!("Found a pipe, but no boundary: Destination likely follows");
tokens.push(Token::Anchor(candidate.clone()));
state.context.inline = Inline::None;
return true;
} else if lexeme.is_punctuation() && lexeme.is_next_whitespace() {
log!("Found puncutation followed by whitespace");
} else if lexeme.match_as_char('|') && !candidate.balanced {
log!("Found a pipe, but no boundary: Destination likely follows");
candidate.balanced = true;
return true;
} else if lexeme.match_as_char('|') {
log!("End: Explicit end-of-destination pipe");
candidate.destination = Some(buffer.destination.clone());
return true;
} else if !candidate.external
&& lexeme.is_punctuation()
&& lexeme.is_next_whitespace()
{
log!("End: Punctuation followed by whitespace");
candidate.destination = Some(buffer.destination.clone());
tokens.push(Token::Anchor(candidate.clone()));
state.context.inline = Inline::None;
@ -73,16 +99,28 @@ pub fn parse(
} else if lexeme.is_whitespace() {
log!("End: Whitespace");
candidate.destination = Some(buffer.destination.clone());
tokens.push(Token::Anchor(candidate.clone()));
state.context.inline = Inline::None;
return false;
// This else branch is the 'no end found yet' state and will keep
// pushing lexemes into the buffer until an end is found above
} else {
log!(
"Pushing non-terminal {:#?} into buffer {:#?}",
lexeme.text(), buffer.destination,
lexeme.text(),
buffer.destination,
);
if lexeme.match_as_char(':') {
candidate.external = true;
}
buffer.destination.push_str(&lexeme.text());
return true
if lexeme.last() {
candidate.destination = Some(buffer.destination.clone());
tokens.push(Token::Anchor(candidate.clone()));
state.context.inline = Inline::None;
}
return true;
}
}
@ -90,7 +128,8 @@ pub fn parse(
// which would mean there is some case where the end of the destination
// was never found and we kept filling the buffer endlessly,
// causing the program to panic anyways when rendering anchors
assert!(candidate.destination.is_some(),
assert!(
candidate.destination.is_some(),
"Anchor context parsing done but no destination found: {:#?}",
state.buffers.anchor
);