rachinations 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -16
- data/Gemfile.lock +2 -0
- data/lib/rachinations/domain/diagrams/diagram.rb +46 -14
- data/lib/rachinations/domain/edges/edge.rb +34 -20
- data/lib/rachinations/domain/modules/common/hash_init.rb +2 -1
- data/lib/rachinations/domain/modules/common/invariant.rb +2 -2
- data/lib/rachinations/domain/modules/common/refiners/number_modifiers.rb +22 -0
- data/lib/rachinations/domain/nodes/converter.rb +5 -5
- data/lib/rachinations/domain/nodes/gate.rb +77 -0
- data/lib/rachinations/domain/nodes/node.rb +69 -36
- data/lib/rachinations/domain/nodes/pool.rb +121 -74
- data/lib/rachinations/domain/nodes/resourceful_node.rb +0 -1
- data/lib/rachinations/domain/nodes/sink.rb +3 -0
- data/lib/rachinations/domain/nodes/source.rb +3 -2
- data/lib/rachinations/domain/resource_bag.rb +3 -4
- data/lib/rachinations/dsl/bad_dsl.rb +2 -0
- data/lib/rachinations/dsl/bootstrap.rb +59 -0
- data/lib/rachinations/dsl/diagram_shorthand_methods.rb +107 -0
- data/lib/rachinations/dsl/helpers/parser.rb +170 -0
- data/lib/rachinations/extras/constant_hash.rb +25 -0
- data/lib/rachinations/extras/fifo.rb +25 -19
- data/lib/rachinations/helpers/edge_helper.rb +40 -0
- data/lib/rachinations/utils/string_helper.rb +7 -0
- data/lib/rachinations/version.rb +1 -1
- data/lib/rachinations.rb +13 -5
- data/rachinations.gemspec +3 -2
- data/testing/simulations/modelo1.rb +1 -1
- data/testing/simulations/sequencial.rb +1 -1
- data/testing/simulations/sobonito.rb +1 -1
- data/testing/simulations/sobonitowhile.rb +1 -1
- data/testing/simulations/whatIwish1.rb +2 -2
- data/testing/spec/canon/conditions_spec.rb +3 -4
- data/testing/spec/converter_spec.rb +3 -4
- data/testing/spec/diagram_spec.rb +293 -238
- data/testing/spec/edge_spec.rb +28 -14
- data/testing/spec/gate_spec.rb +34 -0
- data/testing/spec/pool_spec.rb +8 -10
- data/testing/spec/release1/dsl_spec.rb +204 -0
- data/testing/spec/spec_helper.rb +1 -0
- data/testing/spec/xexeo_spec.rb +39 -40
- metadata +30 -8
- data/lib/rachinations/domain/edges/random_edge.rb +0 -4
- data/lib/rachinations/dsl/dsl.rb +0 -63
- data/testing/spec/canon/trigger_spec.rb +0 -128
@@ -4,12 +4,14 @@ require_relative '../../domain/resources/token'
|
|
4
4
|
require_relative '../resource_bag'
|
5
5
|
require_relative '../../domain/exceptions/no_elements_matching_condition_error'
|
6
6
|
require_relative '../../domain/modules/common/refiners/proc_convenience_methods'
|
7
|
-
|
7
|
+
require_relative '../../../../lib/rachinations/helpers/edge_helper'
|
8
8
|
|
9
9
|
using ProcConvenienceMethods
|
10
10
|
|
11
11
|
class Pool < ResourcefulNode
|
12
12
|
|
13
|
+
Helper = Helpers::EdgeHelper
|
14
|
+
|
13
15
|
def initialize(hsh={})
|
14
16
|
|
15
17
|
check_options!(hsh)
|
@@ -17,7 +19,8 @@ class Pool < ResourcefulNode
|
|
17
19
|
|
18
20
|
@resources = get_initial_resources(params[:initial_value])
|
19
21
|
|
20
|
-
@types = get_types(params[:initial_value],
|
22
|
+
@types = get_types(initial_value: params[:initial_value],
|
23
|
+
given_types: params[:types])
|
21
24
|
|
22
25
|
#reference to the underlying diagram
|
23
26
|
@diagram = params[:diagram]
|
@@ -47,16 +50,25 @@ class Pool < ResourcefulNode
|
|
47
50
|
|
48
51
|
pull_any!
|
49
52
|
|
50
|
-
|
53
|
+
elsif push? && all?
|
54
|
+
|
55
|
+
push_all!
|
56
|
+
|
57
|
+
elsif pull? && all?
|
58
|
+
|
59
|
+
pull_all!
|
51
60
|
|
61
|
+
else
|
62
|
+
|
63
|
+
end
|
52
64
|
end
|
53
65
|
end
|
54
66
|
|
55
|
-
def resource_count(type
|
67
|
+
def resource_count(type: nil, expr: nil)
|
56
68
|
|
57
|
-
raise ArgumentError.new('Please provide either a type or a block, but not both.') if
|
69
|
+
raise ArgumentError.new('Please provide either a type or a block, but not both.') if !expr.nil? && !type.nil?
|
58
70
|
|
59
|
-
if type.nil? &&
|
71
|
+
if type.nil? && expr.nil?
|
60
72
|
resources.count_where { |r| r.unlocked? }
|
61
73
|
elsif type.is_a?(Class) && type <= Token
|
62
74
|
|
@@ -67,12 +79,12 @@ class Pool < ResourcefulNode
|
|
67
79
|
else
|
68
80
|
raise UnsupportedTypeError.new "Unsupported type: #{type.name}"
|
69
81
|
end
|
70
|
-
elsif
|
82
|
+
elsif !expr.nil?
|
71
83
|
|
72
84
|
# client doesn't need to know about locked vs unlocked resources
|
73
|
-
unlock_condition =
|
85
|
+
unlock_condition = proc { |r| r.unlocked? }
|
74
86
|
|
75
|
-
resources.count_where { |r| unlock_condition.match?(r) &&
|
87
|
+
resources.count_where { |r| unlock_condition.match?(r) && expr.match?(r) }
|
76
88
|
|
77
89
|
else
|
78
90
|
raise ArgumentError.new("Wrong parameter types passed to #{__callee__}")
|
@@ -117,16 +129,16 @@ class Pool < ResourcefulNode
|
|
117
129
|
end
|
118
130
|
end
|
119
131
|
|
120
|
-
def take_resource!(
|
132
|
+
def take_resource!(expression=nil)
|
121
133
|
|
122
|
-
|
134
|
+
if expression.nil?
|
123
135
|
# if no conditions given, then anything goes.
|
124
|
-
expression = Proc.new{ |res| true }
|
136
|
+
expression = Proc.new { |res| true }
|
125
137
|
end
|
126
138
|
|
127
139
|
raise RuntimeError.new unless resources.count_where(&expression) > 0
|
128
140
|
|
129
|
-
res=remove_resource!
|
141
|
+
res=remove_resource! expression
|
130
142
|
|
131
143
|
fire_triggers!
|
132
144
|
|
@@ -135,7 +147,7 @@ class Pool < ResourcefulNode
|
|
135
147
|
end
|
136
148
|
|
137
149
|
def to_s
|
138
|
-
"Pool '#{@name}':
|
150
|
+
"Pool '#{@name}': #{resources} "
|
139
151
|
end
|
140
152
|
|
141
153
|
# TODO this smells. where is this used? can i do without it?
|
@@ -156,25 +168,6 @@ class Pool < ResourcefulNode
|
|
156
168
|
|
157
169
|
end
|
158
170
|
|
159
|
-
# TODO document this or else refactor it out
|
160
|
-
def get_types(initial_value, given_types)
|
161
|
-
inv { !self.instance_variable_defined?(:@types) }
|
162
|
-
|
163
|
-
if initial_value.is_a?(Fixnum) && given_types.empty?
|
164
|
-
# nothing to do
|
165
|
-
elsif initial_value == 0 && !given_types.empty?
|
166
|
-
# nothing to do
|
167
|
-
elsif initial_value.is_a?(Hash)
|
168
|
-
initial_value.each_key { |type| given_types.push(type) }
|
169
|
-
else
|
170
|
-
raise ArgumentError.new
|
171
|
-
end
|
172
|
-
|
173
|
-
given_types.uniq
|
174
|
-
|
175
|
-
end
|
176
|
-
|
177
|
-
|
178
171
|
def types
|
179
172
|
@types
|
180
173
|
end
|
@@ -183,16 +176,28 @@ class Pool < ResourcefulNode
|
|
183
176
|
|
184
177
|
attr_reader :resources
|
185
178
|
|
186
|
-
|
179
|
+
# Tries to remove a Resource that matches given expression from this node.
|
180
|
+
# @param [Proc] expression the expression to match against Resources
|
181
|
+
# @raise [RuntimeError] In case it was not possible to remove a Resource
|
182
|
+
def remove_resource!(expression)
|
187
183
|
|
188
184
|
# client doesn't need to know about locked vs unlocked resources
|
189
185
|
unlocked_expression = Proc.new { |r| r.unlocked? }
|
190
186
|
|
191
|
-
|
187
|
+
begin
|
188
|
+
# the original expression plus the expression that specifies that only unlocked
|
189
|
+
# resources may be retrieved
|
190
|
+
res=resources.get_where { |r| unlocked_expression.call(r) && expression.call(r) }
|
191
|
+
rescue ArgumentError
|
192
|
+
raise RuntimeError, 'wrong arguments'
|
193
|
+
rescue NoElementsMatchingConditionError
|
194
|
+
raise RuntimeError, 'no elements matching'
|
195
|
+
else
|
196
|
+
@resources_removed[res.class] += 1
|
197
|
+
res
|
198
|
+
end
|
192
199
|
|
193
|
-
@resources_removed[res.class] += 1
|
194
200
|
|
195
|
-
res
|
196
201
|
end
|
197
202
|
|
198
203
|
def add_resource!(res)
|
@@ -201,43 +206,22 @@ class Pool < ResourcefulNode
|
|
201
206
|
|
202
207
|
end
|
203
208
|
|
204
|
-
def options
|
205
|
-
[:conditions, :name, :activation, :mode, :types, :initial_value, :diagram]
|
206
|
-
end
|
207
|
-
|
208
|
-
def defaults
|
209
|
-
{
|
210
|
-
activation: :passive,
|
211
|
-
mode: :pull_any,
|
212
|
-
types: [],
|
213
|
-
initial_value: 0
|
214
|
-
}
|
215
|
-
end
|
216
|
-
|
217
|
-
def aliases
|
218
|
-
{:initial_values => :initial_value}
|
219
|
-
end
|
220
|
-
|
221
209
|
def push_any!
|
222
210
|
|
223
211
|
outgoing_edges.shuffle.each do |edge|
|
224
|
-
|
225
|
-
|
226
|
-
rescue => ex
|
227
|
-
# Could not get a block for one Edge, but this is push_any so I'll go ahead.
|
228
|
-
next #other edges might still be able to serve me.
|
229
|
-
end
|
212
|
+
|
213
|
+
blk = edge.push_expression
|
230
214
|
|
231
215
|
edge.label.times do
|
232
216
|
begin
|
233
|
-
res = remove_resource!(
|
234
|
-
rescue
|
235
|
-
|
217
|
+
res = remove_resource!(blk)
|
218
|
+
rescue RuntimeError
|
219
|
+
# Failed to remove this resource. Let's try another Edge, perhaps?
|
236
220
|
break
|
221
|
+
else
|
222
|
+
edge.push!(res)
|
237
223
|
end
|
238
224
|
|
239
|
-
edge.push!(res)
|
240
|
-
|
241
225
|
end
|
242
226
|
|
243
227
|
end
|
@@ -245,26 +229,89 @@ class Pool < ResourcefulNode
|
|
245
229
|
|
246
230
|
def pull_any!
|
247
231
|
incoming_edges.shuffle.each do |edge|
|
248
|
-
|
249
|
-
|
250
|
-
rescue RuntimeError => ex
|
251
|
-
# Could not get a block for one Edge, but this is pull_any so I'll go ahead.
|
252
|
-
next #other edges might still be able to serve me.
|
253
|
-
end
|
232
|
+
|
233
|
+
blk = edge.pull_expression
|
254
234
|
|
255
235
|
edge.label.times do
|
256
236
|
begin
|
257
|
-
res = edge.pull!(
|
258
|
-
rescue RuntimeError
|
237
|
+
res = edge.pull!(blk)
|
238
|
+
rescue RuntimeError
|
259
239
|
# Let's try another Edge, perhaps?
|
260
240
|
break
|
241
|
+
else
|
242
|
+
add_resource!(res.lock!)
|
261
243
|
end
|
262
244
|
|
263
|
-
|
245
|
+
end
|
246
|
+
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def pull_all!
|
251
|
+
|
252
|
+
enabled_incoming_edges = incoming_edges.select { |edge| edge.enabled? }
|
253
|
+
|
254
|
+
if enabled_incoming_edges.all? { |edge| edge.test_ping? }
|
255
|
+
|
256
|
+
enabled_incoming_edges.each do |edge|
|
257
|
+
blk = edge.pull_expression
|
258
|
+
|
259
|
+
edge.label.times do
|
260
|
+
res = edge.pull!(blk)
|
261
|
+
|
262
|
+
add_resource!(res.lock!)
|
263
|
+
|
264
|
+
end
|
264
265
|
|
265
266
|
end
|
267
|
+
fire_triggers!
|
268
|
+
end
|
269
|
+
|
270
|
+
end
|
271
|
+
|
272
|
+
def push_all!
|
273
|
+
|
274
|
+
enabled_outgoing_edges = outgoing_edges.select { |edge| edge.enabled? }
|
275
|
+
|
276
|
+
|
277
|
+
if Helper.all_can_push?(edges, require_all: true)
|
278
|
+
|
279
|
+
enabled_outgoing_edges.each do |edge|
|
280
|
+
|
281
|
+
expression = edge.push_expression
|
282
|
+
|
283
|
+
edge.label.times do
|
284
|
+
|
285
|
+
res = remove_resource!(expression)
|
286
|
+
|
287
|
+
edge.push!(res)
|
288
|
+
|
289
|
+
end
|
266
290
|
|
291
|
+
end
|
292
|
+
fire_triggers!
|
267
293
|
end
|
294
|
+
|
295
|
+
|
296
|
+
end
|
297
|
+
|
298
|
+
def options
|
299
|
+
[:conditions, :name, :activation, :mode, :types, :initial_value, :diagram]
|
300
|
+
end
|
301
|
+
|
302
|
+
def defaults
|
303
|
+
{
|
304
|
+
activation: :passive,
|
305
|
+
mode: :pull_any,
|
306
|
+
types: [],
|
307
|
+
initial_value: 0
|
308
|
+
}
|
309
|
+
end
|
310
|
+
|
311
|
+
def aliases
|
312
|
+
{
|
313
|
+
:initial_values => :initial_value
|
314
|
+
}
|
268
315
|
end
|
269
316
|
|
270
317
|
end
|
@@ -28,7 +28,7 @@ class Source < ResourcefulNode
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def to_s
|
31
|
-
"Source '#{@name}'
|
31
|
+
"Source '#{@name}'\n\n"
|
32
32
|
end
|
33
33
|
|
34
34
|
def put_resource!;
|
@@ -98,7 +98,7 @@ class Source < ResourcefulNode
|
|
98
98
|
|
99
99
|
#we'll need to change this if source starts accepting
|
100
100
|
# more than a single type
|
101
|
-
inv { types.size === 1 }
|
101
|
+
inv("Sources can only have a single type") { types.size === 1 }
|
102
102
|
|
103
103
|
if type.nil?
|
104
104
|
res = Token.new
|
@@ -144,6 +144,7 @@ class Source < ResourcefulNode
|
|
144
144
|
end
|
145
145
|
|
146
146
|
end
|
147
|
+
fire_triggers!
|
147
148
|
end
|
148
149
|
|
149
150
|
def options
|
@@ -82,16 +82,15 @@ class ResourceBag
|
|
82
82
|
unlocked = count_where{|r| (r.is_type? klass) && (r.unlocked?) }
|
83
83
|
locked = count_where{|r| (r.is_type? klass) && (r.locked?) }
|
84
84
|
|
85
|
-
out += "
|
85
|
+
out += "#{name} -> #{unlocked} (#{locked}) \n"
|
86
86
|
end
|
87
87
|
|
88
88
|
if classes.empty?
|
89
|
-
"
|
89
|
+
"Empty\n\n"
|
90
90
|
else
|
91
|
-
out
|
91
|
+
out+"\n"
|
92
92
|
end
|
93
93
|
|
94
|
-
|
95
94
|
end
|
96
95
|
|
97
96
|
# created so that I don't have to call count everytime just to see whether there's at least one element matching said condition
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require_relative '../../../lib/rachinations/domain/diagrams/diagram'
|
2
|
+
require_relative 'diagram_shorthand_methods'
|
3
|
+
require_relative 'bad_dsl'
|
4
|
+
require_relative 'helpers/parser'
|
5
|
+
|
6
|
+
# Bootstraping a diagram using the DiagramShorthandMethods
|
7
|
+
module DSL
|
8
|
+
module Bootstrap
|
9
|
+
|
10
|
+
def diagram(name='new_diagram', mode: :silent, &blk)
|
11
|
+
|
12
|
+
# cant verbose be a simple boolean instead?
|
13
|
+
if mode == :silent || mode == 'silent'
|
14
|
+
dia= Diagram.new(Parser.validate_name!(name))
|
15
|
+
elsif mode == :verbose || mode == 'verbose'
|
16
|
+
dia = VerboseDiagram.new(Parser.validate_name!(name))
|
17
|
+
else
|
18
|
+
raise BadDSL, "Unknown diagram mode: #{mode.to_s}"
|
19
|
+
end
|
20
|
+
|
21
|
+
# methods in the block get run as if they were called on
|
22
|
+
# the diagram (augmented by the DiagramShorthandMethods module) itself
|
23
|
+
dia.instance_eval(&blk)
|
24
|
+
|
25
|
+
dia
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
def non_deterministic_diagram(name, verbose=:silent, &blk)
|
31
|
+
|
32
|
+
dia=NonDeterministicDiagram.new(Parser.validate_name!(name))
|
33
|
+
|
34
|
+
# cant verbose be a simple boolean instead?
|
35
|
+
if verbose === :verbose
|
36
|
+
dia.extend(Verbose)
|
37
|
+
end
|
38
|
+
|
39
|
+
dia.instance_eval &blk
|
40
|
+
|
41
|
+
dia
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
# This is just a convenience method to create a proc in a way that's more
|
46
|
+
# intuitive for ends users.
|
47
|
+
# @example Create a proc, equivalent to proc{|x| x+1 }
|
48
|
+
# expr{|x| x+1 }
|
49
|
+
def expr(&blk)
|
50
|
+
if !block_given?
|
51
|
+
raise ArgumentError, "expected a block, but none was given"
|
52
|
+
else
|
53
|
+
Proc.new(&blk)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require_relative '../domain/diagrams/diagram'
|
2
|
+
require_relative '../domain/diagrams/verbose_diagram'
|
3
|
+
require_relative '../domain/diagrams/non_deterministic_diagram'
|
4
|
+
require_relative '../domain/modules/diagrams/verbose'
|
5
|
+
require_relative '../domain/modules/common/refiners/proc_convenience_methods'
|
6
|
+
require_relative './bad_dsl'
|
7
|
+
require_relative '../utils/string_helper'
|
8
|
+
require_relative 'helpers/parser'
|
9
|
+
|
10
|
+
module DSL
|
11
|
+
|
12
|
+
module DiagramShorthandMethods
|
13
|
+
|
14
|
+
using ProcConvenienceMethods
|
15
|
+
|
16
|
+
# if you can turn this into a refiner and still keep all tests passing,
|
17
|
+
# send a PR for me :smile:
|
18
|
+
class ::Diagram
|
19
|
+
|
20
|
+
alias_method :run, :run!
|
21
|
+
|
22
|
+
def pool(*args)
|
23
|
+
|
24
|
+
hash = Parser.parse_arguments(args)
|
25
|
+
|
26
|
+
add_node! Pool, hash
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
def source(*args)
|
31
|
+
|
32
|
+
hash = Parser.parse_arguments(args)
|
33
|
+
|
34
|
+
add_node! Source, hash
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
def sink(*args)
|
39
|
+
|
40
|
+
hash = Parser.parse_arguments(args)
|
41
|
+
|
42
|
+
add_node! Sink, hash
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
def converter(*args)
|
47
|
+
|
48
|
+
hash = Parser.parse_arguments(args)
|
49
|
+
|
50
|
+
add_node! Converter, hash
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
def trader(*args)
|
55
|
+
|
56
|
+
hash = Parser.parse_arguments(args)
|
57
|
+
|
58
|
+
add_node! Trader, hash
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
def gate(*args)
|
63
|
+
# gate is different because it doesn't take some arguments
|
64
|
+
hash = Parser.parse_gate_arguments(args)
|
65
|
+
|
66
|
+
add_node! Gate, hash
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
# methods to create edges
|
71
|
+
def edge(*args)
|
72
|
+
|
73
|
+
hash = Parser.parse_edge_arguments(args)
|
74
|
+
|
75
|
+
add_edge! Edge, hash
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
# so that I can easily access elements which have been given a name
|
81
|
+
# (mostly nodes and maybe edges too)
|
82
|
+
def method_missing(method_sym, *args, &block)
|
83
|
+
super if method_sym.to_s == 'anonymous' #these aren't actual node names
|
84
|
+
|
85
|
+
begin
|
86
|
+
#does a node with that name exist?
|
87
|
+
node=get_node(method_sym.to_s)
|
88
|
+
rescue RuntimeError => err_node
|
89
|
+
#what about an edge?
|
90
|
+
begin
|
91
|
+
edge = get_edge(method_sym.to_s)
|
92
|
+
rescue RuntimeError => err_edge
|
93
|
+
#no luck, sorry
|
94
|
+
super
|
95
|
+
else
|
96
|
+
edge
|
97
|
+
end
|
98
|
+
else
|
99
|
+
node
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|