lust/builtins/
mod.rs

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            "min",
760            "Return the smaller of two numbers",
761            &["other"],
762            vec![TypeExpr::Float],
763            TypeExpr::Float,
764        ),
765        method(
766            TypeExpr::Float,
767            "max",
768            "Return the larger of two numbers",
769            &["other"],
770            vec![TypeExpr::Float],
771            TypeExpr::Float,
772        ),
773        method(
774            TypeExpr::Float,
775            "clamp",
776            "Clamp the float between a minimum and maximum value",
777            &["min", "max"],
778            vec![TypeExpr::Float, TypeExpr::Float],
779            TypeExpr::Float,
780        ),
781    ]
782}
783
784fn int_methods() -> Vec<BuiltinMethod> {
785    vec![
786        method(
787            TypeExpr::Int,
788            "to_float",
789            "Convert the integer to a float",
790            &[],
791            vec![],
792            TypeExpr::Float,
793        ),
794        method(
795            TypeExpr::Int,
796            "abs",
797            "Return the absolute value of the integer",
798            &[],
799            vec![],
800            TypeExpr::Int,
801        ),
802        method(
803            TypeExpr::Int,
804            "min",
805            "Return the smaller of two integers",
806            &["other"],
807            vec![TypeExpr::Int],
808            TypeExpr::Int,
809        ),
810        method(
811            TypeExpr::Int,
812            "max",
813            "Return the larger of two integers",
814            &["other"],
815            vec![TypeExpr::Int],
816            TypeExpr::Int,
817        ),
818        method(
819            TypeExpr::Int,
820            "clamp",
821            "Clamp the integer between a minimum and maximum value",
822            &["min", "max"],
823            vec![TypeExpr::Int, TypeExpr::Int],
824            TypeExpr::Int,
825        ),
826    ]
827}
828
829fn lua_table_methods() -> Vec<BuiltinMethod> {
830    vec![
831        method(
832            TypeExpr::Named("LuaTable"),
833            "len",
834            "Return the length of the array portion (1-based contiguous keys)",
835            &[],
836            vec![],
837            TypeExpr::Int,
838        ),
839        method(
840            TypeExpr::Named("LuaTable"),
841            "push",
842            "Append a value to the array portion",
843            &["value"],
844            vec![TypeExpr::Unknown],
845            TypeExpr::Unit,
846        ),
847        method(
848            TypeExpr::Named("LuaTable"),
849            "insert",
850            "Insert a value into the array portion at a position",
851            &["pos", "value"],
852            vec![TypeExpr::Unknown, TypeExpr::Unknown],
853            TypeExpr::Unit,
854        ),
855        method(
856            TypeExpr::Named("LuaTable"),
857            "remove",
858            "Remove a value from the array portion at a position",
859            &["pos"],
860            vec![TypeExpr::Unknown],
861            TypeExpr::Unknown,
862        ),
863        method(
864            TypeExpr::Named("LuaTable"),
865            "concat",
866            "Concatenate array elements into a string",
867            &["sep", "i", "j"],
868            vec![TypeExpr::Unknown, TypeExpr::Unknown, TypeExpr::Unknown],
869            TypeExpr::String,
870        ),
871        method(
872            TypeExpr::Named("LuaTable"),
873            "unpack",
874            "Unpack array elements as multiple returns",
875            &["i", "j"],
876            vec![TypeExpr::Unknown, TypeExpr::Unknown],
877            TypeExpr::Unknown,
878        ),
879        method(
880            TypeExpr::Named("LuaTable"),
881            "sort",
882            "Sort the array portion",
883            &["comp"],
884            vec![TypeExpr::Unknown],
885            TypeExpr::Unit,
886        ),
887        method(
888            TypeExpr::Named("LuaTable"),
889            "maxn",
890            "Find the largest positive numeric index",
891            &[],
892            vec![],
893            TypeExpr::Int,
894        ),
895    ]
896}
897
898static BASE_FUNCTIONS: StaticOnceCell<Vec<BuiltinFunction>> = StaticOnceCell::new();
899
900fn build_base_functions() -> Vec<BuiltinFunction> {
901    vec![
902        BuiltinFunction {
903            name: "print",
904            description: "Print values without a newline",
905            signature: BuiltinSignature {
906                params: vec![TypeExpr::Unknown],
907                return_type: TypeExpr::Unit,
908            },
909            param_names: &["value"],
910        },
911        BuiltinFunction {
912            name: "println",
913            description: "Print values followed by a newline",
914            signature: BuiltinSignature {
915                params: vec![TypeExpr::Unknown],
916                return_type: TypeExpr::Unit,
917            },
918            param_names: &["value"],
919        },
920        BuiltinFunction {
921            name: "type",
922            description: "Return the runtime type name",
923            signature: BuiltinSignature {
924                params: vec![TypeExpr::Unknown],
925                return_type: TypeExpr::String,
926            },
927            param_names: &["value"],
928        },
929        BuiltinFunction {
930            name: "tostring",
931            description: "Convert a value to a string",
932            signature: BuiltinSignature {
933                params: vec![TypeExpr::Unknown],
934                return_type: TypeExpr::String,
935            },
936            param_names: &["value"],
937        },
938        BuiltinFunction {
939            name: "error",
940            description: "Raise a runtime error",
941            signature: BuiltinSignature {
942                params: vec![TypeExpr::Unknown],
943                return_type: TypeExpr::Unknown,
944            },
945            param_names: &["message"],
946        },
947        BuiltinFunction {
948            name: "assert",
949            description: "Assert a condition or raise an error",
950            signature: BuiltinSignature {
951                params: vec![TypeExpr::Unknown, TypeExpr::Unknown],
952                return_type: TypeExpr::Unknown,
953            },
954            param_names: &["cond", "message"],
955        },
956        BuiltinFunction {
957            name: "tonumber",
958            description: "Convert a value to a number",
959            signature: BuiltinSignature {
960                params: vec![TypeExpr::Unknown, TypeExpr::Unknown],
961                return_type: TypeExpr::Unknown,
962            },
963            param_names: &["value", "base"],
964        },
965        BuiltinFunction {
966            name: "pairs",
967            description: "Iterate over key/value pairs",
968            signature: BuiltinSignature {
969                params: vec![TypeExpr::Unknown],
970                return_type: TypeExpr::Named("Iterator"),
971            },
972            param_names: &["table"],
973        },
974        BuiltinFunction {
975            name: "ipairs",
976            description: "Iterate over array elements with indices",
977            signature: BuiltinSignature {
978                params: vec![TypeExpr::Unknown],
979                return_type: TypeExpr::Named("Iterator"),
980            },
981            param_names: &["array"],
982        },
983        BuiltinFunction {
984            name: "select",
985            description: "Return arguments starting at an index or the argument count",
986            signature: BuiltinSignature {
987                params: vec![TypeExpr::Unknown, TypeExpr::Unknown],
988                return_type: TypeExpr::Unknown,
989            },
990            param_names: &["index_or_hash", "..."],
991        },
992        BuiltinFunction {
993            name: "random",
994            description: "Generate a random number in an optional range",
995            signature: BuiltinSignature {
996                params: vec![TypeExpr::Unknown, TypeExpr::Unknown],
997                return_type: TypeExpr::Unknown,
998            },
999            param_names: &["m", "n"],
1000        },
1001        BuiltinFunction {
1002            name: "randomseed",
1003            description: "Seed the random number generator",
1004            signature: BuiltinSignature {
1005                params: vec![TypeExpr::Unknown],
1006                return_type: TypeExpr::Unit,
1007            },
1008            param_names: &["seed"],
1009        },
1010        BuiltinFunction {
1011            name: "unpack",
1012            description: "Unpack array elements into multiple returns",
1013            signature: BuiltinSignature {
1014                params: vec![TypeExpr::Unknown, TypeExpr::Unknown, TypeExpr::Unknown],
1015                return_type: TypeExpr::Unknown,
1016            },
1017            param_names: &["table", "i", "j"],
1018        },
1019        BuiltinFunction {
1020            name: "setmetatable",
1021            description: "Assign a metatable to a Lua table value",
1022            signature: BuiltinSignature {
1023                params: vec![TypeExpr::Unknown, TypeExpr::Unknown],
1024                return_type: TypeExpr::Unknown,
1025            },
1026            param_names: &["table", "meta"],
1027        },
1028    ]
1029}
1030
1031static STRING_FUNCTIONS: StaticOnceCell<Vec<BuiltinFunction>> = StaticOnceCell::new();
1032static TASK_FUNCTIONS: StaticOnceCell<Vec<BuiltinFunction>> = StaticOnceCell::new();
1033static LUA_FUNCTIONS: StaticOnceCell<Vec<BuiltinFunction>> = StaticOnceCell::new();
1034
1035fn build_task_functions() -> Vec<BuiltinFunction> {
1036    vec![
1037        BuiltinFunction {
1038            name: "task.run",
1039            description: "Run a function as a task",
1040            signature: BuiltinSignature {
1041                params: vec![TypeExpr::Unknown],
1042                return_type: TypeExpr::Named("Task"),
1043            },
1044            param_names: &["func"],
1045        },
1046        BuiltinFunction {
1047            name: "task.create",
1048            description: "Create a suspended task",
1049            signature: BuiltinSignature {
1050                params: vec![TypeExpr::Unknown],
1051                return_type: TypeExpr::Named("Task"),
1052            },
1053            param_names: &["func"],
1054        },
1055        BuiltinFunction {
1056            name: "task.status",
1057            description: "Get the status of a task",
1058            signature: BuiltinSignature {
1059                params: vec![TypeExpr::Named("Task")],
1060                return_type: TypeExpr::Named("TaskStatus"),
1061            },
1062            param_names: &["task"],
1063        },
1064        BuiltinFunction {
1065            name: "task.info",
1066            description: "Get detailed information about a task",
1067            signature: BuiltinSignature {
1068                params: vec![TypeExpr::Named("Task")],
1069                return_type: TypeExpr::Named("TaskInfo"),
1070            },
1071            param_names: &["task"],
1072        },
1073        BuiltinFunction {
1074            name: "task.resume",
1075            description: "Resume a suspended task",
1076            signature: BuiltinSignature {
1077                params: vec![TypeExpr::Named("Task")],
1078                return_type: TypeExpr::Named("TaskInfo"),
1079            },
1080            param_names: &["task"],
1081        },
1082        BuiltinFunction {
1083            name: "task.yield",
1084            description: "Yield from the current task",
1085            signature: BuiltinSignature {
1086                params: vec![TypeExpr::Unknown],
1087                return_type: TypeExpr::Unknown,
1088            },
1089            param_names: &["value"],
1090        },
1091        BuiltinFunction {
1092            name: "task.stop",
1093            description: "Stop a running task",
1094            signature: BuiltinSignature {
1095                params: vec![TypeExpr::Named("Task")],
1096                return_type: TypeExpr::Bool,
1097            },
1098            param_names: &["task"],
1099        },
1100        BuiltinFunction {
1101            name: "task.restart",
1102            description: "Restart a completed task",
1103            signature: BuiltinSignature {
1104                params: vec![TypeExpr::Named("Task")],
1105                return_type: TypeExpr::Named("TaskInfo"),
1106            },
1107            param_names: &["task"],
1108        },
1109        BuiltinFunction {
1110            name: "task.current",
1111            description: "Return the currently executing task",
1112            signature: BuiltinSignature {
1113                params: vec![],
1114                return_type: TypeExpr::Option(Box::new(TypeExpr::Named("Task"))),
1115            },
1116            param_names: &[],
1117        },
1118    ]
1119}
1120
1121fn build_lua_functions() -> Vec<BuiltinFunction> {
1122    vec![
1123        BuiltinFunction {
1124            name: "lua.to_value",
1125            description: "Wrap a Lust value in LuaValue",
1126            signature: BuiltinSignature {
1127                params: vec![TypeExpr::Unknown],
1128                return_type: TypeExpr::Named("LuaValue"),
1129            },
1130            param_names: &["value"],
1131        },
1132        BuiltinFunction {
1133            name: "lua.require",
1134            description: "Lua-style module resolver (loads from already-initialized globals when available)",
1135            signature: BuiltinSignature {
1136                params: vec![TypeExpr::Unknown],
1137                return_type: TypeExpr::Unknown,
1138            },
1139            param_names: &["name"],
1140        },
1141        BuiltinFunction {
1142            name: "lua.table",
1143            description: "Create an empty Lua-style table",
1144            signature: BuiltinSignature {
1145                params: vec![],
1146                return_type: TypeExpr::Named("LuaTable"),
1147            },
1148            param_names: &[],
1149        },
1150        BuiltinFunction {
1151            name: "lua.setmetatable",
1152            description: "Set the metatable for a Lua table value",
1153            signature: BuiltinSignature {
1154                params: vec![TypeExpr::Named("LuaValue"), TypeExpr::Named("LuaValue")],
1155                return_type: TypeExpr::Named("LuaValue"),
1156            },
1157            param_names: &["table", "meta"],
1158        },
1159        BuiltinFunction {
1160            name: "lua.getmetatable",
1161            description: "Get the metatable for a Lua table value",
1162            signature: BuiltinSignature {
1163                params: vec![TypeExpr::Named("LuaValue")],
1164                return_type: TypeExpr::Named("LuaValue"),
1165            },
1166            param_names: &["table"],
1167        },
1168        BuiltinFunction {
1169            name: "lua.unwrap",
1170            description: "Extract a raw Lust value from a LuaValue wrapper",
1171            signature: BuiltinSignature {
1172                params: vec![TypeExpr::Unknown],
1173                return_type: TypeExpr::Unknown,
1174            },
1175            param_names: &["value"],
1176        },
1177    ]
1178}
1179
1180fn build_string_functions() -> Vec<BuiltinFunction> {
1181    vec![
1182        BuiltinFunction {
1183            name: "string.len",
1184            description: "Return the length of a string",
1185            signature: BuiltinSignature {
1186                params: vec![TypeExpr::Unknown],
1187                return_type: TypeExpr::Int,
1188            },
1189            param_names: &["s"],
1190        },
1191        BuiltinFunction {
1192            name: "string.lower",
1193            description: "Lowercase a string",
1194            signature: BuiltinSignature {
1195                params: vec![TypeExpr::Unknown],
1196                return_type: TypeExpr::String,
1197            },
1198            param_names: &["s"],
1199        },
1200        BuiltinFunction {
1201            name: "string.upper",
1202            description: "Uppercase a string",
1203            signature: BuiltinSignature {
1204                params: vec![TypeExpr::Unknown],
1205                return_type: TypeExpr::String,
1206            },
1207            param_names: &["s"],
1208        },
1209        BuiltinFunction {
1210            name: "string.sub",
1211            description: "Extract a substring using Lua-style indices",
1212            signature: BuiltinSignature {
1213                params: vec![TypeExpr::Unknown, TypeExpr::Unknown, TypeExpr::Unknown],
1214                return_type: TypeExpr::String,
1215            },
1216            param_names: &["s", "i", "j"],
1217        },
1218        BuiltinFunction {
1219            name: "string.byte",
1220            description: "Return byte values from a string slice",
1221            signature: BuiltinSignature {
1222                params: vec![TypeExpr::Unknown, TypeExpr::Unknown, TypeExpr::Unknown],
1223                return_type: TypeExpr::Unknown,
1224            },
1225            param_names: &["s", "i", "j"],
1226        },
1227        BuiltinFunction {
1228            name: "string.char",
1229            description: "Create a string from numeric bytes",
1230            signature: BuiltinSignature {
1231                params: vec![TypeExpr::Unknown],
1232                return_type: TypeExpr::String,
1233            },
1234            param_names: &["byte"],
1235        },
1236        BuiltinFunction {
1237            name: "string.find",
1238            description: "Find a pattern within a string",
1239            signature: BuiltinSignature {
1240                params: vec![
1241                    TypeExpr::Unknown,
1242                    TypeExpr::Unknown,
1243                    TypeExpr::Unknown,
1244                    TypeExpr::Unknown,
1245                ],
1246                return_type: TypeExpr::Unknown,
1247            },
1248            param_names: &["s", "pattern", "init", "plain"],
1249        },
1250        BuiltinFunction {
1251            name: "string.gsub",
1252            description: "Globally substitute occurrences of a pattern",
1253            signature: BuiltinSignature {
1254                params: vec![
1255                    TypeExpr::Unknown,
1256                    TypeExpr::Unknown,
1257                    TypeExpr::Unknown,
1258                    TypeExpr::Unknown,
1259                ],
1260                return_type: TypeExpr::Unknown,
1261            },
1262            param_names: &["s", "pattern", "repl", "n"],
1263        },
1264        BuiltinFunction {
1265            name: "string.format",
1266            description: "Format values according to a pattern",
1267            signature: BuiltinSignature {
1268                params: vec![TypeExpr::Unknown],
1269                return_type: TypeExpr::String,
1270            },
1271            param_names: &["fmt"],
1272        },
1273    ]
1274}
1275
1276static IO_FUNCTIONS: StaticOnceCell<Vec<BuiltinFunction>> = StaticOnceCell::new();
1277
1278fn build_io_functions() -> Vec<BuiltinFunction> {
1279    vec![
1280        BuiltinFunction {
1281            name: "io.read_file",
1282            description: "Read the contents of a file",
1283            signature: BuiltinSignature {
1284                params: vec![TypeExpr::String],
1285                return_type: TypeExpr::Result(
1286                    Box::new(TypeExpr::String),
1287                    Box::new(TypeExpr::String),
1288                ),
1289            },
1290            param_names: &["path"],
1291        },
1292        BuiltinFunction {
1293            name: "io.read_file_bytes",
1294            description: "Read the contents of a file as byte values",
1295            signature: BuiltinSignature {
1296                params: vec![TypeExpr::String],
1297                return_type: TypeExpr::Result(
1298                    Box::new(TypeExpr::Array(Box::new(TypeExpr::Int))),
1299                    Box::new(TypeExpr::String),
1300                ),
1301            },
1302            param_names: &["path"],
1303        },
1304        BuiltinFunction {
1305            name: "io.write_file",
1306            description: "Write contents to a file",
1307            signature: BuiltinSignature {
1308                params: vec![TypeExpr::String, TypeExpr::Unknown],
1309                return_type: TypeExpr::Result(Box::new(TypeExpr::Unit), Box::new(TypeExpr::String)),
1310            },
1311            param_names: &["path", "value"],
1312        },
1313        BuiltinFunction {
1314            name: "io.read_stdin",
1315            description: "Read all available stdin",
1316            signature: BuiltinSignature {
1317                params: vec![],
1318                return_type: TypeExpr::Result(
1319                    Box::new(TypeExpr::String),
1320                    Box::new(TypeExpr::String),
1321                ),
1322            },
1323            param_names: &[],
1324        },
1325        BuiltinFunction {
1326            name: "io.read_line",
1327            description: "Read a single line from stdin",
1328            signature: BuiltinSignature {
1329                params: vec![],
1330                return_type: TypeExpr::Result(
1331                    Box::new(TypeExpr::String),
1332                    Box::new(TypeExpr::String),
1333                ),
1334            },
1335            param_names: &[],
1336        },
1337        BuiltinFunction {
1338            name: "io.write_stdout",
1339            description: "Write a value to stdout",
1340            signature: BuiltinSignature {
1341                params: vec![TypeExpr::Unknown],
1342                return_type: TypeExpr::Result(Box::new(TypeExpr::Unit), Box::new(TypeExpr::String)),
1343            },
1344            param_names: &["value"],
1345        },
1346    ]
1347}
1348
1349static OS_FUNCTIONS: StaticOnceCell<Vec<BuiltinFunction>> = StaticOnceCell::new();
1350
1351fn build_os_functions() -> Vec<BuiltinFunction> {
1352    vec![
1353        BuiltinFunction {
1354            name: "os.time",
1355            description: "Get the current UNIX timestamp with sub-second precision",
1356            signature: BuiltinSignature {
1357                params: vec![],
1358                return_type: TypeExpr::Float,
1359            },
1360            param_names: &[],
1361        },
1362        BuiltinFunction {
1363            name: "os.sleep",
1364            description: "Sleep for the given number of seconds",
1365            signature: BuiltinSignature {
1366                params: vec![TypeExpr::Float],
1367                return_type: TypeExpr::Result(Box::new(TypeExpr::Unit), Box::new(TypeExpr::String)),
1368            },
1369            param_names: &["seconds"],
1370        },
1371        BuiltinFunction {
1372            name: "os.create_file",
1373            description: "Create an empty file on disk",
1374            signature: BuiltinSignature {
1375                params: vec![TypeExpr::String],
1376                return_type: TypeExpr::Result(Box::new(TypeExpr::Unit), Box::new(TypeExpr::String)),
1377            },
1378            param_names: &["path"],
1379        },
1380        BuiltinFunction {
1381            name: "os.create_dir",
1382            description: "Create a directory",
1383            signature: BuiltinSignature {
1384                params: vec![TypeExpr::String],
1385                return_type: TypeExpr::Result(Box::new(TypeExpr::Unit), Box::new(TypeExpr::String)),
1386            },
1387            param_names: &["path"],
1388        },
1389        BuiltinFunction {
1390            name: "os.remove_file",
1391            description: "Remove a file from disk",
1392            signature: BuiltinSignature {
1393                params: vec![TypeExpr::String],
1394                return_type: TypeExpr::Result(Box::new(TypeExpr::Unit), Box::new(TypeExpr::String)),
1395            },
1396            param_names: &["path"],
1397        },
1398        BuiltinFunction {
1399            name: "os.remove_dir",
1400            description: "Remove an empty directory",
1401            signature: BuiltinSignature {
1402                params: vec![TypeExpr::String],
1403                return_type: TypeExpr::Result(Box::new(TypeExpr::Unit), Box::new(TypeExpr::String)),
1404            },
1405            param_names: &["path"],
1406        },
1407        BuiltinFunction {
1408            name: "os.rename",
1409            description: "Rename or move a path",
1410            signature: BuiltinSignature {
1411                params: vec![TypeExpr::String, TypeExpr::String],
1412                return_type: TypeExpr::Result(Box::new(TypeExpr::Unit), Box::new(TypeExpr::String)),
1413            },
1414            param_names: &["from", "to"],
1415        },
1416    ]
1417}
1418
1419static BUILTIN_METHODS: StaticOnceCell<Vec<BuiltinMethod>> = StaticOnceCell::new();
1420
1421fn build_builtin_methods() -> Vec<BuiltinMethod> {
1422    let mut methods = Vec::new();
1423    methods.extend(string_methods());
1424    methods.extend(array_methods());
1425    methods.extend(map_methods());
1426    methods.extend(lua_table_methods());
1427    methods.extend(iterator_methods());
1428    methods.extend(option_methods());
1429    methods.extend(result_methods());
1430    methods.extend(float_methods());
1431    methods.extend(int_methods());
1432    methods
1433}
1434
1435pub fn base_functions() -> &'static [BuiltinFunction] {
1436    BASE_FUNCTIONS.get_or_init(build_base_functions).as_slice()
1437}
1438
1439pub fn string_functions() -> &'static [BuiltinFunction] {
1440    STRING_FUNCTIONS.get_or_init(build_string_functions).as_slice()
1441}
1442
1443pub fn task_functions() -> &'static [BuiltinFunction] {
1444    TASK_FUNCTIONS.get_or_init(build_task_functions).as_slice()
1445}
1446
1447pub fn lua_functions() -> &'static [BuiltinFunction] {
1448    LUA_FUNCTIONS.get_or_init(build_lua_functions).as_slice()
1449}
1450
1451pub fn io_functions() -> &'static [BuiltinFunction] {
1452    IO_FUNCTIONS.get_or_init(build_io_functions).as_slice()
1453}
1454
1455pub fn os_functions() -> &'static [BuiltinFunction] {
1456    OS_FUNCTIONS.get_or_init(build_os_functions).as_slice()
1457}
1458
1459pub fn builtin_methods() -> &'static [BuiltinMethod] {
1460    BUILTIN_METHODS
1461        .get_or_init(build_builtin_methods)
1462        .as_slice()
1463}
1464
1465pub struct BuiltinModule {
1466    name: &'static str,
1467    description: &'static str,
1468    functions: Vec<&'static BuiltinFunction>,
1469}
1470
1471impl BuiltinModule {
1472    pub fn name(&self) -> &'static str {
1473        self.name
1474    }
1475
1476    pub fn description(&self) -> &'static str {
1477        self.description
1478    }
1479
1480    pub fn functions(&self) -> &[&'static BuiltinFunction] {
1481        &self.functions
1482    }
1483}
1484
1485pub struct BuiltinsDatabase {
1486    global_functions: Vec<&'static BuiltinFunction>,
1487    modules: BTreeMap<&'static str, BuiltinModule>,
1488    methods: HashMap<&'static str, Vec<&'static BuiltinMethod>>,
1489}
1490
1491impl BuiltinsDatabase {
1492    pub fn global_functions(&self) -> &[&'static BuiltinFunction] {
1493        &self.global_functions
1494    }
1495
1496    pub fn module(&self, name: &str) -> Option<&BuiltinModule> {
1497        self.modules.get(name)
1498    }
1499
1500    pub fn methods_for(&self, type_name: &str) -> Option<&[&'static BuiltinMethod]> {
1501        self.methods
1502            .get(type_name)
1503            .map(|methods| methods.as_slice())
1504    }
1505
1506    pub fn modules(&self) -> impl Iterator<Item = &BuiltinModule> {
1507        self.modules.values()
1508    }
1509}
1510
1511fn receiver_key(expr: &TypeExpr) -> Option<&'static str> {
1512    match expr {
1513        TypeExpr::String => Some("String"),
1514        TypeExpr::Array(_) => Some("Array"),
1515        TypeExpr::Map(_, _) => Some("Map"),
1516        TypeExpr::Named(name) => Some(name),
1517        TypeExpr::Option(_) => Some("Option"),
1518        TypeExpr::Result(_, _) => Some("Result"),
1519        TypeExpr::Float => Some("Float"),
1520        TypeExpr::Int => Some("Int"),
1521        TypeExpr::Bool => Some("Bool"),
1522        TypeExpr::Unknown => Some("Unknown"),
1523        TypeExpr::Unit => Some("Unit"),
1524        TypeExpr::Generic(name) => Some(name),
1525        TypeExpr::SelfType => Some("Self"),
1526        TypeExpr::Function { .. } => Some("function"),
1527    }
1528}
1529
1530static BUILTINS_DATABASE: StaticOnceCell<BuiltinsDatabase> = StaticOnceCell::new();
1531
1532fn build_builtins_database() -> BuiltinsDatabase {
1533    let mut modules: BTreeMap<&'static str, BuiltinModule> = BTreeMap::new();
1534    let module_specs: [(&'static str, &'static str, &'static [BuiltinFunction]); 4] = [
1535        ("task", "task runtime module", task_functions()),
1536        ("io", "io file & console module", io_functions()),
1537        ("os", "os filesystem module", os_functions()),
1538        ("string", "string compatibility module", string_functions()),
1539    ];
1540    for (name, description, functions) in module_specs {
1541        let mut module_funcs: Vec<&'static BuiltinFunction> = functions.iter().collect();
1542        module_funcs.sort_by(|a, b| a.name.cmp(b.name));
1543        modules.insert(
1544            name,
1545            BuiltinModule {
1546                name,
1547                description,
1548                functions: module_funcs,
1549            },
1550        );
1551    }
1552
1553    let mut global_functions: Vec<&'static BuiltinFunction> = base_functions().iter().collect();
1554    global_functions.sort_by(|a, b| a.name.cmp(b.name));
1555
1556    let mut methods: HashMap<&'static str, Vec<&'static BuiltinMethod>> = HashMap::new();
1557    for method in builtin_methods() {
1558        if let Some(key) = receiver_key(&method.receiver) {
1559            methods.entry(key).or_default().push(method);
1560        }
1561    }
1562    for vec in methods.values_mut() {
1563        vec.sort_by(|a, b| a.name.cmp(b.name));
1564    }
1565
1566    BuiltinsDatabase {
1567        global_functions,
1568        modules,
1569        methods,
1570    }
1571}
1572
1573pub fn builtins() -> &'static BuiltinsDatabase {
1574    BUILTINS_DATABASE.get_or_init(build_builtins_database)
1575}
1576
1577pub fn lookup_builtin_method(
1578    receiver: &Type,
1579    name: &str,
1580) -> Option<(&'static BuiltinMethod, HashMap<&'static str, Type>)> {
1581    for method in builtin_methods() {
1582        if method.name == name {
1583            if let Some(bindings) = match_receiver(&method.receiver, receiver) {
1584                return Some((method, bindings));
1585            }
1586        }
1587    }
1588    None
1589}