diff --git a/CMakeLists.txt b/CMakeLists.txt index 147ba47..0daea99 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,7 +34,7 @@ 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 external/libdye/src/dye.c ${CFILES} ${LEXER_C}) +add_executable(argon external/xxhash/xxhash.c external/cwalk/src/cwalk.c external/libdye/src/dye.c external/linenoise/linenoise.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) diff --git a/Makefile b/Makefile index 160248e..830cc35 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ BINARY = bin/argon FLEX_TOOL = flex -CFILES = external/xxhash/xxhash.c external/cwalk/src/cwalk.c external/libdye/src/dye.c $(shell find src -name '*.c') +CFILES = external/xxhash/xxhash.c external/cwalk/src/cwalk.c external/libdye/src/dye.c external/linenoise/linenoise.c $(shell find src -name '*.c') LEXER_SRC = src/lexer/lex.l LEXER_C = src/lexer/lex.yy.c diff --git a/src/main.c b/src/main.c index 1827e8d..f8a0a3d 100644 --- a/src/main.c +++ b/src/main.c @@ -12,6 +12,7 @@ #include "memory.h" #include "parser/parser.h" #include "returnTypes.h" +#include "shell.h" #include "runtime/runtime.h" #include "translator/translator.h" @@ -358,32 +359,29 @@ Translated load_argon_file(char *path, ArErr *err) { start = clock(); *err = parser(path, &ast, &tokens, false); + darray_free(&tokens, free_token); if (err->exists) { - darray_free(&tokens, free_token); 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); - darray_free(&tokens, free_token); 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); - darray_free(&ast, free_parsed); return (Translated){}; } end = clock(); time_spent = (double)(end - start) / CLOCKS_PER_SEC; fprintf(stderr, "Translation time taken: %f seconds\n", time_spent); - - darray_free(&ast, free_parsed); #if defined(__linux__) malloc_trim(0); #endif @@ -458,9 +456,9 @@ int main(int argc, char *argv[]) { generate_siphash_key(siphash_key); bootstrap_types(); bootstrap_globals(); - char *CWD = get_current_directory(); if (argc <= 1) - return -1; + return shell(); + char *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)); diff --git a/src/parser/number/number.c b/src/parser/number/number.c index e06acc5..c32c9db 100644 --- a/src/parser/number/number.c +++ b/src/parser/number/number.c @@ -7,6 +7,7 @@ #include "number.h" #include "../../memory.h" #include +#include #include #include #include @@ -180,7 +181,8 @@ ParsedValueReturn parse_number(Token *token, char *path) { mpq_init(*r_ptr); int err = mpq_set_decimal_str_exp(*r_ptr, token->value, token->length); if (err) { - free_parsed(parsedValue); + mpq_clear(*r_ptr); + free(r_ptr); free(parsedValue); return (ParsedValueReturn){create_err(token->line, token->column, token->length, path, "Parsing Error", @@ -189,4 +191,5 @@ ParsedValueReturn parse_number(Token *token, char *path) { } parsedValue->data = r_ptr; return (ParsedValueReturn){no_err, parsedValue}; -} \ No newline at end of file +} + diff --git a/src/shell.c b/src/shell.c new file mode 100644 index 0000000..9ecf3b2 --- /dev/null +++ b/src/shell.c @@ -0,0 +1,252 @@ +/* + * SPDX-FileCopyrightText: 2025 William Bell + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +#include "../external/linenoise/linenoise.h" +#include "./lexer/lexer.h" +#include "./runtime/call/call.h" +#include "./runtime/objects/functions/functions.h" +#include "./runtime/objects/term/term.h" +#include "./runtime/runtime.h" +#include "./translator/translator.h" +#include +#include +#include +#include +#include +#include +#if defined(__linux__) +#include +#endif + +volatile sig_atomic_t interrupted = 0; + +// Ctrl+C handler +void handle_sigint(int sig) { + (void)sig; + interrupted = 1; +} + +int execute_code(FILE *stream, char *path, Stack *scope, + RuntimeState *runtime_state) { + if (!stream) { + perror("fmemopen"); + return 1; + } + + ArErr err; + + DArray tokens; + darray_init(&tokens, sizeof(Token)); + LexerState state = {path, stream, 0, 0, &tokens}; + err = lexer(state); + if (err.exists) { + darray_free(&tokens, free_token); + output_err(err); + return 1; + } + + DArray ast; + + darray_init(&ast, sizeof(ParsedValue)); + + err = parser(path, &ast, &tokens, false); + darray_free(&tokens, free_token); + if (err.exists) { + darray_free(&ast, free_parsed); + output_err(err); + return 1; + } + Translated __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); + output_err(err); + return 1; + } + + hashmap_free(__translated.constants.hashmap, NULL); + Translated translated = { + __translated.registerCount, NULL, {}, {}, __translated.path}; + translated.bytecode.data = ar_alloc(__translated.bytecode.capacity); + memcpy(translated.bytecode.data, __translated.bytecode.data, + __translated.bytecode.capacity); + translated.bytecode.element_size = __translated.bytecode.element_size; + translated.bytecode.size = __translated.bytecode.size; + translated.bytecode.resizable = false; + translated.bytecode.capacity = + __translated.bytecode.size * __translated.bytecode.element_size; + translated.constants.data = ar_alloc(__translated.constants.capacity); + memcpy(translated.constants.data, __translated.constants.data, + __translated.constants.capacity); + translated.constants.size = __translated.constants.size; + translated.constants.capacity = __translated.constants.capacity; + darray_free(&__translated.bytecode, NULL); + free(__translated.constants.data); + *runtime_state = init_runtime_state(translated, path); + err = runtime(translated, *runtime_state, scope); + if (err.exists) { + output_err(err); + return 1; + } + return 0; +} + +// Simple input function +char *input(const char *prompt) { + printf("%s", prompt); + fflush(stdout); + + char *buffer = NULL; + size_t size = 0; + ssize_t len = getline(&buffer, &size, stdin); + + if (len == -1) { + free(buffer); + return NULL; + } + + // Remove trailing newline + if (len > 0 && buffer[len - 1] == '\n') { + buffer[len - 1] = '\0'; + } + + return buffer; +} + +char *read_all_stdin(size_t *out_len) { + size_t size = 1024; + size_t len = 0; + char *buffer = malloc(size); + if (!buffer) + exit(1); + + int c; + while ((c = fgetc(stdin)) != EOF) { + if (len + 1 >= size) { + size *= 2; + buffer = realloc(buffer, size); + if (!buffer) + exit(1); + } + buffer[len++] = (char)c; + } + + *out_len = len; + return buffer; +} + +int shell() { + + Stack *main_scope = create_scope(Global_Scope); + + if (!isatty(STDIN_FILENO)) { + RuntimeState runtime_state; + size_t len; + char *data = read_all_stdin(&len); + FILE *file = fmemopen(data, len, "r"); + int resp = execute_code(file, "", main_scope, &runtime_state); + fclose(file); + free(data); + return resp; + } + + signal(SIGINT, handle_sigint); + + printf("Welcome to the Argon shell!\n\n"); + + ArgonObject *output_object = create_argon_native_function("log", term_log); + char *totranslate = NULL; + size_t totranslatelength = 0; + + while (true) { +#if defined(__linux__) + malloc_trim(0); +#endif + if (totranslate) { + free(totranslate); + totranslate = NULL; + totranslatelength = 0; + }; + int indent = 0; + char textBefore[] = ">>> "; + + // Dynamic array of lines + + do { + // indent string + size_t isz = (size_t)indent * 4; + char *indentStr = (char *)malloc(isz + 1); + if (!indentStr) + exit(1); + memset(indentStr, ' ', isz); + indentStr[isz] = '\0'; + + // prompt + size_t p_len = strlen(textBefore) + isz; + char *prompt = (char *)malloc(p_len + 1); + if (!prompt) + exit(1); + memcpy(prompt, textBefore, strlen(textBefore)); + memcpy(prompt + strlen(textBefore), indentStr, isz + 1); + + char *inp = linenoise(prompt); + free(prompt); + + if (!inp) { + printf("\nBye :)\n"); + // Free previously collected lines + free(inp); + free(totranslate); + free(indentStr); + return 0; + } + if (inp[0] != '\0') { + // Optionally add line to history + linenoiseHistoryAdd(inp); + } + + // Append line to totranslate + size_t length = strlen(inp); + totranslate = realloc(totranslate, totranslatelength + isz + length + 1); + memcpy(totranslate + totranslatelength, indentStr, isz); + memcpy(totranslate + totranslatelength + isz, inp, length); + totranslatelength += isz + length + 1; + totranslate[totranslatelength - 1] = '\n'; + + char *trimmed = inp; + while (*trimmed == ' ' || *trimmed == '\t') + trimmed++; + + size_t len = strlen(trimmed); + if (len >= 2 && strcmp(trimmed + len - 2, "do") == 0) { + indent++; + } else if (len == 0 && indent > 0) { + indent--; + } + free(inp); + strcpy(textBefore, "... "); + free(indentStr); + + } while (indent > 0); + totranslate = realloc(totranslate, totranslatelength + 1); + totranslate[totranslatelength] = '\0'; + RuntimeState runtime_state; + FILE *file = fmemopen((void *)totranslate, totranslatelength, "r"); + int resp = execute_code(file, "", main_scope, &runtime_state); + fclose(file); + if (resp) { + continue; + } + ArErr err; + argon_call(output_object, 1, (ArgonObject *[]){runtime_state.registers[0]}, + &err, &runtime_state); + totranslatelength = 0; + } + + return 0; +} \ No newline at end of file diff --git a/src/shell.h b/src/shell.h new file mode 100644 index 0000000..7c7b1f3 --- /dev/null +++ b/src/shell.h @@ -0,0 +1,12 @@ +/* + * SPDX-FileCopyrightText: 2025 William Bell + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#ifndef ARGON_SHELL_H +#define ARGON_SHELL_H + +int shell(); + +#endif // ARGON_SHELL_H \ No newline at end of file