1use std::collections::{BTreeMap, HashMap};
2use std::rc::Rc;
3use std::sync::atomic::Ordering;
4use std::{cell::RefCell, future::Future, pin::Pin};
5
6use crate::mcp::VmMcpClientHandle;
7use crate::BuiltinId;
8
9use super::{VmAtomicHandle, VmChannelHandle, VmClosure, VmError, VmGenerator, VmRange};
10
11pub type VmAsyncBuiltinFn =
13 Rc<dyn Fn(Vec<VmValue>) -> Pin<Box<dyn Future<Output = Result<VmValue, VmError>>>>>;
14
15#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct StructLayout {
18 struct_name: String,
19 field_names: Vec<String>,
20 field_indexes: HashMap<String, usize>,
21}
22
23impl StructLayout {
24 pub fn new(struct_name: impl Into<String>, field_names: Vec<String>) -> Self {
25 let mut deduped = Vec::with_capacity(field_names.len());
26 let mut field_indexes = HashMap::with_capacity(field_names.len());
27 for field_name in field_names {
28 if field_indexes.contains_key(&field_name) {
29 continue;
30 }
31 let index = deduped.len();
32 field_indexes.insert(field_name.clone(), index);
33 deduped.push(field_name);
34 }
35
36 Self {
37 struct_name: struct_name.into(),
38 field_names: deduped,
39 field_indexes,
40 }
41 }
42
43 pub fn from_map(struct_name: impl Into<String>, fields: &BTreeMap<String, VmValue>) -> Self {
44 Self::new(struct_name, fields.keys().cloned().collect())
45 }
46
47 pub fn struct_name(&self) -> &str {
48 &self.struct_name
49 }
50
51 pub fn field_names(&self) -> &[String] {
52 &self.field_names
53 }
54
55 pub fn field_index(&self, field_name: &str) -> Option<usize> {
56 if self.field_names.len() <= 8 {
57 return self
58 .field_names
59 .iter()
60 .position(|candidate| candidate == field_name);
61 }
62 self.field_indexes.get(field_name).copied()
63 }
64
65 pub fn with_appended_field(&self, field_name: String) -> Self {
66 if self.field_indexes.contains_key(&field_name) {
67 return self.clone();
68 }
69 let mut field_names = self.field_names.clone();
70 field_names.push(field_name);
71 Self::new(self.struct_name.clone(), field_names)
72 }
73}
74
75#[derive(Debug, Clone)]
82pub enum VmValue {
83 Int(i64),
84 Float(f64),
85 String(Rc<str>),
86 Bytes(Rc<Vec<u8>>),
87 Bool(bool),
88 Nil,
89 List(Rc<Vec<VmValue>>),
90 Dict(Rc<BTreeMap<String, VmValue>>),
91 Closure(Rc<VmClosure>),
92 BuiltinRef(Rc<str>),
96 BuiltinRefId {
99 id: BuiltinId,
100 name: Rc<str>,
101 },
102 Duration(u64),
103 EnumVariant {
104 enum_name: Rc<str>,
105 variant: Rc<str>,
106 fields: Rc<Vec<VmValue>>,
107 },
108 StructInstance {
109 layout: Rc<StructLayout>,
110 fields: Rc<Vec<Option<VmValue>>>,
111 },
112 TaskHandle(String),
113 Channel(VmChannelHandle),
114 Atomic(VmAtomicHandle),
115 McpClient(VmMcpClientHandle),
116 Set(Rc<Vec<VmValue>>),
117 Generator(VmGenerator),
118 Range(VmRange),
119 Iter(Rc<RefCell<crate::vm::iter::VmIter>>),
121 Pair(Rc<(VmValue, VmValue)>),
126}
127
128impl VmValue {
129 pub fn enum_variant(
130 enum_name: impl Into<Rc<str>>,
131 variant: impl Into<Rc<str>>,
132 fields: Vec<VmValue>,
133 ) -> Self {
134 VmValue::EnumVariant {
135 enum_name: enum_name.into(),
136 variant: variant.into(),
137 fields: Rc::new(fields),
138 }
139 }
140
141 pub fn struct_instance(
142 struct_name: impl Into<Rc<str>>,
143 fields: BTreeMap<String, VmValue>,
144 ) -> Self {
145 Self::struct_instance_from_map(struct_name.into().to_string(), fields)
146 }
147
148 pub fn is_truthy(&self) -> bool {
149 match self {
150 VmValue::Bool(b) => *b,
151 VmValue::Nil => false,
152 VmValue::Int(n) => *n != 0,
153 VmValue::Float(n) => *n != 0.0,
154 VmValue::String(s) => !s.is_empty(),
155 VmValue::Bytes(bytes) => !bytes.is_empty(),
156 VmValue::List(l) => !l.is_empty(),
157 VmValue::Dict(d) => !d.is_empty(),
158 VmValue::Closure(_) => true,
159 VmValue::BuiltinRef(_) => true,
160 VmValue::BuiltinRefId { .. } => true,
161 VmValue::Duration(ms) => *ms > 0,
162 VmValue::EnumVariant { .. } => true,
163 VmValue::StructInstance { .. } => true,
164 VmValue::TaskHandle(_) => true,
165 VmValue::Channel(_) => true,
166 VmValue::Atomic(_) => true,
167 VmValue::McpClient(_) => true,
168 VmValue::Set(s) => !s.is_empty(),
169 VmValue::Generator(_) => true,
170 VmValue::Range(_) => true,
173 VmValue::Iter(_) => true,
174 VmValue::Pair(_) => true,
175 }
176 }
177
178 pub fn type_name(&self) -> &'static str {
179 match self {
180 VmValue::String(_) => "string",
181 VmValue::Bytes(_) => "bytes",
182 VmValue::Int(_) => "int",
183 VmValue::Float(_) => "float",
184 VmValue::Bool(_) => "bool",
185 VmValue::Nil => "nil",
186 VmValue::List(_) => "list",
187 VmValue::Dict(_) => "dict",
188 VmValue::Closure(_) => "closure",
189 VmValue::BuiltinRef(_) => "builtin",
190 VmValue::BuiltinRefId { .. } => "builtin",
191 VmValue::Duration(_) => "duration",
192 VmValue::EnumVariant { .. } => "enum",
193 VmValue::StructInstance { .. } => "struct",
194 VmValue::TaskHandle(_) => "task_handle",
195 VmValue::Channel(_) => "channel",
196 VmValue::Atomic(_) => "atomic",
197 VmValue::McpClient(_) => "mcp_client",
198 VmValue::Set(_) => "set",
199 VmValue::Generator(_) => "generator",
200 VmValue::Range(_) => "range",
201 VmValue::Iter(_) => "iter",
202 VmValue::Pair(_) => "pair",
203 }
204 }
205
206 pub fn struct_name(&self) -> Option<&str> {
207 match self {
208 VmValue::StructInstance { layout, .. } => Some(layout.struct_name()),
209 _ => None,
210 }
211 }
212
213 pub fn struct_field(&self, field_name: &str) -> Option<&VmValue> {
214 match self {
215 VmValue::StructInstance { layout, fields } => layout
216 .field_index(field_name)
217 .and_then(|index| fields.get(index))
218 .and_then(Option::as_ref),
219 _ => None,
220 }
221 }
222
223 pub fn struct_fields_map(&self) -> Option<BTreeMap<String, VmValue>> {
224 match self {
225 VmValue::StructInstance { layout, fields } => {
226 Some(struct_fields_to_map(layout, fields))
227 }
228 _ => None,
229 }
230 }
231
232 pub fn struct_instance_from_map(
233 struct_name: impl Into<String>,
234 fields: BTreeMap<String, VmValue>,
235 ) -> Self {
236 let layout = Rc::new(StructLayout::from_map(struct_name, &fields));
237 let slots = layout
238 .field_names()
239 .iter()
240 .map(|name| fields.get(name).cloned())
241 .collect();
242 VmValue::StructInstance {
243 layout,
244 fields: Rc::new(slots),
245 }
246 }
247
248 pub fn struct_instance_with_layout(
249 struct_name: impl Into<String>,
250 field_names: Vec<String>,
251 field_values: BTreeMap<String, VmValue>,
252 ) -> Self {
253 let layout = Rc::new(StructLayout::new(struct_name, field_names));
254 let fields = layout
255 .field_names()
256 .iter()
257 .map(|name| field_values.get(name).cloned())
258 .collect();
259 VmValue::StructInstance {
260 layout,
261 fields: Rc::new(fields),
262 }
263 }
264
265 pub fn struct_instance_with_property(
266 &self,
267 field_name: String,
268 value: VmValue,
269 ) -> Option<Self> {
270 let VmValue::StructInstance { layout, fields } = self else {
271 return None;
272 };
273
274 let mut new_fields = fields.as_ref().clone();
275 let layout = match layout.field_index(&field_name) {
276 Some(index) => {
277 if index >= new_fields.len() {
278 new_fields.resize(index + 1, None);
279 }
280 new_fields[index] = Some(value);
281 Rc::clone(layout)
282 }
283 None => {
284 let new_layout = Rc::new(layout.with_appended_field(field_name));
285 new_fields.push(Some(value));
286 new_layout
287 }
288 };
289
290 Some(VmValue::StructInstance {
291 layout,
292 fields: Rc::new(new_fields),
293 })
294 }
295
296 pub fn display(&self) -> String {
297 let mut out = String::new();
298 self.write_display(&mut out);
299 out
300 }
301
302 pub fn write_display(&self, out: &mut String) {
305 use std::fmt::Write;
306
307 match self {
308 VmValue::Int(n) => {
309 let _ = write!(out, "{n}");
310 }
311 VmValue::Float(n) => {
312 if *n == (*n as i64) as f64 && n.abs() < 1e15 {
313 let _ = write!(out, "{n:.1}");
314 } else {
315 let _ = write!(out, "{n}");
316 }
317 }
318 VmValue::String(s) => out.push_str(s),
319 VmValue::Bytes(bytes) => {
320 const MAX_PREVIEW_BYTES: usize = 32;
321
322 out.push_str("b\"");
323 for byte in bytes.iter().take(MAX_PREVIEW_BYTES) {
324 let _ = write!(out, "{byte:02x}");
325 }
326 if bytes.len() > MAX_PREVIEW_BYTES {
327 let _ = write!(out, "...+{}", bytes.len() - MAX_PREVIEW_BYTES);
328 }
329 out.push('"');
330 }
331 VmValue::Bool(b) => out.push_str(if *b { "true" } else { "false" }),
332 VmValue::Nil => out.push_str("nil"),
333 VmValue::List(items) => {
334 out.push('[');
335 for (i, item) in items.iter().enumerate() {
336 if i > 0 {
337 out.push_str(", ");
338 }
339 item.write_display(out);
340 }
341 out.push(']');
342 }
343 VmValue::Dict(map) => {
344 out.push('{');
345 for (i, (k, v)) in map.iter().enumerate() {
346 if i > 0 {
347 out.push_str(", ");
348 }
349 out.push_str(k);
350 out.push_str(": ");
351 v.write_display(out);
352 }
353 out.push('}');
354 }
355 VmValue::Closure(c) => {
356 let _ = write!(out, "<fn({})>", c.func.params.join(", "));
357 }
358 VmValue::BuiltinRef(name) => {
359 let _ = write!(out, "<builtin {name}>");
360 }
361 VmValue::BuiltinRefId { name, .. } => {
362 let _ = write!(out, "<builtin {name}>");
363 }
364 VmValue::Duration(ms) => {
365 if *ms >= 3_600_000 && ms % 3_600_000 == 0 {
366 let _ = write!(out, "{}h", ms / 3_600_000);
367 } else if *ms >= 60_000 && ms % 60_000 == 0 {
368 let _ = write!(out, "{}m", ms / 60_000);
369 } else if *ms >= 1000 && ms % 1000 == 0 {
370 let _ = write!(out, "{}s", ms / 1000);
371 } else {
372 let _ = write!(out, "{}ms", ms);
373 }
374 }
375 VmValue::EnumVariant {
376 enum_name,
377 variant,
378 fields,
379 } => {
380 if fields.is_empty() {
381 let _ = write!(out, "{enum_name}.{variant}");
382 } else {
383 let _ = write!(out, "{enum_name}.{variant}(");
384 for (i, v) in fields.iter().enumerate() {
385 if i > 0 {
386 out.push_str(", ");
387 }
388 v.write_display(out);
389 }
390 out.push(')');
391 }
392 }
393 VmValue::StructInstance { layout, fields } => {
394 let _ = write!(out, "{} {{", layout.struct_name());
395 for (i, (k, v)) in struct_fields_to_map(layout, fields).iter().enumerate() {
396 if i > 0 {
397 out.push_str(", ");
398 }
399 out.push_str(k);
400 out.push_str(": ");
401 v.write_display(out);
402 }
403 out.push('}');
404 }
405 VmValue::TaskHandle(id) => {
406 let _ = write!(out, "<task:{id}>");
407 }
408 VmValue::Channel(ch) => {
409 let _ = write!(out, "<channel:{}>", ch.name);
410 }
411 VmValue::Atomic(a) => {
412 let _ = write!(out, "<atomic:{}>", a.value.load(Ordering::SeqCst));
413 }
414 VmValue::McpClient(c) => {
415 let _ = write!(out, "<mcp_client:{}>", c.name);
416 }
417 VmValue::Set(items) => {
418 out.push_str("set(");
419 for (i, item) in items.iter().enumerate() {
420 if i > 0 {
421 out.push_str(", ");
422 }
423 item.write_display(out);
424 }
425 out.push(')');
426 }
427 VmValue::Generator(g) => {
428 if g.done.get() {
429 out.push_str("<generator (done)>");
430 } else {
431 out.push_str("<generator>");
432 }
433 }
434 VmValue::Range(r) => {
437 let _ = write!(out, "{} to {}", r.start, r.end);
438 if !r.inclusive {
439 out.push_str(" exclusive");
440 }
441 }
442 VmValue::Iter(h) => {
443 if matches!(&*h.borrow(), crate::vm::iter::VmIter::Exhausted) {
444 out.push_str("<iter (exhausted)>");
445 } else {
446 out.push_str("<iter>");
447 }
448 }
449 VmValue::Pair(p) => {
450 out.push('(');
451 p.0.write_display(out);
452 out.push_str(", ");
453 p.1.write_display(out);
454 out.push(')');
455 }
456 }
457 }
458
459 pub fn as_dict(&self) -> Option<&BTreeMap<String, VmValue>> {
461 if let VmValue::Dict(d) = self {
462 Some(d)
463 } else {
464 None
465 }
466 }
467
468 pub fn as_int(&self) -> Option<i64> {
469 if let VmValue::Int(n) = self {
470 Some(*n)
471 } else {
472 None
473 }
474 }
475
476 pub fn as_bytes(&self) -> Option<&[u8]> {
477 if let VmValue::Bytes(bytes) = self {
478 Some(bytes.as_slice())
479 } else {
480 None
481 }
482 }
483}
484
485pub fn struct_fields_to_map(
486 layout: &StructLayout,
487 fields: &[Option<VmValue>],
488) -> BTreeMap<String, VmValue> {
489 layout
490 .field_names()
491 .iter()
492 .enumerate()
493 .filter_map(|(index, name)| {
494 fields
495 .get(index)
496 .and_then(Option::as_ref)
497 .map(|value| (name.clone(), value.clone()))
498 })
499 .collect()
500}
501
502pub type VmBuiltinFn = Rc<dyn Fn(&[VmValue], &mut String) -> Result<VmValue, VmError>>;