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.