1use sim_kernel::{Error, Expr, Result, Symbol};
10
11use crate::build::sym;
12
13fn key_is(key: &Expr, name: &str) -> bool {
14 matches!(key, Expr::Symbol(symbol) if &*symbol.name == name && symbol.namespace.is_none())
15}
16
17fn key_is_any(key: &Expr, name: &str) -> bool {
19 key_is(key, name) || matches!(key, Expr::String(text) if text == name)
20}
21
22fn key_name(key: &Expr) -> Option<&str> {
26 match key {
27 Expr::Symbol(symbol) if symbol.namespace.is_none() => Some(&symbol.name),
28 Expr::String(text) => Some(text),
29 _ => None,
30 }
31}
32
33pub fn entry_field<'a>(entries: &'a [(Expr, Expr)], name: &str) -> Option<&'a Expr> {
37 entries
38 .iter()
39 .find_map(|(key, value)| key_is(key, name).then_some(value))
40}
41
42pub fn entry_field_any<'a>(entries: &'a [(Expr, Expr)], name: &str) -> Option<&'a Expr> {
45 entries
46 .iter()
47 .find_map(|(key, value)| key_is_any(key, name).then_some(value))
48}
49
50pub fn field<'a>(map: &'a Expr, name: &str) -> Option<&'a Expr> {
52 match map {
53 Expr::Map(entries) => entry_field(entries, name),
54 _ => None,
55 }
56}
57
58pub fn field_q<'a>(map: &'a Expr, ns: &str, name: &str) -> Option<&'a Expr> {
60 let Expr::Map(entries) = map else {
61 return None;
62 };
63 entries.iter().find_map(|(key, value)| {
64 matches!(key, Expr::Symbol(symbol) if symbol.namespace.as_deref() == Some(ns) && &*symbol.name == name)
65 .then_some(value)
66 })
67}
68
69pub fn field_any<'a>(map: &'a Expr, name: &str) -> Option<&'a Expr> {
74 match map {
75 Expr::Map(entries) => entry_field_any(entries, name),
76 _ => None,
77 }
78}
79
80pub fn required<'a>(map: &'a Expr, name: &str, context: &str) -> Result<&'a Expr> {
83 field_any(map, name).ok_or_else(|| Error::Eval(format!("{context} is missing field {name}")))
84}
85
86pub fn entry_required<'a>(
90 entries: &'a [(Expr, Expr)],
91 name: &str,
92 context: &str,
93) -> Result<&'a Expr> {
94 entry_field_any(entries, name)
95 .ok_or_else(|| Error::Eval(format!("{context} is missing field {name}")))
96}
97
98fn type_mismatch(expected: &'static str, found: &Expr) -> Error {
103 Error::TypeMismatch {
104 expected,
105 found: crate::kind::expr_kind(found),
106 }
107}
108
109fn entry_required_bare<'a>(
116 entries: &'a [(Expr, Expr)],
117 name: &str,
118 context: &str,
119) -> Result<&'a Expr> {
120 entry_field(entries, name)
121 .ok_or_else(|| Error::Eval(format!("{context} is missing field {name}")))
122}
123
124pub fn entry_required_str<'a>(
130 entries: &'a [(Expr, Expr)],
131 name: &str,
132 expected: &'static str,
133) -> Result<&'a str> {
134 match entry_required_bare(entries, name, expected)? {
135 Expr::String(value) => Ok(value),
136 other => Err(type_mismatch(expected, other)),
137 }
138}
139
140pub fn entry_required_sym<'a>(
143 entries: &'a [(Expr, Expr)],
144 name: &str,
145 expected: &'static str,
146) -> Result<&'a Symbol> {
147 match entry_required_bare(entries, name, expected)? {
148 Expr::Symbol(value) => Ok(value),
149 other => Err(type_mismatch(expected, other)),
150 }
151}
152
153pub fn entry_required_bool(
156 entries: &[(Expr, Expr)],
157 name: &str,
158 expected: &'static str,
159) -> Result<bool> {
160 match entry_required_bare(entries, name, expected)? {
161 Expr::Bool(value) => Ok(*value),
162 other => Err(type_mismatch(expected, other)),
163 }
164}
165
166pub fn entry_required_list<'a>(
169 entries: &'a [(Expr, Expr)],
170 name: &str,
171 expected: &'static str,
172) -> Result<&'a [Expr]> {
173 match entry_required_bare(entries, name, expected)? {
174 Expr::List(items) => Ok(items),
175 other => Err(type_mismatch(expected, other)),
176 }
177}
178
179pub fn entry_required_str_any<'a>(
183 entries: &'a [(Expr, Expr)],
184 name: &str,
185 expected: &'static str,
186) -> Result<&'a str> {
187 match entry_required(entries, name, expected)? {
188 Expr::String(value) => Ok(value),
189 other => Err(type_mismatch(expected, other)),
190 }
191}
192
193pub fn entry_required_sym_any<'a>(
196 entries: &'a [(Expr, Expr)],
197 name: &str,
198 expected: &'static str,
199) -> Result<&'a Symbol> {
200 match entry_required(entries, name, expected)? {
201 Expr::Symbol(value) => Ok(value),
202 other => Err(type_mismatch(expected, other)),
203 }
204}
205
206pub fn entry_required_bool_any(
209 entries: &[(Expr, Expr)],
210 name: &str,
211 expected: &'static str,
212) -> Result<bool> {
213 match entry_required(entries, name, expected)? {
214 Expr::Bool(value) => Ok(*value),
215 other => Err(type_mismatch(expected, other)),
216 }
217}
218
219pub fn entry_required_list_any<'a>(
222 entries: &'a [(Expr, Expr)],
223 name: &str,
224 expected: &'static str,
225) -> Result<&'a [Expr]> {
226 match entry_required(entries, name, expected)? {
227 Expr::List(items) => Ok(items),
228 other => Err(type_mismatch(expected, other)),
229 }
230}
231
232pub fn required_str<'a>(map: &'a Expr, name: &str, context: &str) -> Result<&'a str> {
237 as_str(required(map, name, context)?)
238 .ok_or_else(|| Error::Eval(format!("{context} field {name} is not a string")))
239}
240
241pub fn required_sym(map: &Expr, name: &str, context: &str) -> Result<Symbol> {
243 match required(map, name, context)? {
244 Expr::Symbol(symbol) => Ok(symbol.clone()),
245 _ => Err(Error::Eval(format!(
246 "{context} field {name} is not a symbol"
247 ))),
248 }
249}
250
251pub fn required_bool(map: &Expr, name: &str, context: &str) -> Result<bool> {
253 match required(map, name, context)? {
254 Expr::Bool(value) => Ok(*value),
255 _ => Err(Error::Eval(format!("{context} field {name} is not a bool"))),
256 }
257}
258
259pub fn required_map<'a>(map: &'a Expr, name: &str, context: &str) -> Result<&'a [(Expr, Expr)]> {
262 match required(map, name, context)? {
263 Expr::Map(entries) => Ok(entries),
264 _ => Err(Error::Eval(format!("{context} field {name} is not a map"))),
265 }
266}
267
268pub fn map_entries<'a>(map: &'a Expr, expected: &'static str) -> Result<&'a [(Expr, Expr)]> {
272 match map {
273 Expr::Map(entries) => Ok(entries),
274 _ => Err(Error::TypeMismatch {
275 expected,
276 found: "non-map",
277 }),
278 }
279}
280
281pub fn extra_fields<'a>(map: &'a Expr, known: &[&str]) -> Vec<&'a str> {
285 let Expr::Map(entries) = map else {
286 return Vec::new();
287 };
288 entries
289 .iter()
290 .filter_map(|(key, _)| key_name(key))
291 .filter(|name| !known.contains(name))
292 .collect()
293}
294
295pub fn field_sym(map: &Expr, name: &str) -> Option<Symbol> {
297 match field(map, name) {
298 Some(Expr::Symbol(symbol)) => Some(symbol.clone()),
299 _ => None,
300 }
301}
302
303pub fn field_str<'a>(map: &'a Expr, name: &str) -> Option<&'a str> {
305 field(map, name).and_then(as_str)
306}
307
308pub fn field_i64(map: &Expr, name: &str) -> Option<i64> {
310 field(map, name).and_then(as_i64)
311}
312
313pub fn field_f64(map: &Expr, name: &str) -> Option<f64> {
315 field(map, name).and_then(as_f64)
316}
317
318pub fn field_bool(map: &Expr, name: &str) -> Option<bool> {
321 match field_any(map, name) {
322 Some(Expr::Bool(value)) => Some(*value),
323 _ => None,
324 }
325}
326
327pub fn as_i64(value: &Expr) -> Option<i64> {
329 match value {
330 Expr::Number(number) => number.canonical.parse::<i64>().ok(),
331 _ => None,
332 }
333}
334
335pub fn as_f64(value: &Expr) -> Option<f64> {
337 match value {
338 Expr::Number(number) => number.canonical.parse::<f64>().ok(),
339 _ => None,
340 }
341}
342
343pub fn as_str(value: &Expr) -> Option<&str> {
345 match value {
346 Expr::String(text) => Some(text),
347 _ => None,
348 }
349}
350
351pub fn set(map: &Expr, name: &str, value: Expr) -> Expr {
354 let mut entries = match map {
355 Expr::Map(entries) => entries.clone(),
356 _ => Vec::new(),
357 };
358 if let Some(slot) = entries.iter_mut().find(|(key, _)| key_is(key, name)) {
359 slot.1 = value;
360 } else {
361 entries.push((sym(name), value));
362 }
363 Expr::Map(entries)
364}
365
366pub fn remove(map: &Expr, name: &str) -> Expr {
368 let entries = match map {
369 Expr::Map(entries) => entries.clone(),
370 _ => Vec::new(),
371 };
372 Expr::Map(
373 entries
374 .into_iter()
375 .filter(|(key, _)| !key_is(key, name))
376 .collect(),
377 )
378}