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