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 .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 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 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 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 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 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))) },
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 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 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 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 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");