On Tue, Feb 19, 2002 at 01:21:31PM +0900, Yukihiro Matsumoto wrote:

Thanks for the answers matz, they helped me understand this a bit
better. Though they, of course, resulted in my questions :)

> the assignemnt to the variable a is done in the scope of the execute
> method, whic vaporized every time execute called.  On the other hand,
> 
> |static VALUE cContext_execute(VALUE self, VALUE str)
> |{
> |    return rb_funcall(self, rb_intern("instance_eval"), 1, str);
> |}
> 
> this case, the assignment is done at the toplevel scope, which is used
> again and again.  That's the difference.

Ah, I understand now. So is there an easy and nice way to make something
execute inside a scope from C? What I did now was to rip out PUSH_SCOPE
and POP_SCOPE from eval.c and put them around the eval like this:

    PUSH_SCOPE();
    v = rb_funcall(self, rb_intern("eval"), 1, str);
    POP_SCOPE();

so I now get

"
5
undefined local variable or method `a' for #<Context:0x401b5af4>
"

when I run the program, which is what I want. But it feels a bit like
language abuse to do it the way I'm doing it now.

(I've attached the whole program to this mail if someone wants to see it.)


The next problem I'm having is that I want to be able to return from the
the eval. I.e. I want to be able to evaluate something like this:

    a = 1
    if a == 1
        return "a is one"
    else
        return "a is not one"
    end

which means it has to be evaluated inside a method somehow. That's why I
put the eval/instance_eval call inside the execute method, which I
define in the Context class, because I thought that it might then be
evaluated inside that method.
But it's not working. If I remove rb_protect which is protecting the
call to cContext_execute_helper I get segmentation faults. So I'm
obviously doing something wrong, the question is what?

Is this even possible?


/joakim

-- 
Joakim Andersson ; tyrak / borgship.net ; http://borgship.net/~tyrak/
#include "ruby.h"
#include "env.h"

static void scope_dup _((struct SCOPE *));
static int scope_vmode = 0;

#define SCOPE_PUBLIC    0

#define PUSH_SCOPE() {                  \
    volatile int _vmode = scope_vmode;  \
    struct SCOPE * volatile _old;       \
    NEWOBJ(_scope, struct SCOPE);       \
    OBJSETUP(_scope, 0, T_SCOPE);       \
    _scope->local_tbl = 0;              \
    _scope->local_vars = 0;             \
    _scope->flag = 0;                   \
    _old = ruby_scope;                  \
    ruby_scope = _scope;                \
    scope_vmode = SCOPE_PUBLIC;

/* from eval.c with minor modifications */
#define POP_SCOPE()                     \
    if (ruby_scope->flag & SCOPE_DONT_RECYCLE) {\
       if (_old) scope_dup(_old);       \
    }                                   \
    if (!(ruby_scope->flag & SCOPE_MALLOC)) {\
        ruby_scope->local_vars = 0;     \
        ruby_scope->local_tbl  = 0;     \
        if (!(ruby_scope->flag & SCOPE_DONT_RECYCLE)) {  \
            rb_gc_force_recycle((VALUE)ruby_scope);\
        }                               \
    }                                   \
    ruby_scope->flag |= SCOPE_NOSTACK;  \
    ruby_scope = _old;                  \
    scope_vmode = _vmode;               \
}

static void
scope_dup(scope)
    struct SCOPE *scope;
{
    ID *tbl;
    VALUE *vars;

    scope->flag |= SCOPE_DONT_RECYCLE;
    if (scope->flag & SCOPE_MALLOC) return;

    if (scope->local_tbl) {
        tbl = scope->local_tbl;
        vars = ALLOC_N(VALUE, tbl[0]+1);
        *vars++ = scope->local_vars[-1];
        MEMCPY(vars, scope->local_vars, VALUE, tbl[0]);
        scope->local_vars = vars;
        scope->flag |= SCOPE_MALLOC;
    }
}

static VALUE cContext_execute_helper(VALUE ary)
{
    VALUE self, str, v;
    self = rb_funcall(ary, rb_intern("at"), 1, INT2FIX(0));
    str = rb_funcall(ary, rb_intern("at"), 1, INT2FIX(1));
    PUSH_SCOPE();
    v = rb_funcall(self, rb_intern("eval"), 1, str);
    POP_SCOPE();
    return v;
}

static VALUE cContext_execute(VALUE self, VALUE str)
{
    VALUE v, ary;
    int state = 0;

    ary = rb_ary_new3(2, self, str);
    v = rb_protect(cContext_execute_helper, ary, &state);
    if (state)
    {
        /* we got an exception */
        v = rb_gv_get("$!");
        v = rb_funcall(v, rb_intern("to_s"), 0);
        printf("%s\n", RSTRING(v)->ptr);
        return Qnil;
    } 
    else
    {       
        return v;
    }
}

int main(void)
{
    VALUE cContext, a, b;

    ruby_init();

    cContext = rb_define_class("Context", rb_cObject);
    rb_define_method(cContext, "execute", cContext_execute, 1);

    a = rb_class_new_instance(0, NULL, cContext);
    rb_funcall(a, rb_intern("execute"), 1, rb_str_new2("a = 5 ; puts a"));
    
    b = rb_class_new_instance(0, NULL, cContext);
    rb_funcall(b, rb_intern("execute"), 1, rb_str_new2("puts a"));

    return 0;
}