Skip to main content

harn_parser/typechecker/
method_registry.rs

1//! Canonical builtin-method name tables.
2//!
3//! These lists mirror the method-dispatch match arms in the VM
4//! (`crates/harn-vm/src/vm/methods/*.rs`). They are the single source of
5//! truth the typechecker uses to decide whether a `receiver.method()` call
6//! names a method that actually exists on a *concrete* builtin receiver, so
7//! that `"x".frobnicate()` / `(3.14).frobnicate()` fail `harn check` instead
8//! of crashing (strings/lists/sets) or silently returning `nil` (numbers) at
9//! runtime.
10//!
11//! Drift guard: the `drift_guard_tests` in `harn-vm`'s `methods/dispatch.rs`
12//! assert both directions. Every name here must be accepted by the
13//! corresponding VM dispatch (a stale entry would let a crashing call pass
14//! `harn check`), and every VM dispatch arm must be listed here (a missing
15//! entry would be a false "unknown method"). When you add a method to a VM
16//! dispatch, add its name to the matching list below.
17
18/// `string` receiver methods (`call_string_method`), plus the universal
19/// `.iter()` bridge that `call_method_sync` handles for every iterable.
20pub const STRING_METHODS: &[&str] = &[
21    "count",
22    "empty",
23    "contains",
24    "includes",
25    "replace",
26    "split",
27    "trim",
28    "starts_with",
29    "ends_with",
30    "lowercase",
31    "uppercase",
32    "substring",
33    "slice",
34    "index_of",
35    "chars",
36    "repeat",
37    "reversed",
38    "reverse",
39    "pad_left",
40    "pad_right",
41    "trim_start",
42    "trim_end",
43    "lines",
44    "char_at",
45    "last_index_of",
46    "rfind",
47    "lower",
48    "to_lower",
49    "upper",
50    "to_upper",
51    "len",
52    "iter",
53];
54
55/// `list` receiver methods (`call_list_method_sync` + `call_list_method`),
56/// plus `.iter()`.
57pub const LIST_METHODS: &[&str] = &[
58    "count",
59    "empty",
60    "map",
61    "filter",
62    "find",
63    "flat_map",
64    "sorted_by",
65    "sort_by",
66    "partition",
67    "group_by",
68    "reduce",
69    "any",
70    "all",
71    "every",
72    "all?",
73    "sorted",
74    "sort",
75    "reversed",
76    "reverse",
77    "join",
78    "contains",
79    "includes",
80    "index_of",
81    "enumerate",
82    "zip",
83    "slice",
84    "unique",
85    "take",
86    "skip",
87    "sum",
88    "min",
89    "max",
90    "flatten",
91    "appending",
92    "push",
93    "dropping_last",
94    "pop",
95    "none",
96    "none?",
97    "find_index",
98    "first",
99    "last",
100    "chunk",
101    "each_slice",
102    "min_by",
103    "max_by",
104    "compact",
105    "window",
106    "each_cons",
107    "sliding_window",
108    "tally",
109    "to_list",
110    "to_set",
111    "take_while",
112    "drop_while",
113    "count_by",
114    "iter",
115];
116
117/// `set` receiver methods (`call_set_method_sync` + `call_set_method`), plus
118/// `.iter()`.
119pub const SET_METHODS: &[&str] = &[
120    "count",
121    "len",
122    "empty",
123    "contains",
124    "includes",
125    "adding",
126    "add",
127    "removing",
128    "remove",
129    "delete",
130    "union",
131    "intersect",
132    "intersection",
133    "difference",
134    "symmetric_difference",
135    "is_subset",
136    "is_superset",
137    "is_disjoint",
138    "to_list",
139    "to_set",
140    "map",
141    "filter",
142    "any",
143    "all",
144    "every",
145    "iter",
146];
147
148/// `dict` receiver methods (`call_dict_method_sync` + `call_dict_method`),
149/// plus `.iter()`. Dicts are *not* directly existence-checked (a dict may
150/// store a callable under a key and be invoked as `d.field()`), but their
151/// method names still belong to the recognized-method universe.
152pub const DICT_METHODS: &[&str] = &[
153    "keys",
154    "values",
155    "entries",
156    "count",
157    "has",
158    "merging",
159    "merge",
160    "map_values",
161    "rekeyed",
162    "rekey",
163    "map_keys",
164    "filter",
165    "removing",
166    "remove",
167    "get",
168    "to_dict",
169    "to_list",
170    "iter",
171];
172
173/// `range` receiver methods (`call_range_method_sync`), plus `.iter()`.
174pub const RANGE_METHODS: &[&str] = &[
175    "len",
176    "count",
177    "empty",
178    "contains",
179    "includes",
180    "first",
181    "last",
182    "to_string",
183    "iter",
184];
185
186/// `Iter<T>` combinators and sinks (`call_iter_method` + the lazy-iter arms
187/// in `infer_type`). Part of the recognized-method universe.
188pub const ITER_METHODS: &[&str] = &[
189    "iter",
190    "map",
191    "flat_map",
192    "filter",
193    "take",
194    "skip",
195    "take_while",
196    "skip_while",
197    "zip",
198    "enumerate",
199    "chain",
200    "chunks",
201    "windows",
202    "to_list",
203    "to_set",
204    "to_dict",
205    "count",
206    "sum",
207    "min",
208    "max",
209    "first",
210    "last",
211    "find",
212    "any",
213    "all",
214    "for_each",
215    "reduce",
216];
217
218/// Generator / stream drive methods (`call_generator_method`).
219pub const GENERATOR_METHODS: &[&str] = &["done", "next", "value"];
220
221/// True when `method` names a method on *any* builtin receiver. Used for the
222/// permissive tier (`int` / `float` / `bool`), whose VM dispatch either
223/// returns `nil` for every name (numbers) or has no methods at all (bool):
224/// there is no closed per-type set to check against, so we only reject names
225/// that are unknown everywhere — enough to catch typos like `frobnicate`
226/// without ever flagging a real method name.
227pub(super) fn is_any_known_builtin_method(method: &str) -> bool {
228    STRING_METHODS.contains(&method)
229        || LIST_METHODS.contains(&method)
230        || SET_METHODS.contains(&method)
231        || DICT_METHODS.contains(&method)
232        || RANGE_METHODS.contains(&method)
233        || ITER_METHODS.contains(&method)
234        || GENERATOR_METHODS.contains(&method)
235}