xpflow 0.1b

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. data/bin/xpflow +96 -0
  2. data/lib/colorado.rb +198 -0
  3. data/lib/json/add/core.rb +243 -0
  4. data/lib/json/add/rails.rb +8 -0
  5. data/lib/json/common.rb +423 -0
  6. data/lib/json/editor.rb +1369 -0
  7. data/lib/json/ext.rb +28 -0
  8. data/lib/json/pure/generator.rb +442 -0
  9. data/lib/json/pure/parser.rb +320 -0
  10. data/lib/json/pure.rb +15 -0
  11. data/lib/json/version.rb +8 -0
  12. data/lib/json.rb +62 -0
  13. data/lib/mime/types.rb +881 -0
  14. data/lib/mime-types.rb +3 -0
  15. data/lib/restclient/abstract_response.rb +106 -0
  16. data/lib/restclient/exceptions.rb +193 -0
  17. data/lib/restclient/net_http_ext.rb +55 -0
  18. data/lib/restclient/payload.rb +235 -0
  19. data/lib/restclient/raw_response.rb +34 -0
  20. data/lib/restclient/request.rb +316 -0
  21. data/lib/restclient/resource.rb +169 -0
  22. data/lib/restclient/response.rb +24 -0
  23. data/lib/restclient.rb +174 -0
  24. data/lib/xpflow/bash.rb +341 -0
  25. data/lib/xpflow/bundle.rb +113 -0
  26. data/lib/xpflow/cmdline.rb +249 -0
  27. data/lib/xpflow/collection.rb +122 -0
  28. data/lib/xpflow/concurrency.rb +79 -0
  29. data/lib/xpflow/data.rb +393 -0
  30. data/lib/xpflow/dsl.rb +816 -0
  31. data/lib/xpflow/engine.rb +574 -0
  32. data/lib/xpflow/ensemble.rb +135 -0
  33. data/lib/xpflow/events.rb +56 -0
  34. data/lib/xpflow/experiment.rb +65 -0
  35. data/lib/xpflow/exts/facter.rb +30 -0
  36. data/lib/xpflow/exts/g5k.rb +931 -0
  37. data/lib/xpflow/exts/g5k_use.rb +50 -0
  38. data/lib/xpflow/exts/gui.rb +140 -0
  39. data/lib/xpflow/exts/model.rb +155 -0
  40. data/lib/xpflow/graph.rb +1603 -0
  41. data/lib/xpflow/graph_xpflow.rb +251 -0
  42. data/lib/xpflow/import.rb +196 -0
  43. data/lib/xpflow/library.rb +349 -0
  44. data/lib/xpflow/logging.rb +153 -0
  45. data/lib/xpflow/manager.rb +147 -0
  46. data/lib/xpflow/nodes.rb +1250 -0
  47. data/lib/xpflow/runs.rb +773 -0
  48. data/lib/xpflow/runtime.rb +125 -0
  49. data/lib/xpflow/scope.rb +168 -0
  50. data/lib/xpflow/ssh.rb +186 -0
  51. data/lib/xpflow/stat.rb +50 -0
  52. data/lib/xpflow/stdlib.rb +381 -0
  53. data/lib/xpflow/structs.rb +369 -0
  54. data/lib/xpflow/taktuk.rb +193 -0
  55. data/lib/xpflow/templates/ssh-config.basic +14 -0
  56. data/lib/xpflow/templates/ssh-config.inria +18 -0
  57. data/lib/xpflow/templates/ssh-config.proxy +13 -0
  58. data/lib/xpflow/templates/taktuk +6590 -0
  59. data/lib/xpflow/templates/utils/batch +4 -0
  60. data/lib/xpflow/templates/utils/bootstrap +12 -0
  61. data/lib/xpflow/templates/utils/hostname +3 -0
  62. data/lib/xpflow/templates/utils/ping +3 -0
  63. data/lib/xpflow/templates/utils/rsync +12 -0
  64. data/lib/xpflow/templates/utils/scp +17 -0
  65. data/lib/xpflow/templates/utils/scp_many +8 -0
  66. data/lib/xpflow/templates/utils/ssh +3 -0
  67. data/lib/xpflow/templates/utils/ssh-interactive +4 -0
  68. data/lib/xpflow/templates/utils/taktuk +19 -0
  69. data/lib/xpflow/threads.rb +187 -0
  70. data/lib/xpflow/utils.rb +569 -0
  71. data/lib/xpflow/visual.rb +230 -0
  72. data/lib/xpflow/with_g5k.rb +7 -0
  73. data/lib/xpflow.rb +349 -0
  74. metadata +135 -0
