compose_library/foundations/
value.rs

1use crate::diag::{At, SourceResult, Spanned};
2use crate::foundations::boxed::Boxed;
3use crate::{CastInfo, Str, SyntaxContext, UnitValue};
4use crate::{FromValue, Func};
5use crate::{IntoValue, Vm};
6use crate::{NativeScope, Reflect};
7use crate::{Sink, Type};
8use compose_library::diag::{StrResult, bail, error};
9use compose_library::foundations::range::RangeValue;
10use compose_library::repr::Repr;
11use compose_library::{Args, ArrayValue, Heap, IterValue, MapValue, Module};
12use compose_macros::func;
13use compose_macros::scope;
14use compose_syntax::Span;
15use ecow::{EcoString, eco_format};
16use std::{fmt, iter};
17
18#[derive(Debug, Clone, PartialEq)]
19pub enum Value {
20    Int(i64),
21    Bool(bool),
22    Unit(UnitValue),
23    Str(Str),
24    Func(Func),
25    Type(Type),
26    Iterator(IterValue),
27    Box(Boxed),
28    Array(ArrayValue),
29    Range(RangeValue),
30    Map(MapValue),
31    Module(Module),
32}
33
34#[scope]
35impl Value {
36    #[func(name = "to_string")]
37    pub fn to_string(self, vm: &dyn Vm) -> EcoString {
38        self.repr(vm)
39    }
40
41    #[func(name = "clone")]
42    pub fn shallow_clone(self, vm: &mut dyn Vm) -> StrResult<Self> {
43        Ok(match self {
44            Value::Int(v) => Value::Int(v),
45            Value::Bool(v) => Value::Bool(v),
46            Value::Unit(v) => Value::Unit(v),
47            Value::Str(v) => Value::Str(v),
48            Value::Func(v) => Value::Func(v),
49            Value::Type(v) => Value::Type(v),
50            Value::Iterator(v) => Value::Iterator(v.shallow_clone(vm)),
51            Value::Box(v) => Value::Box(v.shallow_clone(vm)),
52            Value::Array(v) => Value::Array(v.shallow_clone(vm)),
53            Value::Range(v) => Value::Range(v),
54            Value::Map(v) => Value::Map(v.shallow_clone(vm)),
55            Value::Module(v) => Value::Module(v.clone()),
56        })
57    }
58
59    #[func]
60    pub fn tap(self, vm: &mut dyn Vm, side_effect: Func) -> SourceResult<Self> {
61        vm.call_func(
62            &side_effect,
63            Args::new(side_effect.span, iter::once(self.clone())),
64        )?;
65        Ok(self)
66    }
67
68    #[func]
69    pub fn pipe(self, vm: &mut dyn Vm, transform: Func) -> SourceResult<Self> {
70        vm.call_func(&transform, Args::new(transform.span, iter::once(self)))
71    }
72}
73
74impl Value {
75    pub fn is_box(&self) -> bool {
76        matches!(self, Value::Box(_))
77    }
78}
79
80impl Value {
81    pub fn ty(&self) -> Type {
82        match self {
83            Value::Int(_) => Type::of::<i64>(),
84            Value::Bool(_) => Type::of::<bool>(),
85            Value::Unit(_) => Type::of::<UnitValue>(),
86            Value::Str(_) => Type::of::<Str>(),
87            Value::Func(_) => Type::of::<Func>(),
88            Value::Type(_) => Type::of::<Type>(),
89            Value::Iterator(_) => Type::of::<IterValue>(),
90            Value::Box(_) => Type::of::<Boxed>(),
91            Value::Array(_) => Type::of::<ArrayValue>(),
92            Value::Range(_) => Type::of::<RangeValue>(),
93            Value::Map(_) => Type::of::<MapValue>(),
94            Value::Module(_) => Type::of::<Module>(),
95        }
96    }
97
98    pub fn unit() -> Value {
99        Value::Unit(UnitValue)
100    }
101
102    pub fn cast<T: FromValue>(self) -> StrResult<T> {
103        T::from_value(self)
104    }
105
106    pub fn spanned(self, span: Span) -> Self {
107        match self {
108            Value::Func(v) => Value::Func(v.spanned(span)),
109            _ => self,
110        }
111    }
112
113    pub fn named(self, name: Spanned<EcoString>) -> Self {
114        match self {
115            Value::Func(v) => Value::Func(v.named(name)),
116
117            other @ (Value::Int(_)
118            | Value::Bool(_)
119            | Value::Unit(_)
120            | Value::Str(_)
121            | Value::Type(_)
122            | Value::Iterator(_)
123            | Value::Box(_)
124            | Value::Array(_)
125            | Value::Range(_)
126            | Value::Map(_)
127            | Value::Module(_)) => other,
128        }
129    }
130
131    pub fn resolved(self) -> SourceResult<Self> {
132        match self {
133            Value::Func(f) => {
134                f.resolve()?;
135                Ok(Value::Func(f))
136            }
137            _ => Ok(self),
138        }
139    }
140
141    /// Access a field on this value (with dot syntax) `a.b`
142    pub fn field(&self, field: &str, access_span: Span, sink: &mut Sink) -> StrResult<Value> {
143        let field_value = match self {
144            Self::Func(func) => func.field(field, access_span, sink).cloned(),
145            Self::Type(ty) => ty.field(field, access_span, sink).cloned(),
146            _ => Err(error!(
147                "no field or method named `{}` on `{}`",
148                field,
149                self.ty()
150            )),
151        };
152
153        if let Ok(field_value) = field_value {
154            return Ok(field_value);
155        }
156
157        // Get the field from the type
158        let type_field_value = self.ty().field(field, access_span, sink).cloned();
159        if let Ok(type_field_value) = type_field_value {
160            return Ok(type_field_value);
161        }
162
163        // Try and get the type from Value itself
164        let self_field_value = Value::scope()
165            .get(field)
166            .map(|b| b.read_checked(access_span, sink))
167            .cloned();
168
169        match self_field_value {
170            None => bail!("no field or method named `{}` on `{}`", field, self.ty()),
171            Some(v) => Ok(v),
172        }
173    }
174
175    pub fn index(&self, index: Value, target_span: Span, index_span: Span, heap: &Heap) -> SourceResult<Option<Value>> {
176        match self {
177            Value::Array(arr) => arr.index(index, index_span, heap),
178            _ => bail!(target_span, "cannot index into `{}`", self.ty()),
179        }
180    }
181
182    /// Access an associated value of this value by path syntax `a::b`
183    pub fn path(
184        &self,
185        path: &str,
186        access_span: Span,
187        sink: &mut Sink,
188        ctx: &SyntaxContext,
189    ) -> SourceResult<Value> {
190        match self {
191            Self::Type(ty) => ty.path(path, access_span, sink).cloned().at(access_span),
192            Self::Func(func) => func.path(path, access_span, sink).cloned().at(access_span),
193            Self::Module(module) => module.path(path, access_span, sink, ctx).cloned(),
194            _ => bail!(
195                access_span,
196                "no associated field or method named `{}` on `{}`",
197                path,
198                self.ty()
199            ),
200        }
201    }
202    
203}
204
205impl fmt::Display for Value {
206    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207        match self {
208            Value::Int(v) => write!(f, "{}", v),
209            Value::Bool(v) => write!(f, "{}", v),
210            Value::Unit(_) => write!(f, "()"),
211            Value::Str(v) => write!(f, "{}", v),
212            Value::Func(v) => write!(f, "{}", v),
213            Value::Type(v) => write!(f, "{}", v),
214            Value::Iterator(v) => write!(f, "{:?}", v),
215            Value::Box(v) => write!(f, "{}", v),
216            Value::Array(v) => write!(f, "{:?}", v),
217            Value::Range(v) => write!(f, "{:?}", v),
218            Value::Map(v) => write!(f, "{:?}", v),
219            Value::Module(v) => write!(f, "{}", v.name()),
220        }
221    }
222}
223
224impl Repr for Value {
225    fn repr(&self, vm: &dyn Vm) -> EcoString {
226        match self {
227            Value::Int(v) => eco_format!("{v}"),
228            Value::Bool(v) => eco_format!("{v}"),
229            Value::Unit(_) => eco_format!("()"),
230            Value::Str(v) => v.repr(vm),
231            Value::Func(v) => eco_format!("{v}"),
232            Value::Type(v) => eco_format!("{v}"),
233            Value::Iterator(v) => eco_format!("iterator({v:?})"),
234            Value::Box(v) => eco_format!("{v}"),
235            Value::Array(v) => v.repr(vm),
236            Value::Range(r) => r.repr(vm),
237            Value::Map(m) => m.repr(vm),
238            Value::Module(m) => m.name().clone(),
239        }
240    }
241}
242
243impl Default for Value {
244    fn default() -> Self {
245        Value::Unit(UnitValue)
246    }
247}
248
249/// Implements traits for primitives (Value enum variants).
250macro_rules! primitive {
251    (
252        $ty:ty: $name:literal, $variant:ident
253        $(, $other:ident$(($binding:ident))? => $out:expr)*
254    ) => {
255        impl Reflect for $ty {
256            fn input() -> CastInfo {
257                CastInfo::Type(Type::of::<Self>())
258            }
259
260            fn output() -> CastInfo {
261                CastInfo::Type(Type::of::<Self>())
262            }
263
264            fn castable(value: &Value) -> bool {
265                matches!(value, Value::$variant(_)
266                    $(|  primitive!(@$other $(($binding))?))*)
267            }
268        }
269
270        impl IntoValue for $ty {
271            fn into_value(self) -> Value {
272                Value::$variant(self)
273            }
274        }
275
276        impl FromValue for $ty {
277            fn from_value(value: Value) -> StrResult<Self> {
278                match value {
279                    Value::$variant(v) => Ok(v),
280                    $(Value::$other$(($binding))? => Ok($out),)*
281                    v => Err(<Self as Reflect>::error(&v)),
282                }
283            }
284        }
285    };
286
287    (@$other:ident($binding:ident)) => { Value::$other(_) };
288    (@$other:ident) => { Value::$other };
289}
290
291primitive!(i64: "Int", Int);
292primitive!(bool: "Bool", Bool);
293primitive!(Str: "Str", Str);
294primitive!(Func: "Func", Func);
295primitive!(Type: "Type", Type);
296primitive!(UnitValue: "Unit", Unit);
297primitive!(IterValue: "Iterator", Iterator);
298primitive!(Boxed: "Box", Box);
299primitive!(ArrayValue: "Array", Array);
300primitive!(RangeValue: "Range", Range);
301primitive!(MapValue: "Map", Map);
302primitive!(Module: "Module", Module);