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