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}