mumustd/
lib.rs

1// src/lib.rs
2//
3// MuMu/Lava "std" plugin – basic stdin/stdout utilities + deep printer.
4//
5// Exposes five functions:
6//
7//   - std:input()                -> string
8//   - std:input_iter()           -> InkTransform (pulls one line per call; EOF => "NO_MORE_DATA")
9//   - std:put(x)                 -> print x with NO newline; if x is an iterator/transform, drain it
10//   - std:log(x)                 -> print x WITH newline; if x is an iterator/transform, drain it
11//   - std:deep(x)                -> deep, recursive pretty representation WITH newline; returns x
12//
13// Build/install (as in your Makefile):
14//   make && sudo make install && sudo ldconfig
15//
16
17use std::io::{self, Write};
18use std::sync::{Arc, Mutex};
19
20use mumu::{
21    parser::interpreter::Interpreter,
22    parser::types::{FunctionValue, InkIteratorKind, Value},
23};
24
25/// ---------- small helpers ----------
26
27fn to_printable(v: &Value) -> String {
28    match v {
29        Value::SingleString(s) => s.clone(),
30        Value::Int(i) => i.to_string(),
31        Value::Long(l) => l.to_string(),
32        Value::Float(f) => f.to_string(),
33        Value::Bool(b) => b.to_string(),
34        // fall back to the engine’s Debug format for structured values
35        _ => format!("{:?}", v),
36    }
37}
38
39fn escape_str(s: &str) -> String {
40    let mut out = String::with_capacity(s.len() + 8);
41    for ch in s.chars() {
42        match ch {
43            '\\' => out.push_str("\\\\"),
44            '"'  => out.push_str("\\\""),
45            '\n' => out.push_str("\\n"),
46            '\r' => out.push_str("\\r"),
47            '\t' => out.push_str("\\t"),
48            other => out.push(other),
49        }
50    }
51    out
52}
53
54/// Deep, recursive, single-line representation using MuMu-ish syntax:
55///  - Strings quoted
56///  - Keyed arrays as [k: v, …] (not {})
57///  - 2-D arrays as nested bracketed rows
58///  - Ref is dereferenced so you see the underlying value
59fn deep_to_string(v: &Value) -> String {
60    deep_to_string_inner(v, 0)
61}
62
63fn deep_to_string_inner(v: &Value, depth: usize) -> String {
64    if depth > 64 {
65        return "<depth-limit>".to_string();
66    }
67    match v {
68        Value::SingleString(s) => format!("\"{}\"", escape_str(s)),
69        Value::Int(i) => i.to_string(),
70        Value::Long(l) => l.to_string(),
71        Value::Float(f) => {
72            // Show integers like "1.0" as "1.0" (not "1")
73            if f.fract() == 0.0 { format!("{:.1}", f) } else { f.to_string() }
74        }
75        Value::Bool(b) => b.to_string(),
76        Value::Undefined => "undefined".to_string(),
77        Value::Placeholder => "_".to_string(),
78
79        Value::IntArray(xs) => {
80            let inner = xs.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(", ");
81            format!("[{}]", inner)
82        }
83        Value::FloatArray(xs) => {
84            let inner = xs.iter()
85                .map(|f| if f.fract()==0.0 { format!("{:.1}", f) } else { f.to_string() })
86                .collect::<Vec<_>>().join(", ");
87            format!("[{}]", inner)
88        }
89        Value::BoolArray(xs) => {
90            let inner = xs.iter().map(|b| b.to_string()).collect::<Vec<_>>().join(", ");
91            format!("[{}]", inner)
92        }
93        Value::StrArray(xs) => {
94            let inner = xs.iter().map(|s| format!("\"{}\"", escape_str(s))).collect::<Vec<_>>().join(", ");
95            format!("[{}]", inner)
96        }
97        Value::Int2DArray(rows) => {
98            let inner = rows.iter()
99                .map(|r| format!("[{}]", r.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(", ")))
100                .collect::<Vec<_>>().join(", ");
101            format!("[{}]", inner)
102        }
103        Value::Float2DArray(rows) => {
104            let fmt = |f: &f64| if f.fract()==0.0 { format!("{:.1}", f) } else { f.to_string() };
105            let inner = rows.iter()
106                .map(|r| format!("[{}]", r.iter().map(fmt).collect::<Vec<_>>().join(", ")))
107                .collect::<Vec<_>>().join(", ");
108            format!("[{}]", inner)
109        }
110        Value::MixedArray(items) => {
111            let inner = items.iter()
112                .map(|x| deep_to_string_inner(x, depth+1))
113                .collect::<Vec<_>>().join(", ");
114            format!("[{}]", inner)
115        }
116        Value::KeyedArray(map) => {
117            let parts = map.iter()
118                .map(|(k, v)| format!("{}: {}", k, deep_to_string_inner(v, depth+1)))
119                .collect::<Vec<_>>().join(", ");
120            format!("[{}]", parts)
121        }
122
123        Value::Function(_)     => "[Function]".to_string(),
124        Value::Stream(sh)      => format!("<Stream id={}, label={}>", sh.stream_id, sh.label),
125        Value::InkIterator(_)  => "[InkIterator]".to_string(),
126        Value::InkTransform(_) => "[InkTransform]".to_string(),
127        Value::Tensor(_)       => "[Tensor]".to_string(),
128        Value::Ref(cell)       => {
129            let guard = cell.lock().unwrap();
130            deep_to_string_inner(&*guard, depth+1)
131        }
132        Value::Regex(rx)       => format!("Regex(/{}{}/)", rx.pattern, rx.flags),
133    }
134}
135
136fn print_no_nl(interp: &mut Interpreter, s: &str) {
137    print!("{}", s);
138    let _ = io::stdout().flush();
139    interp.push_print(s);
140}
141
142fn print_with_nl(interp: &mut Interpreter, s: &str) {
143    println!("{}", s);
144    interp.push_print(&(s.to_string() + "\n"));
145}
146
147/// Drain an InkTransform (RustClosure based), calling it until it returns
148/// Err("NO_MORE_DATA"). Each produced item is printed via the provided printer.
149fn drain_ink_transform(
150    interp: &mut Interpreter,
151    f: &FunctionValue,
152    printer: &dyn Fn(&mut Interpreter, &str),
153) -> Result<(), String> {
154    match f {
155        FunctionValue::RustClosure(_desc, closure_arc, _arity) => {
156            loop {
157                let cb = closure_arc
158                    .lock()
159                    .map_err(|_| "std:put => closure poisoned".to_string())?;
160                let out = cb(interp, vec![]);
161                drop(cb);
162
163                match out {
164                    Ok(item) => {
165                        let s = to_printable(&item);
166                        printer(interp, &s);
167                    }
168                    Err(e) if e == "NO_MORE_DATA" => break,
169                    Err(e) => return Err(e),
170                }
171            }
172            Ok(())
173        }
174        _ => Ok(()),
175    }
176}
177
178/// ---------- bridges ----------
179
180fn std_input_bridge(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
181    if args.len() > 1 {
182        return Err(format!(
183            "std:input => expected 0 or 1 arg (prompt), got {}",
184            args.len()
185        ));
186    }
187
188    if let Some(Value::SingleString(prompt)) = args.get(0) {
189        print!("{}", prompt);
190        let _ = io::stdout().flush();
191    }
192
193    let mut buf = String::new();
194    match io::stdin().read_line(&mut buf) {
195        Ok(0) => Ok(Value::SingleString(String::new())), // EOF => empty string
196        Ok(_) => {
197            if buf.ends_with('\n') {
198                buf.pop();
199                if buf.ends_with('\r') {
200                    buf.pop();
201                }
202            }
203            Ok(Value::SingleString(buf))
204        }
205        Err(e) => Err(format!("std:input => {}", e)),
206    }
207}
208
209fn std_input_iter_bridge(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
210    if !args.is_empty() {
211        return Err(format!(
212            "std:input_iter => expected 0 args, got {}",
213            args.len()
214        ));
215    }
216
217    let closure = Arc::new(Mutex::new(
218        move |_i: &mut Interpreter, _argv: Vec<Value>| -> Result<Value, String> {
219            let mut buf = String::new();
220            let n = io::stdin()
221                .read_line(&mut buf)
222                .map_err(|e| format!("std:input_iter => {}", e))?;
223            if n == 0 {
224                return Err("NO_MORE_DATA".into());
225            }
226            if buf.ends_with('\n') {
227                buf.pop();
228                if buf.ends_with('\r') {
229                    buf.pop();
230                }
231            }
232            Ok(Value::SingleString(buf))
233        },
234    ));
235
236    Ok(Value::InkTransform(Box::new(FunctionValue::RustClosure(
237        "std:input_iter".to_string(),
238        closure,
239        0,
240    ))))
241}
242
243fn std_put_bridge(interp: &mut Interpreter, mut args: Vec<Value>) -> Result<Value, String> {
244    if args.len() != 1 {
245        return Err(format!(
246            "std:put => expected exactly 1 argument, got {}",
247            args.len()
248        ));
249    }
250    let val = args.remove(0);
251
252    let res: Result<(), String> = match &val {
253        Value::InkTransform(fb) => drain_ink_transform(interp, fb, &print_no_nl),
254        Value::InkIterator(handle) => match &handle.kind {
255            InkIteratorKind::Core(state_arc) => {
256                let mut guard = state_arc
257                    .lock()
258                    .map_err(|_| "std:put => iterator lock error".to_string())?;
259                while !guard.done && guard.current < guard.end {
260                    let item = guard.current;
261                    guard.current += 1;
262                    if guard.current >= guard.end {
263                        guard.done = true;
264                    }
265                    drop(guard);
266                    print_no_nl(interp, &item.to_string());
267                    guard = state_arc
268                        .lock()
269                        .map_err(|_| "std:put => iterator lock error".to_string())?;
270                }
271                Ok(())
272            }
273            InkIteratorKind::Plugin(plugin_arc) => {
274                let mut plugin = plugin_arc
275                    .lock()
276                    .map_err(|_| "std:put => plugin iterator lock error".to_string())?;
277                loop {
278                    match plugin.next_value() {
279                        Ok(item) => {
280                            let s = to_printable(&item);
281                            print_no_nl(interp, &s);
282                        }
283                        Err(e) if e == "NO_MORE_DATA" => break,
284                        Err(e) => return Err(e),
285                    }
286                }
287                Ok(())
288            }
289        },
290        other => {
291            let s = to_printable(other);
292            print_no_nl(interp, &s);
293            Ok(())
294        }
295    };
296
297    res?;
298    Ok(val)
299}
300
301fn std_log_bridge(interp: &mut Interpreter, mut args: Vec<Value>) -> Result<Value, String> {
302    if args.len() != 1 {
303        return Err(format!(
304            "std:log => expected exactly 1 argument, got {}",
305            args.len()
306        ));
307    }
308    let val = args.remove(0);
309
310    let res: Result<(), String> = match &val {
311        Value::InkTransform(fb) => drain_ink_transform(interp, fb, &print_with_nl),
312        Value::InkIterator(handle) => match &handle.kind {
313            InkIteratorKind::Core(state_arc) => {
314                let mut guard = state_arc
315                    .lock()
316                    .map_err(|_| "std:log => iterator lock error".to_string())?;
317                while !guard.done && guard.current < guard.end {
318                    let item = guard.current;
319                    guard.current += 1;
320                    if guard.current >= guard.end {
321                        guard.done = true;
322                    }
323                    drop(guard);
324                    print_with_nl(interp, &item.to_string());
325                    guard = state_arc
326                        .lock()
327                        .map_err(|_| "std:log => iterator lock error".to_string())?;
328                }
329                Ok(())
330            }
331            InkIteratorKind::Plugin(plugin_arc) => {
332                let mut plugin = plugin_arc
333                    .lock()
334                    .map_err(|_| "std:log => plugin iterator lock error".to_string())?;
335                loop {
336                    match plugin.next_value() {
337                        Ok(item) => {
338                            let s = to_printable(&item);
339                            print_with_nl(interp, &s);
340                        }
341                        Err(e) if e == "NO_MORE_DATA" => break,
342                        Err(e) => return Err(e),
343                    }
344                }
345                Ok(())
346            }
347        },
348        other => {
349            let s = to_printable(other);
350            print_with_nl(interp, &s);
351            Ok(())
352        }
353    };
354
355    res?;
356    Ok(val)
357}
358
359/// Deep printer: prints a *recursive* representation and returns the original value.
360fn std_deep_bridge(interp: &mut Interpreter, mut args: Vec<Value>) -> Result<Value, String> {
361    if args.len() != 1 {
362        return Err(format!(
363            "std:deep => expected exactly 1 argument, got {}",
364            args.len()
365        ));
366    }
367    let val = args.remove(0);
368    let s = deep_to_string(&val);
369    println!("{}", s);
370    interp.push_print(&(s + "\n"));
371    Ok(val)
372}
373
374/// ---------- plugin entry ----------
375
376#[export_name = "Cargo_lock"]
377pub unsafe extern "C" fn cargo_lock(
378    interp_ptr: *mut std::ffi::c_void,
379    _extra: *const std::ffi::c_void,
380) -> i32 {
381    if interp_ptr.is_null() {
382        return 1;
383    }
384    let interp = &mut *(interp_ptr as *mut Interpreter);
385
386    // Register dynamic functions
387    let f_input = Arc::new(Mutex::new(std_input_bridge));
388    let f_iter  = Arc::new(Mutex::new(std_input_iter_bridge));
389    let f_put   = Arc::new(Mutex::new(std_put_bridge));
390    let f_log   = Arc::new(Mutex::new(std_log_bridge));
391    let f_deep  = Arc::new(Mutex::new(std_deep_bridge));
392
393    interp.register_dynamic_function("std:input", f_input);
394    interp.register_dynamic_function("std:input_iter", f_iter);
395    interp.register_dynamic_function("std:put", f_put);
396    interp.register_dynamic_function("std:log", f_log);
397    interp.register_dynamic_function("std:deep", f_deep);
398
399    // Create top-level variables (callables inside MuMu)
400    interp.set_variable(
401        "std:input",
402        Value::Function(Box::new(FunctionValue::Named("std:input".into()))),
403    );
404    interp.set_variable(
405        "std:input_iter",
406        Value::Function(Box::new(FunctionValue::Named("std:input_iter".into()))),
407    );
408    interp.set_variable(
409        "std:put",
410        Value::Function(Box::new(FunctionValue::Named("std:put".into()))),
411    );
412    interp.set_variable(
413        "std:log",
414        Value::Function(Box::new(FunctionValue::Named("std:log".into()))),
415    );
416    interp.set_variable(
417        "std:deep",
418        Value::Function(Box::new(FunctionValue::Named("std:deep".into()))),
419    );
420
421    0
422}