Shared Rust kernels
eval/compile
All modules · quoted at commit 9e1884369a67 of DnaCalc/DnaCalc. Comments are the source's own.
declared_name · 39 lines · calc-fml/src/eval/compile.rs on GitHub/// The name a LET name or LAMBDA parameter node declares, upper case and without its file
/// prefix, and whether it is optional (`[y]`, or `_xlop.y` in a file). A node that is no
/// name, or that binding read as a reference, is refused as syntax Excel does not accept.
fn declared_name(&mut self, node: &SyntaxNode) -> Result<(String, bool), CompileError> {
let span = node.span();
let refused = CompileError::Syntax { span };
if node.kind() != SyntaxKind::IdentifierExpr {
return Err(refused);
}
let text = only_token(node).map(Token::text).ok_or(refused.clone())?;
if text.starts_with('[') {
// An optional parameter, which a formula inside a table may also read as a
// structured reference: as a parameter it is a name.
self.sites.remove(&span);
} else if self.sites.contains_key(&span) {
return Err(refused);
}
if text.eq_ignore_ascii_case("TRUE") || text.eq_ignore_ascii_case("FALSE") {
return Err(refused);
}
let (text, optional) = match text.strip_prefix('[').and_then(|t| t.strip_suffix(']')) {
Some(inner) => (inner, true),
None => match without_prefix(text, "_xlop.") {
Some(inner) => (inner, true),
None => (text, false),
},
};
let valid = text
.chars()
.next()
.is_some_and(|c| c.is_alphabetic() || c == '_' || c == '\\')
&& text
.chars()
.all(|c| c.is_alphanumeric() || c == '_' || c == '.');
if !valid {
return Err(refused);
}
Ok((parameter_key(text), optional))
}
Used by 2 functions.
parameter_key · 8 lines · calc-fml/src/eval/compile.rs on GitHub/// The key a LET name or LAMBDA parameter is matched by: without a leading `_xlpm.` (any ASCII
/// case), under the simple Unicode case fold Excel matches parameters by (dc-cpr:
/// `=LET(Größe,21,GRÖßE*2)` finds the parameter, `GRÖSSE` does not and is read as a defined
/// name; calc-value's `parameter_names_equal`). A defined name's block is keyed by the full fold,
/// as defined names compare.
fn parameter_key(text: &str) -> String {
calc_func::value::fold_parameter_name(without_prefix(text, "_xlpm.").unwrap_or(text))
}
Used by 2 functions.
without_prefix · 7 lines · calc-fml/src/eval/compile.rs on GitHub/// The rest of `text` when its first bytes are the ASCII `prefix` in any ASCII case. Bytes equal
/// to ASCII bytes are ASCII themselves, so the rest starts on a character boundary.
fn without_prefix<'t>(text: &'t str, prefix: &str) -> Option<&'t str> {
let n = prefix.len();
let starts = text.len() >= n && text.as_bytes()[..n].eq_ignore_ascii_case(prefix.as_bytes());
starts.then(|| &text[n..])
}
Used by 2 functions.
only_token · 7 lines · calc-fml/src/eval/compile.rs on GitHub/// The token of a node that holds exactly one.
fn only_token(node: &SyntaxNode) -> Option<&Token> {
match node.children() {
[SyntaxChild::Token(token)] => Some(token),
_ => None,
}
}
Used by 2 functions.
text · 13 lines · calc-fml/src/eval/compile.rs on GitHub/// A string literal's text: inside its quotes, each doubled quote one quote.
fn text(node: &SyntaxNode) -> Result<Literal, CompileError> {
let span = node.span();
let quoted = only_token(node)
.map(Token::text)
.ok_or(CompileError::Syntax { span })?;
let inner = quoted
.strip_prefix('"')
.and_then(|rest| rest.strip_suffix('"'))
.ok_or(CompileError::Syntax { span })?;
let units = inner.replace("\"\"", "\"").encode_utf16().collect();
Ok(Literal::Text(ExcelText::from_utf16_code_units(units)))
}
Used by 2 functions.