erlang-terms 1.1.0 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.coveralls.yml +1 -0
  3. data/.editorconfig +20 -0
  4. data/.gitignore +10 -18
  5. data/.ruby-gemset +1 -0
  6. data/.ruby-version +1 -0
  7. data/.travis.yml +15 -3
  8. data/.yardopts +6 -0
  9. data/CHANGELOG.md +9 -0
  10. data/Gemfile +21 -1
  11. data/LICENSE.txt +1 -1
  12. data/README.md +95 -17
  13. data/Rakefile +8 -3
  14. data/erlang-terms.gemspec +14 -11
  15. data/lib/erlang-terms.rb +1 -0
  16. data/lib/erlang/associable.rb +98 -0
  17. data/lib/erlang/atom.rb +257 -0
  18. data/lib/erlang/binary.rb +425 -0
  19. data/lib/erlang/bitstring.rb +464 -0
  20. data/lib/erlang/cons.rb +122 -0
  21. data/lib/erlang/enumerable.rb +160 -0
  22. data/lib/erlang/error.rb +4 -0
  23. data/lib/erlang/export.rb +110 -12
  24. data/lib/erlang/float.rb +201 -0
  25. data/lib/erlang/function.rb +259 -0
  26. data/lib/erlang/immutable.rb +101 -0
  27. data/lib/erlang/list.rb +1685 -24
  28. data/lib/erlang/map.rb +935 -21
  29. data/lib/erlang/nil.rb +73 -10
  30. data/lib/erlang/pid.rb +120 -18
  31. data/lib/erlang/port.rb +123 -0
  32. data/lib/erlang/reference.rb +161 -0
  33. data/lib/erlang/string.rb +175 -3
  34. data/lib/erlang/term.rb +24 -0
  35. data/lib/erlang/terms.rb +324 -8
  36. data/lib/erlang/terms/version.rb +1 -1
  37. data/lib/erlang/trie.rb +364 -0
  38. data/lib/erlang/tuple.rb +1582 -14
  39. data/lib/erlang/undefined.rb +32 -0
  40. metadata +49 -71
  41. data/spec/erlang/export_spec.rb +0 -17
  42. data/spec/erlang/list_spec.rb +0 -39
  43. data/spec/erlang/map_spec.rb +0 -24
  44. data/spec/erlang/nil_spec.rb +0 -18
  45. data/spec/erlang/pid_spec.rb +0 -21
  46. data/spec/erlang/string_spec.rb +0 -11
  47. data/spec/erlang/terms_spec.rb +0 -7
  48. data/spec/erlang/tuple_spec.rb +0 -20
  49. data/spec/spec_helper.rb +0 -7
@@ -1,15 +1,78 @@
1
+ require 'singleton'
2
+
1
3
  module Erlang
2
- class Nil
3
- def inspect
4
- "#<#{self.class.name} []>"
4
+ # A list without any elements. This is a singleton, since all empty lists are equivalent.
5
+ #
6
+ # Licensing
7
+ # =========
8
+ #
9
+ # Portions taken and modified from https://github.com/hamstergem/hamster
10
+ #
11
+ # Copyright (c) 2009-2014 Simon Harris
12
+ #
13
+ # Permission is hereby granted, free of charge, to any person obtaining
14
+ # a copy of this software and associated documentation files (the
15
+ # "Software"), to deal in the Software without restriction, including
16
+ # without limitation the rights to use, copy, modify, merge, publish,
17
+ # distribute, sublicense, and/or sell copies of the Software, and to
18
+ # permit persons to whom the Software is furnished to do so, subject to
19
+ # the following conditions:
20
+ #
21
+ # The above copyright notice and this permission notice shall be
22
+ # included in all copies or substantial portions of the Software.
23
+ #
24
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31
+ #
32
+ # @private
33
+ class EmptyList
34
+ include Singleton
35
+ include Erlang::Term
36
+ include Erlang::List
37
+ include Erlang::Immutable
38
+
39
+ # @private
40
+ def hash
41
+ return Erlang::EmptyList.hash
42
+ end
43
+
44
+ # There is no first item in an empty list, so return `Undefined`.
45
+ # @return [Undefined]
46
+ def head
47
+ return Erlang::Undefined
48
+ end
49
+ alias :first :head
50
+
51
+ # There are no subsequent elements, so return an empty list.
52
+ # @return [self]
53
+ def tail
54
+ return self
5
55
  end
6
56
 
7
- def ==(other)
8
- if other == []
9
- true
10
- else
11
- other === self.class
12
- end
57
+ def empty?
58
+ return true
13
59
  end
