Skip to content

Commit

Permalink
Merge pull request #300 from savi-lang/add/integer-format-binary
Browse files Browse the repository at this point in the history
Add `Integer.Format.Binary` for showing integers in binary format.
  • Loading branch information
jemc authored Apr 19, 2022
2 parents f055ac2 + 39b56bd commit 8c5a48f
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 1 deletion.
2 changes: 1 addition & 1 deletion core/Comparable.savi
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
:trait Comparable(A Comparable(A)'read)
:fun "=="(other A'box) Bool // TODO: use :is Equatable(A) instead
:is Equatable(A)
:fun "<"(other A'box) Bool
:fun "<="(other A'box) Bool: (@ == other) || (@ < other)
:fun ">="(other A'box) Bool: (@ < other).is_false
Expand Down
1 change: 1 addition & 0 deletions core/Equatable.savi
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
:trait Equatable(A Equatable(A)'read)
:fun "=="(other A'box) Bool
:fun "!="(other A'box) Bool: (@ == other).is_false
55 changes: 55 additions & 0 deletions core/Integer.Format.savi
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@

:fun decimal: Integer.Format.Decimal(T)._new(@_value)
:fun hexadecimal: Integer.Format.Hexadecimal(T)._new(@_value)
:fun binary: Integer.Format.Binary(T)._new(@_value)

:fun dec: @decimal
:fun hex: @hexadecimal
:fun bin: @binary

:: Format the given integer into a variable number of decimal digits.
:struct val Integer.Format.Decimal(T Integer(T)'val)
Expand Down Expand Up @@ -161,3 +163,56 @@
u4 = @_value.bit_shr(shr).u8.bit_and(0xF)
a = if @_is_uppercase ('A' | 'a')
if (u4 <= 9) (u4 + '0' | u4 + a - 0xA)

:: Format the given integer into a fixed width binary representation.
::
:: By default the digits are shown with a "0b" prefix, but this is adjustable.
:struct val Integer.Format.Binary(T Integer(T)'val)
:is IntoString

:let _value T
:let _prefix String
:let _has_leading_zeros Bool
:new val _new(
@_value
@_prefix = "0b"
@_has_leading_zeros = True
)

:: Format without the standard "0b" binary prefix.
:fun bare: @with_prefix("")

:: Use the given prefix instead of the standard "0b" binary prefix.
:fun with_prefix(prefix)
@_new(@_value, prefix, @_has_leading_zeros)

:: Disable the default behavior of including all leading zeros.
:fun without_leading_zeros
@_new(@_value, @_prefix, False)

:fun into_string_space USize
// TODO: different strategy when `_has_leading_zeros` is False.
@_prefix.size + T.bit_width.usize

:fun into_string(out String'iso) String'iso
show_zeros = @_has_leading_zeros
out << @_prefix

// Begin with a bit mask highlighting only the single most significant bit.
bit_mask = T.one.bit_shl(T.bit_width - 1)

// Iterate the bit mask downward toward the lest significant bit until it
// reaches zero, emitting the appropriate character to show each bit.
while (Inspect.out(bit_mask.i16.format.hex), bit_mask != T.zero) (
if (@_value.bit_and(bit_mask) == T.zero) (
if show_zeros out.push_byte('0')
|
show_zeros = True, out.push_byte('1')
)
bit_mask = bit_mask.bit_shr(1)
)

// If we haven't seen any zeros or ones yet, show at least one zero
if !show_zeros out.push_byte('0')

--out
1 change: 1 addition & 0 deletions core/Integer.savi
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
:is Integer.SafeArithmetic(T)
:is Integer.WideArithmetic(T)
:is Integer.BitwiseArithmetic(T)
:is Numeric.Comparable(T)
:is Integer.Countable(T)
:is Integer.Formattable(T)

Expand Down
14 changes: 14 additions & 0 deletions spec/core/Numeric.Spec.savi
Original file line number Diff line number Diff line change
Expand Up @@ -628,3 +628,17 @@
assert: "\(36.format.hex.without_leading_zeros)" == "0x24"
assert: "\((-36).format.hex.without_leading_zeros)" == "0xFFFFFFDC"
assert: "\(1025.format.hex.without_leading_zeros)" == "0x401"

:it "can format integers in a standard binary representation"
assert: "\(False.format.bin)" == "0b0"
assert: "\(True.format.bin)" == "0b1"
assert: "\(U8[0].format.bin)" == "0b00000000"
assert: "\(I16[36].format.bin)" == "0b0000000000100100"
assert: "\(I16[-36].format.bin)" == "0b1111111111011100"

:it "can format integers in a binary without leading zeros"
assert: "\(False.format.bin.without_leading_zeros)" == "0b0"
assert: "\(True.format.bin.without_leading_zeros)" == "0b1"
assert: "\(U8[0].format.bin.without_leading_zeros)" == "0b0"
assert: "\(I16[36].format.bin.without_leading_zeros)" == "0b100100"
assert: "\(I16[-36].format.bin.without_leading_zeros)" == "0b1111111111011100"

0 comments on commit 8c5a48f

Please sign in to comment.