#include #include #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]); } } bool sv_equal(StringView a, StringView b) { if (a.n == b.n) { for (size_t i = 0; i < a.n; i++) { if (a.start[i] != b.start[i]) { return false; } } return true; } return false; } 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; // TODO: Handle double quotes and escaped quotes. 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, }; } return (Tokens){.start = (StringView *)&tokens_buf, .n = n}; } typedef enum ObjectType { OBJ_TYPE_FIXNUM, OBJ_TYPE_BOOL, OBJ_TYPE_NIL, OBJ_TYPE_SYMBOL, OBJ_TYPE_STRING, OBJ_TYPE_PAIR, OBJ_TYPE_LIST, } ObjectType; typedef struct Object { ObjectType type; union { // OBJ_TYPE_FIXNUM ssize_t fixnum; // OBJ_TYPE_BOOL bool boolean; // OBJ_TYPE_STRING struct { char *string; size_t string_n; }; // OBJ_TYPE_PAIR struct { struct Object *car; struct Object *cdr; }; // OBJ_TYPE_LIST struct { // NOTE: Probably want to do this as a linked list using pairs. struct Object **list; size_t list_n; }; // OBJ_TYPE_SYMBOL struct { char *symbol; size_t symbol_n; }; }; } Object; Object * make_fixnum(ssize_t num) { Object *obj = malloc(sizeof(Object)); obj->type = OBJ_TYPE_FIXNUM; obj->fixnum = num; return obj; } Object * make_boolean(bool b) { Object *obj = malloc(sizeof(Object)); obj->type = OBJ_TYPE_BOOL; obj->boolean = b; return obj; } Object * make_string(const char *str, size_t n) { Object *obj = malloc(sizeof(Object)); obj->type = OBJ_TYPE_STRING; obj->string = malloc(sizeof(char) * n); memcpy(obj->string, str, n); obj->string_n = n; return obj; } Object * make_symbol(const char *str, size_t n) { Object *obj = malloc(sizeof(Object)); obj->type = OBJ_TYPE_SYMBOL; obj->string = malloc(sizeof(char) * n); memcpy(obj->string, str, n); obj->string_n = n; return obj; } Object * make_empty_list(void) { Object *obj = malloc(sizeof(Object)); obj->type = OBJ_TYPE_NIL; obj->list = NULL; obj->list_n = 0; return obj; } void push_obj_to_list(Object *list, Object **obj) { assert(list != NULL); assert(list->type == OBJ_TYPE_NIL || list->type == OBJ_TYPE_LIST); if (obj == NULL || *obj == NULL) { return; } if (list->type == OBJ_TYPE_NIL) { list->type = OBJ_TYPE_LIST; } list->list = realloc(list->list, (list->list_n + 1) * sizeof(struct Object *)); list->list[list->list_n++] = *obj; } bool token_is_fixnum(StringView token) { for (size_t i = 0; i < token.n; i++) { char c = token.start[i]; if (i == 0 && c == '-') { continue; } if (!isdigit(c)) { return false; } } return true; } #define TRUE_TOKEN (StringView){"true", 4} #define FALSE_TOKEN (StringView){"false", 5} Object * build_ast(Tokens tokens) { // DEBUG: Printing tokens. // printf("N_TOKENS: %ld\n", tokens.n); // for (size_t i = 0; i < tokens.n; i++) { // printf("TOKEN: "); // sv_write(tokens.start[i]); // printf("\tN: %ld", tokens.start[i].n); // printf("\n"); // } // TODO: Report error if we haven't consumed all the tokens? for (size_t i = 0; i < tokens.n; i++) { StringView token = tokens.start[i]; // OBJ_TYPE_FIXNUM if (token_is_fixnum(token)) { // Convert token to fixnum. ssize_t num = 0; int sign = 1; for (size_t i = 0; i < token.n; i++) { char c = token.start[i]; if (c == '-') { sign = -1; continue; } num = num * 10 + (c - '0'); } return make_fixnum(num * sign); } // OBJ_TYPE_BOOL if (sv_equal(token, TRUE_TOKEN)) { return make_boolean(true); } if (sv_equal(token, FALSE_TOKEN)) { return make_boolean(false); } // OBJ_TYPE_LIST if (token.start[0] == ')') { return NULL; } if (token.start[0] == '(') { if ((i + 1) < tokens.n && tokens.start[i + 1].start[0] == ')') { return make_empty_list(); } Object *list = make_empty_list(); Object *next_obj = NULL; while (((i + 1) < tokens.n) && (next_obj = build_ast((Tokens){&tokens.start[i + 1], tokens.n - i - 1})) != NULL) { push_obj_to_list(list, &next_obj); // FIXME: must consume tokens instead, otherwise recursive lists // will not work. i++; } return list; } // OBJ_TYPE_SYMBOL return make_symbol(token.start, token.n); } return NULL; } void display(Object *root) { if (root == NULL) { return; } switch (root->type) { case OBJ_TYPE_FIXNUM: { printf("%zd", root->fixnum); } break; case OBJ_TYPE_BOOL: { if (root->boolean) { printf("true"); } else { printf("false"); } } break; case OBJ_TYPE_NIL: { printf("()"); } break; case OBJ_TYPE_STRING: { printf("\"%.*s\"", (int)root->string_n, root->string); } break; case OBJ_TYPE_SYMBOL: { printf(":%.*s", (int)root->symbol_n, root->symbol); } break; case OBJ_TYPE_LIST: { printf("("); for (size_t i = 0; i < root->list_n; i++) { display(root->list[i]); if ((i + 1) < root->list_n) { printf(" "); } } printf(")"); } break; default: { printf("TYPE NOT IMPLEMENTED FOR DISPLAY."); } break; } } #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(); Object *ast = build_ast(tokenize(line)); if (ast) { display(ast); printf("\n"); } } return 0; }