├── .github └── workflows │ └── deploy.yml ├── .rustfmt.toml ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── examples ├── ffi │ ├── config.lm │ └── src │ │ ├── main.c │ │ ├── main.lm │ │ └── types.h ├── file-printer │ ├── config.lm │ └── src │ │ └── main.lm ├── fizz-buzz │ ├── config.lm │ └── src │ │ └── main.lm ├── hello-world │ ├── config.lm │ └── src │ │ └── main.lm ├── lists │ ├── config.lm │ └── src │ │ └── main.lm ├── maybe-implementation │ ├── config.lm │ └── src │ │ └── main.lm ├── modules │ ├── config.lm │ └── src │ │ ├── main.lm │ │ ├── other_dir │ │ ├── file.lm │ │ └── lib.lm │ │ └── other_file.lm ├── operators │ ├── config.lm │ └── src │ │ └── main.lm ├── raw-function-pointers │ ├── config.lm │ └── src │ │ └── main.lm ├── records │ ├── config.lm │ └── src │ │ └── main.lm ├── tuples │ ├── config.lm │ └── src │ │ └── main.lm └── using-ext-library │ ├── config.lm │ └── src │ └── main.lm ├── lumina-collections ├── Cargo.toml └── src │ ├── kindmap.rs │ ├── lib.rs │ ├── map.rs │ ├── modmap.rs │ ├── ro.rs │ └── tests.rs ├── lumina-compiler ├── Cargo.toml └── src │ ├── ast │ ├── attr.rs │ ├── collect.rs │ ├── config.rs │ ├── entities.rs │ ├── mod.rs │ ├── resolve.rs │ └── sources.rs │ ├── backend │ ├── cranelift │ │ ├── debuginfo │ │ │ ├── mod.rs │ │ │ └── unwind.rs │ │ ├── layout.rs │ │ ├── mod.rs │ │ └── ssa │ │ │ ├── array.rs │ │ │ ├── call.rs │ │ │ ├── layout_mapping.rs │ │ │ ├── mod.rs │ │ │ ├── num.rs │ │ │ ├── pointer.rs │ │ │ ├── record.rs │ │ │ └── sum.rs │ └── mod.rs │ ├── debuginfo │ ├── emit.rs │ └── mod.rs │ ├── fmt.rs │ ├── hir │ ├── expr.rs │ ├── mod.rs │ ├── pat.rs │ ├── scope.rs │ └── ty.rs │ ├── lib.rs │ ├── lir │ ├── debug.rs │ ├── dyn_dispatch.rs │ ├── expr.rs │ ├── mod.rs │ ├── mono.rs │ ├── pat.rs │ ├── reflect.rs │ └── ssa │ │ ├── mod.rs │ │ ├── opts.rs │ │ ├── rewrite.rs │ │ └── snapshots │ │ ├── lumina_compiler__lir__ssa__opts__tests__functions.snap │ │ ├── lumina_compiler__lir__ssa__opts__tests__inline_block.snap │ │ ├── lumina_compiler__lir__ssa__opts__tests__practical.snap │ │ └── lumina_compiler__lir__ssa__opts__tests__tricky.snap │ ├── mir │ ├── builtins.rs │ ├── expr.rs │ ├── func.rs │ ├── lower │ │ ├── expr.rs │ │ ├── mod.rs │ │ └── pat │ │ │ ├── init.rs │ │ │ ├── merge.rs │ │ │ ├── missing.rs │ │ │ ├── mod.rs │ │ │ ├── range.rs │ │ │ ├── snapshots │ │ │ ├── lumina_compiler__mir__lower__pat__tests__bind_inside_tuple_nested.snap │ │ │ ├── lumina_compiler__mir__lower__pat__tests__instant_wildcard-2.snap │ │ │ ├── lumina_compiler__mir__lower__pat__tests__instant_wildcard.snap │ │ │ ├── lumina_compiler__mir__lower__pat__tests__int_matrix.snap │ │ │ ├── lumina_compiler__mir__lower__pat__tests__just_x_nothing.snap │ │ │ ├── lumina_compiler__mir__lower__pat__tests__list_x_xs.snap │ │ │ ├── lumina_compiler__mir__lower__pat__tests__missing_maybes.snap │ │ │ └── lumina_compiler__mir__lower__pat__tests__nested_lists.snap │ │ │ └── tests.rs │ ├── mod.rs │ ├── patc.rs │ ├── tcheck.rs │ └── tyfmt.rs │ ├── prelude.rs │ └── target.rs ├── lumina-gc ├── Cargo.lock ├── Cargo.toml ├── build ├── src.old │ ├── active_plan.rs │ ├── api.rs │ ├── collection.rs │ ├── lib.rs │ ├── object_model.rs │ ├── reference_glue.rs │ └── scanning.rs └── src │ ├── active_plan.rs │ ├── api.rs │ ├── collection.rs │ ├── lib.rs │ ├── object_model.rs │ ├── reference_glue.rs │ └── scanning.rs ├── lumina-key ├── Cargo.toml └── src │ └── lib.rs ├── lumina-parser ├── Cargo.toml └── src │ ├── alias.rs │ ├── error.rs │ ├── expr.rs │ ├── func.rs │ ├── impl.rs │ ├── lexer.rs │ ├── lib.rs │ ├── pat.rs │ ├── recover.rs │ ├── shared.rs │ ├── snapshots │ ├── lumina_parser__tests__decl_alias.snap │ ├── lumina_parser__tests__decl_from_trait.snap │ ├── lumina_parser__tests__decl_from_trait_optional_eq.snap │ ├── lumina_parser__tests__decl_impl_into.snap │ ├── lumina_parser__tests__decl_iterator.snap │ ├── lumina_parser__tests__decl_map.snap │ ├── lumina_parser__tests__decl_option.snap │ ├── lumina_parser__tests__decl_param_span.snap │ ├── lumina_parser__tests__decl_point_multi_line.snap │ ├── lumina_parser__tests__decl_point_multi_line_optional_comma.snap │ ├── lumina_parser__tests__decl_point_single_line.snap │ ├── lumina_parser__tests__decl_use_assign.snap │ ├── lumina_parser__tests__decl_use_assign_then_expose.snap │ ├── lumina_parser__tests__decl_use_expose.snap │ ├── lumina_parser__tests__decl_use_simple.snap │ ├── lumina_parser__tests__decl_use_with_annotation.snap │ ├── lumina_parser__tests__decl_val.snap │ ├── lumina_parser__tests__decl_val_annotated.snap │ ├── lumina_parser__tests__decl_when_multi_line.snap │ ├── lumina_parser__tests__decl_when_multi_line_optional_comma.snap │ ├── lumina_parser__tests__decl_when_single_line.snap │ ├── lumina_parser__tests__expr_complex_lambda.snap │ ├── lumina_parser__tests__expr_complex_lambda.snap.working │ ├── lumina_parser__tests__expr_deep_set.snap │ ├── lumina_parser__tests__expr_deep_update.snap │ ├── lumina_parser__tests__expr_field_of_access.snap │ ├── lumina_parser__tests__expr_if.snap │ ├── lumina_parser__tests__expr_match.snap │ ├── lumina_parser__tests__expr_match_nested.snap │ ├── lumina_parser__tests__expr_multiple_accessors.snap │ ├── lumina_parser__tests__expr_multiple_accessors_as_param.snap │ ├── lumina_parser__tests__expr_nested_constr.snap │ ├── lumina_parser__tests__expr_nested_list.snap │ ├── lumina_parser__tests__expr_nested_tuple.snap │ ├── lumina_parser__tests__expr_pass_expr.snap │ ├── lumina_parser__tests__expr_pass_fn.snap │ ├── lumina_parser__tests__expr_pass_lambda.snap │ ├── lumina_parser__tests__expr_pass_opnum.snap │ ├── lumina_parser__tests__expr_pass_partial.snap │ ├── lumina_parser__tests__expr_pipe_eval_of_fn.snap │ ├── lumina_parser__tests__expr_pipe_eval_of_lambda.snap │ ├── lumina_parser__tests__expr_pipe_field.snap │ ├── lumina_parser__tests__expr_pipe_fn.snap │ ├── lumina_parser__tests__expr_pipe_lambda.snap │ ├── lumina_parser__tests__expr_pipe_left.snap │ ├── lumina_parser__tests__expr_pipes.snap │ ├── lumina_parser__tests__expr_simple_annotated_record.snap │ ├── lumina_parser__tests__expr_simple_constr.snap │ ├── lumina_parser__tests__expr_simple_infered_record.snap │ ├── lumina_parser__tests__expr_simple_lambda.snap │ ├── lumina_parser__tests__expr_simple_list.snap │ ├── lumina_parser__tests__expr_simple_punned_record.snap │ ├── lumina_parser__tests__expr_string_literal.snap │ ├── lumina_parser__tests__expr_type_annotated_call.snap │ ├── lumina_parser__tests__pat_advanced_strings.snap │ ├── lumina_parser__tests__pat_int_ranges.snap │ ├── lumina_parser__tests__pat_nested_constr.snap │ ├── lumina_parser__tests__pat_nested_list.snap │ ├── lumina_parser__tests__pat_nested_tuple.snap │ ├── lumina_parser__tests__pat_simple_annotated_record.snap │ ├── lumina_parser__tests__pat_simple_constr.snap │ ├── lumina_parser__tests__pat_simple_infered_record.snap │ ├── lumina_parser__tests__pat_simple_list.snap │ ├── lumina_parser__tests__pat_simple_punned_record.snap │ ├── lumina_parser__tests__pat_string_extractors.snap │ ├── lumina_parser__tests__pat_string_literal.snap │ ├── lumina_parser__tests__pat_string_start_with_extractor.snap │ ├── lumina_parser__tests__type_annotated_trait.snap │ ├── lumina_parser__tests__type_bool.snap │ ├── lumina_parser__tests__type_floats.snap │ ├── lumina_parser__tests__type_fnptr.snap │ ├── lumina_parser__tests__type_ints.snap │ ├── lumina_parser__tests__type_nested_higher_order.snap │ ├── lumina_parser__tests__type_nested_tuple.snap │ ├── lumina_parser__tests__type_ptr.snap │ ├── lumina_parser__tests__type_simple_closure.snap │ ├── lumina_parser__tests__type_simple_closure_ret_sugar.snap │ ├── lumina_parser__tests__type_simple_defined.snap │ └── lumina_parser__tests__type_simple_tuple.snap │ ├── tests.rs │ ├── ty.rs │ ├── use.rs │ ├── val.rs │ └── when.rs ├── lumina-rvsdg ├── Cargo.toml └── src │ ├── lib.rs │ ├── region.rs │ ├── tests.rs │ └── verifier.rs ├── lumina-tokentree ├── Cargo.toml └── src │ ├── block.rs │ ├── format.rs │ ├── lexer.rs │ ├── lib.rs │ ├── meta.rs │ ├── operator.rs │ ├── regroup.rs │ ├── snapshots │ ├── lumina_tokentree__tests__nested_match.snap │ └── lumina_tokentree__tests__precedence.snap │ └── tests.rs ├── lumina-typesystem ├── Cargo.toml └── src │ ├── check.rs │ ├── fin.rs │ ├── generic.rs │ ├── intsize.rs │ ├── iquery.rs │ ├── lib.rs │ ├── tenv.rs │ ├── tests.rs │ └── transform.rs ├── lumina-util ├── Cargo.toml └── src │ ├── error.rs │ ├── helpers.rs │ ├── highlight.rs │ ├── ignored.rs │ ├── lib.rs │ ├── location.rs │ ├── span.rs │ └── test_logger.rs ├── lumina ├── Cargo.toml ├── src │ ├── build.rs │ ├── cli.rs │ ├── formatter.rs │ ├── init.rs │ ├── lib.rs │ └── main.rs └── tests │ ├── examples.rs │ ├── formatting.rs │ ├── snapshots │ ├── formatting__format_examples__ffi.snap │ ├── formatting__format_examples__fizz-buzz.snap │ ├── formatting__format_examples__hello-world.snap │ ├── formatting__format_examples__lists.snap │ ├── formatting__format_examples__maybe-implementation.snap │ ├── formatting__format_examples__modules.snap │ ├── formatting__format_examples__operators.snap │ ├── formatting__format_examples__raw-function-pointers.snap │ ├── formatting__format_examples__records.snap │ ├── formatting__format_examples__tuples.snap │ └── formatting__format_examples__using-ext-library.snap │ └── tests.rs ├── luminapath ├── ext │ ├── example_lib │ │ ├── config.lm │ │ └── src │ │ │ └── lib.lm │ └── minimal-env │ │ ├── config.lm │ │ └── src │ │ ├── lib.lm │ │ └── prelude │ │ └── lib.lm ├── std │ ├── array │ │ └── lib.lm │ ├── bool │ │ └── lib.lm │ ├── char │ │ └── lib.lm │ ├── env │ │ └── lib.lm │ ├── fs │ │ └── lib.lm │ ├── gc │ │ └── lib.lm │ ├── io │ │ └── lib.lm │ ├── libc │ │ └── lib.lm │ ├── list │ │ ├── lib.lm │ │ ├── slice.lm │ │ └── vec.lm │ ├── math │ │ ├── i16.lm │ │ ├── i32.lm │ │ ├── i64.lm │ │ ├── i8.lm │ │ ├── lib.lm │ │ ├── non_zero.lm │ │ ├── u16.lm │ │ ├── u32.lm │ │ ├── u64.lm │ │ └── u8.lm │ ├── maybe │ │ └── lib.lm │ ├── nothing │ │ └── lib.lm │ ├── prelude │ │ └── lib.lm │ ├── ptr │ │ └── lib.lm │ ├── ref │ │ └── lib.lm │ ├── result │ │ └── lib.lm │ ├── string │ │ ├── extractor.lm │ │ ├── lib.lm │ │ └── num.lm │ ├── tuple │ │ └── lib.lm │ ├── tyinfo │ │ └── lib.lm │ └── unwind │ │ ├── lib.lm │ │ └── parse.lm └── targets │ ├── bin │ ├── ld.lld │ ├── lld │ ├── lld-link │ └── wasm-ld │ ├── darwin │ └── stub │ ├── linux │ ├── gnu │ │ └── libunwind.a │ ├── musl │ │ ├── crt1.o │ │ ├── crti.o │ │ ├── crtn.o │ │ ├── libc.a │ │ ├── libcrypt.a │ │ ├── libdl.a │ │ ├── libm.a │ │ ├── libpthread.a │ │ ├── libresolv.a │ │ ├── librt.a │ │ ├── libutil.a │ │ └── libxnet.a │ ├── syscall.asm │ ├── syscall.o │ └── syscall │ │ └── stub │ └── windows │ └── stub ├── mdbook ├── book.toml ├── src │ ├── README.md │ ├── SUMMARY.md │ ├── features │ │ ├── attributes.md │ │ ├── ffi.md │ │ ├── functions.md │ │ ├── generics-and-traits.md │ │ ├── let-and-do.md │ │ ├── list-and-strings.md │ │ ├── matching.md │ │ ├── modules.md │ │ ├── nested-records.md │ │ ├── partial.md │ │ ├── pipes.md │ │ ├── pointers.md │ │ ├── types.md │ │ └── val.md │ ├── getting-started │ │ ├── compiling-and-running.md │ │ ├── create-project.md │ │ └── installation.md │ └── std │ │ ├── io.md │ │ ├── lists.md │ │ ├── std.md │ │ └── targets.md └── theme │ └── highlight.js ├── misc ├── lumina-compiler-overview.png ├── lumina-example.png └── lumina.vim └── tests ├── mem-autoboxed-struct ├── config.lm ├── expected └── src │ └── main.lm ├── mem-large-struct ├── config.lm ├── expected └── src │ └── main.lm ├── mem-large-sum ├── config.lm ├── expected └── src │ └── main.lm ├── mem-nested-combination ├── config.lm ├── expected └── src │ └── main.lm ├── mem-recursive-sum ├── config.lm ├── expected └── src │ └── main.lm ├── mem-small-struct ├── config.lm ├── expected └── src │ └── main.lm ├── mem-small-sum ├── config.lm ├── expected └── src │ └── main.lm └── mem-sum-in-struct ├── config.lm ├── expected └── src └── main.lm /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: deploy 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | build: 10 | runs-on: self-hosted 11 | 12 | steps: 13 | - name: Pulling changes 14 | working-directory: workdir/ 15 | run: | 16 | if [ -d "lumina/" ]; then 17 | cd lumina/ 18 | git pull 19 | else 20 | git clone https://github.com/${{ github.repository }}.git lumina 21 | echo "target" >> lumina/.gitignore 22 | echo "mdbook/publish" >> lumina/.gitignore 23 | fi 24 | 25 | - name: Build Documentation 26 | working-directory: workdir/lumina/mdbook 27 | run: | 28 | mdbook build -d publish/ 29 | 30 | - name: Test 31 | working-directory: workdir/lumina 32 | run: | 33 | cargo test -- --nocapture 34 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | struct_lit_width = 60 2 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "lumina", 4 | "lumina-parser", 5 | "lumina-util", 6 | "lumina-key", 7 | "lumina-compiler", 8 | "lumina-typesystem", 9 | "lumina-collections", "lumina-tokentree", "lumina-rvsdg" 10 | ] 11 | exclude = [ 12 | # Compiled separately with different flags as a staticlib 13 | "lumina-gc" 14 | ] 15 | resolver = "2" 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Example of the Lumina programming language and compiler.](misc/lumina-example.png) 2 | 3 | ## The Lumina Programming Language 4 | 5 | Lumina is an eager-by-default natively compiled functional programming language with the core goals of readibility, practicality, compiler-driven development and simplicity. 6 | 7 | It aims to be a high-level general-purpose language, but also support systems-level functionality such as raw pointer arithmetics to allow for high-level abstractions to be built on top of low-level high performance Lumina code. 8 | 9 | ## Documentation 10 | 11 | A work-in-progress guide is available at https://docs.luminalang.com/ 12 | 13 | Examples are available in the `examples/` folder 14 | 15 | ## Supported Targets 16 | 17 | * `x86_64-linux-glibc` 18 | * `x86_64-linux-musl` 19 | 20 | (many more planned) 21 | 22 | ## Compiler Developer Documentation 23 | 24 | This project is using `tracing_tree` combined with `env_logger`. To track a specific entity throughout the compilation pipeline, use: 25 | `RUST_LOG="[{entity=myFunction}]=trace"` 26 | 27 | To get an overview look of what the compiler is doing; use `RUST_LOG=info` 28 | 29 | ### Compiler Overview 30 | 31 | ![Overview of the compiler architecture](misc/lumina-compiler-overview.png) 32 | 33 | ### Status 34 | 35 | Most language features, basic and advanced, lower to LIR and can compile natively with Cranelift. 36 | Since there is no finished garbage collector, all allocations currently leak unless manually freed with `do libc:free ptr then ...`. 37 | 38 | C-representation of declared data is currently not supported. To call FFI functions you need to write in primitives manually according to the C-abi. 39 | Lumina's own ABI is currently a transparent representation of the data given. So; the C ABI can be followed manully on top. 40 | 41 | The standard library is severely lacking and doesn't have much outside of basic int, list, string, stdout, and file io. 42 | All of this is written in a non-platform-agnostic way and needs to be changed to properly utilise `@[platform "..."]` rules. 43 | 44 | ### Known remaining high-priority tasks 45 | 46 | - [ ] Garbage Collector (Linking to `mmtk` works, but I'm not sure what we'll end up doing) 47 | - [x] Make `pub` actually have an effect. 48 | - [ ] Basic guide/showcase 49 | - [x] Replace all remaining `todo!("ET: ");`s with proper error messages 50 | - [x] Give the compiler the ability to detect and use system linkers 51 | - [x] Vectorised linked lists or finger trees as default list desugaring 52 | - [x] Fix the type checker sometimes confusing the expected type with the given type and vice-versa 53 | - [ ] Basic Windows target 54 | - [ ] Basic MacOS target 55 | - [ ] Basic introductory website hosted on our domain 56 | - [x] Re-add the unreachability warning messages 57 | - [x] Strings 58 | - [ ] Basic standard library 59 | - [ ] Floats 60 | - [ ] Reflection API with const-time folding post-monomorphization (plan is to use this instead of macros) 61 | 62 | ### Known remaining lower-priority tasks 63 | 64 | - [ ] Associated types 65 | - [ ] Trait-overloaded key-value syntactic sugar for hashmaps 66 | - [ ] Higher kinded types (monomorphised) 67 | - [ ] Documentation Generator 68 | - [x] `@[repr "C"]` attributes and FFI-binding generator 69 | - [ ] Incremental compilation 70 | - [ ] Self-hosted compiler reimplementation 71 | - [ ] Stack backtraces and runtime debugging 72 | - [ ] Code formatter 73 | - [ ] Syntax files for various text editors 74 | - [ ] trait specialisation 75 | - [x] Inline smaller sum-type data payloads for increased performance 76 | - [x] Natively aligned data for improved performance 77 | - [x] Stable public function symbols 78 | - [ ] Deduplicate ReadOnlyBytes 79 | -------------------------------------------------------------------------------- /examples/ffi/config.lm: -------------------------------------------------------------------------------- 1 | val name = "ffi" 2 | val version = "1.0" 3 | val authors = [] 4 | 5 | val dependencies = [] 6 | 7 | val linker_args = ["-flto"] 8 | val linker_libs = ["src/main.c"] 9 | 10 | -------------------------------------------------------------------------------- /examples/ffi/src/main.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | 3 | struct Outer returns() { 4 | struct Outer outer; 5 | outer.a = 1; 6 | 7 | struct Inner inner; 8 | inner.a = 2; 9 | inner.b = 3; 10 | inner.c = 4; 11 | inner.d = 5; 12 | outer.b = inner; 13 | 14 | outer.c = 6; 15 | 16 | return outer; 17 | } 18 | 19 | unsigned int takes(struct Outer outer) { 20 | if (outer.a != 1) return 10; 21 | if (outer.b.a != 2) return 20; 22 | if (outer.b.b != 3) return 30; 23 | if (outer.b.c != 4) return 40; 24 | if (outer.b.d != 5) return 50; 25 | if (outer.c != 6) return 60; 26 | 27 | return 100; 28 | } 29 | -------------------------------------------------------------------------------- /examples/ffi/src/main.lm: -------------------------------------------------------------------------------- 1 | @[extern "exit", platform ["linux-gnu", "linux-musl"]] 2 | fn libc_exit as i32 -> () 3 | 4 | fn main = 5 | let outer = returns in 6 | let n = takes outer in 7 | if n == 100 8 | then libc_exit 0 9 | else libc_exit n 10 | 11 | @[repr "C"] 12 | type Inner { 13 | a u32 14 | b u8 15 | c u64 16 | d u16 17 | } 18 | 19 | @[repr "C"] 20 | type Outer { 21 | a u16 22 | b Inner 23 | c u8 24 | } 25 | 26 | @[extern "takes"] 27 | fn takes as Outer -> i32 28 | 29 | @[extern "returns"] 30 | fn returns as Outer 31 | 32 | -------------------------------------------------------------------------------- /examples/ffi/src/types.h: -------------------------------------------------------------------------------- 1 | struct Inner { 2 | unsigned int a; 3 | unsigned char b; 4 | unsigned long long c; 5 | unsigned short d; 6 | }; 7 | 8 | struct Outer { 9 | unsigned short a; 10 | struct Inner b; 11 | unsigned char c; 12 | }; 13 | 14 | 15 | struct Outer returns(); 16 | unsigned int takes(struct Outer outer); 17 | -------------------------------------------------------------------------------- /examples/file-printer/config.lm: -------------------------------------------------------------------------------- 1 | val name = "file-printer" 2 | val version = "1.0" 3 | val authors = [] 4 | 5 | val dependencies = [] 6 | 7 | -------------------------------------------------------------------------------- /examples/file-printer/src/main.lm: -------------------------------------------------------------------------------- 1 | use std:env 2 | use std:io 3 | use std:fs 4 | 5 | fn main = 6 | match env:get_args 7 | | [_, file : xs] -> 8 | fs:readFileAt file 9 | . or_crash "could not read file" 10 | . io:println 11 | 12 | | _ -> io:println "missing input filename" 13 | 14 | -------------------------------------------------------------------------------- /examples/fizz-buzz/config.lm: -------------------------------------------------------------------------------- 1 | val name = "fizz-buzz" 2 | val version = "1.0" 3 | val authors = [] 4 | 5 | val dependencies = [] 6 | 7 | -------------------------------------------------------------------------------- /examples/fizz-buzz/src/main.lm: -------------------------------------------------------------------------------- 1 | use std:io 2 | 3 | fn main = 4 | [1, 2, 3, 4, 5, 6, 7, 8, 9] 5 | . map #fizz 6 | . forEach #io:println 7 | 8 | fn fizz n as int -> string = 9 | if n % 15 == 0 then 10 | "FizzBuz" 11 | else if n % 3 == 0 then 12 | "Fizz" 13 | else if n % 5 == 0 then 14 | "Buzz" 15 | else 16 | show n 17 | 18 | -------------------------------------------------------------------------------- /examples/hello-world/config.lm: -------------------------------------------------------------------------------- 1 | val name = "hello-world" 2 | val version = "1.0" 3 | val authors = ["Simon Larsson"] 4 | 5 | val dependencies = [] 6 | 7 | -------------------------------------------------------------------------------- /examples/hello-world/src/main.lm: -------------------------------------------------------------------------------- 1 | use std:io 2 | 3 | fn main = io:println ("Hello" <> " " <> "World!") 4 | 5 | -------------------------------------------------------------------------------- /examples/lists/config.lm: -------------------------------------------------------------------------------- 1 | val name = "lists" 2 | val version = "1.0" 3 | val authors = [] 4 | 5 | val dependencies = [] 6 | 7 | -------------------------------------------------------------------------------- /examples/lists/src/main.lm: -------------------------------------------------------------------------------- 1 | use std:io 2 | use std:list 3 | 4 | fn main = 5 | (1 : [2, 3, 4] ++ [5, 6, 7, 8, 9]) 6 | . map #(* 2) 7 | . sum 8 | . io:println 9 | 10 | fn digits as [int] = list:from_range (0, 10) #(\i -> i as int) 11 | 12 | fn my_map f list as (fn a -> b) [a] -> [b] = 13 | match list 14 | | [x : xs] -> f x : my_map #f xs 15 | | [] -> [] 16 | 17 | pub fn my_fold f acc list as (fn b a -> b) b [a] -> b = 18 | match list 19 | | [x : xs] -> my_fold #f (f acc x) xs 20 | | [] -> acc 21 | 22 | -------------------------------------------------------------------------------- /examples/maybe-implementation/config.lm: -------------------------------------------------------------------------------- 1 | val name = "maybe-implementation" 2 | val version = "1.0" 3 | val authors = [] 4 | 5 | val dependencies = [] 6 | 7 | -------------------------------------------------------------------------------- /examples/maybe-implementation/src/main.lm: -------------------------------------------------------------------------------- 1 | use std:io 2 | 3 | // A simple implementation of the `Maybe` sum type 4 | type Maybe a = Just a | Nothing 5 | 6 | fn map f m as (fn a -> b) (Maybe a) -> Maybe b = 7 | match m 8 | | Just a -> Just (f a) 9 | | Nothing -> Nothing 10 | 11 | fn and f m as (fn a -> Maybe a) (Maybe a) -> Maybe a = 12 | match m 13 | | Just a -> f a 14 | | Nothing -> Nothing 15 | 16 | fn or fallback m as (fn a) (Maybe a) -> a = 17 | match m 18 | | Just a -> a 19 | | Nothing -> fallback 20 | 21 | when a can ToString 22 | impl ToString for Maybe a 23 | fn show m as self -> string = 24 | match m 25 | | Nothing -> "Nothing" 26 | | Just a -> "Just " <> show a 27 | 28 | fn main = 29 | Just 5 30 | . map #(+ 1) 31 | . and #(\n -> Just (n + 1)) 32 | . io:println 33 | 34 | -------------------------------------------------------------------------------- /examples/modules/config.lm: -------------------------------------------------------------------------------- 1 | val name = "modules" 2 | val version = "1.0" 3 | val authors = [] 4 | 5 | val dependencies = [] 6 | 7 | -------------------------------------------------------------------------------- /examples/modules/src/main.lm: -------------------------------------------------------------------------------- 1 | use other_file [hello] 2 | use other_dir [world] 3 | // using a directory will refer to the `lib.lm` file 4 | use other_dir:file [Color [..], Direction [Right, Down]] 5 | 6 | fn main = 7 | do file:print (hello <> world) 8 | then file:print (other_file:hello <> project:other_dir:world) 9 | 10 | fn allowed as (Color, Direction) = (Green, Down) 11 | 12 | // Variants of a sum type can also be access through the sum type's namespace 13 | fn blocked as (Color, Direction) = (Color:Red, Direction:Up) 14 | 15 | -------------------------------------------------------------------------------- /examples/modules/src/other_dir/file.lm: -------------------------------------------------------------------------------- 1 | pub fn print str as string -> () = 2 | std:io:println str 3 | 4 | pub type Color = Red | Green | Blue 5 | 6 | pub type Direction = Up | Down | Left | Right 7 | 8 | -------------------------------------------------------------------------------- /examples/modules/src/other_dir/lib.lm: -------------------------------------------------------------------------------- 1 | pub fn world as string = "World" 2 | 3 | -------------------------------------------------------------------------------- /examples/modules/src/other_file.lm: -------------------------------------------------------------------------------- 1 | pub fn hello as string = "Hello" 2 | 3 | -------------------------------------------------------------------------------- /examples/operators/config.lm: -------------------------------------------------------------------------------- 1 | val name = "operators" 2 | val version = "1.0" 3 | val authors = [] 4 | 5 | val dependencies = [] 6 | 7 | -------------------------------------------------------------------------------- /examples/operators/src/main.lm: -------------------------------------------------------------------------------- 1 | use std:io 2 | 3 | fn main = io:println (10 - 1 * 2 / (4 + 1) - 2 +++ 5) 4 | 5 | fn +++ x y as int int -> int = x + y + y 6 | 7 | -------------------------------------------------------------------------------- /examples/raw-function-pointers/config.lm: -------------------------------------------------------------------------------- 1 | val name = "raw-function-pointers" 2 | val version = "1.0" 3 | val authors = [] 4 | 5 | val dependencies = [] 6 | 7 | -------------------------------------------------------------------------------- /examples/raw-function-pointers/src/main.lm: -------------------------------------------------------------------------------- 1 | type Handlers { 2 | this (fnptr i32 -> u32) 3 | that (fnptr u32 -> i32) 4 | } 5 | 6 | fn main = 7 | let handlers = { this = #!this_handler, that = #!that_handler } in 8 | handlers.this 5 9 | . handlers.that 10 | . std:io:println 11 | 12 | fn this_handler n as i32 -> u32 = n as u32 13 | 14 | fn that_handler n as u32 -> i32 = n as i32 15 | 16 | -------------------------------------------------------------------------------- /examples/records/config.lm: -------------------------------------------------------------------------------- 1 | val name = "records" 2 | val version = "1.0" 3 | val authors = [] 4 | 5 | val dependencies = [] 6 | 7 | -------------------------------------------------------------------------------- /examples/records/src/main.lm: -------------------------------------------------------------------------------- 1 | use std:io 2 | 3 | fn main = simple_example 4 | 5 | // Simple Example 6 | type User { 7 | name string 8 | age int 9 | } 10 | 11 | fn simple_example = 12 | let jonas = { User | name = "Jonas", age = 15 } in 13 | let older_jonas = { jonas ~ age@n = n + 10 } 14 | in io:println (can_drive jonas, can_drive older_jonas) 15 | 16 | where 17 | fn can_drive { age } as User -> bool = age >= 18 18 | 19 | // Complex Example 20 | type Pair a { 21 | x a 22 | y a 23 | } 24 | 25 | fn modify_deeply_nested pair as Pair (Pair (Pair u8)) -> (Pair (Pair (Pair u8))) = 26 | { pair ~ x.x.x@n = n + 1, y.x.x@n = n + 2 } 27 | 28 | fn complex_example = 29 | let inner = { Pair u8 | x = 3, y = 4 } in 30 | let middle = { Pair (Pair u8) | x = inner, y = inner } in 31 | let outer = { Pair (Pair (Pair u8)) | x = middle, y = middle } in 32 | (modify_deeply_nested outer).x.x.x 33 | . io:println 34 | 35 | -------------------------------------------------------------------------------- /examples/tuples/config.lm: -------------------------------------------------------------------------------- 1 | val name = "tuples" 2 | val version = "1.0" 3 | val authors = [] 4 | 5 | val dependencies = [] 6 | 7 | -------------------------------------------------------------------------------- /examples/tuples/src/main.lm: -------------------------------------------------------------------------------- 1 | use std:io 2 | 3 | fn main = 4 | let user = ("Jonas", 25) 5 | in print_user user 6 | 7 | fn print_user (name, age) as (string, int) -> () = 8 | io:println (name <> " " <> show age) 9 | 10 | -------------------------------------------------------------------------------- /examples/using-ext-library/config.lm: -------------------------------------------------------------------------------- 1 | val name = "using-ext-library" 2 | val version = "1.0" 3 | val authors = [] 4 | 5 | val dependencies = [{ name = "example_lib" }] 6 | 7 | -------------------------------------------------------------------------------- /examples/using-ext-library/src/main.lm: -------------------------------------------------------------------------------- 1 | use std:io 2 | use ext:example_lib [text] 3 | 4 | fn main = io:println text 5 | 6 | -------------------------------------------------------------------------------- /lumina-collections/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lumina-collections" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /lumina-collections/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests; 3 | 4 | mod map; 5 | pub use map::{KeysIter, Map, MapKey}; 6 | 7 | mod ro; 8 | pub use ro::ReadOnlyTable; 9 | 10 | mod modmap; 11 | pub use modmap::{MMap, Module, M}; 12 | 13 | mod kindmap; 14 | 15 | #[macro_export] 16 | macro_rules! map_key_impl { 17 | ($ty:ident($int:ty), $name:literal) => { 18 | map_key_impl!($ty($int), |this, f| write!(f, "{}{}", $name, this.0)); 19 | }; 20 | ($ty:ident($int:ty), |$this:ident, $f:ident| $do:expr) => { 21 | impl From for $ty { 22 | fn from(n: usize) -> $ty { 23 | $ty(n as $int) 24 | } 25 | } 26 | 27 | impl Into for $ty { 28 | fn into(self) -> usize { 29 | self.0 as usize 30 | } 31 | } 32 | 33 | impl std::fmt::Display for $ty { 34 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 35 | let $this = self; 36 | let $f = f; 37 | $do 38 | } 39 | } 40 | 41 | impl std::fmt::Debug for $ty { 42 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 43 | let $this = self; 44 | let $f = f; 45 | $do 46 | } 47 | } 48 | 49 | impl $crate::MapKey for $ty {} 50 | 51 | impl $ty { 52 | #[inline(always)] 53 | #[allow(dead_code)] 54 | pub fn inside(self, module: $crate::Module) -> M<$ty> { 55 | $crate::M(module, self) 56 | } 57 | } 58 | }; 59 | } 60 | -------------------------------------------------------------------------------- /lumina-collections/src/ro.rs: -------------------------------------------------------------------------------- 1 | use super::{Map, MapKey}; 2 | use std::collections::HashMap; 3 | use std::rc::Rc; 4 | 5 | /// An de-duplicating read-only value table 6 | pub struct ReadOnlyTable { 7 | bytes: Map>>, 8 | query: HashMap>, K>, 9 | } 10 | 11 | impl ReadOnlyTable { 12 | pub fn new() -> Self { 13 | Self { bytes: Map::new(), query: HashMap::new() } 14 | } 15 | 16 | pub fn push_bytes(&mut self, bytes: Vec) -> K { 17 | if let Some(key) = self.query.get(&bytes) { 18 | return *key; 19 | } 20 | 21 | let str = Rc::new(bytes); 22 | 23 | let key = self.bytes.push(str.clone()); 24 | self.query.insert(str, key); 25 | 26 | key 27 | } 28 | 29 | pub fn get(&self, key: K) -> &[u8] { 30 | self.bytes[key].as_slice() 31 | } 32 | 33 | pub fn iter(&self) -> impl Iterator { 34 | self.bytes.iter().map(|(k, v)| (k, v.as_slice())) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lumina-collections/src/tests.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[derive(Clone, Copy, PartialEq, Eq)] 4 | struct TestKey(u32); 5 | 6 | map_key_impl!(TestKey(u32), "test"); 7 | 8 | #[test] 9 | fn keys_iter() { 10 | let map: Map = Map::from([0, 1, 2, 3]); 11 | let vec: Vec<(TestKey, usize)> = map.keys().map(|k| (k, map[k])).collect(); 12 | assert_eq!( 13 | vec.as_slice(), 14 | [0, 1, 2, 3].map(|i| (TestKey(i as u32), i)).as_slice() 15 | ); 16 | 17 | let mut mmap: MMap = MMap::from_iter([0, 1, 2, 3].map(Module::from)); 18 | mmap.push_as(M(Module(0), TestKey(0)), 0); 19 | mmap.push_as(M(Module(1), TestKey(0)), 1); 20 | 21 | let secondary = mmap.secondary_with(|_, v| *v); 22 | assert_eq!(secondary, mmap); 23 | 24 | assert_eq!( 25 | KeysIter::up_to(TestKey(2)).collect::>(), 26 | vec![TestKey(0), TestKey(1)] 27 | ); 28 | } 29 | 30 | #[test] 31 | fn secondary() { 32 | let map: Map = Map::from([0, 1, 2, 3]); 33 | 34 | let mut secondary = map.secondary(); 35 | assert_eq!(secondary, Map::new()); 36 | 37 | secondary.push_as(TestKey(0), 0); 38 | secondary.push_as(TestKey(1), 1); 39 | 40 | assert_eq!(secondary, Map::from([0, 1])); 41 | } 42 | -------------------------------------------------------------------------------- /lumina-compiler/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lumina-compiler" 3 | version = "0.1.0" 4 | authors = ["Simon "] 5 | edition = "2021" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | lumina-util = { path = "../lumina-util" } 11 | lumina-parser = { path = "../lumina-parser" } 12 | lumina-typesystem = { path = "../lumina-typesystem" } 13 | lumina-collections = { path = "../lumina-collections" } 14 | target-lexicon = "0.12.*" 15 | lumina-key = { path = "../lumina-key" } 16 | derive-new = "0.7" 17 | either = "1.13.0" 18 | ibig = { version = "0.3.6", features = [], default-features = false } 19 | owo-colors = { version = "4.0.*", features = ["supports-colors"] } 20 | itertools = "0.10.*" 21 | smallvec = "1.10.*" 22 | take_mut = "*" 23 | derive_more = { version = "1.0.*", features = ["from", "display", "index", "index_mut", "deref", "deref_mut", "add", "add_assign"] } 24 | insta = "*" 25 | tracing = "0.1.41" 26 | cranelift = "0.114.*" 27 | cranelift-entity = "0.114.*" 28 | cranelift-codegen = "0.114.*" 29 | cranelift-module = "0.114.*" 30 | cranelift-object = "0.114.*" 31 | cranelift-native = "0.114.*" 32 | tracing-subscriber = { version = "0.3.19", features = ["env-filter"] } 33 | tracing-tree = "0.4.0" 34 | gimli = "*" 35 | object = "*" 36 | -------------------------------------------------------------------------------- /lumina-compiler/src/backend/cranelift/ssa/array.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use layout::SpecialPointer; 3 | 4 | impl<'a, 's, 'f> InstHelper<'a, 's, 'f> { 5 | pub(super) fn indice_of_entry(&mut self, entry: VLayout, indice: Value) -> VLayout { 6 | match entry { 7 | Layout::SpecialPointer(kind, ptr) => match kind { 8 | SpecialPointer::StackArray(inner, len) => { 9 | let (_, elem_size, _) = self.structs.size_and_align_of_array(&inner, len); 10 | let indice_offset = self.ins().imul_imm(indice, elem_size as i64); 11 | let nptr = self.ins().iadd(ptr, indice_offset); 12 | self.deref_type(nptr, ByteOffset(0), &inner) 13 | } 14 | _ => panic!("attempted indice of non-array"), 15 | }, 16 | _ => panic!("cannot access indice of: {entry:?}"), 17 | } 18 | } 19 | 20 | fn alloc_array(&mut self, inner: &MonoType, len: usize) -> (ir::StackSlot, u32) { 21 | let (size, elem_size, align) = self.structs.size_and_align_of_array(inner, len as u64); 22 | (self.create_struct_stack_slot(size, align as u8), elem_size) 23 | } 24 | } 25 | 26 | impl<'c, 'a, 'f> Translator<'c, 'a, 'f> { 27 | pub(super) fn construct_array(&mut self, values: &[lir::Value], inner: &MonoType) -> VLayout { 28 | if values.is_empty() { 29 | return Layout::ZST; 30 | } 31 | 32 | match self.ctx.structs.arr_pass_mode(values.len() as u64, inner) { 33 | PassBy::Pointer => { 34 | let ptr = self.construct_array_on_stack(inner, values); 35 | let kind = SpecialPointer::StackArray(inner.clone(), values.len() as u64); 36 | Layout::SpecialPointer(kind, ptr) 37 | } 38 | PassBy::Value => { 39 | let values = self.construct_array_in_regs(values); 40 | Layout::ArrayFlat(inner.clone(), values) 41 | } 42 | PassBy::Transparent(_) => unreachable!(), 43 | } 44 | } 45 | 46 | pub(super) fn construct_array_on_stack( 47 | &mut self, 48 | inner: &MonoType, 49 | values: &[lir::Value], 50 | ) -> Value { 51 | let (slot, elem_size) = self.ins().alloc_array(inner, values.len()); 52 | let size_t = self.ctx.size_t(); 53 | 54 | let mut offset = 0; 55 | 56 | for value in values.iter() { 57 | let entry = self.value_to_vlayout(*value); 58 | let slot_addr = self.cins().stack_addr(size_t, slot, offset); 59 | 60 | self.ins().write_vlayout_to_ptr(slot_addr, &entry); 61 | offset += elem_size as i32; 62 | } 63 | 64 | self.cins().stack_addr(size_t, slot, 0) 65 | } 66 | 67 | pub(super) fn construct_array_in_regs(&mut self, values: &[lir::Value]) -> Vec { 68 | values.iter().map(|v| self.value_to_vlayout(*v)).collect() 69 | } 70 | 71 | pub(super) fn replicate_array( 72 | &mut self, 73 | value: lir::Value, 74 | inner: &MonoType, 75 | times: u64, 76 | ) -> VLayout { 77 | let values = vec![value; times as usize]; 78 | self.construct_array(&values, inner) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /lumina-compiler/src/backend/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod cranelift; 2 | 3 | use super::{ast, target::LinuxPlatform, target::Platform, Target}; 4 | use std::ffi::OsStr; 5 | use std::fs::File; 6 | use std::io::Write; 7 | use std::path::{Path, PathBuf}; 8 | use std::process::Command; 9 | use std::process::ExitCode; 10 | use tracing::info; 11 | 12 | pub fn link_native_binary( 13 | config: ast::ProjectConfig, 14 | target: Target, 15 | output: &Path, 16 | projectpath: PathBuf, 17 | luminapath: PathBuf, 18 | object: Vec, 19 | ) -> Result<(), ExitCode> { 20 | let project_name = config.name.clone(); 21 | let workdir = create_workdir(&luminapath, &project_name); 22 | 23 | let objectfile = { 24 | let mut path = workdir.join(&project_name); 25 | path.set_extension(target.object_extension()); 26 | 27 | let mut f = File::create(&path).unwrap(); 28 | f.write_all(&object).unwrap(); 29 | 30 | path 31 | }; 32 | 33 | let targetdir = luminapath.join("targets"); 34 | 35 | let bindir = targetdir.join("bin"); 36 | 37 | let mut linker = match target.platform { 38 | Platform::Linux { sub } => { 39 | let linuxdir = targetdir.join("linux"); 40 | let sublinuxdir = linuxdir.join(sub.to_string()); 41 | 42 | let mut linker = if matches!(sub, LinuxPlatform::Gnu) { 43 | let mut linker = Command::new("gcc"); 44 | linker.arg("-no-pie").arg("-flto"); 45 | linker 46 | } else { 47 | Command::new(bindir.join("ld.lld")) 48 | }; 49 | 50 | linker.arg("-o").arg(output).arg(&objectfile); 51 | 52 | for arg in config.linker_args { 53 | linker.arg(arg); 54 | } 55 | 56 | iter_objects(&sublinuxdir, &["o", "a"], |path| { 57 | linker.arg(path); 58 | }); 59 | 60 | for lib in config.linker_libs { 61 | linker.arg(projectpath.join(lib)); 62 | } 63 | 64 | linker.arg(linuxdir.join("syscall.o")); 65 | 66 | linker 67 | } 68 | }; 69 | 70 | info!("invoking system linker as: {:#?}", linker); 71 | 72 | let status = linker 73 | .spawn() 74 | .expect("failed to invoke linker") 75 | .wait() 76 | .unwrap(); 77 | 78 | if status.success() { 79 | std::fs::remove_dir_all(workdir).unwrap(); 80 | Ok(()) 81 | } else { 82 | Err(ExitCode::FAILURE) 83 | } 84 | } 85 | 86 | fn iter_objects(path: &Path, objs: &[&str], mut f: impl FnMut(PathBuf)) { 87 | for file in path.read_dir().unwrap() { 88 | let path = file.unwrap().path(); 89 | if let Some(ext) = path.extension() { 90 | if objs.iter().any(|o| OsStr::new(*o) == ext) { 91 | f(path); 92 | } 93 | } 94 | } 95 | } 96 | 97 | fn create_workdir(luminapath: &Path, project_name: &str) -> PathBuf { 98 | let mut workdir = luminapath.to_path_buf(); 99 | workdir.push("workdirs"); 100 | workdir.push(project_name); 101 | 102 | for i in 0.. { 103 | if !workdir.exists() { 104 | break; 105 | } 106 | 107 | workdir.pop(); 108 | workdir.push(format!("{project_name}_{i}")); 109 | } 110 | 111 | std::fs::create_dir_all(&workdir).expect("unable to create workdir directory in luminapath"); 112 | 113 | workdir 114 | } 115 | -------------------------------------------------------------------------------- /lumina-compiler/src/fmt.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! impl_map_arrow_fmt { 3 | (<$($l:lifetime),*> $form:path; for $ty:ty; $(($name:literal, $field:ident, $fmt:expr)),*) => { 4 | impl<$($l),*> $form for $ty { 5 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 6 | use itertools::Itertools; 7 | 8 | $({ 9 | let _name = $name; 10 | write!(f, "{_name}:\n {}", self.$field.iter().map($fmt).format("\n "))?; 11 | })*; 12 | 13 | Ok(()) 14 | } 15 | } 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /lumina-compiler/src/hir/scope.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | 3 | #[derive(Debug)] 4 | pub struct Bindings<'s> { 5 | scopes: Vec>, 6 | use_count: Map, 7 | } 8 | 9 | #[derive(Debug, Default)] 10 | struct Scope<'s> { 11 | binds: Vec<(key::Bind, &'s str)>, 12 | captures: Vec, 13 | where_binding_refs: Vec, 14 | } 15 | 16 | impl<'s> Default for Bindings<'s> { 17 | fn default() -> Self { 18 | Self { scopes: vec![Scope::default()], use_count: Map::new() } 19 | } 20 | } 21 | 22 | type Captures = Vec; 23 | 24 | impl<'s> Bindings<'s> { 25 | pub fn new() -> Self { 26 | Self::default() 27 | } 28 | 29 | pub fn enter(&mut self) { 30 | self.scopes.push(Scope::default()); 31 | } 32 | pub fn leave(&mut self) -> (Captures, Vec) { 33 | let scope = self.scopes.pop().unwrap(); 34 | (scope.captures, scope.where_binding_refs) 35 | } 36 | 37 | // Since tracking captures is usually done on the callstack of resolve, it'll be missed if a 38 | // lambda uses a where-binding. As it then needs to capture all the captures needed by the where-binding. 39 | pub fn reference_where_bind(&mut self, key: key::Lambda) { 40 | for scope in self.scopes.iter_mut().rev() { 41 | scope.where_binding_refs.push(key); 42 | } 43 | } 44 | 45 | pub fn declare_nameless(&mut self) -> key::Bind { 46 | let bind = self.use_count.push(0); 47 | trace!("declaring nameless {bind}"); 48 | bind 49 | } 50 | 51 | pub fn declare(&mut self, name: &'s str) -> key::Bind { 52 | let bind = self.use_count.push(0); 53 | trace!("declaring {name} as {bind}"); 54 | self.scopes.last_mut().unwrap().binds.push((bind, name)); 55 | bind 56 | } 57 | 58 | pub fn resolve(&mut self, name: &'s str) -> Option { 59 | Self::resolve_in(&mut self.scopes, name).map(|bind| { 60 | self.use_count[bind] += 1; 61 | bind 62 | }) 63 | } 64 | 65 | fn resolve_in(scopes: &mut [Scope<'s>], name: &'s str) -> Option { 66 | let (scope, xs) = scopes.split_last_mut()?; 67 | 68 | scope 69 | .binds 70 | .iter() 71 | .rev() 72 | .find_map(|(bind, n)| (name == *n).then_some(*bind)) 73 | .or_else(|| { 74 | Self::resolve_in(xs, name).map(|bind| { 75 | if !scope.captures.contains(&bind) { 76 | trace!("capturing {bind}"); 77 | scope.captures.push(bind); 78 | } 79 | bind 80 | }) 81 | }) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /lumina-compiler/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod ast; 2 | pub mod backend; 3 | mod debuginfo; 4 | pub mod hir; 5 | pub mod lir; 6 | pub mod mir; 7 | mod prelude; 8 | pub mod target; 9 | pub use target::Target; 10 | 11 | mod fmt; 12 | 13 | use derive_new::new; 14 | use key::M; 15 | use lumina_key as key; 16 | 17 | pub const LISTABLE_CONS: key::Method = key::Method(0); 18 | pub const LISTABLE_NEW: key::Method = key::Method(1); 19 | pub const LISTABLE_WITH_CAPACITY: key::Method = key::Method(2); 20 | pub const LISTABLE_SPLIT: key::Method = key::Method(3); 21 | 22 | pub const STRINGABLE_FROM_RAW_PARTS: key::Method = key::Method(4); 23 | 24 | pub const LIST_CONCAT: key::Variant = key::Variant(1); 25 | pub const LIST_SINGLETON: key::Variant = key::Variant(2); 26 | pub const LIST_NIL: key::Variant = key::Variant(3); 27 | 28 | pub const MAYBE_JUST: key::Variant = key::Variant(0); 29 | pub const MAYBE_NONE: key::Variant = key::Variant(1); 30 | 31 | pub const CLOSURE_CALL: key::Method = key::Method(0); 32 | pub const CLOSURE_CAPTURES: key::Param = key::Param(0); 33 | 34 | pub const SIZE_OF: key::Method = key::Method(0); 35 | 36 | pub const TRAIT_OBJECT_DATA_FIELD: key::Field = key::Field(0); 37 | pub const VTABLE_FIELD: key::Field = key::Field(1); 38 | 39 | #[derive(new, Clone, Copy)] 40 | pub struct ProjectInfo { 41 | main: M, 42 | sys_init: M, 43 | closure: M, 44 | allocator: (M, M), 45 | reflect_type: M, 46 | listable: M, 47 | global_list_default: M, 48 | stringable: M, 49 | string: M, 50 | maybe: M, 51 | } 52 | -------------------------------------------------------------------------------- /lumina-compiler/src/lir/reflect.rs: -------------------------------------------------------------------------------- 1 | use super::{FuncLower, Value}; 2 | use lumina_typesystem::Type; 3 | use tracing::error; 4 | 5 | impl<'a> FuncLower<'a> { 6 | pub fn create_reflection(&mut self, weak: Type) -> Value { 7 | error!("{}", &weak); 8 | 9 | // let reflect_weak = to_morphization!(self.lir, self.mir, &mut self.current.tmap) 10 | // .apply_weak(&Type::defined(self.info.reflect_type, vec![])); 11 | 12 | todo!("reflection API needs to be redesigned, it's not useful if you can't resolve implementations"); 13 | } 14 | } 15 | 16 | // const INT: Value = Value::UInt(0, mono::TAG_SIZE); 17 | // const FLOAT: Value = Value::UInt(1, mono::TAG_SIZE); 18 | // const BOOL: Value = Value::UInt(2, mono::TAG_SIZE); 19 | // const NEVER: Value = Value::UInt(3, mono::TAG_SIZE); 20 | // const POISON: Value = Value::UInt(4, mono::TAG_SIZE); 21 | // const POINTER: Value = Value::UInt(5, mono::TAG_SIZE); 22 | // const FN_POINTER: Value = Value::UInt(6, mono::TAG_SIZE); 23 | // const STRUCT: Value = Value::UInt(7, mono::TAG_SIZE); 24 | // const SUM: Value = Value::UInt(8, mono::TAG_SIZE); 25 | // const TUPLE: Value = Value::UInt(9, mono::TAG_SIZE); 26 | // const OBJECT: Value = Value::UInt(10, mono::TAG_SIZE); 27 | 28 | // (std:prelude) 29 | // 30 | // pub type Type 31 | // = Int bool u8 32 | // | Float 33 | // | Bool 34 | // | Never 35 | // | Poison 36 | // | Pointer Type 37 | // | FnPointer [Type] Type 38 | // | Struct string [(string, Type)] 39 | // | Sum string [(string, [Type])] 40 | // | Tuple [Type] 41 | // | Object string 42 | -------------------------------------------------------------------------------- /lumina-compiler/src/lir/ssa/snapshots/lumina_compiler__lir__ssa__opts__tests__functions.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-compiler/src/lir/ssa/opts.rs 3 | expression: "format!(\"{before}\\n{after}\")" 4 | snapshot_kind: text 5 | --- 6 | BEFORE: 7 | 8 | block0(): predecessors=1 9 | v0 = abs 0 : u0 10 | v1 = call mfunc1(v0) : u1 11 | return v1 // v2 12 | 13 | 14 | block0(v0: u2): predecessors=1 15 | jump block1(v0) // v1 16 | 17 | block1(v2: u2): predecessors=1 18 | jump mfunc2(v2) // v3 19 | 20 | AFTER: 21 | 22 | block0(): predecessors=1 23 | v0 = abs 0 : u0 24 | jump block2(v0) // v1 25 | 26 | block2(v2: u2): predecessors=1 27 | jump block3(v2) // v3 28 | 29 | block3(v4: u2): predecessors=1 30 | v5 = call mfunc2(v4) : mr0:??? 31 | jump block1(v5) // v6 32 | 33 | block1(v7: u1): predecessors=1 34 | return v7 // v8 35 | -------------------------------------------------------------------------------- /lumina-compiler/src/lir/ssa/snapshots/lumina_compiler__lir__ssa__opts__tests__inline_block.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-compiler/src/lir/ssa/opts.rs 3 | expression: "format!(\"{before}\\n{after}\")" 4 | snapshot_kind: text 5 | --- 6 | BEFORE: 7 | 8 | block0(v0: u0): predecessors=1 9 | v1 = add v0 1 : u1 10 | jump block1(v1) // v2 11 | 12 | block1(v3: u3): predecessors=1 13 | v4 = (construct v0 v1 v1 v3) : u4 14 | return v4 // v5 15 | 16 | AFTER: 17 | 18 | block0(v0: u0): predecessors=1 19 | v1 = add v0 1 : u1 20 | v2 = (construct v0 v1 v1 v1) : u4 21 | return v2 // v3 22 | -------------------------------------------------------------------------------- /lumina-compiler/src/lir/ssa/snapshots/lumina_compiler__lir__ssa__opts__tests__practical.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-compiler/src/lir/ssa/opts.rs 3 | expression: "format!(\"{before}\\n{after}\")" 4 | snapshot_kind: text 5 | --- 6 | BEFORE: 7 | 8 | block0(v0: u0): predecessors=1 9 | v1 = cast-payload v0 : u1 10 | v2 = abs v0 : u2 11 | jump block1() // v3 12 | 13 | block1(): predecessors=1 14 | v4 = cast-tag v0 : u16 15 | return v4 // v5 16 | 17 | AFTER: 18 | 19 | block0(v0: u0): predecessors=1 20 | v1 = cast-payload v0 : u1 21 | v2 = abs v0 : u2 22 | v3 = cast-tag v0 : u16 23 | return v3 // v4 24 | -------------------------------------------------------------------------------- /lumina-compiler/src/lir/ssa/snapshots/lumina_compiler__lir__ssa__opts__tests__tricky.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-compiler/src/lir/ssa/opts.rs 3 | expression: "format!(\"{before}\\n{after}\")" 4 | snapshot_kind: text 5 | --- 6 | BEFORE: 7 | 8 | block0(): predecessors=1 9 | select 1 10 | | true -> block1(1) 11 | | false -> block1(2) // v0 12 | 13 | block1(v1: u8): predecessors=2 14 | jump block2() // v2 15 | 16 | block2(): predecessors=1 17 | return v1 // v3 18 | 19 | AFTER: 20 | 21 | block0(): predecessors=1 22 | select 1 23 | | true -> block1(1) 24 | | false -> block1(2) // v0 25 | 26 | block1(v1: u8): predecessors=2 27 | return v1 // v2 28 | -------------------------------------------------------------------------------- /lumina-compiler/src/mir/lower/pat/snapshots/lumina_compiler__mir__lower__pat__tests__bind_inside_tuple_nested.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-compiler/src/mir/lower/pat/tests.rs 3 | expression: "format!\n(\"{}\\n\\nmissing patterns {{\\n {}\\n}}\", tree,\nmissing.into_iter().map(| p |\np.fmt(& name_of_var, & name_of_field).to_string()).format(\"\\n \"))" 4 | --- 5 | tuple(len = 4) 6 | u8 .. 7 | tuple(len = 3) 8 | u8 .. 9 | u8 .. 10 | u8 .. 11 | u8 .. 12 | { 13 | table = [PointTable { binds: [(b0, 0), (b1, 1), (b2, 2), (b3, 3), (b4, 4), (b5, 5), (b6, 6), (b7, 7)] }], 14 | excess = [(▵·0:sum0 u8)] 15 | tail = 0 16 | } 17 | 18 | missing patterns { 19 | 20 | } 21 | -------------------------------------------------------------------------------- /lumina-compiler/src/mir/lower/pat/snapshots/lumina_compiler__mir__lower__pat__tests__instant_wildcard-2.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-compiler/src/mir/lower/pat/tests.rs 3 | expression: "format!\n(\"{}\\n\\nmissing patterns {{\\n {}\\n}}\", tree,\nmissing.into_iter().map(| p |\np.fmt(& name_of_var, & name_of_field).to_string()).format(\"\\n \"))" 4 | --- 5 | { 6 | table = [PointTable { binds: [(b0, 0)] }], 7 | excess = [] 8 | tail = 0 9 | } 10 | 11 | missing patterns { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /lumina-compiler/src/mir/lower/pat/snapshots/lumina_compiler__mir__lower__pat__tests__instant_wildcard.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-compiler/src/mir/lower/pat/tests.rs 3 | expression: "format!\n(\"{}\\n\\nmissing patterns {{\\n {}\\n}}\", tree,\nmissing.into_iter().map(| p |\np.fmt(& name_of_var, & name_of_field).to_string()).format(\"\\n \"))" 4 | --- 5 | { 6 | table = [PointTable { binds: [] }], 7 | excess = [] 8 | tail = 0 9 | } 10 | 11 | missing patterns { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /lumina-compiler/src/mir/lower/pat/snapshots/lumina_compiler__mir__lower__pat__tests__int_matrix.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-compiler/src/mir/lower/pat/tests.rs 3 | expression: "format!\n(\"{}\\n\\nmissing patterns {{\\n {}\\n}}\", tree,\nmissing.into_iter().map(| p |\np.fmt(& name_of_var, & name_of_field).to_string()).format(\"\\n \"))" 4 | --- 5 | tuple(len = 3) 6 | u8 ..1 7 | u8 ..49 8 | unreached: u8 9 | u8 50..200 10 | u8 ..128 11 | { 12 | table = [PointTable { binds: [] }], 13 | excess = [] 14 | tail = 0 15 | } 16 | u8 129.. 17 | { 18 | table = [PointTable { binds: [] }], 19 | excess = [] 20 | tail = 1 21 | } 22 | u8 201.. 23 | u8 ..128 24 | { 25 | table = [PointTable { binds: [] }], 26 | excess = [] 27 | tail = 0 28 | } 29 | u8 129.. 30 | unreached: 31 | u8 2..3 32 | u8 ..49 33 | unreached: u8 34 | u8 50..200 35 | u8 ..128 36 | { 37 | table = [PointTable { binds: [] }], 38 | excess = [] 39 | tail = 0 40 | } 41 | u8 129.. 42 | { 43 | table = [PointTable { binds: [] }], 44 | excess = [] 45 | tail = 1 46 | } 47 | u8 201..210 48 | u8 ..128 49 | { 50 | table = [PointTable { binds: [] }], 51 | excess = [] 52 | tail = 0 53 | } 54 | u8 129.. 55 | { 56 | table = [PointTable { binds: [] }], 57 | excess = [] 58 | tail = 3 59 | } 60 | u8 211.. 61 | u8 ..128 62 | { 63 | table = [PointTable { binds: [] }], 64 | excess = [] 65 | tail = 0 66 | } 67 | u8 129.. 68 | unreached: 69 | u8 4..6 70 | u8 ..49 71 | unreached: u8 72 | u8 50..200 73 | u8 ..128 74 | { 75 | table = [PointTable { binds: [] }], 76 | excess = [] 77 | tail = 0 78 | } 79 | u8 129.. 80 | { 81 | table = [PointTable { binds: [] }], 82 | excess = [] 83 | tail = 1 84 | } 85 | u8 201.. 86 | u8 ..128 87 | { 88 | table = [PointTable { binds: [] }], 89 | excess = [] 90 | tail = 0 91 | } 92 | u8 129.. 93 | unreached: 94 | u8 7.. 95 | unreached: u8, u8 96 | 97 | missing patterns { 98 | (_, _, _) 99 | } 100 | -------------------------------------------------------------------------------- /lumina-compiler/src/mir/lower/pat/snapshots/lumina_compiler__mir__lower__pat__tests__just_x_nothing.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-compiler/src/mir/lower/pat/tests.rs 3 | expression: "format!\n(\"{}\\n\\nmissing patterns {{\\n {}\\n}}\", tree,\nmissing.into_iter().map(| p |\np.fmt(& name_of_var, & name_of_field).to_string()).format(\"\\n \"))" 4 | --- 5 | ▵·0:sum0 variant0 6 | { 7 | table = [PointTable { binds: [(b0, 1)] }], 8 | excess = [u8] 9 | tail = 0 10 | } 11 | ▵·0:sum0 variant1 12 | { 13 | table = [PointTable { binds: [] }], 14 | excess = [] 15 | tail = 2 16 | } 17 | 18 | missing patterns { 19 | 20 | } 21 | -------------------------------------------------------------------------------- /lumina-compiler/src/mir/lower/pat/snapshots/lumina_compiler__mir__lower__pat__tests__list_x_xs.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-compiler/src/mir/lower/pat/tests.rs 3 | expression: "format!\n(\"{}\\n\\nmissing patterns {{\\n {}\\n}}\", tree,\nmissing.into_iter().map(| p |\np.fmt(& name_of_var, & name_of_field).to_string()).format(\"\\n \"))" 4 | --- 5 | list variant0 6 | u8 .. 7 | { 8 | table = [PointTable { binds: [(b10, 1), (b20, 2)] }], 9 | excess = [[u8]] 10 | tail = 0 11 | } 12 | list variant1 13 | { 14 | table = [PointTable { binds: [] }], 15 | excess = [] 16 | tail = 1 17 | } 18 | 19 | missing patterns { 20 | 21 | } 22 | -------------------------------------------------------------------------------- /lumina-compiler/src/mir/lower/pat/snapshots/lumina_compiler__mir__lower__pat__tests__missing_maybes.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-compiler/src/mir/lower/pat/tests.rs 3 | expression: "format!\n(\"{}\\n\\nmissing patterns {{\\n {}\\n}}\", tree,\nmissing.into_iter().map(| p |\np.fmt(& name_of_var, & name_of_field).to_string()).format(\"\\n \"))" 4 | --- 5 | tuple(len = 2) 6 | ▵·0:sum0 variant0 7 | u8 .. 8 | ▵·0:sum0 variant0 9 | u8 .. 10 | { 11 | table = [PointTable { binds: [] }], 12 | excess = [] 13 | tail = 1 14 | } 15 | ▵·0:sum0 variant1 16 | unreached: 17 | ▵·0:sum0 variant1 18 | ▵·0:sum0 variant0 19 | u8 .. 20 | { 21 | table = [PointTable { binds: [] }], 22 | excess = [] 23 | tail = 0 24 | } 25 | ▵·0:sum0 variant1 26 | { 27 | table = [PointTable { binds: [] }], 28 | excess = [] 29 | tail = 3 30 | } 31 | 32 | missing patterns { 33 | (Just _, None) 34 | } 35 | -------------------------------------------------------------------------------- /lumina-compiler/src/mir/lower/pat/snapshots/lumina_compiler__mir__lower__pat__tests__nested_lists.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-compiler/src/mir/lower/pat/tests.rs 3 | expression: "format!\n(\"{}\\n\\nmissing patterns {{\\n {}\\n}}\", tree,\nmissing.into_iter().map(| p |\np.fmt(& name_of_var, & name_of_field).to_string()).format(\"\\n \"))" 4 | --- 5 | list variant0 6 | list variant0 7 | tuple(len = 0) 8 | list variant0 9 | _ 10 | _ 11 | { 12 | table = [PointTable { binds: [(b0, 5), (b1, 6)] }], 13 | excess = [[[()]]] 14 | tail = 0 15 | } 16 | list variant1 17 | { 18 | table = [PointTable { binds: [(b0, 3), (b1, 4)] }], 19 | excess = [[[()]]] 20 | tail = 0 21 | } 22 | list variant1 23 | { 24 | table = [PointTable { binds: [(b0, 1), (b1, 2)] }], 25 | excess = [[[()]]] 26 | tail = 0 27 | } 28 | list variant1 29 | { 30 | table = [PointTable { binds: [] }], 31 | excess = [] 32 | tail = 2 33 | } 34 | 35 | missing patterns { 36 | 37 | } 38 | -------------------------------------------------------------------------------- /lumina-compiler/src/prelude.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused)] 2 | 3 | pub use crate::{ast, hir, lir, mir}; 4 | pub use derive_new::new; 5 | pub use itertools::Itertools; 6 | pub use lumina_key as key; 7 | pub use lumina_key::{MMap, Map, M}; 8 | pub use lumina_util::{Span, Spanned, Tr}; 9 | pub use std::collections::HashMap; 10 | pub use tracing::{error, info, trace, warn}; 11 | -------------------------------------------------------------------------------- /lumina-gc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lumina-gc" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [lib] 7 | crate-type = ["staticlib"] 8 | 9 | [profile.dev] 10 | # panic='abort' 11 | lto = true 12 | 13 | [profile.release] 14 | # panic='abort' 15 | lto = true 16 | 17 | [dependencies] 18 | mmtk = "0.30.0" 19 | log = "*" 20 | libc = "*" 21 | 22 | [features] 23 | default = [] 24 | is_mmtk_object = ["mmtk/is_mmtk_object"] 25 | malloc_counted_size = ["mmtk/malloc_counted_size"] 26 | -------------------------------------------------------------------------------- /lumina-gc/build: -------------------------------------------------------------------------------- 1 | #!/bin/env bash 2 | 3 | # cargo build -Z build-std=std,panic_abort -Z build-std-features=panic_immediate_abort --target x86_64-unknown-linux-musl 4 | # cargo build --release -Z build-std=std,panic_abort -Z build-std-features=panic_immediate_abort --target x86_64-unknown-linux-musl 5 | # 6 | # cargo build -Z build-std=std,panic_abort -Z build-std-features=panic_immediate_abort --target x86_64-unknown-linux-gnu 7 | # cargo build --release -Z build-std=std,panic_abort -Z build-std-features=panic_immediate_abort --target x86_64-unknown-linux-gnu 8 | 9 | # cargo build -Z build-std=std --target x86_64-unknown-linux-musl 10 | # cargo build -Z build-std=std --release --target x86_64-unknown-linux-musl 11 | 12 | cargo build -Z build-std=std --target x86_64-unknown-linux-gnu 13 | # cargo build -Z build-std=std --release --target x86_64-unknown-linux-gnu 14 | -------------------------------------------------------------------------------- /lumina-gc/src.old/active_plan.rs: -------------------------------------------------------------------------------- 1 | use crate::DummyVM; 2 | use mmtk::util::opaque_pointer::*; 3 | use mmtk::vm::ActivePlan; 4 | use mmtk::Mutator; 5 | 6 | pub struct VMActivePlan {} 7 | 8 | // Documentation: https://docs.mmtk.io/api/mmtk/vm/active_plan/trait.ActivePlan.html 9 | impl ActivePlan for VMActivePlan { 10 | fn number_of_mutators() -> usize { 11 | unimplemented!() 12 | } 13 | 14 | fn is_mutator(_tls: VMThread) -> bool { 15 | // FIXME: Properly check if the thread is a mutator 16 | true 17 | } 18 | 19 | fn mutator(_tls: VMMutatorThread) -> &'static mut Mutator { 20 | unimplemented!() 21 | } 22 | 23 | fn mutators<'a>() -> Box> + 'a> { 24 | unimplemented!() 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lumina-gc/src.old/collection.rs: -------------------------------------------------------------------------------- 1 | use crate::DummyVM; 2 | use mmtk::util::opaque_pointer::*; 3 | use mmtk::vm::Collection; 4 | use mmtk::vm::GCThreadContext; 5 | use mmtk::Mutator; 6 | 7 | pub struct VMCollection {} 8 | 9 | // Documentation: https://docs.mmtk.io/api/mmtk/vm/collection/trait.Collection.html 10 | impl Collection for VMCollection { 11 | fn stop_all_mutators(_tls: VMWorkerThread, _mutator_visitor: F) 12 | where 13 | F: FnMut(&'static mut Mutator), 14 | { 15 | unimplemented!() 16 | } 17 | 18 | fn resume_mutators(_tls: VMWorkerThread) { 19 | unimplemented!() 20 | } 21 | 22 | fn block_for_gc(_tls: VMMutatorThread) { 23 | unimplemented!() 24 | } 25 | 26 | fn spawn_gc_thread(_tls: VMThread, _ctx: GCThreadContext) { 27 | unimplemented!() 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lumina-gc/src.old/lib.rs: -------------------------------------------------------------------------------- 1 | // extern crate libc; 2 | // extern crate mmtk; 3 | 4 | use std::sync::OnceLock; 5 | 6 | use mmtk::vm::VMBinding; 7 | use mmtk::MMTK; 8 | 9 | pub mod active_plan; 10 | pub mod api; 11 | pub mod collection; 12 | pub mod object_model; 13 | pub mod reference_glue; 14 | pub mod scanning; 15 | 16 | pub type DummyVMSlot = mmtk::vm::slot::SimpleSlot; 17 | 18 | #[derive(Default)] 19 | pub struct DummyVM; 20 | 21 | // Documentation: https://docs.mmtk.io/api/mmtk/vm/trait.VMBinding.html 22 | impl VMBinding for DummyVM { 23 | type VMObjectModel = object_model::VMObjectModel; 24 | type VMScanning = scanning::VMScanning; 25 | type VMCollection = collection::VMCollection; 26 | type VMActivePlan = active_plan::VMActivePlan; 27 | type VMReferenceGlue = reference_glue::VMReferenceGlue; 28 | type VMSlot = DummyVMSlot; 29 | type VMMemorySlice = mmtk::vm::slot::UnimplementedMemorySlice; 30 | 31 | /// Allowed maximum alignment in bytes. 32 | const MAX_ALIGNMENT: usize = 1 << 6; 33 | 34 | const MIN_ALIGNMENT: usize = 4; 35 | } 36 | 37 | pub static GC: OnceLock>> = OnceLock::new(); 38 | 39 | fn mmtk() -> &'static MMTK { 40 | GC.get().unwrap() 41 | } 42 | 43 | // MMTK expects the langauge runtime to spawn threads. 44 | // 45 | // This makes sense; since it means Lumina will always be aware of which threads exist 46 | // (including those implicitly created by MMTK) 47 | // 48 | // However; it also means that hooking up the musl/glibc calls for that will be important. 49 | -------------------------------------------------------------------------------- /lumina-gc/src.old/reference_glue.rs: -------------------------------------------------------------------------------- 1 | use crate::DummyVM; 2 | use mmtk::util::opaque_pointer::VMWorkerThread; 3 | use mmtk::util::ObjectReference; 4 | use mmtk::vm::ReferenceGlue; 5 | 6 | pub struct VMReferenceGlue {} 7 | 8 | // Documentation: https://docs.mmtk.io/api/mmtk/vm/reference_glue/trait.ReferenceGlue.html 9 | impl ReferenceGlue for VMReferenceGlue { 10 | type FinalizableType = ObjectReference; 11 | 12 | fn set_referent(_reference: ObjectReference, _referent: ObjectReference) { 13 | unimplemented!() 14 | } 15 | fn get_referent(_object: ObjectReference) -> Option { 16 | unimplemented!() 17 | } 18 | fn clear_referent(_object: ObjectReference) { 19 | unimplemented!() 20 | } 21 | fn enqueue_references(_references: &[ObjectReference], _tls: VMWorkerThread) { 22 | unimplemented!() 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lumina-gc/src.old/scanning.rs: -------------------------------------------------------------------------------- 1 | use crate::DummyVM; 2 | use crate::DummyVMSlot; 3 | use mmtk::util::opaque_pointer::*; 4 | use mmtk::util::ObjectReference; 5 | use mmtk::vm::RootsWorkFactory; 6 | use mmtk::vm::Scanning; 7 | use mmtk::vm::SlotVisitor; 8 | use mmtk::Mutator; 9 | 10 | pub struct VMScanning {} 11 | 12 | // Documentation: https://docs.mmtk.io/api/mmtk/vm/scanning/trait.Scanning.html 13 | impl Scanning for VMScanning { 14 | fn scan_roots_in_mutator_thread( 15 | _tls: VMWorkerThread, 16 | _mutator: &'static mut Mutator, 17 | _factory: impl RootsWorkFactory, 18 | ) { 19 | unimplemented!() 20 | } 21 | fn scan_vm_specific_roots(_tls: VMWorkerThread, _factory: impl RootsWorkFactory) { 22 | unimplemented!() 23 | } 24 | fn scan_object>( 25 | _tls: VMWorkerThread, 26 | _object: ObjectReference, 27 | _slot_visitor: &mut SV, 28 | ) { 29 | unimplemented!() 30 | } 31 | fn notify_initial_thread_scan_complete(_partial_scan: bool, _tls: VMWorkerThread) { 32 | unimplemented!() 33 | } 34 | fn supports_return_barrier() -> bool { 35 | unimplemented!() 36 | } 37 | fn prepare_for_roots_re_scanning() { 38 | unimplemented!() 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lumina-gc/src/active_plan.rs: -------------------------------------------------------------------------------- 1 | use crate::DummyVM; 2 | use mmtk::util::opaque_pointer::*; 3 | use mmtk::vm::ActivePlan; 4 | use mmtk::Mutator; 5 | 6 | pub struct VMActivePlan {} 7 | 8 | // Documentation: https://docs.mmtk.io/api/mmtk/vm/active_plan/trait.ActivePlan.html 9 | impl ActivePlan for VMActivePlan { 10 | fn number_of_mutators() -> usize { 11 | 1 12 | } 13 | 14 | fn is_mutator(_tls: VMThread) -> bool { 15 | // FIXME: Properly check if the thread is a mutator 16 | true 17 | } 18 | 19 | fn mutator(_tls: VMMutatorThread) -> &'static mut Mutator { 20 | unimplemented!() 21 | } 22 | 23 | fn mutators<'a>() -> Box> + 'a> { 24 | unimplemented!() 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lumina-gc/src/collection.rs: -------------------------------------------------------------------------------- 1 | use crate::DummyVM; 2 | use mmtk::util::opaque_pointer::*; 3 | use mmtk::vm::Collection; 4 | use mmtk::vm::GCThreadContext; 5 | use mmtk::Mutator; 6 | 7 | pub struct VMCollection {} 8 | 9 | // Documentation: https://docs.mmtk.io/api/mmtk/vm/collection/trait.Collection.html 10 | impl Collection for VMCollection { 11 | fn stop_all_mutators(_tls: VMWorkerThread, _mutator_visitor: F) 12 | where 13 | F: FnMut(&'static mut Mutator), 14 | { 15 | unimplemented!() 16 | } 17 | 18 | fn resume_mutators(_tls: VMWorkerThread) { 19 | unimplemented!() 20 | } 21 | 22 | fn block_for_gc(_tls: VMMutatorThread) { 23 | unimplemented!() 24 | } 25 | 26 | fn spawn_gc_thread(_tls: VMThread, _ctx: GCThreadContext) {} 27 | } 28 | -------------------------------------------------------------------------------- /lumina-gc/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::sync::OnceLock; 2 | 3 | use mmtk::vm::VMBinding; 4 | use mmtk::{Mutator, MMTK}; 5 | 6 | pub mod active_plan; 7 | pub mod api; 8 | pub mod collection; 9 | pub mod object_model; 10 | pub mod reference_glue; 11 | pub mod scanning; 12 | 13 | pub type DummyVMSlot = mmtk::vm::slot::SimpleSlot; 14 | 15 | #[derive(Default)] 16 | pub struct DummyVM; 17 | 18 | // Documentation: https://docs.mmtk.io/api/mmtk/vm/trait.VMBinding.html 19 | impl VMBinding for DummyVM { 20 | type VMObjectModel = object_model::VMObjectModel; 21 | type VMScanning = scanning::VMScanning; 22 | type VMCollection = collection::VMCollection; 23 | type VMActivePlan = active_plan::VMActivePlan; 24 | type VMReferenceGlue = reference_glue::VMReferenceGlue; 25 | type VMSlot = DummyVMSlot; 26 | type VMMemorySlice = mmtk::vm::slot::UnimplementedMemorySlice; 27 | 28 | /// Allowed maximum alignment in bytes. 29 | const MAX_ALIGNMENT: usize = 1 << 6; 30 | } 31 | 32 | use mmtk::util::{Address, ObjectReference}; 33 | 34 | impl DummyVM { 35 | pub fn object_start_to_ref(start: Address) -> ObjectReference { 36 | // Safety: start is the allocation result, and it should not be zero with an offset. 37 | unsafe { 38 | ObjectReference::from_raw_address_unchecked( 39 | start + crate::object_model::OBJECT_REF_OFFSET, 40 | ) 41 | } 42 | } 43 | } 44 | 45 | pub static SINGLETON: OnceLock>> = OnceLock::new(); 46 | 47 | fn mmtk() -> &'static MMTK { 48 | SINGLETON.get().unwrap() 49 | } 50 | -------------------------------------------------------------------------------- /lumina-gc/src/object_model.rs: -------------------------------------------------------------------------------- 1 | use crate::DummyVM; 2 | use mmtk::util::copy::{CopySemantics, GCWorkerCopyContext}; 3 | use mmtk::util::{Address, ObjectReference}; 4 | use mmtk::vm::*; 5 | 6 | pub struct VMObjectModel {} 7 | 8 | /// This is the offset from the allocation result to the object reference for the object. 9 | /// For bindings that this offset is not a constant, you can implement the calculation in the method `ref_to_object_start`, and 10 | /// remove this constant. 11 | pub const OBJECT_REF_OFFSET: usize = 0; 12 | 13 | // This is the offset from the object reference to the object header. 14 | // This value is used in `ref_to_header` where MMTk loads header metadata from. 15 | pub const OBJECT_HEADER_OFFSET: usize = 0; 16 | 17 | // Documentation: https://docs.mmtk.io/api/mmtk/vm/object_model/trait.ObjectModel.html 18 | impl ObjectModel for VMObjectModel { 19 | // Global metadata 20 | 21 | const GLOBAL_LOG_BIT_SPEC: VMGlobalLogBitSpec = VMGlobalLogBitSpec::side_first(); 22 | 23 | // Local metadata 24 | 25 | // Forwarding pointers have to be in the header. It is okay to overwrite the object payload with a forwarding pointer. 26 | // FIXME: The bit offset needs to be set properly. 27 | const LOCAL_FORWARDING_POINTER_SPEC: VMLocalForwardingPointerSpec = 28 | VMLocalForwardingPointerSpec::in_header(0); 29 | // The other metadata can be put in the side metadata. 30 | const LOCAL_FORWARDING_BITS_SPEC: VMLocalForwardingBitsSpec = 31 | VMLocalForwardingBitsSpec::side_first(); 32 | const LOCAL_MARK_BIT_SPEC: VMLocalMarkBitSpec = 33 | VMLocalMarkBitSpec::side_after(Self::LOCAL_FORWARDING_BITS_SPEC.as_spec()); 34 | const LOCAL_LOS_MARK_NURSERY_SPEC: VMLocalLOSMarkNurserySpec = 35 | VMLocalLOSMarkNurserySpec::side_after(Self::LOCAL_MARK_BIT_SPEC.as_spec()); 36 | 37 | const OBJECT_REF_OFFSET_LOWER_BOUND: isize = OBJECT_REF_OFFSET as isize; 38 | 39 | fn copy( 40 | _from: ObjectReference, 41 | _semantics: CopySemantics, 42 | _copy_context: &mut GCWorkerCopyContext, 43 | ) -> ObjectReference { 44 | unimplemented!() 45 | } 46 | 47 | fn copy_to(_from: ObjectReference, _to: ObjectReference, _region: Address) -> Address { 48 | unimplemented!() 49 | } 50 | 51 | fn get_current_size(_object: ObjectReference) -> usize { 52 | unimplemented!() 53 | } 54 | 55 | fn get_size_when_copied(object: ObjectReference) -> usize { 56 | // FIXME: This assumes the object size is unchanged during copying. 57 | Self::get_current_size(object) 58 | } 59 | 60 | fn get_align_when_copied(_object: ObjectReference) -> usize { 61 | unimplemented!() 62 | } 63 | 64 | fn get_align_offset_when_copied(_object: ObjectReference) -> usize { 65 | unimplemented!() 66 | } 67 | 68 | fn get_reference_when_copied_to(_from: ObjectReference, _to: Address) -> ObjectReference { 69 | unimplemented!() 70 | } 71 | 72 | fn get_type_descriptor(_reference: ObjectReference) -> &'static [i8] { 73 | unimplemented!() 74 | } 75 | 76 | fn ref_to_object_start(object: ObjectReference) -> Address { 77 | object.to_raw_address().sub(OBJECT_REF_OFFSET) 78 | } 79 | 80 | fn ref_to_header(object: ObjectReference) -> Address { 81 | object.to_raw_address().sub(OBJECT_HEADER_OFFSET) 82 | } 83 | 84 | fn dump_object(_object: ObjectReference) { 85 | unimplemented!() 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /lumina-gc/src/reference_glue.rs: -------------------------------------------------------------------------------- 1 | use crate::DummyVM; 2 | use mmtk::util::opaque_pointer::VMWorkerThread; 3 | use mmtk::util::ObjectReference; 4 | use mmtk::vm::ReferenceGlue; 5 | 6 | pub struct VMReferenceGlue {} 7 | 8 | // Documentation: https://docs.mmtk.io/api/mmtk/vm/reference_glue/trait.ReferenceGlue.html 9 | impl ReferenceGlue for VMReferenceGlue { 10 | type FinalizableType = ObjectReference; 11 | 12 | fn set_referent(_reference: ObjectReference, _referent: ObjectReference) { 13 | unimplemented!() 14 | } 15 | fn get_referent(_object: ObjectReference) -> Option { 16 | unimplemented!() 17 | } 18 | fn clear_referent(_object: ObjectReference) { 19 | unimplemented!() 20 | } 21 | fn enqueue_references(_references: &[ObjectReference], _tls: VMWorkerThread) { 22 | unimplemented!() 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lumina-gc/src/scanning.rs: -------------------------------------------------------------------------------- 1 | use crate::DummyVM; 2 | use crate::DummyVMSlot; 3 | use mmtk::util::opaque_pointer::*; 4 | use mmtk::util::ObjectReference; 5 | use mmtk::vm::RootsWorkFactory; 6 | use mmtk::vm::Scanning; 7 | use mmtk::vm::SlotVisitor; 8 | use mmtk::Mutator; 9 | 10 | pub struct VMScanning {} 11 | 12 | // Documentation: https://docs.mmtk.io/api/mmtk/vm/scanning/trait.Scanning.html 13 | impl Scanning for VMScanning { 14 | fn scan_roots_in_mutator_thread( 15 | _tls: VMWorkerThread, 16 | _mutator: &'static mut Mutator, 17 | _factory: impl RootsWorkFactory, 18 | ) { 19 | unimplemented!() 20 | } 21 | fn scan_vm_specific_roots(_tls: VMWorkerThread, _factory: impl RootsWorkFactory) { 22 | unimplemented!() 23 | } 24 | fn scan_object>( 25 | _tls: VMWorkerThread, 26 | _object: ObjectReference, 27 | _slot_visitor: &mut SV, 28 | ) { 29 | unimplemented!() 30 | } 31 | fn notify_initial_thread_scan_complete(_partial_scan: bool, _tls: VMWorkerThread) { 32 | unimplemented!() 33 | } 34 | fn supports_return_barrier() -> bool { 35 | unimplemented!() 36 | } 37 | fn prepare_for_roots_re_scanning() { 38 | unimplemented!() 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lumina-key/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lumina-key" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | lumina-collections = { path = "../lumina-collections" } 10 | itertools = "*" 11 | -------------------------------------------------------------------------------- /lumina-key/src/lib.rs: -------------------------------------------------------------------------------- 1 | use lumina_collections::{kind_key, map_key_impl}; 2 | pub use lumina_collections::{MMap, Map}; 3 | pub use lumina_collections::{Module, M}; 4 | 5 | kind_key! { 6 | pub enum TypeKind { 7 | Record(Record), 8 | Sum(Sum), 9 | Trait(Trait), 10 | } 11 | } 12 | 13 | #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] 14 | pub struct Record(pub u32); 15 | map_key_impl!(Record(u32), "record"); 16 | 17 | #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] 18 | pub struct Sum(pub u32); 19 | map_key_impl!(Sum(u32), "sum"); 20 | 21 | #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] 22 | pub struct AssociatedType(pub u32); 23 | map_key_impl!(AssociatedType(u32), "assoc"); 24 | 25 | #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] 26 | pub struct Func(u32); 27 | map_key_impl!(Func(u32), "func"); 28 | 29 | #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] 30 | pub struct Impl(u32); 31 | map_key_impl!(Impl(u32), "impl"); 32 | 33 | #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] 34 | pub struct Param(pub u32); 35 | map_key_impl!(Param(u32), "param"); 36 | 37 | #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] 38 | pub struct Val(u32); 39 | map_key_impl!(Val(u32), "val"); 40 | 41 | /// A Sum variant 42 | #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] 43 | pub struct Variant(pub u32); 44 | map_key_impl!(Variant(u32), "variant"); 45 | 46 | /// A Record field 47 | #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] 48 | pub struct Field(pub u32); 49 | map_key_impl!(Field(u32), "field"); 50 | 51 | #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] 52 | pub struct ReadOnly(u32); 53 | map_key_impl!(ReadOnly(u32), "ro"); 54 | 55 | #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] 56 | pub struct Bind(pub u32); 57 | map_key_impl!(Bind(u32), "b"); 58 | 59 | #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] 60 | pub struct Capture(u32); 61 | map_key_impl!(Capture(u32), "c"); 62 | 63 | #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] 64 | pub struct DecisionTreeTail(pub u32); 65 | map_key_impl!(DecisionTreeTail(u32), "tail"); 66 | 67 | #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] 68 | pub struct Method(pub u32); 69 | map_key_impl!(Method(u32), "method"); 70 | 71 | #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] 72 | pub struct Trait(u32); 73 | map_key_impl!(Trait(u32), "trait"); 74 | 75 | #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] 76 | pub struct Generic(pub u32); 77 | map_key_impl!(Generic(u32), |this, f| ((this.0 as u8 + b'a') as char) 78 | .fmt(f)); 79 | 80 | #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] 81 | pub struct Lambda(pub u32); 82 | map_key_impl!(Lambda(u32), |this, f| write!(f, "λ·{}", this.0)); 83 | 84 | pub const PRELUDE: Module = Module(0); 85 | -------------------------------------------------------------------------------- /lumina-parser/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lumina-parser" 3 | version = "0.1.0" 4 | authors = ["Simon "] 5 | edition = "2021" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | lumina-util = { path = "../lumina-util/" } 11 | lumina-key = { path = "../lumina-key" } 12 | lumina-tokentree = { path = "../lumina-tokentree" } 13 | itertools = "0.10.*" 14 | logos = { git = "https://github.com/simvux/logos" } 15 | insta = "1.39.*" 16 | smallvec = "*" 17 | tracing = "0.1.41" 18 | -------------------------------------------------------------------------------- /lumina-parser/src/alias.rs: -------------------------------------------------------------------------------- 1 | use super::{Expr, Parser, Token, Type}; 2 | use lumina_util::{Highlighting, Tr}; 3 | use std::fmt; 4 | 5 | #[derive(Debug)] 6 | pub struct Declaration<'a> { 7 | pub name: Tr<&'a str>, 8 | pub dst: Tr>, 9 | pub attributes: Vec>>, 10 | } 11 | 12 | impl<'a> Parser<'a> { 13 | pub fn alias(&mut self, attributes: Vec>>) -> Option> { 14 | let name = self.expect_name("alias name")?; 15 | self.expect(Token::Equal)?; 16 | let dst = self.type_newline_sensitive()?; 17 | Some(Declaration { name, dst, attributes }) 18 | } 19 | } 20 | 21 | impl<'a> fmt::Display for Declaration<'a> { 22 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 23 | write!( 24 | f, 25 | "{} {} {} {}", 26 | "alias".keyword(), 27 | self.name, 28 | '='.symbol(), 29 | &self.dst 30 | ) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lumina-parser/src/error.rs: -------------------------------------------------------------------------------- 1 | use crate::{expr::DiffConflict, expr::IndentConflict, Parser, Token}; 2 | use lumina_util::Span; 3 | 4 | #[derive(Clone, Debug)] 5 | pub enum Error { 6 | ExpectedButGot(Span, String, String), 7 | ExpectedTokenButGot(Span, String, Token), 8 | MissingSquareForExtractor(Span), 9 | FnNeedsParenthesis(Span), 10 | // Unexpected(Span, String), 11 | InvalidAttributes(Span, Span), 12 | ToplevelWhere(Span), 13 | BadIndentation(Span), 14 | BadDefault(Span, Token), 15 | BadIndentForMatch(Span, DiffConflict), 16 | BadHeaderForWhere(Span, Token), 17 | Unmatched(Span, String), 18 | InvalidTraitMember(Span), 19 | InvalidNestedMatch { previous: Span, new: Span }, 20 | ConflictingBars(IndentConflict), 21 | MissingReturnType(Span), 22 | NestedWhere { previous: Span, kw: Span }, 23 | } 24 | 25 | impl<'a> Parser<'a> { 26 | pub(crate) fn err_expected_but_got( 27 | &mut self, 28 | span: Span, 29 | exp: impl Into, 30 | got: impl Into, 31 | ) { 32 | self.errors 33 | .push(Error::ExpectedButGot(span, exp.into(), got.into())); 34 | } 35 | 36 | pub(crate) fn err_unexpected_token( 37 | &mut self, 38 | (token, span): (Token, Span), 39 | exp: impl Into, 40 | ) { 41 | self.errors 42 | .push(Error::ExpectedTokenButGot(span, exp.into(), token)); 43 | } 44 | 45 | pub(crate) fn err_fn_needs_parenthesis(&mut self, span: Span) { 46 | self.errors.push(Error::FnNeedsParenthesis(span)); 47 | } 48 | 49 | pub(crate) fn err_bad_header_for_where(&mut self, span: Span, header: Token) { 50 | self.errors.push(Error::BadHeaderForWhere(span, header)); 51 | } 52 | 53 | pub(crate) fn err_bad_indentation(&mut self, span: Span) { 54 | self.errors.push(Error::BadIndentation(span)); 55 | } 56 | 57 | pub(crate) fn err_bad_default(&mut self, span: Span, other: Token) { 58 | self.errors.push(Error::BadDefault(span, other)); 59 | } 60 | 61 | pub(crate) fn err_bad_bar_indent(&mut self, span: Span, conflict: DiffConflict) { 62 | self.errors.push(Error::BadIndentForMatch(span, conflict)); 63 | } 64 | 65 | pub(crate) fn err_unmatched(&mut self, span: Span, for_: &str) { 66 | self.errors.push(Error::Unmatched(span, for_.to_string())); 67 | } 68 | 69 | pub(crate) fn err_invalid_trait_member(&mut self, span: Span) { 70 | self.errors.push(Error::InvalidTraitMember(span)); 71 | } 72 | 73 | pub(crate) fn err_invalid_nested_match(&mut self, previous: Span, new: Span) { 74 | self.errors 75 | .push(Error::InvalidNestedMatch { previous, new }); 76 | } 77 | 78 | pub(crate) fn err_conflicting_bars(&mut self, err: IndentConflict) { 79 | self.errors.push(Error::ConflictingBars(err)); 80 | } 81 | 82 | pub(crate) fn err_nested_where(&mut self, previous: Span, kw: Span) { 83 | self.errors.push(Error::NestedWhere { previous, kw }); 84 | } 85 | 86 | pub(crate) fn err_toplevel_where(&mut self, span: Span) { 87 | self.errors.push(Error::ToplevelWhere(span)); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__decl_alias.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"alias IoResult = Result int IoError\"; format! (\"\\n{}\", ast) }" 4 | --- 5 | alias IoResult = (Result int IoError) 6 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__decl_from_trait.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"\ntrait From a\n fn from as a -> self\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | type From a 7 | fn from as a -> self 8 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__decl_from_trait_optional_eq.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"\ntrait From a =\n fn from as a -> self\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | type From a 7 | fn from as a -> self 8 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__decl_impl_into.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{\n \"\nimpl Into a for b\n fn into b as b -> a =\n From:from b\n \n\";\n format! (\"\\n{}\", ast)\n}" 4 | --- 5 | impl (Into a) for b 6 | fn into b as b -> a = 7 | From:from b 8 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__decl_iterator.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{\n \"\ntrait Iterator\n type Item\n \n fn next as self -> (Item, self)\n\";\n format!(\"\\n{}\", ast)\n}" 4 | --- 5 | 6 | type Iterator 7 | type Item 8 | 9 | fn next as self -> (Item, self) 10 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__decl_map.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{\n \"\nfn map f list as (fn a -> b) [a] -> [b] = \n match list\n | x : xs -> f x : map #f xs\n | [] -> []\n\";\n format! (\"\\n{}\", ast)\n}" 4 | snapshot_kind: text 5 | --- 6 | fn map f list as (fn a -> b) [a] -> [b] = 7 | match list 8 | | (x : xs) -> f x : map #f xs 9 | | [] -> [] 10 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__decl_option.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"type Option a = Just a | None\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | type Option a = Just a | None 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__decl_param_span.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"\nparam Span can PartialEq + Eq\n\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | param Span can PartialEq + Eq 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__decl_point_multi_line.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"\ntype Point a {\n x a\n y a\n}\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | type Point a { 7 | x a 8 | y a 9 | } 10 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__decl_point_multi_line_optional_comma.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"\ntype Point a {\n x a,\n y a,\n}\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | type Point a { 7 | x a 8 | y a 9 | } 10 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__decl_point_single_line.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"type Point a { x a, y a }\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | type Point a { 7 | x a 8 | y a 9 | } 10 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__decl_use_assign.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"use std:io input_output\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | use std:io() input_output [] 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__decl_use_assign_then_expose.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{\n \"use std:io input_output [puts, Target [Stdout, Stdin], Target [..]]\";\n format!(\"\\n{}\", ast)\n}" 4 | --- 5 | 6 | use std:io() input_output [puts [], Target [Stdout, Stdin], Target [..]] 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__decl_use_expose.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"use std:io [puts, Target [Stdout, Stdin, Stderr]]\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | use std:io() [puts [], Target [Stdout, Stdin, Stderr]] 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__decl_use_simple.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"use std:io\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | use std:io() [] 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__decl_use_with_annotation.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{\n \"use ext:lumina:parser(Span as MySpan int, Output as This)\";\n format!(\"\\n{}\", ast)\n}" 4 | --- 5 | 6 | use ext:lumina:parser(Span as (MySpan int), Output as This) [] 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__decl_val.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"val LineBuffering = 0\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | val LineBuffering = 0 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__decl_val_annotated.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"val LineBuffering as *string = 0\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | val LineBuffering as *string = 0 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__decl_when_multi_line.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"\nwhen\n a can Show\n n can Add n\n\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | when 7 | a gen Show 8 | n gen (Add n) 9 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__decl_when_multi_line_optional_comma.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"\nwhen \n a can Show,\n n can Add n,\n\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | when 7 | a gen Show 8 | n gen (Add n) 9 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__decl_when_single_line.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"when a can Show, n can Add n\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | when 7 | a gen Show 8 | n gen (Add n) 9 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__expr_complex_lambda.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"(\\\\(x, y) {a, b} -> \\\\ -> 0) 2\"; format! (\"\\n{}\", ast) }" 4 | --- 5 | ((\(x, y) { a, b } -> (\ -> 0))) 2 6 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__expr_complex_lambda.snap.working: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"(\\\\(x, y) {a, b} -> \\\\ -> 0) 2\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | ((1^\(x, y) { a, b } -> (\ -> 0)) 2) 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__expr_deep_set.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"{ v | one.two.three.four @ four = 0 }\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | { v | one.two.three.four @ four = 0 } 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__expr_deep_update.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"{ v | one.two.three.four @ four = four }\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | { v | one.two.three.four @ four = four } 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__expr_field_of_access.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"{ v | one = one.two.three }\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | { v | one = one.two.three } 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__expr_if.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{\n \"if (if true then true else false) then true else if true then true else false\";\n format! (\"\\n{}\", ast)\n}" 4 | --- 5 | if 6 | ((if true 7 | then true 8 | else false)) 9 | then true 10 | else 11 | if true 12 | then true 13 | else false 14 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__expr_match.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{\n \"\n match n\n | 0 ->\n true\n | 1 -> false\n | 2 ->\n true\n\";\n format!(\"\\n{}\", ast)\n}" 4 | --- 5 | 6 | match n 7 | | 0 -> true 8 | | 1 -> false 9 | | 2 -> true 10 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__expr_match_nested.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{\n \"\nmatch (match 0 | 0 -> true | _ -> false)\n | true ->\n match 0\n | 0 -> 0\n | 1 -> 1\n | _ -> 2\n| false ->\n match 0 | false -> 0 | true -> 1\n\";\n format! (\"\\n{}\", ast)\n}" 4 | --- 5 | match 6 | (match 0 7 | | 0 -> true 8 | | _ -> false) 9 | | true -> 10 | match 0 11 | | 0 -> 0 12 | | 1 -> 1 13 | | _ -> 2 14 | | false -> 15 | match 0 16 | | false -> 0 17 | | true -> 1 18 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__expr_multiple_accessors.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"a.b.c.d\"; format! (\"\\n{}\", ast) }" 4 | --- 5 | a.b.c.d 6 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__expr_multiple_accessors_as_param.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"f a.b.c.d 0\"; format! (\"\\n{}\", ast) }" 4 | --- 5 | f a.b.c.d 0 6 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__expr_nested_constr.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"(Just (Just (Pair 10 20)))\"; format! (\"\\n{}\", ast) }" 4 | --- 5 | (Just (Just (Pair 10 20))) 6 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__expr_nested_list.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"[[[], 1], [[2], [[3, 4]], 5]]\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | [[[], 1], [[2], [[3, 4]], 5]] 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__expr_nested_tuple.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"(((x, y), (x, y), ()))\"; format! (\"\\n{}\", ast) }" 4 | --- 5 | (((x, y), (x, y), ())) 6 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__expr_pass_expr.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"map #1\"; format! (\"\\n{}\", ast) }" 4 | --- 5 | map #1 6 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__expr_pass_fn.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"map #inc\"; format! (\"\\n{}\", ast) }" 4 | --- 5 | map #inc 6 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__expr_pass_lambda.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"map #(\\\\n -> n)\"; format! (\"\\n{}\", ast) }" 4 | --- 5 | map #((\n -> n)) 6 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__expr_pass_opnum.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"#(* 2)\"; format! (\"\\n{}\", ast) }" 4 | --- 5 | #(* 2) 6 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__expr_pass_partial.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"map #(add 1)\"; format! (\"\\n{}\", ast) }" 4 | --- 5 | map #(add 1) 6 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__expr_pipe_eval_of_fn.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"1 . (f 0)\"; format! (\"\\n{}\", ast) }" 4 | --- 5 | 1 . (f 0) 6 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__expr_pipe_eval_of_lambda.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"1 . ((\\\\ -> 0))\"; format! (\"\\n{}\", ast) }" 4 | --- 5 | 1 . (((\ -> 0))) 6 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__expr_pipe_field.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"a.b . c.d . e\"; format! (\"\\n{}\", ast) }" 4 | --- 5 | a.b . c.d . e 6 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__expr_pipe_fn.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"1 . f 0\"; format! (\"\\n{}\", ast) }" 4 | --- 5 | 1 . f 0 6 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__expr_pipe_lambda.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"1 . (\\\\n -> 1)\"; format! (\"\\n{}\", ast) }" 4 | --- 5 | 1 . ((\n -> 1)) 6 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__expr_pipe_left.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"f 0 . f 1 . f 2\"; format! (\"\\n{}\", ast) }" 4 | --- 5 | f 0 . f 1 . f 2 6 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__expr_pipes.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"1 . add 2 . (\\\\n -> add n . 3)\"; format! (\"\\n{}\", ast) }" 4 | --- 5 | 1 . add 2 . ((\n -> add n . 3)) 6 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__expr_simple_annotated_record.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"{ point int | x = 1, y = 2 }\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | { (point int) | x = 1, y = 2 } 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__expr_simple_constr.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"(Just 20)\"; format! (\"\\n{}\", ast) }" 4 | --- 5 | (Just 20) 6 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__expr_simple_infered_record.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"{ x = 1, y = 2 }\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | { x = 1, y = 2 } 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__expr_simple_lambda.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"\\\\x y -> x + y\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | (\x y -> x + y) 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__expr_simple_list.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"[1,2, 3]\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | [1, 2, 3] 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__expr_simple_punned_record.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"{x, y}\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | { x, y } 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__expr_string_literal.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"\\\"hello\\\\\\\" world\\\"\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | "hello\" world" 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__expr_type_annotated_call.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{\n \"Functor(self as Option):map(a as int, b as Option float)\";\n format!(\"\\n{}\", ast)\n}" 4 | --- 5 | 6 | Functor:map(0 => (self as Option) & 1 => (a as int, b as (Option float))) 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__pat_advanced_strings.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"\\\"hello\\\" x^1 y^m:f xs\"; format! (\"\\n{}\", ast) }" 4 | --- 5 | "hello" x^1 y^m:f xs 6 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__pat_int_ranges.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"(0..10, 0..20, ..30, 30..)\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | (0..10, 0..20, ..30, 30..) 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__pat_nested_constr.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"(Just (Just (Pair 10 20)))\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | (Just (Just (Pair 10 20))) 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__pat_nested_list.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"[[[], 1], [[2], [[3, 4]], 5]]\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | [[[], 1], [[2], [[3, 4]], 5]] 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__pat_nested_tuple.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"(((x, y), (x, y), ()))\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | ((x, y), (x, y), ()) 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__pat_simple_annotated_record.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"{ point int | x = 1, y = 2 }\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | { (point int) | x = 1, y = 2 } 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__pat_simple_constr.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"(Just 20)\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | (Just 20) 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__pat_simple_infered_record.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"{ x = 1, y = 2 }\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | { x = 1, y = 2 } 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__pat_simple_list.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"[1,2, 3]\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | [1, 2, 3] 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__pat_simple_punned_record.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"{x, y}\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | { x, y } 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__pat_string_extractors.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"\\\"hello\\\" #(f 0)@a #(f 1) #f@b x xs\"; format! (\"\\n{}\", ast) }" 4 | --- 5 | "hello" #(f 0)@a #(f 1) #f@b x xs 6 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__pat_string_literal.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"\\\"hello\\\\\\\" world\\\"\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | "hello\" world" 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__pat_string_start_with_extractor.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"#(f 0)@a \\\"a\\\"\"; format! (\"\\n{}\", ast) }" 4 | --- 5 | #(f 0)@a "a" 6 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__type_annotated_trait.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"std:Iterator(Item as Option int)\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | std:Iterator(1 => (Item as (Option int))) 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__type_bool.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"bool\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | bool 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__type_floats.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"Con f32 f64\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | (Con f32 f64) 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__type_fnptr.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"(fnptr a -> b)\"; format! (\"\\n{}\", ast) }" 4 | snapshot_kind: text 5 | --- 6 | (fnptr a -> b) 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__type_ints.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"Con int uint i8 i64 u8 u64\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | (Con int uint i8 i64 u8 u64) 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__type_nested_higher_order.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{\n \"(fn (fnptr a b -> c) (fn d -> e) -> (fn f -> (fn -> g)))\"; format!\n (\"\\n{}\", ast)\n}" 4 | snapshot_kind: text 5 | --- 6 | (fn (fnptr a b -> c) (fn d -> e) -> (fn f -> (fn g))) 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__type_nested_tuple.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"((((a, b), c)), d)\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | (((a, b), c), d) 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__type_ptr.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"(*string, *(fnptr *int), ****int)\"; format! (\"\\n{}\", ast) }" 4 | snapshot_kind: text 5 | --- 6 | (*string, *(fnptr *int), ****int) 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__type_simple_closure.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"(fn a -> b)\"; format! (\"\\n{}\", ast) }" 4 | snapshot_kind: text 5 | --- 6 | (fn a -> b) 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__type_simple_closure_ret_sugar.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"(fn -> b)\"; format! (\"\\n{}\", ast) }" 4 | snapshot_kind: text 5 | --- 6 | (fn b) 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__type_simple_defined.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"Con int\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | (Con int) 7 | -------------------------------------------------------------------------------- /lumina-parser/src/snapshots/lumina_parser__tests__type_simple_tuple.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-parser/src/tests.rs 3 | expression: "{ \"(a, b)\"; format!(\"\\n{}\", ast) }" 4 | --- 5 | 6 | (a, b) 7 | -------------------------------------------------------------------------------- /lumina-parser/src/val.rs: -------------------------------------------------------------------------------- 1 | use super::{select, Expr, Parser, Type, T}; 2 | use lumina_util::{Highlighting, Span, Tr}; 3 | use std::fmt; 4 | 5 | #[derive(Debug)] 6 | pub struct Declaration<'a> { 7 | pub span: Span, 8 | pub name: &'a str, 9 | pub type_: Option>>, 10 | pub value: Tr>, 11 | pub public: bool, 12 | } 13 | 14 | impl<'a> Parser<'a> { 15 | pub fn val(&mut self) -> Option> { 16 | let name = self.expect_name("value declaration")?; 17 | 18 | select! { self, "a value or type annotation"; 19 | T::Equal => self.finalize_val(name, None), 20 | T::As => { 21 | let type_ = self.type_with_params()?; 22 | self.expect(T::Equal) 23 | .and_then(|_| self.finalize_val(name, Some(type_))) 24 | } 25 | } 26 | } 27 | 28 | fn finalize_val( 29 | &mut self, 30 | name: Tr<&'a str>, 31 | type_: Option>>, 32 | ) -> Option> { 33 | self.expr().map(|value| Declaration { 34 | name: *name, 35 | type_, 36 | span: name.span.extend(value.span), 37 | value, 38 | public: false, 39 | }) 40 | } 41 | } 42 | 43 | impl<'a> fmt::Display for Declaration<'a> { 44 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 45 | let val = "val".keyword(); 46 | let as_ = "as".keyword(); 47 | 48 | let type_ = match &self.type_ { 49 | Some(t) => format!(" {as_} {}", t.type_()), 50 | None => "".into(), 51 | }; 52 | write!(f, "{val} {}{type_} = {}", self.name, self.value) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lumina-parser/src/when.rs: -------------------------------------------------------------------------------- 1 | use super::{select, Parser, Type, T}; 2 | use itertools::Itertools; 3 | use lumina_util::{Highlighting, Span, Tr}; 4 | use std::fmt; 5 | 6 | #[derive(Clone, Debug, Default)] 7 | pub struct Constraints<'a> { 8 | pub generics: Vec<(Span, &'a str, Vec>>)>, 9 | } 10 | 11 | impl<'a> Constraints<'a> { 12 | pub fn empty() -> Self { 13 | Self { generics: vec![] } 14 | } 15 | } 16 | 17 | impl<'a> Parser<'a> { 18 | pub fn when(&mut self) -> Option> { 19 | let mut generics = vec![]; 20 | 21 | loop { 22 | match self.bind() { 23 | Some(bind) => generics.push(bind), 24 | None => { 25 | self.recover_for([T::Comma, T::NewLines], true); 26 | } 27 | } 28 | 29 | match self.lexer.peek_line_sensitive().0 { 30 | T::NewLines | T::Comma => { 31 | self.progress(); 32 | if self.lexer.peek().0 != T::Path { 33 | break Some(Constraints { generics }); 34 | } 35 | } 36 | _ => break Some(Constraints { generics }), 37 | } 38 | } 39 | } 40 | 41 | fn bind(&mut self) -> Option<(Span, &'a str, Vec>>)> { 42 | let name = self.expect_name("generic to constrain")?; 43 | self.expect(T::Can)?; 44 | self.traits_for_can() 45 | .map(|(cons, end)| (name.span.extend(end), name.value, cons)) 46 | } 47 | 48 | pub fn traits_for_can(&mut self) -> Option<(Vec>>, Span)> { 49 | let mut constraints = vec![]; 50 | loop { 51 | match self.type_newline_sensitive() { 52 | Some(t) => constraints.push(t), 53 | None => { 54 | self.recover_for([T::NewLines, T::Operator, T::Comma], true); 55 | } 56 | }; 57 | let src = self.lexer.source(); 58 | select! { self, "`+`, new line, or next item", span sensitive: true peeked: true; 59 | T::NewLines | T::Equal => break Some((constraints, span.move_indice(-1))), 60 | T::Comma => break Some((constraints, span)), 61 | T::EOF => break Some((constraints, span.move_indice(-1))), 62 | T::Operator if span.get_str(src) == "+" => { 63 | self.progress(); 64 | continue 65 | }, 66 | t if t.is_header() => break Some((constraints, span.move_indice(-1))), 67 | } 68 | } 69 | } 70 | } 71 | 72 | impl<'a> fmt::Display for Constraints<'a> { 73 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 74 | if self.generics.is_empty() { 75 | return Ok(()); 76 | } 77 | 78 | writeln!(f, "{}", "when".keyword())?; 79 | 80 | self.generics 81 | .iter() 82 | .format_with("\n", |(_, gen, cons), f| { 83 | f(&format_args!( 84 | " {gen} {} {}", 85 | "gen".keyword(), 86 | cons.iter().format(" + ") 87 | )) 88 | }) 89 | .fmt(f) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /lumina-rvsdg/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lumina-rvsdg" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | lumina-collections = { path = "../lumina-collections" } 8 | derive-new = "0.7" 9 | derive_more = {version = "*", features = ["full"]} 10 | -------------------------------------------------------------------------------- /lumina-rvsdg/src/tests.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[test] 4 | fn example() { 5 | let int = || Value::Meta(Test::Int); 6 | 7 | Node::::omega(|region| { 8 | // val x = 1 + 2 9 | let constant = region.delta(&[], int(), |region| { 10 | let one = region.int(int(), 1); 11 | let two = region.int(int(), 2); 12 | let add = region.add(int()); 13 | 14 | region.connect(Origin::output(one, 0), User::input(add, 0)); 15 | region.connect(Origin::output(two, 0), User::input(add, 1)); 16 | 17 | region.connect(Origin::output(add, 0), User::result(0)); 18 | }); 19 | 20 | // fn f0 v = f1 (v + 1) 21 | // fn f1 v = f0 (v - 1) 22 | let funs = region.phi( 23 | [ 24 | RegionSignature::from(vec![int()], vec![int()]), 25 | RegionSignature::from(vec![int()], vec![int()]), 26 | ], 27 | |i, lambdas, region| { 28 | let one = Node::int(int(), 1); 29 | let one = region.nodes.push(one); 30 | 31 | let operation = match i { 32 | 0 => Node::add(int()), 33 | _ => Node::sub(int()), 34 | }; 35 | let operation = region.nodes.push(operation); 36 | 37 | region.connect(Origin::output(one, 0), User::input(operation, 0)); 38 | region.connect(Origin::arg(0), User::input(operation, 1)); 39 | 40 | // Call the other function 41 | let apply = region.apply(lambdas[0], &[Origin::output(operation, 0)]); 42 | 43 | region.connect(Origin::output(apply, 0), User::result(0)); 44 | }, 45 | ); 46 | 47 | // fn main = f0 x 48 | let main = region.lambda( 49 | true, 50 | &[Origin::output(funs, 0), Origin::output(constant, 0)], 51 | &[], 52 | [int()], 53 | |region, _| { 54 | let f0 = Origin::arg(0); 55 | let constant = Origin::arg(1); 56 | let apply = region.apply(f0, &[constant]); 57 | region.connect(Origin::output(apply, 0), User::result(0)) 58 | }, 59 | ); 60 | 61 | region.connect(Origin::output(main, 0), User::result(0)); 62 | }) 63 | .verify(); 64 | } 65 | 66 | #[derive(Clone, Copy, Debug, PartialEq)] 67 | enum Test { 68 | Int, 69 | Unit, 70 | } 71 | -------------------------------------------------------------------------------- /lumina-rvsdg/src/verifier.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | impl Node { 4 | pub fn verify(&self) { 5 | match &self.kind { 6 | NodeKind::Simple(simple_node_kind) => match simple_node_kind { 7 | SimpleNodeKind::Const(_) => { 8 | expect_len(0, "regions", self.regions.len(), "const"); 9 | expect_len(0, "inputs", self.sig.inputs.len(), "const"); 10 | } 11 | SimpleNodeKind::Unit => { 12 | expect_len(0, "regions", self.regions.len(), "unit"); 13 | expect_len(0, "inputs", self.sig.inputs.len(), "unit"); 14 | } 15 | SimpleNodeKind::Apply => { 16 | expect_len(0, "regions", self.regions.len(), "apply"); 17 | assert!( 18 | self.sig.inputs.len() > 0, 19 | "apply must have at least lambda input" 20 | ); 21 | } 22 | SimpleNodeKind::Add => { 23 | expect_len(0, "regions", self.regions.len(), "add"); 24 | expect_len(2, "inputs", self.sig.inputs.len(), "add"); 25 | } 26 | SimpleNodeKind::Sub => { 27 | expect_len(0, "regions", self.regions.len(), "sub"); 28 | expect_len(2, "inputs", self.sig.inputs.len(), "sub"); 29 | } 30 | }, 31 | NodeKind::Structural(structural_node_kind) => match structural_node_kind { 32 | StructuralNodeKind::Gamma => {} 33 | StructuralNodeKind::Theta => {} 34 | StructuralNodeKind::Lambda { .. } => {} 35 | StructuralNodeKind::Delta => expect_len(1, "regions", self.regions.len(), "delta"), 36 | StructuralNodeKind::Phi => { 37 | self.regions.values().for_each(|region| { 38 | assert!( 39 | region.sig.arguments.len() 40 | == self.sig.inputs.len() + self.regions.len() 41 | ); 42 | }); 43 | 44 | self.sig 45 | .outputs 46 | .values() 47 | .for_each(|v| assert!(matches!(v, Value::Lambda(..)))); 48 | } 49 | StructuralNodeKind::Omega => {} 50 | }, 51 | } 52 | 53 | self.regions.values().for_each(|region| { 54 | assert!( 55 | self.sig.inputs.len() >= region.sig.arguments.len(), 56 | "node input not propegated to region arguments" 57 | ); 58 | 59 | region.verify() 60 | }); 61 | } 62 | 63 | fn as_lambda(&self) { 64 | match self.kind { 65 | NodeKind::Structural(StructuralNodeKind::Lambda { dependencies }) => { 66 | // &self.regions[RegionKey(0)].sig 67 | // todo!(); 68 | } 69 | _ => panic!("not a lambda"), 70 | } 71 | } 72 | } 73 | 74 | fn expect_len(exp: usize, kind_: &str, got: usize, for_: &str) { 75 | if got != exp { 76 | panic!("expected {exp} {kind_} (not {got}) for {for_}"); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /lumina-tokentree/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lumina-tokentree" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | lumina-util = { path = "../lumina-util" } 8 | logos = "*" 9 | itertools = "*" 10 | insta = "*" 11 | tracing = "*" 12 | bitflags = "*" 13 | take_mut = "*" 14 | -------------------------------------------------------------------------------- /lumina-tokentree/src/block.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | impl<'s> Parser<'s> { 4 | pub fn handle_whereblock_if_not_expected( 5 | &mut self, 6 | allow_missing: bool, 7 | span: Span, 8 | ) -> Entity<'s> { 9 | for ctx in self.ctx.iter().rev() { 10 | match ctx { 11 | Context::Header("fn" | "fnptr") => { 12 | return self.next_then(|this| this.indent_block(span)) 13 | } 14 | // If we're inside a clause then this is an invalid but still an parseable where binding set 15 | Context::Clause(_) => return self.next_then(|this| this.indent_block(span)), 16 | 17 | Context::Operators(_) 18 | | Context::InMatch(_) 19 | | Context::Indent(_) 20 | | Context::Header(_) => break, 21 | } 22 | } 23 | 24 | if !allow_missing { 25 | return self.next_then(|this| this.indent_block(span)); 26 | } 27 | 28 | assert!(allow_missing); 29 | Entity::Missing 30 | } 31 | 32 | pub fn indent_block(&mut self, span: Span) -> Entity<'s> { 33 | let name = self.take(span); 34 | self.ctx.push(Context::Indent(name)); 35 | let src = self.lexer.source(); 36 | 37 | let mut buf = vec![]; 38 | 39 | let finalize = |this: &mut Self, buf| { 40 | assert!(matches!(this.ctx.pop().unwrap(), Context::Indent(..))); 41 | return Entity::IndentBlock(name.tr(span), buf); 42 | }; 43 | 44 | loop { 45 | let token = self.lexer.peek(); 46 | 47 | if token.kind == TokenKind::EOF { 48 | return finalize(self, buf); 49 | } 50 | 51 | let where_indent = get_indent_level(src, span, false).unwrap(); 52 | if let Some(indent) = get_indent_level(src, token.span, true) { 53 | if indent <= where_indent { 54 | return finalize(self, buf); 55 | } 56 | } 57 | // No need to handle `None` because we allow `where fn ...` 58 | 59 | let declaration = self.next(false); 60 | match &declaration.kind { 61 | Entity::EOF => todo!(), 62 | _ => {} 63 | } 64 | buf.push(declaration); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lumina-tokentree/src/meta.rs: -------------------------------------------------------------------------------- 1 | use lumina_util::{Span, Tr}; 2 | use std::fmt; 3 | use std::ops::Deref; 4 | 5 | // TODO: use smarter data compression for this kind of thing 6 | #[derive(Clone, Copy)] 7 | pub struct Meta { 8 | pub kind: T, 9 | pub span: Span, 10 | pub comment: Span, 11 | } 12 | 13 | impl Meta { 14 | pub fn new(data: T, span: Span, comment: Span) -> Self { 15 | Self { kind: data, span, comment } 16 | } 17 | 18 | pub fn n(data: T, span: Span) -> Self { 19 | Self { kind: data, span, comment: Span::null() } 20 | } 21 | 22 | pub fn get_comment<'s>(&self, src: &'s str) -> Option<&'s str> { 23 | (self.comment != Span::null()).then(|| self.comment.get_str(src)) 24 | } 25 | 26 | pub fn map(self, f: impl FnOnce(T) -> U) -> Meta { 27 | Meta { kind: f(self.kind), span: self.span, comment: self.comment } 28 | } 29 | 30 | pub fn union(self, other: Meta, f: impl FnOnce(Meta, Meta) -> O) -> Meta { 31 | let span = self.span.extend(other.span); 32 | let comment = if self.comment == Span::null() { 33 | other.comment 34 | } else { 35 | self.comment.extend(other.comment) 36 | }; 37 | let kind = f(self, other); 38 | Meta { kind, span, comment } 39 | } 40 | 41 | pub fn without_comment(&self) -> Meta<&T> { 42 | let mut v = self.as_ref(); 43 | v.comment = Span::null(); 44 | v 45 | } 46 | 47 | pub fn as_ref(&self) -> Meta<&T> { 48 | Meta { kind: &self.kind, span: self.span, comment: self.comment } 49 | } 50 | } 51 | 52 | impl Deref for Meta { 53 | type Target = T; 54 | 55 | fn deref(&self) -> &Self::Target { 56 | &self.kind 57 | } 58 | } 59 | 60 | impl PartialEq for Meta { 61 | fn eq(&self, other: &Self) -> bool { 62 | self.kind.eq(&other.kind) 63 | } 64 | } 65 | 66 | impl Eq for Meta {} 67 | 68 | impl fmt::Display for Meta { 69 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 70 | if self.comment != Span::null() { 71 | write!(f, "{}({:?})", self.kind, self.comment) 72 | } else { 73 | Tr::new(self.span, &self.kind).fmt(f) 74 | } 75 | } 76 | } 77 | 78 | impl fmt::Debug for Meta { 79 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 80 | if self.comment != Span::null() { 81 | write!(f, "{:?}({:?})", self.kind, self.comment) 82 | } else { 83 | Tr::new(self.span, &self.kind).fmt(f) 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /lumina-tokentree/src/regroup.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | // I think the easiest is to just make `can` an identifier and then split_on for the entities 4 | 5 | impl<'s> Entity<'s> { 6 | pub fn for_double_sided_operator<'a>( 7 | mut lhs: Meta<&'a Entity<'s>>, 8 | parts: &'a [Meta>], 9 | mut f: impl FnMut(Meta<&Entity<'s>>, &[Meta>]), 10 | ) { 11 | for (i, part) in parts.iter().enumerate() { 12 | match &part.kind { 13 | Entity::Sequence(elems) => { 14 | let (next_lhs, xs) = elems.split_last().unwrap(); 15 | f(lhs, xs); 16 | lhs = next_lhs.as_ref(); 17 | } 18 | _ => { 19 | if i == parts.len() - 1 { 20 | f(lhs, &[part.clone()]) 21 | } else { 22 | f(lhs, &[Meta::n(Entity::Missing, Span::null())]); 23 | lhs = part.as_ref(); 24 | } 25 | } 26 | } 27 | } 28 | } 29 | } 30 | 31 | // Let's try handling this in TT construction instead. 32 | // impl<'s> Entity<'s> { 33 | // pub fn regroup_when_bindings(entity: Entity<'s>) -> Vec> { 34 | // match entity { 35 | // Entity::Sequence(elems) => { 36 | // let mut elems = elems.into_iter(); 37 | // while let Some(lhs) = elems.next() { 38 | // match elems.next() { 39 | // Some(Tr { 40 | // value: Entity::Header(Tr { value: "can", .. }, and_then), 41 | // .. 42 | // }) => { 43 | // todo!() 44 | // } 45 | // _ => todo!(), 46 | // } 47 | // // let seq = vec![lhs]; 48 | // 49 | // // while let Some(elem) = elems.next() { 50 | // // match &elem.value { 51 | // // Entity::Can 52 | // // } 53 | // // } 54 | // 55 | // todo!(); 56 | // } 57 | // 58 | // todo!(); 59 | // } 60 | // _ => vec![entity], 61 | // } 62 | // } 63 | // } 64 | -------------------------------------------------------------------------------- /lumina-tokentree/src/snapshots/lumina_tokentree__tests__nested_match.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-tokentree/src/tests.rs 3 | expression: entity 4 | snapshot_kind: text 5 | --- 6 | Header(match) 7 | Operators(3) 8 | 0 9 | Op(0, |) 10 | [ outer0 11 | , Header(->) 12 | r0 13 | ] 14 | Op(1, |) 15 | [ outer1 16 | , Header(->) 17 | Header(match) 18 | Operators(1) 19 | 1 20 | Op(0, |) 21 | Operators(1) 22 | [ inner0 23 | , Header(->) 24 | 10 25 | ] 26 | Op(0, |) 27 | [ inner1 28 | , Header(->) 29 | 11 30 | ] 31 | ] 32 | Op(2, |) 33 | [ outer2 34 | , Header(->) 35 | Header(match) 36 | Operators(1) 37 | 2 38 | Op(0, |) 39 | Operators(1) 40 | [ inner0 41 | , Header(->) 42 | 20 43 | ] 44 | Op(0, |) 45 | [ inner1 46 | , Header(->) 47 | 21 48 | ] 49 | ] 50 | -------------------------------------------------------------------------------- /lumina-tokentree/src/snapshots/lumina_tokentree__tests__precedence.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina-tokentree/src/tests.rs 3 | expression: entity 4 | snapshot_kind: text 5 | --- 6 | [ 1 7 | , Header(=) 8 | Operators(2) 9 | 2 10 | Op(0, ,) 11 | Operators(1) 12 | 3 13 | Op(0, .) 14 | Operators(1) 15 | Operators(2) 16 | 4 17 | Op(0, +) 18 | 4 19 | Op(1, +) 20 | 5 21 | Op(0, *) 22 | 5 23 | Op(1, ,) 24 | Operators(1) 25 | 6 26 | Op(0, +) 27 | 6 28 | ] 29 | -------------------------------------------------------------------------------- /lumina-tokentree/src/tests.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use insta; 3 | 4 | macro_rules! tree { 5 | ($name:ident, $src:literal) => { 6 | #[test] 7 | fn $name() { 8 | lumina_util::test_logger(); 9 | let mut parser = Parser::new($src); 10 | let entity = parser.next(false); 11 | insta::assert_snapshot!(entity); 12 | } 13 | }; 14 | } 15 | 16 | tree!(precedence, "1 = 2, 3 . 4 + 4 + 5 * 5, 6 + 6"); 17 | tree!( 18 | nested_match, 19 | "match 0 20 | | outer0 -> r0 21 | | outer1 -> 22 | match 1 23 | | inner0 -> 10 24 | | inner1 -> 11 25 | | outer2 -> 26 | match 2 27 | | inner0 -> 20 28 | | inner1 -> 21 29 | " 30 | ); 31 | -------------------------------------------------------------------------------- /lumina-typesystem/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lumina-typesystem" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | itertools = "*" 10 | lumina-util = { path = "../lumina-util" } 11 | lumina-key = { path = "../lumina-key" } 12 | lumina-collections = { path = "../lumina-collections" } 13 | derive_more = { version = "1.0.*", features = ["from", "display", "index", "index_mut", "deref", "deref_mut"] } 14 | derive-new = "0.7" 15 | tracing = "0.1.41" 16 | smallvec = "*" 17 | -------------------------------------------------------------------------------- /lumina-typesystem/src/intsize.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | #[derive(PartialEq, Eq, Copy, Clone, Debug, PartialOrd, Ord, Hash)] 4 | pub struct IntSize { 5 | pub signed: bool, 6 | bits: u8, 7 | } 8 | 9 | impl IntSize { 10 | pub const fn new(signed: bool, bits: u8) -> Self { 11 | assert!(bits < 67, "int sizes larger than 64 is not supported"); 12 | IntSize { signed, bits } 13 | } 14 | 15 | pub fn bytes(self) -> u8 { 16 | self.bits / 8 17 | } 18 | 19 | pub fn bits(self) -> u8 { 20 | self.bits 21 | } 22 | 23 | pub fn max_value(self) -> u64 { 24 | let max = (1 as u64) 25 | .checked_shl(self.bits() as u32) 26 | .map(|n| n - 1) 27 | .unwrap_or(u64::MAX); 28 | 29 | if self.signed { 30 | max / 2 31 | } else { 32 | max 33 | } 34 | } 35 | 36 | pub fn min_value(&self) -> i64 { 37 | if self.signed { 38 | (-(self.max_value() as i64)) - 1 39 | } else { 40 | 0 41 | } 42 | } 43 | 44 | pub fn minimum_for(signed: bool, n: u128) -> IntSize { 45 | if signed { 46 | [ 47 | (i8::MAX as u128, 8), 48 | (i16::MAX as u128, 16), 49 | (i32::MAX as u128, 32), 50 | (i64::MAX as u128, 64), 51 | ] 52 | } else { 53 | [ 54 | (u8::MAX as u128, 8), 55 | (u16::MAX as u128, 16), 56 | (u32::MAX as u128, 32), 57 | (u64::MAX as u128, 64), 58 | ] 59 | } 60 | .iter() 61 | .find_map(|&(m, b)| (n < m).then(|| IntSize::new(signed, b))) 62 | .unwrap_or(IntSize::new(signed, 64)) 63 | } 64 | 65 | pub fn fmt_with_default(&self, f: &mut fmt::Formatter, default: u8) -> fmt::Result { 66 | if self.bits == default { 67 | if self.signed { 68 | write!(f, "int") 69 | } else { 70 | write!(f, "uint") 71 | } 72 | } else { 73 | write!(f, "{}{}", if self.signed { 'i' } else { 'u' }, self.bits) 74 | } 75 | } 76 | } 77 | 78 | impl fmt::Display for IntSize { 79 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 80 | self.fmt_with_default(f, u8::MAX) 81 | } 82 | } 83 | 84 | #[cfg(test)] 85 | mod tests { 86 | use super::IntSize; 87 | 88 | #[test] 89 | fn constraints() { 90 | let size = IntSize::new(true, 64); 91 | assert_eq!(size.max_value(), i64::MAX as u64); 92 | assert_eq!(size.min_value(), i64::MIN); 93 | 94 | let size = IntSize::new(false, 64); 95 | assert_eq!(size.max_value(), u64::MAX); 96 | assert_eq!(size.min_value(), u64::MIN as i64); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /lumina-util/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lumina-util" 3 | version = "0.1.0" 4 | authors = ["Simon "] 5 | edition = "2021" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | owo-colors = { version = "4.0.*", features = ["supports-colors"] } 11 | itertools = "0.10.*" 12 | tinyvec = { version = "1.8.0", features = ["alloc"] } 13 | tracing-subscriber = { version = "0.3.19", features = ["env-filter"] } 14 | tracing-tree = "0.4.0" 15 | tracing = "0.1.41" 16 | -------------------------------------------------------------------------------- /lumina-util/src/helpers.rs: -------------------------------------------------------------------------------- 1 | use itertools::Itertools; 2 | use std::fmt; 3 | 4 | pub struct Indented(T); 5 | 6 | pub trait Indentation: fmt::Display + Sized { 7 | fn indent(&self) -> Indented<&Self> { 8 | Indented(self) 9 | } 10 | } 11 | 12 | impl Indentation for T {} 13 | 14 | impl fmt::Display for Indented { 15 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 16 | write!(f, "{}", self.0.to_string().lines().format("\n ")) 17 | } 18 | } 19 | 20 | pub struct ParamFmt<'a, H, T> { 21 | sep: &'static str, 22 | clause: bool, 23 | header: &'a H, 24 | params: &'a [T], 25 | } 26 | 27 | impl<'a, H, T> ParamFmt<'a, H, T> { 28 | pub fn new(header: &'a H, params: &'a [T]) -> Self { 29 | Self { sep: " ", clause: true, header, params } 30 | } 31 | 32 | pub fn no_clause(mut self) -> Self { 33 | self.clause = false; 34 | self 35 | } 36 | } 37 | 38 | impl<'a, H: fmt::Display, T: fmt::Display> fmt::Display for ParamFmt<'a, H, T> { 39 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 40 | if self.params.is_empty() { 41 | self.header.fmt(f) 42 | } else { 43 | if self.clause { 44 | '('.fmt(f)?; 45 | } 46 | write!(f, "{} {}", self.header, self.params.iter().format(self.sep))?; 47 | if self.clause { 48 | ')'.fmt(f)?; 49 | } 50 | Ok(()) 51 | } 52 | } 53 | } 54 | 55 | impl<'a, H: fmt::Display, T: fmt::Debug> fmt::Debug for ParamFmt<'a, H, T> { 56 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 57 | if self.params.is_empty() { 58 | self.header.fmt(f) 59 | } else { 60 | if self.clause { 61 | '('.fmt(f)?; 62 | } 63 | write!( 64 | f, 65 | "{} {:?}", 66 | self.header, 67 | self.params.iter().format(self.sep) 68 | )?; 69 | if self.clause { 70 | ')'.fmt(f)?; 71 | } 72 | Ok(()) 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /lumina-util/src/highlight.rs: -------------------------------------------------------------------------------- 1 | use owo_colors::{colors, styles::DimDisplay, FgColorDisplay, OwoColorize}; 2 | use std::fmt; 3 | use std::sync::atomic::{AtomicBool, Ordering}; 4 | 5 | pub fn enable_highlighting(enabled: bool) { 6 | owo_colors::set_override(enabled); 7 | IS_ENABLED.store(enabled, Ordering::Relaxed); 8 | } 9 | 10 | pub static IS_ENABLED: AtomicBool = AtomicBool::new(true); 11 | 12 | pub enum Highlighted { 13 | Off(T), 14 | On(F), 15 | } 16 | 17 | fn highlight(v: T, f: fn(T) -> F) -> Highlighted { 18 | if IS_ENABLED.load(Ordering::Relaxed) { 19 | Highlighted::On(f(v)) 20 | } else { 21 | Highlighted::Off(v) 22 | } 23 | } 24 | 25 | /// Provides helper methods for consistent color highlighting across modules 26 | pub trait Highlighting: Sized { 27 | fn keyword(&self) -> Highlighted<&Self, FgColorDisplay<'_, colors::Green, Self>> { 28 | highlight(self, |a| a.green()) 29 | } 30 | 31 | fn numeric(&self) -> Highlighted<&Self, FgColorDisplay<'_, colors::Magenta, Self>> { 32 | highlight(self, |a| a.magenta()) 33 | } 34 | 35 | fn path(&self) -> &Self { 36 | self 37 | } 38 | 39 | fn type_(&self) -> Highlighted<&Self, FgColorDisplay<'_, colors::Magenta, Self>> { 40 | highlight(self, |a| a.magenta()) 41 | } 42 | 43 | fn symbol(&self) -> Highlighted<&Self, FgColorDisplay<'_, colors::Cyan, Self>> { 44 | highlight(self, |a| a.cyan()) 45 | } 46 | 47 | fn comment(&self) -> Highlighted<&Self, DimDisplay<'_, Self>> { 48 | highlight(self, |a| a.dimmed()) 49 | } 50 | } 51 | 52 | impl Highlighting for T {} 53 | 54 | impl fmt::Display for Highlighted { 55 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 56 | match self { 57 | Highlighted::On(a) => a.fmt(f), 58 | Highlighted::Off(a) => a.fmt(f), 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lumina-util/src/ignored.rs: -------------------------------------------------------------------------------- 1 | // A simple wrapper type that causes the interior value to be ignored by `PartialEq` and `Hash` 2 | // derives. Useful for keeping metadata in hashmap keys. 3 | #[derive(Debug, Clone, Default, Copy)] 4 | pub struct Ignored { 5 | pub inner: T, 6 | } 7 | 8 | impl Ignored { 9 | pub const fn new(inner: T) -> Self { 10 | Self { inner } 11 | } 12 | } 13 | 14 | impl PartialEq for Ignored { 15 | fn eq(&self, _: &Self) -> bool { 16 | true 17 | } 18 | } 19 | impl Eq for Ignored {} 20 | impl std::hash::Hash for Ignored { 21 | fn hash(&self, _: &mut H) {} 22 | } 23 | 24 | impl std::ops::Deref for Ignored { 25 | type Target = T; 26 | 27 | fn deref(&self) -> &T { 28 | &self.inner 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lumina-util/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod location; 2 | pub use location::Identifier; 3 | 4 | mod ignored; 5 | pub use ignored::*; 6 | 7 | mod error; 8 | pub use error::*; 9 | 10 | mod highlight; 11 | pub use highlight::*; 12 | 13 | mod span; 14 | pub use span::*; 15 | 16 | mod helpers; 17 | pub use helpers::{Indentation, ParamFmt}; 18 | 19 | mod test_logger; 20 | pub use test_logger::test_logger; 21 | -------------------------------------------------------------------------------- /lumina-util/src/test_logger.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Once; 2 | use tracing_subscriber::{layer::SubscriberExt, registry::Registry, EnvFilter}; 3 | 4 | static TRACING_INIT: Once = Once::new(); 5 | 6 | pub fn test_logger() { 7 | TRACING_INIT.call_once(|| { 8 | let filter = EnvFilter::from_default_env(); 9 | 10 | let layer = tracing_tree::HierarchicalLayer::default() 11 | .with_writer(std::io::stdout) 12 | .with_indent_lines(true) 13 | .with_indent_amount(2) 14 | .with_verbose_entry(false) 15 | .with_verbose_exit(false) 16 | .with_targets(true); 17 | 18 | let subscriber = Registry::default().with(layer).with(filter); 19 | 20 | tracing::subscriber::set_global_default(subscriber).unwrap(); 21 | }); 22 | } 23 | -------------------------------------------------------------------------------- /lumina/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lumina" 3 | version = "0.1.0" 4 | authors = ["Simon "] 5 | edition = "2021" 6 | 7 | [dependencies] 8 | lumina-util = { path = "../lumina-util" } 9 | lumina-key = { path = "../lumina-key" } 10 | lumina-compiler = { path = "../lumina-compiler" } 11 | lumina-tokentree = { path = "../lumina-tokentree" } 12 | itertools = "*" 13 | clap = { version = "4.5.*", features = ["derive"] } 14 | directories = "4.0.*" 15 | tracing = "0.1.41" 16 | tracing-subscriber = { version = "0.3.19", features = ["env-filter"] } 17 | tracing-tree = "0.4.0" 18 | target-lexicon = "0.13.0" 19 | insta = "*" 20 | -------------------------------------------------------------------------------- /lumina/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Exports for integration tests 2 | 3 | mod build; 4 | pub use build::{build_project, run_built_binary}; 5 | pub mod cli; 6 | pub mod formatter; 7 | use lumina_util::test_logger; 8 | use std::path::PathBuf; 9 | 10 | pub fn run(path: &str) -> std::process::Output { 11 | test_logger(); 12 | 13 | let manifest = env!("CARGO_MANIFEST_DIR"); 14 | 15 | let environment = crate::cli::Environment { 16 | current_directory: PathBuf::from(format!("{manifest}/../{path}")), 17 | lumina_directory: PathBuf::from(format!("{manifest}/../luminapath")), 18 | }; 19 | 20 | let buildflags = crate::cli::BuildFlags { 21 | target: None, 22 | epanic: true, 23 | output: None, 24 | super_debug: false, 25 | project: Some(environment.current_directory.clone()), 26 | }; 27 | 28 | match build_project(environment, true, buildflags) { 29 | Ok(binary) => { 30 | let output = std::process::Command::new(binary) 31 | .output() 32 | .expect("failed to run produced binary"); 33 | 34 | if !output.stderr.is_empty() { 35 | println!("{}", String::from_utf8_lossy(&output.stderr)); 36 | } 37 | 38 | output 39 | } 40 | Err(code) => panic!("running project {path} failed with status code {code:#?}"), 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lumina/src/main.rs: -------------------------------------------------------------------------------- 1 | use clap::Parser; 2 | use std::process::ExitCode; 3 | use tracing::info; 4 | use tracing_subscriber::{layer::SubscriberExt, registry::Registry, EnvFilter}; 5 | use tracing_tree; 6 | 7 | mod build; 8 | use build::{build_project, run_built_binary}; 9 | mod cli; 10 | mod formatter; 11 | mod init; 12 | 13 | fn init_logger() { 14 | let filter = EnvFilter::from_default_env(); 15 | 16 | let layer = tracing_tree::HierarchicalLayer::default() 17 | .with_writer(std::io::stdout) 18 | .with_indent_lines(true) 19 | .with_indent_amount(2) 20 | .with_verbose_entry(false) 21 | .with_verbose_exit(false) 22 | .with_targets(true); 23 | 24 | let subscriber = Registry::default().with(layer).with(filter); 25 | 26 | tracing::subscriber::set_global_default(subscriber).unwrap(); 27 | } 28 | 29 | fn main() -> ExitCode { 30 | init_logger(); 31 | 32 | info!("parsing command line arguments"); 33 | let cli = cli::Cli::parse_from(std::env::args().take_while(|arg| arg != "--")); 34 | 35 | info!("initialising lumina environment"); 36 | let env = cli::Environment::parse(); 37 | 38 | let run_output = matches!(&cli.command, cli::Commands::Run(..)); 39 | 40 | match cli.command { 41 | cli::Commands::Init(settings) => init::create_new_lumina_project(settings), 42 | cli::Commands::Run(settings) | cli::Commands::Build(settings) => { 43 | match build_project(env, run_output, settings) { 44 | Ok(output) if run_output => run_built_binary(&output), 45 | Ok(_) => ExitCode::SUCCESS, 46 | Err(code) => code, 47 | } 48 | } 49 | cli::Commands::Fmt(settings) => formatter::run(env, settings), 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lumina/tests/examples.rs: -------------------------------------------------------------------------------- 1 | fn run(path: &str, expected: &str) { 2 | let output = lumina::run(path); 3 | 4 | if output.stdout != expected.as_bytes() { 5 | panic!( 6 | "unexpected output from example:\n expected:\n{expected}\n got:\n{}", 7 | String::from_utf8_lossy(&output.stdout) 8 | ); 9 | } 10 | 11 | if !output.status.success() { 12 | panic!("non-success error code: {}", output.status); 13 | } 14 | } 15 | 16 | #[test] 17 | fn example_hello_world() { 18 | run("examples/hello-world", "Hello World!\n"); 19 | } 20 | 21 | #[test] 22 | fn example_lists() { 23 | run("examples/lists", "90\n"); 24 | } 25 | 26 | #[test] 27 | fn example_maybe_implementation() { 28 | run("examples/maybe-implementation", "Just 7\n"); 29 | } 30 | 31 | #[test] 32 | fn example_operators() { 33 | run("examples/operators", "18\n"); 34 | } 35 | 36 | #[test] 37 | fn example_records() { 38 | run("examples/records", "(false, true)\n"); 39 | } 40 | 41 | #[test] 42 | fn example_tuples() { 43 | run("examples/tuples", "Jonas 25\n"); 44 | } 45 | 46 | #[test] 47 | fn example_using_ext_library() { 48 | run( 49 | "examples/using-ext-library", 50 | "Hello from $LUMINAPATH/ext/example_lib/src/main.lm\n", 51 | ); 52 | } 53 | 54 | #[test] 55 | fn example_modules() { 56 | run("examples/modules", "HelloWorld\nHelloWorld\n"); 57 | } 58 | 59 | #[cfg(unix)] 60 | #[test] 61 | fn example_ffi() { 62 | run("examples/ffi", ""); 63 | } 64 | 65 | #[test] 66 | fn example_raw_fn_pointers() { 67 | run("examples/raw-function-pointers", "5\n"); 68 | } 69 | 70 | #[test] 71 | fn example_fizz_buzz() { 72 | run( 73 | "examples/fizz-buzz", 74 | "1\n2\nFizz\n4\nBuzz\nFizz\n7\n8\nFizz\n", 75 | ); 76 | } 77 | -------------------------------------------------------------------------------- /lumina/tests/formatting.rs: -------------------------------------------------------------------------------- 1 | //! Snapshot testing to test if formatter changes still look good 2 | 3 | use insta; 4 | use lumina_util::test_logger; 5 | use std::path::PathBuf; 6 | 7 | fn run(path: &str, file: &str, checked: bool) { 8 | test_logger(); 9 | 10 | if checked { 11 | let output = lumina::run(path); 12 | 13 | if !output.status.success() { 14 | panic!("non-success error code: {}", output.status); 15 | } 16 | } 17 | 18 | let name = path; 19 | 20 | let manifest = env!("CARGO_MANIFEST_DIR"); 21 | let path = PathBuf::from(format!("{manifest}/../{path}/{file}")); 22 | 23 | let formatted = lumina::formatter::run_file(&path); 24 | 25 | insta::assert_snapshot!(format!("format_{name}"), formatted); 26 | } 27 | 28 | #[test] 29 | fn format_hello_world() { 30 | run("examples/hello-world", "src/main.lm", false); 31 | } 32 | 33 | #[test] 34 | fn format_lists() { 35 | run("examples/lists", "src/main.lm", false); 36 | } 37 | 38 | #[test] 39 | fn format_maybe_implementation() { 40 | run("examples/maybe-implementation", "src/main.lm", false); 41 | } 42 | 43 | #[test] 44 | fn format_operators() { 45 | run("examples/operators", "src/main.lm", false); 46 | } 47 | 48 | #[test] 49 | fn format_records() { 50 | run("examples/records", "src/main.lm", false); 51 | } 52 | 53 | #[test] 54 | fn format_tuples() { 55 | run("examples/tuples", "src/main.lm", false); 56 | } 57 | 58 | #[test] 59 | fn format_using_ext_library() { 60 | run("examples/using-ext-library", "src/main.lm", false); 61 | } 62 | 63 | #[test] 64 | fn format_modules() { 65 | run("examples/modules", "src/main.lm", false); 66 | } 67 | 68 | #[cfg(unix)] 69 | #[test] 70 | fn format_ffi() { 71 | run("examples/ffi", "src/main.lm", false); 72 | } 73 | 74 | #[test] 75 | fn format_raw_fn_pointers() { 76 | run("examples/raw-function-pointers", "src/main.lm", false); 77 | } 78 | 79 | #[test] 80 | fn format_fizz_buzz() { 81 | run("examples/fizz-buzz", "src/main.lm", false); 82 | } 83 | -------------------------------------------------------------------------------- /lumina/tests/snapshots/formatting__format_examples__ffi.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina/tests/formatting.rs 3 | expression: formatted 4 | snapshot_kind: text 5 | --- 6 | @[extern "exit", platform ["linux-gnu", "linux-musl"]] 7 | fn libc_exit as i32 -> () 8 | 9 | fn main = 10 | let outer = returns in 11 | let n = takes outer in 12 | if n == 100 13 | then libc_exit 0 14 | else libc_exit n 15 | 16 | @[repr "C"] 17 | type Inner { 18 | a u32 19 | b u8 20 | c u64 21 | d u16 22 | } 23 | 24 | @[repr "C"] 25 | type Outer { 26 | a u16 27 | b Inner 28 | c u8 29 | } 30 | 31 | @[extern "takes"] 32 | fn takes as Outer -> i32 33 | 34 | @[extern "returns"] 35 | fn returns as Outer 36 | -------------------------------------------------------------------------------- /lumina/tests/snapshots/formatting__format_examples__fizz-buzz.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina/tests/formatting.rs 3 | expression: formatted 4 | snapshot_kind: text 5 | --- 6 | use std:io 7 | 8 | fn main = 9 | [1, 2, 3, 4, 5, 6, 7, 8, 9] 10 | . map #fizz 11 | . forEach #io:println 12 | 13 | fn fizz n as int -> string = 14 | if n % 15 == 0 then 15 | "FizzBuz" 16 | else if n % 3 == 0 then 17 | "Fizz" 18 | else if n % 5 == 0 then 19 | "Buzz" 20 | else 21 | show n 22 | -------------------------------------------------------------------------------- /lumina/tests/snapshots/formatting__format_examples__hello-world.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina/tests/formatting.rs 3 | expression: formatted 4 | snapshot_kind: text 5 | --- 6 | use std:io 7 | 8 | fn main = io:println ("Hello" <> " " <> "World!") 9 | -------------------------------------------------------------------------------- /lumina/tests/snapshots/formatting__format_examples__lists.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina/tests/formatting.rs 3 | expression: formatted 4 | snapshot_kind: text 5 | --- 6 | use std:io 7 | use std:list 8 | 9 | fn main = 10 | (1 : [2, 3, 4] ++ [5, 6, 7, 8, 9]) 11 | . map #(* 2) 12 | . sum 13 | . io:println 14 | 15 | fn digits as [int] = list:from_range (0, 10) #(\i -> i as int) 16 | 17 | fn my_map f list as (fn a -> b) [a] -> [b] = 18 | match list 19 | | [x : xs] -> f x : my_map #f xs 20 | | [] -> [] 21 | 22 | pub fn my_fold f acc list as (fn b a -> b) b [a] -> b = 23 | match list 24 | | [x : xs] -> my_fold #f (f acc x) xs 25 | | [] -> acc 26 | -------------------------------------------------------------------------------- /lumina/tests/snapshots/formatting__format_examples__maybe-implementation.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina/tests/formatting.rs 3 | expression: formatted 4 | snapshot_kind: text 5 | --- 6 | use std:io 7 | 8 | // A simple implementation of the `Maybe` sum type 9 | type Maybe a = Just a | Nothing 10 | 11 | fn map f m as (fn a -> b) (Maybe a) -> Maybe b = 12 | match m 13 | | Just a -> Just (f a) 14 | | Nothing -> Nothing 15 | 16 | fn and f m as (fn a -> Maybe a) (Maybe a) -> Maybe a = 17 | match m 18 | | Just a -> f a 19 | | Nothing -> Nothing 20 | 21 | fn or fallback m as (fn a) (Maybe a) -> a = 22 | match m 23 | | Just a -> a 24 | | Nothing -> fallback 25 | 26 | when a can ToString 27 | impl ToString for Maybe a 28 | fn show m as self -> string = 29 | match m 30 | | Nothing -> "Nothing" 31 | | Just a -> "Just " <> show a 32 | 33 | fn main = 34 | Just 5 35 | . map #(+ 1) 36 | . and #(\n -> Just (n + 1)) 37 | . io:println 38 | -------------------------------------------------------------------------------- /lumina/tests/snapshots/formatting__format_examples__modules.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina/tests/formatting.rs 3 | expression: formatted 4 | snapshot_kind: text 5 | --- 6 | use other_file [hello] 7 | use other_dir [world] 8 | // using a directory will refer to the `lib.lm` file 9 | use other_dir:file [Color [..], Direction [Right, Down]] 10 | 11 | fn main = 12 | do file:print (hello <> world) 13 | then file:print (other_file:hello <> project:other_dir:world) 14 | 15 | fn allowed as (Color, Direction) = (Green, Down) 16 | 17 | // Variants of a sum type can also be access through the sum type's namespace 18 | fn blocked as (Color, Direction) = (Color:Red, Direction:Up) 19 | -------------------------------------------------------------------------------- /lumina/tests/snapshots/formatting__format_examples__operators.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina/tests/formatting.rs 3 | expression: formatted 4 | snapshot_kind: text 5 | --- 6 | use std:io 7 | 8 | fn main = io:println (10 - 1 * 2 / (4 + 1) - 2 +++ 5) 9 | 10 | fn +++ x y as int int -> int = x + y + y 11 | -------------------------------------------------------------------------------- /lumina/tests/snapshots/formatting__format_examples__raw-function-pointers.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina/tests/formatting.rs 3 | expression: formatted 4 | snapshot_kind: text 5 | --- 6 | type Handlers { 7 | this (fnptr i32 -> u32) 8 | that (fnptr u32 -> i32) 9 | } 10 | 11 | fn main = 12 | let handlers = { this = #!this_handler, that = #!that_handler } in 13 | handlers.this 5 14 | . handlers.that 15 | . std:io:println 16 | 17 | fn this_handler n as i32 -> u32 = n as u32 18 | 19 | fn that_handler n as u32 -> i32 = n as i32 20 | -------------------------------------------------------------------------------- /lumina/tests/snapshots/formatting__format_examples__records.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina/tests/formatting.rs 3 | expression: formatted 4 | snapshot_kind: text 5 | --- 6 | use std:io 7 | 8 | fn main = simple_example 9 | 10 | // Simple Example 11 | type User { 12 | name string 13 | age int 14 | } 15 | 16 | fn simple_example = 17 | let jonas = { User | name = "Jonas", age = 15 } in 18 | let older_jonas = { jonas ~ age@n = n + 10 } 19 | in io:println (can_drive jonas, can_drive older_jonas) 20 | 21 | where 22 | fn can_drive { age } as User -> bool = age >= 18 23 | 24 | // Complex Example 25 | type Pair a { 26 | x a 27 | y a 28 | } 29 | 30 | fn modify_deeply_nested pair as Pair (Pair (Pair u8)) -> (Pair (Pair (Pair u8))) = 31 | { pair ~ x.x.x@n = n + 1, y.x.x@n = n + 2 } 32 | 33 | fn complex_example = 34 | let inner = { Pair u8 | x = 3, y = 4 } in 35 | let middle = { Pair (Pair u8) | x = inner, y = inner } in 36 | let outer = { Pair (Pair (Pair u8)) | x = middle, y = middle } in 37 | (modify_deeply_nested outer).x.x.x 38 | . io:println 39 | -------------------------------------------------------------------------------- /lumina/tests/snapshots/formatting__format_examples__tuples.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina/tests/formatting.rs 3 | expression: formatted 4 | snapshot_kind: text 5 | --- 6 | use std:io 7 | 8 | fn main = 9 | let user = ("Jonas", 25) 10 | in print_user user 11 | 12 | fn print_user (name, age) as (string, int) -> () = 13 | io:println (name <> " " <> show age) 14 | -------------------------------------------------------------------------------- /lumina/tests/snapshots/formatting__format_examples__using-ext-library.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: lumina/tests/formatting.rs 3 | expression: formatted 4 | snapshot_kind: text 5 | --- 6 | use std:io 7 | use ext:example_lib [text] 8 | 9 | fn main = io:println text 10 | -------------------------------------------------------------------------------- /lumina/tests/tests.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | fn run(path: &str) { 4 | let output = lumina::run(path); 5 | 6 | let manifest = env!("CARGO_MANIFEST_DIR"); 7 | let expected_exit_code_path = PathBuf::from(format!("{manifest}/../{path}/expected")); 8 | 9 | let expected_exit_code_file = std::fs::read_to_string(expected_exit_code_path).unwrap(); 10 | 11 | let expected = expected_exit_code_file 12 | .strip_suffix("\n") 13 | .unwrap() 14 | .parse::() 15 | .unwrap(); 16 | 17 | assert_eq!(output.status.code(), Some(expected)); 18 | } 19 | 20 | #[test] 21 | fn tests_mem_autoboxed_struct() { 22 | run("tests/mem-autoboxed-struct"); 23 | } 24 | 25 | #[test] 26 | fn tests_mem_recursive_sum() { 27 | run("tests/mem-recursive-sum"); 28 | } 29 | 30 | #[test] 31 | fn tests_mem_large_struct() { 32 | run("tests/mem-large-struct"); 33 | } 34 | 35 | #[test] 36 | fn tests_mem_small_struct() { 37 | run("tests/mem-small-struct"); 38 | } 39 | 40 | #[test] 41 | fn tests_mem_large_sum() { 42 | run("tests/mem-large-sum"); 43 | } 44 | 45 | #[test] 46 | fn tests_mem_small_sum() { 47 | run("tests/mem-small-sum"); 48 | } 49 | 50 | #[test] 51 | fn tests_mem_nested_combination() { 52 | run("tests/mem-nested-combination"); 53 | } 54 | 55 | #[test] 56 | fn tests_mem_sum_in_struct() { 57 | run("tests/mem-sum-in-struct"); 58 | } 59 | -------------------------------------------------------------------------------- /luminapath/ext/example_lib/config.lm: -------------------------------------------------------------------------------- 1 | val name = "example_lib" 2 | 3 | val version = "1.0" 4 | 5 | val authors = [] 6 | 7 | val dependencies = [] 8 | 9 | -------------------------------------------------------------------------------- /luminapath/ext/example_lib/src/lib.lm: -------------------------------------------------------------------------------- 1 | pub fn text as string = "Hello from $LUMINAPATH/ext/example_lib/src/main.lm" 2 | 3 | -------------------------------------------------------------------------------- /luminapath/ext/minimal-env/config.lm: -------------------------------------------------------------------------------- 1 | val name = "minimal-env" 2 | 3 | val version = "1.0" 4 | 5 | val authors = [] 6 | 7 | val prelude = "project:prelude" 8 | 9 | val dependencies = [] 10 | 11 | -------------------------------------------------------------------------------- /luminapath/ext/minimal-env/src/lib.lm: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /luminapath/ext/minimal-env/src/prelude/lib.lm: -------------------------------------------------------------------------------- 1 | // use std:gc 2 | 3 | // mandatory but unused lang items 4 | trait Listable 5 | 6 | type string {} 7 | 8 | trait Type 9 | 10 | type List a = Nil 11 | 12 | pub type Maybe a = Just a | Nothing 13 | 14 | pub fn + a b as i32 i32 -> i32 = builtin:plus a b 15 | 16 | trait Stringable 17 | fn split_at as self int -> (self, self) 18 | fn split_while as self (fn u8 -> bool) -> (self, self) 19 | fn split_first as self -> (u8, self) 20 | fn equals as self self -> bool 21 | fn from_raw_parts as *u8 uint -> self 22 | 23 | //impl Stringable for string 24 | // fn split_at _ _ as self, int -> (self, self) = 25 | // ({ string | }, { string | }) 26 | // 27 | // fn split_while _ _ as self, fn(u8 -> bool) -> (self, self) = 28 | // ({ string | }, { string | }) 29 | // 30 | // fn split_first _ as self -> (u8, self) = (0, { string | }) 31 | // 32 | // fn equals _ _ as self, self -> bool = true 33 | // 34 | // fn from_raw_parts _ _ as *u8, uint -> self = { string | } 35 | @![langItem(list as List)] 36 | 37 | trait Closure p r 38 | fn call as self p -> r 39 | 40 | pub trait Size 41 | fn of as uint 42 | 43 | impl Size for a 44 | fn of as uint = builtin:size_of(self as a) 45 | 46 | // fn string_from_raw_parts _ _ as *u8, uint -> string = { string | } 47 | @[platform "linux"] 48 | fn alloc size as int -> *u8 = 49 | libc_alloc size 50 | 51 | @[platform "linux"] 52 | fn dealloc ptr size as *u8 int -> () = free ptr 53 | 54 | @[extern "malloc", platform "linux"] 55 | fn libc_alloc as int -> *u8 56 | 57 | @[extern "free", platform "linux"] 58 | fn free as *u8 -> () 59 | 60 | @[extern "exit", platform "linux"] 61 | pub fn libc_exit code as i32 -> () 62 | 63 | @[no_mangle, platform ["linux-gnu", "linux-musl"]] 64 | fn _lumina_sys_init argc argv as i32 **u8 -> () = () 65 | 66 | @[no_mangle, platform "linux-syscall"] 67 | fn _lumina_sys_init as () = () 68 | 69 | @[extern "x86_64_syscall", platform "linux"] 70 | pub fn syscall as int int int int int int -> () 71 | 72 | -------------------------------------------------------------------------------- /luminapath/std/bool/lib.lm: -------------------------------------------------------------------------------- 1 | impl ToString for bool 2 | fn show b as bool -> string = 3 | if b 4 | then "true" 5 | else "false" 6 | 7 | -------------------------------------------------------------------------------- /luminapath/std/char/lib.lm: -------------------------------------------------------------------------------- 1 | // Placeholder module until proper utf-8 support is implemented 2 | pub fn is_lower_letter c as u8 -> bool = 3 | c >= 'a' && c <= 'z' 4 | 5 | pub fn is_upper_letter c as u8 -> bool = 6 | c >= 'A' && c <= 'Z' 7 | 8 | pub fn is_number c as u8 -> bool = c >= '0' && c <= '9' 9 | 10 | pub fn to_digit c as u8 -> uint = 11 | (c as uint) - ('0' as uint) 12 | 13 | pub fn from_digit c as uint -> u8 = ((c as u8) + '0') 14 | 15 | -------------------------------------------------------------------------------- /luminapath/std/env/lib.lm: -------------------------------------------------------------------------------- 1 | use std:list [List [Slice]] 2 | use std:list:vec [Vec] 3 | 4 | // Written to by the `_lumina_sys_init` function 5 | val process_arguments as Vec (Vec u8) = vec:empty 6 | 7 | pub fn get_args as [string] = 8 | process_arguments 9 | . map #std:string:fromByteVec 10 | . to_slice 11 | . to_list 12 | 13 | -------------------------------------------------------------------------------- /luminapath/std/fs/lib.lm: -------------------------------------------------------------------------------- 1 | use std:libc 2 | use std:string [to_c_str, string] 3 | use std:list:vec 4 | use std:list [List [Slice]] 5 | use std:ptr 6 | 7 | @[platform "linux"] 8 | type File { fd i32 } 9 | 10 | pub type FileMode = ReadOnly | WriteOnly | ReadWrite 11 | 12 | @[platform "linux"] 13 | pub fn open path mode as string FileMode -> Result File IoError = 14 | libc:open (to_c_str path) flag 15 | . handle_io_errno #(\fd -> Ok { fd }) 16 | where 17 | fn flag = 18 | match mode 19 | | ReadOnly -> libc:O_RDONLY 20 | | WriteOnly -> libc:O_WRONLY 21 | | ReadWrite -> libc:O_RDWR 22 | 23 | fn errno_to_io_error as IoError = 24 | match libc:errno 25 | | 1 -> PermissionDenied 26 | | 2 -> NoSuchFileOrDirectory 27 | | n -> Unknown n 28 | 29 | when n can Num + Compare 30 | fn handle_io_errno f n as (fn n -> Result a IoError) n -> Result a IoError = 31 | if n < Num:zero 32 | then Err errno_to_io_error 33 | else f n 34 | 35 | pub type IoError = PermissionDenied | NoSuchFileOrDirectory | Unknown i32 36 | 37 | pub fn readFileAt path as string -> Result string IoError = 38 | open path ReadOnly . and #readFile 39 | 40 | pub fn readFile { fd } as File -> Result string IoError = 41 | let rptr = ptr:alloc(t as libc:stat) in 42 | libc:fstat fd rptr 43 | . handle_io_errno #(\_ -> read rptr) 44 | where 45 | fn read rptr = 46 | let size = ptr:deref ((ptr:offset rptr 48) as *i64) in 47 | let buf = vec:capacity(a as u8) (size as uint) in 48 | libc:read fd buf.ptr size 49 | . handle_io_errno #(\_ -> Ok (string:fromByteVec buf)) 50 | 51 | impl ToString for IoError 52 | fn show err as self -> string = 53 | match err 54 | | PermissionDenied -> "permission denied" 55 | | NoSuchFileOrDirectory -> "no such file or directory" 56 | | Unknown code -> "failure with code " <> code 57 | 58 | -------------------------------------------------------------------------------- /luminapath/std/gc/lib.lm: -------------------------------------------------------------------------------- 1 | use std:libc 2 | use std:ptr 3 | 4 | // val session as () = lumina_gc_init 5 | pub fn alloc size as int -> *u8 = libc:malloc size 6 | 7 | // pub fn alloc size as int -> *u8 = mmtk_alloc (size as uint) 4 0 8 | pub fn free ptr as *u8 -> () = libc:free ptr 9 | 10 | @[extern "mmtk_alloc"] 11 | fn mmtk_alloc as uint uint i32 -> *u8 12 | 13 | // used for internal debugging 14 | // @[extern "mmtk_info_log_number", platform "linux"] 15 | // pub fn logn as int -> () 16 | // 17 | @[extern "mmtk_init", platform "linux"] 18 | fn lumina_gc_init as () 19 | 20 | // @[extern "mmtk_bind_mutator", platform "linux"] 21 | // fn mmtk_bind_mutator as *u8 -> *u8 22 | 23 | // @[extern "mmtk_alloc", platform "linux"] 24 | // pub fn mmtk_alloc mutator size align offset allocator 25 | // as *u8, int, int, int, int -> *u8 26 | // 27 | // // TODO: To support multi-threadding we'd need thread local storage 28 | // // then initialise MMTK seperately from creating the mutator 29 | // // then create a mutator for each thread 30 | // pub val main_thread_mutator as *u8 = 31 | // do lumina_gc_init then mutator 32 | // let mutator = mmtk_bind_mutator ptr:null in 33 | // do mmtk_initialize_collection mutator 34 | // then mutator 35 | 36 | // @[extern "mmtk_initialize_collection", platform "linux"] 37 | // fn mmtk_initialize_collection as *u8 -> () 38 | -------------------------------------------------------------------------------- /luminapath/std/io/lib.lm: -------------------------------------------------------------------------------- 1 | use std:libc 2 | use std:list [List [Slice]] 3 | use std:list:vec [Vec] 4 | 5 | @[platform "linux"] 6 | pub fn stderr as i32 = 2 7 | 8 | @[platform "linux"] 9 | pub fn stdout as i32 = 1 10 | 11 | @[platform "linux"] 12 | pub fn stdin as i32 = 0 13 | 14 | @[platform "linux"] 15 | when s can ToString 16 | pub fn eprint str as s -> () = 17 | print_into (show str) stderr 18 | 19 | @[platform "linux"] 20 | when s can ToString 21 | pub fn eprintln str as s -> () = 22 | print_into (str <> "\n") stderr 23 | 24 | @[platform "linux"] 25 | when s can ToString 26 | pub fn print str as s -> () = 27 | print_into (show str) stdout 28 | 29 | @[platform "linux"] 30 | when s can ToString 31 | pub fn println str as s -> () = 32 | print_into (str <> "\n") stdout 33 | 34 | @[platform "linux"] 35 | fn print_into str fd as string i32 -> () = 36 | let { ptr, len } = show str . toByteVec in 37 | do libc:write fd ptr (len as int) 38 | then () 39 | 40 | @[platform "linux"] 41 | pub fn interupt = libc:raise sigint 42 | 43 | fn sigint = 2 44 | 45 | pub fn raw_print str len as string uint -> () = 46 | match str.inner 47 | | Slice { vec:Slice u8 | ptr, len } -> 48 | do libc:write stdout ptr (len as int) 49 | then () 50 | 51 | | _ -> () 52 | 53 | when s can ToString 54 | pub fn trace s as s -> s = 55 | do print "trace: " then 56 | do print s then 57 | do print "\n" 58 | then s 59 | 60 | @[platform ["linux-gnu", "linux-musl"]] 61 | pub fn crash text as string -> a = 62 | do println text then 63 | do libc:exit 1 64 | then builtin:unreachable(self as a) 65 | 66 | -------------------------------------------------------------------------------- /luminapath/std/libc/lib.lm: -------------------------------------------------------------------------------- 1 | @[platform ["linux-gnu", "linux-musl"]] 2 | pub fn errno as i32 = ptr:deref errno_location 3 | 4 | @[extern "__errno_location", platform ["linux-gnu", "linux-musl"]] 5 | pub fn errno_location as *i32 6 | 7 | @[extern "getenv", platform ["linux-gnu", "linux-musl"]] 8 | pub fn getenv as *u8 -> *u8 9 | 10 | @[extern "exit", platform ["linux-gnu", "linux-musl"]] 11 | pub fn exit code as i32 -> () 12 | 13 | @[extern "write", platform ["linux-gnu", "linux-musl"]] 14 | pub fn write as i32 *u8 int -> int 15 | 16 | @[extern "read", platform ["linux-gnu", "linux-musl"]] 17 | pub fn read as i32 *u8 int -> int 18 | 19 | @[extern "raise", platform ["linux-gnu", "linux-musl"]] 20 | pub fn raise as int -> () 21 | 22 | @[extern "malloc", platform ["linux-gnu", "linux-musl"]] 23 | pub fn malloc as int -> *u8 24 | 25 | @[extern "free", platform ["linux-gnu", "linux-musl"]] 26 | pub fn free as *u8 -> () 27 | 28 | @[extern "open", platform ["linux-gnu", "linux-musl"]] 29 | pub fn open path flags as *u8 i32 -> i32 30 | 31 | @[extern "fstat", platform ["linux-gnu", "linux-musl"]] 32 | pub fn fstat as i32 *stat -> i32 33 | 34 | pub fn O_RDONLY as i32 = 0 35 | 36 | pub fn O_WRONLY as i32 = 1 37 | 38 | pub fn O_RDWR as i32 = 2 39 | 40 | @[platform ["linux-gnu", "linux-musl"]] 41 | type stat { 42 | st_dev u64 43 | 44 | // dev_t 45 | st_ino u64 46 | 47 | // ino_t 48 | st_mode u32 49 | 50 | // mode_t 51 | st_nlink u64 52 | 53 | // nlink_t 54 | st_uid i32 55 | 56 | // uid_t 57 | st_gid u32 58 | 59 | // gid_t 60 | _padding0 u32 61 | st_rdev u64 62 | 63 | // dev_t 64 | st_size i64 65 | 66 | // off_t 67 | st_blksize i64 68 | 69 | // blksize_t 70 | st_blocks i64 71 | 72 | // blkcnt_t 73 | st_atim timespec 74 | st_mtim timespec 75 | st_ctim timespec 76 | _excess1 u64 77 | } 78 | 79 | @[platform ["linux-gnu", "linux-musl"]] 80 | type timespec { 81 | tv_sec i64 82 | tv_nsec i64 83 | } 84 | 85 | -------------------------------------------------------------------------------- /luminapath/std/list/slice.lm: -------------------------------------------------------------------------------- 1 | use std:list:vec [unsafe_take, from_range] 2 | 3 | pub type Slice a { 4 | // For pointer marking to work in the GC we need to hold on to the root 5 | source *a 6 | 7 | ptr *a 8 | len uint 9 | } 10 | 11 | // zero-cost conversion from slice to vector 12 | // must never outlive the slice 13 | fn weak_vec { ptr, len } as Slice a -> Vec a = 14 | { Vec a | ptr, len } 15 | 16 | pub fn split slice as Slice a -> Maybe (a, (Slice a)) = 17 | get 0 slice 18 | . map #(\v -> (v, skip 1 slice)) 19 | 20 | pub fn is_empty { len } as Slice a -> bool = len == 0 21 | 22 | pub fn empty as (Slice a) = 23 | { 24 | source = 25 | 0 as *a, ptr = 0 as *a, len = 0 26 | } 27 | 28 | pub fn get i slice as uint (Slice a) -> Maybe a = 29 | weak_vec slice . get i 30 | 31 | pub fn unsafe_get i slice as uint (Slice a) -> a = 32 | weak_vec slice . unsafe_get i 33 | 34 | // TODO: we should make it so that dot-calls prioritise type-specific *over* defined in current module 35 | // for the sake of overloading. Local bindings should still be prioritised though? 36 | pub fn len { len } as Slice a -> uint = len 37 | 38 | pub fn forEach f slice as (fn a -> ()) (Slice a) -> () = 39 | list:itimes #visit slice.len 40 | where 41 | fn visit i as uint -> () = weak_vec slice . unsafe_get i . f 42 | 43 | pub fn map f slice as (fn a -> b) (Slice a) -> Slice b = 44 | from_range 45 | (0, slice.len) 46 | #(\i -> unsafe_get i slice . f) 47 | . to_slice 48 | 49 | // Splits the slice at the given index. 50 | pub fn split_at at slice as uint (Slice a) -> ((Slice a), (Slice a)) = 51 | (take at slice, skip (at) slice) 52 | 53 | pub fn take n slice as uint (Slice a) -> (Slice a) = 54 | { slice ~ len@len = min n len } 55 | 56 | pub fn skip n slice as uint (Slice a) -> (Slice a) = 57 | if n >= slice.len then 58 | empty 59 | else 60 | { 61 | slice 62 | ~ ptr = 63 | ptr:offsetu slice.ptr (n * Type(a):size) 64 | , len = slice.len - n 65 | } 66 | 67 | pub fn any f slice as (fn a -> bool) (Slice a) -> bool = 68 | next 0 69 | where 70 | fn next i as uint -> bool = 71 | match get i slice 72 | | Nothing -> false 73 | | Just elem -> 74 | if f elem 75 | then true 76 | else next (i + 1) 77 | 78 | pub fn all f slice as (fn a -> bool) (Slice a) -> bool = 79 | not (any #(\v -> not (f v)) slice) 80 | 81 | pub fn to_list slice as Slice a -> List a = 82 | if len slice == 0 83 | then List:Nil 84 | else List:Slice slice 85 | 86 | // TODO: do something more efficient 87 | when a can ToString 88 | impl ToString for (Slice a) 89 | fn show slice as self -> string = 90 | "[" <> show_ slice 91 | where 92 | fn show_ slice as Slice a -> string = 93 | match split slice 94 | | Nothing -> "]" 95 | | Just (x, xs) -> 96 | if xs.len == 0 97 | then show x <> "]" 98 | else show x <> ", " <> show_ xs 99 | 100 | -------------------------------------------------------------------------------- /luminapath/std/list/vec.lm: -------------------------------------------------------------------------------- 1 | use std:ptr 2 | use std:list [Listable] 3 | use std:list:slice [Slice] 4 | use std:math [Compare [>=]] 5 | use std:io [crash] 6 | use std:string [<>] 7 | 8 | // An immutable heap-allocated array 9 | pub type Vec a { 10 | ptr *a 11 | len uint 12 | } 13 | 14 | pub fn capacity len as uint -> Vec a = 15 | let ptr = 16 | std:prelude:alloc ((Type(a):size * len) as int) 17 | in { ptr = ptr as *a, len } 18 | 19 | pub fn empty as Vec a = { Vec a | ptr = ptr:null, len = 0 } 20 | 21 | pub fn singleton v as a -> Vec a = 22 | { ptr = ptr:box v, len = 1 } 23 | 24 | pub fn to_slice { ptr, len } as Vec a -> Slice a = 25 | { Slice a | ptr, len, source = ptr } 26 | 27 | pub fn get i vec as uint (Vec a) -> Maybe a = 28 | if i >= vec.len 29 | then Nothing 30 | else Just (unsafe_get i vec) 31 | 32 | pub fn unsafe_get i { ptr, len } as uint (Vec a) -> a = 33 | ptr:offsetu ptr (Type(a):size * i) . ptr:deref 34 | 35 | pub fn unsafe_set i v { ptr, len } as uint a (Vec a) -> () = 36 | let at = 37 | ptr:offsetu ptr (Type(a):size * i) 38 | in ptr:write at v 39 | 40 | pub fn from_range (start, end) f as (uint, uint) (fn uint -> a) -> Vec a = 41 | if start > end then 42 | crash 43 | ( 44 | "error: from_range end larger than start " 45 | <> end 46 | <> " > " 47 | <> start 48 | ) 49 | else let count = (end - start) in 50 | let vec = capacity count in 51 | do list:itimes #(\i -> unsafe_set i (f (i + start)) vec) count 52 | then vec 53 | 54 | fn bytes_from_null_terminated ptr as *u8 -> Vec u8 = 55 | findZero 0 56 | where 57 | fn findZero i as uint -> Vec u8 = 58 | if ptr:deref (ptr:offsetu ptr i) == 0 59 | then { ptr, len = i } 60 | else findZero (i + 1) 61 | 62 | pub fn unsafe_take n { ptr, len } as uint (Vec a) -> Vec a = 63 | { ptr, len = n } 64 | 65 | pub fn map f vec as (fn a -> b) (Vec a) -> Vec b = 66 | from_range 67 | (0, vec.len) 68 | #(\i -> unsafe_get i vec . f) 69 | 70 | pub fn clone vec as Vec a -> Vec a = 71 | from_range 72 | (0, vec.len) 73 | #(\i -> unsafe_get i vec) 74 | 75 | when a can ToString 76 | impl ToString for Vec a 77 | fn show vec as self -> string = 78 | show (vec . to_slice) 79 | 80 | -------------------------------------------------------------------------------- /luminapath/std/math/i16.lm: -------------------------------------------------------------------------------- 1 | pub fn abs n as i16 -> i16 = builtin:iabs n 2 | 3 | -------------------------------------------------------------------------------- /luminapath/std/math/i32.lm: -------------------------------------------------------------------------------- 1 | pub fn abs n as i32 -> i32 = builtin:iabs n 2 | 3 | -------------------------------------------------------------------------------- /luminapath/std/math/i64.lm: -------------------------------------------------------------------------------- 1 | pub fn abs n as i64 -> i64 = builtin:iabs n 2 | 3 | -------------------------------------------------------------------------------- /luminapath/std/math/i8.lm: -------------------------------------------------------------------------------- 1 | pub fn abs n as i8 -> i8 = builtin:iabs n 2 | 3 | -------------------------------------------------------------------------------- /luminapath/std/math/non_zero.lm: -------------------------------------------------------------------------------- 1 | type NonZero n { inner n } 2 | 3 | when n can Num + Compare 4 | pub fn non_zero n as n -> Maybe (NonZero n) = 5 | if n == Num:zero 6 | then Nothing 7 | else Just { inner = n } 8 | 9 | -------------------------------------------------------------------------------- /luminapath/std/math/u16.lm: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /luminapath/std/math/u32.lm: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /luminapath/std/math/u64.lm: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /luminapath/std/math/u8.lm: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /luminapath/std/maybe/lib.lm: -------------------------------------------------------------------------------- 1 | use std:math [Compare [..]] 2 | use std:io [crash] 3 | 4 | pub type Maybe a = Just a | Nothing 5 | 6 | pub fn isJust m as Maybe a -> bool = 7 | match m 8 | | Just _ -> true 9 | | Nothing -> false 10 | 11 | pub fn or f opt as (fn a) (Maybe a) -> a = 12 | match opt 13 | | Just a -> a 14 | | Nothing -> f 15 | 16 | pub fn and f opt as (fn a -> Maybe b) (Maybe a) -> Maybe b = 17 | match opt 18 | | Just a -> f a 19 | | Nothing -> Nothing 20 | 21 | pub fn map f m as (fn a -> b) (Maybe a) -> Maybe b = 22 | match m 23 | | Just a -> Just (f a) 24 | | Nothing -> Nothing 25 | 26 | pub fn or_crash msg m as string (Maybe a) -> a = 27 | match m 28 | | Just a -> a 29 | | Nothing -> crash msg 30 | 31 | when a can Compare 32 | impl Compare for Maybe a 33 | fn compare a b as self self -> Ordering = 34 | match (a, b) 35 | | (Nothing, Nothing) -> Ordering:Equal 36 | | (Just x, Just y) -> compare x y 37 | | (Nothing, Just _) -> Ordering:Less 38 | | (Just _, Nothing) -> Ordering:Greater 39 | 40 | when a can ToString 41 | impl ToString for Maybe a 42 | fn show m as Maybe a -> string = 43 | match m 44 | | Nothing -> "Nothing" 45 | | Just a -> show a 46 | 47 | -------------------------------------------------------------------------------- /luminapath/std/nothing/lib.lm: -------------------------------------------------------------------------------- 1 | pub type nothing {} 2 | 3 | -------------------------------------------------------------------------------- /luminapath/std/prelude/lib.lm: -------------------------------------------------------------------------------- 1 | use std:gc 2 | pub use std:math [Num [..], Compare [..], Ordering] 3 | pub use std:math:u8 4 | pub use std:math:u16 5 | pub use std:math:u32 6 | pub use std:math:u64 7 | pub use std:math:i8 8 | pub use std:math:i16 9 | pub use std:math:i32 10 | pub use std:math:i64 11 | pub use std:maybe [Maybe [..]] 12 | pub use std:result [Result [..]] 13 | pub use std:string [string, ToString [show], <>] 14 | pub use std:list [Listable [:], List, ++] 15 | use std:string [Stringable] 16 | pub use std:nothing [nothing] 17 | use std:ptr 18 | use std:list:vec [Vec] 19 | use std:env [process_arguments] 20 | use std:array 21 | use std:unwind 22 | pub use std:tyinfo [Type] 23 | // we need these so that their implementations are always included 24 | use std:tuple 25 | use std:bool 26 | 27 | @![langItem(list as List)] 28 | 29 | trait Closure p r 30 | fn call as self p -> r 31 | 32 | @[platform ["linux-gnu", "linux-musl"]] 33 | fn alloc size as int -> *u8 = std:gc:alloc size 34 | 35 | @[platform ["linux-gnu", "linux-musl"]] 36 | fn dealloc ptr size as *u8 int -> () = std:gc:free ptr 37 | 38 | @[extern "x86_64_syscall", platform "linux"] 39 | fn syscall as int int int int int int -> () 40 | 41 | @[no_mangle, platform ["linux-gnu", "linux-musl"]] 42 | fn _lumina_sys_init argc argv as i32 **u8 -> () = 43 | setup_args argc argv 44 | 45 | fn setup_args argc argv as i32 **u8 -> () = 46 | let args = vec:from_range (0, argc as uint) #(\i -> vec:bytes_from_null_terminated (ptr:offsetu argv (i * 8) . ptr:deref)) 47 | in ptr:write (builtin:val_to_ref process_arguments) args 48 | 49 | pub fn not b as bool -> bool = 50 | if b 51 | then false 52 | else true 53 | 54 | -------------------------------------------------------------------------------- /luminapath/std/ptr/lib.lm: -------------------------------------------------------------------------------- 1 | // Overwrite the data stored at pointer location with supplied value 2 | pub fn write ptr v as *a a -> () = builtin:write ptr v 3 | 4 | // Dereference the pointer 5 | pub fn deref ptr as *a -> a = builtin:deref ptr 6 | 7 | // TODO: pretty sure we made it so that builtin:add works with pointer. 8 | // So; we shouldn't need builtin:offset anymore. 9 | // Offset the pointer by a set amount of bytes 10 | pub fn offset ptr by as *a int -> *a = 11 | builtin:offset ptr (by as uint) 12 | 13 | pub fn offsetu ptr by as *a uint -> *a = 14 | builtin:offset ptr by 15 | 16 | pub fn null as *a = 0 as *a 17 | 18 | when t can Type 19 | pub fn alloc as *t = std:prelude:alloc (Type(t):size as int) as *t 20 | 21 | when t can Type 22 | pub fn dealloc ptr as *t -> () = 23 | std:prelude:dealloc (ptr as *u8) (Type(t):size as int) 24 | 25 | when t can Type 26 | pub fn box v as t -> *t = 27 | let ptr = alloc(t as t) in 28 | do write ptr v 29 | then ptr 30 | 31 | -------------------------------------------------------------------------------- /luminapath/std/ref/lib.lm: -------------------------------------------------------------------------------- 1 | pub type Ref a { raw *a } 2 | 3 | pub fn new a as a -> Ref a = { raw = std:ptr:box a } 4 | 5 | pub fn set v { raw } as a (Ref a) -> () = 6 | std:ptr:write raw v 7 | 8 | pub fn get { raw } as Ref a -> a = std:ptr:deref raw 9 | 10 | -------------------------------------------------------------------------------- /luminapath/std/result/lib.lm: -------------------------------------------------------------------------------- 1 | use std:io 2 | 3 | pub type Result a e = Ok a | Err e 4 | 5 | pub fn is_ok m as Result a e -> bool = 6 | match m 7 | | Ok _ -> true 8 | | Err _ -> false 9 | 10 | pub fn map f m as (fn a -> b) (Result a e) -> Result b e = 11 | match m 12 | | Ok a -> Ok (f a) 13 | | Err e -> Err e 14 | 15 | pub fn and f m as (fn a -> Result b e) (Result a e) -> Result b e = 16 | match m 17 | | Ok a -> f a 18 | | Err e -> Err e 19 | 20 | when e can ToString 21 | pub fn or_crash msg r as string (Result a e) -> a = 22 | match r 23 | | Ok v -> v 24 | | Err err -> 25 | io:crash (msg <> ": " <> show err) 26 | 27 | pub fn or_crash_ msg r as string (Result a e) -> a = 28 | match r 29 | | Ok v -> v 30 | | Err _ -> io:crash msg 31 | 32 | -------------------------------------------------------------------------------- /luminapath/std/string/extractor.lm: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /luminapath/std/string/num.lm: -------------------------------------------------------------------------------- 1 | use std:string [<:>, <+>] 2 | 3 | impl ToString for i64 4 | fn show n = 5 | let str = show ((std:math:i64:abs n) as u64) in 6 | if n < 0 7 | then 45 <:> str 8 | else str 9 | 10 | impl ToString for i32 11 | fn show n = ToString:show (n as i64) 12 | 13 | impl ToString for i16 14 | fn show n = ToString:show (n as i64) 15 | 16 | impl ToString for i8 17 | fn show n = show (n as i64) 18 | 19 | // TODO: This would be much more efficient if we manually used `Vec`. 20 | // However; I think a better way to approach this is a more general MIR/LIR optimisation 21 | // which detects and transforms this kind of code? Should be possible. 22 | // 23 | // Since `string` has the same layout as `List` perhaps we could do it transparently post-monomorphisation? 24 | impl ToString for u64 25 | fn show n = 26 | if n == 0 27 | then "0" 28 | else showNext n 29 | 30 | where 31 | fn showNext n as u64 -> string = 32 | match n 33 | | 0 -> "" 34 | | _ -> 35 | let c = ((n % 10) as u8) + 48 36 | in showNext (n / 10) <+> c 37 | 38 | impl ToString for u32 39 | fn show n = ToString:show (n as u64) 40 | 41 | impl ToString for u16 42 | fn show n = ToString:show (n as u64) 43 | 44 | impl ToString for u8 45 | fn show n = show (n as u64) 46 | 47 | -------------------------------------------------------------------------------- /luminapath/std/tuple/lib.lm: -------------------------------------------------------------------------------- 1 | use std:string [<++>] 2 | 3 | impl ToString for () 4 | fn show _ as () -> string = "()" 5 | 6 | // TODO: using `<>` instead of `<++>` here causes bools to display as ints. 7 | // That does not make any sense, and must be some sort of problem with our trait solver? 8 | when 9 | a can ToString 10 | b can ToString 11 | impl ToString for (a, b) 12 | fn show (a, b) as (a, b) -> string = 13 | "(" <++> show a <++> show ", " <++> show b <++> show ")" 14 | 15 | when 16 | a can ToString 17 | b can ToString 18 | c can ToString 19 | impl ToString for (a, b, c) 20 | fn show (a, b, c) as (a, b, c) -> string = 21 | "(" 22 | <++> show a 23 | <++> show ", " 24 | <++> show b 25 | <++> show ", " 26 | <++> show c 27 | <++> show ")" 28 | 29 | when 30 | a can ToString 31 | b can ToString 32 | c can ToString 33 | d can ToString 34 | impl ToString for (a, b, c, d) 35 | fn show (a, b, c, d) as (a, b, c, d) -> string = 36 | "(" 37 | <++> show a 38 | <++> show ", " 39 | <++> show b 40 | <++> show ", " 41 | <++> show c 42 | <++> show ", " 43 | <++> show d 44 | <++> show ")" 45 | 46 | when 47 | a can ToString 48 | b can ToString 49 | c can ToString 50 | d can ToString 51 | e can ToString 52 | impl ToString for (a, b, c, d, e) 53 | fn show (a, b, c, d, e) as (a, b, c, d, e) -> string = 54 | "(" 55 | <++> show a 56 | <++> show ", " 57 | <++> show b 58 | <++> show ", " 59 | <++> show c 60 | <++> show ", " 61 | <++> show d 62 | <++> show ", " 63 | <++> show e 64 | <++> show ")" 65 | 66 | when 67 | a can ToString 68 | b can ToString 69 | c can ToString 70 | d can ToString 71 | e can ToString 72 | f can ToString 73 | impl ToString for (a, b, c, d, e, f) 74 | fn show (a, b, c, d, e, f) as (a, b, c, d, e, f) -> string = 75 | "(" 76 | <++> show a 77 | <++> show ", " 78 | <++> show b 79 | <++> show ", " 80 | <++> show c 81 | <++> show ", " 82 | <++> show d 83 | <++> show ", " 84 | <++> show e 85 | <++> show ", " 86 | <++> show f 87 | <++> show ")" 88 | 89 | -------------------------------------------------------------------------------- /luminapath/std/tyinfo/lib.lm: -------------------------------------------------------------------------------- 1 | pub trait Type 2 | fn size as uint 3 | fn align as uint 4 | 5 | // fn kind as TypeKind 6 | impl Type for a 7 | fn size as uint = builtin:size_of(a) 8 | fn align as uint = builtin:align_of(a) 9 | 10 | // fn kind as TypeKind = builtin:kind_of(a) 11 | 12 | // pub type TypeKind = UInt u8 | Int u8 | Defined u64 13 | 14 | // -- // 15 | 16 | // Something like this could probably work? 17 | // 18 | // Hm. But for CT reflection we probably want to just have a single TypeId for everything including primitives. 19 | // I suspect it'd be easier to use that way, and, we wouldn't have to create lots of different builtins for the 20 | // type kinds. 21 | // 22 | // The generated type id's can simple be `MonoTypeKey + all_primitives.len()` if we decide to take that route. 23 | 24 | // when 25 | // ty can Type 26 | // trait_ can Type 27 | // pub fn find_implementation as Maybe Implementation = 28 | // builtin:find(ty as Type, trait_ as Type) 29 | // 30 | // type Implementation { 31 | // methods (Vec *u8) 32 | // } 33 | -------------------------------------------------------------------------------- /luminapath/std/unwind/lib.lm: -------------------------------------------------------------------------------- 1 | use std:list:vec [Vec] 2 | use std:ptr 3 | use std:libc 4 | use std:io [crash] 5 | 6 | @[extern "unw_getcontext", platform "x86_64"] 7 | fn unw_getcontext outcontext as *u8 -> int 8 | 9 | @[extern "unw_init_local", platform "x86_64"] 10 | fn unw_init_local cursor context as *u8 *u8 -> i32 11 | 12 | @[extern "unw_init_remote", platform "x86_64"] 13 | fn unw_init_remote cursor addr_space void as *u8 *u8 *u8 -> int 14 | 15 | @[extern "unw_step", platform "x86_64"] 16 | fn unw_step cursor as *u8 -> i32 17 | 18 | @[extern "unw_resume", platform "x86_64"] 19 | fn unw_resume cursor as *u8 -> i32 20 | 21 | @[repr "C"] 22 | type unw_proc_info { 23 | start_ip u32 24 | end_ip u32 25 | lsda u32 26 | handler u32 27 | _gp u32 28 | _flags u32 29 | format u32 30 | unwind_info_size u32 31 | unwind_info u32 32 | extra u32 33 | } 34 | 35 | @[extern "unw_get_proc_info", platform "x86_64"] 36 | fn unw_get_proc_info cursor outinfo as *u8 *unw_proc_info -> i32 37 | 38 | @[extern "unw_get_proc_name", platform "x86_64"] 39 | fn unw_get_proc_name cursor charptr size offp as *u8 *u8 int *u32 -> i32 40 | 41 | pub type FuncUnwindInfo { 42 | raw *unw_proc_info 43 | name (Vec u8) 44 | previous (Maybe FuncUnwindInfo) 45 | } 46 | 47 | // fn UNW_CONTEXT_SIZE as int = 21 48 | @[platform "linux"] 49 | fn UNW_CONTEXT_SIZE as int = 21 50 | 51 | @[platform "linux"] 52 | fn CNW_CURSOR_SIZE as int = 33 53 | 54 | @[platform "windows"] 55 | fn UNW_CONTEXT_SIZE as int = 54 56 | 57 | @[platform "windows"] 58 | fn CNW_CURSOR_SIZE as int = 204 59 | val UNW_ESUCCESS as int = 0 60 | // no error 61 | val UNW_EUNSPEC as int = -6540 62 | // unspecified (general) error 63 | val UNW_ENOMEM as int = -6541 64 | // out of memory 65 | val UNW_EBADREG as int = -6542 66 | // bad register number 67 | val UNW_EREADONLYREG as int = -6543 68 | // attempt to write read-only register 69 | val UNW_ESTOPUNWIND as int = -6544 70 | // stop unwinding 71 | val UNW_EINVALIDIP as int = -6545 72 | // invalid IP 73 | val UNW_EBADFRAME as int = -6546 74 | // bad frame 75 | val UNW_EINVAL as int = -6547 76 | // unsupported operation or bad value 77 | val UNW_EBADVERSION as int = -6548 78 | // unwind info has unsupported version 79 | val UNW_ENOINFO as int = -6549 80 | 81 | // no unwind info found 82 | // TODO: we need to create an API for stack-allocations 83 | pub fn generate_unwind_info as FuncUnwindInfo = 84 | let context = libc:malloc (8 * UNW_CONTEXT_SIZE) in 85 | do io:print "context: " then 86 | do io:println (unw_getcontext context) then 87 | let cursor = libc:malloc (8 * CNW_CURSOR_SIZE) in 88 | do io:print "unw_init_local: " then 89 | do io:println (unw_init_local cursor context) 90 | then unwind_at cursor 91 | 92 | fn unwind_at cursor as *u8 -> FuncUnwindInfo = 93 | let raw = ptr:alloc(t as unw_proc_info) in 94 | let name = vec:capacity 50 in 95 | let offp = ptr:alloc(t as u32) in 96 | do io:print "unw_get_proc_info: " then 97 | do io:println (unw_get_proc_info cursor raw) then 98 | do io:print "unw_get_proc_name: " then 99 | do io:println 100 | (unw_get_proc_name cursor name.ptr (name.len as int) offp) 101 | then { raw, name, previous = unwind_next cursor } 102 | 103 | fn unwind_next cursor as *u8 -> Maybe FuncUnwindInfo = 104 | if unw_step cursor == 0 105 | then Nothing 106 | else Just (unwind_at cursor) 107 | 108 | -------------------------------------------------------------------------------- /luminapath/std/unwind/parse.lm: -------------------------------------------------------------------------------- 1 | // A Dwarf parser for the informtation we use and nothing else 2 | alias DwarfUnsigned = u64 3 | alias DwarfOff = u64 4 | alias DwarfSigned = i64 5 | 6 | type Func { 7 | name *u8 8 | lopc DwarfUnsigned 9 | hipc DwarfUnsigned 10 | call_file DwarfUnsigned 11 | call_line DwarfUnsigned 12 | ranges *u8 13 | ranges_cnt DwarfSigned 14 | inlined_caller *Func 15 | next *Func 16 | 17 | // hh UT_hash_handle 18 | } 19 | 20 | type CU { 21 | off DwarfOff 22 | lopc DwarfUnsigned 23 | hipc DwarfUnsigned 24 | srcfiles **u8 25 | funclist *Func 26 | } 27 | 28 | // 1. call dwarf_init with the file descriptor 29 | // 2. call dwarf_get_elf to get the elfptr 30 | // 3. 31 | // while ok 32 | // 33 | -------------------------------------------------------------------------------- /luminapath/targets/bin/ld.lld: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luminalang/lumina/259c0fc2f7b6487a5c3aa24ed0fc9bd9214c8674/luminapath/targets/bin/ld.lld -------------------------------------------------------------------------------- /luminapath/targets/bin/lld: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luminalang/lumina/259c0fc2f7b6487a5c3aa24ed0fc9bd9214c8674/luminapath/targets/bin/lld -------------------------------------------------------------------------------- /luminapath/targets/bin/lld-link: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luminalang/lumina/259c0fc2f7b6487a5c3aa24ed0fc9bd9214c8674/luminapath/targets/bin/lld-link -------------------------------------------------------------------------------- /luminapath/targets/bin/wasm-ld: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luminalang/lumina/259c0fc2f7b6487a5c3aa24ed0fc9bd9214c8674/luminapath/targets/bin/wasm-ld -------------------------------------------------------------------------------- /luminapath/targets/darwin/stub: -------------------------------------------------------------------------------- 1 | This file exists so that git is willing to commit the directory 2 | -------------------------------------------------------------------------------- /luminapath/targets/linux/gnu/libunwind.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luminalang/lumina/259c0fc2f7b6487a5c3aa24ed0fc9bd9214c8674/luminapath/targets/linux/gnu/libunwind.a -------------------------------------------------------------------------------- /luminapath/targets/linux/musl/crt1.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luminalang/lumina/259c0fc2f7b6487a5c3aa24ed0fc9bd9214c8674/luminapath/targets/linux/musl/crt1.o -------------------------------------------------------------------------------- /luminapath/targets/linux/musl/crti.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luminalang/lumina/259c0fc2f7b6487a5c3aa24ed0fc9bd9214c8674/luminapath/targets/linux/musl/crti.o -------------------------------------------------------------------------------- /luminapath/targets/linux/musl/crtn.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luminalang/lumina/259c0fc2f7b6487a5c3aa24ed0fc9bd9214c8674/luminapath/targets/linux/musl/crtn.o -------------------------------------------------------------------------------- /luminapath/targets/linux/musl/libc.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luminalang/lumina/259c0fc2f7b6487a5c3aa24ed0fc9bd9214c8674/luminapath/targets/linux/musl/libc.a -------------------------------------------------------------------------------- /luminapath/targets/linux/musl/libcrypt.a: -------------------------------------------------------------------------------- 1 | ! 2 | -------------------------------------------------------------------------------- /luminapath/targets/linux/musl/libdl.a: -------------------------------------------------------------------------------- 1 | ! 2 | -------------------------------------------------------------------------------- /luminapath/targets/linux/musl/libm.a: -------------------------------------------------------------------------------- 1 | ! 2 | -------------------------------------------------------------------------------- /luminapath/targets/linux/musl/libpthread.a: -------------------------------------------------------------------------------- 1 | ! 2 | -------------------------------------------------------------------------------- /luminapath/targets/linux/musl/libresolv.a: -------------------------------------------------------------------------------- 1 | ! 2 | -------------------------------------------------------------------------------- /luminapath/targets/linux/musl/librt.a: -------------------------------------------------------------------------------- 1 | ! 2 | -------------------------------------------------------------------------------- /luminapath/targets/linux/musl/libutil.a: -------------------------------------------------------------------------------- 1 | ! 2 | -------------------------------------------------------------------------------- /luminapath/targets/linux/musl/libxnet.a: -------------------------------------------------------------------------------- 1 | ! 2 | -------------------------------------------------------------------------------- /luminapath/targets/linux/syscall.asm: -------------------------------------------------------------------------------- 1 | global x86_64_syscall 2 | section .text 3 | 4 | ; system-v user application: rdi, rsi, rdx, rcx, r8, r9 5 | ; system-v kern application: rdi, rsi, rdx, r10, r8, r9 6 | 7 | x86_64_syscall: 8 | push rax 9 | mov r10, rcx 10 | mov rax, r9 ; the last parameter is used as the syscall number instead of an actual parameter 11 | syscall 12 | pop rax 13 | ret 14 | -------------------------------------------------------------------------------- /luminapath/targets/linux/syscall.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luminalang/lumina/259c0fc2f7b6487a5c3aa24ed0fc9bd9214c8674/luminapath/targets/linux/syscall.o -------------------------------------------------------------------------------- /luminapath/targets/linux/syscall/stub: -------------------------------------------------------------------------------- 1 | This file exists so that git is willing to commit the directory 2 | -------------------------------------------------------------------------------- /luminapath/targets/windows/stub: -------------------------------------------------------------------------------- 1 | This file exists so that git is willing to commit the directory 2 | -------------------------------------------------------------------------------- /mdbook/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["Simon Larsson"] 3 | language = "en" 4 | multilingual = false 5 | src = "src" 6 | title = "Lumina Showcase" 7 | 8 | [output.html] 9 | git-repository-url = "https://github.com/luminalang/lumina" 10 | -------------------------------------------------------------------------------- /mdbook/src/README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | The Lumina Programming Language 4 | 5 | Lumina is an eager-by-default natively compiled functional programming language with the core goals of readibility, practicality, compiler-driven development and simplicity. 6 | 7 | It aims to be a high-level general-purpose language, but also support systems-level functionality such as raw pointer arithmetics to allow for high-level abstractions to be built on top of low-level high performance Lumina code. 8 | -------------------------------------------------------------------------------- /mdbook/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | [The Lumina Programming Language](./README.md) 4 | 5 | # Getting Started 6 | 7 | - [Installation](./getting-started/installation.md) 8 | - [Compiling & Running Programs](./getting-started/compiling-and-running.md) 9 | - [Creating a Lumina Project](./getting-started/create-project.md) 10 | 11 | # Features 12 | 13 | - [Functions](./features/functions.md) 14 | - [Types](./features/types.md) 15 | - [let & do](./features/let-and-do.md) 16 | - [Pattern Matching & Conditionals](./features/matching.md) 17 | - [Closures & Partial Application](./features/partial.md) 18 | - [Lists & Strings](./features/list-and-strings.md) 19 | - [Declaring Global Values](./features/val.md) 20 | - [Module System](./features/modules.md) 21 | - [Generics & Traits](./features/generics-and-traits.md) 22 | - [Pipes (dot notation)](./features/pipes.md) 23 | - [Attributes](./features/attributes.md) 24 | 25 | # Advanced Features 26 | 27 | - [Pointer Arithmetics](./features/pointers.md) 28 | - [Calling C functions via FFI](./features/ffi.md) 29 | - [Updating Nested Records](./features/nested-records.md) 30 | 31 | # Standard Library 32 | 33 | - [Available Targets](./std/targets.md) 34 | - [IO and the file system](./std/io.md) 35 | - [List and its Sub-types](./std/lists.md) 36 | -------------------------------------------------------------------------------- /mdbook/src/features/attributes.md: -------------------------------------------------------------------------------- 1 | # Attributes 2 | 3 | Attributes serve as settings for the compiler and may be placed above any item. 4 | 5 | ```lumina 6 | @[platform "linux"] 7 | fn platform_name = "linux" 8 | 9 | @[platform "windows"] 10 | fn platform_name = "windows" 11 | 12 | @[precedence 2500] 13 | fn & as int int -> int = ... 14 | ``` 15 | -------------------------------------------------------------------------------- /mdbook/src/features/ffi.md: -------------------------------------------------------------------------------- 1 | # Calling C functions via FFI 2 | -------------------------------------------------------------------------------- /mdbook/src/features/functions.md: -------------------------------------------------------------------------------- 1 | # Functions 2 | 3 | ## Declaring Functions 4 | 5 | Functions are defined using the `fn` keyword. 6 | 7 | ```lm 8 | fn add x y as int int -> int = 9 | x + y 10 | ``` 11 |      A function which takes two parameters, x and y of type int, then adds them together to return a single int 12 | 13 | The type annotation is optional. 14 | 15 | ```lm 16 | fn add x y = x + y 17 | ``` 18 | Although remember that type annotations often help as a form of documentation. 19 | 20 | ## Calling Functions 21 | 22 | White-space is used to separate the arguments to a function 23 | 24 | ```lm 25 | fn main = 26 | add 1 2 27 | ``` 28 | 29 | Parenthesis can also be used to 'group' expressions. 30 | 31 | ```lm 32 | fn main = 33 | std:io:println (add (add 1 2) (add 3 4)) 34 | ``` 35 |      A function adding numbers then printing them to the terminal 36 | 37 | ## Pattern Parameters 38 | 39 | The parameters in a function declaration may be any infallible pattern, not just plain identifiers. 40 | 41 | ```lm 42 | fn add_pairs xy ab as (int, int) (int, int) -> (int, int) = 43 | ... 44 | 45 | // can instead be written as 46 | 47 | fn add_pairs (x, y) (a, b) as (int, int) (int, int) -> (int, int) = 48 | (x + a, y + b) 49 | 50 | fn main = 51 | std:io:println (add_pairs (1, 2) (3, 4)) 52 | ``` 53 |      A function with two tuple parameters being pattern matched in the function declaration 54 | 55 | Read more about patterns in the [Pattern Matching & Conditionals](./matching.md) chapter 56 | 57 | ## Where-Bindings 58 | 59 | A function may also be defined inside another function, of which it'll have access to its parent function's parameters. 60 | 61 | ```lm 62 | fn main = 63 | std:io:println (add 5 6) 64 | where 65 | fn add x y = x + y 66 | ``` 67 |      A function declared inside another function as a where-binding 68 | 69 | ## Operators 70 | 71 | New operators can also be defined similarly to functions. 72 | 73 | ```lm 74 | fn +++ left right as int int -> int = 75 | left + right + right + right 76 | ``` 77 | -------------------------------------------------------------------------------- /mdbook/src/features/generics-and-traits.md: -------------------------------------------------------------------------------- 1 | # Generics & Traits 2 | 3 | Generics are used for making types and functions more flexible. 4 | 5 | Imagine a scenario such as 6 | 7 | ```lumina 8 | fn select cond x y as bool string string -> string = 9 | if cond 10 | then x 11 | else y 12 | 13 | fn main = 14 | select (1 > 2) "this" "that" 15 | . io:println 16 | ``` 17 | 18 | Here we're using the type `string`. However; the function `select` doesn't actually care which type its used with. 19 | 20 | Instead; we use a generic type. 21 | 22 | ```lumina 23 | fn select cond x y as bool a a -> a = 24 | ... 25 | ``` 26 | 27 | When defining functions, any lowercase single-letter type will be treated as a generic type. 28 | 29 | Generics can also be used for type declarations to grant type parameters for a declared type. 30 | 31 | ```lumina 32 | type WithId v { 33 | id int 34 | value v 35 | } 36 | 37 | fn create_user as string -> WithId User = 38 | ... 39 | ``` 40 | 41 | 42 | ## Traits 43 | 44 | If `type T {...}` defines what a type has, and `type T = A | B` defines what a type can have, then `trait` defines what a type should be able to do. 45 | 46 | ```lumina 47 | trait Animal 48 | fn make_noise as string 49 | fn move as (int, int) -> (int, int) 50 | ``` 51 | 52 | Traits can be used in one of two ways. Either as a type by itself, which we call a trait object. Or as a constraint for generic types. 53 | 54 | ```lumina 55 | // Used as trait object 56 | fn add as Animal Zoo -> Zoo = 57 | ... 58 | 59 | // Used as trait constraint 60 | when 61 | a can Animal 62 | fn add as a Zoo -> Zoo = 63 | ... 64 | ``` 65 | 66 | To implement a trait for a type, use the `impl` keyword. 67 | 68 | ```lumina 69 | type Cat 70 | 71 | impl Animal for Cat 72 | fn make_noise = "meow" 73 | fn move (x, y) = (x + 5, y + 2) 74 | ``` 75 | 76 | *TODO: Most of this only makes sense if you're used to Rust or some degree Haskell. It should be explained more fundamentally.* 77 | -------------------------------------------------------------------------------- /mdbook/src/features/let-and-do.md: -------------------------------------------------------------------------------- 1 | # Evaluating Multiple Expressions 2 | 3 | ### Let bindings 4 | 5 | As a function grows larger, complex, or more and more nested. It can become difficult to read. 6 | 7 | ```lm 8 | fn main = 9 | add (add 1 2) (add 3 4) 10 | ``` 11 | 12 | A useful way to mitigate this is to seperate out the expressions into a sequence of steps. 13 | `let` allows you to bind the result of an expression to a pattern (often an identifier). 14 | 15 | ```lm 16 | fn main = 17 | let x = add 1 2 in 18 | let y = add 3 4 in 19 | add x y 20 | ``` 21 | 22 | This also comes with the benefit of allowing you to *name* the result of an expression, which is important for readability. 23 | 24 | The left side of a let binding can be any pattern, and not just an identifier. 25 | 26 | ```lm 27 | fn main = 28 | let (x, y) = (add 1 2, add 3 4) 29 | in add x y 30 | ``` 31 |      A let-binding with a tuple pattern binding to a tuple expression 32 | 33 | ### Do expressions 34 | 35 | Sometimes it's useful to run an expression for its side effects without using its return type. 36 | In those cases, `do...then` notation works similarly to `let` except it always discards the value instead of binding it to a pattern. 37 | 38 | ```lm 39 | fn main = 40 | // Run an expression 41 | do io:println "Starting Program..." then 42 | 0 // Then do something else to generate a value 43 | 44 | // ... Is a clearer way of writing 45 | 46 | fn main = 47 | // Run an expression 48 | let _ = io:println "Starting Program..." in 49 | 0 // Then do something else to generate a value 50 | ``` 51 | -------------------------------------------------------------------------------- /mdbook/src/features/list-and-strings.md: -------------------------------------------------------------------------------- 1 | # Lists & Strings 2 | -------------------------------------------------------------------------------- /mdbook/src/features/matching.md: -------------------------------------------------------------------------------- 1 | # Pattern Matching & Conditionals 2 | 3 | **Pattern Matching with `match`** 4 | 5 | Lumina provides pattern matching via the `match` keyword. A `match` expression takes one expression as input and then a set of branches which checks for a pattern to run a specified expression. 6 | A pattern is in some ways the inverse of an expression. Expressions construct and create data, while patterns destruct data. Destructing data with a pattern will allow you to check for whether the data looks a particular way, while also binding specific bits of data to new identifiers. 7 | 8 | Pattern matching is a core component of Lumina and will be the primary part of most functions. 9 | 10 | ```lm 11 | // Matching integers 12 | fn check_integer n as int -> string = 13 | match n 14 | | 0 -> "zero" 15 | | 1 -> "one" 16 | | 2..9 -> "digit" 17 | | _ -> "large number" 18 | 19 | // Matching sum types 20 | fn or default m as int (Maybe int) -> int = 21 | match m 22 | | Nothing -> default 23 | | Just n -> n 24 | 25 | // Matching records 26 | fn encode_user user as User -> string = 27 | match user 28 | | { kind = Admin, name, .. } -> "admin_" <++> name 29 | | { kind = Guest, name, .. } -> "guest_" <++> name 30 | 31 | // Type annotation is optional 32 | | { User | kind = Guest, name, .. } -> "guest_" <++> name 33 | 34 | // Matching strings 35 | fn parse_user str as string -> (int, string) = 36 | match str 37 | | "id:" id " username:" username -> 38 | (to_digit id, username) 39 | | _ -> 40 | crash ("parsing error: " <++> str) 41 | 42 | // Matching lists 43 | fn first_three list as [int] -> Maybe (int, int, int) = 44 | match list 45 | | [x, y, z : _rest] -> Just (x, y, z) 46 | | _ -> Nothing 47 | 48 | // Matching tuples 49 | fn far_away position as (int, int) -> bool = 50 | match position 51 | | (100.., _) -> true 52 | | (_, 100..) -> true 53 | | (_, _) -> false 54 | ``` 55 | 56 | *TODO: Should we show and explain string extractors here or under advanced features? Should probably be after partial application* 57 | 58 | **If Expressions** 59 | 60 | ```lm 61 | fn can_drive age = 62 | if age > 18 63 | then "This person can own a drivers license" 64 | else "This person can not own a drivers license" 65 | ``` 66 | -------------------------------------------------------------------------------- /mdbook/src/features/modules.md: -------------------------------------------------------------------------------- 1 | # Module System 2 | 3 | A Lumina project is structured like this 4 | 5 | ``` 6 | project-name 7 | ├── config.lm 8 | └── src 9 | ├── main.lm 10 | ├── other_dir 11 | │   ├── file.lm 12 | │   └── lib.lm 13 | └── other_file.lm 14 | ``` 15 | 16 | When importing modules inside of Lumina source code, the relative filepath corresponds directly to the path in the use item. 17 | 18 | So from `main.lm` the other modules would be imported as. 19 | 20 | ```lumina 21 | use other_dir:file 22 | use other_dir 23 | use other_file 24 | ``` 25 | 26 | `lib.lm` and `main.lm` are magic filenames which are put under the namespace of the folder they're in. 27 | So to import `other_dir/lib.lm` in `main.lm` then instead of `use other_dir:lib`, you'd use `use other_dir` 28 | 29 | When importing a module, you can also import items from that module directly. 30 | 31 | ```lumina 32 | use other_file:file [Direction [Right, Down]] 33 | // ^ Item 34 | // ^ Members under Item 35 | ``` 36 | 37 | As opposed to using items through the module name. 38 | 39 | ```lumina 40 | fn down = file:Direction:Down 41 | ``` 42 | -------------------------------------------------------------------------------- /mdbook/src/features/nested-records.md: -------------------------------------------------------------------------------- 1 | # Updating Nested Records 2 | 3 | ```lumina 4 | // Setting a field that's deeply nested in records 5 | fn set_id id e as int Entity -> Entity = 6 | { e ~ information.tracking.item.id = id } 7 | 8 | // Modifying a field that's deeply nested in records 9 | fn inc_id e as Entity -> Entity = 10 | { e ~ information.tracking.item.id @ id = id + 1 } 11 | ``` 12 | -------------------------------------------------------------------------------- /mdbook/src/features/partial.md: -------------------------------------------------------------------------------- 1 | # Partial Application 2 | 3 | A large part of why functional programming is so powerful is being able to conveniently pass around functions as if they're values. 4 | 5 | This lets you create functions whose behavior is more generalised as a portion of the behavior can be passed down as a parameter. 6 | 7 | **The Closure Type** 8 | 9 | Among types such as `int` or `(float, float)`, functions-as-values also have types in the form of closures. 10 | 11 | ```lm 12 | (fn int int -> int) 13 | ``` 14 |      The type of a closure which expects two `int` parameters and returns an `int` 15 | 16 | So for example; 17 | 18 | ```lm 19 | fn change_if_just f m as (fn int -> int) (Maybe int) -> Maybe int = 20 | match m 21 | | Nothing -> Nothing 22 | | Just n -> Just (f n) 23 | ``` 24 |      A function which runs a function to change a value if it exists 25 | 26 | **The Magic `#` Unary Operator** 27 | 28 | To treat a function as if it's a value, the `#` symbol is used. 29 | 30 | ```lm 31 | fn add_five_if_just m as Maybe int -> Maybe int = 32 | change_if_just #add_five m 33 | 34 | fn add_five x as int -> int = 35 | x + 5 36 | ``` 37 |      A function which adds 5 to an integer if it exists 38 | 39 | The `#` symbol is a general-purpose way to pass various expressions as closures and can be used in a couple of different ways. 40 | 41 | ```lm 42 | fn add x y as int int -> int = x + y 43 | 44 | // ... // 45 | 46 | // turns the function into a closure of type 47 | // (fn int int -> int) 48 | #add 49 | 50 | // partially applys the function to create a closure with one less parameter 51 | // (fn int -> int) 52 | #(add 5) 53 | 54 | // partially applys an operator to create a closure with a single parameter 55 | // (fn int -> int) 56 | #(+ 5) 57 | 58 | // turn a literal value into a closure 59 | // (fn -> int) 60 | #5 61 | ``` 62 | With this in mind, we can rewrite the previous example as: 63 | 64 | ```lm 65 | fn add_five_if_just m as Maybe int -> Maybe int = 66 | change_if_just #(+ 5) m 67 | ``` 68 |      A function which adds 5 to an integer if it exists 69 | 70 | 71 | **Anonymous Functions with Lambdas** 72 | 73 | If a function doesn't seem important enough to give it a name, we can inline it with a lambda. 74 | 75 | ```lm 76 | fn main = 77 | (\n -> n + 1) 5 78 | ``` 79 |      Runs the inline function with the parameter `5` to create `6` 80 | 81 | Lambdas can also be passed as closures the same way as named functions. 82 | 83 | ```lm 84 | fn add_five_if_just m as Maybe int -> Maybe int = 85 | change_if_just #(\n -> n + 5) m 86 | ``` 87 | 88 | **Partially Applicating Where-Bindings** 89 | 90 | *TODO: Is it overly complicated and confusing to explain this here? Maybe we should have a separate design patterns chapter* 91 | 92 | A common design pattern is to partially apply where-bindings 93 | 94 | ```lm 95 | fn add_five_if_just m as Maybe int -> Maybe int = 96 | change_if_just #(add 5) 97 | where 98 | fn add x y = x + y 99 | 100 | fn change_if_just f as fn(int -> int) -> Maybe int = 101 | match m 102 | | Nothing -> Nothing 103 | | Just n -> Just (f n) 104 | ``` 105 | -------------------------------------------------------------------------------- /mdbook/src/features/pipes.md: -------------------------------------------------------------------------------- 1 | # Pipes (dot calls) 2 | 3 | As functional programming often devolves to a tree of many nested function calls, Lumina provides numerous ways to split out nesting to make the code more readable. 4 | 5 | The builtin `pipe` construct serves as an alternative way of chaining function calls in a much more intuitive way. 6 | 7 | ```lumina 8 | forEach #io:println (filter #(> 6) (map #(+ 4) [1, 2, 3])) 9 | 10 | // May instead be written as 11 | 12 | [1, 2, 3] 13 | . map #(+ 4) 14 | . filter #(> 6) 15 | . forEach #io:println 16 | ``` 17 | 18 | However; `.` provides extra convenience by resolving functions from the module of where the type from the left-hand-side expression is defined. 19 | 20 | Instead of 21 | 22 | ```lumina 23 | use std:list [forEach] 24 | use std:io 25 | 26 | fn main = [1, 2, 3] . forEach #io:println 27 | ``` 28 | 29 | You may write 30 | 31 | ```lumina 32 | use std:io 33 | 34 | // `[_]` is defined in `std:list`. Thus; `forEach` is resolved from the module `std:list` automatically. 35 | fn main = [1, 2, 3] . forEach #io:println 36 | ``` 37 | -------------------------------------------------------------------------------- /mdbook/src/features/pointers.md: -------------------------------------------------------------------------------- 1 | # Pointer Arithmetics 2 | -------------------------------------------------------------------------------- /mdbook/src/features/val.md: -------------------------------------------------------------------------------- 1 | # Declaring Global Values 2 | 3 | Global values can be declared with the `val` keyword. 4 | 5 | This is sometimes useful to provide clarity as opposed to defining functions for constants. 6 | 7 | ```lumina 8 | val min_members = 1 9 | val max_members = 20 10 | ``` 11 | 12 | All initializations for global values occur before the `main` function is ran. 13 | 14 | If you're writing low-level code then it might be useful to receieve the raw pointer of a global value and mutate it at runtime. 15 | 16 | ```lumina 17 | use std:io 18 | use std:ptr 19 | 20 | val count = 0 21 | 22 | fn main = 23 | let ptr = builtin:val_to_ref count in 24 | do ptr:write ptr 5 25 | then io:println count 26 | ``` 27 | -------------------------------------------------------------------------------- /mdbook/src/getting-started/compiling-and-running.md: -------------------------------------------------------------------------------- 1 | # Compiling & Running Programs 2 | 3 | The Lumina repository contains an example folder. 4 | 5 | To run one of the examples 6 | 7 | ```bash 8 | $ lumina run examples/hello-world 9 | Hello World! 10 | ``` 11 | 12 | Or to compile to a binary 13 | 14 | ```bash 15 | $ lumina build -o hello-world examples/hello-world 16 | $ ./hello-world 17 | Hello World! 18 | ``` 19 | -------------------------------------------------------------------------------- /mdbook/src/getting-started/create-project.md: -------------------------------------------------------------------------------- 1 | # Creating a Lumina Project 2 | 3 | To create a new Lumina project, use 4 | 5 | ```bash 6 | $ lumina init my-awesome-project/ 7 | $ cd my-awesome-project/ 8 | $ lumina run 9 | Hello World! 10 | ``` 11 | 12 | Lumina also ships with a code formatter. 13 | 14 | ```bash 15 | $ lumina fmt --overwrite my-awersome-project/ 16 | ``` 17 | 18 | It's currently experimental so don't use it for code you don't have a backup off. 19 | -------------------------------------------------------------------------------- /mdbook/src/getting-started/installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | ## Compiling from scratch 4 | 5 | You need to have the Rust compiler installed. 6 | 7 | Clone the repository 8 | 9 | ```bash 10 | $ git clone https://github.com/luminalang/lumina.git 11 | ``` 12 | 13 | Compile and install the compiler 14 | 15 | ```bash 16 | $ cd lumina/ 17 | $ cargo build --release 18 | $ sudo mv target/release/lumina /usr/bin/ 19 | ``` 20 | 21 | Copy the `luminapath` directory containing the Lumina libraries to a suitable runtime folder and point the `$LUMINAPATH` environment variable to it. 22 | 23 | ```bash 24 | $ cp -r luminapath/ $HOME/.local/share/lumina 25 | 26 | # If you're using bash 27 | $ echo "export LUMINAPATH=$HOME/.local/share/lumina/" >> $HOME/.bashrc 28 | ``` 29 | -------------------------------------------------------------------------------- /mdbook/src/std/io.md: -------------------------------------------------------------------------------- 1 | # IO and the file system 2 | -------------------------------------------------------------------------------- /mdbook/src/std/lists.md: -------------------------------------------------------------------------------- 1 | # List & its Sub-types 2 | -------------------------------------------------------------------------------- /mdbook/src/std/std.md: -------------------------------------------------------------------------------- 1 | # Standard Library 2 | -------------------------------------------------------------------------------- /mdbook/src/std/targets.md: -------------------------------------------------------------------------------- 1 | # Available Targets 2 | -------------------------------------------------------------------------------- /misc/lumina-compiler-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luminalang/lumina/259c0fc2f7b6487a5c3aa24ed0fc9bd9214c8674/misc/lumina-compiler-overview.png -------------------------------------------------------------------------------- /misc/lumina-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luminalang/lumina/259c0fc2f7b6487a5c3aa24ed0fc9bd9214c8674/misc/lumina-example.png -------------------------------------------------------------------------------- /tests/mem-autoboxed-struct/config.lm: -------------------------------------------------------------------------------- 1 | val name = "mem-autoboxed-struct" 2 | val version = "1.0" 3 | val authors = [] 4 | val prelude = "ext:minimal-env:src:prelude" 5 | 6 | val dependencies = [] 7 | 8 | -------------------------------------------------------------------------------- /tests/mem-autoboxed-struct/expected: -------------------------------------------------------------------------------- 1 | 70 2 | -------------------------------------------------------------------------------- /tests/mem-autoboxed-struct/src/main.lm: -------------------------------------------------------------------------------- 1 | type LargeStruct elem { 2 | a i32 3 | b i8 4 | tuple (elem, elem) 5 | d i64 6 | e i32 7 | f i32 8 | } 9 | 10 | type Indirect extra { 11 | v i32 12 | and_then extra 13 | } 14 | 15 | type Construct { field LargeStruct (Indirect (LargeStruct i32)) } 16 | 17 | fn passes v as Construct -> Construct = v 18 | 19 | fn returns a b v d e f arg as i32 i8 i32 i64 i32 i32 i32 -> Construct = 20 | { 21 | field = 22 | { 23 | a 24 | , b 25 | , tuple = 26 | ( 27 | { v, and_then = { a, b, tuple = (arg, arg), d, e, f } } 28 | , { v, and_then = { a, b, tuple = (arg, arg), d, e, f } } 29 | ) 30 | , d 31 | , e 32 | , f 33 | } 34 | } 35 | 36 | fn exits { field = { a, b, tuple = (fst, snd), d, e, f } } as Construct -> () = 37 | let (fst_arg_one, fst_arg_two) = fst.and_then.tuple in 38 | let (snd_arg_one, snd_arg_two) = snd.and_then.tuple in 39 | libc_exit 40 | ( 41 | a 42 | + (fst.v + fst.and_then.a + fst.and_then.e + fst.and_then.f) 43 | + (snd.v + snd.and_then.a + snd.and_then.e + snd.and_then.f) 44 | + (fst_arg_one + fst_arg_two + snd_arg_one + snd_arg_two) 45 | + e 46 | + f 47 | ) 48 | 49 | // a + v + a + e + f + v + a + e + f + arg + arg + arg + arg + e + f == 70 50 | fn main = 51 | exits (passes (returns 1 2 3 4 5 6 7)) 52 | 53 | -------------------------------------------------------------------------------- /tests/mem-large-struct/config.lm: -------------------------------------------------------------------------------- 1 | val name = "mem-large-struct" 2 | val version = "1.0" 3 | val authors = [] 4 | val prelude = "ext:minimal-env:src:prelude" 5 | 6 | val dependencies = [] 7 | 8 | -------------------------------------------------------------------------------- /tests/mem-large-struct/expected: -------------------------------------------------------------------------------- 1 | 21 2 | -------------------------------------------------------------------------------- /tests/mem-large-struct/src/main.lm: -------------------------------------------------------------------------------- 1 | type LargeStruct { 2 | a i32 3 | b i8 4 | c i32 5 | d i64 6 | e i32 7 | f i32 8 | } 9 | 10 | fn passes v as LargeStruct -> LargeStruct = v 11 | 12 | fn returns a b c d e f as i32 i8 i32 i64 i32 i32 -> LargeStruct = 13 | { a, b, c, d, e, f } 14 | 15 | fn exits { a, b, c, d, e, f } as LargeStruct -> () = 16 | do libc_exit (a + (b as i32) + c + (d as i32) + e + f) 17 | then () 18 | 19 | fn main = 20 | exits (passes (returns 1 2 3 4 5 6)) 21 | 22 | -------------------------------------------------------------------------------- /tests/mem-large-sum/config.lm: -------------------------------------------------------------------------------- 1 | val name = "mem-large-sum" 2 | val version = "1.0" 3 | val authors = [] 4 | val prelude = "ext:minimal-env:src:prelude" 5 | 6 | val dependencies = [] 7 | 8 | -------------------------------------------------------------------------------- /tests/mem-large-sum/expected: -------------------------------------------------------------------------------- 1 | 10 2 | -------------------------------------------------------------------------------- /tests/mem-large-sum/src/main.lm: -------------------------------------------------------------------------------- 1 | type Option = Some (i32, i32, i32, i32) | None 2 | 3 | fn passes v as Option -> Option = v 4 | 5 | fn returns a as (i32, i32, i32, i32) -> Option = 6 | Some a 7 | 8 | fn exits a as Option -> () = 9 | match a 10 | | Some (a, b, c, d) -> libc_exit (a + b + c + d) 11 | | None -> libc_exit 100 12 | 13 | fn main = 14 | exits (passes (returns (1, 2, 3, 4))) 15 | 16 | -------------------------------------------------------------------------------- /tests/mem-nested-combination/config.lm: -------------------------------------------------------------------------------- 1 | val name = "mem-nested-combination" 2 | val version = "1.0" 3 | val authors = [] 4 | val prelude = "ext:minimal-env:src:prelude" 5 | 6 | val dependencies = [] 7 | 8 | -------------------------------------------------------------------------------- /tests/mem-nested-combination/expected: -------------------------------------------------------------------------------- 1 | 28 2 | -------------------------------------------------------------------------------- /tests/mem-nested-combination/src/main.lm: -------------------------------------------------------------------------------- 1 | type SmallStruct { 2 | x i32 3 | y i32 4 | } 5 | 6 | type LargeStruct { 7 | a i32 8 | b i8 9 | small SmallStruct 10 | c i32 11 | d i64 12 | e i32 13 | f i32 14 | } 15 | 16 | type Option = Some LargeStruct | None 17 | 18 | fn passes v as Option -> Option = v 19 | 20 | fn returns (a, b, x, y, c, d, e, f) as (i32, i8, i32, i32, i32, i64, i32, i32) -> Option = 21 | Some { a, b, small = { x, y }, c, d, e, f } 22 | 23 | fn exits a as Option -> () = 24 | match a 25 | | Some { a, b, small = { x, y }, c, d, e, f } -> libc_exit (a + x + y + c + e + f) 26 | | None -> libc_exit 100 27 | 28 | fn main = 29 | exits (passes (returns (1, 2, 3, 4, 5, 6, 7, 8))) 30 | 31 | -------------------------------------------------------------------------------- /tests/mem-recursive-sum/config.lm: -------------------------------------------------------------------------------- 1 | val name = "mem-recursive-sum" 2 | val version = "1.0" 3 | val authors = [] 4 | val prelude = "ext:minimal-env:src:prelude" 5 | 6 | val dependencies = [] 7 | 8 | -------------------------------------------------------------------------------- /tests/mem-recursive-sum/expected: -------------------------------------------------------------------------------- 1 | 40 2 | -------------------------------------------------------------------------------- /tests/mem-recursive-sum/src/main.lm: -------------------------------------------------------------------------------- 1 | type Option a = Some a | None 2 | 3 | fn passes v as Option (Option (Option i32)) -> Option (Option (Option i32)) = v 4 | 5 | fn returns a as i32 -> Option (Option (Option i32)) = 6 | Some a . Some . Some 7 | 8 | fn exits a as Option (Option (Option i32)) -> () = 9 | match a 10 | | Some (Some (Some n)) -> libc_exit n 11 | | _ -> libc_exit 100 12 | 13 | fn main = 14 | exits (passes (returns 40)) 15 | 16 | -------------------------------------------------------------------------------- /tests/mem-small-struct/config.lm: -------------------------------------------------------------------------------- 1 | val name = "mem-layouts" 2 | val version = "1.0" 3 | val authors = [] 4 | val prelude = "ext:minimal-env:src:prelude" 5 | 6 | val dependencies = [] 7 | 8 | -------------------------------------------------------------------------------- /tests/mem-small-struct/expected: -------------------------------------------------------------------------------- 1 | 3 2 | -------------------------------------------------------------------------------- /tests/mem-small-struct/src/main.lm: -------------------------------------------------------------------------------- 1 | type SmallStruct { 2 | x i32 3 | y i32 4 | } 5 | 6 | fn passes v as SmallStruct -> SmallStruct = v 7 | 8 | fn returns x y as i32 i32 -> SmallStruct = 9 | { x, y } 10 | 11 | fn exits { x, y } as SmallStruct -> () = 12 | do libc_exit (x + y) 13 | then () 14 | 15 | fn main = 16 | exits (passes (returns 1 2)) 17 | 18 | -------------------------------------------------------------------------------- /tests/mem-small-sum/config.lm: -------------------------------------------------------------------------------- 1 | val name = "mem-small-sum" 2 | val version = "1.0" 3 | val authors = [] 4 | val prelude = "ext:minimal-env:src:prelude" 5 | 6 | val dependencies = [] 7 | 8 | -------------------------------------------------------------------------------- /tests/mem-small-sum/expected: -------------------------------------------------------------------------------- 1 | 40 2 | -------------------------------------------------------------------------------- /tests/mem-small-sum/src/main.lm: -------------------------------------------------------------------------------- 1 | type Option = Some i32 | None 2 | 3 | fn passes v as Option -> Option = v 4 | 5 | fn returns a as i32 -> Option = 6 | Some a 7 | 8 | fn exits a as Option -> () = 9 | match a 10 | | Some n -> libc_exit n 11 | | None -> libc_exit 100 12 | 13 | fn main = 14 | exits (passes (returns 40)) 15 | 16 | -------------------------------------------------------------------------------- /tests/mem-sum-in-struct/config.lm: -------------------------------------------------------------------------------- 1 | val name = "mem-sum-in-struct" 2 | val version = "1.0" 3 | val authors = [] 4 | val prelude = "ext:minimal-env:src:prelude" 5 | 6 | val dependencies = [] 7 | 8 | -------------------------------------------------------------------------------- /tests/mem-sum-in-struct/expected: -------------------------------------------------------------------------------- 1 | 20 2 | -------------------------------------------------------------------------------- /tests/mem-sum-in-struct/src/main.lm: -------------------------------------------------------------------------------- 1 | type Wrapper { 2 | x i8 3 | sum Option 4 | y i32 5 | z i64 6 | } 7 | 8 | type Option = Some (i32, i32, i32, i32) | None 9 | 10 | fn passes v as Wrapper -> Wrapper = v 11 | 12 | fn returns (x, a, b, c, d, y, z) as (i8, i32, i32, i32, i32, i32, i64) -> Wrapper = 13 | { x, sum = Some (a, b, c, d), y, z } 14 | 15 | fn exits { x, sum, y, z } as Wrapper -> () = 16 | match sum 17 | | Some (a, b, c, d) -> libc_exit (a + b + c + d + y) 18 | | None -> libc_exit 100 19 | 20 | fn main = 21 | exits (passes (returns (1, 2, 3, 4, 5, 6, 7))) 22 | 23 | --------------------------------------------------------------------------------