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
@@ -0,0 +1,58 @@
|
|
1
|
+
context :Bind, :execute do
|
2
|
+
role :block do
|
3
|
+
end
|
4
|
+
role :local do
|
5
|
+
end
|
6
|
+
role :aliased_role do
|
7
|
+
end
|
8
|
+
initialize do |local, aliased_role, block|
|
9
|
+
@local = local
|
10
|
+
@aliased_role = aliased_role
|
11
|
+
@block = block
|
12
|
+
end
|
13
|
+
##
|
14
|
+
#removes call to bind in a block
|
15
|
+
#and replaces it with assignment to the proper role player local variables
|
16
|
+
#in the end of the block the local variables have their original values reassigned
|
17
|
+
execute do
|
18
|
+
must_b_sym = 'aliased_role must be a Symbol'.to_sym
|
19
|
+
local_must_b_sym = 'local must be a Symbol'.to_sym
|
20
|
+
raise must_b_sym unless aliased_role.instance_of? Symbol
|
21
|
+
raise local_must_b_sym unless local.instance_of? Symbol
|
22
|
+
# assigning role player to role field
|
23
|
+
#notice that this will be executed after the next block
|
24
|
+
aliased_field = ('@' + aliased_role.to_s).to_sym
|
25
|
+
temp_symbol = ('temp____' + aliased_role.to_s).to_sym
|
26
|
+
|
27
|
+
assignment = Sexp.new
|
28
|
+
assignment[0] = :iasgn
|
29
|
+
assignment[1] = aliased_field
|
30
|
+
load_arg = Sexp.new
|
31
|
+
load_arg[0] = :lvar
|
32
|
+
load_arg[1] = local
|
33
|
+
assignment[2] = load_arg
|
34
|
+
block.insert 1, assignment
|
35
|
+
|
36
|
+
# assign role player to temp
|
37
|
+
# notice this is prepended Ie. inserted in front of the role player to role field
|
38
|
+
|
39
|
+
assignment = Sexp.new
|
40
|
+
assignment[0] = :lasgn
|
41
|
+
assignment[1] = temp_symbol
|
42
|
+
load_field = Sexp.new
|
43
|
+
load_field[0] = :ivar
|
44
|
+
load_field[1] = aliased_field
|
45
|
+
assignment[2] = load_field
|
46
|
+
block.insert 1, assignment
|
47
|
+
|
48
|
+
# reassign original player
|
49
|
+
assignment = Sexp.new
|
50
|
+
assignment[0] = :iasgn
|
51
|
+
assignment[1] = aliased_field
|
52
|
+
load_temp = Sexp.new
|
53
|
+
load_temp[0] = :lvar
|
54
|
+
load_temp[1] = temp_symbol
|
55
|
+
assignment[2] = load_temp
|
56
|
+
block[block.length] = assignment
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
context :ImmutableQueue do
|
2
|
+
role :front do
|
3
|
+
end
|
4
|
+
role :back do
|
5
|
+
end
|
6
|
+
push do |element|
|
7
|
+
b = back || ImmutableStack::empty
|
8
|
+
ImmutableQueue.new(front, b.push(element))
|
9
|
+
end
|
10
|
+
|
11
|
+
pop do
|
12
|
+
f, b = front, back
|
13
|
+
if f == ImmutableStack::empty
|
14
|
+
#reverse the back stack to be able to pop from the front in correct order
|
15
|
+
until b == ImmutableStack::empty
|
16
|
+
e, b = b.pop
|
17
|
+
f = f.push(e)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
head, f = f.pop
|
21
|
+
if f == b #can only happen if they are both ImmutableStack::empty
|
22
|
+
[head, ImmutableQueue::empty]
|
23
|
+
else
|
24
|
+
[head, ImmutableQueue.new(f, b)]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
empty true do
|
29
|
+
@@empty ||= ImmutableQueue.new(ImmutableStack::empty, ImmutableStack::empty)
|
30
|
+
end
|
31
|
+
|
32
|
+
push_array do |arr|
|
33
|
+
q = self
|
34
|
+
if arr
|
35
|
+
arr.each do |i|
|
36
|
+
q = q.push i
|
37
|
+
end
|
38
|
+
end
|
39
|
+
q
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
initialize do |front, back|
|
45
|
+
@front = front || ImmutableStack::empty
|
46
|
+
@back = back || ImmutableStack::empty
|
47
|
+
self.freeze
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
data/base/maroon_base.rb
ADDED
@@ -0,0 +1,267 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
##
|
4
|
+
# The Context class is used to define a DCI context with roles and their role methods
|
5
|
+
# to define a context call define with the name of the context (this name will become the name of the class that defines the context)
|
6
|
+
# the name should be a symbol and since it's going to be used as a class name, use class naming conventions
|
7
|
+
# follow the name with a block. With in this block you can define roles and interactions
|
8
|
+
# and interaction is defined by write the name of the interaction (hello in the below example) followed by a block
|
9
|
+
# the block will become the method body
|
10
|
+
# a role can be defined much like a context. instead of calling the define method call the role method followed by the role name (as a symbol)
|
11
|
+
# the role will be used for a private instance variable and the naming convention should match this
|
12
|
+
# With in the block supplied to the role method you can define role methods the same way as you define interactions. See the method who
|
13
|
+
# in the below example
|
14
|
+
# = Example
|
15
|
+
# Context::define :Greeter do
|
16
|
+
# role :who do
|
17
|
+
# say do
|
18
|
+
# @who #could be self as well to refer to the current role player of the 'who' role
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
# greeting do
|
22
|
+
# p "Hello #{who.say}!"
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# class Greeter
|
27
|
+
# def initialize(player)
|
28
|
+
# #@who = player
|
29
|
+
# end
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# Greeter.new('world').greeting #Will print "Hello world!"
|
33
|
+
#maroon is base on Marvin which was the first injectionless language for DCI
|
34
|
+
#being injectionless there's no runtime extend or anything else impacting the performance. There' only regular method invocation even when using role methods
|
35
|
+
#Author:: Rune Funch Søltoft (funchsoltoft@gmail.com)
|
36
|
+
#License:: Same as for Ruby
|
37
|
+
##
|
38
|
+
Context::generate_files_in 'generated'
|
39
|
+
context :Context do
|
40
|
+
role :roles do
|
41
|
+
end
|
42
|
+
role :interactions do
|
43
|
+
end
|
44
|
+
role :defining_role do
|
45
|
+
end
|
46
|
+
role :role_alias do
|
47
|
+
end
|
48
|
+
role :alias_list do
|
49
|
+
end
|
50
|
+
role :cached_roles_and_alias_list do
|
51
|
+
end
|
52
|
+
|
53
|
+
#define is the only exposed method and can be used to define a context (class)
|
54
|
+
#if maroon/kernel is required calling context of Context::define are equivalent
|
55
|
+
#params
|
56
|
+
#name:: the name of the context. Since this is used as the name of a class, class naming convetions should be used
|
57
|
+
#block:: the body of the context. Can include definitions of roles (through the role method) or definitions of interactions
|
58
|
+
#by simply calling a method with the name of the interaction and passing a block as the body of the interaction
|
59
|
+
define :block => :block, :self => self do |*args|
|
60
|
+
@@with_contracts ||= nil
|
61
|
+
@@generate_file_path ||= nil
|
62
|
+
alias method_missing role_or_interaction_method
|
63
|
+
base_class, ctx, default_interaction, name = self.send(:create_context_factory, args, block)
|
64
|
+
ctx.generate_files_in(args.last()) if args.last().instance_of? FalseClass or args.last().instance_of? TrueClass
|
65
|
+
return ctx.send(:finalize, name, base_class, default_interaction)
|
66
|
+
end
|
67
|
+
|
68
|
+
generate_files_in :block => :b,:self => self do |*args|
|
69
|
+
return role_or_interaction_method(:generate_files_in, *args, &b) if block_given?
|
70
|
+
|
71
|
+
@@generate_file_path = args[0]
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
with_contracts self do |*args|
|
76
|
+
return @@with_contracts if args.length == 0
|
77
|
+
value = args[0]
|
78
|
+
raise 'make up your mind! disabling contracts during execution will result in undefined behavior' if @@with_contracts && !value
|
79
|
+
@@with_contracts = value
|
80
|
+
end
|
81
|
+
|
82
|
+
##
|
83
|
+
#Defines a role with the given name
|
84
|
+
#role methods can be defined inside a block passed to this method
|
85
|
+
# = Example
|
86
|
+
# role :who do
|
87
|
+
# say do
|
88
|
+
# p @who
|
89
|
+
# end
|
90
|
+
# end
|
91
|
+
#The above code defines a role called 'who' with a role method called say
|
92
|
+
##
|
93
|
+
role :block => :b do |*args|
|
94
|
+
role_name = args[0]
|
95
|
+
return role_or_interaction_method(:role, *args, &b) if args.length != 1 or not (role_name.instance_of? Symbol)
|
96
|
+
|
97
|
+
@defining_role = role_name
|
98
|
+
@roles[role_name] = Hash.new
|
99
|
+
yield if block_given?
|
100
|
+
@defining_role = nil
|
101
|
+
end
|
102
|
+
|
103
|
+
initialize :block => :b do |*args|
|
104
|
+
if block_given?
|
105
|
+
role_or_interaction_method(:initialize, *args, &b)
|
106
|
+
else
|
107
|
+
@roles = Hash.new
|
108
|
+
@interactions = Hash.new
|
109
|
+
@role_alias = Hash.new
|
110
|
+
@contracts = Hash.new
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
role_or_interaction_method(:private) do
|
115
|
+
@private = true
|
116
|
+
end
|
117
|
+
|
118
|
+
current_interpretation_context :block => :b do |*args|
|
119
|
+
return role_or_interaction_method(:current_interpretation_context, *args, &b) if block_given?
|
120
|
+
InterpretationContext.new(roles, contracts, role_alias, nil)
|
121
|
+
end
|
122
|
+
|
123
|
+
get_methods :block => :b do |*args|
|
124
|
+
return role_or_interaction_method(:get_methods, *args, &b) if block_given?
|
125
|
+
name = args[0]
|
126
|
+
sources = (@defining_role ? (@roles[@defining_role]) : (@interactions))[name]
|
127
|
+
if @defining_role && !sources
|
128
|
+
@roles[@defining_role][name] = []
|
129
|
+
else
|
130
|
+
(@interactions)[name] = []
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
role :contracts do
|
135
|
+
end
|
136
|
+
|
137
|
+
add_method :block => :b do |*args|
|
138
|
+
return role_or_interaction_method(:add_method, *args, &b) if block_given?
|
139
|
+
name, method = *args
|
140
|
+
sources = get_methods(name)
|
141
|
+
sources << method
|
142
|
+
end
|
143
|
+
|
144
|
+
finalize :block => :b do |*args|
|
145
|
+
return role_or_interaction_method(:finalize, *args, &b) if block_given?
|
146
|
+
name, base_class, default = *args
|
147
|
+
|
148
|
+
code = generate_context_code(default, name)
|
149
|
+
|
150
|
+
if @@generate_file_path
|
151
|
+
name = name.to_s
|
152
|
+
complete = 'class ' + name + (base_class ? '<< ' + base_class.name : '') + '
|
153
|
+
' + code.to_s + '
|
154
|
+
end'
|
155
|
+
File.open('./' + @@generate_file_path.to_s + '/' + name + '.rb', 'w') { |f| f.write(complete) }
|
156
|
+
complete
|
157
|
+
else
|
158
|
+
|
159
|
+
c = base_class ? (Class.new base_class) : Class.new
|
160
|
+
if @@with_contracts
|
161
|
+
c.class_eval('def self.assert_that(obj)
|
162
|
+
ContextAsserter.new(self.contracts,obj)
|
163
|
+
end
|
164
|
+
def self.refute_that(obj)
|
165
|
+
ContextAsserter.new(self.contracts,obj,false)
|
166
|
+
end
|
167
|
+
def self.contracts
|
168
|
+
@@contracts
|
169
|
+
end
|
170
|
+
def self.contracts=(value)
|
171
|
+
raise \'Contracts must be supplied\' unless value
|
172
|
+
@@contracts = value
|
173
|
+
end')
|
174
|
+
c.contracts=contracts
|
175
|
+
end
|
176
|
+
Kernel.const_set name, c
|
177
|
+
temp = c.class_eval(code)
|
178
|
+
(temp || c)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
create_context_factory self do |args, block|
|
183
|
+
name, base_class, default_interaction = *args
|
184
|
+
#if there's two arguments and the second is not a class it must be an interaction
|
185
|
+
if default_interaction && (!base_class.instance_of? Class) then
|
186
|
+
base_class = eval(base_class.to_s)
|
187
|
+
end
|
188
|
+
base_class, default_interaction = default_interaction, base_class if base_class and !default_interaction and !base_class.instance_of? Class
|
189
|
+
ctx = Context.new
|
190
|
+
ctx.instance_eval &block
|
191
|
+
return base_class, ctx, default_interaction, name
|
192
|
+
end
|
193
|
+
|
194
|
+
generate_context_code :block => :b do |*args|
|
195
|
+
return role_or_interaction_method(:generate_context_code, *args, &b) if block_given?
|
196
|
+
default, name = args
|
197
|
+
|
198
|
+
getters = ''
|
199
|
+
impl = ''
|
200
|
+
interactions = ''
|
201
|
+
@interactions.each do |method_name, methods|
|
202
|
+
methods.each do |method|
|
203
|
+
@defining_role = nil
|
204
|
+
code = ' ' + (method.build_as_context_method method_name, current_interpretation_context)
|
205
|
+
if method.is_private
|
206
|
+
getters << code
|
207
|
+
else
|
208
|
+
interactions << code
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
if default
|
214
|
+
interactions << '
|
215
|
+
def self.call(*args)
|
216
|
+
arity = ' + name.to_s + '.method(:new).arity
|
217
|
+
newArgs = args[0..arity-1]
|
218
|
+
obj = ' + name.to_s + '.new *newArgs
|
219
|
+
if arity < args.length
|
220
|
+
methodArgs = args[arity..-1]
|
221
|
+
obj.' + default.to_s + ' *methodArgs
|
222
|
+
else
|
223
|
+
obj.' + default.to_s + '
|
224
|
+
end
|
225
|
+
end
|
226
|
+
'
|
227
|
+
interactions << '
|
228
|
+
def call(*args);' + default.to_s + ' *args; end
|
229
|
+
'
|
230
|
+
end
|
231
|
+
|
232
|
+
@roles.each do |role, methods|
|
233
|
+
getters << 'attr_reader :' + role.to_s + '
|
234
|
+
'
|
235
|
+
|
236
|
+
methods.each do |method_name, method_sources|
|
237
|
+
raise 'Duplicate definition of ' + method_name.to_s unless method_sources.length < 2
|
238
|
+
raise 'No source for ' + method_name.to_s unless method_sources.length > 0
|
239
|
+
|
240
|
+
method_source = method_sources[0]
|
241
|
+
@defining_role = role
|
242
|
+
rewritten_method_name = 'self_' + role.to_s + '_' + method_name.to_s
|
243
|
+
definition = method_source.build_as_context_method rewritten_method_name, current_interpretation_context
|
244
|
+
(impl << ' ' + definition.to_s) if definition
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
|
249
|
+
interactions + '
|
250
|
+
private
|
251
|
+
' + getters + '
|
252
|
+
' + impl + '
|
253
|
+
'
|
254
|
+
end
|
255
|
+
|
256
|
+
role_or_interaction_method({:block => :b}) do |*arguments|
|
257
|
+
method_name, on_self = *arguments
|
258
|
+
unless method_name.instance_of? Symbol
|
259
|
+
on_self = method_name
|
260
|
+
method_name = :role_or_interaction_method
|
261
|
+
end
|
262
|
+
|
263
|
+
raise 'Method with out block ' + method_name.to_s unless block_given?
|
264
|
+
|
265
|
+
add_method(method_name, MethodInfo.new(on_self, b.to_sexp, @private))
|
266
|
+
end
|
267
|
+
end
|
data/base/method_call.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require_relative '../lib/maroon/kernel'
|
2
|
+
context :MethodCall, :rewrite_call? do
|
3
|
+
role :interpretation_context do
|
4
|
+
contracts {
|
5
|
+
@interpretation_context.contracts || {}
|
6
|
+
}
|
7
|
+
roles {
|
8
|
+
@interpretation_context.roles || {}
|
9
|
+
}
|
10
|
+
role_aliases {
|
11
|
+
@interpretation_context.role_aliases || {}
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
role :method do
|
16
|
+
call_in_block? {
|
17
|
+
@in_block unless @in_block == nil
|
18
|
+
(@in_block = (method && method[0] == :lvar))
|
19
|
+
}
|
20
|
+
|
21
|
+
get_role_definition {
|
22
|
+
is_call_expression = method && method[0] == :call
|
23
|
+
self_is_instance_expression = (is_call_expression && !method[1])
|
24
|
+
role_name = nil
|
25
|
+
role_name = method[2] if self_is_instance_expression
|
26
|
+
if (not self_is_instance_expression) and method[1]
|
27
|
+
role_name = method[1][2] if method[1][1] == nil and method[1][0] == :call #call role field is instance
|
28
|
+
role_name = interpretation_context.role_aliases[method[1][1]] if method[1][0] == :lvar #local var potentially bound
|
29
|
+
end
|
30
|
+
role = role_name ? interpretation_context.roles[role_name] : nil
|
31
|
+
[role, (role ? role_name : nil)]
|
32
|
+
}
|
33
|
+
|
34
|
+
role_method_call? { |method_name|
|
35
|
+
|
36
|
+
return nil, nil unless method
|
37
|
+
|
38
|
+
role, role_name = method.get_role_definition #is it a call to a role getter
|
39
|
+
|
40
|
+
#in_block = method.call_in_block?
|
41
|
+
#role_name = interpretation_context.role_aliases[role_name] if in_block
|
42
|
+
is_role_method = role && role.has_key?(method_name)
|
43
|
+
|
44
|
+
return role_name, is_role_method
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
rewrite_call? do
|
49
|
+
method_name = method[2]
|
50
|
+
if method[0] == :call
|
51
|
+
if method[1] == nil && method.length < 5 && method[3] && method[3].length == 1 && method[3][0] == :arglist
|
52
|
+
#accessing a role field?
|
53
|
+
is_role = interpretation_context.roles.has_key? method[3]
|
54
|
+
method[3] = ':@' + method[3].to_sym if is_role
|
55
|
+
else
|
56
|
+
role_name, is_role_method = method.role_method_call? method_name
|
57
|
+
if is_role_method #role_name only returned if it's a role method call
|
58
|
+
method[1] = nil #remove call to attribute
|
59
|
+
method[2] = ('self_' + role_name.to_s + '_' + method_name.to_s).to_sym
|
60
|
+
else # it's an instance method invocation
|
61
|
+
if interpretation_context.roles.has_key? role_name
|
62
|
+
unless method.length == 3 && method[1] == nil && method[2] == role_name
|
63
|
+
contract_methods = (interpretation_context.contracts[role_name] ||= {})
|
64
|
+
contract_methods[method_name] ||= 0
|
65
|
+
contract_methods[method_name] += 1
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
initialize do |method, interpretation_context|
|
73
|
+
raise 'No method supplied' unless method
|
74
|
+
|
75
|
+
@method = method
|
76
|
+
@interpretation_context = interpretation_context
|
77
|
+
end
|
78
|
+
end
|