start adding error message support

This commit is contained in:
2025-07-09 01:55:40 +01:00
parent 9e5e932d39
commit e234ea074b
19 changed files with 293 additions and 115 deletions

2
.gitmodules vendored
View File

@@ -6,4 +6,4 @@
url = https://github.com/likle/cwalk.git url = https://github.com/likle/cwalk.git
[submodule "external/libdye"] [submodule "external/libdye"]
path = external/libdye path = external/libdye
url = https://github.com/kuzalekon/libdye.git url = https://github.com/Ugric/libdye.git

View File

@@ -30,9 +30,10 @@ add_custom_command(
add_custom_target(GenerateLexer DEPENDS ${LEXER_C} ${LEXER_H}) add_custom_target(GenerateLexer DEPENDS ${LEXER_C} ${LEXER_H})
# Step 3: Add executable # 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) 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/cwalk/include)
target_include_directories(argon PRIVATE ${CMAKE_SOURCE_DIR}/external/libdye/include)
# Step 4: Build order # Step 4: Build order
add_dependencies(argon GenerateLexer) add_dependencies(argon GenerateLexer)

View File

@@ -2,8 +2,8 @@ LEXER_SRC = src/lexer/lex.l
LEXER_C = src/lexer/lex.yy.c LEXER_C = src/lexer/lex.yy.c
LEXER_H = src/lexer/lex.yy.h LEXER_H = src/lexer/lex.yy.h
CFILES = external/xxhash/xxhash.c external/cwalk/src/cwalk.c $(shell find src -name '*.c') 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 CFLAGS = $(ARCHFLAGS) -lm -lgc -lgmp -Wall -Wextra -Wno-unused-function -Iexternal/cwalk/include -Iexternal/libdye/include
BINARY = bin/argon BINARY = bin/argon
all: $(BINARY) all: $(BINARY)

52
src/arobject.h Normal file
View File

@@ -0,0 +1,52 @@
#ifndef AROBJECT_H
#define AROBJECT_H
#include <gmp.h>
#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

134
src/err.c Normal file
View File

@@ -0,0 +1,134 @@
#include "err.h"
#include "../external/libdye/include/dye.h"
#include <ctype.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
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");
}
}
}

7
src/err.h Normal file
View File

@@ -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);

View File

