├── cli ├── b.lazy ├── b │ └── c.lazy ├── Cargo.toml ├── .gitignore ├── README.md ├── main.lazy └── src │ └── main.rs ├── media └── logo.png ├── src ├── lib.rs ├── parser │ ├── src │ │ ├── lib.rs │ │ ├── input_parser │ │ │ └── mod.rs │ │ ├── ast │ │ │ ├── utils.rs │ │ │ ├── model.rs │ │ │ └── mod.rs │ │ └── tokenizer │ │ │ └── mod.rs │ └── Cargo.toml ├── semantic_analyzer │ ├── src │ │ ├── lib.rs │ │ ├── path.rs │ │ ├── file_host.rs │ │ ├── checker.rs │ │ ├── module.rs │ │ └── symbol.rs │ └── Cargo.toml └── errors │ ├── Cargo.toml │ └── src │ ├── builder.rs │ ├── diagnostics.rs │ └── lib.rs ├── book ├── book.toml └── src │ ├── metaprogramming │ └── main.md │ ├── custom_types │ ├── main.md │ ├── generic_bounds.md │ ├── impl.md │ ├── partials.md │ ├── enums.md │ └── structs.md │ ├── SUMMARY.md │ ├── primitives │ ├── main.md │ ├── functions.md │ ├── iterators.md │ └── literals.md │ ├── welcome.md │ ├── modules │ └── main.md │ ├── promises │ └── main.md │ ├── loops │ └── main.md │ ├── variables │ └── main.md │ └── logic │ └── main.md ├── Cargo.toml ├── .gitignore ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── workflows │ └── gh-pages.yml ├── CONTRIBUTING.md └── CODE_OF_CONDUCT.md ├── README.md └── LICENSE /cli/b.lazy: -------------------------------------------------------------------------------- 1 | 2 | 3 | export enum A {} -------------------------------------------------------------------------------- /cli/b/c.lazy: -------------------------------------------------------------------------------- 1 | 2 | import * from "../main" 3 | 4 | export type A = {}; -------------------------------------------------------------------------------- /media/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lazy-lang/Lazy/HEAD/media/logo.png -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | pub use parser; 3 | pub use errors; 4 | pub use semantic_analyzer; -------------------------------------------------------------------------------- /src/parser/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | #[macro_use] 3 | extern crate bitflags; 4 | 5 | pub mod input_parser; 6 | pub mod tokenizer; 7 | pub mod ast; -------------------------------------------------------------------------------- /src/semantic_analyzer/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | pub mod symbol; 3 | pub mod file_host; 4 | pub mod module; 5 | pub mod path; 6 | pub mod checker; -------------------------------------------------------------------------------- /src/errors/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "errors" 3 | version = "0.1.0" 4 | authors = ["GoogleFeud "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | colored = "2.0.0" -------------------------------------------------------------------------------- /book/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["GoogleFeud"] 3 | language = "en" 4 | multilingual = true 5 | src = "src" 6 | title = "Lazy By Example" 7 | 8 | [build] 9 | build-dir = "../docs" 10 | create-missing = false -------------------------------------------------------------------------------- /src/parser/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "parser" 3 | version = "0.1.0" 4 | authors = ["GoogleFeud "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | bitflags = "1.3.2" 9 | errors = { path = "../errors" } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lazy" 3 | version = "0.1.0" 4 | authors = ["GoogleFeud "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | parser = { path = "./src/parser" } 9 | errors = { path = "./src/errors" } 10 | semantic_analyzer = { path = "./src/semantic_analyzer" } -------------------------------------------------------------------------------- /src/semantic_analyzer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "semantic_analyzer" 3 | version = "0.1.0" 4 | authors = ["GoogleFeud "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | parser = { path = "../parser" } 9 | errors = { path = "../errors" } 10 | rustc-hash = "1.1.0" 11 | bitflags = "1.3.2" -------------------------------------------------------------------------------- /book/src/metaprogramming/main.md: -------------------------------------------------------------------------------- 1 | 2 | # Metaprogramming 3 | 4 | Lazy allows you to create **function macros** which allow you to write cleaner and less-repetitive code. They're very similar to rust macros: 5 | 6 | ``` 7 | macro my_macro($a: ident, $b: expr) => { 8 | $a[$b]; 9 | } 10 | 11 | main { 12 | 13 | } 14 | ``` -------------------------------------------------------------------------------- /cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lazy-cli" 3 | version = "0.1.0" 4 | authors = ["GoogleFeud "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | clap = "3.0.0-beta.2" 11 | lazy = { path = "../" } 12 | 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | 13 | # Added by cargo 14 | 15 | /target 16 | -------------------------------------------------------------------------------- /cli/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | 13 | # Added by cargo 14 | 15 | /target 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /cli/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Lazy-Cli 3 | 4 | To use lazy-cli, first build it via `cargo`: 5 | 6 | ``` 7 | cargo build --release 8 | ``` 9 | 10 | and then run the executable: 11 | 12 | ``` 13 | ./target/release/lazy-cli.exe -r main.lazy 14 | ``` 15 | 16 | ## Flags and options 17 | 18 | ``` 19 | USAGE: 20 | lazy-cli.exe [FLAGS] [OPTIONS] 21 | 22 | FLAGS: 23 | -h, --help Prints help information 24 | -t, --time Shows you the parsing time of the code 25 | -V, --version Prints version information 26 | 27 | OPTIONS: 28 | -r, --run Runs a lazy file 29 | ``` -------------------------------------------------------------------------------- /.github/workflows/gh-pages.yml: -------------------------------------------------------------------------------- 1 | name: github pages 2 | 3 | on: 4 | pull_request: 5 | workflow_dispatch: 6 | push: 7 | branches: 8 | - main 9 | 10 | jobs: 11 | deploy: 12 | runs-on: ubuntu-18.04 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Setup mdBook 17 | uses: peaceiris/actions-mdbook@v1 18 | with: 19 | mdbook-version: 'latest' 20 | 21 | - run: mdbook build ./book 22 | 23 | - name: Deploy to GitHub Pages 24 | uses: JamesIves/github-pages-deploy-action@4.1.3 25 | with: 26 | branch: gh-pages 27 | folder: docs 28 | -------------------------------------------------------------------------------- /book/src/custom_types/main.md: -------------------------------------------------------------------------------- 1 | 2 | # Custom Types 3 | 4 | There are two ways to create custom types in **Lazy**: 5 | 6 | - `enum` - For creating enumerations with different variants, which can hold values of different types 7 | - `struct` - Structs are very similar to C structs, except they can contain functions 8 | 9 | ## Type aliasing 10 | 11 | Type aliasing can be done by using the `type` keyword: 12 | 13 | ``` 14 | type i = i8; 15 | 16 | main { 17 | let a: i = 4; 18 | } 19 | ``` 20 | 21 | ## None-able types 22 | 23 | A type which is prefixed with `?` is `none`-able. The value it may have may be `none` or the type. 24 | 25 | ``` 26 | static div = fn(a: i32, b: i32) -> i32? { 27 | if b == 0 none 28 | else a / b 29 | } 30 | ``` -------------------------------------------------------------------------------- /book/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Welcome](./welcome.md) 4 | - [Primitives](./primitives/main.md) 5 | - [Literals](./primitives/literals.md) 6 | - [Functions](./primitives/functions.md) 7 | - [Iterators](./primitives/iterators.md) 8 | - [Custom Types](./custom_types/main.md) 9 | - [Enums](./custom_types/enums.md) 10 | - [Structs](./custom_types/structs.md) 11 | - [Partials](./custom_types/partials.md) 12 | - [Impl](./custom_types/impl.md) 13 | - [Bounds](./custom_types/generic_bounds.md) 14 | - [Variables](./variables/main.md) 15 | - [Logic](./logic/main.md) 16 | - [Loops](./loops/main.md) 17 | - [Promises](./promises/main.md) 18 | - [Modules](./modules/main.md) 19 | - [Metaprogramming](./metaprogramming/main.md) -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | 29 | **Additional context** 30 | Add any other context about the problem here. 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lazy 2 | 3 |

4 | 5 |

