Skip to content

Instantly share code, notes, and snippets.

@walf443
Forked from mrkn/measure_lexical_with_test.rb
Created November 22, 2008 08:56
Show Gist options
  • Save walf443/27778 to your computer and use it in GitHub Desktop.
Save walf443/27778 to your computer and use it in GitHub Desktop.
module Measure
class Unit
class << self
def unit_name
return nil
end
alias __inspect__ inspect
def inspect
if unit_name
"#<Measure::Unit[#{unit_name}]>"
else
__inspect__
end
end
end
end
class Context
def initialize
@units = {}
end
def has_unit?(name)
name = name.intern if String === name
return @units.has_key? name
end
def [](name)
name = name.intern if String === name
return nil unless has_unit? name
return @units[name]
end
def def_unit(name)
name = name.intern if String === name
unless has_unit? name
@units[name] = Class.new Unit
@units[name].class_eval do
@@unit_name = name
def self.unit_name
@@unit_name
end
end
end
return @units[name]
end
end
class Quantity
def initialize(value, unit)
@value, @unit = value, unit
end
def to_s
"#{@value} [#{@unit.unit_name}]"
end
end
class << self
def default_context
@@default_context ||= Context.new
return @@default_context
end
def current_context
get_thread_context || default_context
end
def with(ctx, &block)
begin
saved_ctx = get_thread_context
set_thread_context ctx
block.call
ensure
set_thread_context saved_ctx
end
end
private
def has_thread_storage?
return !!Thread.current[:measure]
end
def get_thread_storage(key)
return nil unless has_thread_storage?
return Thread.current[:measure][key]
end
def set_thread_storage(key, value)
Thread.current[:measure] ||= {}
Thread.current[:measure][key] = value
return nil
end
def get_thread_context
return nil unless has_thread_storage?
return get_thread_storage(:context)
end
def set_thread_context(context)
if context == Measure.default_context
set_thread_storage :context, nil
else
set_thread_storage :context, context
end
return context
end
def push_call_stack(*values)
stack = get_thread_storage :call_stack
unless stack
stack = []
set_thread_storage :call_stack, stack
end
stack.push values
return nil
end
def pop_call_stack
stack = get_thread_storage :call_stack
return nil unless stack
return stack.pop
end
def call_stack(index)
stack = get_thread_storage :call_stack
return stack[index] if stack
return nil
end
def dump_call_stack
p get_thread_storage :call_stack
end
def with_context
# dump_call_stack
a = call_stack(-3) # expect Measure.with
b = call_stack(-2) # expect Proc.call with Measure::Context
return get_thread_context if a && a[0] == Measure && a[1] == :with
return b[2] if b && b[0] == Proc && b[1] == :call
return nil
end
def set_context_to_proc(f)
# dump_call_stack
a = call_stack(-3) # expected Measure.with
b = call_stack(-2) # expected Proc.call with Measure::Context
if (a && a[0] == Measure && a[1] == :with) ||
(b && b[0] == Proc && b[1] == :call && b[2])
context = Measure.current_context
f.instance_eval { @_measure_context = context }
end
return f
end
tracer = lambda do |event, file, line, id, binding, klass|
case event
when "call"
next if klass == Measure && id != :with
if klass == Proc && (id == :call || id == :[])
f = binding.eval("self")
context = f.instance_eval { @_measure_context }
Measure.module_eval { push_call_stack Proc, :call, context }
else
Measure.module_eval { push_call_stack klass, id }
end
when "return"
top = Measure.module_eval { call_stack(-1) }
if top && top[0] == klass && top[1] == id
Measure.module_eval { pop_call_stack }
end
end
end
set_trace_func tracer
end
end
$measure = Measure::Context.new
$measure.def_unit :km
module Kernel
alias __lambda__ lambda
def lambda(&block)
f = __lambda__(&block)
return Measure.module_eval { set_context_to_proc f }
end
alias __proc__ proc
def proc(&block)
f = __proc__(&block)
return Measure.module_eval { set_context_to_proc f }
end
end
class Proc
class << self
alias __new__ new
def new(&block)
f = __new__(&block)
return Measure.module_eval { set_context_to_proc f }
end
end
alias __call__ call
def call(*args)
if @_measure_context
begin
ctx = @_measure_context
save_ctx = Measure.current_context
Measure.module_eval { set_thread_context ctx }
return __call__(*args)
ensure
Measure.module_eval { set_thread_context save_ctx }
end
else
return __call__(*args)
end
end
alias [] call
end
class Numeric
def km
context = Measure.module_eval { with_context }
if context
Measure::Quantity.new(self, context[:km])
else
self
end
end
end
def method_outside_with
puts "method_outside_with(1): #{1.km}"
end
method_outside_with
$proc_outside_with = lambda { puts "proc_outside_with(1): #{1.km}" }
Measure.with($measure) do
method_outside_with
puts "inside_with(1 [km]): #{1.km}"
lambda { puts "proc_inside_with_call(1 [km]): #{1.km}" }.call
$proc_inside_with = lambda { puts "proc_inside_with(1 [km]): #{1.km}" }
$proc_outside_with.call
lambda {
$proc_inside_proc_inside_with = lambda {
puts "proc_inside_proc_inside_with(1 [km]): #{1.km}"
}
}.call
lambda {
lambda {
$proc_inside_proc_inside_proc_inside_with = lambda {
puts "proc_inside_proc_inside_proc_inside_with(1 [km]): #{1.km}"
}
}.call
}.call
lambda {
lambda {
Measure.with(nil) do
$context_conservation = lambda {
puts "(false): #{Measure.current_context == $measure}"
}
end
}.call
}.call
$proc_inside_proc_inside_with.call
$context_conservation.call
end
module Measure
class Unit
class << self
def unit_name
return nil
end
alias __inspect__ inspect
def inspect
if unit_name
"#<Measure::Unit[#{unit_name}]>"
else
__inspect__
end
end
end
end
class Context
def initialize
@units = {}
end
def has_unit?(name)
name = name.intern if String === name
return @units.has_key?(name)
end
def [](name)
name = name.intern if String === name
return nil unless has_unit? name
return @units[name]
end
def def_unit(name)
name = name.intern if String === name
unless has_unit? name
@units[name] = Class.new Unit
@units[name].class_eval do
@@unit_name = name
def self.unit_name
@@unit_name
end
end
end
return @units[name]
end
end
class Quantity
def initialize(value, unit)
@value, @unit = value, unit
end
def to_s
"#{@value} [#{@unit.unit_name}]"
end
end
class << self
def default_context
@@default_context ||= Context.new
return @@default_context
end
def with(ctx, &block)
begin
saved_ctx, thread_context = thread_context, ctx
block.call
ensure
thread_context = saved_ctx
end
end
private
def has_thread_storage?
return !!Thread.current[:measure]
end
def get_thread_storage(key)
return nil unless has_thread_storage?
return Thread.current[:measure][key]
end
def set_thread_storage(key, value)
Thread.current[:measure] ||= {}
Thread.current[:measure][key] = value
return nil
end
def thread_context
return Measure.default_context unless has_thread_storage?
return get_thread_storage(:context) || Measure.default_context
end
def thread_context=(context)
if context == Measure.default_context
set_thread_storage :context, nil
else
set_thread_storage :context, context
end
return context
end
def push_call_stack(*values)
stack = get_thread_storage :call_stack
unless stack
stack = []
set_thread_storage :call_stack, stack
end
stack.push values
return nil
end
def pop_call_stack
stack = get_thread_storage :call_stack
return nil unless stack
return stack.pop
end
def call_stack(index)
stack = get_thread_storage :call_stack
return stack[index] if stack
return nil
end
def dump_call_stack
p get_thread_storage(:call_stack)
end
def inside_with_block?
a, b = call_stack(-5), call_stack(-4)
return true if b && b[2] == "Proc.call" && b[0]
return true if a && a[2] == "Measure.with"
return false
end
def setup_proc(f)
a = call_stack(-5)
f.instance_eval { @measure_with = a }
return f
end
tracer = lambda do |event, file, line, id, binding, klass|
case event
when "c-call"
if klass == Proc && (id == :call || id == :[])
f = binding.__send__(:eval, "self")
with_flag = f.instance_eval { @measure_with && @measure_with[0] }
Measure.module_eval do
push_call_stack with_flag, nil, "#{klass}.#{id}"
end
end
when "c-return"
if klass == Proc && (id == :call || id == :[])
Measure.module_eval { pop_call_stack }
end
when "call"
with_flag = (klass == Measure && id == :with)
Measure.module_eval do
cont = callcc do |cont|
push_call_stack with_flag, cont, "#{klass}.#{id}"
end
end
when "return"
Measure.module_eval do
pop_call_stack
end
end
end
set_trace_func tracer
end
end
$measure = Measure::Context.new
$measure.def_unit :km
module Kernel
alias __lambda__ lambda
def lambda(&block)
f = __lambda__(&block)
return Measure.module_eval { setup_proc f }
end
alias __proc__ proc
def proc(&block)
f = __proc__(&block)
return Measure.module_eval { setup_proc f }
end
end
class << Proc
alias __new__ new
def new(&block)
f = __new__(&block)
return Measure.module_eval { setup_proc f }
end
end
class Numeric
def km
# Measure.module_eval { dump_call_stack }
if Measure.module_eval { inside_with_block? }
Measure::Quantity.new(self, $measure[:km])
else
self
end
end
end
def foo
p [:foo, 1.km.to_s]
end
foo
$hoge = nil
$fuga = lambda { p [:fuga, 1.km.to_s] }
Measure.with($measure) do
foo
p [:in_with, 1.km.to_s]
lambda { p [:in_with_lambda, 1.km.to_s] }.call
$hoge = lambda { p [:hoge, 1.km.to_s] }
$fuga.call
end
$hoge.call
# on ruby 1.8.6 (2007-09-23 patchlevel 110) [i686-darwin9.1.0]
# [:foo, "1"]
# [:foo, "1"]
# [:in_with, "1 [km]"]
# [:in_with_lambda, "1"]
# [:fuga, "1"]
# [:hoge, "1"]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment