2026-05-10 14:18:46 +01:00
|
|
|
#include "lex-internal.h"
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
#include <bshell/parse/token.h>
|
|
|
|
|
|
|
|
|
|
static bool char_can_continue_word(struct bshell_lex_ctx *ctx, fx_wchar c)
|
2026-05-10 19:14:24 +01:00
|
|
|
{
|
|
|
|
|
if (fx_wchar_is_alnum(c)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (fx_wchar_is_space(c)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (c == '$') {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
if (char_can_begin_symbol_in_state(ctx, c, BSHELL_LEX_STATE_WORD)) {
|
2026-05-10 19:14:24 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
static enum bshell_status command_symbol(struct bshell_lex_ctx *ctx)
|
2026-05-10 14:18:46 +01:00
|
|
|
{
|
2026-05-25 10:33:29 +01:00
|
|
|
const struct bshell_lex_token_definition *sym = NULL;
|
2026-05-10 14:18:46 +01:00
|
|
|
enum bshell_status status = read_symbol(ctx, &sym);
|
|
|
|
|
|
|
|
|
|
if (status != BSHELL_SUCCESS) {
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-11 23:02:02 +01:00
|
|
|
handle_lex_state_transition(ctx, sym->id);
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_token *tok = NULL;
|
2026-05-10 14:18:46 +01:00
|
|
|
switch (sym->id) {
|
2026-05-25 10:33:29 +01:00
|
|
|
case BSHELL_SYM_DQUOTE:
|
2026-05-12 22:48:57 +01:00
|
|
|
return BSHELL_SUCCESS;
|
2026-05-25 10:33:29 +01:00
|
|
|
case BSHELL_SYM_SQUOTE:
|
2026-05-10 14:18:46 +01:00
|
|
|
status = read_literal_string(ctx, &tok);
|
|
|
|
|
if (status != BSHELL_SUCCESS) {
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
enqueue_token(ctx, tok);
|
|
|
|
|
return BSHELL_SUCCESS;
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
case BSHELL_SYM_HASH:
|
2026-05-10 14:18:46 +01:00
|
|
|
return read_line_comment(ctx);
|
2026-05-25 10:33:29 +01:00
|
|
|
case BSHELL_SYM_DOLLAR:
|
|
|
|
|
status = read_var(ctx, BSHELL_TOK_VAR, &tok);
|
2026-05-10 14:18:46 +01:00
|
|
|
if (status != BSHELL_SUCCESS) {
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-10 19:14:24 +01:00
|
|
|
if (char_can_continue_word(ctx, peek_char(ctx))) {
|
2026-05-25 10:33:29 +01:00
|
|
|
lex_state_push(ctx, BSHELL_LEX_STATE_WORD, 0);
|
2026-05-10 19:14:24 +01:00
|
|
|
}
|
|
|
|
|
|
2026-05-10 14:18:46 +01:00
|
|
|
enqueue_token(ctx, tok);
|
|
|
|
|
return status;
|
2026-05-25 10:33:29 +01:00
|
|
|
case BSHELL_SYM_AT:
|
|
|
|
|
status = read_var(ctx, BSHELL_TOK_VAR_SPLAT, &tok);
|
2026-05-10 14:18:46 +01:00
|
|
|
if (status != BSHELL_SUCCESS) {
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enqueue_token(ctx, tok);
|
|
|
|
|
return status;
|
2026-05-25 10:33:29 +01:00
|
|
|
case BSHELL_SYM_DOLLAR_LEFT_BRACE:
|
|
|
|
|
status = read_braced_var(ctx, BSHELL_TOK_VAR, &tok);
|
2026-05-10 14:18:46 +01:00
|
|
|
if (status != BSHELL_SUCCESS) {
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-10 19:14:24 +01:00
|
|
|
if (char_can_continue_word(ctx, peek_char(ctx))) {
|
2026-05-25 10:33:29 +01:00
|
|
|
lex_state_push(ctx, BSHELL_LEX_STATE_WORD, 0);
|
2026-05-10 19:14:24 +01:00
|
|
|
}
|
|
|
|
|
|
2026-05-10 14:18:46 +01:00
|
|
|
enqueue_token(ctx, tok);
|
|
|
|
|
return status;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
push_symbol(ctx, sym->id);
|
|
|
|
|
|
|
|
|
|
return BSHELL_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-12 22:48:57 +01:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
static enum bshell_status command_word(struct bshell_lex_ctx *ctx)
|
2026-05-10 14:18:46 +01:00
|
|
|
{
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_token *word = NULL;
|
2026-05-10 19:14:24 +01:00
|
|
|
enum bshell_status status
|
|
|
|
|
= read_word(ctx, READ_NO_NUMBER_RECOGNITION, &word);
|
2026-05-10 14:18:46 +01:00
|
|
|
if (status != BSHELL_SUCCESS) {
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-10 19:14:24 +01:00
|
|
|
bool continue_word = false;
|
|
|
|
|
|
|
|
|
|
fx_wchar c = peek_char(ctx);
|
2026-05-12 22:48:57 +01:00
|
|
|
const char *s = word->tok_str;
|
2026-05-25 10:33:29 +01:00
|
|
|
if (char_can_begin_symbol_in_state(ctx, c, BSHELL_LEX_STATE_WORD)) {
|
2026-05-10 19:14:24 +01:00
|
|
|
continue_word = true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
if (char_has_flags(ctx, c, BSHELL_LEX_TOKEN_TERMINATES_WORD)) {
|
2026-05-10 19:14:24 +01:00
|
|
|
continue_word = false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-12 22:48:57 +01:00
|
|
|
if (string_is_redirection(s)) {
|
|
|
|
|
continue_word = false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-10 19:14:24 +01:00
|
|
|
if (continue_word) {
|
2026-05-25 10:33:29 +01:00
|
|
|
lex_state_push(ctx, BSHELL_LEX_STATE_WORD, 0);
|
2026-05-10 19:14:24 +01:00
|
|
|
}
|
|
|
|
|
|
2026-05-10 14:18:46 +01:00
|
|
|
enqueue_token(ctx, word);
|
|
|
|
|
return BSHELL_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
enum bshell_status command_pump_token(struct bshell_lex_ctx *ctx)
|
2026-05-10 14:18:46 +01:00
|
|
|
{
|
|
|
|
|
fx_wchar c = peek_char(ctx);
|
|
|
|
|
bool newline = false;
|
|
|
|
|
|
2026-05-10 19:13:29 +01:00
|
|
|
set_token_start(ctx);
|
2026-05-10 14:18:46 +01:00
|
|
|
while (fx_wchar_is_space(c)) {
|
|
|
|
|
if (c == '\n') {
|
|
|
|
|
newline = true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-10 19:13:29 +01:00
|
|
|
set_token_end(ctx);
|
2026-05-10 14:18:46 +01:00
|
|
|
advance_char_noread(ctx);
|
|
|
|
|
c = peek_char_noread(ctx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (newline) {
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_token *tok
|
|
|
|
|
= bshell_lex_token_create(BSHELL_TOK_LINEFEED);
|
2026-05-10 14:18:46 +01:00
|
|
|
enqueue_token(ctx, tok);
|
2026-05-25 10:33:29 +01:00
|
|
|
handle_lex_state_transition(ctx, BSHELL_TOK_LINEFEED);
|
2026-05-10 14:18:46 +01:00
|
|
|
return BSHELL_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (char_can_begin_symbol(ctx, c)) {
|
|
|
|
|
return command_symbol(ctx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return command_word(ctx);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
const struct bshell_lex_state_link links[] = {
|
|
|
|
|
LINK_PUSH(BSHELL_SYM_DQUOTE, BSHELL_LEX_STATE_STRING, 0),
|
2026-06-20 15:06:04 +01:00
|
|
|
LINK_PUSH_WITH_TERM(
|
2026-05-25 10:33:29 +01:00
|
|
|
BSHELL_SYM_LEFT_PAREN,
|
|
|
|
|
BSHELL_LEX_STATE_STATEMENT,
|
2026-06-20 15:06:04 +01:00
|
|
|
STATEMENT_F_DISABLE_KEYWORDS,
|
|
|
|
|
BSHELL_SYM_RIGHT_PAREN),
|
2026-05-25 10:33:29 +01:00
|
|
|
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),
|
2026-05-11 23:02:02 +01:00
|
|
|
LINK_END,
|
|
|
|
|
};
|
|
|
|
|
|
2026-05-11 23:57:35 +01:00
|
|
|
static const unsigned int symbols[] = {
|
2026-05-25 10:33:29 +01:00
|
|
|
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,
|
2026-05-11 23:57:35 +01:00
|
|
|
};
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
const struct bshell_lex_state_type lex_command_state = {
|
|
|
|
|
.s_id = BSHELL_LEX_STATE_COMMAND,
|
2026-05-10 14:18:46 +01:00
|
|
|
.s_pump_token = command_pump_token,
|
2026-05-11 23:02:02 +01:00
|
|
|
.s_links = links,
|
2026-05-11 23:57:35 +01:00
|
|
|
.s_symbols = symbols,
|
2026-05-10 14:18:46 +01:00
|
|
|
};
|