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
data/Test/alltests.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
`git ls-files`.split($/).grep(%r{(test|spec|features).rb}).select {|f| p f; require_relative("../#{f}")}
|
@@ -1,13 +1,21 @@
|
|
1
|
-
|
1
|
+
require 'ripper'
|
2
|
+
require 'ruby2ruby'
|
3
|
+
|
4
|
+
module SourceAssertions
|
2
5
|
def assert_source_equal(expected, actual)
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
+
|
7
|
+
expected_sexp = if expected.instance_of? String then Ripper::sexp expected else expected end
|
8
|
+
actual_sexp = if actual.instance_of? String then Ripper::sexp actual else actual end
|
9
|
+
|
10
|
+
message = "
|
11
|
+
Expected: #{expected}
|
12
|
+
but got: #{actual}"
|
13
|
+
assert_sexp_with_ident(expected_sexp, actual_sexp, message)
|
6
14
|
assert_equal(1,1) #just getting the correct assertion count
|
7
15
|
end
|
8
16
|
|
9
17
|
def is_terminal(sexp)
|
10
|
-
sexp == :@ident || sexp == :@int || sexp == :@ivar
|
18
|
+
sexp == :@ident || sexp == :@int || sexp == :@ivar || :@tstring_content
|
11
19
|
end
|
12
20
|
|
13
21
|
def assert_sexp_with_ident(expected, actual, message)
|
@@ -28,11 +36,11 @@ module Source_assertions
|
|
28
36
|
if actual[i].instance_of? Array
|
29
37
|
assert_sexp_with_ident(expected[i], actual[i], message)
|
30
38
|
else
|
31
|
-
|
39
|
+
refute(true, message || "the arrays differ at index #{i}. Actual was an element but an array was expected")
|
32
40
|
end
|
33
41
|
else
|
34
42
|
if expected[i] != actual[i]
|
35
|
-
|
43
|
+
assert_equal(expected[i],actual[i], message || "the arrays differ at index #{i}")
|
36
44
|
end
|
37
45
|
end
|
38
46
|
end
|
data/Test/bind_test.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require_relative '../generated/bind'
|
2
|
+
require_relative 'test_helper'
|
3
|
+
|
4
|
+
class Bind_test < MiniTest::Unit::TestCase
|
5
|
+
def test_sunny
|
6
|
+
block = Sexp.new
|
7
|
+
Bind.new(:role, :alias, block).execute
|
8
|
+
assert_equal(nil, block[0])
|
9
|
+
assert_equal(:@alias, block[1][2][1])
|
10
|
+
assert_equal(:role, block[2][2][1])
|
11
|
+
assert_equal(:@alias, block.last()[1])
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require_relative '../generated/MethodDefinition'
|
2
|
+
require_relative 'test_helper'
|
3
|
+
require 'ripper'
|
4
|
+
|
5
|
+
class Expression_test < MiniTest::Unit::TestCase
|
6
|
+
include SourceAssertions
|
7
|
+
def get_context
|
8
|
+
contracts = {}
|
9
|
+
roles = {:foo=>{:bar=>nil},:baz=>{:rolemethod=>nil},:role=>{}}
|
10
|
+
aliases = {}
|
11
|
+
InterpretationContext.new(roles,contracts,aliases,:role)
|
12
|
+
end
|
13
|
+
def assert_transform(expected,block)
|
14
|
+
raise "No block" unless block
|
15
|
+
|
16
|
+
ctx = get_context
|
17
|
+
md = MethodDefinition.new(block,ctx)
|
18
|
+
md.transform
|
19
|
+
|
20
|
+
assert_source_equal(expected,block)
|
21
|
+
ctx
|
22
|
+
end
|
23
|
+
def test_method_call
|
24
|
+
block = (get_sexp { baz.rolemethod })[3]
|
25
|
+
|
26
|
+
expected = (get_sexp { self_baz_rolemethod })[3]
|
27
|
+
|
28
|
+
assert_transform(expected,block)
|
29
|
+
end
|
30
|
+
def test_index
|
31
|
+
block = (get_sexp do
|
32
|
+
self[0]
|
33
|
+
end)[3]
|
34
|
+
expected = (get_sexp do
|
35
|
+
role[0]
|
36
|
+
end)[3]
|
37
|
+
ctx = assert_transform(expected,block)
|
38
|
+
assert_equal(1,ctx.contracts[:role][:[]])
|
39
|
+
end
|
40
|
+
def test_bind
|
41
|
+
block = get_sexp do [].each do |r|
|
42
|
+
bind :r=>:foo
|
43
|
+
r.bar
|
44
|
+
foo.baz
|
45
|
+
end
|
46
|
+
end
|
47
|
+
expected = (get_sexp do
|
48
|
+
[].each do |r|
|
49
|
+
temp____foo = @foo
|
50
|
+
@foo = r
|
51
|
+
self_foo_bar
|
52
|
+
foo.baz
|
53
|
+
@foo = temp____foo
|
54
|
+
end
|
55
|
+
end)
|
56
|
+
assert_transform(expected,block)
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_sunny
|
60
|
+
block = get_sexp do
|
61
|
+
[].each do |r|
|
62
|
+
bind :r => :foo
|
63
|
+
r.bar
|
64
|
+
r.baz
|
65
|
+
baz.rolemethod
|
66
|
+
self[boo]
|
67
|
+
end
|
68
|
+
self[0]
|
69
|
+
end
|
70
|
+
expected = (get_sexp do
|
71
|
+
[].each do |r|
|
72
|
+
temp____foo = @foo
|
73
|
+
@foo = r
|
74
|
+
self_foo_bar
|
75
|
+
r.baz
|
76
|
+
self_baz_rolemethod
|
77
|
+
role[boo]
|
78
|
+
@foo = temp____foo
|
79
|
+
end
|
80
|
+
role[0]
|
81
|
+
end)
|
82
|
+
|
83
|
+
interpretation_context = get_context
|
84
|
+
MethodDefinition.new(block,interpretation_context).transform
|
85
|
+
assert_source_equal(expected,block)
|
86
|
+
contracts = interpretation_context.contracts
|
87
|
+
assert_equal(2,contracts.length)
|
88
|
+
assert(contracts[:role].has_key? :[])
|
89
|
+
assert_equal(1,contracts[:role].length)
|
90
|
+
assert(contracts[:foo].has_key? :baz)
|
91
|
+
assert_nil(contracts[:baz])
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_nested_lambda
|
95
|
+
|
96
|
+
block = (get_sexp {lambda {
|
97
|
+
lambda {baz.rolemethod}}.call})
|
98
|
+
|
99
|
+
expected = (get_sexp {lambda {
|
100
|
+
lambda {self_baz_rolemethod}}.call})
|
101
|
+
|
102
|
+
assert_transform(expected,block)
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require_relative '../generated/MethodCall'
|
2
|
+
require_relative 'test_helper'
|
3
|
+
|
4
|
+
class Method_call_test < MiniTest::Unit::TestCase
|
5
|
+
def get_method_call &b
|
6
|
+
exp = get_sexp &b
|
7
|
+
exp[3]
|
8
|
+
end
|
9
|
+
include SourceAssertions
|
10
|
+
def test_adding_to_contracts_no_role
|
11
|
+
method_call = get_method_call {foo.bar}
|
12
|
+
|
13
|
+
contracts ={}
|
14
|
+
MethodCall.new(method_call, InterpretationContext.new({},contracts,nil,nil)).rewrite_call?
|
15
|
+
assert_nil(contracts[nil]) #wasn't a role
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_adding_to_contracts_with_role
|
19
|
+
method_call = get_method_call {foo.bar}
|
20
|
+
|
21
|
+
contracts ={}
|
22
|
+
roles = Hash.new
|
23
|
+
roles[:foo] = Hash.new
|
24
|
+
MethodCall.new(method_call, InterpretationContext.new(roles,contracts,nil,nil)).rewrite_call?
|
25
|
+
assert_equal(1,contracts.length)
|
26
|
+
assert_equal(1, contracts[:foo].length)
|
27
|
+
assert_equal(1, contracts[:foo][:bar])
|
28
|
+
end
|
29
|
+
def test_role_methods_not_added_to_contracts
|
30
|
+
method_call = get_method_call {foo.bar}
|
31
|
+
|
32
|
+
contracts ={}
|
33
|
+
roles = Hash.new
|
34
|
+
roles[:foo] = {:bar => nil}
|
35
|
+
MethodCall.new(method_call, InterpretationContext.new(roles,contracts,nil,nil)).rewrite_call?
|
36
|
+
assert_equal(0,contracts.length)
|
37
|
+
assert_source_equal(get_method_call {self_foo_bar},method_call)
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_contract_and_bind
|
41
|
+
block =get_sexp do [].each do |r|
|
42
|
+
temp____foo = @foo
|
43
|
+
@foo = r
|
44
|
+
r.bar
|
45
|
+
foo.baz
|
46
|
+
@foo = temp____foo
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
contracts ={}
|
51
|
+
roles = {:foo=> {:bar => nil},:role=>{}}
|
52
|
+
methodcall1 = block[3][3][3]
|
53
|
+
methodcall2 = block[3][3][4]
|
54
|
+
expected1 = get_method_call {self_foo_bar}
|
55
|
+
expected2 = get_method_call {foo.baz}
|
56
|
+
|
57
|
+
MethodCall.new(methodcall1, InterpretationContext.new(roles,contracts,{:r=>:foo},:role)).rewrite_call?
|
58
|
+
MethodCall.new(methodcall2, InterpretationContext.new(roles,contracts,{:r=>:foo},:role)).rewrite_call?
|
59
|
+
|
60
|
+
assert_source_equal(expected2,methodcall2)
|
61
|
+
assert_source_equal(expected1,methodcall1)
|
62
|
+
assert_equal(1,contracts.length)
|
63
|
+
assert_equal(1,contracts[:foo].length)
|
64
|
+
assert_equal(1,contracts[:foo][:baz])
|
65
|
+
assert_nil(contracts[:bar])
|
66
|
+
end
|
67
|
+
def test_index_contracts
|
68
|
+
methodcall = (get_sexp do
|
69
|
+
role[boo]
|
70
|
+
end)[3]
|
71
|
+
|
72
|
+
contracts = {}
|
73
|
+
roles = {:foo=>{:bar=>nil},:baz=>{:rolemethod=>nil},:role=>{}}
|
74
|
+
aliases = {}
|
75
|
+
interpretation_context = InterpretationContext.new(roles,contracts,aliases,:role)
|
76
|
+
mc = MethodCall.new(methodcall, interpretation_context)
|
77
|
+
mc.rewrite_call?
|
78
|
+
|
79
|
+
assert_equal(1,contracts.length)
|
80
|
+
assert_equal(1,interpretation_context.contracts[:role].length)
|
81
|
+
assert_equal(1,interpretation_context.contracts[:role][:[]])
|
82
|
+
end
|
83
|
+
end
|
data/Test/self_test.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require_relative '../generated/self'
|
2
|
+
require_relative 'test_helper'
|
3
|
+
|
4
|
+
require 'ruby2ruby'
|
5
|
+
|
6
|
+
class Self_test < MiniTest::Unit::TestCase
|
7
|
+
include SourceAssertions
|
8
|
+
def assert_self(abstract_syntax_tree, defining_role)
|
9
|
+
assert_equal(:call,abstract_syntax_tree[0])
|
10
|
+
assert_nil(abstract_syntax_tree[1])
|
11
|
+
assert_equal(defining_role, abstract_syntax_tree[2])
|
12
|
+
assert_instance_of(Sexp,abstract_syntax_tree[3])
|
13
|
+
assert_equal(abstract_syntax_tree[3][0], :arglist)
|
14
|
+
end
|
15
|
+
def interpretation_context
|
16
|
+
InterpretationContext.new({:role=>{}},nil,nil,:role)
|
17
|
+
end
|
18
|
+
def test_sunny
|
19
|
+
ast = (get_sexp { self.bar })[3]
|
20
|
+
|
21
|
+
Self.new(ast[1],interpretation_context).execute
|
22
|
+
|
23
|
+
expected = (get_sexp { role.bar })[3]
|
24
|
+
assert_source_equal(expected,ast)
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_indexer
|
28
|
+
ast = (get_sexp {self[0]})[3]
|
29
|
+
|
30
|
+
Self.new(ast,interpretation_context).execute
|
31
|
+
|
32
|
+
expected = (get_sexp { role[0] })[3]
|
33
|
+
assert_source_equal(expected,ast)
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_as_index
|
37
|
+
ast = (get_sexp {bar[self]})[3]
|
38
|
+
|
39
|
+
Self.new(ast[3],interpretation_context).execute
|
40
|
+
|
41
|
+
expected = (get_sexp { bar[role] })[3]
|
42
|
+
refute_nil(ast)
|
43
|
+
refute_equal(0,ast.length)
|
44
|
+
assert_source_equal(expected,ast)
|
45
|
+
end
|
46
|
+
end
|
data/Test/stack_test.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require_relative '../generated/ImmutableStack'
|
3
|
+
|
4
|
+
ImmutableStack.new nil,nil
|
5
|
+
|
6
|
+
class Stack_Test < Test::Unit::TestCase
|
7
|
+
|
8
|
+
def test_push_pop
|
9
|
+
stack = ImmutableStack.empty.push(1)
|
10
|
+
stack = stack.push 2
|
11
|
+
f,stack = stack.pop
|
12
|
+
s,stack = stack.pop
|
13
|
+
assert_equal(2,f)
|
14
|
+
assert_equal(1,s)
|
15
|
+
assert_equal(stack,ImmutableStack::empty)
|
16
|
+
end
|
17
|
+
end
|
data/Test/test_helper.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'sourcify'
|
3
|
+
require_relative 'assertions'
|
4
|
+
require_relative '../generated/interpretation_context'
|
5
|
+
#require 'debugger'
|
6
|
+
|
7
|
+
def get_sexp &b
|
8
|
+
begin
|
9
|
+
b.to_sexp
|
10
|
+
rescue
|
11
|
+
puts "failed to get expression"
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
context :ImmutableStack do
|
2
|
+
role :head do
|
3
|
+
end
|
4
|
+
role :tail do
|
5
|
+
end
|
6
|
+
pop do
|
7
|
+
[@head, @tail]
|
8
|
+
end
|
9
|
+
push do |element|
|
10
|
+
ImmutableStack.new element, self
|
11
|
+
end
|
12
|
+
|
13
|
+
empty self do
|
14
|
+
@@empty ||= self.new(nil, nil)
|
15
|
+
end
|
16
|
+
|
17
|
+
each do
|
18
|
+
yield head
|
19
|
+
t = tail
|
20
|
+
while t != ImmutableStack::empty do
|
21
|
+
h, t = t.pop
|
22
|
+
yield h
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
initialize do |h, t|
|
27
|
+
@head = h
|
28
|
+
@tail = t
|
29
|
+
self.freeze
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
@@ -0,0 +1,124 @@
|
|
1
|
+
context :MethodDefinition, :transform do
|
2
|
+
|
3
|
+
rebind do
|
4
|
+
@exp, @expressions = expressions.pop
|
5
|
+
@block, @potential_bind = nil
|
6
|
+
if @exp && (@exp.instance_of? Sexp) && @exp[0] == :iter
|
7
|
+
@exp[1..-1].each do |expr|
|
8
|
+
#find the block
|
9
|
+
if expr && expr.length && expr[0] == :block
|
10
|
+
@block, @potential_bind = expr, expr[1]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
@expressions = if @exp.instance_of? Sexp then
|
15
|
+
@expressions.push_array(exp)
|
16
|
+
else
|
17
|
+
@expressions
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
transform do
|
22
|
+
#could have been recursive but the stack depth isn't enough for even simple contexts
|
23
|
+
until expressions.empty?
|
24
|
+
|
25
|
+
block.transform
|
26
|
+
if exp && (exp.instance_of? Sexp)
|
27
|
+
is_indexer = exp[0] == :call && exp[1] == nil && (exp[2] == :[] || exp[2] == :[]=)
|
28
|
+
if (is_indexer || (exp[0] == :self)) && @interpretation_context.defining_role
|
29
|
+
Self.new(exp, interpretation_context).execute
|
30
|
+
end
|
31
|
+
if exp[0] == :call
|
32
|
+
MethodCall.new(exp, interpretation_context).rewrite_call?
|
33
|
+
end
|
34
|
+
end
|
35
|
+
rebind
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
initialize do |exp, interpretationcontext|
|
40
|
+
no_exp = 'No expression supplied'.to_sym
|
41
|
+
no_ctx = 'No interpretation context'.to_sym
|
42
|
+
|
43
|
+
raise no_exp unless exp
|
44
|
+
raise no_ctx unless interpretationcontext
|
45
|
+
|
46
|
+
@interpretation_context = interpretationcontext
|
47
|
+
@expressions = ImmutableQueue::empty.push exp
|
48
|
+
rebind
|
49
|
+
end
|
50
|
+
|
51
|
+
role :interpretation_context do
|
52
|
+
addalias do |key, value|
|
53
|
+
@interpretation_context.role_aliases[key] = value
|
54
|
+
end
|
55
|
+
end
|
56
|
+
role :exp do
|
57
|
+
end
|
58
|
+
role :expressions do
|
59
|
+
empty? do
|
60
|
+
expressions == ImmutableQueue::empty
|
61
|
+
end
|
62
|
+
end
|
63
|
+
role :potential_bind do
|
64
|
+
is_bind? do
|
65
|
+
potential_bind &&
|
66
|
+
potential_bind.length &&
|
67
|
+
(potential_bind[0] == :call &&
|
68
|
+
potential_bind[1] == nil &&
|
69
|
+
potential_bind[2] == :bind)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
role :block do
|
73
|
+
##
|
74
|
+
#Transforms blocks as needed
|
75
|
+
#-Rewrites self in role methods to the role getter
|
76
|
+
#-Rewrites binds when needed
|
77
|
+
#-Rewrites role method calls to instance method calls on the context
|
78
|
+
##
|
79
|
+
transform {
|
80
|
+
if block
|
81
|
+
if block.transform_bind?
|
82
|
+
@expressions.push_array(block[1..-1])
|
83
|
+
end
|
84
|
+
end
|
85
|
+
}
|
86
|
+
##
|
87
|
+
#Calls rewrite_block if needed and will return true if the AST was changed otherwise false
|
88
|
+
##
|
89
|
+
transform_bind? {
|
90
|
+
#check if the first call is a bind call
|
91
|
+
potential_bind.is_bind? && block.rewrite
|
92
|
+
}
|
93
|
+
|
94
|
+
rewrite {
|
95
|
+
changed = false
|
96
|
+
arguments = potential_bind[3]
|
97
|
+
|
98
|
+
if arguments && arguments[0] == :hash
|
99
|
+
block.delete_at 1
|
100
|
+
count = (arguments.length-1) / 2
|
101
|
+
(1..count).each do |j|
|
102
|
+
temp = j * 2
|
103
|
+
local = arguments[temp-1][1]
|
104
|
+
if local.instance_of? Sexp
|
105
|
+
local = local[1]
|
106
|
+
end
|
107
|
+
raise 'invalid value for role alias' unless local.instance_of? Symbol
|
108
|
+
#find the name of the role being bound to
|
109
|
+
aliased_role = arguments[temp][1]
|
110
|
+
if aliased_role.instance_of? Sexp
|
111
|
+
aliased_role = aliased_role[1]
|
112
|
+
end
|
113
|
+
raise aliased_role.to_s + 'used in binding is an unknown role ' + roles.to_s unless aliased_role.instance_of? Symbol and interpretation_context.roles.has_key? aliased_role
|
114
|
+
interpretation_context.addalias local, aliased_role
|
115
|
+
#replace bind call with assignment of iteration variable to role field
|
116
|
+
Bind.new(local, aliased_role, block).execute
|
117
|
+
changed = true
|
118
|
+
end
|
119
|
+
end
|
120
|
+
changed
|
121
|
+
}
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|