bshell: re-organise build into three separate components
bshell: the front-end binary bshell.runtime: contains the parser, compiler, and classes needed to run bshell scripts bshell.core: contains the builtin commandlets and aliases
This commit is contained in:
@@ -0,0 +1,232 @@
|
||||
#include "lex-internal.h"
|
||||
|
||||
static enum bshell_status arithmetic_hyphen(struct bshell_lex_ctx *ctx)
|
||||
{
|
||||
fx_wchar c = peek_char(ctx);
|
||||
if (!fx_wchar_is_alnum(c)) {
|
||||
push_symbol(ctx, BSHELL_SYM_HYPHEN);
|
||||
handle_lex_state_transition(ctx, BSHELL_SYM_HYPHEN);
|
||||
return BSHELL_SUCCESS;
|
||||
}
|
||||
|
||||
struct bshell_lex_token *tok = NULL;
|
||||
enum bshell_status status = read_word(
|
||||
ctx,
|
||||
READ_NO_SET_TOKEN_START | READ_APPEND_HYPHEN,
|
||||
&tok);
|
||||
if (status != BSHELL_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
|
||||
unsigned int token_type = BSHELL_TOK_WORD;
|
||||
if (convert_word_to_number(tok)) {
|
||||
token_type = tok->tok_type;
|
||||
|
||||
/* because of APPEND_HYPHEN (which is needed to ensure operator
|
||||
* tokens are detected properly), the resulting number will be
|
||||
* negative.
|
||||
* this token will be preceded by a HYPHEN token, so the number
|
||||
* must be positive */
|
||||
fx_value neg = FX_INT(-1);
|
||||
fx_value_multiply(&tok->tok_number, &neg, &tok->tok_number);
|
||||
push_symbol(ctx, BSHELL_SYM_HYPHEN);
|
||||
} else if (convert_word_to_operator(ctx, tok)) {
|
||||
token_type = tok->tok_operator;
|
||||
}
|
||||
|
||||
enqueue_token(ctx, tok);
|
||||
return BSHELL_SUCCESS;
|
||||
}
|
||||
|
||||
static enum bshell_status arithmetic_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_HYPHEN:
|
||||
return arithmetic_hyphen(ctx);
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
enqueue_token(ctx, tok);
|
||||
return status;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
push_symbol(ctx, sym->id);
|
||||
|
||||
return BSHELL_SUCCESS;
|
||||
}
|
||||
|
||||
static enum bshell_status arithmetic_word(struct bshell_lex_ctx *ctx)
|
||||
{
|
||||
struct bshell_lex_token *word = NULL;
|
||||
enum bshell_status status = read_word(ctx, 0, &word);
|
||||
if (status != BSHELL_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
|
||||
unsigned int token_type = BSHELL_TOK_WORD;
|
||||
bool kw = false, number = false;
|
||||
if (convert_word_to_keyword(word)) {
|
||||
token_type = word->tok_keyword;
|
||||
} else if (convert_word_to_number(word)) {
|
||||
token_type = word->tok_type;
|
||||
}
|
||||
|
||||
handle_lex_state_transition(ctx, token_type);
|
||||
|
||||
enqueue_token(ctx, word);
|
||||
return BSHELL_SUCCESS;
|
||||
}
|
||||
|
||||
static enum bshell_status arithmetic_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 arithmetic_symbol(ctx);
|
||||
}
|
||||
|
||||
return arithmetic_word(ctx);
|
||||
}
|
||||
|
||||
static const struct bshell_lex_state_link links[] = {
|
||||
LINK_CHANGE(BSHELL_SYM_EQUAL, BSHELL_LEX_STATE_STATEMENT),
|
||||
LINK_PUSH(BSHELL_SYM_DQUOTE, BSHELL_LEX_STATE_STRING, 0),
|
||||
LINK_PUSH(BSHELL_SYM_DOLLAR_LEFT_PAREN, BSHELL_LEX_STATE_STATEMENT, 0),
|
||||
LINK_POP(BSHELL_SYM_RIGHT_PAREN),
|
||||
LINK_CHANGE(BSHELL_SYM_SEMICOLON, BSHELL_LEX_STATE_STATEMENT),
|
||||
LINK_CHANGE(BSHELL_TOK_LINEFEED, BSHELL_LEX_STATE_STATEMENT),
|
||||
LINK_CHANGE(BSHELL_SYM_PIPE, BSHELL_LEX_STATE_STATEMENT),
|
||||
LINK_PUSH(BSHELL_SYM_AT_LEFT_BRACE, BSHELL_LEX_STATE_HASHTABLE, 0),
|
||||
LINK_PUSH(
|
||||
BSHELL_SYM_LEFT_PAREN,
|
||||
BSHELL_LEX_STATE_STATEMENT,
|
||||
STATEMENT_F_DISABLE_KEYWORDS),
|
||||
LINK_END,
|
||||
};
|
||||
|
||||
static const unsigned int keywords[] = {
|
||||
BSHELL_KW_IF,
|
||||
BSHELL_KW_ELSEIF,
|
||||
BSHELL_KW_ELSE,
|
||||
BSHELL_KW_NONE,
|
||||
};
|
||||
|
||||
static const unsigned int operators[] = {
|
||||
BSHELL_TKOP_F, BSHELL_TKOP_BAND, BSHELL_TKOP_BOR, BSHELL_TKOP_BXOR,
|
||||
BSHELL_TKOP_BNOT, BSHELL_TKOP_SHL, BSHELL_TKOP_SHR, BSHELL_TKOP_EQ,
|
||||
BSHELL_TKOP_NE, BSHELL_TKOP_GT, BSHELL_TKOP_LT, BSHELL_TKOP_GE,
|
||||
BSHELL_TKOP_LE, BSHELL_TKOP_MATCH, BSHELL_TKOP_NOTMATCH, BSHELL_TKOP_REPLACE,
|
||||
BSHELL_TKOP_LIKE, BSHELL_TKOP_NOTLIKE, BSHELL_TKOP_IN, BSHELL_TKOP_NOTIN,
|
||||
BSHELL_TKOP_CONTAINS, BSHELL_TKOP_NOTCONTAINS, BSHELL_TKOP_AND, BSHELL_TKOP_OR,
|
||||
BSHELL_TKOP_XOR, BSHELL_TKOP_NOT, BSHELL_TKOP_SPLIT, BSHELL_TKOP_JOIN,
|
||||
BSHELL_TKOP_IS, BSHELL_TKOP_ISNOT, BSHELL_TKOP_AS, BSHELL_TKOP_NONE,
|
||||
};
|
||||
|
||||
static const unsigned int symbols[] = {
|
||||
BSHELL_SYM_BANG,
|
||||
BSHELL_SYM_PLUS,
|
||||
BSHELL_SYM_HYPHEN,
|
||||
BSHELL_SYM_FORWARD_SLASH,
|
||||
BSHELL_SYM_ASTERISK,
|
||||
BSHELL_SYM_AMPERSAND,
|
||||
BSHELL_SYM_PERCENT,
|
||||
BSHELL_SYM_SQUOTE,
|
||||
BSHELL_SYM_DQUOTE,
|
||||
BSHELL_SYM_HASH,
|
||||
BSHELL_SYM_DOLLAR,
|
||||
BSHELL_SYM_DOLLAR_LEFT_PAREN,
|
||||
BSHELL_SYM_DOLLAR_LEFT_BRACE,
|
||||
BSHELL_SYM_AT,
|
||||
BSHELL_SYM_AT_LEFT_BRACE,
|
||||
BSHELL_SYM_PIPE,
|
||||
BSHELL_SYM_COMMA,
|
||||
BSHELL_SYM_SEMICOLON,
|
||||
BSHELL_SYM_LEFT_PAREN,
|
||||
BSHELL_SYM_RIGHT_PAREN,
|
||||
BSHELL_SYM_LEFT_BRACE,
|
||||
BSHELL_SYM_RIGHT_BRACE,
|
||||
BSHELL_SYM_LEFT_BRACKET,
|
||||
BSHELL_SYM_RIGHT_BRACKET,
|
||||
BSHELL_SYM_QUESTION_DOT,
|
||||
BSHELL_SYM_QUESTION_LEFT_BRACKET,
|
||||
BSHELL_SYM_EQUAL,
|
||||
BSHELL_SYM_PLUS_EQUAL,
|
||||
BSHELL_SYM_HYPHEN_EQUAL,
|
||||
BSHELL_SYM_FORWARD_SLASH_EQUAL,
|
||||
BSHELL_SYM_ASTERISK_EQUAL,
|
||||
BSHELL_SYM_PERCENT_EQUAL,
|
||||
BSHELL_SYM_DOT,
|
||||
BSHELL_SYM_DOT_DOT,
|
||||
BSHELL_SYM_COLON_COLON,
|
||||
BSHELL_SYM_NONE,
|
||||
};
|
||||
|
||||
const struct bshell_lex_state_type lex_arithmetic_state = {
|
||||
.s_id = BSHELL_LEX_STATE_ARITHMETIC,
|
||||
.s_pump_token = arithmetic_pump_token,
|
||||
.s_links = links,
|
||||
.s_keywords = keywords,
|
||||
.s_operators = operators,
|
||||
.s_symbols = symbols,
|
||||
};
|
||||
@@ -0,0 +1,228 @@
|
||||
#include "lex-internal.h"
|
||||
|
||||
#include <bshell/parse/token.h>
|
||||
|
||||
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(
|
||||
BSHELL_SYM_LEFT_PAREN,
|
||||
BSHELL_LEX_STATE_STATEMENT,
|
||||
STATEMENT_F_DISABLE_KEYWORDS),
|
||||
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,
|
||||
};
|
||||
@@ -0,0 +1,185 @@
|
||||
#include "lex-internal.h"
|
||||
|
||||
static enum bshell_status hashtable_hyphen(struct bshell_lex_ctx *ctx)
|
||||
{
|
||||
fx_wchar c = peek_char(ctx);
|
||||
if (!fx_wchar_is_alnum(c)) {
|
||||
push_symbol(ctx, BSHELL_SYM_HYPHEN);
|
||||
handle_lex_state_transition(ctx, BSHELL_SYM_HYPHEN);
|
||||
return BSHELL_SUCCESS;
|
||||
}
|
||||
|
||||
struct bshell_lex_token *tok = NULL;
|
||||
enum bshell_status status = read_word(
|
||||
ctx,
|
||||
READ_NO_SET_TOKEN_START | READ_APPEND_HYPHEN,
|
||||
&tok);
|
||||
if (status != BSHELL_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
|
||||
unsigned int token_type = BSHELL_TOK_WORD;
|
||||
if (convert_word_to_number(tok)) {
|
||||
token_type = tok->tok_type;
|
||||
/* because of APPEND_HYPHEN (which is needed to ensure operator
|
||||
* tokens are detected properly), the resulting number will be
|
||||
* negative.
|
||||
* this token will be preceded by a HYPHEN token, so the number
|
||||
* must be positive */
|
||||
fx_value neg = FX_INT(-1);
|
||||
fx_value_multiply(&tok->tok_number, &neg, &tok->tok_number);
|
||||
push_symbol(ctx, BSHELL_SYM_HYPHEN);
|
||||
} else if (convert_word_to_operator(ctx, tok)) {
|
||||
token_type = tok->tok_operator;
|
||||
}
|
||||
|
||||
handle_lex_state_transition(ctx, token_type);
|
||||
enqueue_token(ctx, tok);
|
||||
return BSHELL_SUCCESS;
|
||||
}
|
||||
|
||||
static enum bshell_status hashtable_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_SQUOTE:
|
||||
status = read_literal_string(ctx, &tok);
|
||||
if (status != BSHELL_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
enqueue_token(ctx, tok);
|
||||
return BSHELL_SUCCESS;
|
||||
case BSHELL_SYM_HYPHEN:
|
||||
return hashtable_hyphen(ctx);
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
enqueue_token(ctx, tok);
|
||||
return status;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
push_symbol(ctx, sym->id);
|
||||
|
||||
return BSHELL_SUCCESS;
|
||||
}
|
||||
|
||||
static enum bshell_status hashtable_word(struct bshell_lex_ctx *ctx)
|
||||
{
|
||||
struct bshell_lex_token *word = NULL;
|
||||
enum bshell_status status = read_word(ctx, 0, &word);
|
||||
if (status != BSHELL_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
|
||||
convert_word_to_number(word);
|
||||
|
||||
handle_lex_state_transition(ctx, word->tok_type);
|
||||
enqueue_token(ctx, word);
|
||||
return BSHELL_SUCCESS;
|
||||
}
|
||||
|
||||
static enum bshell_status hashtable_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 1
|
||||
if (newline) {
|
||||
struct bshell_lex_token *tok = bshell_lex_token_create(BSHELL_TOK_LINEFEED);
|
||||
enqueue_token(ctx, tok);
|
||||
return BSHELL_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (char_can_begin_symbol(ctx, c)) {
|
||||
return hashtable_symbol(ctx);
|
||||
}
|
||||
|
||||
return hashtable_word(ctx);
|
||||
}
|
||||
|
||||
static const struct bshell_lex_state_link links[] = {
|
||||
LINK_PUSH_WITH_TERM(
|
||||
BSHELL_SYM_EQUAL,
|
||||
BSHELL_LEX_STATE_STATEMENT,
|
||||
0,
|
||||
BSHELL_SYM_RIGHT_BRACE,
|
||||
BSHELL_SYM_SEMICOLON,
|
||||
BSHELL_TOK_LINEFEED),
|
||||
LINK_PUSH_WITH_TERM(
|
||||
BSHELL_TOK_LINEFEED,
|
||||
BSHELL_LEX_STATE_STATEMENT,
|
||||
0,
|
||||
BSHELL_SYM_SEMICOLON,
|
||||
BSHELL_TOK_LINEFEED),
|
||||
LINK_PUSH(BSHELL_SYM_DQUOTE, BSHELL_LEX_STATE_STRING, 0),
|
||||
LINK_PUSH(
|
||||
BSHELL_SYM_LEFT_PAREN,
|
||||
BSHELL_LEX_STATE_STATEMENT,
|
||||
STATEMENT_F_DISABLE_KEYWORDS),
|
||||
LINK_PUSH(BSHELL_SYM_DOLLAR_LEFT_PAREN, BSHELL_LEX_STATE_STATEMENT, 0),
|
||||
LINK_POP2(BSHELL_SYM_RIGHT_BRACE, LINK_ALLOW_RECURSION),
|
||||
LINK_END,
|
||||
};
|
||||
|
||||
static const unsigned int symbols[] = {
|
||||
BSHELL_SYM_EQUAL,
|
||||
BSHELL_SYM_DQUOTE,
|
||||
BSHELL_SYM_SQUOTE,
|
||||
BSHELL_SYM_SEMICOLON,
|
||||
BSHELL_SYM_RIGHT_BRACE,
|
||||
BSHELL_SYM_DOLLAR_LEFT_PAREN,
|
||||
BSHELL_SYM_LEFT_PAREN,
|
||||
BSHELL_SYM_HASH,
|
||||
BSHELL_SYM_NONE,
|
||||
};
|
||||
|
||||
const struct bshell_lex_state_type lex_hashtable_state = {
|
||||
.s_id = BSHELL_LEX_STATE_HASHTABLE,
|
||||
.s_pump_token = hashtable_pump_token,
|
||||
.s_links = links,
|
||||
.s_symbols = symbols,
|
||||
};
|
||||
@@ -0,0 +1,204 @@
|
||||
#ifndef PARSE_LEX_INTERNAL_H_
|
||||
#define PARSE_LEX_INTERNAL_H_
|
||||
|
||||
#include <bshell/parse/lex.h>
|
||||
#include <bshell/parse/token.h>
|
||||
#include <bshell/status.h>
|
||||
|
||||
struct bshell_lex_ctx;
|
||||
|
||||
enum state_flags {
|
||||
/* statement: don't convert matching words to keywords */
|
||||
STATEMENT_F_DISABLE_KEYWORDS = 0x01u,
|
||||
/* arithmetic: don't switch back to statement mode even when
|
||||
* encountering a token that would otherwise require it. */
|
||||
ARITHMETIC_F_DISABLE_STATEMENTS = 0x01u,
|
||||
};
|
||||
|
||||
enum read_flags {
|
||||
READ_APPEND_HYPHEN = 0x01u,
|
||||
READ_NO_SET_TOKEN_START = 0x02u,
|
||||
READ_NO_NUMBER_RECOGNITION = 0x04u,
|
||||
};
|
||||
|
||||
enum link_flags {
|
||||
LINK_ALLOW_RECURSION = 0x01u,
|
||||
};
|
||||
|
||||
#define LINK_PUSH(tok, target, flags) \
|
||||
((struct bshell_lex_state_link) { \
|
||||
.l_token = (tok), \
|
||||
.l_type = BSHELL_LEX_STATE_LINK_PUSH, \
|
||||
.l_target = (target), \
|
||||
.l_target_flags = (flags), \
|
||||
})
|
||||
#define LINK_PUSH_WITH_TERM(tok, target, flags, ...) \
|
||||
((struct bshell_lex_state_link) { \
|
||||
.l_token = (tok), \
|
||||
.l_type = BSHELL_LEX_STATE_LINK_PUSH, \
|
||||
.l_target = (target), \
|
||||
.l_target_flags = (flags), \
|
||||
.l_terminators = {__VA_ARGS__, BSHELL_TOK_NONE}, \
|
||||
})
|
||||
#define LINK_CHANGE(tok, target) \
|
||||
((struct bshell_lex_state_link) { \
|
||||
.l_token = (tok), \
|
||||
.l_type = BSHELL_LEX_STATE_LINK_CHANGE, \
|
||||
.l_target = (target), \
|
||||
})
|
||||
#define LINK_POP(tok) \
|
||||
((struct bshell_lex_state_link) { \
|
||||
.l_token = (tok), \
|
||||
.l_type = BSHELL_LEX_STATE_LINK_POP, \
|
||||
})
|
||||
#define LINK_POP2(tok, flags) \
|
||||
((struct bshell_lex_state_link) { \
|
||||
.l_token = (tok), \
|
||||
.l_type = BSHELL_LEX_STATE_LINK_POP, \
|
||||
.l_flags = (flags), \
|
||||
})
|
||||
#define LINK_NONE(tok) \
|
||||
((struct bshell_lex_state_link) { \
|
||||
.l_token = (tok), \
|
||||
.l_type = BSHELL_LEX_STATE_LINK_NONE, \
|
||||
})
|
||||
#define LINK_END ((struct bshell_lex_state_link) {})
|
||||
|
||||
struct bshell_lex_state_link {
|
||||
unsigned int l_token;
|
||||
enum {
|
||||
BSHELL_LEX_STATE_LINK_NONE,
|
||||
BSHELL_LEX_STATE_LINK_PUSH,
|
||||
BSHELL_LEX_STATE_LINK_CHANGE,
|
||||
BSHELL_LEX_STATE_LINK_POP,
|
||||
} l_type;
|
||||
enum link_flags l_flags;
|
||||
enum bshell_lex_state_id l_target;
|
||||
enum state_flags l_target_flags;
|
||||
unsigned int l_terminators[BSHELL_LEX_STATE_MAX_TERMINATORS];
|
||||
};
|
||||
|
||||
typedef enum bshell_status (*lex_state_pump_token)(struct bshell_lex_ctx *);
|
||||
typedef enum bshell_status (*lex_state_begin)(struct bshell_lex_ctx *);
|
||||
typedef enum bshell_status (*lex_state_end)(struct bshell_lex_ctx *);
|
||||
|
||||
struct bshell_lex_state_type {
|
||||
enum bshell_lex_state_id s_id;
|
||||
lex_state_pump_token s_pump_token;
|
||||
lex_state_begin s_begin;
|
||||
lex_state_end s_end;
|
||||
|
||||
const unsigned int *s_keywords;
|
||||
const unsigned int *s_operators;
|
||||
const unsigned int *s_symbols;
|
||||
const struct bshell_lex_state_link *s_links;
|
||||
};
|
||||
|
||||
extern enum bshell_status pump_token_statement(struct bshell_lex_ctx *ctx);
|
||||
extern enum bshell_status pump_token_expression(struct bshell_lex_ctx *ctx);
|
||||
extern enum bshell_status pump_token_command(struct bshell_lex_ctx *ctx);
|
||||
extern enum bshell_status pump_token_arithmetic(struct bshell_lex_ctx *ctx);
|
||||
extern enum bshell_status pump_token_string(struct bshell_lex_ctx *ctx);
|
||||
|
||||
extern void set_token_start(struct bshell_lex_ctx *ctx);
|
||||
extern void set_token_end(struct bshell_lex_ctx *ctx);
|
||||
|
||||
extern struct bshell_lex_state *lex_state_push(
|
||||
struct bshell_lex_ctx *ctx,
|
||||
enum bshell_lex_state_id state_type,
|
||||
enum state_flags flags);
|
||||
extern void lex_state_pop(struct bshell_lex_ctx *ctx);
|
||||
extern struct bshell_lex_state *lex_state_get(struct bshell_lex_ctx *ctx);
|
||||
extern void lex_state_change(
|
||||
struct bshell_lex_ctx *ctx,
|
||||
enum bshell_lex_state_id type);
|
||||
extern fx_string *lex_state_get_tempstr(struct bshell_lex_ctx *ctx);
|
||||
extern void lex_state_add_terminator(
|
||||
struct bshell_lex_state *state,
|
||||
unsigned int tok);
|
||||
extern bool lex_state_terminates_at_token(
|
||||
struct bshell_lex_ctx *ctx,
|
||||
unsigned int tok);
|
||||
|
||||
extern fx_wchar peek_char(struct bshell_lex_ctx *ctx);
|
||||
extern fx_wchar peek_char_noread(struct bshell_lex_ctx *ctx);
|
||||
extern fx_wchar peek2_char(struct bshell_lex_ctx *ctx);
|
||||
extern fx_wchar peek2_char_noread(struct bshell_lex_ctx *ctx);
|
||||
extern void advance_char(struct bshell_lex_ctx *ctx);
|
||||
extern void advance_char_noread(struct bshell_lex_ctx *ctx);
|
||||
|
||||
extern bool string_is_valid_number(
|
||||
const char *s,
|
||||
fx_value *out,
|
||||
enum bshell_lex_token_type *out_type);
|
||||
extern bool convert_word_to_number(struct bshell_lex_token *tok);
|
||||
extern bool convert_word_to_keyword(struct bshell_lex_token *tok);
|
||||
extern bool convert_word_to_operator(
|
||||
struct bshell_lex_ctx *ctx,
|
||||
struct bshell_lex_token *tok);
|
||||
|
||||
extern void enqueue_token(
|
||||
struct bshell_lex_ctx *ctx,
|
||||
struct bshell_lex_token *tok);
|
||||
extern void enqueue_token_with_coordinates(
|
||||
struct bshell_lex_ctx *ctx,
|
||||
struct bshell_lex_token *tok,
|
||||
const struct bshell_char_cell *start,
|
||||
const struct bshell_char_cell *end);
|
||||
|
||||
extern enum bshell_status read_word(
|
||||
struct bshell_lex_ctx *ctx,
|
||||
enum read_flags flags,
|
||||
struct bshell_lex_token **out);
|
||||
extern enum bshell_status read_symbol(
|
||||
struct bshell_lex_ctx *ctx,
|
||||
const struct bshell_lex_token_definition **out);
|
||||
extern enum bshell_status read_literal_string(
|
||||
struct bshell_lex_ctx *ctx,
|
||||
struct bshell_lex_token **out);
|
||||
extern enum bshell_status read_line_comment(struct bshell_lex_ctx *lex);
|
||||
extern enum bshell_status read_var(
|
||||
struct bshell_lex_ctx *ctx,
|
||||
enum bshell_lex_token_type type,
|
||||
struct bshell_lex_token **out);
|
||||
extern enum bshell_status read_braced_var(
|
||||
struct bshell_lex_ctx *ctx,
|
||||
enum bshell_lex_token_type type,
|
||||
struct bshell_lex_token **out);
|
||||
|
||||
extern enum bshell_status push_symbol(
|
||||
struct bshell_lex_ctx *ctx,
|
||||
enum bshell_lex_symbol sym);
|
||||
|
||||
extern bool char_can_begin_symbol(struct bshell_lex_ctx *ctx, char c);
|
||||
extern bool char_can_begin_symbol_in_state(
|
||||
struct bshell_lex_ctx *ctx,
|
||||
char c,
|
||||
enum bshell_lex_state_id state_type);
|
||||
extern bool char_has_flags(
|
||||
struct bshell_lex_ctx *ctx,
|
||||
char c,
|
||||
enum bshell_lex_token_flags flags);
|
||||
extern bool keyword_has_flags(
|
||||
struct bshell_lex_ctx *ctx,
|
||||
enum bshell_lex_keyword kw,
|
||||
enum bshell_lex_token_flags flags);
|
||||
extern enum bshell_lex_token_flags keyword_get_flags(
|
||||
struct bshell_lex_ctx *ctx,
|
||||
enum bshell_lex_keyword kw);
|
||||
extern bool symbol_has_flags(
|
||||
struct bshell_lex_ctx *ctx,
|
||||
enum bshell_lex_symbol sym,
|
||||
enum bshell_lex_token_flags flags);
|
||||
extern enum bshell_lex_token_flags symbol_get_flags(
|
||||
struct bshell_lex_ctx *ctx,
|
||||
enum bshell_lex_symbol sym);
|
||||
extern enum bshell_lex_operator get_bshell_operator_with_string(
|
||||
struct bshell_lex_ctx *ctx,
|
||||
const char *s);
|
||||
|
||||
extern void handle_lex_state_transition(
|
||||
struct bshell_lex_ctx *ctx,
|
||||
unsigned int token);
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,254 @@
|
||||
#include "lex-internal.h"
|
||||
|
||||
static enum bshell_status statement_hyphen(struct bshell_lex_ctx *ctx)
|
||||
{
|
||||
fx_wchar c = peek_char(ctx);
|
||||
if (!fx_wchar_is_alnum(c)) {
|
||||
push_symbol(ctx, BSHELL_SYM_HYPHEN);
|
||||
handle_lex_state_transition(ctx, BSHELL_SYM_HYPHEN);
|
||||
return BSHELL_SUCCESS;
|
||||
}
|
||||
|
||||
struct bshell_lex_token *tok = NULL;
|
||||
enum bshell_status status = read_word(
|
||||
ctx,
|
||||
READ_NO_SET_TOKEN_START | READ_APPEND_HYPHEN,
|
||||
&tok);
|
||||
if (status != BSHELL_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
|
||||
unsigned int token_type = BSHELL_TOK_WORD;
|
||||
|
||||
if (convert_word_to_number(tok)) {
|
||||
token_type = tok->tok_type;
|
||||
|
||||
/* because of APPEND_HYPHEN (which is needed to ensure operator
|
||||
* tokens are detected properly), the resulting number will be
|
||||
* negative.
|
||||
* this token will be preceded by a HYPHEN token, so the number
|
||||
* must be positive */
|
||||
fx_value neg = FX_INT(-1);
|
||||
fx_value_multiply(&tok->tok_number, &neg, &tok->tok_number);
|
||||
push_symbol(ctx, BSHELL_SYM_HYPHEN);
|
||||
} else if (convert_word_to_operator(ctx, tok)) {
|
||||
token_type = tok->tok_operator;
|
||||
}
|
||||
|
||||
handle_lex_state_transition(ctx, token_type);
|
||||
enqueue_token(ctx, tok);
|
||||
|
||||
return BSHELL_SUCCESS;
|
||||
}
|
||||
|
||||
static enum bshell_status statement_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_HYPHEN:
|
||||
return statement_hyphen(ctx);
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
enqueue_token(ctx, tok);
|
||||
return status;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
push_symbol(ctx, sym->id);
|
||||
return BSHELL_SUCCESS;
|
||||
}
|
||||
|
||||
static enum bshell_status statement_word(struct bshell_lex_ctx *ctx)
|
||||
{
|
||||
struct bshell_lex_token *word = NULL;
|
||||
enum bshell_status status = read_word(ctx, 0, &word);
|
||||
if (status != BSHELL_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
|
||||
struct bshell_lex_state *state = lex_state_get(ctx);
|
||||
|
||||
bool enable_keywords = !(state->s_flags & STATEMENT_F_DISABLE_KEYWORDS);
|
||||
unsigned int token = BSHELL_TOK_WORD;
|
||||
|
||||
if (enable_keywords && convert_word_to_keyword(word)) {
|
||||
token = word->tok_keyword;
|
||||
} else if (convert_word_to_number(word)) {
|
||||
token = word->tok_type;
|
||||
}
|
||||
|
||||
handle_lex_state_transition(ctx, token);
|
||||
|
||||
enqueue_token(ctx, word);
|
||||
return BSHELL_SUCCESS;
|
||||
}
|
||||
|
||||
static enum bshell_status statement_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 statement_symbol(ctx);
|
||||
}
|
||||
|
||||
if (char_has_flags(ctx, c, BSHELL_LEX_TOKEN_UNARY_ARITHMETIC)) {
|
||||
lex_state_change(ctx, BSHELL_LEX_STATE_ARITHMETIC);
|
||||
return BSHELL_SUCCESS;
|
||||
}
|
||||
|
||||
return statement_word(ctx);
|
||||
}
|
||||
|
||||
static const struct bshell_lex_state_link links[] = {
|
||||
LINK_PUSH(BSHELL_SYM_DQUOTE, BSHELL_LEX_STATE_STRING, 0),
|
||||
/* arithmetic tokens */
|
||||
LINK_CHANGE(BSHELL_TOK_KEYWORD, BSHELL_LEX_STATE_ARITHMETIC),
|
||||
LINK_CHANGE(BSHELL_SYM_SQUOTE, BSHELL_LEX_STATE_ARITHMETIC),
|
||||
LINK_CHANGE(BSHELL_TOK_INT, BSHELL_LEX_STATE_ARITHMETIC),
|
||||
LINK_CHANGE(BSHELL_TOK_DOUBLE, BSHELL_LEX_STATE_ARITHMETIC),
|
||||
LINK_PUSH(BSHELL_SYM_DOLLAR, BSHELL_LEX_STATE_ARITHMETIC, 0),
|
||||
LINK_PUSH_WITH_TERM(
|
||||
BSHELL_SYM_DOLLAR_LEFT_BRACE,
|
||||
BSHELL_LEX_STATE_ARITHMETIC,
|
||||
0,
|
||||
BSHELL_SYM_RIGHT_BRACE),
|
||||
LINK_CHANGE(BSHELL_SYM_AT_LEFT_BRACE, BSHELL_LEX_STATE_ARITHMETIC),
|
||||
LINK_CHANGE(BSHELL_SYM_AT_LEFT_PAREN, BSHELL_LEX_STATE_ARITHMETIC),
|
||||
LINK_PUSH_WITH_TERM(
|
||||
BSHELL_SYM_AT_LEFT_BRACE,
|
||||
BSHELL_LEX_STATE_HASHTABLE,
|
||||
0,
|
||||
BSHELL_SYM_RIGHT_BRACE),
|
||||
LINK_PUSH(BSHELL_SYM_AT, BSHELL_LEX_STATE_ARITHMETIC, 0),
|
||||
LINK_CHANGE(BSHELL_SYM_LEFT_PAREN, BSHELL_LEX_STATE_ARITHMETIC),
|
||||
LINK_CHANGE(BSHELL_SYM_LEFT_BRACKET, BSHELL_LEX_STATE_ARITHMETIC),
|
||||
LINK_CHANGE(BSHELL_SYM_BANG, BSHELL_LEX_STATE_ARITHMETIC),
|
||||
LINK_PUSH_WITH_TERM(
|
||||
BSHELL_SYM_LEFT_PAREN,
|
||||
BSHELL_LEX_STATE_STATEMENT,
|
||||
STATEMENT_F_DISABLE_KEYWORDS,
|
||||
BSHELL_SYM_RIGHT_PAREN),
|
||||
|
||||
/* statement tokens */
|
||||
LINK_PUSH_WITH_TERM(
|
||||
BSHELL_SYM_LEFT_BRACE,
|
||||
BSHELL_LEX_STATE_STATEMENT,
|
||||
0,
|
||||
BSHELL_SYM_RIGHT_BRACE),
|
||||
LINK_PUSH_WITH_TERM(
|
||||
BSHELL_SYM_DOLLAR_LEFT_PAREN,
|
||||
BSHELL_LEX_STATE_STATEMENT,
|
||||
0,
|
||||
BSHELL_SYM_RIGHT_PAREN),
|
||||
|
||||
/* command tokens */
|
||||
LINK_CHANGE(BSHELL_KW_FUNC, BSHELL_LEX_STATE_COMMAND),
|
||||
LINK_CHANGE(BSHELL_SYM_AMPERSAND, BSHELL_LEX_STATE_COMMAND),
|
||||
LINK_CHANGE(BSHELL_TOK_WORD, BSHELL_LEX_STATE_COMMAND),
|
||||
LINK_END,
|
||||
};
|
||||
|
||||
static const unsigned int keywords[] = {
|
||||
BSHELL_KW_FUNC,
|
||||
BSHELL_KW_IF,
|
||||
BSHELL_KW_ELSEIF,
|
||||
BSHELL_KW_ELSE,
|
||||
BSHELL_KW_NONE,
|
||||
};
|
||||
|
||||
static const unsigned int operators[] = {
|
||||
BSHELL_TKOP_BNOT,
|
||||
BSHELL_TKOP_NOT,
|
||||
BSHELL_TKOP_NONE,
|
||||
};
|
||||
|
||||
static const unsigned int symbols[] = {
|
||||
BSHELL_SYM_AMPERSAND,
|
||||
BSHELL_SYM_BANG,
|
||||
BSHELL_SYM_SQUOTE,
|
||||
BSHELL_SYM_DQUOTE,
|
||||
BSHELL_SYM_HASH,
|
||||
BSHELL_SYM_AT,
|
||||
BSHELL_SYM_AT_LEFT_PAREN,
|
||||
BSHELL_SYM_AT_LEFT_BRACE,
|
||||
BSHELL_SYM_PIPE,
|
||||
BSHELL_SYM_COMMA,
|
||||
BSHELL_SYM_SEMICOLON,
|
||||
BSHELL_SYM_LEFT_BRACE,
|
||||
BSHELL_SYM_RIGHT_BRACE,
|
||||
BSHELL_SYM_LEFT_BRACKET,
|
||||
BSHELL_SYM_RIGHT_BRACKET,
|
||||
BSHELL_SYM_LEFT_PAREN,
|
||||
BSHELL_SYM_RIGHT_PAREN,
|
||||
BSHELL_SYM_NONE,
|
||||
};
|
||||
|
||||
const struct bshell_lex_state_type lex_statement_state = {
|
||||
.s_id = BSHELL_LEX_STATE_STATEMENT,
|
||||
.s_pump_token = statement_pump_token,
|
||||
.s_links = links,
|
||||
.s_keywords = keywords,
|
||||
.s_operators = operators,
|
||||
.s_symbols = symbols,
|
||||
};
|
||||
@@ -0,0 +1,141 @@
|
||||
#include "lex-internal.h"
|
||||
|
||||
static enum bshell_status string_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_DOLLAR_LEFT_PAREN:
|
||||
return push_symbol(ctx, sym->id);
|
||||
case BSHELL_SYM_DOLLAR:
|
||||
status = read_var(ctx, BSHELL_TOK_VAR, &tok);
|
||||
if (status != BSHELL_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
enqueue_token(ctx, tok);
|
||||
return status;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return BSHELL_ERR_BAD_SYNTAX;
|
||||
}
|
||||
|
||||
static enum bshell_status string_content(struct bshell_lex_ctx *ctx)
|
||||
{
|
||||
fx_wchar c = FX_WCHAR_INVALID;
|
||||
fx_string *temp = lex_state_get_tempstr(ctx);
|
||||
set_token_start(ctx);
|
||||
fx_string_clear(temp);
|
||||
|
||||
while (1) {
|
||||
c = peek_char(ctx);
|
||||
if (c == FX_WCHAR_INVALID) {
|
||||
/* EOF without end of string */
|
||||
ctx->lex_status = BSHELL_ERR_BAD_SYNTAX;
|
||||
}
|
||||
|
||||
if (char_can_begin_symbol(ctx, c)) {
|
||||
break;
|
||||
}
|
||||
|
||||
fx_string_append_wc(temp, c);
|
||||
set_token_end(ctx);
|
||||
advance_char(ctx);
|
||||
}
|
||||
|
||||
if (fx_string_get_size(temp, FX_STRLEN_NORMAL) == 0) {
|
||||
return BSHELL_SUCCESS;
|
||||
}
|
||||
|
||||
struct bshell_lex_token *tok = bshell_lex_token_create_with_string(
|
||||
BSHELL_TOK_STRING,
|
||||
fx_string_get_cstr(temp));
|
||||
enqueue_token(ctx, tok);
|
||||
|
||||
return BSHELL_SUCCESS;
|
||||
}
|
||||
|
||||
static enum bshell_status string_begin(struct bshell_lex_ctx *ctx)
|
||||
{
|
||||
struct bshell_lex_token *tok = bshell_lex_token_create(BSHELL_TOK_STR_START);
|
||||
if (!tok) {
|
||||
return BSHELL_ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
enqueue_token(ctx, tok);
|
||||
return BSHELL_SUCCESS;
|
||||
}
|
||||
|
||||
static enum bshell_status string_end(struct bshell_lex_ctx *ctx)
|
||||
{
|
||||
struct bshell_lex_token *tok = bshell_lex_token_create(BSHELL_TOK_STR_END);
|
||||
if (!tok) {
|
||||
return BSHELL_ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
enqueue_token(ctx, tok);
|
||||
return BSHELL_SUCCESS;
|
||||
}
|
||||
|
||||
static enum bshell_status string_pump_token(struct bshell_lex_ctx *ctx)
|
||||
{
|
||||
fx_wchar c = peek_char(ctx);
|
||||
|
||||
if (char_can_begin_symbol(ctx, c)) {
|
||||
return string_symbol(ctx);
|
||||
}
|
||||
|
||||
return string_content(ctx);
|
||||
}
|
||||
|
||||
static const struct bshell_lex_state_link links[] = {
|
||||
LINK_PUSH(BSHELL_SYM_DOLLAR_LEFT_PAREN, BSHELL_LEX_STATE_STATEMENT, 0),
|
||||
LINK_POP(BSHELL_SYM_DQUOTE),
|
||||
LINK_END,
|
||||
};
|
||||
|
||||
static const unsigned int symbols[] = {
|
||||
BSHELL_SYM_DOLLAR,
|
||||
BSHELL_SYM_DOLLAR_LEFT_PAREN,
|
||||
BSHELL_SYM_DOLLAR_LEFT_BRACE,
|
||||
BSHELL_SYM_DQUOTE,
|
||||
BSHELL_SYM_NONE,
|
||||
};
|
||||
|
||||
const struct bshell_lex_state_type lex_string_state = {
|
||||
.s_id = BSHELL_LEX_STATE_STRING,
|
||||
.s_begin = string_begin,
|
||||
.s_end = string_end,
|
||||
.s_pump_token = string_pump_token,
|
||||
.s_links = links,
|
||||
.s_symbols = symbols,
|
||||
};
|
||||
@@ -0,0 +1,162 @@
|
||||
#include "lex-internal.h"
|
||||
|
||||
static enum bshell_status word_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;
|
||||
}
|
||||
|
||||
struct bshell_lex_token *tok = NULL;
|
||||
|
||||
switch (sym->id) {
|
||||
case BSHELL_SYM_DOLLAR_LEFT_PAREN:
|
||||
status = push_symbol(ctx, sym->id);
|
||||
if (status != BSHELL_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
|
||||
lex_state_push(ctx, BSHELL_LEX_STATE_STATEMENT, 0);
|
||||
return BSHELL_SUCCESS;
|
||||
case BSHELL_SYM_RIGHT_PAREN:
|
||||
lex_state_pop(ctx);
|
||||
|
||||
status = push_symbol(ctx, sym->id);
|
||||
if (status != BSHELL_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
return BSHELL_SUCCESS;
|
||||
case BSHELL_SYM_DOLLAR:
|
||||
status = read_var(ctx, BSHELL_TOK_VAR, &tok);
|
||||
if (status != BSHELL_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
|
||||
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;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return BSHELL_ERR_BAD_SYNTAX;
|
||||
}
|
||||
|
||||
static enum bshell_status word_content(struct bshell_lex_ctx *ctx)
|
||||
{
|
||||
fx_wchar c = FX_WCHAR_INVALID;
|
||||
fx_string *temp = lex_state_get_tempstr(ctx);
|
||||
set_token_start(ctx);
|
||||
fx_string_clear(temp);
|
||||
|
||||
while (1) {
|
||||
c = peek_char(ctx);
|
||||
if (c == FX_WCHAR_INVALID) {
|
||||
/* EOF without end of word */
|
||||
ctx->lex_status = BSHELL_ERR_BAD_SYNTAX;
|
||||
}
|
||||
|
||||
if (fx_wchar_is_space(c)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (char_can_begin_symbol(ctx, c)) {
|
||||
break;
|
||||
}
|
||||
|
||||
fx_string_append_wc(temp, c);
|
||||
set_token_end(ctx);
|
||||
advance_char(ctx);
|
||||
}
|
||||
|
||||
if (fx_string_get_size(temp, FX_STRLEN_NORMAL) == 0) {
|
||||
return BSHELL_SUCCESS;
|
||||
}
|
||||
|
||||
struct bshell_lex_token *tok = bshell_lex_token_create_with_string(
|
||||
BSHELL_TOK_WORD,
|
||||
fx_string_get_cstr(temp));
|
||||
enqueue_token(ctx, tok);
|
||||
|
||||
return BSHELL_SUCCESS;
|
||||
}
|
||||
|
||||
static enum bshell_status word_begin(struct bshell_lex_ctx *ctx)
|
||||
{
|
||||
struct bshell_lex_token *tok = bshell_lex_token_create(BSHELL_TOK_WORD_START);
|
||||
if (!tok) {
|
||||
return BSHELL_ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
enqueue_token_with_coordinates(
|
||||
ctx,
|
||||
tok,
|
||||
&ctx->lex_start,
|
||||
&ctx->lex_start);
|
||||
return BSHELL_SUCCESS;
|
||||
}
|
||||
|
||||
static enum bshell_status word_end(struct bshell_lex_ctx *ctx)
|
||||
{
|
||||
struct bshell_lex_token *tok = bshell_lex_token_create(BSHELL_TOK_WORD_END);
|
||||
if (!tok) {
|
||||
return BSHELL_ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
enqueue_token_with_coordinates(ctx, tok, &ctx->lex_end, &ctx->lex_end);
|
||||
return BSHELL_SUCCESS;
|
||||
}
|
||||
|
||||
static enum bshell_status word_pump_token(struct bshell_lex_ctx *ctx)
|
||||
{
|
||||
fx_wchar c = peek_char(ctx);
|
||||
|
||||
if (fx_wchar_is_space(c)) {
|
||||
lex_state_pop(ctx);
|
||||
return BSHELL_SUCCESS;
|
||||
}
|
||||
|
||||
if (char_has_flags(ctx, c, BSHELL_LEX_TOKEN_TERMINATES_WORD)) {
|
||||
lex_state_pop(ctx);
|
||||
return BSHELL_SUCCESS;
|
||||
}
|
||||
|
||||
if (char_can_begin_symbol(ctx, c)) {
|
||||
return word_symbol(ctx);
|
||||
}
|
||||
|
||||
return word_content(ctx);
|
||||
}
|
||||
|
||||
static const unsigned int symbols[] = {
|
||||
BSHELL_SYM_AMPERSAND,
|
||||
BSHELL_SYM_HASH,
|
||||
BSHELL_SYM_DOLLAR,
|
||||
BSHELL_SYM_DOLLAR_LEFT_PAREN,
|
||||
BSHELL_SYM_DOLLAR_LEFT_BRACE,
|
||||
BSHELL_SYM_PIPE,
|
||||
BSHELL_SYM_COMMA,
|
||||
BSHELL_SYM_SEMICOLON,
|
||||
BSHELL_SYM_LEFT_BRACE,
|
||||
BSHELL_SYM_RIGHT_BRACE,
|
||||
BSHELL_SYM_LEFT_PAREN,
|
||||
BSHELL_SYM_RIGHT_PAREN,
|
||||
BSHELL_SYM_NONE,
|
||||
};
|
||||
|
||||
const struct bshell_lex_state_type lex_word_state = {
|
||||
.s_id = BSHELL_LEX_STATE_WORD,
|
||||
.s_begin = word_begin,
|
||||
.s_end = word_end,
|
||||
.s_pump_token = word_pump_token,
|
||||
.s_symbols = symbols,
|
||||
};
|
||||
Reference in New Issue
Block a user