gisele-vm 0.6.0

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