summaryrefslogtreecommitdiff
path: root/src/lexparse.c
diff options
context:
space:
mode:
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
commitfc27548c447ed9bcd07493983431e0a6e72c7cef (patch)
treedbb25a8cc09cbacd8d0d1dcd4a3962f468f96551 /src/lexparse.c
parent2efe642849cf6d45d5413cd77d8cafaf9b32db00 (diff)
restructure dir
Diffstat (limited to 'src/lexparse.c')
-rw-r--r--src/lexparse.c229
1 files changed, 229 insertions, 0 deletions
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};
+}