#include "lua_base.h" static char *history_buffer[MAX_HISTORY_LINES]; // 指针数组 static int history_count = 0; // 当前存储了多少条 static int history_index = -1; // 当前正在浏览的历史索引 (-1 表示当前输入) lua_output lua_print = NULL; static lua_State *L = NULL; static char *buffer = NULL; static char *wrapped_code = NULL; static size_t buffer_len = 0; static int incomplete = 0; static size_t capacity = 256; static char *linebuffer = NULL; static size_t len = 0; static size_t cursor_pos = 0; static int escape_mode = 0; // 0: 正常, 1: 收到 ESC, 2: 收到 ESC [ static void add_to_history(const char *cmd) { if (cmd == NULL || strlen(cmd) == 0) return; history_index = -1; if (history_count > 0 && strcmp(history_buffer[history_count - 1], cmd) == 0) { return; } if (history_count >= MAX_HISTORY_LINES) { RD_FREE(history_buffer[0]); for (int i = 0; i < MAX_HISTORY_LINES - 1; i++) { history_buffer[i] = history_buffer[i + 1]; } history_count = MAX_HISTORY_LINES - 1; } char *new_entry = (char *)RD_MALLOC(strlen(cmd) + 1); if (new_entry) { strcpy(new_entry, cmd); history_buffer[history_count++] = new_entry; } } static const char* get_history(int offset) { if (history_count == 0) return NULL; if (offset < 0) { if (history_index == -1) { history_index = history_count - 1; } else if (history_index > 0) { history_index--; } } else { if (history_index != -1) { history_index++; if (history_index >= history_count) { history_index = -1; } } } if (history_index == -1) return NULL; return history_buffer[history_index]; } static char *trim(char *str) { while (isspace((unsigned char)*str)) str++; if (*str == 0) return str; char *end = str + strlen(str) - 1; while (end > str && isspace((unsigned char)*end)) end--; end[1] = '\0'; return str; } static void print_number_fixed3(double num) { if (NULL == lua_print) return; if (num < 0) { lua_print("-"); num = -num; } lua_Integer int_part = (lua_Integer)num; double frac_double = (num - (double)int_part) * 1000.0 + 0.5; int frac_part = (int)frac_double; if (frac_part >= 1000) { int_part += 1; frac_part -= 1000; } lua_print("%ld", (long)int_part); if (frac_part > 0) { lua_print("."); char frac_buf[4]; frac_buf[0] = (frac_part / 100) + '0'; frac_buf[1] = (frac_part % 100 / 10) + '0'; frac_buf[2] = (frac_part % 10) + '0'; frac_buf[3] = '\0'; int end_idx = 2; while (end_idx >= 0 && frac_buf[end_idx] == '0') { end_idx--; } for (int i = 0; i <= end_idx; i++) { lua_print("%c", frac_buf[i]); } } } static void print_result(lua_State *L, int status) { if (NULL == lua_print) return; if (status != LUA_OK) { lua_print("Error: %s\n", lua_tostring(L, -1)); lua_pop(L, 1); return; } int n = lua_gettop(L); if (n == 0) return; // 没有返回值 // 打印所有返回值 for (int i = 1; i <= n; i++) { if (i > 1) { lua_print("\t"); // 多个值之间用制表符分隔 } if (lua_isnumber(L, i)) { // 优化数字显示:如果是整数则不显示小数点 double num = lua_tonumber(L, i); if (num == (lua_Integer)num) { lua_print("%d", (lua_Integer)num); } else { print_number_fixed3(num); } } else if (lua_isstring(L, i)) { lua_print("%s", lua_tostring(L, i)); } else if (lua_isboolean(L, i)) { lua_print("%s", lua_toboolean(L, i) ? "true" : "false"); } else if (lua_isnil(L, i)) { lua_print("nil"); } else { lua_print("%s: %p", luaL_typename(L, i), lua_topointer(L, i)); } } lua_print("\n"); lua_pop(L, n); } static char *readline(const char *_pcPrompt, char *_pcBuffer, int _iSize, int _iEcho) { if (NULL == lua_print) { log_e("\nError: printf is NULL\n"); return NULL; } if (linebuffer == NULL) { linebuffer = (char *)RD_MALLOC(capacity); if (!linebuffer) { log_e("\nError: Out of memory\n"); return NULL; } } size_t limit = (_iSize < 0) ? SIZE_MAX : (size_t)_iSize; for (size_t i = 0; i < limit; i++) { int c; if (NULL == _pcBuffer) { c = fgetc(stdin); } else { c = (int)_pcBuffer[i]; } if (c == EOF) { lua_print("\n"); RD_FREE(linebuffer); linebuffer = NULL; log_e("\nError: End of EOF\n"); return NULL; } if (len == 0 && cursor_pos == 0 && (c == '\n' || c == '\r')) { continue; } if (escape_mode == 1) { if (c == '[') { escape_mode = 2; continue; } else { escape_mode = 0; continue; } } else if (escape_mode == 2) { escape_mode = 0; // 先重置,无论是否匹配 if (c == 'A' || c == 'B') { const char *hist = (c == 'A') ? get_history(-1) : get_history(1); lua_print("\r\033[K%s%s", _pcPrompt, hist ? hist : ""); if (hist) { len = strlen(hist); if (len >= capacity - 1) { capacity = len + 10; linebuffer = (char *)RD_REALLOC(linebuffer, capacity); if(!linebuffer) return NULL; } strcpy(linebuffer, hist); cursor_pos = len; } else { len = 0; cursor_pos = 0; linebuffer[0] = '\0'; } continue; } else if (c == 'C') { if (cursor_pos < len) { lua_print("%c", linebuffer[cursor_pos]); cursor_pos++; } continue; } else if (c == 'D') { if (cursor_pos > 0) { lua_print("\b"); cursor_pos--; } continue; } continue; } if (c == 0x1B) { escape_mode = 1; continue; } if (c == '\n' || c == '\r') { if (_iEcho) { lua_print("\n"); } linebuffer[len] = '\0'; char *result = strdup(linebuffer); if (len > 0 && strcmp(linebuffer, "exit") != 0) { add_to_history(linebuffer); } len = 0; cursor_pos = 0; escape_mode = 0; linebuffer[0] = '\0'; return result; } if (c == '\b' || c == 127) { if (cursor_pos > 0) { cursor_pos--; for (size_t i = cursor_pos; i < len - 1; i++) { linebuffer[i] = linebuffer[i+1]; } len--; linebuffer[len] = '\0'; lua_print("\b"); for (size_t i = cursor_pos; i < len; i++) { lua_print("%c", linebuffer[i]); } lua_print(" "); for (size_t i = cursor_pos; i <= len; i++) { lua_print("\b"); } } continue; } if (c >= 32 && c <= 126) { if (len >= capacity - 1) { capacity += 256; char *new_buf = (char *)RD_REALLOC(linebuffer, capacity); if (!new_buf) { log_e("\nError: Out of memory\n"); RD_FREE(linebuffer); return NULL; } linebuffer = new_buf; } for (size_t i = len; i > cursor_pos; i--) { linebuffer[i] = linebuffer[i-1]; } linebuffer[cursor_pos] = (char)c; len++; linebuffer[len] = '\0'; if (_iEcho) { for (size_t i = cursor_pos; i < len; i++) { lua_print("%c", linebuffer[i]); } } for (size_t i = cursor_pos; i < len - 1; i++) { lua_print("\b"); } cursor_pos++; continue; } } return NULL; } WEAK int luaopen_stm32(lua_State *L) { lua_newtable(L); lua_setglobal(L, "st"); return 1; } int lua_init(lua_output _lua_print) { if (NULL == _lua_print) return -1; lua_print = _lua_print; buffer_len = 0; capacity = 256; len = 0; cursor_pos = 0; incomplete = 0; escape_mode = 0; if (linebuffer != NULL) { RD_FREE(linebuffer); linebuffer = NULL; } if (buffer != NULL) { RD_FREE(buffer); buffer = NULL; } if (wrapped_code != NULL) { RD_FREE(wrapped_code); wrapped_code = NULL; } if (L != NULL) { lua_close(L); L = NULL; } L = luaL_newstate(); if (!L) { lua_print("Failed to create Lua state\n"); return RD_FAILURE; } luaL_openlibs(L); luaopen_stm32(L); lua_print("Lua REPL V5.4\nType 'exit' or Ctrl+D to quit.\n\n"); lua_print("%s", incomplete ? CONT_PROMPT : PROMPT); return 0; } int lua_repl(char *_pcBuffer, int _iSize, int _iEcho) { int iRet = 0; char *line = readline(incomplete ? CONT_PROMPT : PROMPT, _pcBuffer, _iSize, _iEcho); if (!line) { return RD_SUCCESS; } if (strcmp(line, "exit") == 0) { RD_FREE(line); iRet = RD_SUCCESS; goto cleanup; } if (incomplete) { size_t line_len = strlen(line); char *new_buf = RD_REALLOC(buffer, buffer_len + line_len + 2); buffer = new_buf; strcat(buffer, "\n"); strcat(buffer, line); buffer_len += line_len + 1; RD_FREE(line); } else { RD_FREE(buffer); buffer = line; buffer_len = strlen(line); } char *final_code = NULL; int status = LUA_ERRSYNTAX; // 1. 预检查:是否明显是表达式?(数字、符号开头) int try_return_first = 0; const char *trimmed = trim(buffer); int looks_like_block = (strncmp(trimmed, "function", 8) == 0) || (strncmp(trimmed, "if", 2) == 0) || (strncmp(trimmed, "for", 3) == 0) || (strncmp(trimmed, "while", 5) == 0) || (strncmp(trimmed, "do", 2) == 0) || (strncmp(trimmed, "repeat", 6) == 0); if (strlen(trimmed) > 0) { char first = trimmed[0]; if (isdigit(first) || first == '(' || first == '"' || first == '\'' || first == '-' || first == '{' || first == '[') { try_return_first = 1; } } // 情况 A: 明显是表达式,直接加 return 编译 if (try_return_first) { wrapped_code = RD_MALLOC(buffer_len + 10); sprintf(wrapped_code, "return %s", buffer); final_code = wrapped_code; status = luaL_loadbuffer(L, final_code, strlen(final_code), "=repl"); } else { // 情况 B: 看起来像语句 (字母开头),先尝试原样编译 status = luaL_loadbuffer(L, buffer, buffer_len, "=repl"); if (status == LUA_ERRSYNTAX) { lua_pop(L, 1); // 弹出旧错误 wrapped_code = RD_MALLOC(buffer_len + 10); sprintf(wrapped_code, "return %s", buffer); status = luaL_loadbuffer(L, wrapped_code, strlen(wrapped_code), "=repl"); if (status == LUA_ERRSYNTAX) { const char *new_err = lua_tostring(L, -1); if (strstr(new_err, "") != NULL || strstr(new_err, "near 'if'") != NULL || strstr(new_err, "near 'function'") != NULL || strstr(new_err, "near 'for'") != NULL || strstr(new_err, "near 'while'") != NULL || strstr(new_err, "near 'do'") != NULL) { lua_pop(L, 1); RD_FREE(wrapped_code); wrapped_code = NULL; incomplete = 1; lua_print("%s", incomplete ? CONT_PROMPT : PROMPT); return RD_FAILURE; } } } } if (status == LUA_ERRSYNTAX) { const char *err_msg = lua_tostring(L, -1); if (strstr(err_msg, "") != NULL || (strstr(err_msg, "'(' expected near") != NULL && looks_like_block)) { incomplete = 1; lua_print("%s", incomplete ? CONT_PROMPT : PROMPT); lua_pop(L, 1); if (wrapped_code) { RD_FREE(wrapped_code); wrapped_code = NULL; } return RD_FAILURE; } else { lua_print("Syntax Error: %s\n", err_msg); lua_pop(L, 1); incomplete = 0; lua_print("%s", incomplete ? CONT_PROMPT : PROMPT); RD_FREE(buffer); buffer = NULL; buffer_len = 0; if (wrapped_code) { RD_FREE(wrapped_code); wrapped_code = NULL; } return RD_FAILURE; } } else if (status != LUA_OK) { lua_print("Load Error: %s\n", lua_tostring(L, -1)); lua_pop(L, 1); incomplete = 0; lua_print("%s", incomplete ? CONT_PROMPT : PROMPT); RD_FREE(buffer); buffer = NULL; buffer_len = 0; if (wrapped_code) { RD_FREE(wrapped_code); wrapped_code = NULL; } return RD_FAILURE; } status = lua_pcall(L, 0, LUA_MULTRET, 0); if (status == LUA_OK) { print_result(L, status); } else { const char *err_msg = lua_tostring(L, -1); lua_print("%s\n", err_msg ? err_msg : "Unknown error"); lua_pop(L, 1); } incomplete = 0; lua_print("%s", incomplete ? CONT_PROMPT : PROMPT); RD_FREE(buffer); buffer = NULL; buffer_len = 0; if (wrapped_code) { RD_FREE(wrapped_code); wrapped_code = NULL; } return RD_SUCCESS; cleanup: if (wrapped_code) { RD_FREE(wrapped_code); wrapped_code = NULL; } RD_FREE(buffer); lua_close(L); L = NULL; lua_init(lua_print); return iRet; } void luaprint_stdout(const char *fmt, ...) { va_list args; va_start(args, fmt); vprintf(fmt, args); fflush(stdout); va_end(args); } #ifdef MAIN #include #include #include int main(int argc, const char *argv[]) { RD_INIT(); struct termios orig_termios; struct termios raw_termios; // 1. 获取当前终端设置 if (tcgetattr(STDIN_FILENO, &orig_termios) == -1) { log_e("%s tcgetattr", strerror(errno)); return 1; } // 2. 复制一份用于修改 raw_termios = orig_termios; // 3. 设置 Raw Mode // 关闭规范模式 (ICANON) -> 禁用行缓冲,字符立即可读 // 关闭回显 (ECHO) -> 防止内核自动打印,由 readline 手动打印 // 关闭信号生成 (ISIG) -> Ctrl+C 不会被当成中断,而是作为普通字符读取 (可选,看你是否需要 Ctrl+C 退出) raw_termios.c_lflag &= ~(ICANON | ECHO); // 确保非阻塞或最小字符数设置 (可选,通常 fgetc 会阻塞直到有字符) raw_termios.c_cc[VMIN] = 1; raw_termios.c_cc[VTIME] = 0; // 4. 应用新设置 if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw_termios) == -1) { log_e("%s tcsetattr", strerror(errno)); return 1; } lua_init(luaprint_stdout); while(1) { lua_repl(NULL, -1, 1); } // 5. 程序退出前恢复终端设置 tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios); log_i("\nTerminal restored. Goodbye!\n"); return 0; } #endif