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.
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