@@ -0,0 +1,773 @@
1
+ # encoding: UTF-8
2
+
3
+ #
4
+ # Implementation of 'runs' or actions that can be executed
5
+ # within the experiment engine. They build logic behind DSL.
6
+ #
7
+
8
+ require 'timeout'
9
+
10
+ module XPFlow
11
+
12
+ class AbstractRun
13
+
14
+ include Traverse
15
+ include Meta
16
+
17
+ attr_accessor :key
18
+ constructor :key
19
+
20
+ def engine
21
+ # shortcut to get an engine
22
+ return Scope.engine
23
+ end
24
+
25
+ def run()
26
+ exc = nil
27
+ begin
28
+ x = execute()
29
+ Scope.current[@key] = x
30
+ rescue => e
31
+ raise if (e.is_a?(RunError)) and (e.run == self)
32
+ exc = RunError.new(self, e)
33
+ end
34
+ raise exc unless exc.nil?
35
+ return x
36
+ end
37
+
38
+ def run_threads(list, opts = {}, &block)
39
+ # TODO: pool should be in the scope...
40
+ list = listize(list)
41
+ pool_size = engine.getset.get(:pool)
42
+ nonnil = opts.select { |k, v| !v.nil? }
43
+ opts = { :pool => pool_size }.merge(nonnil)
44
+ return Threads.run(self, list, opts, &block)
45
+ end
46
+
47
+ def restart()
48
+ # returns non-nil value (in fact, a hash) when can restart from this activity
49
+ # the returned hash contains hash to copy to the scope
50
+ return false
51
+ end
52
+
53
+ def listize(o)
54
+ # tries to execute :to_list, if not
55
+ # checks if is an array, otherwise panics
56
+ if o.respond_to?(:to_list)
57
+ o = o.to_list
58
+ end
59
+ if !o.is_a?(Array)
60
+ raise "#{o} is not an array"
61
+ end
62
+ return o
63
+ end
64
+
65
+ end
66
+
67
+
68
+ class SequenceRun < AbstractRun
69
+
70
+ attr_reader :body
71
+
72
+ constructor [ :key ], :body
73
+ children :body
74
+
75
+ def check_restartability()
76
+ name = engine.config(:checkpoint)
77
+ if engine.config(:ignore_checkpoints)
78
+ engine.paranoic("Ignoring checkpoints.") \
79
+ if @body.any? { |r| r.is_a?(CheckpointRun) }
80
+ return @body
81
+ end
82
+ list = []
83
+ # TODO: I should collect all checkpoints and verify
84
+ for r in @body.reverse do
85
+ if name.nil? == false and r.is_a?(CheckpointRun) and r.name.to_s != name
86
+ list = [ r ] + list
87
+ next
88
+ end
89
+ cp = r.restart()
90
+ if cp == true
91
+ # we restarted
92
+ engine.log("Checkpoint '#{r.name}' restarted.")
93
+ break
94
+ else
95
+ list = [ r ] + list # standard case
96
+ end
97
+ end
98
+ return list
99
+ end
100
+
101
+ def execute()
102
+ tail = check_restartability()
103
+ results = []
104
+ for r in tail do
105
+ x = r.run()
106
+ results.push(x)
107
+ end
108
+ return results.last
109
+ end
110
+
111
+ def attach_to_checkpoints(obj)
112
+ @body.each do |i|
113
+ if i.is_a?(CheckpointRun)
114
+ i.parent = obj
115
+ end
116
+ end
117
+ end
118
+
119
+ def split(node)
120
+ before = []
121
+ after = []
122
+ first = true
123
+ @body.each do |el|
124
+ first = false if el == node
125
+ (first ? before : after).push(el) if el != node
126
+ end
127
+ return [before, after].map { |x| ActivityList.new(x) }
128
+ end
129
+
130
+ end
131
+
132
+ class SeqtryRun < AbstractRun
133
+
134
+ attr_reader :body
135
+ constructor [ :key ], :body
136
+
137
+ children :body
138
+
139
+ def execute()
140
+ result = nil
141
+ return nil if @body.length == 0
142
+ for r in @body do
143
+ fine = true
144
+ begin
145
+ result = r.run()
146
+ rescue RunError => e
147
+ engine.verbose("Error caused by #{e.summary}. Trying the next activity.")
148
+ fine = false
149
+ end
150
+ return result if fine == true
151
+ end
152
+ raise "Seqtry execution failed."
153
+ end
154
+
155
+ end
156
+
157
+ class ExperimentRun < AbstractRun
158
+
159
+ constructor [ :key ], :name, :args
160
+ children :name, :args
161
+
162
+ def execute()
163
+ # TODO: more things here
164
+ r = nil
165
+ name = @name.evaluate(Scope.current)
166
+ args = @args.evaluate(Scope.current)
167
+ full_name = "#{name}.__standard__"
168
+ ActivityRun.run_activity_block(full_name) do |activity|
169
+ Scope.region do |scope|
170
+ # scope[:__collection__] = Collection.new
171
+ text = "Running experiment #{name}"
172
+ r = engine.activity_period(text, { :gantt => true }) do
173
+ activity.execute(args, &@block)
174
+ end
175
+ end
176
+ end
177
+ return r
178
+ end
179
+
180
+ def get_name()
181
+ # tries to evaluate the experiment name
182
+ # without running (if it is possible)
183
+ # returns nil otherwise
184
+ return @name.evaluate_offline()
185
+ end
186
+
187
+ end
188
+
189
+ class ActivityRun < AbstractRun
190
+
191
+ constructor [ :key ], :name, :args, :opts, :block
192
+ children :args
193
+
194
+ attr_reader :opts
195
+ attr_reader :name
196
+
197
+ def self.run_activity_block(full_name)
198
+ # runs activity using the current scope
199
+ full_name = full_name.to_s
200
+ lib = Scope.current[:__library__]
201
+ ns = Scope.current[:__namespace__]
202
+ if full_name.start_with?("/")
203
+ full_name = full_name[1..-1]
204
+ lib = Scope.engine
205
+ end
206
+ libs, name = lib.into_parts(full_name)
207
+ library = lib.resolve_libs(libs)
208
+ result = Scope.region do |scope|
209
+ scope[:__library__] = library
210
+ scope[:__namespace__] = namespace = ns + libs
211
+ activity = library.get_activity_object(name)
212
+ raise "No such activity '#{namespace.join(".")}.#{name}'" if activity.nil?
213
+ yield(activity)
214
+ end
215
+ return result
216
+ end
217
+
218
+ def execute()
219
+ r = nil
220
+ this_name = @name.evaluate(Scope.current)
221
+ preargs = []
222
+ if this_name.is_a?(RunLater)
223
+ preargs = this_name.args
224
+ this_name = this_name.name
225
+ end
226
+ ActivityRun.run_activity_block(this_name) do |activity|
227
+
228
+ activity_id = engine.activity_id(this_name)
229
+ args = preargs + @args.evaluate(Scope.current)
230
+
231
+ opts = { :gantt => true }.merge(activity.opts).merge(@opts)
232
+
233
+ text = "#{this_name}:#{activity_id}"
234
+
235
+ log_level = :verbose
236
+ if this_name.to_s.start_with?("__")
237
+ log_level = :paranoic
238
+ end
239
+ if activity.doc.nil? == false
240
+ text = "[#{activity.doc}] (#{text})"
241
+ log_level = :normal
242
+ end
243
+
244
+ if !opts[:log_level].nil?
245
+ log_level = opts[:log_level]
246
+ end
247
+
248
+ period_opts = opts.merge({ :log_level => log_level })
249
+ r = engine.activity_period(text, period_opts) do
250
+ activity.execute(args, &@block)
251
+ end
252
+ end
253
+ return r
254
+ end
255
+
256
+ def get_name
257
+ @name
258
+ end
259
+
260
+ def report(started, args)
261
+ age = Time.now - started
262
+ return {
263
+ :title => "Activity #{get_name.to_s}",
264
+ :args => args.inspect,
265
+ :started => started,
266
+ :age => "#{age} s"
267
+ }
268
+ end
269
+
270
+ def to_s
271
+ "<Activity #{get_name}>"
272
+ end
273
+
274
+ def builtin?
275
+ # TODO: do it properly
276
+ name = get_name().to_s
277
+ return name.start_with?('__') && !name.start_with?('__nodes__')
278
+ end
279
+
280
+ end
281
+
282
+ class ForEachRun < AbstractRun
283
+
284
+ attr_reader :body
285
+
286
+ constructor [ :key ], :list, :iter, :opts, :body
287
+ children :list, :body
288
+ declares :iter
289
+
290
+ def execute()
291
+ list = @list.evaluate(Scope.current)
292
+ opts = @opts.evaluate(Scope.current)
293
+ ignore_errors = opts[:ignore_errors]
294
+ result = []
295
+ for item in listize(list) do
296
+ Scope.region do |scope|
297
+ scope[@iter] = item
298
+ x = Marker.new
299
+ begin
300
+ x = @body.run()
301
+ rescue RunError
302
+ raise if !ignore_errors
303
+ end
304
+ result.push(x) if !x.is_a?(Marker)
305
+ end
306
+ end
307
+ return result
308
+ end
309
+
310
+ end
311
+
312
+ class Marker
313
+ end
314
+
315
+ class ForAllRun < AbstractRun
316
+
317
+ attr_reader :body
318
+
319
+ constructor [ :key ], :list, :iter, :opts, :body
320
+ children :list, :body
321
+ declares :iter
322
+
323
+ def execute()
324
+ list = @list.evaluate(Scope.current)
325
+ opts = @opts.evaluate(Scope.current)
326
+ size = opts[:pool]
327
+ ignore_errors = opts[:ignore_errors]
328
+ result = OrderedArray.new
329
+ scope = Scope.current
330
+ run_threads(list, :pool => size) do |el, i|
331
+ Scope.set(scope.push, { @iter => el })
332
+ x = Marker.new
333
+ begin
334
+ x = @body.run()
335
+ rescue RunError
336
+ raise if !ignore_errors
337
+ end
338
+ result.give(i, x)
339
+ end
340
+ tabl = result.take(list.length)
341
+ tabl = tabl.select { |x| !x.is_a?(Marker) }
342
+ return tabl
343
+ end
344
+
345
+ end
346
+
347
+ class ForManyRun < AbstractRun
348
+
349
+ attr_reader :body
350
+
351
+ constructor [ :key ], :number, :list, :iter, :body
352
+ children :number, :list, :body
353
+ declares :iter
354
+
355
+ def execute()
356
+ rendez = Meeting.new(self)
357
+ n = @number.evaluate(Scope.current)
358
+ list = @list.evaluate(Scope.current)
359
+ scope = Scope.current
360
+ run_threads(list, :join => false) do |it, _|
361
+ Scope.set(scope.push, { @iter => it })
362
+ x = @body.run()
363
+ rendez.give(x)
364
+ end
365
+ # TODO: what about joining?
366
+ return rendez.take(n)
367
+ end
368
+
369
+ end
370
+
371
+ class ManyRun < AbstractRun
372
+
373
+ attr_reader :body
374
+
375
+ constructor [ :key ], :number, :body
376
+ children :number, :body
377
+
378
+ def execute()
379
+ n = @number.evaluate(Scope.current)
380
+ rendez = Meeting.new(self)
381
+ scope = Scope.current
382
+ run_threads(@body, :join => false) do |r, _|
383
+ Scope.set(scope)
384
+ x = r.run()
385
+ rendez.give(x)
386
+ end
387
+ return rendez.take(n)
388
+ end
389
+
390
+ end
391
+
392
+ class ParallelRun < AbstractRun
393
+
394
+ attr_reader :body
395
+
396
+ constructor [ :key ], :body
397
+ children :body
398
+
399
+ def execute()
400
+ arr = OrderedArray.new
401
+ scope = Scope.current
402
+ rs = run_threads(@body) do |r, i|
403
+ Scope.set(scope)
404
+ x = r.run()
405
+ arr.give(i, x)
406
+ end
407
+ values = arr.take(@body.length)
408
+ return values.last
409
+ end
410
+ end
411
+
412
+ class IfRun < AbstractRun
413
+
414
+ attr_reader :on_true
415
+ attr_reader :on_false
416
+
417
+ constructor [ :key ], :condition, :on_true, :on_false
418
+ children :condition, :on_true, :on_false
419
+
420
+ def execute()
421
+ x = @condition.evaluate(Scope.current)
422
+ if x
423
+ return @on_true.run()
424
+ else
425
+ return @on_false.run()
426
+ end
427
+ end
428
+
429
+ end
430
+
431
+ class SwitchRun < AbstractRun
432
+
433
+ constructor [ :key ], :cases, :default
434
+ children :cases, :default
435
+
436
+ def execute()
437
+ matches = @cases.select { |cond, result| cond.evaluate(Scope.current) }
438
+ return execute_cases(matches)
439
+ end
440
+
441
+ def execute_cases(cases)
442
+ if cases.length != 0
443
+ _, r = cases.first # execute only the first match
444
+ return r.run()
445
+ elsif @default
446
+ return @default.run()
447
+ else
448
+ return nil
449
+ end
450
+ end
451
+
452
+ end
453
+
454
+ class MultiRun < SwitchRun
455
+
456
+ def execute_cases(cases)
457
+ if cases.length != 0
458
+ scope = Scope.current
459
+ arr = OrderedArray.new
460
+ ress = run_threads(cases) do |r, i|
461
+ Scope.set(scope.push)
462
+ x = r.last.run()
463
+ arr.give(i, x)
464
+ end
465
+ return arr.take(cases.length)
466
+ elsif @default
467
+ return @default.run()
468
+ else
469
+ return nil
470
+ end
471
+ end
472
+
473
+ end
474
+
475
+ class BoundSwitchRun < SwitchRun
476
+
477
+ constructor [ :key, :cases, :default ], :condition
478
+ children :cases, :default, :condition
479
+
480
+ def execute()
481
+ v = @condition.evaluate(Scope.current)
482
+ matches = @cases.select { |cond, result|
483
+ cond.evaluate(Scope.current) == v
484
+ }
485
+ return execute_cases(matches)
486
+ end
487
+
488
+ end
489
+
490
+ class CheckpointRun < AbstractRun
491
+
492
+ attr_accessor :parent
493
+
494
+ constructor [ :key ], :name, :opts, :parent
495
+ children
496
+
497
+ def name
498
+ return '[no name]' if @name.nil?
499
+ return @name
500
+ end
501
+
502
+ def parent_keys
503
+ return @parent.args # arguments to the process
504
+ end
505
+
506
+ def state_keys
507
+ before, after = @parent.split(self)
508
+ vars1 = before.declarations.keys # vars defined BEFORE the checkpoint
509
+ vars2 = after.vars # vars used AFTER the checkpoint
510
+ return (vars1 + parent_keys) # & vars2
511
+ end
512
+
513
+ def meta_info(scope)
514
+ {
515
+ :type => :checkpoint,
516
+ :args => parent_keys.map { |x| scope[x] },
517
+ :name => @name,
518
+ :key => @key,
519
+ :parent => @parent.name
520
+ }
521
+ end
522
+
523
+ def checkpointable_libs()
524
+ libs = engine.get_libraries
525
+ libs = libs.select { |ns, l| l.respond_to?(:checkpoint) }
526
+ return libs
527
+ end
528
+
529
+ def execute()
530
+ scope = Scope.current
531
+ state = { 'vars' => {} }
532
+ state_keys.each { |k| state['vars'][k] = scope.get(k, true) } # TODO: fix this
533
+ state['meta'] = meta_info(scope)
534
+ lib_dump = checkpointable_libs().map do |ns, l|
535
+ {
536
+ :state => l.checkpoint(),
537
+ :namespace => ns
538
+ }
539
+ end
540
+ state['libs'] = lib_dump
541
+ engine.dumper.dump(state, @opts)
542
+ engine.verbose "Checkpoint '#{name}' saved."
543
+ return nil
544
+ end
545
+
546
+ def restart()
547
+ scope = Scope.current
548
+ m = meta_info(scope)
549
+ obj = engine.dumper.load(m)
550
+ return false if obj.nil?
551
+ vars = obj['vars']
552
+ libs = obj['libs']
553
+ vars.each_pair { |k, v| scope[k] = v }
554
+ cplibs = checkpointable_libs()
555
+ raise "Fatal checkpoint error (#{cplibs.length} != #{libs.length})" if cplibs.length != libs.length
556
+ raise "Fatal checkpoint error (something wrong)" if cplibs.length != libs.length
557
+ names1 = cplibs.map { |x| x.last }
558
+ names2 = libs.map { |x| x[:namespace] }
559
+ libs.each do |cp|
560
+ ns = cp[:namespace]
561
+ library = cplibs[ns]
562
+ library.restore(cp[:state])
563
+ end
564
+ return true
565
+ end
566
+
567
+ def to_s
568
+ "<Checkpoint #{@name.inspect}>"
569
+ end
570
+
571
+ end
572
+
573
+ class CacheRun < AbstractRun
574
+
575
+ attr_reader :body
576
+
577
+ constructor [ :key ], :opts, :body
578
+ children :body
579
+
580
+ def meta_info()
581
+ {
582
+ :type => :cache,
583
+ :key => @key
584
+ }
585
+ end
586
+
587
+ def execute()
588
+ ignoring = engine.config(:ignore_checkpoints)
589
+ m = meta_info()
590
+ o = engine.dumper.load(m)
591
+ if o.nil? or ignoring == true
592
+ value = @body.run()
593
+ obj = { 'meta' => m, 'value' => value }
594
+ engine.dumper.dump(obj)
595
+ return value
596
+ else
597
+ engine.verbose("Cached block for key = #{@key} loaded.")
598
+ return o['value']
599
+ end
600
+ end
601
+
602
+ end
603
+
604
+ class InfoRun < AbstractRun
605
+
606
+ attr_reader :body
607
+
608
+ constructor [ :key ], :opts, :body
609
+ children :body
610
+
611
+ def execute()
612
+ opts = @opts.evaluate(Scope.current)
613
+ opts = { :fail => true }.merge(opts)
614
+ failed = false
615
+ begin
616
+ start_time = Time.now.to_f
617
+ @body.run()
618
+ end_time = Time.now.to_f
619
+ total_time = end_time - start_time
620
+ rescue RunError => e
621
+ engine.verbose("Info run errored with #{e.summary}")
622
+ failed = true
623
+ total_time = 0.0
624
+ end
625
+ if opts[:fail] and failed
626
+ raise "info block failed: "
627
+ end
628
+ return {
629
+ :time => total_time,
630
+ :failed => failed
631
+ }
632
+ end
633
+
634
+ end
635
+
636
+ class TryRun < AbstractRun
637
+
638
+ attr_reader :body
639
+
640
+ constructor [ :key ], :opts, :body
641
+ children :body
642
+
643
+ def execute()
644
+ opts = { :retry => 1, :timeout => 0 }.merge(@opts.evaluate(Scope.current))
645
+ engine.verbose("Try block: #{opts}")
646
+ timeout, times = opts[:timeout], opts[:retry]
647
+ times = 1 if times == false
648
+ times = Infinity if times == true
649
+ exc = nil
650
+ for i in 1..times do
651
+ begin
652
+ if timeout == 0
653
+ return @body.run()
654
+ else
655
+ begin
656
+ r = Timeout::timeout(timeout, exc) do
657
+ @body.run()
658
+ end
659
+ return r
660
+ rescue Timeout::Error => e
661
+ raise RunMsgError.new(self, "Timeout")
662
+ end
663
+ end
664
+ rescue RunError => e
665
+ engine.verbose("Try rerun at #{meta.location}; caused by #{e.summary}")
666
+ exc = e
667
+ end
668
+ end
669
+ raise exc
670
+ end
671
+ end
672
+
673
+ class ResultRun < AbstractRun
674
+
675
+ attr_reader :body
676
+
677
+ constructor [ :key ], :path, :opts, :body
678
+ children :body
679
+
680
+ def execute()
681
+ path = @path.evaluate(Scope.current)
682
+ opts = @opts.evaluate(Scope.current)
683
+ if File.exist?(path)
684
+ engine.log("Result `#{path}' exists already. I won't run again.")
685
+ yaml = IO.read(path)
686
+ return YAML.load(yaml)
687
+ end
688
+ r = @body.run()
689
+ IO.write(path, r.to_yaml)
690
+ return r
691
+ end
692
+
693
+ end
694
+
695
+ class TimesRun < AbstractRun
696
+
697
+ attr_reader :body
698
+
699
+ constructor [ :key ], :loops, :iter, :body
700
+ children :body
701
+
702
+ def execute()
703
+ loops = @loops.evaluate(Scope.current)
704
+ vals = []
705
+ for i in 0...loops do
706
+ Scope.region do |scope|
707
+ scope[@iter] = i
708
+ r = @body.run()
709
+ vals.push(r)
710
+ end
711
+ end
712
+ return vals
713
+ end
714
+
715
+ end
716
+
717
+ class LoopRun < AbstractRun
718
+
719
+ attr_reader :body
720
+
721
+ constructor [ :key ], :flag, :iter, :array, :body, :opts
722
+ children :body
723
+
724
+ def execute()
725
+ opts = @opts.evaluate(Scope.current)
726
+ max = opts[:max]
727
+ arr = []
728
+ result = Scope.region do |scope|
729
+ scope[@flag] = done = [ :nothing, nil ]
730
+
731
+ count = 0
732
+ while true do
733
+ scope[@iter] = count
734
+ scope[@array] = arr.clone
735
+ res = nil
736
+ for r in @body do
737
+ res = r.run()
738
+ done = scope[@flag]
739
+ break if done.first != :nothing
740
+ end
741
+ break if done.first == :return
742
+ arr.push(res)
743
+ count += 1
744
+ break if (!max.nil? and count >= max)
745
+ end
746
+
747
+ if done.last.nil?
748
+ arr
749
+ else
750
+ done.last
751
+ end
752
+ end
753
+ return result
754
+ end
755
+
756
+ end
757
+
758
+ class ReturnLoopRun < AbstractRun
759
+
760
+ constructor [ :key ], :flag, :cond, :value
761
+ children
762
+
763
+ def execute()
764
+ v = @cond.evaluate(Scope.current)
765
+ if v then
766
+ r = @value.evaluate(Scope.current)
767
+ Scope.current[@flag] = [ :return, r ]
768
+ end
769
+ end
770
+ end
771
+
772
+ end
773
+