1use crate::fromnow::from_now;
2use crate::interpreter::Context;
3use crate::value::{Function, Value};
4use anyhow::Result;
5use lazy_static::lazy_static;
6use std::convert::TryInto;
7
8lazy_static! {
9 pub(crate) static ref BUILTINS: Context<'static> = {
10 let mut builtins = Context::new();
11 builtins.insert("abs", Value::Function(Function::new("abs", abs_builtin)));
12 builtins.insert("str", Value::Function(Function::new("str", str_builtin)));
13 builtins.insert("len", Value::Function(Function::new("len", len_builtin)));
14 builtins.insert("min", Value::Function(Function::new("min", min_builtin)));
15 builtins.insert("max", Value::Function(Function::new("max", max_builtin)));
16 builtins.insert("sqrt", Value::Function(Function::new("sqrt", sqrt_builtin)));
17 builtins.insert("ceil", Value::Function(Function::new("ceil", ceil_builtin)));
18 builtins.insert(
19 "floor",
20 Value::Function(Function::new("floor", floor_builtin)),
21 );
22 builtins.insert(
23 "lowercase",
24 Value::Function(Function::new("lowercase", lowercase_builtin)),
25 );
26 builtins.insert(
27 "uppercase",
28 Value::Function(Function::new("uppercase", uppercase_builtin)),
29 );
30 builtins.insert(
31 "number",
32 Value::Function(Function::new("number", number_builtin)),
33 );
34 builtins.insert(
35 "strip",
36 Value::Function(Function::new("strip", strip_builtin)),
37 );
38 builtins.insert("range", Value::Function(Function::new("range", range_builtin)));
39 builtins.insert(
40 "rstrip",
41 Value::Function(Function::new("rstrip", rstrip_builtin)),
42 );
43 builtins.insert(
44 "lstrip",
45 Value::Function(Function::new("lstrip", lstrip_builtin)),
46 );
47 builtins.insert("join", Value::Function(Function::new("join", join_builtin)));
48 builtins.insert(
49 "split",
50 Value::Function(Function::new("split", split_builtin)),
51 );
52 builtins.insert(
53 "fromNow",
54 Value::Function(Function::new("fromNow", from_now_builtin)),
55 );
56 builtins.insert(
57 "typeof",
58 Value::Function(Function::new("typeof", typeof_builtin)),
59 );
60 builtins.insert(
61 "defined",
62 Value::Function(Function::new("defined", defined_builtin)),
63 );
64 builtins
65 };
66}
67
68fn array_arithmetic<F: Fn(f64, f64) -> f64>(args: &[Value], f: F) -> Result<Value> {
71 let mut res = None;
72 for arg in args {
73 let arg = *arg
74 .as_f64()
75 .ok_or_else(|| interpreter_error!("invalid arguments to builtin: min"))?;
76 if let Some(r) = res {
77 res = Some(f(arg, r));
78 } else {
79 res = Some(arg);
80 }
81 }
82 if let Some(r) = res {
83 Ok(Value::Number(r))
84 } else {
85 Err(interpreter_error!("invalid arguments to builtin: min"))
86 }
87}
88
89fn unary_arithmetic<F: Fn(f64) -> f64>(args: &[Value], op: F) -> Result<Value> {
90 if args.len() != 1 {
91 return Err(interpreter_error!("expected one argument"));
92 }
93 let v = &args[0];
94
95 match v {
96 Value::Number(n) => Ok(Value::Number(op(*n))),
97 _ => Err(interpreter_error!("invalid arguments to builtin")),
98 }
99}
100
101fn unary_string<F: Fn(&str) -> String>(args: &[Value], op: F) -> Result<Value> {
102 if args.len() != 1 {
103 return Err(interpreter_error!("expected one argument"));
104 }
105 let v = &args[0];
106
107 match v {
108 Value::String(s) => Ok(Value::String(op(s.as_ref()))),
109 _ => Err(interpreter_error!("invalid arguments to builtin")),
110 }
111}
112
113fn abs_builtin(_context: &Context, args: &[Value]) -> Result<Value> {
116 unary_arithmetic(args, f64::abs)
117}
118
119fn str_builtin(_context: &Context, args: &[Value]) -> Result<Value> {
120 if args.len() != 1 {
121 return Err(interpreter_error!("str expects one argument"));
122 }
123 let v = &args[0];
124
125 match v {
126 Value::Null | Value::String(_) | Value::Number(_) | Value::Bool(_) => {
127 v.stringify().map(|s| Value::String(s))
128 }
129 _ => Err(interpreter_error!("invalid arguments to builtin: str")),
130 }
131}
132
133fn len_builtin(_context: &Context, args: &[Value]) -> Result<Value> {
134 if args.len() != 1 {
135 return Err(interpreter_error!("len expects one argument"));
136 }
137 let v = &args[0];
138
139 match v {
140 Value::String(s) => Ok(Value::Number(s.chars().count() as f64)),
141 Value::Array(a) => Ok(Value::Number(a.len() as f64)),
142 _ => Err(interpreter_error!("invalid arguments to builtin: len")),
143 }
144}
145
146fn min_builtin(_context: &Context, args: &[Value]) -> Result<Value> {
147 array_arithmetic(args, |a, b| if a < b { a } else { b })
148}
149
150fn max_builtin(_context: &Context, args: &[Value]) -> Result<Value> {
151 array_arithmetic(args, |a, b| if a < b { b } else { a })
152}
153
154fn sqrt_builtin(_context: &Context, args: &[Value]) -> Result<Value> {
155 unary_arithmetic(args, f64::sqrt)
156}
157
158fn ceil_builtin(_context: &Context, args: &[Value]) -> Result<Value> {
159 unary_arithmetic(args, f64::ceil)
160}
161
162fn floor_builtin(_context: &Context, args: &[Value]) -> Result<Value> {
163 unary_arithmetic(args, f64::floor)
164}
165
166fn lowercase_builtin(_context: &Context, args: &[Value]) -> Result<Value> {
167 unary_string(args, str::to_lowercase)
168}
169
170fn uppercase_builtin(_context: &Context, args: &[Value]) -> Result<Value> {
171 unary_string(args, str::to_uppercase)
172}
173
174fn number_builtin(_context: &Context, args: &[Value]) -> Result<Value> {
175 if args.len() != 1 {
176 return Err(interpreter_error!("number expects one argument"));
177 }
178 let v = &args[0];
179 let num: f64 = match v {
180 Value::String(s) => match s.parse() {
181 Ok(num) => num,
182 Err(_) => return Err(interpreter_error!("string can't be converted to number")),
183 },
184 _ => return Err(interpreter_error!("invalid arguments to builtin: number")),
185 };
186 Ok(Value::Number(num))
187}
188
189fn strip_builtin(_context: &Context, args: &[Value]) -> Result<Value> {
190 unary_string(args, |s| str::trim(s).to_owned())
191}
192
193fn range_builtin(_context: &Context, args: &[Value]) -> Result<Value> {
194 if args.len() < 2 || args.len() > 3 {
195 return Err(interpreter_error!("range requires two arguments and optionally supports a third"));
196 }
197 let start = &args[0];
198 let start: i64 = match start {
199 Value::Number(n) if n.fract() == 0.0 => n.round() as i64,
200 _ => return Err(interpreter_error!("invalid arguments to builtin: range")),
201 };
202 let stop = &args[1];
203 let stop: i64 = match stop {
204 Value::Number(n) if n.fract() == 0.0 => n.round() as i64,
205 _ => return Err(interpreter_error!("invalid arguments to builtin: range")),
206 };
207 let step: i64 = match args.get(2) {
208 None => 1,
210 Some(val) => match val {
211 Value::Number(n) if n.fract() == 0.0 => n.round() as i64,
212 _ => return Err(interpreter_error!("invalid arguments to builtin: range")),
213 }
214 };
215
216 if step > 0 {
217 let step: usize = step.try_into()?;
218 let range = (start..stop).step_by(step).map(|i| Value::Number(i as f64)).collect();
219 Ok(Value::Array(range))
220 } else if step < 0 {
221 let step: usize = (step * -1).try_into()?;
222 let range = (stop+1..=start).rev().step_by(step).map(|i| Value::Number(i as f64)).collect();
223 Ok(Value::Array(range))
224 } else {
225 return Err(interpreter_error!("invalid argument `step` to builtin: range"));
226 }
227}
228
229fn rstrip_builtin(_context: &Context, args: &[Value]) -> Result<Value> {
230 unary_string(args, |s| str::trim_end(s).to_owned())
231}
232
233fn lstrip_builtin(_context: &Context, args: &[Value]) -> Result<Value> {
234 unary_string(args, |s| str::trim_start(s).to_owned())
235}
236
237fn join_builtin(_context: &Context, args: &[Value]) -> Result<Value> {
238 if args.len() != 2 {
239 return Err(interpreter_error!("join expects two arguments"));
240 }
241 let v = &args[0];
242 let sep = &args[1];
243
244 let sep = match sep.stringify() {
245 Ok(s) => s,
246 Err(_) => return Err(interpreter_error!("invalid separator for split")),
247 };
248
249 match v {
250 Value::Array(v) => {
251 let strings: Result<Vec<String>> = v.into_iter().map(|val| val.stringify()).collect();
252 match strings {
253 Ok(s) => Ok(Value::String(s.join(&sep))),
254 Err(_) => Err(interpreter_error!(
255 "BuiltinError: invalid arguments to builtin: join"
256 )),
257 }
258 }
259 _ => Err(interpreter_error!(
260 "BuiltinError: invalid arguments to builtin: join"
261 )),
262 }
263}
264
265fn split_builtin(_context: &Context, args: &[Value]) -> Result<Value> {
266 if args.len() != 2 {
267 return Err(interpreter_error!("split expects two arguments"));
268 }
269 let v = &args[0];
270 let sep = &args[1];
271
272 let sep = match sep.stringify() {
273 Ok(s) => s,
274 Err(_) => return Err(interpreter_error!("invalid separator for split")),
275 };
276
277 match v {
278 Value::String(s) => {
279 if s.is_empty() {
280 return Ok(Value::Array(vec![Value::String("".to_string())]));
281 };
282 let strings = s
283 .split(&sep)
284 .filter(|v| !v.is_empty())
285 .map(|v| Value::String(v.to_string()))
286 .collect();
287 Ok(Value::Array(strings))
288 }
289 _ => Err(interpreter_error!(
290 "BuiltinError: invalid arguments to builtin: split"
291 )),
292 }
293}
294
295fn from_now_builtin(context: &Context, args: &[Value]) -> Result<Value> {
296 if args.len() != 1 && args.len() != 2 {
297 return Err(interpreter_error!("from_now expects one or two arguments"));
298 }
299
300 let v = &args[0];
301
302 let reference = if args.len() == 2 {
303 match &args[1] {
304 Value::String(s) => s.to_owned(),
305 _ => {
306 return Err(interpreter_error!(
307 "BuiltinError: invalid arguments to builtin: fromNow"
308 ))
309 }
310 }
311 } else {
312 match context.get("now") {
313 None => unreachable!(), Some(Value::String(s)) => s.to_owned(),
315 _ => return Err(interpreter_error!("context value `now` must be a string")),
316 }
317 };
318
319 match v {
320 Value::String(s) => Ok(Value::String(from_now(&s, &reference)?)),
321 _ => Err(interpreter_error!(
322 "BuiltinError: invalid arguments to builtin: fromNow"
323 )),
324 }
325}
326
327fn typeof_builtin(_context: &Context, args: &[Value]) -> Result<Value> {
328 if args.len() != 1 {
329 return Err(interpreter_error!("typeof expects one argument"));
330 }
331
332 let v = &args[0];
333
334 let type_ = match v {
335 Value::String(_) => "string",
336 Value::Number(_) => "number",
337 Value::Bool(_) => "boolean",
338 Value::Array(_) => "array",
339 Value::Object(_) => "object",
340 Value::Null => "null",
341 Value::Function(_) => "function",
342 _ => {
343 return Err(interpreter_error!(
344 "BuiltinError: invalid arguments to builtin: split"
345 ))
346 }
347 };
348
349 Ok(Value::String(type_.to_string()))
350}
351
352fn defined_builtin(context: &Context, args: &[Value]) -> Result<Value> {
353 if args.len() != 1 {
354 return Err(interpreter_error!("builtin expects one argument"));
355 }
356 let v = &args[0];
357
358 match v {
359 Value::String(s) => Ok(Value::Bool(context.get(s).is_some())),
360 _ => Err(interpreter_error!(
361 "BuiltinError: invalid arguments to builtin: split"
362 )),
363 }
364}