1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006 |
- /*
- * Copyright (c) 2022, Ali Mohammad Pur <mpfard@serenityos.org>
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
- #include <AK/CharacterTypes.h>
- #include <Shell/PosixLexer.h>
- static bool is_operator(StringView text)
- {
- return Shell::Posix::Token::operator_from_name(text).has_value();
- }
- static bool is_part_of_operator(StringView text, char ch)
- {
- StringBuilder builder;
- builder.append(text);
- builder.append(ch);
- return Shell::Posix::Token::operator_from_name(builder.string_view()).has_value();
- }
- namespace Shell::Posix {
- ErrorOr<Vector<Token>> Lexer::batch_next(Optional<Reduction> starting_reduction)
- {
- if (starting_reduction.has_value())
- m_next_reduction = *starting_reduction;
- for (; m_next_reduction != Reduction::None;) {
- auto result = TRY(reduce(m_next_reduction));
- m_next_reduction = result.next_reduction;
- if (!result.tokens.is_empty())
- return result.tokens;
- }
- return Vector<Token> {};
- }
- ExpansionRange Lexer::range(ssize_t offset) const
- {
- return {
- m_state.position.end_offset - m_state.position.start_offset + offset,
- 0,
- };
- }
- char Lexer::consume()
- {
- auto ch = m_lexer.consume();
- if (ch == '\n') {
- m_state.position.end_line.line_number++;
- m_state.position.end_line.line_column = 0;
- }
- m_state.position.end_offset++;
- return ch;
- }
- void Lexer::reconsume(StringView string)
- {
- for (auto byte : string.bytes()) {
- if (byte == '\n') {
- m_state.position.end_line.line_number++;
- m_state.position.end_line.line_column = 0;
- }
- m_state.position.end_offset++;
- }
- }
- bool Lexer::consume_specific(char ch)
- {
- if (m_lexer.peek() == ch) {
- consume();
- return true;
- }
- return false;
- }
- ErrorOr<Lexer::ReductionResult> Lexer::reduce(Reduction reduction)
- {
- switch (reduction) {
- case Reduction::None:
- return ReductionResult { {}, Reduction::None };
- case Reduction::End:
- return reduce_end();
- case Reduction::Operator:
- return reduce_operator();
- case Reduction::Comment:
- return reduce_comment();
- case Reduction::SingleQuotedString:
- return reduce_single_quoted_string();
- case Reduction::DoubleQuotedString:
- return reduce_double_quoted_string();
- case Reduction::Expansion:
- return reduce_expansion();
- case Reduction::CommandExpansion:
- return reduce_command_expansion();
- case Reduction::Start:
- return reduce_start();
- case Reduction::ArithmeticExpansion:
- return reduce_arithmetic_expansion();
- case Reduction::SpecialParameterExpansion:
- return reduce_special_parameter_expansion();
- case Reduction::ParameterExpansion:
- return reduce_parameter_expansion();
- case Reduction::CommandOrArithmeticSubstitutionExpansion:
- return reduce_command_or_arithmetic_substitution_expansion();
- case Reduction::ExtendedParameterExpansion:
- return reduce_extended_parameter_expansion();
- case Reduction::HeredocContents:
- return reduce_heredoc_contents();
- }
- VERIFY_NOT_REACHED();
- }
- ErrorOr<Lexer::ReductionResult> Lexer::reduce_end()
- {
- return ReductionResult {
- .tokens = { Token::eof() },
- .next_reduction = Reduction::None,
- };
- }
- Lexer::HeredocKeyResult Lexer::process_heredoc_key(Token const& token)
- {
- StringBuilder builder;
- enum ParseState {
- Free,
- InDoubleQuotes,
- InSingleQuotes,
- };
- Vector<ParseState, 4> parse_state;
- parse_state.append(Free);
- bool escaped = false;
- bool had_a_single_quote_segment = false;
- for (auto byte : token.value.bytes()) {
- switch (parse_state.last()) {
- case Free:
- switch (byte) {
- case '"':
- if (escaped) {
- builder.append(byte);
- escaped = false;
- } else {
- parse_state.append(InDoubleQuotes);
- }
- break;
- case '\'':
- if (escaped) {
- builder.append(byte);
- escaped = false;
- } else {
- had_a_single_quote_segment = true;
- parse_state.append(InSingleQuotes);
- }
- break;
- case '\\':
- if (escaped) {
- builder.append(byte);
- escaped = false;
- } else {
- escaped = true;
- }
- break;
- default:
- // NOTE: bash eats the backslash outside quotes :shrug:
- if (escaped && parse_state.last() != Free) {
- builder.append('\\');
- escaped = false;
- }
- builder.append(byte);
- break;
- }
- break;
- case InDoubleQuotes:
- if (!escaped && byte == '"') {
- parse_state.take_last();
- break;
- }
- if (escaped) {
- if (byte != '"')
- builder.append('\\');
- builder.append(byte);
- break;
- }
- if (byte == '\\')
- escaped = true;
- else
- builder.append(byte);
- break;
- case InSingleQuotes:
- if (byte == '\'') {
- parse_state.take_last();
- break;
- }
- builder.append(byte);
- break;
- }
- }
- // NOTE: Not checking the final state as any garbage that even partially parses is allowed to be used as a key :/
- return {
- .key = builder.to_string().release_value_but_fixme_should_propagate_errors(),
- .allow_interpolation = !had_a_single_quote_segment,
- };
- }
- ErrorOr<Lexer::ReductionResult> Lexer::reduce_operator()
- {
- if (m_lexer.is_eof()) {
- if (is_operator(m_state.buffer.string_view())) {
- auto tokens = TRY(Token::operators_from(m_state));
- m_state.buffer.clear();
- m_state.position.start_offset = m_state.position.end_offset;
- m_state.position.start_line = m_state.position.end_line;
- return ReductionResult {
- .tokens = move(tokens),
- .next_reduction = Reduction::End,
- };
- }
- return reduce(Reduction::Start);
- }
- if (is_part_of_operator(m_state.buffer.string_view(), m_lexer.peek())) {
- m_state.buffer.append(consume());
- return ReductionResult {
- .tokens = {},
- .next_reduction = Reduction::Operator,
- };
- }
- auto tokens = Vector<Token> {};
- if (is_operator(m_state.buffer.string_view())) {
- tokens.extend(TRY(Token::operators_from(m_state)));
- m_state.buffer.clear();
- m_state.position.start_offset = m_state.position.end_offset;
- m_state.position.start_line = m_state.position.end_line;
- }
- auto expect_heredoc_entry = !tokens.is_empty() && (tokens.last().type == Token::Type::DoubleLessDash || tokens.last().type == Token::Type::DoubleLess);
- auto result = TRY(reduce(Reduction::Start));
- tokens.extend(move(result.tokens));
- while (expect_heredoc_entry && tokens.size() == 1) {
- result = TRY(reduce(result.next_reduction));
- tokens.extend(move(result.tokens));
- }
- if (expect_heredoc_entry && tokens.size() > 1) {
- auto [key, interpolation] = process_heredoc_key(tokens[1]);
- m_state.heredoc_entries.append(HeredocEntry {
- .key = key,
- .allow_interpolation = interpolation,
- .dedent = tokens[0].type == Token::Type::DoubleLessDash,
- });
- }
- return ReductionResult {
- .tokens = move(tokens),
- .next_reduction = result.next_reduction,
- };
- }
- ErrorOr<Lexer::ReductionResult> Lexer::reduce_comment()
- {
- if (m_lexer.is_eof()) {
- return ReductionResult {
- .tokens = {},
- .next_reduction = Reduction::End,
- };
- }
- if (consume() == '\n') {
- m_state.on_new_line = true;
- return ReductionResult {
- .tokens = { Token::newline() },
- .next_reduction = Reduction::Start,
- };
- }
- return ReductionResult {
- .tokens = {},
- .next_reduction = Reduction::Comment,
- };
- }
- ErrorOr<Lexer::ReductionResult> Lexer::reduce_single_quoted_string()
- {
- if (m_lexer.is_eof()) {
- auto tokens = TRY(Token::maybe_from_state(m_state));
- tokens.append(Token::continuation('\''));
- return ReductionResult {
- .tokens = move(tokens),
- .next_reduction = Reduction::End,
- };
- }
- auto ch = consume();
- m_state.buffer.append(ch);
- if (ch == '\'') {
- return ReductionResult {
- .tokens = {},
- .next_reduction = Reduction::Start,
- };
- }
- return ReductionResult {
- .tokens = {},
- .next_reduction = Reduction::SingleQuotedString,
- };
- }
- ErrorOr<Lexer::ReductionResult> Lexer::reduce_double_quoted_string()
- {
- m_state.previous_reduction = Reduction::DoubleQuotedString;
- if (m_lexer.is_eof()) {
- auto tokens = TRY(Token::maybe_from_state(m_state));
- tokens.append(Token::continuation('"'));
- return ReductionResult {
- .tokens = move(tokens),
- .next_reduction = Reduction::End,
- };
- }
- auto ch = consume();
- m_state.buffer.append(ch);
- if (m_state.escaping) {
- m_state.escaping = false;
- return ReductionResult {
- .tokens = {},
- .next_reduction = Reduction::DoubleQuotedString,
- };
- }
- switch (ch) {
- case '\\':
- m_state.escaping = true;
- return ReductionResult {
- .tokens = {},
- .next_reduction = Reduction::DoubleQuotedString,
- };
- case '"':
- m_state.previous_reduction = Reduction::Start;
- return ReductionResult {
- .tokens = {},
- .next_reduction = Reduction::Start,
- };
- case '$':
- if (m_lexer.next_is("("))
- m_state.expansions.empend(CommandExpansion { .command = StringBuilder {}, .range = range(-1) });
- else
- m_state.expansions.empend(ParameterExpansion { .parameter = StringBuilder {}, .range = range(-1) });
- return ReductionResult {
- .tokens = {},
- .next_reduction = Reduction::Expansion,
- };
- case '`':
- m_state.expansions.empend(CommandExpansion { .command = StringBuilder {}, .range = range(-1) });
- return ReductionResult {
- .tokens = {},
- .next_reduction = Reduction::CommandExpansion,
- };
- default:
- return ReductionResult {
- .tokens = {},
- .next_reduction = Reduction::DoubleQuotedString,
- };
- }
- }
- ErrorOr<Lexer::ReductionResult> Lexer::reduce_expansion()
- {
- if (m_lexer.is_eof())
- return reduce(m_state.previous_reduction);
- auto ch = m_lexer.peek();
- switch (ch) {
- case '{':
- consume();
- m_state.buffer.append(ch);
- return ReductionResult {
- .tokens = {},
- .next_reduction = Reduction::ExtendedParameterExpansion,
- };
- case '(':
- consume();
- m_state.buffer.append(ch);
- return ReductionResult {
- .tokens = {},
- .next_reduction = Reduction::CommandOrArithmeticSubstitutionExpansion,
- };
- case 'a' ... 'z':
- case 'A' ... 'Z':
- case '_': {
- consume();
- m_state.buffer.append(ch);
- auto& expansion = m_state.expansions.last().get<ParameterExpansion>();
- expansion.parameter.append(ch);
- expansion.range.length = m_state.position.end_offset - expansion.range.start - m_state.position.start_offset;
- return ReductionResult {
- .tokens = {},
- .next_reduction = Reduction::ParameterExpansion,
- };
- }
- case '0' ... '9':
- case '-':
- case '!':
- case '@':
- case '#':
- case '?':
- case '*':
- case '$':
- return reduce(Reduction::SpecialParameterExpansion);
- default:
- m_state.buffer.append(ch);
- return reduce(m_state.previous_reduction);
- }
- }
- ErrorOr<Lexer::ReductionResult> Lexer::reduce_command_expansion()
- {
- if (m_lexer.is_eof()) {
- auto& expansion = m_state.expansions.last().get<CommandExpansion>();
- expansion.range.length = m_state.position.end_offset - expansion.range.start - m_state.position.start_offset;
- return ReductionResult {
- .tokens = { Token::continuation('`') },
- .next_reduction = m_state.previous_reduction,
- };
- }
- auto ch = consume();
- if (!m_state.escaping && ch == '`') {
- m_state.buffer.append(ch);
- auto& expansion = m_state.expansions.last().get<CommandExpansion>();
- expansion.range.length = m_state.position.end_offset - expansion.range.start - m_state.position.start_offset;
- return ReductionResult {
- .tokens = {},
- .next_reduction = m_state.previous_reduction,
- };
- }
- if (!m_state.escaping && ch == '\\') {
- m_state.escaping = true;
- return ReductionResult {
- .tokens = {},
- .next_reduction = Reduction::CommandExpansion,
- };
- }
- m_state.escaping = false;
- m_state.buffer.append(ch);
- m_state.expansions.last().get<CommandExpansion>().command.append(ch);
- return ReductionResult {
- .tokens = {},
- .next_reduction = Reduction::CommandExpansion,
- };
- }
- ErrorOr<Lexer::ReductionResult> Lexer::reduce_heredoc_contents()
- {
- if (m_lexer.is_eof()) {
- auto tokens = TRY(Token::maybe_from_state(m_state));
- m_state.buffer.clear();
- m_state.position.start_offset = m_state.position.end_offset;
- m_state.position.start_line = m_state.position.end_line;
- return ReductionResult {
- .tokens = move(tokens),
- .next_reduction = Reduction::End,
- };
- }
- if (!m_state.escaping && consume_specific('\\')) {
- m_state.escaping = true;
- m_state.buffer.append('\\');
- return ReductionResult {
- .tokens = {},
- .next_reduction = Reduction::HeredocContents,
- };
- }
- if (!m_state.escaping && consume_specific('$')) {
- m_state.buffer.append('$');
- if (m_lexer.next_is("("))
- m_state.expansions.empend(CommandExpansion { .command = StringBuilder {}, .range = range(-1) });
- else
- m_state.expansions.empend(ParameterExpansion { .parameter = StringBuilder {}, .range = range(-1) });
- return ReductionResult {
- .tokens = {},
- .next_reduction = Reduction::Expansion,
- };
- }
- if (!m_state.escaping && consume_specific('`')) {
- m_state.buffer.append('`');
- m_state.expansions.empend(CommandExpansion { .command = StringBuilder {}, .range = range(-1) });
- return ReductionResult {
- .tokens = {},
- .next_reduction = Reduction::CommandExpansion,
- };
- }
- m_state.escaping = false;
- m_state.buffer.append(consume());
- return ReductionResult {
- .tokens = {},
- .next_reduction = Reduction::HeredocContents,
- };
- }
- ErrorOr<Lexer::ReductionResult> Lexer::reduce_start()
- {
- auto was_on_new_line = m_state.on_new_line;
- m_state.on_new_line = false;
- if (m_lexer.is_eof()) {
- auto tokens = TRY(Token::maybe_from_state(m_state));
- m_state.buffer.clear();
- m_state.expansions.clear();
- m_state.position.start_offset = m_state.position.end_offset;
- m_state.position.start_line = m_state.position.end_line;
- return ReductionResult {
- .tokens = move(tokens),
- .next_reduction = Reduction::End,
- };
- }
- if (was_on_new_line && !m_state.heredoc_entries.is_empty()) {
- auto const& entry = m_state.heredoc_entries.first();
- auto start_index = m_lexer.tell();
- Optional<size_t> end_index;
- for (; !m_lexer.is_eof();) {
- auto index = m_lexer.tell();
- auto possible_end_index = m_lexer.tell();
- if (m_lexer.consume_specific('\n')) {
- if (entry.dedent)
- m_lexer.ignore_while(is_any_of("\t"sv));
- if (m_lexer.consume_specific(entry.key.bytes_as_string_view())) {
- if (m_lexer.consume_specific('\n') || m_lexer.is_eof()) {
- end_index = possible_end_index;
- break;
- }
- }
- }
- if (m_lexer.tell() == index)
- m_lexer.ignore();
- }
- auto contents = m_lexer.input().substring_view(start_index, end_index.value_or(m_lexer.tell()) - start_index);
- reconsume(contents);
- if (end_index.has_value())
- reconsume(m_lexer.input().substring_view_starting_after_substring(contents).substring_view(0, m_lexer.tell() - *end_index));
- m_state.buffer.clear();
- m_state.buffer.append(contents);
- auto token = TRY(Token::maybe_from_state(m_state)).first();
- token.relevant_heredoc_key = entry.key;
- token.type = Token::Type::HeredocContents;
- m_state.heredoc_entries.take_first();
- m_state.on_new_line = true;
- m_state.buffer.clear();
- m_state.position.start_offset = m_state.position.end_offset;
- m_state.position.start_line = m_state.position.end_line;
- Vector<Token> tokens { move(token), Token::newline() };
- return ReductionResult {
- .tokens = move(tokens),
- .next_reduction = Reduction::Start,
- };
- }
- if (m_state.escaping && consume_specific('\n')) {
- m_state.escaping = false;
- auto buffer = m_state.buffer.to_deprecated_string().substring(0, m_state.buffer.length() - 1);
- m_state.buffer.clear();
- m_state.buffer.append(buffer);
- return ReductionResult {
- .tokens = {},
- .next_reduction = Reduction::Start,
- };
- }
- if (!m_state.escaping && m_lexer.peek() == '#' && m_state.buffer.is_empty()) {
- consume();
- return ReductionResult {
- .tokens = {},
- .next_reduction = Reduction::Comment,
- };
- }
- if (!m_state.escaping && consume_specific('\n')) {
- auto tokens = TRY(Token::maybe_from_state(m_state));
- tokens.append(Token::newline());
- m_state.on_new_line = true;
- m_state.buffer.clear();
- m_state.expansions.clear();
- m_state.position.start_offset = m_state.position.end_offset;
- m_state.position.start_line = m_state.position.end_line;
- return ReductionResult {
- .tokens = move(tokens),
- .next_reduction = Reduction::Start,
- };
- }
- if (!m_state.escaping && consume_specific('\\')) {
- m_state.escaping = true;
- m_state.buffer.append('\\');
- return ReductionResult {
- .tokens = {},
- .next_reduction = Reduction::Start,
- };
- }
- if (!m_state.escaping && consume_specific('\'')) {
- m_state.buffer.append('\'');
- return ReductionResult {
- .tokens = {},
- .next_reduction = Reduction::SingleQuotedString,
- };
- }
- if (!m_state.escaping && consume_specific('"')) {
- m_state.buffer.append('"');
- return ReductionResult {
- .tokens = {},
- .next_reduction = Reduction::DoubleQuotedString,
- };
- }
- if (!m_state.escaping && is_ascii_space(m_lexer.peek())) {
- consume();
- auto tokens = TRY(Token::maybe_from_state(m_state));
- m_state.buffer.clear();
- m_state.expansions.clear();
- m_state.position.start_offset = m_state.position.end_offset;
- m_state.position.start_line = m_state.position.end_line;
- return ReductionResult {
- .tokens = move(tokens),
- .next_reduction = Reduction::Start,
- };
- }
- if (!m_state.escaping && consume_specific('$')) {
- m_state.buffer.append('$');
- if (m_lexer.next_is("("))
- m_state.expansions.empend(CommandExpansion { .command = StringBuilder {}, .range = range(-1) });
- else
- m_state.expansions.empend(ParameterExpansion { .parameter = StringBuilder {}, .range = range(-1) });
- return ReductionResult {
- .tokens = {},
- .next_reduction = Reduction::Expansion,
- };
- }
- if (!m_state.escaping && consume_specific('`')) {
- m_state.buffer.append('`');
- m_state.expansions.empend(CommandExpansion { .command = StringBuilder {}, .range = range(-1) });
- return ReductionResult {
- .tokens = {},
- .next_reduction = Reduction::CommandExpansion,
- };
- }
- if (!m_state.escaping && m_state.in_skip_mode && is_any_of("})"sv)(m_lexer.peek())) {
- // That's an eof for us.
- return ReductionResult {
- .tokens = {},
- .next_reduction = Reduction::None,
- };
- }
- if (!m_state.escaping && is_part_of_operator(""sv, m_lexer.peek())) {
- auto tokens = TRY(Token::maybe_from_state(m_state));
- m_state.buffer.clear();
- m_state.buffer.append(consume());
- m_state.expansions.clear();
- m_state.position.start_offset = m_state.position.end_offset;
- m_state.position.start_line = m_state.position.end_line;
- return ReductionResult {
- .tokens = move(tokens),
- .next_reduction = Reduction::Operator,
- };
- }
- m_state.escaping = false;
- m_state.buffer.append(consume());
- return ReductionResult {
- .tokens = {},
- .next_reduction = Reduction::Start,
- };
- }
- ErrorOr<Lexer::ReductionResult> Lexer::reduce_arithmetic_expansion()
- {
- if (m_lexer.is_eof()) {
- auto& expansion = m_state.expansions.last().get<ArithmeticExpansion>();
- expansion.range.length = m_state.position.end_offset - expansion.range.start - m_state.position.start_offset;
- return ReductionResult {
- .tokens = { Token::continuation("$(("_string) },
- .next_reduction = m_state.previous_reduction,
- };
- }
- if (m_lexer.peek() == ')' && m_state.buffer.string_view().ends_with(')')) {
- m_state.buffer.append(consume());
- auto& expansion = m_state.expansions.last().get<ArithmeticExpansion>();
- expansion.expression = TRY(String::from_utf8(expansion.value.string_view().substring_view(0, expansion.value.length() - 1)));
- expansion.value.clear();
- expansion.range.length = m_state.position.end_offset - expansion.range.start - m_state.position.start_offset;
- return ReductionResult {
- .tokens = {},
- .next_reduction = m_state.previous_reduction,
- };
- }
- auto ch = consume();
- m_state.buffer.append(ch);
- m_state.expansions.last().get<ArithmeticExpansion>().value.append(ch);
- return ReductionResult {
- .tokens = {},
- .next_reduction = Reduction::ArithmeticExpansion,
- };
- }
- ErrorOr<Lexer::ReductionResult> Lexer::reduce_special_parameter_expansion()
- {
- auto ch = consume();
- m_state.buffer.append(ch);
- m_state.expansions.last() = ParameterExpansion {
- .parameter = StringBuilder {},
- .range = range(-2),
- };
- auto& expansion = m_state.expansions.last().get<ParameterExpansion>();
- expansion.parameter.append(ch);
- expansion.range.length = m_state.position.end_offset - expansion.range.start - m_state.position.start_offset;
- return ReductionResult {
- .tokens = {},
- .next_reduction = m_state.previous_reduction,
- };
- }
- ErrorOr<Lexer::ReductionResult> Lexer::reduce_parameter_expansion()
- {
- auto& expansion = m_state.expansions.last().get<ParameterExpansion>();
- if (m_lexer.is_eof()) {
- return ReductionResult {
- .tokens = {},
- .next_reduction = Reduction::Start,
- };
- }
- auto next = m_lexer.peek();
- if (is_ascii_alphanumeric(next) || next == '_') {
- m_state.buffer.append(consume());
- expansion.parameter.append(next);
- expansion.range.length = m_state.position.end_offset - expansion.range.start - m_state.position.start_offset;
- return ReductionResult {
- .tokens = {},
- .next_reduction = Reduction::ParameterExpansion,
- };
- }
- return reduce(m_state.previous_reduction);
- }
- ErrorOr<Lexer::ReductionResult> Lexer::reduce_command_or_arithmetic_substitution_expansion()
- {
- auto ch = m_lexer.peek();
- if (ch == '(' && m_state.buffer.string_view().ends_with("$("sv)) {
- m_state.buffer.append(consume());
- m_state.expansions.last() = ArithmeticExpansion {
- .expression = {},
- .value = StringBuilder {},
- .range = range(-2)
- };
- return ReductionResult {
- .tokens = {},
- .next_reduction = Reduction::ArithmeticExpansion,
- };
- }
- auto saved_position = m_state.position;
- {
- auto skip_mode = switch_to_skip_mode();
- auto next_reduction = Reduction::Start;
- do {
- auto result = TRY(reduce(next_reduction));
- next_reduction = result.next_reduction;
- } while (next_reduction != Reduction::None);
- saved_position = m_state.position;
- }
- auto const skipped_text = m_lexer.input().substring_view(m_state.position.end_offset, saved_position.end_offset - m_state.position.end_offset);
- m_state.position.end_offset = saved_position.end_offset;
- m_state.position.end_line = saved_position.end_line;
- m_state.buffer.append(skipped_text);
- m_state.expansions.last().get<CommandExpansion>().command.append(skipped_text);
- m_state.expansions.last().visit([&](auto& expansion) {
- expansion.range.length = m_state.position.end_offset - expansion.range.start - m_state.position.start_offset;
- });
- if (m_lexer.is_eof()) {
- return ReductionResult {
- .tokens = { Token::continuation("$("_string) },
- .next_reduction = m_state.previous_reduction,
- };
- }
- ch = m_lexer.peek();
- if (ch == '(' && m_state.buffer.string_view().ends_with("$("sv)) {
- m_state.buffer.append(consume());
- m_state.expansions.last() = ArithmeticExpansion {
- .expression = {},
- .value = m_state.expansions.last().get<CommandExpansion>().command,
- .range = range(-2)
- };
- return ReductionResult {
- .tokens = {},
- .next_reduction = Reduction::ArithmeticExpansion,
- };
- }
- if (ch == ')') {
- m_state.buffer.append(consume());
- m_state.expansions.last().visit([&](auto& expansion) {
- expansion.range.length = m_state.position.end_offset - expansion.range.start - m_state.position.start_offset;
- });
- return ReductionResult {
- .tokens = {},
- .next_reduction = m_state.previous_reduction,
- };
- }
- m_state.buffer.append(consume());
- m_state.expansions.last().get<CommandExpansion>().command.append(ch);
- return ReductionResult {
- .tokens = {},
- .next_reduction = Reduction::CommandOrArithmeticSubstitutionExpansion,
- };
- }
- ErrorOr<Lexer::ReductionResult> Lexer::reduce_extended_parameter_expansion()
- {
- auto& expansion = m_state.expansions.last().get<ParameterExpansion>();
- if (m_lexer.is_eof()) {
- return ReductionResult {
- .tokens = { Token::continuation("${"_string) },
- .next_reduction = m_state.previous_reduction,
- };
- }
- auto ch = m_lexer.peek();
- if (ch == '}') {
- m_state.buffer.append(consume());
- expansion.range.length = m_state.position.end_offset - expansion.range.start - m_state.position.start_offset;
- return ReductionResult {
- .tokens = {},
- .next_reduction = m_state.previous_reduction,
- };
- }
- m_state.buffer.append(consume());
- expansion.parameter.append(ch);
- expansion.range.length = m_state.position.end_offset - expansion.range.start - m_state.position.start_offset;
- return ReductionResult {
- .tokens = {},
- .next_reduction = Reduction::ExtendedParameterExpansion,
- };
- }
- StringView Token::type_name() const
- {
- switch (type) {
- case Type::Eof:
- return "Eof"sv;
- case Type::Newline:
- return "Newline"sv;
- case Type::Continuation:
- return "Continuation"sv;
- case Type::Token:
- return "Token"sv;
- case Type::And:
- return "And"sv;
- case Type::Pipe:
- return "Pipe"sv;
- case Type::OpenParen:
- return "OpenParen"sv;
- case Type::CloseParen:
- return "CloseParen"sv;
- case Type::Great:
- return "Great"sv;
- case Type::Less:
- return "Less"sv;
- case Type::AndIf:
- return "AndIf"sv;
- case Type::OrIf:
- return "OrIf"sv;
- case Type::DoubleSemicolon:
- return "DoubleSemicolon"sv;
- case Type::DoubleLess:
- return "DoubleLess"sv;
- case Type::DoubleGreat:
- return "DoubleGreat"sv;
- case Type::LessAnd:
- return "LessAnd"sv;
- case Type::GreatAnd:
- return "GreatAnd"sv;
- case Type::LessGreat:
- return "LessGreat"sv;
- case Type::DoubleLessDash:
- return "DoubleLessDash"sv;
- case Type::Clobber:
- return "Clobber"sv;
- case Type::Semicolon:
- return "Semicolon"sv;
- case Type::HeredocContents:
- return "HeredocContents"sv;
- case Type::AssignmentWord:
- return "AssignmentWord"sv;
- case Type::Bang:
- return "Bang"sv;
- case Type::Case:
- return "Case"sv;
- case Type::CloseBrace:
- return "CloseBrace"sv;
- case Type::Do:
- return "Do"sv;
- case Type::Done:
- return "Done"sv;
- case Type::Elif:
- return "Elif"sv;
- case Type::Else:
- return "Else"sv;
- case Type::Esac:
- return "Esac"sv;
- case Type::Fi:
- return "Fi"sv;
- case Type::For:
- return "For"sv;
- case Type::If:
- return "If"sv;
- case Type::In:
- return "In"sv;
- case Type::IoNumber:
- return "IoNumber"sv;
- case Type::OpenBrace:
- return "OpenBrace"sv;
- case Type::Then:
- return "Then"sv;
- case Type::Until:
- return "Until"sv;
- case Type::VariableName:
- return "VariableName"sv;
- case Type::While:
- return "While"sv;
- case Type::Word:
- return "Word"sv;
- }
- return "Idk"sv;
- }
- }
|