parser: simplify BoolSlice logic with descriptive helper methods

This commit is contained in:
Simon Gardling 2025-12-05 20:20:00 -05:00
parent 3305227ffe
commit 3288752dfb
Signed by: titaniumtown
GPG Key ID: 9AB28AC10ECE533D

View File

@ -62,46 +62,73 @@ impl BoolSlice {
self.number && !self.masked_num self.number && !self.masked_num
} }
/// Returns true if this char is a function name letter (not a standalone variable)
const fn is_function_letter(&self) -> bool {
self.letter && !self.is_unmasked_variable()
}
/// Returns true if this is a "term" - something that can be multiplied
const fn is_term(&self) -> bool {
self.is_unmasked_number() || self.is_unmasked_variable() || self.letter
}
const fn calculate_mask(&mut self, other: &BoolSlice) { const fn calculate_mask(&mut self, other: &BoolSlice) {
if other.masked_num && self.number { if other.masked_num && self.number {
// If previous char was a masked number, and current char is a number, mask current char's variable status // Propagate number masking through consecutive digits
self.masked_num = true; self.masked_num = true;
} else if other.masked_var && self.variable { } else if other.masked_var && self.variable {
// If previous char was a masked variable, and current char is a variable, mask current char's variable status // Propagate variable masking through consecutive variables
self.masked_var = true; self.masked_var = true;
} else if other.letter && !other.is_unmasked_variable() { } else if other.is_function_letter() {
// After a function letter, mask following numbers/variables as part of function name
self.masked_num = self.number; self.masked_num = self.number;
self.masked_var = self.variable; self.masked_var = self.variable;
} }
} }
const fn splitable(&self, c: &char, other: &BoolSlice, split: &SplitType) -> bool { /// Determines if we should split (insert implicit multiplication) before current char
if (*c == '*') | (matches!(split, &SplitType::Term) && other.open_parens) { const fn splitable(&self, c: &char, prev: &BoolSlice, split: &SplitType) -> bool {
true // Always split on explicit multiplication
} else if other.closing_parens { if *c == '*' {
// Cases like `)x`, `)2`, and `)(`
return (*c == '(')
| (self.letter && !self.is_unmasked_variable())
| self.is_unmasked_variable()
| self.is_unmasked_number();
} else if *c == '(' {
// Cases like `x(` and `2(`
return (other.is_unmasked_variable() | other.is_unmasked_number()) && !other.letter;
} else if other.is_unmasked_number() {
// Cases like `2x` and `2sin(x)`
return self.is_unmasked_variable() | self.letter;
} else if self.is_unmasked_variable() | self.letter {
// Cases like `e2` and `xx`
return other.is_unmasked_number()
| (other.is_unmasked_variable() && self.is_unmasked_variable())
| other.is_unmasked_variable();
} else if (self.is_unmasked_number() | self.letter | self.is_unmasked_variable())
&& (other.is_unmasked_number() | other.letter)
{
return true; return true;
} else {
return self.is_unmasked_number() && other.is_unmasked_variable();
} }
// For Term split type, also split after open parens
if matches!(split, &SplitType::Term) && prev.open_parens {
return true;
}
// After closing paren: split before `(`, letters, variables, or numbers
// e.g., `)x`, `)2`, `)(`, `)sin`
if prev.closing_parens {
return *c == '(' || self.is_term();
}
// Before open paren: split if previous was a standalone number or variable
// e.g., `x(`, `2(` but not `sin(`
if *c == '(' {
return (prev.is_unmasked_variable() || prev.is_unmasked_number()) && !prev.letter;
}
// After a number: split before variables or function letters
// e.g., `2x`, `2sin`
if prev.is_unmasked_number() {
return self.is_unmasked_variable() || self.letter;
}
// Current is a variable or letter: split if previous was a number or variable
// e.g., `e2`, `xx`, `xe`
if self.is_unmasked_variable() || self.letter {
return prev.is_unmasked_number() || prev.is_unmasked_variable();
}
// Current is a number after a variable
// e.g., `x2`
if self.is_unmasked_number() && prev.is_unmasked_variable() {
return true;
}
false
} }
} }