diff --git a/.gitmodules b/.gitmodules index c5521f5..ec5deb2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,4 +6,4 @@ url = https://github.com/likle/cwalk.git [submodule "external/libdye"] path = external/libdye - url = https://github.com/kuzalekon/libdye.git + url = https://github.com/Ugric/libdye.git diff --git a/CMakeLists.txt b/CMakeLists.txt index ed72893..bfa76ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,9 +30,10 @@ add_custom_command( add_custom_target(GenerateLexer DEPENDS ${LEXER_C} ${LEXER_H}) # Step 3: Add executable -add_executable(argon external/xxhash/xxhash.c external/cwalk/src/cwalk.c ${CFILES} ${LEXER_C}) +add_executable(argon external/xxhash/xxhash.c external/cwalk/src/cwalk.c external/libdye/src/dye.c ${CFILES} ${LEXER_C}) set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) target_include_directories(argon PRIVATE ${CMAKE_SOURCE_DIR}/external/cwalk/include) +target_include_directories(argon PRIVATE ${CMAKE_SOURCE_DIR}/external/libdye/include) # Step 4: Build order add_dependencies(argon GenerateLexer) diff --git a/Makefile b/Makefile index 2bedbac..6331bef 100644 --- a/Makefile +++ b/Makefile @@ -2,8 +2,8 @@ LEXER_SRC = src/lexer/lex.l LEXER_C = src/lexer/lex.yy.c LEXER_H = src/lexer/lex.yy.h -CFILES = external/xxhash/xxhash.c external/cwalk/src/cwalk.c $(shell find src -name '*.c') -CFLAGS = $(ARCHFLAGS) -lm -lgc -lgmp -Wall -Wextra -Wno-unused-function -Iexternal/cwalk/include +CFILES = external/xxhash/xxhash.c external/cwalk/src/cwalk.c external/libdye/src/dye.c $(shell find src -name '*.c') +CFLAGS = $(ARCHFLAGS) -lm -lgc -lgmp -Wall -Wextra -Wno-unused-function -Iexternal/cwalk/include -Iexternal/libdye/include BINARY = bin/argon all: $(BINARY) diff --git a/external/libdye b/external/libdye index da48196..c3fa048 160000 --- a/external/libdye +++ b/external/libdye @@ -1 +1 @@ -Subproject commit da48196fffcc542051717977c859e4abceb4699d +Subproject commit c3fa048e6535ec828d7c07f789d754b13662e99b diff --git a/src/arobject.h b/src/arobject.h new file mode 100644 index 0000000..6242745 --- /dev/null +++ b/src/arobject.h @@ -0,0 +1,52 @@ +#ifndef AROBJECT_H +#define AROBJECT_H + +#include +#include "runtime/internals/dynamic_array_armem/darray_armem.h" + +typedef struct ArgonObject ArgonObject; // forward declaration + +typedef enum ArgonType { + TYPE_NULL, + TYPE_BOOL, + TYPE_NUMBER, + TYPE_STRING, + TYPE_FUNCTION, + TYPE_NATIVE_FUNCTION, + TYPE_OBJECT, +} ArgonType; + +struct string_struct { + char *data; + size_t length; +}; + +typedef struct Stack { + ArgonObject *scope; + struct Stack *prev; +} Stack; + +struct argon_function_struct { + darray_armem bytecode; + Stack stack; + size_t number_of_parameters; + char **parameters; +}; + +// full definition of ArgonObject (no typedef again!) +struct ArgonObject { + ArgonType type; + char *name; + ArgonObject *self; + ArgonObject *baseObject; + struct hashmap_GC *fields; + union { + mpq_t as_number; + bool as_bool; + struct string_struct as_str; + void *native_fn; + struct argon_function_struct argon_fn; + } value; +}; + +#endif // AROBJECT_H \ No newline at end of file diff --git a/src/err.c b/src/err.c new file mode 100644 index 0000000..4bb550b --- /dev/null +++ b/src/err.c @@ -0,0 +1,134 @@ +#include "err.h" +#include "../external/libdye/include/dye.h" +#include +#include +#include +#include +#include +#include +#include +#include + +const ArErr no_err = (ArErr){false}; + +ArErr create_err(int64_t line, int64_t column, int length, char *path, + const char *type, const char *fmt, ...) { + ArErr err; + err.exists = true; + err.path = path; + err.line = line; + err.column = column; + err.length = length; + + // Copy error type safely + strncpy(err.type, type, sizeof(err.type) - 1); + err.type[sizeof(err.type) - 1] = '\0'; + + // Format error message + va_list args; + va_start(args, fmt); + vsnprintf(err.message, sizeof(err.message), fmt, args); + va_end(args); + + return err; +} + +void output_err(ArErr err) { + if (!err.exists) + return; + dye(stderr, DYE_WHITE, DYE_RED); + fprintf(stderr, "ERROR!"); + dye(stderr, DYE_RESET, DYE_RESET); + fprintf(stderr, " "); + dyefg(stderr, DYE_RED); + dye_style(stderr, DYE_STYLE_BOLD); + fprintf(stderr, "%s", err.type); + dye_style(stderr, DYE_STYLE_RESET); + dyefg(stderr, DYE_RESET); + fprintf(stderr, ": "); + dyefg(stderr, DYE_RED); + fprintf(stderr, "%s", err.message); + dye_style(stderr, DYE_STYLE_RESET); + dyefg(stderr, DYE_RESET); + fprintf(stderr, "\n"); + + if (err.path && err.line) { + dyefg(stderr, DYE_GRAY); + fprintf(stderr, " --> "); + dyefg(stderr, DYE_CYAN); + fprintf(stderr, "%s", err.path); + dyefg(stderr, DYE_GRAY); + fprintf(stderr, ":"); + dyefg(stderr, DYE_YELLOW); + fprintf(stderr, "%zu", err.line); + dyefg(stderr, DYE_GRAY); + fprintf(stderr, ":"); + dyefg(stderr, DYE_YELLOW); + fprintf(stderr, "%zu", err.column); + dye_style(stderr, DYE_STYLE_RESET); + dyefg(stderr, DYE_RESET); + fprintf(stderr, "\n"); + FILE *file = fopen(err.path, "r"); + if (file) { + dye_style(stderr, DYE_STYLE_RESET); + dyefg(stderr, DYE_RESET); + int line_number_width = snprintf(NULL, 0, "%zu", err.line); + char *buffer = NULL; + size_t size = 0; + int current_line = 1; + ssize_t len; + + while ((len = getline(&buffer, &size, file)) != -1) { + if (current_line == err.line) { + break; + } + current_line++; + } + fprintf(stderr, " "); + for (int i = 0; i < line_number_width; i++) { + fprintf(stderr, " "); + } + fprintf(stderr, "|\n"); + getline(&buffer, &size, file); + + char *line_starts = buffer; + while (*line_starts && isspace((unsigned char)*line_starts)) { + line_starts++; + err.column--; + } + fprintf(stderr, " %zu | ", err.line); + if (err.length) { + fprintf(stderr, "%.*s", (int)err.column - 1, line_starts); + dyefg(stderr, DYE_RED); + dye_style(stderr, DYE_STYLE_BOLD); + fprintf(stderr, "%.*s", err.length, line_starts + err.column - 1); + dye_style(stderr, DYE_STYLE_RESET); + dyefg(stderr, DYE_RESET); + fprintf(stderr, "%s", line_starts + (int)err.column + err.length - 1); + for (int64_t i = 0; i < err.column - 1; i++) { + fprintf(stderr, " "); + } + } else { + fprintf(stderr, "%s", line_starts); + } + free(buffer); + fprintf(stderr, "\n "); + for (int i = 0; i < line_number_width; i++) { + fprintf(stderr, " "); + } + fprintf(stderr, "| "); + + for (int i = 1; i < err.column; i++) { + fprintf(stderr, " "); + } + dyefg(stderr, DYE_RED); + dye_style(stderr, DYE_STYLE_BOLD); + for (int i = 0; i < err.length; i++) { + fprintf(stderr, "^"); + } + dye_style(stderr, DYE_STYLE_RESET); + dyefg(stderr, DYE_RESET); + fprintf(stderr, "\n"); + } + } +} \ No newline at end of file diff --git a/src/err.h b/src/err.h new file mode 100644 index 0000000..1d8e9c2 --- /dev/null +++ b/src/err.h @@ -0,0 +1,7 @@ +#include "returnTypes.h" + +extern const ArErr no_err; + +ArErr create_err(int64_t line, int64_t column, int length, char *path, const char *type, + const char *fmt, ...); +void output_err(ArErr err); \ No newline at end of file diff --git a/src/hashmap/hashmap.h b/src/hashmap/hashmap.h index eb8f969..fc2514a 100644 --- a/src/hashmap/hashmap.h +++ b/src/hashmap/hashmap.h @@ -3,8 +3,6 @@ #include #include -typedef struct ArgonObject ArgonObject; - typedef void (*free_val_func)(void *val); struct node { diff --git a/src/lexer/lex.l b/src/lexer/lex.l index 3800f3a..466c047 100644 --- a/src/lexer/lex.l +++ b/src/lexer/lex.l @@ -108,8 +108,6 @@ int yywrap(void * unused_param) { #[^\n]* { /* skip comment */ } . { - GET_STATE - fprintf(stderr, "%s:%zu:%zu error: unexpected character '%s'\n", state->path, state->current_line+1, COLUMN_NO+1, yytext); - exit(1); + return TOKEN_INVALID; } %% \ No newline at end of file diff --git a/src/lexer/lexer.c b/src/lexer/lexer.c index 4e9e3fc..5d8869b 100644 --- a/src/lexer/lexer.c +++ b/src/lexer/lexer.c @@ -1,8 +1,8 @@ #include "lexer.h" -#include "lex.yy.h" #include "../string/string.h" +#include "lex.yy.h" -void lexer(LexerState state) { +ArErr lexer(LexerState state) { size_t line = 1; size_t column = 1; int ch; @@ -32,13 +32,16 @@ void lexer(LexerState state) { int token; while ((token = yylex(scanner)) != 0) { - Token token_struct = (Token){ - token, - state.current_line+1, - state.current_column+1, - yyget_leng(scanner), - cloneString(yyget_text(scanner)) - }; + if (token == TOKEN_INVALID) { + ArErr err = create_err(state.current_line + 1, state.current_column + 1, + yyget_leng(scanner), state.path, "Syntax Error", + "Invalid Token '%s'", yyget_text(scanner)); + yylex_destroy(scanner); + return err; + } + Token token_struct = + (Token){token, state.current_line + 1, state.current_column + 1, + yyget_leng(scanner), cloneString(yyget_text(scanner))}; darray_push(state.tokens, &token_struct); if (token == TOKEN_NEW_LINE) { state.current_line += 1; @@ -48,4 +51,5 @@ void lexer(LexerState state) { } } yylex_destroy(scanner); + return no_err; } \ No newline at end of file diff --git a/src/lexer/lexer.h b/src/lexer/lexer.h index 0d7b51a..87eac13 100644 --- a/src/lexer/lexer.h +++ b/src/lexer/lexer.h @@ -4,9 +4,10 @@ #include "../dynamic_array/darray.h" #include "token.h" #include +#include "../err.h" typedef struct { - const char *path; + char *path; FILE *file; size_t current_line; size_t current_column; @@ -14,6 +15,6 @@ typedef struct { // add more fields as needed } LexerState; -void lexer(LexerState state); +ArErr lexer(LexerState state); #endif // LEXER_H \ No newline at end of file diff --git a/src/lexer/token.h b/src/lexer/token.h index 8658ece..0eae406 100644 --- a/src/lexer/token.h +++ b/src/lexer/token.h @@ -72,6 +72,7 @@ typedef enum { TOKEN_COMMA, TOKEN_COLON, TOKEN_EXCLAMATION, + TOKEN_INVALID, } TokenType; typedef struct { diff --git a/src/main.c b/src/main.c index ba21e66..5da036d 100644 --- a/src/main.c +++ b/src/main.c @@ -35,6 +35,7 @@ #include #include #endif +#include "err.h" char *get_current_directory() { char *buffer = NULL; @@ -160,7 +161,7 @@ FAILED: return 1; } -ArgonObject *execute(char*absolute_path) { +Execution execute(char *absolute_path) { clock_t start, end; double time_spent, total_time_spent = 0; @@ -168,7 +169,10 @@ ArgonObject *execute(char*absolute_path) { size_t basename_length; cwk_path_get_basename(absolute_path, &basename_ptr, &basename_length); - if (!basename_ptr) return NULL; + if (!basename_ptr) + return (Execution){create_err(0, 0 ,0 , NULL, "Path Error", + "path has no basename '%s'", absolute_path), + (Stack){NULL, NULL}}; char basename[FILENAME_MAX]; memcpy(basename, basename_ptr, basename_length); @@ -181,15 +185,20 @@ ArgonObject *execute(char*absolute_path) { parent_directory[parent_directory_length] = '\0'; char cache_folder_path[FILENAME_MAX]; - cwk_path_join(parent_directory, CACHE_FOLDER, cache_folder_path, sizeof(cache_folder_path)); + cwk_path_join(parent_directory, CACHE_FOLDER, cache_folder_path, + sizeof(cache_folder_path)); char cache_file_path[FILENAME_MAX]; - cwk_path_join(cache_folder_path, basename, cache_file_path, sizeof(cache_file_path)); - cwk_path_change_extension(cache_file_path, BYTECODE_EXTENTION, cache_file_path, sizeof(cache_file_path)); + cwk_path_join(cache_folder_path, basename, cache_file_path, + sizeof(cache_file_path)); + cwk_path_change_extension(cache_file_path, BYTECODE_EXTENTION, + cache_file_path, sizeof(cache_file_path)); FILE *file = fopen(absolute_path, "r"); if (!file) { - return NULL; + return (Execution){create_err(0, 0, 0, NULL, "File Error", "Unable to open file '%s'", + absolute_path), + (Stack){NULL, NULL}}; } XXH3_state_t *hash_state = XXH3_createState(); @@ -215,7 +224,13 @@ ArgonObject *execute(char*absolute_path) { LexerState state = {absolute_path, file, 0, 0, &tokens}; start = clock(); - lexer(state); + ArErr err = lexer(state); + if (err.exists) { + free_translator(&translated); + darray_free(&tokens, free_token); + return (Execution){err, + (Stack){NULL, NULL}}; + } end = clock(); time_spent = (double)(end - start) / CLOCKS_PER_SEC; total_time_spent += time_spent; @@ -269,7 +284,8 @@ ArgonObject *execute(char*absolute_path) { start = clock(); RuntimeState state = init_runtime_state(translated); - ArgonObject *resp = runtime(translated,state); + Stack main_scope = create_scope(NULL); + ArErr err = runtime(translated, state, main_scope); end = clock(); time_spent = (double)(end - start) / CLOCKS_PER_SEC; @@ -278,7 +294,7 @@ ArgonObject *execute(char*absolute_path) { printf("total time taken: %f seconds\n", total_time_spent); free_translator(&translated); - return resp; + return (Execution){err, main_scope}; } int main(int argc, char *argv[]) { @@ -291,10 +307,12 @@ int main(int argc, char *argv[]) { return -1; char *path_non_absolute = argv[1]; char path[FILENAME_MAX]; - cwk_path_get_absolute(CWD, path_non_absolute, path, - sizeof(path)); + cwk_path_get_absolute(CWD, path_non_absolute, path, sizeof(path)); free(CWD); - ArgonObject *resp = execute(path); - if (resp) return 0; - return -1; + Execution resp = execute(path); + if (resp.err.exists){ + output_err(resp.err); + return -1; + } + return 0; } \ No newline at end of file diff --git a/src/returnTypes.h b/src/returnTypes.h new file mode 100644 index 0000000..fea6a47 --- /dev/null +++ b/src/returnTypes.h @@ -0,0 +1,22 @@ +#ifndef RETURN_TYPES_H +#define RETURN_TYPES_H +#include +#include "arobject.h" + +#define ERR_MSG_MAX_LEN 256 + +typedef struct ArErr { + bool exists; + char *path; + int64_t line; + int64_t column; + int length; + char type[32]; + char message[ERR_MSG_MAX_LEN]; +} ArErr; + +typedef struct Execution { + ArErr err; + Stack stack; +} Execution; +#endif // RETURN_TYPES_ \ No newline at end of file diff --git a/src/runtime/internals/hashmap/hashmap.h b/src/runtime/internals/hashmap/hashmap.h index b2dd9b4..1e47135 100644 --- a/src/runtime/internals/hashmap/hashmap.h +++ b/src/runtime/internals/hashmap/hashmap.h @@ -3,8 +3,6 @@ #include #include -typedef struct ArgonObject ArgonObject; - struct node_GC { uint64_t hash; void *key; diff --git a/src/runtime/objects/object.h b/src/runtime/objects/object.h index f9254b6..ac91b9f 100644 --- a/src/runtime/objects/object.h +++ b/src/runtime/objects/object.h @@ -1,53 +1,15 @@ #ifndef OBJECT_H #define OBJECT_H #include "../internals/hashmap/hashmap.h" -#include "../internals/dynamic_array_armem/darray_armem.h" -#include -#include #include "../runtime.h" +#include extern ArgonObject *BASE_CLASS; -struct string_struct { - char *data; - size_t length; -}; -struct argon_function_struct { - darray_armem bytecode; - struct Stack stack; - size_t number_of_parameters; - char** parameters; -}; - -typedef enum { - TYPE_NULL, - TYPE_BOOL, - TYPE_NUMBER, - TYPE_STRING, - TYPE_FUNCTION, - TYPE_NATIVE_FUNCTION, - TYPE_OBJECT, // generic user object -} ArgonType; - -struct ArgonObject { - ArgonType type; - char* name; - ArgonObject *self; - ArgonObject *baseObject; - struct hashmap_GC *fields; // dynamic fields/methods - union { - mpq_t as_number; - bool as_bool; - struct string_struct as_str; - void *native_fn; - struct argon_function_struct argon_fn; - // others as needed - } value; -}; typedef struct ArgonObject ArgonObject; void init_base_field(); -ArgonObject* init_child_argon_object(ArgonObject *cls); -ArgonObject* init_argon_class(char*name); +ArgonObject *init_child_argon_object(ArgonObject *cls); +ArgonObject *init_argon_class(char *name); -void add_field(ArgonObject*target, char* name, ArgonObject *object); +void add_field(ArgonObject *target, char *name, ArgonObject *object); #endif // OBJECT_H \ No newline at end of file diff --git a/src/runtime/runtime.c b/src/runtime/runtime.c index 526ce0d..83ca44a 100644 --- a/src/runtime/runtime.c +++ b/src/runtime/runtime.c @@ -1,4 +1,5 @@ #include "runtime.h" +#include "../err.h" #include "../translator/translator.h" #include "objects/functions/functions.h" #include "objects/null/null.h" @@ -62,20 +63,8 @@ void load_const(Translated *translated, RuntimeState *state) { state->registers[to_register] = object; } -const ArErr no_err = (ArErr){false}; - -ArErr create_err(char *path, int64_t line, char *type, char *message) { - return (ArErr){ - false, - path, - line, - type, - message - }; -} - - ArErr run_instruction(Translated *translated, RuntimeState *state, - struct Stack stack) { +ArErr run_instruction(Translated *translated, RuntimeState *state, + struct Stack stack) { OperationType opcode = pop_byte(translated, state); switch (opcode) { case OP_LOAD_NULL: @@ -88,24 +77,26 @@ ArErr create_err(char *path, int64_t line, char *type, char *message) { load_argon_function(translated, state, stack); break; default: - fprintf(stderr, "bytecode invalid\n"); - exit(EXIT_FAILURE); + return create_err(0, 0, 0, NULL, "Runtime Error", "Invalid Opcode %#x", opcode); } return no_err; } RuntimeState init_runtime_state(Translated translated) { - RuntimeState state = { + return (RuntimeState){ checked_malloc(translated.registerCount * sizeof(ArgonObject *)), 0}; - return state; } -ArgonObject *runtime(Translated translated, RuntimeState state) { - struct Stack stack = {NULL, NULL}; +Stack create_scope(Stack *prev) { return (Stack){NULL, prev}; } + +ArErr runtime(Translated translated, RuntimeState state, Stack stack) { state.head = 0; while (state.head < translated.bytecode.size) { - run_instruction(&translated, &state, stack); + ArErr err = run_instruction(&translated, &state, stack); + if (err.exists) { + return err; + } } free(state.registers); - return stack.scope; + return no_err; } \ No newline at end of file diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index 4f4367d..278197c 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -2,25 +2,13 @@ #define RUNTIME_H #include "../translator/translator.h" #include "internals/hashmap/hashmap.h" +#include "../returnTypes.h" typedef struct { ArgonObject **registers; size_t head; } RuntimeState; -typedef struct { - bool exists; - char *path; - int64_t line; - char *type; - char *message; -} ArErr; - -typedef struct Stack { - ArgonObject *scope; - struct Stack *prev; -} Stack; - void init_types(); uint64_t pop_bytecode(Translated *translated, RuntimeState *state); @@ -30,6 +18,8 @@ ArErr run_instruction(Translated *translated, RuntimeState *state, RuntimeState init_runtime_state(Translated translated); -ArgonObject *runtime(Translated translated, RuntimeState state); +Stack create_scope(Stack *prev); + +ArErr runtime(Translated translated, RuntimeState state, Stack stack); #endif // RUNTIME_H \ No newline at end of file diff --git a/testing.ar b/testing.ar new file mode 100644 index 0000000..63fc4a5 --- /dev/null +++ b/testing.ar @@ -0,0 +1 @@ +10~10 \ No newline at end of file