From 659fdd3506ad4d43c95819b2aee366466b396110 Mon Sep 17 00:00:00 2001 From: Scott Richmond Date: Thu, 3 Jul 2025 12:41:00 -0400 Subject: [PATCH] add string keys to dicts --- src/base.rs | 38 ++++++++++++++++++++++++++++---- src/value.rs | 62 ++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 82 insertions(+), 18 deletions(-) diff --git a/src/base.rs b/src/base.rs index 2dcf86c..21e8083 100644 --- a/src/base.rs +++ b/src/base.rs @@ -60,7 +60,15 @@ pub fn doc(f: &Value) -> Value { pub fn assoc(dict: &Value, key: &Value, value: &Value) -> Value { match (dict, key) { - (Value::Dict(d), Value::Keyword(k)) => Value::Dict(Box::new(d.update(k, value.clone()))), + (Value::Dict(d), Value::Keyword(k)) => { + Value::Dict(Box::new(d.update(Key::Keyword(k), value.clone()))) + } + (Value::Dict(d), Value::Interned(k)) => { + Value::Dict(Box::new(d.update(Key::Interned(k), value.clone()))) + } + (Value::Dict(d), Value::String(s)) => { + Value::Dict(Box::new(d.update(Key::String(s.clone()), value.clone()))) + } _ => unreachable!("internal Ludus error calling assoc with ({dict}, {key}, {value})"), } } @@ -175,7 +183,17 @@ pub fn dissoc(dict: &Value, key: &Value) -> Value { match (dict, key) { (Value::Dict(dict), Value::Keyword(key)) => { let mut new = dict.clone(); - new.remove(key); + new.remove(&Key::Keyword(key)); + Value::Dict(new) + } + (Value::Dict(dict), Value::Interned(key)) => { + let mut new = dict.clone(); + new.remove(&Key::Interned(key)); + Value::Dict(new) + } + (Value::Dict(dict), Value::String(key)) => { + let mut new = dict.clone(); + new.remove(&Key::String(key.clone())); Value::Dict(new) } _ => unreachable!("internal Ludus error"), @@ -220,7 +238,15 @@ pub fn at(ordered: &Value, i: &Value) -> Value { pub fn get(dict: &Value, key: &Value) -> Value { match (dict, key) { - (Value::Dict(dict), Value::Keyword(key)) => match dict.get(key) { + (Value::Dict(dict), Value::Keyword(key)) => match dict.get(&Key::Keyword(key)) { + Some(x) => x.clone(), + None => Value::Nil, + }, + (Value::Dict(dict), Value::Interned(key)) => match dict.get(&Key::Interned(key)) { + Some(x) => x.clone(), + None => Value::Nil, + }, + (Value::Dict(dict), Value::String(key)) => match dict.get(&Key::String(key.clone())) { Some(x) => x.clone(), None => Value::Nil, }, @@ -344,7 +370,7 @@ pub fn list(x: &Value) -> Value { let kvs = d.iter(); let mut list = vector![]; for (key, value) in kvs { - let kv = Value::Tuple(Rc::new(vec![Value::Keyword(key), value.clone()])); + let kv = Value::Tuple(Rc::new(vec![key.to_value(), value.clone()])); list.push_back(kv); } Value::List(Box::new(list)) @@ -645,5 +671,9 @@ pub fn make_base() -> Value { ("unbox", Value::BaseFn(BaseFn::Unary("unbox", unbox))), ("upcase", Value::BaseFn(BaseFn::Unary("upcase", upcase))), ]; + let members = members + .iter() + .map(|(name, bfn)| (Key::Keyword(name), bfn.clone())) + .collect::>(); Value::Dict(Box::new(HashMap::from(members))) } diff --git a/src/value.rs b/src/value.rs index 086fff4..0f8e5c8 100644 --- a/src/value.rs +++ b/src/value.rs @@ -112,6 +112,42 @@ pub struct Partial { pub function: Value, } +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum Key { + Keyword(&'static str), + Interned(&'static str), + String(Rc), +} + +impl std::fmt::Display for Key { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Key::Keyword(s) => write!(f, ":{s}"), + Key::Interned(s) => write!(f, "\"{s}\""), + Key::String(s) => write!(f, "\"{s}\""), + } + } +} + +impl Key { + pub fn to_value(&self) -> Value { + match self { + Key::Keyword(s) => Value::Keyword(s), + Key::Interned(s) => Value::Interned(s), + Key::String(s) => Value::String(s.clone()), + } + } + + pub fn from_value(value: Value) -> Key { + match value { + Value::Keyword(s) => Key::Keyword(s), + Value::Interned(s) => Key::Keyword(s), + Value::String(s) => Key::String(s.clone()), + _ => unreachable!("dict keys must be keywords or strings"), + } + } +} + #[derive(Clone, Debug)] pub enum Value { Nothing, @@ -124,7 +160,7 @@ pub enum Value { Number(f64), Tuple(Rc>), List(Box>), - Dict(Box>), + Dict(Box>), Box(Rc>), Fn(Rc), BaseFn(BaseFn), @@ -234,9 +270,8 @@ impl Value { let members = d .iter() .map(|(k, v)| { - let key_show = Value::Keyword(k).show(); let value_show = v.show(); - format!("{key_show} {value_show}") + format!("{k} {value_show}") }) .collect::>() .join(", "); @@ -338,9 +373,8 @@ impl Value { let members = d .iter() .map(|(k, v)| { - let key_show = Value::Keyword(k).stringify(); let value_show = v.stringify(); - format!("{key_show} {value_show}") + format!("{k} {value_show}") }) .collect::>() .join(", "); @@ -437,13 +471,13 @@ impl Value { Value::Tuple(Rc::new(vec)) } - pub fn get_shared_box(&self, name: &'static str) -> Value { - match self { - Value::Dict(dict) => dict - .get(name) - .expect("expected dict to have requested value") - .clone(), - _ => unreachable!("expected dict"), - } - } + // pub fn get_shared_box(&self, name: &'static str) -> Value { + // match self { + // Value::Dict(dict) => dict + // .get(name) + // .expect("expected dict to have requested value") + // .clone(), + // _ => unreachable!("expected dict"), + // } + // } }