tsv_ts 0.8.0

TypeScript/JS formatter, type stripper and acorn-compatible parser — part of tsv
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
// Arrow function parsing: predicate scans (`x =>`, `(...) =>`, `<T>() =>`) and
// the builders for parenthesized, single-param, generic, and async arrows. The
// Pratt kernel in `expression.rs` calls into these; they never call back into it.

use crate::ast::{
    ArrowFunctionBody, ArrowFunctionExpression, Expression, ExpressionKind, Identifier,
};
use crate::lexer::TokenKind;
use tsv_lang::{Error, Span};

use super::Parser;
use super::expression::alloc_expr;
use super::expression_lookahead::{ArrowHead, scan_angle_brackets, scan_arrow_after_identifier};
use super::scan::skip_whitespace_and_comments;

impl<'a, 'arena> Parser<'a, 'arena> {
    /// Consume the arrow `=>`, enforcing `ArrowFunction`'s `[no LineTerminator here]`
    /// restriction (ecma262): no line terminator between the arrow parameters (or
    /// return type) and `=>`. A bare newline, a line comment, or a block comment
    /// containing or followed by a newline in that gap is a syntax error — acorn
    /// rejects it (the cover grammar reinterprets the `(…)` as a parenthesized
    /// expression, leaving `=>` unexpected), and as a drop-in replacement tsv must
    /// too. The byte-scan arrow predicates skip newlines, so this is the single
    /// enforcement point, shared by the paren, single-param, generic, and async
    /// arrow builders. A same-line block comment (`(a) /* c */ =>`) carries no line
    /// terminator and stays valid.
    /// Consume the arrow's `=>` and return the byte offset of its `=` (the arrow
    /// token start), so builders can record it on the node instead of the printer
    /// re-scanning source for it.
    fn expect_arrow(&mut self) -> Result<u32, Error> {
        if self.check(&TokenKind::Arrow) && self.had_line_terminator {
            return Err(self.error_msg("Line terminator not permitted before '=>'"));
        }
        // Full-document coordinate (`current_pos` adds `base_offset`), matching the
        // AST spans the printer indexes with — NOT the raw `self.current.start`, which
        // is slice-relative under Svelte embedding. Same reason `params_start` does this.
        let arrow_token = self.current_pos().0 as u32;
        self.expect(&TokenKind::Arrow)?;
        Ok(arrow_token)
    }

