Skip to content
Permalink
Browse files

Extract Noscriptable mixin

  • Loading branch information...
Josep M. Bach
Josep M. Bach committed Jan 25, 2012
1 parent 82ff767 commit 56ecf0359a823b0a7a8fa3a63c6b78a7443a34f0
Showing with 130 additions and 127 deletions.
  1. +16 −16 kernel/test_case.nsc
  2. +6 −6 kernel/traits.nsc
  3. +5 −5 lib/noscript/compiler.rb
  4. +93 −95 lib/noscript/runtime.rb
  5. +1 −1 lib/noscript/signature.rb
  6. +9 −4 test/compiler_test.rb
@@ -478,8 +478,8 @@ US-ASCII
6
errors
x
3
get
16
__noscript_get__
x
5
Empty
@@ -530,8 +530,8 @@ x
10
noscript:+
x
3
put
16
__noscript_put__
p
15
I
@@ -750,8 +750,8 @@ US-ASCII
6
errors
x
3
get
16
__noscript_get__
x
5
Empty
@@ -802,8 +802,8 @@ x
10
noscript:+
x
3
put
16
__noscript_put__
p
15
I
@@ -1038,8 +1038,8 @@ US-ASCII
5
tests
x
3
get
16
__noscript_get__
x
5
Empty
@@ -1325,8 +1325,8 @@ US-ASCII
9
each slot
x
3
get
16
__noscript_get__
x
5
Empty
@@ -1524,8 +1524,8 @@ US-ASCII
5
setup
x
3
get
16
__noscript_get__
x
5
Empty
@@ -1581,8 +1581,8 @@ x
10
noscript:+
x
3
put
16
__noscript_put__
p
17
I
@@ -211,8 +211,8 @@ US-ASCII
6
traits
x
3
get
16
__noscript_get__
x
5
Empty
@@ -261,8 +261,8 @@ x
3
new
x
3
put
16
__noscript_put__
n
x
5
@@ -347,8 +347,8 @@ x
8
__name__
x
3
put
16
__noscript_put__
p
9
I
@@ -125,14 +125,14 @@ def visit_Identifier(o)
elsif o.deref? # @foo equals to self.foo
g.push_self
g.push_literal o.name
g.send :get, 1
g.send :__noscript_get__, 1
g.raise_if_empty NameError, "Object has no slot named #{o.name}"
elsif o.self?
g.push_self
elsif s.slot_for(o.name)
visit_LocalVariableAccess(o)
else
raise "CANT FIND #{o.name}"
raise "BUG: CANT FIND #{o.name}"
end
end

@@ -177,7 +177,7 @@ def visit_LocalVariableAssignment(o)
elsif identifier.deref?
g.push_literal name
g.swap
g.send :put, 2
g.send :__noscript_put__, 2
else
s.set_local name
end
@@ -212,7 +212,7 @@ def visit_SlotGet(o)
g.find_const o.name.name.to_sym
else
g.push_literal o.name.name.to_sym
g.send :get, 1
g.send :__noscript_get__, 1
end

g.raise_if_empty NameError, "Object has no slot named #{o.name}"
@@ -223,7 +223,7 @@ def visit_SlotAssign(o)
o.receiver.accept(self)
g.push_literal o.name
o.value.accept(self)
g.send :put, 2
g.send :__noscript_put__, 2
end

def visit_IfNode(o)
@@ -1,3 +1,86 @@
module Noscriptable
def __noscript_prototype__
@__noscript_prototype__
end

def __noscript_prototype__=(proto)
@__noscript_prototype__ = proto
end

def __noscript_slots__
@__noscript_slots__ ||= begin
slots = Rubinius::LookupTable.new
slots[:__name__] = "Object"
slots[:traits] = []
slots
end
end

def __noscript_get__(name)
if __noscript_slots__.key?(name)
__noscript_slots__[name]
elsif self.methods.include?(:"noscript:#{name}")
self.method(:"noscript:#{name}")
elsif method = __noscript_trait_has__(name)
method
elsif proto = __noscript_prototype__
proto.__noscript_get__(name)
else
Empty.new
end
end

def __noscript_trait_has__(name)
# Check for an explicit call, i.e. @Businessman run()
explicit_method = __noscript_slots__[:traits].map do |trait|
explicit_name = name.to_s.split(trait.__noscript_slots__[:__name__]).last.strip.to_sym
if name != explicit_name && trait.__noscript_has_property__(explicit_name, false)
trait.__noscript_get__(explicit_name)
else
nil
end
end.compact.first
return explicit_method if explicit_method

# Otherwise, check for the normal trait chain
matching = __noscript_slots__[:traits].map do |trait|
if trait.__noscript_has_property__(name, false)
trait.__noscript_get__(name)
else
nil
end
end.compact
return false if matching.length == 0
raise "Trait conflict: ##{name} is implemented by more than one trait." if matching.length > 1
return matching.first
end

def __noscript_has_property__(name, lookup=true)
if result = __noscript_slots__.key?(name)
result
elsif lookup && __noscript_trait_has__(name)
true
elsif lookup && proto = __noscript_prototype__
proto.__noscript_has_property__(name)
else
false
end
end

def __noscript_put__(name, object)
__noscript_slots__[name] = object
end

def method_missing(m, *args)
function = m.to_s.split(":").last.to_sym
if __noscript_has_property__(function)
fn = __noscript_get__(function)
return fn.call(*args) # First arg is this.
end
super
end
end

class Module
def noscript_alias(noscript_name, ruby_name=nil)
Array(noscript_name).each do |noscript|
@@ -18,6 +101,7 @@ def noscript_def(name, &block)
end

class Object
# include Noscriptable
def noscript_send(name, *args)
__send__ "noscript:#{send}", *args
end
@@ -40,34 +124,22 @@ class Empty
end

class Runtime
# Object protocol:
#
# get(name<Symbol>) => object
# put(name<Symbol>, object<Object>)
#
class ObjectType < Rubinius::LookupTable
attr_accessor :prototype

def initialize
@prototype = nil
self[:__name__] = "Object"
self[:traits] = []
end

class ObjectKind
include Noscriptable
noscript_def("clone") do |*args|
obj = ObjectType.new
obj.prototype = self
obj = ObjectKind.new
obj.__noscript_prototype__ = self
if properties = args.first
properties.keys.each do |k|
obj.put(k.to_sym, properties[k])
obj.__noscript_put__(k.to_sym, properties[k])
end
end
obj
end

noscript_def("each slot") do |*args|
fn = args.shift
to_a.each do |k, v|
__noscript_slots__.to_a.each do |k, v|
fn.call(self, k.to_s, v)
end
end
@@ -77,88 +149,14 @@ def initialize
end

noscript_def("put") do |k, v|
put(k, v)
__noscript_put__(k, v)
end

noscript_def("get") do |slot|
get(slot)
end

# def function(name, block=name)
# if block.is_a?(Symbol)
# block = method(block).executable
# else
# block = block.code
# end

# self[name] = Function.new(name, block)
# end

def get(name)
if self.key?(name)
self[name]
elsif self.methods.include?(:"noscript:#{name}")
self.method(:"noscript:#{name}")
elsif method = trait_implementation_of(name)
method
elsif proto = prototype
proto.get(name)
else
Empty.new
end
end

def trait_implementation_of(name)
# Check for an explicit call, i.e. @Businessman run()
explicit_method = self[:traits].map do |trait|
explicit_name = name.to_s.split(trait[:__name__]).last.strip.to_sym
if name != explicit_name && trait.key?(explicit_name)
trait.get(explicit_name)
else
nil
end
end.compact.first
return explicit_method if explicit_method

# Otherwise, check for the normal trait chain
matching = self[:traits].map do |trait|
if trait.key?(name)
trait.get(name)
else
nil
end
end.compact
return false if matching.length == 0
raise "Trait conflict: ##{name} is implemented by more than one trait." if matching.length > 1
return matching.first
end

def put(name, object)
self[name] = object
end

def has_property?(name)
if result = key?(name)
result
elsif trait_implementation_of(name)
true
elsif proto = prototype
proto.has_property?(name)
else
false
end
end

def method_missing(m, *args)
fun = m.to_s.split(":").last.to_sym
if has_property?(fun)
this = args.shift
return get(fun).call(this, *args)
end
super
__noscript_get__(slot)
end
end
Object = ObjectType.new
Object = ObjectKind.new
end

class Function
@@ -1,3 +1,3 @@
# This file is generated by `rake signature`. The signature
# is used to ensure that only current compiled files are loaded.
Noscript::Signature = Rubinius::Signature ^ 1377728806764044089
Noscript::Signature = Rubinius::Signature ^ 9737304784934064492
Oops, something went wrong.

0 comments on commit 56ecf03

Please sign in to comment.
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.