├── .devcontainer └── devcontainer.json ├── .dockerignore ├── .gitattributes ├── .github └── workflows │ ├── build.yml │ ├── docker.yml │ └── docs.yml ├── .gitignore ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── Cargo.lock ├── Cargo.toml ├── Dockerfile ├── LICENSE.md ├── MISSING.md ├── Makefile ├── README.md ├── codecov.yml ├── common ├── README.md ├── grammar.js ├── minicoro │ └── minicoro.h └── tree-sitter.json ├── docs └── lang_guide.md ├── examples ├── backtrace.alu ├── constants.alu ├── coroutines.alu ├── defer_and_move.alu ├── dyn.alu ├── file_io.alu ├── first_class_functions.alu ├── formatting.alu ├── hello_world.alu ├── iterators.alu ├── lambdas_closures.alu ├── line_numbering.alu ├── macros.alu ├── peano.alu ├── process.alu ├── reflection.alu ├── result.alu ├── statics.alu ├── tcp_client_server.alu ├── threading.alu ├── typeof.alu ├── unit_tests.alu └── variadic.alu ├── libraries ├── README.md ├── aluminac │ ├── README.md │ ├── lib │ │ ├── arena.alu │ │ ├── common.alu │ │ ├── mod.alu │ │ └── node_kinds.alu │ └── mod.alu ├── json │ ├── builder.alu │ ├── marshal.alu │ ├── mod.alu │ ├── parser.alu │ ├── reader.alu │ └── writer.alu └── tree_sitter │ └── mod.alu ├── src ├── alumina-boot-macros │ ├── Cargo.toml │ └── src │ │ └── lib.rs └── alumina-boot │ ├── Cargo.toml │ ├── build.rs │ ├── dump_lang.c │ └── src │ ├── ast │ ├── expressions.rs │ ├── format.rs │ ├── lang.rs │ ├── macros.rs │ ├── maker.rs │ ├── mod.rs │ ├── pretty.rs │ ├── rebind.rs │ ├── serialization.rs │ └── types.rs │ ├── codegen │ ├── elide_zst.rs │ ├── functions.rs │ ├── mod.rs │ └── types.rs │ ├── common.rs │ ├── compiler.rs │ ├── diagnostics.rs │ ├── global_ctx.rs │ ├── ir │ ├── builder.rs │ ├── const_eval.rs │ ├── dce.rs │ ├── fold.rs │ ├── infer.rs │ ├── inline.rs │ ├── layout.rs │ ├── mod.rs │ └── mono │ │ ├── intrinsics.rs │ │ └── mod.rs │ ├── main.rs │ ├── parser.rs │ ├── src │ ├── mod.rs │ ├── pass1.rs │ ├── path.rs │ ├── resolver.rs │ └── scope.rs │ └── visitors.rs ├── sysroot ├── libc │ ├── aarch64_apple_darwin.alu │ ├── aarch64_linux_android.alu │ ├── aarch64_unknown_linux_gnu.alu │ ├── aarch64_unknown_linux_musl.alu │ ├── mod.alu │ ├── x86_64_apple_darwin.alu │ ├── x86_64_unknown_linux_gnu.alu │ └── x86_64_unknown_linux_musl.alu ├── mod.alu ├── std │ ├── builtins.alu │ ├── cmp.alu │ ├── collections │ │ ├── deque.alu │ │ ├── hashmap.alu │ │ ├── hashset.alu │ │ ├── heap.alu │ │ ├── mod.alu │ │ └── vector.alu │ ├── ffi.alu │ ├── fmt │ │ ├── mod.alu │ │ └── ryu │ │ │ ├── common.alu │ │ │ ├── d2s.alu │ │ │ ├── d2s_full_table.alu │ │ │ ├── d2s_intrinsics.alu │ │ │ ├── digit_table.alu │ │ │ ├── f2s.alu │ │ │ ├── f2s_intrinsics.alu │ │ │ ├── mod.alu │ │ │ ├── pretty │ │ │ ├── exponent.alu │ │ │ ├── mantissa.alu │ │ │ └── mod.alu │ │ │ ├── s2d.alu │ │ │ └── s2f.alu │ ├── fs │ │ ├── mod.alu │ │ └── unix.alu │ ├── hash │ │ ├── mod.alu │ │ └── xxhash.alu │ ├── intrinsics.alu │ ├── io │ │ ├── mod.alu │ │ └── unix.alu │ ├── iter.alu │ ├── macros.alu │ ├── math.alu │ ├── mem.alu │ ├── mod.alu │ ├── net │ │ ├── address.alu │ │ ├── mod.alu │ │ └── unix.alu │ ├── option.alu │ ├── panicking.alu │ ├── prelude.alu │ ├── process │ │ ├── mod.alu │ │ └── unix.alu │ ├── random │ │ ├── mod.alu │ │ └── ziggurat.alu │ ├── range.alu │ ├── result.alu │ ├── runtime │ │ ├── backtrace.alu │ │ ├── minicoro.alu │ │ └── mod.alu │ ├── string │ │ ├── mod.alu │ │ └── unicode.alu │ ├── sync │ │ ├── channel.alu │ │ └── mod.alu │ ├── thread │ │ ├── mod.alu │ │ ├── parker │ │ │ ├── futex.alu │ │ │ ├── mod.alu │ │ │ └── pthread.alu │ │ └── pool.alu │ ├── time.alu │ ├── typing.alu │ └── util.alu └── test.alu ├── tests ├── README.md ├── diag │ ├── aligned_and_packed.alu │ ├── assign_to_const.alu │ ├── assign_to_const_2.alu │ ├── assign_to_rvalue.alu │ ├── break_outside_loop.alu │ ├── builtin_protocol_dyn.alu │ ├── builtins_are_special_mkay.alu │ ├── cannot_et_cetera.alu │ ├── cannot_read_file.alu │ ├── cannot_reference_local.alu │ ├── char_literal.alu │ ├── closures_are_not_fns.alu │ ├── common.alu │ ├── const_eval_1.alu │ ├── const_eval_10.alu │ ├── const_eval_11.alu │ ├── const_eval_2.alu │ ├── const_eval_3.alu │ ├── const_eval_4.alu │ ├── const_eval_5.alu │ ├── const_eval_6.alu │ ├── const_eval_7.alu │ ├── const_eval_8.alu │ ├── const_eval_9.alu │ ├── const_panic.alu │ ├── constant_string.alu │ ├── continue_outside_loop.alu │ ├── coroutine_return_type.alu │ ├── cyclic.alu │ ├── default_case_last.alu │ ├── defer_in_defer.alu │ ├── defer_in_non_function_scope.alu │ ├── dollared_identifier.alu │ ├── duplicate.alu │ ├── duplicate_attribute.alu │ ├── duplicate_enum_member.alu │ ├── duplicate_field_init.alu │ ├── et_cetera.alu │ ├── et_cetera_in_et_cetera.alu │ ├── extern_coroutine.alu │ ├── extern_generic.alu │ ├── extern_static_generic.alu │ ├── extern_static_init.alu │ ├── freestanding_impl.alu │ ├── function_or_static_expected_here.alu │ ├── generic_arg_in_path.alu │ ├── generic_count_mismatch.alu │ ├── generic_protocol_fn.alu │ ├── indirect_dyn.alu │ ├── integer_out_of_range.alu │ ├── intrinsics_are_special_mkay.alu │ ├── invalid_attribute.alu │ ├── invalid_bin_op.alu │ ├── invalid_cast.alu │ ├── invalid_enum_variant.alu │ ├── invalid_escape.alu │ ├── invalid_format_string.alu │ ├── invalid_transparent.alu │ ├── invalid_type_op.alu │ ├── invalid_un_op.alu │ ├── ir_inline_early_return.alu │ ├── ir_inline_flow.alu │ ├── ir_inline_vars.alu │ ├── is_a_macro.alu │ ├── lang_item_missing.alu │ ├── literal.alu │ ├── macro_expected.alu │ ├── macros_with_items.alu │ ├── macros_with_lambdas.alu │ ├── method_not_found.alu │ ├── mismatched_branches.alu │ ├── multiple_etceteras.alu │ ├── no_bodied_function.alu │ ├── no_et_ceteras.alu │ ├── non_dynnable_fn.alu │ ├── non_protocol_dyn.alu │ ├── nonlocal_bind.alu │ ├── not_a_method.alu │ ├── not_a_protocol.alu │ ├── not_callable.alu │ ├── not_callable_field_confusion.alu │ ├── not_enough_macro_args.alu │ ├── param_count_mismatch.alu │ ├── protocol_coroutine.alu │ ├── protocol_extern_fn.alu │ ├── protocol_match.alu │ ├── protocol_mismatch.alu │ ├── recursive_macro.alu │ ├── recursive_protocol_bound.alu │ ├── recursive_static_init.alu │ ├── return_in_non_function_scope.alu │ ├── struct_like_expected.alu │ ├── syntax.alu │ ├── too_many_enum_variants.alu │ ├── tuple_call_arg_count.alu │ ├── tuple_call_arg_type.alu │ ├── tuple_index_out_of_bounds.alu │ ├── type_hint_required.alu │ ├── type_mismatch.alu │ ├── typedef_impl.alu │ ├── typedef_with_no_target.alu │ ├── unexpected_generic.alu │ ├── unknown_builtin_macro.alu │ ├── unpopulated_item.alu │ ├── unresolved_item.alu │ ├── user_defined.alu │ ├── var_args.alu │ ├── warnings.alu │ ├── yield_in_defer.alu │ ├── yield_outside_coroutine.alu │ └── zst_pointer_difference.alu └── lang │ └── lang.alu └── tools ├── alumina-doc ├── common.alu ├── error.alu ├── main.alu ├── markdown.alu ├── static │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── apple-touch-icon.png │ ├── browserconfig.xml │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon.ico │ ├── logo.svg │ ├── manifest.json │ ├── mstile-144x144.png │ ├── mstile-150x150.png │ ├── mstile-310x150.png │ ├── mstile-310x310.png │ ├── mstile-70x70.png │ ├── prism-alumina.js │ ├── safari-pinned-tab.svg │ ├── site.js │ └── styles.css ├── visitors.alu └── watch_docs.sh ├── bench.py ├── cloc_language_def.txt ├── diag.py ├── tree-sitter-codegen └── main.alu └── vscode-highlighting ├── .gitignore ├── .vscode └── launch.json ├── .vscodeignore ├── LICENSE.md ├── README.md ├── images └── icon.png ├── language-configuration.json ├── package.json └── syntaxes └── alumina.tmLanguage.json /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Alumina environment", 3 | "context": "..", 4 | "dockerFile": "../Dockerfile", 5 | "build": { 6 | "target": "environment" 7 | }, 8 | "extensions": [ 9 | "rust-lang.rust-analyzer", 10 | "tibordp.alumina" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | 3 | # Build artifacts 4 | /aluminac 5 | /alumina-boot 6 | 7 | # Manual testing 8 | /quick* 9 | 10 | # Generated by Cargo 11 | # will have compiled files and executables 12 | debug/ 13 | target/ 14 | build/ 15 | 16 | # These are backup files generated by rustfmt 17 | **/*.rs.bk 18 | 19 | # MSVC Windows builds of rustc generate these, which store debugging information 20 | *.pdb 21 | 22 | # Output directory 23 | output/ 24 | 25 | # Byte-compiled / optimized / DLL files 26 | __pycache__/ 27 | *.py[cod] 28 | *$py.class 29 | 30 | # C extensions 31 | *.so 32 | 33 | # Distribution / packaging 34 | .Python 35 | build/ 36 | develop-eggs/ 37 | dist/ 38 | downloads/ 39 | eggs/ 40 | .eggs/ 41 | parts/ 42 | sdist/ 43 | var/ 44 | wheels/ 45 | share/python-wheels/ 46 | *.egg-info/ 47 | .installed.cfg 48 | *.egg 49 | MANIFEST 50 | 51 | # PyInstaller 52 | # Usually these files are written by a python script from a template 53 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 54 | *.manifest 55 | *.spec 56 | 57 | # Installer logs 58 | pip-log.txt 59 | pip-delete-this-directory.txt 60 | 61 | # Unit test / coverage reports 62 | htmlcov/ 63 | .tox/ 64 | .nox/ 65 | .coverage 66 | .coverage.* 67 | .cache 68 | nosetests.xml 69 | coverage.xml 70 | *.cover 71 | *.py,cover 72 | .hypothesis/ 73 | .pytest_cache/ 74 | cover/ 75 | 76 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 77 | __pypackages__/ 78 | 79 | 80 | # Environments 81 | .env 82 | .venv 83 | env/ 84 | venv/ 85 | ENV/ 86 | env.bak/ 87 | venv.bak/ 88 | 89 | # mypy 90 | .mypy_cache/ 91 | .dmypy.json 92 | dmypy.json 93 | 94 | # Pyre type checker 95 | .pyre/ 96 | 97 | # pytype static type analyzer 98 | .pytype/ 99 | 100 | # Cython debug symbols 101 | cython_debug/ 102 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.alu linguist-language=Rust 2 | libraries/aluminac/lib/node_kinds.alu linguist-generated 3 | common/minicoro/minicoro.h linguist-vendored 4 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build Alumina 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | 9 | jobs: 10 | lint: 11 | name: Lint rust 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Install Rust 16 | uses: actions-rs/toolchain@v1 17 | with: 18 | profile: minimal 19 | toolchain: stable 20 | override: true 21 | components: rustfmt, clippy 22 | - name: Install Tree-sitter 23 | run: npm install -g tree-sitter-cli 24 | 25 | - name: Lint Rust 26 | timeout-minutes: 10 27 | run: make lint-rust 28 | 29 | tests: 30 | name: Build and test (base only) 31 | runs-on: ${{ matrix.os }} 32 | strategy: 33 | matrix: 34 | os: [ubuntu-latest, macos-latest] 35 | steps: 36 | - uses: actions/checkout@v2 37 | - name: Install Rust 38 | uses: actions-rs/toolchain@v1 39 | with: 40 | profile: minimal 41 | toolchain: stable 42 | override: true 43 | components: rustfmt, clippy 44 | - name: Install Tree-sitter 45 | run: npm install -g tree-sitter-cli 46 | 47 | - name: Install libbacktrace 48 | if: matrix.os == 'macos-latest' 49 | run: | 50 | git clone https://github.com/ianlancetaylor/libbacktrace 51 | cd libbacktrace 52 | ./configure 53 | make -j8 54 | sudo make install 55 | 56 | - name: Run all tests 57 | timeout-minutes: 10 58 | run: make test -j8 59 | 60 | tests-musl: 61 | name: Build and test (alpine) 62 | runs-on: ubuntu-latest 63 | container: 64 | image: alpine:latest 65 | 66 | steps: 67 | - uses: actions/checkout@v2 68 | - name: Set up packages 69 | run: apk add --no-cache gcc musl-dev make nodejs npm curl git 70 | 71 | - name: Install Rust 72 | run: | 73 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y 74 | echo "$HOME/.cargo/bin" >> $GITHUB_PATH 75 | 76 | - name: Install Tree-sitter 77 | run: cargo install tree-sitter-cli 78 | 79 | - name: Install libbacktrace 80 | run: | 81 | git clone https://github.com/ianlancetaylor/libbacktrace 82 | cd libbacktrace 83 | ./configure 84 | make -j8 85 | make install 86 | 87 | - name: Run all tests 88 | timeout-minutes: 10 89 | run: make test -j8 90 | 91 | dist-check: 92 | name: Build and test (full) 93 | runs-on: ubuntu-latest 94 | strategy: 95 | matrix: 96 | cache_ast: [with-ast-cache, without-ast-cache] 97 | steps: 98 | - uses: actions/checkout@v2 99 | - name: Install Rust 100 | uses: actions-rs/toolchain@v1 101 | with: 102 | profile: minimal 103 | toolchain: stable 104 | override: true 105 | components: rustfmt, clippy 106 | 107 | - name: Install Tree-sitter (with runtime) 108 | run: | 109 | npm install -g tree-sitter-cli 110 | curl -fsSL https://github.com/tree-sitter/tree-sitter/archive/refs/tags/v0.25.4.tar.gz | tar -xz 111 | cd tree-sitter-* 112 | make 113 | sudo make install 114 | sudo ldconfig 115 | 116 | - name: Build full 117 | run: make dist-check -j8 118 | if: ${{ matrix.cache_ast == 'without-ast-cache' }} 119 | 120 | - name: Build full (with AST cache) 121 | run: make dist-check -j8 122 | if: ${{ matrix.cache_ast == 'with-ast-cache' }} 123 | env: 124 | CACHE_AST: 1 125 | 126 | code-coverage: 127 | name: Collect code coverage 128 | runs-on: ubuntu-latest 129 | steps: 130 | - uses: actions/checkout@v2 131 | - name: Install Rust 132 | uses: actions-rs/toolchain@v1 133 | with: 134 | profile: minimal 135 | toolchain: stable 136 | override: true 137 | components: rustfmt, clippy 138 | 139 | - name: Install LLVM 140 | run: | 141 | wget https://apt.llvm.org/llvm.sh 142 | chmod +x llvm.sh 143 | sudo ./llvm.sh 19 144 | sudo apt-get install -y llvm-19 llvm-19-dev clang-tools-19 145 | 146 | - name: Install Tree-sitter (with runtime) 147 | run: | 148 | npm install -g tree-sitter-cli 149 | curl -fsSL https://github.com/tree-sitter/tree-sitter/archive/refs/tags/v0.25.4.tar.gz | tar -xz 150 | cd tree-sitter-* 151 | make 152 | sudo make install 153 | sudo ldconfig 154 | 155 | - name: Install rustfilt 156 | run: cargo install rustfilt 157 | 158 | - name: Build dist-check with coverage 159 | timeout-minutes: 10 160 | run: CC=clang-19 LLVM_SUFFIX=-19 make coverage -j8 161 | 162 | - name: Upload coverage reports to Codecov 163 | uses: codecov/codecov-action@v3 164 | with: 165 | token: ${{ secrets.CODECOV_TOKEN }} 166 | files: ./build/coverage/coverage.txt 167 | fail_ci_if_error: true 168 | 169 | 170 | docker: 171 | name: Build docker image 172 | runs-on: ubuntu-latest 173 | steps: 174 | - uses: actions/checkout@v2 175 | - run: docker build . 176 | -------------------------------------------------------------------------------- /.github/workflows/docker.yml: -------------------------------------------------------------------------------- 1 | name: Publish Docker image 2 | 3 | on: 4 | schedule: 5 | - cron: "30 6 * * 1" 6 | workflow_dispatch: {} 7 | 8 | jobs: 9 | docker: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v2 14 | - name: Set up QEMU 15 | uses: docker/setup-qemu-action@master 16 | with: 17 | platforms: all 18 | 19 | - name: Set up Docker Buildx 20 | id: buildx 21 | uses: docker/setup-buildx-action@master 22 | 23 | - name: Log into GitHub Container Registry 24 | run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login https://ghcr.io -u ${{ github.actor }} --password-stdin 25 | 26 | - name: Build 27 | uses: docker/build-push-action@v2 28 | with: 29 | builder: ${{ steps.buildx.outputs.name }} 30 | context: . 31 | target: alumina-boot 32 | file: ./Dockerfile 33 | platforms: linux/amd64,linux/arm64 34 | push: true 35 | tags: ghcr.io/alumina-lang/alumina-boot:latest 36 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: Documentation 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | 9 | jobs: 10 | docs: 11 | name: Publish documentation 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Install Rust 16 | uses: actions-rs/toolchain@v1 17 | with: 18 | profile: minimal 19 | toolchain: stable 20 | override: true 21 | components: rustfmt, clippy 22 | - name: Install Tree-sitter (with runtime) 23 | run: | 24 | npm install -g tree-sitter-cli 25 | curl -fsSL https://github.com/tree-sitter/tree-sitter/archive/refs/tags/v0.25.4.tar.gz | tar -xz 26 | cd tree-sitter-* 27 | make -j8 28 | sudo make install 29 | sudo ldconfig 30 | 31 | - name: Build documentation 32 | env: 33 | ALUMINA_DOC_BASE_PATH: https://docs.alumina-lang.net 34 | run: make -j8 docs 35 | 36 | - name: Generate the sitemap 37 | id: sitemap 38 | uses: cicirello/generate-sitemap@v1 39 | with: 40 | base-url-path: https://docs.alumina-lang.net 41 | path-to-root: ./build/debug/html 42 | 43 | - name: Publish to GitHub Pages 44 | uses: peaceiris/actions-gh-pages@v3 45 | if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} 46 | with: 47 | github_token: ${{ secrets.GITHUB_TOKEN }} 48 | publish_dir: ./build/debug/html 49 | cname: docs.alumina-lang.net 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build artifacts 2 | /aluminac 3 | /alumina-boot 4 | 5 | # Manual testing 6 | /quick* 7 | 8 | # Generated by Cargo 9 | # will have compiled files and executables 10 | debug/ 11 | target/ 12 | build/ 13 | 14 | # These are backup files generated by rustfmt 15 | **/*.rs.bk 16 | **/.DS_Store 17 | 18 | # MSVC Windows builds of rustc generate these, which store debugging information 19 | *.pdb 20 | 21 | # Output directory 22 | output/ 23 | 24 | # Byte-compiled / optimized / DLL files 25 | __pycache__/ 26 | *.py[cod] 27 | *$py.class 28 | 29 | # C extensions 30 | *.so 31 | 32 | # Distribution / packaging 33 | .Python 34 | build/ 35 | develop-eggs/ 36 | dist/ 37 | downloads/ 38 | eggs/ 39 | .eggs/ 40 | parts/ 41 | sdist/ 42 | var/ 43 | wheels/ 44 | share/python-wheels/ 45 | *.egg-info/ 46 | .installed.cfg 47 | *.egg 48 | MANIFEST 49 | 50 | # PyInstaller 51 | # Usually these files are written by a python script from a template 52 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 53 | *.manifest 54 | *.spec 55 | 56 | # Installer logs 57 | pip-log.txt 58 | pip-delete-this-directory.txt 59 | 60 | # Unit test / coverage reports 61 | htmlcov/ 62 | .tox/ 63 | .nox/ 64 | .coverage 65 | .coverage.* 66 | .cache 67 | nosetests.xml 68 | coverage.xml 69 | *.cover 70 | *.py,cover 71 | .hypothesis/ 72 | .pytest_cache/ 73 | cover/ 74 | 75 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 76 | __pypackages__/ 77 | 78 | 79 | # Environments 80 | .env 81 | .venv 82 | env/ 83 | venv/ 84 | ENV/ 85 | env.bak/ 86 | venv.bak/ 87 | 88 | # mypy 89 | .mypy_cache/ 90 | .dmypy.json 91 | dmypy.json 92 | 93 | # Pyre type checker 94 | .pyre/ 95 | 96 | # pytype static type analyzer 97 | .pytype/ 98 | 99 | # Cython debug symbols 100 | cython_debug/ 101 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "lldb", 9 | "request": "launch", 10 | "name": "Debug", 11 | "program": "${workspaceFolder}/quick", 12 | "preLaunchTask": "quick", 13 | "args": [], 14 | "cwd": "${workspaceFolder}" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.formatting.provider": "black", 3 | "debug.allowBreakpointsEverywhere": true, 4 | "cSpell.enabled": false, 5 | "files.associations": { 6 | "minicoro.h": "c" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "quick", 8 | "type": "shell", 9 | "command": "make quick", 10 | "group": { 11 | "kind": "build" 12 | } 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "src/alumina-boot", 5 | "src/alumina-boot-macros" 6 | ] 7 | 8 | [profile.coverage] 9 | inherits = "dev" 10 | 11 | [profile.profiling] 12 | inherits = "release" 13 | debug = true 14 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:24.04 AS environment 2 | 3 | ENV DEBIAN_FRONTEND=noninteractive 4 | ENV CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse 5 | 6 | RUN apt-get update && apt-get install -y software-properties-common curl build-essential git ca-certificates gnupg 7 | 8 | RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y 9 | ENV PATH="/root/.cargo/bin:${PATH}" 10 | 11 | RUN mkdir -p /etc/apt/keyrings && \ 12 | (curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg) && \ 13 | (echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_22.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list) && \ 14 | apt-get update && \ 15 | apt-get install -y nodejs 16 | 17 | RUN cargo install tree-sitter-cli 18 | 19 | WORKDIR /alumina/deps 20 | RUN curl -fsSL https://github.com/tree-sitter/tree-sitter/archive/refs/tags/v0.25.4.tar.gz | tar -xz 21 | RUN cd tree-sitter-* && make -j8 && make install && ldconfig 22 | RUN curl -fsSL https://github.com/ianlancetaylor/libbacktrace/archive/master.tar.gz | tar -xz 23 | RUN cd libbacktrace-* && ./configure && make -j8 && make install 24 | 25 | FROM environment AS builder 26 | 27 | WORKDIR /alumina 28 | ADD . . 29 | 30 | ENV RELEASE=1 31 | RUN make -j8 32 | 33 | FROM ubuntu:24.04 AS alumina-boot 34 | 35 | COPY --from=builder /alumina/build/release/alumina-boot /usr/bin/alumina-boot 36 | COPY ./sysroot /usr/include/alumina 37 | 38 | WORKDIR /workspace 39 | ENV ALUMINA_SYSROOT=/usr/include/alumina 40 | 41 | ENTRYPOINT [ "/usr/bin/alumina-boot" ] 42 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2022 Tibor Djurica Potpara 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | project: 4 | default: 5 | target: auto 6 | threshold: 5% 7 | 8 | patch: no 9 | changes: no -------------------------------------------------------------------------------- /common/README.md: -------------------------------------------------------------------------------- 1 | # Common 2 | 3 | This directory contains common files that are used by both `alumina-boot` and tools, notably the syntax grammar for tree-sitter. 4 | -------------------------------------------------------------------------------- /common/tree-sitter.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://tree-sitter.github.io/tree-sitter/assets/schemas/config.schema.json", 3 | "grammars": [ 4 | { 5 | "name": "alumina", 6 | "camelcase": "Alumina", 7 | "title": "Alumina", 8 | "scope": "source.alumina", 9 | "file-types": [ 10 | "alumina" 11 | ], 12 | "injection-regex": "^alumina$", 13 | "class-name": "TreeSitterAlumina" 14 | } 15 | ], 16 | "metadata": { 17 | "version": "0.1.0", 18 | "license": "MIT", 19 | "description": "Alumina grammar for tree-sitter", 20 | "authors": [ 21 | { 22 | "name": "Tibor Djurica Potpara", 23 | "email": "tibor.djurica@ojdip.net" 24 | } 25 | ], 26 | "links": { 27 | "repository": "https://github.com/alumina-lang/alumina" 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /examples/backtrace.alu: -------------------------------------------------------------------------------- 1 | use std::cmp::{Ordering, sort_by}; 2 | use std::random::thread_rng; 3 | 4 | fn chaos_comparer(a: &i32, b: &i32) -> Ordering { 5 | let r: f64 = thread_rng().next_float(); 6 | if r < 0.0001 { 7 | panic!("oops, I'm a bad comparer"); 8 | } else { 9 | a.compare(b) 10 | } 11 | } 12 | 13 | fn main() { 14 | let vec = 15 | (0..10000) 16 | .map(|_: i32| -> i32 { thread_rng().next(..) }) 17 | .to_vector(); 18 | defer vec.free(); 19 | 20 | vec[..].sort_by(chaos_comparer); 21 | } 22 | -------------------------------------------------------------------------------- /examples/constants.alu: -------------------------------------------------------------------------------- 1 | // Simple constant values 2 | const INT: u32 = 1; 3 | const STR = "Hello"; 4 | const ARR = [1, 2, 3]; 5 | 6 | // Constants with compile-time evaluation 7 | const EVAL: u32 = 1 + 1; 8 | 9 | // Complex expressions with loops and function calls 10 | const RANDOM_NUMBERS: [usize; 20] = { 11 | let rng = std::random::Pcg32 { 12 | state: 0xa285d44c06ab9542, 13 | increment: 0x71bc6da31db36d8d, 14 | }; 15 | 16 | let ret: [usize; 20]; 17 | (0..20) 18 | .map(|&rng, _: i32| -> usize { rng.next(0usize..=10) }) 19 | .fill_slice(&ret); 20 | 21 | ret 22 | }; 23 | 24 | // Enum variants can have constant values 25 | enum Color { 26 | Red = (255 << 16) | (0 << 8) | 0, 27 | Green = (0 << 16) | (255 << 8) | 0, 28 | Blue = (0 << 16) | (0 << 8) | 255, 29 | } 30 | 31 | struct Foo { 32 | range: std::range::Range 33 | } 34 | 35 | // arrays, tuples and structs are supported 36 | const OPT: Option = Option::none(); 37 | 38 | const COMPLEX_CONST: [(Foo, Foo); 2] = [ 39 | (Foo { range: 1..2 }, Foo { range: 3..4 }), 40 | (Foo { range: 5..6 }, Foo { range: 7..8 }), 41 | ]; 42 | 43 | fn main() { 44 | use std::fmt::{hex, zero_pad}; 45 | 46 | print!("RANDOM NUMBERS = ["); 47 | for (idx, num) in RANDOM_NUMBERS.iter().enumerate() { 48 | if idx == 0 { 49 | print!("{}", num); 50 | } else { 51 | print!(", {}", num); 52 | } 53 | } 54 | println!("]"); 55 | 56 | // Constants can be used e.g. in array sizes... 57 | let arr: [i32; RANDOM_NUMBERS[5]]; 58 | println!("arr.len() = {}", arr.len()); 59 | 60 | // ...or as tuple indices 61 | let tup = (1, "a", true); 62 | println!("{}", tup.(RANDOM_NUMBERS[5] % 3)); 63 | 64 | for color in [Color::Red, Color::Green, Color::Blue] { 65 | println!("{}", (color as i32).hex().zero_pad(6)); 66 | } 67 | 68 | println!("OPT = {}", OPT); 69 | println!("COMPLEX_CONST[0].1.range.lower = {}", COMPLEX_CONST[0].1.range.lower); 70 | } 71 | -------------------------------------------------------------------------------- /examples/coroutines.alu: -------------------------------------------------------------------------------- 1 | use std::fs::{Path, File}; 2 | use std::string::starts_with; 3 | use std::io::{BufferedReader, lines, Error}; 4 | 5 | fn* read_lines(path: Path) -> Coroutine> { 6 | use std::result::yield_try as try; 7 | 8 | let file = File::open(path)?; 9 | defer file.close(); 10 | 11 | let reader = BufferedReader::new(&file, 1024 * 64); 12 | defer reader.free(); 13 | 14 | let lines = reader.lines(); 15 | defer lines.free(); 16 | 17 | for line in lines { 18 | yield line; 19 | } 20 | } 21 | 22 | fn main() { 23 | let lines = read_lines(Path::new("/proc/stat")); 24 | defer lines.close(); // All the defered calls in the generator will be executed, even if we break out early. 25 | 26 | for line in lines { 27 | let line = line.unwrap(); 28 | 29 | if !line.starts_with("cpu") { 30 | break; 31 | } 32 | println!("{}", line); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /examples/defer_and_move.alu: -------------------------------------------------------------------------------- 1 | use std::string::StringBuf; 2 | 3 | fn accept_vector(vec: StringBuf) { 4 | // do something with it 5 | println!("moved: {}", vec[..]); 6 | vec.free(); 7 | } 8 | 9 | fn main() { 10 | let vec = StringBuf::new(); 11 | defer vec.free(); // vec will now be automatically freed when `main` exits 12 | 13 | vec.extend_from_slice("Hello, world!"); 14 | 15 | // This would be wrong and lead to double free, as the same vector would be 16 | // freed both in `accept_vector` and `main`. 17 | // accept_vector(vec); 18 | 19 | // If we need to transfer ownership, we can use the move() function. This 20 | // creates an efficient shallow copy of the vector and leaves the original 21 | // `vec` nulled, so free is a no-op. 22 | accept_vector(vec.move()); 23 | 24 | println!("original: {}", vec[..]) 25 | } 26 | -------------------------------------------------------------------------------- /examples/dyn.alu: -------------------------------------------------------------------------------- 1 | protocol Slot { 2 | fn get(self: &Self) -> Option; 3 | fn set(self: &mut Self, element: Element); 4 | fn type_name(self: &Self) -> &[u8] { 5 | std::typing::type_name::() 6 | } 7 | } 8 | 9 | struct RealSlot { 10 | value: Option, 11 | } 12 | impl RealSlot { 13 | fn get(self: &RealSlot) -> Option { 14 | self.value 15 | } 16 | fn set(self: &mut RealSlot, element: i32) { 17 | self.value = Option::some(element); 18 | } 19 | mixin Slot; 20 | } 21 | 22 | struct NullSlot {} 23 | impl NullSlot { 24 | fn get(self: &NullSlot) -> Option { 25 | Option::none() 26 | } 27 | fn set(self: &mut NullSlot, _element: i32) { 28 | } 29 | mixin Slot; 30 | } 31 | 32 | /// Static polymorphism. 33 | /// 34 | /// During monomorphization, the compiler will generate a copy of `roundtrip_generic` 35 | /// for each type of slot encountered (RealSlot and NullSlot). 36 | fn roundtrip_generic>(slot: &mut T) { 37 | let val = 42; 38 | println!("[GENERIC] Inserting {} into {}", val, slot.type_name()); 39 | slot.set(val); 40 | println!("[GENERIC] Retrieving value from {}: {}", slot.type_name(), slot.get()); 41 | } 42 | 43 | /// Dynamic polymorphism. 44 | /// 45 | /// `roundtrip_dyn` is a non-generic function. It takes a dyn object, which contains 46 | /// a pointer to a virtual method table so an appropriate implementation of `Slot` can be 47 | /// found at runtime. 48 | fn roundtrip_dyn(slot: &mut dyn Slot) { 49 | let val = 42; 50 | println!("[DYN] Inserting {} into {}", val, slot.type_name()); 51 | slot.set(val); 52 | println!("[DYN] Retrieving value from {}: {}", slot.type_name(), slot.get()); 53 | } 54 | 55 | fn main() { 56 | let real_slot = RealSlot { value: Option::none() }; 57 | let null_slot = NullSlot {}; 58 | 59 | roundtrip_generic(&real_slot); 60 | roundtrip_generic(&null_slot); 61 | 62 | roundtrip_dyn(&real_slot); 63 | roundtrip_dyn(&null_slot); 64 | } 65 | -------------------------------------------------------------------------------- /examples/file_io.alu: -------------------------------------------------------------------------------- 1 | use std::fs::{File, Path}; 2 | use std::io::{Error, Readable, Writable}; 3 | 4 | fn copy, D: Writable>( 5 | src: &mut S, 6 | dst: &mut D 7 | ) -> Result<(), Error> { 8 | let buf: [u8; 1024]; 9 | 10 | loop { 11 | let n = src.read(&buf)?; 12 | if n == 0 { 13 | break; 14 | } 15 | dst.write_all(buf[0..n])?; 16 | } 17 | 18 | dst.flush() 19 | } 20 | 21 | fn copy_file(src: Path, dst: Path) -> Result<(), Error> { 22 | let src = File::open(src)?; 23 | defer src.close(); 24 | 25 | let dst = File::create(dst)?; 26 | defer dst.close(); 27 | 28 | copy(&src, &dst)?; 29 | 30 | Result::ok(()) 31 | } 32 | 33 | fn main() -> i32 { 34 | let ret = copy_file( 35 | Path::new("/proc/mounts"), 36 | Path::new("./mounts.txt") 37 | ); 38 | 39 | if ret.is_err() { 40 | eprintln!("failed: {}", ret.unwrap_err()); 41 | 1 42 | } else { 43 | 0 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /examples/first_class_functions.alu: -------------------------------------------------------------------------------- 1 | // This function will accept a function pointer as a parameter. 2 | fn say_twice_pointer(sayer: fn(T), what: T) { 3 | sayer(what); 4 | sayer(what); 5 | } 6 | 7 | // say_twice_mono will be monomorphized using a specific function say for a given T it is invoked 8 | // with. This allows for e.g. inlining and other optimizations. Unlike function pointers, 9 | // function types are zero-sized (each function type is a unit type containing only one 10 | // function), so the "sayer" parameter will be elided away and `say` will be called directly 11 | // from the body of the function. 12 | fn say_twice_mono(sayer: say, what: T) { 13 | sayer(what); 14 | sayer(what); 15 | } 16 | 17 | // Similar to the previous example, but much more useful. This will accept any function matching 18 | // the given Fn protocol, but will still be monomorphized with a specific function. 19 | fn say_twice_protocol(sayer: F, what: T) { 20 | sayer(what); 21 | sayer(what); 22 | } 23 | 24 | fn say(what: T) { 25 | println!("{}", what); 26 | } 27 | 28 | fn main() { 29 | say_twice_pointer(say, "hello"); 30 | say_twice_mono(say, "world"); 31 | 32 | // Unfortunately type inference is not yet smart enough to figure out the T parameter 33 | // of say if it is only constrained by the protocol bound, so we need to add a type 34 | // hint. 35 | say_twice_protocol(say::<&[u8]>, "!"); 36 | } 37 | -------------------------------------------------------------------------------- /examples/formatting.alu: -------------------------------------------------------------------------------- 1 | struct Foo { 2 | a: i32, 3 | b: i32 4 | } 5 | 6 | impl Foo { 7 | use std::fmt::{Formatter, write, Error}; 8 | 9 | fn fmt>(self: &Foo, formatter: &mut F) -> Result<(), Error> { 10 | write!(formatter, "Foo {{ a: {}, b: {} }}", self.a, self.b) 11 | } 12 | } 13 | 14 | fn main() { 15 | println!("{}", Foo { a: 1, b: 2 }); 16 | println!("{}", Foo { a: 2, b: 3 }); 17 | } 18 | -------------------------------------------------------------------------------- /examples/hello_world.alu: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // look ma, no 0-terminated strings! 3 | println!("Hello, world!"); 4 | } 5 | -------------------------------------------------------------------------------- /examples/iterators.alu: -------------------------------------------------------------------------------- 1 | use std::string::to_upper; 2 | 3 | fn main() { 4 | let without_wovels = "Quick brown fox jumps over the lazy dog" 5 | .iter() 6 | .filter(|a: u8| -> bool { 7 | switch a.to_upper() { 8 | 'A', 'E', 'I', 'O', 'U' => false, 9 | _ => true 10 | } 11 | }) 12 | .to_vector(); 13 | 14 | defer without_wovels.free(); 15 | println!("{}", without_wovels[..]); 16 | 17 | let sum_of_squares = (0..100) 18 | .iter() 19 | .map(|a: i32| -> i32 { a * a }) 20 | .enumerate() 21 | .inspect(|a: (usize, i32)| { println!("{}th square is {}", a.0, a.1) }) 22 | .take_while(|a: (usize, i32)| -> bool { a.1 < 1000 }) 23 | .reduce(0, |a: i32, b: (usize, i32)| -> i32 { a + b.1 }); 24 | 25 | println!("Sum of squares is {}", sum_of_squares); 26 | } 27 | -------------------------------------------------------------------------------- /examples/lambdas_closures.alu: -------------------------------------------------------------------------------- 1 | use std::range::Range; 2 | use std::collections::Vector; 3 | 4 | fn is_prime(v: i64) -> bool { 5 | let i: i64 = 2; 6 | while i < v { 7 | if v % i == 0 { 8 | return false; 9 | } 10 | i += 1; 11 | } 12 | true 13 | } 14 | 15 | fn print_filtered bool>(range: Range, filter: F) { 16 | let first = true; 17 | for i in range { 18 | if filter(i) { 19 | if first { 20 | print!("{}", i); 21 | first = false; 22 | } else { 23 | print!(", {}", i) 24 | } 25 | } 26 | } 27 | println!(""); 28 | } 29 | 30 | fn main() { 31 | // Anonymous functions that do not capture ambient variables 32 | let hw = || { println!("Hello, world!"); }; 33 | hw(); 34 | hw(); 35 | 36 | print_filtered(1..100, |p: i64| -> bool { p < 10 }); 37 | print_filtered(1..100, |p: i64| -> bool { is_prime(p) }); 38 | 39 | // Anonymous functions can be coerced to function pointers. 40 | let hw_ptr: fn() = hw; 41 | hw_ptr(); 42 | 43 | // Closures are anonymous functions that capture variables from ambient scope. 44 | // Captures can be either by value (`=capture`) or by reference (`&capture`) and 45 | // they need to be explicitly specified in the function signature. 46 | 47 | // `a` is passed by value (is copied) into the closure. Captured values are part of the 48 | // closure's state and can be mutated, but the original variable remains unchanged. 49 | let a = 1i64; 50 | let f = |=a, increment: i64| { 51 | a += increment; 52 | println!("[inside] a = {}", a) 53 | }; 54 | 55 | f(10); 56 | f(1); 57 | 58 | println!("[outside] a = {}", a); 59 | 60 | 61 | // `composites` is passed by reference. The changes will be reflected in the original 62 | // variable. 63 | let composites: Vector = Vector::new(); 64 | defer composites.free(); 65 | 66 | print_filtered(1..100, |&composites, p: i64| -> bool { 67 | if is_prime(p) { 68 | true 69 | } else { 70 | composites.push(p); 71 | false 72 | } 73 | }); 74 | 75 | println!("composites.len() = {}", composites.len()); 76 | } 77 | -------------------------------------------------------------------------------- /examples/line_numbering.alu: -------------------------------------------------------------------------------- 1 | 2 | use std::io::{StdioStream, BufferedReader, lines}; 3 | use std::string::StringBuf; 4 | use std::fmt::write; 5 | 6 | fn main() { 7 | let br = BufferedReader::new(&StdioStream::stdin(), 60 * 1024); 8 | defer br.free(); 9 | 10 | let lines_iter = br.lines(); 11 | defer lines_iter.free(); 12 | 13 | let padded = StringBuf::new(); 14 | defer padded.free(); 15 | 16 | let i = 1; 17 | for line in lines_iter { 18 | padded.clear(); 19 | write!(&padded, "{}", i).unwrap(); 20 | while padded.len() < 4 { 21 | padded.insert(0, ' '); 22 | } 23 | 24 | println!("{} | {}", padded[..], line.unwrap()); 25 | i += 1; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/macros.alu: -------------------------------------------------------------------------------- 1 | fn printf_impl(fmt: &[u8], args: &[&void]) { 2 | let i = 0usize; 3 | let buf: [u8; 32]; 4 | let buf: &mut [u8] = &buf; 5 | 6 | let buf_pos = 0usize; 7 | let in_escape = false; 8 | 9 | // Macros are hygienic, but if defined in linear scopes, 10 | // they can bind ambient variables (like buf, buf_pos here) 11 | macro buf_append($val) { 12 | buf[buf_pos] = $val; 13 | buf_pos += 1; 14 | if buf_pos == buf.len() { 15 | print!("{}", buf); 16 | buf_pos = 0; 17 | } 18 | } 19 | 20 | macro pop_arg() { 21 | if args.len() == 0 { 22 | panic!("too few arguments"); 23 | } 24 | let arg = args[0]; 25 | args = args[1..]; 26 | arg 27 | } 28 | 29 | while i < fmt.len() { 30 | let ch = fmt[i]; 31 | if in_escape { 32 | switch ch { 33 | 's' => { 34 | print!("{}", buf[0..buf_pos]); 35 | print!("{}", *(pop_arg!() as &&[u8])); 36 | buf_pos = 0; 37 | } 38 | 'd' => { 39 | print!("{}", buf[0..buf_pos]); 40 | print!("{}", *(pop_arg!() as &i32)); 41 | buf_pos = 0; 42 | } 43 | '%' => { 44 | buf_append!(ch); 45 | } 46 | _ => { 47 | panic!("unknown escape code") 48 | } 49 | } 50 | in_escape = false; 51 | } else { 52 | if ch == '%' { 53 | in_escape = true; 54 | } else { 55 | buf_append!(ch); 56 | } 57 | } 58 | i += 1; 59 | } 60 | 61 | print!("{}", buf[0..buf_pos]); 62 | 63 | if args.len() > 0 { 64 | panic!("too many arguments"); 65 | } 66 | } 67 | 68 | macro printf($fmt, $expr...) { 69 | printf_impl($fmt, &[(&$expr as &void)$...]) 70 | } 71 | 72 | 73 | fn main() { 74 | printf!( 75 | "hello %s, you are %d years old, you have %d dogs and %d cats\n", 76 | "user", 77 | 42, 78 | 3, 79 | 6 80 | ); 81 | 82 | // Macros can also be invoked using universal call syntax 83 | "%d + %d = %d\n".printf!(1, 2, 3); 84 | } 85 | -------------------------------------------------------------------------------- /examples/peano.alu: -------------------------------------------------------------------------------- 1 | use std::builtins::generic_args_of; 2 | use std::util::unit; 3 | 4 | // Peano integers 0, S(0), S(S(0)), ... 5 | struct Zero {} 6 | struct Succ {} 7 | 8 | impl Zero { 9 | fn val(_v: Zero) -> i32 { 0 } 10 | } 11 | 12 | impl Succ { 13 | fn val(_v: Succ) -> i32 { unit::().val() + 1 } 14 | } 15 | 16 | fn is_zero() -> bool { 17 | std::typing::matches::() 18 | } 19 | 20 | type One = Succ; 21 | type Two = Succ; 22 | type Three = Succ; 23 | type Four = Succ; 24 | type Five = Succ; 25 | type Six = Succ; 26 | type Seven = Succ; 27 | type Eight = Succ; 28 | type Nine = Succ; 29 | type Ten = Succ; 30 | type Eleven = Succ; 31 | type Twelve = Succ; 32 | 33 | type Not = when is_zero::() { One } else { Zero }; 34 | type And = when is_zero::() { Zero } else { B }; 35 | type Or = when is_zero::() { B } else { One }; 36 | type Xor = when is_zero::() { B } else { Not }; 37 | 38 | type Prev = when is_zero::() { Zero } else { generic_args_of.0 }; 39 | type Add = when is_zero::() { A } else { Succ>> }; 40 | type Sub = when is_zero::() { A } else { Sub, Prev> }; 41 | type Mul = when is_zero::() { Zero } else { Add>> }; 42 | 43 | type DivT = when is_zero::() { 44 | typeof(compile_fail!("division by zero")) 45 | } else when is_zero::, B>>() { 46 | Idx 47 | } else { 48 | DivT, B, Succ> 49 | }; 50 | 51 | type Div = DivT; 52 | type Mod = Sub, B>>; 53 | 54 | type Fibonacci = when is_zero::>() { 55 | T 56 | } else { 57 | Add>, Fibonacci>>> 58 | }; 59 | 60 | type IsPrimeT = when std::typing::matches::() { 61 | One // = true 62 | } else when is_zero::>() { 63 | Zero // = false 64 | } else { 65 | IsPrimeT> 66 | }; 67 | 68 | type IsPrime = when is_zero::>() { 69 | Zero // = false 70 | } else { 71 | IsPrimeT 72 | }; 73 | 74 | type Gcd = when is_zero::() { 75 | A 76 | } else { 77 | Gcd> 78 | }; 79 | 80 | fn main() { 81 | println!("Arithmetic:"); 82 | println!("10 * 5 + (6 * 2) / 3 = {}", unit::< 83 | Add< 84 | Mul, 85 | Div< 86 | Mul, 87 | Three 88 | > 89 | >>() 90 | .val()); 91 | 92 | println!("Fibonacci:"); 93 | println!("fib(0) = {}", unit::>().val()); 94 | println!("fib(1) = {}", unit::>().val()); 95 | println!("fib(2) = {}", unit::>().val()); 96 | println!("fib(3) = {}", unit::>().val()); 97 | println!("fib(4) = {}", unit::>().val()); 98 | println!("fib(5) = {}", unit::>().val()); 99 | println!("fib(6) = {}", unit::>().val()); 100 | println!("fib(7) = {}", unit::>().val()); 101 | println!("fib(8) = {}", unit::>().val()); 102 | println!("fib(9) = {}", unit::>().val()); 103 | println!("fib(10) = {}", unit::>().val()); 104 | println!("fib(11) = {}", unit::>().val()); 105 | println!("fib(12) = {}", unit::>().val()); 106 | 107 | println!("Primality:"); 108 | println!("is_prime(1) = {}", unit::>().val()); 109 | println!("is_prime(2) = {}", unit::>().val()); 110 | println!("is_prime(3) = {}", unit::>().val()); 111 | println!("is_prime(4) = {}", unit::>().val()); 112 | println!("is_prime(5) = {}", unit::>().val()); 113 | println!("is_prime(6) = {}", unit::>().val()); 114 | println!("is_prime(7) = {}", unit::>().val()); 115 | println!("is_prime(8) = {}", unit::>().val()); 116 | println!("is_prime(9) = {}", unit::>().val()); 117 | println!("is_prime(10) = {}", unit::>().val()); 118 | println!("is_prime(11) = {}", unit::>().val()); 119 | println!("is_prime(12) = {}", unit::>().val()); 120 | } 121 | -------------------------------------------------------------------------------- /examples/process.alu: -------------------------------------------------------------------------------- 1 | use std::string::{trim}; 2 | use std::process::{Command, Stdio}; 3 | use std::fs::Path; 4 | 5 | fn main() { 6 | let command = Command::new(Path::new("/usr/bin/uname")) 7 | .stdout(Stdio::Piped) 8 | .args(&["-s"]); 9 | 10 | let output = command.spawn() 11 | .unwrap() 12 | .wait_with_output() 13 | .unwrap(); 14 | defer output.free(); 15 | 16 | println!("Running on {}", output.stdout[..].trim()); 17 | } 18 | -------------------------------------------------------------------------------- /examples/reflection.alu: -------------------------------------------------------------------------------- 1 | use std::typing::Type; 2 | use std::builtins::{Struct, Union}; 3 | 4 | /// Set a field on an struct by name 5 | fn set(obj: &mut T, name: &[u8], value: F) { 6 | let ty = Type::new::(); 7 | let value_ty = Type::new::(); 8 | 9 | let fields = ty.fields(); 10 | for const i in 0usize..fields.len() { 11 | let field_ty = fields.(i).type(); 12 | 13 | if fields.(i).name() == Option::some(name) { 14 | when field_ty.is_same_as(value_ty) { 15 | *fields.(i).as_mut_ptr(obj) = value; 16 | return; 17 | } else { 18 | panic!( 19 | "expected type {}, got {}", 20 | field_ty.debug_name(), 21 | value_ty.debug_name() 22 | ); 23 | } 24 | } 25 | } 26 | 27 | panic!("field not found: {}", name); 28 | } 29 | 30 | struct Foo { 31 | bar: i32, 32 | quux: bool, 33 | } 34 | 35 | fn main() { 36 | let foo: Foo; 37 | foo.set("bar", 42); 38 | foo.set("quux", true); 39 | 40 | // These would panic at runtime 41 | // foo.set("bar", true); 42 | // foo.set("unknown", 42); 43 | 44 | println!("bar = {}", foo.bar); 45 | println!("quux = {}", foo.quux); 46 | } 47 | -------------------------------------------------------------------------------- /examples/result.alu: -------------------------------------------------------------------------------- 1 | use std::random::thread_rng; 2 | use std::result::{Result, try}; 3 | 4 | fn try_get_number() -> Result { 5 | let val = thread_rng().next(0..3); 6 | 7 | if val > 0 { 8 | Result::ok(val) 9 | } else { 10 | Result::err(()) 11 | } 12 | } 13 | 14 | fn try_cast() -> Result { 15 | let value = try_get_number()?; 16 | 17 | Result::ok(value as i64) 18 | } 19 | 20 | fn main() { 21 | let ok = try_cast(); 22 | 23 | println!("value: {}", ok.unwrap()); 24 | } 25 | -------------------------------------------------------------------------------- /examples/statics.alu: -------------------------------------------------------------------------------- 1 | use std::io::{print, println}; 2 | 3 | // Static variables are initialized in dependency order and if there 4 | // are cycles in the graph, a compile-time error will be produced. 5 | // If a variable is unused, initialization code is not guaranteed to run. 6 | 7 | static global_var1: i32 = hello(); 8 | static global_var2: i32 = global_var3 + 1; 9 | static global_var3: i32 = global_var4 + 1; 10 | static global_var4: i32 = global_var1 + 1; 11 | static global_var5: i32 = global_var6 + 1; 12 | static global_var6: i32 = if global_var2 > 2 { global_var2 } else { 0 }; 13 | static global_var7: i32 = loop { 14 | if global_var5 > 1000 { 15 | break global_var5 16 | } 17 | 18 | global_var5 *= 2; 19 | }; 20 | 21 | // Statics can also be generic. This can be useful to create associated statics for 22 | // multiple distinct types 23 | static TYPE_NAME: &[u8] = std::typing::type_name::(); 24 | 25 | fn hello() -> i32 { 26 | 3 27 | } 28 | 29 | fn main() { 30 | println!("{}", global_var7); 31 | 32 | /// Each instance of a generic static is distinct, but also mutable 33 | TYPE_NAME:: = "32-bit signed integer"; 34 | 35 | println!("{}", TYPE_NAME::); 36 | println!("{}", TYPE_NAME::); 37 | println!("{}", TYPE_NAME::>); 38 | } 39 | -------------------------------------------------------------------------------- /examples/tcp_client_server.alu: -------------------------------------------------------------------------------- 1 | //! A line numbering service 2 | 3 | use std::net::{ 4 | NameLookup, TcpStream, TcpListener, Shutdown, 5 | SocketAddr, buffered 6 | }; 7 | use std::collections::Vector; 8 | use std::io::{lines, Error, StdioStream, copy}; 9 | use std::thread::spawn; 10 | use std::fmt::{write, writeln, pad_with}; 11 | 12 | const PORT: u16 = 24601; 13 | 14 | fn client() -> Result<(), Error> { 15 | let addresses = NameLookup::resolve("localhost", PORT)?; 16 | defer addresses.free(); 17 | 18 | // Take the first address 19 | let address = addresses.next() 20 | .ok_or(Error::custom("cannot resolve the address"))?; 21 | 22 | let socket = TcpStream::connect(&address)?; 23 | defer socket.close(); 24 | 25 | // We send to the socket in a separate thread to avoid buffers 26 | // getting full. 27 | let reader = spawn(|&socket| -> Result<(), Error> { 28 | copy(&StdioStream::stdin(), &socket)?; 29 | socket.shutdown(Shutdown::Write)?; 30 | Result::ok(()) 31 | }); 32 | defer reader.join().unwrap()?; 33 | 34 | copy(&socket, &StdioStream::stdout())?; 35 | 36 | Result::ok(()) 37 | } 38 | 39 | fn handle_connection(socket: TcpStream) -> Result<(), Error> { 40 | defer socket.close(); 41 | 42 | // "Split" the socket into a buffered reader and writer pair 43 | let (reader, writer) = socket.buffered(1024, 1024); 44 | defer reader.free(); 45 | defer writer.free(); 46 | 47 | let lines_iter = reader.lines(); 48 | defer lines_iter.free(); 49 | 50 | for (idx, maybe_line) in lines_iter.enumerate() { 51 | writeln!(&writer, "{} | {}", (idx + 1).pad_with(5, ' '), maybe_line?) 52 | .map_err(|_: std::fmt::Error| -> Error { Error::custom("failed to write to socket") } )?; 53 | } 54 | 55 | writer.flush()?; 56 | socket.shutdown(Shutdown::Write)?; 57 | 58 | Result::ok(()) 59 | } 60 | 61 | fn server() -> Result { 62 | let addr = SocketAddr::parse("[::]:24601").unwrap(); 63 | let listener = TcpListener::bind(&addr)?; 64 | defer listener.close(); 65 | 66 | println!("Listening on {}", listener.socket_addr()?); 67 | 68 | loop { 69 | let (socket, addr) = listener.accept()?; 70 | 71 | println!("Received a connection from {}", addr); 72 | 73 | // Spawn a new thread for handling the connection 74 | spawn(|=socket| { 75 | let ret = handle_connection(socket); 76 | if ret.is_err() { 77 | eprintln!("error while reading from socket: {}", ret.unwrap_err()); 78 | } 79 | }) 80 | .detach(); 81 | } 82 | } 83 | 84 | fn main(args: &[&[u8]]) -> i32 { 85 | // Mask sigpipe 86 | libc::signal(libc::SIGPIPE, libc::SIG_IGN); 87 | 88 | macro usage() { 89 | eprintln!("usage: {} [--server|--client]", args[0]); 90 | return 1; 91 | } 92 | 93 | if args.len() != 2 { 94 | usage!() 95 | } 96 | 97 | switch args[1] { 98 | "--server" => server().unwrap(), 99 | "--client" => client().unwrap(), 100 | _ => usage!() 101 | }; 102 | 103 | 0 104 | } 105 | -------------------------------------------------------------------------------- /examples/threading.alu: -------------------------------------------------------------------------------- 1 | use std::thread::{sleep, spawn}; 2 | use std::time::Duration; 3 | use std::sync::channel::Channel; 4 | 5 | fn main() { 6 | // Create a new channel with capacity of 5 7 | let c1: Channel = Channel::new(5); 8 | defer c1.free(); 9 | 10 | let t1 = spawn(|&c1| { 11 | for i in 0..10 { 12 | c1.send(i); 13 | println!("Sent {}", i); 14 | } 15 | 16 | // Signal that no more values are coming 17 | c1.close(); 18 | }); 19 | defer t1.join().unwrap(); 20 | 21 | // Give the producer a head start 22 | std::thread::sleep(Duration::from_millis(100)); 23 | 24 | // Receive all the values slowly 25 | for i in c1 { 26 | println!("Received {}", i); 27 | std::thread::sleep(Duration::from_millis(100)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /examples/typeof.alu: -------------------------------------------------------------------------------- 1 | use std::io::println; 2 | use std::builtins::{Array, ZeroSized, Numeric}; 3 | use std::fmt::{Formattable, format}; 4 | use std::typing::matches; 5 | use std::string::StringBuf; 6 | 7 | type element_type = typeof({ let v: T; v[0] }); 8 | 9 | fn last_element(v: T) -> element_type { 10 | when (v is Array) && (v is ZeroSized) { 11 | // We want to allow e.g. [(); 42], where the whole array is still zero-sized but has 12 | // a non-zero length. 13 | when !(v[0] is ZeroSized) { 14 | compile_fail!("Cannot get the last element of a zero-length array"); 15 | } 16 | } 17 | 18 | v[v.len() - 1] 19 | } 20 | 21 | fn plus>(lhs: T, rhs: T) -> when typing::is_numeric::() { T } else { StringBuf } { 22 | when lhs is Numeric { 23 | lhs + rhs 24 | } else { 25 | format!("{}{}", lhs, rhs).unwrap() 26 | } 27 | } 28 | 29 | 30 | fn main() { 31 | let a = 10usize; 32 | let _: typeof(a) = 12; // = 12usize; 33 | 34 | /// Works for fixed-size arrays 35 | println!("last int: {}", [1,2,3,4,5].last_element()); 36 | println!("last string: {}", ["Hello", "World"].last_element()); 37 | 38 | // And for slices 39 | let values: &[bool] = &[true, true, false]; 40 | println!("last bool: {}", last_element(values)); 41 | 42 | // This works... 43 | let _ = [(), (), ()].last_element(); 44 | 45 | // ... but this would be a compile-time error: 46 | // let zst: [i32; 0] = []; 47 | // println!("{}", last_element(zst)); 48 | 49 | println!("10 + 10 = {}", 10.plus(10)); 50 | println!("\"hello\" + \"world\" = {}", "hello".plus("world")); 51 | } 52 | -------------------------------------------------------------------------------- /examples/unit_tests.alu: -------------------------------------------------------------------------------- 1 | // Compile with `--cfg test` and run the compiled output. 2 | use std::builtins::Numeric; 3 | 4 | fn plus(a: T, b: T) -> T { 5 | a + b 6 | } 7 | 8 | fn my_panic() -> ! { 9 | libc::_exit(1337); 10 | } 11 | 12 | fn main() { 13 | } 14 | 15 | 16 | #[cfg(test)] 17 | mod tests { 18 | #[test] 19 | fn test_arithmetic() { 20 | assert_eq!(plus(1, 1), 2); 21 | assert_eq!(plus(2, 2), 4); 22 | } 23 | 24 | #[test] 25 | fn test_panic() { 26 | test::assert_panics!(my_panic()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /examples/variadic.alu: -------------------------------------------------------------------------------- 1 | use std::builtins::Tuple; 2 | use std::typing::{type_name, is_void}; 3 | 4 | // #[tuple_args] attribute is used to make the function receive its arguments as a single tuple 5 | // instead of multiple arguments. 6 | #[tuple_args] 7 | fn print_debug(args: T) { 8 | // We can use recursion to go through all the elements... 9 | 10 | when args.len() == 0 { 11 | println!(""); 12 | } else when args.len() == 1 { 13 | println!("[{}] {}", type_name::(), args.0); 14 | } else { 15 | print!("[{}] {}, ", type_name::(), args.0); 16 | 17 | // invoke is the converse of #[tuple_args], it allows us to call a function with a 18 | // tuple of arguments. 19 | (print_debug::).invoke(args.(1..)); 20 | } 21 | } 22 | 23 | #[tuple_args] 24 | fn make_array(t: T) -> [T.0; t.len()] { 25 | // ...but we don't have to, we can use a const loop instead! 26 | 27 | let arr: [T.0; t.len()]; 28 | for const i in 0usize..t.len() { 29 | arr[i] = t.(i); 30 | } 31 | arr 32 | } 33 | 34 | 35 | fn main() { 36 | print_debug(); 37 | print_debug(1); 38 | print_debug(1, "hello"); 39 | print_debug(1, "hello", true); 40 | 41 | const ARR = make_array(1, 2, 3); 42 | println!("{}, {}, {}", ARR[0], ARR[1], ARR[2]); 43 | } 44 | -------------------------------------------------------------------------------- /libraries/README.md: -------------------------------------------------------------------------------- 1 | ## Libraries 2 | 3 | Contains various reusable Alumina libraries that do not belong in the standard library. 4 | 5 | - [./tree_sitter.alu](./tree_sitter.alu) - Language bindings for the Tree-Sitter library -------------------------------------------------------------------------------- /libraries/aluminac/README.md: -------------------------------------------------------------------------------- 1 | ## aluminac: self hosted compiler 2 | 3 | This will eventually be the self-hosted compiler for the language. For now it just does some basic 4 | parsing. 5 | -------------------------------------------------------------------------------- /libraries/aluminac/lib/arena.alu: -------------------------------------------------------------------------------- 1 | use std::mem::{size_of, align_of}; 2 | 3 | const DEFAULT_CAPACITY: usize = 4096; 4 | 5 | #[cfg_attr(target_pointer_width = "32", align(16))] 6 | #[cfg_attr(target_pointer_width = "64", align(32))] 7 | struct ArenaNode { 8 | ptr: &mut u8, 9 | remaining: usize, 10 | next: &mut ArenaNode, 11 | } 12 | 13 | impl ArenaNode { 14 | fn create(capacity: usize) -> &mut ArenaNode { 15 | let mem = libc::malloc(size_of::() + capacity) as &mut u8; 16 | let node = mem as &mut ArenaNode; 17 | 18 | node.ptr = mem + size_of::(); 19 | node.remaining = capacity; 20 | node.next = null; 21 | 22 | node 23 | } 24 | 25 | fn free(self: &mut ArenaNode) { 26 | std::mem::free(self); 27 | } 28 | } 29 | 30 | struct Arena { 31 | increment: usize, 32 | root: &mut ArenaNode, 33 | tail: &mut ArenaNode, 34 | } 35 | 36 | #[inline] 37 | fn align_offset(base: usize, alignment: usize) -> usize { 38 | std::debug_assert!((alignment != 0) && ((alignment & (alignment - 1)) == 0)); 39 | ((base + alignment - 1) & ~(alignment - 1)) - base 40 | } 41 | 42 | impl Arena { 43 | fn new() -> Arena { 44 | with_capacity(DEFAULT_CAPACITY) 45 | } 46 | 47 | fn with_capacity(capacity: usize) -> Arena { 48 | let root_node = ArenaNode::create(capacity); 49 | Arena { 50 | root: root_node, 51 | tail: root_node, 52 | increment: capacity, 53 | } 54 | } 55 | 56 | #[cold] 57 | fn grow(self: &mut Arena, size: usize) { 58 | // If we want a single large allocation, do not increase the increment. 59 | self.increment = self.increment * 2; 60 | let size = std::cmp::max(self.increment, size); 61 | 62 | let node = ArenaNode::create(size); 63 | self.tail.next = node; 64 | self.tail = node; 65 | } 66 | 67 | fn aligned_alloc(self: &mut Arena, size: usize, alignment: usize) -> &mut void { 68 | let offset = align_offset(self.tail.ptr as usize, alignment); 69 | let bump = offset + size; 70 | 71 | if bump <= self.tail.remaining { 72 | self.tail.remaining -= bump; 73 | let result = self.tail.ptr + offset; 74 | self.tail.ptr += bump; 75 | result as &mut void 76 | } else { 77 | self.grow(size); 78 | self.aligned_alloc(size, alignment) 79 | } 80 | } 81 | 82 | #[inline] 83 | fn alloc(self: &mut Arena, value: T) -> &mut T { 84 | let ptr = self.aligned_alloc(size_of::(), align_of::()) as &mut T; 85 | *ptr = value; 86 | ptr 87 | } 88 | 89 | #[inline] 90 | fn alloc_slice(self: &mut Arena, len: usize) -> &mut [T] { 91 | let ptr = self.aligned_alloc(size_of::() * len, align_of::()) as &mut T; 92 | std::mem::slice::from_raw(ptr, len) 93 | } 94 | 95 | fn move(self: &mut Arena) -> Arena { 96 | let moved = *self; 97 | self.root = null; 98 | self.tail = null; 99 | moved 100 | } 101 | 102 | fn free(self: &mut Arena) { 103 | let node = self.root; 104 | while node != null { 105 | let next = node.next; 106 | std::mem::free(node); 107 | node = next; 108 | } 109 | } 110 | } 111 | 112 | #[cfg(test)] 113 | mod tests { 114 | #[test] 115 | fn test_arena() { 116 | let arena = Arena::new(); 117 | defer arena.free(); 118 | 119 | for i in 0..1000001 { 120 | let ptr: &mut i32 = arena.alloc(1023); 121 | let slice: &mut [i32] = arena.alloc_slice(133); 122 | 123 | *ptr = i; 124 | slice[4] = i; 125 | } 126 | } 127 | } 128 | 129 | -------------------------------------------------------------------------------- /libraries/aluminac/lib/common.alu: -------------------------------------------------------------------------------- 1 | use std::result::{Result, try}; 2 | use std::typing::matches; 3 | use tree_sitter::{Node, TSFieldId}; 4 | use node_kinds::{FieldKind}; 5 | 6 | macro child_by($node, $field) { 7 | $node.child_by_field_id($field as TSFieldId) 8 | } 9 | 10 | macro children_by($node, $field, $cursor) { 11 | $node.children_by_field_id($field as TSFieldId, $cursor) 12 | } 13 | 14 | protocol NodeVisitorExt { 15 | fn visit_children(self: &mut Self, node: Node) -> ReturnType { 16 | let cursor = node.walk(); 17 | defer cursor.free(); 18 | 19 | when matches::() { 20 | for node in node.children(&cursor) { 21 | self.visit(node); 22 | } 23 | } else { 24 | for node in node.children(&cursor) { 25 | self.visit(node)?; 26 | } 27 | ReturnType::ok(()) 28 | } 29 | } 30 | 31 | fn visit_children_by_field(self: &mut Self, node: Node, field: FieldKind) -> ReturnType { 32 | let cursor = node.walk(); 33 | defer cursor.free(); 34 | 35 | when matches::() { 36 | for node in children_by!(node, field, &cursor) { 37 | self.visit(node); 38 | } 39 | } else { 40 | for node in children_by!(node, field, &cursor) { 41 | self.visit(node)?; 42 | } 43 | ReturnType::ok(()) 44 | } 45 | } 46 | } 47 | 48 | struct Error {} 49 | -------------------------------------------------------------------------------- /libraries/aluminac/lib/mod.alu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alumina-lang/alumina/ee360b9878523f927b791bc08178505b50270655/libraries/aluminac/lib/mod.alu -------------------------------------------------------------------------------- /libraries/aluminac/mod.alu: -------------------------------------------------------------------------------- 1 | #![docs(no_index)] -------------------------------------------------------------------------------- /libraries/json/builder.alu: -------------------------------------------------------------------------------- 1 | //! Convenience macros for constructing JSON values. 2 | 3 | use std::builtins::{Numeric, Array}; 4 | 5 | fn _to_json(val: T) -> JsonValue { 6 | when val is JsonValue { 7 | val 8 | } else when val is () { 9 | JsonValue::null() 10 | } else when val is Numeric { 11 | JsonValue::number(val) 12 | } else when val is bool { 13 | JsonValue::boolean(val) 14 | } else when val is StringBuf { 15 | JsonValue::string(val) 16 | } else when val is std::option::AnyOption { 17 | if val.is_none() { 18 | JsonValue::null() 19 | } else { 20 | _to_json(val.unwrap()) 21 | } 22 | } else when val is Array { 23 | let array = Vector::with_capacity::(val.len()); 24 | for item in val { 25 | array.push(_to_json(item)); 26 | } 27 | JsonValue::array(array) 28 | } else when (val is &[u8]) || (val is &mut [u8]) { 29 | JsonValue::string(StringBuf::from_slice(val)) 30 | } else { 31 | std::compile_fail!("unsupported type for into!(...)"); 32 | } 33 | } 34 | 35 | fn _to_json_propery(prop: (K, V)) -> (StringBuf, JsonValue) { 36 | let key = when prop.0 is StringBuf { 37 | prop.0 38 | } else when (prop.0 is &[u8]) || (prop.0 is &mut [u8]) { 39 | StringBuf::from_slice(prop.0) 40 | } else { 41 | std::compile_fail!("unsupported type for json property key"); 42 | }; 43 | 44 | (key, _to_json(prop.1)) 45 | } 46 | 47 | /// Convert any supported value into a [JsonValue]. 48 | /// 49 | /// ## Examples 50 | /// ``` 51 | /// use json::builder::into; 52 | /// 53 | /// true.into!(); // true 54 | /// 1.into!(); // 1 55 | /// (3.1415).into!(); // 3.1415 56 | /// ().into!(); // null 57 | /// "hello".into!(); // "hello" 58 | /// [1, 2, 3].into!(); // [1, 2, 3] 59 | /// Option::some(42).into!(); // 42 60 | /// Option::none::().into!(); // null 61 | /// ``` 62 | macro into($val) { 63 | _to_json($val) 64 | } 65 | 66 | /// Construct a JSON object into a [JsonValue]. 67 | /// 68 | /// The arguments are a list of key-value pairs, where the key is a string-like value 69 | /// and the value is any supported value. 70 | /// 71 | /// ## Example 72 | /// ``` 73 | /// use json::builder::object; 74 | /// 75 | /// let value = object!( 76 | /// ("name", "John"), 77 | /// ("age", 42), 78 | /// ("is_admin", false) 79 | /// ); 80 | /// defer value.free(); 81 | /// 82 | /// println!("{}", value); // {"name":"John","age":42,"is_admin":false} 83 | /// ``` 84 | /// ## Note 85 | /// If the expressions in the key-value pairs perform flow control (e.g. through try operator `?`), 86 | /// the `object!` macro may leak memory. For example, the following code will leak memory: 87 | /// 88 | /// ```dubious 89 | /// use json::JsonValue; 90 | /// use json::builder::object; 91 | /// 92 | /// fn is_admin(_name: &[u8]) -> Result { 93 | /// Result::err(()) // Not yet implemented 94 | /// } 95 | /// 96 | /// fn serialize_user(name: &[u8]) -> Result { 97 | /// let value = object!( 98 | /// ("name", name), 99 | /// ("is_admin", is_admin(name)?) // <-- Leaks memory 100 | /// ); 101 | /// 102 | /// Result::ok(value) 103 | /// } 104 | /// 105 | /// let _ = serialize_user("John"); 106 | /// ``` 107 | macro object($val...) { 108 | use std::macros::count; 109 | use std::collections::HashMap; 110 | use std::string::StringBuf; 111 | 112 | let object: HashMap = HashMap::with_capacity(count!($val$...)); 113 | { 114 | let prop = _to_json_propery($val); 115 | object.insert(prop.0, prop.1); 116 | }$...; 117 | 118 | JsonValue::object(object) 119 | } 120 | 121 | /// Construct a JSON array into a [JsonValue]. 122 | /// 123 | /// The arguments are a list of values, where each value is any supported value. 124 | /// 125 | /// ## Example 126 | /// ``` 127 | /// use json::builder::array; 128 | /// 129 | /// let value = array!(1, true, "hello"); 130 | /// defer value.free(); 131 | /// 132 | /// println!("{}", value); // [1,true,"hello"] 133 | /// ``` 134 | /// ## Note 135 | /// If the expressions in the array perform flow control, the `array!` macro may leak memory. 136 | /// See the note in the [object] macro for an example. 137 | macro array($val...) { 138 | use std::macros::count; 139 | use std::collections::Vector; 140 | 141 | let array: Vector = Vector::with_capacity(count!($val$...)); 142 | { array.push(_to_json($val)); }$...; 143 | 144 | JsonValue::array(array) 145 | } 146 | -------------------------------------------------------------------------------- /src/alumina-boot-macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "alumina-boot-macros" 3 | description = "Procedural macros for alumina-boot" 4 | repository = "https://github.com/alumina-lang/alumina" 5 | version = "0.1.0" 6 | edition = "2021" 7 | 8 | [lib] 9 | proc-macro = true 10 | 11 | [dependencies] 12 | syn = { version = "2", features = ["full"] } 13 | quote = "1" 14 | blake3 = "1" 15 | walkdir = "2" 16 | -------------------------------------------------------------------------------- /src/alumina-boot/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "alumina-boot" 3 | description = "Bootstrap compiler for the Alumina programming language" 4 | repository = "https://github.com/alumina-lang/alumina" 5 | version = "0.1.0" 6 | edition = "2021" 7 | 8 | [build-dependencies] 9 | cc = "1" 10 | syn = { version = "2", features = ["full"] } 11 | quote = "1" 12 | serde = { version = "1", features = ["derive"] } 13 | serde_json = "1" 14 | proc-macro2 = "1" 15 | itertools = "0.14" 16 | 17 | [dependencies] 18 | regex = "1" 19 | walkdir = "2" 20 | clap = { version = "4", features = ["derive", "env"] } 21 | serde = { version = "1", features = ["derive"] } 22 | serde_json = "1" 23 | alumina-boot-macros = { path = "../alumina-boot-macros" } 24 | tree-sitter = "0.25.4" 25 | bumpalo = "3" 26 | thiserror = "2" 27 | indexmap = "2" 28 | once_cell = "1" 29 | colored = "2" 30 | rustc-hash = "2" 31 | strum = "0.27" 32 | strum_macros = "0.27" 33 | -------------------------------------------------------------------------------- /src/alumina-boot/dump_lang.c: -------------------------------------------------------------------------------- 1 | /* 2 | Dumps the names of the symbols and fields with associated IDs to JSON. 3 | This is done because node-types.json does not include numeric IDs, which requires 4 | the parsing to be done by string names (error-prone and inefficient). 5 | */ 6 | 7 | #include 8 | #include "tree_sitter/parser.h" 9 | 10 | extern const TSLanguage *tree_sitter_alumina(void); 11 | 12 | int main(void) { 13 | TSLanguage *language = (TSLanguage *)tree_sitter_alumina(); 14 | printf("{\"fields\":["); 15 | for (int i = 1; i <= language->field_count; ++i) { 16 | if (i > 1) { 17 | printf(","); 18 | } 19 | printf("{\"name\":\"%s\",\"id\":%d}", language->field_names[i], i); 20 | } 21 | printf("],\"symbols\":["); 22 | int first = true; 23 | for (int i = 1; i <= language->symbol_count + 1; ++i) { 24 | if (language->symbol_metadata[i].visible && language->symbol_metadata[i].named) { 25 | if (first) { 26 | first = false; 27 | } else { 28 | printf(","); 29 | } 30 | printf("{\"name\":\"%s\",\"id\":%d}", language->symbol_names[i], i); 31 | } 32 | } 33 | printf("]}"); 34 | } 35 | -------------------------------------------------------------------------------- /src/alumina-boot/src/ast/format.rs: -------------------------------------------------------------------------------- 1 | use crate::common::{AluminaError, CodeDiagnostic, CodeErrorBuilder, HashSet}; 2 | 3 | use super::Span; 4 | 5 | pub enum Piece { 6 | String(Vec), 7 | Argument(usize), 8 | } 9 | 10 | pub fn format_args( 11 | span: Option, 12 | fmt_string: &[u8], 13 | num_args: usize, 14 | ) -> Result, AluminaError> { 15 | #[derive(PartialEq, Eq, Debug)] 16 | enum State { 17 | Normal, 18 | BraceOpen, 19 | Index, 20 | BraceClose, 21 | } 22 | 23 | let mut used_arguments = HashSet::default(); 24 | 25 | let mut args = Vec::new(); 26 | let mut string_part = Vec::new(); 27 | 28 | let mut state = State::Normal; 29 | let mut arg_index = 0; 30 | 31 | let mut index_part = Vec::new(); 32 | 33 | macro_rules! push_arg { 34 | () => { 35 | let index_part = std::mem::take(&mut index_part); 36 | 37 | let index = if !index_part.is_empty() { 38 | std::str::from_utf8(&index_part) 39 | .ok() 40 | .and_then(|idx| idx.parse().ok()) 41 | .ok_or_else(|| { 42 | CodeDiagnostic::InvalidFormatString("invalid argument index".to_string()) 43 | }) 44 | .with_span(span)? 45 | } else { 46 | let idx = arg_index; 47 | arg_index += 1; 48 | idx 49 | }; 50 | 51 | if !string_part.is_empty() { 52 | args.push(Piece::String(std::mem::take(&mut string_part))); 53 | } 54 | 55 | if num_args <= index { 56 | return Err(CodeDiagnostic::InvalidFormatString( 57 | "not enough arguments".to_string(), 58 | )) 59 | .with_span(span); 60 | } 61 | 62 | used_arguments.insert(index); 63 | args.push(Piece::Argument(index)); 64 | }; 65 | } 66 | 67 | for ch in fmt_string.iter().copied() { 68 | state = match state { 69 | State::Normal => match ch { 70 | b'{' => State::BraceOpen, 71 | b'}' => State::BraceClose, 72 | _ => { 73 | string_part.push(ch); 74 | State::Normal 75 | } 76 | }, 77 | State::BraceClose => match ch { 78 | b'}' => { 79 | string_part.push(ch); 80 | State::Normal 81 | } 82 | _ => { 83 | return Err(CodeDiagnostic::InvalidFormatString(format!( 84 | "unexpected {:?}", 85 | ch as char 86 | ))) 87 | .with_span(span); 88 | } 89 | }, 90 | State::Index => match ch { 91 | b'0'..=b'9' => { 92 | index_part.push(ch); 93 | State::Index 94 | } 95 | b'}' => { 96 | push_arg!(); 97 | State::Normal 98 | } 99 | _ => { 100 | return Err(CodeDiagnostic::InvalidFormatString(format!( 101 | "unexpected {:?}", 102 | ch as char 103 | ))) 104 | .with_span(span); 105 | } 106 | }, 107 | State::BraceOpen => match ch { 108 | b'{' => { 109 | string_part.push(ch); 110 | State::Normal 111 | } 112 | b'}' => { 113 | push_arg!(); 114 | State::Normal 115 | } 116 | b'0'..=b'9' => { 117 | index_part.push(ch); 118 | State::Index 119 | } 120 | _ => { 121 | return Err(CodeDiagnostic::InvalidFormatString(format!( 122 | "unexpected {:?}", 123 | ch as char 124 | ))) 125 | .with_span(span); 126 | } 127 | }, 128 | }; 129 | } 130 | 131 | if state != State::Normal { 132 | return Err(CodeDiagnostic::InvalidFormatString( 133 | "unexpected end of format string".to_string(), 134 | )) 135 | .with_span(span); 136 | } 137 | 138 | if num_args > used_arguments.len() { 139 | return Err(CodeDiagnostic::InvalidFormatString( 140 | "unused arguments".to_string(), 141 | )) 142 | .with_span(span); 143 | } 144 | 145 | if !string_part.is_empty() { 146 | args.push(Piece::String(std::mem::take(&mut string_part))); 147 | } 148 | 149 | Ok(args) 150 | } 151 | -------------------------------------------------------------------------------- /src/alumina-boot/src/global_ctx.rs: -------------------------------------------------------------------------------- 1 | use crate::common::HashMap; 2 | use crate::diagnostics::{self, DiagnosticContext}; 3 | 4 | use std::cell::{Ref, RefCell}; 5 | use std::rc::Rc; 6 | 7 | #[derive(Copy, Clone)] 8 | pub enum OutputType { 9 | Library, 10 | Executable, 11 | } 12 | 13 | struct GlobalCtxInner { 14 | pub diag: DiagnosticContext, 15 | pub cfg: HashMap>, 16 | pub options: HashMap>, 17 | pub output_type: OutputType, 18 | } 19 | 20 | #[derive(Clone)] 21 | pub struct GlobalCtx { 22 | inner: Rc>, 23 | } 24 | 25 | impl GlobalCtx { 26 | pub fn new(output_type: OutputType, options: Vec<(String, Option)>) -> Self { 27 | let mut result = Self { 28 | inner: Rc::new(RefCell::new(GlobalCtxInner { 29 | diag: DiagnosticContext::new(), 30 | cfg: HashMap::default(), 31 | options: options.into_iter().collect(), 32 | output_type, 33 | })), 34 | }; 35 | 36 | // We are the alumina-boot compiler 37 | result.add_cfg_flag("boot"); 38 | 39 | // No cross-compilation, so we just use whatever the compiler was compiled with 40 | result.add_cfg("target_os", std::env::consts::OS); 41 | result.add_cfg("target_family", std::env::consts::FAMILY); 42 | result.add_cfg("target_arch", std::env::consts::ARCH); 43 | result.add_cfg("target_env", std::env!("ALUMINA_BUILD_TARGET_ENV")); 44 | result.add_cfg( 45 | "target_pointer_width", 46 | (std::mem::size_of::() * 8).to_string(), 47 | ); 48 | 49 | #[cfg(target_endian = "big")] 50 | result.add_cfg("target_endian", "big".to_string()); 51 | #[cfg(target_endian = "little")] 52 | result.add_cfg("target_endian", "little".to_string()); 53 | 54 | match output_type { 55 | OutputType::Executable => { 56 | result.add_cfg("output_type", "executable"); 57 | } 58 | OutputType::Library => { 59 | result.add_cfg("output_type", "library"); 60 | } 61 | }; 62 | 63 | if result.has_option("deny-warnings") { 64 | result.diag().add_override(diagnostics::Override { 65 | span: None, 66 | kind: None, 67 | action: diagnostics::Action::Deny, 68 | }); 69 | } 70 | 71 | if result.has_option("allow-warnings") { 72 | result.diag().add_override(diagnostics::Override { 73 | span: None, 74 | kind: None, 75 | action: diagnostics::Action::Allow, 76 | }); 77 | } 78 | 79 | result 80 | } 81 | 82 | pub fn should_generate_main_glue(&self) -> bool { 83 | matches!(self.inner.borrow().output_type, OutputType::Executable) 84 | } 85 | 86 | pub fn diag(&self) -> Ref<'_, DiagnosticContext> { 87 | Ref::map(self.inner.borrow(), |inner| &inner.diag) 88 | } 89 | 90 | pub fn has_cfg(&self, name: &str) -> bool { 91 | self.inner.borrow().cfg.contains_key(name) 92 | } 93 | 94 | pub fn has_option(&self, name: &str) -> bool { 95 | self.inner.borrow().options.contains_key(name) 96 | } 97 | 98 | pub fn add_cfg_flag(&mut self, value: impl ToString) { 99 | let mut borrowed = self.inner.borrow_mut(); 100 | borrowed.cfg.insert(value.to_string(), None); 101 | } 102 | 103 | pub fn add_cfg(&mut self, value: impl ToString, value_str: impl ToString) { 104 | let mut borrowed = self.inner.borrow_mut(); 105 | borrowed 106 | .cfg 107 | .insert(value.to_string(), Some(value_str.to_string())); 108 | } 109 | 110 | pub fn cfg(&self, key: &str) -> Option> { 111 | let borrowed = self.inner.borrow(); 112 | borrowed.cfg.get(key).cloned() 113 | } 114 | 115 | pub fn option(&self, key: &str) -> Option> { 116 | let borrowed = self.inner.borrow(); 117 | borrowed.options.get(key).cloned() 118 | } 119 | 120 | pub fn cfgs(&self) -> Vec<(String, Option)> { 121 | let borrowed = self.inner.borrow(); 122 | let mut ret: Vec<_> = borrowed 123 | .cfg 124 | .iter() 125 | .map(|(k, v)| (k.clone(), v.clone())) 126 | .collect(); 127 | 128 | ret.sort_by(|(a, _), (b, _)| a.cmp(b)); 129 | ret 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/alumina-boot/src/ir/inline.rs: -------------------------------------------------------------------------------- 1 | use crate::common::{AluminaError, CodeDiagnostic, HashMap}; 2 | use crate::diagnostics::DiagnosticsStack; 3 | use crate::ir::builder::ExpressionBuilder; 4 | use crate::ir::{ExprKind, ExprP, Id, IrCtx, Statement}; 5 | 6 | use super::fold::{Folder, IdUsageCounter}; 7 | use super::LocalDef; 8 | 9 | /// Inlining in IR is very experimental and very unstable. Basically, don't do it 10 | /// unless you are std. The main reason this even exists is to allow some lang functions 11 | /// that only construct a struct to be used in const contexts (looking at you slice_new!). 12 | pub struct IrInliner; 13 | 14 | impl IrInliner { 15 | pub fn inline<'ir, I>( 16 | diag: DiagnosticsStack, 17 | ir: &'ir IrCtx<'ir>, 18 | body: ExprP<'ir>, 19 | args: I, 20 | ) -> Result<(ExprP<'ir>, Vec>), AluminaError> 21 | where 22 | I: IntoIterator)>, 23 | { 24 | let local_counts = IdUsageCounter::count_locals(body)?; 25 | let mut statements = Vec::new(); 26 | let builder = ExpressionBuilder::new(ir); 27 | 28 | let mut local_defs = Vec::new(); 29 | let mut replacements: HashMap<_, _> = args.into_iter().collect(); 30 | 31 | for (id, expr) in replacements.iter_mut() { 32 | if local_counts.get(id).copied().unwrap_or(0) > 1 { 33 | let new_id = ir.make_id(); 34 | let ty = expr.ty; 35 | 36 | let expr = std::mem::replace(expr, builder.local(new_id, ty, expr.span)); 37 | 38 | statements.push(Statement::Expression(builder.assign( 39 | builder.local(new_id, ty, expr.span), 40 | expr, 41 | expr.span, 42 | ))); 43 | local_defs.push(LocalDef { id: new_id, ty }); 44 | } 45 | } 46 | 47 | let body = Folder::fold( 48 | body, 49 | ir, 50 | |expr| match expr.kind { 51 | ExprKind::Local(id) => Ok(Some(replacements.get(&id).copied().unwrap_or(expr))), 52 | ExprKind::Return(_) => Err(diag.err(CodeDiagnostic::IrInlineEarlyReturn)), 53 | ExprKind::Goto(_) => Err(diag.err(CodeDiagnostic::IrInlineFlowControl)), 54 | _ => Ok(None), 55 | }, 56 | |stmt| match stmt { 57 | Statement::Label(_) => Err(diag.err(CodeDiagnostic::IrInlineFlowControl)), 58 | _ => Ok(None), 59 | }, 60 | )?; 61 | 62 | Ok((builder.block(statements, body, body.span), local_defs)) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/alumina-boot/src/parser.rs: -------------------------------------------------------------------------------- 1 | use crate::ast::Span; 2 | use crate::common::{AluminaError, CodeDiagnostic, CodeError, FileId, Marker}; 3 | 4 | use once_cell::unsync::OnceCell; 5 | 6 | use std::iter::FusedIterator; 7 | use std::marker::PhantomData; 8 | 9 | include!(concat!(env!("OUT_DIR"), "/parser.rs")); 10 | 11 | pub struct ParseCtx<'src> { 12 | source: String, 13 | tree: OnceCell, 14 | file_id: FileId, 15 | _phantom: PhantomData<&'src ()>, 16 | } 17 | 18 | impl<'src> ParseCtx<'src> { 19 | pub fn from_source(file_id: FileId, source: String) -> Self { 20 | ParseCtx { 21 | source, 22 | tree: OnceCell::new(), 23 | file_id, 24 | _phantom: PhantomData, 25 | } 26 | } 27 | 28 | pub fn source(&'src self) -> &'src str { 29 | &self.source 30 | } 31 | 32 | pub fn file_id(&self) -> FileId { 33 | self.file_id 34 | } 35 | 36 | pub fn root_node(&'src self) -> tree_sitter::Node<'src> { 37 | match self.tree.get() { 38 | Some(tree) => tree.root_node(), 39 | None => { 40 | let mut parser = tree_sitter::Parser::new(); 41 | parser.set_language(&language()).unwrap(); 42 | 43 | self.tree 44 | .set(parser.parse(self.source.as_str(), None).unwrap()) 45 | .unwrap(); 46 | self.root_node() 47 | } 48 | } 49 | } 50 | 51 | pub fn check_syntax_errors( 52 | &'src self, 53 | node: tree_sitter::Node<'src>, 54 | ) -> Result<(), AluminaError> { 55 | let mut errors = Vec::new(); 56 | 57 | for node in traverse_pre(node.walk()) { 58 | if node.is_error() { 59 | errors.push(CodeError { 60 | kind: CodeDiagnostic::ParseError(self.node_text(node).to_string()), 61 | backtrace: vec![Marker::Span(Span::from_node(self.file_id, node))], 62 | }) 63 | } else if node.is_missing() { 64 | errors.push(CodeError { 65 | kind: CodeDiagnostic::ParseErrorMissing(node.kind().to_string()), 66 | backtrace: vec![Marker::Span(Span::from_node(self.file_id, node))], 67 | }) 68 | } 69 | } 70 | 71 | if errors.is_empty() { 72 | Ok(()) 73 | } else { 74 | Err(AluminaError::CodeErrors(errors)) 75 | } 76 | } 77 | 78 | pub fn node_text(&'src self, node: tree_sitter::Node<'src>) -> &'src str { 79 | &self.source[node.byte_range()] 80 | } 81 | } 82 | 83 | // The following code is adapted from tree-sitter-traversal by Sebastian Mendez 84 | // and carries the following license: 85 | // 86 | // MIT License 87 | // 88 | // Copyright (c) 2021 Sebastian Mendez 89 | // 90 | // Permission is hereby granted, free of charge, to any person obtaining a copy 91 | // of this software and associated documentation files (the "Software"), to deal 92 | // in the Software without restriction, including without limitation the rights 93 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 94 | // copies of the Software, and to permit persons to whom the Software is 95 | // furnished to do so, subject to the following conditions: 96 | // 97 | // The above copyright notice and this permission notice shall be included in all 98 | // copies or substantial portions of the Software. 99 | // 100 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 101 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 102 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 103 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 104 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 105 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 106 | // SOFTWARE. 107 | 108 | struct PreorderTraverse<'a> { 109 | cursor: Option>, 110 | } 111 | 112 | impl<'a> PreorderTraverse<'a> { 113 | pub fn new(c: tree_sitter::TreeCursor<'a>) -> Self { 114 | PreorderTraverse { cursor: Some(c) } 115 | } 116 | } 117 | 118 | impl<'a> Iterator for PreorderTraverse<'a> { 119 | type Item = tree_sitter::Node<'a>; 120 | 121 | fn next(&mut self) -> Option { 122 | let c = match self.cursor.as_mut() { 123 | None => { 124 | return None; 125 | } 126 | Some(c) => c, 127 | }; 128 | 129 | // We will always return the node we were on at the start; 130 | // the node we traverse to will either be returned on the next iteration, 131 | // or will be back to the root node, at which point we'll clear out 132 | // the reference to the cursor 133 | let node = c.node(); 134 | 135 | // First, try to go to a child or a sibling; if either succeed, this will be the 136 | // first time we touch that node, so it'll be the next starting node 137 | if c.goto_first_child() || c.goto_next_sibling() { 138 | return Some(node); 139 | } 140 | 141 | loop { 142 | // If we can't go to the parent, then that means we've reached the root, and our 143 | // iterator will be done in the next iteration 144 | if !c.goto_parent() { 145 | self.cursor = None; 146 | break; 147 | } 148 | 149 | // If we get to a sibling, then this will be the first time we touch that node, 150 | // so it'll be the next starting node 151 | if c.goto_next_sibling() { 152 | break; 153 | } 154 | } 155 | 156 | Some(node) 157 | } 158 | } 159 | 160 | impl FusedIterator for PreorderTraverse<'_> {} 161 | 162 | fn traverse_pre(mut cursor: tree_sitter::TreeCursor<'_>) -> PreorderTraverse<'_> { 163 | assert!(!cursor.goto_parent()); 164 | PreorderTraverse::new(cursor) 165 | } 166 | -------------------------------------------------------------------------------- /src/alumina-boot/src/src/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod pass1; 2 | pub mod path; 3 | pub mod resolver; 4 | pub mod scope; 5 | -------------------------------------------------------------------------------- /src/alumina-boot/src/src/path.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Debug, Display, Formatter}; 2 | 3 | use alumina_boot_macros::AstSerializable; 4 | 5 | #[derive(Debug, PartialEq, Eq, Hash, Clone, AstSerializable)] 6 | pub struct PathSegment<'ast>(pub &'ast str); 7 | 8 | impl Display for PathSegment<'_> { 9 | fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { 10 | write!(fmt, "{}", self.0) 11 | } 12 | } 13 | 14 | #[derive(Default, PartialEq, Eq, Hash, Clone, Debug, AstSerializable)] 15 | pub struct Path<'ast> { 16 | pub absolute: bool, 17 | pub segments: Vec>, 18 | } 19 | 20 | impl Display for Path<'_> { 21 | fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { 22 | if self.absolute { 23 | write!(fmt, "::")?; 24 | } 25 | for (i, seg) in self.segments.iter().enumerate() { 26 | if i > 0 { 27 | write!(fmt, "::")?; 28 | } 29 | write!(fmt, "{}", seg)?; 30 | } 31 | Ok(()) 32 | } 33 | } 34 | 35 | impl<'ast> From> for Path<'ast> { 36 | fn from(item: PathSegment<'ast>) -> Self { 37 | Self { 38 | absolute: false, 39 | segments: vec![item], 40 | } 41 | } 42 | } 43 | 44 | impl<'ast> Path<'ast> { 45 | pub fn root() -> Self { 46 | Self { 47 | absolute: true, 48 | segments: vec![], 49 | } 50 | } 51 | 52 | pub fn extend(&self, part: PathSegment<'ast>) -> Self { 53 | Self { 54 | absolute: self.absolute, 55 | segments: self 56 | .segments 57 | .iter() 58 | .cloned() 59 | .chain(std::iter::once(part)) 60 | .collect(), 61 | } 62 | } 63 | 64 | pub fn join_with(&self, lhs: Path<'ast>) -> Self { 65 | assert!(!lhs.absolute || self.absolute || self.segments.is_empty()); 66 | 67 | Self { 68 | absolute: lhs.absolute || self.absolute, 69 | segments: self.segments.iter().cloned().chain(lhs.segments).collect(), 70 | } 71 | } 72 | 73 | pub fn pop(&self) -> Self { 74 | Self { 75 | absolute: self.absolute, 76 | segments: self.segments[..self.segments.len() - 1].to_vec(), 77 | } 78 | } 79 | 80 | pub fn is_root(&self) -> bool { 81 | self.segments.is_empty() && self.absolute 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /sysroot/libc/mod.alu: -------------------------------------------------------------------------------- 1 | //! "FFI" interfaces to libc. 2 | //! 3 | //! Target-specific interfaces are in submodules of this module and they are conditionally `*`-imported, 4 | //! so e.g. `libc::pause()` can be used instead of `libc::x86_64_unknown_linux_gnu::pause()` and it will 5 | //! select the appropriate target. 6 | //! 7 | //! Mostly copied/adapted from [Rust's libc crate](https://github.com/rust-lang/libc). 8 | 9 | type c_void = void; 10 | 11 | // FFI bindings to the standard C math library. 12 | 13 | extern "C" fn acos(x: f64) -> f64; 14 | extern "C" fn acosf(x: f32) -> f32; 15 | extern "C" fn acosh(x: f64) -> f64; 16 | extern "C" fn acoshf(x: f32) -> f32; 17 | extern "C" fn asin(x: f64) -> f64; 18 | extern "C" fn asinf(x: f32) -> f32; 19 | extern "C" fn asinh(x: f64) -> f64; 20 | extern "C" fn asinhf(x: f32) -> f32; 21 | extern "C" fn atan(x: f64) -> f64; 22 | extern "C" fn atan2(y: f64, x: f64) -> f64; 23 | extern "C" fn atan2f(y: f32, x: f32) -> f32; 24 | extern "C" fn atanf(x: f32) -> f32; 25 | extern "C" fn atanh(x: f64) -> f64; 26 | extern "C" fn atanhf(x: f32) -> f32; 27 | extern "C" fn cbrt(x: f64) -> f64; 28 | extern "C" fn cbrtf(x: f32) -> f32; 29 | extern "C" fn ceil(x: f64) -> f64; 30 | extern "C" fn ceilf(x: f32) -> f32; 31 | extern "C" fn copysign(x: f64, y: f64) -> f64; 32 | extern "C" fn copysignf(x: f32, y: f32) -> f32; 33 | extern "C" fn cos(x: f64) -> f64; 34 | extern "C" fn cosf(x: f32) -> f32; 35 | extern "C" fn cosh(x: f64) -> f64; 36 | extern "C" fn coshf(x: f32) -> f32; 37 | extern "C" fn erf(x: f64) -> f64; 38 | extern "C" fn erfc(x: f64) -> f64; 39 | extern "C" fn erff(x: f32) -> f32; 40 | extern "C" fn erfcf(x: f32) -> f32; 41 | extern "C" fn exp(x: f64) -> f64; 42 | extern "C" fn expf(x: f32) -> f32; 43 | extern "C" fn expm1(x: f64) -> f64; 44 | extern "C" fn expm1f(x: f32) -> f32; 45 | extern "C" fn fabs(x: f64) -> f64; 46 | extern "C" fn fabsf(x: f32) -> f32; 47 | extern "C" fn fdim(x: f64, y: f64) -> f64; 48 | extern "C" fn fdimf(x: f32, y: f32) -> f32; 49 | extern "C" fn floor(x: f64) -> f64; 50 | extern "C" fn floorf(x: f32) -> f32; 51 | extern "C" fn fma(x: f64, y: f64, z: f64) -> f64; 52 | extern "C" fn fmaf(x: f32, y: f32, z: f32) -> f32; 53 | extern "C" fn fmax(x: f64, y: f64) -> f64; 54 | extern "C" fn fmaxf(x: f32, y: f32) -> f32; 55 | extern "C" fn fmin(x: f64, y: f64) -> f64; 56 | extern "C" fn fminf(x: f32, y: f32) -> f32; 57 | extern "C" fn fmod(x: f64, y: f64) -> f64; 58 | extern "C" fn fmodf(x: f32, y: f32) -> f32; 59 | extern "C" fn frexp(x: f64, exp: &mut i32) -> f64; 60 | extern "C" fn frexpf(x: f32, exp: &mut i32) -> f32; 61 | extern "C" fn hypot(x: f64, y: f64) -> f64; 62 | extern "C" fn hypotf(x: f32, y: f32) -> f32; 63 | extern "C" fn ilogb(x: f64) -> i32; 64 | extern "C" fn ilogbf(x: f32) -> i32; 65 | extern "C" fn ldexp(x: f64, n: i32) -> f64; 66 | extern "C" fn ldexpf(x: f32, n: i32) -> f32; 67 | extern "C" fn lgamma(x: f64) -> f64; 68 | extern "C" fn lgammaf(x: f32) -> f32; 69 | extern "C" fn log(x: f64) -> f64; 70 | extern "C" fn log10(x: f64) -> f64; 71 | extern "C" fn log10f(x: f32) -> f32; 72 | extern "C" fn log1p(x: f64) -> f64; 73 | extern "C" fn log1pf(x: f32) -> f32; 74 | extern "C" fn log2(x: f64) -> f64; 75 | extern "C" fn log2f(x: f32) -> f32; 76 | extern "C" fn logf(x: f32) -> f32; 77 | extern "C" fn modf(x: f64, iptr: &mut f64) -> f64; 78 | extern "C" fn modff(x: f32, iptr: &mut f32) -> f32; 79 | extern "C" fn nextafter(x: f64, y: f64) -> f64; 80 | extern "C" fn nextafterf(x: f32, y: f32) -> f32; 81 | extern "C" fn pow(x: f64, y: f64) -> f64; 82 | extern "C" fn powf(x: f32, y: f32) -> f32; 83 | extern "C" fn remainder(x: f64, y: f64) -> f64; 84 | extern "C" fn remainderf(x: f32, y: f32) -> f32; 85 | extern "C" fn remquo(x: f64, y: f64, quo: &mut i32) -> f64; 86 | extern "C" fn remquof(x: f32, y: f32, quo: &mut i32) -> f32; 87 | extern "C" fn round(x: f64) -> f64; 88 | extern "C" fn roundf(x: f32) -> f32; 89 | extern "C" fn scalbn(x: f64, n: i32) -> f64; 90 | extern "C" fn scalbnf(x: f32, n: i32) -> f32; 91 | extern "C" fn sin(x: f64) -> f64; 92 | extern "C" fn sinf(x: f32) -> f32; 93 | extern "C" fn sinh(x: f64) -> f64; 94 | extern "C" fn sinhf(x: f32) -> f32; 95 | extern "C" fn sqrt(x: f64) -> f64; 96 | extern "C" fn sqrtf(x: f32) -> f32; 97 | extern "C" fn tan(x: f64) -> f64; 98 | extern "C" fn tanf(x: f32) -> f32; 99 | extern "C" fn tanh(x: f64) -> f64; 100 | extern "C" fn tanhf(x: f32) -> f32; 101 | extern "C" fn tgamma(x: f64) -> f64; 102 | extern "C" fn tgammaf(x: f32) -> f32; 103 | extern "C" fn trunc(x: f64) -> f64; 104 | extern "C" fn truncf(x: f32) -> f32; 105 | 106 | #[cfg(all(target_arch = "aarch64", target_os = "macos"))] 107 | use libc::aarch64_apple_darwin::*; 108 | #[cfg(all(target_arch = "x86_64", target_os = "macos"))] 109 | use libc::x86_64_apple_darwin::*; 110 | #[cfg(all(target_arch = "aarch64", target_os = "android"))] 111 | use libc::aarch64_linux_android::*; 112 | 113 | #[cfg(target_env = "musl")] 114 | { 115 | #[cfg(all(target_arch = "aarch64", target_os = "linux"))] 116 | use libc::aarch64_unknown_linux_musl::*; 117 | #[cfg(all(target_arch = "x86_64", target_os = "linux"))] 118 | use libc::x86_64_unknown_linux_musl::*; 119 | } 120 | #[cfg(target_env = "gnu")] 121 | { 122 | #[cfg(all(target_arch = "aarch64", target_os = "linux"))] 123 | use libc::aarch64_unknown_linux_gnu::*; 124 | #[cfg(all(target_arch = "x86_64", target_os = "linux"))] 125 | use libc::x86_64_unknown_linux_gnu::*; 126 | } 127 | -------------------------------------------------------------------------------- /sysroot/mod.alu: -------------------------------------------------------------------------------- 1 | //! #! Alumina programming language 2 | //! 3 | //! Welcome to the Alumina programming language! Alumina is an imperative, general-purpose, 4 | //! statically typed, compiled system programming language. It is heavily inspired by Rust, but 5 | //! it keeps C-style manual memory management and memory unsafety. 6 | //! 7 | //! ```bare 8 | //! fn main() { 9 | //! println!("Hello, world!"); 10 | //! } 11 | //! ``` 12 | //! 13 | //! See [Alumina GitHub page](https://github.com/alumina-lang/alumina), the 14 | //! [language guide](https://github.com/alumina-lang/alumina/blob/master/docs/lang_guide.md) and 15 | //! [the online compiler playground](https://play.alumina-lang.net) to get started with Alumina. 16 | //! 17 | //! # Standard library structure 18 | //! This is the root module (`::`) of the standard library, all the code contained in its submodules 19 | //! modules is always available to programs and libraries written in Alumina. It consists of 20 | //! [std], which is the bulk of the library and a couple of auxillary modules, such as [libc] for 21 | //! bindings to the C standard library and [test] for the built-in unit test runner. 22 | //! 23 | //! # About the documentation 24 | //! The documentation is automatically generated from the comments in the source code using 25 | //! the [alumina-doc tool](https://github.com/alumina-lang/alumina/tree/master/tools/alumina-doc). 26 | //! 27 | //! Contributions to the documentation are most welcome! Fork 28 | //! [the repository](https://github.com/alumina-lang/alumina) and submit a pull request. CI will 29 | //! ensure that the documentation is built and all examples compile (and optionally run) successfully. 30 | 31 | #[cfg(not(no_prelude))] 32 | use std::prelude::*; 33 | -------------------------------------------------------------------------------- /sysroot/std/collections/mod.alu: -------------------------------------------------------------------------------- 1 | //! Heap-allocating collections. 2 | 3 | use vector::Vector; 4 | use hashmap::HashMap; 5 | use hashset::HashSet; 6 | use deque::Deque; 7 | use heap::BinaryHeap; 8 | 9 | 10 | #[cfg(all(test_std, test))] 11 | #[docs(hide)] 12 | mod tests { 13 | 14 | #[test] 15 | fn test_free_all() { 16 | struct Free { counter: &mut usize } 17 | impl Free { fn free(self: &mut Free) { *self.counter += 1; } } 18 | 19 | let free_count = 0usize; 20 | let f: Vector = Vector::new(); 21 | 22 | for _ in 0..42 { 23 | f.push(Free { counter: &free_count }); 24 | } 25 | f.free_all(); 26 | 27 | assert_eq!(free_count, 42); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /sysroot/std/ffi.alu: -------------------------------------------------------------------------------- 1 | //! Utilities related to FFI bindings. 2 | 3 | /// Compile-time null-terminated string. 4 | /// 5 | /// ## Example 6 | /// ``` 7 | /// use std::ffi::c_str; 8 | /// let f = c_str!("Hello, world!"); 9 | /// 10 | /// libc::puts(f); 11 | /// ``` 12 | macro c_str($s) { 13 | // This concat is technically redundant since C codegen already emits 14 | // those as C strings, but it doesn't hurt to be explicit if this is 15 | // ever changed. Also concat has a nice side-effect of guarding against 16 | // strings that are not a compile-time constant. 17 | concat!($s, "\0")._ptr as &libc::c_char 18 | } 19 | 20 | /// A wrapper for a null-terminated (C) string. 21 | /// 22 | /// Allocates memory for the string on the heap, so it must be freed. 23 | /// 24 | /// ## Example 25 | /// ``` 26 | /// use std::ffi::CString; 27 | /// let s = CString::from_slice("Hello, world!"); 28 | /// defer s.free(); 29 | /// 30 | /// libc::puts(s.ptr); 31 | /// ``` 32 | #[transparent] 33 | struct CString { 34 | ptr: &libc::c_char, 35 | } 36 | 37 | impl CString { 38 | /// Create a new CString from a string slice 39 | /// 40 | /// Allocates memory. 41 | fn from_slice(s: &[u8]) -> CString { 42 | use mem::slice; 43 | 44 | let ret = slice::alloc::(s.len() + 1); 45 | s.copy_to_nonoverlapping(&ret[0]); 46 | ret[s.len()] = '\0'; 47 | 48 | CString { ptr: &ret[0] as &libc::c_char } 49 | } 50 | 51 | /// Create a CString from a raw pointer to a null-terminated string. 52 | fn from_raw(ptr: &libc::c_char) -> CString { 53 | CString { ptr: ptr } 54 | } 55 | 56 | /// A null C string. 57 | fn null() -> CString { 58 | CString { ptr: null } 59 | } 60 | 61 | /// Returns the length of the string. 62 | fn len(self: &CString) -> usize { 63 | util::cast(libc::strlen(self.ptr)) 64 | } 65 | 66 | /// @ mem::AsSlice::as_slice 67 | #[allow(unnecessary_cast)] 68 | fn as_slice(self: &CString) -> &[u8] { 69 | mem::slice::from_raw(self.ptr as &u8, self.len()) 70 | } 71 | 72 | /// @ mem::Freeable::free 73 | fn free(self: &mut CString) { 74 | libc::free(self.ptr as &mut void); 75 | } 76 | 77 | /// @ mem::Clonable::clone 78 | fn clone(self: &CString) -> CString { 79 | from_slice(self[..]) 80 | } 81 | 82 | /// @ mem::Movable::move 83 | fn move(self: &mut CString) -> CString { 84 | let ret = *self; 85 | self.ptr = null; 86 | ret 87 | } 88 | 89 | /// @ fmt::Formattable::fmt 90 | fn fmt>(self: &CString, f: &mut F) -> fmt::Result { 91 | f.write_str(self.as_slice()) 92 | } 93 | } 94 | 95 | 96 | #[cfg(all(test, test_std))] 97 | #[docs(hide)] 98 | mod tests { 99 | #[test] 100 | fn test_cstring() { 101 | let s = CString::from_raw(c_str!("Hello, World")); 102 | 103 | assert_eq!(s[..], "Hello, World"); 104 | let f = fmt::format!("{}", s).unwrap(); 105 | defer f.free(); 106 | 107 | assert_eq!(f[..] as &[u8], "Hello, World"); 108 | } 109 | 110 | #[test] 111 | fn test_cstring_clone() { 112 | let s1 = CString::from_raw(c_str!("Hello, World")); 113 | let s2 = s1.clone(); 114 | defer s2.free(); 115 | 116 | *(s2.ptr as &mut u8) = 'Y'; 117 | 118 | assert_eq!(s1[..], "Hello, World"); 119 | assert_eq!(s2[..], "Yello, World"); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /sysroot/std/fmt/ryu/common.alu: -------------------------------------------------------------------------------- 1 | // Translated from Rust to Alumina. The Rust port can be found at https://github.com/dtolnay/ryu 2 | // The original C code can be found at https://github.com/ulfjack/ryu and carries the following license: 3 | // 4 | // Copyright 2018 Ulf Adams 5 | // 6 | // The contents of this file may be used under the terms of the Apache License, 7 | // Version 2.0. 8 | // 9 | // (See accompanying file LICENSE-Apache or copy at 10 | // http://www.apache.org/licenses/LICENSE-2.0) 11 | // 12 | // Alternatively, the contents of this file may be used under the terms of 13 | // the Boost Software License, Version 1.0. 14 | // (See accompanying file LICENSE-Boost or copy at 15 | // https://www.boost.org/LICENSE_1_0.txt) 16 | // 17 | // Unless required by applicable law or agreed to in writing, this software 18 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 19 | // KIND, either express or implied. 20 | 21 | // Returns the number of decimal digits in v, which must not contain more than 9 22 | // digits. 23 | #[inline] 24 | fn decimal_length9(v: u32) -> u32 { 25 | // Function precondition: v is not a 10-digit number. 26 | // (f2s: 9 digits are sufficient for round-tripping.) 27 | debug_assert!(v < 1000000000); 28 | 29 | if v >= 100000000 { 30 | 9 31 | } else if v >= 10000000 { 32 | 8 33 | } else if v >= 1000000 { 34 | 7 35 | } else if v >= 100000 { 36 | 6 37 | } else if v >= 10000 { 38 | 5 39 | } else if v >= 1000 { 40 | 4 41 | } else if v >= 100 { 42 | 3 43 | } else if v >= 10 { 44 | 2 45 | } else { 46 | 1 47 | } 48 | } 49 | 50 | // Returns e == 0 ? 1 : [log_2(5^e)]; requires 0 <= e <= 3528. 51 | #[inline] 52 | fn log2_pow5(e: i32) -> i32 /* or u32 -> u32 */ { 53 | // This approximation works up to the point that the multiplication 54 | // overflows at e = 3529. If the multiplication were done in 64 bits, it 55 | // would fail at 5^4004 which is just greater than 2^9297. 56 | debug_assert!(e >= 0); 57 | debug_assert!(e <= 3528); 58 | ((e as u32 * 1217359) >> 19) as i32 59 | } 60 | 61 | // Returns e == 0 ? 1 : ceil(log_2(5^e)); requires 0 <= e <= 3528. 62 | #[inline] 63 | fn pow5bits(e: i32) -> i32 /* or u32 -> u32 */ { 64 | // This approximation works up to the point that the multiplication 65 | // overflows at e = 3529. If the multiplication were done in 64 bits, it 66 | // would fail at 5^4004 which is just greater than 2^9297. 67 | debug_assert!(e >= 0); 68 | debug_assert!(e <= 3528); 69 | (((e as u32 * 1217359) >> 19) + 1) as i32 70 | } 71 | 72 | #[inline] 73 | fn ceil_log2_pow5(e: i32) -> i32 /* or u32 -> u32 */ { 74 | log2_pow5(e) + 1 75 | } 76 | 77 | // Returns floor(log_10(2^e)); requires 0 <= e <= 1650. 78 | #[inline] 79 | fn log10_pow2(e: i32) -> u32 /* or u32 -> u32 */ { 80 | // The first value this approximation fails for is 2^1651 which is just greater than 10^297. 81 | debug_assert!(e >= 0); 82 | debug_assert!(e <= 1650); 83 | (e as u32 * 78913) >> 18 84 | } 85 | 86 | // Returns floor(log_10(5^e)); requires 0 <= e <= 2620. 87 | #[inline] 88 | fn log10_pow5(e: i32) -> u32 /* or u32 -> u32 */ { 89 | // The first value this approximation fails for is 5^2621 which is just greater than 10^1832. 90 | debug_assert!(e >= 0); 91 | debug_assert!(e <= 2620); 92 | (e as u32 * 732923) >> 20 93 | } 94 | 95 | 96 | #[cfg(all(test_std, test))] 97 | #[docs(hide)] 98 | mod tests { 99 | #[test] 100 | fn test_decimal_length9() { 101 | assert_eq!(decimal_length9(0), 1); 102 | assert_eq!(decimal_length9(1), 1); 103 | assert_eq!(decimal_length9(9), 1); 104 | assert_eq!(decimal_length9(10), 2); 105 | assert_eq!(decimal_length9(99), 2); 106 | assert_eq!(decimal_length9(100), 3); 107 | assert_eq!(decimal_length9(999), 3); 108 | assert_eq!(decimal_length9(999999999), 9); 109 | } 110 | 111 | #[test] 112 | fn test_ceil_log2_pow5() { 113 | assert_eq!(ceil_log2_pow5(0), 1); 114 | assert_eq!(ceil_log2_pow5(1), 3); 115 | assert_eq!(ceil_log2_pow5(2), 5); 116 | assert_eq!(ceil_log2_pow5(3), 7); 117 | assert_eq!(ceil_log2_pow5(4), 10); 118 | assert_eq!(ceil_log2_pow5(3528), 8192); 119 | } 120 | 121 | #[test] 122 | fn test_log10_pow2() { 123 | assert_eq!(log10_pow2(0), 0); 124 | assert_eq!(log10_pow2(1), 0); 125 | assert_eq!(log10_pow2(2), 0); 126 | assert_eq!(log10_pow2(3), 0); 127 | assert_eq!(log10_pow2(4), 1); 128 | assert_eq!(log10_pow2(1650), 496); 129 | } 130 | 131 | #[test] 132 | fn test_log10_pow5() { 133 | assert_eq!(log10_pow5(0), 0); 134 | assert_eq!(log10_pow5(1), 0); 135 | assert_eq!(log10_pow5(2), 1); 136 | assert_eq!(log10_pow5(3), 2); 137 | assert_eq!(log10_pow5(4), 2); 138 | assert_eq!(log10_pow5(2620), 1831); 139 | } 140 | 141 | #[test] 142 | fn test_float_to_bits() { 143 | assert_eq!(0.0f32.to_bits(), 0); 144 | assert_eq!(3.1415926f32.to_bits(), 0x40490fda); 145 | } 146 | 147 | #[test] 148 | fn test_double_to_bits() { 149 | assert_eq!(0.0f64.to_bits(), 0); 150 | assert_eq!( 151 | 3.1415926535897932384626433f64.to_bits(), 152 | 0x400921FB54442D18u64, 153 | ); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /sysroot/std/fmt/ryu/d2s_intrinsics.alu: -------------------------------------------------------------------------------- 1 | // Translated from Rust to Alumina. The Rust port can be found at https://github.com/dtolnay/ryu 2 | // The original C code can be found at https://github.com/ulfjack/ryu and carries the following license: 3 | // 4 | // Copyright 2018 Ulf Adams 5 | // 6 | // The contents of this file may be used under the terms of the Apache License, 7 | // Version 2.0. 8 | // 9 | // (See accompanying file LICENSE-Apache or copy at 10 | // http://www.apache.org/licenses/LICENSE-2.0) 11 | // 12 | // Alternatively, the contents of this file may be used under the terms of 13 | // the Boost Software License, Version 1.0. 14 | // (See accompanying file LICENSE-Boost or copy at 15 | // https://www.boost.org/LICENSE_1_0.txt) 16 | // 17 | // Unless required by applicable law or agreed to in writing, this software 18 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 19 | // KIND, either express or implied. 20 | 21 | #[inline] 22 | fn div5(x: u64) -> u64 { 23 | x / 5 24 | } 25 | 26 | #[inline] 27 | fn div10(x: u64) -> u64 { 28 | x / 10 29 | } 30 | 31 | #[inline] 32 | fn div100(x: u64) -> u64 { 33 | x / 100 34 | } 35 | 36 | #[inline] 37 | fn pow5_factor(value: u64) -> u32 { 38 | let count = 0u32; 39 | loop { 40 | debug_assert!(value != 0); 41 | let q = div5(value); 42 | let r = (value as u32).wrapping_sub(5u32.wrapping_mul(q as u32)); 43 | if r != 0 { 44 | break; 45 | } 46 | value = q; 47 | count += 1; 48 | } 49 | count 50 | } 51 | 52 | // Returns `true` if value is divisible by 5^p. 53 | #[inline] 54 | fn multiple_of_power_of_5(value: u64, p: u32) -> bool { 55 | // I tried a case distinction on p, but there was no performance difference. 56 | pow5_factor(value) >= p 57 | } 58 | 59 | // Returns `true` if value is divisible by 2^p. 60 | #[inline] 61 | fn multiple_of_power_of_2(value: u64, p: u32) -> bool { 62 | debug_assert!(value != 0); 63 | debug_assert!(p < 64); 64 | // __builtin_ctzll doesn't appear to be faster here. 65 | (value & ((1u64 << p) - 1)) == 0 66 | } 67 | 68 | #[inline] 69 | fn mul_shift_64(m: u64, mul: &(u64, u64), j: u32) -> u64 { 70 | let b0 = m as u128 * mul.0 as u128; 71 | let b2 = m as u128 * mul.1 as u128; 72 | (((b0 >> 64) + b2) >> (j - 64)) as u64 73 | } 74 | 75 | #[inline] 76 | fn mul_shift_all_64( 77 | m: u64, 78 | mul: &(u64, u64), 79 | j: u32, 80 | vp: &mut u64, 81 | vm: &mut u64, 82 | mm_shift: u32, 83 | ) -> u64 { 84 | *vp = mul_shift_64(4 * m + 2, mul, j); 85 | *vm = mul_shift_64(4 * m - 1 - mm_shift as u64, mul, j); 86 | mul_shift_64(4 * m, mul, j) 87 | } 88 | -------------------------------------------------------------------------------- /sysroot/std/fmt/ryu/digit_table.alu: -------------------------------------------------------------------------------- 1 | // Translated from Rust to Alumina. The Rust port can be found at https://github.com/dtolnay/ryu 2 | // The original C code can be found at https://github.com/ulfjack/ryu and carries the following license: 3 | // 4 | // Copyright 2018 Ulf Adams 5 | // 6 | // The contents of this file may be used under the terms of the Apache License, 7 | // Version 2.0. 8 | // 9 | // (See accompanying file LICENSE-Apache or copy at 10 | // http://www.apache.org/licenses/LICENSE-2.0) 11 | // 12 | // Alternatively, the contents of this file may be used under the terms of 13 | // the Boost Software License, Version 1.0. 14 | // (See accompanying file LICENSE-Boost or copy at 15 | // https://www.boost.org/LICENSE_1_0.txt) 16 | // 17 | // Unless required by applicable law or agreed to in writing, this software 18 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 19 | // KIND, either express or implied. 20 | 21 | // A table of all two-digit numbers. This is used to speed up decimal digit 22 | // generation by copying pairs of digits into the final output. 23 | const DIGIT_TABLE: &[u8] = concat!( 24 | "0001020304050607080910111213141516171819", 25 | "2021222324252627282930313233343536373839", 26 | "4041424344454647484950515253545556575859", 27 | "6061626364656667686970717273747576777879", 28 | "8081828384858687888990919293949596979899" 29 | ); 30 | -------------------------------------------------------------------------------- /sysroot/std/fmt/ryu/f2s_intrinsics.alu: -------------------------------------------------------------------------------- 1 | // Translated from Rust to Alumina. The Rust port can be found at https://github.com/dtolnay/ryu 2 | // The original C code can be found at https://github.com/ulfjack/ryu and carries the following license: 3 | // 4 | // Copyright 2018 Ulf Adams 5 | // 6 | // The contents of this file may be used under the terms of the Apache License, 7 | // Version 2.0. 8 | // 9 | // (See accompanying file LICENSE-Apache or copy at 10 | // http://www.apache.org/licenses/LICENSE-2.0) 11 | // 12 | // Alternatively, the contents of this file may be used under the terms of 13 | // the Boost Software License, Version 1.0. 14 | // (See accompanying file LICENSE-Boost or copy at 15 | // https://www.boost.org/LICENSE_1_0.txt) 16 | // 17 | // Unless required by applicable law or agreed to in writing, this software 18 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 19 | // KIND, either express or implied. 20 | 21 | const FLOAT_POW5_INV_BITCOUNT: i32 = d2s::DOUBLE_POW5_INV_BITCOUNT - 64; 22 | const FLOAT_POW5_BITCOUNT: i32 = d2s::DOUBLE_POW5_BITCOUNT - 64; 23 | 24 | #[inline] 25 | fn pow5factor_32(value: u32) -> u32 { 26 | let count = 0u32; 27 | loop { 28 | debug_assert!(value != 0); 29 | let q = value / 5; 30 | let r = value % 5; 31 | if r != 0 { 32 | break; 33 | } 34 | value = q; 35 | count += 1; 36 | } 37 | count 38 | } 39 | 40 | // Returns `true` if value is divisible by 5^p. 41 | #[inline] 42 | fn multiple_of_power_of_5_32(value: u32, p: u32) -> bool { 43 | pow5factor_32(value) >= p 44 | } 45 | 46 | // Returns `true` if value is divisible by 2^p. 47 | #[inline] 48 | fn multiple_of_power_of_2_32(value: u32, p: u32) -> bool { 49 | // __builtin_ctz doesn't appear to be faster here. 50 | (value & ((1u32 << p) - 1)) == 0 51 | } 52 | 53 | // It seems to be slightly faster to avoid uint128_t here, although the 54 | // generated code for uint128_t looks slightly nicer. 55 | #[inline] 56 | fn mul_shift_32(m: u32, factor: u64, shift: i32) -> u32 { 57 | debug_assert!(shift > 32); 58 | 59 | // The casts here help MSVC to avoid calls to the __allmul library 60 | // function. 61 | let factor_lo = factor as u32; 62 | let factor_hi = (factor >> 32) as u32; 63 | let bits0 = m as u64 * factor_lo as u64; 64 | let bits1 = m as u64 * factor_hi as u64; 65 | 66 | let sum = (bits0 >> 32) + bits1; 67 | let shifted_sum = sum >> (shift - 32); 68 | debug_assert!(shifted_sum <= u32::max_value() as u64); 69 | shifted_sum as u32 70 | } 71 | 72 | #[inline] 73 | fn mul_pow5_inv_div_pow2(m: u32, q: u32, j: i32) -> u32 { 74 | debug_assert!(q < d2s::DOUBLE_POW5_INV_SPLIT.len() as u32); 75 | mul_shift_32( 76 | m, 77 | d2s::DOUBLE_POW5_INV_SPLIT[q as usize].1 + 1, 78 | j, 79 | ) 80 | } 81 | 82 | #[inline] 83 | fn mul_pow5_div_pow2(m: u32, i: u32, j: i32) -> u32 { 84 | debug_assert!(i < d2s::DOUBLE_POW5_SPLIT.len() as u32); 85 | mul_shift_32(m, d2s::DOUBLE_POW5_SPLIT[i as usize].1, j) 86 | } 87 | -------------------------------------------------------------------------------- /sysroot/std/fmt/ryu/mod.alu: -------------------------------------------------------------------------------- 1 | #![docs(no_index)] 2 | 3 | // Translated from Rust to Alumina. The Rust port can be found at https://github.com/dtolnay/ryu 4 | // The original C code can be found at https://github.com/ulfjack/ryu and carries the following license: 5 | // 6 | // Copyright 2018 Ulf Adams 7 | // 8 | // The contents of this file may be used under the terms of the Apache License, 9 | // Version 2.0. 10 | // 11 | // (See accompanying file LICENSE-Apache or copy at 12 | // http://www.apache.org/licenses/LICENSE-2.0) 13 | // 14 | // Alternatively, the contents of this file may be used under the terms of 15 | // the Boost Software License, Version 1.0. 16 | // (See accompanying file LICENSE-Boost or copy at 17 | // https://www.boost.org/LICENSE_1_0.txt) 18 | // 19 | // Unless required by applicable law or agreed to in writing, this software 20 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 21 | // KIND, either express or implied. 22 | 23 | //! Alumina port of Ryū algorithm for formatting floating-point numbers. 24 | //! 25 | //! Includes the float and double parsing functions. 26 | //! 27 | //! Translated from Rust to Alumina The Rust port can be found at [https://github.com/dtolnay/ryu](https://github.com/dtolnay/ryu). 28 | //! Rust port is in turn based on the reference C implementation, which can be found at 29 | //! [https://github.com/ulfjack/ryu](https://github.com/ulfjack/ryu) 30 | 31 | use pretty::{format32, format64}; 32 | use s2d::parse64; 33 | use s2f::parse32; 34 | 35 | /// Error during parsing 36 | enum Error { 37 | InputTooShort, 38 | InputTooLong, 39 | MalformedInput, 40 | } 41 | 42 | impl Error { 43 | /// @ cmp::Equatable::equals 44 | fn equals(self: &Error, other: &Error) -> bool { 45 | *self == *other 46 | } 47 | 48 | /// @ fmt::Formattable::fmt 49 | fn fmt>(self: &Error, f: &mut F) -> fmt::Result { 50 | switch *self { 51 | Error::InputTooShort => f.write_str("input too short"), 52 | Error::InputTooLong => f.write_str("input too long"), 53 | Error::MalformedInput => f.write_str("malformed input"), 54 | _ => unreachable!() 55 | } 56 | } 57 | 58 | mixin cmp::Equatable; 59 | } 60 | -------------------------------------------------------------------------------- /sysroot/std/fmt/ryu/pretty/exponent.alu: -------------------------------------------------------------------------------- 1 | use digit_table::*; 2 | 3 | #[inline] 4 | fn write_exponent3(k: isize, result: &mut u8) -> usize { 5 | let sign = k < 0; 6 | if sign { 7 | *result = '-'; 8 | result = result + 1; 9 | k = -k; 10 | } 11 | 12 | debug_assert!(k < 1000); 13 | if k >= 100 { 14 | *result = '0' + (k / 100) as u8; 15 | k %= 100; 16 | let d = DIGIT_TABLE.as_ptr() + (k * 2); 17 | mem::copy_nonoverlapping(d, result + 1, 2); 18 | sign as usize + 3 19 | } else if k >= 10 { 20 | let d = DIGIT_TABLE.as_ptr() + (k * 2); 21 | mem::copy_nonoverlapping(d, result, 2); 22 | sign as usize + 2 23 | } else { 24 | *result = '0' + k as u8; 25 | sign as usize + 1 26 | } 27 | } 28 | 29 | #[inline] 30 | fn write_exponent2(k: isize, result: &mut u8) -> usize { 31 | let sign = k < 0; 32 | if sign { 33 | *result = '-'; 34 | result = result + 1; 35 | k = -k; 36 | } 37 | 38 | debug_assert!(k < 100); 39 | if k >= 10 { 40 | let d = DIGIT_TABLE.as_ptr() + (k * 2); 41 | mem::copy_nonoverlapping(d, result, 2); 42 | sign as usize + 2 43 | } else { 44 | *result = '0' + k as u8; 45 | sign as usize + 1 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /sysroot/std/fmt/ryu/pretty/mantissa.alu: -------------------------------------------------------------------------------- 1 | use digit_table::*; 2 | 3 | #[inline] 4 | fn write_mantissa_long(output: u64, result: &mut u8) { 5 | if (output >> 32) != 0 { 6 | // One expensive 64-bit division. 7 | let output2 = (output - 100000000 * (output / 100000000)) as u32; 8 | output /= 100000000; 9 | 10 | let c = output2 % 10000; 11 | output2 /= 10000; 12 | let d = output2 % 10000; 13 | let c0 = (c % 100) << 1; 14 | let c1 = (c / 100) << 1; 15 | let d0 = (d % 100) << 1; 16 | let d1 = (d / 100) << 1; 17 | 18 | mem::copy_nonoverlapping( 19 | DIGIT_TABLE.as_ptr() + (c0 as isize), 20 | result - 2, 21 | 2, 22 | ); 23 | mem::copy_nonoverlapping( 24 | DIGIT_TABLE.as_ptr() + (c1 as isize), 25 | result - 4, 26 | 2, 27 | ); 28 | mem::copy_nonoverlapping( 29 | DIGIT_TABLE.as_ptr() + (d0 as isize), 30 | result - 6, 31 | 2, 32 | ); 33 | mem::copy_nonoverlapping( 34 | DIGIT_TABLE.as_ptr() + (d1 as isize), 35 | result - 8, 36 | 2, 37 | ); 38 | result = result - 8; 39 | } 40 | write_mantissa(output as u32, result); 41 | } 42 | 43 | #[inline] 44 | fn write_mantissa(output: u32, result: &mut u8) { 45 | while output >= 10000 { 46 | let c = output - 10000 * (output / 10000); 47 | output /= 10000; 48 | let c0 = (c % 100) << 1; 49 | let c1 = (c / 100) << 1; 50 | mem::copy_nonoverlapping( 51 | DIGIT_TABLE.as_ptr() + (c0 as isize), 52 | result - 2, 53 | 2, 54 | ); 55 | mem::copy_nonoverlapping( 56 | DIGIT_TABLE.as_ptr() + (c1 as isize), 57 | result - 4, 58 | 2, 59 | ); 60 | result = result - 4; 61 | } 62 | if output >= 100 { 63 | let c = (output % 100) << 1; 64 | output /= 100; 65 | mem::copy_nonoverlapping( 66 | DIGIT_TABLE.as_ptr() + (c as isize), 67 | result - 2, 68 | 2, 69 | ); 70 | result = result - 2; 71 | } 72 | if output >= 10 { 73 | let c = output << 1; 74 | mem::copy_nonoverlapping( 75 | DIGIT_TABLE.as_ptr() + (c as isize), 76 | result - 2, 77 | 2, 78 | ); 79 | } else { 80 | *(result - 1) = '0' + output as u8; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /sysroot/std/macros.alu: -------------------------------------------------------------------------------- 1 | //! Utilities for advanced macro usage 2 | 3 | /// Create a macro "closure" 4 | /// 5 | /// This built-in macro takes another macro and an arbitrarty number or arguments 6 | /// as parameters and returns a reference to a macro that has these arguments 7 | /// appended on every invocation. 8 | /// 9 | /// ## Example 10 | /// ``` 11 | /// use std::macros::bind; 12 | /// 13 | /// macro print_prefixed($prefix, $arg) { 14 | /// println!("{}{}", $prefix, $arg); 15 | /// } 16 | /// 17 | /// macro foreach($f, $arg...) { 18 | /// $f!($arg)$...; 19 | /// } 20 | /// 21 | /// // prefix1 22 | /// // prefix2 23 | /// // prefix3 24 | /// foreach!( 25 | /// bind!(print_prefixed, "prefix"), 26 | /// 1, 27 | /// 2, 28 | /// 3 29 | /// ); 30 | /// ``` 31 | #[builtin] macro bind($mac, $arg...) {} 32 | 33 | /// Perform a left-associative fold over the arguments 34 | /// 35 | /// `$mac` is a reference to a macro, `$base` is a mandatory base case followed 36 | /// by a variable number of extra arguments to fold. 37 | /// 38 | /// ```pseudo_alumina 39 | /// reduce!(m, a1, a2, b3, a4, a5) 40 | /// // Expands to 41 | /// m!(m!(m!(m!(a1, a2), a3), a4), a5) 42 | /// ``` 43 | /// 44 | /// ## Example 45 | /// ``` 46 | /// use std::macros::reduce; 47 | /// 48 | /// macro plus($a, $b) { 49 | /// $a + $b 50 | /// } 51 | /// 52 | /// assert_eq!(reduce!(plus, 0, 1, 2, 3, 4, 5), 15); 53 | /// ``` 54 | #[builtin] macro reduce($mac, $base, $arg...) {} 55 | 56 | 57 | /// Count the number of arguments 58 | /// 59 | /// This macro counts the number of arguments passed to it. The result is a 60 | /// [usize] constant expression. 61 | /// 62 | /// ## Example 63 | /// ``` 64 | /// use std::macros::count; 65 | /// 66 | /// assert_eq!(count!(), 0); 67 | /// assert_eq!(count!("a", 1, true), 3); 68 | /// ``` 69 | macro count($arg...) { 70 | reduce!(internal::count_reducer, 0usize, $arg$...) 71 | } 72 | 73 | 74 | #[docs(no_index)] 75 | mod internal { 76 | macro count_reducer($acc, $_) { 77 | $acc + 1usize 78 | } 79 | } 80 | 81 | #[cfg(all(test, test_std))] 82 | #[docs(hide)] 83 | mod tests { 84 | #[test] 85 | fn test_count() { 86 | assert_eq!(count!(), 0); 87 | assert_eq!(count!("a", 1, true), 3); 88 | } 89 | 90 | #[test] 91 | fn test_bind_reduce() { 92 | macro times($a, $b) { 93 | $a * $b 94 | } 95 | 96 | macro plus($a, $b) { 97 | $a + $b 98 | } 99 | 100 | macro sum($arg...) { 101 | reduce!(plus, 0, $arg$...) 102 | } 103 | 104 | macro map($receiver, $m, $arg...) { 105 | $receiver!($m!($arg)$...) 106 | } 107 | 108 | // prefix1 109 | // prefix2 110 | // prefix3 111 | let x = map!(sum, bind!(times, 3), 1, 2, 3); 112 | assert_eq!(x, 18); // 1 * 3 + 2 * 3 + 3 * 3 113 | let x = map!(sum, bind!(times, 10), 1, 2, 3); 114 | assert_eq!(x, 60); // 1 * 10 + 2 * 10 + 3 * 10 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /sysroot/std/net/mod.alu: -------------------------------------------------------------------------------- 1 | //! Network sockets 2 | 3 | #[cfg(any(target_os="linux", target_os="macos", target_os="android"))] 4 | use unix::{ 5 | TcpStream, 6 | TcpListener, 7 | UdpSocket, 8 | NameLookup, 9 | Shutdown, 10 | Socket 11 | }; 12 | 13 | use address::{AddrKind, IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; 14 | 15 | /// Create a buffered reader/writer pair for a socket. 16 | fn buffered(self: &mut TcpStream, read_buf_size: usize, write_buf_size: usize) 17 | -> (io::BufferedReader, io::BufferedWriter) { 18 | let reader = io::BufferedReader::new(self, read_buf_size); 19 | let writer = io::BufferedWriter::new(self, write_buf_size); 20 | 21 | (reader, writer) 22 | } 23 | -------------------------------------------------------------------------------- /sysroot/std/prelude.alu: -------------------------------------------------------------------------------- 1 | //! Items available everywhere without having to `use` them 2 | //! 3 | //! This file is the similar to the Rust's "prelude", items defined here are 4 | //! available in the root lexical-scope, so they are available everywhere (but can be 5 | //! shadowed by local definitions). 6 | //! 7 | //! In general, prelude should only contain use aliases to items defined elsewhere. 8 | 9 | use std::io::{print, println, eprint, eprintln}; 10 | use std::{compile_fail, unreachable, dbg}; 11 | use std::{assert, assert_eq, assert_ne}; 12 | use std::panicking::panic; 13 | use std::builtins::{ 14 | u8, u16, u32, u64, u128, usize, 15 | i8, i16, i32, i64, i128, isize, 16 | f32, f64, 17 | bool, void 18 | }; 19 | use std::option::Option; 20 | use std::result::{try, Result}; 21 | use std::typing::Self; 22 | use std::runtime::Coroutine; 23 | -------------------------------------------------------------------------------- /sysroot/std/process/mod.alu: -------------------------------------------------------------------------------- 1 | //! A module for working with processes. 2 | 3 | #[cfg(any(target_os="linux", target_os="macos", target_os="android"))] 4 | { 5 | use unix::{ 6 | ExitStatus, Command, Forked, Output, Child, EnvVars, env, 7 | current_dir, set_current_dir 8 | }; 9 | } 10 | 11 | /// Exits the process with a given return code. 12 | /// 13 | /// This is a graceful exit (`atexit` handlers will run). For an immediate abort, 14 | /// see `libc::_exit` or `libc::abort`. 15 | /// 16 | /// ## Examples 17 | /// ``` 18 | /// use std::process::exit; 19 | /// 20 | /// exit(0); // Exit with success 21 | /// ``` 22 | /// ```no_run 23 | /// use std::process::exit; 24 | /// 25 | /// exit(1); // Exit with error 26 | /// ``` 27 | fn exit(code: i32) -> ! { 28 | libc::exit(code as libc::c_int) 29 | } 30 | 31 | /// Standard I/O stream redirection type 32 | enum Stdio { 33 | /// A new pipe should be arranged to connect the parent and child processes. 34 | Piped, 35 | /// Null stream 36 | /// 37 | /// When used on the input stream, the child will receive an end of file 38 | /// immediately. When used on the output or error stream, the output will be 39 | /// discarded. 40 | Null, 41 | /// This stream should be inherited from the parent. 42 | Inherit, 43 | } 44 | -------------------------------------------------------------------------------- /sysroot/std/runtime/backtrace.alu: -------------------------------------------------------------------------------- 1 | //! Bindings to `libbacktrace` 2 | 3 | // Translated from C to Alumina. Original source available at 4 | // https://github.com/ianlancetaylor/libbacktrace. The original license 5 | // is reproduced below. 6 | // 7 | // Copyright (C) 2012-2016 Free Software Foundation, Inc. 8 | // 9 | // Redistribution and use in source and binary forms, with or without 10 | // modification, are permitted provided that the following conditions are 11 | // met: 12 | // 13 | // (1) Redistributions of source code must retain the above copyright 14 | // notice, this list of conditions and the following disclaimer. 15 | // 16 | // (2) Redistributions in binary form must reproduce the above copyright 17 | // notice, this list of conditions and the following disclaimer in 18 | // the documentation and/or other materials provided with the 19 | // distribution. 20 | // 21 | // (3) The name of the author may not be used to 22 | // endorse or promote products derived from this software without 23 | // specific prior written permission. 24 | // 25 | // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 26 | // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 27 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 28 | // DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 29 | // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 30 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 31 | // SERVICES; LOSS OF USE, `data`, OR PROFITS; OR BUSINESS INTERRUPTION) 32 | // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 33 | // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 34 | // IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 | // POSSIBILITY OF SUCH DAMAGE. 36 | 37 | /// The backtrace state. 38 | 39 | /// This struct is intentionally not defined in the public interface. 40 | struct BacktraceState {} 41 | 42 | /// The type of the error callback argument to backtrace functions. 43 | /// 44 | /// This function, if not `null`, will be called for certain error cases. 45 | /// The `data` argument is passed to the function that calls this one. 46 | /// The MSG argument is an error message. The `errnum` argument, if 47 | /// greater than 0, holds an errno value. The MSG buffer may become 48 | /// invalid after this function returns. 49 | /// 50 | /// As a special case, the `errnum` argument will be passed as -1 if no 51 | /// debug info can be found for the executable, or if the debug info 52 | /// exists but has an unsupported version, but the function requires 53 | /// debug info (e.g., [backtrace_full], `backtrace_pcinfo`). The MSG in 54 | /// this case will be something along the lines of "no debug info". 55 | /// Similarly, `errnum` will be passed as -1 if there is no symbol table, 56 | /// but the function requires a symbol table (e.g., `backtrace_syminfo`). 57 | /// This may be used as a signal that some other approach should be 58 | /// tried. 59 | type BacktraceErrorCallback = fn( 60 | &mut void, // state 61 | &libc::c_char, // msg 62 | libc::c_int); // errnum 63 | 64 | /// Create state information for the backtrace routines. 65 | /// 66 | /// This must be called before any of the other routines, and its return value must 67 | /// be passed to all of the other routines. `filename` is the path name 68 | /// of the executable file; if it is `null` the library will try 69 | /// system-specific path names. If not `null`, `filename` must point to a 70 | /// permanent buffer. If `threaded` is non-zero the state may be 71 | /// accessed by multiple threads simultaneously, and the library will 72 | /// use appropriate atomic operations. If `threaded` is zero the state 73 | /// may only be accessed by one thread at a time. This returns a state 74 | /// pointer on success, `null` on error. If an error occurs, this will 75 | /// call the `error_callback` routine. 76 | /// 77 | /// Calling this function allocates resources that cannot be freed. 78 | /// There is no `backtrace_free_state` function. The state is used to 79 | /// cache information that is expensive to recompute. Programs are 80 | /// expected to call this function at most once and to save the return 81 | /// value for all later calls to backtrace functions. 82 | extern "C" fn backtrace_create_state( 83 | filename: &libc::c_char, 84 | threaded: libc::c_int, 85 | error: BacktraceErrorCallback, 86 | data: &mut void 87 | ) -> &mut BacktraceState; 88 | 89 | 90 | /// The type of the callback argument to the backtrace_full function. 91 | /// 92 | /// `data` is the argument passed to backtrace_full. `pc` is the program 93 | /// counter. `filename` is the name of the file containing `pc`, or `null` 94 | /// if not available. `lineno` is the line number in `filename` containing 95 | /// `pc`, or 0 if not available. `function` is the name of the function 96 | /// containing `pc`, or `null` if not available. This should return 0 to 97 | /// continuing tracing. The `filename` and `function` buffers may become 98 | /// invalid after this function returns. 99 | type BacktraceFullCallback = fn( 100 | &mut void, // data 101 | usize, // pc 102 | &libc::c_char, // filename 103 | libc::c_int, // lineno 104 | &libc::c_char // function 105 | ) -> libc::c_int; 106 | 107 | /// Get a full stack backtrace. 108 | /// 109 | /// `skip` is the number of frames to skip; passing 0 will start the trace with the 110 | /// function calling backtrace_full. `data` is passed to the callback routine. 111 | /// If any call to `callback` returns a non-zero value, the stack backtrace 112 | /// stops, and backtrace returns that value; this may be used to limit 113 | /// the number of stack frames desired. If all calls to `callback` 114 | /// return 0, backtrace returns 0. The [backtrace_full] function will 115 | /// make at least one call to either `callback` or `error_callback`. This 116 | /// function requires debug info for the executable. 117 | extern "C" fn backtrace_full( 118 | state: &mut BacktraceState, 119 | skip: libc::c_int, 120 | callback: BacktraceFullCallback, 121 | error: BacktraceErrorCallback, 122 | data: &mut void 123 | ) -> libc::c_int; 124 | -------------------------------------------------------------------------------- /sysroot/std/runtime/minicoro.alu: -------------------------------------------------------------------------------- 1 | #![docs(no_index)] 2 | 3 | //! Bindings to the `minicoro` library. 4 | //! 5 | //! This provides runtime support for stackful coroutines in Alumina 6 | 7 | enum mco_state { 8 | MCO_DEAD = 0 as libc::c_int, 9 | MCO_NORMAL, 10 | MCO_RUNNING, 11 | MCO_SUSPENDED 12 | } 13 | 14 | enum mco_result { 15 | MCO_SUCCESS = 0 as libc::c_int, 16 | MCO_GENERIC_ERROR, 17 | MCO_INVALID_POINTER, 18 | MCO_INVALID_COROUTINE, 19 | MCO_NOT_SUSPENDED, 20 | MCO_NOT_RUNNING, 21 | MCO_MAKE_CONTEXT_ERROR, 22 | MCO_SWITCH_CONTEXT_ERROR, 23 | MCO_NOT_ENOUGH_SPACE, 24 | MCO_OUT_OF_MEMORY, 25 | MCO_INVALID_ARGUMENTS, 26 | MCO_INVALID_OPERATION, 27 | MCO_STACK_OVERFLOW 28 | } 29 | 30 | struct mco_coro { 31 | context: &mut void, 32 | state: mco_state, 33 | func: fn(&mut mco_coro), 34 | prev_co: &mut mco_coro, 35 | user_data: &mut void, 36 | coro_size: usize, 37 | allocator_data: &mut void, 38 | dealloc_cb: fn(&mut void, usize, &mut void), 39 | stack_base: &mut void, 40 | stack_size: usize, 41 | storage: &mut u8, 42 | bytes_stored: usize, 43 | storage_size: usize, 44 | asan_prev_stack: &mut void, 45 | tsan_prev_fiber: &mut void, 46 | tsan_fiber: &mut void, 47 | magic_number: usize, 48 | } 49 | 50 | struct mco_desc { 51 | func: fn(&mut mco_coro), 52 | user_data: &mut void, 53 | 54 | alloc_cb: fn(usize, &mut void) -> &mut void, 55 | dealloc_cb: fn(&mut void, usize, &mut void), 56 | allocator_data: &mut void, 57 | storage_size: usize, 58 | 59 | coro_size: usize, 60 | stack_size: usize, 61 | } 62 | 63 | extern "C" fn mco_desc_init(func: fn(&mut mco_coro), stack_size: usize) -> mco_desc; 64 | extern "C" fn mco_init(co: &mut mco_coro, desc: &mut mco_desc) -> mco_result; 65 | extern "C" fn mco_uninit(co: &mut mco_coro) -> mco_result; 66 | extern "C" fn mco_create(out_co: &mut &mut mco_coro, desc: &mut mco_desc) -> mco_result; 67 | extern "C" fn mco_destroy(co: &mut mco_coro) -> mco_result; 68 | extern "C" fn mco_resume(co: &mut mco_coro) -> mco_result; 69 | extern "C" fn mco_yield(co: &mut mco_coro) -> mco_result; 70 | extern "C" fn mco_status(co: &mut mco_coro) -> mco_state; 71 | extern "C" fn mco_get_user_data(co: &mut mco_coro) -> &mut void; 72 | 73 | extern "C" fn mco_push(co: &mut mco_coro, src: &void, len: usize) -> mco_result; 74 | extern "C" fn mco_pop(co: &mut mco_coro, dest: &mut void, len: usize) -> mco_result; 75 | extern "C" fn mco_peek(co: &mut mco_coro, dest: &mut void, len: usize) -> mco_result; 76 | extern "C" fn mco_get_bytes_stored(co: &mut mco_coro) -> usize; 77 | extern "C" fn mco_get_storage_size(co: &mut mco_coro) -> usize; 78 | 79 | extern "C" fn mco_running() -> &mut mco_coro; 80 | extern "C" fn mco_result_description(res: mco_result) -> &libc::c_char; 81 | 82 | -------------------------------------------------------------------------------- /sysroot/std/thread/parker/futex.alu: -------------------------------------------------------------------------------- 1 | #![cfg(any(target_os = "linux", target_os = "android"))] 2 | #![docs(no_index)] 3 | 4 | use sync::Ordering; 5 | use time::Duration; 6 | 7 | const PARKED: i32 = -1; 8 | const EMPTY: i32 = 0; 9 | const NOTIFIED: i32 = 1; 10 | 11 | struct Parker { 12 | state: Atomic, 13 | } 14 | 15 | fn futex_wait(futex: &mut Atomic, expected: i32, timeout: Option) { 16 | let maybe_timespec = timeout.map(|d: Duration| -> libc::timespec { 17 | libc::timespec { 18 | tv_sec: util::cast(d.secs), 19 | tv_nsec: util::cast(d.nanos) 20 | } 21 | }); 22 | 23 | libc::syscall( 24 | libc::SYS_futex, 25 | &futex._inner, 26 | libc::FUTEX_WAIT | libc::FUTEX_PRIVATE_FLAG, 27 | expected, 28 | maybe_timespec.as_nullable_ptr() 29 | ); 30 | } 31 | 32 | fn futex_wake(futex: &mut Atomic) { 33 | libc::syscall( 34 | libc::SYS_futex, 35 | &futex._inner, 36 | libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG, 37 | 1i32 38 | ); 39 | } 40 | 41 | impl Parker { 42 | fn new() -> Parker { 43 | Parker { state: Atomic::new(EMPTY) } 44 | } 45 | 46 | // Assumes this is only called by the thread that owns the Parker, 47 | // which means that `self.state != PARKED`. 48 | fn park(self: &mut Parker) { 49 | // Change NOTIFIED=>EMPTY or EMPTY=>PARKED, and directly return in the 50 | // first case. 51 | if self.state.fetch_sub(1, Ordering::Acquire) == NOTIFIED { 52 | return; 53 | } 54 | loop { 55 | // Wait for something to happen, assuming it's still set to PARKED. 56 | futex_wait(&self.state, PARKED, Option::none()); 57 | // Change NOTIFIED=>EMPTY and return in that case. 58 | if self.state.compare_exchange(NOTIFIED, EMPTY, Ordering::Acquire, Ordering::Acquire).is_ok() { 59 | return; 60 | } else { 61 | // Spurious wake up. We loop to try again. 62 | } 63 | } 64 | } 65 | 66 | // Assumes this is only called by the thread that owns the Parker, 67 | // which means that `self.state != PARKED`. 68 | fn park_timeout(self: &mut Parker, timeout: Duration) { 69 | // Change NOTIFIED=>EMPTY or EMPTY=>PARKED, and directly return in the 70 | // first case. 71 | if self.state.fetch_sub(1, Ordering::Acquire) == NOTIFIED { 72 | return; 73 | } 74 | // Wait for something to happen, assuming it's still set to PARKED. 75 | futex_wait(&self.state, PARKED, Option::some(timeout)); 76 | // This is not just a store, because we need to establish a 77 | // release-acquire ordering with unpark(). 78 | if self.state.exchange(EMPTY, Ordering::Acquire) == NOTIFIED { 79 | // Woke up because of unpark(). 80 | } else { 81 | // Timeout or spurious wake up. 82 | // We return either way, because we can't easily tell if it was the 83 | // timeout or not. 84 | } 85 | } 86 | 87 | #[inline] 88 | fn unpark(self: &mut Parker) { 89 | // Change PARKED=>NOTIFIED, EMPTY=>NOTIFIED, or NOTIFIED=>NOTIFIED, and 90 | // wake the thread in the first case. 91 | // 92 | // Note that even NOTIFIED=>NOTIFIED results in a write. This is on 93 | // purpose, to make sure every unpark() has a release-acquire ordering 94 | // with park(). 95 | if self.state.exchange(NOTIFIED, Ordering::Release) == PARKED { 96 | futex_wake(&self.state); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /sysroot/std/thread/parker/mod.alu: -------------------------------------------------------------------------------- 1 | #![docs(no_index)] 2 | 3 | //! Thread "parking". Adapted from Rust's `Parker` implementation. 4 | //! 5 | //! See [Futex implementation](https://github.com/rust-lang/rust/blob/master/library/std/src/sys_common/thread_parker/futex.rs) and 6 | //! [generic implementation using pthread](https://github.com/rust-lang/rust/blob/master/library/std/src/sys_common/thread_parker/generic.rs). 7 | 8 | #[cfg(not(any(target_os = "linux", target_os = "android")))] 9 | use pthread::Parker; 10 | #[cfg(any(target_os = "linux", target_os = "android"))] 11 | use futex::Parker; 12 | 13 | #[cfg(all(threading, test, test_std))] 14 | #[docs(hide)] 15 | mod tests { 16 | use time::Duration; 17 | 18 | #[test] 19 | fn test_park() { 20 | let parker = Parker::new(); 21 | parker.unpark(); 22 | parker.park(); 23 | } 24 | 25 | #[test] 26 | fn test_park_timeout() { 27 | let parker = Parker::new(); 28 | parker.unpark(); 29 | parker.park_timeout(Duration::from_secs(5)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /sysroot/std/thread/parker/pthread.alu: -------------------------------------------------------------------------------- 1 | #![docs(no_index)] 2 | 3 | use sync::{Atomic, Ordering, CondVar, Mutex}; 4 | use time::Duration; 5 | 6 | const EMPTY: usize = 0; 7 | const PARKED: usize = 1; 8 | const NOTIFIED: usize = 2; 9 | 10 | struct Parker { 11 | state: Atomic, 12 | lock: Mutex, 13 | cvar: CondVar, 14 | } 15 | 16 | impl Parker { 17 | fn new() -> Parker { 18 | Parker { state: Atomic::new(EMPTY), lock: Mutex::new(), cvar: CondVar::new() } 19 | } 20 | 21 | 22 | fn park(self: &mut Parker) { 23 | // If we were previously notified then we consume this notification and 24 | // return quickly. 25 | if self.state.compare_exchange(NOTIFIED, EMPTY, Ordering::SeqCst, Ordering::SeqCst).is_ok() { 26 | return; 27 | } 28 | 29 | // Otherwise we need to coordinate going to sleep 30 | self.lock.lock(); 31 | defer self.lock.unlock(); 32 | 33 | let ret = self.state.compare_exchange(EMPTY, PARKED, Ordering::SeqCst, Ordering::SeqCst); 34 | if ret.is_err() { 35 | if ret.unwrap_err() == NOTIFIED { 36 | let old = self.state.exchange(EMPTY, Ordering::SeqCst); 37 | assert_eq!(old, NOTIFIED); 38 | return; 39 | } else { 40 | panic!("inconsistent park state") 41 | } 42 | } 43 | 44 | loop { 45 | self.cvar.wait(&self.lock); 46 | if self.state.compare_exchange(NOTIFIED, EMPTY, Ordering::SeqCst, Ordering::SeqCst).is_ok() { 47 | return; 48 | } 49 | } 50 | } 51 | 52 | fn park_timeout(self: &mut Parker, dur: Duration) { 53 | // Like `park` above we have a fast path for an already-notified thread, and 54 | // afterwards we start coordinating for a sleep. 55 | // return quickly. 56 | if self.state.compare_exchange(NOTIFIED, EMPTY, Ordering::SeqCst, Ordering::SeqCst).is_ok() { 57 | return; 58 | } 59 | self.lock.lock(); 60 | defer self.lock.unlock(); 61 | 62 | let ret = self.state.compare_exchange(EMPTY, PARKED, Ordering::SeqCst, Ordering::SeqCst); 63 | if ret.is_err() { 64 | if ret.unwrap_err() == NOTIFIED { 65 | let old = self.state.exchange(EMPTY, Ordering::SeqCst); 66 | assert_eq!(old, NOTIFIED); 67 | return; 68 | } else { 69 | panic!("inconsistent park state") 70 | } 71 | } 72 | 73 | // Wait with a timeout, and if we spuriously wake up or otherwise wake up 74 | // from a notification we just want to unconditionally set the state back to 75 | // empty, either consuming a notification or un-flagging ourselves as 76 | // parked. 77 | self.cvar.wait_timeout(&self.lock, dur); 78 | switch self.state.exchange(EMPTY, Ordering::SeqCst) { 79 | NOTIFIED => {} // got a notification, hurray! 80 | PARKED => {} // no notification, alas 81 | _ => panic!("inconsistent park_timeout state"), 82 | } 83 | } 84 | 85 | fn unpark(self: &mut Parker) { 86 | // To ensure the unparked thread will observe any writes we made 87 | // before this call, we must perform a release operation that `park` 88 | // can synchronize with. To do that we must write `NOTIFIED` even if 89 | // `state` is already `NOTIFIED`. That is why this must be a swap 90 | // rather than a compare-and-swap that returns if it reads `NOTIFIED` 91 | // on failure. 92 | switch self.state.exchange(NOTIFIED, Ordering::SeqCst) { 93 | EMPTY => return, // no one was waiting 94 | NOTIFIED => return, // already unparked 95 | PARKED => {} // gotta go wake someone up 96 | _ => panic!("inconsistent state in unpark"), 97 | } 98 | 99 | // There is a period between when the parked thread sets `state` to 100 | // `PARKED` (or last checked `state` in the case of a spurious wake 101 | // up) and when it actually waits on `cvar`. If we were to notify 102 | // during this period it would be ignored and then when the parked 103 | // thread went to sleep it would never wake up. Fortunately, it has 104 | // `lock` locked at this stage so we can acquire `lock` to wait until 105 | // it is ready to receive the notification. 106 | // 107 | // Releasing `lock` before the call to `notify_one` means that when the 108 | // parked thread wakes it doesn't get woken only to have to wait for us 109 | // to release `lock`. 110 | self.lock.lock(); 111 | self.lock.unlock(); 112 | self.cvar.notify_one() 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /sysroot/std/util.alu: -------------------------------------------------------------------------------- 1 | //! Various utility functions useful in the generic context. 2 | 3 | /// Identity function 4 | /// 5 | /// ## Example 6 | /// ``` 7 | /// use std::util::identity; 8 | /// 9 | /// assert_eq!(identity(1), 1); 10 | /// ``` 11 | #[inline(ir)] fn identity(t: T) -> T { t } 12 | 13 | /// Take any parameter and discard the result 14 | /// 15 | /// ## Example 16 | /// ``` 17 | /// use std::util::discard; 18 | /// 19 | /// // Use foreach to loop through the iterator, but don't do anything with the values. 20 | /// (0..10) 21 | /// .inspect(|i: i32| { println!("{}", i); }) 22 | /// .foreach(discard::); 23 | /// ``` 24 | #[inline(ir)] fn discard(_: T) { } 25 | 26 | /// Cast a value to a different type. 27 | /// 28 | /// ## Example 29 | /// ``` 30 | /// use std::util::cast; 31 | /// 32 | /// #[cfg(target_pointer_width = "64")] 33 | /// let x: u64; 34 | /// #[cfg(target_pointer_width = "32")] 35 | /// let x: u32; 36 | /// 37 | /// // We do not know whether x is `u64` or `u32`, but we know 10 38 | /// // fits in either. 39 | /// x = cast(10); 40 | /// ``` 41 | #[inline] 42 | #[allow(unnecessary_cast)] 43 | fn cast(t: T1) -> T2 { 44 | t as T2 45 | } 46 | 47 | /// Coerce a value to a different type without casting 48 | /// 49 | /// ## Example 50 | /// ``` 51 | /// use std::util::coerce; 52 | /// use std::mem::slice; 53 | /// 54 | /// let x: Option<&mut [u8]> = Option::some(slice::alloc(10)); 55 | /// defer x.unwrap().free(); 56 | /// 57 | /// let _: Option<&[u8]> = x.map(coerce::<&mut [u8], &[u8]>); 58 | /// ``` 59 | #[inline] fn coerce(t: T1) -> T2 { t } 60 | 61 | /// Reinterpret the bits of the value as another type. Requires that the 62 | /// types have the same size and alignment. 63 | /// 64 | /// ## Examples 65 | /// ``` 66 | /// use std::util::transmute; 67 | /// 68 | /// let x: u64 = 0x400921fb54442d18; 69 | /// let y: f64 = transmute(x); 70 | /// 71 | /// assert_eq!(y, 3.14159265358979323846264338327950288f64); 72 | /// ``` 73 | /// ```compile_fail 74 | /// use std::util::transmute; 75 | /// 76 | /// let x: u8 = 123; 77 | /// let y: u32 = transmute(x); // compile error 78 | /// ``` 79 | #[inline(ir)] 80 | fn transmute(t: T1) -> T2 { 81 | when !(t is builtins::SameLayoutAs) { 82 | compile_fail!("types do not have the same layout"); 83 | } 84 | 85 | intrinsics::transmute::(t) 86 | } 87 | 88 | /// Dereference a pointer. 89 | #[inline(ir)] fn deref(v: &T) -> T { *v } 90 | 91 | /// Produce the only value of a unit type out of thin air. 92 | /// 93 | /// Does not work for the [builtins::never] type. 94 | /// 95 | /// ## Example 96 | /// ``` 97 | /// use std::builtins::NamedFunction; 98 | /// use std::util::unit; 99 | /// 100 | /// fn hello_world() { 101 | /// println!("Hello, world!"); 102 | /// } 103 | /// 104 | /// fn call() { 105 | /// // Named functions are a family of zero-sized types 106 | /// let f = unit::(); 107 | /// f(); 108 | /// } 109 | /// 110 | /// call::(); 111 | /// ``` 112 | #[inline(ir)] 113 | fn unit() -> T { 114 | mem::uninitialized() 115 | } 116 | 117 | 118 | /// Applies a function on the provided value. 119 | /// 120 | /// Useful in method chaining 121 | /// 122 | /// ## Example 123 | /// ``` 124 | /// use std::util::apply; 125 | /// 126 | /// let v = 3 127 | /// .apply(|v: i32| -> i32 { v + 1 }) 128 | /// .apply(|v: i32| -> Option { Option::some(v) }) 129 | /// .apply(|v: Option| -> i32 { v.unwrap() }); 130 | /// 131 | /// assert_eq!(v, Option::some(3 + 1).unwrap()); 132 | /// ``` 133 | #[inline(ir)] 134 | fn apply R>(v: T, func: F) -> R { 135 | func(v) 136 | } 137 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | # Tests 2 | 3 | This directory contains tests for the compiler. They are organized into subdirectories: 4 | - [`lang`](./lang/) - tests for various language features that are not specific to standard library. These tests run through the standard test runner (the whole direcory is one module). 5 | - [`diag`](./diag/) - cases for which the compiler should emit a diagnostic (either a failed compilation or a warning). These tests are executed by the [`diag` test runner](../tools/diag.py). Each `.alu` file is compiled separately to C code, but the C code is not compiled into an executable. 6 | 7 | To run these tests, run `make test-lang` or `make test-diag` 8 | 9 | The standard library tests are not in this directory, but in [`sysroot`](../sysroot/) directory, collocated with the standard library source code. 10 | -------------------------------------------------------------------------------- /tests/diag/aligned_and_packed.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | #[align(16)] 3 | #[packed(8)] // diag: error(align_and_packed): "`#[align(...)]` cannot be used together with `#[packed]`" 4 | struct Foo {} 5 | -------------------------------------------------------------------------------- /tests/diag/assign_to_const.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | const FOO = 1; 4 | FOO = 2; // diag: error(cannot_assign_to_const): "cannot assign to const" 5 | } 6 | -------------------------------------------------------------------------------- /tests/diag/assign_to_const_2.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | let a = 1; 4 | let b: &i32 = &a; 5 | *b = 2; // diag: error(cannot_assign_to_const): "cannot assign to const" 6 | } 7 | -------------------------------------------------------------------------------- /tests/diag/assign_to_rvalue.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | 1 = 2; // diag: error(cannot_assign_to_r_value): "cannot assign to rvalue" 4 | } 5 | -------------------------------------------------------------------------------- /tests/diag/break_outside_loop.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | break; // diag: error(break_outside_of_loop): "break outside of loop" 4 | } 5 | -------------------------------------------------------------------------------- /tests/diag/builtin_protocol_dyn.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | let _: &dyn std::builtins::Numeric; // diag: error(builtin_protocol_dyn): "builtin protocols cannot be used with `dyn`" 4 | } 5 | -------------------------------------------------------------------------------- /tests/diag/builtins_are_special_mkay.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | let _a: std::builtins::array; // diag: error(builtin_types_are_special_mkay): "this is not a type you can actually use, sorry" 4 | } 5 | -------------------------------------------------------------------------------- /tests/diag/cannot_et_cetera.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | macro foo($a...) { 3 | 1 + $a... // diag: error(et_cetera_expr_in_unsupported): "... can only be used inside a tuple" 4 | } 5 | 6 | fn main() { 7 | println!("{}", foo!(1, 2, 3)); 8 | } 9 | -------------------------------------------------------------------------------- /tests/diag/cannot_read_file.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | const FOO = std::include_bytes!("/path/to/some/nonexistent/file"); // diag: error(cannot_read_file): "cannot read file `/path/to/some/nonexistent/file`" 3 | -------------------------------------------------------------------------------- /tests/diag/cannot_reference_local.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | let a = 42; 4 | let b = || { println!("{}", a) }; // diag: error(cannot_reference_local): "cannot reference `a` in a nested function" 5 | 6 | b(); 7 | } 8 | -------------------------------------------------------------------------------- /tests/diag/char_literal.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | struct Foo {} 3 | 4 | fn main() { 5 | let _a = 'hello'; // diag: error(invalid_char_literal): "character literals must be exactly one byte" 6 | } 7 | -------------------------------------------------------------------------------- /tests/diag/closures_are_not_fns.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | let a: i32; 4 | let _: fn() = |&a| { a = 1; }; // diag: error(closures_are_not_fns): "anonymous functions that bind environment variables cannot be coerced to a function pointer" 5 | } 6 | -------------------------------------------------------------------------------- /tests/diag/common.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | 3 | fn main() { 4 | let a = some::nonexisting::path; // diag: error(unresolved_path): "could not resolve the path `some::nonexisting`" 5 | } 6 | -------------------------------------------------------------------------------- /tests/diag/const_eval_1.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | const FOO = libc::abort(); // diag: error(cannot_const_evaluate): "this expression is not evaluable at compile time (function `abort` is not supported in constant context)" 4 | 5 | FOO; 6 | } 7 | -------------------------------------------------------------------------------- /tests/diag/const_eval_10.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | 3 | 4 | fn main() { 5 | const FOO = { 6 | let foo = std::mem::slice::alloc::(10); 7 | foo.free(); 8 | 9 | foo[1] = 10; // diag: error(cannot_const_evaluate): "this expression is not evaluable at compile time (dynamically allocated memory used after being freed)" 10 | }; 11 | 12 | FOO; 13 | } 14 | -------------------------------------------------------------------------------- /tests/diag/const_eval_11.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | 3 | 4 | fn main() { 5 | const FOO = { 6 | let foo = std::mem::slice::alloc::(10); 7 | foo.free(); 8 | foo.free(); // diag: error(cannot_const_evaluate): "this expression is not evaluable at compile time (invalid pointer used to free memory)" 9 | }; 10 | 11 | FOO; 12 | } 13 | -------------------------------------------------------------------------------- /tests/diag/const_eval_2.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | const FOO = loop {}; // diag: error(cannot_const_evaluate): "this expression is not evaluable at compile time (max iterations exceeded)" 4 | 5 | FOO; 6 | } 7 | -------------------------------------------------------------------------------- /tests/diag/const_eval_3.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | const FOO = { 4 | let a: i32; 5 | a += 1; // diag: error(cannot_const_evaluate): "this expression is not evaluable at compile time (not constant or unsupported expression)" 6 | }; 7 | 8 | FOO; 9 | } 10 | -------------------------------------------------------------------------------- /tests/diag/const_eval_4.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | const FOO = { 4 | let a: [i32; 1]; 5 | *(&a[0] + 5) += 10; // diag: error(cannot_const_evaluate): "this expression is not evaluable at compile time (index out of bounds)" 6 | }; 7 | 8 | FOO; 9 | } 10 | -------------------------------------------------------------------------------- /tests/diag/const_eval_5.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | const FOO = { 4 | let a = 127i8; 5 | a += 1; // diag: error(cannot_const_evaluate): "this expression is not evaluable at compile time (arithmetic overflow)" 6 | }; 7 | 8 | FOO; 9 | } 10 | -------------------------------------------------------------------------------- /tests/diag/const_eval_6.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | const FOO = { 4 | std::intrinsics::unreachable(); // diag: error(cannot_const_evaluate): "this expression is not evaluable at compile time (reached unreachable code)" 5 | }; 6 | 7 | FOO; 8 | } 9 | -------------------------------------------------------------------------------- /tests/diag/const_eval_7.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | const FOO = { 4 | let _ = 1 / 0; // diag: error(cannot_const_evaluate): "this expression is not evaluable at compile time (division by zero)" 5 | }; 6 | 7 | FOO; 8 | } 9 | -------------------------------------------------------------------------------- /tests/diag/const_eval_8.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | 3 | fn re_cursed() { 4 | re_cursed(); // diag: error(cannot_const_evaluate): "this expression is not evaluable at compile time (max recursion depth exceeded)" 5 | } 6 | 7 | fn main() { 8 | const FOO = { 9 | re_cursed(); 10 | }; 11 | 12 | FOO; 13 | } 14 | -------------------------------------------------------------------------------- /tests/diag/const_eval_9.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | 3 | fn main() { 4 | const FOO: &mut i32 = { // diag: error(cannot_const_evaluate): "this expression is not evaluable at compile time (contains pointer to a local variable)" 5 | let a: i32; 6 | &a 7 | }; 8 | 9 | *FOO; 10 | } 11 | -------------------------------------------------------------------------------- /tests/diag/const_panic.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | std::const_assert_eq!(1, 2); // diag: error(const_panic): "panic during constant evaluation at tests/diag/const_panic.alu:3:5: assertion failed (1 != 2))" 4 | } 5 | -------------------------------------------------------------------------------- /tests/diag/constant_string.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | let foo = "hello world"; 4 | println!(foo); // diag: error(constant_string_expected): "constant string expected" 5 | } 6 | -------------------------------------------------------------------------------- /tests/diag/continue_outside_loop.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | continue; // diag: error(continue_outside_of_loop): "continue outside of loop" 4 | } 5 | -------------------------------------------------------------------------------- /tests/diag/coroutine_return_type.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn* coro() -> i32 { // diag: error(coroutine_return_type): "coroutines must have Coroutine<_, _> as return type" 3 | yield; 4 | } 5 | 6 | fn main() { 7 | coro(); 8 | } 9 | -------------------------------------------------------------------------------- /tests/diag/cyclic.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | 3 | mod foo { 4 | use bar::a as b; // diag: error(cycle_detected): "cycle detected while resolving aliases" 5 | } 6 | mod bar { 7 | use foo::b as a; 8 | } 9 | -------------------------------------------------------------------------------- /tests/diag/default_case_last.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | switch true { 4 | _ => println!("wow"), 5 | true => {} // diag: error(default_case_must_be_last): "default case must be last in a switch expression" 6 | }; 7 | } 8 | -------------------------------------------------------------------------------- /tests/diag/defer_in_defer.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | defer { 4 | defer {}; // diag: error(defer_in_defer): "cannot defer inside a defered expression" 5 | }; 6 | } 7 | -------------------------------------------------------------------------------- /tests/diag/defer_in_non_function_scope.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() -> typeof(defer {}) { // diag: error(not_in_a_function_scope): "can only do that in function scope" 3 | } 4 | -------------------------------------------------------------------------------- /tests/diag/dollared_identifier.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | $a = 4; // diag: error(dollared_outside_of_macro): "`$` identifiers can only be used in macros" 4 | } 5 | -------------------------------------------------------------------------------- /tests/diag/duplicate.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | 3 | fn main() { 4 | 5 | } 6 | 7 | fn main() { // diag: error(duplicate_name): "duplicate name `main`" 8 | 9 | } 10 | -------------------------------------------------------------------------------- /tests/diag/duplicate_attribute.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | #[align(2)] 3 | #[align(4)] // diag: error(duplicate_attribute): "duplicate attribute `align`" 4 | struct Foo { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /tests/diag/duplicate_enum_member.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | enum Foo { 3 | A = 1, 4 | B = 2, 5 | A = 3 // diag: error(duplicate_name): "duplicate name `A`" 6 | } 7 | -------------------------------------------------------------------------------- /tests/diag/duplicate_field_init.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | struct Foo { 3 | foo: i32 4 | } 5 | 6 | fn main() { 7 | let _ = Foo { 8 | foo: 1, 9 | foo: 2 // diag: error(duplicate_field_initializer): "duplicate field `foo` in struct initializer" 10 | }; 11 | } 12 | -------------------------------------------------------------------------------- /tests/diag/et_cetera.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | let a = 4$...; // diag: error(macro_et_cetera_outside_of_macro): "`$...` expressions can only be used in macros" 4 | let a = 4...; 5 | } 6 | -------------------------------------------------------------------------------- /tests/diag/et_cetera_in_et_cetera.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | macro foo($a...) { 3 | ([$a$...]$...,); // diag: error(macro_et_cetera_in_macro_et_cetera): "nested `$...` expansions are not supported (yet)" 4 | } 5 | 6 | fn main() { 7 | foo!(1, 2, 3); 8 | } 9 | -------------------------------------------------------------------------------- /tests/diag/extern_coroutine.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | extern "C" fn* foo() { // diag: error(extern_coroutine): "coroutines cannot be extern" 3 | 4 | } 5 | -------------------------------------------------------------------------------- /tests/diag/extern_generic.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | extern "C" fn wow(); // diag: error(extern_c_generic_params): "extern \"C\" functions cannot have generic parameters" 3 | -------------------------------------------------------------------------------- /tests/diag/extern_static_generic.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | extern "C" static FOO; // diag: error(extern_static_cannot_be_generic): "extern statics cannot be generic" 3 | -------------------------------------------------------------------------------- /tests/diag/extern_static_init.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | extern "C" static FOO: i32 = 4; // diag: error(extern_static_must_have_type): "extern statics cannot have initializers" 3 | -------------------------------------------------------------------------------- /tests/diag/freestanding_impl.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | impl Bar { // diag: error(no_free_standing_impl): "cannot have an `impl` block without a corresponding type" 3 | 4 | } 5 | -------------------------------------------------------------------------------- /tests/diag/function_or_static_expected_here.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | let a = 1; 4 | a::(); // diag: error(function_or_static_expected_here): "function or static expected" 5 | } 6 | -------------------------------------------------------------------------------- /tests/diag/generic_arg_in_path.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | let a: std::foo::i32; // diag: error(generic_args_in_path): "generic type parameters cannot be used in this context (did you mean to call a function?)" 4 | } 5 | -------------------------------------------------------------------------------- /tests/diag/generic_count_mismatch.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | 3 | fn main() { 4 | std::util::identity::(43); // diag: error(generic_param_count_mismatch): "1 generic parameters expected, 2 found" 5 | } 6 | -------------------------------------------------------------------------------- /tests/diag/generic_protocol_fn.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | protocol Foo { // diag: error(mixin_only_protocol): "protocols containing generic functions can only be used as mixins" 3 | fn foo(); 4 | } 5 | 6 | fn main() { 7 | std::typing::matches::; 8 | } 9 | -------------------------------------------------------------------------------- /tests/diag/indirect_dyn.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | protocol Foo { 3 | fn foo(self: &Self); 4 | } 5 | 6 | protocol Foo2 { 7 | fn foo(self: &Self); 8 | } 9 | 10 | struct Concrete { 11 | } 12 | 13 | impl Concrete { 14 | fn foo(self: &Concrete) { 15 | 16 | } 17 | } 18 | 19 | fn coerced>(f: &F) { 20 | let _: &dyn Foo = f; // diag: error(indirect_dyn): "indirect `dyn` pointers are not supported" 21 | } 22 | 23 | fn main() { 24 | let f: &dyn (Foo + Foo2) = &Concrete {}; 25 | coerced(&f); 26 | } 27 | -------------------------------------------------------------------------------- /tests/diag/integer_out_of_range.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | let _a = 10000u8; // diag: error(integer_out_of_range): "integer literal out of range (10000 does not fit into u8)" 4 | } 5 | -------------------------------------------------------------------------------- /tests/diag/intrinsics_are_special_mkay.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | let _ = &std::intrinsics::unreachable; // diag: error(intrinsics_are_special_mkay): "cannot take address of a compiler intrinsic" 4 | } 5 | -------------------------------------------------------------------------------- /tests/diag/invalid_attribute.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | #[align(-1)] // diag: error(invalid_attribute): "invalid attribute" 3 | struct Foo { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /tests/diag/invalid_bin_op.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | 1 + "foo"; // diag: error(invalid_bin_op): "cannot perform Plus between `i32` and `&[u8]`" 4 | } 5 | -------------------------------------------------------------------------------- /tests/diag/invalid_cast.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | 4 as (); // diag: error(invalid_cast): "cannot cast `i32` into `()`" 4 | } 5 | -------------------------------------------------------------------------------- /tests/diag/invalid_enum_variant.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | enum Foo { 3 | A = "hello" // diag: error(invalid_value_for_enum_variant): "values of enum variants can only be integers" 4 | } 5 | 6 | 7 | fn main() { 8 | let _ = Foo::A; 9 | } 10 | -------------------------------------------------------------------------------- /tests/diag/invalid_escape.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | const FOO = "\Q"; // diag: error(invalid_escape_sequence): "invalid escape sequence" 3 | -------------------------------------------------------------------------------- /tests/diag/invalid_format_string.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | println!("{") // diag: error(invalid_format_string): "invalid format string (unexpected end of format string)" 4 | } 5 | -------------------------------------------------------------------------------- /tests/diag/invalid_transparent.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | #[transparent] // diag: error(invalid_transparent): "transparent structs and unions must have exactly one field" 3 | struct A { 4 | foo: i32, 5 | bar: i64 6 | } 7 | -------------------------------------------------------------------------------- /tests/diag/invalid_type_op.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | let _f: std::builtins::arguments_of; // diag: error(invalid_type_operator): "invalid type operator" 4 | } 5 | -------------------------------------------------------------------------------- /tests/diag/invalid_un_op.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | -"foo"; // diag: error(invalid_un_op): "cannot perform Neg on `&[u8]`" 4 | } 5 | -------------------------------------------------------------------------------- /tests/diag/ir_inline_early_return.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | #[inline(ir)] 3 | fn vary(i: i32) { 4 | if i > 0 { return; } 5 | if i > 1 { return; } 6 | if i > 2 { return; } 7 | } 8 | 9 | fn main() { 10 | vary(5); // diag: error(ir_inline_early_return): "cannot IR-inline functions that can return early" 11 | } 12 | -------------------------------------------------------------------------------- /tests/diag/ir_inline_flow.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | #[inline(ir)] 3 | fn loopy() { 4 | loop {} 5 | } 6 | 7 | fn main() { 8 | loopy(); // diag: error(ir_inline_flow_control): "cannot IR-inline functions that use flow control" 9 | } 10 | -------------------------------------------------------------------------------- /tests/diag/ir_inline_vars.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | #[inline(ir)] // diag: error(ir_inline_local_defs): "cannot IR-inline functions that use variables" 3 | fn vary() { 4 | let a = 1; 5 | a += 1; 6 | a += 1; 7 | } 8 | 9 | fn main() { 10 | vary(); 11 | } 12 | -------------------------------------------------------------------------------- /tests/diag/is_a_macro.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | println("{}", "Hello, world!"); // diag: error(is_a_macro): "unexpanded macro (hint: append `!` to invoke it)" 4 | } 5 | -------------------------------------------------------------------------------- /tests/diag/lang_item_missing.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | #[lang(super_special_function)] // diag: error(unknown_lang_item): "unknown lang item super_special_function" 3 | fn explode() {} 4 | 5 | fn main() { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /tests/diag/literal.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | struct Foo {} 3 | 4 | fn main() { 5 | let _a = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; // diag: error(invalid_literal): "invalid literal" 6 | } 7 | -------------------------------------------------------------------------------- /tests/diag/macro_expected.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn foo() {} 3 | 4 | fn main() { 5 | foo!(); // diag: error(not_a_macro): "expression is not a macro" 6 | } 7 | -------------------------------------------------------------------------------- /tests/diag/macros_with_items.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | macro foo() { 3 | fn lol() { // diag: error(macros_cannot_define_items): "cannot define new items in a macro body (yet)" 4 | 5 | } 6 | } 7 | 8 | fn main() { 9 | foo!(); 10 | } 11 | -------------------------------------------------------------------------------- /tests/diag/macros_with_lambdas.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | macro foo() { 3 | let a = || {}; // diag: error(macros_cannot_define_lambdas): "anonymous functions are not supported in a macro body (yet)" 4 | } 5 | 6 | fn main() { 7 | foo!(); 8 | } 9 | -------------------------------------------------------------------------------- /tests/diag/method_not_found.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | #[export] 3 | fn main() { 4 | let foo = (); 5 | foo.voidify(); // diag: error(method_not_found): "method `voidify` not found on `()`" 6 | } 7 | -------------------------------------------------------------------------------- /tests/diag/mismatched_branches.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | if 1 > 2 { // diag: error(mismatched_branch_types): "branches have incompatible types (`i32`, `bool`)" 4 | 1 5 | } else { 6 | false 7 | }; 8 | } 9 | -------------------------------------------------------------------------------- /tests/diag/multiple_etceteras.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | macro foo($a..., $b...) { // diag: error(multiple_macro_et_ceteras): "macro can have at most one `...` parameter" 3 | $a$...; 4 | } 5 | -------------------------------------------------------------------------------- /tests/diag/no_bodied_function.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn foo(); // diag: error(function_must_have_body): "function must have a body" 3 | 4 | fn main() { 5 | foo(); 6 | } 7 | -------------------------------------------------------------------------------- /tests/diag/no_et_ceteras.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | macro foo($a) { 3 | $a$...; // diag: error(no_macro_et_cetera_args): "this macro does not have any `...` arguments" 4 | } 5 | -------------------------------------------------------------------------------- /tests/diag/non_dynnable_fn.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | protocol Foo { 3 | fn foo(self: &Self); 4 | fn bar(); 5 | } 6 | 7 | struct Concrete { 8 | } 9 | 10 | impl Concrete { 11 | fn foo(self: &Concrete) {} 12 | fn bar() {} 13 | } 14 | 15 | fn main() { 16 | let _: &dyn Foo = &Concrete {}; // diag: error(non_dynnable_function): "signature of `bar` is incompatible with virtual dispatch" 17 | } 18 | -------------------------------------------------------------------------------- /tests/diag/non_protocol_dyn.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | let _: &dyn i32; // diag: error(non_protocol_dyn): "dyn requires a protocol" 4 | } 5 | -------------------------------------------------------------------------------- /tests/diag/nonlocal_bind.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | const FOO = 42; 3 | 4 | fn main() { 5 | let f = |=FOO| {}; // diag: error(can_only_close_over_locals): "can only bind local variables" 6 | } 7 | -------------------------------------------------------------------------------- /tests/diag/not_a_method.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | struct Foo { } 3 | 4 | impl Foo { 5 | fn new() -> Foo { 6 | Foo {} 7 | } 8 | } 9 | 10 | fn main() { 11 | let a: Foo; 12 | a.new(); // diag: error(not_a_method): "cannot be called as a method" 13 | } 14 | -------------------------------------------------------------------------------- /tests/diag/not_a_protocol.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | struct Bar {} 3 | 4 | struct Foo { 5 | } 6 | 7 | impl Foo { 8 | mixin Bar; // diag: error(not_a_protocol): "type is not a protocol" 9 | } 10 | 11 | 12 | fn main() { 13 | let a: Foo; // diag: warning(unused_variable): "unused variable `a`" 14 | } 15 | -------------------------------------------------------------------------------- /tests/diag/not_callable.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | 1(); // diag: error(not_callable): "`i32` is not callable" 4 | } 5 | -------------------------------------------------------------------------------- /tests/diag/not_callable_field_confusion.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | struct Foo { 3 | bar: i32 4 | } 5 | 6 | impl Foo { 7 | fn bar(self: &Foo) -> i32 { 8 | self.bar 9 | } 10 | } 11 | 12 | fn main() { 13 | let foo: Foo; 14 | foo.bar(); // diag: error(not_callable_field_confusion): "`i32` is not callable (hint: fields have precedence over methods, if there is a method with the same name, you can call the it explicitly with `Type::method(...)`)" 15 | } 16 | -------------------------------------------------------------------------------- /tests/diag/not_enough_macro_args.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | macro foo($_a, $_b, $_c...) { 3 | 4 | } 5 | 6 | fn main() { 7 | foo!(1); // diag: error(not_enough_macro_arguments): "not enough macro arguments, at least 2 expected" 8 | } 9 | -------------------------------------------------------------------------------- /tests/diag/param_count_mismatch.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | libc::exit(1, 2, 3, 4); // diag: error(param_count_mismatch): "expected 1 arguments, found 4" 4 | } 5 | -------------------------------------------------------------------------------- /tests/diag/protocol_coroutine.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | protocol Foo { 3 | fn* foo(); // diag: error(protocol_coroutine): "coroutines cannot be protocol functions" 4 | } 5 | -------------------------------------------------------------------------------- /tests/diag/protocol_extern_fn.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | protocol Foo { 3 | extern "C" fn foo(); // diag: error(protocol_fns_cannot_be_extern): "protocol functions cannot be extern" 4 | } 5 | -------------------------------------------------------------------------------- /tests/diag/protocol_match.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn foo(_v: T) { // diag: error(protocol_match): "type `i32` matches `Numeric`, which it should not" 3 | } 4 | 5 | fn main() { 6 | foo(42); 7 | } 8 | -------------------------------------------------------------------------------- /tests/diag/protocol_mismatch.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn foo(_v: T) { // diag: error(protocol_mismatch): "type `&[u8]` does not match `Numeric`" 3 | } 4 | 5 | fn main() { 6 | foo("hello"); 7 | } 8 | -------------------------------------------------------------------------------- /tests/diag/recursive_macro.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | macro foo() { // diag: error(recursive_macro_call): "recursive macro calls are not allowed" 3 | foo!(); 4 | } 5 | -------------------------------------------------------------------------------- /tests/diag/recursive_protocol_bound.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | protocol Foo> { 3 | 4 | } 5 | 6 | protocol Bar> { // diag: error(cyclic_protocol_bound): "recursive protocol bounds are not supported" 7 | 8 | } 9 | 10 | fn main() { 11 | std::typing::matches::>(); 12 | } 13 | -------------------------------------------------------------------------------- /tests/diag/recursive_static_init.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | static FOO: i32 = BAR - 1; 3 | static BAR: i32 = FOO + 1; // diag: error(recursive_static_initialization): "cyclic dependency during static initialization" 4 | 5 | fn main() { 6 | FOO; 7 | } 8 | -------------------------------------------------------------------------------- /tests/diag/return_in_non_function_scope.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | const FOO = return 42; // diag: error(not_in_a_function_scope): "can only do that in function scope" 3 | 4 | fn main() { 5 | FOO; 6 | } 7 | -------------------------------------------------------------------------------- /tests/diag/struct_like_expected.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | let _ = main { // diag: error(struct_like_expected_here): "expected a struct-like type here" 4 | a: 16 5 | }; 6 | } 7 | -------------------------------------------------------------------------------- /tests/diag/syntax.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | 3 | fn main() { 4 | let a = 1 // diag: error(parse_error): "syntax error: unexpected `1`" 5 | a += "foo"; 6 | } 7 | -------------------------------------------------------------------------------- /tests/diag/too_many_enum_variants.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | enum Incredible { 3 | A0 = 0u8, 4 | A1 = 1, 5 | A2 = 2, 6 | A3 = 3, 7 | A4 = 4, 8 | A5 = 5, 9 | A6 = 6, 10 | A7 = 7, 11 | A8 = 8, 12 | A9 = 9, 13 | A10 = 10, 14 | A11 = 11, 15 | A12 = 12, 16 | A13 = 13, 17 | A14 = 14, 18 | A15 = 15, 19 | A16 = 16, 20 | A17 = 17, 21 | A18 = 18, 22 | A19 = 19, 23 | A20 = 20, 24 | A21 = 21, 25 | A22 = 22, 26 | A23 = 23, 27 | A24 = 24, 28 | A25 = 25, 29 | A26 = 26, 30 | A27 = 27, 31 | A28 = 28, 32 | A29 = 29, 33 | A30 = 30, 34 | A31 = 31, 35 | A32 = 32, 36 | A33 = 33, 37 | A34 = 34, 38 | A35 = 35, 39 | A36 = 36, 40 | A37 = 37, 41 | A38 = 38, 42 | A39 = 39, 43 | A40 = 40, 44 | A41 = 41, 45 | A42 = 42, 46 | A43 = 43, 47 | A44 = 44, 48 | A45 = 45, 49 | A46 = 46, 50 | A47 = 47, 51 | A48 = 48, 52 | A49 = 49, 53 | A50 = 50, 54 | A51 = 51, 55 | A52 = 52, 56 | A53 = 53, 57 | A54 = 54, 58 | A55 = 55, 59 | A56 = 56, 60 | A57 = 57, 61 | A58 = 58, 62 | A59 = 59, 63 | A60 = 60, 64 | A61 = 61, 65 | A62 = 62, 66 | A63 = 63, 67 | A64 = 64, 68 | A65 = 65, 69 | A66 = 66, 70 | A67 = 67, 71 | A68 = 68, 72 | A69 = 69, 73 | A70 = 70, 74 | A71 = 71, 75 | A72 = 72, 76 | A73 = 73, 77 | A74 = 74, 78 | A75 = 75, 79 | A76 = 76, 80 | A77 = 77, 81 | A78 = 78, 82 | A79 = 79, 83 | A80 = 80, 84 | A81 = 81, 85 | A82 = 82, 86 | A83 = 83, 87 | A84 = 84, 88 | A85 = 85, 89 | A86 = 86, 90 | A87 = 87, 91 | A88 = 88, 92 | A89 = 89, 93 | A90 = 90, 94 | A91 = 91, 95 | A92 = 92, 96 | A93 = 93, 97 | A94 = 94, 98 | A95 = 95, 99 | A96 = 96, 100 | A97 = 97, 101 | A98 = 98, 102 | A99 = 99, 103 | A100 = 100, 104 | A101 = 101, 105 | A102 = 102, 106 | A103 = 103, 107 | A104 = 104, 108 | A105 = 105, 109 | A106 = 106, 110 | A107 = 107, 111 | A108 = 108, 112 | A109 = 109, 113 | A110 = 110, 114 | A111 = 111, 115 | A112 = 112, 116 | A113 = 113, 117 | A114 = 114, 118 | A115 = 115, 119 | A116 = 116, 120 | A117 = 117, 121 | A118 = 118, 122 | A119 = 119, 123 | A120 = 120, 124 | A121 = 121, 125 | A122 = 122, 126 | A123 = 123, 127 | A124 = 124, 128 | A125 = 125, 129 | A126 = 126, 130 | A127 = 127, 131 | A128 = 128, 132 | A129 = 129, 133 | A130 = 130, 134 | A131 = 131, 135 | A132 = 132, 136 | A133 = 133, 137 | A134 = 134, 138 | A135 = 135, 139 | A136 = 136, 140 | A137 = 137, 141 | A138 = 138, 142 | A139 = 139, 143 | A140 = 140, 144 | A141 = 141, 145 | A142 = 142, 146 | A143 = 143, 147 | A144 = 144, 148 | A145 = 145, 149 | A146 = 146, 150 | A147 = 147, 151 | A148 = 148, 152 | A149 = 149, 153 | A150 = 150, 154 | A151 = 151, 155 | A152 = 152, 156 | A153 = 153, 157 | A154 = 154, 158 | A155 = 155, 159 | A156 = 156, 160 | A157 = 157, 161 | A158 = 158, 162 | A159 = 159, 163 | A160 = 160, 164 | A161 = 161, 165 | A162 = 162, 166 | A163 = 163, 167 | A164 = 164, 168 | A165 = 165, 169 | A166 = 166, 170 | A167 = 167, 171 | A168 = 168, 172 | A169 = 169, 173 | A170 = 170, 174 | A171 = 171, 175 | A172 = 172, 176 | A173 = 173, 177 | A174 = 174, 178 | A175 = 175, 179 | A176 = 176, 180 | A177 = 177, 181 | A178 = 178, 182 | A179 = 179, 183 | A180 = 180, 184 | A181 = 181, 185 | A182 = 182, 186 | A183 = 183, 187 | A184 = 184, 188 | A185 = 185, 189 | A186 = 186, 190 | A187 = 187, 191 | A188 = 188, 192 | A189 = 189, 193 | A190 = 190, 194 | A191 = 191, 195 | A192 = 192, 196 | A193 = 193, 197 | A194 = 194, 198 | A195 = 195, 199 | A196 = 196, 200 | A197 = 197, 201 | A198 = 198, 202 | A199 = 199, 203 | A200 = 200, 204 | A201 = 201, 205 | A202 = 202, 206 | A203 = 203, 207 | A204 = 204, 208 | A205 = 205, 209 | A206 = 206, 210 | A207 = 207, 211 | A208 = 208, 212 | A209 = 209, 213 | A210 = 210, 214 | A211 = 211, 215 | A212 = 212, 216 | A213 = 213, 217 | A214 = 214, 218 | A215 = 215, 219 | A216 = 216, 220 | A217 = 217, 221 | A218 = 218, 222 | A219 = 219, 223 | A220 = 220, 224 | A221 = 221, 225 | A222 = 222, 226 | A223 = 223, 227 | A224 = 224, 228 | A225 = 225, 229 | A226 = 226, 230 | A227 = 227, 231 | A228 = 228, 232 | A229 = 229, 233 | A230 = 230, 234 | A231 = 231, 235 | A232 = 232, 236 | A233 = 233, 237 | A234 = 234, 238 | A235 = 235, 239 | A236 = 236, 240 | A237 = 237, 241 | A238 = 238, 242 | A239 = 239, 243 | A240 = 240, 244 | A241 = 241, 245 | A242 = 242, 246 | A243 = 243, 247 | A244 = 244, 248 | A245 = 245, 249 | A246 = 246, 250 | A247 = 247, 251 | A248 = 248, 252 | A249 = 249, 253 | A250 = 250, 254 | A251 = 251, 255 | A252 = 252, 256 | A253 = 253, 257 | A254 = 254, 258 | A255 = 255, 259 | A256, // diag: error(too_many_enum_variants): "too many enum variants for underlying type `u8`" 260 | } 261 | 262 | 263 | fn main() { 264 | let _ = Incredible::A256; 265 | } 266 | -------------------------------------------------------------------------------- /tests/diag/tuple_call_arg_count.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | #[tuple_args] // diag: error(tuple_call_arg_count): "functions with #[tuple_args] must have a single argument" 3 | fn foo(a: i32, b: i32) { 4 | a + b 5 | } 6 | 7 | fn main() { 8 | foo(1, 2); 9 | } 10 | -------------------------------------------------------------------------------- /tests/diag/tuple_call_arg_type.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | #[tuple_args] // diag: error(tuple_call_arg_type): "the argument to a #[tuple_args] function must be a tuple" 3 | fn foo(_a: i32) { 4 | } 5 | 6 | fn main() { 7 | foo(1); 8 | } 9 | -------------------------------------------------------------------------------- /tests/diag/tuple_index_out_of_bounds.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | let a = (1, 2, 3); 4 | a.10; // diag: error(tuple_index_out_of_bounds): "tuple index out of bounds" 5 | } 6 | -------------------------------------------------------------------------------- /tests/diag/type_hint_required.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | let _foo = std::util::cast(5); // diag: error(type_hint_required): "type hint required" 4 | } 5 | -------------------------------------------------------------------------------- /tests/diag/type_mismatch.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | let a = 1; 4 | a = 2usize; // diag: error(type_mismatch): "type mismatch: `i32` expected, `usize` found" 5 | } 6 | -------------------------------------------------------------------------------- /tests/diag/typedef_impl.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | type Foo = i32; 3 | 4 | impl Foo { // diag: error(duplicate_name): "duplicate name `Foo`" 5 | 6 | } 7 | -------------------------------------------------------------------------------- /tests/diag/typedef_with_no_target.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | type Foo; // diag: error(typedef_without_target): "type alias must have a target" 3 | 4 | fn main() { 5 | let _: Foo; 6 | } 7 | -------------------------------------------------------------------------------- /tests/diag/unexpected_generic.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | struct A { 3 | foo: i32 4 | } 5 | 6 | fn main() { 7 | let a: A; 8 | 9 | a.foo::; // diag: error(unexpected_generic_args): "unexpected generic arguments (is this a method that needs to be called?)" 10 | } 11 | -------------------------------------------------------------------------------- /tests/diag/unknown_builtin_macro.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | #[builtin] macro absolve() {} // diag: error(unknown_builtin_macro): "unknown builtin macro `absolve`" 3 | -------------------------------------------------------------------------------- /tests/diag/unpopulated_item.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn goo() -> i32 { 3 | // This is an error for now - in theory we don't need to mono function bodies while 4 | // resolving typeof, but in reality, it could still happen, e.g. when resolving a constant 5 | foo() // diag: error(unpopulated_item): "cyclic dependency during type resolution" 6 | } 7 | 8 | fn foo() -> typeof(goo()) { 9 | 42 10 | } 11 | 12 | fn main() { 13 | foo(); 14 | } 15 | -------------------------------------------------------------------------------- /tests/diag/unresolved_item.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | struct Foo {} 3 | 4 | fn main() { 5 | let a: Foo; 6 | a.foo = 42; // diag: error(unresolved_item): "could not resolve item `foo`" 7 | } 8 | -------------------------------------------------------------------------------- /tests/diag/user_defined.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 0 2 | fn main() { 3 | std::compile_warn!("hello"); // diag: warning(user_defined): "hello" 4 | } 5 | -------------------------------------------------------------------------------- /tests/diag/var_args.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn foo(a: i32, ...) { // diag: error(var_args_can_only_be_extern): "varargs functions can only be extern" 3 | 4 | } 5 | -------------------------------------------------------------------------------- /tests/diag/warnings.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 0 2 | 3 | struct Foo { a: i32 } 4 | 5 | 6 | impl Foo { 7 | fn foo() {} 8 | } 9 | 10 | impl Foo { 11 | fn foo() {} // diag: warning(duplicate_name_shadow): "duplicate function name \"foo\" (this function will shadow a previous one)" 12 | 13 | fn hey(a: Self) {} // diag: warning(self_confusion): "this is `std::typing::Self`, did you mean the enclosing type?", warning(unused_parameter): "unused parameter `a` (prefix with `_` if required)" 14 | } 15 | 16 | #[align(1)] // diag: warning(align1): "`#[align(1)]` has no effect, did you mean to use `#[packed]`?" 17 | union Bar { a: i32, b: i32 } 18 | 19 | fn maybe() -> Result<(),()> { 20 | Result::ok(()) 21 | } 22 | 23 | {} // diag: warning(top_level_block_without_attributes): "redundant top level block (no attributes)" 24 | 25 | fn main() { 26 | let _f: std::builtins::Numeric; // diag: warning(protocols_are_special_mkay): "protocol is used as a concrete type (did you mean to use `&dyn Numeric`?)" 27 | const CONST = 42; 28 | static STATIC: i32 = 42; 29 | let _f: CONST; // diag: warning(invalid_type_for_value): "named constants used as a concrete type, this is probably not what you want" 30 | let _f: STATIC; // diag: warning(invalid_type_for_value): "statics used as a concrete type, this is probably not what you want" 31 | loop { defer {}; break; } // diag: warning(defer_in_a_loop): "defer inside a loop: this defered statement will only be executed once" 32 | let f = Foo {}; // diag: warning(uninitialized_field): "field `a` is not initialized" 33 | let f = Bar { a: 1, b: 2 }; // diag: warning(union_initializer_override): "initializer overrides prior initialization of this union" 34 | maybe(); // diag: warning(unused_must_use): "unused `Result<(), ()>` that must be used" 35 | let abcd: i32; // diag: warning(unused_variable): "unused variable `abcd`" 36 | let _ = |=f| {}; // diag: warning(unused_closure_binding): "unused closure binding `f`" 37 | fn foo (a: i32) {} // diag: warning(unused_parameter): "unused parameter `a` (prefix with `_` if required)" 38 | use std::io::Error; // diag: warning(unused_import): "unused import `Error`" 39 | #[allow(lol_what_is_this_even)] {} // diag: warning(im_so_meta_even_this_acronym): "#[allow(lol_what_is_this_even)] refers to a lint that does not currently exist" 40 | 41 | if true { println!("yay"); } // diag: warning(constant_condition): "condition is always `true`, did you mean to use `when`?" 42 | 43 | 42; // diag: warning(pure_statement): "statement has no effect" 44 | std::intrinsics::const_alloc::(42); // diag: warning(const_only): "const-only functions should not be used at runtime (guard with `std::runtime::in_const_context()`)" 45 | 46 | let a: (); 47 | &a + 100; // diag: warning(zst_pointer_offset): "pointer offset on zero-sized types is a no-op" 48 | 49 | #[sdhfkjsdf] {} // diag: warning(unknown_attribute): "unknown attribute `sdhfkjsdf`" 50 | 51 | let _ = 1i32 as i32; // diag: warning(unnecessary_cast): "unnecessary cast (value is already `i32`)" 52 | 53 | while 1 > 2 {}; // diag: warning(constant_condition): "condition is always `false`, did you mean to use `when`?" 54 | loop{} 55 | println!("Yay"); // diag: warning(dead_code): "this code is unreachable (dead code)" 56 | } 57 | -------------------------------------------------------------------------------- /tests/diag/yield_in_defer.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn* gen() -> Coroutine<()> { 3 | defer { 4 | yield // diag: error(yield_in_defer): "cannot yield inside a defered expression" 5 | } 6 | } 7 | 8 | fn main() { 9 | gen(); 10 | } 11 | -------------------------------------------------------------------------------- /tests/diag/yield_outside_coroutine.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | yield; // diag: error(yield_outside_of_coroutine): "yield can only be used in a coroutine" 4 | } 5 | -------------------------------------------------------------------------------- /tests/diag/zst_pointer_difference.alu: -------------------------------------------------------------------------------- 1 | //! exit_code: 1 2 | fn main() { 3 | let a: (); 4 | let b: (); 5 | 6 | &a - &b; // diag: error(zst_pointer_difference): "pointer difference on zero-sized types is meaningless" 7 | } 8 | -------------------------------------------------------------------------------- /tools/alumina-doc/error.alu: -------------------------------------------------------------------------------- 1 | use std::string::StringBuf; 2 | 3 | type Result = std::result::Result; 4 | 5 | struct Error { 6 | inner: StringBuf, 7 | } 8 | 9 | impl Error { 10 | use std::fmt::{Formatter, write}; 11 | 12 | fn new(msg: &[u8]) -> Error { 13 | Error { 14 | inner: StringBuf::from_slice(msg), 15 | } 16 | } 17 | 18 | fn from(err: T) -> Error { 19 | anywho!("{}", err) 20 | } 21 | 22 | fn fmt>(self: &Error, f: &mut F) -> std::fmt::Result { 23 | write!(f, "{}", self.inner.as_slice()) 24 | } 25 | 26 | fn free(self: &mut Error) { 27 | self.inner.free(); 28 | } 29 | 30 | fn move(self: &mut Error) -> Error { 31 | Error { inner: self.inner.move() } 32 | } 33 | } 34 | 35 | macro from_io($a) { 36 | $a.map_err(Error::from::) 37 | } 38 | 39 | macro from_fmt($a) { 40 | $a.map_err(Error::from::) 41 | } 42 | 43 | macro anywho($fmt, $args...) { 44 | use std::fmt::format; 45 | Error { inner: format!($fmt, $args$...).unwrap() } 46 | } 47 | 48 | macro bail($fmt, $args...) { 49 | return Result::err(anywho!($fmt, $args$...)); 50 | } 51 | -------------------------------------------------------------------------------- /tools/alumina-doc/static/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alumina-lang/alumina/ee360b9878523f927b791bc08178505b50270655/tools/alumina-doc/static/android-chrome-192x192.png -------------------------------------------------------------------------------- /tools/alumina-doc/static/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alumina-lang/alumina/ee360b9878523f927b791bc08178505b50270655/tools/alumina-doc/static/android-chrome-512x512.png -------------------------------------------------------------------------------- /tools/alumina-doc/static/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alumina-lang/alumina/ee360b9878523f927b791bc08178505b50270655/tools/alumina-doc/static/apple-touch-icon.png -------------------------------------------------------------------------------- /tools/alumina-doc/static/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #da532c 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /tools/alumina-doc/static/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alumina-lang/alumina/ee360b9878523f927b791bc08178505b50270655/tools/alumina-doc/static/favicon-16x16.png -------------------------------------------------------------------------------- /tools/alumina-doc/static/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alumina-lang/alumina/ee360b9878523f927b791bc08178505b50270655/tools/alumina-doc/static/favicon-32x32.png -------------------------------------------------------------------------------- /tools/alumina-doc/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alumina-lang/alumina/ee360b9878523f927b791bc08178505b50270655/tools/alumina-doc/static/favicon.ico -------------------------------------------------------------------------------- /tools/alumina-doc/static/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 13 | 15 | 17 | 18 | 20 | image/svg+xml 21 | 23 | 24 | 25 | 26 | 27 | 29 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /tools/alumina-doc/static/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Alumina Documentation", 3 | "short_name": "Alumina Documentation", 4 | "icons": [ 5 | { 6 | "src": "/static/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/static/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ffffff", 17 | "background_color": "#ffffff", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /tools/alumina-doc/static/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alumina-lang/alumina/ee360b9878523f927b791bc08178505b50270655/tools/alumina-doc/static/mstile-144x144.png -------------------------------------------------------------------------------- /tools/alumina-doc/static/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alumina-lang/alumina/ee360b9878523f927b791bc08178505b50270655/tools/alumina-doc/static/mstile-150x150.png -------------------------------------------------------------------------------- /tools/alumina-doc/static/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alumina-lang/alumina/ee360b9878523f927b791bc08178505b50270655/tools/alumina-doc/static/mstile-310x150.png -------------------------------------------------------------------------------- /tools/alumina-doc/static/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alumina-lang/alumina/ee360b9878523f927b791bc08178505b50270655/tools/alumina-doc/static/mstile-310x310.png -------------------------------------------------------------------------------- /tools/alumina-doc/static/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alumina-lang/alumina/ee360b9878523f927b791bc08178505b50270655/tools/alumina-doc/static/mstile-70x70.png -------------------------------------------------------------------------------- /tools/alumina-doc/static/prism-alumina.js: -------------------------------------------------------------------------------- 1 | (function (Prism) { 2 | 3 | var multilineComment = /\/\*(?:[^*/]|\*(?!\/)|\/(?!\*)|)*\*\//.source; 4 | for (var i = 0; i < 2; i++) { 5 | // support 4 levels of nested comments 6 | multilineComment = multilineComment.replace(//g, function () { return multilineComment; }); 7 | } 8 | multilineComment = multilineComment.replace(//g, function () { return /[^\s\S]/.source; }); 9 | 10 | 11 | Prism.languages.alumina = { 12 | 'comment': [ 13 | { 14 | pattern: RegExp(/(^|[^\\])/.source + multilineComment), 15 | lookbehind: true, 16 | greedy: true 17 | }, 18 | { 19 | pattern: /(^|[^\\:])\/\/.*/, 20 | lookbehind: true, 21 | greedy: true 22 | } 23 | ], 24 | 'string': { 25 | pattern: /"(?:\\[\s\S]|[^\\"])*"|b?r(#*)"(?:[^"]|"(?!\1))*"\1/, 26 | greedy: true 27 | }, 28 | 'char': { 29 | pattern: /'(?:\\(?:x[0-7][\da-fA-F]|u\{(?:[\da-fA-F]_*){1,6}\}|.)|[^\\\r\n\t'])'/, 30 | greedy: true 31 | }, 32 | 'attribute': { 33 | pattern: /#!?\[(?:[^\[\]"]|"(?:\\[\s\S]|[^\\"])*")*\]/, 34 | greedy: true, 35 | alias: 'attr-name', 36 | inside: { 37 | 'string': null // see below 38 | } 39 | }, 40 | 41 | // Closure params should not be confused with bitwise OR | 42 | 'closure-params': { 43 | pattern: /([=(,:]\s*)\|[^|]*\||\|[^|]*\|(?=\s*(?:\{|->))/, 44 | lookbehind: true, 45 | greedy: true, 46 | inside: { 47 | 'closure-punctuation': { 48 | pattern: /^\||\|$/, 49 | alias: 'punctuation' 50 | }, 51 | rest: null // see below 52 | } 53 | }, 54 | 55 | 'fragment-specifier': { 56 | pattern: /(\$\w+:)[a-z]+/, 57 | lookbehind: true, 58 | alias: 'punctuation' 59 | }, 60 | 'variable': /\$\w+/, 61 | 62 | 'function-definition': { 63 | pattern: /(\bfn\s+)\w+/, 64 | lookbehind: true, 65 | alias: 'function' 66 | }, 67 | 'type-definition': { 68 | pattern: /(\b(?:enum|struct|protocol|type|union)\s+)\w+/, 69 | lookbehind: true, 70 | alias: 'class-name' 71 | }, 72 | 'module-declaration': [ 73 | { 74 | pattern: /(\b(?:crate|mod)\s+)[a-z][a-z_\d]*/, 75 | lookbehind: true, 76 | alias: 'namespace' 77 | }, 78 | { 79 | pattern: /(\b(?:crate|self|super)\s*)::\s*[a-z][a-z_\d]*\b(?:\s*::(?:\s*[a-z][a-z_\d]*\s*::)*)?/, 80 | lookbehind: true, 81 | alias: 'namespace', 82 | inside: { 83 | 'punctuation': /::/ 84 | } 85 | } 86 | ], 87 | 'keyword': [ 88 | /\b(?:Self|const|dyn|enum|extern|[Ff]n|impl|let|macro|mod|mut|self|static|struct|mixin|protocol|try|type|union|use|typeof|as|is)\b/, 89 | /\b(?:null|void|bool|f(?:32|64)|[ui](?:8|16|32|64|128|size))\b/ 90 | ], 91 | 'control-keyword': [ 92 | /\b(?:break|continue|else|for|if|in|defer|loop|switch|return|yield|when|while)\b/, 93 | ], 94 | 95 | 'function': /\b[a-z_]\w*(?=\s*(?:::\s*<|\())/, 96 | 'macro': { 97 | pattern: /\b\w+!/, 98 | alias: 'property' 99 | }, 100 | 'constant': /\b[A-Z_][A-Z_\d]+\b/, 101 | 'class-name': /\b[A-Z]\w*\b/, 102 | 103 | 'namespace': { 104 | pattern: /(?:\b[a-z][a-z_\d]*\s*::\s*)*\b[a-z][a-z_\d]*\s*::(?!\s*<)/, 105 | inside: { 106 | 'punctuation': /::/ 107 | } 108 | }, 109 | 110 | 'number': /\b(?:0x[\dA-Fa-f](?:_?[\dA-Fa-f])*|0o[0-7](?:_?[0-7])*|0b[01](?:_?[01])*|(?:(?:\d(?:_?\d)*)?\.)?\d(?:_?\d)*(?:[Ee][+-]?\d+)?)(?:_?(?:f32|f64|[iu](?:8|16|32|64|size)?))?\b/, 111 | 'boolean': /\b(?:false|true)\b/, 112 | 'punctuation': /->|\.\.=|\.{1,3}|::|[{}[\];(),:]/, 113 | 'operator': /[-+*\/%!^]=?|=[=>]?|&[&=]?|\|[|=]?|<>?=?|[@?]/ 114 | }; 115 | 116 | Prism.languages.alumina['closure-params'].inside.rest = Prism.languages.alumina; 117 | Prism.languages.alumina['attribute'].inside['string'] = Prism.languages.alumina['string']; 118 | 119 | }(Prism)); 120 | -------------------------------------------------------------------------------- /tools/alumina-doc/static/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.14, written by Peter Selinger 2001-2017 9 | 10 | 12 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /tools/alumina-doc/static/site.js: -------------------------------------------------------------------------------- 1 | window.updateSearchResults = () => {}; 2 | window.loadSearchIndex = (idx) => { 3 | window.searchIndex = idx; 4 | window.updateSearchResults(); 5 | }; 6 | window.debounceTimer = null; 7 | window.addEventListener("load", () => { 8 | const mainContent = document.getElementById("main-content"); 9 | const searchResults = document.getElementById("search-results"); 10 | const searchBox = document.getElementById("search-input"); 11 | 12 | const queryParams = new URLSearchParams(window.location.search); 13 | const initialSearch = queryParams.get("q"); 14 | if (initialSearch) { 15 | searchBox.value = initialSearch; 16 | } 17 | 18 | window.updateSearchResults = () => { 19 | if (searchBox.value !== "") { 20 | searchResults.style.display = 'block'; 21 | mainContent.style.display = 'none'; 22 | } else { 23 | searchResults.style.display = 'none'; 24 | mainContent.style.display = 'block'; 25 | return; 26 | } 27 | 28 | if (!window.searchIndex) { 29 | searchResults.innerText = "Loading search index..."; 30 | return; 31 | } 32 | 33 | const table = document.createElement("div"); 34 | table.className = "search-results-table"; 35 | 36 | const needles = searchBox.value.split(" ") 37 | .map(s => s.trim().toLowerCase()) 38 | .filter(s => !!s); 39 | 40 | const matcher = (s) => { 41 | const haystack = s.toLowerCase(); 42 | for (var index = 0; index < needles.length; index++) { 43 | if (!haystack.includes(needles[index])) { 44 | return false; 45 | } 46 | } 47 | return true; 48 | }; 49 | 50 | const results = window.searchIndex 51 | .filter(([path, doc, __]) => matcher(path)); 52 | 53 | // Put the exact matches to the top 54 | const exactMatchSuffix = `::${searchBox.value}`.toLowerCase(); 55 | const exactMatcher = (s) => s === searchBox.value || s.toLowerCase().endsWith(exactMatchSuffix); 56 | 57 | results.sort(([a, _, __], [b, ___, ____]) => { 58 | if (exactMatcher(a) ^ exactMatcher(b)) { 59 | return exactMatcher(a) ? -1 : 1 60 | } else { 61 | return 0 62 | } 63 | }); 64 | 65 | const hasMore = results.splice(500).length > 0; 66 | results.forEach(([path, doc, href]) => { 67 | const child = document.createElement("div"); 68 | child.className = "row"; 69 | const pathCell = document.createElement("div"); 70 | pathCell.className = "cell-name"; 71 | const link = document.createElement("a"); 72 | link.href = href; 73 | link.innerText = path; 74 | pathCell.appendChild(link); 75 | const docCell = document.createElement("div"); 76 | docCell.className = "cell-doc"; 77 | docCell.innerText = doc; 78 | child.appendChild(pathCell); 79 | child.appendChild(docCell); 80 | table.appendChild(child); 81 | }); 82 | 83 | if (table.children.length == 0) { 84 | searchResults.innerText = "No results"; 85 | } else { 86 | searchResults.innerText = ""; 87 | searchResults.appendChild(table); 88 | if (hasMore) { 89 | searchResults.appendChild(document.createTextNode("More found, refine your search.")); 90 | } 91 | } 92 | }; 93 | 94 | 95 | searchBox.addEventListener("input", (evt) => { 96 | clearTimeout(window.debounceTimer); 97 | if (searchBox.value) { 98 | history.replaceState(null, null, `?q=${encodeURIComponent(searchBox.value)}`); 99 | window.debounceTimer = setTimeout(window.updateSearchResults, 200); 100 | } else { 101 | history.replaceState(null, null, window.location.href.split("?")[0]); 102 | window.updateSearchResults(); 103 | } 104 | }); 105 | 106 | window.updateSearchResults(); 107 | }); 108 | -------------------------------------------------------------------------------- /tools/alumina-doc/watch_docs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if ! command -v inotifywait &> /dev/null; then 4 | echo "inotifywait could not be found (inotify-utils)" 5 | exit 1 6 | fi 7 | 8 | serve() { 9 | cd $BUILD_DIR/html 10 | python3 -m http.server 11 | } 12 | 13 | serve & 14 | 15 | while true; do 16 | while ! make -q docs; do 17 | make docs || break 18 | done 19 | inotifywait --exclude '^(\./)?(target/|build/)' -qre close_write .; 20 | done 21 | -------------------------------------------------------------------------------- /tools/bench.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | Simple benchmarking tool that collects timings from the compiler run multiple 5 | times and prints the per-stage results. 6 | """ 7 | 8 | import sys 9 | import re 10 | import subprocess 11 | import argparse 12 | 13 | from collections import defaultdict 14 | 15 | def main(): 16 | parser = argparse.ArgumentParser(description=__doc__) 17 | parser.add_argument( 18 | "-n", "--num-runs", type=int, default=10, 19 | help="Number of times to run the compiler", 20 | ) 21 | parser.add_argument( 22 | "-m", "--markdown", action="store_true", help="Output in markdown format" 23 | ) 24 | parser.add_argument( 25 | "cmd", nargs=argparse.REMAINDER, help="Command to run the compiler" 26 | ) 27 | args = parser.parse_args() 28 | 29 | timings = defaultdict(list) 30 | 31 | for i in range(args.num_runs): 32 | print(f"Running {i+1}/{args.num_runs}...") 33 | 34 | proc = subprocess.run( 35 | args.cmd, 36 | stdin=subprocess.DEVNULL, 37 | stderr=subprocess.PIPE, 38 | stdout=subprocess.DEVNULL, 39 | ) 40 | if proc.returncode != 0: 41 | print(f"Compiler failed with code {proc.returncode}") 42 | sys.exit(1) 43 | 44 | for line in proc.stderr.decode("utf-8").splitlines(): 45 | m = re.search(r"(\w+) took (\d+)ms", line) 46 | if m: 47 | stage = m.group(1) 48 | timing = int(m.group(2)) 49 | if stage == "TOTAL" and args.markdown: 50 | stage = "**TOTAL**" 51 | 52 | timings[stage].append(timing) 53 | 54 | if args.markdown: 55 | print( 56 | "| Stage | Median time (ms) | Min time (ms) | Max time (ms) | Average time (ms) | Standard deviation (ms) |" 57 | ) 58 | print( 59 | "| --- | --: | --: | --: | --: | --: |" 60 | ) 61 | 62 | for stage, times in timings.items(): 63 | times = sorted(times) 64 | 65 | p50 = times[len(times) // 2] 66 | min = times[0] 67 | max = times[-1] 68 | avg = sum(times) / len(times) 69 | stddev = (sum((x - avg) ** 2 for x in times) / len(times)) ** 0.5 70 | 71 | if args.markdown: 72 | print(f"| {stage} | {p50} | {min} | {max} | {avg:.2f} | {stddev:.2f} |") 73 | else: 74 | print( 75 | f"{stage}: {p50}ms (min {min}ms, max {max}ms, avg {avg:.2f}ms, stddev {stddev:.2f}ms)" 76 | ) 77 | 78 | 79 | if __name__ == "__main__": 80 | main() 81 | -------------------------------------------------------------------------------- /tools/cloc_language_def.txt: -------------------------------------------------------------------------------- 1 | Alumina 2 | filter rm_comments_in_strings " /* */ 3 | filter rm_comments_in_strings " // 4 | filter call_regexp_common C++ 5 | extension alu 6 | 3rd_gen_scale 1.00 7 | -------------------------------------------------------------------------------- /tools/diag.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | Test runner for compile-time (diagnostics) tests. It takes the expected compile errors/warnings/notes from comments 5 | in source files and matches them against the actual compilation results. 6 | """ 7 | 8 | import sys 9 | import re 10 | import subprocess 11 | import argparse 12 | import json 13 | import logging 14 | import sys 15 | import dataclasses 16 | import functools 17 | 18 | from collections import defaultdict 19 | 20 | logging.basicConfig(level=logging.INFO) 21 | logger = logging.getLogger(__name__) 22 | 23 | 24 | @functools.total_ordering 25 | @dataclasses.dataclass 26 | class Diagnostic: 27 | level: str 28 | kind: str 29 | message: str 30 | 31 | def __eq__(self, other): 32 | return self.level == other.level and self.kind == other.kind 33 | 34 | def __lt__(self, other): 35 | return self.level < other.level or (self.level == other.level and self.kind < other.kind) 36 | 37 | 38 | def main(): 39 | parser = argparse.ArgumentParser(description=__doc__) 40 | parser.add_argument("file", help="File to test") 41 | parser.add_argument( 42 | "--fix", 43 | action="store_true", 44 | help="Fix files instead of checking them", 45 | ) 46 | parser.add_argument("cmd", nargs=argparse.REMAINDER, help="Command to run the compiler") 47 | args = parser.parse_args() 48 | 49 | with open(args.file) as f: 50 | contents = f.read().strip() 51 | 52 | extra_args = [] 53 | 54 | directives = re.findall(r"^//!\s*([a-zA-Z0-9_]+):\s*(.*)$", contents, flags=re.MULTILINE) 55 | expected_exit_code = 0 56 | 57 | for key, value in directives: 58 | if key == "extra_args": 59 | extra_args.extend(json.loads(value)) 60 | if key == "exit_code": 61 | expected_exit_code = json.loads(value) 62 | 63 | command = [*args.cmd, "-Zdiag-report", *extra_args, f"main={args.file}"] 64 | 65 | logger.debug(f"Executing {command}") 66 | proc = subprocess.run( 67 | command, 68 | stdin=subprocess.DEVNULL, 69 | stderr=subprocess.PIPE, 70 | stdout=subprocess.DEVNULL, 71 | ) 72 | 73 | errors = defaultdict(list) 74 | 75 | try: 76 | results = json.loads(proc.stderr) 77 | except: 78 | logger.error(proc.stderr) 79 | sys.exit(1) 80 | 81 | for error in results: 82 | for span in error["backtrace"]: 83 | if span["file"] == args.file: 84 | errors[span["line"] - 1].append( 85 | Diagnostic(error["level"], error["kind"], error["message"]) 86 | ) 87 | break 88 | 89 | for k in errors: 90 | errors[k].sort() 91 | 92 | if args.fix: 93 | contents = re.sub(r"\s*// diag: .*$", "", contents, flags=re.MULTILINE) 94 | with open(args.file, "w") as f: 95 | if extra_args: 96 | f.write(f"//! extra_args: {json.dumps(extra_args)}\n") 97 | 98 | f.write(f"//! exit_code: {proc.returncode}\n") 99 | 100 | for line_num, s in enumerate(contents.split("\n")): 101 | if re.match(r"^//!\s*([a-zA-Z0-9_]+):\s*(.*)$", s): 102 | continue 103 | 104 | f.write(s) 105 | if errors[line_num]: 106 | diags = ", ".join( 107 | f"{diag.level}({diag.kind}): {json.dumps(diag.message)}" 108 | for diag in errors[line_num] 109 | ) 110 | f.write(f" // diag: {diags}") 111 | f.write("\n") 112 | else: 113 | success = True 114 | if proc.returncode != expected_exit_code: 115 | print( 116 | f"{args.file}: exit code mismatch ({proc.returncode} != {expected_exit_code})", 117 | file=sys.stderr, 118 | ) 119 | success = False 120 | 121 | for line_num, s in enumerate(contents.split("\n")): 122 | if match := re.fullmatch(r"^.*\s*// diag: (.*)$", s): 123 | diags = list( 124 | sorted( 125 | Diagnostic(vals[0], vals[1], json.loads(vals[2])) 126 | for vals in re.findall( 127 | r'([a-z]+)\(([a-z0-9_]+)\): ("(.*?)(?"] 23 | ], 24 | "indentationRules": { 25 | "increaseIndentPattern": "^.*\\{[^}\"']*$|^.*\\([^\\)\"']*$", 26 | "decreaseIndentPattern": "^\\s*(\\s*\\/[*].*[*]\\/\\s*)*[})]" 27 | }, 28 | "folding": { 29 | "markers": { 30 | "start": "^\\s*//\\s*#?region\\b", 31 | "end": "^\\s*//\\s*#?endregion\\b" 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tools/vscode-highlighting/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "alumina", 3 | "displayName": "Alumina", 4 | "description": "Language support for Alumina programming language", 5 | "icon": "images/icon.png", 6 | "publisher": "tibordp", 7 | "version": "0.0.5", 8 | "engines": { 9 | "vscode": "^1.63.0" 10 | }, 11 | "categories": [ 12 | "Programming Languages" 13 | ], 14 | "contributes": { 15 | "languages": [ 16 | { 17 | "id": "alumina", 18 | "aliases": [ 19 | "Alumina", 20 | "alumina" 21 | ], 22 | "extensions": [ 23 | ".alu" 24 | ], 25 | "configuration": "./language-configuration.json" 26 | } 27 | ], 28 | "grammars": [ 29 | { 30 | "language": "alumina", 31 | "scopeName": "source.alumina", 32 | "path": "./syntaxes/alumina.tmLanguage.json" 33 | } 34 | ] 35 | }, 36 | "repository": { 37 | "type": "git", 38 | "url": "https://github.com/alumina-lang/alumina.git" 39 | }, 40 | "homepage": "https://github.com/alumina-lang/alumina" 41 | } 42 | --------------------------------------------------------------------------------