/src/serenity/Userland/Libraries/LibShell/PosixLexer.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2022, Ali Mohammad Pur <mpfard@serenityos.org> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #include <AK/CharacterTypes.h> |
8 | | #include <LibShell/PosixLexer.h> |
9 | | |
10 | | static bool is_operator(StringView text) |
11 | 8.41M | { |
12 | 8.41M | return Shell::Posix::Token::operator_from_name(text).has_value(); |
13 | 8.41M | } |
14 | | |
15 | | static bool is_part_of_operator(StringView text, char ch) |
16 | 78.3M | { |
17 | 78.3M | StringBuilder builder; |
18 | 78.3M | builder.append(text); |
19 | 78.3M | builder.append(ch); |
20 | | |
21 | 78.3M | return Shell::Posix::Token::operator_from_name(builder.string_view()).has_value(); |
22 | 78.3M | } |
23 | | |
24 | | namespace Shell::Posix { |
25 | | |
26 | | ErrorOr<Vector<Token>> Lexer::batch_next(Optional<Reduction> starting_reduction) |
27 | 14.2M | { |
28 | 14.2M | if (starting_reduction.has_value()) |
29 | 19.7k | m_next_reduction = *starting_reduction; |
30 | | |
31 | 111M | for (; m_next_reduction != Reduction::None;) { |
32 | 111M | auto result = TRY(reduce(m_next_reduction)); |
33 | 111M | m_next_reduction = result.next_reduction; |
34 | 111M | if (!result.tokens.is_empty()) |
35 | 14.2M | return result.tokens; |
36 | 111M | } |
37 | | |
38 | 0 | return Vector<Token> {}; |
39 | 14.2M | } |
40 | | |
41 | | ExpansionRange Lexer::range(ssize_t offset) const |
42 | 8.75M | { |
43 | 8.75M | return { |
44 | 8.75M | m_state.position.end_offset - m_state.position.start_offset + offset, |
45 | 8.75M | 0, |
46 | 8.75M | }; |
47 | 8.75M | } |
48 | | |
49 | | char Lexer::consume() |
50 | 156M | { |
51 | 156M | auto ch = m_lexer.consume(); |
52 | 156M | if (ch == '\n') { |
53 | 3.85M | m_state.position.end_line.line_number++; |
54 | 3.85M | m_state.position.end_line.line_column = 0; |
55 | 3.85M | } |
56 | | |
57 | 156M | m_state.position.end_offset++; |
58 | 156M | return ch; |
59 | 156M | } |
60 | | |
61 | | void Lexer::reconsume(StringView string) |
62 | 2.68k | { |
63 | 30.2M | for (auto byte : string.bytes()) { |
64 | 30.2M | if (byte == '\n') { |
65 | 1.22M | m_state.position.end_line.line_number++; |
66 | 1.22M | m_state.position.end_line.line_column = 0; |
67 | 1.22M | } |
68 | | |
69 | 30.2M | m_state.position.end_offset++; |
70 | 30.2M | } |
71 | 2.68k | } |
72 | | |
73 | | bool Lexer::consume_specific(char ch) |
74 | 470M | { |
75 | 470M | if (m_lexer.peek() == ch) { |
76 | 10.3M | consume(); |
77 | 10.3M | return true; |
78 | 10.3M | } |
79 | 460M | return false; |
80 | 470M | } |
81 | | |
82 | | ErrorOr<Lexer::ReductionResult> Lexer::reduce(Reduction reduction) |
83 | 171M | { |
84 | 171M | switch (reduction) { |
85 | 0 | case Reduction::None: |
86 | 0 | return ReductionResult { {}, Reduction::None }; |
87 | 730k | case Reduction::End: |
88 | 730k | return reduce_end(); |
89 | 10.2M | case Reduction::Operator: |
90 | 10.2M | return reduce_operator(); |
91 | 6.28M | case Reduction::Comment: |
92 | 6.28M | return reduce_comment(); |
93 | 12.2M | case Reduction::SingleQuotedString: |
94 | 12.2M | return reduce_single_quoted_string(); |
95 | 28.1M | case Reduction::DoubleQuotedString: |
96 | 28.1M | return reduce_double_quoted_string(); |
97 | 5.00M | case Reduction::Expansion: |
98 | 5.00M | return reduce_expansion(); |
99 | 13.6M | case Reduction::CommandExpansion: |
100 | 13.6M | return reduce_command_expansion(); |
101 | 81.9M | case Reduction::Start: |
102 | 81.9M | return reduce_start(); |
103 | 1.71M | case Reduction::ArithmeticExpansion: |
104 | 1.71M | return reduce_arithmetic_expansion(); |
105 | 564k | case Reduction::SpecialParameterExpansion: |
106 | 564k | return reduce_special_parameter_expansion(); |
107 | 3.26M | case Reduction::ParameterExpansion: |
108 | 3.26M | return reduce_parameter_expansion(); |
109 | 223k | case Reduction::CommandOrArithmeticSubstitutionExpansion: |
110 | 223k | return reduce_command_or_arithmetic_substitution_expansion(); |
111 | 5.34M | case Reduction::ExtendedParameterExpansion: |
112 | 5.34M | return reduce_extended_parameter_expansion(); |
113 | 2.41M | case Reduction::HeredocContents: |
114 | 2.41M | return reduce_heredoc_contents(); |
115 | 171M | } |
116 | | |
117 | 0 | VERIFY_NOT_REACHED(); |
118 | 0 | } |
119 | | |
120 | | ErrorOr<Lexer::ReductionResult> Lexer::reduce_end() |
121 | 730k | { |
122 | 730k | return ReductionResult { |
123 | 730k | .tokens = { Token::eof() }, |
124 | 730k | .next_reduction = Reduction::None, |
125 | 730k | }; |
126 | 730k | } |
127 | | |
128 | | Lexer::HeredocKeyResult Lexer::process_heredoc_key(Token const& token) |
129 | 1.45M | { |
130 | 1.45M | StringBuilder builder; |
131 | 1.45M | enum ParseState { |
132 | 1.45M | Free, |
133 | 1.45M | InDoubleQuotes, |
134 | 1.45M | InSingleQuotes, |
135 | 1.45M | }; |
136 | 1.45M | Vector<ParseState, 4> parse_state; |
137 | 1.45M | parse_state.append(Free); |
138 | 1.45M | bool escaped = false; |
139 | 1.45M | bool had_a_single_quote_segment = false; |
140 | | |
141 | 18.4M | for (auto byte : token.value.bytes()) { |
142 | 18.4M | switch (parse_state.last()) { |
143 | 8.94M | case Free: |
144 | 8.94M | switch (byte) { |
145 | 14.0k | case '"': |
146 | 14.0k | if (escaped) { |
147 | 0 | builder.append(byte); |
148 | 0 | escaped = false; |
149 | 14.0k | } else { |
150 | 14.0k | parse_state.append(InDoubleQuotes); |
151 | 14.0k | } |
152 | 14.0k | break; |
153 | 1.06k | case '\'': |
154 | 1.06k | if (escaped) { |
155 | 0 | builder.append(byte); |
156 | 0 | escaped = false; |
157 | 1.06k | } else { |
158 | 1.06k | had_a_single_quote_segment = true; |
159 | 1.06k | parse_state.append(InSingleQuotes); |
160 | 1.06k | } |
161 | 1.06k | break; |
162 | 272 | case '\\': |
163 | 272 | if (escaped) { |
164 | 0 | builder.append(byte); |
165 | 0 | escaped = false; |
166 | 272 | } else { |
167 | 272 | escaped = true; |
168 | 272 | } |
169 | 272 | break; |
170 | 8.93M | default: |
171 | | // NOTE: bash eats the backslash outside quotes :shrug: |
172 | 8.93M | if (escaped && parse_state.last() != Free) { |
173 | 0 | builder.append('\\'); |
174 | 0 | escaped = false; |
175 | 0 | } |
176 | 8.93M | builder.append(byte); |
177 | 8.93M | break; |
178 | 8.94M | } |
179 | 8.94M | break; |
180 | 8.94M | case InDoubleQuotes: |
181 | 7.96M | if (!escaped && byte == '"') { |
182 | 13.9k | parse_state.take_last(); |
183 | 13.9k | break; |
184 | 13.9k | } |
185 | 7.94M | if (escaped) { |
186 | 3.40M | if (byte != '"') |
187 | 3.40M | builder.append('\\'); |
188 | 3.40M | builder.append(byte); |
189 | 3.40M | break; |
190 | 3.40M | } |
191 | 4.54M | if (byte == '\\') |
192 | 38 | escaped = true; |
193 | 4.54M | else |
194 | 4.54M | builder.append(byte); |
195 | 4.54M | break; |
196 | 1.53M | case InSingleQuotes: |
197 | 1.53M | if (byte == '\'') { |
198 | 1.05k | parse_state.take_last(); |
199 | 1.05k | break; |
200 | 1.05k | } |
201 | 1.53M | builder.append(byte); |
202 | 1.53M | break; |
203 | 18.4M | } |
204 | 18.4M | } |
205 | | |
206 | | // NOTE: Not checking the final state as any garbage that even partially parses is allowed to be used as a key :/ |
207 | | |
208 | 1.45M | return { |
209 | 1.45M | .key = builder.to_string().release_value_but_fixme_should_propagate_errors(), |
210 | 1.45M | .allow_interpolation = !had_a_single_quote_segment, |
211 | 1.45M | }; |
212 | 1.45M | } |
213 | | |
214 | | ErrorOr<Lexer::ReductionResult> Lexer::reduce_operator() |
215 | 10.2M | { |
216 | 10.2M | if (m_lexer.is_eof()) { |
217 | 9.46k | if (is_operator(m_state.buffer.string_view())) { |
218 | 9.46k | auto tokens = TRY(Token::operators_from(m_state)); |
219 | 9.46k | m_state.buffer.clear(); |
220 | 9.46k | m_state.position.start_offset = m_state.position.end_offset; |
221 | 9.46k | m_state.position.start_line = m_state.position.end_line; |
222 | | |
223 | 9.46k | return ReductionResult { |
224 | 9.46k | .tokens = move(tokens), |
225 | 9.46k | .next_reduction = Reduction::End, |
226 | 9.46k | }; |
227 | 9.46k | } |
228 | | |
229 | 0 | return reduce(Reduction::Start); |
230 | 9.46k | } |
231 | | |
232 | 10.2M | if (is_part_of_operator(m_state.buffer.string_view(), m_lexer.peek())) { |
233 | 1.85M | m_state.buffer.append(consume()); |
234 | 1.85M | return ReductionResult { |
235 | 1.85M | .tokens = {}, |
236 | 1.85M | .next_reduction = Reduction::Operator, |
237 | 1.85M | }; |
238 | 1.85M | } |
239 | | |
240 | 8.40M | auto tokens = Vector<Token> {}; |
241 | 8.40M | if (is_operator(m_state.buffer.string_view())) { |
242 | 8.40M | tokens.extend(TRY(Token::operators_from(m_state))); |
243 | 8.40M | m_state.buffer.clear(); |
244 | 8.40M | m_state.position.start_offset = m_state.position.end_offset; |
245 | 8.40M | m_state.position.start_line = m_state.position.end_line; |
246 | 8.40M | } |
247 | | |
248 | 8.40M | auto expect_heredoc_entry = !tokens.is_empty() && (tokens.last().type == Token::Type::DoubleLessDash || tokens.last().type == Token::Type::DoubleLess); |
249 | | |
250 | 8.40M | auto result = TRY(reduce(Reduction::Start)); |
251 | 8.40M | tokens.extend(move(result.tokens)); |
252 | | |
253 | 23.0M | while (expect_heredoc_entry && tokens.size() == 1 && result.next_reduction != Reduction::None) { |
254 | 14.6M | result = TRY(reduce(result.next_reduction)); |
255 | 14.6M | tokens.extend(move(result.tokens)); |
256 | 14.6M | } |
257 | | |
258 | 8.40M | if (expect_heredoc_entry && tokens.size() > 1) { |
259 | 1.14M | auto [key, interpolation] = process_heredoc_key(tokens[1]); |
260 | 1.14M | m_state.heredoc_entries.append(HeredocEntry { |
261 | 1.14M | .key = key, |
262 | 1.14M | .allow_interpolation = interpolation, |
263 | 1.14M | .dedent = tokens[0].type == Token::Type::DoubleLessDash, |
264 | 1.14M | }); |
265 | 1.14M | } |
266 | | |
267 | 8.40M | return ReductionResult { |
268 | 8.40M | .tokens = move(tokens), |
269 | 8.40M | .next_reduction = result.next_reduction, |
270 | 8.40M | }; |
271 | 8.40M | } |
272 | | |
273 | | ErrorOr<Lexer::ReductionResult> Lexer::reduce_comment() |
274 | 6.28M | { |
275 | 6.28M | if (m_lexer.is_eof()) { |
276 | 640 | return ReductionResult { |
277 | 640 | .tokens = {}, |
278 | 640 | .next_reduction = Reduction::End, |
279 | 640 | }; |
280 | 640 | } |
281 | | |
282 | 6.28M | if (consume() == '\n') { |
283 | 243k | m_state.on_new_line = true; |
284 | 243k | return ReductionResult { |
285 | 243k | .tokens = { Token::newline() }, |
286 | 243k | .next_reduction = Reduction::Start, |
287 | 243k | }; |
288 | 243k | } |
289 | | |
290 | 6.03M | return ReductionResult { |
291 | 6.03M | .tokens = {}, |
292 | 6.03M | .next_reduction = Reduction::Comment, |
293 | 6.03M | }; |
294 | 6.28M | } |
295 | | |
296 | | ErrorOr<Lexer::ReductionResult> Lexer::reduce_single_quoted_string() |
297 | 12.2M | { |
298 | 12.2M | if (m_lexer.is_eof()) { |
299 | 371 | auto tokens = TRY(Token::maybe_from_state(m_state)); |
300 | 363 | tokens.append(Token::continuation('\'')); |
301 | 363 | return ReductionResult { |
302 | 363 | .tokens = move(tokens), |
303 | 363 | .next_reduction = Reduction::End, |
304 | 363 | }; |
305 | 371 | } |
306 | | |
307 | 12.2M | auto ch = consume(); |
308 | 12.2M | m_state.buffer.append(ch); |
309 | | |
310 | 12.2M | if (ch == '\'') { |
311 | 1.97k | return ReductionResult { |
312 | 1.97k | .tokens = {}, |
313 | 1.97k | .next_reduction = Reduction::Start, |
314 | 1.97k | }; |
315 | 1.97k | } |
316 | | |
317 | 12.2M | return ReductionResult { |
318 | 12.2M | .tokens = {}, |
319 | 12.2M | .next_reduction = Reduction::SingleQuotedString, |
320 | 12.2M | }; |
321 | 12.2M | } |
322 | | |
323 | | ErrorOr<Lexer::ReductionResult> Lexer::reduce_double_quoted_string() |
324 | 28.1M | { |
325 | 28.1M | m_state.previous_reduction = Reduction::DoubleQuotedString; |
326 | 28.1M | if (m_lexer.is_eof()) { |
327 | 12.6k | auto tokens = TRY(Token::maybe_from_state(m_state)); |
328 | 12.6k | tokens.append(Token::continuation('"')); |
329 | 12.6k | return ReductionResult { |
330 | 12.6k | .tokens = move(tokens), |
331 | 12.6k | .next_reduction = Reduction::End, |
332 | 12.6k | }; |
333 | 12.6k | } |
334 | | |
335 | 28.1M | auto ch = consume(); |
336 | 28.1M | m_state.buffer.append(ch); |
337 | | |
338 | 28.1M | if (m_state.escaping) { |
339 | 84.3k | m_state.escaping = false; |
340 | | |
341 | 84.3k | return ReductionResult { |
342 | 84.3k | .tokens = {}, |
343 | 84.3k | .next_reduction = Reduction::DoubleQuotedString, |
344 | 84.3k | }; |
345 | 84.3k | } |
346 | | |
347 | 28.0M | switch (ch) { |
348 | 84.3k | case '\\': |
349 | 84.3k | m_state.escaping = true; |
350 | 84.3k | return ReductionResult { |
351 | 84.3k | .tokens = {}, |
352 | 84.3k | .next_reduction = Reduction::DoubleQuotedString, |
353 | 84.3k | }; |
354 | 287k | case '"': |
355 | 287k | m_state.previous_reduction = Reduction::Start; |
356 | 287k | return ReductionResult { |
357 | 287k | .tokens = {}, |
358 | 287k | .next_reduction = Reduction::Start, |
359 | 287k | }; |
360 | 508k | case '$': |
361 | 508k | if (m_lexer.next_is("(")) |
362 | 116k | m_state.expansions.empend(CommandExpansion { .command = StringBuilder {}, .range = range(-1) }); |
363 | 391k | else |
364 | 391k | m_state.expansions.empend(ParameterExpansion { .parameter = StringBuilder {}, .range = range(-1) }); |
365 | 508k | return ReductionResult { |
366 | 508k | .tokens = {}, |
367 | 508k | .next_reduction = Reduction::Expansion, |
368 | 508k | }; |
369 | 63.0k | case '`': |
370 | 63.0k | m_state.expansions.empend(CommandExpansion { .command = StringBuilder {}, .range = range(-1) }); |
371 | 63.0k | return ReductionResult { |
372 | 63.0k | .tokens = {}, |
373 | 63.0k | .next_reduction = Reduction::CommandExpansion, |
374 | 63.0k | }; |
375 | 27.0M | default: |
376 | 27.0M | return ReductionResult { |
377 | 27.0M | .tokens = {}, |
378 | 27.0M | .next_reduction = Reduction::DoubleQuotedString, |
379 | 27.0M | }; |
380 | 28.0M | } |
381 | 28.0M | } |
382 | | |
383 | | ErrorOr<Lexer::ReductionResult> Lexer::reduce_expansion() |
384 | 5.00M | { |
385 | 5.00M | if (m_lexer.is_eof()) |
386 | 150k | return reduce(m_state.previous_reduction); |
387 | | |
388 | 4.85M | auto ch = m_lexer.peek(); |
389 | | |
390 | 4.85M | switch (ch) { |
391 | 19.6k | case '{': |
392 | 19.6k | consume(); |
393 | 19.6k | m_state.buffer.append(ch); |
394 | 19.6k | return ReductionResult { |
395 | 19.6k | .tokens = {}, |
396 | 19.6k | .next_reduction = Reduction::ExtendedParameterExpansion, |
397 | 19.6k | }; |
398 | 184k | case '(': |
399 | 184k | consume(); |
400 | 184k | m_state.buffer.append(ch); |
401 | 184k | return ReductionResult { |
402 | 184k | .tokens = {}, |
403 | 184k | .next_reduction = Reduction::CommandOrArithmeticSubstitutionExpansion, |
404 | 184k | }; |
405 | 35.3k | case 'a' ... 'z': |
406 | 104k | case 'A' ... 'Z': |
407 | 155k | case '_': { |
408 | 155k | consume(); |
409 | 155k | m_state.buffer.append(ch); |
410 | 155k | auto& expansion = m_state.expansions.last().get<ParameterExpansion>(); |
411 | 155k | expansion.parameter.append(ch); |
412 | 155k | expansion.range.length = m_state.position.end_offset - expansion.range.start - m_state.position.start_offset; |
413 | | |
414 | 155k | return ReductionResult { |
415 | 155k | .tokens = {}, |
416 | 155k | .next_reduction = Reduction::ParameterExpansion, |
417 | 155k | }; |
418 | 104k | } |
419 | 854 | case '0' ... '9': |
420 | 31.4k | case '-': |
421 | 39.7k | case '!': |
422 | 50.1k | case '@': |
423 | 50.6k | case '#': |
424 | 58.5k | case '?': |
425 | 59.5k | case '*': |
426 | 564k | case '$': |
427 | 564k | return reduce(Reduction::SpecialParameterExpansion); |
428 | 3.93M | default: |
429 | 3.93M | m_state.buffer.append(ch); |
430 | 3.93M | return reduce(m_state.previous_reduction); |
431 | 4.85M | } |
432 | 4.85M | } |
433 | | |
434 | | ErrorOr<Lexer::ReductionResult> Lexer::reduce_command_expansion() |
435 | 13.6M | { |
436 | 13.6M | if (m_lexer.is_eof()) { |
437 | 100 | auto& expansion = m_state.expansions.last().get<CommandExpansion>(); |
438 | 100 | expansion.range.length = m_state.position.end_offset - expansion.range.start - m_state.position.start_offset; |
439 | | |
440 | 100 | return ReductionResult { |
441 | 100 | .tokens = { Token::continuation('`') }, |
442 | 100 | .next_reduction = m_state.previous_reduction, |
443 | 100 | }; |
444 | 100 | } |
445 | | |
446 | 13.6M | auto ch = consume(); |
447 | | |
448 | 13.6M | if (!m_state.escaping && ch == '`') { |
449 | 3.07M | m_state.buffer.append(ch); |
450 | 3.07M | auto& expansion = m_state.expansions.last().get<CommandExpansion>(); |
451 | 3.07M | expansion.range.length = m_state.position.end_offset - expansion.range.start - m_state.position.start_offset; |
452 | | |
453 | 3.07M | return ReductionResult { |
454 | 3.07M | .tokens = {}, |
455 | 3.07M | .next_reduction = m_state.previous_reduction, |
456 | 3.07M | }; |
457 | 3.07M | } |
458 | | |
459 | 10.5M | if (!m_state.escaping && ch == '\\') { |
460 | 40.9k | m_state.escaping = true; |
461 | 40.9k | return ReductionResult { |
462 | 40.9k | .tokens = {}, |
463 | 40.9k | .next_reduction = Reduction::CommandExpansion, |
464 | 40.9k | }; |
465 | 40.9k | } |
466 | | |
467 | 10.5M | m_state.escaping = false; |
468 | 10.5M | m_state.buffer.append(ch); |
469 | 10.5M | m_state.expansions.last().get<CommandExpansion>().command.append(ch); |
470 | 10.5M | return ReductionResult { |
471 | 10.5M | .tokens = {}, |
472 | 10.5M | .next_reduction = Reduction::CommandExpansion, |
473 | 10.5M | }; |
474 | 10.5M | } |
475 | | |
476 | | ErrorOr<Lexer::ReductionResult> Lexer::reduce_heredoc_contents() |
477 | 2.41M | { |
478 | 2.41M | if (m_lexer.is_eof()) { |
479 | 3.70k | auto tokens = TRY(Token::maybe_from_state(m_state)); |
480 | 3.70k | m_state.buffer.clear(); |
481 | 3.70k | m_state.position.start_offset = m_state.position.end_offset; |
482 | 3.70k | m_state.position.start_line = m_state.position.end_line; |
483 | | |
484 | 3.70k | return ReductionResult { |
485 | 3.70k | .tokens = move(tokens), |
486 | 3.70k | .next_reduction = Reduction::End, |
487 | 3.70k | }; |
488 | 3.70k | } |
489 | | |
490 | 2.40M | if (!m_state.escaping && consume_specific('\\')) { |
491 | 710 | m_state.escaping = true; |
492 | 710 | m_state.buffer.append('\\'); |
493 | 710 | return ReductionResult { |
494 | 710 | .tokens = {}, |
495 | 710 | .next_reduction = Reduction::HeredocContents, |
496 | 710 | }; |
497 | 710 | } |
498 | | |
499 | 2.40M | if (!m_state.escaping && consume_specific('$')) { |
500 | 11.7k | m_state.buffer.append('$'); |
501 | 11.7k | if (m_lexer.next_is("(")) |
502 | 2.84k | m_state.expansions.empend(CommandExpansion { .command = StringBuilder {}, .range = range(-1) }); |
503 | 8.92k | else |
504 | 8.92k | m_state.expansions.empend(ParameterExpansion { .parameter = StringBuilder {}, .range = range(-1) }); |
505 | | |
506 | 11.7k | return ReductionResult { |
507 | 11.7k | .tokens = {}, |
508 | 11.7k | .next_reduction = Reduction::Expansion, |
509 | 11.7k | }; |
510 | 11.7k | } |
511 | | |
512 | 2.39M | if (!m_state.escaping && consume_specific('`')) { |
513 | 4.22k | m_state.buffer.append('`'); |
514 | 4.22k | m_state.expansions.empend(CommandExpansion { .command = StringBuilder {}, .range = range(-1) }); |
515 | 4.22k | return ReductionResult { |
516 | 4.22k | .tokens = {}, |
517 | 4.22k | .next_reduction = Reduction::CommandExpansion, |
518 | 4.22k | }; |
519 | 4.22k | } |
520 | | |
521 | 2.39M | m_state.escaping = false; |
522 | 2.39M | m_state.buffer.append(consume()); |
523 | 2.39M | return ReductionResult { |
524 | 2.39M | .tokens = {}, |
525 | 2.39M | .next_reduction = Reduction::HeredocContents, |
526 | 2.39M | }; |
527 | 2.39M | } |
528 | | |
529 | | ErrorOr<Lexer::ReductionResult> Lexer::reduce_start() |
530 | 81.9M | { |
531 | 81.9M | auto was_on_new_line = m_state.on_new_line; |
532 | 81.9M | m_state.on_new_line = false; |
533 | | |
534 | 81.9M | if (m_lexer.is_eof()) { |
535 | 706k | auto tokens = TRY(Token::maybe_from_state(m_state)); |
536 | 705k | m_state.buffer.clear(); |
537 | 705k | m_state.expansions.clear(); |
538 | 705k | m_state.position.start_offset = m_state.position.end_offset; |
539 | 705k | m_state.position.start_line = m_state.position.end_line; |
540 | | |
541 | 705k | return ReductionResult { |
542 | 705k | .tokens = move(tokens), |
543 | 705k | .next_reduction = Reduction::End, |
544 | 705k | }; |
545 | 706k | } |
546 | | |
547 | 81.2M | if (was_on_new_line && !m_state.heredoc_entries.is_empty()) { |
548 | 2.52k | auto const& entry = m_state.heredoc_entries.first(); |
549 | | |
550 | 2.52k | auto start_index = m_lexer.tell(); |
551 | 2.52k | Optional<size_t> end_index; |
552 | | |
553 | 30.1M | for (; !m_lexer.is_eof();) { |
554 | 30.1M | auto index = m_lexer.tell(); |
555 | 30.1M | auto possible_end_index = m_lexer.tell(); |
556 | 30.1M | if (m_lexer.consume_specific('\n')) { |
557 | 1.22M | if (entry.dedent) |
558 | 117k | m_lexer.ignore_while(is_any_of("\t"sv)); |
559 | 1.22M | if (m_lexer.consume_specific(entry.key.bytes_as_string_view())) { |
560 | 22.5k | if (m_lexer.consume_specific('\n') || m_lexer.is_eof()) { |
561 | 158 | end_index = possible_end_index; |
562 | 158 | break; |
563 | 158 | } |
564 | 22.5k | } |
565 | 1.22M | } |
566 | 30.1M | if (m_lexer.tell() == index) |
567 | 28.9M | m_lexer.ignore(); |
568 | 30.1M | } |
569 | | |
570 | 2.52k | auto contents = m_lexer.input().substring_view(start_index, end_index.value_or(m_lexer.tell()) - start_index); |
571 | 2.52k | reconsume(contents); |
572 | 2.52k | if (end_index.has_value()) |
573 | 158 | reconsume(m_lexer.input().substring_view_starting_after_substring(contents).substring_view(0, m_lexer.tell() - *end_index)); |
574 | | |
575 | 2.52k | m_state.buffer.clear(); |
576 | 2.52k | m_state.buffer.append(contents); |
577 | | |
578 | 2.52k | auto token = TRY(Token::maybe_from_state(m_state)).first(); |
579 | 2.49k | token.relevant_heredoc_key = entry.key; |
580 | 2.49k | token.type = Token::Type::HeredocContents; |
581 | | |
582 | 2.49k | m_state.heredoc_entries.take_first(); |
583 | | |
584 | 2.49k | m_state.on_new_line = true; |
585 | | |
586 | 2.49k | m_state.buffer.clear(); |
587 | 2.49k | m_state.position.start_offset = m_state.position.end_offset; |
588 | 2.49k | m_state.position.start_line = m_state.position.end_line; |
589 | | |
590 | 2.49k | Vector<Token> tokens { move(token), Token::newline() }; |
591 | | |
592 | 2.49k | return ReductionResult { |
593 | 2.49k | .tokens = move(tokens), |
594 | 2.49k | .next_reduction = Reduction::Start, |
595 | 2.49k | }; |
596 | 2.52k | } |
597 | | |
598 | 81.2M | if (m_state.escaping && consume_specific('\n')) { |
599 | 228 | m_state.escaping = false; |
600 | | |
601 | 228 | auto buffer = m_state.buffer.to_byte_string().substring(0, m_state.buffer.length() - 1); |
602 | 228 | m_state.buffer.clear(); |
603 | 228 | m_state.buffer.append(buffer); |
604 | | |
605 | 228 | return ReductionResult { |
606 | 228 | .tokens = {}, |
607 | 228 | .next_reduction = Reduction::Start, |
608 | 228 | }; |
609 | 228 | } |
610 | | |
611 | 81.2M | if (!m_state.escaping && m_lexer.peek() == '#' && m_state.buffer.is_empty()) { |
612 | 243k | consume(); |
613 | 243k | return ReductionResult { |
614 | 243k | .tokens = {}, |
615 | 243k | .next_reduction = Reduction::Comment, |
616 | 243k | }; |
617 | 243k | } |
618 | | |
619 | 80.9M | if (!m_state.escaping && consume_specific('\n')) { |
620 | 2.49M | auto tokens = TRY(Token::maybe_from_state(m_state)); |
621 | 2.49M | tokens.append(Token::newline()); |
622 | | |
623 | 2.49M | m_state.on_new_line = true; |
624 | | |
625 | 2.49M | m_state.buffer.clear(); |
626 | 2.49M | m_state.expansions.clear(); |
627 | 2.49M | m_state.position.start_offset = m_state.position.end_offset; |
628 | 2.49M | m_state.position.start_line = m_state.position.end_line; |
629 | | |
630 | 2.49M | return ReductionResult { |
631 | 2.49M | .tokens = move(tokens), |
632 | 2.49M | .next_reduction = Reduction::Start, |
633 | 2.49M | }; |
634 | 2.49M | } |
635 | | |
636 | 78.4M | if (!m_state.escaping && consume_specific('\\')) { |
637 | 27.2k | m_state.escaping = true; |
638 | 27.2k | m_state.buffer.append('\\'); |
639 | 27.2k | return ReductionResult { |
640 | 27.2k | .tokens = {}, |
641 | 27.2k | .next_reduction = Reduction::Start, |
642 | 27.2k | }; |
643 | 27.2k | } |
644 | | |
645 | 78.4M | if (!m_state.escaping && consume_specific('\'')) { |
646 | 2.34k | m_state.buffer.append('\''); |
647 | 2.34k | return ReductionResult { |
648 | 2.34k | .tokens = {}, |
649 | 2.34k | .next_reduction = Reduction::SingleQuotedString, |
650 | 2.34k | }; |
651 | 2.34k | } |
652 | | |
653 | 78.4M | if (!m_state.escaping && consume_specific('"')) { |
654 | 287k | m_state.buffer.append('"'); |
655 | 287k | return ReductionResult { |
656 | 287k | .tokens = {}, |
657 | 287k | .next_reduction = Reduction::DoubleQuotedString, |
658 | 287k | }; |
659 | 287k | } |
660 | | |
661 | 78.1M | if (!m_state.escaping && is_ascii_space(m_lexer.peek())) { |
662 | 2.42M | consume(); |
663 | 2.42M | auto tokens = TRY(Token::maybe_from_state(m_state)); |
664 | 2.42M | m_state.buffer.clear(); |
665 | 2.42M | m_state.expansions.clear(); |
666 | 2.42M | m_state.position.start_offset = m_state.position.end_offset; |
667 | 2.42M | m_state.position.start_line = m_state.position.end_line; |
668 | | |
669 | 2.42M | return ReductionResult { |
670 | 2.42M | .tokens = move(tokens), |
671 | 2.42M | .next_reduction = Reduction::Start, |
672 | 2.42M | }; |
673 | 2.42M | } |
674 | | |
675 | 75.7M | if (!m_state.escaping && consume_specific('$')) { |
676 | 4.48M | m_state.buffer.append('$'); |
677 | 4.48M | if (m_lexer.next_is("(")) |
678 | 65.1k | m_state.expansions.empend(CommandExpansion { .command = StringBuilder {}, .range = range(-1) }); |
679 | 4.42M | else |
680 | 4.42M | m_state.expansions.empend(ParameterExpansion { .parameter = StringBuilder {}, .range = range(-1) }); |
681 | | |
682 | 4.48M | return ReductionResult { |
683 | 4.48M | .tokens = {}, |
684 | 4.48M | .next_reduction = Reduction::Expansion, |
685 | 4.48M | }; |
686 | 4.48M | } |
687 | | |
688 | 71.2M | if (!m_state.escaping && consume_specific('`')) { |
689 | 3.00M | m_state.buffer.append('`'); |
690 | 3.00M | m_state.expansions.empend(CommandExpansion { .command = StringBuilder {}, .range = range(-1) }); |
691 | 3.00M | return ReductionResult { |
692 | 3.00M | .tokens = {}, |
693 | 3.00M | .next_reduction = Reduction::CommandExpansion, |
694 | 3.00M | }; |
695 | 3.00M | } |
696 | | |
697 | 68.2M | if (!m_state.escaping && m_state.in_skip_mode && is_any_of("})"sv)(m_lexer.peek())) { |
698 | | // That's an eof for us. |
699 | 99.2k | return ReductionResult { |
700 | 99.2k | .tokens = {}, |
701 | 99.2k | .next_reduction = Reduction::None, |
702 | 99.2k | }; |
703 | 99.2k | } |
704 | | |
705 | 68.1M | if (!m_state.escaping && is_part_of_operator(""sv, m_lexer.peek())) { |
706 | 8.42M | auto tokens = TRY(Token::maybe_from_state(m_state)); |
707 | 8.42M | m_state.buffer.clear(); |
708 | 8.42M | m_state.buffer.append(consume()); |
709 | 8.42M | m_state.expansions.clear(); |
710 | 8.42M | m_state.position.start_offset = m_state.position.end_offset; |
711 | 8.42M | m_state.position.start_line = m_state.position.end_line; |
712 | | |
713 | 8.42M | return ReductionResult { |
714 | 8.42M | .tokens = move(tokens), |
715 | 8.42M | .next_reduction = Reduction::Operator, |
716 | 8.42M | }; |
717 | 8.42M | } |
718 | | |
719 | 59.7M | m_state.escaping = false; |
720 | 59.7M | m_state.buffer.append(consume()); |
721 | 59.7M | return ReductionResult { |
722 | 59.7M | .tokens = {}, |
723 | 59.7M | .next_reduction = Reduction::Start, |
724 | 59.7M | }; |
725 | 68.1M | } |
726 | | |
727 | | ErrorOr<Lexer::ReductionResult> Lexer::reduce_arithmetic_expansion() |
728 | 1.71M | { |
729 | 1.71M | if (m_lexer.is_eof()) { |
730 | 14 | auto& expansion = m_state.expansions.last().get<ArithmeticExpansion>(); |
731 | 14 | expansion.range.length = m_state.position.end_offset - expansion.range.start - m_state.position.start_offset; |
732 | | |
733 | 14 | return ReductionResult { |
734 | 14 | .tokens = { Token::continuation("$(("_string) }, |
735 | 14 | .next_reduction = m_state.previous_reduction, |
736 | 14 | }; |
737 | 14 | } |
738 | | |
739 | 1.71M | if (m_lexer.peek() == ')' && m_state.buffer.string_view().ends_with(')')) { |
740 | 108k | m_state.buffer.append(consume()); |
741 | 108k | auto& expansion = m_state.expansions.last().get<ArithmeticExpansion>(); |
742 | 108k | expansion.expression = TRY(String::from_utf8(expansion.value.string_view().substring_view(0, expansion.value.length() - 1))); |
743 | 108k | expansion.value.clear(); |
744 | 108k | expansion.range.length = m_state.position.end_offset - expansion.range.start - m_state.position.start_offset; |
745 | | |
746 | 108k | return ReductionResult { |
747 | 108k | .tokens = {}, |
748 | 108k | .next_reduction = m_state.previous_reduction, |
749 | 108k | }; |
750 | 108k | } |
751 | | |
752 | 1.60M | auto ch = consume(); |
753 | 1.60M | m_state.buffer.append(ch); |
754 | 1.60M | m_state.expansions.last().get<ArithmeticExpansion>().value.append(ch); |
755 | 1.60M | return ReductionResult { |
756 | 1.60M | .tokens = {}, |
757 | 1.60M | .next_reduction = Reduction::ArithmeticExpansion, |
758 | 1.60M | }; |
759 | 1.71M | } |
760 | | |
761 | | ErrorOr<Lexer::ReductionResult> Lexer::reduce_special_parameter_expansion() |
762 | 564k | { |
763 | 564k | auto ch = consume(); |
764 | 564k | m_state.buffer.append(ch); |
765 | 564k | m_state.expansions.last() = ParameterExpansion { |
766 | 564k | .parameter = StringBuilder {}, |
767 | 564k | .range = range(-2), |
768 | 564k | }; |
769 | 564k | auto& expansion = m_state.expansions.last().get<ParameterExpansion>(); |
770 | 564k | expansion.parameter.append(ch); |
771 | 564k | expansion.range.length = m_state.position.end_offset - expansion.range.start - m_state.position.start_offset; |
772 | | |
773 | 564k | return ReductionResult { |
774 | 564k | .tokens = {}, |
775 | 564k | .next_reduction = m_state.previous_reduction, |
776 | 564k | }; |
777 | 564k | } |
778 | | |
779 | | ErrorOr<Lexer::ReductionResult> Lexer::reduce_parameter_expansion() |
780 | 3.26M | { |
781 | 3.26M | auto& expansion = m_state.expansions.last().get<ParameterExpansion>(); |
782 | | |
783 | 3.26M | if (m_lexer.is_eof()) { |
784 | 987 | return ReductionResult { |
785 | 987 | .tokens = {}, |
786 | 987 | .next_reduction = Reduction::Start, |
787 | 987 | }; |
788 | 987 | } |
789 | | |
790 | 3.26M | auto next = m_lexer.peek(); |
791 | 3.26M | if (is_ascii_alphanumeric(next) || next == '_') { |
792 | 3.10M | m_state.buffer.append(consume()); |
793 | 3.10M | expansion.parameter.append(next); |
794 | 3.10M | expansion.range.length = m_state.position.end_offset - expansion.range.start - m_state.position.start_offset; |
795 | | |
796 | 3.10M | return ReductionResult { |
797 | 3.10M | .tokens = {}, |
798 | 3.10M | .next_reduction = Reduction::ParameterExpansion, |
799 | 3.10M | }; |
800 | 3.10M | } |
801 | | |
802 | 154k | return reduce(m_state.previous_reduction); |
803 | 3.26M | } |
804 | | |
805 | | ErrorOr<Lexer::ReductionResult> Lexer::reduce_command_or_arithmetic_substitution_expansion() |
806 | 223k | { |
807 | 223k | auto ch = m_lexer.peek(); |
808 | 223k | if (ch == '(' && m_state.buffer.string_view().ends_with("$("sv)) { |
809 | 108k | m_state.buffer.append(consume()); |
810 | 108k | m_state.expansions.last() = ArithmeticExpansion { |
811 | 108k | .expression = {}, |
812 | 108k | .value = StringBuilder {}, |
813 | 108k | .range = range(-2) |
814 | 108k | }; |
815 | 108k | return ReductionResult { |
816 | 108k | .tokens = {}, |
817 | 108k | .next_reduction = Reduction::ArithmeticExpansion, |
818 | 108k | }; |
819 | 108k | } |
820 | | |
821 | 115k | auto saved_position = m_state.position; |
822 | 115k | { |
823 | 115k | auto skip_mode = switch_to_skip_mode(); |
824 | | |
825 | 115k | auto next_reduction = Reduction::Start; |
826 | 32.4M | do { |
827 | 32.4M | auto result = TRY(reduce(next_reduction)); |
828 | 32.4M | next_reduction = result.next_reduction; |
829 | 32.4M | } while (next_reduction != Reduction::None); |
830 | 115k | saved_position = m_state.position; |
831 | 115k | } |
832 | | |
833 | 115k | auto const skipped_text = m_lexer.input().substring_view(m_state.position.end_offset, saved_position.end_offset - m_state.position.end_offset); |
834 | 115k | m_state.position.end_offset = saved_position.end_offset; |
835 | 115k | m_state.position.end_line = saved_position.end_line; |
836 | | |
837 | 115k | m_state.buffer.append(skipped_text); |
838 | 115k | m_state.expansions.last().get<CommandExpansion>().command.append(skipped_text); |
839 | 115k | m_state.expansions.last().visit([&](auto& expansion) { |
840 | 115k | expansion.range.length = m_state.position.end_offset - expansion.range.start - m_state.position.start_offset; |
841 | 115k | }); Unexecuted instantiation: PosixLexer.cpp:auto Shell::Posix::Lexer::reduce_command_or_arithmetic_substitution_expansion()::$_0::operator()<Shell::Posix::ParameterExpansion>(Shell::Posix::ParameterExpansion&) const PosixLexer.cpp:auto Shell::Posix::Lexer::reduce_command_or_arithmetic_substitution_expansion()::$_0::operator()<Shell::Posix::CommandExpansion>(Shell::Posix::CommandExpansion&) const Line | Count | Source | 839 | 115k | m_state.expansions.last().visit([&](auto& expansion) { | 840 | 115k | expansion.range.length = m_state.position.end_offset - expansion.range.start - m_state.position.start_offset; | 841 | 115k | }); |
Unexecuted instantiation: PosixLexer.cpp:auto Shell::Posix::Lexer::reduce_command_or_arithmetic_substitution_expansion()::$_0::operator()<Shell::Posix::ArithmeticExpansion>(Shell::Posix::ArithmeticExpansion&) const |
842 | | |
843 | 115k | if (m_lexer.is_eof()) { |
844 | 15.8k | return ReductionResult { |
845 | 15.8k | .tokens = { Token::continuation("$("_string) }, |
846 | 15.8k | .next_reduction = m_state.previous_reduction, |
847 | 15.8k | }; |
848 | 15.8k | } |
849 | | |
850 | 99.2k | ch = m_lexer.peek(); |
851 | 99.2k | if (ch == '(' && m_state.buffer.string_view().ends_with("$("sv)) { |
852 | 0 | m_state.buffer.append(consume()); |
853 | 0 | m_state.expansions.last() = ArithmeticExpansion { |
854 | 0 | .expression = {}, |
855 | 0 | .value = m_state.expansions.last().get<CommandExpansion>().command, |
856 | 0 | .range = range(-2) |
857 | 0 | }; |
858 | 0 | return ReductionResult { |
859 | 0 | .tokens = {}, |
860 | 0 | .next_reduction = Reduction::ArithmeticExpansion, |
861 | 0 | }; |
862 | 0 | } |
863 | | |
864 | 99.2k | if (ch == ')') { |
865 | 60.2k | m_state.buffer.append(consume()); |
866 | 60.2k | m_state.expansions.last().visit([&](auto& expansion) { |
867 | 60.2k | expansion.range.length = m_state.position.end_offset - expansion.range.start - m_state.position.start_offset; |
868 | 60.2k | }); Unexecuted instantiation: PosixLexer.cpp:auto Shell::Posix::Lexer::reduce_command_or_arithmetic_substitution_expansion()::$_1::operator()<Shell::Posix::ParameterExpansion>(Shell::Posix::ParameterExpansion&) const PosixLexer.cpp:auto Shell::Posix::Lexer::reduce_command_or_arithmetic_substitution_expansion()::$_1::operator()<Shell::Posix::CommandExpansion>(Shell::Posix::CommandExpansion&) const Line | Count | Source | 866 | 60.2k | m_state.expansions.last().visit([&](auto& expansion) { | 867 | 60.2k | expansion.range.length = m_state.position.end_offset - expansion.range.start - m_state.position.start_offset; | 868 | 60.2k | }); |
Unexecuted instantiation: PosixLexer.cpp:auto Shell::Posix::Lexer::reduce_command_or_arithmetic_substitution_expansion()::$_1::operator()<Shell::Posix::ArithmeticExpansion>(Shell::Posix::ArithmeticExpansion&) const |
869 | 60.2k | return ReductionResult { |
870 | 60.2k | .tokens = {}, |
871 | 60.2k | .next_reduction = m_state.previous_reduction, |
872 | 60.2k | }; |
873 | 60.2k | } |
874 | | |
875 | 39.0k | m_state.buffer.append(consume()); |
876 | 39.0k | m_state.expansions.last().get<CommandExpansion>().command.append(ch); |
877 | 39.0k | return ReductionResult { |
878 | 39.0k | .tokens = {}, |
879 | 39.0k | .next_reduction = Reduction::CommandOrArithmeticSubstitutionExpansion, |
880 | 39.0k | }; |
881 | 99.2k | } |
882 | | |
883 | | ErrorOr<Lexer::ReductionResult> Lexer::reduce_extended_parameter_expansion() |
884 | 5.34M | { |
885 | 5.34M | auto& expansion = m_state.expansions.last().get<ParameterExpansion>(); |
886 | | |
887 | 5.34M | if (m_lexer.is_eof()) { |
888 | 163 | return ReductionResult { |
889 | 163 | .tokens = { Token::continuation("${"_string) }, |
890 | 163 | .next_reduction = m_state.previous_reduction, |
891 | 163 | }; |
892 | 163 | } |
893 | | |
894 | 5.34M | auto ch = m_lexer.peek(); |
895 | 5.34M | if (ch == '}') { |
896 | 19.5k | m_state.buffer.append(consume()); |
897 | 19.5k | expansion.range.length = m_state.position.end_offset - expansion.range.start - m_state.position.start_offset; |
898 | | |
899 | 19.5k | return ReductionResult { |
900 | 19.5k | .tokens = {}, |
901 | 19.5k | .next_reduction = m_state.previous_reduction, |
902 | 19.5k | }; |
903 | 19.5k | } |
904 | | |
905 | 5.32M | m_state.buffer.append(consume()); |
906 | 5.32M | expansion.parameter.append(ch); |
907 | 5.32M | expansion.range.length = m_state.position.end_offset - expansion.range.start - m_state.position.start_offset; |
908 | | |
909 | 5.32M | return ReductionResult { |
910 | 5.32M | .tokens = {}, |
911 | 5.32M | .next_reduction = Reduction::ExtendedParameterExpansion, |
912 | 5.32M | }; |
913 | 5.34M | } |
914 | | |
915 | | StringView Token::type_name() const |
916 | 2.83M | { |
917 | 2.83M | switch (type) { |
918 | 26.8k | case Type::Eof: |
919 | 26.8k | return "Eof"sv; |
920 | 144k | case Type::Newline: |
921 | 144k | return "Newline"sv; |
922 | 153 | case Type::Continuation: |
923 | 153 | return "Continuation"sv; |
924 | 0 | case Type::Token: |
925 | 0 | return "Token"sv; |
926 | 230k | case Type::And: |
927 | 230k | return "And"sv; |
928 | 971k | case Type::Pipe: |
929 | 971k | return "Pipe"sv; |
930 | 26.3k | case Type::OpenParen: |
931 | 26.3k | return "OpenParen"sv; |
932 | 1.04M | case Type::CloseParen: |
933 | 1.04M | return "CloseParen"sv; |
934 | 524 | case Type::Great: |
935 | 524 | return "Great"sv; |
936 | 43 | case Type::Less: |
937 | 43 | return "Less"sv; |
938 | 306 | case Type::AndIf: |
939 | 306 | return "AndIf"sv; |
940 | 177k | case Type::OrIf: |
941 | 177k | return "OrIf"sv; |
942 | 380 | case Type::DoubleSemicolon: |
943 | 380 | return "DoubleSemicolon"sv; |
944 | 40 | case Type::DoubleLess: |
945 | 40 | return "DoubleLess"sv; |
946 | 2 | case Type::DoubleGreat: |
947 | 2 | return "DoubleGreat"sv; |
948 | 48.6k | case Type::LessAnd: |
949 | 48.6k | return "LessAnd"sv; |
950 | 12.7k | case Type::GreatAnd: |
951 | 12.7k | return "GreatAnd"sv; |
952 | 6 | case Type::LessGreat: |
953 | 6 | return "LessGreat"sv; |
954 | 0 | case Type::DoubleLessDash: |
955 | 0 | return "DoubleLessDash"sv; |
956 | 1 | case Type::Clobber: |
957 | 1 | return "Clobber"sv; |
958 | 72.5k | case Type::Semicolon: |
959 | 72.5k | return "Semicolon"sv; |
960 | 0 | case Type::HeredocContents: |
961 | 0 | return "HeredocContents"sv; |
962 | 473 | case Type::AssignmentWord: |
963 | 473 | return "AssignmentWord"sv; |
964 | 62.4k | case Type::ListAssignmentWord: |
965 | 62.4k | return "ListAssignmentWord"sv; |
966 | 0 | case Type::Bang: |
967 | 0 | return "Bang"sv; |
968 | 20 | case Type::Case: |
969 | 20 | return "Case"sv; |
970 | 119 | case Type::CloseBrace: |
971 | 119 | return "CloseBrace"sv; |
972 | 9 | case Type::Do: |
973 | 9 | return "Do"sv; |
974 | 0 | case Type::Done: |
975 | 0 | return "Done"sv; |
976 | 278 | case Type::Elif: |
977 | 278 | return "Elif"sv; |
978 | 0 | case Type::Else: |
979 | 0 | return "Else"sv; |
980 | 0 | case Type::Esac: |
981 | 0 | return "Esac"sv; |
982 | 159 | case Type::Fi: |
983 | 159 | return "Fi"sv; |
984 | 349 | case Type::For: |
985 | 349 | return "For"sv; |
986 | 773 | case Type::If: |
987 | 773 | return "If"sv; |
988 | 1.30k | case Type::In: |
989 | 1.30k | return "In"sv; |
990 | 526 | case Type::IoNumber: |
991 | 526 | return "IoNumber"sv; |
992 | 537 | case Type::OpenBrace: |
993 | 537 | return "OpenBrace"sv; |
994 | 0 | case Type::Then: |
995 | 0 | return "Then"sv; |
996 | 0 | case Type::Until: |
997 | 0 | return "Until"sv; |
998 | 1.12k | case Type::VariableName: |
999 | 1.12k | return "VariableName"sv; |
1000 | 0 | case Type::While: |
1001 | 0 | return "While"sv; |
1002 | 5.65k | case Type::Word: |
1003 | 5.65k | return "Word"sv; |
1004 | 2.83M | } |
1005 | 0 | return "Idk"sv; |
1006 | 2.83M | } |
1007 | | |
1008 | | } |