summaryrefslogtreecommitdiffhomepage
path: root/mrbgems/mruby-binding/src/binding.c
diff options
context:
space:
mode:
Diffstat (limited to 'mrbgems/mruby-binding/src/binding.c')
-rw-r--r--mrbgems/mruby-binding/src/binding.c173
1 files changed, 173 insertions, 0 deletions
diff --git a/mrbgems/mruby-binding/src/binding.c b/mrbgems/mruby-binding/src/binding.c
new file mode 100644
index 000000000..67692e2b9
--- /dev/null
+++ b/mrbgems/mruby-binding/src/binding.c
@@ -0,0 +1,173 @@
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/class.h>
+#include <mruby/compile.h>
+#include <mruby/error.h>
+#include <mruby/proc.h>
+#include <mruby/presym.h>
+#include <mruby/string.h>
+
+mrb_noreturn void mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args);
+void mrb_proc_merge_lvar(mrb_state *mrb, mrb_irep *irep, struct REnv *env, int num, const mrb_sym *lv, const mrb_value *stack);
+mrb_value mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p);
+const struct RProc *mrb_binding_extract_proc(mrb_state *mrb, mrb_value binding);
+struct REnv *mrb_binding_extract_env(mrb_state *mrb, mrb_value binding);
+typedef mrb_bool mrb_parser_foreach_top_variable_func(mrb_state *mrb, mrb_sym sym, void *user);
+void mrb_parser_foreach_top_variable(mrb_state *mrb, struct mrb_parser_state *p, mrb_parser_foreach_top_variable_func *func, void *user);
+
+static void
+insert_args(mrb_state *mrb, size_t offset, mrb_value obj)
+{
+ mrb_callinfo *ci = mrb->c->ci;
+ mrb_value *argp = ci->stack + 1 /* recv */;
+
+ if (ci->argc < 0) {
+ mrb_ary_splice(mrb, *argp, offset, 0, obj);
+ }
+ else {
+ argp += offset;
+ mrb_stack_extend(mrb, ci->argc + offset + 2 /* recv + block */);
+ memmove(argp + 1 /* obj */, argp, sizeof(mrb_value) * (ci->argc - offset + 1 /* block */));
+ *argp = obj;
+ ci->argc++;
+ }
+}
+
+static void
+binding_eval_error_check(mrb_state *mrb, struct mrb_parser_state *p, const char *file)
+{
+ if (!p) {
+ mrb_raise(mrb, E_RUNTIME_ERROR, "Failed to create parser state (out of memory)");
+ }
+
+ if (0 < p->nerr) {
+ mrb_value str;
+
+ if (file) {
+ str = mrb_format(mrb, "file %s line %d: %s",
+ file,
+ p->error_buffer[0].lineno,
+ p->error_buffer[0].message);
+ }
+ else {
+ str = mrb_format(mrb, "line %d: %s",
+ p->error_buffer[0].lineno,
+ p->error_buffer[0].message);
+ }
+ mrb_exc_raise(mrb, mrb_exc_new_str(mrb, E_SYNTAX_ERROR, str));
+ }
+}
+
+#define LV_BUFFERS 8
+
+struct expand_lvspace {
+ mrb_irep *irep;
+ struct REnv *env;
+ size_t numvar;
+ mrb_sym syms[LV_BUFFERS];
+};
+
+static mrb_bool
+expand_lvspace(mrb_state *mrb, mrb_sym sym, void *user)
+{
+ struct expand_lvspace *p = (struct expand_lvspace*)user;
+ mrb_int symlen;
+ const char *symname = mrb_sym_name_len(mrb, sym, &symlen);
+
+ if (symname && symlen > 0) {
+ if (symname[0] != '&' && symname[0] != '*') {
+ p->syms[p->numvar++] = sym;
+ if (p->numvar >= LV_BUFFERS) {
+ mrb_proc_merge_lvar(mrb, p->irep, p->env, p->numvar, p->syms, NULL);
+ p->numvar = 0;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+struct binding_eval_prepare_body {
+ mrb_value binding;
+ const char *file;
+ const char *expr;
+ mrb_int exprlen;
+ mrbc_context *mrbc;
+ struct mrb_parser_state *pstate;
+};
+
+static mrb_value
+binding_eval_prepare_body(mrb_state *mrb, void *opaque)
+{
+ struct binding_eval_prepare_body *p = (struct binding_eval_prepare_body*)opaque;
+
+ const struct RProc *proc = mrb_binding_extract_proc(mrb, p->binding);
+ mrb_assert(!MRB_PROC_CFUNC_P(proc));
+
+ p->mrbc = mrbc_context_new(mrb);
+ mrbc_filename(mrb, p->mrbc, p->file ? p->file : "(eval)");
+ p->mrbc->upper = proc;
+ p->mrbc->capture_errors = TRUE;
+ p->pstate = mrb_parse_nstring(mrb, p->expr, p->exprlen, p->mrbc);
+ binding_eval_error_check(mrb, p->pstate, p->file);
+
+ struct expand_lvspace args = {
+ (mrb_irep*)proc->body.irep,
+ mrb_binding_extract_env(mrb, p->binding),
+ 0,
+ { 0 }
+ };
+ mrb_parser_foreach_top_variable(mrb, p->pstate, expand_lvspace, &args);
+ if (args.numvar > 0) {
+ mrb_proc_merge_lvar(mrb, args.irep, args.env, args.numvar, args.syms, NULL);
+ }
+
+ return mrb_nil_value();
+}
+
+static void
+binding_eval_prepare(mrb_state *mrb, mrb_value binding)
+{
+ struct binding_eval_prepare_body d = { binding, NULL, NULL, 0, NULL, NULL };
+ mrb_int argc;
+ mrb_value *argv;
+ mrb_get_args(mrb, "s|z*!", &d.expr, &d.exprlen, &d.file, &argv, &argc);
+
+ mrb_bool error;
+ mrb_value ret = mrb_protect_error(mrb, binding_eval_prepare_body, &d, &error);
+ if (d.pstate) mrb_parser_free(d.pstate);
+ if (d.mrbc) mrbc_context_free(mrb, d.mrbc);
+ if (error) mrb_exc_raise(mrb, ret);
+}
+
+static mrb_value
+mrb_binding_eval(mrb_state *mrb, mrb_value binding)
+{
+ binding_eval_prepare(mrb, binding);
+
+ struct RClass *c = mrb->kernel_module;
+ mrb_method_t m = mrb_method_search_vm(mrb, &c, MRB_SYM(eval));
+ if (MRB_METHOD_UNDEF_P(m)) {
+ int argc = mrb->c->ci->argc;
+ mrb_value *argv = mrb->c->ci->stack + 1;
+ mrb_value args = (argc < 0) ? argv[0] : mrb_ary_new_from_values(mrb, argc, argv);
+ mrb_method_missing(mrb, MRB_SYM(eval), binding, args);
+ }
+
+ insert_args(mrb, 1, binding);
+ struct RProc *proc = MRB_METHOD_PROC_P(m) ? MRB_METHOD_PROC(m) : mrb_proc_new_cfunc(mrb, MRB_METHOD_FUNC(m));
+ mrb->c->ci->u.target_class = c;
+ return mrb_exec_irep(mrb, binding, proc);
+}
+
+void
+mrb_mruby_binding_gem_init(mrb_state *mrb)
+{
+ struct RClass *binding = mrb_class_get_id(mrb, MRB_SYM(Binding));
+ mrb_define_method(mrb, binding, "eval", mrb_binding_eval, MRB_ARGS_ANY());
+}
+
+void
+mrb_mruby_binding_gem_final(mrb_state *mrb)
+{
+}