1use sim_kernel::{Expr, Symbol};
12use sim_value::build::qsym;
13
14#[derive(Clone, Debug, PartialEq)]
16pub enum TableOp {
17 Get(Symbol),
19 Set(Symbol, Expr),
21 Has(Symbol),
23 Delete(Symbol),
25 Keys,
27 Entries,
29 Len,
31 Clear,
33 Mkdir(Symbol),
35 Opendir(Symbol),
37 Rmdir(Symbol),
39 IsDir(Symbol),
41}
42
43#[derive(Clone, Debug, PartialEq)]
45pub enum TableOpError {
46 NotATableCall,
48 UnknownOp(String),
50 BadArity(String),
52 BadArg(String),
54}
55
56fn wire_name(op: &TableOp) -> &'static str {
59 match op {
60 TableOp::Get(_) => "get",
61 TableOp::Set(_, _) => "set",
62 TableOp::Has(_) => "has",
63 TableOp::Delete(_) => "del",
64 TableOp::Keys => "keys",
65 TableOp::Entries => "entries",
66 TableOp::Len => "len",
67 TableOp::Clear => "clear",
68 TableOp::Mkdir(_) => "mkdir",
69 TableOp::Opendir(_) => "opendir",
70 TableOp::Rmdir(_) => "rmdir",
71 TableOp::IsDir(_) => "dir?",
72 }
73}
74
75pub fn encode_table_op(op: &TableOp) -> Expr {
77 let args = match op {
78 TableOp::Get(key)
79 | TableOp::Has(key)
80 | TableOp::Delete(key)
81 | TableOp::Mkdir(key)
82 | TableOp::Opendir(key)
83 | TableOp::Rmdir(key)
84 | TableOp::IsDir(key) => vec![Expr::Symbol(key.clone())],
85 TableOp::Set(key, value) => vec![Expr::Symbol(key.clone()), value.clone()],
86 TableOp::Keys | TableOp::Entries | TableOp::Len | TableOp::Clear => Vec::new(),
87 };
88 Expr::Call {
89 operator: Box::new(qsym("table", wire_name(op))),
90 args,
91 }
92}
93
94fn one_key(op: &str, args: &[Expr]) -> Result<Symbol, TableOpError> {
96 match args {
97 [Expr::Symbol(key)] => Ok(key.clone()),
98 [_] => Err(TableOpError::BadArg(op.to_owned())),
99 _ => Err(TableOpError::BadArity(op.to_owned())),
100 }
101}
102
103fn no_args(op: &str, args: &[Expr]) -> Result<(), TableOpError> {
105 if args.is_empty() {
106 Ok(())
107 } else {
108 Err(TableOpError::BadArity(op.to_owned()))
109 }
110}
111
112pub fn decode_table_op(expr: &Expr) -> Result<TableOp, TableOpError> {
114 let Expr::Call { operator, args } = expr else {
115 return Err(TableOpError::NotATableCall);
116 };
117 let Expr::Symbol(symbol) = operator.as_ref() else {
118 return Err(TableOpError::NotATableCall);
119 };
120 if symbol.namespace.as_deref() != Some("table") {
121 return Err(TableOpError::NotATableCall);
122 }
123 let name = symbol.name.as_ref();
124 let op = match name {
125 "get" => TableOp::Get(one_key(name, args)?),
126 "set" => match args.as_slice() {
127 [Expr::Symbol(key), value] => TableOp::Set(key.clone(), value.clone()),
128 [_, _] => return Err(TableOpError::BadArg(name.to_owned())),
129 _ => return Err(TableOpError::BadArity(name.to_owned())),
130 },
131 "has" => TableOp::Has(one_key(name, args)?),
132 "del" => TableOp::Delete(one_key(name, args)?),
133 "keys" => {
134 no_args(name, args)?;
135 TableOp::Keys
136 }
137 "entries" => {
138 no_args(name, args)?;
139 TableOp::Entries
140 }
141 "len" => {
142 no_args(name, args)?;
143 TableOp::Len
144 }
145 "clear" => {
146 no_args(name, args)?;
147 TableOp::Clear
148 }
149 "mkdir" => TableOp::Mkdir(one_key(name, args)?),
150 "opendir" => TableOp::Opendir(one_key(name, args)?),
151 "rmdir" => TableOp::Rmdir(one_key(name, args)?),
152 "dir?" => TableOp::IsDir(one_key(name, args)?),
153 other => return Err(TableOpError::UnknownOp(other.to_owned())),
154 };
155 Ok(op)
156}