sylt_std/
sylt.rs

1use crate as sylt_std;
2
3use owo_colors::OwoColorize;
4use std::collections::HashMap;
5use std::cell::RefCell;
6use std::rc::Rc;
7use sungod::Ra;
8use sylt_common::error::RuntimeError;
9use sylt_common::{Blob, RuntimeContext, Type, Value};
10
11#[sylt_macro::sylt_doc(dbg, "Writes the type and value of anything you enter", [One(Value(val))] Type::Void)]
12#[sylt_macro::sylt_link(dbg, "sylt_std::sylt")]
13pub fn dbg<'t>(ctx: RuntimeContext<'t>) -> Result<Value, RuntimeError> {
14    let values = ctx.machine.stack_from_base(ctx.stack_base);
15    println!(
16        "{}: {:?}, {:?}",
17        "DBG".purple(),
18        values.iter().map(Type::from).collect::<Vec<_>>(),
19        values
20    );
21    Ok(Value::Nil)
22}
23
24#[sylt_macro::sylt_doc(random_choice, "Selects an element randomly from a list", [One(Value(list))] Type::Unknown)]
25#[sylt_macro::sylt_link(random_choice, "sylt_std::sylt")]
26pub fn random_choice<'t>(ctx: RuntimeContext<'t>) -> Result<Value, RuntimeError> {
27    let values = ctx.machine.stack_from_base(ctx.stack_base);
28    match values.as_ref() {
29        [Value::List(list)] => {
30            return Ok(list.borrow()[Ra::ggen::<usize>() % list.borrow().len()].clone());
31        }
32        _ => {}
33    }
34
35    return Err(RuntimeError::ExternTypeMismatch(
36        "random_choice".to_string(),
37        values.iter().map(Type::from).collect(),
38    ));
39}
40
41#[sylt_macro::sylt_link(for_each, "sylt_std::sylt")]
42pub fn for_each(ctx: RuntimeContext<'_>) -> Result<Value, RuntimeError> {
43    let values = ctx.machine.stack_from_base(ctx.stack_base);
44    match values.as_ref() {
45        [Value::List(list), callable] => {
46            let list = Rc::clone(list);
47            let callable = callable.clone();
48            for element in list.borrow().iter() {
49                ctx.machine.eval_call(callable.clone(), &[element]).unwrap();
50            }
51            return Ok(Value::Nil);
52        }
53        [Value::Dict(dict), callable] => {
54            let dict = Rc::clone(dict);
55            let callable = callable.clone();
56            for (key, value) in dict.borrow().iter() {
57                ctx.machine.eval_call(callable.clone(), &[key, value]).unwrap();
58            }
59            return Ok(Value::Nil);
60        }
61        _ => {}
62    }
63
64    return Err(RuntimeError::ExternTypeMismatch(
65        "for_each".to_string(),
66        values.iter().map(Type::from).collect(),
67    ));
68}
69
70#[sylt_macro::sylt_link(map, "sylt_std::sylt")]
71pub fn map(ctx: RuntimeContext<'_>) -> Result<Value, RuntimeError> {
72    let values = ctx.machine.stack_from_base(ctx.stack_base);
73    match values.as_ref() {
74        [Value::List(list), callable] => {
75            let list = Rc::clone(list);
76            let callable = callable.clone();
77            let mapped = list
78                .borrow()
79                .iter()
80                .map(|element| ctx.machine.eval_call(callable.clone(), &[element]).unwrap())
81                .collect();
82            return Ok(Value::List(Rc::new(RefCell::new(mapped))));
83        }
84        _ => {}
85    }
86
87    return Err(RuntimeError::ExternTypeMismatch(
88        "map".to_string(),
89        values.iter().map(Type::from).collect(),
90    ));
91}
92
93#[sylt_macro::sylt_link(filter, "sylt_std::sylt")]
94pub fn filter(ctx: RuntimeContext<'_>) -> Result<Value, RuntimeError> {
95    let values = ctx.machine.stack_from_base(ctx.stack_base);
96    match values.as_ref() {
97        [Value::List(list), callable] => {
98            let list = Rc::clone(list);
99            let callable = callable.clone();
100            let filtered = list
101                .borrow()
102                .iter()
103                .filter(|element| ctx.machine.eval_call(callable.clone(), &[element]).unwrap() == Value::Bool(true))
104                .map(Value::clone)
105                .collect();
106            return Ok(Value::List(Rc::new(RefCell::new(filtered))));
107        }
108        [Value::Dict(dict), callable] => {
109            let dict = Rc::clone(dict);
110            let callable = callable.clone();
111            let filtered = dict
112                .borrow()
113                .iter()
114                .filter(|(key, value)| ctx.machine.eval_call(callable.clone(), &[key, value]).unwrap() == Value::Bool(true))
115                // We can't .cloned() since we need the inner values cloned, not the outer tuple
116                .map(|(key, value)| (key.clone(), value.clone()))
117                .collect();
118            return Ok(Value::Dict(Rc::new(RefCell::new(filtered))));
119        }
120        _ => {}
121    }
122
123    return Err(RuntimeError::ExternTypeMismatch(
124        "filter".to_string(),
125        values.iter().map(Type::from).collect(),
126    ));
127}
128
129#[sylt_macro::sylt_doc(args, "Returns the args parsed into a dict, split on =",
130  [] Type::Dict(Box::new(Type::String), Box::new(Type::String)))]
131#[sylt_macro::sylt_link(args, "sylt_std::sylt")]
132pub fn args<'t>(ctx: RuntimeContext<'t>) -> Result<Value, RuntimeError> {
133    if ctx.typecheck {
134        Ok(Value::from(Type::Dict(Box::new(Type::String), Box::new(Type::String))))
135    } else {
136        let mut args = HashMap::new();
137        args.insert(Value::from("prog"), Value::from(ctx.machine.args()[0].as_str()));
138
139        for arg in ctx.machine.args().iter().skip(1) {
140            let (pre, suf) = arg.split_once("=").unwrap_or((arg.as_str(), ""));
141            args.insert(Value::from(pre), Value::from(suf));
142        }
143        Ok(Value::Dict(Rc::new(RefCell::new(args))))
144    }
145}
146
147
148#[sylt_macro::sylt_doc(push, "Appends an element to the end of a list",
149  [One(List(ls)), One(Value(val))] Type::Void)]
150#[sylt_macro::sylt_link(push, "sylt_std::sylt")]
151pub fn push<'t>(ctx: RuntimeContext<'t>) -> Result<Value, RuntimeError> {
152    let values = ctx.machine.stack_from_base(ctx.stack_base);
153    match (values.as_ref(), ctx.typecheck) {
154        ([Value::List(ls), v], true) => {
155            let ls = ls.borrow();
156            assert!(ls.len() == 1);
157            let ls = Type::from(&ls[0]);
158            let v = Type::from(&*v);
159            if ls.fits(&v, &ctx.machine.blobs()).is_ok() || matches!(ls, Type::Unknown) {
160                Ok(Value::Nil)
161            } else {
162                Err(RuntimeError::TypeMismatch(ls, v))
163            }
164        }
165        ([Value::List(ls), v], false) => {
166            // NOTE(ed): Deliberately no type checking.
167            ls.borrow_mut().push(v.clone());
168            Ok(Value::Nil)
169        }
170        (values, _) => Err(RuntimeError::ExternTypeMismatch(
171            "push".to_string(),
172            values.iter().map(Type::from).collect(),
173        )),
174    }
175}
176
177#[sylt_macro::sylt_doc(add, "Inserts a value into a set",
178  [One(Set(ls)), One(Value(val))] Type::Void)]
179#[sylt_macro::sylt_link(add, "sylt_std::sylt")]
180pub fn add<'t>(ctx: RuntimeContext<'t>) -> Result<Value, RuntimeError> {
181    let values = ctx.machine.stack_from_base(ctx.stack_base);
182    match (values.as_ref(), ctx.typecheck) {
183        ([Value::Set(ls), v], true) => {
184            let ls = Type::from(Value::Set(ls.clone()));
185            let ty = if let Type::Set(ty) = &ls {
186                ty
187            } else {
188                unreachable!()
189            };
190            let v = Type::from(&*v);
191            if ty.fits(&v, &ctx.machine.blobs()).is_ok() || matches!(ls, Type::Unknown) {
192                Ok(Value::Nil)
193            } else {
194                Err(RuntimeError::TypeMismatch(ls, v))
195            }
196        }
197        ([Value::Set(ls), v], false) => {
198            // NOTE(ed): Deliberately no type checking.
199            ls.borrow_mut().insert(v.clone());
200            Ok(Value::Nil)
201        }
202        (values, _) => Err(RuntimeError::ExternTypeMismatch(
203            "add".to_string(),
204            values.iter().map(Type::from).collect(),
205        )),
206    }
207}
208
209#[sylt_macro::sylt_doc(clear, "Removes all elements from the list", [One(List(ls))] Type::Void)]
210#[sylt_macro::sylt_link(clear, "sylt_std::sylt")]
211pub fn clear<'t>(ctx: RuntimeContext<'t>) -> Result<Value, RuntimeError> {
212    let values = ctx.machine.stack_from_base(ctx.stack_base);
213    match (values.as_ref(), ctx.typecheck) {
214        ([Value::List(ls)], _) => {
215            ls.borrow_mut().clear();
216            Ok(Value::Nil)
217        }
218        (values, _) => Err(RuntimeError::ExternTypeMismatch(
219            "empty".to_string(),
220            values.iter().map(Type::from).collect(),
221        )),
222    }
223}
224
225#[sylt_macro::sylt_doc(prepend, "Adds an element to the start of a list", [One(List(ls)), One(Value(val))] Type::Void)]
226#[sylt_macro::sylt_link(prepend, "sylt_std::sylt")]
227pub fn prepend<'t>(ctx: RuntimeContext<'t>) -> Result<Value, RuntimeError> {
228    let values = ctx.machine.stack_from_base(ctx.stack_base);
229    match (values.as_ref(), ctx.typecheck) {
230        ([Value::List(ls), v], true) => {
231            let ls = &ls.borrow();
232            assert!(ls.len() == 1);
233            let ls = Type::from(&ls[0]);
234            let v: Type = Type::from(&*v);
235            if ls.fits(&v, ctx.machine.blobs()).is_ok() {
236                Ok(Value::Nil)
237            } else {
238                Err(RuntimeError::TypeMismatch(ls, v))
239            }
240        }
241        ([Value::List(ls), v], false) => {
242            // NOTE(ed): Deliberately no type checking.
243            ls.borrow_mut().insert(0, v.clone());
244            Ok(Value::Nil)
245        }
246        (values, _) => Err(RuntimeError::ExternTypeMismatch(
247            "prepend".to_string(),
248            values.iter().map(Type::from).collect(),
249        )),
250    }
251}
252
253#[sylt_macro::sylt_doc(len, "Gives the length of tuples and lists", [One(Tuple(ls))] Type::Int, [One(List(ls))] Type::Int)]
254#[sylt_macro::sylt_link(len, "sylt_std::sylt")]
255pub fn len<'t>(ctx: RuntimeContext) -> Result<Value, RuntimeError> {
256    let values = ctx.machine.stack_from_base(ctx.stack_base);
257    match values.as_ref() {
258        [Value::Tuple(ls)] => Ok(Value::Int(ls.len() as i64)),
259        [Value::List(ls)] => Ok(Value::Int(ls.borrow().len() as i64)),
260        [Value::Dict(dict)] => Ok(Value::Int(dict.borrow().len() as i64)),
261        values => Err(RuntimeError::ExternTypeMismatch(
262            "len".to_string(),
263            values.iter().map(Type::from).collect(),
264        )),
265    }
266}
267
268sylt_macro::extern_function!(
269    "sylt_std::sylt"
270    atan2
271    ""
272    [One(Float(x)), One(Float(y))] -> Type::Float => {
273        Ok(Float(y.atan2(*x)))
274    },
275);
276
277sylt_macro::extern_function!(
278    "sylt_std::sylt"
279    sin
280    "The sine function you know and love from trigonometry class"
281    [One(Float(t))] -> Type::Float => {
282        Ok(Float(t.sin()))
283    },
284);
285
286sylt_macro::extern_function!(
287    "sylt_std::sylt"
288    cos
289    "The cosine function you know and love from trigonometry class"
290    [One(Float(t))] -> Type::Float => {
291        Ok(Float(t.cos()))
292    },
293);
294
295sylt_macro::extern_function!(
296    "sylt_std::sylt"
297    as_float
298    "Converts the int to a float"
299    [One(Int(t))] -> Type::Float => {
300        Ok(Float(*t as f64))
301    },
302    [Two(Int(t), Int(u))] -> Type::Tuple(vec![Type::Float, Type::Float]) => {
303        Ok(Tuple(Rc::new(vec![Float(*t as f64), Float(*u as f64)])))
304    },
305);
306
307sylt_macro::extern_function!(
308    "sylt_std::sylt"
309    as_int
310    "Converts something to an int"
311    [One(Float(t))] -> Type::Int => {
312        Ok(Int(*t as i64))
313    },
314);
315
316sylt_macro::extern_function!(
317    "sylt_std::sylt"
318    as_char
319    "Converts a string containing a single char to an int"
320    [One(String(s))] -> Type::Int => {
321        let mut chars = s.chars();
322        let c = match chars.next() {
323            Some(c) => c,
324            //TODO(gu): Actually what went wrong
325            None => return Err(RuntimeError::ExternTypeMismatch("as_char".to_string(), vec![Type::String])),
326        };
327        if chars.next().is_none() {
328            Ok(Int(c as i64))
329        } else {
330            //TODO(gu): Actually what went wrong
331            Err(RuntimeError::ExternTypeMismatch("as_char".to_string(), vec![Type::String]))
332        }
333    },
334);
335
336sylt_macro::extern_function!(
337    "sylt_std::sylt"
338    floor
339    "Rounds a float down (towards -inf)"
340    [One(Float(t))] -> Type::Int => {
341        Ok(Int(t.floor() as i64))
342    },
343);
344
345sylt_macro::extern_function!(
346    "sylt_std::sylt"
347    as_chars
348    "Converts an ASCII string into a list of chars. Non-ASCII is converted to '?'."
349    [One(String(s))] -> Type::List(Box::new(Type::Int)) => {
350        let chars = s
351            .chars()
352            .map(|c|
353                if c.is_ascii()
354                    || c == 'å'
355                    || c == 'ä'
356                    || c == 'ö'
357                {
358                    c
359                } else {
360                    '?'
361                } as i64
362            )
363            .map(Value::Int)
364            .collect();
365
366        Ok(Value::List(Rc::new(RefCell::new(chars))))
367    },
368);
369
370sylt_macro::extern_function!(
371    "sylt_std::sylt"
372    as_str
373    "Converts to a string representation"
374    [One(Int(i))] -> Type::String => {
375        Ok(Value::String(Rc::new(i.to_string())))
376    },
377);
378
379sylt_macro::extern_function!(
380    "sylt_std::sylt"
381    sqrt
382    "Returns the square root"
383    [One(Float(x))] -> Type::Float => {
384        Ok(Float(x.sqrt()))
385    },
386);
387
388sylt_macro::extern_function!(
389    "sylt_std::sylt"
390    abs
391    "Returns the absolute value"
392    [One(Float(x))] -> Type::Float => {
393        Ok(Float(x.abs()))
394    },
395);
396
397sylt_macro::extern_function!(
398    "sylt_std::sylt"
399    sign
400    "Returns the sign of the value"
401    [One(Float(x))] -> Type::Float => {
402        Ok(Float(x.signum()))
403    },
404    [One(Int(x))] -> Type::Float => {
405        Ok(Int(x.signum()))
406    },
407);
408
409sylt_macro::extern_function!(
410    "sylt_std::sylt"
411    clamp
412    "Clamps the value 'a' between 'lo' and 'hi'"
413    [One(Float(a)), One(Float(lo)), One(Float(hi))] -> Type::Float => {
414        Ok(Float(a.min(*hi).max(*lo)))
415    },
416    [One(Int(a)), One(Int(lo)), One(Int(hi))] -> Type::Int => {
417        Ok(Int(*a.min(hi).max(lo))) //TODO Other borrows than above
418    },
419);
420
421sylt_macro::extern_function!(
422    "sylt_std::sylt"
423    min
424    "Returns the smallest"
425    [One(Float(a)), One(Float(b))] -> Type::Float => {
426        Ok(Float(a.min(*b)))
427    },
428);
429
430sylt_macro::extern_function!(
431    "sylt_std::sylt"
432    max
433    "Returns the largest"
434    [One(Float(a)), One(Float(b))] -> Type::Float => {
435        Ok(Float(a.max(*b)))
436    },
437);
438
439sylt_macro::extern_function!(
440    "sylt_std::sylt"
441    rem
442    "Returns the value x modulo y"
443    [One(Float(x)), One(Float(y))] -> Type::Float => {
444        Ok(Float(x.rem_euclid(*y)))
445    },
446    [One(Int(x)), One(Int(y))] -> Type::Int => {
447        Ok(Int(x.rem_euclid(*y)))
448    },
449);
450
451sylt_macro::extern_function!(
452    "sylt_std::sylt"
453    pow
454    "Raises the first argument to the power of the second argument"
455    [One(Float(x)), One(Float(y))] -> Type::Float => {
456        Ok(Float(x.powf(*y)))
457    },
458);
459
460sylt_macro::extern_function!(
461    "sylt_std::sylt"
462    angle
463    "Calculates the angle of a 2d vector"
464    [Two(Float(x), Float(y))] -> Type::Float => {
465        Ok(Float(y.atan2(*x)))
466    },
467);
468
469#[sylt_macro::sylt_doc(magnitude_squared, "Calculates the squared magnitude of the tuple as a vector", [Tuple(Float)] Type::Float)]
470#[sylt_macro::sylt_link(magnitude_squared, "sylt_std::sylt")]
471pub fn magnitude_squared<'t>(ctx: RuntimeContext<'t>) -> Result<Value, RuntimeError> {
472    let values = ctx.machine.stack_from_base(ctx.stack_base);
473    match (values.as_ref(), ctx.typecheck) {
474        ([Value::Tuple(ls)], true) => {
475            for value in ls.iter() {
476                if Type::from(value) != Type::Float {
477                    return Err(RuntimeError::ExternTypeMismatch(
478                            "magnitude_squared".to_string(),
479                            values.iter().map(Type::from).collect(),
480                    ));
481                }
482            }
483            Ok(Value::from(Type::Float))
484        }
485        ([Value::Tuple(ls)], false) => {
486            let mut sum = 0.0;
487            for value in ls.iter() {
488                if let Value::Float(value) = value {
489                    sum += value * value;
490                } else {
491                    return Err(RuntimeError::ExternTypeMismatch(
492                            "magnitude_squared".to_string(),
493                            values.iter().map(Type::from).collect(),
494                    ));
495                }
496            }
497            Ok(Value::Float(sum))
498        }
499        (values, _) => Err(RuntimeError::ExternTypeMismatch(
500            "magnitude_squared".to_string(),
501            values.iter().map(Type::from).collect(),
502        )),
503    }
504}
505
506#[sylt_macro::sylt_doc(magnitude, "Calculates the squared magnitude of the tuple as a vector", [Tuple(Float)] Type::Float)]
507#[sylt_macro::sylt_link(magnitude, "sylt_std::sylt")]
508pub fn magnitude<'t>(ctx: RuntimeContext<'t>) -> Result<Value, RuntimeError> {
509    if let Value::Float(mag) = magnitude_squared(ctx)? {
510        Ok(Value::Float(mag.abs().sqrt()))
511    } else {
512        unreachable!();
513    }
514}
515
516sylt_macro::extern_function!(
517    "sylt_std::sylt"
518    normalize
519    "Returns a unit length vector pointing in the same direction."
520    [Two(Float(x), Float(y))] -> Type::Tuple(vec![Type::Float, Type::Float]) => {
521        let length = (x * x + y * y).sqrt();
522        let (x, y) = if length != 0.0 {
523            (x / length, y / length)
524        } else {
525            (*x, *y)
526        };
527        Ok(Tuple(Rc::new(vec![Float(x), Float(y)])))
528    },
529);
530
531sylt_macro::extern_function!(
532    "sylt_std::sylt"
533    reflect
534    "Flips the component of 'v' that points towards 'n'"
535    [Two(Float(vx), Float(vy)), Two(Float(nx), Float(ny))]
536    -> Type::Tuple(vec![Type::Float, Type::Float]) => {
537        let s = 2.0 * (vx * nx + vy * ny);
538        Ok(Tuple(Rc::new(vec![Float(vx - s * nx), Float(vy - s * ny)])))
539    },
540);
541
542sylt_macro::extern_function!(
543    "sylt_std::sylt"
544    dot
545    "Computes the scalar product"
546    [One(Float(a)), One(Float(b))] -> Type::Float => {
547        Ok(Float(a * b))
548    },
549    [Two(Float(ax), Float(ay)), Two(Float(bx), Float(by))] -> Type::Float => {
550        Ok(Float(ax * bx + ay * by))
551    },
552    [Three(Float(ax), Float(ay), Float(az)), Three(Float(bx), Float(by), Float(bz))] -> Type::Float => {
553        Ok(Float(ax * bx + ay * by + az * bz))
554    },
555);
556
557sylt_macro::extern_function!(
558    "sylt_std::sylt"
559    debug_assertions
560    "Whether the sylt runtime was compiled with debug assertions or not."
561    [] -> Type::Bool => {
562        Ok(Bool(cfg!(debug_assertions)))
563    },
564);
565
566pub fn union_type<'t>(a: Type, b: Type, blobs: &[Blob]) -> Type {
567    if a.fits(&b, blobs).is_ok() {
568        a
569    } else if b.fits(&a, blobs).is_ok() {
570        b
571    } else {
572        match (a, b) {
573            (Type::Union(a), Type::Union(b)) => Type::Union(a.union(&b).cloned().collect()),
574            (b, Type::Union(a)) | (Type::Union(a), b) => {
575                let mut a = a.clone();
576                a.insert(b.clone());
577                Type::Union(a)
578            }
579            (a, b) => Type::Union([a, b].iter().cloned().collect()),
580        }
581    }
582}
583
584#[sylt_macro::sylt_doc(pop, "Removes the last element in the list, and returns it", [One(List(l))] Type::Value)]
585#[sylt_macro::sylt_link(pop, "sylt_std::sylt")]
586pub fn pop<'t>(ctx: RuntimeContext<'t>) -> Result<Value, RuntimeError> {
587    let values = ctx.machine.stack_from_base(ctx.stack_base);
588    match (values.as_ref(), ctx.typecheck) {
589        ([Value::List(ls)], true) => {
590            let ls = &ls.borrow();
591            // TODO(ed): Write correct typing
592            let ls = Type::from(&ls[0]);
593            let ret = union_type(ls, Type::Void, ctx.machine.blobs());
594            Ok(Value::from(ret))
595        }
596        ([Value::List(ls)], false) => {
597            // NOTE(ed): Deliberately no type checking.
598            let last = ls.borrow_mut().pop().unwrap_or(Value::Nil);
599            Ok(last)
600        }
601        (values, _) => Err(RuntimeError::ExternTypeMismatch(
602            "pop".to_string(),
603            values.iter().map(Type::from).collect(),
604        )),
605    }
606}
607
608#[sylt_macro::sylt_link(last, "sylt_std::sylt")]
609pub fn last(ctx: RuntimeContext<'_>) -> Result<Value, RuntimeError> {
610    let values = ctx.machine.stack_from_base(ctx.stack_base);
611    match (values.as_ref(), ctx.typecheck) {
612        ([Value::List(ls)], true) => {
613            let ls = &ls.borrow();
614            // TODO(ed): Write correct typing
615            let ls = Type::from(&ls[0]);
616            let ret = union_type(ls, Type::Void, ctx.machine.blobs());
617            Ok(Value::from(ret))
618        }
619        ([Value::List(ls)], false) => {
620            // NOTE(ed): Deliberately no type checking.
621            let last = ls.borrow_mut().last().cloned().unwrap_or(Value::Nil);
622            Ok(last)
623        }
624        (values, _) => Err(RuntimeError::ExternTypeMismatch(
625            "pop".to_string(),
626            values.iter().map(Type::from).collect(),
627        )),
628    }
629}
630
631sylt_macro::extern_function!(
632    "sylt_std::sylt"
633    thread_sleep
634    "Sleep (blocking) for some time."
635    [One(Float(secs))] -> Type::Void => {
636        std::thread::sleep(std::time::Duration::from_secs_f64(*secs));
637        Ok(Value::Nil)
638    },
639);
640
641sylt_macro::sylt_link_gen!("sylt_std::sylt");