swc_ecma_lexer/
lib.rs

1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![cfg_attr(test, feature(test))]
3#![deny(clippy::all)]
4#![deny(unused)]
5#![allow(clippy::nonminimal_bool)]
6#![allow(clippy::too_many_arguments)]
7#![allow(clippy::unnecessary_unwrap)]
8#![allow(clippy::vec_box)]
9#![allow(clippy::wrong_self_convention)]
10#![allow(clippy::match_like_matches_macro)]
11
12use serde::{Deserialize, Serialize};
13
14pub mod lexer;
15
16use input::Tokens;
17pub use lexer::*;
18
19#[macro_use]
20pub mod token;
21pub mod error;
22pub mod input;
23mod utils;
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
26#[serde(deny_unknown_fields, tag = "syntax")]
27pub enum Syntax {
28    /// Standard
29    #[serde(rename = "ecmascript")]
30    Es(EsSyntax),
31    /// This variant requires the cargo feature `typescript` to be enabled.
32    #[cfg(feature = "typescript")]
33    #[cfg_attr(docsrs, doc(cfg(feature = "typescript")))]
34    #[serde(rename = "typescript")]
35    Typescript(TsSyntax),
36}
37
38impl Default for Syntax {
39    fn default() -> Self {
40        Syntax::Es(Default::default())
41    }
42}
43
44impl Syntax {
45    pub fn auto_accessors(self) -> bool {
46        match self {
47            Syntax::Es(EsSyntax {
48                auto_accessors: true,
49                ..
50            }) => true,
51            #[cfg(feature = "typescript")]
52            Syntax::Typescript(_) => true,
53            _ => false,
54        }
55    }
56
57    pub fn import_attributes(self) -> bool {
58        match self {
59            Syntax::Es(EsSyntax {
60                import_attributes, ..
61            }) => import_attributes,
62            #[cfg(feature = "typescript")]
63            Syntax::Typescript(_) => true,
64        }
65    }
66
67    /// Should we parse jsx?
68    pub fn jsx(self) -> bool {
69        match self {
70            Syntax::Es(EsSyntax { jsx: true, .. }) => true,
71            #[cfg(feature = "typescript")]
72            Syntax::Typescript(TsSyntax { tsx: true, .. }) => true,
73            _ => false,
74        }
75    }
76
77    pub fn fn_bind(self) -> bool {
78        matches!(self, Syntax::Es(EsSyntax { fn_bind: true, .. }))
79    }
80
81    pub fn decorators(self) -> bool {
82        match self {
83            Syntax::Es(EsSyntax {
84                decorators: true, ..
85            }) => true,
86            #[cfg(feature = "typescript")]
87            Syntax::Typescript(TsSyntax {
88                decorators: true, ..
89            }) => true,
90            _ => false,
91        }
92    }
93
94    pub fn decorators_before_export(self) -> bool {
95        match self {
96            Syntax::Es(EsSyntax {
97                decorators_before_export: true,
98                ..
99            }) => true,
100            #[cfg(feature = "typescript")]
101            Syntax::Typescript(..) => true,
102            _ => false,
103        }
104    }
105
106    /// Should we parse typescript?
107    #[cfg(not(feature = "typescript"))]
108    pub const fn typescript(self) -> bool {
109        false
110    }
111
112    /// Should we parse typescript?
113    #[cfg(feature = "typescript")]
114    pub const fn typescript(self) -> bool {
115        matches!(self, Syntax::Typescript(..))
116    }
117
118    pub fn export_default_from(self) -> bool {
119        matches!(
120            self,
121            Syntax::Es(EsSyntax {
122                export_default_from: true,
123                ..
124            })
125        )
126    }
127
128    pub fn dts(self) -> bool {
129        match self {
130            #[cfg(feature = "typescript")]
131            Syntax::Typescript(t) => t.dts,
132            _ => false,
133        }
134    }
135
136    pub fn allow_super_outside_method(self) -> bool {
137        match self {
138            Syntax::Es(EsSyntax {
139                allow_super_outside_method,
140                ..
141            }) => allow_super_outside_method,
142            #[cfg(feature = "typescript")]
143            Syntax::Typescript(_) => true,
144        }
145    }
146
147    pub fn allow_return_outside_function(self) -> bool {
148        match self {
149            Syntax::Es(EsSyntax {
150                allow_return_outside_function,
151                ..
152            }) => allow_return_outside_function,
153            #[cfg(feature = "typescript")]
154            Syntax::Typescript(_) => false,
155        }
156    }
157
158    pub fn early_errors(self) -> bool {
159        match self {
160            #[cfg(feature = "typescript")]
161            Syntax::Typescript(t) => !t.no_early_errors,
162            Syntax::Es(..) => true,
163        }
164    }
165
166    pub fn disallow_ambiguous_jsx_like(self) -> bool {
167        match self {
168            #[cfg(feature = "typescript")]
169            Syntax::Typescript(t) => t.disallow_ambiguous_jsx_like,
170            _ => false,
171        }
172    }
173
174    pub fn explicit_resource_management(&self) -> bool {
175        match self {
176            Syntax::Es(EsSyntax {
177                explicit_resource_management: using_decl,
178                ..
179            }) => *using_decl,
180            #[cfg(feature = "typescript")]
181            Syntax::Typescript(_) => true,
182        }
183    }
184}
185
186#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
187#[serde(rename_all = "camelCase")]
188pub struct TsSyntax {
189    #[serde(default)]
190    pub tsx: bool,
191
192    #[serde(default)]
193    pub decorators: bool,
194
195    /// `.d.ts`
196    #[serde(skip, default)]
197    pub dts: bool,
198
199    #[serde(skip, default)]
200    pub no_early_errors: bool,
201
202    /// babel: `disallowAmbiguousJSXLike`
203    /// Even when JSX parsing is not enabled, this option disallows using syntax
204    /// that would be ambiguous with JSX (`<X> y` type assertions and
205    /// `<X>()=>{}` type arguments)
206    /// see: https://babeljs.io/docs/en/babel-plugin-transform-typescript#disallowambiguousjsxlike
207    #[serde(skip, default)]
208    pub disallow_ambiguous_jsx_like: bool,
209}
210
211#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
212#[serde(rename_all = "camelCase")]
213pub struct EsSyntax {
214    #[serde(default)]
215    pub jsx: bool,
216
217    /// Support function bind expression.
218    #[serde(rename = "functionBind")]
219    #[serde(default)]
220    pub fn_bind: bool,
221
222    /// Enable decorators.
223    #[serde(default)]
224    pub decorators: bool,
225
226    /// babel: `decorators.decoratorsBeforeExport`
227    ///
228    /// Effective only if `decorator` is true.
229    #[serde(rename = "decoratorsBeforeExport")]
230    #[serde(default)]
231    pub decorators_before_export: bool,
232
233    #[serde(default)]
234    pub export_default_from: bool,
235
236    /// Stage 3.
237    #[serde(default, alias = "importAssertions")]
238    pub import_attributes: bool,
239
240    #[serde(default, rename = "allowSuperOutsideMethod")]
241    pub allow_super_outside_method: bool,
242
243    #[serde(default, rename = "allowReturnOutsideFunction")]
244    pub allow_return_outside_function: bool,
245
246    #[serde(default)]
247    pub auto_accessors: bool,
248
249    #[serde(default)]
250    pub explicit_resource_management: bool,
251}
252
253bitflags::bitflags! {
254    #[derive(Debug, Clone, Copy, Default)]
255    pub struct Context: u32 {
256
257        /// `true` while backtracking
258        const IgnoreError = 1 << 0;
259
260        /// Is in module code?
261        const Module = 1 << 1;
262        const CanBeModule = 1 << 2;
263        const Strict = 1 << 3;
264
265        const ForLoopInit = 1 << 4;
266        const ForAwaitLoopInit = 1 << 5;
267
268        const IncludeInExpr = 1 << 6;
269        /// If true, await expression is parsed, and "await" is treated as a
270        /// keyword.
271        const InAsync = 1 << 7;
272        /// If true, yield expression is parsed, and "yield" is treated as a
273        /// keyword.
274        const InGenerator = 1 << 8;
275
276        /// If true, await is treated as a keyword.
277        const InStaticBlock = 1 << 9;
278
279        const IsContinueAllowed = 1 << 10;
280        const IsBreakAllowed = 1 << 11;
281
282        const InType = 1 << 12;
283        /// Typescript extension.
284        const ShouldNotLexLtOrGtAsType = 1 << 13;
285        /// Typescript extension.
286        const InDeclare = 1 << 14;
287
288        /// If true, `:` should not be treated as a type annotation.
289        const InCondExpr = 1 << 15;
290        const WillExpectColonForCond = 1 << 16;
291
292        const InClass = 1 << 17;
293
294        const InClassField = 1 << 18;
295
296        const InFunction = 1 << 19;
297
298        /// This indicates current scope or the scope out of arrow function is
299        /// function declaration or function expression or not.
300        const InsideNonArrowFunctionScope = 1 << 20;
301
302        const InParameters = 1 << 21;
303
304        const HasSuperClass = 1 << 22;
305
306        const InPropertyName = 1 << 23;
307
308        const InForcedJsxContext = 1 << 24;
309
310        // If true, allow super.x and super[x]
311        const AllowDirectSuper = 1 << 25;
312
313        const IgnoreElseClause = 1 << 26;
314
315        const DisallowConditionalTypes = 1 << 27;
316
317        const AllowUsingDecl = 1 << 28;
318
319        const TopLevel = 1 << 29;
320    }
321}
322
323#[cfg(test)]
324fn with_test_sess<F, Ret>(src: &str, f: F) -> Result<Ret, ::testing::StdErr>
325where
326    F: FnOnce(&swc_common::errors::Handler, swc_common::input::StringInput<'_>) -> Result<Ret, ()>,
327{
328    use swc_common::FileName;
329
330    ::testing::run_test(false, |cm, handler| {
331        let fm = cm.new_source_file(FileName::Real("testing".into()).into(), src.into());
332
333        f(handler, (&*fm).into())
334    })
335}
336
337#[macro_export]
338macro_rules! tok {
339    ('`') => {
340        $crate::token::Token::BackQuote
341    };
342    // (';') => { Token::Semi };
343    ('@') => {
344        $crate::token::Token::At
345    };
346    ('#') => {
347        $crate::token::Token::Hash
348    };
349
350    ('&') => {
351        $crate::token::Token::BinOp($crate::token::BinOpToken::BitAnd)
352    };
353    ('|') => {
354        $crate::token::Token::BinOp($crate::token::BinOpToken::BitOr)
355    };
356    ('^') => {
357        $crate::token::Token::BinOp($crate::token::BinOpToken::BitXor)
358    };
359    ('+') => {
360        $crate::token::Token::BinOp($crate::token::BinOpToken::Add)
361    };
362    ('-') => {
363        $crate::token::Token::BinOp($crate::token::BinOpToken::Sub)
364    };
365    ("??") => {
366        $crate::token::Token::BinOp($crate::token::BinOpToken::NullishCoalescing)
367    };
368    ('~') => {
369        $crate::token::Token::Tilde
370    };
371    ('!') => {
372        $crate::token::Token::Bang
373    };
374    ("&&") => {
375        $crate::token::Token::BinOp($crate::token::BinOpToken::LogicalAnd)
376    };
377    ("||") => {
378        $crate::token::Token::BinOp($crate::token::BinOpToken::LogicalOr)
379    };
380    ("&&=") => {
381        $crate::token::Token::AssignOp(swc_ecma_ast::AssignOp::AndAssign)
382    };
383    ("||=") => {
384        $crate::token::Token::AssignOp(swc_ecma_ast::AssignOp::OrAssign)
385    };
386    ("??=") => {
387        $crate::token::Token::AssignOp(swc_ecma_ast::AssignOp::NullishAssign)
388    };
389
390    ("==") => {
391        $crate::token::Token::BinOp($crate::token::BinOpToken::EqEq)
392    };
393    ("===") => {
394        $crate::token::Token::BinOp($crate::token::BinOpToken::EqEqEq)
395    };
396    ("!=") => {
397        $crate::token::Token::BinOp($crate::token::BinOpToken::NotEq)
398    };
399    ("!==") => {
400        $crate::token::Token::BinOp($crate::token::BinOpToken::NotEqEq)
401    };
402
403    (',') => {
404        $crate::token::Token::Comma
405    };
406    ('?') => {
407        $crate::token::Token::QuestionMark
408    };
409    (':') => {
410        $crate::token::Token::Colon
411    };
412    ('.') => {
413        $crate::token::Token::Dot
414    };
415    ("=>") => {
416        $crate::token::Token::Arrow
417    };
418    ("...") => {
419        $crate::token::Token::DotDotDot
420    };
421    ("${") => {
422        $crate::token::Token::DollarLBrace
423    };
424
425    ('+') => {
426        $crate::token::Token::BinOp($crate::token::BinOpToken::Add)
427    };
428    ('-') => {
429        $crate::token::Token::BinOp($crate::token::BinOpToken::Sub)
430    };
431    ('*') => {
432        $crate::token::Token::BinOp($crate::token::BinOpToken::Mul)
433    };
434    ('/') => {
435        $crate::token::Token::BinOp($crate::token::BinOpToken::Div)
436    };
437    ("/=") => {
438        $crate::token::Token::AssignOp(swc_ecma_ast::AssignOp::DivAssign)
439    };
440    ('%') => {
441        $crate::token::Token::BinOp($crate::token::BinOpToken::Mod)
442    };
443    ('~') => {
444        $crate::token::Token::Tilde
445    };
446    ('<') => {
447        $crate::token::Token::BinOp($crate::token::BinOpToken::Lt)
448    };
449    ("<<") => {
450        $crate::token::Token::BinOp($crate::token::BinOpToken::LShift)
451    };
452    ("<=") => {
453        $crate::token::Token::BinOp($crate::token::BinOpToken::LtEq)
454    };
455    ("<<=") => {
456        $crate::token::Token::AssignOp($crate::token::AssignOp::LShiftAssign)
457    };
458    ('>') => {
459        $crate::token::Token::BinOp($crate::token::BinOpToken::Gt)
460    };
461    (">>") => {
462        $crate::token::Token::BinOp($crate::token::BinOpToken::RShift)
463    };
464    (">>>") => {
465        $crate::token::Token::BinOp($crate::token::BinOpToken::ZeroFillRShift)
466    };
467    (">=") => {
468        $crate::token::Token::BinOp($crate::token::BinOpToken::GtEq)
469    };
470    (">>=") => {
471        $crate::token::Token::AssignOp(swc_ecma_ast::AssignOp::RShiftAssign)
472    };
473    (">>>=") => {
474        $crate::token::Token::AssignOp(swc_ecma_ast::AssignOp::ZeroFillRShiftAssign)
475    };
476
477    ("++") => {
478        $crate::token::Token::PlusPlus
479    };
480    ("--") => {
481        $crate::token::Token::MinusMinus
482    };
483
484    ('=') => {
485        $crate::token::Token::AssignOp(swc_ecma_ast::AssignOp::Assign)
486    };
487
488    ('(') => {
489        $crate::token::Token::LParen
490    };
491    (')') => {
492        $crate::token::Token::RParen
493    };
494    ('{') => {
495        $crate::token::Token::LBrace
496    };
497    ('}') => {
498        $crate::token::Token::RBrace
499    };
500    ('[') => {
501        $crate::token::Token::LBracket
502    };
503    (']') => {
504        $crate::token::Token::RBracket
505    };
506
507    ("await") => {
508        $crate::token::Token::Word($crate::token::Word::Keyword($crate::token::Keyword::Await))
509    };
510    ("break") => {
511        $crate::token::Token::Word($crate::token::Word::Keyword($crate::token::Keyword::Break))
512    };
513    ("case") => {
514        $crate::token::Token::Word($crate::token::Word::Keyword($crate::token::Keyword::Case))
515    };
516    ("catch") => {
517        $crate::token::Token::Word($crate::token::Word::Keyword($crate::token::Keyword::Catch))
518    };
519    ("class") => {
520        $crate::token::Token::Word($crate::token::Word::Keyword($crate::token::Keyword::Class))
521    };
522    ("const") => {
523        $crate::token::Token::Word($crate::token::Word::Keyword($crate::token::Keyword::Const))
524    };
525    ("continue") => {
526        $crate::token::Token::Word($crate::token::Word::Keyword(
527            $crate::token::Keyword::Continue,
528        ))
529    };
530    ("debugger") => {
531        $crate::token::Token::Word($crate::token::Word::Keyword(
532            $crate::token::Keyword::Debugger,
533        ))
534    };
535    ("default") => {
536        $crate::token::Token::Word($crate::token::Word::Keyword(
537            $crate::token::Keyword::Default_,
538        ))
539    };
540    ("delete") => {
541        $crate::token::Token::Word($crate::token::Word::Keyword($crate::token::Keyword::Delete))
542    };
543    ("do") => {
544        $crate::token::Token::Word($crate::token::Word::Keyword($crate::token::Keyword::Do))
545    };
546    ("else") => {
547        $crate::token::Token::Word($crate::token::Word::Keyword($crate::token::Keyword::Else))
548    };
549    ("export") => {
550        $crate::token::Token::Word($crate::token::Word::Keyword($crate::token::Keyword::Export))
551    };
552    ("extends") => {
553        $crate::token::Token::Word($crate::token::Word::Keyword(
554            $crate::token::Keyword::Extends,
555        ))
556    };
557    ("false") => {
558        $crate::token::Token::Word($crate::token::Word::False)
559    };
560    ("finally") => {
561        $crate::token::Token::Word($crate::token::Word::Keyword(
562            $crate::token::Keyword::Finally,
563        ))
564    };
565    ("for") => {
566        $crate::token::Token::Word($crate::token::Word::Keyword($crate::token::Keyword::For))
567    };
568    ("function") => {
569        $crate::token::Token::Word($crate::token::Word::Keyword(
570            $crate::token::Keyword::Function,
571        ))
572    };
573    ("if") => {
574        $crate::token::Token::Word($crate::token::Word::Keyword($crate::token::Keyword::If))
575    };
576    ("in") => {
577        $crate::token::Token::Word($crate::token::Word::Keyword($crate::token::Keyword::In))
578    };
579    ("instanceof") => {
580        $crate::token::Token::Word($crate::token::Word::Keyword(
581            $crate::token::Keyword::InstanceOf,
582        ))
583    };
584    ("import") => {
585        $crate::token::Token::Word($crate::token::Word::Keyword($crate::token::Keyword::Import))
586    };
587    ("let") => {
588        $crate::token::Token::Word($crate::token::Word::Keyword($crate::token::Keyword::Let))
589    };
590    ("new") => {
591        $crate::token::Token::Word($crate::token::Word::Keyword($crate::token::Keyword::New))
592    };
593    ("null") => {
594        $crate::token::Token::Word($crate::token::Word::Null)
595    };
596
597    ("return") => {
598        $crate::token::Token::Word($crate::token::Word::Keyword($crate::token::Keyword::Return))
599    };
600    ("super") => {
601        $crate::token::Token::Word($crate::token::Word::Keyword($crate::token::Keyword::Super))
602    };
603    ("switch") => {
604        $crate::token::Token::Word($crate::token::Word::Keyword($crate::token::Keyword::Switch))
605    };
606    ("this") => {
607        $crate::token::Token::Word($crate::token::Word::Keyword($crate::token::Keyword::This))
608    };
609    ("throw") => {
610        $crate::token::Token::Word($crate::token::Word::Keyword($crate::token::Keyword::Throw))
611    };
612    ("true") => {
613        $crate::token::Token::Word($crate::token::Word::True)
614    };
615    ("try") => {
616        $crate::token::Token::Word($crate::token::Word::Keyword($crate::token::Keyword::Try))
617    };
618    ("typeof") => {
619        $crate::token::Token::Word($crate::token::Word::Keyword($crate::token::Keyword::TypeOf))
620    };
621    ("var") => {
622        $crate::token::Token::Word($crate::token::Word::Keyword($crate::token::Keyword::Var))
623    };
624    ("void") => {
625        $crate::token::Token::Word($crate::token::Word::Keyword($crate::token::Keyword::Void))
626    };
627    ("while") => {
628        $crate::token::Token::Word($crate::token::Word::Keyword($crate::token::Keyword::While))
629    };
630    ("with") => {
631        $crate::token::Token::Word($crate::token::Word::Keyword($crate::token::Keyword::With))
632    };
633    ("yield") => {
634        $crate::token::Token::Word($crate::token::Word::Keyword($crate::token::Keyword::Yield))
635    };
636
637    // ----------
638    // JSX
639    // ----------
640    (JSXTagStart) => {
641        $crate::token::Token::JSXTagStart
642    };
643
644    (JSXTagEnd) => {
645        $crate::token::Token::JSXTagEnd
646    };
647
648    ($tt:tt) => {
649        $crate::token::Token::Word($crate::token::Word::Ident($crate::token::IdentLike::Known(
650            known_ident!($tt),
651        )))
652    };
653}