    /// Parse the arrow function whose head a byte-scan predicate just matched, under
    /// tsc's one context rule about its return type — the
    /// `allowReturnTypeInArrowFunction` check in
    /// `parseParenthesizedArrowFunctionExpression`.
    ///
    /// In a conditional's consequent the `:` after a parenthesized head may be the
    /// conditional's own: `a ? (b) : c => d` is `a ? b : (c => d)`, not an arrow
    /// `(b): c => d` with nothing left to end the conditional. tsc parses the arrow
    /// anyway and keeps it only when **another `:` follows** it (`a ? (b): c => d : e`
    /// — a syntax error read any other way, since JS has no second colon
    /// there); otherwise it rewinds and reads the head as a parenthesized expression.
    /// The same here: while the return type is barred
    /// ([`Parser::arrow_return_type_barred`]) the parse runs from a
    /// [`Parser::checkpoint`], and an annotated arrow not followed by `:` is rewound
    /// — `None` hands the head back for the caller to read as what it otherwise is
    /// (a grouped expression, a type assertion, a call to `async`). Outside a
    /// consequent, and for an arrow without an annotation, `parse` is the whole
    /// story. The bar reaches only heads at the consequent's own grouping depth;
    /// inside any delimiter opened since the `?`, and in a `[+In]` body or a
    /// `yield` argument, the annotation is always allowed, as tsc passes `true`
    /// there.
    ///
    /// Only an **annotated** head is ever in question (tsc's `hasReturnColon` gate),
    /// and the annotation is read off the bytes ([`ArrowHead::has_return_type`])
    /// before the parse rather than off the node after it, because an annotated head
    /// that fails to parse as an arrow is rewound too — `a ? (b + c) : d => e` is a
    /// head tsc never reads as a signature, and its `tryParse` rewinds where tsv's
    /// parameter parse errors. A body error rewinds the same way, to a verdict tsc
    /// shares: the parenthesized reading hands the same body to the alternate's
    /// arrow, which fails on it again. A head without an annotation parses straight,
    /// so a body error there keeps its message. The one error handed back instead of
    /// rewound is a **goal gate** ([`Error::is_goal_gated`]): it fires on an
    /// `import.meta` token the parenthesized reading has to consume as well, so the
    /// file is no script either way, and rewinding would trade the one mark the format
    /// fallback reads for a position-only error further on.
    ///
    /// An **unambiguous** head — one tsc reads as a signature without asking
    /// ([`ArrowHead::commits_to_signature`], its `Tristate.True`) — is not
    /// speculated on at all: tsc parses it committed and with
    /// `allowReturnTypeInArrowFunction: true`, and that `true` reaches the arrow's
    /// BODY, so an annotated head inside the body keeps its own annotation and the
    /// conditional runs out of `:`. Speculating instead would parse the body under
    /// the bar, truncate it, find the `:` the inner annotation would have taken and
    /// keep the outer arrow — accepting `a ? (b: B): c => (d): e => f;`, which
    /// neither tsc nor acorn reads any way at all. So the commit is not an
    /// optimization: it is the rule for those heads, and the lifted flag is the
    /// point of it.
    ///
    /// `parse` is a plain `fn` pointer rather than a generic closure so the cold
    /// half below has ONE body across the three head sites; the fast path inlines
    /// here, where the pointer is a constant and the call stays direct.
    #[inline]
    pub(super) fn parse_arrow_or_rewind(
        &mut self,
        head: ArrowHead,
        parse: fn(&mut Self) -> Result<&'arena Expression<'arena>, Error>,
    ) -> Result<Option<&'arena Expression<'arena>>, Error> {
        if !self.arrow_return_type_barred() || !head.has_return_type(self.source.as_bytes()) {
            return parse(self).map(Some);
        }
        self.parse_annotated_arrow_in_consequent(head, parse)
    }

    /// The cold half of [`Parser::parse_arrow_or_rewind`]: an annotated head in a
    /// barred context, either committed with the bar lifted (an unambiguous head)
    /// or parsed from a checkpoint and kept only when `:` follows.
    #[cold]
    #[inline(never)]
    fn parse_annotated_arrow_in_consequent(
        &mut self,
        head: ArrowHead,
        parse: fn(&mut Self) -> Result<&'arena Expression<'arena>, Error>,
    ) -> Result<Option<&'arena Expression<'arena>>, Error> {
        if head.commits_to_signature(self.source.as_bytes()) {
            return self.with_arrow_return_type_allowed(parse).map(Some);
        }
        let checkpoint = self.checkpoint();
        match parse(self) {
            Ok(arrow) if self.check(&TokenKind::Colon) => Ok(Some(arrow)),
            // a goal gate fires on a module-only token the parenthesized reading has to
            // consume too, so rewinding would only trade it for whatever error that
            // reading hits first — and the format fallback reads the gate, not the position
            Err(error) if error.is_goal_gated() => Err(error),
            // a depth limit is no verdict on the reading: the other one would be taken for
            // the stack's sake, not the grammar's
            Err(error) if error.kind() == tsv_lang::ErrorKind::TooDeep => Err(error),
            Ok(_) | Err(_) => {
                self.rewind(checkpoint);
                Ok(None)
            }
        }
    }

    /// Check if current position starts an arrow function
    ///
    /// Scans ahead looking for pattern: `(` ... `)` `=>`. Returns the head
    /// (slice-relative, like `self.current.start`) when it is one — what
    /// `parse_arrow_or_rewind` asks its two byte questions of.
    pub(super) fn paren_arrow_function_start(&self) -> Option<ArrowHead> {
        ArrowHead::at_paren(self.source.as_bytes(), self.current.start as usize)
    }

