Skip to main content

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}