erlang-terms 1.1.0 → 2.0.1

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