--- /dev/null
+CC=gcc
+CFLAGS=-Wall -g -DDEBUG -fsanitize=undefined
+#CFLAGS=-Wall -O3 -flto -DGLEW_STATIC -DDEBUG -fsanitize=undefined
+LIBS=
+
+SRC := src
+OBJ := obj
+BUILDDIR := build
+
+SOURCES := $(shell find $(SRC) -type f -name '*.c' -not -path '$(SRC)/$(RES)/*')
+OBJECTS := $(patsubst $(SRC)/%.c, $(OBJ)/%.o, $(SOURCES))
+
+build/a.out: $(OBJECTS)
+ @mkdir -p $(dir $@)
+ $(CC) $(CFLAGS) -I$(SRC) $(OBJECTS) $(LIBS) -o $@
+
+$(OBJ)/%.o: $(SRC)/%.c
+ @mkdir -p $(dir $@)
+ $(CC) $(CFLAGS) $(LIBS) -I$(SRC) -c $< -o $@
+
+clean:
+ rm -rf $(OBJ) $(BUILDDIR)
--- /dev/null
+#ifndef __SARABANDE_COMMON_H__
+#define __SARABANDE_COMMON_H__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdint.h>
+
+typedef uint64_t u64;
+typedef int64_t i64;
+typedef uint32_t u32;
+typedef int32_t i32;
+typedef uint16_t u16;
+typedef int16_t i16;
+typedef uint8_t u8;
+typedef int8_t i8;
+typedef uint8_t flag;
+typedef size_t usize;
+typedef ptrdiff_t isize;
+
+#include "global.h"
+
+#endif
--- /dev/null
+#ifdef DEBUG
+#define PANIC(msg) do { fprintf(stderr, "panic: " __FILE__ ":%d: " msg "\n", __LINE__); abort(); } while (0)
+#else
+#define PANIC(x) 0
+#endif
--- /dev/null
+#include "common.h"
+
+#include "parse/filereader.h"
+#include "parse/scanner.h"
+
+int main(int argc, char **argv) {
+ if (argc == 2) {
+ hFileReader fr = sbFileReader_open(argv[1]);
+
+ hScanner sc = sbScanner_create(fr);
+
+ sbLexToken t;
+
+ do {
+ t = sbScanner_next(sc);
+
+ if (t.type == T_EOF) {
+ printf("(EOF)");
+ } else if (t.type == T_SPACE) {
+ printf("( )");
+ } else if (t.type == T_NEWLINE) {
+ printf("(\\n)");
+ } else if (t.type == T_IDENTIFIER) {
+ printf("(ID %s)", t.str);
+ } else if (t.type == T_INTEGER) {
+ printf("(INT %d)", t.i);
+ } else {
+ printf(" %c ", t.type);
+ }
+ } while (t.type != T_EOF);
+
+ sbScanner_destroy(sc);
+
+ sbFileReader_close(fr);
+ } else {
+ fprintf(stderr, "please provide a file as input\n");
+ }
+}
--- /dev/null
+#include "arena.h"
+
+#include <string.h>
+
+#define ALIGN 8
+
+struct block {
+ struct block *next;
+ usize used;
+ usize capacity;
+ char data[];
+};
+
+struct sbArena {
+ struct block *first;
+ struct block *current;
+ struct block *last;
+};
+
+hArena sbArena_create(usize initial_size) {
+ hArena arena = malloc(sizeof(struct sbArena));
+
+ while (initial_size % ALIGN != 0) initial_size++;
+
+ struct block *block = malloc(initial_size);
+ block->used = 0;
+ block->next = NULL;
+ block->capacity = initial_size;
+
+ arena->first = block;
+ arena->current = block;
+ arena->last = block;
+
+ return arena;
+}
+
+void *sbArena_alloc(hArena arena, usize size) {
+ while (size % ALIGN != 0) size++;
+
+ if (size > arena->current->capacity - arena->current->used) {
+ /* not enough space in current block. need to move to next block or allocate a new one */
+ if (arena->current != arena->last) {
+ /* move to next block */
+ if (arena->current->next) {
+ arena->current = arena->current->next;
+ } else {
+ PANIC("lost track of memory block in arena somehow; should not happen");
+ }
+ } else {
+ /* need to allocate a new block */
+ usize new_capacity = arena->current->capacity;
+ if (size > new_capacity) new_capacity = size;
+ struct block *block = malloc(sizeof(struct block) + new_capacity);
+ block->capacity = new_capacity;
+ block->next = NULL;
+
+ arena->current->next = block;
+ arena->current = arena->last = block;
+ }
+ }
+
+ void *allocated_ptr = &arena->current->data[arena->current->used];
+ arena->current->used += size;
+ memset(allocated_ptr, 0, size);
+
+ return allocated_ptr;
+}
+
+void sbArena_reset(hArena arena) {
+ struct block *blk = arena->first;
+ do {
+ blk->used = 0;
+ } while (blk != arena->current);
+
+ arena->current = arena->first;
+}
+
+void sbArena_destroy(hArena arena) {
+ struct block *blk = arena->first;
+ struct block *blk_next = arena->first->next;
+ do {
+ blk_next = blk->next;
+ free(blk);
+ blk = blk_next;
+ } while (blk);
+
+ free(arena);
+}
--- /dev/null
+#include "common.h"
+
+typedef struct sbArena *hArena;
+
+hArena sbArena_create(usize initial_size);
+
+void *sbArena_alloc(hArena arena, usize size);
+
+void sbArena_reset(hArena arena);
+
+void sbArena_destroy(hArena arena);
--- /dev/null
+#include "common.h"
+
+#include "arena.h"
+#include "string.h"
--- /dev/null
+#include "string.h"
+
+usize mystrncpy(char *dst, char *src, usize limit) {
+ usize count = 0;
+ char ch = 0;
+
+ do {
+ ch = dst[count] = src[count];
+ count ++;
+ } while (ch && count < limit);
+ dst[--count] = 0;
+
+ return count;
+}
--- /dev/null
+#include "common.h"
+
+usize mystrncpy(char *dst, char *src, usize limit);
--- /dev/null
+#include "filereader.h"
+
+/* this seems pointless right now as a wrapper for FILE* but later, we can make
+ * this work to read from stdin in interactive mode as well, which will be easier
+ * if we just have these few functions separate from the actual lexer code.
+ * we may also want to have multiple files open for imports and stuff... */
+
+struct sbFileReader {
+ FILE *file;
+};
+
+hFileReader sbFileReader_open(const char *filename) {
+ hFileReader fr = malloc(sizeof(struct sbFileReader));
+ fr->file = fopen(filename, "r");
+ if (!fr->file) {
+ perror("Error opening file for reading:");
+ } else {
+ return fr;
+ }
+
+ return fr;
+}
+
+char sbFileReader_next(hFileReader r) {
+ char c = fgetc(r->file);
+ return c;
+}
+
+char sbFileReader_peek(hFileReader r) {
+ char c = fgetc(r->file);
+ ungetc(c, r->file);
+ return c;
+}
+
+void sbFileReader_close(hFileReader r) {
+ fclose(r->file);
+ free(r);
+}
--- /dev/null
+#include "common.h"
+
+typedef struct sbFileReader *hFileReader;
+
+hFileReader sbFileReader_open(const char *filename);
+
+char sbFileReader_next(hFileReader r);
+
+char sbFileReader_peek(hFileReader r);
+
+void sbFileReader_close(hFileReader r);
--- /dev/null
+#include "scanner.h"
+
+#include "filereader.h"
+#include "mem/mem.h"
+
+/* This is like, the pre lexing stage. I mean, this does a lot of the tokenizing,
+ * but it includes stuff like spaces and newlines, too. Think of this as 'normalizing'
+ * the input. The lexer in the second stage will use some additional context and state
+ * on top of the stream of tokens output from this module in order to insert some
+ * additional "ghost" tokens into the stream (invisible parentheses, semicolon insertion),
+ * and will delete stuff like spaces and newlines. But we need to preserve spaces and such
+ * after this stage so we can differentiate between things like 'a.b (1)' and 'a.b(1)',
+ * which have different semantics. */
+
+typedef struct sbScanner {
+ hFileReader file_reader;
+ hArena arena;
+ sbLexToken next_token;
+} sbScanner;
+
+hScanner sbScanner_create(hFileReader fr) {
+ hScanner sc = malloc(sizeof(sbScanner));
+ sc->arena = sbArena_create(65536);
+ sc->file_reader = fr;
+ sc->next_token = (sbLexToken) {0};
+
+ return sc;
+}
+
+sbLexToken sbScanner_peek(hScanner sc) {
+ return sc->next_token;
+}
+
+void sbScanner_destroy(hScanner sc) {
+ sbArena_destroy(sc->arena);
+ free(sc);
+}
+
+/* yeah, yeah, unicode! get off my achin' back! i'll do it later! (maybe) */
+
+flag is_digit(char c) {
+ return ('0' <= c && c <= '9');
+}
+
+flag is_alpha(char c) {
+ return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_';
+}
+
+flag is_space(char c) {
+ return c == ' ' || c == '\t' || c == '\r';
+}
+
+flag accept_char(char expected, hFileReader fr) {
+ char actual = sbFileReader_peek(fr);
+ if (expected == actual) {
+ sbFileReader_next(fr);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+char accept_digit(hFileReader fr) {
+ char actual_char = sbFileReader_peek(fr);
+ if (is_digit(actual_char)) {
+ sbFileReader_next(fr);
+ return actual_char;
+ } else {
+ return 0;
+ }
+}
+
+char accept_alpha(hFileReader fr) {
+ char actual_char = sbFileReader_peek(fr);
+ if (is_alpha(actual_char)) {
+ sbFileReader_next(fr);
+ return actual_char;
+ } else {
+ return 0;
+ }
+}
+
+sbLexToken compute_next_token(hScanner sc) {
+ char ch = sbFileReader_peek(sc->file_reader);
+
+ char token_buffer[256];
+ int token_size = 0;
+
+ sbLexToken new_token = {0};
+
+ if (ch == EOF) {
+ new_token.type = T_EOF;
+ } else if (ch == '\n') {
+ new_token.type = T_NEWLINE;
+
+ /* skip all spaces after the newline */
+ do {
+ sbFileReader_next(sc->file_reader);
+ } while (is_space(sbFileReader_peek(sc->file_reader)));
+ } else if (is_space(ch)) {
+ new_token.type = T_SPACE;
+
+ /* skip all subsequent spaces */
+ do {
+ sbFileReader_next(sc->file_reader);
+ } while (is_space(sbFileReader_peek(sc->file_reader)));
+
+ /* if we run into a newline, forget about the spaces */
+ if (sbFileReader_peek(sc->file_reader) == '\n') {
+ new_token.type = T_NEWLINE;
+
+ /* and skip all spaces after the newline */
+ do {
+ sbFileReader_next(sc->file_reader);
+ } while (is_space(sbFileReader_peek(sc->file_reader)));
+ }
+ } else if (is_alpha(ch)) {
+ new_token.type = T_IDENTIFIER;
+
+ do {
+ token_buffer[token_size] = sbFileReader_next(sc->file_reader);
+ token_size ++;
+ ch = sbFileReader_peek(sc->file_reader);
+ } while (is_alpha(ch) || is_digit(ch));
+
+ /* TODO: I am sleepy. Please check that this has not overflowed the buffer. */
+
+ token_buffer[token_size] = '\0';
+
+ char *storage = sbArena_alloc(sc->arena, token_size + 1);
+
+ mystrncpy(storage, token_buffer, token_size + 1);
+
+ new_token.str = storage;
+ } else if (is_digit(ch)) {
+ new_token.type = T_INTEGER;
+ /* TODO: yes, yes. this isn't correct. listen. i am tired. */
+
+ i64 intval = ch - '0';
+
+ while (is_digit(sbFileReader_peek(sc->file_reader))) {
+ ch = sbFileReader_next(sc->file_reader);
+ intval *= 10;
+ intval += ch - '0';
+ }
+ } else {
+ new_token.type = T_ERROR;
+
+ switch (ch) {
+ case '(':
+ case ')':
+ case '.':
+ /* TODO ok blah blah this isn't right */
+ new_token.type = ch;
+ break;
+ default:
+ fprintf(stderr, "don't know how to process character: '%c'\n", ch);
+ }
+
+ sbFileReader_next(sc->file_reader);
+ }
+
+ return new_token;
+}
+
+sbLexToken sbScanner_next(hScanner sc) {
+ if (sc->next_token.type == T_NULL) {
+ /* if we just started, queue up the first token */
+ sc->next_token = compute_next_token(sc);
+ }
+
+ sbLexToken to_return = sc->next_token;
+ sc->next_token = compute_next_token(sc);
+ return to_return;
+}
--- /dev/null
+#include "common.h"
+
+#include "filereader.h"
+
+typedef struct sbScanner *hScanner;
+
+typedef enum sbTokenType {
+ T_LPAREN = '(',
+ T_RPAREN = ')',
+ T_LBRACKET = '[',
+ T_RBRACKET = '[',
+ T_LBRACE = '{',
+ T_RBRACE = '}',
+ T_ASTERISK = '*',
+ T_SLASH = '*',
+ T_PLUS = '+',
+ T_MINUS = '-',
+ T_PERCENT = '%',
+ T_PIPE = '|',
+ T_DOT = '.',
+ T_COMMA = ',',
+ T_COLON = ':',
+ T_SEMICOLON = ';',
+ T_NEWLINE = '\n',
+ T_SPACE = ' ',
+ T_NULL = 0,
+ T_ERROR = 1,
+ T_INTEGER,
+ T_FLOAT,
+ T_STRING,
+ T_KEYWORD,
+ T_IDENTIFIER,
+ T_ARROW,
+ T_FUNCARROW,
+ T_COLONBRACE,
+ T_PAAMAYIM_NEKUDOTAYIM,
+ T_EOF = 255,
+} sbTokenType;
+
+typedef struct sbLexToken {
+ sbTokenType type;
+ union {
+ char *str;
+ float fl;
+ int i;
+ };
+} sbLexToken;
+
+hScanner sbScanner_create(hFileReader fr);
+
+sbLexToken sbScanner_next(hScanner sc);
+
+sbLexToken sbScanner_peek(hScanner sc);
+
+void sbScanner_destroy(hScanner sc);