From 2bdbf387261285d07e4b62d1e23c9e43efb14604 Mon Sep 17 00:00:00 2001 From: Ákos Kőrösi Date: Tue, 19 May 2026 21:13:19 +0200 Subject: restructure dir, create makefile --- Makefile | 3 + exec.c | 189 --------------------------------- exec.h | 26 ----- expand.c | 104 ------------------ expand.h | 31 ------ lexparse.c | 185 -------------------------------- main.c | 23 ---- preproc.c | 318 ------------------------------------------------------- preproc.h | 116 -------------------- redes/lexparse.c | 185 ++++++++++++++++++++++++++++++++ redes/syntax.h | 167 +++++++++++++++++++++++++++++ src/exec.c | 189 +++++++++++++++++++++++++++++++++ src/exec.h | 26 +++++ src/expand.c | 104 ++++++++++++++++++ src/expand.h | 31 ++++++ src/main.c | 23 ++++ src/preproc.c | 318 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/preproc.h | 116 ++++++++++++++++++++ src/utils.c | 71 +++++++++++++ src/utils.h | 111 +++++++++++++++++++ syntax.h | 167 ----------------------------- utils.c | 71 ------------- utils.h | 111 ------------------- 23 files changed, 1344 insertions(+), 1341 deletions(-) create mode 100644 Makefile delete mode 100644 exec.c delete mode 100644 exec.h delete mode 100644 expand.c delete mode 100644 expand.h delete mode 100644 lexparse.c delete mode 100644 main.c delete mode 100644 preproc.c delete mode 100644 preproc.h create mode 100644 redes/lexparse.c create mode 100644 redes/syntax.h create mode 100644 src/exec.c create mode 100644 src/exec.h create mode 100644 src/expand.c create mode 100644 src/expand.h create mode 100644 src/main.c create mode 100644 src/preproc.c create mode 100644 src/preproc.h create mode 100644 src/utils.c create mode 100644 src/utils.h delete mode 100644 syntax.h delete mode 100644 utils.c delete mode 100644 utils.h diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6952c64 --- /dev/null +++ b/Makefile @@ -0,0 +1,3 @@ +build: + @gcc -g src/main.c src/exec.c src/expand.c src/preproc.c src/utils.c -o bin/yeesh -lreadline + diff --git a/exec.c b/exec.c deleted file mode 100644 index fccda7a..0000000 --- a/exec.c +++ /dev/null @@ -1,189 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include "utils.h" -#include "exec.h" -#include "preproc.h" -#include "expand.h" - - -void execute_command(Command cmd, int* pgid) { - - pid_t pid = fork(); - - if (pid == 0) { - if (*pgid == -1) { - setpgid(0, 0); - } else { - setpgid(0, *pgid); - } - - dup2(cmd.stdin_fd, STDIN_FILENO); - dup2(cmd.stdout_fd, STDOUT_FILENO); - - for (int j = 0; j < cmd.redirs.count; j++) { - FdRedirect fd_rd = *get_from_FdRedirectVec(&cmd.redirs, j); - dup2(fd_rd.new_fd, fd_rd.old_fd); - } - - for (int i = 0; i < cmd.child_close_fds.count; i++) { - close(*get_from_IntVec(&cmd.child_close_fds, i)); - } - - signal(SIGINT, SIG_DFL); - signal(SIGQUIT, SIG_DFL); - signal(SIGTSTP, SIG_DFL); - signal(SIGTTIN, SIG_DFL); - signal(SIGTTOU, SIG_DFL); - - execvp(cmd.args[0], cmd.args); - exit(67); - } - - if (*pgid == -1) { - setpgid(pid, pid); - *pgid = pid; - } else { - setpgid(pid, *pgid); - } -} - -Command make_command(ExpandedPipelineElement elem) { - Command cmd = { - .child_close_fds = make_IntVec(64), - .parent_close_fds = make_IntVec(64), - .redirs = make_FdRedirectVec(32), - .term = elem.conn, - }; - StrVec arg_strs = make_StrVec(256); - for (int i = 0; i < elem.cmd_slots.count; i++) { - Slot curr_slot = *get_from_SlotVec(&elem.cmd_slots, i); - Slot next_slot; - int fd; - FdRedirect redir; - switch (curr_slot.kind) { - case SLOT_STR: - push_to_StrVec(&arg_strs, curr_slot.as.str); - break; - case SLOT_SPEC: - switch (curr_slot.as.spec) { - case READ_REDIR: - i++; - next_slot = *get_from_SlotVec(&elem.cmd_slots, i); - if (next_slot.kind != SLOT_STR) exit(6); - fd = open(next_slot.as.str, O_RDONLY, 0644); - redir = (FdRedirect){.old_fd = STDIN_FILENO, .new_fd = fd}; - push_to_FdRedirectVec(&cmd.redirs, redir); - push_to_IntVec(&cmd.parent_close_fds, fd); - push_to_IntVec(&cmd.child_close_fds, fd); - break; - case WRITE_REDIR_TRUNC: - i++; - next_slot = *get_from_SlotVec(&elem.cmd_slots, i); - if (next_slot.kind != SLOT_STR) exit(6); - fd = open(next_slot.as.str, O_WRONLY | O_CREAT | O_TRUNC , 0644); - redir = (FdRedirect){.old_fd = STDOUT_FILENO, .new_fd = fd}; - push_to_FdRedirectVec(&cmd.redirs, redir); - push_to_IntVec(&cmd.parent_close_fds, fd); - push_to_IntVec(&cmd.child_close_fds, fd); - break; - case WRITE_REDIR_APPEND: - i++; - next_slot = *get_from_SlotVec(&elem.cmd_slots, i); - if (next_slot.kind != SLOT_STR) exit(6); - fd = open(next_slot.as.str, O_WRONLY | O_CREAT | O_APPEND, 0644); - redir = (FdRedirect){.old_fd = STDOUT_FILENO, .new_fd = fd}; - push_to_FdRedirectVec(&cmd.redirs, redir); - push_to_IntVec(&cmd.parent_close_fds, fd); - push_to_IntVec(&cmd.child_close_fds, fd); - break; - default: - exit(7); - } - break; - default: - exit(67); - // TODO: other slot kinds - } - } - cmd.args = malloc(sizeof(char*) * arg_strs.count); - for (int j = 0; j < arg_strs.count; j++) { - cmd.args[j] = *get_from_StrVec(&arg_strs, j); - } - return cmd; -} - - -void run_cmds(CommandVec cmds) { - signal(SIGTTIN, SIG_IGN); - signal(SIGTTOU, SIG_IGN); - - int trunk_fd = STDIN_FILENO; - int pgid = -1; - - for (int i = 0; i < cmds.count; i++) { - Command cmd = *get_from_CommandVec(&cmds, i); - cmd.stdin_fd = trunk_fd; - - if (i > 0) { - push_to_IntVec(&cmd.child_close_fds, trunk_fd); - push_to_IntVec(&cmd.parent_close_fds, trunk_fd); - } - - cmd.stdout_fd = STDOUT_FILENO; - if (cmd.term == CONN_PIPE) { - int pipe_fds[2]; - pipe(pipe_fds); - cmd.stdout_fd = pipe_fds[1]; - push_to_IntVec(&cmd.child_close_fds, pipe_fds[1]); - push_to_IntVec(&cmd.parent_close_fds, pipe_fds[1]); - trunk_fd = pipe_fds[0]; - } - - execute_command(cmd, &pgid); - - for (int j = 0; j < cmd.parent_close_fds.count; j++) { - close(*get_from_IntVec(&cmd.parent_close_fds, j)); - } - } - - tcsetpgrp(STDIN_FILENO, pgid); - while (waitpid(-pgid, NULL, 0) > 0); - tcsetpgrp(STDIN_FILENO, getpgrp()); -} - - -CommandVec make_cmd_vec_pl(ExpandedPipeline pl) { - CommandVec cmds = make_CommandVec(256); - for (int i = 0; i < pl.count; i++) { - ExpandedPipelineElement elem = *get_from_ExpandedPipeline(&pl, i); - Command cmd = make_command(elem); - push_to_CommandVec(&cmds, cmd); - } - return cmds; -} - - -void process_input_line(char* line, ShellState* shstate) { - PreprocArena arena = { - .tokens = make_TokenVec(256), - .chunks = make_ChunkVec(256), - .regions = make_RegionVec(256), - }; - TokenPtrVec line_tok_ptrs = lex(line, &arena); - Token* line_toks = malloc(sizeof(Token) * line_tok_ptrs.count); - for (int i = 0; i < line_tok_ptrs.count; i++) { - line_toks[i] = **get_from_TokenPtrVec(&line_tok_ptrs, i); - } - Pipeline pl = parse_tokstream(line_toks); - ExpandedPipeline exp_pl = expand_pipeline(&pl, shstate); - CommandVec cmds = make_cmd_vec_pl(exp_pl); - run_cmds(cmds); - free(line_toks); -} - diff --git a/exec.h b/exec.h deleted file mode 100644 index 9ee8164..0000000 --- a/exec.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include "utils.h" -#include "preproc.h" - - -typedef struct { - int old_fd; - int new_fd; -} FdRedirect; - -DECLARE_VEC(FdRedirect, FdRedirectVec) - -typedef struct { - char** args; - FdRedirectVec redirs; - int stdin_fd; - int stdout_fd; - IntVec child_close_fds; - IntVec parent_close_fds; - Connector term; -} Command; - -DECLARE_VEC(Command, CommandVec) - -void process_input_line(char* line, ShellState* shstate); diff --git a/expand.c b/expand.c deleted file mode 100644 index 11764f9..0000000 --- a/expand.c +++ /dev/null @@ -1,104 +0,0 @@ -#include "expand.h" -#include "preproc.h" -#include "utils.h" -#include - - -typedef struct { - QuotationMode qmode; - char* content; -} ExpandedChunk; - - -ExpandedChunk expand_chunk(Chunk chunk, ShellState* shstate) { - char* content; - if (chunk.qmode == SINGLE_Q || chunk.kind == LITERAL) { - if (chunk.qmode == SINGLE_Q && chunk.kind != LITERAL) exit(69); - content = chunk.as.literal_str; - } else if (chunk.kind == VAR_EXP) { - content = get_var(chunk.as.varname, shstate); - } else if (chunk.kind == REGION_EXP) { - exit(7); // Not supported yet; arithm - } - return (ExpandedChunk){.qmode = chunk.qmode, .content = content}; -} - - -typedef struct { - StrVec complete_strs; - String active_str; -} WordSplittingGerm; - -void add_expchunk(char* next_str, WordSplittingGerm* curr_res) { - for (int pos = 0; pos < strlen(next_str); pos++) { - if (next_str[pos] == ' ') { - push_to_StrVec(&curr_res->complete_strs, inner(&curr_res->active_str)); - curr_res->active_str = make_String(256); - } else { - push_to_String(&curr_res->active_str, next_str[pos]); - } - } -} - - -void expand_word(Word word, SlotVec* target, ShellState* shstate) { - WordSplittingGerm curr_res = { - .complete_strs = make_StrVec(256), - .active_str = make_String(256) - }; - for (int i = 0; i < word.count; i++) { - Chunk chunk = **get_from_Word(&word, i); - ExpandedChunk expchunk = expand_chunk(chunk, shstate); - switch (expchunk.qmode) { - case UN_Q: - add_expchunk(expchunk.content, &curr_res); - break; - case SINGLE_Q: - append_str_to_String(&curr_res.active_str, expchunk.content); - break; - case DOUBLE_Q: - // TODO: further expansions here - append_str_to_String(&curr_res.active_str, expchunk.content); - break; - } - } - if (curr_res.active_str.count > 0) { - push_to_StrVec(&curr_res.complete_strs, inner(&curr_res.active_str)); - } - for (int j = 0; j < curr_res.complete_strs.count; j++) { - char* str = *get_from_StrVec(&curr_res.complete_strs, j); - push_to_SlotVec(target, (Slot){.kind = SLOT_STR, .as.str = str}); - } -} - -ExpandedPipelineElement expand_pipeline_element(PipelineElement elem, ShellState* shstate) { - ExpandedPipelineElement result = {.cmd_slots = make_SlotVec(256), .conn = elem.conn}; - for (int i = 0; i < elem.cmd_toks.count; i++) { - Token curr_tok = *get_from_TokenVec(&elem.cmd_toks, i); - switch (curr_tok.kind) { - case TOK_WORD: - expand_word(curr_tok.as.word, &result.cmd_slots, shstate); - break; - case TOK_SPECIAL: - push_to_SlotVec(&result.cmd_slots, (Slot){.kind = SLOT_SPEC, .as.spec = curr_tok.as.spec}); - break; - case TOK_PROCSUB_IN: - push_to_SlotVec(&result.cmd_slots, (Slot){.kind = SLOT_PROC_IN, .as.proc_reg= curr_tok.as.procsub_region}); - break; - case TOK_PROCSUB_OUT: - push_to_SlotVec(&result.cmd_slots, (Slot){.kind = SLOT_PROC_OUT, .as.proc_reg= curr_tok.as.procsub_region}); - break; - } - } - return result; -} - -ExpandedPipeline expand_pipeline(Pipeline* pl, ShellState *shstate) { - ExpandedPipeline exp_pl = make_ExpandedPipeline(256); - for (int i = 0; i < pl->count; i++) { - PipelineElement elem = *get_from_Pipeline(pl, i); - ExpandedPipelineElement exp_elem = expand_pipeline_element(elem, shstate); - push_to_ExpandedPipeline(&exp_pl, exp_elem); - } - return exp_pl; -} diff --git a/expand.h b/expand.h deleted file mode 100644 index b353f39..0000000 --- a/expand.h +++ /dev/null @@ -1,31 +0,0 @@ -# pragma once - -#include "preproc.h" -#include "utils.h" - -typedef enum { - SLOT_STR, - SLOT_SPEC, - SLOT_PROC_IN, - SLOT_PROC_OUT, -} SlotKind; - -typedef struct { - SlotKind kind; - union { - char* str; - SpecialToken spec; - Region* proc_reg; - } as; -} Slot; - -DECLARE_VEC(Slot, SlotVec) - -typedef struct { - SlotVec cmd_slots; - Connector conn; -} ExpandedPipelineElement; - -DECLARE_VEC(ExpandedPipelineElement, ExpandedPipeline) - -ExpandedPipeline expand_pipeline(Pipeline* pl, ShellState* shstate); diff --git a/lexparse.c b/lexparse.c deleted file mode 100644 index 0ea86df..0000000 --- a/lexparse.c +++ /dev/null @@ -1,185 +0,0 @@ -#include -#include "syntax.h" -#include "utils.h" - -typedef struct { - Word word_germ; - String lit_germ; -} LexingBuffer; - -typedef struct { - TokenVec* token_pool; - TokenPtrVec* top_tokens; -} PseudoAst; - -static void consolidate_buffer(LexingBuffer* buf, PseudoAst ps_ast) { - if (buf->lit_germ.count > 0) { - push_to_Word(&buf->word_germ, (Chunk){ - .qmode = UN_Q, - .kind = CHUNK_LIT, - .as.lit_str = inner(&buf->lit_germ) - }); - buf->lit_germ = make_String(256); - } - if (buf->word_germ.count > 0) { - Token* tok_ptr = push_to_TokenVec(ps_ast.token_pool, (Token){ - .kind = TOK_WORD, - .as.word = buf->word_germ - }); - push_to_TokenPtrVec(ps_ast.top_tokens, tok_ptr); - } -} - - -static Chunk collect_un_q_literal(char* line, size_t* pos) { - String buf = make_String(256); - size_t i; - for (i = *pos; line[*pos] != '$' && line[*pos] != '\0' && line[*pos] == ' '; i++) { - push_to_String(&buf, line[*pos]); - } - *pos = i; - return (Chunk){ - .qmode = UN_Q, - .kind = CHUNK_LIT, - .as.lit_str = inner(&buf), - }; -} - - -static Chunk collect_single_q_section(char* line, size_t* pos) { - (*pos)++; - String buf = make_String(256); - size_t i; - for (i = *pos; line[*pos] != '\'' && line[*pos] != '\0'; i++) { - push_to_String(&buf, line[*pos]); - } - if (line[i] == '\0') exit(44); - - *pos = ++i; - return (Chunk){ - .qmode = SINGLE_Q, - .kind = CHUNK_LIT, - .as.lit_str = inner(&buf) - }; -} - -static Chunk collect_double_q_section(char* line, size_t* pos) { - // TODO: add the extra double-q expansions - (*pos)++; - String buf = make_String(256); - size_t i; - for (i = *pos; line[*pos] != '\'' && line[*pos] != '\0'; i++) { - push_to_String(&buf, line[*pos]); - } - if (line[i] == '\0') exit(44); - - *pos = ++i; - return (Chunk){ - .qmode = DOUBLE_Q, - .kind = CHUNK_LIT, - .as.lit_str = inner(&buf) - }; -} - -DECLARE_MAYBE(SpecialToken, MaybeSpecialToken) - - -static MaybeSpecialToken recognize_spec_token(char* line, size_t* pos) { - // TODO: add more error guards for peekaheads - char next_ch; - - switch (line[*pos]) { - case '|': - (*pos)++; - return (MaybeSpecialToken){.some = true, .value = SPEC_PIPE}; - case '>': - next_ch = line[(*pos)+1]; - switch (next_ch) { - case '(': - return (MaybeSpecialToken){.some = false}; - case '>': - *pos += 2; - return (MaybeSpecialToken){.some = true, .value = SPEC_REDIR_W_APPEND}; - default: - (*pos)++; - return (MaybeSpecialToken){.some = true, .value = SPEC_REDIR_W_TRUNC}; - } - case '<': - next_ch = line[(*pos)+1]; - if (line[(*pos)+1] == '(') { - return (MaybeSpecialToken){.some = false}; - } else { - (*pos)++; - return (MaybeSpecialToken){.some = true, .value = SPEC_REDIR_R}; - } - default: - return (MaybeSpecialToken){.some = false}; - } -} - -static void lex_level(char* line, size_t* pos, char until, PseudoAst ps_ast) { - LexingBuffer buf = {.word_germ = make_Word(256), .lit_germ = make_String(256)}; - while (line[*pos] != until) { - if (line[*pos] == ' ') { - consolidate_buffer(&buf, ps_ast); - while (line[*pos] == ' ') (*pos)++; - continue; - } - MaybeSpecialToken m_spec = recognize_spec_token(line, pos); - if (m_spec.some) { - Token* tok_ptr = push_to_TokenVec(ps_ast.token_pool, (Token){ - .kind = TOK_SPEC, - .as.spec = m_spec.value - }); - push_to_TokenPtrVec(ps_ast.top_tokens, tok_ptr); - continue; - } - - if (line[*pos] == '\'') { - Chunk chunk = collect_single_q_section(line, pos); - push_to_Word(&buf.word_germ, chunk); - } else if (line[*pos] == '\"') { - Chunk chunk = collect_double_q_section(line, pos); - push_to_Word(&buf.word_germ, chunk); - } else if (line[*pos] == '<' && line[(*pos)+1] == '(') { - *pos += 2; - consolidate_buffer(&buf, ps_ast); - TokenPtrVec procsub_toks = make_TokenPtrVec(256); - lex_level(line, pos, ')', (PseudoAst){ - .token_pool = ps_ast.token_pool, - .top_tokens = &procsub_toks - }); - (*pos)++; // consume ')' - Token* tok_ptr = push_to_TokenVec(ps_ast.token_pool, (Token){ - .kind = TOK_SUB_IN, - .as.procsub_toks = procsub_toks, - }); - push_to_TokenPtrVec(ps_ast.top_tokens, tok_ptr); - } else if (line[*pos] == '>' && line[(*pos)+1] == '(') { - *pos += 2; - consolidate_buffer(&buf, ps_ast); - TokenPtrVec procsub_toks = make_TokenPtrVec(256); - lex_level(line, pos, ')', (PseudoAst){ - .token_pool = ps_ast.token_pool, - .top_tokens = &procsub_toks - }); - (*pos)++; // consume ')' - Token* tok_ptr = push_to_TokenVec(ps_ast.token_pool, (Token){ - .kind = TOK_SUB_OUT, - .as.procsub_toks = procsub_toks, - }); - push_to_TokenPtrVec(ps_ast.top_tokens, tok_ptr); - } - } -} - -Ast lex(char* input) { - size_t line_pos = 0; - TokenVec token_pool = make_TokenVec(256); - TokenPtrVec top_tokens = make_TokenPtrVec(256); - lex_level(input, &line_pos, '\0', (PseudoAst){ - .token_pool = &token_pool, - .top_tokens = &top_tokens - }); - return (Ast){.token_pool = token_pool, .top_tokens = top_tokens}; -} diff --git a/main.c b/main.c deleted file mode 100644 index 35c2bff..0000000 --- a/main.c +++ /dev/null @@ -1,23 +0,0 @@ -#include -#include -#include -#include -#include - -#include "exec.h" - - -int main() { - char* line; - ShellState shstate = {.shell_vars = make_KeyValMap(1), .aliases = make_KeyValMap(1)}; - while (1) { - line = readline("$ "); - if (line[0] == '\0') continue; - if (strcmp(line, "exit") == 0) exit(0); - add_history(line); - process_input_line(line, &shstate); - } - free(line); -} - - diff --git a/preproc.c b/preproc.c deleted file mode 100644 index 4cd1c5b..0000000 --- a/preproc.c +++ /dev/null @@ -1,318 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "preproc.h" -#include "utils.h" - - -// Lexing - -typedef struct { - char* str; - size_t pos; -} RawLine; - - -String collect_identifier(RawLine* line) { - String id_buf = make_String(256); - while (line->str[line->pos] == '_' || isalnum(line->str[line->pos])) { - push_to_String(&id_buf, line->str[line->pos++]); - } - return id_buf; -} - - - -TokenPtrVec lex_level(RawLine* line, char until, PreprocArena* arena); - - -void expect_char(RawLine* line, char expected) { - if (line->str[line->pos] != expected) { - exit(45); // TODO: nicer handling - } - line->pos++; -} - - -/* -MaybeChunk lex_expansion_chunk(RawLine* line, RegionVec* reg_arena, QuotationMode qmode) { - line->pos++; // Consume $ - if (line->str[line->pos] == '(') { - line->pos++; - if (line->str[line->pos] == '(') { - exit(1); - // TODO: this (and potentially the other parsers must be custom, we need to recognize arithm-internal parens from the terminating `))` ) - - - TokenVec inner_toks = lex_level(line, ')', reg_arena); - expect_char(line, ')'); - expect_char(line, ')'); - Region* reg_ptr = push_to_RegionVec( - reg_arena, - (Region){.mode = ARITHM, .tokens = inner_toks} - ); - return (MaybeChunk){ - .some = true, - .value = (Chunk){ - .qmode = qmode, - .kind = REGION_EXP, - .as.reg_ptr = reg_ptr - }, - }; - } else { - TokenVec inner_toks = lex_level(line, ')', reg_arena); - expect_char(line, ')'); - Region* reg_ptr = push_to_RegionVec( - reg_arena, - (Region){.mode = CMDS, .tokens = inner_toks} - ); - return (MaybeChunk) { - .some = true, - .value = (Chunk){ - .qmode = qmode, - .kind = REGION_EXP, - .as.reg_ptr = reg_ptr, - } - }; - } - } else { - String id_name = collect_identifier(line); - if (id_name.count > 0) { - return (MaybeChunk){ - .some = true, - .value = (Chunk){ - .qmode = qmode, - .kind = VAR_EXP, - .as.varname = inner(&id_name), - } - }; - } - } - return (MaybeChunk){.some = false}; -} -*/ - - -void lex_double_quoted_section(RawLine* line, Word* coll, PreprocArena* arena) { - line->pos++; - String accum = make_String(256); - while (line->str[line->pos] != '\"') { - if (line->str[line->pos] == '\0') exit(43); - - MaybeChunk exp_chunk = {.some = false}; - - if (line->str[line->pos] == '$') { - exit(1); - /* - exp_chunk = lex_expansion_chunk(line, reg_arena, DOUBLE_Q); - if (!exp_chunk.some) { - push_to_String(&accum, '$'); - } - */ - } else { - push_to_String(&accum, line->str[line->pos++]); - } - // TODO: other special cases beyond $ - - if (exp_chunk.some) { - if (accum.count > 0) { - Chunk accum_chunk = {.qmode = DOUBLE_Q, .kind = LITERAL, .as.literal_str = inner(&accum)}; - Chunk* accum_ptr = push_to_ChunkVec(&arena->chunks, accum_chunk); - push_to_Word(coll, accum_ptr); - accum = make_String(256); - } - Chunk* exp_ptr = push_to_ChunkVec(&arena->chunks, exp_chunk.value); - push_to_Word(coll, exp_ptr); - exp_chunk.some = false; - } - } - line->pos++; - if (accum.count > 0) { - Chunk accum_chunk = {.qmode = DOUBLE_Q, .kind = LITERAL, .as.literal_str = inner(&accum)}; - Chunk* accum_ptr = push_to_ChunkVec(&arena->chunks, accum_chunk); - push_to_Word(coll, accum_ptr); - accum = make_String(256); - } -} - - -Chunk next_word_unquoted(RawLine* line, RegionVec* reg_arena) { - String buf = make_String(256); - for (char c; (c=line->str[line->pos], c != '$' && c != '\0' && c != ' '); line->pos++) { - push_to_String(&buf, c); - } - return (Chunk){.qmode = UN_Q, .kind = LITERAL, .as.literal_str = inner(&buf)}; -} - - -Chunk collect_single_quoted_section(RawLine* line) { - line->pos++; // Consume quote mark - String chunk_str = make_String(256); - while (line->str[line->pos] != '\'') { - if (line->str[line->pos] == '\0') exit(44); - push_to_String(&chunk_str, line->str[line->pos++]); - } - line->pos++; // Consume closing quote - return (Chunk){ - .qmode = SINGLE_Q, - .kind = LITERAL, - .as.literal_str = inner(&chunk_str), - }; -} - - -typedef struct { - Word word_germ; - String unq_liter_germ; -} LexState; - - -void close_word_and_lit_germs(LexState* state, TokenPtrVec* toks, PreprocArena* arena) { - if (state->unq_liter_germ.count > 0) { - Chunk chu = { - .qmode = UN_Q, - .kind = LITERAL, - .as.literal_str = inner(&state->unq_liter_germ) - }; - Chunk* chu_ptr = push_to_ChunkVec(&arena->chunks, chu); - push_to_Word(&state->word_germ, chu_ptr); - state->unq_liter_germ = make_String(256); - } - if (state->word_germ.count > 0) { - Token tok = {.kind = TOK_WORD, .as.word = state->word_germ}; - Token* tok_ptr = push_to_TokenVec(&arena->tokens, tok); - push_to_TokenPtrVec(toks, tok_ptr); - state->word_germ = make_Word(256); - } -} - -TokenPtrVec lex_level(RawLine* line, char until, PreprocArena* arena) { - - TokenPtrVec line_toks = make_TokenPtrVec(256); - LexState state = { - .word_germ = make_Word(256), - .unq_liter_germ = make_String(256) - }; - - while (line->str[line->pos] != until) { - - if (line->str[line->pos] == ' ') { - close_word_and_lit_germs(&state, &line_toks, arena); - while (line->str[line->pos] == ' ') line->pos++; - continue; - } - - Token* tok_ptr; - - switch (line->str[line->pos]) { - case '\'': - push_to_Word(&state.word_germ, push_to_ChunkVec(&arena->chunks, collect_single_quoted_section(line))); - break; - case '\"': - lex_double_quoted_section(line, &state.word_germ, arena); - break; - case '|': - line->pos++; - close_word_and_lit_germs(&state, &line_toks, arena); - tok_ptr = push_to_TokenVec(&arena->tokens, (Token){.kind = TOK_SPECIAL, .as.spec = PIPE}); - push_to_TokenPtrVec(&line_toks, tok_ptr); - break; - case '$': - exit(1); - /* - line->pos++; - MaybeChunk mchunk = lex_expansion_chunk(line, reg_arena, UN_Q); - if (mchunk.some) { - push_to_Word(&state.word_germ, mchunk.value); - } else { - push_to_String(&state.unq_liter_germ, '$'); - } - */ - break; - case '>': - close_word_and_lit_germs(&state, &line_toks, arena); - line->pos++; - - switch (line->str[line->pos]) { - case '(': - line->pos++; - TokenPtrVec sub_regtoks = lex_level(line, ')', arena); - line->pos++; - Region subreg = {.mode = CMDS, .tokens = sub_regtoks}; - Region* subreg_ptr = push_to_RegionVec(&arena->regions, subreg); - Token tok = {.kind = TOK_PROCSUB_OUT, .as.procsub_region = subreg_ptr}; - tok_ptr = push_to_TokenVec(&arena->tokens, tok); - push_to_TokenPtrVec(&line_toks, tok_ptr); - break; - case '>': - line->pos++; - tok_ptr = push_to_TokenVec(&arena->tokens, (Token){.kind = TOK_SPECIAL, .as.spec = WRITE_REDIR_APPEND}); - push_to_TokenPtrVec(&line_toks, tok_ptr); - break; - default: - tok_ptr = push_to_TokenVec(&arena->tokens, (Token){.kind = TOK_SPECIAL, .as.spec = WRITE_REDIR_TRUNC}); - push_to_TokenPtrVec(&line_toks, tok_ptr); - } - break; - case '<': - close_word_and_lit_germs(&state, &line_toks, arena); - line->pos++; - if (line->str[line->pos] == '(') { - line->pos++; - TokenPtrVec sub_regtoks = lex_level(line, ')', arena); - line->pos++; - Region subreg = {.mode = CMDS, .tokens = sub_regtoks}; - Region* reg_ptr = push_to_RegionVec(&arena->regions, subreg); - Token reg_tok = {.kind = TOK_PROCSUB_IN, .as.procsub_region = reg_ptr}; - Token* reg_tok_ptr = push_to_TokenVec(&arena->tokens, reg_tok); - push_to_TokenPtrVec(&line_toks, reg_tok_ptr); - } else { - Token* redir_ptr = push_to_TokenVec(&arena->tokens, (Token){.kind = TOK_SPECIAL, .as.spec = READ_REDIR}); - push_to_TokenPtrVec(&line_toks, redir_ptr); - } - break; - default: - push_to_String(&state.unq_liter_germ, line->str[line->pos++]); - } - } - // TODO: handle until char _and_ EOL - close_word_and_lit_germs(&state, &line_toks, arena); - return line_toks; -} - - -TokenPtrVec lex(char* input, PreprocArena* arena) { - RawLine line = {.str = input, .pos = 0}; - TokenPtrVec toks = lex_level(&line, '\0', arena); - Token* eol_ptr = push_to_TokenVec(&arena->tokens, (Token){.kind = TOK_SPECIAL, .as.spec = EOL}); - push_to_TokenPtrVec(&toks, eol_ptr); - return toks; -} - - - - -// Parsing - -Pipeline parse_tokstream(Token* toks) { - size_t tok_pos = 0; - Pipeline result = make_Pipeline(256); - TokenVec tok_buf = make_TokenVec(256); - while (true) { - Token curr_tok = toks[tok_pos++]; - if (curr_tok.kind == TOK_SPECIAL && curr_tok.as.spec == PIPE) { - push_to_Pipeline(&result, (PipelineElement){.cmd_toks = tok_buf, .conn = CONN_PIPE}); - tok_buf = make_TokenVec(256); - } else if (curr_tok.kind == TOK_SPECIAL && curr_tok.as.spec == EOL) { - push_to_Pipeline(&result, (PipelineElement){.cmd_toks = tok_buf, .conn = CONN_EOL}); - break; - } else { - push_to_TokenVec(&tok_buf, curr_tok); - } - } - return result; -} diff --git a/preproc.h b/preproc.h deleted file mode 100644 index 7907f9d..0000000 --- a/preproc.h +++ /dev/null @@ -1,116 +0,0 @@ -#pragma once - -#include "utils.h" - -typedef struct Region Region; - - -// Lexing stage -typedef enum { - UN_Q, - SINGLE_Q, - DOUBLE_Q, -} QuotationMode; - -typedef enum { - LITERAL, - REGION_EXP, - VAR_EXP, // TODO: Later include other exps -} ChunkKind; - -typedef struct { - ChunkKind kind; - QuotationMode qmode; - union { - char* literal_str; - Region* reg_ptr; - char* varname; - } as; -} Chunk; -DECLARE_MAYBE(Chunk, MaybeChunk); -DECLARE_VEC(Chunk*, Word) - -typedef enum { - TOK_WORD, - TOK_SPECIAL, - TOK_PROCSUB_IN, - TOK_PROCSUB_OUT, -} TokenKind; - -typedef enum { - PIPE, - WRITE_REDIR_TRUNC, - WRITE_REDIR_APPEND, - READ_REDIR, - EOL, -} SpecialToken; -DECLARE_MAYBE(SpecialToken, MaybeSpecialToken) - -typedef struct { - TokenKind kind; - - union { - Word word; - SpecialToken spec; - Region* procsub_region; - } as; -} Token; - - -DECLARE_VEC(Token*, TokenPtrVec) - - -typedef enum { - CMDS, - ARITHM, -} RegionMode; - -struct Region { - RegionMode mode; - TokenPtrVec tokens; -}; -DECLARE_VEC(Region, RegionVec) - - - -DECLARE_VEC(Chunk, ChunkVec) -DECLARE_VEC(Token, TokenVec) - - - -// Lexing - -typedef struct { - RegionVec regions; - TokenVec tokens; - ChunkVec chunks; -} PreprocArena; - - -TokenPtrVec lex(char* input, PreprocArena* arena); - - -// Parsing - -typedef enum { - CONN_PIPE, - CONN_EOL, - // TODO: add the |& thingy -} Connector; - -DECLARE_MAYBE(Connector, MaybeConnector) - -typedef struct { - TokenVec cmd_toks; - Connector conn; -} PipelineElement; - -DECLARE_VEC(PipelineElement, Pipeline) - -Pipeline parse_tokstream(Token* tokstream); - - - - - - diff --git a/redes/lexparse.c b/redes/lexparse.c new file mode 100644 index 0000000..0ea86df --- /dev/null +++ b/redes/lexparse.c @@ -0,0 +1,185 @@ +#include +#include "syntax.h" +#include "utils.h" + +typedef struct { + Word word_germ; + String lit_germ; +} LexingBuffer; + +typedef struct { + TokenVec* token_pool; + TokenPtrVec* top_tokens; +} PseudoAst; + +static void consolidate_buffer(LexingBuffer* buf, PseudoAst ps_ast) { + if (buf->lit_germ.count > 0) { + push_to_Word(&buf->word_germ, (Chunk){ + .qmode = UN_Q, + .kind = CHUNK_LIT, + .as.lit_str = inner(&buf->lit_germ) + }); + buf->lit_germ = make_String(256); + } + if (buf->word_germ.count > 0) { + Token* tok_ptr = push_to_TokenVec(ps_ast.token_pool, (Token){ + .kind = TOK_WORD, + .as.word = buf->word_germ + }); + push_to_TokenPtrVec(ps_ast.top_tokens, tok_ptr); + } +} + + +static Chunk collect_un_q_literal(char* line, size_t* pos) { + String buf = make_String(256); + size_t i; + for (i = *pos; line[*pos] != '$' && line[*pos] != '\0' && line[*pos] == ' '; i++) { + push_to_String(&buf, line[*pos]); + } + *pos = i; + return (Chunk){ + .qmode = UN_Q, + .kind = CHUNK_LIT, + .as.lit_str = inner(&buf), + }; +} + + +static Chunk collect_single_q_section(char* line, size_t* pos) { + (*pos)++; + String buf = make_String(256); + size_t i; + for (i = *pos; line[*pos] != '\'' && line[*pos] != '\0'; i++) { + push_to_String(&buf, line[*pos]); + } + if (line[i] == '\0') exit(44); + + *pos = ++i; + return (Chunk){ + .qmode = SINGLE_Q, + .kind = CHUNK_LIT, + .as.lit_str = inner(&buf) + }; +} + +static Chunk collect_double_q_section(char* line, size_t* pos) { + // TODO: add the extra double-q expansions + (*pos)++; + String buf = make_String(256); + size_t i; + for (i = *pos; line[*pos] != '\'' && line[*pos] != '\0'; i++) { + push_to_String(&buf, line[*pos]); + } + if (line[i] == '\0') exit(44); + + *pos = ++i; + return (Chunk){ + .qmode = DOUBLE_Q, + .kind = CHUNK_LIT, + .as.lit_str = inner(&buf) + }; +} + +DECLARE_MAYBE(SpecialToken, MaybeSpecialToken) + + +static MaybeSpecialToken recognize_spec_token(char* line, size_t* pos) { + // TODO: add more error guards for peekaheads + char next_ch; + + switch (line[*pos]) { + case '|': + (*pos)++; + return (MaybeSpecialToken){.some = true, .value = SPEC_PIPE}; + case '>': + next_ch = line[(*pos)+1]; + switch (next_ch) { + case '(': + return (MaybeSpecialToken){.some = false}; + case '>': + *pos += 2; + return (MaybeSpecialToken){.some = true, .value = SPEC_REDIR_W_APPEND}; + default: + (*pos)++; + return (MaybeSpecialToken){.some = true, .value = SPEC_REDIR_W_TRUNC}; + } + case '<': + next_ch = line[(*pos)+1]; + if (line[(*pos)+1] == '(') { + return (MaybeSpecialToken){.some = false}; + } else { + (*pos)++; + return (MaybeSpecialToken){.some = true, .value = SPEC_REDIR_R}; + } + default: + return (MaybeSpecialToken){.some = false}; + } +} + +static void lex_level(char* line, size_t* pos, char until, PseudoAst ps_ast) { + LexingBuffer buf = {.word_germ = make_Word(256), .lit_germ = make_String(256)}; + while (line[*pos] != until) { + if (line[*pos] == ' ') { + consolidate_buffer(&buf, ps_ast); + while (line[*pos] == ' ') (*pos)++; + continue; + } + MaybeSpecialToken m_spec = recognize_spec_token(line, pos); + if (m_spec.some) { + Token* tok_ptr = push_to_TokenVec(ps_ast.token_pool, (Token){ + .kind = TOK_SPEC, + .as.spec = m_spec.value + }); + push_to_TokenPtrVec(ps_ast.top_tokens, tok_ptr); + continue; + } + + if (line[*pos] == '\'') { + Chunk chunk = collect_single_q_section(line, pos); + push_to_Word(&buf.word_germ, chunk); + } else if (line[*pos] == '\"') { + Chunk chunk = collect_double_q_section(line, pos); + push_to_Word(&buf.word_germ, chunk); + } else if (line[*pos] == '<' && line[(*pos)+1] == '(') { + *pos += 2; + consolidate_buffer(&buf, ps_ast); + TokenPtrVec procsub_toks = make_TokenPtrVec(256); + lex_level(line, pos, ')', (PseudoAst){ + .token_pool = ps_ast.token_pool, + .top_tokens = &procsub_toks + }); + (*pos)++; // consume ')' + Token* tok_ptr = push_to_TokenVec(ps_ast.token_pool, (Token){ + .kind = TOK_SUB_IN, + .as.procsub_toks = procsub_toks, + }); + push_to_TokenPtrVec(ps_ast.top_tokens, tok_ptr); + } else if (line[*pos] == '>' && line[(*pos)+1] == '(') { + *pos += 2; + consolidate_buffer(&buf, ps_ast); + TokenPtrVec procsub_toks = make_TokenPtrVec(256); + lex_level(line, pos, ')', (PseudoAst){ + .token_pool = ps_ast.token_pool, + .top_tokens = &procsub_toks + }); + (*pos)++; // consume ')' + Token* tok_ptr = push_to_TokenVec(ps_ast.token_pool, (Token){ + .kind = TOK_SUB_OUT, + .as.procsub_toks = procsub_toks, + }); + push_to_TokenPtrVec(ps_ast.top_tokens, tok_ptr); + } + } +} + +Ast lex(char* input) { + size_t line_pos = 0; + TokenVec token_pool = make_TokenVec(256); + TokenPtrVec top_tokens = make_TokenPtrVec(256); + lex_level(input, &line_pos, '\0', (PseudoAst){ + .token_pool = &token_pool, + .top_tokens = &top_tokens + }); + return (Ast){.token_pool = token_pool, .top_tokens = top_tokens}; +} diff --git a/redes/syntax.h b/redes/syntax.h new file mode 100644 index 0000000..8404d15 --- /dev/null +++ b/redes/syntax.h @@ -0,0 +1,167 @@ +# pragma once + +#include "utils.h" + + +// Stage 1 + +typedef enum { + PLUS, + MINUS, + MULT, +DIV, +} BinOp; + +typedef enum { + ARITHM_INT_LIT, + ARITHM_BINOP, +} ArithmNodeKind; + +typedef struct ArithmNode ArithmNode; +struct ArithmNode { + ArithmNodeKind kind; + union { + int lit_val; + IntVec open_fds; + struct { + BinOp op; + ArithmNode* left; + ArithmNode* right; + } binop; + } as; +}; +DECLARE_VEC(ArithmNode, ArithmNodeVec) + + +typedef struct { + ArithmNodeVec nodes; + ArithmNode* root; +} ArithmAst; + + +typedef enum { + UN_Q, + SINGLE_Q, + DOUBLE_Q, +} QuotationMode; + +typedef enum { + CHUNK_LIT, + CHUNK_VAR, + CHUNK_CMDSUB, + CHUNK_ARITHM, +} ChunkKind; + +typedef struct { + QuotationMode qmode; + ChunkKind kind; + union { + char* lit_str; + char* varname; + ArithmAst arithm_expr; + // TODO: CHUNK_CMDSUB + } as; +} Chunk; + +DECLARE_VEC(Chunk, Word) + + +typedef enum { + TOK_WORD, + TOK_SPEC, + TOK_SUB_IN, + TOK_SUB_OUT, +} TokenKind; + +typedef enum { + SPEC_PIPE, + SPEC_REDIR_W_TRUNC, + SPEC_REDIR_W_APPEND, + SPEC_REDIR_R, + SPEC_EOL, +} SpecialToken; + +typedef struct Token Token; +DECLARE_VEC(Token*, TokenPtrVec) + +struct Token { + TokenKind kind; + union { + Word word; + SpecialToken spec; + TokenPtrVec procsub_toks; + } as; +}; + +DECLARE_VEC(Token, TokenVec) + +typedef struct { + TokenVec token_pool; + TokenPtrVec top_tokens; +} Ast; + + + +// Stage 2 + + +typedef enum { + REDIR_FILE_IN, + REDIR_FILE_OUT, + REDIR_FDS, +} RedirectKind; + +typedef struct { + RedirectKind kind; + union { + char* filename; + struct{int from; int to;} fds; + } as; +} Redirect; + +DECLARE_VEC(Redirect, RedirectVec) + +typedef struct { + char** args; + RedirectVec redirects; +} SimpleCommand; + +DECLARE_VEC(SimpleCommand, SimpleCommandVec) +DECLARE_VEC(SimpleCommand*, SimpleCommandPtrVec) + + +typedef enum { + CONN_PIPE, + CONN_EOL, +} Connector; + +typedef struct { + SimpleCommand* cmd; + SimpleCommandPtrVec deps; + Connector conn; +} PlCommand; + +DECLARE_VEC(PlCommand, PlCommandVec) + +typedef struct { + SimpleCommandVec commands; + PlCommandVec pipeline; +} ExecTree; + + + +// Stage 3 + +typedef struct { + int redirected; + int to; +} FdRedirect; + +DECLARE_VEC(FdRedirect, FdRedirectVec) + +typedef struct { + char** args; + FdRedirectVec redirects; + int stdin_fd, stdout_fd; + IntVec open_fds; +} ExecutableCommand; diff --git a/src/exec.c b/src/exec.c new file mode 100644 index 0000000..fccda7a --- /dev/null +++ b/src/exec.c @@ -0,0 +1,189 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +#include "exec.h" +#include "preproc.h" +#include "expand.h" + + +void execute_command(Command cmd, int* pgid) { + + pid_t pid = fork(); + + if (pid == 0) { + if (*pgid == -1) { + setpgid(0, 0); + } else { + setpgid(0, *pgid); + } + + dup2(cmd.stdin_fd, STDIN_FILENO); + dup2(cmd.stdout_fd, STDOUT_FILENO); + + for (int j = 0; j < cmd.redirs.count; j++) { + FdRedirect fd_rd = *get_from_FdRedirectVec(&cmd.redirs, j); + dup2(fd_rd.new_fd, fd_rd.old_fd); + } + + for (int i = 0; i < cmd.child_close_fds.count; i++) { + close(*get_from_IntVec(&cmd.child_close_fds, i)); + } + + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGTSTP, SIG_DFL); + signal(SIGTTIN, SIG_DFL); + signal(SIGTTOU, SIG_DFL); + + execvp(cmd.args[0], cmd.args); + exit(67); + } + + if (*pgid == -1) { + setpgid(pid, pid); + *pgid = pid; + } else { + setpgid(pid, *pgid); + } +} + +Command make_command(ExpandedPipelineElement elem) { + Command cmd = { + .child_close_fds = make_IntVec(64), + .parent_close_fds = make_IntVec(64), + .redirs = make_FdRedirectVec(32), + .term = elem.conn, + }; + StrVec arg_strs = make_StrVec(256); + for (int i = 0; i < elem.cmd_slots.count; i++) { + Slot curr_slot = *get_from_SlotVec(&elem.cmd_slots, i); + Slot next_slot; + int fd; + FdRedirect redir; + switch (curr_slot.kind) { + case SLOT_STR: + push_to_StrVec(&arg_strs, curr_slot.as.str); + break; + case SLOT_SPEC: + switch (curr_slot.as.spec) { + case READ_REDIR: + i++; + next_slot = *get_from_SlotVec(&elem.cmd_slots, i); + if (next_slot.kind != SLOT_STR) exit(6); + fd = open(next_slot.as.str, O_RDONLY, 0644); + redir = (FdRedirect){.old_fd = STDIN_FILENO, .new_fd = fd}; + push_to_FdRedirectVec(&cmd.redirs, redir); + push_to_IntVec(&cmd.parent_close_fds, fd); + push_to_IntVec(&cmd.child_close_fds, fd); + break; + case WRITE_REDIR_TRUNC: + i++; + next_slot = *get_from_SlotVec(&elem.cmd_slots, i); + if (next_slot.kind != SLOT_STR) exit(6); + fd = open(next_slot.as.str, O_WRONLY | O_CREAT | O_TRUNC , 0644); + redir = (FdRedirect){.old_fd = STDOUT_FILENO, .new_fd = fd}; + push_to_FdRedirectVec(&cmd.redirs, redir); + push_to_IntVec(&cmd.parent_close_fds, fd); + push_to_IntVec(&cmd.child_close_fds, fd); + break; + case WRITE_REDIR_APPEND: + i++; + next_slot = *get_from_SlotVec(&elem.cmd_slots, i); + if (next_slot.kind != SLOT_STR) exit(6); + fd = open(next_slot.as.str, O_WRONLY | O_CREAT | O_APPEND, 0644); + redir = (FdRedirect){.old_fd = STDOUT_FILENO, .new_fd = fd}; + push_to_FdRedirectVec(&cmd.redirs, redir); + push_to_IntVec(&cmd.parent_close_fds, fd); + push_to_IntVec(&cmd.child_close_fds, fd); + break; + default: + exit(7); + } + break; + default: + exit(67); + // TODO: other slot kinds + } + } + cmd.args = malloc(sizeof(char*) * arg_strs.count); + for (int j = 0; j < arg_strs.count; j++) { + cmd.args[j] = *get_from_StrVec(&arg_strs, j); + } + return cmd; +} + + +void run_cmds(CommandVec cmds) { + signal(SIGTTIN, SIG_IGN); + signal(SIGTTOU, SIG_IGN); + + int trunk_fd = STDIN_FILENO; + int pgid = -1; + + for (int i = 0; i < cmds.count; i++) { + Command cmd = *get_from_CommandVec(&cmds, i); + cmd.stdin_fd = trunk_fd; + + if (i > 0) { + push_to_IntVec(&cmd.child_close_fds, trunk_fd); + push_to_IntVec(&cmd.parent_close_fds, trunk_fd); + } + + cmd.stdout_fd = STDOUT_FILENO; + if (cmd.term == CONN_PIPE) { + int pipe_fds[2]; + pipe(pipe_fds); + cmd.stdout_fd = pipe_fds[1]; + push_to_IntVec(&cmd.child_close_fds, pipe_fds[1]); + push_to_IntVec(&cmd.parent_close_fds, pipe_fds[1]); + trunk_fd = pipe_fds[0]; + } + + execute_command(cmd, &pgid); + + for (int j = 0; j < cmd.parent_close_fds.count; j++) { + close(*get_from_IntVec(&cmd.parent_close_fds, j)); + } + } + + tcsetpgrp(STDIN_FILENO, pgid); + while (waitpid(-pgid, NULL, 0) > 0); + tcsetpgrp(STDIN_FILENO, getpgrp()); +} + + +CommandVec make_cmd_vec_pl(ExpandedPipeline pl) { + CommandVec cmds = make_CommandVec(256); + for (int i = 0; i < pl.count; i++) { + ExpandedPipelineElement elem = *get_from_ExpandedPipeline(&pl, i); + Command cmd = make_command(elem); + push_to_CommandVec(&cmds, cmd); + } + return cmds; +} + + +void process_input_line(char* line, ShellState* shstate) { + PreprocArena arena = { + .tokens = make_TokenVec(256), + .chunks = make_ChunkVec(256), + .regions = make_RegionVec(256), + }; + TokenPtrVec line_tok_ptrs = lex(line, &arena); + Token* line_toks = malloc(sizeof(Token) * line_tok_ptrs.count); + for (int i = 0; i < line_tok_ptrs.count; i++) { + line_toks[i] = **get_from_TokenPtrVec(&line_tok_ptrs, i); + } + Pipeline pl = parse_tokstream(line_toks); + ExpandedPipeline exp_pl = expand_pipeline(&pl, shstate); + CommandVec cmds = make_cmd_vec_pl(exp_pl); + run_cmds(cmds); + free(line_toks); +} + diff --git a/src/exec.h b/src/exec.h new file mode 100644 index 0000000..9ee8164 --- /dev/null +++ b/src/exec.h @@ -0,0 +1,26 @@ +#pragma once + +#include "utils.h" +#include "preproc.h" + + +typedef struct { + int old_fd; + int new_fd; +} FdRedirect; + +DECLARE_VEC(FdRedirect, FdRedirectVec) + +typedef struct { + char** args; + FdRedirectVec redirs; + int stdin_fd; + int stdout_fd; + IntVec child_close_fds; + IntVec parent_close_fds; + Connector term; +} Command; + +DECLARE_VEC(Command, CommandVec) + +void process_input_line(char* line, ShellState* shstate); diff --git a/src/expand.c b/src/expand.c new file mode 100644 index 0000000..11764f9 --- /dev/null +++ b/src/expand.c @@ -0,0 +1,104 @@ +#include "expand.h" +#include "preproc.h" +#include "utils.h" +#include + + +typedef struct { + QuotationMode qmode; + char* content; +} ExpandedChunk; + + +ExpandedChunk expand_chunk(Chunk chunk, ShellState* shstate) { + char* content; + if (chunk.qmode == SINGLE_Q || chunk.kind == LITERAL) { + if (chunk.qmode == SINGLE_Q && chunk.kind != LITERAL) exit(69); + content = chunk.as.literal_str; + } else if (chunk.kind == VAR_EXP) { + content = get_var(chunk.as.varname, shstate); + } else if (chunk.kind == REGION_EXP) { + exit(7); // Not supported yet; arithm + } + return (ExpandedChunk){.qmode = chunk.qmode, .content = content}; +} + + +typedef struct { + StrVec complete_strs; + String active_str; +} WordSplittingGerm; + +void add_expchunk(char* next_str, WordSplittingGerm* curr_res) { + for (int pos = 0; pos < strlen(next_str); pos++) { + if (next_str[pos] == ' ') { + push_to_StrVec(&curr_res->complete_strs, inner(&curr_res->active_str)); + curr_res->active_str = make_String(256); + } else { + push_to_String(&curr_res->active_str, next_str[pos]); + } + } +} + + +void expand_word(Word word, SlotVec* target, ShellState* shstate) { + WordSplittingGerm curr_res = { + .complete_strs = make_StrVec(256), + .active_str = make_String(256) + }; + for (int i = 0; i < word.count; i++) { + Chunk chunk = **get_from_Word(&word, i); + ExpandedChunk expchunk = expand_chunk(chunk, shstate); + switch (expchunk.qmode) { + case UN_Q: + add_expchunk(expchunk.content, &curr_res); + break; + case SINGLE_Q: + append_str_to_String(&curr_res.active_str, expchunk.content); + break; + case DOUBLE_Q: + // TODO: further expansions here + append_str_to_String(&curr_res.active_str, expchunk.content); + break; + } + } + if (curr_res.active_str.count > 0) { + push_to_StrVec(&curr_res.complete_strs, inner(&curr_res.active_str)); + } + for (int j = 0; j < curr_res.complete_strs.count; j++) { + char* str = *get_from_StrVec(&curr_res.complete_strs, j); + push_to_SlotVec(target, (Slot){.kind = SLOT_STR, .as.str = str}); + } +} + +ExpandedPipelineElement expand_pipeline_element(PipelineElement elem, ShellState* shstate) { + ExpandedPipelineElement result = {.cmd_slots = make_SlotVec(256), .conn = elem.conn}; + for (int i = 0; i < elem.cmd_toks.count; i++) { + Token curr_tok = *get_from_TokenVec(&elem.cmd_toks, i); + switch (curr_tok.kind) { + case TOK_WORD: + expand_word(curr_tok.as.word, &result.cmd_slots, shstate); + break; + case TOK_SPECIAL: + push_to_SlotVec(&result.cmd_slots, (Slot){.kind = SLOT_SPEC, .as.spec = curr_tok.as.spec}); + break; + case TOK_PROCSUB_IN: + push_to_SlotVec(&result.cmd_slots, (Slot){.kind = SLOT_PROC_IN, .as.proc_reg= curr_tok.as.procsub_region}); + break; + case TOK_PROCSUB_OUT: + push_to_SlotVec(&result.cmd_slots, (Slot){.kind = SLOT_PROC_OUT, .as.proc_reg= curr_tok.as.procsub_region}); + break; + } + } + return result; +} + +ExpandedPipeline expand_pipeline(Pipeline* pl, ShellState *shstate) { + ExpandedPipeline exp_pl = make_ExpandedPipeline(256); + for (int i = 0; i < pl->count; i++) { + PipelineElement elem = *get_from_Pipeline(pl, i); + ExpandedPipelineElement exp_elem = expand_pipeline_element(elem, shstate); + push_to_ExpandedPipeline(&exp_pl, exp_elem); + } + return exp_pl; +} diff --git a/src/expand.h b/src/expand.h new file mode 100644 index 0000000..b353f39 --- /dev/null +++ b/src/expand.h @@ -0,0 +1,31 @@ +# pragma once + +#include "preproc.h" +#include "utils.h" + +typedef enum { + SLOT_STR, + SLOT_SPEC, + SLOT_PROC_IN, + SLOT_PROC_OUT, +} SlotKind; + +typedef struct { + SlotKind kind; + union { + char* str; + SpecialToken spec; + Region* proc_reg; + } as; +} Slot; + +DECLARE_VEC(Slot, SlotVec) + +typedef struct { + SlotVec cmd_slots; + Connector conn; +} ExpandedPipelineElement; + +DECLARE_VEC(ExpandedPipelineElement, ExpandedPipeline) + +ExpandedPipeline expand_pipeline(Pipeline* pl, ShellState* shstate); diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..35c2bff --- /dev/null +++ b/src/main.c @@ -0,0 +1,23 @@ +#include +#include +#include +#include +#include + +#include "exec.h" + + +int main() { + char* line; + ShellState shstate = {.shell_vars = make_KeyValMap(1), .aliases = make_KeyValMap(1)}; + while (1) { + line = readline("$ "); + if (line[0] == '\0') continue; + if (strcmp(line, "exit") == 0) exit(0); + add_history(line); + process_input_line(line, &shstate); + } + free(line); +} + + diff --git a/src/preproc.c b/src/preproc.c new file mode 100644 index 0000000..4cd1c5b --- /dev/null +++ b/src/preproc.c @@ -0,0 +1,318 @@ +#include +#include +#include +#include +#include +#include + +#include "preproc.h" +#include "utils.h" + + +// Lexing + +typedef struct { + char* str; + size_t pos; +} RawLine; + + +String collect_identifier(RawLine* line) { + String id_buf = make_String(256); + while (line->str[line->pos] == '_' || isalnum(line->str[line->pos])) { + push_to_String(&id_buf, line->str[line->pos++]); + } + return id_buf; +} + + + +TokenPtrVec lex_level(RawLine* line, char until, PreprocArena* arena); + + +void expect_char(RawLine* line, char expected) { + if (line->str[line->pos] != expected) { + exit(45); // TODO: nicer handling + } + line->pos++; +} + + +/* +MaybeChunk lex_expansion_chunk(RawLine* line, RegionVec* reg_arena, QuotationMode qmode) { + line->pos++; // Consume $ + if (line->str[line->pos] == '(') { + line->pos++; + if (line->str[line->pos] == '(') { + exit(1); + // TODO: this (and potentially the other parsers must be custom, we need to recognize arithm-internal parens from the terminating `))` ) + + + TokenVec inner_toks = lex_level(line, ')', reg_arena); + expect_char(line, ')'); + expect_char(line, ')'); + Region* reg_ptr = push_to_RegionVec( + reg_arena, + (Region){.mode = ARITHM, .tokens = inner_toks} + ); + return (MaybeChunk){ + .some = true, + .value = (Chunk){ + .qmode = qmode, + .kind = REGION_EXP, + .as.reg_ptr = reg_ptr + }, + }; + } else { + TokenVec inner_toks = lex_level(line, ')', reg_arena); + expect_char(line, ')'); + Region* reg_ptr = push_to_RegionVec( + reg_arena, + (Region){.mode = CMDS, .tokens = inner_toks} + ); + return (MaybeChunk) { + .some = true, + .value = (Chunk){ + .qmode = qmode, + .kind = REGION_EXP, + .as.reg_ptr = reg_ptr, + } + }; + } + } else { + String id_name = collect_identifier(line); + if (id_name.count > 0) { + return (MaybeChunk){ + .some = true, + .value = (Chunk){ + .qmode = qmode, + .kind = VAR_EXP, + .as.varname = inner(&id_name), + } + }; + } + } + return (MaybeChunk){.some = false}; +} +*/ + + +void lex_double_quoted_section(RawLine* line, Word* coll, PreprocArena* arena) { + line->pos++; + String accum = make_String(256); + while (line->str[line->pos] != '\"') { + if (line->str[line->pos] == '\0') exit(43); + + MaybeChunk exp_chunk = {.some = false}; + + if (line->str[line->pos] == '$') { + exit(1); + /* + exp_chunk = lex_expansion_chunk(line, reg_arena, DOUBLE_Q); + if (!exp_chunk.some) { + push_to_String(&accum, '$'); + } + */ + } else { + push_to_String(&accum, line->str[line->pos++]); + } + // TODO: other special cases beyond $ + + if (exp_chunk.some) { + if (accum.count > 0) { + Chunk accum_chunk = {.qmode = DOUBLE_Q, .kind = LITERAL, .as.literal_str = inner(&accum)}; + Chunk* accum_ptr = push_to_ChunkVec(&arena->chunks, accum_chunk); + push_to_Word(coll, accum_ptr); + accum = make_String(256); + } + Chunk* exp_ptr = push_to_ChunkVec(&arena->chunks, exp_chunk.value); + push_to_Word(coll, exp_ptr); + exp_chunk.some = false; + } + } + line->pos++; + if (accum.count > 0) { + Chunk accum_chunk = {.qmode = DOUBLE_Q, .kind = LITERAL, .as.literal_str = inner(&accum)}; + Chunk* accum_ptr = push_to_ChunkVec(&arena->chunks, accum_chunk); + push_to_Word(coll, accum_ptr); + accum = make_String(256); + } +} + + +Chunk next_word_unquoted(RawLine* line, RegionVec* reg_arena) { + String buf = make_String(256); + for (char c; (c=line->str[line->pos], c != '$' && c != '\0' && c != ' '); line->pos++) { + push_to_String(&buf, c); + } + return (Chunk){.qmode = UN_Q, .kind = LITERAL, .as.literal_str = inner(&buf)}; +} + + +Chunk collect_single_quoted_section(RawLine* line) { + line->pos++; // Consume quote mark + String chunk_str = make_String(256); + while (line->str[line->pos] != '\'') { + if (line->str[line->pos] == '\0') exit(44); + push_to_String(&chunk_str, line->str[line->pos++]); + } + line->pos++; // Consume closing quote + return (Chunk){ + .qmode = SINGLE_Q, + .kind = LITERAL, + .as.literal_str = inner(&chunk_str), + }; +} + + +typedef struct { + Word word_germ; + String unq_liter_germ; +} LexState; + + +void close_word_and_lit_germs(LexState* state, TokenPtrVec* toks, PreprocArena* arena) { + if (state->unq_liter_germ.count > 0) { + Chunk chu = { + .qmode = UN_Q, + .kind = LITERAL, + .as.literal_str = inner(&state->unq_liter_germ) + }; + Chunk* chu_ptr = push_to_ChunkVec(&arena->chunks, chu); + push_to_Word(&state->word_germ, chu_ptr); + state->unq_liter_germ = make_String(256); + } + if (state->word_germ.count > 0) { + Token tok = {.kind = TOK_WORD, .as.word = state->word_germ}; + Token* tok_ptr = push_to_TokenVec(&arena->tokens, tok); + push_to_TokenPtrVec(toks, tok_ptr); + state->word_germ = make_Word(256); + } +} + +TokenPtrVec lex_level(RawLine* line, char until, PreprocArena* arena) { + + TokenPtrVec line_toks = make_TokenPtrVec(256); + LexState state = { + .word_germ = make_Word(256), + .unq_liter_germ = make_String(256) + }; + + while (line->str[line->pos] != until) { + + if (line->str[line->pos] == ' ') { + close_word_and_lit_germs(&state, &line_toks, arena); + while (line->str[line->pos] == ' ') line->pos++; + continue; + } + + Token* tok_ptr; + + switch (line->str[line->pos]) { + case '\'': + push_to_Word(&state.word_germ, push_to_ChunkVec(&arena->chunks, collect_single_quoted_section(line))); + break; + case '\"': + lex_double_quoted_section(line, &state.word_germ, arena); + break; + case '|': + line->pos++; + close_word_and_lit_germs(&state, &line_toks, arena); + tok_ptr = push_to_TokenVec(&arena->tokens, (Token){.kind = TOK_SPECIAL, .as.spec = PIPE}); + push_to_TokenPtrVec(&line_toks, tok_ptr); + break; + case '$': + exit(1); + /* + line->pos++; + MaybeChunk mchunk = lex_expansion_chunk(line, reg_arena, UN_Q); + if (mchunk.some) { + push_to_Word(&state.word_germ, mchunk.value); + } else { + push_to_String(&state.unq_liter_germ, '$'); + } + */ + break; + case '>': + close_word_and_lit_germs(&state, &line_toks, arena); + line->pos++; + + switch (line->str[line->pos]) { + case '(': + line->pos++; + TokenPtrVec sub_regtoks = lex_level(line, ')', arena); + line->pos++; + Region subreg = {.mode = CMDS, .tokens = sub_regtoks}; + Region* subreg_ptr = push_to_RegionVec(&arena->regions, subreg); + Token tok = {.kind = TOK_PROCSUB_OUT, .as.procsub_region = subreg_ptr}; + tok_ptr = push_to_TokenVec(&arena->tokens, tok); + push_to_TokenPtrVec(&line_toks, tok_ptr); + break; + case '>': + line->pos++; + tok_ptr = push_to_TokenVec(&arena->tokens, (Token){.kind = TOK_SPECIAL, .as.spec = WRITE_REDIR_APPEND}); + push_to_TokenPtrVec(&line_toks, tok_ptr); + break; + default: + tok_ptr = push_to_TokenVec(&arena->tokens, (Token){.kind = TOK_SPECIAL, .as.spec = WRITE_REDIR_TRUNC}); + push_to_TokenPtrVec(&line_toks, tok_ptr); + } + break; + case '<': + close_word_and_lit_germs(&state, &line_toks, arena); + line->pos++; + if (line->str[line->pos] == '(') { + line->pos++; + TokenPtrVec sub_regtoks = lex_level(line, ')', arena); + line->pos++; + Region subreg = {.mode = CMDS, .tokens = sub_regtoks}; + Region* reg_ptr = push_to_RegionVec(&arena->regions, subreg); + Token reg_tok = {.kind = TOK_PROCSUB_IN, .as.procsub_region = reg_ptr}; + Token* reg_tok_ptr = push_to_TokenVec(&arena->tokens, reg_tok); + push_to_TokenPtrVec(&line_toks, reg_tok_ptr); + } else { + Token* redir_ptr = push_to_TokenVec(&arena->tokens, (Token){.kind = TOK_SPECIAL, .as.spec = READ_REDIR}); + push_to_TokenPtrVec(&line_toks, redir_ptr); + } + break; + default: + push_to_String(&state.unq_liter_germ, line->str[line->pos++]); + } + } + // TODO: handle until char _and_ EOL + close_word_and_lit_germs(&state, &line_toks, arena); + return line_toks; +} + + +TokenPtrVec lex(char* input, PreprocArena* arena) { + RawLine line = {.str = input, .pos = 0}; + TokenPtrVec toks = lex_level(&line, '\0', arena); + Token* eol_ptr = push_to_TokenVec(&arena->tokens, (Token){.kind = TOK_SPECIAL, .as.spec = EOL}); + push_to_TokenPtrVec(&toks, eol_ptr); + return toks; +} + + + + +// Parsing + +Pipeline parse_tokstream(Token* toks) { + size_t tok_pos = 0; + Pipeline result = make_Pipeline(256); + TokenVec tok_buf = make_TokenVec(256); + while (true) { + Token curr_tok = toks[tok_pos++]; + if (curr_tok.kind == TOK_SPECIAL && curr_tok.as.spec == PIPE) { + push_to_Pipeline(&result, (PipelineElement){.cmd_toks = tok_buf, .conn = CONN_PIPE}); + tok_buf = make_TokenVec(256); + } else if (curr_tok.kind == TOK_SPECIAL && curr_tok.as.spec == EOL) { + push_to_Pipeline(&result, (PipelineElement){.cmd_toks = tok_buf, .conn = CONN_EOL}); + break; + } else { + push_to_TokenVec(&tok_buf, curr_tok); + } + } + return result; +} diff --git a/src/preproc.h b/src/preproc.h new file mode 100644 index 0000000..7907f9d --- /dev/null +++ b/src/preproc.h @@ -0,0 +1,116 @@ +#pragma once + +#include "utils.h" + +typedef struct Region Region; + + +// Lexing stage +typedef enum { + UN_Q, + SINGLE_Q, + DOUBLE_Q, +} QuotationMode; + +typedef enum { + LITERAL, + REGION_EXP, + VAR_EXP, // TODO: Later include other exps +} ChunkKind; + +typedef struct { + ChunkKind kind; + QuotationMode qmode; + union { + char* literal_str; + Region* reg_ptr; + char* varname; + } as; +} Chunk; +DECLARE_MAYBE(Chunk, MaybeChunk); +DECLARE_VEC(Chunk*, Word) + +typedef enum { + TOK_WORD, + TOK_SPECIAL, + TOK_PROCSUB_IN, + TOK_PROCSUB_OUT, +} TokenKind; + +typedef enum { + PIPE, + WRITE_REDIR_TRUNC, + WRITE_REDIR_APPEND, + READ_REDIR, + EOL, +} SpecialToken; +DECLARE_MAYBE(SpecialToken, MaybeSpecialToken) + +typedef struct { + TokenKind kind; + + union { + Word word; + SpecialToken spec; + Region* procsub_region; + } as; +} Token; + + +DECLARE_VEC(Token*, TokenPtrVec) + + +typedef enum { + CMDS, + ARITHM, +} RegionMode; + +struct Region { + RegionMode mode; + TokenPtrVec tokens; +}; +DECLARE_VEC(Region, RegionVec) + + + +DECLARE_VEC(Chunk, ChunkVec) +DECLARE_VEC(Token, TokenVec) + + + +// Lexing + +typedef struct { + RegionVec regions; + TokenVec tokens; + ChunkVec chunks; +} PreprocArena; + + +TokenPtrVec lex(char* input, PreprocArena* arena); + + +// Parsing + +typedef enum { + CONN_PIPE, + CONN_EOL, + // TODO: add the |& thingy +} Connector; + +DECLARE_MAYBE(Connector, MaybeConnector) + +typedef struct { + TokenVec cmd_toks; + Connector conn; +} PipelineElement; + +DECLARE_VEC(PipelineElement, Pipeline) + +Pipeline parse_tokstream(Token* tokstream); + + + + + + diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..1f08445 --- /dev/null +++ b/src/utils.c @@ -0,0 +1,71 @@ +#include "utils.h" +#include +#include + +void append_str_to_String(String* target, const char* src_str) { + size_t len = strlen(src_str); + + for (size_t i = 0; i < len; i++) { + push_to_String(target, src_str[i]); + } +} + +char* inner(String* str) { + char *out = malloc(str->count + 1); + if (!out) abort(); + + for (size_t i = 0; i < str->count; i++) { + out[i] = *get_from_String(str, i); + } + + out[str->count] = '\0'; + return out; +} + + +void set_map_pair(KeyValMap* map, char* key, char* val) { +for (int i = 0; i < map->count; i++) { + char* key_i = get_from_KeyValMap(map, i)->key; + if (key_i == NULL) continue; + if (strcmp(key_i, key) == 0) { + // TODO: maybe copy instead + strcpy( + map->blocks[i / map->block_size][i % map->block_size].val, + val + ); + break; + } + } +} + +MaybeStr get_map_value(KeyValMap* map, char* key) { + for (int i = 0; i < map->count; i++) { + char* key_i = get_from_KeyValMap(map, i)->key; + if (key_i == NULL) continue; + if (strcmp(key_i, key) == 0) { + return (MaybeStr){ + .some = true, + .value = get_from_KeyValMap(map, i)->val + }; + } + } + return (MaybeStr){.some = false}; +} + +void unset_map_key(KeyValMap* map, char* key) { + for (int i = 0; i < map->count; i++) { + char* key_i = get_from_KeyValMap(map, i)->key; + if (key_i == NULL) continue; + if (strcmp(key_i, key) == 0) { + map->blocks[i / map->block_size][i % map->block_size].key = NULL; + break; + } + } +} + + +char* get_var(char* varname, ShellState* shstate) { + MaybeStr maybe_varval = get_map_value(&shstate->shell_vars, varname); + if (maybe_varval.some == true) return maybe_varval.value; + return getenv(varname); +} diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 0000000..a213c71 --- /dev/null +++ b/src/utils.h @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + + +#define DECLARE_VEC(T, Name) \ + typedef struct { \ + T **blocks; \ + size_t count; \ + size_t used; \ + size_t cap; \ + size_t block_size; \ + } Name; \ + \ + static inline Name make_##Name(size_t block_size) { \ + return (Name){ \ + .blocks = NULL, \ + .count = 0, \ + .used = 0, \ + .cap = 0, \ + .block_size = block_size, \ + }; \ + } \ + \ + static inline T *push_to_##Name(Name *vec, T item) { \ + size_t block_index = vec->count / vec->block_size; \ + size_t item_index = vec->count % vec->block_size; \ + \ + if (block_index == vec->used) { \ + if (vec->used == vec->cap) { \ + size_t new_cap = vec->cap \ + ? vec->cap * 2 \ + : 8; \ + \ + T **new_blocks = realloc( \ + vec->blocks, \ + new_cap * sizeof(T *) \ + ); \ + \ + if (!new_blocks) abort(); \ + \ + vec->blocks = new_blocks; \ + vec->cap = new_cap; \ + } \ + \ + vec->blocks[vec->used] = \ + malloc(vec->block_size * sizeof(T)); \ + \ + if (!vec->blocks[vec->used]) abort(); \ + \ + vec->used++; \ + } \ + \ + T *ptr = &vec->blocks[block_index][item_index]; \ + *ptr = item; \ + \ + vec->count++; \ + return ptr; \ + } \ + \ + static inline T* get_from_##Name(Name *vec, size_t index) { \ + if (index >= vec->count) return NULL; \ + \ + return &vec->blocks \ + [index / vec->block_size] \ + [index % vec->block_size]; \ + } + + +DECLARE_VEC(int, IntVec) + +DECLARE_VEC(char, String); +DECLARE_VEC(char*, StrVec) + + +void append_str_to_String(String* target, const char* src_str); +char* inner(String* str); + + + +#define DECLARE_MAYBE(T, Name) \ + typedef struct { \ + bool some; \ + T value; \ + } Name; + +DECLARE_MAYBE(char*, MaybeStr) + + +typedef struct { + char* key; + char* val; +} StrPair; +DECLARE_VEC(StrPair, KeyValMap) + +void set_map_pair(KeyValMap* map, char* key, char* val); +MaybeStr get_map_value(KeyValMap* map, char* key); +void unset_map_key(KeyValMap* map, char* key); + + +typedef struct { + KeyValMap aliases; + KeyValMap shell_vars; +} ShellState; + +char* get_var(char* varname, ShellState* shstate); diff --git a/syntax.h b/syntax.h deleted file mode 100644 index 8404d15..0000000 --- a/syntax.h +++ /dev/null @@ -1,167 +0,0 @@ -# pragma once - -#include "utils.h" - - -// Stage 1 - -typedef enum { - PLUS, - MINUS, - MULT, -DIV, -} BinOp; - -typedef enum { - ARITHM_INT_LIT, - ARITHM_BINOP, -} ArithmNodeKind; - -typedef struct ArithmNode ArithmNode; -struct ArithmNode { - ArithmNodeKind kind; - union { - int lit_val; - IntVec open_fds; - struct { - BinOp op; - ArithmNode* left; - ArithmNode* right; - } binop; - } as; -}; -DECLARE_VEC(ArithmNode, ArithmNodeVec) - - -typedef struct { - ArithmNodeVec nodes; - ArithmNode* root; -} ArithmAst; - - -typedef enum { - UN_Q, - SINGLE_Q, - DOUBLE_Q, -} QuotationMode; - -typedef enum { - CHUNK_LIT, - CHUNK_VAR, - CHUNK_CMDSUB, - CHUNK_ARITHM, -} ChunkKind; - -typedef struct { - QuotationMode qmode; - ChunkKind kind; - union { - char* lit_str; - char* varname; - ArithmAst arithm_expr; - // TODO: CHUNK_CMDSUB - } as; -} Chunk; - -DECLARE_VEC(Chunk, Word) - - -typedef enum { - TOK_WORD, - TOK_SPEC, - TOK_SUB_IN, - TOK_SUB_OUT, -} TokenKind; - -typedef enum { - SPEC_PIPE, - SPEC_REDIR_W_TRUNC, - SPEC_REDIR_W_APPEND, - SPEC_REDIR_R, - SPEC_EOL, -} SpecialToken; - -typedef struct Token Token; -DECLARE_VEC(Token*, TokenPtrVec) - -struct Token { - TokenKind kind; - union { - Word word; - SpecialToken spec; - TokenPtrVec procsub_toks; - } as; -}; - -DECLARE_VEC(Token, TokenVec) - -typedef struct { - TokenVec token_pool; - TokenPtrVec top_tokens; -} Ast; - - - -// Stage 2 - - -typedef enum { - REDIR_FILE_IN, - REDIR_FILE_OUT, - REDIR_FDS, -} RedirectKind; - -typedef struct { - RedirectKind kind; - union { - char* filename; - struct{int from; int to;} fds; - } as; -} Redirect; - -DECLARE_VEC(Redirect, RedirectVec) - -typedef struct { - char** args; - RedirectVec redirects; -} SimpleCommand; - -DECLARE_VEC(SimpleCommand, SimpleCommandVec) -DECLARE_VEC(SimpleCommand*, SimpleCommandPtrVec) - - -typedef enum { - CONN_PIPE, - CONN_EOL, -} Connector; - -typedef struct { - SimpleCommand* cmd; - SimpleCommandPtrVec deps; - Connector conn; -} PlCommand; - -DECLARE_VEC(PlCommand, PlCommandVec) - -typedef struct { - SimpleCommandVec commands; - PlCommandVec pipeline; -} ExecTree; - - - -// Stage 3 - -typedef struct { - int redirected; - int to; -} FdRedirect; - -DECLARE_VEC(FdRedirect, FdRedirectVec) - -typedef struct { - char** args; - FdRedirectVec redirects; - int stdin_fd, stdout_fd; - IntVec open_fds; -} ExecutableCommand; diff --git a/utils.c b/utils.c deleted file mode 100644 index 1f08445..0000000 --- a/utils.c +++ /dev/null @@ -1,71 +0,0 @@ -#include "utils.h" -#include -#include - -void append_str_to_String(String* target, const char* src_str) { - size_t len = strlen(src_str); - - for (size_t i = 0; i < len; i++) { - push_to_String(target, src_str[i]); - } -} - -char* inner(String* str) { - char *out = malloc(str->count + 1); - if (!out) abort(); - - for (size_t i = 0; i < str->count; i++) { - out[i] = *get_from_String(str, i); - } - - out[str->count] = '\0'; - return out; -} - - -void set_map_pair(KeyValMap* map, char* key, char* val) { -for (int i = 0; i < map->count; i++) { - char* key_i = get_from_KeyValMap(map, i)->key; - if (key_i == NULL) continue; - if (strcmp(key_i, key) == 0) { - // TODO: maybe copy instead - strcpy( - map->blocks[i / map->block_size][i % map->block_size].val, - val - ); - break; - } - } -} - -MaybeStr get_map_value(KeyValMap* map, char* key) { - for (int i = 0; i < map->count; i++) { - char* key_i = get_from_KeyValMap(map, i)->key; - if (key_i == NULL) continue; - if (strcmp(key_i, key) == 0) { - return (MaybeStr){ - .some = true, - .value = get_from_KeyValMap(map, i)->val - }; - } - } - return (MaybeStr){.some = false}; -} - -void unset_map_key(KeyValMap* map, char* key) { - for (int i = 0; i < map->count; i++) { - char* key_i = get_from_KeyValMap(map, i)->key; - if (key_i == NULL) continue; - if (strcmp(key_i, key) == 0) { - map->blocks[i / map->block_size][i % map->block_size].key = NULL; - break; - } - } -} - - -char* get_var(char* varname, ShellState* shstate) { - MaybeStr maybe_varval = get_map_value(&shstate->shell_vars, varname); - if (maybe_varval.some == true) return maybe_varval.value; - return getenv(varname); -} diff --git a/utils.h b/utils.h deleted file mode 100644 index a213c71..0000000 --- a/utils.h +++ /dev/null @@ -1,111 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - - -#define DECLARE_VEC(T, Name) \ - typedef struct { \ - T **blocks; \ - size_t count; \ - size_t used; \ - size_t cap; \ - size_t block_size; \ - } Name; \ - \ - static inline Name make_##Name(size_t block_size) { \ - return (Name){ \ - .blocks = NULL, \ - .count = 0, \ - .used = 0, \ - .cap = 0, \ - .block_size = block_size, \ - }; \ - } \ - \ - static inline T *push_to_##Name(Name *vec, T item) { \ - size_t block_index = vec->count / vec->block_size; \ - size_t item_index = vec->count % vec->block_size; \ - \ - if (block_index == vec->used) { \ - if (vec->used == vec->cap) { \ - size_t new_cap = vec->cap \ - ? vec->cap * 2 \ - : 8; \ - \ - T **new_blocks = realloc( \ - vec->blocks, \ - new_cap * sizeof(T *) \ - ); \ - \ - if (!new_blocks) abort(); \ - \ - vec->blocks = new_blocks; \ - vec->cap = new_cap; \ - } \ - \ - vec->blocks[vec->used] = \ - malloc(vec->block_size * sizeof(T)); \ - \ - if (!vec->blocks[vec->used]) abort(); \ - \ - vec->used++; \ - } \ - \ - T *ptr = &vec->blocks[block_index][item_index]; \ - *ptr = item; \ - \ - vec->count++; \ - return ptr; \ - } \ - \ - static inline T* get_from_##Name(Name *vec, size_t index) { \ - if (index >= vec->count) return NULL; \ - \ - return &vec->blocks \ - [index / vec->block_size] \ - [index % vec->block_size]; \ - } - - -DECLARE_VEC(int, IntVec) - -DECLARE_VEC(char, String); -DECLARE_VEC(char*, StrVec) - - -void append_str_to_String(String* target, const char* src_str); -char* inner(String* str); - - - -#define DECLARE_MAYBE(T, Name) \ - typedef struct { \ - bool some; \ - T value; \ - } Name; - -DECLARE_MAYBE(char*, MaybeStr) - - -typedef struct { - char* key; - char* val; -} StrPair; -DECLARE_VEC(StrPair, KeyValMap) - -void set_map_pair(KeyValMap* map, char* key, char* val); -MaybeStr get_map_value(KeyValMap* map, char* key); -void unset_map_key(KeyValMap* map, char* key); - - -typedef struct { - KeyValMap aliases; - KeyValMap shell_vars; -} ShellState; - -char* get_var(char* varname, ShellState* shstate); -- cgit v1.2.3