erlectricity 0.1.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 (42) hide show
  1. data/Manifest.txt +41 -0
  2. data/README.txt +3 -0
  3. data/Rakefile +68 -0
  4. data/examples/gruff/gruff.erl +62 -0
  5. data/examples/gruff/gruff_provider.rb +38 -0
  6. data/examples/gruff/gruff_run.erl +17 -0
  7. data/examples/gruff/stat_run.erl +18 -0
  8. data/examples/gruff/stat_writer.erl +40 -0
  9. data/examples/tinderl/tinderl.erl +41 -0
  10. data/examples/tinderl/tinderl.rb +23 -0
  11. data/lib/erlectricity/condition.rb +48 -0
  12. data/lib/erlectricity/conditions/hash.rb +15 -0
  13. data/lib/erlectricity/conditions/static.rb +17 -0
  14. data/lib/erlectricity/conditions/type.rb +19 -0
  15. data/lib/erlectricity/constants.rb +37 -0
  16. data/lib/erlectricity/decoder.rb +202 -0
  17. data/lib/erlectricity/encoder.rb +127 -0
  18. data/lib/erlectricity/errors/decode_error.rb +3 -0
  19. data/lib/erlectricity/errors/encode_error.rb +3 -0
  20. data/lib/erlectricity/errors/erlectricity_error.rb +3 -0
  21. data/lib/erlectricity/match_context.rb +20 -0
  22. data/lib/erlectricity/matcher.rb +60 -0
  23. data/lib/erlectricity/port.rb +46 -0
  24. data/lib/erlectricity/receiver.rb +74 -0
  25. data/lib/erlectricity/types/function.rb +3 -0
  26. data/lib/erlectricity/types/new_function.rb +3 -0
  27. data/lib/erlectricity/types/new_reference.rb +3 -0
  28. data/lib/erlectricity/types/pid.rb +3 -0
  29. data/lib/erlectricity/types/reference.rb +3 -0
  30. data/lib/erlectricity/version.rb +9 -0
  31. data/lib/erlectricity.rb +23 -0
  32. data/setup.rb +1585 -0
  33. data/test/condition_spec.rb +78 -0
  34. data/test/decode_spec.rb +133 -0
  35. data/test/encode_spec.rb +125 -0
  36. data/test/matcher_spec.rb +73 -0
  37. data/test/port_spec.rb +35 -0
  38. data/test/receiver_spec.rb +105 -0
  39. data/test/spec_suite.rb +2 -0
  40. data/test/test_erlectricity.rb +2 -0
  41. data/test/test_helper.rb +36 -0
  42. metadata +87 -0
