rimu_stdlib/
lib.rs

1use std::{cell::RefCell, rc::Rc};
2
3use rimu_eval::call;
4use rimu_meta::{Span, Spanned};
5use rimu_value::{
6    Environment, EvalError, Function, FunctionBody, NativeFunction, SerdeValue, SerdeValueObject,
7    SpannedValue, Value,
8};
9use rust_decimal::prelude::ToPrimitive;
10
11pub fn create_stdlib() -> SerdeValueObject {
12    let mut lib = SerdeValueObject::new();
13    lib.insert("length".into(), length().into());
14    lib.insert("map".into(), map().into());
15    lib.insert("range".into(), range().into());
16    lib
17}
18
19fn empty_env() -> Rc<RefCell<Environment>> {
20    Rc::new(RefCell::new(Environment::new()))
21}
22
23pub fn length() -> Function {
24    let function = |span: Span, args: &[Spanned<Value>]| -> Result<SpannedValue, EvalError> {
25        let (arg, arg_span) = &args[0].clone().take();
26        let value = match arg {
27            Value::List(list) => list.len().into(),
28            Value::String(string) => string.len().into(),
29            _ => {
30                return Err(EvalError::TypeError {
31                    span: arg_span.clone(),
32                    expected: "list | string".into(),
33                    got: arg.clone().into(),
34                })
35            }
36        };
37        Ok(Spanned::new(value, span))
38    };
39    Function {
40        args: vec!["arg".into()],
41        env: empty_env(),
42        body: FunctionBody::Native(NativeFunction::new(function)),
43    }
44}
45pub fn map() -> Function {
46    let function = |span: Span, args: &[Spanned<Value>]| -> Result<SpannedValue, EvalError> {
47        let (arg, arg_span) = &args[0].clone().take();
48        match arg {
49            Value::Object(object) => {
50                let list_arg = object.get("list").map(|a| a.inner());
51                let mapper_arg = object.get("each").map(|a| a.inner());
52                match (list_arg, mapper_arg) {
53                    (Some(Value::List(list)), Some(Value::Function(mapper))) => map_op(
54                        span,
55                        MapOptions {
56                            list: list.clone(),
57                            mapper: mapper.clone(),
58                        },
59                    ),
60                    _ => Err(EvalError::TypeError {
61                        span: arg_span.clone(),
62                        expected: "{ list: list, each: (item) => next }".into(),
63                        got: arg.clone().into(),
64                    }),
65                }
66            }
67            _ => Err(EvalError::TypeError {
68                span: arg_span.clone(),
69                expected: "object".into(),
70                got: arg.clone().into(),
71            }),
72        }
73    };
74
75    Function {
76        args: vec!["arg".into()],
77        env: empty_env(),
78        body: FunctionBody::Native(NativeFunction::new(function)),
79    }
80}
81
82struct MapOptions {
83    list: Vec<SpannedValue>,
84    mapper: Function,
85}
86
87fn map_op(span: Span, options: MapOptions) -> Result<SpannedValue, EvalError> {
88    let MapOptions { list, mapper } = options;
89    let next_list = list
90        .iter()
91        .map(|item| call(span.clone(), mapper.clone(), &[item.clone()]))
92        .collect::<Result<Vec<SpannedValue>, EvalError>>()?;
93    Ok(Spanned::new(Value::List(next_list), span))
94}
95
96pub fn range() -> Function {
97    let function = |span: Span, args: &[Spanned<Value>]| -> Result<SpannedValue, EvalError> {
98        let (arg, arg_span) = &args[0].clone().take();
99        match arg {
100            Value::Object(object) => {
101                let start = object.get("start");
102                let end = object.get("end");
103                let start = start.map(|a| a.clone().take());
104                let end = end.map(|a| a.clone().take());
105                match (start, end) {
106                    (None, None) => Ok(Spanned::new(Value::List(vec![]), span)),
107                    (None, Some((Value::Number(end), end_span))) => {
108                        let end = end.to_usize().ok_or_else(|| EvalError::TypeError {
109                            span: end_span,
110                            expected: "zero or positive integer".into(),
111                            got: SerdeValue::Number(end),
112                        })?;
113                        range_op(span, RangeOptions { start: None, end })
114                    }
115                    (
116                        Some((Value::Number(start), start_span)),
117                        Some((Value::Number(end), end_span)),
118                    ) => {
119                        let start = start.to_usize().ok_or_else(|| EvalError::TypeError {
120                            span: start_span,
121                            expected: "zero or positive integer".into(),
122                            got: SerdeValue::Number(start),
123                        })?;
124                        let end = end.to_usize().ok_or_else(|| EvalError::TypeError {
125                            span: end_span,
126                            expected: "zero or positive integer".into(),
127                            got: SerdeValue::Number(end),
128                        })?;
129                        range_op(
130                            span,
131                            RangeOptions {
132                                start: Some(start),
133                                end,
134                            },
135                        )
136                    }
137                    _ => Err(EvalError::TypeError {
138                        span: arg_span.clone(),
139                        expected: "{ start?: number, end: number }".into(),
140                        got: arg.clone().into(),
141                    }),
142                }
143            }
144            _ => Err(EvalError::TypeError {
145                span: arg_span.clone(),
146                expected: "object".into(),
147                got: arg.clone().into(),
148            }),
149        }
150    };
151
152    Function {
153        args: vec!["arg".into()],
154        env: empty_env(),
155        body: FunctionBody::Native(NativeFunction::new(function)),
156    }
157}
158
159struct RangeOptions {
160    start: Option<usize>,
161    end: usize,
162}
163
164fn range_op(span: Span, options: RangeOptions) -> Result<SpannedValue, EvalError> {
165    let RangeOptions { start, end } = options;
166    let start = start.unwrap_or(0);
167    let list = (start..end).map(Into::into).collect();
168    Ok(SerdeValue::List(list).with_span(span))
169}