CE implement a pratt parser
All checks were successful
Build documentation / build-and-deploy (push) Successful in 2m39s
All checks were successful
Build documentation / build-and-deploy (push) Successful in 2m39s
This commit is contained in:
271
ce/ce.c
271
ce/ce.c
@@ -14,22 +14,176 @@
|
|||||||
#include <system.h>
|
#include <system.h>
|
||||||
#include <terminal_cursor.h>
|
#include <terminal_cursor.h>
|
||||||
|
|
||||||
|
static struct arena arena;
|
||||||
|
|
||||||
#define LINE_BUFFER_MAX 1024
|
#define LINE_BUFFER_MAX 1024
|
||||||
#define TOKEN_MAX 64
|
#define TOKEN_MAX 64
|
||||||
|
#define CMD_ARGS_MAX 64
|
||||||
#define PROMPT "ce $ "
|
#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 token {
|
||||||
struct list_node_link tokens_link;
|
struct list_node_link tokens_link;
|
||||||
char buffer[TOKEN_MAX];
|
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_pid;
|
||||||
static int e_pgid;
|
static int e_pgid;
|
||||||
|
|
||||||
static bool run = true;
|
static bool run = true;
|
||||||
|
|
||||||
static struct arena arena;
|
|
||||||
|
|
||||||
static void putch (char ch) { mail_send (e_pgid, &ch, 1); }
|
static void putch (char ch) { mail_send (e_pgid, &ch, 1); }
|
||||||
void putchar_ (char ch) { putch (ch); }
|
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));
|
struct token* token = arena_malloc (&arena, sizeof (*token));
|
||||||
|
|
||||||
if (token == NULL)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
memset (token, 0, sizeof (*token));
|
memset (token, 0, sizeof (*token));
|
||||||
memcpy (token->buffer, start, min (sizeof (token->buffer) - 1, len));
|
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;
|
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;
|
struct list_node_link *token_link, *token_tmp_link;
|
||||||
list_foreach (tokens, token_link, token_tmp_link) {
|
list_foreach (tokens, token_link, token_tmp_link) {
|
||||||
struct token* token = list_entry (token_link, struct token, tokens_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) {
|
static void execute (struct ast_node* root);
|
||||||
if (tokens == NULL) {
|
|
||||||
printf ("ERROR: no file paths provided\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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;
|
struct desc desc;
|
||||||
char volume[VOLUME_MAX];
|
char volume[VOLUME_MAX];
|
||||||
const char* path;
|
const char* path;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
struct list_node_link *token_link, *token_tmp_link;
|
for (size_t i = 0; i < files_count; i++) {
|
||||||
list_foreach (tokens, token_link, token_tmp_link) {
|
const char* file_path = file_paths[i];
|
||||||
struct token* token = list_entry (token_link, struct token, tokens_link);
|
|
||||||
|
|
||||||
if (!path_parse (token->buffer, volume, &path)) {
|
if (!path_parse (file_path, volume, &path)) {
|
||||||
printf ("ERROR bad path '%s'\n", token->buffer);
|
printf ("ERROR bad path '%s'\n", file_path);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,21 +278,15 @@ static void cmd_cat (struct list_node_link* tokens) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cmd_ls (struct list_node_link* tokens) {
|
static void ls (const char* path_string) {
|
||||||
if (tokens == NULL) {
|
|
||||||
printf ("ERROR no directory path provided\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct token* token = list_entry (tokens, struct token, tokens_link);
|
|
||||||
struct desc desc;
|
struct desc desc;
|
||||||
char volume[VOLUME_MAX];
|
char volume[VOLUME_MAX];
|
||||||
const char* path;
|
const char* path;
|
||||||
int ret;
|
int ret;
|
||||||
struct dir_entry entry;
|
struct dir_entry entry;
|
||||||
|
|
||||||
if (!path_parse (token->buffer, volume, &path)) {
|
if (!path_parse (path_string, volume, &path)) {
|
||||||
printf ("ERROR bad path '%s'\n", token->buffer);
|
printf ("ERROR bad path '%s'\n", path_string);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,12 +312,12 @@ static void cmd_ls (struct list_node_link* tokens) {
|
|||||||
volume_close ();
|
volume_close ();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cmd_quit (void) {
|
static void quit1 (void) {
|
||||||
run = false;
|
run = false;
|
||||||
printf ("Goodbye!\n");
|
printf ("Goodbye!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cmd_help (void) {
|
static void help (void) {
|
||||||
printf ("Available commands:\n");
|
printf ("Available commands:\n");
|
||||||
printf ("echo <word1> <word2> <word3> ...\n");
|
printf ("echo <word1> <word2> <word3> ...\n");
|
||||||
printf ("help\n");
|
printf ("help\n");
|
||||||
@@ -158,21 +326,34 @@ static void cmd_help (void) {
|
|||||||
printf ("quit\n");
|
printf ("quit\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void exec_tokens (struct list_node_link* tokens) {
|
static void execute_cmd (struct ast_cmd* cmd) {
|
||||||
struct token* cmd_token = list_entry (tokens, struct token, tokens_link);
|
if (strcmp (cmd->name, "echo") == 0) {
|
||||||
|
echo (cmd->args, cmd->arg_count);
|
||||||
if (strcmp (cmd_token->buffer, "echo") == 0) {
|
} else if (strcmp (cmd->name, "help") == 0) {
|
||||||
cmd_echo (tokens->next);
|
help ();
|
||||||
} else if (strcmp (cmd_token->buffer, "help") == 0) {
|
} else if (strcmp (cmd->name, "cat") == 0) {
|
||||||
cmd_help ();
|
cat (cmd->args, cmd->arg_count);
|
||||||
} else if (strcmp (cmd_token->buffer, "cat") == 0) {
|
} else if (strcmp (cmd->name, "ls") == 0) {
|
||||||
cmd_cat (tokens->next);
|
ls (cmd->args[0]);
|
||||||
} else if (strcmp (cmd_token->buffer, "ls") == 0) {
|
} else if (strcmp (cmd->name, "quit") == 0) {
|
||||||
cmd_ls (tokens->next);
|
quit1 ();
|
||||||
} else if (strcmp (cmd_token->buffer, "quit") == 0) {
|
|
||||||
cmd_quit ();
|
|
||||||
} else {
|
} 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);
|
strtokenize (line, ' ', &tokens, &tokenize_line);
|
||||||
|
|
||||||
if (tokens != NULL)
|
if (tokens != NULL) {
|
||||||
exec_tokens (tokens);
|
classify_tokens (tokens);
|
||||||
|
parse_tokens (tokens);
|
||||||
|
}
|
||||||
|
|
||||||
arena_reset (&arena);
|
arena_reset (&arena);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user