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.
- checksums.yaml +4 -4
- data/.coveralls.yml +1 -0
- data/.editorconfig +20 -0
- data/.gitignore +10 -18
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +15 -3
- data/.yardopts +6 -0
- data/CHANGELOG.md +9 -0
- data/Gemfile +21 -1
- data/LICENSE.txt +1 -1
- data/README.md +95 -17
- data/Rakefile +8 -3
- data/erlang-terms.gemspec +14 -11
- data/lib/erlang-terms.rb +1 -0
- data/lib/erlang/associable.rb +98 -0
- data/lib/erlang/atom.rb +257 -0
- data/lib/erlang/binary.rb +425 -0
- data/lib/erlang/bitstring.rb +464 -0
- data/lib/erlang/cons.rb +122 -0
- data/lib/erlang/enumerable.rb +160 -0
- data/lib/erlang/error.rb +4 -0
- data/lib/erlang/export.rb +110 -12
- data/lib/erlang/float.rb +201 -0
- data/lib/erlang/function.rb +259 -0
- data/lib/erlang/immutable.rb +101 -0
- data/lib/erlang/list.rb +1685 -24
- data/lib/erlang/map.rb +935 -21
- data/lib/erlang/nil.rb +73 -10
- data/lib/erlang/pid.rb +120 -18
- data/lib/erlang/port.rb +123 -0
- data/lib/erlang/reference.rb +161 -0
- data/lib/erlang/string.rb +175 -3
- data/lib/erlang/term.rb +24 -0
- data/lib/erlang/terms.rb +324 -8
- data/lib/erlang/terms/version.rb +1 -1
- data/lib/erlang/trie.rb +364 -0
- data/lib/erlang/tuple.rb +1582 -14
- data/lib/erlang/undefined.rb +32 -0
- metadata +49 -71
- data/spec/erlang/export_spec.rb +0 -17
- data/spec/erlang/list_spec.rb +0 -39
- data/spec/erlang/map_spec.rb +0 -24
- data/spec/erlang/nil_spec.rb +0 -18
- data/spec/erlang/pid_spec.rb +0 -21
- data/spec/erlang/string_spec.rb +0 -11
- data/spec/erlang/terms_spec.rb +0 -7
- data/spec/erlang/tuple_spec.rb +0 -20
- data/spec/spec_helper.rb +0 -7
data/lib/erlang/cons.rb
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
module Erlang
|
2
|
+
# The basic building block for constructing lists
|
3
|
+
#
|
4
|
+
# A Cons, also known as a "cons cell", has a "head" and a "tail", where
|
5
|
+
# the head is an element in the list, and the tail is a reference to the
|
6
|
+
# rest of the list. This way a singly linked list can be constructed, with
|
7
|
+
# each `Erlang::Cons` holding a single element and a pointer to the next
|
8
|
+
# `Erlang::Cons`.
|
9
|
+
#
|
10
|
+
# The last `Erlang::Cons` instance in the chain has the {Erlang::Nil} as its tail.
|
11
|
+
#
|
12
|
+
# Licensing
|
13
|
+
# =========
|
14
|
+
#
|
15
|
+
# Portions taken and modified from https://github.com/hamstergem/hamster
|
16
|
+
#
|
17
|
+
# Copyright (c) 2009-2014 Simon Harris
|
18
|
+
#
|
19
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
20
|
+
# a copy of this software and associated documentation files (the
|
21
|
+
# "Software"), to deal in the Software without restriction, including
|
22
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
23
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
24
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
25
|
+
# the following conditions:
|
26
|
+
#
|
27
|
+
# The above copyright notice and this permission notice shall be
|
28
|
+
# included in all copies or substantial portions of the Software.
|
29
|
+
#
|
30
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
31
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
32
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
33
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
34
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
35
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
36
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
37
|
+
#
|
38
|
+
# @private
|
39
|
+
class Cons
|
40
|
+
include Erlang::Term
|
41
|
+
include Erlang::List
|
42
|
+
include Erlang::Immutable
|
43
|
+
|
44
|
+
attr_reader :head
|
45
|
+
|
46
|
+
attr_reader :tail
|
47
|
+
|
48
|
+
class << self
|
49
|
+
def inspect
|
50
|
+
return Erlang::List.inspect
|
51
|
+
end
|
52
|
+
|
53
|
+
def pretty_inspect
|
54
|
+
return Erlang::List.pretty_inspect
|
55
|
+
end
|
56
|
+
|
57
|
+
def pretty_print(q)
|
58
|
+
return Erlang::List.pretty_print(q)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def initialize(head, tail = Erlang::Nil)
|
63
|
+
@head = Erlang.from(head)
|
64
|
+
@tail = Erlang.from(tail)
|
65
|
+
@improper = @tail.kind_of?(Erlang::List) ? @tail.improper? : true
|
66
|
+
end
|
67
|
+
|
68
|
+
def empty?
|
69
|
+
return false
|
70
|
+
end
|
71
|
+
|
72
|
+
def improper?
|
73
|
+
return !!@improper
|
74
|
+
end
|
75
|
+
|
76
|
+
# Return the number of items in this `Erlang::List`.
|
77
|
+
# @return [Integer]
|
78
|
+
def size
|
79
|
+
raise Erlang::ImproperListError if improper?
|
80
|
+
return 0 if empty?
|
81
|
+
size = 0
|
82
|
+
list = self
|
83
|
+
until list.empty?
|
84
|
+
list = list.tail
|
85
|
+
size += 1
|
86
|
+
end
|
87
|
+
return size
|
88
|
+
end
|
89
|
+
memoize :size
|
90
|
+
alias :length :size
|
91
|
+
|
92
|
+
# @return [::Array]
|
93
|
+
# @private
|
94
|
+
def marshal_dump
|
95
|
+
if improper?
|
96
|
+
return [to_proper_list.to_a, last(true)]
|
97
|
+
else
|
98
|
+
return [to_a, Erlang::Nil]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# @private
|
103
|
+
def marshal_load(args)
|
104
|
+
h, t = args
|
105
|
+
if h.size == 0
|
106
|
+
return t
|
107
|
+
elsif Erlang::Nil.eql?(t)
|
108
|
+
head = h[0]
|
109
|
+
tail = Erlang::List.from_enum(h[1..-1])
|
110
|
+
initialize(head, tail)
|
111
|
+
__send__(:immutable!)
|
112
|
+
return self
|
113
|
+
else
|
114
|
+
head = h[0]
|
115
|
+
tail = Erlang::List.from_enum(h[1..-1]) + t
|
116
|
+
initialize(head, tail)
|
117
|
+
__send__(:immutable!)
|
118
|
+
return self
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
module Erlang
|
2
|
+
# Helper module for Erlang sequential collections
|
3
|
+
#
|
4
|
+
# Classes including `Erlang::Enumerable` must implement:
|
5
|
+
#
|
6
|
+
# - `#each` (just like `::Enumerable`).
|
7
|
+
# - `#select`, which takes a block, and returns an instance of the same class
|
8
|
+
# with only the items for which the block returns a true value
|
9
|
+
#
|
10
|
+
# Licensing
|
11
|
+
# =========
|
12
|
+
#
|
13
|
+
# Portions taken and modified from https://github.com/hamstergem/hamster
|
14
|
+
#
|
15
|
+
# Copyright (c) 2009-2014 Simon Harris
|
16
|
+
#
|
17
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
18
|
+
# a copy of this software and associated documentation files (the
|
19
|
+
# "Software"), to deal in the Software without restriction, including
|
20
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
21
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
22
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
23
|
+
# the following conditions:
|
24
|
+
#
|
25
|
+
# The above copyright notice and this permission notice shall be
|
26
|
+
# included in all copies or substantial portions of the Software.
|
27
|
+
#
|
28
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
29
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
30
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
31
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
32
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
33
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
34
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
35
|
+
#
|
36
|
+
module Enumerable
|
37
|
+
include ::Enumerable
|
38
|
+
|
39
|
+
# Return a new collection with all the elements for which the block returns false.
|
40
|
+
def reject
|
41
|
+
return enum_for(:reject) if not block_given?
|
42
|
+
return select { |item| !yield(item) }
|
43
|
+
end
|
44
|
+
alias :delete_if :reject
|
45
|
+
|
46
|
+
# Return a new collection with all `nil` elements removed.
|
47
|
+
def compact
|
48
|
+
return select { |item| !item.nil? }
|
49
|
+
end
|
50
|
+
|
51
|
+
# Search the collection for elements which are `#===` to `item`. Yield them to
|
52
|
+
# the optional code block if provided, and return them as a new collection.
|
53
|
+
def grep(pattern, &block)
|
54
|
+
result = select { |item| pattern === item }
|
55
|
+
result = result.map(&block) if block_given?
|
56
|
+
return result
|
57
|
+
end
|
58
|
+
|
59
|
+
# Search the collection for elements which are not `#===` to `item`. Yield
|
60
|
+
# them to the optional code block if provided, and return them as a new
|
61
|
+
# collection.
|
62
|
+
def grep_v(pattern, &block)
|
63
|
+
result = select { |item| !(pattern === item) }
|
64
|
+
result = result.map(&block) if block_given?
|
65
|
+
return result
|
66
|
+
end
|
67
|
+
|
68
|
+
# Yield all integers from 0 up to, but not including, the number of items in
|
69
|
+
# this collection. For collections which provide indexed access, these are all
|
70
|
+
# the valid, non-negative indices into the collection.
|
71
|
+
def each_index(&block)
|
72
|
+
return enum_for(:each_index) unless block_given?
|
73
|
+
0.upto(size-1, &block)
|
74
|
+
return self
|
75
|
+
end
|
76
|
+
|
77
|
+
# Multiply all the items (presumably numeric) in this collection together.
|
78
|
+
def product
|
79
|
+
return reduce(1, &:*)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Add up all the items (presumably numeric) in this collection.
|
83
|
+
def sum
|
84
|
+
return reduce(0, &:+)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Return 2 collections, the first containing all the elements for which the block
|
88
|
+
# evaluates to true, the second containing the rest.
|
89
|
+
def partition
|
90
|
+
return enum_for(:partition) if not block_given?
|
91
|
+
a,b = super
|
92
|
+
return Erlang::Tuple[self.class.new(a), self.class.new(b)]
|
93
|
+
end
|
94
|
+
|
95
|
+
# Groups the collection into sub-collections by the result of yielding them to
|
96
|
+
# the block. Returns a {Map} where the keys are return values from the block,
|
97
|
+
# and the values are sub-collections. All the sub-collections are built up from
|
98
|
+
# `empty_group`, which should respond to `#add` by returning a new collection
|
99
|
+
# with an added element.
|
100
|
+
def group_by_with(empty_group, &block)
|
101
|
+
block ||= lambda { |item| item }
|
102
|
+
return reduce(EmptyMap) do |map, item|
|
103
|
+
key = block.call(item)
|
104
|
+
group = map.get(key) || empty_group
|
105
|
+
map.put(key, group.add(item))
|
106
|
+
end
|
107
|
+
end
|
108
|
+
protected :group_by_with
|
109
|
+
|
110
|
+
# Groups the collection into sub-collections by the result of yielding them to
|
111
|
+
# the block. Returns a {Map} where the keys are return values from the block,
|
112
|
+
# and the values are sub-collections (of the same type as this one).
|
113
|
+
def group_by(&block)
|
114
|
+
return group_by_with(self.class.empty, &block)
|
115
|
+
end
|
116
|
+
|
117
|
+
# Convert all the elements into strings and join them together, separated by
|
118
|
+
# `separator`. By default, the `separator` is `$,`, the global default string
|
119
|
+
# separator, which is normally `nil`.
|
120
|
+
def join(separator = $,)
|
121
|
+
result = ""
|
122
|
+
if separator
|
123
|
+
each_with_index { |obj, i| result << separator if i > 0; result << obj.to_s }
|
124
|
+
else
|
125
|
+
each { |obj| result << obj.to_s }
|
126
|
+
end
|
127
|
+
return Erlang.from(result)
|
128
|
+
end
|
129
|
+
|
130
|
+
# Convert this collection to a programmer-readable `String` representation.
|
131
|
+
def inspect
|
132
|
+
result = "#{self.class}["
|
133
|
+
each_with_index { |obj, i| result << ', ' if i > 0; result << obj.inspect }
|
134
|
+
return result << "]"
|
135
|
+
end
|
136
|
+
|
137
|
+
# @private
|
138
|
+
def pretty_print(pp)
|
139
|
+
return pp.group(1, "#{self.class}[", "]") do
|
140
|
+
pp.breakable ''
|
141
|
+
pp.seplist(self) { |obj| obj.pretty_print(pp) }
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
alias :to_ary :to_a
|
146
|
+
alias :index :find_index
|
147
|
+
|
148
|
+
## Compatibility fixes
|
149
|
+
|
150
|
+
if RUBY_ENGINE == 'rbx'
|
151
|
+
# Rubinius implements Enumerable#sort_by using Enumerable#map
|
152
|
+
# Because we do our own, custom implementations of #map, that doesn't work well
|
153
|
+
# @private
|
154
|
+
def sort_by(&block)
|
155
|
+
result = Erlang.from(to_a)
|
156
|
+
return result.frozen? ? result.sort_by(&block) : result.sort_by!(&block)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
data/lib/erlang/error.rb
ADDED
data/lib/erlang/export.rb
CHANGED
@@ -1,23 +1,121 @@
|
|
1
1
|
module Erlang
|
2
|
+
# An `Export` is an external function. It corresponds to the `fun M:F/A` syntax from Erlang.
|
3
|
+
#
|
4
|
+
# ### Creating Exports
|
5
|
+
#
|
6
|
+
# Erlang::Export[:erlang, :make_ref, 0]
|
7
|
+
# # => Erlang::Export[:erlang, :make_ref, 0]
|
8
|
+
#
|
2
9
|
class Export
|
3
|
-
|
10
|
+
include Erlang::Term
|
11
|
+
include Erlang::Immutable
|
4
12
|
|
13
|
+
# Return the module for this `Export`
|
14
|
+
# @return [Atom]
|
15
|
+
attr_reader :mod
|
16
|
+
|
17
|
+
# Return the function for this `Export`
|
18
|
+
# @return [Atom]
|
19
|
+
attr_reader :function
|
20
|
+
|
21
|
+
# Return the arity for this `Export`
|
22
|
+
# @return [Integer]
|
23
|
+
attr_reader :arity
|
24
|
+
|
25
|
+
class << self
|
26
|
+
# Create a new `Export` populated with the given `mod`, `function`, and `arity`.
|
27
|
+
# @param mod [Atom, Symbol] The module atom
|
28
|
+
# @param function [Atom, Symbol] The function atom
|
29
|
+
# @param arity [Integer] The arity of the function
|
30
|
+
# @return [Export]
|
31
|
+
# @raise [ArgumentError] if `arity` is not an `Integer`
|
32
|
+
def [](mod, function, arity)
|
33
|
+
return new(mod, function, arity)
|
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 [Export] The left argument
|
40
|
+
# @param b [Export] The right argument
|
41
|
+
# @return [-1, 0, 1]
|
42
|
+
# @raise [ArgumentError] if `a` or `b` is not an `Export`
|
43
|
+
def compare(a, b)
|
44
|
+
raise ArgumentError, "'a' must be of Erlang::Export type" unless a.kind_of?(Erlang::Export)
|
45
|
+
raise ArgumentError, "'b' must be of Erlang::Export type" unless b.kind_of?(Erlang::Export)
|
46
|
+
c = Erlang.compare(a.mod, b.mod)
|
47
|
+
return c if c != 0
|
48
|
+
c = Erlang.compare(a.function, b.function)
|
49
|
+
return c if c != 0
|
50
|
+
c = Erlang.compare(a.arity, b.arity)
|
51
|
+
return c
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# @private
|
5
56
|
def initialize(mod, function, arity)
|
6
|
-
|
7
|
-
|
8
|
-
|
57
|
+
raise ArgumentError, 'arity must be a non-negative Integer' if not arity.is_a?(::Integer) or arity < 0
|
58
|
+
@mod = Erlang::Atom[mod]
|
59
|
+
@function = Erlang::Atom[function]
|
60
|
+
@arity = arity.freeze
|
61
|
+
end
|
62
|
+
|
63
|
+
# Return true if `other` has the same type and contents as this `Export`.
|
64
|
+
#
|
65
|
+
# @param other [Object] The object to compare with
|
66
|
+
# @return [Boolean]
|
67
|
+
def eql?(other)
|
68
|
+
return true if other.equal?(self)
|
69
|
+
if instance_of?(other.class)
|
70
|
+
return !!(mod == other.mod &&
|
71
|
+
function == other.function &&
|
72
|
+
arity == other.arity)
|
73
|
+
else
|
74
|
+
return !!(Erlang.compare(other, self) == 0)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
alias :== :eql?
|
78
|
+
|
79
|
+
# Return the contents of this `Export` as a Erlang-readable `::String`.
|
80
|
+
#
|
81
|
+
# @example
|
82
|
+
# Erlang::Export[:erlang, :make_ref, 0].erlang_inspect
|
83
|
+
# # => "fun 'erlang':'make_ref'/0"
|
84
|
+
#
|
85
|
+
# @return [::String]
|
86
|
+
def erlang_inspect(raw = false)
|
87
|
+
result = 'fun '
|
88
|
+
result << Erlang.inspect(@mod, raw: raw)
|
89
|
+
result << ':'
|
90
|
+
result << Erlang.inspect(@function, raw: raw)
|
91
|
+
result << '/'
|
92
|
+
result << Erlang.inspect(@arity, raw: raw)
|
93
|
+
return result
|
9
94
|
end
|
10
95
|
|
11
|
-
# @return [String] the nicely formatted version of the
|
96
|
+
# @return [::String] the nicely formatted version of the `Export`.
|
12
97
|
def inspect
|
13
|
-
"
|
98
|
+
return "Erlang::Export[#{mod.inspect}, #{function.inspect}, #{arity.inspect}]"
|
99
|
+
end
|
100
|
+
|
101
|
+
# @private
|
102
|
+
def hash
|
103
|
+
state = [@mod, @function, @arity]
|
104
|
+
return state.reduce(Erlang::Export.hash) { |acc, item| (acc << 5) - acc + item.hash }
|
105
|
+
end
|
106
|
+
|
107
|
+
# @return [::Array]
|
108
|
+
# @private
|
109
|
+
def marshal_dump
|
110
|
+
return [@mod, @function, @arity]
|
14
111
|
end
|
15
112
|
|
16
|
-
|
17
|
-
|
18
|
-
mod
|
19
|
-
|
20
|
-
|
113
|
+
# @private
|
114
|
+
def marshal_load(args)
|
115
|
+
mod, function, arity = args
|
116
|
+
initialize(mod, function, arity)
|
117
|
+
__send__(:immutable!)
|
118
|
+
return self
|
21
119
|
end
|
22
120
|
end
|
23
|
-
end
|
121
|
+
end
|
data/lib/erlang/float.rb
ADDED
@@ -0,0 +1,201 @@
|
|
1
|
+
require 'bigdecimal'
|
2
|
+
|
3
|
+
module Erlang
|
4
|
+
# An `Float` is a literal constant float-precision number.
|
5
|
+
#
|
6
|
+
# ### Creating Floats
|
7
|
+
#
|
8
|
+
# Erlang::Float["1.0e10"]
|
9
|
+
# # => 1.00000000000000000000e+10
|
10
|
+
# Erlang::Float[0]
|
11
|
+
# # => 0.00000000000000000000e+00
|
12
|
+
# Erlang::Float[-0.0, old: true]
|
13
|
+
# # => Erlang::Float["-0.00000000000000000000e+00", old: true]
|
14
|
+
# Erlang::Float[-1e308]
|
15
|
+
# # => -1.00000000000000000000e+308
|
16
|
+
# Erlang::Float[1e-308]
|
17
|
+
# # => 1.00000000000000000000e-308
|
18
|
+
#
|
19
|
+
class Float
|
20
|
+
include Erlang::Term
|
21
|
+
include Erlang::Immutable
|
22
|
+
|
23
|
+
# Return the data for this `Float`
|
24
|
+
# @return [::BigDecimal]
|
25
|
+
attr_reader :data
|
26
|
+
|
27
|
+
# Return the old flag for this `Float`
|
28
|
+
# @return [::Boolean]
|
29
|
+
attr_reader :old
|
30
|
+
|
31
|
+
class << self
|
32
|
+
# Create a new `Float` populated with the given `data`.
|
33
|
+
# @param data [::BigDecimal, ::Float] The content of the `Float`
|
34
|
+
# @param old [Boolean] Whether the `Float` should be considered old or not
|
35
|
+
# @return [Float]
|
36
|
+
# @raise [ArgumentError] if `data` cannot be coerced to be a `::BigDecimal`
|
37
|
+
def [](data, old: false)
|
38
|
+
if data.is_a?(::String)
|
39
|
+
data = ::BigDecimal.new(data)
|
40
|
+
elsif not data.is_a?(::BigDecimal)
|
41
|
+
data = ::BigDecimal.new(data.to_s)
|
42
|
+
end
|
43
|
+
return new(data, old)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Compares `a` and `b` and returns whether they are less than,
|
47
|
+
# equal to, or greater than each other.
|
48
|
+
#
|
49
|
+
# @param a [Float] The left argument
|
50
|
+
# @param b [Float] The right argument
|
51
|
+
# @return [-1, 0, 1]
|
52
|
+
# @raise [ArgumentError] if `a` or `b` is not an `Float`
|
53
|
+
def compare(a, b)
|
54
|
+
raise ArgumentError, "'a' must be of Erlang::Float type" unless a.kind_of?(Erlang::Float)
|
55
|
+
raise ArgumentError, "'b' must be of Erlang::Float type" unless b.kind_of?(Erlang::Float)
|
56
|
+
return a.data <=> b.data
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# @private
|
61
|
+
def initialize(data, old)
|
62
|
+
raise ArgumentError, 'data must be a BigDecimal' if not data.is_a?(::BigDecimal)
|
63
|
+
@data = data.freeze
|
64
|
+
@old = !!old
|
65
|
+
if @old == false and @data != @data.to_f
|
66
|
+
@data = ::BigDecimal.new(@data.to_f.to_s).freeze
|
67
|
+
end
|
68
|
+
raise ArgumentError, "data cannot be positive or negative Infinity: #{data.inspect}" if @data.to_s.include?("Infinity")
|
69
|
+
end
|
70
|
+
|
71
|
+
# @private
|
72
|
+
def hash
|
73
|
+
return @data.hash
|
74
|
+
end
|
75
|
+
|
76
|
+
# If a `numeric` is the same type as `self`, returns an array containing `numeric` and `self`.
|
77
|
+
# Otherwise, returns an array with both a numeric and num represented as `Float` objects.
|
78
|
+
#
|
79
|
+
# @param numeric [Erlang::Float, Numeric]
|
80
|
+
# @return [::Array]
|
81
|
+
def coerce(numeric)
|
82
|
+
if numeric.is_a?(Erlang::Float)
|
83
|
+
return [numeric, self]
|
84
|
+
else
|
85
|
+
return [numeric.to_f, to_f]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Return true if `other` has the same type and contents as this `Float`.
|
90
|
+
#
|
91
|
+
# @param other [Object] The object to compare with
|
92
|
+
# @return [Boolean]
|
93
|
+
def eql?(other)
|
94
|
+
return true if other.equal?(self)
|
95
|
+
if instance_of?(other.class)
|
96
|
+
return !!(self.data == other.data)
|
97
|
+
else
|
98
|
+
return !!(Erlang.compare(other, self) == 0)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
alias :== :eql?
|
102
|
+
|
103
|
+
# Return the contents of this `Float` as a Erlang-readable `::String`.
|
104
|
+
#
|
105
|
+
# @example
|
106
|
+
# Erlang::Float[-1e308].erlang_inspect
|
107
|
+
# # => "-1.00000000000000000000e+308"
|
108
|
+
#
|
109
|
+
# @return [::String]
|
110
|
+
def erlang_inspect(raw = false)
|
111
|
+
return to_float_string
|
112
|
+
end
|
113
|
+
|
114
|
+
# @return [::String] the nicely formatted version of the `Float`
|
115
|
+
def inspect
|
116
|
+
if @old
|
117
|
+
return "Erlang::Float[#{to_float_string.inspect}, old: true]"
|
118
|
+
else
|
119
|
+
float_string = to_float_string
|
120
|
+
float_object = ::Kernel::Float(float_string)
|
121
|
+
if Erlang::PositiveInfinity == float_object or Erlang::NegativeInfinity == float_object
|
122
|
+
return "Erlang::Float[#{float_string.inspect}]"
|
123
|
+
else
|
124
|
+
return float_string
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# @return [::String] the float string format of the `Float`
|
130
|
+
def to_float_string
|
131
|
+
string = @data.to_s
|
132
|
+
sign = (string.getbyte(0) == 45) ? '-' : ''
|
133
|
+
offset = (sign.bytesize == 1) ? 1 : 0
|
134
|
+
dotpos = string.index(?.)
|
135
|
+
epos = string.index(?e)
|
136
|
+
if epos.nil?
|
137
|
+
string << "e00"
|
138
|
+
epos = string.index(?e)
|
139
|
+
end
|
140
|
+
if @data.zero?
|
141
|
+
return Erlang::Terms.binary_encoding([
|
142
|
+
sign,
|
143
|
+
'0.00000000000000000000e+00'
|
144
|
+
].join)
|
145
|
+
end
|
146
|
+
integer = string.byteslice(offset, dotpos - offset)
|
147
|
+
fractional = string.byteslice(dotpos + 1, epos - dotpos - 1)
|
148
|
+
e = string.byteslice(epos + 1, string.bytesize - epos - 1).to_i
|
149
|
+
while fractional.bytesize > 0 and integer == ?0 and e > -323
|
150
|
+
b = fractional.getbyte(0)
|
151
|
+
fractional = fractional.byteslice(1, fractional.bytesize - 1)
|
152
|
+
e -= 1
|
153
|
+
if b != 48
|
154
|
+
integer.setbyte(0, b)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
if fractional.bytesize > 20
|
158
|
+
fractional = fractional.byteslice(0, 20)
|
159
|
+
elsif fractional.bytesize < 20
|
160
|
+
fractional = fractional.ljust(20, ?0)
|
161
|
+
end
|
162
|
+
return Erlang::Terms.binary_encoding([
|
163
|
+
sign,
|
164
|
+
integer,
|
165
|
+
'.',
|
166
|
+
fractional,
|
167
|
+
(e < 0) ? 'e-' : 'e+',
|
168
|
+
e.abs.to_s.rjust(2, ?0)
|
169
|
+
].join)
|
170
|
+
end
|
171
|
+
|
172
|
+
# @return [::Float] the float version of the `Float`
|
173
|
+
def to_f
|
174
|
+
return @data.to_f
|
175
|
+
end
|
176
|
+
|
177
|
+
# @return [::String] the string version of the `Float`
|
178
|
+
def to_s
|
179
|
+
return to_float_string
|
180
|
+
end
|
181
|
+
alias :to_str :to_s
|
182
|
+
|
183
|
+
# @return [::String]
|
184
|
+
# @private
|
185
|
+
def marshal_dump
|
186
|
+
return [to_float_string, @old]
|
187
|
+
end
|
188
|
+
|
189
|
+
# @private
|
190
|
+
def marshal_load(args)
|
191
|
+
float_string, old = args
|
192
|
+
initialize(::BigDecimal.new(float_string), old)
|
193
|
+
__send__(:immutable!)
|
194
|
+
return self
|
195
|
+
end
|
196
|
+
|
197
|
+
end
|
198
|
+
|
199
|
+
PositiveInfinity = (1.0 / 0.0).freeze
|
200
|
+
NegativeInfinity = -PositiveInfinity
|
201
|
+
end
|