#include "lex-internal.h" #include static bool char_can_continue_word(struct bshell_lex_ctx *ctx, fx_wchar c) { if (fx_wchar_is_alnum(c)) { return true; } if (fx_wchar_is_space(c)) { return false; } if (c == '$') { return true; } if (char_can_begin_symbol_in_state(ctx, c, BSHELL_LEX_STATE_WORD)) { return false; } return true; } static enum bshell_status command_symbol(struct bshell_lex_ctx *ctx) { const struct bshell_lex_token_definition *sym = NULL; enum bshell_status status = read_symbol(ctx, &sym); if (status != BSHELL_SUCCESS) { return status; } handle_lex_state_transition(ctx, sym->id); struct bshell_lex_token *tok = NULL; switch (sym->id) { case BSHELL_SYM_DQUOTE: return BSHELL_SUCCESS; case BSHELL_SYM_SQUOTE: status = read_literal_string(ctx, &tok); if (status != BSHELL_SUCCESS) { return status; } enqueue_token(ctx, tok); return BSHELL_SUCCESS; case BSHELL_SYM_HASH: return read_line_comment(ctx); case BSHELL_SYM_DOLLAR: status = read_var(ctx, BSHELL_TOK_VAR, &tok); if (status != BSHELL_SUCCESS) { return status; } if (char_can_continue_word(ctx, peek_char(ctx))) { lex_state_push(ctx, BSHELL_LEX_STATE_WORD, 0); } enqueue_token(ctx, tok); return status; case BSHELL_SYM_AT: status = read_var(ctx, BSHELL_TOK_VAR_SPLAT, &tok); if (status != BSHELL_SUCCESS) { return status; } enqueue_token(ctx, tok); return status; case BSHELL_SYM_DOLLAR_LEFT_BRACE: status = read_braced_var(ctx, BSHELL_TOK_VAR, &tok); if (status != BSHELL_SUCCESS) { return status; } if (char_can_continue_word(ctx, peek_char(ctx))) { lex_state_push(ctx, BSHELL_LEX_STATE_WORD, 0); } enqueue_token(ctx, tok); return status; default: break; } push_symbol(ctx, sym->id); return BSHELL_SUCCESS; } static bool string_is_redirection(const char *s) { if (!*s) { return false; } if (!strcmp(s, ">") || !strcmp(s, ">>")) { return true; } long nr_angles = 0; for (size_t i = 0; s[i];) { fx_wchar c = fx_wchar_utf8_codepoint_decode(s); if (fx_wchar_is_number(c)) { if (nr_angles) { return false; } } else if (c == '>') { nr_angles++; if (nr_angles > 2) { return false; } } else { return false; } s += fx_wchar_utf8_codepoint_stride(s); } return true; } static enum bshell_status command_word(struct bshell_lex_ctx *ctx) { struct bshell_lex_token *word = NULL; enum bshell_status status = read_word(ctx, READ_NO_NUMBER_RECOGNITION, &word); if (status != BSHELL_SUCCESS) { return status; } bool continue_word = false; fx_wchar c = peek_char(ctx); const char *s = word->tok_str; if (char_can_begin_symbol_in_state(ctx, c, BSHELL_LEX_STATE_WORD)) { continue_word = true; } if (char_has_flags(ctx, c, BSHELL_LEX_TOKEN_TERMINATES_WORD)) { continue_word = false; } if (string_is_redirection(s)) { continue_word = false; } if (continue_word) { lex_state_push(ctx, BSHELL_LEX_STATE_WORD, 0); } enqueue_token(ctx, word); return BSHELL_SUCCESS; } enum bshell_status command_pump_token(struct bshell_lex_ctx *ctx) { fx_wchar c = peek_char(ctx); bool newline = false; set_token_start(ctx); while (fx_wchar_is_space(c)) { if (c == '\n') { newline = true; } set_token_end(ctx); advance_char_noread(ctx); c = peek_char_noread(ctx); } if (newline) { struct bshell_lex_token *tok = bshell_lex_token_create(BSHELL_TOK_LINEFEED); enqueue_token(ctx, tok); handle_lex_state_transition(ctx, BSHELL_TOK_LINEFEED); return BSHELL_SUCCESS; } if (char_can_begin_symbol(ctx, c)) { return command_symbol(ctx); } return command_word(ctx); } const struct bshell_lex_state_link links[] = { LINK_PUSH(BSHELL_SYM_DQUOTE, BSHELL_LEX_STATE_STRING, 0), LINK_PUSH_WITH_TERM( BSHELL_SYM_LEFT_PAREN, BSHELL_LEX_STATE_STATEMENT, STATEMENT_F_DISABLE_KEYWORDS, BSHELL_SYM_RIGHT_PAREN), LINK_PUSH(BSHELL_SYM_DOLLAR_LEFT_PAREN, BSHELL_LEX_STATE_STATEMENT, 0), LINK_POP(BSHELL_SYM_RIGHT_PAREN), LINK_POP(BSHELL_SYM_RIGHT_BRACE), LINK_CHANGE(BSHELL_SYM_SEMICOLON, BSHELL_LEX_STATE_STATEMENT), LINK_PUSH(BSHELL_SYM_AT_LEFT_BRACE, BSHELL_LEX_STATE_HASHTABLE, 0), LINK_CHANGE(BSHELL_TOK_LINEFEED, BSHELL_LEX_STATE_STATEMENT), LINK_END, }; static const unsigned int symbols[] = { BSHELL_SYM_DQUOTE, BSHELL_SYM_SQUOTE, BSHELL_SYM_DOLLAR, BSHELL_SYM_DOLLAR_LEFT_PAREN, BSHELL_SYM_DOLLAR_LEFT_BRACE, BSHELL_SYM_AT, BSHELL_SYM_AT_LEFT_BRACE, BSHELL_SYM_AT_LEFT_PAREN, BSHELL_SYM_AMPERSAND, BSHELL_SYM_PIPE, BSHELL_SYM_SEMICOLON, BSHELL_SYM_RIGHT_PAREN, BSHELL_SYM_LEFT_PAREN, BSHELL_SYM_LEFT_BRACE, BSHELL_SYM_RIGHT_BRACE, BSHELL_SYM_NONE, }; const struct bshell_lex_state_type lex_command_state = { .s_id = BSHELL_LEX_STATE_COMMAND, .s_pump_token = command_pump_token, .s_links = links, .s_symbols = symbols, };