summaryrefslogtreecommitdiff
path: root/userland/minibox/utilities/sh/ast.c
diff options
context:
space:
mode:
authorAnton Kling <anton@kling.gg>2023-11-17 23:58:14 +0100
committerAnton Kling <anton@kling.gg>2023-11-17 23:58:14 +0100
commit01a9392ad6051878e217bffeffd6261ccf994c42 (patch)
tree17bb2d06531c32e17396ff6978983e133b79c6df /userland/minibox/utilities/sh/ast.c
parent0c9282bb61b0d7c463045139655b3f1f1ec5422b (diff)
Minibox: Add a somewhat improved shell
This shell actually lexes and produces a AST which makes it easier to add features and will makes it more difficult to introduce bugs. So basically it is just better code.
Diffstat (limited to 'userland/minibox/utilities/sh/ast.c')
-rw-r--r--userland/minibox/utilities/sh/ast.c99
1 files changed, 99 insertions, 0 deletions
diff --git a/userland/minibox/utilities/sh/ast.c b/userland/minibox/utilities/sh/ast.c
new file mode 100644
index 0000000..64dd725
--- /dev/null
+++ b/userland/minibox/utilities/sh/ast.c
@@ -0,0 +1,99 @@
+#include "ast.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+void free_ast_command(struct AST *ast) {
+ free_ast(ast->children);
+ free_ast(ast->pipe_rhs);
+}
+
+void free_ast(struct AST *ast) {
+ for (; ast;) {
+ if (AST_COMMAND == ast->type)
+ free_ast_command(ast);
+ struct AST *old = ast;
+ ast = ast->next;
+ free(old);
+ }
+}
+
+struct AST *allocate_ast(void) {
+ struct AST *r = malloc(sizeof(struct AST));
+ memset(r, 0, sizeof(struct AST));
+ return r;
+}
+
+int parse_command(struct TOKEN **token_ptr, struct AST *cur) {
+ struct TOKEN *token = *token_ptr;
+ if (TOKEN_CHARS != token->type)
+ return 0;
+ cur->type = AST_COMMAND;
+ cur->val.type = AST_VALUE_STRING;
+ cur->val.string = token->string_rep;
+ // Parse the arguments
+ if (token->next && TOKEN_CHARS == token->next->type) {
+ token = token->next;
+ cur->children = allocate_ast();
+ struct AST *child = cur->children;
+ for (;;) {
+ child->type = AST_EXPRESSION;
+ child->val.type = AST_VALUE_STRING;
+ child->val.string = token->string_rep;
+ if (!token->next)
+ break;
+ if (TOKEN_CHARS != token->next->type)
+ break;
+ token = token->next;
+ child->next = allocate_ast();
+ child = child->next;
+ }
+ }
+ token = token->next;
+ // Parse the stream modifier "prog > file.txt"
+ if (token &&
+ (TOKEN_STREAM == token->type || TOKEN_STREAM_APPEND == token->type)) {
+ cur->file_out_append = (TOKEN_STREAM_APPEND == token->type);
+ // TODO: Allow it to be modified
+ cur->file_out_fd_to_use = STDOUT_FILENO;
+ token = token->next;
+ cur->file_out = token->string_rep;
+ token = token->next;
+ }
+ // Parse pipe '|'
+ if (token && TOKEN_PIPE == token->type) {
+ cur->pipe_rhs = allocate_ast();
+ token = token->next;
+ if (!parse_command(&token, cur->pipe_rhs)) {
+ fprintf(stderr, "Expected command after |.");
+ exit(1);
+ }
+ }
+ *token_ptr = token;
+ return 1;
+}
+
+struct AST *generate_ast(struct TOKEN *token) {
+ struct AST *head = NULL;
+ struct AST *prev = NULL;
+ for (; token;) {
+ struct AST *cur = allocate_ast();
+ if (prev)
+ prev->next = cur;
+ if (parse_command(&token, cur)) {
+ } else if (TOKEN_AND == token->type) {
+ cur->type = AST_CONDITIONAL_AND;
+ token = token->next;
+ } else if (TOKEN_NOT == token->type) {
+ cur->type = AST_CONDITIONAL_NOT;
+ token = token->next;
+ } else {
+ token = token->next;
+ }
+ if (!head)
+ head = cur;
+ prev = cur;
+ }
+ return head;
+}