#include "repl_input.h" #include #include #include RavaREPLInput_t* rava_repl_input_create(void) { RavaREPLInput_t *input = malloc(sizeof(RavaREPLInput_t)); if (!input) return NULL; input->capacity = RAVA_REPL_MAX_INPUT; input->buffer = malloc(input->capacity); if (!input->buffer) { free(input); return NULL; } input->buffer[0] = '\0'; input->size = 0; input->brace_depth = 0; input->bracket_depth = 0; input->paren_depth = 0; input->in_string = false; input->string_delimiter = 0; input->line_count = 0; return input; } void rava_repl_input_destroy(RavaREPLInput_t *input) { if (!input) return; free(input->buffer); free(input); } void rava_repl_input_reset(RavaREPLInput_t *input) { if (!input) return; input->buffer[0] = '\0'; input->size = 0; input->brace_depth = 0; input->bracket_depth = 0; input->paren_depth = 0; input->in_string = false; input->string_delimiter = 0; input->line_count = 0; } static void update_nesting(RavaREPLInput_t *input, const char *line) { for (const char *p = line; *p; p++) { if (input->in_string) { if (*p == '\\' && *(p + 1)) { p++; continue; } if (*p == input->string_delimiter) { input->in_string = false; } continue; } switch (*p) { case '"': case '\'': input->in_string = true; input->string_delimiter = *p; break; case '{': input->brace_depth++; break; case '}': input->brace_depth--; break; case '[': input->bracket_depth++; break; case ']': input->bracket_depth--; break; case '(': input->paren_depth++; break; case ')': input->paren_depth--; break; case '/': if (*(p + 1) == '/') { return; } break; } } } static bool ends_with_operator(const char *line) { size_t len = strlen(line); while (len > 0 && isspace(line[len - 1])) len--; if (len == 0) return false; char last = line[len - 1]; if (last == '+' || last == '-' || last == '*' || last == '/' || last == '%' || last == '&' || last == '|' || last == '^' || last == '=' || last == '<' || last == '>' || last == '!' || last == '?' || last == ':' || last == ',') { return true; } return false; } RavaREPLInputState_e rava_repl_input_append(RavaREPLInput_t *input, const char *line) { if (!input || !line) return RAVA_REPL_INPUT_ERROR; size_t line_len = strlen(line); if (input->size + line_len + 2 > input->capacity) { return RAVA_REPL_INPUT_ERROR; } if (input->size > 0) { input->buffer[input->size++] = '\n'; } memcpy(input->buffer + input->size, line, line_len); input->size += line_len; input->buffer[input->size] = '\0'; input->line_count++; update_nesting(input, line); if (input->in_string) return RAVA_REPL_INPUT_CONTINUE; if (input->brace_depth > 0) return RAVA_REPL_INPUT_CONTINUE; if (input->bracket_depth > 0) return RAVA_REPL_INPUT_CONTINUE; if (input->paren_depth > 0) return RAVA_REPL_INPUT_CONTINUE; if (ends_with_operator(input->buffer)) return RAVA_REPL_INPUT_CONTINUE; return RAVA_REPL_INPUT_COMPLETE; } const char* rava_repl_input_get(RavaREPLInput_t *input) { if (!input) return NULL; return input->buffer; } bool rava_repl_input_is_empty(RavaREPLInput_t *input) { if (!input) return true; return input->size == 0; } static bool starts_with_keyword(const char *code, const char *keyword) { size_t len = strlen(keyword); if (strncmp(code, keyword, len) != 0) return false; char next = code[len]; return next == ' ' || next == '\t' || next == '\n' || next == '(' || next == '{' || next == '['; } RavaREPLCodeType_e rava_repl_input_detect_type(const char *code) { while (*code && isspace(*code)) code++; if (starts_with_keyword(code, "public") || starts_with_keyword(code, "private") || starts_with_keyword(code, "protected")) { const char *p = code; while (*p && !isspace(*p)) p++; while (*p && isspace(*p)) p++; if (starts_with_keyword(p, "class")) { return RAVA_REPL_CODE_CLASS_DECL; } if (starts_with_keyword(p, "static")) { p += 6; while (*p && isspace(*p)) p++; } bool has_paren = false; const char *scan = p; while (*scan && *scan != ';' && *scan != '{') { if (*scan == '(') { has_paren = true; break; } scan++; } if (has_paren) { return RAVA_REPL_CODE_METHOD_DECL; } return RAVA_REPL_CODE_VAR_DECL; } if (starts_with_keyword(code, "class")) { return RAVA_REPL_CODE_CLASS_DECL; } if (starts_with_keyword(code, "interface")) { return RAVA_REPL_CODE_CLASS_DECL; } if (starts_with_keyword(code, "enum")) { return RAVA_REPL_CODE_CLASS_DECL; } if (starts_with_keyword(code, "int") || starts_with_keyword(code, "long") || starts_with_keyword(code, "double") || starts_with_keyword(code, "float") || starts_with_keyword(code, "boolean") || starts_with_keyword(code, "char") || starts_with_keyword(code, "byte") || starts_with_keyword(code, "short") || starts_with_keyword(code, "String") || starts_with_keyword(code, "void")) { const char *p = code; while (*p && !isspace(*p) && *p != '[') p++; if (*p == '[') { while (*p == '[' || *p == ']' || isspace(*p)) p++; } while (*p && isspace(*p)) p++; while (*p && (isalnum(*p) || *p == '_')) p++; while (*p && isspace(*p)) p++; if (*p == '[') { while (*p == '[' || *p == ']' || isspace(*p)) p++; } if (*p == '(') { return RAVA_REPL_CODE_METHOD_DECL; } if (*p == '=' || *p == ';' || *p == ',') { return RAVA_REPL_CODE_VAR_DECL; } } if (starts_with_keyword(code, "if") || starts_with_keyword(code, "while") || starts_with_keyword(code, "for") || starts_with_keyword(code, "do") || starts_with_keyword(code, "switch") || starts_with_keyword(code, "try") || starts_with_keyword(code, "return") || starts_with_keyword(code, "break") || starts_with_keyword(code, "continue") || starts_with_keyword(code, "throw")) { return RAVA_REPL_CODE_STATEMENT; } const char *p = code; if (isupper(*p)) { while (*p && (isalnum(*p) || *p == '_')) p++; if (*p == '[') { while (*p == '[' || *p == ']' || isspace(*p)) p++; } while (*p && isspace(*p)) p++; if (islower(*p) || *p == '_') { const char *var_start = p; while (*p && (isalnum(*p) || *p == '_')) p++; if (p > var_start) { while (*p && isspace(*p)) p++; if (*p == '=' || *p == ';') { return RAVA_REPL_CODE_VAR_DECL; } } } } p = code; bool has_semicolon = false; bool has_assign = false; while (*p) { if (*p == ';') has_semicolon = true; if (*p == '=' && *(p+1) != '=') has_assign = true; p++; } if (has_assign && has_semicolon) { return RAVA_REPL_CODE_STATEMENT; } return RAVA_REPL_CODE_EXPRESSION; }