├── .github └── workflows │ └── ci.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── allocator └── allocator.c ├── comparison ├── circular_linked_list_abstraction.rs ├── enum_catch_all.rs ├── lifetime_alloc_infer_success.rs └── struct.rs ├── docs ├── architecture.md ├── language.md └── philosophy.md ├── editors └── vscode │ ├── .github │ └── workflows │ │ └── main.yml │ ├── .gitignore │ ├── .vscode │ ├── launch.json │ └── tasks.json │ ├── .vscodeignore │ ├── CHANGELOG.md │ ├── LICENSE.txt │ ├── README.md │ ├── gulpfile.js │ ├── images │ └── icon.png │ ├── language-configuration.json │ ├── package-lock.json │ ├── package.json │ ├── syntaxes │ ├── june.tmLanguage.json │ └── june.tmLanguage.yml │ └── test │ └── test.rs ├── examples └── june_hello_world │ ├── .gitignore │ ├── June.toml │ └── main │ └── main.june ├── planning.md ├── src ├── build.rs ├── codegen.rs ├── compiler.rs ├── errors.rs ├── lifetime_checker.rs ├── main.rs ├── new.rs ├── parser.rs ├── run.rs └── typechecker.rs └── tests ├── integration ├── classes │ ├── inheritance │ │ ├── named_args_coercion.june │ │ ├── partial_implementation.june │ │ └── simple.june │ └── private_by_default.june ├── control_flow │ ├── for_range.june │ ├── for_range_var.june │ ├── loop.june │ └── loop_with_temporary.june ├── data_structures │ ├── circular_linked_list.june │ ├── circular_linked_list_abstraction.june │ ├── circular_linked_list_abstraction_shared.june │ ├── circular_linked_list_abstraction_shorthand.june │ ├── circular_linked_list_helper.june │ └── vector_of_ints.june ├── data_types │ ├── boolean.june │ ├── c_char.june │ ├── c_string.june │ ├── double.june │ └── int.june ├── defer │ ├── defer_simple.june │ └── defer_simple_error.june ├── enums │ ├── enum_catchall.june │ ├── enum_method.june │ ├── enum_missing_arm_error.june │ ├── enum_multi_arm.june │ ├── enum_same_name.june │ ├── enum_simple.june │ ├── enum_simple_error.june │ ├── enum_single_payload.june │ ├── enum_single_pointer_payload.june │ ├── enum_static_method.june │ ├── enum_struct_payload.june │ ├── enum_var_scope_error.june │ ├── enum_var_scope_error2.june │ └── enum_var_scope_error3.june ├── extern_c │ ├── file_io_abstraction.june │ ├── file_ptr.june │ ├── file_ptr_all.june │ ├── file_ptr_defer.june │ └── puts.june ├── first_class_functions │ ├── first_class_function_allocation.june │ ├── first_class_function_allocation2.june │ ├── first_class_function_arg.june │ ├── first_class_function_return.june │ ├── first_class_function_return_error.june │ ├── first_class_function_return_error2.june │ ├── first_class_function_value.june │ ├── first_class_function_value_call.june │ └── first_class_function_value_call_error.june ├── generics │ ├── generic_enum.june │ ├── generic_enum2.june │ ├── generic_enum_struct_case.june │ ├── generic_fun.june │ ├── generic_method.june │ └── generic_struct.june ├── hello_world │ ├── hello_fun.june │ ├── hello_fun_rev_order.june │ ├── hello_fun_rev_order2.june │ ├── hello_main.june │ └── hello_world.june ├── jason.june ├── jason2.june ├── jason3.june ├── jason_variant1.june ├── lifetime_inference │ ├── alias_inference.june │ ├── alias_inference2.june │ ├── alias_inference_error.june │ ├── alias_return_error.june │ ├── annotation_param_compatible.june │ ├── annotation_return_compatible.june │ ├── annotation_return_compatible2.june │ ├── cleanup_on_return.june │ ├── cleanup_on_return2.june │ ├── escaping_local_alt_error.june │ ├── escaping_local_error.june │ ├── escaping_local_error2.june │ ├── incompatible_param_error.june │ ├── incompatible_param_error2.june │ ├── lifetime_alloc_infer_error.june │ ├── lifetime_alloc_infer_success.june │ ├── lifetime_error.june │ ├── lifetime_error2.june │ ├── lifetime_infer_helper_function.june │ ├── lifetime_infer_helper_function2.june │ ├── lifetime_with_constraints.june │ ├── passthrough_error.june │ ├── passthrough_error2.june │ ├── scope_struct.june │ ├── scope_struct_to_upper_scope.june │ └── scope_struct_to_upper_scope2.june ├── math │ ├── bitwise.june │ ├── int_math.june │ ├── int_math_main.june │ ├── int_math_negative.june │ └── math_in_brackets.june ├── modules │ ├── collisions.june │ ├── main.june │ ├── utils.june │ └── utils2.june ├── owned_abstractions │ ├── moved_owned_var_error.june │ ├── moved_owned_var_error2.june │ ├── moved_owned_var_error3.june │ ├── moved_owned_var_into_shared_error.june │ ├── multiple_method_calls_on_self.june │ ├── owned_method_doesnt_permanently_move.june │ ├── owned_struct.june │ ├── owned_struct_error.june │ ├── owned_struct_error2.june │ ├── owned_struct_error3.june │ ├── owned_struct_private.june │ └── owned_struct_private2.june ├── parsing │ ├── bad_condition.june │ ├── unicode.june │ ├── unicode2.june │ └── unicode3.june ├── raw_buffers │ ├── raw_buffer_argument.june │ ├── raw_buffer_assignment.june │ ├── raw_buffer_assignment_error.june │ ├── raw_buffer_defer.june │ ├── raw_buffer_resize.june │ ├── raw_buffer_return.june │ ├── raw_buffer_simple.june │ └── raw_buffer_with_pointers.june ├── structs │ ├── method_and_struct_allocator.june │ ├── method_immutable_self_error.june │ ├── method_in_method.june │ ├── method_mutation.june │ ├── method_simple.june │ ├── method_simple_error.june │ ├── static_method.june │ ├── struct.june │ ├── struct_allocator.june │ ├── struct_arg_count_error.june │ ├── struct_field.june │ ├── struct_field_access_caller.june │ ├── struct_field_access_caller2.june │ ├── struct_field_access_locally.june │ ├── struct_field_deep.june │ ├── struct_field_helper.june │ ├── struct_field_math.june │ ├── struct_field_math_mainless.june │ ├── struct_field_mutable_param.june │ ├── struct_field_private.june │ ├── struct_field_private2.june │ ├── struct_field_private3.june │ ├── struct_field_private_error.june │ ├── struct_field_private_error2.june │ ├── struct_field_private_error3.june │ ├── struct_field_update.june │ ├── struct_helper.june │ ├── struct_helper_deep.june │ ├── struct_in_fun.june │ ├── struct_in_fun_error.june │ ├── struct_in_struct.june │ └── struct_new_field_error.june ├── typechecking │ ├── call_labeled_error.june │ ├── function_in_function.june │ ├── global_variable_error.june │ ├── mutability_mismatch_error.june │ ├── params_dont_leak_error.june │ ├── raw_buffer_type_inference.june │ ├── raw_buffer_type_inference2.june │ ├── return_infer_error.june │ ├── return_missing_error.june │ ├── return_missing_value_error.june │ ├── return_top_level_error.june │ ├── return_value.june │ └── return_value_error.june └── variables │ ├── variable.june │ ├── variable_and_function.june │ ├── variable_bad_optional_type.june │ ├── variable_mutation.june │ ├── variable_mutation2.june │ ├── variable_mutation3.june │ ├── variable_mutation_error.june │ ├── variable_simple.june │ ├── variable_simple_error.june │ └── variable_simple_error2.june ├── integration_tests.rs └── test_data └── alphabet.txt /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | on: 2 | pull_request: 3 | push: 4 | branches: 5 | - main 6 | 7 | name: continuous-integration 8 | 9 | env: 10 | NUSHELL_CARGO_PROFILE: ci 11 | NU_LOG_LEVEL: DEBUG 12 | CLIPPY_OPTIONS: "-D warnings" 13 | 14 | jobs: 15 | fmt-clippy: 16 | strategy: 17 | fail-fast: true 18 | matrix: 19 | # Pinning to Ubuntu 20.04 because building on newer Ubuntu versions causes linux-gnu 20 | # builds to link against a too-new-for-many-Linux-installs glibc version. Consider 21 | # revisiting this when 20.04 is closer to EOL (April 2025) 22 | platform: [macos-latest, ubuntu-20.04, windows-latest] 23 | feature: [default] 24 | include: 25 | - feature: default 26 | flags: "" 27 | 28 | runs-on: ${{ matrix.platform }} 29 | 30 | steps: 31 | - uses: actions/checkout@v4 32 | 33 | - name: Setup Rust toolchain and cache 34 | uses: actions-rust-lang/setup-rust-toolchain@v1.5.0 35 | with: 36 | rustflags: "" 37 | 38 | - name: cargo fmt 39 | run: cargo fmt --all -- --check 40 | 41 | - name: Clippy 42 | run: cargo clippy --workspace ${{ matrix.flags }} -- $CLIPPY_OPTIONS 43 | 44 | # In tests we don't have to deny unwrap 45 | - name: Clippy of tests 46 | run: cargo clippy --tests --workspace ${{ matrix.flags }} -- -D warnings 47 | 48 | tests: 49 | strategy: 50 | fail-fast: true 51 | matrix: 52 | platform: [macos-latest, ubuntu-20.04, windows-latest] 53 | feature: [default] 54 | include: 55 | - feature: default 56 | flags: "" 57 | flags: 58 | - # default 59 | - --all-features 60 | 61 | runs-on: ${{ matrix.platform }} 62 | 63 | steps: 64 | - uses: actions/checkout@v4 65 | 66 | - name: Setup Rust toolchain and cache 67 | uses: actions-rust-lang/setup-rust-toolchain@v1.5.0 68 | with: 69 | rustflags: "" 70 | 71 | - name: Tests 72 | run: cargo test ${{ matrix.flags }} 73 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | tags 3 | tags.lock 4 | tags.temp 5 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | june_language@fastmail.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.21.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler" 16 | version = "1.0.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 19 | 20 | [[package]] 21 | name = "aho-corasick" 22 | version = "1.1.2" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" 25 | dependencies = [ 26 | "memchr", 27 | ] 28 | 29 | [[package]] 30 | name = "anstream" 31 | version = "0.6.13" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" 34 | dependencies = [ 35 | "anstyle", 36 | "anstyle-parse", 37 | "anstyle-query", 38 | "anstyle-wincon", 39 | "colorchoice", 40 | "utf8parse", 41 | ] 42 | 43 | [[package]] 44 | name = "anstyle" 45 | version = "1.0.6" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" 48 | 49 | [[package]] 50 | name = "anstyle-parse" 51 | version = "0.2.3" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" 54 | dependencies = [ 55 | "utf8parse", 56 | ] 57 | 58 | [[package]] 59 | name = "anstyle-query" 60 | version = "1.0.2" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" 63 | dependencies = [ 64 | "windows-sys 0.52.0", 65 | ] 66 | 67 | [[package]] 68 | name = "anstyle-wincon" 69 | version = "3.0.2" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" 72 | dependencies = [ 73 | "anstyle", 74 | "windows-sys 0.52.0", 75 | ] 76 | 77 | [[package]] 78 | name = "backtrace" 79 | version = "0.3.69" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" 82 | dependencies = [ 83 | "addr2line", 84 | "cc", 85 | "cfg-if", 86 | "libc", 87 | "miniz_oxide", 88 | "object", 89 | "rustc-demangle", 90 | ] 91 | 92 | [[package]] 93 | name = "cc" 94 | version = "1.0.88" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "02f341c093d19155a6e41631ce5971aac4e9a868262212153124c15fa22d1cdc" 97 | 98 | [[package]] 99 | name = "cfg-if" 100 | version = "1.0.0" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 103 | 104 | [[package]] 105 | name = "clap" 106 | version = "4.5.1" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" 109 | dependencies = [ 110 | "clap_builder", 111 | "clap_derive", 112 | ] 113 | 114 | [[package]] 115 | name = "clap_builder" 116 | version = "4.5.1" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" 119 | dependencies = [ 120 | "anstream", 121 | "anstyle", 122 | "clap_lex", 123 | "strsim", 124 | ] 125 | 126 | [[package]] 127 | name = "clap_derive" 128 | version = "4.5.0" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" 131 | dependencies = [ 132 | "heck", 133 | "proc-macro2", 134 | "quote", 135 | "syn", 136 | ] 137 | 138 | [[package]] 139 | name = "clap_lex" 140 | version = "0.7.0" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" 143 | 144 | [[package]] 145 | name = "color-eyre" 146 | version = "0.6.2" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204" 149 | dependencies = [ 150 | "backtrace", 151 | "color-spantrace", 152 | "eyre", 153 | "indenter", 154 | "once_cell", 155 | "owo-colors", 156 | "tracing-error", 157 | ] 158 | 159 | [[package]] 160 | name = "color-spantrace" 161 | version = "0.2.1" 162 | source = "registry+https://github.com/rust-lang/crates.io-index" 163 | checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2" 164 | dependencies = [ 165 | "once_cell", 166 | "owo-colors", 167 | "tracing-core", 168 | "tracing-error", 169 | ] 170 | 171 | [[package]] 172 | name = "colorchoice" 173 | version = "1.0.0" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" 176 | 177 | [[package]] 178 | name = "eyre" 179 | version = "0.6.12" 180 | source = "registry+https://github.com/rust-lang/crates.io-index" 181 | checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" 182 | dependencies = [ 183 | "indenter", 184 | "once_cell", 185 | ] 186 | 187 | [[package]] 188 | name = "gimli" 189 | version = "0.28.1" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" 192 | 193 | [[package]] 194 | name = "heck" 195 | version = "0.4.1" 196 | source = "registry+https://github.com/rust-lang/crates.io-index" 197 | checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 198 | 199 | [[package]] 200 | name = "hermit-abi" 201 | version = "0.3.9" 202 | source = "registry+https://github.com/rust-lang/crates.io-index" 203 | checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" 204 | 205 | [[package]] 206 | name = "indenter" 207 | version = "0.3.3" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" 210 | 211 | [[package]] 212 | name = "june" 213 | version = "0.1.0" 214 | dependencies = [ 215 | "clap", 216 | "color-eyre", 217 | "libtest-mimic", 218 | "tracing", 219 | "tracing-subscriber", 220 | "tracing-tree", 221 | ] 222 | 223 | [[package]] 224 | name = "lazy_static" 225 | version = "1.4.0" 226 | source = "registry+https://github.com/rust-lang/crates.io-index" 227 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 228 | 229 | [[package]] 230 | name = "libc" 231 | version = "0.2.153" 232 | source = "registry+https://github.com/rust-lang/crates.io-index" 233 | checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" 234 | 235 | [[package]] 236 | name = "libtest-mimic" 237 | version = "0.6.1" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | checksum = "6d8de370f98a6cb8a4606618e53e802f93b094ddec0f96988eaec2c27e6e9ce7" 240 | dependencies = [ 241 | "clap", 242 | "termcolor", 243 | "threadpool", 244 | ] 245 | 246 | [[package]] 247 | name = "log" 248 | version = "0.4.21" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" 251 | 252 | [[package]] 253 | name = "matchers" 254 | version = "0.1.0" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" 257 | dependencies = [ 258 | "regex-automata 0.1.10", 259 | ] 260 | 261 | [[package]] 262 | name = "memchr" 263 | version = "2.7.1" 264 | source = "registry+https://github.com/rust-lang/crates.io-index" 265 | checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" 266 | 267 | [[package]] 268 | name = "miniz_oxide" 269 | version = "0.7.2" 270 | source = "registry+https://github.com/rust-lang/crates.io-index" 271 | checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" 272 | dependencies = [ 273 | "adler", 274 | ] 275 | 276 | [[package]] 277 | name = "nu-ansi-term" 278 | version = "0.46.0" 279 | source = "registry+https://github.com/rust-lang/crates.io-index" 280 | checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" 281 | dependencies = [ 282 | "overload", 283 | "winapi", 284 | ] 285 | 286 | [[package]] 287 | name = "nu-ansi-term" 288 | version = "0.49.0" 289 | source = "registry+https://github.com/rust-lang/crates.io-index" 290 | checksum = "c073d3c1930d0751774acf49e66653acecb416c3a54c6ec095a9b11caddb5a68" 291 | dependencies = [ 292 | "windows-sys 0.48.0", 293 | ] 294 | 295 | [[package]] 296 | name = "num_cpus" 297 | version = "1.16.0" 298 | source = "registry+https://github.com/rust-lang/crates.io-index" 299 | checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" 300 | dependencies = [ 301 | "hermit-abi", 302 | "libc", 303 | ] 304 | 305 | [[package]] 306 | name = "object" 307 | version = "0.32.2" 308 | source = "registry+https://github.com/rust-lang/crates.io-index" 309 | checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" 310 | dependencies = [ 311 | "memchr", 312 | ] 313 | 314 | [[package]] 315 | name = "once_cell" 316 | version = "1.19.0" 317 | source = "registry+https://github.com/rust-lang/crates.io-index" 318 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 319 | 320 | [[package]] 321 | name = "overload" 322 | version = "0.1.1" 323 | source = "registry+https://github.com/rust-lang/crates.io-index" 324 | checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" 325 | 326 | [[package]] 327 | name = "owo-colors" 328 | version = "3.5.0" 329 | source = "registry+https://github.com/rust-lang/crates.io-index" 330 | checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" 331 | 332 | [[package]] 333 | name = "pin-project-lite" 334 | version = "0.2.13" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" 337 | 338 | [[package]] 339 | name = "proc-macro2" 340 | version = "1.0.78" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" 343 | dependencies = [ 344 | "unicode-ident", 345 | ] 346 | 347 | [[package]] 348 | name = "quote" 349 | version = "1.0.35" 350 | source = "registry+https://github.com/rust-lang/crates.io-index" 351 | checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" 352 | dependencies = [ 353 | "proc-macro2", 354 | ] 355 | 356 | [[package]] 357 | name = "regex" 358 | version = "1.10.3" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" 361 | dependencies = [ 362 | "aho-corasick", 363 | "memchr", 364 | "regex-automata 0.4.5", 365 | "regex-syntax 0.8.2", 366 | ] 367 | 368 | [[package]] 369 | name = "regex-automata" 370 | version = "0.1.10" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" 373 | dependencies = [ 374 | "regex-syntax 0.6.29", 375 | ] 376 | 377 | [[package]] 378 | name = "regex-automata" 379 | version = "0.4.5" 380 | source = "registry+https://github.com/rust-lang/crates.io-index" 381 | checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" 382 | dependencies = [ 383 | "aho-corasick", 384 | "memchr", 385 | "regex-syntax 0.8.2", 386 | ] 387 | 388 | [[package]] 389 | name = "regex-syntax" 390 | version = "0.6.29" 391 | source = "registry+https://github.com/rust-lang/crates.io-index" 392 | checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" 393 | 394 | [[package]] 395 | name = "regex-syntax" 396 | version = "0.8.2" 397 | source = "registry+https://github.com/rust-lang/crates.io-index" 398 | checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" 399 | 400 | [[package]] 401 | name = "rustc-demangle" 402 | version = "0.1.23" 403 | source = "registry+https://github.com/rust-lang/crates.io-index" 404 | checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" 405 | 406 | [[package]] 407 | name = "sharded-slab" 408 | version = "0.1.7" 409 | source = "registry+https://github.com/rust-lang/crates.io-index" 410 | checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" 411 | dependencies = [ 412 | "lazy_static", 413 | ] 414 | 415 | [[package]] 416 | name = "smallvec" 417 | version = "1.13.1" 418 | source = "registry+https://github.com/rust-lang/crates.io-index" 419 | checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" 420 | 421 | [[package]] 422 | name = "strsim" 423 | version = "0.11.0" 424 | source = "registry+https://github.com/rust-lang/crates.io-index" 425 | checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" 426 | 427 | [[package]] 428 | name = "syn" 429 | version = "2.0.52" 430 | source = "registry+https://github.com/rust-lang/crates.io-index" 431 | checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" 432 | dependencies = [ 433 | "proc-macro2", 434 | "quote", 435 | "unicode-ident", 436 | ] 437 | 438 | [[package]] 439 | name = "termcolor" 440 | version = "1.4.1" 441 | source = "registry+https://github.com/rust-lang/crates.io-index" 442 | checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" 443 | dependencies = [ 444 | "winapi-util", 445 | ] 446 | 447 | [[package]] 448 | name = "thread_local" 449 | version = "1.1.8" 450 | source = "registry+https://github.com/rust-lang/crates.io-index" 451 | checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" 452 | dependencies = [ 453 | "cfg-if", 454 | "once_cell", 455 | ] 456 | 457 | [[package]] 458 | name = "threadpool" 459 | version = "1.8.1" 460 | source = "registry+https://github.com/rust-lang/crates.io-index" 461 | checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" 462 | dependencies = [ 463 | "num_cpus", 464 | ] 465 | 466 | [[package]] 467 | name = "tracing" 468 | version = "0.1.40" 469 | source = "registry+https://github.com/rust-lang/crates.io-index" 470 | checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" 471 | dependencies = [ 472 | "pin-project-lite", 473 | "tracing-attributes", 474 | "tracing-core", 475 | ] 476 | 477 | [[package]] 478 | name = "tracing-attributes" 479 | version = "0.1.27" 480 | source = "registry+https://github.com/rust-lang/crates.io-index" 481 | checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" 482 | dependencies = [ 483 | "proc-macro2", 484 | "quote", 485 | "syn", 486 | ] 487 | 488 | [[package]] 489 | name = "tracing-core" 490 | version = "0.1.32" 491 | source = "registry+https://github.com/rust-lang/crates.io-index" 492 | checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" 493 | dependencies = [ 494 | "once_cell", 495 | "valuable", 496 | ] 497 | 498 | [[package]] 499 | name = "tracing-error" 500 | version = "0.2.0" 501 | source = "registry+https://github.com/rust-lang/crates.io-index" 502 | checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" 503 | dependencies = [ 504 | "tracing", 505 | "tracing-subscriber", 506 | ] 507 | 508 | [[package]] 509 | name = "tracing-log" 510 | version = "0.2.0" 511 | source = "registry+https://github.com/rust-lang/crates.io-index" 512 | checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" 513 | dependencies = [ 514 | "log", 515 | "once_cell", 516 | "tracing-core", 517 | ] 518 | 519 | [[package]] 520 | name = "tracing-subscriber" 521 | version = "0.3.18" 522 | source = "registry+https://github.com/rust-lang/crates.io-index" 523 | checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" 524 | dependencies = [ 525 | "matchers", 526 | "nu-ansi-term 0.46.0", 527 | "once_cell", 528 | "regex", 529 | "sharded-slab", 530 | "smallvec", 531 | "thread_local", 532 | "tracing", 533 | "tracing-core", 534 | "tracing-log", 535 | ] 536 | 537 | [[package]] 538 | name = "tracing-tree" 539 | version = "0.3.0" 540 | source = "registry+https://github.com/rust-lang/crates.io-index" 541 | checksum = "65139ecd2c3f6484c3b99bc01c77afe21e95473630747c7aca525e78b0666675" 542 | dependencies = [ 543 | "nu-ansi-term 0.49.0", 544 | "tracing-core", 545 | "tracing-log", 546 | "tracing-subscriber", 547 | ] 548 | 549 | [[package]] 550 | name = "unicode-ident" 551 | version = "1.0.12" 552 | source = "registry+https://github.com/rust-lang/crates.io-index" 553 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 554 | 555 | [[package]] 556 | name = "utf8parse" 557 | version = "0.2.1" 558 | source = "registry+https://github.com/rust-lang/crates.io-index" 559 | checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" 560 | 561 | [[package]] 562 | name = "valuable" 563 | version = "0.1.0" 564 | source = "registry+https://github.com/rust-lang/crates.io-index" 565 | checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" 566 | 567 | [[package]] 568 | name = "winapi" 569 | version = "0.3.9" 570 | source = "registry+https://github.com/rust-lang/crates.io-index" 571 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 572 | dependencies = [ 573 | "winapi-i686-pc-windows-gnu", 574 | "winapi-x86_64-pc-windows-gnu", 575 | ] 576 | 577 | [[package]] 578 | name = "winapi-i686-pc-windows-gnu" 579 | version = "0.4.0" 580 | source = "registry+https://github.com/rust-lang/crates.io-index" 581 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 582 | 583 | [[package]] 584 | name = "winapi-util" 585 | version = "0.1.6" 586 | source = "registry+https://github.com/rust-lang/crates.io-index" 587 | checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" 588 | dependencies = [ 589 | "winapi", 590 | ] 591 | 592 | [[package]] 593 | name = "winapi-x86_64-pc-windows-gnu" 594 | version = "0.4.0" 595 | source = "registry+https://github.com/rust-lang/crates.io-index" 596 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 597 | 598 | [[package]] 599 | name = "windows-sys" 600 | version = "0.48.0" 601 | source = "registry+https://github.com/rust-lang/crates.io-index" 602 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 603 | dependencies = [ 604 | "windows-targets 0.48.5", 605 | ] 606 | 607 | [[package]] 608 | name = "windows-sys" 609 | version = "0.52.0" 610 | source = "registry+https://github.com/rust-lang/crates.io-index" 611 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 612 | dependencies = [ 613 | "windows-targets 0.52.4", 614 | ] 615 | 616 | [[package]] 617 | name = "windows-targets" 618 | version = "0.48.5" 619 | source = "registry+https://github.com/rust-lang/crates.io-index" 620 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 621 | dependencies = [ 622 | "windows_aarch64_gnullvm 0.48.5", 623 | "windows_aarch64_msvc 0.48.5", 624 | "windows_i686_gnu 0.48.5", 625 | "windows_i686_msvc 0.48.5", 626 | "windows_x86_64_gnu 0.48.5", 627 | "windows_x86_64_gnullvm 0.48.5", 628 | "windows_x86_64_msvc 0.48.5", 629 | ] 630 | 631 | [[package]] 632 | name = "windows-targets" 633 | version = "0.52.4" 634 | source = "registry+https://github.com/rust-lang/crates.io-index" 635 | checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" 636 | dependencies = [ 637 | "windows_aarch64_gnullvm 0.52.4", 638 | "windows_aarch64_msvc 0.52.4", 639 | "windows_i686_gnu 0.52.4", 640 | "windows_i686_msvc 0.52.4", 641 | "windows_x86_64_gnu 0.52.4", 642 | "windows_x86_64_gnullvm 0.52.4", 643 | "windows_x86_64_msvc 0.52.4", 644 | ] 645 | 646 | [[package]] 647 | name = "windows_aarch64_gnullvm" 648 | version = "0.48.5" 649 | source = "registry+https://github.com/rust-lang/crates.io-index" 650 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 651 | 652 | [[package]] 653 | name = "windows_aarch64_gnullvm" 654 | version = "0.52.4" 655 | source = "registry+https://github.com/rust-lang/crates.io-index" 656 | checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" 657 | 658 | [[package]] 659 | name = "windows_aarch64_msvc" 660 | version = "0.48.5" 661 | source = "registry+https://github.com/rust-lang/crates.io-index" 662 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 663 | 664 | [[package]] 665 | name = "windows_aarch64_msvc" 666 | version = "0.52.4" 667 | source = "registry+https://github.com/rust-lang/crates.io-index" 668 | checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" 669 | 670 | [[package]] 671 | name = "windows_i686_gnu" 672 | version = "0.48.5" 673 | source = "registry+https://github.com/rust-lang/crates.io-index" 674 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 675 | 676 | [[package]] 677 | name = "windows_i686_gnu" 678 | version = "0.52.4" 679 | source = "registry+https://github.com/rust-lang/crates.io-index" 680 | checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" 681 | 682 | [[package]] 683 | name = "windows_i686_msvc" 684 | version = "0.48.5" 685 | source = "registry+https://github.com/rust-lang/crates.io-index" 686 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 687 | 688 | [[package]] 689 | name = "windows_i686_msvc" 690 | version = "0.52.4" 691 | source = "registry+https://github.com/rust-lang/crates.io-index" 692 | checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" 693 | 694 | [[package]] 695 | name = "windows_x86_64_gnu" 696 | version = "0.48.5" 697 | source = "registry+https://github.com/rust-lang/crates.io-index" 698 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 699 | 700 | [[package]] 701 | name = "windows_x86_64_gnu" 702 | version = "0.52.4" 703 | source = "registry+https://github.com/rust-lang/crates.io-index" 704 | checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" 705 | 706 | [[package]] 707 | name = "windows_x86_64_gnullvm" 708 | version = "0.48.5" 709 | source = "registry+https://github.com/rust-lang/crates.io-index" 710 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 711 | 712 | [[package]] 713 | name = "windows_x86_64_gnullvm" 714 | version = "0.52.4" 715 | source = "registry+https://github.com/rust-lang/crates.io-index" 716 | checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" 717 | 718 | [[package]] 719 | name = "windows_x86_64_msvc" 720 | version = "0.48.5" 721 | source = "registry+https://github.com/rust-lang/crates.io-index" 722 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 723 | 724 | [[package]] 725 | name = "windows_x86_64_msvc" 726 | version = "0.52.4" 727 | source = "registry+https://github.com/rust-lang/crates.io-index" 728 | checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" 729 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "june" 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 | clap = { version = "4", features = ["derive"] } 10 | tracing = "0.1.40" 11 | tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } 12 | tracing-tree = "0.3.0" 13 | 14 | [dev-dependencies] 15 | color-eyre = "0.6.2" 16 | libtest-mimic = "0.6.1" 17 | 18 | [[test]] 19 | name = "integration_tests" 20 | path = "tests/integration_tests.rs" 21 | harness = false 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Sophia June Turner 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PLEASE NOTE 2 | 3 | June is no longer maintained. For more information, please read [my blog post](https://www.sophiajt.com/following-new-paths-ahead/) 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | # PREVIOUS README 13 | # June, a gradual, safe systems language 14 | 15 | ## Goals 16 | 17 | - Teachability | Learnability | Readability 18 | - Efficiency | Productivity 19 | - Systems and application programming 20 | 21 | Also see the [philosophy](docs/philosophy.md) for more info. 22 | 23 | ## Participating in the closed alpha/beta 24 | 25 | * How to file feedback 26 | * File feedback as issues on the repo: https://github.com/sophiajt/june/issues 27 | * When you file issues, please also submit the git hash of the revision you've built from 28 | 29 | ## Status 30 | 31 | The June project is still in its early days of development. While we have developed a compiler in tandem with designing the language, both should be considered experimental and pre-alpha in quality. Significant changes to both the language and the compiler should be expected. 32 | 33 | ## Running the test suite 34 | 35 | To run the June test suite, be sure to have the following installed: 36 | 37 | * A recent `rustc` 38 | * A recent `cargo` 39 | * A reasonably good `clang` in your path 40 | 41 | The June compiler outputs C code, which you can then build with a C-compatible compiler. The test suite assumes `clang` is the compiler that is available. (Note for Windows users: Visual Studio can install 'clang' as an optional install) 42 | 43 | ## How to compile 44 | 45 | June currently provides some rudimentary support for building june projects. We don't yet support creating new june projects. June only supports binary projects at the moment, with a main.june file inside a `main` folder. The project root is marked by a `June.toml` file which is currently empty and unused, but will eventually contain metadata about the project including the output binary name. Inside of a properly setup june project one should be able to build by simply typing `june build`. Once the build step completes you should have a binary in `build/debug` called`main` which you can run manually. 46 | 47 | ``` 48 | project_dir/ 49 | main/ 50 | main.june 51 | June.toml 52 | .gitignore 53 | build/ 54 | ``` 55 | 56 | Alternatively, the current version of June can be used compile June code to C by passing the filename directly such as with `june main.june`. After outputting C, you'll need to redirect this to a file, and then use a C compiler to build the June application. 57 | 58 | ## The June language 59 | 60 | For more information on the June language, check out [the June language documentation](docs/language.md) 61 | 62 | ## Reporting issues 63 | 64 | The best way to file issues is to file them through the [GitHub issue tracker](https://github.com/sophiajt/june/issues). 65 | 66 | ## Roadmap 67 | 68 | Our main goal with June is to build out an implementation of its initial design and to test it. 69 | 70 | As we dogfood, we will likely change the design of the language where we see it doesn't meet the goals of the project. 71 | 72 | We'll likely also work towards support in IDEs (using things like LSP) to make it easier to write larger June projects and get interactive feedback from the tools. 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /allocator/allocator.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | const long DEFAULT_PAGE_SIZE = 1024; 10 | const bool DEBUG_ZEROING = true; 11 | 12 | struct ResourceCleanup 13 | { 14 | void *resource; 15 | void (*cleanup_fun)(long, void *); 16 | 17 | struct ResourceCleanup *next; 18 | }; 19 | 20 | struct AllocatorPage 21 | { 22 | void *block; 23 | void *next_free; 24 | long amt_free; 25 | }; 26 | 27 | struct AllocatorLevel 28 | { 29 | int num_pages; 30 | struct AllocatorPage **pages; 31 | struct ResourceCleanup *cleanups; 32 | }; 33 | 34 | struct Allocator 35 | { 36 | int num_levels; 37 | struct AllocatorLevel **levels; 38 | }; 39 | 40 | void print_allocator_level(struct AllocatorLevel *allocator_level) 41 | { 42 | printf(" Resource cleanup\n"); 43 | 44 | struct ResourceCleanup *current = allocator_level->cleanups; 45 | while (current != NULL) 46 | { 47 | printf(" Cleanup:\n"); 48 | printf(" Pointer: %p\n", current->resource); 49 | printf(" Callback: %p\n", current->cleanup_fun); 50 | } 51 | 52 | printf(" Number of pages: %i\n", allocator_level->num_pages); 53 | 54 | for (int i = 0; i < allocator_level->num_pages; ++i) 55 | { 56 | printf(" Page: %i\n", i); 57 | printf(" Block: %p\n", allocator_level->pages[i]->block); 58 | printf(" Next free: %p\n", allocator_level->pages[i]->next_free); 59 | printf(" Remaining: %li\n", allocator_level->pages[i]->amt_free); 60 | } 61 | } 62 | 63 | void print_allocator(struct Allocator *allocator) 64 | { 65 | long index = 0; 66 | long page = 0; 67 | 68 | printf("--[Allocator]--\n"); 69 | 70 | for (int i = 0; i < allocator->num_levels; ++i) 71 | { 72 | if (allocator->levels[i] != NULL) 73 | { 74 | printf("Allocator level: %i\n", i); 75 | print_allocator_level(allocator->levels[i]); 76 | } 77 | } 78 | } 79 | 80 | struct Allocator *create_allocator(int num_default_levels) 81 | { 82 | struct Allocator *allocator = (struct Allocator *)malloc(sizeof(struct Allocator)); 83 | 84 | allocator->levels = (struct AllocatorLevel **)malloc(num_default_levels * sizeof(struct AllocatorLevel *)); 85 | memset(allocator->levels, 0, num_default_levels * sizeof(struct AllocatorLevel *)); 86 | allocator->num_levels = num_default_levels; 87 | 88 | return allocator; 89 | } 90 | 91 | void grow_allocator_levels(struct Allocator *allocator, int level) 92 | { 93 | // keep levels at a multiple of 10 as we grow to cut down on the number 94 | // of times we do this, plus one because of zero-base 95 | level += (10 - level % 10) + 1; 96 | 97 | allocator->levels = (struct AllocatorLevel **)realloc(allocator->levels, level * sizeof(struct AllocatorLevel *)); 98 | memset(allocator->levels + allocator->num_levels * sizeof(struct Allocator *), 0, (level - allocator->num_levels) * sizeof(struct AllocatorLevel *)); 99 | } 100 | 101 | void create_allocator_level(struct Allocator *allocator, int level) 102 | { 103 | if (allocator->num_levels <= level) 104 | { 105 | grow_allocator_levels(allocator, level); 106 | } 107 | 108 | // don't want to create a level where there already is one 109 | assert(allocator->levels[level] == NULL); 110 | 111 | allocator->levels[level] = (struct AllocatorLevel *)malloc(sizeof(struct AllocatorLevel)); 112 | allocator->levels[level]->num_pages = 0; 113 | allocator->levels[level]->pages = NULL; 114 | allocator->levels[level]->cleanups = NULL; 115 | } 116 | 117 | void add_resource_cleanup(struct Allocator *allocator, int level, void *resource, void (*cleanup_fun)(long, void *)) 118 | { 119 | if (allocator->num_levels <= level) 120 | { 121 | grow_allocator_levels(allocator, level); 122 | } 123 | 124 | if (allocator->levels[level] == NULL) 125 | { 126 | create_allocator_level(allocator, level); 127 | } 128 | 129 | if (allocator->levels[level]->cleanups == NULL) 130 | { 131 | allocator->levels[level]->cleanups = (struct ResourceCleanup *)malloc(sizeof(struct ResourceCleanup)); 132 | allocator->levels[level]->cleanups->cleanup_fun = cleanup_fun; 133 | allocator->levels[level]->cleanups->resource = resource; 134 | allocator->levels[level]->cleanups->next = NULL; 135 | } 136 | else 137 | { 138 | // struct ResourceCleanup *current = allocator->levels[level]->cleanups; 139 | 140 | // while (current->next != NULL) 141 | // { 142 | // current = current->next; 143 | // } 144 | 145 | // current->next = (struct ResourceCleanup *)malloc(sizeof(struct ResourceCleanup)); 146 | // current->next->cleanup_fun = cleanup_fun; 147 | // current->next->resource = resource; 148 | // current->next->next = NULL; 149 | 150 | struct ResourceCleanup *new_node = (struct ResourceCleanup *)malloc(sizeof(struct ResourceCleanup)); 151 | new_node->cleanup_fun = cleanup_fun; 152 | new_node->resource = resource; 153 | new_node->next = allocator->levels[level]->cleanups; 154 | allocator->levels[level]->cleanups = new_node; 155 | } 156 | } 157 | 158 | struct AllocatorPage *create_allocator_page() 159 | { 160 | struct AllocatorPage *output = (struct AllocatorPage *)malloc(sizeof(struct AllocatorPage)); 161 | 162 | output->block = NULL; 163 | output->next_free = NULL; 164 | output->amt_free = 0; 165 | 166 | return output; 167 | } 168 | 169 | long calculate_default_size(long size) 170 | { 171 | if (size < DEFAULT_PAGE_SIZE) 172 | { 173 | return DEFAULT_PAGE_SIZE; 174 | } 175 | else if (size > (4 * 1024 * 1024)) 176 | { 177 | // large allocation, just fit the allocation instead of having extra 178 | return size; 179 | } 180 | else 181 | { 182 | // a note on alignment: in theory, since we're using structs/unions 183 | // I *hope* the C requirements will keep us memory aligned even 184 | // though we're doing our own allocations by requesting aligned sizes. 185 | return size + 1024; 186 | } 187 | } 188 | 189 | void *allocate_on_allocator_level(struct AllocatorLevel *allocator_level, long size) 190 | { 191 | if (allocator_level->num_pages == 0) 192 | { 193 | allocator_level->pages = (struct AllocatorPage **)malloc(sizeof(struct AllocatorPage *)); 194 | allocator_level->pages[0] = create_allocator_page(); 195 | 196 | allocator_level->num_pages = 1; 197 | } 198 | 199 | for (int i = 0; i < allocator_level->num_pages; ++i) 200 | { 201 | if (allocator_level->pages[i]->block == NULL) 202 | { 203 | // We have a page that hasn't been used yet 204 | long default_size = calculate_default_size(size); 205 | allocator_level->pages[i]->block = malloc(default_size); 206 | void *output = allocator_level->pages[i]->block; 207 | allocator_level->pages[i]->next_free = allocator_level->pages[i]->block + size; 208 | allocator_level->pages[i]->amt_free = default_size - size; 209 | 210 | return output; 211 | } 212 | else if (allocator_level->pages[i]->amt_free >= size) 213 | { 214 | void *output = allocator_level->pages[i]->next_free; 215 | allocator_level->pages[i]->next_free += size; 216 | allocator_level->pages[i]->amt_free -= size; 217 | 218 | return output; 219 | } 220 | } 221 | 222 | // We can't find any pages that can hold what we have, so let's make a new one 223 | allocator_level->pages = realloc(allocator_level->pages, sizeof(struct AllocatorPage *) * (allocator_level->num_pages + 1)); 224 | allocator_level->pages[allocator_level->num_pages] = create_allocator_page(); 225 | 226 | long default_size = calculate_default_size(size); 227 | allocator_level->pages[allocator_level->num_pages]->block = malloc(default_size); 228 | void *output = allocator_level->pages[allocator_level->num_pages]->block; 229 | allocator_level->pages[allocator_level->num_pages]->next_free = allocator_level->pages[allocator_level->num_pages]->block + size; 230 | allocator_level->pages[allocator_level->num_pages]->amt_free = default_size - size; 231 | 232 | ++allocator_level->num_pages; 233 | 234 | return output; 235 | } 236 | 237 | void *allocate_resizeable_page_on_allocator_level(struct Allocator *allocator, int level, long size) 238 | { 239 | if (level >= allocator->num_levels) 240 | { 241 | grow_allocator_levels(allocator, level); 242 | } 243 | 244 | if (allocator->levels[level] == NULL) 245 | { 246 | create_allocator_level(allocator, level); 247 | } 248 | 249 | struct AllocatorLevel *allocator_level = allocator->levels[level]; 250 | // Each resizeable page is isolated, so let's make a new one 251 | allocator_level->pages = realloc(allocator_level->pages, sizeof(struct AllocatorPage *) * (allocator_level->num_pages + 1)); 252 | allocator_level->pages[allocator_level->num_pages] = create_allocator_page(); 253 | 254 | allocator_level->pages[allocator_level->num_pages]->block = malloc(size); 255 | void *output = allocator_level->pages[allocator_level->num_pages]->block; 256 | allocator_level->pages[allocator_level->num_pages]->next_free = allocator_level->pages[allocator_level->num_pages]->block + size; 257 | allocator_level->pages[allocator_level->num_pages]->amt_free = 0; 258 | 259 | ++allocator_level->num_pages; 260 | 261 | return output; 262 | } 263 | 264 | void *resize_page_on_allocator_level(struct Allocator *allocator, int level, void *origin, long new_size) 265 | { 266 | struct AllocatorLevel *allocator_level = allocator->levels[level]; 267 | 268 | for (int i = 0; i < allocator_level->num_pages; ++i) 269 | { 270 | if (allocator_level->pages[i]->block == origin) 271 | { 272 | allocator_level->pages[i]->block = realloc(allocator_level->pages[i]->block, new_size); 273 | allocator_level->pages[i]->next_free = allocator_level->pages[i]->block + new_size; 274 | 275 | return allocator_level->pages[i]->block; 276 | } 277 | } 278 | 279 | printf("INTERNAL ERROR: could not find origin: %p\n", origin); 280 | return NULL; 281 | } 282 | 283 | void free_allocator_level(struct Allocator *allocator, int level) 284 | { 285 | if (allocator->levels[level] == NULL) 286 | { 287 | return; 288 | } 289 | 290 | struct AllocatorLevel *allocator_level = allocator->levels[level]; 291 | 292 | // 1) Start with the resource cleanup 293 | 294 | while (allocator_level->cleanups != NULL) 295 | { 296 | allocator_level->cleanups->cleanup_fun(/*unused*/ 0, allocator_level->cleanups->resource); 297 | struct ResourceCleanup *old = allocator_level->cleanups; 298 | allocator_level->cleanups = allocator_level->cleanups->next; 299 | 300 | free(old); 301 | } 302 | 303 | // 2) Then, do memory cleanup 304 | 305 | if (allocator_level->num_pages == 0) 306 | { 307 | return; 308 | } 309 | 310 | // Only free the first page if it's too big 311 | if ((allocator_level->pages[0]->next_free - allocator_level->pages[0]->block + allocator_level->pages[0]->amt_free) > DEFAULT_PAGE_SIZE) 312 | { 313 | if (DEBUG_ZEROING) 314 | { 315 | memset(allocator_level->pages[0]->block, 0, allocator_level->pages[0]->next_free - allocator_level->pages[0]->block); 316 | allocator_level->pages[0]->amt_free += allocator_level->pages[0]->next_free - allocator_level->pages[0]->block; 317 | allocator_level->pages[0]->next_free = allocator_level->pages[0]->block; 318 | } 319 | else 320 | { 321 | free(allocator_level->pages[0]->block); 322 | allocator_level->pages[0]->block = NULL; 323 | allocator_level->pages[0]->next_free = NULL; 324 | allocator_level->pages[0]->amt_free = 0; 325 | } 326 | } 327 | else 328 | { 329 | // Reuse the previous block if it's small enough 330 | if (DEBUG_ZEROING) 331 | { 332 | memset(allocator_level->pages[0]->block, 0, allocator_level->pages[0]->next_free - allocator_level->pages[0]->block); 333 | } 334 | allocator_level->pages[0]->amt_free += (allocator_level->pages[0]->next_free - allocator_level->pages[0]->block); 335 | allocator_level->pages[0]->next_free = allocator_level->pages[0]->block; 336 | } 337 | 338 | for (int i = 1; i < allocator_level->num_pages; ++i) 339 | { 340 | if (DEBUG_ZEROING) 341 | { 342 | memset(allocator_level->pages[i]->block, 0, allocator_level->pages[i]->next_free - allocator_level->pages[i]->block); 343 | allocator_level->pages[i]->amt_free += allocator_level->pages[i]->next_free - allocator_level->pages[i]->block; 344 | allocator_level->pages[i]->next_free = allocator_level->pages[i]->block; 345 | } 346 | else 347 | { 348 | free(allocator_level->pages[i]->block); 349 | allocator_level->pages[i]->block = NULL; 350 | allocator_level->pages[i]->next_free = NULL; 351 | allocator_level->pages[i]->amt_free = 0; 352 | } 353 | } 354 | } 355 | 356 | void *allocate(struct Allocator *allocator, long size, int level) 357 | { 358 | if (level >= allocator->num_levels) 359 | { 360 | grow_allocator_levels(allocator, level); 361 | } 362 | 363 | if (allocator->levels[level] == NULL) 364 | { 365 | create_allocator_level(allocator, level); 366 | } 367 | 368 | return allocate_on_allocator_level(allocator->levels[level], size); 369 | } 370 | -------------------------------------------------------------------------------- /comparison/circular_linked_list_abstraction.rs: -------------------------------------------------------------------------------- 1 | struct Node { 2 | data: i64, 3 | next: Option<*mut Node>, 4 | } 5 | 6 | struct CircularList { 7 | node: Option<*mut Node>, 8 | } 9 | 10 | impl Drop for CircularList { 11 | fn drop(&mut self) { 12 | use std::alloc::{dealloc, Layout}; 13 | use std::ptr; 14 | 15 | match self.node { 16 | Some(ptr) => { 17 | let start = ptr; 18 | let mut current = ptr; 19 | 20 | loop { 21 | let next = unsafe { (*current).next.expect("internal error") }; 22 | 23 | unsafe { 24 | ptr::drop_in_place(current); 25 | dealloc(current as *mut u8, Layout::new::()); 26 | } 27 | 28 | if next == start { 29 | break; 30 | } else { 31 | current = next; 32 | } 33 | } 34 | } 35 | None => {} 36 | } 37 | } 38 | } 39 | 40 | impl CircularList { 41 | pub fn default() -> Self { 42 | Self { node: None } 43 | } 44 | 45 | pub fn push(&mut self, data: i64) { 46 | match self.node { 47 | Some(ptr) => unsafe { 48 | let next = (*ptr).next; 49 | (*ptr).next = Some(Box::into_raw(Box::new(Node { data, next }))); 50 | }, 51 | None => { 52 | self.node = Some(Box::into_raw(Box::new(Node { data, next: None }))); 53 | unsafe { (*self.node.expect("internal error")).next = self.node }; 54 | } 55 | } 56 | } 57 | 58 | pub fn next(&mut self) -> i64 { 59 | match self.node { 60 | Some(ptr) => { 61 | let ret = unsafe { (*ptr).data }; 62 | 63 | self.node = Some(unsafe { (*ptr).next.expect("internal error") }); 64 | 65 | ret 66 | } 67 | None => 0, 68 | } 69 | } 70 | } 71 | 72 | fn helper() -> CircularList { 73 | let list = CircularList::default(); 74 | 75 | list 76 | } 77 | 78 | fn main() { 79 | let mut list = helper(); 80 | 81 | list.push(1); 82 | list.push(2); 83 | list.push(3); 84 | 85 | println!("{}", list.next()); 86 | println!("{}", list.next()); 87 | println!("{}", list.next()); 88 | println!("{}", list.next()); 89 | println!("{}", list.next()); 90 | println!("{}", list.next()); 91 | } 92 | -------------------------------------------------------------------------------- /comparison/enum_catch_all.rs: -------------------------------------------------------------------------------- 1 | enum State { 2 | None, 3 | Some(i64), 4 | Struct { x: i64, y: i64 }, 5 | } 6 | 7 | fn main() { 8 | let x = State::Struct { x: 66, y: 77 }; 9 | 10 | match x { 11 | State::None => println!("None"), 12 | _ => println!("Some"), 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /comparison/lifetime_alloc_infer_success.rs: -------------------------------------------------------------------------------- 1 | struct Stats { 2 | age: i64, 3 | } 4 | 5 | struct Employee<'a> { 6 | name: String, 7 | stats: &'a Stats, 8 | } 9 | 10 | fn helper<'a, 'b>(employee: &'a mut Employee<'b>, stats: &'b Stats) { 11 | employee.stats = stats; 12 | } 13 | 14 | fn main() { 15 | // Rust doesn't allow this to be created by `helper`, so we have 16 | // to create it from outside and pass it in 17 | let stats2 = Stats { age: 33 }; 18 | 19 | let stats = Stats { age: 22 }; 20 | 21 | let mut employee = Employee { 22 | name: "Sophia".into(), 23 | stats: &stats, 24 | }; 25 | 26 | helper(&mut employee, &stats2); 27 | 28 | println!("{}", employee.stats.age); 29 | } 30 | -------------------------------------------------------------------------------- /comparison/struct.rs: -------------------------------------------------------------------------------- 1 | struct Employee { 2 | name: String, 3 | age: i64, 4 | } 5 | 6 | fn main() { 7 | let employee = Employee { 8 | name: "Bob".into(), 9 | age: 123, 10 | }; 11 | println!("{}", employee.age) 12 | } 13 | -------------------------------------------------------------------------------- /docs/architecture.md: -------------------------------------------------------------------------------- 1 | # June Compiler Architecture 2 | 3 | - Parser 4 | - Lexer 5 | - type checker 6 | - name binding 7 | - lifetime checker 8 | - codegen 9 | 10 | The June compiler data model is based on the data oriented school of design 11 | (similar to an ecs, which will be used as an analogy throughout this document). 12 | The compiler parses the program and creates a flattened AST of nodes and 13 | `NodeId`s that index into the vec of nodes. You can think NodeIds as equivalent 14 | to entity identifiers in an ECS and the nodes themselves as a component of that 15 | entity. Each pass of the compiler then creates additional vectors of 16 | information associated with those same `NodeId`s, essentially setting up 17 | additional components for each entity. 18 | 19 | ## Parser 20 | 21 | The June parser is based on a classic recursive decent parser, using operator 22 | precedence parsing inspired by the [Shunting yard 23 | algorithm](https://en.wikipedia.org/wiki/Shunting_yard_algorithm). The parser 24 | uses the lexer's `peek` and `next` methods to perform the recursive decent 25 | parsing. At each step it will peek the next token to see if it makes sense in 26 | the current parsing context. If it does, it will consume it and continue, if 27 | not it will give up on that parsing context and attempt to reinterpret that 28 | token based on the next step in the algorithm. The June lexer never looks 29 | further ahead than the next token. 30 | 31 | ## Type Checker 32 | 33 | The type checker walks over the AST generated by the parser starting from the 34 | outermost node, to create a new vec of type information that it associates with 35 | the nodeids of their related AST Nodes. It walks the tree in a depth first 36 | fashion (with exceptions), drilling down into the AST until it can concretely 37 | determine the types of expressions purely based on their AST node kind, then it 38 | bubbles up this type information to then determine the types of the more 39 | complex compound expressions. 40 | 41 | The type checker checks each scope in two passes. First it finds all function 42 | signatures and stores those in a `Scope` object (via `typecheck_fun_predecl`). 43 | The active `Scope`s for the current position being type checked are stored in a 44 | vector as a stack and act as a lookup table for information needed to type 45 | check that scope. Then, the type checker checks the function bodies, using 46 | those `Scope` lookup tables to resolve calls within the body. `Scopes` are 47 | discarded once type checking of the scope is completed, at which point the type 48 | checker then continues checking the outer `Scope`. 49 | 50 | ## Lifetime Checker 51 | 52 | The goal of the lifetime checker is to determine the lifetime of each 53 | allocation. We associate lifetime information with all expressions that can 54 | influence the lifetime of an allocation, such as variable bindings, 55 | assignments, function calls, or returns. A nodes lifetime can be one of three 56 | things: 57 | 58 | * Local - this value does not escape this function 59 | * Parameter - when mutable can be used to store values and have them escape functions. 60 | * Return Lifetimes - values returned by functions escape 61 | 62 | 63 | ``` 64 | // param lifetime example: 65 | struct Foo { a: A } 66 | fun bar(mut foo: Foo) { 67 | foo.a = new A() 68 | } 69 | 70 | // return lifetime example #1 71 | fun a() -> A { 72 | return new A() 73 | } 74 | 75 | // return lifetime example #2 76 | fun b() -> A { 77 | return a() 78 | } 79 | 80 | // return lifetime example #3 81 | fun c() -> Foo { 82 | mut foo = new Foo() 83 | foo.a = new A() 84 | 85 | return foo 86 | } 87 | ``` 88 | 89 | ``` 90 | fun id(a: A) -> A { 91 | return a 92 | } 93 | 94 | ``` 95 | 96 | ``` 97 | A() <-- return to here 98 | B() 99 | C() <-- allocate here 100 | D() 101 | E() 102 | ``` 103 | 104 | We also use fixed point inference to infer lifetimes where we keep 105 | repeating the lifetime inference until they stop changing, which lets us 106 | determine that the `new A()` value in example three above has a `Return` 107 | lifetime. 108 | 109 | ## Codegen 110 | -------------------------------------------------------------------------------- /docs/language.md: -------------------------------------------------------------------------------- 1 | # Language Basics 2 | 3 | ## Data types 4 | 5 | ### Basic data types 6 | 7 | * `i64`: `123`, `0` 8 | * `f64`: `3.5`, `1.0` 9 | * `bool`: `true`, `false` 10 | * `void` 11 | 12 | ### C interop 13 | * `c_string`: `c"hello"` 14 | * `c_voidptr` 15 | * `c_char`: `c'b'` 16 | * `c_int` 17 | 18 | ### Enums 19 | 20 | ``` 21 | enum State { 22 | None 23 | Some(i64) 24 | Struct { x: i64, y: i64 } 25 | } 26 | 27 | let x = State::Struct(x: 66, y: 77) 28 | 29 | match x { 30 | State::None => println(c"None") 31 | State::Some(x) => println(x) 32 | State::Struct(x, y) => println(y) 33 | } 34 | ``` 35 | 36 | ### Structs 37 | 38 | ``` 39 | struct Box { 40 | width: i64 41 | 42 | fun grow_width(mut self, amount: i64) { 43 | .width += amount 44 | } 45 | } 46 | ``` 47 | 48 | Methods live inside of struct definitions. You can also use a shorthand to refer to the instance variables. Rather than writing `self.width`, you can write `.width`. 49 | 50 | ### Classes 51 | 52 | You can use the `class` keyword interchangebly with `struct`. The only difference between the two is that classes make members and functions private by default. Note: This part of the design is currently in flux. 53 | 54 | ### Functions 55 | 56 | ``` 57 | fun foo(x: i64) -> i64 { 58 | return x 59 | } 60 | 61 | let bar = foo 62 | 63 | println(bar(3)) 64 | ``` 65 | 66 | ### Raw buffers 67 | 68 | Raw buffers represent a simple array with unchecked access. Because the access in unchecked, you need to index using an unsafe block. 69 | 70 | ``` 71 | mut buffer = raw[1, 2, 3] 72 | unsafe { 73 | buffer[1] = 5 74 | println(buffer[1]) 75 | } 76 | ``` 77 | 78 | You can also resize raw buffers (syntax definitely not final) 79 | 80 | ``` 81 | mut x = raw[1] 82 | 83 | unsafe { 84 | resize x 2000 85 | x[1999] = 100 86 | println(x[1999]) 87 | } 88 | ``` 89 | 90 | ## Variables 91 | 92 | ### Immutable-by-default 93 | 94 | Variables, when created, are immutable by default. This binding controls the mutability of what is bound. 95 | 96 | For example: 97 | 98 | ``` 99 | let x = 3 100 | ``` 101 | 102 | Creates the immutable variable `x`. Once created, its value can not be changed. 103 | 104 | Likewise: 105 | 106 | ``` 107 | let foo = new Foo() 108 | ``` 109 | 110 | Creates an immutable `foo`. Reaching through `foo` gives you an immutable value, so it can not be used to updated the `Foo` object. 111 | 112 | ### Mutable bindings 113 | 114 | You can create a mutable binding variable with `mut`, like so: 115 | 116 | ``` 117 | mut x = 3 118 | x = 5 119 | ``` 120 | 121 | ## Control flow 122 | 123 | ### if..else 124 | 125 | ``` 126 | if true { 127 | println("hello") 128 | } else { 129 | println("world") 130 | } 131 | ``` 132 | 133 | ### for..in 134 | 135 | ``` 136 | mut total = 0 137 | for i in 1..10 { 138 | total += i 139 | } 140 | 141 | println(total) 142 | ``` 143 | 144 | ### while 145 | 146 | ``` 147 | mut x = 1 148 | while x < 10 { 149 | x = x + 1 150 | } 151 | 152 | println(x) 153 | ``` 154 | 155 | ### return 156 | 157 | ``` 158 | fun foo() -> i64 { 159 | return 3 160 | } 161 | ``` 162 | 163 | ### break 164 | 165 | ``` 166 | while true { 167 | break 168 | } 169 | ``` 170 | 171 | ## Defer :clock11: 172 | 173 | You can also defer work to be done when a pointer's group is being freed. To do this, use the `defer` keyword and pass in both the pointer to infer the group from and the function to run on the pointer right before the group is freed. 174 | 175 | ``` 176 | struct Person { 177 | name: c_string 178 | } 179 | 180 | fun greet(p: Person) { 181 | println(p.name) 182 | } 183 | 184 | fun main() { 185 | let person = new Person(name: c"Felicia"); 186 | defer person greet 187 | } 188 | ``` 189 | 190 | ## Generics 191 | 192 | ### Generic enum 193 | 194 | ``` 195 | enum Option { 196 | None 197 | Some(T) 198 | } 199 | ``` 200 | 201 | ### Generic struct 202 | 203 | ``` 204 | struct Foo { 205 | x: T 206 | } 207 | ``` 208 | 209 | ### Generic functions 210 | 211 | ``` 212 | fun id(x: T) [x == return] -> T { 213 | return x 214 | } 215 | ``` 216 | 217 | ## Lifetime annotations 218 | 219 | To describe the relationship between parameters and how they may be used, specifically how they may escape into other parameters or be used as a return value, we need to tell the compiler that this is allowed. To do so, we use a 'lifetime annotation' on the function. 220 | 221 | Example lifetime annotations saying that two parameters have the same lifetime: 222 | ``` 223 | fun assign(mut person: Person, stats: Stats) [stats == person] { 224 | person.stats = stats 225 | } 226 | ``` 227 | 228 | Example lifetime annotation saying the parameter can be used as a return value: 229 | ``` 230 | fun identity(x: Stats) [x == return] -> Stats { 231 | return x 232 | } 233 | ``` 234 | 235 | ## Modules :pie: 236 | 237 | Note: The module system is currently under active development and does not yet match the ultimately planned behavior. 238 | 239 | #### Current State 240 | 241 | Simple multi-file project support example: 242 | 243 | main.june 244 | ``` 245 | use mod1; 246 | 247 | mod1::foo() 248 | ``` 249 | 250 | mod1.june (in the future, this will likely require the `export` keyword) 251 | ``` 252 | fn foo() {} 253 | ``` 254 | 255 | file structure: 256 | ``` 257 | src/ 258 | main.june mod1.june 259 | ``` 260 | 261 | ### Future design 262 | Public APIs of modules are defined using the `export` keyword. The module tree structure of a june application or library is defined by its file structure. Modules can be either files in the same directory as their parent module, or they can be subdirectories, with an optional file matching the subdirectory name defining the submodule, with additional files being submodules of the submodule. 263 | 264 | 265 | ``` 266 | main/ 267 | main.june 268 | mod1.june 269 | mod2.june 270 | ``` 271 | 272 | ``` 273 | lib/ 274 | lib.june 275 | mod1/ 276 | mod1.june sub_mod1.june 277 | mod2.june 278 | ``` 279 | 280 | 281 | 282 | ## Top-level code 283 | 284 | June source files may begin execution in an explicit `main` function. You may also use top-level code, instead. 285 | 286 | For example, to print hello world, you can create a "hello.june": 287 | 288 | ``` 289 | println(c"hello world") 290 | ``` 291 | 292 | # Memory Safety 293 | 294 | ## Lifetime checker :mag_right: 295 | 296 | Lifetimes are a fundamental concept of June. :muscle: 297 | 298 | In June, lifetimes are associated with groups of related allocations. For example, a linked list begins with a pointer pointing to the start of the list, and then follows with nodes pointing to each successive node. All nodes in this linked list have the same lifetime, denoted by the lifetime of the head pointer. 299 | 300 | As groups of related allocations share a lifetime, the compiler will infer how long this group needs to live, and once the group has finished it will be automatically deleted and the memory will be reclaimed. 301 | 302 | The lifetime checker also infers the lifetime of allocations in your code. This is a modular inference. Inference will infer the lifetime of an allocation to be in one of three groups: 303 | 304 | * **Local**: this allocation does not escape this function 305 | * **Param(name)**: This allocation escapes via the parameter called 'name' 306 | * **Return**: This allocation escapes as the return of the function 307 | 308 | ## Constructing and inferring lifetimes 309 | 310 | Constructing a new value using `new` will construct and infer the lifetime of the value being constructed. 311 | 312 | ``` 313 | let x = new Foo() 314 | ``` 315 | 316 | ## Enforcing lifetimes 317 | 318 | You can use the keyword `local` instead of `new` to enforce that the given allocation is only ever used locally and does not escape the current function. 319 | 320 | For example: 321 | 322 | ``` 323 | let x = local Foo() 324 | ``` 325 | 326 | ## Lifetime groups 327 | 328 | Because we track the lifetime of groups of related allocations, we're also able to use a custom allocator to allocate these related items beside each other in memory, allowing for better cache locality. 329 | 330 | ## Owned pointers 331 | 332 | Owned pointers can be created using the `owned` keyword. This will check that the abstraction being instantiated is fully-encapsulated. If it is, the compiler will create a single-owner pointer. 333 | 334 | ``` 335 | let x = new owned Foo() 336 | ``` 337 | 338 | ## ABI 339 | 340 | June uses a C ABI for laying out both struct and enum. 341 | 342 | ## Safe memory recycling (not yet implemented :construction:) 343 | 344 | If an allocation is no longer needed but the group of allocations is still alive, you can opt to track the number of live aliases of a pointer and recycle that pointer if it is the sole alias of that memory. 345 | 346 | ## C interop 347 | 348 | There are a few features in June to aid with interop with C. 349 | 350 | Here's an example of these features: 351 | 352 | ``` 353 | extern type FILE; 354 | 355 | extern "C" fun fopen(filename: c_string, mode: c_string) -> FILE? 356 | extern "C" fun fclose(file: FILE?) -> c_int; 357 | extern "C" fun fgetc(file: FILE?) -> c_int; 358 | 359 | unsafe { 360 | let file = fopen(c"tests/test_data/alphabet.txt", c"rb"); 361 | if file != none { 362 | let x = fgetc(file) 363 | println(x as c_char) 364 | } 365 | fclose(file) 366 | } 367 | 368 | ``` 369 | -------------------------------------------------------------------------------- /docs/philosophy.md: -------------------------------------------------------------------------------- 1 | # Goals 2 | 3 | ## Teachability, learnability, and readability 4 | 5 | June was born of multiple years of experience teaching Rust. Rust, while being a powerful systems language, is also incredibly difficult to learn and a special challenge as a teacher to deliver complex concepts to classrooms of diverse technical backgrounds. In essence, June comes from the idea: "what if we could do safe systems programming in a simpler way?" 6 | 7 | From this vision came a language that used a different granularity for lifetimes, which allowed for easier inference and an easier lifetime mental model: "related things are allocated together". 8 | 9 | The language itself draws from a variety of inspirations in addition to Rust: Go, Swift, embedded C, and others. The syntax has the goal of not requiring a lot of decorations or uncommon annotations. Instead, the syntax is built to be light and readable regardless of the languages the programmer is familiar with. 10 | 11 | June is also built to be a teaching language that could be used to teach software engineering concepts. It is suitable to be used as someone's first programming language. 12 | 13 | ## Efficiency 14 | 15 | One major benefit of systems programming, in addition to the flexibility of being usable on bare-metal, is the efficiency of the compiled code. June leverages these benefits in its design, and includes some uncommon language design choices that leverage decades of embedded and high-performance work. 16 | 17 | Namely, June uses the idea of a custom allocator as one of its core implementation concepts. June developers will be invoking the default (and in the future custom) allocation strategies that allow groups of related allocations to efficiently sit beside each other in memory, even if those allocations are interleaved with allocations meant for other groups. 18 | 19 | The end goal is that related allocations maintain cache locality as much as possible. This gives the added benefit that programmers need not restrict themselves to vectors of structs to maximise efficiency via cache locality. Instead, they can build data structures in a way that feels more natural and readable, while still benefitting from the underlying locality of the allocations used to build those data structures. 20 | 21 | ## Systems and application programming 22 | 23 | June is a systems language, meaning it is intended to be able to be used to create low-level applications that can work directly on the metal. Use of the allocator will be tune-able for these systems, and is already a natural fit for systems that do not have access to OS-level memory management. 24 | 25 | June is also geared towards application programming. Its direct OO approach coupled with its ease-of-use encourages application development using tradition OOP UI approaches as well as more innovative data-oriented styles. In essence, June allows the developer to pick the implementation that works best for the problem, and then encourages strong encapsulation for code reuse. 26 | 27 | # Non-goals 28 | 29 | ## Fine-grained memory reclamation 30 | 31 | One characteristic of June that you'll notice is that it prefers holding on to memory longer than other languages. You can think of this as a kind of memory "bloat" compared to more fine-grained allocation system. In one sense, the style is something more akin to garbage collection, though June does not use a garbage collector. 32 | 33 | As it turns out, using intentional memory bloat is fairly common. Rust and C++ developers, for example, use bloat to their advantage. Take for example this Rust code: 34 | 35 | ```rust 36 | fn main() { 37 | let mut v = vec![]; 38 | 39 | println!("{}", v.capacity()); 40 | 41 | for _ in 1..10000 { 42 | v.push(5); 43 | } 44 | 45 | println!("{}", v.capacity()); 46 | 47 | for _ in 1..10000 { 48 | v.pop(); 49 | } 50 | 51 | println!("{}", v.capacity()); 52 | } 53 | ``` 54 | 55 | Notice how capacity does not decrease, even as you empty the vector of its contents. The same is true in C++. This is an intentional design choice to allow you to begin pushing values again and already have ample space. 56 | 57 | In a way, vectors in C++ and Rust assume a "high-water mark" style of allocation, where they don't aggressively deallocate as the container shrinks its content size. Instead, they keep the highest allocation size as the working set. 58 | 59 | In June, we use this concept for groups of related allocations. Much in the same way that a vector maintains a high-water allocation size, June encourages memory reuse rather than deletion and reallocation. 60 | 61 | The goal of this is two-fold: it is more efficient to not free and reallocate memory from the system allocator, and it allows us to track the lifetime of related allocations together as a single entity. The former helps improve speed while the latter helps make the lifetime checking of a June program significantly simpler than borrow checking systems with fine-grained lifetimes. 62 | 63 | sjt: do we want to call this "bloat" or give it a nicer name? 64 | 65 | ## RAII 66 | 67 | RAII, or Resource Acquisition Is Initialisation, is a common pattern with C++ and Rust developers. 68 | 69 | In June, however, it is not the goal of the language to support this pattern. There are a few reasons for this. 70 | 71 | RAII assumes a focus on a single object's lifetime, and an immediate trigger of an event (often the destructor) when the object's lifetime has ended. This might be used to free memory or additional resources. 72 | 73 | June's memory system instead tracks groups of related lifetimes together as one. This muddies the RAII concept, as there is no implied ordering of allocations among a group's allocations. Without this order, it is unclear as to which order destructors should run. 74 | 75 | Equally important is June's ability to drop _all_ memory allocated to a group as a single drop. This is much more efficient then having to order destructors and waiting for them to run in sequence. 76 | 77 | June does offer the `defer` command to defer the freeing of a resource, which can be attached to the lifetime of a group via one of its pointers. In effect, this can approximate some of the cleanup logic of RAII without enforcing the whole of the RAII pattern. 78 | 79 | # Grouping memory 80 | 81 | June is designed to consider allocations as grouped and related. For example, allocations that make up a linked list are seen a part of the same allocation group. This is in contrast to languages like Rust and C++ that, by default, think of allocations as separate and unrelated. 82 | 83 | This group of allocations has some helpful properties: 84 | 85 | * Grouped allocations make for allocations that are easier to use as they can be used to created arbitrary data structures. 86 | * Grouped allocations simplify lifetimes down, so that a pointer can carry a single lifetime for itself and all data it points to. This simplifies by lifetime inference and lifetime annotation. 87 | * Grouped allocations also give lifetime names that are easier to read and understand. If, for example, the group comes from the parameter called `foo`, we can tell the user that the lifetime is `param(foo)` making it clear where the lifetime requirement comes from. 88 | 89 | Grouped allocations do also have some drawbacks: 90 | 91 | * Grouped allocations mean allocations live and die together. If you need to reuse memory before the group has completely, you'll need to use a memory recycling scheme. 92 | * It will sometimes be the case that you may unintentionally grow the amount of memory used by a group if you're more accustomed to garbage collected languages. This will need to be addressed via static analysis and addition profiling that comes with June. 93 | 94 | # Incremental improvement 95 | 96 | Rather than requiring that a program meet a strict set of requirements out the outset, June is set up to be more incremental in its enforcement of rules. By default, all programs are memory safe. The same programs, though, are not required to be thread safe or safe with regards to the use of aliases. 97 | 98 | This allows developers to more easily experiment with an idea, trying it out until they get what they want, and then incrementally increase the amount of checking the compiler does. 99 | 100 | # Importance of encapsulation 101 | 102 | June makes heavy use of the concept of encapsulation. In June, encapsulated private state represented internal implementation details that should not be shared with the outside world, and the developer can ask the June compiler to enforce this. 103 | 104 | By enforcing full encapsulation, the developer can create an abstraction that: 105 | 106 | 1. Is safe to move between threads 107 | 2. Forms a reusable module with a clear interface 108 | 3. Creates a memory boundary with the rest of the program, allowing for targeted memory reuse 109 | 110 | # Connections to other languages 111 | 112 | June has connections to a number of languages. Its most direct connection is to the language Rust, as Rust helped show what was possible with a safe systems language. 113 | 114 | While June shares some of Rust's goals, it does not share Rust's design to reach them. Like languages like Vale, June is aimed at reaching memory safety in ways that do not include the borrow checker. 115 | 116 | We do plan for June to have interop with Rust in the future. To achieve this goal, we'll need to connect to Rust via a stable ABI that allows the June compiler to lay out data in memory in a way that is accessible from June and also compatible with Rust. We hope to collaborate on this as June develops. 117 | -------------------------------------------------------------------------------- /editors/vscode/.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: [master, dev] 5 | pull_request: 6 | branches: [master, dev] 7 | jobs: 8 | test: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: run Textmate scope tests 13 | run: npx vscode-tmgrammar-test -s source.rust -g syntaxes/rust.tmLanguage.json -t "test/**/*.rs" 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v2 18 | - name: package extension 19 | run: npx vsce package 20 | -------------------------------------------------------------------------------- /editors/vscode/.gitignore: -------------------------------------------------------------------------------- 1 | *.afdesign 2 | *.vsix 3 | refs/ 4 | node_modules 5 | -------------------------------------------------------------------------------- /editors/vscode/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that launches the extension inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "runtimeExecutable": "${execPath}", 13 | "args": [ 14 | "--extensionDevelopmentPath=${workspaceFolder}" 15 | ] 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /editors/vscode/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Gulp watch: YAML -> JSON", 6 | "type": "gulp", 7 | "task": "watch-yaml", 8 | "runOptions": { 9 | "runOn": "folderOpen" 10 | }, 11 | "problemMatcher": [], 12 | "group": { 13 | "kind": "build", 14 | "isDefault": true 15 | }, 16 | "presentation": { 17 | "clear": false, 18 | "echo": true, 19 | "focus": false, 20 | "panel": "shared", 21 | "reveal": "silent", 22 | "showReuseMessage": true 23 | } 24 | }, 25 | { 26 | "label": "Test TM scopes", 27 | "type": "shell", 28 | "command": "npx vscode-tmgrammar-test -c -s source.rust -g syntaxes/rust.tmLanguage.json -t \"test/**/*.rs\"", 29 | "group": { 30 | "kind": "test", 31 | "isDefault": true 32 | }, 33 | "presentation": { 34 | "clear": true, 35 | "echo": true, 36 | "focus": false, 37 | "panel": "shared", 38 | "reveal": "always", 39 | "showReuseMessage": true 40 | }, 41 | "problemMatcher": { 42 | "fileLocation": [ 43 | "relative", 44 | "${workspaceFolder}" 45 | ], 46 | "pattern": [ 47 | { 48 | "regexp": "^(ERROR)\\s([^:]+):(\\d+):(\\d+):(\\d+)\\s(.*)$", 49 | "severity": 1, 50 | "file": 2, 51 | "line": 3, 52 | "column": 4, 53 | "endColumn": 5, 54 | "message": 6 55 | } 56 | ] 57 | } 58 | } 59 | ] 60 | } 61 | -------------------------------------------------------------------------------- /editors/vscode/.vscodeignore: -------------------------------------------------------------------------------- 1 | .gitignore 2 | .vscode-test/** 3 | .vscode/** 4 | rust.tmLanguage.yaml 5 | vsc-extension-quickstart.md 6 | -------------------------------------------------------------------------------- /editors/vscode/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | Notable changes to the Rust Syntax extension will be documented in this file. 4 | 5 | ## **[0.5.0]:** 2021-06-09 6 | 7 | - allow granular targeting of `storage.type` scopes by adding `keyword` scopes (thanks to @a5hk) 8 | 9 | ## **[0.4.0]:** 2020-11-10 10 | 11 | - add a stub for future scope tests 12 | - add dev dependencies and a CI workflow for contributors 13 | 14 | ## **[0.3.0]:** 2020-11-08 15 | 16 | - more conventional keyword scopes (thanks: @cynecx) 17 | - many interval patch fixes (ranges, angle brackets, operator precedence, turbofish, comments, raw strings) 18 | 19 | ## **[0.2.0]:** 2020-10-10 20 | 21 | - move non-control keywords into `storage` and `keyword.other` 22 | - bundle macro bangs into macro scopes 23 | - unique scope for `macro_rules` 24 | 25 | ## **[0.1.2]:** 2020-10-03 26 | 27 | - change to a single meta scope inside string interpolations 28 | 29 | ## **[0.1.0]:** 2020-09-26 30 | 31 | - Initial release 32 | -------------------------------------------------------------------------------- /editors/vscode/LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Dustin Pomerleau 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /editors/vscode/README.md: -------------------------------------------------------------------------------- 1 | # Rust Syntax 2 | 3 | This extension provides a TextMate grammar for Rust. In most cases, you won't need to install the extension, as this repository is upstreamed by VS Code (issues and PRs should be submitted here). 4 | 5 | If you are doing a significant amount of Rust programming, the semantic highlighting provided by [Rust Analyzer][] will be superior to a textmate grammar. For example, semantic highlighting can easily distinguish enums, structs, and traits. 6 | 7 | Rust Syntax is compatible with Rust Analyzer, but the scopes provided by this extension will not be visible while semantic highlighting is enabled. If for some reason you would like to disable semantic highlighting, you can do this in your `settings.json`: 8 | 9 | ```json 10 | "[rust]": { 11 | "editor.semanticHighlighting.enabled": false 12 | } 13 | ``` 14 | 15 | ## Compatibility 16 | 17 | Not all themes are specifically optimized for Rust. 18 | We have tried to provide sensible default scopes that will work with most themes. 19 | If you want to modify the colors in a particular theme, you can do so in your `settings.json`: 20 | 21 | ```json 22 | "editor.tokenColorCustomizations": { 23 | "[Theme Name]": { 24 | "textMateRules": [ 25 | { 26 | "scope": "variable.other.rust", 27 | "settings": { 28 | "foreground": "#ffff00" 29 | } 30 | } 31 | ] 32 | } 33 | } 34 | ``` 35 | 36 | The VS Code command `Developer: Inspect Editor Tokens and Scopes` will show you the scope stack at the current cursor position. 37 | 38 | ## Contributing 39 | 40 | The grammar is maintained as YAML, using tasks to generate JSON on save (please don't edit the JSON grammar directly). 41 | You can regenerate the JSON manually from the command palette using `Tasks: Run Build Task`. 42 | 43 | [Rust Analyzer]: https://marketplace.visualstudio.com/items?itemName=matklad.rust-analyzer 44 | -------------------------------------------------------------------------------- /editors/vscode/gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp') 2 | var yaml = require('gulp-yaml') 3 | 4 | gulp.task('yaml', function(cb) { 5 | gulp 6 | .src('./syntaxes/*.yml') 7 | .pipe(yaml({ safe: false, space: 4 })) 8 | .pipe( 9 | gulp.dest(function (f) { 10 | return f.base 11 | }) 12 | ) 13 | cb() 14 | }) 15 | 16 | gulp.task( 17 | 'watch-yaml', 18 | gulp.series('yaml', function(cb) { 19 | gulp.watch('./syntaxes/*.yml', gulp.series('yaml')) 20 | cb() 21 | }) 22 | ) 23 | -------------------------------------------------------------------------------- /editors/vscode/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sophiajt/june/f071a7ce5b6215586817eb80b69db539cb898aed/editors/vscode/images/icon.png -------------------------------------------------------------------------------- /editors/vscode/language-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": { 3 | // symbol used for single line comment. Remove this entry if your language does not support line comments 4 | "lineComment": "//", 5 | // symbols used for start and end a block comment. Remove this entry if your language does not support block comments 6 | "blockComment": [ "/*", "*/" ] 7 | }, 8 | // symbols used as brackets 9 | "brackets": [ 10 | ["{", "}"], 11 | ["[", "]"], 12 | ["(", ")"] 13 | ], 14 | // symbols that are auto closed when typing 15 | "autoClosingPairs": [ 16 | ["{", "}"], 17 | ["[", "]"], 18 | ["(", ")"], 19 | ["\"", "\""], 20 | ["'", "'"] 21 | ], 22 | // symbols that can be used to surround a selection 23 | "surroundingPairs": [ 24 | ["{", "}"], 25 | ["[", "]"], 26 | ["(", ")"], 27 | ["\"", "\""], 28 | ["'", "'"] 29 | ] 30 | } -------------------------------------------------------------------------------- /editors/vscode/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "june-syntax", 3 | "displayName": "June Syntax", 4 | "version": "0.6.1", 5 | "description": "Improved June syntax highlighting", 6 | "publisher": "dustypomerleau", 7 | "homepage": "https://github.com/dustypomerleau/rust-syntax", 8 | "author": { 9 | "name": "jt" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/dustypomerleau/rust-syntax" 14 | }, 15 | "bugs": { 16 | "url": "https://github.com/dustypomerleau/rust-syntax/issues" 17 | }, 18 | "license": "Please see LICENSE.txt", 19 | "galleryBanner": { 20 | "color": "#292d3e", 21 | "theme": "dark" 22 | }, 23 | "icon": "images/icon.png", 24 | "categories": [ 25 | "Programming Languages" 26 | ], 27 | "keywords": [ 28 | "june", 29 | "language", 30 | "syntax", 31 | "grammar", 32 | "highlighting" 33 | ], 34 | "contributes": { 35 | "languages": [ 36 | { 37 | "id": "june", 38 | "aliases": [ 39 | "June" 40 | ], 41 | "extensions": [ 42 | ".june" 43 | ], 44 | "configuration": "./language-configuration.json" 45 | } 46 | ], 47 | "grammars": [ 48 | { 49 | "language": "june", 50 | "scopeName": "source.june", 51 | "path": "./syntaxes/june.tmLanguage.json" 52 | } 53 | ] 54 | }, 55 | "devDependencies": { 56 | "gulp": "^4.0.2", 57 | "gulp-yaml": "^2.0.4", 58 | "js-yaml": "^4.1.0", 59 | "vsce": "^2.9.2", 60 | "vscode-tmgrammar-test": "^0.1.1" 61 | }, 62 | "engines": { 63 | "vscode": "^1.44.0" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /editors/vscode/syntaxes/june.tmLanguage.yml: -------------------------------------------------------------------------------- 1 | # todo: struct fields are getting source.june (see rust2.rs lines 1885 and 1888 with self.slice_head) 2 | # you can capture this because function and method calls will always have parens, even if they are chained to another function by a dot, so you can match the case where a dot is followed by lowercase and then a word boundary without parens 3 | $schema: https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json 4 | name: June 5 | fileTypes: 6 | - june 7 | scopeName: source.june 8 | patterns: 9 | - comment: boxed slice literal 10 | begin: (<)(\[) 11 | beginCaptures: 12 | 1: 13 | name: punctuation.brackets.angle.june 14 | 2: 15 | name: punctuation.brackets.square.june 16 | end: ">" 17 | endCaptures: 18 | 0: 19 | name: punctuation.brackets.angle.june 20 | patterns: 21 | - include: "#block-comments" 22 | - include: "#comments" 23 | - include: "#gtypes" 24 | - include: "#lvariables" 25 | - include: "#lifetimes" 26 | - include: "#punctuation" 27 | - include: "#types" 28 | - comment: macro type metavariables 29 | name: meta.macro.metavariable.type.june 30 | match: (\$)((crate)|([A-Z][A-Za-z0-9_]*))((:)(block|expr|ident|item|lifetime|literal|meta|path?|stmt|tt|ty|vis))? 31 | captures: 32 | 1: 33 | name: keyword.operator.macro.dollar.june 34 | 3: 35 | name: keyword.other.crate.june 36 | 4: 37 | name: entity.name.type.metavariable.june 38 | 6: 39 | name: keyword.operator.key-value.june 40 | 7: 41 | name: variable.other.metavariable.specifier.june 42 | patterns: 43 | - include: "#keywords" 44 | - comment: macro metavariables 45 | name: meta.macro.metavariable.june 46 | match: (\$)([a-z][A-Za-z0-9_]*)((:)(block|expr|ident|item|lifetime|literal|meta|path?|stmt|tt|ty|vis))? 47 | captures: 48 | 1: 49 | name: keyword.operator.macro.dollar.june 50 | 2: 51 | name: variable.other.metavariable.name.june 52 | 4: 53 | name: keyword.operator.key-value.june 54 | 5: 55 | name: variable.other.metavariable.specifier.june 56 | patterns: 57 | - include: "#keywords" 58 | - comment: macro rules 59 | name: meta.macro.rules.june 60 | match: \b(macro_rules!)\s+(([a-z0-9_]+)|([A-Z][a-z0-9_]*))\s+(\{) 61 | captures: 62 | 1: 63 | name: entity.name.function.macro.rules.june 64 | 3: 65 | name: entity.name.function.macro.june 66 | 4: 67 | name: entity.name.type.macro.june 68 | 5: 69 | name: punctuation.brackets.curly.june 70 | - comment: attributes 71 | name: meta.attribute.june 72 | begin: '(#)(\!?)(\[)' 73 | beginCaptures: 74 | 1: 75 | name: punctuation.definition.attribute.june 76 | 2: 77 | name: keyword.operator.attribute.inner.june 78 | 3: 79 | name: punctuation.brackets.attribute.june 80 | end: '\]' 81 | endCaptures: 82 | 0: 83 | name: punctuation.brackets.attribute.june 84 | patterns: 85 | - include: "#block-comments" 86 | - include: "#comments" 87 | - include: "#keywords" 88 | - include: "#lifetimes" 89 | - include: "#punctuation" 90 | - include: "#strings" 91 | - include: "#gtypes" 92 | - include: "#types" 93 | - comment: modules 94 | match: (mod)\s+((?:r#(?!crate|[Ss]elf|super))?[a-z][A-Za-z0-9_]*) 95 | captures: 96 | 1: 97 | name: storage.type.june 98 | 2: 99 | name: entity.name.module.june 100 | - comment: external crate imports 101 | name: meta.import.june 102 | begin: \b(extern)\s+(crate) 103 | beginCaptures: 104 | 1: 105 | name: storage.type.june 106 | 2: 107 | name: keyword.other.crate.june 108 | end: ; 109 | endCaptures: 110 | 0: 111 | name: punctuation.semi.june 112 | patterns: 113 | - include: "#block-comments" 114 | - include: "#comments" 115 | - include: "#keywords" 116 | - include: "#punctuation" 117 | - comment: use statements 118 | name: meta.use.june 119 | begin: \b(use)\s 120 | beginCaptures: 121 | 1: 122 | name: keyword.other.june 123 | end: ; 124 | endCaptures: 125 | 0: 126 | name: punctuation.semi.june 127 | patterns: 128 | - include: "#block-comments" 129 | - include: "#comments" 130 | - include: "#keywords" 131 | - include: "#namespaces" 132 | - include: "#punctuation" 133 | - include: "#types" 134 | - include: "#lvariables" 135 | - include: "#block-comments" 136 | - include: "#comments" 137 | - include: "#lvariables" 138 | - include: "#constants" 139 | - include: "#gtypes" 140 | - include: "#functions" 141 | - include: "#types" 142 | - include: "#keywords" 143 | - include: "#lifetimes" 144 | - include: "#macros" 145 | - include: "#namespaces" 146 | - include: "#punctuation" 147 | - include: "#strings" 148 | - include: "#variables" 149 | repository: 150 | comments: 151 | patterns: 152 | - comment: documentation comments 153 | name: comment.line.documentation.june 154 | match: ^\s*///.* 155 | - comment: line comments 156 | name: comment.line.double-slash.june 157 | match: \s*//.* 158 | block-comments: 159 | patterns: 160 | - comment: empty block comments 161 | name: comment.block.june 162 | match: /\*\*/ 163 | - comment: block documentation comments 164 | name: comment.block.documentation.june 165 | begin: /\*\* 166 | end: \*/ 167 | patterns: 168 | - include: "#block-comments" 169 | - comment: block comments 170 | name: comment.block.june 171 | begin: /\*(?!\*) 172 | end: \*/ 173 | patterns: 174 | - include: "#block-comments" 175 | constants: 176 | patterns: 177 | - comment: ALL CAPS constants 178 | name: constant.other.caps.june 179 | match: \b[A-Z]{2}[A-Z0-9_]*\b 180 | - comment: constant declarations 181 | match: \b(const)\s+([A-Z][A-Za-z0-9_]*)\b 182 | captures: 183 | 1: 184 | name: storage.type.june 185 | 2: 186 | name: constant.other.caps.june 187 | - comment: decimal integers and floats 188 | name: constant.numeric.decimal.june 189 | match: \b\d[\d_]*(\.?)[\d_]*(?:(E)([+-])([\d_]+))?(f32|f64|i128|i16|i32|i64|i8|isize|u128|u16|u32|u64|u8|usize)?\b 190 | captures: 191 | 1: 192 | name: punctuation.separator.dot.decimal.june 193 | 2: 194 | name: keyword.operator.exponent.june 195 | 3: 196 | name: keyword.operator.exponent.sign.june 197 | 4: 198 | name: constant.numeric.decimal.exponent.mantissa.june 199 | 5: 200 | name: entity.name.type.numeric.june 201 | - comment: hexadecimal integers 202 | name: constant.numeric.hex.june 203 | match: \b0x[\da-fA-F_]+(i128|i16|i32|i64|i8|isize|u128|u16|u32|u64|u8|usize)?\b 204 | captures: 205 | 1: 206 | name: entity.name.type.numeric.june 207 | - comment: octal integers 208 | name: constant.numeric.oct.june 209 | match: \b0o[0-7_]+(i128|i16|i32|i64|i8|isize|u128|u16|u32|u64|u8|usize)?\b 210 | captures: 211 | 1: 212 | name: entity.name.type.numeric.june 213 | - comment: binary integers 214 | name: constant.numeric.bin.june 215 | match: \b0b[01_]+(i128|i16|i32|i64|i8|isize|u128|u16|u32|u64|u8|usize)?\b 216 | captures: 217 | 1: 218 | name: entity.name.type.numeric.june 219 | - comment: booleans 220 | name: constant.language.bool.june 221 | match: \b(true|false)\b 222 | - comment: pointers 223 | name: constant.language.pointers.june 224 | match: \b(none)\b 225 | escapes: 226 | comment: "escapes: ASCII, byte, Unicode, quote, regex" 227 | name: constant.character.escape.june 228 | match: (\\)(?:(?:(x[0-7][0-7a-fA-F])|(u(\{)[\da-fA-F]{4,6}(\}))|.)) 229 | captures: 230 | 1: 231 | name: constant.character.escape.backslash.june 232 | 2: 233 | name: constant.character.escape.bit.june 234 | 3: 235 | name: constant.character.escape.unicode.june 236 | 4: 237 | name: constant.character.escape.unicode.punctuation.june 238 | 5: 239 | name: constant.character.escape.unicode.punctuation.june 240 | functions: 241 | patterns: 242 | - comment: pub as a function 243 | match: \b(pub)(\() 244 | captures: 245 | 1: 246 | name: keyword.other.june 247 | 2: 248 | name: punctuation.brackets.round.june 249 | - comment: function definition 250 | name: meta.function.definition.june 251 | begin: \b(fun)\s+((?:r#(?!crate|[Ss]elf|super))?[A-Za-z0-9_]+)((\()|(<)) 252 | beginCaptures: 253 | 1: 254 | name: keyword.other.fun.june 255 | 2: 256 | name: entity.name.function.june 257 | 4: 258 | name: punctuation.brackets.round.june 259 | 5: 260 | name: punctuation.brackets.angle.june 261 | end: \{|; 262 | endCaptures: 263 | 0: 264 | name: punctuation.brackets.curly.june 265 | patterns: 266 | - include: "#block-comments" 267 | - include: "#comments" 268 | - include: "#keywords" 269 | - include: "#lvariables" 270 | - include: "#constants" 271 | - include: "#gtypes" 272 | - include: "#functions" 273 | - include: "#lifetimes" 274 | - include: "#macros" 275 | - include: "#namespaces" 276 | - include: "#punctuation" 277 | - include: "#strings" 278 | - include: "#types" 279 | - include: "#variables" 280 | - # todo: capitalized functions in most cases represent enum members or tuple structs 281 | # separate these out and color them accordingly 282 | # this has to be done without breaking struct scoping when the struct keyword is used: 283 | # struct MyStruct() 284 | # this currently highlights correctly, even with parens 285 | comment: function/method calls, chaining 286 | name: meta.function.call.june 287 | begin: ((?:r#(?!crate|[Ss]elf|super))?[A-Za-z0-9_]+)(\() 288 | beginCaptures: 289 | 1: 290 | name: entity.name.function.june 291 | 2: 292 | name: punctuation.brackets.round.june 293 | end: \) 294 | endCaptures: 295 | 0: 296 | name: punctuation.brackets.round.june 297 | patterns: 298 | - include: "#block-comments" 299 | - include: "#comments" 300 | - include: "#keywords" 301 | - include: "#lvariables" 302 | - include: "#constants" 303 | - include: "#gtypes" 304 | - include: "#functions" 305 | - include: "#lifetimes" 306 | - include: "#macros" 307 | - include: "#namespaces" 308 | - include: "#punctuation" 309 | - include: "#strings" 310 | - include: "#types" 311 | - include: "#variables" 312 | - comment: function/method calls with turbofish 313 | name: meta.function.call.june 314 | begin: ((?:r#(?!crate|[Ss]elf|super))?[A-Za-z0-9_]+)(?=::<.*>\() 315 | beginCaptures: 316 | 1: 317 | name: entity.name.function.june 318 | end: \) 319 | endCaptures: 320 | 0: 321 | name: punctuation.brackets.round.june 322 | patterns: 323 | - include: "#block-comments" 324 | - include: "#comments" 325 | - include: "#keywords" 326 | - include: "#lvariables" 327 | - include: "#constants" 328 | - include: "#gtypes" 329 | - include: "#functions" 330 | - include: "#lifetimes" 331 | - include: "#macros" 332 | - include: "#namespaces" 333 | - include: "#punctuation" 334 | - include: "#strings" 335 | - include: "#types" 336 | - include: "#variables" 337 | keywords: 338 | patterns: 339 | - comment: control flow keywords 340 | name: keyword.control.june 341 | match: \b(await|break|continue|do|else|for|if|loop|match|return|try|while|yield)\b 342 | - comment: storage keywords 343 | name: keyword.other.june storage.type.june 344 | match: \b(extern|let|macro|mod)\b 345 | - comment: const keyword 346 | name: storage.modifier.june 347 | match: \b(const)\b 348 | - comment: raw keyword 349 | name: storage.modifier.june 350 | match: \b(raw)\b 351 | - comment: type keyword 352 | name: keyword.declaration.type.june storage.type.june 353 | match: \b(type)\b 354 | - comment: enum keyword 355 | name: keyword.declaration.enum.june storage.type.june 356 | match: \b(enum)\b 357 | - comment: trait keyword 358 | name: keyword.declaration.trait.june storage.type.june 359 | match: \b(trait)\b 360 | - comment: struct keyword 361 | name: keyword.declaration.struct.june storage.type.june 362 | match: \b(struct)\b 363 | - comment: class keyword 364 | name: keyword.declaration.class.june storage.type.june 365 | match: \b(class)\b - comment: noalloc keyword 366 | name: keyword.declaration.noalloc.june storage.type.june 367 | match: \b(noalloc)\b 368 | - comment: private keyword 369 | name: keyword.declaration.private.june storage.type.june 370 | match: \b(private)\b 371 | - comment: public keyword 372 | name: keyword.declaration.public.june storage.type.june 373 | match: \b(public)\b 374 | - comment: storage modifiers 375 | name: storage.modifier.june 376 | match: \b(abstract|static)\b 377 | - comment: other keywords 378 | name: keyword.other.june 379 | match: \b(as|async|become|box|dyn|move|final|impl|in|override|priv|pub|ref|typeof|union|unsafe|unsized|use|virtual|where)\b 380 | - comment: fun 381 | name: keyword.other.fun.june 382 | match: \bfn\b 383 | - comment: crate 384 | name: keyword.other.crate.june 385 | match: \bcrate\b 386 | - comment: defer 387 | name: keyword.other.defer.june 388 | match: \bdefer\b 389 | - comment: mut 390 | name: storage.modifier.mut.june 391 | match: \bmut\b 392 | - comment: local 393 | name: storage.modifier.local.june 394 | match: \blocal\b 395 | - comment: new 396 | name: storage.modifier.new.june 397 | match: \bnew\b 398 | - comment: owned 399 | name: storage.modifier.owned.june 400 | match: \bowned\b 401 | - comment: logical operators 402 | name: keyword.operator.logical.june 403 | match: (\^|\||\|\||&&|<<|>>|!)(?!=) 404 | - comment: logical AND, borrow references 405 | name: keyword.operator.borrow.and.june 406 | match: "&(?![&=])" 407 | - comment: assignment operators 408 | name: keyword.operator.assignment.june 409 | match: (\+=|-=|\*=|/=|%=|\^=|&=|\|=|<<=|>>=) 410 | - comment: single equal 411 | name: keyword.operator.assignment.equal.june 412 | match: "(?])=(?!=|>)" 413 | - comment: comparison operators 414 | name: keyword.operator.comparison.june 415 | match: (=(=)?(?!>)|!=|<=|(?=) 416 | - comment: math operators 417 | name: keyword.operator.math.june 418 | match: '(([+%]|(\*(?!\w)))(?!=))|(-(?!>))|(/(?!/))' 419 | - comment: less than, greater than (special case) 420 | match: (?:\b|(?:(\))|(\])|(\})))[ \t]+([<>])[ \t]+(?:\b|(?:(\()|(\[)|(\{))) 421 | captures: 422 | 1: 423 | name: punctuation.brackets.round.june 424 | 2: 425 | name: punctuation.brackets.square.june 426 | 3: 427 | name: punctuation.brackets.curly.june 428 | 4: 429 | name: keyword.operator.comparison.june 430 | 5: 431 | name: punctuation.brackets.round.june 432 | 6: 433 | name: punctuation.brackets.square.june 434 | 7: 435 | name: punctuation.brackets.curly.june 436 | - comment: namespace operator 437 | name: keyword.operator.namespace.june 438 | match: "::" 439 | - comment: dereference asterisk 440 | match: (\*)(?=\w+) 441 | captures: 442 | 1: 443 | name: keyword.operator.dereference.june 444 | - comment: subpattern binding 445 | name: keyword.operator.subpattern.june 446 | match: "@" 447 | - comment: dot access 448 | name: keyword.operator.access.dot.june 449 | match: \.(?!\.) 450 | - comment: ranges, range patterns 451 | name: keyword.operator.range.june 452 | match: \.{2}(=|\.)? 453 | - comment: colon 454 | name: keyword.operator.key-value.june 455 | match: ":(?!:)" 456 | - comment: dashrocket, skinny arrow 457 | name: keyword.operator.arrow.skinny.june 458 | match: -> 459 | - comment: hashrocket, fat arrow 460 | name: keyword.operator.arrow.fat.june 461 | match: => 462 | - comment: dollar macros 463 | name: keyword.operator.macro.dollar.june 464 | match: \$ 465 | - comment: question mark operator, questionably sized, macro kleene matcher 466 | name: keyword.operator.question.june 467 | match: \? 468 | interpolations: 469 | comment: curly brace interpolations 470 | name: meta.interpolation.june 471 | match: '({)[^"{}]*(})' 472 | captures: 473 | 1: 474 | name: punctuation.definition.interpolation.june 475 | 2: 476 | name: punctuation.definition.interpolation.june 477 | lifetimes: 478 | patterns: 479 | - comment: named lifetime parameters 480 | match: (['])([a-zA-Z_][0-9a-zA-Z_]*)(?!['])\b 481 | captures: 482 | 1: 483 | name: punctuation.definition.lifetime.june 484 | 2: 485 | name: entity.name.type.lifetime.june 486 | - comment: borrowing references to named lifetimes 487 | match: (\&)(['])([a-zA-Z_][0-9a-zA-Z_]*)(?!['])\b 488 | captures: 489 | 1: 490 | name: keyword.operator.borrow.june 491 | 2: 492 | name: punctuation.definition.lifetime.june 493 | 3: 494 | name: entity.name.type.lifetime.june 495 | macros: 496 | patterns: 497 | - comment: macros 498 | name: meta.macro.june 499 | match: (([a-z_][A-Za-z0-9_]*!)|([A-Z_][A-Za-z0-9_]*!)) 500 | captures: 501 | 2: 502 | name: entity.name.function.macro.june 503 | 3: 504 | name: entity.name.type.macro.june 505 | namespaces: 506 | patterns: 507 | - comment: namespace (non-type, non-function path segment) 508 | match: (?" 529 | endCaptures: 530 | 0: 531 | name: punctuation.brackets.angle.june 532 | patterns: 533 | - include: "#block-comments" 534 | - include: "#comments" 535 | - include: "#keywords" 536 | - include: "#lvariables" 537 | - include: "#lifetimes" 538 | - include: "#punctuation" 539 | - include: "#types" 540 | - include: "#variables" 541 | - comment: primitive types 542 | name: entity.name.type.primitive.june 543 | match: \b(bool|char|str)\b 544 | - comment: trait declarations 545 | match: \b(trait)\s+([A-Z][A-Za-z0-9]*)\b 546 | captures: 547 | 1: 548 | name: keyword.declaration.trait.june storage.type.june 549 | 2: 550 | name: entity.name.type.trait.june 551 | # todo: add a specific case for struct fields so they can have different scope than variables - make sure not to catch namespaces 552 | - comment: struct declarations 553 | match: \b(struct)\s+([A-Z][A-Za-z0-9]*)\b 554 | captures: 555 | 1: 556 | name: keyword.declaration.struct.june storage.type.june 557 | 2: 558 | name: entity.name.type.struct.june 559 | - comment: class declarations 560 | match: \b(class)\s+([A-Z][A-Za-z0-9]*)\b 561 | captures: 562 | 1: 563 | name: keyword.declaration.class.june storage.type.june 564 | 2: 565 | name: entity.name.type.class.june 566 | - comment: enum declarations 567 | match: \b(enum)\s+([A-Z][A-Za-z0-9_]*)\b 568 | captures: 569 | 1: 570 | name: keyword.declaration.enum.june storage.type.june 571 | 2: 572 | name: entity.name.type.enum.june 573 | - comment: type declarations 574 | match: \b(type)\s+([A-Z][A-Za-z0-9_]*)\b 575 | captures: 576 | 1: 577 | name: keyword.declaration.type.june storage.type.june 578 | 2: 579 | name: entity.name.type.declaration.june 580 | - comment: types 581 | name: entity.name.type.june 582 | match: '\b[A-Z][A-Za-z0-9]*\b(?!!)' 583 | gtypes: 584 | patterns: 585 | - comment: option types 586 | name: entity.name.type.option.june 587 | match: \b(Some|None)\b 588 | - comment: result types 589 | name: entity.name.type.result.june 590 | match: \b(Ok|Err)\b 591 | punctuation: 592 | patterns: 593 | - comment: comma 594 | name: punctuation.comma.june 595 | match: "," 596 | - comment: curly braces 597 | name: punctuation.brackets.curly.june 598 | match: "[{}]" 599 | - comment: parentheses, round brackets 600 | name: punctuation.brackets.round.june 601 | match: "[()]" 602 | - comment: semicolon 603 | name: punctuation.semi.june 604 | match: ; 605 | - comment: square brackets 606 | name: punctuation.brackets.square.june 607 | match: '[\[\]]' 608 | - comment: angle brackets 609 | name: punctuation.brackets.angle.june 610 | match: "(?]" 611 | strings: 612 | patterns: 613 | - comment: double-quoted strings and byte strings 614 | name: string.quoted.double.june 615 | begin: '((b|c)?)(")' 616 | beginCaptures: 617 | 1: 618 | name: string.quoted.byte.raw.june 619 | 2: 620 | name: punctuation.definition.string.june 621 | end: '"' 622 | endCaptures: 623 | 0: 624 | name: punctuation.definition.string.june 625 | patterns: 626 | - include: "#escapes" 627 | - include: "#interpolations" 628 | - comment: double-quoted raw strings and raw byte strings 629 | name: string.quoted.double.june 630 | begin: '((b|c)?r)(#*)(")' 631 | beginCaptures: 632 | 1: 633 | name: string.quoted.byte.raw.june 634 | 2: 635 | name: punctuation.definition.string.raw.june 636 | 3: 637 | name: punctuation.definition.string.june 638 | end: '(")(\2)' 639 | endCaptures: 640 | 1: 641 | name: punctuation.definition.string.june 642 | 2: 643 | name: punctuation.definition.string.raw.june 644 | - comment: characters and bytes 645 | name: string.quoted.single.char.june 646 | begin: "(b|c)?(')" 647 | beginCaptures: 648 | 1: 649 | name: string.quoted.byte.raw.june 650 | 2: 651 | name: punctuation.definition.char.june 652 | end: "'" 653 | endCaptures: 654 | 0: 655 | name: punctuation.definition.char.june 656 | patterns: 657 | - include: "#escapes" 658 | lvariables: 659 | patterns: 660 | - comment: self 661 | name: variable.language.self.june 662 | match: \b[Ss]elf\b 663 | - comment: super 664 | name: variable.language.super.june 665 | match: \bsuper\b 666 | variables: 667 | patterns: 668 | # In order to capture variables ending ranges, but not struct field access, we match a preceding dot, only if it's preceded by at least one other dot. 669 | # The double negation states that the pattern "must not be preceded by a dot that is not preceded by a dot." 670 | # Attempting to match on (\.{2,})? won't work, because then struct field access can match after the dot. 671 | - comment: variables 672 | name: variable.other.june 673 | match: \b(?, 34 | 35 | #[command(subcommand)] 36 | pub command: Option, 37 | } 38 | 39 | #[derive(clap::Subcommand, Debug)] 40 | pub enum Command { 41 | Build(build::Args), 42 | New(new::Args), 43 | Run(run::Args), 44 | } 45 | 46 | impl Cli { 47 | pub fn parse() -> Self { 48 | clap::Parser::parse() 49 | } 50 | 51 | fn project_root(&self) -> Result { 52 | let curr = std::env::current_dir().map_err(ProjectRootError::CurrentDir)?; 53 | 54 | for path in curr.ancestors() { 55 | if path.join("June.toml").is_file() { 56 | return Ok(path.to_path_buf()); 57 | } 58 | } 59 | 60 | Err(ProjectRootError::JuneToml) 61 | } 62 | 63 | fn new_project(&self, args: &new::Args) -> Result<(), CLIError> { 64 | let main_dir = args.path.join("main"); 65 | // create the directory 66 | // create the main subdirectory 67 | std::fs::create_dir_all(&main_dir).map_err(NewProjectError::MainDir)?; 68 | // .expect("path should be a valid path the user can create"); 69 | // create the hello world main.june file 70 | let hello_world = r#"println(c"hello")"#; 71 | std::fs::write(main_dir.join("main.june"), hello_world) 72 | .map_err(NewProjectError::MainJune)?; 73 | // create the empty June.toml file in the root 74 | std::fs::File::create(args.path.join("June.toml")).map_err(NewProjectError::JuneToml)?; 75 | // initialize git vcs 76 | let _cmd = std::process::Command::new("git") 77 | .arg("init") 78 | .arg("-b") 79 | .arg("main") 80 | .current_dir(&args.path) 81 | .output() 82 | .map_err(NewProjectError::GitInit)?; 83 | 84 | Ok(()) 85 | } 86 | 87 | fn build(&self, _args: &build::Args) -> Result<(), CLIError> { 88 | let root_dir = self.project_root().map_err(BuildProjectError::from)?; 89 | let main_root = root_dir.join("main").join("main.june"); 90 | let build_dir = root_dir.join("build").join("debug"); 91 | std::fs::create_dir_all(&build_dir).map_err(BuildProjectError::BuildDir)?; 92 | 93 | if main_root.is_file() { 94 | let mut compiler = Compiler::new(); 95 | compiler = compile(&main_root, compiler); 96 | let codegen = codegen::Codegen::new(compiler); 97 | 98 | let output = codegen.codegen(); 99 | 100 | let c_output_filepath = build_dir.join("main.c"); 101 | // TODO: get actual project name from toml file 102 | let app_filepath = build_dir.join("main"); 103 | let mut output_file = 104 | File::create(&c_output_filepath).map_err(BuildProjectError::MainCCreate)?; 105 | output_file 106 | .write_all(&output) 107 | .map_err(BuildProjectError::MainCWrite)?; 108 | 109 | // Next, compile the file 110 | let compiler = std::process::Command::new("clang") 111 | .arg(&c_output_filepath) 112 | .arg("-o") 113 | .arg(&app_filepath) 114 | .output() 115 | .map_err(BuildProjectError::Clang)?; 116 | 117 | if !compiler.status.success() { 118 | let _ = std::io::stdout().write_all(&compiler.stdout); 119 | let _ = std::io::stdout().write_all(&compiler.stderr); 120 | panic!("Clang did not compile successfully"); 121 | } 122 | } 123 | 124 | Ok(()) 125 | } 126 | 127 | fn run(&self, args: &run::Args) -> Result<(), CLIError> { 128 | let project_root = self.project_root().map_err(RunProjectError::from)?; 129 | let cmd = project_root.join("build").join("debug").join("main"); 130 | let status = std::process::Command::new(cmd) 131 | .args(&args.user_args) 132 | .status() 133 | .map_err(RunProjectError::Subprocess)?; 134 | 135 | if let Some(code) = status.code() { 136 | std::process::exit(code); 137 | } 138 | 139 | Ok(()) 140 | } 141 | } 142 | 143 | #[derive(Debug)] 144 | enum CLIError { 145 | New(NewProjectError), 146 | Build(BuildProjectError), 147 | Run(RunProjectError), 148 | } 149 | 150 | impl fmt::Display for CLIError { 151 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 152 | match self { 153 | CLIError::New(_) => f.write_str("cannot to create new project"), 154 | CLIError::Build(_) => f.write_str("cannot to compile project"), 155 | CLIError::Run(_) => f.write_str("cannot to run project"), 156 | } 157 | } 158 | } 159 | 160 | impl Error for CLIError { 161 | fn source(&self) -> Option<&(dyn Error + 'static)> { 162 | match self { 163 | CLIError::New(src) => Some(src), 164 | CLIError::Build(src) => Some(src), 165 | CLIError::Run(src) => Some(src), 166 | } 167 | } 168 | } 169 | 170 | impl From for CLIError { 171 | fn from(value: NewProjectError) -> Self { 172 | Self::New(value) 173 | } 174 | } 175 | 176 | impl From for CLIError { 177 | fn from(value: BuildProjectError) -> Self { 178 | Self::Build(value) 179 | } 180 | } 181 | 182 | impl From for CLIError { 183 | fn from(value: RunProjectError) -> Self { 184 | Self::Run(value) 185 | } 186 | } 187 | 188 | #[derive(Debug)] 189 | enum ProjectRootError { 190 | CurrentDir(io::Error), 191 | JuneToml, 192 | } 193 | 194 | impl fmt::Display for ProjectRootError { 195 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 196 | match self { 197 | ProjectRootError::CurrentDir(_) => f.write_str("cannot to access current directory"), 198 | ProjectRootError::JuneToml => f.write_str("June.toml file not found"), 199 | } 200 | } 201 | } 202 | 203 | impl Error for ProjectRootError { 204 | fn source(&self) -> Option<&(dyn Error + 'static)> { 205 | match self { 206 | ProjectRootError::CurrentDir(src) => Some(src), 207 | ProjectRootError::JuneToml => None, 208 | } 209 | } 210 | } 211 | 212 | #[derive(Debug)] 213 | enum NewProjectError { 214 | MainDir(io::Error), 215 | MainJune(io::Error), 216 | JuneToml(io::Error), 217 | GitInit(io::Error), 218 | } 219 | 220 | impl fmt::Display for NewProjectError { 221 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 222 | match self { 223 | NewProjectError::MainDir(_) => f.write_str("cannot to create main directory"), 224 | NewProjectError::MainJune(_) => f.write_str("cannot to create main.june file"), 225 | NewProjectError::JuneToml(_) => f.write_str("cannot to create June.toml file"), 226 | NewProjectError::GitInit(_) => f.write_str("cannot to initialize git repository"), 227 | } 228 | } 229 | } 230 | 231 | impl Error for NewProjectError { 232 | fn source(&self) -> Option<&(dyn Error + 'static)> { 233 | match self { 234 | NewProjectError::MainDir(src) => Some(src), 235 | NewProjectError::MainJune(src) => Some(src), 236 | NewProjectError::JuneToml(src) => Some(src), 237 | NewProjectError::GitInit(src) => Some(src), 238 | } 239 | } 240 | } 241 | 242 | #[derive(Debug)] 243 | enum BuildProjectError { 244 | NoProjectRoot(ProjectRootError), 245 | BuildDir(io::Error), 246 | MainCCreate(io::Error), 247 | MainCWrite(io::Error), 248 | Clang(io::Error), 249 | } 250 | 251 | impl fmt::Display for BuildProjectError { 252 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 253 | match self { 254 | BuildProjectError::NoProjectRoot(_) => { 255 | f.write_str("Current directory does not appear to be part of a valid June project") 256 | } 257 | BuildProjectError::BuildDir(_) => f.write_str("cannot create build directory"), 258 | BuildProjectError::MainCCreate(_) => { 259 | f.write_str("cannot create temporary file for c codegen") 260 | } 261 | BuildProjectError::MainCWrite(_) => f.write_str("cannot write to codegen file"), 262 | BuildProjectError::Clang(_) => f.write_str("cannot compile codegen into binary"), 263 | } 264 | } 265 | } 266 | 267 | impl Error for BuildProjectError { 268 | fn source(&self) -> Option<&(dyn Error + 'static)> { 269 | match self { 270 | BuildProjectError::NoProjectRoot(src) => Some(src), 271 | BuildProjectError::BuildDir(src) => Some(src), 272 | BuildProjectError::MainCCreate(src) => Some(src), 273 | BuildProjectError::MainCWrite(src) => Some(src), 274 | BuildProjectError::Clang(src) => Some(src), 275 | } 276 | } 277 | } 278 | 279 | impl From for BuildProjectError { 280 | fn from(value: ProjectRootError) -> Self { 281 | Self::NoProjectRoot(value) 282 | } 283 | } 284 | 285 | #[derive(Debug)] 286 | enum RunProjectError { 287 | NoProjectRoot(ProjectRootError), 288 | Subprocess(io::Error), 289 | } 290 | 291 | impl fmt::Display for RunProjectError { 292 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 293 | match self { 294 | RunProjectError::NoProjectRoot(_) => { 295 | f.write_str("Current directory does not appear to be part of a valid June project") 296 | } 297 | RunProjectError::Subprocess(_) => f.write_str("cannot invoke executable"), 298 | } 299 | } 300 | } 301 | 302 | impl Error for RunProjectError { 303 | fn source(&self) -> Option<&(dyn Error + 'static)> { 304 | match self { 305 | RunProjectError::Subprocess(src) => Some(src), 306 | RunProjectError::NoProjectRoot(src) => Some(src), 307 | } 308 | } 309 | } 310 | 311 | impl From for RunProjectError { 312 | fn from(value: ProjectRootError) -> Self { 313 | Self::NoProjectRoot(value) 314 | } 315 | } 316 | 317 | fn compile(fname: &Path, mut compiler: Compiler) -> Compiler { 318 | let debug_output = false; 319 | 320 | let span_offset = compiler.span_offset(); 321 | compiler.add_file(fname); 322 | 323 | let parser = Parser::new(compiler, span_offset); 324 | let compiler = parser.parse(); 325 | 326 | for error in &compiler.errors { 327 | compiler.print_error(error) 328 | } 329 | 330 | if !compiler.errors.is_empty() { 331 | std::process::exit(1); 332 | } 333 | 334 | let typechecker = Typechecker::new(compiler); 335 | let compiler = typechecker.typecheck(); 336 | 337 | for error in &compiler.errors { 338 | compiler.print_error(error) 339 | } 340 | 341 | if !compiler.errors.is_empty() { 342 | std::process::exit(1); 343 | } 344 | 345 | let lifetime_checker = LifetimeChecker::new(compiler); 346 | let compiler = lifetime_checker.check_lifetimes(); 347 | 348 | for error in &compiler.errors { 349 | compiler.print_error(error) 350 | } 351 | 352 | if debug_output { 353 | println!(); 354 | println!("Results:"); 355 | compiler.print(); 356 | } 357 | 358 | if !compiler.errors.is_empty() { 359 | std::process::exit(1); 360 | } 361 | 362 | compiler 363 | } 364 | 365 | fn main() { 366 | let cli = Cli::parse(); 367 | let fmt_layer = tracing_tree::HierarchicalLayer::default() 368 | .with_writer(std::io::stderr) 369 | .with_indent_lines(true) 370 | .with_targets(true) 371 | .with_indent_amount(2); 372 | let filter_layer = EnvFilter::try_from_default_env() 373 | .or_else(|_| EnvFilter::try_new("info")) 374 | .unwrap(); 375 | 376 | tracing_subscriber::registry() 377 | .with(filter_layer) 378 | .with(fmt_layer) 379 | .init(); 380 | 381 | let mut compiler = Compiler::new(); 382 | 383 | let result = match &cli.command { 384 | Some(command) => match command { 385 | Command::Build(args) => cli.build(args), 386 | Command::New(args) => cli.new_project(args), 387 | Command::Run(args) => cli.run(args), 388 | }, 389 | None => { 390 | for fname in &cli.files { 391 | compiler = compile(fname, compiler); 392 | } 393 | 394 | let codegen = codegen::Codegen::new(compiler); 395 | 396 | let output = codegen.codegen(); 397 | println!("{}", String::from_utf8_lossy(&output)); 398 | Ok(()) 399 | } 400 | }; 401 | 402 | match result { 403 | Ok(()) => {} 404 | Err(error) => { 405 | eprintln!("Error:"); 406 | let error: &(dyn Error + 'static) = &error; 407 | let chain = std::iter::successors(Some(error), |&e| e.source()); 408 | for (ind, e) in chain.enumerate() { 409 | eprintln!(" {ind}: {e}"); 410 | } 411 | } 412 | } 413 | } 414 | -------------------------------------------------------------------------------- /src/new.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | #[derive(clap::Args, Debug)] 4 | /// Create a new june package at 5 | pub struct Args { 6 | pub path: PathBuf, 7 | } 8 | -------------------------------------------------------------------------------- /src/run.rs: -------------------------------------------------------------------------------- 1 | #[derive(clap::Args, Debug)] 2 | pub struct Args { 3 | #[command(flatten)] 4 | pub build_args: super::build::Args, 5 | 6 | #[arg(last = true)] 7 | pub user_args: Vec, 8 | } 9 | -------------------------------------------------------------------------------- /tests/integration/classes/inheritance/named_args_coercion.june: -------------------------------------------------------------------------------- 1 | // output: button down 2 | 3 | class Button { 4 | public button_down: bool 5 | 6 | fun draw_button_down(self) 7 | fun draw_button_up(self) 8 | 9 | } 10 | 11 | class FancyButton: Button { 12 | fun draw_button_up(self) { 13 | // impl here 14 | println(c"button up") 15 | } 16 | } 17 | 18 | class FanciestButton: FancyButton { 19 | fun draw_button_down(self) { 20 | // impl here 21 | println(c"button down") 22 | } 23 | } 24 | 25 | fun draw(b: Button) { 26 | if b.button_down { 27 | b.draw_button_down() 28 | } else { 29 | b.draw_button_up() 30 | } 31 | } 32 | 33 | fun main() { 34 | let button = new FanciestButton(button_down: true) 35 | draw(b: button) 36 | } 37 | 38 | -------------------------------------------------------------------------------- /tests/integration/classes/inheritance/partial_implementation.june: -------------------------------------------------------------------------------- 1 | // output: button down 2 | 3 | class Button { 4 | public button_down: bool 5 | 6 | fun draw_button_down(self) 7 | fun draw_button_up(self) 8 | 9 | fun draw(self) { 10 | if self.button_down { 11 | self.draw_button_down() 12 | } else { 13 | self.draw_button_up() 14 | } 15 | } 16 | } 17 | 18 | class FancyButton: Button { 19 | fun draw_button_up(self) { 20 | // impl here 21 | println(c"button up") 22 | } 23 | } 24 | 25 | class FanciestButton: FancyButton { 26 | fun draw_button_down(self) { 27 | // impl here 28 | println(c"button down") 29 | } 30 | } 31 | 32 | fun main() { 33 | let button = new FanciestButton(button_down: true); 34 | button.draw() 35 | } 36 | 37 | -------------------------------------------------------------------------------- /tests/integration/classes/inheritance/simple.june: -------------------------------------------------------------------------------- 1 | // output: button up 2 | 3 | class Button { 4 | public button_down: bool 5 | 6 | fun draw_button_down(self) 7 | fun draw_button_up(self) 8 | 9 | fun draw(self) { 10 | if self.button_down { 11 | self.draw_button_down() 12 | } else { 13 | self.draw_button_up() 14 | } 15 | } 16 | } 17 | 18 | class FancyButton: Button { 19 | fun draw_button_down(self) { 20 | // impl here 21 | println(c"button down") 22 | } 23 | 24 | fun draw_button_up(self) { 25 | // impl here 26 | println(c"button up") 27 | } 28 | } 29 | 30 | fun main() { 31 | let button = new FancyButton(button_down: false); 32 | button.draw() 33 | } 34 | 35 | -------------------------------------------------------------------------------- /tests/integration/classes/private_by_default.june: -------------------------------------------------------------------------------- 1 | // error: 'new' used on private member field from outside struct or class 2 | 3 | class Foo { 4 | x: i64 5 | } 6 | 7 | let foo = new Foo(x: 100) -------------------------------------------------------------------------------- /tests/integration/control_flow/for_range.june: -------------------------------------------------------------------------------- 1 | // output: 55 2 | 3 | mut total = 0 4 | for i in 1..10 { 5 | total += i 6 | } 7 | 8 | println(total) -------------------------------------------------------------------------------- /tests/integration/control_flow/for_range_var.june: -------------------------------------------------------------------------------- 1 | // output: 45 2 | 3 | mut total = 0 4 | 5 | let j = 5 6 | for i in j..10 { 7 | total += i 8 | } 9 | 10 | println(total) -------------------------------------------------------------------------------- /tests/integration/control_flow/loop.june: -------------------------------------------------------------------------------- 1 | // output: 10 2 | 3 | fun main() { 4 | mut x = 1 5 | while x < 10 { 6 | x = x + 1 7 | } 8 | 9 | println(x) 10 | } -------------------------------------------------------------------------------- /tests/integration/control_flow/loop_with_temporary.june: -------------------------------------------------------------------------------- 1 | // output: 12 2 | 3 | struct Employee { 4 | name: c_string, 5 | age: i64, 6 | } 7 | 8 | fun main() { 9 | mut x = 1 10 | while x < 12 { 11 | let employee = new Employee(name: c"Bob", age: 123) 12 | x = x + 1 13 | } 14 | 15 | println(x) 16 | } -------------------------------------------------------------------------------- /tests/integration/data_structures/circular_linked_list.june: -------------------------------------------------------------------------------- 1 | // output: -2 2 | 3 | struct Node { 4 | data: i64 5 | next: Node? 6 | } 7 | 8 | fun main() { 9 | mut node3 = new Node(data: 3, next: none) 10 | let node2 = new Node(data: 2, next: node3) 11 | let node1 = new Node(data: 1, next: node2) 12 | node3.next = node1 13 | 14 | mut curr: Node? = node2 15 | let end = curr 16 | 17 | // println(curr.data) 18 | mut total = curr.data 19 | 20 | while curr.next != end { 21 | curr = curr.next 22 | total -= curr.data 23 | } 24 | 25 | println(total) 26 | } -------------------------------------------------------------------------------- /tests/integration/data_structures/circular_linked_list_abstraction.june: -------------------------------------------------------------------------------- 1 | // output: 132132 2 | 3 | struct Node { 4 | data: i64 5 | next: Node? 6 | } 7 | 8 | class CircularList { 9 | node: Node? 10 | 11 | fun default() -> owned CircularList { 12 | return new owned CircularList(node: none) 13 | } 14 | 15 | fun push(mut self, data: i64) { 16 | if self.node != none { 17 | let next = self.node.next 18 | self.node.next = new Node(data: data, next: next) 19 | } else { 20 | self.node = new Node(data: data, next: none) 21 | self.node.next = self.node 22 | } 23 | } 24 | 25 | fun next(mut self) -> i64 { 26 | if self.node != none { 27 | let ret = self.node.data 28 | 29 | self.node = self.node.next 30 | return ret 31 | } else { 32 | return 0 33 | } 34 | } 35 | } 36 | 37 | fun helper() -> owned CircularList { 38 | let list = CircularList::default() 39 | 40 | return list 41 | } 42 | 43 | fun main() { 44 | mut list = helper() 45 | 46 | list.push(1) 47 | list.push(2) 48 | list.push(3) 49 | 50 | println(list.next()) 51 | println(list.next()) 52 | println(list.next()) 53 | println(list.next()) 54 | println(list.next()) 55 | println(list.next()) 56 | } -------------------------------------------------------------------------------- /tests/integration/data_structures/circular_linked_list_abstraction_shared.june: -------------------------------------------------------------------------------- 1 | // output: 132132 2 | 3 | struct Node { 4 | data: i64 5 | next: Node? 6 | } 7 | 8 | struct CircularList { 9 | private node: Node? 10 | 11 | fun default() -> owned CircularList { 12 | return new owned CircularList(node: none) 13 | } 14 | 15 | fun push(mut self, data: i64) { 16 | if self.node != none { 17 | let next = self.node.next 18 | self.node.next = new Node(data: data, next: next) 19 | } else { 20 | self.node = new Node(data: data, next: none) 21 | self.node.next = self.node 22 | } 23 | } 24 | 25 | fun next(mut self) -> i64 { 26 | if self.node != none { 27 | let ret = self.node.data 28 | 29 | self.node = self.node.next 30 | return ret 31 | } else { 32 | return 0 33 | } 34 | } 35 | } 36 | 37 | fun helper() -> owned CircularList { 38 | let list = CircularList::default() 39 | 40 | return list 41 | } 42 | 43 | fun main() { 44 | mut list: CircularList = helper() 45 | 46 | list.push(1) 47 | list.push(2) 48 | list.push(3) 49 | 50 | println(list.next()) 51 | println(list.next()) 52 | println(list.next()) 53 | println(list.next()) 54 | println(list.next()) 55 | println(list.next()) 56 | } -------------------------------------------------------------------------------- /tests/integration/data_structures/circular_linked_list_abstraction_shorthand.june: -------------------------------------------------------------------------------- 1 | // output: 132132 2 | 3 | struct Node { 4 | data: i64 5 | next: Node? 6 | } 7 | 8 | struct CircularList { 9 | private node: Node? 10 | 11 | fun default() -> owned CircularList { 12 | return new owned CircularList(node: none) 13 | } 14 | 15 | fun push(mut self, data: i64) { 16 | if .node != none { 17 | let next = .node.next 18 | .node.next = new Node(data: data, next: next) 19 | } else { 20 | .node = new Node(data: data, next: none) 21 | .node.next = .node 22 | } 23 | } 24 | 25 | fun next(mut self) -> i64 { 26 | if .node != none { 27 | let ret = .node.data 28 | 29 | .node = .node.next 30 | return ret 31 | } else { 32 | return 0 33 | } 34 | } 35 | } 36 | 37 | fun helper() -> owned CircularList { 38 | let list = CircularList::default() 39 | 40 | return list 41 | } 42 | 43 | fun main() { 44 | mut list = helper() 45 | 46 | list.push(1) 47 | list.push(2) 48 | list.push(3) 49 | 50 | println(list.next()) 51 | println(list.next()) 52 | println(list.next()) 53 | println(list.next()) 54 | println(list.next()) 55 | println(list.next()) 56 | } -------------------------------------------------------------------------------- /tests/integration/data_structures/circular_linked_list_helper.june: -------------------------------------------------------------------------------- 1 | // output: -2 2 | 3 | struct Node { 4 | data: i64 5 | next: Node? 6 | } 7 | 8 | fun builder() -> Node { 9 | mut node3 = new Node(data: 3, next: none) 10 | let node2 = new Node(data: 2, next: node3) 11 | let node1 = new Node(data: 1, next: node2) 12 | node3.next = node1 13 | 14 | return node2 15 | } 16 | 17 | fun main() { 18 | mut curr: Node? = builder() 19 | let end = curr 20 | 21 | // println(curr.data) 22 | mut total = curr.data 23 | 24 | while curr.next != end { 25 | curr = curr.next 26 | total -= curr.data 27 | } 28 | 29 | println(total) 30 | } -------------------------------------------------------------------------------- /tests/integration/data_structures/vector_of_ints.june: -------------------------------------------------------------------------------- 1 | // output: 21020 2 | 3 | struct Vector { 4 | num_items: i64 5 | capacity: i64 6 | contents: raw[i64] 7 | 8 | fun create_vector() -> Vector { 9 | return new Vector(num_items: 0, capacity: 0, contents: raw[]) 10 | } 11 | 12 | fun size(self) -> i64 { 13 | return .num_items 14 | } 15 | 16 | fun push(mut self, data: i64) { 17 | if .capacity == 0 { 18 | unsafe { resize .contents 1 } 19 | .capacity = 1 20 | } else if .num_items == .capacity { 21 | let new_capacity = .capacity * 2 22 | unsafe { resize .contents new_capacity } 23 | .capacity = new_capacity 24 | } 25 | 26 | unsafe { .contents[.num_items] = data } 27 | .num_items += 1 28 | } 29 | 30 | fun get(self, index: i64) -> i64 { 31 | unsafe { 32 | if index < .num_items { 33 | return .contents[index] 34 | } else { 35 | println(c"Index past end of vector") 36 | return 0 37 | } 38 | } 39 | } 40 | } 41 | 42 | mut v = Vector::create_vector() 43 | v.push(10) 44 | v.push(20) 45 | 46 | println(v.size()) 47 | 48 | println(v.get(0)) 49 | println(v.get(1)) 50 | -------------------------------------------------------------------------------- /tests/integration/data_types/boolean.june: -------------------------------------------------------------------------------- 1 | // output: true 2 | 3 | println(true) -------------------------------------------------------------------------------- /tests/integration/data_types/c_char.june: -------------------------------------------------------------------------------- 1 | // output: b 2 | 3 | println(c'b') -------------------------------------------------------------------------------- /tests/integration/data_types/c_string.june: -------------------------------------------------------------------------------- 1 | // output: hello world 2 | 3 | println(c"hello world") -------------------------------------------------------------------------------- /tests/integration/data_types/double.june: -------------------------------------------------------------------------------- 1 | // output: 3.500000 2 | 3 | println(3.5) -------------------------------------------------------------------------------- /tests/integration/data_types/int.june: -------------------------------------------------------------------------------- 1 | // output: 123 2 | 3 | println(123) -------------------------------------------------------------------------------- /tests/integration/defer/defer_simple.june: -------------------------------------------------------------------------------- 1 | // output: helloFelicia 2 | 3 | struct Person { 4 | name: c_string 5 | } 6 | 7 | fun greet(p: Person) { 8 | println(p.name) 9 | } 10 | 11 | fun main() { 12 | let person = new Person(name: c"Felicia"); 13 | defer person greet 14 | println(c"hello") 15 | } -------------------------------------------------------------------------------- /tests/integration/defer/defer_simple_error.june: -------------------------------------------------------------------------------- 1 | // error: incompatible type in callback for 'defer' 2 | 3 | struct Person { 4 | name: c_string 5 | } 6 | 7 | fun greet(p: i64) { 8 | println(p) 9 | } 10 | 11 | fun main() { 12 | let person = new Person(name: c"Felicia"); 13 | defer person greet 14 | println(c"hello") 15 | } -------------------------------------------------------------------------------- /tests/integration/enums/enum_catchall.june: -------------------------------------------------------------------------------- 1 | // output: Some 2 | 3 | enum State { 4 | None 5 | Some(i64) 6 | Struct { x: i64, y: i64 } 7 | } 8 | 9 | fun main() { 10 | let x = State::Struct(x: 66, y: 77) 11 | 12 | match x { 13 | State::None => println(c"None") 14 | _ => println(c"Some") 15 | } 16 | } -------------------------------------------------------------------------------- /tests/integration/enums/enum_method.june: -------------------------------------------------------------------------------- 1 | // output: 44None 2 | 3 | enum State { 4 | None 5 | Some(i64) 6 | 7 | fun print(self) { 8 | match self { 9 | State::None => println(c"None") 10 | State::Some(i) => println(i) 11 | } 12 | } 13 | } 14 | 15 | fun main() { 16 | let x = State::Some(44) 17 | x.print() 18 | 19 | let y = State::None 20 | y.print() 21 | } -------------------------------------------------------------------------------- /tests/integration/enums/enum_missing_arm_error.june: -------------------------------------------------------------------------------- 1 | // error: missing pattern match for None 2 | 3 | enum State { 4 | None 5 | Some(i64) 6 | } 7 | 8 | fun main() { 9 | let x = State::Some(77) 10 | 11 | match x { 12 | State::Some(i) => println(i) 13 | } 14 | } -------------------------------------------------------------------------------- /tests/integration/enums/enum_multi_arm.june: -------------------------------------------------------------------------------- 1 | // output: 77 2 | 3 | enum State { 4 | None 5 | Some(i64) 6 | } 7 | 8 | fun main() { 9 | let x = State::Some(77) 10 | 11 | match x { 12 | State::None => println(c"None") 13 | State::Some(i) => println(i) 14 | } 15 | } -------------------------------------------------------------------------------- /tests/integration/enums/enum_same_name.june: -------------------------------------------------------------------------------- 1 | // output: 2 2 | 3 | enum State { 4 | Foo { x: i64, y: i64 } 5 | Bar { x: i64, y: i64 } 6 | } 7 | 8 | let state = State::Bar(x: 1, y: 2) 9 | 10 | match state { 11 | State::Foo(x, y) => { 12 | println(x) 13 | } 14 | State::Bar(x, y) => { 15 | println(y) 16 | } 17 | } -------------------------------------------------------------------------------- /tests/integration/enums/enum_simple.june: -------------------------------------------------------------------------------- 1 | // output: None 2 | 3 | enum State { 4 | None 5 | } 6 | 7 | fun main() { 8 | let x = State::None 9 | 10 | match x { 11 | State::None => println(c"None") 12 | } 13 | } -------------------------------------------------------------------------------- /tests/integration/enums/enum_simple_error.june: -------------------------------------------------------------------------------- 1 | // error: could not find match enum case 2 | 3 | enum State { 4 | None 5 | } 6 | 7 | fun main() { 8 | let x = State::None 9 | 10 | match x { 11 | State::Some => println(c"Some") 12 | } 13 | } -------------------------------------------------------------------------------- /tests/integration/enums/enum_single_payload.june: -------------------------------------------------------------------------------- 1 | // output: 4 2 | 3 | enum State { 4 | StateId(i64) 5 | } 6 | 7 | fun main() { 8 | let y = State::StateId(4) 9 | 10 | match y { 11 | State::StateId(x) => println(x) 12 | } 13 | } -------------------------------------------------------------------------------- /tests/integration/enums/enum_single_pointer_payload.june: -------------------------------------------------------------------------------- 1 | // output: 88 2 | 3 | struct Stats { 4 | age: i64 5 | } 6 | 7 | enum State { 8 | Stats(Stats) 9 | } 10 | 11 | fun helper() -> State { 12 | let y = State::Stats(new Stats(age: 88)) 13 | 14 | return y 15 | } 16 | 17 | fun main() { 18 | let z = helper(); 19 | 20 | match z { 21 | State::Stats(x) => println(x.age) 22 | } 23 | } -------------------------------------------------------------------------------- /tests/integration/enums/enum_static_method.june: -------------------------------------------------------------------------------- 1 | // output: 99 2 | 3 | enum State { 4 | None 5 | Some(i64) 6 | 7 | fun create_value(x: i64) -> State { 8 | return State::Some(x) 9 | } 10 | } 11 | 12 | fun main() { 13 | let x = State::create_value(99) 14 | 15 | match x { 16 | State::None => println(c"None") 17 | State::Some(i) => println(i) 18 | } 19 | } -------------------------------------------------------------------------------- /tests/integration/enums/enum_struct_payload.june: -------------------------------------------------------------------------------- 1 | // output: 9 2 | 3 | enum State { 4 | Duration { from: i64, to: i64 } 5 | } 6 | 7 | fun main() { 8 | let z = State::Duration(from: 1, to: 10) 9 | 10 | match z { 11 | State::Duration(from, to) => { 12 | println(to - from) 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /tests/integration/enums/enum_var_scope_error.june: -------------------------------------------------------------------------------- 1 | // error: can't find variable 2 | 3 | enum State { 4 | StateId(i64) 5 | } 6 | 7 | fun main() { 8 | let y = State::StateId(4) 9 | 10 | match y { 11 | State::StateId(x) => println(x) 12 | } 13 | 14 | println(x) 15 | } -------------------------------------------------------------------------------- /tests/integration/enums/enum_var_scope_error2.june: -------------------------------------------------------------------------------- 1 | // error: can't find variable 2 | 3 | enum State { 4 | StateId(i64) 5 | StatId(i64) 6 | } 7 | 8 | fun main() { 9 | let y = State::StateId(4) 10 | 11 | match y { 12 | State::StateId(x) => println(x) 13 | State::StatId(y) => println(x) 14 | } 15 | } -------------------------------------------------------------------------------- /tests/integration/enums/enum_var_scope_error3.june: -------------------------------------------------------------------------------- 1 | // error: can't find variable 2 | 3 | enum State { 4 | StateId(i64) 5 | } 6 | 7 | fun main() { 8 | let y = State::StateId(4) 9 | 10 | match y { 11 | State::StateId(x) => { 12 | let z = x; 13 | } 14 | } 15 | 16 | println(z) 17 | } -------------------------------------------------------------------------------- /tests/integration/extern_c/file_io_abstraction.june: -------------------------------------------------------------------------------- 1 | // output: abcdefgdoneclosing 2 | 3 | extern type FILE; 4 | 5 | extern "C" fun fopen(filename: c_string, mode: c_string) -> FILE? 6 | extern "C" fun fclose(file: FILE?) -> c_int; 7 | extern "C" fun fgetc(file: FILE?) -> c_int; 8 | 9 | fun cleanup(f: FILE?) { 10 | println(c"closing") 11 | unsafe { fclose(f) } 12 | } 13 | 14 | enum Output { 15 | EOF 16 | Char(c_int) 17 | } 18 | 19 | class File { 20 | file: FILE? 21 | 22 | fun open(fname: c_string) -> File { 23 | unsafe { 24 | let file = fopen(fname, c"rb") 25 | defer file cleanup 26 | 27 | return new File(file: file) 28 | } 29 | } 30 | 31 | fun get_char(mut self) -> Output { 32 | unsafe { 33 | let x = fgetc(self.file) 34 | 35 | if x == -1 { 36 | return Output::EOF 37 | } else { 38 | return Output::Char(x) 39 | } 40 | } 41 | } 42 | } 43 | 44 | fun main() { 45 | mut file = File::open(c"tests/test_data/alphabet.txt"); 46 | while true { 47 | let x = file.get_char(); 48 | 49 | match x { 50 | Output::EOF => { 51 | println(c"done") 52 | break 53 | } 54 | Output::Char(c) => { 55 | if c != 10 && c != 13 { 56 | println(c as c_char) 57 | } 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /tests/integration/extern_c/file_ptr.june: -------------------------------------------------------------------------------- 1 | // output: a 2 | 3 | extern type FILE; 4 | 5 | extern "C" fun fopen(filename: c_string, mode: c_string) -> FILE? 6 | extern "C" fun fclose(file: FILE?) -> c_int; 7 | extern "C" fun fgetc(file: FILE?) -> c_int; 8 | 9 | fun main() { 10 | unsafe { 11 | let file = fopen(c"tests/test_data/alphabet.txt", c"rb"); 12 | if file != none { 13 | let x = fgetc(file) 14 | println(x as c_char) 15 | } 16 | fclose(file) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/integration/extern_c/file_ptr_all.june: -------------------------------------------------------------------------------- 1 | // output: abcdefg 2 | 3 | extern type FILE; 4 | 5 | extern "C" fun fopen(filename: c_string, mode: c_string) -> FILE? 6 | extern "C" fun fclose(file: FILE?) -> c_int; 7 | extern "C" fun fgetc(file: FILE?) -> c_int; 8 | 9 | fun main() { 10 | unsafe { 11 | let file = fopen(c"tests/test_data/alphabet.txt", c"rb"); 12 | if file != none { 13 | mut c = fgetc(file) 14 | while c != -1 && c != 10 && c != 13 { 15 | println(c as c_char) 16 | c = fgetc(file) 17 | } 18 | fclose(file) 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/integration/extern_c/file_ptr_defer.june: -------------------------------------------------------------------------------- 1 | // output: aclosing 2 | 3 | extern type FILE; 4 | 5 | extern "C" fun fopen(filename: c_string, mode: c_string) -> FILE? 6 | extern "C" fun fclose(file: FILE?) -> c_int; 7 | extern "C" fun fgetc(file: FILE?) -> c_int; 8 | 9 | fun cleanup(f: FILE?) { 10 | println(c"closing") 11 | unsafe { fclose(f) } 12 | } 13 | 14 | fun main() { 15 | unsafe { 16 | let file = fopen(c"tests/test_data/alphabet.txt", c"rb"); 17 | defer file cleanup 18 | if file != none { 19 | let x = fgetc(file) 20 | println(x as c_char) 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/integration/extern_c/puts.june: -------------------------------------------------------------------------------- 1 | // output: hello world 2 | 3 | extern "C" fun puts(s: c_string) -> c_int 4 | 5 | unsafe { puts(c"hello world") } -------------------------------------------------------------------------------- /tests/integration/first_class_functions/first_class_function_allocation.june: -------------------------------------------------------------------------------- 1 | // output: leaving 2 | 3 | struct Foo {} 4 | 5 | fun exit_notice(f: Foo) { 6 | println(c"leaving") 7 | } 8 | 9 | fun make_foo() -> Foo { 10 | let f = new Foo() 11 | defer f exit_notice 12 | 13 | return f 14 | } 15 | 16 | fun main() { 17 | let mf = make_foo 18 | 19 | let y = mf() 20 | } -------------------------------------------------------------------------------- /tests/integration/first_class_functions/first_class_function_allocation2.june: -------------------------------------------------------------------------------- 1 | // output: leaving 2 | 3 | struct Foo {} 4 | 5 | struct Bar { 6 | f: fun() -> Foo 7 | } 8 | 9 | fun exit_notice(f: Foo) { 10 | println(c"leaving") 11 | } 12 | 13 | fun make_foo() -> Foo { 14 | let f = new Foo() 15 | defer f exit_notice 16 | 17 | return f 18 | } 19 | 20 | fun main() { 21 | let bar = new Bar(f: make_foo) 22 | 23 | let y = bar.f() 24 | } -------------------------------------------------------------------------------- /tests/integration/first_class_functions/first_class_function_arg.june: -------------------------------------------------------------------------------- 1 | // output: 98 2 | 3 | fun bar(f: fun(i64) -> i64) -> i64 { 4 | return f(88) 5 | } 6 | 7 | fun foo(x: i64) -> i64 { 8 | return x + 10 9 | } 10 | 11 | println(bar(foo)) -------------------------------------------------------------------------------- /tests/integration/first_class_functions/first_class_function_return.june: -------------------------------------------------------------------------------- 1 | // output: 76 2 | 3 | fun bar() -> fun(i64) -> i64 { 4 | return foo 5 | } 6 | 7 | fun foo(x: i64) -> i64 { 8 | return x + 10 9 | } 10 | 11 | println(bar()(66)) -------------------------------------------------------------------------------- /tests/integration/first_class_functions/first_class_function_return_error.june: -------------------------------------------------------------------------------- 1 | // error: type mismatch for arg 2 | 3 | fun bar() -> fun(i64) -> i64 { 4 | return foo 5 | } 6 | 7 | fun foo(x: i64) -> i64 { 8 | return x + 10 9 | } 10 | 11 | println(bar()(c"bob")) -------------------------------------------------------------------------------- /tests/integration/first_class_functions/first_class_function_return_error2.june: -------------------------------------------------------------------------------- 1 | // error: found: fun (i64) -> i64 expected: fun (c_string) -> c_string 2 | 3 | fun bar() -> fun(c_string) -> c_string { 4 | return foo 5 | } 6 | 7 | fun foo(x: i64) -> i64 { 8 | return x + 10 9 | } 10 | 11 | println(bar()(c"bob")) -------------------------------------------------------------------------------- /tests/integration/first_class_functions/first_class_function_value.june: -------------------------------------------------------------------------------- 1 | // output: done 2 | 3 | fun foo(x: i64) -> i64 { 4 | return x 5 | } 6 | 7 | let bar = foo 8 | 9 | println(c"done") -------------------------------------------------------------------------------- /tests/integration/first_class_functions/first_class_function_value_call.june: -------------------------------------------------------------------------------- 1 | // output: 3 2 | 3 | fun foo(x: i64) -> i64 { 4 | return x 5 | } 6 | 7 | let bar = foo 8 | 9 | println(bar(3)) -------------------------------------------------------------------------------- /tests/integration/first_class_functions/first_class_function_value_call_error.june: -------------------------------------------------------------------------------- 1 | // error: type mismatch for arg 2 | 3 | fun foo(x: i64) -> i64 { 4 | return x 5 | } 6 | 7 | let bar = foo 8 | 9 | println(bar(c"bob")) -------------------------------------------------------------------------------- /tests/integration/generics/generic_enum.june: -------------------------------------------------------------------------------- 1 | // output: 3 2 | 3 | enum Option { 4 | None 5 | Some(T) 6 | } 7 | 8 | let x = Option::Some(3) 9 | 10 | match x { 11 | Option::None => println(c"None") 12 | Option::Some(y) => println(y) 13 | } 14 | -------------------------------------------------------------------------------- /tests/integration/generics/generic_enum2.june: -------------------------------------------------------------------------------- 1 | // output: 3 2 | 3 | enum Option { 4 | Some(T) 5 | None 6 | } 7 | 8 | let x = Option::Some(3) 9 | 10 | match x { 11 | Option::Some(y) => println(y) 12 | Option::None => println(c"None") 13 | } 14 | -------------------------------------------------------------------------------- /tests/integration/generics/generic_enum_struct_case.june: -------------------------------------------------------------------------------- 1 | // output: -2 2 | 3 | enum Option { 4 | None 5 | Point { x: T, y: U } 6 | } 7 | 8 | let x = Option::Point(x: 3, y: 5) 9 | 10 | match x { 11 | Option::None => println(c"None") 12 | Option::Point(x, y) => println(x - y) 13 | } 14 | -------------------------------------------------------------------------------- /tests/integration/generics/generic_fun.june: -------------------------------------------------------------------------------- 1 | // output: 123 2 | 3 | fun id(x: T) [x == return] -> T { 4 | return x 5 | } 6 | 7 | println(id(123)) -------------------------------------------------------------------------------- /tests/integration/generics/generic_method.june: -------------------------------------------------------------------------------- 1 | // output: 100 2 | 3 | struct Foo { 4 | x: T 5 | 6 | fun get_value(self) [return == self] -> T { 7 | return self.x 8 | } 9 | } 10 | 11 | fun main() { 12 | let bar = new Foo(x: 100) 13 | 14 | println(bar.get_value()) 15 | } -------------------------------------------------------------------------------- /tests/integration/generics/generic_struct.june: -------------------------------------------------------------------------------- 1 | // output: 100 2 | 3 | struct Foo { 4 | x: T 5 | } 6 | 7 | fun main() { 8 | let bar = new Foo(x: 100) 9 | 10 | println(bar.x) 11 | } -------------------------------------------------------------------------------- /tests/integration/hello_world/hello_fun.june: -------------------------------------------------------------------------------- 1 | // output: hello 2 | 3 | fun greet() { 4 | println(c"hello") 5 | } 6 | 7 | fun main() { 8 | greet() 9 | } -------------------------------------------------------------------------------- /tests/integration/hello_world/hello_fun_rev_order.june: -------------------------------------------------------------------------------- 1 | // output: hello 2 | 3 | fun main() { 4 | greet() 5 | } 6 | 7 | fun greet() { 8 | println(c"hello") 9 | } 10 | -------------------------------------------------------------------------------- /tests/integration/hello_world/hello_fun_rev_order2.june: -------------------------------------------------------------------------------- 1 | // output: hello 2 | 3 | fun main() { 4 | greet(c"hello") 5 | } 6 | 7 | fun greet(msg: c_string) { 8 | println(msg) 9 | } 10 | -------------------------------------------------------------------------------- /tests/integration/hello_world/hello_main.june: -------------------------------------------------------------------------------- 1 | // output: hello 2 | 3 | fun main() { 4 | println(c"hello") 5 | } -------------------------------------------------------------------------------- /tests/integration/hello_world/hello_world.june: -------------------------------------------------------------------------------- 1 | // output: hello 2 | 3 | println(c"hello") -------------------------------------------------------------------------------- /tests/integration/jason.june: -------------------------------------------------------------------------------- 1 | // output: 200 2 | 3 | struct Stats { 4 | age: i64 5 | } 6 | 7 | struct Employee { 8 | stats: Stats 9 | } 10 | 11 | fun main() { 12 | mut employee = new Employee(stats: new Stats(age: 100)); 13 | let employee2 = new Employee(stats: new Stats(age: 200)); 14 | 15 | employee.stats = employee2.stats; 16 | 17 | println(employee.stats.age) 18 | } -------------------------------------------------------------------------------- /tests/integration/jason2.june: -------------------------------------------------------------------------------- 1 | struct Stats { 2 | age: i64 3 | } 4 | 5 | struct Employee { 6 | stats: Stats 7 | } 8 | 9 | fun main() { 10 | mut employee = new Employee(stats: new Stats(age: 100)); 11 | let employee2 = new Employee(stats: employee.stats); 12 | } -------------------------------------------------------------------------------- /tests/integration/jason3.june: -------------------------------------------------------------------------------- 1 | struct Stats { 2 | age: i64 3 | } 4 | 5 | struct Employee { 6 | name: c_string, 7 | stats: Stats, 8 | } 9 | 10 | fun helper(mut employee: Employee) { 11 | for x in 1..100000 { 12 | employee.stats = new Stats(age: 100) 13 | } 14 | } 15 | 16 | fun main() { 17 | helper(new Employee(name: "foo", new Stats(age: 100))) 18 | } 19 | -------------------------------------------------------------------------------- /tests/integration/jason_variant1.june: -------------------------------------------------------------------------------- 1 | struct Stats { 2 | age: i64 3 | } 4 | 5 | struct Employee { 6 | stats: Stats 7 | } 8 | 9 | fun get_employee() -> Employee { 10 | mut employee = new Employee(stats: new Stats(age: 100)); 11 | let employee2 = new Employee(stats: new Stats(age: 200)); 12 | 13 | if true { 14 | return employee 15 | } 16 | employee.stats = employee2.stats; 17 | } 18 | 19 | fun main() { 20 | let employee = get_employee() 21 | } -------------------------------------------------------------------------------- /tests/integration/lifetime_inference/alias_inference.june: -------------------------------------------------------------------------------- 1 | // output: 100 2 | 3 | struct Foo { 4 | x: i64 5 | } 6 | 7 | struct Bar { 8 | foo: Foo 9 | } 10 | 11 | fun qux() -> Foo { 12 | let foo = new Foo(x: 100) 13 | 14 | let bar = new Bar(foo: foo) 15 | 16 | return foo 17 | } 18 | 19 | fun main() { 20 | let foo = qux() 21 | 22 | println(foo.x) 23 | } -------------------------------------------------------------------------------- /tests/integration/lifetime_inference/alias_inference2.june: -------------------------------------------------------------------------------- 1 | // output: 100 2 | 3 | struct Foo { 4 | x: i64 5 | } 6 | 7 | struct Bar { 8 | foo: Foo 9 | } 10 | 11 | fun qux() -> Foo { 12 | let foo = new Foo(x: 100) 13 | 14 | let bar = new Bar(foo: foo) 15 | 16 | return bar.foo 17 | } 18 | 19 | fun main() { 20 | let foo = qux() 21 | 22 | println(foo.x) 23 | } -------------------------------------------------------------------------------- /tests/integration/lifetime_inference/alias_inference_error.june: -------------------------------------------------------------------------------- 1 | // error: compatible lifetime 2 | 3 | struct Foo { 4 | x: i64 5 | } 6 | 7 | struct Bar { 8 | foo: Foo 9 | } 10 | 11 | fun qux(mut bar: Bar) -> Foo { 12 | let foo = new Foo(x: 100) 13 | 14 | bar.foo = foo 15 | 16 | return foo 17 | } 18 | 19 | fun main() { 20 | mut bar = new Bar(foo: new Foo(x: 88)) 21 | 22 | let foo = qux(bar) 23 | 24 | println(foo.x) 25 | } -------------------------------------------------------------------------------- /tests/integration/lifetime_inference/alias_return_error.june: -------------------------------------------------------------------------------- 1 | // error: can't find compatible lifetime 2 | 3 | struct Foo { 4 | x: i64 5 | } 6 | 7 | struct Bar { 8 | foo: Foo 9 | } 10 | 11 | fun return_alias(bar: Bar) -> Foo { 12 | let foo = bar.foo 13 | 14 | return foo 15 | } 16 | 17 | fun main() { 18 | let xyz = new Bar(foo: new Foo(x: 55)); 19 | 20 | let abc = return_alias(bar: xyz) 21 | 22 | println(abc.x) 23 | } -------------------------------------------------------------------------------- /tests/integration/lifetime_inference/annotation_param_compatible.june: -------------------------------------------------------------------------------- 1 | // output: 44 2 | 3 | struct Stats { 4 | age: i64 5 | } 6 | 7 | struct Person { 8 | stats: Stats 9 | } 10 | 11 | fun assign(mut person: Person, stats: Stats) [stats == person] { 12 | person.stats = stats 13 | } 14 | 15 | mut person = new Person(stats: new Stats(age: 33)) 16 | 17 | assign(person, new Stats(age: 44)) 18 | 19 | println(person.stats.age) -------------------------------------------------------------------------------- /tests/integration/lifetime_inference/annotation_return_compatible.june: -------------------------------------------------------------------------------- 1 | // output: 88 2 | 3 | struct Stats { 4 | age: i64 5 | } 6 | 7 | fun identity(x: Stats) [x == return] -> Stats { 8 | return x 9 | } 10 | 11 | println(identity(new Stats(age: 88)).age) -------------------------------------------------------------------------------- /tests/integration/lifetime_inference/annotation_return_compatible2.june: -------------------------------------------------------------------------------- 1 | // output: 77 2 | 3 | struct Stats { 4 | age: i64 5 | } 6 | 7 | fun helper(y: Stats) [y == return] -> Stats { 8 | return identity(y) 9 | } 10 | 11 | fun identity(x: Stats) [x == return] -> Stats { 12 | return x 13 | } 14 | 15 | println(helper(new Stats(age: 77)).age) -------------------------------------------------------------------------------- /tests/integration/lifetime_inference/cleanup_on_return.june: -------------------------------------------------------------------------------- 1 | // output: done 2 | 3 | struct Foo {} 4 | 5 | fun echo() { 6 | println(c"done") 7 | } 8 | 9 | fun bar() -> i64 { 10 | let x = new Foo() 11 | defer x echo 12 | 13 | if true { 14 | return 10 15 | } 16 | 17 | return 3 18 | } 19 | 20 | bar() 21 | -------------------------------------------------------------------------------- /tests/integration/lifetime_inference/cleanup_on_return2.june: -------------------------------------------------------------------------------- 1 | // output: goodbyeecho 2 | 3 | struct Foo {} 4 | 5 | fun echo() { 6 | println(c"echo") 7 | } 8 | 9 | fun goodbye() { 10 | println(c"goodbye") 11 | } 12 | 13 | fun bar() -> i64 { 14 | let x = new Foo() 15 | defer x echo 16 | 17 | if true { 18 | defer x goodbye 19 | 20 | if true { 21 | return 10 22 | } 23 | } 24 | 25 | return 3 26 | } 27 | 28 | bar() 29 | -------------------------------------------------------------------------------- /tests/integration/lifetime_inference/escaping_local_alt_error.june: -------------------------------------------------------------------------------- 1 | // error: allocation is not local, lifetime inferred to be return 2 | 3 | struct Stats { 4 | age: i64 5 | } 6 | 7 | fun foo() -> Stats { 8 | return new(local) Stats(age: 100) 9 | } 10 | 11 | fun main() { 12 | println(foo().age) 13 | } -------------------------------------------------------------------------------- /tests/integration/lifetime_inference/escaping_local_error.june: -------------------------------------------------------------------------------- 1 | // error: allocation is not local, lifetime inferred to be return 2 | 3 | struct Stats { 4 | age: i64 5 | } 6 | 7 | fun foo() -> Stats { 8 | return local Stats(age: 100) 9 | } 10 | 11 | fun main() { 12 | println(foo().age) 13 | } -------------------------------------------------------------------------------- /tests/integration/lifetime_inference/escaping_local_error2.june: -------------------------------------------------------------------------------- 1 | // error: allocation is not local, lifetime inferred to be param 'employee' 2 | 3 | struct Stats { 4 | age: i64 5 | } 6 | 7 | struct Employee { 8 | name: c_string, 9 | stats: Stats, 10 | } 11 | 12 | fun set_stats(mut employee: Employee) { 13 | employee.stats = local Stats(age: 33) 14 | } 15 | 16 | fun main() { 17 | mut employee = new Employee(name: c"Sophia", stats: new Stats(age: 22)) 18 | 19 | set_stats(employee) 20 | 21 | println(employee.stats.age) 22 | } -------------------------------------------------------------------------------- /tests/integration/lifetime_inference/incompatible_param_error.june: -------------------------------------------------------------------------------- 1 | // error: can't find compatible lifetime between param 'stats' and param 'person' 2 | 3 | struct Stats { 4 | age: i64 5 | } 6 | 7 | struct Person { 8 | stats: Stats 9 | } 10 | 11 | fun assign(mut person: Person, stats: Stats) { 12 | person.stats = stats 13 | } 14 | 15 | mut person = new Person(stats: new Stats(age: 33)) 16 | 17 | assign(person, new Stats(age: 44)) -------------------------------------------------------------------------------- /tests/integration/lifetime_inference/incompatible_param_error2.june: -------------------------------------------------------------------------------- 1 | // error: can't find compatible lifetime between param 'stats' and param 'person' 2 | 3 | struct Stats { 4 | age: i64 5 | } 6 | 7 | struct Person { 8 | stats: Stats 9 | } 10 | 11 | fun helper(mut person: Person, stats: Stats) { 12 | assign(person, stats) 13 | } 14 | 15 | fun assign(mut person: Person, stats: Stats) [stats == person] { 16 | person.stats = stats 17 | } 18 | 19 | mut person = new Person(stats: new Stats(age: 33)) 20 | 21 | helper(person, new Stats(age: 44)) -------------------------------------------------------------------------------- /tests/integration/lifetime_inference/lifetime_alloc_infer_error.june: -------------------------------------------------------------------------------- 1 | // error: is not an allocator 2 | 3 | struct Stats { 4 | age: i64 5 | } 6 | 7 | struct noalloc Employee { 8 | name: c_string, 9 | stats: Stats, 10 | } 11 | 12 | fun helper(mut employee: Employee) { 13 | employee.stats = new Stats(age: 33) 14 | } 15 | 16 | fun main() { 17 | mut employee = new Employee(name: c"Sophia", stats: new Stats(age: 22)) 18 | 19 | helper(employee) 20 | 21 | println(employee.stats.age) 22 | } -------------------------------------------------------------------------------- /tests/integration/lifetime_inference/lifetime_alloc_infer_success.june: -------------------------------------------------------------------------------- 1 | // output: 33 2 | 3 | struct Stats { 4 | age: i64 5 | } 6 | 7 | struct Employee { 8 | name: c_string, 9 | stats: Stats, 10 | } 11 | 12 | fun set_stats(mut employee: Employee) { 13 | employee.stats = new Stats(age: 33) 14 | } 15 | 16 | fun main() { 17 | mut employee = new Employee(name: c"Sophia", stats: new Stats(age: 22)) 18 | 19 | set_stats(employee) 20 | 21 | println(employee.stats.age) 22 | } -------------------------------------------------------------------------------- /tests/integration/lifetime_inference/lifetime_error.june: -------------------------------------------------------------------------------- 1 | // error: can't find compatible lifetime between param 'stats' and param 'employee' 2 | 3 | struct Stats { 4 | age: i64 5 | } 6 | 7 | struct Employee { 8 | name: c_string, 9 | stats: Stats, 10 | } 11 | 12 | fun helper(mut employee: Employee, stats: Stats) { 13 | employee.stats = stats 14 | } 15 | 16 | fun main() { 17 | mut employee = new Employee(name: c"Sophia", stats: new Stats(age: 22)) 18 | 19 | helper(employee, new Stats(age: 33)) 20 | 21 | println(employee.stats.age) 22 | } -------------------------------------------------------------------------------- /tests/integration/lifetime_inference/lifetime_error2.june: -------------------------------------------------------------------------------- 1 | // error: can't find compatible lifetime between param 'stats' and param 'employee' 2 | 3 | struct Stats { 4 | age: i64 5 | } 6 | 7 | struct Employee { 8 | name: c_string, 9 | stats: Stats, 10 | } 11 | 12 | fun helper(mut employee: Employee) { 13 | return helper2(employee, new Stats(age: 33)) 14 | } 15 | 16 | fun helper2(mut employee: Employee, stats: Stats) { 17 | employee.stats = stats 18 | } 19 | 20 | fun main() { 21 | mut employee = new Employee(name: c"Sophia", stats: new Stats(age: 22)) 22 | 23 | helper(employee) 24 | 25 | println(employee.stats.age) 26 | } -------------------------------------------------------------------------------- /tests/integration/lifetime_inference/lifetime_infer_helper_function.june: -------------------------------------------------------------------------------- 1 | // output: 33 2 | 3 | struct Stats { 4 | age: i64 5 | } 6 | 7 | struct Employee { 8 | name: c_string, 9 | stats: Stats, 10 | } 11 | 12 | fun helper() -> Stats { 13 | return new Stats(age: 33) 14 | } 15 | 16 | fun main() { 17 | mut employee = new Employee(name: c"Sophia", stats: new Stats(age: 22)) 18 | 19 | employee.stats = helper() 20 | 21 | println(employee.stats.age) 22 | } -------------------------------------------------------------------------------- /tests/integration/lifetime_inference/lifetime_infer_helper_function2.june: -------------------------------------------------------------------------------- 1 | // output: 33 2 | 3 | struct Stats { 4 | age: i64 5 | } 6 | 7 | struct Employee { 8 | name: c_string, 9 | stats: Stats, 10 | } 11 | 12 | fun create_stats() -> Stats { 13 | return new Stats(age: 33) 14 | } 15 | 16 | fun helper(mut employee: Employee) { 17 | employee.stats = create_stats() 18 | } 19 | 20 | fun main() { 21 | mut employee = new Employee(name: c"Sophia", stats: new Stats(age: 22)) 22 | 23 | helper(employee) 24 | 25 | println(employee.stats.age) 26 | } -------------------------------------------------------------------------------- /tests/integration/lifetime_inference/lifetime_with_constraints.june: -------------------------------------------------------------------------------- 1 | // output: 33 2 | 3 | struct Stats { 4 | age: i64 5 | } 6 | 7 | struct Employee { 8 | name: c_string, 9 | stats: Stats, 10 | } 11 | 12 | fun helper(mut employee: Employee, stats: Stats) [employee == stats] { 13 | employee.stats = stats 14 | } 15 | 16 | fun main() { 17 | mut employee = new Employee(name: c"Sophia", stats: new Stats(age: 22)) 18 | 19 | helper(employee, new Stats(age: 33)) 20 | 21 | println(employee.stats.age) 22 | } 23 | -------------------------------------------------------------------------------- /tests/integration/lifetime_inference/passthrough_error.june: -------------------------------------------------------------------------------- 1 | // error: can't find compatible lifetime between param 'x' and return 2 | 3 | struct Stats { 4 | age: i64 5 | } 6 | 7 | fun identity(x: Stats) -> Stats { 8 | return x 9 | } 10 | 11 | identity(new Stats(age: 88)) -------------------------------------------------------------------------------- /tests/integration/lifetime_inference/passthrough_error2.june: -------------------------------------------------------------------------------- 1 | // error: can't find compatible lifetime between param 'x' and return 2 | 3 | struct Stats { 4 | age: i64 5 | } 6 | 7 | fun helper(x: Stats) -> Stats { 8 | return identity(x) 9 | } 10 | 11 | fun identity(x: Stats) [x == return] -> Stats { 12 | return x 13 | } 14 | 15 | helper(new Stats(age: 88)) -------------------------------------------------------------------------------- /tests/integration/lifetime_inference/scope_struct.june: -------------------------------------------------------------------------------- 1 | // output: 123 2 | 3 | struct Employee { 4 | name: c_string, 5 | age: i64, 6 | } 7 | 8 | fun main() { 9 | if true { 10 | let employee = new Employee(name: c"Bob", age: 123) 11 | println(employee.age) 12 | } 13 | } -------------------------------------------------------------------------------- /tests/integration/lifetime_inference/scope_struct_to_upper_scope.june: -------------------------------------------------------------------------------- 1 | // output: 456 2 | 3 | struct Employee { 4 | name: c_string, 5 | age: i64, 6 | } 7 | 8 | fun main() { 9 | mut employee = new Employee(name: c"Bob", age: 123) 10 | if true { 11 | let employee2 = new Employee(name: c"Sam", age: 456) 12 | employee = employee2 13 | } 14 | println(employee.age) 15 | } -------------------------------------------------------------------------------- /tests/integration/lifetime_inference/scope_struct_to_upper_scope2.june: -------------------------------------------------------------------------------- 1 | // output: 123 2 | 3 | struct Employee { 4 | name: c_string, 5 | age: i64, 6 | } 7 | 8 | fun main() { 9 | mut employee = new Employee(name: c"Bob", age: 123) 10 | if true { 11 | let employee2 = new Employee(name: c"Sam", age: 456) 12 | } 13 | println(employee.age) 14 | } -------------------------------------------------------------------------------- /tests/integration/math/bitwise.june: -------------------------------------------------------------------------------- 1 | // output: 1613 2 | 3 | println(3 >> 1) 4 | println(3 << 1) 5 | println(3 & 1) 6 | println(3 | 2) -------------------------------------------------------------------------------- /tests/integration/math/int_math.june: -------------------------------------------------------------------------------- 1 | // output: 22 2 | 3 | println(2 + 4 * 5) -------------------------------------------------------------------------------- /tests/integration/math/int_math_main.june: -------------------------------------------------------------------------------- 1 | // output: 22 2 | 3 | fun main() { 4 | println(2 + 4 * 5) 5 | } -------------------------------------------------------------------------------- /tests/integration/math/int_math_negative.june: -------------------------------------------------------------------------------- 1 | // output: -18 2 | 3 | println(2 + 4 * -5) -------------------------------------------------------------------------------- /tests/integration/math/math_in_brackets.june: -------------------------------------------------------------------------------- 1 | // output: 23 2 | mut data = raw[1,2,3] 3 | unsafe { 4 | println(data[1]) 5 | data[0 + 1] = 3 6 | println(data[1]) 7 | } 8 | -------------------------------------------------------------------------------- /tests/integration/modules/collisions.june: -------------------------------------------------------------------------------- 1 | // output: 123123 2 | // RUST_LOG: june::parser[use_statement]=debug,june::typechecker[typecheck_namespaced_lookup]=debug,june::typechecker[typecheck_module]=debug 3 | 4 | fun say_message(message: i64) { 5 | println(message) 6 | } 7 | 8 | use utils2; 9 | 10 | 11 | fun main() { 12 | let message = 123 13 | say_message(message) 14 | utils2::say_message(message) 15 | } 16 | -------------------------------------------------------------------------------- /tests/integration/modules/main.june: -------------------------------------------------------------------------------- 1 | // output: hello! 2 | // RUST_LOG: june::parser[use_statement]=debug,june::typechecker[typecheck_namespaced_lookup]=debug,june::typechecker[typecheck_module]=debug 3 | 4 | use utils; 5 | 6 | fun main() { 7 | utils::say_hello() 8 | } 9 | -------------------------------------------------------------------------------- /tests/integration/modules/utils.june: -------------------------------------------------------------------------------- 1 | fun say_hello() { 2 | println(c"hello!") 3 | } 4 | -------------------------------------------------------------------------------- /tests/integration/modules/utils2.june: -------------------------------------------------------------------------------- 1 | fun say_message(message: i64) { 2 | println(message) 3 | } 4 | -------------------------------------------------------------------------------- /tests/integration/owned_abstractions/moved_owned_var_error.june: -------------------------------------------------------------------------------- 1 | // error: moved variable accessed after move 2 | 3 | struct Foo { 4 | z: i64 5 | } 6 | 7 | let foo = new owned Foo(z: 100) 8 | 9 | let y = foo 10 | 11 | println(foo.z) -------------------------------------------------------------------------------- /tests/integration/owned_abstractions/moved_owned_var_error2.june: -------------------------------------------------------------------------------- 1 | // error: moved variable accessed after move 2 | 3 | struct Foo { 4 | z: i64 5 | } 6 | 7 | fun bar(abc: Foo) { 8 | 9 | } 10 | 11 | let foo = new owned Foo(z: 100) 12 | 13 | bar(foo) 14 | 15 | println(foo.z) -------------------------------------------------------------------------------- /tests/integration/owned_abstractions/moved_owned_var_error3.june: -------------------------------------------------------------------------------- 1 | // error: moved variable accessed after move 2 | 3 | struct Foo { 4 | z: i64 5 | 6 | fun bar(self, abc: Foo) { 7 | 8 | } 9 | } 10 | 11 | 12 | let foo = new owned Foo(z: 100) 13 | 14 | foo.bar(foo) 15 | 16 | println(foo.z) -------------------------------------------------------------------------------- /tests/integration/owned_abstractions/moved_owned_var_into_shared_error.june: -------------------------------------------------------------------------------- 1 | // error: moved variable accessed after move 2 | 3 | struct Foo { 4 | z: i64 5 | } 6 | 7 | let foo = new owned Foo(z: 100) 8 | 9 | let y: Foo = foo 10 | 11 | println(foo.z) -------------------------------------------------------------------------------- /tests/integration/owned_abstractions/multiple_method_calls_on_self.june: -------------------------------------------------------------------------------- 1 | // output: 88 2 | 3 | struct Foo { 4 | x: i64 5 | y: i64 6 | 7 | fun set_x(mut self, new_x: i64) { 8 | .x = new_x 9 | } 10 | 11 | fun set_y(mut self, new_y: i64) { 12 | .y = new_y 13 | } 14 | 15 | fun create() -> owned Foo { 16 | return new owned Foo(x: 0, y: 0) 17 | } 18 | } 19 | 20 | mut abc = Foo::create() 21 | abc.set_x(88) 22 | abc.set_y(99) 23 | 24 | println(abc.x) 25 | -------------------------------------------------------------------------------- /tests/integration/owned_abstractions/owned_method_doesnt_permanently_move.june: -------------------------------------------------------------------------------- 1 | // output: 100 2 | 3 | struct Foo { 4 | z: i64 5 | 6 | fun bar(self) { 7 | 8 | } 9 | } 10 | 11 | let foo = new owned Foo(z: 100) 12 | 13 | foo.bar() 14 | 15 | println(foo.z) -------------------------------------------------------------------------------- /tests/integration/owned_abstractions/owned_struct.june: -------------------------------------------------------------------------------- 1 | // output: 88 2 | 3 | struct Foo { 4 | x: i64 5 | } 6 | 7 | fun main() { 8 | let foo = new owned Foo(x: 88) 9 | 10 | println(foo.x) 11 | } -------------------------------------------------------------------------------- /tests/integration/owned_abstractions/owned_struct_error.june: -------------------------------------------------------------------------------- 1 | // error: public field is a shared pointer 2 | 3 | struct Stats { 4 | age: i64 5 | } 6 | 7 | struct Foo { 8 | stats: Stats 9 | } 10 | 11 | fun main() { 12 | let foo = new owned Foo(stats: new Stats(age: 88)) 13 | } -------------------------------------------------------------------------------- /tests/integration/owned_abstractions/owned_struct_error2.june: -------------------------------------------------------------------------------- 1 | // error: param is a shared pointer, and self is mutable 2 | 3 | struct Stats { 4 | age: i64 5 | } 6 | 7 | struct Foo { 8 | private stats: Stats 9 | 10 | fun create(age: i64) -> owned Foo { 11 | return new owned Foo(stats: new Stats(age: age)) 12 | } 13 | 14 | fun set_stats(mut self, stats: Stats) { 15 | .stats = stats 16 | } 17 | } 18 | 19 | fun main() { 20 | let foo = Foo::create(age: 88) 21 | } -------------------------------------------------------------------------------- /tests/integration/owned_abstractions/owned_struct_error3.june: -------------------------------------------------------------------------------- 1 | // error: return type is a shared pointer 2 | 3 | struct Stats { 4 | age: i64 5 | } 6 | 7 | struct Foo { 8 | private stats: Stats 9 | 10 | fun create(age: i64) -> owned Foo { 11 | return new owned Foo(stats: new Stats(age: age)) 12 | } 13 | 14 | fun get_stats(self) -> Stats { 15 | return .stats 16 | } 17 | } 18 | 19 | fun main() { 20 | let foo = Foo::create(age: 88) 21 | } -------------------------------------------------------------------------------- /tests/integration/owned_abstractions/owned_struct_private.june: -------------------------------------------------------------------------------- 1 | // output: 88 2 | 3 | struct Stats { 4 | age: i64 5 | } 6 | 7 | struct Foo { 8 | private stats: Stats 9 | 10 | fun create(age: i64) -> owned Foo { 11 | return new owned Foo(stats: new Stats(age: age)) 12 | } 13 | 14 | fun print_age(self) { 15 | println(.stats.age) 16 | } 17 | } 18 | 19 | fun main() { 20 | let foo = Foo::create(age: 88) 21 | 22 | foo.print_age() 23 | } -------------------------------------------------------------------------------- /tests/integration/owned_abstractions/owned_struct_private2.june: -------------------------------------------------------------------------------- 1 | // output: true 2 | 3 | struct Stats { 4 | age: i64 5 | } 6 | 7 | struct Foo { 8 | private stats: Stats 9 | 10 | fun create(age: i64) -> owned Foo { 11 | return new owned Foo(stats: new Stats(age: age)) 12 | } 13 | 14 | fun same_age(self, stats: Stats) -> bool { 15 | return .stats.age == stats.age 16 | } 17 | } 18 | 19 | fun main() { 20 | let foo = Foo::create(age: 88) 21 | 22 | println(foo.same_age(new Stats(age: 88))) 23 | } -------------------------------------------------------------------------------- /tests/integration/parsing/bad_condition.june: -------------------------------------------------------------------------------- 1 | // error: assignment found in expression 2 | 3 | let x = 0 4 | 5 | if x = 1 { 6 | println("true") 7 | } else { 8 | println("false") 9 | } -------------------------------------------------------------------------------- /tests/integration/parsing/unicode.june: -------------------------------------------------------------------------------- 1 | // output: 123 2 | 3 | let 🦈 = 123 4 | 5 | println(🦈) -------------------------------------------------------------------------------- /tests/integration/parsing/unicode2.june: -------------------------------------------------------------------------------- 1 | // output: 3.140000 2 | 3 | struct 🦈 { 4 | 🥧:f64 5 | } 6 | 7 | let shark = new 🦈(🥧: 3.14) 8 | 9 | println(shark.🥧) -------------------------------------------------------------------------------- /tests/integration/parsing/unicode3.june: -------------------------------------------------------------------------------- 1 | // output: 68 2 | 3 | enum 🦈{ 4 | 🥧 5 | 🍇(i64) 6 | 🍉 { 🍍: i64 } 7 | } 8 | 9 | let a = 🦈::🥧 10 | let b = 🦈::🍇(34) 11 | let c = 🦈::🍉(🍍: 68) 12 | 13 | match c { 14 | 🦈::🥧 => println(c"pie") 15 | 🦈::🍇(x) => println(x) 16 | 🦈::🍉(🍍) => println(🍍) 17 | } 18 | 19 | -------------------------------------------------------------------------------- /tests/integration/raw_buffers/raw_buffer_argument.june: -------------------------------------------------------------------------------- 1 | // output: world 2 | 3 | fun greet(greetings: raw[c_string]) { 4 | unsafe { println(greetings[1]) } 5 | } 6 | 7 | greet(raw[c"hello", c"world"]) -------------------------------------------------------------------------------- /tests/integration/raw_buffers/raw_buffer_assignment.june: -------------------------------------------------------------------------------- 1 | // output: 5 2 | 3 | mut buffer = raw[1, 2, 3] 4 | unsafe { 5 | buffer[1] = 5 6 | println(buffer[1]) 7 | } 8 | -------------------------------------------------------------------------------- /tests/integration/raw_buffers/raw_buffer_assignment_error.june: -------------------------------------------------------------------------------- 1 | // error: variable is not mutable 2 | 3 | let buffer = raw[1, 2, 3] 4 | buffer[1] = 5 5 | println(buffer[1]) -------------------------------------------------------------------------------- /tests/integration/raw_buffers/raw_buffer_defer.june: -------------------------------------------------------------------------------- 1 | // output: hellogoodbye 2 | 3 | fun goodbye(g: raw[i64]) { 4 | println(c"goodbye") 5 | } 6 | 7 | let x = raw[1, 2, 3] 8 | defer x goodbye 9 | println(c"hello") -------------------------------------------------------------------------------- /tests/integration/raw_buffers/raw_buffer_resize.june: -------------------------------------------------------------------------------- 1 | // output: 100 2 | 3 | mut x = raw[1] 4 | 5 | unsafe { 6 | resize x 2000 7 | x[1999] = 100 8 | println(x[1999]) 9 | } 10 | -------------------------------------------------------------------------------- /tests/integration/raw_buffers/raw_buffer_return.june: -------------------------------------------------------------------------------- 1 | // output: 20 2 | 3 | fun create_buffer() -> raw[i64] { 4 | return raw[10, 20, 30] 5 | } 6 | 7 | let buffer = create_buffer() 8 | unsafe { println(buffer[1]) } -------------------------------------------------------------------------------- /tests/integration/raw_buffers/raw_buffer_simple.june: -------------------------------------------------------------------------------- 1 | // output: 2 2 | 3 | let buffer = raw[1, 2, 3] 4 | unsafe { println(buffer[1]) } -------------------------------------------------------------------------------- /tests/integration/raw_buffers/raw_buffer_with_pointers.june: -------------------------------------------------------------------------------- 1 | // output: 11 2 | 3 | struct Person { 4 | height: i64 5 | } 6 | 7 | let a = raw[new Person(height: 11), new Person(height: 12)] 8 | 9 | unsafe { println(a[0].height) } -------------------------------------------------------------------------------- /tests/integration/structs/method_and_struct_allocator.june: -------------------------------------------------------------------------------- 1 | // output: 99 2 | 3 | struct Stat { 4 | width: i64 5 | } 6 | 7 | struct Box { 8 | stat: Stat 9 | 10 | fun recreate_stat(mut self) { 11 | .stat = new Stat(width: 99) 12 | } 13 | } 14 | 15 | fun main() { 16 | mut box = new Box(stat: new Stat(width: 22)) 17 | box.recreate_stat() 18 | 19 | println(box.stat.width) 20 | } -------------------------------------------------------------------------------- /tests/integration/structs/method_immutable_self_error.june: -------------------------------------------------------------------------------- 1 | // error: 'self' variable is not mutable 2 | 3 | struct Box { 4 | width: i64 5 | 6 | fun grow_width(self) { 7 | .width = 99 8 | } 9 | } 10 | 11 | fun main() { 12 | let box = new Box(width: 22) 13 | box.grow_width() 14 | 15 | println(box.width) 16 | } -------------------------------------------------------------------------------- /tests/integration/structs/method_in_method.june: -------------------------------------------------------------------------------- 1 | // output: 25 2 | 3 | struct Box { 4 | width: i64 5 | 6 | fun get_width(self) -> i64 { 7 | return .width 8 | } 9 | 10 | fun get_area(self) -> i64 { 11 | return .get_width() * .get_width() 12 | } 13 | } 14 | 15 | fun main() { 16 | let box = new Box(width: 5) 17 | 18 | println(box.get_area()) 19 | } -------------------------------------------------------------------------------- /tests/integration/structs/method_mutation.june: -------------------------------------------------------------------------------- 1 | // output: 15 2 | 3 | struct Box { 4 | width: i64 5 | 6 | fun grow_width(mut self, amount: i64) { 7 | .width += amount 8 | } 9 | } 10 | 11 | fun main() { 12 | mut box = new Box(width: 10) 13 | box.grow_width(5) 14 | 15 | println(box.width) 16 | } -------------------------------------------------------------------------------- /tests/integration/structs/method_simple.june: -------------------------------------------------------------------------------- 1 | // output: 100 2 | 3 | struct Box { 4 | width: i64 5 | 6 | fun get_area(self) -> i64 { 7 | return .width * .width 8 | } 9 | } 10 | 11 | fun main() { 12 | let box = new Box(width: 10) 13 | 14 | println(box.get_area()) 15 | } -------------------------------------------------------------------------------- /tests/integration/structs/method_simple_error.june: -------------------------------------------------------------------------------- 1 | // error: can't find 'self' variable 2 | 3 | struct Box { 4 | width: i64 5 | 6 | fun get_area() -> i64 { 7 | return .width * .width 8 | } 9 | } 10 | 11 | fun main() { 12 | let box = new Box(width: 10) 13 | 14 | println(box.get_area()) 15 | } -------------------------------------------------------------------------------- /tests/integration/structs/static_method.june: -------------------------------------------------------------------------------- 1 | // output: 100 2 | 3 | struct Foo { 4 | x: i64 5 | 6 | fun default() -> Foo { 7 | return new Foo(x: 100) 8 | } 9 | } 10 | 11 | fun main() { 12 | let default = Foo::default() 13 | 14 | println(default.x) 15 | } -------------------------------------------------------------------------------- /tests/integration/structs/struct.june: -------------------------------------------------------------------------------- 1 | // output: 123 2 | 3 | struct Employee { 4 | name: c_string, 5 | age: i64, 6 | } 7 | 8 | fun main() { 9 | let employee = new Employee(name: c"Bob", age: 123) 10 | println(employee.age) 11 | } -------------------------------------------------------------------------------- /tests/integration/structs/struct_allocator.june: -------------------------------------------------------------------------------- 1 | // output: 10 2 | 3 | struct Box { 4 | width: i64 5 | } 6 | 7 | fun main() { 8 | let box = new Box(width: 10) 9 | 10 | println(box.width) 11 | } -------------------------------------------------------------------------------- /tests/integration/structs/struct_arg_count_error.june: -------------------------------------------------------------------------------- 1 | // error: expected: 2, found: 1 2 | 3 | struct Employee { 4 | name: c_string, 5 | age: i64, 6 | } 7 | 8 | fun main() { 9 | let employee = new Employee(name: "Bob") 10 | println(employee.age) 11 | } -------------------------------------------------------------------------------- /tests/integration/structs/struct_field.june: -------------------------------------------------------------------------------- 1 | // output: Bob 2 | 3 | struct Employee { 4 | name: c_string, 5 | age: i64, 6 | } 7 | 8 | fun main() { 9 | let employee = new Employee(name: c"Bob", age: 123) 10 | println(employee.name) 11 | } -------------------------------------------------------------------------------- /tests/integration/structs/struct_field_access_caller.june: -------------------------------------------------------------------------------- 1 | // output: 22 2 | 3 | struct Stats { 4 | age: i64 5 | } 6 | 7 | struct Employee { 8 | name: c_string, 9 | stats: Stats, 10 | } 11 | 12 | fun helper() -> Stats { 13 | let employee = new Employee(name: c"Sally", stats: new Stats(age: 22)) 14 | 15 | return employee.stats 16 | } 17 | 18 | fun main() { 19 | println(helper().age) 20 | } -------------------------------------------------------------------------------- /tests/integration/structs/struct_field_access_caller2.june: -------------------------------------------------------------------------------- 1 | // output: 22 2 | 3 | struct Stats { 4 | age: i64 5 | } 6 | 7 | struct Employee { 8 | name: c_string, 9 | stats: Stats, 10 | } 11 | 12 | fun helper() -> Stats { 13 | let stats = new Stats(age: 22) 14 | let employee = new Employee(name: c"Sally", stats: stats) 15 | 16 | return employee.stats 17 | } 18 | 19 | fun main() { 20 | println(helper().age) 21 | } -------------------------------------------------------------------------------- /tests/integration/structs/struct_field_access_locally.june: -------------------------------------------------------------------------------- 1 | // output: 123 2 | 3 | struct Employee { 4 | name: c_string, 5 | age: i64, 6 | } 7 | 8 | fun helper() -> i64 { 9 | let employee = new Employee(name: c"Bob", age: 123) 10 | 11 | return employee.age 12 | } 13 | 14 | fun main() { 15 | println(helper()) 16 | } -------------------------------------------------------------------------------- /tests/integration/structs/struct_field_deep.june: -------------------------------------------------------------------------------- 1 | // output: 123 2 | 3 | struct Stats { 4 | age: i64 5 | } 6 | 7 | struct Person { 8 | stats: Stats 9 | } 10 | 11 | mut foo = new Person(stats: new Stats(age: 12)) 12 | 13 | foo.stats.age = 123 14 | 15 | println(foo.stats.age) -------------------------------------------------------------------------------- /tests/integration/structs/struct_field_helper.june: -------------------------------------------------------------------------------- 1 | // output: 123 2 | 3 | struct Employee { 4 | name: c_string, 5 | age: i64, 6 | } 7 | 8 | fun get_field(employee: Employee) -> i64 { 9 | return employee.age 10 | } 11 | 12 | fun main() { 13 | let employee = new Employee(name: c"Bob", age: 123) 14 | 15 | let age = get_field(employee) 16 | 17 | println(age) 18 | } -------------------------------------------------------------------------------- /tests/integration/structs/struct_field_math.june: -------------------------------------------------------------------------------- 1 | // output: 405 2 | 3 | struct Employee { 4 | name: c_string, 5 | age: i64, 6 | } 7 | 8 | fun main() { 9 | let employee = new Employee(name: c"Bob", age: 123) 10 | println(employee.age * 3 + 4 * 9) 11 | } -------------------------------------------------------------------------------- /tests/integration/structs/struct_field_math_mainless.june: -------------------------------------------------------------------------------- 1 | // output: 405 2 | 3 | struct Employee { 4 | name: c_string, 5 | age: i64, 6 | } 7 | 8 | let employee = new Employee(name: c"Bob", age: 123) 9 | println(employee.age * 3 + 4 * 9) 10 | -------------------------------------------------------------------------------- /tests/integration/structs/struct_field_mutable_param.june: -------------------------------------------------------------------------------- 1 | // output: 33 2 | 3 | struct Employee { 4 | name: c_string, 5 | age: i64, 6 | } 7 | 8 | fun helper(mut employee: Employee) { 9 | employee.age = 33 10 | } 11 | 12 | fun main() { 13 | mut employee = new Employee(name: c"Sophia", age: 22) 14 | 15 | helper(employee) 16 | 17 | println(employee.age) 18 | } -------------------------------------------------------------------------------- /tests/integration/structs/struct_field_private.june: -------------------------------------------------------------------------------- 1 | // output: 100 2 | 3 | struct Stats { 4 | private age: i64 5 | 6 | fun print_age(self) { 7 | println(.age) 8 | } 9 | 10 | fun create(age: i64) -> Stats { 11 | return new Stats(age: age) 12 | } 13 | } 14 | 15 | fun main() { 16 | let stats = Stats::create(100) 17 | 18 | stats.print_age() 19 | } -------------------------------------------------------------------------------- /tests/integration/structs/struct_field_private2.june: -------------------------------------------------------------------------------- 1 | // output: 77 2 | 3 | struct Stats { 4 | private age: i64 5 | 6 | fun set_age(mut self, new_age: i64) { 7 | .age = new_age 8 | } 9 | 10 | fun print_age(self) { 11 | println(.age) 12 | } 13 | 14 | fun create(age: i64) -> Stats { 15 | return new Stats(age: age) 16 | } 17 | } 18 | 19 | fun main() { 20 | mut stats = Stats::create(age: 88) 21 | 22 | stats.set_age(77) 23 | 24 | stats.print_age() 25 | } -------------------------------------------------------------------------------- /tests/integration/structs/struct_field_private3.june: -------------------------------------------------------------------------------- 1 | // output: 88 2 | 3 | struct Stats { 4 | private age: i64 5 | 6 | fun get_age(self) -> i64 { 7 | return .age 8 | } 9 | 10 | fun create(age: i64) -> Stats { 11 | return new Stats(age: age) 12 | } 13 | } 14 | 15 | fun main() { 16 | let stats = Stats::create(age: 88) 17 | 18 | println(stats.get_age()) 19 | } -------------------------------------------------------------------------------- /tests/integration/structs/struct_field_private_error.june: -------------------------------------------------------------------------------- 1 | // error: access of private field 2 | 3 | struct Stats { 4 | private age: i64 5 | 6 | fun create(age: i64) -> Stats { 7 | return new Stats(age: age) 8 | } 9 | } 10 | 11 | fun main() { 12 | let stats = Stats::create(age: 88) 13 | 14 | println(stats.age) 15 | } -------------------------------------------------------------------------------- /tests/integration/structs/struct_field_private_error2.june: -------------------------------------------------------------------------------- 1 | // error: modifying private member field 2 | 3 | struct Stats { 4 | private age: i64 5 | 6 | fun create(age: i64) -> Stats { 7 | return new Stats(age: age) 8 | } 9 | } 10 | 11 | fun main() { 12 | mut stats = Stats::create(age: 88) 13 | 14 | stats.age = 77 15 | } -------------------------------------------------------------------------------- /tests/integration/structs/struct_field_private_error3.june: -------------------------------------------------------------------------------- 1 | // error: 'new' used on private member field from outside struct or class 2 | 3 | struct Stats { 4 | private age: i64 5 | 6 | fun print_age(self) { 7 | println(.age) 8 | } 9 | } 10 | 11 | fun main() { 12 | let stats = new Stats(age: 88) 13 | 14 | stats.print_age() 15 | } -------------------------------------------------------------------------------- /tests/integration/structs/struct_field_update.june: -------------------------------------------------------------------------------- 1 | // output: 200 2 | 3 | struct Employee { 4 | name: c_string, 5 | age: i64, 6 | } 7 | 8 | fun main() { 9 | mut employee = new Employee(name: c"Bob", age: 123) 10 | employee.age = 200; 11 | println(employee.age) 12 | } -------------------------------------------------------------------------------- /tests/integration/structs/struct_helper.june: -------------------------------------------------------------------------------- 1 | // output: 123 2 | 3 | struct Employee { 4 | name: c_string, 5 | age: i64, 6 | } 7 | 8 | fun helper() -> Employee { 9 | let employee = new Employee(name: c"Bob", age: 123) 10 | 11 | return employee 12 | } 13 | 14 | fun main() { 15 | let employee = helper() 16 | println(employee.age) 17 | } -------------------------------------------------------------------------------- /tests/integration/structs/struct_helper_deep.june: -------------------------------------------------------------------------------- 1 | // output: 123 2 | 3 | struct Employee { 4 | name: c_string, 5 | age: i64, 6 | } 7 | 8 | fun helper2() -> Employee { 9 | let employee = new Employee(name: c"Bob", age: 123) 10 | 11 | return employee 12 | } 13 | 14 | fun helper1() -> Employee { 15 | return helper2() 16 | } 17 | 18 | fun main() { 19 | let employee = helper1() 20 | println(employee.age) 21 | } -------------------------------------------------------------------------------- /tests/integration/structs/struct_in_fun.june: -------------------------------------------------------------------------------- 1 | // output: 123 2 | 3 | fun main() { 4 | struct Employee { 5 | name: c_string, 6 | age: i64, 7 | } 8 | 9 | if true { 10 | let employee = new Employee(name: c"Bob", age: 123) 11 | println(employee.age) 12 | } 13 | } -------------------------------------------------------------------------------- /tests/integration/structs/struct_in_fun_error.june: -------------------------------------------------------------------------------- 1 | // error: unknown type 2 | 3 | fun foo() { 4 | struct Employee { 5 | name: c_string, 6 | age: i64, 7 | } 8 | } 9 | 10 | let employee = new Employee(name: c"Bob", age: 123) 11 | println(employee.age) 12 | -------------------------------------------------------------------------------- /tests/integration/structs/struct_in_struct.june: -------------------------------------------------------------------------------- 1 | // output: 22 2 | 3 | struct Stats { 4 | age: i64 5 | } 6 | 7 | struct Employee { 8 | name: c_string, 9 | stats: Stats, 10 | } 11 | 12 | fun main() { 13 | let employee = new Employee(name: c"Sally", stats: new Stats(age: 22)) 14 | 15 | println(employee.stats.age) 16 | } -------------------------------------------------------------------------------- /tests/integration/structs/struct_new_field_error.june: -------------------------------------------------------------------------------- 1 | // error: unknown field 2 | 3 | struct Employee { 4 | name: c_string, 5 | age: i64, 6 | } 7 | 8 | fun main() { 9 | let employee = new Employee(name: c"Bob", height: 123) 10 | println(employee.age) 11 | } -------------------------------------------------------------------------------- /tests/integration/typechecking/call_labeled_error.june: -------------------------------------------------------------------------------- 1 | // error: expected name 'x' 2 | 3 | fun foo(x: i64) { 4 | println(x) 5 | } 6 | 7 | // Wrong label in call 8 | foo(u: 99) -------------------------------------------------------------------------------- /tests/integration/typechecking/function_in_function.june: -------------------------------------------------------------------------------- 1 | // output: hello! 2 | 3 | fun main() { 4 | fun say_hello() { 5 | println(c"hello!") 6 | } 7 | 8 | say_hello() 9 | } -------------------------------------------------------------------------------- /tests/integration/typechecking/global_variable_error.june: -------------------------------------------------------------------------------- 1 | // error: can't find variable 2 | 3 | let x = 123 4 | 5 | fun foo() { 6 | println(x) 7 | } 8 | 9 | foo() -------------------------------------------------------------------------------- /tests/integration/typechecking/mutability_mismatch_error.june: -------------------------------------------------------------------------------- 1 | // error: argument to function needs to be mutable 2 | 3 | struct Foo { 4 | x: i64 5 | 6 | fun set_x(mut self, new_x: i64) { 7 | .x = new_x 8 | } 9 | 10 | fun create() -> owned Foo { 11 | return new owned Foo(x: 0) 12 | } 13 | } 14 | 15 | let abc = Foo::create() 16 | abc.set_x(88) 17 | 18 | println(abc.x) 19 | -------------------------------------------------------------------------------- /tests/integration/typechecking/params_dont_leak_error.june: -------------------------------------------------------------------------------- 1 | // error: can't find variable 2 | 3 | fun foo(x: i64) -> i64 { 4 | return x 5 | } 6 | 7 | println(x) -------------------------------------------------------------------------------- /tests/integration/typechecking/raw_buffer_type_inference.june: -------------------------------------------------------------------------------- 1 | // output: done 2 | 3 | let x: raw[i64] = raw[] 4 | 5 | let y: raw[raw[c_string]] = raw[] 6 | 7 | println(c"done") 8 | 9 | -------------------------------------------------------------------------------- /tests/integration/typechecking/raw_buffer_type_inference2.june: -------------------------------------------------------------------------------- 1 | // output: abc 2 | 3 | mut x = raw[] 4 | 5 | unsafe { 6 | resize x 1 7 | x[0] = c"abc" 8 | println(x[0]) 9 | } 10 | 11 | -------------------------------------------------------------------------------- /tests/integration/typechecking/return_infer_error.june: -------------------------------------------------------------------------------- 1 | struct Stats { 2 | age: i64 3 | } 4 | 5 | fun foo(a: Stats, b: Stats) -> Stats { 6 | if true { 7 | return a 8 | } else { 9 | return b 10 | } 11 | } 12 | 13 | fun main() { 14 | let f = foo(new Stats(age: 22), new Stats(age: 33)) 15 | 16 | println(f.age) 17 | } -------------------------------------------------------------------------------- /tests/integration/typechecking/return_missing_error.june: -------------------------------------------------------------------------------- 1 | // error: function is missing expected return at end of function 2 | 3 | 4 | fun foo() -> i64 { 5 | } 6 | 7 | println(foo()) -------------------------------------------------------------------------------- /tests/integration/typechecking/return_missing_value_error.june: -------------------------------------------------------------------------------- 1 | // error: return needs value 2 | 3 | 4 | fun foo() -> i64 { 5 | return 6 | } 7 | 8 | println(foo()) -------------------------------------------------------------------------------- /tests/integration/typechecking/return_top_level_error.june: -------------------------------------------------------------------------------- 1 | // error: outside of a function 2 | 3 | return 9 -------------------------------------------------------------------------------- /tests/integration/typechecking/return_value.june: -------------------------------------------------------------------------------- 1 | // output: 3 2 | 3 | fun foo() -> i64 { 4 | return 3 5 | } 6 | 7 | println(foo()) -------------------------------------------------------------------------------- /tests/integration/typechecking/return_value_error.june: -------------------------------------------------------------------------------- 1 | // error: incompatible type at return 2 | 3 | fun foo() -> i64 { 4 | return c"bob" 5 | } 6 | 7 | println(foo()) -------------------------------------------------------------------------------- /tests/integration/variables/variable.june: -------------------------------------------------------------------------------- 1 | // output: 123 2 | 3 | fun main() { 4 | let x = 123 5 | 6 | println(x) 7 | } -------------------------------------------------------------------------------- /tests/integration/variables/variable_and_function.june: -------------------------------------------------------------------------------- 1 | // output: 3 2 | 3 | foo(3) 4 | 5 | fun foo(x: i64) { 6 | println(x); 7 | } -------------------------------------------------------------------------------- /tests/integration/variables/variable_bad_optional_type.june: -------------------------------------------------------------------------------- 1 | // error: do not match 2 | 3 | let x: bool = 3 -------------------------------------------------------------------------------- /tests/integration/variables/variable_mutation.june: -------------------------------------------------------------------------------- 1 | // output: 11 2 | 3 | mut x = 5 4 | 5 | x = x + 6 6 | 7 | println(x) -------------------------------------------------------------------------------- /tests/integration/variables/variable_mutation2.june: -------------------------------------------------------------------------------- 1 | // output: 12 2 | 3 | mut x = 5 4 | 5 | x += 7 6 | 7 | println(x) -------------------------------------------------------------------------------- /tests/integration/variables/variable_mutation3.june: -------------------------------------------------------------------------------- 1 | // output: 20 2 | 3 | mut x = 5 4 | 5 | x += 7 6 | x -= 2 7 | x *= 8 8 | x /= 4 9 | 10 | println(x) -------------------------------------------------------------------------------- /tests/integration/variables/variable_mutation_error.june: -------------------------------------------------------------------------------- 1 | // error: is not mutable 2 | 3 | let x = 5 4 | 5 | // variable is not mutable, so this will error 6 | x = x + 6 7 | 8 | println(x) -------------------------------------------------------------------------------- /tests/integration/variables/variable_simple.june: -------------------------------------------------------------------------------- 1 | // output: 123 2 | 3 | let x = 123 4 | 5 | println(x) -------------------------------------------------------------------------------- /tests/integration/variables/variable_simple_error.june: -------------------------------------------------------------------------------- 1 | // error: can't find variable 2 | 3 | let x = 123 4 | 5 | 123 + (abc + 12) -------------------------------------------------------------------------------- /tests/integration/variables/variable_simple_error2.june: -------------------------------------------------------------------------------- 1 | // error: can't find variable 2 | 3 | let x = 123 4 | 5 | y 6 | // test comment -------------------------------------------------------------------------------- /tests/integration_tests.rs: -------------------------------------------------------------------------------- 1 | type TestResult = Result<(), Report>; 2 | 3 | use color_eyre::{ 4 | eyre::{self, eyre, Report, WrapErr}, 5 | Section, SectionExt, 6 | }; 7 | use libtest_mimic::{Arguments, Failed, Trial}; 8 | use std::{ 9 | env, 10 | ffi::OsStr, 11 | fs, io, 12 | io::Write, 13 | path::{Path, PathBuf}, 14 | process::Command, 15 | sync::Once, 16 | }; 17 | 18 | fn main() -> eyre::Result<()> { 19 | let args = Arguments::from_args(); 20 | let tests = collect_tests()?; 21 | libtest_mimic::run(&args, tests).exit(); 22 | } 23 | 24 | /// Creates one test for each `.june` file in the current directory or 25 | /// sub-directories of the current directory. 26 | fn collect_tests() -> eyre::Result> { 27 | fn visit_dir(path: &Path, tests: &mut Vec) -> eyre::Result<()> { 28 | for entry in fs::read_dir(path)? { 29 | let entry = entry?; 30 | let file_type = entry.file_type()?; 31 | 32 | // Handle files 33 | let path = entry.path(); 34 | if file_type.is_file() { 35 | if path.extension() == Some(OsStr::new("june")) { 36 | let name = path 37 | .strip_prefix(env::current_dir()?)? 38 | .display() 39 | .to_string(); 40 | 41 | if parse_special_comments(&path).is_none() { 42 | continue; 43 | } 44 | 45 | let test = Trial::test(name, move || eval_source_runner(&path)); 46 | tests.push(test); 47 | } 48 | } else if file_type.is_dir() { 49 | // Handle directories 50 | visit_dir(&path, tests)?; 51 | } 52 | } 53 | 54 | Ok(()) 55 | } 56 | 57 | // We recursively look for `.june` files, starting from the current 58 | // directory. 59 | let mut tests = Vec::new(); 60 | let current_dir = env::var("CARGO_MANIFEST_DIR") 61 | .map(PathBuf::from)? 62 | .join("tests"); 63 | visit_dir(¤t_dir, &mut tests)?; 64 | 65 | Ok(tests) 66 | } 67 | 68 | /// testrunner adapter for libtest-mimic 69 | pub fn eval_source_runner(fname: &Path) -> Result<(), Failed> { 70 | match test_example(fname) { 71 | Ok(()) => Ok(()), 72 | Err(report) => Err(format!("{report:?}"))?, 73 | } 74 | } 75 | 76 | fn test_example(test_name: &Path) -> TestResult { 77 | static INIT_REPORTER: Once = Once::new(); 78 | INIT_REPORTER.call_once(|| { 79 | color_eyre::install().unwrap(); 80 | }); 81 | 82 | // Create it if it's not there 83 | let mut temp_dir = std::env::var("JUNE_TESTDIR") 84 | .map(PathBuf::from) 85 | .unwrap_or_else(|_| std::env::temp_dir()); 86 | temp_dir.push("june_tests"); 87 | 88 | let relative_path = PathBuf::from(test_name); 89 | 90 | let parent = relative_path.parent() 91 | .expect("all files are found by path so they should exist in a directory we know about and have a valid parent directory"); 92 | let parent = parent.file_name().unwrap(); 93 | temp_dir.push(parent); 94 | 95 | // eprintln!("temp_dir: {:?}", temp_dir); 96 | 97 | let _ = fs::create_dir_all(&temp_dir); 98 | 99 | let test_filepath = { 100 | let mut test_filename = PathBuf::from(test_name); 101 | test_filename.set_extension("june"); 102 | 103 | let mut test_filepath = PathBuf::from("./tests/integration"); 104 | test_filepath.push(test_filename); 105 | 106 | // eprintln!("test_filepath: {:?}", test_filepath); 107 | 108 | test_filepath 109 | }; 110 | 111 | let config = parse_special_comments(&test_filepath) 112 | .expect("test should have an \"output:\" or \"error:\" test configuration comment"); 113 | 114 | let c_output_filepath = { 115 | let c_output_filename = PathBuf::from(test_name); 116 | let mut c_output_filename = PathBuf::from( 117 | c_output_filename 118 | .file_name() 119 | .expect("missing test filename"), 120 | ); 121 | c_output_filename.set_extension("c"); 122 | 123 | let mut c_output_filepath = temp_dir.clone(); 124 | c_output_filepath.push(c_output_filename); 125 | 126 | // eprintln!("c_output_filepath: {:?}", c_output_filepath); 127 | 128 | c_output_filepath 129 | }; 130 | 131 | let app_filepath = { 132 | let mut app_filepath = temp_dir.clone(); 133 | 134 | let test_name = PathBuf::from(test_name); 135 | let test_name = test_name.file_name().expect("missing test filename"); 136 | app_filepath.push(test_name); 137 | 138 | app_filepath 139 | }; 140 | 141 | let mut command = Command::new("./target/debug/june"); 142 | command.arg(&test_filepath); 143 | let log_env = match (std::env::var("RUST_LOG"), config.log_config) { 144 | (Ok(env), None) => Some(env), 145 | (Ok(env), Some(test_cfg)) => { 146 | let joined = env + "," + &test_cfg; 147 | Some(joined) 148 | } 149 | (Err(_), Some(test_cfg)) => Some(test_cfg), 150 | (Err(_), None) => None, 151 | }; 152 | if let Some(log_env) = log_env { 153 | command.env("RUST_LOG", log_env); 154 | } 155 | 156 | let output = command.output()?; 157 | let command_err = String::from_utf8_lossy(&output.stderr); 158 | let command_out = String::from_utf8_lossy(&output.stdout); 159 | 160 | if !output.status.success() && config.expected_error.is_none() { 161 | Err(eyre!("June did not compile successfully")) 162 | .with_section(|| command_out.trim().to_string().header("Stdout:")) 163 | .with_section(|| command_err.trim().to_string().header("Stderr:"))?; 164 | } 165 | 166 | if let Some(expected_error) = &config.expected_error { 167 | // println!("Checking:\n{}expected: {}\n", command_err, expected_error); 168 | 169 | assert!(command_err.contains(expected_error)); 170 | } 171 | 172 | let app_output = if config.expected_error.is_none() { 173 | // Now, output our C to a file 174 | // eprintln!("c_output_filepath: {:?}", c_output_filepath); 175 | let mut output_file = fs::File::create(&c_output_filepath).unwrap(); 176 | let _ = output_file.write_all(&output.stdout); 177 | 178 | // Next, compile the file 179 | let compiler = Command::new("clang") 180 | .arg(&c_output_filepath) 181 | .arg("-o") 182 | .arg(&app_filepath) 183 | .output() 184 | .wrap_err("Cannot execute clang")?; 185 | 186 | if !compiler.status.success() { 187 | let _ = io::stdout().write_all(&compiler.stdout); 188 | let _ = io::stdout().write_all(&compiler.stderr); 189 | panic!("Clang did not compile successfully"); 190 | } 191 | 192 | let app = Command::new(app_filepath).output().unwrap(); 193 | if !app.status.success() { 194 | panic!("App did not run successfully"); 195 | } 196 | 197 | // Lastly, compare the expected output 198 | let app_output = app.stdout.to_vec(); 199 | let app_output = String::from_utf8_lossy(&app_output); 200 | 201 | let app_output = app_output.replace("\r\n", ""); 202 | app_output.replace('\n', "") 203 | } else { 204 | String::new() 205 | }; 206 | 207 | if let Some(expected_output) = config.expected_output { 208 | assert_eq!(expected_output, app_output); 209 | } 210 | 211 | Ok(()) 212 | } 213 | 214 | struct TestConfig { 215 | expected_output: Option, 216 | expected_error: Option, 217 | log_config: Option, 218 | } 219 | 220 | fn parse_special_comments(test_filepath: &Path) -> Option { 221 | let example_file_contents = std::fs::read_to_string(test_filepath).unwrap(); 222 | let mut expected_output = None; 223 | let mut expected_error = None; 224 | let mut log_config = None; 225 | for line in example_file_contents.lines() { 226 | if line.starts_with("// output: ") && expected_output.is_none() { 227 | expected_output = line.strip_prefix("// output: ").map(ToOwned::to_owned); 228 | } else if line.starts_with("// error: ") && expected_error.is_none() { 229 | expected_error = line.strip_prefix("// error: ").map(ToOwned::to_owned); 230 | } else if line.starts_with("// RUST_LOG: ") && log_config.is_none() { 231 | log_config = line.strip_prefix("// RUST_LOG: ").map(ToOwned::to_owned); 232 | } 233 | } 234 | 235 | if expected_output.is_none() && expected_error.is_none() { 236 | return None; 237 | } 238 | 239 | Some(TestConfig { 240 | expected_output, 241 | expected_error, 242 | log_config, 243 | }) 244 | } 245 | -------------------------------------------------------------------------------- /tests/test_data/alphabet.txt: -------------------------------------------------------------------------------- 1 | abcdefg 2 | --------------------------------------------------------------------------------