mumustd/
lib.rs

1// src/lib.rs
2//
3// MuMu/Lava "std" plugin – basic stdin/stdout utilities.
4//
5// Exposes four 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//
12// The key fix here is that std:put/std:log *consume* InkIterator / InkTransform
13// rather than printing a debug handle. This lets:
14//
15//   extend("std")
16//   std:log("Type lines; they’ll be printed back with NO extra newline per item.")
17//   std:put(std:input_iter())
18//   std:put("\n")
19//
20// …block for user input and echo each line immediately (no extra newline per
21// item), then return to a fresh prompt line at the end.
22//
23// Build/install (as in your Makefile):
24//   make && sudo make install && sudo ldconfig
25//
26
27use std::io::{self, Write};
28use std::sync::{Arc, Mutex};
29
30use mumu::{
31    parser::interpreter::Interpreter,
32    parser::types::{FunctionValue, InkIteratorKind, Value},
33};
34
35/// ---------- small helpers ----------
36
37fn to_printable(v: &Value) -> String {
38    match v {
39        Value::SingleString(s) => s.clone(),
40        Value::Int(i) => i.to_string(),
41        Value::Long(l) => l.to_string(),
42        Value::Float(f) => f.to_string(),
43        Value::Bool(b) => b.to_string(),
44        // fall back to the engine’s Debug format for structured values
45        _ => format!("{:?}", v),
46    }
47}
48
49fn print_no_nl(interp: &mut Interpreter, s: &str) {
50    print!("{}", s);
51    let _ = io::stdout().flush();
52    interp.push_print(s);
53}
54
55fn print_with_nl(interp: &mut Interpreter, s: &str) {
56    println!("{}", s);
57    interp.push_print(&(s.to_string() + "\n"));
58}
59
60/// Drain an InkTransform (RustClosure based), calling it until it returns
61/// Err("NO_MORE_DATA"). Each produced item is printed via the provided printer.
62fn drain_ink_transform(
63    interp: &mut Interpreter,
64    f: &FunctionValue,
65    printer: &dyn Fn(&mut Interpreter, &str),
66) -> Result<(), String> {
67    match f {
68        FunctionValue::RustClosure(_desc, closure_arc, _arity) => {
69            loop {
70                // lock, call, then drop before the next loop turn
71                let cb = closure_arc
72                    .lock()
73                    .map_err(|_| "std:put => closure poisoned".to_string())?;
74                let out = cb(interp, vec![]);
75                drop(cb);
76
77                match out {
78                    Ok(item) => {
79                        let s = to_printable(&item);
80                        printer(interp, &s);
81                    }
82                    Err(e) if e == "NO_MORE_DATA" => break,
83                    Err(e) => return Err(e),
84                }
85            }
86            Ok(())
87        }
88        _ => {
89            // If it’s some other function kind we can’t iterate – do nothing.
90            Ok(())
91        }
92    }
93}
94
95/// ---------- bridges ----------
96
97fn std_input_bridge(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
98    if args.len() > 1 {
99        return Err(format!(
100            "std:input => expected 0 or 1 arg (prompt), got {}",
101            args.len()
102        ));
103    }
104
105    if let Some(Value::SingleString(prompt)) = args.get(0) {
106        print!("{}", prompt);
107        let _ = io::stdout().flush();
108    }
109
110    let mut buf = String::new();
111    match io::stdin().read_line(&mut buf) {
112        Ok(0) => Ok(Value::SingleString(String::new())), // EOF => empty string
113        Ok(_) => {
114            if buf.ends_with('\n') {
115                buf.pop();
116                if buf.ends_with('\r') {
117                    buf.pop();
118                }
119            }
120            Ok(Value::SingleString(buf))
121        }
122        Err(e) => Err(format!("std:input => {}", e)),
123    }
124}
125
126fn std_input_iter_bridge(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
127    if !args.is_empty() {
128        return Err(format!(
129            "std:input_iter => expected 0 args, got {}",
130            args.len()
131        ));
132    }
133
134    // Implement as an InkTransform backed by a 0-arity RustClosure that returns
135    // one line per call (blocking), and "NO_MORE_DATA" on EOF.
136    let closure = Arc::new(Mutex::new(
137        move |_i: &mut Interpreter, _argv: Vec<Value>| -> Result<Value, String> {
138            let mut buf = String::new();
139            let n = io::stdin()
140                .read_line(&mut buf)
141                .map_err(|e| format!("std:input_iter => {}", e))?;
142            if n == 0 {
143                return Err("NO_MORE_DATA".into());
144            }
145            if buf.ends_with('\n') {
146                buf.pop();
147                if buf.ends_with('\r') {
148                    buf.pop();
149                }
150            }
151            Ok(Value::SingleString(buf))
152        },
153    ));
154
155    Ok(Value::InkTransform(Box::new(FunctionValue::RustClosure(
156        "std:input_iter".to_string(),
157        closure,
158        0,
159    ))))
160}
161
162fn std_put_bridge(interp: &mut Interpreter, mut args: Vec<Value>) -> Result<Value, String> {
163    if args.len() != 1 {
164        return Err(format!(
165            "std:put => expected exactly 1 argument, got {}",
166            args.len()
167        ));
168    }
169    let val = args.remove(0);
170
171    // Perform the draining/printing while only borrowing `val`,
172    // then return `val` *after* the borrow ends to satisfy the borrow checker.
173    let res: Result<(), String> = match &val {
174        // Drain transforms
175        Value::InkTransform(fb) => drain_ink_transform(interp, fb, &print_no_nl),
176
177        // Drain iterators (both core and plugin kinds) if encountered
178        Value::InkIterator(handle) => match &handle.kind {
179            InkIteratorKind::Core(state_arc) => {
180                let mut guard = state_arc
181                    .lock()
182                    .map_err(|_| "std:put => iterator lock error".to_string())?;
183                while !guard.done && guard.current < guard.end {
184                    let item = guard.current;
185                    guard.current += 1;
186                    if guard.current >= guard.end {
187                        guard.done = true;
188                    }
189                    drop(guard);
190                    print_no_nl(interp, &item.to_string());
191                    guard = state_arc
192                        .lock()
193                        .map_err(|_| "std:put => iterator lock error".to_string())?;
194                }
195                Ok(())
196            }
197            InkIteratorKind::Plugin(plugin_arc) => {
198                let mut plugin = plugin_arc
199                    .lock()
200                    .map_err(|_| "std:put => plugin iterator lock error".to_string())?;
201                loop {
202                    match plugin.next_value() {
203                        Ok(item) => {
204                            let s = to_printable(&item);
205                            print_no_nl(interp, &s);
206                        }
207                        Err(e) if e == "NO_MORE_DATA" => break,
208                        Err(e) => return Err(e),
209                    }
210                }
211                Ok(())
212            }
213        },
214
215        // Scalar/other values – print once, no newline
216        other => {
217            let s = to_printable(other);
218            print_no_nl(interp, &s);
219            Ok(())
220        }
221    };
222
223    res?;
224    Ok(val)
225}
226
227fn std_log_bridge(interp: &mut Interpreter, mut args: Vec<Value>) -> Result<Value, String> {
228    if args.len() != 1 {
229        return Err(format!(
230            "std:log => expected exactly 1 argument, got {}",
231            args.len()
232        ));
233    }
234    let val = args.remove(0);
235
236    // Same trick as std_put_bridge: do work while `val` is only borrowed,
237    // then return the owned `val` afterwards.
238    let res: Result<(), String> = match &val {
239        Value::InkTransform(fb) => drain_ink_transform(interp, fb, &print_with_nl),
240        Value::InkIterator(handle) => match &handle.kind {
241            InkIteratorKind::Core(state_arc) => {
242                let mut guard = state_arc
243                    .lock()
244                    .map_err(|_| "std:log => iterator lock error".to_string())?;
245                while !guard.done && guard.current < guard.end {
246                    let item = guard.current;
247                    guard.current += 1;
248                    if guard.current >= guard.end {
249                        guard.done = true;
250                    }
251                    drop(guard);
252                    print_with_nl(interp, &item.to_string());
253                    guard = state_arc
254                        .lock()
255                        .map_err(|_| "std:log => iterator lock error".to_string())?;
256                }
257                Ok(())
258            }
259            InkIteratorKind::Plugin(plugin_arc) => {
260                let mut plugin = plugin_arc
261                    .lock()
262                    .map_err(|_| "std:log => plugin iterator lock error".to_string())?;
263                loop {
264                    match plugin.next_value() {
265                        Ok(item) => {
266                            let s = to_printable(&item);
267                            print_with_nl(interp, &s);
268                        }
269                        Err(e) if e == "NO_MORE_DATA" => break,
270                        Err(e) => return Err(e),
271                    }
272                }
273                Ok(())
274            }
275        },
276        other => {
277            let s = to_printable(other);
278            print_with_nl(interp, &s);
279            Ok(())
280        }
281    };
282
283    res?;
284    Ok(val)
285}
286
287/// ---------- plugin entry ----------
288
289#[export_name = "Cargo_lock"]
290pub unsafe extern "C" fn cargo_lock(
291    interp_ptr: *mut std::ffi::c_void,
292    _extra: *const std::ffi::c_void,
293) -> i32 {
294    if interp_ptr.is_null() {
295        return 1;
296    }
297    let interp = &mut *(interp_ptr as *mut Interpreter);
298
299    // Register dynamic functions
300    let f_input = Arc::new(Mutex::new(std_input_bridge));
301    let f_iter = Arc::new(Mutex::new(std_input_iter_bridge));
302    let f_put = Arc::new(Mutex::new(std_put_bridge));
303    let f_log = Arc::new(Mutex::new(std_log_bridge));
304
305    interp.register_dynamic_function("std:input", f_input);
306    interp.register_dynamic_function("std:input_iter", f_iter);
307    interp.register_dynamic_function("std:put", f_put);
308    interp.register_dynamic_function("std:log", f_log);
309
310    // Create top-level variables (callables inside MuMu)
311    interp.set_variable(
312        "std:input",
313        Value::Function(Box::new(FunctionValue::Named("std:input".into()))),
314    );
315    interp.set_variable(
316        "std:input_iter",
317        Value::Function(Box::new(FunctionValue::Named("std:input_iter".into()))),
318    );
319    interp.set_variable(
320        "std:put",
321        Value::Function(Box::new(FunctionValue::Named("std:put".into()))),
322    );
323    interp.set_variable(
324        "std:log",
325        Value::Function(Box::new(FunctionValue::Named("std:log".into()))),
326    );
327
328    0
329}