#include #include #include "shorthand.h" typedef struct StringView { char *start; size_t n; } StringView; void sv_write(StringView sv) { for (size_t i = 0; i < sv.n; i++) { putchar(sv.start[i]); } } StringView read_line(void) { #define RL_BUF_SIZE 1024 static char readline_buf[RL_BUF_SIZE]; // Clear buffer. for (size_t i = 0; i < RL_BUF_SIZE; i++) { readline_buf[i] = 0; } // Barebones readline implementation. size_t n = 0; char c; while ((c = getchar()) != '\n') { if (c == '\b') { readline_buf[n] = '\0'; n--; } else if (((u8)c >= 0x20 && (u8)c <= 0x7F) && n < RL_BUF_SIZE) { readline_buf[n] = c; n++; } } return (StringView){.start = (char *)&readline_buf, .n = n}; } typedef struct Tokens { StringView *start; size_t n; } Tokens; Tokens tokenize(StringView sv) { // NOTE: Not allocating any memory for now, but we are limited by a maximum // number of tokens we can process. #define TOKENS_BUF_SIZE 1024 static StringView tokens_buf[TOKENS_BUF_SIZE]; // Clear buffer. for (size_t i = 0; i < TOKENS_BUF_SIZE; i++) { tokens_buf[i] = (StringView){0}; } size_t n = 0; size_t token_n = 0; for (size_t i = 0; i < sv.n; i++) { switch (sv.start[i]) { case ' ': case '\f': case '\n': case '\r': case '\t': case '\v': { if (token_n != 0) { // Push token. tokens_buf[n++] = (StringView){ .start = &sv.start[i - token_n], .n = token_n, }; token_n = 0; } continue; } break; case '(': { if ((i + 1 < sv.n)) { char next_c = sv.start[i + 1]; if (isspace(next_c)) { fprintf(stderr, "error: lparen delimiter followed by space\n"); return (Tokens){0}; } } if (token_n != 0) { fprintf(stderr, "error: lparen delimiter within symbol name\n"); return (Tokens){0}; } // Push paren token. tokens_buf[n++] = (StringView){ .start = &sv.start[i], .n = 1, }; } break; case ')': { if ((i + 1 < sv.n)) { char next_c = sv.start[i + 1]; if ((next_c != ')' && !isspace(next_c))) { fprintf(stderr, "error: rparen delimiter within symbol name\n"); return (Tokens){0}; } } if (token_n != 0) { // Push previous token. tokens_buf[n++] = (StringView){ .start = &sv.start[i - token_n], .n = token_n, }; token_n = 0; } // Push paren token. tokens_buf[n++] = (StringView){ .start = &sv.start[i], .n = 1, }; } break; default: { token_n++; } break; } } if (token_n != 0) { // End of line encountered. tokens_buf[n++] = (StringView){ .start = &sv.start[sv.n - token_n], .n = token_n, }; } // DEBUG: Printing tokens. printf("N_TOKENS: %ld\n", n); for (size_t i = 0; i < n; i++) { printf("TOKEN: "); sv_write(tokens_buf[i]); printf("\tN: %ld", tokens_buf[i].n); printf("\n"); } return (Tokens){.start = (StringView *)&tokens_buf, .n = n}; } void display(StringView sv) { if (sv.n != 0) { sv_write(sv); printf("\n"); } } #define REPL_PROMPT "bdl> " int main(void) { printf("BDL REPL (Press Ctrl-C to exit)\n"); while (true) { printf(REPL_PROMPT); StringView line = read_line(); tokenize(line); display(line); } return 0; }