1use crate::ast::{Span, Type, TypeKind};
2use crate::lazy::StaticOnceCell;
3use crate::FunctionSignature;
4use alloc::{boxed::Box, collections::BTreeMap, string::ToString, vec, vec::Vec};
5use hashbrown::HashMap;
6
7#[derive(Debug, Clone)]
8pub struct BuiltinSignature {
9 pub params: Vec<TypeExpr>,
10 pub return_type: TypeExpr,
11}
12
13#[derive(Debug, Clone)]
14pub struct BuiltinFunction {
15 pub name: &'static str,
16 pub description: &'static str,
17 pub signature: BuiltinSignature,
18 pub param_names: &'static [&'static str],
19}
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
22pub enum MethodSemantics {
23 Simple,
24 ArrayMap,
25 ArrayFilter,
26 ArrayReduce,
27}
28
29#[derive(Debug, Clone)]
30pub struct BuiltinMethod {
31 pub receiver: TypeExpr,
32 pub name: &'static str,
33 pub description: &'static str,
34 pub signature: BuiltinSignature,
35 pub param_names: &'static [&'static str],
36 pub semantics: MethodSemantics,
37}
38
39#[derive(Debug, Clone)]
40pub enum TypeExpr {
41 Int,
42 Float,
43 Bool,
44 String,
45 Unit,
46 Unknown,
47 Named(&'static str),
48 Array(Box<TypeExpr>),
49 Map(Box<TypeExpr>, Box<TypeExpr>),
50 Result(Box<TypeExpr>, Box<TypeExpr>),
51 Option(Box<TypeExpr>),
52 Table,
53 Generic(&'static str),
54 SelfType,
55 Function {
56 params: Vec<TypeExpr>,
57 return_type: Box<TypeExpr>,
58 },
59}
60
61impl BuiltinFunction {
62 pub fn to_signature(&self, span: Span) -> FunctionSignature {
63 FunctionSignature {
64 params: self
65 .signature
66 .params
67 .iter()
68 .map(|expr| expr.instantiate(&HashMap::new(), Some(span)))
69 .collect(),
70 return_type: self
71 .signature
72 .return_type
73 .instantiate(&HashMap::new(), Some(span)),
74 is_method: false,
75 }
76 }
77
78 pub fn parameters(&self) -> Vec<(&'static str, &TypeExpr)> {
79 self.signature
80 .params
81 .iter()
82 .enumerate()
83 .map(|(idx, ty)| {
84 let name = self.param_names.get(idx).copied().unwrap_or("");
85 (name, ty)
86 })
87 .collect()
88 }
89
90 pub fn return_type(&self) -> &TypeExpr {
91 &self.signature.return_type
92 }
93}
94
95impl BuiltinMethod {
96 pub fn parameters(&self) -> Vec<(&'static str, &TypeExpr)> {
97 self.signature
98 .params
99 .iter()
100 .enumerate()
101 .map(|(idx, ty)| {
102 let name = self.param_names.get(idx).copied().unwrap_or("");
103 (name, ty)
104 })
105 .collect()
106 }
107
108 pub fn return_type(&self) -> &TypeExpr {
109 &self.signature.return_type
110 }
111
112 pub fn receiver_type(&self) -> &TypeExpr {
113 &self.receiver
114 }
115}
116
117impl TypeExpr {
118 pub fn instantiate(&self, generics: &HashMap<&'static str, Type>, span: Option<Span>) -> Type {
119 let span = span.unwrap_or_else(Span::dummy);
120 match self {
121 TypeExpr::Int => Type::new(TypeKind::Int, span),
122 TypeExpr::Float => Type::new(TypeKind::Float, span),
123 TypeExpr::Bool => Type::new(TypeKind::Bool, span),
124 TypeExpr::String => Type::new(TypeKind::String, span),
125 TypeExpr::Unit => Type::new(TypeKind::Unit, span),
126 TypeExpr::Unknown => Type::new(TypeKind::Unknown, span),
127 TypeExpr::Named(name) => Type::new(TypeKind::Named((*name).to_string()), span),
128 TypeExpr::Array(inner) => Type::new(
129 TypeKind::Array(Box::new(inner.instantiate(generics, Some(span)))),
130 span,
131 ),
132 TypeExpr::Map(key, value) => Type::new(
133 TypeKind::Map(
134 Box::new(key.instantiate(generics, Some(span))),
135 Box::new(value.instantiate(generics, Some(span))),
136 ),
137 span,
138 ),
139 TypeExpr::Result(ok, err) => Type::new(
140 TypeKind::Result(
141 Box::new(ok.instantiate(generics, Some(span))),
142 Box::new(err.instantiate(generics, Some(span))),
143 ),
144 span,
145 ),
146 TypeExpr::Option(inner) => Type::new(
147 TypeKind::Option(Box::new(inner.instantiate(generics, Some(span)))),
148 span,
149 ),
150 TypeExpr::Table => Type::new(TypeKind::Table, span),
151 TypeExpr::Generic(name) => generics
152 .get(name)
153 .cloned()
154 .unwrap_or_else(|| Type::new(TypeKind::Unknown, span)),
155 TypeExpr::SelfType => generics
156 .get("Self")
157 .cloned()
158 .unwrap_or_else(|| Type::new(TypeKind::Unknown, span)),
159 TypeExpr::Function {
160 params,
161 return_type,
162 } => Type::new(
163 TypeKind::Function {
164 params: params
165 .iter()
166 .map(|param| param.instantiate(generics, Some(span)))
167 .collect(),
168 return_type: Box::new(return_type.instantiate(generics, Some(span))),
169 },
170 span,
171 ),
172 }
173 }
174}
175
176fn match_type_expr(
177 pattern: &TypeExpr,
178 actual: &Type,
179 bindings: &mut HashMap<&'static str, Type>,
180) -> bool {
181 match (pattern, &actual.kind) {
182 (TypeExpr::SelfType, _) => {
183 bindings.insert("Self", actual.clone());
184 true
185 }
186 (TypeExpr::Generic(name), _) => {
187 if let Some(existing) = bindings.get(name) {
188 existing.kind == actual.kind
189 } else {
190 bindings.insert(name, actual.clone());
191 true
192 }
193 }
194 (TypeExpr::Int, TypeKind::Int) => true,
195 (TypeExpr::Float, TypeKind::Float) => true,
196 (TypeExpr::Bool, TypeKind::Bool) => true,
197 (TypeExpr::String, TypeKind::String) => true,
198 (TypeExpr::Unit, TypeKind::Unit) => true,
199 (TypeExpr::Unknown, TypeKind::Unknown) => true,
200 (TypeExpr::Named(expected), TypeKind::Named(actual_name)) => expected == actual_name,
201 (TypeExpr::Array(pattern_inner), TypeKind::Array(actual_inner)) => {
202 match_type_expr(pattern_inner, actual_inner, bindings)
203 }
204 (TypeExpr::Map(pattern_key, pattern_value), TypeKind::Map(actual_key, actual_value)) => {
205 match_type_expr(pattern_key, actual_key, bindings)
206 && match_type_expr(pattern_value, actual_value, bindings)
207 }
208 (TypeExpr::Option(pattern_inner), TypeKind::Option(actual_inner)) => {
209 match_type_expr(pattern_inner, actual_inner, bindings)
210 }
211 (TypeExpr::Result(pattern_ok, pattern_err), TypeKind::Result(actual_ok, actual_err)) => {
212 match_type_expr(pattern_ok, actual_ok, bindings)
213 && match_type_expr(pattern_err, actual_err, bindings)
214 }
215 (TypeExpr::Table, TypeKind::Table) => true,
216 _ => false,
217 }
218}
219
220pub fn match_receiver(pattern: &TypeExpr, actual: &Type) -> Option<HashMap<&'static str, Type>> {
221 let mut bindings = HashMap::new();
222 if match_type_expr(pattern, actual, &mut bindings) {
223 Some(bindings)
224 } else {
225 None
226 }
227}
228
229fn method(
230 receiver: TypeExpr,
231 name: &'static str,
232 description: &'static str,
233 param_names: &'static [&'static str],
234 params: Vec<TypeExpr>,
235 return_type: TypeExpr,
236) -> BuiltinMethod {
237 BuiltinMethod {
238 receiver,
239 name,
240 description,
241 signature: BuiltinSignature {
242 params,
243 return_type,
244 },
245 param_names,
246 semantics: MethodSemantics::Simple,
247 }
248}
249
250fn method_with_semantics(
251 receiver: TypeExpr,
252 name: &'static str,
253 description: &'static str,
254 param_names: &'static [&'static str],
255 params: Vec<TypeExpr>,
256 return_type: TypeExpr,
257 semantics: MethodSemantics,
258) -> BuiltinMethod {
259 let mut m = method(
260 receiver,
261 name,
262 description,
263 param_names,
264 params,
265 return_type,
266 );
267 m.semantics = semantics;
268 m
269}
270
271fn string_methods() -> Vec<BuiltinMethod> {
272 vec![
273 method(
274 TypeExpr::String,
275 "len",
276 "Return the length of the string in bytes",
277 &[],
278 vec![],
279 TypeExpr::Int,
280 ),
281 method(
282 TypeExpr::String,
283 "substring",
284 "Extract a substring from the string",
285 &["start", "end"],
286 vec![TypeExpr::Int, TypeExpr::Int],
287 TypeExpr::String,
288 ),
289 method(
290 TypeExpr::String,
291 "find",
292 "Find the first occurrence of a substring",
293 &["pattern"],
294 vec![TypeExpr::String],
295 TypeExpr::Option(Box::new(TypeExpr::Int)),
296 ),
297 method(
298 TypeExpr::String,
299 "starts_with",
300 "Check whether the string starts with a prefix",
301 &["prefix"],
302 vec![TypeExpr::String],
303 TypeExpr::Bool,
304 ),
305 method(
306 TypeExpr::String,
307 "ends_with",
308 "Check whether the string ends with a suffix",
309 &["suffix"],
310 vec![TypeExpr::String],
311 TypeExpr::Bool,
312 ),
313 method(
314 TypeExpr::String,
315 "contains",
316 "Check whether the string contains a substring",
317 &["substring"],
318 vec![TypeExpr::String],
319 TypeExpr::Bool,
320 ),
321 method(
322 TypeExpr::String,
323 "split",
324 "Split the string on a separator",
325 &["delimiter"],
326 vec![TypeExpr::String],
327 TypeExpr::Array(Box::new(TypeExpr::String)),
328 ),
329 method(
330 TypeExpr::String,
331 "trim",
332 "Trim whitespace from both ends of the string",
333 &[],
334 vec![],
335 TypeExpr::String,
336 ),
337 method(
338 TypeExpr::String,
339 "trim_start",
340 "Trim whitespace from the start of the string",
341 &[],
342 vec![],
343 TypeExpr::String,
344 ),
345 method(
346 TypeExpr::String,
347 "trim_end",
348 "Trim whitespace from the end of the string",
349 &[],
350 vec![],
351 TypeExpr::String,
352 ),
353 method(
354 TypeExpr::String,
355 "replace",
356 "Replace occurrences of a substring",
357 &["from", "to"],
358 vec![TypeExpr::String, TypeExpr::String],
359 TypeExpr::String,
360 ),
361 method(
362 TypeExpr::String,
363 "to_upper",
364 "Convert the string to uppercase",
365 &[],
366 vec![],
367 TypeExpr::String,
368 ),
369 method(
370 TypeExpr::String,
371 "to_lower",
372 "Convert the string to lowercase",
373 &[],
374 vec![],
375 TypeExpr::String,
376 ),
377 method(
378 TypeExpr::String,
379 "is_empty",
380 "Check if the string is empty",
381 &[],
382 vec![],
383 TypeExpr::Bool,
384 ),
385 method(
386 TypeExpr::String,
387 "chars",
388 "Return the characters as an array of strings",
389 &[],
390 vec![],
391 TypeExpr::Array(Box::new(TypeExpr::String)),
392 ),
393 method(
394 TypeExpr::String,
395 "lines",
396 "Return the lines as an array of strings",
397 &[],
398 vec![],
399 TypeExpr::Array(Box::new(TypeExpr::String)),
400 ),
401 method(
402 TypeExpr::String,
403 "iter",
404 "Return an iterator over the characters of the string",
405 &[],
406 vec![],
407 TypeExpr::Named("Iterator"),
408 ),
409 ]
410}
411
412fn array_methods() -> Vec<BuiltinMethod> {
413 let receiver = TypeExpr::Array(Box::new(TypeExpr::Generic("T")));
414 let mut methods = Vec::new();
415 methods.push(method(
416 receiver.clone(),
417 "iter",
418 "Return an iterator over the array items",
419 &[],
420 vec![],
421 TypeExpr::Named("Iterator"),
422 ));
423 methods.push(method(
424 receiver.clone(),
425 "len",
426 "Return the number of elements in the array",
427 &[],
428 vec![],
429 TypeExpr::Int,
430 ));
431 methods.push(method(
432 receiver.clone(),
433 "get",
434 "Return the element at the given index, if any",
435 &["index"],
436 vec![TypeExpr::Int],
437 TypeExpr::Option(Box::new(TypeExpr::Generic("T"))),
438 ));
439 methods.push(method(
440 receiver.clone(),
441 "first",
442 "Return the first element, if any",
443 &[],
444 vec![],
445 TypeExpr::Option(Box::new(TypeExpr::Generic("T"))),
446 ));
447 methods.push(method(
448 receiver.clone(),
449 "last",
450 "Return the last element, if any",
451 &[],
452 vec![],
453 TypeExpr::Option(Box::new(TypeExpr::Generic("T"))),
454 ));
455 methods.push(method(
456 receiver.clone(),
457 "push",
458 "Append a value to the array",
459 &["value"],
460 vec![TypeExpr::Generic("T")],
461 TypeExpr::Unit,
462 ));
463 methods.push(method(
464 receiver.clone(),
465 "pop",
466 "Remove and return the last element, if any",
467 &[],
468 vec![],
469 TypeExpr::Option(Box::new(TypeExpr::Generic("T"))),
470 ));
471 methods.push(method_with_semantics(
472 receiver.clone(),
473 "map",
474 "Transform each element using the provided function",
475 &["func"],
476 vec![TypeExpr::Function {
477 params: vec![TypeExpr::Generic("T")],
478 return_type: Box::new(TypeExpr::Unknown),
479 }],
480 TypeExpr::Array(Box::new(TypeExpr::Unknown)),
481 MethodSemantics::ArrayMap,
482 ));
483 methods.push(method_with_semantics(
484 receiver.clone(),
485 "filter",
486 "Keep elements where the predicate returns true",
487 &["func"],
488 vec![TypeExpr::Function {
489 params: vec![TypeExpr::Generic("T")],
490 return_type: Box::new(TypeExpr::Bool),
491 }],
492 TypeExpr::Array(Box::new(TypeExpr::Generic("T"))),
493 MethodSemantics::ArrayFilter,
494 ));
495 methods.push(method_with_semantics(
496 receiver.clone(),
497 "reduce",
498 "Fold elements into a single value",
499 &["initial", "func"],
500 vec![
501 TypeExpr::Unknown,
502 TypeExpr::Function {
503 params: vec![TypeExpr::Unknown, TypeExpr::Generic("T")],
504 return_type: Box::new(TypeExpr::Unknown),
505 },
506 ],
507 TypeExpr::Unknown,
508 MethodSemantics::ArrayReduce,
509 ));
510 methods.push(method(
511 receiver.clone(),
512 "slice",
513 "Return a slice of the array between two indices",
514 &["start", "end"],
515 vec![TypeExpr::Int, TypeExpr::Int],
516 TypeExpr::Array(Box::new(TypeExpr::Generic("T"))),
517 ));
518 methods.push(method(
519 receiver.clone(),
520 "clear",
521 "Remove all elements from the array",
522 &[],
523 vec![],
524 TypeExpr::Unit,
525 ));
526 methods.push(method(
527 receiver,
528 "is_empty",
529 "Check if the array contains no elements",
530 &[],
531 vec![],
532 TypeExpr::Bool,
533 ));
534 methods
535}
536
537fn map_methods() -> Vec<BuiltinMethod> {
538 let receiver = TypeExpr::Map(
539 Box::new(TypeExpr::Generic("K")),
540 Box::new(TypeExpr::Generic("V")),
541 );
542 vec![
543 method(
544 receiver.clone(),
545 "iter",
546 "Iterate over key/value pairs",
547 &[],
548 vec![],
549 TypeExpr::Named("Iterator"),
550 ),
551 method(
552 receiver.clone(),
553 "len",
554 "Return the number of entries in the map",
555 &[],
556 vec![],
557 TypeExpr::Int,
558 ),
559 method(
560 receiver.clone(),
561 "get",
562 "Look up a value by key",
563 &["key"],
564 vec![TypeExpr::Generic("K")],
565 TypeExpr::Option(Box::new(TypeExpr::Generic("V"))),
566 ),
567 method(
568 receiver.clone(),
569 "set",
570 "Insert or overwrite a key/value pair",
571 &["key", "value"],
572 vec![TypeExpr::Generic("K"), TypeExpr::Generic("V")],
573 TypeExpr::Unit,
574 ),
575 method(
576 receiver.clone(),
577 "has",
578 "Check whether the map contains a key",
579 &["key"],
580 vec![TypeExpr::Generic("K")],
581 TypeExpr::Bool,
582 ),
583 method(
584 receiver.clone(),
585 "delete",
586 "Remove an entry from the map",
587 &["key"],
588 vec![TypeExpr::Generic("K")],
589 TypeExpr::Option(Box::new(TypeExpr::Generic("V"))),
590 ),
591 method(
592 receiver.clone(),
593 "keys",
594 "Return the keys as an array",
595 &[],
596 vec![],
597 TypeExpr::Array(Box::new(TypeExpr::Generic("K"))),
598 ),
599 method(
600 receiver,
601 "values",
602 "Return the values as an array",
603 &[],
604 vec![],
605 TypeExpr::Array(Box::new(TypeExpr::Generic("V"))),
606 ),
607 ]
608}
609
610fn table_methods() -> Vec<BuiltinMethod> {
611 vec![
612 method(
613 TypeExpr::Table,
614 "iter",
615 "Iterate over key/value pairs",
616 &[],
617 vec![],
618 TypeExpr::Named("Iterator"),
619 ),
620 method(
621 TypeExpr::Table,
622 "len",
623 "Return the number of entries in the table",
624 &[],
625 vec![],
626 TypeExpr::Int,
627 ),
628 method(
629 TypeExpr::Table,
630 "get",
631 "Look up a value by key",
632 &["key"],
633 vec![TypeExpr::Unknown],
634 TypeExpr::Option(Box::new(TypeExpr::Unknown)),
635 ),
636 method(
637 TypeExpr::Table,
638 "set",
639 "Insert or overwrite a key/value pair",
640 &["key", "value"],
641 vec![TypeExpr::Unknown, TypeExpr::Unknown],
642 TypeExpr::Unit,
643 ),
644 method(
645 TypeExpr::Table,
646 "has",
647 "Check whether the table contains a key",
648 &["key"],
649 vec![TypeExpr::Unknown],
650 TypeExpr::Bool,
651 ),
652 method(
653 TypeExpr::Table,
654 "delete",
655 "Remove an entry from the table",
656 &["key"],
657 vec![TypeExpr::Unknown],
658 TypeExpr::Option(Box::new(TypeExpr::Unknown)),
659 ),
660 method(
661 TypeExpr::Table,
662 "keys",
663 "Return the keys as an array",
664 &[],
665 vec![],
666 TypeExpr::Array(Box::new(TypeExpr::Unknown)),
667 ),
668 method(
669 TypeExpr::Table,
670 "values",
671 "Return the values as an array",
672 &[],
673 vec![],
674 TypeExpr::Array(Box::new(TypeExpr::Unknown)),
675 ),
676 ]
677}
678
679fn iterator_methods() -> Vec<BuiltinMethod> {
680 vec![
681 method(
682 TypeExpr::Named("Iterator"),
683 "iter",
684 "Return the iterator itself",
685 &[],
686 vec![],
687 TypeExpr::Named("Iterator"),
688 ),
689 method(
690 TypeExpr::Named("Iterator"),
691 "next",
692 "Advance the iterator and return the next value",
693 &[],
694 vec![],
695 TypeExpr::Option(Box::new(TypeExpr::Unknown)),
696 ),
697 ]
698}
699
700fn option_methods() -> Vec<BuiltinMethod> {
701 let receiver = TypeExpr::Option(Box::new(TypeExpr::Generic("T")));
702 vec![
703 method(
704 receiver.clone(),
705 "is_some",
706 "Check if the option contains a value",
707 &[],
708 vec![],
709 TypeExpr::Bool,
710 ),
711 method(
712 receiver.clone(),
713 "is_none",
714 "Check if the option is empty",
715 &[],
716 vec![],
717 TypeExpr::Bool,
718 ),
719 method(
720 receiver.clone(),
721 "unwrap",
722 "Unwrap the contained value, panicking if None",
723 &[],
724 vec![],
725 TypeExpr::Generic("T"),
726 ),
727 method(
728 receiver,
729 "unwrap_or",
730 "Return the value or a provided default",
731 &["default"],
732 vec![TypeExpr::Generic("T")],
733 TypeExpr::Generic("T"),
734 ),
735 ]
736}
737
738fn result_methods() -> Vec<BuiltinMethod> {
739 let receiver = TypeExpr::Result(
740 Box::new(TypeExpr::Generic("T")),
741 Box::new(TypeExpr::Generic("E")),
742 );
743 vec![
744 method(
745 receiver.clone(),
746 "is_ok",
747 "Check if the result is Ok",
748 &[],
749 vec![],
750 TypeExpr::Bool,
751 ),
752 method(
753 receiver.clone(),
754 "is_err",
755 "Check if the result is Err",
756 &[],
757 vec![],
758 TypeExpr::Bool,
759 ),
760 method(
761 receiver.clone(),
762 "unwrap",
763 "Unwrap the Ok value, panicking if Err",
764 &[],
765 vec![],
766 TypeExpr::Generic("T"),
767 ),
768 method(
769 receiver,
770 "unwrap_or",
771 "Return the Ok value or a provided default",
772 &["default"],
773 vec![TypeExpr::Generic("T")],
774 TypeExpr::Generic("T"),
775 ),
776 ]
777}
778
779fn float_methods() -> Vec<BuiltinMethod> {
780 vec![
781 method(
782 TypeExpr::Float,
783 "to_int",
784 "Convert the float to an integer by truncation",
785 &[],
786 vec![],
787 TypeExpr::Int,
788 ),
789 method(
790 TypeExpr::Float,
791 "floor",
792 "Return the greatest integer less than or equal to the value",
793 &[],
794 vec![],
795 TypeExpr::Float,
796 ),
797 method(
798 TypeExpr::Float,
799 "ceil",
800 "Return the smallest integer greater than or equal to the value",
801 &[],
802 vec![],
803 TypeExpr::Float,
804 ),
805 method(
806 TypeExpr::Float,
807 "round",
808 "Round the float to the nearest integer",
809 &[],
810 vec![],
811 TypeExpr::Float,
812 ),
813 method(
814 TypeExpr::Float,
815 "sqrt",
816 "Return the square root of the float",
817 &[],
818 vec![],
819 TypeExpr::Float,
820 ),
821 method(
822 TypeExpr::Float,
823 "abs",
824 "Return the absolute value of the float",
825 &[],
826 vec![],
827 TypeExpr::Float,
828 ),
829 method(
830 TypeExpr::Float,
831 "clamp",
832 "Clamp the float between a minimum and maximum value",
833 &["min", "max"],
834 vec![TypeExpr::Float, TypeExpr::Float],
835 TypeExpr::Float,
836 ),
837 ]
838}
839
840fn int_methods() -> Vec<BuiltinMethod> {
841 vec![
842 method(
843 TypeExpr::Int,
844 "to_float",
845 "Convert the integer to a float",
846 &[],
847 vec![],
848 TypeExpr::Float,
849 ),
850 method(
851 TypeExpr::Int,
852 "abs",
853 "Return the absolute value of the integer",
854 &[],
855 vec![],
856 TypeExpr::Int,
857 ),
858 method(
859 TypeExpr::Int,
860 "clamp",
861 "Clamp the integer between a minimum and maximum value",
862 &["min", "max"],
863 vec![TypeExpr::Int, TypeExpr::Int],
864 TypeExpr::Int,
865 ),
866 ]
867}
868
869static BASE_FUNCTIONS: StaticOnceCell<Vec<BuiltinFunction>> = StaticOnceCell::new();
870
871fn build_base_functions() -> Vec<BuiltinFunction> {
872 vec![
873 BuiltinFunction {
874 name: "print",
875 description: "Print values without a newline",
876 signature: BuiltinSignature {
877 params: vec![TypeExpr::Unknown],
878 return_type: TypeExpr::Unit,
879 },
880 param_names: &["value"],
881 },
882 BuiltinFunction {
883 name: "println",
884 description: "Print values followed by a newline",
885 signature: BuiltinSignature {
886 params: vec![TypeExpr::Unknown],
887 return_type: TypeExpr::Unit,
888 },
889 param_names: &["value"],
890 },
891 BuiltinFunction {
892 name: "type",
893 description: "Return the runtime type name",
894 signature: BuiltinSignature {
895 params: vec![TypeExpr::Unknown],
896 return_type: TypeExpr::String,
897 },
898 param_names: &["value"],
899 },
900 BuiltinFunction {
901 name: "tostring",
902 description: "Convert a value to a string",
903 signature: BuiltinSignature {
904 params: vec![TypeExpr::Unknown],
905 return_type: TypeExpr::String,
906 },
907 param_names: &["value"],
908 },
909 ]
910}
911
912static TASK_FUNCTIONS: StaticOnceCell<Vec<BuiltinFunction>> = StaticOnceCell::new();
913
914fn build_task_functions() -> Vec<BuiltinFunction> {
915 vec![
916 BuiltinFunction {
917 name: "task.run",
918 description: "Run a function as a task",
919 signature: BuiltinSignature {
920 params: vec![TypeExpr::Unknown],
921 return_type: TypeExpr::Named("Task"),
922 },
923 param_names: &["func"],
924 },
925 BuiltinFunction {
926 name: "task.create",
927 description: "Create a suspended task",
928 signature: BuiltinSignature {
929 params: vec![TypeExpr::Unknown],
930 return_type: TypeExpr::Named("Task"),
931 },
932 param_names: &["func"],
933 },
934 BuiltinFunction {
935 name: "task.status",
936 description: "Get the status of a task",
937 signature: BuiltinSignature {
938 params: vec![TypeExpr::Named("Task")],
939 return_type: TypeExpr::Named("TaskStatus"),
940 },
941 param_names: &["task"],
942 },
943 BuiltinFunction {
944 name: "task.info",
945 description: "Get detailed information about a task",
946 signature: BuiltinSignature {
947 params: vec![TypeExpr::Named("Task")],
948 return_type: TypeExpr::Named("TaskInfo"),
949 },
950 param_names: &["task"],
951 },
952 BuiltinFunction {
953 name: "task.resume",
954 description: "Resume a suspended task",
955 signature: BuiltinSignature {
956 params: vec![TypeExpr::Named("Task")],
957 return_type: TypeExpr::Named("TaskInfo"),
958 },
959 param_names: &["task"],
960 },
961 BuiltinFunction {
962 name: "task.yield",
963 description: "Yield from the current task",
964 signature: BuiltinSignature {
965 params: vec![TypeExpr::Unknown],
966 return_type: TypeExpr::Unknown,
967 },
968 param_names: &["value"],
969 },
970 BuiltinFunction {
971 name: "task.stop",
972 description: "Stop a running task",
973 signature: BuiltinSignature {
974 params: vec![TypeExpr::Named("Task")],
975 return_type: TypeExpr::Bool,
976 },
977 param_names: &["task"],
978 },
979 BuiltinFunction {
980 name: "task.restart",
981 description: "Restart a completed task",
982 signature: BuiltinSignature {
983 params: vec![TypeExpr::Named("Task")],
984 return_type: TypeExpr::Named("TaskInfo"),
985 },
986 param_names: &["task"],
987 },
988 BuiltinFunction {
989 name: "task.current",
990 description: "Return the currently executing task",
991 signature: BuiltinSignature {
992 params: vec![],
993 return_type: TypeExpr::Option(Box::new(TypeExpr::Named("Task"))),
994 },
995 param_names: &[],
996 },
997 ]
998}
999
1000static IO_FUNCTIONS: StaticOnceCell<Vec<BuiltinFunction>> = StaticOnceCell::new();
1001
1002fn build_io_functions() -> Vec<BuiltinFunction> {
1003 vec![
1004 BuiltinFunction {
1005 name: "io.read_file",
1006 description: "Read the contents of a file",
1007 signature: BuiltinSignature {
1008 params: vec![TypeExpr::String],
1009 return_type: TypeExpr::Result(
1010 Box::new(TypeExpr::String),
1011 Box::new(TypeExpr::String),
1012 ),
1013 },
1014 param_names: &["path"],
1015 },
1016 BuiltinFunction {
1017 name: "io.read_file_bytes",
1018 description: "Read the contents of a file as byte values",
1019 signature: BuiltinSignature {
1020 params: vec![TypeExpr::String],
1021 return_type: TypeExpr::Result(
1022 Box::new(TypeExpr::Array(Box::new(TypeExpr::Int))),
1023 Box::new(TypeExpr::String),
1024 ),
1025 },
1026 param_names: &["path"],
1027 },
1028 BuiltinFunction {
1029 name: "io.write_file",
1030 description: "Write contents to a file",
1031 signature: BuiltinSignature {
1032 params: vec![TypeExpr::String, TypeExpr::Unknown],
1033 return_type: TypeExpr::Result(Box::new(TypeExpr::Unit), Box::new(TypeExpr::String)),
1034 },
1035 param_names: &["path", "value"],
1036 },
1037 BuiltinFunction {
1038 name: "io.read_stdin",
1039 description: "Read all available stdin",
1040 signature: BuiltinSignature {
1041 params: vec![],
1042 return_type: TypeExpr::Result(
1043 Box::new(TypeExpr::String),
1044 Box::new(TypeExpr::String),
1045 ),
1046 },
1047 param_names: &[],
1048 },
1049 BuiltinFunction {
1050 name: "io.read_line",
1051 description: "Read a single line from stdin",
1052 signature: BuiltinSignature {
1053 params: vec![],
1054 return_type: TypeExpr::Result(
1055 Box::new(TypeExpr::String),
1056 Box::new(TypeExpr::String),
1057 ),
1058 },
1059 param_names: &[],
1060 },
1061 BuiltinFunction {
1062 name: "io.write_stdout",
1063 description: "Write a value to stdout",
1064 signature: BuiltinSignature {
1065 params: vec![TypeExpr::Unknown],
1066 return_type: TypeExpr::Result(Box::new(TypeExpr::Unit), Box::new(TypeExpr::String)),
1067 },
1068 param_names: &["value"],
1069 },
1070 ]
1071}
1072
1073static OS_FUNCTIONS: StaticOnceCell<Vec<BuiltinFunction>> = StaticOnceCell::new();
1074
1075fn build_os_functions() -> Vec<BuiltinFunction> {
1076 vec![
1077 BuiltinFunction {
1078 name: "os.create_file",
1079 description: "Create an empty file on disk",
1080 signature: BuiltinSignature {
1081 params: vec![TypeExpr::String],
1082 return_type: TypeExpr::Result(Box::new(TypeExpr::Unit), Box::new(TypeExpr::String)),
1083 },
1084 param_names: &["path"],
1085 },
1086 BuiltinFunction {
1087 name: "os.create_dir",
1088 description: "Create a directory",
1089 signature: BuiltinSignature {
1090 params: vec![TypeExpr::String],
1091 return_type: TypeExpr::Result(Box::new(TypeExpr::Unit), Box::new(TypeExpr::String)),
1092 },
1093 param_names: &["path"],
1094 },
1095 BuiltinFunction {
1096 name: "os.remove_file",
1097 description: "Remove a file from disk",
1098 signature: BuiltinSignature {
1099 params: vec![TypeExpr::String],
1100 return_type: TypeExpr::Result(Box::new(TypeExpr::Unit), Box::new(TypeExpr::String)),
1101 },
1102 param_names: &["path"],
1103 },
1104 BuiltinFunction {
1105 name: "os.remove_dir",
1106 description: "Remove an empty directory",
1107 signature: BuiltinSignature {
1108 params: vec![TypeExpr::String],
1109 return_type: TypeExpr::Result(Box::new(TypeExpr::Unit), Box::new(TypeExpr::String)),
1110 },
1111 param_names: &["path"],
1112 },
1113 BuiltinFunction {
1114 name: "os.rename",
1115 description: "Rename or move a path",
1116 signature: BuiltinSignature {
1117 params: vec![TypeExpr::String, TypeExpr::String],
1118 return_type: TypeExpr::Result(Box::new(TypeExpr::Unit), Box::new(TypeExpr::String)),
1119 },
1120 param_names: &["from", "to"],
1121 },
1122 ]
1123}
1124
1125static BUILTIN_METHODS: StaticOnceCell<Vec<BuiltinMethod>> = StaticOnceCell::new();
1126
1127fn build_builtin_methods() -> Vec<BuiltinMethod> {
1128 let mut methods = Vec::new();
1129 methods.extend(string_methods());
1130 methods.extend(array_methods());
1131 methods.extend(map_methods());
1132 methods.extend(table_methods());
1133 methods.extend(iterator_methods());
1134 methods.extend(option_methods());
1135 methods.extend(result_methods());
1136 methods.extend(float_methods());
1137 methods.extend(int_methods());
1138 methods
1139}
1140
1141pub fn base_functions() -> &'static [BuiltinFunction] {
1142 BASE_FUNCTIONS.get_or_init(build_base_functions).as_slice()
1143}
1144
1145pub fn task_functions() -> &'static [BuiltinFunction] {
1146 TASK_FUNCTIONS.get_or_init(build_task_functions).as_slice()
1147}
1148
1149pub fn io_functions() -> &'static [BuiltinFunction] {
1150 IO_FUNCTIONS.get_or_init(build_io_functions).as_slice()
1151}
1152
1153pub fn os_functions() -> &'static [BuiltinFunction] {
1154 OS_FUNCTIONS.get_or_init(build_os_functions).as_slice()
1155}
1156
1157pub fn builtin_methods() -> &'static [BuiltinMethod] {
1158 BUILTIN_METHODS
1159 .get_or_init(build_builtin_methods)
1160 .as_slice()
1161}
1162
1163pub struct BuiltinModule {
1164 name: &'static str,
1165 description: &'static str,
1166 functions: Vec<&'static BuiltinFunction>,
1167}
1168
1169impl BuiltinModule {
1170 pub fn name(&self) -> &'static str {
1171 self.name
1172 }
1173
1174 pub fn description(&self) -> &'static str {
1175 self.description
1176 }
1177
1178 pub fn functions(&self) -> &[&'static BuiltinFunction] {
1179 &self.functions
1180 }
1181}
1182
1183pub struct BuiltinsDatabase {
1184 global_functions: Vec<&'static BuiltinFunction>,
1185 modules: BTreeMap<&'static str, BuiltinModule>,
1186 methods: HashMap<&'static str, Vec<&'static BuiltinMethod>>,
1187}
1188
1189impl BuiltinsDatabase {
1190 pub fn global_functions(&self) -> &[&'static BuiltinFunction] {
1191 &self.global_functions
1192 }
1193
1194 pub fn module(&self, name: &str) -> Option<&BuiltinModule> {
1195 self.modules.get(name)
1196 }
1197
1198 pub fn methods_for(&self, type_name: &str) -> Option<&[&'static BuiltinMethod]> {
1199 self.methods
1200 .get(type_name)
1201 .map(|methods| methods.as_slice())
1202 }
1203
1204 pub fn modules(&self) -> impl Iterator<Item = &BuiltinModule> {
1205 self.modules.values()
1206 }
1207}
1208
1209fn receiver_key(expr: &TypeExpr) -> Option<&'static str> {
1210 match expr {
1211 TypeExpr::String => Some("String"),
1212 TypeExpr::Array(_) => Some("Array"),
1213 TypeExpr::Map(_, _) => Some("Map"),
1214 TypeExpr::Table => Some("Table"),
1215 TypeExpr::Named(name) => Some(name),
1216 TypeExpr::Option(_) => Some("Option"),
1217 TypeExpr::Result(_, _) => Some("Result"),
1218 TypeExpr::Float => Some("Float"),
1219 TypeExpr::Int => Some("Int"),
1220 TypeExpr::Bool => Some("Bool"),
1221 TypeExpr::Unknown => Some("Unknown"),
1222 TypeExpr::Unit => Some("Unit"),
1223 TypeExpr::Generic(name) => Some(name),
1224 TypeExpr::SelfType => Some("Self"),
1225 TypeExpr::Function { .. } => Some("function"),
1226 }
1227}
1228
1229static BUILTINS_DATABASE: StaticOnceCell<BuiltinsDatabase> = StaticOnceCell::new();
1230
1231fn build_builtins_database() -> BuiltinsDatabase {
1232 let mut modules: BTreeMap<&'static str, BuiltinModule> = BTreeMap::new();
1233 let module_specs: [(&'static str, &'static str, &'static [BuiltinFunction]); 3] = [
1234 ("task", "task runtime module", task_functions()),
1235 ("io", "io file & console module", io_functions()),
1236 ("os", "os filesystem module", os_functions()),
1237 ];
1238 for (name, description, functions) in module_specs {
1239 let mut module_funcs: Vec<&'static BuiltinFunction> = functions.iter().collect();
1240 module_funcs.sort_by(|a, b| a.name.cmp(b.name));
1241 modules.insert(
1242 name,
1243 BuiltinModule {
1244 name,
1245 description,
1246 functions: module_funcs,
1247 },
1248 );
1249 }
1250
1251 let mut global_functions: Vec<&'static BuiltinFunction> = base_functions().iter().collect();
1252 global_functions.sort_by(|a, b| a.name.cmp(b.name));
1253
1254 let mut methods: HashMap<&'static str, Vec<&'static BuiltinMethod>> = HashMap::new();
1255 for method in builtin_methods() {
1256 if let Some(key) = receiver_key(&method.receiver) {
1257 methods.entry(key).or_default().push(method);
1258 }
1259 }
1260 for vec in methods.values_mut() {
1261 vec.sort_by(|a, b| a.name.cmp(b.name));
1262 }
1263
1264 BuiltinsDatabase {
1265 global_functions,
1266 modules,
1267 methods,
1268 }
1269}
1270
1271pub fn builtins() -> &'static BuiltinsDatabase {
1272 BUILTINS_DATABASE.get_or_init(build_builtins_database)
1273}
1274
1275pub fn lookup_builtin_method(
1276 receiver: &Type,
1277 name: &str,
1278) -> Option<(&'static BuiltinMethod, HashMap<&'static str, Type>)> {
1279 for method in builtin_methods() {
1280 if method.name == name {
1281 if let Some(bindings) = match_receiver(&method.receiver, receiver) {
1282 return Some((method, bindings));
1283 }
1284 }
1285 }
1286 None
1287}