566 lines
19 KiB
C
Raw Normal View History

2025-12-05 12:32:09 +01:00
#define _POSIX_C_SOURCE 200809L
#include "repl_executor.h"
#include "repl_input.h"
#include "repl_output.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <time.h>
RavaREPLExecutor_t* rava_repl_executor_create(RavaREPLSession_t *session) {
RavaREPLExecutor_t *executor = calloc(1, sizeof(RavaREPLExecutor_t));
if (!executor) return NULL;
executor->session = session;
executor->timeout_ms = RAVA_REPL_TIMEOUT_MS;
executor->interrupted = false;
executor->show_timing = false;
return executor;
}
void rava_repl_executor_destroy(RavaREPLExecutor_t *executor) {
free(executor);
}
static char* extract_class_name(const char *code) {
const char *p = code;
while (*p && isspace(*p)) p++;
if (strncmp(p, "public", 6) == 0) { p += 6; while (*p && isspace(*p)) p++; }
if (strncmp(p, "class", 5) == 0) p += 5;
else if (strncmp(p, "interface", 9) == 0) p += 9;
else if (strncmp(p, "enum", 4) == 0) p += 4;
else return NULL;
while (*p && isspace(*p)) p++;
const char *start = p;
while (*p && (isalnum(*p) || *p == '_')) p++;
if (p == start) return NULL;
return strndup(start, p - start);
}
static char* extract_method_name(const char *code) {
const char *paren = strchr(code, '(');
if (!paren) return NULL;
const char *end = paren - 1;
while (end > code && isspace(*end)) end--;
const char *start = end;
while (start > code && (isalnum(*(start-1)) || *(start-1) == '_')) start--;
if (start > end) return NULL;
return strndup(start, end - start + 1);
}
static char* extract_method_return_type(const char *code) {
const char *p = code;
while (*p && isspace(*p)) p++;
if (strncmp(p, "public", 6) == 0) { p += 6; while (*p && isspace(*p)) p++; }
if (strncmp(p, "private", 7) == 0) { p += 7; while (*p && isspace(*p)) p++; }
if (strncmp(p, "static", 6) == 0) { p += 6; while (*p && isspace(*p)) p++; }
const char *start = p;
while (*p && (isalnum(*p) || *p == '_' || *p == '[' || *p == ']')) p++;
if (p == start) return NULL;
return strndup(start, p - start);
}
static char* extract_var_name(const char *code) {
const char *p = code;
while (*p && isspace(*p)) p++;
while (*p && (isalnum(*p) || *p == '_' || *p == '[' || *p == ']')) p++;
while (*p && isspace(*p)) p++;
const char *start = p;
while (*p && (isalnum(*p) || *p == '_')) p++;
if (p == start) return NULL;
return strndup(start, p - start);
}
static char* extract_var_type(const char *code) {
const char *p = code;
while (*p && isspace(*p)) p++;
const char *start = p;
while (*p && (isalnum(*p) || *p == '_' || *p == '[' || *p == ']')) p++;
if (p == start) return NULL;
return strndup(start, p - start);
}
static char* extract_var_initializer(const char *code) {
const char *eq = strchr(code, '=');
if (!eq) return NULL;
eq++;
while (*eq && isspace(*eq)) eq++;
const char *end = eq;
while (*end && *end != ';') end++;
if (end == eq) return NULL;
while (end > eq && isspace(*(end-1))) end--;
return strndup(eq, end - eq);
}
static char* generate_full_source(RavaREPLSession_t *session, const char *eval_code) {
size_t capacity = 16384;
char *source = malloc(capacity);
if (!source) return NULL;
size_t len = 0;
source[0] = '\0';
#define APPEND(...) do { \
int needed = snprintf(source + len, capacity - len, __VA_ARGS__); \
if (needed < 0) { free(source); return NULL; } \
while ((size_t)needed >= capacity - len) { \
capacity *= 2; \
char *tmp = realloc(source, capacity); \
if (!tmp) { free(source); return NULL; } \
source = tmp; \
needed = snprintf(source + len, capacity - len, __VA_ARGS__); \
} \
len += needed; \
} while(0)
for (size_t i = 0; i < session->class_count; i++) {
if (session->classes[i].source) {
APPEND("%s\n\n", session->classes[i].source);
}
}
APPEND("public class _REPL_ {\n");
for (size_t i = 0; i < session->variable_count; i++) {
RavaREPLVariable_t *var = &session->variables[i];
const char *type = var->type_name ? var->type_name : "int";
APPEND(" public static %s %s", type, var->name);
if (var->is_initialized) {
if (var->type == RAVA_VAL_OBJECT && var->initializer) {
APPEND(" = %s", var->initializer);
} else {
switch (var->type) {
case RAVA_VAL_INT:
case RAVA_VAL_LONG:
APPEND(" = %ld", var->value.data.int_val);
break;
case RAVA_VAL_DOUBLE:
APPEND(" = %g", var->value.data.double_val);
break;
case RAVA_VAL_BOOLEAN:
APPEND(" = %s", var->value.data.bool_val ? "true" : "false");
break;
case RAVA_VAL_STRING:
if (var->value.data.string_val) {
APPEND(" = \"%s\"", var->value.data.string_val);
}
break;
default:
if (var->initializer) {
APPEND(" = %s", var->initializer);
}
break;
}
}
}
APPEND(";\n");
}
for (size_t i = 0; i < session->method_count; i++) {
if (session->methods[i].source) {
APPEND("\n public static %s\n", session->methods[i].source);
}
}
APPEND("\n public static int _eval_() {\n");
if (eval_code && strlen(eval_code) > 0) {
APPEND(" %s\n", eval_code);
}
APPEND(" return 0;\n");
APPEND(" }\n");
APPEND("}\n");
#undef APPEND
return source;
}
static char* generate_expression_source(RavaREPLSession_t *session, const char *expr) {
size_t capacity = 16384;
char *source = malloc(capacity);
if (!source) return NULL;
size_t len = 0;
source[0] = '\0';
#define APPEND(...) do { \
int needed = snprintf(source + len, capacity - len, __VA_ARGS__); \
if (needed < 0) { free(source); return NULL; } \
while ((size_t)needed >= capacity - len) { \
capacity *= 2; \
char *tmp = realloc(source, capacity); \
if (!tmp) { free(source); return NULL; } \
source = tmp; \
needed = snprintf(source + len, capacity - len, __VA_ARGS__); \
} \
len += needed; \
} while(0)
for (size_t i = 0; i < session->class_count; i++) {
if (session->classes[i].source) {
APPEND("%s\n\n", session->classes[i].source);
}
}
APPEND("public class _REPL_ {\n");
for (size_t i = 0; i < session->variable_count; i++) {
RavaREPLVariable_t *var = &session->variables[i];
const char *type = var->type_name ? var->type_name : "int";
APPEND(" public static %s %s", type, var->name);
if (var->is_initialized) {
if (var->type == RAVA_VAL_OBJECT && var->initializer) {
APPEND(" = %s", var->initializer);
} else {
switch (var->type) {
case RAVA_VAL_INT:
case RAVA_VAL_LONG:
APPEND(" = %ld", var->value.data.int_val);
break;
case RAVA_VAL_DOUBLE:
APPEND(" = %g", var->value.data.double_val);
break;
case RAVA_VAL_BOOLEAN:
APPEND(" = %s", var->value.data.bool_val ? "true" : "false");
break;
case RAVA_VAL_STRING:
if (var->value.data.string_val) {
APPEND(" = \"%s\"", var->value.data.string_val);
}
break;
default:
if (var->initializer) {
APPEND(" = %s", var->initializer);
}
break;
}
}
}
APPEND(";\n");
}
for (size_t i = 0; i < session->method_count; i++) {
if (session->methods[i].source) {
APPEND("\n public static %s\n", session->methods[i].source);
}
}
size_t expr_len = strlen(expr);
while (expr_len > 0 && (expr[expr_len-1] == ';' || isspace(expr[expr_len-1]))) expr_len--;
char *clean_expr = strndup(expr, expr_len);
char *qualified_expr = malloc(strlen(clean_expr) * 10 + 1024);
if (!qualified_expr) {
free(clean_expr);
free(source);
return NULL;
}
strcpy(qualified_expr, clean_expr);
for (size_t i = 0; i < session->variable_count; i++) {
const char *var_name = session->variables[i].name;
size_t var_len = strlen(var_name);
char *qualified = malloc(var_len + 8);
snprintf(qualified, var_len + 8, "_REPL_.%s", var_name);
char *new_expr = malloc(strlen(qualified_expr) * 10 + 1024);
char *dst = new_expr;
char *src = qualified_expr;
while (*src) {
if (strncmp(src, var_name, var_len) == 0) {
char before = (src == qualified_expr) ? ' ' : *(src - 1);
char after = *(src + var_len);
if (!isalnum(before) && before != '_' && before != '.' &&
!isalnum(after) && after != '_') {
strcpy(dst, qualified);
dst += strlen(qualified);
src += var_len;
continue;
}
}
*dst++ = *src++;
}
*dst = '\0';
free(qualified_expr);
qualified_expr = new_expr;
free(qualified);
}
APPEND("\n public static int _eval_() {\n");
APPEND(" System.out.println(%s);\n", qualified_expr);
APPEND(" return 0;\n");
APPEND(" }\n");
APPEND("}\n");
free(clean_expr);
free(qualified_expr);
#undef APPEND
return source;
}
static RavaREPLExecResult_e compile_and_run(RavaREPLExecutor_t *executor, const char *source) {
RavaREPLSession_t *session = executor->session;
if (session->debug_mode) {
fprintf(stderr, "[DEBUG] Compiling:\n%s\n", source);
}
RavaLexer_t *lexer = rava_lexer_create(source);
RavaParser_t *parser = rava_parser_create(lexer);
RavaASTNode_t *ast = rava_parser_parse(parser);
if (!ast || parser->had_error) {
rava_repl_output_error("Parse error", parser->error_message);
rava_parser_destroy(parser);
rava_lexer_destroy(lexer);
return RAVA_REPL_EXEC_PARSE_ERROR;
}
RavaSemanticAnalyzer_t *analyzer = rava_semantic_analyzer_create();
if (!rava_semantic_analyze(analyzer, ast)) {
rava_repl_output_error("Semantic error", analyzer->error_message);
rava_ast_node_destroy(ast);
rava_parser_destroy(parser);
rava_lexer_destroy(lexer);
rava_semantic_analyzer_destroy(analyzer);
return RAVA_REPL_EXEC_SEMANTIC_ERROR;
}
RavaIRGenerator_t *ir_gen = rava_ir_generator_create(analyzer);
RavaProgram_t *program = rava_ir_generate(ir_gen, ast);
if (!program) {
rava_repl_output_error("IR error", "Failed to generate bytecode");
rava_ast_node_destroy(ast);
rava_parser_destroy(parser);
rava_lexer_destroy(lexer);
rava_semantic_analyzer_destroy(analyzer);
rava_ir_generator_destroy(ir_gen);
return RAVA_REPL_EXEC_RUNTIME_ERROR;
}
RavaVM_t *vm = rava_vm_create(program);
struct timespec start_time, end_time;
clock_gettime(CLOCK_MONOTONIC, &start_time);
bool success = rava_vm_execute(vm, "_REPL_", "_eval_");
clock_gettime(CLOCK_MONOTONIC, &end_time);
double elapsed_ms = (end_time.tv_sec - start_time.tv_sec) * 1000.0 +
(end_time.tv_nsec - start_time.tv_nsec) / 1000000.0;
if (!success && vm->error_message) {
rava_repl_output_error("Runtime error", vm->error_message);
}
if (executor->show_timing || elapsed_ms >= 100.0) {
rava_repl_output_timing(elapsed_ms);
}
rava_vm_destroy(vm);
rava_program_destroy(program);
rava_ast_node_destroy(ast);
rava_parser_destroy(parser);
rava_lexer_destroy(lexer);
rava_semantic_analyzer_destroy(analyzer);
rava_ir_generator_destroy(ir_gen);
return success ? RAVA_REPL_EXEC_SUCCESS : RAVA_REPL_EXEC_RUNTIME_ERROR;
}
static RavaValueType_e type_from_string(const char *type_name) {
if (!type_name) return RAVA_VAL_NULL;
if (strcmp(type_name, "int") == 0) return RAVA_VAL_INT;
if (strcmp(type_name, "long") == 0) return RAVA_VAL_LONG;
if (strcmp(type_name, "double") == 0) return RAVA_VAL_DOUBLE;
if (strcmp(type_name, "float") == 0) return RAVA_VAL_FLOAT;
if (strcmp(type_name, "boolean") == 0) return RAVA_VAL_BOOLEAN;
if (strcmp(type_name, "char") == 0) return RAVA_VAL_CHAR;
if (strcmp(type_name, "String") == 0) return RAVA_VAL_STRING;
return RAVA_VAL_OBJECT;
}
static RavaValue_t parse_literal_value(const char *init, RavaValueType_e type) {
RavaValue_t value = rava_value_null();
if (!init) return value;
while (*init && isspace(*init)) init++;
switch (type) {
case RAVA_VAL_INT:
case RAVA_VAL_LONG:
value.type = type;
value.data.int_val = strtol(init, NULL, 10);
break;
case RAVA_VAL_DOUBLE:
case RAVA_VAL_FLOAT:
value.type = type;
value.data.double_val = strtod(init, NULL);
break;
case RAVA_VAL_BOOLEAN:
value.type = RAVA_VAL_BOOLEAN;
value.data.bool_val = (strncmp(init, "true", 4) == 0);
break;
case RAVA_VAL_STRING:
value.type = RAVA_VAL_STRING;
if (init[0] == '"') {
const char *start = init + 1;
const char *end = strchr(start, '"');
if (end) {
value.data.string_val = strndup(start, end - start);
}
}
break;
default:
break;
}
return value;
}
RavaREPLExecResult_e rava_repl_executor_execute_expression(RavaREPLExecutor_t *executor, const char *code) {
char *source = generate_expression_source(executor->session, code);
if (!source) return RAVA_REPL_EXEC_RUNTIME_ERROR;
RavaREPLExecResult_e result = compile_and_run(executor, source);
free(source);
return result;
}
RavaREPLExecResult_e rava_repl_executor_execute_statement(RavaREPLExecutor_t *executor, const char *code) {
char *source = generate_full_source(executor->session, code);
if (!source) return RAVA_REPL_EXEC_RUNTIME_ERROR;
RavaREPLExecResult_e result = compile_and_run(executor, source);
free(source);
return result;
}
RavaREPLExecResult_e rava_repl_executor_execute_var_decl(RavaREPLExecutor_t *executor, const char *code) {
char *var_name = extract_var_name(code);
char *var_type = extract_var_type(code);
char *var_init = extract_var_initializer(code);
if (!var_name || !var_type) {
free(var_name);
free(var_type);
free(var_init);
return rava_repl_executor_execute_statement(executor, code);
}
RavaValueType_e type = type_from_string(var_type);
RavaValue_t value = rava_value_null();
if (var_init && type != RAVA_VAL_OBJECT) {
value = parse_literal_value(var_init, type);
value.type = type;
} else {
value.type = type;
value.data.int_val = 0;
}
rava_repl_session_add_variable(executor->session, var_name, var_type, var_init, type, value);
char *source = generate_full_source(executor->session, NULL);
if (source) {
RavaREPLExecResult_e result = compile_and_run(executor, source);
free(source);
if (result != RAVA_REPL_EXEC_SUCCESS) {
free(var_name);
free(var_type);
free(var_init);
return result;
}
}
free(var_name);
free(var_type);
free(var_init);
return RAVA_REPL_EXEC_SUCCESS;
}
RavaREPLExecResult_e rava_repl_executor_execute_method_decl(RavaREPLExecutor_t *executor, const char *code) {
char *method_name = extract_method_name(code);
char *return_type = extract_method_return_type(code);
if (!method_name) {
rava_repl_output_error("Parse error", "Could not extract method name");
free(return_type);
return RAVA_REPL_EXEC_PARSE_ERROR;
}
rava_repl_session_add_method(executor->session, method_name, return_type ? return_type : "void", code);
char *source = generate_full_source(executor->session, NULL);
if (!source) {
free(method_name);
free(return_type);
return RAVA_REPL_EXEC_RUNTIME_ERROR;
}
RavaREPLExecResult_e result = compile_and_run(executor, source);
free(source);
if (result == RAVA_REPL_EXEC_SUCCESS) {
printf("Method '%s' defined.\n", method_name);
}
free(method_name);
free(return_type);
return result;
}
RavaREPLExecResult_e rava_repl_executor_execute_class_decl(RavaREPLExecutor_t *executor, const char *code) {
char *class_name = extract_class_name(code);
if (!class_name) {
rava_repl_output_error("Parse error", "Could not extract class name");
return RAVA_REPL_EXEC_PARSE_ERROR;
}
rava_repl_session_add_class(executor->session, class_name, code);
char *source = generate_full_source(executor->session, NULL);
if (!source) {
free(class_name);
return RAVA_REPL_EXEC_RUNTIME_ERROR;
}
RavaREPLExecResult_e result = compile_and_run(executor, source);
free(source);
if (result == RAVA_REPL_EXEC_SUCCESS) {
printf("Class '%s' defined.\n", class_name);
}
free(class_name);
return result;
}
RavaREPLExecResult_e rava_repl_executor_execute(RavaREPLExecutor_t *executor, const char *code) {
RavaREPLCodeType_e type = rava_repl_input_detect_type(code);
switch (type) {
case RAVA_REPL_CODE_EXPRESSION:
return rava_repl_executor_execute_expression(executor, code);
case RAVA_REPL_CODE_STATEMENT:
return rava_repl_executor_execute_statement(executor, code);
case RAVA_REPL_CODE_VAR_DECL:
return rava_repl_executor_execute_var_decl(executor, code);
case RAVA_REPL_CODE_METHOD_DECL:
return rava_repl_executor_execute_method_decl(executor, code);
case RAVA_REPL_CODE_CLASS_DECL:
return rava_repl_executor_execute_class_decl(executor, code);
default:
return rava_repl_executor_execute_expression(executor, code);
}
}
RavaValue_t rava_repl_executor_get_result(RavaREPLExecutor_t *executor) {
return executor->last_result;
}
void rava_repl_executor_interrupt(RavaREPLExecutor_t *executor) {
executor->interrupted = true;
}