Skip to content

Commit

Permalink
Add bin() builtin function
Browse files Browse the repository at this point in the history
Signed-off-by: Brian Grenier <[email protected]>
  • Loading branch information
bgreni committed May 17, 2024
1 parent 93ac887 commit 3bdc3d0
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 2 deletions.
9 changes: 8 additions & 1 deletion docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,10 @@ what we publish.
- Added the `Indexer` trait to denote types that implement the `__index__()`
method which allow these types to be accepted in common `__getitem__` and
`__setitem__` implementations, as well as allow a new builtin `index` function
to be called on them. For example:
to be called on them.
([PR #2685](https://github.com/modularml/mojo/pull/2685) by
[@bgreni](https://github.com/bgreni))
For example:

```mojo
@value
Expand All @@ -275,6 +278,10 @@ what we publish.
trailing whitespaces. ([PR #2683](https://github.com/modularml/mojo/pull/2683)
by [@fknfilewalker](https://github.com/fknfilewalker))

- Added the `bin()` builtin function to convert integral types into their binary string representation.
([PR #2603](https://github.com/modularml/mojo/pull/2603) by
[@bgreni](https://github.com/bgreni))

### 🦋 Changed

- The `let` keyword has been completely removed from the language. We previously
Expand Down
86 changes: 86 additions & 0 deletions stdlib/src/builtin/bin.mojo
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# ===----------------------------------------------------------------------=== #
# Copyright (c) 2024, Modular Inc. All rights reserved.
#
# Licensed under the Apache License v2.0 with LLVM Exceptions:
# https://llvm.org/LICENSE.txt
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ===----------------------------------------------------------------------=== #
"""Implements the `bin()` function
These are Mojo built-ins, so you don't need to import them.
"""


# Need this until we have constraints to stop
# the compiler from matching this directly
# to bin[type: DType](num: Scalar[type])
@always_inline("nodebug")
fn bin(b: Scalar[DType.bool]) -> String:
"""Returns the binary representation of a scalar bool.
Args:
b: A scalar bool value.
Returns:
The binary string representation of b.
"""
return bin(index(b))


fn bin[type: DType](num: Scalar[type]) -> String:
"""Return the binary string representation an integral value.
```mojo
print(bin(123))
print(bin(-123))
```
```plaintext
'0b1111011'
'-0b1111011'
```
Parameters:
type: The data type of the integral scalar.
Args:
num: An integral scalar value.
Returns:
The binary string representation of num.
"""
constrained[type.is_integral(), "Expected integral value"]()
alias BIN_PREFIX = "0b"

if num == 0:
return BIN_PREFIX + "0"

# TODD: pre-allocate string size when #2194 is resolved
var result = String()
var cpy = abs(num)
while cpy > 0:
result += str(cpy & 1)
cpy = cpy >> 1

result = BIN_PREFIX + result[::-1]
return "-" + result if num < 0 else result


@always_inline("nodebug")
fn bin[T: Indexer](num: T) -> String:
"""Returns the binary representation of an indexer type.
Parameters:
T: The Indexer type.
Args:
num: An indexer value.
Returns:
The binary string representation of num.
"""
return bin(Scalar[DType.index](index(num)))
2 changes: 1 addition & 1 deletion stdlib/src/builtin/int.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ trait Indexer:


@always_inline("nodebug")
fn index[T: Indexer](idx: T) -> Int:
fn index[T: Indexer](idx: T, /) -> Int:
"""Returns the value of `__index__` for the given value.
Parameters:
Expand Down
55 changes: 55 additions & 0 deletions stdlib/test/builtin/test_bin.mojo
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# ===----------------------------------------------------------------------=== #
# Copyright (c) 2024, Modular Inc. All rights reserved.
#
# Licensed under the Apache License v2.0 with LLVM Exceptions:
# https://llvm.org/LICENSE.txt
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ===----------------------------------------------------------------------=== #
# RUN: %mojo %s

from testing import assert_equal


@value
struct Ind(Indexer):
fn __index__(self) -> Int:
return 1


def test_bin_scalar():
assert_equal(bin(Int8(2)), "0b10")
assert_equal(bin(Int32(123)), "0b1111011")
assert_equal(bin(Int32(-123)), "-0b1111011")
assert_equal(bin(Scalar[DType.bool](True)), "0b1")
assert_equal(bin(Scalar[DType.bool](False)), "0b0")


def test_bin_int():
assert_equal(bin(0), "0b0")
assert_equal(bin(1), "0b1")
assert_equal(bin(-1), "-0b1")
assert_equal(bin(4), "0b100")
assert_equal(bin(Int(-4)), "-0b100")
assert_equal(bin(389703), "0b1011111001001000111")
assert_equal(bin(-10), "-0b1010")


def test_bin_bool():
assert_equal(bin(True), "0b1")
assert_equal(bin(False), "0b0")


def test_indexer():
assert_equal(bin(Ind()), "0b1")


def main():
test_bin_scalar()
test_bin_int()
test_bin_bool()
test_indexer()

0 comments on commit 3bdc3d0

Please sign in to comment.