60
+
61
+ def improper?
62
+ return false
63
+ end
64
+
65
+ # Return the number of items in this `List`.
66
+ # @return [Integer]
67
+ def size
68
+ return 0
69
+ end
70
+ alias :length :size
14
71
  end
15
- end
72
+
73
+ # A list without any elements. This is a singleton, since all empty lists are equivalent.
74
+ Nil = EmptyList.instance
75
+
76
+ # @private
77
+ EmptyList.freeze
78
+ end
@@ -1,31 +1,133 @@
1
1
  module Erlang
2
+ # A `Pid` is a process identifier object obtained from [`erlang:spawn/3`](http://erlang.org/doc/man/erlang.html#spawn-3).
3
+ #
4
+ # ### Creating Pids
5
+ #
6
+ # Erlang::Pid["nonode@nohost", 38, 0, 0]
7
+ # # => Erlang::Pid[:"nonode@nohost", 38, 0, 0]
8
+ #
2
9
  class Pid
3
- attr_accessor :node, :id, :serial, :creation
10
+ include Erlang::Term
11
+ include Erlang::Immutable
4
12
 
13
+ # Return the node for this `Pid`
14
+ # @return [Atom]
15
+ attr_reader :node
16
+
17
+ # Return the id for this `Pid`
18
+ # @return [Integer]
19
+ attr_reader :id
20
+
21
+ # Return the serial for this `Pid`
22
+ # @return [Integer]
23
+ attr_reader :serial
24
+
25
+ # Return the creation for this `Pid`
26
+ # @return [Integer]
27
+ attr_reader :creation
28
+
29
+ class << self
30
+ # Create a new `Pid` populated with the given `node`, `id`, `serial`, and `creation`.
31
+ # @param node [Atom, Symbol] The node atom
32
+ # @param id [Integer] The id as a non-negative integer
33
+ # @param serial [Integer] The serial time as a non-negative integer
34
+ # @param creation [Integer] The creation time as a non-negative integer
35
+ # @return [Pid]
36
+ # @raise [ArgumentError] if `node` is not an `Atom` or one of `id`, `serial`, or `creation` are not non-negative `Integer`s
37
+ def [](node, id, serial = 0, creation = 0)
38
+ return new(node, id, serial, creation)
39
+ end
40
+
41
+ # Compares `a` and `b` and returns whether they are less than,
42
+ # equal to, or greater than each other.
43
+ #
44
+ # @param a [Pid] The left argument
45
+ # @param b [Pid] The right argument
46
+ # @return [-1, 0, 1]
47
+ # @raise [ArgumentError] if `a` or `b` is not a `Pid`
48
+ def compare(a, b)
49
+ raise ArgumentError, "'a' must be of Erlang::Pid type" unless a.kind_of?(Erlang::Pid)
50
+ raise ArgumentError, "'b' must be of Erlang::Pid type" unless b.kind_of?(Erlang::Pid)
51
+ c = Erlang.compare(a.node, b.node)
52
+ return c if c != 0
53
+ c = Erlang.compare(a.id, b.id)
54
+ return c if c != 0
55
+ c = Erlang.compare(a.serial, b.serial)
56
+ return c if c != 0
57
+ c = Erlang.compare(a.creation, b.creation)
58
+ return c
59
+ end
60
+ end
61
+
62
+ # @private
5
63
  def initialize(node, id, serial = 0, creation = 0)
6
- self.node = node
7
- self.id = id
8
- self.serial = serial
9
- self.creation = creation
64
+ raise ArgumentError, 'id must be a non-negative Integer' if not id.is_a?(::Integer) or id < 0
65
+ raise ArgumentError, 'serial must be a non-negative Integer' if not serial.is_a?(::Integer) or serial < 0
66
+ raise ArgumentError, 'creation must be a non-negative Integer' if not creation.is_a?(::Integer) or creation < 0
67
+ @node = Erlang::Atom[node]
68
+ @id = id.freeze
69
+ @serial = serial.freeze
70
+ @creation = creation.freeze
71
+ end
72
+
73
+ # @private
74
+ def hash
75
+ state = [@node, @id, @serial, @creation]
76
+ return state.reduce(Erlang::Pid.hash) { |acc, item| (acc << 5) - acc + item.hash }
77
+ end
78
+
79
+ # Return true if `other` has the same type and contents as this `Pid`.
80
+ #
81
+ # @param other [Object] The object to compare with
82
+ # @return [Boolean]
83
+ def eql?(other)
84
+ return true if other.equal?(self)
85
+ if instance_of?(other.class)
86
+ return !!(node == other.node &&
87
+ id == other.id &&
88
+ serial == other.serial &&
89
+ creation == other.creation)
90
+ else
91
+ return !!(Erlang.compare(other, self) == 0)
92
+ end
93
+ end
94
+ alias :== :eql?
95
+
96
+ # Return the contents of this `Pid` as a Erlang-readable `::String`.
97
+ #
98
+ # @example
99
+ # Erlang::Pid["nonode@nohost", 38, 0, 0].erlang_inspect
100
+ # # => "{'pid','nonode@nohost',38,0,0}"
101
+ #
102
+ # @return [::String]
103
+ def erlang_inspect(raw = false)
104
+ if raw == true and Erlang.respond_to?(:term_to_binary)
105
+ result = 'erlang:binary_to_term('
106
+ result << Erlang.inspect(Erlang.term_to_binary(self), raw: raw)
107
+ result << ')'
108
+ return result
109
+ else
110
+ return Erlang.inspect(Erlang::Tuple[:pid, node, id, serial, creation], raw: raw)
111
+ end
10
112
  end
