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