gisele-vm 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (185) hide show
  1. data/CHANGELOG.md +5 -0
  2. data/Gemfile +18 -0
  3. data/Gemfile.lock +46 -0
  4. data/LICENCE.md +22 -0
  5. data/Manifest.txt +15 -0
  6. data/README.md +10 -0
  7. data/Rakefile +11 -0
  8. data/bin/gvm +9 -0
  9. data/gisele-vm.gemspec +191 -0
  10. data/gisele-vm.noespec +31 -0
  11. data/lib/gisele-vm.rb +4 -0
  12. data/lib/gisele-vm/loader.rb +5 -0
  13. data/lib/gisele-vm/version.rb +16 -0
  14. data/lib/gisele/compiling.rb +3 -0
  15. data/lib/gisele/compiling/gisele2gts.rb +143 -0
  16. data/lib/gisele/compiling/gts.rb +74 -0
  17. data/lib/gisele/compiling/gts2bytecode.rb +127 -0
  18. data/lib/gisele/vm.rb +87 -0
  19. data/lib/gisele/vm/bytecode.rb +84 -0
  20. data/lib/gisele/vm/bytecode/builder.rb +77 -0
  21. data/lib/gisele/vm/bytecode/grammar.citrus +116 -0
  22. data/lib/gisele/vm/bytecode/grammar.rb +19 -0
  23. data/lib/gisele/vm/bytecode/grammar.sexp.yml +113 -0
  24. data/lib/gisele/vm/bytecode/printer.rb +35 -0
  25. data/lib/gisele/vm/command.rb +140 -0
  26. data/lib/gisele/vm/component.rb +91 -0
  27. data/lib/gisele/vm/console.rb +58 -0
  28. data/lib/gisele/vm/enacter.rb +29 -0
  29. data/lib/gisele/vm/errors.rb +26 -0
  30. data/lib/gisele/vm/event.rb +11 -0
  31. data/lib/gisele/vm/event_manager.rb +65 -0
  32. data/lib/gisele/vm/kernel.rb +58 -0
  33. data/lib/gisele/vm/kernel/macros.gvm +214 -0
  34. data/lib/gisele/vm/kernel/opcodes.rb +212 -0
  35. data/lib/gisele/vm/kernel/runner.rb +63 -0
  36. data/lib/gisele/vm/lifecycle.rb +72 -0
  37. data/lib/gisele/vm/logging.rb +18 -0
  38. data/lib/gisele/vm/null_object.rb +19 -0
  39. data/lib/gisele/vm/prog.rb +63 -0
  40. data/lib/gisele/vm/prog_list.rb +55 -0
  41. data/lib/gisele/vm/prog_list/memory.rb +74 -0
  42. data/lib/gisele/vm/prog_list/sqldb.rb +123 -0
  43. data/lib/gisele/vm/prog_list/storage.rb +31 -0
  44. data/lib/gisele/vm/proxy.rb +14 -0
  45. data/lib/gisele/vm/proxy/client.rb +64 -0
  46. data/lib/gisele/vm/proxy/server.rb +29 -0
  47. data/lib/gisele/vm/registry.rb +57 -0
  48. data/lib/gisele/vm/robustness.rb +31 -0
  49. data/lib/gisele/vm/simulator/resumer.rb +32 -0
  50. data/spec/command/gvm_compile.cmd +1 -0
  51. data/spec/command/gvm_compile.stdout +111 -0
  52. data/spec/command/gvm_gts.cmd +1 -0
  53. data/spec/command/gvm_gts.stdout +101 -0
  54. data/spec/command/gvm_help.cmd +1 -0
  55. data/spec/command/gvm_help.stdout +30 -0
  56. data/spec/command/gvm_version.cmd +1 -0
  57. data/spec/command/gvm_version.stdout +2 -0
  58. data/spec/command/test_command.rb +29 -0
  59. data/spec/fixtures/complete.gis +13 -0
  60. data/spec/fixtures/fake_component.rb +24 -0
  61. data/spec/fixtures/kernel.rb +39 -0
  62. data/spec/fixtures/ts.adl +11 -0
  63. data/spec/fixtures/ts.gts +20 -0
  64. data/spec/fixtures/ts.gvm +19 -0
  65. data/spec/spec_helper.rb +86 -0
  66. data/spec/test_examples.rb +29 -0
  67. data/spec/test_gisele-vm.rb +8 -0
  68. data/spec/unit/bytecode/builder/test_at.rb +56 -0
  69. data/spec/unit/bytecode/builder/test_helpers.rb +36 -0
  70. data/spec/unit/bytecode/builder/test_instruction.rb +35 -0
  71. data/spec/unit/bytecode/builder/test_to_a.rb +53 -0
  72. data/spec/unit/bytecode/bytecode.gvm +1 -0
  73. data/spec/unit/bytecode/grammar/fixtures/comments.gvm +16 -0
  74. data/spec/unit/bytecode/grammar/fixtures/every.gvm +46 -0
  75. data/spec/unit/bytecode/grammar/fixtures/singleblock.gvm +2 -0
  76. data/spec/unit/bytecode/grammar/fixtures/twoblocks.gvm +4 -0
  77. data/spec/unit/bytecode/grammar/fixtures/with_end.gvm +5 -0
  78. data/spec/unit/bytecode/grammar/test_array.rb +24 -0
  79. data/spec/unit/bytecode/grammar/test_block.rb +35 -0
  80. data/spec/unit/bytecode/grammar/test_boolean.rb +20 -0
  81. data/spec/unit/bytecode/grammar/test_constant.rb +20 -0
  82. data/spec/unit/bytecode/grammar/test_eol.rb +20 -0
  83. data/spec/unit/bytecode/grammar/test_eol_comment.rb +36 -0
  84. data/spec/unit/bytecode/grammar/test_file.rb +38 -0
  85. data/spec/unit/bytecode/grammar/test_hash.rb +33 -0
  86. data/spec/unit/bytecode/grammar/test_instruction.rb +32 -0
  87. data/spec/unit/bytecode/grammar/test_int.rb +24 -0
  88. data/spec/unit/bytecode/grammar/test_label.rb +24 -0
  89. data/spec/unit/bytecode/grammar/test_opcode.rb +23 -0
  90. data/spec/unit/bytecode/grammar/test_string.rb +25 -0
  91. data/spec/unit/bytecode/grammar/test_symbol.rb +30 -0
  92. data/spec/unit/bytecode/test_build.rb +36 -0
  93. data/spec/unit/bytecode/test_coerce.rb +41 -0
  94. data/spec/unit/bytecode/test_fetch.rb +20 -0
  95. data/spec/unit/bytecode/test_grammar.rb +30 -0
  96. data/spec/unit/bytecode/test_parse.rb +22 -0
  97. data/spec/unit/bytecode/test_plus.rb +27 -0
  98. data/spec/unit/bytecode/test_to_a.rb +19 -0
  99. data/spec/unit/bytecode/test_to_s.rb +32 -0
  100. data/spec/unit/command/code.gis +3 -0
  101. data/spec/unit/command/test_vm.rb +51 -0
  102. data/spec/unit/compiling/gisele2gts/test_on_par_st.rb +51 -0
  103. data/spec/unit/compiling/gisele2gts/test_on_seq_st.rb +46 -0
  104. data/spec/unit/compiling/gisele2gts/test_on_task_call_st.rb +37 -0
  105. data/spec/unit/compiling/gisele2gts/test_on_task_def.rb +49 -0
  106. data/spec/unit/compiling/gisele2gts/test_on_unit_def.rb +35 -0
  107. data/spec/unit/compiling/gts2bytecode/test_on_end.rb +31 -0
  108. data/spec/unit/compiling/gts2bytecode/test_on_event.rb +37 -0
  109. data/spec/unit/compiling/gts2bytecode/test_on_fork.rb +41 -0
  110. data/spec/unit/compiling/gts2bytecode/test_on_join.rb +42 -0
  111. data/spec/unit/compiling/gts2bytecode/test_on_listen.rb +36 -0
  112. data/spec/unit/compiling/gts2bytecode/test_on_nop.rb +30 -0
  113. data/spec/unit/component/test_component_name.rb +16 -0
  114. data/spec/unit/component/test_logging.rb +36 -0
  115. data/spec/unit/enacter/test_component.rb +11 -0
  116. data/spec/unit/event/test_to_s.rb +12 -0
  117. data/spec/unit/event_manager/test_component.rb +9 -0
  118. data/spec/unit/event_manager/test_subscribe.rb +40 -0
  119. data/spec/unit/event_manager/test_unsubscribe.rb +39 -0
  120. data/spec/unit/kernel/macros/test_fork.rb +37 -0
  121. data/spec/unit/kernel/macros/test_join.rb +43 -0
  122. data/spec/unit/kernel/macros/test_listen.rb +37 -0
  123. data/spec/unit/kernel/macros/test_notify.rb +57 -0
  124. data/spec/unit/kernel/macros/test_react.rb +47 -0
  125. data/spec/unit/kernel/macros/test_schedule_at.rb +30 -0
  126. data/spec/unit/kernel/opcodes/test_op_del.rb +42 -0
  127. data/spec/unit/kernel/opcodes/test_op_event.rb +25 -0
  128. data/spec/unit/kernel/opcodes/test_op_fetch.rb +27 -0
  129. data/spec/unit/kernel/opcodes/test_op_flip.rb +17 -0
  130. data/spec/unit/kernel/opcodes/test_op_fold.rb +29 -0
  131. data/spec/unit/kernel/opcodes/test_op_fork.rb +63 -0
  132. data/spec/unit/kernel/opcodes/test_op_forka.rb +51 -0
  133. data/spec/unit/kernel/opcodes/test_op_get.rb +62 -0
  134. data/spec/unit/kernel/opcodes/test_op_getr.rb +48 -0
  135. data/spec/unit/kernel/opcodes/test_op_ifenil.rb +41 -0
  136. data/spec/unit/kernel/opcodes/test_op_ifezero.rb +32 -0
  137. data/spec/unit/kernel/opcodes/test_op_invoke.rb +34 -0
  138. data/spec/unit/kernel/opcodes/test_op_nop.rb +18 -0
  139. data/spec/unit/kernel/opcodes/test_op_parent.rb +39 -0
  140. data/spec/unit/kernel/opcodes/test_op_pop.rb +22 -0
  141. data/spec/unit/kernel/opcodes/test_op_push.rb +17 -0
  142. data/spec/unit/kernel/opcodes/test_op_save.rb +32 -0
  143. data/spec/unit/kernel/opcodes/test_op_savea.rb +34 -0
  144. data/spec/unit/kernel/opcodes/test_op_self.rb +20 -0
  145. data/spec/unit/kernel/opcodes/test_op_send.rb +20 -0
  146. data/spec/unit/kernel/opcodes/test_op_set.rb +61 -0
  147. data/spec/unit/kernel/opcodes/test_op_then.rb +50 -0
  148. data/spec/unit/kernel/opcodes/test_op_unfold.rb +22 -0
  149. data/spec/unit/kernel/opcodes/test_op_uuid.rb +16 -0
  150. data/spec/unit/kernel/runner/test_pop.rb +26 -0
  151. data/spec/unit/kernel/runner/test_stack.rb +28 -0
  152. data/spec/unit/kernel/test_progress.rb +47 -0
  153. data/spec/unit/kernel/test_resume.rb +53 -0
  154. data/spec/unit/kernel/test_start.rb +36 -0
  155. data/spec/unit/prog/test_to_hash.rb +29 -0
  156. data/spec/unit/prog/test_waitlist_eq.rb +20 -0
  157. data/spec/unit/prog_list/memory/test_component.rb +9 -0
  158. data/spec/unit/prog_list/memory/test_fetch.rb +40 -0
  159. data/spec/unit/prog_list/memory/test_pick.rb +39 -0
  160. data/spec/unit/prog_list/memory/test_save.rb +91 -0
  161. data/spec/unit/prog_list/memory/test_to_relation.rb +17 -0
  162. data/spec/unit/prog_list/sqldb/test_component.rb +11 -0
  163. data/spec/unit/prog_list/sqldb/test_connect.rb +46 -0
  164. data/spec/unit/prog_list/test_memory.rb +9 -0
  165. data/spec/unit/prog_list/test_sqldb.rb +13 -0
  166. data/spec/unit/prog_list/test_storage.rb +51 -0
  167. data/spec/unit/registry/test_component.rb +9 -0
  168. data/spec/unit/registry/test_connect.rb +53 -0
  169. data/spec/unit/registry/test_disconnect.rb +51 -0
  170. data/spec/unit/registry/test_registration.rb +44 -0
  171. data/spec/unit/shared/a_component.rb +49 -0
  172. data/spec/unit/shared/a_storage.rb +114 -0
  173. data/spec/unit/test_logging.rb +46 -0
  174. data/spec/unit/test_prog.rb +57 -0
  175. data/spec/unit/test_prog_list.rb +22 -0
  176. data/spec/unit/vm/test_event_facace.rb +11 -0
  177. data/spec/unit/vm/test_initialize.rb +59 -0
  178. data/spec/unit/vm/test_proglist_facade.rb +21 -0
  179. data/tasks/debug_mail.rake +75 -0
  180. data/tasks/debug_mail.txt +13 -0
  181. data/tasks/gem.rake +73 -0
  182. data/tasks/spec_test.rake +71 -0
  183. data/tasks/unit_test.rake +76 -0
  184. data/tasks/yard.rake +51 -0
  185. metadata +493 -0
