aboutsummaryrefslogtreecommitdiffstats
path: root/parser.c
diff options
context:
space:
mode:
Diffstat (limited to 'parser.c')
-rw-r--r--parser.c271
1 files changed, 271 insertions, 0 deletions
diff --git a/parser.c b/parser.c
new file mode 100644
index 0000000..c3da716
--- /dev/null
+++ b/parser.c
@@ -0,0 +1,271 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+void
+push_end_of_file(struct parser_context *ctx)
+{
+ push_semicolon(ctx, 1);
+ if (ctx->parser_state->parent || ctx->parser_state->ncommands)
+ ctx->premature_end_of_file = 1;
+}
+
+
+void
+push_whitespace(struct parser_context *ctx, int strict)
+{
+ if (ctx->parser_state->need_right_hand_side) {
+ if (strict)
+ eprintf("premature end of command\n");
+ return;
+ }
+
+ if (ctx->parser_state->current_argument) {
+ ctx->parser_state->arguments = erealloc(ctx->parser_state->arguments,
+ (ctx->parser_state->narguments + 1) *
+ sizeof(*ctx->parser_state->arguments));
+ ctx->parser_state->arguments[ctx->parser_state->narguments++] = ctx->parser_state->current_argument;
+ ctx->parser_state->current_argument = NULL;
+ ctx->parser_state->current_argument_end = NULL;
+ }
+}
+
+
+static void
+push_command_terminal(struct parser_context *ctx, enum command_terminal terminal)
+{
+ struct command *new_command;
+
+ push_whitespace(ctx, 1);
+
+ ctx->parser_state->commands = erealloc(ctx->parser_state->commands,
+ (ctx->parser_state->ncommands + 1) *
+ sizeof(*ctx->parser_state->commands));
+ new_command = ecalloc(1, sizeof(*new_command));
+ ctx->parser_state->commands[ctx->parser_state->ncommands++] = new_command;
+ new_command->terminal = terminal;
+ new_command->arguments = ctx->parser_state->arguments;
+ new_command->narguments = ctx->parser_state->narguments;
+ new_command->redirections = ctx->parser_state->redirections;
+ new_command->nredirections = ctx->parser_state->nredirections;
+ ctx->parser_state->arguments = NULL;
+ ctx->parser_state->narguments = 0;
+ ctx->parser_state->redirections = NULL;
+ ctx->parser_state->nredirections = 0;
+
+ if (!ctx->parser_state->parent) {
+ if (terminal == DOUBLE_SEMICOLON || terminal == SEMICOLON || terminal == AMPERSAND) {
+ /* TODO unless in a special construct such as while, case, for, if, or {, run and clear
+ * also require that any here-document is specified (count them and run when given);
+ * if terminal == AMPERSAND: perform </dev/null first, and reset exist status to 0
+ */
+ }
+ }
+}
+
+
+void
+push_semicolon(struct parser_context *ctx, int maybe)
+{
+ if (!maybe || ctx->parser_state->narguments)
+ push_command_terminal(ctx, SEMICOLON);
+}
+
+
+static void
+push_new_argument_part(struct parser_context *ctx, enum argument_type type)
+{
+ struct argument *new_part;
+
+ new_part = ecalloc(1, sizeof(*new_part));
+ new_part->type = type;
+ new_part->line_number = ctx->tokeniser_line_number;
+
+ if (ctx->parser_state->current_argument_end) {
+ ctx->parser_state->current_argument_end->next_part = new_part;
+ ctx->parser_state->current_argument_end = new_part;
+ } else {
+ ctx->parser_state->current_argument = new_part;
+ ctx->parser_state->current_argument_end = new_part;
+ }
+}
+
+
+static void
+push_redirection(struct parser_context *ctx, enum redirection_type type)
+{
+ struct redirection *new_redirection;
+ struct argument *new_argument;
+ struct here_document *new_here_document;
+
+ new_redirection = ecalloc(1, sizeof(*new_redirection));
+ new_redirection->type = type;
+
+ ctx->parser_state->redirections = erealloc(ctx->parser_state->redirections,
+ (ctx->parser_state->nredirections + 1) *
+ sizeof(*ctx->parser_state->redirections));
+ ctx->parser_state->redirections[ctx->parser_state->nredirections++] = new_redirection;
+
+ if (ctx->parser_state->current_argument) {
+ if (ctx->parser_state->current_argument->type == REDIRECTION ||
+ ctx->parser_state->current_argument_end->type == QUOTED ||
+ ctx->parser_state->current_argument_end->type == QUOTE_EXPRESSION ||
+ type == REDIRECT_OUTPUT_AND_STDERR ||
+ type == REDIRECT_OUTPUT_AND_STDERR_APPEND ||
+ type == REDIRECT_OUTPUT_AND_STDERR_CLOBBER ||
+ type == REDIRECT_OUTPUT_AND_STDERR_TO_FD) {
+ push_whitespace(ctx, 1);
+ } else {
+ new_redirection->left_hand_side = ctx->parser_state->current_argument;
+ }
+ }
+
+ new_argument = ecalloc(1, sizeof(*new_argument));
+ new_argument->type = REDIRECTION;
+ new_argument->line_number = ctx->tokeniser_line_number;
+ ctx->parser_state->current_argument = new_argument;
+
+ if (type == HERE_DOCUMENT || type == HERE_DOCUMENT_INDENTED) {
+ new_here_document = emalloc(sizeof(*new_here_document));
+ new_here_document->redirection = new_redirection;
+ new_here_document->argument = new_argument;
+ new_here_document->next = NULL;
+ *ctx->here_documents_next = new_here_document;
+ ctx->here_documents_next = &new_here_document->next;
+ }
+
+ ctx->parser_state->need_right_hand_side = 1;
+}
+
+
+static void
+push_shell_io(struct parser_context *ctx, enum argument_type type, enum tokeniser_mode mode)
+{
+ push_mode(ctx, mode);
+ push_enter(ctx, type);
+}
+
+
+static void
+push_function_mark(struct parser_context *ctx)
+{
+ push_whitespace(ctx, 1);
+ push_new_argument_part(ctx, FUNCTION_MARK);
+ push_whitespace(ctx, 1);
+}
+
+
+size_t
+push_symbol(struct parser_context *ctx, char *token, size_t token_len)
+{
+#define LIST_SYMBOLS(_)\
+ _("<<<", push_redirection(ctx, HERE_STRING))\
+ _("<<-", push_redirection(ctx, HERE_DOCUMENT_INDENTED))\
+ _("<>(", push_shell_io(ctx, PROCESS_SUBSTITUTION_INPUT_OUTPUT, NORMAL_MODE))\
+ _("<>|", push_command_terminal(ctx, SOCKET_PIPE))\
+ _("<>&", push_redirection(ctx, REDIRECT_INPUT_OUTPUT_TO_FD))\
+ _("&>>", push_redirection(ctx, REDIRECT_OUTPUT_AND_STDERR_APPEND))\
+ _("&>&", push_redirection(ctx, REDIRECT_OUTPUT_AND_STDERR_TO_FD))\
+ _("&>|", push_redirection(ctx, REDIRECT_OUTPUT_AND_STDERR_CLOBBER))\
+ _("()", push_function_mark(ctx))\
+ _("((", push_shell_io(ctx, ARITHMETIC_SUBSHELL, RRB_QUOTE_MODE))\
+ _(";;", push_command_terminal(ctx, DOUBLE_SEMICOLON))\
+ _("<(", push_shell_io(ctx, PROCESS_SUBSTITUTION_OUTPUT, NORMAL_MODE))\
+ _("<<", push_redirection(ctx, HERE_DOCUMENT))\
+ _("<>", push_redirection(ctx, REDIRECT_INPUT_OUTPUT))\
+ _("<&", push_redirection(ctx, REDIRECT_INPUT_TO_FD))\
+ _(">(", push_shell_io(ctx, PROCESS_SUBSTITUTION_INPUT, NORMAL_MODE))\
+ _(">>", push_redirection(ctx, REDIRECT_OUTPUT_APPEND))\
+ _(">&", push_redirection(ctx, REDIRECT_OUTPUT_TO_FD))\
+ _(">|", push_redirection(ctx, REDIRECT_OUTPUT_CLOBBER))\
+ _("||", push_command_terminal(ctx, OR))\
+ _("|&", push_command_terminal(ctx, PIPE_AMPERSAND))\
+ _("&&", push_command_terminal(ctx, AND))\
+ _("&|", push_command_terminal(ctx, PIPE_AMPERSAND)) /* synonym for |& to match &> */\
+ _("&>", push_redirection(ctx, REDIRECT_OUTPUT_AND_STDERR))\
+ _("(", push_shell_io(ctx, SUBSHELL, NORMAL_MODE))\
+ _(";", push_semicolon(ctx, 0))\
+ _("<", push_redirection(ctx, REDIRECT_INPUT))\
+ _(">", push_redirection(ctx, REDIRECT_OUTPUT))\
+ _("|", push_command_terminal(ctx, PIPE))\
+ _("&", push_command_terminal(ctx, AMPERSAND))
+
+#define X(SYMBOL, ACTION)\
+ if (token_len >= sizeof(SYMBOL) - 1 && !strncmp(token, SYMBOL, sizeof(SYMBOL) - 1)) {\
+ ACTION;\
+ return token_len;\
+ }
+ LIST_SYMBOLS(X)
+#undef X
+
+ push_unquoted(ctx, token, 1);
+ return 1;
+}
+
+
+static void
+push_text(struct parser_context *ctx, char *text, size_t text_len, enum argument_type type)
+{
+ struct argument *arg_part;
+
+ ctx->parser_state->need_right_hand_side = 0;
+
+ if (!ctx->parser_state->current_argument_end ||
+ ctx->parser_state->current_argument_end->type != type ||
+ ctx->parser_state->current_argument_end->line_number != ctx->tokeniser_line_number)
+ push_new_argument_part(ctx, type);
+ arg_part = ctx->parser_state->current_argument_end;
+
+ arg_part->text = erealloc(arg_part->text, arg_part->length + text_len + 1);
+ memcpy(&arg_part->text[arg_part->length], text, text_len);
+ arg_part->length += text_len;
+ arg_part->text[arg_part->length] = '\0';
+}
+
+
+void
+push_quoted(struct parser_context *ctx, char *text, size_t text_len)
+{
+ push_text(ctx, text, text_len, QUOTED);
+}
+
+
+void
+push_escaped(struct parser_context *ctx, char *text, size_t text_len)
+{
+ /* TODO resolve backslashes in text */
+ push_text(ctx, text, text_len, QUOTED);
+}
+
+
+void
+push_unquoted(struct parser_context *ctx, char *text, size_t text_len)
+{
+ push_text(ctx, text, text_len, UNQUOTED);
+}
+
+
+void
+push_enter(struct parser_context *ctx, enum argument_type type)
+{
+ struct parser_state *new_state;
+
+ ctx->parser_state->need_right_hand_side = 0;
+ push_new_argument_part(ctx, type);
+
+ new_state = ecalloc(1, sizeof(*new_state));
+ new_state->parent = ctx->parser_state;
+ ctx->parser_state->current_argument_end->child = new_state;
+ ctx->parser_state = new_state;
+}
+
+
+void
+push_leave(struct parser_context *ctx)
+{
+ if (ctx->mode_stack->mode == NORMAL_MODE)
+ push_semicolon(ctx, 1);
+ /* TODO else if (ctx->mode_stack->mode == BQ_QUOTE_MODE), parse content */
+ /* TODO validate subshell content */
+ ctx->parser_state = ctx->parser_state->parent;
+}