maroon 0.6.1 → 0.6.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Examples/Dijkstra/CalculateShortestDistance.rb +16 -12
- data/Examples/Dijkstra/calculate_shortest_path.rb +47 -27
- data/Examples/Dijkstra/data.rb +41 -14
- data/Examples/Dijkstra/dijkstra.rb +4 -3
- data/Examples/MoneyTransfer.rb +61 -60
- data/Examples/greeter.rb +8 -7
- data/Examples/meter.rb +35 -29
- data/Gemfile +9 -4
- data/LICENSE.txt +21 -21
- data/README.md +13 -0
- data/Rakefile +41 -1
- data/Test/Generate/method_info_test.rb +12 -0
- data/Test/{Greeter_test.rb → Greeter_test_disabled.rb} +24 -20
- data/Test/ImmutableQueue_test.rb +18 -0
- data/Test/MethodInfo_test.rb +65 -0
- data/Test/alltests.rb +1 -0
- data/Test/{source_assertions.rb → assertions.rb} +15 -7
- data/Test/bind_test.rb +13 -0
- data/Test/expression_test.rb +105 -0
- data/Test/method_call_test.rb +83 -0
- data/Test/self_test.rb +46 -0
- data/Test/stack_test.rb +17 -0
- data/Test/test_helper.rb +14 -0
- data/base/ImmutableStack.rb +32 -0
- data/base/MethodDefinition.rb +124 -0
- data/base/bind_rewriter.rb +58 -0
- data/base/immutable_queue.rb +50 -0
- data/base/maroon_base.rb +267 -0
- data/base/method_call.rb +78 -0
- data/base/method_info.rb +86 -0
- data/base/self.rb +60 -0
- data/generated/build.rb +13 -0
- data/generated/interpretation_context.rb +29 -0
- data/lib/Bind.rb +65 -0
- data/lib/Context.rb +187 -0
- data/lib/ImmutableQueue.rb +50 -0
- data/lib/ImmutableStack.rb +38 -0
- data/lib/MethodCall.rb +91 -0
- data/lib/MethodDefinition.rb +114 -0
- data/lib/MethodInfo.rb +78 -0
- data/lib/Self.rb +71 -0
- data/lib/build.rb +14 -0
- data/lib/interpretation_context.rb +30 -0
- data/lib/maroon/contracts.rb +43 -0
- data/lib/maroon/kernel.rb +2 -0
- data/lib/maroon/version.rb +3 -3
- data/maroon.gemspec +26 -26
- metadata +49 -31
- data/lib/Source_cleaner.rb +0 -34
- data/lib/maroon.rb +0 -165
- data/lib/rewriter.rb +0 -185
@@ -0,0 +1,38 @@
|
|
1
|
+
class ImmutableStack
|
2
|
+
|
3
|
+
def pop()
|
4
|
+
[@head, @tail]
|
5
|
+
end
|
6
|
+
|
7
|
+
def push(element)
|
8
|
+
ImmutableStack.new(element, self)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.empty()
|
12
|
+
@@empty ||= self.new(nil, nil)
|
13
|
+
end
|
14
|
+
|
15
|
+
def each()
|
16
|
+
yield(head)
|
17
|
+
t = tail
|
18
|
+
while t.!=(ImmutableStack.empty) do
|
19
|
+
h, t = t.pop
|
20
|
+
yield(h)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(h,t)
|
26
|
+
@head = h
|
27
|
+
@tail = t
|
28
|
+
self.freeze
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
attr_reader :head
|
34
|
+
attr_reader :tail
|
35
|
+
|
36
|
+
|
37
|
+
|
38
|
+
end
|
data/lib/MethodCall.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
class MethodCall
|
2
|
+
|
3
|
+
def rewrite_call?()
|
4
|
+
method_name = method[2]
|
5
|
+
if (method[0] == :call) then
|
6
|
+
if (method[1] == nil) and ((method.length < 5) and (method[3] and ((method[3].length == 1) and (method[3][0] == :arglist)))) then
|
7
|
+
is_role = interpretation_context.roles.has_key?(method[3])
|
8
|
+
method[3] = (":@" + method[3].to_sym) if is_role
|
9
|
+
else
|
10
|
+
role_name, is_role_method = self_method_role_method_call?(method_name)
|
11
|
+
if is_role_method then
|
12
|
+
method[1] = nil
|
13
|
+
method[2] = ((("self_" + role_name.to_s) + "_") + method_name.to_s).to_sym
|
14
|
+
else
|
15
|
+
if interpretation_context.roles.has_key?(role_name) then
|
16
|
+
unless (method.length == 3) and ((method[1] == nil) and (method[2] == role_name)) then
|
17
|
+
contract_methods = interpretation_context.contracts[role_name] ||= {}
|
18
|
+
contract_methods[method_name] ||= 0
|
19
|
+
contract_methods[method_name] += 1
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(method,interpretation_context)
|
29
|
+
raise("No method supplied") unless method
|
30
|
+
@method = method
|
31
|
+
@interpretation_context = interpretation_context
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.call(*args)
|
36
|
+
arity = MethodCall.method(:new).arity
|
37
|
+
newArgs = args[0..arity-1]
|
38
|
+
obj = MethodCall.new *newArgs
|
39
|
+
if arity < args.length
|
40
|
+
methodArgs = args[arity..-1]
|
41
|
+
obj.rewrite_call? *methodArgs
|
42
|
+
else
|
43
|
+
obj.rewrite_call?
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def call(*args);rewrite_call? *args; end
|
48
|
+
|
49
|
+
private
|
50
|
+
attr_reader :interpretation_context
|
51
|
+
attr_reader :method
|
52
|
+
|
53
|
+
|
54
|
+
def self_interpretation_context_role_aliases()
|
55
|
+
(@interpretation_context.role_aliases or {})
|
56
|
+
end
|
57
|
+
|
58
|
+
def self_method_call_in_block?()
|
59
|
+
@in_block unless (@in_block == nil)
|
60
|
+
@in_block = (method and (method[0] == :lvar))
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
def self_method_get_role_definition()
|
65
|
+
is_call_expression = (method and (method[0] == :call))
|
66
|
+
self_is_instance_expression = (is_call_expression and (not method[1]))
|
67
|
+
role_name = nil
|
68
|
+
role_name = method[2] if self_is_instance_expression
|
69
|
+
if (not self_is_instance_expression) and method[1] then
|
70
|
+
if (method[1][1] == nil) and (method[1][0] == :call) then
|
71
|
+
role_name = method[1][2]
|
72
|
+
end
|
73
|
+
if (method[1][0] == :lvar) then
|
74
|
+
role_name = self_interpretation_context_role_aliases[method[1][1]]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
role = role_name ? (interpretation_context.roles[role_name]) : (nil)
|
78
|
+
[role, role ? (role_name) : (nil)]
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
def self_method_role_method_call?(method_name)
|
83
|
+
return [nil, nil] unless method
|
84
|
+
role, role_name = self_method_get_role_definition
|
85
|
+
is_role_method = (role and role.has_key?(method_name))
|
86
|
+
return [role_name, is_role_method]
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
class MethodDefinition
|
2
|
+
|
3
|
+
def rebind()
|
4
|
+
@exp, @expressions = expressions.pop
|
5
|
+
@block, @potential_bind = nil
|
6
|
+
if @exp and (@exp.instance_of?(Sexp) and (@exp[0] == :iter)) then
|
7
|
+
@exp[(1..-1)].each do |expr|
|
8
|
+
if expr and (expr.length and (expr[0] == :block)) then
|
9
|
+
@block, @potential_bind = expr, expr[1]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
@expressions = @exp.instance_of?(Sexp) ? (@expressions.push_array(exp)) : (@expressions)
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
def transform()
|
18
|
+
until self_expressions_empty? do
|
19
|
+
(self_block_transform
|
20
|
+
if exp and exp.instance_of?(Sexp) then
|
21
|
+
is_indexer = ((exp[0] == :call) and ((exp[1] == nil) and ((exp[2] == :[]) or (exp[2] == :[]=))))
|
22
|
+
if (is_indexer or (exp[0] == :self)) and @interpretation_context.defining_role then
|
23
|
+
Self.new(exp, interpretation_context).execute
|
24
|
+
end
|
25
|
+
if (exp[0] == :call) then
|
26
|
+
MethodCall.new(exp, interpretation_context).rewrite_call?
|
27
|
+
end
|
28
|
+
end
|
29
|
+
rebind)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(exp,interpretationcontext)
|
34
|
+
no_exp = "No expression supplied".to_sym
|
35
|
+
no_ctx = "No interpretation context".to_sym
|
36
|
+
raise(no_exp) unless exp
|
37
|
+
raise(no_ctx) unless interpretationcontext
|
38
|
+
@interpretation_context = interpretationcontext
|
39
|
+
@expressions = ImmutableQueue.empty.push(exp)
|
40
|
+
rebind
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.call(*args)
|
45
|
+
arity = MethodDefinition.method(:new).arity
|
46
|
+
newArgs = args[0..arity-1]
|
47
|
+
obj = MethodDefinition.new *newArgs
|
48
|
+
if arity < args.length
|
49
|
+
methodArgs = args[arity..-1]
|
50
|
+
obj.transform *methodArgs
|
51
|
+
else
|
52
|
+
obj.transform
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def call(*args);transform *args; end
|
57
|
+
|
58
|
+
private
|
59
|
+
attr_reader :interpretation_context
|
60
|
+
attr_reader :exp
|
61
|
+
attr_reader :expressions
|
62
|
+
attr_reader :potential_bind
|
63
|
+
attr_reader :block
|
64
|
+
|
65
|
+
|
66
|
+
def self_interpretation_context_addalias(key,value)
|
67
|
+
@interpretation_context.role_aliases[key] = value
|
68
|
+
end
|
69
|
+
|
70
|
+
def self_expressions_empty?()
|
71
|
+
(expressions == ImmutableQueue.empty)
|
72
|
+
end
|
73
|
+
|
74
|
+
def self_potential_bind_is_bind?()
|
75
|
+
potential_bind and (potential_bind.length and ((potential_bind[0] == :call) and ((potential_bind[1] == nil) and (potential_bind[2] == :bind))))
|
76
|
+
end
|
77
|
+
|
78
|
+
def self_block_transform()
|
79
|
+
if block then
|
80
|
+
@expressions.push_array(block[(1..-1)]) if self_block_transform_bind?
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def self_block_transform_bind?()
|
85
|
+
self_potential_bind_is_bind? and self_block_rewrite
|
86
|
+
end
|
87
|
+
|
88
|
+
def self_block_rewrite()
|
89
|
+
changed = false
|
90
|
+
arguments = potential_bind[3]
|
91
|
+
if arguments and (arguments[0] == :hash) then
|
92
|
+
block.delete_at(1)
|
93
|
+
count = ((arguments.length - 1) / 2)
|
94
|
+
(1..count).each do |j|
|
95
|
+
temp = (j * 2)
|
96
|
+
local = arguments[(temp - 1)][1]
|
97
|
+
local = local[1] if local.instance_of?(Sexp)
|
98
|
+
raise("invalid value for role alias") unless local.instance_of?(Symbol)
|
99
|
+
aliased_role = arguments[temp][1]
|
100
|
+
aliased_role = aliased_role[1] if aliased_role.instance_of?(Sexp)
|
101
|
+
unless aliased_role.instance_of?(Symbol) and interpretation_context.roles.has_key?(aliased_role) then
|
102
|
+
raise(((aliased_role.to_s + "used in binding is an unknown role ") + roles.to_s))
|
103
|
+
end
|
104
|
+
self_interpretation_context_addalias(local, aliased_role)
|
105
|
+
Bind.new(local, aliased_role, block).execute
|
106
|
+
changed = true
|
107
|
+
end
|
108
|
+
end
|
109
|
+
changed
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
end
|
data/lib/MethodInfo.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
class MethodInfo
|
2
|
+
|
3
|
+
def initialize(on_self,block_source,is_private)
|
4
|
+
raise("Must be S-Expressions") unless block_source.instance_of?(Sexp)
|
5
|
+
if on_self.instance_of?(Hash) then
|
6
|
+
@block = on_self[:block]
|
7
|
+
@on_self = on_self[:self]
|
8
|
+
else
|
9
|
+
@on_self = on_self
|
10
|
+
end
|
11
|
+
@block_source = block_source
|
12
|
+
@private = is_private
|
13
|
+
self.freeze
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
def is_private()
|
18
|
+
@private
|
19
|
+
end
|
20
|
+
|
21
|
+
def build_as_context_method(context_method_name,interpretation_context)
|
22
|
+
MethodDefinition.new(self_block_source_body, interpretation_context).transform
|
23
|
+
body = Ruby2Ruby.new.process(self_block_source_body)
|
24
|
+
args = if self_block_source_arguments then
|
25
|
+
(("(" + self_block_source_arguments) + ")")
|
26
|
+
else
|
27
|
+
""
|
28
|
+
end
|
29
|
+
on = on_self ? ("self.") : ("")
|
30
|
+
(((((("\ndef " + on.to_s) + context_method_name.to_s) + args) + "\n ") + body) + "\n end\n")
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
attr_reader :on_self
|
36
|
+
attr_reader :block
|
37
|
+
attr_reader :block_source
|
38
|
+
|
39
|
+
|
40
|
+
def self_block_source_get_arguments()
|
41
|
+
sexp = block_source[2]
|
42
|
+
return nil unless sexp
|
43
|
+
return sexp[1] if (sexp[0] == :lasgn)
|
44
|
+
return [] if (sexp[1] == nil)
|
45
|
+
sexp = sexp[(1..-1)]
|
46
|
+
args = []
|
47
|
+
sexp.each do |e|
|
48
|
+
(args << (if e.instance_of?(Symbol) then
|
49
|
+
e
|
50
|
+
else
|
51
|
+
(e[0] == :splat) ? (("*" + e[1][1].to_s)) : (e[1])
|
52
|
+
end))
|
53
|
+
end
|
54
|
+
if block then
|
55
|
+
b = ("&" + block.to_s)
|
56
|
+
if args then
|
57
|
+
args = [args] unless args.instance_of?(Array)
|
58
|
+
(args << b)
|
59
|
+
else
|
60
|
+
args = [b]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
args
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
def self_block_source_arguments()
|
68
|
+
args = self_block_source_get_arguments
|
69
|
+
args and args.length ? (args.join(",")) : (nil)
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
def self_block_source_body()
|
74
|
+
block_source[3]
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
end
|
data/lib/Self.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
class Self
|
2
|
+
|
3
|
+
def initialize(abstract_syntax_tree,interpretationcontext)
|
4
|
+
raise("Interpretation context missing") unless interpretationcontext
|
5
|
+
unless interpretationcontext.defining_role then
|
6
|
+
raise("Must have a defining role")
|
7
|
+
end
|
8
|
+
@abstract_syntax_tree = abstract_syntax_tree
|
9
|
+
@interpretation_context = interpretationcontext
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
def execute()
|
14
|
+
if abstract_syntax_tree then
|
15
|
+
if (abstract_syntax_tree[0] == :self) then
|
16
|
+
abstract_syntax_tree[0] = :call
|
17
|
+
abstract_syntax_tree[1] = nil
|
18
|
+
abstract_syntax_tree[2] = interpretation_context.defining_role
|
19
|
+
else
|
20
|
+
if (abstract_syntax_tree[0] == :call) and (abstract_syntax_tree[1] == nil) then
|
21
|
+
method_name = abstract_syntax_tree[2]
|
22
|
+
if ((method_name == :[]) or (method_name == :[]=)) then
|
23
|
+
get_role = Sexp.new
|
24
|
+
get_role[0] = :call
|
25
|
+
get_role[1] = nil
|
26
|
+
get_role[2] = interpretation_context.defining_role
|
27
|
+
abstract_syntax_tree[1] = get_role
|
28
|
+
end
|
29
|
+
else
|
30
|
+
if abstract_syntax_tree.instance_of?(Sexp) then
|
31
|
+
if self_abstract_syntax_tree_is_indexer_call_on_self then
|
32
|
+
getter = new(Sexp.new)
|
33
|
+
getter[0] = :call
|
34
|
+
getter[1] = nil
|
35
|
+
getter[2] = interpretation_context.defining_role
|
36
|
+
arglist = Sexp.new
|
37
|
+
getter[3] = arglist
|
38
|
+
arglist[0] = :arglist
|
39
|
+
abstract_syntax_tree[1] = getter
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.call(*args)
|
48
|
+
arity = Self.method(:new).arity
|
49
|
+
newArgs = args[0..arity-1]
|
50
|
+
obj = Self.new *newArgs
|
51
|
+
if arity < args.length
|
52
|
+
methodArgs = args[arity..-1]
|
53
|
+
obj.execute *methodArgs
|
54
|
+
else
|
55
|
+
obj.execute
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def call(*args);execute *args; end
|
60
|
+
|
61
|
+
private
|
62
|
+
attr_reader :abstract_syntax_tree
|
63
|
+
attr_reader :interpretation_context
|
64
|
+
|
65
|
+
|
66
|
+
def self_abstract_syntax_tree_is_indexer_call_on_self()
|
67
|
+
(abstract_syntax_tree.length == 4) and ((abstract_syntax_tree[0] == :call) and ((abstract_syntax_tree[1] == nil) and ((abstract_syntax_tree[2] == :[]) and (abstract_syntax_tree[3][0] == :argslist))))
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
end
|
data/lib/build.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'sorcerer'
|
2
|
+
require 'sourcify'
|
3
|
+
|
4
|
+
require_relative 'self'
|
5
|
+
require_relative 'bind'
|
6
|
+
require_relative 'ImmutableQueue'
|
7
|
+
require_relative 'ImmutableStack'
|
8
|
+
require_relative 'interpretation_context'
|
9
|
+
require_relative 'MethodDefinition'
|
10
|
+
require_relative 'MethodInfo'
|
11
|
+
require_relative 'MethodCall'
|
12
|
+
|
13
|
+
require_relative 'Context'
|
14
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class InterpretationContext
|
2
|
+
def contracts
|
3
|
+
(@contracts ||= {})
|
4
|
+
end
|
5
|
+
|
6
|
+
def roles
|
7
|
+
(@roles ||= {})
|
8
|
+
end
|
9
|
+
|
10
|
+
def role_aliases
|
11
|
+
(@role_aliases ||= {})
|
12
|
+
end
|
13
|
+
|
14
|
+
def defining_role
|
15
|
+
@defining_role
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(roles, contracts, role_aliases, defining_role)
|
19
|
+
raise "Aliases must be a hash" unless role_aliases.instance_of? Hash or role_aliases == nil
|
20
|
+
raise "Roles must be a hash" unless roles.instance_of? Hash or roles == nil
|
21
|
+
raise "Contracts must be a hash" unless contracts.instance_of? Hash or contracts == nil
|
22
|
+
|
23
|
+
@roles = roles
|
24
|
+
raise "Defining role is undefined" if defining_role && (!self.roles.has_key? defining_role)
|
25
|
+
|
26
|
+
@contracts = contracts
|
27
|
+
@role_aliases = role_aliases
|
28
|
+
@defining_role = defining_role
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
if Kernel::const_defined? :MiniTest
|
2
|
+
class ContextAsserterBase < MiniTest::Unit::TestCase
|
3
|
+
|
4
|
+
end
|
5
|
+
else
|
6
|
+
class ContextAsserterBase
|
7
|
+
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class ContextAsserter < ContextAsserterBase
|
12
|
+
def initialize(contracts, obj, is_asserting = true)
|
13
|
+
raise 'Contracts must be supplied' unless contracts
|
14
|
+
@obj = obj
|
15
|
+
@contracts = contracts
|
16
|
+
@is_asserting = is_asserting
|
17
|
+
end
|
18
|
+
|
19
|
+
def is_test
|
20
|
+
Kernel::const_defined? :MiniTest
|
21
|
+
end
|
22
|
+
|
23
|
+
def can_play(role)
|
24
|
+
msg = "#{@is_asserting ? 'Was' : "wasn't"} expected to be able to play #{role}".intern
|
25
|
+
if is_test
|
26
|
+
assert_equal(@is_asserting, can_play?(role), msg)
|
27
|
+
else
|
28
|
+
raise msg if @is_asserting != can_play?(role)
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
def can_play?(role)
|
35
|
+
required_methods = @contracts[role]
|
36
|
+
methods = @obj.public_methods
|
37
|
+
missing = required_methods.select do |rm|
|
38
|
+
!methods.include? rm.to_sym
|
39
|
+
end
|
40
|
+
missing.length == 0
|
41
|
+
end
|
42
|
+
end
|
43
|
+
Context::with_contracts = true
|