    /// Check if current position starts a single-param arrow function: `x =>`
    ///
    /// Scans ahead looking for pattern: `identifier` `=>`
    pub(super) fn is_single_param_arrow_start(&self) -> bool {
        // The current token IS the identifier, so its end is already known — see
        // `scan_arrow_after_identifier`, which no longer re-walks the name to find it.
        // Slice-relative like `self.source`, matching the byte slice this indexes.
        scan_arrow_after_identifier(self.source.as_bytes(), self.current.end as usize)
    }

    /// Check if current position starts a generic arrow function: `<T>() =>`
    ///
    /// Scans ahead looking for pattern: `<` ... `>` `(` ... `)` `=>`
    ///
    /// Returns the head (slice-relative, like `self.current.start`) when it is one
    /// — what `parse_arrow_or_rewind` asks its two byte questions of.
    pub(super) fn generic_arrow_function_start(&self) -> Option<ArrowHead> {
        let bytes = self.source.as_bytes();
        let start = self.current.start as usize;

        // Must start with '<'
        if start >= bytes.len() || bytes[start] != b'<' {
            return None;
        }

        // Scan through type parameters: <T, U extends V, ...>
        let pos = scan_angle_brackets(bytes, start);
        if pos == 0 {
            return None;
        }

        // After '>', check for `(...) =>` (allow comments: `<T> /* comment */ () =>`)
        ArrowHead::behind_type_parameters(bytes, skip_whitespace_and_comments(bytes, pos))
    }

    /// Parse generic arrow function: `<T>() => ...`, `<T, U extends V>() => ...`
    pub(super) fn parse_generic_arrow_function(
        &mut self,
    ) -> Result<&'arena Expression<'arena>, Error> {
        let (start, _) = self.current_pos();

        // Parse type parameters: <T, U extends V, ...>
        let type_parameters = self.parse_type_parameters()?;

        // Capture paren position before parsing params
        let (params_start, _) = self.current_pos();

        // Params + body in the arrow's own `[~Await, ~Yield]` context (an arrow is
        // never async/generator itself; the return type between them is await-free,
        // and `yield` in an arrow inside a generator is a plain identifier).
        let (params, return_type, body, arrow_token) = self.with_fn_context(false, false, |p| {
            let params = p.parse_parameter_list_no_decorators()?.into_bump_slice();
            // Return type annotation: <T>(): type => ... or type predicate
            let return_type = p.parse_optional_return_type()?;
            p.arrow_return_pair(return_type.as_ref())?;
            let arrow_token = p.expect_arrow()?; // consume '=>' (no LineTerminator before it)
            let body = p.parse_arrow_body()?;
            Ok((params, return_type, body, arrow_token))
        })?;
        let end = self.prev_token_end() as u32;
        debug_assert!(
            body.span().end <= end,
            "an arrow's extent encloses its body"
        );

        Ok(alloc_expr(
            self.arena,
            Expression {
                span: Span::new(start as u32, end),
                kind: ExpressionKind::ArrowFunctionExpression(self.arena.alloc(
                    ArrowFunctionExpression {
                        type_parameters: Some(type_parameters),
                        params,
                        body,
                        return_type,
                        r#async: false,
                        params_start: Some(params_start as u32),
                        arrow_token,
                    },
                )),
            },
        ))
    }

