lust/builtins/
mod.rs

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}