From af7478f28b25aafa050151a24cead2526a78fe44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8C=97=E9=9C=B2?= <69190413+illusory0x0@users.noreply.github.com> Date: Thu, 9 Jan 2025 14:08:08 +0800 Subject: [PATCH 01/17] remove redundant comparisons for sorted_set --- sorted_set/set.mbt | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/sorted_set/set.mbt b/sorted_set/set.mbt index 7cadfc3d8..6f8b41a83 100644 --- a/sorted_set/set.mbt +++ b/sorted_set/set.mbt @@ -1,4 +1,4 @@ -// Copyright 2024 International Digital Economy Academy +// Copyright 2025 International Digital Economy Academy // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -169,16 +169,18 @@ pub fn union[V : Compare](self : T[V], src : T[V]) -> T[V] { fn split[V : Compare](root : Node[V]?, value : V) -> (Node[V]?, Node[V]?) { match root { None => (None, None) - Some(node) => - if value == node.value { + Some(node) => { + let comp = value.compare(node.value) + if comp == 0 { (node.left, node.right) - } else if value < node.value { + } else if comp < 0 { let (l, r) = split(node.left, value) (l, Some(join(r, node.value, node.right))) } else { let (l, r) = split(node.right, value) (Some(join(node.left, node.value, l)), r) } + } } } @@ -554,13 +556,14 @@ fn rotate_rl[V : Compare](n : Node[V]) -> Node[V] { fn add_node[V : Compare](root : Node[V]?, value : V) -> (Node[V]?, Bool) { match root { None => (Some(new_node(value)), true) - Some(n) => - if value == n.value { + Some(n) => { + let comp = value.compare(n.value) + if comp == 0 { n.value = value (Some(n), false) } else { let (l, r) = (n.left, n.right) - if value < n.value { + if comp < 0 { let (nl, inserted) = add_node(l, value) n.left = nl (Some(balance(n)), inserted) @@ -570,12 +573,14 @@ fn add_node[V : Compare](root : Node[V]?, value : V) -> (Node[V]?, Bool) { (Some(balance(n)), inserted) } } + } } } ///| fn delete_node[V : Compare](root : Node[V], value : V) -> (Node[V]?, Bool) { - if value == root.value { + let comp = value.compare(root.value) + if comp == 0 { let (l, r) = (root.left, root.right) let n = match (l, r) { (Some(_), Some(nr)) => { @@ -586,7 +591,7 @@ fn delete_node[V : Compare](root : Node[V], value : V) -> (Node[V]?, Bool) { (Some(_), None) | (None, None) => l } (n, true) - } else if value < root.value { + } else if comp < 0 { match root.left { None => (Some(root), false) Some(l) => { From 18780bd7df18429fe01ea772446b96ee78139f59 Mon Sep 17 00:00:00 2001 From: Haoxiang Fei Date: Wed, 8 Jan 2025 17:43:29 +0800 Subject: [PATCH 02/17] doc: buffer --- buffer/buffer.mbt | 507 ++++++++++++++++++++++++++++++++++++++++-- buffer/deprecated.mbt | 17 +- 2 files changed, 500 insertions(+), 24 deletions(-) diff --git a/buffer/buffer.mbt b/buffer/buffer.mbt index fc75e1892..dc4feca91 100644 --- a/buffer/buffer.mbt +++ b/buffer/buffer.mbt @@ -63,37 +63,163 @@ fn grow_if_necessary(self : T, required : Int) -> Unit { } ///| -/// Return the given buffer's length in bytes. +/// Returns the number of bytes currently stored in the buffer. +/// +/// Parameters: +/// +/// * `buffer`: The buffer to get the length from. +/// +/// Returns the length of the buffer in bytes. +/// +/// Example: +/// +/// ```moonbit +/// test "length" { +/// let buf = @buffer.new() +/// buf.write_string("Test") +/// inspect!(buf.length(), content="8") // each char takes 2 bytes in UTF-16 +/// } +/// ``` pub fn length(self : T) -> Int { self.len } ///| -/// Return whether the given buffer is empty. +/// Returns whether the buffer is empty. +/// +/// Parameters: +/// +/// * `buffer` : The buffer to check. +/// +/// Returns `true` if the buffer is empty (i.e., contains no bytes), `false` +/// otherwise. +/// +/// Example: +/// +/// ```moonbit +/// test "buffer::is_empty" { +/// let buf = @buffer.new() +/// inspect!(buf.is_empty(), content="true") +/// buf.write_string("test") +/// inspect!(buf.is_empty(), content="false") +/// } +/// ``` pub fn is_empty(self : T) -> Bool { self.len == 0 } -///| Return the contents of the buffer as a Bytes. +///| +/// Returns a copy of the buffer's content as a sequence of bytes. +/// +/// Parameters: +/// +/// * `buffer` : The buffer to read from. +/// +/// Returns a `Bytes` object containing all bytes written to the buffer. +/// +/// Example: +/// +/// ```moonbit +/// test "contents" { +/// let buf = @buffer.new() +/// buf.write_string("Test") +/// inspect!( +/// buf.contents(), +/// content= +/// #|b"\x54\x00\x65\x00\x73\x00\x74\x00" +/// , +/// ) +/// } +/// ``` pub fn contents(self : T) -> Bytes { Bytes::from_fixedarray(self.data, len=self.len) } -///| Create a buffer with initial capacity (in bytes). +///| +/// Creates a new extensible buffer with specified initial capacity. If the +/// initial capacity is less than 1, the buffer will be initialized with capacity +/// 1. +/// +/// Parameters: +/// +/// * `size_hint` : Initial capacity of the buffer in bytes. Defaults to 0. +/// +/// Returns a new buffer of type `T`. +/// +/// Example: +/// +/// ```moonbit +/// test "T::new" { +/// let buf = @buffer.T::new(size_hint=10) +/// inspect!(buf.length(), content="0") +/// buf.write_string("test") +/// inspect!(buf.length(), content="8") +/// } +/// ``` pub fn T::new(size_hint~ : Int = 0) -> T { let initial = if size_hint < 1 { 1 } else { size_hint } let data = FixedArray::make(initial, Byte::default()) { data, len: 0, initial_data: data } } -///| Write a string into buffer in little endian format. +///| +/// Writes a UTF-16LE encoded string into the buffer. The buffer will +/// automatically grow if needed to accommodate the string. +/// +/// Parameters: +/// +/// * `buffer` : The buffer to write to. +/// * `string` : The string to be written. +/// +/// Example: +/// +/// ```moonbit +/// test "write_string" { +/// let buf = @buffer.new() +/// buf.write_string("Test") +/// // Each UTF-16 char takes 2 bytes in little-endian format +/// // 'T' -> [0x54, 0x00] +/// // 'e' -> [0x65, 0x00] +/// // 's' -> [0x73, 0x00] +/// // 't' -> [0x74, 0x00] +/// inspect!( +/// buf.contents(), +/// content= +/// #|b"\x54\x00\x65\x00\x73\x00\x74\x00" +/// , +/// ) +/// } +/// ``` pub fn write_string(self : T, value : String) -> Unit { self.grow_if_necessary(self.len + value.length() * 2) self.data.blit_from_string(self.len, value, 0, value.length()) self.len += value.length() * 2 } -///| Write an UInt64 into buffer in big endian format. +///| +/// Writes an unsigned 64-bit integer into the buffer in big-endian format (most +/// significant byte first). +/// +/// Parameters: +/// +/// * `buffer` : The buffer to write to. +/// * `value` : The unsigned 64-bit integer to be written. +/// +/// Example: +/// +/// ```moonbit +/// test "write_uint64_be" { +/// let buf = @buffer.new() +/// buf.write_uint64_be(0xAABBCCDD11223344) +/// // Bytes are written in big-endian order +/// inspect!( +/// buf.contents(), +/// content= +/// #|b"\xaa\xbb\xcc\xdd\x11\x22\x33\x44" +/// , +/// ) +/// } +/// ``` pub fn write_uint64_be(self : T, value : UInt64) -> Unit { self.write_byte((value >> 56).to_byte()) self.write_byte((value >> 48).to_byte()) @@ -105,7 +231,29 @@ pub fn write_uint64_be(self : T, value : UInt64) -> Unit { self.write_byte(value.to_byte()) } -///| Write a UInt64 into buffer in little endian format. +///| +/// Writes an unsigned 64-bit integer to the buffer in little-endian byte order. +/// Each byte is written sequentially from least significant to most significant. +/// +/// Parameters: +/// +/// * `buffer` : The buffer to write to. +/// * `value` : The UInt64 value to be written. +/// +/// Example: +/// +/// ```moonbit +/// test "write_uint64_le" { +/// let buf = @buffer.new() +/// buf.write_uint64_le(0x0123456789ABCDEF) +/// inspect!( +/// buf.contents(), +/// content= +/// #|b"\xef\xcd\xab\x89\x67\x45\x23\x01" +/// , +/// ) +/// } +/// ``` pub fn write_uint64_le(self : T, value : UInt64) -> Unit { self.write_byte(value.to_byte()) self.write_byte((value >> 8).to_byte()) @@ -117,17 +265,74 @@ pub fn write_uint64_le(self : T, value : UInt64) -> Unit { self.write_byte((value >> 56).to_byte()) } -///| Write an Int64 into buffer in big endian format. +///| +/// Writes a 64-bit integer into the buffer in big-endian format, where the most +/// significant byte is written first. +/// +/// Parameters: +/// +/// * `buffer` : The buffer to write into. +/// * `value` : The 64-bit integer to be written. +/// +/// Example: +/// +/// ```moonbit +/// test "write_int64_be" { +/// let buf = @buffer.new() +/// buf.write_int64_be(0x0102030405060708L) +/// inspect!( +/// buf.contents(), +/// content= +/// #|b"\x01\x02\x03\x04\x05\x06\x07\x08" +/// , +/// ) +/// } +/// ``` pub fn write_int64_be(self : T, value : Int64) -> Unit { self.write_uint64_be(value.reinterpret_as_uint64()) } -///| Write an Int64 into buffer in little endian format. +///| +/// Writes a 64-bit signed integer to the buffer in little-endian byte order. +/// +/// Parameters: +/// +/// * `buffer` : The buffer to write to. +/// * `value` : The 64-bit signed integer to write. +/// +/// Example: +/// +/// ```moonbit +/// test "write_int64_le" { +/// let buf = @buffer.new() +/// buf.write_int64_le(-1L) +/// inspect!(buf.contents(), content= +/// #|b"\xff\xff\xff\xff\xff\xff\xff\xff" +/// ) +/// } +/// ``` pub fn write_int64_le(self : T, value : Int64) -> Unit { self.write_uint64_le(value.reinterpret_as_uint64()) } -///| Write an UInt into buffer in big endian format. +///| +/// Writes a 32-bit unsigned integer into the buffer in big-endian format (most +/// significant byte first). +/// +/// Parameters: +/// +/// * `buffer` : The buffer to write to. +/// * `value` : The unsigned integer value to write. +/// +/// Example: +/// +/// ```moonbit +/// test "write_uint_be" { +/// let buf = @buffer.new() +/// buf.write_uint_be(0x12345678) +/// inspect!(buf.contents(), content="b\"\\x12\\x34\\x56\\x78\"") +/// } +/// ``` pub fn write_uint_be(self : T, value : UInt) -> Unit { self.write_byte((value >> 24).to_byte()) self.write_byte((value >> 16).to_byte()) @@ -135,7 +340,25 @@ pub fn write_uint_be(self : T, value : UInt) -> Unit { self.write_byte(value.to_byte()) } -///| Write an UInt into buffer in little endian format. +///| +/// Writes a 32-bit unsigned integer into the buffer in little-endian format. The +/// integer is split into 4 bytes and written in order from least significant to +/// most significant byte. +/// +/// Parameters: +/// +/// * `buffer` : The buffer to write to. +/// * `value` : A 32-bit unsigned integer to be written. +/// +/// Example: +/// +/// ```moonbit +/// test "write_uint_le" { +/// let buf = @buffer.new() +/// buf.write_uint_le(0x12345678) +/// inspect!(buf.contents(), content="b\"\\x78\\x56\\x34\\x12\"") +/// } +/// ``` pub fn write_uint_le(self : T, value : UInt) -> Unit { self.write_byte(value.to_byte()) self.write_byte((value >> 8).to_byte()) @@ -143,42 +366,191 @@ pub fn write_uint_le(self : T, value : UInt) -> Unit { self.write_byte((value >> 24).to_byte()) } -///| Write an Int into buffer in big endian format. +///| +/// Writes a 32-bit integer to the buffer in big-endian format. Big-endian means +/// the most significant byte is written first. +/// +/// Parameters: +/// +/// * `buffer` : The buffer to write to. +/// * `value` : The 32-bit integer to be written. +/// +/// Example: +/// +/// ```moonbit +/// test "write_int_be" { +/// let buf = @buffer.new() +/// buf.write_int_be(0x12345678) +/// inspect!(buf.contents(), content="b\"\\x12\\x34\\x56\\x78\"") +/// } +/// ``` pub fn write_int_be(self : T, value : Int) -> Unit { self.write_uint_be(value.reinterpret_as_uint()) } -///| Write an Int into buffer in little endian format. +///| +/// Writes a 32-bit integer into the buffer in little-endian format. The integer +/// is first reinterpreted as an unsigned integer, then written as 4 bytes where +/// the least significant byte is written first. +/// +/// Parameters: +/// +/// * `buffer` : The buffer to write into. +/// * `value` : The integer value to be written. +/// +/// Example: +/// +/// ```moonbit +/// test "write_int_le" { +/// let buf = @buffer.new() +/// buf.write_int_le(-1) +/// inspect!(buf.contents(), content="b\"\\xff\\xff\\xff\\xff\"") +/// } +/// ``` pub fn write_int_le(self : T, value : Int) -> Unit { self.write_uint_le(value.reinterpret_as_uint()) } -///| Write a Double into buffer in big endian format. +///| +/// Writes an IEEE 754 double-precision floating-point number into the buffer in +/// big-endian format (most significant byte first). +/// +/// Parameters: +/// +/// * `buffer` : The buffer to write to. +/// * `value` : The double-precision floating-point number to be written. +/// +/// Example: +/// +/// ```moonbit +/// test "write_double_be" { +/// let buf = @buffer.new() +/// buf.write_double_be(1.0) +/// inspect!(buf.contents(), content="b\"\\x3f\\xf0\\x00\\x00\\x00\\x00\\x00\\x00\"") +/// } +/// ``` pub fn write_double_be(self : T, value : Double) -> Unit { self.write_uint64_be(value.reinterpret_as_uint64()) } -///| Write a Double into buffer in little endian format. +///| +/// Writes a double-precision floating-point number into the buffer in +/// little-endian format. +/// +/// Parameters: +/// +/// * `buffer` : The buffer to write to. +/// * `value` : The double-precision floating-point number to write. +/// +/// Example: +/// +/// ```moonbit +/// test "write_double_le" { +/// let buf = @buffer.new() +/// buf.write_double_le(3.14) +/// inspect!( +/// buf.contents(), +/// content="b\"\\x1f\\x85\\xeb\\x51\\xb8\\x1e\\x09\\x40\"", +/// ) +/// } +/// ``` pub fn write_double_le(self : T, value : Double) -> Unit { self.write_uint64_le(value.reinterpret_as_uint64()) } -///| Write a Float into buffer in big endian format. +///| +/// Writes a 32-bit floating-point number to the buffer in big-endian byte order. +/// The float value is first reinterpreted as a 32-bit unsigned integer before +/// writing. +/// +/// Parameters: +/// +/// * `buffer` : The buffer to write to. +/// * `value` : The floating-point number to be written. +/// +/// Example: +/// +/// ```moonbit +/// test "write_float_be" { +/// let buf = @buffer.new() +/// buf.write_float_be(3.14) +/// // In big-endian format, 3.14 is represented as [0x40, 0x48, 0xF5, 0xC3] +/// inspect!(buf.contents(), content="b\"\\x40\\x48\\xf5\\xc3\"") +/// } +/// ``` pub fn write_float_be(self : T, value : Float) -> Unit { self.write_uint_be(value.reinterpret_as_uint()) } -///| Write a Float into buffer in little endian format. +///| +/// Writes a Float value into the buffer in little-endian format. The float value +/// is converted to its binary representation and written as four bytes. +/// +/// Parameters: +/// +/// * `buffer` : The buffer to write to. +/// * `value` : The Float value to be written. +/// +/// Example: +/// +/// ```moonbit +/// test "write_float_le" { +/// let buf = @buffer.new() +/// buf.write_float_le(3.14) +/// // The bytes are written in little-endian format +/// inspect!(buf.contents(), content="b\"\\xc3\\xf5\\x48\\x40\"") +/// } +/// ``` pub fn write_float_le(self : T, value : Float) -> Unit { self.write_uint_le(value.reinterpret_as_uint()) } ///| +/// Writes a string representation of any value that implements the `Show` trait +/// into the buffer. +/// +/// Parameters: +/// +/// * `buffer` : The buffer to write to. +/// * `value` : Any value that implements the `Show` trait. The value will be +/// converted to a string using its `to_string` method before being written to +/// the buffer. +/// +/// Example: +/// +/// ```moonbit +/// test "write_object" { +/// let buf = @buffer.new() +/// buf.write_object(42) +/// inspect!(buf.contents().to_unchecked_string(), content="42") +/// } +/// ``` pub fn write_object(self : T, value : &Show) -> Unit { self.write_string(value.to_string()) } ///| +/// Writes a sequence of bytes into the buffer. +/// +/// Parameters: +/// +/// * `buffer` : An extensible buffer to write into. +/// * `bytes` : The sequence of bytes to be written. +/// +/// Example: +/// +/// ```moonbit +/// test "write_bytes" { +/// let buf = @buffer.new() +/// buf.write_bytes(b"Test") +/// inspect!( +/// buf.contents(), +/// content= +/// #|b"\x54\x65\x73\x74" +/// , +/// ) +/// } +/// ``` pub fn write_bytes(self : T, value : Bytes) -> Unit { let val_len = value.length() self.grow_if_necessary(self.len + val_len) @@ -187,7 +559,31 @@ pub fn write_bytes(self : T, value : Bytes) -> Unit { } ///| -/// Write a sub-string into buffer. +/// Writes a portion of a string into the buffer in UTF-16LE encoding. +/// +/// Parameters: +/// +/// * `self` : The buffer to write to. +/// * `str` : The source string from which the substring will be taken. +/// * `offset` : The starting position in the source string (inclusive). Must be +/// non-negative. +/// * `count` : The number of characters to write. Must be non-negative and +/// `offset + count` must not exceed the length of the source string. +/// +/// Example: +/// +/// ```moonbit +/// test "write_substring" { +/// let buf = @buffer.new() +/// buf.write_substring("Hello, World!", 0, 5) +/// inspect!( +/// buf.contents(), +/// content= +/// #|b"\x48\x00\x65\x00\x6c\x00\x6c\x00\x6f\x00" +/// , +/// ) +/// } +/// ``` pub fn write_substring( self : T, value : String, @@ -201,7 +597,25 @@ pub fn write_substring( } ///| -/// Write a char into buffer. +/// Writes a UTF-16LE encoded character into the buffer. Automatically grows the +/// buffer if necessary. +/// +/// Parameters: +/// +/// * `buffer` : The buffer to write to. +/// * `char` : The character to be written. +/// +/// Example: +/// +/// ```moonbit +/// test "write_char" { +/// let buf = @buffer.new() +/// buf.write_char('A') +/// inspect!(buf.contents(), content= +/// #|b"\x41\x00" +/// ) +/// } +/// ``` pub fn write_char(self : T, value : Char) -> Unit { self.grow_if_necessary(self.len + 4) let inc = self.data.set_utf16le_char(self.len, value) @@ -209,7 +623,23 @@ pub fn write_char(self : T, value : Char) -> Unit { } ///| -/// Write a byte into buffer. +/// Writes a single byte to the end of the buffer. The buffer will automatically +/// grow if necessary to accommodate the new byte. +/// +/// Parameters: +/// +/// * `buffer` : The buffer to write to. +/// * `byte` : The byte value to be written. +/// +/// Example: +/// +/// ```moonbit +/// test "write_byte" { +/// let buf = @buffer.new() +/// buf.write_byte(b'\x41') +/// inspect!(buf.contents(), content="b\"\\x41\"") +/// } +/// ``` pub fn write_byte(self : T, value : Byte) -> Unit { self.grow_if_necessary(self.len + 1) self.data[self.len] = value @@ -217,12 +647,49 @@ pub fn write_byte(self : T, value : Byte) -> Unit { } ///| +/// Resets the buffer to its initial state by restoring its initial capacity and +/// clearing its contents. +/// +/// Parameters: +/// +/// * `self` : The buffer to be reset. +/// +/// Example: +/// +/// ```moonbit +/// test "reset" { +/// let buf = @buffer.new(size_hint=4) +/// buf.write_string("Test") +/// buf.reset() +/// inspect!(buf.length(), content="0") +/// inspect!(buf.is_empty(), content="true") +/// } +/// ``` pub fn reset(self : T) -> Unit { self.data = self.initial_data self.len = 0 } ///| +/// Returns a copy of the buffer's contents as a `Bytes` object. The returned +/// bytes will have the same length as the buffer. +/// +/// Parameters: +/// +/// * `buffer` : The buffer whose contents will be converted to bytes. +/// +/// Returns a `Bytes` object containing a copy of the buffer's contents. +/// +/// Example: +/// +/// ```moonbit +/// test "to_bytes" { +/// let buf = @buffer.new() +/// buf.write_string("Test") +/// let bytes = buf.to_bytes() +/// inspect!(bytes.length(), content="8") +/// } +/// ``` pub fn to_bytes(self : T) -> Bytes { Bytes::from_fixedarray(self.data, len=self.len) } diff --git a/buffer/deprecated.mbt b/buffer/deprecated.mbt index 8e2917a43..b3799c49d 100644 --- a/buffer/deprecated.mbt +++ b/buffer/deprecated.mbt @@ -14,7 +14,7 @@ ///| /// Return a new string contains the data in buffer. -/// +/// /// @alert deprecated "Use `Buffer::contents` to read the contents of the buffer" /// @coverage.skip pub fn to_string(self : T) -> String { @@ -23,9 +23,9 @@ pub fn to_string(self : T) -> String { ///| /// Return a new unchecked string contains the data in buffer. -/// Note this function does not validate the encoding of the byte sequence, +/// Note this function does not validate the encoding of the byte sequence, /// it simply copy the bytes into a new String. -/// +/// /// @alert deprecated "Use `Buffer::contents` to read the contents of the buffer" /// @coverage.skip pub fn to_unchecked_string(self : T) -> String { @@ -33,7 +33,16 @@ pub fn to_unchecked_string(self : T) -> String { } ///| -/// Write a sub-string into buffer. +/// Write a substring from a given string into the buffer. This is a deprecated +/// function, use `write_substring` instead. +/// +/// Parameters: +/// +/// * `self` : The buffer to write into. +/// * `string` : The source string from which to extract the substring. +/// * `offset` : The starting position in the source string (inclusive). +/// * `length` : The number of characters to write from the starting position. +/// /// @alert deprecated "Use `Buffer::write_substring` instead" /// @coverage.skip pub fn write_sub_string( From 06158634001448c29dbdc8608c95cc4e2c0d4854 Mon Sep 17 00:00:00 2001 From: Haoxiang Fei Date: Wed, 8 Jan 2025 18:14:04 +0800 Subject: [PATCH 03/17] doc: double --- double/double.mbt | 239 ++++++++++++++++++++++++++++++++++++++++-- double/exp_js.mbt | 27 +++++ double/exp_nonjs.mbt | 26 +++++ double/log.mbt | 59 +++++++++++ double/mod_js.mbt | 23 ++++ double/mod_nonjs.mbt | 26 ++++- double/pow_js.mbt | 24 +++++ double/pow_nonjs.mbt | 27 ++++- double/round.mbt | 74 ++++++++++++- double/round_js.mbt | 74 ++++++++++++- double/round_wasm.mbt | 74 ++++++++++++- 11 files changed, 655 insertions(+), 18 deletions(-) diff --git a/double/double.mbt b/double/double.mbt index 51695a072..68ae93d91 100644 --- a/double/double.mbt +++ b/double/double.mbt @@ -31,11 +31,47 @@ pub let min_value : Double = 0xFFEFFFFFFFFFFFFFL.reinterpret_as_double() pub let min_positive : Double = 0x0010000000000000L.reinterpret_as_double() ///| +/// Converts an integer to a double-precision floating-point number. +/// +/// Parameters: +/// +/// * `integer` : The integer value to be converted. +/// +/// Returns a double-precision floating-point number representing the given +/// integer value. +/// +/// Example: +/// +/// ```moonbit +/// test "Double::from_int" { +/// inspect!(Double::from_int(42), content="42") +/// inspect!(Double::from_int(-1), content="-1") +/// } +/// ``` pub fn Double::from_int(i : Int) -> Double { i.to_double() } ///| +/// Returns the absolute value of a double-precision floating-point number. +/// +/// Parameters: +/// +/// * `value` : The double-precision floating-point number to compute the +/// absolute value of. +/// +/// Returns the absolute value of the input number. For any input `x`, the result +/// is equivalent to `if x < 0.0 { -x } else { x }`. +/// +/// Example: +/// +/// ```moonbit +/// test "abs" { +/// inspect!((-2.5).abs(), content="2.5") +/// inspect!(3.14.abs(), content="3.14") +/// inspect!(0.0.abs(), content="0") +/// } +/// ``` pub fn abs(self : Double) -> Double = "%f64.abs" ///| @@ -53,8 +89,22 @@ pub fn signum(self : Double) -> Double { } } -///| Returns a "not-a-number" value -/// +///| +/// \[DEPRECATED] Returns a "not-a-number" (NaN) value. +/// +/// This function is deprecated. Use `@double.not_a_number` instead. +/// +/// Returns a double-precision floating-point NaN value. +/// +/// Example: +/// +/// ```moonbit +/// test "Double::nan" { +/// let nan = @double.not_a_number +/// inspect!(nan.is_nan(), content="true") +/// } +/// ``` +/// /// @alert deprecated "Use `@double.not_a_number` instead" /// @coverage.skip pub fn Double::nan() -> Double { @@ -74,7 +124,21 @@ pub fn Double::inf(sign : Int) -> Double { } ///| -/// Returns the smallest positive normal value of Double +/// Returns the smallest positive normal value of a double-precision +/// floating-point number. +/// +/// Returns a `Double` value that represents the smallest positive normal number +/// that can be represented by a double-precision floating-point number +/// (approximately 2.2250738585072014e-308). +/// +/// Example: +/// +/// ```moonbit +/// test "Double::min_normal" { +/// inspect!(@double.min_positive, content="2.2250738585072014e-308") +/// } +/// ``` +/// /// @alert deprecated "Use `@double.min_positive` instead" /// @coverage.skip pub fn Double::min_normal() -> Double { @@ -82,26 +146,93 @@ pub fn Double::min_normal() -> Double { } ///| -/// Check whether the double is a "not-a-number" value +/// Checks whether a double-precision floating-point number represents a "Not a +/// Number" (NaN) value. +/// +/// Parameters: +/// +/// * `number` : A double-precision floating-point value to be checked. +/// +/// Returns `true` if the number is NaN, `false` otherwise. +/// +/// Example: +/// +/// ```moonbit +/// test "is_nan" { +/// inspect!(not_a_number.is_nan(), content="true") +/// inspect!(42.0.is_nan(), content="false") +/// inspect!((0.0 / 0.0).is_nan(), content="true") +/// } +/// ``` pub fn is_nan(self : Double) -> Bool { // only NaNs satisfy f != f. self != self } ///| -/// Check whether the double is infinity +/// Checks whether a double-precision floating-point number represents positive +/// or negative infinity. +/// +/// Parameters: +/// +/// * `value` : The double-precision floating-point number to check. +/// +/// Returns `true` if the value is either positive or negative infinity, `false` +/// otherwise. +/// +/// Example: +/// +/// ```moonbit +/// test "is_inf" { +/// inspect!(infinity.is_inf(), content="true") +/// inspect!(neg_infinity.is_inf(), content="true") +/// inspect!(42.0.is_inf(), content="false") +/// } +/// ``` pub fn is_inf(self : Double) -> Bool { self > max_value || self < min_value } ///| -/// Check whether the double is positive infinity +/// Checks whether a double-precision floating-point number is positive infinity. +/// +/// Parameters: +/// +/// * `value` : The double-precision floating-point number to check. +/// +/// Returns `true` if the number is positive infinity, `false` otherwise. +/// +/// Example: +/// +/// ```moonbit +/// test "is_pos_inf" { +/// inspect!(infinity.is_pos_inf(), content="true") +/// inspect!(neg_infinity.is_pos_inf(), content="false") +/// inspect!(42.0.is_pos_inf(), content="false") +/// } +/// ``` pub fn is_pos_inf(self : Double) -> Bool { self > max_value } ///| -/// Check whether the double is negative infinity +/// Checks whether a double-precision floating-point number is negative infinity. +/// +/// Parameters: +/// +/// * `self` : The double-precision floating-point number to check. +/// +/// Returns a boolean value indicating whether the number is negative infinity. +/// +/// Example: +/// +/// ```moonbit +/// test "is_neg_inf" { +/// inspect!((-1.0 / 0.0).is_neg_inf(), content="true") +/// inspect!(42.0.is_neg_inf(), content="false") +/// inspect!((1.0 / 0.0).is_neg_inf(), content="false") // positive infinity +/// } +/// ``` pub fn is_neg_inf(self : Double) -> Bool { self < min_value } @@ -170,6 +301,27 @@ pub impl Hash for Double with hash_combine(self, hasher) { } ///| +/// Converts a double-precision floating-point number to its string +/// representation. +/// +/// Parameters: +/// +/// * `self`: The double-precision floating-point number to be converted. +/// +/// Returns a string representation of the double-precision floating-point +/// number. +/// +/// Example: +/// +/// ```moonbit +/// test "Double::to_string" { +/// inspect!(42.0.to_string(), content="42") +/// inspect!(3.14159.to_string(), content="3.14159") +/// inspect!((-0.0).to_string(), content="0") +/// inspect!(not_a_number.to_string(), content="NaN") +/// } +/// ``` +/// /// @intrinsic %f64.to_string pub fn to_string(self : Double) -> String { @ryu.ryu_to_string(self) @@ -181,6 +333,35 @@ pub impl Show for Double with output(self, logger) { } ///| +/// Determines whether two floating-point numbers are approximately equal within +/// specified tolerances. +/// The implementation follows the algorithm described in PEP 485 for Python's +/// `math.isclose()`. +/// +/// Parameters: +/// +/// * `self` : The first floating-point number to compare. +/// * `other` : The second floating-point number to compare. +/// * `relative_tolerance` : The relative tolerance for the comparison. Must be +/// non-negative. Defaults to 1e-9. +/// * `absolute_tolerance` : The absolute tolerance for the comparison. Must be +/// non-negative. Defaults to 0.0. +/// +/// Returns whether the two numbers are considered approximately equal. Returns +/// `true` if the numbers are exactly equal or if they are within either the +/// relative or absolute tolerance. Returns `false` if either number is infinite. +/// +/// Example: +/// +/// ```moonbit +/// test "is_close" { +/// let x = 1.0 +/// let y = 1.000000001 +/// inspect!(x.is_close(y), content="false") +/// inspect!(x.is_close(y, relative_tolerance=1.0e-10), content="false") +/// inspect!(infinity.is_close(infinity), content="true") +/// } +/// ``` pub fn is_close( self : Double, other : Double, @@ -204,12 +385,52 @@ pub fn is_close( diff <= absolute_tolerance } -///| Converts the Double to a Bytes in big-endian byte order. +///| +/// Converts a double-precision floating-point number to a sequence of bytes in +/// big-endian byte order (most significant byte first). +/// +/// Parameters: +/// +/// * `self` : The double-precision floating-point number to be converted. +/// +/// Returns a sequence of 8 bytes representing the double-precision +/// floating-point number in big-endian byte order. +/// +/// Example: +/// +/// ```moonbit +/// test "to_be_bytes" { +/// let d = 1.0 +/// inspect!(d.to_be_bytes(), content= +/// #|b"\x3f\xf0\x00\x00\x00\x00\x00\x00" +/// ) +/// } +/// ``` pub fn to_be_bytes(self : Double) -> Bytes { self.reinterpret_as_uint64().to_be_bytes() } -///| Converts the Double to a Bytes in little-endian byte order. +///| +/// Converts a double-precision floating-point number to a sequence of bytes in +/// little-endian order (least significant byte first). +/// +/// Parameters: +/// +/// * `self` : A double-precision floating-point number to be converted. +/// +/// Returns a sequence of 8 bytes representing the double-precision +/// floating-point number in little-endian order. +/// +/// Example: +/// +/// ```moonbit +/// test "to_le_bytes" { +/// let d = 1.0 +/// inspect!(d.to_le_bytes(), content= +/// #|b"\x00\x00\x00\x00\x00\x00\xf0\x3f" +/// ) +/// } +/// ``` pub fn to_le_bytes(self : Double) -> Bytes { self.reinterpret_as_uint64().to_le_bytes() } diff --git a/double/exp_js.mbt b/double/exp_js.mbt index a41dc0b29..715ed83e8 100644 --- a/double/exp_js.mbt +++ b/double/exp_js.mbt @@ -13,4 +13,31 @@ // limitations under the License. ///| +/// Computes `e` raised to the power of a given number. +/// +/// Parameters: +/// +/// * `self` : The exponent value to compute `e^x`. +/// +/// Returns the value of `e^x`, where `e` is Euler's number (approximately +/// 2.71828). +/// +/// Special cases: +/// +/// * Returns `x` if `x` is `infinity` (positive infinity) +/// * Returns `0` if `x` is negative infinity +/// * Returns `NaN` if `x` is `NaN` +/// * Returns `1` if `x` is `0` +/// +/// Example: +/// +/// ```moonbit +/// test "exp" { +/// inspect!(0.0.exp(), content="1") +/// inspect!(1.0.exp(), content="2.718281828459045") +/// inspect!((-1.0).exp(), content="0.36787944117144233") +/// inspect!(not_a_number.exp().is_nan(), content="true") +/// } +/// ``` +/// ``` pub fn exp(self : Double) -> Double = "Math" "exp" diff --git a/double/exp_nonjs.mbt b/double/exp_nonjs.mbt index d5d1ea6b1..90fb0c48e 100644 --- a/double/exp_nonjs.mbt +++ b/double/exp_nonjs.mbt @@ -13,6 +13,32 @@ // limitations under the License. ///| +/// Computes `e` raised to the power of a given number. +/// +/// Parameters: +/// +/// * `self` : The exponent value to compute `e^x`. +/// +/// Returns the value of `e^x`, where `e` is Euler's number (approximately +/// 2.71828). +/// +/// Special cases: +/// +/// * Returns `x` if `x` is `infinity` (positive infinity) +/// * Returns `0` if `x` is negative infinity +/// * Returns `NaN` if `x` is `NaN` +/// * Returns `1` if `x` is `0` +/// +/// Example: +/// +/// ```moonbit +/// test "exp" { +/// inspect!(0.0.exp(), content="1") +/// inspect!(1.0.exp(), content="2.718281828459045") +/// inspect!((-1.0).exp(), content="0.36787944117144233") +/// inspect!(not_a_number.exp().is_nan(), content="true") +/// } +/// ``` pub fn exp(self : Double) -> Double { fn get_high_word(x : Double) -> UInt { (x.reinterpret_as_uint64() >> 32).to_uint() diff --git a/double/log.mbt b/double/log.mbt index 3352d7e85..3172cc586 100644 --- a/double/log.mbt +++ b/double/log.mbt @@ -71,6 +71,29 @@ fn frexp(f : Double) -> (Double, Int) { } ///| +/// Calculates the natural logarithm of a double-precision floating-point number. +/// +/// Parameters: +/// +/// * `self`: The input number. +/// +/// Returns the natural logarithm of the input number, with the following special +/// cases: +/// +/// * Returns NaN if the input is NaN or negative +/// * Returns negative infinity if the input is zero +/// * Returns the input if it is positive infinity +/// +/// Example: +/// +/// ```moonbit +/// test "ln" { +/// inspect!(2.0.ln(), content="0.6931471805599453") +/// inspect!(1.0.ln(), content="0") +/// inspect!((-1.0).ln(), content="NaN") +/// inspect!(0.0.ln(), content="-Infinity") +/// } +/// ``` pub fn ln(self : Double) -> Double { if self.is_nan() || self.is_inf() { return self @@ -96,6 +119,23 @@ pub fn ln(self : Double) -> Double { } ///| +/// Calculates the base-2 logarithm of a double-precision floating-point number. +/// +/// Parameters: +/// +/// * `x` : A double-precision floating-point number. +/// +/// Returns the base-2 logarithm of the input number. +/// +/// Example: +/// +/// ```moonbit +/// test "log2" { +/// inspect!(2.0.log2(), content="1") +/// inspect!(0.5.log2(), content="-1") +/// inspect!(3.0.log2(), content="1.584962500721156") +/// } +/// ``` pub fn log2(self : Double) -> Double { let (f, e) = frexp(self) if f == 0.5 { @@ -105,6 +145,25 @@ pub fn log2(self : Double) -> Double { } ///| +/// Calculates the base-10 logarithm of a double-precision floating-point number. +/// +/// Parameters: +/// +/// * `self` : The double-precision floating-point number to calculate the +/// logarithm of. +/// +/// Returns a double-precision floating-point number representing the base-10 +/// logarithm of the input. +/// +/// Example: +/// +/// ```moonbit +/// test "log10" { +/// inspect!(1.0.log10(), content="0") +/// inspect!(10.0.log10(), content="1") +/// inspect!(100.0.log10(), content="2") +/// } +/// ``` pub fn log10(self : Double) -> Double { ln(self) / ln10 } diff --git a/double/mod_js.mbt b/double/mod_js.mbt index 8cc51d316..aa6315dbe 100644 --- a/double/mod_js.mbt +++ b/double/mod_js.mbt @@ -13,5 +13,28 @@ // limitations under the License. ///| +/// Calculates the floating-point remainder when `dividend` is divided by +/// `divisor`. Returns NaN if either operand is NaN, or if `dividend` is +/// infinity, or if `divisor` is zero. +/// +/// Parameters: +/// +/// * `dividend` : The floating-point number to be divided. +/// * `divisor` : The floating-point number used to divide the `dividend`. +/// +/// Returns the remainder of the division. The result has the same sign as the +/// `dividend` and has an absolute value less than the absolute value of +/// `divisor`. +/// +/// Example: +/// +/// ```moonbit +/// test "op_mod" { +/// inspect!(5.0.op_mod(3.0), content="2") +/// inspect!((-5.0).op_mod(3.0), content="-2") +/// inspect!(5.0.op_mod(not_a_number), content="NaN") +/// inspect!(infinity.op_mod(3.0), content="NaN") +/// } +/// ``` pub extern "js" fn op_mod(self : Double, other : Double) -> Double = #| (a, b) => (a % b) diff --git a/double/mod_nonjs.mbt b/double/mod_nonjs.mbt index 3f6fa1964..03338c17b 100644 --- a/double/mod_nonjs.mbt +++ b/double/mod_nonjs.mbt @@ -13,6 +13,29 @@ // limitations under the License. ///| +/// Calculates the floating-point remainder when `dividend` is divided by +/// `divisor`. Returns NaN if either operand is NaN, or if `dividend` is +/// infinity, or if `divisor` is zero. +/// +/// Parameters: +/// +/// * `dividend` : The floating-point number to be divided. +/// * `divisor` : The floating-point number used to divide the `dividend`. +/// +/// Returns the remainder of the division. The result has the same sign as the +/// `dividend` and has an absolute value less than the absolute value of +/// `divisor`. +/// +/// Example: +/// +/// ```moonbit +/// test "op_mod" { +/// inspect!(5.0.op_mod(3.0), content="2") +/// inspect!((-5.0).op_mod(3.0), content="-2") +/// inspect!(5.0.op_mod(not_a_number), content="NaN") +/// inspect!(infinity.op_mod(3.0), content="NaN") +/// } +/// ``` pub fn op_mod(self : Double, other : Double) -> Double { let x = self let y = other @@ -31,7 +54,6 @@ pub fn op_mod(self : Double, other : Double) -> Double { } return x } - // normalize x and y if ex == 0 { i = uint64_x << 12 @@ -55,7 +77,6 @@ pub fn op_mod(self : Double, other : Double) -> Double { uint64_y = uint64_y & (18446744073709551615UL >> 12) uint64_y = uint64_y | (1UL << 52) } - // x mod y while ex > ey { i = uint64_x - uint64_y @@ -79,7 +100,6 @@ pub fn op_mod(self : Double, other : Double) -> Double { uint64_x = uint64_x << 1 ex -= 1 } - // scale result if ex > 0 { uint64_x = uint64_x - (1UL << 52) diff --git a/double/pow_js.mbt b/double/pow_js.mbt index ca65214eb..d9075166a 100644 --- a/double/pow_js.mbt +++ b/double/pow_js.mbt @@ -13,4 +13,28 @@ // limitations under the License. ///| +/// Calculates the power of a number by raising the base to the specified +/// exponent. Handles special cases and edge conditions according to IEEE 754 +/// standards. +/// +/// Parameters: +/// +/// * `base` : The base number to be raised to a power. +/// * `exponent` : The power to raise the base number to. +/// +/// Returns the result of raising `base` to the power of `exponent`. +/// +/// Example: +/// +/// ```moonbit +/// test "pow" { +/// let x = 2.0 +/// inspect!(x.pow(3.0), content="8") +/// inspect!(x.pow(0.5), content="1.4142135623730951") +/// inspect!(x.pow(0.0), content="1") +/// inspect!((-1.0).pow(2.0), content="1") +/// inspect!(0.0.pow(0.0), content="1") +/// inspect!(infinity.pow(-1.0), content="0") +/// } +/// ``` pub fn pow(self : Double, other : Double) -> Double = "Math" "pow" diff --git a/double/pow_nonjs.mbt b/double/pow_nonjs.mbt index 0aabf0c91..6918e0461 100644 --- a/double/pow_nonjs.mbt +++ b/double/pow_nonjs.mbt @@ -108,6 +108,30 @@ const POW_ivln2_h = 1.44269502162933349609e+00 const POW_ivln2_l = 1.92596299112661746887e-08 ///| +/// Calculates the power of a number by raising the base to the specified +/// exponent. Handles special cases and edge conditions according to IEEE 754 +/// standards. +/// +/// Parameters: +/// +/// * `base` : The base number to be raised to a power. +/// * `exponent` : The power to raise the base number to. +/// +/// Returns the result of raising `base` to the power of `exponent`. +/// +/// Example: +/// +/// ```moonbit +/// test "pow" { +/// let x = 2.0 +/// inspect!(x.pow(3.0), content="8") +/// inspect!(x.pow(0.5), content="1.4142135623730951") +/// inspect!(x.pow(0.0), content="1") +/// inspect!((-1.0).pow(2.0), content="1") +/// inspect!(0.0.pow(0.0), content="1") +/// inspect!(infinity.pow(-1.0), content="0") +/// } +/// ``` pub fn pow(self : Double, other : Double) -> Double { fn set_low_word(d : Double, v : UInt) -> Double { let bits : UInt64 = d.reinterpret_as_uint64() @@ -156,7 +180,6 @@ pub fn pow(self : Double, other : Double) -> Double { let mut k : Int = 0 let mut yisint : Int = 0 let mut n : Int = 0 - // int hx, hy, ix, iy; // unsigned lx, ly; // @@ -170,7 +193,6 @@ pub fn pow(self : Double, other : Double) -> Double { let ly : UInt = (y.reinterpret_as_uint64() & 0xFFFFFFFF).to_uint() let mut ix : Int = hx & 0x7FFFFFFF let iy : Int = hy & 0x7FFFFFFF - // y==zero: x**0 = 1 if (iy.reinterpret_as_uint() | ly) == 0 { return ONE @@ -360,7 +382,6 @@ pub fn pow(self : Double, other : Double) -> Double { t1 = set_low_word(t1, 0) t2 = z_l - (t1 - t - pow_dp_h[k] - z_h) } - // split up y into y1+y2 and compute (y1+y2)*(t1+t2) y1 = y y1 = set_low_word(y1, 0) diff --git a/double/round.mbt b/double/round.mbt index 970b4b040..14050d016 100644 --- a/double/round.mbt +++ b/double/round.mbt @@ -25,6 +25,24 @@ let exp_bits = 11 let frac_bits = 52 ///| +/// Returns an integer value by discarding the decimal part of the floating-point +/// number (truncation toward zero). +/// +/// Parameters: +/// +/// * `self` : The floating-point number to be truncated. +/// +/// Returns a floating-point number representing the integer part of the input. +/// +/// Example: +/// +/// ```moonbit +/// test "trunc" { +/// inspect!(3.7.trunc(), content="3") +/// inspect!((-3.7).trunc(), content="-3") +/// inspect!(0.0.trunc(), content="0") +/// } +/// ``` pub fn trunc(self : Double) -> Double { let u64 = self.reinterpret_as_uint64() let biased_exp = ((u64 >> frac_bits) & ((0x1UL << exp_bits) - 1)).to_int() @@ -39,6 +57,23 @@ pub fn trunc(self : Double) -> Double { } ///| +/// Returns the smallest integer greater than or equal to the given number. +/// +/// Parameters: +/// +/// * `self` : The floating point number to find the ceiling of. +/// +/// Returns the ceiling value of the input number. +/// +/// Example: +/// +/// ```moonbit +/// test "ceil" { +/// inspect!(3.7.ceil(), content="4") +/// inspect!((-3.7).ceil(), content="-3") +/// inspect!(42.0.ceil(), content="42") +/// } +/// ``` pub fn ceil(self : Double) -> Double { let trunced = self.trunc() if self > trunced { @@ -49,6 +84,24 @@ pub fn ceil(self : Double) -> Double { } ///| +/// Returns the largest integer less than or equal to the given number. +/// +/// Parameters: +/// +/// * `number` : A floating-point number to be rounded down. +/// +/// Returns a double-precision floating-point number representing the largest +/// integer less than or equal to the input. +/// +/// Example: +/// +/// ```moonbit +/// test "floor" { +/// inspect!(3.7.floor(), content="3") +/// inspect!((-3.7).floor(), content="-4") +/// inspect!(0.0.floor(), content="0") +/// } +/// ``` pub fn floor(self : Double) -> Double { let trunced = self.trunc() if self < trunced { @@ -59,7 +112,26 @@ pub fn floor(self : Double) -> Double { } ///| -// Round to nearest, ties to Ceiling +/// Rounds a floating-point number to the nearest integer using "round half up" +/// rule. In this rule, when a number is halfway between two integers (like 3.5), +/// it is rounded up to the next integer. +/// +/// Parameters: +/// +/// * `value` : The floating-point number to be rounded. +/// +/// Returns the rounded value as a double-precision floating-point number. +/// +/// Example: +/// +/// ```moonbit +/// test "round" { +/// inspect!(3.7.round(), content="4") +/// inspect!(3.2.round(), content="3") +/// inspect!(3.5.round(), content="4") +/// inspect!((-3.5).round(), content="-3") +/// } +/// ``` pub fn round(self : Double) -> Double { floor(self + 0.5) } diff --git a/double/round_js.mbt b/double/round_js.mbt index cfc4b7cd1..960adcdf1 100644 --- a/double/round_js.mbt +++ b/double/round_js.mbt @@ -13,14 +13,86 @@ // limitations under the License. ///| +/// Returns an integer value by discarding the decimal part of the floating-point +/// number (truncation toward zero). +/// +/// Parameters: +/// +/// * `self` : The floating-point number to be truncated. +/// +/// Returns a floating-point number representing the integer part of the input. +/// +/// Example: +/// +/// ```moonbit +/// test "trunc" { +/// inspect!(3.7.trunc(), content="3") +/// inspect!((-3.7).trunc(), content="-3") +/// inspect!(0.0.trunc(), content="0") +/// } +/// ``` pub fn trunc(self : Double) -> Double = "Math" "trunc" ///| +/// Returns the smallest integer greater than or equal to the given number. +/// +/// Parameters: +/// +/// * `self` : The floating point number to find the ceiling of. +/// +/// Returns the ceiling value of the input number. +/// +/// Example: +/// +/// ```moonbit +/// test "ceil" { +/// inspect!(3.7.ceil(), content="4") +/// inspect!((-3.7).ceil(), content="-3") +/// inspect!(42.0.ceil(), content="42") +/// } +/// ``` pub fn ceil(self : Double) -> Double = "Math" "ceil" ///| +/// Returns the largest integer less than or equal to the given number. +/// +/// Parameters: +/// +/// * `number` : A floating-point number to be rounded down. +/// +/// Returns a double-precision floating-point number representing the largest +/// integer less than or equal to the input. +/// +/// Example: +/// +/// ```moonbit +/// test "floor" { +/// inspect!(3.7.floor(), content="3") +/// inspect!((-3.7).floor(), content="-4") +/// inspect!(0.0.floor(), content="0") +/// } +/// ``` pub fn floor(self : Double) -> Double = "Math" "floor" ///| -// Round to nearest, ties to Ceiling +/// Rounds a floating-point number to the nearest integer using "round half up" +/// rule. In this rule, when a number is halfway between two integers (like 3.5), +/// it is rounded up to the next integer. +/// +/// Parameters: +/// +/// * `value` : The floating-point number to be rounded. +/// +/// Returns the rounded value as a double-precision floating-point number. +/// +/// Example: +/// +/// ```moonbit +/// test "round" { +/// inspect!(3.7.round(), content="4") +/// inspect!(3.2.round(), content="3") +/// inspect!(3.5.round(), content="4") +/// inspect!((-3.5).round(), content="-3") +/// } +/// ``` pub fn round(self : Double) -> Double = "Math" "round" diff --git a/double/round_wasm.mbt b/double/round_wasm.mbt index b25604be5..0f263356c 100644 --- a/double/round_wasm.mbt +++ b/double/round_wasm.mbt @@ -13,16 +13,88 @@ // limitations under the License. ///| +/// Returns an integer value by discarding the decimal part of the floating-point +/// number (truncation toward zero). +/// +/// Parameters: +/// +/// * `self` : The floating-point number to be truncated. +/// +/// Returns a floating-point number representing the integer part of the input. +/// +/// Example: +/// +/// ```moonbit +/// test "trunc" { +/// inspect!(3.7.trunc(), content="3") +/// inspect!((-3.7).trunc(), content="-3") +/// inspect!(0.0.trunc(), content="0") +/// } +/// ``` pub fn trunc(self : Double) -> Double = "(func (param $d f64) (result f64) (f64.trunc (local.get $d)))" ///| +/// Returns the smallest integer greater than or equal to the given number. +/// +/// Parameters: +/// +/// * `self` : The floating point number to find the ceiling of. +/// +/// Returns the ceiling value of the input number. +/// +/// Example: +/// +/// ```moonbit +/// test "ceil" { +/// inspect!(3.7.ceil(), content="4") +/// inspect!((-3.7).ceil(), content="-3") +/// inspect!(42.0.ceil(), content="42") +/// } +/// ``` pub fn ceil(self : Double) -> Double = "(func (param $d f64) (result f64) (f64.ceil (local.get $d)))" ///| +/// Returns the largest integer less than or equal to the given number. +/// +/// Parameters: +/// +/// * `number` : A floating-point number to be rounded down. +/// +/// Returns a double-precision floating-point number representing the largest +/// integer less than or equal to the input. +/// +/// Example: +/// +/// ```moonbit +/// test "floor" { +/// inspect!(3.7.floor(), content="3") +/// inspect!((-3.7).floor(), content="-4") +/// inspect!(0.0.floor(), content="0") +/// } +/// ``` pub fn floor(self : Double) -> Double = "(func (param $d f64) (result f64) (f64.floor (local.get $d)))" ///| -// Round to nearest, ties to Ceiling +/// Rounds a floating-point number to the nearest integer using "round half up" +/// rule. In this rule, when a number is halfway between two integers (like 3.5), +/// it is rounded up to the next integer. +/// +/// Parameters: +/// +/// * `value` : The floating-point number to be rounded. +/// +/// Returns the rounded value as a double-precision floating-point number. +/// +/// Example: +/// +/// ```moonbit +/// test "round" { +/// inspect!(3.7.round(), content="4") +/// inspect!(3.2.round(), content="3") +/// inspect!(3.5.round(), content="4") +/// inspect!((-3.5).round(), content="-3") +/// } +/// ``` pub fn round(self : Double) -> Double { floor(self + 0.5) } From 98fc7bae23aef44370c2546a48d6e473c4b1eb46 Mon Sep 17 00:00:00 2001 From: Haoxiang Fei Date: Thu, 9 Jan 2025 15:05:20 +0800 Subject: [PATCH 04/17] doc: deque (#1443) * doc: deque * update README.md --- deque/README.md | 7 +- deque/deque.mbt | 324 +++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 312 insertions(+), 19 deletions(-) diff --git a/deque/README.md b/deque/README.md index 093d72a89..85e8aa1fd 100644 --- a/deque/README.md +++ b/deque/README.md @@ -109,14 +109,13 @@ dv.front() // Some(2) dv.length() // 3 ``` -If you only want to pop an element without getting the return value, you can use `pop_front_exn()` with `pop_back_exn()`. -These two functions will panic if the queue is empty. +If you only want to pop an element without getting the return value, you can use `unsafe_pop_front()` with `unsafe_pop_back()`. These two functions will panic if the queue is empty. ```moonbit let dv = @deque.of([1, 2, 3, 4, 5]) -dv.pop_front_exn() +dv.unsafe_pop_front() dv.front() // Some(2) -dv.pop_back_exn() +dv.unsafe_pop_back() dv.back() // Some(3) ``` diff --git a/deque/deque.mbt b/deque/deque.mbt index f118d0785..71731f5bc 100644 --- a/deque/deque.mbt +++ b/deque/deque.mbt @@ -16,7 +16,31 @@ fn set_null[T](buffer : UninitializedArray[T], index : Int) = "%fixedarray.set_null" ///| -/// Creates a new, empty deque. +/// Creates a new empty deque with an optional initial capacity. +/// +/// Parameters: +/// +/// * `capacity` : The initial capacity of the deque. If not specified, defaults +/// to 0 and will be automatically adjusted as elements are added. +/// +/// Returns a new empty deque of type `T[A]` where `A` is the type of elements +/// the deque will hold. +/// +/// Example: +/// +/// ```moonbit +/// test "T::new" { +/// let dq : @deque.T[Int] = @deque.T::new() +/// inspect!(dq.length(), content="0") +/// inspect!(dq.capacity(), content="0") +/// } +/// +/// test "T::new/with_capacity" { +/// let dq : @deque.T[Int] = @deque.T::new(capacity=10) +/// inspect!(dq.length(), content="0") +/// inspect!(dq.capacity(), content="10") +/// } +/// ``` pub fn T::new[A](capacity~ : Int = 0) -> T[A] { T::{ buf: UninitializedArray::make(capacity), len: 0, head: 0, tail: 0 } } @@ -27,7 +51,24 @@ pub impl[A : Show] Show for T[A] with output(self, logger) { } ///| -/// Creates a new deque from an array. +/// Creates a new deque with elements copied from an array. +/// +/// Parameters: +/// +/// * `array` : The array to initialize the deque with. All elements from the +/// array will be copied into the new deque in the same order. +/// +/// Returns a new deque containing all elements from the input array. +/// +/// Example: +/// +/// ```moonbit +/// test "T::from_array" { +/// let arr = [1, 2, 3, 4, 5] +/// let dq = @deque.T::from_array(arr) +/// inspect!(dq, content="@deque.of([1, 2, 3, 4, 5])") +/// } +/// ``` pub fn T::from_array[A](arr : Array[A]) -> T[A] { let deq = T::{ buf: UninitializedArray::make(arr.length()), @@ -42,7 +83,26 @@ pub fn T::from_array[A](arr : Array[A]) -> T[A] { } ///| -/// Creates a new copy of this deque. +/// Creates a new deque with the same elements as the original deque. The new +/// deque will have a capacity equal to its length, and its elements will be +/// stored contiguously starting from index 0. +/// +/// Parameters: +/// +/// * `self` : The deque to be copied. +/// +/// Returns a new deque containing all elements from the original deque in the +/// same order. +/// +/// Example: +/// +/// ```moonbit +/// test "T::copy" { +/// let dq = @deque.of([1, 2, 3, 4, 5]) +/// let copied = dq.copy() +/// inspect!(copied, content="@deque.of([1, 2, 3, 4, 5])") +/// } +/// ``` pub fn T::copy[A](self : T[A]) -> T[A] { let len = self.len let deq = T::{ @@ -58,6 +118,22 @@ pub fn T::copy[A](self : T[A]) -> T[A] { } ///| +/// Creates a new deque from a fixed array, preserving the order of elements. +/// +/// Parameters: +/// +/// * `array` : A fixed-size array containing the initial elements of the deque. +/// +/// Returns a new deque containing all elements from the input array. +/// +/// Example: +/// +/// ```moonbit +/// test "T::of" { +/// let dq = @deque.T::of([1, 2, 3]) +/// inspect!(dq, content="@deque.of([1, 2, 3])") +/// } +/// ``` pub fn T::of[A](arr : FixedArray[A]) -> T[A] { let deq = T::{ buf: UninitializedArray::make(arr.length()), @@ -72,13 +148,48 @@ pub fn T::of[A](arr : FixedArray[A]) -> T[A] { } ///| -/// Returns the deque of elements in the vector. +/// Returns the number of elements in the deque. +/// +/// Parameters: +/// +/// * `deque` : The deque to get the length of. +/// +/// Returns the current number of elements in the deque. +/// +/// Example: +/// +/// ```moonbit +/// test "length" { +/// let dq = @deque.of([1, 2, 3]) +/// inspect!(dq.length(), content="3") +/// dq.push_back(4) +/// inspect!(dq.length(), content="4") +/// } +/// ``` pub fn length[A](self : T[A]) -> Int { self.len } ///| -/// Returns the total number of elements the deque can hold without reallocating. +/// Returns the total number of elements the deque can hold in its internal +/// buffer before requiring reallocation. +/// +/// Parameters: +/// +/// * `deque` : The deque whose capacity is being queried. +/// +/// Returns the current capacity of the deque's internal buffer. +/// +/// Example: +/// +/// ```moonbit +/// test "capacity" { +/// let dq = @deque.new(capacity=10) +/// dq.push_back(1) +/// dq.push_back(2) +/// inspect!(dq.capacity(), content="10") +/// } +/// ``` pub fn capacity[A](self : T[A]) -> Int { self.buf.length() } @@ -219,6 +330,25 @@ pub fn unsafe_pop_front[A](self : T[A]) -> Unit { } ///| +/// Removes and discards the first element from the deque. This function is a +/// deprecated version of `unsafe_pop_front`. +/// +/// Parameters: +/// +/// * `self` : The deque to remove the first element from. +/// +/// Throws a runtime error if the deque is empty. +/// +/// Example: +/// +/// ```moonbit +/// test "pop_front_exn" { +/// let dq = @deque.of([1, 2, 3]) +/// dq.unsafe_pop_front() +/// inspect!(dq, content="@deque.of([2, 3])") +/// } +/// ``` +/// /// @alert deprecated "Use `unsafe_pop_front` instead" /// @coverage.skip pub fn pop_front_exn[A](self : T[A]) -> Unit { @@ -255,6 +385,27 @@ pub fn unsafe_pop_back[A](self : T[A]) -> Unit { } ///| +/// Removes and discards the last element from a deque. +/// +/// Parameters: +/// +/// * `deque` : The deque to remove the last element from. +/// +/// Throws a runtime error if the deque is empty. +/// +/// Example: +/// +/// ```moonbit +/// test "pop_back_exn" { +/// let dq = @deque.of([1, 2, 3]) +/// // Deprecated way: +/// // dq.pop_back_exn() +/// // Recommended way: +/// dq.unsafe_pop_back() +/// inspect!(dq, content="@deque.of([1, 2])") +/// } +/// ``` +/// /// @alert deprecated "Use `unsafe_pop_back` instead" /// @coverage.skip pub fn pop_back_exn[A](self : T[A]) -> Unit { @@ -413,7 +564,27 @@ pub fn T::as_views[A](self : T[A]) -> (ArrayView[A], ArrayView[A]) { } ///| -/// Compares two deques for equality. +/// Compares two deques for equality. Returns `true` if both deques contain the +/// same elements in the same order. +/// +/// Parameters: +/// +/// * `self` : The first deque to compare. +/// * `other` : The second deque to compare with. +/// +/// Returns `true` if both deques are equal, `false` otherwise. +/// +/// Example: +/// +/// ```moonbit +/// test "op_equal" { +/// let dq1 = @deque.of([1, 2, 3]) +/// let dq2 = @deque.of([1, 2, 3]) +/// let dq3 = @deque.of([3, 2, 1]) +/// inspect!(dq1 == dq2, content="true") +/// inspect!(dq1 == dq3, content="false") +/// } +/// ``` pub fn op_equal[A : Eq](self : T[A], other : T[A]) -> Bool { if self.len != other.len { return false @@ -579,11 +750,24 @@ pub fn is_empty[A](self : T[A]) -> Bool { } ///| +/// Searches for a value in the deque and returns its position. /// -/// # Example -/// ``` -/// let dv = @deque.of([3, 4, 5]) -/// assert_eq!(dv.search(3), Some(0)) +/// Parameters: +/// +/// * `self` : The deque to search in. +/// * `value` : The value to search for. +/// +/// Returns the index of the first occurrence of the value in the deque, or +/// `None` if the value is not found. +/// +/// Example: +/// +/// ```moonbit +/// test "search" { +/// let dq = @deque.of([1, 2, 3, 2, 1]) +/// inspect!(dq.search(2), content="Some(1)") +/// inspect!(dq.search(4), content="None") +/// } /// ``` pub fn search[A : Eq](self : T[A], value : A) -> Int? { for i = 0; i < self.len; i = i + 1 { @@ -595,12 +779,24 @@ pub fn search[A : Eq](self : T[A], value : A) -> Int? { } ///| -/// Checks if the array contains an element. +/// Tests whether a deque contains a specific element. /// -/// # Example -/// ``` -/// let dv = @deque.of([3, 4, 5]) -/// assert_true!(dv.contains(3)) +/// Parameters: +/// +/// * `self` : The deque to search in. +/// * `value` : The element to search for. +/// +/// Returns `true` if the deque contains the specified element, `false` +/// otherwise. +/// +/// Example: +/// +/// ```moonbit +/// test "T::contains" { +/// let dq = @deque.of([1, 2, 3, 4, 5]) +/// inspect!(dq.contains(3), content="true") +/// inspect!(dq.contains(6), content="false") +/// } /// ``` pub fn contains[A : Eq](self : T[A], value : A) -> Bool { self.iter().contains(value) @@ -829,6 +1025,25 @@ pub fn retain[A](self : T[A], f : (A) -> Bool) -> Unit { } ///| +/// Creates an iterator over the elements of the deque, allowing sequential +/// access to its elements in order from front to back. +/// +/// Parameters: +/// +/// * `deque` : The deque to iterate over. +/// +/// Returns an iterator that yields each element in the deque in order. +/// +/// Example: +/// +/// ```moonbit +/// test "T::iter" { +/// let dq = @deque.of([1, 2, 3, 4, 5]) +/// let mut sum = 0 +/// dq.iter().each(fn(x) { sum = sum + x }) +/// inspect!(sum, content="15") +/// } +/// ``` pub fn iter[A](self : T[A]) -> Iter[A] { Iter::new(fn(yield_) { guard not(self.is_empty()) else { IterContinue } @@ -855,6 +1070,27 @@ pub fn iter[A](self : T[A]) -> Iter[A] { } ///| +/// Returns an iterator that yields pairs of indices and elements from the deque +/// in order, starting from the front. +/// +/// Parameters: +/// +/// * `self` : The deque to iterate over. +/// +/// Returns an iterator of type `Iter2[Int, A]` that produces tuples of `(index, +/// element)`, where `index` starts from 0 and increments by 1 for each element, +/// and `element` is the corresponding element from the deque. +/// +/// Example: +/// +/// ```moonbit +/// test "iter2" { +/// let dq = @deque.of([10, 20, 30]) +/// let mut sum = 0 +/// dq.iter2().each(fn(i, x) { sum = sum + i * x }) +/// inspect!(sum, content="80") // 0*10 + 1*20 + 2*30 = 80 +/// } +/// ``` pub fn iter2[A](self : T[A]) -> Iter2[Int, A] { Iter2::new(fn(yield_) { guard not(self.is_empty()) else { IterContinue } @@ -882,6 +1118,25 @@ pub fn iter2[A](self : T[A]) -> Iter2[Int, A] { } ///| +/// Creates an iterator that yields elements in reverse order. +/// +/// Parameters: +/// +/// * `self` : The deque to iterate over. +/// +/// Returns an iterator that yields elements from the deque in reverse order, +/// starting from the last element. +/// +/// Example: +/// +/// ```moonbit +/// test "T::rev_iter" { +/// let dq = @deque.of([1, 2, 3]) +/// let mut sum = 0 +/// dq.rev_iter().each(fn(x) { sum = sum * 10 + x }) +/// inspect!(sum, content="321") +/// } +/// ``` pub fn rev_iter[A](self : T[A]) -> Iter[A] { Iter::new(fn(yield_) { guard not(self.is_empty()) else { IterContinue } @@ -908,6 +1163,27 @@ pub fn rev_iter[A](self : T[A]) -> Iter[A] { } ///| +/// Creates an iterator that yields index-value pairs of elements in the deque in +/// reverse order. +/// +/// Parameters: +/// +/// * `self` : The deque to iterate over. +/// +/// Returns an iterator that yields tuples of `(index, value)` pairs, where the +/// index starts from 0 and increments by 1, while values are taken from the +/// deque in reverse order. +/// +/// Example: +/// +/// ```moonbit +/// test "rev_iter2" { +/// let dq = @deque.of([1, 2, 3]) +/// let mut s = "" +/// dq.rev_iter2().each(fn(i, x) { s = s + "\{i}:\{x} " }) +/// inspect!(s, content="0:3 1:2 2:1 ") +/// } +/// ``` pub fn rev_iter2[A](self : T[A]) -> Iter2[Int, A] { Iter2::new(fn(yield_) { guard not(self.is_empty()) else { IterContinue } @@ -935,6 +1211,24 @@ pub fn rev_iter2[A](self : T[A]) -> Iter2[Int, A] { } ///| +/// Creates a new deque containing the elements from the given iterator. +/// +/// Parameters: +/// +/// * `iter` : An iterator containing the elements to be added to the deque. +/// +/// Returns a new deque containing all elements from the iterator in the same +/// order. +/// +/// Example: +/// +/// ```moonbit +/// test "T::from_iter" { +/// let arr = [1, 2, 3, 4, 5] +/// let dq = @deque.T::from_iter(arr.iter()) +/// inspect!(dq, content="@deque.of([1, 2, 3, 4, 5])") +/// } +/// ``` pub fn T::from_iter[A](iter : Iter[A]) -> T[A] { let dq = T::new() iter.each(fn(e) { dq.push_back(e) }) From 369520b0d1eaf9fc7a5cde48a623861e92b6c467 Mon Sep 17 00:00:00 2001 From: Haoxiang Fei Date: Thu, 9 Jan 2025 15:06:38 +0800 Subject: [PATCH 05/17] doc: byte, bytes (#1441) --- byte/byte.mbt | 16 +++++ bytes/bytes.mbt | 169 +++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 168 insertions(+), 17 deletions(-) diff --git a/byte/byte.mbt b/byte/byte.mbt index dc438c978..0df305e93 100644 --- a/byte/byte.mbt +++ b/byte/byte.mbt @@ -19,6 +19,22 @@ pub let max_value : Byte = b'\xFF' pub let min_value : Byte = b'\x00' ///| +/// Converts a byte value to an unsigned 64-bit integer. +/// +/// Parameters: +/// +/// * `byte` : The byte value to be converted. +/// +/// Returns an unsigned 64-bit integer representation of the byte value. +/// +/// Example: +/// +/// ```moonbit +/// test "to_uint64" { +/// let b = b'\xFF' +/// inspect!(b.to_uint64(), content="255") +/// } +/// ``` pub fn to_uint64(self : Byte) -> UInt64 { self.to_uint().to_uint64() } diff --git a/bytes/bytes.mbt b/bytes/bytes.mbt index 73aad9533..758101ea1 100644 --- a/bytes/bytes.mbt +++ b/bytes/bytes.mbt @@ -13,21 +13,54 @@ // limitations under the License. ///| -/// Makes a new Bytes with the given byte array. -/// -/// # Usage -/// +/// Creates a new bytes sequence from a byte array. +/// +/// Parameters: +/// +/// * `array` : An array of bytes to be converted. +/// +/// Returns a new bytes sequence containing the same bytes as the input array. +/// +/// Example: +/// +/// ```moonbit +/// test "Bytes::from_array" { +/// let arr = [b'h', b'i'] +/// let bytes = Bytes::from_array(arr) +/// inspect!(bytes, content= +/// #|b"\x68\x69" +/// ) +/// } /// ``` -/// let b = @bytes.from_array([b'\x41', b'\x42']) -/// assert_eq!(b, b"AB") -/// ``` -/// pub fn Bytes::from_array(arr : Array[Byte]) -> Bytes { Bytes::makei(arr.length(), fn(i) { arr[i] }) } ///| -/// Makes a new Bytes from a fixedarray. +/// Creates a new bytes sequence from a fixed-size array of bytes with an +/// optional length parameter. +/// +/// Parameters: +/// +/// * `array` : A fixed-size array of bytes to be converted into a bytes +/// sequence. +/// * `length` : (Optional) The length of the resulting bytes sequence. If not +/// provided, uses the full length of the input array. +/// +/// Returns a new bytes sequence containing the bytes from the input array. If a +/// length is specified, only includes up to that many bytes. +/// +/// Example: +/// +/// ```moonbit +/// test "Bytes::from_fixedarray" { +/// let arr : FixedArray[Byte] = [b'h', b'e', b'l', b'l', b'o'] +/// let bytes = Bytes::from_fixedarray(arr, len=3) +/// inspect!(bytes, content= +/// #|b"\x68\x65\x6c" +/// ) +/// } +/// ``` pub fn Bytes::from_fixedarray(arr : FixedArray[Byte], len? : Int) -> Bytes { let len = match len { None => arr.length() @@ -37,7 +70,30 @@ pub fn Bytes::from_fixedarray(arr : FixedArray[Byte], len? : Int) -> Bytes { } ///| -/// Converts a Bytes to a fixedarray. +/// Converts a bytes sequence into a fixed-size array of bytes. If an optional +/// length is provided, the resulting array will have exactly that length, +/// otherwise it will match the length of the input bytes. +/// +/// Parameters: +/// +/// * `self` : The bytes sequence to convert. +/// * `len` : Optional. The desired length of the output array. If specified, the +/// resulting array will have this length. If not specified, the length of the +/// input bytes sequence will be used. +/// +/// Returns a fixed-size array containing the bytes from the input sequence. +/// +/// Example: +/// +/// ```moonbit +/// test "Bytes::to_fixedarray" { +/// let bytes = b"hello" +/// let arr = bytes.to_fixedarray() +/// inspect!(arr, content="[b'\\x68', b'\\x65', b'\\x6C', b'\\x6C', b'\\x6F']") +/// let arr2 = bytes.to_fixedarray(len=3) +/// inspect!(arr2, content="[b'\\x68', b'\\x65', b'\\x6C']") +/// } +/// ``` pub fn Bytes::to_fixedarray(self : Bytes, len? : Int) -> FixedArray[Byte] { let len = match len { None => self.length() @@ -51,18 +107,49 @@ pub fn Bytes::to_fixedarray(self : Bytes, len? : Int) -> FixedArray[Byte] { } ///| +/// Creates a new bytes sequence from an iterator of bytes. +/// +/// Parameters: +/// +/// * `iterator` : An iterator that yields bytes. +/// +/// Returns a new bytes sequence containing all the bytes from the iterator. +/// +/// Example: +/// +/// ```moonbit +/// test "Bytes::from_iter" { +/// let iter = Iter::singleton(b'h') +/// let bytes = Bytes::from_iter(iter) +/// inspect!(bytes, content= +/// #|b"\x68" +/// ) +/// } +/// ``` pub fn Bytes::from_iter(iter : Iter[Byte]) -> Bytes { Bytes::from_array(iter.collect()) } ///| -/// Makes a new Bytes with the given byte fixedarray. -/// -/// # Usage -/// -/// ``` -/// let b = @bytes.of([b'\x41', b'\x42']) -/// assert_eq!(b, b"AB") +/// Creates a new bytes sequence from a fixed-size byte array. +/// +/// Parameters: +/// +/// * `array` : A fixed-size array of bytes to be converted into a bytes +/// sequence. Elements in the array should be of type `Byte`. +/// +/// Returns a new bytes sequence containing the same bytes as the input array. +/// +/// Example: +/// +/// ```moonbit +/// test "Bytes::of" { +/// let arr : FixedArray[Byte] = [b'h', b'e', b'l', b'l', b'o'] +/// let bytes = Bytes::of(arr) +/// inspect!(bytes, content= +/// #|b"\x68\x65\x6c\x6c\x6f" +/// ) +/// } /// ``` /// TODO: marked as intrinsic, inline if it is constant pub fn Bytes::of(arr : FixedArray[Byte]) -> Bytes { @@ -70,6 +157,23 @@ pub fn Bytes::of(arr : FixedArray[Byte]) -> Bytes { } ///| +/// Converts a bytes sequence into an array of bytes. +/// +/// Parameters: +/// +/// * `bytes` : A sequence of bytes to be converted into an array. +/// +/// Returns an array containing the same bytes as the input sequence. +/// +/// Example: +/// +/// ```moonbit +/// test "Bytes::to_array" { +/// let bytes = b"hello" +/// let arr = bytes.to_array() +/// inspect!(arr, content="[b'\\x68', b'\\x65', b'\\x6C', b'\\x6C', b'\\x6F']") +/// } +/// ``` pub fn to_array(self : Bytes) -> Array[Byte] { let rv = Array::make(self.length(), b'0') for i = 0; i < self.length(); i = i + 1 { @@ -79,6 +183,24 @@ pub fn to_array(self : Bytes) -> Array[Byte] { } ///| +/// Creates an iterator over the bytes in the sequence. +/// +/// Parameters: +/// +/// * `bytes` : A byte sequence to iterate over. +/// +/// Returns an iterator that yields each byte in the sequence in order. +/// +/// Example: +/// +/// ```moonbit +/// test "Bytes::iter" { +/// let bytes = Bytes::from_array([b'h', b'i']) +/// let mut sum = 0 +/// bytes.iter().each(fn(b) { sum = sum + b.to_int() }) +/// inspect!(sum, content="209") // ASCII values: 'h'(104) + 'i'(105) = 209 +/// } +/// ``` pub fn iter(self : Bytes) -> Iter[Byte] { Iter::new(fn(yield_) { for i = 0, len = self.length(); i < len; i = i + 1 { @@ -92,6 +214,19 @@ pub fn iter(self : Bytes) -> Iter[Byte] { } ///| +/// Creates a new empty bytes sequence. +/// +/// Returns an empty bytes sequence. +/// +/// Example: +/// +/// ```moonbit +/// test "Bytes::default" { +/// let bytes = Bytes::default() +/// inspect!(bytes, content="b\"\"") +/// inspect!(bytes.length(), content="0") +/// } +/// ``` pub fn Bytes::default() -> Bytes { b"" } From 6f6bf51fcc533e2e5602ea3aa56073004a03e70c Mon Sep 17 00:00:00 2001 From: Haoxiang Fei Date: Thu, 9 Jan 2025 15:10:36 +0800 Subject: [PATCH 06/17] doc: builtin/array* (#1440) --- builtin/array.mbt | 1033 ++++++++++++++++++++++++++++------- builtin/array_block.mbt | 106 +++- builtin/arraycore_nonjs.mbt | 70 ++- builtin/arrayview.mbt | 221 +++++++- 4 files changed, 1208 insertions(+), 222 deletions(-) diff --git a/builtin/array.mbt b/builtin/array.mbt index f54ccac58..4e8cd428c 100644 --- a/builtin/array.mbt +++ b/builtin/array.mbt @@ -13,7 +13,25 @@ // limitations under the License. ///| -/// Creates a new array from an array. +/// Creates a new dynamic array from a fixed-size array. +/// +/// Parameters: +/// +/// * `arr` : The fixed-size array to convert. The elements of this array will be +/// copied to the new array. +/// +/// Returns a new dynamic array containing all elements from the input fixed-size +/// array. +/// +/// Example: +/// +/// ```moonbit +/// test "Array::from_fixed_array" { +/// let fixed = FixedArray::make(3, 42) +/// let dynamic = Array::from_fixed_array(fixed) +/// inspect!(dynamic, content="[42, 42, 42]") +/// } +/// ``` pub fn Array::from_fixed_array[T](arr : FixedArray[T]) -> Array[T] { let len = arr.length() let arr2 = Array::make_uninit(len) @@ -22,7 +40,32 @@ pub fn Array::from_fixed_array[T](arr : FixedArray[T]) -> Array[T] { } ///| -/// Creates a new array, with the specified length [len] and element [elem]. +/// Creates a new array with a specified length and initializes all elements with +/// the given value. +/// +/// Parameters: +/// +/// * `length` : The length of the array to create. Must be a non-negative +/// integer. +/// * `initial_value` : The value used to initialize all elements in the array. +/// +/// Returns a new array of type `Array[T]` with `length` elements, where each +/// element is initialized to `initial_value`. +/// +/// Throws an error if `length` is negative. +/// +/// Example: +/// +/// ```moonbit +/// test "Array::make" { +/// let arr = Array::make(3, 42) +/// inspect!(arr, content="[42, 42, 42]") +/// } +/// +/// test "panic Array::make/negative_length" { +/// ignore(Array::make(-1, 0)) +/// } +/// ``` pub fn Array::make[T](len : Int, elem : T) -> Array[T] { let arr = Array::make_uninit(len) for i = 0; i < len; i = i + 1 { @@ -32,27 +75,81 @@ pub fn Array::make[T](len : Int, elem : T) -> Array[T] { } ///| -/// Returns the total number of elements the array can hold without reallocating. +/// Returns the total capacity of the array, which is the number of elements that +/// the array can hold without requiring reallocation of its internal buffer. +/// +/// Parameters: +/// +/// * `array` : The array whose capacity is to be determined. +/// +/// Returns the current capacity of the array as an integer. +/// +/// Example: +/// +/// ```moonbit +/// test "Array::capacity" { +/// let arr = Array::new(capacity=10) +/// arr.push(1) +/// arr.push(2) +/// inspect!(arr.capacity(), content="10") +/// } +/// ``` pub fn capacity[T](self : Array[T]) -> Int { self.buffer()._.length() } ///| -/// @intrinsic %array.unsafe_get +/// Retrieves the element at the specified index from an array without bounds +/// checking. +/// +/// Parameters: +/// +/// * `array` : The array from which to retrieve the element. +/// * `index` : The position in the array from which to retrieve the element. +/// +/// Returns the element at the specified index. +/// +/// Example: +/// +/// ```moonbit +/// test "Array::unsafe_get/basic" { +/// let arr = [1, 2, 3] +/// inspect!(arr.unsafe_get(1), content="2") +/// } +/// ``` +/// /// @alert unsafe "Panic if index is out of bounds" pub fn unsafe_get[T](self : Array[T], idx : Int) -> T { self.buffer()[idx] } ///| -/// Retrieves the element at the specified index from the array. +/// Retrieves an element from the array at the specified index. /// -/// # Example -/// ``` -/// let v = [] -/// v.push(3) -/// assert_eq!(v[0], 3) +/// Parameters: +/// +/// * `array` : The array to get the element from. +/// * `index` : The position in the array from which to retrieve the element. +/// +/// Returns the element at the specified index. +/// +/// Throws a panic if the index is negative or greater than or equal to the +/// length of the array. +/// +/// Example: +/// +/// ```moonbit +/// test "Array::op_get" { +/// let arr = [1, 2, 3] +/// inspect!(arr[1], content="2") +/// } +/// +/// test "panic Array::op_get/out_of_bounds" { +/// let arr = [1, 2, 3] +/// ignore(arr[3]) // Index out of bounds +/// } /// ``` +/// /// @alert unsafe "Panic if index is out of bounds" /// @intrinsic %array.get pub fn op_get[T](self : Array[T], index : Int) -> T { @@ -62,13 +159,25 @@ pub fn op_get[T](self : Array[T], index : Int) -> T { } ///| -/// Retrieves the element at the specified index from the array, or `None` if index is out of bounds +/// Retrieves the element at the specified index from the array. /// -/// # Example -/// ``` -/// let v = [] -/// v.push(3) -/// assert_eq!(v.get(0), Some(3)) +/// Parameters: +/// +/// * `self` : The array to get the element from. +/// * `index` : The position in the array from which to retrieve the element. +/// +/// Returns `Some(element)` if the index is within bounds, or `None` if the index +/// is out of bounds. +/// +/// Example: +/// +/// ```moonbit +/// test "Array::get" { +/// let arr = [1, 2, 3] +/// inspect!(arr.get(-1), content="None") +/// inspect!(arr.get(0), content="Some(1)") +/// inspect!(arr.get(3), content="None") +/// } /// ``` pub fn get[T](self : Array[T], index : Int) -> T? { let len = self.length() @@ -83,14 +192,33 @@ fn unsafe_set[T](self : Array[T], idx : Int, val : T) -> Unit { } ///| -/// Sets the value of the element at the specified index. +/// Sets the element at the specified index in the array to a new value. The +/// original value at that index is overwritten. /// -/// # Example -/// ``` -/// let v = [] -/// v.push(3) -/// assert_eq!(v[0], 3) +/// Parameters: +/// +/// * `array` : The array to modify. +/// * `index` : The position in the array where the value will be set. +/// * `value` : The new value to assign at the specified index. +/// +/// Throws an error if `index` is negative or greater than or equal to the length +/// of the array. +/// +/// Example: +/// +/// ```moonbit +/// test "Array::op_set" { +/// let arr = [1, 2, 3] +/// arr[1] = 42 +/// inspect!(arr, content="[1, 42, 3]") +/// } +/// +/// test "panic Array::op_set/out_of_bounds" { +/// let arr = [1, 2, 3] +/// arr[3] = 42 // Index out of bounds +/// } /// ``` +/// /// @alert unsafe "Panic if index is out of bounds." /// @intrinsic %array.set pub fn op_set[T](self : Array[T], index : Int, value : T) -> Unit { @@ -100,7 +228,27 @@ pub fn op_set[T](self : Array[T], index : Int, value : T) -> Unit { } ///| -/// Compares two arrays for equality. +/// Compares two arrays for equality. Returns true if both arrays have the same +/// length and contain equal elements in the same order. +/// +/// Parameters: +/// +/// * `self` : The first array to compare. +/// * `other` : The second array to compare. +/// +/// Returns true if the arrays are equal, false otherwise. +/// +/// Example: +/// +/// ```moonbit +/// test "Array::op_equal" { +/// let arr1 = [1, 2, 3] +/// let arr2 = [1, 2, 3] +/// let arr3 = [1, 2, 4] +/// inspect!(arr1 == arr2, content="true") +/// inspect!(arr1 == arr3, content="false") +/// } +/// ``` pub fn op_equal[T : Eq](self : Array[T], other : Array[T]) -> Bool { guard self.length() == other.length() else { return false } for i = 0 { @@ -116,6 +264,37 @@ pub fn op_equal[T : Eq](self : Array[T], other : Array[T]) -> Bool { } ///| +/// Compares two arrays lexicographically. +/// +/// First compares the lengths of the arrays. If they differ, returns -1 if the +/// first array is shorter, 1 if it's longer. If the lengths are equal, compares +/// elements pairwise until a difference is found or all elements have been +/// compared. +/// +/// Parameters: +/// +/// * `self` : The first array to compare. +/// * `other` : The second array to compare. +/// +/// Returns an integer that indicates the relative order: +/// +/// * A negative value if `self` is less than `other` +/// * Zero if `self` equals `other` +/// * A positive value if `self` is greater than `other` +/// +/// Example: +/// +/// ```moonbit +/// test "Array::compare" { +/// let arr1 = [1, 2, 3] +/// let arr2 = [1, 2, 4] +/// let arr3 = [1, 2] +/// inspect!(arr1.compare(arr2), content="-1") // arr1 < arr2 +/// inspect!(arr2.compare(arr1), content="1") // arr2 > arr1 +/// inspect!(arr1.compare(arr3), content="1") // arr1 > arr3 (longer) +/// inspect!(arr1.compare(arr1), content="0") // arr1 = arr1 +/// } +/// ``` pub fn compare[T : Compare](self : Array[T], other : Array[T]) -> Int { let len_self = self.length() let len_other = other.length() @@ -136,6 +315,25 @@ pub fn compare[T : Compare](self : Array[T], other : Array[T]) -> Int { } ///| +/// Concatenates two arrays into a new array. The resulting array contains all +/// elements from the first array followed by all elements from the second array. +/// +/// Parameters: +/// +/// * `self` : The first array to concatenate. +/// * `other` : The second array to concatenate. +/// +/// Returns a new array containing all elements from both arrays in order. +/// +/// Example: +/// +/// ```moonbit +/// test "Array::op_add" { +/// let a = [1, 2, 3] +/// let b = [4, 5] +/// inspect!(a + b, content="[1, 2, 3, 4, 5]") +/// } +/// ``` pub fn op_add[T](self : Array[T], other : Array[T]) -> Array[T] { let result = Array::make_uninit(self.length() + other.length()) UninitializedArray::unsafe_blit( @@ -156,13 +354,30 @@ pub fn op_add[T](self : Array[T], other : Array[T]) -> Array[T] { } ///| -/// Appends all the elements of other array into self +/// Appends all elements from one array to the end of another array. The elements +/// are added in-place, modifying the original array. /// -/// # Example -/// ``` -/// let v1 = [3, 4, 5] -/// let v2 = [6, 7, 8] -/// v1.append(v2) +/// Parameters: +/// +/// * `self` : The array to append to. +/// * `other` : The array whose elements will be appended. +/// +/// Example: +/// +/// ```moonbit +/// test "Array::append" { +/// let v1 = [1, 2, 3] +/// let v2 = [4, 5, 6] +/// v1.append(v2) +/// inspect!(v1, content="[1, 2, 3, 4, 5, 6]") +/// } +/// +/// test "Array::append/empty" { +/// let v1 = [1, 2, 3] +/// let v2 : Array[Int] = [] +/// v1.append(v2) +/// inspect!(v1, content="[1, 2, 3]") +/// } /// ``` pub fn append[T](self : Array[T], other : Array[T]) -> Unit { other.blit_to( @@ -174,16 +389,25 @@ pub fn append[T](self : Array[T], other : Array[T]) -> Unit { } ///| -/// Iterates over the elements of the array. +/// Iterates through each element of the array in order, applying the given +/// function to each element. /// -/// # Example -/// ``` -/// let v = Array::new(capacity=3) -/// v.push(3) -/// v.push(4) -/// v.push(5) -/// let mut sum = 0 -/// v.each(fn (x) {sum = sum + x}) +/// Parameters: +/// +/// * `array` : The array to iterate over. +/// * `function` : A function that takes a single element of type `T` as input +/// and returns `Unit`. This function is applied to each element of the array in +/// order. +/// +/// Example: +/// +/// ```moonbit +/// test "Array::each" { +/// let arr = [1, 2, 3] +/// let mut sum = 0 +/// arr.each(fn(x) { sum = sum + x }) +/// inspect!(sum, content="6") +/// } /// ``` pub fn each[T](self : Array[T], f : (T) -> Unit) -> Unit { for v in self { @@ -208,7 +432,6 @@ pub fn each[T](self : Array[T], f : (T) -> Unit) -> Unit { /// let mut sum = 0 /// v.rev_each(fn(x) { sum = sum - x }) /// @json.inspect!(sum, content=-12) -/// /// ``` pub fn rev_each[T](self : Array[T], f : (T) -> Unit) -> Unit { let len = self.length() @@ -219,7 +442,7 @@ pub fn rev_each[T](self : Array[T], f : (T) -> Unit) -> Unit { ///| /// Iterates over the elements of the array with index in reversed order. -/// +/// /// # Example /// ``` /// let v = [3, 4, 5] @@ -332,13 +555,26 @@ pub fn mapi_inplace[T](self : Array[T], f : (Int, T) -> T) -> Unit { } ///| -/// Filters the array with a predicate function. +/// Creates a new array containing all elements from the input array that satisfy +/// the given predicate function. /// -/// # Example -/// ``` -/// let arr = [1, 2, 3, 4, 5, 6] -/// let v = arr.filter(fn (x) { x % 2 == 0 }) -/// assert_eq!(v, [2, 4, 6]) +/// Parameters: +/// +/// * `array` : The array to filter. +/// * `predicate` : A function that takes an element and returns a boolean +/// indicating whether the element should be included in the result. +/// +/// Returns a new array containing only the elements for which the predicate +/// function returns `true`. The relative order of the elements is preserved. +/// +/// Example: +/// +/// ```moonbit +/// test "Array::filter" { +/// let arr = [1, 2, 3, 4, 5] +/// let evens = arr.filter(fn(x) { x % 2 == 0 }) +/// inspect!(evens, content="[2, 4]") +/// } /// ``` pub fn filter[T](self : Array[T], f : (T) -> Bool) -> Array[T] { let arr = [] @@ -351,24 +587,55 @@ pub fn filter[T](self : Array[T], f : (T) -> Bool) -> Array[T] { } ///| -/// Test if the array is empty. +/// Tests whether the array contains no elements. /// -/// # Example -/// ``` -/// let v : Array[Int] = [] -/// assert_true!(v.is_empty()) +/// Parameters: +/// +/// * `array` : The array to check. +/// +/// Returns `true` if the array has no elements, `false` otherwise. +/// +/// Example: +/// +/// ```moonbit +/// test "Array::is_empty" { +/// let empty : Array[Int] = [] +/// inspect!(empty.is_empty(), content="true") +/// let non_empty = [1, 2, 3] +/// inspect!(non_empty.is_empty(), content="false") +/// } /// ``` pub fn is_empty[T](self : Array[T]) -> Bool { self.length() == 0 } ///| -/// Test if the array is sorted. +/// Tests whether the array is sorted in ascending order. /// -/// # Example -/// ``` -/// let v = [3, 4, 5] -/// assert_true!(v.is_sorted()) +/// Parameters: +/// +/// * `self` : The array to be tested. +/// * `T` : The type of elements in the array. Must implement the `Compare` +/// trait. +/// +/// Returns a boolean value indicating whether the array is sorted in ascending +/// order: +/// +/// * `true` if the array is empty, contains only one element, or all elements +/// are in ascending order. +/// * `false` if any element is greater than the element that follows it. +/// +/// Example: +/// +/// ```moonbit +/// test "Array::is_sorted/basic" { +/// let ascending = [1, 2, 3, 4, 5] +/// let descending = [5, 4, 3, 2, 1] +/// let unsorted = [1, 3, 2, 4, 5] +/// inspect!(ascending.is_sorted(), content="true") +/// inspect!(descending.is_sorted(), content="false") +/// inspect!(unsorted.is_sorted(), content="false") +/// } /// ``` pub fn is_sorted[T : Compare](self : Array[T]) -> Bool { for i = 1 { @@ -383,12 +650,27 @@ pub fn is_sorted[T : Compare](self : Array[T]) -> Bool { } ///| -/// Reverses the order of elements in the Array, in place. +/// Reverses the order of elements in an array in place, modifying the original +/// array. /// -/// # Example -/// ``` -/// let v = [3, 4, 5] -/// v.rev_inplace() +/// Parameters: +/// +/// * `self` : The array to be reversed. +/// +/// Example: +/// +/// ```moonbit +/// test "Array::rev_inplace" { +/// let arr = [1, 2, 3, 4, 5] +/// arr.rev_inplace() +/// inspect!(arr, content="[5, 4, 3, 2, 1]") +/// } +/// +/// test "Array::rev_inplace/empty" { +/// let arr : Array[Int] = [] +/// arr.rev_inplace() +/// inspect!(arr, content="[]") +/// } /// ``` pub fn rev_inplace[T](self : Array[T]) -> Unit { for i = 0; i < self.length() / 2; i = i + 1 { @@ -399,7 +681,24 @@ pub fn rev_inplace[T](self : Array[T]) -> Unit { } ///| -/// Reverses the order of elements in the Array and returns a new Array. +/// Creates a new array with elements in reversed order. +/// +/// Parameters: +/// +/// * `self` : The array to be reversed. +/// +/// Returns a new array containing the same elements as the input array but in +/// reverse order. The original array remains unchanged. +/// +/// Example: +/// +/// ```moonbit +/// test "Array::rev" { +/// let arr = [1, 2, 3, 4, 5] +/// inspect!(arr.rev(), content="[5, 4, 3, 2, 1]") +/// inspect!(arr, content="[1, 2, 3, 4, 5]") // original array unchanged +/// } +/// ``` pub fn rev[T](self : Array[T]) -> Array[T] { let arr = Array::make_uninit(self.length()) for i = 0; i < self.length(); i = i + 1 { @@ -443,12 +742,29 @@ pub fn split_at[T](self : Array[T], index : Int) -> (Array[T], Array[T]) { } ///| -/// Checks if the array contains an element. +/// Checks whether the array contains an element equal to the given value. /// -/// # Example -/// ``` -/// let v = [3, 4, 5] -/// assert_true!(v.contains(3)) +/// Parameters: +/// +/// * `array` : The array to search in. +/// * `value` : The value to search for. +/// +/// Returns `true` if the array contains an element equal to the given value, +/// `false` otherwise. +/// +/// Example: +/// +/// ```moonbit +/// test "Array::contains" { +/// let arr = [1, 2, 3, 4, 5] +/// inspect!(arr.contains(3), content="true") +/// inspect!(arr.contains(6), content="false") +/// } +/// +/// test "Array::contains/empty" { +/// let arr : Array[Int] = [] +/// inspect!(arr.contains(1), content="false") +/// } /// ``` pub fn contains[T : Eq](self : Array[T], value : T) -> Bool { for v in self { @@ -461,12 +777,29 @@ pub fn contains[T : Eq](self : Array[T], value : T) -> Bool { } ///| -/// Check if the array starts with a given prefix. +/// Checks if the array begins with all elements of the provided prefix array in +/// order. /// -/// # Example -/// ``` -/// let v = [3, 4, 5] -/// assert_true!(v.starts_with([3, 4])) +/// Parameters: +/// +/// * `self` : The array to check against. +/// * `prefix` : The array containing the sequence of elements to look for at the +/// beginning. +/// +/// Returns `true` if the array starts with all elements in `prefix` in the same +/// order, `false` otherwise. An empty prefix array always returns `true`, and a +/// prefix longer than the array always returns `false`. +/// +/// Example: +/// +/// ```moonbit +/// test "Array::starts_with" { +/// let arr = [1, 2, 3, 4, 5] +/// inspect!(arr.starts_with([1, 2]), content="true") +/// inspect!(arr.starts_with([2, 3]), content="false") +/// inspect!(arr.starts_with([]), content="true") +/// inspect!(arr.starts_with([1, 2, 3, 4, 5, 6]), content="false") +/// } /// ``` pub fn starts_with[T : Eq](self : Array[T], prefix : Array[T]) -> Bool { if prefix.length() > self.length() { @@ -482,12 +815,30 @@ pub fn starts_with[T : Eq](self : Array[T], prefix : Array[T]) -> Bool { } ///| -/// Check if the array ends with a given suffix. +/// Tests if an array ends with the given suffix. /// -/// # Example -/// ``` -/// let v = [3, 4, 5] -/// assert_true!(v.ends_with([5])) +/// Parameters: +/// +/// * `self` : The array to check. +/// * `suffix` : The array to test against. +/// +/// Returns `true` if the array ends with the given suffix, `false` otherwise. +/// +/// Example: +/// +/// ```moonbit +/// test "Array::ends_with/basic" { +/// let arr = [1, 2, 3, 4, 5] +/// inspect!(arr.ends_with([4, 5]), content="true") +/// inspect!(arr.ends_with([3, 4]), content="false") +/// inspect!(arr.ends_with([]), content="true") +/// } +/// +/// test "Array::ends_with/empty" { +/// let arr : Array[Int] = [] +/// inspect!(arr.ends_with([]), content="true") +/// inspect!(arr.ends_with([1]), content="false") +/// } /// ``` pub fn ends_with[T : Eq](self : Array[T], suffix : Array[T]) -> Bool { if suffix.length() > self.length() { @@ -504,15 +855,25 @@ pub fn ends_with[T : Eq](self : Array[T], suffix : Array[T]) -> Bool { } ///| -/// Strip a prefix from the array. +/// Removes a prefix from an array if it exists. /// -/// If the array starts with the prefix, return the array after the prefix, otherwise return None. +/// Parameters: /// -/// # Example -/// ``` -/// let v = [3, 4, 5] -/// let v2 = v.strip_prefix([3]) -/// assert_eq!(v2, Some([4, 5])) +/// * `array` : The array to remove the prefix from. +/// * `prefix` : The array to be removed from the beginning of `array`. +/// +/// Returns `Some(array)` containing the remaining elements after removing the +/// prefix if the array starts with the prefix, or `None` if the array doesn't +/// start with the prefix. +/// +/// Example: +/// +/// ```moonbit +/// test "strip_prefix" { +/// let arr = [1, 2, 3, 4, 5] +/// inspect!(arr.strip_prefix([1, 2]), content="Some([3, 4, 5])") +/// inspect!(arr.strip_prefix([2, 3]), content="None") +/// } /// ``` pub fn strip_prefix[T : Eq](self : Array[T], prefix : Array[T]) -> Array[T]? { if self.starts_with(prefix) { @@ -553,13 +914,25 @@ pub fn strip_suffix[T : Eq](self : Array[T], suffix : Array[T]) -> Array[T]? { } ///| -/// Search the array index for a given element. +/// Searches for the first occurrence of a value in the array and returns its +/// index. /// -/// # Example -/// ``` -/// let v = [3, 4, 5] -/// let index = v.search(3) -/// assert_eq!(index, Some(0)) +/// Parameters: +/// +/// * `self` : The array to search in. +/// * `value` : The value to search for. +/// +/// Returns an `Option` containing the index of the first occurrence of `value` +/// if found, or `None` if the value is not present in the array. +/// +/// Example: +/// +/// ```moonbit +/// test "Array::search" { +/// let arr = [1, 2, 3, 2, 4] +/// inspect!(arr.search(2), content="Some(1)") // first occurrence +/// inspect!(arr.search(5), content="None") // not found +/// } /// ``` pub fn search[T : Eq](self : Array[T], value : T) -> Int? { for i, v in self { @@ -572,6 +945,28 @@ pub fn search[T : Eq](self : Array[T], value : T) -> Int? { } ///| +/// Searches the array for the first element that satisfies the predicate +/// function. +/// +/// Parameters: +/// +/// * `array` : The array to search in. +/// * `predicate` : A function that takes an element and returns a boolean +/// indicating whether the element satisfies the search condition. +/// +/// Returns the index of the first element that satisfies the predicate, or +/// `None` if no such element is found. +/// +/// Example: +/// +/// ```moonbit +/// test "find_index" { +/// let arr = [1, 2, 3, 4, 5] +/// inspect!(arr.search_by(fn(x) { x > 3 }), content="Some(3)") +/// inspect!(arr.search_by(fn(x) { x > 10 }), content="None") +/// } +/// ``` +/// /// @alert deprecated "Use `search_by` instead." /// @coverage.skip pub fn find_index[T](self : Array[T], f : (T) -> Bool) -> Int? { @@ -582,7 +977,7 @@ pub fn find_index[T](self : Array[T], f : (T) -> Bool) -> Int? { /// Search the index of the first element that satisfies the predicate. /// /// # Example -/// +/// /// ``` /// let v = [1, 2, 3, 4, 5] /// match v.search_by(fn(x) { x == 3 }) { @@ -602,26 +997,26 @@ pub fn search_by[T](self : Array[T], f : (T) -> Bool) -> Int? { ///| /// Performs a binary search on a sorted array to find the index of a given element. -/// +/// /// # Example /// ``` /// let v = [3, 4, 5] /// let result = v.binary_search(3) /// assert_eq!(result, Ok(0)) // The element 3 is found at index 0 /// ``` -/// +/// /// # Arguments /// - `self`: The array in which to perform the search. /// - `value`: The element to search for in the array. -/// +/// /// # Returns /// - `Result[Int, Int]`: -/// If the element is found, an `Ok` variant is returned, containing the index of the matching element in the array. -/// If there are multiple matches, the leftmost match will be returned. +/// If the element is found, an `Ok` variant is returned, containing the index of the matching element in the array. +/// If there are multiple matches, the leftmost match will be returned. /// If the element is not found, an `Err` variant is returned, containing the index where the element could be inserted to maintain the sorted order. -/// +/// /// # Notes -/// - Ensure that the array is sorted in increasing order before calling this function. +/// - Ensure that the array is sorted in increasing order before calling this function. /// - If the array is not sorted, the returned result is undefined and should not be relied on. pub fn binary_search[T : Compare]( self : Array[T], @@ -647,33 +1042,61 @@ pub fn binary_search[T : Compare]( } ///| -/// Performs a binary search on a sorted array using a custom comparator function, and returns the index of the given element. -/// -/// The comparator function should return an `Int` that indicates whether the argument is less than `(-1)`, equal to `(0)`, or greater than `(1)` -/// the target element. -/// -/// # Example -/// ``` -/// let v = [3, 4, 5] -/// let result = v.binary_search_by( -/// fn(v : Int) { if v < 3 { -1 } else if v == 3 { 0 } else { 1 } }, -/// ) -/// assert_eq!(result, Ok(0)) // The element 3 is found at index 0 +/// Performs a binary search on a sorted array using a custom comparison +/// function. Returns the position of the matching element if found, or the +/// position where the element could be inserted while maintaining the sorted +/// order. +/// +/// Parameters: +/// +/// * `array` : The sorted array to search in. +/// * `comparator` : A function that compares each element with the target value, +/// returning: +/// * A negative integer if the element is less than the target +/// * Zero if the element equals the target +/// * A positive integer if the element is greater than the target +/// +/// Returns a `Result` containing either: +/// +/// * `Ok(index)` if a matching element is found at position `index` +/// * `Err(index)` if no match is found, where `index` is the position where the +/// element could be inserted +/// +/// Example: +/// +/// ```moonbit +/// test "binary_search_by" { +/// let arr = [1, 3, 5, 7, 9] +/// let find_3 = arr.binary_search_by(fn(x) { +/// if x < 3 { +/// -1 +/// } else if x > 3 { +/// 1 +/// } else { +/// 0 +/// } +/// }) +/// inspect!(find_3, content="Ok(1)") +/// let find_4 = arr.binary_search_by(fn(x) { +/// if x < 4 { +/// -1 +/// } else if x > 4 { +/// 1 +/// } else { +/// 0 +/// } +/// }) +/// inspect!(find_4, content="Err(2)") +/// } /// ``` -/// # Type Parameters -/// - `T`: The type of elements in the array. -/// -/// # Arguments -/// - `self`: The array in which to perform the search. -/// - `cmp`: A closure that defines the comparator function, taking an element of type `T` and returning an `Int`. -/// -/// # Returns -/// - `Result[Int, Int]`: If the element is found, `Ok` is returned with the index of the element. -/// If the element is not found, `Err` is returned with the index where the element could be inserted to maintain sorted order. -/// -/// # Notes -/// - The array must be sorted, and the comparator function should be consistent with the sorted order of the array. -/// - If not, the returned result is undefined and should not be relied on. +/// +/// Notes: +/// +/// * Assumes the array is sorted according to the ordering implied by the +/// comparison function +/// * For multiple matches, returns the leftmost matching position +/// * Returns an insertion point that maintains the sort order when no match is +/// found pub fn binary_search_by[T]( self : Array[T], cmp : (T) -> Int @@ -698,12 +1121,30 @@ pub fn binary_search_by[T]( } ///| -/// Swap two elements in the array. +/// Swaps the values at two positions in the array. /// -/// # Example -/// ``` -/// let v = [3, 4, 5] -/// v.swap(1, 2) +/// Parameters: +/// +/// * `array` : The array in which to swap elements. +/// * `index1` : The index of the first element to be swapped. +/// * `index2` : The index of the second element to be swapped. +/// +/// Throws an error if either index is negative or greater than or equal to the +/// length of the array. +/// +/// Example: +/// +/// ```moonbit +/// test "Array::swap" { +/// let arr = [1, 2, 3] +/// arr.swap(0, 2) +/// inspect!(arr, content="[3, 2, 1]") +/// } +/// +/// test "panic Array::swap/out_of_bounds" { +/// let arr = [1, 2, 3] +/// ignore(arr.swap(0, 3)) // Index out of bounds +/// } /// ``` /// @alert unsafe "Panic if index is out of bounds." pub fn swap[T](self : Array[T], i : Int, j : Int) -> Unit { @@ -719,12 +1160,36 @@ pub fn swap[T](self : Array[T], i : Int, j : Int) -> Unit { } ///| -/// Retains only the elements specified by the predicate. +/// Removes all elements from the array that do not satisfy the predicate +/// function, modifying the array in place. The order of remaining elements is +/// preserved. /// -/// # Example -/// ``` -/// let v = [3, 4, 5] -/// v.retain(fn(x) { x > 3 }) +/// Parameters: +/// +/// * `array` : The array to be filtered. +/// * `predicate` : A function that takes an element and returns `true` if the +/// element should be kept, `false` if it should be removed. +/// +/// Example: +/// +/// ```moonbit +/// test "retain" { +/// let arr = [1, 2, 3, 4, 5] +/// arr.retain(fn(x) { x % 2 == 0 }) +/// inspect!(arr, content="[2, 4]") +/// } +/// +/// test "retain/empty_result" { +/// let arr = [1, 2, 3] +/// arr.retain(fn(x) { x > 10 }) +/// inspect!(arr, content="[]") +/// } +/// +/// test "retain/keep_all" { +/// let arr = [1, 2, 3] +/// arr.retain(fn { _ => true }) +/// inspect!(arr, content="[1, 2, 3]") +/// } /// ``` /// TODO: perf could be improved pub fn retain[T](self : Array[T], f : (T) -> Bool) -> Unit { @@ -735,20 +1200,45 @@ pub fn retain[T](self : Array[T], f : (T) -> Bool) -> Unit { } continue i + 1, j } else { - // we use `else` here to capture `j` + // we use `else` here to capture `j` self.unsafe_truncate_to_length(j) } } ///| -/// Resize the array to a new length with a default value. +/// Resizes an array to a specified length, either by truncating if the new +/// length is smaller, or by appending copies of a default value if the new +/// length is larger. /// -/// # Example -/// ``` -/// let v = [3, 4, 5] -/// v.resize(1, 0) -/// assert_eq!(v, [3]) +/// Parameters: +/// +/// * `array` : The array to be resized. +/// * `new_length` : The desired length of the array after resizing. +/// * `default_value` : The value to append when extending the array. +/// +/// Throws a panic if `new_length` is negative. +/// +/// Examples: +/// +/// ```moonbit +/// test "Array::resize/shrink" { +/// let arr = [1, 2, 3, 4, 5] +/// arr.resize(3, 0) +/// inspect!(arr, content="[1, 2, 3]") +/// } +/// +/// test "Array::resize/extend" { +/// let arr = [1, 2, 3] +/// arr.resize(5, 0) +/// inspect!(arr, content="[1, 2, 3, 0, 0]") +/// } +/// +/// test "panic Array::resize/negative_length" { +/// let arr = [1, 2, 3] +/// ignore(arr.resize(-1, 0)) +/// } /// ``` +/// /// @alert unsafe "Panic if new length is negative." pub fn resize[T](self : Array[T], new_len : Int, f : T) -> Unit { if new_len < 0 { @@ -766,8 +1256,9 @@ pub fn resize[T](self : Array[T], new_len : Int, f : T) -> Unit { ///| /// Flattens a array of arrays into a array. /// -/// # Example -/// ``` +/// Example: +/// +/// ```moonbit /// let v = [[3, 4], [5, 6]].flatten() /// assert_eq!(v, [3, 4, 5, 6]) /// ``` @@ -783,8 +1274,9 @@ pub fn flatten[T](self : Array[Array[T]]) -> Array[T] { ///| /// Create a array by repeat a given array for a given times. /// -/// # Example -/// ``` +/// Example: +/// +/// ```moonbit /// let v = [3, 4].repeat(2) /// assert_eq!(v, [3, 4, 3, 4]) /// ``` @@ -799,8 +1291,9 @@ pub fn repeat[T](self : Array[T], times : Int) -> Array[T] { ///| /// Fold out values from an array according to certain rules. /// -/// # Example -/// ``` +/// Example: +/// +/// ```moonbit /// let sum = [1, 2, 3, 4, 5].fold(init=0, fn { sum, elem => sum + elem }) /// assert_eq!(sum, 15) /// ``` @@ -815,8 +1308,9 @@ pub fn fold[A, B](self : Array[A], init~ : B, f : (B, A) -> B) -> B { ///| /// Fold out values from an array according to certain rules in reversed turn. /// -/// # Example -/// ``` +/// Example: +/// +/// ```moonbit /// let sum = [1, 2, 3, 4, 5].rev_fold(init=0, fn { sum, elem => sum + elem }) /// assert_eq!(sum, 15) /// ``` @@ -831,8 +1325,9 @@ pub fn rev_fold[A, B](self : Array[A], init~ : B, f : (B, A) -> B) -> B { ///| /// Fold out values from an array according to certain rules with index. /// -/// # Example -/// ``` +/// Example: +/// +/// ```moonbit /// let sum = [1, 2, 3, 4, 5].foldi(init=0, fn { index, sum, _elem => sum + index }) /// assert_eq!(sum, 10) /// ``` @@ -847,8 +1342,9 @@ pub fn foldi[A, B](self : Array[A], init~ : B, f : (Int, B, A) -> B) -> B { ///| /// Fold out values from an array according to certain rules in reversed turn with index. /// -/// # Example -/// ``` +/// Example: +/// +/// ```moonbit /// let sum = [1, 2, 3, 4, 5].rev_foldi(init=0, fn { index, sum, _elem => sum + index }) /// assert_eq!(sum, 10) /// ``` @@ -864,8 +1360,9 @@ pub fn rev_foldi[A, B](self : Array[A], init~ : B, f : (Int, B, A) -> B) -> B { ///| /// Fold out values from an array according to certain rules. /// -/// # Example -/// ``` +/// Example: +/// +/// ```moonbit /// let sum = [1, 2, 3, 4, 5].fold(init=0, fn { sum, elem => sum + elem }) /// assert_eq!(sum, 15) /// ``` @@ -878,8 +1375,9 @@ pub fn fold_left[T, U](self : Array[T], f : (U, T) -> U, init~ : U) -> U { ///| /// Fold out values from an array according to certain rules in reversed turn. /// -/// # Example -/// ``` +/// Example: +/// +/// ```moonbit /// let sum = [1, 2, 3, 4, 5].rev_fold(init=0, fn { sum, elem => sum + elem }) /// assert_eq!(sum, 15) /// ``` @@ -892,8 +1390,9 @@ pub fn fold_right[T, U](self : Array[T], f : (U, T) -> U, init~ : U) -> U { ///| /// Fold out values from an array according to certain rules with index. /// -/// # Example -/// ``` +/// Example: +/// +/// ```moonbit /// let sum = [1, 2, 3, 4, 5].foldi(init=0, fn { index, sum, _elem => sum + index }) /// assert_eq!(sum, 10) /// ``` @@ -906,8 +1405,9 @@ pub fn fold_lefti[T, U](self : Array[T], f : (Int, U, T) -> U, init~ : U) -> U { ///| /// Fold out values from an array according to certain rules in reversed turn with index. /// -/// # Example -/// ``` +/// Example: +/// +/// ```moonbit /// let sum = [1, 2, 3, 4, 5].rev_foldi(init=0, fn { index, sum, _elem => sum + index }) /// assert_eq!(sum, 10) /// ``` @@ -918,24 +1418,40 @@ pub fn fold_righti[T, U](self : Array[T], f : (Int, U, T) -> U, init~ : U) -> U } ///| -/// Removes consecutive repeated elements in the array according to the Eq trait. +/// Removes consecutive duplicate elements from an array in-place, using equality +/// comparison. The first occurrence of each element is retained while subsequent +/// equal elements are removed. /// -/// # Example +/// Parameters: /// -/// ``` -/// let v = [3, 4, 4, 5, 5, 5] -/// v.dedup() // v = [3, 4, 5] -/// ``` +/// * `array` : The array to remove duplicates from. Must contain elements that +/// implement the `Eq` trait for equality comparison. /// -/// # Notes +/// Example: /// -/// Usually, you might want to sort the array before calling this function. For example: +/// ```moonbit +/// test "dedup" { +/// let arr = [1, 2, 2, 3, 3, 3, 2] +/// arr.dedup() +/// inspect!(arr, content="[1, 2, 3, 2]") +/// } /// +/// test "dedup/sorted" { +/// let arr = [1, 2, 2, 2, 3, 3] +/// arr.dedup() +/// inspect!(arr, content="[1, 2, 3]") +/// } +/// +/// test "dedup/empty" { +/// let arr : Array[Int] = [] +/// arr.dedup() +/// inspect!(arr, content="[]") +/// } /// ``` -/// let v = [3, 4, 5, 4, 5, 5] -/// v.sort() -/// v.dedup() // v = [3, 4, 5] -/// ``` +/// +/// Note: For best results when removing all duplicates regardless of position, +/// sort the array before calling this function. When used on an unsorted array, +/// this function only removes consecutive duplicates. pub fn dedup[T : Eq](self : Array[T]) -> Unit { if self.is_empty() { return @@ -951,16 +1467,28 @@ pub fn dedup[T : Eq](self : Array[T]) -> Unit { } ///| -/// Extract elements from the array according to the given function. +/// Extracts elements from an array that satisfy a given predicate function. The +/// extracted elements are removed from the original array and returned as a new +/// array. The relative order of the extracted elements is preserved. /// -/// This function will remove the elements from the original array and return a new array. -/// # Example -/// ``` -/// let -/// v = [3, 4, 5] -/// let vv = v.extract_if(fn(x) { x > 3 }) -/// assert_eq!(vv, [4, 5]) -/// assert_eq!(v, [3]) +/// Parameters: +/// +/// * `array` : The array to extract elements from. +/// * `predicate` : A function that takes an element and returns `true` if the +/// element should be extracted, `false` otherwise. +/// +/// Returns a new array containing all elements that satisfy the predicate +/// function, in the order they appeared in the original array. +/// +/// Example: +/// +/// ```moonbit +/// test "extract_if" { +/// let arr = [1, 2, 3, 4, 5] +/// let extracted = arr.extract_if(fn(x) { x % 2 == 0 }) +/// inspect!(extracted, content="[2, 4]") +/// inspect!(arr, content="[1, 3, 5]") +/// } /// ``` pub fn extract_if[T](self : Array[T], f : (T) -> Bool) -> Array[T] { let v = [] @@ -978,15 +1506,30 @@ pub fn extract_if[T](self : Array[T], f : (T) -> Bool) -> Array[T] { } ///| -/// Group the elements of the array into sized chunks. +/// Divides an array into smaller arrays (chunks) of the specified size. +/// +/// Parameters: /// -/// If the elements of the array cannot be divided into equal-sized chunks, the last chunk will be smaller. +/// * `array` : The array to be divided into chunks. +/// * `size` : The size of each chunk. Must be a positive integer. /// -/// # Example -/// ``` -/// let v = [1, 2, 3, 4, 5, 6, 7, 8, 9] -/// let chunks = v.chunks(3) -/// assert_eq!(chunks, [[1, 2, 3], [4, 5, 6], [7, 8, 9]]) +/// Returns an array of arrays, where each inner array is a chunk containing +/// elements from the original array. If the length of the original array is not +/// divisible by the chunk size, the last chunk will contain fewer elements. +/// +/// Example: +/// +/// ```moonbit +/// test "chunks" { +/// let arr = [1, 2, 3, 4, 5] +/// let chunks = arr.chunks(2) +/// inspect!(chunks, content="[[1, 2], [3, 4], [5]]") +/// } +/// +/// test "chunks/empty" { +/// let arr : Array[Int] = [] +/// inspect!(arr.chunks(3), content="[]") +/// } /// ``` pub fn chunks[T](self : Array[T], size : Int) -> Array[Array[T]] { let chunks = [] @@ -1003,13 +1546,31 @@ pub fn chunks[T](self : Array[T], size : Int) -> Array[Array[T]] { } ///| -/// Group the elements of the array into chunks based on a predicate. +/// Groups consecutive elements of the array into chunks where adjacent elements +/// satisfy the given predicate function. /// -/// # Example -/// ``` -/// let v = [1, 1, 2, 3, 2, 3, 2, 3, 4] -/// let chunks = v.chunk_by(fn(x, y) { x <= y }) -/// assert_eq!(chunks, [[1, 1, 2, 3], [2, 3], [2, 3, 4]]) +/// Parameters: +/// +/// * `array` : The array to be chunked. +/// * `predicate` : A function that takes two adjacent elements and returns +/// `true` if they should be in the same chunk, `false` otherwise. +/// +/// Returns an array of arrays, where each inner array is a chunk of consecutive +/// elements that satisfy the predicate with their adjacent elements. +/// +/// Example: +/// +/// ```moonbit +/// test "chunk_by" { +/// let v = [1, 1, 2, 3, 2, 3, 2, 3, 4] +/// let chunks = v.chunk_by(fn(x, y) { x <= y }) +/// inspect!(chunks, content="[[1, 1, 2, 3], [2, 3], [2, 3, 4]]") +/// } +/// +/// test "chunk_by/empty" { +/// let v : Array[Int] = [] +/// inspect!(v.chunk_by(fn(x, y) { x <= y }), content="[]") +/// } /// ``` pub fn chunk_by[T](self : Array[T], pred : (T, T) -> Bool) -> Array[Array[T]] { let chunks = [] @@ -1028,13 +1589,32 @@ pub fn chunk_by[T](self : Array[T], pred : (T, T) -> Bool) -> Array[Array[T]] { } ///| -/// Split the array into chunks based on a predicate. +/// Splits an array into chunks using a predicate function. Creates chunks by +/// grouping consecutive elements that do not satisfy the predicate function. +/// Elements that satisfy the predicate function are excluded from the resulting +/// chunks and act as delimiters. /// -/// # Example -/// ``` -/// let v = [1, 0, 2, 0, 3, 0, 4] -/// let chunks = v.split(fn(x) { x == 0 }) -/// assert_eq!(chunks, [[1], [2], [3], [4]]) +/// Parameters: +/// +/// * `array` : The array to be split into chunks. +/// * `predicate` : A function that takes an element and returns `true` if the +/// element should be used as a delimiter. +/// +/// Returns an array of arrays, where each inner array is a chunk of consecutive +/// elements that do not satisfy the predicate. +/// +/// Example: +/// +/// ```moonbit +/// test "split/basic" { +/// let arr = [1, 0, 2, 0, 3, 0, 4] +/// inspect!(arr.split(fn(x) { x == 0 }), content="[[1], [2], [3], [4]]") +/// } +/// +/// test "split/empty_chunks" { +/// let arr = [0, 1, 0, 0, 2, 0] +/// inspect!(arr.split(fn(x) { x == 0 }), content="[[], [1], [], [2]]") +/// } /// ``` pub fn split[T](self : Array[T], pred : (T) -> Bool) -> Array[Array[T]] { let chunks = [] @@ -1052,6 +1632,24 @@ pub fn split[T](self : Array[T], pred : (T) -> Bool) -> Array[Array[T]] { } ///| +/// Creates an iterator over the elements of the array. +/// +/// Parameters: +/// +/// * `array` : The array to create an iterator from. +/// +/// Returns an iterator that yields each element of the array in order. +/// +/// Example: +/// +/// ```moonbit +/// test "Array::iter" { +/// let arr = [1, 2, 3] +/// let mut sum = 0 +/// arr.iter().each(fn(x) { sum = sum + x }) +/// inspect!(sum, content="6") +/// } +/// ``` pub fn iter[T](self : Array[T]) -> Iter[T] { Iter::new(fn(yield_) { for v in self { @@ -1064,6 +1662,26 @@ pub fn iter[T](self : Array[T]) -> Iter[T] { } ///| +/// Returns an iterator that provides both indices and values of the array in +/// order. +/// +/// Parameters: +/// +/// * `self` : The array to iterate over. +/// +/// Returns an iterator that yields tuples of index and value pairs, where +/// indices start from 0. +/// +/// Example: +/// +/// ```moonbit +/// test "Array::iter2" { +/// let arr = [10, 20, 30] +/// let mut sum = 0 +/// arr.iter2().each(fn(i, x) { sum = sum + i + x }) +/// inspect!(sum, content="63") // (0 + 10) + (1 + 20) + (2 + 30) = 63 +/// } +/// ``` pub fn iter2[A](self : Array[A]) -> Iter2[Int, A] { Iter2::new(fn(yield_) { for i, v in self { @@ -1076,6 +1694,19 @@ pub fn iter2[A](self : Array[A]) -> Iter2[Int, A] { } ///| +/// Creates a new empty array. +/// +/// Returns an empty array of type `Array[T]`. +/// +/// Example: +/// +/// ```moonbit +/// test "Array::default" { +/// let arr : Array[Int] = Array::default() +/// inspect!(arr.length(), content="0") +/// inspect!(arr.is_empty(), content="true") +/// } +/// ``` pub fn Array::default[T]() -> Array[T] { [] } diff --git a/builtin/array_block.mbt b/builtin/array_block.mbt index 607083525..ba429bafe 100644 --- a/builtin/array_block.mbt +++ b/builtin/array_block.mbt @@ -13,6 +13,39 @@ // limitations under the License. ///| +/// Copies elements from one array to another array. Works correctly even when +/// the source and destination arrays overlap. +/// +/// Parameters: +/// +/// * `dst` : The destination array where elements will be copied to. +/// * `dst_offset` : The starting index in the destination array where elements +/// should be copied. +/// * `src` : The source array from which elements will be copied. +/// * `src_offset` : The starting index in the source array from which elements +/// should be copied. +/// * `len` : The number of elements to copy. +/// +/// The behavior is undefined if any of the following conditions are met: +/// +/// * `len` is negative +/// * `dst_offset` is negative +/// * `src_offset` is negative +/// * `dst_offset + len` exceeds the length of destination array +/// * `src_offset + len` exceeds the length of source array +/// +/// Example: +/// +/// ```moonbit +/// test "Array::unsafe_blit" { +/// let src = [1, 2, 3, 4, 5] +/// let dst = [0, 0, 0, 0, 0] +/// Array::unsafe_blit(dst, 1, src, 2, 2) +/// inspect!(dst, content="[0, 3, 4, 0, 0]") +/// } +/// ``` +/// +/// @alert unsafe "Panic if the indices or length are out of bounds" pub fn Array::unsafe_blit[A]( dst : Array[A], dst_offset : Int, @@ -30,6 +63,33 @@ pub fn Array::unsafe_blit[A]( } ///| +/// Copies elements from a fixed-size array to a dynamic array. The arrays may +/// overlap, in which case the copy is performed in a way that preserves the +/// data. +/// +/// Parameters: +/// +/// * `dst` : The destination dynamic array where elements will be copied to. +/// * `dst_offset` : The starting index in the destination array where copying +/// begins. +/// * `src` : The source fixed-size array from which elements will be copied. +/// * `src_offset` : The starting index in the source array from which copying +/// begins. +/// * `len` : The number of elements to copy. +/// +/// Example: +/// +/// ```moonbit +/// test "Array::unsafe_blit_fixed" { +/// let src = FixedArray::make(5, 0) +/// let dst = Array::make(5, 0) +/// for i in 0..<5 { +/// src[i] = i + 1 +/// } +/// Array::unsafe_blit_fixed(dst, 1, src, 0, 2) +/// inspect!(dst, content="[0, 1, 2, 0, 0]") +/// } +/// ``` pub fn Array::unsafe_blit_fixed[A]( dst : Array[A], dst_offset : Int, @@ -47,22 +107,38 @@ pub fn Array::unsafe_blit_fixed[A]( } ///| -/// Copies elements from one array to another array. The destination array will grow if necessary. -/// -/// # Arguments -/// - `self`: The source array to copy elements from -/// - `dst`: The destination array to copy elements to -/// - `len`: The number of elements to copy -/// - `src_offset`: Starting index in the source array (default: 0) -/// - `dst_offset`: Starting index in the destination array (default: 0) -/// -/// # Panics +/// Copies elements from one array to another array, with support for growing the +/// destination array if needed. The arrays may overlap, in which case the copy +/// is performed in a way that preserves the data. +/// +/// Parameters: +/// +/// * `source` : The array to copy elements from. +/// * `destination` : The array to copy elements to. Will be automatically grown +/// if needed to accommodate the copied elements. +/// * `length` : The number of elements to copy. +/// * `source_start` : Starting index in the source array. Defaults to 0. +/// * `destination_start` : Starting index in the destination array. Defaults to +/// 0. +/// +/// Example: +/// +/// ```moonbit +/// test "Array::blit_to/basic" { +/// let src = [1, 2, 3, 4, 5] +/// let dst = [0, 0] +/// src.blit_to(dst, len=3, dst_offset=1) +/// inspect!(dst, content="[0, 1, 2, 3]") +/// } +/// ``` +/// /// Panics if: -/// - `len` is negative -/// - `src_offset` is negative -/// - `dst_offset` is negative -/// - `src_offset + len` exceeds source array length -/// - `dst_offset` exceeds destination array length +/// +/// * `len` is negative +/// * `src_offset` is negative +/// * `dst_offset` is negative +/// * `dst_offset + len` exceeds the length of destination array +/// * `src_offset + len` exceeds the length of source array pub fn Array::blit_to[A]( self : Array[A], dst : Array[A], diff --git a/builtin/arraycore_nonjs.mbt b/builtin/arraycore_nonjs.mbt index f1548a448..dcabafeec 100644 --- a/builtin/arraycore_nonjs.mbt +++ b/builtin/arraycore_nonjs.mbt @@ -29,7 +29,30 @@ fn Array::make_uninit[T](len : Int) -> Array[T] { } ///| -/// Creates a new array. +/// Creates a new empty array with an optional initial capacity. +/// +/// Parameters: +/// +/// * `capacity` : The initial capacity of the array. If 0 (default), creates an +/// array with minimum capacity. Must be non-negative. +/// +/// Returns a new empty array of type `Array[T]` with the specified initial +/// capacity. +/// +/// Example: +/// +/// ```moonbit +/// test "Array::new" { +/// let arr : Array[Int] = Array::new(capacity=10) +/// inspect!(arr.length(), content="0") +/// inspect!(arr.capacity(), content="10") +/// } +/// +/// test "Array::new/default" { +/// let arr : Array[Int] = Array::new() +/// inspect!(arr.length(), content="0") +/// } +/// ``` pub fn Array::new[T](capacity~ : Int = 0) -> Array[T] { if capacity == 0 { [] @@ -40,7 +63,23 @@ pub fn Array::new[T](capacity~ : Int = 0) -> Array[T] { ///| /// Returns the number of elements in the array. -/// @intrinsic %array.length +/// +/// Parameters: +/// +/// * `array` : The array whose length is to be determined. +/// +/// Returns the number of elements in the array as an integer. +/// +/// Example: +/// +/// ```moonbit +/// test "Array::length" { +/// let arr = [1, 2, 3] +/// inspect!(arr.length(), content="3") +/// let empty : Array[Int] = [] +/// inspect!(empty.length(), content="0") +/// } +/// ``` pub fn length[T](self : Array[T]) -> Int { self.len } @@ -243,8 +282,29 @@ pub fn pop_exn[T](self : Array[T]) -> T { } ///| -/// Removes the last element from a array and returns it. -/// +/// Removes and returns the last element from the array. +/// +/// Parameters: +/// +/// * `array` : The array from which to remove and return the last element. +/// +/// Returns the last element of the array before removal. +/// +/// Example: +/// +/// ```moonbit +/// test "unsafe_pop" { +/// let arr = [1, 2, 3] +/// inspect!(arr.unsafe_pop(), content="3") +/// inspect!(arr, content="[1, 2]") +/// } +/// +/// test "panic unsafe_pop/empty" { +/// let arr : Array[Int] = [] +/// ignore(arr.unsafe_pop()) // Panics when array is empty +/// } +/// ``` +/// /// @alert unsafe "Panic if the array is empty." pub fn unsafe_pop[T](self : Array[T]) -> T { let len = self.length() @@ -347,7 +407,7 @@ pub fn insert[T](self : Array[T], index : Int, value : T) -> Unit { ///| /// Resize the array in-place so that `len` is equal to `new_len`. /// -/// If `new_len` is greater than `len`, the array will be extended by the +/// If `new_len` is greater than `len`, the array will be extended by the /// difference, and the values in the new slots are left uninitilized. /// If `new_len` is less than `len`, it will panic /// diff --git a/builtin/arrayview.mbt b/builtin/arrayview.mbt index fbb3304ad..1c1267143 100644 --- a/builtin/arrayview.mbt +++ b/builtin/arrayview.mbt @@ -21,11 +21,57 @@ struct ArrayView[T] { } ///| +/// Returns the length (number of elements) of an array view. +/// +/// Parameters: +/// +/// * `array_view` : The array view whose length is to be determined. +/// +/// Returns an integer representing the number of elements in the array view. +/// +/// Example: +/// +/// ```moonbit +/// test "ArrayView::length" { +/// let arr = [1, 2, 3, 4, 5] +/// let view = arr[2:4] +/// inspect!(view.length(), content="2") +/// } +/// ``` pub fn length[T](self : ArrayView[T]) -> Int { self.len } ///| +/// Retrieves an element at the specified index from the array view. +/// +/// Parameters: +/// +/// * `self` : The array view to access. +/// * `index` : The position in the array view from which to retrieve the +/// element. +/// +/// Returns the element at the specified index. +/// +/// Throws a runtime error if the index is out of bounds (less than 0 or greater +/// than or equal to the length of the array view). +/// +/// Example: +/// +/// ```moonbit +/// test "ArrayView::op_get" { +/// let arr = [1, 2, 3, 4, 5] +/// let view = arr[2:4] +/// inspect!(view[0], content="3") +/// inspect!(view[1], content="4") +/// } +/// +/// test "panic ArrayView::op_get/out_of_bounds" { +/// let arr = [1, 2, 3] +/// let view = arr[1:] +/// ignore(view[2]) // Index out of bounds +/// } +/// ``` pub fn op_get[T](self : ArrayView[T], index : Int) -> T { guard index >= 0 && index < self.len else { abort( @@ -35,7 +81,34 @@ pub fn op_get[T](self : ArrayView[T], index : Int) -> T { self.buf[self.start + index] } -///| +///| +/// Retrieves an element from the array view at the specified index without +/// performing bounds checking. +/// +/// Parameters: +/// +/// * `array_view` : The array view to retrieve the element from. +/// * `index` : The position in the array view from which to retrieve the +/// element. +/// +/// Returns the element at the specified index in the array view. +/// +/// Example: +/// +/// ```moonbit +/// test "ArrayView::unsafe_get" { +/// let arr = [1, 2, 3, 4, 5] +/// let view = arr[1:4] +/// inspect!(view.unsafe_get(1), content="3") +/// } +/// +/// test "panic ArrayView::unsafe_get/out_of_bounds" { +/// let arr = [1, 2, 3] +/// let view = arr[1:] +/// ignore(view.unsafe_get(5)) // Index out of bounds +/// } +/// ``` +/// /// @intrinsic %arrayview.unsafe_get /// @alert unsafe "Panic if index is out of bounds" pub fn unsafe_get[T](self : ArrayView[T], index : Int) -> T { @@ -43,6 +116,35 @@ pub fn unsafe_get[T](self : ArrayView[T], index : Int) -> T { } ///| +/// Sets the value at the specified index in the array view. +/// +/// Parameters: +/// +/// * `array_view` : The array view to modify. +/// * `index` : The position in the array view where the value will be set. +/// * `value` : The value to be assigned at the specified index. +/// +/// Throws a panic if `index` is negative or greater than or equal to the length +/// of the array view. +/// +/// Example: +/// +/// ```moonbit +/// test "ArrayView::op_set" { +/// let arr = [1, 2, 3, 4, 5] +/// let view = arr[1:4] // view contains [2, 3, 4] +/// view.op_set(1, 42) +/// inspect!(arr, content="[1, 2, 42, 4, 5]") +/// } +/// +/// test "panic ArrayView::op_set/out_of_bounds" { +/// let arr = [1, 2, 3] +/// let view = arr[1:] +/// ignore(view.op_set(2, 42)) // Index out of bounds +/// } +/// ``` +/// +/// @alert unsafe "Panic if index is out of bounds." pub fn op_set[T](self : ArrayView[T], index : Int, value : T) -> Unit { guard index >= 0 && index < self.len else { abort( @@ -53,6 +155,34 @@ pub fn op_set[T](self : ArrayView[T], index : Int, value : T) -> Unit { } ///| +/// Swaps two elements in the array view at the specified indices. +/// +/// Parameters: +/// +/// * `self` : The array view in which to swap elements. +/// * `i` : The index of the first element to be swapped. +/// * `j` : The index of the second element to be swapped. +/// +/// Throws a panic if either index is negative or greater than or equal to the +/// length of the array view. +/// +/// Example: +/// +/// ```moonbit +/// test "ArrayView::swap" { +/// let arr = [1, 2, 3, 4, 5] +/// let view = arr[1:4] // view = [2, 3, 4] +/// view.swap(0, 2) // view = [4, 3, 2] +/// inspect!(view, content="[4, 3, 2]") +/// } +/// +/// test "panic ArrayView::swap/out_of_bounds" { +/// let view = [1, 2, 3][:] +/// ignore(view.swap(0, 3)) // Index out of bounds +/// } +/// ``` +/// +/// @alert unsafe "Panic if index is out of bounds" pub fn swap[T](self : ArrayView[T], i : Int, j : Int) -> Unit { guard i >= 0 && i < self.len && j >= 0 && j < self.len else { abort( @@ -65,6 +195,37 @@ pub fn swap[T](self : ArrayView[T], i : Int, j : Int) -> Unit { } ///| +/// Creates a view of a portion of the array. The view provides read-write access +/// to the underlying array without copying the elements. +/// +/// Parameters: +/// +/// * `array` : The array to create a view from. +/// * `start` : The starting index of the view (inclusive). Defaults to 0. +/// * `end` : The ending index of the view (exclusive). If not provided, defaults +/// to the length of the array. +/// +/// Returns an `ArrayView` that provides a window into the specified portion of +/// the array. +/// +/// Throws a panic if the indices are invalid (i.e., `start` is negative, `end` +/// is greater than the array length, or `start` is greater than `end`). +/// +/// Example: +/// +/// ```moonbit +/// test "op_as_view" { +/// let arr = [1, 2, 3, 4, 5] +/// let view = arr[1:4] // Create a view of elements at indices 1, 2, and 3 +/// inspect!(view[0], content="2") // First element of view is arr[1] +/// inspect!(view.length(), content="3") // View contains 3 elements +/// } +/// +/// test "panic op_as_view/invalid_range" { +/// let arr = [1, 2, 3] +/// ignore(arr[2:1]) // Panic: start index greater than end index +/// } +/// ``` pub fn op_as_view[T]( self : Array[T], start~ : Int = 0, @@ -82,6 +243,44 @@ pub fn op_as_view[T]( } ///| +/// Creates a new view into a portion of the array view. +/// +/// Parameters: +/// +/// * `self` : The array view to create a new view from. +/// * `start` : The starting index in the current view (inclusive). Defaults to +/// 0. +/// * `end` : The ending index in the current view (exclusive). Defaults to the +/// length of the current view. +/// +/// Returns a new `ArrayView` that provides a window into the specified portion +/// of the original array view. The indices are relative to the start of the +/// current view. +/// +/// Throws a panic if: +/// +/// * `start` is negative +/// * `end` is greater than the length of the current view +/// * `start` is greater than `end` +/// +/// Example: +/// +/// ```moonbit +/// test "ArrayView::op_as_view" { +/// let arr = [1, 2, 3, 4, 5] +/// let view = arr[1:4] // view = [2, 3, 4] +/// let subview = view[1:2] // subview = [3] +/// inspect!(subview[0], content="3") +/// } +/// ``` +/// +/// ```moonbit +/// test "panic ArrayView::op_as_view/invalid_range" { +/// let arr = [1, 2, 3, 4, 5] +/// let view = arr[1:4] +/// ignore(view[2:5]) // Panic: end index out of bounds +/// } +/// ``` pub fn op_as_view[T]( self : ArrayView[T], start~ : Int = 0, @@ -99,6 +298,26 @@ pub fn op_as_view[T]( } ///| +/// Returns an iterator that yields each element of the array view in sequence +/// from start to end. +/// +/// Parameters: +/// +/// * `array_view` : The array view to iterate over. +/// +/// Returns an iterator that yields elements of type `A` from the array view. +/// +/// Example: +/// +/// ```moonbit +/// test "ArrayView::iter" { +/// let arr = [1, 2, 3] +/// let view = arr[1:] +/// let mut sum = 0 +/// view.iter().each(fn(x) { sum = sum + x }) +/// inspect!(sum, content="5") +/// } +/// ``` pub fn iter[A](self : ArrayView[A]) -> Iter[A] { Iter::new(fn(yield_) { for v in self { From 401afd2de16906994240b65682043f40d1e8ea5b Mon Sep 17 00:00:00 2001 From: Bao Zhiyuan <71200607+bzy-debug@users.noreply.github.com> Date: Thu, 9 Jan 2025 15:16:00 +0800 Subject: [PATCH 07/17] make @list.unzip a method (#1444) * make unzip a method * update mbti --------- Co-authored-by: Bao Zhiyuan --- immut/list/list.mbt | 4 ++-- immut/list/list.mbti | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/immut/list/list.mbt b/immut/list/list.mbt index ede72bf1e..214a6ad82 100644 --- a/immut/list/list.mbt +++ b/immut/list/list.mbt @@ -643,8 +643,8 @@ pub fn is_empty[A](self : T[A]) -> Bool { /// assert_eq!(a, @list.from_array([1, 3, 5])) /// assert_eq!(b, @list.from_array([2, 4, 6])) /// ``` -pub fn unzip[A, B](list : T[(A, B)]) -> (T[A], T[B]) { - match list { +pub fn unzip[A, B](self : T[(A, B)]) -> (T[A], T[B]) { + match self { Nil => (Nil, Nil) Cons((x, y), xs) => { let (xs, ys) = unzip(xs) diff --git a/immut/list/list.mbti b/immut/list/list.mbti index bd15056d9..431bca2d5 100644 --- a/immut/list/list.mbti +++ b/immut/list/list.mbti @@ -10,8 +10,6 @@ fn singleton[A](A) -> T[A] fn unfold[A, S]((S) -> (A, S)?, init~ : S) -> T[A] -fn unzip[A, B](T[(A, B)]) -> (T[A], T[B]) - // Types and methods pub(all) enum T { Nil @@ -87,6 +85,7 @@ impl T { unsafe_maximum[A : Compare](Self[A]) -> A unsafe_minimum[A : Compare](Self[A]) -> A unsafe_nth[A](Self[A], Int) -> A + unzip[A, B](Self[(A, B)]) -> (Self[A], Self[B]) zip[A, B](Self[A], Self[B]) -> Self[(A, B)]? } impl[A : Eq] Eq for T[A] From 1a440a713e3628de4d0ea2ec254a95c61f6b27e0 Mon Sep 17 00:00:00 2001 From: Young-Flash Date: Thu, 9 Jan 2025 15:22:31 +0800 Subject: [PATCH 08/17] minor: tweak license header --- buffer/deprecated.mbt | 2 +- builtin/array.mbt | 2 +- builtin/array_block.mbt | 2 +- builtin/arraycore_nonjs.mbt | 2 +- builtin/arrayview.mbt | 2 +- byte/byte.mbt | 2 +- double/exp_js.mbt | 2 +- double/exp_nonjs.mbt | 2 +- double/log.mbt | 2 +- double/mod_js.mbt | 2 +- double/mod_nonjs.mbt | 2 +- double/pow_js.mbt | 2 +- double/pow_nonjs.mbt | 2 +- double/round.mbt | 2 +- double/round_js.mbt | 2 +- double/round_wasm.mbt | 2 +- immut/list/list.mbt | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/buffer/deprecated.mbt b/buffer/deprecated.mbt index b3799c49d..67a6d968d 100644 --- a/buffer/deprecated.mbt +++ b/buffer/deprecated.mbt @@ -1,4 +1,4 @@ -// Copyright 2024 International Digital Economy Academy +// Copyright 2025 International Digital Economy Academy // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/builtin/array.mbt b/builtin/array.mbt index 4e8cd428c..af3bceb3d 100644 --- a/builtin/array.mbt +++ b/builtin/array.mbt @@ -1,4 +1,4 @@ -// Copyright 2024 International Digital Economy Academy +// Copyright 2025 International Digital Economy Academy // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/builtin/array_block.mbt b/builtin/array_block.mbt index ba429bafe..9b536cd1c 100644 --- a/builtin/array_block.mbt +++ b/builtin/array_block.mbt @@ -1,4 +1,4 @@ -// Copyright 2024 International Digital Economy Academy +// Copyright 2025 International Digital Economy Academy // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/builtin/arraycore_nonjs.mbt b/builtin/arraycore_nonjs.mbt index dcabafeec..929edec3a 100644 --- a/builtin/arraycore_nonjs.mbt +++ b/builtin/arraycore_nonjs.mbt @@ -1,4 +1,4 @@ -// Copyright 2024 International Digital Economy Academy +// Copyright 2025 International Digital Economy Academy // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/builtin/arrayview.mbt b/builtin/arrayview.mbt index 1c1267143..765a06265 100644 --- a/builtin/arrayview.mbt +++ b/builtin/arrayview.mbt @@ -1,4 +1,4 @@ -// Copyright 2024 International Digital Economy Academy +// Copyright 2025 International Digital Economy Academy // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/byte/byte.mbt b/byte/byte.mbt index 0df305e93..104aa2cc7 100644 --- a/byte/byte.mbt +++ b/byte/byte.mbt @@ -1,4 +1,4 @@ -// Copyright 2024 International Digital Economy Academy +// Copyright 2025 International Digital Economy Academy // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/double/exp_js.mbt b/double/exp_js.mbt index 715ed83e8..a198f8cf6 100644 --- a/double/exp_js.mbt +++ b/double/exp_js.mbt @@ -1,4 +1,4 @@ -// Copyright 2024 International Digital Economy Academy +// Copyright 2025 International Digital Economy Academy // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/double/exp_nonjs.mbt b/double/exp_nonjs.mbt index 90fb0c48e..3034f3578 100644 --- a/double/exp_nonjs.mbt +++ b/double/exp_nonjs.mbt @@ -1,4 +1,4 @@ -// Copyright 2024 International Digital Economy Academy +// Copyright 2025 International Digital Economy Academy // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/double/log.mbt b/double/log.mbt index 3172cc586..e3a1cd447 100644 --- a/double/log.mbt +++ b/double/log.mbt @@ -1,4 +1,4 @@ -// Copyright 2024 International Digital Economy Academy +// Copyright 2025 International Digital Economy Academy // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/double/mod_js.mbt b/double/mod_js.mbt index aa6315dbe..ce869b2d0 100644 --- a/double/mod_js.mbt +++ b/double/mod_js.mbt @@ -1,4 +1,4 @@ -// Copyright 2024 International Digital Economy Academy +// Copyright 2025 International Digital Economy Academy // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/double/mod_nonjs.mbt b/double/mod_nonjs.mbt index 03338c17b..ce9911cda 100644 --- a/double/mod_nonjs.mbt +++ b/double/mod_nonjs.mbt @@ -1,4 +1,4 @@ -// Copyright 2024 International Digital Economy Academy +// Copyright 2025 International Digital Economy Academy // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/double/pow_js.mbt b/double/pow_js.mbt index d9075166a..48f0c0cd7 100644 --- a/double/pow_js.mbt +++ b/double/pow_js.mbt @@ -1,4 +1,4 @@ -// Copyright 2024 International Digital Economy Academy +// Copyright 2025 International Digital Economy Academy // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/double/pow_nonjs.mbt b/double/pow_nonjs.mbt index 6918e0461..d9e55095f 100644 --- a/double/pow_nonjs.mbt +++ b/double/pow_nonjs.mbt @@ -1,4 +1,4 @@ -// Copyright 2024 International Digital Economy Academy +// Copyright 2025 International Digital Economy Academy // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/double/round.mbt b/double/round.mbt index 14050d016..bd570830b 100644 --- a/double/round.mbt +++ b/double/round.mbt @@ -1,4 +1,4 @@ -// Copyright 2024 International Digital Economy Academy +// Copyright 2025 International Digital Economy Academy // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/double/round_js.mbt b/double/round_js.mbt index 960adcdf1..454b2ad16 100644 --- a/double/round_js.mbt +++ b/double/round_js.mbt @@ -1,4 +1,4 @@ -// Copyright 2024 International Digital Economy Academy +// Copyright 2025 International Digital Economy Academy // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/double/round_wasm.mbt b/double/round_wasm.mbt index 0f263356c..9bb252e5a 100644 --- a/double/round_wasm.mbt +++ b/double/round_wasm.mbt @@ -1,4 +1,4 @@ -// Copyright 2024 International Digital Economy Academy +// Copyright 2025 International Digital Economy Academy // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/immut/list/list.mbt b/immut/list/list.mbt index 214a6ad82..5677cc525 100644 --- a/immut/list/list.mbt +++ b/immut/list/list.mbt @@ -1,4 +1,4 @@ -// Copyright 2024 International Digital Economy Academy +// Copyright 2025 International Digital Economy Academy // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. From 604ace1e01f242dda68284ab0fd692c458bca97b Mon Sep 17 00:00:00 2001 From: Hongbo Zhang Date: Thu, 9 Jan 2025 15:47:32 +0800 Subject: [PATCH 09/17] add Array::rev_iter (#1455) --- builtin/array.mbt | 32 ++++++++++++++++++++++++++++++++ builtin/builtin.mbti | 1 + immut/list/list.mbt | 6 ++++-- 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/builtin/array.mbt b/builtin/array.mbt index af3bceb3d..45bb62ae6 100644 --- a/builtin/array.mbt +++ b/builtin/array.mbt @@ -1661,6 +1661,38 @@ pub fn iter[T](self : Array[T]) -> Iter[T] { }) } +///| +/// Returns an iterator that yields elements from the array in reverse order, +/// from the last element to the first. +/// +/// Parameters: +/// +/// * `array` : The array to iterate over in reverse order. +/// +/// Returns an iterator that yields each element of the array, starting from the +/// last element and moving towards the first. +/// +/// Example: +/// +/// ```moonbit +/// test "Array::rev_iter" { +/// let arr = [1, 2, 3] +/// let result = [] +/// arr.rev_iter().each(fn(x) { result.push(x) }) +/// inspect!(result, content="[3, 2, 1]") +/// } +/// ``` +pub fn rev_iter[T](self : Array[T]) -> Iter[T] { + Iter::new(fn(yield_) { + for i = self.length() - 1; i >= 0; i = i - 1 { + guard let IterContinue = yield_(self.unsafe_get(i)) else { x => break x } + + } else { + IterContinue + } + }) +} + ///| /// Returns an iterator that provides both indices and values of the array in /// order. diff --git a/builtin/builtin.mbti b/builtin/builtin.mbti index 65dd2aabd..fb1231f23 100644 --- a/builtin/builtin.mbti +++ b/builtin/builtin.mbti @@ -107,6 +107,7 @@ impl Array { rev_fold[A, B](Self[A], init~ : B, (B, A) -> B) -> B rev_foldi[A, B](Self[A], init~ : B, (Int, B, A) -> B) -> B rev_inplace[T](Self[T]) -> Unit + rev_iter[T](Self[T]) -> Iter[T] search[T : Eq](Self[T], T) -> Int? search_by[T](Self[T], (T) -> Bool) -> Int? shrink_to_fit[T](Self[T]) -> Unit diff --git a/immut/list/list.mbt b/immut/list/list.mbt index 5677cc525..df436a3f9 100644 --- a/immut/list/list.mbt +++ b/immut/list/list.mbt @@ -30,9 +30,11 @@ pub fn T::to_string[A : Show](xs : T[A]) -> String { ///| pub impl[A : ToJson] ToJson for T[A] with to_json(self) { let capacity = self.length() - guard capacity != 0 else { return Array([]) } + guard capacity != 0 else { return [] } let jsons = Array::new(capacity~) - self.each(fn(a) { jsons.push(a.to_json()) }) + for a in self { + jsons.push(a.to_json()) + } Array(jsons) } From 0d1b37a34695fd08149247d33e3ba1071ab5d22e Mon Sep 17 00:00:00 2001 From: Haoxiang Fei Date: Thu, 9 Jan 2025 15:27:59 +0800 Subject: [PATCH 10/17] add some float utils functions --- float/float.mbt | 89 ++++++++++++++++++++++++++++++++++++++++++++ float/float_test.mbt | 45 ++++++++++++++++++++++ 2 files changed, 134 insertions(+) diff --git a/float/float.mbt b/float/float.mbt index 0f824ed65..cef7570a7 100644 --- a/float/float.mbt +++ b/float/float.mbt @@ -62,3 +62,92 @@ pub fn to_be_bytes(self : Float) -> Bytes { pub fn to_le_bytes(self : Float) -> Bytes { self.reinterpret_as_uint().to_le_bytes() } + +///| +/// Determines if the floating-point number is positive or negative infinity. +/// +/// Parameters: +/// +/// * `self` : The floating-point number to be checked. +/// +/// Returns a boolean value indicating whether the number is positive or negative +/// infinity. +/// +/// Example: +/// +/// ```moonbit +/// test "Float::is_inf" { +/// inspect!(@float.infinity.is_inf(), content="true") +/// inspect!(@float.neg_infinity.is_inf(), content="true") +/// inspect!((1.0 : Float).is_inf(), content="false") +/// } +/// ``` +pub fn Float::is_inf(self : Float) -> Bool { + self.is_pos_inf() || self.is_neg_inf() +} + +///| +/// Determines if the floating-point number is positive infinity. +/// +/// Parameters: +/// +/// * `self` : The floating-point number to be checked. +/// +/// Returns a boolean value indicating whether the number is positive infinity. +/// +/// Example: +/// +/// ```moonbit +/// test "Float::is_pos_inf" { +/// inspect!(@float.infinity.is_pos_inf(), content="true") +/// inspect!((1.0 : Float).is_pos_inf(), content="false") +/// inspect!(@float.neg_infinity.is_pos_inf(), content="false") +/// } +/// ``` +pub fn Float::is_pos_inf(self : Float) -> Bool { + self > max_value +} + +///| +/// Determines if the floating-point number is negative infinity. +/// +/// Parameters: +/// +/// * `self` : The floating-point number to be checked. +/// +/// Returns a boolean value indicating whether the number is negative infinity. +/// +/// Example: +/// +/// ```moonbit +/// test "Float::is_neg_inf" { +/// inspect!(@float.neg_infinity.is_neg_inf(), content="true") +/// inspect!((1.0 : Float).is_neg_inf(), content="false") +/// inspect!(@float.infinity.is_neg_inf(), content="false") +/// } +/// ``` +pub fn Float::is_neg_inf(self : Float) -> Bool { + self < min_value +} + +///| +/// Determines if the floating-point number is NaN (Not a Number). +/// +/// Parameters: +/// +/// * `self` : The floating-point number to be checked. +/// +/// Returns a boolean value indicating whether the number is NaN. +/// +/// Example: +/// +/// ```moonbit +/// test "Float::is_nan" { +/// inspect!(@float.not_a_number.is_nan(), content="true") +/// inspect!((1.0 : Float).is_nan(), content="false") +/// inspect!(@float.infinity.is_nan(), content="false") +/// } +/// ``` +pub fn Float::is_nan(self : Float) -> Bool { + self != self +} diff --git a/float/float_test.mbt b/float/float_test.mbt index 7756a4f0b..b2a440e86 100644 --- a/float/float_test.mbt +++ b/float/float_test.mbt @@ -70,3 +70,48 @@ test "Hash" { inspect!(a.a.hash(), content="749479517") inspect!((2.0 : Float).hash(), content="-1861484393") } + +test "Float::is_inf/special_values" { + inspect!(@float.infinity.is_inf(), content="true") + inspect!(@float.neg_infinity.is_inf(), content="true") + inspect!(@float.not_a_number.is_inf(), content="false") + inspect!((0.0 : Float).is_inf(), content="false") + inspect!(@float.max_value.is_inf(), content="false") + inspect!(@float.min_value.is_inf(), content="false") + inspect!(@float.min_positive.is_inf(), content="false") +} + +test "@float.Float::is_pos_inf/special_values" { + inspect!(@float.infinity.is_pos_inf(), content="true") + inspect!(@float.neg_infinity.is_pos_inf(), content="false") + inspect!(@float.not_a_number.is_pos_inf(), content="false") + inspect!(@float.max_value.is_pos_inf(), content="false") + inspect!((-@float.max_value).is_pos_inf(), content="false") + inspect!(@float.min_positive.is_pos_inf(), content="false") + inspect!((0.0 : Float).is_pos_inf(), content="false") + inspect!((1.0 : Float).is_pos_inf(), content="false") + inspect!((-1.0 : Float).is_pos_inf(), content="false") +} + +test "@float.Float::is_neg_inf" { + inspect!((-1.0 : Float).is_neg_inf(), content="false") + inspect!((0.0 : Float).is_neg_inf(), content="false") + inspect!((1.0 : Float).is_neg_inf(), content="false") + inspect!(@float.neg_infinity.is_neg_inf(), content="true") + inspect!(@float.infinity.is_neg_inf(), content="false") + inspect!(@float.not_a_number.is_neg_inf(), content="false") + inspect!(@float.min_value.is_neg_inf(), content="false") + inspect!((@float.min_value - 1.0e38).is_neg_inf(), content="true") +} + +test "@float.Float::is_nan" { + inspect!((0.0 : Float).is_nan(), content="false") + inspect!((1.0 : Float).is_nan(), content="false") + inspect!((-1.0 : Float).is_nan(), content="false") + inspect!(@float.not_a_number.is_nan(), content="true") + inspect!(@float.infinity.is_nan(), content="false") + inspect!(@float.neg_infinity.is_nan(), content="false") + inspect!(@float.max_value.is_nan(), content="false") + inspect!(@float.min_value.is_nan(), content="false") + inspect!(@float.min_positive.is_nan(), content="false") +} From 812a3004990a5eaf64987e80b31cfe27b4450d69 Mon Sep 17 00:00:00 2001 From: Haoxiang Fei Date: Thu, 9 Jan 2025 15:59:59 +0800 Subject: [PATCH 11/17] update moon info --- float/float.mbti | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/float/float.mbti b/float/float.mbti index c92ea5b90..a7c3e0a72 100644 --- a/float/float.mbti +++ b/float/float.mbti @@ -20,6 +20,10 @@ impl Float { default() -> Float exp(Float) -> Float floor(Float) -> Float + is_inf(Float) -> Bool + is_nan(Float) -> Bool + is_neg_inf(Float) -> Bool + is_pos_inf(Float) -> Bool ln(Float) -> Float op_mod(Float, Float) -> Float pow(Float, Float) -> Float From 2161d20cc1bdbcc583645e9f4d5e1c58036a26d0 Mon Sep 17 00:00:00 2001 From: Haoxiang Fei Date: Thu, 9 Jan 2025 17:25:34 +0800 Subject: [PATCH 12/17] fix license header --- float/float_test.mbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/float/float_test.mbt b/float/float_test.mbt index b2a440e86..4bf7b743b 100644 --- a/float/float_test.mbt +++ b/float/float_test.mbt @@ -1,4 +1,4 @@ -// Copyright 2024 International Digital Economy Academy +// Copyright 2025 International Digital Economy Academy // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. From f40719f43677ab8025077ec6af13059f8f8851bd Mon Sep 17 00:00:00 2001 From: Yu Zhang Date: Wed, 8 Jan 2025 15:44:32 +0800 Subject: [PATCH 13/17] add stringview --- builtin/char.mbt | 2 +- string/string.mbti | 14 +++ string/utils.mbt | 2 +- string/view.mbt | 240 +++++++++++++++++++++++++++++++++++++++++++ string/view_test.mbt | 183 +++++++++++++++++++++++++++++++++ 5 files changed, 439 insertions(+), 2 deletions(-) create mode 100644 string/view.mbt create mode 100644 string/view_test.mbt diff --git a/builtin/char.mbt b/builtin/char.mbt index 1978cab1d..244f60313 100644 --- a/builtin/char.mbt +++ b/builtin/char.mbt @@ -1,4 +1,4 @@ -// Copyright 2024 International Digital Economy Academy +// Copyright 2025 International Digital Economy Academy // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/string/string.mbti b/string/string.mbti index d51ad0cea..c19f92caf 100644 --- a/string/string.mbti +++ b/string/string.mbti @@ -3,6 +3,18 @@ package moonbitlang/core/string // Values // Types and methods +type StringIndex +impl Eq for StringIndex +impl Show for StringIndex + +type StringView +impl StringView { + length(Self) -> Int + op_as_view(Self, start~ : Int = .., end? : Int) -> Self + op_get(Self, Int) -> Char +} +impl Show for StringView + impl String { compare(String, String) -> Int concat(Array[String], separator~ : String = ..) -> String @@ -13,12 +25,14 @@ impl String { fold[A](String, init~ : A, (A, Char) -> A) -> A from_array(Array[Char]) -> String from_iter(Iter[Char]) -> String + index_at(String, Int, start~ : StringIndex = ..) -> StringIndex? index_of(String, String, from~ : Int = ..) -> Int is_blank(String) -> Bool is_empty(String) -> Bool iter(String) -> Iter[Char] iter2(String) -> Iter2[Int, Char] last_index_of(String, String, from~ : Int = ..) -> Int + op_as_view(String, start~ : StringIndex = .., end? : StringIndex) -> StringView pad_end(String, Int, Char) -> String pad_start(String, Int, Char) -> String repeat(String, Int) -> String diff --git a/string/utils.mbt b/string/utils.mbt index 4e3755a95..d1ada06cb 100644 --- a/string/utils.mbt +++ b/string/utils.mbt @@ -1,4 +1,4 @@ -// Copyright 2024 International Digital Economy Academy +// Copyright 2025 International Digital Economy Academy // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/string/view.mbt b/string/view.mbt new file mode 100644 index 000000000..fbbce6cd2 --- /dev/null +++ b/string/view.mbt @@ -0,0 +1,240 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// 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. + +///| +/// A `StringView` represents a view of a String that maintains proper Unicode +/// character boundaries. It allows safe access to a substring while handling +/// multi-byte characters correctly. +struct StringView { + // # Fields + // + // - `str`: The source String being viewed + // - `start`: Starting UTF-16 code unit index into the string + // - `end`: Ending UTF-16 code unit index into the string (not included) + // + // `len` is not included because it will make the operation of `op_as_view` + // has complexity O(n) where n is the length of the code points in the view. + str : String + start : Int + end : Int +} + +///| +/// A `StringIndex` represents an unicode-aware position within a string. +/// +/// This type is intentionally opaque - it is recommended to use the +/// `str.index_at(offset_by: Int) -> StringIndex` method to construct a +/// `StringIndex`. +type StringIndex Int derive(Show, Eq) + +///| +/// Returns a `StringIndex` representing the position after skipping `offset_by` +/// Unicode characters. +/// +/// # Examples +/// +/// ``` +/// let str = "🤣🤣🤣" +/// guard let Some(offset) = str.index_at(1) // Skip 1 character +/// inspect!(offset, content="StringIndex(2)") // Points to second emoji +/// +/// guard let Some(offset) = str.index_at(start=offset, 1) // Skip 2 characters +/// inspect!(offset, content="StringIndex(4)") // Points to third emoji +/// +/// let offset = str.index_at(3) // Skip 3 characters +/// inspect!(offset, content="None") // Beyond end of string +/// ``` +pub fn index_at( + self : String, + offset_by : Int, + start~ : StringIndex = 0 +) -> StringIndex? { + let str_len = self.length() + let mut utf16_offset = start._ + let mut char_count = 0 + while utf16_offset < str_len && char_count < offset_by { + let c1 = self[utf16_offset] + // check if this is a surrogate pair + if is_leading_surrogate(c1) && utf16_offset + 1 < str_len { + let c2 = self[utf16_offset + 1] + if is_trailing_surrogate(c2) { + utf16_offset = utf16_offset + 2 + char_count = char_count + 1 + continue + } + } + // single utf-16 code unit + utf16_offset = utf16_offset + 1 + char_count = char_count + 1 + } + // Return None if: + // 1. We couldn't reach the requested character offset + // 2. The resulting offset is beyond the end of the string + if char_count < offset_by || utf16_offset >= str_len { + None + } else { + Some(utf16_offset) + } +} + +///| +/// Returns the number of Unicode characters in this view. +/// +/// Note this has O(n) complexity where n is the length of the code points in +/// the view. +pub fn length(self : StringView) -> Int { + let mut len = 0 + for index = self.start; index < self.end; index = index + 1 { + let c1 = self.str[index] + if is_leading_surrogate(c1) && index + 1 < self.end { + let c2 = self.str[index + 1] + if is_trailing_surrogate(c2) { + len = len + 1 + continue index + 2 + } else { + abort("invalid surrogate pair") + } + } + len = len + 1 + } + len +} + +///| +/// Creates a `StringView` into a `String`. +/// +/// # Example +/// +/// ``` +/// let str = "Hello🤣🤣🤣" +/// guard let Some(start) = str.index_at(1) +/// guard let Some(end) = str.index_at(6) +/// let view = str[start:end] +/// inspect!(view, content= +/// #|"ello🤣" +///) +/// ``` +pub fn op_as_view( + self : String, + start~ : StringIndex = 0, + end? : StringIndex +) -> StringView { + let str_len = self.length() + let start = start._ + let end = match end { + Some(e) => e._ + None => str_len + } + guard start >= 0 && start <= end && end <= str_len else { + abort("Invalid index for StringView") + } + { str: self, start, end } +} + +///| +/// Creates a new `StringView` from an existing `StringView`. +/// +/// # Example +/// +/// ``` +/// let str = "Hello🤣🤣🤣" +/// guard let Some(start) = str.index_at(1) +/// guard let Some(end) = str.index_at(7) +/// let view = str[start:end] +/// let view2 = view[1:5] +/// inspect!(view2, content= +/// #|"llo🤣" +/// ) +/// ``` +pub fn op_as_view( + self : StringView, + start~ : Int = 0, + end? : Int +) -> StringView { + match end { + Some(end) => { + guard start <= end else { abort("Invalid index for StringView") } + guard let Some(start) = index_at(self.str, start, start=self.start) + // TODO: provide index_at_rev or index_at2 to avoid repeatedly iterate the string + guard let Some(end) = index_at(self.str, end, start=self.start) + guard start._ <= self.end && end._ <= self.end else { + abort("Invalid index for StringView") + } + { str: self.str, start: start._, end: end._ } + } + None => { + guard let Some(start) = index_at(self.str, start, start=self.start) + guard start._ <= self.end else { abort("Invalid index for StringView") } + { str: self.str, start: start._, end: self.end } + } + } +} + +///| +/// Return the character at the given index. +/// +/// This method has O(n) complexity. +/// +/// # Example +/// +/// ``` +/// let str = "Hello🤣🤣🤣" +/// guard let Some(start) = str.index_at(1) +/// guard let Some(end) = str.index_at(6) +/// let view = str[start:end] +/// inspect!(view[0], content="'e'") +/// inspect!(view[4], content="'🤣'") +/// ``` +pub fn op_get(self : StringView, index : Int) -> Char { + guard index >= 0 else { + abort("Index out of bounds: cannot access negative index") + } + let mut utf16_offset = self.start + let mut char_count = 0 + while char_count < index && utf16_offset < self.end { + let c1 = self.str[utf16_offset] + if is_leading_surrogate(c1) && utf16_offset + 1 < self.str.length() { + let c2 = self.str[utf16_offset + 1] + if is_trailing_surrogate(c2) { + utf16_offset = utf16_offset + 2 + char_count = char_count + 1 + continue + } else { + abort("invalid surrogate pair") + } + } + utf16_offset = utf16_offset + 1 + char_count = char_count + 1 + } + guard char_count == index && utf16_offset < self.end else { + abort("Index out of bounds: cannot access index \{index}") + } + let c1 = self.str[utf16_offset] + if is_leading_surrogate(c1) { + let c2 = self.str[utf16_offset + 1] + if is_trailing_surrogate(c2) { + code_point_of_surrogate_pair(c1, c2) + } else { + abort("invalid surrogate pair") + } + } else { + c1 + } +} + +///| +pub impl Show for StringView with output(self, logger) { + let substr = self.str.substring(start=self.start, end=self.end) + String::output(substr, logger) +} diff --git a/string/view_test.mbt b/string/view_test.mbt new file mode 100644 index 000000000..a82284179 --- /dev/null +++ b/string/view_test.mbt @@ -0,0 +1,183 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// 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. + +test "index_at" { + let str = "Hello" + let offset = str.index_at(3) + inspect!(offset, content="Some(StringIndex(3))") + let offset = str.index_at(5) + inspect!(offset, content="None") +} + +test "index_at with surrogate pairs" { + let str = "🤣🤣🤣" + let offset = str.index_at(1) + inspect!(offset, content="Some(StringIndex(2))") + let offset = str.index_at(2) + inspect!(offset, content="Some(StringIndex(4))") + let offset = str.index_at(3) + inspect!(offset, content="None") +} + +test "stringview basic" { + let str = "Hello🤣🤣🤣" + guard let Some(start) = str.index_at(1) + guard let Some(end) = str.index_at(6) + inspect!( + str[start:], + content= + #|"ello🤣🤣🤣" + , + ) + inspect!( + str[:end], + content= + #|"Hello🤣" + , + ) + inspect!( + str[start:end], + content= + #|"ello🤣" + , + ) + inspect!( + str[:], + content= + #|"Hello🤣🤣🤣" + , + ) +} + +test "stringview basic2" { + let str = "He🤣🤣🤣llo" + guard let Some(start) = str.index_at(1) + guard let Some(end) = str.index_at(7) + inspect!( + str[start:], + content= + #|"e🤣🤣🤣llo" + , + ) + inspect!( + str[:end], + content= + #|"He🤣🤣🤣ll" + , + ) + inspect!( + str[start:end], + content= + #|"e🤣🤣🤣ll" + , + ) + inspect!( + str[:], + content= + #|"He🤣🤣🤣llo" + , + ) +} + +test "stringview subview" { + let str = "Hello🤣🤣🤣" + guard let Some(start) = str.index_at(1) + guard let Some(end) = str.index_at(6) + let view = str[start:end] + inspect!( + view[1:], + content= + #|"llo🤣" + , + ) + inspect!( + view[1:4], + content= + #|"llo" + , + ) + inspect!( + view[:4], + content= + #|"ello" + , + ) + inspect!( + view[:], + content= + #|"ello🤣" + , + ) +} + +test "stringview op_get" { + let str = "Hello🤣🤣🤣" + guard let Some(start) = str.index_at(1) + guard let Some(end) = str.index_at(6) + let view = str[start:end] + inspect!(view[0], content="'e'") + inspect!(view[4], content="'🤣'") +} + +test "stringview length" { + let str = "Hello🤣🤣🤣" + guard let Some(start) = str.index_at(1) + guard let Some(end) = str.index_at(6) + let view = str[start:end] + inspect!(view.length(), content="5") +} + +test "stringview empty" { + let str = "hello" + guard let Some(start) = str.index_at(0) + guard let Some(end) = str.index_at(0) + let view = str[start:end] + inspect!(view.length(), content="0") +} + +test "panic out of bounds1" { + let str = "Hello🤣🤣🤣" + guard let Some(start) = str.index_at(1) + guard let Some(end) = str.index_at(6) + let view = str[start:end] + let _ = view[5] + +} + +test "panic out of bounds2" { + let str = "Hello🤣🤣🤣" + guard let Some(start) = str.index_at(1) + guard let Some(end) = str.index_at(6) + let view = str[start:end] + let _ = view[6:] + +} + +test "panic out of bounds3" { + let str = "Hello🤣🤣🤣" + guard let Some(start) = str.index_at(1) + guard let Some(end) = str.index_at(6) + let view = str[start:end] + let _ = view[:6] + +} + +test "panic out of bounds4" { + let str = "hello" + guard let Some(start) = str.index_at(0) + guard let Some(end) = str.index_at(0) + let view = str[start:end] + let _ = view[0] + +} From 85d82eb96283e0aecc93601cbc1b1780a308b609 Mon Sep 17 00:00:00 2001 From: Young-Flash Date: Thu, 9 Jan 2025 10:50:52 +0800 Subject: [PATCH 14/17] internal: add sys package --- sys/internal/ffi/ffi.mbti | 34 ++++++++ sys/internal/ffi/moon.pkg.json | 12 +++ sys/internal/ffi/sys_js.mbt | 70 ++++++++++++++++ sys/internal/ffi/sys_native.mbt | 91 +++++++++++++++++++++ sys/internal/ffi/sys_wasm.mbt | 138 ++++++++++++++++++++++++++++++++ sys/moon.pkg.json | 9 +++ sys/sys.mbt | 38 +++++++++ sys/sys.mbti | 19 +++++ sys/sys_test.mbt | 32 ++++++++ 9 files changed, 443 insertions(+) create mode 100644 sys/internal/ffi/ffi.mbti create mode 100644 sys/internal/ffi/moon.pkg.json create mode 100644 sys/internal/ffi/sys_js.mbt create mode 100644 sys/internal/ffi/sys_native.mbt create mode 100644 sys/internal/ffi/sys_wasm.mbt create mode 100644 sys/moon.pkg.json create mode 100644 sys/sys.mbt create mode 100644 sys/sys.mbti create mode 100644 sys/sys_test.mbt diff --git a/sys/internal/ffi/ffi.mbti b/sys/internal/ffi/ffi.mbti new file mode 100644 index 000000000..c9dd6fc2b --- /dev/null +++ b/sys/internal/ffi/ffi.mbti @@ -0,0 +1,34 @@ +package moonbitlang/core/sys/internal/ffi + +// Values +fn exit(Int) -> Unit + +fn get_cli_args() -> Array[String] + +fn get_env_vars() -> Map[String, String] + +fn now() -> UInt64 + +fn set_env_var(String, String) -> Unit + +fn string_array_from_extern(XExternStringArray) -> Array[String] + +fn string_from_extern(XExternString) -> String + +fn string_to_extern(String) -> XExternString + +// Types and methods +pub(all) type XExternString + +pub(all) type XExternStringArray + +pub(all) type XStringArrayReadHandle + +pub(all) type XStringCreateHandle + +pub(all) type XStringReadHandle + +// Type aliases + +// Traits + diff --git a/sys/internal/ffi/moon.pkg.json b/sys/internal/ffi/moon.pkg.json new file mode 100644 index 000000000..e61a10879 --- /dev/null +++ b/sys/internal/ffi/moon.pkg.json @@ -0,0 +1,12 @@ +{ + "import": [ + "moonbitlang/core/builtin", + "moonbitlang/core/string", + "moonbitlang/core/array" + ], + "targets": { + "sys_wasm.mbt": ["wasm", "wasm-gc"], + "sys_js.mbt": ["js"], + "sys_native.mbt": ["native"] + } +} \ No newline at end of file diff --git a/sys/internal/ffi/sys_js.mbt b/sys/internal/ffi/sys_js.mbt new file mode 100644 index 000000000..4c8f95d2b --- /dev/null +++ b/sys/internal/ffi/sys_js.mbt @@ -0,0 +1,70 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// 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. + +///| +pub fn get_cli_args() -> Array[String] { + get_cli_args_internal() +} + +///| +extern "js" fn get_cli_args_internal() -> Array[String] = + #| function() { + #| return process.argv; + #| } + +///| +pub fn get_env_vars() -> Map[String, String] { + let tmp = get_env_vars_internal() + let res = {} + for i = 0; i < tmp.length(); i = i + 2 { + res[tmp[i]] = tmp[i + 1] + } + res +} + +///| +extern "js" fn get_env_vars_internal() -> Array[String] = + #| function() { + #| const env = process.env; + #| const result = []; + #| for (const key in env) { + #| result.push(key); + #| result.push(env[key]); + #| } + #| return result; + #| } + +///| +pub fn set_env_var(key : String, value : String) -> Unit { + set_env_var_internal(key, value) +} + +///| +extern "js" fn set_env_var_internal(key : String, value : String) -> Unit = + #| function(key, value) { + #| process.env[key] = value; + #| } + +///| +pub extern "js" fn exit(code : Int) -> Unit = + #| function(code) { + #| process.exit(code); + #| } + +///| +pub extern "js" fn now() -> UInt64 = + #| function() { + #| const nowMs = Date.now(); + #| return ({ hi : Number(BigInt(nowMs) >> 32n), lo : nowMs & 0xFFFFFFFF }); + #| } diff --git a/sys/internal/ffi/sys_native.mbt b/sys/internal/ffi/sys_native.mbt new file mode 100644 index 000000000..8614ed91d --- /dev/null +++ b/sys/internal/ffi/sys_native.mbt @@ -0,0 +1,91 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// 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. + +///| +fn internal_get_cli_args() -> FixedArray[Bytes] = "$moonbit.get_cli_args" + +///| +pub fn get_cli_args() -> Array[String] { + fn utf8_bytes_to_mbt_string(bytes : Bytes) -> String { + let res : Array[Char] = [] + let len = bytes.length() + let mut i = 0 + while i < len { + let mut c = bytes[i].to_int() + if c < 0x80 { + res.push(Char::from_int(c)) + i += 1 + } else if c < 0xE0 { + if i + 1 >= len { + break + } + c = ((c & 0x1F) << 6) | (bytes[i + 1].to_int() & 0x3F) + res.push(Char::from_int(c)) + i += 2 + } else if c < 0xF0 { + if i + 2 >= len { + break + } + c = ((c & 0x0F) << 12) | + ((bytes[i + 1].to_int() & 0x3F) << 6) | + (bytes[i + 2].to_int() & 0x3F) + res.push(Char::from_int(c)) + i += 3 + } else { + if i + 3 >= len { + break + } + c = ((c & 0x07) << 18) | + ((bytes[i + 1].to_int() & 0x3F) << 12) | + ((bytes[i + 2].to_int() & 0x3F) << 6) | + (bytes[i + 3].to_int() & 0x3F) + c -= 0x10000 + res.push(Char::from_int((c >> 10) + 0xD800)) + res.push(Char::from_int((c & 0x3FF) + 0xDC00)) + i += 4 + } + } + String::from_array(res) + } + + Array::from_fixed_array( + internal_get_cli_args().map(fn { + // Here we assume that the CLI arguments are encoded in well-formed UTF-8. + arg => utf8_bytes_to_mbt_string(arg) + }), + ) +} + +///| +pub fn get_env_vars() -> Map[String, String] { + // not implement yet + panic() +} + +///| +pub fn set_env_var(_key : String, _value : String) -> Unit { + // not implement yet + panic() +} + +///| +pub extern "c" fn exit(code : Int) -> Unit = "exit" + +///| +extern "c" fn time(ptr : Int) -> UInt64 = "time" + +///| +pub fn now() -> UInt64 { + time(0) +} diff --git a/sys/internal/ffi/sys_wasm.mbt b/sys/internal/ffi/sys_wasm.mbt new file mode 100644 index 000000000..3727b4358 --- /dev/null +++ b/sys/internal/ffi/sys_wasm.mbt @@ -0,0 +1,138 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// 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. + +///| +pub(all) type XStringCreateHandle + +///| +pub(all) type XStringReadHandle + +///| +pub(all) type XExternString + +///| +fn begin_create_string() -> XStringCreateHandle = "__moonbit_fs_unstable" "begin_create_string" + +///| +fn string_append_char(handle : XStringCreateHandle, ch : Char) = "__moonbit_fs_unstable" "string_append_char" + +///| +fn finish_create_string(handle : XStringCreateHandle) -> XExternString = "__moonbit_fs_unstable" "finish_create_string" + +///| +pub fn string_to_extern(s : String) -> XExternString { + let handle = begin_create_string() + for i = 0; i < s.length(); i = i + 1 { + string_append_char(handle, s[i]) + } + finish_create_string(handle) +} + +///| +fn begin_read_string(s : XExternString) -> XStringReadHandle = "__moonbit_fs_unstable" "begin_read_string" + +///| Read one char from the string, returns -1 if the end of the string is reached. +/// The number returned is the unicode codepoint of the character. +fn string_read_char(handle : XStringReadHandle) -> Int = "__moonbit_fs_unstable" "string_read_char" + +///| +fn finish_read_string(handle : XStringReadHandle) = "__moonbit_fs_unstable" "finish_read_string" + +///| +pub fn string_from_extern(e : XExternString) -> String { + let buf = StringBuilder::new() + let handle = begin_read_string(e) + while true { + let ch = string_read_char(handle) + if ch == -1 { + break + } else { + buf.write_char(Char::from_int(ch)) + } + } + finish_read_string(handle) + buf.to_string() +} + +///| +pub(all) type XStringArrayReadHandle + +///| +pub(all) type XExternStringArray + +///| +fn begin_read_string_array(sa : XExternStringArray) -> XStringArrayReadHandle = "__moonbit_fs_unstable" "begin_read_string_array" + +///| +fn string_array_read_string(handle : XStringArrayReadHandle) -> XExternString = "__moonbit_fs_unstable" "string_array_read_string" + +///| +fn finish_read_string_array(handle : XStringArrayReadHandle) = "__moonbit_fs_unstable" "finish_read_string_array" + +///| +pub fn string_array_from_extern(e : XExternStringArray) -> Array[String] { + let buf = Array::new() + let handle = begin_read_string_array(e) + while true { + let extern_str = string_array_read_string(handle) + let str = string_from_extern(extern_str) + // keep "ffi_end_of_/string_array" same with moonrun + if str == "ffi_end_of_/string_array" { + break + } else { + buf.push(str) + } + } + finish_read_string_array(handle) + buf +} + +///| +pub fn get_cli_args() -> Array[String] { + let args = get_cli_args_ffi() + string_array_from_extern(args) +} + +///| +fn get_cli_args_ffi() -> XExternStringArray = "__moonbit_fs_unstable" "args_get" + +///| +pub fn get_env_vars() -> Map[String, String] { + let env = get_env_vars_ffi() + let tmp = string_array_from_extern(env) + let res = {} + for i = 0; i < tmp.length(); i = i + 2 { + res[tmp[i]] = tmp[i + 1] + } + res +} + +///| +fn get_env_vars_ffi() -> XExternStringArray = "__moonbit_fs_unstable" "get_env_vars" + +///| +pub fn set_env_var(key : String, value : String) -> Unit { + let key = string_to_extern(key) + let value = string_to_extern(value) + set_env_var_ffi(key, value) +} + +///| +fn set_env_var_ffi(key : XExternString, value : XExternString) = "__moonbit_fs_unstable" "set_env_var" + +///| +pub fn exit(code : Int) = "__moonbit_sys_unstable" "exit" + +///| +pub fn now() -> UInt64 = "__moonbit_time_unstable" "now" diff --git a/sys/moon.pkg.json b/sys/moon.pkg.json new file mode 100644 index 000000000..b3f81fba9 --- /dev/null +++ b/sys/moon.pkg.json @@ -0,0 +1,9 @@ +{ + "import": [ + "moonbitlang/core/sys/internal/ffi", + "moonbitlang/core/builtin" + ], + "targets": { + "sys_test.mbt": ["not", "native"] + } +} diff --git a/sys/sys.mbt b/sys/sys.mbt new file mode 100644 index 000000000..9f02a1700 --- /dev/null +++ b/sys/sys.mbt @@ -0,0 +1,38 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// 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. + +///| +pub fn get_cli_args() -> Array[String] { + @ffi.get_cli_args() +} + +///| +pub fn get_env_vars() -> Map[String, String] { + @ffi.get_env_vars() +} + +///| +pub fn set_env_var(key : String, value : String) -> Unit { + @ffi.set_env_var(key, value) +} + +///| +pub fn exit(code : Int) -> Unit { + @ffi.exit(code) +} + +///| +pub fn now() -> UInt64 { + @ffi.now() +} diff --git a/sys/sys.mbti b/sys/sys.mbti new file mode 100644 index 000000000..7305b0acf --- /dev/null +++ b/sys/sys.mbti @@ -0,0 +1,19 @@ +package moonbitlang/core/sys + +// Values +fn exit(Int) -> Unit + +fn get_cli_args() -> Array[String] + +fn get_env_vars() -> Map[String, String] + +fn now() -> UInt64 + +fn set_env_var(String, String) -> Unit + +// Types and methods + +// Type aliases + +// Traits + diff --git a/sys/sys_test.mbt b/sys/sys_test.mbt new file mode 100644 index 000000000..4071a2af5 --- /dev/null +++ b/sys/sys_test.mbt @@ -0,0 +1,32 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// 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. + +test "get_env_vars" { + let (k, v) = ("THIS_IS_A_TEST_FOR_ENV_VAR", "IT_WORKS") + let old_env = @sys.get_env_vars()[k] + inspect!( + old_env, + content= + #|None + , + ) + @sys.set_env_var(k, v) + let res = @sys.get_env_vars()[k] + inspect!( + res, + content= + #|Some("IT_WORKS") + , + ) +} From 7cb803580f4a19d795b358003cb0b6a6d401474c Mon Sep 17 00:00:00 2001 From: zihang Date: Thu, 9 Jan 2025 15:34:11 +0800 Subject: [PATCH 15/17] chore: rename package --- {sys => env}/internal/ffi/ffi.mbti | 0 {sys => env}/internal/ffi/moon.pkg.json | 0 {sys => env}/internal/ffi/sys_js.mbt | 0 {sys => env}/internal/ffi/sys_native.mbt | 0 {sys => env}/internal/ffi/sys_wasm.mbt | 0 {sys => env}/moon.pkg.json | 2 +- {sys => env}/sys.mbt | 0 {sys => env}/sys.mbti | 0 {sys => env}/sys_test.mbt | 6 +++--- 9 files changed, 4 insertions(+), 4 deletions(-) rename {sys => env}/internal/ffi/ffi.mbti (100%) rename {sys => env}/internal/ffi/moon.pkg.json (100%) rename {sys => env}/internal/ffi/sys_js.mbt (100%) rename {sys => env}/internal/ffi/sys_native.mbt (100%) rename {sys => env}/internal/ffi/sys_wasm.mbt (100%) rename {sys => env}/moon.pkg.json (73%) rename {sys => env}/sys.mbt (100%) rename {sys => env}/sys.mbti (100%) rename {sys => env}/sys_test.mbt (89%) diff --git a/sys/internal/ffi/ffi.mbti b/env/internal/ffi/ffi.mbti similarity index 100% rename from sys/internal/ffi/ffi.mbti rename to env/internal/ffi/ffi.mbti diff --git a/sys/internal/ffi/moon.pkg.json b/env/internal/ffi/moon.pkg.json similarity index 100% rename from sys/internal/ffi/moon.pkg.json rename to env/internal/ffi/moon.pkg.json diff --git a/sys/internal/ffi/sys_js.mbt b/env/internal/ffi/sys_js.mbt similarity index 100% rename from sys/internal/ffi/sys_js.mbt rename to env/internal/ffi/sys_js.mbt diff --git a/sys/internal/ffi/sys_native.mbt b/env/internal/ffi/sys_native.mbt similarity index 100% rename from sys/internal/ffi/sys_native.mbt rename to env/internal/ffi/sys_native.mbt diff --git a/sys/internal/ffi/sys_wasm.mbt b/env/internal/ffi/sys_wasm.mbt similarity index 100% rename from sys/internal/ffi/sys_wasm.mbt rename to env/internal/ffi/sys_wasm.mbt diff --git a/sys/moon.pkg.json b/env/moon.pkg.json similarity index 73% rename from sys/moon.pkg.json rename to env/moon.pkg.json index b3f81fba9..fd7cac1ca 100644 --- a/sys/moon.pkg.json +++ b/env/moon.pkg.json @@ -1,6 +1,6 @@ { "import": [ - "moonbitlang/core/sys/internal/ffi", + "moonbitlang/core/env/internal/ffi", "moonbitlang/core/builtin" ], "targets": { diff --git a/sys/sys.mbt b/env/sys.mbt similarity index 100% rename from sys/sys.mbt rename to env/sys.mbt diff --git a/sys/sys.mbti b/env/sys.mbti similarity index 100% rename from sys/sys.mbti rename to env/sys.mbti diff --git a/sys/sys_test.mbt b/env/sys_test.mbt similarity index 89% rename from sys/sys_test.mbt rename to env/sys_test.mbt index 4071a2af5..9d6126956 100644 --- a/sys/sys_test.mbt +++ b/env/sys_test.mbt @@ -14,15 +14,15 @@ test "get_env_vars" { let (k, v) = ("THIS_IS_A_TEST_FOR_ENV_VAR", "IT_WORKS") - let old_env = @sys.get_env_vars()[k] + let old_env = @env.get_env_vars()[k] inspect!( old_env, content= #|None , ) - @sys.set_env_var(k, v) - let res = @sys.get_env_vars()[k] + @env.set_env_var(k, v) + let res = @env.get_env_vars()[k] inspect!( res, content= From 102e8a6923ff6685d738a6abb21eebdca78dc878 Mon Sep 17 00:00:00 2001 From: zihang Date: Thu, 9 Jan 2025 16:27:39 +0800 Subject: [PATCH 16/17] refactor: remove unsupported apis --- env/env.mbti | 13 +++++ env/internal/ffi/ffi.mbti | 8 +-- env/internal/ffi/moon.pkg.json | 6 +-- env/internal/ffi/sys_js.mbt | 52 +++--------------- env/internal/ffi/sys_native.mbt | 94 ++++++++++++++------------------- env/internal/ffi/sys_wasm.mbt | 27 ---------- env/sys.mbt | 19 ++----- env/sys.mbti | 19 ------- env/sys_test.mbt | 32 ----------- 9 files changed, 66 insertions(+), 204 deletions(-) create mode 100644 env/env.mbti delete mode 100644 env/sys.mbti delete mode 100644 env/sys_test.mbt diff --git a/env/env.mbti b/env/env.mbti new file mode 100644 index 000000000..117eadf27 --- /dev/null +++ b/env/env.mbti @@ -0,0 +1,13 @@ +package moonbitlang/core/env + +// Values +fn args() -> Array[String] + +fn now() -> UInt64 + +// Types and methods + +// Type aliases + +// Traits + diff --git a/env/internal/ffi/ffi.mbti b/env/internal/ffi/ffi.mbti index c9dd6fc2b..d8b3c5de1 100644 --- a/env/internal/ffi/ffi.mbti +++ b/env/internal/ffi/ffi.mbti @@ -1,16 +1,10 @@ -package moonbitlang/core/sys/internal/ffi +package moonbitlang/core/env/internal/ffi // Values -fn exit(Int) -> Unit - fn get_cli_args() -> Array[String] -fn get_env_vars() -> Map[String, String] - fn now() -> UInt64 -fn set_env_var(String, String) -> Unit - fn string_array_from_extern(XExternStringArray) -> Array[String] fn string_from_extern(XExternString) -> String diff --git a/env/internal/ffi/moon.pkg.json b/env/internal/ffi/moon.pkg.json index e61a10879..7f34ece99 100644 --- a/env/internal/ffi/moon.pkg.json +++ b/env/internal/ffi/moon.pkg.json @@ -1,8 +1,8 @@ { "import": [ - "moonbitlang/core/builtin", - "moonbitlang/core/string", - "moonbitlang/core/array" + "moonbitlang/core/builtin", + "moonbitlang/core/string", + "moonbitlang/core/array" ], "targets": { "sys_wasm.mbt": ["wasm", "wasm-gc"], diff --git a/env/internal/ffi/sys_js.mbt b/env/internal/ffi/sys_js.mbt index 4c8f95d2b..7b96fb90e 100644 --- a/env/internal/ffi/sys_js.mbt +++ b/env/internal/ffi/sys_js.mbt @@ -13,53 +13,13 @@ // limitations under the License. ///| -pub fn get_cli_args() -> Array[String] { - get_cli_args_internal() -} - -///| -extern "js" fn get_cli_args_internal() -> Array[String] = - #| function() { - #| return process.argv; - #| } - -///| -pub fn get_env_vars() -> Map[String, String] { - let tmp = get_env_vars_internal() - let res = {} - for i = 0; i < tmp.length(); i = i + 2 { - res[tmp[i]] = tmp[i + 1] - } - res -} - -///| -extern "js" fn get_env_vars_internal() -> Array[String] = +pub extern "js" fn get_cli_args() -> Array[String] = #| function() { - #| const env = process.env; - #| const result = []; - #| for (const key in env) { - #| result.push(key); - #| result.push(env[key]); - #| } - #| return result; - #| } - -///| -pub fn set_env_var(key : String, value : String) -> Unit { - set_env_var_internal(key, value) -} - -///| -extern "js" fn set_env_var_internal(key : String, value : String) -> Unit = - #| function(key, value) { - #| process.env[key] = value; - #| } - -///| -pub extern "js" fn exit(code : Int) -> Unit = - #| function(code) { - #| process.exit(code); + #| if (typeof process !== "undefined" && typeof process.argv !== "undefined") { + #| return process.argv; + #| } else { + #| return []; + #| } #| } ///| diff --git a/env/internal/ffi/sys_native.mbt b/env/internal/ffi/sys_native.mbt index 8614ed91d..d8d43266f 100644 --- a/env/internal/ffi/sys_native.mbt +++ b/env/internal/ffi/sys_native.mbt @@ -17,48 +17,6 @@ fn internal_get_cli_args() -> FixedArray[Bytes] = "$moonbit.get_cli_args" ///| pub fn get_cli_args() -> Array[String] { - fn utf8_bytes_to_mbt_string(bytes : Bytes) -> String { - let res : Array[Char] = [] - let len = bytes.length() - let mut i = 0 - while i < len { - let mut c = bytes[i].to_int() - if c < 0x80 { - res.push(Char::from_int(c)) - i += 1 - } else if c < 0xE0 { - if i + 1 >= len { - break - } - c = ((c & 0x1F) << 6) | (bytes[i + 1].to_int() & 0x3F) - res.push(Char::from_int(c)) - i += 2 - } else if c < 0xF0 { - if i + 2 >= len { - break - } - c = ((c & 0x0F) << 12) | - ((bytes[i + 1].to_int() & 0x3F) << 6) | - (bytes[i + 2].to_int() & 0x3F) - res.push(Char::from_int(c)) - i += 3 - } else { - if i + 3 >= len { - break - } - c = ((c & 0x07) << 18) | - ((bytes[i + 1].to_int() & 0x3F) << 12) | - ((bytes[i + 2].to_int() & 0x3F) << 6) | - (bytes[i + 3].to_int() & 0x3F) - c -= 0x10000 - res.push(Char::from_int((c >> 10) + 0xD800)) - res.push(Char::from_int((c & 0x3FF) + 0xDC00)) - i += 4 - } - } - String::from_array(res) - } - Array::from_fixed_array( internal_get_cli_args().map(fn { // Here we assume that the CLI arguments are encoded in well-formed UTF-8. @@ -68,20 +26,48 @@ pub fn get_cli_args() -> Array[String] { } ///| -pub fn get_env_vars() -> Map[String, String] { - // not implement yet - panic() -} - -///| -pub fn set_env_var(_key : String, _value : String) -> Unit { - // not implement yet - panic() +fn utf8_bytes_to_mbt_string(bytes : Bytes) -> String { + let res : Array[Char] = Array::new(capacity=bytes.length()) + let len = bytes.length() + let mut i = 0 + while i < len { + let mut c = bytes[i].to_int() + if c < 0x80 { + res.push(Char::from_int(c)) + i += 1 + } else if c < 0xE0 { + if i + 1 >= len { + break + } + c = ((c & 0x1F) << 6) | (bytes[i + 1].to_int() & 0x3F) + res.push(Char::from_int(c)) + i += 2 + } else if c < 0xF0 { + if i + 2 >= len { + break + } + c = ((c & 0x0F) << 12) | + ((bytes[i + 1].to_int() & 0x3F) << 6) | + (bytes[i + 2].to_int() & 0x3F) + res.push(Char::from_int(c)) + i += 3 + } else { + if i + 3 >= len { + break + } + c = ((c & 0x07) << 18) | + ((bytes[i + 1].to_int() & 0x3F) << 12) | + ((bytes[i + 2].to_int() & 0x3F) << 6) | + (bytes[i + 3].to_int() & 0x3F) + c -= 0x10000 + res.push(Char::from_int((c >> 10) + 0xD800)) + res.push(Char::from_int((c & 0x3FF) + 0xDC00)) + i += 4 + } + } + res.to_string() } -///| -pub extern "c" fn exit(code : Int) -> Unit = "exit" - ///| extern "c" fn time(ptr : Int) -> UInt64 = "time" diff --git a/env/internal/ffi/sys_wasm.mbt b/env/internal/ffi/sys_wasm.mbt index 3727b4358..1460398c0 100644 --- a/env/internal/ffi/sys_wasm.mbt +++ b/env/internal/ffi/sys_wasm.mbt @@ -107,32 +107,5 @@ pub fn get_cli_args() -> Array[String] { ///| fn get_cli_args_ffi() -> XExternStringArray = "__moonbit_fs_unstable" "args_get" -///| -pub fn get_env_vars() -> Map[String, String] { - let env = get_env_vars_ffi() - let tmp = string_array_from_extern(env) - let res = {} - for i = 0; i < tmp.length(); i = i + 2 { - res[tmp[i]] = tmp[i + 1] - } - res -} - -///| -fn get_env_vars_ffi() -> XExternStringArray = "__moonbit_fs_unstable" "get_env_vars" - -///| -pub fn set_env_var(key : String, value : String) -> Unit { - let key = string_to_extern(key) - let value = string_to_extern(value) - set_env_var_ffi(key, value) -} - -///| -fn set_env_var_ffi(key : XExternString, value : XExternString) = "__moonbit_fs_unstable" "set_env_var" - -///| -pub fn exit(code : Int) = "__moonbit_sys_unstable" "exit" - ///| pub fn now() -> UInt64 = "__moonbit_time_unstable" "now" diff --git a/env/sys.mbt b/env/sys.mbt index 9f02a1700..09d66c1fa 100644 --- a/env/sys.mbt +++ b/env/sys.mbt @@ -13,26 +13,13 @@ // limitations under the License. ///| -pub fn get_cli_args() -> Array[String] { +/// Get the command line arguments passed to the program. +pub fn args() -> Array[String] { @ffi.get_cli_args() } ///| -pub fn get_env_vars() -> Map[String, String] { - @ffi.get_env_vars() -} - -///| -pub fn set_env_var(key : String, value : String) -> Unit { - @ffi.set_env_var(key, value) -} - -///| -pub fn exit(code : Int) -> Unit { - @ffi.exit(code) -} - -///| +/// Get the current time in milliseconds since the Unix epoch. pub fn now() -> UInt64 { @ffi.now() } diff --git a/env/sys.mbti b/env/sys.mbti deleted file mode 100644 index 7305b0acf..000000000 --- a/env/sys.mbti +++ /dev/null @@ -1,19 +0,0 @@ -package moonbitlang/core/sys - -// Values -fn exit(Int) -> Unit - -fn get_cli_args() -> Array[String] - -fn get_env_vars() -> Map[String, String] - -fn now() -> UInt64 - -fn set_env_var(String, String) -> Unit - -// Types and methods - -// Type aliases - -// Traits - diff --git a/env/sys_test.mbt b/env/sys_test.mbt deleted file mode 100644 index 9d6126956..000000000 --- a/env/sys_test.mbt +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2025 International Digital Economy Academy -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// 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. - -test "get_env_vars" { - let (k, v) = ("THIS_IS_A_TEST_FOR_ENV_VAR", "IT_WORKS") - let old_env = @env.get_env_vars()[k] - inspect!( - old_env, - content= - #|None - , - ) - @env.set_env_var(k, v) - let res = @env.get_env_vars()[k] - inspect!( - res, - content= - #|Some("IT_WORKS") - , - ) -} From 96d1c11731cff24d58477d687c866388172ecb11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8C=97=E9=9C=B2?= <69190413+illusory0x0@users.noreply.github.com> Date: Thu, 9 Jan 2025 16:57:33 +0800 Subject: [PATCH 17/17] improve eachi for sorted_set --- sorted_set/set.mbt | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/sorted_set/set.mbt b/sorted_set/set.mbt index 6f8b41a83..578047273 100644 --- a/sorted_set/set.mbt +++ b/sorted_set/set.mbt @@ -348,29 +348,21 @@ fn each[V](self : Node[V], f : (V) -> Unit) -> Unit { ///| /// Iterates the set with index. pub fn eachi[V](self : T[V], f : (Int, V) -> Unit) -> Unit { - match self.root { - None => () - Some(root) => root.eachi(f) - } -} - -///| -fn eachi[V](self : Node[V], f : (Int, V) -> Unit) -> Unit { - let s = [] - let mut p = Some(self) let mut i = 0 - while not(p.is_empty()) || not(s.is_empty()) { - while not(p.is_empty()) { - s.push(p) - p = p.unwrap().left - } - if not(s.is_empty()) { - p = s.unsafe_pop() - f(i, p.unwrap().value) - p = p.unwrap().right - i += 1 + fn dfs(root : Node[V]?) -> Unit { + match root { + Some(root) => { + dfs(root.left) + f(i, root.value) + i = i + 1 + dfs(root.right) + } + None => () } + () } + + dfs(self.root) } ///|