251 lines
7.8 KiB
C
Raw Normal View History

2025-12-05 12:32:09 +01:00
#include "repl_input.h"
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
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;
}