use sim_kernel::{Expr, Symbol};
use sim_value::build::qsym;
#[derive(Clone, Debug, PartialEq)]
pub enum TableOp {
Get(Symbol),
Set(Symbol, Expr),
Has(Symbol),
Delete(Symbol),
Keys,
Entries,
Len,
Clear,
Mkdir(Symbol),
Opendir(Symbol),
Rmdir(Symbol),
IsDir(Symbol),
}
#[derive(Clone, Debug, PartialEq)]
pub enum TableOpError {
NotATableCall,
UnknownOp(String),
BadArity(String),
BadArg(String),
}
fn wire_name(op: &TableOp) -> &'static str {
match op {
TableOp::Get(_) => "get",
TableOp::Set(_, _) => "set",
TableOp::Has(_) => "has",
TableOp::Delete(_) => "del",
TableOp::Keys => "keys",
TableOp::Entries => "entries",
TableOp::Len => "len",
TableOp::Clear => "clear",
TableOp::Mkdir(_) => "mkdir",
TableOp::Opendir(_) => "opendir",
TableOp::Rmdir(_) => "rmdir",
TableOp::IsDir(_) => "dir?",
}
}
pub fn encode_table_op(op: &TableOp) -> Expr {
let args = match op {
TableOp::Get(key)
| TableOp::Has(key)
| TableOp::Delete(key)
| TableOp::Mkdir(key)
| TableOp::Opendir(key)
| TableOp::Rmdir(key)
| TableOp::IsDir(key) => vec![Expr::Symbol(key.clone())],
TableOp::Set(key, value) => vec![Expr::Symbol(key.clone()), value.clone()],
TableOp::Keys | TableOp::Entries | TableOp::Len | TableOp::Clear => Vec::new(),
};
Expr::Call {
operator: Box::new(qsym("table", wire_name(op))),
args,
}
}
fn one_key(op: &str, args: &[Expr]) -> Result<Symbol, TableOpError> {
match args {
[Expr::Symbol(key)] => Ok(key.clone()),
[_] => Err(TableOpError::BadArg(op.to_owned())),
_ => Err(TableOpError::BadArity(op.to_owned())),
}
}
fn no_args(op: &str, args: &[Expr]) -> Result<(), TableOpError> {
if args.is_empty() {
Ok(())
} else {
Err(TableOpError::BadArity(op.to_owned()))
}
}
pub fn decode_table_op(expr: &Expr) -> Result<TableOp, TableOpError> {
let Expr::Call { operator, args } = expr else {
return Err(TableOpError::NotATableCall);
};
let Expr::Symbol(symbol) = operator.as_ref() else {
return Err(TableOpError::NotATableCall);
};
if symbol.namespace.as_deref() != Some("table") {
return Err(TableOpError::NotATableCall);
}
let name = symbol.name.as_ref();
let op = match name {
"get" => TableOp::Get(one_key(name, args)?),
"set" => match args.as_slice() {
[Expr::Symbol(key), value] => TableOp::Set(key.clone(), value.clone()),
[_, _] => return Err(TableOpError::BadArg(name.to_owned())),
_ => return Err(TableOpError::BadArity(name.to_owned())),
},
"has" => TableOp::Has(one_key(name, args)?),
"del" => TableOp::Delete(one_key(name, args)?),
"keys" => {
no_args(name, args)?;
TableOp::Keys
}
"entries" => {
no_args(name, args)?;
TableOp::Entries
}
"len" => {
no_args(name, args)?;
TableOp::Len
}
"clear" => {
no_args(name, args)?;
TableOp::Clear
}
"mkdir" => TableOp::Mkdir(one_key(name, args)?),
"opendir" => TableOp::Opendir(one_key(name, args)?),
"rmdir" => TableOp::Rmdir(one_key(name, args)?),
"dir?" => TableOp::IsDir(one_key(name, args)?),
other => return Err(TableOpError::UnknownOp(other.to_owned())),
};
Ok(op)
}