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.
- data/CHANGELOG.md +5 -0
- data/Gemfile +18 -0
- data/Gemfile.lock +46 -0
- data/LICENCE.md +22 -0
- data/Manifest.txt +15 -0
- data/README.md +10 -0
- data/Rakefile +11 -0
- data/bin/gvm +9 -0
- data/gisele-vm.gemspec +191 -0
- data/gisele-vm.noespec +31 -0
- data/lib/gisele-vm.rb +4 -0
- data/lib/gisele-vm/loader.rb +5 -0
- data/lib/gisele-vm/version.rb +16 -0
- data/lib/gisele/compiling.rb +3 -0
- data/lib/gisele/compiling/gisele2gts.rb +143 -0
- data/lib/gisele/compiling/gts.rb +74 -0
- data/lib/gisele/compiling/gts2bytecode.rb +127 -0
- data/lib/gisele/vm.rb +87 -0
- data/lib/gisele/vm/bytecode.rb +84 -0
- data/lib/gisele/vm/bytecode/builder.rb +77 -0
- data/lib/gisele/vm/bytecode/grammar.citrus +116 -0
- data/lib/gisele/vm/bytecode/grammar.rb +19 -0
- data/lib/gisele/vm/bytecode/grammar.sexp.yml +113 -0
- data/lib/gisele/vm/bytecode/printer.rb +35 -0
- data/lib/gisele/vm/command.rb +140 -0
- data/lib/gisele/vm/component.rb +91 -0
- data/lib/gisele/vm/console.rb +58 -0
- data/lib/gisele/vm/enacter.rb +29 -0
- data/lib/gisele/vm/errors.rb +26 -0
- data/lib/gisele/vm/event.rb +11 -0
- data/lib/gisele/vm/event_manager.rb +65 -0
- data/lib/gisele/vm/kernel.rb +58 -0
- data/lib/gisele/vm/kernel/macros.gvm +214 -0
- data/lib/gisele/vm/kernel/opcodes.rb +212 -0
- data/lib/gisele/vm/kernel/runner.rb +63 -0
- data/lib/gisele/vm/lifecycle.rb +72 -0
- data/lib/gisele/vm/logging.rb +18 -0
- data/lib/gisele/vm/null_object.rb +19 -0
- data/lib/gisele/vm/prog.rb +63 -0
- data/lib/gisele/vm/prog_list.rb +55 -0
- data/lib/gisele/vm/prog_list/memory.rb +74 -0
- data/lib/gisele/vm/prog_list/sqldb.rb +123 -0
- data/lib/gisele/vm/prog_list/storage.rb +31 -0
- data/lib/gisele/vm/proxy.rb +14 -0
- data/lib/gisele/vm/proxy/client.rb +64 -0
- data/lib/gisele/vm/proxy/server.rb +29 -0
- data/lib/gisele/vm/registry.rb +57 -0
- data/lib/gisele/vm/robustness.rb +31 -0
- data/lib/gisele/vm/simulator/resumer.rb +32 -0
- data/spec/command/gvm_compile.cmd +1 -0
- data/spec/command/gvm_compile.stdout +111 -0
- data/spec/command/gvm_gts.cmd +1 -0
- data/spec/command/gvm_gts.stdout +101 -0
- data/spec/command/gvm_help.cmd +1 -0
- data/spec/command/gvm_help.stdout +30 -0
- data/spec/command/gvm_version.cmd +1 -0
- data/spec/command/gvm_version.stdout +2 -0
- data/spec/command/test_command.rb +29 -0
- data/spec/fixtures/complete.gis +13 -0
- data/spec/fixtures/fake_component.rb +24 -0
- data/spec/fixtures/kernel.rb +39 -0
- data/spec/fixtures/ts.adl +11 -0
- data/spec/fixtures/ts.gts +20 -0
- data/spec/fixtures/ts.gvm +19 -0
- data/spec/spec_helper.rb +86 -0
- data/spec/test_examples.rb +29 -0
- data/spec/test_gisele-vm.rb +8 -0
- data/spec/unit/bytecode/builder/test_at.rb +56 -0
- data/spec/unit/bytecode/builder/test_helpers.rb +36 -0
- data/spec/unit/bytecode/builder/test_instruction.rb +35 -0
- data/spec/unit/bytecode/builder/test_to_a.rb +53 -0
- data/spec/unit/bytecode/bytecode.gvm +1 -0
- data/spec/unit/bytecode/grammar/fixtures/comments.gvm +16 -0
- data/spec/unit/bytecode/grammar/fixtures/every.gvm +46 -0
- data/spec/unit/bytecode/grammar/fixtures/singleblock.gvm +2 -0
- data/spec/unit/bytecode/grammar/fixtures/twoblocks.gvm +4 -0
- data/spec/unit/bytecode/grammar/fixtures/with_end.gvm +5 -0
- data/spec/unit/bytecode/grammar/test_array.rb +24 -0
- data/spec/unit/bytecode/grammar/test_block.rb +35 -0
- data/spec/unit/bytecode/grammar/test_boolean.rb +20 -0
- data/spec/unit/bytecode/grammar/test_constant.rb +20 -0
- data/spec/unit/bytecode/grammar/test_eol.rb +20 -0
- data/spec/unit/bytecode/grammar/test_eol_comment.rb +36 -0
- data/spec/unit/bytecode/grammar/test_file.rb +38 -0
- data/spec/unit/bytecode/grammar/test_hash.rb +33 -0
- data/spec/unit/bytecode/grammar/test_instruction.rb +32 -0
- data/spec/unit/bytecode/grammar/test_int.rb +24 -0
- data/spec/unit/bytecode/grammar/test_label.rb +24 -0
- data/spec/unit/bytecode/grammar/test_opcode.rb +23 -0
- data/spec/unit/bytecode/grammar/test_string.rb +25 -0
- data/spec/unit/bytecode/grammar/test_symbol.rb +30 -0
- data/spec/unit/bytecode/test_build.rb +36 -0
- data/spec/unit/bytecode/test_coerce.rb +41 -0
- data/spec/unit/bytecode/test_fetch.rb +20 -0
- data/spec/unit/bytecode/test_grammar.rb +30 -0
- data/spec/unit/bytecode/test_parse.rb +22 -0
- data/spec/unit/bytecode/test_plus.rb +27 -0
- data/spec/unit/bytecode/test_to_a.rb +19 -0
- data/spec/unit/bytecode/test_to_s.rb +32 -0
- data/spec/unit/command/code.gis +3 -0
- data/spec/unit/command/test_vm.rb +51 -0
- data/spec/unit/compiling/gisele2gts/test_on_par_st.rb +51 -0
- data/spec/unit/compiling/gisele2gts/test_on_seq_st.rb +46 -0
- data/spec/unit/compiling/gisele2gts/test_on_task_call_st.rb +37 -0
- data/spec/unit/compiling/gisele2gts/test_on_task_def.rb +49 -0
- data/spec/unit/compiling/gisele2gts/test_on_unit_def.rb +35 -0
- data/spec/unit/compiling/gts2bytecode/test_on_end.rb +31 -0
- data/spec/unit/compiling/gts2bytecode/test_on_event.rb +37 -0
- data/spec/unit/compiling/gts2bytecode/test_on_fork.rb +41 -0
- data/spec/unit/compiling/gts2bytecode/test_on_join.rb +42 -0
- data/spec/unit/compiling/gts2bytecode/test_on_listen.rb +36 -0
- data/spec/unit/compiling/gts2bytecode/test_on_nop.rb +30 -0
- data/spec/unit/component/test_component_name.rb +16 -0
- data/spec/unit/component/test_logging.rb +36 -0
- data/spec/unit/enacter/test_component.rb +11 -0
- data/spec/unit/event/test_to_s.rb +12 -0
- data/spec/unit/event_manager/test_component.rb +9 -0
- data/spec/unit/event_manager/test_subscribe.rb +40 -0
- data/spec/unit/event_manager/test_unsubscribe.rb +39 -0
- data/spec/unit/kernel/macros/test_fork.rb +37 -0
- data/spec/unit/kernel/macros/test_join.rb +43 -0
- data/spec/unit/kernel/macros/test_listen.rb +37 -0
- data/spec/unit/kernel/macros/test_notify.rb +57 -0
- data/spec/unit/kernel/macros/test_react.rb +47 -0
- data/spec/unit/kernel/macros/test_schedule_at.rb +30 -0
- data/spec/unit/kernel/opcodes/test_op_del.rb +42 -0
- data/spec/unit/kernel/opcodes/test_op_event.rb +25 -0
- data/spec/unit/kernel/opcodes/test_op_fetch.rb +27 -0
- data/spec/unit/kernel/opcodes/test_op_flip.rb +17 -0
- data/spec/unit/kernel/opcodes/test_op_fold.rb +29 -0
- data/spec/unit/kernel/opcodes/test_op_fork.rb +63 -0
- data/spec/unit/kernel/opcodes/test_op_forka.rb +51 -0
- data/spec/unit/kernel/opcodes/test_op_get.rb +62 -0
- data/spec/unit/kernel/opcodes/test_op_getr.rb +48 -0
- data/spec/unit/kernel/opcodes/test_op_ifenil.rb +41 -0
- data/spec/unit/kernel/opcodes/test_op_ifezero.rb +32 -0
- data/spec/unit/kernel/opcodes/test_op_invoke.rb +34 -0
- data/spec/unit/kernel/opcodes/test_op_nop.rb +18 -0
- data/spec/unit/kernel/opcodes/test_op_parent.rb +39 -0
- data/spec/unit/kernel/opcodes/test_op_pop.rb +22 -0
- data/spec/unit/kernel/opcodes/test_op_push.rb +17 -0
- data/spec/unit/kernel/opcodes/test_op_save.rb +32 -0
- data/spec/unit/kernel/opcodes/test_op_savea.rb +34 -0
- data/spec/unit/kernel/opcodes/test_op_self.rb +20 -0
- data/spec/unit/kernel/opcodes/test_op_send.rb +20 -0
- data/spec/unit/kernel/opcodes/test_op_set.rb +61 -0
- data/spec/unit/kernel/opcodes/test_op_then.rb +50 -0
- data/spec/unit/kernel/opcodes/test_op_unfold.rb +22 -0
- data/spec/unit/kernel/opcodes/test_op_uuid.rb +16 -0
- data/spec/unit/kernel/runner/test_pop.rb +26 -0
- data/spec/unit/kernel/runner/test_stack.rb +28 -0
- data/spec/unit/kernel/test_progress.rb +47 -0
- data/spec/unit/kernel/test_resume.rb +53 -0
- data/spec/unit/kernel/test_start.rb +36 -0
- data/spec/unit/prog/test_to_hash.rb +29 -0
- data/spec/unit/prog/test_waitlist_eq.rb +20 -0
- data/spec/unit/prog_list/memory/test_component.rb +9 -0
- data/spec/unit/prog_list/memory/test_fetch.rb +40 -0
- data/spec/unit/prog_list/memory/test_pick.rb +39 -0
- data/spec/unit/prog_list/memory/test_save.rb +91 -0
- data/spec/unit/prog_list/memory/test_to_relation.rb +17 -0
- data/spec/unit/prog_list/sqldb/test_component.rb +11 -0
- data/spec/unit/prog_list/sqldb/test_connect.rb +46 -0
- data/spec/unit/prog_list/test_memory.rb +9 -0
- data/spec/unit/prog_list/test_sqldb.rb +13 -0
- data/spec/unit/prog_list/test_storage.rb +51 -0
- data/spec/unit/registry/test_component.rb +9 -0
- data/spec/unit/registry/test_connect.rb +53 -0
- data/spec/unit/registry/test_disconnect.rb +51 -0
- data/spec/unit/registry/test_registration.rb +44 -0
- data/spec/unit/shared/a_component.rb +49 -0
- data/spec/unit/shared/a_storage.rb +114 -0
- data/spec/unit/test_logging.rb +46 -0
- data/spec/unit/test_prog.rb +57 -0
- data/spec/unit/test_prog_list.rb +22 -0
- data/spec/unit/vm/test_event_facace.rb +11 -0
- data/spec/unit/vm/test_initialize.rb +59 -0
- data/spec/unit/vm/test_proglist_facade.rb +21 -0
- data/tasks/debug_mail.rake +75 -0
- data/tasks/debug_mail.txt +13 -0
- data/tasks/gem.rake +73 -0
- data/tasks/spec_test.rake +71 -0
- data/tasks/unit_test.rake +76 -0
- data/tasks/yard.rake +51 -0
- 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,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
|