@@ -0,0 +1,58 @@
1
+ module Gisele
2
+ class VM
3
+ class Console < Component
4
+
5
+ module Handler
6
+ attr_accessor :interactive
7
+ def notify_readable
8
+ case s = @io.readline.strip
9
+ when /^l(ist)?$/ then interactive.list_action
10
+ when /^n(ew)?\s+(.+)$/ then interactive.new_action($2)
11
+ when /^r(esume)?\s+(.+)$/ then interactive.resume_action($2)
12
+ when /^q(uit)?$/ then interactive.stop_action
13
+ else
14
+ puts "Unrecognized: #{s}" unless s.empty?
15
+ end
16
+ rescue Exception => ex
17
+ puts "ERROR: #{ex.message}"
18
+ ensure
19
+ interactive.prompt if interactive.connected?
20
+ end
21
+ end
22
+
23
+ def enter_heartbeat
24
+ EM.watch($stdin, Handler){|c|
25
+ c.interactive = self
26
+ c.notify_readable = true
27
+ }
28
+ prompt
29
+ end
30
+
31
+ def prompt
32
+ $stdout << "\n? Please choose an action:(list, new, resume or quit)\ngisele-vm> "
33
+ end
34
+
35
+ def list_action(lispy = Alf.lispy)
36
+ rel = vm.progs
37
+ rel = lispy.extend(rel, :waitlist => lambda{ waitlist.keys })
38
+ rel = lispy.group(rel, [:root], :progs, :allbut => true)
39
+ puts rel.to_relation
40
+ end
41
+
42
+ def new_action(args)
43
+ vm.start(:main, [ args.strip.to_sym ])
44
+ end
45
+
46
+ def resume_action(args)
47
+ puid, *input = args.split(/\s+/)
48
+ input = input.map{|x| Bytecode::Grammar.parse(x, :root => :arg).value}
49
+ vm.resume(puid, input)
50
+ end
51
+
52
+ def stop_action
53
+ vm.stop
54
+ end
55
+
56
+ end # class Console
57
+ end # class VM
58
+ end # module Gisele
@@ -0,0 +1,29 @@
1
+ module Gisele
2
+ class VM
3
+ class Enacter < Component
4
+
5
+ def enter_heartbeat
6
+ @timer = EM.add_periodic_timer(0.01, &method(:enact))
7
+ end
8
+
9
+ def leave_heartbeat
10
+ @timer.cancel
11
+ end
12
+
13
+ private
14
+
15
+ def enact
16
+ prog = vm.pick(:waitfor => :enacter)
17
+ enact_one(prog) if prog
18
+ end
19
+
20
+ def enact_one(prog)
21
+ debug("Enacting Prog #{prog.puid}@#{prog.pc}")
22
+ vm.progress(prog)
23
+ rescue Exception => ex
24
+ error error_message(ex, "Progress error (#{prog.puid}):")
25
+ end
26
+
27
+ end # class Enacter
28
+ end # class VM
29
+ end # module Gisele
@@ -0,0 +1,26 @@
1
+ module Gisele
2
+ class VM
3
+
4
+ class Error < StandardError
5
+ end # class Error
6
+
7
+ class InvalidBytecodeError < Error
8
+ end # class InvalidBytecodeError
9
+
10
+ class InvalidLabelError < Error
11
+ end # class InvalidLabelError
12
+
13
+ class InvalidInputError < Error
14
+ end # class InvalidInputError
15
+
16
+ class InvalidStateError < Error
17
+ end # class InvalidStateError
18
+
19
+ class BadUsageError < Error
20
+ end # BadUsageError
21
+
22
+ class InvalidPUIDError < Error
23
+ end # class InvalidPUIDError
24
+
25
+ end # class VM
26
+ end # module Gisele
@@ -0,0 +1,11 @@
1
+ module Gisele
2
+ class VM
3
+ class Event < Struct.new(:prog, :type, :args)
4
+
5
+ def to_s
6
+ "#{type}[#{prog.puid}](#{args.join(', ')})"
7
+ end
8
+
9
+ end # class Event
10
+ end # class VM
11
+ end # module Gisele
@@ -0,0 +1,65 @@
1
+ module Gisele
2
+ class VM
3
+ class EventManager < Component
4
+
5
+ def initialize
6
+ super()
7
+ @listeners = []
8
+ end
9
+
10
+ def connect
11
+ super
12
+ @channel, @names = EM::Channel.new, {}
13
+ @listeners.each do |l|
14
+ em_subscribe(l)
15
+ end
16
+ end
17
+
18
+ def disconnect
19
+ super
20
+ @listeners.each do |l|
21
+ em_unsubscribe(l)
22
+ end
23
+ @channel = @names = nil
24
+ end
25
+
26
+ def subscribe(a = nil, &b)
27
+ @listeners << (a || b)
28
+ em_subscribe(@listeners.last) if connected?
29
+ end
30
+
31
+ def unsubscribe(l)
32
+ if @listeners.delete(l) and connected?
33
+ em_unsubscribe(l)
34
+ end
35
+ end
36
+
37
+ def subscribed?(l)
38
+ @listeners.include?(l)
39
+ end
40
+
41
+ def event(event)
42
+ debug(event.to_s)
43
+ @channel.push(event) if connected?
44
+ rescue Exception => ex
45
+ error error_message(ex, "Error when processing `#{event.to_s}`")
46
+ end
47
+
48
+ private
49
+
50
+ def name_of(l)
51
+ @names[l]
52
+ end
53
+
54
+ def em_subscribe(l)
55
+ @names[l] = @channel.subscribe(l)
56
+ end
57
+
58
+ def em_unsubscribe(l)
59
+ @channel.unsubscribe(@names[l])
60
+ @names.delete(l)
61
+ end
62
+
63
+ end # class EventManager
64
+ end # class VM
65
+ end # module Gisele
@@ -0,0 +1,58 @@
1
+ module Gisele
2
+ class VM
3
+ class Kernel < Component
4
+ include Robustness
5
+
6
+ def self.bytecode
7
+ @kernelbc ||= Bytecode.parse(Path.dir/'kernel/macros.gvm')
8
+ end
9
+
10
+ def_delegators :vm, :pick,
11
+ :fetch,
12
+ :save,
13
+ :event
14
+
15
+ def start(at, input)
16
+ at = valid_label!(at)
17
+ input = valid_input!(input)
18
+
19
+ runner(nil) do |k|
20
+ stack = k.run(:start, [ input, at ])
21
+ stack.first
22
+ end
23
+ end
24
+
25
+ def resume(puid, input)
26
+ prog = Prog===puid ? puid : fetch(puid)
27
+ prog = valid_prog!(puid, :world)
28
+ input = valid_input!(input)
29
+
30
+ runner(prog) do |k|
31
+ k.run(:resume, [ input ])
32
+ end
33
+ end
34
+
35
+ def progress(puid)
36
+ prog = valid_prog!(puid, :enacter)
37
+
38
+ runner(prog) do |k|
39
+ k.run(:progress, [ ])
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def runner(prog = nil)
46
+ if block_given?
47
+ yield Runner.new(vm, prog)
48
+ else
49
+ Runner.new(vm, prog)
50
+ end
51
+ end
52
+
53
+ end # class Kernel
54
+ end # class VM
55
+ end # module Gisele
56
+ require_relative 'kernel/opcodes'
57
+ require_relative 'kernel/runner'
58
+
@@ -0,0 +1,214 @@
1
+ # Make the current Prog progress by running it on its input.
2
+ #
3
+ # INPUT: [ ]
4
+ # OUTPUT: [ ]
5
+ # PRE: - the current Prog must be scheduled (waitfor: enacter)
6
+ # - its input must be set as well as its program counter
7
+ # POST: - the Prog has been executed on its input
8
+ # - its input is now [ ]
9
+ #
10
+ progress: self # [ Prog ]
11
+ get :pc # [ Prog, pc ]
12
+ then # [ Prog ] # enqueue instructions at pc
13
+ get :input # [ Prog, [input...] ]
14
+ flip # [ [ input... ], Prog ]
15
+ push [] # [ [input...], Prog, [] ]
16
+ set :input # [ [input...], Prog ]
17
+ save # [ [input...], puid ]
18
+ pop # [ [input...] ]
19
+ unfold # [ input... ]
20
+
21
+ # Forks a fresh new process and schedules it for running at a given label.
22
+ #
23
+ # INPUT: [ [input...], `startat` ]
24
+ # OUTPUT: [ puid ]
25
+ # PRE: - true
26
+ # POST: - a new process has been created
27
+ # - it is scheduled for running at `startat`
28
+ # - its input has been set to `input`
29
+ #
30
+ start: fork # [ [input...], Prog ]
31
+ flip # [ Prog, [input...] ]
32
+ set :input # [ Prog ]
33
+ save # [ puid ]
34
+
35
+ # Schedule the current prog and set its input.
36
+ #
37
+ # INPUT: [ [input...] ]
38
+ # OUTPUT: [ ]
39
+ # PRE: - the current Prog should not be scheduled
40
+ # POST: - the current Prog is scheduled
41
+ # - its input is set to `input`
42
+ #
43
+ resume: self # [ input, Prog ]
44
+ push :enacter # [ input, Prog, :enacter ]
45
+ set :waitfor # [ input, Prog ]
46
+ flip # [ input, Prog ] -> [ Prog, input ]
47
+ set :input # [ Prog ]
48
+ then :save_it # [ ]
49
+
50
+ # Given a Prog on the stack, simply saves it.
51
+ #
52
+ # INPUT: [ Prog ] (aka `peek`)
53
+ # OUTPUT: [ ]
54
+ # PRE: - `peek` should have been modified
55
+ # POST: - `peek` is now saved
56
+ #
57
+ save_it: save
58
+ pop
59
+
60
+ # Given a label on the stack, schedule the current Prog for execution there. The effective
61
+ # execution will only take place when the Prog will be selected for running.
62
+ #
63
+ # INPUT: [ `at` ]
64
+ # OUTPUT: [ ]
65
+ # PRE: - `at` must be a valid label
66
+ # POST: - the current Prog is scheduled and its program counter set to `at` (and it is
67
+ # saved)
68
+ # - its waiting list is empty
69
+ #
70
+ schedule_at: self # [ pc, Prog ]
71
+ flip # [ Prog, pc ]
72
+ set :pc # [ Prog ]
73
+ push :enacter # [ Prog, :enacter ]
74
+ set :waitfor # [ Prog ]
75
+ push {} # [ Prog, {} ]
76
+ set :waitlist # [ Prog ]
77
+ then :save_it # [ ]
78
+
79
+ # Given an event Hash on the stack. Registers it as waitlist. Unschedule the current prog
80
+ # for continuation at :react.
81
+ #
82
+ # INPUT: [ `events` ]
83
+ # OUTPUT: [ ]
84
+ # PRE: - `events` should be a valid waitlist
85
+ # POST: - the current Prog sleeps at :react.
86
+ # - its waitlist has been set to `events`
87
+ #
88
+ listen: self # [ events, Prog ]
89
+ flip # [ Prog, events ]
90
+ set :waitlist # [ Prog ]
91
+ push :world # [ Prog, :world ]
92
+ set :waitfor # [ Prog ]
93
+ push :react # [ Prog, :react ]
94
+ set :pc # [ Prog ]
95
+ then :save_it # [ ]
96
+
97
+ # Given an event on the stack, get the current Prog, check if it waits for this particular
98
+ # event. If yes, schedule the Prog as specified in its wait list. Otherwise, return to
99
+ # sleep.
100
+ #
101
+ # INPUT: [ `event` ]
102
+ # OUTPUT: [ ]
103
+ # PRE: - the current Prog should be waiting for events (in its waitlist)
104
+ # POST: - if `event` is waited for, the current Prog is scheduled at the corresponding
105
+ # location.
106
+ # - otherwise, the current Prog falls alsleep (program counter unchanged)
107
+ #
108
+ react: self # [ event, Prog ]
109
+ getr :waitlist # [ event, waitlist ]
110
+ flip # [ waitlist, event ]
111
+ getr # [ pc/nil ]
112
+ ifenil #
113
+ then :react_sleep #
114
+ then :schedule_at #
115
+ react_sleep: pop # [ ]
116
+ self # [ Prog ]
117
+ push :world # [ Prog, :world ]
118
+ set :waitfor # [ Prog ]
119
+ then :save_it #
120
+
121
+ # Fork a list of children at specified locations. Wait for joining them at a label
122
+ # `joinat` specified through the stack as well.
123
+ #
124
+ # INPUT: [ joinat, [ forkat... ] ]
125
+ # OUTPUT: [ Prog ] (the current prog)
126
+ # PRE: - `joinat` is an existing label
127
+ # - `forkat` is an array of existing labels.
128
+ # POST: - a child Prog has been created for each label in `forkat`
129
+ # - those children are all scheduled
130
+ # - the current Prog waits for them all at `joinat` (not scheduled)
131
+ #
132
+ fork: forka # [ joinat, [ Prog, ... ] ]
133
+ savea # [ joinat, [ puid, ... ] ]
134
+ self # [ joinat, [ puid, ... ], Prog ]
135
+ flip # [ joinat, Prog, [ puid, ... ] ]
136
+ set :waitlist # [ joinat, Prog ]
137
+ flip # [ Prog, joinat ]
138
+ set :pc # [ Prog ]
139
+ push :children # [ Prog, :children ]
140
+ set :waitfor # [ Prog ]
141
+ then :save_it # [ ]
142
+
143
+ # Given a Hash of events, if the waitlist of the current prog is empty, schedule execution
144
+ # at the label associated to the `wake` event. Otherwise simply return asleep.
145
+ #
146
+ # INPUT: [ {:wake => `wakeat`} ] (aka `events`)
147
+ # OUTPUT: [ ]
148
+ # PRE: - `events` must include a :wake key
149
+ # - `wakeat` must be an existing label
150
+ # POST: - if the waitlist of the current prog is empty, it is scheduled at `wakeat`
151
+ # - if ... is not empty, it is unscheduled (program counter unchanged)
152
+ #
153
+ join: self # [ {evt...}, Prog ]
154
+ getr :waitlist # [ {evt...}, waitlist ]
155
+ push []
156
+ send :size # [ {evt...}, size ]
157
+ ifezero
158
+ push :wake
159
+ push :wait # [ {evt...}, size, wake/wait ]
160
+ flip # [ {evt...}, wake/wait, size ]
161
+ pop # [ {evt...}, wake/wait ]
162
+ get # [ {evt...}, label/nil ]
163
+ flip # [ label/nil, {evt...} ]
164
+ pop # [ label/nil ]
165
+ ifenil # ...
166
+ then :join_sleep # [ ]
167
+ then :schedule_at # [ ]
168
+ join_sleep: pop
169
+ self
170
+ push :children
171
+ set :waitfor
172
+ then :save_it
173
+
174
+ # Expects a Prog on the stack (typically the parent). Remove the current puid from its
175
+ # waitlist and schedule it.
176
+ #
177
+ # INPUT: [ Prog ] (aka peek)
178
+ # OUTPUT: [ ]
179
+ # PRE: - the current `puid` should be a key in `peek`'s waitlist
180
+ # POST: - `peek`'s waitlist no longer contains an entry for `puid`
181
+ # - `peek` is scheduled
182
+ #
183
+ notify_it: get :waitlist # [ Prog, waitlist ]
184
+ puid # [ Prog, waitlist, puid ]
185
+ del # [ Prog, waitlist ]
186
+ set :waitlist # [ Prog ]
187
+ push :enacter # [ Prog, :enacter ]
188
+ set :waitfor # [ Prog ]
189
+ then :save_it # [ ]
190
+
191
+ # End the current Prog then notify its parent through notify_it.
192
+ #
193
+ # INPUT: [ ]
194
+ # OUTPUT: [ ]
195
+ # PRE: - the current Prog should not be ended
196
+ # POST: - the program counter of the current Prog is -1
197
+ # => - its waitfor attribute has been set to :none
198
+ # - its parent has been notified, if any (@see notify_it)
199
+ #
200
+ notify: self # [ Prog ]
201
+ push -1 # [ Prog, -1 ]
202
+ set :pc # [ Prog ]
203
+ push :none # [ Prog, :none ]
204
+ set :waitfor # [ Prog ]
205
+ save # [ puid ]
206
+ pop # [ ]
207
+ parent # [ Prog/nil ]
208
+ ifenil
209
+ pop # [ ]
210
+ then :notify_it # [ ]
211
+
212
+ ping: fold 1
213
+ event :pong
214
+ then :notify