From d98af5fa6327dc9d0ba94c3992d259b39b112fcc Mon Sep 17 00:00:00 2001 From: Li Junchen Date: Thu, 7 Mar 2024 19:02:17 +0800 Subject: [PATCH] init moonbitlang/core --- .github/workflows/check.yml | 59 ++++ .gitignore | 2 + CONTRIBUTING.md | 61 ++++ LICENSE | 201 ++++++++++++ NOTICE | 2 + README.md | 12 + array/array.mbt | 181 +++++++++++ array/moon.pkg.json | 5 + assertion/assertion.mbt | 71 +++++ assertion/moon.pkg.json | 1 + bool/bool.mbt | 16 + bool/moon.pkg.json | 1 + bytes/bytes.mbt | 16 + bytes/moon.pkg.json | 5 + char/char.mbt | 16 + char/moon.pkg.json | 1 + double/double.mbt | 16 + double/moon.pkg.json | 1 + int/int.mbt | 16 + int/moon.pkg.json | 1 + int64/int64.mbt | 16 + int64/moon.pkg.json | 1 + list/list.mbt | 604 ++++++++++++++++++++++++++++++++++++ list/moon.pkg.json | 5 + moon.mod.json | 8 + option/moon.pkg.json | 5 + option/option.mbt | 201 ++++++++++++ ref/moon.pkg.json | 5 + ref/ref.mbt | 110 +++++++ result/moon.pkg.json | 5 + result/result.mbt | 16 + string/moon.pkg.json | 1 + string/string.mbt | 16 + unit/moon.pkg.json | 1 + unit/unit.mbt | 16 + 35 files changed, 1694 insertions(+) create mode 100644 .github/workflows/check.yml create mode 100644 .gitignore create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE create mode 100644 NOTICE create mode 100644 README.md create mode 100644 array/array.mbt create mode 100644 array/moon.pkg.json create mode 100644 assertion/assertion.mbt create mode 100644 assertion/moon.pkg.json create mode 100644 bool/bool.mbt create mode 100644 bool/moon.pkg.json create mode 100644 bytes/bytes.mbt create mode 100644 bytes/moon.pkg.json create mode 100644 char/char.mbt create mode 100644 char/moon.pkg.json create mode 100644 double/double.mbt create mode 100644 double/moon.pkg.json create mode 100644 int/int.mbt create mode 100644 int/moon.pkg.json create mode 100644 int64/int64.mbt create mode 100644 int64/moon.pkg.json create mode 100644 list/list.mbt create mode 100644 list/moon.pkg.json create mode 100644 moon.mod.json create mode 100644 option/moon.pkg.json create mode 100644 option/option.mbt create mode 100644 ref/moon.pkg.json create mode 100644 ref/ref.mbt create mode 100644 result/moon.pkg.json create mode 100644 result/result.mbt create mode 100644 string/moon.pkg.json create mode 100644 string/string.mbt create mode 100644 unit/moon.pkg.json create mode 100644 unit/unit.mbt diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml new file mode 100644 index 000000000..5b75aef7c --- /dev/null +++ b/.github/workflows/check.yml @@ -0,0 +1,59 @@ +name: check + +on: + push: + branches: + - main + pull_request: + +jobs: + cla-check: + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + outputs: + cla-signed: ${{ steps.cla-check-step.outputs.cla-signed }} + steps: + - name: CLA check + id: cla-check-step + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + PR_AUTHOR=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \ + https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.number }} \ + | jq -r '.user.login') + echo "The PR author is $PR_AUTHOR" + CLA_RESPONSE=$(curl -s "https://mooncakes.io/api/v0/cla/check?gh_username=$PR_AUTHOR") + echo "CLA check response: $CLA_RESPONSE" + SIGNED=$(echo $CLA_RESPONSE | jq -r '.signed') + echo "cla-signed=$SIGNED" >> $GITHUB_ENV + echo "If you have any questions about the CLA result, please contact us." + if [ "$SIGNED" != "true" ]; then + echo "CLA is not signed." + exit 1 + else + echo "CLA is signed." + fi + + build: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + + - name: install + run: | + /bin/bash -c "$(curl -fsSL https://cli.moonbitlang.com/ubuntu_x86_64_moon_setup.sh)" + echo "/home/runner/.moon/bin" >> $GITHUB_PATH + + - name: moon check + run: moon check + + - name: moon test + run: moon test + + - name: moon bundle + run: moon bundle + + - name: format diff + run: | + moon fmt + git diff diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..b1283a746 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +target/ +.mooncakes/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..cc75c2cb4 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,61 @@ +# Contributing to moonbitlang/core + +## Step 0: Contributor License Agreement + +- Before sending your first change to the moonbitlang/core project, you must have completed one of the following two CLAs, depending on who owns the copyright to your work: + - For the Individual Contributor License Agreement, you can sign it online by visiting . + - For the Corporate Contributor License Agreement, please contact us. + +## Step 1: Clone the repository + +- To start working on the project, you need a local copy of the repository. Currently, `moon` looks for moonbitlang/core at `~/.moon/lib/core`. So, remove it if it exists: + + ```bash + rm -rf ~/.moon/lib/core + ``` + +- Then, use the following command to get the latest version of moonbitlang/core: + + ```bash + git clone https://github.com/moonbitlang/core ~/.moon/lib/core + ``` + +- Run the following command to bundle moonbitlang/core: + + ```bash + moon bundle --source-dir ~/.moon/lib/core + ``` + +- If everything goes well, it will generate a bundled core file at `~/.moon/lib/core/target/bundle/core.core`. + +- Now, you can create a new project and run it to check if everything is working as expected: + + ```bash + moon new hello + cd hello + echo """fn main { + println(@list.reverse(@list.from_array([1, 2, 3]))) + }""" > main/main.mbt + moon run main + # Output: List::Cons(3,List::Cons(2,List::Cons(1,Nil))) + ``` + +## Step 2: Make your change + +Now it's time to make your changes to the codebase. Whether it's fixing a bug, adding a new feature, or improving documentation, your contributions are welcome. Ensure that your changes are clear and understandable to others who will review your code. + +## Step 3: Test your change + +After making your changes, it's important to test them to ensure they work as expected and do not introduce new issues. Run the following commands to test your changes: + + ```bash + moon check + moon test + moon bundle + ``` + +## Step 4: Submit a pull request and request a review + +Simply follow the standard [GitHub flow](https://docs.github.com/en/get-started/using-github/github-flow) to submit your pull request. + +After submitting your pull request, request a review from the project maintainers or other contributors. diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..f49a4e16e --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. \ No newline at end of file diff --git a/NOTICE b/NOTICE new file mode 100644 index 000000000..de2b50163 --- /dev/null +++ b/NOTICE @@ -0,0 +1,2 @@ +This product includes software developed at +International Digital Economy Academy (https://www.idea.edu.cn/). diff --git a/README.md b/README.md new file mode 100644 index 000000000..841136759 --- /dev/null +++ b/README.md @@ -0,0 +1,12 @@ +# moonbitlang/core + +moonbitlang/core is the standard library of the [MoonBit language](moonbitlang.com). It is released alongside the compiler. You can view the documentation for the latest official release at . This repository serves as the development repository. + +## Current status + +It is experimental and under active development. The API is subject to change. + +## Contributing +We are actively developing moonbitlang/core and appreciate your help! + +To contribute, please read the contribution guidelines at [CONTRIBUTING.md](./CONTRIBUTING.md). \ No newline at end of file diff --git a/array/array.mbt b/array/array.mbt new file mode 100644 index 000000000..63c8bba48 --- /dev/null +++ b/array/array.mbt @@ -0,0 +1,181 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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. + +/// Iterates over each element. +/// +/// # Arguments +/// +/// - `self`: The array to iterate over. +/// - `f`: The function to apply to each element. +/// +/// # Example +/// +/// ``` +/// [1, 2, 3, 4, 5].iter(fn(x){ print("\(x) ") }) //output: 1 2 3 4 5 +/// ``` +pub fn iter[T](self : Array[T], f : (T) -> Unit) { + let mut i = 0 + while i < self.length() { + f(self[i]) + i = i + 1 + } +} + +test "iter" { + let mut i = 0 + let mut failed = false + [1, 2, 3, 4, 5].iter( + fn(elem) { + if elem != i + 1 { failed = true } + i = i + 1 + } + ) + if failed { return Err("iter test failed") } +} + +/// Iterates over the array with index. +/// +/// # Arguments +/// +/// - `self`: The array to iterate over. +/// - `f`: A function that takes an `Int` representing the index and a `T` representing the element of the array, and returns `Unit`. +/// +/// # Example +/// +/// ``` +/// [1, 2, 3, 4, 5].iteri(fn(index, elem){ +/// print("(\(index),\(elem)) ") +/// }) //output: (0,1) (1,2) (2,3) (3,4) (4,5) +/// ``` +pub fn iteri[T](self : Array[T], f : (Int, T) -> Unit) { + let mut i = 0 + while i < self.length() { + f(i, self[i]) + i = i + 1 + } +} + +test "iteri" { + let mut i = 0 + let mut failed = false + [1, 2, 3, 4, 5].iteri( + fn(index, elem) { + if index != i || elem != i + 1 { failed = true } + i = i + 1 + }, + ) + if failed { return Err("iteri test failed") } +} + +/// Applies a function to each element of the array and returns a new array with the results. +/// +/// # Example +/// +/// ``` +/// let arr = [1, 2, 3, 4, 5] +/// let doubled = arr.map(fn(x){ x * 2 }) +/// debug(doubled) //output: [2, 4, 6, 8, 10] +/// ``` +pub fn map[T, U](self : Array[T], f : (T) -> U) -> Array[U] { + let res = Array::make(self.length(), f(self[0])) + let mut i = 0 + while i < self.length() { + res[i] = f(self[i]) + i = i + 1 + } + res +} + +test "map" { + let arr = [1, 2, 3, 4, 5] + let doubled = arr.map(fn(x) { x * 2 }) + @assertion.assert_eq(doubled, [2, 4, 6, 8, 10])? +} + +pub fn map_with_index[T, U](self : Array[T], f : (T, Int) -> U) -> Array[U] { + let res = Array::make(self.length(), f(self[0], 0)) + loop 1 { + i => if i < self.length() { + res[i] = f(self[i], i) + continue i + 1 + } + } + res +} + +test "map with index" { + let arr = [1, 2, 3, 4, 5] + let doubled = arr.map_with_index(fn(x, i) { x * 2 + i }) + @assertion.assert_eq(doubled, [2, 5, 8, 11, 14])? +} + +pub fn op_equal[T : Eq](self : Array[T], that : Array[T]) -> Bool { + if self.length() != that.length() { + return false + } + let mut i = 0 + while i < self.length() { + if self[i] != that[i] { + return false + } + i = i + 1 + } + true +} + +/// Create a new array. Values are lazily built. +pub fn new[T](length : Int, value : () -> T) -> Array[T] { + if length <= 0 { + Array::default() + } else { + let array = Array::make(length, value()) + loop 1 { + i => if i < length { + array[i] = value() + continue i + 1 + } + } + array + } +} + +test "new" { + let arr = new(2, fn() { { val: 1 } }) + @assertion.assert_false(arr[0] === arr[1])? +} + +/// Create a new array. Values are built from indexes. +pub fn new_with_index[T](length : Int, value : (Int) -> T) -> Array[T] { + if length <= 0 { + Array::default() + } else { + let array = Array::make(length, value(0)) + loop 1 { + i => if i < length { + array[i] = value(i) + continue i + 1 + } + } + array + } +} + +test "new index" { + let arr = new_with_index(2, fn { i => i }) + @assertion.assert_eq(arr[0], 0)? + @assertion.assert_eq(arr[1], 1)? +} diff --git a/array/moon.pkg.json b/array/moon.pkg.json new file mode 100644 index 000000000..461766b4f --- /dev/null +++ b/array/moon.pkg.json @@ -0,0 +1,5 @@ +{ + "import": [ + { "path": "moonbitlang/core/assertion", "alias": "assertion" } + ] +} \ No newline at end of file diff --git a/assertion/assertion.mbt b/assertion/assertion.mbt new file mode 100644 index 000000000..b370f0210 --- /dev/null +++ b/assertion/assertion.mbt @@ -0,0 +1,71 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 debug_string[T : Debug](t : T) -> String { + let buf = Buffer::make(50) + t.debug_write(buf) + buf.to_string() +} + +pub fn assert_eq[T : Debug + Eq](a : T, b : T) -> Result[Unit,String] { + if a == b { + Ok(()) + } else { + let a = debug_string(a) + let b = debug_string(b) + Err("assertion failed for `\(a) == \(b)`") + } +} + +pub fn assert_ne[T : Debug + Eq](a : T, b : T) -> Result[Unit,String] { + if a != b { + Ok(()) + } else { + let a = debug_string(a) + let b = debug_string(b) + Err("assertion failed for `\(a) == \(b)`") + } +} + +pub fn assert_false(x : Bool) -> Result[Unit,String] { + if x == false { + Ok(()) + } else { + Err("assert_false failed") + } +} + +pub fn assert_true(x : Bool) -> Result[Unit,String] { + if x { + Ok(()) + } else { + Err("assert_true failed") + } +} + +test "assert_true.true" { + assert_true(true)? +} + +test "assert_false.false" { + assert_false(false)? +} + +test "assert_eq.eq" { + assert_eq(1, 1)? + assert_eq("123","123")? +} diff --git a/assertion/moon.pkg.json b/assertion/moon.pkg.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/assertion/moon.pkg.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/bool/bool.mbt b/bool/bool.mbt new file mode 100644 index 000000000..b248758bc --- /dev/null +++ b/bool/bool.mbt @@ -0,0 +1,16 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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. diff --git a/bool/moon.pkg.json b/bool/moon.pkg.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/bool/moon.pkg.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/bytes/bytes.mbt b/bytes/bytes.mbt new file mode 100644 index 000000000..b248758bc --- /dev/null +++ b/bytes/bytes.mbt @@ -0,0 +1,16 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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. diff --git a/bytes/moon.pkg.json b/bytes/moon.pkg.json new file mode 100644 index 000000000..461766b4f --- /dev/null +++ b/bytes/moon.pkg.json @@ -0,0 +1,5 @@ +{ + "import": [ + { "path": "moonbitlang/core/assertion", "alias": "assertion" } + ] +} \ No newline at end of file diff --git a/char/char.mbt b/char/char.mbt new file mode 100644 index 000000000..b248758bc --- /dev/null +++ b/char/char.mbt @@ -0,0 +1,16 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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. diff --git a/char/moon.pkg.json b/char/moon.pkg.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/char/moon.pkg.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/double/double.mbt b/double/double.mbt new file mode 100644 index 000000000..b248758bc --- /dev/null +++ b/double/double.mbt @@ -0,0 +1,16 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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. diff --git a/double/moon.pkg.json b/double/moon.pkg.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/double/moon.pkg.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/int/int.mbt b/int/int.mbt new file mode 100644 index 000000000..b248758bc --- /dev/null +++ b/int/int.mbt @@ -0,0 +1,16 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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. diff --git a/int/moon.pkg.json b/int/moon.pkg.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/int/moon.pkg.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/int64/int64.mbt b/int64/int64.mbt new file mode 100644 index 000000000..b248758bc --- /dev/null +++ b/int64/int64.mbt @@ -0,0 +1,16 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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. diff --git a/int64/moon.pkg.json b/int64/moon.pkg.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/int64/moon.pkg.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/list/list.mbt b/list/list.mbt new file mode 100644 index 000000000..4cd341246 --- /dev/null +++ b/list/list.mbt @@ -0,0 +1,604 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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. + +/// Convert array to list. +/// +/// # Example +/// +/// ``` +/// let ls = from_array([1, 2, 3, 4, 5]) +/// println(ls) // output: from_array([1, 2, 3, 4, 5]) +/// ``` +pub fn from_array[T](arr : Array[T]) -> List[T] { + let mut list = List::Nil + for i = arr.length() - 1; i >= 0; i = i - 1 { + list = List::Cons(arr[i], list) + } + return list +} + +test "from_array" { + let ls = from_array([1, 2, 3, 4, 5]) + @assertion.assert_eq(ls, Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Nil))))))? +} + +/// Get the length of the list. +pub fn length[T](self : List[T]) -> Int { + match self { + Nil => 0 + Cons(_, tail) => 1 + length(tail) + } +} + +test "length" { + let ls = from_array([1, 2, 3, 4, 5]) + @assertion.assert_eq(ls.length(), 5)? +} + +/// Iterates over the list. +/// +/// # Example +/// +/// ``` +/// from_array([1, 2, 3, 4, 5]).iter(print) // output: 12345 +/// ``` +pub fn iter[T](self : List[T], f : (T) -> Unit) { + match self { + Nil => () + Cons(head, tail) => { + f(head) + iter(tail, f) + } + } +} + +test "iter" { + let ls = from_array([1, 2, 3, 4, 5]) + let mut i = 0 + let mut failed = false + ls.iter(fn(x) { i = i + 1; if x != i { failed = true }}) + if failed { return Err("iter test failed") } +} + +/// Iterates over the list with index. +/// +/// # Example +/// +/// ``` +/// from_array([1, 2, 3, 4, 5]).iteri(fn(i, x) { print("(\(i),\(x)) ") }) +/// // output: (0,1) (1,2) (2,3) (3,4) (4,5) +/// ``` +pub fn iteri[T](self : List[T], f : (Int, T) -> Unit) { + let mut i = 0 + self.iter(fn(x) { f(i, x); i = i + 1 }) +} + +test "iteri" { + let mut v = 0 + let mut failed = false + let ls = from_array([1, 2, 3, 4, 5]) + ls.iteri( + fn(i, x) { + if (x != v + 1 || i != v) { failed = true } + v = v + 1 + } + ) + if failed { return Err("iteri test failed") } +} + +/// Maps the list. +/// +/// # Example +/// +/// ``` +/// debug(from_array([1, 2, 3, 4, 5]).map(fn(x){ x * 2})) +/// // output: from_array([2, 4, 6, 8, 10]) +/// ``` +pub fn map[T, U](self : List[T], f : (T) -> U) -> List[U] { + match self { + Nil => Nil + Cons(head, tail) => Cons(f(head), map(tail, f)) + } +} + +test "map" { + let ls = from_array([1, 2, 3, 4, 5]) + let rs = from_array([2, 4, 6, 8, 10]) + @assertion.assert_eq(ls.map(fn(x) { x * 2 }), rs)? +} + +/// Convert list to array. +pub fn to_array[T : Default](self : List[T]) -> Array[T] { + let arr = Array::make(self.length(), T::default()) + self.iteri(fn(i, x) { arr[i] = x }) + return arr +} + +/// Filter the list. +/// +/// # Example +/// +/// ``` +/// debug(from_array([1, 2, 3, 4, 5]).filter(fn(x){ x % 2 == 0})) +/// // output: from_array([2, 4]) +/// ``` +pub fn filter[T](self : List[T], f : (T) -> Bool) -> List[T] { + match self { + Nil => Nil + Cons(head, tail) => + if f(head) { + Cons(head, filter(tail, f)) + } else { + filter(tail, f) + } + } +} + +test "filter" { + let ls = from_array([1, 2, 3, 4, 5]) + let rs = from_array([2, 4]) + @assertion.assert_eq(ls.filter(fn(x) { x % 2 == 0 }), rs)? +} + +/// Tail of the list. +/// +/// # Example +/// +/// ``` +/// debug(from_array([1, 2, 3, 4, 5]).tail()) +/// // output: from_array([2, 3, 4, 5]) +/// ``` +pub fn tail[T](self : List[T]) -> List[T] { + match self { + Nil => Nil + Cons(_, tail) => tail + } +} + +test "tail" { + let ls = from_array([1, 2, 3, 4, 5]) + let rs = from_array([2, 3, 4, 5]) + @assertion.assert_eq(ls.tail(), rs)? +} + +/// Get first element of the list. +/// +/// # Example +/// +/// ``` +/// debug(from_array([1, 2, 3, 4, 5]).head()) +/// // output: 1 +/// ``` +pub fn head[T](self : List[T]) -> T { + match self { + Nil => abort("head of empty list") + Cons(head, _) => head + } +} + +test "head" { + let ls = from_array([1, 2, 3, 4, 5]) + @assertion.assert_eq(ls.head(), 1)? +} + +/// Get first element of the list. +/// +/// # Example +/// +/// ``` +/// debug(from_array([1, 2, 3, 4, 5]).head()) +/// // output: Some(1) +/// debug(from_array([]).head()) +/// // output: None +/// ``` +pub fn head_option[T](self : List[T]) -> Option[T] { + match self { + Nil => None + Cons(head, _) => Some(head) + } +} + +test "head option" { + let ls = from_array([1, 2, 3, 4, 5]) + @assertion.assert_eq(ls.head_option(), Some(1))? + let ls2 : List[Unit] = from_array([]) + @assertion.assert_eq(ls2, Nil)? +} + +/// Last element of the list. +/// +/// # Example +/// +/// ``` +/// debug(from_array([1, 2, 3, 4, 5]).last()) +/// // output: 5 +/// ``` +pub fn last[T](self : List[T]) -> T { + match self { + Nil => abort("last of empty list") + Cons(head, Nil) => head + Cons(_, tail) => last(tail) + } +} + +test "last" { + let ls = from_array([1, 2, 3, 4, 5]) + @assertion.assert_eq(ls.last(), 5)? +} + +/// Init of the list. +/// +/// # Example +/// +/// ``` +/// debug(from_array([1, 2, 3, 4, 5]).init_()) +/// // output: from_array([1, 2, 3, 4]) +/// ``` +pub fn init_[T](self : List[T]) -> List[T] { + match self { + Nil => Nil + Cons(_, Nil) => Nil + Cons(head, tail) => Cons(head, init_(tail)) + } +} + +test "init_" { + let ls = from_array([1, 2, 3, 4, 5]) + let rs = from_array([1, 2, 3, 4]) + @assertion.assert_eq(ls.init_(), rs)? +} + +/// Concatenate two lists. +/// +/// # Example +/// +/// ``` +/// let ls = from_array([1, 2, 3, 4, 5]).concat(from_array([6, 7, 8, 9, 10])) +/// debug(ls) // output: from_array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) +/// ``` +pub fn concat[T](self : List[T], other : List[T]) -> List[T] { + match self { + Nil => other + Cons(head, tail) => Cons(head, concat(tail, other)) + } +} + +test "concat" { + let ls = from_array([1, 2, 3, 4, 5]) + let rs = from_array([6, 7, 8, 9, 10]) + let ts = from_array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) + @assertion.assert_eq(ls.concat(rs), ts)? +} + +/// Reverse the list. +/// +/// # Example +/// +/// ``` +/// debug(from_array([1, 2, 3, 4, 5]).reverse()) +/// // output: from_array([5, 4, 3, 2, 1]) +/// ``` +pub fn reverse[T](self : List[T]) -> List[T] { + match self { + Nil => Nil + Cons(head, tail) => concat(reverse(tail), Cons(head, Nil)) + } +} + +test "reverse" { + let ls = from_array([1, 2, 3, 4, 5]) + @assertion.assert_eq(ls, ls.reverse().reverse())? +} + +/// Fold the list. +/// +/// # Example +/// +/// ``` +/// let r = from_array([1, 2, 3, 4, 5]).fold(0, fn(acc, x) { acc + x }) +/// debug(r) // output: 15 +/// ``` +pub fn fold[T, U](self : List[T], initial : U, f : (U, T) -> U) -> U { + match self { + Nil => initial + Cons(head, tail) => fold(tail, f(initial, head), f) + } +} + +test "fold" { + let ls = from_array([1, 2, 3, 4, 5]) + @assertion.assert_eq(ls.fold(0, fn(acc, x) { acc + x }), 15)? +} + +/// Zip two lists. +/// +/// # Example +/// +/// ``` +/// let r = zip(from_array([1, 2, 3, 4, 5]), from_array([6, 7, 8, 9, 10])) +/// debug(r) // output: from_array([(1, 6), (2, 7), (3, 8), (4, 9), (5, 10)] +/// ``` +/// +/// # Panics +/// +/// If the two lists have different lengths, the function will panic. +/// +pub fn zip[T, U](self : List[T], other : List[U]) -> List[(T, U)] { + match (self, other) { + (Nil, Nil) => Nil + (Cons(x, xs), Cons(y, ys)) => Cons((x, y), zip(xs, ys)) + _ => abort("zip: lists have different lengths") + } +} + +/// map over the list and concat all results. +/// +/// `concat_map(f, ls)` equal to `ls.map(f).fold(Nil, fn(acc, x) { acc.concat(x) })))` +/// +/// # Example +/// +/// ``` +/// let ls = from_array([1, 2, 3]) +/// let r = ls.concat_map(fn(x) { [x, x * 2] }) +/// debug(r) // output: from_array([1, 2, 2, 4, 3, 6]) +/// ``` +pub fn concat_map[T, U](self : List[T], f : (T) -> List[U]) -> List[U] { + match self { + Nil => Nil + Cons(head, tail) => concat(f(head), concat_map(tail, f)) + } +} + +test "concat_map" { + let ls = from_array([1, 2, 3]) + let rs = from_array([1, 2, 2, 4, 3, 6]) + @assertion.assert_eq(ls.concat_map(fn(x) { from_array([x, x * 2]) }), rs)? +} + +/// Get nth element of the list +pub fn nth[T](self : List[T], n : Int) -> T { + match self { + Nil => abort("nth: index out of bounds") + Cons(head, tail) => if n == 0 { head } else { nth(tail, n - 1) } + } +} + +test "nth" { + let ls = from_array([1, 2, 3, 4, 5]) + @assertion.assert_eq(ls.nth(0), 1)? + @assertion.assert_eq(ls.nth(1), 2)? + @assertion.assert_eq(ls.nth(2), 3)? + @assertion.assert_eq(ls.nth(3), 4)? + @assertion.assert_eq(ls.nth(4), 5)? +} + +/// Create a list of length n with the given value +/// +/// # Example +/// +/// ``` +/// debug(repeat(5, 1)) // output: from_array([1, 1, 1, 1, 1]) +/// ``` +pub fn repeat[T](n : Int, x : T) -> List[T] { + if n == 0 { + Nil + } else { + Cons(x, repeat(n - 1, x)) + } +} + +test "repeat" { + let ls = from_array([1, 1, 1, 1, 1]) + @assertion.assert_eq(repeat(5, 1), ls)? +} + +/// Insert separator to the list. +/// +/// # Example +/// +/// ``` +/// let ls = from_array(["1", "2", "3", "4", "5"]).separate_by("|") +/// debug(ls) // output: from_array(["1", "|", "2", "|", "3", "|", "4", "|", "5"]) +/// ``` +pub fn separate_by[T](self : List[T], seperator : T) -> List[T] { + match self { + Nil => Nil + Cons(head, Nil) => Cons(head, Nil) + Cons(head, tail) => + Cons(head, Cons(seperator, separate_by(tail, seperator))) + } +} + +test "separate_by" { + let ls = from_array(["1", "|", "2", "|", "3", "|", "4", "|", "5"]) + @assertion.assert_eq(from_array(["1", "2", "3", "4", "5"]).separate_by("|"), ls)? +} + +/// Check if the list is empty. +pub fn is_empty[T](self : List[T]) -> Bool { + match self { + Nil => true + _ => false + } +} + +test "is_empty" { + let ls = from_array([1, 2, 3, 4, 5]) + @assertion.assert_false(ls.is_empty())? + @assertion.assert_true((List::Nil : List[Unit]).is_empty())? +} + +/// Unzip two lists. +/// +/// # Example +/// +/// ``` +/// let (a,b) = unzip(from_array([(1,2),(3,4),(5,6)])) +/// debug(a) // output: from_array([1, 3, 5]) +/// debug(b) // output: from_array([2, 4, 6]) +/// ``` +pub fn unzip[T, U](list : List[(T, U)]) -> (List[T], List[U]) { + match list { + Nil => (Nil, Nil) + Cons((x, y), xs) => { + let (xs, ys) = unzip(xs) + (Cons(x, xs), Cons(y, ys)) + } + } +} + +test "unzip" { + let ls = from_array([(1, 2), (3, 4), (5, 6)]) + let (a, b) = unzip(ls) + @assertion.assert_eq(a, from_array([1, 3, 5]))? + @assertion.assert_eq(b, from_array([2, 4, 6]))? +} + +/// flatten a list of lists. +/// +/// # Example +/// +/// ``` +/// let ls = flatten(from_array([from_array([1,2,3]), from_array([4,5,6]), from_array([7,8,9])])) +/// debug(ls) // output: from_array([1, 2, 3, 4, 5, 6, 7, 8, 9]) +/// ``` +pub fn flatten[T](list : List[List[T]]) -> List[T] { + match list { + Nil => Nil + Cons(head, tail) => concat(head, flatten(tail)) + } +} + +test "flatten" { + let ls = from_array( + [from_array([1, 2, 3]), from_array([4, 5, 6]), from_array([7, 8, 9])], + ) + let rs = from_array([1, 2, 3, 4, 5, 6, 7, 8, 9]) + @assertion.assert_eq(flatten(ls), rs)? +} + +/// Get maximum element of the list. +pub fn maximum[T : Compare](self : List[T]) -> T { + match self { + Nil => abort("maximum: empty list") + Cons(x, Nil) => x + Cons(x, xs) => { + let y = maximum(xs) + if x > y { + x + } else { + y + } + } + } +} + +test "maximum" { + let ls = from_array([1, 123, 52, 3, 6, 0, -6, -76]) + @assertion.assert_eq(ls.maximum(), 123)? +} + +/// Get minimum element of the list. +pub fn minimum[T : Compare](self : List[T]) -> T { + match self { + Nil => abort("minimum: empty list") + Cons(x, Nil) => x + Cons(x, xs) => { + let y = minimum(xs) + if x < y { + x + } else { + y + } + } + } +} + +test "minimum" { + let ls = from_array([1, 123, 52, 3, 6, 0, -6, -76]) + @assertion.assert_eq(ls.minimum(), -76)? +} + +/// Sort the list in ascending order. +/// +/// # Example +/// +/// ``` +/// let ls = sort(from_array([1,123,52,3,6,0,-6,-76])) +/// debug(ls) // output: from_array([-76, -6, 0, 1, 3, 6, 52, 123]) +/// ``` +pub fn sort[T : Compare](self : List[T]) -> List[T] { + match self { + Nil => Nil + Cons(x, xs) => { + let smaller = filter(xs, fn(y) { y < x }) + let greater = filter(xs, fn(y) { y >= x }) + concat(sort(smaller), Cons(x, sort(greater))) + } + } +} + +test "sort" { + let ls = from_array([1, 123, 52, 3, 6, 0, -6, -76]) + let rs = from_array([-76, -6, 0, 1, 3, 6, 52, 123]) + @assertion.assert_eq(ls.sort(), rs)? +} + +/// Concatenate two lists. +/// +/// `a + b` equal to `a.concat(b)` +pub fn op_add[T](self : List[T], other : List[T]) -> List[T] { + self.concat(other) +} + +/// Check if the list contains the value. +pub fn contain[T : Eq](self : List[T], value : T) -> Bool { + match self { + Nil => false + Cons(x, xs) => if x == value { true } else { contain(xs, value) } + } +} + +test "contain" { + let ls = from_array([1, 2, 3]) + @assertion.assert_true(ls.contain(1))? + @assertion.assert_true(ls.contain(2))? + @assertion.assert_true(ls.contain(3))? + @assertion.assert_false(ls.contain(0))? + @assertion.assert_false(ls.contain(4))? +} + +/// Produces a collection iteratively. +/// +/// # Example +/// +/// ``` +/// // from_array([0, 1, 2]) +/// unfold(0, fn { i => if i == 3 { None } else { Some(i, i + 1) } }) |> debug +/// ``` +pub fn unfold[T, State](init : State, f : (State) -> Option[(T, State)]) -> + List[T] { + match f(init) { + Some(element, new_state) => Cons(element, unfold(new_state, f)) + None => Nil + } +} + +test "unfold" { + let ls = unfold(0, fn { i => if i == 3 { None } else { Some(i, i + 1) } }) + @assertion.assert_eq(ls, Cons(0, Cons(1, Cons(2, Nil))))? +} diff --git a/list/moon.pkg.json b/list/moon.pkg.json new file mode 100644 index 000000000..461766b4f --- /dev/null +++ b/list/moon.pkg.json @@ -0,0 +1,5 @@ +{ + "import": [ + { "path": "moonbitlang/core/assertion", "alias": "assertion" } + ] +} \ No newline at end of file diff --git a/moon.mod.json b/moon.mod.json new file mode 100644 index 000000000..c65ef75fd --- /dev/null +++ b/moon.mod.json @@ -0,0 +1,8 @@ +{ + "name": "moonbitlang/core", + "version": "0.1.0", + "readme": "README.md", + "repository": "https://github.com/moonbitlang/core", + "license": "Apache-2.0", + "keywords": ["core","standard library"] +} \ No newline at end of file diff --git a/option/moon.pkg.json b/option/moon.pkg.json new file mode 100644 index 000000000..461766b4f --- /dev/null +++ b/option/moon.pkg.json @@ -0,0 +1,5 @@ +{ + "import": [ + { "path": "moonbitlang/core/assertion", "alias": "assertion" } + ] +} \ No newline at end of file diff --git a/option/option.mbt b/option/option.mbt new file mode 100644 index 000000000..1529ad97f --- /dev/null +++ b/option/option.mbt @@ -0,0 +1,201 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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. + +/// Creates an `Option` containing a value if the given condition is true, otherwise returns `None`. +/// +/// # Arguments +/// +/// * `condition`: A boolean value indicating whether the option should contain a value. +/// * `value`: A function that returns the value to be contained in the option. +/// +/// # Returns +/// +/// An `Option` containing the value if the condition is true, otherwise `None`. +/// +/// # Example +/// +/// ``` +/// let result = when(true, fn(){ "Hello, World!" }) +/// debug(result) //output: Some("Hello, World!" +/// ``` +pub fn when[T](condition : Bool, value : () -> T) -> Option[T] { + if condition { + Some(value()) + } else { + None + } +} + +/// The `unless` function returns an `Option` value based on a condition. +/// +/// `unless(condition, value)` is equivalent to `when(not(condition), value)`. +/// +/// # Arguments +/// +/// * `condition`: A boolean value indicating whether the condition is true or false. +/// * `value`: A function that returns a value of type `T`. +/// +/// # Returns +/// +/// An `Option` value that is `Some(value())` if the condition is false, otherwise `None`. +/// +pub fn unless[T](condition : Bool, value : () -> T) -> Option[T] { + when(condition.not(), value) +} + + +/// Creates an empty `Option` of type `T`. +pub fn empty[T]() -> Option[T] { + None +} + +/// Creates an `Option` that contains a value. +pub fn some[T](value : T) -> Option[T] { + Some(value) +} + +/// Maps the value of an `Option` using a provided function. +/// +/// # Example +/// +/// ``` +/// let a = Some(5) +/// debug(a.map(fn(x){ x * 2 })) //output: Some(10) +/// +/// let b = None +/// debug(b.map(fn(x){ x * 2 })) //output: None +/// ``` +pub fn map[T, U](self : Option[T], f : (T) -> U) -> Option[U] { + Some(f(self?)) +} + +/// Binds an option to a function that returns another option. +/// +/// # Example +/// +/// ``` +/// let a = Option::Some(5) +/// let r1 = a.bind(fn(x){ Some(x * 2) }) +/// debug(r1) //output: Some(10) +/// +/// let b : Option[Int] = None +/// let r2 = b.bind(fn(x){ Some(x * 2) }) +/// debug(r2) //output: None +/// ``` +pub fn bind[T, U](self : Option[T], f : (T) -> Option[U]) -> Option[U] { + f(self?) +} + +test "bind" { + let a = Option::Some(5) + let b : Option[Int] = None + @assertion.assert_eq(a.bind(fn(x){ Some(x * 2) }), Some(10))? + @assertion.assert_eq(b.bind(fn(x){ Some(x * 2) }), None)? +} + +/// Flattens an `Option` of `Option` into a single `Option`. +/// +/// If the input `Option` is `Some(Some(value))`, the function returns `Some(value)`. +/// +/// # Example +/// +/// ``` +/// let a = Some(Some(42)); +/// debug(flatten(a)) //output: Some(42) +/// let b = Some(None) +/// debug(flatten(b)) //output: None +/// ``` +pub fn flatten[T](self : Option[Option[T]]) -> Option[T] { + self? +} + +test "flatten" { + let a : Option[Option[Int]] = Some(Some(42)); + @assertion.assert_eq(flatten(a),Some(42))? + let b : Option[Option[Int]] = Some(None) + @assertion.assert_eq(flatten(b),None)? +} + +/// Checks if the option is empty. +pub fn is_empty[T](self : Option[T]) -> Bool { + match self { + None => true + _ => false + } +} + +test "is_empty" { + let x = Option::Some(3) + let y: Option[Int] = None + @assertion.assert_false(x.is_empty())? + @assertion.assert_true(y.is_empty())? +} + +/// Filters the option by applying the given predicate function `f`. +/// +/// If the predicate function `f` returns `true` for the value contained in the option, +/// the same option is returned. Otherwise, `None` is returned. +/// +/// # Example +/// ``` +/// let x = Some(3) +/// debug(x.filter(fn(x){ x > 5 })) //output: None +/// debug(x.filter(fn(x){ x < 5 })) //output: Some(3) +/// ``` +pub fn filter[T](self : Option[T], f : (T) -> Bool) -> Option[T] { + if f(self?) { + self + } else { + None + } +} + +test "filter" { + let x = Option::Some(3) + @assertion.assert_eq(x.filter(fn(x){ x > 5 }),None)? + @assertion.assert_eq(x.filter(fn(x){ x < 5 }),Some(3))? +} + +/// Return the contained `Some` value or the provided default. +pub fn or[T](self : Option[T], default : T) -> T { + match self { + None => default + Some(t) => t + } +} + +test "or" { + let x = Option::Some(3) + @assertion.assert_eq(x.or(5), 3)? + @assertion.assert_eq((None : Option[Int]).or(5), 5)? +} + +/// Return the contained `Some` value or the provided default. +/// +/// Default is lazily evaluated +pub fn or_else[T](self : Option[T], default : () -> T) -> T { + match self { + None => default() + Some(t) => t + } +} + +test "or else" { + let x = Option::Some(3) + @assertion.assert_eq(x.or_else(fn () { 5 }), 3)? + @assertion.assert_eq((None : Option[Int]).or_else(fn () { 5 }), 5)? +} \ No newline at end of file diff --git a/ref/moon.pkg.json b/ref/moon.pkg.json new file mode 100644 index 000000000..461766b4f --- /dev/null +++ b/ref/moon.pkg.json @@ -0,0 +1,5 @@ +{ + "import": [ + { "path": "moonbitlang/core/assertion", "alias": "assertion" } + ] +} \ No newline at end of file diff --git a/ref/ref.mbt b/ref/ref.mbt new file mode 100644 index 000000000..34d0b547a --- /dev/null +++ b/ref/ref.mbt @@ -0,0 +1,110 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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. + +/// create a reference from value +pub fn ref[T](x : T) -> Ref[T] { + { val: x } +} + + +/// Maps the value of a `Ref` using a given function. +/// +/// # Example +/// +/// ``` +/// debug(ref(1).map(fn(a){ a + 1 })) //output: ref(2) +/// ``` +pub fn map[T,R](self : Ref[T], f : (T) -> R) -> Ref[R] { + { val: f(self.val) } +} + +test "map" { + let x = ref(1) + let y = map(x, fn(a){ a + 1 }) + @assertion.assert_eq(y.val, 2)? +} + + +/// This function allows you to temporarily replace the value of a reference with a new value, +/// execute a given function, and then restore the original value of the reference. +/// +/// # Arguments +/// +/// - `self`: The reference whose value will be temporarily replaced. +/// - `a`: The new value to assign to the reference. +/// - `f`: The function to execute while the reference value is replaced. +/// +/// # Returns +/// +/// The result of executing the provided function `f`. +/// +/// # Example +/// +/// ``` +/// let x = ref(1) +/// x.protext(2, fn(){ +/// debug(x) //output: ref(2) +/// x.val = 3 +/// debug(x) //output: ref(3) +/// }) +/// debug(x) //output: ref(1) +/// ``` +pub fn protect[T,R](self : Ref[T], a : T, f : () -> R) -> R { + let old = self.val + self.val = a + let r = f() + self.val = old + r +} + +test "protect" { + let x = ref(1) + @assertion.assert_eq(x.val, 1)? + protect(x, 2, fn(){ + @assertion.assert_eq(x.val, 2) + })? + @assertion.assert_eq(x.val, 1)? +} + +/// Swaps the values of two references. +/// +/// # Example +/// +/// ``` +/// let x = ref(1) +/// let y = ref(2) +/// swap(x, y) +/// debug(x) //output: ref(2) +/// debug(y) //output: ref(1) +/// ``` +pub fn swap[T](self : Ref[T], that : Ref[T]) { + let tmp = self.val + self.val = that.val + that.val = tmp +} + +test "swap" { + let x = ref(1) + let y = ref(2) + swap(x, y) + @assertion.assert_eq(x.val, 2)? + @assertion.assert_eq(y.val, 1)? +} + +pub fn update[T](self : Ref[T], f : (T) -> T) { + self.val = f(self.val) +} \ No newline at end of file diff --git a/result/moon.pkg.json b/result/moon.pkg.json new file mode 100644 index 000000000..461766b4f --- /dev/null +++ b/result/moon.pkg.json @@ -0,0 +1,5 @@ +{ + "import": [ + { "path": "moonbitlang/core/assertion", "alias": "assertion" } + ] +} \ No newline at end of file diff --git a/result/result.mbt b/result/result.mbt new file mode 100644 index 000000000..b248758bc --- /dev/null +++ b/result/result.mbt @@ -0,0 +1,16 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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. diff --git a/string/moon.pkg.json b/string/moon.pkg.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/string/moon.pkg.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/string/string.mbt b/string/string.mbt new file mode 100644 index 000000000..b248758bc --- /dev/null +++ b/string/string.mbt @@ -0,0 +1,16 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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. diff --git a/unit/moon.pkg.json b/unit/moon.pkg.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/unit/moon.pkg.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/unit/unit.mbt b/unit/unit.mbt new file mode 100644 index 000000000..b248758bc --- /dev/null +++ b/unit/unit.mbt @@ -0,0 +1,16 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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.