gisele-vm 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|