use crate::error::HedlResult;
use crate::value::Value;
use std::sync::OnceLock;
#[derive(Clone)]
struct LookupEntry {
pattern: &'static str,
value: ValueTemplate,
}
#[derive(Clone)]
enum ValueTemplate {
Bool(bool),
}
impl ValueTemplate {
#[inline(always)]
fn to_value(&self) -> Value {
match self {
ValueTemplate::Bool(b) => Value::Bool(*b),
}
}
}
static COMMON_VALUES: OnceLock<Vec<Option<LookupEntry>>> = OnceLock::new();
fn init_common_values() -> Vec<Option<LookupEntry>> {
let mut table = vec![None; 256];
let entries = [
("true", ValueTemplate::Bool(true)),
("false", ValueTemplate::Bool(false)),
];
for (pattern, value) in entries {
let hash = hash_string(pattern);
table[hash] = Some(LookupEntry { pattern, value });
}
table
}
#[inline(always)]
fn hash_string(s: &str) -> usize {
let len = s.len();
let first = s.as_bytes().first().copied().unwrap_or(0);
(len ^ ((first as usize) << 3)) & 0xFF
}
#[inline]
pub(super) fn try_lookup_common(s: &str) -> Option<Value> {
let table = COMMON_VALUES.get_or_init(init_common_values);
let hash = hash_string(s);
if let Some(entry) = &table[hash] {
if entry.pattern == s {
return Some(entry.value.to_value());
}
}
None
}
pub(super) fn infer_expanded_alias(s: &str, _line_num: usize) -> HedlResult<Value> {
if s == "true" {
return Ok(Value::Bool(true));
}
if s == "false" {
return Ok(Value::Bool(false));
}
if let Some(value) = try_parse_number(s) {
return Ok(value);
}
Ok(Value::String(Box::from(s)))
}
pub(super) fn try_parse_number(s: &str) -> Option<Value> {
let trimmed = s.trim();
let bytes = trimmed.as_bytes();
if bytes.is_empty() {
return None;
}
let first = bytes[0];
if first != b'-' && !first.is_ascii_digit() {
return None;
}
let has_decimal = memchr::memchr(b'.', bytes).is_some();
if has_decimal {
trimmed.parse::<f64>().ok().and_then(|f| {
if f.is_finite() && !trimmed.ends_with('.') {
Some(Value::Float(f))
} else {
None
}
})
} else {
trimmed.parse::<i64>().ok().map(Value::Int)
}
}
pub fn infer_quoted_value(s: &str) -> Value {
let unescaped = s.replace("\"\"", "\"");
Value::String(unescaped.into_boxed_str())
}