322 lines
7.7 KiB
C
322 lines
7.7 KiB
C
#include <malloc.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
/* FreeRTOS */
|
|
#include "FreeRTOS.h"
|
|
#include "task.h"
|
|
|
|
/* LN */
|
|
#include "linenoise.h"
|
|
|
|
/* mruby */
|
|
#include "mruby.h"
|
|
#include "mruby/compile.h"
|
|
#include "mruby/error.h"
|
|
#include "mruby/internal.h"
|
|
#include "mruby/presym.h"
|
|
#include "mruby/proc.h"
|
|
#include "mruby/string.h"
|
|
#include "mruby/variable.h"
|
|
|
|
/* Private */
|
|
#include "app_mrb_repl.h"
|
|
|
|
typedef struct {
|
|
float used;
|
|
float total;
|
|
} app_mrb_repl_heap_usage_t;
|
|
|
|
/* FIXME: We use static variables here since linenoise can't be used for multiple instances. */
|
|
|
|
static mrb_state *s_mrb_state;
|
|
static mrbc_context *s_mrbc_cxt;
|
|
static int s_gc_arena_idx;
|
|
static int s_stack_keep = 0;
|
|
|
|
static bool s_code_block_open = false;
|
|
|
|
static char *s_prompt_str = NULL;
|
|
static char *s_ruby_code = NULL;
|
|
|
|
static void app_mrb_repl_heap_usage(app_mrb_repl_heap_usage_t *usage) {
|
|
struct mallinfo m_info = mallinfo();
|
|
|
|
usage->used = (float)m_info.uordblks / 1024.0f;
|
|
usage->total = (float)(m_info.fordblks + m_info.uordblks) / 1024.0f;
|
|
}
|
|
|
|
static bool app_mrb_repl_code_block_open(struct mrb_parser_state *state) {
|
|
bool ret = false;
|
|
|
|
/* This is reference to mirb's is_code_block_open. */
|
|
|
|
/* HEREDOC */
|
|
if (state->parsing_heredoc) {
|
|
return true;
|
|
}
|
|
|
|
/* Unterminated string */
|
|
if (state->lex_strterm) {
|
|
return true;
|
|
}
|
|
|
|
if (state->nerr > 0) {
|
|
const char unexpected_end[] = "syntax error, unexpected end of file";
|
|
const char *message = state->error_buffer[0].message;
|
|
|
|
/* Compare error strings, the code block finished except unexpected EOF. */
|
|
if (strncmp(message, unexpected_end, sizeof(unexpected_end) - 1) == 0) {
|
|
return true;
|
|
}
|
|
|
|
if (strcmp(message, "syntax error, unexpected keyword_end") == 0) {
|
|
return false;
|
|
}
|
|
|
|
if (strcmp(message, "syntax error, unexpected tREGEXP_BEG") == 0) {
|
|
return false;
|
|
}
|
|
return ret; /* Other errors, still need to close the block */
|
|
}
|
|
|
|
switch (state->lstate) {
|
|
case EXPR_BEG:
|
|
case EXPR_ARG:
|
|
ret = false;
|
|
break;
|
|
case EXPR_DOT:
|
|
case EXPR_CLASS:
|
|
case EXPR_FNAME:
|
|
case EXPR_VALUE:
|
|
ret = true;
|
|
break;
|
|
case EXPR_CMDARG:
|
|
case EXPR_END:
|
|
case EXPR_ENDARG:
|
|
case EXPR_ENDFN:
|
|
case EXPR_MID:
|
|
case EXPR_MAX_STATE:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void app_mrb_repl_p(mrb_state *mrb, mrb_value obj, int prompt) {
|
|
mrb_value val;
|
|
|
|
val = mrb_funcall_id(mrb, obj, MRB_SYM(inspect), 0);
|
|
if (prompt) {
|
|
if (!mrb->exc) {
|
|
printf("=> ");
|
|
} else {
|
|
val = mrb_funcall_id(mrb, mrb_obj_value(mrb->exc), MRB_SYM(inspect), 0);
|
|
}
|
|
}
|
|
if (!mrb_string_p(val)) {
|
|
val = mrb_obj_as_string(mrb, obj);
|
|
}
|
|
|
|
printf("%s\n", RSTRING_PTR(val));
|
|
}
|
|
|
|
int app_mrb_repl_init(void) {
|
|
int ret = 0;
|
|
|
|
s_prompt_str = malloc(64);
|
|
if (s_prompt_str == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
s_mrb_state = mrb_open();
|
|
if (s_mrb_state == NULL) {
|
|
ret = -1;
|
|
goto mrb_alloc_fail;
|
|
}
|
|
s_mrbc_cxt = mrbc_context_new(s_mrb_state);
|
|
if (s_mrbc_cxt == NULL) {
|
|
ret = -1;
|
|
goto mrbc_cxt_alloc_fail;
|
|
}
|
|
|
|
s_mrbc_cxt->capture_errors = TRUE;
|
|
s_mrbc_cxt->lineno = 1;
|
|
|
|
s_gc_arena_idx = mrb_gc_arena_save(s_mrb_state);
|
|
|
|
mrb_show_version(s_mrb_state);
|
|
mrb_show_copyright(s_mrb_state);
|
|
|
|
time_t rawtime;
|
|
struct tm *timeinfo;
|
|
|
|
time(&rawtime);
|
|
timeinfo = localtime(&rawtime);
|
|
|
|
printf("Time: %s\n", asctime(timeinfo));
|
|
|
|
return 0;
|
|
|
|
mrbc_cxt_alloc_fail:
|
|
mrb_close(s_mrb_state);
|
|
|
|
mrb_alloc_fail:
|
|
free(s_prompt_str);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int app_mrb_repl_deinit(void) {
|
|
printf("Destroying old world...\n\n");
|
|
linenoiseClearScreen();
|
|
|
|
linenoiseAtExit();
|
|
|
|
free(s_prompt_str);
|
|
|
|
mrbc_cleanup_local_variables(s_mrb_state, s_mrbc_cxt);
|
|
mrbc_context_free(s_mrb_state, s_mrbc_cxt);
|
|
mrb_close(s_mrb_state);
|
|
|
|
s_code_block_open = 0;
|
|
s_gc_arena_idx = 0;
|
|
s_stack_keep = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int app_mrb_repl_exec(bool *exit) {
|
|
char *line;
|
|
app_mrb_repl_heap_usage_t heap_stat;
|
|
|
|
char prompt_identifier;
|
|
if (s_code_block_open) {
|
|
prompt_identifier = '*';
|
|
} else {
|
|
prompt_identifier = '>';
|
|
}
|
|
|
|
app_mrb_repl_heap_usage(&heap_stat);
|
|
snprintf(s_prompt_str, 64, "[%.02fkiB|%.02fkiB](mirb)(%d)%c ", heap_stat.used, heap_stat.total, s_mrbc_cxt->lineno,
|
|
prompt_identifier);
|
|
|
|
line = linenoise(s_prompt_str);
|
|
|
|
if (line == NULL) {
|
|
*exit = true;
|
|
return 0;
|
|
}
|
|
|
|
/* Input nothing, just a return */
|
|
if (strlen(line) == 0) {
|
|
linenoiseFree(line);
|
|
return 0;
|
|
}
|
|
|
|
linenoiseHistoryAdd(line);
|
|
|
|
/* First item in the block */
|
|
if (s_ruby_code == NULL) {
|
|
size_t nts_len = strlen(line) + 2; /* for an additional \n and NUL */
|
|
s_ruby_code = malloc(nts_len);
|
|
if (s_ruby_code == NULL) {
|
|
linenoiseFree(line);
|
|
*exit = true;
|
|
return -1;
|
|
}
|
|
|
|
strcpy(s_ruby_code, line);
|
|
s_ruby_code[nts_len - 2] = '\n';
|
|
s_ruby_code[nts_len - 1] = '\0';
|
|
} else {
|
|
size_t nts_len = strlen(s_ruby_code) + strlen(line) + 2; /* Same */
|
|
void *alloc_buf = realloc(s_ruby_code, nts_len);
|
|
if (alloc_buf == NULL) {
|
|
linenoiseFree(line);
|
|
free(s_ruby_code);
|
|
|
|
*exit = true;
|
|
return -1;
|
|
}
|
|
|
|
s_ruby_code = alloc_buf;
|
|
|
|
strcat(s_ruby_code, line);
|
|
s_ruby_code[nts_len - 2] = '\n';
|
|
s_ruby_code[nts_len - 1] = '\0';
|
|
}
|
|
|
|
linenoiseFree(line);
|
|
|
|
struct mrb_parser_state *parser = mrb_parser_new(s_mrb_state);
|
|
if (parser == NULL) {
|
|
*exit = true;
|
|
return -1;
|
|
}
|
|
|
|
parser->s = s_ruby_code;
|
|
parser->send = s_ruby_code + strlen(s_ruby_code);
|
|
parser->lineno = s_mrbc_cxt->lineno;
|
|
|
|
mrb_parser_parse(parser, s_mrbc_cxt);
|
|
|
|
s_code_block_open = app_mrb_repl_code_block_open(parser);
|
|
|
|
if (s_code_block_open) {
|
|
goto next_iter;
|
|
}
|
|
|
|
if (parser->nwarn) {
|
|
printf("Warn at line %d: %s\n", parser->warn_buffer[0].lineno, parser->warn_buffer[0].message);
|
|
}
|
|
|
|
if (parser->nerr) {
|
|
printf("Error at line %d: %s\n", parser->error_buffer[0].lineno, parser->error_buffer[0].message);
|
|
goto block_has_error;
|
|
}
|
|
|
|
/* Compile byte code */
|
|
struct RProc *proc = mrb_generate_code(s_mrb_state, parser);
|
|
if (proc == NULL) {
|
|
goto block_has_error;
|
|
}
|
|
|
|
/* Mysterious ENV */
|
|
if (s_mrb_state->c->cibase->u.env) {
|
|
struct REnv *e = mrb_vm_ci_env(s_mrb_state->c->cibase);
|
|
if (e && MRB_ENV_LEN(e) < proc->body.irep->nlocals) {
|
|
MRB_ENV_SET_LEN(e, proc->body.irep->nlocals);
|
|
}
|
|
}
|
|
|
|
/* Run */
|
|
mrb_value result = mrb_vm_run(s_mrb_state, proc, mrb_top_self(s_mrb_state), s_stack_keep);
|
|
s_stack_keep = proc->body.irep->nlocals;
|
|
|
|
/* Exception occurred? */
|
|
if (s_mrb_state->exc) {
|
|
app_mrb_repl_p(s_mrb_state, mrb_obj_value(s_mrb_state->exc), 0);
|
|
s_mrb_state->exc = 0;
|
|
} else {
|
|
if (!mrb_respond_to(s_mrb_state, result, MRB_SYM(inspect))) {
|
|
result = mrb_any_to_s(s_mrb_state, result);
|
|
}
|
|
app_mrb_repl_p(s_mrb_state, result, 1);
|
|
}
|
|
|
|
block_has_error:
|
|
mrb_gc_arena_restore(s_mrb_state, s_gc_arena_idx);
|
|
|
|
/* We are done with current block, free buffer */
|
|
free(s_ruby_code);
|
|
s_ruby_code = NULL;
|
|
|
|
next_iter:
|
|
mrb_parser_free(parser);
|
|
s_mrbc_cxt->lineno++;
|
|
|
|
*exit = false;
|
|
return 0;
|
|
} |