summaryrefslogtreecommitdiff
path: root/src/repl/repl.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/repl/repl.c')
-rw-r--r--src/repl/repl.c1587
1 files changed, 1333 insertions, 254 deletions
diff --git a/src/repl/repl.c b/src/repl/repl.c
index 99d7b43..b04b2c2 100644
--- a/src/repl/repl.c
+++ b/src/repl/repl.c
@@ -6,13 +6,969 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+#include <ctype.h>
ASTNode *parse_program(ParserContext *ctx, Lexer *l);
static int is_header_line(const char *line)
{
- return (strncmp(line, "import ", 7) == 0 || strncmp(line, "include ", 8) == 0 ||
- strncmp(line, "#include", 8) == 0);
+ // Skip whitespace
+ while (*line && (*line == ' ' || *line == '\t'))
+ {
+ line++;
+ }
+ if (strncmp(line, "struct", 6) == 0)
+ {
+ return 1;
+ }
+ if (strncmp(line, "impl", 4) == 0)
+ {
+ return 1;
+ }
+ if (strncmp(line, "fn", 2) == 0)
+ {
+ return 1;
+ }
+ if (strncmp(line, "use", 3) == 0)
+ {
+ return 1;
+ }
+ if (strncmp(line, "include", 7) == 0)
+ {
+ return 1;
+ }
+ if (strncmp(line, "typedef", 7) == 0)
+ {
+ return 1;
+ }
+ if (strncmp(line, "enum", 4) == 0)
+ {
+ return 1;
+ }
+ if (strncmp(line, "const", 5) == 0)
+ {
+ return 1;
+ }
+ if (strncmp(line, "def", 3) == 0)
+ {
+ return 1;
+ }
+ if (strncmp(line, "#include", 8) == 0)
+ {
+ return 1;
+ }
+ if (strncmp(line, "import", 6) == 0)
+ {
+ return 1;
+ }
+
+ return 0;
+}
+
+static void repl_error_callback(void *data, Token t, const char *msg)
+{
+ (void)data;
+ (void)t;
+ fprintf(stderr, "\033[1;31merror:\033[0m %s\n", msg);
+}
+
+static struct termios orig_termios;
+static int raw_mode_enabled = 0;
+
+static void disable_raw_mode()
+{
+ if (raw_mode_enabled)
+ {
+ tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios);
+ raw_mode_enabled = 0;
+ }
+}
+
+static void enable_raw_mode()
+{
+ if (!raw_mode_enabled)
+ {
+ tcgetattr(STDIN_FILENO, &orig_termios);
+ atexit(disable_raw_mode);
+ struct termios raw = orig_termios;
+ raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
+ raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+ raw.c_oflag &= ~(OPOST);
+ raw.c_cflag |= (CS8);
+ raw.c_cc[VMIN] = 1;
+ raw.c_cc[VTIME] = 0;
+ tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
+ raw_mode_enabled = 1;
+ }
+}
+
+static const char *KEYWORDS[] = {
+ "fn", "struct", "var", "let", "def", "const", "return", "if",
+ "else", "for", "while", "do", "switch", "case", "default", "break",
+ "continue", "typedef", "enum", "union", "sizeof", "typeof", "import", "include",
+ "defer", "guard", "match", "impl", "trait", "comptime", "asm", "plugin",
+ "true", "false", "null", "NULL", NULL};
+
+static const char *TYPES[] = {"void", "int", "char", "float", "double", "long",
+ "short", "unsigned", "signed", "bool", NULL};
+
+static int find_matching_brace(const char *buf, int pos)
+{
+ if (pos < 0 || pos >= (int)strlen(buf))
+ {
+ return -1;
+ }
+ char c = buf[pos];
+ int dir = 0;
+ char match = 0;
+ if (c == '{')
+ {
+ match = '}';
+ dir = 1;
+ }
+ else if (c == '(')
+ {
+ match = ')';
+ dir = 1;
+ }
+ else if (c == '[')
+ {
+ match = ']';
+ dir = 1;
+ }
+ else if (c == '}')
+ {
+ match = '{';
+ dir = -1;
+ }
+ else if (c == ')')
+ {
+ match = '(';
+ dir = -1;
+ }
+ else if (c == ']')
+ {
+ match = '[';
+ dir = -1;
+ }
+ else
+ {
+ return -1;
+ }
+
+ int depth = 1;
+ int p = pos + dir;
+ int len = strlen(buf);
+ while (p >= 0 && p < len)
+ {
+ if (buf[p] == c)
+ {
+ depth++;
+ }
+ else if (buf[p] == match)
+ {
+ depth--;
+ if (depth == 0)
+ {
+ return p;
+ }
+ }
+ p += dir;
+ }
+ return -1;
+}
+
+// Calculate visible length of a string (ignoring ANSI codes)
+static int get_visible_length(const char *str)
+{
+ int len = 0;
+ int in_esc = 0;
+ while (*str)
+ {
+ if (*str == '\033')
+ {
+ in_esc = 1;
+ }
+ else if (in_esc)
+ {
+ if (*str == 'm' || *str == 'K') // End of SGR or EL
+ {
+ in_esc = 0;
+ }
+ if (isalpha(*str))
+ {
+ in_esc = 0; // Terminating char
+ }
+ }
+ else
+ {
+ len++;
+ }
+ str++;
+ }
+ return len;
+}
+
+// Simple syntax highlighter for the REPL
+static void repl_highlight(const char *buf, int cursor_pos);
+
+static int is_definition_of(const char *code, const char *name)
+{
+ Lexer l;
+ lexer_init(&l, code);
+ Token t = lexer_next(&l);
+ int is_header = 0;
+
+ if (t.type == TOK_UNION)
+ {
+ is_header = 1;
+ }
+ else if (t.type == TOK_IDENT)
+ {
+ if ((t.len == 2 && strncmp(t.start, "fn", 2) == 0) ||
+ (t.len == 6 && strncmp(t.start, "struct", 6) == 0) ||
+ (t.len == 4 && strncmp(t.start, "enum", 4) == 0) ||
+ (t.len == 7 && strncmp(t.start, "typedef", 7) == 0) ||
+ (t.len == 5 && strncmp(t.start, "const", 5) == 0))
+ {
+ is_header = 1;
+ }
+ }
+
+ if (is_header)
+ {
+ Token name_tok = lexer_next(&l);
+ if (name_tok.type == TOK_IDENT)
+ {
+ if (strlen(name) == (size_t)name_tok.len &&
+ strncmp(name, name_tok.start, name_tok.len) == 0)
+ {
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+static void repl_highlight(const char *buf, int cursor_pos)
+{
+ const char *p = buf;
+
+ int match_pos = -1;
+ int brace_pos = -1;
+
+ // Check under cursor
+ if (find_matching_brace(buf, cursor_pos) != -1)
+ {
+ brace_pos = cursor_pos;
+ match_pos = find_matching_brace(buf, cursor_pos);
+ }
+ // Check before cursor (common behavior when typing)
+ else if (cursor_pos > 0 && find_matching_brace(buf, cursor_pos - 1) != -1)
+ {
+ brace_pos = cursor_pos - 1;
+ match_pos = find_matching_brace(buf, cursor_pos - 1);
+ }
+
+ while (*p)
+ {
+ long idx = p - buf;
+
+ // Highlight matching braces
+ if (idx == brace_pos || idx == match_pos)
+ {
+ printf("\033[1;44;37m"); // Bright White on Blue background
+ putchar(*p);
+ printf("\033[0m");
+ p++;
+ continue;
+ }
+
+ if (strncmp(p, "//", 2) == 0)
+ {
+ printf("\033[1;30m");
+ printf("%s", p);
+ printf("\033[0m");
+ break;
+ }
+ else if (*p == ':' && isalpha(p[1]))
+ {
+ printf("\033[1;35m");
+ while (*p && !isspace(*p))
+ {
+ putchar(*p);
+ p++;
+ }
+ printf("\033[0m");
+ }
+ else if (isdigit(*p))
+ {
+ printf("\033[1;35m");
+ while (isdigit(*p) || *p == '.' || *p == 'x' || *p == 'X')
+ {
+ putchar(*p);
+ p++;
+ }
+ printf("\033[0m");
+ }
+ else if (*p == '"' || *p == '\'')
+ {
+ char quote = *p;
+ printf("\033[1;32m");
+ putchar(*p);
+ p++;
+ while (*p && *p != quote)
+ {
+ if (*p == '\\' && p[1])
+ {
+ putchar(*p);
+ p++;
+ }
+ putchar(*p);
+ p++;
+ }
+ if (*p == quote)
+ {
+ putchar(*p);
+ p++;
+ }
+ printf("\033[0m");
+ }
+ else if (strchr(",;.", *p))
+ {
+ printf("\033[1;30m");
+ putchar(*p);
+ printf("\033[0m");
+ p++;
+ }
+ else if (strchr("{}[]()", *p))
+ {
+ printf("\033[0;36m");
+ putchar(*p);
+ printf("\033[0m");
+ p++;
+ }
+ else if (strchr("+-*/=<>!&|^~%", *p))
+ {
+ printf("\033[1;37m");
+ putchar(*p);
+ printf("\033[0m");
+ p++;
+ }
+ else if (isalpha(*p) || *p == '_')
+ {
+ const char *start = p;
+ while (isalnum(*p) || *p == '_')
+ {
+ p++;
+ }
+ int len = p - start;
+ char word[256];
+ if (len < 256)
+ {
+ strncpy(word, start, len);
+ word[len] = 0;
+
+ int is_keyword = 0;
+ for (int i = 0; KEYWORDS[i]; i++)
+ {
+ if (strcmp(word, KEYWORDS[i]) == 0)
+ {
+ is_keyword = 1;
+ break;
+ }
+ }
+
+ int is_type = 0;
+ if (!is_keyword)
+ {
+ for (int i = 0; TYPES[i]; i++)
+ {
+ if (strcmp(word, TYPES[i]) == 0)
+ {
+ is_type = 1;
+ break;
+ }
+ }
+ }
+
+ int is_func = 0;
+ if (!is_keyword && !is_type)
+ {
+ const char *peek = p;
+ while (*peek && isspace(*peek))
+ {
+ peek++;
+ }
+ if (*peek == '(')
+ {
+ is_func = 1;
+ }
+ }
+
+ int is_const = 0;
+ if (!is_keyword && !is_type && !is_func && len > 1)
+ {
+ int all_upper = 1;
+ int has_upper = 0;
+ for (int i = 0; word[i]; i++)
+ {
+ if (islower(word[i]))
+ {
+ all_upper = 0;
+ }
+ if (isupper(word[i]))
+ {
+ has_upper = 1;
+ }
+ }
+ if (all_upper && has_upper)
+ {
+ is_const = 1;
+ }
+ }
+
+ if (is_keyword)
+ {
+ printf("\033[1;36m");
+ }
+ else if (is_type)
+ {
+ printf("\033[1;33m");
+ }
+ else if (is_func)
+ {
+ printf("\033[1;34m");
+ }
+ else if (is_const)
+ {
+ printf("\033[1;31m");
+ }
+
+ printf("%s", word);
+ printf("\033[0m");
+ }
+ else
+ {
+ printf("%.*s", len, start);
+ }
+ }
+ else
+ {
+ putchar(*p);
+ p++;
+ }
+ }
+}
+
+static char *repl_complete(const char *buf, int pos)
+{
+ int start = pos;
+ while (start > 0 && (isalnum(buf[start - 1]) || buf[start - 1] == '_' ||
+ buf[start - 1] == ':' || buf[start - 1] == '!'))
+ {
+ start--;
+ }
+
+ int len = pos - start;
+ if (len == 0)
+ {
+ return NULL;
+ }
+
+ char prefix[256];
+ if (len >= 255)
+ {
+ return NULL;
+ }
+ strncpy(prefix, buf + start, len);
+ prefix[len] = 0;
+
+ char *match = NULL;
+ int match_count = 0;
+
+ for (int i = 0; KEYWORDS[i]; i++)
+ {
+ if (strncmp(KEYWORDS[i], prefix, len) == 0)
+ {
+ match = (char *)KEYWORDS[i];
+ match_count++;
+ }
+ }
+
+ static const char *COMMANDS[] = {
+ ":help", ":reset", ":imports", ":vars", ":funcs", ":structs", ":history", ":type",
+ ":time", ":c", ":doc", ":run", ":edit", ":save", ":load", ":watch",
+ ":unwatch", ":undo", ":delete", ":clear", ":quit", NULL};
+
+ if (prefix[0] == ':')
+ {
+ for (int i = 0; COMMANDS[i]; i++)
+ {
+ if (strncmp(COMMANDS[i], prefix, len) == 0)
+ {
+ match = (char *)COMMANDS[i];
+ match_count++;
+ }
+ }
+ }
+
+ if (match_count == 1)
+ {
+ return strdup(match + len);
+ }
+
+ return NULL;
+}
+
+static char *repl_readline(const char *prompt, char **history, int history_len, int indent_level)
+{
+ enable_raw_mode();
+
+ int buf_size = 1024;
+ char *buf = malloc(buf_size);
+ buf[0] = 0;
+ int len = 0;
+ int pos = 0;
+
+ if (indent_level > 0)
+ {
+ for (int i = 0; i < indent_level * 4; i++)
+ {
+ if (len >= buf_size - 1)
+ {
+ buf_size *= 2;
+ buf = realloc(buf, buf_size);
+ }
+ buf[len++] = ' ';
+ }
+ buf[len] = 0;
+ pos = len;
+ }
+
+ int history_idx = history_len;
+ char *saved_current_line = NULL;
+
+ int in_search_mode = 0;
+ char search_buf[256];
+ search_buf[0] = 0;
+ int search_match_idx = -1;
+
+ printf("\r\033[K%s", prompt);
+ repl_highlight(buf, pos);
+ fflush(stdout);
+
+ while (1)
+ {
+ char c;
+ if (read(STDIN_FILENO, &c, 1) != 1)
+ {
+ break;
+ }
+
+ if (c == '\x1b')
+ {
+ char seq[3];
+ if (read(STDIN_FILENO, &seq[0], 1) != 1)
+ {
+ continue;
+ }
+ if (read(STDIN_FILENO, &seq[1], 1) != 1)
+ {
+ continue;
+ }
+
+ if (seq[0] == '[')
+ {
+ if (seq[1] == 'A')
+ {
+ if (history_idx > 0)
+ {
+ if (history_idx == history_len)
+ {
+ if (saved_current_line)
+ {
+ free(saved_current_line);
+ }
+ saved_current_line = strdup(buf);
+ }
+ history_idx--;
+ if (history_idx >= 0 && history_idx < history_len)
+ {
+ free(buf);
+ buf = strdup(history[history_idx]);
+ buf_size = strlen(buf) + 1;
+ len = strlen(buf);
+ pos = len;
+ }
+ }
+ }
+ else if (seq[1] == 'B')
+ {
+ if (history_idx < history_len)
+ {
+ history_idx++;
+ free(buf);
+ if (history_idx == history_len)
+ {
+ if (saved_current_line)
+ {
+ buf = strdup(saved_current_line);
+ }
+ else
+ {
+ buf = strdup("");
+ }
+ }
+ else
+ {
+ buf = strdup(history[history_idx]);
+ }
+ buf_size = strlen(buf) + 1;
+ len = strlen(buf);
+ pos = len;
+ }
+ }
+ else if (seq[1] == 'C')
+ {
+ if (pos < len)
+ {
+ pos++;
+ }
+ }
+ else if (seq[1] == 'D')
+ {
+ if (pos > 0)
+ {
+ pos--;
+ }
+ }
+ else if (seq[1] == 'H')
+ {
+ pos = 0;
+ }
+ else if (seq[1] == 'F')
+ {
+ pos = len;
+ }
+ }
+ }
+ else if (c == 127 || c == 8)
+ {
+ if (pos > 0)
+ {
+ memmove(buf + pos - 1, buf + pos, len - pos + 1);
+ len--;
+ pos--;
+ }
+ }
+ else if (c == '\r' || c == '\n')
+ {
+ printf("\r\n");
+ break;
+ }
+ else if (c == 3)
+ {
+ printf("^C\r\n");
+ free(buf);
+ if (saved_current_line)
+ {
+ free(saved_current_line);
+ }
+ disable_raw_mode();
+ return strdup("");
+ }
+ else if (c == 4)
+ {
+ if (len == 0)
+ {
+ free(buf);
+ if (saved_current_line)
+ {
+ free(saved_current_line);
+ }
+ disable_raw_mode();
+ return NULL;
+ }
+ }
+ else if (c == '\t')
+ {
+ char *completion = repl_complete(buf, pos);
+ if (completion)
+ {
+ int clen = strlen(completion);
+ if (len + clen < buf_size - 1)
+ {
+ // Insert completion
+ memmove(buf + pos + clen, buf + pos, len - pos + 1);
+ memcpy(buf + pos, completion, clen);
+ len += clen;
+ pos += clen;
+ }
+ free(completion);
+ }
+ }
+ else if (c == 18)
+ {
+ if (!in_search_mode)
+ {
+ in_search_mode = 1;
+ search_buf[0] = 0;
+ search_match_idx = history_len;
+ }
+
+ int found = -1;
+ int start_idx = search_match_idx - 1;
+ if (start_idx >= history_len)
+ {
+ start_idx = history_len - 1;
+ }
+
+ for (int i = start_idx; i >= 0; i--)
+ {
+ if (strstr(history[i], search_buf))
+ {
+ found = i;
+ break;
+ }
+ }
+
+ if (found != -1)
+ {
+ search_match_idx = found;
+ free(buf);
+ buf = strdup(history[found]);
+ buf_size = strlen(buf) + 1;
+ len = strlen(buf);
+ pos = len;
+ history_idx = found; // Sync history navigation
+ }
+ }
+ else if (in_search_mode)
+ {
+ if (c == 127 || c == 8) // Backspace
+ {
+ int sl = strlen(search_buf);
+ if (sl > 0)
+ {
+ search_buf[sl - 1] = 0;
+ search_match_idx = history_len;
+ int found = -1;
+ for (int i = history_len - 1; i >= 0; i--)
+ {
+ if (strstr(history[i], search_buf))
+ {
+ found = i;
+ break;
+ }
+ }
+ if (found != -1)
+ {
+ search_match_idx = found;
+ free(buf);
+ buf = strdup(history[found]);
+ buf_size = strlen(buf) + 1;
+ len = strlen(buf);
+ pos = len;
+ history_idx = found;
+ }
+ }
+ }
+ else if (c == '\r' || c == '\n' || c == 27 || c == 7 ||
+ c == 3) // Enter/Esc/Ctrl+G/Ctrl+C
+ {
+ in_search_mode = 0;
+ if (c == 3)
+ {
+ // Abort
+ free(buf);
+ buf = strdup("");
+ len = 0;
+ pos = 0;
+ printf("^C\r\n");
+ return buf;
+ }
+ if (c == 7)
+ {
+ // Keep current match
+ }
+ else if (c == '\r' || c == '\n')
+ {
+ printf("\r\n");
+ break;
+ }
+ }
+ else if (!iscntrl(c))
+ {
+ int sl = strlen(search_buf);
+ if (sl < 255)
+ {
+ search_buf[sl] = c;
+ search_buf[sl + 1] = 0;
+
+ int found = -1;
+ for (int i = history_len - 1; i >= 0; i--)
+ {
+ if (strstr(history[i], search_buf))
+ {
+ found = i;
+ break;
+ }
+ }
+ if (found != -1)
+ {
+ search_match_idx = found;
+ free(buf);
+ buf = strdup(history[found]);
+ buf_size = strlen(buf) + 1;
+ len = strlen(buf);
+ pos = len;
+ history_idx = found;
+ }
+ }
+ }
+ }
+ else if (c == 1)
+ {
+ pos = 0;
+ }
+ else if (c == 5)
+ {
+ pos = len;
+ }
+ else if (c == 12)
+ {
+ printf("\033[2J\033[H");
+ }
+ else if (c == 21)
+ {
+ if (pos > 0)
+ {
+ memmove(buf, buf + pos, len - pos + 1);
+ len -= pos;
+ pos = 0;
+ }
+ }
+ else if (c == 11)
+ {
+ buf[pos] = 0;
+ len = pos;
+ }
+ else if (c == 14)
+ {
+ printf("^N\r\n");
+ free(buf);
+ if (saved_current_line)
+ {
+ free(saved_current_line);
+ }
+ disable_raw_mode();
+ return strdup(":reset");
+ }
+ else if (!iscntrl(c))
+ {
+ if (len >= buf_size - 1)
+ {
+ buf_size *= 2;
+ buf = realloc(buf, buf_size);
+ }
+ memmove(buf + pos + 1, buf + pos, len - pos + 1);
+ buf[pos] = c;
+ len++;
+ pos++;
+ }
+
+ if (in_search_mode)
+ {
+ printf("\r\033[K(reverse-i-search)`%s': %s", search_buf, buf);
+ }
+ else
+ {
+ printf("\r\033[K%s", prompt);
+ repl_highlight(buf, pos);
+ int prompt_len = get_visible_length(prompt);
+ if (pos + prompt_len > 0)
+ {
+ printf("\r\033[%dC", pos + prompt_len);
+ }
+ else
+ {
+ printf("\r");
+ }
+ }
+
+ fflush(stdout);
+ }
+
+ if (saved_current_line)
+ {
+ free(saved_current_line);
+ }
+ disable_raw_mode();
+
+ return buf;
+}
+
+static void repl_get_code(char **history, int len, char **out_global, char **out_main)
+{
+ size_t total_len = 0;
+ for (int i = 0; i < len; i++)
+ {
+ total_len += strlen(history[i]) + 2;
+ }
+
+ char *global_buf = malloc(total_len + 1);
+ char *main_buf = malloc(total_len + 1);
+ global_buf[0] = 0;
+ main_buf[0] = 0;
+
+ int brace_depth = 0;
+ int in_global = 0;
+
+ for (int i = 0; i < len; i++)
+ {
+ char *line = history[i];
+
+ if (brace_depth == 0)
+ {
+ if (is_header_line(line))
+ {
+ in_global = 1;
+ }
+ else
+ {
+ in_global = 0;
+ }
+ }
+
+ if (in_global)
+ {
+ strcat(global_buf, line);
+ strcat(global_buf, "\n");
+ }
+ else
+ {
+ strcat(main_buf, line);
+ strcat(main_buf, " ");
+ }
+
+ for (char *p = line; *p; p++)
+ {
+ if (*p == '{')
+ {
+ brace_depth++;
+ }
+ else if (*p == '}')
+ {
+ brace_depth--;
+ }
+ }
+ }
+
+ *out_global = global_buf;
+ *out_main = main_buf;
}
void run_repl(const char *self_path)
@@ -21,7 +977,6 @@ void run_repl(const char *self_path)
printf("Type 'exit' or 'quit' to leave.\n");
printf("Type :help for commands.\n");
- // Dynamic history.
int history_cap = 64;
int history_len = 0;
char **history = xmalloc(history_cap * sizeof(char *));
@@ -69,7 +1024,6 @@ void run_repl(const char *self_path)
history_path[0] = 0;
}
- // Watch list.
char *watches[16];
int watches_len = 0;
for (int i = 0; i < 16; i++)
@@ -77,7 +1031,6 @@ void run_repl(const char *self_path)
watches[i] = NULL;
}
- // Load startup file (~/.zprep_init.zc) if exists
if (home)
{
char init_path[512];
@@ -128,21 +1081,39 @@ void run_repl(const char *self_path)
while (1)
{
- if (brace_depth > 0 || paren_depth > 0)
+ char cwd[1024];
+ char prompt_text[1280];
+ if (getcwd(cwd, sizeof(cwd)))
{
- printf("... ");
+ char *base = strrchr(cwd, '/');
+ if (base)
+ {
+ base++;
+ }
+ else
+ {
+ base = cwd;
+ }
+ snprintf(prompt_text, sizeof(prompt_text), "\033[1;32m%s >>>\033[0m ", base);
}
else
{
- printf("\033[1;32m>>>\033[0m ");
+ strcpy(prompt_text, "\033[1;32m>>>\033[0m ");
}
- if (!fgets(line_buf, sizeof(line_buf), stdin))
+ const char *prompt = (brace_depth > 0 || paren_depth > 0) ? "... " : prompt_text;
+ int indent = (brace_depth > 0) ? brace_depth : 0;
+ char *rline = repl_readline(prompt, history, history_len, indent);
+
+ if (!rline)
{
break;
}
+ strncpy(line_buf, rline, sizeof(line_buf) - 2);
+ line_buf[sizeof(line_buf) - 2] = 0;
+ strcat(line_buf, "\n");
+ free(rline);
- // Handle commands (only on fresh line).
if (NULL == input_buffer)
{
size_t len = strlen(line_buf);
@@ -162,8 +1133,13 @@ void run_repl(const char *self_path)
break;
}
- // Commands
- if (cmd_buf[0] == ':' || cmd_buf[0] == '!')
+ if (cmd_buf[0] == '!')
+ {
+ int ret = system(cmd_buf + 1);
+ printf("(exit code: %d)\n", ret);
+ continue;
+ }
+ if (cmd_buf[0] == ':')
{
if (0 == strcmp(cmd_buf, ":help"))
{
@@ -183,14 +1159,21 @@ void run_repl(const char *self_path)
printf(" :edit [n] Edit command n (default: last) in $EDITOR\n");
printf(" :save <f> Save session to file\n");
printf(" :load <f> Load file into session\n");
- printf(" :load <f> Load file into session\n");
- printf(" :watch <x> Watch expression output\n");
- printf(" :unwatch <n> Remove watch n\n");
printf(" :undo Remove last command\n");
printf(" :delete <n> Remove command at index n\n");
+ printf(" :watch <x> Watch expression output\n");
+ printf(" :unwatch <n> Remove watch n\n");
printf(" :clear Clear screen\n");
printf(" ! <cmd> Run shell command\n");
printf(" :quit Exit REPL\n");
+ printf("\nShortcuts:\n");
+ printf(" Up/Down History navigation\n");
+ printf(" Tab Completion\n");
+ printf(" Ctrl+A Go to start\n");
+ printf(" Ctrl+E Go to end\n");
+ printf(" Ctrl+L Clear screen\n");
+ printf(" Ctrl+U Clear line to start\n");
+ printf(" Ctrl+K Clear line to end\n");
continue;
}
else if (0 == strcmp(cmd_buf, ":reset"))
@@ -207,9 +1190,107 @@ void run_repl(const char *self_path)
{
break;
}
+ else if (0 == strncmp(cmd_buf, ":show ", 6))
+ {
+ char *name = cmd_buf + 6;
+ while (*name && isspace(*name))
+ {
+ name++;
+ }
+
+ int found = 0;
+ printf("Source definition for '%s':\n", name);
+
+ for (int i = history_len - 1; i >= 0; i--)
+ {
+ if (is_definition_of(history[i], name))
+ {
+ printf(" \033[90m// Found in history:\033[0m\n");
+ printf(" ");
+ repl_highlight(history[i], -1);
+ printf("\n");
+ found = 1;
+ break;
+ }
+ }
+
+ if (found)
+ {
+ continue;
+ }
+
+ printf("Source definition for '%s':\n", name);
+
+ size_t show_code_size = 4096;
+ for (int i = 0; i < history_len; i++)
+ {
+ show_code_size += strlen(history[i]) + 2;
+ }
+ char *show_code = malloc(show_code_size);
+ strcpy(show_code, "");
+ for (int i = 0; i < history_len; i++)
+ {
+ strcat(show_code, history[i]);
+ strcat(show_code, "\n");
+ }
+
+ ParserContext ctx = {0};
+ ctx.is_repl = 1;
+ ctx.skip_preamble = 1;
+ ctx.is_fault_tolerant = 1;
+ ctx.on_error = repl_error_callback;
+ Lexer l;
+ lexer_init(&l, show_code);
+ ASTNode *nodes = parse_program(&ctx, &l);
+
+ ASTNode *search = nodes;
+ if (search && search->type == NODE_ROOT)
+ {
+ search = search->root.children;
+ }
+
+ for (ASTNode *n = search; n; n = n->next)
+ {
+ if (n->type == NODE_FUNCTION && 0 == strcmp(n->func.name, name))
+ {
+ printf(" fn %s(%s) -> %s\n", n->func.name,
+ n->func.args ? n->func.args : "",
+ n->func.ret_type ? n->func.ret_type : "void");
+ found = 1;
+ break;
+ }
+ else if (n->type == NODE_STRUCT && 0 == strcmp(n->strct.name, name))
+ {
+ printf(" struct %s {\n", n->strct.name);
+ for (ASTNode *field = n->strct.fields; field; field = field->next)
+ {
+ if (field->type == NODE_FIELD)
+ {
+ printf(" %s: %s;\n", field->field.name, field->field.type);
+ }
+ else if (field->type == NODE_VAR_DECL)
+ {
+ // Fields might be VAR_DECLs in some parses? No, usually
+ // NODE_FIELD for structs.
+ printf(" %s: %s;\n", field->var_decl.name,
+ field->var_decl.type_str);
+ }
+ }
+ printf(" }\n");
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ {
+ printf(" (not found)\n");
+ }
+ free(show_code);
+ continue;
+ }
else if (0 == strcmp(cmd_buf, ":clear"))
{
- printf("\033[2J\033[H"); // ANSI clear screen
+ printf("\033[2J\033[H");
continue;
}
else if (0 == strcmp(cmd_buf, ":undo"))
@@ -289,9 +1370,7 @@ void run_repl(const char *self_path)
const char *editor = getenv("EDITOR");
if (!editor)
{
- editor = "nano"; // Default fallback,
- // 'cause I know some of you
- // don't know how to exit Vim.
+ editor = "nano";
}
char cmd[1024];
@@ -300,7 +1379,6 @@ void run_repl(const char *self_path)
if (0 == status)
{
- // Read back file.
FILE *fr = fopen(edit_path, "r");
if (fr)
{
@@ -415,23 +1493,15 @@ void run_repl(const char *self_path)
FILE *f = fopen(filename, "w");
if (f)
{
- for (int i = 0; i < history_len; i++)
- {
- if (is_header_line(history[i]))
- {
- fprintf(f, "%s\n", history[i]);
- }
- }
- // Write main function body.
- fprintf(f, "\nfn main() {\n");
- for (int i = 0; i < history_len; i++)
- {
- if (!is_header_line(history[i]))
- {
- fprintf(f, " %s\n", history[i]);
- }
- }
- fprintf(f, "}\n");
+ char *global_code = NULL;
+ char *main_code = NULL;
+ repl_get_code(history, history_len, &global_code, &main_code);
+
+ fprintf(f, "%s\n", global_code);
+ fprintf(f, "\nfn main() {\n%s\n}\n", main_code);
+
+ free(global_code);
+ free(main_code);
fclose(f);
printf("Session saved to %s\n", filename);
}
@@ -501,36 +1571,21 @@ void run_repl(const char *self_path)
else if (0 == strcmp(cmd_buf, ":vars") || 0 == strcmp(cmd_buf, ":funcs") ||
0 == strcmp(cmd_buf, ":structs"))
{
- size_t code_size = 4096;
- for (int i = 0; i < history_len; i++)
- {
- code_size += strlen(history[i]) + 2;
- }
- char *code = malloc(code_size + 128);
- strcpy(code, "");
+ char *global_code = NULL;
+ char *main_code = NULL;
+ repl_get_code(history, history_len, &global_code, &main_code);
- for (int i = 0; i < history_len; i++)
- {
- if (is_header_line(history[i]))
- {
- strcat(code, history[i]);
- strcat(code, "\n");
- }
- }
- strcat(code, "fn main() { ");
- for (int i = 0; i < history_len; i++)
- {
- if (!is_header_line(history[i]))
- {
- strcat(code, history[i]);
- strcat(code, " ");
- }
- }
- strcat(code, " }");
+ size_t code_size = strlen(global_code) + strlen(main_code) + 128;
+ char *code = malloc(code_size);
+ sprintf(code, "%s\nfn main() { %s }", global_code, main_code);
+ free(global_code);
+ free(main_code);
ParserContext ctx = {0};
ctx.is_repl = 1;
ctx.skip_preamble = 1;
+ ctx.is_fault_tolerant = 1;
+ ctx.on_error = repl_error_callback;
Lexer l;
lexer_init(&l, code);
@@ -553,11 +1608,27 @@ void run_repl(const char *self_path)
break;
}
}
- printf("Variables:\n");
+
+ // Generate probe code to print values
+ char *global_code = NULL;
+ char *main_code = NULL;
+ repl_get_code(history, history_len, &global_code, &main_code);
+
+ // Generate probe code to print values
+ size_t probe_size = strlen(global_code) + strlen(main_code) + 4096;
+ char *probe_code = malloc(probe_size);
+
+ sprintf(probe_code,
+ "%s\nfn main() { _z_suppress_stdout(); %s _z_restore_stdout(); "
+ "printf(\"Variables:\\n\"); ",
+ global_code, main_code);
+ free(global_code);
+ free(main_code);
+
+ int found_vars = 0;
if (main_func && main_func->func.body &&
main_func->func.body->type == NODE_BLOCK)
{
- int found = 0;
for (ASTNode *s = main_func->func.body->block.statements; s;
s = s->next)
{
@@ -565,19 +1636,92 @@ void run_repl(const char *self_path)
{
char *t =
s->var_decl.type_str ? s->var_decl.type_str : "Inferred";
- printf(" %s: %s\n", s->var_decl.name, t);
- found = 1;
+ // Heuristic for format
+ char fmt[64];
+ char val_expr[128];
+
+ if (s->var_decl.type_str)
+ {
+ if (strcmp(t, "int") == 0 || strcmp(t, "i32") == 0)
+ {
+ strcpy(fmt, "%d");
+ strcpy(val_expr, s->var_decl.name);
+ }
+ else if (strcmp(t, "i64") == 0)
+ {
+ strcpy(fmt, "%ld");
+ sprintf(val_expr, "(long)%s", s->var_decl.name);
+ }
+ else if (strcmp(t, "float") == 0 ||
+ strcmp(t, "double") == 0 ||
+ strcmp(t, "f32") == 0 || strcmp(t, "f64") == 0)
+ {
+ strcpy(fmt, "%f");
+ strcpy(val_expr, s->var_decl.name);
+ }
+ else if (strcmp(t, "bool") == 0)
+ {
+ strcpy(fmt, "%s");
+ sprintf(val_expr, "%s ? \"true\" : \"false\"",
+ s->var_decl.name);
+ }
+ else if (strcmp(t, "string") == 0 ||
+ strcmp(t, "char*") == 0)
+ {
+ strcpy(fmt, "\\\"%s\\\"");
+ strcpy(val_expr, s->var_decl.name);
+ } // quote strings
+ else if (strcmp(t, "char") == 0)
+ {
+ strcpy(fmt, "'%c'");
+ strcpy(val_expr, s->var_decl.name);
+ }
+ else
+ {
+ // Fallback: address
+ strcpy(fmt, "@%p");
+ sprintf(val_expr, "(void*)&%s", s->var_decl.name);
+ }
+ }
+ else
+ {
+ // Inferred: Safe fallback? Or try to guess?
+ // For now, minimal safety: print address
+ strcpy(fmt, "? @%p");
+ sprintf(val_expr, "(void*)&%s", s->var_decl.name);
+ }
+
+ char print_stmt[512];
+ snprintf(print_stmt, sizeof(print_stmt),
+ "printf(\" %s (%s): %s\\n\", %s); ", s->var_decl.name,
+ t, fmt, val_expr);
+ strcat(probe_code, print_stmt);
+ found_vars = 1;
}
}
- if (!found)
- {
- printf(" (none)\n");
- }
}
- else
+
+ if (!found_vars)
{
- printf(" (none)\n");
+ strcat(probe_code, "printf(\" (none)\\n\");");
+ }
+
+ strcat(probe_code, " }");
+
+ // Execute
+ char tmp_path[256];
+ snprintf(tmp_path, sizeof(tmp_path), "/tmp/zen_repl_vars_%d.zc", getpid());
+ FILE *f = fopen(tmp_path, "w");
+ if (f)
+ {
+ fprintf(f, "%s", probe_code);
+ fclose(f);
+ char cmd[512];
+ snprintf(cmd, sizeof(cmd), "%s run -q %s", self_path, tmp_path);
+ system(cmd);
+ remove(tmp_path);
}
+ free(probe_code);
}
else if (0 == strcmp(cmd_buf, ":funcs"))
{
@@ -621,36 +1765,21 @@ void run_repl(const char *self_path)
{
char *expr = cmd_buf + 6;
- size_t probe_size = 4096;
- for (int i = 0; i < history_len; i++)
- {
- probe_size += strlen(history[i]) + 2;
- }
+ char *global_code = NULL;
+ char *main_code = NULL;
+ repl_get_code(history, history_len, &global_code, &main_code);
- char *probe_code = malloc(probe_size + strlen(expr) + 256);
- strcpy(probe_code, "");
+ size_t probe_size =
+ strlen(global_code) + strlen(main_code) + strlen(expr) + 4096;
+ char *probe_code = malloc(probe_size);
- for (int i = 0; i < history_len; i++)
- {
- if (is_header_line(history[i]))
- {
- strcat(probe_code, history[i]);
- strcat(probe_code, "\n");
- }
- }
-
- strcat(probe_code, "fn main() { _z_suppress_stdout(); ");
- for (int i = 0; i < history_len; i++)
- {
- if (!is_header_line(history[i]))
- {
- strcat(probe_code, history[i]);
- strcat(probe_code, " ");
- }
- }
+ sprintf(probe_code, "%s\nfn main() { _z_suppress_stdout(); %s", global_code,
+ main_code);
+ free(global_code);
+ free(main_code);
strcat(probe_code, " raw { typedef struct { int _u; } __REVEAL_TYPE__; } ");
- strcat(probe_code, " var _z_type_probe: __REVEAL_TYPE__; _z_type_probe = (");
+ strcat(probe_code, " let _z_type_probe: __REVEAL_TYPE__; _z_type_probe = (");
strcat(probe_code, expr);
strcat(probe_code, "); }");
@@ -686,18 +1815,51 @@ void run_repl(const char *self_path)
int found = 0;
while (fgets(buf, sizeof(buf), p))
{
- char *marker = "right operand has type '";
- char *start = strstr(buf, marker);
+ char *start = strstr(buf, "from type ");
+ char quote = 0;
+ if (!start)
+ {
+ start = strstr(buf, "incompatible type ");
+ }
+
if (start)
{
- start += strlen(marker);
- char *end = strchr(start, '\'');
- if (end)
+ char *q = strchr(start, '\'');
+ if (!q)
{
- *end = 0;
- printf("\033[1;36mType: %s\033[0m\n", start);
- found = 1;
- break;
+ q = strstr(start, "\xe2\x80\x98");
+ }
+
+ if (q)
+ {
+ if (*q == '\'')
+ {
+ start = q + 1;
+ quote = '\'';
+ }
+ else
+ {
+ start = q + 3;
+ quote = 0;
+ }
+
+ char *end = NULL;
+ if (quote)
+ {
+ end = strchr(start, quote);
+ }
+ else
+ {
+ end = strstr(start, "\xe2\x80\x99");
+ }
+
+ if (end)
+ {
+ *end = 0;
+ printf("\033[1;36mType: %s\033[0m\n", start);
+ found = 1;
+ break;
+ }
}
}
}
@@ -716,33 +1878,21 @@ void run_repl(const char *self_path)
// Benchmark an expression.
char *expr = cmd_buf + 6;
- size_t code_size = 4096;
- for (int i = 0; i < history_len; i++)
- {
- code_size += strlen(history[i]) + 2;
- }
- char *code = malloc(code_size + strlen(expr) + 256);
- strcpy(code, "");
+ char *global_code = NULL;
+ char *main_code = NULL;
+ repl_get_code(history, history_len, &global_code, &main_code);
+
+ size_t code_size =
+ strlen(global_code) + strlen(main_code) + strlen(expr) + 4096;
+ char *code = malloc(code_size);
+
+ sprintf(code,
+ "%s\ninclude \"time.h\"\nfn main() { _z_suppress_stdout();\n%s "
+ "_z_restore_stdout();\n",
+ global_code, main_code);
+ free(global_code);
+ free(main_code);
- for (int i = 0; i < history_len; i++)
- {
- if (is_header_line(history[i]))
- {
- strcat(code, history[i]);
- strcat(code, "\n");
- }
- }
- strcat(code, "include \"time.h\"\n");
- strcat(code, "fn main() { _z_suppress_stdout();\n");
- for (int i = 0; i < history_len; i++)
- {
- if (!is_header_line(history[i]))
- {
- strcat(code, history[i]);
- strcat(code, " ");
- }
- }
- strcat(code, "_z_restore_stdout();\n");
strcat(code, "raw { clock_t _start = clock(); }\n");
strcat(code, "for _i in 0..1000 { ");
strcat(code, expr);
@@ -800,17 +1950,11 @@ void run_repl(const char *self_path)
while (brace_depth > 0)
{
- printf("... ");
- char more[1024];
- if (!fgets(more, sizeof(more), stdin))
+ char *more = repl_readline("... ", history, history_len, brace_depth);
+ if (!more)
{
break;
}
- size_t mlen = strlen(more);
- if (mlen > 0 && more[mlen - 1] == '\n')
- {
- more[--mlen] = 0;
- }
strcat(expr_buf, "\n");
strcat(expr_buf, more);
for (char *p = more; *p; p++)
@@ -824,35 +1968,20 @@ void run_repl(const char *self_path)
brace_depth--;
}
}
+ free(more);
}
- size_t code_size = 4096 + strlen(expr_buf);
- for (int i = 0; i < history_len; i++)
- {
- code_size += strlen(history[i]) + 2;
- }
- char *code = malloc(code_size + 128);
- strcpy(code, "");
+ char *global_code = NULL;
+ char *main_code = NULL;
+ repl_get_code(history, history_len, &global_code, &main_code);
- for (int i = 0; i < history_len; i++)
- {
- if (is_header_line(history[i]))
- {
- strcat(code, history[i]);
- strcat(code, "\n");
- }
- }
- strcat(code, "fn main() {\n");
- for (int i = 0; i < history_len; i++)
- {
- if (!is_header_line(history[i]))
- {
- strcat(code, history[i]);
- strcat(code, " ");
- }
- }
- strcat(code, expr_buf);
- strcat(code, "\n}");
+ size_t code_size =
+ strlen(global_code) + strlen(main_code) + strlen(expr_buf) + 128;
+ char *code = malloc(code_size);
+
+ sprintf(code, "%s\nfn main() { %s %s }", global_code, main_code, expr_buf);
+ free(global_code);
+ free(main_code);
free(expr_buf);
char tmp_path[256];
@@ -877,33 +2006,16 @@ void run_repl(const char *self_path)
}
else if (0 == strcmp(cmd_buf, ":run"))
{
- size_t code_size = 4096;
- for (int i = 0; i < history_len; i++)
- {
- code_size += strlen(history[i]) + 2;
- }
+ char *global_code = NULL;
+ char *main_code = NULL;
+ repl_get_code(history, history_len, &global_code, &main_code);
+
+ size_t code_size = strlen(global_code) + strlen(main_code) + 128;
char *code = malloc(code_size);
- strcpy(code, "");
- for (int i = 0; i < history_len; i++)
- {
- if (is_header_line(history[i]))
- {
- strcat(code, history[i]);
- strcat(code, "\n");
- }
- }
- strcat(code, "fn main() {\n");
- for (int i = 0; i < history_len; i++)
- {
- if (!is_header_line(history[i]))
- {
- strcat(code, " ");
- strcat(code, history[i]);
- strcat(code, "\n");
- }
- }
- strcat(code, "}\n");
+ sprintf(code, "%s\nfn main() { %s }", global_code, main_code);
+ free(global_code);
+ free(main_code);
char tmp_path[256];
sprintf(tmp_path, "/tmp/zprep_repl_run_%d.zc", rand());
@@ -1082,7 +2194,6 @@ void run_repl(const char *self_path)
}
if (!found)
{
- // Fallback: try man pages, show only SYNOPSIS.
char man_cmd[256];
sprintf(man_cmd,
"man 3 %s 2>/dev/null | sed -n '/^SYNOPSIS/,/^[A-Z]/p' | "
@@ -1187,7 +2298,6 @@ void run_repl(const char *self_path)
continue;
}
- // Add to history.
if (history_len >= history_cap)
{
history_cap *= 2;
@@ -1201,40 +2311,20 @@ void run_repl(const char *self_path)
brace_depth = 0;
paren_depth = 0;
- size_t total_size = 4096;
- for (int i = 0; i < history_len; i++)
- {
- total_size += strlen(history[i]) + 2;
- }
+ char *global_code = NULL;
+ char *main_code = NULL;
+ repl_get_code(history, history_len, &global_code, &main_code);
+
+ size_t total_size = strlen(global_code) + strlen(main_code) + 4096;
if (watches_len > 0)
{
- total_size += 16 * 1024; // Plenty of space for watches. Yeah static ik.
+ total_size += 16 * 1024;
}
char *full_code = malloc(total_size);
- strcpy(full_code, "");
-
- // Hoisting pass.
- for (int i = 0; i < history_len; i++)
- {
- if (is_header_line(history[i]))
- {
- strcat(full_code, history[i]);
- strcat(full_code, "\n");
- }
- }
-
- strcat(full_code, "fn main() { _z_suppress_stdout(); ");
-
- for (int i = 0; i < history_len - 1; i++)
- {
- if (is_header_line(history[i]))
- {
- continue;
- }
- strcat(full_code, history[i]);
- strcat(full_code, " ");
- }
+ sprintf(full_code, "%s\nfn main() { _z_suppress_stdout(); %s", global_code, main_code);
+ free(global_code);
+ free(main_code);
strcat(full_code, "_z_restore_stdout(); ");
@@ -1249,6 +2339,8 @@ void run_repl(const char *self_path)
ParserContext ctx = {0};
ctx.is_repl = 1;
ctx.skip_preamble = 1;
+ ctx.is_fault_tolerant = 1;
+ ctx.on_error = repl_error_callback;
Lexer l;
lexer_init(&l, check_buf);
ASTNode *node = parse_statement(&ctx, &l);
@@ -1272,33 +2364,17 @@ void run_repl(const char *self_path)
if (is_expr)
{
- size_t probesz = 4096;
- for (int i = 0; i < history_len - 1; i++)
- {
- probesz += strlen(history[i]) + 2;
- }
- char *probe_code = malloc(probesz + strlen(last_line) + 512);
- strcpy(probe_code, "");
+ char *global_code = NULL;
+ char *main_code = NULL;
+ repl_get_code(history, history_len - 1, &global_code, &main_code);
- for (int i = 0; i < history_len - 1; i++)
- {
- if (is_header_line(history[i]))
- {
- strcat(probe_code, history[i]);
- strcat(probe_code, "\n");
- }
- }
-
- strcat(probe_code, "fn main() { _z_suppress_stdout(); ");
+ size_t probesz = strlen(global_code) + strlen(main_code) + strlen(last_line) + 4096;
+ char *probe_code = malloc(probesz);
- for (int i = 0; i < history_len - 1; i++)
- {
- if (!is_header_line(history[i]))
- {
- strcat(probe_code, history[i]);
- strcat(probe_code, " ");
- }
- }
+ sprintf(probe_code, "%s\nfn main() { _z_suppress_stdout(); %s", global_code,
+ main_code);
+ free(global_code);
+ free(main_code);
strcat(probe_code, " raw { typedef struct { int _u; } __REVEAL_TYPE__; } ");
strcat(probe_code, " var _z_type_probe: __REVEAL_TYPE__; _z_type_probe = (");
@@ -1356,10 +2432,9 @@ void run_repl(const char *self_path)
if (watches_len > 0)
{
- strcat(full_code, "; "); // separator.
+ strcat(full_code, "; ");
for (int i = 0; i < watches_len; i++)
{
- // Use printf for label, then print "{expr}" for value.
char wbuf[1024];
sprintf(wbuf,
"printf(\"\\033[90mwatch:%s = \\033[0m\"); print \"{%s}\"; "
@@ -1401,7 +2476,8 @@ void run_repl(const char *self_path)
FILE *hf = fopen(history_path, "w");
if (hf)
{
- for (int i = 0; i < history_len; i++)
+ int start = history_len > 1000 ? history_len - 1000 : 0;
+ for (int i = start; i < history_len; i++)
{
fprintf(hf, "%s\n", history[i]);
}
@@ -1409,11 +2485,14 @@ void run_repl(const char *self_path)
}
}
- for (int i = 0; i < history_len; i++)
+ if (history)
{
- free(history[i]);
+ for (int i = 0; i < history_len; i++)
+ {
+ free(history[i]);
+ }
+ free(history);
}
- free(history);
if (input_buffer)
{
free(input_buffer);