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// For wasm builds, std:input / std:input_iter are stubbed (return an error), while
14// std:put / std:log / std:deep work and still populate the interpreter's print buffer.
15//
16
17use std::io::{self, Write};
18use std::sync::{Arc, Mutex};
19
20use core_mumu::{
21    parser::interpreter::Interpreter,
22    parser::types::{FunctionValue, IteratorHandle, IteratorKind, PluginIterator, 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            if f.fract() == 0.0 { format!("{:.1}", f) } else { f.to_string() }
73        }
74        Value::Bool(b) => b.to_string(),
75        Value::Undefined => "undefined".to_string(),
76        Value::Placeholder => "_".to_string(),
77
78        Value::IntArray(xs) => {
79            let inner = xs.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(", ");
80            format!("[{}]", inner)
81        }
82        Value::FloatArray(xs) => {
83            let inner = xs.iter()
84                .map(|f| if f.fract()==0.0 { format!("{:.1}", f) } else { f.to_string() })
85                .collect::<Vec<_>>().join(", ");
86            format!("[{}]", inner)
87        }
88        Value::BoolArray(xs) => {
89            let inner = xs.iter().map(|b| b.to_string()).collect::<Vec<_>>().join(", ");
90            format!("[{}]", inner)
91        }
92        Value::StrArray(xs) => {
93            let inner = xs.iter().map(|s| format!("\"{}\"", escape_str(s))).collect::<Vec<_>>().join(", ");
94            format!("[{}]", inner)
95        }
96        Value::Int2DArray(rows) => {
97            let inner = rows.iter()
98                .map(|r| format!("[{}]", r.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(", ")))
99                .collect::<Vec<_>>().join(", ");
100            format!("[{}]", inner)
101        }
102        Value::Float2DArray(rows) => {
103            let fmt = |f: &f64| if f.fract()==0.0 { format!("{:.1}", f) } else { f.to_string() };
104            let inner = rows.iter()
105                .map(|r| format!("[{}]", r.iter().map(fmt).collect::<Vec<_>>().join(", ")))
106                .collect::<Vec<_>>().join(", ");
107            format!("[{}]", inner)
108        }
109        Value::MixedArray(items) => {
110            let inner = items.iter()
111                .map(|x| deep_to_string_inner(x, depth+1))
112                .collect::<Vec<_>>().join(", ");
113            format!("[{}]", inner)
114        }
115        Value::KeyedArray(map) => {
116            let parts = map.iter()
117                .map(|(k, v)| format!("{}: {}", k, deep_to_string_inner(v, depth+1)))
118                .collect::<Vec<_>>().join(", ");
119            format!("[{}]", parts)
120        }
121
122        Value::Function(_)     => "[Function]".to_string(),
123        Value::Stream(sh)      => format!("<Stream id={}, label={}>", sh.stream_id, sh.label),
124        Value::Iterator(_)     => "[Iterator]".to_string(),
125        Value::Tensor(_)       => "[Tensor]".to_string(),
126        Value::Ref(cell)       => {
127            let guard = cell.lock().unwrap();
128            deep_to_string_inner(&*guard, depth+1)
129        }
130        Value::Regex(rx)       => format!("Regex(/{}{}/)", rx.pattern, rx.flags),
131    }
132}
133
134fn print_no_nl(interp: &mut Interpreter, s: &str) {
135    print!("{}", s);
136    let _ = io::stdout().flush();
137    interp.push_print(s);
138}
139
140fn print_with_nl(interp: &mut Interpreter, s: &str) {
141    println!("{}", s);
142    interp.push_print(&(s.to_string() + "\n"));
143}
144
145/// Drain an **Iterator**, printing each produced item using the provided printer.
146/// This is a **blocking** drain (unlike `slog`/`sput` which register pollers).
147fn drain_iterator_blocking(
148    interp: &mut Interpreter,
149    it: &IteratorHandle,
150    printer: &dyn Fn(&mut Interpreter, &str),
151) -> Result<(), String> {
152    match &it.kind {
153        IteratorKind::Core(state_arc) => {
154            let mut guard = state_arc
155                .lock()
156                .map_err(|_| "std: drain => iterator lock error".to_string())?;
157            while !guard.done && guard.current < guard.end {
158                let item = guard.current;
159                guard.current += 1;
160                if guard.current >= guard.end {
161                    guard.done = true;
162                }
163                drop(guard);
164                printer(interp, &item.to_string());
165                guard = state_arc
166                    .lock()
167                    .map_err(|_| "std: drain => iterator lock error".to_string())?;
168            }
169            Ok(())
170        }
171        IteratorKind::Plugin(plugin_arc) => {
172            let mut plugin =
173                plugin_arc.lock().map_err(|_| "std: drain => plugin iterator lock error".to_string())?;
174            loop {
175                match plugin.next_value() {
176                    Ok(item) => {
177                        let s = to_printable(&item);
178                        printer(interp, &s);
179                    }
180                    Err(e) if e == "NO_MORE_DATA" => break,
181                    Err(e) => return Err(e),
182                }
183            }
184            Ok(())
185        }
186    }
187}
188
189/// ---------- bridges ----------
190
191// Native/stdin implementations (host)
192#[cfg(not(target_arch = "wasm32"))]
193fn std_input_bridge(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
194    if args.len() > 1 {
195        return Err(format!(
196            "std:input => expected 0 or 1 arg (prompt), got {}",
197            args.len()
198        ));
199    }
200
201    if let Some(Value::SingleString(prompt)) = args.get(0) {
202        print!("{}", prompt);
203        let _ = io::stdout().flush();
204    }
205
206    let mut buf = String::new();
207    match io::stdin().read_line(&mut buf) {
208        Ok(0) => Ok(Value::SingleString(String::new())), // EOF => empty string
209        Ok(_) => {
210            if buf.ends_with('\n') {
211                buf.pop();
212                if buf.ends_with('\r') {
213                    buf.pop();
214                }
215            }
216            Ok(Value::SingleString(buf))
217        }
218        Err(e) => Err(format!("std:input => {}", e)),
219    }
220}
221
222#[cfg(target_arch = "wasm32")]
223fn std_input_bridge(_interp: &mut Interpreter, _args: Vec<Value>) -> Result<Value, String> {
224    Err("std:input is unavailable in this build (browser)".to_string())
225}
226
227#[cfg(not(target_arch = "wasm32"))]
228#[derive(Default)]
229struct StdinLines;
230
231#[cfg(not(target_arch = "wasm32"))]
232impl PluginIterator for StdinLines {
233    fn next_value(&mut self) -> Result<Value, String> {
234        let mut buf = String::new();
235        let n = io::stdin()
236            .read_line(&mut buf)
237            .map_err(|e| format!("std:input_iter => {}", e))?;
238        if n == 0 {
239            return Err("NO_MORE_DATA".into());
240        }
241        if buf.ends_with('\n') {
242            buf.pop();
243            if buf.ends_with('\r') {
244                buf.pop();
245            }
246        }
247        Ok(Value::SingleString(buf))
248    }
249}
250
251#[cfg(not(target_arch = "wasm32"))]
252fn std_input_iter_bridge(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
253    if !args.is_empty() {
254        return Err(format!(
255            "std:input_iter => expected 0 args, got {}",
256            args.len()
257        ));
258    }
259
260    let handle = IteratorHandle {
261        kind: IteratorKind::Plugin(Arc::new(Mutex::new(StdinLines::default()))),
262    };
263
264    Ok(Value::Iterator(handle))
265}
266
267#[cfg(target_arch = "wasm32")]
268fn std_input_iter_bridge(_interp: &mut Interpreter, _args: Vec<Value>) -> Result<Value, String> {
269    Err("std:input_iter is unavailable in this build (browser)".to_string())
270}
271
272fn std_put_bridge(interp: &mut Interpreter, mut args: Vec<Value>) -> Result<Value, String> {
273    if args.len() != 1 {
274        return Err(format!(
275            "std:put => expected exactly 1 argument, got {}",
276            args.len()
277        ));
278    }
279    let val = args.remove(0);
280
281    match &val {
282        Value::Iterator(handle) => {
283            drain_iterator_blocking(interp, handle, &print_no_nl)?;
284            Ok(val)
285        }
286        other => {
287            let s = to_printable(other);
288            print_no_nl(interp, &s);
289            Ok(val)
290        }
291    }
292}
293
294fn std_log_bridge(interp: &mut Interpreter, mut args: Vec<Value>) -> Result<Value, String> {
295    if args.len() != 1 {
296        return Err(format!(
297            "std:log => expected exactly 1 argument, got {}",
298            args.len()
299        ));
300    }
301    let val = args.remove(0);
302
303    match &val {
304        Value::Iterator(handle) => {
305            drain_iterator_blocking(interp, handle, &print_with_nl)?;
306            Ok(val)
307        }
308        other => {
309            let s = to_printable(other);
310            print_with_nl(interp, &s);
311            Ok(val)
312        }
313    }
314}
315
316/// Deep printer: prints a *recursive* representation and returns the original value.
317fn std_deep_bridge(interp: &mut Interpreter, mut args: Vec<Value>) -> Result<Value, String> {
318    if args.len() != 1 {
319        return Err(format!(
320            "std:deep => expected exactly 1 argument, got {}",
321            args.len()
322        ));
323    }
324    let val = args.remove(0);
325    let s = deep_to_string(&val);
326    println!("{}", s);
327    interp.push_print(&(s + "\n"));
328    Ok(val)
329}
330
331/// ---------- registration API (for baked-in / wasm) ----------
332
333pub fn register_all(interp: &mut Interpreter) {
334    macro_rules! reg {
335        ($name:expr, $f:expr) => {{
336            let func = Arc::new(Mutex::new($f));
337            interp.register_dynamic_function($name, func);
338            interp.set_variable($name, Value::Function(Box::new(FunctionValue::Named($name.into()))));
339        }};
340    }
341
342    // Always available
343    reg!("std:put",  std_put_bridge);
344    reg!("std:log",  std_log_bridge);
345    reg!("std:deep", std_deep_bridge);
346
347    // Input / input_iter: register native or stub depending on target
348    reg!("std:input",      std_input_bridge);
349    reg!("std:input_iter", std_input_iter_bridge);
350}
351
352/// ---------- native dynamic entry (non-wasm targets) ----------
353
354#[cfg(not(target_arch = "wasm32"))]
355#[no_mangle]
356pub unsafe extern "C" fn Cargo_lock(
357    interp_ptr: *mut std::ffi::c_void,
358    _extra: *const std::ffi::c_void,
359) -> i32 {
360    if interp_ptr.is_null() {
361        return 1;
362    }
363    let interp = &mut *(interp_ptr as *mut Interpreter);
364    register_all(interp);
365    0
366}