diff --git a/perf.data b/perf.data deleted file mode 100644 index 4f37086..0000000 Binary files a/perf.data and /dev/null differ diff --git a/perf.data.old b/perf.data.old deleted file mode 100644 index 78e08e2..0000000 Binary files a/perf.data.old and /dev/null differ diff --git a/src/arobject.h b/src/arobject.h index 1c3bdc6..6b7d89a 100644 --- a/src/arobject.h +++ b/src/arobject.h @@ -12,6 +12,32 @@ #include "runtime/internals/hashmap/hashmap.h" #include + +typedef enum { + __base__, + __class__, + __name__, + __add__, + __string__, + __subtract__, + __multiply__, + __division__, + __new__, + __init__, + __boolean__, + __get_attr__, + __binding__, + __function__, + field__address, + __call__, + __number__, + field_log, + field_length, + __getattribute__, + + BUILT_IN_FIELDS_COUNT, +} built_in_fields; + typedef struct ArErr ArErr; typedef struct RuntimeState RuntimeState; @@ -77,6 +103,7 @@ struct ArgonObject { ArgonType type; ArgonType child_type; struct hashmap_GC *dict; + ArgonObject* Builtin_slots[BUILT_IN_FIELDS_COUNT]; bool as_bool; union { struct as_number { diff --git a/src/err.h b/src/err.h index 4a9f623..dc0703c 100644 --- a/src/err.h +++ b/src/err.h @@ -4,10 +4,13 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ +#ifndef RETURN_TYPE_H +#define RETURN_TYPE_H #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 +void output_err(ArErr err); +#endif // RETURN_TYPE_H \ No newline at end of file diff --git a/src/import.c b/src/import.c new file mode 100644 index 0000000..b399fb2 --- /dev/null +++ b/src/import.c @@ -0,0 +1,468 @@ +#include "../external/cwalk/include/cwalk.h" +#include "../external/xxhash/xxhash.h" +#include "arobject.h" +#include "err.h" +#include "hash_data/hash_data.h" +#include "hashmap/hashmap.h" +#include "lexer/lexer.h" +#include "lexer/token.h" +#include "runtime/internals/hashmap/hashmap.h" +#include "runtime/runtime.h" +#include "translator/translator.h" +#include +#include +#include +#include +#ifdef _WIN32 +#include // for _mkdir +#include // for _stat +#include +#else +#include +#include +#include +#endif +#ifdef _WIN32 +#include +#else +#include +#include +#endif +#include "err.h" +#include +#if defined(_WIN32) || defined(_WIN64) + +// Windows / MinGW usually uses little-endian, so these can be no-ops +// But define them explicitly to avoid implicit declaration warnings + +static inline uint32_t le32toh(uint32_t x) { return x; } +static inline uint64_t le64toh(uint64_t x) { return x; } +static inline uint32_t htole32(uint32_t x) { return x; } +static inline uint64_t htole64(uint64_t x) { return x; } + +#elif defined(__linux__) +#include +#include +#elif defined(__APPLE__) +#include +#define htole32(x) OSSwapHostToLittleInt32(x) +#define le32toh(x) OSSwapLittleToHostInt32(x) +#define htole64(x) OSSwapHostToLittleInt64(x) +#define le64toh(x) OSSwapLittleToHostInt64(x) +// Add others as needed +#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) +#include +#include +#endif + +int ensure_dir_exists(const char *path) { +#ifdef _WIN32 + struct _stat st; + if (_stat(path, &st) != 0) { + // Directory does not exist, create it + if (_mkdir(path) != 0) { + perror("_mkdir failed"); + return -1; + } + } else if (!(st.st_mode & _S_IFDIR)) { + fprintf(stderr, "Path exists but is not a directory\n"); + return -1; + } +#else + struct stat st; + if (stat(path, &st) != 0) { + // Directory does not exist, create it + if (mkdir(path, 0755) != 0) { + perror("mkdir failed"); + return -1; + } + } else if (!S_ISDIR(st.st_mode)) { + fprintf(stderr, "Path exists but is not a directory\n"); + return -1; + } +#endif + return 0; +} + +char *CWD; + +const char CACHE_FOLDER[] = "__arcache__"; +const char FILE_IDENTIFIER[5] = "ARBI"; +const char BYTECODE_EXTENTION[] = "arbin"; +const uint32_t version_number = 0; + +bool file_exists(const char *path) { + struct stat st; + if (stat(path, &st) == 0) { + return S_ISREG(st.st_mode); // true only if it's a regular file + } + return false; // doesn't exist, or stat failed +} + +static inline void write_and_hash(FILE *file, XXH64_state_t *state, + const void *ptr, size_t size, size_t count) { + fwrite(ptr, size, count, file); + XXH64_update(state, ptr, size * count); +} + +static inline void update_hash_from_file(FILE *file, XXH64_state_t *state, + size_t size) { + char buffer[4096]; + size_t bytes_read; + size_t remaining = size; + + while (remaining > 0 && + (bytes_read = + fread(buffer, 1, + remaining > sizeof(buffer) ? sizeof(buffer) : remaining, + file)) > 0) { + XXH64_update(state, buffer, bytes_read); + remaining -= bytes_read; + } +} + +int load_cache(Translated *translated_dest, char *joined_paths, uint64_t hash, + char *source_path) { + FILE *bytecode_file = fopen(joined_paths, "rb"); + if (!bytecode_file) { + fprintf(stderr, "cache doesnt exist... compiling from source.\n"); + return 1; + } + + // Find file size + fseek(bytecode_file, 0, SEEK_END); + long file_size = ftell(bytecode_file); + if (file_size < (long)sizeof(uint64_t)) { + goto FAILED; + } + fseek(bytecode_file, 0, SEEK_SET); + + // Footer is the last 8 bytes + long data_size = file_size - sizeof(uint64_t); + + // Set up hash state + XXH64_state_t *state = XXH64_createState(); + XXH64_reset(state, 0); + + // Hash everything except last 8 bytes + update_hash_from_file(bytecode_file, state, data_size); + + // Read stored footer hash + uint64_t stored_hash_le; + if (fread(&stored_hash_le, 1, sizeof(stored_hash_le), bytecode_file) != + sizeof(stored_hash_le)) { + XXH64_freeState(state); + goto FAILED; + } + uint64_t stored_hash = le64toh(stored_hash_le); + + // Compare + uint64_t calc_hash = XXH64_digest(state); + XXH64_freeState(state); + + if (calc_hash != stored_hash) { + fprintf(stderr, "cache hash mismatch (corrupted?)\n"); + goto FAILED; + } + + // Now actually parse the file contents + fseek(bytecode_file, 0, SEEK_SET); // rewind to start + + char file_identifier_from_cache[sizeof(FILE_IDENTIFIER)] = {0}; + if (fread(&file_identifier_from_cache, 1, + sizeof(file_identifier_from_cache) - 1, + bytecode_file) != sizeof(file_identifier_from_cache) - 1 || + memcmp(file_identifier_from_cache, FILE_IDENTIFIER, + sizeof(file_identifier_from_cache)) != 0) { + goto FAILED; + } + + uint32_t read_version; + if (fread(&read_version, 1, sizeof(read_version), bytecode_file) != + sizeof(read_version)) { + goto FAILED; + } + read_version = le32toh(read_version); + + if (read_version != version_number) { + goto FAILED; + } + + uint64_t read_hash; + if (fread(&read_hash, 1, sizeof(read_hash), bytecode_file) != + sizeof(read_hash)) { + goto FAILED; + } + read_hash = le64toh(read_hash); + + if (read_hash != hash) { + goto FAILED; + } + + uint8_t register_count; + if (fread(®ister_count, 1, sizeof(register_count), bytecode_file) != + sizeof(register_count)) { + goto FAILED; + } + + uint64_t constantsSize; + if (fread(&constantsSize, 1, sizeof(constantsSize), bytecode_file) != + sizeof(constantsSize)) { + goto FAILED; + } + constantsSize = le64toh(constantsSize); + + uint64_t bytecodeSize; + if (fread(&bytecodeSize, 1, sizeof(bytecodeSize), bytecode_file) != + sizeof(bytecodeSize)) { + goto FAILED; + } + bytecodeSize = le64toh(bytecodeSize); + + *translated_dest = init_translator(source_path); + + translated_dest->registerCount = register_count; + + arena_resize(&translated_dest->constants, constantsSize); + + if (fread(translated_dest->constants.data, 1, constantsSize, bytecode_file) != + constantsSize) { + goto FAILED; + } + + translated_dest->constants.size = constantsSize; + + darray_resize(&translated_dest->bytecode, bytecodeSize); + + if (fread(translated_dest->bytecode.data, 1, bytecodeSize, bytecode_file) != + bytecodeSize) { + goto FAILED; + } + + translated_dest->bytecode.size = bytecodeSize; + + fprintf(stderr, "cache exists and is valid, so will be used.\n"); + fclose(bytecode_file); + return 0; +FAILED: + fprintf(stderr, "cache is invalid... compiling from source.\n"); + fclose(bytecode_file); + return 1; +} + +Translated load_argon_file(char *path, ArErr *err) { + clock_t start, end; + clock_t beginning = clock(); + double time_spent, total_time_spent = 0; + + const char *basename_ptr; + size_t basename_length; + cwk_path_get_basename(path, &basename_ptr, &basename_length); + + if (!basename_ptr) { + *err = create_err(0, 0, 0, NULL, "Path Error", "path has no basename '%s'", + path); + return (Translated){}; + } + + char basename[FILENAME_MAX]; + memcpy(basename, basename_ptr, basename_length); + + size_t parent_directory_length; + cwk_path_get_dirname(path, &parent_directory_length); + + char parent_directory[FILENAME_MAX]; + memcpy(parent_directory, path, parent_directory_length); + 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)); + + 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)); + + FILE *file = fopen(path, "r"); + if (!file) { + *err = create_err(0, 0, 0, NULL, "File Error", "Unable to open file '%s'", + path); + return (Translated){}; + } + + XXH3_state_t *hash_state = XXH3_createState(); + XXH3_64bits_reset(hash_state); + + char buffer[8192]; + size_t bytes; + while ((bytes = fread(buffer, 1, sizeof(buffer), file)) > 0) { + XXH3_64bits_update(hash_state, buffer, bytes); + } + rewind(file); + uint64_t hash = XXH3_64bits_digest(hash_state); + XXH3_freeState(hash_state); + + Translated translated; + + if (load_cache(&translated, cache_file_path, hash, path) != 0) { + + DArray tokens; + darray_init(&tokens, sizeof(Token)); + + LexerState state = {path, file, 0, 0, &tokens}; + start = clock(); + *err = lexer(state); + if (err->exists) { + darray_free(&tokens, free_token); + return (Translated){}; + } + end = clock(); + time_spent = (double)(end - start) / CLOCKS_PER_SEC; + fprintf(stderr, "Lexer time taken: %f seconds\n", time_spent); + fclose(state.file); + + DArray ast; + + darray_init(&ast, sizeof(ParsedValue)); + + start = clock(); + *err = parser(path, &ast, &tokens, false); + darray_free(&tokens, free_token); + if (err->exists) { + darray_free(&ast, free_parsed); + return (Translated){}; + } + end = clock(); + time_spent = (double)(end - start) / CLOCKS_PER_SEC; + fprintf(stderr, "Parser time taken: %f seconds\n", time_spent); + + start = clock(); + + translated = init_translator(path); + *err = translate(&translated, &ast); + darray_free(&ast, free_parsed); + if (err->exists) { + darray_free(&translated.bytecode, NULL); + free(translated.constants.data); + hashmap_free(translated.constants.hashmap, NULL); + return (Translated){}; + } + end = clock(); + time_spent = (double)(end - start) / CLOCKS_PER_SEC; + fprintf(stderr, "Translation time taken: %f seconds\n", time_spent); +#if defined(__linux__) + malloc_trim(0); +#endif + + ensure_dir_exists(cache_folder_path); + + file = fopen(cache_file_path, "wb"); + + uint64_t constantsSize = translated.constants.size; + uint64_t bytecodeSize = translated.bytecode.size; + + uint32_t version_number_htole32ed = htole32(version_number); + uint64_t net_hash = htole64(hash); + constantsSize = htole64(constantsSize); + bytecodeSize = htole64(bytecodeSize); + + XXH64_state_t *hash_state = XXH64_createState(); + XXH64_reset(hash_state, 0); + + write_and_hash(file, hash_state, &FILE_IDENTIFIER, sizeof(char), + strlen(FILE_IDENTIFIER)); + write_and_hash(file, hash_state, &version_number_htole32ed, + sizeof(uint32_t), 1); + write_and_hash(file, hash_state, &net_hash, sizeof(net_hash), 1); + write_and_hash(file, hash_state, &translated.registerCount, sizeof(uint8_t), + 1); + write_and_hash(file, hash_state, &constantsSize, sizeof(uint64_t), 1); + write_and_hash(file, hash_state, &bytecodeSize, sizeof(uint64_t), 1); + write_and_hash(file, hash_state, translated.constants.data, 1, + translated.constants.size); + write_and_hash(file, hash_state, translated.bytecode.data, + translated.bytecode.element_size, translated.bytecode.size); + + // Finalize the hash + uint64_t file_hash = XXH64_digest(hash_state); + XXH64_freeState(hash_state); + + // Convert to little-endian before writing if needed + uint64_t file_hash_le = htole64(file_hash); + fwrite(&file_hash_le, sizeof(file_hash_le), 1, file); + + fclose(file); + } + hashmap_free(translated.constants.hashmap, NULL); + Translated gc_translated = { + translated.registerCount, translated.registerAssignment, NULL, {}, {}, + translated.path}; + gc_translated.bytecode.data = ar_alloc_atomic(translated.bytecode.capacity); + memcpy(gc_translated.bytecode.data, translated.bytecode.data, + translated.bytecode.capacity); + gc_translated.bytecode.element_size = translated.bytecode.element_size; + gc_translated.bytecode.size = translated.bytecode.size; + gc_translated.bytecode.resizable = false; + gc_translated.bytecode.capacity = + translated.bytecode.size * translated.bytecode.element_size; + gc_translated.constants.data = ar_alloc_atomic(translated.constants.capacity); + memcpy(gc_translated.constants.data, translated.constants.data, + translated.constants.capacity); + gc_translated.constants.size = translated.constants.size; + gc_translated.constants.capacity = translated.constants.capacity; + free(translated.bytecode.data); + free(translated.constants.data); + total_time_spent = (double)(clock() - beginning) / CLOCKS_PER_SEC; + fprintf(stderr, "total time taken loading file (%s): %f seconds\n", path, + total_time_spent); + return gc_translated; +} + +const char *PRE_PATHS_TO_TEST[] = {"", "", "argon_modules", "argon_modules"}; +const char *POST_PATHS_TO_TEST[sizeof(PRE_PATHS_TO_TEST)/sizeof(char *)] = {"", "init.ar", "", + "init.ar"}; + +struct hashmap *importing_hash_table = NULL; +struct hashmap_GC *imported_hash_table = NULL; + +Stack *ar_import(char *current_directory, char *path_relative, ArErr *err) { + char path[FILENAME_MAX]; + bool found = false; + for (size_t i = 0; i < sizeof(PRE_PATHS_TO_TEST)/sizeof(char *); i++) { + cwk_path_get_absolute(current_directory, PRE_PATHS_TO_TEST[i], path, sizeof(path)); + cwk_path_get_absolute(path, path_relative, path, sizeof(path)); + cwk_path_get_absolute(path, POST_PATHS_TO_TEST[i], path, sizeof(path)); + if (file_exists(path)) { + found = true; + break; + } + } + if (!found) { + *err = create_err(0, 0, 0, NULL, "File Error", "Unable to find file '%s'", + path_relative); + return NULL; + } + if (!importing_hash_table) importing_hash_table = createHashmap(); + uint64_t hash = siphash64_bytes(path, strlen(path), siphash_key); + hashmap_insert(importing_hash_table, hash, path, (void*)true, 0); + Translated translated = load_argon_file(path, err); + if (err->exists) { + return NULL; + } + clock_t start = clock(), end; + RuntimeState state = init_runtime_state(translated, path); + Stack *main_scope = create_scope(Global_Scope, true); + runtime(translated, state, main_scope, err); + if (err->exists) { + return NULL; + } + end = clock(); + double time_spent = (double)(end - start) / CLOCKS_PER_SEC; + fprintf(stderr, "Execution time taken: %f seconds\n", time_spent); + hashmap_insert(importing_hash_table, hash, path, (void*)false, 0); + if (!imported_hash_table) imported_hash_table = createHashmap_GC(); + hashmap_insert_GC(imported_hash_table, hash, path, main_scope, 0); + return main_scope; +} \ No newline at end of file diff --git a/src/import.h b/src/import.h new file mode 100644 index 0000000..a969326 --- /dev/null +++ b/src/import.h @@ -0,0 +1,15 @@ +/* + * SPDX-FileCopyrightText: 2025 William Bell + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#ifndef IMPORT_H +#define IMPORT_H +#include "err.h" + +extern char*CWD; + +Stack *ar_import(char *current_directory, char *path_relative, ArErr *err); + +#endif // IMPORT_H \ No newline at end of file diff --git a/src/main.c b/src/main.c index 1f83d95..66186f2 100644 --- a/src/main.c +++ b/src/main.c @@ -4,75 +4,20 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -#include "arobject.h" -#include "dynamic_array/darray.h" +#include "err.h" #include "hashmap/hashmap.h" -#include "lexer/lexer.h" -#include "lexer/token.h" +#include "import.h" #include "memory.h" -#include "parser/parser.h" -#include "returnTypes.h" #include "runtime/objects/object.h" -#include "runtime/runtime.h" #include "shell.h" -#include "translator/translator.h" -#include "../external/xxhash/xxhash.h" #include "hash_data/hash_data.h" #include #include #include #include -#include #include -#include -#include #include -#ifdef _WIN32 -#include // for _mkdir -#include // for _stat -#include -#else -#include -#include -#include -#endif -#include "../external/cwalk/include/cwalk.h" -#include - -#ifdef _WIN32 -#include -#else -#include -#include -#endif -#include "err.h" -#include - -#if defined(_WIN32) || defined(_WIN64) - -// Windows / MinGW usually uses little-endian, so these can be no-ops -// But define them explicitly to avoid implicit declaration warnings - -static inline uint32_t le32toh(uint32_t x) { return x; } -static inline uint64_t le64toh(uint64_t x) { return x; } -static inline uint32_t htole32(uint32_t x) { return x; } -static inline uint64_t htole64(uint64_t x) { return x; } - -#elif defined(__linux__) -#include -#include -#elif defined(__APPLE__) -#include -#define htole32(x) OSSwapHostToLittleInt32(x) -#define le32toh(x) OSSwapLittleToHostInt32(x) -#define htole64(x) OSSwapHostToLittleInt64(x) -#define le64toh(x) OSSwapLittleToHostInt64(x) -// Add others as needed -#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) -#include -#include -#endif char *get_current_directory() { char *buffer = NULL; @@ -102,392 +47,24 @@ char *get_current_directory() { return buffer; } -int ensure_dir_exists(const char *path) { -#ifdef _WIN32 - struct _stat st; - if (_stat(path, &st) != 0) { - // Directory does not exist, create it - if (_mkdir(path) != 0) { - perror("_mkdir failed"); - return -1; - } - } else if (!(st.st_mode & _S_IFDIR)) { - fprintf(stderr, "Path exists but is not a directory\n"); - return -1; - } -#else - struct stat st; - if (stat(path, &st) != 0) { - // Directory does not exist, create it - if (mkdir(path, 0755) != 0) { - perror("mkdir failed"); - return -1; - } - } else if (!S_ISDIR(st.st_mode)) { - fprintf(stderr, "Path exists but is not a directory\n"); - return -1; - } -#endif - return 0; -} - -static inline void write_and_hash(FILE *file, XXH64_state_t *state, - const void *ptr, size_t size, size_t count) { - fwrite(ptr, size, count, file); - XXH64_update(state, ptr, size * count); -} - -static inline void update_hash_from_file(FILE *file, XXH64_state_t *state, - size_t size) { - char buffer[4096]; - size_t bytes_read; - size_t remaining = size; - - while (remaining > 0 && - (bytes_read = - fread(buffer, 1, - remaining > sizeof(buffer) ? sizeof(buffer) : remaining, - file)) > 0) { - XXH64_update(state, buffer, bytes_read); - remaining -= bytes_read; - } -} - -const char CACHE_FOLDER[] = "__arcache__"; -const char FILE_IDENTIFIER[5] = "ARBI"; -const char BYTECODE_EXTENTION[] = "arbin"; -const uint32_t version_number = 0; - -int load_cache(Translated *translated_dest, char *joined_paths, uint64_t hash, - char *source_path) { - FILE *bytecode_file = fopen(joined_paths, "rb"); - if (!bytecode_file) { - fprintf(stderr, "cache doesnt exist... compiling from source.\n"); - return 1; - } - - // Find file size - fseek(bytecode_file, 0, SEEK_END); - long file_size = ftell(bytecode_file); - if (file_size < (long)sizeof(uint64_t)) { - goto FAILED; - } - fseek(bytecode_file, 0, SEEK_SET); - - // Footer is the last 8 bytes - long data_size = file_size - sizeof(uint64_t); - - // Set up hash state - XXH64_state_t *state = XXH64_createState(); - XXH64_reset(state, 0); - - // Hash everything except last 8 bytes - update_hash_from_file(bytecode_file, state, data_size); - - // Read stored footer hash - uint64_t stored_hash_le; - if (fread(&stored_hash_le, 1, sizeof(stored_hash_le), bytecode_file) != - sizeof(stored_hash_le)) { - XXH64_freeState(state); - goto FAILED; - } - uint64_t stored_hash = le64toh(stored_hash_le); - - // Compare - uint64_t calc_hash = XXH64_digest(state); - XXH64_freeState(state); - - if (calc_hash != stored_hash) { - fprintf(stderr, "cache hash mismatch (corrupted?)\n"); - goto FAILED; - } - - // Now actually parse the file contents - fseek(bytecode_file, 0, SEEK_SET); // rewind to start - - char file_identifier_from_cache[sizeof(FILE_IDENTIFIER)] = {0}; - if (fread(&file_identifier_from_cache, 1, - sizeof(file_identifier_from_cache) - 1, - bytecode_file) != sizeof(file_identifier_from_cache) - 1 || - memcmp(file_identifier_from_cache, FILE_IDENTIFIER, - sizeof(file_identifier_from_cache)) != 0) { - goto FAILED; - } - - uint32_t read_version; - if (fread(&read_version, 1, sizeof(read_version), bytecode_file) != - sizeof(read_version)) { - goto FAILED; - } - read_version = le32toh(read_version); - - if (read_version != version_number) { - goto FAILED; - } - - uint64_t read_hash; - if (fread(&read_hash, 1, sizeof(read_hash), bytecode_file) != - sizeof(read_hash)) { - goto FAILED; - } - read_hash = le64toh(read_hash); - - if (read_hash != hash) { - goto FAILED; - } - - uint8_t register_count; - if (fread(®ister_count, 1, sizeof(register_count), bytecode_file) != - sizeof(register_count)) { - goto FAILED; - } - - uint64_t constantsSize; - if (fread(&constantsSize, 1, sizeof(constantsSize), bytecode_file) != - sizeof(constantsSize)) { - goto FAILED; - } - constantsSize = le64toh(constantsSize); - - uint64_t bytecodeSize; - if (fread(&bytecodeSize, 1, sizeof(bytecodeSize), bytecode_file) != - sizeof(bytecodeSize)) { - goto FAILED; - } - bytecodeSize = le64toh(bytecodeSize); - - *translated_dest = init_translator(source_path); - - - translated_dest->registerCount = register_count; - - arena_resize(&translated_dest->constants, constantsSize); - - if (fread(translated_dest->constants.data, 1, constantsSize, bytecode_file) != - constantsSize) { - goto FAILED; - } - - translated_dest->constants.size = constantsSize; - - darray_resize(&translated_dest->bytecode, bytecodeSize); - - if (fread(translated_dest->bytecode.data, 1, bytecodeSize, bytecode_file) != - bytecodeSize) { - goto FAILED; - } - - translated_dest->bytecode.size = bytecodeSize; - - fprintf(stderr, "cache exists and is valid, so will be used.\n"); - fclose(bytecode_file); - return 0; -FAILED: - fprintf(stderr, "cache is invalid... compiling from source.\n"); - fclose(bytecode_file); - return 1; -} - -Translated load_argon_file(char *path, ArErr *err) { - clock_t start, end; - clock_t beginning = clock(); - double time_spent, total_time_spent = 0; - - const char *basename_ptr; - size_t basename_length; - cwk_path_get_basename(path, &basename_ptr, &basename_length); - - if (!basename_ptr) { - *err = create_err(0, 0, 0, NULL, "Path Error", "path has no basename '%s'", - path); - return (Translated){}; - } - - char basename[FILENAME_MAX]; - memcpy(basename, basename_ptr, basename_length); - - size_t parent_directory_length; - cwk_path_get_dirname(path, &parent_directory_length); - - char parent_directory[FILENAME_MAX]; - memcpy(parent_directory, path, parent_directory_length); - 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)); - - 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)); - - FILE *file = fopen(path, "r"); - if (!file) { - *err = create_err(0, 0, 0, NULL, "File Error", "Unable to open file '%s'", - path); - return (Translated){}; - } - - XXH3_state_t *hash_state = XXH3_createState(); - XXH3_64bits_reset(hash_state); - - char buffer[8192]; - size_t bytes; - while ((bytes = fread(buffer, 1, sizeof(buffer), file)) > 0) { - XXH3_64bits_update(hash_state, buffer, bytes); - } - rewind(file); - uint64_t hash = XXH3_64bits_digest(hash_state); - XXH3_freeState(hash_state); - - Translated translated; - - if (load_cache(&translated, cache_file_path, hash, path) != 0) { - - DArray tokens; - darray_init(&tokens, sizeof(Token)); - - LexerState state = {path, file, 0, 0, &tokens}; - start = clock(); - *err = lexer(state); - if (err->exists) { - darray_free(&tokens, free_token); - return (Translated){}; - } - end = clock(); - time_spent = (double)(end - start) / CLOCKS_PER_SEC; - fprintf(stderr, "Lexer time taken: %f seconds\n", time_spent); - fclose(state.file); - - DArray ast; - - darray_init(&ast, sizeof(ParsedValue)); - - start = clock(); - *err = parser(path, &ast, &tokens, false); - darray_free(&tokens, free_token); - if (err->exists) { - darray_free(&ast, free_parsed); - return (Translated){}; - } - end = clock(); - time_spent = (double)(end - start) / CLOCKS_PER_SEC; - fprintf(stderr, "Parser time taken: %f seconds\n", time_spent); - - start = clock(); - - translated = init_translator(path); - *err = translate(&translated, &ast); - darray_free(&ast, free_parsed); - if (err->exists) { - darray_free(&translated.bytecode, NULL); - free(translated.constants.data); - hashmap_free(translated.constants.hashmap, NULL); - return (Translated){}; - } - end = clock(); - time_spent = (double)(end - start) / CLOCKS_PER_SEC; - fprintf(stderr, "Translation time taken: %f seconds\n", time_spent); -#if defined(__linux__) - malloc_trim(0); -#endif - - ensure_dir_exists(cache_folder_path); - - file = fopen(cache_file_path, "wb"); - - uint64_t constantsSize = translated.constants.size; - uint64_t bytecodeSize = translated.bytecode.size; - - uint32_t version_number_htole32ed = htole32(version_number); - uint64_t net_hash = htole64(hash); - constantsSize = htole64(constantsSize); - bytecodeSize = htole64(bytecodeSize); - - XXH64_state_t *hash_state = XXH64_createState(); - XXH64_reset(hash_state, 0); - - write_and_hash(file, hash_state, &FILE_IDENTIFIER, sizeof(char), - strlen(FILE_IDENTIFIER)); - write_and_hash(file, hash_state, &version_number_htole32ed, - sizeof(uint32_t), 1); - write_and_hash(file, hash_state, &net_hash, sizeof(net_hash), 1); - write_and_hash(file, hash_state, &translated.registerCount, sizeof(uint8_t), - 1); - write_and_hash(file, hash_state, &constantsSize, sizeof(uint64_t), 1); - write_and_hash(file, hash_state, &bytecodeSize, sizeof(uint64_t), 1); - write_and_hash(file, hash_state, translated.constants.data, 1, - translated.constants.size); - write_and_hash(file, hash_state, translated.bytecode.data, - translated.bytecode.element_size, translated.bytecode.size); - - // Finalize the hash - uint64_t file_hash = XXH64_digest(hash_state); - XXH64_freeState(hash_state); - - // Convert to little-endian before writing if needed - uint64_t file_hash_le = htole64(file_hash); - fwrite(&file_hash_le, sizeof(file_hash_le), 1, file); - - fclose(file); - } - hashmap_free(translated.constants.hashmap, NULL); - Translated gc_translated = { - translated.registerCount, translated.registerAssignment, NULL, {}, {}, - translated.path}; - gc_translated.bytecode.data = ar_alloc_atomic(translated.bytecode.capacity); - memcpy(gc_translated.bytecode.data, translated.bytecode.data, - translated.bytecode.capacity); - gc_translated.bytecode.element_size = translated.bytecode.element_size; - gc_translated.bytecode.size = translated.bytecode.size; - gc_translated.bytecode.resizable = false; - gc_translated.bytecode.capacity = - translated.bytecode.size * translated.bytecode.element_size; - gc_translated.constants.data = ar_alloc_atomic(translated.constants.capacity); - memcpy(gc_translated.constants.data, translated.constants.data, - translated.constants.capacity); - gc_translated.constants.size = translated.constants.size; - gc_translated.constants.capacity =translated.constants.capacity; - free(translated.bytecode.data); - free(translated.constants.data); - total_time_spent = (double)(clock() - beginning) / CLOCKS_PER_SEC; - fprintf(stderr, "total time taken loading file (%s): %f seconds\n", path, - total_time_spent); - return gc_translated; -} - int main(int argc, char *argv[]) { setlocale(LC_ALL, ""); ar_memory_init(); generate_siphash_key(siphash_key); - init_built_in_field_hashes(); bootstrap_types(); bootstrap_globals(); if (argc <= 1) return shell(); - char *CWD = get_current_directory(); + CWD = get_current_directory(); char *path_non_absolute = argv[1]; - char path[FILENAME_MAX]; - cwk_path_get_absolute(CWD, path_non_absolute, path, sizeof(path)); - free(CWD); ArErr err = no_err; - Translated translated = load_argon_file(path, &err); + ar_import(CWD, path_non_absolute, &err); if (err.exists) { output_err(err); return 1; } - clock_t start = clock(), end; - RuntimeState state = init_runtime_state(translated, path); - Stack *main_scope = create_scope(Global_Scope, true); - runtime(translated, state, main_scope, &err); - - end = clock(); - double time_spent = (double)(end - start) / CLOCKS_PER_SEC; - fprintf(stderr, "Execution time taken: %f seconds\n", time_spent); - + free(CWD); if (runtime_hash_table) hashmap_free(runtime_hash_table, NULL); if (err.exists) { diff --git a/src/runtime/access/access.c b/src/runtime/access/access.c deleted file mode 100644 index 3e72a37..0000000 --- a/src/runtime/access/access.c +++ /dev/null @@ -1,44 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2025 William Bell - * - * SPDX-License-Identifier: GPL-3.0-or-later - */ -#include "access.h" -#include - -ArgonObject *ARGON_TYPE_TYPE___get_attr__(size_t argc, ArgonObject **argv, - ArErr *err, RuntimeState *state) { - (void)state; - if (argc != 3) { - *err = create_err(0, 0, 0, "", "Runtime Error", - "__get_attr__ expects 3 arguments, got %" PRIu64); - return ARGON_NULL; - } - ArgonObject *to_access = argv[0]; - bool check_field = argv[1] == ARGON_TRUE; - if (check_field) { - ArgonObject *access = argv[2]; - uint64_t hash; - if (access->value.as_str.hash_computed) { - hash = access->value.as_str.hash; - } else { - hash = - runtime_hash(access->value.as_str.data, access->value.as_str.length, - access->value.as_str.prehash); - access->value.as_str.hash = hash; - access->value.as_str.hash_computed = true; - } - ArgonObject *value = get_field_l(to_access, access->value.as_str.data, hash, - access->value.as_str.length, true, false); - if (value) - return value; - ArgonObject *name = get_builtin_field_for_class( - get_builtin_field(to_access, __class__, false, false), __name__, - to_access); - *err = create_err( - 0, 0, 0, "", "Runtime Error", "'%.*s' object has no attribute '%.*s'", - (int)name->value.as_str.length, name->value.as_str.data, - (int)access->value.as_str.length, access->value.as_str.data); - } - return ARGON_NULL; -} \ No newline at end of file diff --git a/src/runtime/access/access.h b/src/runtime/access/access.h deleted file mode 100644 index 959ed53..0000000 --- a/src/runtime/access/access.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2025 William Bell - * - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -#ifndef runtime_access_H -#define runtime_access_H -#include "../objects/literals/literals.h" -#include "../objects/object.h" -#include "../runtime.h" -#include - -ArgonObject *ARGON_TYPE_TYPE___get_attr__(size_t argc, ArgonObject **argv, - ArErr *err, RuntimeState *state); - -#endif // runtime_access_H \ No newline at end of file diff --git a/src/runtime/call/call.c b/src/runtime/call/call.c index 5a028ed..bf3ea4f 100644 --- a/src/runtime/call/call.c +++ b/src/runtime/call/call.c @@ -85,7 +85,7 @@ void run_call(ArgonObject *original_object, size_t argc, ArgonObject **argv, if (object->type != TYPE_FUNCTION && object->type != TYPE_NATIVE_FUNCTION && object->type != TYPE_METHOD) { ArgonObject *call_method = get_builtin_field_for_class( - get_builtin_field(object, __class__, false, false), __call__, + get_builtin_field(object, __class__), __call__, original_object); if (call_method) { object = call_method; @@ -93,7 +93,7 @@ void run_call(ArgonObject *original_object, size_t argc, ArgonObject **argv, } if (object->type == TYPE_METHOD) { ArgonObject *binding_object = - get_builtin_field(object, __binding__, false, false); + get_builtin_field(object, __binding__); if (binding_object) { ArgonObject **new_call_args = ar_alloc(sizeof(ArgonObject *) * (argc + 1)); @@ -105,14 +105,14 @@ void run_call(ArgonObject *original_object, size_t argc, ArgonObject **argv, argc++; } ArgonObject *function_object = - get_builtin_field(object, __function__, false, false); + get_builtin_field(object, __function__); if (function_object) object = function_object; } if (object->type == TYPE_FUNCTION) { if (argc != object->value.argon_fn.number_of_parameters) { ArgonObject *type_object_name = get_builtin_field_for_class( - get_builtin_field(object, __class__, false, false), __name__, + get_builtin_field(object, __class__), __name__, original_object); ArgonObject *object_name = get_builtin_field_for_class(object, __name__, original_object); @@ -196,7 +196,7 @@ void run_call(ArgonObject *original_object, size_t argc, ArgonObject **argv, return; } ArgonObject *type_object_name = get_builtin_field_for_class( - get_builtin_field(original_object, __class__, false, false), __name__, + get_builtin_field(original_object, __class__), __name__, original_object); *err = create_err(state->source_location.line, state->source_location.column, state->source_location.length, state->path, "Type Error", diff --git a/src/runtime/internals/hashmap/hashmap.c b/src/runtime/internals/hashmap/hashmap.c index 8312bcb..b637c82 100644 --- a/src/runtime/internals/hashmap/hashmap.c +++ b/src/runtime/internals/hashmap/hashmap.c @@ -27,7 +27,7 @@ struct hashmap_GC *createHashmap_GC() { } void clear_hashmap_GC(struct hashmap_GC *t) { - if (!t->hashmap_count) + if (!t->count) return; t->order = 1; t->count = 0; diff --git a/src/runtime/objects/functions/functions.c b/src/runtime/objects/functions/functions.c index 365b48e..f5bc757 100644 --- a/src/runtime/objects/functions/functions.c +++ b/src/runtime/objects/functions/functions.c @@ -15,8 +15,7 @@ ArgonObject *ARGON_FUNCTION_TYPE = NULL; ArgonObject *create_argon_native_function(char *name, native_fn native_fn) { - ArgonObject *object = new_object(); - add_builtin_field(object, __class__, ARGON_FUNCTION_TYPE); + ArgonObject *object = new_instance(ARGON_FUNCTION_TYPE); object->type = TYPE_NATIVE_FUNCTION; add_builtin_field(object, __name__, new_string_object(name, strlen(name), 0, 0)); @@ -26,8 +25,7 @@ ArgonObject *create_argon_native_function(char *name, native_fn native_fn) { void load_argon_function(Translated *translated, RuntimeState *state, struct Stack *stack) { - ArgonObject *object = new_object(); - add_builtin_field(object, __class__, ARGON_FUNCTION_TYPE); + ArgonObject *object = new_instance(ARGON_FUNCTION_TYPE); object->type = TYPE_FUNCTION; uint64_t offset = pop_bytecode(translated, state); uint64_t length = pop_bytecode(translated, state); diff --git a/src/runtime/objects/number/number.c b/src/runtime/objects/number/number.c index 7a87f75..2d6c687 100644 --- a/src/runtime/objects/number/number.c +++ b/src/runtime/objects/number/number.c @@ -37,7 +37,7 @@ ArgonObject *ARGON_NUMBER_TYPE___new__(size_t argc, ArgonObject **argv, self->type = TYPE_STRING; ArgonObject *boolean_convert_method = get_builtin_field_for_class( - get_builtin_field(object, __class__, false, false), __number__, object); + get_builtin_field(object, __class__), __number__, object); if (boolean_convert_method) { ArgonObject *boolean_object = argon_call(boolean_convert_method, 0, NULL, err, state); @@ -46,7 +46,7 @@ ArgonObject *ARGON_NUMBER_TYPE___new__(size_t argc, ArgonObject **argv, return boolean_object; } ArgonObject *type_name = get_builtin_field_for_class( - get_builtin_field(object, __class__, false, false), __name__, object); + get_builtin_field(object, __class__), __name__, object); *err = create_err( 0, 0, 0, "", "Runtime Error", "cannot convert type '%.*s' to number", type_name->value.as_str.length, type_name->value.as_str.data); @@ -85,7 +85,7 @@ ArgonObject *ARGON_NUMBER_TYPE___add__(size_t argc, ArgonObject **argv, } if (argv[1]->type != TYPE_NUMBER) { ArgonObject *type_name = get_builtin_field_for_class( - get_builtin_field(argv[1], __class__, false, false), __name__, argv[1]); + get_builtin_field(argv[1], __class__), __name__, argv[1]); *err = create_err( 0, 0, 0, "", "Runtime Error", "__add__ cannot perform addition between a number and %.*s", @@ -148,7 +148,7 @@ ArgonObject *ARGON_NUMBER_TYPE___subtract__(size_t argc, ArgonObject **argv, } if (argv[1]->type != TYPE_NUMBER) { ArgonObject *type_name = get_builtin_field_for_class( - get_builtin_field(argv[1], __class__, false, false), __name__, argv[1]); + get_builtin_field(argv[1], __class__), __name__, argv[1]); *err = create_err( 0, 0, 0, "", "Runtime Error", "__subtract__ cannot perform subtraction between number and %.*s", @@ -213,7 +213,7 @@ ArgonObject *ARGON_NUMBER_TYPE___multiply__(size_t argc, ArgonObject **argv, } if (argv[1]->type != TYPE_NUMBER) { ArgonObject *type_name = get_builtin_field_for_class( - get_builtin_field(argv[1], __class__, false, false), __name__, argv[1]); + get_builtin_field(argv[1], __class__), __name__, argv[1]); *err = create_err( 0, 0, 0, "", "Runtime Error", "__multiply__ cannot perform multiplication between number and %.*s", @@ -278,7 +278,7 @@ ArgonObject *ARGON_NUMBER_TYPE___division__(size_t argc, ArgonObject **argv, } if (argv[1]->type != TYPE_NUMBER) { ArgonObject *type_name = get_builtin_field_for_class( - get_builtin_field(argv[1], __class__, false, false), __name__, argv[1]); + get_builtin_field(argv[1], __class__), __name__, argv[1]); *err = create_err( 0, 0, 0, "", "Runtime Error", "__division__ cannot perform division between number and %.*s", @@ -288,6 +288,13 @@ ArgonObject *ARGON_NUMBER_TYPE___division__(size_t argc, ArgonObject **argv, if (argv[0]->value.as_number.is_int64 && argv[1]->value.as_number.is_int64) { int64_t a = argv[0]->value.as_number.n.i64; int64_t b = argv[1]->value.as_number.n.i64; + if (!b) { + *err = + create_err(state->source_location.line, state->source_location.column, + state->source_location.length, state->path, + "Zero Division Error", "division by zero"); + return NULL; + } return new_number_object_from_num_and_den(a, b); } else if (!argv[0]->value.as_number.is_int64 && !argv[1]->value.as_number.is_int64) { @@ -307,6 +314,13 @@ ArgonObject *ARGON_NUMBER_TYPE___division__(size_t argc, ArgonObject **argv, mpq_set(b_GMP, *argv[1]->value.as_number.n.mpq); } else { mpq_set(a_GMP, *argv[0]->value.as_number.n.mpq); + if (!argv[1]->value.as_number.n.i64) { + *err = create_err(state->source_location.line, + state->source_location.column, + state->source_location.length, state->path, + "Zero Division Error", "division by zero"); + return NULL; + } mpq_set_si(b_GMP, argv[1]->value.as_number.n.i64, 1); } mpq_div(a_GMP, a_GMP, b_GMP); @@ -492,7 +506,7 @@ void init_small_ints() { } void create_ARGON_NUMBER_TYPE() { - ARGON_NUMBER_TYPE = new_object(); + ARGON_NUMBER_TYPE = new_class(); add_builtin_field(ARGON_NUMBER_TYPE, __name__, new_string_object_null_terminated("number")); add_builtin_field( @@ -600,8 +614,7 @@ ArgonObject *new_number_object(mpq_t number) { if (is_int64 && i64 >= small_ints_min && i64 <= small_ints_max) { return &small_ints[i64 - small_ints_min]; } - ArgonObject *object = new_object(); - add_builtin_field(object, __class__, ARGON_NUMBER_TYPE); + ArgonObject *object = new_instance(ARGON_NUMBER_TYPE); object->type = TYPE_NUMBER; object->value.as_number.n.i64 = i64; object->value.as_number.is_int64 = is_int64; @@ -618,8 +631,7 @@ ArgonObject *new_number_object_from_num_and_den(int64_t n, uint64_t d) { if (d == 1 && n >= small_ints_min && n <= small_ints_max) { return &small_ints[n - small_ints_min]; } - ArgonObject *object = new_object(); - add_builtin_field(object, __class__, ARGON_NUMBER_TYPE); + ArgonObject *object = new_instance(ARGON_NUMBER_TYPE); object->type = TYPE_NUMBER; if (d == 1) { object->value.as_number.is_int64 = true; @@ -641,8 +653,7 @@ ArgonObject *new_number_object_from_int64(int64_t i64) { if (i64 >= small_ints_min && i64 <= small_ints_max) { return &small_ints[i64 - small_ints_min]; } - ArgonObject *object = new_object(); - add_builtin_field(object, __class__, ARGON_NUMBER_TYPE); + ArgonObject *object = new_instance(ARGON_NUMBER_TYPE); object->type = TYPE_NUMBER; object->value.as_number.is_int64 = true; object->value.as_number.n.i64 = i64; @@ -656,8 +667,7 @@ ArgonObject *new_number_object_from_double(double d) { if (is_int64 && i64 >= small_ints_min && i64 <= small_ints_max) { return &small_ints[i64 - small_ints_min]; } - ArgonObject *object = new_object(); - add_builtin_field(object, __class__, ARGON_NUMBER_TYPE); + ArgonObject *object = new_instance(ARGON_NUMBER_TYPE); object->type = TYPE_NUMBER; object->value.as_number.n.i64 = i64; object->value.as_number.is_int64 = is_int64; diff --git a/src/runtime/objects/object.c b/src/runtime/objects/object.c index b0bd36b..0e2df97 100644 --- a/src/runtime/objects/object.c +++ b/src/runtime/objects/object.c @@ -5,7 +5,6 @@ */ #include "object.h" -#include "../../hash_data/hash_data.h" #include "../../memory.h" #include "type/type.h" #include @@ -20,35 +19,48 @@ const char *built_in_field_names[BUILT_IN_FIELDS_COUNT] = { "__string__", "__subtract__", "__multiply__", "__division__", "__new__", "__init__", "__boolean__", "__get_attr__", "__binding__", "__function__", "address", "__call__", - "__number__", "log", "length"}; + "__number__", "log", "length", "__getattribute__"}; -uint64_t built_in_field_hashes[BUILT_IN_FIELDS_COUNT]; ArgonObject *new_object() { ArgonObject *object = ar_alloc(sizeof(ArgonObject)); + memset(object->Builtin_slots, 0, sizeof(object->Builtin_slots)); object->type = TYPE_OBJECT; - object->dict = createHashmap_GC(); - add_builtin_field(object, __class__, ARGON_TYPE_TYPE); - add_builtin_field(object, __base__, BASE_CLASS); + object->dict = NULL; object->as_bool = true; return object; } -void init_built_in_field_hashes() { - for (int i = 0; i < BUILT_IN_FIELDS_COUNT; i++) { - built_in_field_hashes[i] = siphash64_bytes( - built_in_field_names[i], strlen(built_in_field_names[i]), siphash_key); - } +ArgonObject *new_class() { + ArgonObject *object = new_object(); + add_builtin_field(object, __class__, ARGON_TYPE_TYPE); + add_builtin_field(object, __base__, BASE_CLASS); + return object; } -void add_builtin_field(ArgonObject *target, built_in_fields field, - ArgonObject *object) { - hashmap_insert_GC(target->dict, built_in_field_hashes[field], - (char *)built_in_field_names[field], object, 0); +ArgonObject *new_instance(ArgonObject *of) { + ArgonObject *object = new_object(); + add_builtin_field(object, __class__, of); + return object; +} + +inline void add_builtin_field(ArgonObject *target, built_in_fields field, + ArgonObject *object) { + target->Builtin_slots[field] = object; + // hashmap_insert_GC(target->dict, built_in_field_hashes[field], + // (char *)built_in_field_names[field], object, 0); } void add_field(ArgonObject *target, char *name, uint64_t hash, ArgonObject *object) { + for (size_t i = 0; i < BUILT_IN_FIELDS_COUNT; i++) { + if (strcmp(name, built_in_field_names[i])) { + target->Builtin_slots[i] = object; + return; + } + } + if (!object->dict) + object->dict = createHashmap_GC(); hashmap_insert_GC(target->dict, hash, name, object, 0); } @@ -60,7 +72,7 @@ ArgonObject *bind_object_to_function(ArgonObject *object, add_builtin_field(bound_method_wrapper, __binding__, object); add_builtin_field(bound_method_wrapper, __function__, function); ArgonObject *function_name = - get_builtin_field(function, __name__, false, false); + get_builtin_field(function, __name__); if (function_name) add_builtin_field(bound_method_wrapper, __name__, function_name); return bound_method_wrapper; @@ -79,7 +91,7 @@ ArgonObject *get_field_for_class_l(ArgonObject *target, char *name, } return object; } - target = get_builtin_field(target, __base__, false, false); + target = get_builtin_field(target, __base__); } return NULL; } @@ -87,6 +99,13 @@ ArgonObject *get_field_for_class_l(ArgonObject *target, char *name, ArgonObject *get_field_l(ArgonObject *target, char *name, uint64_t hash, size_t length, bool recursive, bool disable_method_wrapper) { + for (size_t i = 0; i < BUILT_IN_FIELDS_COUNT; i++) { + if (strcmp(name, built_in_field_names[i])) { + return target->Builtin_slots[i]; + } + } + if (!target->dict) + return NULL; ArgonObject *object = hashmap_lookup_GC(target->dict, hash); if (!recursive || object) return object; @@ -94,38 +113,35 @@ ArgonObject *get_field_l(ArgonObject *target, char *name, uint64_t hash, if (disable_method_wrapper) binding = NULL; return get_field_for_class_l( - hashmap_lookup_GC(target->dict, built_in_field_hashes[__class__]), name, + get_builtin_field(target, __class__), name, hash, length, binding); } ArgonObject *get_builtin_field_for_class(ArgonObject *target, built_in_fields field, ArgonObject *binding_object) { - while (target) { - ArgonObject *object = get_builtin_field(target, field, false, false); - if (object) { - if ((object->type == TYPE_FUNCTION || - object->type == TYPE_NATIVE_FUNCTION) && - binding_object) { - object = bind_object_to_function(binding_object, object); - } - return object; + ArgonObject *object = get_builtin_field(target, field); + if (object) { + if ((object->type == TYPE_FUNCTION || + object->type == TYPE_NATIVE_FUNCTION) && + binding_object) { + object = bind_object_to_function(binding_object, object); } - target = get_builtin_field(target, __base__, false, false); + return object; } return NULL; } -ArgonObject *get_builtin_field(ArgonObject *target, built_in_fields field, - bool recursive, bool disable_method_wrapper) { - ArgonObject *object = - hashmap_lookup_GC(target->dict, built_in_field_hashes[field]); - if (!recursive || object) - return object; - ArgonObject *binding = target; - if (disable_method_wrapper) - binding = NULL; - return get_builtin_field_for_class( - hashmap_lookup_GC(target->dict, built_in_field_hashes[__class__]), field, - binding); +ArgonObject *get_builtin_field(ArgonObject *target, built_in_fields field) { + return target->Builtin_slots[field]; + // ArgonObject *object = + // hashmap_lookup_GC(target->dict, built_in_field_hashes[field]); + // if (!recursive || object) + // return object; + // ArgonObject *binding = target; + // if (disable_method_wrapper) + // binding = NULL; + // return get_builtin_field_for_class( + // hashmap_lookup_GC(target->dict, built_in_field_hashes[__class__]), + // field, binding); } \ No newline at end of file diff --git a/src/runtime/objects/object.h b/src/runtime/objects/object.h index 1e6e1cc..2dfe02b 100644 --- a/src/runtime/objects/object.h +++ b/src/runtime/objects/object.h @@ -11,35 +11,12 @@ #include #include -typedef enum { - __base__, - __class__, - __name__, - __add__, - __string__, - __subtract__, - __multiply__, - __division__, - __new__, - __init__, - __boolean__, - __get_attr__, - __binding__, - __function__, - field__address, - __call__, - __number__, - field_log, - field_length, - BUILT_IN_FIELDS_COUNT -} built_in_fields; - -void init_built_in_field_hashes(); - extern ArgonObject *BASE_CLASS; typedef struct ArgonObject ArgonObject; -ArgonObject *new_object(); + +ArgonObject *new_class(); +ArgonObject *new_instance(ArgonObject * of); void add_builtin_field(ArgonObject *target, built_in_fields field, ArgonObject *object); @@ -62,7 +39,6 @@ ArgonObject *get_builtin_field_for_class(ArgonObject *target, built_in_fields field, ArgonObject *binding_object); -ArgonObject *get_builtin_field(ArgonObject *target, built_in_fields field, - bool recursive, bool disable_method_wrapper); +ArgonObject *get_builtin_field(ArgonObject *target, built_in_fields field); #endif // OBJECT_H \ No newline at end of file diff --git a/src/runtime/objects/string/string.c b/src/runtime/objects/string/string.c index e56edbd..5b1b889 100644 --- a/src/runtime/objects/string/string.c +++ b/src/runtime/objects/string/string.c @@ -15,8 +15,7 @@ ArgonObject *ARGON_STRING_TYPE = NULL; ArgonObject *new_string_object_without_memcpy(char *data, size_t length, uint64_t prehash, uint64_t hash) { - ArgonObject *object = new_object(); - add_builtin_field(object, __class__, ARGON_STRING_TYPE); + ArgonObject *object = new_instance(ARGON_STRING_TYPE); add_builtin_field(object, field_length, new_number_object_from_int64(length)); object->type = TYPE_STRING; diff --git a/src/runtime/objects/term/term.c b/src/runtime/objects/term/term.c index 8666b2c..75fd6e5 100644 --- a/src/runtime/objects/term/term.c +++ b/src/runtime/objects/term/term.c @@ -14,7 +14,7 @@ ArgonObject *term_log(size_t argc, ArgonObject **argv, ArErr *err, if (i != 0) printf(" "); ArgonObject *string_convert_method = get_builtin_field_for_class( - get_builtin_field(argv[i], __class__, false, false), __string__, argv[i]); + get_builtin_field(argv[i], __class__), __string__, argv[i]); if (string_convert_method) { ArgonObject *string_object = diff --git a/src/runtime/runtime.c b/src/runtime/runtime.c index b80bb87..e985f1e 100644 --- a/src/runtime/runtime.c +++ b/src/runtime/runtime.c @@ -9,7 +9,6 @@ #include "../hash_data/hash_data.h" #include "../parser/number/number.h" #include "../translator/translator.h" -#include "access/access.h" #include "assignment/assignment.h" #include "call/call.h" #include "declaration/declaration.h" @@ -34,7 +33,7 @@ ArgonObject *ARGON_METHOD_TYPE; Stack *Global_Scope = NULL; -ArgonObject *ACCESS_FUNCTION; +ArgonObject *GETATTRIBUTE_FUNCTION; ArgonObject *ADDITION_FUNCTION; ArgonObject *SUBTRACTION_FUNCTION; ArgonObject *MULTIPLY_FUNCTION; @@ -43,6 +42,53 @@ ArgonObject *POWER_FUNCTION; ArgonObject *MODULO_FUNCTION; ArgonObject *FLOORDIVISION_FUNCTION; +ArgonObject *BASE_CLASS___getattribute__(size_t argc, ArgonObject **argv, + ArErr *err, RuntimeState *state) { + (void)state; + if (argc != 3) { + *err = create_err(0, 0, 0, "", "Runtime Error", + "__getattribute__ expects 3 arguments, got %" PRIu64); + return ARGON_NULL; + } + ArgonObject *to_access = argv[0]; + bool check_field = argv[1] == ARGON_TRUE; + if (check_field) { + ArgonObject *access = argv[2]; + uint64_t hash; + if (access->value.as_str.hash_computed) { + hash = access->value.as_str.hash; + } else { + hash = + runtime_hash(access->value.as_str.data, access->value.as_str.length, + access->value.as_str.prehash); + access->value.as_str.hash = hash; + access->value.as_str.hash_computed = true; + } + ArgonObject *value = get_field_l(to_access, access->value.as_str.data, hash, + access->value.as_str.length, true, false); + if (value) + return value; + ArgonObject *cls__get_attr__ = get_builtin_field_for_class( + get_builtin_field(to_access, __class__), __get_attr__, + to_access); + if (cls__get_attr__) { + value = argon_call(cls__get_attr__, 1, (ArgonObject *[]){access}, err, state); + if (err->exists) { + return ARGON_NULL; + } + return value; + } + ArgonObject *name = get_builtin_field_for_class( + get_builtin_field(to_access, __class__), __name__, + to_access); + *err = create_err( + 0, 0, 0, "", "Runtime Error", "'%.*s' object has no attribute '%.*s'", + (int)name->value.as_str.length, name->value.as_str.data, + (int)access->value.as_str.length, access->value.as_str.data); + } + return ARGON_NULL; +} + ArgonObject *ARGON_ADDITION_FUNCTION(size_t argc, ArgonObject **argv, ArErr *err, RuntimeState *state) { if (argc < 1) { @@ -53,10 +99,10 @@ ArgonObject *ARGON_ADDITION_FUNCTION(size_t argc, ArgonObject **argv, ArgonObject *output = argv[0]; for (size_t i = 1; i < argc; i++) { ArgonObject *object__add__ = get_builtin_field_for_class( - get_builtin_field(output, __class__, false, false), __add__, output); + get_builtin_field(output, __class__), __add__, output); if (!object__add__) { ArgonObject *cls___name__ = - get_builtin_field(output, __name__, true, false); + get_builtin_field(output, __name__); *err = create_err(0, 0, 0, "", "Runtime Error", "Object '%.*s' is missing __add__ method", (int)cls___name__->value.as_str.length, @@ -80,11 +126,11 @@ ArgonObject *ARGON_SUBTRACTION_FUNCTION(size_t argc, ArgonObject **argv, ArgonObject *output = argv[0]; for (size_t i = 1; i < argc; i++) { ArgonObject *function__subtract__ = get_builtin_field_for_class( - get_builtin_field(output, __class__, false, false), __subtract__, + get_builtin_field(output, __class__), __subtract__, output); if (!function__subtract__) { ArgonObject *cls___name__ = - get_builtin_field(output, __name__, true, false); + get_builtin_field(output, __name__); *err = create_err(0, 0, 0, "", "Runtime Error", "Object '%.*s' is missing __subtract__ method", (int)cls___name__->value.as_str.length, @@ -108,11 +154,11 @@ ArgonObject *ARGON_MULTIPLY_FUNCTION(size_t argc, ArgonObject **argv, ArgonObject *output = argv[0]; for (size_t i = 1; i < argc; i++) { ArgonObject *function__multiply__ = get_builtin_field_for_class( - get_builtin_field(output, __class__, false, false), __multiply__, + get_builtin_field(output, __class__), __multiply__, output); if (!function__multiply__) { ArgonObject *cls___name__ = - get_builtin_field(output, __name__, true, false); + get_builtin_field(output, __name__); *err = create_err(0, 0, 0, "", "Runtime Error", "Object '%.*s' is missing __multiply__ method", (int)cls___name__->value.as_str.length, @@ -136,11 +182,11 @@ ArgonObject *ARGON_DIVISION_FUNCTION(size_t argc, ArgonObject **argv, ArgonObject *output = argv[0]; for (size_t i = 1; i < argc; i++) { ArgonObject *function__division__ = get_builtin_field_for_class( - get_builtin_field(output, __class__, false, false), __division__, + get_builtin_field(output, __class__), __division__, output); if (!function__division__) { ArgonObject *cls___name__ = - get_builtin_field(output, __name__, true, false); + get_builtin_field(output, __name__); *err = create_err(0, 0, 0, "", "Runtime Error", "Object '%.*s' is missing __division__ method", (int)cls___name__->value.as_str.length, @@ -164,7 +210,7 @@ ArgonObject *ARGON_TYPE_TYPE___call__(size_t argc, ArgonObject **argv, } ArgonObject *cls = argv[0]; if (cls == ARGON_TYPE_TYPE && argc == 2) { - ArgonObject *cls_class = get_builtin_field(argv[1], __class__, true, false); + ArgonObject *cls_class = get_builtin_field(argv[1], __class__); if (cls_class) return cls_class; return ARGON_NULL; @@ -173,7 +219,7 @@ ArgonObject *ARGON_TYPE_TYPE___call__(size_t argc, ArgonObject **argv, get_builtin_field_for_class(argv[0], __new__, NULL); if (!cls___new__) { ArgonObject *cls___name__ = - get_builtin_field(argv[0], __name__, true, false); + get_builtin_field(argv[0], __name__); *err = create_err( 0, 0, 0, "", "Runtime Error", "Object '%.*s' is missing __new__ method, so cannot be initialised", @@ -194,7 +240,7 @@ ArgonObject *ARGON_TYPE_TYPE___call__(size_t argc, ArgonObject **argv, get_builtin_field_for_class(argv[0], __init__, new_object); if (!cls___init__) { ArgonObject *cls___name__ = - get_builtin_field(argv[0], __name__, true, false); + get_builtin_field(argv[0], __name__); *err = create_err( 0, 0, 0, "", "Runtime Error", "Object '%.*s' is missing __init__ method, so cannot be initialised", @@ -232,8 +278,7 @@ ArgonObject *BASE_CLASS___new__(size_t argc, ArgonObject **argv, ArErr *err, "__new__ expects at least 1 argument, got %" PRIu64, argc); return ARGON_NULL; } - ArgonObject *new_obj = new_object(); - add_builtin_field(new_obj, __class__, argv[0]); + ArgonObject *new_obj = new_instance(argv[0]); return new_obj; } @@ -259,7 +304,7 @@ ArgonObject *BASE_CLASS___string__(size_t argc, ArgonObject **argv, ArErr *err, ArgonObject *object_name = get_builtin_field_for_class(argv[0], __name__, NULL); ArgonObject *class_name = get_builtin_field_for_class( - get_builtin_field(argv[0], __class__, false, false), __name__, NULL); + get_builtin_field(argv[0], __class__), __name__, NULL); char buffer[100]; if (class_name && object_name) @@ -298,7 +343,7 @@ ArgonObject *ARGON_STRING_TYPE___init__(size_t argc, ArgonObject **argv, self->value.as_str.length = 0; self->type = TYPE_STRING; ArgonObject *string_convert_method = get_builtin_field_for_class( - get_builtin_field(object, __class__, false, false), __string__, object); + get_builtin_field(object, __class__), __string__, object); if (string_convert_method) { ArgonObject *string_object = argon_call(string_convert_method, 0, NULL, err, state); @@ -325,7 +370,7 @@ ArgonObject *ARGON_BOOL_TYPE___new__(size_t argc, ArgonObject **argv, self->type = TYPE_STRING; ArgonObject *boolean_convert_method = get_builtin_field_for_class( - get_builtin_field(object, __class__, false, false), __boolean__, object); + get_builtin_field(object, __class__), __boolean__, object); if (boolean_convert_method) { ArgonObject *boolean_object = argon_call(boolean_convert_method, 0, NULL, err, state); @@ -334,7 +379,7 @@ ArgonObject *ARGON_BOOL_TYPE___new__(size_t argc, ArgonObject **argv, return boolean_object; } ArgonObject *type_name = get_builtin_field_for_class( - get_builtin_field(object, __class__, false, false), __name__, object); + get_builtin_field(object, __class__), __name__, object); *err = create_err( 0, 0, 0, "", "Runtime Error", "cannot convert type '%.*s' to bool", type_name->value.as_str.length, type_name->value.as_str.data); @@ -351,7 +396,7 @@ ArgonObject *ARGON_STRING_TYPE___add__(size_t argc, ArgonObject **argv, } if (argv[1]->type != TYPE_STRING) { ArgonObject *type_name = get_builtin_field_for_class( - get_builtin_field(argv[1], __class__, false, false), __name__, argv[1]); + get_builtin_field(argv[1], __class__), __name__, argv[1]); *err = create_err( 0, 0, 0, "", "Runtime Error", "__add__ cannot perform concatenation between a string and %.*s", @@ -476,32 +521,29 @@ ArgonObject *ARGON_NULL_TYPE___string__(size_t argc, ArgonObject **argv, } void bootstrap_types() { - BASE_CLASS = new_object(); - ARGON_TYPE_TYPE = new_object(); + BASE_CLASS = new_class(); + ARGON_TYPE_TYPE = new_class(); add_builtin_field(ARGON_TYPE_TYPE, __base__, BASE_CLASS); add_builtin_field(ARGON_TYPE_TYPE, __class__, ARGON_TYPE_TYPE); - ARGON_NULL_TYPE = new_object(); + ARGON_NULL_TYPE = new_class(); add_builtin_field(ARGON_NULL_TYPE, __base__, BASE_CLASS); - ARGON_NULL = new_object(); + ARGON_NULL = new_instance(ARGON_NULL_TYPE); ARGON_NULL->type = TYPE_NULL; - add_builtin_field(ARGON_NULL, __class__, ARGON_NULL_TYPE); ARGON_NULL->as_bool = false; - add_builtin_field(BASE_CLASS, __base__, NULL); + add_builtin_field(BASE_CLASS, __base__, ARGON_NULL); add_builtin_field(BASE_CLASS, __class__, ARGON_TYPE_TYPE); - ARGON_BOOL_TYPE = new_object(); + ARGON_BOOL_TYPE = new_class(); add_builtin_field(ARGON_BOOL_TYPE, __base__, BASE_CLASS); - ARGON_TRUE = new_object(); + ARGON_TRUE = new_instance(ARGON_BOOL_TYPE); ARGON_TRUE->type = TYPE_BOOL; - add_builtin_field(ARGON_TRUE, __class__, ARGON_BOOL_TYPE); - ARGON_FALSE = new_object(); + ARGON_FALSE = new_instance(ARGON_BOOL_TYPE); ARGON_FALSE->type = TYPE_BOOL; - add_builtin_field(ARGON_FALSE, __class__, ARGON_BOOL_TYPE); ARGON_NULL->as_bool = false; - ARGON_STRING_TYPE = new_object(); + ARGON_STRING_TYPE = new_class(); add_builtin_field(ARGON_STRING_TYPE, __base__, BASE_CLASS); add_builtin_field(ARGON_STRING_TYPE, __name__, @@ -515,12 +557,12 @@ void bootstrap_types() { add_builtin_field(ARGON_BOOL_TYPE, __name__, new_string_object_null_terminated("boolean")); - ARGON_FUNCTION_TYPE = new_object(); + ARGON_FUNCTION_TYPE = new_class(); add_builtin_field(ARGON_FUNCTION_TYPE, __base__, BASE_CLASS); add_builtin_field(ARGON_FUNCTION_TYPE, __name__, new_string_object_null_terminated("function")); - ARGON_METHOD_TYPE = new_object(); + ARGON_METHOD_TYPE = new_class(); add_builtin_field(ARGON_METHOD_TYPE, __base__, BASE_CLASS); add_builtin_field(ARGON_METHOD_TYPE, __name__, new_string_object_null_terminated("method")); @@ -580,8 +622,8 @@ void bootstrap_types() { add_builtin_field( ARGON_BOOL_TYPE, __number__, create_argon_native_function("__number__", ARGON_BOOL_TYPE___number__)); - ACCESS_FUNCTION = create_argon_native_function("__get_attr__", - ARGON_TYPE_TYPE___get_attr__); + GETATTRIBUTE_FUNCTION = create_argon_native_function("__get_attr__", + BASE_CLASS___getattribute__); ADDITION_FUNCTION = create_argon_native_function("add", ARGON_ADDITION_FUNCTION); SUBTRACTION_FUNCTION = @@ -590,7 +632,7 @@ void bootstrap_types() { create_argon_native_function("multiply", ARGON_MULTIPLY_FUNCTION); DIVISION_FUNCTION = create_argon_native_function("division", ARGON_DIVISION_FUNCTION); - add_builtin_field(BASE_CLASS, __get_attr__, ACCESS_FUNCTION); + add_builtin_field(BASE_CLASS, __getattribute__, GETATTRIBUTE_FUNCTION); } void add_to_scope(Stack *stack, char *name, ArgonObject *value) { @@ -611,7 +653,7 @@ void bootstrap_globals() { add_to_scope(Global_Scope, "multiply", MULTIPLY_FUNCTION); add_to_scope(Global_Scope, "division", DIVISION_FUNCTION); - ArgonObject *argon_term = new_object(); + ArgonObject *argon_term = new_class(); add_builtin_field(argon_term, __init__, ARGON_NULL); add_builtin_field(argon_term, field_log, create_argon_native_function("log", term_log)); @@ -723,7 +765,7 @@ void runtime(Translated _translated, RuntimeState _state, Stack *stack, [OP_COPY_TO_REGISTER] = &&DO_COPY_TO_REGISTER, [OP_ADDITION] = &&DO_ADDITION, [OP_SUBTRACTION] = &&DO_SUBTRACTION, - [OP_LOAD_ACCESS_FUNCTION] = &&DO_LOAD_ACCESS_FUNCTION, + [OP_LOAD_GETATTRIBUTE_FUNCTION] = &&DO_LOAD_GETATTRIBUTE_FUNCTION, [OP_MULTIPLICATION] = &&DO_MULTIPLICATION, [OP_DIVISION] = &&DO_DIVISION, [OP_NOT] = &&DO_NOT}; @@ -830,8 +872,16 @@ void runtime(Translated _translated, RuntimeState _state, Stack *stack, state->registers[0] = pop_byte(translated, state) ? ARGON_TRUE : ARGON_FALSE; continue; - DO_LOAD_ACCESS_FUNCTION: - state->registers[0] = ACCESS_FUNCTION; + DO_LOAD_GETATTRIBUTE_FUNCTION: + state->registers[0] = get_builtin_field_for_class( + get_builtin_field(state->registers[0], __class__), + __getattribute__, state->registers[0]); + if (!state->registers[0]) { + *err = create_err( + state->source_location.line, state->source_location.column, + state->source_location.length, state->path, "Runtime Error", + "unable to get __getattribute__ from objects class"); + } continue; DO_COPY_TO_REGISTER: { uint8_t from_register = pop_byte(translated, state); @@ -1034,6 +1084,13 @@ void runtime(Translated _translated, RuntimeState _state, Stack *stack, valueB->value.as_number.is_int64)) { int64_t a = valueA->value.as_number.n.i64; int64_t b = valueB->value.as_number.n.i64; + if (!b) { + *err = create_err(state->source_location.line, + state->source_location.column, + state->source_location.length, state->path, + "Zero Division Error", "division by zero"); + continue; + } state->registers[registerC] = new_number_object_from_num_and_den(a, b); } else if (!valueA->value.as_number.is_int64 && @@ -1053,6 +1110,13 @@ void runtime(Translated _translated, RuntimeState _state, Stack *stack, mpq_set(b_GMP, *valueB->value.as_number.n.mpq); } else { mpq_set(a_GMP, *valueA->value.as_number.n.mpq); + if (!valueB->value.as_number.n.i64) { + *err = create_err(state->source_location.line, + state->source_location.column, + state->source_location.length, state->path, + "Zero Division Error", "division by zero"); + continue; + } mpq_set_si(b_GMP, valueB->value.as_number.n.i64, 1); } mpq_div(a_GMP, a_GMP, b_GMP); diff --git a/src/translator/access/access.c b/src/translator/access/access.c index 400b404..ddf7ed6 100644 --- a/src/translator/access/access.c +++ b/src/translator/access/access.c @@ -8,22 +8,23 @@ size_t translate_access(Translated *translated, ParsedAccess *access, ArErr *err) { set_registers(translated, 1); - uint64_t first = push_instruction_byte(translated, OP_LOAD_ACCESS_FUNCTION); + uint64_t first = translate_parsed(translated, &access->to_access, err); + if (err->exists) + return 0; + push_instruction_byte(translated, OP_LOAD_GETATTRIBUTE_FUNCTION); push_instruction_byte(translated, OP_INIT_CALL); - push_instruction_code(translated, access->access.size + 2); - - translate_parsed(translated, &access->to_access, err); - push_instruction_byte(translated, OP_INSERT_ARG); - push_instruction_code(translated, 0); + push_instruction_code(translated, access->access.size+1); push_instruction_byte(translated, OP_LOAD_BOOL); push_instruction_byte(translated, access->access_fields); push_instruction_byte(translated, OP_INSERT_ARG); - push_instruction_code(translated, 1); + push_instruction_code(translated, 0); for (size_t i = 0; i < access->access.size; i++) { translate_parsed(translated, darray_get(&access->access, i), err); + if (err->exists) + return 0; push_instruction_byte(translated, OP_INSERT_ARG); - push_instruction_code(translated, 2+i); + push_instruction_code(translated, 1 + i); } push_instruction_byte(translated, OP_SOURCE_LOCATION); diff --git a/src/translator/bytecode_spec.md b/src/translator/bytecode_spec.md index f1d5788..f0b9b91 100644 --- a/src/translator/bytecode_spec.md +++ b/src/translator/bytecode_spec.md @@ -120,9 +120,9 @@ sets the source location onto the runtime 1. the column 1. the length -## OP_LOAD_ACCESS_FUNCTION +## OP_LOAD_GETATTRIBUTE_FUNCTION -loads the access function into register 1 +loads the \_\_getattribute\_\_ function from the objects class in register 1 and put it into register 1 ## OP_LOAD_BOOL diff --git a/src/translator/translator.h b/src/translator/translator.h index 370bbce..4062f90 100644 --- a/src/translator/translator.h +++ b/src/translator/translator.h @@ -36,7 +36,7 @@ typedef enum { OP_COPY_TO_REGISTER, OP_ADDITION, OP_SUBTRACTION, - OP_LOAD_ACCESS_FUNCTION, + OP_LOAD_GETATTRIBUTE_FUNCTION, OP_MULTIPLICATION, OP_DIVISION, OP_NOT diff --git a/tests/iteration-test.ar b/tests/iteration-test.ar index 2c929db..0f7853b 100644 --- a/tests/iteration-test.ar +++ b/tests/iteration-test.ar @@ -1,3 +1,3 @@ -let i = 1e7 +let i = 1e6 while (i) do i=i-1 \ No newline at end of file