11
113
 
12
- # @return [String] the nicely formatted version of the message
114
+ # @return [::String] the nicely formatted version of the `Pid`.
13
115
  def inspect
14
- "#<#{self.class.name} <0.#{id}.#{serial}> @node=#{node.inspect} @creation=#{creation.inspect}>"
116
+ return "Erlang::Pid[#{node.inspect}, #{id.inspect}, #{serial.inspect}, #{creation.inspect}]"
15
117
  end
16
118
 
17
- def pretty_inspect
18
- "#<#{self.class.name} <0.#{id}.#{serial}>\n" <<
19
- " @node=#{node.inspect}\n" <<
20
- " @creation=#{creation.inspect}>"
119
+ # @return [::Array]
120
+ # @private
121
+ def marshal_dump
122
+ return [@node, @id, @serial, @creation]
21
123
  end
22
124
 
23
- def ==(other)
24
- self.class === other &&
25
- node.to_s == other.node.to_s &&
26
- id.to_s == other.id.to_s &&
27
- serial == other.serial &&
28
- creation == other.creation
125
+ # @private
126
+ def marshal_load(args)
127
+ node, id, serial, creation = args
128
+ initialize(node, id, serial, creation)
129
+ __send__(:immutable!)
130
+ return self
29
131
  end
30
132
  end
