#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static struct arena arena; #define LINE_BUFFER_MAX 1024 #define TOKEN_MAX 64 #define CMD_ARGS_MAX 64 #define PROMPT "$ " #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 void putch (char ch) { mail_send (e_pgid, &ch, 1); } void putchar_ (char ch) { putch (ch); } static bool tokenize_line (void* ctx, const char* start, size_t len) { struct list_node_link** head = ctx; struct token* token = arena_malloc (&arena, sizeof (*token)); memset (token, 0, sizeof (*token)); memcpy (token->buffer, start, min (sizeof (token->buffer) - 1, len)); list_append ((*head), &token->tokens_link); return true; } 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); 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 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 ", strings[i]); } static void mkfile (char** file_paths, size_t files_count) { char volume[VOLUME_MAX]; const char* path; int ret; for (size_t i = 0; i < files_count; i++) { const char* file_path = file_paths[i]; if (!path_parse (file_path, volume, &path)) { printf ("ERROR bad path '%s'\n", file_path); continue; } if ((ret = volume_open (volume)) < 0) { printf ("ERROR could not open volume '%s': %s\n", volume, str_status[-ret]); continue; } create_file (path); volume_close (); } } static void cat (char** file_paths, size_t files_count) { struct desc desc; char volume[VOLUME_MAX]; const char* path; int ret; for (size_t i = 0; i < files_count; i++) { const char* file_path = file_paths[i]; if (!path_parse (file_path, volume, &path)) { printf ("ERROR bad path '%s'\n", file_path); continue; } if ((ret = volume_open (volume)) < 0) { printf ("ERROR could not open volume '%s': %s\n", volume, str_status[-ret]); continue; } describe (path, &desc); if (desc.type != FS_FILE) goto close; char* buffer = malloc (desc.size + 1); if (buffer == NULL) goto close; memset (buffer, 0, desc.size + 1); read_file (path, 0, (uint8_t*)buffer, desc.size); printf ("%s\n", buffer); close: if (buffer != NULL) free (buffer); volume_close (); } } 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 (path_string, volume, &path)) { printf ("ERROR bad path '%s'\n", path_string); return; } if ((ret = volume_open (volume)) < 0) { printf ("ERROR could not open volume '%s': %s\n", volume, str_status[-ret]); return; } describe (path, &desc); if (desc.type != FS_DIR) { printf ("ERROR '%s' is not a directory\n", path_string); volume_close (); return; } size_t entries = desc.size; printf ("total entries: %zu\n", entries); printf ("\n"); for (size_t entry_num = 0; entry_num < entries; entry_num++) { read_dir_entry (path, &entry, entry_num); describe (entry.path, &desc); printf ("%-40s%c %-40zu\n", entry.path, (desc.type == FS_DIR ? '*' : ' '), desc.size); } volume_close (); } static void quit1 (void) { run = false; printf ("Goodbye!\n"); } static void help (void) { printf ("Available commands:\n"); printf ("echo ...\n"); printf ("help\n"); printf ("cat \n"); printf ("ls \n"); printf ("mkfile \n"); printf ("quit\n"); } 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 if (strcmp (cmd->name, "mkfile") == 0) { mkfile (cmd->args, cmd->arg_count); } else { 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; } } static void exec_line (const char* line) { struct list_node_link* tokens = NULL; strtokenize (line, ' ', &tokens, &tokenize_line); if (tokens != NULL) { classify_tokens (tokens); parse_tokens (tokens); } arena_reset (&arena); } void app_main (void) { e_pid = get_exec_pid (); e_pgid = get_procgroup (e_pid); size_t line_cursor = 0; char line_buffer[LINE_BUFFER_MAX + 1]; memset (line_buffer, 0, sizeof (line_buffer)); while (run) { printf (PROMPT); char ch = 0; for (;;) { mail_receive (&ch, 1); if (ch == '\n') break; if (line_cursor < LINE_BUFFER_MAX) { line_buffer[line_cursor++] = ch; printf ("%c", ch); } } printf ("\n"); exec_line (line_buffer); line_cursor = 0; memset (line_buffer, 0, sizeof (line_buffer)); } arena_destroy (&arena); }