diff --git a/arch/linux/main.c b/arch/linux/main.c new file mode 100644 index 0000000..a1815aa --- /dev/null +++ b/arch/linux/main.c @@ -0,0 +1,53 @@ +#include "../../parser.h" +#include +#include + +#define EMBED_FILE(name) \ + void emit_##name(const char *filename) \ + { \ + FILE *f = fopen(filename, "wb"); \ + if(f) { \ + fwrite(name, 1, name##_len, f); \ + fclose(f); \ + } \ + } + +static char* readFile(const char* path) { + FILE* file = fopen(path, "rb"); + if (file == NULL) { + fprintf(stderr, "Could not open file \"%s\".\n", path); + exit(74); + } + + fseek(file, 0L, SEEK_END); + size_t fileSize = ftell(file); + rewind(file); + + char* buffer = (char*)malloc(fileSize + 1); + if (buffer == NULL) { + fprintf(stderr, "Not enough memory to read \"%s\".\n", path); + exit(74); + } + size_t bytesRead = fread(buffer, sizeof(char), fileSize, file); + if (bytesRead < fileSize) { + fprintf(stderr, "Could not read file \"%s\".\n", path); + exit(74); + } + + buffer[bytesRead] = '\0'; + + fclose(file); + return buffer; +} + +int +main(int argc, char **argv) +{ + if(argc != 2) return EXIT_FAILURE; + + char* source = readFile(argv[1]); + compile(source); + free(source); + + return EXIT_SUCCESS; +} diff --git a/arch/web.c b/arch/web.c deleted file mode 100644 index eb27e20..0000000 --- a/arch/web.c +++ /dev/null @@ -1 +0,0 @@ -// wip diff --git a/arch/linux.c b/arch/web/main.c similarity index 100% rename from arch/linux.c rename to arch/web/main.c diff --git a/build b/build index 7571fb6..1e95c7d 100755 --- a/build +++ b/build @@ -2,16 +2,32 @@ set -e +if [ -z $ARCH ]; then + ARCH='linux' +fi + if [ -z $MODE ]; then MODE='debug' fi -if [ -z $CC ]; then - CC='gcc' +if [ -z $EMIT ]; then + EMIT='c' fi +case $ARCH in + "linux") + if [ -z $CC ]; then + CC='gcc' + fi + ;; + "web") + CC=emcc + ;; +esac + # setup dirs -BUILD_DIR=./out +SRC_DIR=./arch/$ARCH +BUILD_DIR=./out/$EMIT/$ARCH GEN_DIR=$BUILD_DIR/gen TOOL_SRC=tools/file2header.c TOOL_EXE=$BUILD_DIR/file2header @@ -47,20 +63,6 @@ if [ ! -f $TOOL_EXE ] || [ $TOOL_SRC -nt $TOOL_EXE ]; then $CC $TOOL_SRC -o $TOOL_EXE fi -if [ -d "arch" ]; then - for file in arch/*.c; do - name=$(basename "$file" .c) - out_header="$GEN_DIR/${name}.h" - - if [ ! -f "$out_header" ] || [ "$file" -nt "$out_header" ]; then - echo "Packing $file -> $out_header" - $TOOL_EXE "$file" > "$out_header" - fi - done -else - echo "Warning: 'arch' directory not found." -fi - # setup the build flags based on the build mode case $MODE in "debug") @@ -71,7 +73,24 @@ case $MODE in ;; esac -BUILD_CMD="$CC -o $BUILD_DIR/undar main.c -I$GEN_DIR $BUILD_FLAGS" +# build the core VM +VM_BUILD_FLAGS="$BUILD_FLAGS -std=c89 -ffreestanding -nostdlib -fno-builtin" +${CC} -c libc.c -o $BUILD_DIR/libc.o $VM_BUILD_FLAGS +${CC} -c list.c -o $BUILD_DIR/list.o $VM_BUILD_FLAGS +${CC} -c lexer.c -o $BUILD_DIR/lexer.o $VM_BUILD_FLAGS +${CC} -c parser.c -o $BUILD_DIR/parser.o $VM_BUILD_FLAGS + +#BUILD_CMD="$CC -o $BUILD_DIR/undar $SRC_DIR/main.c libc.c list.c lexer.c parser.c -I$GEN_DIR $BUILD_FLAGS" + +# Set up the final build command +case $ARCH in + "linux") + BUILD_CMD="$CC -o $BUILD_DIR/undar $SRC_DIR/main.c emit/$EMIT/emit.c $LINK_FLAGS $BUILD_DIR/libc.o $BUILD_DIR/list.o $BUILD_DIR/lexer.o $BUILD_DIR/parser.o $BUILD_FLAGS $LINK_FLAGS" + ;; + "web") + BUILD_CMD="$CC $SRC_DIR/main.c emit/$EMIT/emit.c $BUILD_DIR/libc.o $BUILD_DIR/list.o $BUILD_DIR/lexer.o $BUILD_DIR/parser.o -o $BUILD_DIR/undar.html $BUILD_FLAGS $LINK_FLAGS" + ;; +esac echo "$BUILD_CMD" ${BUILD_CMD} diff --git a/emit.h b/emit.h new file mode 100644 index 0000000..1002f6d --- /dev/null +++ b/emit.h @@ -0,0 +1,76 @@ +#ifndef UNDAR_EMIT_H +#define UNDAR_EMIT_H + +#include "libc.h" + +typedef enum expression_config_e { + PREFIX, + INFIX, + POSTFIX +} ExpressionConfig; + +typedef enum output_config_e { + BINARY, + TEXT +} OutputConfig; + +ExpressionConfig expression_config(); +OutputConfig output_config(); + +/** + * Prolog is for all of the "setup" boilerplate + */ +void prolog(); + +/** + * Prolog is for all of the "cleanup" boilerplate + */ +void epilogue(); + +/** + * Emitters + */ +void emit_add(); +void emit_sub(); +void emit_mul(); +void emit_div(); +void emit_lt(); +void emit_le(); +void emit_gt(); +void emit_ge(); +void emit_ne(); +void emit_eq(); +void emit_false(); +void emit_true(); +void emit_nil(); +void emit_void(); +void emit_int(const char *str, i32 length); +void emit_nat(const char *str, i32 length); +void emit_real(const char *str, i32 length); +void emit_str(const char *str, i32 length); +void emit_bool_type(); +void emit_byte_type(); +void emit_int_type(); +void emit_nat_type(); +void emit_real_type(); +void emit_str_type(); +void emit_u8_type(); +void emit_i8_type(); +void emit_i16_type(); +void emit_u16_type(); +void emit_i32_type(); +void emit_u32_type(); +void emit_f32_type(); +void emit_array(); +void emit_function(); +void emit_plex(); +void emit_method(); +void emit_trait(); +void emit_const(); +void emit_print(); +void emit_neg(); +void emit_not(); +void emit_open_paren(); +void emit_close_paren(); + +#endif \ No newline at end of file diff --git a/emit/c/emit.c b/emit/c/emit.c new file mode 100644 index 0000000..31ad2c4 --- /dev/null +++ b/emit/c/emit.c @@ -0,0 +1,258 @@ +#include "../../emit.h" +#include + +ExpressionConfig +expression_config() +{ + return INFIX; +} + +OutputConfig +output_config() +{ + return TEXT; +} + +void +prolog() +{ + +} + +void +epilogue() +{ + +} + +void +emit_add() +{ + printf("+ "); +} + +void +emit_sub() +{ + printf("- "); +} + +void +emit_mul() +{ + printf("* "); +} + +void +emit_div() +{ + printf("/ "); +} + +void +emit_lt() +{ + printf("< "); +} + +void +emit_le() +{ + printf("<= "); +} + +void +emit_gt() +{ + printf("> "); +} + +void +emit_ge() +{ + printf(">= "); +} + +void +emit_ne() +{ + printf("!= "); +} + +void +emit_eq() +{ + printf("== "); +} + +void +emit_false() +{ + printf("false "); +} + +void +emit_true() +{ + printf("true "); +} + +void +emit_nil() +{ + printf("NULL "); +} + +void +emit_neg() +{ + printf("-"); +} + +void +emit_not() +{ + printf("!"); +} + +void +emit_void() +{ +} + +void +emit_bool_type() +{ +} + +void +emit_byte_type() +{ +} + +void +emit_int_type() +{ +} + +void +emit_nat_type() +{ +} + +void +emit_real_type() +{ +} + +void +emit_str_type() +{ +} + +void +emit_u8_type() +{ +} + +void +emit_i8_type() +{ +} + +void +emit_i16_type() +{ +} + +void +emit_u16_type() +{ +} + +void +emit_i32_type() +{ +} + +void +emit_u32_type() +{ +} + +void +emit_f32_type() +{ +} + +void +emit_int(const char *str, i32 length) +{ + printf("%.*s ", length, str); +} + +void +emit_nat(const char *str, i32 length) +{ + printf("%.*s ", length, str); +} + +void +emit_real(const char *str, i32 length) +{ + printf("%.*s ", length, str); +} + +void +emit_str(const char *str, i32 length) +{ + printf("%.*s ", length, str); +} + +void +emit_array() +{ +} + +void +emit_function() +{ +} + +void +emit_plex() +{ +} + +void +emit_method() +{ +} + +void +emit_trait() +{ +} + +void +emit_const() +{ +} + +void +emit_print() +{ + printf("printf(\"%%s\", "); +} + +void +emit_open_paren() +{ + printf("("); +} + +void +emit_close_paren() +{ + printf(")"); +} diff --git a/emit/emit_c.c b/emit/emit_c.c deleted file mode 100644 index e69de29..0000000 diff --git a/emit/uxntal/emit.c b/emit/uxntal/emit.c new file mode 100644 index 0000000..70054e4 --- /dev/null +++ b/emit/uxntal/emit.c @@ -0,0 +1,257 @@ +#include "../../emit.h" +#include + +ExpressionConfig +expression_config() +{ + return POSTFIX; +} + +OutputConfig +output_config() +{ + return TEXT; +} + +void +prolog() +{ + +} + +void +epilogue() +{ + +} + +void +emit_add() +{ + printf("+ "); +} + +void +emit_sub() +{ + printf("- "); +} + +void +emit_mul() +{ + printf("* "); +} + +void +emit_div() +{ + printf("/ "); +} + +void +emit_lt() +{ + printf("< "); +} + +void +emit_le() +{ + printf("<= "); +} + +void +emit_gt() +{ + printf("> "); +} + +void +emit_ge() +{ + printf(">= "); +} + +void +emit_ne() +{ + printf("!= "); +} + +void +emit_eq() +{ + printf("== "); +} + +void +emit_false() +{ + printf("false "); +} + +void +emit_true() +{ + printf("true "); +} + +void +emit_nil() +{ + printf("NULL "); +} + +void +emit_neg() +{ + printf("-"); +} + +void +emit_not() +{ + printf("!"); +} + +void +emit_void() +{ +} + +void +emit_bool_type() +{ +} + +void +emit_byte_type() +{ +} + +void +emit_int_type() +{ +} + +void +emit_nat_type() +{ +} + +void +emit_real_type() +{ +} + +void +emit_str_type() +{ +} + +void +emit_u8_type() +{ +} + +void +emit_i8_type() +{ +} + +void +emit_i16_type() +{ +} + +void +emit_u16_type() +{ +} + +void +emit_i32_type() +{ +} + +void +emit_u32_type() +{ +} + +void +emit_f32_type() +{ +} + +void +emit_int(const char *str, i32 length) +{ + printf("%.*s ", length, str); +} + +void +emit_nat(const char *str, i32 length) +{ + printf("%.*s ", length, str); +} + +void +emit_real(const char *str, i32 length) +{ + printf("%.*s ", length, str); +} + +void +emit_str(const char *str, i32 length) +{ + printf("%.*s ", length, str); +} + +void +emit_array() +{ +} + +void +emit_function() +{ +} + +void +emit_plex() +{ +} + +void +emit_method() +{ +} + +void +emit_trait() +{ +} + +void +emit_const() +{ +} + +void +emit_print() +{ + printf("printf(\"%%s\", "); +} + +void +emit_open_paren() +{ + printf("("); +} +void +emit_close_paren() +{ + printf(")"); +} diff --git a/lexer.c b/lexer.c index 3e4914f..41c00e2 100644 --- a/lexer.c +++ b/lexer.c @@ -1,5 +1,3 @@ -#include - #include "lexer.h" typedef struct lexer_s Lexer; @@ -83,7 +81,7 @@ error_token(const char *message) Token token; token.type = TOKEN_ERROR; token.start = message; - token.length = (i32)strlen(message); + token.length = (i32)slen(message); token.line = lexer.line; return token; } @@ -105,11 +103,11 @@ skip_whitespace() break; case '/': if(peek_next() == '/') { - // Single-line comment: skip until newline or end of file + /* Single-line comment: skip until newline or end of file */ advance(); while(peek() != '\n' && !is_at_end()) advance(); } else if(peek_next() == '*') { - // Multi-line comment: skip until '*/' or end of file + /* Multi-line comment: skip until '* /' or end of file */ advance(); advance(); while(!is_at_end()) { @@ -117,12 +115,12 @@ skip_whitespace() if(peek() == '*' && peek_next() == '/') { advance(); advance(); - break; // Exit loop, comment ended + break; /* Exit loop, comment ended */ } advance(); } } else { - return; // Not a comment, let tokenization handle it + return; /* Not a comment, let tokenization handle it */ } break; default: @@ -135,7 +133,7 @@ static TokenType check_keyword(i32 start, i32 length, const char *rest, TokenType type) { if(lexer.current - lexer.start == start + length && - memcmp(lexer.start + start, rest, length) == 0) { + sleq(lexer.start + start, rest, length) == 0) { return type; } @@ -232,9 +230,10 @@ identifierType() switch(lexer.start[1]) { case 't': return check_keyword(2, 1, "r", TOKEN_TYPE_PTR); - case 'l': return check_keyword(2, 2, "ex", TOKEN_KEYWORD_PLEX); + case 'r': + return check_keyword(2, 3, "int", TOKEN_KEYWORD_PRINT); } } break; @@ -376,14 +375,16 @@ string() Token next_token() { + char c; + char next; skip_whitespace(); lexer.start = lexer.current; if(is_at_end()) return make_token(TOKEN_EOF); - char c = advance(); + c = advance(); if(is_alpha(c)) return identifier(); - char next = peek(); + next = peek(); if((c == '-' && is_digit(next)) || is_digit(c)) return number(); switch(c) { diff --git a/lexer.h b/lexer.h index b32f5dd..a3953c3 100644 --- a/lexer.h +++ b/lexer.h @@ -20,6 +20,7 @@ typedef enum { TOKEN_TYPE_REAL, TOKEN_TYPE_STR, TOKEN_TYPE_BOOL, + TOKEN_TYPE_BYTE, TOKEN_TYPE_VOID, TOKEN_TYPE_PTR, TOKEN_KEYWORD_PLEX, @@ -46,6 +47,7 @@ typedef enum { TOKEN_KEYWORD_NIL, TOKEN_KEYWORD_TRUE, TOKEN_KEYWORD_FALSE, + TOKEN_KEYWORD_PRINT, TOKEN_OPERATOR_NOT, TOKEN_OPERATOR_AND, TOKEN_OPERATOR_OR, diff --git a/libc.c b/libc.c index 169fb21..9108e65 100644 --- a/libc.c +++ b/libc.c @@ -76,7 +76,7 @@ snlen(const char *str, u32 max_len) } void * -aaloc(Arena *arena, u32 size) +aalloc(Arena *arena, u32 size) { u32 pos; if(arena == nil) return nil; diff --git a/list.c b/list.c index 5a28ded..68103ae 100644 --- a/list.c +++ b/list.c @@ -22,6 +22,7 @@ node_value(Node *n) void * list_push(Arena *arena, List *list, void *data, u32 data_size) { + void *dest; void *ptr = aalloc(arena, sizeof(Node) + data_size); Node *node = (Node *)ptr; @@ -38,7 +39,7 @@ list_push(Arena *arena, List *list, void *data, u32 data_size) list->count++; - void *dest = node_value(node); + dest = node_value(node); if (data && data_size > 0) { mcpy(dest, data, data_size); } @@ -49,9 +50,10 @@ list_push(Arena *arena, List *list, void *data, u32 data_size) void list_foreach(List *list, list_iter_fn func) { + Node *curr; if (!list || !func) return; - Node *curr = list->head; + curr = list->head; while (curr) { func(node_value(curr)); curr = curr->next; @@ -61,11 +63,12 @@ list_foreach(List *list, list_iter_fn func) void * list_get(List *list, u32 index) { + u32 i; + Node *curr; if (!list || index >= list->count) return nil; - Node *curr = list->head; - // Walk to the desired index - for (u32 i = 0; i < index; i++) { + curr = list->head; + for (i = 0; i < index; i++) { curr = curr->next; } @@ -73,7 +76,7 @@ list_get(List *list, u32 index) } void -list_set(List *list, u32 index, const void *data, u32 size) +list_set(List *list, u32 index, void *data, u32 size) { void *target = list_get(list, index); if (target) { @@ -82,13 +85,16 @@ list_set(List *list, u32 index, const void *data, u32 size) } void * -list_find(List *list, compare_fn compare, const void *target) +list_find(List *list, compare_fn compare, void *target) { + Node *curr; + void *data; + if (!list || !compare) return nil; - Node *curr = list->head; + curr = list->head; while (curr) { - void *data = node_value(curr); + data = node_value(curr); if (compare(data, target)) { return data; } diff --git a/list.h b/list.h index 7249228..8d7df04 100644 --- a/list.h +++ b/list.h @@ -15,15 +15,15 @@ struct list_s { u32 count; }; -typedef bool (*compare_fn)(const void *data, const void *target); +typedef bool (*compare_fn)(void *data, void *target); typedef void (*list_iter_fn)(void *data); List *new_list(Arena *arena); void *node_value(Node *n); void *list_push(Arena *arena, List *list, void *data, u32 data_size); void list_foreach(List *list, list_iter_fn func); -void *list_find(List *list, compare_fn compare, const void *target); +void *list_find(List *list, compare_fn compare, void *target); void *list_get(List *list, u32 index); -void list_set(List *list, u32 index, const void *data, u32 size); +void list_set(List *list, u32 index, void *data, u32 size); #endif \ No newline at end of file diff --git a/main.c b/main.c deleted file mode 100644 index 6216242..0000000 --- a/main.c +++ /dev/null @@ -1,26 +0,0 @@ -#include -#include - -#define EMBED_FILE(name) \ - void emit_##name(const char *filename) \ - { \ - FILE *f = fopen(filename, "wb"); \ - if(f) { \ - fwrite(name, 1, name##_len, f); \ - fclose(f); \ - } \ - } - -int -main(int argc, char **argv) -{ - char *name; - - if(argc > 1) - name = argv[1]; - else - name = "'u'"; - - printf("nuqneH %s?\n", name); - return EXIT_SUCCESS; -} diff --git a/parser.c b/parser.c index 59c0c41..814d590 100644 --- a/parser.c +++ b/parser.c @@ -1,7 +1,72 @@ #include "parser.h" +#include "emit.h" Parser parser; +/**************************************************** + * Scope + ***************************************************/ + +void +scope_push(Arena *arena) +{ + Scope *child = aalloc(arena, sizeof(Scope)); + child->symbols = new_list(arena); + child->parent = parser.current_scope; + + parser.current_scope = child; +} + +void +scope_pop() +{ + Scope *prev = parser.current_scope->parent; + parser.current_scope = prev; +} + +Symbol * +scope_get_symbol(Scope *scope, const char *name, u32 name_length) +{ + u32 count, i; + if(!scope) return nil; + + count = scope->symbols->count; + for(i = 0; i < count; i++) { + Symbol *sym = list_get(scope->symbols, i); + if(sleq(sym->name, name, name_length)) return sym; + } + + return scope_get_symbol(scope->parent, name, name_length); +} + +Symbol * +scope_add_symbol(Arena *arena, const char *name, u32 name_length, + SymbolType type, u32 size) +{ + Symbol *sym = scope_get_symbol(parser.current_scope, name, name_length); + if(sym != nil) return sym; + + sym = aalloc(arena, sizeof(Symbol)); + scpy(sym->name, name, slen(name)); + sym->name_length = slen(name); + sym->type = type; + sym->secondary_type = VOID; + sym->size = size; + sym->ref = 0; + if(type == PLEX || type == METHOD || type == TRAIT) { + sym->args = new_list(arena); + sym->fields = new_list(arena); + } + + list_push(arena, parser.current_scope->symbols, sym, sizeof(Symbol)); + + return sym; +} + +/**************************************************** + * Parser + ***************************************************/ + bool advance() { @@ -14,138 +79,312 @@ advance() return false; } } - -/**************************************************** - * Scope - ***************************************************/ - -void -scope_push(Arena *arena) +static bool +check(TokenType type) { - Scope *child = aalloc(arena, sizeof(Scope)); - child->symbols = new_list(arena); - child->parent = parser.current_scope; - - parser.current_scope = child; + return parser.current.type == type; } -void -scope_pop(Arena *arena) +bool +consume(TokenType type) { - Scope *prev = parser.current_scope->parent; - parser.current_scope = prev; -} - -Symbol * -scope_get_symbol(Scope *scope, const char *name, u32 name_length) -{ - if (!scope) return nil; - - u32 count = scope->symbols->count; - for (u32 i = 0; i < count; i++) { - Symbol *sym = list_get(scope->symbols, i); - if (sleq(sym->name, name, name_length)) { - return sym; - } - } - - return scope_get_symbol(scope->parent, name, name_length); -} - -Symbol * -scope_add_symbol(Arena *arena, const char *name, u32 name_length, SymbolType type, u32 size) -{ - Symbol *sym = scope_get_symbol(parser.current_scope, name, name_length); - if (sym != nil) return sym; - - sym = aalloc(arena, sizeof(Symbol)); - scpy(sym->name, name, slen(name)); - sym->name_length = slen(name); - sym->type = type; - sym->secondary_type = VOID; // Default - sym->size = size; - sym->ref = 0; - if (type == PLEX || type == METHOD || type == TRAIT) { - sym->args = new_list(arena); - sym->fields = new_list(arena); + if(check(type)) { + advance(); + return true; } - list_push(arena, parser.current_scope->symbols, sym, sizeof(Symbol)); - - return sym; + return false; } -/**************************************************** - * Parser - ***************************************************/ -bool -parse_trait() +static bool +match(TokenType type) { + if(!check(type)) return false; + advance(); return true; } -bool -parse_plex() +static void expression(); +static void statement(); +static void declaration(); +static ParseRule *get_rule(TokenType type); +static void parse_precedence(Precedence precedence); + +static void +binary() { - return true; + TokenType operatorType = parser.previous.type; + ParseRule *rule = get_rule(operatorType); + + switch(operatorType) { + case TOKEN_BANG_EQ: + emit_ne(); + break; + case TOKEN_EQ_EQ: + emit_eq(); + break; + case TOKEN_GT: + emit_ge(); + break; + case TOKEN_GTE: + emit_ge(); + break; + case TOKEN_LT: + emit_lt(); + break; + case TOKEN_LTE: + emit_le(); + break; + case TOKEN_PLUS: + emit_add(); + break; + case TOKEN_MINUS: + emit_sub(); + break; + case TOKEN_STAR: + emit_mul(); + break; + case TOKEN_SLASH: + emit_div(); + break; + default: + return; + } + + /* Move to above switch statement for postfix */ + parse_precedence((Precedence)(rule->precedence + 1)); } -bool -parse_function() +static void +literal() { - return true; + switch(parser.previous.type) { + case TOKEN_KEYWORD_FALSE: + emit_false(); + break; + case TOKEN_KEYWORD_TRUE: + emit_true(); + break; + case TOKEN_KEYWORD_NIL: + emit_nil(); + break; + default: + return; + } } -bool -parse_if() +static void +expression() { - return true; + parse_precedence(PREC_ASSIGNMENT); } -bool -parse_loop() +static void +declaration() { - return true; + if(match(TOKEN_TYPE_INT)) { + emit_int_type(); + } else if(match(TOKEN_TYPE_NAT)) { + emit_nat_type(); + } else if(match(TOKEN_TYPE_REAL)) { + emit_real_type(); + } else if(match(TOKEN_TYPE_STR)) { + emit_str_type(); + } else if(match(TOKEN_TYPE_BOOL)) { + emit_bool_type(); + } else if(match(TOKEN_TYPE_BYTE)) { + emit_byte_type(); + } else if(match(TOKEN_TYPE_U8)) { + emit_u8_type(); + } else if(match(TOKEN_TYPE_I8)) { + emit_i8_type(); + } else if(match(TOKEN_TYPE_U16)) { + emit_u16_type(); + } else if(match(TOKEN_TYPE_I16)) { + emit_i16_type(); + } else if(match(TOKEN_IDENTIFIER)) { + /** + * maybe a Plex? + * lookup plex defs + * if not then + */ + } + + statement(); } -bool -parse_while() +static void +statement() { - return true; + if(match(TOKEN_KEYWORD_PRINT)) + emit_print(); + else + expression(); } -bool -parse_for() +static void +grouping() { - return true; + emit_open_paren(); + + expression(); + + emit_close_paren(); + consume(TOKEN_RPAREN); } -bool -parse_return() +static void +number() { - return true; + emit_int(parser.previous.start, parser.previous.length); } -bool -parse_assignment() +static void +string() { - return true; + emit_str(parser.previous.start, parser.previous.length); } -bool -parse_expression() +static void +unary() { - return true; + TokenType operatorType = parser.previous.type; + + switch(operatorType) { + case TOKEN_MINUS: + emit_neg(); + break; + case TOKEN_BANG: + emit_not(); + break; + default: + return; + } + + /* Move to above switch statement for postfix */ + parse_precedence(PREC_UNARY); } -bool -parse_statement() +ParseRule rules[] = { + /* 0 */ {NULL, NULL, PREC_NONE}, + /* 1 */ {NULL, NULL, PREC_NONE}, + /* 2 */ {NULL, NULL, PREC_NONE}, + /* 3 */ {number, NULL, PREC_NONE}, /* TOKEN_LITERAL_INT */ + /* 4 */ {number, NULL, PREC_NONE}, /* TOKEN_LITERAL_NAT */ + /* 5 */ {number, NULL, PREC_NONE}, /* TOKEN_LITERAL_REAL */ + /* 6 */ {string, NULL, PREC_NONE}, /* TOKEN_LITERAL_STR */ + /* 7 */ {NULL, NULL, PREC_NONE}, /* TOKEN_TYPE_I8 */ + /* 8 */ {NULL, NULL, PREC_NONE}, /* TOKEN_TYPE_I16 */ + /* 9 */ {NULL, NULL, PREC_NONE}, /* TOKEN_TYPE_INT */ + /*10 */ {NULL, NULL, PREC_NONE}, /* TOKEN_TYPE_U8 */ + /*11 */ {NULL, NULL, PREC_NONE}, /* TOKEN_TYPE_U16 */ + /*12 */ {NULL, NULL, PREC_NONE}, /* TOKEN_TYPE_NAT */ + /*13 */ {NULL, NULL, PREC_NONE}, /* TOKEN_TYPE_REAL */ + /*14 */ {NULL, NULL, PREC_NONE}, /* TOKEN_TYPE_STR */ + /*15 */ {NULL, NULL, PREC_NONE}, /* TOKEN_TYPE_BOOL */ + /*16 */ {NULL, NULL, PREC_NONE}, /* TOKEN_TYPE_BYTE */ + /*17 */ {NULL, NULL, PREC_NONE}, /* TOKEN_TYPE_VOID */ + /*18 */ {NULL, NULL, PREC_NONE}, /* TOKEN_TYPE_PTR */ + /*19 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_PLEX */ + /*20 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_FN */ + /*21 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_CONST */ + /*22 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_IF */ + /*23 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_IS */ + /*24 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_AS */ + /*25 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_ELSE */ + /*26 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_WHILE */ + /*27 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_FOR */ + /*28 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_RETURN */ + /*29 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_USE */ + /*30 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_INIT */ + /*31 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_THIS */ + /*32 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_GLOBAL */ + /*33 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_OPEN */ + /*34 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_READ */ + /*35 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_WRITE */ + /*36 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_STAT */ + /*37 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_CLOSE */ + /*38 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_LOOP */ + /*39 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_DO */ + /*40 */ {literal, NULL, PREC_NONE}, /* TOKEN_KEYWORD_NIL */ + /*41 */ {literal, NULL, PREC_NONE}, /* TOKEN_KEYWORD_TRUE */ + /*42 */ {literal, NULL, PREC_NONE}, /* TOKEN_KEYWORD_FALSE */ + /*43 */ {NULL, NULL, PREC_NONE}, /* TOKEN_KEYWORD_PRINT */ + /*44 */ {NULL, NULL, PREC_NONE}, /* TOKEN_OPERATOR_NOT */ + /*45 */ {NULL, NULL, PREC_NONE}, /* TOKEN_OPERATOR_AND */ + /*46 */ {NULL, NULL, PREC_NONE}, /* TOKEN_OPERATOR_OR */ + /*47 */ {unary, NULL, PREC_NONE}, /* TOKEN_BANG */ + /*48 */ {NULL, binary, PREC_EQUALITY}, /* TOKEN_BANG_EQ */ + /*49 */ {NULL, NULL, PREC_NONE}, /* TOKEN_EQ */ + /*50 */ {NULL, binary, PREC_EQUALITY}, /* TOKEN_EQ_EQ */ + /*51 */ {NULL, NULL, PREC_NONE}, /* TOKEN_AND */ + /*52 */ {NULL, NULL, PREC_NONE}, /* TOKEN_AND_AND */ + /*53 */ {NULL, NULL, PREC_NONE}, /* TOKEN_PIPE */ + /*54 */ {NULL, NULL, PREC_NONE}, /* TOKEN_PIPE_PIPE */ + /*55 */ {NULL, NULL, PREC_NONE}, /* TOKEN_QUESTION */ + /*56 */ {NULL, NULL, PREC_NONE}, /* TOKEN_QUESTION_DOT */ + /*57 */ {NULL, binary, PREC_TERM}, /* TOKEN_PLUS */ + /*58 */ {unary, binary, PREC_TERM}, /* TOKEN_MINUS */ + /*59 */ {NULL, binary, PREC_FACTOR}, /* TOKEN_STAR */ + /*60 */ {NULL, binary, PREC_FACTOR}, /* TOKEN_SLASH */ + /*61 */ {NULL, NULL, PREC_NONE}, /* TOKEN_MESH */ + /*62 */ {NULL, NULL, PREC_NONE}, /* TOKEN_BIG_MONEY */ + /*63 */ {NULL, binary, PREC_COMPARISON}, /* TOKEN_GT */ + /*64 */ {NULL, binary, PREC_COMPARISON}, /* TOKEN_LT */ + /*65 */ {NULL, binary, PREC_COMPARISON}, /* TOKEN_GTE */ + /*66 */ {NULL, binary, PREC_COMPARISON}, /* TOKEN_LTE */ + /*67 */ {NULL, NULL, PREC_NONE}, /* TOKEN_DOT */ + /*68 */ {NULL, NULL, PREC_NONE}, /* TOKEN_COMMA */ + /*69 */ {NULL, NULL, PREC_NONE}, /* TOKEN_COLON */ + /*70 */ {NULL, NULL, PREC_NONE}, /* TOKEN_CARET */ + /*71 */ {NULL, NULL, PREC_NONE}, /* TOKEN_SEMICOLON */ + /*72 */ {grouping, NULL, PREC_NONE}, /* TOKEN_LPAREN */ + /*73 */ {NULL, NULL, PREC_NONE}, /* TOKEN_RPAREN */ + /*74 */ {NULL, NULL, PREC_NONE}, /* TOKEN_LBRACE */ + /*75 */ {NULL, NULL, PREC_NONE}, /* TOKEN_RBRACE */ + /*76 */ {NULL, NULL, PREC_NONE}, /* TOKEN_LBRACKET */ + /*77 */ {NULL, NULL, PREC_NONE}, /* TOKEN_RBRACKET */ + /*78 */ {NULL, NULL, PREC_NONE} /* TOKEN_ARROW_RIGHT */ +}; + +static void +parse_precedence(Precedence precedence) { - return true; + ParseFn prefixRule; + ParseFn infixRule; + + advance(); + prefixRule = get_rule(parser.previous.type)->prefix; + if(prefixRule == NULL) { + return; + } + + prefixRule(); + + while(precedence <= get_rule(parser.current.type)->precedence) { + advance(); + infixRule = get_rule(parser.previous.type)->infix; + infixRule(); + } +} + +static ParseRule * +get_rule(TokenType type) +{ + return &rules[type]; } bool compile(char *source) { + init_lexer(source); + advance(); + + prolog(); + + while(!match(TOKEN_EOF)) declaration(); + + epilogue(); + return true; } diff --git a/parser.h b/parser.h index c7b17d5..52435b8 100644 --- a/parser.h +++ b/parser.h @@ -4,6 +4,7 @@ #include "libc.h" #include "list.h" #include "lexer.h" +#include "emit.h" typedef enum symbol_type_e { VOID, @@ -25,12 +26,13 @@ typedef enum symbol_type_e { PLEX, METHOD, TRAIT, - CONST, + CONST } SymbolType; typedef struct symbol_s Symbol; typedef struct scope_s Scope; typedef struct parser_s Parser; +typedef struct parse_rule_s ParseRule; #define MAX_SYMBOL_NAME_LENGTH 64 struct symbol_s { @@ -49,6 +51,28 @@ struct scope_s { List *symbols; /* list of symbols that live in this scope */ }; +typedef enum { + PREC_NONE, + PREC_ASSIGNMENT, /* = */ + PREC_OR, /* or */ + PREC_AND, /* and */ + PREC_EQUALITY, /* == != */ + PREC_COMPARISON, /* < > <= >= */ + PREC_TERM, /* + - */ + PREC_FACTOR, /* * / */ + PREC_UNARY, /* ! - */ + PREC_CALL, /* . () */ + PREC_PRIMARY +} Precedence; + +typedef void (*ParseFn)(); + +struct parse_rule_s { + ParseFn prefix; + ParseFn infix; + Precedence precedence; +}; + struct parser_s { Scope *current_scope; Token current; @@ -57,4 +81,7 @@ struct parser_s { bool compile(char *source); +extern void emit_return(); +extern void emit_constant(); + #endif diff --git a/test/add.ul b/test/add.ul new file mode 100755 index 0000000..228444f --- /dev/null +++ b/test/add.ul @@ -0,0 +1,8 @@ +/** + * Add two numbers together + */ +function add(int a, int b) int { + return a + b; +} + +print(add(1, 1) as str); diff --git a/test/bang.ul b/test/bang.ul new file mode 100755 index 0000000..0016e07 --- /dev/null +++ b/test/bang.ul @@ -0,0 +1,5 @@ +bool flag = false; + +if (!flag) { + print("flag is false"); +} diff --git a/test/expression.ul b/test/expression.ul new file mode 100644 index 0000000..c042509 --- /dev/null +++ b/test/expression.ul @@ -0,0 +1 @@ +!(5 - 4 > 3 * 2 == false) \ No newline at end of file diff --git a/test/fib.ul b/test/fib.ul new file mode 100755 index 0000000..a6cb166 --- /dev/null +++ b/test/fib.ul @@ -0,0 +1,9 @@ +/** + * Recursively calculate fibonacci + */ +function fib(int n) int { + if (n < 2) { return n; } + return fib(n - 2) + fib(n - 1); +} + +print(fib(35) as str); \ No newline at end of file diff --git a/test/for.ul b/test/for.ul new file mode 100755 index 0000000..61496b9 --- /dev/null +++ b/test/for.ul @@ -0,0 +1,9 @@ +nat i = 0; + +for (nat x in 10) { + print(x as str); +} + +for (nat j = 0; j < 10; j = j + 1) { + print(j as str); +} \ No newline at end of file diff --git a/test/hello.ul b/test/hello.ul new file mode 100755 index 0000000..dd150bd --- /dev/null +++ b/test/hello.ul @@ -0,0 +1 @@ +print("nuqneH 'u'?"); diff --git a/test/if.ul b/test/if.ul new file mode 100755 index 0000000..f007804 --- /dev/null +++ b/test/if.ul @@ -0,0 +1,9 @@ +nat x = 10; + +if (x == 10) { + print("x is 10"); +} else if (x == 20) { + print("x is 20"); +} else { + print("x is something else"); +} diff --git a/test/malloc.ul b/test/malloc.ul new file mode 100755 index 0000000..1532618 --- /dev/null +++ b/test/malloc.ul @@ -0,0 +1,9 @@ +/** + * Main function + */ +function main() { + print("Enter a string: "); + str msg = read(32); + print(msg); + return 0; +} diff --git a/test/paint.ul b/test/paint.ul new file mode 100755 index 0000000..51dc67b --- /dev/null +++ b/test/paint.ul @@ -0,0 +1,109 @@ +/** + * Constants + */ +const byte BLACK = 0; +const byte WHITE = 255; +const byte DARK_GRAY = 73; +const byte GRAY = 146; +const byte LIGHT_GRAY = 182; + +byte selected_color = 255; + +trait Device { + nat handle; +} + +plex Screen implements Device { + nat handle; + nat width; + nat height; + byte[] buffer; + + draw() { + write(this, this.buffer, this.buffer.length); + } +} + +plex Mouse implements Device { + nat handle; + nat x; + nat y; + bool left; + bool right; + bool middle; + bool btn4; +} + +/** + * Main function + */ +function main() { + Screen screen = open("/dev/screen/0", 0); + Mouse mouse = open("/dev/mouse/0", 0); + + outline_swatch(screen, BLACK, 1, 1); + outline_swatch(screen, WHITE, 21, 1); + screen.draw(); + + loop { + mouse.refresh(); + if (!mouse.left) continue; + + int box_size = 20; + int x = 1; + int y = 1; + byte color = BLACK; + outlined_swatch(screen, color, x, y); + set_color(box_size, x, y, mouse.x, mouse.y, color); + + color = WHITE; + x = 21; + outlined_swatch(screen, color, x, y); + set_color(box_size, x, y, mouse.x, mouse.y, color); + screen.draw(); + + rectangle(screen, selected_color, x, y, 5, 5); + } + exit(0); +} + +/** + * Checks if the click is within the bound and update the selected color if so. + */ +function set_color(int box_size, int bx, int by, int mx, int my, byte color) { + int right = bx + box_size; + int bottom = by + box_size; + + if (mx < bx) return; + if (mx > right) return; + if (my < by) return; + if (my > bottom) return; + + selected_color = color; +} + +/** + * Draw a color box with a grey outline, if selected use a darker color + */ +function outline_swatch(Device screen, byte color, int x, int y) { + byte bg_color = GRAY; + if (selected_color == color) { + bg_color = DARK_GRAY; + } + + rectangle(screen, bg_color, x, y, 20, 20); + rectangle(screen, color, x + 2, y + 2, 17, 17); +} + +/** + * Draw a rectangle + */ +function rectangle(Device screen, byte color, int x, int y, int width, int height) { + int base = y * screen.width + x + screen.buffer.ptr + 4; + + for (int i = height; i > 0; i--) { + int row = base + width; + memset(screen.buffer, row, color, width); + base += screen.width; + } +} diff --git a/test/plex.c b/test/plex.c new file mode 100644 index 0000000..193c3b1 --- /dev/null +++ b/test/plex.c @@ -0,0 +1,152 @@ + +#include +#include + +typedef struct arena_s Arena; +struct arena_s { + unsigned char *tape; + unsigned int count; + unsigned int capacity; +}; + +void *aalloc(Arena *arena, unsigned int size){ + unsigned int pos; + if(arena == NULL) return NULL; + if(arena->count + size > arena->capacity) return NULL; + + pos = arena->count; + arena->count += size; + return (void *)&arena->tape[pos]; +} + +unsigned int +scpy(char *to, const char *from, unsigned int length) +{ + unsigned int i; + if(to == NULL || from == NULL) return -1; + if(length == 0) return 0; + for(i = 0; i < length - 1 && from[i] != '\0'; i++) to[i] = from[i]; + to[i] = '\0'; + return 0; +} + +char * +ascpy(Arena *arena, const char *start, unsigned int length) +{ + char *str; + if(!start) return NULL; + str = (char *)aalloc(arena, length + 1); + if(!str) return NULL; + scpy(str, start, length); + return str; +} + +void * +areturn(Arena *arena, unsigned int checkpoint, const void *src, unsigned int size) +{ + void *dest; + if(arena == NULL || src == NULL) return NULL; + + dest = (void *)&arena->tape[checkpoint]; + if (src == dest) return dest; + + memmove(dest, src, size); + arena->count = checkpoint + size; + + return dest; +} + +#define ARENA_RETURN(arena, ckpt, src_ptr, type) \ + return (type *)areturn((arena), (ckpt), (src_ptr), sizeof(type)) + +#define ARENA_RETURN_ARRAY(arena, ckpt, src_ptr, type, count) \ + return (type *)areturn((arena), (ckpt), (src_ptr), sizeof(type) * (count)) + +typedef struct Point Point; +struct Point { + unsigned int x; + unsigned int y; +}; + +Point * +Point_new(Arena *a, unsigned int x, unsigned int y) +{ + Point *this = (Point *)aalloc(a, sizeof(Point)); + this->x = x; + this->y = y; + return this; +} + +char* +Point_toS(Arena *a, Point *this) +{ + unsigned int ckpt = a->count; + char *s = (char*)&a->tape[a->count]; + int len = sprintf(s, "[x:%d, y:%d]", + this->x, + this->y); + s[len] = '\0'; + a->count += len + 1; + ARENA_RETURN_ARRAY(a, ckpt, s, char, len + 1); +} + +typedef struct Rect Rect; +struct Rect { + Point top_left; + Point bottom_right; + unsigned int width; + unsigned int height; +}; + +Rect * +Rect_new(Arena *a, Point *tl, Point *br, unsigned int width, unsigned int height) +{ + Rect *this = (Rect *)aalloc(a, sizeof(Rect)); + memcpy(&this->top_left, tl, sizeof(Point)); + memcpy(&this->bottom_right, br, sizeof(Point)); + this->width = width; + this->height = height; + return this; +} + +char* +Rect_toS(Arena *a, Rect *this) +{ + unsigned int ckpt = a->count; + char *s1 = Point_toS(a, &this->top_left); + char *s2 = Point_toS(a, &this->bottom_right); + + char *s = (char*)&a->tape[a->count]; + int len = sprintf(s, "[top_left: %s, bottom_right: %s, width:%d, height:%d]", + s1, + s2, + this->width, + this->height); + s[len] = '\0'; + a->count += len + 1; + ARENA_RETURN_ARRAY(a, ckpt, s, char, len + 1); +} + +Rect * +create_geometry(Arena *a) +{ + unsigned int ckpt = a->count; + Rect *final_rect; + Point *temp_tl; + Point *temp_br; + + temp_tl = Point_new(a, 10, 20); + temp_br = Point_new(a, 100, 200); + + final_rect = Rect_new(a, temp_tl, temp_br, 90, 180); + + ARENA_RETURN(a, ckpt, final_rect, Rect); +} + +int main() { + unsigned char tape[64000]; + Arena a = {tape, 0, 64000}; + Rect *r = create_geometry(&a); + printf("%s\n", Rect_toS(&a, r)); + return 0; +} diff --git a/test/plex.ul b/test/plex.ul new file mode 100755 index 0000000..dc82858 --- /dev/null +++ b/test/plex.ul @@ -0,0 +1,52 @@ +plex Point { + nat x; + nat y; + + init(nat x, nat y) { + this.x = x; + this.y = y; + } + + toS() { + pln("[x:%d, y:%d]", + this.x, + this.y); + } +} + +plex Rect { + Point top_left; + Point bottom_right; + nat width; + nat height; + + init(Point tl, Point br, nat width, nat height) { + this.top_left = tl; + this.bottom_right = br; + this.width = width; + this.height = height; + } + + toS() { + pln("[top_left: [x:%d, y:%d], bottom_right: [x:%d, y:%d], width:%d, height:%d]]", + this.top_left.x, + this.top_left.y, + this.bottom_right.x, + this.bottom_right.y, + this.width, + this.height); + } +} + +function create_geometry() Rect { + Point tl(10, 20); + Point br(100, 50); + Rect final_rect(tl, br, 90, 180); + return final_rect; +} + +function main() int { + Rect r = create_geometry(); + print(r.toS()); + return 0; +} diff --git a/test/simple.ul b/test/simple.ul new file mode 100755 index 0000000..6b38962 --- /dev/null +++ b/test/simple.ul @@ -0,0 +1 @@ +print((1.0 + 1.0) as str); diff --git a/test/trait.ul b/test/trait.ul new file mode 100755 index 0000000..7149236 --- /dev/null +++ b/test/trait.ul @@ -0,0 +1,21 @@ +trait Device { + nat handle; +} + +trait Printable { + print(); +} + +plex Screen implements Device { + nat handle; + nat width; +} + +plex Monitor implements Device, Printable { + nat handle; + nat width; + nat height; + + print() { + } +} diff --git a/test/while.ul b/test/while.ul new file mode 100755 index 0000000..ef621ba --- /dev/null +++ b/test/while.ul @@ -0,0 +1,6 @@ +nat i = 0; + +while (i < 10) { + print(i as str); + i = i + 1; +} diff --git a/test/window.ul b/test/window.ul new file mode 100755 index 0000000..ee8f4dc --- /dev/null +++ b/test/window.ul @@ -0,0 +1,72 @@ +/** + * Constants + */ +const str screen_namespace = "/dev/screen/0"; +const str mouse_namespace = "/dev/mouse/0"; +const str terminal_namespace = "/dev/term/0"; +const str new_line = "\n"; +const byte WHITE = 255; + +/** + * Devices + */ +plex Terminal { + nat handle; +} + +plex Screen { + nat handle; + nat width; + nat height; + byte[] buffer; + + draw() { + write(this, this.buffer, this.buffer_size); + } +} + +plex Mouse { + nat handle; + nat x; + nat y; + bool left; + bool right; + bool middle; + bool btn4; + nat size; +} + +/** + * Main function + */ +function main() { + Screen screen = open(screen_namespace, 0); + print(screen.handle as str); + print(screen.width as str); + print(screen.size as str); + unsafe { + print(screen.buffer.ptr as str); + } + + Mouse mouse = open(mouse_namespace, 0); + screen.draw(); + + loop { + if (mouse.left) { + unsafe { + screen.buffer[mouse.y * width + mouse.x + + screen.buffer.ptr + 4] = WHITE; + screen.draw(); + } + } + } +} + +/** + * Print with a newline + */ +function print(str message) { + Terminal term = open(terminal_namespace, 0); + write(term, message, message.length); + write(term, nl, nl.length); +}