Skip to main content

mumumath/
lib.rs

1// src/lib.rs
2
3use mumu::{
4    parser::interpreter::{DynamicFnInfo, Interpreter},
5    parser::types::{FunctionValue, Value},
6};
7use std::sync::{Arc, Mutex};
8
9#[cfg(not(target_arch = "wasm32"))]
10use std::ffi::{c_void, CStr};
11
12mod arb;
13mod sqrt;
14mod unary;
15mod partials;
16mod rotate_point;
17mod srand;
18
19use arb::math_arb_bridge;
20use sqrt::math_sqrt_bridge;
21use unary::*;
22use partials::*;
23use rotate_point::math_rotate_point_bridge;
24use srand::math_srand_bridge;
25
26/// Maximum depth for value_to_string to prevent stack overflow.
27const VALUE_TO_STRING_RECURSION_LIMIT: usize = 32;
28
29/// Recursion-protected conversion of `Value` to string.
30/// Falls back to "<recursion>" if nesting is too deep.
31pub fn value_to_string(val: &Value) -> String {
32    value_to_string_with_depth(val, 0)
33}
34
35fn value_to_string_with_depth(val: &Value, depth: usize) -> String {
36    if depth > VALUE_TO_STRING_RECURSION_LIMIT {
37        return "<recursion>".to_string();
38    }
39    match val {
40        Value::Int(x) => x.to_string(),
41        Value::IntArray(xs) => format!("{:?}", xs),
42        Value::Int2DArray(rows) => format!("{:?}", rows),
43        Value::Float(f) => f.to_string(),
44        Value::FloatArray(ff) => format!("{:?}", ff),
45        Value::Float2DArray(rows) => format!("{:?}", rows),
46        Value::StrArray(ss) => format!("{:?}", ss),
47        Value::KeyedArray(map) => {
48            let mut s = String::from("{ ");
49            let mut first = true;
50            for (k, v) in map.iter() {
51                if !first {
52                    s.push_str(", ");
53                }
54                first = false;
55                s.push_str(k);
56                s.push_str(": ");
57                s.push_str(&value_to_string_with_depth(v, depth + 1));
58            }
59            s.push_str(" }");
60            s
61        }
62        // NEW: pretty-print KeyedError to keep output friendly
63        Value::KeyedError(map) => {
64            let msg = map.get("message").and_then(|v| {
65                if let Value::SingleString(s) = v { Some(s.as_str()) } else { None }
66            }).unwrap_or("");
67            let line = map.get("line").and_then(|v| {
68                if let Value::Int(i) = v { Some(*i) } else { None }
69            }).unwrap_or(0);
70            let col = map.get("col").and_then(|v| {
71                if let Value::Int(i) = v { Some(*i) } else { None }
72            }).unwrap_or(0);
73
74            if !msg.is_empty() {
75                if line > 0 || col > 0 {
76                    format!("Error{{ line:{}, col:{}, message:\"{}\" }}", line, col, msg)
77                } else {
78                    format!("Error{{ message:\"{}\" }}", msg)
79                }
80            } else {
81                format!("Error{{ …{} keys… }}", map.len())
82            }
83        }
84        Value::Function(_) => "[Function]".to_string(),
85        Value::Bool(b) => b.to_string(),
86        Value::BoolArray(bb) => format!("{:?}", bb),
87        Value::Placeholder => "_".to_string(),
88        Value::SingleString(s) => format!("\"{}\"", s), // always print strings in quotes
89        Value::Long(l) => l.to_string(),
90        Value::Stream(sh) => format!("<Stream id={}, label={}>", sh.stream_id, sh.label),
91        Value::Iterator(_) => "[Iterator]".to_string(),
92        Value::Tensor(_) => "[Tensor]".to_string(),
93        Value::MixedArray(items) => {
94            let inner = items
95                .iter()
96                .map(|v| value_to_string_with_depth(v, depth + 1))
97                .collect::<Vec<_>>()
98                .join(", ");
99            format!("[{}]", inner)
100        }
101        Value::Ref(arc_mutex) => {
102            // Avoid infinite loop on self-referential structures
103            value_to_string_with_depth(&arc_mutex.lock().unwrap(), depth + 1)
104        }
105        Value::Undefined => "undefined".to_string(),
106        Value::Regex(rx) => format!("Regex(/{}{}/)", rx.pattern, rx.flags),
107    }
108}
109
110/// Register all `math:*` bridges into the provided interpreter.
111/// This is used by embedded/static (WASM) builds and can also be used by hosts.
112pub fn register_all(interp: &mut Interpreter) {
113    macro_rules! reg {
114        ($name:expr, $f:expr) => {{
115            let func = Arc::new(Mutex::new($f));
116            interp.register_dynamic_function_ex($name, DynamicFnInfo::new(func.clone(), false));
117            interp.set_variable($name, Value::Function(Box::new(FunctionValue::Named($name.into()))));
118        }};
119    }
120
121    // ---- Math Arithmetic (partials) ----
122    reg!("math:plus",     math_plus_bridge);
123    reg!("math:subtract", math_subtract_bridge);
124    reg!("math:multiply", math_multiply_bridge);
125    reg!("math:divide",   math_divide_bridge);
126    reg!("math:pow",      math_pow_bridge);
127    reg!("math:mod",      math_mod_bridge);
128
129    // ---- Multi-argument math ----
130    reg!("math:atan2",    math_atan2_bridge);
131    reg!("math:imul",     math_imul_bridge);
132    reg!("math:max",      math_max_bridge);
133    reg!("math:min",      math_min_bridge);
134    reg!("math:hypot",    math_hypot_bridge);
135
136    // ---- Single-argument math (unary.rs) ----
137    reg!("math:abs",      math_abs_bridge);
138    reg!("math:acos",     math_acos_bridge);
139    reg!("math:acosh",    math_acosh_bridge);
140    reg!("math:asin",     math_asin_bridge);
141    reg!("math:asinh",    math_asinh_bridge);
142    reg!("math:atan",     math_atan_bridge);
143    reg!("math:atanh",    math_atanh_bridge);
144    reg!("math:cbrt",     math_cbrt_bridge);
145    reg!("math:ceil",     math_ceil_bridge);
146    reg!("math:clz32",    math_clz32_bridge);
147    reg!("math:cos",      math_cos_bridge);
148    reg!("math:cosh",     math_cosh_bridge);
149    reg!("math:exp",      math_exp_bridge);
150    reg!("math:expm1",    math_expm1_bridge);
151    reg!("math:floor",    math_floor_bridge);
152    reg!("math:fround",   math_fround_bridge);
153    reg!("math:log",      math_log_bridge);
154    reg!("math:log10",    math_log10_bridge);
155    reg!("math:log1p",    math_log1p_bridge);
156    reg!("math:log2",     math_log2_bridge);
157    reg!("math:round",    math_round_bridge);
158    reg!("math:sign",     math_sign_bridge);
159    reg!("math:sin",      math_sin_bridge);
160    reg!("math:sinh",     math_sinh_bridge);
161    reg!("math:tan",      math_tan_bridge);
162    reg!("math:tanh",     math_tanh_bridge);
163    reg!("math:trunc",    math_trunc_bridge);
164
165    // ---- Sqrt & arbitrary precision (arb) ----
166    reg!("math:sqrt",     math_sqrt_bridge);
167    reg!("math:arb",      math_arb_bridge);
168
169    // ---- 3-arg math (rotate_point) ----
170    reg!("math:rotate_point", math_rotate_point_bridge);
171
172    // ---- Random seedable generator (srand) ----
173    reg!("math:srand",    math_srand_bridge);
174}
175
176/// Host/dynamic loader entrypoint (used when the plugin is loaded via extend()).
177/// This symbol must NOT be present in WASM builds to avoid duplicate symbols.
178#[cfg(not(target_arch = "wasm32"))]
179#[no_mangle]
180pub unsafe extern "C" fn Cargo_lock(
181    interp_ptr: *mut c_void,
182    extra_str: *const c_void,
183) -> i32 {
184    let interp = &mut *(interp_ptr as *mut Interpreter);
185
186    let verbose = interp.is_verbose();
187    if verbose && !extra_str.is_null() {
188        let c_str = CStr::from_ptr(extra_str as *const i8);
189        eprintln!(
190            "(math) cargo_lock => bridging => extra arg='{}'",
191            c_str.to_string_lossy()
192        );
193    }
194    if verbose {
195        eprintln!("(math) => registering math plugin bridges …");
196    }
197
198    // Register everything via the shared function.
199    register_all(interp);
200
201    if verbose {
202        eprintln!("(math) cargo_lock => done registering all bridging functions.");
203    }
204
205    0
206}