mumustd/share/
mod.rs

1// src/share/mod.rs
2//
3// Shared helpers for std-mumu: formatting, deep printing, and iterator draining.
4//
5
6pub mod format;
7
8use std::io::{self, Write};
9
10use core_mumu::{
11    parser::interpreter::Interpreter,
12    parser::types::{IteratorHandle, IteratorKind, Value},
13};
14
15/// Render a Value into a flat, printable string (non-recursive).
16/// Includes a human-friendly formatting for `KeyedError`.
17pub fn to_printable(v: &Value) -> String {
18    match v {
19        Value::SingleString(s) => s.clone(),
20        Value::Int(i) => i.to_string(),
21        Value::Long(l) => l.to_string(),
22        Value::Float(f) => f.to_string(),
23        Value::Bool(b) => b.to_string(),
24        Value::KeyedError(map) => {
25            let msg = map
26                .get("message")
27                .and_then(|v| if let Value::SingleString(s) = v { Some(s.as_str()) } else { None })
28                .unwrap_or("");
29            let line = map
30                .get("line")
31                .and_then(|v| if let Value::Int(i) = v { Some(*i) } else { None })
32                .unwrap_or(0);
33            let col = map
34                .get("col")
35                .and_then(|v| if let Value::Int(i) = v { Some(*i) } else { None })
36                .unwrap_or(0);
37            if !msg.is_empty() {
38                if line > 0 || col > 0 {
39                    format!("Error{{ line:{}, col:{}, message:\"{}\" }}", line, col, msg)
40                } else {
41                    format!("Error{{ message:\"{}\" }}", msg)
42                }
43            } else {
44                format!("Error{{ …{} keys… }}", map.len())
45            }
46        }
47        _ => format!("{:?}", v),
48    }
49}
50
51/// Escape a string for inclusion in a quoted single-line representation.
52pub fn escape_str(s: &str) -> String {
53    let mut out = String::with_capacity(s.len() + 8);
54    for ch in s.chars() {
55        match ch {
56            '\\' => out.push_str("\\\\"),
57            '"' => out.push_str("\\\""),
58            '\n' => out.push_str("\\n"),
59            '\r' => out.push_str("\\r"),
60            '\t' => out.push_str("\\t"),
61            other => out.push(other),
62        }
63    }
64    out
65}
66
67/// Deep, recursive, MuMu-ish single-line representation.
68pub fn deep_to_string(v: &Value) -> String {
69    deep_to_string_inner(v, 0)
70}
71
72fn deep_to_string_inner(v: &Value, depth: usize) -> String {
73    if depth > 64 {
74        return "<depth-limit>".to_string();
75    }
76    match v {
77        Value::SingleString(s) => format!("\"{}\"", escape_str(s)),
78        Value::Int(i) => i.to_string(),
79        Value::Long(l) => l.to_string(),
80        Value::Float(f) => {
81            if f.fract() == 0.0 {
82                format!("{:.1}", f)
83            } else {
84                f.to_string()
85            }
86        }
87        Value::Bool(b) => b.to_string(),
88        Value::Undefined => "undefined".to_string(),
89        Value::Placeholder => "_".to_string(),
90
91        Value::IntArray(xs) => {
92            let inner = xs.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(", ");
93            format!("[{}]", inner)
94        }
95        Value::FloatArray(xs) => {
96            let inner = xs
97                .iter()
98                .map(|f| if f.fract() == 0.0 { format!("{:.1}", f) } else { f.to_string() })
99                .collect::<Vec<_>>()
100                .join(", ");
101            format!("[{}]", inner)
102        }
103        Value::BoolArray(xs) => {
104            let inner = xs.iter().map(|b| b.to_string()).collect::<Vec<_>>().join(", ");
105            format!("[{}]", inner)
106        }
107        Value::StrArray(xs) => {
108            let inner = xs
109                .iter()
110                .map(|s| format!("\"{}\"", escape_str(s)))
111                .collect::<Vec<_>>()
112                .join(", ");
113            format!("[{}]", inner)
114        }
115        Value::Int2DArray(rows) => {
116            let inner = rows
117                .iter()
118                .map(|r| format!("[{}]", r.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(", ")))
119                .collect::<Vec<_>>()
120                .join(", ");
121            format!("[{}]", inner)
122        }
123        Value::Float2DArray(rows) => {
124            let fmt = |f: &f64| if f.fract() == 0.0 { format!("{:.1}", f) } else { f.to_string() };
125            let inner = rows
126                .iter()
127                .map(|r| format!("[{}]", r.iter().map(fmt).collect::<Vec<_>>().join(", ")))
128                .collect::<Vec<_>>()
129                .join(", ");
130            format!("[{}]", inner)
131        }
132        Value::MixedArray(items) => {
133            let inner = items
134                .iter()
135                .map(|x| deep_to_string_inner(x, depth + 1))
136                .collect::<Vec<_>>()
137                .join(", ");
138            format!("[{}]", inner)
139        }
140        Value::KeyedArray(map) => {
141            let parts = map
142                .iter()
143                .map(|(k, v)| format!("{}: {}", k, deep_to_string_inner(v, depth + 1)))
144                .collect::<Vec<_>>()
145                .join(", ");
146            format!("[{}]", parts)
147        }
148        Value::KeyedError(map) => {
149            let msg = map
150                .get("message")
151                .and_then(|v| if let Value::SingleString(s) = v { Some(s.as_str()) } else { None })
152                .unwrap_or("");
153            let line = map
154                .get("line")
155                .and_then(|v| if let Value::Int(i) = v { Some(*i) } else { None })
156                .unwrap_or(0);
157            let col = map
158                .get("col")
159                .and_then(|v| if let Value::Int(i) = v { Some(*i) } else { None })
160                .unwrap_or(0);
161            if !msg.is_empty() {
162                if line > 0 || col > 0 {
163                    format!("Error{{ line:{}, col:{}, message:\"{}\" }}", line, col, escape_str(msg))
164                } else {
165                    format!("Error{{ message:\"{}\" }}", escape_str(msg))
166                }
167            } else {
168                format!("Error{{ …{} keys… }}", map.len())
169            }
170        }
171
172        Value::Function(_) => "[Function]".to_string(),
173        Value::Stream(sh) => format!("<Stream id={}, label={}>", sh.stream_id, sh.label),
174        Value::Iterator(_) => "[Iterator]".to_string(),
175        Value::Tensor(_) => "[Tensor]".to_string(),
176        Value::Ref(cell) => {
177            let guard = cell.lock().unwrap();
178            deep_to_string_inner(&*guard, depth + 1)
179        }
180        Value::Regex(rx) => format!("Regex(/{}{}/)", rx.pattern, rx.flags),
181    }
182}
183
184/// Print with no trailing newline and push to the interpreter's buffer.
185pub fn print_no_nl(interp: &mut Interpreter, s: &str) {
186    print!("{}", s);
187    let _ = io::stdout().flush();
188    interp.push_print(s);
189}
190
191/// Print with newline and push to the interpreter's buffer (with '\n').
192pub fn print_with_nl(interp: &mut Interpreter, s: &str) {
193    println!("{}", s);
194    interp.push_print(&(s.to_string() + "\n"));
195}
196
197/// Drain an Iterator **blocking**, sending each item through `printer`.
198pub fn drain_iterator_blocking(
199    interp: &mut Interpreter,
200    it: &IteratorHandle,
201    printer: &dyn Fn(&mut Interpreter, &str),
202) -> Result<(), String> {
203    match &it.kind {
204        IteratorKind::Core(state_arc) => {
205            let mut guard = state_arc
206                .lock()
207                .map_err(|_| "std: drain => iterator lock error".to_string())?;
208            while !guard.done && guard.current < guard.end {
209                let item = guard.current;
210                guard.current += 1;
211                if guard.current >= guard.end {
212                    guard.done = true;
213                }
214                drop(guard);
215                printer(interp, &item.to_string());
216                guard = state_arc
217                    .lock()
218                    .map_err(|_| "std: drain => iterator lock error".to_string())?;
219            }
220            Ok(())
221        }
222        IteratorKind::Plugin(plugin_arc) => {
223            let mut plugin =
224                plugin_arc.lock().map_err(|_| "std: drain => plugin iterator lock error".to_string())?;
225            loop {
226                match plugin.next_value() {
227                    Ok(item) => {
228                        let s = to_printable(&item);
229                        printer(interp, &s);
230                    }
231                    Err(e) if e == "NO_MORE_DATA" => break,
232                    Err(e) => return Err(e),
233                }
234            }
235            Ok(())
236        }
237    }
238}