#include #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; 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 MaybeChunk handle_dollar_expansion(char* line, size_t* pos, QuotationMode qmode) { (*pos)++; if (line[*pos] == '(') { // TODO exit(66); } else if (line[*pos] == '_' || isalnum(line[*pos])){ String buf = make_String(256); while (line[*pos] == '_' || isalnum(line[*pos])) { push_to_String(&buf, line[(*pos)++]); } return (MaybeChunk){ .some = true, .value = (Chunk){ .qmode = qmode, .kind = CHUNK_VAR, .as.varname = inner(&buf) } }; } else { return (MaybeChunk){.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[i] != '$' && line[i] != '\0' && line[i] == ' '; i++) { push_to_String(&buf, line[i]); } *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[i] != '\'' && line[i] != '\0'; i++) { push_to_String(&buf, line[i]); } 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[i] != '\'' && line[i] != '\0'; i++) { push_to_String(&buf, line[i]); } 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] == '$') { MaybeChunk dollar_chunk = handle_dollar_expansion(line, pos, UN_Q); if (dollar_chunk.some) { push_to_Word(&buf.word_germ, dollar_chunk.value); } else { push_to_String(&buf.lit_germ, '$'); } } 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); } 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}; }