#include #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; 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}; }