2026-05-10 14:18:46 +01:00
|
|
|
#include "lex-internal.h"
|
2026-05-09 19:00:02 +01:00
|
|
|
|
2026-05-11 23:02:02 +01:00
|
|
|
#include <assert.h>
|
2026-05-25 10:33:29 +01:00
|
|
|
#include <bshell/line-source.h>
|
|
|
|
|
#include <bshell/parse/lex.h>
|
|
|
|
|
#include <bshell/parse/token.h>
|
2026-05-11 23:02:02 +01:00
|
|
|
|
2026-05-11 23:57:35 +01:00
|
|
|
#define SYMBOL_DEF(i, n, f) \
|
2026-05-25 10:33:29 +01:00
|
|
|
[i - __BSHELL_SYM_INDEX_BASE] = { \
|
2026-05-11 23:57:35 +01:00
|
|
|
.id = (i), \
|
|
|
|
|
.name = (n), \
|
|
|
|
|
.flags = (f), \
|
|
|
|
|
}
|
2026-05-25 10:33:29 +01:00
|
|
|
#define BSHELL_KW_DEF(i, n, f) \
|
|
|
|
|
[i - __BSHELL_KW_INDEX_BASE] = { \
|
2026-05-11 23:57:35 +01:00
|
|
|
.id = (i), \
|
|
|
|
|
.name = (n), \
|
|
|
|
|
.flags = (f), \
|
|
|
|
|
}
|
2026-05-25 10:33:29 +01:00
|
|
|
#define BSHELL_TKOP_DEF(i, n, f) \
|
|
|
|
|
[i - __BSHELL_TKOP_INDEX_BASE] = { \
|
2026-05-11 23:57:35 +01:00
|
|
|
.id = (i), \
|
|
|
|
|
.name = (n), \
|
|
|
|
|
.flags = (f), \
|
|
|
|
|
}
|
2026-05-09 19:00:02 +01:00
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
static struct bshell_lex_token_definition keywords[] = {
|
|
|
|
|
BSHELL_KW_DEF(BSHELL_KW_FUNC, "func", BSHELL_LEX_TOKEN_COMMAND_MODE),
|
|
|
|
|
BSHELL_KW_DEF(BSHELL_KW_IF, "if", 0),
|
|
|
|
|
BSHELL_KW_DEF(BSHELL_KW_ELSEIF, "elseif", 0),
|
|
|
|
|
BSHELL_KW_DEF(BSHELL_KW_ELSE, "else", 0),
|
2026-05-09 19:00:02 +01:00
|
|
|
};
|
|
|
|
|
static const size_t nr_keywords = sizeof keywords / sizeof keywords[0];
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
static struct bshell_lex_token_definition operators[] = {
|
|
|
|
|
BSHELL_TKOP_DEF(BSHELL_TKOP_BAND, "-band", 0),
|
|
|
|
|
BSHELL_TKOP_DEF(BSHELL_TKOP_BOR, "-bor", 0),
|
|
|
|
|
BSHELL_TKOP_DEF(BSHELL_TKOP_BXOR, "-bxor", 0),
|
|
|
|
|
BSHELL_TKOP_DEF(BSHELL_TKOP_BNOT, "-bnot", 0),
|
|
|
|
|
BSHELL_TKOP_DEF(BSHELL_TKOP_SHL, "-shl", 0),
|
|
|
|
|
BSHELL_TKOP_DEF(BSHELL_TKOP_SHR, "-shr", 0),
|
|
|
|
|
BSHELL_TKOP_DEF(BSHELL_TKOP_EQ, "-eq", 0),
|
|
|
|
|
BSHELL_TKOP_DEF(BSHELL_TKOP_NE, "-ne", 0),
|
|
|
|
|
BSHELL_TKOP_DEF(BSHELL_TKOP_GT, "-gt", 0),
|
|
|
|
|
BSHELL_TKOP_DEF(BSHELL_TKOP_LT, "-lt", 0),
|
|
|
|
|
BSHELL_TKOP_DEF(BSHELL_TKOP_GE, "-ge", 0),
|
|
|
|
|
BSHELL_TKOP_DEF(BSHELL_TKOP_LE, "-le", 0),
|
|
|
|
|
BSHELL_TKOP_DEF(BSHELL_TKOP_MATCH, "-match", 0),
|
|
|
|
|
BSHELL_TKOP_DEF(BSHELL_TKOP_NOTMATCH, "-notmatch", 0),
|
|
|
|
|
BSHELL_TKOP_DEF(BSHELL_TKOP_REPLACE, "-replace", 0),
|
|
|
|
|
BSHELL_TKOP_DEF(BSHELL_TKOP_LIKE, "-like", 0),
|
|
|
|
|
BSHELL_TKOP_DEF(BSHELL_TKOP_NOTLIKE, "-notlike", 0),
|
|
|
|
|
BSHELL_TKOP_DEF(BSHELL_TKOP_CONTAINS, "-contains", 0),
|
|
|
|
|
BSHELL_TKOP_DEF(BSHELL_TKOP_NOTCONTAINS, "-notcontains", 0),
|
|
|
|
|
BSHELL_TKOP_DEF(BSHELL_TKOP_AND, "-and", 0),
|
|
|
|
|
BSHELL_TKOP_DEF(BSHELL_TKOP_OR, "-or", 0),
|
|
|
|
|
BSHELL_TKOP_DEF(BSHELL_TKOP_XOR, "-xor", 0),
|
|
|
|
|
BSHELL_TKOP_DEF(BSHELL_TKOP_NOT, "-not", 0),
|
|
|
|
|
BSHELL_TKOP_DEF(BSHELL_TKOP_SPLIT, "-split", 0),
|
|
|
|
|
BSHELL_TKOP_DEF(BSHELL_TKOP_JOIN, "-join", 0),
|
|
|
|
|
BSHELL_TKOP_DEF(BSHELL_TKOP_IS, "-is", 0),
|
|
|
|
|
BSHELL_TKOP_DEF(BSHELL_TKOP_ISNOT, "-isnot", 0),
|
|
|
|
|
BSHELL_TKOP_DEF(BSHELL_TKOP_AS, "-as", 0),
|
|
|
|
|
BSHELL_TKOP_DEF(BSHELL_TKOP_F, "-f", 0),
|
2026-05-10 19:14:24 +01:00
|
|
|
};
|
|
|
|
|
static const size_t nr_operators = sizeof operators / sizeof operators[0];
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
static struct bshell_lex_token_definition symbols[] = {
|
|
|
|
|
SYMBOL_DEF(BSHELL_SYM_BANG, "!", BSHELL_LEX_TOKEN_UNARY_ARITHMETIC),
|
|
|
|
|
SYMBOL_DEF(BSHELL_SYM_PLUS, "+", BSHELL_LEX_TOKEN_UNARY_ARITHMETIC),
|
|
|
|
|
SYMBOL_DEF(BSHELL_SYM_HYPHEN, "-", BSHELL_LEX_TOKEN_UNARY_ARITHMETIC),
|
|
|
|
|
SYMBOL_DEF(BSHELL_SYM_FORWARD_SLASH, "/", 0),
|
|
|
|
|
SYMBOL_DEF(BSHELL_SYM_ASTERISK, "*", 0),
|
|
|
|
|
SYMBOL_DEF(BSHELL_SYM_AMPERSAND, "&", 0),
|
|
|
|
|
SYMBOL_DEF(BSHELL_SYM_PERCENT, "%", 0),
|
|
|
|
|
SYMBOL_DEF(BSHELL_SYM_SQUOTE, "'", 0),
|
|
|
|
|
SYMBOL_DEF(BSHELL_SYM_DQUOTE, "\"", 0),
|
|
|
|
|
SYMBOL_DEF(BSHELL_SYM_HASH, "#", 0),
|
|
|
|
|
SYMBOL_DEF(BSHELL_SYM_DOLLAR, "$", BSHELL_LEX_TOKEN_UNARY_ARITHMETIC),
|
|
|
|
|
SYMBOL_DEF(
|
|
|
|
|
BSHELL_SYM_DOLLAR_LEFT_PAREN,
|
|
|
|
|
"$(",
|
|
|
|
|
BSHELL_LEX_TOKEN_UNARY_ARITHMETIC),
|
|
|
|
|
SYMBOL_DEF(
|
|
|
|
|
BSHELL_SYM_DOLLAR_LEFT_BRACE,
|
|
|
|
|
"${",
|
|
|
|
|
BSHELL_LEX_TOKEN_UNARY_ARITHMETIC),
|
|
|
|
|
SYMBOL_DEF(BSHELL_SYM_AT, "@", 0),
|
|
|
|
|
SYMBOL_DEF(BSHELL_SYM_PIPE, "|", BSHELL_LEX_TOKEN_TERMINATES_WORD),
|
|
|
|
|
SYMBOL_DEF(BSHELL_SYM_COMMA, ",", BSHELL_LEX_TOKEN_TERMINATES_WORD),
|
|
|
|
|
SYMBOL_DEF(BSHELL_SYM_SEMICOLON, ";", BSHELL_LEX_TOKEN_TERMINATES_WORD),
|
|
|
|
|
SYMBOL_DEF(
|
|
|
|
|
BSHELL_SYM_AT_LEFT_BRACE,
|
|
|
|
|
"@{",
|
|
|
|
|
BSHELL_LEX_TOKEN_UNARY_ARITHMETIC),
|
|
|
|
|
SYMBOL_DEF(BSHELL_SYM_AT_LEFT_PAREN, "@(", 0),
|
|
|
|
|
SYMBOL_DEF(
|
|
|
|
|
BSHELL_SYM_LEFT_BRACE,
|
|
|
|
|
"{",
|
|
|
|
|
BSHELL_LEX_TOKEN_TERMINATES_WORD),
|
|
|
|
|
SYMBOL_DEF(
|
|
|
|
|
BSHELL_SYM_RIGHT_BRACE,
|
|
|
|
|
"}",
|
|
|
|
|
BSHELL_LEX_TOKEN_TERMINATES_WORD),
|
|
|
|
|
SYMBOL_DEF(BSHELL_SYM_LEFT_BRACKET, "[", 0),
|
|
|
|
|
SYMBOL_DEF(BSHELL_SYM_RIGHT_BRACKET, "]", 0),
|
|
|
|
|
SYMBOL_DEF(BSHELL_SYM_QUESTION_LEFT_BRACKET, "?[", 0),
|
|
|
|
|
SYMBOL_DEF(
|
|
|
|
|
BSHELL_SYM_LEFT_PAREN,
|
|
|
|
|
"(",
|
|
|
|
|
BSHELL_LEX_TOKEN_TERMINATES_WORD),
|
|
|
|
|
SYMBOL_DEF(
|
|
|
|
|
BSHELL_SYM_RIGHT_PAREN,
|
|
|
|
|
")",
|
|
|
|
|
BSHELL_LEX_TOKEN_TERMINATES_WORD),
|
|
|
|
|
SYMBOL_DEF(BSHELL_SYM_EQUAL, "=", 0),
|
|
|
|
|
SYMBOL_DEF(BSHELL_SYM_PLUS_EQUAL, "+=", 0),
|
|
|
|
|
SYMBOL_DEF(BSHELL_SYM_HYPHEN_EQUAL, "-=", 0),
|
|
|
|
|
SYMBOL_DEF(BSHELL_SYM_FORWARD_SLASH_EQUAL, "/=", 0),
|
|
|
|
|
SYMBOL_DEF(BSHELL_SYM_ASTERISK_EQUAL, "*=", 0),
|
|
|
|
|
SYMBOL_DEF(BSHELL_SYM_PERCENT_EQUAL, "%=", 0),
|
|
|
|
|
SYMBOL_DEF(BSHELL_SYM_DOT, ".", 0),
|
|
|
|
|
SYMBOL_DEF(BSHELL_SYM_COLON_COLON, "::", 0),
|
|
|
|
|
SYMBOL_DEF(BSHELL_SYM_DOT_DOT, "..", 0),
|
|
|
|
|
SYMBOL_DEF(BSHELL_SYM_QUESTION_DOT, "?.", 0),
|
2026-05-09 19:00:02 +01:00
|
|
|
};
|
|
|
|
|
static const size_t nr_symbols = sizeof symbols / sizeof symbols[0];
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
extern const struct bshell_lex_state_type lex_statement_state;
|
|
|
|
|
extern const struct bshell_lex_state_type lex_command_state;
|
|
|
|
|
extern const struct bshell_lex_state_type lex_arithmetic_state;
|
|
|
|
|
extern const struct bshell_lex_state_type lex_string_state;
|
|
|
|
|
extern const struct bshell_lex_state_type lex_word_state;
|
|
|
|
|
extern const struct bshell_lex_state_type lex_hashtable_state;
|
|
|
|
|
|
|
|
|
|
static const struct bshell_lex_state_type *state_types[] = {
|
|
|
|
|
[BSHELL_LEX_STATE_STATEMENT] = &lex_statement_state,
|
|
|
|
|
[BSHELL_LEX_STATE_COMMAND] = &lex_command_state,
|
|
|
|
|
[BSHELL_LEX_STATE_ARITHMETIC] = &lex_arithmetic_state,
|
|
|
|
|
[BSHELL_LEX_STATE_STRING] = &lex_string_state,
|
|
|
|
|
[BSHELL_LEX_STATE_WORD] = &lex_word_state,
|
|
|
|
|
[BSHELL_LEX_STATE_HASHTABLE] = &lex_hashtable_state,
|
2026-05-09 19:00:02 +01:00
|
|
|
};
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
void set_token_start(struct bshell_lex_ctx *ctx)
|
2026-05-10 19:13:29 +01:00
|
|
|
{
|
|
|
|
|
memcpy(&ctx->lex_start, &ctx->lex_cursor, sizeof ctx->lex_cursor);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
void set_token_end(struct bshell_lex_ctx *ctx)
|
2026-05-10 19:13:29 +01:00
|
|
|
{
|
|
|
|
|
memcpy(&ctx->lex_end, &ctx->lex_cursor, sizeof ctx->lex_cursor);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
static const char *lex_state_type_id_to_string(enum bshell_lex_state_id id)
|
2026-05-11 23:02:02 +01:00
|
|
|
{
|
|
|
|
|
#define ENUM_STR(v) \
|
|
|
|
|
case v: \
|
|
|
|
|
return #v
|
|
|
|
|
switch (id) {
|
2026-05-25 10:33:29 +01:00
|
|
|
ENUM_STR(BSHELL_LEX_STATE_STATEMENT);
|
|
|
|
|
ENUM_STR(BSHELL_LEX_STATE_COMMAND);
|
|
|
|
|
ENUM_STR(BSHELL_LEX_STATE_ARITHMETIC);
|
|
|
|
|
ENUM_STR(BSHELL_LEX_STATE_STRING);
|
|
|
|
|
ENUM_STR(BSHELL_LEX_STATE_WORD);
|
|
|
|
|
ENUM_STR(BSHELL_LEX_STATE_HASHTABLE);
|
2026-05-11 23:02:02 +01:00
|
|
|
default:
|
|
|
|
|
return "<unknown>";
|
|
|
|
|
}
|
|
|
|
|
#undef ENUM_STR
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_state *lex_state_push(
|
|
|
|
|
struct bshell_lex_ctx *ctx,
|
|
|
|
|
enum bshell_lex_state_id state_type,
|
2026-05-10 19:10:14 +01:00
|
|
|
enum state_flags flags)
|
2026-05-09 19:00:02 +01:00
|
|
|
{
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_state *state = malloc(sizeof *state);
|
2026-05-09 19:00:02 +01:00
|
|
|
if (!state) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-11 23:02:02 +01:00
|
|
|
#if defined(VERBOSE)
|
|
|
|
|
printf("push(%s, 0x%04x)\n",
|
|
|
|
|
lex_state_type_id_to_string(state_type),
|
|
|
|
|
flags);
|
|
|
|
|
#endif
|
2026-05-09 19:00:02 +01:00
|
|
|
memset(state, 0x0, sizeof *state);
|
|
|
|
|
|
2026-05-10 14:18:46 +01:00
|
|
|
state->s_type = state_types[state_type];
|
2026-05-10 19:10:14 +01:00
|
|
|
state->s_flags = flags;
|
2026-05-09 19:00:02 +01:00
|
|
|
fx_queue_push_back(&ctx->lex_state, &state->s_entry);
|
|
|
|
|
|
2026-05-10 14:18:46 +01:00
|
|
|
if (state->s_type->s_begin) {
|
|
|
|
|
state->s_type->s_begin(ctx);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-09 19:00:02 +01:00
|
|
|
return state;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
void lex_state_pop(struct bshell_lex_ctx *ctx)
|
2026-05-09 19:00:02 +01:00
|
|
|
{
|
2026-05-10 14:18:46 +01:00
|
|
|
fx_queue_entry *entry = fx_queue_last(&ctx->lex_state);
|
|
|
|
|
if (!entry || !fx_queue_prev(entry)) {
|
|
|
|
|
/* don't pop if this is the root state */
|
2026-05-09 19:00:02 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_state *state
|
|
|
|
|
= fx_unbox(struct bshell_lex_state, entry, s_entry);
|
2026-05-10 14:18:46 +01:00
|
|
|
|
2026-05-11 23:02:02 +01:00
|
|
|
#if defined(VERBOSE)
|
2026-05-12 22:51:45 +01:00
|
|
|
printf("pop(%s) -> %s\n",
|
|
|
|
|
lex_state_type_id_to_string(state->s_type->s_id),
|
|
|
|
|
lex_state_type_id_to_string(lex_state_get(ctx)->s_type->s_id));
|
2026-05-11 23:02:02 +01:00
|
|
|
#endif
|
|
|
|
|
|
2026-05-10 14:18:46 +01:00
|
|
|
if (state->s_type->s_end) {
|
|
|
|
|
state->s_type->s_end(ctx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fx_queue_pop_back(&ctx->lex_state);
|
|
|
|
|
|
|
|
|
|
if (state->s_tempstr) {
|
|
|
|
|
fx_string_unref(state->s_tempstr);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-09 19:00:02 +01:00
|
|
|
free(state);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_state *lex_state_get(struct bshell_lex_ctx *ctx)
|
2026-05-09 19:00:02 +01:00
|
|
|
{
|
|
|
|
|
fx_queue_entry *entry = fx_queue_last(&ctx->lex_state);
|
|
|
|
|
if (!entry) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
return fx_unbox(struct bshell_lex_state, entry, s_entry);
|
2026-05-09 19:00:02 +01:00
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
void lex_state_change(struct bshell_lex_ctx *ctx, enum bshell_lex_state_id type)
|
2026-05-10 14:18:46 +01:00
|
|
|
{
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_state *state = lex_state_get(ctx);
|
2026-05-10 14:18:46 +01:00
|
|
|
if (!state) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-11 23:02:02 +01:00
|
|
|
#if defined(VERBOSE)
|
|
|
|
|
printf("change(%s -> %s)\n",
|
|
|
|
|
lex_state_type_id_to_string(state->s_type->s_id),
|
|
|
|
|
lex_state_type_id_to_string(type));
|
|
|
|
|
#endif
|
|
|
|
|
|
2026-05-10 14:18:46 +01:00
|
|
|
if (state->s_type->s_end) {
|
|
|
|
|
state->s_type->s_end(ctx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
state->s_type = state_types[type];
|
|
|
|
|
|
|
|
|
|
if (state->s_type->s_begin) {
|
|
|
|
|
state->s_type->s_begin(ctx);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
fx_string *lex_state_get_tempstr(struct bshell_lex_ctx *ctx)
|
2026-05-10 14:18:46 +01:00
|
|
|
{
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_state *state = lex_state_get(ctx);
|
2026-05-10 14:18:46 +01:00
|
|
|
if (!state) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!state->s_tempstr) {
|
|
|
|
|
state->s_tempstr = fx_string_create();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!state->s_tempstr) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return state->s_tempstr;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
void lex_state_add_terminator(struct bshell_lex_state *state, unsigned int tok)
|
2026-05-11 23:02:02 +01:00
|
|
|
{
|
2026-05-25 10:33:29 +01:00
|
|
|
if (state->s_nr_terminators < BSHELL_LEX_STATE_MAX_TERMINATORS) {
|
2026-05-11 23:02:02 +01:00
|
|
|
state->s_terminators[state->s_nr_terminators++] = tok;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
static struct bshell_lex_symbol_node *get_symbol_node(
|
|
|
|
|
struct bshell_lex_symbol_node *node,
|
2026-05-09 19:00:02 +01:00
|
|
|
char c)
|
|
|
|
|
{
|
|
|
|
|
fx_queue_entry *entry = fx_queue_first(&node->s_children);
|
|
|
|
|
while (entry) {
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_symbol_node *child = fx_unbox(
|
|
|
|
|
struct bshell_lex_symbol_node,
|
2026-05-17 15:29:14 +01:00
|
|
|
entry,
|
|
|
|
|
s_entry);
|
2026-05-09 19:00:02 +01:00
|
|
|
if (child->s_char == c) {
|
|
|
|
|
return child;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
entry = fx_queue_next(entry);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum bshell_status put_symbol(
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_symbol_node *tree,
|
|
|
|
|
struct bshell_lex_token_definition *sym)
|
2026-05-09 19:00:02 +01:00
|
|
|
{
|
|
|
|
|
for (size_t i = 0; sym->name[i]; i++) {
|
|
|
|
|
char c = sym->name[i];
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_symbol_node *child = get_symbol_node(tree, c);
|
2026-05-09 19:00:02 +01:00
|
|
|
if (child) {
|
|
|
|
|
tree = child;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
child = malloc(sizeof *child);
|
|
|
|
|
if (!child) {
|
|
|
|
|
return BSHELL_ERR_NO_MEMORY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memset(child, 0x0, sizeof *child);
|
|
|
|
|
|
|
|
|
|
child->s_def = NULL;
|
|
|
|
|
child->s_char = c;
|
|
|
|
|
|
|
|
|
|
fx_queue_push_back(&tree->s_children, &child->s_entry);
|
|
|
|
|
tree = child;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tree->s_def = sym;
|
|
|
|
|
return BSHELL_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
static void destroy_symbol_tree(struct bshell_lex_symbol_node *tree)
|
2026-05-09 19:00:02 +01:00
|
|
|
{
|
|
|
|
|
fx_queue_entry *entry = fx_queue_first(&tree->s_children);
|
|
|
|
|
while (entry) {
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_symbol_node *node = fx_unbox(
|
|
|
|
|
struct bshell_lex_symbol_node,
|
2026-05-17 15:29:14 +01:00
|
|
|
entry,
|
|
|
|
|
s_entry);
|
2026-05-09 19:00:02 +01:00
|
|
|
fx_queue_entry *next = fx_queue_next(entry);
|
|
|
|
|
fx_queue_delete(&tree->s_children, entry);
|
|
|
|
|
|
|
|
|
|
destroy_symbol_tree(node);
|
|
|
|
|
|
|
|
|
|
entry = next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(tree);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
static struct bshell_lex_symbol_node *build_symbol_tree(void)
|
2026-05-09 19:00:02 +01:00
|
|
|
{
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_symbol_node *root = malloc(sizeof *root);
|
2026-05-09 19:00:02 +01:00
|
|
|
if (!root) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memset(root, 0x0, sizeof *root);
|
|
|
|
|
root->s_def = NULL;
|
|
|
|
|
|
|
|
|
|
enum bshell_status status = BSHELL_SUCCESS;
|
|
|
|
|
for (size_t i = 0; i < nr_symbols; i++) {
|
2026-05-11 23:57:35 +01:00
|
|
|
if (!symbols[i].name) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-09 19:00:02 +01:00
|
|
|
status = put_symbol(root, &symbols[i]);
|
|
|
|
|
|
|
|
|
|
if (status != BSHELL_SUCCESS) {
|
|
|
|
|
destroy_symbol_tree(root);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return root;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
static void init_token_enabled_states(
|
|
|
|
|
const struct bshell_lex_state_type *state_type)
|
2026-05-11 23:57:35 +01:00
|
|
|
{
|
|
|
|
|
if (state_type->s_keywords) {
|
|
|
|
|
for (size_t i = 0; state_type->s_keywords[i]; i++) {
|
|
|
|
|
unsigned int id = state_type->s_keywords[i];
|
2026-05-25 10:33:29 +01:00
|
|
|
keywords[id - __BSHELL_KW_INDEX_BASE].enabled_states
|
2026-05-11 23:57:35 +01:00
|
|
|
|= state_type->s_id;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (state_type->s_operators) {
|
|
|
|
|
for (size_t i = 0; state_type->s_operators[i]; i++) {
|
|
|
|
|
unsigned int id = state_type->s_operators[i];
|
2026-05-25 10:33:29 +01:00
|
|
|
operators[id - __BSHELL_TKOP_INDEX_BASE].enabled_states
|
2026-05-11 23:57:35 +01:00
|
|
|
|= state_type->s_id;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (state_type->s_symbols) {
|
|
|
|
|
for (size_t i = 0; state_type->s_symbols[i]; i++) {
|
|
|
|
|
unsigned int id = state_type->s_symbols[i];
|
2026-05-25 10:33:29 +01:00
|
|
|
symbols[id - __BSHELL_SYM_INDEX_BASE].enabled_states
|
2026-05-11 23:57:35 +01:00
|
|
|
|= state_type->s_id;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
enum bshell_status bshell_lex_ctx_init(
|
|
|
|
|
struct bshell_lex_ctx *ctx,
|
|
|
|
|
enum bshell_lex_flags flags,
|
|
|
|
|
struct bshell_line_source *src)
|
2026-05-09 19:00:02 +01:00
|
|
|
{
|
|
|
|
|
memset(ctx, 0x0, sizeof *ctx);
|
|
|
|
|
|
|
|
|
|
ctx->lex_flags = flags;
|
|
|
|
|
ctx->lex_status = BSHELL_SUCCESS;
|
|
|
|
|
ctx->lex_buf = fx_stringstream_create();
|
|
|
|
|
ctx->lex_sym_tree = build_symbol_tree();
|
2026-05-25 10:33:29 +01:00
|
|
|
lex_state_push(ctx, BSHELL_LEX_STATE_STATEMENT, 0);
|
2026-05-09 19:00:02 +01:00
|
|
|
ctx->lex_src = src;
|
|
|
|
|
ctx->lex_ch = FX_WCHAR_INVALID;
|
2026-05-10 19:13:29 +01:00
|
|
|
ctx->lex_cursor.c_row = ctx->lex_cursor.c_col = 1;
|
2026-05-09 19:00:02 +01:00
|
|
|
|
2026-05-11 23:57:35 +01:00
|
|
|
init_token_enabled_states(&lex_statement_state);
|
|
|
|
|
init_token_enabled_states(&lex_command_state);
|
|
|
|
|
init_token_enabled_states(&lex_arithmetic_state);
|
|
|
|
|
init_token_enabled_states(&lex_string_state);
|
|
|
|
|
init_token_enabled_states(&lex_word_state);
|
|
|
|
|
init_token_enabled_states(&lex_hashtable_state);
|
|
|
|
|
|
2026-05-09 19:00:02 +01:00
|
|
|
return BSHELL_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
enum bshell_status bshell_lex_ctx_cleanup(struct bshell_lex_ctx *ctx)
|
2026-05-09 19:00:02 +01:00
|
|
|
{
|
|
|
|
|
if (ctx->lex_sym_tree) {
|
|
|
|
|
destroy_symbol_tree(ctx->lex_sym_tree);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ctx->lex_buf) {
|
|
|
|
|
fx_stringstream_unref(ctx->lex_buf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ctx->lex_tmp) {
|
|
|
|
|
fx_string_unref(ctx->lex_tmp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memset(ctx, 0x0, sizeof *ctx);
|
|
|
|
|
return BSHELL_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
static enum bshell_status refill_buffer(struct bshell_lex_ctx *ctx)
|
2026-05-09 19:00:02 +01:00
|
|
|
{
|
|
|
|
|
fx_stringstream_reset(ctx->lex_buf);
|
|
|
|
|
ctx->lex_ch = FX_WCHAR_INVALID;
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
return bshell_line_source_readline(ctx->lex_src, ctx->lex_buf);
|
2026-05-09 19:00:02 +01:00
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
static fx_wchar __peek_char(struct bshell_lex_ctx *ctx, bool noread)
|
2026-05-09 19:00:02 +01:00
|
|
|
{
|
|
|
|
|
if (ctx->lex_status != BSHELL_SUCCESS) {
|
|
|
|
|
return FX_WCHAR_INVALID;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ctx->lex_ch != FX_WCHAR_INVALID) {
|
|
|
|
|
return ctx->lex_ch;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fx_status status = fx_stream_read_char(ctx->lex_buf, &ctx->lex_ch);
|
|
|
|
|
if (!FX_OK(status) && !noread) {
|
|
|
|
|
enum bshell_status status2 = refill_buffer(ctx);
|
|
|
|
|
if (status2 != BSHELL_SUCCESS) {
|
|
|
|
|
ctx->lex_status = status2;
|
|
|
|
|
ctx->lex_ch = FX_WCHAR_INVALID;
|
|
|
|
|
} else {
|
|
|
|
|
fx_stream_read_char(ctx->lex_buf, &ctx->lex_ch);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ctx->lex_ch;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
fx_wchar peek_char(struct bshell_lex_ctx *ctx)
|
2026-05-09 19:00:02 +01:00
|
|
|
{
|
|
|
|
|
return __peek_char(ctx, false);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
fx_wchar peek_char_noread(struct bshell_lex_ctx *ctx)
|
2026-05-09 19:00:02 +01:00
|
|
|
{
|
|
|
|
|
return __peek_char(ctx, true);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
static void __advance_char(struct bshell_lex_ctx *ctx, bool noread)
|
2026-05-09 19:00:02 +01:00
|
|
|
{
|
2026-05-10 19:13:29 +01:00
|
|
|
if (ctx->lex_status != BSHELL_SUCCESS) {
|
2026-05-09 19:00:02 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-10 19:13:29 +01:00
|
|
|
ctx->lex_cursor.c_col++;
|
|
|
|
|
if (ctx->lex_ch == '\n') {
|
|
|
|
|
ctx->lex_cursor.c_col = 1;
|
|
|
|
|
ctx->lex_cursor.c_row++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ctx->lex_ch != FX_WCHAR_INVALID) {
|
|
|
|
|
ctx->lex_ch = FX_WCHAR_INVALID;
|
2026-05-09 19:00:02 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fx_status status = fx_stream_read_char(ctx->lex_buf, &ctx->lex_ch);
|
|
|
|
|
if (!FX_OK(status) && !noread) {
|
|
|
|
|
enum bshell_status status2 = refill_buffer(ctx);
|
|
|
|
|
if (status2 != BSHELL_SUCCESS) {
|
|
|
|
|
ctx->lex_status = status2;
|
|
|
|
|
ctx->lex_ch = FX_WCHAR_INVALID;
|
|
|
|
|
} else {
|
|
|
|
|
fx_stream_read_char(ctx->lex_buf, &ctx->lex_ch);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
void advance_char(struct bshell_lex_ctx *ctx)
|
2026-05-09 19:00:02 +01:00
|
|
|
{
|
|
|
|
|
return __advance_char(ctx, false);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
void advance_char_noread(struct bshell_lex_ctx *ctx)
|
2026-05-09 19:00:02 +01:00
|
|
|
{
|
|
|
|
|
return __advance_char(ctx, true);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
bool convert_word_to_keyword(struct bshell_lex_token *tok)
|
2026-05-09 19:00:02 +01:00
|
|
|
{
|
2026-05-25 10:33:29 +01:00
|
|
|
if (!bshell_lex_token_has_string_value(tok)) {
|
2026-05-09 19:00:02 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < nr_keywords; i++) {
|
|
|
|
|
const char *kw_str = keywords[i].name;
|
2026-05-11 23:57:35 +01:00
|
|
|
if (!kw_str || strcmp(kw_str, tok->tok_str) != 0) {
|
2026-05-09 19:00:02 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
bshell_lex_token_change_type(tok, BSHELL_TOK_KEYWORD);
|
2026-05-10 14:18:46 +01:00
|
|
|
tok->tok_keyword = keywords[i].id;
|
2026-05-09 19:00:02 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
bool convert_word_to_operator(
|
|
|
|
|
struct bshell_lex_ctx *ctx,
|
|
|
|
|
struct bshell_lex_token *tok)
|
2026-05-10 19:14:24 +01:00
|
|
|
{
|
2026-05-25 10:33:29 +01:00
|
|
|
if (!bshell_lex_token_has_string_value(tok)) {
|
2026-05-10 19:14:24 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
enum bshell_lex_operator op
|
|
|
|
|
= get_bshell_operator_with_string(ctx, tok->tok_str);
|
|
|
|
|
if (op == BSHELL_TKOP_NONE) {
|
2026-05-10 19:14:24 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
bshell_lex_token_change_type(tok, BSHELL_TOK_OPERATOR);
|
2026-05-10 19:14:24 +01:00
|
|
|
tok->tok_operator = op;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-09 19:00:02 +01:00
|
|
|
static int get_int_base_by_prefix(const char **s)
|
|
|
|
|
{
|
|
|
|
|
#define CH(x) (tolower(value[x]))
|
|
|
|
|
const char *value = *s;
|
|
|
|
|
if (CH(0) != '0') {
|
|
|
|
|
return 10;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (CH(1)) {
|
|
|
|
|
case 'x':
|
|
|
|
|
*s += 2;
|
|
|
|
|
return 16;
|
|
|
|
|
case 'b':
|
|
|
|
|
*s += 2;
|
|
|
|
|
return 2;
|
|
|
|
|
default:
|
|
|
|
|
*s += 1;
|
|
|
|
|
return 8;
|
|
|
|
|
}
|
|
|
|
|
#undef CH
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static size_t get_int_multiplier_by_suffix(const char *suffix)
|
|
|
|
|
{
|
|
|
|
|
#define CH(x) (tolower(suffix[x]))
|
|
|
|
|
if (CH(1) != 'b' || CH(2) != 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (CH(0)) {
|
|
|
|
|
case 'k':
|
|
|
|
|
return 0x400;
|
|
|
|
|
case 'm':
|
|
|
|
|
return 0x100000;
|
|
|
|
|
case 'g':
|
|
|
|
|
return 0x40000000;
|
|
|
|
|
case 't':
|
|
|
|
|
return 0x10000000000;
|
|
|
|
|
case 'b':
|
|
|
|
|
return 0x4000000000000;
|
|
|
|
|
default:
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#undef CH
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-24 20:13:55 +01:00
|
|
|
static bool string_could_be_double(const char *s)
|
|
|
|
|
{
|
|
|
|
|
if (strchr(s, '.')) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (strchr(s, 'e') || strchr(s, 'E')) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool string_is_valid_number(
|
|
|
|
|
const char *s,
|
|
|
|
|
fx_value *out,
|
2026-05-25 10:33:29 +01:00
|
|
|
enum bshell_lex_token_type *out_type)
|
2026-05-09 19:00:02 +01:00
|
|
|
{
|
2026-05-10 19:14:24 +01:00
|
|
|
if (s[0] == '\0') {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-24 20:13:55 +01:00
|
|
|
char *ep = NULL;
|
|
|
|
|
double dvalue = strtod(s, &ep);
|
|
|
|
|
if (string_could_be_double(s) && *ep == '\0') {
|
|
|
|
|
if (out) {
|
|
|
|
|
*out = FX_DOUBLE(dvalue);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (out_type) {
|
2026-05-25 10:33:29 +01:00
|
|
|
*out_type = BSHELL_TOK_DOUBLE;
|
2026-05-24 20:13:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-09 19:00:02 +01:00
|
|
|
int base = get_int_base_by_prefix(&s);
|
|
|
|
|
|
2026-05-24 20:13:55 +01:00
|
|
|
long long ivalue = strtoll(s, &ep, base);
|
2026-05-09 19:00:02 +01:00
|
|
|
if (*ep == '\0') {
|
2026-05-24 20:13:55 +01:00
|
|
|
if (out) {
|
|
|
|
|
*out = FX_LONGLONG(ivalue);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (out_type) {
|
2026-05-25 10:33:29 +01:00
|
|
|
*out_type = BSHELL_TOK_INT;
|
2026-05-24 20:13:55 +01:00
|
|
|
}
|
|
|
|
|
|
2026-05-09 19:00:02 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t multiplier = get_int_multiplier_by_suffix(ep);
|
|
|
|
|
if (multiplier != 0) {
|
2026-05-24 20:13:55 +01:00
|
|
|
if (out) {
|
|
|
|
|
*out = FX_LONGLONG(ivalue * multiplier);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (out_type) {
|
2026-05-25 10:33:29 +01:00
|
|
|
*out_type = BSHELL_TOK_INT;
|
2026-05-24 20:13:55 +01:00
|
|
|
}
|
|
|
|
|
|
2026-05-09 19:00:02 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
bool convert_word_to_number(struct bshell_lex_token *tok)
|
2026-05-09 19:00:02 +01:00
|
|
|
{
|
2026-05-25 10:33:29 +01:00
|
|
|
if (!bshell_lex_token_has_string_value(tok)) {
|
2026-05-09 19:00:02 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *s = tok->tok_str;
|
2026-05-24 20:13:55 +01:00
|
|
|
fx_value value;
|
2026-05-25 10:33:29 +01:00
|
|
|
enum bshell_lex_token_type type;
|
2026-05-24 20:13:55 +01:00
|
|
|
if (!string_is_valid_number(s, &value, &type)) {
|
2026-05-10 14:18:46 +01:00
|
|
|
return false;
|
2026-05-09 19:00:02 +01:00
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
bshell_lex_token_change_type(tok, type);
|
2026-05-24 20:13:55 +01:00
|
|
|
tok->tok_number = value;
|
2026-05-10 14:18:46 +01:00
|
|
|
|
|
|
|
|
return true;
|
2026-05-09 19:00:02 +01:00
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
static struct bshell_lex_token *get_next_token(struct bshell_lex_ctx *ctx)
|
2026-05-09 19:00:02 +01:00
|
|
|
{
|
2026-05-09 21:21:51 +01:00
|
|
|
fx_queue_entry *entry = fx_queue_first(&ctx->lex_tokens);
|
2026-05-25 10:33:29 +01:00
|
|
|
return fx_unbox(struct bshell_lex_token, entry, tok_entry);
|
2026-05-09 19:00:02 +01:00
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
void enqueue_token(struct bshell_lex_ctx *ctx, struct bshell_lex_token *tok)
|
2026-05-09 19:00:02 +01:00
|
|
|
{
|
2026-05-10 19:13:29 +01:00
|
|
|
enqueue_token_with_coordinates(
|
|
|
|
|
ctx,
|
|
|
|
|
tok,
|
|
|
|
|
&ctx->lex_start,
|
|
|
|
|
&ctx->lex_end);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extern void enqueue_token_with_coordinates(
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_ctx *ctx,
|
|
|
|
|
struct bshell_lex_token *tok,
|
|
|
|
|
const struct bshell_char_cell *start,
|
|
|
|
|
const struct bshell_char_cell *end)
|
2026-05-10 19:13:29 +01:00
|
|
|
{
|
2026-05-25 10:33:29 +01:00
|
|
|
if (tok->tok_type == BSHELL_TOK_LINEFEED
|
|
|
|
|
&& ctx->lex_prev_token == BSHELL_TOK_LINEFEED) {
|
|
|
|
|
bshell_lex_token_destroy(tok);
|
2026-05-11 23:02:02 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-10 19:13:29 +01:00
|
|
|
tok->tok_start = *start;
|
|
|
|
|
tok->tok_end = *end;
|
2026-05-11 23:02:02 +01:00
|
|
|
ctx->lex_prev_token = tok->tok_type;
|
2026-05-10 19:13:29 +01:00
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
if (ctx->lex_token_scanned) {
|
|
|
|
|
ctx->lex_token_scanned(ctx, tok);
|
2026-05-09 21:21:51 +01:00
|
|
|
}
|
|
|
|
|
|
2026-05-09 19:00:02 +01:00
|
|
|
fx_queue_push_back(&ctx->lex_tokens, &tok->tok_entry);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
static struct bshell_lex_token *dequeue_next_token(struct bshell_lex_ctx *ctx)
|
2026-05-09 19:00:02 +01:00
|
|
|
{
|
|
|
|
|
fx_queue_entry *entry = fx_queue_pop_front(&ctx->lex_tokens);
|
2026-05-25 10:33:29 +01:00
|
|
|
return fx_unbox(struct bshell_lex_token, entry, tok_entry);
|
2026-05-09 21:21:51 +01:00
|
|
|
}
|
2026-05-09 19:00:02 +01:00
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
static fx_string *get_temp_string(struct bshell_lex_ctx *ctx)
|
2026-05-09 19:00:02 +01:00
|
|
|
{
|
|
|
|
|
if (!ctx->lex_tmp) {
|
|
|
|
|
ctx->lex_tmp = fx_string_create();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fx_string_clear(ctx->lex_tmp);
|
|
|
|
|
return ctx->lex_tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
enum bshell_status push_symbol(
|
|
|
|
|
struct bshell_lex_ctx *ctx,
|
|
|
|
|
enum bshell_lex_symbol sym)
|
2026-05-09 19:00:02 +01:00
|
|
|
{
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_token *tok
|
|
|
|
|
= bshell_lex_token_create(BSHELL_TOK_SYMBOL);
|
2026-05-09 19:00:02 +01:00
|
|
|
if (!tok) {
|
|
|
|
|
return BSHELL_ERR_NO_MEMORY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tok->tok_symbol = sym;
|
|
|
|
|
enqueue_token(ctx, tok);
|
|
|
|
|
return BSHELL_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-10 14:18:46 +01:00
|
|
|
enum bshell_status read_var(
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_ctx *ctx,
|
|
|
|
|
enum bshell_lex_token_type type,
|
|
|
|
|
struct bshell_lex_token **out)
|
2026-05-09 19:00:02 +01:00
|
|
|
{
|
|
|
|
|
fx_string *tmp = get_temp_string(ctx);
|
|
|
|
|
|
|
|
|
|
bool done = false;
|
|
|
|
|
while (!done) {
|
|
|
|
|
fx_wchar c = peek_char(ctx);
|
|
|
|
|
if (c == FX_WCHAR_INVALID) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool valid = fx_wchar_is_alnum(c) || (c == '_') || (c == ':');
|
|
|
|
|
|
|
|
|
|
if (!valid) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (done) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fx_string_append_wc(tmp, c);
|
2026-05-10 19:13:29 +01:00
|
|
|
set_token_end(ctx);
|
2026-05-09 19:00:02 +01:00
|
|
|
advance_char(ctx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (fx_string_get_size(tmp, FX_STRLEN_NORMAL) == 0) {
|
|
|
|
|
return ctx->lex_status;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_token *tok = bshell_lex_token_create_with_string(
|
2026-05-17 15:29:14 +01:00
|
|
|
type,
|
|
|
|
|
fx_string_get_cstr(tmp));
|
2026-05-09 19:00:02 +01:00
|
|
|
|
2026-05-10 14:18:46 +01:00
|
|
|
*out = tok;
|
2026-05-09 19:00:02 +01:00
|
|
|
return BSHELL_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-10 14:18:46 +01:00
|
|
|
enum bshell_status read_braced_var(
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_ctx *ctx,
|
|
|
|
|
enum bshell_lex_token_type type,
|
|
|
|
|
struct bshell_lex_token **out)
|
2026-05-09 19:00:02 +01:00
|
|
|
{
|
|
|
|
|
fx_string *tmp = get_temp_string(ctx);
|
|
|
|
|
bool ok = false;
|
|
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
|
fx_wchar c = peek_char(ctx);
|
|
|
|
|
if (c == FX_WCHAR_INVALID) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (c == '}') {
|
|
|
|
|
ok = true;
|
|
|
|
|
advance_char(ctx);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fx_string_append_wc(tmp, c);
|
2026-05-10 19:13:29 +01:00
|
|
|
set_token_end(ctx);
|
2026-05-09 19:00:02 +01:00
|
|
|
advance_char(ctx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!ok) {
|
|
|
|
|
return BSHELL_ERR_BAD_SYNTAX;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (fx_string_get_size(tmp, FX_STRLEN_NORMAL) == 0) {
|
|
|
|
|
return ctx->lex_status;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_token *tok = bshell_lex_token_create_with_string(
|
2026-05-17 15:29:14 +01:00
|
|
|
type,
|
|
|
|
|
fx_string_get_cstr(tmp));
|
2026-05-09 19:00:02 +01:00
|
|
|
|
2026-05-10 14:18:46 +01:00
|
|
|
*out = tok;
|
2026-05-09 19:00:02 +01:00
|
|
|
return BSHELL_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-10 14:18:46 +01:00
|
|
|
enum bshell_status read_literal_string(
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_ctx *ctx,
|
|
|
|
|
struct bshell_lex_token **out)
|
2026-05-09 19:00:02 +01:00
|
|
|
{
|
|
|
|
|
fx_string *tmp = get_temp_string(ctx);
|
|
|
|
|
|
|
|
|
|
bool done = false;
|
|
|
|
|
bool fail = true;
|
|
|
|
|
while (!done) {
|
|
|
|
|
fx_wchar c = peek_char(ctx);
|
|
|
|
|
if (c == FX_WCHAR_INVALID) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (c == '\'') {
|
|
|
|
|
fail = false;
|
|
|
|
|
done = true;
|
2026-05-10 19:13:29 +01:00
|
|
|
set_token_end(ctx);
|
2026-05-09 19:00:02 +01:00
|
|
|
advance_char(ctx);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fx_string_append_wc(tmp, c);
|
|
|
|
|
advance_char(ctx);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_token *tok = bshell_lex_token_create_with_string(
|
|
|
|
|
BSHELL_TOK_STRING,
|
2026-05-09 19:00:02 +01:00
|
|
|
fx_string_get_cstr(tmp));
|
|
|
|
|
|
2026-05-10 14:18:46 +01:00
|
|
|
*out = tok;
|
2026-05-09 19:00:02 +01:00
|
|
|
return BSHELL_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
enum bshell_status read_line_comment(struct bshell_lex_ctx *lex)
|
2026-05-09 19:00:02 +01:00
|
|
|
{
|
|
|
|
|
while (true) {
|
|
|
|
|
fx_wchar c = peek_char(lex);
|
|
|
|
|
|
|
|
|
|
if (c == FX_WCHAR_INVALID) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
advance_char(lex);
|
|
|
|
|
|
|
|
|
|
if (c == '\n') {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return BSHELL_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-10 19:14:24 +01:00
|
|
|
enum bshell_status read_word(
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_ctx *ctx,
|
2026-05-10 19:14:24 +01:00
|
|
|
enum read_flags flags,
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_token **out)
|
2026-05-09 19:00:02 +01:00
|
|
|
{
|
2026-05-10 19:14:24 +01:00
|
|
|
fx_string *tmp = get_temp_string(ctx);
|
|
|
|
|
bool word_is_number = false;
|
2026-05-09 19:00:02 +01:00
|
|
|
|
2026-05-10 19:14:24 +01:00
|
|
|
if (!(flags & READ_NO_SET_TOKEN_START)) {
|
|
|
|
|
set_token_start(ctx);
|
2026-05-09 19:00:02 +01:00
|
|
|
}
|
|
|
|
|
|
2026-05-10 19:14:24 +01:00
|
|
|
if (flags & READ_APPEND_HYPHEN) {
|
|
|
|
|
fx_string_append_c(tmp, '-');
|
2026-05-09 19:00:02 +01:00
|
|
|
}
|
|
|
|
|
|
2026-05-10 19:14:24 +01:00
|
|
|
bool number_recog = !(flags & READ_NO_NUMBER_RECOGNITION);
|
2026-05-09 19:00:02 +01:00
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
enum bshell_lex_operator op = BSHELL_TKOP_NONE;
|
2026-05-10 14:18:46 +01:00
|
|
|
bool done = false;
|
|
|
|
|
while (!done) {
|
|
|
|
|
fx_wchar c = peek_char(ctx);
|
|
|
|
|
if (c == FX_WCHAR_INVALID) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
2026-05-09 19:00:02 +01:00
|
|
|
|
2026-05-10 14:18:46 +01:00
|
|
|
if (fx_wchar_is_space(c)) {
|
|
|
|
|
done = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-10 19:14:24 +01:00
|
|
|
const char *s = fx_string_get_cstr(tmp);
|
2026-05-24 20:13:55 +01:00
|
|
|
if (number_recog && string_is_valid_number(s, NULL, NULL)) {
|
|
|
|
|
if (c == '.') {
|
|
|
|
|
fx_string_append_wc(tmp, c);
|
|
|
|
|
set_token_end(ctx);
|
|
|
|
|
advance_char(ctx);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-10 19:14:24 +01:00
|
|
|
if (char_can_begin_symbol_in_state(
|
|
|
|
|
ctx,
|
|
|
|
|
c,
|
2026-05-25 10:33:29 +01:00
|
|
|
BSHELL_LEX_STATE_ARITHMETIC)) {
|
2026-05-10 19:14:24 +01:00
|
|
|
done = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2026-05-10 14:18:46 +01:00
|
|
|
}
|
|
|
|
|
|
2026-05-24 20:13:55 +01:00
|
|
|
if (char_can_begin_symbol(ctx, c)) {
|
|
|
|
|
done = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-10 19:14:24 +01:00
|
|
|
if (!fx_wchar_is_alpha(c)) {
|
2026-05-25 10:33:29 +01:00
|
|
|
op = get_bshell_operator_with_string(ctx, s);
|
|
|
|
|
if (op != BSHELL_TKOP_NONE) {
|
2026-05-10 19:14:24 +01:00
|
|
|
done = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2026-05-10 14:18:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fx_string_append_wc(tmp, c);
|
2026-05-10 19:14:24 +01:00
|
|
|
set_token_end(ctx);
|
2026-05-10 14:18:46 +01:00
|
|
|
advance_char(ctx);
|
2026-05-09 19:00:02 +01:00
|
|
|
}
|
|
|
|
|
|
2026-05-10 14:18:46 +01:00
|
|
|
if (fx_string_get_size(tmp, FX_STRLEN_NORMAL) == 0) {
|
|
|
|
|
if (ctx->lex_status == BSHELL_SUCCESS) {
|
|
|
|
|
return BSHELL_ERR_BAD_SYNTAX;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ctx->lex_status;
|
2026-05-09 19:00:02 +01:00
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_token *tok = bshell_lex_token_create_with_string(
|
|
|
|
|
BSHELL_TOK_WORD,
|
2026-05-10 14:18:46 +01:00
|
|
|
fx_string_get_cstr(tmp));
|
|
|
|
|
|
|
|
|
|
*out = tok;
|
2026-05-09 19:00:02 +01:00
|
|
|
return BSHELL_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-10 14:18:46 +01:00
|
|
|
enum bshell_status read_symbol(
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_ctx *ctx,
|
|
|
|
|
const struct bshell_lex_token_definition **out)
|
2026-05-09 19:00:02 +01:00
|
|
|
{
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_state *state = lex_state_get(ctx);
|
2026-05-10 19:13:29 +01:00
|
|
|
set_token_start(ctx);
|
2026-05-09 19:00:02 +01:00
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_symbol_node *node = ctx->lex_sym_tree;
|
2026-05-09 19:00:02 +01:00
|
|
|
char prev = 0;
|
|
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
|
fx_wchar c = peek_char(ctx);
|
|
|
|
|
if (c < 0) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_symbol_node *next = get_symbol_node(node, c);
|
2026-05-09 19:00:02 +01:00
|
|
|
if (!next
|
2026-05-11 23:02:02 +01:00
|
|
|
|| (next->s_def
|
|
|
|
|
&& !(next->s_def->enabled_states
|
|
|
|
|
& state->s_type->s_id))) {
|
2026-05-09 19:00:02 +01:00
|
|
|
prev = c;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
node = next;
|
2026-05-10 19:13:29 +01:00
|
|
|
set_token_end(ctx);
|
2026-05-09 19:00:02 +01:00
|
|
|
advance_char(ctx);
|
|
|
|
|
prev = c;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!node || node->s_def == NULL) {
|
|
|
|
|
return BSHELL_ERR_BAD_SYNTAX;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-10 14:18:46 +01:00
|
|
|
*out = node->s_def;
|
2026-05-09 19:00:02 +01:00
|
|
|
return BSHELL_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-10 14:18:46 +01:00
|
|
|
bool char_can_begin_symbol_in_state(
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_ctx *ctx,
|
2026-05-10 14:18:46 +01:00
|
|
|
char c,
|
2026-05-25 10:33:29 +01:00
|
|
|
enum bshell_lex_state_id state_type)
|
2026-05-09 19:00:02 +01:00
|
|
|
{
|
|
|
|
|
for (size_t i = 0; i < nr_symbols; i++) {
|
2026-05-11 23:57:35 +01:00
|
|
|
if (!symbols[i].name) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-09 19:00:02 +01:00
|
|
|
if (symbols[i].name[0] != c) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-10 14:18:46 +01:00
|
|
|
if (symbols[i].enabled_states & state_type) {
|
|
|
|
|
return true;
|
2026-05-09 19:00:02 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
bool char_can_begin_symbol(struct bshell_lex_ctx *ctx, char c)
|
2026-05-09 19:00:02 +01:00
|
|
|
{
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_state *state = lex_state_get(ctx);
|
2026-05-10 14:18:46 +01:00
|
|
|
return char_can_begin_symbol_in_state(ctx, c, state->s_type->s_id);
|
2026-05-09 19:00:02 +01:00
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
bool char_has_flags(
|
|
|
|
|
struct bshell_lex_ctx *ctx,
|
|
|
|
|
char c,
|
|
|
|
|
enum bshell_lex_token_flags flags)
|
2026-05-10 19:14:24 +01:00
|
|
|
{
|
|
|
|
|
for (size_t i = 0; i < nr_symbols; i++) {
|
2026-05-11 23:57:35 +01:00
|
|
|
if (!symbols[i].name) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-10 19:14:24 +01:00
|
|
|
if (symbols[i].name[0] != c) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (symbols[i].flags & flags) == flags;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-11 23:02:02 +01:00
|
|
|
bool keyword_has_flags(
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_ctx *ctx,
|
|
|
|
|
enum bshell_lex_keyword kw,
|
|
|
|
|
enum bshell_lex_token_flags flags)
|
2026-05-11 23:02:02 +01:00
|
|
|
{
|
|
|
|
|
for (size_t i = 0; i < nr_symbols; i++) {
|
|
|
|
|
if (keywords[i].id == kw) {
|
|
|
|
|
return (keywords[i].flags & flags) == flags;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
enum bshell_lex_token_flags keyword_get_flags(
|
|
|
|
|
struct bshell_lex_ctx *ctx,
|
|
|
|
|
enum bshell_lex_keyword kw)
|
2026-05-11 23:02:02 +01:00
|
|
|
{
|
|
|
|
|
for (size_t i = 0; i < nr_symbols; i++) {
|
|
|
|
|
if (keywords[i].id == kw) {
|
|
|
|
|
return keywords[i].flags;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool symbol_has_flags(
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_ctx *ctx,
|
|
|
|
|
enum bshell_lex_symbol sym,
|
|
|
|
|
enum bshell_lex_token_flags flags)
|
2026-05-11 23:02:02 +01:00
|
|
|
{
|
|
|
|
|
for (size_t i = 0; i < nr_symbols; i++) {
|
|
|
|
|
if (symbols[i].id == sym) {
|
|
|
|
|
return (symbols[i].flags & flags) == flags;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
enum bshell_lex_token_flags symbol_get_flags(
|
|
|
|
|
struct bshell_lex_ctx *ctx,
|
|
|
|
|
enum bshell_lex_symbol sym)
|
2026-05-11 23:02:02 +01:00
|
|
|
{
|
|
|
|
|
for (size_t i = 0; i < nr_symbols; i++) {
|
|
|
|
|
if (symbols[i].id == sym) {
|
|
|
|
|
return symbols[i].flags;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
enum bshell_lex_operator get_bshell_operator_with_string(
|
|
|
|
|
struct bshell_lex_ctx *ctx,
|
|
|
|
|
const char *s)
|
2026-05-10 19:14:24 +01:00
|
|
|
{
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_state *state = lex_state_get(ctx);
|
2026-05-10 19:14:24 +01:00
|
|
|
|
|
|
|
|
for (size_t i = 0; i < nr_operators; i++) {
|
|
|
|
|
const char *op_str = operators[i].name;
|
2026-05-11 23:57:35 +01:00
|
|
|
if (!op_str || strcmp(op_str, s) != 0) {
|
2026-05-10 19:14:24 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!(operators[i].enabled_states & state->s_type->s_id)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return operators[i].id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-11 23:02:02 +01:00
|
|
|
int compare_token_types(unsigned int a, unsigned int b)
|
|
|
|
|
{
|
|
|
|
|
if (a == b) {
|
|
|
|
|
return 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define BETWEEN(v, lo, hi) ((v) >= (lo) && (v) <= (hi))
|
2026-05-25 10:33:29 +01:00
|
|
|
enum bshell_lex_token_type a_type = BSHELL_TOK_NONE, b_type
|
|
|
|
|
= BSHELL_TOK_NONE;
|
|
|
|
|
|
|
|
|
|
if (BETWEEN(a, __BSHELL_KW_INDEX_BASE, __BSHELL_KW_INDEX_LIMIT)) {
|
|
|
|
|
a_type = BSHELL_TOK_KEYWORD;
|
|
|
|
|
} else if (BETWEEN(a,
|
|
|
|
|
__BSHELL_TKOP_INDEX_BASE,
|
|
|
|
|
__BSHELL_TKOP_INDEX_LIMIT)) {
|
|
|
|
|
a_type = BSHELL_TOK_OPERATOR;
|
|
|
|
|
} else if (BETWEEN(a,
|
|
|
|
|
__BSHELL_SYM_INDEX_BASE,
|
|
|
|
|
__BSHELL_SYM_INDEX_LIMIT)) {
|
|
|
|
|
a_type = BSHELL_TOK_SYMBOL;
|
2026-05-11 23:02:02 +01:00
|
|
|
} else {
|
|
|
|
|
a_type = a;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
if (BETWEEN(b, __BSHELL_KW_INDEX_BASE, __BSHELL_KW_INDEX_LIMIT)) {
|
|
|
|
|
b_type = BSHELL_TOK_KEYWORD;
|
|
|
|
|
} else if (BETWEEN(b,
|
|
|
|
|
__BSHELL_TKOP_INDEX_BASE,
|
|
|
|
|
__BSHELL_TKOP_INDEX_LIMIT)) {
|
|
|
|
|
b_type = BSHELL_TOK_OPERATOR;
|
|
|
|
|
} else if (BETWEEN(b,
|
|
|
|
|
__BSHELL_SYM_INDEX_BASE,
|
|
|
|
|
__BSHELL_SYM_INDEX_LIMIT)) {
|
|
|
|
|
b_type = BSHELL_TOK_SYMBOL;
|
2026-05-11 23:02:02 +01:00
|
|
|
} else {
|
|
|
|
|
b_type = b;
|
|
|
|
|
}
|
|
|
|
|
#undef BETWEEN
|
|
|
|
|
|
|
|
|
|
int result = 0;
|
|
|
|
|
if (a_type == b_type) {
|
|
|
|
|
if (a != a_type && b != b_type) {
|
|
|
|
|
result = 0;
|
|
|
|
|
} else {
|
|
|
|
|
result = a == b ? 2 : 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (result < 0) {
|
|
|
|
|
result = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-12 22:51:45 +01:00
|
|
|
static bool do_lex_state_transition(
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_ctx *ctx,
|
2026-05-12 22:51:45 +01:00
|
|
|
unsigned int token,
|
|
|
|
|
bool recursive)
|
2026-05-11 23:02:02 +01:00
|
|
|
{
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_state *state = lex_state_get(ctx);
|
2026-05-12 22:51:45 +01:00
|
|
|
enum link_flags required_flags = 0;
|
|
|
|
|
if (recursive) {
|
|
|
|
|
required_flags |= LINK_ALLOW_RECURSION;
|
|
|
|
|
}
|
2026-05-11 23:02:02 +01:00
|
|
|
|
2026-05-12 22:51:45 +01:00
|
|
|
if (!recursive) {
|
|
|
|
|
for (unsigned int i = 0; i < state->s_nr_terminators; i++) {
|
|
|
|
|
if (state->s_terminators[i] == token) {
|
|
|
|
|
lex_state_pop(ctx);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2026-05-11 23:02:02 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
const struct bshell_lex_state_link *table = state->s_type->s_links;
|
2026-05-11 23:02:02 +01:00
|
|
|
if (!table) {
|
2026-05-12 22:51:45 +01:00
|
|
|
return false;
|
2026-05-11 23:02:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define MAX_MATCHES 8
|
2026-05-25 10:33:29 +01:00
|
|
|
const struct bshell_lex_state_link *best_matches[MAX_MATCHES] = {0};
|
2026-05-11 23:02:02 +01:00
|
|
|
unsigned int match_count = 0;
|
|
|
|
|
int best_score = 0;
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
for (unsigned int i = 0; table[i].l_token != BSHELL_TOK_NONE; i++) {
|
2026-05-11 23:02:02 +01:00
|
|
|
int score = compare_token_types(table[i].l_token, token);
|
2026-05-12 22:51:45 +01:00
|
|
|
if ((table[i].l_flags & required_flags) != required_flags) {
|
|
|
|
|
score = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-11 23:02:02 +01:00
|
|
|
if (score == 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(match_count < MAX_MATCHES
|
|
|
|
|
|| "lex state has too many matches");
|
|
|
|
|
if (score == best_score) {
|
|
|
|
|
best_matches[match_count++] = &table[i];
|
|
|
|
|
} else if (score > best_score) {
|
|
|
|
|
match_count = 0;
|
|
|
|
|
best_matches[match_count++] = &table[i];
|
|
|
|
|
best_score = score;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#undef MAX_MATCHES
|
|
|
|
|
|
|
|
|
|
if (!match_count) {
|
2026-05-12 22:51:45 +01:00
|
|
|
return false;
|
2026-05-11 23:02:02 +01:00
|
|
|
}
|
|
|
|
|
|
2026-05-12 22:51:45 +01:00
|
|
|
bool result = false;
|
2026-05-11 23:02:02 +01:00
|
|
|
for (unsigned int i = 0; i < match_count; i++) {
|
2026-05-25 10:33:29 +01:00
|
|
|
const struct bshell_lex_state_link *link = best_matches[i];
|
2026-05-11 23:02:02 +01:00
|
|
|
switch (link->l_type) {
|
2026-05-25 10:33:29 +01:00
|
|
|
case BSHELL_LEX_STATE_LINK_POP:
|
2026-05-11 23:02:02 +01:00
|
|
|
lex_state_pop(ctx);
|
2026-05-12 22:51:45 +01:00
|
|
|
result = true;
|
2026-05-11 23:02:02 +01:00
|
|
|
break;
|
2026-05-25 10:33:29 +01:00
|
|
|
case BSHELL_LEX_STATE_LINK_PUSH: {
|
|
|
|
|
struct bshell_lex_state *state = lex_state_push(
|
2026-05-11 23:02:02 +01:00
|
|
|
ctx,
|
|
|
|
|
link->l_target,
|
|
|
|
|
link->l_target_flags);
|
|
|
|
|
for (unsigned int i = 0; link->l_terminators[i]; i++) {
|
|
|
|
|
lex_state_add_terminator(
|
|
|
|
|
state,
|
|
|
|
|
link->l_terminators[i]);
|
|
|
|
|
}
|
2026-05-12 22:51:45 +01:00
|
|
|
result = true;
|
2026-05-11 23:02:02 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
case BSHELL_LEX_STATE_LINK_CHANGE:
|
2026-05-11 23:02:02 +01:00
|
|
|
lex_state_change(ctx, link->l_target);
|
2026-05-12 22:51:45 +01:00
|
|
|
result = true;
|
2026-05-11 23:02:02 +01:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-05-12 22:51:45 +01:00
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
void handle_lex_state_transition(struct bshell_lex_ctx *ctx, unsigned int token)
|
2026-05-12 22:51:45 +01:00
|
|
|
{
|
|
|
|
|
bool cont = false;
|
|
|
|
|
bool recursive = false;
|
|
|
|
|
do {
|
|
|
|
|
cont = do_lex_state_transition(ctx, token, recursive);
|
|
|
|
|
recursive = true;
|
|
|
|
|
} while (cont);
|
2026-05-11 23:02:02 +01:00
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
static enum bshell_status read_string_content(struct bshell_lex_ctx *ctx)
|
2026-05-09 19:00:02 +01:00
|
|
|
{
|
|
|
|
|
fx_wchar c = FX_WCHAR_INVALID;
|
|
|
|
|
fx_string *str = get_temp_string(ctx);
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_state *state = lex_state_get(ctx);
|
2026-05-09 19:00:02 +01:00
|
|
|
|
|
|
|
|
if (!str) {
|
|
|
|
|
return BSHELL_ERR_NO_MEMORY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
|
c = peek_char(ctx);
|
|
|
|
|
if (c < 0) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-09 21:21:51 +01:00
|
|
|
if (char_can_begin_symbol(ctx, c)) {
|
2026-05-09 19:00:02 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fx_string_append_wc(str, c);
|
|
|
|
|
// set_token_end(lex);
|
|
|
|
|
advance_char(ctx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (fx_string_get_size(str, FX_STRLEN_NORMAL) == 0) {
|
|
|
|
|
return BSHELL_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_token *tok = bshell_lex_token_create_with_string(
|
|
|
|
|
BSHELL_TOK_STRING,
|
2026-05-09 19:00:02 +01:00
|
|
|
fx_string_get_cstr(str));
|
|
|
|
|
enqueue_token(ctx, tok);
|
|
|
|
|
|
|
|
|
|
return BSHELL_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-10 14:18:46 +01:00
|
|
|
#if 0
|
2026-05-25 10:33:29 +01:00
|
|
|
static enum bshell_status do_pump_token_string(struct bshell_lex_ctx *ctx)
|
2026-05-09 19:00:02 +01:00
|
|
|
{
|
|
|
|
|
fx_wchar c = peek_char(ctx);
|
|
|
|
|
enum bshell_status status = BSHELL_SUCCESS;
|
|
|
|
|
bool ok = false;
|
|
|
|
|
|
2026-05-09 21:21:51 +01:00
|
|
|
if (char_can_begin_symbol(ctx, c)) {
|
|
|
|
|
status = read_symbol(ctx);
|
2026-05-09 19:00:02 +01:00
|
|
|
ok = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (status != BSHELL_SUCCESS || !ok) {
|
|
|
|
|
status = read_string_content(ctx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
static enum bshell_status do_pump_token_normal(struct bshell_lex_ctx *ctx)
|
2026-05-09 19:00:02 +01:00
|
|
|
{
|
|
|
|
|
enum bshell_status status = BSHELL_SUCCESS;
|
|
|
|
|
|
|
|
|
|
fx_wchar c = peek_char(ctx);
|
|
|
|
|
bool newline = false;
|
|
|
|
|
|
|
|
|
|
while (fx_wchar_is_space(c)) {
|
|
|
|
|
if (c == '\n') {
|
|
|
|
|
newline = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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-09 19:00:02 +01:00
|
|
|
enqueue_token(ctx, tok);
|
|
|
|
|
return BSHELL_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (c == '-') {
|
|
|
|
|
return read_flag(ctx);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-09 21:21:51 +01:00
|
|
|
if (char_can_begin_symbol(ctx, c)) {
|
|
|
|
|
return read_symbol(ctx);
|
2026-05-09 19:00:02 +01:00
|
|
|
}
|
|
|
|
|
|
2026-05-09 21:21:51 +01:00
|
|
|
return read_word(ctx);
|
2026-05-09 19:00:02 +01:00
|
|
|
}
|
2026-05-10 14:18:46 +01:00
|
|
|
#endif
|
2026-05-09 19:00:02 +01:00
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
static enum bshell_status pump_tokens(struct bshell_lex_ctx *ctx)
|
2026-05-09 19:00:02 +01:00
|
|
|
{
|
|
|
|
|
enum bshell_status status = BSHELL_SUCCESS;
|
2026-05-10 14:18:46 +01:00
|
|
|
while (fx_queue_empty(&ctx->lex_tokens) && status == BSHELL_SUCCESS) {
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_state *state = lex_state_get(ctx);
|
2026-05-10 14:18:46 +01:00
|
|
|
status = state->s_type->s_pump_token(ctx);
|
2026-05-09 19:00:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
static void discard_all_tokens(struct bshell_lex_ctx *ctx)
|
2026-05-09 21:21:51 +01:00
|
|
|
{
|
|
|
|
|
fx_queue_entry *cur = fx_queue_first(&ctx->lex_tokens);
|
|
|
|
|
while (cur) {
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_token *tok
|
|
|
|
|
= fx_unbox(struct bshell_lex_token, cur, tok_entry);
|
|
|
|
|
if (tok->tok_type == BSHELL_TOK_LINEFEED) {
|
2026-05-09 21:21:51 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fx_queue_pop_front(&ctx->lex_tokens);
|
2026-05-25 10:33:29 +01:00
|
|
|
bshell_lex_token_destroy(tok);
|
2026-05-09 21:21:51 +01:00
|
|
|
cur = fx_queue_first(&ctx->lex_tokens);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_token *bshell_lex_ctx_peek(struct bshell_lex_ctx *ctx)
|
2026-05-09 19:00:02 +01:00
|
|
|
{
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_token *tok = get_next_token(ctx);
|
2026-05-09 19:00:02 +01:00
|
|
|
if (tok) {
|
|
|
|
|
return tok;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-09 21:21:51 +01:00
|
|
|
pump_tokens(ctx);
|
|
|
|
|
tok = get_next_token(ctx);
|
|
|
|
|
|
2026-05-09 19:00:02 +01:00
|
|
|
return tok;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_token *bshell_lex_ctx_claim(struct bshell_lex_ctx *ctx)
|
2026-05-09 19:00:02 +01:00
|
|
|
{
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_token *tok = dequeue_next_token(ctx);
|
2026-05-09 19:00:02 +01:00
|
|
|
if (tok) {
|
|
|
|
|
return tok;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (fx_queue_empty(&ctx->lex_tokens)) {
|
2026-05-09 21:21:51 +01:00
|
|
|
pump_tokens(ctx);
|
2026-05-09 19:00:02 +01:00
|
|
|
|
2026-05-09 21:21:51 +01:00
|
|
|
tok = get_next_token(ctx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return dequeue_next_token(ctx);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 10:33:29 +01:00
|
|
|
void bshell_lex_ctx_discard(struct bshell_lex_ctx *ctx)
|
2026-05-09 19:00:02 +01:00
|
|
|
{
|
2026-05-25 10:33:29 +01:00
|
|
|
struct bshell_lex_token *tok = dequeue_next_token(ctx);
|
2026-05-09 19:00:02 +01:00
|
|
|
if (tok) {
|
2026-05-25 10:33:29 +01:00
|
|
|
bshell_lex_token_destroy(tok);
|
2026-05-09 19:00:02 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-09 21:21:51 +01:00
|
|
|
/* request more tokens if necessary */
|
2026-05-09 19:00:02 +01:00
|
|
|
if (fx_queue_empty(&ctx->lex_tokens)) {
|
2026-05-09 21:21:51 +01:00
|
|
|
pump_tokens(ctx);
|
2026-05-09 19:00:02 +01:00
|
|
|
}
|
|
|
|
|
}
|