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