Serenity Operating System
at master 2251 lines 79 kB view raw
1/* 2 * Copyright (c) 2020-2021, the SerenityOS developers. 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include "Parser.h" 8#include "Shell.h" 9#include <AK/AllOf.h> 10#include <AK/GenericLexer.h> 11#include <AK/ScopeGuard.h> 12#include <AK/ScopedValueRollback.h> 13#include <AK/TemporaryChange.h> 14#include <ctype.h> 15#include <stdio.h> 16#include <unistd.h> 17 18namespace Shell { 19 20Parser::SavedOffset Parser::save_offset() const 21{ 22 return { m_offset, m_line }; 23} 24 25char Parser::peek() 26{ 27 if (at_end()) 28 return 0; 29 30 VERIFY(m_offset < m_input.length()); 31 32 auto ch = m_input[m_offset]; 33 if (ch == '\\' && m_input.length() > m_offset + 1 && m_input[m_offset + 1] == '\n') { 34 m_offset += 2; 35 ++m_line.line_number; 36 m_line.line_column = 0; 37 return peek(); 38 } 39 40 return ch; 41} 42 43char Parser::consume() 44{ 45 if (at_end()) 46 return 0; 47 48 auto ch = peek(); 49 ++m_offset; 50 51 if (ch == '\n') { 52 ++m_line.line_number; 53 m_line.line_column = 0; 54 } else { 55 ++m_line.line_column; 56 } 57 58 return ch; 59} 60 61bool Parser::expect(char ch) 62{ 63 return expect(StringView { &ch, 1 }); 64} 65 66bool Parser::expect(StringView expected) 67{ 68 auto offset_at_start = m_offset; 69 auto line_at_start = line(); 70 71 if (expected.length() + m_offset > m_input.length()) 72 return false; 73 74 for (auto& c : expected) { 75 if (peek() != c) { 76 restore_to(offset_at_start, line_at_start); 77 return false; 78 } 79 80 consume(); 81 } 82 83 return true; 84} 85 86template<typename A, typename... Args> 87NonnullRefPtr<A> Parser::create(Args&&... args) 88{ 89 return adopt_ref(*new A(AST::Position { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, forward<Args>(args)...)); 90} 91 92[[nodiscard]] OwnPtr<Parser::ScopedOffset> Parser::push_start() 93{ 94 return make<ScopedOffset>(m_rule_start_offsets, m_rule_start_lines, m_offset, m_line.line_number, m_line.line_column); 95} 96 97Parser::Offset Parser::current_position() 98{ 99 return Offset { m_offset, { m_line.line_number, m_line.line_column } }; 100} 101 102static constexpr bool is_whitespace(char c) 103{ 104 return c == ' ' || c == '\t'; 105} 106 107static constexpr bool is_digit(char c) 108{ 109 return c <= '9' && c >= '0'; 110} 111 112static constexpr auto is_not(char c) 113{ 114 return [c](char ch) { return ch != c; }; 115} 116 117static inline char to_byte(char a, char b) 118{ 119 char buf[3] { a, b, 0 }; 120 return strtol(buf, nullptr, 16); 121} 122 123RefPtr<AST::Node> Parser::parse() 124{ 125 m_offset = 0; 126 m_line = { 0, 0 }; 127 128 auto toplevel = parse_toplevel(); 129 130 if (m_offset < m_input.length()) { 131 // Parsing stopped midway, this is a syntax error. 132 auto error_start = push_start(); 133 while (!at_end()) 134 consume(); 135 auto syntax_error_node = create<AST::SyntaxError>("Unexpected tokens past the end"_string.release_value_but_fixme_should_propagate_errors()); 136 if (!toplevel) 137 toplevel = move(syntax_error_node); 138 else if (!toplevel->is_syntax_error()) 139 toplevel->set_is_syntax_error(*syntax_error_node); 140 } 141 142 return toplevel; 143} 144 145RefPtr<AST::Node> Parser::parse_as_single_expression() 146{ 147 auto input = Shell::escape_token_for_double_quotes(m_input); 148 Parser parser { input }; 149 return parser.parse_expression(); 150} 151 152Vector<NonnullRefPtr<AST::Node>> Parser::parse_as_multiple_expressions() 153{ 154 Vector<NonnullRefPtr<AST::Node>> nodes; 155 for (;;) { 156 consume_while(is_whitespace); 157 auto node = parse_expression(); 158 if (!node) 159 node = parse_redirection(); 160 if (!node) 161 return nodes; 162 nodes.append(node.release_nonnull()); 163 } 164} 165 166RefPtr<AST::Node> Parser::parse_toplevel() 167{ 168 auto rule_start = push_start(); 169 170 SequenceParseResult result; 171 Vector<NonnullRefPtr<AST::Node>> sequence; 172 Vector<AST::Position> positions; 173 do { 174 result = parse_sequence(); 175 if (result.entries.is_empty()) 176 break; 177 178 sequence.extend(move(result.entries)); 179 positions.extend(move(result.separator_positions)); 180 } while (result.decision == ShouldReadMoreSequences::Yes); 181 182 if (sequence.is_empty()) 183 return nullptr; 184 185 return create<AST::Execute>( 186 create<AST::Sequence>(move(sequence), move(positions))); 187} 188 189Parser::SequenceParseResult Parser::parse_sequence() 190{ 191 Vector<NonnullRefPtr<AST::Node>> left; 192 auto read_terminators = [&](bool consider_tabs_and_spaces) { 193 if (m_heredoc_initiations.is_empty()) { 194 discard_terminators:; 195 consume_while(is_any_of(consider_tabs_and_spaces ? " \t\n;"sv : "\n;"sv)); 196 } else { 197 for (;;) { 198 if (consider_tabs_and_spaces && (peek() == '\t' || peek() == ' ')) { 199 consume(); 200 continue; 201 } 202 if (peek() == ';') { 203 consume(); 204 continue; 205 } 206 if (peek() == '\n') { 207 auto rule_start = push_start(); 208 consume(); 209 if (!parse_heredoc_entries()) { 210 StringBuilder error_builder; 211 error_builder.append("Expected to find heredoc entries for "sv); 212 bool first = true; 213 for (auto& entry : m_heredoc_initiations) { 214 if (first) 215 error_builder.appendff("{} (at {}:{})", entry.end, entry.node->position().start_line.line_column, entry.node->position().start_line.line_number); 216 else 217 error_builder.appendff(", {} (at {}:{})", entry.end, entry.node->position().start_line.line_column, entry.node->position().start_line.line_number); 218 first = false; 219 } 220 left.append(create<AST::SyntaxError>(error_builder.to_string().release_value_but_fixme_should_propagate_errors(), true)); 221 // Just read the rest of the newlines 222 goto discard_terminators; 223 } 224 continue; 225 } 226 break; 227 } 228 } 229 }; 230 231 read_terminators(true); 232 233 auto rule_start = push_start(); 234 { 235 auto var_decls = parse_variable_decls(); 236 if (var_decls) 237 left.append(var_decls.release_nonnull()); 238 } 239 240 auto pos_before_seps = save_offset(); 241 242 switch (peek()) { 243 case '}': 244 return { move(left), {}, ShouldReadMoreSequences::No }; 245 case '\n': 246 read_terminators(false); 247 [[fallthrough]]; 248 case ';': { 249 if (left.is_empty()) 250 break; 251 252 consume_while(is_any_of("\n;"sv)); 253 auto pos_after_seps = save_offset(); 254 AST::Position separator_position { pos_before_seps.offset, pos_after_seps.offset, pos_before_seps.line, pos_after_seps.line }; 255 256 return { move(left), { move(separator_position) }, ShouldReadMoreSequences::Yes }; 257 } 258 default: 259 break; 260 } 261 262 auto first_entry = parse_function_decl(); 263 264 Vector<AST::Position> separator_positions; 265 266 if (!first_entry) 267 first_entry = parse_or_logical_sequence(); 268 269 if (!first_entry) 270 return { move(left), {}, ShouldReadMoreSequences::No }; 271 272 left.append(first_entry.release_nonnull()); 273 separator_positions.empend(pos_before_seps.offset, pos_before_seps.offset, pos_before_seps.line, pos_before_seps.line); 274 275 consume_while(is_whitespace); 276 277 pos_before_seps = save_offset(); 278 switch (peek()) { 279 case '\n': 280 read_terminators(false); 281 [[fallthrough]]; 282 case ';': { 283 consume_while(is_any_of("\n;"sv)); 284 auto pos_after_seps = save_offset(); 285 separator_positions.empend(pos_before_seps.offset, pos_after_seps.offset, pos_before_seps.line, pos_after_seps.line); 286 return { move(left), move(separator_positions), ShouldReadMoreSequences::Yes }; 287 } 288 case '&': { 289 consume(); 290 auto pos_after_seps = save_offset(); 291 auto bg = create<AST::Background>(left.take_last()); // Execute Background 292 left.append(move(bg)); 293 separator_positions.empend(pos_before_seps.offset, pos_after_seps.offset, pos_before_seps.line, pos_after_seps.line); 294 return { move(left), move(separator_positions), ShouldReadMoreSequences::Yes }; 295 } 296 default: 297 return { move(left), move(separator_positions), ShouldReadMoreSequences::No }; 298 } 299} 300 301RefPtr<AST::Node> Parser::parse_variable_decls() 302{ 303 auto rule_start = push_start(); 304 305 consume_while(is_whitespace); 306 307 auto pos_before_name = save_offset(); 308 auto var_name = consume_while(is_word_character); 309 if (var_name.is_empty()) 310 return nullptr; 311 312 if (!expect('=')) { 313 restore_to(pos_before_name.offset, pos_before_name.line); 314 return nullptr; 315 } 316 317 auto name_expr = create<AST::BarewordLiteral>(String::from_utf8(var_name).release_value_but_fixme_should_propagate_errors()); 318 319 auto start = push_start(); 320 auto expression = parse_expression(); 321 if (!expression || expression->is_syntax_error()) { 322 restore_to(*start); 323 if (peek() == '(') { 324 consume(); 325 auto command = parse_pipe_sequence(); 326 if (!command) 327 restore_to(*start); 328 else if (!expect(')')) 329 command->set_is_syntax_error(*create<AST::SyntaxError>("Expected a terminating close paren"_string.release_value_but_fixme_should_propagate_errors(), true)); 330 expression = command; 331 } 332 } 333 if (!expression) { 334 if (is_whitespace(peek())) { 335 auto string_start = push_start(); 336 expression = create<AST::StringLiteral>(String {}, AST::StringLiteral::EnclosureType::None); 337 } else { 338 restore_to(pos_before_name.offset, pos_before_name.line); 339 return nullptr; 340 } 341 } 342 343 Vector<AST::VariableDeclarations::Variable> variables; 344 variables.append({ move(name_expr), expression.release_nonnull() }); 345 346 if (consume_while(is_whitespace).is_empty()) 347 return create<AST::VariableDeclarations>(move(variables)); 348 349 auto rest = parse_variable_decls(); 350 if (!rest) 351 return create<AST::VariableDeclarations>(move(variables)); 352 353 VERIFY(rest->is_variable_decls()); 354 auto* rest_decl = static_cast<AST::VariableDeclarations*>(rest.ptr()); 355 356 variables.extend(rest_decl->variables()); 357 358 return create<AST::VariableDeclarations>(move(variables)); 359} 360 361RefPtr<AST::Node> Parser::parse_function_decl() 362{ 363 auto rule_start = push_start(); 364 365 auto restore = [&] { 366 restore_to(*rule_start); 367 return nullptr; 368 }; 369 370 consume_while(is_whitespace); 371 auto pos_before_name = save_offset(); 372 auto function_name = consume_while(is_word_character); 373 auto pos_after_name = save_offset(); 374 if (function_name.is_empty()) 375 return restore(); 376 377 if (!expect('(')) 378 return restore(); 379 380 Vector<AST::NameWithPosition> arguments; 381 for (;;) { 382 consume_while(is_whitespace); 383 384 if (expect(')')) 385 break; 386 387 auto name_offset = m_offset; 388 auto start_line = line(); 389 auto arg_name = consume_while(is_word_character); 390 if (arg_name.is_empty()) { 391 // FIXME: Should this be a syntax error, or just return? 392 return restore(); 393 } 394 arguments.append({ String::from_utf8(arg_name).release_value_but_fixme_should_propagate_errors(), { name_offset, m_offset, start_line, line() } }); 395 } 396 397 consume_while(is_any_of("\n\t "sv)); 398 399 { 400 RefPtr<AST::Node> syntax_error; 401 { 402 auto obrace_error_start = push_start(); 403 syntax_error = create<AST::SyntaxError>("Expected an open brace '{' to start a function body"_string.release_value_but_fixme_should_propagate_errors(), true); 404 } 405 if (!expect('{')) { 406 return create<AST::FunctionDeclaration>( 407 AST::NameWithPosition { 408 String::from_utf8(function_name).release_value_but_fixme_should_propagate_errors(), 409 { pos_before_name.offset, pos_after_name.offset, pos_before_name.line, pos_after_name.line } }, 410 move(arguments), 411 move(syntax_error)); 412 } 413 } 414 415 TemporaryChange controls { m_continuation_controls_allowed, false }; 416 auto body = parse_toplevel(); 417 418 { 419 RefPtr<AST::SyntaxError> syntax_error; 420 { 421 auto cbrace_error_start = push_start(); 422 syntax_error = create<AST::SyntaxError>("Expected a close brace '}' to end a function body"_string.release_value_but_fixme_should_propagate_errors(), true); 423 } 424 if (!expect('}')) { 425 if (body) 426 body->set_is_syntax_error(*syntax_error); 427 else 428 body = move(syntax_error); 429 430 return create<AST::FunctionDeclaration>( 431 AST::NameWithPosition { 432 String::from_utf8(function_name).release_value_but_fixme_should_propagate_errors(), 433 { pos_before_name.offset, pos_after_name.offset, pos_before_name.line, pos_after_name.line } }, 434 move(arguments), 435 move(body)); 436 } 437 } 438 439 return create<AST::FunctionDeclaration>( 440 AST::NameWithPosition { 441 String::from_utf8(function_name).release_value_but_fixme_should_propagate_errors(), 442 { pos_before_name.offset, pos_after_name.offset, pos_before_name.line, pos_after_name.line } }, 443 move(arguments), 444 move(body)); 445} 446 447RefPtr<AST::Node> Parser::parse_or_logical_sequence() 448{ 449 consume_while(is_whitespace); 450 auto rule_start = push_start(); 451 auto and_sequence = parse_and_logical_sequence(); 452 if (!and_sequence) 453 return nullptr; 454 455 consume_while(is_whitespace); 456 auto pos_before_or = save_offset(); 457 if (!expect("||"sv)) 458 return and_sequence; 459 auto pos_after_or = save_offset(); 460 461 auto right_and_sequence = parse_and_logical_sequence(); 462 if (!right_and_sequence) 463 right_and_sequence = create<AST::SyntaxError>("Expected an expression after '||'"_string.release_value_but_fixme_should_propagate_errors(), true); 464 465 return create<AST::Or>( 466 and_sequence.release_nonnull(), 467 right_and_sequence.release_nonnull(), 468 AST::Position { pos_before_or.offset, pos_after_or.offset, pos_before_or.line, pos_after_or.line }); 469} 470 471RefPtr<AST::Node> Parser::parse_and_logical_sequence() 472{ 473 consume_while(is_whitespace); 474 auto rule_start = push_start(); 475 auto pipe_sequence = parse_pipe_sequence(); 476 if (!pipe_sequence) 477 return nullptr; 478 479 consume_while(is_whitespace); 480 auto pos_before_and = save_offset(); 481 if (!expect("&&"sv)) 482 return pipe_sequence; 483 auto pos_after_end = save_offset(); 484 485 auto right_and_sequence = parse_and_logical_sequence(); 486 if (!right_and_sequence) 487 right_and_sequence = create<AST::SyntaxError>("Expected an expression after '&&'"_string.release_value_but_fixme_should_propagate_errors(), true); 488 489 return create<AST::And>( 490 pipe_sequence.release_nonnull(), 491 right_and_sequence.release_nonnull(), 492 AST::Position { pos_before_and.offset, pos_after_end.offset, pos_before_and.line, pos_after_end.line }); 493} 494 495RefPtr<AST::Node> Parser::parse_pipe_sequence() 496{ 497 auto rule_start = push_start(); 498 auto left = parse_control_structure(); 499 if (!left) { 500 if (auto cmd = parse_command()) 501 left = cmd; 502 else 503 return nullptr; 504 } 505 506 consume_while(is_whitespace); 507 508 if (peek() != '|') 509 return left; 510 511 auto before_pipe = save_offset(); 512 consume(); 513 auto also_pipe_stderr = peek() == '&'; 514 if (also_pipe_stderr) { 515 consume(); 516 517 RefPtr<AST::Node> redirection; 518 { 519 auto redirection_start = push_start(); 520 redirection = create<AST::Fd2FdRedirection>(STDERR_FILENO, STDOUT_FILENO); 521 } 522 523 left = create<AST::Join>(left.release_nonnull(), redirection.release_nonnull()); 524 } 525 526 if (auto pipe_seq = parse_pipe_sequence()) { 527 return create<AST::Pipe>(left.release_nonnull(), pipe_seq.release_nonnull()); // Pipe 528 } 529 530 restore_to(before_pipe.offset, before_pipe.line); 531 return left; 532} 533 534RefPtr<AST::Node> Parser::parse_command() 535{ 536 auto rule_start = push_start(); 537 consume_while(is_whitespace); 538 539 auto redir = parse_redirection(); 540 if (!redir) { 541 auto list_expr = parse_list_expression(); 542 if (!list_expr) 543 return nullptr; 544 545 auto cast = create<AST::CastToCommand>(list_expr.release_nonnull()); // Cast List Command 546 547 auto next_command = parse_command(); 548 if (!next_command) 549 return cast; 550 551 return create<AST::Join>(move(cast), next_command.release_nonnull()); // Join List Command 552 } 553 554 auto command = parse_command(); 555 if (!command) 556 return redir; 557 558 return create<AST::Join>(redir.release_nonnull(), command.release_nonnull()); // Join Command Command 559} 560 561RefPtr<AST::Node> Parser::parse_control_structure() 562{ 563 auto rule_start = push_start(); 564 consume_while(is_whitespace); 565 if (auto control = parse_continuation_control()) 566 return control; 567 568 if (auto for_loop = parse_for_loop()) 569 return for_loop; 570 571 if (auto loop = parse_loop_loop()) 572 return loop; 573 574 if (auto if_expr = parse_if_expr()) 575 return if_expr; 576 577 if (auto subshell = parse_subshell()) 578 return subshell; 579 580 if (auto match = parse_match_expr()) 581 return match; 582 583 return nullptr; 584} 585 586RefPtr<AST::Node> Parser::parse_continuation_control() 587{ 588 if (!m_continuation_controls_allowed) 589 return nullptr; 590 591 auto rule_start = push_start(); 592 593 if (expect("break"sv)) { 594 { 595 auto break_end = push_start(); 596 if (consume_while(is_any_of(" \t\n;"sv)).is_empty()) { 597 restore_to(*rule_start); 598 return nullptr; 599 } 600 restore_to(*break_end); 601 } 602 return create<AST::ContinuationControl>(AST::ContinuationControl::Break); 603 } 604 605 if (expect("continue"sv)) { 606 { 607 auto continue_end = push_start(); 608 if (consume_while(is_any_of(" \t\n;"sv)).is_empty()) { 609 restore_to(*rule_start); 610 return nullptr; 611 } 612 restore_to(*continue_end); 613 } 614 return create<AST::ContinuationControl>(AST::ContinuationControl::Continue); 615 } 616 617 return nullptr; 618} 619 620RefPtr<AST::Node> Parser::parse_for_loop() 621{ 622 auto rule_start = push_start(); 623 if (!expect("for"sv)) 624 return nullptr; 625 626 if (consume_while(is_any_of(" \t\n"sv)).is_empty()) { 627 restore_to(*rule_start); 628 return nullptr; 629 } 630 631 Optional<AST::NameWithPosition> index_variable_name, variable_name; 632 Optional<AST::Position> in_start_position, index_start_position; 633 634 auto offset_before_index = current_position(); 635 if (expect("index"sv)) { 636 auto offset = current_position(); 637 if (!consume_while(is_whitespace).is_empty()) { 638 auto offset_before_variable = current_position(); 639 auto variable = consume_while(is_word_character); 640 if (!variable.is_empty()) { 641 index_start_position = AST::Position { offset_before_index.offset, offset.offset, offset_before_index.line, offset.line }; 642 643 auto offset_after_variable = current_position(); 644 index_variable_name = AST::NameWithPosition { 645 String::from_utf8(variable).release_value_but_fixme_should_propagate_errors(), 646 { offset_before_variable.offset, offset_after_variable.offset, offset_before_variable.line, offset_after_variable.line }, 647 }; 648 649 consume_while(is_whitespace); 650 } else { 651 restore_to(offset_before_index.offset, offset_before_index.line); 652 } 653 } else { 654 restore_to(offset_before_index.offset, offset_before_index.line); 655 } 656 } 657 658 auto variable_name_start_offset = current_position(); 659 auto name = consume_while(is_word_character); 660 auto variable_name_end_offset = current_position(); 661 if (!name.is_empty()) { 662 variable_name = AST::NameWithPosition { 663 String::from_utf8(name).release_value_but_fixme_should_propagate_errors(), 664 { variable_name_start_offset.offset, variable_name_end_offset.offset, variable_name_start_offset.line, variable_name_end_offset.line } 665 }; 666 consume_while(is_whitespace); 667 auto in_error_start = push_start(); 668 if (!expect("in"sv)) { 669 auto syntax_error = create<AST::SyntaxError>("Expected 'in' after a variable name in a 'for' loop"_string.release_value_but_fixme_should_propagate_errors(), true); 670 return create<AST::ForLoop>(move(variable_name), move(index_variable_name), move(syntax_error), nullptr); // ForLoop Var Iterated Block 671 } 672 in_start_position = AST::Position { in_error_start->offset, m_offset, in_error_start->line, line() }; 673 } 674 675 consume_while(is_whitespace); 676 RefPtr<AST::Node> iterated_expression; 677 { 678 auto iter_error_start = push_start(); 679 iterated_expression = parse_expression(); 680 if (!iterated_expression) 681 iterated_expression = create<AST::SyntaxError>("Expected an expression in 'for' loop"_string.release_value_but_fixme_should_propagate_errors(), true); 682 } 683 684 consume_while(is_any_of(" \t\n"sv)); 685 { 686 auto obrace_error_start = push_start(); 687 if (!expect('{')) { 688 auto syntax_error = create<AST::SyntaxError>("Expected an open brace '{' to start a 'for' loop body"_string.release_value_but_fixme_should_propagate_errors(), true); 689 return create<AST::ForLoop>(move(variable_name), move(index_variable_name), move(iterated_expression), move(syntax_error), move(in_start_position), move(index_start_position)); // ForLoop Var Iterated Block 690 } 691 } 692 693 TemporaryChange controls { m_continuation_controls_allowed, true }; 694 auto body = parse_toplevel(); 695 696 { 697 auto cbrace_error_start = push_start(); 698 if (!expect('}')) { 699 auto error_start = push_start(); 700 auto syntax_error = create<AST::SyntaxError>("Expected a close brace '}' to end a 'for' loop body"_string.release_value_but_fixme_should_propagate_errors(), true); 701 if (body) 702 body->set_is_syntax_error(*syntax_error); 703 else 704 body = syntax_error; 705 } 706 } 707 708 return create<AST::ForLoop>(move(variable_name), move(index_variable_name), move(iterated_expression), move(body), move(in_start_position), move(index_start_position)); // ForLoop Var Iterated Block 709} 710 711RefPtr<AST::Node> Parser::parse_loop_loop() 712{ 713 auto rule_start = push_start(); 714 if (!expect("loop"sv)) 715 return nullptr; 716 717 if (consume_while(is_any_of(" \t\n"sv)).is_empty()) { 718 restore_to(*rule_start); 719 return nullptr; 720 } 721 722 { 723 auto obrace_error_start = push_start(); 724 if (!expect('{')) { 725 auto syntax_error = create<AST::SyntaxError>("Expected an open brace '{' to start a 'loop' loop body"_string.release_value_but_fixme_should_propagate_errors(), true); 726 return create<AST::ForLoop>(AST::NameWithPosition {}, AST::NameWithPosition {}, nullptr, move(syntax_error)); // ForLoop null null Block 727 } 728 } 729 730 TemporaryChange controls { m_continuation_controls_allowed, true }; 731 auto body = parse_toplevel(); 732 733 { 734 auto cbrace_error_start = push_start(); 735 if (!expect('}')) { 736 auto error_start = push_start(); 737 auto syntax_error = create<AST::SyntaxError>("Expected a close brace '}' to end a 'loop' loop body"_string.release_value_but_fixme_should_propagate_errors(), true); 738 if (body) 739 body->set_is_syntax_error(*syntax_error); 740 else 741 body = syntax_error; 742 } 743 } 744 745 return create<AST::ForLoop>(AST::NameWithPosition {}, AST::NameWithPosition {}, nullptr, move(body)); // ForLoop null null Block 746} 747 748RefPtr<AST::Node> Parser::parse_if_expr() 749{ 750 auto rule_start = push_start(); 751 if (!expect("if"sv)) 752 return nullptr; 753 754 if (consume_while(is_any_of(" \t\n"sv)).is_empty()) { 755 restore_to(*rule_start); 756 return nullptr; 757 } 758 759 RefPtr<AST::Node> condition; 760 { 761 auto cond_error_start = push_start(); 762 condition = parse_or_logical_sequence(); 763 if (!condition) 764 condition = create<AST::SyntaxError>("Expected a logical sequence after 'if'"_string.release_value_but_fixme_should_propagate_errors(), true); 765 } 766 767 auto parse_braced_toplevel = [&]() -> RefPtr<AST::Node> { 768 RefPtr<AST::Node> body; 769 { 770 auto obrace_error_start = push_start(); 771 if (!expect('{')) { 772 body = create<AST::SyntaxError>("Expected an open brace '{' to start an 'if' true branch"_string.release_value_but_fixme_should_propagate_errors(), true); 773 } 774 } 775 776 if (!body) 777 body = parse_toplevel(); 778 779 { 780 auto cbrace_error_start = push_start(); 781 if (!expect('}')) { 782 auto error_start = push_start(); 783 RefPtr<AST::SyntaxError> syntax_error = create<AST::SyntaxError>("Expected a close brace '}' to end an 'if' true branch"_string.release_value_but_fixme_should_propagate_errors(), true); 784 if (body) 785 body->set_is_syntax_error(*syntax_error); 786 else 787 body = syntax_error; 788 } 789 } 790 791 return body; 792 }; 793 794 consume_while(is_any_of(" \t\n"sv)); 795 auto true_branch = parse_braced_toplevel(); 796 797 auto end_before_else = m_offset; 798 auto line_before_else = line(); 799 consume_while(is_any_of(" \t\n"sv)); 800 Optional<AST::Position> else_position; 801 { 802 auto else_start = push_start(); 803 if (expect("else"sv)) 804 else_position = AST::Position { else_start->offset, m_offset, else_start->line, line() }; 805 else 806 restore_to(end_before_else, line_before_else); 807 } 808 809 if (else_position.has_value()) { 810 consume_while(is_any_of(" \t\n"sv)); 811 if (peek() == '{') { 812 auto false_branch = parse_braced_toplevel(); 813 return create<AST::IfCond>(else_position, condition.release_nonnull(), move(true_branch), move(false_branch)); // If expr true_branch Else false_branch 814 } 815 816 auto else_if_branch = parse_if_expr(); 817 return create<AST::IfCond>(else_position, condition.release_nonnull(), move(true_branch), move(else_if_branch)); // If expr true_branch Else If ... 818 } 819 820 return create<AST::IfCond>(else_position, condition.release_nonnull(), move(true_branch), nullptr); // If expr true_branch 821} 822 823RefPtr<AST::Node> Parser::parse_subshell() 824{ 825 auto rule_start = push_start(); 826 if (!expect('{')) 827 return nullptr; 828 829 auto body = parse_toplevel(); 830 831 { 832 auto cbrace_error_start = push_start(); 833 if (!expect('}')) { 834 auto error_start = push_start(); 835 RefPtr<AST::SyntaxError> syntax_error = create<AST::SyntaxError>("Expected a close brace '}' to end a subshell"_string.release_value_but_fixme_should_propagate_errors(), true); 836 if (body) 837 body->set_is_syntax_error(*syntax_error); 838 else 839 body = syntax_error; 840 } 841 } 842 843 return create<AST::Subshell>(move(body)); 844} 845 846RefPtr<AST::Node> Parser::parse_match_expr() 847{ 848 auto rule_start = push_start(); 849 if (!expect("match"sv)) 850 return nullptr; 851 852 if (consume_while(is_whitespace).is_empty()) { 853 restore_to(*rule_start); 854 return nullptr; 855 } 856 857 auto match_expression = parse_expression(); 858 if (!match_expression) { 859 return create<AST::MatchExpr>( 860 create<AST::SyntaxError>("Expected an expression after 'match'"_string.release_value_but_fixme_should_propagate_errors(), true), 861 String {}, Optional<AST::Position> {}, Vector<AST::MatchEntry> {}); 862 } 863 864 consume_while(is_any_of(" \t\n"sv)); 865 866 String match_name; 867 Optional<AST::Position> as_position; 868 auto as_start = m_offset; 869 auto as_line = line(); 870 if (expect("as"sv)) { 871 as_position = AST::Position { as_start, m_offset, as_line, line() }; 872 873 if (consume_while(is_any_of(" \t\n"sv)).is_empty()) { 874 auto node = create<AST::MatchExpr>( 875 match_expression.release_nonnull(), 876 String {}, move(as_position), Vector<AST::MatchEntry> {}); 877 node->set_is_syntax_error(create<AST::SyntaxError>("Expected whitespace after 'as' in 'match'"_string.release_value_but_fixme_should_propagate_errors(), true)); 878 return node; 879 } 880 881 match_name = String::from_utf8(consume_while(is_word_character)).release_value_but_fixme_should_propagate_errors(); 882 if (match_name.is_empty()) { 883 auto node = create<AST::MatchExpr>( 884 match_expression.release_nonnull(), 885 String {}, move(as_position), Vector<AST::MatchEntry> {}); 886 node->set_is_syntax_error(create<AST::SyntaxError>("Expected an identifier after 'as' in 'match'"_string.release_value_but_fixme_should_propagate_errors(), true)); 887 return node; 888 } 889 } 890 891 consume_while(is_any_of(" \t\n"sv)); 892 893 if (!expect('{')) { 894 auto node = create<AST::MatchExpr>( 895 match_expression.release_nonnull(), 896 move(match_name), move(as_position), Vector<AST::MatchEntry> {}); 897 node->set_is_syntax_error(create<AST::SyntaxError>("Expected an open brace '{' to start a 'match' entry list"_string.release_value_but_fixme_should_propagate_errors(), true)); 898 return node; 899 } 900 901 consume_while(is_any_of(" \t\n"sv)); 902 903 Vector<AST::MatchEntry> entries; 904 for (;;) { 905 auto entry = parse_match_entry(); 906 consume_while(is_any_of(" \t\n"sv)); 907 if (entry.options.visit([](auto& x) { return x.is_empty(); })) 908 break; 909 910 entries.append(move(entry)); 911 } 912 913 consume_while(is_any_of(" \t\n"sv)); 914 915 if (!expect('}')) { 916 auto node = create<AST::MatchExpr>( 917 match_expression.release_nonnull(), 918 move(match_name), move(as_position), move(entries)); 919 node->set_is_syntax_error(create<AST::SyntaxError>("Expected a close brace '}' to end a 'match' entry list"_string.release_value_but_fixme_should_propagate_errors(), true)); 920 return node; 921 } 922 923 return create<AST::MatchExpr>(match_expression.release_nonnull(), move(match_name), move(as_position), move(entries)); 924} 925 926AST::MatchEntry Parser::parse_match_entry() 927{ 928 auto rule_start = push_start(); 929 930 Vector<NonnullRefPtr<AST::Node>> patterns; 931 Vector<Regex<ECMA262>> regexps; 932 Vector<AST::Position> pipe_positions; 933 Optional<Vector<String>> match_names; 934 Optional<AST::Position> match_as_position; 935 enum { 936 Regex, 937 Glob, 938 } pattern_kind; 939 940 consume_while(is_any_of(" \t\n"sv)); 941 942 auto regex_pattern = parse_regex_pattern(); 943 if (regex_pattern.has_value()) { 944 if (auto error = regex_pattern.value().parser_result.error; error != regex::Error::NoError) 945 return { Vector<NonnullRefPtr<AST::Node>> {}, {}, {}, {}, create<AST::SyntaxError>(String::from_utf8(regex::get_error_string(error)).release_value_but_fixme_should_propagate_errors(), false) }; 946 947 pattern_kind = Regex; 948 regexps.append(regex_pattern.release_value()); 949 } else { 950 auto glob_pattern = parse_match_pattern(); 951 if (!glob_pattern) 952 return { Vector<NonnullRefPtr<AST::Node>> {}, {}, {}, {}, create<AST::SyntaxError>("Expected a pattern in 'match' body"_string.release_value_but_fixme_should_propagate_errors(), true) }; 953 954 pattern_kind = Glob; 955 patterns.append(glob_pattern.release_nonnull()); 956 } 957 958 consume_while(is_any_of(" \t\n"sv)); 959 960 auto previous_pipe_start_position = m_offset; 961 auto previous_pipe_start_line = line(); 962 RefPtr<AST::SyntaxError> error; 963 while (expect('|')) { 964 pipe_positions.append({ previous_pipe_start_position, m_offset, previous_pipe_start_line, line() }); 965 consume_while(is_any_of(" \t\n"sv)); 966 switch (pattern_kind) { 967 case Regex: { 968 auto pattern = parse_regex_pattern(); 969 if (!pattern.has_value()) { 970 error = create<AST::SyntaxError>("Expected a regex pattern to follow '|' in 'match' body"_string.release_value_but_fixme_should_propagate_errors(), true); 971 break; 972 } 973 regexps.append(pattern.release_value()); 974 break; 975 } 976 case Glob: { 977 auto pattern = parse_match_pattern(); 978 if (!pattern) { 979 error = create<AST::SyntaxError>("Expected a pattern to follow '|' in 'match' body"_string.release_value_but_fixme_should_propagate_errors(), true); 980 break; 981 } 982 patterns.append(pattern.release_nonnull()); 983 break; 984 } 985 } 986 987 consume_while(is_any_of(" \t\n"sv)); 988 989 previous_pipe_start_line = line(); 990 previous_pipe_start_position = m_offset; 991 } 992 993 consume_while(is_any_of(" \t\n"sv)); 994 995 auto as_start_position = m_offset; 996 auto as_start_line = line(); 997 if (pattern_kind == Glob && expect("as"sv)) { 998 match_as_position = AST::Position { as_start_position, m_offset, as_start_line, line() }; 999 consume_while(is_any_of(" \t\n"sv)); 1000 if (!expect('(')) { 1001 if (!error) 1002 error = create<AST::SyntaxError>("Expected an explicit list of identifiers after a pattern 'as'"_string.release_value_but_fixme_should_propagate_errors()); 1003 } else { 1004 match_names = Vector<String>(); 1005 for (;;) { 1006 consume_while(is_whitespace); 1007 auto name = consume_while(is_word_character); 1008 if (name.is_empty()) 1009 break; 1010 match_names->append(String::from_utf8(name).release_value_but_fixme_should_propagate_errors()); 1011 } 1012 1013 if (!expect(')')) { 1014 if (!error) 1015 error = create<AST::SyntaxError>("Expected a close paren ')' to end the identifier list of pattern 'as'"_string.release_value_but_fixme_should_propagate_errors(), true); 1016 } 1017 } 1018 consume_while(is_any_of(" \t\n"sv)); 1019 } 1020 1021 if (pattern_kind == Regex) { 1022 Vector<String> names; 1023 for (auto& regex : regexps) { 1024 if (names.is_empty()) { 1025 for (auto& name : regex.parser_result.capture_groups) 1026 names.append(String::from_utf8(name).release_value_but_fixme_should_propagate_errors()); 1027 } else { 1028 size_t index = 0; 1029 for (auto& name : regex.parser_result.capture_groups) { 1030 if (names.size() <= index) { 1031 names.append(String::from_utf8(name).release_value_but_fixme_should_propagate_errors()); 1032 continue; 1033 } 1034 1035 if (names[index] != name.view()) { 1036 if (!error) 1037 error = create<AST::SyntaxError>("Alternative regex patterns must have the same capture groups"_string.release_value_but_fixme_should_propagate_errors(), false); 1038 break; 1039 } 1040 } 1041 } 1042 } 1043 match_names = move(names); 1044 } 1045 1046 if (!expect('{')) { 1047 if (!error) 1048 error = create<AST::SyntaxError>("Expected an open brace '{' to start a match entry body"_string.release_value_but_fixme_should_propagate_errors(), true); 1049 } 1050 1051 auto body = parse_toplevel(); 1052 1053 if (!expect('}')) { 1054 if (!error) 1055 error = create<AST::SyntaxError>("Expected a close brace '}' to end a match entry body"_string.release_value_but_fixme_should_propagate_errors(), true); 1056 } 1057 1058 if (body && error) 1059 body->set_is_syntax_error(*error); 1060 else if (error) 1061 body = error; 1062 1063 if (pattern_kind == Glob) 1064 return { move(patterns), move(match_names), move(match_as_position), move(pipe_positions), move(body) }; 1065 1066 return { move(regexps), move(match_names), move(match_as_position), move(pipe_positions), move(body) }; 1067} 1068 1069RefPtr<AST::Node> Parser::parse_match_pattern() 1070{ 1071 return parse_expression(); 1072} 1073 1074Optional<Regex<ECMA262>> Parser::parse_regex_pattern() 1075{ 1076 auto rule_start = push_start(); 1077 1078 auto start = m_offset; 1079 if (!expect("(?:"sv) && !expect("(?<"sv)) 1080 return {}; 1081 1082 size_t open_parens = 1; 1083 while (open_parens > 0) { 1084 if (at_end()) 1085 break; 1086 1087 if (next_is("("sv)) 1088 ++open_parens; 1089 else if (next_is(")"sv)) 1090 --open_parens; 1091 consume(); 1092 } 1093 1094 if (open_parens != 0) { 1095 restore_to(*rule_start); 1096 return {}; 1097 } 1098 1099 auto end = m_offset; 1100 auto pattern = m_input.substring_view(start, end - start); 1101 return Regex<ECMA262>(pattern); 1102} 1103 1104RefPtr<AST::Node> Parser::parse_redirection() 1105{ 1106 auto rule_start = push_start(); 1107 1108 // heredoc entry 1109 if (next_is("<<-"sv) || next_is("<<~"sv)) 1110 return nullptr; 1111 1112 auto pipe_fd = 0; 1113 auto number = consume_while(is_digit); 1114 if (number.is_empty()) { 1115 pipe_fd = -1; 1116 } else { 1117 auto fd = number.to_int(); 1118 pipe_fd = fd.value_or(-1); 1119 } 1120 1121 switch (peek()) { 1122 case '>': { 1123 consume(); 1124 if (peek() == '>') { 1125 consume(); 1126 consume_while(is_whitespace); 1127 pipe_fd = pipe_fd >= 0 ? pipe_fd : STDOUT_FILENO; 1128 auto path = parse_expression(); 1129 if (!path) { 1130 if (!at_end()) { 1131 // Eat a character and hope the problem goes away 1132 consume(); 1133 } 1134 path = create<AST::SyntaxError>("Expected a path after redirection"_string.release_value_but_fixme_should_propagate_errors(), true); 1135 } 1136 return create<AST::WriteAppendRedirection>(pipe_fd, path.release_nonnull()); // Redirection WriteAppend 1137 } 1138 if (peek() == '&') { 1139 consume(); 1140 // FIXME: 'fd>&-' Syntax not the best. needs discussion. 1141 if (peek() == '-') { 1142 consume(); 1143 pipe_fd = pipe_fd >= 0 ? pipe_fd : STDOUT_FILENO; 1144 return create<AST::CloseFdRedirection>(pipe_fd); // Redirection CloseFd 1145 } 1146 int dest_pipe_fd = 0; 1147 auto number = consume_while(is_digit); 1148 pipe_fd = pipe_fd >= 0 ? pipe_fd : STDOUT_FILENO; 1149 if (number.is_empty()) { 1150 dest_pipe_fd = -1; 1151 } else { 1152 auto fd = number.to_int(); 1153 dest_pipe_fd = fd.value_or(-1); 1154 } 1155 auto redir = create<AST::Fd2FdRedirection>(pipe_fd, dest_pipe_fd); // Redirection Fd2Fd 1156 if (dest_pipe_fd == -1) 1157 redir->set_is_syntax_error(*create<AST::SyntaxError>("Expected a file descriptor"_string.release_value_but_fixme_should_propagate_errors())); 1158 return redir; 1159 } 1160 consume_while(is_whitespace); 1161 pipe_fd = pipe_fd >= 0 ? pipe_fd : STDOUT_FILENO; 1162 auto path = parse_expression(); 1163 if (!path) { 1164 if (!at_end()) { 1165 // Eat a character and hope the problem goes away 1166 consume(); 1167 } 1168 path = create<AST::SyntaxError>("Expected a path after redirection"_string.release_value_but_fixme_should_propagate_errors(), true); 1169 } 1170 return create<AST::WriteRedirection>(pipe_fd, path.release_nonnull()); // Redirection Write 1171 } 1172 case '<': { 1173 consume(); 1174 enum { 1175 Read, 1176 ReadWrite, 1177 } mode { Read }; 1178 1179 if (peek() == '>') { 1180 mode = ReadWrite; 1181 consume(); 1182 } 1183 1184 consume_while(is_whitespace); 1185 pipe_fd = pipe_fd >= 0 ? pipe_fd : STDIN_FILENO; 1186 auto path = parse_expression(); 1187 if (!path) { 1188 if (!at_end()) { 1189 // Eat a character and hope the problem goes away 1190 consume(); 1191 } 1192 path = create<AST::SyntaxError>("Expected a path after redirection"_string.release_value_but_fixme_should_propagate_errors(), true); 1193 } 1194 if (mode == Read) 1195 return create<AST::ReadRedirection>(pipe_fd, path.release_nonnull()); // Redirection Read 1196 1197 return create<AST::ReadWriteRedirection>(pipe_fd, path.release_nonnull()); // Redirection ReadWrite 1198 } 1199 default: 1200 restore_to(*rule_start); 1201 return nullptr; 1202 } 1203} 1204 1205RefPtr<AST::Node> Parser::parse_list_expression() 1206{ 1207 consume_while(is_whitespace); 1208 1209 auto rule_start = push_start(); 1210 Vector<NonnullRefPtr<AST::Node>> nodes; 1211 1212 do { 1213 auto expr = parse_expression(); 1214 if (!expr) 1215 break; 1216 nodes.append(expr.release_nonnull()); 1217 } while (!consume_while(is_whitespace).is_empty()); 1218 1219 if (nodes.is_empty()) 1220 return nullptr; 1221 1222 return create<AST::ListConcatenate>(move(nodes)); // Concatenate List 1223} 1224 1225RefPtr<AST::Node> Parser::parse_expression() 1226{ 1227 auto rule_start = push_start(); 1228 if (m_rule_start_offsets.size() > max_allowed_nested_rule_depth) 1229 return create<AST::SyntaxError>(String::formatted("Expression nested too deep (max allowed is {})", max_allowed_nested_rule_depth).release_value_but_fixme_should_propagate_errors()); 1230 1231 auto starting_char = peek(); 1232 1233 auto read_concat = [&](auto&& expr) -> NonnullRefPtr<AST::Node> { 1234 if (is_whitespace(peek())) 1235 return move(expr); 1236 1237 if (auto next_expr = parse_expression()) 1238 return create<AST::Juxtaposition>(move(expr), next_expr.release_nonnull()); 1239 1240 return move(expr); 1241 }; 1242 1243 // Heredocs are expressions, so allow them 1244 if (!(next_is("<<-"sv) || next_is("<<~"sv))) { 1245 if (strchr("&|)} ;<>\n", starting_char) != nullptr) 1246 return nullptr; 1247 } 1248 1249 if (m_extra_chars_not_allowed_in_barewords.contains_slow(starting_char)) 1250 return nullptr; 1251 1252 if (m_is_in_brace_expansion_spec && next_is(".."sv)) 1253 return nullptr; 1254 1255 if (isdigit(starting_char)) { 1256 ScopedValueRollback offset_rollback { m_offset }; 1257 1258 auto redir = parse_redirection(); 1259 if (redir) 1260 return nullptr; 1261 } 1262 1263 if (starting_char == '$') { 1264 if (auto variable = parse_variable()) 1265 return read_concat(variable.release_nonnull()); 1266 1267 if (auto immediate = parse_immediate_expression()) 1268 return read_concat(immediate.release_nonnull()); 1269 1270 auto inline_exec = parse_evaluate(); 1271 if (inline_exec && !inline_exec->is_syntax_error()) 1272 return read_concat(inline_exec.release_nonnull()); 1273 return inline_exec; 1274 } 1275 1276 if (starting_char == '#') 1277 return parse_comment(); 1278 1279 if (starting_char == '(') { 1280 consume(); 1281 auto list = parse_list_expression(); 1282 if (!expect(')')) { 1283 restore_to(*rule_start); 1284 return nullptr; 1285 } 1286 return read_concat(create<AST::CastToList>(move(list))); // Cast To List 1287 } 1288 1289 if (starting_char == '!' && m_in_interactive_mode) { 1290 if (auto designator = parse_history_designator()) 1291 return designator; 1292 } 1293 1294 if (auto composite = parse_string_composite()) 1295 return read_concat(composite.release_nonnull()); 1296 1297 return nullptr; 1298} 1299 1300RefPtr<AST::Node> Parser::parse_string_composite() 1301{ 1302 auto rule_start = push_start(); 1303 if (auto string = parse_string()) { 1304 if (auto next_part = parse_string_composite()) 1305 return create<AST::Juxtaposition>(string.release_nonnull(), next_part.release_nonnull()); // Concatenate String StringComposite 1306 1307 return string; 1308 } 1309 1310 if (auto variable = parse_variable()) { 1311 if (auto next_part = parse_string_composite()) 1312 return create<AST::Juxtaposition>(variable.release_nonnull(), next_part.release_nonnull()); // Concatenate Variable StringComposite 1313 1314 return variable; 1315 } 1316 1317 if (auto glob = parse_glob()) { 1318 if (auto next_part = parse_string_composite()) 1319 return create<AST::Juxtaposition>(glob.release_nonnull(), next_part.release_nonnull()); // Concatenate Glob StringComposite 1320 1321 return glob; 1322 } 1323 1324 if (auto expansion = parse_brace_expansion()) { 1325 if (auto next_part = parse_string_composite()) 1326 return create<AST::Juxtaposition>(expansion.release_nonnull(), next_part.release_nonnull()); // Concatenate BraceExpansion StringComposite 1327 1328 return expansion; 1329 } 1330 1331 if (auto bareword = parse_bareword()) { 1332 if (auto next_part = parse_string_composite()) 1333 return create<AST::Juxtaposition>(bareword.release_nonnull(), next_part.release_nonnull()); // Concatenate Bareword StringComposite 1334 1335 return bareword; 1336 } 1337 1338 if (auto inline_command = parse_evaluate()) { 1339 if (auto next_part = parse_string_composite()) 1340 return create<AST::Juxtaposition>(inline_command.release_nonnull(), next_part.release_nonnull()); // Concatenate Execute StringComposite 1341 1342 return inline_command; 1343 } 1344 1345 if (auto heredoc = parse_heredoc_initiation_record()) { 1346 if (auto next_part = parse_string_composite()) 1347 return create<AST::Juxtaposition>(heredoc.release_nonnull(), next_part.release_nonnull()); // Concatenate Heredoc StringComposite 1348 1349 return heredoc; 1350 } 1351 1352 return nullptr; 1353} 1354 1355RefPtr<AST::Node> Parser::parse_string() 1356{ 1357 auto rule_start = push_start(); 1358 if (at_end()) 1359 return nullptr; 1360 1361 if (peek() == '"') { 1362 consume(); 1363 auto inner = parse_string_inner(StringEndCondition::DoubleQuote); 1364 if (!inner) 1365 inner = create<AST::SyntaxError>("Unexpected EOF in string"_string.release_value_but_fixme_should_propagate_errors(), true); 1366 if (!expect('"')) { 1367 inner = create<AST::DoubleQuotedString>(move(inner)); 1368 inner->set_is_syntax_error(*create<AST::SyntaxError>("Expected a terminating double quote"_string.release_value_but_fixme_should_propagate_errors(), true)); 1369 return inner; 1370 } 1371 return create<AST::DoubleQuotedString>(move(inner)); // Double Quoted String 1372 } 1373 1374 if (peek() == '\'') { 1375 consume(); 1376 auto text = consume_while(is_not('\'')); 1377 bool is_error = false; 1378 if (!expect('\'')) 1379 is_error = true; 1380 auto result = create<AST::StringLiteral>(String::from_utf8(text).release_value_but_fixme_should_propagate_errors(), AST::StringLiteral::EnclosureType::SingleQuotes); // String Literal 1381 if (is_error) 1382 result->set_is_syntax_error(*create<AST::SyntaxError>("Expected a terminating single quote"_string.release_value_but_fixme_should_propagate_errors(), true)); 1383 return result; 1384 } 1385 1386 return nullptr; 1387} 1388 1389RefPtr<AST::Node> Parser::parse_string_inner(StringEndCondition condition) 1390{ 1391 auto rule_start = push_start(); 1392 if (at_end()) 1393 return nullptr; 1394 1395 StringBuilder builder; 1396 while (!at_end()) { 1397 if (condition == StringEndCondition::DoubleQuote && peek() == '"') { 1398 break; 1399 } 1400 1401 if (peek() == '\\') { 1402 consume(); 1403 if (at_end()) { 1404 break; 1405 } 1406 auto ch = consume(); 1407 switch (ch) { 1408 case '\\': 1409 default: 1410 builder.append(ch); 1411 break; 1412 case 'x': { 1413 if (m_input.length() <= m_offset + 2) 1414 break; 1415 auto first_nibble = tolower(consume()); 1416 auto second_nibble = tolower(consume()); 1417 if (!isxdigit(first_nibble) || !isxdigit(second_nibble)) { 1418 builder.append(first_nibble); 1419 builder.append(second_nibble); 1420 break; 1421 } 1422 builder.append(to_byte(first_nibble, second_nibble)); 1423 break; 1424 } 1425 case 'u': { 1426 if (m_input.length() <= m_offset + 8) 1427 break; 1428 size_t counter = 8; 1429 auto chars = consume_while([&](auto) { return counter-- > 0; }); 1430 if (auto number = AK::StringUtils::convert_to_uint_from_hex(chars); number.has_value()) 1431 builder.append(Utf32View { &number.value(), 1 }); 1432 else 1433 builder.append(chars); 1434 1435 break; 1436 } 1437 case 'a': 1438 builder.append('\a'); 1439 break; 1440 case 'b': 1441 builder.append('\b'); 1442 break; 1443 case 'e': 1444 builder.append('\x1b'); 1445 break; 1446 case 'f': 1447 builder.append('\f'); 1448 break; 1449 case 'r': 1450 builder.append('\r'); 1451 break; 1452 case 'n': 1453 builder.append('\n'); 1454 break; 1455 case 't': 1456 builder.append('\t'); 1457 break; 1458 } 1459 continue; 1460 } 1461 if (peek() == '$') { 1462 auto string_literal = create<AST::StringLiteral>(builder.to_string().release_value_but_fixme_should_propagate_errors(), AST::StringLiteral::EnclosureType::DoubleQuotes); // String Literal 1463 auto read_concat = [&](auto&& node) { 1464 auto inner = create<AST::StringPartCompose>( 1465 move(string_literal), 1466 move(node)); // Compose String Node 1467 1468 if (auto string = parse_string_inner(condition)) { 1469 return create<AST::StringPartCompose>(move(inner), string.release_nonnull()); // Compose Composition Composition 1470 } 1471 1472 return inner; 1473 }; 1474 1475 if (auto variable = parse_variable()) 1476 return read_concat(variable.release_nonnull()); 1477 1478 if (auto immediate = parse_immediate_expression()) 1479 return read_concat(immediate.release_nonnull()); 1480 1481 if (auto evaluate = parse_evaluate()) 1482 return read_concat(evaluate.release_nonnull()); 1483 } 1484 1485 builder.append(consume()); 1486 } 1487 1488 return create<AST::StringLiteral>(builder.to_string().release_value_but_fixme_should_propagate_errors(), AST::StringLiteral::EnclosureType::DoubleQuotes); // String Literal 1489} 1490 1491RefPtr<AST::Node> Parser::parse_variable() 1492{ 1493 auto rule_start = push_start(); 1494 auto ref = parse_variable_ref(); 1495 1496 if (!ref) 1497 return nullptr; 1498 1499 auto variable = static_ptr_cast<AST::VariableNode>(ref); 1500 if (auto slice = parse_slice()) 1501 variable->set_slice(slice.release_nonnull()); 1502 1503 return variable; 1504} 1505 1506RefPtr<AST::Node> Parser::parse_variable_ref() 1507{ 1508 auto rule_start = push_start(); 1509 if (at_end()) 1510 return nullptr; 1511 1512 if (peek() != '$') 1513 return nullptr; 1514 1515 consume(); 1516 switch (peek()) { 1517 case '$': 1518 case '?': 1519 case '*': 1520 case '#': 1521 return create<AST::SpecialVariable>(consume()); // Variable Special 1522 default: 1523 break; 1524 } 1525 1526 auto name = consume_while(is_word_character); 1527 1528 if (name.length() == 0) { 1529 restore_to(rule_start->offset, rule_start->line); 1530 return nullptr; 1531 } 1532 1533 return create<AST::SimpleVariable>(String::from_utf8(name).release_value_but_fixme_should_propagate_errors()); // Variable Simple 1534} 1535 1536RefPtr<AST::Slice> Parser::parse_slice() 1537{ 1538 auto rule_start = push_start(); 1539 if (!next_is("["sv)) 1540 return nullptr; 1541 1542 consume(); // [ 1543 1544 ScopedValueRollback chars_change { m_extra_chars_not_allowed_in_barewords }; 1545 m_extra_chars_not_allowed_in_barewords.append(']'); 1546 auto spec = parse_brace_expansion_spec(); 1547 1548 RefPtr<AST::SyntaxError> error; 1549 1550 if (peek() != ']') 1551 error = create<AST::SyntaxError>("Expected a close bracket ']' to end a variable slice"_string.release_value_but_fixme_should_propagate_errors()); 1552 else 1553 consume(); 1554 1555 if (!spec) { 1556 if (error) 1557 spec = move(error); 1558 else 1559 spec = create<AST::SyntaxError>("Expected either a range, or a comma-seprated list of selectors"_string.release_value_but_fixme_should_propagate_errors()); 1560 } 1561 1562 auto node = create<AST::Slice>(spec.release_nonnull()); 1563 if (error) 1564 node->set_is_syntax_error(*error); 1565 return node; 1566} 1567 1568RefPtr<AST::Node> Parser::parse_evaluate() 1569{ 1570 auto rule_start = push_start(); 1571 if (at_end()) 1572 return nullptr; 1573 1574 if (peek() != '$') 1575 return nullptr; 1576 1577 consume(); 1578 if (peek() == '(') { 1579 consume(); 1580 auto inner = parse_pipe_sequence(); 1581 if (!inner) 1582 inner = create<AST::SyntaxError>("Unexpected EOF in list"_string.release_value_but_fixme_should_propagate_errors(), true); 1583 if (!expect(')')) 1584 inner->set_is_syntax_error(*create<AST::SyntaxError>("Expected a terminating close paren"_string.release_value_but_fixme_should_propagate_errors(), true)); 1585 1586 return create<AST::Execute>(inner.release_nonnull(), true); 1587 } 1588 auto inner = parse_expression(); 1589 1590 if (!inner) { 1591 inner = create<AST::SyntaxError>("Expected a command"_string.release_value_but_fixme_should_propagate_errors(), true); 1592 } else { 1593 if (inner->is_list()) { 1594 auto execute_inner = create<AST::Execute>(inner.release_nonnull(), true); 1595 inner = move(execute_inner); 1596 } else { 1597 auto dyn_inner = create<AST::DynamicEvaluate>(inner.release_nonnull()); 1598 inner = move(dyn_inner); 1599 } 1600 } 1601 1602 return inner; 1603} 1604 1605RefPtr<AST::Node> Parser::parse_immediate_expression() 1606{ 1607 auto rule_start = push_start(); 1608 if (at_end()) 1609 return nullptr; 1610 1611 if (peek() != '$') 1612 return nullptr; 1613 1614 consume(); 1615 1616 if (peek() != '{') { 1617 restore_to(*rule_start); 1618 return nullptr; 1619 } 1620 1621 consume(); 1622 consume_while(is_whitespace); 1623 1624 auto function_name_start_offset = current_position(); 1625 auto function_name = consume_while(is_word_character); 1626 auto function_name_end_offset = current_position(); 1627 AST::Position function_position { 1628 function_name_start_offset.offset, 1629 function_name_end_offset.offset, 1630 function_name_start_offset.line, 1631 function_name_end_offset.line, 1632 }; 1633 1634 consume_while(is_whitespace); 1635 1636 Vector<NonnullRefPtr<AST::Node>> arguments; 1637 do { 1638 auto expr = parse_expression(); 1639 if (!expr) 1640 break; 1641 arguments.append(expr.release_nonnull()); 1642 } while (!consume_while(is_whitespace).is_empty()); 1643 1644 auto ending_brace_start_offset = current_position(); 1645 if (peek() == '}') 1646 consume(); 1647 1648 auto ending_brace_end_offset = current_position(); 1649 1650 auto ending_brace_position = ending_brace_start_offset.offset == ending_brace_end_offset.offset 1651 ? Optional<AST::Position> {} 1652 : Optional<AST::Position> { 1653 AST::Position { 1654 ending_brace_start_offset.offset, 1655 ending_brace_end_offset.offset, 1656 ending_brace_start_offset.line, 1657 ending_brace_end_offset.line, 1658 } 1659 }; 1660 1661 auto node = create<AST::ImmediateExpression>( 1662 AST::NameWithPosition { String::from_utf8(function_name).release_value_but_fixme_should_propagate_errors(), move(function_position) }, 1663 move(arguments), 1664 ending_brace_position); 1665 1666 if (!ending_brace_position.has_value()) 1667 node->set_is_syntax_error(create<AST::SyntaxError>("Expected a closing brace '}' to end an immediate expression"_string.release_value_but_fixme_should_propagate_errors(), true)); 1668 else if (node->function_name().is_empty()) 1669 node->set_is_syntax_error(create<AST::SyntaxError>("Expected an immediate function name"_string.release_value_but_fixme_should_propagate_errors())); 1670 1671 return node; 1672} 1673 1674RefPtr<AST::Node> Parser::parse_history_designator() 1675{ 1676 auto rule_start = push_start(); 1677 1678 VERIFY(peek() == '!'); 1679 consume(); 1680 1681 // Event selector 1682 AST::HistorySelector selector; 1683 RefPtr<AST::SyntaxError> syntax_error; 1684 selector.event.kind = AST::HistorySelector::EventKind::StartingStringLookup; 1685 selector.event.text_position = { m_offset, m_offset, m_line, m_line }; 1686 selector.word_selector_range = { 1687 AST::HistorySelector::WordSelector { 1688 AST::HistorySelector::WordSelectorKind::Index, 1689 0, 1690 { m_offset, m_offset, m_line, m_line }, 1691 nullptr }, 1692 AST::HistorySelector::WordSelector { 1693 AST::HistorySelector::WordSelectorKind::Last, 1694 0, 1695 { m_offset, m_offset, m_line, m_line }, 1696 nullptr } 1697 }; 1698 1699 bool is_word_selector = false; 1700 1701 switch (peek()) { 1702 case ':': 1703 consume(); 1704 [[fallthrough]]; 1705 case '^': 1706 case '$': 1707 case '*': 1708 is_word_selector = true; 1709 break; 1710 case '!': 1711 consume(); 1712 selector.event.kind = AST::HistorySelector::EventKind::IndexFromEnd; 1713 selector.event.index = 0; 1714 selector.event.text = "!"_short_string; 1715 break; 1716 case '?': 1717 consume(); 1718 selector.event.kind = AST::HistorySelector::EventKind::ContainingStringLookup; 1719 [[fallthrough]]; 1720 default: { 1721 TemporaryChange chars_change { m_extra_chars_not_allowed_in_barewords, { ':', '^', '$', '*' } }; 1722 1723 auto bareword = parse_bareword(); 1724 if (!bareword || !bareword->is_bareword()) { 1725 restore_to(*rule_start); 1726 return nullptr; 1727 } 1728 1729 selector.event.text = static_ptr_cast<AST::BarewordLiteral>(bareword)->text(); 1730 selector.event.text_position = bareword->position(); 1731 auto selector_bytes = selector.event.text.bytes(); 1732 auto it = selector_bytes.begin(); 1733 bool is_negative = false; 1734 if (*it == '-') { 1735 ++it; 1736 is_negative = true; 1737 } 1738 if (it != selector_bytes.end() && all_of(it, selector_bytes.end(), is_digit)) { 1739 if (is_negative) 1740 selector.event.kind = AST::HistorySelector::EventKind::IndexFromEnd; 1741 else 1742 selector.event.kind = AST::HistorySelector::EventKind::IndexFromStart; 1743 auto number = abs(selector.event.text.bytes_as_string_view().to_int().value_or(0)); 1744 if (number != 0) 1745 selector.event.index = number - 1; 1746 else 1747 syntax_error = create<AST::SyntaxError>("History entry index value invalid or out of range"_string.release_value_but_fixme_should_propagate_errors()); 1748 } 1749 if (":^$*"sv.contains(peek())) { 1750 is_word_selector = true; 1751 if (peek() == ':') 1752 consume(); 1753 } 1754 } 1755 } 1756 1757 if (!is_word_selector) { 1758 auto node = create<AST::HistoryEvent>(move(selector)); 1759 if (syntax_error) 1760 node->set_is_syntax_error(*syntax_error); 1761 return node; 1762 } 1763 1764 // Word selectors 1765 auto parse_word_selector = [&]() -> Optional<AST::HistorySelector::WordSelector> { 1766 auto c = peek(); 1767 AST::HistorySelector::WordSelectorKind word_selector_kind; 1768 ssize_t offset = -1; 1769 if (isdigit(c)) { 1770 auto num = consume_while(is_digit); 1771 auto value = num.to_uint(); 1772 if (!value.has_value()) 1773 return {}; 1774 word_selector_kind = AST::HistorySelector::WordSelectorKind::Index; 1775 offset = value.value(); 1776 } else if (c == '^') { 1777 consume(); 1778 word_selector_kind = AST::HistorySelector::WordSelectorKind::Index; 1779 offset = 1; 1780 } else if (c == '$') { 1781 consume(); 1782 word_selector_kind = AST::HistorySelector::WordSelectorKind::Last; 1783 offset = 0; 1784 } 1785 if (offset == -1) 1786 return {}; 1787 return AST::HistorySelector::WordSelector { 1788 word_selector_kind, 1789 static_cast<size_t>(offset), 1790 { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, 1791 syntax_error 1792 }; 1793 }; 1794 1795 auto make_word_selector = [&](AST::HistorySelector::WordSelectorKind word_selector_kind, size_t offset) { 1796 return AST::HistorySelector::WordSelector { 1797 word_selector_kind, 1798 offset, 1799 { m_rule_start_offsets.last(), m_offset, m_rule_start_lines.last(), line() }, 1800 syntax_error 1801 }; 1802 }; 1803 1804 auto first_char = peek(); 1805 if (!(is_digit(first_char) || "^$-*"sv.contains(first_char))) { 1806 if (!syntax_error) 1807 syntax_error = create<AST::SyntaxError>("Expected a word selector after ':' in a history event designator"_string.release_value_but_fixme_should_propagate_errors(), true); 1808 } else if (first_char == '*') { 1809 consume(); 1810 selector.word_selector_range.start = make_word_selector(AST::HistorySelector::WordSelectorKind::Index, 1); 1811 selector.word_selector_range.end = make_word_selector(AST::HistorySelector::WordSelectorKind::Last, 0); 1812 } else if (first_char == '-') { 1813 consume(); 1814 selector.word_selector_range.start = make_word_selector(AST::HistorySelector::WordSelectorKind::Index, 0); 1815 auto last_selector = parse_word_selector(); 1816 if (!last_selector.has_value()) 1817 selector.word_selector_range.end = make_word_selector(AST::HistorySelector::WordSelectorKind::Last, 1); 1818 else 1819 selector.word_selector_range.end = last_selector.release_value(); 1820 } else { 1821 auto first_selector = parse_word_selector(); 1822 // peek() should be a digit, ^, or $ here, so this should always have value. 1823 VERIFY(first_selector.has_value()); 1824 selector.word_selector_range.start = first_selector.release_value(); 1825 if (peek() == '-') { 1826 consume(); 1827 auto last_selector = parse_word_selector(); 1828 if (last_selector.has_value()) { 1829 selector.word_selector_range.end = last_selector.release_value(); 1830 } else { 1831 selector.word_selector_range.end = make_word_selector(AST::HistorySelector::WordSelectorKind::Last, 1); 1832 } 1833 } else if (peek() == '*') { 1834 consume(); 1835 selector.word_selector_range.end = make_word_selector(AST::HistorySelector::WordSelectorKind::Last, 0); 1836 } else { 1837 selector.word_selector_range.end.clear(); 1838 } 1839 } 1840 1841 auto node = create<AST::HistoryEvent>(move(selector)); 1842 if (syntax_error) 1843 node->set_is_syntax_error(*syntax_error); 1844 return node; 1845} 1846 1847RefPtr<AST::Node> Parser::parse_comment() 1848{ 1849 if (at_end()) 1850 return nullptr; 1851 1852 if (peek() != '#') 1853 return nullptr; 1854 1855 consume(); 1856 auto text = consume_while(is_not('\n')); 1857 return create<AST::Comment>(String::from_utf8(text).release_value_but_fixme_should_propagate_errors()); // Comment 1858} 1859 1860RefPtr<AST::Node> Parser::parse_bareword() 1861{ 1862 auto rule_start = push_start(); 1863 StringBuilder builder; 1864 auto is_acceptable_bareword_character = [&](char c) { 1865 return strchr("\\\"'*$&#|(){} ?;<>\n", c) == nullptr 1866 && !m_extra_chars_not_allowed_in_barewords.contains_slow(c); 1867 }; 1868 while (!at_end()) { 1869 char ch = peek(); 1870 if (ch == '\\') { 1871 consume(); 1872 if (!at_end()) { 1873 ch = consume(); 1874 if (is_acceptable_bareword_character(ch)) 1875 builder.append('\\'); 1876 } 1877 builder.append(ch); 1878 continue; 1879 } 1880 1881 if (m_is_in_brace_expansion_spec && next_is(".."sv)) { 1882 // Don't eat '..' in a brace expansion spec. 1883 break; 1884 } 1885 1886 if (is_acceptable_bareword_character(ch)) { 1887 builder.append(consume()); 1888 continue; 1889 } 1890 1891 break; 1892 } 1893 1894 if (builder.is_empty()) 1895 return nullptr; 1896 1897 auto current_end = m_offset; 1898 auto current_line = line(); 1899 auto string = builder.to_string().release_value_but_fixme_should_propagate_errors(); 1900 if (string.starts_with('~')) { 1901 String username; 1902 RefPtr<AST::Node> tilde, text; 1903 1904 auto first_slash_index = string.find_byte_offset('/'); 1905 if (first_slash_index.has_value()) { 1906 username = string.substring_from_byte_offset(1, *first_slash_index - 1).release_value_but_fixme_should_propagate_errors(); 1907 string = string.substring_from_byte_offset(*first_slash_index).release_value_but_fixme_should_propagate_errors(); 1908 } else { 1909 username = string.substring_from_byte_offset(1).release_value_but_fixme_should_propagate_errors(); 1910 string = {}; 1911 } 1912 1913 // Synthesize a Tilde Node with the correct positioning information. 1914 { 1915 restore_to(rule_start->offset, rule_start->line); 1916 auto ch = consume(); 1917 VERIFY(ch == '~'); 1918 auto username_length = username.bytes_as_string_view().length(); 1919 tilde = create<AST::Tilde>(move(username)); 1920 // Consume the username (if any) 1921 for (size_t i = 0; i < username_length; ++i) 1922 consume(); 1923 } 1924 1925 if (string.is_empty()) 1926 return tilde; 1927 1928 // Synthesize a BarewordLiteral Node with the correct positioning information. 1929 { 1930 auto text_start = push_start(); 1931 restore_to(current_end, current_line); 1932 text = create<AST::BarewordLiteral>(move(string)); 1933 } 1934 1935 return create<AST::Juxtaposition>(tilde.release_nonnull(), text.release_nonnull()); // Juxtaposition Variable Bareword 1936 } 1937 1938 if (string.starts_with_bytes("\\~"sv)) { 1939 // Un-escape the tilde, but only at the start (where it would be an expansion) 1940 string = string.substring_from_byte_offset(1).release_value_but_fixme_should_propagate_errors(); 1941 } 1942 1943 return create<AST::BarewordLiteral>(move(string)); // Bareword Literal 1944} 1945 1946RefPtr<AST::Node> Parser::parse_glob() 1947{ 1948 auto rule_start = push_start(); 1949 auto bareword_part = parse_bareword(); 1950 1951 if (at_end()) 1952 return bareword_part; 1953 1954 char ch = peek(); 1955 if (ch == '*' || ch == '?') { 1956 auto saved_offset = save_offset(); 1957 consume(); 1958 StringBuilder textbuilder; 1959 if (bareword_part) { 1960 StringView text; 1961 if (bareword_part->is_bareword()) { 1962 auto bareword = static_cast<AST::BarewordLiteral*>(bareword_part.ptr()); 1963 text = bareword->text(); 1964 } else { 1965 // FIXME: Allow composition of tilde+bareword with globs: '~/foo/bar/baz*' 1966 restore_to(saved_offset.offset, saved_offset.line); 1967 bareword_part->set_is_syntax_error(*create<AST::SyntaxError>(String::formatted("Unexpected {} inside a glob", bareword_part->class_name()).release_value_but_fixme_should_propagate_errors())); 1968 return bareword_part; 1969 } 1970 textbuilder.append(text); 1971 } 1972 1973 textbuilder.append(ch); 1974 1975 auto glob_after = parse_glob(); 1976 if (glob_after) { 1977 if (glob_after->is_glob()) { 1978 auto glob = static_cast<AST::Glob*>(glob_after.ptr()); 1979 textbuilder.append(glob->text()); 1980 } else if (glob_after->is_bareword()) { 1981 auto bareword = static_cast<AST::BarewordLiteral*>(glob_after.ptr()); 1982 textbuilder.append(bareword->text()); 1983 } else if (glob_after->is_tilde()) { 1984 auto bareword = static_cast<AST::Tilde*>(glob_after.ptr()); 1985 textbuilder.append('~'); 1986 textbuilder.append(bareword->text()); 1987 } else { 1988 return create<AST::SyntaxError>(String::formatted("Invalid node '{}' in glob position, escape shell special characters", glob_after->class_name()).release_value_but_fixme_should_propagate_errors()); 1989 } 1990 } 1991 1992 return create<AST::Glob>(textbuilder.to_string().release_value_but_fixme_should_propagate_errors()); // Glob 1993 } 1994 1995 return bareword_part; 1996} 1997 1998RefPtr<AST::Node> Parser::parse_brace_expansion() 1999{ 2000 auto rule_start = push_start(); 2001 2002 if (!expect('{')) 2003 return nullptr; 2004 2005 if (auto spec = parse_brace_expansion_spec()) { 2006 if (!expect('}')) 2007 spec->set_is_syntax_error(create<AST::SyntaxError>("Expected a close brace '}' to end a brace expansion"_string.release_value_but_fixme_should_propagate_errors(), true)); 2008 2009 return spec; 2010 } 2011 2012 restore_to(*rule_start); 2013 return nullptr; 2014} 2015 2016RefPtr<AST::Node> Parser::parse_brace_expansion_spec() 2017{ 2018 TemporaryChange is_in_brace_expansion { m_is_in_brace_expansion_spec, true }; 2019 ScopedValueRollback chars_change { m_extra_chars_not_allowed_in_barewords }; 2020 2021 m_extra_chars_not_allowed_in_barewords.append(','); 2022 2023 auto rule_start = push_start(); 2024 Vector<NonnullRefPtr<AST::Node>> subexpressions; 2025 2026 if (next_is(","sv)) { 2027 // Note that we don't consume the ',' here. 2028 subexpressions.append(create<AST::StringLiteral>(String {}, AST::StringLiteral::EnclosureType::None)); 2029 } else { 2030 auto start_expr = parse_expression(); 2031 if (start_expr) { 2032 if (expect(".."sv)) { 2033 if (auto end_expr = parse_expression()) { 2034 if (end_expr->position().start_offset != start_expr->position().end_offset + 2) 2035 end_expr->set_is_syntax_error(create<AST::SyntaxError>("Expected no whitespace between '..' and the following expression in brace expansion"_string.release_value_but_fixme_should_propagate_errors())); 2036 2037 return create<AST::Range>(start_expr.release_nonnull(), end_expr.release_nonnull()); 2038 } 2039 2040 return create<AST::Range>(start_expr.release_nonnull(), create<AST::SyntaxError>("Expected an expression to end range brace expansion with"_string.release_value_but_fixme_should_propagate_errors(), true)); 2041 } 2042 } 2043 2044 if (start_expr) 2045 subexpressions.append(start_expr.release_nonnull()); 2046 } 2047 2048 while (expect(',')) { 2049 auto expr = parse_expression(); 2050 if (expr) { 2051 subexpressions.append(expr.release_nonnull()); 2052 } else { 2053 subexpressions.append(create<AST::StringLiteral>(String {}, AST::StringLiteral::EnclosureType::None)); 2054 } 2055 } 2056 2057 if (subexpressions.is_empty()) 2058 return nullptr; 2059 2060 return create<AST::BraceExpansion>(move(subexpressions)); 2061} 2062 2063RefPtr<AST::Node> Parser::parse_heredoc_initiation_record() 2064{ 2065 if (!next_is("<<"sv)) 2066 return nullptr; 2067 2068 auto rule_start = push_start(); 2069 2070 // '<' '<' 2071 consume(); 2072 consume(); 2073 2074 HeredocInitiationRecord record; 2075 record.end = "<error>"_string.release_value_but_fixme_should_propagate_errors(); 2076 2077 RefPtr<AST::SyntaxError> syntax_error_node; 2078 2079 // '-' | '~' 2080 switch (peek()) { 2081 case '-': 2082 record.deindent = false; 2083 consume(); 2084 break; 2085 case '~': 2086 record.deindent = true; 2087 consume(); 2088 break; 2089 default: 2090 restore_to(*rule_start); 2091 return nullptr; 2092 } 2093 2094 // StringLiteral | bareword 2095 if (auto bareword = parse_bareword()) { 2096 if (!bareword->is_bareword()) { 2097 syntax_error_node = create<AST::SyntaxError>(String::formatted("Expected a bareword or a quoted string, not {}", bareword->class_name()).release_value_but_fixme_should_propagate_errors()); 2098 } else { 2099 if (bareword->is_syntax_error()) 2100 syntax_error_node = bareword->syntax_error_node(); 2101 else 2102 record.end = static_cast<AST::BarewordLiteral*>(bareword.ptr())->text(); 2103 } 2104 2105 record.interpolate = true; 2106 } else if (peek() == '\'') { 2107 consume(); 2108 auto text = consume_while(is_not('\'')); 2109 bool is_error = false; 2110 if (!expect('\'')) 2111 is_error = true; 2112 if (is_error) 2113 syntax_error_node = create<AST::SyntaxError>("Expected a terminating single quote"_string.release_value_but_fixme_should_propagate_errors(), true); 2114 2115 record.end = String::from_utf8(text).release_value_but_fixme_should_propagate_errors(); 2116 record.interpolate = false; 2117 } else { 2118 syntax_error_node = create<AST::SyntaxError>("Expected a bareword or a single-quoted string literal for heredoc end key"_string.release_value_but_fixme_should_propagate_errors(), true); 2119 } 2120 2121 auto node = create<AST::Heredoc>(record.end, record.interpolate, record.deindent); 2122 if (syntax_error_node) 2123 node->set_is_syntax_error(*syntax_error_node); 2124 else 2125 node->set_is_syntax_error(*create<AST::SyntaxError>(String::formatted("Expected heredoc contents for heredoc with end key '{}'", node->end()).release_value_but_fixme_should_propagate_errors(), true)); 2126 2127 record.node = node; 2128 m_heredoc_initiations.append(move(record)); 2129 2130 return node; 2131} 2132 2133bool Parser::parse_heredoc_entries() 2134{ 2135 auto heredocs = move(m_heredoc_initiations); 2136 m_heredoc_initiations.clear(); 2137 // Try to parse heredoc entries, as reverse recorded in the initiation records 2138 for (auto& record : heredocs) { 2139 auto rule_start = push_start(); 2140 if (m_rule_start_offsets.size() > max_allowed_nested_rule_depth) { 2141 record.node->set_is_syntax_error(*create<AST::SyntaxError>(String::formatted("Expression nested too deep (max allowed is {})", max_allowed_nested_rule_depth).release_value_but_fixme_should_propagate_errors())); 2142 continue; 2143 } 2144 bool found_key = false; 2145 if (!record.interpolate) { 2146 // Since no interpolation is allowed, just read lines until we hit the key 2147 Optional<Offset> last_line_offset; 2148 for (;;) { 2149 if (at_end()) 2150 break; 2151 if (peek() == '\n') 2152 consume(); 2153 last_line_offset = current_position(); 2154 auto line = consume_while(is_not('\n')); 2155 if (peek() == '\n') 2156 consume(); 2157 if (line.trim_whitespace() == record.end) { 2158 found_key = true; 2159 break; 2160 } 2161 } 2162 2163 if (!last_line_offset.has_value()) 2164 last_line_offset = current_position(); 2165 // Now just wrap it in a StringLiteral and set it as the node's contents 2166 auto node = create<AST::StringLiteral>( 2167 String::from_utf8(m_input.substring_view(rule_start->offset, last_line_offset->offset - rule_start->offset)).release_value_but_fixme_should_propagate_errors(), 2168 AST::StringLiteral::EnclosureType::None); 2169 if (!found_key) 2170 node->set_is_syntax_error(*create<AST::SyntaxError>(String::formatted("Expected to find the heredoc key '{}', but found Eof", record.end).release_value_but_fixme_should_propagate_errors(), true)); 2171 record.node->set_contents(move(node)); 2172 } else { 2173 // Interpolation is allowed, so we're going to read doublequoted string innards 2174 // until we find a line that contains the key 2175 auto end_condition = move(m_end_condition); 2176 found_key = false; 2177 set_end_condition(make<Function<bool()>>([this, end = record.end, &found_key] { 2178 if (found_key) 2179 return true; 2180 auto offset = current_position(); 2181 auto cond = move(m_end_condition); 2182 ScopeGuard guard { 2183 [&] { 2184 m_end_condition = move(cond); 2185 } 2186 }; 2187 if (peek() == '\n') { 2188 consume(); 2189 auto line = consume_while(is_not('\n')); 2190 if (peek() == '\n') 2191 consume(); 2192 if (line.trim_whitespace() == end) { 2193 restore_to(offset.offset, offset.line); 2194 found_key = true; 2195 return true; 2196 } 2197 } 2198 restore_to(offset.offset, offset.line); 2199 return false; 2200 })); 2201 2202 auto expr = parse_string_inner(StringEndCondition::Heredoc); 2203 set_end_condition(move(end_condition)); 2204 2205 if (found_key) { 2206 auto offset = current_position(); 2207 if (peek() == '\n') 2208 consume(); 2209 auto line = consume_while(is_not('\n')); 2210 if (peek() == '\n') 2211 consume(); 2212 if (line.trim_whitespace() != record.end) 2213 restore_to(offset.offset, offset.line); 2214 } 2215 2216 if (!expr && found_key) { 2217 expr = create<AST::StringLiteral>(String {}, AST::StringLiteral::EnclosureType::None); 2218 } else if (!expr) { 2219 expr = create<AST::SyntaxError>(String::formatted("Expected to find a valid string inside a heredoc (with end key '{}')", record.end).release_value_but_fixme_should_propagate_errors(), true); 2220 } else if (!found_key) { 2221 expr->set_is_syntax_error(*create<AST::SyntaxError>(String::formatted("Expected to find the heredoc key '{}'", record.end).release_value_but_fixme_should_propagate_errors(), true)); 2222 } 2223 2224 record.node->set_contents(create<AST::DoubleQuotedString>(move(expr))); 2225 } 2226 } 2227 return true; 2228} 2229 2230StringView Parser::consume_while(Function<bool(char)> condition) 2231{ 2232 if (at_end()) 2233 return {}; 2234 2235 auto start_offset = m_offset; 2236 2237 while (!at_end() && condition(peek())) 2238 consume(); 2239 2240 return m_input.substring_view(start_offset, m_offset - start_offset); 2241} 2242 2243bool Parser::next_is(StringView next) 2244{ 2245 auto start = current_position(); 2246 auto res = expect(next); 2247 restore_to(start.offset, start.line); 2248 return res; 2249} 2250 2251}