diff --git a/ce/ce.c b/ce/ce.c index 877d631..c1a1d52 100644 --- a/ce/ce.c +++ b/ce/ce.c @@ -14,22 +14,176 @@ #include #include +static struct arena arena; + #define LINE_BUFFER_MAX 1024 #define TOKEN_MAX 64 +#define CMD_ARGS_MAX 64 #define PROMPT "ce $ " +#define TOKEN_CLASS_OPAREN 0 +#define TOKEN_CLASS_CPAREN 1 +#define TOKEN_CLASS_WORD 2 +#define TOKEN_CLASS_SEMICOLON 3 + struct token { struct list_node_link tokens_link; char buffer[TOKEN_MAX]; + int class; }; +#define PREC_NONE 0 +#define PREC_SEQ 1 +#define PREC_LOWEST 2 + +#define AST_NODE_CLASS_CMD 0 +#define AST_NODE_CLASS_SUBSHELL 1 +#define AST_NODE_CLASS_SEQ 2 + +struct ast_node; + +struct ast_cmd { + char* name; + char* args[CMD_ARGS_MAX]; + int arg_count; +}; + +struct ast_subshell { + struct ast_node* inner; +}; + +struct ast_seq { + struct ast_node* left; + struct ast_node* right; +}; + +struct ast_node { + int class; + union { + struct ast_cmd cmd; + struct ast_subshell subshell; + struct ast_seq seq; + } u; +}; + +struct parser; + +typedef struct ast_node* (*nud_func_t) (struct parser* parser, struct token* token); +typedef struct ast_node* (*led_func_t) (struct parser* parser, struct token* token, + struct ast_node* left); + +struct parse_rule { + nud_func_t nud; + led_func_t led; + int precedence; +}; + +struct parser { + struct token* current; + struct token* next; +}; + +static struct ast_node* word_nud (struct parser* parser, struct token* token); + +static struct ast_node* oparen_nud (struct parser* parser, struct token* token); + +static struct ast_node* semicolon_led (struct parser* parser, struct token* token, + struct ast_node* left); + +static struct parse_rule parse_rules[] = { + [TOKEN_CLASS_WORD] = {&word_nud, NULL, PREC_NONE}, + [TOKEN_CLASS_OPAREN] = {&oparen_nud, NULL, PREC_NONE}, + [TOKEN_CLASS_CPAREN] = {NULL, NULL, PREC_NONE}, + [TOKEN_CLASS_SEMICOLON] = {NULL, &semicolon_led, PREC_SEQ}, +}; + +static struct token* get_token (struct list_node_link* link) { + if (link == NULL) + return NULL; + + return list_entry (link, struct token, tokens_link); +} + +static struct token* advance (struct parser* parser) { + struct token* token = parser->next; + + if (token != NULL) { + parser->current = token; + parser->next = get_token (token->tokens_link.next); + } else { + parser->current = NULL; + } + + return token; +} + +static struct ast_node* parse_precedence (struct parser* parser, int precedence) { + struct token* token = advance (parser); + + if (token == NULL) + return NULL; + + nud_func_t nud = parse_rules[token->class].nud; + + if (nud == NULL) + return NULL; + + struct ast_node* left = nud (parser, token); + + while (parser->next && precedence < parse_rules[parser->next->class].precedence) { + token = advance (parser); + led_func_t led = parse_rules[token->class].led; + + if (led != NULL) + left = led (parser, token, left); + } + + return left; +} + +static struct ast_node* word_nud (struct parser* parser, struct token* token) { + struct ast_node* node = arena_malloc (&arena, sizeof (*node)); + node->class = AST_NODE_CLASS_CMD; + node->u.cmd.name = token->buffer; + node->u.cmd.arg_count = 0; + + while (parser->next != NULL && parser->next->class == TOKEN_CLASS_WORD) { + struct token* arg = advance (parser); + + if (node->u.cmd.arg_count < CMD_ARGS_MAX) + node->u.cmd.args[node->u.cmd.arg_count++] = arg->buffer; + } + + return node; +} + +static struct ast_node* oparen_nud (struct parser* parser, struct token* token) { + struct ast_node* node = arena_malloc (&arena, sizeof (*node)); + node->class = AST_NODE_CLASS_SUBSHELL; + + node->u.subshell.inner = parse_precedence (parser, PREC_LOWEST); + + if (parser->next != NULL && parser->next->class == TOKEN_CLASS_CPAREN) + advance (parser); + + return node; +} + +static struct ast_node* semicolon_led (struct parser* parser, struct token* token, + struct ast_node* left) { + struct ast_node* node = arena_malloc (&arena, sizeof (*node)); + node->class = AST_NODE_CLASS_SEQ; + node->u.seq.left = left; + node->u.seq.right = parse_precedence (parser, PREC_SEQ); + + return node; +} + static int e_pid; static int e_pgid; static bool run = true; -static struct arena arena; - static void putch (char ch) { mail_send (e_pgid, &ch, 1); } void putchar_ (char ch) { putch (ch); } @@ -38,9 +192,6 @@ static bool tokenize_line (void* ctx, const char* start, size_t len) { struct token* token = arena_malloc (&arena, sizeof (*token)); - if (token == NULL) - return false; - memset (token, 0, sizeof (*token)); memcpy (token->buffer, start, min (sizeof (token->buffer) - 1, len)); @@ -49,32 +200,55 @@ static bool tokenize_line (void* ctx, const char* start, size_t len) { return true; } -static void cmd_echo (struct list_node_link* tokens) { +static void classify_tokens (struct list_node_link* tokens) { struct list_node_link *token_link, *token_tmp_link; list_foreach (tokens, token_link, token_tmp_link) { struct token* token = list_entry (token_link, struct token, tokens_link); - printf ("%s ", token->buffer); + if (strcmp (token->buffer, "(") == 0) { + token->class = TOKEN_CLASS_OPAREN; + } else if (strcmp (token->buffer, ")") == 0) { + token->class = TOKEN_CLASS_CPAREN; + } else if (strcmp (token->buffer, ";") == 0) { + token->class = TOKEN_CLASS_SEMICOLON; + } else { + token->class = TOKEN_CLASS_WORD; + } } } -static void cmd_cat (struct list_node_link* tokens) { - if (tokens == NULL) { - printf ("ERROR: no file paths provided\n"); - return; - } +static void execute (struct ast_node* root); +static void parse_tokens (struct list_node_link* tokens) { + struct parser parser; + parser.current = NULL; + parser.next = get_token (tokens); + + while (parser.next != NULL) { + struct ast_node* root = parse_precedence (&parser, PREC_LOWEST); + + if (root != NULL) { + execute (root); + } + } +} + +static void echo (char** strings, size_t strings_count) { + for (size_t i = 0; i < strings_count; i++) + printf ("%s\n", strings[i]); +} + +static void cat (char** file_paths, size_t files_count) { struct desc desc; char volume[VOLUME_MAX]; const char* path; int ret; - struct list_node_link *token_link, *token_tmp_link; - list_foreach (tokens, token_link, token_tmp_link) { - struct token* token = list_entry (token_link, struct token, tokens_link); + for (size_t i = 0; i < files_count; i++) { + const char* file_path = file_paths[i]; - if (!path_parse (token->buffer, volume, &path)) { - printf ("ERROR bad path '%s'\n", token->buffer); + if (!path_parse (file_path, volume, &path)) { + printf ("ERROR bad path '%s'\n", file_path); continue; } @@ -104,21 +278,15 @@ static void cmd_cat (struct list_node_link* tokens) { } } -static void cmd_ls (struct list_node_link* tokens) { - if (tokens == NULL) { - printf ("ERROR no directory path provided\n"); - return; - } - - struct token* token = list_entry (tokens, struct token, tokens_link); +static void ls (const char* path_string) { struct desc desc; char volume[VOLUME_MAX]; const char* path; int ret; struct dir_entry entry; - if (!path_parse (token->buffer, volume, &path)) { - printf ("ERROR bad path '%s'\n", token->buffer); + if (!path_parse (path_string, volume, &path)) { + printf ("ERROR bad path '%s'\n", path_string); return; } @@ -144,12 +312,12 @@ static void cmd_ls (struct list_node_link* tokens) { volume_close (); } -static void cmd_quit (void) { +static void quit1 (void) { run = false; printf ("Goodbye!\n"); } -static void cmd_help (void) { +static void help (void) { printf ("Available commands:\n"); printf ("echo ...\n"); printf ("help\n"); @@ -158,21 +326,34 @@ static void cmd_help (void) { printf ("quit\n"); } -static void exec_tokens (struct list_node_link* tokens) { - struct token* cmd_token = list_entry (tokens, struct token, tokens_link); - - if (strcmp (cmd_token->buffer, "echo") == 0) { - cmd_echo (tokens->next); - } else if (strcmp (cmd_token->buffer, "help") == 0) { - cmd_help (); - } else if (strcmp (cmd_token->buffer, "cat") == 0) { - cmd_cat (tokens->next); - } else if (strcmp (cmd_token->buffer, "ls") == 0) { - cmd_ls (tokens->next); - } else if (strcmp (cmd_token->buffer, "quit") == 0) { - cmd_quit (); +static void execute_cmd (struct ast_cmd* cmd) { + if (strcmp (cmd->name, "echo") == 0) { + echo (cmd->args, cmd->arg_count); + } else if (strcmp (cmd->name, "help") == 0) { + help (); + } else if (strcmp (cmd->name, "cat") == 0) { + cat (cmd->args, cmd->arg_count); + } else if (strcmp (cmd->name, "ls") == 0) { + ls (cmd->args[0]); + } else if (strcmp (cmd->name, "quit") == 0) { + quit1 (); } else { - printf ("ERROR: unknown command '%s'\n", cmd_token->buffer); + printf ("ERROR unknown command '%s'\n", cmd->name); + } +} + +static void execute (struct ast_node* root) { + switch (root->class) { + case AST_NODE_CLASS_CMD: + execute_cmd (&root->u.cmd); + break; + case AST_NODE_CLASS_SUBSHELL: + execute (root->u.subshell.inner); + break; + case AST_NODE_CLASS_SEQ: + execute (root->u.seq.left); + execute (root->u.seq.right); + break; } } @@ -181,8 +362,10 @@ static void exec_line (const char* line) { strtokenize (line, ' ', &tokens, &tokenize_line); - if (tokens != NULL) - exec_tokens (tokens); + if (tokens != NULL) { + classify_tokens (tokens); + parse_tokens (tokens); + } arena_reset (&arena); }