smelt_stdlib/classes.rs
1//! Synthetic standard-library class recognition shared by frontends and codegen.
2
3/// Standard-library class modeled with dedicated runtime support instead of
4/// user-defined struct emission.
5#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
6#[non_exhaustive]
7pub enum StdlibClass {
8 /// JavaScript `Date`, represented by timestamp and date helper operations.
9 Date,
10 /// JavaScript `Map`, represented by dictionary HIR values.
11 Map,
12 /// Synthetic `RegExp` match result (`RegExp.exec` / `String.matchAll`),
13 /// backed by the generated concrete `SmeltMatch` runtime type. Consumer
14 /// reads (`m[0]`, `m.index`, `m.input`, `m.groups.name`) resolve to typed
15 /// accessors on `SmeltMatch` instead of the erased `SmeltUnknown` path.
16 Match,
17 /// Synthetic named-capture-group accessor for `matchResult.groups`. It is
18 /// the same underlying `SmeltMatch` value; a `.name` read on it resolves to
19 /// the typed named-group accessor.
20 MatchGroups,
21 /// Remeda parser helper result class synthesized during TypeScript lowering.
22 MatchFnResult,
23 /// JavaScript `RegExp`, backed by the generated regex runtime shim.
24 RegExp,
25 /// JavaScript `Set`, represented by set HIR values.
26 Set,
27}
28
29/// Reserved synthetic class name for a `RegExp` match result value.
30///
31/// The name is not writable in user TypeScript (double-underscore prefix), so
32/// it never collides with a source class; it exists only to carry the concrete
33/// match shape through the internal type system.
34pub const MATCH_CLASS_NAME: &str = "__SmeltMatch";
35
36/// Reserved synthetic class name for `matchResult.groups` named-group access.
37pub const MATCH_GROUPS_CLASS_NAME: &str = "__SmeltMatchGroups";
38
39/// Return the stdlib class modeled by a TypeScript class type name.
40///
41/// Codegen consults this instead of comparing class symbol names inline so
42/// stdlib class identities stay in one registry.
43#[must_use]
44pub fn typescript_stdlib_class(name: &str) -> Option<StdlibClass> {
45 match name {
46 "Date" => Some(StdlibClass::Date),
47 "Map" => Some(StdlibClass::Map),
48 MATCH_CLASS_NAME => Some(StdlibClass::Match),
49 MATCH_GROUPS_CLASS_NAME => Some(StdlibClass::MatchGroups),
50 "MatchFnResult" => Some(StdlibClass::MatchFnResult),
51 "RegExp" => Some(StdlibClass::RegExp),
52 "Set" => Some(StdlibClass::Set),
53 _ => None,
54 }
55}
56
57/// The JavaScript typed-array view constructor names, in a stable order.
58///
59/// These are the eleven `TypedArray` element-view classes (`Uint8Array`,
60/// `Int8Array`, ..., `BigInt64Array`, `BigUint64Array`). Smelt models a typed
61/// array as a plain numeric list (so `.length`, integer indexing, iteration and
62/// value-equality reuse the list machinery), while this shared name list is the
63/// single source of truth every frontend/codegen site consults to decide
64/// whether a constructor / bare identifier / `instanceof` target names a typed
65/// array. Keeping the set here — rather than re-spelling the `matches!(...)` arm
66/// in each site — stops the construction side, the value-resolution side, and
67/// the `instanceof` side from drifting apart.
68pub const TYPED_ARRAY_CLASS_NAMES: [&str; 11] = [
69 "Int8Array",
70 "Uint8Array",
71 "Uint8ClampedArray",
72 "Int16Array",
73 "Uint16Array",
74 "Int32Array",
75 "Uint32Array",
76 "Float32Array",
77 "Float64Array",
78 "BigInt64Array",
79 "BigUint64Array",
80];
81
82/// Return whether a class name is one of the JavaScript typed-array views.
83///
84/// Consulted by the TypeScript frontend (constructor lowering, bare-value
85/// resolution, `instanceof` targeting) and codegen so the eleven typed-array
86/// names are recognized from one place. `BigInt64Array` / `BigUint64Array` are
87/// included even though their elements are `BigInt` values: Smelt's minimum-viable
88/// typed array model backs every view with the same numeric list, so they share
89/// the recognizer with the numeric views.
90#[must_use]
91pub fn is_typed_array_class_name(name: &str) -> bool {
92 TYPED_ARRAY_CLASS_NAMES.contains(&name)
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98
99 /// Exact stdlib class names resolve to their registry identity.
100 #[test]
101 fn recognizes_stdlib_class_names() {
102 assert_eq!(typescript_stdlib_class("Date"), Some(StdlibClass::Date));
103 assert_eq!(typescript_stdlib_class("Map"), Some(StdlibClass::Map));
104 assert_eq!(
105 typescript_stdlib_class(MATCH_CLASS_NAME),
106 Some(StdlibClass::Match)
107 );
108 assert_eq!(
109 typescript_stdlib_class(MATCH_GROUPS_CLASS_NAME),
110 Some(StdlibClass::MatchGroups)
111 );
112 assert_eq!(
113 typescript_stdlib_class("MatchFnResult"),
114 Some(StdlibClass::MatchFnResult)
115 );
116 assert_eq!(typescript_stdlib_class("RegExp"), Some(StdlibClass::RegExp));
117 assert_eq!(typescript_stdlib_class("Set"), Some(StdlibClass::Set));
118 }
119
120 /// User class names never resolve to a stdlib identity.
121 #[test]
122 fn rejects_user_class_names() {
123 for name in ["Regexp", "RegExpLike", "Dates", "HashMap", "MyClass"] {
124 assert_eq!(typescript_stdlib_class(name), None);
125 }
126 }
127
128 /// Every typed-array view constructor is recognized, including the BigInt
129 /// views, while plain `Array` and lookalikes are not.
130 #[test]
131 fn recognizes_typed_array_class_names() {
132 for name in TYPED_ARRAY_CLASS_NAMES {
133 assert!(
134 is_typed_array_class_name(name),
135 "expected `{name}` to be recognized as a typed array"
136 );
137 }
138 assert!(is_typed_array_class_name("BigUint64Array"));
139 for name in ["Array", "Uint8", "TypedArray", "Float16Array", "MyUint8Array"] {
140 assert!(
141 !is_typed_array_class_name(name),
142 "did not expect `{name}` to be recognized as a typed array"
143 );
144 }
145 }
146}