use sim_kernel::Expr;
use crate::build::sym;
#[derive(Clone, Debug, PartialEq)]
pub enum Segment {
Key(Expr),
Index(usize),
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Path(pub Vec<Segment>);
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum PathError {
NotAList,
BadSegment,
NotAMap,
NotASequence,
MissingKey,
IndexOutOfBounds,
RemoveIndexUnsupported,
EmptyRemove,
}
impl Path {
pub fn new() -> Self {
Self(Vec::new())
}
pub fn key(mut self, key: Expr) -> Self {
self.0.push(Segment::Key(key));
self
}
pub fn index(mut self, index: usize) -> Self {
self.0.push(Segment::Index(index));
self
}
pub fn to_expr(&self) -> Expr {
Expr::List(
self.0
.iter()
.map(|segment| match segment {
Segment::Key(key) => Expr::Vector(vec![sym("k"), key.clone()]),
Segment::Index(index) => {
Expr::Vector(vec![sym("i"), Expr::String(index.to_string())])
}
})
.collect(),
)
}
pub fn from_expr(expr: &Expr) -> Result<Self, PathError> {
let Expr::List(segments) = expr else {
return Err(PathError::NotAList);
};
let mut out = Vec::with_capacity(segments.len());
for segment in segments {
out.push(parse_segment(segment)?);
}
Ok(Path(out))
}
}
fn parse_segment(segment: &Expr) -> Result<Segment, PathError> {
let Expr::Vector(parts) = segment else {
return Err(PathError::BadSegment);
};
match parts.as_slice() {
[Expr::Symbol(tag), key] if &*tag.name == "k" => Ok(Segment::Key(key.clone())),
[Expr::Symbol(tag), Expr::String(index)] if &*tag.name == "i" => index
.parse::<usize>()
.map(Segment::Index)
.map_err(|_| PathError::BadSegment),
_ => Err(PathError::BadSegment),
}
}
fn seq_items(value: &Expr) -> Option<&Vec<Expr>> {
match value {
Expr::List(items) | Expr::Vector(items) | Expr::Set(items) => Some(items),
_ => None,
}
}
fn rewrap(original: &Expr, items: Vec<Expr>) -> Expr {
match original {
Expr::Vector(_) => Expr::Vector(items),
Expr::Set(_) => Expr::Set(items),
_ => Expr::List(items),
}
}
pub fn get<'a>(root: &'a Expr, path: &Path) -> Option<&'a Expr> {
let mut current = root;
for segment in &path.0 {
current = match segment {
Segment::Key(key) => {
let Expr::Map(entries) = current else {
return None;
};
entries
.iter()
.find_map(|(entry_key, value)| (entry_key == key).then_some(value))?
}
Segment::Index(index) => seq_items(current)?.get(*index)?,
};
}
Some(current)
}
pub fn set_at(root: &Expr, path: &Path, value: Expr) -> Result<Expr, PathError> {
set_rec(root, &path.0, value)
}
fn set_rec(node: &Expr, segments: &[Segment], value: Expr) -> Result<Expr, PathError> {
let Some((first, rest)) = segments.split_first() else {
return Ok(value);
};
match first {
Segment::Key(key) => {
let Expr::Map(entries) = node else {
return Err(PathError::NotAMap);
};
let mut entries = entries.clone();
match entries.iter_mut().find(|(entry_key, _)| entry_key == key) {
Some(slot) => slot.1 = set_rec(&slot.1.clone(), rest, value)?,
None if rest.is_empty() => entries.push((key.clone(), value)),
None => return Err(PathError::MissingKey),
}
Ok(Expr::Map(entries))
}
Segment::Index(index) => {
let mut items = seq_items(node).ok_or(PathError::NotASequence)?.clone();
let slot = items.get(*index).ok_or(PathError::IndexOutOfBounds)?;
let replaced = set_rec(&slot.clone(), rest, value)?;
items[*index] = replaced;
Ok(rewrap(node, items))
}
}
}
pub fn remove_at(root: &Expr, path: &Path) -> Result<Expr, PathError> {
let Some((last, parents)) = path.0.split_last() else {
return Err(PathError::EmptyRemove);
};
let Segment::Key(key) = last else {
return Err(PathError::RemoveIndexUnsupported);
};
remove_rec(root, parents, key)
}
fn remove_rec(node: &Expr, parents: &[Segment], key: &Expr) -> Result<Expr, PathError> {
let Some((first, rest)) = parents.split_first() else {
let Expr::Map(entries) = node else {
return Err(PathError::NotAMap);
};
return Ok(Expr::Map(
entries
.iter()
.filter(|(entry_key, _)| entry_key != key)
.cloned()
.collect(),
));
};
match first {
Segment::Key(parent_key) => {
let Expr::Map(entries) = node else {
return Err(PathError::NotAMap);
};
let mut entries = entries.clone();
let slot = entries
.iter_mut()
.find(|(entry_key, _)| entry_key == parent_key)
.ok_or(PathError::MissingKey)?;
slot.1 = remove_rec(&slot.1.clone(), rest, key)?;
Ok(Expr::Map(entries))
}
Segment::Index(index) => {
let mut items = seq_items(node).ok_or(PathError::NotASequence)?.clone();
let slot = items.get(*index).ok_or(PathError::IndexOutOfBounds)?;
items[*index] = remove_rec(&slot.clone(), rest, key)?;
Ok(rewrap(node, items))
}
}
}