Error handling.
This commit is contained in:
+201
@@ -0,0 +1,201 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "types.h"
|
||||
#include "error.h"
|
||||
|
||||
extern Token tokens[];
|
||||
extern int tk_idx;
|
||||
extern int pc;
|
||||
extern CallStack call_stack;
|
||||
|
||||
void push_call_frame(const char *function_name, const char *filename, int line, int is_native) {
|
||||
if (call_stack.depth >= MAX_CALL_STACK) {
|
||||
return;
|
||||
}
|
||||
|
||||
CallStackFrame *frame = &call_stack.frames[call_stack.depth];
|
||||
frame->function_name = function_name;
|
||||
frame->filename = filename;
|
||||
frame->line = line;
|
||||
frame->is_native = is_native;
|
||||
call_stack.depth++;
|
||||
}
|
||||
|
||||
void pop_call_frame() {
|
||||
if (call_stack.depth > 0) {
|
||||
call_stack.depth--;
|
||||
}
|
||||
}
|
||||
|
||||
void print_stacktrace() {
|
||||
if (call_stack.depth == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "╔════════════════════════════════════════════════════════════════╗\n");
|
||||
fprintf(stderr, "║ STACK TRACE ║\n");
|
||||
fprintf(stderr, "╚════════════════════════════════════════════════════════════════╝\n");
|
||||
|
||||
int frames_to_show = call_stack.depth;
|
||||
if (frames_to_show > 10) {
|
||||
frames_to_show = 10;
|
||||
}
|
||||
|
||||
for (int i = call_stack.depth - 1; i >= call_stack.depth - frames_to_show; i--) {
|
||||
if (i < 0) break;
|
||||
|
||||
CallStackFrame *frame = &call_stack.frames[i];
|
||||
const char *type_marker = frame->is_native ? "[native]" : "[script]";
|
||||
|
||||
fprintf(stderr, " %s ", type_marker);
|
||||
|
||||
if (frame->function_name) {
|
||||
fprintf(stderr, "%s()", frame->function_name);
|
||||
} else {
|
||||
fprintf(stderr, "<anonymous>");
|
||||
}
|
||||
|
||||
if (frame->filename && frame->line > 0) {
|
||||
fprintf(stderr, "\n at %s:%d", frame->filename, frame->line);
|
||||
} else if (frame->filename) {
|
||||
fprintf(stderr, "\n at %s", frame->filename);
|
||||
}
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
if (call_stack.depth > 10) {
|
||||
fprintf(stderr, " ... %d more frames\n", call_stack.depth - 10);
|
||||
}
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
static const char* get_error_tip(const char *error_type, const char *message) {
|
||||
if (strstr(message, "index out of bounds") || strstr(message, "Token index")) {
|
||||
return "Check array bounds and ensure index is within valid range";
|
||||
}
|
||||
if (strstr(message, "Too many")) {
|
||||
return "Consider breaking down into smaller components or increasing limits";
|
||||
}
|
||||
if (strstr(message, "Stack overflow")) {
|
||||
return "Reduce recursion depth or local variable usage";
|
||||
}
|
||||
if (strstr(message, "Undefined variable")) {
|
||||
return "Ensure variable is declared before use with: int varname;";
|
||||
}
|
||||
if (strstr(message, "Unknown function")) {
|
||||
return "Check function name spelling and ensure it's defined before calling";
|
||||
}
|
||||
if (strstr(message, "Unexpected token")) {
|
||||
return "Check for missing semicolons, braces, or parentheses";
|
||||
}
|
||||
if (strstr(message, "division by zero") || strstr(message, "divide by zero")) {
|
||||
return "Add a check: if (divisor != 0) before division";
|
||||
}
|
||||
if (strstr(message, "Null pointer")) {
|
||||
return "Check if pointer is null before dereferencing: if (ptr != null)";
|
||||
}
|
||||
if (strstr(message, "Memory")) {
|
||||
return "Reduce memory usage or check for memory leaks";
|
||||
}
|
||||
if (strstr(message, "Unknown class") || strstr(message, "Unknown field") ||
|
||||
strstr(message, "Unknown method")) {
|
||||
return "Verify class/field/method name and ensure class is defined";
|
||||
}
|
||||
|
||||
return "Review the code at the error location and check for typos";
|
||||
}
|
||||
|
||||
static void print_code_context(int token_index) {
|
||||
if (token_index < 0 || token_index >= tk_idx) {
|
||||
return;
|
||||
}
|
||||
|
||||
Token *error_token = &tokens[token_index];
|
||||
if (!error_token->filename || error_token->line <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "╔════════════════════════════════════════════════════════════════╗\n");
|
||||
fprintf(stderr, "║ CODE CONTEXT ║\n");
|
||||
fprintf(stderr, "╚════════════════════════════════════════════════════════════════╝\n");
|
||||
|
||||
int start_token = token_index - 3;
|
||||
int end_token = token_index + 3;
|
||||
|
||||
if (start_token < 0) start_token = 0;
|
||||
if (end_token >= tk_idx) end_token = tk_idx - 1;
|
||||
|
||||
for (int i = start_token; i <= end_token && i < tk_idx; i++) {
|
||||
if (i < 0) continue;
|
||||
|
||||
Token *t = &tokens[i];
|
||||
const char *marker = (i == token_index) ? ">>> " : " ";
|
||||
|
||||
fprintf(stderr, "%s", marker);
|
||||
|
||||
if (t->type >= 32 && t->type < 127) {
|
||||
fprintf(stderr, "'%c' ", t->type);
|
||||
} else if (t->type == Num) {
|
||||
fprintf(stderr, "%ld ", t->val);
|
||||
} else if (t->type == Id && t->text) {
|
||||
fprintf(stderr, "%.*s ", (int)t->val, t->text);
|
||||
} else if (t->type == Str && t->text) {
|
||||
fprintf(stderr, "\"%s\" ", t->text);
|
||||
} else {
|
||||
fprintf(stderr, "[token:%d] ", t->type);
|
||||
}
|
||||
|
||||
if (i == token_index) {
|
||||
fprintf(stderr, "<-- ERROR HERE");
|
||||
}
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
void error_with_context(const char *error_type, const char *message, const char *tip) {
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "╔════════════════════════════════════════════════════════════════╗\n");
|
||||
fprintf(stderr, "║ RUNTIME ERROR ║\n");
|
||||
fprintf(stderr, "╚════════════════════════════════════════════════════════════════╝\n");
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
fprintf(stderr, " Error Type: %s\n", error_type ? error_type : "General Error");
|
||||
fprintf(stderr, " Message: %s\n", message);
|
||||
|
||||
if (pc >= 0 && pc < tk_idx) {
|
||||
Token *current_token = &tokens[pc];
|
||||
if (current_token->filename) {
|
||||
fprintf(stderr, " Location: %s", current_token->filename);
|
||||
if (current_token->line > 0) {
|
||||
fprintf(stderr, ":%d", current_token->line);
|
||||
if (current_token->column > 0) {
|
||||
fprintf(stderr, ":%d", current_token->column);
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
const char *actual_tip = tip ? tip : get_error_tip(error_type, message);
|
||||
if (actual_tip) {
|
||||
fprintf(stderr, "╔════════════════════════════════════════════════════════════════╗\n");
|
||||
fprintf(stderr, "║ TIP ║\n");
|
||||
fprintf(stderr, "╚════════════════════════════════════════════════════════════════╝\n");
|
||||
fprintf(stderr, " 💡 %s\n", actual_tip);
|
||||
}
|
||||
|
||||
print_code_context(pc);
|
||||
print_stacktrace();
|
||||
|
||||
exit(1);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#ifndef ERROR_H
|
||||
#define ERROR_H
|
||||
|
||||
void push_call_frame(const char *function_name, const char *filename, int line, int is_native);
|
||||
void pop_call_frame();
|
||||
void print_stacktrace();
|
||||
void error_with_context(const char *error_type, const char *message, const char *tip);
|
||||
|
||||
#endif
|
||||
@@ -23,3 +23,4 @@ ClassDef classes[MAX_CLASSES];
|
||||
int class_count = 0;
|
||||
Object objects[MAX_OBJECTS];
|
||||
int object_count = 0;
|
||||
CallStack call_stack = {{}, 0};
|
||||
|
||||
+2
-6
@@ -5,14 +5,10 @@
|
||||
#include "types.h"
|
||||
#include "interpreter.h"
|
||||
#include "parser.h"
|
||||
#include "error.h"
|
||||
|
||||
void error(char *msg) {
|
||||
if (pc >= 0 && pc < MAX_TOK && pc < tk_idx) {
|
||||
printf("Error at token %d ('%c'): %s\n", pc, tokens[pc].type, msg);
|
||||
} else {
|
||||
printf("Error at token %d: %s\n", pc, msg);
|
||||
}
|
||||
exit(1);
|
||||
error_with_context("ParseError", msg, NULL);
|
||||
}
|
||||
|
||||
void match(int type) {
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ int main(int argc, char **argv) {
|
||||
|
||||
register_native_functions();
|
||||
|
||||
tokenize(src_code);
|
||||
tokenize_with_location(src_code, file_path);
|
||||
scan_classes();
|
||||
scan_functions();
|
||||
link_class_methods();
|
||||
|
||||
+23
-1
@@ -5,6 +5,7 @@
|
||||
#include "parser.h"
|
||||
#include "interpreter.h"
|
||||
#include "string_utils.h"
|
||||
#include "error.h"
|
||||
|
||||
extern Token tokens[];
|
||||
extern int pc;
|
||||
@@ -18,6 +19,7 @@ extern int func_cnt;
|
||||
extern NativeFuncDef native_funcs[];
|
||||
extern int native_func_cnt;
|
||||
extern long ax;
|
||||
extern CallStack call_stack;
|
||||
|
||||
extern void error(char *msg);
|
||||
extern void match(int type);
|
||||
@@ -135,7 +137,17 @@ long factor() {
|
||||
}
|
||||
match(')');
|
||||
|
||||
return native_funcs[nf_idx].func(args, argc);
|
||||
const char *native_filename = NULL;
|
||||
int native_line = 0;
|
||||
if (pc < tk_idx && pc >= 0) {
|
||||
native_filename = tokens[pc].filename;
|
||||
native_line = tokens[pc].line;
|
||||
}
|
||||
push_call_frame(native_funcs[nf_idx].name, native_filename, native_line, 1);
|
||||
long result = native_funcs[nf_idx].func(args, argc);
|
||||
pop_call_frame();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int f_idx = find_func(t->text, t->val);
|
||||
@@ -201,6 +213,14 @@ long factor() {
|
||||
scan_pc++;
|
||||
}
|
||||
|
||||
const char *func_filename = NULL;
|
||||
int func_line = 0;
|
||||
if (funcs[f_idx].entry_point < tk_idx) {
|
||||
func_filename = tokens[funcs[f_idx].entry_point].filename;
|
||||
func_line = tokens[funcs[f_idx].entry_point].line;
|
||||
}
|
||||
push_call_frame(funcs[f_idx].name, func_filename, func_line, 0);
|
||||
|
||||
pc = funcs[f_idx].entry_point;
|
||||
return_flag = 0;
|
||||
statement();
|
||||
@@ -209,6 +229,8 @@ long factor() {
|
||||
ax = 0;
|
||||
return_flag = 0;
|
||||
|
||||
pop_call_frame();
|
||||
|
||||
loc_cnt = old_loc_cnt;
|
||||
if (bp < 0 || bp >= MEM_SIZE) return 0;
|
||||
sp = bp;
|
||||
|
||||
+42
-13
@@ -4,12 +4,27 @@
|
||||
#include "types.h"
|
||||
#include "tokenizer.h"
|
||||
|
||||
void tokenize(char *src) {
|
||||
void tokenize_with_location(char *src, const char *filename) {
|
||||
char *s = src;
|
||||
if (!src) return;
|
||||
|
||||
int line = 1;
|
||||
int column = 1;
|
||||
char *line_start = s;
|
||||
|
||||
while (*s) {
|
||||
if (isspace(*s)) { s++; continue; }
|
||||
if (*s == '\n') {
|
||||
line++;
|
||||
s++;
|
||||
column = 1;
|
||||
line_start = s;
|
||||
continue;
|
||||
}
|
||||
if (isspace(*s)) {
|
||||
s++;
|
||||
column++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tk_idx >= MAX_TOK - 1) {
|
||||
return;
|
||||
@@ -17,9 +32,15 @@ void tokenize(char *src) {
|
||||
|
||||
Token *t = &tokens[tk_idx++];
|
||||
t->text = s;
|
||||
t->line = line;
|
||||
t->column = column;
|
||||
t->filename = filename;
|
||||
|
||||
if (*s == '/' && s[1] && s[1] == '/') {
|
||||
while (*s && *s != '\n') s++;
|
||||
while (*s && *s != '\n') {
|
||||
s++;
|
||||
column++;
|
||||
}
|
||||
tk_idx--;
|
||||
continue;
|
||||
}
|
||||
@@ -57,6 +78,7 @@ void tokenize(char *src) {
|
||||
|
||||
t->val = len;
|
||||
s += len;
|
||||
column += len;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -74,6 +96,7 @@ void tokenize(char *src) {
|
||||
t->type = Num;
|
||||
t->val = strtol(start, NULL, 10);
|
||||
}
|
||||
column += (s - start);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -93,24 +116,30 @@ void tokenize(char *src) {
|
||||
if (*s == '"') s++;
|
||||
*d = 0;
|
||||
t->val = (long)(d - t->text);
|
||||
column += (s - t->text);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strncmp(s, "==", 2)) { t->type = Eq; s += 2; continue; }
|
||||
if (!strncmp(s, "!=", 2)) { t->type = Ne; s += 2; continue; }
|
||||
if (!strncmp(s, "<=", 2)) { t->type = Le; s += 2; continue; }
|
||||
if (!strncmp(s, ">=", 2)) { t->type = Ge; s += 2; continue; }
|
||||
if (!strncmp(s, "&&", 2)) { t->type = And; s += 2; continue; }
|
||||
if (!strncmp(s, "||", 2)) { t->type = Or; s += 2; continue; }
|
||||
if (!strncmp(s, "++", 2)) { t->type = Inc; s += 2; continue; }
|
||||
if (!strncmp(s, "--", 2)) { t->type = Dec; s += 2; continue; }
|
||||
if (!strncmp(s, "==", 2)) { t->type = Eq; s += 2; column += 2; continue; }
|
||||
if (!strncmp(s, "!=", 2)) { t->type = Ne; s += 2; column += 2; continue; }
|
||||
if (!strncmp(s, "<=", 2)) { t->type = Le; s += 2; column += 2; continue; }
|
||||
if (!strncmp(s, ">=", 2)) { t->type = Ge; s += 2; column += 2; continue; }
|
||||
if (!strncmp(s, "&&", 2)) { t->type = And; s += 2; column += 2; continue; }
|
||||
if (!strncmp(s, "||", 2)) { t->type = Or; s += 2; column += 2; continue; }
|
||||
if (!strncmp(s, "++", 2)) { t->type = Inc; s += 2; column += 2; continue; }
|
||||
if (!strncmp(s, "--", 2)) { t->type = Dec; s += 2; column += 2; continue; }
|
||||
|
||||
if (*s == '<') { t->type = Lt; s++; continue; }
|
||||
if (*s == '>') { t->type = Gt; s++; continue; }
|
||||
if (*s == '<') { t->type = Lt; s++; column++; continue; }
|
||||
if (*s == '>') { t->type = Gt; s++; column++; continue; }
|
||||
|
||||
t->type = *s++;
|
||||
column++;
|
||||
}
|
||||
if (tk_idx < MAX_TOK) {
|
||||
tokens[tk_idx].type = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void tokenize(char *src) {
|
||||
tokenize_with_location(src, "<unknown>");
|
||||
}
|
||||
|
||||
@@ -2,5 +2,6 @@
|
||||
#define TOKENIZER_H
|
||||
|
||||
void tokenize(char *src);
|
||||
void tokenize_with_location(char *src, const char *filename);
|
||||
|
||||
#endif
|
||||
|
||||
+19
@@ -58,6 +58,9 @@ typedef struct {
|
||||
long val;
|
||||
double dval;
|
||||
char *text;
|
||||
int line;
|
||||
int column;
|
||||
const char *filename;
|
||||
} Token;
|
||||
|
||||
typedef struct {
|
||||
@@ -157,4 +160,20 @@ extern int class_count;
|
||||
extern Object objects[MAX_OBJECTS];
|
||||
extern int object_count;
|
||||
|
||||
#define MAX_CALL_STACK 100
|
||||
|
||||
typedef struct {
|
||||
const char *function_name;
|
||||
const char *filename;
|
||||
int line;
|
||||
int is_native;
|
||||
} CallStackFrame;
|
||||
|
||||
typedef struct {
|
||||
CallStackFrame frames[MAX_CALL_STACK];
|
||||
int depth;
|
||||
} CallStack;
|
||||
|
||||
extern CallStack call_stack;
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user