6 | 7 | A statically typed programming language which is a mix of **typescript** and **rust**, written in **rust**. 8 | 9 | You can find all of the language's features [here](https://lazy-lang.github.io/Lazy/). 10 | 11 | ## Progress 12 | 13 | - ✔️ | Parser 14 | - ✔️ | Lexer 15 | - ✔️ | AST structure 16 | - ✔️ | Error handling and messages 17 | - ✖️ | Compiler 18 | - 🕒 | Type checker 19 | - ✖️ | Bytecode compiler 20 | - ✖️ | Runtime 21 | - ✖️ | Bytecode interpreter 22 | - ✖️ | JIT compiler 23 | 24 | ## "Hello World" in Lazy 25 | 26 | ``` 27 | main { 28 | print("Hello World"); 29 | } 30 | ``` 31 | 32 | ## Contributing 33 | 34 | If you're interested in the future of this project, feel free to contribute to it! -------------------------------------------------------------------------------- /book/src/custom_types/generic_bounds.md: -------------------------------------------------------------------------------- 1 | 2 | # Generic bounds 3 | 4 | In `struct`s, `enum`s and `type`s, generic parameters can be **bounded**. That means that only a type which satisfies the specified partial can be passed as a generic parameter. 5 | 6 | ``` 7 | struct Error< 8 | T: { to_string: () -> str } 9 | > { 10 | content: T, 11 | type: i32, 12 | format: fn() -> str { 13 | `Error ${self.type}: ${self.content}`; 14 | } 15 | 16 | } 17 | 18 | struct CustomErrorContent { 19 | vars: Vec, 20 | to_string: fn() -> str { 21 | vars.join(", "); 22 | } 23 | } 24 | 25 | main { 26 | const my_custom_error_content = new CustomErrorContent { vars: Vec::from("Hello", " ", "World!") }; 27 | const error = new Error { content: my_custom_error_content, type: 1 }; 28 | print(error.format()); // "Error 1: Hello World" 29 | } 30 | ``` 31 | -------------------------------------------------------------------------------- /book/src/primitives/main.md: -------------------------------------------------------------------------------- 1 | 2 | # Primitives 3 | 4 | Much like the rest programming languages, Lazy provides a bunch of primitive data types. 5 | 6 | ## Scalar types 7 | 8 | - Signed integers: `i8`, `i16`, `i32` 9 | - Unsigned integers: `u8`, `u16`, `u32` 10 | - Floating points: `f32` 11 | - Characters: `char` 12 | - Strings: `str` 13 | - Booleans: `true` or `false` 14 | - The `none` data type 15 | 16 | ## Compound types 17 | 18 | ### Tuples 19 | 20 | Tuples can hold values of different types: 21 | 22 | ``` 23 | const items<[str, i32, bool]> = ["Hello", 64, true]; 24 | 25 | items.1; // 64 26 | ``` 27 | 28 | ### Arrays? 29 | 30 | `Lazy` does not support arrays natively, but it does provide a `Vec` struct, which is an array located on the **heap**. Those are always more useful anyways. 31 | 32 | ``` 33 | let nums = Vec::from(1..=10); 34 | nums.push(15); 35 | nums.filter(fn(n) n % 2); 36 | ``` 37 | 38 | -------------------------------------------------------------------------------- /src/semantic_analyzer/src/path.rs: -------------------------------------------------------------------------------- 1 | 2 | use std::{env::current_dir}; 3 | use std::path::{PathBuf, Component}; 4 | 5 | pub fn join_paths(path1: &str, path2: &str, remove_filename: bool) -> String { 6 | let mut path1buf = PathBuf::from(path1); 7 | if remove_filename { path1buf.pop(); }; 8 | for comp in PathBuf::from(path2).components() { 9 | match comp { 10 | Component::Normal(p) => path1buf.push(p), 11 | Component::ParentDir => { path1buf.pop(); }, 12 | Component::CurDir | Component::Prefix(_) | Component::RootDir => {} 13 | } 14 | }; 15 | path1buf.display().to_string() 16 | } 17 | 18 | pub fn full_path(path: &str) -> String { 19 | if PathBuf::from(path).is_absolute() { return path.to_string() }; 20 | join_paths(¤t_dir().unwrap().display().to_string(), path, false) 21 | } 22 | 23 | pub fn file_dir_and_join(path: &str, path2: &str) -> String { 24 | join_paths(path, path2, true) 25 | } -------------------------------------------------------------------------------- /book/src/welcome.md: -------------------------------------------------------------------------------- 1 | # Welcome 2 | 3 | This book acts as the **specification** for the **Lazy** programming language! Every detail about the language (grammar, data types, memory management, JIT) is provided, as well as examples. 4 | 5 | Lazy is a statically typed, interpreted programming language. 6 | 7 | ## A taste 8 | 9 | ``` 10 | struct Person { 11 | name: str, 12 | age: f32, 13 | hobby: str?, 14 | 15 | age_up: fn() { 16 | self.age += 1; 17 | } 18 | 19 | } 20 | 21 | enum AnimalTypes { 22 | Dog, 23 | Cat, 24 | Fish, 25 | Bird 26 | } 27 | 28 | struct Animal { 29 | name: str, 30 | age: i8, 31 | type: AnimalTypes 32 | } 33 | 34 | type ThingWithName = {name: str}; 35 | 36 | main { 37 | let me = new Person { name: "Google", age: 19 }; 38 | let my_pet = new Animal { name: "Baby", age: 1, type: AnimalTypes::Bird }; 39 | 40 | let things = new Vec{}; 41 | things.push(me); 42 | things.push(my_pet); 43 | 44 | for thing in things { 45 | print(thing.name); 46 | } 47 | } 48 | ``` -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | When contributing to this repository, please first discuss the change you wish to make via issue, 4 | email, or any other method with the owners of this repository before making a change. 5 | 6 | Please note we have a code of conduct, please follow it in all your interactions with the project. 7 | 8 | ## Pull Request Process 9 | 10 | 1. Ensure any install or build dependencies are removed before the end of the layer when doing a 11 | build. 12 | 13 | 2. Update the README.md and any other documentation with details of changes to the interface, this includes new environment 14 | variables, exposed ports, useful file locations and container parameters. 15 | 16 | 3. Increase the version numbers in any examples files and the README.md to the new version that this 17 | Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). 18 | 19 | 4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you 20 | do not have permission to do that, you may request the second reviewer to merge it for you. 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 GoogleFeud 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 | -------------------------------------------------------------------------------- /book/src/modules/main.md: -------------------------------------------------------------------------------- 1 | 2 | # Modules 3 | 4 | Sometimes you want to split your code to multiple files - modules are just the thing you need! 5 | 6 | ## Exporting 7 | 8 | You can export the following things: 9 | 10 | - struct declarations 11 | - enum declarations 12 | - static variables 13 | - type aliases 14 | 15 | ``` 16 | export enum SongGenre { 17 | Rock, 18 | Pop, 19 | Metal, 20 | Jazz, 21 | Rap, 22 | HipHop 23 | } 24 | 25 | export struct Song { 26 | const lyrics: str, 27 | const genre: SongGenre 28 | } 29 | 30 | export static create_song = fn(lyrics: str, genre: SongGenre) -> Song { 31 | new Song { lyrics, genre } 32 | } 33 | ``` 34 | 35 | ## Importing 36 | 37 | ``` 38 | import { create_song } from "./path/to/songs" 39 | 40 | main { 41 | let bloodmoney = create_song("what do you believe in?", SongGenre::Metal); 42 | } 43 | ``` 44 | 45 | ### `as` binding 46 | 47 | ``` 48 | import * from "./path/to/songs" as Songs; 49 | import { create_song as create_a_song } from "./path/to/songs"; 50 | 51 | main { 52 | let bloodmoney = Songs::create_song("what do you believe in?", Songs::SongGenre::Metal); 53 | } 54 | ``` -------------------------------------------------------------------------------- /cli/main.lazy: -------------------------------------------------------------------------------- 1 | import * from "./b/c" 2 | 3 | type A = B 4 | type Addable = { add: () -> void } 5 | 6 | struct Animal { 7 | name: str 8 | age: i8 9 | 10 | make_noise: fn(noise: str?) print(noise || "*quiet*") 11 | } 12 | 13 | struct Human { 14 | name = "Hello" 15 | age: i8 16 | job: str 17 | 18 | make_noise: fn() print("Hello World") 19 | } 20 | 21 | export type WithName = { name: str } 22 | 23 | // Requires the type to have a "make_noise" method - we don't care about params or return value in this case 24 | export type MakesNoise = { make_noise: () } 25 | 26 | main { 27 | let me = new Human{name: "Google", job: "Programmer", age: 19}; 28 | let some_animal = new Animal{name: "Perry", age: 2}; 29 | 30 | 5 >> 10; 31 | 5 >>> 50; 32 | 33 | let stuff_that_makes_noise = Vec::create(); 34 | stuff_that_makes_noise.push(me); 35 | stuff_that_makes_noise.push(some_animal); 36 | 37 | stuff_that_makes_noise[0]?.make_noise(); // "Hello World" 38 | stuff_that_makes_noise[1]?.make_noise(); // *quiet* 39 | } 40 | 41 | static println = fn(...strs: Stringable?) { 42 | for string in strs { 43 | print(string?.to_string() + "\n"); 44 | } 45 | } -------------------------------------------------------------------------------- /book/src/promises/main.md: -------------------------------------------------------------------------------- 1 | 2 | # Promises 3 | 4 | Async programming in `Lazy` is made to be simple and easy: 5 | 6 | ``` 7 | import "std/http" as Http 8 | 9 | main { 10 | const res = await? Http::get("https://google.com"); 11 | print(res.body); 12 | } 13 | ``` 14 | 15 | ## Creating promises 16 | 17 | ``` 18 | let prom = Promise::spawn(fn() -> Result Result::Ok(1 + 1)); 19 | let res = await? prom; // 2 20 | ``` 21 | 22 | Promises return a `Result`, which is a special enum with two possible variants - `T`, the actual return value of the promise, or an `Error`: 23 | 24 | ``` 25 | Promise::spawn(fn() -> Result { 26 | Result::Error("This promise always returns an error :x"); 27 | }); 28 | ``` 29 | 30 | A question mark (`?`) can be put after the `await` keyword to short-circuit the execution context if the promise returns an `Error`. 31 | 32 | 33 | ## Under the hood 34 | 35 | Under the hood, `Promise`s are handled by the [tokio](https://tokio.rs/) runtime. 36 | 37 | ## Timers 38 | 39 | ### Repeating a function 40 | 41 | ``` 42 | let counter = 0; 43 | Promise::interval(fn() { 44 | print(counter); 45 | counter += 1; 46 | }, 1m); 47 | ``` 48 | 49 | ### Timeout 50 | 51 | ``` 52 | Promise::timeout(fn() { 53 | print("This will be executed in 5 seconds!"); 54 | }, 5s); 55 | ``` 56 | 57 | ### Stopping function execution 58 | 59 | ``` 60 | await Promise::block(5m); 61 | ``` -------------------------------------------------------------------------------- /book/src/loops/main.md: -------------------------------------------------------------------------------- 1 | 2 | # Loops 3 | 4 | Loops are an imperative way to execute an **expression** multiple times. `Lazy` provides two way to create an imperative loop: `for` and `while`. 5 | 6 | 7 | ## For 8 | 9 | In `Lazy`, the for loop only has one variant - `for...in`: 10 | 11 | ``` 12 | for i in 0..10 { 13 | print(i); 14 | } 15 | // Prints 0, then 1, then 2... up to 9 16 | ``` 17 | 18 | The expression after `in` can be any valid iterator - and an iterator satisfies the following type: 19 | 20 | ``` 21 | type Iterator = { next: () -> T? } // Any struct with a "next" function is a valid iterator! 22 | ``` 23 | 24 | ## While 25 | 26 | A traditional `while` loop. The expression gets executed as long as the condition is `true`. 27 | 28 | ``` 29 | while true { 30 | // A never ending loop! 31 | } 32 | ``` 33 | 34 | ## Breaking a loop 35 | 36 | Both types of loops are expressions... but what do they return? By default, `none`, unless you use the `yield` keyword inside them. The `yield` keyword stops the execution of the loop and returns the provided expression. 37 | 38 | The following snippet checks if `20` is in `vector` and if it is, the `value` variable is set to `true`, otherwise it's `none`. 39 | 40 | ``` 41 | let vector = new Vec{ iter: 0..30 }; 42 | 43 | let value = for i in vector { 44 | if i == 20 yield true 45 | } 46 | 47 | if value print("20 is in the vector!") 48 | else print("20 is NOT in the vector!") 49 | ``` -------------------------------------------------------------------------------- /src/parser/src/input_parser/mod.rs: -------------------------------------------------------------------------------- 1 | 2 | use errors::{LoC}; 3 | 4 | pub struct InputParser { 5 | code: Vec, 6 | pub line: usize, 7 | pub col: usize, 8 | pos: usize 9 | } 10 | 11 | impl InputParser { 12 | 13 | pub fn new(code: &str) -> Self { 14 | InputParser { 15 | line: 1, 16 | col: 0, 17 | pos: 0, 18 | code: code.chars().collect() 19 | } 20 | } 21 | 22 | pub fn consume(&mut self) -> Option { 23 | if self.is_eof() { return None }; 24 | let char = self.code[self.pos]; 25 | self.pos += 1; 26 | if char == '\n' { 27 | self.line += 1; 28 | self.col = 0; 29 | } else { self.col += 1; }; 30 | Some(char) 31 | } 32 | 33 | pub fn peek(&self, am: usize) -> Option { 34 | if (self.pos + am) >= self.code.len() { return None; }; 35 | Some(self.code[self.pos + am]) 36 | } 37 | 38 | pub fn unpeek(&mut self, am: usize) { 39 | self.pos -= am; 40 | } 41 | 42 | pub fn is_eof(&self) -> bool { 43 | self.pos >= self.code.len() 44 | } 45 | 46 | pub fn skip_line(&mut self) { 47 | if self.is_eof() { return; }; 48 | while self.code[self.pos] != '\n' { 49 | self.pos += 1; 50 | } 51 | self.pos += 1; 52 | self.col = 0; 53 | self.line += 1; 54 | } 55 | 56 | pub fn loc(&self) -> LoC { 57 | LoC { line: self.line, col: self.col, pos: self.pos } 58 | } 59 | 60 | } -------------------------------------------------------------------------------- /book/src/custom_types/impl.md: -------------------------------------------------------------------------------- 1 | 2 | # Impl 3 | 4 | The **impl** keyword is used to combine a struct or an enum with a **partial** type, so the struct/enum fits the partial. Generally, this keyword doesn't have to be used with structs in order to make a struct compatible with a type, but it's nice syntactic sugar. 5 | 6 | ``` 7 | struct Human { 8 | age: i8 9 | } 10 | 11 | struct Animal { 12 | age: i8, 13 | species: AnimalTypes 14 | } 15 | 16 | type Speak = { speak: (...args: str?) -> str } // A partial which requires the structure to have a "speak" method which returns a `str` 17 | 18 | impl Speak for Human { 19 | speak: fn(...args: str?) -> str { 20 | if args.0 args.0; 21 | } 22 | } 23 | 24 | impl Speak for Animal { 25 | speak: fn(...args: str?) -> str { 26 | match self.species { 27 | AnimalTypes::Cat => "meow", 28 | AnimalTypes::Dog => "woof", 29 | AnimalTypes::Mouse => "*squick*" 30 | } 31 | } 32 | } 33 | 34 | ``` 35 | 36 | ## Type guards 37 | 38 | This feature can also be used to create type guards: 39 | 40 | ``` 41 | type TotalStrLen { 42 | total_len: () -> i32 43 | } 44 | 45 | impl TotalStrLen for Vec { 46 | total_len: fn() -> i32 { 47 | let len = 0; 48 | for string in self { 49 | len += string.length; 50 | } 51 | len; 52 | } 53 | } 54 | 55 | main { 56 | let vec_of_strs = Vec::from("a", "b", "cde"); 57 | vec_of_strs.total_len(); // 5 58 | let vec_of_nums = Vec::from(1, 2, 3, 4, 5); 59 | vec_of_nums.total_len(); // Error! 60 | } 61 | ``` -------------------------------------------------------------------------------- /book/src/primitives/functions.md: -------------------------------------------------------------------------------- 1 | 2 | # Functions 3 | 4 | In Lazy, functions are first-class citizens, this means they can be passed as function arguments, stored easily in data structures, and be saved in variables. 5 | 6 | ``` 7 | main { 8 | const say_alphabet_lowercase = fn () print(...'a'..='z'); 9 | say_alphabet_lowercase(); 10 | } 11 | ``` 12 | 13 | A function may have up to 255 arguments! 14 | 15 | ## Function return values 16 | 17 | A return value type can be specified to any function by adding and arrow and the type after the arguments part: 18 | 19 | ``` 20 | const is_ok = fn() -> bool { 21 | true; 22 | } 23 | ``` 24 | 25 | As you notice, there's no `return` statement in `Lazy`. The last expression in the function body always gets returned. Everything in `Lazy` is an expression, except `type`, `struct` and `enum` declarations, so it's all good. 26 | 27 | If a return type is not provided, the function will always return `none`. 28 | 29 | ## Optional parameters 30 | 31 | ``` 32 | const add(num1: i32, num2: i32, num3: i32?) -> i32 { 33 | num1 + num2 + (num3 || 0); 34 | } 35 | ``` 36 | 37 | ## Default values 38 | 39 | ``` 40 | const add(num1: i32, num2: i32, num3 = 0) -> i32 { 41 | num1 + num2 + num3; 42 | } 43 | ``` 44 | 45 | ## Execution context 46 | 47 | All functions in `Lazy` have an **execution context** which can be referenced via `self` in the function body. The execution context of functions is always `none`, unless the function is defined inside a structure: 48 | 49 | ``` 50 | struct Car { 51 | model: str, 52 | beep_noise: str, 53 | 54 | beep: fn() { 55 | print(self.beep_noise); 56 | } 57 | } 58 | ``` 59 | 60 | -------------------------------------------------------------------------------- /book/src/variables/main.md: -------------------------------------------------------------------------------- 1 | 2 | # Variables 3 | 4 | Defining variables is done with the `let`, `const` and `static` keywords. 5 | 6 | ## let 7 | 8 | `let` defines a **mutable** variable. The value inside the variable itself is **always** mutable, but the **variable** can be changed. 9 | 10 | ``` 11 | let a = 5; 12 | a = 10; // Valid 13 | a = 3.14; // Invalid, `a` is implicitly an i32 14 | ``` 15 | 16 | ## const 17 | 18 | `const` defines an **immutable** variable. The value is still **mutable**, but the **variable** cannot be changed. 19 | 20 | ``` 21 | const a = 5; 22 | a = 10; // Invalid 23 | ``` 24 | 25 | ``` 26 | struct Pair { 27 | key: str, 28 | val: V 29 | } 30 | 31 | const my_pair = new Pair{key: "Hello", val: 3}; 32 | my_pair.key = "World"; // Valid 33 | ``` 34 | 35 | ## static 36 | 37 | `static` is used for values you **never** want to get garbage-collected. They cannot be defined in functions or the `main` block. 38 | 39 | ``` 40 | static PI = 3.14; 41 | 42 | main { 43 | PI = 4; // Invalid 44 | let PI = 4; // Invalid 45 | const PI = 5; // Invalid 46 | } 47 | ``` 48 | 49 | 50 | ## Type hinting in variables 51 | 52 | ``` 53 | let someOption = none; // Incorrect 54 | ``` 55 | 56 | Here, the compiler doesn't know what else can `someOption` be other than none, so it throws an error. You need to specify the other possible type. 57 | 58 | ``` 59 | let someOption: i32? = none; // Correct! 60 | ``` 61 | 62 | ## Deconstructing structs or tuples 63 | 64 | ``` 65 | let my_tuple = [1, 2, 3, 4, 5]; 66 | let [firstElement, secondElement] = my_tuple; 67 | print(firstElement, secondElement); // 1, 2 68 | ``` 69 | 70 | ``` 71 | struct Student { 72 | grades: Map 73 | favorite_subject: str 74 | } 75 | 76 | const { favorite_subject } = new Student { grades: Map::create(), favorite_subject: "programming" }; 77 | print(favorite_subject); // "programming" 78 | ``` -------------------------------------------------------------------------------- /book/src/primitives/iterators.md: -------------------------------------------------------------------------------- 1 | 2 | # Iterators 3 | 4 | `Iterator` is a type which allows you to loop over a struct. `for...in` loops are powered by iterators. An `Iterator` is any struct which satisfies the following type: 5 | 6 | ``` 7 | type Iterator = { next: () -> T? } 8 | ``` 9 | 10 | Any struct with a "next" function is a valid iterator! 11 | 12 | 13 | ``` 14 | struct RangeIter { 15 | min: i32 16 | max: i32 17 | progress: i32 18 | 19 | next: () -> i32? { 20 | if self.progress == self.max none 21 | else self.progress += 1 22 | } 23 | } 24 | 25 | main { 26 | let my_range = new Range { min: 0, max: 15, progress: 0 }; 27 | for i in my_range print(i); 28 | } 29 | ``` 30 | 31 | 32 | ## Literals 33 | 34 | Iterator literals are called `range` iterators, because they create an iterator that iterates through a range of numbers. 35 | 36 | ``` 37 | 0..5 // Exclusive 38 | 0..=5 // Inclusive 39 | ``` 40 | 41 | ## Spreading iterators 42 | 43 | The `spread` syntax can be used to turn iterators into vectors! 44 | 45 | ``` 46 | let iterator = 0..10; 47 | let vec = ...iterator; 48 | print(vec.to_string()); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; 49 | ``` 50 | 51 | ### Spread syntax in functions 52 | 53 | The `spread` syntax has a special meaning in function parameters: 54 | 55 | ``` 56 | type Stringable = { to_string: () -> str }; 57 | 58 | const print = fn(...strs: Stringable?) { 59 | // code 60 | } 61 | ``` 62 | 63 | The `...strs:` syntax means that the function can receive an unlimited amount of values with the `Stringable` type. The `strs` value itself is either a `Vector` which contains `Stringable`, or `none`.. 64 | 65 | ``` 66 | const println = fn(...strs: Stringable?) { 67 | for string in strs { 68 | print(string?.to_string() + "\n"); 69 | } 70 | } 71 | 72 | print_many("Hello", new Vec{}, 4, true); 73 | // Prints: 74 | /* 75 | Hello 76 | [] 77 | 4, 78 | true 79 | */ 80 | ``` -------------------------------------------------------------------------------- /book/src/custom_types/partials.md: -------------------------------------------------------------------------------- 1 | # Partials 2 | 3 | `Lazy` does not have core OOP design patters like inheritance, but it has `Partials`, which are very similar to rust's `Traits`, but a little more dynamic. 4 | 5 | ## Partial struct 6 | 7 | ``` 8 | struct Animal { 9 | name: str, 10 | age: i8, 11 | 12 | make_noise: fn(noise: str?) print(noise || "*quiet*") 13 | } 14 | 15 | struct Human { 16 | name: str, 17 | age: i8, 18 | job: str 19 | 20 | make_noise: fn() print("Hello World") 21 | } 22 | 23 | type WithName = { name: str } 24 | 25 | main { 26 | // The function will only have access to the name field 27 | let get_name = fn(thing: WithName) -> str { 28 | thing.name; 29 | } 30 | 31 | let me = new Human{name: "Google", job: "Programmer", age: 19}; 32 | let some_animal = new Animal{name: "Perry", age: 2}; 33 | 34 | get_name(me); // Google 35 | get_name(some_animal); // Perry 36 | } 37 | ``` 38 | 39 | ## Combining types 40 | 41 | Partials can be combined to create more complex partials: 42 | 43 | ``` 44 | type Stringable = { 45 | to_string: () -> str 46 | } 47 | 48 | type Numberable = { 49 | to_int: () -> i32 50 | } 51 | 52 | static num_and_str = fn(val: Stringable + Numerable) -> [str, i32] { 53 | [val.to_string(), val.to_int()]; 54 | } 55 | ``` 56 | 57 | 58 | ## Partial function 59 | 60 | ``` 61 | // Requires the type to have a "make_noise" method - we don't care about params or return value in this case 62 | type MakesNoise = { make_noise: () }; 63 | 64 | main { 65 | let me = new Human{name: "Google", job: "Programmer", age: 19}; 66 | let some_animal = new Animal{name: "Perry", age: 2}; 67 | 68 | let stuff_that_makes_noise = Vec::create(); 69 | stuff_that_makes_noise.push(me); 70 | stuff_that_makes_noise.push(some_animal); 71 | 72 | stuff_that_makes_noise[0]?.make_noise(); // "Hello World" 73 | stuff_that_makes_noise[1]?.make_noise(); // *quiet* 74 | } 75 | ``` -------------------------------------------------------------------------------- /cli/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate clap; 2 | use clap::{Arg, App}; 3 | use std::path::Path; 4 | use std::ffi::OsStr; 5 | use std::time::{Instant}; 6 | use lazy::semantic_analyzer::{file_host::{FSFileHost, FileHost}}; 7 | use lazy::errors::builder::ErrorFormatter; 8 | 9 | fn get_extention_validity(filename: &str) -> Option<&str> { 10 | Path::new(filename) 11 | .extension() 12 | .and_then(OsStr::to_str) 13 | } 14 | 15 | fn main() { 16 | let matches = App::new("lazy") 17 | .version("0.0.0") 18 | .about("A command-line interface to interact with the lazy programming language") 19 | .arg( 20 | Arg::new("run") 21 | .short('r') 22 | .long("run") 23 | .about("Runs a lazy file") 24 | .takes_value(true) 25 | ) 26 | .arg( 27 | Arg::new("time") 28 | .short('t') 29 | .long("time") 30 | .about("Shows you the parsing time of the code") 31 | .takes_value(false) 32 | ) 33 | .get_matches(); 34 | 35 | if let Some(exe_file) = matches.value_of("run") { 36 | if Path::new(&exe_file).exists() { 37 | if get_extention_validity(&exe_file) == Some("lazy") { 38 | let mut files = FSFileHost::new(); 39 | let before = Instant::now(); 40 | let module = files.create(&exe_file); 41 | if matches.is_present("time") { 42 | println!("Parsing took {} nanoseconds", before.elapsed().as_nanos()); 43 | } 44 | match module { 45 | Err(errors) => { 46 | for error in errors.collected { 47 | println!("{}", files.format_err(&error, &errors.filename).unwrap()); 48 | } 49 | }, 50 | Ok(_module) => { 51 | println!("Successfully parsed and analyzed module!"); 52 | } 53 | } 54 | } 55 | else{ 56 | println!("Could not parse the source code. Error: Could not find a lazy file.") 57 | } 58 | 59 | } 60 | else{ 61 | println!("Path does not exist.") 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /book/src/logic/main.md: -------------------------------------------------------------------------------- 1 | 2 | # Logic 3 | 4 | ## if/else 5 | 6 | In Lazy, if/else conditionals are expressions. All branches of the condition **must** return the same type, or none. Any expression can be used for the condition and the body - even other if expressions. 7 | 8 | ``` 9 | const a = 10; 10 | 11 | if a > 10 print("a is more than 10") 12 | else if a > 5 print("a is more than 5") 13 | else print("a is less than 5") 14 | ``` 15 | 16 | ``` 17 | 18 | enum Number { 19 | Int: i32, 20 | Float: f32 21 | } 22 | 23 | let enum_field = Number::Int(15); 24 | 25 | // if "enum_field" is of type `Number::Float`, return the unwrapped float inside it, otherwise return 0. 26 | let my_num = if let Number::Float(num) = enum_field enum_field else 0; 27 | 28 | print(my_num == 0) // true 29 | ``` 30 | 31 | ## match 32 | 33 | The `match` expression is exactly the same as Rust's `match`. 34 | 35 | ``` 36 | let my_num = Number:Float(3.14); 37 | 38 | match my_num { 39 | Number::Float(num) => print("Found float ", num), 40 | Number::Int(num) => print("Found integer ", num), 41 | 42 | // Specific cases 43 | Number::Float(3.14) => print("Found pi!"), 44 | 45 | // Guards 46 | Number::Int(num) if num > 10 => print("Found integer bigger than 10"), 47 | 48 | // Execute the same body for different expressions 49 | Number::Int(3) | Number::Int(5) => print("Number is either 3 or 5"), 50 | 51 | // Acts as an "else" - only executed when nothing above gets matched 52 | _ => print("Weird...") 53 | } 54 | ``` 55 | 56 | The contents of the `match` expression are called `arms` - each arm has a condition (which can be followed by a guard) and a body. 57 | 58 | A condition can be: 59 | 60 | - Enum variants (`Enum::variant`) 61 | - Literals (`"hello"`, `'c'`, `3`, `45.3`, `[1, 2, 3]`, `true`, `false`, `none`) 62 | - Range iterators (`0..10`, `5..=1000`, `'a'..='z'`) 63 | - A list of expressions separated by `|` (`1 | 5 | 7`) - if not all expressions return the same type, the value doesn't get unwrapped, but the body still gets executed. 64 | 65 | Guards can be any expression. 66 | 67 | A body can be any expression. -------------------------------------------------------------------------------- /book/src/primitives/literals.md: -------------------------------------------------------------------------------- 1 | 2 | # Literals 3 | 4 | Almost all literals are what'd you expect them to be. 5 | 6 | ## Numbers 7 | 8 | ``` 9 | let my_age = 18; // inferred type: i32 10 | let my_height = 6.1; // inferred type: f32 11 | 12 | let not_inferred: u8 = 12; // Not inferred, number is an u8 13 | ``` 14 | 15 | Numbers can contain underscores (`_`) to improve readability: 16 | 17 | ``` 18 | let big_num = 100_00_00; 19 | let same_big_num = 1000000; 20 | big_num == same_big_num; // returns "true" 21 | ``` 22 | 23 | To improve working with milliseconds, numbers can be prefixed with `s`, `m`, `h` and `d` to be automatically converted. This is done during compile time, so there's zero overhead! 24 | 25 | ``` 26 | let one_min_in_milliseconds = 1m; 27 | one_min_in_milliseconds == 60000; // returns "true" 28 | ``` 29 | 30 | ### Binary / Octal / Hex literals 31 | 32 | Numbers can be prefixed with `0b`, `0o` and `0x` to be parsed as binary / octal / hex numbers. 33 | 34 | ``` 35 | 0b010101 == 21 // true 36 | 0o03621623 == 992147 // true 37 | 0x0123ABCd == 19114957 // true 38 | ``` 39 | 40 | ## Template literals 41 | 42 | String concatenation is very messy in many languages - Lazy makes this easy with template literals: 43 | 44 | ``` 45 | let age = 1 + 17; 46 | print(`Hello World! I am ${age} years old`); // Hello World! I am 18 years old 47 | ``` 48 | 49 | They work exactly like in javascript! 50 | 51 | ## Iterators 52 | 53 | ``` 54 | 0..5; // Creates an iterator from 0 (inclusive) to 5 (exclusive), so 0, 1, 2, 3 and 4. 55 | 5..=10; // Creates an iterator from 5 (inclusive) to 10 (inclusive) 56 | ..5; // Short syntax for 0..5 57 | ..=10; // Short syntax for 0..=10 58 | ``` 59 | 60 | ## Functions 61 | 62 | In `Lazy`, functions are **first-class citizens**, you can pass them as parameters, save them in variables and so on. 63 | 64 | ``` 65 | let my_fn = fn() 1 + 1; 66 | my_fn(); // returns 2 67 | ``` 68 | 69 | ## Natural literals 70 | 71 | `natural` literals are `tuples` or `iterators` which can only contain literals. For example: 72 | 73 | ``` 74 | person.age..=18; // This is NOT a natural iterator 75 | 0..=18; // This IS, because both the start and the end are literals 76 | 77 | [1, 2, none, true] // This IS a natural tuple 78 | [1, 2, func(1 + 1)] // This is NOT a natural tuple 79 | ``` 80 | 81 | Only natural `tuples` or `iterators` are allowed in `match` arms. -------------------------------------------------------------------------------- /book/src/custom_types/enums.md: -------------------------------------------------------------------------------- 1 | 2 | # Enums 3 | 4 | Enums are types which may be one or a few different variants. 5 | 6 | ``` 7 | enum Number { 8 | Int: i32 9 | Float: f32 10 | } 11 | 12 | enum TokenTypes { 13 | String: str 14 | Integer: i32 15 | Number: Number 16 | Character: char 17 | Invalid 18 | } 19 | 20 | main { 21 | let str_token = TokenTypes::String("some token type"); 22 | str_token == TokenTypes::String; // returns true 23 | 24 | // str_token gets automatically unwrapped 25 | if str_token == TokenTypes::String print(str_token) // prints "some token type" 26 | else if str_token == TokenTypes::Integer print(str_token + 5) 27 | 28 | let num_token = TokenTypess::Number(Numbers::Int(3000)); 29 | } 30 | ``` 31 | 32 | ## "Unwrapping" enums 33 | 34 | By default all items are "wrapped" - their values and unknown and can be anything. 35 | 36 | ``` 37 | let char_token = TokenTypes::Char('a'); 38 | // Even if it's clear that char_token is of type `char`, Lazy doesn't allow you to use it. 39 | ``` 40 | 41 | To use the value inside the variant you'll have to "unwrap" it. 42 | 43 | ### if unwrapping 44 | 45 | ``` 46 | if let TokenTypes::Char(ch) = char_token { 47 | // You can use `char_token` as a character now. 48 | ch.to_string(); // "a" 49 | } 50 | // char_token is wrapped here. 51 | 52 | if char_token == TokenTypes::Char('a') { 53 | // char_token is still wrapped, but you know it's value. 54 | } 55 | ``` 56 | 57 | ### match unwrapping 58 | 59 | ``` 60 | match char_token { 61 | TokenTypes::Char(ch) => { 62 | print(ch); 63 | // char_token is unwrapped here 64 | }, 65 | _ => { 66 | // char_token is wrapped, this portion only executes when nothing else got matched 67 | } 68 | } 69 | ``` 70 | 71 | ## Attaching methods and properties to enums 72 | 73 | Attaching methods and properties to enums is done via the **impl** keyword. 74 | 75 | ``` 76 | enum Option { 77 | Some: T, 78 | None 79 | } 80 | 81 | type Unwrap = { 82 | unwrap: () -> T, 83 | is_some: () -> bool, 84 | unwrap_or: (v: T) -> T 85 | } 86 | 87 | // The impl block's typings must match the structure's 88 | impl Unwrap for Option { 89 | unwrap: fn() -> T { 90 | match self { 91 | Option::Some(val) => val, 92 | Option::None => error("Tried to unwrap an empty option!") 93 | } 94 | } 95 | 96 | is_some: fn() -> bool { 97 | self == Option::Some; 98 | } 99 | 100 | unwrap_or: fn(v: T) -> T { 101 | match self { 102 | Option::Some(val) => val, 103 | Option::None => v 104 | } 105 | } 106 | } 107 | 108 | main { 109 | let maybe: Option = Option::None; 110 | maybe.unwrap_or(0); // 0 111 | maybe = Option::Some(32); 112 | maybe.is_some(); // true 113 | maybe.unwrap(); // 32 114 | } 115 | ``` -------------------------------------------------------------------------------- /src/errors/src/builder.rs: -------------------------------------------------------------------------------- 1 | 2 | use colored::*; 3 | use super::*; 4 | 5 | fn get_digits_in_num(num: usize) -> usize { 6 | ((num as f32).log10() as usize) + 1 7 | } 8 | 9 | pub trait ErrorFormatter { 10 | fn get_file_contents(&self, file: &str) -> Option<&str>; 11 | 12 | fn format_err(&self, err: &BaseError, filename: &str) -> Option { 13 | let space_to_border = get_digits_in_num(err.range.end.line) + 1; // + 1 because of the padding between the number and the border 14 | let mut res = format!("{}{} {} {}: {}\n", " ".repeat(space_to_border), "┎──".cyan(), filename, err.range, err.msg.to_string().red()); 15 | let file_contents = self.get_file_contents(filename)?; 16 | let lines = file_contents.lines().collect::>(); 17 | let cyan_wall = "┃".cyan(); 18 | let red_arrow = &format!("{}", "^".red()); 19 | for ind in err.range.start.line..=err.range.end.line { 20 | let line_text = lines[ind as usize - 1]; 21 | let mut line = format!("{}{}{} {}\n", ind, " ".repeat(space_to_border - get_digits_in_num(ind)), cyan_wall, line_text); 22 | if ind == err.range.start.line { 23 | let mut cols = format!("{} {} ", " ".repeat(space_to_border - 1), cyan_wall); 24 | if err.range.start.col == err.range.end.col && err.range.start.line == err.range.end.line { 25 | cols.push_str(&format!("{}{}", &" ".repeat(err.range.start.col), "^".red())); 26 | } else { 27 | for col in 0..=line_text.len() { 28 | if col >= err.range.start.col && col < err.range.end.col { cols.push_str(&red_arrow); } 29 | else { cols.push(' '); } 30 | } 31 | } 32 | cols.push('\n'); 33 | line.push_str(&cols); 34 | } 35 | if ind == err.range.end.line && err.range.start.line != err.range.end.line { 36 | let mut cols = format!("{} {} ", " ".repeat(space_to_border - 1), cyan_wall); 37 | for col in 0..=line_text.len() { 38 | if col >= err.range.end.col { cols.push_str(&red_arrow); } 39 | else { cols.push(' '); } 40 | } 41 | cols.push('\n'); 42 | line.push_str(&cols); 43 | } 44 | res.push_str(&line); 45 | } 46 | for label in &err.labels { 47 | match label.variant { 48 | ErrorLabelVariants::Help => { 49 | res.push_str(&format!("{}", format!("{} {} Help: {}", " ".repeat(space_to_border - 1), cyan_wall, label.msg).bright_black())) 50 | } 51 | ErrorLabelVariants::Sub(range) => { 52 | let line_in_question = lines[range.start.line - 1]; 53 | let mut cols = format!("\n{} {} ", " ".repeat(space_to_border - 1), cyan_wall); 54 | for col in 0..=line_in_question.len() { 55 | if col >= range.start.col && col < range.end.col { cols.push_str(&red_arrow); } 56 | else { cols.push(' '); } 57 | } 58 | res.push_str(&format!("{}\n{} {} {}{}", cyan_wall, cyan_wall, " ".repeat(space_to_border - 1), line_in_question, cols)); 59 | } 60 | } 61 | } 62 | Some(res) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/parser/src/ast/utils.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | pub fn full_expression_range(ast: &ASTExpression) -> Range { 4 | match ast { 5 | ASTExpression::Var(v) => v.range, 6 | ASTExpression::Str(v) => v.range, 7 | ASTExpression::Bool(v) => v.range, 8 | ASTExpression::Float(v) => v.range, 9 | ASTExpression::Int(v) => v.range, 10 | ASTExpression::Binary(bin) => Range{start: full_expression_range(&bin.left).start, end: full_expression_range(&bin.right).end}, 11 | ASTExpression::Unary(un) => Range { start: un.range.start, end: full_expression_range(&un.value).end }, 12 | ASTExpression::DotAccess(access) => Range { start: full_expression_range(&access.value).start, end: access.range.end }, 13 | ASTExpression::IndexAccess(access) => Range { start: full_expression_range(&access.value).start, end: access.range.end }, 14 | ASTExpression::Block(block) => block.range, 15 | ASTExpression::Declare(l) => l.range, 16 | ASTExpression::Init(init) => init.range, 17 | ASTExpression::Optional(op) => { 18 | let start = full_expression_range(&op.value).start; 19 | Range { start, end: op.range.end } 20 | }, 21 | ASTExpression::Function(fun) => fun.range, 22 | ASTExpression::Iterator(init) => { 23 | let start = full_expression_range(&init.start).start; 24 | Range { start, end: init.range.end } 25 | }, 26 | ASTExpression::Call(call) => { 27 | let start = full_expression_range(&call.target).start; 28 | Range { start, end: call.range.end } 29 | }, 30 | ASTExpression::ForIn(for_in) => for_in.range, 31 | ASTExpression::While(while_loop) => while_loop.range, 32 | ASTExpression::If(ifexp) => ifexp.range, 33 | ASTExpression::Char(ch) => ch.range, 34 | ASTExpression::ModAccess(e) => e.range, 35 | ASTExpression::Tuple(tup) => tup.range, 36 | ASTExpression::Yield(y) => y.range, 37 | ASTExpression::Spread(sp) => sp.range, 38 | ASTExpression::Match(mtch) => mtch.range, 39 | ASTExpression::Await(aw) => aw.range, 40 | ASTExpression::TempStr(tmp) => tmp.range, 41 | ASTExpression::None(range) => *range 42 | } 43 | } 44 | 45 | pub fn is_natural_iter(ast: &ASTIterator) -> bool { 46 | matches!(*ast.start, ASTExpression::Char(_) | ASTExpression::Int(_)) && matches!(*ast.end, ASTExpression::Char(_) | ASTExpression::Int(_)) 47 | } 48 | 49 | pub fn is_natural_tuple(ast: &ASTExpressionList) -> bool { 50 | for value in &ast.expressions { 51 | if !is_natural_val(value) { return false }; 52 | } 53 | true 54 | } 55 | 56 | pub fn is_natural_mod_access(ast: &ASTModAccess) -> bool { 57 | if let Some(init) = &ast.init { 58 | for val in &init.expressions { 59 | if !is_natural_val(val) { return false }; 60 | } 61 | return true 62 | } else { 63 | return true 64 | } 65 | } 66 | 67 | pub fn is_natural_val(ast: &ASTExpression) -> bool { 68 | match ast { 69 | ASTExpression::Char(_) | ASTExpression::Int(_) | ASTExpression::Float(_) | ASTExpression::Str(_) | ASTExpression::None(_) | ASTExpression::Bool(_) => true, 70 | ASTExpression::Iterator(iter) => is_natural_iter(iter), 71 | ASTExpression::Tuple(tup) => is_natural_tuple(tup), 72 | ASTExpression::ModAccess(m) => is_natural_mod_access(m), 73 | _ => false 74 | } 75 | } -------------------------------------------------------------------------------- /book/src/custom_types/structs.md: -------------------------------------------------------------------------------- 1 | 2 | # Structs 3 | 4 | Structs contain key-value pairs called `fields`. All keys and the types of the values must be known at compile-time. 5 | 6 | ``` 7 | struct Something { 8 | val: GenericParam, 9 | 10 | set: fn(val: GenericParam) -> bool { 11 | self.val = val; 12 | true; 13 | } 14 | 15 | } 16 | ``` 17 | 18 | ## Field modifiers 19 | 20 | All of the following field modifiers can be used together on a single field, and the order of the modifiers does **not** matter. 21 | 22 | ### Static fields 23 | 24 | Sometimes you want a field to be attached to the struct itself and not an instance of the struct. 25 | 26 | ``` 27 | struct Person { 28 | name: str, 29 | 30 | static create: fn(name: str) -> Person { 31 | new Person { name }; 32 | } 33 | 34 | } 35 | 36 | let me = Person::create("Google"); 37 | ``` 38 | 39 | ### Hidden/Private fields 40 | 41 | Fields that can only be accessed in the execution context of the functions inside the sctruct. 42 | 43 | ``` 44 | struct Person { 45 | name: str, 46 | private id: i32 47 | 48 | static create: fn(name: str) -> Person { 49 | new Person { name, id: rng() }; 50 | } 51 | 52 | } 53 | 54 | let me = Person::create("Google"); 55 | me.id; // Error! 56 | ``` 57 | 58 | ### Immutable fields 59 | 60 | Fields that cannot be mutated. 61 | 62 | ``` 63 | struct Person { 64 | const name: str, 65 | private id: i32 66 | 67 | static create: fn(name: str) -> Person { 68 | new Person { name, id: rng() }; 69 | } 70 | 71 | } 72 | 73 | let me = Person::create("Google"); 74 | me.name = "Pedro"; // Error! 75 | ``` 76 | 77 | ## Optional fields 78 | 79 | Fields which have a question mark (`?`) after the type are **optional**. 80 | 81 | ``` 82 | struct StructWithOptionalField { 83 | // Can either be "none", or "str" 84 | something: str? 85 | } 86 | ``` 87 | 88 | ## Accessing fields 89 | 90 | Fields are accessed using the dot notation (`.`). 91 | 92 | ``` 93 | main { 94 | let my_smth = new Something{val: "Hello"}; 95 | my_smth.set(val: "World"); 96 | my_smth.val.length; // returns 5 97 | } 98 | ``` 99 | 100 | 101 | ## Accessing optional fields 102 | 103 | Optional fields can be accessed by the dot notation, but you must put a question mark (`?`) before the dot. If the optional struct is empty, then the expression returns `none`. 104 | 105 | ``` 106 | main { 107 | let my_struct = new StructWithOptionalField{}; // the optional field is `none` 108 | my_fn(my_struct.something?); // The function doesn't get executed because my_struct cannot be unwrapped 109 | my_struct.something = "Hello"; 110 | my_fn(my_struct.something?); // Runs fine! 111 | } 112 | ``` 113 | 114 | Keep in mind that you **cannot** use optional fields before you make sure they are not `none`. 115 | 116 | ``` 117 | if my_struct != none { 118 | // my_struct is guaranteed to not be none here, no need for question marks! 119 | print(my_struct.something); 120 | } 121 | ``` 122 | 123 | ## Operator overloading 124 | 125 | Operator overloading is done via partials. 126 | 127 | ``` 128 | import * from "std/ops" as Ops 129 | 130 | struct Person { 131 | first_name: str 132 | middle_name: str 133 | last_name: str 134 | } 135 | 136 | impl Ops::Add for Person { 137 | 138 | add: fn(other: Person) -> str { 139 | self.first_name + " " + other.middle_name + " " + other.last_name; 140 | } 141 | 142 | } 143 | 144 | main { 145 | const me = new Person {first_name: "Google", middle_name: "Something", last_name: "Feud"}; 146 | const you = new Person {first_name: "Taylor", middle_name: "Alison", last_name: "Swift"}; 147 | print(me + you); // Google Alison Swift 148 | } 149 | ``` -------------------------------------------------------------------------------- /src/semantic_analyzer/src/file_host.rs: -------------------------------------------------------------------------------- 1 | 2 | use crate::{module::Module}; 3 | use std::collections::HashMap; 4 | use errors::{builder::ErrorFormatter, LazyMultiResult}; 5 | use std::fs; 6 | use crate::path::full_path; 7 | 8 | pub trait FileHost: ErrorFormatter { 9 | fn create(&mut self, path: &str) -> LazyMultiResult>; 10 | fn get(&self, path: &str) -> Option<&Module>; 11 | fn get_or_create(&mut self, path: &str) -> LazyMultiResult>; 12 | fn get_unique_id(&mut self) -> u32; 13 | } 14 | 15 | pub struct VirtualFileHost { 16 | pub id_counter: u32, 17 | pub files: HashMap, 18 | pub file_contents: HashMap, 19 | pub file_cache: HashMap, 20 | } 21 | 22 | impl ErrorFormatter for VirtualFileHost { 23 | 24 | fn get_file_contents(&self, file: &str) -> Option<&str> { 25 | Some(&self.file_contents.get(file)?) 26 | } 27 | } 28 | 29 | impl FileHost for VirtualFileHost { 30 | 31 | fn get(&self, path: &str) -> Option<&Module> { 32 | self.files.get(path) 33 | } 34 | 35 | fn get_or_create(&mut self, path: &str) -> LazyMultiResult> { 36 | if let Some(file_contents) = self.file_cache.remove(path) { 37 | self.create_virtual(path, file_contents) 38 | } else { 39 | Ok(self.files.get(path)) 40 | } 41 | } 42 | 43 | fn get_unique_id(&mut self) -> u32 { 44 | self.id_counter += 1; 45 | self.id_counter 46 | } 47 | 48 | fn create(&mut self, _path: &str) -> LazyMultiResult> { 49 | panic!("'create' method doesn't exist for virtual file hosts! Use the 'create_virtual' method instead.") 50 | } 51 | 52 | } 53 | 54 | impl VirtualFileHost { 55 | 56 | pub fn new() -> Self { 57 | Self { 58 | id_counter: 0, 59 | files: HashMap::new(), 60 | file_contents: HashMap::new(), 61 | file_cache: HashMap::new() 62 | } 63 | } 64 | 65 | pub fn add_to_cache(&mut self, path: &str, content: String) { 66 | self.file_cache.insert(path.to_string(), content); 67 | } 68 | 69 | pub fn create_virtual(&mut self, path: &str, content: String) -> LazyMultiResult> { 70 | self.file_contents.insert(path.to_string(), content.clone()); 71 | let module = Module::from_str(self, path, &content)?; 72 | self.files.insert(path.to_string(), module); 73 | Ok(self.files.get(path)) 74 | } 75 | } 76 | 77 | pub struct FSFileHost { 78 | pub id_counter: u32, 79 | pub files: HashMap, 80 | pub file_contents: HashMap, 81 | } 82 | 83 | impl ErrorFormatter for FSFileHost { 84 | 85 | fn get_file_contents(&self, file: &str) -> Option<&str> { 86 | Some(&self.file_contents.get(file)?) 87 | } 88 | } 89 | 90 | impl FileHost for FSFileHost { 91 | 92 | fn get_unique_id(&mut self) -> u32 { 93 | self.id_counter += 1; 94 | self.id_counter 95 | } 96 | 97 | fn get(&self, path: &str) -> Option<&Module> { 98 | self.files.get(path) 99 | } 100 | 101 | fn create(&mut self, path: &str) -> LazyMultiResult> { 102 | let mut full_path = full_path(path); 103 | if !full_path.ends_with(".lazy") { full_path += ".lazy" }; 104 | if let Ok(text) = fs::read_to_string(&full_path) { 105 | let module = Module::from_str(self, &full_path, &text); 106 | self.file_contents.insert(full_path.to_string(), text); 107 | let module = module?; 108 | self.files.insert(full_path.clone(), module); 109 | Ok(self.files.get(&full_path)) 110 | } else { 111 | Ok(None) 112 | } 113 | } 114 | 115 | fn get_or_create(&mut self, path: &str) -> LazyMultiResult> { 116 | if self.files.contains_key(path) { 117 | Ok(self.files.get(path)) 118 | } else { 119 | self.create(path) 120 | } 121 | } 122 | 123 | } 124 | 125 | impl FSFileHost { 126 | 127 | pub fn new() -> Self { 128 | Self { 129 | id_counter: 0, 130 | files: HashMap::new(), 131 | file_contents: HashMap::new() 132 | } 133 | } 134 | } -------------------------------------------------------------------------------- /src/semantic_analyzer/src/checker.rs: -------------------------------------------------------------------------------- 1 | use errors::*; 2 | use crate::{module::*, symbol::*}; 3 | use rustc_hash::FxHashMap; 4 | 5 | pub struct TypeChecker { 6 | pub symbols: FxHashMap 7 | } 8 | 9 | impl SymbolCollector for TypeChecker { 10 | 11 | fn get_symbol(&self, name: &u32) -> Option<&Symbol> { 12 | self.symbols.get(name) 13 | } 14 | 15 | fn get_mut_symbol(&mut self, name: &u32) -> Option<&mut Symbol> { 16 | self.symbols.get_mut(name) 17 | } 18 | 19 | fn insert_symbol(&mut self, sym: Symbol) { 20 | self.symbols.insert(sym.id, sym); 21 | } 22 | 23 | } 24 | 25 | impl TypeChecker { 26 | 27 | pub fn check_module(&mut self, module: &mut Module) -> LazyResult<()> { 28 | Ok(()) 29 | } 30 | 31 | fn check_struct(&mut self, module: &mut Module, structure: &ASTStruct) -> LazyResult { 32 | let mut props: HashMap = HashMap::new(); 33 | for prop in &structure.fields.pairs { 34 | let sym_id = self.get_sym_from_type(module, prop.value.as_ref().unwrap(), structure.name.value == prop.name)?.to_symbol(self); 35 | // TODO: If the property has a default value, get the type from it. The property's not guaranteed to have a type. 36 | props.insert(prop.name.clone(), sym_id.to_ref()); 37 | } 38 | panic!("TODO") 39 | } 40 | 41 | // 42 | // Checks if a type is valid. 43 | // 44 | fn get_sym_from_type(&mut self, module: &mut Module, typing: &ASTTypings, handle_temps: bool) -> LazyResult { 45 | match typing { 46 | ASTTypings::Var(name) => { 47 | let sym_id = self.get_sym_from_var(module, &name.value, handle_temps)?; 48 | let sym = self.symbols.get(&sym_id).unwrap(); 49 | if let Some(generics) = &name.typings { 50 | let checked_list = self.check_list(module, generics, handle_temps)?; 51 | let sym = self.symbols.get_mut(&sym_id).unwrap(); 52 | Ok(sym.create_or_get_instance(checked_list, generics)?) 53 | } 54 | else { 55 | Ok(SymbolRef::new_ref(sym.id)) 56 | } 57 | }, 58 | ASTTypings::Optional(typ) => { 59 | let typing = self.get_sym_from_type(module, typ, handle_temps)?; 60 | Ok(typing.clone().make_optional()) 61 | } 62 | ASTTypings::Mod(name) => { 63 | let mut val = self.get_sym_from_var(module, &name.path[0], handle_temps)?.to_symbol(self); 64 | let mut is_enum = false; 65 | for i in 1..name.path.len() { 66 | let var = &name.path[i]; 67 | if let Some(typ) = val.get_mod_type(self, &var.value) { 68 | if val.kind.is_enum() { is_enum = true }; 69 | val = typ.to_symbol(self); 70 | } else { 71 | return Err(err!(NAME_NOT_FOUND, var.range, &var.value)) 72 | } 73 | }; 74 | if is_enum { 75 | Err(err!(VAL_AS_TYPE, name.range)) 76 | } else { 77 | Ok(val.to_ref()) 78 | } 79 | } 80 | _ => Err(err!(UNEXPECTED_EOF, Range::default(), &module.filename)) 81 | } 82 | } 83 | 84 | fn get_sym_from_var(&mut self, module: &mut Module, var: &ASTVar, handle_temps: bool) -> LazyResult { 85 | match module.temporary.remove(&var.value) { 86 | Some(mut val) if handle_temps => { 87 | val.kind = match &val.declaration { 88 | StatementOrExpression::StructStatement(structure) => self.check_struct(module, structure)?, 89 | _ => SymbolKind::None 90 | }; 91 | let sym_id = val.id; 92 | self.symbols.insert(val.id, val); 93 | Ok(sym_id) 94 | }, 95 | _ => { 96 | if let Some(sym) = module.get_sym(&var.value) { 97 | Ok(sym.id) 98 | } else { 99 | Err(err!(NAME_NOT_FOUND, var.range, &module.filename, &var.value)) 100 | } 101 | } 102 | } 103 | } 104 | 105 | fn check_list(&mut self, module: &mut Module, list: &ASTListTyping, handle_temps: bool) -> LazyResult> { 106 | let mut result: Vec = Vec::new(); 107 | for typing in &list.entries { 108 | result.push(self.get_sym_from_type(module, typing, handle_temps)?); 109 | } 110 | Ok(result) 111 | } 112 | 113 | } -------------------------------------------------------------------------------- /src/semantic_analyzer/src/module.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use crate::{file_host::{FileHost}, symbol::{Symbol, SymbolRef, StatementOrExpression}}; 3 | use parser::{ast::{Parser, model::{ASTImportThing, ASTStatement}}}; 4 | use errors::*; 5 | use crate::path::file_dir_and_join; 6 | 7 | pub struct Module { 8 | pub local: HashMap, 9 | pub exported: HashMap, 10 | pub temporary: HashMap, 11 | pub filename: String 12 | } 13 | 14 | impl Module { 15 | 16 | 17 | pub fn get_sym(&self, name: &str) -> Option<&SymbolRef> { 18 | self.local.get(name).or_else(|| self.exported.get(name)) 19 | } 20 | 21 | pub fn from_str(host: &mut T, filename: &str, content: &str) -> LazyMultiResult { 22 | let mut temp_syms: HashMap = HashMap::new(); 23 | let mut local: HashMap = HashMap::new(); 24 | let mut exported: HashMap = HashMap::new(); 25 | let mut errors = ErrorCollector::new(filename); 26 | let mut parser = Parser::new(&content, &mut errors); 27 | let ast = parser.parse(); 28 | for statement in ast { 29 | if let Some((name, range, is_exported, decl)) = match statement { 30 | ASTStatement::Import(decl) => { 31 | let path_to_mod = file_dir_and_join(filename, &decl.path.value); 32 | let module = if let Some(m) = host.get_or_create(&path_to_mod)? { m } else { 33 | errors.push(err!(MOD_NOT_FOUND, decl.path.range, &filename, &decl.path.value)); 34 | continue; 35 | }; 36 | match decl.thing { 37 | ASTImportThing::All => { 38 | for (name, id) in module.exported.iter() { 39 | local.insert(name.clone(), id.clone()); 40 | } 41 | } 42 | ASTImportThing::Items(item_list) => { 43 | for item in item_list { 44 | let item_name = item.name.clone(); 45 | let item_id = if let Some(id) = module.exported.get(&item_name) { id.clone() } else { 46 | errors.push(err!(TYPE_NOT_FOUND_FROM_MOD, item.range, &filename, &item_name, &decl.path.to_string())); 47 | SymbolRef::new_ref(0) 48 | }; 49 | let name = if let Some(alias) = item.r#as { 50 | alias.value 51 | } else { 52 | item.name 53 | }; 54 | local.insert(name, item_id); 55 | } 56 | } 57 | } 58 | None 59 | } 60 | ASTStatement::EnumDeclaration(decl) => Some((decl.name.value.clone(), decl.name.range, false, StatementOrExpression::EnumStatement(decl))), 61 | ASTStatement::Struct(decl) => Some((decl.name.value.clone(), decl.name.range, false, StatementOrExpression::StructStatement(decl))), 62 | ASTStatement::Type(decl) => Some((decl.name.value.clone(), decl.name.range, false, StatementOrExpression::TypeStatement(decl))), 63 | ASTStatement::Export(decl) => { 64 | match *decl.value { 65 | ASTStatement::EnumDeclaration(decl) => Some((decl.name.value.clone(), decl.name.range, true, StatementOrExpression::EnumStatement(decl))), 66 | ASTStatement::Struct(decl) => Some((decl.name.value.clone(), decl.name.range, true, StatementOrExpression::StructStatement(decl))), 67 | ASTStatement::Type(decl) => Some((decl.name.value.clone(), decl.name.range, true, StatementOrExpression::TypeStatement(decl))), 68 | _ => None 69 | } 70 | } 71 | _ => None 72 | } { 73 | if temp_syms.contains_key(&name) || local.contains_key(&name) { 74 | errors.push(err!(DUPLICATE_IDENT, range, &name)); 75 | continue; 76 | } 77 | let id = host.get_unique_id(); 78 | let reference = SymbolRef::new_ref(id); 79 | if is_exported { exported.insert(name.to_string(), reference); } 80 | else { local.insert(name.to_string(), reference); }; 81 | temp_syms.insert(name.to_string(), Symbol::empty(id, name, decl)); 82 | } 83 | } 84 | if errors.collected.is_empty() { 85 | Ok(Self { local, exported, filename: filename.to_string(), temporary: temp_syms }) 86 | } else { 87 | Err(errors) 88 | } 89 | } 90 | 91 | } -------------------------------------------------------------------------------- /src/errors/src/diagnostics.rs: -------------------------------------------------------------------------------- 1 | 2 | pub struct Diagnostic { 3 | pub code: u16, 4 | pub message: &'static str 5 | } 6 | 7 | macro_rules! make_diagnostics { 8 | ($([$name:ident, $code:expr, $msg:expr]),+) => { 9 | $( 10 | pub const $name: Diagnostic = Diagnostic { 11 | code: $code, 12 | message: $msg 13 | }; 14 | )+ 15 | } 16 | } 17 | 18 | pub struct Diagnostics; 19 | 20 | impl Diagnostics { 21 | 22 | make_diagnostics!([ 23 | END_OF_STR, 24 | 1001, 25 | "Expected end of string." 26 | ], [ 27 | DECIMAL_POINT, 28 | 1002, 29 | "Floating points cannot contain more than one decimal point." 30 | ], [ 31 | EXPECTED_PROP_NAME, 32 | 1003, 33 | "Expected a property name." 34 | ], [ 35 | INVALID_CHAR, 36 | 1004, 37 | "Invalid character $." 38 | ], [ 39 | UNEXPECTED_OP, 40 | 1005, 41 | "Unexpected operator $." 42 | ], [ 43 | UNEXPECTED_PUNC, 44 | 1006, 45 | "Unexpected punctuation $." 46 | ], [ 47 | SEMICOLON, 48 | 1007, 49 | "Expected semicolon at the end of the expression." 50 | ], [ 51 | END_OF_BLOCK, 52 | 1008, 53 | "Expected end of block." 54 | ], [ 55 | EXPECTED_FOUND, 56 | 1009, 57 | "Expected $, but found $." 58 | ], [ 59 | START_OF_BLOCK, 60 | 1010, 61 | "Expected start of block." 62 | ], [ 63 | ARROW_ACCESS, 64 | 1011, 65 | "Arrow access cannot be chained." 66 | ], [ 67 | EXPECTED_DELIMITER, 68 | 1012, 69 | "Expected delimiter $." 70 | ], [ 71 | TOO_MUCH_TYPES, 72 | 1013, 73 | "Too much typings provided, expected only $." 74 | ], [ 75 | EMPTY_CHAR_LITERAL, 76 | 1014, 77 | "Empty character literal." 78 | ], [ 79 | CONST_WITHOUT_INIT, 80 | 1015, 81 | "Constant variables must have an initializer." 82 | ], [ 83 | NO_GENERICS, 84 | 1016, 85 | "Generics are not allowed here." 86 | ], [ 87 | END_OF_ITER, 88 | 1017, 89 | "Expected end of iterator." 90 | ], [ 91 | DISALLOWED, 92 | 1018, 93 | "$ is not allowed here." 94 | ], [ 95 | MANY_ENTRIES, 96 | 1019, 97 | "Too many entry points. There can be only one." 98 | ], [ 99 | WRONG_MATCH_ARM_EXP, 100 | 1020, 101 | "Incorrect match arm expression. Match arms only accept enum variants or literals." 102 | ], [ 103 | ALREADY_HAS_MODIFIER, 104 | 1021, 105 | "The field is already $. Unnecessary modifier." 106 | ], [ 107 | CONFUSABLE, 108 | 1022, 109 | "Found $, which is very similar to $." 110 | ], [ 111 | INVALID_DIGIT, 112 | 1023, 113 | "Invalid digit." 114 | ], [ 115 | POINTLESS_TEMPLATE, 116 | 1024, 117 | "Pointless template literals. Use normal string literals instead." 118 | ], [ 119 | ONE_CHAR_ENDPOINT, 120 | 1025, 121 | "Expected character to contain only one codepoint." 122 | ], [ 123 | EXPECTED, 124 | 1026, 125 | "Expected $." 126 | ], [ 127 | UNEXPECTED, 128 | 1027, 129 | "Unexpected $." 130 | ], [ 131 | UNEXPECTED_EOF, 132 | 1028, 133 | "Unexpected end of file." 134 | ], [ 135 | EMPTY_TYPE_PARAMS, 136 | 1029, 137 | "Expected at least one type parameter." 138 | ], [ 139 | TYPE_NOT_FOUND_FROM_MOD, 140 | 2001, 141 | "Type $ not found from module $." 142 | ], [ 143 | MOD_NOT_FOUND, 144 | 2002, 145 | "Cannot find module \"$\"." 146 | ], [ 147 | DUPLICATE_IDENT, 148 | 2003, 149 | "Identifier \"$\" is already defined." 150 | ], [ 151 | FILE_RECURSION, 152 | 2004, 153 | "Detected recursive importing. Module \"$\" requires \"$\" and vice-versa." 154 | ], [ 155 | NAME_NOT_FOUND, 156 | 2005, 157 | "Couldn't find name $." 158 | ], [ 159 | VAL_AS_TYPE, 160 | 2006, 161 | "A value is being used as a type here." 162 | ], [ 163 | INVALID_AMOUNT_OF_TYPE_PARAMS, 164 | 2007, 165 | "Invalid amount of type parameters. Expected $, found $." 166 | ] 167 | ); 168 | 169 | } 170 | 171 | pub fn format_diagnostic(diagnostic: &Diagnostic, vars: Vec<&str>) -> String { 172 | let msg = diagnostic.message; 173 | if vars.is_empty() { return msg.to_string() }; 174 | let mut ind: usize = 0; 175 | let mut new_str = String::new(); 176 | for ch in msg.chars() { 177 | if ch == '$' { 178 | new_str.push_str(&vars[ind]); 179 | ind += 1; 180 | } else { 181 | new_str.push(ch) 182 | } 183 | } 184 | new_str 185 | } -------------------------------------------------------------------------------- /src/errors/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod builder; 2 | pub mod diagnostics; 3 | use std::fmt; 4 | 5 | pub use diagnostics::*; 6 | pub use builder::*; 7 | 8 | #[derive(Copy, Default)] 9 | pub struct LoC { 10 | pub line: usize, 11 | pub col: usize, 12 | pub pos: usize 13 | } 14 | 15 | impl std::clone::Clone for LoC { 16 | fn clone(&self) -> Self { 17 | *self 18 | } 19 | } 20 | 21 | impl std::fmt::Display for LoC { 22 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 23 | write!(f, "({}:{})", self.line, self.col) 24 | } 25 | } 26 | 27 | impl LoC { 28 | 29 | pub fn to_range(&self) -> Range { 30 | Range { start: self.clone(), end: self.clone()} 31 | } 32 | 33 | pub fn end(self, other: &LoC) -> Range { 34 | Range { start: self, end: other.clone() } 35 | } 36 | } 37 | 38 | #[derive(Copy, Default)] 39 | pub struct Range { 40 | pub start: LoC, 41 | pub end: LoC 42 | } 43 | 44 | impl std::clone::Clone for Range { 45 | fn clone(&self) -> Self { 46 | *self 47 | } 48 | } 49 | 50 | impl fmt::Display for Range { 51 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 52 | write!(f, "({}:{} - {}:{})", self.start.line, self.start.col, self.end.line, self.end.col) 53 | } 54 | } 55 | 56 | impl Range { 57 | 58 | pub fn end_with(self, end: &LoC) -> Range { 59 | Range { start: self.start, end: end.clone() } 60 | } 61 | 62 | } 63 | 64 | pub enum ErrorLabelVariants { 65 | Help, 66 | Sub(Range) 67 | } 68 | 69 | pub struct ErrorLabel { 70 | pub msg: String, 71 | pub variant: ErrorLabelVariants 72 | } 73 | 74 | /* These errors are used only for the sterr output 75 | 76 | Error labels must: 77 | - Be between the main error range 78 | */ 79 | 80 | pub struct BaseError { 81 | pub range: Range, 82 | pub msg: String, 83 | pub labels: Vec 84 | } 85 | 86 | impl BaseError { 87 | 88 | pub fn new(msg: String, range: Range) -> Self { 89 | Self { 90 | msg, 91 | range: range, 92 | labels: vec![] 93 | } 94 | } 95 | 96 | pub fn new_with_labels(msg: String, range: Range, labels: Vec) -> Self { 97 | Self { 98 | msg, 99 | range: range, 100 | labels 101 | } 102 | } 103 | 104 | } 105 | 106 | impl fmt::Debug for BaseError { 107 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 108 | f.debug_struct("Error") 109 | .field("msg", &self.msg.to_string()) 110 | .field("range", &self.range.to_string()) 111 | .finish() 112 | } 113 | } 114 | 115 | impl fmt::Display for BaseError { 116 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 117 | writeln!(f, "Error: {}", self.msg.to_string()) 118 | } 119 | } 120 | 121 | impl std::error::Error for BaseError {} 122 | 123 | pub type LazyResult = Result; 124 | pub type LazyMultiResult = Result; 125 | 126 | pub struct ErrorCollector { 127 | pub collected: Vec, 128 | pub filename: String 129 | } 130 | 131 | impl ErrorCollector { 132 | pub fn new(filename: &str) -> Self { 133 | ErrorCollector { 134 | collected: vec![], 135 | filename: filename.to_string() 136 | } 137 | } 138 | 139 | pub fn push(&mut self, err: BaseError) { 140 | self.collected.push(err); 141 | } 142 | 143 | } 144 | 145 | #[macro_export] 146 | macro_rules! err { 147 | ($diagnostic: ident, $range: expr, $($vars: expr),*; $([$label_text: expr, $label_range: expr]),*) => { 148 | BaseError { 149 | msg: format_diagnostic(&Diagnostics::$diagnostic, vec![$($vars),*]), 150 | range: $range, 151 | labels: vec![$( 152 | ErrorLabel { 153 | msg: String::from($label_text), 154 | variant: ErrorLabelVariants::Sub($label_range) 155 | } 156 | ),*] 157 | } 158 | }; 159 | ($diagnostic: ident, $range: expr, $($vars: expr),*; $([$label_text: expr]),*) => { 160 | BaseError { 161 | msg: format_diagnostic(&Diagnostics::$diagnostic, vec![$($vars),*]), 162 | range: $range, 163 | labels: vec![$( 164 | ErrorLabel { 165 | msg: String::from($label_text), 166 | variant: ErrorLabelVariants::Help, 167 | } 168 | ),*] 169 | } 170 | }; 171 | ($diagnostic: ident, $range: expr, $($vars: expr),*) => { 172 | BaseError { 173 | msg: format_diagnostic(&Diagnostics::$diagnostic, vec![$($vars),*]), 174 | range: $range, 175 | labels: vec![] 176 | } 177 | }; 178 | ($diagnostic: ident, $range: expr) => { 179 | BaseError { 180 | msg: Diagnostics::$diagnostic.message.to_string(), 181 | range: $range, 182 | labels: vec![] 183 | } 184 | }; 185 | ($diagnostic: expr, $range: expr) => { 186 | BaseError { 187 | msg: $diagnostic, 188 | range: $range, 189 | labels: vec![] 190 | } 191 | } 192 | } 193 | 194 | #[macro_export] 195 | macro_rules! dia { 196 | ($diagnostic: ident, $($vars: expr),*) => { 197 | format_diagnostic(&Diagnostics::$diagnostic, vec![$($vars),*]) 198 | }; 199 | ($diagnostic: ident) => { 200 | format_diagnostic(&Diagnostics::$diagnostic, vec![]) 201 | } 202 | } -------------------------------------------------------------------------------- /.github/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 | volen.sl666@gmail.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. -------------------------------------------------------------------------------- /src/semantic_analyzer/src/symbol.rs: -------------------------------------------------------------------------------- 1 | use errors::*; 2 | pub use parser::ast::{model::*}; 3 | 4 | bitflags::bitflags! { 5 | pub struct SymbolFlags: u32 { 6 | const STATIC = 1 << 0; 7 | const CONST = 1 << 1; 8 | const OPTIONAL = 1 << 2; 9 | const TYPE_PARAM = 1 << 3; 10 | } 11 | } 12 | 13 | pub trait SymbolCollector { 14 | fn insert_symbol(&mut self, sym: Symbol); 15 | fn get_symbol(&self, name: &u32) -> Option<&Symbol>; 16 | fn get_mut_symbol(&mut self, name: &u32) -> Option<&mut Symbol>; 17 | } 18 | 19 | pub enum StatementOrExpression { 20 | EnumStatement(ASTEnumDeclaration), 21 | StructStatement(ASTStruct), 22 | TypeStatement(ASTType), 23 | None 24 | } 25 | 26 | pub struct SymbolProperty { 27 | kind: SymbolRef, 28 | flags: ASTModifiers 29 | } 30 | 31 | pub enum SymbolKind { 32 | Struct(HashMap), 33 | Enum(HashMap), 34 | Fn{ 35 | parameters: HashMap, 36 | return_type: SymbolRef 37 | }, 38 | Module(HashMap), 39 | None 40 | } 41 | 42 | impl SymbolKind { 43 | pub fn is_enum(&self) -> bool { 44 | matches!(self, Self::Enum(_)) 45 | } 46 | 47 | pub fn is_struct(&self) -> bool { 48 | matches!(self, Self::Struct(_)) 49 | } 50 | 51 | pub fn is_fn(&self) -> bool { 52 | matches!(self, Self::Fn{parameters: _, return_type: _}) 53 | } 54 | 55 | pub fn is_module(&self) -> bool { 56 | matches!(self, Self::Module(_)) 57 | } 58 | } 59 | 60 | #[derive(Clone)] 61 | pub struct SymbolRef { 62 | pub id: u32, 63 | pub instance_id: Option, 64 | flags: SymbolFlags 65 | } 66 | 67 | impl SymbolRef { 68 | 69 | pub fn new_ref(id: u32) -> Self { 70 | SymbolRef { id, instance_id: None, flags: SymbolFlags::empty() } 71 | } 72 | 73 | pub fn new_instance(id: u32, instance_id: usize) -> Self { 74 | SymbolRef { id: id, instance_id: Some(instance_id), flags: SymbolFlags::empty() } 75 | } 76 | 77 | pub fn get_kind<'a, T: SymbolCollector>(&self, collector: &'a T) -> &'a SymbolKind { 78 | let sym = collector.get_symbol(&self.id).unwrap(); 79 | match self.instance_id { 80 | Some(id ) => &sym.instances[id].kind, 81 | None => &sym.kind 82 | } 83 | } 84 | 85 | pub fn is_value(&self) -> bool { 86 | self.flags.contains(SymbolFlags::CONST) | self.flags.contains(SymbolFlags::STATIC) 87 | } 88 | 89 | pub fn is_optional(&self) -> bool { 90 | self.flags.contains(SymbolFlags::OPTIONAL) 91 | } 92 | 93 | pub fn is_const(&self) -> bool { 94 | self.flags.contains(SymbolFlags::CONST) 95 | } 96 | 97 | pub fn is_static(&self) -> bool { 98 | self.flags.contains(SymbolFlags::STATIC) 99 | } 100 | 101 | pub fn is_type_param(&self) -> bool { 102 | self.flags.contains(SymbolFlags::TYPE_PARAM) 103 | } 104 | 105 | pub fn make_optional(mut self) -> Self { 106 | self.flags.set(SymbolFlags::OPTIONAL, true); 107 | self 108 | } 109 | 110 | pub fn make_const(mut self) -> Self { 111 | self.flags.set(SymbolFlags::CONST, true); 112 | self 113 | } 114 | 115 | pub fn make_static(mut self) -> Self { 116 | self.flags.set(SymbolFlags::STATIC, true); 117 | self 118 | } 119 | 120 | pub fn make_type_param(mut self) -> Self { 121 | self.flags.set(SymbolFlags::TYPE_PARAM, true); 122 | self 123 | } 124 | 125 | } 126 | 127 | pub trait ToSymbol { 128 | fn to_symbol<'a, C: SymbolCollector>(&self, collector: &'a C) -> &'a Symbol; 129 | } 130 | 131 | impl ToSymbol for u32 { 132 | fn to_symbol<'a, C: SymbolCollector>(&self, collector: &'a C) -> &'a Symbol { 133 | collector.get_symbol(self).unwrap() 134 | } 135 | } 136 | 137 | impl ToSymbol for SymbolRef { 138 | fn to_symbol<'a, T: SymbolCollector>(&self, collector: &'a T) -> &'a Symbol { 139 | collector.get_symbol(&self.id).unwrap() 140 | } 141 | } 142 | 143 | pub struct Symbol { 144 | pub name: String, 145 | pub id: u32, 146 | pub kind: SymbolKind, 147 | pub type_params: HashMap>, 148 | pub instances: Vec, 149 | pub declaration: StatementOrExpression, 150 | pub impls: Vec 151 | } 152 | 153 | pub struct SymbolInstance { 154 | pub id: usize, 155 | pub kind: SymbolKind, 156 | pub type_args: Vec 157 | } 158 | 159 | impl Symbol { 160 | 161 | pub fn empty(id: u32, name: String, decl: StatementOrExpression) -> Self { 162 | Self { 163 | id, 164 | name, 165 | kind: SymbolKind::None, 166 | type_params: HashMap::new(), 167 | instances: Vec::new(), 168 | declaration: decl, 169 | impls: Vec::new() 170 | } 171 | } 172 | 173 | pub fn create_or_get_instance(&mut self, params: Vec, ast: &ASTListTyping) -> LazyResult { 174 | let params_len = params.len(); 175 | if params_len != self.type_params.len() { 176 | return Err(err!(INVALID_AMOUNT_OF_TYPE_PARAMS, ast.range, &self.type_params.len().to_string(), ¶ms_len.to_string())); 177 | }; 178 | 'outer: for instance in &self.instances { 179 | for ind in 0..params_len { 180 | if params[ind].id != instance.type_args[ind].id { 181 | continue 'outer; 182 | } 183 | } 184 | return Ok(SymbolRef::new_instance(self.id, instance.id)); 185 | }; 186 | let instance_id = self.instances.len() + 1; 187 | self.instances.push(SymbolInstance { 188 | id: instance_id, 189 | // Create a new kind from this symbol's kind, but replace type arguments with actual values 190 | kind: SymbolKind::None, 191 | type_args: params 192 | }); 193 | Ok(SymbolRef::new_instance(self.id, instance_id)) 194 | } 195 | 196 | pub fn to_ref(&self) -> SymbolRef { 197 | SymbolRef { id: self.id, instance_id: None, flags: SymbolFlags::empty() } 198 | } 199 | 200 | pub fn get_mod_type<'a, C: SymbolCollector>(&'a self, collector: &'a C, name: &str) -> Option<&'a SymbolRef> { 201 | match &self.kind { 202 | SymbolKind::Struct(props) => { 203 | let prop = props.get(name)?; 204 | if prop.flags.contains(ASTModifiers::STATIC) { 205 | return Some(&prop.kind); 206 | }; 207 | }, 208 | SymbolKind::Enum(members) => return members.get(name), 209 | SymbolKind::Module(exported) => return exported.get(name), 210 | _ => {} 211 | }; 212 | for implementation in &self.impls { 213 | if let SymbolKind::Struct(properties) = implementation.get_kind(collector) { 214 | let prop = properties.get(name)?; 215 | if prop.flags.contains(ASTModifiers::STATIC) { 216 | return Some(&prop.kind); 217 | }; 218 | } 219 | }; 220 | None 221 | } 222 | 223 | } 224 | -------------------------------------------------------------------------------- /src/parser/src/tokenizer/mod.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use errors::*; 3 | use super::input_parser::{InputParser}; 4 | 5 | #[derive(PartialEq)] 6 | #[derive(Clone)] 7 | pub enum TokenType { 8 | Str(String), 9 | TempStrStart, 10 | Float(f32), 11 | Int(i32), 12 | Kw(String), 13 | Bool(bool), 14 | Var(String), 15 | Op(char), 16 | Char(char), 17 | Punc(char), 18 | None 19 | } 20 | 21 | #[derive(PartialEq, fmt::Debug)] 22 | pub enum NumberType { 23 | Binary, // 0b 24 | Octal, // 0o 25 | Hex, // 0x 26 | None 27 | } 28 | 29 | impl fmt::Display for TokenType { 30 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 31 | match self { 32 | Self::Str(string) => write!(f, "string {}", string), 33 | Self::Float(num) => write!(f, "float {}", num), 34 | Self::Int(num) => write!(f, "integer {}", num), 35 | Self::Kw(kw) => write!(f, "keyword {}", kw), 36 | Self::Bool(bo) => write!(f, "boolean {}", bo), 37 | Self::Var(name) => write!(f, "identifier {}", name), 38 | Self::Op(op) => write!(f, "operator {}", op), 39 | Self::Punc(punc) => write!(f, "punctuation {}", punc), 40 | Self::Char(ch) => write!(f, "char {}", ch), 41 | Self::TempStrStart => write!(f, "beginning of template literal"), 42 | Self::None => write!(f, "none") 43 | } 44 | } 45 | } 46 | 47 | macro_rules! match_str { 48 | ($s: expr, $($strs: expr),*) => { 49 | match $s { 50 | $($strs)|+ => true, 51 | _ => false 52 | } 53 | }; 54 | } 55 | 56 | pub struct Token { 57 | pub range: Range, 58 | pub val: TokenType 59 | } 60 | 61 | 62 | pub struct Tokenizer<'a> { 63 | current: Option, 64 | pub errors: &'a mut ErrorCollector, 65 | pub input: InputParser, 66 | pub is_last_num_as_str: bool, 67 | pub last_loc: LoC 68 | } 69 | 70 | impl<'a> Tokenizer<'a> { 71 | 72 | pub fn new(code: &str, errors: &'a mut ErrorCollector) -> Self { 73 | Tokenizer { 74 | current: None, 75 | errors, 76 | is_last_num_as_str: false, 77 | input: InputParser::new(code), 78 | last_loc: LoC::default() 79 | } 80 | } 81 | 82 | fn parse_str(&mut self, end_char: char) -> Token { 83 | let start = self.input.loc(); 84 | self.input.consume(); // Consume the starting " 85 | let mut str = String::new(); 86 | loop { 87 | match self.input.consume() { 88 | Some(character) => { 89 | if character == end_char { break; }; 90 | str.push(character); 91 | }, 92 | None => { 93 | self.errors.push(err!(END_OF_STR, Range { start, end: self.last_loc })); 94 | break; 95 | } 96 | } 97 | }; 98 | Token { val: TokenType::Str(str), range: Range {start, end: self.input.loc()} } 99 | } 100 | 101 | fn parse_char(&mut self) -> Token { 102 | let start = self.input.loc(); 103 | self.input.consume(); // Consume the starting ' 104 | let maybe_ch = self.input.consume(); 105 | let val = match maybe_ch { 106 | Some(ch) => ch, 107 | None => { 108 | self.errors.push(err!(EMPTY_CHAR_LITERAL, self.range_here())); 109 | '_' 110 | } 111 | }; 112 | let next = self.input.consume(); 113 | if next == None || next.unwrap() != '\'' { 114 | self.errors.push(err!(ONE_CHAR_ENDPOINT, Range { start, end: self.last_loc })); 115 | } 116 | Token { val: TokenType::Char(val), range: Range { start, end: self.input.loc() }} 117 | } 118 | 119 | fn parse_num(&mut self) -> Token { 120 | let mut dot = false; 121 | let mut num = String::new(); 122 | let start = self.input.loc(); 123 | let mut num_type = NumberType::None; 124 | while !self.input.is_eof() { 125 | match self.input.peek(0) { 126 | Some(ch) => { 127 | match ch { 128 | '0' => { 129 | let next = self.input.peek(1); 130 | if next.is_none() { 131 | break; 132 | } 133 | match next.unwrap() { 134 | 'o' => { 135 | num_type = NumberType::Octal; 136 | self.input.consume(); 137 | self.input.consume(); 138 | }, 139 | 'x' => { 140 | num_type = NumberType::Hex; 141 | self.input.consume(); 142 | self.input.consume(); 143 | }, 144 | 'b' => { 145 | num_type = NumberType::Binary; 146 | self.input.consume(); 147 | self.input.consume(); 148 | }, 149 | _ => { 150 | num.push('0'); 151 | self.input.consume(); 152 | } 153 | } 154 | }, 155 | '1'..='9' => { 156 | match num_type { 157 | NumberType::Binary if ch > '1' => { 158 | self.errors.push(err!(INVALID_DIGIT, self.range_here())); 159 | num_type = NumberType::None; 160 | }, 161 | NumberType::Octal if ch > '7' => { 162 | self.errors.push(err!(INVALID_DIGIT, self.range_here())); 163 | num_type = NumberType::None; 164 | }, 165 | _ => {} 166 | }; 167 | num.push(self.input.consume().unwrap()) 168 | }, 169 | 'A'..='F' | 'a'..='f' => { 170 | if num_type == NumberType::Hex { 171 | num.push(self.input.consume().unwrap()) 172 | } else { 173 | self.errors.push(err!(INVALID_DIGIT, self.range_here())); 174 | break; 175 | } 176 | }, 177 | '.' => { 178 | if self.is_last_num_as_str { 179 | break; 180 | } 181 | if self.input.peek(1) == Some('.') { 182 | return Token { val: TokenType::Int(num.parse().unwrap()), range: Range {start, end: self.input.loc()} } 183 | } 184 | if dot { 185 | self.input.consume(); 186 | self.errors.push(err!(DECIMAL_POINT, Range {start, end: self.input.loc()})); 187 | break; 188 | }; 189 | self.input.consume(); 190 | dot = true; 191 | num.push(ch); 192 | }, 193 | '_' => { 194 | self.input.consume(); 195 | continue; 196 | }, 197 | _ => break 198 | } 199 | }, 200 | None => break 201 | } 202 | }; 203 | 204 | let multiply_val = match self.input.peek(0) { 205 | Some('s') => { 206 | self.input.consume(); 207 | 1000 208 | }, 209 | Some('m') => { 210 | self.input.consume(); 211 | 60 * 1000 212 | }, 213 | Some('h') => { 214 | self.input.consume(); 215 | 60 * 60 * 1000 216 | }, 217 | Some('d') => { 218 | self.input.consume(); 219 | 24 * 60 * 60 * 1000 220 | }, 221 | _ => 1 222 | } as isize; 223 | let token_type = match num_type { 224 | NumberType::Hex => TokenType::Int((isize::from_str_radix(&num, 16).unwrap() * multiply_val) as i32), 225 | NumberType::Octal => TokenType::Int((isize::from_str_radix(&num, 8).unwrap() * multiply_val) as i32), 226 | NumberType::Binary => TokenType::Int((isize::from_str_radix(&num, 2).unwrap() * multiply_val) as i32), 227 | NumberType::None => { 228 | if dot { TokenType::Float(num.parse::().unwrap() * (multiply_val as f32)) } 229 | else { TokenType::Int(num.parse::().unwrap() * (multiply_val as i32)) } 230 | } 231 | }; 232 | Token { val: token_type, range: Range {start, end: self.input.loc()} } 233 | } 234 | 235 | fn parse_ident(&mut self) -> Token { 236 | let mut ident = String::new(); 237 | let start = self.input.loc(); 238 | while !self.input.is_eof() { 239 | match self.input.peek(0) { 240 | Some(ch) => { 241 | match ch { 242 | 'a'..='z' | 'A'..='Z' | '0'..='9' | '_' => ident.push(self.input.consume().unwrap()), 243 | _ => break 244 | } 245 | }, 246 | None => break 247 | } 248 | }; 249 | if ident == "true" { return Token { val: TokenType::Bool(true), range: Range {start, end: self.input.loc()} } } 250 | else if ident == "false" { return Token { val: TokenType::Bool(false), range: Range {start, end: self.input.loc()} } } 251 | else if ident == "none" { return Token { val: TokenType::None, range: Range { start, end: self.input.loc() } } } 252 | 253 | let token_type = if match_str!(ident.as_str(), "main", "let", "for", "while", "if", "else", "enum", "struct", "fn", "type", "const", "yield", "match", "static", "new", "private", "export", "import", "as", "await", "impl", "in", "from") { TokenType::Kw(ident) } else { TokenType::Var(ident) }; 254 | Token { val: token_type, range: Range {start, end: self.input.loc() } } 255 | } 256 | 257 | fn parse_punc(&mut self) -> Token { 258 | let range = Range { start: self.input.loc(), end: self.input.loc() }; 259 | Token { val: TokenType::Punc(self.input.consume().unwrap()), range } 260 | } 261 | 262 | fn parse_op(&mut self) -> Token { 263 | let range = Range { start: self.input.loc(), end: self.input.loc() }; 264 | Token { val: TokenType::Op(self.input.consume().unwrap()), range } 265 | } 266 | 267 | pub fn parse_full_op(&mut self, start_of_op: Option) -> String { 268 | let mut op = if let Some(start_op) = start_of_op { start_op.to_string() } else { String::new() }; 269 | while !self.input.is_eof() { 270 | let ch = self.input.peek(0).unwrap(); 271 | if match_str!(ch, '+', '-', '>', '<', '=', '!', '%', '|', '&', '.', '?', '~', '^', '*', '/') { op.push(self.input.consume().unwrap()) } 272 | else { break; }; 273 | }; 274 | op 275 | } 276 | 277 | fn _next(&mut self) -> Option { 278 | if self.input.is_eof() { return None; }; 279 | self.last_loc = self.input.loc(); 280 | let tok = self.input.peek(0)?; 281 | if tok == '/' && self.input.peek(1)? == '/' { 282 | self.input.consume(); 283 | self.input.consume(); 284 | while !self.input.is_eof() { 285 | if self.input.consume()? == '\n' { break; }; 286 | } 287 | return self._next(); 288 | } 289 | if tok == '/' && self.input.peek(1)? == '*' { 290 | self.input.consume(); 291 | while !self.input.is_eof() { 292 | if self.input.consume()? == '*' && self.input.peek(0)? =='/' { break; }; 293 | } 294 | self.input.consume(); 295 | return self._next(); 296 | } 297 | match tok { 298 | '\'' => Some(self.parse_char()), 299 | '"' => Some(self.parse_str('"')), 300 | '`' => { 301 | self.input.consume(); 302 | Some(Token { 303 | val: TokenType::TempStrStart, 304 | range: self.range_here() 305 | }) 306 | }, 307 | '0'..='9' => Some(self.parse_num()), 308 | ' ' | '\n' | '\t' | '\r' => { 309 | self.input.consume(); 310 | self._next() 311 | }, 312 | '+' | '-' | '>' | '<' | '=' | '!' | '%' | '|' | '&' | '.' | '?' | '~' | '^' | '*' | '/' => Some(self.parse_op()), 313 | ',' | ':' | ';' | '{' | '}' | '[' | ']' | '(' | ')' | '#' => Some(self.parse_punc()), 314 | 'a'..='z' | 'A'..='Z' | '_' => Some(self.parse_ident()), 315 | ch => { 316 | let loc = self.input.loc(); 317 | self.input.consume(); 318 | if let Some(confused_err) = Self::is_confusable(ch) { 319 | self.errors.push(BaseError::new(confused_err, loc.to_range())); 320 | return None; 321 | }; 322 | self.errors.push(err!(INVALID_CHAR, loc.to_range(), &ch.to_string())); 323 | None 324 | } 325 | } 326 | } 327 | 328 | pub fn consume(&mut self) -> Option { 329 | if self.current.is_some() { 330 | self.current.take() 331 | } else { 332 | self._next() 333 | } 334 | } 335 | 336 | pub fn peek(&mut self) -> Option<&Token> { 337 | if self.current.is_some() { 338 | return self.current.as_ref(); 339 | } 340 | self.current = self._next(); 341 | self.current.as_ref() 342 | } 343 | 344 | #[inline] 345 | pub fn range_here(&self) -> Range { 346 | Range {start: self.last_loc, end: self.last_loc } 347 | } 348 | 349 | pub fn is_next(&mut self, tok: TokenType) -> bool { 350 | let next = self.peek(); 351 | match next { 352 | Some(token) => token.val == tok, 353 | None => true 354 | } 355 | } 356 | 357 | pub fn is_next_full_op(&mut self, op: &[char]) -> bool { 358 | if let Some(first_ch) = self.peek() { 359 | if first_ch.val != TokenType::Op(op[0]) { 360 | return false; 361 | }; 362 | for i in 1..op.len() { 363 | if self.input.peek(i - 1) == Some(op[i]) { 364 | continue; 365 | } else { 366 | return false; 367 | } 368 | } 369 | for _ in op { 370 | self.consume(); 371 | } 372 | return true; 373 | } else { 374 | false 375 | } 376 | } 377 | 378 | pub fn skip_or_err_full_op(&mut self, op: &str, err: Option) -> LazyResult<()> { 379 | let ch = if let Some(tok) = &self.current { 380 | match tok.val { 381 | TokenType::Op(ch) => { 382 | self.current = None; 383 | Some(ch) 384 | }, 385 | _ => None 386 | } 387 | } else { None }; 388 | let tok = self.parse_full_op(ch); 389 | if tok != op { 390 | Err(err.unwrap_or(err!(EXPECTED_FOUND, self.last_loc.to_range(), op, &tok))) 391 | } else { 392 | Ok(()) 393 | } 394 | } 395 | 396 | pub fn skip_or_err(&mut self, tok: TokenType, err: Option) -> LazyResult<()> { 397 | match self.peek() { 398 | Some(token) => { 399 | if token.val != tok { 400 | let other = token.val.to_string(); 401 | Err(err.unwrap_or(err!(EXPECTED_FOUND, self.last_loc.to_range(), &tok.to_string(), &other;))) 402 | } else { 403 | self.consume(); 404 | Ok(()) 405 | } 406 | }, 407 | None => { 408 | Err(err.unwrap_or(err!(EXPECTED, self.last_loc.to_range(), &tok.to_string()))) 409 | } 410 | } 411 | } 412 | 413 | pub fn expect_punc(&mut self, puncs: &[char], loc: Option) -> LazyResult { 414 | let location = loc.unwrap_or(Range { start: self.last_loc.clone(), end: self.last_loc.clone() }); 415 | match self.peek() { 416 | Some(tok) => { 417 | match tok.val { 418 | TokenType::Punc(punc) if puncs.contains(&punc) => { 419 | self.consume(); 420 | Ok(punc) 421 | }, 422 | _ => { 423 | let tstr = tok.val.to_string(); 424 | Err(err!(EXPECTED_FOUND, location, &format!("one of {}", puncs.iter().map(|i| format!("({})", i.to_string())).collect::>().join(", ")), &tstr)) 425 | } 426 | } 427 | }, 428 | None => { 429 | Err(err!(EXPECTED, location, &format!("one of {}", puncs.iter().map(|i| format!("({})", i.to_string())).collect::>().join(", ")))) 430 | } 431 | } 432 | } 433 | 434 | pub fn is_confusable(ch: char) -> Option { 435 | match ch { 436 | ';' => Some(dia!(CONFUSABLE, "; (Greek question mark)", "; (semicolon)")), 437 | '‚' => Some(dia!(CONFUSABLE, "‚ (low-9 quatation mark)", ", (comma)")), 438 | '٫' => Some(dia!(CONFUSABLE, "‚ (arabic decimal separator)", ", (comma)")), 439 | ':' => Some(dia!(CONFUSABLE, ": (fullwidth colon)", ": (colon)")), 440 | '։' => Some(dia!(CONFUSABLE, ": (armenian full stop)", ": (colon)")), 441 | '∶' => Some(dia!(CONFUSABLE, "∶ (ratio)", ": (colon)")), 442 | '!' => Some(dia!(CONFUSABLE, "! (fullwidth exclamation mark)", "! (exclamation mark)")), 443 | 'ǃ' => Some(dia!(CONFUSABLE, "ǃ (latin letter retroflex click)", "! (exclamation mark)")), 444 | '․' => Some(dia!(CONFUSABLE, "․ (one dot leader)", ". (full stop)")), 445 | _ => None 446 | } 447 | } 448 | 449 | } -------------------------------------------------------------------------------- /src/parser/src/ast/model.rs: -------------------------------------------------------------------------------- 1 | 2 | use std::fmt; 3 | pub use errors::{Range}; 4 | use super::{TokenType}; 5 | pub use std::collections::hash_map::HashMap; 6 | 7 | // A string literal 8 | pub struct ASTStr { 9 | pub value: String, 10 | pub range: Range 11 | } 12 | 13 | pub struct ASTTempStr { 14 | pub template: String, 15 | pub values: HashMap, 16 | pub range: Range 17 | } 18 | 19 | // A floating point literal 20 | pub struct ASTFloat { 21 | pub value: f32, 22 | pub range: Range 23 | } 24 | 25 | // An integer literal 26 | pub struct ASTInt { 27 | pub value: i32, 28 | pub range: Range 29 | } 30 | 31 | // A boolean literal 32 | pub struct ASTBool { 33 | pub value: bool, 34 | pub range: Range 35 | } 36 | 37 | // A variable / typing name 38 | pub struct ASTVar { 39 | pub value: String, 40 | pub range: Range 41 | } 42 | 43 | pub struct ASTVarList { 44 | pub values: Vec, 45 | pub range: Range 46 | } 47 | 48 | pub enum ASTDeclareTypes { 49 | TupleDeconstruct(ASTVarList), 50 | StructDeconstruct(ASTVarList), 51 | Var(ASTVar) 52 | } 53 | 54 | pub struct ASTDeclare { 55 | pub var: ASTDeclareTypes, 56 | pub is_const: bool, 57 | pub value: Option>, 58 | pub typings: Option, 59 | pub range: Range, 60 | } 61 | 62 | pub struct ASTStatic { 63 | pub var: ASTVar, 64 | pub typings: Option, 65 | pub value: ASTExpression, 66 | pub range: Range 67 | } 68 | 69 | pub struct ASTTypeParameter { 70 | pub name: ASTVar, 71 | pub constraint: Option, 72 | pub range: Range 73 | } 74 | 75 | pub struct ASTStruct { 76 | pub name: ASTVar, 77 | pub fields: ASTPairListTyping, 78 | pub typings: Vec, 79 | pub range: Range 80 | } 81 | 82 | // A key value pair list 83 | pub struct ASTPairList { 84 | pub pairs: Vec<(String, Option)>, 85 | pub range: Range 86 | } 87 | 88 | // A binary expression 89 | pub struct ASTBinary { 90 | pub op: String, 91 | pub left: Box, 92 | pub right: Box, 93 | pub range: Range 94 | } 95 | 96 | pub struct ASTUnary { 97 | pub op: String, 98 | pub value: Box, 99 | pub range: Range 100 | } 101 | 102 | pub struct ASTDotAccess { 103 | pub value: Box, 104 | pub target: ASTVar, 105 | pub range: Range 106 | } 107 | 108 | pub struct ASTIndexAccess { 109 | pub value: Box, 110 | pub target: Box, 111 | pub range: Range 112 | } 113 | 114 | pub struct ASTOptional { 115 | pub value: Box, 116 | pub range: Range 117 | } 118 | 119 | pub struct ASTEnumDeclaration { 120 | pub name: ASTVar, 121 | pub values: ASTPairListTyping, 122 | pub typings: Vec, 123 | pub range: Range 124 | } 125 | 126 | pub struct ASTFunction { 127 | pub params: Box, 128 | pub body: Option>, 129 | pub return_type: Option>, 130 | pub typings: Vec, 131 | pub range: Range 132 | } 133 | 134 | pub struct ASTBlock { 135 | pub elements: Vec, 136 | pub range: Range 137 | } 138 | 139 | pub struct ASTInitializor { 140 | pub target: ASTModAccessValues, 141 | pub params: ASTPairList, 142 | pub typings: Option, 143 | pub range: Range 144 | } 145 | 146 | pub struct ASTIterator { 147 | pub start: Box, 148 | pub end: Box, 149 | pub inclusive: bool, 150 | pub range: Range 151 | } 152 | 153 | pub struct ASTIf { 154 | pub condition: Box, 155 | pub then: Box, 156 | pub otherwise: Option>, 157 | pub range: Range 158 | } 159 | 160 | pub struct ASTChar { 161 | pub value: char, 162 | pub range: Range 163 | } 164 | 165 | pub enum ASTModAccessValues { 166 | ModAccess(ASTModAccess), 167 | Var(ASTVarTyping) 168 | } 169 | 170 | pub struct ASTModAccess { 171 | pub path: Vec, 172 | pub init: Option, 173 | pub typings: Option, 174 | pub range: Range 175 | } 176 | 177 | pub struct ASTCall { 178 | pub target: Box, 179 | pub args: ASTExpressionList, 180 | pub typings: Option, 181 | pub range: Range 182 | } 183 | 184 | pub struct ASTForIn { 185 | pub var: ASTVar, 186 | pub iterable: Box, 187 | pub body: Box, 188 | pub range: Range 189 | } 190 | 191 | pub struct ASTWhile { 192 | pub condition: Box, 193 | pub body: Box, 194 | pub range: Range 195 | } 196 | 197 | pub struct ASTType { 198 | pub name: ASTVar, 199 | pub typings: Vec, 200 | pub value: ASTTypings, 201 | pub range: Range 202 | } 203 | 204 | pub struct ASTExpressionList { 205 | pub expressions: Vec, 206 | pub range: Range 207 | } 208 | 209 | pub struct ASTYield { 210 | pub value: Option>, 211 | pub range: Range 212 | } 213 | 214 | pub struct ASTSpread { 215 | pub value: Box, 216 | pub range: Range 217 | } 218 | 219 | pub struct ASTMain { 220 | pub expression: ASTBlock, 221 | pub range: Range 222 | } 223 | 224 | pub enum ASTMatchArmExpressions { 225 | String(ASTStr), 226 | Int(ASTInt), 227 | Float(ASTFloat), 228 | Iterator(ASTIterator), 229 | Char(ASTChar), 230 | Bool(ASTBool), 231 | Tuple(ASTExpressionList), 232 | None(Range), 233 | Rest, 234 | Enum(ASTModAccess), 235 | EnumVar(ASTModAccess) 236 | } 237 | 238 | pub struct ASTMatchArm { 239 | pub possibilities: Vec, 240 | pub guard: Option, 241 | pub body: ASTExpression, 242 | pub range: Range 243 | } 244 | 245 | pub struct ASTMatch { 246 | pub arms: Vec, 247 | pub expression: Box, 248 | pub range: Range 249 | } 250 | 251 | pub struct ASTExport { 252 | pub value: Box, 253 | pub range: Range 254 | } 255 | 256 | pub struct ASTImportItem { 257 | pub name: String, 258 | pub r#as: Option, 259 | pub range: Range 260 | } 261 | 262 | pub enum ASTImportThing { 263 | All, 264 | Items(Vec) 265 | } 266 | 267 | pub struct ASTImport { 268 | pub path: ASTStr, 269 | pub thing: ASTImportThing, 270 | pub r#as: Option, 271 | pub range: Range 272 | } 273 | 274 | pub struct ASTAwait { 275 | pub optional: bool, 276 | pub expression: Box, 277 | pub range: Range 278 | } 279 | 280 | pub struct ASTImpl { 281 | pub partial: ASTModAccessValues, 282 | pub target: ASTModAccessValues, 283 | pub typings: Option, 284 | pub fields: ASTPairListTyping, 285 | pub range: Range 286 | } 287 | 288 | pub struct ASTMeta { 289 | pub name: ASTVar, 290 | pub args: Vec, 291 | pub target: Box, 292 | pub range: Range 293 | } 294 | 295 | // Any expression 296 | pub enum ASTExpression { 297 | Str(ASTStr), 298 | TempStr(ASTTempStr), 299 | Float(ASTFloat), 300 | Int(ASTInt), 301 | Bool(ASTBool), 302 | Var(ASTVar), 303 | Char(ASTChar), 304 | Binary(ASTBinary), 305 | Unary(ASTUnary), 306 | DotAccess(ASTDotAccess), 307 | IndexAccess(ASTIndexAccess), 308 | ModAccess(ASTModAccess), 309 | Optional(ASTOptional), 310 | Block(ASTBlock), 311 | Function(ASTFunction), 312 | Init(ASTInitializor), 313 | Iterator(ASTIterator), 314 | Call(ASTCall), 315 | ForIn(ASTForIn), 316 | While(ASTWhile), 317 | If(ASTIf), 318 | Declare(ASTDeclare), 319 | Tuple(ASTExpressionList), 320 | Yield(ASTYield), 321 | Spread(ASTSpread), 322 | None(Range), 323 | Match(ASTMatch), 324 | Await(ASTAwait) 325 | } 326 | 327 | // Any statement 328 | pub enum ASTStatement { 329 | EnumDeclaration(ASTEnumDeclaration), 330 | Struct(ASTStruct), 331 | Static(Box), 332 | Type(ASTType), 333 | Main(ASTMain), 334 | Export(ASTExport), 335 | Import(ASTImport), 336 | Meta(ASTMeta), 337 | Impl(ASTImpl) 338 | } 339 | 340 | impl ASTStatement { 341 | 342 | pub fn range(&self) -> Range { 343 | match self { 344 | Self::EnumDeclaration(en) => en.range, 345 | Self::Struct(ty) => ty.range, 346 | Self::Static(st) => st.range, 347 | Self::Type(ty) => ty.range, 348 | Self::Main(main) => main.range, 349 | Self::Export(ex) => ex.range, 350 | Self::Import(im) => im.range, 351 | Self::Meta(m) => m.range, 352 | Self::Impl(im) => im.range 353 | } 354 | } 355 | } 356 | 357 | bitflags! { 358 | pub struct ASTModifiers: u32 { 359 | const PRIVATE = 1 << 0; 360 | const STATIC = 1 << 1; 361 | const CONST = 1 << 2; 362 | } 363 | } 364 | 365 | impl ASTModifiers { 366 | pub fn clear(&mut self) { 367 | self.bits = 0; 368 | } 369 | } 370 | 371 | pub struct ASTPairTypingItem { 372 | pub name: String, 373 | pub value: Option, 374 | pub spread: bool, 375 | pub default_value: Option, 376 | pub modifiers: ASTModifiers 377 | } 378 | 379 | pub struct ASTPairListTyping { 380 | pub pairs: Vec, 381 | pub range: Range 382 | } 383 | 384 | pub struct ASTListTyping { 385 | pub entries: Vec, 386 | pub range: Range 387 | } 388 | 389 | pub struct ASTVarTyping { 390 | pub value: ASTVar, 391 | pub typings: Option, 392 | pub range: Range 393 | } 394 | 395 | pub struct ASTCombineTyping { 396 | pub left: Box, 397 | pub right: Box, 398 | pub range: Range 399 | } 400 | 401 | pub struct ASTImplTyping { 402 | pub value: Box, 403 | pub range: Range 404 | } 405 | 406 | pub enum ASTTypings { 407 | Var(ASTVarTyping), 408 | Mod(ASTModAccess), 409 | PairList(ASTPairListTyping), 410 | Function(ASTFunction), 411 | Optional(Box), 412 | Tuple(ASTListTyping), 413 | Combine(ASTCombineTyping), 414 | Impl(ASTImplTyping) 415 | } 416 | 417 | impl fmt::Display for ASTVarTyping { 418 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 419 | write!(f, "{}{}", self.value, if self.typings.is_some() { format!("<{}>", self.typings.as_ref().unwrap().to_string()) } else { String::from("") }) 420 | } 421 | } 422 | 423 | impl fmt::Display for ASTPairListTyping { 424 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 425 | let mut string: Vec = vec![]; 426 | for pair in &self.pairs { 427 | let modifiers = { 428 | let mut mods = String::new(); 429 | if pair.modifiers.contains(ASTModifiers::CONST) { mods += "const " }; 430 | if pair.modifiers.contains(ASTModifiers::STATIC) { mods += "static "}; 431 | if pair.modifiers.contains(ASTModifiers::PRIVATE) { mods += "private " }; 432 | mods 433 | }; 434 | string.push(format!("{}{}{}{}{}", modifiers, if pair.spread { "..." } else {""}, pair.name, if pair.value.is_some() { format!(": {}", pair.value.as_ref().unwrap()) } else { String::from("")}, if pair.default_value.is_some() { format!(" = {}", pair.default_value.as_ref().unwrap()) } else { String::from("") })); 435 | }; 436 | write!(f, "{}", string.join(", ")) 437 | } 438 | } 439 | 440 | impl fmt::Display for ASTFunction { 441 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 442 | write!(f, "fn<{}>({}) -> {} {}", self.typings.iter().map(|t| t.to_string()).collect::>().join(", "), self.params, if self.return_type.is_some() { self.return_type.as_ref().unwrap().to_string() } else { String::from("none") } ,if self.body.is_some() { self.body.as_ref().unwrap().to_string() } else { String::from("") }) 443 | } 444 | } 445 | 446 | impl fmt::Display for ASTListTyping { 447 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 448 | let mut string: Vec = vec![]; 449 | for entry in &self.entries { 450 | string.push(entry.to_string()); 451 | }; 452 | write!(f, "{}", string.join(", ")) 453 | } 454 | } 455 | 456 | impl fmt::Display for ASTExpression { 457 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 458 | match &self { 459 | Self::Str(str) => str.fmt(f), 460 | Self::Bool(boolean) => boolean.fmt(f), 461 | Self::Int(i) => i.fmt(f), 462 | Self::Float(fl) => fl.fmt(f), 463 | Self::Binary(bin) => bin.fmt(f), 464 | Self::Unary(un) => un.fmt(f), 465 | Self::Var(variable) => variable.fmt(f), 466 | Self::Optional(op) => op.fmt(f), 467 | Self::DotAccess(op) => op.fmt(f), 468 | Self::IndexAccess(op) => op.fmt(f), 469 | Self::Block(block) => block.fmt(f), 470 | Self::Function(func) => func.fmt(f), 471 | Self::Declare(st) => st.fmt(f), 472 | Self::Init(initializor) => initializor.fmt(f), 473 | Self::Iterator(it) => it.fmt(f), 474 | Self::If(exp) => exp.fmt(f), 475 | Self::Char(ch) => ch.fmt(f), 476 | Self::ModAccess(e) => e.fmt(f), 477 | Self::Call(call) => call.fmt(f), 478 | Self::ForIn(for_loop) => for_loop.fmt(f), 479 | Self::While(while_loop) => while_loop.fmt(f), 480 | Self::Tuple(tup) => write!(f, "[{}]", tup.to_string()), 481 | Self::Yield(y) => y.fmt(f), 482 | Self::Spread(sp) => write!(f, "...{}", sp.value.to_string()), 483 | Self::Match(mtch) => mtch.fmt(f), 484 | Self::Await(aw) => aw.fmt(f), 485 | Self::TempStr(tmp) => tmp.fmt(f), 486 | Self::None(_) => write!(f, "none") 487 | } 488 | } 489 | } 490 | 491 | 492 | impl fmt::Display for ASTTypings { 493 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 494 | match &self { 495 | Self::Tuple(tup) => write!(f, "[{}]", tup), 496 | Self::Var(var) => var.fmt(f), 497 | Self::PairList(list) => list.fmt(f), 498 | Self::Optional(typing) => write!(f, "{}?", typing), 499 | Self::Function(func) => func.fmt(f), 500 | Self::Combine(c) => c.fmt(f), 501 | Self::Mod(m) => m.fmt(f), 502 | Self::Impl(b) => write!(f, "impl {}", b.value) 503 | } 504 | } 505 | } 506 | 507 | impl fmt::Display for ASTStatement { 508 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 509 | match &self { 510 | Self::Struct(structure) => structure.fmt(f), 511 | Self::EnumDeclaration(en) => en.fmt(f), 512 | Self::Type(typing) => typing.fmt(f), 513 | Self::Static(st) => st.fmt(f), 514 | Self::Main(m) => m.fmt(f), 515 | Self::Export(ex) => ex.fmt(f), 516 | Self::Import(imp) => imp.fmt(f), 517 | Self::Impl(imp) => imp.fmt(f), 518 | Self::Meta(m) => m.fmt(f) 519 | } 520 | } 521 | } 522 | 523 | impl fmt::Display for ASTMain { 524 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 525 | write!(f, "main {}", self.expression) 526 | } 527 | } 528 | 529 | impl fmt::Display for ASTStr { 530 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 531 | write!(f, "\"{}\"", self.value) 532 | } 533 | } 534 | 535 | impl fmt::Display for ASTInt { 536 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 537 | write!(f, "{}", self.value) 538 | } 539 | } 540 | 541 | impl fmt::Display for ASTFloat { 542 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 543 | write!(f, "{}", self.value) 544 | } 545 | } 546 | 547 | impl fmt::Display for ASTVar { 548 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 549 | write!(f, "{}", self.value) 550 | } 551 | } 552 | 553 | impl fmt::Display for ASTBool { 554 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 555 | write!(f, "{}", self.value) 556 | } 557 | } 558 | 559 | impl fmt::Display for ASTChar { 560 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 561 | write!(f, "'{}'", self.value) 562 | } 563 | } 564 | 565 | impl fmt::Display for ASTBinary { 566 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 567 | write!(f, "{} {} {}", self.left, self.op, self.right) 568 | } 569 | } 570 | 571 | impl fmt::Display for ASTUnary { 572 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 573 | write!(f, "{}{}", self.op, self.value) 574 | } 575 | } 576 | 577 | impl fmt::Display for ASTIterator { 578 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 579 | write!(f, "{}..{}{}", self.start, if self.inclusive {"="} else {""}, self.end) 580 | } 581 | } 582 | 583 | 584 | impl fmt::Display for ASTDotAccess { 585 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 586 | write!(f, "{}.{}", self.value, self.target) 587 | } 588 | } 589 | 590 | impl fmt::Display for ASTPairList { 591 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 592 | let mut string = String::new(); 593 | for ind in 0..self.pairs.len() { 594 | let pair = &self.pairs[ind]; 595 | string.push_str(&format!("{}: {}{}", pair.0, if pair.1.is_some() { pair.1.as_ref().unwrap().to_string() } else { String::from("{}") }, if ind == self.pairs.len() - 1 { "" } else { ", " })); 596 | }; 597 | write!(f, "{{ {} }}", string) 598 | } 599 | } 600 | 601 | impl fmt::Display for ASTCall { 602 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 603 | write!(f, "{}{}({})", self.target, if self.typings.is_some() { format!("<{}>", self.typings.as_ref().unwrap()) } else { String::from("") }, self.args) 604 | } 605 | } 606 | 607 | impl fmt::Display for ASTModAccess { 608 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 609 | let mut path = String::new(); 610 | for path_part in 0..self.path.len() { 611 | if path_part != 0 { path += "::" }; 612 | path += &self.path[path_part].to_string(); 613 | } 614 | write!(f, "{}{}{}", path, if self.typings.is_some() { format!("<{}>", self.typings.as_ref().unwrap().to_string()) } else { String::from("") }, if self.init.is_some() { format!("({})", self.init.as_ref().unwrap().to_string()) } else { String::from("") }) 615 | } 616 | } 617 | 618 | impl fmt::Display for ASTModAccessValues { 619 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 620 | match self { 621 | Self::ModAccess(m) => m.fmt(f), 622 | Self::Var(v) => v.fmt(f) 623 | } 624 | } 625 | } 626 | 627 | impl fmt::Display for ASTInitializor { 628 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 629 | write!(f, "new {}{}{}", self.target, if self.typings.is_some() { format!("<{}>", self.typings.as_ref().unwrap()) } else { String::from("") }, self.params) 630 | } 631 | } 632 | 633 | impl fmt::Display for ASTForIn { 634 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 635 | write!(f, "for {} in {} {{\n {} \n}}", self.var, self.iterable, self.body) 636 | } 637 | } 638 | 639 | impl fmt::Display for ASTWhile { 640 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 641 | write!(f, "while {} {{\n {} \n}}",self.condition, self.body) 642 | } 643 | } 644 | 645 | impl fmt::Display for ASTBlock { 646 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 647 | let mut string = String::new(); 648 | for exp in &self.elements { 649 | string.push_str(&format!("\n{}", exp)); 650 | } 651 | write!(f, "{{{} \n}}", string) 652 | } 653 | } 654 | 655 | impl fmt::Display for ASTDeclare { 656 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 657 | writeln!(f, "{} {}{} = {}", if self.is_const { "const" } else { "let" },self.var, if self.typings.is_some() { format!(": {}", self.typings.as_ref().unwrap().to_string()) } else { String::from("") }, if self.value.is_some() { self.value.as_ref().unwrap().to_string()} else { String::from("none") }) 658 | } 659 | } 660 | 661 | impl fmt::Display for ASTType { 662 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 663 | writeln!(f, "type {}{} = {}", self.name, if !self.typings.is_empty() { format!("<{}>", self.typings.iter().map(|t| t.to_string()).collect::>().join(", ")) } else { String::from("") }, self.value) 664 | } 665 | } 666 | 667 | impl fmt::Display for ASTEnumDeclaration { 668 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 669 | writeln!(f, "enum {}{} {{\n {} }}", self.name, if !self.typings.is_empty() { format!("<{}>", self.typings.iter().map(|t| t.to_string()).collect::>().join(", ") )} else { String::from("") }, self.values) 670 | } 671 | } 672 | 673 | impl fmt::Display for ASTStruct { 674 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 675 | write!(f, "struct {}{} {{\n {} }}\n", self.name, if !self.typings.is_empty() { format!("<{}>", self.typings.iter().map(|t| t.to_string()).collect::>().join(", ")) } else { String::from("") }, self.fields) 676 | } 677 | } 678 | 679 | impl fmt::Display for ASTOptional { 680 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 681 | write!(f, "{}?", self.value) 682 | } 683 | } 684 | 685 | impl fmt::Display for ASTIf { 686 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 687 | write!(f, "if {} {} {}", self.condition, self.then, if self.otherwise.is_some() { format!("else {}", self.otherwise.as_ref().unwrap()) } else {String::from("")}) 688 | } 689 | } 690 | 691 | impl fmt::Display for ASTExpressionList { 692 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 693 | let mut string: Vec = vec![]; 694 | for exp in &self.expressions { 695 | string.push(exp.to_string()); 696 | } 697 | write!(f, "{}", string.join(", ")) 698 | } 699 | } 700 | 701 | impl fmt::Display for ASTYield { 702 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 703 | write!(f, "yield {}", if self.value.is_some() { self.value.as_ref().unwrap().to_string() } else { String::from(";") }) 704 | } 705 | } 706 | 707 | impl fmt::Display for ASTMatch { 708 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 709 | let mut str = String::new(); 710 | for arm in &self.arms { 711 | str.push_str(&format!("{}\n", arm)); 712 | }; 713 | write!(f, "match {} {{\n{}}}", self.expression, str) 714 | } 715 | } 716 | 717 | impl fmt::Display for ASTMatchArm { 718 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 719 | write!(f, "{} {} => {}", self.possibilities.iter().map(|i| i.to_string()).collect::>().join(" | "), if self.guard.is_some() { format!("when {}", self.guard.as_ref().unwrap()) } else { String::from("")}, self.body) 720 | } 721 | } 722 | 723 | impl fmt::Display for ASTMatchArmExpressions { 724 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 725 | match self { 726 | Self::Char(ch) => ch.fmt(f), 727 | Self::String(st) => st.fmt(f), 728 | Self::Int(int) => int.fmt(f), 729 | Self::Float(fl) => fl.fmt(f), 730 | Self::Iterator(iter) => iter.fmt(f), 731 | Self::Enum(en) | Self::EnumVar(en) => en.fmt(f), 732 | Self::Tuple(t) => write!(f, "[{}]", t), 733 | Self::Bool(b) => b.fmt(f), 734 | Self::None(_) => write!(f, "none"), 735 | Self::Rest => write!(f, "_") 736 | } 737 | } 738 | } 739 | 740 | 741 | impl fmt::Display for ASTStatic { 742 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 743 | writeln!(f, "static {}{} = {}", self.var, if self.typings.is_some() { format!("<{}>", self.typings.as_ref().unwrap().to_string()) } else { String::from("") }, self.value) 744 | } 745 | } 746 | 747 | impl fmt::Display for ASTExport { 748 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 749 | writeln!(f, "export {}", self.value) 750 | } 751 | } 752 | 753 | impl fmt::Display for ASTImportThing { 754 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 755 | match self { 756 | ASTImportThing::All => writeln!(f, "*"), 757 | ASTImportThing::Items(items) => writeln!(f, "{{ {} }}", items.iter().map(|i| format!("{}{}", i.name, if let Some(ass) = &i.r#as { ass.to_string() } else { String::new() })).collect::>().join(", ")) 758 | } 759 | } 760 | } 761 | 762 | impl fmt::Display for ASTImport { 763 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 764 | writeln!(f, "import {}{}{}", self.thing, self.path, if let Some(ass) = &self.r#as { ass.to_string() } else { String::new() }) 765 | } 766 | } 767 | 768 | impl fmt::Display for ASTAwait { 769 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 770 | writeln!(f, "await{} {}", if self.optional { "?" } else { "" }, self.expression) 771 | } 772 | } 773 | 774 | impl fmt::Display for ASTCombineTyping { 775 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 776 | write!(f, "{} + {}", self.left, self.right) 777 | } 778 | } 779 | 780 | impl fmt::Display for ASTImpl { 781 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 782 | write!(f, "impl{} {} for {} {{\n{}\n}}", if let Some(t) = &self.typings { format!("<{}>", t) } else { String::from("") }, self.partial, self.target, self.fields) 783 | } 784 | } 785 | 786 | impl fmt::Display for ASTVarList { 787 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 788 | write!(f, "{}", self.values.iter().map(|v| v.to_string()).collect::>().join(", ")) 789 | } 790 | } 791 | 792 | impl fmt::Display for ASTDeclareTypes { 793 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 794 | match self { 795 | Self::Var(v) => v.fmt(f), 796 | Self::TupleDeconstruct(vars) => write!(f, "[{}]", vars), 797 | Self::StructDeconstruct(vars) => write!(f, "{{{}}}", vars) 798 | } 799 | } 800 | } 801 | 802 | impl fmt::Display for ASTMeta { 803 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 804 | write!(f, "#{}({})\n{}", self.name, self.args.iter().map(|v| v.to_string()).collect::>().join(", "), self.target) 805 | } 806 | } 807 | 808 | impl fmt::Display for ASTIndexAccess { 809 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 810 | write!(f, "{}[{}]", self.value, self.target) 811 | } 812 | } 813 | 814 | impl fmt::Display for ASTTempStr { 815 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 816 | let mut new_str = String::new(); 817 | for (ind, ch) in self.template.chars().enumerate() { 818 | if let Some(k) = self.values.get(&ind) { 819 | new_str.push_str(&format!("${{{}}}", k)); 820 | } else { 821 | new_str.push(ch); 822 | }; 823 | } 824 | write!(f, "`{}`", new_str) 825 | } 826 | } 827 | 828 | impl fmt::Display for ASTTypeParameter { 829 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 830 | write!(f, "{}{}", self.name.value, if let Some(c) = &self.constraint { format!(": {}", c) } else { String::from("") }) 831 | } 832 | } -------------------------------------------------------------------------------- /src/parser/src/ast/mod.rs: -------------------------------------------------------------------------------- 1 | 2 | use super::tokenizer::{Tokenizer, TokenType}; 3 | pub use errors::{LoC}; 4 | pub mod model; 5 | pub mod utils; 6 | use model::*; 7 | use errors::*; 8 | 9 | pub struct Parser<'a> { 10 | pub tokens: Tokenizer<'a>, 11 | is_last_block: bool, 12 | allow_exp_statements: bool, 13 | parsed_main: bool 14 | } 15 | 16 | impl<'a> Parser<'a> { 17 | 18 | pub fn new(source: &str, err_collector: &'a mut ErrorCollector) -> Self { 19 | Parser { 20 | tokens: Tokenizer::new(source, err_collector), 21 | parsed_main: false, 22 | is_last_block: false, 23 | allow_exp_statements: false 24 | } 25 | } 26 | 27 | fn get_prec(op: &str) -> i8 { 28 | match op { 29 | "=" | "+=" | "-=" | "*=" | "/=" | "%=" => 1, 30 | "&" | "|" | "^" => 2, 31 | "||" => 5, 32 | "&&" => 7, 33 | "<" | ">" | "<=" | ">=" | "==" | "!=" => 10, 34 | "<<" | ">>" | ">>>" => 11, 35 | "+" | "-" => 15, 36 | "*" | "/" | "%" => 20, 37 | _ => -1 38 | } 39 | } 40 | 41 | fn parse_binary(&mut self, left_tok: ASTExpression, prec: i8) -> LazyResult { 42 | let start = self.tokens.input.loc(); 43 | let next = self.tokens.peek(); 44 | if next.is_none() { 45 | return Ok(left_tok) 46 | }; 47 | if let TokenType::Op(op_start) = next.unwrap().val { 48 | let opval = self.tokens.parse_full_op(Some(op_start)); 49 | let other_prec = Self::get_prec(&opval); 50 | if other_prec == -1 { 51 | return Ok(left_tok) 52 | } 53 | if other_prec > prec { 54 | self.tokens.consume(); 55 | let exp = if let Some(exp) = self.parse_expression_part(false)? { 56 | exp 57 | } else { 58 | return Ok(left_tok); 59 | }; 60 | let right = self.parse_binary(exp, other_prec)?; 61 | return self.parse_binary(ASTExpression::Binary(ASTBinary { 62 | op: opval.to_string(), 63 | left: Box::from(left_tok), 64 | right: Box::from(right), 65 | range: start.end(&self.tokens.last_loc) 66 | }), prec); 67 | } 68 | Ok(left_tok) 69 | } else { 70 | Ok(left_tok) 71 | } 72 | } 73 | 74 | fn parse_suffix(&mut self, token: ASTExpression, parse_generics: bool) -> LazyResult { 75 | let start = self.tokens.input.loc(); 76 | let next_token = if let Some(t) = self.tokens.peek() { t } else { 77 | return Ok(token); 78 | }; 79 | match &next_token.val { 80 | TokenType::Op(val) => { 81 | match val { 82 | '.' => { 83 | self.tokens.consume(); 84 | if self.tokens.is_next(TokenType::Op('.')) { 85 | self.tokens.consume(); 86 | let mut inclusive = false; 87 | if self.tokens.is_next(TokenType::Op('=')) { 88 | inclusive = true; 89 | self.tokens.consume(); 90 | } 91 | let end = if let Some(end) = self.parse_expression_part(true)? { end } else { 92 | return Err(err!(END_OF_ITER, start.end(&self.tokens.last_loc))); 93 | }; 94 | return Ok(ASTExpression::Iterator( 95 | ASTIterator { 96 | start: Box::from(token), 97 | end: Box::from(end), 98 | inclusive, 99 | range: start.end(&self.tokens.last_loc) 100 | } 101 | )); 102 | } 103 | let target = self.parse_varname(true, false, !matches!(token, ASTExpression::Int(_) | ASTExpression::Float(_)), true)?.0; 104 | self.parse_suffix(ASTExpression::DotAccess( 105 | ASTDotAccess { 106 | target: target, 107 | value: Box::from(token), 108 | range: start.end(&self.tokens.last_loc) 109 | } 110 | ), parse_generics) 111 | }, 112 | '?' => { 113 | self.tokens.consume(); 114 | self.parse_suffix(ASTExpression::Optional( 115 | ASTOptional { 116 | value: Box::from(token), 117 | range: start.end(&self.tokens.last_loc) 118 | } 119 | ), parse_generics) 120 | }, 121 | _ => Ok(token) 122 | } 123 | }, 124 | TokenType::Punc(punc) => { 125 | match punc { 126 | '(' => { 127 | self.tokens.consume(); 128 | let args = self.parse_expression_list(')')?; 129 | self.parse_suffix(ASTExpression::Call( 130 | ASTCall { 131 | target: Box::from(token), 132 | typings: None, 133 | args, 134 | range: start.end(&self.tokens.last_loc) 135 | } 136 | ), parse_generics) 137 | }, 138 | '[' => { 139 | self.tokens.consume(); 140 | let target = if let Some(exp) = self.parse_expression()? { Box::from(exp) } else { 141 | return Err(err!(EXPECTED, start.end(&self.tokens.last_loc), "expression")); 142 | }; 143 | self.tokens.skip_or_err(TokenType::Punc(']'), None)?; 144 | self.parse_suffix(ASTExpression::IndexAccess( 145 | ASTIndexAccess { 146 | target: Box::from(target), 147 | value: Box::from(token), 148 | range: start.end(&self.tokens.last_loc) 149 | } 150 | ), parse_generics) 151 | }, 152 | ':' => { 153 | if let ASTExpression::Var(v) = token { 154 | match self.parse_mod_access_or_var(v, true, true)? { 155 | ASTModAccessValues::ModAccess(mod_access) => Ok(ASTExpression::ModAccess(mod_access)), 156 | ASTModAccessValues::Var(v) => Ok(ASTExpression::Var(v.value)) 157 | } 158 | } else { 159 | Err(err!(EXPECTED, self.tokens.range_here(), "identifier")) 160 | } 161 | } 162 | _ => Ok(token) 163 | } 164 | } 165 | _ => Ok(token) 166 | } 167 | } 168 | 169 | pub fn parse_mod_access_or_var_without_var(&mut self, allow_exp_end: bool, allow_typings: bool) -> LazyResult { 170 | let name = self.parse_varname(false, false, false, false)?.0; 171 | self.parse_mod_access_or_var(name, allow_exp_end, allow_typings) 172 | } 173 | 174 | pub fn parse_mod_access_or_var(&mut self, start: ASTVar, allow_exp_end: bool, allow_typings: bool) -> LazyResult { 175 | if !self.tokens.is_next(TokenType::Punc(':')) { 176 | let r = start.range; 177 | let typings = if self.tokens.is_next(TokenType::Op('<')) { 178 | if !allow_typings { 179 | return Err(err!(UNEXPECTED, self.tokens.range_here(), "typings")); 180 | } 181 | self.tokens.consume(); 182 | Some(self.parse_typing_list(false, false, TokenType::Op('>'))?) 183 | } else { None }; 184 | return Ok(ASTModAccessValues::Var(ASTVarTyping { value: start, range: r, typings })); 185 | }; 186 | 187 | let mut path: Vec = vec![start]; 188 | let start = self.tokens.input.loc(); 189 | while self.tokens.is_next(TokenType::Punc(':')) { 190 | self.tokens.consume(); 191 | self.tokens.skip_or_err(TokenType::Punc(':'), Some(err!(EXPECTED, self.tokens.range_here(), "Another colon (:)"; ["Add another colon to make the mod access expression (Module::Item)"])))?; 192 | if let Some(tok) = self.tokens.consume() { 193 | match tok.val { 194 | TokenType::Var(v) => path.push(ASTVar { value: v, range: tok.range }), 195 | TokenType::Kw(v) => path.push(ASTVar { value: v, range: tok.range}), 196 | _ => { 197 | if !allow_exp_end { 198 | return Err(err!(UNEXPECTED, self.tokens.range_here(), "expression")); 199 | } 200 | break; 201 | } 202 | } 203 | } 204 | }; 205 | let typings = if self.tokens.is_next(TokenType::Op('<')) { 206 | if !allow_typings { 207 | return Err(err!(UNEXPECTED, self.tokens.range_here(), "typings")); 208 | } 209 | self.tokens.consume(); 210 | Some(self.parse_typing_list(false, false, TokenType::Op('>'))?) 211 | } else { None }; 212 | let init = if self.tokens.is_next(TokenType::Punc('(')) { 213 | if !allow_exp_end { 214 | return Err(err!(UNEXPECTED, self.tokens.range_here(), "initializer";)); 215 | } 216 | self.tokens.consume(); 217 | Some(self.parse_expression_list(')')?) 218 | } else { None }; 219 | Ok(ASTModAccessValues::ModAccess( 220 | ASTModAccess { 221 | path, 222 | range: start.end(&self.tokens.last_loc), 223 | typings, 224 | init 225 | } 226 | )) 227 | } 228 | 229 | fn parse_typing(&mut self, allow_fn_keyword: bool, allow_optional_after_var: bool, allow_mod: bool) -> LazyResult { 230 | let range = self.tokens.input.loc(); 231 | let maybe_token = self.tokens.peek(); 232 | let t = match maybe_token { 233 | Some(token) => { 234 | match &token.val { 235 | TokenType::Punc('{') => { 236 | self.tokens.consume(); 237 | Some(ASTTypings::PairList(self.parse_typing_pair_list(false, false, false, true, false, '}')?)) 238 | }, 239 | TokenType::Punc('(') => { 240 | self.tokens.consume(); 241 | let params = Box::from(self.parse_typing_pair_list(false, allow_fn_keyword, true, false, false, ')')?); 242 | let return_type = if self.tokens.is_next_full_op(&['-', '>']) { 243 | let typing = self.parse_typing(allow_fn_keyword, true, allow_mod)?; 244 | Some(Box::from(typing)) 245 | } else { None }; 246 | Some(ASTTypings::Function(ASTFunction { 247 | params, 248 | return_type, 249 | range: range.end(&self.tokens.last_loc), 250 | typings: Vec::new(), 251 | body: None 252 | })) 253 | }, 254 | TokenType::Punc('[') => { 255 | self.tokens.consume(); 256 | let values = self.parse_typing_list(false, false, TokenType::Punc(']'))?; 257 | Some(ASTTypings::Tuple(values)) 258 | }, 259 | TokenType::Var(name) => { 260 | let tok_range = token.range; 261 | let var = ASTVar { value: name.clone(), range: tok_range }; 262 | self.tokens.consume(); 263 | if allow_mod { 264 | match self.parse_mod_access_or_var(var, false, true)? { 265 | ASTModAccessValues::ModAccess(acc) => Some(ASTTypings::Mod(acc)), 266 | ASTModAccessValues::Var(v) => Some(ASTTypings::Var(v)) 267 | } 268 | } else { 269 | let typings = if self.tokens.is_next(TokenType::Op('<')) { 270 | self.tokens.consume(); 271 | Some(self.parse_typing_list(true, false, TokenType::Op('>'))?) 272 | } else { None }; 273 | Some(ASTTypings::Var(ASTVarTyping { value: var, range: tok_range, typings })) 274 | } 275 | }, 276 | TokenType::Kw(kw) => { 277 | match kw.as_str() { 278 | "fn" => { 279 | if !allow_fn_keyword { 280 | return Err(err!(UNEXPECTED, self.tokens.range_here(), "keyword fn"; ["Only function signatures are allowed here. Remove the `fn` and the function body, if there is one."])); 281 | } 282 | self.tokens.consume(); 283 | Some(ASTTypings::Function(self.parse_function(true)?)) 284 | }, 285 | "impl" => { 286 | self.tokens.consume(); 287 | let val = self.parse_typing(false, false, true)?; 288 | match val { 289 | ASTTypings::Var(_) | ASTTypings::Mod(_) => Some(ASTTypings::Impl(ASTImplTyping { 290 | value: Box::from(val), 291 | range: range.end(&self.tokens.last_loc) 292 | })), 293 | _ => return Err(err!(EXPECTED, range.end(&self.tokens.last_loc), "identifier or module access"; ["Save the typing via the \"type\" keyword."])) 294 | } 295 | } 296 | _ => None 297 | } 298 | } 299 | _ => { 300 | None 301 | } 302 | } 303 | }, 304 | None => None 305 | }; 306 | if let Some(typing) = t { 307 | if let Some(tok) = self.tokens.peek() { 308 | match &tok.val { 309 | TokenType::Op(op) => { 310 | match op { 311 | '?' if allow_optional_after_var => { 312 | self.tokens.consume(); 313 | Ok(ASTTypings::Optional(Box::from(typing))) 314 | }, 315 | '+' => { 316 | self.tokens.consume(); 317 | let right = self.parse_typing(false, false, allow_mod)?; 318 | Ok(ASTTypings::Combine( 319 | ASTCombineTyping { 320 | left: Box::from(typing), 321 | right: Box::from(right), 322 | range: range.end(&self.tokens.last_loc) 323 | } 324 | )) 325 | } 326 | _ => { Ok(typing) } 327 | } 328 | }, 329 | _ => { Ok(typing) } 330 | } 331 | } else { 332 | Ok(typing) 333 | } 334 | } else { 335 | Err(err!(EXPECTED, range.to_range(), "typing")) 336 | } 337 | } 338 | 339 | fn parse_block(&mut self, allow_statement_as_exp: bool) -> LazyResult { 340 | let range = self.tokens.input.loc(); 341 | let mut res: Vec = vec![]; 342 | while !self.tokens.input.is_eof() && !self.tokens.is_next(TokenType::Punc('}')) { 343 | let exp = if let Some(exp) = if allow_statement_as_exp { self.parse_expression_or_expression_statement()? } else { self.parse_expression()? } { exp } else { 344 | continue; 345 | }; 346 | let range = utils::full_expression_range(&exp); 347 | res.push(exp); 348 | if !self.is_last_block { 349 | self.tokens.skip_or_err(TokenType::Punc(';'), Some(err!(SEMICOLON, range)))?; 350 | }; 351 | } 352 | self.tokens.skip_or_err(TokenType::Punc('}'), Some(err!(END_OF_BLOCK, range.end(&self.tokens.last_loc))))?; 353 | self.is_last_block = true; 354 | Ok(ASTBlock { 355 | elements: res, 356 | range: range.end(&self.tokens.last_loc) 357 | }) 358 | } 359 | 360 | fn parse_varname(&mut self, allow_generics: bool, only_varnames_as_generics: bool, allow_ints: bool, allow_keywords: bool) -> LazyResult<(ASTVar, Option)> { 361 | if allow_ints { self.tokens.is_last_num_as_str = true }; 362 | let next = self.tokens.consume(); 363 | if allow_ints { self.tokens.is_last_num_as_str = false }; 364 | let unwrapped = if let Some(val) = next { 365 | val 366 | } else { 367 | return Err(err!(EXPECTED, self.tokens.range_here(), "identifier")); 368 | }; 369 | let var = match unwrapped.val { 370 | TokenType::Var(v) => ASTVar { value: v, range: unwrapped.range }, 371 | TokenType::Kw(kw) if allow_keywords => ASTVar { value: kw.to_string(), range: unwrapped.range }, 372 | TokenType::Int(i) if allow_ints => ASTVar { value: i.to_string(), range: unwrapped.range }, 373 | _ => { 374 | return Err(err!(EXPECTED_FOUND, unwrapped.range, "identifier", &unwrapped.val.to_string();)); 375 | } 376 | }; 377 | if self.tokens.is_next(TokenType::Op('<')) { 378 | if !allow_generics { 379 | return Ok((var, None)); 380 | } 381 | self.tokens.consume(); 382 | return Ok((var, Some(self.parse_typing_list(only_varnames_as_generics, false, TokenType::Op('>'))?))); 383 | } 384 | Ok((var, None)) 385 | } 386 | 387 | fn parse_pair_list(&mut self, allow_without_val: bool, closing_punc: char) -> LazyResult { 388 | let range = self.tokens.input.loc(); 389 | let mut res: Vec<(String, Option)> = vec![]; 390 | let mut has_consumed_bracket = false; 391 | while !self.tokens.is_next(TokenType::Punc(closing_punc)) { 392 | let tok_start = self.tokens.input.loc(); 393 | let key = self.parse_varname(false, false, false, true)?.0; 394 | match self.tokens.expect_punc(&[',', ':', closing_punc], Some(tok_start.end(&self.tokens.last_loc)))? { 395 | ',' => { 396 | if !allow_without_val { 397 | return Err(err!(EXPECTED, tok_start.end(&self.tokens.last_loc), "value")); 398 | } 399 | res.push((key.value, None)); 400 | }, 401 | ':' => { 402 | let exp = if let Some(exp) = self.parse_expression()? { Some(exp) } else { 403 | return Err(err!(EXPECTED, tok_start.end(&self.tokens.last_loc), "expression")); 404 | }; 405 | res.push((key.value, exp)); 406 | }, 407 | ch if ch == closing_punc => { 408 | if !allow_without_val { 409 | return Err(err!(EXPECTED, tok_start.end(&self.tokens.last_loc), "typing")); 410 | } 411 | has_consumed_bracket = true; 412 | res.push((key.value, None)); 413 | break; 414 | }, 415 | _ => {} 416 | } 417 | if self.tokens.is_next(TokenType::Punc(',')) { self.tokens.consume(); }; 418 | }; 419 | if !has_consumed_bracket { self.tokens.skip_or_err(TokenType::Punc(closing_punc), None)?; }; 420 | Ok(ASTPairList { 421 | range: range.end(&self.tokens.last_loc), 422 | pairs: res 423 | }) 424 | } 425 | 426 | fn parse_expression_list(&mut self, closing_punc: char) -> LazyResult { 427 | let range = self.tokens.input.loc(); 428 | let mut expressions: Vec = vec![]; 429 | let mut is_first = true; 430 | while !self.tokens.is_next(TokenType::Punc(closing_punc)) { 431 | if !is_first { 432 | self.tokens.skip_or_err(TokenType::Punc(','), None)?; 433 | }; 434 | let exp = if let Some(exp) = self.parse_expression()? { exp } else { 435 | return Err(err!(EXPECTED, self.tokens.range_here(), "expression")); 436 | }; 437 | expressions.push(exp); 438 | is_first = false; 439 | }; 440 | self.tokens.skip_or_err(TokenType::Punc(closing_punc), None)?; 441 | Ok(ASTExpressionList { 442 | expressions, 443 | range: range.end(&self.tokens.last_loc) 444 | }) 445 | } 446 | 447 | fn parse_varname_list(&mut self, closing_punc: char) -> LazyResult { 448 | let range = self.tokens.input.loc(); 449 | let mut values: Vec = vec![]; 450 | let mut is_first = true; 451 | while !self.tokens.is_next(TokenType::Punc(closing_punc)) { 452 | if !is_first { 453 | self.tokens.skip_or_err(TokenType::Punc(','), None)?; 454 | }; 455 | let exp = self.parse_varname(false, false, false, true)?.0; 456 | values.push(exp); 457 | is_first = false; 458 | }; 459 | self.tokens.skip_or_err(TokenType::Punc(closing_punc), None)?; 460 | Ok(ASTVarList { 461 | values, 462 | range: range.end(&self.tokens.last_loc) 463 | }) 464 | } 465 | 466 | fn parse_typing_pair_list(&mut self, allow_without_val: bool, allow_fn_keyword: bool, allow_spread: bool, allow_modifiers: bool, allow_default: bool, closing_punc: char) -> LazyResult { 467 | let range = self.tokens.input.loc(); 468 | let mut res: Vec = vec![]; 469 | let mut has_consumed_bracket = false; 470 | let mut modifiers = ASTModifiers::empty(); 471 | while !self.tokens.is_next(TokenType::Punc(closing_punc)) { 472 | let tok_range = self.tokens.input.loc(); 473 | let is_spread = if self.tokens.is_next_full_op(&['.', '.', '.']) { 474 | if !allow_spread { 475 | return Err(err!(DISALLOWED, tok_range.end(&self.tokens.last_loc), "spread operator";)); 476 | } 477 | true 478 | } else { false }; 479 | if allow_modifiers { 480 | if let Some(t) = self.tokens.peek() { 481 | let mod_range = t.range; 482 | if let TokenType::Kw(kw) = &t.val { 483 | match kw.as_str() { 484 | "const" => { 485 | self.tokens.consume(); 486 | if modifiers.contains(ASTModifiers::CONST) { 487 | return Err(err!(ALREADY_HAS_MODIFIER, mod_range.end_with(&self.tokens.last_loc), "const";)); 488 | }; 489 | modifiers.insert(ASTModifiers::CONST); 490 | continue; 491 | }, 492 | "static" => { 493 | self.tokens.consume(); 494 | if modifiers.contains(ASTModifiers::STATIC) { 495 | return Err(err!(ALREADY_HAS_MODIFIER, mod_range.end_with(&self.tokens.last_loc), "static";)); 496 | }; 497 | modifiers.insert(ASTModifiers::STATIC); 498 | continue; 499 | }, 500 | "private" => { 501 | self.tokens.consume(); 502 | if modifiers.contains(ASTModifiers::PRIVATE) { 503 | return Err(err!(ALREADY_HAS_MODIFIER, mod_range.end_with(&self.tokens.last_loc), "private";)); 504 | }; 505 | modifiers.insert(ASTModifiers::PRIVATE); 506 | continue; 507 | }, 508 | _ => {} 509 | } 510 | } 511 | } 512 | }; 513 | let key = self.parse_varname(false, false, false, true)?.0; 514 | if self.tokens.is_next(TokenType::Op('=')) { 515 | if !allow_default { 516 | return Err(err!(DISALLOWED, self.tokens.range_here(), "default parameter")); 517 | } 518 | self.tokens.consume(); 519 | let default_value = if let Some(exp) = self.parse_expression()? { Some(exp) } else { 520 | return Err(err!(EXPECTED, self.tokens.range_here(), "expression")); 521 | }; 522 | res.push(ASTPairTypingItem {name: key.value, value: None, default_value, modifiers, spread: is_spread}); 523 | continue; 524 | } 525 | match self.tokens.expect_punc(&[',', ':', closing_punc], None)? { 526 | ',' => { 527 | if !allow_without_val { 528 | return Err(err!(EXPECTED, tok_range.end(&self.tokens.last_loc), "type")); 529 | } 530 | res.push(ASTPairTypingItem {name: key.value, value: None, default_value: None, modifiers, spread: is_spread}); 531 | modifiers.clear(); 532 | }, 533 | ':' => { 534 | let exp = self.parse_typing(allow_fn_keyword, true, true)?; 535 | let default_value = if self.tokens.is_next(TokenType::Op('=')) { 536 | if !allow_default { 537 | return Err(err!(DISALLOWED, Range { start: self.tokens.last_loc, end: self.tokens.input.loc() }, "default parameter")); 538 | } 539 | self.tokens.consume(); 540 | Some(if let Some(exp) = self.parse_expression()? { exp } else { 541 | return Err(err!(EXPECTED, self.tokens.range_here(), "expression")); 542 | }) 543 | } else { None }; 544 | res.push(ASTPairTypingItem { name: key.value, value: Some(exp), default_value, modifiers, spread: is_spread}); 545 | modifiers.clear(); 546 | }, 547 | ch if ch == closing_punc => { 548 | if !allow_without_val { 549 | return Err(err!(EXPECTED, tok_range.end(&self.tokens.last_loc), "type")); 550 | } 551 | has_consumed_bracket = true; 552 | res.push(ASTPairTypingItem { name: key.value, value: None, default_value: None, modifiers, spread: is_spread}); 553 | modifiers.clear(); 554 | break; 555 | }, 556 | _ => {} 557 | }; 558 | } 559 | if !has_consumed_bracket { self.tokens.skip_or_err(TokenType::Punc(closing_punc), None)?; }; 560 | Ok(ASTPairListTyping { 561 | range: range.end(&self.tokens.last_loc), 562 | pairs: res 563 | }) 564 | } 565 | 566 | fn parse_typing_list(&mut self, only_varnames: bool, allow_fn_keyword: bool, closing_tok: TokenType) -> LazyResult { 567 | let range = self.tokens.input.loc(); 568 | let mut res: Vec = vec![]; 569 | let mut is_first = true; 570 | while !self.tokens.is_next(closing_tok.clone()) { 571 | if !is_first { 572 | self.tokens.skip_or_err(TokenType::Punc(','), None)?; 573 | }; 574 | let id_range = self.tokens.input.loc(); 575 | let typing = self.parse_typing(allow_fn_keyword, false, !only_varnames)?; 576 | if only_varnames { 577 | match &typing { 578 | ASTTypings::Var(v) => { 579 | if v.typings.is_some() { 580 | return Err(err!(NO_GENERICS, v.range)); 581 | } 582 | }, 583 | _ => { 584 | return Err(err!(EXPECTED, id_range.end(&self.tokens.last_loc), "identifier")); 585 | } 586 | } 587 | } 588 | res.push(typing); 589 | is_first = false; 590 | }; 591 | self.tokens.skip_or_err(closing_tok, None)?; 592 | Ok(ASTListTyping { 593 | entries: res, 594 | range: range.end(&self.tokens.last_loc) 595 | }) 596 | } 597 | 598 | fn parse_type_params(&mut self) -> LazyResult> { 599 | if self.tokens.is_next(TokenType::Op('>')) { 600 | self.tokens.consume(); 601 | return Err(err!(EMPTY_TYPE_PARAMS, self.tokens.range_here())); 602 | } 603 | let start = self.tokens.input.loc(); 604 | let mut res: Vec = vec![]; 605 | let mut is_first = true; 606 | while !self.tokens.is_next(TokenType::Op('>')) { 607 | if !is_first { 608 | self.tokens.skip_or_err(TokenType::Punc(','), None)?; 609 | }; 610 | let varname = self.parse_varname(false, false, false, false)?.0; 611 | let constraint = if self.tokens.is_next(TokenType::Punc(':')) { 612 | self.tokens.consume(); 613 | Some(self.parse_typing(false, false, true)?) 614 | } else { 615 | None 616 | }; 617 | res.push(ASTTypeParameter { 618 | name: varname, 619 | constraint, 620 | range: Range { start, end: self.tokens.last_loc } 621 | }); 622 | is_first = false; 623 | } 624 | self.tokens.skip_or_err(TokenType::Op('>'), None)?; 625 | Ok(res) 626 | } 627 | 628 | 629 | fn parse_function(&mut self, allow_body: bool) -> LazyResult { 630 | let range = self.tokens.input.loc(); 631 | let typings = if self.tokens.is_next(TokenType::Op('<')) { 632 | self.tokens.consume(); 633 | self.parse_type_params()? 634 | } else { Vec::new() }; 635 | self.tokens.skip_or_err(TokenType::Punc('('), Some(err!(EXPECTED, self.tokens.range_here(), "start of function parameters")))?; 636 | let params = Box::from(self.parse_typing_pair_list(true, false, true, false, true, ')')?); 637 | let return_type = if self.tokens.is_next_full_op(&['-', '>']) { 638 | Some(Box::from(self.parse_typing(false, true, true)?)) 639 | } else { None }; 640 | let body = if allow_body { 641 | if let Some(e) = self.parse_expression()? { Some(Box::from(e)) } else { 642 | return Err(err!(EXPECTED, self.tokens.range_here(), "expression")); 643 | } 644 | } else { None }; 645 | Ok(ASTFunction { 646 | range: range.end(&self.tokens.last_loc), 647 | params, 648 | typings, 649 | return_type, 650 | body 651 | }) 652 | } 653 | 654 | fn parse_match_arm_exp(&mut self) -> LazyResult { 655 | let range = self.tokens.input.loc(); 656 | let exp = if let Some(exp) = self.parse_expression_part(false)? { 657 | exp 658 | } else { 659 | return Err(err!(EXPECTED, self.tokens.range_here(), "match arm condition")); 660 | }; 661 | match exp { 662 | ASTExpression::Str(str_obj) => Ok(ASTMatchArmExpressions::String(str_obj)), 663 | ASTExpression::Int(int_obj) => Ok(ASTMatchArmExpressions::Int(int_obj)), 664 | ASTExpression::Float(f_obj) => Ok(ASTMatchArmExpressions::Float(f_obj)), 665 | ASTExpression::Bool(b_obj) => Ok(ASTMatchArmExpressions::Bool(b_obj)), 666 | ASTExpression::Tuple(t_obj) => { 667 | if !utils::is_natural_tuple(&t_obj) { 668 | return Err(err!(EXPECTED, range.end(&self.tokens.last_loc), "natural tuple literal")); 669 | } 670 | Ok(ASTMatchArmExpressions::Tuple(t_obj)) 671 | }, 672 | ASTExpression::Iterator(i_obj) => { 673 | if !utils::is_natural_iter(&i_obj) { 674 | return Err(err!(EXPECTED, range.end(&self.tokens.last_loc), "natural iterator literal")); 675 | } 676 | Ok(ASTMatchArmExpressions::Iterator(i_obj)) 677 | }, 678 | ASTExpression::Var(v) => { 679 | if v.value != "_" { 680 | return Err(err!(UNEXPECTED, range.end(&self.tokens.last_loc), "variable name")); 681 | }; 682 | Ok(ASTMatchArmExpressions::Rest) 683 | }, 684 | ASTExpression::None(r) => Ok(ASTMatchArmExpressions::None(r)), 685 | ASTExpression::ModAccess(acc) => { 686 | if let Some(init) = &acc.init { 687 | if init.expressions.len() == 1 && matches!(&init.expressions[0], ASTExpression::Var(_)) { 688 | return Ok(ASTMatchArmExpressions::EnumVar(acc)); 689 | } 690 | } 691 | if !utils::is_natural_mod_access(&acc) { 692 | return Err(err!(EXPECTED, range.end(&self.tokens.last_loc), "natural enum value")); 693 | } 694 | Ok(ASTMatchArmExpressions::Enum(acc)) 695 | }, 696 | _ => { 697 | Err(err!(WRONG_MATCH_ARM_EXP, range.end(&self.tokens.last_loc))) 698 | } 699 | } 700 | } 701 | 702 | fn parse_expression_part(&mut self, parse_generics_in_suffix: bool) -> LazyResult> { 703 | self.is_last_block = false; 704 | let exp = { 705 | let token = if let Some(t) = self.tokens.consume() { 706 | t 707 | } else { 708 | return Err(err!(UNEXPECTED_EOF, self.tokens.range_here())); 709 | }; 710 | match token.val { 711 | TokenType::Int(value) => ASTExpression::Int(ASTInt { value, range: token.range } ), 712 | TokenType::Float(value) => ASTExpression::Float(ASTFloat { value, range: token.range }), 713 | TokenType::Str(value) => ASTExpression::Str(ASTStr { value, range: token.range }), 714 | TokenType::Char(value) => ASTExpression::Char(ASTChar { value, range: token.range }), 715 | TokenType::None => ASTExpression::None(token.range), 716 | TokenType::Var(value) => ASTExpression::Var(ASTVar { value, range: token.range }), 717 | TokenType::Bool(value) => ASTExpression::Bool(ASTBool { value, range: token.range }), 718 | TokenType::TempStrStart => { 719 | let mut string = String::new(); 720 | let mut exps: HashMap = HashMap::new(); 721 | let mut is_prev_escape = false; 722 | loop { 723 | match self.tokens.input.consume() { 724 | Some(ch) => { 725 | match ch { 726 | '`' => break, 727 | '$' if !is_prev_escape => { 728 | self.tokens.skip_or_err(TokenType::Punc('{'), None)?; 729 | let exp = if let Some(exp) = self.parse_expression()? { exp } else { 730 | return Err(err!(EXPECTED, self.tokens.range_here(), "expression")); 731 | }; 732 | self.tokens.skip_or_err(TokenType::Punc('}'), None)?; 733 | exps.insert(string.len(), exp); 734 | string.push(' '); 735 | }, 736 | '\\' => is_prev_escape = true, 737 | _ => { 738 | is_prev_escape = false; 739 | string.push(ch); 740 | } 741 | } 742 | }, 743 | None => { 744 | return Err(err!(END_OF_STR, token.range.end_with(&self.tokens.last_loc))); 745 | } 746 | } 747 | }; 748 | if exps.len() == 0 { 749 | return Err(err!(POINTLESS_TEMPLATE, token.range.end_with(&self.tokens.last_loc))); 750 | } 751 | ASTExpression::TempStr(ASTTempStr { 752 | template: string, 753 | values: exps, 754 | range: token.range.end_with(&self.tokens.last_loc) 755 | }) 756 | }, 757 | TokenType::Op(value) => { 758 | // Prefixes 759 | match self.tokens.parse_full_op(Some(value)).as_str() { 760 | "-" | "!" | "~" => { 761 | let val = if let Some(val) = self.parse_expression_part(parse_generics_in_suffix)? { Box::from(val) } else { 762 | return Err(err!(EXPECTED, token.range, "expression")); 763 | }; 764 | ASTExpression::Unary( 765 | ASTUnary { 766 | op: value.to_string(), 767 | value: val, 768 | range: token.range 769 | } 770 | ) 771 | }, 772 | val @ ".." | val @ "..=" => ASTExpression::Iterator(ASTIterator { 773 | start: Box::from(ASTExpression::Int(ASTInt { value: 0, range: token.range.clone() })), 774 | end: if let Some(exp) = self.parse_expression()? { Box::from(exp) } else { 775 | return Err(err!(EXPECTED, self.tokens.range_here(), "expression")); 776 | }, 777 | inclusive: val == "..=", 778 | range: token.range.end_with(&self.tokens.last_loc) 779 | }), 780 | "..." => { 781 | ASTExpression::Spread( 782 | ASTSpread { 783 | value: if let Some(exp) = self.parse_expression()? { Box::from(exp) } else { 784 | return Err(err!(EXPECTED, self.tokens.range_here(), "expression")); 785 | }, 786 | range: token.range.end_with(&self.tokens.last_loc) 787 | } 788 | ) 789 | } 790 | val @ _ => { 791 | return Err(err!(UNEXPECTED_OP, token.range.end_with(&self.tokens.last_loc), val)); 792 | } 793 | } 794 | }, 795 | TokenType::Punc(val) => { 796 | match val { 797 | '(' => { 798 | if self.tokens.is_next(TokenType::Punc(')')) { 799 | return Err(err!(UNEXPECTED, self.tokens.range_here(), "empty expression")); 800 | }; 801 | let exp = if let Some(exp) = self.parse_expression()? { exp } else { 802 | return Err(err!(EXPECTED, self.tokens.range_here(), "expression")); 803 | }; 804 | self.tokens.skip_or_err(TokenType::Punc(')'), Some(err!(EXPECTED, self.tokens.range_here(), "end of wrapped expression")))?; 805 | exp 806 | }, 807 | ';' => return Ok(None), 808 | '{' => ASTExpression::Block(self.parse_block(true)?), 809 | '[' => { 810 | if self.tokens.is_next(TokenType::Punc(']')) { 811 | return Err(err!(UNEXPECTED, self.tokens.range_here(), "empty tuple")); 812 | }; 813 | ASTExpression::Tuple(self.parse_expression_list(']')?) 814 | }, 815 | _ => { 816 | return Err(err!(UNEXPECTED_PUNC, token.range.end_with(&self.tokens.last_loc), &val.to_string())); 817 | } 818 | } 819 | }, 820 | TokenType::Kw(val) => { 821 | match val.as_str() { 822 | "let" | "const" => { 823 | let is_const = val.as_str() == "const"; 824 | let to_get_name = if let Some(n) = self.tokens.consume() { 825 | n 826 | } else { 827 | return Err(err!(EXPECTED, token.range.end_with(&self.tokens.last_loc), "variable name")); 828 | }; 829 | let var = match to_get_name.val { 830 | TokenType::Punc('[') => ASTDeclareTypes::TupleDeconstruct(self.parse_varname_list(']')?), 831 | TokenType::Punc('{') => ASTDeclareTypes::StructDeconstruct(self.parse_varname_list('}')?), 832 | TokenType::Var(v) => ASTDeclareTypes::Var(ASTVar { value: v, range: to_get_name.range }), 833 | _ => { 834 | return Err(err!(EXPECTED_FOUND, to_get_name.range, "identifier or deconstruct pattern")); 835 | } 836 | }; 837 | let typings = if self.tokens.is_next(TokenType::Punc(':')) { 838 | self.tokens.consume(); 839 | Some(self.parse_typing(false, true, true)?) 840 | } else { None }; 841 | let value = if self.tokens.is_next(TokenType::Op('=')) { 842 | self.tokens.consume(); // Skip = 843 | if let Some(exp) = self.parse_expression()? { Some(Box::from(exp)) } else { 844 | return Err(err!(EXPECTED, self.tokens.range_here(), "expression")); 845 | } 846 | } else { 847 | if is_const { 848 | return Err(err!(CONST_WITHOUT_INIT, token.range.end_with(&self.tokens.last_loc))); 849 | } 850 | None 851 | }; 852 | return Ok(Some(ASTExpression::Declare( 853 | ASTDeclare { 854 | var, 855 | is_const, 856 | typings, 857 | value, 858 | range: token.range.end_with(&self.tokens.last_loc) 859 | } 860 | ))) 861 | }, 862 | "fn" => ASTExpression::Function(self.parse_function(true)?), 863 | "if" => { 864 | let condition = if let Some(exp) = self.parse_expression()? { Box::from(exp) } else { 865 | return Err(err!(EXPECTED, self.tokens.range_here(), "expression")); 866 | }; 867 | let then = if let Some(exp) = self.parse_expression_or_expression_statement()? { Box::from(exp) } else { 868 | return Err(err!(EXPECTED, self.tokens.range_here(), "expression")); 869 | }; 870 | let otherwise = if self.tokens.is_next(TokenType::Kw(String::from("else"))) { 871 | self.tokens.consume(); 872 | if let Some(exp) = self.parse_expression_or_expression_statement()? { Some(Box::from(exp)) } else { 873 | return Err(err!(EXPECTED, self.tokens.range_here(), "expression")); 874 | } 875 | } else { None }; 876 | return Ok(Some(ASTExpression::If( 877 | ASTIf { 878 | condition, 879 | then, 880 | otherwise, 881 | range: token.range.end_with(&mut self.tokens.last_loc) 882 | } 883 | ))) 884 | }, 885 | "for" => { 886 | let var = self.parse_varname(false, false, false, false)?.0; 887 | self.tokens.skip_or_err(TokenType::Kw(String::from("in")), None)?; 888 | let iterator = if let Some(exp) = self.parse_expression()? { Box::from(exp) } else { 889 | return Err(err!(EXPECTED, self.tokens.range_here(), "expression")); 890 | }; 891 | let turn_off_exp_statements = !self.allow_exp_statements; 892 | self.allow_exp_statements = true; 893 | let body = if let Some(exp) = self.parse_expression_or_expression_statement()? { Box::from(exp) } else { 894 | return Err(err!(EXPECTED, self.tokens.range_here(), "expression")); 895 | }; 896 | if turn_off_exp_statements { self.allow_exp_statements = false; } 897 | return Ok(Some(ASTExpression::ForIn( 898 | ASTForIn { 899 | var, 900 | iterable: iterator, 901 | body, 902 | range: token.range.end_with(&self.tokens.last_loc) 903 | } 904 | ))) 905 | }, 906 | "while" => { 907 | let cond = if let Some(exp) = self.parse_expression()? { Box::from(exp) } else { 908 | return Err(err!(EXPECTED, self.tokens.range_here(), "expression")); 909 | }; 910 | let turn_off_exp_statements = !self.allow_exp_statements; 911 | self.allow_exp_statements = true; 912 | let body = if let Some(exp) = self.parse_expression_or_expression_statement()? { Box::from(exp) } else { 913 | return Err(err!(EXPECTED, self.tokens.range_here(), "expression")); 914 | }; 915 | if turn_off_exp_statements { self.allow_exp_statements = false; } 916 | return Ok(Some(ASTExpression::While( 917 | ASTWhile { 918 | condition: cond, 919 | body, 920 | range: token.range.end_with(&self.tokens.last_loc) 921 | } 922 | ))) 923 | }, 924 | "match" => { 925 | let to_get_matched = if let Some(exp) = self.parse_expression()? { Box::from(exp) } else { 926 | return Err(err!(EXPECTED, self.tokens.range_here(), "expression")); 927 | }; 928 | self.tokens.skip_or_err(TokenType::Punc('{'), None)?; 929 | let mut arms: Vec = vec![]; 930 | while !self.tokens.is_next(TokenType::Punc('}')) { 931 | let match_arm_start = self.tokens.input.loc(); 932 | let mut possibilities: Vec = vec![]; 933 | possibilities.push(self.parse_match_arm_exp()?); 934 | if self.tokens.is_next(TokenType::Op('|')) { 935 | self.tokens.consume(); 936 | while !self.tokens.is_next(TokenType::Op('=')) && !self.tokens.is_next(TokenType::Kw(String::from("if"))) { 937 | possibilities.push(self.parse_match_arm_exp()?); 938 | if self.tokens.is_next(TokenType::Op('|')) { self.tokens.consume(); }; 939 | } 940 | } 941 | let guard = if self.tokens.is_next(TokenType::Kw(String::from("if"))) { 942 | self.tokens.consume(); 943 | if let Some(exp) = self.parse_expression()? { Some(exp) } else { 944 | return Err(err!(EXPECTED, self.tokens.range_here(), "expression")); 945 | } 946 | } else { None }; 947 | 948 | self.tokens.skip_or_err_full_op("=>", None)?; 949 | 950 | let body = if let Some(exp) = self.parse_expression()? { exp } else { 951 | return Err(err!(EXPECTED, self.tokens.range_here(), "expression")); 952 | }; 953 | if self.tokens.is_next(TokenType::Punc(',')) { self.tokens.consume(); }; 954 | arms.push(ASTMatchArm { 955 | guard, 956 | possibilities, 957 | body, 958 | range: match_arm_start.end(&self.tokens.last_loc) 959 | }); 960 | } 961 | self.tokens.skip_or_err(TokenType::Punc('}'), None)?; 962 | self.is_last_block = true; 963 | return Ok(Some(ASTExpression::Match(ASTMatch { 964 | arms, 965 | range: token.range.end_with(&self.tokens.last_loc), 966 | expression: to_get_matched 967 | }))) 968 | }, 969 | "new" => { 970 | let target = self.parse_mod_access_or_var_without_var(false, true)?; 971 | let typings = if self.tokens.is_next(TokenType::Op('<')) { 972 | self.tokens.consume(); 973 | Some(self.parse_typing_list(false, false, TokenType::Op('>'))?) 974 | } else { None }; 975 | self.tokens.skip_or_err(TokenType::Punc('{'), Some(err!(EXPECTED, self.tokens.range_here(), "struct initializor")))?; 976 | ASTExpression::Init( 977 | ASTInitializor { 978 | target, 979 | params: self.parse_pair_list(true, '}')?, 980 | typings, 981 | range: token.range.end_with(&self.tokens.last_loc) 982 | } 983 | ) 984 | }, 985 | "await" => { 986 | let optional = if self.tokens.is_next(TokenType::Op('?')) { 987 | self.tokens.consume(); 988 | true 989 | } else { false }; 990 | let expression = if let Some(exp) = self.parse_expression()? { Box::from(exp) } else { 991 | return Err(err!(EXPECTED, self.tokens.range_here(), "expression")); 992 | }; 993 | ASTExpression::Await( 994 | ASTAwait { 995 | optional, 996 | expression, 997 | range: token.range.end_with(&self.tokens.last_loc) 998 | } 999 | ) 1000 | } 1001 | _ => { 1002 | return Err(err!(EXPECTED_FOUND, token.range, &format!("keyword \"{}\"", val))); 1003 | } 1004 | } 1005 | } 1006 | } 1007 | }; 1008 | Ok(Some(self.parse_suffix(exp, parse_generics_in_suffix)?)) 1009 | } 1010 | 1011 | fn parse_expression(&mut self) -> LazyResult> { 1012 | if let Some(exp) = self.parse_expression_part(true)? { 1013 | Ok(Some(self.parse_binary(exp, 0)?)) 1014 | } else { 1015 | Ok(None) 1016 | } 1017 | } 1018 | 1019 | fn parse_expression_or_expression_statement(&mut self) -> LazyResult> { 1020 | let range = self.tokens.input.loc(); 1021 | let thing = if let Some(t) = self.tokens.peek() { 1022 | t 1023 | } else { 1024 | return Err(err!(UNEXPECTED_EOF, self.tokens.range_here())); 1025 | }; 1026 | match &thing.val { 1027 | TokenType::Kw(kw) => { 1028 | match kw.as_str() { 1029 | "yield" => { 1030 | self.tokens.consume(); 1031 | if !self.allow_exp_statements { 1032 | return Err(err!(UNEXPECTED, range.end(&self.tokens.last_loc), "yield expression")); 1033 | } 1034 | let value = if let Some(exp) = self.parse_expression()? { 1035 | Some(Box::from(exp)) 1036 | } else { None }; 1037 | Ok(Some(ASTExpression::Yield(ASTYield { 1038 | value, 1039 | range: range.end(&self.tokens.last_loc) 1040 | }))) 1041 | }, 1042 | _ => self.parse_expression() 1043 | } 1044 | }, 1045 | _ => self.parse_expression() 1046 | } 1047 | } 1048 | 1049 | fn parse_statement(&mut self) -> LazyResult { 1050 | let range = self.tokens.input.loc(); 1051 | let token = if let Some(t) = self.tokens.consume() { t } else { 1052 | return Err(err!(UNEXPECTED_EOF, self.tokens.range_here())); 1053 | }; 1054 | match &token.val { 1055 | TokenType::Kw(keyword) => { 1056 | match keyword.as_str() { 1057 | "struct" => { 1058 | let name = self.parse_varname(false, false, false, false)?.0; 1059 | let typings = if self.tokens.is_next(TokenType::Op('<')) { 1060 | self.tokens.consume(); 1061 | self.parse_type_params()? 1062 | } else { Vec::new() }; 1063 | self.tokens.skip_or_err(TokenType::Punc('{'), Some(err!(EXPECTED, self.tokens.range_here(), "start of struct fields")))?; 1064 | Ok(ASTStatement::Struct(ASTStruct { 1065 | name, 1066 | typings, 1067 | fields: self.parse_typing_pair_list(true, true, false, true, true, '}')?, 1068 | range: range.end(&self.tokens.last_loc) 1069 | })) 1070 | } 1071 | "enum" => { 1072 | let name = self.parse_varname(false, false, false, false)?; 1073 | let typings = if self.tokens.is_next(TokenType::Op('<')) { 1074 | self.tokens.consume(); 1075 | self.parse_type_params()? 1076 | } else { Vec::new() }; 1077 | self.tokens.skip_or_err(TokenType::Punc('{'), Some(err!(EXPECTED, self.tokens.range_here(), "start of enum fields")))?; 1078 | Ok(ASTStatement::EnumDeclaration(ASTEnumDeclaration { 1079 | name: name.0, 1080 | values: self.parse_typing_pair_list(true, false, false, false, true, '}')?, 1081 | typings, 1082 | range: range.end(&self.tokens.last_loc) 1083 | })) 1084 | }, 1085 | "type" => { 1086 | let name = self.parse_varname(false, false, false, false)?; 1087 | let typings = if self.tokens.is_next(TokenType::Op('<')) { 1088 | self.tokens.consume(); 1089 | self.parse_type_params()? 1090 | } else { Vec::new() }; 1091 | self.tokens.skip_or_err(TokenType::Op('='), None)?; 1092 | let typing = self.parse_typing(false, false, true)?; 1093 | Ok(ASTStatement::Type( 1094 | ASTType { 1095 | name: name.0, 1096 | typings, 1097 | value: typing, 1098 | range: range.end(&self.tokens.last_loc) 1099 | } 1100 | )) 1101 | }, 1102 | "main" => { 1103 | if self.parsed_main { 1104 | return Err(err!(MANY_ENTRIES, range.end(&self.tokens.last_loc))); 1105 | }; 1106 | self.tokens.skip_or_err(TokenType::Punc('{'), None)?; 1107 | let exp = self.parse_block(false)?; 1108 | self.parsed_main = true; 1109 | Ok(ASTStatement::Main( 1110 | ASTMain { 1111 | expression: exp, 1112 | range: range.end(&self.tokens.last_loc) 1113 | } 1114 | )) 1115 | }, 1116 | "static" => { 1117 | let varname = self.parse_varname(false, false, false, false)?; 1118 | self.tokens.skip_or_err(TokenType::Op('='), None)?; 1119 | let typings = if let Some(typing) = varname.1 { 1120 | let len = typing.entries.len(); 1121 | if len == 0 || len > 1 { 1122 | return Err(err!(EXPECTED, token.range, "only one type")); 1123 | } else { 1124 | Some(typing) 1125 | } 1126 | } else { None }; 1127 | let exp = self.parse_expression()?; 1128 | if exp.is_none() { 1129 | return Err(err!(EXPECTED, self.tokens.range_here(), "initializor")); 1130 | } 1131 | Ok(ASTStatement::Static( 1132 | Box::from(ASTStatic { 1133 | typings, 1134 | var: varname.0, 1135 | value: exp.unwrap(), 1136 | range: range.end(&self.tokens.last_loc) 1137 | }) 1138 | )) 1139 | }, 1140 | "export" => { 1141 | let value = self.parse_statement()?; 1142 | if matches!(value, ASTStatement::Main(_)) { 1143 | return Err(err!(UNEXPECTED, range.end(&self.tokens.last_loc), "main entry")); 1144 | } 1145 | Ok(ASTStatement::Export( 1146 | ASTExport { 1147 | value: Box::from(value), 1148 | range: range.end(&self.tokens.last_loc) 1149 | } 1150 | )) 1151 | }, 1152 | "import" => { 1153 | let path_start = self.tokens.input.loc(); 1154 | let item = if self.tokens.is_next(TokenType::Punc('{')) { 1155 | self.tokens.consume(); 1156 | let mut items: Vec = vec![]; 1157 | let mut tok = self.tokens.peek(); 1158 | while matches!(tok, Some(_)) && tok.unwrap().val != TokenType::Punc('}') { 1159 | let text = self.parse_varname(false, false, false, false)?.0; 1160 | if self.tokens.is_next(TokenType::Kw(String::from("as"))) { 1161 | self.tokens.consume(); 1162 | let alias = self.parse_varname(false, false, false, false)?.0; 1163 | let alias_range = alias.range.end; 1164 | items.push(ASTImportItem { name: text.value, r#as: Some(alias), range: Range { start: text.range.start, end: alias_range } }); 1165 | } else { 1166 | items.push(ASTImportItem { name: text.value, range: text.range, r#as: None }) 1167 | } 1168 | if self.tokens.is_next(TokenType::Punc('}')) { 1169 | self.tokens.consume(); 1170 | break; 1171 | } 1172 | self.tokens.skip_or_err(TokenType::Punc(','), None)?; 1173 | tok = self.tokens.peek(); 1174 | } 1175 | ASTImportThing::Items(items) 1176 | } else if self.tokens.is_next(TokenType::Op('*')) { 1177 | self.tokens.consume(); 1178 | ASTImportThing::All 1179 | } else { 1180 | return Err(err!(EXPECTED, self.tokens.range_here(), "either an import deconstructor or a star (*)")); 1181 | }; 1182 | self.tokens.skip_or_err(TokenType::Kw(String::from("from")), None)?; 1183 | let path = if let Some(ASTExpression::Str(string)) = self.parse_expression_part(false)? { 1184 | string 1185 | } else { 1186 | return Err(err!(EXPECTED, range.end(&path_start), "path to module")); 1187 | }; 1188 | let as_binding = if self.tokens.is_next(TokenType::Kw(String::from("as"))) { 1189 | self.tokens.consume(); 1190 | Some(self.parse_varname(false, false, false, false)?.0) 1191 | } else { None }; 1192 | Ok(ASTStatement::Import( 1193 | ASTImport { 1194 | path, 1195 | thing: item, 1196 | r#as: as_binding, 1197 | range: range.end(&self.tokens.last_loc) 1198 | } 1199 | )) 1200 | }, 1201 | "impl" => { 1202 | let typings = if self.tokens.is_next(TokenType::Op('<')) { 1203 | self.tokens.consume(); 1204 | Some(self.parse_typing_list(true, false, TokenType::Op('>'))?) 1205 | } else { None }; 1206 | let partial = self.parse_mod_access_or_var_without_var(false, true)?; 1207 | self.tokens.skip_or_err(TokenType::Kw(String::from("for")), None)?; 1208 | let target = self.parse_mod_access_or_var_without_var(false, true)?; 1209 | self.tokens.skip_or_err(TokenType::Punc('{'), None)?; 1210 | Ok(ASTStatement::Impl( 1211 | ASTImpl { 1212 | partial, 1213 | target, 1214 | typings, 1215 | fields: self.parse_typing_pair_list(false, true, false, true, false, '}')?, 1216 | range: range.end(&self.tokens.last_loc) 1217 | } 1218 | )) 1219 | }, 1220 | _ => { 1221 | self.tokens.input.skip_line(); 1222 | return Err(err!(EXPECTED_FOUND, token.range, "statement", &token.val.to_string())); 1223 | }, 1224 | } 1225 | }, 1226 | TokenType::Punc('#') => { 1227 | let name = self.parse_varname(false, false, false, true)?.0; 1228 | let mut args: Vec = vec![]; 1229 | if self.tokens.is_next(TokenType::Punc('(')) { 1230 | self.tokens.consume(); 1231 | let mut is_first = true; 1232 | while !self.tokens.is_next(TokenType::Punc(')')) { 1233 | if !is_first { 1234 | self.tokens.skip_or_err(TokenType::Punc(','), None)?; 1235 | } 1236 | if self.tokens.is_next(TokenType::Punc(')')) { break; }; 1237 | 1238 | args.push(self.tokens.consume().unwrap().val); 1239 | is_first = false; 1240 | } 1241 | self.tokens.skip_or_err(TokenType::Punc(')'), None)?; 1242 | } 1243 | let target = Box::from(self.parse_statement()?); 1244 | Ok(ASTStatement::Meta( 1245 | ASTMeta { 1246 | name, 1247 | args, 1248 | target, 1249 | range: token.range.end_with(&self.tokens.last_loc) 1250 | })) 1251 | }, 1252 | _ => { 1253 | self.tokens.input.skip_line(); 1254 | return Err(err!(EXPECTED_FOUND, token.range, "statement", &token.val.to_string())); 1255 | } 1256 | } 1257 | } 1258 | 1259 | pub fn parse(&mut self) -> Vec { 1260 | let mut res = vec![]; 1261 | while !self.tokens.input.is_eof() { 1262 | match self.parse_statement() { 1263 | Ok(stmt) => res.push(stmt), 1264 | Err(error) => self.tokens.errors.push(error) 1265 | } 1266 | } 1267 | res 1268 | } 1269 | 1270 | } --------------------------------------------------------------------------------