arithmetic_eval/fns/std.rs
1//! Functions that require the Rust standard library.
2
3use std::fmt;
4
5use crate::{CallContext, EvalResult, ModuleId, NativeFn, SpannedValue, Value};
6use arithmetic_parser::CodeFragment;
7
8/// Acts similarly to the `dbg!` macro, outputting the argument(s) to stderr and returning
9/// them. If a single argument is provided, it's returned as-is; otherwise, the arguments
10/// are wrapped into a tuple.
11///
12/// # Examples
13///
14/// ```
15/// # use arithmetic_parser::grammars::{F32Grammar, Parse, Untyped};
16/// # use arithmetic_eval::{fns, Environment, Value, VariableMap};
17/// # fn main() -> anyhow::Result<()> {
18/// let program = "dbg(1 + 2) > 2.5";
19/// let program = Untyped::<F32Grammar>::parse_statements(program)?;
20/// let module = Environment::new()
21/// .insert_native_fn("dbg", fns::Dbg)
22/// .compile_module("test_assert", &program)?;
23///
24/// let value = module.run()?;
25/// // Should output `[test_assert:1:5] 1 + 2 = 3` to stderr.
26/// assert_eq!(value, Value::Bool(true));
27/// # Ok(())
28/// # }
29/// ```
30#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
31#[derive(Debug, Clone, Copy, Default)]
32pub struct Dbg;
33
34impl Dbg {
35 fn print_value<T: fmt::Display>(module_id: &dyn ModuleId, value: &SpannedValue<'_, T>) {
36 match value.fragment() {
37 CodeFragment::Str(code) => eprintln!(
38 "[{module}:{line}:{col}] {code} = {val}",
39 module = module_id,
40 line = value.location_line(),
41 col = value.get_column(),
42 code = code,
43 val = value.extra
44 ),
45 CodeFragment::Stripped(_) => eprintln!(
46 "[{module}:{line}:{col}] {val}",
47 module = module_id,
48 line = value.location_line(),
49 col = value.get_column(),
50 val = value.extra
51 ),
52 }
53 }
54}
55
56impl<T: fmt::Display> NativeFn<T> for Dbg {
57 fn evaluate<'a>(
58 &self,
59 mut args: Vec<SpannedValue<'a, T>>,
60 ctx: &mut CallContext<'_, 'a, T>,
61 ) -> EvalResult<'a, T> {
62 let module_id = ctx.call_span().module_id();
63 for arg in &args {
64 Self::print_value(module_id, arg);
65 }
66
67 Ok(if args.len() == 1 {
68 args.pop().unwrap().extra
69 } else {
70 Value::Tuple(args.into_iter().map(|spanned| spanned.extra).collect())
71 })
72 }
73}