370 lines
12 KiB
C
370 lines
12 KiB
C
|
|
#define _POSIX_C_SOURCE 200809L
|
||
|
|
#include "repl_session.h"
|
||
|
|
#include <stdlib.h>
|
||
|
|
#include <string.h>
|
||
|
|
#include <stdio.h>
|
||
|
|
|
||
|
|
RavaREPLSession_t* rava_repl_session_create(void) {
|
||
|
|
RavaREPLSession_t *session = calloc(1, sizeof(RavaREPLSession_t));
|
||
|
|
if (!session) return NULL;
|
||
|
|
|
||
|
|
session->variable_capacity = RAVA_REPL_MAX_VARIABLES;
|
||
|
|
session->variables = calloc(session->variable_capacity, sizeof(RavaREPLVariable_t));
|
||
|
|
if (!session->variables) {
|
||
|
|
free(session);
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
session->method_capacity = RAVA_REPL_MAX_METHODS;
|
||
|
|
session->methods = calloc(session->method_capacity, sizeof(RavaREPLMethod_t));
|
||
|
|
if (!session->methods) {
|
||
|
|
free(session->variables);
|
||
|
|
free(session);
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
session->class_capacity = RAVA_REPL_MAX_CLASSES;
|
||
|
|
session->classes = calloc(session->class_capacity, sizeof(RavaREPLClass_t));
|
||
|
|
if (!session->classes) {
|
||
|
|
free(session->methods);
|
||
|
|
free(session->variables);
|
||
|
|
free(session);
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
session->history = rava_repl_history_create(RAVA_REPL_MAX_HISTORY);
|
||
|
|
session->start_time = time(NULL);
|
||
|
|
session->debug_mode = false;
|
||
|
|
session->execution_count = 0;
|
||
|
|
session->program = NULL;
|
||
|
|
session->session_class = NULL;
|
||
|
|
session->vm = NULL;
|
||
|
|
session->last_error = NULL;
|
||
|
|
|
||
|
|
return session;
|
||
|
|
}
|
||
|
|
|
||
|
|
void rava_repl_session_destroy(RavaREPLSession_t *session) {
|
||
|
|
if (!session) return;
|
||
|
|
|
||
|
|
for (size_t i = 0; i < session->variable_count; i++) {
|
||
|
|
free(session->variables[i].name);
|
||
|
|
free(session->variables[i].type_name);
|
||
|
|
free(session->variables[i].initializer);
|
||
|
|
}
|
||
|
|
free(session->variables);
|
||
|
|
|
||
|
|
for (size_t i = 0; i < session->method_count; i++) {
|
||
|
|
free(session->methods[i].name);
|
||
|
|
free(session->methods[i].return_type);
|
||
|
|
free(session->methods[i].source);
|
||
|
|
for (size_t j = 0; j < session->methods[i].param_count; j++) {
|
||
|
|
free(session->methods[i].param_names[j]);
|
||
|
|
free(session->methods[i].param_types[j]);
|
||
|
|
}
|
||
|
|
free(session->methods[i].param_names);
|
||
|
|
free(session->methods[i].param_types);
|
||
|
|
}
|
||
|
|
free(session->methods);
|
||
|
|
|
||
|
|
for (size_t i = 0; i < session->class_count; i++) {
|
||
|
|
free(session->classes[i].name);
|
||
|
|
free(session->classes[i].source);
|
||
|
|
for (size_t j = 0; j < session->classes[i].field_count; j++) {
|
||
|
|
free(session->classes[i].field_names[j]);
|
||
|
|
free(session->classes[i].field_types[j]);
|
||
|
|
}
|
||
|
|
free(session->classes[i].field_names);
|
||
|
|
free(session->classes[i].field_types);
|
||
|
|
for (size_t j = 0; j < session->classes[i].method_count; j++) {
|
||
|
|
free(session->classes[i].method_names[j]);
|
||
|
|
}
|
||
|
|
free(session->classes[i].method_names);
|
||
|
|
}
|
||
|
|
free(session->classes);
|
||
|
|
|
||
|
|
rava_repl_history_destroy(session->history);
|
||
|
|
|
||
|
|
if (session->vm) {
|
||
|
|
rava_vm_destroy(session->vm);
|
||
|
|
}
|
||
|
|
if (session->program) {
|
||
|
|
rava_program_destroy(session->program);
|
||
|
|
}
|
||
|
|
free(session->last_error);
|
||
|
|
free(session);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool rava_repl_session_add_variable(RavaREPLSession_t *session, const char *name,
|
||
|
|
const char *type_name, const char *initializer,
|
||
|
|
RavaValueType_e type, RavaValue_t value) {
|
||
|
|
if (!session || !name) return false;
|
||
|
|
|
||
|
|
RavaREPLVariable_t *existing = rava_repl_session_get_variable(session, name);
|
||
|
|
if (existing) {
|
||
|
|
free(existing->type_name);
|
||
|
|
free(existing->initializer);
|
||
|
|
existing->type_name = type_name ? strdup(type_name) : NULL;
|
||
|
|
existing->initializer = initializer ? strdup(initializer) : NULL;
|
||
|
|
existing->type = type;
|
||
|
|
existing->value = value;
|
||
|
|
existing->is_initialized = true;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (session->variable_count >= session->variable_capacity) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
RavaREPLVariable_t *var = &session->variables[session->variable_count++];
|
||
|
|
var->name = strdup(name);
|
||
|
|
var->type_name = type_name ? strdup(type_name) : NULL;
|
||
|
|
var->initializer = initializer ? strdup(initializer) : NULL;
|
||
|
|
var->type = type;
|
||
|
|
var->value = value;
|
||
|
|
var->is_initialized = true;
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
RavaREPLVariable_t* rava_repl_session_get_variable(RavaREPLSession_t *session, const char *name) {
|
||
|
|
if (!session || !name) return NULL;
|
||
|
|
for (size_t i = 0; i < session->variable_count; i++) {
|
||
|
|
if (strcmp(session->variables[i].name, name) == 0) {
|
||
|
|
return &session->variables[i];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool rava_repl_session_set_variable(RavaREPLSession_t *session, const char *name, RavaValue_t value) {
|
||
|
|
RavaREPLVariable_t *var = rava_repl_session_get_variable(session, name);
|
||
|
|
if (!var) return false;
|
||
|
|
var->value = value;
|
||
|
|
var->type = value.type;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
void rava_repl_session_clear_variables(RavaREPLSession_t *session) {
|
||
|
|
if (!session) return;
|
||
|
|
for (size_t i = 0; i < session->variable_count; i++) {
|
||
|
|
free(session->variables[i].name);
|
||
|
|
free(session->variables[i].type_name);
|
||
|
|
free(session->variables[i].initializer);
|
||
|
|
}
|
||
|
|
session->variable_count = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool rava_repl_session_add_method(RavaREPLSession_t *session, const char *name,
|
||
|
|
const char *return_type, const char *source) {
|
||
|
|
if (!session || !name) return false;
|
||
|
|
|
||
|
|
RavaREPLMethod_t *existing = rava_repl_session_get_method(session, name);
|
||
|
|
if (existing) {
|
||
|
|
free(existing->return_type);
|
||
|
|
free(existing->source);
|
||
|
|
existing->return_type = return_type ? strdup(return_type) : NULL;
|
||
|
|
existing->source = source ? strdup(source) : NULL;
|
||
|
|
existing->compiled = NULL;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (session->method_count >= session->method_capacity) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
RavaREPLMethod_t *method = &session->methods[session->method_count++];
|
||
|
|
method->name = strdup(name);
|
||
|
|
method->return_type = return_type ? strdup(return_type) : NULL;
|
||
|
|
method->source = source ? strdup(source) : NULL;
|
||
|
|
method->param_names = NULL;
|
||
|
|
method->param_types = NULL;
|
||
|
|
method->param_count = 0;
|
||
|
|
method->compiled = NULL;
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
RavaREPLMethod_t* rava_repl_session_get_method(RavaREPLSession_t *session, const char *name) {
|
||
|
|
if (!session || !name) return NULL;
|
||
|
|
for (size_t i = 0; i < session->method_count; i++) {
|
||
|
|
if (strcmp(session->methods[i].name, name) == 0) {
|
||
|
|
return &session->methods[i];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool rava_repl_session_add_class(RavaREPLSession_t *session, const char *name, const char *source) {
|
||
|
|
if (!session || !name) return false;
|
||
|
|
|
||
|
|
RavaREPLClass_t *existing = rava_repl_session_get_class(session, name);
|
||
|
|
if (existing) {
|
||
|
|
free(existing->source);
|
||
|
|
existing->source = source ? strdup(source) : NULL;
|
||
|
|
existing->compiled = NULL;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (session->class_count >= session->class_capacity) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
RavaREPLClass_t *cls = &session->classes[session->class_count++];
|
||
|
|
cls->name = strdup(name);
|
||
|
|
cls->source = source ? strdup(source) : NULL;
|
||
|
|
cls->field_names = NULL;
|
||
|
|
cls->field_types = NULL;
|
||
|
|
cls->field_count = 0;
|
||
|
|
cls->method_names = NULL;
|
||
|
|
cls->method_count = 0;
|
||
|
|
cls->compiled = NULL;
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
RavaREPLClass_t* rava_repl_session_get_class(RavaREPLSession_t *session, const char *name) {
|
||
|
|
if (!session || !name) return NULL;
|
||
|
|
for (size_t i = 0; i < session->class_count; i++) {
|
||
|
|
if (strcmp(session->classes[i].name, name) == 0) {
|
||
|
|
return &session->classes[i];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
char* rava_repl_session_generate_source(RavaREPLSession_t *session) {
|
||
|
|
if (!session) return NULL;
|
||
|
|
|
||
|
|
size_t capacity = 4096;
|
||
|
|
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; } \
|
||
|
|
if ((size_t)needed >= capacity - len) { \
|
||
|
|
capacity = capacity * 2 + needed; \
|
||
|
|
char *tmp = realloc(source, capacity); \
|
||
|
|
if (!tmp) { free(source); return NULL; } \
|
||
|
|
source = tmp; \
|
||
|
|
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_Session_ {\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;\n", type, var->name);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (session->variable_count > 0) {
|
||
|
|
APPEND("\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
for (size_t i = 0; i < session->method_count; i++) {
|
||
|
|
if (session->methods[i].source) {
|
||
|
|
APPEND(" %s\n\n", session->methods[i].source);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
APPEND("}\n");
|
||
|
|
|
||
|
|
#undef APPEND
|
||
|
|
|
||
|
|
return source;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool rava_repl_session_recompile(RavaREPLSession_t *session) {
|
||
|
|
if (!session) return false;
|
||
|
|
|
||
|
|
char *source = rava_repl_session_generate_source(session);
|
||
|
|
if (!source) {
|
||
|
|
rava_repl_session_set_error(session, "Failed to generate session source");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (session->debug_mode) {
|
||
|
|
fprintf(stderr, "[DEBUG] Session source:\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_session_set_error(session, parser->error_message ? parser->error_message : "Parse error");
|
||
|
|
rava_parser_destroy(parser);
|
||
|
|
rava_lexer_destroy(lexer);
|
||
|
|
free(source);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
RavaSemanticAnalyzer_t *analyzer = rava_semantic_analyzer_create();
|
||
|
|
if (!rava_semantic_analyze(analyzer, ast)) {
|
||
|
|
rava_repl_session_set_error(session, analyzer->error_message ? analyzer->error_message : "Semantic error");
|
||
|
|
rava_ast_node_destroy(ast);
|
||
|
|
rava_parser_destroy(parser);
|
||
|
|
rava_lexer_destroy(lexer);
|
||
|
|
rava_semantic_analyzer_destroy(analyzer);
|
||
|
|
free(source);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
RavaIRGenerator_t *ir_gen = rava_ir_generator_create(analyzer);
|
||
|
|
RavaProgram_t *program = rava_ir_generate(ir_gen, ast);
|
||
|
|
|
||
|
|
if (!program) {
|
||
|
|
rava_repl_session_set_error(session, "IR generation failed");
|
||
|
|
rava_ast_node_destroy(ast);
|
||
|
|
rava_parser_destroy(parser);
|
||
|
|
rava_lexer_destroy(lexer);
|
||
|
|
rava_semantic_analyzer_destroy(analyzer);
|
||
|
|
rava_ir_generator_destroy(ir_gen);
|
||
|
|
free(source);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (session->vm) {
|
||
|
|
rava_vm_destroy(session->vm);
|
||
|
|
}
|
||
|
|
if (session->program) {
|
||
|
|
rava_program_destroy(session->program);
|
||
|
|
}
|
||
|
|
|
||
|
|
session->program = program;
|
||
|
|
session->vm = rava_vm_create(program);
|
||
|
|
session->session_class = NULL;
|
||
|
|
|
||
|
|
rava_ast_node_destroy(ast);
|
||
|
|
rava_parser_destroy(parser);
|
||
|
|
rava_lexer_destroy(lexer);
|
||
|
|
rava_semantic_analyzer_destroy(analyzer);
|
||
|
|
rava_ir_generator_destroy(ir_gen);
|
||
|
|
free(source);
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
void rava_repl_session_set_error(RavaREPLSession_t *session, const char *error) {
|
||
|
|
if (!session) return;
|
||
|
|
free(session->last_error);
|
||
|
|
session->last_error = error ? strdup(error) : NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
const char* rava_repl_session_get_error(RavaREPLSession_t *session) {
|
||
|
|
if (!session) return NULL;
|
||
|
|
return session->last_error;
|
||
|
|
}
|