#include "types.h" #include #include #include #include #include #define Array(t) Array_Named(t, t) #define Array_Named(t, name) \ typedef struct name { \ isize length; \ t elements[]; \ } name##_t Array_Named(i32, line_offsets); // Array_Named(i8, bytes); // Location in the source file typedef struct Location { // Offset in file of the first character u32 offset; // Line number (zero indexed) u32 line; // Offset on the line of the first character u32 column; // Length in bytes u32 size; } Location_t; typedef struct Token { // identifying tag enum TokenType { t_error, t_size, t_multiply, t_divide, t_add, t_subtract, t_space, t_tab, t_newline, t_number, } type; Location_t pos; } Token_t; Array_Named(Token_t, TokenList); typedef struct AExp { enum AExpType { e_error, e_mul, e_div, e_add, e_sub, e_number, } type; union { i32 number; struct AExp* e_unary; struct { struct AExp* a; struct AExp* b; } e_binary; } data; // Token_t* t; } AExp_t; static usize f_read(FILE* restrict stream, char** restrict dst); static usize f_size(FILE* restrict stream); static void l_print_token(Token_t token, char* restrict text); static line_offsets_t* l_read(char const* restrict text, usize text_length, TokenList_t** restrict dst); static AExp_t* p_arith(char const* restrict text, TokenList_t* restrict tokens); static AExp_t p_number(char const* restrict text, Token_t* restrict tnum); int main(int argc, char* argv[]) { if (argc == 2) { FILE* srcfile = NULL; // pointer to a buffer holding the raw bytes of the file char* contents = NULL; // length of contents usize content_length = 0; // tokens, parsed by l_read TokenList_t* tokens = NULL; // auxilliary structure to keep track of the lines, used for debugging // -- maybe turn off with a flag? line_offsets_t* lines = NULL; srcfile = fopen(argv[1], "re"); if (srcfile == NULL) { fprintf(stdout, "! Failed to open file: %s\n", argv[1]); } if (srcfile != NULL) { content_length = f_read(srcfile, &contents); if (content_length > 0) { printf("%s:%ld\n%s\n", argv[1], content_length, contents); } if (fclose(srcfile)) { fprintf(stdout, "! Failed to close file %s: %s\n", argv[1], strerror(errno)); } } if (content_length > 0) { lines = l_read(contents, strlen(contents), &tokens); } if (lines != NULL) { const isize num_tokens = tokens->length; printf("%ld tokens\n", num_tokens); for (int tokenidx = 0; tokenidx < num_tokens; tokenidx++) { l_print_token(tokens->elements[tokenidx], contents); } AExp_t* expression = p_arith(contents, tokens); printf("ast:\n"); i16 indentation = 0; for (int tokenidx = 0; tokenidx < num_tokens; tokenidx++) { switch (expression[tokenidx].type) { case e_mul: printf("%*s*\n", indentation, " "); indentation++; break; case e_div: printf("%*s/\n", indentation, " "); indentation++; break; case e_add: printf("%*s+\n", indentation, " "); indentation++; break; case e_sub: printf("%*s-\n", indentation, " "); indentation++; break; case e_number: printf("%*d\n", indentation, expression[tokenidx].data.number); indentation--; break; case e_error: default: printf("%*s\n", indentation, "error: invalid token"); break; } } } if (tokens != NULL) { free(tokens); } if (lines != NULL) { free((void*)lines); } if (contents != NULL) { free(contents); } } return 0; } /* Copies the contents of the file stream to *dst. * @param stream An open file stream. * @param dst** Destination buffer. If *dst is NULL allocates a buffer the size * of stream using malloc. * @return The number of bytes copied into dst. * @error returns -1 on error. */ static usize f_read(FILE* restrict stream, char** restrict dst) { usize file_sz = f_size(stream); if (dst == NULL) { fprintf(stderr, "! Got NULL ptr\n"); } if (dst != NULL && *dst == NULL && file_sz > 0) { *dst = (char*)malloc(file_sz * sizeof(char)); } if (dst != NULL && *dst == NULL && file_sz > 0) { fprintf(stderr, "! Failed to allocate %ld bytes: %s\n", file_sz * sizeof(char), strerror(errno)); } usize bytes_read = 0; if (dst != NULL && *dst != NULL && file_sz > 0) { bytes_read = fread(*dst, sizeof(char), file_sz, stream); } if (bytes_read < file_sz && feof(stream)) { fprintf(stderr, "! Failed to read file, read %ld out of %ld bytes\n", bytes_read, file_sz); } return bytes_read; } /* Returns the size in bytes and rewinds the stream. * @param stream An open file stream. * @return the size of the file in bytes. * @error Returns -1 on error. */ static usize f_size(FILE* restrict stream) { // Could ftell and rewind to previous position, but scratch that i64 size = -1; i32 error = fseek(stream, 0, SEEK_END); if (error) { // TODO: handle pipes (check errno == ESPIPE) fprintf(stderr, "! Failed to seek file: %s\n", strerror(errno)); } else { size = ftell(stream); error = fseek(stream, 0, SEEK_SET); if (error) { fprintf(stderr, "! Failed to rewind file: %s\n", strerror(errno)); } } if (size == -1) { fprintf(stderr, "! Failed to ftell file: %s\n", strerror(errno)); size = 0; } return (usize)size; } static void l_print_token(Token_t token, char* restrict text) { fprintf(stdout, "[%4d] %2d:%d-%d ", token.pos.offset, token.pos.line, token.pos.column, token.pos.size); switch (token.type) { case t_error: fputs(" error ", stdout); break; case t_size: fprintf(stdout, "size"); break; case t_multiply: fputs(" * ", stdout); break; case t_divide: fputs(" / ", stdout); break; case t_add: fputs(" + ", stdout); break; case t_subtract: fputs(" - ", stdout); break; case t_number: fputs("number:", stdout); fwrite(&text[token.pos.offset], token.pos.size, sizeof(char), stdout); break; case t_space: fprintf(stdout, "space:%d", token.pos.size); break; case t_tab: fprintf(stdout, "tab:%d", token.pos.size); break; case t_newline: fprintf(stdout, "\\n offset:%d line:%d column:%d length:%d", token.pos.offset, token.pos.line, token.pos.column, token.pos.size); break; default: break; } puts(""); } /* Reads text and parses it into an array of tokens * @param text A buffer holding the input text * @return a pointer to dst * @error Returns NULL on error. */ static line_offsets_t* l_read(char const* restrict text, usize text_length, TokenList_t** restrict dst) { line_offsets_t* lines = NULL; if (dst == NULL) { fprintf(stderr, "dst is NULL"); } if (text_length > 0 && dst != NULL && *dst == NULL) { // upper bound of tokens must be equal to text_length *dst = (TokenList_t*)malloc(sizeof(isize) + text_length * sizeof(Token_t)); lines = (line_offsets_t*)malloc(sizeof(isize) + text_length * sizeof(i32)); } if (text_length > 0 && dst != NULL && *dst == NULL) { fprintf(stderr, "! Failed to allocate %ld bytes for `dst`\n", sizeof(isize) + text_length * sizeof(Token_t)); } if (text_length > 0 && dst != NULL && lines == NULL) { fprintf(stderr, "! Failed to allocate %ld bytes for `lines`\n", sizeof(isize) + text_length * sizeof(i32)); } if (text_length > 0 && dst != NULL && *dst != NULL && lines != NULL) { // Token counter isize tokenno = 0; i32 error = 0; enum TokenType token_previous = t_error; enum TokenType token = t_error; Location_t cursor; cursor.line = 0; cursor.column = 1; cursor.size = 0; do { switch (text[cursor.offset]) { // case t_size: // break; case '*': token = t_multiply; break; case '/': token = t_divide; break; case t_add: token = t_add; break; case '-': token = t_subtract; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': token = t_number; break; case '+': token = t_add; break; case ' ': token = t_space; break; case '\t': token = t_tab; break; case '\n': token = t_newline; break; default: token = t_error; error = 1; break; } if (token == token_previous) { (*dst)->elements[tokenno - 1].pos.size = ++cursor.size; } // if (token == t_newline && token == token_previous) { // cursor.size = 1; // (*dst)->elements[tokenno].type = token; // (*dst)->elements[tokenno].pos = cursor; // token_previous = token; //} if (token != token_previous) { cursor.size = 1; (*dst)->elements[tokenno].type = token; (*dst)->elements[tokenno].pos = cursor; tokenno++; token_previous = token; } if (token == t_newline) { lines->elements[cursor.line] = cursor.offset; cursor.line++; cursor.column = 0; } cursor.offset++; cursor.column++; } while (!error && cursor.offset < text_length && cursor.offset < INT_MAX); (*dst)->length = tokenno; lines->length = cursor.line; // for (int line = 0; line < lines->length; line++) { // printf("%d: line:%d\n", line, lines->elements[line]); // } } return lines; } static AExp_t* p_arith(char const* restrict text, TokenList_t* restrict tokens) { i64 exp_idx = 0; const i64 num_tokens = tokens->length; AExp_t* expression = malloc(sizeof(AExp_t) * num_tokens * 2); for (int tokenidx = 0; tokenidx < num_tokens; tokenidx++) { if (tokens->elements[tokenidx].type == t_multiply) { expression[exp_idx] = (AExp_t){.type = e_mul}; exp_idx++; } else if (tokens->elements[tokenidx].type == t_add) { expression[exp_idx] = (AExp_t){.type = e_add}; exp_idx++; } else if (tokens->elements[tokenidx].type == t_number) { expression[exp_idx] = p_number(text, &tokens->elements[tokenidx]); exp_idx++; } else if (tokens->elements[tokenidx].type == t_newline) continue; else if (tokens->elements[tokenidx].type == t_space) continue; // else // break; } return expression; } static AExp_t p_number(char const* restrict text, Token_t* restrict tnum) { i32 number = 0; char* start = (char*)&text[tnum->pos.offset]; for (u32 i = 0; i < tnum->pos.size; i++) { number *= 10; number += (*start - '0'); start++; } return (AExp_t){ .type = e_number, .data.number = number, }; }