14. Update 'maybe_wrap_in_math' to remove 'wrap_within' and 'post_process'!

This commit is contained in:
Ian Wrzesinski 2024-10-10 17:51:05 -04:00
parent 97229d24e4
commit 4ce0b069f6

View File

@ -490,6 +490,8 @@ fn math_args(p: &mut Parser) {
let mut has_arrays = false; let mut has_arrays = false;
let mut array = p.marker(); let mut array = p.marker();
let mut arg = p.marker(); let mut arg = p.marker();
// The number of math expressions per argument.
let mut count = 0;
while !p.end() && !p.at(SyntaxKind::Dollar) { while !p.end() && !p.at(SyntaxKind::Dollar) {
if namable if namable
@ -506,20 +508,22 @@ fn math_args(p: &mut Parser) {
match p.current_text() { match p.current_text() {
")" => break, ")" => break,
";" => { ";" => {
maybe_wrap_in_math(p, arg, named); maybe_wrap_in_math(p, arg, count, named);
p.wrap(array, SyntaxKind::Array); p.wrap(array, SyntaxKind::Array);
p.convert_and_eat(SyntaxKind::Semicolon); p.convert_and_eat(SyntaxKind::Semicolon);
array = p.marker(); array = p.marker();
arg = p.marker(); arg = p.marker();
count = 0;
namable = true; namable = true;
named = None; named = None;
has_arrays = true; has_arrays = true;
continue; continue;
} }
"," => { "," => {
maybe_wrap_in_math(p, arg, named); maybe_wrap_in_math(p, arg, count, named);
p.convert_and_eat(SyntaxKind::Comma); p.convert_and_eat(SyntaxKind::Comma);
arg = p.marker(); arg = p.marker();
count = 0;
namable = true; namable = true;
if named.is_some() { if named.is_some() {
array = p.marker(); array = p.marker();
@ -532,6 +536,7 @@ fn math_args(p: &mut Parser) {
if p.at_set(set::MATH_EXPR) { if p.at_set(set::MATH_EXPR) {
math_expr(p); math_expr(p);
count += 1;
} else { } else {
p.unexpected(); p.unexpected();
} }
@ -540,7 +545,7 @@ fn math_args(p: &mut Parser) {
} }
if arg != p.marker() { if arg != p.marker() {
maybe_wrap_in_math(p, arg, named); maybe_wrap_in_math(p, arg, count, named);
if named.is_some() { if named.is_some() {
array = p.marker(); array = p.marker();
} }
@ -560,25 +565,26 @@ fn math_args(p: &mut Parser) {
p.wrap(m, SyntaxKind::Args); p.wrap(m, SyntaxKind::Args);
} }
/// Wrap math function arguments in a "Math" SyntaxKind to combine adjacent expressions /// Wrap math function arguments to join adjacent math content or create an
/// or create blank content. /// empty 'Math' node for when we have 0 args.
/// ///
/// We don't wrap when `exprs == 1`, as there is only one expression, so the grouping /// We don't wrap when `count == 1`, since wrapping would change the type of the
/// isn't needed, and this would change the type of the expression from potentially /// expression from potentially non-content to content. Ex: `$ func(#12pt) $`
/// non-content to content. /// would change the type from size to content if wrapped.
/// fn maybe_wrap_in_math(p: &mut Parser, arg: Marker, count: usize, named: Option<Marker>) {
/// Note that `exprs` might be 0 if we have whitespace or trivia before a comma i.e. if count == 0 {
/// `mat(; ,)` or `sin(x, , , ,)`. This would create an empty Math element before that // Flush trivia so that the new empty Math node will be wrapped _inside_
/// trivia if we called `p.wrap()` -- breaking the expected AST for 2-d arguments -- so // any `SyntaxKind::Array` elements created in `math_args`.
/// we instead manually wrap to our current marker using `p.wrap_within()`. // (And if we don't follow by wrapping in an array, it has no effect.)
fn maybe_wrap_in_math(p: &mut Parser, arg: Marker, named: Option<Marker>) { // The difference in node layout without this would look like:
let exprs = p.post_process(arg).filter(|node| node.is::<ast::Expr>()).count(); // Expression: `$ mat( ;) $`
if exprs != 1 { // - Correct: [ .., Space(" "), Array[Math[], ], Semicolon(";"), .. ]
// Convert 0 exprs into a blank math element (so empty arguments are allowed). // - Incorrect: [ .., Math[], Array[], Space(" "), Semicolon(";"), .. ]
// Convert 2+ exprs into a math element (so they become a joined sequence). p.flush_trivia();
p.wrap_within(arg, p.marker(), SyntaxKind::Math); }
// We need to update `n_trivia` since we no longer have any.
p.token.n_trivia = 0; // TODO: Maybe create a `flush_trivia()` method? if count != 1 {
p.wrap(arg, SyntaxKind::Math);
} }
if let Some(m) = named { if let Some(m) = named {
@ -1732,14 +1738,6 @@ impl<'s> Parser<'s> {
Marker(self.nodes.len() - self.token.n_trivia) Marker(self.nodes.len() - self.token.n_trivia)
} }
/// Iterate over the non-trivia tokens following the marker.
#[track_caller]
fn post_process(&mut self, m: Marker) -> impl Iterator<Item = &mut SyntaxNode> {
self.nodes[m.0..]
.iter_mut()
.filter(|child| !child.kind().is_error() && !child.kind().is_trivia())
}
/// Eat the current node and return a reference for in-place mutation. /// Eat the current node and return a reference for in-place mutation.
#[track_caller] #[track_caller]
fn eat_and_get(&mut self) -> &mut SyntaxNode { fn eat_and_get(&mut self) -> &mut SyntaxNode {
@ -1793,17 +1791,19 @@ impl<'s> Parser<'s> {
self.token = Self::lex(&mut self.nodes, &mut self.lexer, self.nl_mode); self.token = Self::lex(&mut self.nodes, &mut self.lexer, self.nl_mode);
} }
/// Detach the parsed trivia nodes from this token (but not newline info) so
/// that subsequent wrapping will include the trivia.
fn flush_trivia(&mut self) {
self.token.n_trivia = 0;
self.token.prev_end = self.token.start;
}
/// Wrap the nodes from a marker up to (but excluding) the current token in /// Wrap the nodes from a marker up to (but excluding) the current token in
/// a new [inner node](`SyntaxNode::inner`) of the given kind. This is an /// a new [inner node](`SyntaxNode::inner`) of the given kind. This is an
/// easy interface for creating nested syntax nodes _after_ having parsed /// easy interface for creating nested syntax nodes _after_ having parsed
/// their children. /// their children.
fn wrap(&mut self, from: Marker, kind: SyntaxKind) { fn wrap(&mut self, from: Marker, kind: SyntaxKind) {
self.wrap_within(from, self.before_trivia(), kind); let to = self.before_trivia().0;
}
fn wrap_within(&mut self, from: Marker, to: Marker, kind: SyntaxKind) {
let len = self.nodes.len();
let to = to.0.min(len);
let from = from.0.min(to); let from = from.0.min(to);
let children = self.nodes.drain(from..to).collect(); let children = self.nodes.drain(from..to).collect();
self.nodes.insert(from, SyntaxNode::inner(kind, children)); self.nodes.insert(from, SyntaxNode::inner(kind, children));