extern crate alloc;
#[allow(unused_imports)]
#[cfg(all(not(feature = "std"), feature = "no-std-unix-debug"))]
use crate::println;
use crate::{
ast::{Arena, NodeRef, Text, TextQualifier},
parser::{Context, Delimiter, DelimiterProcessor, ParseStackElemRef},
text::{self, Reader, Segment},
util::{char_at, is_unicode_space, is_unicode_symbol_or_punct},
};
pub fn parse_delimiter<'a>(
arena: &mut Arena,
_parent_ref: NodeRef,
reader: &mut text::BlockReader<'a>,
minimum: usize,
processor: &DelimiterProcessor,
ctx: &mut Context,
) -> Option<NodeRef> {
let (line, segment) = reader.peek_line_bytes()?;
let c = line[0];
let mut j = 0;
if segment.padding() != 0 || !processor.is_delimiter(c) {
return None;
}
while j < line.len() && c == line[j] {
j += 1;
}
if j >= minimum {
let precending = reader.precending_charater();
let after = char_at(&line, j).unwrap_or(' ');
let before_is_punct = is_unicode_symbol_or_punct(precending);
let before_is_space = is_unicode_space(precending);
let after_is_punct = is_unicode_symbol_or_punct(after);
let after_is_space = is_unicode_space(after);
let is_left = !after_is_space && (!after_is_punct || before_is_space || before_is_punct);
let is_right = !before_is_space && (!before_is_punct || after_is_space || after_is_punct);
let can_open = if c == b'_' {
is_left && (!is_right || before_is_punct)
} else {
is_left
};
let can_close = if c == b'_' {
is_right && (!is_left || after_is_punct)
} else {
is_right
};
reader.advance(j);
let delim = Delimiter::new(c, j, can_open, can_close, processor.clone());
let seg: Segment = (segment.start(), segment.start() + j).into();
let t = arena.new_node(Text::with_qualifiers(seg, TextQualifier::TEMP));
ctx.delimiters_mut().push(delim, t);
return Some(t);
}
None
}
pub fn process_delimiters(arena: &mut Arena, bottom: Option<ParseStackElemRef>, ctx: &mut Context) {
let Some(last_delimiter_ref) = ctx.last_delimiter() else {
return;
};
let last_delimiter = ctx.delimiters().elem(last_delimiter_ref);
let mut closer_ref_opt: Option<ParseStackElemRef> = None;
if let Some(bottom_ref) = bottom {
if bottom_ref != last_delimiter_ref {
let mut c = last_delimiter.previous();
while c.is_some_and(|c| c != bottom_ref) {
closer_ref_opt = c;
c = ctx
.delimiters()
.get_elem(c.unwrap())
.and_then(|d| d.previous());
}
}
} else {
closer_ref_opt = ctx.delimiters().bottom();
}
if closer_ref_opt.is_none() {
ctx.delimiters_mut().remove_until(arena, bottom);
return;
};
while let Some(closer_ref) = closer_ref_opt {
let mut consume = 0usize;
let mut found = false;
let mut maybe_opener = false;
let mut opener_ref_opt: Option<ParseStackElemRef>;
{
let closer = ctx.delimiters().elem(closer_ref);
let closer_data = ctx.delimiters().data(closer_ref);
if !closer_data.can_close() {
closer_ref_opt = closer.next();
continue;
}
opener_ref_opt = closer.previous();
while let Some(opener_ref) = opener_ref_opt {
let opener = ctx.delimiters().elem(opener_ref);
if let Some(bottom) = bottom {
if opener_ref == bottom {
break;
}
}
let opener_data = ctx.delimiters().data(opener_ref);
if opener_data.can_open()
&& opener_data
.processor()
.can_open_closer(opener_data, closer_data)
{
maybe_opener = true;
consume = opener_data.calc_consumption(closer_data);
if consume > 0 {
found = true;
break;
}
}
opener_ref_opt = opener.previous();
}
}
if !found {
let can_open = ctx.delimiters().data(closer_ref).can_open();
let next = ctx.delimiters().elem(closer_ref).next();
if !maybe_opener && !can_open {
ctx.delimiters_mut().remove(arena, closer_ref);
}
closer_ref_opt = next;
continue;
}
let opener_ref = opener_ref_opt.unwrap();
ctx.delimiters_mut().data_mut(opener_ref).consume(consume);
ctx.delimiters_mut().data_mut(closer_ref).consume(consume);
let node_ref = (ctx.delimiters().data(opener_ref).processor().on_match)(arena, consume);
let opener = ctx.delimiters().elem(opener_ref).node_ref();
if let Some(p) = arena[opener].pos() {
arena[node_ref].set_pos(p);
}
let o = ctx.delimiters().elem(opener_ref).node_ref();
let p_opt = arena[o].parent();
let mut c_opt = arena[o].next_sibling();
let end = ctx.delimiters().elem(closer_ref).node_ref;
while let Some(c) = c_opt {
if c == end {
break;
}
let next = arena[c].next_sibling();
node_ref.append_child(arena, c);
c_opt = next;
}
if let Some(p) = p_opt {
p.insert_after(arena, o, node_ref);
}
let mut d_opt = ctx.delimiters().elem(opener_ref).next();
while let Some(d_ref) = d_opt {
if d_ref == closer_ref {
break;
}
let next = ctx.delimiters().elem(d_ref).next();
ctx.delimiters_mut().remove(arena, d_ref);
d_opt = next;
}
if ctx.delimiters().data(opener_ref).remaining() == 0 {
ctx.delimiters_mut().remove(arena, opener_ref);
}
if ctx.delimiters().data(closer_ref).remaining() == 0 {
let next = ctx.delimiters().elem(closer_ref).next();
ctx.delimiters_mut().remove(arena, closer_ref);
closer_ref_opt = next;
}
}
ctx.delimiters_mut().remove_until(arena, bottom);
}