    /// Parse arrow function body: expression or block statement
    ///
    /// The body is one wire level below the arrow (the depth limit's count,
    /// [`Parser::nested`]): the recursion `a => b => c` passes no grouping delimiter. A block
    /// body enters it with the body's own levels, in one count.
    fn parse_arrow_body(&mut self) -> Result<ArrowFunctionBody<'arena>, Error> {
        if self.check(&TokenKind::BraceOpen) {
            // A block body is a `FunctionBody` (`[+In]`) — `in` is the binary
            // operator even when this arrow sits in a for-header init — and a fresh
            // frame for a parenthesized arrow's return type.
            let block = self.with_body_frame(|p| p.parse_function_body_levels(3))?;
            Ok(ArrowFunctionBody::BlockStatement(block))
        } else {
            // A concise body is `AssignmentExpression[?In]` — it inherits the
            // outer In context, so `for (a = () => x in y;;)` still rejects.
            // Use assignment_expression so comma doesn't consume next object property
            let body_start = self.current_pos().0;
            let body = self.nested(1, Self::parse_assignment_expression_ref)?;
            // a bare body the printer parenthesizes — a conditional (`() => (a ? b : c)`),
            // or one its position takes a pair around (`() => (@dec class {})`, asked of
            // the printer's predicate) — a level of the output's nesting
            if body.span().start as usize == body_start
                && (matches!(body.kind, ExpressionKind::ConditionalExpression(_))
                    || crate::printer::printer_adds_operand_pair(
                        body,
                        crate::printer::ParenContext::ArrowBody,
                        self.in_for_header,
                    ))
            {
                self.printer_pair()?;
            }
            Ok(ArrowFunctionBody::Expression(body))
        }
    }

    /// Parse arrow function with parentheses: `() => expr` or `(x, y) => expr` or `() => { ... }`
    ///
    /// Supports:
    /// - No parameters: `() => expr`
    /// - Single parameter: `(x) => expr`
    /// - Multiple parameters: `(x, y) => expr`
    /// - Destructuring parameters: `([a, b]) => ...`, `({x, y}) => ...`
    /// - Default values: `(a = 1) => ...`
    /// - Expression body: `() => expr`
    /// - Block body: `() => { ... }`
    ///
    /// Note: Single parameter without parens (`x => expr`) is handled by
    /// `parse_single_param_arrow_function()`.
    pub(super) fn parse_arrow_function(&mut self) -> Result<&'arena Expression<'arena>, Error> {
        let (start, _) = self.current_pos();

        // Capture paren position before parsing params
        let (params_start, _) = self.current_pos();

        // Params + body in the arrow's own `[~Await, ~Yield]` context (an arrow is
        // never async/generator itself).
        let (params, return_type, body, arrow_token) = self.with_fn_context(false, false, |p| {
            let params = p.parse_parameter_list_no_decorators()?.into_bump_slice();
            // Return type annotation: (): type => ... or type predicate
            let return_type = p.parse_optional_return_type()?;
            p.arrow_return_pair(return_type.as_ref())?;
            let arrow_token = p.expect_arrow()?; // consume '=>' (no LineTerminator before it)
            let body = p.parse_arrow_body()?;
            Ok((params, return_type, body, arrow_token))
        })?;
        let end = self.prev_token_end() as u32;
        debug_assert!(
            body.span().end <= end,
            "an arrow's extent encloses its body"
        );

        Ok(alloc_expr(
            self.arena,
            Expression {
                span: Span::new(start as u32, end),
                kind: ExpressionKind::ArrowFunctionExpression(self.arena.alloc(
                    ArrowFunctionExpression {
                        type_parameters: None, // Generic arrows like <T>() => {} are handled by parse_generic_arrow_function()
                        params,
                        body,
                        return_type,
                        r#async: false, // Non-async arrow function; async ones are parsed via parse_async_arrow_function
                        params_start: Some(params_start as u32),
                        arrow_token,
                    },
                )),
            },
        ))
    }

    /// Parse single-parameter arrow function without parentheses: `x => expr`
    pub(super) fn parse_single_param_arrow_function(
        &mut self,
    ) -> Result<&'arena Expression<'arena>, Error> {
        let (start, _) = self.current_pos();

        // Parse the single parameter: a plain identifier, a contextual type keyword
        // (`any => …`), or `await` as a `BindingIdentifier` at Script `[~Await]`
        // (`await => …`) — the dispatcher gates every reachable case through the
        // `try_binding_name` binding-name predicate.
        debug_assert!(self.at_binding_name());
        let (id_start, id_end) = self.current_pos();
        let name = self.current_ident_name_or_await();
        // the parameter's levels in the format's output, which writes it in a pair
        // (`(a) => …`): its `Identifier` and the pair's grouping — a step that returns before
        // the body (the depth limit's count, `Parser::nested`)
        self.nested(2, |_| Ok(()))?;
        self.advance()?;

        let mut params = self.bvec();
        params.push(Expression::from_identifier(Identifier::simple(
            name,
            Span::new(id_start as u32, id_end as u32),
        )));
        let params = params.into_bump_slice();

        let arrow_token = self.expect_arrow()?; // consume '=>' (no LineTerminator before it)

        // Non-async single-param arrow body is `[~Await]`.
        let body = self.with_fn_context(false, false, Self::parse_arrow_body)?;
        let end = self.prev_token_end() as u32;
        debug_assert!(
            body.span().end <= end,
            "an arrow's extent encloses its body"
        );

        Ok(alloc_expr(
            self.arena,
            Expression {
                span: Span::new(start as u32, end),
                kind: ExpressionKind::ArrowFunctionExpression(self.arena.alloc(
                    ArrowFunctionExpression {
                        type_parameters: None,
                        params,
                        body,
                        return_type: None, // Single-param without parens can't have return type
                        r#async: false,
                        params_start: None, // No parens for single-param arrows
                        arrow_token,
                    },
                )),
            },
        ))
    }

    /// Parse async arrow function after 'async' has been consumed: `() => ...`, `x => ...`, or `<T>() => ...`
    pub(super) fn parse_async_arrow_function_after_async(
        &mut self,
        start: usize,
    ) -> Result<&'arena Expression<'arena>, Error> {
        // Check for type parameters: `async <T>() => ...`
        let type_parameters = self.parse_optional_type_parameters()?;

        // Parse parameter list or single parameter
        // Note: with type parameters, must have parentheses
        let (params, params_start): (&'arena [Expression<'arena>], Option<u32>) = if self
            .check(&TokenKind::ParenOpen)
        {
            let (paren_pos, _) = self.current_pos();
            (
                // Async arrow params are `[+Await]`.
                self.with_fn_context(true, false, Self::parse_parameter_list_no_decorators)?
                    .into_bump_slice(),
                Some(paren_pos as u32),
            )
        } else if type_parameters.is_none() && matches!(self.current_kind(), TokenKind::Identifier)
        {
            // Single parameter without parens: `async x => ...`
            // (Not allowed with type parameters)
            let (id_start, id_end) = self.current_pos();
            let name = self.current_ident_name();
            // its levels in the format's output, `async (x) => …` (the depth limit's count)
            self.nested(2, |_| Ok(()))?;
            self.advance()?;
            let mut params = self.bvec();
            params.push(Expression::from_identifier(Identifier::simple(
                name,
                Span::new(id_start as u32, id_end as u32),
            )));
            (params.into_bump_slice(), None)
        } else {
            return Err(self.error_expected_after("'(' or identifier", "async"));
        };

        // Check for return type annotation or type predicate
        let return_type = self.parse_optional_return_type()?;
        self.arrow_return_pair(return_type.as_ref())?;

        let arrow_token = self.expect_arrow()?; // consume '=>' (no LineTerminator before it)

        // Async arrow body is `[+Await]`.
        let body = self.with_fn_context(true, false, Self::parse_arrow_body)?;
        let end = self.prev_token_end() as u32;
        debug_assert!(
            body.span().end <= end,
            "an arrow's extent encloses its body"
        );

        Ok(alloc_expr(
            self.arena,
            Expression {
                span: Span::new(start as u32, end),
                kind: ExpressionKind::ArrowFunctionExpression(self.arena.alloc(
                    ArrowFunctionExpression {
                        type_parameters,
                        params,
                        body,
                        return_type,
                        r#async: true,
                        params_start,
                        arrow_token,
                    },
                )),
            },
        ))
    }
}