1use std::collections::BTreeMap;
2use std::rc::Rc;
3use std::sync::atomic::Ordering;
4use std::{cell::RefCell, future::Future, pin::Pin};
5
6use crate::mcp::VmMcpClientHandle;
7
8use super::{VmAtomicHandle, VmChannelHandle, VmClosure, VmError, VmGenerator, VmRange};
9
10pub type VmAsyncBuiltinFn =
12 Rc<dyn Fn(Vec<VmValue>) -> Pin<Box<dyn Future<Output = Result<VmValue, VmError>>>>>;
13
14#[derive(Debug, Clone)]
16pub enum VmValue {
17 Int(i64),
18 Float(f64),
19 String(Rc<str>),
20 Bool(bool),
21 Nil,
22 List(Rc<Vec<VmValue>>),
23 Dict(Rc<BTreeMap<String, VmValue>>),
24 Closure(Rc<VmClosure>),
25 BuiltinRef(Rc<str>),
29 Duration(u64),
30 EnumVariant {
31 enum_name: String,
32 variant: String,
33 fields: Vec<VmValue>,
34 },
35 StructInstance {
36 struct_name: String,
37 fields: BTreeMap<String, VmValue>,
38 },
39 TaskHandle(String),
40 Channel(VmChannelHandle),
41 Atomic(VmAtomicHandle),
42 McpClient(VmMcpClientHandle),
43 Set(Rc<Vec<VmValue>>),
44 Generator(VmGenerator),
45 Range(VmRange),
46 Iter(Rc<RefCell<crate::vm::iter::VmIter>>),
48 Pair(Rc<(VmValue, VmValue)>),
53}
54
55impl VmValue {
56 pub fn is_truthy(&self) -> bool {
57 match self {
58 VmValue::Bool(b) => *b,
59 VmValue::Nil => false,
60 VmValue::Int(n) => *n != 0,
61 VmValue::Float(n) => *n != 0.0,
62 VmValue::String(s) => !s.is_empty(),
63 VmValue::List(l) => !l.is_empty(),
64 VmValue::Dict(d) => !d.is_empty(),
65 VmValue::Closure(_) => true,
66 VmValue::BuiltinRef(_) => true,
67 VmValue::Duration(ms) => *ms > 0,
68 VmValue::EnumVariant { .. } => true,
69 VmValue::StructInstance { .. } => true,
70 VmValue::TaskHandle(_) => true,
71 VmValue::Channel(_) => true,
72 VmValue::Atomic(_) => true,
73 VmValue::McpClient(_) => true,
74 VmValue::Set(s) => !s.is_empty(),
75 VmValue::Generator(_) => true,
76 VmValue::Range(_) => true,
79 VmValue::Iter(_) => true,
80 VmValue::Pair(_) => true,
81 }
82 }
83
84 pub fn type_name(&self) -> &'static str {
85 match self {
86 VmValue::String(_) => "string",
87 VmValue::Int(_) => "int",
88 VmValue::Float(_) => "float",
89 VmValue::Bool(_) => "bool",
90 VmValue::Nil => "nil",
91 VmValue::List(_) => "list",
92 VmValue::Dict(_) => "dict",
93 VmValue::Closure(_) => "closure",
94 VmValue::BuiltinRef(_) => "builtin",
95 VmValue::Duration(_) => "duration",
96 VmValue::EnumVariant { .. } => "enum",
97 VmValue::StructInstance { .. } => "struct",
98 VmValue::TaskHandle(_) => "task_handle",
99 VmValue::Channel(_) => "channel",
100 VmValue::Atomic(_) => "atomic",
101 VmValue::McpClient(_) => "mcp_client",
102 VmValue::Set(_) => "set",
103 VmValue::Generator(_) => "generator",
104 VmValue::Range(_) => "range",
105 VmValue::Iter(_) => "iter",
106 VmValue::Pair(_) => "pair",
107 }
108 }
109
110 pub fn display(&self) -> String {
111 let mut out = String::new();
112 self.write_display(&mut out);
113 out
114 }
115
116 pub fn write_display(&self, out: &mut String) {
119 use std::fmt::Write;
120
121 match self {
122 VmValue::Int(n) => {
123 let _ = write!(out, "{n}");
124 }
125 VmValue::Float(n) => {
126 if *n == (*n as i64) as f64 && n.abs() < 1e15 {
127 let _ = write!(out, "{n:.1}");
128 } else {
129 let _ = write!(out, "{n}");
130 }
131 }
132 VmValue::String(s) => out.push_str(s),
133 VmValue::Bool(b) => out.push_str(if *b { "true" } else { "false" }),
134 VmValue::Nil => out.push_str("nil"),
135 VmValue::List(items) => {
136 out.push('[');
137 for (i, item) in items.iter().enumerate() {
138 if i > 0 {
139 out.push_str(", ");
140 }
141 item.write_display(out);
142 }
143 out.push(']');
144 }
145 VmValue::Dict(map) => {
146 out.push('{');
147 for (i, (k, v)) in map.iter().enumerate() {
148 if i > 0 {
149 out.push_str(", ");
150 }
151 out.push_str(k);
152 out.push_str(": ");
153 v.write_display(out);
154 }
155 out.push('}');
156 }
157 VmValue::Closure(c) => {
158 let _ = write!(out, "<fn({})>", c.func.params.join(", "));
159 }
160 VmValue::BuiltinRef(name) => {
161 let _ = write!(out, "<builtin {name}>");
162 }
163 VmValue::Duration(ms) => {
164 if *ms >= 3_600_000 && ms % 3_600_000 == 0 {
165 let _ = write!(out, "{}h", ms / 3_600_000);
166 } else if *ms >= 60_000 && ms % 60_000 == 0 {
167 let _ = write!(out, "{}m", ms / 60_000);
168 } else if *ms >= 1000 && ms % 1000 == 0 {
169 let _ = write!(out, "{}s", ms / 1000);
170 } else {
171 let _ = write!(out, "{}ms", ms);
172 }
173 }
174 VmValue::EnumVariant {
175 enum_name,
176 variant,
177 fields,
178 } => {
179 if fields.is_empty() {
180 let _ = write!(out, "{enum_name}.{variant}");
181 } else {
182 let _ = write!(out, "{enum_name}.{variant}(");
183 for (i, v) in fields.iter().enumerate() {
184 if i > 0 {
185 out.push_str(", ");
186 }
187 v.write_display(out);
188 }
189 out.push(')');
190 }
191 }
192 VmValue::StructInstance {
193 struct_name,
194 fields,
195 } => {
196 let _ = write!(out, "{struct_name} {{");
197 for (i, (k, v)) in fields.iter().enumerate() {
198 if i > 0 {
199 out.push_str(", ");
200 }
201 out.push_str(k);
202 out.push_str(": ");
203 v.write_display(out);
204 }
205 out.push('}');
206 }
207 VmValue::TaskHandle(id) => {
208 let _ = write!(out, "<task:{id}>");
209 }
210 VmValue::Channel(ch) => {
211 let _ = write!(out, "<channel:{}>", ch.name);
212 }
213 VmValue::Atomic(a) => {
214 let _ = write!(out, "<atomic:{}>", a.value.load(Ordering::SeqCst));
215 }
216 VmValue::McpClient(c) => {
217 let _ = write!(out, "<mcp_client:{}>", c.name);
218 }
219 VmValue::Set(items) => {
220 out.push_str("set(");
221 for (i, item) in items.iter().enumerate() {
222 if i > 0 {
223 out.push_str(", ");
224 }
225 item.write_display(out);
226 }
227 out.push(')');
228 }
229 VmValue::Generator(g) => {
230 if g.done.get() {
231 out.push_str("<generator (done)>");
232 } else {
233 out.push_str("<generator>");
234 }
235 }
236 VmValue::Range(r) => {
239 let _ = write!(out, "{} to {}", r.start, r.end);
240 if !r.inclusive {
241 out.push_str(" exclusive");
242 }
243 }
244 VmValue::Iter(h) => {
245 if matches!(&*h.borrow(), crate::vm::iter::VmIter::Exhausted) {
246 out.push_str("<iter (exhausted)>");
247 } else {
248 out.push_str("<iter>");
249 }
250 }
251 VmValue::Pair(p) => {
252 out.push('(');
253 p.0.write_display(out);
254 out.push_str(", ");
255 p.1.write_display(out);
256 out.push(')');
257 }
258 }
259 }
260
261 pub fn as_dict(&self) -> Option<&BTreeMap<String, VmValue>> {
263 if let VmValue::Dict(d) = self {
264 Some(d)
265 } else {
266 None
267 }
268 }
269
270 pub fn as_int(&self) -> Option<i64> {
271 if let VmValue::Int(n) = self {
272 Some(*n)
273 } else {
274 None
275 }
276 }
277}
278
279pub type VmBuiltinFn = Rc<dyn Fn(&[VmValue], &mut String) -> Result<VmValue, VmError>>;