#include "../syntax.h" #include static bool parse_fword(struct bshell_parse_ctx *ctx, struct bshell_ast_node **out) { if (peek_token_type(ctx) != BSHELL_TOK_WORD_START) { return false; } discard_token(ctx); struct bshell_fstring_ast_node *fstring = (struct bshell_fstring_ast_node *)bshell_ast_node_create( BSHELL_AST_FSTRING); if (!fstring) { ctx->p_status = BSHELL_ERR_NO_MEMORY; return false; } bool ok = true; while (ok) { if (peek_token_type(ctx) == BSHELL_TOK_WORD_END) { discard_token(ctx); break; } struct bshell_ast_node *item = NULL; if (peek_token_type(ctx) == BSHELL_TOK_WORD) { struct bshell_word_ast_node *n = (struct bshell_word_ast_node *) bshell_ast_node_create(BSHELL_AST_WORD); if (!n) { ctx->p_status = BSHELL_ERR_NO_MEMORY; ok = false; break; } n->n_value = claim_token(ctx); item = (struct bshell_ast_node *)n; } else { if (!parse_arith_value(ctx, &item)) { ok = false; break; } } fx_queue_push_back(&fstring->n_elements, &item->n_entry); } if (!ok) { bshell_ast_node_destroy((struct bshell_ast_node *)fstring); fstring = NULL; } *out = (struct bshell_ast_node *)fstring; return ok; return false; } static bool parse_cmdcall_arg( struct bshell_parse_ctx *ctx, struct bshell_ast_node **out) { if (ctx->p_status != BSHELL_SUCCESS) { return false; } struct bshell_lex_token *tok = peek_token(ctx); if (!tok) { return false; } struct bshell_ast_node *arg = NULL; switch (tok->tok_type) { case BSHELL_TOK_WORD_START: return parse_fword(ctx, out); case BSHELL_TOK_STR_START: return parse_fstring(ctx, out); case BSHELL_TOK_WORD: { struct bshell_word_ast_node *n = (struct bshell_word_ast_node *)bshell_ast_node_create( BSHELL_AST_WORD); if (!n) { ctx->p_status = BSHELL_ERR_NO_MEMORY; return false; } n->n_value = claim_token(ctx); *out = (struct bshell_ast_node *)n; return true; } case BSHELL_TOK_VAR: { struct bshell_var_ast_node *n = (struct bshell_var_ast_node *)bshell_ast_node_create( BSHELL_AST_VAR); if (!n) { ctx->p_status = BSHELL_ERR_NO_MEMORY; return false; } n->n_ident = claim_token(ctx); *out = (struct bshell_ast_node *)n; return true; } case BSHELL_TOK_VAR_SPLAT: { struct bshell_var_splat_ast_node *n = (struct bshell_var_splat_ast_node *) bshell_ast_node_create(BSHELL_AST_VAR_SPLAT); if (!n) { ctx->p_status = BSHELL_ERR_NO_MEMORY; return false; } n->n_ident = claim_token(ctx); *out = (struct bshell_ast_node *)n; return true; } case BSHELL_TOK_STRING: { struct bshell_string_ast_node *n = (struct bshell_string_ast_node *) bshell_ast_node_create(BSHELL_AST_STRING); if (!n) { ctx->p_status = BSHELL_ERR_NO_MEMORY; return false; } n->n_value = claim_token(ctx); *out = (struct bshell_ast_node *)n; return true; } case BSHELL_TOK_SYMBOL: switch (tok->tok_symbol) { case BSHELL_SYM_LEFT_PAREN: case BSHELL_SYM_LEFT_BRACE: case BSHELL_SYM_DOLLAR_LEFT_PAREN: case BSHELL_SYM_AT_LEFT_BRACE: case BSHELL_SYM_AT_LEFT_PAREN: return parse_arith_value(ctx, out); default: report_error( ctx, "encountered unsupported command arg"); return false; } break; default: report_error(ctx, "encountered unsupported command arg"); return false; } return true; } static bool parse_redirect_to_fd( struct bshell_parse_ctx *ctx, unsigned int in_fd, bool append, struct bshell_ast_node **out) { if (ctx->p_status != BSHELL_SUCCESS) { return false; } struct bshell_redirection_ast_node *redirect = (struct bshell_redirection_ast_node *)bshell_ast_node_create( BSHELL_AST_REDIRECTION); redirect->n_in = in_fd; redirect->n_append = append; if (!parse_symbol(ctx, BSHELL_SYM_AMPERSAND)) { bshell_ast_node_destroy((struct bshell_ast_node *)redirect); return false; } struct bshell_lex_token *out_tok = NULL; struct bshell_ast_node *out_expr = NULL; long long out_fd = -1; if (peek_word(ctx, &out_tok)) { const char *s = out_tok->tok_str; char *ep; out_fd = strtoll(s, &ep, 10); if (*ep == '\0') { discard_token(ctx); out_tok = NULL; } else { out_fd = -1; } } else if (!parse_cmdcall_arg(ctx, &out_expr)) { return false; } redirect->n_out_is_fd = (out_fd >= 0) || out_expr; redirect->n_out_is_expr = out_expr != NULL; redirect->n_out = (unsigned int)out_fd; redirect->n_out_path_expr = out_expr; if (out_tok) { redirect->n_out_tok = claim_token(ctx); redirect->n_out_path = out_tok->tok_str; } *out = (struct bshell_ast_node *)redirect; return true; } static bool parse_redirect_to_file_squashed( struct bshell_parse_ctx *ctx, unsigned int in_fd, bool append, const char *str, struct bshell_ast_node **out) { if (ctx->p_status != BSHELL_SUCCESS) { return false; } struct bshell_lex_token *tok = peek_token(ctx); if (*str == '\0') { return false; } struct bshell_redirection_ast_node *redirect = (struct bshell_redirection_ast_node *)bshell_ast_node_create( BSHELL_AST_REDIRECTION); redirect->n_in = in_fd; redirect->n_append = append; redirect->n_out_is_fd = false; redirect->n_out_is_expr = false; redirect->n_out_path = str; redirect->n_out_tok = claim_token(ctx); *out = (struct bshell_ast_node *)redirect; return true; } static bool parse_redirect_to_file_separate( struct bshell_parse_ctx *ctx, unsigned int in_fd, bool append, struct bshell_ast_node **out) { if (ctx->p_status != BSHELL_SUCCESS) { return false; } struct bshell_ast_node *out_path = NULL; if (!parse_cmdcall_arg(ctx, &out_path)) { ctx->p_status = BSHELL_ERR_BAD_SYNTAX; return false; } struct bshell_redirection_ast_node *redirect = (struct bshell_redirection_ast_node *)bshell_ast_node_create( BSHELL_AST_REDIRECTION); redirect->n_in = in_fd; redirect->n_append = append; redirect->n_out_is_fd = false; redirect->n_out_is_expr = true; redirect->n_out_path_expr = out_path; *out = (struct bshell_ast_node *)redirect; return true; } bool parse_redirect(struct bshell_parse_ctx *ctx, struct bshell_ast_node **out) { struct bshell_lex_token *tok = peek_token(ctx); if (!tok || tok->tok_type != BSHELL_TOK_WORD) { return false; } unsigned int in_fd = 1; const char *str = tok->tok_str; bool append = false; if (fx_wchar_is_number(*str)) { in_fd = 0; while (fx_wchar_is_number(*str)) { in_fd *= 10; in_fd += *str - '0'; str++; } } if (*str != '>') { return false; } str++; if (*str == '>') { append = true; str++; } if (*str != '\0') { return parse_redirect_to_file_squashed( ctx, in_fd, append, str, out); } discard_token(ctx); if (parse_redirect_to_fd(ctx, in_fd, append, out)) { return true; } if (parse_redirect_to_file_separate(ctx, in_fd, append, out)) { return true; } return false; } static bool peek_cmdcall_item(struct bshell_parse_ctx *ctx, bool unrestricted) { /* each token type falls into one of three categories: * - cmdcall item: the token can be used as part of a command call. the * token indicates the start of a command call. * - NOT a cmdcall item: the token cannot be used as part of a command * call, usually because it as a cmdcall operator like | or &. * encountering one of these tokens ends the cmdcall currently being * parsed. * - RESTRICTED cmdcall item: the token can be used as part of a * command, but will not be considered the start of a cmdcall. to run * a command with this token as its name, the call operator must be * used. */ switch (peek_token_type(ctx)) { case BSHELL_TOK_KEYWORD: case BSHELL_TOK_INT: case BSHELL_TOK_DOUBLE: case BSHELL_TOK_VAR: case BSHELL_TOK_VAR_SPLAT: case BSHELL_TOK_STRING: case BSHELL_TOK_WORD_START: return unrestricted; case BSHELL_TOK_SYMBOL: switch (peek_unknown_symbol(ctx)) { case BSHELL_SYM_PLUS: case BSHELL_SYM_HYPHEN: return unrestricted; case BSHELL_SYM_PIPE: case BSHELL_SYM_AMPERSAND: case BSHELL_SYM_SEMICOLON: case BSHELL_SYM_RIGHT_PAREN: case BSHELL_SYM_RIGHT_BRACE: case BSHELL_SYM_RIGHT_BRACKET: return false; default: return true; } case BSHELL_TOK_NONE: case BSHELL_TOK_LINEFEED: return false; default: return true; } } bool parse_cmdcall(struct bshell_parse_ctx *ctx, struct bshell_ast_node **out) { struct bshell_cmdcall_ast_node *node = (struct bshell_cmdcall_ast_node *)bshell_ast_node_create( BSHELL_AST_CMDCALL); if (!node) { ctx->p_status = BSHELL_ERR_NO_MEMORY; return false; } struct bshell_ast_node *child = NULL; bool unrestricted = false; bool ok = true; bool stop = false; if (parse_symbol(ctx, BSHELL_SYM_AMPERSAND)) { unrestricted = true; } if (!peek_cmdcall_item(ctx, unrestricted)) { return false; } struct bshell_lex_token *tok = peek_token(ctx); if (!tok) { return false; } if (!parse_cmdcall_arg(ctx, &child)) { return false; } fx_queue_push_back(&node->n_args, &child->n_entry); while (ok && !stop) { if (!peek_cmdcall_item(ctx, true)) { break; } struct bshell_lex_token *tok = peek_token(ctx); if (!tok) { break; } if (parse_redirect(ctx, &child)) { fx_queue_push_back(&node->n_redirect, &child->n_entry); } else if (parse_cmdcall_arg(ctx, &child)) { fx_queue_push_back(&node->n_args, &child->n_entry); } else { ctx->p_status = BSHELL_ERR_BAD_SYNTAX; ok = false; break; } } if (!ok) { bshell_ast_node_destroy((struct bshell_ast_node *)node); node = NULL; } *out = (struct bshell_ast_node *)node; return ok; } bool peek_command(struct bshell_parse_ctx *ctx) { if (peek_symbol(ctx, BSHELL_SYM_AMPERSAND)) { return true; } return peek_cmdcall_item(ctx, false); } bool parse_command(struct bshell_parse_ctx *ctx, struct bshell_ast_node **out) { struct bshell_ast_node *cmdcall = NULL; if (!parse_cmdcall(ctx, &cmdcall)) { return false; } struct bshell_pipeline_ast_node *pipeline = NULL; while (1) { if (peek_symbol(ctx, BSHELL_SYM_SEMICOLON) || peek_linefeed(ctx)) { break; } if (!parse_symbol(ctx, BSHELL_SYM_PIPE)) { break; } if (!pipeline) { pipeline = (struct bshell_pipeline_ast_node *) bshell_ast_node_create(BSHELL_AST_PIPELINE); if (!pipeline) { ctx->p_status = BSHELL_ERR_NO_MEMORY; bshell_ast_node_destroy(cmdcall); return false; } fx_queue_push_back( &pipeline->n_stages, &cmdcall->n_entry); } if (!parse_cmdcall(ctx, &cmdcall)) { ctx->p_status = BSHELL_ERR_BAD_SYNTAX; return false; } fx_queue_push_back(&pipeline->n_stages, &cmdcall->n_entry); } if (pipeline) { *out = (struct bshell_ast_node *)pipeline; } else { *out = cmdcall; } return true; } bool parse_pipeline( struct bshell_parse_ctx *ctx, struct bshell_ast_node *first_item, struct bshell_ast_node **out) { struct bshell_pipeline_ast_node *pipeline = (struct bshell_pipeline_ast_node *)bshell_ast_node_create( BSHELL_AST_PIPELINE); fx_queue_push_back(&pipeline->n_stages, &first_item->n_entry); while (1) { if (!parse_symbol(ctx, BSHELL_SYM_PIPE)) { break; } struct bshell_ast_node *cmdcall = NULL; if (!parse_cmdcall(ctx, &cmdcall)) { ctx->p_status = BSHELL_ERR_BAD_SYNTAX; return false; } fx_queue_push_back(&pipeline->n_stages, &cmdcall->n_entry); } *out = (struct bshell_ast_node *)pipeline; return true; }