maroon 0.6.1 → 0.6.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|