diff --git a/core/src/ast.rs b/core/src/ast.rs index 33ab59fc..7a986ddf 100644 --- a/core/src/ast.rs +++ b/core/src/ast.rs @@ -678,6 +678,46 @@ pub(crate) fn resolve_identifier( attrs: Attrs, context: &mut crate::Context, int: &I, +) -> FResult { + let cloned_scope = scope.clone(); + if let Some(ref scope) = cloned_scope { + if let Some(val) = scope.get(ident, attrs, context, int)? { + return Ok(val); + } + } + if let Some(val) = context.variables.get(ident.as_str()) { + return Ok(val.clone()); + } + + let builtin_result = resolve_builtin_identifier(ident, cloned_scope, attrs, context, int); + if !matches!(builtin_result, Err(FendError::IdentifierNotFound(_))) { + return builtin_result; + } + let unit_result = crate::units::query_unit(ident.as_str(), attrs, context, int); + if !matches!(unit_result, Err(FendError::IdentifierNotFound(_))) { + return unit_result; + } + + if !ident.as_str().bytes().all(|b| b.is_ascii_digit() || b.is_ascii_uppercase()) { + return unit_result; + } + let lowercase_builtin_result = resolve_builtin_identifier( + &ident.as_str().to_ascii_lowercase().into(), + scope, + attrs, + context, + int, + ); + // "Unknown identifier" errors should use the uppercase ident. + lowercase_builtin_result.or(unit_result) +} + +fn resolve_builtin_identifier( + ident: &Ident, + scope: Option>, + attrs: Attrs, + context: &mut crate::Context, + int: &I, ) -> FResult { macro_rules! eval_box { ($input:expr) => { @@ -690,14 +730,6 @@ pub(crate) fn resolve_identifier( )?) }; } - if let Some(scope) = scope.clone() { - if let Some(val) = scope.get(ident, attrs, context, int)? { - return Ok(val); - } - } - if let Some(val) = context.variables.get(ident.as_str()) { - return Ok(val.clone()); - } Ok(match ident.as_str() { "pi" | "\u{3c0}" => Value::Num(Box::new(Number::pi())), "tau" | "\u{3c4}" => Value::Num(Box::new(Number::pi().mul(2.into(), int)?)), @@ -774,7 +806,7 @@ pub(crate) fn resolve_identifier( "tomorrow" => Value::Date(crate::date::Date::today(context)?.next()), "yesterday" => Value::Date(crate::date::Date::today(context)?.prev()), "trans" => Value::String(Cow::Borrowed("🏳️‍⚧️")), - _ => return crate::units::query_unit(ident.as_str(), attrs, context, int), + _ => return Err(FendError::IdentifierNotFound(ident.clone())), }) } diff --git a/core/tests/integration_tests.rs b/core/tests/integration_tests.rs index 86f4870b..cfe28701 100644 --- a/core/tests/integration_tests.rs +++ b/core/tests/integration_tests.rs @@ -5994,3 +5994,14 @@ fn fibonacci() { test_eval("fib 10", "55"); test_eval("fib 11", "89"); } + +#[test] +fn uppercase_identifiers() { + test_eval("SIN PI", "0"); + test_eval("COS TAU", "1"); + test_eval("LOG 1", "approx. 0"); + test_eval("LOG10 1", "approx. 0"); + test_eval("EXP 0", "approx. 1"); + + expect_error("foo = 1; FOO", Some("unknown identifier 'FOO'")); +}