Skip to content

Commit

Permalink
[External] [stdlib] Add bin() builtin function (#40176)
Browse files Browse the repository at this point in the history
[External] [stdlib] Add `bin()` builtin function

Fixes modularml#2604

Implements the `bin()` builtin function from Python for converting
integral values to a binary string representation.

Co-authored-by: bgreni <[email protected]>
Closes modularml#2603
MODULAR_ORIG_COMMIT_REV_ID: 96515ed124867bcac5a49781d57a92fae6bc480b
  • Loading branch information
bgreni authored and msaelices committed May 18, 2024
1 parent fe05df4 commit 4f07fa5
Show file tree
Hide file tree
Showing 4 changed files with 149 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 @@ -269,7 +269,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 Down Expand Up @@ -298,6 +301,10 @@ what we publish.
the ability to execute a `Benchmark` and allows for benchmarking configuration
via the `BenchmarkConfig` struct.

- 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
85 changes: 85 additions & 0 deletions stdlib/src/builtin/bin.mojo
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# ===----------------------------------------------------------------------=== #
# 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 4f07fa5

Please sign in to comment.