maroon 0.6.1 → 0.6.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/Examples/Dijkstra/CalculateShortestDistance.rb +16 -12
  3. data/Examples/Dijkstra/calculate_shortest_path.rb +47 -27
  4. data/Examples/Dijkstra/data.rb +41 -14
  5. data/Examples/Dijkstra/dijkstra.rb +4 -3
  6. data/Examples/MoneyTransfer.rb +61 -60
  7. data/Examples/greeter.rb +8 -7
  8. data/Examples/meter.rb +35 -29
  9. data/Gemfile +9 -4
  10. data/LICENSE.txt +21 -21
  11. data/README.md +13 -0
  12. data/Rakefile +41 -1
  13. data/Test/Generate/method_info_test.rb +12 -0
  14. data/Test/{Greeter_test.rb → Greeter_test_disabled.rb} +24 -20
  15. data/Test/ImmutableQueue_test.rb +18 -0
  16. data/Test/MethodInfo_test.rb +65 -0
  17. data/Test/alltests.rb +1 -0
  18. data/Test/{source_assertions.rb → assertions.rb} +15 -7
  19. data/Test/bind_test.rb +13 -0
  20. data/Test/expression_test.rb +105 -0
  21. data/Test/method_call_test.rb +83 -0
  22. data/Test/self_test.rb +46 -0
  23. data/Test/stack_test.rb +17 -0
  24. data/Test/test_helper.rb +14 -0
  25. data/base/ImmutableStack.rb +32 -0
  26. data/base/MethodDefinition.rb +124 -0
  27. data/base/bind_rewriter.rb +58 -0
  28. data/base/immutable_queue.rb +50 -0
  29. data/base/maroon_base.rb +267 -0
  30. data/base/method_call.rb +78 -0
  31. data/base/method_info.rb +86 -0
  32. data/base/self.rb +60 -0
  33. data/generated/build.rb +13 -0
  34. data/generated/interpretation_context.rb +29 -0
  35. data/lib/Bind.rb +65 -0
  36. data/lib/Context.rb +187 -0
  37. data/lib/ImmutableQueue.rb +50 -0
  38. data/lib/ImmutableStack.rb +38 -0
  39. data/lib/MethodCall.rb +91 -0
  40. data/lib/MethodDefinition.rb +114 -0
  41. data/lib/MethodInfo.rb +78 -0
  42. data/lib/Self.rb +71 -0
  43. data/lib/build.rb +14 -0
  44. data/lib/interpretation_context.rb +30 -0
  45. data/lib/maroon/contracts.rb +43 -0
  46. data/lib/maroon/kernel.rb +2 -0
  47. data/lib/maroon/version.rb +3 -3
  48. data/maroon.gemspec +26 -26
  49. metadata +49 -31
  50. data/lib/Source_cleaner.rb +0 -34
  51. data/lib/maroon.rb +0 -165
  52. 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
@@ -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
@@ -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