Skip to main content

tsz_scanner/
lib.rs

1//! Scanner types and utilities for TypeScript lexical analysis.
2//!
3//! This module contains the `SyntaxKind` enum, scanner implementation,
4//! and character code constants for TypeScript lexical analysis.
5// Scanner implementation - tokenization logic
6pub mod scanner_impl;
7
8// Character code constants used by the scanner
9pub mod char_codes;
10
11use serde::Serialize;
12use wasm_bindgen::prelude::wasm_bindgen;
13
14// =============================================================================
15// SyntaxKind Enum - Token Types (Scanner Output)
16// =============================================================================
17
18/// Syntax kind enum matching TypeScript's `SyntaxKind`.
19/// This enum contains only the token types produced by the scanner (0-186).
20/// AST node types are not included here.
21#[wasm_bindgen]
22#[repr(u16)]
23#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Hash, Serialize)]
24pub enum SyntaxKind {
25    Unknown = 0,
26    EndOfFileToken = 1,
27    SingleLineCommentTrivia = 2,
28    MultiLineCommentTrivia = 3,
29    NewLineTrivia = 4,
30    WhitespaceTrivia = 5,
31    ShebangTrivia = 6,
32    ConflictMarkerTrivia = 7,
33    NonTextFileMarkerTrivia = 8,
34    // Literals
35    NumericLiteral = 9,
36    BigIntLiteral = 10,
37    StringLiteral = 11,
38    JsxText = 12,
39    JsxTextAllWhiteSpaces = 13,
40    RegularExpressionLiteral = 14,
41    NoSubstitutionTemplateLiteral = 15,
42    // Pseudo-literals
43    TemplateHead = 16,
44    TemplateMiddle = 17,
45    TemplateTail = 18,
46    // Punctuation
47    OpenBraceToken = 19,
48    CloseBraceToken = 20,
49    OpenParenToken = 21,
50    CloseParenToken = 22,
51    OpenBracketToken = 23,
52    CloseBracketToken = 24,
53    DotToken = 25,
54    DotDotDotToken = 26,
55    SemicolonToken = 27,
56    CommaToken = 28,
57    QuestionDotToken = 29,
58    LessThanToken = 30,
59    LessThanSlashToken = 31,
60    GreaterThanToken = 32,
61    LessThanEqualsToken = 33,
62    GreaterThanEqualsToken = 34,
63    EqualsEqualsToken = 35,
64    ExclamationEqualsToken = 36,
65    EqualsEqualsEqualsToken = 37,
66    ExclamationEqualsEqualsToken = 38,
67    EqualsGreaterThanToken = 39,
68    PlusToken = 40,
69    MinusToken = 41,
70    AsteriskToken = 42,
71    AsteriskAsteriskToken = 43,
72    SlashToken = 44,
73    PercentToken = 45,
74    PlusPlusToken = 46,
75    MinusMinusToken = 47,
76    LessThanLessThanToken = 48,
77    GreaterThanGreaterThanToken = 49,
78    GreaterThanGreaterThanGreaterThanToken = 50,
79    AmpersandToken = 51,
80    BarToken = 52,
81    CaretToken = 53,
82    ExclamationToken = 54,
83    TildeToken = 55,
84    AmpersandAmpersandToken = 56,
85    BarBarToken = 57,
86    QuestionToken = 58,
87    ColonToken = 59,
88    AtToken = 60,
89    QuestionQuestionToken = 61,
90    BacktickToken = 62,
91    HashToken = 63,
92    // Assignments
93    EqualsToken = 64,
94    PlusEqualsToken = 65,
95    MinusEqualsToken = 66,
96    AsteriskEqualsToken = 67,
97    AsteriskAsteriskEqualsToken = 68,
98    SlashEqualsToken = 69,
99    PercentEqualsToken = 70,
100    LessThanLessThanEqualsToken = 71,
101    GreaterThanGreaterThanEqualsToken = 72,
102    GreaterThanGreaterThanGreaterThanEqualsToken = 73,
103    AmpersandEqualsToken = 74,
104    BarEqualsToken = 75,
105    BarBarEqualsToken = 76,
106    AmpersandAmpersandEqualsToken = 77,
107    QuestionQuestionEqualsToken = 78,
108    CaretEqualsToken = 79,
109    // Identifiers
110    Identifier = 80,
111    PrivateIdentifier = 81,
112    JSDocCommentTextToken = 82,
113    // Reserved words (keywords)
114    BreakKeyword = 83,
115    CaseKeyword = 84,
116    CatchKeyword = 85,
117    ClassKeyword = 86,
118    ConstKeyword = 87,
119    ContinueKeyword = 88,
120    DebuggerKeyword = 89,
121    DefaultKeyword = 90,
122    DeleteKeyword = 91,
123    DoKeyword = 92,
124    ElseKeyword = 93,
125    EnumKeyword = 94,
126    ExportKeyword = 95,
127    ExtendsKeyword = 96,
128    FalseKeyword = 97,
129    FinallyKeyword = 98,
130    ForKeyword = 99,
131    FunctionKeyword = 100,
132    IfKeyword = 101,
133    ImportKeyword = 102,
134    InKeyword = 103,
135    InstanceOfKeyword = 104,
136    NewKeyword = 105,
137    NullKeyword = 106,
138    ReturnKeyword = 107,
139    SuperKeyword = 108,
140    SwitchKeyword = 109,
141    ThisKeyword = 110,
142    ThrowKeyword = 111,
143    TrueKeyword = 112,
144    TryKeyword = 113,
145    TypeOfKeyword = 114,
146    VarKeyword = 115,
147    VoidKeyword = 116,
148    WhileKeyword = 117,
149    WithKeyword = 118,
150    // Strict mode reserved words
151    ImplementsKeyword = 119,
152    InterfaceKeyword = 120,
153    LetKeyword = 121,
154    PackageKeyword = 122,
155    PrivateKeyword = 123,
156    ProtectedKeyword = 124,
157    PublicKeyword = 125,
158    StaticKeyword = 126,
159    YieldKeyword = 127,
160    // Contextual keywords
161    AbstractKeyword = 128,
162    AccessorKeyword = 129,
163    AsKeyword = 130,
164    AssertsKeyword = 131,
165    AssertKeyword = 132,
166    AnyKeyword = 133,
167    AsyncKeyword = 134,
168    AwaitKeyword = 135,
169    BooleanKeyword = 136,
170    ConstructorKeyword = 137,
171    DeclareKeyword = 138,
172    GetKeyword = 139,
173    InferKeyword = 140,
174    IntrinsicKeyword = 141,
175    IsKeyword = 142,
176    KeyOfKeyword = 143,
177    ModuleKeyword = 144,
178    NamespaceKeyword = 145,
179    NeverKeyword = 146,
180    OutKeyword = 147,
181    ReadonlyKeyword = 148,
182    RequireKeyword = 149,
183    NumberKeyword = 150,
184    ObjectKeyword = 151,
185    SatisfiesKeyword = 152,
186    SetKeyword = 153,
187    StringKeyword = 154,
188    SymbolKeyword = 155,
189    TypeKeyword = 156,
190    UndefinedKeyword = 157,
191    UniqueKeyword = 158,
192    UnknownKeyword = 159,
193    UsingKeyword = 160,
194    FromKeyword = 161,
195    GlobalKeyword = 162,
196    BigIntKeyword = 163,
197    OverrideKeyword = 164,
198    OfKeyword = 165,
199    DeferKeyword = 166, // LastKeyword and LastToken
200}
201
202// =============================================================================
203// SyntaxKind Constants
204// =============================================================================
205
206impl SyntaxKind {
207    pub const FIRST_TOKEN: Self = Self::Unknown;
208    pub const LAST_TOKEN: Self = Self::DeferKeyword;
209    pub const FIRST_KEYWORD: Self = Self::BreakKeyword;
210    pub const LAST_KEYWORD: Self = Self::DeferKeyword;
211    pub const FIRST_PUNCTUATION: Self = Self::OpenBraceToken;
212    pub const LAST_PUNCTUATION: Self = Self::CaretEqualsToken;
213    pub const FIRST_LITERAL_TOKEN: Self = Self::NumericLiteral;
214    pub const LAST_LITERAL_TOKEN: Self = Self::NoSubstitutionTemplateLiteral;
215    pub const FIRST_TEMPLATE_TOKEN: Self = Self::NoSubstitutionTemplateLiteral;
216    pub const LAST_TEMPLATE_TOKEN: Self = Self::TemplateTail;
217    pub const FIRST_RESERVED_WORD: Self = Self::BreakKeyword;
218    pub const LAST_RESERVED_WORD: Self = Self::WithKeyword;
219    pub const FIRST_FUTURE_RESERVED_WORD: Self = Self::ImplementsKeyword;
220    pub const LAST_FUTURE_RESERVED_WORD: Self = Self::YieldKeyword;
221
222    /// Safely convert a u16 to `SyntaxKind` if it's a valid token kind.
223    /// Returns None for extended syntax kinds (AST nodes > 166).
224    #[must_use]
225    pub fn try_from_u16(value: u16) -> Option<Self> {
226        // Static assertion: SyntaxKind must be repr(u16) and same size as u16
227        const _: () = assert!(
228            std::mem::size_of::<SyntaxKind>() == std::mem::size_of::<u16>(),
229            "SyntaxKind must be same size as u16 for safe conversion"
230        );
231        // Valid token range is 0 to LAST_TOKEN (Unknown to DeferKeyword).
232        if value <= Self::LAST_TOKEN as u16 {
233            KIND_BY_VALUE.get(value as usize).copied()
234        } else {
235            None
236        }
237    }
238}
239
240const KIND_BY_VALUE: [SyntaxKind; 167] = [
241    SyntaxKind::Unknown,
242    SyntaxKind::EndOfFileToken,
243    SyntaxKind::SingleLineCommentTrivia,
244    SyntaxKind::MultiLineCommentTrivia,
245    SyntaxKind::NewLineTrivia,
246    SyntaxKind::WhitespaceTrivia,
247    SyntaxKind::ShebangTrivia,
248    SyntaxKind::ConflictMarkerTrivia,
249    SyntaxKind::NonTextFileMarkerTrivia,
250    SyntaxKind::NumericLiteral,
251    SyntaxKind::BigIntLiteral,
252    SyntaxKind::StringLiteral,
253    SyntaxKind::JsxText,
254    SyntaxKind::JsxTextAllWhiteSpaces,
255    SyntaxKind::RegularExpressionLiteral,
256    SyntaxKind::NoSubstitutionTemplateLiteral,
257    SyntaxKind::TemplateHead,
258    SyntaxKind::TemplateMiddle,
259    SyntaxKind::TemplateTail,
260    SyntaxKind::OpenBraceToken,
261    SyntaxKind::CloseBraceToken,
262    SyntaxKind::OpenParenToken,
263    SyntaxKind::CloseParenToken,
264    SyntaxKind::OpenBracketToken,
265    SyntaxKind::CloseBracketToken,
266    SyntaxKind::DotToken,
267    SyntaxKind::DotDotDotToken,
268    SyntaxKind::SemicolonToken,
269    SyntaxKind::CommaToken,
270    SyntaxKind::QuestionDotToken,
271    SyntaxKind::LessThanToken,
272    SyntaxKind::LessThanSlashToken,
273    SyntaxKind::GreaterThanToken,
274    SyntaxKind::LessThanEqualsToken,
275    SyntaxKind::GreaterThanEqualsToken,
276    SyntaxKind::EqualsEqualsToken,
277    SyntaxKind::ExclamationEqualsToken,
278    SyntaxKind::EqualsEqualsEqualsToken,
279    SyntaxKind::ExclamationEqualsEqualsToken,
280    SyntaxKind::EqualsGreaterThanToken,
281    SyntaxKind::PlusToken,
282    SyntaxKind::MinusToken,
283    SyntaxKind::AsteriskToken,
284    SyntaxKind::AsteriskAsteriskToken,
285    SyntaxKind::SlashToken,
286    SyntaxKind::PercentToken,
287    SyntaxKind::PlusPlusToken,
288    SyntaxKind::MinusMinusToken,
289    SyntaxKind::LessThanLessThanToken,
290    SyntaxKind::GreaterThanGreaterThanToken,
291    SyntaxKind::GreaterThanGreaterThanGreaterThanToken,
292    SyntaxKind::AmpersandToken,
293    SyntaxKind::BarToken,
294    SyntaxKind::CaretToken,
295    SyntaxKind::ExclamationToken,
296    SyntaxKind::TildeToken,
297    SyntaxKind::AmpersandAmpersandToken,
298    SyntaxKind::BarBarToken,
299    SyntaxKind::QuestionToken,
300    SyntaxKind::ColonToken,
301    SyntaxKind::AtToken,
302    SyntaxKind::QuestionQuestionToken,
303    SyntaxKind::BacktickToken,
304    SyntaxKind::HashToken,
305    SyntaxKind::EqualsToken,
306    SyntaxKind::PlusEqualsToken,
307    SyntaxKind::MinusEqualsToken,
308    SyntaxKind::AsteriskEqualsToken,
309    SyntaxKind::AsteriskAsteriskEqualsToken,
310    SyntaxKind::SlashEqualsToken,
311    SyntaxKind::PercentEqualsToken,
312    SyntaxKind::LessThanLessThanEqualsToken,
313    SyntaxKind::GreaterThanGreaterThanEqualsToken,
314    SyntaxKind::GreaterThanGreaterThanGreaterThanEqualsToken,
315    SyntaxKind::AmpersandEqualsToken,
316    SyntaxKind::BarEqualsToken,
317    SyntaxKind::BarBarEqualsToken,
318    SyntaxKind::AmpersandAmpersandEqualsToken,
319    SyntaxKind::QuestionQuestionEqualsToken,
320    SyntaxKind::CaretEqualsToken,
321    SyntaxKind::Identifier,
322    SyntaxKind::PrivateIdentifier,
323    SyntaxKind::JSDocCommentTextToken,
324    SyntaxKind::BreakKeyword,
325    SyntaxKind::CaseKeyword,
326    SyntaxKind::CatchKeyword,
327    SyntaxKind::ClassKeyword,
328    SyntaxKind::ConstKeyword,
329    SyntaxKind::ContinueKeyword,
330    SyntaxKind::DebuggerKeyword,
331    SyntaxKind::DefaultKeyword,
332    SyntaxKind::DeleteKeyword,
333    SyntaxKind::DoKeyword,
334    SyntaxKind::ElseKeyword,
335    SyntaxKind::EnumKeyword,
336    SyntaxKind::ExportKeyword,
337    SyntaxKind::ExtendsKeyword,
338    SyntaxKind::FalseKeyword,
339    SyntaxKind::FinallyKeyword,
340    SyntaxKind::ForKeyword,
341    SyntaxKind::FunctionKeyword,
342    SyntaxKind::IfKeyword,
343    SyntaxKind::ImportKeyword,
344    SyntaxKind::InKeyword,
345    SyntaxKind::InstanceOfKeyword,
346    SyntaxKind::NewKeyword,
347    SyntaxKind::NullKeyword,
348    SyntaxKind::ReturnKeyword,
349    SyntaxKind::SuperKeyword,
350    SyntaxKind::SwitchKeyword,
351    SyntaxKind::ThisKeyword,
352    SyntaxKind::ThrowKeyword,
353    SyntaxKind::TrueKeyword,
354    SyntaxKind::TryKeyword,
355    SyntaxKind::TypeOfKeyword,
356    SyntaxKind::VarKeyword,
357    SyntaxKind::VoidKeyword,
358    SyntaxKind::WhileKeyword,
359    SyntaxKind::WithKeyword,
360    SyntaxKind::ImplementsKeyword,
361    SyntaxKind::InterfaceKeyword,
362    SyntaxKind::LetKeyword,
363    SyntaxKind::PackageKeyword,
364    SyntaxKind::PrivateKeyword,
365    SyntaxKind::ProtectedKeyword,
366    SyntaxKind::PublicKeyword,
367    SyntaxKind::StaticKeyword,
368    SyntaxKind::YieldKeyword,
369    SyntaxKind::AbstractKeyword,
370    SyntaxKind::AccessorKeyword,
371    SyntaxKind::AsKeyword,
372    SyntaxKind::AssertsKeyword,
373    SyntaxKind::AssertKeyword,
374    SyntaxKind::AnyKeyword,
375    SyntaxKind::AsyncKeyword,
376    SyntaxKind::AwaitKeyword,
377    SyntaxKind::BooleanKeyword,
378    SyntaxKind::ConstructorKeyword,
379    SyntaxKind::DeclareKeyword,
380    SyntaxKind::GetKeyword,
381    SyntaxKind::InferKeyword,
382    SyntaxKind::IntrinsicKeyword,
383    SyntaxKind::IsKeyword,
384    SyntaxKind::KeyOfKeyword,
385    SyntaxKind::ModuleKeyword,
386    SyntaxKind::NamespaceKeyword,
387    SyntaxKind::NeverKeyword,
388    SyntaxKind::OutKeyword,
389    SyntaxKind::ReadonlyKeyword,
390    SyntaxKind::RequireKeyword,
391    SyntaxKind::NumberKeyword,
392    SyntaxKind::ObjectKeyword,
393    SyntaxKind::SatisfiesKeyword,
394    SyntaxKind::SetKeyword,
395    SyntaxKind::StringKeyword,
396    SyntaxKind::SymbolKeyword,
397    SyntaxKind::TypeKeyword,
398    SyntaxKind::UndefinedKeyword,
399    SyntaxKind::UniqueKeyword,
400    SyntaxKind::UnknownKeyword,
401    SyntaxKind::UsingKeyword,
402    SyntaxKind::FromKeyword,
403    SyntaxKind::GlobalKeyword,
404    SyntaxKind::BigIntKeyword,
405    SyntaxKind::OverrideKeyword,
406    SyntaxKind::OfKeyword,
407    SyntaxKind::DeferKeyword,
408];
409
410// =============================================================================
411// Token Classification Functions
412// =============================================================================
413
414/// Check if a token is a keyword.
415const fn token_is_keyword_inner(token: SyntaxKind) -> bool {
416    let t = token as u16;
417    t >= SyntaxKind::BreakKeyword as u16 && t <= SyntaxKind::DeferKeyword as u16
418}
419
420#[must_use]
421#[cfg(not(target_arch = "wasm32"))]
422pub const fn token_is_keyword(token: SyntaxKind) -> bool {
423    token_is_keyword_inner(token)
424}
425
426#[cfg(target_arch = "wasm32")]
427#[wasm_bindgen(js_name = tokenIsKeyword)]
428#[must_use]
429pub fn token_is_keyword(token: SyntaxKind) -> bool {
430    token_is_keyword_inner(token)
431}
432
433/// Check if a token is an identifier or keyword.
434const fn token_is_identifier_or_keyword_inner(token: SyntaxKind) -> bool {
435    token as u16 >= SyntaxKind::Identifier as u16
436}
437
438#[must_use]
439#[cfg(not(target_arch = "wasm32"))]
440pub const fn token_is_identifier_or_keyword(token: SyntaxKind) -> bool {
441    token_is_identifier_or_keyword_inner(token)
442}
443
444#[cfg(target_arch = "wasm32")]
445#[wasm_bindgen(js_name = tokenIsIdentifierOrKeyword)]
446#[must_use]
447pub fn token_is_identifier_or_keyword(token: SyntaxKind) -> bool {
448    token_is_identifier_or_keyword_inner(token)
449}
450
451/// Check if a token is a reserved word (strict reserved words).
452const fn token_is_reserved_word_inner(token: SyntaxKind) -> bool {
453    let t = token as u16;
454    t >= SyntaxKind::BreakKeyword as u16 && t <= SyntaxKind::WithKeyword as u16
455}
456
457#[must_use]
458#[cfg(not(target_arch = "wasm32"))]
459pub const fn token_is_reserved_word(token: SyntaxKind) -> bool {
460    token_is_reserved_word_inner(token)
461}
462
463#[cfg(target_arch = "wasm32")]
464#[wasm_bindgen(js_name = tokenIsReservedWord)]
465#[must_use]
466pub fn token_is_reserved_word(token: SyntaxKind) -> bool {
467    token_is_reserved_word_inner(token)
468}
469
470/// Check if a token is a strict mode reserved word.
471const fn token_is_strict_mode_reserved_word_inner(token: SyntaxKind) -> bool {
472    let t = token as u16;
473    t >= SyntaxKind::ImplementsKeyword as u16 && t <= SyntaxKind::YieldKeyword as u16
474}
475
476#[must_use]
477#[cfg(not(target_arch = "wasm32"))]
478pub const fn token_is_strict_mode_reserved_word(token: SyntaxKind) -> bool {
479    token_is_strict_mode_reserved_word_inner(token)
480}
481
482#[cfg(target_arch = "wasm32")]
483#[wasm_bindgen(js_name = tokenIsStrictModeReservedWord)]
484#[must_use]
485pub fn token_is_strict_mode_reserved_word(token: SyntaxKind) -> bool {
486    token_is_strict_mode_reserved_word_inner(token)
487}
488
489/// Check if a token is a literal (number, string, etc.).
490const fn token_is_literal_inner(token: SyntaxKind) -> bool {
491    let t = token as u16;
492    t >= SyntaxKind::NumericLiteral as u16 && t <= SyntaxKind::NoSubstitutionTemplateLiteral as u16
493}
494
495#[must_use]
496#[cfg(not(target_arch = "wasm32"))]
497pub const fn token_is_literal(token: SyntaxKind) -> bool {
498    token_is_literal_inner(token)
499}
500
501#[cfg(target_arch = "wasm32")]
502#[wasm_bindgen(js_name = tokenIsLiteral)]
503#[must_use]
504pub fn token_is_literal(token: SyntaxKind) -> bool {
505    token_is_literal_inner(token)
506}
507
508/// Check if a token is a template literal token.
509const fn token_is_template_literal_inner(token: SyntaxKind) -> bool {
510    let t = token as u16;
511    t >= SyntaxKind::NoSubstitutionTemplateLiteral as u16 && t <= SyntaxKind::TemplateTail as u16
512}
513
514#[must_use]
515#[cfg(not(target_arch = "wasm32"))]
516pub const fn token_is_template_literal(token: SyntaxKind) -> bool {
517    token_is_template_literal_inner(token)
518}
519
520#[cfg(target_arch = "wasm32")]
521#[wasm_bindgen(js_name = tokenIsTemplateLiteral)]
522#[must_use]
523pub fn token_is_template_literal(token: SyntaxKind) -> bool {
524    token_is_template_literal_inner(token)
525}
526
527/// Check if a token is punctuation.
528const fn token_is_punctuation_inner(token: SyntaxKind) -> bool {
529    let t = token as u16;
530    t >= SyntaxKind::OpenBraceToken as u16 && t <= SyntaxKind::CaretEqualsToken as u16
531}
532
533#[must_use]
534#[cfg(not(target_arch = "wasm32"))]
535pub const fn token_is_punctuation(token: SyntaxKind) -> bool {
536    token_is_punctuation_inner(token)
537}
538
539#[cfg(target_arch = "wasm32")]
540#[wasm_bindgen(js_name = tokenIsPunctuation)]
541#[must_use]
542pub fn token_is_punctuation(token: SyntaxKind) -> bool {
543    token_is_punctuation_inner(token)
544}
545
546/// Check if a token is an assignment operator.
547const fn token_is_assignment_operator_inner(token: SyntaxKind) -> bool {
548    let t = token as u16;
549    t >= SyntaxKind::EqualsToken as u16 && t <= SyntaxKind::CaretEqualsToken as u16
550}
551
552#[must_use]
553#[cfg(not(target_arch = "wasm32"))]
554pub const fn token_is_assignment_operator(token: SyntaxKind) -> bool {
555    token_is_assignment_operator_inner(token)
556}
557
558#[cfg(target_arch = "wasm32")]
559#[wasm_bindgen(js_name = tokenIsAssignmentOperator)]
560#[must_use]
561pub fn token_is_assignment_operator(token: SyntaxKind) -> bool {
562    token_is_assignment_operator_inner(token)
563}
564
565/// Check if a token is trivia (whitespace, comments).
566const fn token_is_trivia_inner(token: SyntaxKind) -> bool {
567    let t = token as u16;
568    t >= SyntaxKind::SingleLineCommentTrivia as u16
569        && t <= SyntaxKind::NonTextFileMarkerTrivia as u16
570}
571
572#[must_use]
573#[cfg(not(target_arch = "wasm32"))]
574pub const fn token_is_trivia(token: SyntaxKind) -> bool {
575    token_is_trivia_inner(token)
576}
577
578#[cfg(target_arch = "wasm32")]
579#[wasm_bindgen(js_name = tokenIsTrivia)]
580#[must_use]
581pub fn token_is_trivia(token: SyntaxKind) -> bool {
582    token_is_trivia_inner(token)
583}
584
585// =============================================================================
586// Keyword Text Mapping
587// =============================================================================
588
589/// Internal non-allocating version - returns static str reference.
590/// Use this for Rust-internal code to avoid allocations.
591#[must_use]
592pub const fn keyword_to_text_static(token: SyntaxKind) -> Option<&'static str> {
593    match token {
594        SyntaxKind::BreakKeyword => Some("break"),
595        SyntaxKind::CaseKeyword => Some("case"),
596        SyntaxKind::CatchKeyword => Some("catch"),
597        SyntaxKind::ClassKeyword => Some("class"),
598        SyntaxKind::ConstKeyword => Some("const"),
599        SyntaxKind::ContinueKeyword => Some("continue"),
600        SyntaxKind::DebuggerKeyword => Some("debugger"),
601        SyntaxKind::DefaultKeyword => Some("default"),
602        SyntaxKind::DeleteKeyword => Some("delete"),
603        SyntaxKind::DoKeyword => Some("do"),
604        SyntaxKind::ElseKeyword => Some("else"),
605        SyntaxKind::EnumKeyword => Some("enum"),
606        SyntaxKind::ExportKeyword => Some("export"),
607        SyntaxKind::ExtendsKeyword => Some("extends"),
608        SyntaxKind::FalseKeyword => Some("false"),
609        SyntaxKind::FinallyKeyword => Some("finally"),
610        SyntaxKind::ForKeyword => Some("for"),
611        SyntaxKind::FunctionKeyword => Some("function"),
612        SyntaxKind::IfKeyword => Some("if"),
613        SyntaxKind::ImportKeyword => Some("import"),
614        SyntaxKind::InKeyword => Some("in"),
615        SyntaxKind::InstanceOfKeyword => Some("instanceof"),
616        SyntaxKind::NewKeyword => Some("new"),
617        SyntaxKind::NullKeyword => Some("null"),
618        SyntaxKind::ReturnKeyword => Some("return"),
619        SyntaxKind::SuperKeyword => Some("super"),
620        SyntaxKind::SwitchKeyword => Some("switch"),
621        SyntaxKind::ThisKeyword => Some("this"),
622        SyntaxKind::ThrowKeyword => Some("throw"),
623        SyntaxKind::TrueKeyword => Some("true"),
624        SyntaxKind::TryKeyword => Some("try"),
625        SyntaxKind::TypeOfKeyword => Some("typeof"),
626        SyntaxKind::VarKeyword => Some("var"),
627        SyntaxKind::VoidKeyword => Some("void"),
628        SyntaxKind::WhileKeyword => Some("while"),
629        SyntaxKind::WithKeyword => Some("with"),
630        // Strict mode reserved words
631        SyntaxKind::ImplementsKeyword => Some("implements"),
632        SyntaxKind::InterfaceKeyword => Some("interface"),
633        SyntaxKind::LetKeyword => Some("let"),
634        SyntaxKind::PackageKeyword => Some("package"),
635        SyntaxKind::PrivateKeyword => Some("private"),
636        SyntaxKind::ProtectedKeyword => Some("protected"),
637        SyntaxKind::PublicKeyword => Some("public"),
638        SyntaxKind::StaticKeyword => Some("static"),
639        SyntaxKind::YieldKeyword => Some("yield"),
640        // Contextual keywords
641        SyntaxKind::AbstractKeyword => Some("abstract"),
642        SyntaxKind::AccessorKeyword => Some("accessor"),
643        SyntaxKind::AsKeyword => Some("as"),
644        SyntaxKind::AssertsKeyword => Some("asserts"),
645        SyntaxKind::AssertKeyword => Some("assert"),
646        SyntaxKind::AnyKeyword => Some("any"),
647        SyntaxKind::AsyncKeyword => Some("async"),
648        SyntaxKind::AwaitKeyword => Some("await"),
649        SyntaxKind::BooleanKeyword => Some("boolean"),
650        SyntaxKind::ConstructorKeyword => Some("constructor"),
651        SyntaxKind::DeclareKeyword => Some("declare"),
652        SyntaxKind::GetKeyword => Some("get"),
653        SyntaxKind::InferKeyword => Some("infer"),
654        SyntaxKind::IntrinsicKeyword => Some("intrinsic"),
655        SyntaxKind::IsKeyword => Some("is"),
656        SyntaxKind::KeyOfKeyword => Some("keyof"),
657        SyntaxKind::ModuleKeyword => Some("module"),
658        SyntaxKind::NamespaceKeyword => Some("namespace"),
659        SyntaxKind::NeverKeyword => Some("never"),
660        SyntaxKind::OutKeyword => Some("out"),
661        SyntaxKind::ReadonlyKeyword => Some("readonly"),
662        SyntaxKind::RequireKeyword => Some("require"),
663        SyntaxKind::NumberKeyword => Some("number"),
664        SyntaxKind::ObjectKeyword => Some("object"),
665        SyntaxKind::SatisfiesKeyword => Some("satisfies"),
666        SyntaxKind::SetKeyword => Some("set"),
667        SyntaxKind::StringKeyword => Some("string"),
668        SyntaxKind::SymbolKeyword => Some("symbol"),
669        SyntaxKind::TypeKeyword => Some("type"),
670        SyntaxKind::UndefinedKeyword => Some("undefined"),
671        SyntaxKind::UniqueKeyword => Some("unique"),
672        SyntaxKind::UnknownKeyword => Some("unknown"),
673        SyntaxKind::UsingKeyword => Some("using"),
674        SyntaxKind::FromKeyword => Some("from"),
675        SyntaxKind::GlobalKeyword => Some("global"),
676        SyntaxKind::BigIntKeyword => Some("bigint"),
677        SyntaxKind::OverrideKeyword => Some("override"),
678        SyntaxKind::OfKeyword => Some("of"),
679        SyntaxKind::DeferKeyword => Some("defer"),
680        _ => None,
681    }
682}
683
684/// Get the text representation of a keyword token.
685/// WASM-exported version that allocates a String for JS compatibility.
686#[wasm_bindgen(js_name = keywordToText)]
687#[must_use]
688pub fn keyword_to_text(token: SyntaxKind) -> Option<String> {
689    keyword_to_text_static(token).map(std::convert::Into::into)
690}
691
692/// Internal non-allocating version - returns static str reference.
693/// Use this for Rust-internal code to avoid allocations.
694#[must_use]
695pub const fn punctuation_to_text_static(token: SyntaxKind) -> Option<&'static str> {
696    match token {
697        SyntaxKind::OpenBraceToken => Some("{"),
698        SyntaxKind::CloseBraceToken => Some("}"),
699        SyntaxKind::OpenParenToken => Some("("),
700        SyntaxKind::CloseParenToken => Some(")"),
701        SyntaxKind::OpenBracketToken => Some("["),
702        SyntaxKind::CloseBracketToken => Some("]"),
703        SyntaxKind::DotToken => Some("."),
704        SyntaxKind::DotDotDotToken => Some("..."),
705        SyntaxKind::SemicolonToken => Some(";"),
706        SyntaxKind::CommaToken => Some(","),
707        SyntaxKind::QuestionDotToken => Some("?."),
708        SyntaxKind::LessThanToken => Some("<"),
709        SyntaxKind::LessThanSlashToken => Some("</"),
710        SyntaxKind::GreaterThanToken => Some(">"),
711        SyntaxKind::LessThanEqualsToken => Some("<="),
712        SyntaxKind::GreaterThanEqualsToken => Some(">="),
713        SyntaxKind::EqualsEqualsToken => Some("=="),
714        SyntaxKind::ExclamationEqualsToken => Some("!="),
715        SyntaxKind::EqualsEqualsEqualsToken => Some("==="),
716        SyntaxKind::ExclamationEqualsEqualsToken => Some("!=="),
717        SyntaxKind::EqualsGreaterThanToken => Some("=>"),
718        SyntaxKind::PlusToken => Some("+"),
719        SyntaxKind::MinusToken => Some("-"),
720        SyntaxKind::AsteriskToken => Some("*"),
721        SyntaxKind::AsteriskAsteriskToken => Some("**"),
722        SyntaxKind::SlashToken => Some("/"),
723        SyntaxKind::PercentToken => Some("%"),
724        SyntaxKind::PlusPlusToken => Some("++"),
725        SyntaxKind::MinusMinusToken => Some("--"),
726        SyntaxKind::LessThanLessThanToken => Some("<<"),
727        SyntaxKind::GreaterThanGreaterThanToken => Some(">>"),
728        SyntaxKind::GreaterThanGreaterThanGreaterThanToken => Some(">>>"),
729        SyntaxKind::AmpersandToken => Some("&"),
730        SyntaxKind::BarToken => Some("|"),
731        SyntaxKind::CaretToken => Some("^"),
732        SyntaxKind::ExclamationToken => Some("!"),
733        SyntaxKind::TildeToken => Some("~"),
734        SyntaxKind::AmpersandAmpersandToken => Some("&&"),
735        SyntaxKind::BarBarToken => Some("||"),
736        SyntaxKind::QuestionToken => Some("?"),
737        SyntaxKind::ColonToken => Some(":"),
738        SyntaxKind::AtToken => Some("@"),
739        SyntaxKind::QuestionQuestionToken => Some("??"),
740        SyntaxKind::BacktickToken => Some("`"),
741        SyntaxKind::HashToken => Some("#"),
742        // Assignment operators
743        SyntaxKind::EqualsToken => Some("="),
744        SyntaxKind::PlusEqualsToken => Some("+="),
745        SyntaxKind::MinusEqualsToken => Some("-="),
746        SyntaxKind::AsteriskEqualsToken => Some("*="),
747        SyntaxKind::AsteriskAsteriskEqualsToken => Some("**="),
748        SyntaxKind::SlashEqualsToken => Some("/="),
749        SyntaxKind::PercentEqualsToken => Some("%="),
750        SyntaxKind::LessThanLessThanEqualsToken => Some("<<="),
751        SyntaxKind::GreaterThanGreaterThanEqualsToken => Some(">>="),
752        SyntaxKind::GreaterThanGreaterThanGreaterThanEqualsToken => Some(">>>="),
753        SyntaxKind::AmpersandEqualsToken => Some("&="),
754        SyntaxKind::BarEqualsToken => Some("|="),
755        SyntaxKind::BarBarEqualsToken => Some("||="),
756        SyntaxKind::AmpersandAmpersandEqualsToken => Some("&&="),
757        SyntaxKind::QuestionQuestionEqualsToken => Some("??="),
758        SyntaxKind::CaretEqualsToken => Some("^="),
759        _ => None,
760    }
761}
762
763/// Get the text representation of a punctuation token.
764/// WASM-exported version that allocates a String for JS compatibility.
765#[wasm_bindgen(js_name = punctuationToText)]
766#[must_use]
767pub fn punctuation_to_text(token: SyntaxKind) -> Option<String> {
768    punctuation_to_text_static(token).map(std::convert::Into::into)
769}
770
771// =============================================================================
772// Text to Keyword Lookup
773// =============================================================================
774
775/// Convert a string to its keyword `SyntaxKind`, if it's a keyword.
776/// Returns None if the text is not a keyword.
777#[wasm_bindgen(js_name = textToKeyword)]
778#[must_use]
779pub fn text_to_keyword(text: &str) -> Option<SyntaxKind> {
780    match text {
781        // Reserved words
782        "break" => Some(SyntaxKind::BreakKeyword),
783        "case" => Some(SyntaxKind::CaseKeyword),
784        "catch" => Some(SyntaxKind::CatchKeyword),
785        "class" => Some(SyntaxKind::ClassKeyword),
786        "const" => Some(SyntaxKind::ConstKeyword),
787        "continue" => Some(SyntaxKind::ContinueKeyword),
788        "debugger" => Some(SyntaxKind::DebuggerKeyword),
789        "default" => Some(SyntaxKind::DefaultKeyword),
790        "delete" => Some(SyntaxKind::DeleteKeyword),
791        "do" => Some(SyntaxKind::DoKeyword),
792        "else" => Some(SyntaxKind::ElseKeyword),
793        "enum" => Some(SyntaxKind::EnumKeyword),
794        "export" => Some(SyntaxKind::ExportKeyword),
795        "extends" => Some(SyntaxKind::ExtendsKeyword),
796        "false" => Some(SyntaxKind::FalseKeyword),
797        "finally" => Some(SyntaxKind::FinallyKeyword),
798        "for" => Some(SyntaxKind::ForKeyword),
799        "function" => Some(SyntaxKind::FunctionKeyword),
800        "if" => Some(SyntaxKind::IfKeyword),
801        "import" => Some(SyntaxKind::ImportKeyword),
802        "in" => Some(SyntaxKind::InKeyword),
803        "instanceof" => Some(SyntaxKind::InstanceOfKeyword),
804        "new" => Some(SyntaxKind::NewKeyword),
805        "null" => Some(SyntaxKind::NullKeyword),
806        "return" => Some(SyntaxKind::ReturnKeyword),
807        "super" => Some(SyntaxKind::SuperKeyword),
808        "switch" => Some(SyntaxKind::SwitchKeyword),
809        "this" => Some(SyntaxKind::ThisKeyword),
810        "throw" => Some(SyntaxKind::ThrowKeyword),
811        "true" => Some(SyntaxKind::TrueKeyword),
812        "try" => Some(SyntaxKind::TryKeyword),
813        "typeof" => Some(SyntaxKind::TypeOfKeyword),
814        "var" => Some(SyntaxKind::VarKeyword),
815        "void" => Some(SyntaxKind::VoidKeyword),
816        "while" => Some(SyntaxKind::WhileKeyword),
817        "with" => Some(SyntaxKind::WithKeyword),
818        // Strict mode reserved words
819        "implements" => Some(SyntaxKind::ImplementsKeyword),
820        "interface" => Some(SyntaxKind::InterfaceKeyword),
821        "let" => Some(SyntaxKind::LetKeyword),
822        "package" => Some(SyntaxKind::PackageKeyword),
823        "private" => Some(SyntaxKind::PrivateKeyword),
824        "protected" => Some(SyntaxKind::ProtectedKeyword),
825        "public" => Some(SyntaxKind::PublicKeyword),
826        "static" => Some(SyntaxKind::StaticKeyword),
827        "yield" => Some(SyntaxKind::YieldKeyword),
828        // Contextual keywords
829        "abstract" => Some(SyntaxKind::AbstractKeyword),
830        "accessor" => Some(SyntaxKind::AccessorKeyword),
831        "as" => Some(SyntaxKind::AsKeyword),
832        "asserts" => Some(SyntaxKind::AssertsKeyword),
833        "assert" => Some(SyntaxKind::AssertKeyword),
834        "any" => Some(SyntaxKind::AnyKeyword),
835        "async" => Some(SyntaxKind::AsyncKeyword),
836        "await" => Some(SyntaxKind::AwaitKeyword),
837        "boolean" => Some(SyntaxKind::BooleanKeyword),
838        "constructor" => Some(SyntaxKind::ConstructorKeyword),
839        "declare" => Some(SyntaxKind::DeclareKeyword),
840        "get" => Some(SyntaxKind::GetKeyword),
841        "infer" => Some(SyntaxKind::InferKeyword),
842        "intrinsic" => Some(SyntaxKind::IntrinsicKeyword),
843        "is" => Some(SyntaxKind::IsKeyword),
844        "keyof" => Some(SyntaxKind::KeyOfKeyword),
845        "module" => Some(SyntaxKind::ModuleKeyword),
846        "namespace" => Some(SyntaxKind::NamespaceKeyword),
847        "never" => Some(SyntaxKind::NeverKeyword),
848        "out" => Some(SyntaxKind::OutKeyword),
849        "readonly" => Some(SyntaxKind::ReadonlyKeyword),
850        "require" => Some(SyntaxKind::RequireKeyword),
851        "number" => Some(SyntaxKind::NumberKeyword),
852        "object" => Some(SyntaxKind::ObjectKeyword),
853        "satisfies" => Some(SyntaxKind::SatisfiesKeyword),
854        "set" => Some(SyntaxKind::SetKeyword),
855        "string" => Some(SyntaxKind::StringKeyword),
856        "symbol" => Some(SyntaxKind::SymbolKeyword),
857        "type" => Some(SyntaxKind::TypeKeyword),
858        "undefined" => Some(SyntaxKind::UndefinedKeyword),
859        "unique" => Some(SyntaxKind::UniqueKeyword),
860        "unknown" => Some(SyntaxKind::UnknownKeyword),
861        "using" => Some(SyntaxKind::UsingKeyword),
862        "from" => Some(SyntaxKind::FromKeyword),
863        "global" => Some(SyntaxKind::GlobalKeyword),
864        "bigint" => Some(SyntaxKind::BigIntKeyword),
865        "override" => Some(SyntaxKind::OverrideKeyword),
866        "of" => Some(SyntaxKind::OfKeyword),
867        "defer" => Some(SyntaxKind::DeferKeyword),
868        _ => None,
869    }
870}
871
872/// Get the token kind for a given text, including identifiers and keywords.
873/// Returns `Identifier` if the text is not a keyword.
874#[wasm_bindgen(js_name = stringToToken)]
875#[must_use]
876pub fn string_to_token(text: &str) -> SyntaxKind {
877    text_to_keyword(text).unwrap_or(SyntaxKind::Identifier)
878}