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