├── .clog.toml ├── .github └── workflows │ ├── release.yml │ └── rust.yml ├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Cargo.lock ├── Cargo.toml ├── Cross.toml ├── LICENSE ├── README.md ├── appveyor.yml ├── base ├── Cargo.toml ├── src │ ├── ast.rs │ ├── error.rs │ ├── fixed.rs │ ├── fnv.rs │ ├── kind.rs │ ├── lib.rs │ ├── macros.rs │ ├── merge.rs │ ├── metadata.rs │ ├── pos.rs │ ├── resolve.rs │ ├── scoped_map.rs │ ├── serialization.rs │ ├── source.rs │ ├── symbol.rs │ └── types │ │ ├── flags.rs │ │ ├── mod.rs │ │ └── pretty_print.rs └── tests │ ├── compile-fail │ ├── arena-1.rs │ └── arena-2.rs │ ├── compiletest.rs │ └── types.rs ├── benches ├── check.rs ├── function_call.rs └── precompiled.rs ├── book ├── .gitignore ├── book.toml └── src │ ├── SUMMARY.md │ ├── anatomy-of-a-gluon-program.md │ ├── dissecting-hello-world.md │ ├── embedding-api.md │ ├── extending-the-guessing-game-with-effects.md │ ├── getting-started.md │ ├── marshalling-types.md │ ├── metadata.md │ ├── modules.md │ ├── standard-types-and-functions.md │ ├── syntax-and-semantics.md │ └── using-the-repl.md ├── build.rs ├── c-api ├── Cargo.toml └── src │ └── lib.rs ├── check ├── Cargo.toml ├── src │ ├── implicits.rs │ ├── kindcheck.rs │ ├── lib.rs │ ├── metadata.rs │ ├── recursion_check.rs │ ├── rename.rs │ ├── substitution.rs │ ├── typ.rs │ ├── typecheck.rs │ ├── typecheck │ │ ├── error.rs │ │ ├── generalize.rs │ │ └── mod_type.rs │ ├── unify.rs │ └── unify_type.rs └── tests │ ├── effect.rs │ ├── error_recovery.rs │ ├── fail.rs │ ├── forall.rs │ ├── gadt.rs │ ├── implicits.rs │ ├── metadata.rs │ ├── pass.rs │ ├── recursive.rs │ ├── row_polymorphism.rs │ ├── snapshots │ ├── fail__alias_mismatch.snap │ ├── fail__multiple_extra_parameters_error.snap │ ├── fail__no_inference_variable_in_error.snap │ ├── fail__unable_to_resolve_implicit_error_message.snap │ └── fail__unification_error_with_empty_record_displays_good_error_message.snap │ ├── stack_overflow.rs │ ├── support │ └── mod.rs │ └── type_projection.rs ├── codegen ├── Cargo.toml ├── src │ ├── arena_clone.rs │ ├── ast_clone.rs │ ├── attr.rs │ ├── functor.rs │ ├── getable.rs │ ├── lib.rs │ ├── pushable.rs │ ├── shared.rs │ ├── trace.rs │ ├── userdata.rs │ └── vm_type.rs └── tests │ ├── derive_getable.rs │ ├── derive_pushable.rs │ ├── derive_userdata.rs │ ├── derive_vm_type.rs │ ├── full.rs │ └── init │ └── mod.rs ├── completion ├── Cargo.toml ├── src │ └── lib.rs └── tests │ ├── completion.rs │ ├── metadata.rs │ ├── signature_help.rs │ ├── suggest.rs │ └── support │ └── mod.rs ├── doc ├── Cargo.toml ├── build.rs ├── src │ ├── doc │ │ ├── module.html │ │ └── style.css │ ├── lib.rs │ └── main.rs └── tests │ └── doc.rs ├── examples ├── 24.glu ├── 24.rs ├── fib.rs ├── http │ ├── main.rs │ └── server.glu ├── lisp │ ├── lisp.glu │ ├── main.rs │ ├── parser.glu │ └── types.glu └── marshalling.rs ├── format ├── Cargo.toml ├── src │ ├── lib.rs │ └── pretty_print.rs └── tests │ ├── pretty_print.rs │ └── std.rs ├── parser ├── Cargo.toml ├── benches │ └── parser.rs ├── build.rs ├── src │ ├── grammar.lalrpop │ ├── infix.rs │ ├── layout.rs │ ├── lib.rs │ ├── str_suffix.rs │ └── token.rs └── tests │ ├── attributes.rs │ ├── basic.rs │ ├── error_handling.rs │ ├── indentation.rs │ ├── stack_overflow.rs │ ├── support │ └── mod.rs │ └── types.rs ├── repl ├── Cargo.toml ├── build.rs ├── src │ ├── main.rs │ ├── repl.glu │ └── repl.rs └── tests │ ├── basic.rs │ ├── print.glu │ └── rexpect.rs ├── scripts ├── before_deploy.sh ├── ci.sh ├── install.sh ├── install_cross.sh ├── install_mdbook.sh ├── install_sccache.sh ├── publish.sh ├── release.sh ├── sync_publish.sh └── version.sh ├── src ├── compiler_pipeline.rs ├── import.rs ├── lib.rs ├── lift_io.rs ├── query.rs ├── std_lib.rs └── std_lib │ ├── env.rs │ ├── http.rs │ ├── io.rs │ ├── process.rs │ ├── random.rs │ └── regex.rs ├── std ├── alternative.glu ├── applicative.glu ├── array.glu ├── assert.glu ├── bool.glu ├── byte.glu ├── category.glu ├── channel.glu ├── char.glu ├── cmp.glu ├── debug.glu ├── disposable.glu ├── effect.glu ├── effect │ ├── alt.glu │ ├── error.glu │ ├── io.glu │ ├── io │ │ ├── read.glu │ │ └── write.glu │ ├── lift.glu │ ├── reader.glu │ ├── reference.glu │ ├── st.glu │ ├── st │ │ └── string.glu │ ├── state.glu │ └── writer.glu ├── env.glu ├── float.glu ├── foldable.glu ├── fs.glu ├── function.glu ├── functor.glu ├── group.glu ├── http.glu ├── http │ └── types.glu ├── identity.glu ├── int.glu ├── io.glu ├── io │ ├── base.glu │ ├── read.glu │ └── write.glu ├── json.glu ├── json │ ├── de.glu │ └── ser.glu ├── lazy.glu ├── lazyt.glu ├── list.glu ├── map.glu ├── monad.glu ├── monoid.glu ├── num.glu ├── option.glu ├── parser.glu ├── path.glu ├── path │ └── types.glu ├── prelude.glu ├── process.glu ├── random.glu ├── reference.glu ├── regex.glu ├── regex │ └── types.glu ├── result.glu ├── semigroup.glu ├── show.glu ├── state.glu ├── statet.glu ├── stream.glu ├── string.glu ├── test.glu ├── thread.glu ├── transformer.glu ├── traversable.glu ├── types.glu ├── unit.glu └── writer.glu ├── tests ├── api.rs ├── array.rs ├── compile-fail │ ├── get-reference.rs │ ├── getable-reference-str.rs │ ├── getable-reference.rs │ ├── run_expr_str_ref.rs │ └── store-ref.rs ├── compiletest.rs ├── de.rs ├── debug.rs ├── error.rs ├── fail.rs ├── fail │ ├── cyclic_dependency.glu │ ├── deps │ │ └── cyclic_dependency2.glu │ └── unwrap.glu ├── inline.rs ├── io.rs ├── limits.rs ├── main.rs ├── metadata.rs ├── optimize │ ├── cmp.glu │ ├── inline_num.glu │ ├── inline_through_module.glu │ └── inline_through_module2.glu ├── parallel.rs ├── pass │ ├── alternative.glu │ ├── arithmetic.glu │ ├── buffered_io.glu │ ├── channel.glu │ ├── char.glu │ ├── deep_clone_userdata.glu │ ├── derive.glu │ ├── io.glu │ ├── json │ │ ├── de.glu │ │ └── ser.glu │ ├── lazy.glu │ ├── lazyt.glu │ ├── lisp.glu │ ├── list.glu │ ├── map.glu │ ├── match_literal.glu │ ├── parser.glu │ ├── path.glu │ ├── reference.glu │ ├── regex.glu │ ├── state.glu │ ├── statet.glu │ ├── stream.glu │ ├── string.glu │ ├── thread.glu │ ├── unwrap.glu │ └── writer.glu ├── pattern_match.rs ├── row_polymorphism.rs ├── safety.rs ├── serialization.rs ├── skeptic-template.md ├── skeptic-tests.rs ├── snapshots │ └── ui__macro_error_with_line_column_info.snap ├── stack_overflow.rs ├── support │ └── mod.rs ├── tutorial.rs ├── ui.rs ├── unrelated_type_error.glu └── vm.rs └── vm ├── Cargo.toml ├── build.rs └── src ├── api ├── de.rs ├── function.rs ├── json.rs ├── mac.rs ├── mod.rs ├── opaque.rs ├── record.rs ├── scoped.rs ├── ser.rs └── typ.rs ├── array.rs ├── channel.rs ├── compiler.rs ├── core ├── costs.rs ├── dead_code.rs ├── grammar.lalrpop ├── interpreter.rs ├── mod.rs ├── optimize.rs ├── pretty.rs └── purity.rs ├── debug.rs ├── derive ├── deserialize.rs ├── eq.rs ├── mod.rs ├── serialize.rs └── show.rs ├── dynamic.rs ├── gc.rs ├── gc └── mutex.rs ├── interner.rs ├── lazy.rs ├── lib.rs ├── macros.rs ├── primitives.rs ├── reference.rs ├── serialization.rs ├── source_map.rs ├── stack.rs ├── thread.rs ├── types.rs ├── value.rs └── vm.rs /.clog.toml: -------------------------------------------------------------------------------- 1 | [clog] 2 | repository = "https://github.com/gluon-lang/gluon" 3 | 4 | changelog = "CHANGELOG.md" 5 | 6 | from-latest-tag = true 7 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | on: 3 | push: 4 | tags: 5 | - '*' 6 | jobs: 7 | release: 8 | runs-on: ubuntu-latest 9 | - name: release 10 | uses: actions/create-release@v1 11 | id: create_release 12 | with: 13 | draft: false 14 | prerelease: false 15 | release_name: ${{ steps.version.outputs.version }} 16 | tag_name: ${{ github.ref }} 17 | body_path: CHANGELOG.md 18 | env: 19 | GITHUB_TOKEN: ${{ github.token }} 20 | # Only run deployment on git tags 21 | - CURRENT_TAG=`git describe --exact-match --abbrev=0 --tags || true` 22 | - > 23 | if [[ $CURRENT_TAG != "" ]]; then 24 | export GIT_HASH=$(git rev-parse HEAD) 25 | sh scripts/before_deploy.sh 26 | fi 27 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - master 7 | jobs: 8 | test: 9 | name: test 10 | runs-on: ubuntu-latest 11 | strategy: 12 | matrix: 13 | rust: [stable, nightly] 14 | env: 15 | CRATE_NAME: gluon 16 | CARGO_INCREMENTAL: 0 # Incremental compilation is slower and bloats the cache 17 | RUST_BACKTRACE: 1 18 | # RUSTC_WRAPPER: sccache 19 | SCCACHE_CACHE_SIZE: 500M 20 | steps: 21 | - name: Checkout repository 22 | uses: actions/checkout@v2 23 | - name: Use cache 24 | uses: actions/cache@v2 25 | with: 26 | path: | 27 | ~/bin 28 | key: ${{ runner.os }}-${{ matrix.rust }} 29 | - name: Install Rust 30 | uses: hecrj/setup-rust-action@v1 31 | with: 32 | rust-version: ${{ matrix.rust }} 33 | - uses: Swatinem/rust-cache@v2 34 | - run: echo "$HOME/bin" >> $GITHUB_PATH 35 | - run: mkdir -p $HOME/bin 36 | - name: Setup tools 37 | if: steps.cache.outputs.cache-hit != 'true' 38 | run: | 39 | # ./scripts/install_sccache.sh $TARGET 40 | source ~/.cargo/env || true 41 | ./scripts/install_mdbook.sh $TARGET 42 | - name: Run tests 43 | run: | 44 | if [ ! -z $DISABLE_TESTS ]; then 45 | return 46 | elif [[ -z ${WASM+set} ]]; then 47 | mdbook build book 48 | ./scripts/ci.sh 49 | if ! git diff-index HEAD --; then 50 | echo "Detected changes in the source after running tests" 51 | exit 1 52 | fi 53 | else 54 | rustup target add wasm32-unknown-unknown 55 | cargo check --target wasm32-unknown-unknown -p gluon_c-api 56 | fi 57 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | *.swp 3 | *.swo 4 | # rustfmt backup files 5 | *.bk 6 | *.bak 7 | *.rs.fmt 8 | 9 | *.bak 10 | 11 | target 12 | 13 | .vscode 14 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Gluon 2 | 3 | ## Filing bug reports 4 | 5 | It does not matter if you found a soundness issue in typechecker or found the documentation confusing. Either way filing an issue to the [issue tracker][] is extremely helpful. 6 | 7 | [issue tracker]:https://github.com/gluon-lang/gluon/issues 8 | 9 | ## Finding something to work on 10 | 11 | A good place to start is to look at the issues marked as [beginner][]. These are issues that should be possible to work on without knowledge on the inner workings of Gluon. 12 | 13 | If you find something that looks interesting, please leave a comment on the issue. That way, you can get assistance quicker and there is no risk of duplicating work. 14 | 15 | [beginner]:https://github.com/gluon-lang/gluon/labels/Beginner 16 | 17 | ## Building 18 | 19 | Gluon can build with version 1.9.0 of Rust or later but we recommend version 1.11.0 or later to avoid some very long compile times for the `gluon_parser` crate. 20 | 21 | ## Testing 22 | 23 | To build and run all(*) tests for Gluon you can call `cargo test --features test --all`. Instead of `--all` you can pass the `-p ` and `--test ` flags to compile a specific crate and/or test module. For instance, `cargo test --features test -p gluon_parser --test basic` to run the tests in [parsers/tests/basic.rs](https://github.com/gluon-lang/gluon/blob/master/parser/tests/basic.rs). 24 | 25 | (*) You can see what Github actions actually builds and tests in [scripts/ci.sh](https://github.com/gluon-lang/gluon/blob/master/scripts/ci.sh). Most of the time you should not need to worry about these additional tests and can just rely on CI running them. 26 | 27 | ## Pull requests 28 | 29 | Once you have made some changes, you will need to file a pull request to get your changes merged into the main repository. If the code is still a work in progress, it can still be a good idea to submit a PR. That will let other contributors see your progress and provide assistance (you may prefix the PR message with [WIP] to make it explicit that the PR is incomplete). 30 | 31 | You may see that some of the [commits][] follow the [commit message convention of Angular][]. Following this convention is optional but if you enjoy using it, feel free to do so! 32 | 33 | [commits]:https://github.com/gluon-lang/gluon/commit/9b36d699c63e482969239ed9f84779f7cd1ad2f3 34 | [commit message convention of Angular]:https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md#commit-message-format 35 | 36 | ## Releases 37 | 38 | Releases are done by running `./scripts/release.sh ` on a branch and making a PR. After the PR is merged and has passed CI `git push --tags` will make CI publish the new version. 39 | -------------------------------------------------------------------------------- /Cross.toml: -------------------------------------------------------------------------------- 1 | [build.env] 2 | passthrough = [ 3 | "RUST_BACKTRACE", 4 | "RUST_LOG", 5 | "GIT_HASH", 6 | ] 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Markus Westerlind 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 13 | all 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 21 | THE SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | global: 3 | # This will be used as part of the zipfile name 4 | PROJECT_NAME: gluon 5 | # By default schannel checks revocation of certificates unlike some other SSL 6 | # backends, but we've historically had problems on CI where a revocation 7 | # server goes down presumably. See rust-lang/#43333 for more info 8 | CARGO_HTTP_CHECK_REVOKE: false 9 | matrix: 10 | - TARGET: x86_64-pc-windows-msvc 11 | CHANNEL: nightly 12 | 13 | # Install Rust and Cargo 14 | # (Based on from https://github.com/rust-lang/libc/blob/master/appveyor.yml) 15 | install: 16 | - curl -sSf -o rustup-init.exe https://win.rustup.rs 17 | - rustup-init.exe --default-host %TARGET% --default-toolchain %CHANNEL% -y 18 | - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin 19 | - rustc -Vv 20 | - cargo -V 21 | 22 | # 'cargo test' takes care of building for us, so disable Appveyor's build stage. This prevents 23 | # the "directory does not contain a project or solution file" error. 24 | # source: https://github.com/starkat99/appveyor-rust/blob/master/appveyor.yml#L113 25 | build: false 26 | 27 | # Equivalent to Travis' `script` phase 28 | # TODO modify this phase as you see fit 29 | test_script: 30 | - cargo test --features test --all 31 | - cargo check --all --no-default-features 32 | - cargo build --release -p gluon_repl 33 | 34 | before_deploy: 35 | # Generate artifacts for release 36 | - cargo build --release -p gluon_repl 37 | - mkdir staging 38 | # TODO update this part to copy the artifacts that make sense for your project 39 | - copy target\release\gluon.exe staging 40 | - cd staging 41 | # release zipfile will look like 'rust-everywhere-v1.2.3-x86_64-pc-windows-msvc' 42 | - 7z a ..\%PROJECT_NAME%-%APPVEYOR_REPO_TAG_NAME%-%TARGET%.zip * 43 | - appveyor PushArtifact ..\%PROJECT_NAME%-%APPVEYOR_REPO_TAG_NAME%-%TARGET%.zip 44 | 45 | deploy: 46 | description: 'Windows release' 47 | artifact: target\release\gluon.exe 48 | # TODO Regenerate this auth_token for your project, this one won't work for you. Here's how: 49 | # - Go to 'https://github.com/settings/tokens/new' and generate a Token with only the 50 | # `public_repo` scope enabled 51 | # - Then go to 'https://ci.appveyor.com/tools/encrypt' and enter the newly generated token. 52 | # - Enter the "encrypted value" below 53 | auth_token: 54 | secure: 0fkZyK+5fQgQVFWFTVCFwrZJDp8aAaMJorsgNZd8YF0aasbd7hWC8EUVCL5YSNuc 55 | provider: GitHub 56 | # deploy when a new tag is pushed and only on the nightly channel 57 | on: 58 | # channel to use to produce the release artifacts 59 | CHANNEL: nightly 60 | appveyor_repo_tag: true 61 | 62 | branches: 63 | only: 64 | - master 65 | # IMPORTANT Regex to match tags. Required, or appveyor may not trigger deploys when a new tag 66 | # is pushed. This regex matches semantic versions like v1.2.3-rc4+2016.02.22 67 | - /^v\d+\.\d+\.\d+.*$/ 68 | -------------------------------------------------------------------------------- /base/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gluon_base" 3 | version = "0.18.2" # GLUON 4 | authors = ["Markus "] 5 | edition = "2018" 6 | 7 | license = "MIT" 8 | 9 | description = "Basic type definitions and functions for the gluon programming language" 10 | 11 | homepage = "https://gluon-lang.org" 12 | repository = "https://github.com/gluon-lang/gluon" 13 | documentation = "https://docs.rs/gluon" 14 | 15 | [dependencies] 16 | bitflags = "1.3.2" 17 | hashbrown = "0.11.2" 18 | log = "0.4.20" 19 | quick-error = "2.0.1" 20 | fnv = "1.0.7" 21 | pretty = "0.10.0" 22 | smallvec = "1.11.0" 23 | collect-mac = "0.1.0" 24 | anymap = { version = "0.12.1", optional = true } 25 | itertools = "0.10.5" 26 | ordered-float = "2.10.0" 27 | codespan = "0.11.1" 28 | codespan-reporting = "0.11.1" 29 | either = "1.9.0" 30 | vec_map = "0.8.2" 31 | typed-arena = "2.0.2" 32 | 33 | gluon_codegen = { version = "0.18.2", path = "../codegen" } # GLUON 34 | 35 | serde = { version = "1.0.188", features = ["rc"], optional = true } 36 | serde_state = { version = "0.4.8", features = ["rc"], optional = true } 37 | serde_derive = { version = "1.0.188", optional = true } 38 | serde_derive_state = { version = "0.4.10", optional = true } 39 | 40 | # Crates used in testing 41 | compiletest_rs = { version = "0.7.1", optional = true } 42 | 43 | [dev-dependencies] 44 | env_logger = "0.9.3" 45 | pretty_assertions = "1.4.0" 46 | 47 | [features] 48 | serialization = ["serde", "serde_state", "serde_derive", "serde_derive_state", "anymap"] 49 | nightly = ["compiletest_rs"] 50 | -------------------------------------------------------------------------------- /base/src/fnv.rs: -------------------------------------------------------------------------------- 1 | extern crate fnv; 2 | 3 | use std::collections::{HashMap, HashSet}; 4 | use std::hash::BuildHasherDefault; 5 | 6 | pub use self::fnv::FnvHasher; 7 | 8 | /// Non-crypto `HashMap` using Fnv Hasher 9 | /// 10 | /// The default hashing implementation in `std::collections` uses `SipHasher` 11 | /// since gluon doesn't need the cryptographic guarantee provided by `SipHasher`, 12 | /// we've opted for the faster fnv hash. 13 | pub type FnvMap = HashMap>; 14 | 15 | /// Non-crypto `HashSet` using Fnv Hasher 16 | /// 17 | /// The default hashing implementation in `std::collections` uses `SipHasher` 18 | /// since gluon doesn't need the cryptographic guarantee provided by `SipHasher`, 19 | /// we've opted for the faster fnv hash. 20 | pub type FnvSet = HashSet>; 21 | -------------------------------------------------------------------------------- /base/src/macros.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! ice { 3 | () => ({ 4 | panic!("ICE: Please report an issue at https://github.com/gluon-lang/gluon/issues") 5 | }); 6 | ($msg:expr) => ({ 7 | panic!(concat!($msg, ". Please report an issue at https://github.com/gluon-lang/gluon/issues")) 8 | }); 9 | ($fmt:expr, $($arg:tt)+) => ({ 10 | panic!(concat!($fmt, ". Please report an issue at https://github.com/gluon-lang/gluon/issues"), $($arg)+) 11 | }); 12 | } 13 | -------------------------------------------------------------------------------- /base/tests/compile-fail/arena-1.rs: -------------------------------------------------------------------------------- 1 | extern crate gluon_base; 2 | 3 | use gluon_base::{ 4 | ast::{Arena, Expr, RootExpr}, 5 | mk_ast_arena, pos, 6 | }; 7 | 8 | fn main() { 9 | mk_ast_arena!(arena1); 10 | mk_ast_arena!(arena2); 11 | //~^ `tag` does not live long enough [E0597] 12 | 13 | let arena1_expr = arena1.alloc(pos::spanned( 14 | Default::default(), 15 | Expr::::Error(None), 16 | )); 17 | 18 | RootExpr::new(arena2, arena1_expr); 19 | } 20 | -------------------------------------------------------------------------------- /base/tests/compile-fail/arena-2.rs: -------------------------------------------------------------------------------- 1 | extern crate gluon_base; 2 | 3 | use gluon_base::{ 4 | ast::{Arena, Expr, RootExpr}, 5 | mk_ast_arena, pos, 6 | }; 7 | 8 | fn main() { 9 | mk_ast_arena!(arena1); 10 | mk_ast_arena!(arena2); 11 | //~^ `tag` does not live long enough [E0597] 12 | 13 | let arena2_expr = arena2.alloc(pos::spanned( 14 | //~^ `arena2` does not live long enough [E0597] 15 | Default::default(), 16 | Expr::::Error(None), 17 | )); 18 | 19 | // Should fail 20 | RootExpr::new(arena1, arena2_expr); 21 | } 22 | -------------------------------------------------------------------------------- /base/tests/compiletest.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "nightly")] 2 | extern crate compiletest_rs as compiletest; 3 | extern crate env_logger; 4 | 5 | use std::fs; 6 | use std::path::{Path, PathBuf}; 7 | 8 | fn lib_dir(out_dir: &Path, lib_name: &str) -> PathBuf { 9 | // Just loading gluon through -L dir does not work as we compile gluon with different sets of 10 | // flags which gives ambiguity errors. 11 | // Instead retrieve the latest compiled gluon library which should usually be the correct one 12 | let mut gluon_rlibs: Vec<_> = fs::read_dir(out_dir.join("deps")) 13 | .unwrap() 14 | .filter_map(|entry| { 15 | let entry = entry.expect("dir entry"); 16 | if entry 17 | .path() 18 | .to_str() 19 | .map_or(false, |name| name.contains(lib_name)) 20 | { 21 | Some(entry) 22 | } else { 23 | None 24 | } 25 | }) 26 | .collect(); 27 | gluon_rlibs.sort_by(|l, r| { 28 | l.metadata() 29 | .unwrap() 30 | .modified() 31 | .unwrap() 32 | .cmp(&r.metadata().unwrap().modified().unwrap()) 33 | }); 34 | gluon_rlibs.last().expect("libgluon not found").path() 35 | } 36 | 37 | fn run_mode(mode: &'static str) { 38 | // Retrieve the path where library dependencies are output 39 | let out_dir = PathBuf::from("../target/debug"); 40 | let gluon_base_rlib = lib_dir(&out_dir, "libgluon_base-"); 41 | 42 | let mut config = compiletest::Config::default(); 43 | let cfg_mode = mode.parse().ok().expect("Invalid mode"); 44 | 45 | config.verbose = true; 46 | config.mode = cfg_mode; 47 | config.src_base = PathBuf::from(format!("tests/{}", mode)); 48 | config.target_rustcflags = Some(format!( 49 | "-L {}/deps --extern gluon_base={}", 50 | out_dir.display(), 51 | gluon_base_rlib.display(), 52 | )); 53 | println!("{}", config.target_rustcflags.as_ref().unwrap()); 54 | compiletest::run_tests(&config); 55 | } 56 | 57 | #[test] 58 | fn compile_test() { 59 | let _ = env_logger::try_init(); 60 | run_mode("compile-fail"); 61 | } 62 | -------------------------------------------------------------------------------- /benches/precompiled.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate criterion; 3 | 4 | use std::{fs::File, io::Read}; 5 | 6 | use criterion::{Bencher, Criterion}; 7 | 8 | use gluon::{compiler_pipeline::compile_to, new_vm, ThreadExt}; 9 | 10 | fn precompiled_prelude(b: &mut Bencher) { 11 | let thread = new_vm(); 12 | let prelude = { 13 | let mut out = String::new(); 14 | File::open("std/prelude.glu") 15 | .unwrap() 16 | .read_to_string(&mut out) 17 | .unwrap(); 18 | out 19 | }; 20 | let mut serialized_prelude = Vec::new(); 21 | { 22 | let mut serializer = serde_json::Serializer::new(&mut serialized_prelude); 23 | futures::executor::block_on(compile_to( 24 | &prelude[..], 25 | &mut thread.module_compiler(&mut thread.get_database()), 26 | &thread, 27 | "std.prelude", 28 | &prelude, 29 | None, 30 | &mut serializer, 31 | )) 32 | .unwrap() 33 | } 34 | b.iter(|| { 35 | use gluon::compiler_pipeline::{Executable, Precompiled}; 36 | 37 | let mut deserializer = serde_json::Deserializer::from_slice(&serialized_prelude); 38 | futures::executor::block_on(Precompiled(&mut deserializer).run_expr( 39 | &mut thread.module_compiler(&mut thread.get_database()), 40 | &*thread, 41 | "std.prelude", 42 | "", 43 | (), 44 | )) 45 | .unwrap() 46 | }) 47 | } 48 | 49 | fn source_prelude(b: &mut Bencher) { 50 | let mut prelude_source = String::new(); 51 | File::open("std/prelude.glu") 52 | .and_then(|mut f| f.read_to_string(&mut prelude_source)) 53 | .unwrap(); 54 | let thread = new_vm(); 55 | 56 | b.iter(|| { 57 | use gluon::compiler_pipeline::Executable; 58 | futures::executor::block_on(prelude_source.run_expr( 59 | &mut thread.module_compiler(&mut thread.get_database()), 60 | &*thread, 61 | "std.prelude", 62 | &prelude_source, 63 | None, 64 | )) 65 | .unwrap() 66 | }) 67 | } 68 | 69 | fn precompiled_benchmark(c: &mut Criterion) { 70 | c.bench_function("source std/prelude", source_prelude); 71 | c.bench_function("precompiled std/prelude", precompiled_prelude); 72 | } 73 | 74 | criterion_group!(precompiled, precompiled_benchmark); 75 | criterion_main!(precompiled); 76 | -------------------------------------------------------------------------------- /book/.gitignore: -------------------------------------------------------------------------------- 1 | book 2 | -------------------------------------------------------------------------------- /book/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "Gluon Documentation" 3 | description = "Gluon is a static, type inferred and embeddable language written in Rust" 4 | authors = ["Markus Westerlind"] 5 | multilingual = false 6 | src = "src" 7 | -------------------------------------------------------------------------------- /book/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Getting started](./getting-started.md) 4 | 5 | - [Dissecting Hello World](./dissecting-hello-world.md) 6 | 7 | - [Using the REPL](./using-the-repl.md) 8 | 9 | - [Anatomy of a gluon program](./anatomy-of-a-gluon-program.md) 10 | 11 | - [Extending the guessing game with extensible effects](./extending-the-guessing-game-with-effects.md) 12 | 13 | - [Syntax and semantics](./syntax-and-semantics.md) 14 | 15 | - [Metadata](./metadata.md) 16 | 17 | - [Modules](./modules.md) 18 | 19 | - [Embedding API](./embedding-api.md) 20 | 21 | - [Marshalling types](./marshalling-types.md) 22 | 23 | - [Standard types and functions](./standard-types-and-functions.md) 24 | -------------------------------------------------------------------------------- /book/src/dissecting-hello-world.md: -------------------------------------------------------------------------------- 1 | # Dissecting Hello World 2 | 3 | ```gluon 4 | let io = import! std.io 5 | io.println "Hello world!" 6 | ``` 7 | 8 | There are a number of things going on in the hello world example so lets break them down one step at a time. 9 | 10 | ```gluon 11 | let 12 | ``` 13 | 14 | Gluon uses the keyword `let` to bind values for later use. 15 | 16 | ```gluon 17 | import! std.io 18 | ``` 19 | 20 | `import!` is a builtin macro which loads the contents of another module. In the example we use it to get access to the standard library's `io` module. Since it appeared on the right side of `let io =` we thus bound the `std.io` module to the `io` binding. 21 | 22 | ``` 23 | io.println 24 | ``` 25 | 26 | Here we access the `println` function from the `io` module we bound earlier which is a function that lets us write strings to stdout. 27 | 28 | ```gluon 29 | "Hello world!" 30 | ``` 31 | 32 | Finally we create a string literal which gets passed to `println` to get printed. 33 | -------------------------------------------------------------------------------- /book/src/getting-started.md: -------------------------------------------------------------------------------- 1 | # Getting started 2 | 3 | To get started with gluon we must first have a way to compile and run Gluon programs. The fastest way to get that is to download one of the pre-built binaries for Linux, Windows, OSX or FreeBSD from https://github.com/gluon-lang/gluon/releases. Alternatively, if you have a Rust compiler and Cargo installed you may install it with `cargo install gluon_repl`. 4 | 5 | Once installed you can verify that it works by saving the following program into a file named `hello_world.glu` and then compile and run it with `gluon hello_world.glu`. 6 | 7 | ```gluon 8 | let io = import! std.io 9 | io.println "Hello world!" 10 | ``` 11 | 12 | If everything works the program should have printed `Hello world!` to your terminal. 13 | -------------------------------------------------------------------------------- /book/src/modules.md: -------------------------------------------------------------------------------- 1 | # Modules 2 | 3 | ## Importing modules 4 | 5 | As is often the case, it is convenient to separate code into multiple files which can later be imported and used from multiple other files. To do this, we can use the `import!` macro which takes a single string literal as argument and loads and compiles that file at compile time before the importing module is compiled. 6 | 7 | For example, say that we need the `assert` function from the `test` module which can be found at `std/test.glu`. We might write something like this: 8 | 9 | ```f#,rust 10 | let { assert } = import! std.test 11 | assert (1 == 1) 12 | ``` 13 | 14 | ## Writing modules 15 | 16 | Importing standard modules is all well and good but it is also necessary to write your own once a program starts getting too big for a single file. As it turns out, if you have been following along so far, you already know everything about writing a module! Creating and loading a module in gluon entails creating a file containing an expression which is then loaded and evaluated using `import!`. `import!` is then just the value of the evaluated expression. 17 | 18 | ```f# 19 | // module.glu 20 | type Named a = { name: String, value: a } 21 | let twice f x = f (f x) 22 | { twice, Named } 23 | 24 | //main.glu 25 | let { twice, Named } = import! "module.glu" 26 | let addTwice = twice (\x -> x + 1) 27 | let namedFloat : Named Float = { name = "pi", value = 3.14 } 28 | addTwice 10 29 | ``` 30 | 31 | Though modules are most commonly a record, this does not have to be the case. If you wanted, you could write a module returning any other value as well. 32 | 33 | ```f# 34 | // pi.glu 35 | 3.14 36 | 37 | //main.glu 38 | let pi = import! "pi.glu" 39 | 2 * pi * 10 40 | ``` 41 | 42 | -------------------------------------------------------------------------------- /book/src/standard-types-and-functions.md: -------------------------------------------------------------------------------- 1 | # Standard types and functions 2 | 3 | The API documentation for the standard library can be found [here][std-docs]. Some of the modules 4 | are only available if Gluon is compiled with the required features: 5 | 6 | - `std.regex` requires the `regex` feature (enabled by default) 7 | - `std.random` requires the `rand` feature (enabled by default) 8 | - All `std.json.*` modules require the `serialization` feature 9 | 10 | TODO 11 | 12 | ### Prelude 13 | 14 | When compiling an expression, the compiler automatically inserts a small prelude before the expression itself, which gives automatic access to basic operators such as `+`, `-`, etc as well as types such as `Option` and `Result`. 15 | 16 | ### Threads and channels 17 | 18 | Gluon has support for cooperative threading and communication between them through the `Thread` and `Sender`/`Receiver` types. 19 | 20 | ### Strings 21 | 22 | `String` is a built-in data type. The module `std.string` provides the infix operatoin `++`, concatenating two strings. The operation `show` converts the Int to a printable String. 23 | ``` 24 | let io @ { ? } = import! std.io 25 | let string @ { len } = import! std.string 26 | let { show } = import! std.show 27 | let message : String = "Hello" ++ " " ++ "World" ++ "!" 28 | let prompt s = "<" ++ show (len s) ++ "> " 29 | io.println ( (prompt message) ++ message ) 30 | ``` 31 | The output will be 32 | ``` 33 | <12> Hello World! 34 | ``` 35 | 36 | The following API doc for the module [`std.string`](https://gluon-lang.org/doc/crates_io/std/std/string.html) is a list of all string operations. 37 | ``` 38 | 39 | TODO 40 | 41 | [std-docs]: http://gluon-lang.org/doc/nightly/std/index.html 42 | -------------------------------------------------------------------------------- /book/src/using-the-repl.md: -------------------------------------------------------------------------------- 1 | # Using the REPL 2 | 3 | Though it is possible to continue running any programs by saving it to a file and running it with `gluon my_script.glu` there is an easier way to go about it when you want to experiment quickly with small programs. By running `gluon -i`, gluon starts in "interactive" mode, giving you a REPL where you may evaluate expressions and inspect their results. Try evaluating some simple arithmetic expressions to see that it works. 4 | 5 | ``` 6 | > 1 + 2 7 | 3 8 | > 100 * 3 + 4 9 | 304 10 | > 3.14 * 10.0 11 | 31.400000000000002 12 | ``` 13 | 14 | Evaluating only a single expression can get quite unwieldy so if we want to break something up into multiple steps we can use `let` to give a name to an expression. 15 | 16 | ``` 17 | > let pi_2 = 3.14 * 2.0 18 | 6.28 19 | > pi_2 * 3.0 20 | 18.84 21 | ``` 22 | 23 | These are the basic parts of the REPL and if you want to you can try writing hello world again by using the features above. 24 | 25 | If you still have the `hello_world.glu` file around there is another way to run it from inside the REPL by using the special `:script` (`:s`) command. 26 | 27 | ``` 28 | > :s hello_world.glu 29 | Hello World! 30 | ``` 31 | 32 | There are a few other of these special commands as well and you can find them all with `:help` (`:h`). 33 | 34 | 35 | ``` 36 | > :type 1 37 | Int 38 | > :info std.io.println 39 | std.io.println: String -> IO () 40 | > :kind std.option.Option 41 | Type -> Type 42 | ``` 43 | 44 | Finally you may quit the REPL using the `:quit` (`:q`) command or using ``. 45 | -------------------------------------------------------------------------------- /c-api/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gluon_c-api" 3 | version = "0.18.2" # GLUON 4 | authors = ["Markus Westerlind "] 5 | edition = "2018" 6 | 7 | license = "MIT" 8 | 9 | description = "C-api for gluon, a static, type inferred programming language for application embedding" 10 | 11 | homepage = "https://gluon-lang.org" 12 | repository = "https://github.com/gluon-lang/gluon" 13 | documentation = "https://docs.rs/gluon" 14 | 15 | [lib] 16 | crate-type = ["cdylib"] 17 | 18 | [dependencies] 19 | gluon = { version = "0.18.2", path = ".." } # GLUON 20 | futures = "0.3.28" 21 | 22 | [target.'cfg(not(target_arch = "wasm32"))'.dependencies] 23 | libc = "0.2.147" 24 | 25 | [features] 26 | test = ["gluon/test"] 27 | nightly = ["gluon/nightly"] 28 | -------------------------------------------------------------------------------- /check/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gluon_check" 3 | version = "0.18.2" # GLUON 4 | authors = ["Markus "] 5 | edition = "2018" 6 | 7 | license = "MIT" 8 | 9 | description = "The typechecker for the gluon programming language" 10 | 11 | homepage = "https://gluon-lang.org" 12 | repository = "https://github.com/gluon-lang/gluon" 13 | documentation = "https://docs.rs/gluon" 14 | 15 | [dependencies] 16 | collect-mac = "0.1.0" 17 | ena = "0.14.2" 18 | log = "0.4.20" 19 | itertools = "0.10.5" 20 | pretty = "0.10.0" 21 | smallvec = "1.11.0" 22 | rpds = "0.10.0" 23 | quick-error = "2.0.1" 24 | 25 | codespan = "0.11.1" 26 | codespan-reporting = "0.11.1" 27 | 28 | strsim = "0.10.0" 29 | 30 | gluon_base = { path = "../base", version = "0.18.2" } # GLUON 31 | gluon_codegen = { path = "../codegen", version = "0.18.2" } # GLUON 32 | 33 | [dev-dependencies] 34 | env_logger = "0.9.3" 35 | insta = "1.31.0" 36 | 37 | gluon_parser = { path = "../parser", version = "0.18.2" } # GLUON 38 | gluon_format = { path = "../format", version = ">=0.9" } 39 | 40 | collect-mac = "0.1.0" 41 | difference = "2.0.0" 42 | pretty_assertions = "1.4.0" 43 | 44 | -------------------------------------------------------------------------------- /check/src/typ.rs: -------------------------------------------------------------------------------- 1 | pub use crate::base::types::{ArcType as RcType, Flags, TypeExt, TypePtr}; 2 | 3 | #[cfg(test)] 4 | mod tests { 5 | use super::*; 6 | 7 | use crate::base::{ 8 | symbol::Symbol, 9 | types::{Generic, Type}, 10 | }; 11 | 12 | #[test] 13 | fn flags() { 14 | let gen = Type::<_, RcType>::generic(Generic::new(Symbol::from("a"), Default::default())); 15 | assert_eq!(gen.flags(), Flags::HAS_GENERICS); 16 | assert_eq!( 17 | Type::function(vec![gen.clone()], gen.clone()).flags(), 18 | Flags::HAS_GENERICS 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /check/src/typecheck/mod_type.rs: -------------------------------------------------------------------------------- 1 | use std::{fmt, ops}; 2 | 3 | use base::types::ArcType; 4 | 5 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 6 | pub enum TypeModifier { 7 | Wobbly, 8 | Rigid, 9 | } 10 | 11 | impl Default for TypeModifier { 12 | fn default() -> Self { 13 | TypeModifier::Wobbly 14 | } 15 | } 16 | 17 | impl ops::BitOr for TypeModifier { 18 | type Output = Self; 19 | 20 | fn bitor(mut self, typ: Self) -> Self { 21 | self |= typ; 22 | self 23 | } 24 | } 25 | impl ops::BitOrAssign for TypeModifier { 26 | fn bitor_assign(&mut self, typ: Self) { 27 | match (*self, typ) { 28 | (TypeModifier::Rigid, TypeModifier::Rigid) => (), 29 | _ => *self = TypeModifier::Wobbly, 30 | } 31 | } 32 | } 33 | 34 | pub type ModTypeRef<'a> = ModType<&'a ArcType>; 35 | 36 | #[derive(Clone, Copy, Debug)] 37 | pub struct ModType { 38 | pub modifier: TypeModifier, 39 | pub concrete: T, 40 | } 41 | 42 | impl fmt::Display for ModType 43 | where 44 | T: fmt::Display, 45 | { 46 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 47 | self.concrete.fmt(f) 48 | } 49 | } 50 | 51 | impl ops::Deref for ModType { 52 | type Target = T; 53 | 54 | fn deref(&self) -> &Self::Target { 55 | &self.concrete 56 | } 57 | } 58 | 59 | impl ops::DerefMut for ModType { 60 | fn deref_mut(&mut self) -> &mut Self::Target { 61 | &mut self.concrete 62 | } 63 | } 64 | 65 | impl ops::BitOrAssign for ModType { 66 | fn bitor_assign(&mut self, typ: Self) { 67 | self.modifier |= typ.modifier; 68 | self.concrete = typ.concrete; 69 | } 70 | } 71 | 72 | impl ModType { 73 | pub fn new(modifier: TypeModifier, typ: T) -> Self { 74 | ModType { 75 | modifier, 76 | concrete: typ, 77 | } 78 | } 79 | 80 | pub fn rigid(typ: T) -> Self { 81 | Self::new(TypeModifier::Rigid, typ) 82 | } 83 | 84 | pub fn wobbly(typ: T) -> Self { 85 | Self::new(TypeModifier::Wobbly, typ) 86 | } 87 | 88 | pub fn as_ref(&self) -> ModType<&T> { 89 | ModType::new(self.modifier, &self.concrete) 90 | } 91 | } 92 | 93 | impl<'a, T> ModType<&'a T> 94 | where 95 | T: Clone, 96 | { 97 | pub fn to_owned(&self) -> ModType { 98 | ModType::new(self.modifier, self.concrete.clone()) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /check/tests/error_recovery.rs: -------------------------------------------------------------------------------- 1 | extern crate gluon_base as base; 2 | 3 | mod support; 4 | 5 | use crate::support::*; 6 | 7 | use crate::base::ast::Typed; 8 | 9 | #[test] 10 | fn partial_let() { 11 | let src = r#" 12 | /// Alias of `or` 13 | #[infix(left, 3)] 14 | let (<|>) : () = () 15 | let a 16 | { 17 | (<|>) 18 | } 19 | "#; 20 | let (expr, result) = typecheck_partial_expr(src); 21 | assert_req!(result.map(|t| t.to_string()), Ok("{ (<|>) : () }")); 22 | assert_eq!( 23 | expr.env_type_of(&MockEnv::new()).to_string(), 24 | "{ (<|>) : () }" 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /check/tests/snapshots/fail__alias_mismatch.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: check/tests/fail.rs 3 | expression: "&*format!(\"{}\", result . unwrap_err()).replace(\"\\t\", \" \")" 4 | --- 5 | error: Expected the following types to be equal 6 | Expected: test.A 7 | Found: test.B 8 | 1 errors were found during unification: 9 | Row labels do not match. 10 | Expected: A 11 | Found: B 12 | ┌─ test:5:11 13 | │ 14 | 5 │ eq (A 0) (B 0.0) 15 | │ ^^^^^ 16 | 17 | 18 | -------------------------------------------------------------------------------- /check/tests/snapshots/fail__multiple_extra_parameters_error.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: check/tests/fail.rs 3 | expression: "&*format!(\"{}\", result . unwrap_err()).replace(\"\\t\", \" \")" 4 | --- 5 | error: Expected the following types to be equal 6 | Expected: Int -> Float -> a 7 | Found: String 8 | 1 errors were found during unification: 9 | Types do not match: 10 | Expected: Int -> Float -> a 11 | Found: String 12 | ┌─ test:3:7 13 | │ 14 | 3 │ id "" 1 1.0 15 | │ ----- Attempted to call function with 3 arguments but its type only has 1 16 | 17 | 18 | -------------------------------------------------------------------------------- /check/tests/snapshots/fail__no_inference_variable_in_error.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: check/tests/fail.rs 3 | expression: "&*format!(\"{}\", result . unwrap_err()).replace(\"\\t\", \" \")" 4 | --- 5 | error: Expected the following types to be equal 6 | Expected: Int -> a 7 | Found: () 8 | 1 errors were found during unification: 9 | Types do not match: 10 | Expected: Int -> a 11 | Found: () 12 | ┌─ test:2:4 13 | │ 14 | 2 │ () 1 15 | │ - Attempted to call a non-function value 16 | 17 | 18 | -------------------------------------------------------------------------------- /check/tests/snapshots/fail__unable_to_resolve_implicit_error_message.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: check/tests/fail.rs 3 | expression: "&*format!(\"{}\", result . unwrap_err()).replace(\"\\t\", \" \")" 4 | --- 5 | error: Implicit parameter with type `test.Eq Int` could not be resolved. 6 | ┌─ test:11:3 7 | │ 8 | 11 │ f (Test (Test 1)) 9 | │ --------------- 10 | │ │ 11 | │ Required because of an implicit parameter of `[test.Eq Int] -> test.Eq (test.Test Int)` 12 | │ Required because of an implicit parameter of `[test.Eq (test.Test Int)] -> test.Eq (test.Test (test.Test Int))` 13 | 14 | 15 | -------------------------------------------------------------------------------- /check/tests/snapshots/fail__unification_error_with_empty_record_displays_good_error_message.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: check/tests/fail.rs 3 | expression: "&*format!(\"{}\", result . unwrap_err()).replace(\"\\t\", \" \")" 4 | --- 5 | error: Expected the following types to be equal 6 | Expected: () 7 | Found: { x : Int } 8 | 1 errors were found during unification: 9 | The type `()` lacks the following fields: x 10 | ┌─ test:4:7 11 | │ 12 | 4 │ f { } { x = 1 } 13 | │ ^^^^^^^^^ 14 | 15 | 16 | -------------------------------------------------------------------------------- /codegen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gluon_codegen" 3 | version = "0.18.2" # GLUON 4 | authors = ["Markus "] 5 | 6 | edition = "2018" 7 | 8 | license = "MIT" 9 | 10 | description = "Code generation macros for the gluon programming language" 11 | 12 | repository = "https://github.com/gluon-lang/gluon" 13 | documentation = "https://docs.rs/gluon" 14 | 15 | [lib] 16 | proc-macro = true 17 | 18 | [dependencies] 19 | syn = { version = "1.0.109", features = ["extra-traits"] } 20 | quote = "1.0.33" 21 | proc-macro2 = "1.0.66" 22 | 23 | [dev-dependencies] 24 | env_logger = "0.9.3" 25 | serde = "1.0.188" 26 | serde_derive = "1.0.188" 27 | gluon = { version = ">=0.8.0", path = "..", features = ["serialization"] } 28 | gluon_vm = { version = ">=0.8.0", path = "../vm" } 29 | -------------------------------------------------------------------------------- /codegen/src/userdata.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::{Ident, Span, TokenStream}; 2 | use syn::{self, Data, DeriveInput, Generics}; 3 | 4 | use crate::{ 5 | attr::{Container, CrateName}, 6 | shared::{map_lifetimes, map_type_params, split_for_impl}, 7 | }; 8 | 9 | pub fn derive(input: TokenStream) -> TokenStream { 10 | let derive_input = syn::parse2(input).expect("Input is checked by rustc"); 11 | 12 | let container = Container::from_ast(&derive_input); 13 | 14 | let DeriveInput { 15 | ident, 16 | data, 17 | generics, 18 | .. 19 | } = derive_input; 20 | 21 | let tokens = match data { 22 | Data::Struct(_) | Data::Enum(_) => gen_impl(&container, ident, generics), 23 | Data::Union(_) => panic!("Unions are not supported"), 24 | }; 25 | 26 | tokens.into() 27 | } 28 | 29 | fn gen_impl(container: &Container, ident: Ident, generics: Generics) -> TokenStream { 30 | let trait_bounds = &map_type_params(&generics, |ty| { 31 | quote! { #ty: 'static + ::std::fmt::Debug + _gluon_gc::Trace + Sync + Send } 32 | }); 33 | 34 | let lifetime_bounds = &map_lifetimes(&generics, |lifetime| quote! { #lifetime: 'static }); 35 | 36 | let (impl_generics, ty_generics, where_clause) = split_for_impl(&generics, &[], &[]); 37 | 38 | let gluon = match container.crate_name { 39 | CrateName::Some(ref ident) => quote! { 40 | use #ident::api as _gluon_api; 41 | use #ident::gc as _gluon_gc; 42 | use #ident::Result as _gluon_Result; 43 | }, 44 | CrateName::GluonVm => quote! { 45 | use crate::api as _gluon_api; 46 | use crate::thread as _gluon_gc; 47 | use crate::Result as _gluon_Result; 48 | }, 49 | CrateName::None => quote! { 50 | use gluon::vm::api as _gluon_api; 51 | use gluon::vm::gc as _gluon_gc; 52 | use gluon::vm::Result as _gluon_Result; 53 | }, 54 | }; 55 | 56 | let dummy_const = Ident::new(&format!("_IMPL_USERDATA_FOR_{}", ident), Span::call_site()); 57 | 58 | let deep_clone = if container.clone { 59 | quote! { 60 | fn deep_clone<'gc>( 61 | &self, 62 | deep_cloner: &'gc mut _gluon_api::Cloner 63 | ) -> _gluon_Result<_gluon_gc::GcRef<'gc, Box>> { 64 | let data: Box = Box::new(self.clone()); 65 | deep_cloner.gc().alloc(_gluon_gc::Move(data)) 66 | } 67 | } 68 | } else { 69 | quote! {} 70 | }; 71 | 72 | quote! { 73 | #[allow(non_upper_case_globals)] 74 | const #dummy_const: () = { 75 | #gluon 76 | 77 | #[automatically_derived] 78 | #[allow(unused_attributes, unused_variables)] 79 | impl #impl_generics _gluon_api::Userdata for #ident #ty_generics 80 | #where_clause #(#trait_bounds,)* #(#lifetime_bounds),* 81 | { 82 | #deep_clone 83 | } 84 | }; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /codegen/tests/derive_userdata.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate gluon_codegen; 3 | extern crate gluon; 4 | #[macro_use] 5 | extern crate gluon_vm; 6 | 7 | mod init; 8 | 9 | use std::sync::Arc; 10 | 11 | use gluon::{ 12 | import, 13 | vm::{self, ExternModule}, 14 | Thread, ThreadExt, 15 | }; 16 | 17 | use init::new_vm; 18 | 19 | #[derive(Userdata, Trace, Debug, VmType)] 20 | #[gluon(vm_type = "WindowHandle")] 21 | struct WindowHandle { 22 | id: Arc, 23 | metadata: Arc, 24 | } 25 | 26 | fn load_mod(vm: &Thread) -> vm::Result { 27 | vm.register_type::("WindowHandle", &[])?; 28 | 29 | let module = record! { 30 | create_hwnd => primitive!(2, create_hwnd), 31 | id => primitive!(1, id), 32 | metadata => primitive!(1, metadata), 33 | }; 34 | 35 | ExternModule::new(vm, module) 36 | } 37 | 38 | fn create_hwnd(id: u64, metadata: String) -> WindowHandle { 39 | WindowHandle { 40 | id: Arc::new(id), 41 | metadata: Arc::from(metadata), 42 | } 43 | } 44 | 45 | fn id(hwnd: &WindowHandle) -> u64 { 46 | *hwnd.id 47 | } 48 | 49 | fn metadata(hwnd: &WindowHandle) -> String { 50 | String::from(&*hwnd.metadata) 51 | } 52 | 53 | #[test] 54 | fn userdata() { 55 | let vm = new_vm(); 56 | 57 | import::add_extern_module(&vm, "hwnd", load_mod); 58 | 59 | let script = r#" 60 | let { assert } = import! std.test 61 | let { create_hwnd, id, metadata } = import! hwnd 62 | 63 | let hwnd = create_hwnd 0 "Window1" 64 | 65 | let _ = assert (id hwnd == 0) 66 | assert (metadata hwnd == "Window1") 67 | "#; 68 | 69 | if let Err(why) = vm.run_expr::<()>("test", script) { 70 | panic!("{}", why); 71 | } 72 | } 73 | 74 | #[derive(Userdata, Trace, Debug, VmType)] 75 | #[gluon(vm_type = "Empty")] 76 | struct Empty; 77 | -------------------------------------------------------------------------------- /codegen/tests/derive_vm_type.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate gluon_codegen; 3 | extern crate gluon; 4 | 5 | mod init; 6 | 7 | use gluon::vm::api; 8 | use gluon::{base::types::Type, vm::api::VmType}; 9 | #[macro_use] 10 | extern crate serde_derive; 11 | use init::new_vm; 12 | 13 | #[derive(VmType)] 14 | #[allow(unused)] 15 | struct Struct { 16 | string: String, 17 | number: u32, 18 | vec: Vec, 19 | } 20 | 21 | #[test] 22 | fn struct_() { 23 | let vm = new_vm(); 24 | 25 | assert_eq!( 26 | Struct::make_type(&vm).to_string(), 27 | "{ string : String, number : Int, vec : Array Float }" 28 | ); 29 | } 30 | 31 | #[derive(VmType, Serialize, Deserialize)] 32 | #[allow(unused)] 33 | enum Enum { 34 | One, 35 | Two(u32), 36 | Three { id: String }, 37 | } 38 | 39 | #[test] 40 | fn enum_() { 41 | let vm = new_vm(); 42 | 43 | let src = api::typ::make_source::(&vm).unwrap(); 44 | println!("Enum Types:\n{}", src); 45 | 46 | assert_eq!( 47 | Enum::make_type(&vm).to_string(), 48 | "| One\n| Two Int\n| Three { id : String }" 49 | ); 50 | } 51 | 52 | #[derive(VmType)] 53 | #[allow(unused)] 54 | struct NewtypeInner(Struct); 55 | 56 | #[test] 57 | fn newtype_inner() { 58 | let vm = new_vm(); 59 | 60 | assert_eq!( 61 | NewtypeInner::make_type(&vm).to_string(), 62 | Struct::make_type(&vm).to_string(), 63 | ); 64 | } 65 | 66 | #[derive(VmType)] 67 | #[gluon(newtype)] 68 | #[allow(unused)] 69 | struct Newtype(Struct); 70 | 71 | #[test] 72 | fn newtype() { 73 | let vm = new_vm(); 74 | 75 | match &*Newtype::make_type(&vm) { 76 | Type::Alias(alias) => { 77 | assert_eq!(alias.name.declared_name(), "Newtype"); 78 | assert_eq!( 79 | alias.unresolved_type().to_string(), 80 | Struct::make_type(&vm).to_string() 81 | ); 82 | } 83 | _ => panic!(), 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /codegen/tests/full.rs: -------------------------------------------------------------------------------- 1 | extern crate gluon; 2 | #[macro_use] 3 | extern crate gluon_codegen; 4 | #[macro_use] 5 | extern crate gluon_vm; 6 | 7 | mod init; 8 | 9 | use gluon::{ 10 | import, 11 | vm::{self, ExternModule}, 12 | Thread, ThreadExt, 13 | }; 14 | use init::new_vm; 15 | 16 | #[derive(Debug, PartialEq, Getable, Pushable, VmType)] 17 | struct Struct { 18 | string: String, 19 | number: u32, 20 | vec: Vec, 21 | } 22 | 23 | fn load_struct_mod(vm: &Thread) -> vm::Result { 24 | let module = record! { 25 | new_struct => primitive!(1, new_struct), 26 | }; 27 | 28 | ExternModule::new(vm, module) 29 | } 30 | 31 | fn new_struct(_: ()) -> Struct { 32 | Struct { 33 | string: "hello".to_owned(), 34 | number: 1, 35 | vec: vec![1.0, 2.0, 3.0], 36 | } 37 | } 38 | 39 | #[test] 40 | fn normal_struct() { 41 | let vm = new_vm(); 42 | import::add_extern_module(&vm, "functions", load_struct_mod); 43 | 44 | let script = r#" 45 | let { new_struct } = import! functions 46 | 47 | new_struct () 48 | "#; 49 | 50 | let (s, _) = vm 51 | .run_expr::("test", script) 52 | .unwrap_or_else(|why| panic!("{}", why)); 53 | 54 | assert_eq!( 55 | s, 56 | Struct { 57 | string: "hello".into(), 58 | number: 1, 59 | vec: vec![1.0, 2.0, 3.0], 60 | } 61 | ); 62 | } 63 | 64 | #[derive(Debug, PartialEq, VmType, Pushable, Getable)] 65 | struct Newtype(pub Struct); 66 | 67 | fn load_newtype_mod(vm: &Thread) -> vm::Result { 68 | let module = record! { 69 | newtype_id => primitive!(1, newtype_id), 70 | }; 71 | 72 | ExternModule::new(vm, module) 73 | } 74 | 75 | fn newtype_id(val: Newtype) -> Newtype { 76 | val 77 | } 78 | 79 | #[test] 80 | fn newtype() { 81 | let vm = new_vm(); 82 | import::add_extern_module(&vm, "functions", load_newtype_mod); 83 | 84 | // newtypes should map to the inner type 85 | let script = r#" 86 | let { newtype_id } = import! functions 87 | 88 | newtype_id { string = "test", number = 42, vec = [1.0, 1.0, 2.0, 3.0, 5.0] } 89 | "#; 90 | 91 | let (s, _) = vm 92 | .run_expr::("test", script) 93 | .unwrap_or_else(|why| panic!("{}", why)); 94 | 95 | assert_eq!( 96 | s, 97 | Newtype(Struct { 98 | string: "test".into(), 99 | number: 42, 100 | vec: vec![1.0, 1.0, 2.0, 3.0, 5.0], 101 | }) 102 | ); 103 | } 104 | -------------------------------------------------------------------------------- /codegen/tests/init/mod.rs: -------------------------------------------------------------------------------- 1 | use gluon::{self, import::Import, RootedThread}; 2 | 3 | pub fn new_vm() -> RootedThread { 4 | if ::std::env::var("GLUON_PATH").is_err() { 5 | ::std::env::set_var("GLUON_PATH", ".."); 6 | } 7 | 8 | let vm = gluon::new_vm(); 9 | let import = vm.get_macros().get("import"); 10 | import 11 | .as_ref() 12 | .and_then(|import| import.downcast_ref::()) 13 | .expect("Import macro") 14 | .add_path(".."); 15 | 16 | vm 17 | } 18 | -------------------------------------------------------------------------------- /completion/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gluon_completion" 3 | version = "0.18.2" # GLUON 4 | authors = ["Markus "] 5 | edition = "2018" 6 | 7 | license = "MIT" 8 | 9 | description = "Auto-completion for the gluon programming language" 10 | 11 | repository = "https://github.com/gluon-lang/gluon" 12 | documentation = "https://docs.rs/gluon" 13 | 14 | [dependencies] 15 | either = "1.9.0" 16 | itertools = "0.10.5" 17 | walkdir = "2.3.3" 18 | codespan = "0.11.1" 19 | 20 | gluon_base = { path = "../base", version = "0.18.2" } # GLUON 21 | 22 | [dev-dependencies] 23 | collect-mac = "0.1.0" 24 | env_logger = "0.9.3" 25 | pretty_assertions = "1.4.0" 26 | quick-error = "2.0.1" 27 | 28 | gluon_check = { path = "../check", version = "0.18.2" } # GLUON 29 | gluon_parser = { path = "../parser", version = "0.18.2" } # GLUON 30 | -------------------------------------------------------------------------------- /completion/tests/signature_help.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate collect_mac; 3 | extern crate env_logger; 4 | 5 | extern crate gluon_base as base; 6 | extern crate gluon_check as check; 7 | extern crate gluon_completion as completion; 8 | extern crate gluon_parser as parser; 9 | 10 | mod support; 11 | 12 | use crate::support::*; 13 | 14 | use crate::base::types::Type; 15 | 16 | use crate::completion::SignatureHelp; 17 | 18 | fn signature_help(expr_str: &str, row: usize, column: usize) -> Option { 19 | let offset = loc(expr_str, row, column); 20 | let (expr, _result) = support::typecheck_partial_expr(expr_str); 21 | let expr = expr.expr(); 22 | completion::signature_help(&support::MockEnv::new(), expr.span, &expr, offset) 23 | } 24 | 25 | #[test] 26 | fn just_identifier() { 27 | let _ = env_logger::try_init(); 28 | 29 | let result = signature_help( 30 | r#" 31 | let test x y : Int -> String -> Int = x 32 | test // 33 | "#, 34 | 2, 35 | 5, 36 | ); 37 | let expected = Some(SignatureHelp { 38 | name: "test".to_string(), 39 | typ: Type::function(vec![typ("Int"), typ("String")], typ("Int")), 40 | index: Some(0), 41 | }); 42 | 43 | assert_eq!(result, expected); 44 | } 45 | 46 | #[test] 47 | fn on_function() { 48 | let _ = env_logger::try_init(); 49 | 50 | let result = signature_help( 51 | r#" 52 | let test x y : Int -> String -> Int = x 53 | test 123// 54 | "#, 55 | 2, 56 | 3, 57 | ); 58 | let expected = Some(SignatureHelp { 59 | name: "test".to_string(), 60 | typ: Type::function(vec![typ("Int"), typ("String")], typ("Int")), 61 | index: None, 62 | }); 63 | 64 | assert_eq!(result, expected); 65 | } 66 | 67 | #[test] 68 | fn after_first_argument() { 69 | let _ = env_logger::try_init(); 70 | 71 | let result = signature_help( 72 | r#" 73 | let test x y : Int -> String -> Int = x 74 | test 123 // 75 | "#, 76 | 2, 77 | 9, 78 | ); 79 | let expected = Some(SignatureHelp { 80 | name: "test".to_string(), 81 | typ: Type::function(vec![typ("Int"), typ("String")], typ("Int")), 82 | index: Some(1), 83 | }); 84 | 85 | assert_eq!(result, expected); 86 | } 87 | 88 | #[test] 89 | fn inside_argument() { 90 | let _ = env_logger::try_init(); 91 | 92 | let result = signature_help( 93 | r#" 94 | let test x y : Int -> String -> Int = x 95 | test { x = "" } 96 | "#, 97 | 2, 98 | 13, 99 | ); 100 | let expected = Some(SignatureHelp { 101 | name: "".to_string(), 102 | typ: typ("String"), 103 | index: None, 104 | }); 105 | 106 | assert_eq!(result, expected); 107 | } 108 | -------------------------------------------------------------------------------- /doc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gluon_doc" 3 | version = "0.18.2" # GLUON 4 | authors = ["Markus Westerlind "] 5 | edition = "2018" 6 | 7 | license = "MIT" 8 | 9 | description = "The documentation generator for the gluon programming language" 10 | 11 | repository = "https://github.com/gluon-lang/gluon" 12 | documentation = "https://docs.rs/gluon" 13 | 14 | [dependencies] 15 | clap = "2.34.0" 16 | env_logger = "0.9.3" 17 | anyhow = "1.0.75" 18 | futures = "0.3.28" 19 | handlebars = "4.3.7" 20 | itertools = "0.10.5" 21 | lazy_static = "1.4.0" 22 | log = "0.4.20" 23 | opener = "0.5.2" 24 | pretty = "0.10.0" 25 | pulldown-cmark = "0.8.0" 26 | rayon = "1.7.0" 27 | regex = "1.9.4" 28 | structopt = "0.3.26" 29 | walkdir = "2.3.3" 30 | 31 | serde = "1.0.188" 32 | serde_derive = "1.0.188" 33 | serde_json = "1.0.105" 34 | 35 | gluon = { version = "0.18.2", default-features = false, path = ".." } # GLUON 36 | completion = { package = "gluon_completion", version = "0.18.2", path = "../completion" } # GLUON 37 | 38 | 39 | [dev-dependencies] 40 | cargo-deadlinks = "0.8.1" 41 | -------------------------------------------------------------------------------- /doc/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::path::Path; 3 | use std::process::Command; 4 | 5 | fn main() { 6 | if env::var("GIT_HASH").is_err() { 7 | let output = Command::new("git") 8 | .args(&["rev-parse", "HEAD"]) 9 | .output() 10 | .unwrap(); 11 | let git_hash = String::from_utf8(output.stdout).unwrap(); 12 | println!("cargo:rustc-env=GIT_HASH={}", git_hash); 13 | } 14 | let git_edit_msg = "../.git/COMMIT_EDITMSG"; 15 | if Path::new(git_edit_msg).exists() { 16 | // This is the closest thing to making sure we rebuild this every time a new commit is made 17 | println!("cargo:rerun-if-changed={}", git_edit_msg); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /doc/src/doc/style.css: -------------------------------------------------------------------------------- 1 | .anchor { 2 | color: inherit; 3 | text-decoration: inherit; 4 | } 5 | -------------------------------------------------------------------------------- /doc/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate env_logger; 2 | extern crate opener; 3 | extern crate rayon; 4 | extern crate structopt; 5 | 6 | extern crate gluon; 7 | extern crate gluon_doc; 8 | 9 | use std::path::Path; 10 | 11 | use structopt::StructOpt; 12 | 13 | fn main() { 14 | if let Err(err) = main_() { 15 | eprintln!("{}", err); 16 | } 17 | } 18 | 19 | fn main_() -> Result<(), anyhow::Error> { 20 | env_logger::init(); 21 | 22 | let opt = gluon_doc::Opt::from_args(); 23 | 24 | if let Some(jobs) = opt.jobs { 25 | rayon::ThreadPoolBuilder::new() 26 | .num_threads(jobs) 27 | .build_global()?; 28 | } 29 | 30 | gluon_doc::generate(&gluon_doc::Options::from(&opt), &gluon::new_vm())?; 31 | 32 | if opt.open { 33 | let path = Path::new(&opt.output) 34 | .join(&opt.input) 35 | .with_extension("html"); 36 | eprintln!("Opening {}", path.display()); 37 | opener::open(path)?; 38 | } 39 | 40 | Ok(()) 41 | } 42 | -------------------------------------------------------------------------------- /doc/tests/doc.rs: -------------------------------------------------------------------------------- 1 | use std::{fs, path::Path}; 2 | 3 | use {itertools::Itertools, rayon::prelude::*}; 4 | 5 | use gluon_doc as doc; 6 | 7 | use gluon::{check::metadata::metadata, RootedThread, ThreadExt}; 8 | 9 | fn new_vm() -> RootedThread { 10 | ::gluon::VmBuilder::new() 11 | .import_paths(Some(vec!["..".into()])) 12 | .build() 13 | } 14 | 15 | fn doc_check(module: &str, expected: doc::Record) { 16 | let vm = new_vm(); 17 | let (expr, typ) = vm.typecheck_str("basic", module, None).unwrap(); 18 | let (meta, _) = metadata(&vm.get_env(), &expr.expr()); 19 | 20 | let out = doc::record( 21 | "basic", 22 | &typ, 23 | &Default::default(), 24 | &<() as gluon::base::source::Source>::new(""), 25 | &meta, 26 | ); 27 | assert_eq!(out, expected,); 28 | } 29 | 30 | #[test] 31 | fn basic() { 32 | let module = r#" 33 | /// This is the test function 34 | let test x = x 35 | { test } 36 | "#; 37 | doc_check( 38 | module, 39 | doc::Record { 40 | types: Vec::new(), 41 | values: vec![doc::Field { 42 | name: "test".to_string(), 43 | args: vec![doc::Argument { 44 | implicit: false, 45 | name: "x".to_string(), 46 | }], 47 | typ: handlebars::html_escape("forall a . a -> a"), 48 | attributes: "".to_string(), 49 | comment: "This is the test function".to_string(), 50 | definition_line: None, 51 | }], 52 | }, 53 | ); 54 | } 55 | 56 | #[test] 57 | fn doc_hidden() { 58 | let module = r#" 59 | #[doc(hidden)] 60 | type Test = Int 61 | #[doc(hidden)] 62 | let test x = x 63 | { Test, test } 64 | "#; 65 | doc_check( 66 | module, 67 | doc::Record { 68 | types: Vec::new(), 69 | values: vec![], 70 | }, 71 | ); 72 | } 73 | 74 | #[test] 75 | fn check_links() { 76 | let _ = env_logger::try_init(); 77 | 78 | let out = Path::new("../target/doc_test"); 79 | if out.exists() { 80 | fs::remove_dir_all(out).unwrap_or_else(|err| panic!("{}", err)); 81 | } 82 | doc::generate_for_path(&new_vm(), "../std", out).unwrap_or_else(|err| panic!("{}", err)); 83 | 84 | let out = fs::canonicalize(out).unwrap(); 85 | let errors = cargo_deadlinks::unavailable_urls( 86 | &out, 87 | &cargo_deadlinks::CheckContext { 88 | check_http: cargo_deadlinks::HttpCheck::Enabled, 89 | ..Default::default() 90 | }, 91 | ) 92 | .collect::>(); 93 | 94 | assert!(errors.is_empty(), "{}", errors.iter().format("\n")); 95 | } 96 | -------------------------------------------------------------------------------- /examples/24.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "rand")] 2 | 3 | use std::{fs::File, io::Read}; 4 | 5 | use gluon::{vm::api::IO, ThreadExt}; 6 | 7 | fn main() { 8 | env_logger::init(); 9 | 10 | if let Err(err) = main_() { 11 | eprintln!("{}", err); 12 | std::process::exit(1); 13 | } 14 | } 15 | 16 | fn main_() -> Result<(), Box> { 17 | let thread = gluon::new_vm(); 18 | thread.get_database_mut().run_io(true); 19 | 20 | let mut source = String::new(); 21 | let mut file = File::open("examples/24.glu")?; 22 | file.read_to_string(&mut source)?; 23 | 24 | thread.run_expr::>("24", &source)?; 25 | Ok(()) 26 | } 27 | -------------------------------------------------------------------------------- /examples/fib.rs: -------------------------------------------------------------------------------- 1 | use gluon::{new_vm, vm::api::FunctionRef, ThreadExt}; 2 | 3 | fn fib(n: u64) -> u64 { 4 | if n <= 1 { 5 | 1 6 | } else { 7 | fib(n - 1) + fib(n - 2) 8 | } 9 | } 10 | 11 | fn main() { 12 | const N: u64 = 46; 13 | if std::env::args().nth(1).as_ref().map(|s| &s[..]) == Some("rust") { 14 | println!("{}", fib(N)); 15 | } else { 16 | let vm = new_vm(); 17 | let text = r#" 18 | let fib n = 19 | if n #Int< 2 20 | then 1 21 | else fib (n #Int- 1) #Int+ fib (n #Int- 2) 22 | fib 23 | "#; 24 | vm.load_script("fib", text) 25 | .unwrap_or_else(|err| panic!("{}", err)); 26 | let mut fib: FunctionRef u64> = 27 | vm.get_global("fib").unwrap_or_else(|err| panic!("{}", err)); 28 | let result = fib.call(N).unwrap_or_else(|err| panic!("{}", err)); 29 | println!("{}", result); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /examples/lisp/main.rs: -------------------------------------------------------------------------------- 1 | extern crate env_logger; 2 | 3 | extern crate gluon; 4 | #[macro_use] 5 | extern crate gluon_codegen; 6 | 7 | use std::io::{self, BufRead}; 8 | 9 | use gluon::{ 10 | new_vm, 11 | vm::api::{FunctionRef, OpaqueValue}, 12 | RootedThread, ThreadExt, 13 | }; 14 | 15 | #[derive(VmType)] 16 | #[gluon(vm_type = "examples.lisp.lisp.Expr")] 17 | struct ExprMarker; 18 | type Expr = OpaqueValue; 19 | 20 | #[derive(VmType)] 21 | #[gluon(vm_type = "examples.lisp.lisp.LispState")] 22 | struct LispStateMarker; 23 | type LispState = OpaqueValue; 24 | 25 | fn main() { 26 | if let Err(err) = main_() { 27 | eprintln!("{}", err); 28 | } 29 | } 30 | 31 | fn main_() -> gluon::Result<()> { 32 | env_logger::init(); 33 | 34 | let thread = new_vm(); 35 | thread.load_file("examples/lisp/lisp.glu")?; 36 | 37 | let mut eval: FunctionRef Result<(Expr, LispState), String>> = 38 | thread.get_global("examples.lisp.lisp.eval_env_string")?; 39 | 40 | let mut show: FunctionRef String> = 41 | thread.get_global("examples.lisp.lisp.show.show")?; 42 | 43 | let mut env: LispState = thread.get_global("examples.lisp.lisp.default_env")?; 44 | 45 | let stdin = io::stdin(); 46 | for line in stdin.lock().lines() { 47 | let line = line?; 48 | match eval.call(line, env.clone())? { 49 | Ok((msg, new_env)) => { 50 | println!("{}", show.call(msg.clone())?); 51 | env = new_env; 52 | } 53 | Err(err) => { 54 | eprintln!("{}", err); 55 | } 56 | } 57 | } 58 | Ok(()) 59 | } 60 | 61 | #[cfg(test)] 62 | mod tests { 63 | use super::*; 64 | 65 | use gluon::Error; 66 | 67 | fn eval_lisp(expr: &str) -> Result { 68 | let thread = new_vm(); 69 | thread.load_file("examples/lisp/lisp.glu")?; 70 | 71 | let mut eval: FunctionRef Result<(Expr, LispState), String>> = 72 | thread.get_global("examples.lisp.lisp.eval_env_string")?; 73 | 74 | let mut show: FunctionRef String> = 75 | thread.get_global("examples.lisp.lisp.show.show")?; 76 | 77 | let env: LispState = thread.get_global("examples.lisp.lisp.default_env")?; 78 | 79 | let (msg, _) = eval.call(expr.to_string(), env.clone())??; 80 | Ok(show.call(msg.clone())?) 81 | } 82 | 83 | #[test] 84 | fn basic() { 85 | assert_eq!(eval_lisp("(+ 1 2 3)").unwrap(), "6"); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /examples/lisp/parser.glu: -------------------------------------------------------------------------------- 1 | let prelude = import! std.prelude 2 | let { Expr } = import! "examples/lisp/types.glu" 3 | let char = import! std.char 4 | let { List } = import! std.list 5 | let { Result } = import! std.result 6 | let string = import! std.string 7 | let float = import! std.float 8 | let int = import! std.int 9 | 10 | let { 11 | Parser, 12 | fail, 13 | between, 14 | lazy_parser, 15 | many, 16 | one_of, 17 | satisfy, 18 | spaces, 19 | token, 20 | recognize, 21 | take1, 22 | skip_many, 23 | skip_many1, 24 | ? 25 | } = 26 | import! std.parser 27 | 28 | let { (<>) } = import! std.prelude 29 | 30 | let { Applicative, (<*), (*>), map2, wrap } = import! std.applicative 31 | let { map } = import! std.functor 32 | let { (<|>) } = import! std.alternative 33 | let { flat_map } = import! std.monad 34 | 35 | let atom : Parser Expr = 36 | let symbol = one_of "!#$%&|*+-/:<=>?@^_~" 37 | let alpha = satisfy char.is_alphabetic 38 | let alpha_num = satisfy char.is_alphanumeric 39 | map (\x -> Atom x) (recognize ((alpha <|> symbol) *> skip_many (alpha_num <|> symbol))) 40 | 41 | let parse_parser p f msg : Parser a -> (String -> Result () b) -> String -> Parser b = 42 | do s = recognize p 43 | match f s with 44 | | Ok i -> wrap i 45 | | Err _ -> fail ("Expected " <> msg) 46 | 47 | let int_parser : Parser Expr = 48 | map 49 | (\i -> Int i) 50 | (parse_parser (skip_many1 (satisfy char.is_numeric)) int.parse "integer") 51 | 52 | let float_parser : Parser Expr = 53 | let number = skip_many1 (satisfy char.is_numeric) 54 | map (\f -> Float f) (parse_parser (number *> token '.' *> number) float.parse "float") 55 | 56 | rec 57 | let list _ : () -> Parser Expr = 58 | let e = expr () 59 | between (token '(') (token ')') (spaces *> map (\x -> List x) (many e)) 60 | let expr _ : () -> Parser Expr = (atom <|> float_parser <|> int_parser <|> lazy_parser list) <* spaces 61 | in 62 | 63 | { 64 | expr = spaces *> lazy_parser expr, 65 | } 66 | -------------------------------------------------------------------------------- /examples/lisp/types.glu: -------------------------------------------------------------------------------- 1 | let { List } = import! std.list 2 | let { Map } = import! std.map 3 | let { Option } = import! std.option 4 | let { Result } = import! std.result 5 | 6 | let { Eff } = import! std.effect 7 | let { State } = import! std.effect.state 8 | let { Error } = import! std.effect.error 9 | 10 | rec 11 | type Expr = 12 | | Atom String 13 | | Int Int 14 | | Float Float 15 | | List (List Expr) 16 | | Function Function 17 | | Primitive (forall r . List Expr -> Eff (LispEffect r) Expr) 18 | type Function = { 19 | params : List String, 20 | vararg : Option String, 21 | body : List Expr, 22 | closure : Map String Expr 23 | } 24 | type LispState = Map String Expr 25 | type LispEffect r a = [| error : Error String, state : State LispState | r |] a 26 | in 27 | 28 | { Expr, Function, LispState, LispEffect } 29 | -------------------------------------------------------------------------------- /format/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gluon_format" 3 | version = "0.18.2" # GLUON 4 | authors = ["Markus "] 5 | edition = "2018" 6 | 7 | license = "MIT" 8 | 9 | description = "Code formatting for the gluon programming language" 10 | 11 | repository = "https://github.com/gluon-lang/gluon" 12 | documentation = "https://docs.rs/gluon" 13 | 14 | [dependencies] 15 | log = "0.4.20" 16 | pretty = "0.10.0" 17 | itertools = "0.10.5" 18 | codespan = "0.11.1" 19 | 20 | gluon_base = { path = "../base", version = "0.18.2" } # GLUON 21 | 22 | [dev-dependencies] 23 | difference = "2.0.0" 24 | env_logger = "0.9.3" 25 | expect-test = "1.4.1" 26 | futures = "0.3.28" 27 | pretty_assertions = "1.4.0" 28 | tokio = { version = "1.32.0", features = ["macros"] } 29 | walkdir = "2.3.3" 30 | 31 | gluon_base = { path = "../base", version = "0.18.2" } # GLUON 32 | gluon = { path = "..", version = ">=0.9" } 33 | 34 | tensile = { version = "0.7", features = ["tokio"] } 35 | 36 | [features] 37 | test = [] 38 | 39 | [[test]] 40 | name = "std" 41 | harness = false 42 | -------------------------------------------------------------------------------- /format/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Code formatter. 2 | #![doc(html_root_url = "https://docs.rs/gluon_formatter/0.18.2")] // # GLUON 3 | 4 | extern crate codespan; 5 | #[macro_use] 6 | extern crate gluon_base as base; 7 | extern crate itertools; 8 | extern crate pretty; 9 | 10 | use base::{ast::SpannedExpr, source::Source, symbol::Symbol}; 11 | 12 | mod pretty_print; 13 | 14 | pub fn pretty_expr(input: &dyn Source, expr: &SpannedExpr) -> String { 15 | Formatter::default().pretty_expr(input, expr) 16 | } 17 | 18 | #[derive(Default, Debug, Clone)] 19 | pub struct Formatter { 20 | /// Prints the source code after macro expansion 21 | /// 22 | /// NOTE: This is only provided for debug purposes and is likely to have have bugs 23 | pub expanded: bool, 24 | } 25 | 26 | impl Formatter { 27 | pub fn pretty_expr(&self, source: &dyn Source, expr: &SpannedExpr) -> String { 28 | let input = source.src(); 29 | let newline = match input.find(|c: char| c == '\n' || c == '\r') { 30 | Some(i) => { 31 | if input[i..].starts_with("\r\n") { 32 | "\r\n" 33 | } else if input[i..].starts_with("\r") { 34 | "\r" 35 | } else { 36 | "\n" 37 | } 38 | } 39 | None => "\n", 40 | }; 41 | 42 | let arena = pretty::Arena::<()>::new(); 43 | let printer = pretty_print::Printer::new(&arena, source, self.clone()); 44 | printer.format(100, newline, &expr) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /parser/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gluon_parser" 3 | version = "0.18.2" # GLUON 4 | authors = ["Markus "] 5 | edition = "2018" 6 | 7 | license = "MIT" 8 | 9 | description = "The parser for the gluon programming language" 10 | 11 | homepage = "https://gluon-lang.org" 12 | repository = "https://github.com/gluon-lang/gluon" 13 | documentation = "https://docs.rs/gluon" 14 | 15 | build = "build.rs" 16 | 17 | [dependencies] 18 | collect-mac = "0.1.0" 19 | itertools = "0.10.5" 20 | quick-error = "2.0.1" 21 | lalrpop-util = "0.19.12" 22 | log = "0.4.20" 23 | gluon_base = { path = "../base", version = "0.18.2" } # GLUON 24 | ordered-float = "2.10.0" 25 | codespan = "0.11.1" 26 | codespan-reporting = "0.11.1" 27 | 28 | [dev-dependencies] 29 | criterion = "0.3.6" 30 | env_logger = "0.9.3" 31 | difference = "2.0.0" 32 | pretty_assertions = "1.4.0" 33 | 34 | [build-dependencies] 35 | lalrpop = "0.19.12" 36 | 37 | [[bench]] 38 | name = "parser" 39 | harness = false 40 | -------------------------------------------------------------------------------- /parser/benches/parser.rs: -------------------------------------------------------------------------------- 1 | extern crate gluon_base as base; 2 | extern crate gluon_parser as parser; 3 | 4 | use std::fs; 5 | 6 | use criterion::{criterion_group, criterion_main, Bencher, Criterion}; 7 | 8 | use crate::base::{ 9 | ast, mk_ast_arena, 10 | symbol::{SymbolModule, Symbols}, 11 | types::TypeCache, 12 | }; 13 | 14 | fn parse_file(b: &mut Bencher, file: &str) { 15 | let text = fs::read_to_string(file).unwrap(); 16 | 17 | b.iter(|| { 18 | let mut symbols = Symbols::new(); 19 | let mut symbols = SymbolModule::new("".into(), &mut symbols); 20 | mk_ast_arena!(arena); 21 | let expr = parser::parse_expr(arena.borrow(), &mut symbols, &TypeCache::default(), &text) 22 | .unwrap_or_else(|err| panic!("{:?}", err)); 23 | let expr = arena.alloc(expr); 24 | ast::RootExpr::new(arena.clone(), expr) 25 | }) 26 | } 27 | 28 | fn parse_benchmark(c: &mut Criterion) { 29 | c.bench_function("std/prelude", |b| parse_file(b, "../std/prelude.glu")); 30 | c.bench_function("examples/lisp", |b| { 31 | parse_file(b, "../examples/lisp/lisp.glu") 32 | }); 33 | c.bench_function("examples/http", |b| { 34 | parse_file(b, "../examples/http/server.glu") 35 | }); 36 | } 37 | 38 | criterion_group!(parser, parse_benchmark); 39 | criterion_main!(parser); 40 | -------------------------------------------------------------------------------- /parser/build.rs: -------------------------------------------------------------------------------- 1 | extern crate lalrpop; 2 | 3 | fn main() { 4 | lalrpop::Configuration::new() 5 | .use_cargo_dir_conventions() 6 | .process_file("src/grammar.lalrpop") 7 | .unwrap(); 8 | 9 | println!("cargo:rerun-if-changed=src/grammar.lalrpop"); 10 | } 11 | -------------------------------------------------------------------------------- /parser/tests/attributes.rs: -------------------------------------------------------------------------------- 1 | extern crate env_logger; 2 | extern crate gluon_base as base; 3 | extern crate gluon_parser as parser; 4 | 5 | #[macro_use] 6 | mod support; 7 | 8 | use crate::support::*; 9 | 10 | #[test] 11 | fn any_tokens() { 12 | let _ = ::env_logger::try_init(); 13 | let text = r#" 14 | #[test(ident "string" 42 = 'a' + )] 15 | let (+) x y = error "" 16 | { } 17 | "#; 18 | parse_clear_span!(text); 19 | } 20 | 21 | #[test] 22 | fn bindings() { 23 | let _ = ::env_logger::try_init(); 24 | let text = r#" 25 | #[infix(left, 6)] 26 | let (+) x y = error "" 27 | #[implicit] 28 | type Test = Int 29 | 30 | { 31 | #[abc()] 32 | Test, 33 | #[test] 34 | t = \_ -> True 35 | } 36 | "#; 37 | parse_clear_span!(text); 38 | } 39 | -------------------------------------------------------------------------------- /parser/tests/types.rs: -------------------------------------------------------------------------------- 1 | extern crate gluon_base as base; 2 | extern crate gluon_parser as parser; 3 | 4 | use crate::base::{ast::Expr, mk_ast_arena, types::TypeContext}; 5 | use crate::support::{clear_span, parse, typ}; 6 | 7 | mod support; 8 | 9 | #[test] 10 | fn function_type() { 11 | let _ = env_logger::try_init(); 12 | 13 | let input = "let _ : Int -> Float -> String = 1 in 1"; 14 | let expr = parse(input).unwrap_or_else(|err| panic!("{}", err.1)); 15 | mk_ast_arena!(arena); 16 | let mut arena = (*arena).borrow(); 17 | match clear_span(expr).expr().value { 18 | Expr::LetBindings(ref bindings, _) => { 19 | assert_eq!( 20 | bindings[0].typ, 21 | Some(arena.function( 22 | vec![typ(arena, "Int"), typ(arena, "Float")], 23 | typ(arena, "String"), 24 | ),) 25 | ); 26 | } 27 | _ => panic!("Expected let"), 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /repl/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gluon_repl" 3 | version = "0.18.2" # GLUON 4 | authors = ["Markus Westerlind "] 5 | edition = "2018" 6 | 7 | license = "MIT" 8 | description = "REPL for gluon. A static, type inferred programming language for application embedding" 9 | 10 | homepage = "https://gluon-lang.org" 11 | repository = "https://github.com/gluon-lang/gluon" 12 | documentation = "https://docs.rs/gluon" 13 | 14 | [[bin]] 15 | name = "gluon" 16 | path = "src/main.rs" 17 | doc = false 18 | 19 | [dependencies] 20 | gluon = { version = "0.18.2", path = "..", features = ["serialization"] } # GLUON 21 | gluon_vm = { version = "0.18.2", path = "../vm", features = ["serialization"] } # GLUON 22 | gluon_completion = { path = "../completion", version = "0.18.2" } # GLUON 23 | gluon_codegen = { path = "../codegen", version = "0.18.2" } # GLUON 24 | gluon_format = { version = "0.18.2", path = "../format" } # GLUON 25 | gluon_doc = { version = "0.18.2", path = "../doc" } # GLUON 26 | 27 | app_dirs = { package = "app_dirs2", version = "2.5.5" } 28 | anyhow = "1.0.75" 29 | futures = "0.3.28" 30 | tokio = { version = "1.32.0", features = ["rt-multi-thread", "macros", "signal"] } 31 | clap = "2.34.0" 32 | structopt = "0.3.26" 33 | log = "0.4.20" 34 | env_logger = { version = "0.9.3", optional = true } 35 | lazy_static = "1.4.0" 36 | rustyline = "=6.0.0" 37 | walkdir = "2.3.3" 38 | codespan = "0.11.1" 39 | codespan-reporting = "0.11.1" 40 | quick-error = "2.0.1" 41 | 42 | serde = "1.0.188" 43 | serde_derive = "1.0.188" 44 | 45 | [target.'cfg(not(windows))'.dependencies] 46 | ansi_term = "0.12.1" 47 | 48 | [dev-dependencies] 49 | pretty_assertions = "1.4.0" 50 | tokio = "1.32.0" 51 | 52 | [target.'cfg(unix)'.dev-dependencies] 53 | rexpect = "0.4.0" 54 | 55 | [features] 56 | default = ["env_logger"] 57 | test = [] 58 | 59 | -------------------------------------------------------------------------------- /repl/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::path::Path; 3 | use std::process::Command; 4 | 5 | fn main() { 6 | if env::var("GIT_HASH").is_err() { 7 | let output = Command::new("git") 8 | .args(&["rev-parse", "HEAD"]) 9 | .output() 10 | .unwrap(); 11 | let git_hash = String::from_utf8(output.stdout).unwrap(); 12 | println!("cargo:rustc-env=GIT_HASH={}", git_hash); 13 | } 14 | 15 | let edit_msg = Path::new("../.git/COMMIT_EDITMSG"); 16 | if edit_msg.exists() { 17 | // This is the closest thing to making sure we rebuild this every time a new commit is made 18 | println!("cargo:rerun-if-changed={}", edit_msg.display()); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /repl/tests/basic.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate pretty_assertions; 3 | 4 | use std::env; 5 | use std::path::Path; 6 | use std::process::{Command, Stdio}; 7 | 8 | #[test] 9 | fn issue_365_run_io_from_command_line() { 10 | if ::std::env::var("GLUON_PATH").is_err() { 11 | ::std::env::set_var("GLUON_PATH", ".."); 12 | } 13 | 14 | let path = env::args().next().unwrap(); 15 | let gluon_path = Path::new(&path[..]) 16 | .parent() 17 | .and_then(|p| p.parent()) 18 | .expect("folder") 19 | .join("gluon"); 20 | let output = Command::new(&*gluon_path) 21 | .stdin(Stdio::piped()) 22 | .stdout(Stdio::piped()) 23 | .stderr(Stdio::piped()) 24 | .arg("tests/print.glu") 25 | .output() 26 | .unwrap_or_else(|err| panic!("{}\nWhen opening `{}`", err, gluon_path.display())); 27 | 28 | let stderr = String::from_utf8_lossy(&output.stderr); 29 | if stderr != "" { 30 | panic!("{}", stderr); 31 | } 32 | assert_eq!(String::from_utf8_lossy(&output.stdout), "123\n"); 33 | } 34 | -------------------------------------------------------------------------------- /repl/tests/print.glu: -------------------------------------------------------------------------------- 1 | let io = import! std.io 2 | io.println "123" 3 | -------------------------------------------------------------------------------- /scripts/before_deploy.sh: -------------------------------------------------------------------------------- 1 | # This script takes care of building your crate and packaging it for release 2 | 3 | set -ex 4 | 5 | main() { 6 | local src=$(pwd) \ 7 | stage= 8 | 9 | case $TRAVIS_OS_NAME in 10 | linux) 11 | stage=$(mktemp -d) 12 | ;; 13 | osx) 14 | stage=$(mktemp -d -t tmp) 15 | ;; 16 | esac 17 | 18 | test -f Cargo.lock || cargo generate-lockfile 19 | 20 | cross build -p gluon_repl --target $TARGET --release 21 | 22 | # Copy the files that are needed in the distribution 23 | if [ -f target/$TARGET/release/gluon ]; then 24 | cp target/$TARGET/release/gluon $stage/ 25 | elif [ -f target/$TARGET/release/gluon.exe ]; then 26 | cp target/$TARGET/release/gluon.exe $stage/ 27 | else 28 | echo "Could not find gluon executable" 29 | exit 1 30 | fi 31 | mkdir $stage/std 32 | cp -r std/* $stage/std/ 33 | 34 | cd $stage 35 | tar czf $src/$CRATE_NAME-$TRAVIS_TAG-$TARGET.tar.gz * 36 | cd $src 37 | 38 | rm -rf $stage 39 | 40 | mv $src/$CRATE_NAME-$TRAVIS_TAG-$TARGET.tar.gz target/ 41 | } 42 | 43 | main 44 | -------------------------------------------------------------------------------- /scripts/ci.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | 4 | export RUST_BACKTRACE=1 5 | 6 | declare -a PROJECTS=( 7 | gluon_codegen 8 | gluon_base 9 | gluon_parser 10 | gluon_check 11 | gluon_completion 12 | gluon_vm 13 | gluon_format 14 | gluon 15 | gluon_c-api 16 | gluon_doc 17 | gluon_repl 18 | ) 19 | 20 | if [ -z $NO_NORMAL_TEST ]; then 21 | cargo test --features "test" --all "$@" 22 | cargo test --features "test" --all --bins "$@" 23 | cargo test --features "test" --all --examples "$@" 24 | cargo test --features "test" --all --benches "$@" 25 | cargo test --features "test" -p gluon_parser --benches "$@" 26 | echo "" | cargo run --features "test" --example 24 27 | cargo run --features "test" --example marshalling 28 | 29 | echo "TRAVIS_RUST_VERSION=$TRAVIS_RUST_VERSION" 30 | (echo $TRAVIS_RUST_VERSION | grep nightly) && cargo test --features "test nightly" -p gluon --test compiletest "$@" 31 | 32 | # Check each crate individually so that features from one do not affect another which would break publish 33 | for PROJECT in "${PROJECTS[@]}" 34 | do 35 | cargo check --package "${PROJECT}" --no-default-features "$@" 36 | done 37 | fi 38 | -------------------------------------------------------------------------------- /scripts/install_cross.sh: -------------------------------------------------------------------------------- 1 | set -ex 2 | 3 | main() { 4 | local target=x86_64-unknown-linux-musl 5 | # https://github.com/cross-rs/cross/releases/download/v0.2.5/cross-x86_64-unknown-linux-musl.tar.gz 6 | # This fetches latest stable release 7 | local tag=$(git ls-remote --tags --refs --exit-code https://github.com/cross-rs/cross \ 8 | | cut -d/ -f3 \ 9 | | grep -E '^v[0.1.0-9.]+$' \ 10 | | sort --version-sort \ 11 | | tail -n1) 12 | sh ./scripts/install.sh -- \ 13 | --force \ 14 | --git cross-rs/cross \ 15 | --tag $tag \ 16 | --target $target 17 | } 18 | 19 | main 20 | -------------------------------------------------------------------------------- /scripts/install_mdbook.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ex 4 | 5 | 6 | if [[ $1 == *"apple"* ]]; then 7 | exit 0 8 | else 9 | TARGET='x86_64-unknown-linux-musl' 10 | fi 11 | 12 | MDBOOK_VERSION="mdbook-v0.2.1-${TARGET}" 13 | curl -L "https://github.com/rust-lang-nursery/mdBook/releases/download/v0.2.1/$MDBOOK_VERSION.tar.gz" | tar -xvz 14 | chmod +x ./mdbook 15 | mv ./mdbook $HOME/bin/ 16 | -------------------------------------------------------------------------------- /scripts/install_sccache.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ex 4 | 5 | # https://stackoverflow.com/a/34676160/2489366 6 | # the directory of the script 7 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 8 | 9 | # the temp directory used, within $DIR 10 | # omit the -p parameter to create a temporal directory in the default location 11 | WORK_DIR=`mktemp -d "$DIR.XXXXXXXX"` 12 | 13 | # check if tmp dir was created 14 | if [[ ! "$WORK_DIR" || ! -d "$WORK_DIR" ]]; then 15 | echo "Could not create temp dir" 16 | exit 1 17 | fi 18 | 19 | # deletes the temp directory 20 | function cleanup { 21 | rm -rf "$WORK_DIR" 22 | echo "Deleted temp working directory $WORK_DIR" 23 | } 24 | 25 | # register the cleanup function to be called on the EXIT signal 26 | trap cleanup EXIT 27 | 28 | # Sample links 29 | # https://github.com/mozilla/sccache/releases/download/v0.5.4/sccache-dist-v0.5.4-x86_64-unknown-linux-musl.tar.gz 30 | # https://github.com/mozilla/sccache/releases/download/v0.5.4/sccache-v0.5.4-aarch64-apple-darwin.tar.gz 31 | 32 | VERSION="v0.4.2" 33 | 34 | if [[ $1 == *"apple"* ]]; then 35 | TARGET=$1 36 | SCCACHE_VERSION="sccache-${VERSION}-aarch64-${TARGET}-darwin" 37 | else 38 | TARGET='x86_64-unknown-linux-musl' 39 | SCCACHE_VERSION="sccache-dist-${VERSION}-${TARGET}" 40 | fi 41 | 42 | pushd ${WORK_DIR} 43 | 44 | curl -L "https://github.com/mozilla/sccache/releases/download/${VERSION}/$SCCACHE_VERSION.tar.gz" | tar -xvz 45 | mv $SCCACHE_VERSION/sccache* ./sccache 46 | # it is sccache-dist on linux and sccache on mac 47 | chmod +x ./sccache 48 | mv ./sccache $HOME/bin/ 49 | 50 | popd 51 | -------------------------------------------------------------------------------- /scripts/publish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | 4 | VERSION=$(echo $1 | sed 's/v//') 5 | shift 6 | 7 | function retry { 8 | for i in {0..10}; do 9 | $@ 10 | if [ $? -eq 0 ]; then 11 | exit 0 12 | fi 13 | sleep 3 14 | done 15 | exit 1 16 | } 17 | 18 | 19 | declare -a PROJECTS=( 20 | gluon_codegen 21 | gluon_base 22 | gluon_parser 23 | gluon_check 24 | gluon_completion 25 | gluon_vm 26 | gluon_format 27 | gluon 28 | gluon_c-api 29 | gluon_doc 30 | gluon_repl 31 | ) 32 | 33 | for PROJECT in "${PROJECTS[@]}" 34 | do 35 | PROJECT_PATH=$(echo "$PROJECT" | sed 's/gluon_//' | sed 's/gluon/./') 36 | 37 | if ! (./scripts/sync_publish.sh "$(pwd)/${PROJECT_PATH}" -f "$@"); then 38 | echo "Failed to publish $PROJECT_PATH" 39 | exit 1 40 | fi 41 | done 42 | -------------------------------------------------------------------------------- /scripts/release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | 4 | LEVEL=$1 5 | VERSION=$2 6 | if [ -z "$LEVEL" ]; then 7 | echo "Expected patch, minor or major" 8 | exit 1 9 | fi 10 | 11 | clog --$LEVEL 12 | if [[ -z $(head -1 CHANGELOG.md | grep $VERSION) ]]; then 13 | git checkout CHANGELOG.md 14 | echo "Wrong version specified" 15 | exit 1 16 | fi 17 | 18 | git add CHANGELOG.md 19 | 20 | ./scripts/version.sh $VERSION 21 | -------------------------------------------------------------------------------- /scripts/sync_publish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Usage: sync_publish /path/to/crate -f 4 | # 5 | # Publish a crate and wait for it to become available. 6 | # 7 | # https://gist.github.com/Riateche/a1c500fe760a2b9190beb0a7134db82d 8 | 9 | set -e 10 | set -o pipefail 11 | 12 | TMP_DIR=/tmp/test1 13 | 14 | DIR="$1" 15 | FORCE="$2" 16 | shift 17 | shift 18 | 19 | NAME=$(grep '^name' "$DIR/Cargo.toml" | head -n 1 | sed 's/name = "\([^"]*\)"/\1/') 20 | cd "$DIR" 21 | 22 | VERSION=$(cargo metadata --format-version 1 2>/dev/null | jq -r '.packages[] | select(.name=="'$NAME'").version') 23 | 24 | rm -rf "$TMP_DIR" 25 | cargo new "$TMP_DIR" > /dev/null 2>&1 26 | cd "$TMP_DIR" 27 | if cargo add "$NAME@$VERSION" > /dev/null 2>&1; then 28 | echo "$NAME=$VERSION already exists, skipping." 29 | exit 0 30 | fi 31 | 32 | echo "Publishing $NAME=$VERSION" 33 | if [ "$FORCE" != "-f" ]; then 34 | echo "This is a dry run. Run with -f to publish." 35 | exit 0 36 | fi 37 | 38 | cd "$DIR" 39 | cargo publish $@ 40 | 41 | cd "$TMP_DIR" 42 | while ! cargo add "$NAME@$VERSION" > /dev/null 2>&1; do 43 | echo "Waiting for crate to be published..." 44 | sleep 1 45 | done 46 | -------------------------------------------------------------------------------- /scripts/version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Modified from https://github.com/nikomatsakis/lalrpop/blob/master/version.sh 3 | # 4 | # A script to bump the version number on all Cargo.toml files etc in 5 | # an atomic fashion. 6 | 7 | set -e 8 | 9 | if [ "$1" == "" ]; then 10 | echo "Usage: version.sh " 11 | exit 1 12 | fi 13 | 14 | VERSION=$( 15 | ls **/Cargo.toml | \ 16 | xargs grep "# GLUON" | \ 17 | perl -p -e 's/.*version = "([0-9.]+)"[^#]+# GLUON/$1/' | 18 | sort | 19 | uniq) 20 | 21 | if [ $(echo $VERSION | wc -w) != 1 ]; then 22 | echo "Error: inconsistent versions detected across Cargo.toml files!" 23 | echo "$VERSION" 24 | exit 1 25 | fi 26 | 27 | echo "Found consistent version $VERSION" 28 | 29 | perl -p -i -e 's/version *= *"[0-9.]+"([^#]+)# GLUON/version = "'$1'"$1# GLUON/' \ 30 | $(ls **/Cargo.toml Cargo.toml) 31 | 32 | perl -p -i -e 's/^gluon *= *"[0-9.]+"/gluon = "'$1'"/' \ 33 | README.md 34 | 35 | perl -p -i -e 's/[0-9][0-9.]+([^#]+)# GLUON/'$1'$1# GLUON/' \ 36 | $(ls **/src/lib.rs src/lib.rs) 37 | 38 | # Update Cargo.lock 39 | cargo fetch 40 | 41 | git add . 42 | CHANGES=$(git diff HEAD --unified=0 CHANGELOG.md | tail +6 | sed -e 's/^\+//') 43 | git commit -m "Version ${1}"$'\n\n'"${CHANGES}" 44 | git tag "v${1}" 45 | -------------------------------------------------------------------------------- /src/std_lib.rs: -------------------------------------------------------------------------------- 1 | pub mod env; 2 | #[cfg(feature = "http")] 3 | pub mod http; 4 | pub mod io; 5 | pub mod process; 6 | #[cfg(all(feature = "random", not(target_arch = "wasm32")))] 7 | pub mod random; 8 | #[cfg(feature = "regex")] 9 | pub mod regex; 10 | -------------------------------------------------------------------------------- /src/std_lib/process.rs: -------------------------------------------------------------------------------- 1 | use crate::real_std::process::Command; 2 | use crate::vm::{api::IO, thread::Thread, ExternModule, Result}; 3 | 4 | #[derive(Getable, VmType)] 5 | #[gluon(crate_name = "::vm")] 6 | struct CreateProcess<'a> { 7 | command: &'a str, 8 | args: Vec<&'a str>, 9 | env: Option>, 10 | current_dir: Option<&'a str>, 11 | } 12 | 13 | fn execute(create: CreateProcess) -> IO> { 14 | let mut command = Command::new(create.command); 15 | for arg in &create.args { 16 | command.arg(arg); 17 | } 18 | match create.env { 19 | Some(env) => { 20 | command.env_clear(); 21 | for (key, value) in &env { 22 | command.env(key, value); 23 | } 24 | } 25 | None => (), 26 | } 27 | if let Some(current_dir) = create.current_dir { 28 | command.current_dir(current_dir); 29 | } 30 | IO::from(command.status().map(|status| status.code())) 31 | } 32 | 33 | mod std { 34 | pub mod process { 35 | pub use crate::std_lib::process as prim; 36 | } 37 | } 38 | 39 | pub fn load(vm: &Thread) -> Result { 40 | ExternModule::new( 41 | vm, 42 | record! { 43 | execute => primitive!(1, std::process::prim::execute) 44 | }, 45 | ) 46 | } 47 | -------------------------------------------------------------------------------- /src/std_lib/random.rs: -------------------------------------------------------------------------------- 1 | //! Module containing bindings to the `rand` library. 2 | 3 | extern crate rand; 4 | extern crate rand_xorshift; 5 | 6 | use self::rand::{Rng, SeedableRng}; 7 | 8 | use crate::vm::{ 9 | self, 10 | api::{RuntimeResult, IO}, 11 | thread::Thread, 12 | types::VmInt, 13 | ExternModule, 14 | }; 15 | 16 | #[derive(Clone, Debug, Userdata, Trace, VmType)] 17 | #[gluon(vm_type = "std.random.XorShiftRng")] 18 | #[gluon_userdata(clone)] 19 | #[gluon(crate_name = "::vm")] 20 | #[gluon_trace(skip)] 21 | struct XorShiftRng(self::rand_xorshift::XorShiftRng); 22 | 23 | field_decl! { value, gen } 24 | 25 | fn next_int(_: ()) -> IO { 26 | IO::Value(rand::thread_rng().gen()) 27 | } 28 | 29 | fn next_float(_: ()) -> IO { 30 | IO::Value(rand::thread_rng().gen()) 31 | } 32 | 33 | fn gen_int_range(low: VmInt, high: VmInt) -> IO { 34 | IO::Value(rand::thread_rng().gen_range(low..high)) 35 | } 36 | 37 | type RngNext = record_type! { 38 | value => VmInt, 39 | gen => G 40 | }; 41 | 42 | fn xor_shift_new(seed: &[u8]) -> RuntimeResult { 43 | if seed.len() == 16 { 44 | let seed = unsafe { *(seed.as_ptr() as *const [u8; 16]) }; 45 | RuntimeResult::Return(XorShiftRng(self::rand_xorshift::XorShiftRng::from_seed( 46 | seed, 47 | ))) 48 | } else { 49 | RuntimeResult::Panic("Expected xorshift seed to have 16 elements".to_string()) 50 | } 51 | } 52 | 53 | fn xor_shift_next(gen: &XorShiftRng) -> RngNext { 54 | let mut gen = gen.clone(); 55 | record_no_decl! { 56 | value => gen.0.gen(), 57 | gen => gen 58 | } 59 | } 60 | 61 | mod std { 62 | pub mod random { 63 | pub use crate::std_lib::random as prim; 64 | } 65 | } 66 | 67 | pub fn load(vm: &Thread) -> vm::Result { 68 | vm.register_type::("std.random.XorShiftRng", &[])?; 69 | 70 | ExternModule::new( 71 | vm, 72 | record! { 73 | type std::random::XorShiftRng => XorShiftRng, 74 | next_int => primitive!(1, std::random::prim::next_int), 75 | next_float => primitive!(1, std::random::prim::next_float), 76 | gen_int_range => primitive!(2, std::random::prim::gen_int_range), 77 | xor_shift_new => primitive!(1, std::random::prim::xor_shift_new), 78 | xor_shift_next => primitive!(1, std::random::prim::xor_shift_next) 79 | }, 80 | ) 81 | } 82 | -------------------------------------------------------------------------------- /src/std_lib/regex.rs: -------------------------------------------------------------------------------- 1 | //! Module containing bindings to the `regex` library. 2 | 3 | extern crate regex; 4 | 5 | use crate::vm::{self, thread::Thread, ExternModule}; 6 | 7 | #[derive(Debug, Userdata, Trace, VmType)] 8 | #[gluon(vm_type = "std.regex.Regex")] 9 | #[gluon(crate_name = "vm")] 10 | #[gluon_trace(skip)] 11 | struct Regex(regex::Regex); 12 | 13 | #[derive(Debug, Userdata, Trace, VmType)] 14 | #[gluon(vm_type = "std.regex.Error")] 15 | #[gluon(crate_name = "vm")] 16 | #[gluon_trace(skip)] 17 | struct Error(regex::Error); 18 | 19 | fn new(re: &str) -> Result { 20 | match regex::Regex::new(re) { 21 | Ok(r) => Ok(Regex(r)), 22 | Err(e) => Err(Error(e)), 23 | } 24 | } 25 | 26 | fn is_match(re: &Regex, text: &str) -> bool { 27 | let &Regex(ref re) = re; 28 | re.is_match(text) 29 | } 30 | 31 | #[derive(Pushable, VmType)] 32 | #[gluon(vm_type = "std.regex.types.Match")] 33 | #[gluon(crate_name = "vm")] 34 | struct Match<'a> { 35 | start: usize, 36 | end: usize, 37 | text: &'a str, 38 | } 39 | 40 | impl<'a> Match<'a> { 41 | fn new(m: regex::Match<'a>) -> Self { 42 | Match { 43 | start: m.start(), 44 | end: m.end(), 45 | text: m.as_str(), 46 | } 47 | } 48 | } 49 | 50 | fn find<'a>(re: &Regex, text: &'a str) -> Option> { 51 | let &Regex(ref re) = re; 52 | re.find(text).map(Match::new) 53 | } 54 | 55 | fn captures<'a>(re: &Regex, text: &'a str) -> Option>>> { 56 | let &Regex(ref re) = re; 57 | re.captures(text) 58 | .map(|c| (0..c.len()).map(move |i| c.get(i).map(Match::new))) 59 | // FIXME Once rust stops ICEing .map(Collect::new) 60 | .map(|i| i.collect()) 61 | } 62 | 63 | fn error_to_string(err: &Error) -> String { 64 | let &Error(ref err) = err; 65 | err.to_string() 66 | } 67 | 68 | mod std { 69 | pub mod regex { 70 | pub use crate::std_lib::regex as prim; 71 | } 72 | } 73 | 74 | pub fn load(vm: &Thread) -> vm::Result { 75 | vm.register_type::("std.regex.Regex", &[])?; 76 | vm.register_type::("std.regex.Error", &[])?; 77 | 78 | ExternModule::new( 79 | vm, 80 | record! { 81 | type Error => Error, 82 | type Regex => Regex, 83 | type Match => Match, 84 | 85 | new => primitive!(1, std::regex::prim::new), 86 | is_match => primitive!(2, std::regex::prim::is_match), 87 | find => primitive!(2, std::regex::prim::find), 88 | // Workaround MIR bug in rustc 89 | captures => primitive!(2, "std.regex.prim.captures", |x, y| std::regex::prim::captures(x, y)), 90 | error_to_string => primitive!(1, std::regex::prim::error_to_string) 91 | }, 92 | ) 93 | } 94 | -------------------------------------------------------------------------------- /std/alternative.glu: -------------------------------------------------------------------------------- 1 | //@NO-IMPLICIT-PRELUDE 2 | //! Implementation of the `Alternative` type 3 | let { Applicative } = import! std.applicative 4 | 5 | /// A monoid on applicative functors. 6 | #[implicit] 7 | type Alternative f = { 8 | applicative : Applicative f, 9 | /// The identify of `or` 10 | empty : forall a . f a, 11 | /// An associative binary operation. 12 | /// 13 | /// Evaluates to the first argument if it is not `empty`, otherwise evaluates to the second argument. 14 | or : forall a . f a -> f a -> f a 15 | } 16 | 17 | let empty ?alt : [Alternative f] -> f a = alt.empty 18 | 19 | let or ?alt : [Alternative f] -> f a -> f a -> f a = alt.or 20 | 21 | /// An associative binary operation. Alias of `or`. 22 | /// 23 | /// Evaluates to the first argument if it is not `empty`, otherwise evaluates to the second argument. 24 | #[infix(left, 3)] 25 | let (<|>) : [Alternative f] -> f a -> f a -> f a = or 26 | 27 | { 28 | Alternative, 29 | empty, 30 | or, 31 | (<|>), 32 | } 33 | -------------------------------------------------------------------------------- /std/assert.glu: -------------------------------------------------------------------------------- 1 | //@NO-IMPLICIT-PRELUDE 2 | //! Assertion functions. 3 | let { error } = import! std.prim 4 | 5 | let assert_msg x s = if x then () else error s 6 | let assert x = assert_msg x "Assertion failed" 7 | 8 | { 9 | assert, 10 | assert_msg, 11 | } 12 | -------------------------------------------------------------------------------- /std/bool.glu: -------------------------------------------------------------------------------- 1 | //@NO-IMPLICIT-PRELUDE 2 | //! The boolean type. 3 | 4 | let { Bool, Ordering } = import! std.types 5 | let { Semigroup } = import! std.semigroup 6 | let { Monoid } = import! std.monoid 7 | let { Group } = import! std.group 8 | let { Eq, Ord } = import! std.cmp 9 | let { Show } = import! std.show 10 | let { id } = import! std.function 11 | 12 | /// Boolean 'not' 13 | let not x : Bool -> Bool = if x then False else True 14 | 15 | /// Boolean 'exclusive or' 16 | let xor x y : Bool -> Bool -> Bool = if x then not y else y 17 | 18 | let conjunctive = 19 | let semigroup : Semigroup Bool = { 20 | append = \x y -> x && y, 21 | } 22 | 23 | let monoid : Monoid Bool = { 24 | semigroup = semigroup, 25 | empty = True, 26 | } 27 | 28 | { semigroup, monoid } 29 | 30 | let disjunctive = 31 | let semigroup : Semigroup Bool = { 32 | append = \x y -> x || y, 33 | } 34 | 35 | let monoid : Monoid Bool = { 36 | semigroup = semigroup, 37 | empty = False, 38 | } 39 | 40 | { semigroup, monoid } 41 | 42 | let exclusive = 43 | let semigroup : Semigroup Bool = { append = xor } 44 | 45 | let monoid : Monoid Bool = { 46 | semigroup = semigroup, 47 | empty = False, 48 | } 49 | 50 | let group : Group Bool = { 51 | monoid = monoid, 52 | inverse = id, 53 | } 54 | 55 | { semigroup, monoid, group } 56 | 57 | let eq : Eq Bool = { 58 | (==) = \l r -> if l then r else not r, 59 | } 60 | 61 | let ord : Ord Bool = { 62 | eq = eq, 63 | compare = \l r -> if l then if r then EQ else GT else LT, 64 | } 65 | 66 | let show : Show Bool = { 67 | show = \x -> if x then "True" else "False", 68 | } 69 | 70 | { 71 | Bool, 72 | not, 73 | xor, 74 | conjunctive, 75 | disjunctive, 76 | exclusive, 77 | eq, 78 | ord, 79 | show, 80 | } 81 | -------------------------------------------------------------------------------- /std/byte.glu: -------------------------------------------------------------------------------- 1 | //! An 8-bit unsigned integer. 2 | 3 | let { Semigroup, Monoid, Group, Eq, Ord, Ordering, Num, Show } = import! std.prelude 4 | 5 | let additive = 6 | let semigroup : Semigroup Byte = { 7 | append = \x y -> x #Byte+ y, 8 | } 9 | 10 | let monoid : Monoid Byte = { 11 | semigroup = semigroup, 12 | empty = 0b, 13 | } 14 | 15 | let group : Group Byte = { 16 | monoid = monoid, 17 | inverse = \x -> 0b #Byte- x, 18 | } 19 | 20 | { semigroup, monoid, group } 21 | 22 | let multiplicative = 23 | let semigroup : Semigroup Byte = { 24 | append = \x y -> x #Byte* y, 25 | } 26 | 27 | let monoid : Monoid Byte = { 28 | semigroup = semigroup, 29 | empty = 1b, 30 | } 31 | 32 | { semigroup, monoid } 33 | 34 | let eq : Eq Byte = { 35 | (==) = \l r -> l #Byte== r, 36 | } 37 | 38 | let ord : Ord Byte = { 39 | eq = eq, 40 | compare = \l r -> if l #Byte< r then LT else if l #Byte== r then EQ else GT, 41 | } 42 | 43 | let num : Num Byte = { 44 | ord = ord, 45 | (+) = additive.semigroup.append, 46 | (-) = \l r -> l #Byte- r, 47 | (*) = multiplicative.semigroup.append, 48 | (/) = \l r -> l #Byte/ r, 49 | negate = additive.group.inverse, 50 | } 51 | 52 | let show : Show Byte = { 53 | show = (import! std.prim).show_byte, 54 | } 55 | 56 | { 57 | additive, 58 | multiplicative, 59 | eq, 60 | ord, 61 | num, 62 | show, 63 | .. 64 | import! std.byte.prim 65 | } 66 | -------------------------------------------------------------------------------- /std/category.glu: -------------------------------------------------------------------------------- 1 | //@NO-IMPLICIT-PRELUDE 2 | //! Implementation of the `Category` type 3 | 4 | #[implicit] 5 | type Category (cat : Type -> Type -> Type) = { 6 | id : forall a . cat a a, 7 | compose : forall a b c . cat b c -> cat a b -> cat a c 8 | } 9 | 10 | let id ?cat : forall cat a . [Category cat] -> cat a a = cat.id 11 | let compose ?cat : forall a b c . [Category cat] -> cat b c -> cat a b -> cat a c = cat.compose 12 | 13 | /// Right-to-left composition. Alias for `compose`. 14 | #[infix(right, 9)] 15 | let (<<) : forall a b c . [Category cat] -> cat b c -> cat a b -> cat a c = compose 16 | 17 | /// Left-to-right composition. Alias for `compose`, but with the arguments flipped. 18 | #[infix(left, 9)] 19 | let (>>) f g : forall a b c . [Category cat] -> cat a b -> cat b c -> cat a c = compose g f 20 | 21 | { 22 | Category, 23 | id, 24 | compose, 25 | 26 | (<<), 27 | (>>), 28 | } 29 | -------------------------------------------------------------------------------- /std/channel.glu: -------------------------------------------------------------------------------- 1 | //! Mpmc channels. 2 | 3 | let prim = import! std.channel.prim 4 | 5 | { 6 | .. 7 | prim 8 | } 9 | -------------------------------------------------------------------------------- /std/char.glu: -------------------------------------------------------------------------------- 1 | //! A character type. 2 | 3 | let { Eq, Ord, Ordering, Show } = import! std.prelude 4 | 5 | let eq : Eq Char = { (==) = \l r -> l #Char== r } 6 | 7 | let ord : Ord Char = { 8 | eq = eq, 9 | compare = \l r -> if l #Char< r then LT else if l #Char== r then EQ else GT, 10 | } 11 | 12 | let show : Show Char = { show = (import! std.prim).show_char } 13 | 14 | { 15 | eq, 16 | ord, 17 | show, 18 | .. 19 | import! std.char.prim 20 | } 21 | -------------------------------------------------------------------------------- /std/cmp.glu: -------------------------------------------------------------------------------- 1 | //@NO-IMPLICIT-PRELUDE 2 | //! Functionality for ordering and comparison. 3 | 4 | let { Bool, Ordering } = import! std.types 5 | let { Semigroup } = import! std.semigroup 6 | let { Monoid } = import! std.monoid 7 | 8 | /// `Eq a` defines equality (==) on `a` 9 | #[implicit] 10 | type Eq a = { 11 | /// Tests whether the values are equal. 12 | (==) : a -> a -> Bool 13 | } 14 | 15 | #[infix(left, 4)] 16 | let (==) ?eq : [Eq a] -> a -> a -> Bool = eq.(==) 17 | 18 | /// Tests whether the values are not equal. 19 | #[infix(left, 4)] 20 | let (/=) ?eq l r : [Eq a] -> a -> a -> Bool = if (eq.(==) l r) then False else True 21 | 22 | /// `Ord a` defines an ordering on `a` 23 | #[implicit] 24 | type Ord a = { 25 | eq : Eq a, 26 | /// Compares two values and returns wheter the first is less than, equal or greater than the second. 27 | compare : a -> a -> Ordering 28 | } 29 | 30 | let compare ?ord : [Ord a] -> a -> a -> Ordering = ord.compare 31 | 32 | /// Returns whether `l` is less than or equal to `r`. 33 | #[infix(left, 4)] 34 | let (<=) l r : [Ord a] -> a -> a -> Bool = 35 | match compare l r with 36 | | LT -> True 37 | | EQ -> True 38 | | GT -> False 39 | 40 | /// Returns whether `l` is less than `r`. 41 | #[infix(left, 4)] 42 | let (<) l r : [Ord a] -> a -> a -> Bool = 43 | match compare l r with 44 | | LT -> True 45 | | EQ -> False 46 | | GT -> False 47 | 48 | /// Returns whether `l` is greater than `r`. 49 | #[infix(left, 4)] 50 | let (>) l r : [Ord a] -> a -> a -> Bool = 51 | match compare l r with 52 | | LT -> False 53 | | EQ -> False 54 | | GT -> True 55 | 56 | /// Returns whether `l` is greater than or equal to `r`. 57 | #[infix(left, 4)] 58 | let (>=) l r : [Ord a] -> a -> a -> Bool = 59 | match compare l r with 60 | | LT -> False 61 | | EQ -> True 62 | | GT -> True 63 | 64 | let min l r : [Ord a] -> a -> a -> a = 65 | if l <= r then l 66 | else r 67 | 68 | let max l r : [Ord a] -> a -> a -> a = 69 | if r >= l then r 70 | else l 71 | 72 | let semigroup : Semigroup Ordering = { 73 | append = \x y -> 74 | match x with 75 | | EQ -> y 76 | | _ -> x, 77 | } 78 | 79 | let monoid : Monoid Ordering = { 80 | semigroup, 81 | empty = EQ, 82 | } 83 | 84 | { 85 | Eq, 86 | (==), 87 | (/=), 88 | 89 | Bool, 90 | 91 | Ord, 92 | compare, 93 | (<), 94 | (<=), 95 | (>=), 96 | (>), 97 | min, 98 | max, 99 | 100 | Ordering, 101 | 102 | semigroup, 103 | 104 | monoid, 105 | } 106 | -------------------------------------------------------------------------------- /std/debug.glu: -------------------------------------------------------------------------------- 1 | //! Debug library. 2 | 3 | let prim = import! std.debug.prim 4 | 5 | { 6 | .. 7 | prim 8 | } 9 | -------------------------------------------------------------------------------- /std/disposable.glu: -------------------------------------------------------------------------------- 1 | //@NO-IMPLICIT-PRELUDE 2 | //! A `Disposable` abstracts over different kinds of resources. 3 | 4 | let { IO, wrap, flat_map } = import! std.io.prim 5 | let { Bool } = import! std.types 6 | 7 | 8 | /// A resource that has to be released after use, for example a file handle or a 9 | /// database connection. 10 | #[implicit] 11 | type Disposable a = { 12 | /// Disposes of `a`, releasing the resources it manages. Calling this function 13 | /// a second time (or more) has no effect. 14 | dispose : a -> IO (), 15 | /// Indicates if `a` has been disposed, meaning that `dispose` has been called 16 | /// at least once. 17 | is_disposed : a -> Bool 18 | } 19 | 20 | let dispose ?disposable : [Disposable a] -> a -> IO () = disposable.dispose 21 | 22 | let is_disposed ?disposable : [Disposable a] -> a -> Bool = disposable.is_disposed 23 | 24 | /// Calls `action` with `disposable` and disposes `disposable` afterwards. Returns 25 | /// the result of `action`, unless disposing `disposable` fails. 26 | let using disposable action : forall r . [Disposable a] -> a -> (a -> IO r) -> IO r = 27 | let result = action disposable 28 | do _ = dispose disposable 29 | result 30 | 31 | 32 | { 33 | Disposable, 34 | 35 | dispose, 36 | is_disposed, 37 | using, 38 | } 39 | -------------------------------------------------------------------------------- /std/effect.glu: -------------------------------------------------------------------------------- 1 | //@NO-IMPLICIT-PRELUDE 2 | //! Composable effect types 3 | let { error } = import! std.prim 4 | let option = import! std.option 5 | let { Result, ? } = import! std.result 6 | let { (<<), (|>) } = import! std.function 7 | let { (<>) } = import! std.semigroup 8 | let monoid @ { Monoid } = import! std.monoid 9 | 10 | let { Functor, map } = import! std.functor 11 | let { Applicative, wrap } = import! std.applicative 12 | let { Monad } = import! std.monad 13 | 14 | rec 15 | /// An effectful function `a -> b` 16 | type Arr r a b = a -> Eff r b 17 | 18 | /// The `Eff` monad provides composable effect via a `Row` of effects (`r`) and produces a value of type `a`. 19 | type Eff r a = 20 | | Pure a 21 | | Impure : forall x . r x -> Arr r x a -> Eff r a 22 | in 23 | let compose f g : Arr r a b -> Arr r b c -> Arr r a c = \a -> 24 | match f a with 25 | | Pure b -> g b 26 | | Impure r h -> Impure r (compose h g) 27 | 28 | let wrap_eff x : a -> Eff r a = Pure x 29 | let flat_map_eff f m : (a -> Eff r b) -> Eff r a -> Eff r b = 30 | match m with 31 | | Pure x -> f x 32 | | Impure row g -> Impure row (compose g f) 33 | 34 | let functor : Functor (Eff r) = { 35 | map = \f m -> flat_map_eff (\x -> wrap_eff (f x)) m, 36 | } 37 | 38 | let applicative : Applicative (Eff r) = { 39 | functor, 40 | apply = \f g -> flat_map_eff (\f1 -> flat_map_eff (\g1 -> wrap_eff (f1 g1)) g) f, 41 | wrap = wrap_eff, 42 | } 43 | 44 | let monad : Monad (Eff r) = { 45 | applicative, 46 | flat_map = flat_map_eff, 47 | } 48 | 49 | type OpenVariant r a = .. r 50 | #[doc(hidden)] 51 | let inject_rest x : forall e . OpenVariant r a -> [| | r |] a = convert_effect! x 52 | 53 | /// Extracts the value of type `a` from an effectful computation. Can only be done once all other 54 | /// effects have been eliminated from the row (leaving `[| |]` as the empty effect). See each 55 | /// individual effects module on how to eliminate the effect. 56 | let run_pure eff : Eff [| |] a -> a = 57 | match eff with 58 | | Pure v -> v 59 | | Impure _ _ -> error "Impossible: run_pure" 60 | 61 | { 62 | Eff, 63 | Arr, 64 | 65 | functor, 66 | applicative, 67 | monad, 68 | 69 | run_pure, 70 | 71 | inject_rest, 72 | } 73 | -------------------------------------------------------------------------------- /std/effect/alt.glu: -------------------------------------------------------------------------------- 1 | //! Implementation of the `Alt` effect 2 | 3 | let { Eff, inject_rest, ? } = import! std.effect 4 | let { map } = import! std.functor 5 | let { wrap } = import! std.applicative 6 | let { Alternative } = import! std.alternative 7 | let { (<<), id } = import! std.function 8 | 9 | /// The `Alt` effect lets `Eff` implement `Alternative` 10 | type Alt r a = 11 | | Empty 12 | .. r 13 | 14 | let extract_alt x : forall s . [| alt : Alt | r |] a -> Alt r a = convert_variant! x 15 | 16 | let send_alt f : Alt r a -> Eff [| alt : Alt | r |] a = Impure (convert_effect! alt f) Pure 17 | 18 | let run_alt_inner transform fail eff_1 eff_2 : (a -> b) 19 | -> (() -> Eff [| | s |] b) 20 | -> Eff [| alt : Alt | r |] a 21 | -> Eff [| alt : Alt | r |] a 22 | -> Eff [| | s |] b 23 | = 24 | let loop next ve : (() -> Eff [| | s |] b) -> Eff [| alt : Alt | r |] a -> _ = 25 | match ve with 26 | | Pure value -> wrap (transform value) 27 | | Impure e f -> 28 | match extract_alt e with 29 | | Empty -> 30 | next () 31 | | rest -> 32 | Impure (inject_rest rest) (loop next << f) 33 | let loop_2 _ = loop fail eff_2 34 | loop loop_2 eff_1 35 | 36 | let empty : forall s . Eff [| alt : Alt | r |] s = 37 | send_alt Empty 38 | 39 | let alternative : Alternative (Eff [| alt : Alt | r |]) = { 40 | applicative = (import! std.effect).applicative, 41 | empty = empty, 42 | or = \l r -> run_alt_inner id (\_ -> empty) l r, 43 | } 44 | 45 | /// Eliminates the `Alt` effect returning `None` if the `Alt` effect is `empty`, otherwise returns `Some a` with the value 46 | /// 47 | /// ``` 48 | /// let { assert_eq, ? } = import! std.test 49 | /// let alt @ { ? } = import! std.effect.alt 50 | /// let state = import! std.effect.state 51 | /// let { (*>) } = import! std.applicative 52 | /// let { empty } = import! std.alternative 53 | /// let { Eff, run_pure, ? } = import! std.effect 54 | /// 55 | /// let incr = state.modify (\x -> x + 1) 56 | /// 57 | /// seq assert_eq (run_pure (state.exec_state 0 (alt.run_alt incr incr))) (1) 58 | /// seq assert_eq (run_pure (state.exec_state 0 (alt.run_alt (incr *> incr) incr))) (2) 59 | /// seq assert_eq (run_pure (state.exec_state 0 (alt.run_alt (incr *> empty *> incr) incr))) (2) 60 | /// assert_eq (run_pure (state.exec_state 0 (alt.run_alt empty incr))) (1) 61 | /// 62 | /// ``` 63 | let run_alt eff_1 eff_2 : Eff [| alt : Alt | r |] a 64 | -> Eff [| alt : Alt | r |] a 65 | -> Eff [| | r |] (Option a) 66 | = 67 | let fail _ = wrap None 68 | run_alt_inner Some fail eff_1 eff_2 69 | 70 | { 71 | Alt, 72 | 73 | alternative, 74 | 75 | run_alt, 76 | } 77 | -------------------------------------------------------------------------------- /std/effect/error.glu: -------------------------------------------------------------------------------- 1 | //! Implementation of the `Error` effect 2 | 3 | let { Eff, inject_rest, ? } = import! std.effect 4 | let { Result } = import! std.result 5 | let { Option } = import! std.option 6 | let { (<<) } = import! std.function 7 | let { wrap } = import! std.applicative 8 | 9 | /// The `Error` effects adds "exceptions" to the `Eff` monad 10 | type Error e r a = 11 | | Error e 12 | .. r 13 | 14 | let send_error f : Error e r a -> Eff [| error : Error e | r |] a = 15 | Impure (convert_effect! error f) Pure 16 | 17 | let extract_error x : forall e . [| error : Error e | r |] a -> Error e r a = convert_variant! x 18 | 19 | /// Throws the error `e` 20 | let throw e : e -> Eff [| error : Error e | r |] a = send_error (Error e) 21 | 22 | /// Moves a `Result` into the `Eff` monad 23 | let ok_or_throw r : Result e t -> Eff [| error : Error e | r |] t = 24 | match r with 25 | | Ok t -> wrap t 26 | | Err e -> throw e 27 | 28 | let some_or_throw e o : e -> Option a -> Eff [| error : Error e | r |] a = 29 | match o with 30 | | Some x -> wrap x 31 | | None -> throw e 32 | 33 | /// Eliminates the `Error` effect and returns a `Result` 34 | let run_error eff : forall e . Eff [| error : Error e | r |] a -> Eff [| | r |] (Result e a) = 35 | let loop ve : Eff [| error : Error e | r |] a -> Eff [| | r |] (Result e a) = 36 | match ve with 37 | | Pure v -> wrap (Ok v) 38 | | Impure e f -> 39 | match extract_error e with 40 | | Error err -> 41 | wrap (Err err) 42 | | rest -> 43 | Impure (inject_rest rest) (loop << f) 44 | loop eff 45 | 46 | /// Catches an "exception", allowing the effect to continue executing 47 | let catch eff handler : forall e . 48 | Eff [| error : Error e | r |] a 49 | -> (e -> Eff [| error : Error e | r |] a) 50 | -> Eff [| error : Error e | r |] a 51 | = 52 | let loop ve : Eff [| error : Error e | r |] a -> Eff [| error : Error e | r |] a = 53 | match ve with 54 | | Pure v -> wrap v 55 | | Impure e f -> 56 | match extract_error e with 57 | | Error err -> 58 | handler err 59 | | rest -> 60 | Impure e (loop << f) 61 | loop eff 62 | 63 | 64 | { 65 | Error, 66 | 67 | catch, 68 | throw, 69 | ok_or_throw, 70 | some_or_throw, 71 | run_error, 72 | } 73 | -------------------------------------------------------------------------------- /std/effect/io.glu: -------------------------------------------------------------------------------- 1 | let { lift } = import! std.effect.lift 2 | lift_io! lift (import! std.io) 3 | -------------------------------------------------------------------------------- /std/effect/io/read.glu: -------------------------------------------------------------------------------- 1 | let { lift } = import! std.effect.lift 2 | let { Read } = import! std.io.read 3 | lift_io! lift (import! std.io.read) 4 | -------------------------------------------------------------------------------- /std/effect/io/write.glu: -------------------------------------------------------------------------------- 1 | let { lift } = import! std.effect.lift 2 | let { Write } = import! std.io.write 3 | lift_io! lift (import! std.io.write) 4 | -------------------------------------------------------------------------------- /std/effect/lift.glu: -------------------------------------------------------------------------------- 1 | //@NO-IMPLICIT-PRELUDE 2 | //! Implementation of the `Lift` effect 3 | let { error } = import! std.prim 4 | let { Eff, inject_rest } = import! std.effect 5 | let { wrap } = import! std.applicative 6 | let { Monad, flat_map } = import! std.monad 7 | let { (<<) } = import! std.function 8 | 9 | /// The `Lift` effect allows a regular monad (usually `IO`) to be used in an `Eff` monad 10 | type Lift m r a = 11 | | Lift (m a) 12 | .. r 13 | 14 | let send_lift f : Lift m r a -> Eff [| lift : Lift m | r |] a = Impure (convert_effect! lift f) Pure 15 | 16 | let extract_state x : forall m . [| lift : Lift m | r |] a -> Lift m r a = convert_variant! x 17 | 18 | /// "Lifts" a monadic action into the `Eff` monad. Since monads do not compose this can only be 19 | let lift m : m a -> Eff [| lift : Lift m | r |] a = send_lift (Lift m) 20 | 21 | /// Eliminates the lifted monad `m`. Can only be used once all other effects have been eliminated 22 | let run_lift eff : [Monad m] -> Eff [| lift : Lift m |] a -> m a = 23 | let loop ve : Eff [| lift : Lift m |] a -> m a = 24 | match ve with 25 | | Pure v -> wrap v 26 | | Impure e f -> 27 | match extract_state e with 28 | | Lift m -> 29 | do a = m 30 | loop (f a) 31 | | _ -> error "Impossible: Lift should always be the only remaining variant" 32 | loop eff 33 | 34 | { 35 | Lift, 36 | 37 | lift, 38 | run_lift, 39 | } 40 | -------------------------------------------------------------------------------- /std/effect/reader.glu: -------------------------------------------------------------------------------- 1 | //! Implementation of the `Reader` effect 2 | let { Eff, inject_rest, ? } = import! std.effect 3 | let { map } = import! std.functor 4 | let { wrap } = import! std.applicative 5 | let { (<<) } = import! std.function 6 | 7 | /// The `Reader` effects provides a shared, immutable environment for the effectful functions using it 8 | type Reader s r a = 9 | | Ask : Reader s r s 10 | .. r 11 | 12 | let extract_reader x : forall s . [| reader : Reader s | r |] a -> Reader s r a = convert_variant! x 13 | 14 | let send_reader f : Reader s r a -> Eff [| reader : Reader s | r |] a = 15 | Impure (convert_effect! reader f) Pure 16 | 17 | /// Retrieve the value from the environment 18 | let ask : forall s . Eff [| reader : Reader s | r |] s = 19 | send_reader Ask 20 | 21 | /// Retrieve the value from the environment while applying `f` to it 22 | let asks f : forall s . (s -> a) -> Eff [| reader : Reader s | r |] a = 23 | map f ask 24 | 25 | /// Runs a computation in a modified environment. 26 | let local f eff : forall s . 27 | (s -> s) -> Eff [| reader : Reader s | r |] a -> Eff [| reader : Reader s | r |] a 28 | = 29 | do s = asks f 30 | let s : s = s 31 | // FIXME Remove after this does not affect inference 32 | let loop ve : Eff [| reader : Reader s | r |] a -> Eff [| reader : Reader s | r |] a = 33 | match ve with 34 | | Pure value -> wrap value 35 | | Impure e f -> 36 | match extract_reader e with 37 | | Ask -> 38 | loop (f s) 39 | | rest -> 40 | Impure (inject_rest rest) (loop << f) 41 | loop eff 42 | 43 | /// Eliminates the `Reader` effect 44 | let run_reader s eff : forall s . s -> Eff [| reader : Reader s | r |] a -> Eff [| | r |] a = 45 | let loop reader ve : s -> Eff [| reader : Reader s | r |] a -> Eff [| | r |] a = 46 | match ve with 47 | | Pure value -> wrap value 48 | | Impure e f -> 49 | match extract_reader e with 50 | | Ask -> 51 | loop reader (f reader) 52 | | rest -> 53 | Impure (inject_rest rest) (loop reader << f) 54 | loop s eff 55 | { 56 | Reader, 57 | ask, 58 | asks, 59 | local, 60 | run_reader, 61 | } 62 | -------------------------------------------------------------------------------- /std/effect/reference.glu: -------------------------------------------------------------------------------- 1 | let { lift } = import! std.effect.lift 2 | let m = lift_io! lift (import! std.reference) 3 | 4 | #[infix(right, 9)] 5 | let (<-) = m.(<-) 6 | 7 | { 8 | (<-), 9 | .. 10 | m 11 | } 12 | -------------------------------------------------------------------------------- /std/effect/st.glu: -------------------------------------------------------------------------------- 1 | //! Implementation of the `st.State` effect 2 | 3 | let { Eff, inject_rest, ? } = import! std.effect 4 | let { map } = import! std.functor 5 | let { wrap } = import! std.applicative 6 | let { (<<) } = import! std.function 7 | let reference @ { Reference, ref, load } = import! std.st.reference.prim 8 | 9 | type STRef s a = Reference a 10 | 11 | #[infix(right, 9)] 12 | let (<-) = reference.(<-) 13 | 14 | /// The `State` effect enables the use of mutable state. By branding the state with `s` the mutable 15 | /// values are prevented from escaping the monad. 16 | type State s r a = 17 | | Call : forall b . (() -> b) -> State s r b 18 | .. r 19 | 20 | #[inline(never)] 21 | let extract_state x : forall s . [| st : State s | r |] a -> State s r a = convert_variant! x 22 | 23 | #[inline(never)] 24 | let send_state f : forall s . State s r a -> Eff [| st : State s | r |] a = 25 | Impure (convert_effect! st f) Pure 26 | 27 | let make_call = Call 28 | 29 | /// Creates a new mutable reference that contains `a`. 30 | let new_ref a : forall s . a -> Eff [| st : State s | r |] (STRef s a) = 31 | send_state (Call (\_ -> ref a)) 32 | 33 | /// Reads the values stored in the reference. 34 | let read_ref ref : forall s . STRef s a -> Eff [| st : State s | r |] a = 35 | send_state (Call (\_ -> load ref)) 36 | 37 | /// Writes a new value into the reference. 38 | let write_ref a ref : forall s . a -> STRef s a -> Eff [| st : State s | r |] () = 39 | send_state (Call (\_ -> ref <- a)) 40 | 41 | /// Eliminates the `State` effect 42 | let run_state eff : (forall s . Eff [| st : State s | r |] a) -> Eff [| | r |] a = 43 | let loop ve : forall s . Eff [| st : State s | r |] a -> Eff [| | r |] a = 44 | match ve with 45 | | Pure value -> wrap value 46 | | Impure e f -> 47 | match extract_state e with 48 | | Call g -> 49 | loop (f (g ())) 50 | | rest -> 51 | Impure (inject_rest rest) (loop << f) 52 | loop eff 53 | 54 | 55 | { 56 | State, 57 | STRef, 58 | 59 | send_state, 60 | 61 | new_ref, 62 | read_ref, 63 | write_ref, 64 | run_state, 65 | make_call, 66 | } 67 | -------------------------------------------------------------------------------- /std/effect/st/string.glu: -------------------------------------------------------------------------------- 1 | let { Eff, ? } = import! std.effect 2 | let { State, send_state, make_call } = import! std.effect.st 3 | let prim @ { StringBuf } = import! std.effect.st.string.prim 4 | 5 | let new : forall s . Eff [| st : State s | r |] (StringBuf s) = 6 | send_state (make_call prim.new) 7 | 8 | let len buf : StringBuf s -> Eff [| st : State s | r |] Int = 9 | send_state (make_call (\_ -> prim.len buf)) 10 | 11 | let push_str buf str : StringBuf s -> String -> Eff [| st : State s | r |] () = 12 | send_state (make_call (\_ -> prim.push_str buf str)) 13 | 14 | let slice buf start end : StringBuf s -> Int -> Int -> Eff [| st : State s | r |] String = 15 | send_state (make_call (\_ -> prim.slice buf start end)) 16 | 17 | /// ``` 18 | /// let { assert_eq, ? } = import! std.test 19 | /// let st = import! std.effect.st 20 | /// let string_buf = import! std.effect.st.string 21 | /// let { (*>) } = import! std.applicative 22 | /// let { Eff, run_pure, ? } = import! std.effect 23 | /// 24 | /// let action = 25 | /// do buf = string_buf.new 26 | /// seq string_buf.push_str buf "field:" 27 | /// seq string_buf.push_str buf " " 28 | /// seq string_buf.push_str buf "123" 29 | /// string_buf.read buf 30 | /// assert_eq (run_pure (st.run_state action)) "field: 123" 31 | /// ``` 32 | let read buf : StringBuf s -> Eff [| st : State s | r |] String = 33 | do l = len buf 34 | slice buf 0 l 35 | 36 | { 37 | StringBuf, 38 | 39 | new, 40 | len, 41 | push_str, 42 | slice, 43 | read, 44 | } 45 | -------------------------------------------------------------------------------- /std/effect/state.glu: -------------------------------------------------------------------------------- 1 | //! Implementation of the `State` effect 2 | 3 | let { Eff, inject_rest, ? } = import! std.effect 4 | let { map } = import! std.functor 5 | let { wrap } = import! std.applicative 6 | let { (<<) } = import! std.function 7 | 8 | /// The `State` effect provides an updatable state 9 | type State s r a = 10 | | Get : State s r s 11 | | Put : s -> State s r () 12 | .. r 13 | 14 | let extract_state x : forall s . [| state : State s | r |] a -> State s r a = convert_variant! x 15 | 16 | let send_state f : State s r a -> Eff [| state : State s | r |] a = 17 | Impure (convert_effect! state f) Pure 18 | 19 | /// Retreive the current value. 20 | let get : forall s . Eff [| state : State s | r |] s = 21 | send_state Get 22 | 23 | /// Retreive the current value and applied `f` to it. 24 | let gets f : forall s . (s -> a) -> Eff [| state : State s | r |] a = 25 | map f get 26 | 27 | /// Store `s` as the new value of the state. 28 | let put s : s -> Eff [| state : State s | r |] () = 29 | send_state (Put s) 30 | 31 | /// Update the state by applying `f`. 32 | let modify f : (s -> s) -> Eff [| state : State s | r |] () = 33 | do s = get 34 | put (f s) 35 | 36 | /// Eliminate the `State` effect and return the state and the computed value 37 | let run_state s eff : forall s . 38 | s 39 | -> Eff [| state : State s | r |] a 40 | -> Eff [| | r |] { state : s, value : a } 41 | = 42 | let loop state ve : s 43 | -> Eff [| state : State s | r |] a 44 | -> Eff [| | r |] { state : s, value : a } 45 | = 46 | match ve with 47 | | Pure value -> wrap { state, value } 48 | | Impure e f -> 49 | match extract_state e with 50 | | Get -> 51 | loop state (f state) 52 | | Put state -> 53 | loop state (f ()) 54 | | rest -> 55 | Impure (inject_rest rest) (loop state << f) 56 | loop s eff 57 | 58 | /// Eliminate the `State` effect and return the state 59 | let exec_state s eff : forall s . s -> Eff [| state : State s | r |] a -> Eff [| | r |] s = 60 | map (\r -> r.state) (run_state s eff) 61 | 62 | /// Eliminate the `State` effect and return the computed value 63 | let eval_state s eff : forall s . s -> Eff [| state : State s | r |] a -> Eff [| | r |] a = 64 | map (\r -> r.value) (run_state s eff) 65 | 66 | { 67 | State, 68 | 69 | get, 70 | gets, 71 | put, 72 | modify, 73 | run_state, 74 | exec_state, 75 | eval_state, 76 | } 77 | -------------------------------------------------------------------------------- /std/effect/writer.glu: -------------------------------------------------------------------------------- 1 | //! Implementation of the `Writer` effect 2 | 3 | let { Eff, inject_rest, ? } = import! std.effect 4 | let monoid @ { Monoid } = import! std.monoid 5 | let { (<>) } = import! std.semigroup 6 | let { wrap } = import! std.applicative 7 | let { (<<) } = import! std.function 8 | 9 | /// The `Writer` effect allows the computations to output values of type `s` 10 | type Writer s r a = 11 | | Tell : s -> Writer s r () 12 | .. r 13 | 14 | #[inline(never)] 15 | let extract_writer x : forall s . [| writer : Writer s | r |] a -> Writer s r a = convert_variant! x 16 | 17 | #[inline(never)] 18 | let send_writer f : Writer s r a -> Eff [| writer : Writer s | r |] a = 19 | Impure (convert_effect! writer f) Pure 20 | 21 | /// Outputs `s` 22 | let tell s : forall s . s -> Eff [| writer : Writer s | r |] () = 23 | send_writer (Tell s) 24 | 25 | /// Eliminates `Writer`, returning the output and computed value. Each output through `tell` are 26 | /// joined via its `Monoid` instance 27 | let run_writer eff : forall s . 28 | [Monoid s] -> Eff [| writer : Writer s | r |] a -> Eff [| | r |] { value : a, writer : s } 29 | = 30 | let loop writer ve : s -> Eff [| writer : Writer s | r |] a -> Eff [| | r |] _ = 31 | match ve with 32 | | Pure value -> wrap { value, writer } 33 | | Impure e f -> 34 | match extract_writer e with 35 | | Tell w -> 36 | loop (writer <> w) (f ()) 37 | | rest -> 38 | Impure (inject_rest rest) (loop writer << f) 39 | loop monoid.empty eff 40 | 41 | { 42 | Writer, 43 | 44 | tell, 45 | run_writer, 46 | } 47 | -------------------------------------------------------------------------------- /std/env.glu: -------------------------------------------------------------------------------- 1 | //! Inspection and manipulation of the process's environment. 2 | 3 | { 4 | .. 5 | import! std.env.prim 6 | } 7 | -------------------------------------------------------------------------------- /std/float.glu: -------------------------------------------------------------------------------- 1 | //@NO-IMPLICIT-PRELUDE 2 | //! The 64-bit floating point type. 3 | 4 | let { Semigroup, Monoid, Group, Eq, Ord, Ordering, Num, Show } = import! std.prelude 5 | 6 | let additive = 7 | let semigroup : Semigroup Float = { append = \x y -> x #Float+ y } 8 | 9 | let monoid : Monoid Float = { 10 | semigroup = semigroup, 11 | empty = 0.0, 12 | } 13 | 14 | let group : Group Float = { 15 | monoid = monoid, 16 | inverse = \x -> 0.0 #Float- x, 17 | } 18 | 19 | { semigroup, monoid, group } 20 | 21 | let multiplicative = 22 | let semigroup : Semigroup Float = { append = \x y -> x #Float* y } 23 | 24 | let monoid : Monoid Float = { 25 | semigroup = semigroup, 26 | empty = 1.0, 27 | } 28 | 29 | let group : Group Float = { 30 | monoid = monoid, 31 | inverse = \x -> 1.0 #Float/ x, 32 | } 33 | 34 | { semigroup, monoid, group } 35 | 36 | let eq : Eq Float = { 37 | (==) = \l r -> l #Float== r, 38 | } 39 | 40 | let ord : Ord Float = { 41 | eq = eq, 42 | compare = \l r -> if l #Float< r then LT else if l #Float== r then EQ else GT, 43 | } 44 | 45 | let num : Num Float = { 46 | ord = ord, 47 | (+) = additive.semigroup.append, 48 | (-) = \l r -> l #Float- r, 49 | (*) = multiplicative.semigroup.append, 50 | (/) = \l r -> l #Float/ r, 51 | negate = additive.group.inverse, 52 | } 53 | 54 | let show : Show Float = { 55 | show = (import! std.prim).show_float, 56 | } 57 | 58 | { 59 | additive, 60 | multiplicative, 61 | eq, 62 | ord, 63 | num, 64 | show, 65 | .. 66 | import! std.float.prim 67 | } 68 | -------------------------------------------------------------------------------- /std/foldable.glu: -------------------------------------------------------------------------------- 1 | //@NO-IMPLICIT-PRELUDE 2 | //! Implementation of the `Foldable` type 3 | 4 | let { Bool, Option } = import! std.types 5 | let { Semigroup, append } = import! std.semigroup 6 | let { Monoid, empty } = import! std.monoid 7 | let { wrap } = import! std.applicative 8 | let { Monad, flat_map } = import! std.monad 9 | let { Eq, (==) } = import! std.cmp 10 | 11 | /// Operations over a data structure that can be folded which means that a functions gets called on 12 | /// each element to reduce the structure to a single value (`Array`, `List` and `Map` are all `Foldable`) 13 | #[implicit] 14 | type Foldable (f : Type -> Type) = { 15 | foldr : forall a b . (a -> b -> b) -> b -> f a -> b, 16 | foldl : forall a b . (b -> a -> b) -> b -> f a -> b 17 | } 18 | 19 | let foldr ?fold : forall a b . [Foldable f] -> (a -> b -> b) -> b -> f a -> b = fold.foldr 20 | let foldl ?fold : forall a b . [Foldable f] -> (b -> a -> b) -> b -> f a -> b = fold.foldl 21 | 22 | let concat : [Foldable t] -> [Monoid m] -> t m -> m = 23 | foldr append empty 24 | 25 | let concat_map f : [Foldable t] -> [Monoid m] -> (a -> m) -> t a -> m = 26 | foldr (\x -> append (f x)) empty 27 | 28 | let fold_m ?fold ?monad f z : [Foldable t] -> [Monad m] -> (a -> b -> m a) -> a -> t b -> m a = 29 | foldl (\acc y -> flat_map (\x -> f x y) acc) (wrap z) 30 | 31 | let find ?fold pred : [Foldable t] -> (a -> Bool) -> t a -> Option a = 32 | let go acc next = 33 | match acc with 34 | | None -> if pred next then Some next else None 35 | | Some _ -> acc 36 | 37 | fold.foldl go None 38 | 39 | let find_map ?fold pred : [Foldable t] -> (a -> Option b) -> t a -> Option b = 40 | let go acc next = 41 | match acc with 42 | | None -> pred next 43 | | Some _ -> acc 44 | 45 | fold.foldl go None 46 | 47 | let all pred : [Foldable t] -> (a -> Bool) -> t a -> Bool = 48 | foldl (\acc x -> acc && pred x) True 49 | 50 | let any pred : [Foldable t] -> (a -> Bool) -> t a -> Bool = 51 | foldl (\acc x -> acc || pred x) False 52 | 53 | let elem x : [Foldable t] -> [Eq a] -> a -> t a -> Bool = 54 | any ((==) x) 55 | 56 | let count : [Foldable t] -> t a -> Int = 57 | foldl (\acc _ -> acc #Int+ 1) 0 58 | 59 | { 60 | Foldable, 61 | foldr, 62 | foldl, 63 | fold_m, 64 | concat, 65 | concat_map, 66 | find, 67 | find_map, 68 | all, 69 | any, 70 | elem, 71 | count, 72 | } 73 | -------------------------------------------------------------------------------- /std/fs.glu: -------------------------------------------------------------------------------- 1 | //@NO-IMPLICIT-PRELUDE 2 | //! Functions for working with the file system 3 | 4 | let fs_prim = import! std.fs.prim 5 | 6 | { 7 | .. 8 | fs_prim 9 | } 10 | 11 | -------------------------------------------------------------------------------- /std/function.glu: -------------------------------------------------------------------------------- 1 | //@NO-IMPLICIT-PRELUDE 2 | //! Conveniences for working with functions. 3 | 4 | let { Semigroup } = import! std.semigroup 5 | let { Monoid } = import! std.monoid 6 | let { Category } = import! std.category 7 | let { Functor } = import! std.functor 8 | let { Monad } = import! std.monad 9 | let { Applicative } = import! std.applicative 10 | 11 | let semigroup s : Semigroup b -> Semigroup (a -> b) = { 12 | append = \f g x -> s.append (f x) (g x), 13 | } 14 | 15 | let monoid m : Monoid b -> Monoid (a -> b) = { 16 | semigroup = semigroup m.semigroup, 17 | empty = \_ -> m.empty, 18 | } 19 | 20 | let category : Category (->) = { 21 | id = \x -> x, 22 | compose = \f g x -> f (g x), 23 | } 24 | 25 | let functor : Functor ((->) a) = { map = category.compose } 26 | 27 | let applicative : Applicative ((->) a) = { 28 | functor = functor, 29 | apply = \f g x -> f x (g x), 30 | wrap = \x y -> x, 31 | } 32 | 33 | let monad : Monad ((->) a) = { 34 | applicative = applicative, 35 | flat_map = \f m x -> f (m x) x, 36 | } 37 | 38 | /// The identity function, where `id x == x` 39 | let id : a -> a = category.id 40 | 41 | /// const `x` creates a function that always returns `x` 42 | let const : a -> b -> a = applicative.wrap 43 | 44 | /// flip `f` creates a new function that takes its two arguments in reverse order 45 | let flip f x y : (a -> b -> c) -> b -> a -> c = f y x 46 | 47 | /// Backward function application, where `f <| x == f x` 48 | #[infix(right, 0)] 49 | let (<|) f x : (a -> b) -> a -> b = f x 50 | 51 | /// Forward function application, where `x |> f == f x` 52 | #[infix(left, 0)] 53 | let (|>) x f : a -> (a -> b) -> b = f x 54 | 55 | /// Right-to-left function composition 56 | #[infix(right, 9)] 57 | let (<<) : (b -> c) -> (a -> b) -> a -> c = category.compose 58 | 59 | /// Left-to-right function composition 60 | #[infix(left, 9)] 61 | let (>>) : (a -> b) -> (b -> c) -> a -> c = flip category.compose 62 | 63 | { 64 | semigroup, 65 | monoid, 66 | category, 67 | functor, 68 | applicative, 69 | monad, 70 | id, 71 | const, 72 | flip, 73 | (<|), 74 | (|>), 75 | (<<), 76 | (>>), 77 | } 78 | -------------------------------------------------------------------------------- /std/functor.glu: -------------------------------------------------------------------------------- 1 | //@NO-IMPLICIT-PRELUDE 2 | //! Implementation of the `Functor` type 3 | 4 | /// A `Functor` represents an action on a parameterized type which does not 5 | /// change the structure with the mapped type. 6 | /// 7 | /// The following laws should hold: 8 | /// 9 | /// * `map id == id` 10 | /// * `map (f << g) == map f << map g` 11 | #[implicit] 12 | type Functor f = { 13 | /// Apply the supplied function to the contents of `f a`, converting it to 14 | /// an `f b` 15 | /// 16 | /// # Examples 17 | /// 18 | /// * `option.functor.map show_Int.show (Some 1) == Some "1"` 19 | /// * `result.functor.map show_Int.show (Some 1) == Ok "1"` 20 | /// * `list.functor.map show_Int.show (list.of [1, 2]) == list.of ["1", "2"]` 21 | /// 22 | /// # Note 23 | /// 24 | /// * Known as `fmap` in Haskell 25 | map : forall a b . (a -> b) -> f a -> f b 26 | } 27 | 28 | let map ?functor : [Functor f] -> (a -> b) -> f a -> f b = functor.map 29 | 30 | { Functor, map } 31 | -------------------------------------------------------------------------------- /std/group.glu: -------------------------------------------------------------------------------- 1 | //@NO-IMPLICIT-PRELUDE 2 | //! Implementation of the `Group` type 3 | 4 | let { Monoid } = import! std.monoid 5 | 6 | /// `Group a` represents an monoid an which has an inverse element. This means 7 | /// the following additional laws must hold: 8 | /// 9 | /// * `forall x . append (inverse x) x = empty = append x (inverse x)` 10 | #[implicit] 11 | type Group a = { 12 | monoid : Monoid a, 13 | /// The inverse operation 14 | inverse : a -> a 15 | } 16 | 17 | { 18 | Group, 19 | } 20 | -------------------------------------------------------------------------------- /std/http/types.glu: -------------------------------------------------------------------------------- 1 | let { Body, ResponseBody, StatusCode, Method, Request, Response, Headers, HttpState, Uri } = 2 | import! std.http.prim_types 3 | 4 | let { Eff } = import! std.effect 5 | let { Error } = import! std.effect.error 6 | let { Alt } = import! std.effect.alt 7 | let { State } = import! std.effect.state 8 | let { Lift } = import! std.effect.lift 9 | 10 | /// Type used by handlers to indicate why they could not process a request 11 | type Failure = 12 | | DontProcess 13 | | Error String 14 | 15 | 16 | type HttpEffect r a = [| alt : Alt, state : State HttpState, lift : Lift IO | r |] a 17 | 18 | { 19 | Method, 20 | Failure, 21 | Request, 22 | StatusCode, 23 | Headers, 24 | Response, 25 | ResponseBody, 26 | HttpEffect, 27 | HttpState, 28 | Uri, 29 | Body, 30 | } 31 | -------------------------------------------------------------------------------- /std/identity.glu: -------------------------------------------------------------------------------- 1 | //@NO-IMPLICIT-PRELUDE 2 | //! The identity functor and monad. 3 | 4 | let { Functor } = import! std.functor 5 | let { Applicative } = import! std.applicative 6 | let { Monad } = import! std.monad 7 | 8 | type Identity a = a 9 | 10 | let functor : Functor Identity = { 11 | map = \f m -> f m, 12 | } 13 | 14 | let applicative : Applicative Identity = { 15 | functor, 16 | apply = \mf m -> mf m, 17 | wrap = \value -> value, 18 | } 19 | 20 | let monad : Monad Identity = { 21 | applicative, 22 | flat_map = \f m -> f m, 23 | } 24 | 25 | { Identity, functor, applicative, monad } 26 | -------------------------------------------------------------------------------- /std/int.glu: -------------------------------------------------------------------------------- 1 | //@NO-IMPLICIT-PRELUDE 2 | //! The signed 64-bit integer type. 3 | 4 | let { Semigroup } = import! std.semigroup 5 | let { Monoid } = import! std.monoid 6 | let { Group } = import! std.group 7 | let { Eq, Ord, Ordering } = import! std.cmp 8 | let { Num } = import! std.num 9 | let { Show } = import! std.show 10 | 11 | let additive = 12 | let semigroup : Semigroup Int = { 13 | append = \x y -> x #Int+ y, 14 | } 15 | 16 | let monoid : Monoid Int = { 17 | semigroup = semigroup, 18 | empty = 0, 19 | } 20 | 21 | let group : Group Int = { 22 | monoid = monoid, 23 | inverse = \x -> 0 #Int- x, 24 | } 25 | 26 | { semigroup, monoid, group } 27 | 28 | let multiplicative = 29 | let semigroup : Semigroup Int = { 30 | append = \x y -> x #Int* y, 31 | } 32 | 33 | let monoid : Monoid Int = { 34 | semigroup = semigroup, 35 | empty = 1, 36 | } 37 | 38 | { semigroup, monoid } 39 | 40 | let eq : Eq Int = { 41 | (==) = \l r -> l #Int== r, 42 | } 43 | 44 | let ord : Ord Int = { 45 | eq = eq, 46 | compare = \l r -> if l #Int< r then LT else if l #Int== r then EQ else GT, 47 | } 48 | 49 | let num : Num Int = { 50 | ord = ord, 51 | (+) = additive.semigroup.append, 52 | (-) = \l r -> l #Int- r, 53 | (*) = multiplicative.semigroup.append, 54 | (/) = \l r -> l #Int/ r, 55 | negate = additive.group.inverse, 56 | } 57 | 58 | let show : Show Int = { 59 | show = (import! std.prim).show_int, 60 | } 61 | 62 | { 63 | additive, 64 | multiplicative, 65 | eq, 66 | ord, 67 | num, 68 | show, 69 | .. 70 | import! std.int.prim 71 | } 72 | -------------------------------------------------------------------------------- /std/io.glu: -------------------------------------------------------------------------------- 1 | //@NO-IMPLICIT-PRELUDE 2 | //! Functions for working with I/O 3 | 4 | let io_prim @ { IO, File } = import! std.io.prim 5 | let { Read } = import! std.io.read 6 | let { Write } = import! std.io.write 7 | let { Disposable } = import! std.disposable 8 | let { functor, applicative, monad } = import! std.io.base 9 | 10 | /// Opens the file at `path` in read-only mode. Fails if the file does not 11 | /// exist. 12 | let open_file path : String -> IO File = 13 | let { OpenOptions } = io_prim 14 | io_prim.open_file_with path [Read] 15 | 16 | /// Opens a file in write-only mode. If the file already exists, it will be 17 | /// truncated. If the file does not exist, it will be created. 18 | let create_file path : String -> IO File = 19 | let { OpenOptions } = io_prim 20 | io_prim.open_file_with path [Create, Write, Truncate] 21 | 22 | let read : Read File = { 23 | read = io_prim.read_file, 24 | read_to_end = io_prim.read_file_to_end, 25 | } 26 | 27 | let write : Write File = { 28 | write_slice = io_prim.write_slice_file, 29 | flush = io_prim.flush_file, 30 | } 31 | 32 | let disposable : Disposable File = { 33 | dispose = io_prim.close_file, 34 | is_disposed = io_prim.is_file_closed, 35 | } 36 | 37 | { 38 | open_file, 39 | create_file, 40 | 41 | functor, 42 | applicative, 43 | monad, 44 | read, 45 | write, 46 | disposable, 47 | .. 48 | io_prim 49 | } 50 | -------------------------------------------------------------------------------- /std/io/base.glu: -------------------------------------------------------------------------------- 1 | let { Functor } = import! std.functor 2 | let { Applicative } = import! std.applicative 3 | let { Monad } = import! std.monad 4 | 5 | let io_prim @ { IO } = import! std.io.prim 6 | 7 | 8 | let functor : Functor IO = { 9 | map = \f -> io_prim.flat_map (\x -> io_prim.wrap (f x)), 10 | } 11 | 12 | let applicative : Applicative IO = 13 | let wrap = io_prim.wrap 14 | let apply f x = io_prim.flat_map (\g -> io_prim.flat_map (\y -> wrap (g y)) x) f 15 | 16 | { functor, apply, wrap } 17 | 18 | let monad : Monad IO = { 19 | applicative = applicative, 20 | flat_map = io_prim.flat_map, 21 | } 22 | 23 | { IO, functor, applicative, monad } 24 | -------------------------------------------------------------------------------- /std/json.glu: -------------------------------------------------------------------------------- 1 | //! A type representing a JSON value. 2 | //! 3 | //! _This module is only available if gluon is compiled with the `serialization` feature._ 4 | 5 | let { Map, ? } = import! std.map 6 | let { ? } = import! std.array 7 | 8 | type Value = 9 | | Null 10 | | Bool Bool 11 | | Int Int 12 | | Float Float 13 | | String String 14 | | Array (Array Value) 15 | | Object (Map String Value) 16 | 17 | { Value } 18 | -------------------------------------------------------------------------------- /std/lazy.glu: -------------------------------------------------------------------------------- 1 | //! Lazy values. 2 | //! 3 | //! A lazy value will only be evaluated once the `force` function is called on it. 4 | //! The value returned by `force` is cached so any subsequence calls of `force` on the same value 5 | //! will only return the cached value and not evaluate the lazy value again. 6 | 7 | let prim = import! std.lazy.prim 8 | 9 | { 10 | .. 11 | prim 12 | } 13 | -------------------------------------------------------------------------------- /std/lazyt.glu: -------------------------------------------------------------------------------- 1 | //@NO-IMPLICIT-PRELUDE 2 | //! Monad transformer version of `Lazy` 3 | 4 | let { Applicative, apply, wrap } = import! std.applicative 5 | let { (<<) } = import! std.function 6 | let { Functor, map } = import! std.functor 7 | let { Lazy, lazy, force } = import! std.lazy 8 | let { Monad, flat_map } = import! std.monad 9 | let { Transformer } = import! std.transformer 10 | 11 | type LazyT m a = Lazy (m a) 12 | 13 | let functor : [Functor m] -> Functor (LazyT m) = 14 | let ltmap f ma = lazy (\_ -> map f (force ma)) 15 | 16 | { map = ltmap } 17 | 18 | let applicative : [Applicative m] -> Applicative (LazyT m) = 19 | let ltwrap a = lazy (\_ -> wrap a) 20 | let ltapply mf ma = lazy (\_ -> apply (force mf) (force ma)) 21 | 22 | { functor, apply = ltapply, wrap = ltwrap } 23 | 24 | let monad : [Monad m] -> Monad (LazyT m) = 25 | let ltflat_map f ma = lazy (\_ -> flat_map (force << f) (force ma)) 26 | 27 | { applicative, flat_map = ltflat_map } 28 | 29 | let transformer : Transformer LazyT = 30 | let wrap_monad ma : [Monad m] -> m a -> LazyT m a = lazy (\_ -> ma) 31 | 32 | { /* monad, */ wrap_monad } 33 | 34 | let force_t : LazyT m a -> m a = force 35 | 36 | { 37 | LazyT, 38 | 39 | force_t, 40 | 41 | functor, 42 | applicative, 43 | monad, 44 | transformer, 45 | } 46 | -------------------------------------------------------------------------------- /std/monad.glu: -------------------------------------------------------------------------------- 1 | //@NO-IMPLICIT-PRELUDE 2 | //! Implementation of the `Monad` type 3 | 4 | let { Bool } = import! std.types 5 | let { Applicative, wrap } = import! std.applicative 6 | 7 | /// A generalised interface for imperatively sequencing actions 8 | #[implicit] 9 | type Monad (m : Type -> Type) = { 10 | applicative : Applicative m, 11 | /// This can be seen as akin to sequential variable binding in an 12 | /// imperative language. For example in Javascript: 13 | /// 14 | /// ```js 15 | /// var x = call_fallible("hello"); 16 | /// do_something(x); 17 | /// ``` 18 | /// 19 | /// In gluon this would look like: 20 | /// 21 | /// ```gluon 22 | /// result.monad.flat_map (\x -> do_something x) (call_fallible "hello") 23 | /// ``` 24 | /// 25 | /// Note that it is sometimes more ergonomic to use the `(>>=)` operator: 26 | /// 27 | /// ```gluon 28 | /// let { (>>=) } = import! std.prelude 29 | /// 30 | /// call_fallible "hello" >>= (\x -> do_something x) 31 | /// ``` 32 | /// 33 | /// # Note 34 | /// 35 | /// * Known as `(=<<) or `flip (>>=)` in Haskell 36 | /// * Known as `Option::and_then` and `Result::and_then` in Rust 37 | flat_map : forall a b . (a -> m b) -> m a -> m b 38 | } 39 | 40 | let flat_map ?m : [Monad m] -> (a -> m b) -> m a -> m b = m.flat_map 41 | 42 | #[infix(right, 1)] 43 | let (=<<) : [Monad m] -> (a -> m b) -> m a -> m b = flat_map 44 | 45 | #[infix(left, 1)] 46 | let (>>=) x f : [Monad m] -> m a -> (a -> m b) -> m b = flat_map f x 47 | 48 | let join mm : [Monad m] -> m (m a) -> m a = mm >>= (\x -> x) 49 | 50 | // Kleisli composition 51 | #[infix(right, 9)] 52 | let (<=<) g f x : [Monad m] -> (b -> m c) -> (a -> m b) -> a -> m c = g =<< f x 53 | 54 | #[infix(left, 9)] 55 | let (>=>) f g x : [Monad m] -> (a -> m b) -> (b -> m c) -> a -> m c = f x >>= g 56 | 57 | { 58 | Monad, 59 | flat_map, 60 | (>>=), 61 | (=<<), 62 | join, 63 | (<=<), 64 | (>=>), 65 | } 66 | -------------------------------------------------------------------------------- /std/monoid.glu: -------------------------------------------------------------------------------- 1 | //@NO-IMPLICIT-PRELUDE 2 | //! Implementation of the `Monoid` type 3 | 4 | let { Semigroup } = import! std.semigroup 5 | 6 | /// `Monoid a` represents an semigroup an which has an identity. This means 7 | /// the following additional laws must hold: 8 | /// 9 | /// * `forall x . append x empty == x` 10 | /// * `forall x . append empty x == x` 11 | #[implicit] 12 | type Monoid a = { 13 | semigroup : Semigroup a, 14 | /// # Note 15 | /// 16 | /// * Known as `mempty` in Haskell 17 | empty : a 18 | } 19 | 20 | let empty ?m : [Monoid a] -> a = m.empty 21 | 22 | { 23 | Monoid, 24 | empty, 25 | } 26 | -------------------------------------------------------------------------------- /std/num.glu: -------------------------------------------------------------------------------- 1 | //@NO-IMPLICIT-PRELUDE 2 | //! Operations on numbers. 3 | 4 | let { Ord } = import! std.cmp 5 | 6 | /// The basic operation on numbers. 7 | /// Defined for both the primitive type `Int` and `Float` 8 | #[implicit] 9 | type Num a = { 10 | ord : Ord a, 11 | /// The addition operator 12 | (+) : a -> a -> a, 13 | /// The subtraction operator 14 | (-) : a -> a -> a, 15 | /// The multiplication operator 16 | (*) : a -> a -> a, 17 | /// The division operator 18 | (/) : a -> a -> a, 19 | /// The negation function 20 | negate : a -> a 21 | } 22 | 23 | #[infix(left, 6)] 24 | let (+) ?num : [Num a] -> a -> a -> a = num.(+) 25 | #[infix(left, 6)] 26 | let (-) ?num : [Num a] -> a -> a -> a = num.(-) 27 | #[infix(left, 7)] 28 | let (*) ?num : [Num a] -> a -> a -> a = num.(*) 29 | #[infix(left, 7)] 30 | let (/) ?num : [Num a] -> a -> a -> a = num.(/) 31 | 32 | let negate ?num : [Num a] -> a -> a = num.negate 33 | 34 | { 35 | Num, 36 | 37 | (+), 38 | (-), 39 | (*), 40 | (/), 41 | negate, 42 | } 43 | -------------------------------------------------------------------------------- /std/path.glu: -------------------------------------------------------------------------------- 1 | //! Functions operating on paths 2 | 3 | let types @ { Component } = import! std.path.types 4 | let _ = import! std.fs.prim 5 | let prim = import! std.path.prim 6 | { 7 | Component, 8 | eq_Component = types.eq_Component, 9 | show_Component = types.show_Component, 10 | .. 11 | prim 12 | } 13 | -------------------------------------------------------------------------------- /std/path/types.glu: -------------------------------------------------------------------------------- 1 | 2 | #[derive(Show, Eq)] 3 | type Component = 4 | | Prefix 5 | | RootDir 6 | | CurDir 7 | | ParentDir 8 | | Normal String 9 | { Component, eq_Component, show_Component } 10 | -------------------------------------------------------------------------------- /std/prelude.glu: -------------------------------------------------------------------------------- 1 | //@NO-IMPLICIT-PRELUDE 2 | //! Definitions which gets implicit re-export in every file. 3 | 4 | let { IO } = import! std.io.prim 5 | let { Option } = import! std.types 6 | let { Functor } = import! std.functor 7 | let { Applicative, (*>), wrap } = import! std.applicative 8 | let { Alternative } = import! std.alternative 9 | let { Foldable } = import! std.foldable 10 | let { Monad } = import! std.monad 11 | let { Semigroup, append, (<>) } = import! std.semigroup 12 | let { Monoid, empty } = import! std.monoid 13 | let { Group } = import! std.group 14 | let { Eq, Ord, Bool, Ordering, (==), (/=), (<), (<=), (>=), (>) } = import! std.cmp 15 | let { Show, show } = import! std.show 16 | let { Category, id, compose } = import! std.category 17 | let { Num, (+), (-), (*), (/), negate } = import! std.num 18 | let { Bool, not } = import! std.bool 19 | let { Array } = import! std.array 20 | let { (++) } = import! std.string 21 | let { error } = import! std.prim 22 | let { flat_map } = import! std.monad 23 | let { (<|) } = import! std.function 24 | 25 | { 26 | IO, 27 | 28 | Ordering, 29 | 30 | Semigroup, 31 | append, 32 | (<>), 33 | 34 | Monoid, 35 | empty, 36 | 37 | Group, 38 | 39 | Eq, 40 | (==), 41 | (/=), 42 | 43 | Ord, 44 | (<), 45 | (<=), 46 | (>=), 47 | (>), 48 | 49 | Category, 50 | id, 51 | compose, 52 | 53 | Functor, 54 | Applicative, 55 | Alternative, 56 | Monad, 57 | 58 | Num, 59 | (+), 60 | (-), 61 | (*), 62 | (/), 63 | negate, 64 | 65 | Show, 66 | show, 67 | 68 | Option, 69 | Bool, 70 | 71 | Array, 72 | 73 | (++), 74 | 75 | not, 76 | 77 | error, 78 | 79 | flat_map, 80 | 81 | (<|), 82 | } 83 | -------------------------------------------------------------------------------- /std/process.glu: -------------------------------------------------------------------------------- 1 | //@NO-IMPLICIT-PRELUDE 2 | //! Functions for working with external processes 3 | 4 | let process_prim = import! std.process.prim 5 | let { Option } = import! std.option 6 | 7 | let proc command args = { command, args, env = None, current_dir = None } 8 | 9 | { 10 | proc, 11 | .. 12 | process_prim 13 | } 14 | -------------------------------------------------------------------------------- /std/random.glu: -------------------------------------------------------------------------------- 1 | //! Basic random number generation 2 | //! 3 | //! _This module is only available if gluon is compiled with the `rand` feature._ 4 | 5 | let prim @ { XorShiftRng } = import! std.random.prim 6 | 7 | type RandomGen g = { next : g -> { value : Int, gen : g } } 8 | 9 | let xor_shift_rng = 10 | let random_gen : RandomGen XorShiftRng = { 11 | next = prim.xor_shift_next, 12 | } 13 | 14 | { 15 | new = prim.xor_shift_new, 16 | random_gen, 17 | } 18 | 19 | { 20 | RandomGen, 21 | XorShiftRng, 22 | 23 | xor_shift_rng, 24 | 25 | thread_rng = { 26 | next_int = prim.next_int, 27 | next_float = prim.next_float, 28 | gen_int_range = prim.gen_int_range, 29 | }, 30 | } 31 | -------------------------------------------------------------------------------- /std/reference.glu: -------------------------------------------------------------------------------- 1 | //! A mutable reference type 2 | 3 | let reference @ { Reference } = import! std.reference.prim 4 | let { flat_map } = import! std.monad 5 | 6 | let { IO, ? } = import! std.io.base 7 | 8 | #[infix(right, 9)] 9 | let (<-) = reference.(<-) 10 | 11 | let modify r f : Reference a -> (a -> a) -> IO () = 12 | do x = reference.load r 13 | r <- f x 14 | { 15 | Reference, 16 | (<-), 17 | modify, 18 | .. 19 | reference 20 | } 21 | -------------------------------------------------------------------------------- /std/regex.glu: -------------------------------------------------------------------------------- 1 | //@NO-IMPLICIT-PRELUDE 2 | //! Bindings for rust-lang/regex 3 | 4 | let { Match, eq_Match, show_Match } = import! std.regex.types 5 | let regex_prim @ { Regex, Error } = import! std.regex.prim 6 | 7 | { 8 | Match, 9 | Regex, 10 | Error, 11 | 12 | eq_Match, 13 | show_Match, 14 | .. 15 | regex_prim 16 | } 17 | 18 | 19 | -------------------------------------------------------------------------------- /std/regex/types.glu: -------------------------------------------------------------------------------- 1 | 2 | #[derive(Eq, Show)] 3 | type Match = { start : Int, end : Int, text : String } 4 | 5 | { Match, eq_Match, show_Match } 6 | -------------------------------------------------------------------------------- /std/semigroup.glu: -------------------------------------------------------------------------------- 1 | //@NO-IMPLICIT-PRELUDE 2 | //! Implementation of the `Applicative` type 3 | 4 | /// `Semigroup a` represents an associative operation on `a`. 5 | /// This means the following laws must hold: 6 | /// 7 | /// * `forall x . append x (append y z) == append (append x y) z` 8 | #[implicit] 9 | type Semigroup a = { 10 | /// # Note 11 | /// 12 | /// * Known as `(<>)` or `mappend` in Haskell 13 | append : a -> a -> a 14 | } 15 | 16 | let append ?s : [Semigroup a] -> a -> a -> a = s.append 17 | #[infix(left, 4)] 18 | let (<>) : [Semigroup a] -> a -> a -> a = append 19 | 20 | { 21 | Semigroup, 22 | append, 23 | (<>), 24 | } 25 | -------------------------------------------------------------------------------- /std/show.glu: -------------------------------------------------------------------------------- 1 | //@NO-IMPLICIT-PRELUDE 2 | //! Value to string conversion. 3 | 4 | /// `Show a` represents a conversion function from `a` to a readable string. 5 | #[implicit] 6 | type Show a = { show : a -> String } 7 | 8 | /// Converts a value into a string. 9 | /// ``` 10 | /// let { ? } = import! std.effect 11 | /// let { assert_eq, ? } = import! std.test 12 | /// let list @ { ? } = import! std.list 13 | /// 14 | /// seq assert_eq (show 123) "123" 15 | /// seq assert_eq (show 3.14) "3.14" 16 | /// seq assert_eq (show "abc") "\"abc\"" 17 | /// assert_eq (show (list.of [1, 2, 3])) "[1, 2, 3]" 18 | /// ``` 19 | let show ?s : [Show a] -> a -> String = s.show 20 | 21 | { 22 | Show, 23 | show, 24 | } 25 | -------------------------------------------------------------------------------- /std/state.glu: -------------------------------------------------------------------------------- 1 | //! The state monad. 2 | 3 | let { Applicative, Functor, Monad } = import! std.prelude 4 | 5 | type State s a = s -> { value : a, state : s } 6 | 7 | let functor : Functor (State s) = 8 | rec let map f m : (a -> b) -> State s a -> State s b = \state -> 9 | let { value, state } = m state 10 | { value = f value, state = state } 11 | 12 | { map } 13 | 14 | let applicative : Applicative (State s) = 15 | rec let apply mf n : State s (a -> b) -> State s a -> State s b = \state -> 16 | let { value, state } = mf state 17 | functor.map value n state 18 | in 19 | let wrap value : a -> State s a = \state -> { value, state } 20 | 21 | { functor, apply, wrap } 22 | 23 | let monad : Monad (State s) = 24 | rec let flat_map f m : (a -> State s b) -> State s a -> State s b = \state -> 25 | let { value, state } = m state 26 | let m2 = f value 27 | m2 state 28 | 29 | { applicative, flat_map } 30 | 31 | let put value : s -> State s () = \state -> { value = (), state = value } 32 | 33 | let get : State s s = \state -> { value = state, state } 34 | 35 | let gets f : (s -> a) -> State s a = \state -> { value = f state, state } 36 | 37 | let modify f : (s -> s) -> State s () = \state -> { value = (), state = f state } 38 | 39 | let runState f state : State s a -> s -> { value : a, state : s } = f state 40 | 41 | let evalState f state : State s a -> s -> a = (runState f state).value 42 | 43 | let execState f state : State s a -> s -> s = (runState f state).state 44 | 45 | { State, applicative, functor, monad, put, get, modify, runState, evalState, execState } 46 | -------------------------------------------------------------------------------- /std/string.glu: -------------------------------------------------------------------------------- 1 | //@NO-IMPLICIT-PRELUDE 2 | //! A UTF-8 encoded string 3 | 4 | let string_prim = import! std.string.prim 5 | let prim = import! std.prim 6 | let { Semigroup, (<>) } = import! std.semigroup 7 | let { Monoid } = import! std.monoid 8 | let { Show } = import! std.show 9 | let { Eq, Ord, Ordering } = import! std.cmp 10 | let function = import! std.function 11 | 12 | let semigroup : Semigroup String = { append = string_prim.append } 13 | 14 | /// Appends two strings. 15 | /// 16 | /// Re-export of `semigroup.append`. 17 | #[infix(left, 4)] 18 | let (++) : String -> String -> String = (<>) 19 | 20 | let monoid : Monoid String = { semigroup, empty = "" } 21 | 22 | let eq : Eq String = { (==) = prim.string_eq } 23 | 24 | let ord : Ord String = { eq, compare = prim.string_compare } 25 | 26 | let show : Show String = { show = \s -> "\"" ++ s ++ "\"" } 27 | 28 | { 29 | eq, 30 | ord, 31 | show, 32 | semigroup, 33 | monoid, 34 | (++), 35 | .. 36 | string_prim 37 | } 38 | -------------------------------------------------------------------------------- /std/thread.glu: -------------------------------------------------------------------------------- 1 | //! Green threading library. 2 | 3 | let prim = import! std.thread.prim 4 | 5 | { 6 | .. 7 | prim 8 | } 9 | -------------------------------------------------------------------------------- /std/transformer.glu: -------------------------------------------------------------------------------- 1 | //@NO-IMPLICIT-PRELUDE 2 | //! Utilities for writing `Monad` transformers 3 | 4 | let { Monad } = import! std.prelude 5 | 6 | #[implicit] 7 | type Transformer t = { wrap_monad : forall a m . [Monad m] -> m a -> t m a } 8 | 9 | let wrap_monad ?_ ?tr ma : [Monad m] -> [Transformer t] -> m a -> t m a = 10 | tr.wrap_monad ma 11 | 12 | { Transformer, wrap_monad } 13 | -------------------------------------------------------------------------------- /std/traversable.glu: -------------------------------------------------------------------------------- 1 | //@NO-IMPLICIT-PRELUDE 2 | //! Implementation of the `Traversable` type 3 | 4 | let { Functor } = import! std.functor 5 | let { Foldable } = import! std.foldable 6 | let { Applicative } = import! std.applicative 7 | 8 | #[implicit] 9 | type Traversable t = { 10 | functor : Functor t, 11 | foldable : Foldable t, 12 | traverse : forall a b m . Applicative m -> (a -> m b) -> t a -> m (t b) 13 | } 14 | 15 | let traverse ?t ?a : forall a b m . 16 | [Traversable t] 17 | -> [Applicative m] 18 | -> (a -> m b) 19 | -> t a 20 | -> m (t b) 21 | = 22 | t.traverse a 23 | 24 | let sequence : [Traversable t] -> [Applicative m] -> t (m a) -> m (t a) = 25 | traverse (\x -> x) 26 | 27 | let for x f : [Traversable t] -> [Applicative m] -> t a -> (a -> m b) -> m (t b) = 28 | traverse f x 29 | 30 | 31 | { 32 | Traversable, 33 | 34 | traverse, 35 | sequence, 36 | for, 37 | } 38 | -------------------------------------------------------------------------------- /std/types.glu: -------------------------------------------------------------------------------- 1 | //@NO-IMPLICIT-PRELUDE 2 | //! Definition of standard types separate from the prelude to allow primitives to use them 3 | 4 | /// `Bool` represents a value which can only be `True` or `False` 5 | type Bool = 6 | | False 7 | | True 8 | 9 | /// `Option` represents a value which may not exist. 10 | type Option a = 11 | | None 12 | | Some a 13 | 14 | /// `Result` represents either success (`Ok`) or an error (`Err`) 15 | type Result e t = 16 | | Err e 17 | | Ok t 18 | 19 | /// `Ordering` represents the result of comparing two values 20 | type Ordering = 21 | | LT 22 | | EQ 23 | | GT 24 | 25 | { Bool, Option, Result, Ordering } 26 | -------------------------------------------------------------------------------- /std/unit.glu: -------------------------------------------------------------------------------- 1 | //! The unit type. 2 | 3 | let { Eq, Ord, Ordering, Show } = import! std.prelude 4 | let { const } = import! std.function 5 | let { Bool } = import! std.bool 6 | 7 | let eq : Eq () = { (==) = const (const True) } 8 | 9 | let ord : Ord () = { eq = eq, compare = const (const EQ) } 10 | 11 | let show : Show () = { show = const "()" } 12 | 13 | { 14 | eq, 15 | ord, 16 | show, 17 | } 18 | -------------------------------------------------------------------------------- /std/writer.glu: -------------------------------------------------------------------------------- 1 | //! Implementation of the `Writer` type 2 | 3 | let { Functor, map } = import! std.functor 4 | let prelude @ { Applicative, Monad, Monoid, (<>) } = import! std.prelude 5 | let { empty } = import! std.monoid 6 | let { Identity } = import! std.identity 7 | 8 | /// The writer Monad 9 | type Writer w a = { value : a, writer : w } 10 | 11 | let functor : [Monoid w] -> Functor (Writer w) = { 12 | map = \f m -> { value = f m.value, writer = m.writer }, 13 | } 14 | 15 | let applicative : [Monoid w] -> Applicative (Writer w) = { 16 | functor, 17 | apply = \mf m -> { value = mf.value m.value, writer = mf.writer <> m.writer }, 18 | wrap = \value -> { value, writer = empty }, 19 | } 20 | 21 | let monad : [Monoid w] -> Monad (Writer w) = { 22 | applicative, 23 | flat_map = \f m -> 24 | let { value, writer } = f m.value 25 | { value, writer = m.writer <> writer }, 26 | } 27 | 28 | let tell w : w -> Writer w () = { value = (), writer = w } 29 | 30 | { Writer, functor, applicative, monad, tell } 31 | -------------------------------------------------------------------------------- /tests/array.rs: -------------------------------------------------------------------------------- 1 | extern crate env_logger; 2 | 3 | extern crate gluon; 4 | 5 | #[macro_use] 6 | mod support; 7 | 8 | test_expr! { array, 9 | r#" 10 | let array = import! std.array.prim 11 | let arr = [1,2,3] 12 | 13 | array.index arr 0 #Int== 1 14 | && array.len arr #Int== 3 15 | && array.len (array.append arr arr) #Int== array.len arr #Int* 2"#, 16 | true 17 | } 18 | 19 | test_expr! { array_byte, 20 | r#" 21 | let array = import! std.array.prim 22 | let arr = [1b,2b,3b] 23 | 24 | let b = array.index arr 2 #Byte== 3b && array.len arr #Int== 3 25 | let arr2 = array.append arr arr 26 | b && array.len arr2 #Int== array.len arr #Int* 2 27 | && array.index arr2 1 #Byte== array.index arr2 4 28 | "#, 29 | true 30 | } 31 | 32 | test_expr! { array_float, 33 | r#" 34 | let array = import! std.array.prim 35 | let arr = [1.0,2.0,3.0] 36 | 37 | let b = array.index arr 2 #Float== 3.0 && array.len arr #Int== 3 38 | let arr2 = array.append arr arr 39 | b && array.len arr2 #Int== array.len arr #Int* 2 40 | && array.index arr2 1 #Float== array.index arr2 4 41 | "#, 42 | true 43 | } 44 | 45 | test_expr! { array_data, 46 | r#" 47 | let array = import! std.array.prim 48 | let arr = [{x = 1, y = "a" }, { x = 2, y = "b" }] 49 | 50 | let b = (array.index arr 1).x #Int== 2 && array.len arr #Int== 2 51 | let arr2 = array.append arr arr 52 | b && array.len arr2 #Int== array.len arr #Int* 2 53 | "#, 54 | true 55 | } 56 | 57 | test_expr! { array_array, 58 | r#" 59 | let array = import! std.array.prim 60 | let arr = [[], [1], [2, 3]] 61 | 62 | let b = array.len (array.index arr 1) #Int== 1 && array.len arr #Int== 3 63 | let arr2 = array.append arr arr 64 | b && array.len arr2 #Int== array.len arr #Int* 2 65 | "#, 66 | true 67 | } 68 | 69 | // Test that empty variants are handled correctly in arrays 70 | test_expr! { array_empty_variant, 71 | r#" 72 | let array = import! std.array.prim 73 | type Test = | Empty | Some Int 74 | let arr = [Empty, Some 1] 75 | match array.index arr 0 with 76 | | Empty -> 0 77 | | Some x -> x 78 | "#, 79 | 0i32 80 | } 81 | 82 | // Test that array append handles array types correctly 83 | test_expr! { array_empty_append, 84 | r#" 85 | let array = import! std.array.prim 86 | type Test = | Empty | Some Int 87 | let arr = array.append [] [Empty, Some 1] 88 | match array.index arr 0 with 89 | | Empty -> 0 90 | | Some x -> x 91 | "#, 92 | 0i32 93 | } 94 | 95 | test_expr! { array_load, 96 | r#" 97 | let array = import! std.array 98 | 0 99 | "#, 100 | 0i32 101 | } 102 | 103 | test_expr! { array_fold, 104 | r#" 105 | let array = import! std.array 106 | array.foldable.foldl (\x y -> y.x) 0 [{ x = 4 }] 107 | "#, 108 | 4 109 | } 110 | -------------------------------------------------------------------------------- /tests/compile-fail/get-reference.rs: -------------------------------------------------------------------------------- 1 | extern crate gluon; 2 | use gluon::new_vm; 3 | use gluon::vm::api::Getable; 4 | use gluon::vm::internal::Value; 5 | use gluon::vm::Variants; 6 | 7 | #[cfg_attr(rustfmt, rustfmt_skip)] 8 | fn main() { 9 | unsafe { 10 | let vm = new_vm(); 11 | let value = Value::int(0); 12 | let value = Variants::new(&value); 13 | //~^ Error `value` does not live long enough 14 | let _: &'static str = <&'static str>::from_value(&vm, value); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/compile-fail/getable-reference-str.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate gluon_vm; 3 | extern crate gluon; 4 | use gluon::{ 5 | import::add_extern_module, 6 | new_vm, 7 | vm::{ 8 | api::primitive_f, 9 | thread::{Status, Thread}, 10 | ExternModule, 11 | }, 12 | }; 13 | 14 | fn f(_: &'static str) {} 15 | 16 | #[cfg_attr(rustfmt, rustfmt_skip)] 17 | fn main() { 18 | let vm = new_vm(); 19 | add_extern_module(&vm, "test", |vm| { 20 | ExternModule::new(vm, primitive!(1, f)) 21 | //~^ `thread` has lifetime `'thread` but it needs to satisfy a `'static` lifetime requirement 22 | }); 23 | } 24 | -------------------------------------------------------------------------------- /tests/compile-fail/getable-reference.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate gluon_vm; 3 | extern crate gluon; 4 | extern crate gluon_codegen; 5 | 6 | use gluon::{ 7 | import::add_extern_module, 8 | new_vm, 9 | vm::{ 10 | api::{primitive_f, Userdata, VmType}, 11 | gc::{Gc, Trace}, 12 | thread::{Status, Thread}, 13 | ExternModule, 14 | }, 15 | }; 16 | 17 | #[derive(Debug, gluon_codegen::Trace)] 18 | struct Test; 19 | 20 | impl Userdata for Test {} 21 | 22 | impl VmType for Test { 23 | type Type = Test; 24 | } 25 | 26 | fn f(_: &'static Test) {} 27 | 28 | #[cfg_attr(rustfmt, rustfmt_skip)] 29 | fn main() { 30 | let vm = new_vm(); 31 | add_extern_module(&vm, "test", |vm| { 32 | ExternModule::new(vm, primitive!(1, f)) 33 | //~^ `thread` has lifetime `'thread` but it needs to satisfy a `'static` lifetime requirement 34 | }); 35 | } 36 | -------------------------------------------------------------------------------- /tests/compile-fail/run_expr_str_ref.rs: -------------------------------------------------------------------------------- 1 | extern crate gluon; 2 | 3 | use gluon::{new_vm, ThreadExt}; 4 | 5 | fn main() { 6 | let vm = new_vm(); 7 | 8 | let _ = vm.run_expr::<&str>("", r#" "test" "#); 9 | //~^ the trait bound `for<'value> &str: Getable<'_, 'value>` is not satisfied [E0277] 10 | } 11 | -------------------------------------------------------------------------------- /tests/compile-fail/store-ref.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate gluon_vm; 3 | extern crate gluon; 4 | extern crate gluon_codegen; 5 | 6 | use std::{fmt, sync::Mutex}; 7 | 8 | use gluon::{ 9 | import::add_extern_module, 10 | new_vm, 11 | vm::{ 12 | api::{primitive_f, Userdata, VmType}, 13 | gc::Trace, 14 | thread::{Status, Thread}, 15 | ExternModule, 16 | }, 17 | }; 18 | 19 | #[derive(gluon_codegen::Trace)] 20 | #[gluon_trace(skip)] 21 | struct Test<'vm>(Mutex<&'vm str>); 22 | 23 | impl Userdata for Test<'static> {} 24 | 25 | impl<'vm> fmt::Debug for Test<'vm> { 26 | fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result { 27 | Ok(()) 28 | } 29 | } 30 | 31 | impl<'vm> VmType for Test<'vm> { 32 | type Type = Test<'static>; 33 | } 34 | 35 | fn f<'vm>(test: &'vm Test<'vm>, s: &'vm str) { 36 | *test.0.lock().unwrap() = s; 37 | } 38 | 39 | #[cfg_attr(rustfmt, rustfmt_skip)] 40 | fn main() { 41 | let vm = new_vm(); 42 | add_extern_module(&vm, "test", |vm| { 43 | ExternModule::new(vm, primitive!(2, f)) 44 | //~^ `thread` has lifetime `'thread` but it needs to satisfy a `'static` lifetime requirement 45 | }); 46 | } 47 | -------------------------------------------------------------------------------- /tests/compiletest.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "nightly")] 2 | extern crate compiletest_rs as compiletest; 3 | extern crate env_logger; 4 | 5 | use std::fs; 6 | use std::path::{Path, PathBuf}; 7 | 8 | fn lib_dir(out_dir: &Path, lib_name: &str) -> PathBuf { 9 | // Just loading gluon through -L dir does not work as we compile gluon with different sets of 10 | // flags which gives ambiguity errors. 11 | // Instead retrieve the latest compiled gluon library which should usually be the correct one 12 | let mut gluon_rlibs: Vec<_> = fs::read_dir(out_dir.join("deps")) 13 | .unwrap() 14 | .filter_map(|entry| { 15 | let entry = entry.expect("dir entry"); 16 | if entry 17 | .path() 18 | .to_str() 19 | .map_or(false, |name| name.contains(lib_name)) 20 | { 21 | Some(entry) 22 | } else { 23 | None 24 | } 25 | }) 26 | .collect(); 27 | gluon_rlibs.sort_by(|l, r| { 28 | l.metadata() 29 | .unwrap() 30 | .modified() 31 | .unwrap() 32 | .cmp(&r.metadata().unwrap().modified().unwrap()) 33 | }); 34 | gluon_rlibs.last().expect("libgluon not found").path() 35 | } 36 | 37 | fn run_mode(mode: &'static str) { 38 | // Retrieve the path where library dependencies are output 39 | let mut out_dir = PathBuf::from(env!("OUT_DIR")); 40 | loop { 41 | match out_dir.file_name() { 42 | Some(name) => match name.to_str() { 43 | Some(name) if name == "debug" => break, 44 | _ => (), 45 | }, 46 | None => break, 47 | } 48 | out_dir.pop(); 49 | } 50 | let gluon_rlib = lib_dir(&out_dir, "libgluon-"); 51 | let gluon_vm_rlib = lib_dir(&out_dir, "libgluon_vm-"); 52 | 53 | let mut config = compiletest::Config::default(); 54 | let cfg_mode = mode.parse().ok().expect("Invalid mode"); 55 | 56 | config.verbose = true; 57 | config.mode = cfg_mode; 58 | config.src_base = PathBuf::from(format!("tests/{}", mode)); 59 | config.target_rustcflags = Some(format!( 60 | "-L {}/deps --extern gluon={} --extern gluon_vm={}", 61 | out_dir.display(), 62 | gluon_rlib.display(), 63 | gluon_vm_rlib.display() 64 | )); 65 | println!("{}", config.target_rustcflags.as_ref().unwrap()); 66 | compiletest::run_tests(&config); 67 | } 68 | 69 | #[test] 70 | #[should_panic] 71 | fn compile_test() { 72 | let _ = env_logger::try_init(); 73 | run_mode("compile-fail"); 74 | } 75 | -------------------------------------------------------------------------------- /tests/fail.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tests/fail/cyclic_dependency.glu: -------------------------------------------------------------------------------- 1 | // ERROR Module 'tests.fail.cyclic_dependency' occurs in a cyclic dependency: `tests.fail.cyclic_dependency -> tests.fail.deps.cyclic_dependency2 -> tests.fail.cyclic_dependency` 2 | let c = import! "tests/fail/deps/cyclic_dependency2.glu" 3 | in 1 4 | -------------------------------------------------------------------------------- /tests/fail/deps/cyclic_dependency2.glu: -------------------------------------------------------------------------------- 1 | let c = import! "tests/fail/cyclic_dependency.glu" 2 | in 1 3 | -------------------------------------------------------------------------------- /tests/fail/unwrap.glu: -------------------------------------------------------------------------------- 1 | // ERROR Option was None 2 | let { Option, unwrap } = import! std.option 3 | 4 | unwrap None 5 | -------------------------------------------------------------------------------- /tests/limits.rs: -------------------------------------------------------------------------------- 1 | mod support; 2 | 3 | use gluon::{ 4 | vm::{ 5 | api::{Hole, OpaqueValue}, 6 | thread::ThreadInternal, 7 | Error as VMError, 8 | }, 9 | Error, Thread, ThreadExt, 10 | }; 11 | 12 | use crate::support::make_vm; 13 | 14 | #[test] 15 | fn out_of_memory() { 16 | let _ = ::env_logger::try_init(); 17 | 18 | let vm = make_vm(); 19 | vm.set_memory_limit(10); 20 | vm.get_database_mut().implicit_prelude(false); 21 | 22 | let expr = " [1, 2, 3, 4] "; 23 | let result = vm.run_expr::>("example", expr); 24 | 25 | match result { 26 | // FIXME This should just need to match on the explicit out of memory error 27 | Err(Error::VM(VMError::OutOfMemory { limit: 10, .. })) => (), 28 | Err(err) => panic!("Unexpected error `{:?}`", err), 29 | Ok(_) => panic!("Expected an error"), 30 | } 31 | } 32 | 33 | #[test] 34 | fn stack_overflow() { 35 | let _ = ::env_logger::try_init(); 36 | 37 | let vm = make_vm(); 38 | vm.context().set_max_stack_size(3); 39 | vm.get_database_mut().implicit_prelude(false); 40 | 41 | let expr = " [1, 2, 3, 4] "; 42 | let result = vm.run_expr::>("example", expr); 43 | 44 | match result { 45 | Err(Error::VM(VMError::StackOverflow(3))) => (), 46 | Err(err) => panic!("Unexpected error `{:?}`", err), 47 | Ok(_) => panic!("Expected an error"), 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tests/metadata.rs: -------------------------------------------------------------------------------- 1 | use gluon::{import::Import, vm::thread::RootedThread, ThreadExt}; 2 | 3 | fn make_vm() -> RootedThread { 4 | let vm = ::gluon::new_vm(); 5 | let import = vm.get_macros().get("import"); 6 | import 7 | .as_ref() 8 | .and_then(|import| import.downcast_ref::()) 9 | .expect("Import macro") 10 | .add_path(".."); 11 | vm 12 | } 13 | 14 | #[test] 15 | fn metadata_from_other_module() { 16 | let _ = ::env_logger::try_init(); 17 | let vm = make_vm(); 18 | let text = r#" 19 | let { List, of } = import! std.list 20 | { List, of } 21 | "#; 22 | vm.load_script("test", text) 23 | .unwrap_or_else(|err| panic!("{}", err)); 24 | 25 | let env = vm.get_env(); 26 | assert!(env.get_metadata("test.of").is_ok()); 27 | assert!(env.get_metadata("test.List").is_ok()); 28 | } 29 | -------------------------------------------------------------------------------- /tests/optimize/cmp.glu: -------------------------------------------------------------------------------- 1 | let { Option, Ordering, Bool } = import! std.types 2 | 3 | #[infix(left, 4)] 4 | let (=?) opt y : Option b -> b -> b = 5 | match opt with 6 | | Some x -> x 7 | | None -> y 8 | 9 | let mk_ord builder = 10 | #[infix(left, 4)] 11 | let (<) l r = True 12 | 13 | { 14 | (<) = builder.(<) =? (<), 15 | } 16 | 17 | { Option, mk_ord } 18 | -------------------------------------------------------------------------------- /tests/optimize/inline_num.glu: -------------------------------------------------------------------------------- 1 | let additive = 2 | let semigroup = { 3 | append = \x y -> x #Int+ y 4 | } 5 | 6 | { semigroup } 7 | 8 | 9 | { 10 | (+) = additive.semigroup.append, 11 | additive, 12 | } 13 | -------------------------------------------------------------------------------- /tests/optimize/inline_through_module.glu: -------------------------------------------------------------------------------- 1 | //@NO-IMPLICIT-PRELUDE 2 | //! Definitions which gets implicit re-export in every file. 3 | 4 | let { Num, (+), (-), (*), (/) } = import! tests.optimize.inline_through_module2 5 | 6 | { 7 | Num, 8 | (+), (-), (*), (/), 9 | } 10 | -------------------------------------------------------------------------------- /tests/optimize/inline_through_module2.glu: -------------------------------------------------------------------------------- 1 | //@NO-IMPLICIT-PRELUDE 2 | 3 | #[implicit] 4 | type Num a = { 5 | /// The addition operator 6 | (+) : a -> a -> a, 7 | /// The subtraction operator 8 | (-) : a -> a -> a, 9 | /// The multiplication operator 10 | (*) : a -> a -> a, 11 | /// The division operator 12 | (/) : a -> a -> a, 13 | } 14 | 15 | let num : Num Int = { 16 | (+) = \l r -> l #Int+ r, 17 | (-) = \l r -> l #Int- r, 18 | (*) = \l r -> l #Int* r, 19 | (/) = \l r -> l #Int/ r, 20 | } 21 | 22 | 23 | #[infix(left, 6)] 24 | let (+) ?num : [Num a] -> a -> a -> a = num.(+) 25 | #[infix(left, 6)] 26 | let (-) ?num : [Num a] -> a -> a -> a = num.(-) 27 | #[infix(left, 7)] 28 | let (*) ?num : [Num a] -> a -> a -> a = num.(*) 29 | #[infix(left, 7)] 30 | let (/) ?num : [Num a] -> a -> a -> a = num.(/) 31 | 32 | { 33 | Num, 34 | 35 | (+), (-), (*), (/), 36 | 37 | num, 38 | } 39 | 40 | -------------------------------------------------------------------------------- /tests/parallel.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate gluon_vm; 3 | 4 | use std::thread::spawn; 5 | 6 | use gluon::{ 7 | new_vm, 8 | vm::{ 9 | api::{FunctionRef, OpaqueValue, IO}, 10 | channel::{ChannelRecord, Receiver, Sender}, 11 | }, 12 | Error, RootedThread, ThreadExt, 13 | }; 14 | 15 | #[test] 16 | fn parallel() { 17 | let _ = env_logger::try_init(); 18 | 19 | if let Err(err) = parallel_() { 20 | assert!(false, "{}", err); 21 | } 22 | } 23 | 24 | fn parallel_() -> Result<(), Error> { 25 | let vm = new_vm(); 26 | 27 | vm.get_database_mut().run_io(true); 28 | 29 | vm.run_expr::<()>("", " let _ = import! std.channel in () ")?; 30 | 31 | let (value, _) = vm.run_expr::>( 32 | "", 33 | " let { channel } = import! std.channel in channel 0 ", 34 | )?; 35 | let record_p! { sender, receiver }: ChannelRecord< 36 | OpaqueValue>, 37 | OpaqueValue>, 38 | > = Result::from(value)?; 39 | 40 | let child = vm.new_thread()?; 41 | let handle1 = spawn(move || -> Result<(), Error> { 42 | let expr = r#" 43 | let { ? } = import! std.io 44 | let { wrap } = import! std.applicative 45 | let { send } = import! std.channel 46 | let f sender = 47 | send sender 1 48 | send sender 2 49 | wrap () 50 | f 51 | "#; 52 | let mut f: FunctionRef>) -> IO<()>> = 53 | child.run_expr("", expr)?.0; 54 | Ok(f.call(sender) 55 | .and_then(|io| Result::from(io).map_err(From::from))?) 56 | }); 57 | 58 | let child2 = vm.new_thread()?; 59 | let handle2 = spawn(move || -> Result<(), Error> { 60 | let expr = r#" 61 | let { assert } = import! std.test 62 | let { wrap } = import! std.applicative 63 | let { ? } = import! std.io 64 | let { Bool } = import! std.bool 65 | let { Result } = import! std.result 66 | let { recv } = import! std.channel 67 | 68 | let f receiver = 69 | do x = recv receiver 70 | match x with 71 | | Ok x -> wrap <| assert (x == 1) 72 | | Err _ -> error "Fail 1" 73 | 74 | do x = recv receiver 75 | match x with 76 | | Ok x -> wrap <| assert (x == 2) 77 | | Err _ -> error "Fail 2" 78 | 79 | f 80 | "#; 81 | let mut f: FunctionRef>) -> IO<()>> = 82 | child2.run_expr("", expr)?.0; 83 | Ok(f.call(receiver) 84 | .and_then(|io| Result::from(io).map_err(From::from))?) 85 | }); 86 | 87 | // Ensure that both threads stop without any panics (just dropping ignores panics) 88 | handle1.join().unwrap()?; 89 | handle2.join().unwrap() 90 | } 91 | -------------------------------------------------------------------------------- /tests/pass/alternative.glu: -------------------------------------------------------------------------------- 1 | let { run, Test, TestCase, assert_eq, test, group, ? } = import! std.test 2 | let { (<|) } = import! std.function 3 | let prelude @ { Alternative } = import! std.prelude 4 | let { Applicative, (*>) } = import! std.applicative 5 | let int = import! std.int 6 | let list @ { ? }= import! std.list 7 | let option = import! std.option 8 | let { (<|>), or, empty } = import! std.alternative 9 | 10 | let test_alt ?alt show eq : [Alternative f] -> Show (f Int) -> Eq (f Int) -> _ = 11 | let { wrap } = alt.applicative 12 | 13 | let assert = assert_eq ?show ?eq 14 | 15 | [ 16 | test "empty equal" <| \_ -> (assert empty empty), 17 | test "or selects non-empty" <| \_ -> (assert (empty <|> wrap 1) (wrap 1)), 18 | test "empty <|> empty == empty" <| \_ -> (assert (empty <|> empty) empty), 19 | test "or selects non-empty 2" <| \_ -> (assert (empty <|> empty <|> wrap 10) (wrap 10)) 20 | ] 21 | 22 | let tests: TestCase r () = 23 | group "alternative" [ 24 | group "option" (test_alt option.show option.eq), 25 | group "list" (test_alt list.show list.eq) 26 | ] 27 | 28 | tests 29 | 30 | -------------------------------------------------------------------------------- /tests/pass/arithmetic.glu: -------------------------------------------------------------------------------- 1 | let { run, Test, assert_eq, test, group, ? } = import! std.test 2 | let { (<|) } = import! std.function 3 | let prelude = import! std.prelude 4 | let { Applicative, (*>), ? } = import! std.applicative 5 | let int = import! std.int 6 | let float = import! std.float 7 | let byte @ { ? } = import! std.byte 8 | let { empty } = import! std.monoid 9 | 10 | let { ? } = import! std.effect 11 | 12 | let byte_tests = 13 | group "byte" [ 14 | test "arithmetic" <| \_ -> 15 | assert_eq 2b 2b 16 | *> assert_eq 12b (10b + 2b) 17 | *> assert_eq 123b (50b * 2b + 9b * 3b - 4b), 18 | test "from_int" <| \_ -> assert_eq (byte.from_int 2) 2b, 19 | test "from_int_truncate" <| \_ -> assert_eq (byte.from_int 2000) 208b, 20 | ] 21 | 22 | let int_tests = 23 | group "int" [ 24 | test "arithmetic" <| \_ -> 25 | assert_eq 2 2 26 | *> assert_eq 12 (10 + 2) 27 | *> assert_eq 123 (50 * 2 + 9 * 3 - 4), 28 | test "from_float" <| \_ -> assert_eq (int.from_float 2.0) 2, 29 | test "from_float_truncate" <| \_ -> assert_eq (int.from_float 2.7) 2, 30 | test "from_byte" <| \_ -> assert_eq (int.from_byte 2b) 2, 31 | group "monoid" [ 32 | test "additive" <| \_ -> 33 | let { ? } = int.additive 34 | assert_eq 0 empty, 35 | test "multiplicative" <| \_ -> 36 | let { ? } = int.multiplicative 37 | assert_eq 1 empty, 38 | ] 39 | ] 40 | 41 | let float_tests = 42 | group "float" [ 43 | test "float" <| \_ -> assert_eq 91.0 (50.0 * 2.0 - 3.0 * 3.0), 44 | test "from_int" <| \_ -> assert_eq (float.from_int 2) 2.0, 45 | ] 46 | 47 | group "arithmetic" [byte_tests, int_tests, float_tests] 48 | -------------------------------------------------------------------------------- /tests/pass/channel.glu: -------------------------------------------------------------------------------- 1 | let { TestEff, run, assert_eq, test, group, ? } = import! std.test 2 | let { lift } = import! std.effect.lift 3 | let { (<|) } = import! std.function 4 | let prelude = import! std.prelude 5 | let { Applicative, (*>), ? } = import! std.applicative 6 | let int = import! std.int 7 | let result @ { Result, ? } = import! std.result 8 | let unit @ { ? } = import! std.unit 9 | let { send, recv, channel } = import! std.channel 10 | 11 | 12 | let { ? } = import! std.effect 13 | 14 | let assert_recv channel expect : [Eq a] -> [Show a] -> _ -> Result () a -> _ = 15 | do x = lift <| recv channel 16 | assert_eq x expect 17 | 18 | test "channel" <| \_ -> 19 | do { sender, receiver } = lift <| channel 0 20 | seq lift <| send sender 0 21 | seq lift <| send sender 1 22 | seq lift <| send sender 2 23 | seq assert_recv receiver (Ok 0) 24 | seq assert_recv receiver (Ok 1) 25 | assert_recv receiver (Ok 2) 26 | -------------------------------------------------------------------------------- /tests/pass/char.glu: -------------------------------------------------------------------------------- 1 | let { run, Test, assert_eq, test, group, ? } = import! std.test 2 | let { (<|) } = import! std.function 3 | let prelude = import! std.prelude 4 | let { Applicative, (*>), ? } = import! std.applicative 5 | let char @ { ? } = import! std.char 6 | 7 | group "char" [ 8 | test "from_int" <| \_ -> assert_eq (char.from_int 97) (Some 'a'), 9 | test "from_int_error" <| \_ -> assert_eq (char.from_int 0x110000) None, 10 | test "to_int" <| \_ -> assert_eq (char.to_int 'a') 97, 11 | ] 12 | -------------------------------------------------------------------------------- /tests/pass/deep_clone_userdata.glu: -------------------------------------------------------------------------------- 1 | let { run, assert, assert_eq, group, test, ? } = import! std.test 2 | let { lift } = import! std.effect.lift 3 | let { ? } = import! std.effect 4 | let { (<|) } = import! std.function 5 | let prelude = import! std.prelude 6 | let { wrap, (*>) } = import! std.applicative 7 | let { Result } = import! std.result 8 | let { ref, load } = import! std.reference 9 | let { lazy, force } = import! std.lazy 10 | let { channel, send, recv } = import! std.channel 11 | let { resume, spawn } = import! std.thread 12 | let { ? } = import! std.io 13 | 14 | // Dummy test 15 | group "deep_clone_userdata" [ 16 | test "1" <| \_ -> 17 | do { sender, receiver } = lift <| channel (lazy (\_ -> 0)) 18 | 19 | do thread = lift <| spawn ( 20 | seq send sender (lazy (\_ -> 1)) 21 | let l = lazy (\_ -> 2) 22 | let _ = force l 23 | send sender l 24 | wrap ()) 25 | 26 | lift <| resume thread 27 | do x = lift <| recv receiver 28 | match x with 29 | | Ok x -> assert_eq (force x) 1 30 | | Err e -> error "Receive 1 error" 31 | do x = lift <| recv receiver 32 | match x with 33 | | Ok x -> assert_eq (force x) 2 34 | | Err e -> error "Receive 2 error", 35 | 36 | test "2" <| \_ -> 37 | do r = lift <| ref 0 38 | do { sender, receiver } = lift <| channel r 39 | 40 | do thread = lift <| spawn ( 41 | do r = ref 3 42 | send sender r 43 | wrap ()) 44 | 45 | do r = lift <| resume thread 46 | match r with 47 | | Ok () -> wrap () 48 | | Err e -> error e 49 | do r = lift <| recv receiver 50 | match r with 51 | | Ok x -> 52 | do x = lift <| load x 53 | assert_eq x 3 54 | | Err e -> error "Receive 3 error" 55 | ] 56 | -------------------------------------------------------------------------------- /tests/pass/io.glu: -------------------------------------------------------------------------------- 1 | let { TestEff, assert_eq, test, group, ? } = import! std.test 2 | let { (<|) } = import! std.function 3 | let { Applicative, (*>), ? } = import! std.applicative 4 | let result = import! std.result 5 | let string = import! std.string 6 | let io = import! std.io 7 | 8 | let { ? } = import! std.effect 9 | let { lift } = import! std.effect.lift 10 | 11 | group "io" [ 12 | test "read_file" <| \_ -> 13 | let path = "tests/pass/io.glu" 14 | do contents1 = lift <| io.read_file_to_string path 15 | do contents2 = lift <| io.read_file_to_array path 16 | assert_eq contents1 (result.unwrap_ok <| string.from_utf8 contents2) 17 | ] 18 | -------------------------------------------------------------------------------- /tests/pass/json/ser.glu: -------------------------------------------------------------------------------- 1 | let { Serialize } = import! std.json.ser 2 | 3 | #[derive(Show, Eq, Serialize)] 4 | type Record = { x : Int } 5 | 6 | #[derive(Show, Eq, Serialize)] 7 | type Record2 = { x : Int, y : String } 8 | 9 | #[derive(Show, Eq, Serialize)] 10 | type Variant = | MyInt Int | MyString String 11 | 12 | #[derive(Show, Eq, Serialize)] 13 | type MyOption a = | MyNone | MySome a 14 | 15 | let result @ { Result, ? } = import! std.result 16 | let ser @ { ValueSerializer, Serialize, ? } = import! std.json.ser 17 | let { Test, run, assert, assert_eq, test, group, ? } = import! std.test 18 | let { Applicative, (*>) } = import! std.applicative 19 | let { map } = import! std.functor 20 | let { (<|) } = import! std.function 21 | let int = import! std.int 22 | let list @ { List, ? } = import! std.list 23 | let { ? } = import! std.array 24 | let { (<>) } = import! std.semigroup 25 | 26 | group "json.ser" [ 27 | test "derive_record_1_field" <| \_ -> 28 | assert_eq (ser.to_string { x = 1 }) (Ok r#"{"x":1}"#), 29 | 30 | test "derive_record_2_fields" <| \_ -> 31 | assert_eq (ser.to_string { x = 1, y = "abc" }) (Ok r#"{"x":1,"y":"abc"}"#), 32 | 33 | test "variant_int" <| \_ -> 34 | assert_eq (ser.to_string (MyInt 123)) (Ok r#"123"#), 35 | 36 | test "variant_string" <| \_ -> 37 | assert_eq (ser.to_string (MyString "abc")) (Ok r#""abc""#), 38 | 39 | test "option_some" <| \_ -> 40 | assert_eq (ser.to_string (MySome "abc")) (Ok r#""abc""#), 41 | 42 | test "option_none" <| \_ -> 43 | let x: MyOption String = MyNone 44 | assert_eq (ser.to_string x) (Ok r#"null"#), 45 | ] 46 | 47 | -------------------------------------------------------------------------------- /tests/pass/lazy.glu: -------------------------------------------------------------------------------- 1 | let { run, TestEff, assert_eq, test, ? } = import! std.test 2 | let { (<|) } = import! std.function 3 | let prelude = import! std.prelude 4 | let { Applicative, (*>), wrap } = import! std.applicative 5 | let { lazy, force } = import! std.lazy 6 | 7 | let { ? } = import! std.effect 8 | 9 | let l = lazy (\_ -> 123 + 57) 10 | 11 | let tests : TestEff r () = 12 | assert_eq (force (lazy (\_ -> 2))) 2 *> wrap () 13 | *> assert_eq 180 (force l) 14 | 15 | test "lazy" <| \_ -> tests 16 | -------------------------------------------------------------------------------- /tests/pass/lazyt.glu: -------------------------------------------------------------------------------- 1 | let { (<|) } = import! std.function 2 | let { Test, run, assert, assert_eq, test, group, ? } = import! std.test 3 | let { LazyT, force_t, ? } = import! std.lazyt 4 | let { Functor, map } = import! std.functor 5 | let { Applicative, wrap, (*>) } = import! std.applicative 6 | let { Monad, (>>=) } = import! std.monad 7 | let { Transformer, wrap_monad } = import! std.transformer 8 | let { Option, unwrap, ? } = import! std.option 9 | let { (++), ? } = import! std.string 10 | let list @ { List, ? } = import! std.list 11 | 12 | 13 | let left_identity x f : [Eq a] -> [Show a] -> a -> (a -> LazyT Option a) -> _ = \_ -> 14 | let mx : LazyT Option _ = wrap x 15 | assert_eq (force_t (mx >>= f)) (force_t (f x)) 16 | 17 | let right_identity x : [Eq a] -> [Show a] -> a -> _ = \_ -> 18 | let mx : LazyT Option _ = wrap x 19 | assert_eq (force_t (mx >>= wrap)) (force_t mx) 20 | 21 | let associativity mx f g : [Monad m] -> [Show (m a)] -> [Eq (m a)] -> m a -> _ -> _ -> _ = \_ -> 22 | let mx : LazyT m _ = wrap_monad mx 23 | assert_eq (force_t ((mx >>= f) >>= g)) (force_t (mx >>= (\x -> f x >>= g))) 24 | 25 | group "lazyt" [ 26 | group "LazyT m is monadic" [ 27 | test "left identity" <| left_identity 324 (\x -> wrap <| x + 89), 28 | test "right identity" <| right_identity "hello", 29 | test "associativity" <| associativity (Some 5) (\x -> wrap (x+5)) (\x -> wrap (x*2)), 30 | ], 31 | let x = list.of [8,6,7,5,3,0,9] 32 | let f = (*) 42 33 | test "LazyT m is lazy" <| \_ -> assert_eq (map f x) (force_t <| map f <| wrap_monad x), 34 | ] 35 | -------------------------------------------------------------------------------- /tests/pass/list.glu: -------------------------------------------------------------------------------- 1 | let { TestEff, assert_eq, test, ? } = import! std.test 2 | let { (<|) } = import! std.function 3 | let { Applicative, (*>), ? } = import! std.applicative 4 | let list @ { List, ? } = import! std.list 5 | 6 | let { ? } = import! std.effect 7 | 8 | let empty_list : List Int = 9 | Nil 10 | 11 | let test_list : TestEff r () = 12 | assert_eq (list.of []) empty_list *> 13 | assert_eq (list.of [10, 20, 30]) (Cons 10 (Cons 20 (Cons 30 Nil))) 14 | 15 | test "list" <| \_ -> test_list 16 | -------------------------------------------------------------------------------- /tests/pass/map.glu: -------------------------------------------------------------------------------- 1 | let prelude @ { Eq, Show } = import! std.prelude 2 | let { (<|) } = import! std.function 3 | let int = import! std.int 4 | let option @ { Option } = import! std.option 5 | let string = import! std.string 6 | let { (<>) } = import! std.prelude 7 | let { Test, run, assert, assert_eq, test, group, ? } = import! std.test 8 | let map @ { empty, singleton, find, insert, to_list, keys, values, ? } = import! std.map 9 | let { Applicative, (*>) } = import! std.applicative 10 | let list @ { List, ? } = import! std.list 11 | 12 | let { ? } = import! std.effect 13 | 14 | let show_Entry : Show { key : String, value : Int } = { 15 | show = \e -> e.key <> int.show.show e.value 16 | } 17 | 18 | let eq_Entry : Eq { key : String, value : Int } = { 19 | (==) = \l r -> l.key == r.key && l.value == r.value 20 | } 21 | 22 | let basic_tests = 23 | let test_map = singleton "test" 1 <> singleton "asd" 2 <> singleton "a" 3 24 | 25 | [ 26 | test "find" <| \_ -> (assert_eq (find "test" test_map) (Some 1) 27 | *> assert_eq (find "asd" test_map) (Some 2) 28 | *> assert_eq (find "b" test_map) None 29 | *> assert_eq (find "test" (insert "test" 10 test_map)) (Some 10) 30 | *> assert_eq (find "test" test_map) (Some 1) 31 | ), 32 | test "to_list" <| \_ -> (assert_eq (to_list test_map) (list.of [{ key = "a", value = 3 }, 33 | { key = "asd", value = 2 }, 34 | { key = "test", value = 1 }])), 35 | test "keys" <| \_ -> (assert_eq (keys test_map) (list.of ["a", "asd", "test"])), 36 | test "values" <| \_ -> (assert_eq (values test_map) (list.of [3, 2, 1])), 37 | test "append" <| \_ -> (assert_eq (to_list (test_map <> empty)) (to_list test_map)), 38 | test "append" <| \_ -> (assert_eq (to_list (empty <> test_map)) (to_list test_map)), 39 | ] 40 | 41 | let append_tests = 42 | let test_map1 = singleton "a" 1 <> singleton "b" 2 <> singleton "c" 3 43 | let test_map2 = singleton "+" 1 <> (singleton "-" 2 <> singleton "*" 3) 44 | assert_eq (find "b" test_map1) (Some 2) 45 | *> assert_eq (find "*" test_map2) (Some 3) 46 | 47 | group "map" [group "basic" basic_tests, test "append" <| \_ -> append_tests] 48 | 49 | -------------------------------------------------------------------------------- /tests/pass/match_literal.glu: -------------------------------------------------------------------------------- 1 | let prelude = import! std.prelude 2 | let { (<|) } = import! std.function 3 | let { run, Test, assert_eq, test, group, ? } = import! std.test 4 | let { (*>), ? } = import! std.applicative 5 | 6 | let { ? } = import! std.effect 7 | 8 | let ints input = 9 | match input with 10 | | 0 -> 1 11 | | 1 -> 2 12 | | _ -> 3 13 | 14 | let strings input = 15 | match input with 16 | | "A" -> 4 17 | | "B" -> 5 18 | | _ -> 6 19 | 20 | type N = | A Int | B 21 | 22 | let ns input = 23 | match input with 24 | | A 1 -> 7 25 | | A 2 -> 8 26 | | B -> 9 27 | | _ -> 10 28 | 29 | type R1 = { x: Int } 30 | 31 | let r1 input = 32 | match input with 33 | | { x = 2 } -> 11 34 | | { x = 3 } -> 12 35 | | _ -> 13 36 | 37 | type R2 = { x: Int, y: Int } 38 | 39 | let r2 input = 40 | match input with 41 | | { x = 2, y = 3 } -> 14 42 | | { x = 3, y = 4 } -> 15 43 | | _ -> 16 44 | 45 | type TestM = | TestM Int String 46 | 47 | let test_m input = 48 | match input with 49 | | TestM 1 "hello" -> 20 50 | | TestM _ "world" -> 21 51 | | TestM 2 _ -> 22 52 | | _ -> 23 53 | 54 | let match_ns = 55 | assert_eq (ns (A 1)) 7 56 | *> assert_eq (ns (A 2)) 8 57 | *> assert_eq (ns B) 9 58 | *> assert_eq (ns (A 3)) 10 59 | 60 | let match_ints = 61 | assert_eq (ints 0) 1 62 | *> assert_eq (ints 1) 2 63 | *> assert_eq (ints 2) 3 64 | *> assert_eq (ints 3) 3 65 | 66 | let match_strings = 67 | assert_eq (strings "A") 4 68 | *> assert_eq (strings "B") 5 69 | *> assert_eq (strings "") 6 70 | 71 | let match_r1 = 72 | assert_eq (r1 { x = 2 }) 11 73 | *> assert_eq (r1 { x = 3 }) 12 74 | *> assert_eq (r1 { x = 4 }) 13 75 | 76 | let match_r2 = 77 | assert_eq (r2 { x = 2, y = 3 }) 14 78 | *> assert_eq (r2 { x = 3, y = 4 }) 15 79 | *> assert_eq (r2 { x = 4, y = 4 }) 16 80 | 81 | let match_test_m = 82 | assert_eq (test_m (TestM 1 "hello")) 20 83 | *> assert_eq (test_m (TestM 2 "world")) 21 84 | *> assert_eq (test_m (TestM 2 "hello")) 22 85 | *> assert_eq (test_m (TestM 3 "")) 23 86 | 87 | group "match_literal" [test "match_ints" <| \_ -> match_ints, test "match_strings" <| \_ -> match_strings, test "match_ns" <| \_ -> match_ns, test "match_r1" <| \_ -> match_r1, test "match_r2" <| \_ -> match_r2, test "match_test_m" <| \_ -> match_test_m] 88 | -------------------------------------------------------------------------------- /tests/pass/parser.glu: -------------------------------------------------------------------------------- 1 | let prelude = import! std.prelude 2 | let { (<|) } = import! std.function 3 | let result @ { Result, ? } = import! std.result 4 | let { Test, run, assert_eq, test, group } = import! std.test 5 | let { ? } = import! std.char 6 | let { Parser, applicative, functor, alternative, monad, any, parse } = import! std.parser 7 | let { Applicative, (*>), ? } = import! std.applicative 8 | let { (<|>) } = import! std.alternative 9 | 10 | group "parser" [ 11 | test "any" <| \_ -> (assert_eq (parse any "abc") (Ok 'a')) 12 | ] 13 | -------------------------------------------------------------------------------- /tests/pass/path.glu: -------------------------------------------------------------------------------- 1 | let { (<|) } = import! std.function 2 | let { Test, run, assert, assert_eq, test, group, ? } = import! std.test 3 | let { ? } = import! std.array 4 | 5 | let path @ { Component, ? } = import! std.path 6 | 7 | group "path" [ 8 | test "components" <| \_ -> assert_eq (path.components "../abc/.") [ParentDir, Normal "abc"], 9 | ] 10 | 11 | -------------------------------------------------------------------------------- /tests/pass/reference.glu: -------------------------------------------------------------------------------- 1 | let { assert_eq, group, test } = import! std.test 2 | let { (<|) } = import! std.function 3 | let { ? } = import! std.io 4 | let int = import! std.int 5 | let { Bool } = import! std.bool 6 | let { Reference, ref, (<-), load } = import! std.effect.reference 7 | let { ? } = import! std.effect 8 | let { lift } = import! std.effect.lift 9 | 10 | let assert_eq_ref l r : [Show a] -> [Eq a] -> a -> Reference a -> _ = 11 | do r = load r 12 | assert_eq l r 13 | 14 | // Dummy test 15 | group "reference" [ 16 | test "basic" <| \_ -> 17 | do ri = ref 0 18 | assert_eq_ref 0 ri 19 | ri <- 2 20 | assert_eq_ref 2 ri 21 | assert_eq_ref 2 ri 22 | ri <- 10 23 | assert_eq_ref 10 ri 24 | ] 25 | -------------------------------------------------------------------------------- /tests/pass/regex.glu: -------------------------------------------------------------------------------- 1 | let { run, Test, assert_eq, test, group, ? } = import! std.test 2 | let { Applicative, (*>) } = import! std.applicative 3 | let { (<|), (|>) } = import! std.function 4 | 5 | let { ? } = import! std.array 6 | let { unwrap_ok, unwrap_err } = import! std.result 7 | 8 | let regex @ { ? } = import! std.regex 9 | 10 | let match_a = regex.new "a" |> unwrap_ok 11 | group "regex" [ 12 | test "is_match_a" <| \_ -> 13 | assert_eq (regex.is_match match_a "a") True, 14 | test "is_not_match_a" <| \_ -> 15 | assert_eq (regex.is_match match_a "b") False, 16 | 17 | test "is_match2" <| \_ -> 18 | let match_hello = regex.new "hello, .*" |> unwrap_ok 19 | assert_eq (regex.is_match match_hello "hello, world") True, 20 | 21 | test "compile error" <| \_ -> 22 | let msg = regex.new ")" |> unwrap_err |> regex.error_to_string 23 | assert_eq msg "regex parse error:\n )\n ^\nerror: unopened group", 24 | 25 | test "captures" <| \_ -> 26 | let re = regex.new r#"[a-z]+(?:([0-9]+)|([A-Z]+))"# |> unwrap_ok 27 | assert_eq 28 | (regex.captures re "abc123") 29 | (Some [Some { start = 0, end = 6, text = "abc123" }, Some { start = 3, end = 6, text = "123" }, None]) 30 | 31 | ] 32 | -------------------------------------------------------------------------------- /tests/pass/state.glu: -------------------------------------------------------------------------------- 1 | let prelude = import! std.prelude 2 | let { (<|) } = import! std.function 3 | let { Test, run, assert, assert_eq, test, group, ? } = import! std.test 4 | let int = import! std.int 5 | let state @ { State, put, get, modify, runState, evalState, execState, ? } = import! std.state 6 | let { Applicative, wrap, (*>), ? } = import! std.applicative 7 | 8 | group "state" [ 9 | test "modify execState" <| \_ -> (assert_eq (execState (modify (\x -> x + 2) *> modify (\x -> x * 4)) 0) 8), 10 | test "modify evalState" <| \_ -> (assert_eq (evalState (modify (\x -> x + 2) *> get) 0) 2), 11 | test "put get evalState" <| \_ -> (assert_eq (evalState (put "hello" *> get) "") "hello"), 12 | test "put get runState" <| \_ -> (assert_eq (runState (put "hello" *> get) "").value "hello"), 13 | 14 | /* FIXME 15 | test "record unpack runState" <| \_ -> 16 | let m : State { x : Int } Int = 17 | do { x } = get 18 | wrap x 19 | assert_eq (evalState m { x = 1 }) 1, 20 | */ 21 | ] 22 | 23 | -------------------------------------------------------------------------------- /tests/pass/statet.glu: -------------------------------------------------------------------------------- 1 | let { (<|) } = import! std.function 2 | let { Test, run, assert, assert_eq, test, group, ? } = import! std.test 3 | let { StateT, put, get, gets, modify, run_state_t, eval_state_t, exec_state_t, ? } = import! std.statet 4 | let { wrap, (*>) } = import! std.applicative 5 | let { Monad, (>>=) } = import! std.monad 6 | let { Option, unwrap, ? } = import! std.option 7 | let { (++), ? } = import! std.string 8 | let list @ { List, ? } = import! std.list 9 | let { Transformer, wrap_monad } = import! std.transformer 10 | 11 | #[infix(right,7)] 12 | let (::) x xs = Cons x xs 13 | 14 | let left_identity x f : [Eq a] -> [Show a] -> a -> (a -> StateT _ Option a) -> _ = \_ -> 15 | let mx : StateT _ Option _ = wrap x 16 | let s = () 17 | assert_eq (eval_state_t (mx >>= f) s) (eval_state_t (f x) s) 18 | 19 | let right_identity x : [Eq a] -> [Show a] -> a -> _ = \_ -> 20 | let mx : StateT _ Option _ = wrap x 21 | let s = () 22 | assert_eq (eval_state_t (mx >>= wrap) s) (eval_state_t mx s) 23 | 24 | let associativity ?mo mx f g : [Monad m] -> [Show (m a)] -> [Eq (m a)] -> m a -> _ -> _ -> _ = \_ -> 25 | let mx : StateT _ m _ = wrap_monad mx 26 | let s = () 27 | assert_eq (eval_state_t ((mx >>= f) >>= g) s) (eval_state_t (mx >>= (\x -> f x >>= g)) s) 28 | 29 | group "statet" [ 30 | // should this be moved to std.monad? 31 | group "StateT s m is monadic" [ 32 | test "left identity" <| left_identity 324 (\x -> wrap <| x + 89), 33 | test "right identity" <| right_identity "hello", 34 | test "associativity" <| associativity (Some 5) (\x -> wrap (x+5)) (\x -> wrap (x*2)), 35 | ], 36 | group "StateT s m has state effects" [ 37 | test "modify exec_state_t" <| \_ -> (assert_eq (exec_state_t (modify (\x -> x + 2) *> modify (\x -> x * 4)) 0) <| Some 8), 38 | test "modify eval_state_t" <| \_ -> (assert_eq (eval_state_t (modify (\x -> x + 2) *> get) 0) <| Some 2), 39 | test "put get eval_state_t" <| \_ -> (assert_eq (eval_state_t (put "hello" *> get) "") <| Some "hello"), 40 | #[derive(Eq, Show)] 41 | type StateOut s a = { value : a, state : s } 42 | test "put get run_state_t" <| \_ -> (assert_eq (run_state_t (put "hello" *> get) "") <| Some {value = "hello", state = "hello"}), 43 | test "gets eval_state_t" <| \_ -> (assert_eq (eval_state_t (gets <| (::) 1) (Cons 2 (Cons 3 Nil))) <| Some (1 :: 2 :: 3 :: Nil)), 44 | ], 45 | ] 46 | -------------------------------------------------------------------------------- /tests/pass/stream.glu: -------------------------------------------------------------------------------- 1 | let prelude = import! std.prelude 2 | let { (<|) } = import! std.function 3 | let { run, Test, assert_eq, test, group, ? } = import! std.test 4 | let { Applicative, (*>) } = import! std.applicative 5 | let int = import! std.int 6 | let stream @ { Stream, ? } = import! std.stream 7 | let list @ { List, ? } = import! std.list 8 | let { Option } = import! std.option 9 | let { foldl } = import! std.foldable 10 | 11 | let s = stream.from (\i -> if i < 5 then Some i else None) 12 | 13 | group "stream" [ 14 | test "fold" <| \_ -> (assert_eq (foldl (+) 0 s) 10), 15 | test "from" <| \_ -> (assert_eq s (stream.of [0, 1, 2, 3, 4])), 16 | test "map" <| \_ -> (assert_eq (stream.functor.map (\x -> x + x) s) (stream.of [0, 2, 4, 6, 8])), 17 | test "zip_with" <| \_ -> (assert_eq (stream.zip_with (+) s s) (stream.of [0, 2, 4, 6, 8])) 18 | ] 19 | -------------------------------------------------------------------------------- /tests/pass/string.glu: -------------------------------------------------------------------------------- 1 | let { run, Test, assert_eq, test, group ? } = import! std.test 2 | let { (<|) } = import! std.function 3 | let { Applicative, (*>), ? } = import! std.applicative 4 | 5 | let string = import! std.string 6 | let { Result, ? } = import! std.result 7 | let { ? } = import! std.unit 8 | 9 | let { ? } = import! std.effect 10 | 11 | let slice_tests = 12 | test "slice" <| \_ -> (assert_eq (string.slice "ab" 0 1) "a" *> assert_eq (string.slice "ab" 1 2) "b" 13 | *> assert_eq (string.slice "abcd" 2 4) "cd") 14 | 15 | let append_tests = 16 | let { (<>) } = import! std.prelude 17 | test "append" <| \_ -> (assert_eq ("ab" <> "cd") "abcd" *> assert_eq ("ab" <> "") "ab" *> assert_eq ("" <> "cd") "cd" 18 | *> assert_eq ("" <> "") "") 19 | 20 | let find_tests = 21 | test "find" <| \_ -> (assert_eq (string.find "abcd1234" "ab") (Some 0) 22 | *> assert_eq (string.find "abcd1234" "b") (Some 1) 23 | *> assert_eq (string.find "abcd1234" "4") (Some 7) 24 | *> assert_eq (string.find "abcd1234" "xyz") None 25 | *> assert_eq (string.rfind "abcdabcd" "b") (Some 5) 26 | *> assert_eq (string.rfind "abcdabcd" "d") (Some 7) 27 | *> assert_eq (string.rfind "abcd1234" "xyz") None) 28 | 29 | let starts_ends_tests = 30 | test "starts_ends_tests" <| \_ -> (assert_eq (string.starts_with "abcd1234" "ab") True 31 | *> assert_eq (string.starts_with "abcd1234" "b") False 32 | *> assert_eq (string.ends_with "abcd1234" "1234") True 33 | *> assert_eq (string.ends_with "abcd1234" "4") True 34 | *> assert_eq (string.ends_with "abcd1234" "ab") False) 35 | 36 | let trim_tests = 37 | test "trim" <| \_ -> (assert_eq (string.trim "ab") "ab" *> assert_eq (string.trim " ab ") "ab" 38 | *> assert_eq (string.trim "ab \t") "ab" 39 | *> assert_eq (string.trim "\t ab") "ab" 40 | *> assert_eq (string.trim_start " ab ") "ab " 41 | *> assert_eq (string.trim_end " ab ") " ab") 42 | 43 | let from_utf8_tests = 44 | test "from_utf8_tests" <| \_ -> (assert_eq (string.from_utf8 []) (Ok "") *> assert_eq (string.from_utf8 [32b]) (Ok " ") 45 | *> assert_eq (string.from_utf8 [195b, 165b, 195b, 164b, 195b, 182b]) (Ok "åäö") 46 | *> assert_eq (string.from_utf8 [195b, 165b, 195b, 164b, 195b]) (Err ()) 47 | *> assert_eq (string.from_utf8 [195b, 165b, 195b, 195b, 182b]) (Err ())) 48 | 49 | group "string" [append_tests, find_tests, starts_ends_tests, trim_tests, from_utf8_tests] 50 | -------------------------------------------------------------------------------- /tests/pass/thread.glu: -------------------------------------------------------------------------------- 1 | let { run, assert_eq, test, ? } = import! std.test 2 | let { lift } = import! std.effect.lift 3 | let { (<|) } = import! std.function 4 | let prelude = import! std.prelude 5 | let { Bool } = import! std.bool 6 | let int = import! std.int 7 | let { ? } = import! std.io 8 | let result @ { Result, ? } = import! std.result 9 | let string = import! std.string 10 | let unit @ { ? } = import! std.unit 11 | let { Applicative, wrap, (*>) } = import! std.applicative 12 | let { flat_map } = import! std.monad 13 | let { send, recv, channel } = import! std.channel 14 | let { spawn, yield, resume } = import! std.thread 15 | 16 | let { ? } = import! std.effect 17 | 18 | let assert_any_err = 19 | assert_eq ?(result.show ?string.show ?unit.show) 20 | ?(result.eq ?{ (==) = \x y -> True } ?unit.eq) 21 | 22 | 23 | let assert_recv channel expect : [Eq a] -> [Show a] -> _ -> Result () a -> _ = 24 | do x = lift <| recv channel 25 | assert_eq x expect 26 | 27 | test "thread" <| \_ -> 28 | do { sender, receiver } = lift <| channel 0 29 | do thread = lift <| spawn ( 30 | seq send sender 0 31 | let _ = yield () 32 | seq send sender 1 33 | wrap () 34 | ) 35 | seq lift <| resume thread 36 | 37 | seq assert_recv receiver (Ok 0) 38 | 39 | seq assert_recv receiver (Err ()) 40 | seq lift <| resume thread 41 | seq assert_recv receiver (Ok 1) 42 | 43 | seq assert_recv receiver (Err ()) 44 | do x = lift <| resume thread 45 | assert_any_err x (Err "Any error message here") 46 | -------------------------------------------------------------------------------- /tests/pass/unwrap.glu: -------------------------------------------------------------------------------- 1 | let { (|>) } = import! std.function 2 | let { (<|) } = import! std.function 3 | let option @ { Option } = import! std.option 4 | let result @ { Result } = import! std.result 5 | let { assert_eq, test, group } = import! std.test 6 | 7 | group "unwrap" [ 8 | test "one" <| \_ -> ( 9 | let one = Some 1 |> option.unwrap 10 | assert_eq one 1 11 | ), 12 | 13 | test "two" <| \_ -> ( 14 | let two = Ok 2 |> result.unwrap_ok 15 | assert_eq two 2 16 | ), 17 | 18 | test "three" <| \_ -> ( 19 | let three = Err 3 |> result.unwrap_err 20 | assert_eq three 3 21 | ) 22 | ] 23 | -------------------------------------------------------------------------------- /tests/pass/writer.glu: -------------------------------------------------------------------------------- 1 | let prelude = import! std.prelude 2 | let { (<|) } = import! std.function 3 | let list @ { ? } = import! std.list 4 | let { Writer, ? } = import! std.writer 5 | let { Test, run_raw, assert, assert_eq, test, ? } = import! std.test 6 | let { Applicative, (*>), ? } = import! std.applicative 7 | let { count } = import! std.foldable 8 | 9 | let { run_pure, ? } = import! std.effect 10 | 11 | let failed_tests = 12 | run_pure <| run_raw ( 13 | assert_eq 1 1 14 | *> assert_eq 1 2 15 | *> assert_eq 1 1 16 | *> assert_eq 1.0 10.0 17 | ) 18 | 19 | test "writer" <| \_ -> (assert_eq (count failed_tests) 2) 20 | -------------------------------------------------------------------------------- /tests/row_polymorphism.rs: -------------------------------------------------------------------------------- 1 | use gluon::{ 2 | vm::api::{FunctionRef, Hole, OpaqueValue}, 3 | Thread, ThreadExt, 4 | }; 5 | 6 | use crate::support::make_vm; 7 | 8 | #[macro_use] 9 | mod support; 10 | 11 | test_expr! { polymorphic_field_access, 12 | r#" 13 | let f record = record.x 14 | f { y = 1, x = 123 } 15 | "#, 16 | 123 17 | } 18 | 19 | test_expr! { polymorphic_record_unpack, 20 | r#" 21 | let f record = 22 | let { x, y } = record 23 | x #Int- y 24 | f { y = 1, z = 0, x = 123 } 25 | "#, 26 | 122 27 | } 28 | 29 | #[test] 30 | fn polymorphic_record_access_from_child_thread() { 31 | let _ = ::env_logger::try_init(); 32 | let vm = make_vm(); 33 | let child = vm.new_thread().unwrap(); 34 | 35 | vm.run_expr::>("", "import! std.function") 36 | .unwrap(); 37 | 38 | let result = child.run_expr:: i32>>( 39 | "test", 40 | r#" 41 | let function = import! std.function 42 | let f r = r.id in 43 | f function 44 | "#, 45 | ); 46 | assert!(result.is_ok(), "{}", result.err().unwrap()); 47 | assert_eq!(result.unwrap().0.call(123), Ok(123)); 48 | } 49 | 50 | // FIXME Add this test back when order no longer matters for fields 51 | // test_expr! { prelude different_order_on_fields, 52 | // r#" 53 | // let x = 54 | // if False then 55 | // { x = 1, y = "a" } 56 | // else 57 | // { y = "b", x = 2 } 58 | // x.y 59 | // "#, 60 | // String::from("a") 61 | // } 62 | // 63 | -------------------------------------------------------------------------------- /tests/safety.rs: -------------------------------------------------------------------------------- 1 | mod support; 2 | 3 | use gluon::{ 4 | vm::{ 5 | api::{FunctionRef, OpaqueValue, IO}, 6 | reference::Reference, 7 | }, 8 | RootedThread, Thread, ThreadExt, 9 | }; 10 | 11 | use crate::support::*; 12 | 13 | fn verify_value_cloned(from: &Thread, to: &Thread) { 14 | from.get_database_mut().run_io(true); 15 | to.get_database_mut().run_io(true); 16 | 17 | from.run_expr::<()>("load", r#"let _ = import! std.reference in () "#) 18 | .unwrap_or_else(|err| panic!("{}", err)); 19 | to.run_expr::<()>("load", r#"let _ = import! std.reference in () "#) 20 | .unwrap_or_else(|err| panic!("{}", err)); 21 | 22 | let expr = r#" 23 | let { ref } = import! std.reference 24 | ref 0 25 | "#; 26 | 27 | let value: OpaqueValue<_, _> = from 28 | .run_expr::>>>("example", expr) 29 | .and_then(|(io, _)| Result::from(io).map_err(From::from)) 30 | .unwrap_or_else(|err| panic!("{}", err)); 31 | 32 | // Load the prelude 33 | type Fn<'t> = FunctionRef<'t, fn(OpaqueValue>) -> IO<()>>; 34 | let store_expr = r#" 35 | let { (<-) } = import! std.reference 36 | \r -> r <- 1 37 | "#; 38 | let (mut store_1, _) = to 39 | .run_expr::("store_1", store_expr) 40 | .unwrap_or_else(|err| panic!("{}", err)); 41 | assert_eq!(store_1.call(value.clone()), Ok(IO::Value(()))); 42 | 43 | let mut load: FunctionRef>) -> IO> = 44 | from.get_global("std.reference.load").unwrap(); 45 | assert_eq!(load.call(value), Ok(IO::Value(0))); 46 | } 47 | 48 | #[test] 49 | fn cant_transfer_opaque_value_between_sibling_threads() { 50 | let _ = ::env_logger::try_init(); 51 | // vm 52 | // / \ 53 | // vm1 vm2 54 | // Passing a value directly from vm1 to vm2 requires the value to be cloned in its entirety 55 | let vm = make_vm(); 56 | let vm1 = vm.new_thread().unwrap(); 57 | let vm2 = vm.new_thread().unwrap(); 58 | verify_value_cloned(&vm1, &vm2); 59 | } 60 | 61 | #[test] 62 | fn cant_transfer_opaque_value_between_disjoint_threads() { 63 | let _ = ::env_logger::try_init(); 64 | // vm1 vm2 65 | // Passing a value directly from vm1 to vm2 requires the value to be cloned in its entirety 66 | // since the vms do not share the same global vm 67 | let vm1 = make_vm(); 68 | let vm2 = make_vm(); 69 | verify_value_cloned(&vm1, &vm2); 70 | } 71 | -------------------------------------------------------------------------------- /tests/skeptic-template.md: -------------------------------------------------------------------------------- 1 | 2 | ```rust,skeptic-root-template 3 | extern crate env_logger; 4 | extern crate gluon; 5 | ``` 6 | -------------------------------------------------------------------------------- /tests/skeptic-tests.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "little-skeptic")] 2 | include!(concat!(env!("OUT_DIR"), "/skeptic-tests.rs")); 3 | -------------------------------------------------------------------------------- /tests/snapshots/ui__macro_error_with_line_column_info.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/ui.rs 3 | expression: result.unwrap_err().emit_string().unwrap() 4 | --- 5 | error: Could not find module 'undefined'. Searched `.`. 6 | ┌─ test:1:1 7 | │ 8 | 1 │ import! undefined 9 | │ ^^^^^^^^^^^^^^^^^ 10 | 11 | 12 | -------------------------------------------------------------------------------- /tests/tutorial.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate gluon_vm; 3 | 4 | use gluon::base::types::Type; 5 | use gluon::import::{add_extern_module, Import}; 6 | use gluon::vm; 7 | use gluon::vm::api::{FunctionRef, Hole, OpaqueValue}; 8 | use gluon::{RootedThread, Thread, ThreadExt}; 9 | 10 | fn new_vm() -> RootedThread { 11 | let vm = ::gluon::new_vm(); 12 | let import = vm.get_macros().get("import"); 13 | import 14 | .as_ref() 15 | .and_then(|import| import.downcast_ref::()) 16 | .expect("Import macro") 17 | .add_path(".."); 18 | vm 19 | } 20 | 21 | #[test] 22 | fn access_field_through_alias() { 23 | let _ = ::env_logger::try_init(); 24 | let vm = new_vm(); 25 | vm.run_expr::>("example", r#" import! std.int "#) 26 | .unwrap(); 27 | let mut add: FunctionRef i32> = vm 28 | .get_global("std.int.num.(+)") 29 | .unwrap_or_else(|err| panic!("{}", err)); 30 | let result = add.call(1, 2); 31 | assert_eq!(result, Ok(3)); 32 | } 33 | 34 | #[test] 35 | fn call_rust_from_gluon() { 36 | let _ = ::env_logger::try_init(); 37 | 38 | fn factorial(x: i32) -> i32 { 39 | if x <= 1 { 40 | 1 41 | } else { 42 | x * factorial(x - 1) 43 | } 44 | } 45 | 46 | fn load_factorial(vm: &Thread) -> vm::Result { 47 | vm::ExternModule::new(vm, primitive!(1, factorial)) 48 | } 49 | 50 | let vm = new_vm(); 51 | 52 | // Introduce a module that can be loaded with `import! factorial` 53 | add_extern_module(&vm, "factorial", load_factorial); 54 | 55 | let expr = r#" 56 | let factorial = import! factorial 57 | factorial 5 58 | "#; 59 | 60 | let (result, _) = vm.run_expr::("factorial", expr).unwrap(); 61 | 62 | assert_eq!(result, 120); 63 | } 64 | 65 | #[test] 66 | fn use_string_module() { 67 | let _ = ::env_logger::try_init(); 68 | 69 | let vm = new_vm(); 70 | let result = vm 71 | .run_expr::( 72 | "example", 73 | " let string = import! \"std/string.glu\" in string.trim \" \ 74 | Hello world \t\" ", 75 | ) 76 | .unwrap(); 77 | let expected = ("Hello world".to_string(), Type::string()); 78 | 79 | assert_eq!(result, expected); 80 | } 81 | -------------------------------------------------------------------------------- /tests/ui.rs: -------------------------------------------------------------------------------- 1 | use gluon::{new_vm, ThreadExt}; 2 | 3 | #[test] 4 | fn macro_error_with_line_column_info() { 5 | let thread = new_vm(); 6 | let result = thread.run_expr::<()>("test", "import! undefined"); 7 | insta::assert_snapshot!(result.unwrap_err().emit_string().unwrap()); 8 | } 9 | -------------------------------------------------------------------------------- /tests/unrelated_type_error.glu: -------------------------------------------------------------------------------- 1 | let f x = x + 1 2 | "" + 1 3 | { f } 4 | -------------------------------------------------------------------------------- /vm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gluon_vm" 3 | version = "0.18.2" # GLUON 4 | authors = ["Markus "] 5 | edition = "2018" 6 | build = "build.rs" 7 | 8 | license = "MIT" 9 | 10 | description = "The virtual machine for executing the gluon programming language" 11 | 12 | homepage = "https://gluon-lang.org" 13 | repository = "https://github.com/gluon-lang/gluon" 14 | documentation = "https://docs.rs/gluon" 15 | 16 | [dependencies] 17 | async-trait = "0.1.73" 18 | bitflags = "1.3.2" 19 | codespan = "0.11.1" 20 | codespan-reporting = "0.11.1" 21 | collect-mac = "0.1.0" 22 | downcast-rs = "1.2.0" 23 | difference = { version = "2.0.0", optional = true } 24 | crossbeam-utils = "0.8.16" 25 | frunk_core = "0.4.2" 26 | futures = { version = "0.3.28", features = ["compat", "async-await"] } 27 | itertools = "0.10.5" 28 | lalrpop-util = { version = "0.19.12", optional = true } 29 | log = "0.4.20" 30 | ordered-float = "2.10.0" 31 | parking_lot = "0.11.2" 32 | petgraph = "0.6.4" 33 | pretty = "0.10.0" 34 | quick-error = "2.0.1" 35 | regex = { version = "1.9.4", optional = true } 36 | smallvec = "1.11.0" 37 | slab = "0.4.9" 38 | typed-arena = "2.0.2" 39 | 40 | serde = { version = "1.0.188", optional = true } 41 | serde_json = { version = "1.0.105", optional = true } 42 | serde_state = { version = "0.4.8", optional = true } 43 | serde_derive = { version = "1.0.188", optional = true } 44 | serde_derive_state = { version = "0.4.10", optional = true } 45 | 46 | gluon_base = { path = "../base", version = "0.18.2" } # GLUON 47 | gluon_check = { path = "../check", version = "0.18.2" } # GLUON 48 | gluon_codegen = { path = "../codegen", version = "0.18.2" } # GLUON 49 | gluon_parser = { path = "../parser", version = "0.18.2", optional = true } # GLUON 50 | 51 | [build-dependencies] 52 | lalrpop = { version = "0.19.12", features = ["lexer"], optional = true } 53 | 54 | [dev-dependencies] 55 | difference = "2.0.0" 56 | env_logger = "0.9.3" 57 | pretty_assertions = "1.4.0" 58 | 59 | # HACK Trick crates.io into letting letting this be published with a dependency on gluon 60 | # (which requires gluon_vm to be published) 61 | gluon = { path = "..", version = ">=0.9" } 62 | 63 | lalrpop-util = "0.19.12" 64 | regex = "1.9.4" 65 | serde_json = "1.0.105" 66 | tokio = { version = "1.32.0", features = ["macros"] } 67 | 68 | gluon_parser = { path = "../parser", version = "0.18.2" } # GLUON 69 | 70 | [features] 71 | serialization = ["serde", "serde_state", "serde_derive", "serde_derive_state", "serde_json", "gluon_base/serialization", "codespan/serialization"] 72 | test = ["difference", "lalrpop", "lalrpop-util", "regex", "serialization", "gluon_parser"] 73 | docs_rs = ["serialization"] 74 | 75 | [package.metadata.docs.rs] 76 | features = ["docs_rs"] 77 | -------------------------------------------------------------------------------- /vm/build.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "test")] 2 | mod build { 3 | extern crate lalrpop; 4 | 5 | pub fn main() { 6 | lalrpop::Configuration::new() 7 | .use_cargo_dir_conventions() 8 | .process_file("src/core/grammar.lalrpop") 9 | .unwrap(); 10 | 11 | println!("cargo:rerun-if-changed=src/core/grammar.lalrpop"); 12 | } 13 | } 14 | 15 | #[cfg(not(feature = "test"))] 16 | mod build { 17 | pub fn main() {} 18 | } 19 | 20 | fn main() { 21 | build::main(); 22 | } 23 | -------------------------------------------------------------------------------- /vm/src/debug.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | api::{generic::A, Generic, OpaqueRef}, 3 | thread::Thread, 4 | value::ValueRepr, 5 | ExternModule, Result, 6 | }; 7 | 8 | fn trace(a: Generic) { 9 | println!("{:?}", a); 10 | } 11 | 12 | fn show(a: Generic) -> String { 13 | format!("{:?}", a) 14 | } 15 | 16 | fn tag(a: OpaqueRef) -> Option { 17 | match a.get_value().get_repr() { 18 | ValueRepr::Data(data) => data.poly_tag().map(|s| s.to_string()), 19 | _ => None, 20 | } 21 | } 22 | 23 | mod std { 24 | pub use crate::debug; 25 | } 26 | 27 | pub fn load(vm: &Thread) -> Result { 28 | ExternModule::new( 29 | vm, 30 | record! { 31 | trace => primitive!(1, std::debug::trace), 32 | show => primitive!(1, std::debug::show), 33 | tag => primitive!(1, std::debug::tag) 34 | }, 35 | ) 36 | } 37 | -------------------------------------------------------------------------------- /vm/src/dynamic.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | 3 | use crate::base::resolve; 4 | use crate::base::types::{ArcType, NullInterner, Type}; 5 | 6 | use crate::thread::{RootedValue, Thread, VmRoot, VmRootInternal}; 7 | 8 | #[derive(Debug)] 9 | pub struct FieldIter<'a, T> 10 | where 11 | T: VmRootInternal, 12 | { 13 | value: &'a RootedValue, 14 | index: usize, 15 | resolved_type: Cow<'a, ArcType>, 16 | } 17 | 18 | impl<'a, T> Iterator for FieldIter<'a, T> 19 | where 20 | T: VmRoot<'a>, 21 | { 22 | type Item = (RootedValue, ArcType); 23 | 24 | fn next(&mut self) -> Option { 25 | match **self.resolved_type { 26 | Type::Record(ref row) => match **row { 27 | Type::ExtendRow { ref fields, .. } => { 28 | let index = self.index; 29 | self.index += 1; 30 | self.value 31 | .get(index) 32 | .map(|value| (value, fields[index].typ.clone())) 33 | } 34 | _ => None, 35 | }, 36 | _ => None, 37 | } 38 | } 39 | } 40 | 41 | pub fn field_iter<'vm, T>( 42 | value: &'vm RootedValue, 43 | typ: &'vm ArcType, 44 | thread: &Thread, 45 | ) -> FieldIter<'vm, T> 46 | where 47 | T: VmRoot<'vm>, 48 | { 49 | FieldIter { 50 | value: value, 51 | index: 0, 52 | resolved_type: resolve::remove_aliases_cow(&thread.get_env(), &mut NullInterner, typ), 53 | } 54 | } 55 | --------------------------------------------------------------------------------