31
- end
133
+ end
@@ -0,0 +1,123 @@
1
+ module Erlang
2
+ # A `Port` is a port object obtained from [`erlang:open_port/2`](http://erlang.org/doc/man/erlang.html#open_port-2).
3
+ #
4
+ # ### Creating Ports
5
+ #
6
+ # Erlang::Port["nonode@nohost", 100, 1]
7
+ # # => Erlang::Port[:"nonode@nohost", 100, 1]
8
+ #
9
+ class Port
10
+ include Erlang::Term
11
+ include Erlang::Immutable
12
+
13
+ # Return the node for this `Port`
14
+ # @return [Atom]
15
+ attr_reader :node
16
+
17
+ # Return the id for this `Port`
18
+ # @return [Integer]
19
+ attr_reader :id
20
+
21
+ # Return the creation for this `Port`
22
+ # @return [Integer]
23
+ attr_reader :creation
24
+
25
+ class << self
26
+ # Create a new `Port` populated with the given `node`, `id`, and `creation`.
27
+ # @param node [Atom, Symbol] The node atom
28
+ # @param id [Integer] The id as a non-negative integer
29
+ # @param creation [Integer] The creation time as a non-negative integer
30
+ # @return [Port]
31
+ # @raise [ArgumentError] if `node` is not an `Atom` or `id` or `creation` are not non-negative `Integer`s
32
+ def [](node, id, creation = 0)
33
+ return new(node, id, creation)
34
+ end
35
+
36
+ # Compares `a` and `b` and returns whether they are less than,
37
+ # equal to, or greater than each other.
38
+ #
39
+ # @param a [Port] The left argument
40
+ # @param b [Port] The right argument
41
+ # @return [-1, 0, 1]
42
+ # @raise [ArgumentError] if `a` or `b` is not a `Port`
43
+ def compare(a, b)
44
+ raise ArgumentError, "'a' must be of Erlang::Port type" unless a.kind_of?(Erlang::Port)
45
+ raise ArgumentError, "'b' must be of Erlang::Port type" unless b.kind_of?(Erlang::Port)
46
+ c = Erlang.compare(a.node, b.node)
47
+ return c if c != 0
48
+ c = Erlang.compare(a.id, b.id)
49
+ return c if c != 0
50
+ c = Erlang.compare(a.creation, b.creation)
51
+ return c
52
+ end
53
+ end
54
+
55
+ # @private
56
+ def initialize(node, id, creation = 0)
57
+ raise ArgumentError, 'id must be a non-negative Integer' if not id.is_a?(::Integer) or id < 0
58
+ raise ArgumentError, 'creation must be a non-negative Integer' if not creation.is_a?(::Integer) or creation < 0
59
+ @node = Erlang::Atom[node]
60
+ @id = id.freeze
61
+ @creation = creation.freeze
62
+ end
63
+
64
+ # @private
65
+ def hash
66
+ state = [@node, @id, @creation]
67
+ return state.reduce(Erlang::Port.hash) { |acc, item| (acc << 5) - acc + item.hash }
68
+ end
69
+
70
+ # Return true if `other` has the same type and contents as this `Port`.
71
+ #
72
+ # @param other [Object] The object to compare with
73
+ # @return [Boolean]
74
+ def eql?(other)
75
+ return true if other.equal?(self)
76
+ if instance_of?(other.class)
77
+ return !!(node == other.node &&
78
+ id == other.id &&
79
+ creation == other.creation)
80
+ else
81
+ return !!(Erlang.compare(other, self) == 0)
82
+ end
83
+ end
84
+ alias :== :eql?
85
+
86
+ # Return the contents of this `Port` as a Erlang-readable `::String`.
87
+ #
88
+ # @example
89
+ # Erlang::Port["nonode@nohost", 100, 1].erlang_inspect
90
+ # # => "{'port','nonode@nohost',100,1}"
91
+ #
92
+ # @return [::String]
93
+ def erlang_inspect(raw = false)
94
+ if raw == true and Erlang.respond_to?(:term_to_binary)
95
+ result = 'erlang:binary_to_term('
96
+ result << Erlang.inspect(Erlang.term_to_binary(self), raw: raw)
97
+ result << ')'
98
+ return result
99
+ else
100
+ return Erlang.inspect(Erlang::Tuple[:port, node, id, creation], raw: raw)
101
+ end
102
+ end
103
+
104
+ # @return [::String] the nicely formatted version of the `Port`.
105
+ def inspect
106
+ return "Erlang::Port[#{node.inspect}, #{id.inspect}, #{creation.inspect}]"
107
+ end
108
+
109
+ # @return [::Array]
110
+ # @private
111
+ def marshal_dump
112
+ return [@node, @id, @creation]
113
+ end
114
+
115
+ # @private
116
+ def marshal_load(args)
117
+ node, id, creation = args
118
+ initialize(node, id, creation)
119
+ __send__(:immutable!)
120
+ return self
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,161 @@
1
+ module Erlang
2
+ # @private
3
+ class NewReferenceError < Erlang::Error; end
4
+
5
+ # A `Reference` is an [unique reference](http://erlang.org/doc/efficiency_guide/advanced.html#unique_references).
6
+ #
7
+ # ### Creating New References
8
+ #
9
+ # # New reference
10
+ # Erlang::Reference["nonode@nohost", 0, [0, 0, 0]]
11
+ # # => Erlang::Reference[:"nonode@nohost", 0, [0, 0, 0]]
12
+ # # Old reference
13
+ # Erlang::Reference["nonode@nohost", 0, 0]
14
+ # # => Erlang::Reference[:"nonode@nohost", 0, 0]
15
+ #
16
+ class Reference
17
+ include Erlang::Term
18
+ include Erlang::Immutable
19
+
20
+ # Return the node for this `Reference`
21
+ # @return [Atom]
22
+ attr_reader :node
23
+
24
+ # Return the creation for this `Reference`
25
+ # @return [Integer]
26
+ attr_reader :creation
27
+
28
+ # Return the ids for this `Reference`
29
+ # @return [[Integer]]
30
+ attr_reader :ids
31
+
32
+ class << self
33
+ # Create a new `Reference` populated with the given node, creation, and id(s).
34
+ # @param node [Atom, Symbol] The node atom
35
+ # @param creation [Integer] The creation time as a non-negative integer
36
+ # @param ids [Integer] The ids as a `List` of non-negative integers
37
+ # @return [Reference]
38
+ # @raise [ArgumentError] if `node` is not an `Atom` or `creation` or `ids` are not non-negative `Integer`s
39
+ def [](node, creation, ids)
40
+ return new(node, creation, ids)
41
+ end
42
+
43
+ # Compares `a` and `b` and returns whether they are less than,
44
+ # equal to, or greater than each other.
45
+ #
46
+ # @param a [Reference] The left argument
47
+ # @param b [Reference] The right argument
48
+ # @return [-1, 0, 1]
49
+ # @raise [ArgumentError] if `a` or `b` is not a `Reference`
50
+ def compare(a, b)
51
+ raise ArgumentError, "'a' must be of Erlang::Reference type" unless a.kind_of?(Erlang::Reference)
52
+ raise ArgumentError, "'b' must be of Erlang::Reference type" unless b.kind_of?(Erlang::Reference)
53
+ c = Erlang.compare(a.node, b.node)
54
+ return c if c != 0
55
+ c = Erlang.compare(a.creation, b.creation)
56
+ return c if c != 0
57
+ c = Erlang.compare(a.ids, b.ids)
58
+ return c
59
+ end
60
+ end
61
+
62
+ # @private
63
+ def initialize(node, creation, ids)
64
+ raise ArgumentError, 'creation must be a non-negative Integer' if not creation.is_a?(::Integer) or creation < 0
65
+ ids = Erlang.from(ids)
66
+ if Erlang.is_list(ids)
67
+ raise ArgumentError, 'ids list cannot be empty' if ids.empty?
68
+ raise ArgumentError, 'ids must be a List of non-negative Integer' if ids.any? { |id| !id.is_a?(::Integer) or id < 0 }
69
+ @node = Erlang::Atom[node]
70
+ @creation = creation.freeze
71
+ @ids = ids
72
+ else
73
+ id = ids
74
+ raise ArgumentError, 'id must be a non-negative Integer' if not id.is_a?(::Integer) or id < 0
75
+ @node = Erlang::Atom[node]
76
+ @creation = creation.freeze
77
+ @ids = id.freeze
78
+ end
79
+ end
80
+
81
+ # @private
82
+ def hash
83
+ state = [@node, @creation, @ids]
84
+ return state.reduce(Erlang::Reference.hash) { |acc, item| (acc << 5) - acc + item.hash }
85
+ end
86
+
87
+ # Return true if `other` has the same type and contents as this `Reference`.
88
+ #
89
+ # @param other [Object] The object to compare with
90
+ # @return [Boolean]
91
+ def eql?(other)
92
+ return true if other.equal?(self)
93
+ if instance_of?(other.class)
94
+ return !!(@node == other.node &&
95
+ @creation == other.creation &&
96
+ @ids == other.ids)
97
+ else
98
+ return !!(Erlang.compare(other, self) == 0)
99
+ end
100
+ end
101
+ alias :== :eql?
102
+
103
+ # Return the singular id if this `Reference` is an old
104
+ # reference. Otherwise, raise a `NewReferenceError`.
105
+ #
106
+ # @return [Integer]
107
+ # @raise [NewReferenceError] if new reference
108
+ def id
109
+ raise Erlang::NewReferenceError if new_reference?
110
+ return @ids
111
+ end
112
+
113
+ # Return true if this is a new reference.
114
+ #
115
+ # @return [Boolean]
116
+ def new_reference?
117
+ return Erlang.is_list(@ids)
118
+ end
119
+
120
+ # Return the contents of this `reference` as a Erlang-readable `::String`.
121
+ #
122
+ # @example
123
+ # # New reference
124
+ # Erlang::Reference["nonode@nohost", 0, [0, 0, 0]].erlang_inspect
125
+ # # => "{'reference','nonode@nohost',0,[0,0,0]}"
126
+ # # Old reference
127
+ # Erlang::Reference["nonode@nohost", 0, 0].erlang_inspect
128
+ # # => "{'reference','nonode@nohost',0,0}"
129
+ #
130
+ # @return [::String]
131
+ def erlang_inspect(raw = false)
132
+ if raw == true and Erlang.respond_to?(:term_to_binary)
133
+ result = 'erlang:binary_to_term('
134
+ result << Erlang.inspect(Erlang.term_to_binary(self), raw: raw)
135
+ result << ')'
136
+ return result
137
+ else
138
+ return Erlang.inspect(Erlang::Tuple[:reference, @node, @creation, @ids], raw: raw)
139
+ end
140
+ end
141
+
142
+ # @return [::String] the nicely formatted version of the `Reference`
143
+ def inspect
144
+ return "Erlang::Reference[#{@node.inspect}, #{@creation.inspect}, #{@ids.inspect}]"
145
+ end
146
+
147
+ # @return [::Array]
148
+ # @private
149
+ def marshal_dump
150
+ return [@node, @creation, @ids]
151
+ end
152
+
153
+ # @private
154
+ def marshal_load(args)
155
+ node, creation, ids = args
156
+ initialize(node, creation, ids)
157
+ __send__(:immutable!)
158
+ return self
159
+ end
160
+ end
161
+ end