diff options
| author | Ákos Kőrösi <korakos99@gmail.com> | 2026-05-21 12:44:01 +0200 |
|---|---|---|
| committer | Ákos Kőrösi <korakos99@gmail.com> | 2026-05-21 12:44:01 +0200 |
| commit | fc27548c447ed9bcd07493983431e0a6e72c7cef (patch) | |
| tree | dbb25a8cc09cbacd8d0d1dcd4a3962f468f96551 /src | |
| parent | 2efe642849cf6d45d5413cd77d8cafaf9b32db00 (diff) | |
restructure dir
Diffstat (limited to 'src')
| -rw-r--r-- | src/exec.c | 194 | ||||
| -rw-r--r-- | src/exec.h | 26 | ||||
| -rw-r--r-- | src/expand.c | 104 | ||||
| -rw-r--r-- | src/expand.h | 31 | ||||
| -rw-r--r-- | src/lexparse.c | 229 | ||||
| -rw-r--r-- | src/main.c | 10 | ||||
| -rw-r--r-- | src/preproc.c | 393 | ||||
| -rw-r--r-- | src/preproc.h | 116 | ||||
| -rw-r--r-- | src/stages.h | 7 | ||||
| -rw-r--r-- | src/syntax.h | 194 |
10 files changed, 616 insertions, 688 deletions
@@ -1,38 +1,35 @@ #include <stdio.h> -#include <stdlib.h> -#include <string.h> #include <unistd.h> #include <fcntl.h> #include <signal.h> #include <sys/wait.h> -#include "utils.h" -#include "exec.h" -#include "preproc.h" -#include "expand.h" +#include "syntax.h" -void execute_command(Command cmd, int* pgid) { +void execute_cmd(ExecutableCommand cmd, int* pgid) { pid_t pid = fork(); - + if (pid == 0) { - if (*pgid == -1) { + + 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.redirects.count; i++) { + FdRedirect fd_rd = *get_from_FdRedirectVec(&cmd.redirects, i); + dup2(fd_rd.to, fd_rd.redirected); } - for (int i = 0; i < cmd.child_close_fds.count; i++) { - close(*get_from_IntVec(&cmd.child_close_fds, i)); + for (int j = 0; j < cmd.open_fds.count; j++) { + int fd = *get_from_IntVec(&cmd.open_fds, j); + close(fd); } signal(SIGINT, SIG_DFL); @@ -53,102 +50,90 @@ void execute_command(Command cmd, int* 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; + +ExecutableCommand process_pl_command(PlCommand pl_cmd) { + StrVec arg_vec = make_StrVec(256); + for (int i = 0; i < pl_cmd.args.count; i++) { + Arg arg = *get_from_ArgVec(&pl_cmd.args, i); + switch (arg.kind) { + case ARG_LIT: + push_to_StrVec(&arg_vec, arg.as.lit_str); + break; + default: + exit(67); + } + } + + FdRedirectVec fd_redirs = make_FdRedirectVec(256); + IntVec open_fds = make_IntVec(256); + for (int j = 0; j < pl_cmd.redirects.count; j++) { + Redirect redir = *get_from_RedirectVec(&pl_cmd.redirects, j); + FdRedirect fd_redir; int fd; - FdRedirect redir; - switch (curr_slot.kind) { - case SLOT_STR: - push_to_StrVec(&arg_strs, curr_slot.as.str); + switch (redir.kind) { + case REDIR_READ: + fd = open(redir.as.filename, O_RDONLY, 0644); + push_to_IntVec(&open_fds, fd); + fd_redir = (FdRedirect){.to = fd, .redirected = STDIN_FILENO}; + break; + case REDIR_WRITE_APPEND: + fd = open(redir.as.filename, O_WRONLY | O_CREAT | O_APPEND, 0644); + push_to_IntVec(&open_fds, fd); + fd_redir = (FdRedirect){.to = fd, .redirected = STDOUT_FILENO}; 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); - } + case REDIR_WRITE_TRUNC: + fd = open(redir.as.filename, O_WRONLY | O_CREAT | O_TRUNC, 0644); + push_to_IntVec(&open_fds, fd); + fd_redir = (FdRedirect){.to = fd, .redirected = STDOUT_FILENO}; 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; + push_to_FdRedirectVec(&fd_redirs, fd_redir); } + char** args = malloc(sizeof(char*) * (arg_vec.count + 1)); + for (int i = 0; i < arg_vec.count; i++) { + args[i] = *get_from_StrVec(&arg_vec, i); + } + args[arg_vec.count] = NULL; + + return (ExecutableCommand){.args = args, .redirects = fd_redirs, .open_fds = open_fds}; +} -void run_cmds(CommandVec cmds) { - signal(SIGTTIN, SIG_IGN); - signal(SIGTTOU, SIG_IGN); +void execute_command_pipeline(Pipeline pipeline) { 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; + signal(SIGTTIN, SIG_IGN); + signal(SIGTTOU, SIG_IGN); + + for (int i = 0; i < pipeline.count; i++) { + PlCommand cmd = *get_from_Pipeline(&pipeline, i); + ExecutableCommand exec_cmd = process_pl_command(cmd); + + exec_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); + push_to_IntVec(&exec_cmd.open_fds, trunk_fd); } - cmd.stdout_fd = STDOUT_FILENO; - if (cmd.term == CONN_PIPE) { + exec_cmd.stdout_fd = STDOUT_FILENO; + + if (cmd.conn == 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]); + exec_cmd.stdout_fd = pipe_fds[1]; + push_to_IntVec(&exec_cmd.open_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)); + execute_cmd(exec_cmd, &pgid); + + for (int j = 0; j < exec_cmd.open_fds.count; j++) { + int fd = *get_from_IntVec(&exec_cmd.open_fds, j); + close(fd); } } @@ -157,33 +142,6 @@ void run_cmds(CommandVec cmds) { 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); +void execute_pipeline_tree(PipelineTree pltree) { + execute_command_pipeline(*pltree.root); } - diff --git a/src/exec.h b/src/exec.h deleted file mode 100644 index 9ee8164..0000000 --- a/src/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/src/expand.c b/src/expand.c deleted file mode 100644 index 11764f9..0000000 --- a/src/expand.c +++ /dev/null @@ -1,104 +0,0 @@ -#include "expand.h" -#include "preproc.h" -#include "utils.h" -#include <string.h> - - -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 deleted file mode 100644 index b353f39..0000000 --- a/src/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/src/lexparse.c b/src/lexparse.c new file mode 100644 index 0000000..3a55b33 --- /dev/null +++ b/src/lexparse.c @@ -0,0 +1,229 @@ +#include <ctype.h> +#include "syntax.h" +#include "../src/utils.h" + +typedef struct { + Word word_germ; + String lit_germ; +} LexingBuffer; + +typedef struct { + TokenVec* token_pool; + TokenPtrVec* top_tokens; +} PseudoAst; + +DECLARE_MAYBE(Token, MaybeToken) + +MaybeToken consolidate_buffer(LexingBuffer* buf) { + 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) { + Word word_germ = buf->word_germ; + buf->word_germ = make_Word(256); + return (MaybeToken){ + .some = true, + .value = (Token){ + .kind = TOK_WORD, + .as.word = word_germ + } + }; + } + return (MaybeToken){.some = false}; +} + + +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 = (SpecialToken){ + .kind = SPEC_OP, + .as.op = OP_PIPE, + } + }; + case '>': + next_ch = line[(*pos)+1]; + switch (next_ch) { + case '(': + return (MaybeSpecialToken){.some = false}; + case '>': + *pos += 2; + return (MaybeSpecialToken){ + .some = true, + .value = (SpecialToken){ + .kind = SPEC_OP, + .as.op = OP_REDIR_W_APPEND, + } + }; + default: + (*pos)++; + return (MaybeSpecialToken){ + .some = true, + .value = (SpecialToken){ + .kind = SPEC_OP, + .as.op = OP_REDIR_W_TRUNC, + } + }; + } + case '<': + next_ch = line[(*pos)+1]; + if (line[(*pos)+1] == '(') { + return (MaybeSpecialToken){.some = false}; + } else { + (*pos)++; + return (MaybeSpecialToken){ + .some = true, + .value = (SpecialToken){ + .kind = SPEC_OP, + .as.op = OP_REDIR_R, + } + }; + } + default: + return (MaybeSpecialToken){.some = false}; + } +} + +TokenVec* lex_level(char* line, size_t* pos, char until, TokenVecVec* tok_seq_pool) { + TokenVec tokens = make_TokenVec(256); + LexingBuffer buf = {.word_germ = make_Word(256), .lit_germ = make_String(256)}; + while (line[*pos] != until) { + if (line[*pos] == ' ') { + MaybeToken m_tok = consolidate_buffer(&buf); + if (m_tok.some) { + push_to_TokenVec(&tokens, m_tok.value); + } + while (line[*pos] == ' ') (*pos)++; + continue; + } + MaybeSpecialToken m_spec = recognize_spec_token(line, pos); + if (m_spec.some) { + MaybeToken m_tok = consolidate_buffer(&buf); + if (m_tok.some) { + push_to_TokenVec(&tokens, m_tok.value); + } + push_to_TokenVec(&tokens, (Token){.kind = TOK_SPEC, .as.spec = m_spec.value}); + 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; + MaybeToken m_tok = consolidate_buffer(&buf); + if (m_tok.some) { + push_to_TokenVec(&tokens, m_tok.value); + } + TokenVec* sub_toks = lex_level(line, pos, ')', tok_seq_pool); + (*pos)++; + push_to_TokenVec(&tokens, (Token){ + .kind = TOK_SPEC, + .as.spec = (SpecialToken){ + .kind = SPEC_SUB_IN, + .as.proc_sub_region = sub_toks + } + }); + } else if (line[*pos] == '>' && line[(*pos)+1] == '(') { + *pos += 2; + MaybeToken m_tok = consolidate_buffer(&buf); + if (m_tok.some) { + push_to_TokenVec(&tokens, m_tok.value); + } + TokenVec* sub_toks = lex_level(line, pos, ')', tok_seq_pool); + (*pos)++; + push_to_TokenVec(&tokens, (Token){ + .kind = TOK_SPEC, + .as.spec = (SpecialToken){ + .kind = SPEC_SUB_OUT, + .as.proc_sub_region = sub_toks + } + }); + } else { + push_to_String(&buf.lit_germ, line[(*pos)++]); + } + } + MaybeToken m_last = consolidate_buffer(&buf); + if (m_last.some) { + push_to_TokenVec(&tokens, m_last.value); + } + + if (until == '\0') { + push_to_TokenVec(&tokens, (Token){.kind = TOK_SPEC, .as.spec = (SpecialToken){.kind = SPEC_OP, .as.op = OP_EOL}}); + } + return push_to_TokenVecVec(tok_seq_pool, tokens); +} + +Ast lex(char* input) { + size_t line_pos = 0; + TokenVecVec token_seqs = make_TokenVecVec(256); + TokenVec* root_ptr = lex_level(input, &line_pos, '\0', &token_seqs); + return (Ast){.region_pool = token_seqs, .root = root_ptr}; +} @@ -4,20 +4,20 @@ #include <readline/history.h> #include <string.h> -#include "exec.h" +#include "syntax.h" +#include "stages.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); + Ast ast = lex(line); + PipelineTree pltree = process_ast(ast); + execute_pipeline_tree(pltree); } free(line); } - - diff --git a/src/preproc.c b/src/preproc.c index 4cd1c5b..63e618f 100644 --- a/src/preproc.c +++ b/src/preproc.c @@ -1,318 +1,135 @@ -#include <ctype.h> -#include <stddef.h> -#include <stdlib.h> -#include <string.h> -#include <stdbool.h> +#include <assert.h> #include <stdio.h> +#include "syntax.h" -#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, '$'); - } - */ +void split_str_on_space(char* str, StrVec* result) { + String buf = make_String(256); + for (int pos = 0; str[pos] != '\0'; pos++) { + if (str[pos] == ' ') { + push_to_StrVec(result, inner(&buf)); + buf = make_String(256); } 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; + push_to_String(&buf, str[pos]); } } - 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); + if (buf.count > 0) { + char* buf_str = inner(&buf); + push_to_StrVec(result, buf_str); } - 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++]); +StrVec process_word(Word word) { + StrVec result = make_StrVec(256); + for (int i = 0; i < word.count; i++) { + Chunk chunk = *get_from_Word(&word, i); + switch (chunk.qmode) { + case SINGLE_Q: + push_to_StrVec(&result, chunk.as.lit_str); + break; + case DOUBLE_Q: + // TODO + exit(67); + break; + case UN_Q: + // TODO: this only does literals, implement the rest + split_str_on_space(chunk.as.lit_str, &result); + break; + } } - line->pos++; // Consume closing quote - return (Chunk){ - .qmode = SINGLE_Q, - .kind = LITERAL, - .as.literal_str = inner(&chunk_str), - }; + return result; } 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); - } -} + TokenKind kind; + union { + char* word; + SpecialToken spec; + } as; +} ExpandedToken; -TokenPtrVec lex_level(RawLine* line, char until, PreprocArena* arena) { +DECLARE_VEC(ExpandedToken, ExpandedTokenVec) - TokenPtrVec line_toks = make_TokenPtrVec(256); - LexState state = { - .word_germ = make_Word(256), - .unq_liter_germ = make_String(256) - }; +Pipeline* process_token_sequence(TokenVec tokens, PipelineVec* pl_pool) { - while (line->str[line->pos] != until) { + ExpandedTokenVec exp_tokens = make_ExpandedTokenVec(256); - if (line->str[line->pos] == ' ') { - close_word_and_lit_germs(&state, &line_toks, arena); - while (line->str[line->pos] == ' ') line->pos++; - continue; + for (int m = 0; m < tokens.count; m++) { + Token tok = *get_from_TokenVec(&tokens, m); + StrVec strs; + switch (tok.kind) { + case TOK_SPEC: + push_to_ExpandedTokenVec(&exp_tokens, (ExpandedToken){.kind = TOK_SPEC, .as.spec = tok.as.spec}); + break; + case TOK_WORD: + strs = process_word(tok.as.word); + for (int n = 0; n < strs.count; n++) { + push_to_ExpandedTokenVec(&exp_tokens, (ExpandedToken){.kind = TOK_WORD, .as.word = *get_from_StrVec(&strs, n)}); + } } + } - Token* tok_ptr; + Pipeline pl = make_Pipeline(256); + size_t tok_pos = 0; - 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++; + PlCommand curr_cmd = {.args = make_ArgVec(256), .redirects = make_RedirectVec(256), .conn = CONN_EOL}; - 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); + PlCommand* root_pl; + for (int i = 0; i < exp_tokens.count; i++) { + ExpandedToken curr_tok = *get_from_ExpandedTokenVec(&exp_tokens, i); + ExpandedToken next_tok; + Redirect rd; + switch (curr_tok.kind) { + case TOK_WORD: + push_to_ArgVec(&curr_cmd.args, (Arg){.kind = ARG_LIT, .as.lit_str = curr_tok.as.word}); + break; + case TOK_SPEC: + switch (curr_tok.as.spec.kind) { + case SPEC_OP: + switch (curr_tok.as.spec.as.op) { + case OP_PIPE: + curr_cmd.conn = CONN_PIPE; + push_to_Pipeline(&pl, curr_cmd); + curr_cmd = (PlCommand){.args = make_ArgVec(256), .redirects = make_RedirectVec(256), .conn = CONN_EOL}; + break; + case OP_EOL: + push_to_Pipeline(&pl, curr_cmd); + break; + case OP_REDIR_R: + next_tok = *get_from_ExpandedTokenVec(&exp_tokens, ++i); + assert(next_tok.kind == TOK_WORD); + rd = (Redirect){.kind = REDIR_READ, .as.filename = next_tok.as.word}; + push_to_RedirectVec(&curr_cmd.redirects, rd); + break; + case OP_REDIR_W_APPEND: + next_tok = *get_from_ExpandedTokenVec(&exp_tokens, ++i); + assert(next_tok.kind == TOK_WORD); + rd = (Redirect){.kind = REDIR_WRITE_APPEND, .as.filename = next_tok.as.word}; + push_to_RedirectVec(&curr_cmd.redirects, rd); + break; + case OP_REDIR_W_TRUNC: + next_tok = *get_from_ExpandedTokenVec(&exp_tokens, ++i); + assert(next_tok.kind == TOK_WORD); + rd = (Redirect){.kind = REDIR_WRITE_TRUNC, .as.filename = next_tok.as.word}; + push_to_RedirectVec(&curr_cmd.redirects, rd); + break; + default: + exit(63); + } 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); + exit(62); } - 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; + } + return push_to_PipelineVec(pl_pool, pl); } - - -// 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; +PipelineTree process_ast(Ast ast) { + PipelineVec pls = make_PipelineVec(256); + TokenVec root_toks = *ast.root; + Pipeline* root_pl = process_token_sequence(root_toks, &pls); + return (PipelineTree){.pl_pool = pls, root_pl}; } diff --git a/src/preproc.h b/src/preproc.h deleted file mode 100644 index 7907f9d..0000000 --- a/src/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/src/stages.h b/src/stages.h new file mode 100644 index 0000000..4faaf14 --- /dev/null +++ b/src/stages.h @@ -0,0 +1,7 @@ +#pragma once + +#include "syntax.h" + +Ast lex(char* input); +PipelineTree process_ast(Ast ast); +void execute_pipeline_tree(PipelineTree pltree); diff --git a/src/syntax.h b/src/syntax.h new file mode 100644 index 0000000..0cdb76c --- /dev/null +++ b/src/syntax.h @@ -0,0 +1,194 @@ +# pragma once + +#include "../src/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, +} TokenKind; + +typedef enum { + OP_PIPE, + OP_REDIR_W_TRUNC, + OP_REDIR_W_APPEND, + OP_REDIR_R, + OP_EOL, +} OperatorToken; + + +typedef enum { + SPEC_OP, + SPEC_SUB_IN, + SPEC_SUB_OUT, +} SpecialTokenKind; + +typedef struct { + SpecialTokenKind kind; + union { + OperatorToken op; + void* proc_sub_region; + } as; +} SpecialToken; + + +typedef struct Token Token; +DECLARE_VEC(Token*, TokenPtrVec) + +struct Token { + TokenKind kind; + union { + Word word; + SpecialToken spec; + } as; +}; + +DECLARE_VEC(Token, TokenVec) +DECLARE_VEC(TokenVec, TokenVecVec) + +typedef struct { + TokenVecVec region_pool; + TokenVec* root; +} Ast; + + + +// Stage 2 + + +typedef enum { + REDIR_WRITE_TRUNC, + REDIR_WRITE_APPEND, + REDIR_READ, + REDIR_FDS, +} RedirectKind; + +typedef struct { + RedirectKind kind; + union { + char* filename; + struct{int from; int to;} fds; + } as; +} Redirect; + +DECLARE_VEC(Redirect, RedirectVec) + +typedef struct PlCommand PlCommand; + +typedef enum { + ARG_PS_IN, + ARG_PS_OUT, + ARG_LIT, +} ArgKind; + +typedef struct { + ArgKind kind; + union { + char* lit_str; + void* sub_pipeline; + } as; +} Arg; + +DECLARE_VEC(Arg, ArgVec) + + +typedef enum { + CONN_PIPE, + CONN_EOL, +} Connector; + + +struct PlCommand { + ArgVec args; + RedirectVec redirects; + Connector conn; +}; + +DECLARE_VEC(PlCommand, Pipeline) +DECLARE_VEC(Pipeline, PipelineVec) + +typedef struct { + PipelineVec pl_pool; + Pipeline* root; +} PipelineTree; + + + +// 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; |
