#define _POSIX_C_SOURCE 200809L #include "repl_executor.h" #include "repl_input.h" #include "repl_output.h" #include #include #include #include #include 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; }