Skip to main content

mq_lang/eval/
runtime_value.rs

1use super::env::Env;
2use crate::{AstParams, Ident, Program, Shared, SharedCell, ast, number::Number};
3use mq_markdown::Node;
4use smol_str::SmolStr;
5use std::{
6    borrow::Cow,
7    cmp::Ordering,
8    collections::BTreeMap,
9    ops::{Index, IndexMut},
10};
11
12/// Runtime selector for indexing into markdown nodes.
13#[derive(Debug, Clone, PartialEq)]
14pub enum Selector {
15    /// Selects a child node at the specified index.
16    Index(usize),
17}
18
19/// Represents a module's runtime environment with its exports.
20#[derive(Clone, Debug)]
21pub struct ModuleEnv {
22    name: SmolStr,
23    exports: Shared<SharedCell<Env>>,
24}
25
26impl ModuleEnv {
27    /// Creates a new module environment with the given name and exports.
28    pub fn new(name: &str, exports: Shared<SharedCell<Env>>) -> Self {
29        Self {
30            name: SmolStr::new(name),
31            exports,
32        }
33    }
34
35    /// Returns the name of the module.
36    pub fn name(&self) -> &str {
37        &self.name
38    }
39
40    /// Returns a reference to the module's exports environment.
41    pub fn exports(&self) -> &Shared<SharedCell<Env>> {
42        &self.exports
43    }
44
45    /// Returns the number of exports in this module.
46    pub fn len(&self) -> usize {
47        #[cfg(not(feature = "sync"))]
48        {
49            self.exports.borrow().len()
50        }
51
52        #[cfg(feature = "sync")]
53        {
54            self.exports.read().unwrap().len()
55        }
56    }
57}
58
59impl PartialEq for ModuleEnv {
60    fn eq(&self, other: &Self) -> bool {
61        #[cfg(not(feature = "sync"))]
62        let exports = self.exports().borrow();
63        #[cfg(feature = "sync")]
64        let exports = self.exports().read().unwrap();
65
66        #[cfg(not(feature = "sync"))]
67        let other_exports = other.exports().borrow();
68        #[cfg(feature = "sync")]
69        let other_exports = other.exports().read().unwrap();
70
71        self.name == other.name && std::ptr::eq(&*exports, &*other_exports)
72    }
73}
74
75/// A value in the mq runtime.
76///
77/// This enum represents all possible value types that can exist during
78/// program execution, including numbers, strings, markdown nodes, functions,
79/// and more complex data structures.
80#[derive(Clone, Default)]
81pub enum RuntimeValue {
82    /// A numeric value.
83    Number(Number),
84    /// A boolean value (`true` or `false`).
85    Boolean(bool),
86    /// A string value.
87    String(String),
88    /// A symbol (interned identifier).
89    Symbol(Ident),
90    /// An array of runtime values.
91    Array(Vec<RuntimeValue>),
92    /// A markdown node with an optional selector for indexing.
93    Markdown(Node, Option<Selector>),
94    /// A user-defined function with parameters, body (program), and captured environment.
95    Function(AstParams, Program, Shared<SharedCell<Env>>),
96    /// A built-in native function identified by name.
97    NativeFunction(Ident),
98    /// A dictionary mapping identifiers to runtime values.
99    Dict(BTreeMap<Ident, RuntimeValue>),
100    /// A module with its exports.
101    Module(ModuleEnv),
102    /// An AST node (quoted expression).
103    Ast(Shared<ast::node::Node>),
104    /// An empty or null value.
105    #[default]
106    None,
107}
108
109// Custom PartialEq implementation to avoid comparing Env pointers
110impl PartialEq for RuntimeValue {
111    fn eq(&self, other: &Self) -> bool {
112        match (self, other) {
113            (RuntimeValue::Number(a), RuntimeValue::Number(b)) => a == b,
114            (RuntimeValue::Boolean(a), RuntimeValue::Boolean(b)) => a == b,
115            (RuntimeValue::String(a), RuntimeValue::String(b)) => a == b,
116            (RuntimeValue::Symbol(a), RuntimeValue::Symbol(b)) => a == b,
117            (RuntimeValue::Array(a), RuntimeValue::Array(b)) => a == b,
118            (RuntimeValue::Markdown(a, sa), RuntimeValue::Markdown(b, sb)) => a == b && sa == sb,
119            (RuntimeValue::Function(a1, b1, _), RuntimeValue::Function(a2, b2, _)) => a1 == a2 && b1 == b2,
120            (RuntimeValue::NativeFunction(a), RuntimeValue::NativeFunction(b)) => a == b,
121            (RuntimeValue::Dict(a), RuntimeValue::Dict(b)) => a == b,
122            (RuntimeValue::Module(a), RuntimeValue::Module(b)) => a == b,
123            (RuntimeValue::Ast(a), RuntimeValue::Ast(b)) => a == b,
124            (RuntimeValue::None, RuntimeValue::None) => true,
125            _ => false,
126        }
127    }
128}
129
130impl From<Node> for RuntimeValue {
131    fn from(node: Node) -> Self {
132        RuntimeValue::Markdown(node, None)
133    }
134}
135
136impl From<bool> for RuntimeValue {
137    fn from(b: bool) -> Self {
138        RuntimeValue::Boolean(b)
139    }
140}
141
142impl From<String> for RuntimeValue {
143    fn from(s: String) -> Self {
144        RuntimeValue::String(s)
145    }
146}
147
148impl From<&str> for RuntimeValue {
149    fn from(s: &str) -> Self {
150        RuntimeValue::String(s.to_string())
151    }
152}
153
154impl From<&mut str> for RuntimeValue {
155    fn from(s: &mut str) -> Self {
156        RuntimeValue::String(s.to_string())
157    }
158}
159
160impl From<Number> for RuntimeValue {
161    fn from(n: Number) -> Self {
162        RuntimeValue::Number(n)
163    }
164}
165
166impl From<Ident> for RuntimeValue {
167    fn from(i: Ident) -> Self {
168        RuntimeValue::Symbol(i)
169    }
170}
171
172impl From<usize> for RuntimeValue {
173    fn from(n: usize) -> Self {
174        RuntimeValue::Number(Number::from(n))
175    }
176}
177
178impl From<Vec<RuntimeValue>> for RuntimeValue {
179    fn from(arr: Vec<RuntimeValue>) -> Self {
180        RuntimeValue::Array(arr)
181    }
182}
183
184impl From<BTreeMap<Ident, RuntimeValue>> for RuntimeValue {
185    fn from(map: BTreeMap<Ident, RuntimeValue>) -> Self {
186        RuntimeValue::Dict(map)
187    }
188}
189
190impl From<Vec<(String, Number)>> for RuntimeValue {
191    fn from(v: Vec<(String, Number)>) -> Self {
192        RuntimeValue::Dict(
193            v.into_iter()
194                .map(|(k, v)| (Ident::new(&k), RuntimeValue::Number(v)))
195                .collect::<BTreeMap<Ident, RuntimeValue>>(),
196        )
197    }
198}
199
200impl From<mq_markdown::AttrValue> for RuntimeValue {
201    fn from(attr_value: mq_markdown::AttrValue) -> Self {
202        match attr_value {
203            mq_markdown::AttrValue::String(s) => RuntimeValue::String(s),
204            mq_markdown::AttrValue::Number(n) => RuntimeValue::Number(n.into()),
205            mq_markdown::AttrValue::Integer(n) => RuntimeValue::Number(n.into()),
206            mq_markdown::AttrValue::Boolean(b) => RuntimeValue::Boolean(b),
207            mq_markdown::AttrValue::Array(arr) => {
208                RuntimeValue::Array(arr.into_iter().map(RuntimeValue::from).collect())
209            }
210            mq_markdown::AttrValue::Null => RuntimeValue::NONE,
211        }
212    }
213}
214
215impl From<yaml_rust2::Yaml> for RuntimeValue {
216    fn from(value: yaml_rust2::Yaml) -> Self {
217        match value {
218            yaml_rust2::Yaml::Null | yaml_rust2::Yaml::BadValue => RuntimeValue::NONE,
219            yaml_rust2::Yaml::Boolean(b) => RuntimeValue::Boolean(b),
220            yaml_rust2::Yaml::Integer(i) => RuntimeValue::Number((i as f64).into()),
221            yaml_rust2::Yaml::Real(s) => s
222                .parse::<f64>()
223                .map(|f| RuntimeValue::Number(f.into()))
224                .unwrap_or(RuntimeValue::NONE),
225            yaml_rust2::Yaml::String(s) => RuntimeValue::String(s),
226            yaml_rust2::Yaml::Array(arr) => RuntimeValue::Array(arr.into_iter().map(RuntimeValue::from).collect()),
227            yaml_rust2::Yaml::Hash(map) => {
228                let mut btree = BTreeMap::new();
229                for (k, v) in map {
230                    let key = match k {
231                        yaml_rust2::Yaml::String(s) => s,
232                        other => format!("{other:?}"),
233                    };
234                    btree.insert(Ident::new(&key), RuntimeValue::from(v));
235                }
236                RuntimeValue::Dict(btree)
237            }
238            yaml_rust2::Yaml::Alias(_) => RuntimeValue::NONE,
239        }
240    }
241}
242
243impl From<serde_json::Value> for RuntimeValue {
244    fn from(value: serde_json::Value) -> Self {
245        match value {
246            serde_json::Value::Null => RuntimeValue::NONE,
247            serde_json::Value::Bool(b) => RuntimeValue::Boolean(b),
248            serde_json::Value::Number(n) => {
249                if let Some(f) = n.as_f64() {
250                    RuntimeValue::Number(f.into())
251                } else {
252                    RuntimeValue::Number(0.into())
253                }
254            }
255            serde_json::Value::String(s) => RuntimeValue::String(s),
256            serde_json::Value::Array(arr) => RuntimeValue::Array(arr.into_iter().map(RuntimeValue::from).collect()),
257            serde_json::Value::Object(obj) => {
258                let mut map = BTreeMap::new();
259                for (k, v) in obj {
260                    map.insert(Ident::new(&k), RuntimeValue::from(v));
261                }
262                RuntimeValue::Dict(map)
263            }
264        }
265    }
266}
267
268impl PartialOrd for RuntimeValue {
269    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
270        match (self, other) {
271            (RuntimeValue::Number(a), RuntimeValue::Number(b)) => a.partial_cmp(b),
272            (RuntimeValue::Boolean(a), RuntimeValue::Boolean(b)) => a.partial_cmp(b),
273            (RuntimeValue::String(a), RuntimeValue::String(b)) => a.partial_cmp(b),
274            (RuntimeValue::Symbol(a), RuntimeValue::Symbol(b)) => a.partial_cmp(b),
275            (RuntimeValue::Array(a), RuntimeValue::Array(b)) => a.partial_cmp(b),
276            (RuntimeValue::Markdown(a, _), RuntimeValue::Markdown(b, _)) => {
277                let a = a.to_string();
278                let b = b.to_string();
279                a.to_string().partial_cmp(&b)
280            }
281            (RuntimeValue::Function(a1, b1, _), RuntimeValue::Function(a2, b2, _)) => match a1.partial_cmp(a2) {
282                Some(Ordering::Equal) => b1.partial_cmp(b2),
283                Some(Ordering::Greater) => Some(Ordering::Greater),
284                Some(Ordering::Less) => Some(Ordering::Less),
285                _ => None,
286            },
287            (RuntimeValue::Dict(_), _) => None,
288            (_, RuntimeValue::Dict(_)) => None,
289            (RuntimeValue::Module(a), RuntimeValue::Module(b)) => a.name.partial_cmp(&b.name),
290            (RuntimeValue::Ast(_), _) => None,
291            (_, RuntimeValue::Ast(_)) => None,
292            _ => None,
293        }
294    }
295}
296
297impl std::fmt::Display for RuntimeValue {
298    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
299        let value: Cow<'_, str> = match self {
300            Self::Number(n) => Cow::Owned(n.to_string()),
301            Self::Boolean(b) => Cow::Owned(b.to_string()),
302            Self::String(s) => Cow::Borrowed(s),
303            Self::Symbol(i) => Cow::Owned(format!(":{}", i)),
304            Self::Array(_) => self.string(),
305            Self::Markdown(m, ..) => Cow::Owned(m.to_string()),
306            Self::None => Cow::Borrowed(""),
307            Self::Function(params, ..) => Cow::Owned(format!("function/{}", params.len())),
308            Self::NativeFunction(_) => Cow::Borrowed("native_function"),
309            Self::Dict(_) => self.string(),
310            Self::Module(module_name) => Cow::Owned(format!(r#"module "{}""#, module_name.name)),
311            Self::Ast(node) => Cow::Owned(node.to_code()),
312        };
313        write!(f, "{}", value)
314    }
315}
316
317impl std::fmt::Debug for RuntimeValue {
318    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
319        let v: Cow<'_, str> = match self {
320            Self::None => Cow::Borrowed("None"),
321            Self::String(s) => Cow::Owned(format!("{:?}", s)),
322            Self::Array(arr) => Cow::Owned(format!("{:?}", arr)),
323            a => a.string(),
324        };
325        write!(f, "{}", v)
326    }
327}
328
329impl RuntimeValue {
330    /// An empty array constant.
331    pub const EMPTY_ARRAY: RuntimeValue = Self::Array(Vec::new());
332    /// The boolean `false` value.
333    pub const FALSE: RuntimeValue = Self::Boolean(false);
334    /// The `None` (null) value.
335    pub const NONE: RuntimeValue = Self::None;
336    /// The boolean `true` value.
337    pub const TRUE: RuntimeValue = Self::Boolean(true);
338
339    /// Creates a new empty dictionary.
340    #[inline(always)]
341    pub fn new_dict() -> RuntimeValue {
342        RuntimeValue::Dict(BTreeMap::new())
343    }
344
345    /// Returns the type name of this runtime value as a string.
346    #[inline(always)]
347    pub fn name(&self) -> &str {
348        match self {
349            RuntimeValue::Number(_) => "number",
350            RuntimeValue::Boolean(_) => "bool",
351            RuntimeValue::String(_) => "string",
352            RuntimeValue::Symbol(_) => "symbol",
353            RuntimeValue::Markdown(_, _) => "markdown",
354            RuntimeValue::Array(_) => "array",
355            RuntimeValue::None => "None",
356            RuntimeValue::Function(_, _, _) => "function",
357            RuntimeValue::NativeFunction(_) => "native_function",
358            RuntimeValue::Dict(_) => "dict",
359            RuntimeValue::Module(_) => "module",
360            RuntimeValue::Ast(_) => "ast",
361        }
362    }
363
364    /// Returns `true` if this value is `None`.
365    #[inline(always)]
366    pub fn is_none(&self) -> bool {
367        matches!(self, RuntimeValue::None)
368    }
369
370    /// Returns `true` if this value is a user-defined function.
371    #[inline(always)]
372    pub fn is_function(&self) -> bool {
373        matches!(self, RuntimeValue::Function(_, _, _))
374    }
375
376    /// Returns `true` if this value is a native (built-in) function.
377    #[inline(always)]
378    pub fn is_native_function(&self) -> bool {
379        matches!(self, RuntimeValue::NativeFunction(_))
380    }
381
382    /// Returns `true` if this value is an array.
383    #[inline(always)]
384    pub fn is_array(&self) -> bool {
385        matches!(self, RuntimeValue::Array(_))
386    }
387
388    /// Returns `true` if this value is a dict.
389    #[inline(always)]
390    pub fn is_dict(&self) -> bool {
391        matches!(self, RuntimeValue::Dict(_))
392    }
393
394    /// Returns `true` if this value is considered empty.
395    ///
396    /// Empty values include empty arrays, empty strings, empty markdown nodes,
397    /// empty dictionaries, and `None`.
398    #[inline(always)]
399    pub fn is_empty(&self) -> bool {
400        match self {
401            RuntimeValue::Array(a) => a.is_empty(),
402            RuntimeValue::String(s) => s.is_empty(),
403            RuntimeValue::Markdown(m, _) => m.value().is_empty(),
404            RuntimeValue::Dict(m) => m.is_empty(),
405            RuntimeValue::None => true,
406            _ => false,
407        }
408    }
409
410    /// Returns `true` if this value is considered truthy in conditional contexts.
411    ///
412    /// Truthy values include non-zero numbers, non-empty strings and arrays,
413    /// `true`, functions, symbols, and modules. Falsy values include `false`,
414    /// zero, empty collections, and `None`.
415    #[inline(always)]
416    pub fn is_truthy(&self) -> bool {
417        match self {
418            RuntimeValue::Boolean(b) => *b,
419            RuntimeValue::Number(n) => n.value() != 0.0,
420            RuntimeValue::String(s) => !s.is_empty(),
421            RuntimeValue::Array(a) => !a.is_empty(),
422            RuntimeValue::Markdown(node, selector) => match selector {
423                Some(Selector::Index(i)) => node.find_at_index(*i).is_some(),
424                None => true,
425            },
426            RuntimeValue::Symbol(_)
427            | RuntimeValue::Function(_, _, _)
428            | RuntimeValue::NativeFunction(_)
429            | RuntimeValue::Dict(_) => true,
430            RuntimeValue::Module(_) => true,
431            RuntimeValue::Ast(_) => true,
432            RuntimeValue::None => false,
433        }
434    }
435
436    /// Returns the length of this value.
437    ///
438    /// For numbers, returns the value as `usize`. For strings and arrays, returns
439    /// the number of elements. For dictionaries, returns the number of entries.
440    #[inline(always)]
441    pub fn len(&self) -> usize {
442        match self {
443            RuntimeValue::Number(n) => n.value() as usize,
444            RuntimeValue::Boolean(_) => 1,
445            RuntimeValue::String(s) => s.len(),
446            RuntimeValue::Symbol(i) => i.as_str().len(),
447            RuntimeValue::Array(a) => a.len(),
448            RuntimeValue::Markdown(m, _) => m.value().len(),
449            RuntimeValue::Dict(m) => m.len(),
450            RuntimeValue::None => 0,
451            RuntimeValue::Function(..) => 0,
452            RuntimeValue::Module(m) => m.len(),
453            RuntimeValue::NativeFunction(..) => 0,
454            RuntimeValue::Ast(_) => 0,
455        }
456    }
457
458    /// Extracts the markdown node from this value, if it is a markdown value.
459    ///
460    /// If a selector is present, returns the selected child node.
461    #[inline(always)]
462    pub fn markdown_node(&self) -> Option<Node> {
463        match self {
464            RuntimeValue::Markdown(n, Some(Selector::Index(i))) => n.find_at_index(*i),
465            RuntimeValue::Markdown(n, _) => Some(n.clone()),
466            _ => None,
467        }
468    }
469
470    /// Updates the value of a markdown node, returning a new runtime value.
471    ///
472    /// If this is not a markdown value, returns `None`.
473    #[inline(always)]
474    pub fn update_markdown_value(&self, value: &str) -> RuntimeValue {
475        match self {
476            RuntimeValue::Markdown(n, Some(Selector::Index(i))) => {
477                RuntimeValue::Markdown(n.with_children_value(value, *i), Some(Selector::Index(*i)))
478            }
479            RuntimeValue::Markdown(n, selector) => RuntimeValue::Markdown(n.with_value(value), selector.clone()),
480            _ => RuntimeValue::NONE,
481        }
482    }
483
484    /// Returns the position information for a markdown node, if available.
485    #[inline(always)]
486    pub fn position(&self) -> Option<mq_markdown::Position> {
487        match self {
488            RuntimeValue::Markdown(node, _) => node.position(),
489            _ => None,
490        }
491    }
492
493    /// Sets the position information for a markdown node.
494    ///
495    /// Only affects markdown values; other value types are unaffected.
496    #[inline(always)]
497    pub fn set_position(&mut self, position: Option<mq_markdown::Position>) {
498        if let RuntimeValue::Markdown(node, _) = self {
499            node.set_position(position);
500        }
501    }
502
503    #[inline(always)]
504    fn string(&self) -> Cow<'_, str> {
505        match self {
506            Self::Number(n) => Cow::Owned(n.to_string()),
507            Self::Boolean(b) => Cow::Owned(b.to_string()),
508            Self::String(s) => Cow::Owned(format!(r#""{}""#, s)),
509            Self::Symbol(i) => Cow::Owned(format!(":{}", i)),
510            Self::Array(a) => Cow::Owned(format!(
511                "[{}]",
512                a.iter().map(|v| v.string()).collect::<Vec<Cow<str>>>().join(", ")
513            )),
514            Self::Markdown(m, ..) => Cow::Owned(m.to_string()),
515            Self::None => Cow::Borrowed(""),
516            Self::Function(f, _, _) => Cow::Owned(format!("function/{}", f.len())),
517            Self::NativeFunction(_) => Cow::Borrowed("native_function"),
518            Self::Module(m) => Cow::Owned(format!("module/{}", m.name())),
519            Self::Ast(node) => Cow::Owned(node.to_code()),
520            Self::Dict(map) => {
521                let items = map
522                    .iter()
523                    .map(|(k, v)| format!("\"{}\": {}", k, v.string()))
524                    .collect::<Vec<String>>()
525                    .join(", ");
526                Cow::Owned(format!("{{{}}}", items))
527            }
528        }
529    }
530
531    /// Returns a new runtime value that is the logical negation of this value.
532    pub fn negated(&self) -> Self {
533        match self {
534            RuntimeValue::Boolean(b) => RuntimeValue::Boolean(!b),
535            RuntimeValue::Number(n) => RuntimeValue::Number((-n.value()).into()),
536            RuntimeValue::String(s) => RuntimeValue::String(s.chars().rev().collect()),
537            _ => self.clone(),
538        }
539    }
540}
541
542/// A collection of runtime values.
543///
544/// Provides utilities for working with multiple values, such as filtering
545/// and updating operations.
546#[derive(Debug, Clone, PartialEq)]
547pub struct RuntimeValues(Vec<RuntimeValue>);
548
549impl From<Vec<RuntimeValue>> for RuntimeValues {
550    fn from(values: Vec<RuntimeValue>) -> Self {
551        Self(values)
552    }
553}
554
555impl Index<usize> for RuntimeValues {
556    type Output = RuntimeValue;
557
558    fn index(&self, index: usize) -> &Self::Output {
559        &self.0[index]
560    }
561}
562
563impl IndexMut<usize> for RuntimeValues {
564    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
565        &mut self.0[index]
566    }
567}
568
569impl IntoIterator for RuntimeValues {
570    type IntoIter = std::vec::IntoIter<RuntimeValue>;
571    type Item = RuntimeValue;
572
573    fn into_iter(self) -> Self::IntoIter {
574        self.0.into_iter()
575    }
576}
577
578impl RuntimeValues {
579    /// Returns a compacted version of this collection, removing `None` and empty values.
580    pub fn compact(&self) -> Vec<RuntimeValue> {
581        self.0
582            .iter()
583            .filter(|v| !v.is_none() && !v.is_empty())
584            .cloned()
585            .collect::<Vec<_>>()
586    }
587
588    /// Returns a reference to the underlying vector of values.
589    pub fn values(&self) -> &Vec<RuntimeValue> {
590        &self.0
591    }
592
593    /// Returns the number of values in this collection.
594    pub fn len(&self) -> usize {
595        self.0.len()
596    }
597
598    /// Returns `true` if this collection contains no values.
599    pub fn is_empty(&self) -> bool {
600        self.0.len() == 0
601    }
602
603    /// Updates this collection with values from another collection.
604    ///
605    /// Pairs corresponding elements from both collections and applies special
606    /// update logic for markdown nodes.
607    pub fn update_with(&self, other: Self) -> Self {
608        self.0
609            .clone()
610            .into_iter()
611            .zip(other)
612            .map(|(current_value, mut updated_value)| {
613                updated_value.set_position(current_value.position());
614
615                if let RuntimeValue::Markdown(node, _) = &current_value {
616                    match &updated_value {
617                        RuntimeValue::None
618                        | RuntimeValue::Function(_, _, _)
619                        | RuntimeValue::Module(_)
620                        | RuntimeValue::Ast(_)
621                        | RuntimeValue::NativeFunction(_) => current_value.clone(),
622                        RuntimeValue::Markdown(node, _) if node.is_empty() => current_value.clone(),
623                        RuntimeValue::Markdown(node, _) => {
624                            if node.is_fragment() {
625                                if let RuntimeValue::Markdown(mut current_node, selector) = current_value {
626                                    current_node.apply_fragment(node.clone());
627                                    RuntimeValue::Markdown(current_node, selector)
628                                } else {
629                                    updated_value
630                                }
631                            } else {
632                                updated_value
633                            }
634                        }
635                        RuntimeValue::String(s) => RuntimeValue::Markdown(node.clone().with_value(s), None),
636                        RuntimeValue::Symbol(i) => RuntimeValue::Markdown(node.clone().with_value(&i.as_str()), None),
637                        RuntimeValue::Boolean(b) => {
638                            RuntimeValue::Markdown(node.clone().with_value(b.to_string().as_str()), None)
639                        }
640                        RuntimeValue::Number(n) => {
641                            RuntimeValue::Markdown(node.clone().with_value(n.to_string().as_str()), None)
642                        }
643                        RuntimeValue::Array(array) => RuntimeValue::Array(
644                            array
645                                .iter()
646                                .filter_map(|o| {
647                                    if o.is_none() {
648                                        None
649                                    } else {
650                                        Some(RuntimeValue::Markdown(
651                                            node.clone().with_value(o.to_string().as_str()),
652                                            None,
653                                        ))
654                                    }
655                                })
656                                .collect::<Vec<_>>(),
657                        ),
658                        RuntimeValue::Dict(map) => {
659                            let mut new_dict = BTreeMap::new();
660                            for (k, v) in map {
661                                if !v.is_none() && !v.is_empty() {
662                                    new_dict.insert(
663                                        *k,
664                                        RuntimeValue::Markdown(node.clone().with_value(v.to_string().as_str()), None),
665                                    );
666                                }
667                            }
668                            RuntimeValue::Dict(new_dict)
669                        }
670                    }
671                } else {
672                    updated_value
673                }
674            })
675            .collect::<Vec<_>>()
676            .into()
677    }
678}
679
680#[cfg(test)]
681mod tests {
682    use crate::ast::node::{IdentWithToken, Param};
683    use rstest::rstest;
684    use smallvec::{SmallVec, smallvec};
685
686    use super::*;
687
688    #[test]
689    fn test_runtime_value_from() {
690        assert_eq!(RuntimeValue::from(true), RuntimeValue::Boolean(true));
691        assert_eq!(RuntimeValue::from(false), RuntimeValue::Boolean(false));
692        assert_eq!(
693            RuntimeValue::from(String::from("test")),
694            RuntimeValue::String(String::from("test"))
695        );
696        assert_eq!(
697            RuntimeValue::from(Number::from(42.0)),
698            RuntimeValue::Number(Number::from(42.0))
699        );
700    }
701
702    #[rstest]
703    #[case(RuntimeValue::Number(Number::from(42.0)), "42")]
704    #[case(RuntimeValue::Boolean(true), "true")]
705    #[case(RuntimeValue::Boolean(false), "false")]
706    #[case(RuntimeValue::String("hello".to_string()), r#""hello""#)]
707    #[case(RuntimeValue::None, "")]
708    #[case(RuntimeValue::Array(vec![
709            RuntimeValue::Number(Number::from(1.0)),
710            RuntimeValue::String("test".to_string())
711        ]), r#"[1, "test"]"#)]
712    #[case(RuntimeValue::Dict({
713            let mut map = BTreeMap::new();
714            map.insert(Ident::new("key1"), RuntimeValue::String("value1".to_string()));
715            map.insert(Ident::new("key2"), RuntimeValue::Number(Number::from(42.0)));
716            map
717        }), r#"{"key1": "value1", "key2": 42}"#)]
718    fn test_string_method(#[case] value: RuntimeValue, #[case] expected: &str) {
719        assert_eq!(value.string(), expected);
720    }
721
722    #[test]
723    fn test_runtime_value_display() {
724        assert_eq!(format!("{}", RuntimeValue::Boolean(true)), "true");
725        assert_eq!(format!("{}", RuntimeValue::Number(Number::from(42.0))), "42");
726        assert_eq!(format!("{}", RuntimeValue::String(String::from("test"))), "test");
727        assert_eq!(format!("{}", RuntimeValue::None), "");
728        let map_val = RuntimeValue::Dict(BTreeMap::default());
729        assert_eq!(format!("{}", map_val), "{}");
730    }
731
732    #[test]
733    fn test_runtime_value_debug() {
734        assert_eq!(format!("{:?}", RuntimeValue::Boolean(true)), "true");
735        assert_eq!(format!("{:?}", RuntimeValue::Number(Number::from(42.0))), "42");
736        assert_eq!(format!("{:?}", RuntimeValue::String(String::from("test"))), "\"test\"");
737        assert_eq!(format!("{:?}", RuntimeValue::None), "None");
738
739        let mut map = BTreeMap::default();
740        map.insert(Ident::new("name"), RuntimeValue::String("MQ".to_string()));
741        map.insert(Ident::new("version"), RuntimeValue::Number(Number::from(1.0)));
742        let map_val = RuntimeValue::Dict(map);
743        let debug_str = format!("{:?}", map_val);
744        assert!(debug_str == r#"{"name": "MQ", "version": 1}"# || debug_str == r#"{"version": 1, "name": "MQ"}"#);
745    }
746
747    #[test]
748    fn test_runtime_value_name() {
749        assert_eq!(RuntimeValue::Boolean(true).name(), "bool");
750        assert_eq!(RuntimeValue::Number(Number::from(42.0)).name(), "number");
751        assert_eq!(RuntimeValue::String(String::from("test")).name(), "string");
752        assert_eq!(RuntimeValue::None.name(), "None");
753        assert_eq!(
754            RuntimeValue::Function(
755                SmallVec::new(),
756                Vec::new(),
757                Shared::new(SharedCell::new(Env::default()))
758            )
759            .name(),
760            "function"
761        );
762        assert_eq!(
763            RuntimeValue::NativeFunction(Ident::new("name")).name(),
764            "native_function"
765        );
766        assert_eq!(
767            RuntimeValue::Markdown(
768                mq_markdown::Node::Text(mq_markdown::Text {
769                    value: "".to_string(),
770                    position: None
771                }),
772                None
773            )
774            .name(),
775            "markdown"
776        );
777        assert_eq!(RuntimeValue::Dict(BTreeMap::default()).name(), "dict");
778    }
779
780    #[test]
781    fn test_runtime_value_is_true() {
782        assert!(RuntimeValue::Boolean(true).is_truthy());
783        assert!(!RuntimeValue::Boolean(false).is_truthy());
784        assert!(RuntimeValue::Number(Number::from(42.0)).is_truthy());
785        assert!(!RuntimeValue::Number(Number::from(0.0)).is_truthy());
786        assert!(RuntimeValue::String(String::from("test")).is_truthy());
787        assert!(!RuntimeValue::String(String::from("")).is_truthy());
788        assert!(RuntimeValue::Array(vec!["".to_string().into()]).is_truthy());
789        assert!(!RuntimeValue::Array(Vec::new()).is_truthy());
790        assert!(
791            RuntimeValue::Markdown(
792                mq_markdown::Node::Text(mq_markdown::Text {
793                    value: "".to_string(),
794                    position: None
795                }),
796                None
797            )
798            .is_truthy()
799        );
800        assert!(
801            !RuntimeValue::Markdown(
802                mq_markdown::Node::Text(mq_markdown::Text {
803                    value: "".to_string(),
804                    position: None
805                }),
806                Some(Selector::Index(1))
807            )
808            .is_truthy()
809        );
810        assert!(!RuntimeValue::Array(Vec::new()).is_truthy());
811        assert!(!RuntimeValue::None.is_truthy());
812        assert!(RuntimeValue::NativeFunction(Ident::new("name")).is_truthy());
813        assert!(
814            RuntimeValue::Function(
815                SmallVec::new(),
816                Vec::new(),
817                Shared::new(SharedCell::new(Env::default()))
818            )
819            .is_truthy()
820        );
821        assert!(RuntimeValue::Dict(BTreeMap::default()).is_truthy());
822    }
823
824    #[test]
825    fn test_runtime_value_partial_ord() {
826        assert!(RuntimeValue::Number(Number::from(1.0)) < RuntimeValue::Number(Number::from(2.0)));
827        assert!(RuntimeValue::String(String::from("a")) < RuntimeValue::String(String::from("b")));
828        assert!(RuntimeValue::Array(Vec::new()) < RuntimeValue::Array(vec!["a".to_string().into()]));
829        assert!(
830            RuntimeValue::Markdown(
831                mq_markdown::Node::Text(mq_markdown::Text {
832                    value: "test".to_string(),
833                    position: None
834                }),
835                None
836            ) < RuntimeValue::Markdown(
837                mq_markdown::Node::Text(mq_markdown::Text {
838                    value: "test2".to_string(),
839                    position: None
840                }),
841                None
842            )
843        );
844        assert!(RuntimeValue::Boolean(false) < RuntimeValue::Boolean(true));
845        assert!(
846            RuntimeValue::Function(
847                SmallVec::new(),
848                Vec::new(),
849                Shared::new(SharedCell::new(Env::default()))
850            ) < RuntimeValue::Function(
851                smallvec![Param::new(IdentWithToken::new("test"))],
852                Vec::new(),
853                Shared::new(SharedCell::new(Env::default()))
854            )
855        );
856    }
857
858    #[test]
859    fn test_runtime_value_len() {
860        assert_eq!(RuntimeValue::Number(Number::from(42.0)).len(), 42);
861        assert_eq!(RuntimeValue::String(String::from("test")).len(), 4);
862        assert_eq!(RuntimeValue::Boolean(true).len(), 1);
863        assert_eq!(RuntimeValue::Array(vec![RuntimeValue::None]).len(), 1);
864        assert_eq!(
865            RuntimeValue::Markdown(
866                mq_markdown::Node::Text(mq_markdown::Text {
867                    value: "a".to_string(),
868                    position: None
869                }),
870                None
871            )
872            .len(),
873            1
874        );
875        let mut map = BTreeMap::default();
876        map.insert(Ident::new("a"), RuntimeValue::String("alpha".to_string()));
877        map.insert(Ident::new("b"), RuntimeValue::String("beta".to_string()));
878        assert_eq!(RuntimeValue::Dict(map).len(), 2);
879    }
880
881    #[test]
882    fn test_negated() {
883        assert_eq!(
884            RuntimeValue::Number(Number::from(42.0)).negated(),
885            RuntimeValue::Number(Number::from(-42.0))
886        );
887        assert_eq!(RuntimeValue::Boolean(true).negated(), RuntimeValue::Boolean(false));
888        assert_eq!(RuntimeValue::Boolean(false).negated(), RuntimeValue::Boolean(true));
889    }
890
891    #[test]
892    fn test_runtime_value_debug_output() {
893        let array = RuntimeValue::Array(vec![
894            RuntimeValue::Number(Number::from(1.0)),
895            RuntimeValue::String("hello".to_string()),
896        ]);
897        assert_eq!(format!("{:?}", array), r#"[1, "hello"]"#);
898
899        let node = mq_markdown::Node::Text(mq_markdown::Text {
900            value: "test markdown".to_string(),
901            position: None,
902        });
903        let markdown = RuntimeValue::Markdown(node, None);
904        assert_eq!(format!("{:?}", markdown), "test markdown");
905
906        let function = RuntimeValue::Function(
907            SmallVec::new(),
908            Vec::new(),
909            Shared::new(SharedCell::new(Env::default())),
910        );
911        assert_eq!(format!("{:?}", function), "function/0");
912
913        let native_fn = RuntimeValue::NativeFunction(Ident::new("debug"));
914        assert_eq!(format!("{:?}", native_fn), "native_function");
915
916        let mut map = BTreeMap::default();
917        map.insert(Ident::new("a"), RuntimeValue::String("alpha".to_string()));
918        let map_val = RuntimeValue::Dict(map);
919        assert_eq!(format!("{:?}", map_val), r#"{"a": "alpha"}"#);
920    }
921
922    #[test]
923    fn test_runtime_value_markdown() {
924        let markdown = RuntimeValue::Markdown("test markdown".to_string().into(), None);
925        assert_eq!(markdown.markdown_node().unwrap().value(), "test markdown");
926
927        let updated = markdown.update_markdown_value("updated markdown");
928        match &updated {
929            RuntimeValue::Markdown(node, selector) => {
930                assert_eq!(node.value(), "updated markdown");
931                assert_eq!(*selector, None);
932            }
933            _ => panic!("Expected Markdown variant"),
934        }
935    }
936
937    #[test]
938    fn test_runtime_value_markdown_with_selector() {
939        let child1 = mq_markdown::Node::Text(mq_markdown::Text {
940            value: "child1".to_string(),
941            position: None,
942        });
943        let child2 = mq_markdown::Node::Text(mq_markdown::Text {
944            value: "child2".to_string(),
945            position: None,
946        });
947
948        let parent = mq_markdown::Node::Strong(mq_markdown::Strong {
949            values: vec![child1, child2],
950            position: None,
951        });
952
953        let markdown_with_selector = RuntimeValue::Markdown(parent.clone(), Some(Selector::Index(1)));
954
955        let selected = markdown_with_selector.markdown_node();
956        assert!(selected.is_some());
957        assert_eq!(selected.unwrap().value(), "child2");
958
959        let updated = markdown_with_selector.update_markdown_value("updated child");
960        match &updated {
961            RuntimeValue::Markdown(node, selector) => {
962                assert_eq!(selector, &Some(Selector::Index(1)));
963                assert_eq!(node.find_at_index(1).unwrap().value(), "updated child");
964            }
965            _ => panic!("Expected Markdown variant"),
966        }
967    }
968
969    #[test]
970    fn test_update_markdown_value_non_markdown() {
971        assert_eq!(
972            RuntimeValue::Number(Number::from(42.0)).update_markdown_value("test"),
973            RuntimeValue::NONE
974        );
975        assert_eq!(
976            RuntimeValue::String("hello".to_string()).update_markdown_value("test"),
977            RuntimeValue::NONE
978        );
979        assert_eq!(
980            RuntimeValue::Boolean(true).update_markdown_value("test"),
981            RuntimeValue::NONE
982        );
983        assert_eq!(RuntimeValue::None.update_markdown_value("test"), RuntimeValue::NONE);
984    }
985
986    #[test]
987    fn test_runtime_value_map_creation_and_equality() {
988        let mut map1_data = BTreeMap::default();
989        map1_data.insert(Ident::new("a"), RuntimeValue::Number(Number::from(1.0)));
990        map1_data.insert(Ident::new("b"), RuntimeValue::String("hello".to_string()));
991        let map1 = RuntimeValue::Dict(map1_data);
992
993        let mut map2_data = BTreeMap::default();
994        map2_data.insert(Ident::new("a"), RuntimeValue::Number(Number::from(1.0)));
995        map2_data.insert(Ident::new("b"), RuntimeValue::String("hello".to_string()));
996        let map2 = RuntimeValue::Dict(map2_data);
997
998        let mut map3_data = BTreeMap::default();
999        map3_data.insert(Ident::new("a"), RuntimeValue::Number(Number::from(1.0)));
1000        map3_data.insert(Ident::new("c"), RuntimeValue::String("world".to_string()));
1001        let map3 = RuntimeValue::Dict(map3_data);
1002
1003        assert_eq!(map1, map2);
1004        assert_ne!(map1, map3);
1005    }
1006
1007    #[test]
1008    fn test_runtime_value_map_is_empty() {
1009        let empty_map = RuntimeValue::Dict(BTreeMap::default());
1010        assert!(empty_map.is_empty());
1011
1012        let mut map_data = BTreeMap::default();
1013        map_data.insert(Ident::new("a"), RuntimeValue::Number(Number::from(1.0)));
1014        let non_empty_map = RuntimeValue::Dict(map_data);
1015        assert!(!non_empty_map.is_empty());
1016    }
1017
1018    #[test]
1019    fn test_runtime_value_map_partial_ord() {
1020        let mut map1_data = BTreeMap::default();
1021        map1_data.insert(Ident::new("a"), RuntimeValue::Number(Number::from(1.0)));
1022        let map1 = RuntimeValue::Dict(map1_data);
1023
1024        let mut map2_data = BTreeMap::default();
1025        map2_data.insert(Ident::new("b"), RuntimeValue::Number(Number::from(2.0)));
1026        let map2 = RuntimeValue::Dict(map2_data);
1027
1028        assert_eq!(map1.partial_cmp(&map2), None);
1029        assert_eq!(map2.partial_cmp(&map1), None);
1030        assert_eq!(map1.partial_cmp(&map1), None);
1031
1032        let num_val = RuntimeValue::Number(Number::from(5.0));
1033        assert_eq!(map1.partial_cmp(&num_val), None);
1034        assert_eq!(num_val.partial_cmp(&map1), None);
1035    }
1036}