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
use ;
use crateescape_len;
use cratesign_starts_number;
use Error;
/// Whether `ch` is a non-ASCII CSS identifier code point.
///
/// Mirrors Svelte's `parseCss` (`read_identifier`: `codePointAt(0) >= 160`): any
/// code point at or above U+00A0 is a valid identifier code point, so symbols and
/// emoji (`โฅ`, `๐ฉ`) are accepted while the C1 control range U+0080โU+009F stays
/// excluded. Single source for the threshold shared by `is_identifier_start` and
/// the continuation guard in `read_identifier`.
pub
/// 256-entry lookup table for the ASCII identifier-continuation fast path
/// ([`ascii_identifier_run_end`], run by `read_identifier` and by the lexer front's common
/// identifier). Each entry is computed from the same byte predicate the
/// per-char continuation arm expands to for ASCII (`[a-zA-Z0-9_-]`), so a lookup
/// replaces the alnum/eq OR-chain plus a full UTF-8 decode with one L1 load on the
/// hot identifier-body loop. Non-ASCII bytes are all `false`, so the fast path stops
/// at the first non-ASCII byte and the char loop decodes it โ byte-identical.
///
/// Shared with `read_number_into`'s dimension-unit body loop: a unit (`px`, `rem`) is an
/// identifier, so it continues on exactly this predicate โ and with the declaration
/// value's boundary scan, which decides whether a `url` is a token start (and so opens an
/// opaque url-token) from the single byte before it.
pub const IDENT_CONTINUE_LUT: = ;
/// How many bytes an identifier run's walk tests behind one bound check
/// ([`ascii_identifier_run_end`], and the lexer front's first chunk).
pub const IDENT_CHUNK: usize = 8;
/// The end of the run of ASCII identifier-continuation bytes (`[a-zA-Z0-9_-]`,
/// [`IDENT_CONTINUE_LUT`]) that starts at `from` โ `from` itself when none does.
///
/// [`IDENT_CHUNK`] bytes at a time while that many are in bounds, each tested against the
/// table in turn with no bound check of its own ([`ascii_identifier_chunk_stop`]), and byte by
/// byte within a chunk of the end of input. Every byte is still the table's to classify; only
/// the bound check is shared across the chunk.
pub
/// The offset of the first byte of `chunk` that does not continue an ASCII identifier
/// ([`IDENT_CONTINUE_LUT`]), or `None` when every byte does.
pub
/// Whether the byte an ASCII continuation run stopped on (`None` at end of input) ends the
/// identifier outright: any ASCII byte but the `\` escape introducer. The run already took
/// every ASCII continuation byte, so only a non-ASCII code point or an escape can extend it.
pub
/// Whether `ch` can begin a CSS identifier token in the lexer dispatch.
///
/// Covers ASCII letters, non-ASCII identifier code points (see
/// `is_non_ascii_identifier_codepoint`), `-`, `_`, and the `\` escape introducer.
/// Digits and a leading `$` have their own dispatch arms (numbers, and `$`-prefixed
/// identifiers), so they're intentionally excluded here โ but a `$` arm uses this to
/// confirm the *next* char begins an identifier.
pub
/// Does a `-` followed by `after` open an IDENT or a NUMBER of its own โ so that the two
/// lex as one token, rather than as a `<delim-token>` and whatever follows it?
///
/// css-syntax-3 ยง4.3.9 "Would start an identifier", read at the `-`: its second bullet
/// admits an ident-start code point, a second `-`, or a valid escape. ยง4.3.10's number is
/// asked over the sign by [`sign_starts_number`] (`-1.5`, `-.5`; a lone `-.` is left
/// alone).
///
/// โ ๏ธ The ident-start set is **this lexer's** โ an ASCII letter, `_`, or any code point at
/// U+00A0 and above ([`is_non_ascii_identifier_codepoint`]) โ not ยง4.2's enumerated
/// ranges, which leave holes at U+00D7 and U+00F7. The question is "would the glued text
/// lex as one token", and the lexings that answer it are tsv's own and that of the parser
/// tsv replaces (`parseCss` reads an identifier at `codePointAt(0) >= 160` too), so `-รa`
/// is one ident here. The `\` bullet is the spec's rather than [`is_identifier_start`]'s:
/// a `\` before a newline or at end of input is no escape at all (ยง4.3.8), which
/// [`escape_len`] answers.
///
/// One reading for its two askers, which must agree or one pass splits what the next
/// glues: the value-run splitter (`parser::value::operators`, deciding whether a `-` at
/// an operand position is the operator) and the value printer (`printer::values`,
/// refusing a head glue that would merge the operator and its member into one token).
pub
/// The ASCII byte form of `is_identifier_start`, for the lexer's byte-first dispatch.
///
/// Equal to `is_identifier_start(b as char)` for every ASCII byte (`b < 0x80`), and
/// `false` for every non-ASCII byte โ the non-ASCII identifier code points (`>= 0xA0`)
/// are `>= 0x80`, so they're handled by the dispatch's non-ASCII arm, not here. Covers the
/// ASCII letters, `-`, `_`, and the `\` escape introducer (digits and a leading `$`
/// have their own dispatch arms).
pub
/// Read a CSS identifier โ the general reader, which the lexer reaches only for an
/// identifier whose ASCII run stops on a non-ASCII byte or a `\`, or that opens with a `\`
/// (the common identifier is read by the lexer's front, `Lexer::next_token_into_local`).
/// CSS identifiers can contain unicode escapes; the characters a-z, A-Z, 0-9, -, _;
/// any non-ASCII code point at or above U+00A0 (symbols and emoji, matching Svelte's
/// `>= 160` rule); plus an optional leading `$` (SCSS-style; the lexer dispatch only
/// routes `$` here when it begins an identifier).
/// Per CSS Syntax Level 3 spec, escape sequences are decoded to their actual characters.
///
/// Returns the token plus the **decoded value only when an escape was present**:
/// the common no-escape identifier returns `None` (no allocation โ its text is the
/// verbatim source slice `source[start..end]`). The decoded buffer is materialized
/// lazily from the verbatim run scanned so far the first time a `\` escape is seen;
/// the caller (`Lexer::read_decoded_identifier_into`) funnels it into the lexer's reused
/// `decode_scratch`, so this owned `String` is the only allocation on the escape path.
pub
/// Decode a CSS unicode escape sequence: \XXXXXX (1-6 hex digits)
/// Advances position past the escape sequence.
///
/// Per CSS Syntax 3 ยง4.3.7 (consume an escaped code point), a value that is
/// zero, is for a surrogate, or is greater than the maximum allowed code point
/// (U+10FFFF) decodes to U+FFFD REPLACEMENT CHARACTER โ it is not a parse error.
pub