@@ -0,0 +1,127 @@
1
+ module Erlectricity
2
+ class Encoder
3
+ include Erlectricity::External::Types
4
+ attr_accessor :out
5
+ def initialize(out)
6
+ self.out = out
7
+ end
8
+
9
+ def write_any obj
10
+ write_1 Erlectricity::External::VERSION
11
+ write_any_raw obj
12
+
13
+ end
14
+
15
+ def write_any_raw obj
16
+ case obj
17
+ when Symbol then write_symbol(obj)
18
+ when Fixnum, Bignum then write_fixnum(obj)
19
+ when Float then write_float(obj)
20
+ when Erlectricity::NewReference then write_new_reference(obj)
21
+ when Erlectricity::Pid then write_pid(obj)
22
+ when Array then write_tuple(obj)
23
+ when String then write_binary(obj)
24
+ else
25
+ fail(obj)
26
+ end
27
+ end
28
+
29
+ def write_1(byte)
30
+ out.write([byte].pack("C"))
31
+ end
32
+
33
+ def write_2(short)
34
+ out.write([short].pack("n"))
35
+ end
36
+
37
+ def write_4(long)
38
+ out.write([long].pack("N"))
39
+ end
40
+
41
+ def write_string(string)
42
+ out.write(string)
43
+ end
44
+
45
+ def write_symbol(sym)
46
+ fail(sym) unless sym.is_a? Symbol
47
+ data = sym.to_s
48
+ write_1 ATOM
49
+ write_2 data.length
50
+ write_string data
51
+ end
52
+
53
+ def write_fixnum(num)
54
+ if num >= 0 && num < 256
55
+ write_1 SMALL_INT
56
+ write_1 num
57
+ elsif num <= Erlectricity::External::MAX_INT && num >= Erlectricity::External::MIN_INT
58
+ write_1 INT
59
+ write_4 num
60
+ else
61
+ write_bignum num
62
+ end
63
+ end
64
+
65
+ def write_float(float)
66
+ write_1 FLOAT
67
+ write_string format("%15.15e", float).ljust(31, "\000")
68
+ end
69
+
70
+ def write_bignum(num)
71
+ fail(num)
72
+ end
73
+
74
+ def write_new_reference(ref)
75
+ fail(ref) unless ref.is_a? Erlectricity::NewReference
76
+ write_1 NEW_REF
77
+ write_2 ref.id.length
78
+ write_symbol(ref.node)
79
+ write_1 ref.creation
80
+ write_string ref.id.pack('N' * ref.id.length)
81
+ end
82
+
83
+ def write_pid(pid)
84
+ fail(pid) unless pid.is_a? Erlectricity::Pid
85
+ write_1 PID
86
+ write_symbol(pid.node)
87
+ write_4 pid.id
88
+ write_4 pid.serial
89
+ write_1 pid.creation
90
+ end
91
+
92
+ def write_tuple(data)
93
+ fail(data) unless data.is_a? Array
94
+ if data.length < 256
95
+ write_1 SMALL_TUPLE
96
+ write_1 data.length
97
+ else
98
+ write_1 LARGE_TUPLE
99
+ write_4 data.length
100
+ end
101
+
102
+ data.each{|e| write_any_raw e }
103
+ end
104
+
105
+ def write_list(data)
106
+ fail(data) unless data.is_a? Array
107
+ write_1 NIL and return if data.empty?
108
+
109
+ #NOTE: we do not ever encode as the string format.
110
+ write_1 LIST
111
+ write_4 data.length
112
+ data.each{|e| write_any_raw e }
113
+ write_1 NIL
114
+ end
115
+
116
+ def write_binary(data)
117
+ write_1 BIN
118
+ write_4 data.length
119
+ write_string data
120
+ end
121
+
122
+ private
123
+ def fail(obj)
124
+ raise EncodeError, "Cannot encode to erlang external format: #{obj.inspect}"
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,3 @@
1
+ class DecodeError < ErlectricityError
2
+
3
+ end
@@ -0,0 +1,3 @@
1
+ class EncodeError < ErlectricityError
2
+
3
+ end
@@ -0,0 +1,3 @@
1
+ class ErlectricityError < StandardError
2
+
3
+ end
@@ -0,0 +1,20 @@
1
+ module Erlectricity
2
+ class MatchContext
3
+ attr_accessor :receiver
4
+ def initialize(receiver)
5
+ self.receiver = receiver
6
+ end
7
+
8
+ def receive(&block)
9
+ receiver.receive(&block)
10
+ end
11
+
12
+ def receive_loop
13
+ receiver.receive_loop
14
+ end
15
+
16
+ def send!(*term)
17
+ receiver.send!(*term)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,60 @@
1
+ module Erlectricity
2
+ class Matcher
3
+ attr_accessor :condition, :block
4
+ attr_accessor :receiver
5
+
6
+ def initialize(parent, condition, block)
7
+ self.receiver = parent
8
+ @block = block
9
+ @condition = condition
10
+ end
11
+
12
+ def run(arg)
13
+ context = MatchContext.new(self.receiver)
14
+
15
+ populate_context context, arg
16
+ context.instance_eval &block
17
+ end
18
+
19
+ def matches?(arg)
20
+ if @condition.is_a?(Array)
21
+ return false unless arg.is_a?(Array)
22
+ return false if @condition.length != arg.length
23
+ @condition.zip(arg).all?{|l,r| l.satisfies?(r)}
24
+ else
25
+ @condition.satisfies?(arg)
26
+ end
27
+ end
28
+
29
+
30
+ private
31
+ def populate_context(context, arg)
32
+ if @condition.is_a?(Array) && arg.is_a?(Array)
33
+ @condition.zip(arg).all?{|l,r| set_binding(context, l, r)}
34
+ else
35
+ set_binding(context, condition, arg)
36
+ end
37
+ end
38
+
39
+ def set_binding(context, condition, arg)
40
+ condition.bindings_for(arg).each do |k, v|
41
+ add_to_context(context, k, v)
42
+ end
43
+ end
44
+
45
+ def add_to_context(context, name, value)
46
+ return if name.nil?
47
+
48
+ context.instance_eval <<-EOS
49
+ def #{name}
50
+ @#{name}
51
+ end
52
+ def #{name}= (value)
53
+ @#{name} = value
54
+ end
55
+ EOS
56
+
57
+ context.send(:"#{name}=", value)
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,46 @@
1
+ module Erlectricity
2
+ class Port
3
+ attr_reader :input, :output
4
+ attr_reader :skipped
5
+ attr_reader :queue
6
+
7
+ def initialize(input=STDIN, output=STDOUT)
8
+ @input = input
9
+ @output = output
10
+
11
+ input.sync = true
12
+ output.sync = true
13
+
14
+ @encoder = Erlectricity::Encoder.new(nil)
15
+ @skipped = []
16
+ @queue = []
17
+ end
18
+
19
+ def receive
20
+ queue.empty? ? read_from_input : queue.shift
21
+ end
22
+
23
+ def send(term)
24
+ @encoder.out = StringIO.new('', 'w')
25
+ @encoder.write_any(term)
26
+ data = @encoder.out.string
27
+ output.write([data.length].pack("N"))
28
+ output.write data
29
+ end
30
+
31
+ def restore_skipped
32
+ @queue = self.skipped + self.queue
33
+ end
34
+
35
+ private
36
+ def read_from_input
37
+ raw = input.read(4)
38
+ return nil unless raw
39
+
40
+ packet_length = raw.unpack('N').first
41
+ data = input.read(packet_length)
42
+ result = Erlectricity::Decoder.read_any_from(data)
43
+ result
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,74 @@
1
+ module Erlectricity
2
+ class Receiver
3
+ include Conditions
4
+
5
+ attr_accessor :port
6
+ attr_accessor :parent
7
+ attr_accessor :matchers
8
+
9
+ RECEIVE_LOOP = Object.new
10
+ NO_MATCH = Object.new
11
+
12
+ def initialize(port, parent=nil, &block)
13
+ @port = port
14
+ @parent = parent
15
+ @matchers = []
16
+ instance_eval &block if block
17
+ end
18
+
19
+ def process(arg)
20
+ matcher = @matchers.find{|r| r.matches? arg}
21
+
22
+ if(matcher)
23
+ port.restore_skipped
24
+ matcher.run arg
25
+ else
26
+ NO_MATCH
27
+ end
28
+ end
29
+
30
+ def match(*args, &block)
31
+ args = args.map{|a| a.is_a?(Condition) ? a : StaticCondition.new(a)}
32
+
33
+ args = args.first if args.length == 1
34
+ @matchers << Matcher.new(self, args, block)
35
+ end
36
+
37
+ def run
38
+
39
+ loop do
40
+ msg = port.receive
41
+ return if msg.nil?
42
+
43
+ case result = process(msg)
44
+ when RECEIVE_LOOP then next
45
+ when NO_MATCH
46
+ port.skipped << msg
47
+ next
48
+ else
49
+ break result
50
+ end
51
+ end
52
+ end
53
+
54
+ def receive(&block)
55
+ Receiver.new(port, self, &block).run
56
+ end
57
+
58
+ def receive_loop
59
+ RECEIVE_LOOP
60
+ end
61
+
62
+ def send!(*term)
63
+ term = term.first if term.length == 1
64
+ port.send(term)
65
+ end
66
+ end
67
+ end
68
+
69
+
70
+ module Kernel
71
+ def receive(input=STDIN, output=STDOUT, &block)
72
+ Erlectricity::Receiver.new(Erlectricity::Port.new(input, output), nil, &block).run
73
+ end
74
+ end
@@ -0,0 +1,3 @@
1
+ module Erlectricity
2
+ Function = Struct.new :pid, :module, :index, :uniq, :free_vars
3
+ end
@@ -0,0 +1,3 @@
1
+ module Erlectricity
2
+ NewFunction = Struct.new :arity, :uniq, :index,:num_free, :module, :old_index, :old_uniq, :pid, :free_vars
3
+ end
@@ -0,0 +1,3 @@
1
+ module Erlectricity
2
+ NewReference = Struct.new :node, :creation, :id
3
+ end
@@ -0,0 +1,3 @@
1
+ module Erlectricity
2
+ Pid = Struct.new :node, :id, :serial, :creation
3
+ end
@@ -0,0 +1,3 @@
1
+ module Erlectricity
2
+ Reference = Struct.new :node, :id, :creator
3
+ end
@@ -0,0 +1,9 @@
1
+ module Erlectricity #:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 1
5
+ TINY = 0
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
@@ -0,0 +1,23 @@
1
+ require 'erlectricity/constants'
2
+
3
+ require 'erlectricity/types/new_reference'
4
+ require 'erlectricity/types/pid'
5
+ require 'erlectricity/types/function'
6
+
7
+ require 'erlectricity/decoder'
8
+ require 'erlectricity/encoder'
9
+
10
+ require 'erlectricity/port'
11
+ require 'erlectricity/matcher'
12
+ require 'erlectricity/match_context'
13
+
14
+ require 'erlectricity/condition'
15
+ require 'erlectricity/conditions/hash'
16
+ require 'erlectricity/conditions/static'
17
+ require 'erlectricity/conditions/type'
18
+
19
+ require 'erlectricity/receiver'
20
+
21
+ require 'erlectricity/errors/erlectricity_error'
22
+ require 'erlectricity/errors/decode_error'
23
+ require 'erlectricity/errors/encode_error'