diff --git a/docs/changelog.md b/docs/changelog.md index 5276e4678..b265c1853 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -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 @@ -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 diff --git a/stdlib/src/builtin/bin.mojo b/stdlib/src/builtin/bin.mojo new file mode 100644 index 000000000..424b68057 --- /dev/null +++ b/stdlib/src/builtin/bin.mojo @@ -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))) diff --git a/stdlib/src/builtin/int.mojo b/stdlib/src/builtin/int.mojo index 3f7bf92e4..841050dfe 100644 --- a/stdlib/src/builtin/int.mojo +++ b/stdlib/src/builtin/int.mojo @@ -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: diff --git a/stdlib/test/builtin/test_bin.mojo b/stdlib/test/builtin/test_bin.mojo new file mode 100644 index 000000000..c09b1d0bf --- /dev/null +++ b/stdlib/test/builtin/test_bin.mojo @@ -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()