@@ -3,8 +3,6 @@
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
typedef struct ArgonObject ArgonObject;
typedef void (*free_val_func)(void *val); typedef void (*free_val_func)(void *val);
struct node { struct node {

View File

@@ -108,8 +108,6 @@ int yywrap(void * unused_param) {
#[^\n]* { /* skip comment */ } #[^\n]* { /* skip comment */ }
. { . {
GET_STATE return TOKEN_INVALID;
fprintf(stderr, "%s:%zu:%zu error: unexpected character '%s'\n", state->path, state->current_line+1, COLUMN_NO+1, yytext);
exit(1);
} }
%% %%

View File

@@ -1,8 +1,8 @@
#include "lexer.h" #include "lexer.h"
#include "lex.yy.h"
#include "../string/string.h" #include "../string/string.h"
#include "lex.yy.h"
void lexer(LexerState state) { ArErr lexer(LexerState state) {
size_t line = 1; size_t line = 1;
size_t column = 1; size_t column = 1;
int ch; int ch;
@@ -32,13 +32,16 @@ void lexer(LexerState state) {
int token; int token;
while ((token = yylex(scanner)) != 0) { while ((token = yylex(scanner)) != 0) {
Token token_struct = (Token){ if (token == TOKEN_INVALID) {
token, ArErr err = create_err(state.current_line + 1, state.current_column + 1,
state.current_line+1, yyget_leng(scanner), state.path, "Syntax Error",
state.current_column+1, "Invalid Token '%s'", yyget_text(scanner));
yyget_leng(scanner), yylex_destroy(scanner);
cloneString(yyget_text(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); darray_push(state.tokens, &token_struct);
if (token == TOKEN_NEW_LINE) { if (token == TOKEN_NEW_LINE) {
state.current_line += 1; state.current_line += 1;
@@ -48,4 +51,5 @@ void lexer(LexerState state) {
} }
} }
yylex_destroy(scanner); yylex_destroy(scanner);
return no_err;
} }

View File

@@ -4,9 +4,10 @@
#include "../dynamic_array/darray.h" #include "../dynamic_array/darray.h"
#include "token.h" #include "token.h"
#include <stdio.h> #include <stdio.h>
#include "../err.h"
typedef struct { typedef struct {
const char *path; char *path;
FILE *file; FILE *file;
size_t current_line; size_t current_line;
size_t current_column; size_t current_column;
@@ -14,6 +15,6 @@ typedef struct {
// add more fields as needed // add more fields as needed
} LexerState; } LexerState;
void lexer(LexerState state); ArErr lexer(LexerState state);
#endif // LEXER_H #endif // LEXER_H

View File

@@ -72,6 +72,7 @@ typedef enum {
TOKEN_COMMA, TOKEN_COMMA,
TOKEN_COLON, TOKEN_COLON,
TOKEN_EXCLAMATION, TOKEN_EXCLAMATION,
TOKEN_INVALID,
} TokenType; } TokenType;
typedef struct { typedef struct {

View File

@@ -35,6 +35,7 @@
#include <limits.h> #include <limits.h>
#include <unistd.h> #include <unistd.h>
#endif #endif
#include "err.h"
char *get_current_directory() { char *get_current_directory() {
char *buffer = NULL; char *buffer = NULL;
@@ -160,7 +161,7 @@ FAILED:
return 1; return 1;
} }
ArgonObject *execute(char*absolute_path) { Execution execute(char *absolute_path) {
clock_t start, end; clock_t start, end;
double time_spent, total_time_spent = 0; double time_spent, total_time_spent = 0;
@@ -168,7 +169,10 @@ ArgonObject *execute(char*absolute_path) {
size_t basename_length; size_t basename_length;
cwk_path_get_basename(absolute_path, &basename_ptr, &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]; char basename[FILENAME_MAX];
memcpy(basename, basename_ptr, basename_length); memcpy(basename, basename_ptr, basename_length);
@@ -181,15 +185,20 @@ ArgonObject *execute(char*absolute_path) {
parent_directory[parent_directory_length] = '\0'; parent_directory[parent_directory_length] = '\0';
char cache_folder_path[FILENAME_MAX]; 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]; char cache_file_path[FILENAME_MAX];
cwk_path_join(cache_folder_path, basename, cache_file_path, sizeof(cache_file_path)); cwk_path_join(cache_folder_path, basename, cache_file_path,
cwk_path_change_extension(cache_file_path, BYTECODE_EXTENTION, cache_file_path, sizeof(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"); FILE *file = fopen(absolute_path, "r");
if (!file) { 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(); XXH3_state_t *hash_state = XXH3_createState();
@@ -215,7 +224,13 @@ ArgonObject *execute(char*absolute_path) {
LexerState state = {absolute_path, file, 0, 0, &tokens}; LexerState state = {absolute_path, file, 0, 0, &tokens};
start = clock(); 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(); end = clock();
time_spent = (double)(end - start) / CLOCKS_PER_SEC; time_spent = (double)(end - start) / CLOCKS_PER_SEC;
total_time_spent += time_spent; total_time_spent += time_spent;
@@ -269,7 +284,8 @@ ArgonObject *execute(char*absolute_path) {
start = clock(); start = clock();
RuntimeState state = init_runtime_state(translated); 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(); end = clock();
time_spent = (double)(end - start) / CLOCKS_PER_SEC; 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); printf("total time taken: %f seconds\n", total_time_spent);
free_translator(&translated); free_translator(&translated);
return resp; return (Execution){err, main_scope};
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
@@ -291,10 +307,12 @@ int main(int argc, char *argv[]) {
return -1; return -1;
char *path_non_absolute = argv[1]; char *path_non_absolute = argv[1];
char path[FILENAME_MAX]; char path[FILENAME_MAX];
cwk_path_get_absolute(CWD, path_non_absolute, path, cwk_path_get_absolute(CWD, path_non_absolute, path, sizeof(path));
sizeof(path));
free(CWD); free(CWD);
ArgonObject *resp = execute(path); Execution resp = execute(path);
if (resp) return 0; if (resp.err.exists){
output_err(resp.err);
return -1; return -1;
} }
return 0;
}

22
src/returnTypes.h Normal file
View File

@@ -0,0 +1,22 @@
#ifndef RETURN_TYPES_H
#define RETURN_TYPES_H
#include <stdint.h>
#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_

View File

@@ -3,8 +3,6 @@
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
typedef struct ArgonObject ArgonObject;
struct node_GC { struct node_GC {
uint64_t hash; uint64_t hash;
void *key; void *key;

View File

@@ -1,49 +1,11 @@
#ifndef OBJECT_H #ifndef OBJECT_H
#define OBJECT_H #define OBJECT_H
#include "../internals/hashmap/hashmap.h" #include "../internals/hashmap/hashmap.h"
#include "../internals/dynamic_array_armem/darray_armem.h"
#include <gmp.h>
#include <stdbool.h>
#include "../runtime.h" #include "../runtime.h"
#include <stdbool.h>
extern ArgonObject *BASE_CLASS; 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; typedef struct ArgonObject ArgonObject;
void init_base_field(); void init_base_field();
ArgonObject *init_child_argon_object(ArgonObject *cls); ArgonObject *init_child_argon_object(ArgonObject *cls);

View File

@@ -1,4 +1,5 @@
#include "runtime.h" #include "runtime.h"
#include "../err.h"
#include "../translator/translator.h" #include "../translator/translator.h"
#include "objects/functions/functions.h" #include "objects/functions/functions.h"
#include "objects/null/null.h" #include "objects/null/null.h"
@@ -62,18 +63,6 @@ void load_const(Translated *translated, RuntimeState *state) {
state->registers[to_register] = object; 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, ArErr run_instruction(Translated *translated, RuntimeState *state,
struct Stack stack) { struct Stack stack) {
OperationType opcode = pop_byte(translated, state); OperationType opcode = pop_byte(translated, state);
@@ -88,24 +77,26 @@ ArErr create_err(char *path, int64_t line, char *type, char *message) {
load_argon_function(translated, state, stack); load_argon_function(translated, state, stack);
break; break;
default: default:
fprintf(stderr, "bytecode invalid\n"); return create_err(0, 0, 0, NULL, "Runtime Error", "Invalid Opcode %#x", opcode);
exit(EXIT_FAILURE);
} }
return no_err; return no_err;
} }
RuntimeState init_runtime_state(Translated translated) { RuntimeState init_runtime_state(Translated translated) {
RuntimeState state = { return (RuntimeState){
checked_malloc(translated.registerCount * sizeof(ArgonObject *)), 0}; checked_malloc(translated.registerCount * sizeof(ArgonObject *)), 0};
return state;
} }
ArgonObject *runtime(Translated translated, RuntimeState state) { Stack create_scope(Stack *prev) { return (Stack){NULL, prev}; }
struct Stack stack = {NULL, NULL};
ArErr runtime(Translated translated, RuntimeState state, Stack stack) {
state.head = 0; state.head = 0;
while (state.head < translated.bytecode.size) { 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); free(state.registers);
return stack.scope; return no_err;
} }

View File

@@ -2,25 +2,13 @@
#define RUNTIME_H #define RUNTIME_H
#include "../translator/translator.h" #include "../translator/translator.h"
#include "internals/hashmap/hashmap.h" #include "internals/hashmap/hashmap.h"
#include "../returnTypes.h"
typedef struct { typedef struct {
ArgonObject **registers; ArgonObject **registers;
size_t head; size_t head;
} RuntimeState; } 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(); void init_types();
uint64_t pop_bytecode(Translated *translated, RuntimeState *state); 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); 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 #endif // RUNTIME_H

1
testing.ar Normal file
View File

@@ -0,0 +1 @@
10~10