From f0016d8946958d029706b39ecd233044d3cf93f5 Mon Sep 17 00:00:00 2001 From: Ákos Kőrösi Date: Tue, 12 May 2026 10:09:02 +0200 Subject: build lexing mostly - outline the whole of lexing (maybe) - split it to separate C file - add standin code to main --- lex.c | 218 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 218 insertions(+) create mode 100644 lex.c (limited to 'lex.c') diff --git a/lex.c b/lex.c new file mode 100644 index 0000000..e8d6425 --- /dev/null +++ b/lex.c @@ -0,0 +1,218 @@ +#include +#include +#include +#include +#include +#include + +#include "lex.h" +#include "typeutils.h" + + + +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; +} + + + +TokenVec lex_level(RawLine* line, char until, ExpansionRegionVec* reg_arena); + + +void expect_char(RawLine* line, char expected) { + if (line->str[line->pos] != expected) exit(42); // TODO: nicer handling + line->pos++; +} + + +MaybeChunk lex_expansion_chunk(RawLine* line, ExpansionRegionVec* reg_arena, QuotationMode qmode) { + line->pos++; // Consume $ + if (line->str[line->pos] == '(') { + if (line->str[line->pos] == '(') { + TokenVec inner_toks = lex_level(line, ')', reg_arena); + expect_char(line, ')'); + expect_char(line, ')'); + push_to_ExpansionRegionVec( + reg_arena, + (ExpansionRegion){.mode = ARITHM, .tokens = inner_toks} + ); + ExpansionRegion* reg_ptr = ®_arena->items[reg_arena->count]; // TODO: maybe have push() return a pointer [in the macro] + 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, ')'); + push_to_ExpansionRegionVec( + reg_arena, + (ExpansionRegion){.mode = CMD_SUB, .tokens = inner_toks} + ); + ExpansionRegion* reg_ptr = ®_arena->items[reg_arena->count]; + 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, ChunkVec* coll, ExpansionRegionVec* reg_arena) { + line->pos++; + String accum = make_String(256); + while (line->str[line->pos] != '\"') { + if (line->str[line->pos] == '\0') exit(42); + + MaybeChunk exp_chunk = {.some = false}; + + if (line->str[line->pos] == '$') { + 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) { + push_to_ChunkVec(coll, (Chunk){ + .qmode = DOUBLE_Q, + .kind = LITERAL, + .as.literal_str = inner(accum) + }); + accum = make_String(256); + } + push_to_ChunkVec(coll, exp_chunk.value); + exp_chunk.some = false; + } + } + line->pos++; + if (accum.count > 0) { + push_to_ChunkVec(coll, (Chunk){ + .qmode = DOUBLE_Q, + .kind = LITERAL, + .as.literal_str = inner(accum) + }); + } +} + + +Chunk next_word_unquoted(RawLine* line, ExpansionRegionVec* 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(42); + 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), + }; +} + + + +TokenVec lex_level(RawLine* line, char until, ExpansionRegionVec* reg_arena) { + + TokenVec tokens = make_TokenVec(256); + ChunkVec curr_chunks = make_ChunkVec(256); + String word_buf = make_String(256); + + while (line->str[line->pos] != until) { + + if (line->str[line->pos] == ' ') { + line->pos++; + continue; + } + + switch (line->str[line->pos]) { + case '\'': + push_to_ChunkVec(&curr_chunks, collect_single_quoted_section(line)); + break; + case '\"': + lex_double_quoted_section(line, &curr_chunks, reg_arena); + break; + case '$': + line->pos++; + MaybeChunk mchunk = lex_expansion_chunk(line, reg_arena, UN_Q); + if (mchunk.some) { + push_to_ChunkVec(&curr_chunks, mchunk.value); + } else { + push_to_String(&word_buf, '$'); + } + break; + default: + push_to_String(&word_buf, line->str[line->pos++]); + } + } + + line->pos++; // consume until char + if (word_buf.count > 0) { + push_to_ChunkVec( + &curr_chunks, + (Chunk){.qmode = UN_Q, .kind = LITERAL, .as.literal_str = inner(word_buf)} + ); + } + if (curr_chunks.count > 0) { + push_to_TokenVec(&tokens, (Token){.kind = TOK_WORD, .as.word = curr_chunks}); + } + return tokens; +} + + +typedef struct { + TokenVec top_tokens; + ExpansionRegionVec reg_arena; +} LexResult; + + +TokenVec lex(char* input) { + RawLine line = {.str = input, .pos = 0}; + ExpansionRegionVec reg_arena = make_ExpansionRegionVec(256); + return lex_level(&line, '\0', ®_arena); +} -- cgit v1.2.3