freeRTOS操作系统机器人实现
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

695 lines
17 KiB

3 days ago
#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, "<eof>") != 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, "<eof>") != 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 <termios.h>
#include <unistd.h>
#include <errno.h>
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