Separate code space/sandbox?

Hi,

I’m interested in figuring out a way to get a separate scope. I do not
need any other traditional sandbox features, i.e. any code goes, I
just want code evaluated in the scope to no affect the parent scope
(at all). Example illustrating what I am after:

a = 10
SeparatedScope.new do
a = a + 1
p a
end
p a

yielding:
11
10

I’d like it to have the same initialization/separation also for
instance variables and even global variables if possible.

Is something like that already available? If not, any good ideas for
how to implement it?

I did have a quick look at sandbox by _why, seems may do what I want
but not so keen on the interpreter patching etc so looking for
opportunities.

Regards,
Patrik

From: “Patrik S.” [email protected]

end
p a

yielding:
11
10

I’d like it to have the same initialization/separation also for
instance variables and even global variables if possible.

This isn’t very pretty, but…

class SeparatedScope
def initialize(code)
eval(code)
end
end

a = 10
SeparatedScope.new %{
a = 11
p a
}
p a

Note there’s no access to outer scope variables at all in
this case, thus the inner “a = 11” instead of “a = a + 1”.

:frowning:

Regards,

Bill

Bill K. wrote:

Note there’s no access to outer scope variables at all in
this case, thus the inner “a = 11” instead of “a = a + 1”.

:frowning:

yeah, that doesn’t quite fly for my purpose - i want an initialization
from current scope, then no impact on the parent scope.

Caleb C. wrote:

Commentary:

  1. This is considerably more complicated (=uglier) than Bill’s
    attempt. There’s even an eval lurking in there, which oughtn’t be
    necessary when using macros. However, it does seem to fulfill all your
    requirements.

  2. A more sophisticated variable renaming scheme may be required if
    you want your program to have variables which end with an underscore.

  3. Use of eval inside a separated_scope will confuse things. Variables
    used in the eval arg will refer to the outer scope, not the separated
    scope.

  4. Macros are not a standard part of ruby syntax; they require my
    macro preprocessor, rubymacros. do a ‘gem install rubymacros’ to get
    it.
    Also see GitHub - coatl/rubymacros: RubyMacros is a lisp-like macro pre-processor for Ruby. More than just a purely textual substitution scheme, RubyMacros can manipulate and morph Ruby parse trees (in the form of RedParse Nodes) at parse time in just about any way you see fit.

  5. Code that defines or uses macros must be pulled into the
    interpreter using a special version of require: Macro.require. That’s
    why there’s a separate file which requires ‘macro’, then calls
    Macro.require.

Awesome! I had not seen rubymacros before. I’ll dig into this and try to
understand it in more detail.

Thank you very much for the help.

On 11/11/09, Patrik S. [email protected] wrote:

p a
end
p a

yielding:
11
10

I’d like it to have the same initialization/separation also for
instance variables and even global variables if possible.

Here’s a macro which seems to do the job. Comments follow.

#file separated_scope_user.rb
require ‘rubygems’
require ‘macro’
Macro.require ‘example/separated_scope’

#file separated_scope.rb
macro separated_scope
code=yield
localnames=[]
code.depthwalk{|parent,i,j,node|
if RedParse::VarNode===node
node.name<<‘
localnames<<node.name
end
}
:frowning:
^localnames.uniq.inject(:(nil)){|sum,lvar|
RedParse::AssignNode[ RedParse::VarNode[lvar],‘=’,sum ]
}
eval local_variables.map{|lvar| lvar+"
="+lvar}.join(’;')
^code
)
end

a = 10
separated_scope do
a = a + 1
p a #=>11
end
p a #=>10

Commentary:

  1. This is considerably more complicated (=uglier) than Bill’s
    attempt. There’s even an eval lurking in there, which oughtn’t be
    necessary when using macros. However, it does seem to fulfill all your
    requirements.

  2. A more sophisticated variable renaming scheme may be required if
    you want your program to have variables which end with an underscore.

  3. Use of eval inside a separated_scope will confuse things. Variables
    used in the eval arg will refer to the outer scope, not the separated
    scope.

  4. Macros are not a standard part of ruby syntax; they require my
    macro preprocessor, rubymacros. do a ‘gem install rubymacros’ to get
    it.
    Also see GitHub - coatl/rubymacros: RubyMacros is a lisp-like macro pre-processor for Ruby. More than just a purely textual substitution scheme, RubyMacros can manipulate and morph Ruby parse trees (in the form of RedParse Nodes) at parse time in just about any way you see fit.

  5. Code that defines or uses macros must be pulled into the
    interpreter using a special version of require: Macro.require. That’s
    why there’s a separate file which requires ‘macro’, then calls
    Macro.require.

If you get stuck or can’t understand something, don’t hesitate to ask
me.

Sweet I want to use macros. How about one to support ++? :slight_smile:
-r

On 11/12/09, Patrik S. [email protected] wrote:

Awesome! I had not seen rubymacros before. I’ll dig into this and try to
understand it in more detail.

If you get stuck or can’t understand something, don’t hesitate to ask
me.

On 11/12/09, Roger P. [email protected] wrote:

If you get stuck or can’t understand something, don’t hesitate to ask
me.

Sweet I want to use macros. How about one to support ++

Hmm, I had thought that ruby’s immutable numeric types meant this was
impossible, but maybe it is feasible to implement as a macro… (not
as a method).

The big problem is that I haven’t (uh, yet) written the magic that
will allow users to define their own operators. (Or (re)define
existing operators as macros…)

But other than that, this ought to be possible… you’d need to use
different names until operator macros are defined, tho.
The preincrement version could be written like this:

macro preincr(n)
:frowning: ^n+=1 )
end

postincrement is somewhat more difficult. Here’s a first pass:

macro postincr(n)
:frowning: (res=^n;^n+=1;res) )
end

(Warning: both increment macros are untested.)

but postincr is problematic. A call like:
postincr( a().b )
would cause a().b to be evaluated twice, which isn’t right. Solving
this, so that side effects in the arg to postincr are evaluated only
once, is more complicated, and I won’t attempt it here.

And nobody wants to type preincr or postincr (or even incr) when they
could just use +=1, so we’d really need user-defined operators
here…

? :slight_smile:

Is that a self-portrait? :wink: