├── .gitattributes ├── .vscode └── settings.json ├── lib ├── edlang_parser │ ├── build.rs │ ├── src │ │ ├── error.rs │ │ ├── lexer.rs │ │ ├── tokens.rs │ │ ├── lib.rs │ │ └── grammar.lalrpop │ └── Cargo.toml ├── edlang_driver │ ├── tests │ │ ├── programs │ │ │ ├── casts.ed │ │ │ ├── simple.ed │ │ │ ├── while.ed │ │ │ ├── refs.ed │ │ │ ├── while_if_false.ed │ │ │ ├── if_if_false.ed │ │ │ ├── factorial.ed │ │ │ ├── struct.ed │ │ │ ├── basic_ifs.ed │ │ │ └── struct_impl.ed │ │ ├── common.rs │ │ └── programs.rs │ ├── Cargo.toml │ └── src │ │ ├── linker.rs │ │ └── lib.rs ├── edlang_codegen_llvm │ ├── src │ │ └── lib.rs │ └── Cargo.toml ├── edlang_span │ ├── src │ │ └── lib.rs │ └── Cargo.toml ├── edlang_ast │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── edlang_session │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── edlang_ir │ ├── Cargo.toml │ └── src │ │ ├── scalar_int.rs │ │ └── lib.rs ├── edlang_lowering │ ├── Cargo.toml │ └── src │ │ ├── common.rs │ │ ├── errors.rs │ │ └── prepass.rs └── edlang_check │ ├── Cargo.toml │ └── src │ └── lib.rs ├── release.toml ├── .github ├── FUNDING.yml └── workflows │ ├── docs.yml │ ├── release.yml │ └── ci.yml ├── .gitignore ├── bin └── edlangc │ ├── src │ └── main.rs │ └── Cargo.toml ├── programs ├── while.ed ├── simple.ed ├── ptr.ed ├── refs.ed ├── factorial.ed ├── struct.ed └── example.ed ├── edb ├── src │ ├── config.rs │ └── main.rs └── Cargo.toml ├── Cargo.toml ├── README.md ├── cliff.toml ├── CHANGELOG.md └── LICENSE /.gitattributes: -------------------------------------------------------------------------------- 1 | *.ed linguist-language=Rust 2 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.showUnlinkedFileNotification": false 3 | } 4 | -------------------------------------------------------------------------------- /lib/edlang_parser/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | lalrpop::process_root().unwrap(); 3 | } 4 | -------------------------------------------------------------------------------- /release.toml: -------------------------------------------------------------------------------- 1 | pre-release-hook = ["git", "cliff", "-o", "CHANGELOG.md", "--tag", "{{version}}" ] 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [edg-l] 4 | liberapay: edgl 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /target_ed 3 | playground/ 4 | 5 | /*.ll 6 | 7 | # Generated by `oranda generate ci` 8 | public/ -------------------------------------------------------------------------------- /bin/edlangc/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | 3 | fn main() -> Result<(), Box> { 4 | Ok(edlang_driver::main()?) 5 | } 6 | -------------------------------------------------------------------------------- /lib/edlang_driver/tests/programs/casts.ed: -------------------------------------------------------------------------------- 1 | pub fn main(argc: i32, argv: *const *const u8) -> i64 { 2 | let a: i32 = 2; 3 | let b: i64 = a as i64; 4 | return b; 5 | } 6 | -------------------------------------------------------------------------------- /lib/edlang_parser/src/error.rs: -------------------------------------------------------------------------------- 1 | use crate::{lexer::LexicalError, tokens::Token}; 2 | use lalrpop_util::ParseError; 3 | 4 | pub type Error = ParseError; 5 | -------------------------------------------------------------------------------- /lib/edlang_driver/tests/programs/simple.ed: -------------------------------------------------------------------------------- 1 | pub fn main(argc: i64) -> i64 { 2 | let mut a: i64 = 0; 3 | 4 | if argc > 2 { 5 | a = 1; 6 | } 7 | 8 | return a; 9 | } 10 | -------------------------------------------------------------------------------- /lib/edlang_driver/tests/programs/while.ed: -------------------------------------------------------------------------------- 1 | pub fn main(argc: i64) -> i64 { 2 | let mut a: i64 = 0; 3 | 4 | while a < 10 { 5 | a = a + 1; 6 | } 7 | 8 | return a; 9 | } 10 | -------------------------------------------------------------------------------- /lib/edlang_driver/tests/programs/refs.ed: -------------------------------------------------------------------------------- 1 | 2 | pub fn main() -> i64 { 3 | let mut a: i64 = 0; 4 | 5 | hello(&mut a); 6 | 7 | return a; 8 | } 9 | 10 | pub fn hello(a: &mut i64) { 11 | *a = 2; 12 | } 13 | -------------------------------------------------------------------------------- /programs/while.ed: -------------------------------------------------------------------------------- 1 | mod Main { 2 | pub fn main(argc: i64) -> i64 { 3 | let mut a: i64 = 0; 4 | 5 | while a < 10 { 6 | a = a + 1; 7 | } 8 | 9 | return a; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /programs/simple.ed: -------------------------------------------------------------------------------- 1 | mod Main { 2 | 3 | pub fn main(argc: i64) -> i64 { 4 | let mut a: i64 = 0; 5 | 6 | if argc > 2 { 7 | a = 1; 8 | } 9 | 10 | return a; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /programs/ptr.ed: -------------------------------------------------------------------------------- 1 | mod Main { 2 | 3 | pub fn main(argc: i64, argv: ptr) -> i64 { 4 | let mut a: i64 = 0; 5 | 6 | if argc > 2 { 7 | a = 1; 8 | } 9 | 10 | return a; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /programs/refs.ed: -------------------------------------------------------------------------------- 1 | mod Main { 2 | pub fn main() -> i64 { 3 | let mut a: i64 = 0; 4 | 5 | hello(&mut a); 6 | 7 | return a; 8 | } 9 | 10 | pub fn hello(a: &mut i64) { 11 | *a = 2; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/edlang_driver/tests/programs/while_if_false.ed: -------------------------------------------------------------------------------- 1 | fn main() -> i32 { 2 | let mut result: i32 = 7; 3 | 4 | while false { 5 | if false { 6 | return 0; 7 | } 8 | result = 14; 9 | } 10 | 11 | return result; 12 | } 13 | -------------------------------------------------------------------------------- /lib/edlang_driver/tests/programs/if_if_false.ed: -------------------------------------------------------------------------------- 1 | 2 | fn main() -> i32 { 3 | let mut foo: i32 = 7; 4 | 5 | if true { 6 | if false { 7 | return 1; 8 | } 9 | } else { 10 | foo = 14; 11 | } 12 | 13 | return foo; 14 | } 15 | -------------------------------------------------------------------------------- /lib/edlang_driver/tests/programs/factorial.ed: -------------------------------------------------------------------------------- 1 | 2 | pub fn main() -> i32 { 3 | let b: i32 = factorial(4); 4 | return b; 5 | } 6 | 7 | fn factorial(n: i32) -> i32 { 8 | if n == 1 { 9 | return n; 10 | } else { 11 | return n * factorial(n - 1); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/edlang_driver/tests/programs/struct.ed: -------------------------------------------------------------------------------- 1 | 2 | struct Hello { 3 | a: i32, 4 | b: i64, 5 | } 6 | 7 | pub fn main() -> i64 { 8 | let mut x: Hello = Hello { 9 | a: 2, 10 | b: 3, 11 | }; 12 | hello(&mut x); 13 | return x.b; 14 | } 15 | 16 | pub fn hello(a: &mut Hello) { 17 | a.b = 5; 18 | } 19 | -------------------------------------------------------------------------------- /programs/factorial.ed: -------------------------------------------------------------------------------- 1 | mod Main { 2 | pub fn main() -> i32 { 3 | let b: i32 = factorial(4); 4 | return b; 5 | } 6 | 7 | pub fn factorial(n: i32) -> i32 { 8 | if n == 1 { 9 | return n; 10 | } else { 11 | return n * factorial(n - 1); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/edlang_driver/tests/programs/basic_ifs.ed: -------------------------------------------------------------------------------- 1 | 2 | fn add(a: i32, b: i32) -> i32 { 3 | return a + b; 4 | } 5 | 6 | fn check(a: i32) -> i32 { 7 | if a == 2 { 8 | return a; 9 | } else { 10 | return 0; 11 | } 12 | } 13 | 14 | pub fn main() -> i32 { 15 | let x: i32 = 2 + 3; 16 | let y: i32 = add(x, 4); 17 | return y; 18 | } 19 | -------------------------------------------------------------------------------- /lib/edlang_driver/tests/programs/struct_impl.ed: -------------------------------------------------------------------------------- 1 | extern fn printf(a: &str); 2 | 3 | fn main() -> u32 { 4 | let x: A = A { 5 | a: 2 6 | }; 7 | 8 | let y: u32 = x.hello(4); 9 | 10 | return y; 11 | } 12 | 13 | struct A { 14 | a: u32, 15 | } 16 | 17 | impl A { 18 | pub fn hello(self: &A, x: u32) -> u32 { 19 | return self.a + x; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /programs/struct.ed: -------------------------------------------------------------------------------- 1 | mod Main { 2 | 3 | struct Hello { 4 | a: i32, 5 | b: i64, 6 | } 7 | 8 | pub fn main() -> i64 { 9 | let x: Hello = Hello { 10 | a: 2, 11 | b: 3, 12 | }; 13 | hello(&mut x); 14 | return x.b; 15 | } 16 | 17 | pub fn hello(a: &mut Hello) { 18 | a.b = 5; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/edlang_codegen_llvm/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::too_many_arguments)] 2 | 3 | use edlang_ir as ir; 4 | use std::path::PathBuf; 5 | 6 | use edlang_session::Session; 7 | use ir::ProgramBody; 8 | 9 | pub mod codegen; 10 | 11 | pub fn compile( 12 | session: &Session, 13 | program: &ProgramBody, 14 | ) -> Result> { 15 | codegen::compile(session, program) 16 | } 17 | -------------------------------------------------------------------------------- /programs/example.ed: -------------------------------------------------------------------------------- 1 | mod Main { 2 | fn add(a: i32, b: i32) -> i32 { 3 | return a + b; 4 | } 5 | 6 | fn check(a: i32) -> i32 { 7 | if a == 2 { 8 | return a; 9 | } else { 10 | return 0; 11 | } 12 | } 13 | 14 | pub fn main(argc: i32) -> i32 { 15 | let x: i32 = 2 + 3; 16 | let y: i32 = add(x, 4); 17 | return y; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/edlang_span/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Range; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Hash)] 4 | pub struct Span { 5 | pub lo: usize, 6 | pub hi: usize, 7 | } 8 | 9 | impl Span { 10 | pub fn new(lo: usize, hi: usize) -> Self { 11 | Self { lo, hi } 12 | } 13 | } 14 | 15 | impl From for Range { 16 | fn from(val: Span) -> Self { 17 | val.lo..val.hi 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/edlang_span/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "edlang_span" 3 | version = "0.0.1-alpha.19" 4 | authors = ["Edgar Luque "] 5 | description = "edlang span" 6 | edition = "2021" 7 | keywords = ["llvm", "compiler"] 8 | license = "AGPL-3.0-only" 9 | categories = ["compilers"] 10 | documentation = "https://docs.rs/edlang_span" 11 | repository = "https://github.com/edg-l/edlang" 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | -------------------------------------------------------------------------------- /lib/edlang_ast/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "edlang_ast" 3 | version = "0.0.1-alpha.19" 4 | authors = ["Edgar Luque "] 5 | description = "edlang AST" 6 | edition = "2021" 7 | keywords = ["llvm", "compiler"] 8 | license = "AGPL-3.0-only" 9 | categories = ["compilers"] 10 | documentation = "https://docs.rs/edlang_ast" 11 | repository = "https://github.com/edg-l/edlang" 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | edlang_span = { version = "0.0.1-alpha.19", path = "../edlang_span" } 17 | -------------------------------------------------------------------------------- /lib/edlang_session/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "edlang_session" 3 | version = "0.0.1-alpha.19" 4 | authors = ["Edgar Luque "] 5 | description = "edlang session" 6 | edition = "2021" 7 | keywords = ["llvm", "compiler"] 8 | license = "AGPL-3.0-only" 9 | categories = ["compilers"] 10 | documentation = "https://docs.rs/edlang_session" 11 | repository = "https://github.com/edg-l/edlang" 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | ariadne = { version = "0.4.1", features = ["auto-color"] } 17 | -------------------------------------------------------------------------------- /edb/src/config.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use serde::{Deserialize, Serialize}; 4 | 5 | #[derive(Debug, Serialize, Deserialize)] 6 | pub struct Config { 7 | pub package: Package, 8 | pub profile: HashMap, 9 | } 10 | 11 | #[derive(Debug, Serialize, Deserialize, Default)] 12 | pub struct Package { 13 | pub name: String, 14 | pub version: String, 15 | pub license: String, 16 | } 17 | 18 | #[derive(Debug, Serialize, Deserialize, Default)] 19 | pub struct Profile { 20 | pub release: bool, 21 | pub opt_level: u8, 22 | pub debug_info: bool, 23 | } 24 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ "bin/edlangc", "edb", "lib/edlang_ast", "lib/edlang_check", "lib/edlang_codegen_llvm", 5 | "lib/edlang_driver", "lib/edlang_ir", "lib/edlang_lowering","lib/edlang_parser", "lib/edlang_session", "lib/edlang_span"] 6 | 7 | [profile.release] 8 | lto = true 9 | codegen-units = 1 10 | 11 | # Set the settings for build scripts and proc-macros. 12 | [profile.dev.build-override] 13 | opt-level = 3 14 | 15 | # On dev optimize dependencies a bit so it's not as slow. 16 | [profile.dev.package."*"] 17 | opt-level = 1 18 | 19 | [workspace.dependencies] 20 | tracing = "0.1.40" 21 | -------------------------------------------------------------------------------- /lib/edlang_ir/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "edlang_ir" 3 | version = "0.0.1-alpha.19" 4 | authors = ["Edgar Luque "] 5 | description = "edlang IR" 6 | edition = "2021" 7 | keywords = ["llvm", "compiler"] 8 | license = "AGPL-3.0-only" 9 | categories = ["compilers"] 10 | documentation = "https://docs.rs/edlang_ir" 11 | repository = "https://github.com/edg-l/edlang" 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | edlang_span = { version = "0.0.1-alpha.19", path = "../edlang_span" } 17 | smallvec = "1.13.2" 18 | educe = "0.5.11" 19 | -------------------------------------------------------------------------------- /bin/edlangc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "edlangc" 3 | version = "0.0.1-alpha.19" 4 | authors = ["Edgar Luque "] 5 | description = "A experimental language using LLVM." 6 | edition = "2021" 7 | readme = "README.md" 8 | keywords = ["llvm", "compiler"] 9 | license = "AGPL-3.0-only" 10 | categories = ["compilers"] 11 | documentation = "https://docs.rs/edlang" 12 | repository = "https://github.com/edg-l/edlang" 13 | 14 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 15 | 16 | [dependencies] 17 | edlang_driver = { version = "0.0.1-alpha.19", path = "../../lib/edlang_driver" } 18 | -------------------------------------------------------------------------------- /lib/edlang_lowering/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "edlang_lowering" 3 | version = "0.0.1-alpha.19" 4 | authors = ["Edgar Luque "] 5 | description = "edlang lowering" 6 | edition = "2021" 7 | keywords = ["llvm", "compiler"] 8 | license = "AGPL-3.0-only" 9 | categories = ["compilers"] 10 | documentation = "https://docs.rs/edlang_lowering" 11 | repository = "https://github.com/edg-l/edlang" 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | edlang_ast = { version = "0.0.1-alpha.19", path = "../edlang_ast" } 17 | edlang_ir = { version = "0.0.1-alpha.19", path = "../edlang_ir" } 18 | tracing.workspace = true 19 | thiserror = "1.0.61" 20 | -------------------------------------------------------------------------------- /edb/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "edb" 3 | version = "0.0.1-alpha.19" 4 | authors = ["Edgar Luque "] 5 | description = "The edlang language builder." 6 | edition = "2021" 7 | readme = "README.md" 8 | keywords = ["llvm", "compiler"] 9 | license = "AGPL-3.0-only" 10 | categories = ["compilers"] 11 | documentation = "https://docs.rs/edlang" 12 | repository = "https://github.com/edg-l/edlang" 13 | 14 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 15 | 16 | [dependencies] 17 | edlang_driver = { version = "0.0.1-alpha.19", path = "../lib/edlang_driver" } 18 | anyhow = "1" 19 | clap = { version = "4.5.4", features = ["derive"] } 20 | toml = "0.8.12" 21 | serde = { version = "1.0.200", features = ["derive"] } 22 | git2 = "0.18.3" 23 | owo-colors = "4.0.0" 24 | -------------------------------------------------------------------------------- /lib/edlang_check/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "edlang_check" 3 | version = "0.0.1-alpha.19" 4 | authors = ["Edgar Luque "] 5 | description = "edlang check" 6 | edition = "2021" 7 | keywords = ["llvm", "compiler"] 8 | license = "AGPL-3.0-only" 9 | categories = ["compilers"] 10 | documentation = "https://docs.rs/edlang_check" 11 | repository = "https://github.com/edg-l/edlang" 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | ariadne = { version = "0.4.1", features = ["auto-color"] } 17 | edlang_ast = { version = "0.0.1-alpha.19", path = "../edlang_ast" } 18 | edlang_lowering = { version = "0.0.1-alpha.19", path = "../edlang_lowering" } 19 | edlang_session = { version = "0.0.1-alpha.19", path = "../edlang_session" } 20 | tracing = { workspace = true } 21 | -------------------------------------------------------------------------------- /lib/edlang_parser/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "edlang_parser" 3 | version = "0.0.1-alpha.19" 4 | authors = ["Edgar Luque "] 5 | description = "edlang parser" 6 | edition = "2021" 7 | keywords = ["llvm", "compiler"] 8 | license = "AGPL-3.0-only" 9 | categories = ["compilers"] 10 | documentation = "https://docs.rs/edlang_parser" 11 | repository = "https://github.com/edg-l/edlang" 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | ariadne = { version = "0.4.1", features = ["auto-color"] } 17 | edlang_ast = { version = "0.0.1-alpha.19", path = "../edlang_ast" } 18 | itertools = "0.13.0" 19 | lalrpop-util = { version = "0.20.2", features = ["lexer"] } 20 | logos = "0.14.0" 21 | tracing = { workspace = true } 22 | 23 | [build-dependencies] 24 | lalrpop = "0.20.2" 25 | -------------------------------------------------------------------------------- /lib/edlang_session/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use ariadne::Source; 4 | 5 | #[derive(Debug, Clone)] 6 | pub struct Session { 7 | pub file_paths: Vec, 8 | pub debug_info: DebugInfo, 9 | pub optlevel: OptLevel, 10 | pub sources: Vec>, 11 | pub library: bool, 12 | pub output_file: PathBuf, 13 | pub output_llvm: bool, 14 | pub output_asm: bool, 15 | } 16 | 17 | impl Session { 18 | pub fn get_platform_library_ext() -> &'static str { 19 | if cfg!(target_os = "macos") { 20 | "dylib" 21 | } else if cfg!(target_os = "windows") { 22 | "dll" 23 | } else { 24 | "so" 25 | } 26 | } 27 | } 28 | 29 | #[derive(Clone, Copy, Debug, PartialEq, Hash)] 30 | pub enum OptLevel { 31 | None, // -O0 32 | Less, // -O1 33 | Default, // -O2 34 | Aggressive, // -O3 35 | } 36 | 37 | #[derive(Clone, Copy, Debug, PartialEq, Hash)] 38 | pub enum DebugInfo { 39 | None, 40 | Full, 41 | } 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # edlang 2 | 3 | An experimental statically-typed compiled programming language made with LLVM and Rust. 4 | 5 | Syntax is subject to change any time right now. It has a rusty style for now. 6 | 7 | ```rust 8 | pub fn main() -> i32 { 9 | let b: i32 = factorial(4); 10 | return b; 11 | } 12 | 13 | pub fn factorial(n: i32) -> i32 { 14 | if n == 1 { 15 | return n; 16 | } else { 17 | return n * factorial(n - 1); 18 | } 19 | } 20 | 21 | mod hello { 22 | pub fn world(ptr: *const u8) -> u8 { 23 | return *ptr; 24 | } 25 | } 26 | ``` 27 | 28 | ## edb: The edlang builder 29 | 30 | `edb` is a tool like cargo but for edlang: 31 | 32 | ``` 33 | edlang builder 34 | 35 | Usage: edlang 36 | 37 | Commands: 38 | new Initialize a project 39 | build Build a project 40 | help Print this message or the help of the given subcommand(s) 41 | 42 | Options: 43 | -h, --help Print help 44 | -V, --version Print version 45 | ``` 46 | 47 | ## Dependencies 48 | 49 | - Rust 50 | - LLVM 18 51 | -------------------------------------------------------------------------------- /lib/edlang_codegen_llvm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "edlang_codegen_llvm" 3 | version = "0.0.1-alpha.19" 4 | authors = ["Edgar Luque "] 5 | description = "edlang LLVM codegen" 6 | edition = "2021" 7 | keywords = ["llvm", "compiler"] 8 | license = "AGPL-3.0-only" 9 | categories = ["compilers"] 10 | documentation = "https://docs.rs/ededlang_codegen_llvmang" 11 | repository = "https://github.com/edg-l/edlang" 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | edlang_ir = { version = "0.0.1-alpha.19", path = "../edlang_ir" } 17 | edlang_parser = { version = "0.0.1-alpha.19", path = "../edlang_parser" } 18 | edlang_session = { version = "0.0.1-alpha.19", path = "../edlang_session" } 19 | llvm-sys = "180.0" 20 | inkwell = { git = "https://github.com/TheDan64/inkwell", rev = "89e06af9dd70dc5d6bc5ae42a2a03f680a367d37", features = [ 21 | "llvm18-0", 22 | ] } 23 | tracing = { workspace = true } 24 | edlang_span = { version = "0.0.1-alpha.19", path = "../edlang_span" } 25 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: Deploy Docs to GitHub Pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | permissions: 9 | contents: write 10 | 11 | jobs: 12 | publish-docs: 13 | name: GitHub Pages 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: dtolnay/rust-toolchain@stable 18 | - uses: Swatinem/rust-cache@v2 19 | - name: add llvm deb repository 20 | uses: myci-actions/add-deb-repo@11 21 | with: 22 | repo: deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-18 main 23 | repo-name: llvm-repo 24 | keys-asc: https://apt.llvm.org/llvm-snapshot.gpg.key 25 | - name: Install LLVM 26 | run: sudo apt-get install llvm-18 llvm-18-dev llvm-18-runtime clang-18 clang-tools-18 lld-18 libpolly-18-dev libmlir-18-dev mlir-18-tools 27 | - name: Build docs 28 | run: cargo doc 29 | - name: Deploy Documentation 30 | uses: peaceiris/actions-gh-pages@v3 31 | with: 32 | github_token: ${{ secrets.GITHUB_TOKEN }} 33 | publish_branch: gh-pages 34 | publish_dir: ./target/doc/ 35 | keep_files: false 36 | -------------------------------------------------------------------------------- /lib/edlang_driver/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "edlang_driver" 3 | version = "0.0.1-alpha.19" 4 | authors = ["Edgar Luque "] 5 | description = "edlang compiler driver library" 6 | edition = "2021" 7 | keywords = ["llvm", "compiler"] 8 | license = "AGPL-3.0-only" 9 | categories = ["compilers"] 10 | documentation = "https://docs.rs/edlang_driver" 11 | repository = "https://github.com/edg-l/edlang" 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | ariadne = { version = "0.4.1", features = ["auto-color"] } 17 | clap = { version = "4.5.4", features = ["derive"] } 18 | anyhow = "1.0.86" 19 | edlang_ast = { version = "0.0.1-alpha.19", path = "../edlang_ast" } 20 | edlang_check = { version = "0.0.1-alpha.19", path = "../edlang_check" } 21 | edlang_codegen_llvm = { version = "0.0.1-alpha.19", path = "../edlang_codegen_llvm" } 22 | edlang_ir = { version = "0.0.1-alpha.19", path = "../edlang_ir" } 23 | edlang_lowering = { version = "0.0.1-alpha.19", path = "../edlang_lowering" } 24 | edlang_parser = { version = "0.0.1-alpha.19", path = "../edlang_parser" } 25 | edlang_session = { version = "0.0.1-alpha.19", path = "../edlang_session" } 26 | tracing = { workspace = true } 27 | tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } 28 | walkdir = "2.5.0" 29 | 30 | [dev-dependencies] 31 | tempfile = "3.10.1" 32 | test-case = "3.3.1" 33 | -------------------------------------------------------------------------------- /lib/edlang_parser/src/lexer.rs: -------------------------------------------------------------------------------- 1 | use std::{fmt::Display, ops::Range}; 2 | 3 | use logos::{Logos, SpannedIter}; 4 | 5 | use crate::tokens::{LexingError, Token}; 6 | 7 | pub type Spanned = Result<(Loc, Tok, Loc), Error>; 8 | 9 | #[derive(Debug, Clone)] 10 | pub enum LexicalError { 11 | InvalidToken(LexingError, Range), 12 | } 13 | 14 | impl Display for LexicalError { 15 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 16 | match self { 17 | LexicalError::InvalidToken(err, span) => { 18 | write!(f, "lexical error at ({:?}): {:?}", err, span) 19 | } 20 | } 21 | } 22 | } 23 | 24 | pub struct Lexer<'input> { 25 | // instead of an iterator over characters, we have a token iterator 26 | token_stream: SpannedIter<'input, Token>, 27 | } 28 | 29 | impl<'input> Lexer<'input> { 30 | pub fn new(input: &'input str) -> Self { 31 | // the Token::lexer() method is provided by the Logos trait 32 | Self { 33 | token_stream: Token::lexer(input).spanned(), 34 | } 35 | } 36 | } 37 | 38 | impl<'input> Iterator for Lexer<'input> { 39 | type Item = Spanned; 40 | 41 | fn next(&mut self) -> Option { 42 | self.token_stream.next().map(|(token, span)| match token { 43 | Ok(token) => Ok((span.start, token, span.end)), 44 | Err(err) => Err(LexicalError::InvalidToken(err, span)), 45 | }) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | permissions: 4 | contents: write 5 | discussions: write 6 | 7 | on: 8 | push: 9 | tags: 10 | - "v*.*.*" 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | outputs: 16 | release_body: ${{ steps.git-cliff.outputs.content }} 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v4 20 | - name: Setup rust env 21 | uses: dtolnay/rust-toolchain@stable 22 | - name: Retreive cached dependecies 23 | uses: Swatinem/rust-cache@v2 24 | - name: add llvm deb repository 25 | uses: myci-actions/add-deb-repo@11 26 | with: 27 | repo: deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-18 main 28 | repo-name: llvm-repo 29 | keys-asc: https://apt.llvm.org/llvm-snapshot.gpg.key 30 | - name: Install LLVM 31 | run: sudo apt-get install llvm-18 llvm-18-dev llvm-18-runtime clang-18 clang-tools-18 lld-18 libpolly-18-dev 32 | - name: Install Link deps 33 | run: sudo apt-get install libc-dev build-essential 34 | - name: build release 35 | run: cargo build --release 36 | - name: Generate a changelog 37 | uses: orhun/git-cliff-action@v3 38 | id: git-cliff 39 | with: 40 | args: -vv --current --strip header 41 | env: 42 | OUTPUT: CHANGES.md 43 | - name: Release 44 | uses: softprops/action-gh-release@v2 45 | with: 46 | body_path: CHANGES.md 47 | files: | 48 | README.md 49 | LICENSE 50 | CHANGES.md 51 | target/release/edlangc 52 | target/release/edb 53 | -------------------------------------------------------------------------------- /lib/edlang_ir/src/scalar_int.rs: -------------------------------------------------------------------------------- 1 | use std::num::NonZeroU8; 2 | 3 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)] 4 | #[repr(packed(1))] 5 | pub struct ScalarInt { 6 | pub data: u128, 7 | /// Size in bytes 8 | pub size: NonZeroU8, 9 | } 10 | 11 | impl ScalarInt { 12 | /// Size in bytes 13 | #[inline] 14 | pub fn size(self) -> u64 { 15 | self.size.get().into() 16 | } 17 | 18 | #[inline] 19 | pub fn try_from_int(i: impl Into, size_in_bytes: u64) -> Option { 20 | let i = i.into(); 21 | // `into` performed sign extension, we have to truncate 22 | let truncated = truncate(size_in_bytes, i as u128); 23 | if sign_extend(size_in_bytes, truncated) as i128 == i { 24 | Some(Self { 25 | data: truncated, 26 | size: NonZeroU8::new(size_in_bytes as u8).unwrap(), 27 | }) 28 | } else { 29 | None 30 | } 31 | } 32 | } 33 | 34 | #[inline] 35 | pub fn sign_extend(size: u64, value: u128) -> u128 { 36 | let size = size * 8; 37 | if size == 0 { 38 | // Truncated until nothing is left. 39 | return 0; 40 | } 41 | // Sign-extend it. 42 | let shift = 128 - size; 43 | // Shift the unsigned value to the left, then shift back to the right as signed 44 | // (essentially fills with sign bit on the left). 45 | (((value << shift) as i128) >> shift) as u128 46 | } 47 | 48 | /// Truncates `value` to `self` bits. 49 | #[inline] 50 | pub fn truncate(size: u64, value: u128) -> u128 { 51 | let size = size * 8; 52 | if size == 0 { 53 | // Truncated until nothing is left. 54 | return 0; 55 | } 56 | let shift = 128 - size; 57 | // Truncate (shift left to drop out leftover values, shift right to fill with zeroes). 58 | (value << shift) >> shift 59 | } 60 | -------------------------------------------------------------------------------- /lib/edlang_driver/tests/common.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | path::{Path, PathBuf}, 3 | process::Child, 4 | }; 5 | 6 | use ariadne::Source; 7 | use edlang_driver::linker::{link_binary, link_shared_lib}; 8 | use edlang_lowering::lower_modules; 9 | use edlang_session::{DebugInfo, OptLevel, Session}; 10 | use tempfile::TempDir; 11 | 12 | #[derive(Debug)] 13 | pub struct CompileResult { 14 | pub folder: TempDir, 15 | pub object_file: PathBuf, 16 | pub binary_file: PathBuf, 17 | } 18 | 19 | pub fn compile_program( 20 | source: &str, 21 | name: &str, 22 | library: bool, 23 | ) -> Result> { 24 | let module = edlang_parser::parse_ast(source, name).unwrap(); 25 | 26 | let test_dir = tempfile::tempdir().unwrap(); 27 | let test_dir_path = test_dir.path().canonicalize()?; 28 | let output_file = test_dir_path.join(PathBuf::from(name)); 29 | let output_file = if library { 30 | output_file.with_extension(Session::get_platform_library_ext()) 31 | } else if cfg!(target_os = "windows") { 32 | output_file.with_extension("exe") 33 | } else { 34 | output_file.with_extension("") 35 | }; 36 | 37 | let input_file = output_file.with_extension("ed"); 38 | std::fs::write(&input_file, source)?; 39 | 40 | let session = Session { 41 | file_paths: vec![input_file], 42 | debug_info: DebugInfo::Full, 43 | optlevel: OptLevel::Default, 44 | sources: vec![Source::from(source.to_string())], 45 | library, 46 | output_file, 47 | output_llvm: false, 48 | output_asm: false, 49 | }; 50 | 51 | let program_ir = lower_modules(&[module]).unwrap(); 52 | 53 | let object_path = edlang_codegen_llvm::compile(&session, &program_ir).unwrap(); 54 | 55 | if library { 56 | link_shared_lib(&[object_path.clone()], &session.output_file).unwrap(); 57 | } else { 58 | link_binary(&[object_path.clone()], &session.output_file).unwrap(); 59 | } 60 | 61 | Ok(CompileResult { 62 | folder: test_dir, 63 | object_file: object_path, 64 | binary_file: session.output_file, 65 | }) 66 | } 67 | 68 | pub fn run_program(program: &Path, args: &[&str]) -> Result { 69 | std::process::Command::new(program).args(args).spawn() 70 | } 71 | -------------------------------------------------------------------------------- /lib/edlang_lowering/src/common.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use edlang_ir::{Body, DefId, Local, ModuleBody, ProgramBody, Statement, TypeKind}; 4 | 5 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)] 6 | pub struct IdGenerator { 7 | pub current_id: usize, 8 | pub module_id: usize, 9 | } 10 | 11 | impl IdGenerator { 12 | pub const fn new(module_id: usize) -> Self { 13 | Self { 14 | current_id: 0, 15 | module_id, 16 | } 17 | } 18 | 19 | pub fn next_id(&mut self) -> usize { 20 | self.current_id += 1; 21 | self.current_id 22 | } 23 | 24 | pub fn module_defid(&self) -> DefId { 25 | DefId { 26 | program_id: self.module_id, 27 | id: 0, 28 | } 29 | } 30 | 31 | pub fn next_defid(&mut self) -> DefId { 32 | let id = self.next_id(); 33 | 34 | DefId { 35 | program_id: self.module_id, 36 | id, 37 | } 38 | } 39 | 40 | pub fn next_module_defid(&mut self) -> DefId { 41 | self.module_id += 1; 42 | self.current_id = 0; 43 | 44 | self.module_defid() 45 | } 46 | } 47 | 48 | #[derive(Debug, Clone, Default)] 49 | pub struct BuildCtx { 50 | pub body: ProgramBody, 51 | pub unresolved_function_signatures: 52 | HashMap, Option)>, 53 | pub gen: IdGenerator, 54 | } 55 | 56 | #[derive(Debug, Clone)] 57 | pub struct BodyBuilder { 58 | pub local_module: DefId, 59 | pub body: Body, 60 | pub statements: Vec, 61 | pub name_to_local: HashMap, 62 | pub ret_local: usize, 63 | pub file_id: usize, 64 | pub ctx: BuildCtx, 65 | } 66 | 67 | impl BodyBuilder { 68 | pub fn add_local(&mut self, local: Local) -> usize { 69 | let id = self.body.locals.len(); 70 | self.body.locals.push(local); 71 | id 72 | } 73 | 74 | pub fn add_temp_local(&mut self, ty_kind: TypeKind) -> usize { 75 | let id = self.body.locals.len(); 76 | self.body.locals.push(Local::temp(ty_kind)); 77 | id 78 | } 79 | 80 | pub fn get_local(&self, name: &str) -> Option<&Local> { 81 | self.body.locals.get(*(self.name_to_local.get(name)?)) 82 | } 83 | 84 | pub fn get_module_body(&self) -> &ModuleBody { 85 | self.ctx.body.modules.get(&self.local_module).unwrap() 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | merge_group: 8 | types: [checks_requested] 9 | 10 | jobs: 11 | check: 12 | name: clippy 13 | runs-on: ubuntu-latest 14 | env: 15 | LLVM_SYS_180_PREFIX: /usr/lib/llvm-18/ 16 | TABLEGEN_180_PREFIX: /usr/lib/llvm-18/ 17 | steps: 18 | - uses: actions/checkout@v4 19 | - uses: dtolnay/rust-toolchain@stable 20 | with: 21 | components: rustfmt, clippy 22 | - uses: Swatinem/rust-cache@v2 23 | - name: add llvm deb repository 24 | uses: myci-actions/add-deb-repo@11 25 | with: 26 | repo: deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-18 main 27 | repo-name: llvm-repo 28 | keys-asc: https://apt.llvm.org/llvm-snapshot.gpg.key 29 | - name: Install LLVM 30 | run: sudo apt-get install llvm-18 llvm-18-dev llvm-18-runtime clang-18 clang-tools-18 lld-18 libpolly-18-dev 31 | - name: Clippy 32 | run: cargo clippy 33 | 34 | fmt: 35 | name: rustfmt 36 | runs-on: ubuntu-latest 37 | steps: 38 | - uses: actions/checkout@v3 39 | - uses: dtolnay/rust-toolchain@stable 40 | with: 41 | components: rustfmt 42 | - run: cargo fmt --all -- --check 43 | 44 | test: 45 | name: test (linux, amd64) 46 | runs-on: ubuntu-latest 47 | env: 48 | LLVM_SYS_180_PREFIX: /usr/lib/llvm-18/ 49 | TABLEGEN_180_PREFIX: /usr/lib/llvm-18/ 50 | RUST_LOG: debug 51 | steps: 52 | - uses: actions/checkout@v4 53 | - name: Setup rust env 54 | uses: dtolnay/rust-toolchain@stable 55 | - name: Retreive cached dependecies 56 | uses: Swatinem/rust-cache@v2 57 | - name: add llvm deb repository 58 | uses: myci-actions/add-deb-repo@11 59 | with: 60 | repo: deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-18 main 61 | repo-name: llvm-repo 62 | keys-asc: https://apt.llvm.org/llvm-snapshot.gpg.key 63 | - name: Install LLVM 64 | run: sudo apt-get install llvm-18 llvm-18-dev llvm-18-runtime clang-18 clang-tools-18 lld-18 libpolly-18-dev 65 | - name: Install Link deps 66 | run: sudo apt-get install libc-dev build-essential 67 | - name: test 68 | run: cargo test 69 | -------------------------------------------------------------------------------- /lib/edlang_lowering/src/errors.rs: -------------------------------------------------------------------------------- 1 | use edlang_ast::{Ident, Span}; 2 | use edlang_ir::{TypeInfo, TypeKind}; 3 | use thiserror::Error; 4 | 5 | use crate::DefId; 6 | 7 | #[derive(Debug, Error, Clone)] 8 | pub enum LoweringError { 9 | #[error("module {module:?} not found")] 10 | ModuleNotFound { 11 | span: Span, 12 | module: String, 13 | file_id: usize, 14 | }, 15 | #[error("function {function:?} not found")] 16 | FunctionNotFound { 17 | span: Span, 18 | function: String, 19 | file_id: usize, 20 | }, 21 | #[error("symbol {:?} not found", symbol.name)] 22 | ImportNotFound { 23 | module_span: Span, 24 | import_span: Span, 25 | symbol: Ident, 26 | file_id: usize, 27 | }, 28 | #[error("trying to mutate a non-mutable reference")] 29 | BorrowNotMutable { 30 | span: Span, 31 | type_span: Option, 32 | name: String, 33 | file_id: usize, 34 | }, 35 | #[error("unrecognized type {name}")] 36 | UnrecognizedType { 37 | span: Span, 38 | name: String, 39 | file_id: usize, 40 | }, 41 | #[error("id not found")] 42 | IdNotFound { 43 | span: Span, 44 | id: DefId, 45 | file_id: usize, 46 | }, 47 | #[error("feature not yet implemented: {message}")] 48 | NotYetImplemented { 49 | span: Span, 50 | message: &'static str, 51 | file_id: usize, 52 | }, 53 | #[error("unexpected type")] 54 | UnexpectedType { 55 | span: Span, 56 | found: TypeKind, 57 | expected: TypeInfo, 58 | file_id: usize, 59 | }, 60 | #[error("use of underclared variable {name:?}")] 61 | UseOfUndeclaredVariable { 62 | span: Span, 63 | name: String, 64 | file_id: usize, 65 | }, 66 | #[error("parameter count mismatch to function call")] 67 | ParamCountMismatch { 68 | span: Span, 69 | has_args: usize, 70 | needs: usize, 71 | file_id: usize, 72 | }, 73 | #[error("can't mutate this value because it's not declared mutable")] 74 | NotMutable { 75 | span: Span, 76 | declare_span: Option, 77 | file_id: usize, 78 | }, 79 | #[error("can't take a mutable borrow to this value because it's not declared mutable")] 80 | CantTakeMutableBorrow { 81 | span: Span, 82 | declare_span: Option, 83 | file_id: usize, 84 | }, 85 | #[error("this method requires a mutable 'self'")] 86 | NotMutableSelf { 87 | span: Span, 88 | path_span: Span, 89 | file_id: usize, 90 | }, 91 | } 92 | -------------------------------------------------------------------------------- /lib/edlang_parser/src/tokens.rs: -------------------------------------------------------------------------------- 1 | use logos::Logos; 2 | use std::convert::Infallible; 3 | 4 | #[derive(Debug, PartialEq, Clone, Default)] 5 | pub enum LexingError { 6 | NumberParseError, 7 | #[default] 8 | Other, 9 | } 10 | 11 | impl From for LexingError { 12 | fn from(_: std::num::ParseIntError) -> Self { 13 | LexingError::NumberParseError 14 | } 15 | } 16 | 17 | impl From for LexingError { 18 | fn from(_: Infallible) -> Self { 19 | LexingError::Other 20 | } 21 | } 22 | 23 | #[derive(Logos, Debug, PartialEq, Clone)] 24 | #[logos(error = LexingError, skip r"[ \t\n\f]+", skip r"//[^\n]*", skip r"/\*(?:[^*]|\*[^/])*\*/")] 25 | pub enum Token { 26 | #[token("let")] 27 | KeywordLet, 28 | #[token("const")] 29 | KeywordConst, 30 | #[token("fn")] 31 | KeywordFn, 32 | #[token("return")] 33 | KeywordReturn, 34 | #[token("struct")] 35 | KeywordStruct, 36 | #[token("if")] 37 | KeywordIf, 38 | #[token("else")] 39 | KeywordElse, 40 | #[token("while")] 41 | KeywordWhile, 42 | #[token("for")] 43 | KeywordFor, 44 | #[token("match")] 45 | KeywordMatch, 46 | #[token("mod")] 47 | KeywordMod, 48 | #[token("pub")] 49 | KeywordPub, 50 | #[token("mut")] 51 | KeywordMut, 52 | #[token("use")] 53 | KeywordUse, 54 | #[token("in")] 55 | KeywordIn, 56 | #[token("extern")] 57 | KeywordExtern, 58 | #[token("as")] 59 | KeywordAs, 60 | #[token("exported")] 61 | KeywordExported, 62 | #[token("impl")] 63 | KeywordImpl, 64 | 65 | // Modern way of allowing identifiers, read: https://unicode.org/reports/tr31/ 66 | #[regex(r"[\p{XID_Start}_]\p{XID_Continue}*", |lex| lex.slice().to_string())] 67 | Identifier(String), 68 | 69 | // Literals 70 | #[regex(r"\d+", |lex| lex.slice().parse::().unwrap())] 71 | Integer(u128), 72 | #[regex(r#""(?:[^"]|\\")*""#, |lex| lex.slice().to_string())] 73 | String(String), 74 | #[regex(r"(true|false)", |lex| lex.slice().parse::().unwrap())] 75 | Boolean(bool), 76 | 77 | #[token("(")] 78 | LeftParen, 79 | #[token(")")] 80 | RightParen, 81 | #[token("{")] 82 | LeftBracket, 83 | #[token("}")] 84 | RightBracket, 85 | #[token("[")] 86 | LeftSquareBracket, 87 | #[token("]")] 88 | RightSquareBracket, 89 | #[token("=")] 90 | Assign, 91 | #[token(";")] 92 | Semicolon, 93 | #[token(":")] 94 | Colon, 95 | #[token("::")] 96 | DoubleColon, 97 | #[token("->")] 98 | Arrow, 99 | #[token(",")] 100 | Coma, 101 | #[token(".")] 102 | Dot, 103 | #[token("..")] 104 | TwoDots, 105 | #[token("<")] 106 | LessThanSign, 107 | #[token(">")] 108 | MoreThanSign, 109 | #[token(">=")] 110 | MoreThanEqSign, 111 | #[token("<=")] 112 | LessThanEqSign, 113 | 114 | #[token("+")] 115 | OperatorAdd, 116 | #[token("-")] 117 | OperatorSub, 118 | #[token("*")] 119 | OperatorMul, 120 | #[token("/")] 121 | OperatorDiv, 122 | #[token("%")] 123 | OperatorRem, 124 | #[token("&&")] 125 | OperatorAnd, 126 | #[token("||")] 127 | OperatorOr, 128 | #[token("==")] 129 | OperatorEq, 130 | #[token("!=")] 131 | OperatorNe, 132 | #[token("!")] 133 | OperatorNot, 134 | #[token("~")] 135 | OperatorBitwiseNot, 136 | #[token("^")] 137 | OperatorBitwiseXor, 138 | #[token("&")] 139 | OperatorBitwiseAnd, 140 | #[token("|")] 141 | OperatorBitwiseOr, 142 | } 143 | -------------------------------------------------------------------------------- /lib/edlang_driver/tests/programs.rs: -------------------------------------------------------------------------------- 1 | use crate::common::{compile_program, run_program}; 2 | use test_case::test_case; 3 | 4 | mod common; 5 | 6 | #[test_case(include_str!("programs/simple.ed"), "simple", false, 0, &["1"] ; "simple.ed 1")] 7 | #[test_case(include_str!("programs/simple.ed"), "simple", false, 1, &["a", "b"] ; "simple.ed 3")] 8 | #[test_case(include_str!("programs/basic_ifs.ed"), "basic_ifs", false, 9, &[] ; "basic_ifs")] 9 | #[test_case(include_str!("programs/while.ed"), "while", false, 10, &[] ; "r#while")] 10 | #[test_case(include_str!("programs/factorial.ed"), "factorial", false, 24, &[] ; "factorial")] 11 | #[test_case(include_str!("programs/refs.ed"), "refs", false, 2, &[] ; "refs")] 12 | #[test_case(include_str!("programs/struct.ed"), "struct", false, 5, &[] ; "r#struct")] 13 | #[test_case(include_str!("programs/struct_impl.ed"), "struct_impl", false, 6, &[] ; "struct_impl")] 14 | #[test_case(include_str!("programs/casts.ed"), "casts", false, 2, &[] ; "casts")] 15 | #[test_case(TEST_ADD, "test_add", false, 2, &[] ; "test_add")] 16 | #[test_case(TEST_SUB, "test_sub", false, 1, &[] ; "test_sub")] 17 | #[test_case(TEST_MUL, "test_mul", false, 4, &[] ; "TEST_MUL")] 18 | #[test_case(TEST_DIV, "test_div", false, 2, &[] ; "TEST_DIV")] 19 | #[test_case(TEST_REM, "test_rem", false, 0, &[] ; "TEST_REM")] 20 | #[test_case(TEST_IF_BOTH, "test_if_both", false, 1, &[] ; "test_if_both")] 21 | #[test_case(TEST_IF_BOTH, "test_if_both", false, 2, &["a"] ; "test_if_both_args")] 22 | #[test_case(TEST_IF_NO_ELSE, "test_if_no_else", false, 1, &[] ; "test_if_no_else")] 23 | #[test_case(TEST_IF_NO_ELSE, "test_if_no_else", false, 2, &["a"] ; "test_if_no_else_args")] 24 | #[test_case(include_str!("programs/while_if_false.ed"), "while_if_false", false, 7, &[] ; "while_if_false")] 25 | #[test_case(include_str!("programs/if_if_false.ed"), "if_if_false", false, 7, &[] ; "if_if_false")] 26 | fn example_tests(source: &str, name: &str, is_library: bool, status_code: i32, args: &[&str]) { 27 | let program = compile_program(source, name, is_library).unwrap(); 28 | 29 | dbg!(&program); 30 | assert!(program.binary_file.exists(), "program not compiled"); 31 | let mut result = run_program(&program.binary_file, args).unwrap(); 32 | let status = result.wait().unwrap(); 33 | assert_eq!( 34 | status.code().unwrap(), 35 | status_code, 36 | "Program {} returned a unexpected status code", 37 | name 38 | ); 39 | } 40 | 41 | const TEST_ADD: &str = r#" 42 | 43 | pub fn main() -> i32 { 44 | let b: i32 = 1 + 1; 45 | return b; 46 | } 47 | 48 | "#; 49 | const TEST_SUB: &str = r#" 50 | 51 | pub fn main() -> i32 { 52 | let b: i32 = 2 - 1; 53 | return b; 54 | } 55 | 56 | "#; 57 | const TEST_MUL: &str = r#" 58 | 59 | pub fn main() -> i32 { 60 | let b: i32 = 2 * 2; 61 | return b; 62 | } 63 | 64 | "#; 65 | const TEST_DIV: &str = r#" 66 | 67 | pub fn main() -> i32 { 68 | let b: i32 = 4 / 2; 69 | return b; 70 | } 71 | 72 | "#; 73 | const TEST_REM: &str = r#" 74 | 75 | pub fn main() -> i32 { 76 | let b: i32 = 4 % 2; 77 | return b; 78 | } 79 | 80 | "#; 81 | const TEST_IF_BOTH: &str = r#" 82 | 83 | pub fn main(argc: i32) -> i32 { 84 | if argc == 1 { 85 | return 1; 86 | } else { 87 | return 2; 88 | } 89 | } 90 | 91 | "#; 92 | const TEST_IF_NO_ELSE: &str = r#" 93 | 94 | pub fn main(argc: i32) -> i32 { 95 | if argc == 1 { 96 | return 1; 97 | } 98 | return 2; 99 | } 100 | 101 | "#; 102 | -------------------------------------------------------------------------------- /cliff.toml: -------------------------------------------------------------------------------- 1 | # git-cliff ~ default configuration file 2 | # https://git-cliff.org/docs/configuration 3 | # 4 | # Lines starting with "#" are comments. 5 | # Configuration options are organized into tables and keys. 6 | # See documentation for more information on available options. 7 | 8 | [changelog] 9 | # changelog header 10 | header = """ 11 | # Changelog\n 12 | All notable changes to this project will be documented in this file.\n 13 | """ 14 | # template for the changelog body 15 | # https://keats.github.io/tera/docs/#introduction 16 | body = """ 17 | {% if version %}\ 18 | ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }} 19 | {% else %}\ 20 | ## [unreleased] 21 | {% endif %}\ 22 | {% for group, commits in commits | group_by(attribute="group") %} 23 | ### {{ group | striptags | trim | upper_first }} 24 | {% for commit in commits %} 25 | - {% if commit.scope %}*({{ commit.scope }})* {% endif %}\ 26 | {% if commit.breaking %}[**breaking**] {% endif %}\ 27 | {{ commit.message | upper_first }}\ 28 | {% endfor %} 29 | {% endfor %}\n 30 | """ 31 | # template for the changelog footer 32 | footer = """ 33 | 34 | """ 35 | # remove the leading and trailing s 36 | trim = true 37 | # postprocessors 38 | postprocessors = [ 39 | # { pattern = '', replace = "https://github.com/orhun/git-cliff" }, # replace repository URL 40 | ] 41 | 42 | [git] 43 | # parse the commits based on https://www.conventionalcommits.org 44 | conventional_commits = true 45 | # filter out the commits that are not conventional 46 | filter_unconventional = false 47 | # process each line of a commit as an individual commit 48 | split_commits = false 49 | # regex for preprocessing the commit messages 50 | commit_preprocessors = [ 51 | # Replace issue numbers 52 | #{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](/issues/${2}))"}, 53 | # Check spelling of the commit with https://github.com/crate-ci/typos 54 | # If the spelling is incorrect, it will be automatically fixed. 55 | #{ pattern = '.*', replace_command = 'typos --write-changes -' }, 56 | ] 57 | # regex for parsing and grouping commits 58 | commit_parsers = [ 59 | { message = "^feat", group = "🚀 Features" }, 60 | { message = "^fix", group = "🐛 Bug Fixes" }, 61 | { message = "^doc", group = "📚 Documentation" }, 62 | { message = "^perf", group = "⚡ Performance" }, 63 | { message = "^refactor", group = "🚜 Refactor" }, 64 | { message = "^style", group = "🎨 Styling" }, 65 | { message = "^test", group = "🧪 Testing" }, 66 | { message = "^chore\\(release\\): prepare for", skip = true }, 67 | { message = "^chore\\(deps.*\\)", skip = true }, 68 | { message = "^chore\\(pr\\)", skip = true }, 69 | { message = "^chore\\(pull\\)", skip = true }, 70 | { message = "^chore|^ci", group = "⚙️ Miscellaneous Tasks" }, 71 | { body = ".*security", group = "🛡️ Security" }, 72 | { message = "^revert", group = "◀️ Revert" }, 73 | { message = ".*", group = "Other"}, 74 | ] 75 | # protect breaking changes from being skipped due to matching a skipping commit_parser 76 | protect_breaking_commits = false 77 | # filter out the commits that are not matched by commit parsers 78 | filter_commits = false 79 | # regex for matching git tags 80 | # tag_pattern = "v[0-9].*" 81 | # regex for skipping tags 82 | # skip_tags = "" 83 | # regex for ignoring tags 84 | # ignore_tags = "" 85 | # sort the tags topologically 86 | topo_order = false 87 | # sort the commits inside sections by oldest/newest order 88 | sort_commits = "oldest" 89 | # limit the number of commits included in the changelog. 90 | # limit_commits = 42 91 | -------------------------------------------------------------------------------- /lib/edlang_driver/src/linker.rs: -------------------------------------------------------------------------------- 1 | use std::path::{Path, PathBuf}; 2 | 3 | use tracing::instrument; 4 | 5 | #[instrument(level = "debug")] 6 | pub fn link_shared_lib(objects: &[PathBuf], output_filename: &Path) -> std::io::Result<()> { 7 | let objects: Vec<_> = objects.iter().map(|x| x.display().to_string()).collect(); 8 | let output_filename = output_filename.to_string_lossy().to_string(); 9 | 10 | let args: Vec<_> = { 11 | #[cfg(target_os = "macos")] 12 | { 13 | let mut args = vec![ 14 | "-demangle", 15 | "-no_deduplicate", 16 | "-dynamic", 17 | "-dylib", 18 | "-L/usr/local/lib", 19 | "-L/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib", 20 | ]; 21 | 22 | args.extend(objects.iter().map(|x| x.as_str())); 23 | 24 | args.extend(&["-o", &output_filename, "-lSystem"]); 25 | 26 | args 27 | } 28 | #[cfg(target_os = "linux")] 29 | { 30 | let mut args = vec!["--hash-style=gnu", "--eh-frame-hdr", "-shared"]; 31 | 32 | args.extend(&["-o", &output_filename]); 33 | 34 | args.extend(&["-L/lib/../lib64", "-L/usr/lib/../lib64", "-lc", "-O1"]); 35 | 36 | args.extend(objects.iter().map(|x| x.as_str())); 37 | 38 | args 39 | } 40 | #[cfg(target_os = "windows")] 41 | { 42 | unimplemented!() 43 | } 44 | }; 45 | 46 | let mut linker = std::process::Command::new("ld"); 47 | let proc = linker.args(args.iter()).spawn()?; 48 | proc.wait_with_output()?; 49 | Ok(()) 50 | } 51 | 52 | #[instrument(level = "debug")] 53 | pub fn link_binary(objects: &[PathBuf], output_filename: &Path) -> std::io::Result<()> { 54 | let objects: Vec<_> = objects.iter().map(|x| x.display().to_string()).collect(); 55 | let output_filename = output_filename.to_string_lossy().to_string(); 56 | 57 | let args: Vec<_> = { 58 | #[cfg(target_os = "macos")] 59 | { 60 | let mut args = vec![ 61 | "-demangle", 62 | "-dynamic", 63 | "-no_deduplicate", 64 | "-L/usr/local/lib", 65 | "-L/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib", 66 | ]; 67 | 68 | args.extend(objects.iter().map(|x| x.as_str())); 69 | 70 | args.extend(&["-o", &output_filename, "-lSystem"]); 71 | 72 | args 73 | } 74 | #[cfg(target_os = "linux")] 75 | { 76 | let (scrt1, crti, crtn) = { 77 | if file_exists("/usr/lib64/Scrt1.o") { 78 | ( 79 | "/usr/lib64/Scrt1.o", 80 | "/usr/lib64/crti.o", 81 | "/usr/lib64/crtn.o", 82 | ) 83 | } else { 84 | ( 85 | "/lib/x86_64-linux-gnu/Scrt1.o", 86 | "/lib/x86_64-linux-gnu/crti.o", 87 | "/lib/x86_64-linux-gnu/crtn.o", 88 | ) 89 | } 90 | }; 91 | 92 | let mut args = vec![ 93 | "-pie", 94 | "--hash-style=gnu", 95 | "--eh-frame-hdr", 96 | "--dynamic-linker", 97 | "/lib64/ld-linux-x86-64.so.2", 98 | "-m", 99 | "elf_x86_64", 100 | scrt1, 101 | crti, 102 | ]; 103 | 104 | args.extend(&["-o", &output_filename]); 105 | 106 | args.extend(&[ 107 | "-L/lib64", 108 | "-L/usr/lib64", 109 | "-L/lib/x86_64-linux-gnu", 110 | "-zrelro", 111 | "--no-as-needed", 112 | "-lc", 113 | "-O1", 114 | crtn, 115 | ]); 116 | 117 | args.extend(objects.iter().map(|x| x.as_str())); 118 | 119 | args 120 | } 121 | #[cfg(target_os = "windows")] 122 | { 123 | unimplemented!() 124 | } 125 | }; 126 | 127 | let mut linker = std::process::Command::new("ld"); 128 | let proc = linker.args(args.iter()).spawn()?; 129 | proc.wait_with_output()?; 130 | Ok(()) 131 | } 132 | 133 | #[cfg(target_os = "linux")] 134 | fn file_exists(path: &str) -> bool { 135 | Path::new(path).exists() 136 | } 137 | -------------------------------------------------------------------------------- /lib/edlang_parser/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Range; 2 | 3 | use crate::error::Error; 4 | use ariadne::{ColorGenerator, Label, Report, ReportKind, Source}; 5 | use itertools::Itertools; 6 | use lalrpop_util::ParseError; 7 | use lexer::{Lexer, LexicalError}; 8 | use tokens::Token; 9 | 10 | pub mod error; 11 | pub mod lexer; 12 | pub mod tokens; 13 | 14 | pub mod grammar { 15 | #![allow(dead_code, unused_imports, unused_variables)] 16 | 17 | pub use self::grammar::*; 18 | use lalrpop_util::lalrpop_mod; 19 | 20 | lalrpop_mod!(pub grammar); 21 | } 22 | 23 | pub fn parse_ast( 24 | source: &str, 25 | module_name: &str, 26 | ) -> Result> { 27 | let lexer = Lexer::new(source); 28 | let parser = grammar::TopLevelModuleParser::new(); 29 | parser.parse(module_name, lexer) 30 | } 31 | 32 | pub fn print_report<'a>( 33 | path: &'a str, 34 | source: &'a str, 35 | report: Report<'static, (&'a str, Range)>, 36 | ) -> Result<(), std::io::Error> { 37 | let source = Source::from(source); 38 | report.eprint((path, source)) 39 | } 40 | 41 | pub fn error_to_report<'a>( 42 | path: &'a str, 43 | error: &Error, 44 | ) -> Result)>, std::io::Error> { 45 | let mut colors = ColorGenerator::new(); 46 | let report = match error { 47 | ParseError::InvalidToken { location } => { 48 | let loc = *location; 49 | Report::build(ReportKind::Error, path, loc) 50 | .with_code("InvalidToken") 51 | .with_label( 52 | Label::new((path, loc..(loc + 1))) 53 | .with_color(colors.next()) 54 | .with_message("invalid token"), 55 | ) 56 | .finish() 57 | } 58 | ParseError::UnrecognizedEof { location, expected } => { 59 | let loc = *location; 60 | Report::build(ReportKind::Error, path, loc) 61 | .with_code("UnrecognizedEof") 62 | .with_label( 63 | Label::new((path, loc..(loc + 1))) 64 | .with_message(format!( 65 | "unrecognized eof, expected one of the following: {}", 66 | expected.iter().join(", ") 67 | )) 68 | .with_color(colors.next()), 69 | ) 70 | .finish() 71 | } 72 | ParseError::UnrecognizedToken { token, expected } => { 73 | Report::build(ReportKind::Error, path, token.0) 74 | .with_code("UnrecognizedToken") 75 | .with_label( 76 | Label::new((path, token.0..token.2)) 77 | .with_message(format!( 78 | "unrecognized token {:?}, expected one of the following: {}", 79 | token.1, 80 | expected.iter().join(", ") 81 | )) 82 | .with_color(colors.next()), 83 | ) 84 | .finish() 85 | } 86 | ParseError::ExtraToken { token } => Report::build(ReportKind::Error, path, token.0) 87 | .with_code("ExtraToken") 88 | .with_message("Extra token") 89 | .with_label( 90 | Label::new((path, token.0..token.2)) 91 | .with_message(format!("unexpected extra token {:?}", token.1)), 92 | ) 93 | .finish(), 94 | ParseError::User { error } => match error { 95 | LexicalError::InvalidToken(err, range) => match err { 96 | tokens::LexingError::NumberParseError => { 97 | Report::build(ReportKind::Error, path, range.start) 98 | .with_code("InvalidToken") 99 | .with_message("Error parsing literal number") 100 | .with_label( 101 | Label::new((path, range.start..range.end)) 102 | .with_message("error parsing literal number") 103 | .with_color(colors.next()), 104 | ) 105 | .finish() 106 | } 107 | tokens::LexingError::Other => Report::build(ReportKind::Error, path, range.start) 108 | .with_code("Other") 109 | .with_message("Other error") 110 | .with_label( 111 | Label::new((path, range.start..range.end)) 112 | .with_message("other error") 113 | .with_color(colors.next()), 114 | ) 115 | .finish(), 116 | }, 117 | }, 118 | }; 119 | 120 | Ok(report) 121 | } 122 | 123 | #[cfg(test)] 124 | mod test {} 125 | -------------------------------------------------------------------------------- /lib/edlang_driver/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::{path::PathBuf, time::Instant}; 2 | 3 | use anyhow::Result; 4 | use ariadne::{sources, Source}; 5 | use clap::Parser; 6 | use edlang_ast::Module; 7 | use edlang_lowering::lower_modules; 8 | use edlang_session::{DebugInfo, OptLevel, Session}; 9 | 10 | use crate::linker::{link_binary, link_shared_lib}; 11 | 12 | pub mod linker; 13 | 14 | #[derive(Parser, Debug)] 15 | #[command(author, version, about = "edlang compiler driver", long_about = None, bin_name = "edlangc")] 16 | pub struct CompilerArgs { 17 | /// The input file. 18 | pub input: PathBuf, 19 | 20 | /// The output file. 21 | pub output: PathBuf, 22 | 23 | /// Build for release with all optimizations. 24 | #[arg(short, long, default_value_t = false)] 25 | pub release: bool, 26 | 27 | /// Set the optimization level, 0,1,2,3 28 | #[arg(short = 'O', long)] 29 | pub optlevel: Option, 30 | 31 | /// Always add debug info 32 | #[arg(long)] 33 | pub debug_info: Option, 34 | 35 | /// Build as a library. 36 | #[arg(short, long, default_value_t = false)] 37 | pub library: bool, 38 | 39 | /// Print the edlang AST 40 | #[arg(long, default_value_t = false)] 41 | pub ast: bool, 42 | 43 | /// Print the edlang IR 44 | #[arg(long, default_value_t = false)] 45 | pub ir: bool, 46 | 47 | /// Output llvm ir 48 | #[arg(long, default_value_t = false)] 49 | pub llvm: bool, 50 | 51 | /// Output asm 52 | #[arg(long, default_value_t = false)] 53 | pub asm: bool, 54 | 55 | /// Output a object file 56 | #[arg(long, default_value_t = false)] 57 | pub object: bool, 58 | } 59 | 60 | pub fn main() -> Result<()> { 61 | tracing_subscriber::fmt::init(); 62 | 63 | let args = CompilerArgs::parse(); 64 | 65 | let object = compile(&args)?; 66 | 67 | if args.library { 68 | link_shared_lib(&[object.clone()], &args.output)?; 69 | } else { 70 | link_binary(&[object.clone()], &args.output)?; 71 | } 72 | 73 | if !args.object { 74 | std::fs::remove_file(object)?; 75 | } 76 | 77 | Ok(()) 78 | } 79 | 80 | pub fn parse_file(modules: &mut Vec<(PathBuf, String, Module)>, mut path: PathBuf) -> Result<()> { 81 | if path.is_dir() { 82 | path = path.join("mod.ed"); 83 | } 84 | 85 | let source = std::fs::read_to_string(&path)?; 86 | 87 | let module_ast = edlang_parser::parse_ast( 88 | &source, 89 | &path.file_stem().expect("no file stem").to_string_lossy(), 90 | ); 91 | 92 | let module_temp = match module_ast { 93 | Ok(module) => module, 94 | Err(error) => { 95 | let path = path.display().to_string(); 96 | let report = edlang_parser::error_to_report(&path, &error)?; 97 | edlang_parser::print_report(&path, &source, report)?; 98 | std::process::exit(1) 99 | } 100 | }; 101 | 102 | for ident in &module_temp.external_modules { 103 | let module_path = path 104 | .parent() 105 | .unwrap() 106 | .join(&ident.name) 107 | .with_extension("ed"); 108 | // todo: fancy error if doesnt exist? 109 | parse_file(modules, module_path)?; 110 | } 111 | 112 | modules.push((path, source, module_temp)); 113 | 114 | Ok(()) 115 | } 116 | 117 | pub fn compile(args: &CompilerArgs) -> Result { 118 | let start_time = Instant::now(); 119 | 120 | let mut modules = Vec::new(); 121 | parse_file(&mut modules, args.input.clone())?; 122 | 123 | let session = Session { 124 | file_paths: modules.iter().map(|x| x.0.clone()).collect(), 125 | debug_info: if let Some(debug_info) = args.debug_info { 126 | if debug_info { 127 | DebugInfo::Full 128 | } else { 129 | DebugInfo::None 130 | } 131 | } else if args.release { 132 | DebugInfo::None 133 | } else { 134 | DebugInfo::Full 135 | }, 136 | optlevel: if let Some(optlevel) = args.optlevel { 137 | match optlevel { 138 | 0 => OptLevel::None, 139 | 1 => OptLevel::Less, 140 | 2 => OptLevel::Default, 141 | _ => OptLevel::Aggressive, 142 | } 143 | } else if args.release { 144 | OptLevel::Aggressive 145 | } else { 146 | OptLevel::None 147 | }, 148 | sources: modules.iter().map(|x| Source::from(x.1.clone())).collect(), 149 | library: args.library, 150 | output_file: args.output.with_extension("o"), 151 | output_asm: args.asm, 152 | output_llvm: args.llvm, 153 | }; 154 | tracing::debug!("Output file: {:#?}", session.output_file); 155 | tracing::debug!("Is library: {:#?}", session.library); 156 | tracing::debug!("Optlevel: {:#?}", session.optlevel); 157 | tracing::debug!("Debug Info: {:#?}", session.debug_info); 158 | 159 | let path_cache: Vec<_> = modules 160 | .iter() 161 | .map(|x| (x.0.display().to_string(), x.1.clone())) 162 | .collect(); 163 | let modules: Vec<_> = modules.iter().map(|x| x.2.clone()).collect(); 164 | 165 | if args.ast { 166 | std::fs::write( 167 | session.output_file.with_extension("ast"), 168 | format!("{:#?}", modules), 169 | )?; 170 | } 171 | 172 | let program_ir = match lower_modules(&modules) { 173 | Ok(ir) => ir, 174 | Err(error) => { 175 | let report = edlang_check::lowering_error_to_report(error, &session); 176 | report.eprint(sources(path_cache))?; 177 | std::process::exit(1); 178 | } 179 | }; 180 | 181 | if args.ir { 182 | std::fs::write( 183 | session.output_file.with_extension("ir"), 184 | format!("{:#?}", program_ir), 185 | )?; 186 | } 187 | 188 | let object_path = 189 | edlang_codegen_llvm::compile(&session, &program_ir).expect("failed to compile"); 190 | 191 | let elapsed = start_time.elapsed(); 192 | tracing::debug!("Done in {:?}", elapsed); 193 | 194 | Ok(object_path) 195 | } 196 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | ## [unreleased] 6 | 7 | ### 🚀 Features 8 | 9 | - Function call checks 10 | - More checks on impl blocks 11 | 12 | ### 🐛 Bug Fixes 13 | 14 | - Fix cf bug 15 | 16 | - Fix test 17 | 18 | 19 | ### Other 20 | 21 | - Initial str type 22 | 23 | - Codegen for impl methods 24 | 25 | - Remove clangd file 26 | 27 | - Add asref check 28 | 29 | - Version 0.0.1-alpha.18 30 | 31 | 32 | ### ⚙️ Miscellaneous Tasks 33 | 34 | - Update deps 35 | 36 | ## [0.0.1-alpha.17] - 2024-04-09 37 | 38 | ### 🚀 Features 39 | 40 | - Add aliases for new and build 41 | 42 | ### Other 43 | 44 | - Don't use defautl scope in other changelog 45 | 46 | - Progress 47 | 48 | - Upd deps 49 | 50 | - Impl block 51 | 52 | - Llvm 18 53 | 54 | - Version 55 | 56 | 57 | ### ⚙️ Miscellaneous Tasks 58 | 59 | - Git cliff config 60 | - Update changelog 61 | - Ci 62 | 63 | - Ci 64 | 65 | 66 | ## [0.0.1-alpha.16] - 2024-03-13 67 | 68 | ### 🐛 Bug Fixes 69 | 70 | - Fix tests 71 | 72 | ### ⚙️ Miscellaneous Tasks 73 | 74 | - Bump version 75 | 76 | ## [0.0.1-alpha.15] - 2024-03-13 77 | 78 | ### 🚀 Features 79 | 80 | - Module file declarations, fixes 81 | 82 | ### 🐛 Bug Fixes 83 | 84 | - Fix 85 | 86 | - Fix multi file function resolution 87 | 88 | - Fix scope 89 | 90 | 91 | ### Other 92 | 93 | - Improv cast 94 | 95 | - Beter2 96 | 97 | - Allow top level module 98 | 99 | - Version 100 | 101 | 102 | ## [0.0.1-alpha.14] - 2024-03-11 103 | 104 | ### 🐛 Bug Fixes 105 | 106 | - Fix wrong linking 107 | 108 | ## [0.0.1-alpha.13] - 2024-03-11 109 | 110 | ### 🚀 Features 111 | 112 | - More checks 113 | - More checks 114 | - Initial work on edb, the edlang project manager 115 | - Usable edb 116 | - Version and readme 117 | 118 | ### 🐛 Bug Fixes 119 | 120 | - Fix span in lalrpop 121 | - Fix on type qualifier lowering 122 | - Fix ci2 123 | 124 | 125 | ### Other 126 | 127 | - Try release ci 128 | 129 | - Try release ci 130 | 131 | 132 | ### ⚙️ Miscellaneous Tasks 133 | 134 | - Upd changelog 135 | 136 | ## [0.0.1-alpha.12] - 2024-03-02 137 | 138 | ### 🚀 Features 139 | 140 | - Add casts 141 | 142 | ### 🐛 Bug Fixes 143 | 144 | - *(ci)* Try to fix release ci changelog 145 | 146 | ### 🧪 Testing 147 | 148 | - Add cast test 149 | 150 | ### ⚙️ Miscellaneous Tasks 151 | 152 | - Version 0.0.1-alpha.12 153 | 154 | ## [0.0.1-alpha.11] - 2024-03-01 155 | 156 | ### 🚀 Features 157 | 158 | - Add modules support, add name mangling 159 | - Allow extern fns 160 | 161 | ### 🐛 Bug Fixes 162 | 163 | - Debug type names 164 | 165 | ### Other 166 | 167 | - Changelog 168 | 169 | - Try to fix release changelog 170 | 171 | - Release 172 | 173 | 174 | ## [0.0.1-alpha.10] - 2024-02-28 175 | 176 | ### 🚀 Features 177 | 178 | - Check type correctness 179 | 180 | ### 🐛 Bug Fixes 181 | 182 | - *(ci)* Fix ci release 183 | - Fix checker 184 | 185 | ### ⚙️ Miscellaneous Tasks 186 | 187 | - Update changelog 188 | - Update dependencies 189 | - Update version 190 | 191 | ## [0.0.1-alpha.9] - 2024-02-24 192 | 193 | ### 🚀 Features 194 | 195 | - Optimize linker output on linux 196 | - More debug locations 197 | - Code checking in lowering 198 | 199 | ### Other 200 | 201 | - Upd 202 | 203 | 204 | ### ⚙️ Miscellaneous Tasks 205 | 206 | - Update version to 0.0.1-alpha.8 207 | 208 | ## [0.0.1-alpha.8] - 2024-02-18 209 | 210 | ### 🚀 Features 211 | 212 | - Struct support 213 | 214 | ### Other 215 | 216 | - Make main always pub 217 | 218 | - Readme 219 | 220 | - Gitattributes 221 | 222 | - Cliff 223 | 224 | - Better 225 | 226 | - Basic struct 227 | 228 | - Struct field 229 | 230 | - Struct init parsing 231 | 232 | - Nice 233 | 234 | - Version 235 | 236 | 237 | ### 🧪 Testing 238 | 239 | - Test 240 | 241 | 242 | ## [0.0.1-alpha.7] - 2024-02-17 243 | 244 | ### 🚀 Features 245 | 246 | - Compile unary op, compile asref, compile deref, reference arguments, avoid some temporaries on direct use 247 | 248 | ### Other 249 | 250 | - Changelog 251 | 252 | - Refs 253 | 254 | - Version and fix no return 255 | 256 | 257 | ## [0.0.1-alpha.6] - 2024-02-17 258 | 259 | ### 🚀 Features 260 | 261 | - Implement while 262 | - Improved logging 263 | - Ptr repr 264 | 265 | ### 🐛 Bug Fixes 266 | 267 | - Fix a miscompilation 268 | - Fixes 269 | 270 | 271 | ### Other 272 | 273 | - Optimize 274 | 275 | - Vv0.0.1-alpha.6 276 | 277 | 278 | ## [0.0.1-alpha.5] - 2024-02-14 279 | 280 | ### 🐛 Bug Fixes 281 | 282 | - *(linker)* Fix linker on distros like ubuntu 283 | - *(ci)* Ci improvements 284 | - Fix ci 285 | 286 | 287 | ### Other 288 | 289 | - Version 290 | 291 | - Changelog 292 | 293 | 294 | ## [0.0.1-alpha.4] - 2024-02-14 295 | 296 | ### 🚀 Features 297 | 298 | - Better cli and add tests 299 | 300 | ### ⚙️ Miscellaneous Tasks 301 | 302 | - Update version 303 | - Changelog 304 | 305 | ## [0.0.1-alpha.3] - 2024-02-13 306 | 307 | ### 🚀 Features 308 | 309 | - Properly version all crates 310 | - Add docs generation 311 | 312 | ### 🐛 Bug Fixes 313 | 314 | - Add all fields to cargo.toml 315 | - Add doc field to cargo toml 316 | 317 | ### ⚙️ Miscellaneous Tasks 318 | 319 | - Fix branch name 320 | - Fix doc ci 321 | 322 | ## [0.0.1-alpha.2] - 2024-02-13 323 | 324 | ### 🚀 Features 325 | 326 | - Compile ifs 327 | 328 | ### 🐛 Bug Fixes 329 | 330 | - Fix 331 | 332 | 333 | ### Other 334 | 335 | - Fmt 336 | 337 | - Readme 338 | 339 | 340 | ### 🎨 Styling 341 | 342 | - Format 343 | - Naming 344 | 345 | ### ⚙️ Miscellaneous Tasks 346 | 347 | - Update deps 348 | - Update version 349 | 350 | ## [0.0.1-alpha.1] - 2024-02-09 351 | 352 | ### 🐛 Bug Fixes 353 | 354 | - Fix double return 355 | 356 | - Fixes 357 | 358 | - Fix 359 | 360 | - Fix 361 | 362 | 363 | ### Other 364 | 365 | - Initial 366 | 367 | - Check 368 | 369 | - Format 370 | 371 | - Start logos 372 | 373 | - Progress 374 | 375 | - Progress 376 | 377 | - Big progress 378 | 379 | - Rly basic type inference 380 | 381 | - Type info improvements 382 | 383 | - Add rudimentary type inference, todo: args 384 | 385 | - If else type analysis 386 | 387 | - Idk 388 | 389 | - Tmp 390 | 391 | - Better 392 | 393 | - Progress 394 | 395 | - Require fully typed integers for now 396 | 397 | - Progress 398 | 399 | - V 400 | 401 | - Spans 402 | 403 | - Arns 404 | 405 | - Padding 406 | 407 | - Cleanup 408 | 409 | - Ok 410 | 411 | - Upd 412 | 413 | - Upd 414 | 415 | - New start 416 | 417 | - Progress 418 | 419 | - Parser 420 | 421 | - Prog 422 | 423 | - Ok 424 | 425 | - Ok 426 | 427 | - Upd 428 | 429 | - Ok 430 | 431 | - A 432 | 433 | - Prog 434 | 435 | - P 436 | 437 | - Fn call 438 | 439 | - Prog 440 | 441 | - Nice 442 | 443 | - Compile 444 | 445 | - Handle unit return 446 | 447 | - Basic binop 448 | 449 | - More 450 | 451 | - Prog 452 | 453 | - . 454 | 455 | - Todo 456 | 457 | - Debug info 458 | 459 | - A 460 | 461 | - Rename 462 | 463 | - Version 464 | 465 | - Clippy 466 | 467 | - Clippy 468 | 469 | 470 | ### 🎨 Styling 471 | 472 | - Style 473 | 474 | 475 | ### ⚙️ Miscellaneous Tasks 476 | 477 | - Ci 478 | 479 | - Ci 480 | 481 | 482 | 483 | -------------------------------------------------------------------------------- /lib/edlang_ast/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | 3 | pub use edlang_span::Span; 4 | 5 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 6 | pub struct Module { 7 | pub name: Ident, 8 | pub imports: Vec, 9 | pub external_modules: Vec, 10 | pub contents: Vec, 11 | pub span: Span, 12 | } 13 | 14 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 15 | pub enum ModuleStatement { 16 | Function(Function), 17 | Constant(Constant), 18 | Struct(Struct), 19 | StructImpl(StructImpl), 20 | Module(Module), 21 | } 22 | 23 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 24 | pub struct Import { 25 | pub module: Vec, 26 | /// If symbols is empty then the last path ident is the symbol. 27 | pub symbols: Vec, 28 | pub span: Span, 29 | } 30 | 31 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 32 | pub struct PathExpr { 33 | pub first: Ident, 34 | pub extra: Vec, 35 | pub span: Span, 36 | } 37 | 38 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 39 | pub enum PathSegment { 40 | Field(Ident), 41 | Index { value: Expression, span: Span }, 42 | Method { value: FnCallExpr, span: Span }, 43 | } 44 | 45 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 46 | pub struct Ident { 47 | pub name: String, 48 | pub span: Span, 49 | } 50 | 51 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 52 | pub enum Type { 53 | Basic { 54 | name: Ident, 55 | generics: Vec, 56 | qualifiers: Vec, 57 | span: Span, 58 | }, 59 | Array { 60 | of: Box, 61 | size: Option, 62 | qualifiers: Vec, 63 | span: Span, 64 | }, 65 | } 66 | 67 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 68 | pub enum TypeQualifier { 69 | Ref, // & 70 | RefMut, // &mut 71 | Ptr, // *const 72 | PtrMut, // *mut 73 | } 74 | 75 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 76 | pub struct FnParam { 77 | pub name: Ident, 78 | pub arg_type: Type, 79 | pub span: Span, 80 | } 81 | 82 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 83 | pub struct Block { 84 | pub body: Vec, 85 | pub span: Span, 86 | } 87 | 88 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 89 | pub enum Statement { 90 | Let(LetStmt), 91 | Assign(AssignStmt), 92 | For(ForStmt), 93 | While(WhileStmt), 94 | If(IfStmt), 95 | Return(ReturnStmt), 96 | FnCall(FnCallExpr), 97 | } 98 | 99 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 100 | pub struct LetStmt { 101 | pub name: Ident, 102 | pub is_mut: bool, 103 | pub r#type: Type, 104 | pub value: Expression, 105 | pub span: Span, 106 | } 107 | 108 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 109 | pub struct AssignStmt { 110 | pub name: PathExpr, 111 | pub value: Expression, 112 | pub deref_times: usize, 113 | pub span: Span, 114 | } 115 | 116 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 117 | pub struct IfStmt { 118 | pub condition: Expression, 119 | pub then_block: Block, 120 | pub else_block: Option, 121 | pub span: Span, 122 | } 123 | 124 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 125 | pub struct ForStmt { 126 | pub name: Ident, 127 | pub from: Expression, 128 | pub to: Option, 129 | pub block: Block, 130 | pub span: Span, 131 | } 132 | 133 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 134 | pub struct WhileStmt { 135 | pub condition: Expression, 136 | pub block: Block, 137 | pub span: Span, 138 | } 139 | 140 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 141 | pub struct ReturnStmt { 142 | pub value: Option, 143 | pub span: Span, 144 | } 145 | 146 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 147 | pub struct Function { 148 | pub name: Ident, 149 | pub is_extern: bool, 150 | pub is_public: bool, 151 | pub is_exported: bool, 152 | pub params: Vec, 153 | pub return_type: Option, 154 | pub body: Option, 155 | pub span: Span, 156 | } 157 | 158 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 159 | pub struct Constant { 160 | pub name: Ident, 161 | pub r#type: Type, 162 | pub value: Expression, 163 | pub span: Span, 164 | } 165 | 166 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 167 | pub struct StructField { 168 | pub name: Ident, 169 | pub r#type: Type, 170 | pub span: Span, 171 | } 172 | 173 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 174 | pub struct Struct { 175 | pub name: Ident, 176 | pub generics: Vec, 177 | pub fields: Vec, 178 | pub span: Span, 179 | } 180 | 181 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 182 | pub struct StructImpl { 183 | pub name: Ident, 184 | pub generics: Vec, 185 | pub methods: Vec, 186 | pub span: Span, 187 | } 188 | 189 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 190 | pub enum Expression { 191 | Value(ValueExpr), 192 | FnCall(FnCallExpr), 193 | StructInit(StructInitExpr), 194 | ArrayInit(ArrayInitExpr), 195 | Unary(UnaryOp, Box), 196 | Binary(Box, BinaryOp, Box), 197 | Deref(Box, Span), 198 | AsRef(Box, bool, Span), 199 | Cast(Box, Type, Span), 200 | } 201 | 202 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 203 | pub enum ValueExpr { 204 | Bool { value: bool, span: Span }, 205 | Char { value: char, span: Span }, 206 | Int { value: u128, span: Span }, 207 | Float { value: String, span: Span }, 208 | Str { value: String, span: Span }, 209 | Path(PathExpr), 210 | } 211 | 212 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 213 | pub struct StructInitField { 214 | pub value: Expression, 215 | pub span: Span, 216 | } 217 | 218 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 219 | pub struct StructInitExpr { 220 | pub name: Ident, 221 | pub generics: Vec, 222 | pub fields: BTreeMap, 223 | pub span: Span, 224 | } 225 | 226 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 227 | pub struct ArrayInitExpr { 228 | pub data: Vec, 229 | pub span: Span, 230 | } 231 | 232 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 233 | pub struct FnCallExpr { 234 | pub name: Ident, 235 | pub params: Vec, 236 | pub span: Span, 237 | } 238 | 239 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 240 | pub enum UnaryOp { 241 | ArithNeg(Span), 242 | LogicalNot(Span), 243 | BitwiseNot(Span), 244 | } 245 | 246 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 247 | pub enum BinaryOp { 248 | Arith(ArithOp, Span), 249 | Logic(LogicOp, Span), 250 | Compare(CmpOp, Span), 251 | Bitwise(BitwiseOp, Span), 252 | } 253 | 254 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 255 | pub enum ArithOp { 256 | Add, 257 | Sub, 258 | Mul, 259 | Div, 260 | Mod, 261 | } 262 | 263 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 264 | pub enum LogicOp { 265 | And, 266 | Or, 267 | } 268 | 269 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 270 | pub enum CmpOp { 271 | Eq, 272 | NotEq, 273 | Lt, 274 | LtEq, 275 | Gt, 276 | GtEq, 277 | } 278 | 279 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 280 | pub enum BitwiseOp { 281 | And, 282 | Or, 283 | Xor, 284 | } 285 | -------------------------------------------------------------------------------- /edb/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, fs::File, io::Read, path::PathBuf, time::Instant}; 2 | 3 | use anyhow::{bail, Context, Result}; 4 | use clap::{Parser, Subcommand}; 5 | use config::{Config, Package, Profile}; 6 | use edlang_driver::{ 7 | compile, 8 | linker::{link_binary, link_shared_lib}, 9 | CompilerArgs, 10 | }; 11 | use git2::{IndexAddOption, Repository}; 12 | use owo_colors::OwoColorize; 13 | 14 | mod config; 15 | 16 | #[derive(Parser, Debug)] 17 | #[command(author, version, about = "edlang builder", long_about = None, bin_name = "edlang")] 18 | pub struct Cli { 19 | #[command(subcommand)] 20 | command: Commands, 21 | } 22 | 23 | #[derive(Subcommand, Debug)] 24 | enum Commands { 25 | /// Initialize a project 26 | #[clap(alias = "n")] 27 | New { 28 | path: PathBuf, 29 | 30 | /// The name of the project, defaults to the directory name 31 | #[arg(long)] 32 | name: Option, 33 | 34 | /// Use a binary (application) template \[default\] 35 | #[arg(long, group = "binary", default_value_t = true)] 36 | bin: bool, 37 | 38 | /// Use a library template 39 | #[arg(long, group = "binary")] 40 | lib: bool, 41 | }, 42 | /// Build a project 43 | #[clap(alias = "b")] 44 | Build { 45 | /// Build for release with all optimizations. 46 | #[arg(short, long, default_value_t = false)] 47 | release: bool, 48 | 49 | /// Override the profile to use. 50 | #[arg(short, long)] 51 | profile: Option, 52 | }, 53 | } 54 | 55 | fn main() -> Result<()> { 56 | let cli = Cli::parse(); 57 | 58 | match cli.command { 59 | Commands::New { 60 | path, 61 | name, 62 | bin, 63 | lib, 64 | } => { 65 | let name = name.unwrap_or_else(|| { 66 | path.file_name() 67 | .context("Failed to get project name") 68 | .unwrap() 69 | .to_string_lossy() 70 | .to_string() 71 | }); 72 | 73 | if !path.exists() { 74 | std::fs::create_dir_all(&path).context("failed to create the project directory")?; 75 | std::fs::create_dir_all(path.join("src")).context("failed to create src/")?; 76 | } 77 | 78 | let config_path = path.join("Ed.toml"); 79 | 80 | let mut profiles = HashMap::new(); 81 | 82 | profiles.insert( 83 | "release".to_string(), 84 | Profile { 85 | release: true, 86 | opt_level: 3, 87 | debug_info: false, 88 | }, 89 | ); 90 | 91 | profiles.insert( 92 | "dev".to_string(), 93 | Profile { 94 | release: false, 95 | opt_level: 0, 96 | debug_info: true, 97 | }, 98 | ); 99 | 100 | let config = Config { 101 | package: Package { 102 | name: name.clone(), 103 | version: "0.1.0".to_string(), 104 | license: "AGPL-3.0-only".to_string(), 105 | }, 106 | profile: profiles, 107 | }; 108 | 109 | std::fs::write(config_path, toml::to_string_pretty(&config)?) 110 | .context("failed to write Ed.toml")?; 111 | std::fs::write(path.join(".gitignore"), "/build\n") 112 | .context("failed to write .gitignore")?; 113 | std::fs::write(path.join(".gitattributes"), "*.ed linguist-language=Rust\n") 114 | .context("failed to write .gitattributes")?; 115 | 116 | if bin { 117 | std::fs::write( 118 | path.join("src").join("main.ed"), 119 | r#"pub fn main() -> i32 { 120 | return 0; 121 | }"#, 122 | )?; 123 | } 124 | 125 | if lib { 126 | std::fs::write( 127 | path.join("src").join("lib.ed"), 128 | r#"pub fn main() -> i32 { 129 | return 0; 130 | }"#, 131 | )?; 132 | } 133 | 134 | { 135 | let repo = Repository::init(&path).context("failed to create repository")?; 136 | let sig = repo.signature()?; 137 | let tree_id = { 138 | let mut index = repo.index()?; 139 | 140 | index.add_all(["."].iter(), IndexAddOption::DEFAULT, None)?; 141 | index.write()?; 142 | index.write_tree()? 143 | }; 144 | 145 | let tree = repo.find_tree(tree_id).context("failed to find git tree")?; 146 | repo.commit(Some("HEAD"), &sig, &sig, "Initial commit", &tree, &[]) 147 | .context("failed to create initial commit")?; 148 | } 149 | 150 | if bin { 151 | println!( 152 | " {} binary (application) `{}` package", 153 | "Created".green().bold(), 154 | name 155 | ); 156 | } else { 157 | println!(" {} library `{}` package", "Created".green(), name); 158 | } 159 | } 160 | Commands::Build { release, profile } => { 161 | let mut current_dir = std::env::current_dir()?; 162 | let mut config_path = None; 163 | 164 | for _ in 0..3 { 165 | if !current_dir.join("Ed.toml").exists() { 166 | current_dir = if let Some(parent) = current_dir.parent() { 167 | parent.to_path_buf() 168 | } else { 169 | bail!("Couldn't find Ed.toml"); 170 | }; 171 | } else { 172 | config_path = Some(current_dir.join("Ed.toml")); 173 | break; 174 | } 175 | } 176 | 177 | let config_path = match config_path { 178 | Some(x) => x, 179 | None => bail!("Couldn't find Ed.toml"), 180 | }; 181 | 182 | let base_dir = config_path 183 | .parent() 184 | .context("couldn't get config parent dir")?; 185 | let mut config = File::open(&config_path).context("Failed to open Ed.toml")?; 186 | let mut buf = String::new(); 187 | config.read_to_string(&mut buf)?; 188 | 189 | let config: Config = toml::from_str(&buf).context("failed to parse Ed.toml")?; 190 | 191 | println!( 192 | " {} {} v{} ({})", 193 | "Compiling".green().bold(), 194 | config.package.name, 195 | config.package.version, 196 | base_dir.display() 197 | ); 198 | 199 | let src_dir = base_dir.join("src"); 200 | let target_dir = base_dir.join("build"); 201 | 202 | if !target_dir.exists() { 203 | std::fs::create_dir_all(&target_dir)?; 204 | } 205 | 206 | let output = target_dir.join(config.package.name); 207 | 208 | let (profile, profile_name) = if let Some(profile) = profile { 209 | ( 210 | config 211 | .profile 212 | .get(&profile) 213 | .context("Couldn't get requested profile")?, 214 | profile, 215 | ) 216 | } else if release { 217 | ( 218 | config 219 | .profile 220 | .get("release") 221 | .context("Couldn't get profile: release")?, 222 | "release".to_string(), 223 | ) 224 | } else { 225 | ( 226 | config 227 | .profile 228 | .get("dev") 229 | .context("Couldn't get profile: dev")?, 230 | "dev".to_string(), 231 | ) 232 | }; 233 | 234 | let lib_ed = src_dir.join("lib.ed"); 235 | let main_ed = src_dir.join("main.ed"); 236 | 237 | let start = Instant::now(); 238 | 239 | for file in [main_ed, lib_ed] { 240 | if file.exists() { 241 | let is_lib = file.file_stem().unwrap() == "lib"; 242 | 243 | let compile_args = CompilerArgs { 244 | input: file, 245 | output: if is_lib { 246 | let name = output.file_stem().unwrap().to_string_lossy().to_string(); 247 | let name = format!("lib{name}"); 248 | output 249 | .with_file_name(name) 250 | .with_extension(get_platform_library_ext()) 251 | } else { 252 | output.clone() 253 | }, 254 | release, 255 | optlevel: Some(profile.opt_level), 256 | debug_info: Some(profile.debug_info), 257 | library: is_lib, 258 | ast: false, 259 | ir: false, 260 | llvm: true, 261 | asm: false, 262 | object: true, 263 | }; 264 | let object = compile(&compile_args)?; 265 | 266 | if compile_args.library { 267 | link_shared_lib(&[object], &compile_args.output)?; 268 | } else { 269 | link_binary(&[object], &compile_args.output)?; 270 | } 271 | } 272 | } 273 | 274 | let elapsed = start.elapsed(); 275 | 276 | println!( 277 | " {} {} [{}{}] in {elapsed:?}", 278 | "Finished".green().bold(), 279 | profile_name, 280 | if profile.opt_level > 0 { 281 | "optimized" 282 | } else { 283 | "unoptimized" 284 | }, 285 | if profile.debug_info { 286 | " + debuginfo" 287 | } else { 288 | "" 289 | } 290 | ); 291 | /* 292 | 293 | Finished dev [unoptimized + debuginfo] target(s) in 0.06s 294 | Running `/data2/edgar/edlang/target/debug/edb build` 295 | */ 296 | } 297 | } 298 | 299 | Ok(()) 300 | } 301 | 302 | pub fn get_platform_library_ext() -> &'static str { 303 | if cfg!(target_os = "macos") { 304 | "dylib" 305 | } else if cfg!(target_os = "windows") { 306 | "dll" 307 | } else { 308 | "so" 309 | } 310 | } 311 | -------------------------------------------------------------------------------- /lib/edlang_check/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Range; 2 | 3 | use ariadne::{ColorGenerator, Label, Report, ReportKind}; 4 | use edlang_lowering::errors::LoweringError; 5 | use edlang_session::Session; 6 | 7 | /// Creates a report from a lowering error. 8 | pub fn lowering_error_to_report( 9 | error: LoweringError, 10 | session: &Session, 11 | ) -> Report<(String, Range)> { 12 | let mut colors = ColorGenerator::new(); 13 | colors.next(); 14 | match error { 15 | LoweringError::ModuleNotFound { 16 | span, 17 | module, 18 | file_id, 19 | } => { 20 | let path = session.file_paths[file_id].display().to_string(); 21 | let offset = span.lo; 22 | Report::build(ReportKind::Error, path.clone(), offset) 23 | .with_code("E1") 24 | .with_label( 25 | Label::new((path, span.into())) 26 | .with_message(format!("Module {module:?} not found.")) 27 | .with_color(colors.next()), 28 | ) 29 | .with_message("Unresolved import.") 30 | .finish() 31 | } 32 | LoweringError::FunctionNotFound { 33 | span, 34 | function, 35 | file_id, 36 | } => { 37 | let path = session.file_paths[file_id].display().to_string(); 38 | Report::build(ReportKind::Error, path.clone(), span.lo) 39 | .with_code("EFNNOTFOUND") 40 | .with_label( 41 | Label::new((path, span.into())) 42 | .with_message(format!("Function {function:?} not found.")) 43 | .with_color(colors.next()), 44 | ) 45 | .finish() 46 | } 47 | LoweringError::ImportNotFound { 48 | import_span, 49 | module_span, 50 | symbol, 51 | file_id, 52 | } => { 53 | let path = session.file_paths[file_id].display().to_string(); 54 | let offset = symbol.span.lo; 55 | Report::build(ReportKind::Error, path.clone(), offset) 56 | .with_code("E2") 57 | .with_label( 58 | Label::new((path.clone(), module_span.into())) 59 | .with_message("In module this module."), 60 | ) 61 | .with_label( 62 | Label::new((path.clone(), import_span.into())) 63 | .with_message("In this import statement"), 64 | ) 65 | .with_label( 66 | Label::new((path, symbol.span.into())) 67 | .with_message(format!("Failed to find symbol {:?}", symbol.name)) 68 | .with_color(colors.next()), 69 | ) 70 | .with_message("Unresolved import.") 71 | .finish() 72 | } 73 | LoweringError::BorrowNotMutable { 74 | span, 75 | name, 76 | type_span, 77 | file_id, 78 | } => { 79 | let path = session.file_paths[file_id].display().to_string(); 80 | let mut labels = vec![Label::new((path.clone(), span.into())) 81 | .with_message(format!( 82 | "Can't mutate {name:?} because it's behind a immutable borrow" 83 | )) 84 | .with_color(colors.next())]; 85 | 86 | if let Some(type_span) = type_span { 87 | labels.push( 88 | Label::new((path.clone(), type_span.into())) 89 | .with_message(format!("Variable {name:?} has this type")) 90 | .with_color(colors.next()), 91 | ); 92 | } 93 | 94 | Report::build(ReportKind::Error, path.clone(), span.lo) 95 | .with_code("EREFMUT") 96 | .with_labels(labels) 97 | .finish() 98 | } 99 | LoweringError::UnrecognizedType { 100 | span, 101 | name, 102 | file_id, 103 | } => { 104 | let path = session.file_paths[file_id].display().to_string(); 105 | Report::build(ReportKind::Error, path.clone(), span.lo) 106 | .with_code("E3") 107 | .with_label( 108 | Label::new((path, span.into())) 109 | .with_message(format!("Failed to find type {:?}", name)) 110 | .with_color(colors.next()), 111 | ) 112 | .with_message(format!("Unresolved type {:?}.", name)) 113 | .finish() 114 | } 115 | LoweringError::UnexpectedType { 116 | span, 117 | found, 118 | expected, 119 | file_id, 120 | } => { 121 | let path = session.file_paths[file_id].display().to_string(); 122 | let mut labels = vec![Label::new((path.clone(), span.into())) 123 | .with_message(format!( 124 | "Unexpected type '{}', expected '{}'", 125 | found, expected.kind 126 | )) 127 | .with_color(colors.next())]; 128 | 129 | if let Some(span) = expected.span { 130 | labels.push( 131 | Label::new((path.clone(), span.into())) 132 | .with_message(format!("expected '{}' due to this type", expected.kind)) 133 | .with_color(colors.next()), 134 | ); 135 | } 136 | 137 | Report::build(ReportKind::Error, path.clone(), span.lo) 138 | .with_code("E3") 139 | .with_labels(labels) 140 | .with_message(format!("expected type {}.", expected.kind)) 141 | .finish() 142 | } 143 | LoweringError::IdNotFound { span, id, file_id } => { 144 | let path = session.file_paths[file_id].display().to_string(); 145 | Report::build(ReportKind::Error, path.clone(), span.lo) 146 | .with_code("E_ID") 147 | .with_label( 148 | Label::new((path, span.into())) 149 | .with_message("Failed to definition id") 150 | .with_color(colors.next()), 151 | ) 152 | .with_message(format!("Failed to find definition id {id:?}, this is most likely a compiler bug or a unimplemented lowering")) 153 | .finish() 154 | } 155 | LoweringError::NotYetImplemented { 156 | span, 157 | message, 158 | file_id, 159 | } => { 160 | let path = session.file_paths[file_id].display().to_string(); 161 | Report::build(ReportKind::Error, path.clone(), span.lo) 162 | .with_code("TODO") 163 | .with_label( 164 | Label::new((path, span.into())) 165 | .with_message(message) 166 | .with_color(colors.next()), 167 | ) 168 | .finish() 169 | } 170 | LoweringError::UseOfUndeclaredVariable { 171 | span, 172 | name, 173 | file_id, 174 | } => { 175 | let path = session.file_paths[file_id].display().to_string(); 176 | Report::build(ReportKind::Error, path.clone(), span.lo) 177 | .with_code("UseOfUndeclaredVariable") 178 | .with_label( 179 | Label::new((path, span.into())) 180 | .with_message(format!("use of undeclared variable {:?}", name)) 181 | .with_color(colors.next()), 182 | ) 183 | .finish() 184 | } 185 | LoweringError::ParamCountMismatch { 186 | span, 187 | has_args, 188 | needs, 189 | file_id, 190 | } => { 191 | let path = session.file_paths[file_id].display().to_string(); 192 | Report::build(ReportKind::Error, path.clone(), span.lo) 193 | .with_code("ParamCountMismatch") 194 | .with_label( 195 | Label::new((path, span.into())) 196 | .with_message(format!( 197 | "function call parameter count mismatch: has {}, needs {}.", 198 | has_args, needs 199 | )) 200 | .with_color(colors.next()), 201 | ) 202 | .finish() 203 | } 204 | LoweringError::NotMutable { 205 | span, 206 | declare_span, 207 | file_id, 208 | } => { 209 | let path = session.file_paths[file_id].display().to_string(); 210 | let mut report = Report::build(ReportKind::Error, path.clone(), span.lo) 211 | .with_code("NotMutable") 212 | .with_label( 213 | Label::new((path.clone(), span.into())) 214 | .with_message("can't mutate this variable because it's not mutable") 215 | .with_color(colors.next()), 216 | ); 217 | 218 | if let Some(declare_span) = declare_span { 219 | report = report.with_label( 220 | Label::new((path, declare_span.into())) 221 | .with_message("variable declared here") 222 | .with_color(colors.next()), 223 | ); 224 | } 225 | report.finish() 226 | } 227 | LoweringError::NotMutableSelf { 228 | span, 229 | path_span, 230 | file_id, 231 | } => { 232 | let path = session.file_paths[file_id].display().to_string(); 233 | Report::build(ReportKind::Error, path.clone(), span.lo) 234 | .with_code("NotMutableSelf") 235 | .with_label( 236 | Label::new((path.clone(), path_span.into())) 237 | .with_message("this value is not declared mutable") 238 | .with_color(colors.next()), 239 | ) 240 | .with_label( 241 | Label::new((path, span.into())) 242 | .with_message("this method requires a mutable 'self'") 243 | .with_color(colors.next()), 244 | ) 245 | .finish() 246 | } 247 | LoweringError::CantTakeMutableBorrow { 248 | span, 249 | declare_span, 250 | file_id, 251 | } => { 252 | let path = session.file_paths[file_id].display().to_string(); 253 | let mut report = Report::build(ReportKind::Error, path.clone(), span.lo) 254 | .with_code("CantTakeMutableBorrow") 255 | .with_label( 256 | Label::new((path.clone(), span.into())) 257 | .with_message("can't take a mutate borrow to this variable because it's not declared mutable") 258 | .with_color(colors.next()), 259 | ); 260 | 261 | if let Some(declare_span) = declare_span { 262 | report = report.with_label( 263 | Label::new((path, declare_span.into())) 264 | .with_message("variable declared here") 265 | .with_color(colors.next()), 266 | ); 267 | } 268 | report.finish() 269 | } 270 | } 271 | } 272 | -------------------------------------------------------------------------------- /lib/edlang_ir/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Based on a cfg 2 | 3 | use std::{ 4 | collections::{BTreeMap, HashMap, HashSet}, 5 | fmt, 6 | }; 7 | 8 | use edlang_span::Span; 9 | use educe::Educe; 10 | use smallvec::SmallVec; 11 | 12 | pub mod scalar_int; 13 | 14 | #[derive(Debug, Clone, Default)] 15 | pub struct SymbolTable { 16 | pub symbols: BTreeMap, 17 | pub modules: BTreeMap, 18 | pub functions: BTreeMap, 19 | pub methods: BTreeMap>, 20 | pub constants: BTreeMap, 21 | pub structs: BTreeMap, 22 | pub types: BTreeMap, 23 | } 24 | 25 | #[derive(Debug, Clone, Default)] 26 | pub struct ProgramBody { 27 | pub top_level_module_names: BTreeMap, 28 | /// The top level modules. 29 | pub top_level_modules: Vec, 30 | /// All the modules in a flat map. 31 | pub modules: BTreeMap, 32 | /// This stores all the functions from all modules 33 | pub functions: BTreeMap, 34 | pub structs: BTreeMap, 35 | /// The function signatures. 36 | pub function_signatures: BTreeMap, TypeInfo)>, 37 | pub file_names: BTreeMap, 38 | } 39 | 40 | #[derive(Debug, Clone)] 41 | pub struct ModuleBody { 42 | pub module_id: DefId, 43 | pub parent_ids: Vec, 44 | pub file_id: usize, 45 | pub name: String, 46 | pub symbols: SymbolTable, 47 | /// Functions defined in this module. 48 | pub functions: HashSet, 49 | /// Structs defined in this module. 50 | pub structs: HashSet, 51 | /// Types defined in this module. 52 | pub types: HashSet, 53 | /// Constants defined in this module. 54 | pub constants: HashSet, 55 | /// Submodules defined in this module. 56 | pub modules: HashSet, 57 | /// Imported items. symbol -> id 58 | pub imports: BTreeMap, 59 | pub span: Span, 60 | } 61 | 62 | /// Definition id. 63 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] 64 | pub struct DefId { 65 | pub program_id: usize, 66 | pub id: usize, 67 | } 68 | 69 | #[derive(Debug, Clone)] 70 | pub struct Body { 71 | pub def_id: DefId, 72 | pub is_pub: bool, 73 | pub is_extern: bool, 74 | // exported means externally available in a shared library or as main 75 | pub is_exported: bool, 76 | pub name: String, 77 | pub locals: SmallVec<[Local; 4]>, 78 | pub blocks: SmallVec<[BasicBlock; 8]>, 79 | pub fn_span: Span, 80 | } 81 | 82 | impl Body { 83 | pub fn get_args(&self) -> SmallVec<[Local; 4]> { 84 | let mut args = SmallVec::default(); 85 | 86 | for x in &self.locals { 87 | if let LocalKind::Arg = x.kind { 88 | args.push(x.clone()); 89 | } 90 | } 91 | 92 | args 93 | } 94 | 95 | pub fn get_return_local(&self) -> Local { 96 | self.locals[0].clone() 97 | } 98 | 99 | pub fn get_mangled_name(&self) -> String { 100 | if self.is_extern || self.is_exported { 101 | return self.name.clone(); 102 | } 103 | 104 | format!( 105 | "{}${}${}", 106 | self.name, self.def_id.program_id, self.def_id.id 107 | ) 108 | } 109 | } 110 | 111 | #[derive(Debug, Clone)] 112 | pub struct AdtBody { 113 | pub def_id: DefId, 114 | pub mod_id: DefId, 115 | pub is_pub: bool, 116 | pub name: String, 117 | pub variants: Vec, 118 | pub name_to_idx: HashMap, 119 | pub span: Span, 120 | } 121 | 122 | /// struct field or enum variant 123 | #[derive(Debug, Clone)] 124 | pub struct AdtVariant { 125 | pub def_id: DefId, 126 | pub name: String, 127 | pub ty: TypeInfo, 128 | } 129 | 130 | #[derive(Debug, Clone)] 131 | pub struct DebugInfo { 132 | pub id: usize, 133 | pub span: Span, 134 | } 135 | 136 | #[derive(Debug, Clone)] 137 | pub struct BasicBlock { 138 | pub statements: SmallVec<[Statement; 8]>, 139 | pub terminator: Terminator, 140 | pub terminator_span: Option, 141 | } 142 | 143 | #[derive(Debug, Clone)] 144 | pub struct Local { 145 | pub mutable: bool, 146 | pub span: Option, 147 | pub debug_name: Option, 148 | pub ty: TypeInfo, 149 | pub kind: LocalKind, 150 | } 151 | 152 | impl Local { 153 | pub fn new( 154 | span: Option, 155 | kind: LocalKind, 156 | ty: TypeInfo, 157 | debug_name: Option, 158 | mutable: bool, 159 | ) -> Self { 160 | Self { 161 | span, 162 | kind, 163 | ty, 164 | debug_name, 165 | mutable, 166 | } 167 | } 168 | 169 | pub const fn temp(ty: TypeKind) -> Self { 170 | Self { 171 | span: None, 172 | ty: TypeInfo { 173 | span: None, 174 | kind: ty, 175 | }, 176 | kind: LocalKind::Temp, 177 | debug_name: None, 178 | mutable: false, 179 | } 180 | } 181 | 182 | pub fn is_mutable(&self) -> bool { 183 | if self.mutable { 184 | return true; 185 | } 186 | 187 | match self.ty.kind { 188 | TypeKind::Ptr(is_mut, _) => is_mut, 189 | TypeKind::Ref(is_mut, _) => is_mut, 190 | _ => false, 191 | } 192 | } 193 | } 194 | 195 | #[derive(Debug, Clone, Copy)] 196 | pub enum LocalKind { 197 | Temp, 198 | Arg, 199 | ReturnPointer, 200 | } 201 | 202 | #[derive(Debug, Clone)] 203 | pub struct Statement { 204 | pub span: Option, 205 | pub kind: StatementKind, 206 | } 207 | 208 | #[derive(Debug, Clone)] 209 | pub enum StatementKind { 210 | Assign(Place, RValue), 211 | StorageLive(usize), 212 | StorageDead(usize), 213 | } 214 | 215 | #[derive(Debug, Clone)] 216 | pub enum Terminator { 217 | Target(usize), 218 | Return, 219 | SwitchInt { 220 | discriminator: Operand, 221 | targets: SwitchTarget, 222 | }, 223 | Call { 224 | /// The function to call. 225 | func: DefId, 226 | /// The arguments. 227 | args: Vec, 228 | /// The place in memory to store the return value of the function call. 229 | destination: Place, 230 | /// What basic block to jump to after the function call, if the function is non-diverging (i.e it returns control back). 231 | target: Option, 232 | }, 233 | Unreachable, 234 | } 235 | 236 | /// Used for ifs, match 237 | #[derive(Debug, Clone)] 238 | pub struct SwitchTarget { 239 | pub values: Vec, 240 | pub targets: Vec, 241 | } 242 | 243 | #[derive(Debug, Clone, Educe, Eq, PartialOrd, Ord)] 244 | #[educe(PartialEq)] 245 | pub struct TypeInfo { 246 | #[educe(PartialEq(ignore))] 247 | pub span: Option, 248 | pub kind: TypeKind, 249 | } 250 | 251 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] 252 | pub enum TypeKind { 253 | Unit, 254 | Bool, 255 | Char, 256 | Int(IntTy), 257 | Uint(UintTy), 258 | Float(FloatTy), 259 | FnDef(DefId, Vec), // The vec are generic types, not arg types 260 | Str, 261 | Ptr(bool, Box), 262 | Ref(bool, Box), 263 | // name for print purposes 264 | Struct(DefId, String), // todo, add generics 265 | Slice(Box, Option), 266 | } 267 | 268 | impl TypeKind { 269 | pub const fn is_unit(&self) -> bool { 270 | matches!(self, Self::Unit) 271 | } 272 | 273 | pub const fn is_integer(&self) -> bool { 274 | matches!(self, Self::Int(_) | Self::Uint(_)) 275 | } 276 | 277 | pub const fn is_signed_integer(&self) -> bool { 278 | matches!(self, Self::Int(_)) 279 | } 280 | 281 | pub const fn is_float(&self) -> bool { 282 | matches!(self, Self::Float(_)) 283 | } 284 | 285 | pub fn get_falsy_value(&self) -> ValueTree { 286 | match self { 287 | Self::Bool => ValueTree::Leaf(ConstValue::Bool(false)), 288 | Self::Char => todo!(), 289 | Self::Int(ty) => match ty { 290 | IntTy::I8 => ValueTree::Leaf(ConstValue::I8(0)), 291 | IntTy::I16 => ValueTree::Leaf(ConstValue::I16(0)), 292 | IntTy::I32 => ValueTree::Leaf(ConstValue::I32(0)), 293 | IntTy::I64 => ValueTree::Leaf(ConstValue::I64(0)), 294 | IntTy::I128 => ValueTree::Leaf(ConstValue::I128(0)), 295 | IntTy::Isize => todo!(), 296 | }, 297 | Self::Uint(ty) => match ty { 298 | UintTy::U8 => ValueTree::Leaf(ConstValue::U8(0)), 299 | UintTy::U16 => ValueTree::Leaf(ConstValue::U16(0)), 300 | UintTy::U32 => ValueTree::Leaf(ConstValue::U32(0)), 301 | UintTy::U64 => ValueTree::Leaf(ConstValue::U64(0)), 302 | UintTy::U128 => ValueTree::Leaf(ConstValue::U128(0)), 303 | UintTy::Usize => todo!(), 304 | }, 305 | Self::Float(_) => todo!(), 306 | TypeKind::Unit => unreachable!(), 307 | TypeKind::FnDef(_, _) => unreachable!(), 308 | TypeKind::Ptr(_, _pointee) => todo!(), 309 | TypeKind::Ref(_, inner) => inner.kind.get_falsy_value(), 310 | TypeKind::Struct(_, _name) => todo!(), 311 | TypeKind::Str => todo!(), 312 | TypeKind::Slice(_, _) => todo!(), 313 | } 314 | } 315 | } 316 | 317 | impl fmt::Display for TypeKind { 318 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 319 | match self { 320 | TypeKind::Unit => write!(f, "()"), 321 | TypeKind::Bool => write!(f, "bool"), 322 | TypeKind::Char => write!(f, "char"), 323 | TypeKind::Int(ty) => match ty { 324 | IntTy::I128 => write!(f, "i128"), 325 | IntTy::I64 => write!(f, "i64"), 326 | IntTy::I32 => write!(f, "i32"), 327 | IntTy::I16 => write!(f, "i16"), 328 | IntTy::I8 => write!(f, "i8"), 329 | IntTy::Isize => write!(f, "isize"), 330 | }, 331 | TypeKind::Uint(ty) => match ty { 332 | UintTy::U128 => write!(f, "u128"), 333 | UintTy::U64 => write!(f, "u64"), 334 | UintTy::U32 => write!(f, "u32"), 335 | UintTy::U16 => write!(f, "u16"), 336 | UintTy::U8 => write!(f, "u8"), 337 | UintTy::Usize => write!(f, "usize"), 338 | }, 339 | TypeKind::Float(ty) => match ty { 340 | FloatTy::F32 => write!(f, "f64"), 341 | FloatTy::F64 => write!(f, "f32"), 342 | }, 343 | TypeKind::FnDef(_, _) => todo!(), 344 | TypeKind::Str => write!(f, "str"), 345 | TypeKind::Ptr(is_mut, inner) => { 346 | let word = if *is_mut { "mut" } else { "const" }; 347 | 348 | write!(f, "*{word} {}", inner.kind) 349 | } 350 | TypeKind::Ref(is_mut, inner) => { 351 | let word = if *is_mut { "mut " } else { "" }; 352 | 353 | write!(f, "&{word}{}", inner.kind) 354 | } 355 | TypeKind::Struct(_, name) => write!(f, "{}", name), 356 | TypeKind::Slice(inner, size) => { 357 | let size = if let Some(size) = size { 358 | format!("; {size}") 359 | } else { 360 | "".to_string() 361 | }; 362 | 363 | write!(f, "[{}{size}]", inner.kind) 364 | } 365 | } 366 | } 367 | } 368 | 369 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Copy)] 370 | pub enum IntTy { 371 | I128, 372 | I64, 373 | I32, 374 | I16, 375 | I8, 376 | Isize, 377 | } 378 | 379 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Copy)] 380 | pub enum UintTy { 381 | U128, 382 | U64, 383 | U32, 384 | U16, 385 | U8, 386 | Usize, 387 | } 388 | 389 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Copy)] 390 | pub enum FloatTy { 391 | F32, 392 | F64, 393 | } 394 | 395 | #[derive(Debug, Clone)] 396 | pub struct ConstData { 397 | pub span: Option, 398 | pub type_info: TypeInfo, 399 | pub kind: ConstKind, 400 | } 401 | 402 | #[derive(Debug, Clone)] 403 | pub enum ConstKind { 404 | Value(ValueTree), 405 | ZeroSized, 406 | } 407 | 408 | #[derive(Debug, Clone)] 409 | pub enum ValueTree { 410 | Leaf(ConstValue), 411 | Branch(Vec), 412 | } 413 | 414 | impl ValueTree { 415 | pub fn get_type(&self) -> TypeKind { 416 | match self { 417 | ValueTree::Leaf(value) => match value { 418 | ConstValue::Bool(_) => TypeKind::Bool, 419 | ConstValue::I8(_) => TypeKind::Int(IntTy::I8), 420 | ConstValue::I16(_) => TypeKind::Int(IntTy::I16), 421 | ConstValue::I32(_) => TypeKind::Int(IntTy::I32), 422 | ConstValue::I64(_) => TypeKind::Int(IntTy::I64), 423 | ConstValue::I128(_) => TypeKind::Int(IntTy::I128), 424 | ConstValue::U8(_) => TypeKind::Uint(UintTy::U8), 425 | ConstValue::U16(_) => TypeKind::Uint(UintTy::U16), 426 | ConstValue::U32(_) => TypeKind::Uint(UintTy::U32), 427 | ConstValue::U64(_) => TypeKind::Uint(UintTy::U64), 428 | ConstValue::U128(_) => TypeKind::Uint(UintTy::U8), 429 | ConstValue::F32(_) => TypeKind::Float(FloatTy::F32), 430 | ConstValue::F64(_) => TypeKind::Float(FloatTy::F64), 431 | ConstValue::Char(_) => TypeKind::Char, 432 | ConstValue::Isize(_) => TypeKind::Int(IntTy::Isize), 433 | ConstValue::Str(_) => TypeKind::Str, 434 | }, 435 | ValueTree::Branch(_) => todo!(), 436 | } 437 | } 438 | } 439 | 440 | #[derive(Debug, Clone)] 441 | pub enum RValue { 442 | Use(Operand, Span), 443 | Ref(bool, Operand, Span), 444 | BinOp(BinOp, Operand, Operand, Span), 445 | LogicOp(LogicalOp, Operand, Operand, Span), 446 | UnOp(UnOp, Operand, Span), 447 | Cast(Operand, TypeInfo, Span), 448 | } 449 | 450 | impl RValue { 451 | pub fn get_local(&self) -> Option { 452 | match self { 453 | RValue::Use(op, _) => op.get_local(), 454 | RValue::Ref(_, op, _) => op.get_local(), 455 | RValue::BinOp(_, _, _, _) => None, 456 | RValue::LogicOp(_, _, _, _) => None, 457 | RValue::UnOp(_, _, _) => None, 458 | RValue::Cast(op, _, _) => op.get_local(), 459 | } 460 | } 461 | } 462 | 463 | #[derive(Debug, Clone)] 464 | pub enum Operand { 465 | Copy(Place), 466 | Move(Place), 467 | Constant(ConstData), 468 | } 469 | 470 | impl Operand { 471 | pub fn get_local(&self) -> Option { 472 | match self { 473 | Operand::Copy(place) => Some(place.local), 474 | Operand::Move(place) => Some(place.local), 475 | Operand::Constant(_) => None, 476 | } 477 | } 478 | } 479 | 480 | #[derive(Debug, Clone)] 481 | pub struct Place { 482 | pub local: usize, 483 | pub projection: SmallVec<[PlaceElem; 1]>, 484 | } 485 | 486 | #[derive(Debug, Clone)] 487 | pub enum PlaceElem { 488 | Deref, 489 | Field { field_idx: usize }, 490 | Index { value: Box }, 491 | ConstIndex { index: usize }, 492 | } 493 | 494 | impl TypeKind { 495 | pub fn get_i128() -> Self { 496 | Self::Int(IntTy::I128) 497 | } 498 | 499 | pub fn get_i64() -> Self { 500 | Self::Int(IntTy::I64) 501 | } 502 | 503 | pub fn get_i32() -> Self { 504 | Self::Int(IntTy::I32) 505 | } 506 | 507 | pub fn get_i16() -> Self { 508 | Self::Int(IntTy::I16) 509 | } 510 | 511 | pub fn get_i8() -> Self { 512 | Self::Int(IntTy::I8) 513 | } 514 | 515 | pub fn get_u128() -> Self { 516 | Self::Uint(UintTy::U128) 517 | } 518 | 519 | pub fn get_u64() -> Self { 520 | Self::Uint(UintTy::U64) 521 | } 522 | 523 | pub fn get_u32() -> Self { 524 | Self::Uint(UintTy::U32) 525 | } 526 | 527 | pub fn get_u16() -> Self { 528 | Self::Uint(UintTy::U16) 529 | } 530 | 531 | pub fn get_u8() -> Self { 532 | Self::Uint(UintTy::U8) 533 | } 534 | 535 | pub fn get_f32() -> Self { 536 | Self::Float(FloatTy::F32) 537 | } 538 | 539 | pub fn get_f64() -> Self { 540 | Self::Float(FloatTy::F64) 541 | } 542 | 543 | pub fn get_bool() -> Self { 544 | Self::Bool 545 | } 546 | 547 | pub fn get_char() -> Self { 548 | Self::Char 549 | } 550 | } 551 | 552 | #[derive(Debug, Clone, Copy)] 553 | pub enum BinOp { 554 | Add, 555 | Sub, 556 | Mul, 557 | Div, 558 | Rem, 559 | BitXor, 560 | BitAnd, 561 | BitOr, 562 | Shl, 563 | Shr, 564 | Eq, 565 | Lt, 566 | Le, 567 | Ne, 568 | Ge, 569 | Gt, 570 | Offset, 571 | } 572 | 573 | // Diferent than BinOp because operands needs to be lazily evaluated. 574 | #[derive(Debug, Clone, Copy)] 575 | pub enum LogicalOp { 576 | And, 577 | Or, 578 | } 579 | 580 | #[derive(Debug, Clone, Copy)] 581 | pub enum UnOp { 582 | Not, 583 | Neg, 584 | } 585 | 586 | #[derive(Debug, Clone)] 587 | pub enum ConstValue { 588 | Bool(bool), 589 | Char(char), 590 | I8(i8), 591 | I16(i16), 592 | I32(i32), 593 | I64(i64), 594 | I128(i128), 595 | Isize(isize), 596 | U8(u8), 597 | U16(u16), 598 | U32(u32), 599 | U64(u64), 600 | U128(u128), 601 | F32(f32), 602 | F64(f64), 603 | Str(String), 604 | } 605 | -------------------------------------------------------------------------------- /lib/edlang_lowering/src/prepass.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use crate::{errors::LoweringError, DefId}; 4 | 5 | use super::common::BuildCtx; 6 | use edlang_ast as ast; 7 | use edlang_ir::ModuleBody; 8 | 9 | pub fn prepass_module( 10 | mut ctx: BuildCtx, 11 | mod_def: &ast::Module, 12 | file_id: usize, 13 | ) -> Result { 14 | let module_id = ctx.gen.next_defid(); 15 | tracing::debug!("running ir prepass on module {:?}", module_id); 16 | 17 | ctx.body 18 | .top_level_module_names 19 | .insert(mod_def.name.name.clone(), module_id); 20 | ctx.body.top_level_modules.push(module_id); 21 | 22 | ctx.body.modules.insert( 23 | module_id, 24 | ModuleBody { 25 | module_id, 26 | parent_ids: vec![], 27 | name: mod_def.name.name.clone(), 28 | symbols: Default::default(), 29 | modules: Default::default(), 30 | functions: Default::default(), 31 | structs: Default::default(), 32 | types: Default::default(), 33 | constants: Default::default(), 34 | imports: Default::default(), 35 | span: mod_def.span, 36 | file_id, 37 | }, 38 | ); 39 | 40 | { 41 | let mut gen = ctx.gen; 42 | let current_module = ctx 43 | .body 44 | .modules 45 | .get_mut(&module_id) 46 | .expect("module should exist"); 47 | 48 | for ct in &mod_def.contents { 49 | match ct { 50 | ast::ModuleStatement::Constant(info) => { 51 | let next_id = gen.next_defid(); 52 | current_module 53 | .symbols 54 | .constants 55 | .insert(info.name.name.clone(), next_id); 56 | current_module.constants.insert(next_id); 57 | } 58 | ast::ModuleStatement::Function(info) => { 59 | let next_id = gen.next_defid(); 60 | current_module 61 | .symbols 62 | .functions 63 | .insert(info.name.name.clone(), next_id); 64 | current_module.functions.insert(next_id); 65 | ctx.unresolved_function_signatures.insert( 66 | next_id, 67 | ( 68 | info.params.iter().map(|x| &x.arg_type).cloned().collect(), 69 | info.return_type.clone(), 70 | ), 71 | ); 72 | } 73 | ast::ModuleStatement::Struct(info) => { 74 | if !current_module.symbols.structs.contains_key(&info.name.name) { 75 | let next_id = gen.next_defid(); 76 | current_module 77 | .symbols 78 | .structs 79 | .insert(info.name.name.clone(), next_id); 80 | current_module.structs.insert(next_id); 81 | } 82 | } 83 | /* 84 | ast::ModuleStatement::Type(info) => { 85 | let next_id = gen.next_defid(); 86 | current_module 87 | .symbols 88 | .types 89 | .insert(info.name.name.clone(), next_id); 90 | current_module.types.insert(next_id); 91 | } 92 | */ 93 | ast::ModuleStatement::Module(info) => { 94 | let next_id = gen.next_defid(); 95 | current_module 96 | .symbols 97 | .modules 98 | .insert(info.name.name.clone(), next_id); 99 | current_module.modules.insert(next_id); 100 | } 101 | ast::ModuleStatement::StructImpl(info) => { 102 | if !current_module.symbols.structs.contains_key(&info.name.name) { 103 | let next_id = gen.next_defid(); 104 | current_module 105 | .symbols 106 | .structs 107 | .insert(info.name.name.clone(), next_id); 108 | current_module.structs.insert(next_id); 109 | } 110 | 111 | let struct_id = *current_module.symbols.structs.get(&info.name.name).unwrap(); 112 | 113 | for method in &info.methods { 114 | let next_id = gen.next_defid(); 115 | let struct_methods = 116 | current_module.symbols.methods.entry(struct_id).or_default(); 117 | current_module.functions.insert(next_id); 118 | struct_methods.insert(method.name.name.clone(), next_id); 119 | 120 | ctx.unresolved_function_signatures.insert( 121 | next_id, 122 | ( 123 | method.params.iter().map(|x| &x.arg_type).cloned().collect(), 124 | method.return_type.clone(), 125 | ), 126 | ); 127 | } 128 | } 129 | } 130 | } 131 | 132 | ctx.gen = gen; 133 | } 134 | 135 | for ct in &mod_def.contents { 136 | if let ast::ModuleStatement::Module(info) = ct { 137 | let current_module = ctx 138 | .body 139 | .modules 140 | .get_mut(&module_id) 141 | .expect("module should exist"); 142 | 143 | let next_id = *current_module.symbols.modules.get(&info.name.name).unwrap(); 144 | ctx = prepass_sub_module(ctx, &[module_id], next_id, info, file_id)?; 145 | } 146 | } 147 | 148 | Ok(ctx) 149 | } 150 | 151 | pub fn prepass_sub_module( 152 | mut ctx: BuildCtx, 153 | parent_ids: &[DefId], 154 | module_id: DefId, 155 | mod_def: &ast::Module, 156 | file_id: usize, 157 | ) -> Result { 158 | tracing::debug!("running ir prepass on submodule {:?}", module_id); 159 | let mut submodule_parents_ids = parent_ids.to_vec(); 160 | submodule_parents_ids.push(module_id); 161 | 162 | { 163 | let mut gen = ctx.gen; 164 | let mut submodule = ModuleBody { 165 | module_id, 166 | name: mod_def.name.name.clone(), 167 | parent_ids: parent_ids.to_vec(), 168 | imports: Default::default(), 169 | symbols: Default::default(), 170 | modules: Default::default(), 171 | functions: Default::default(), 172 | structs: Default::default(), 173 | types: Default::default(), 174 | constants: Default::default(), 175 | span: mod_def.span, 176 | file_id, 177 | }; 178 | 179 | for ct in &mod_def.contents { 180 | match ct { 181 | ast::ModuleStatement::Constant(info) => { 182 | let next_id = gen.next_defid(); 183 | submodule 184 | .symbols 185 | .constants 186 | .insert(info.name.name.clone(), next_id); 187 | submodule.constants.insert(next_id); 188 | } 189 | ast::ModuleStatement::Function(info) => { 190 | let next_id = gen.next_defid(); 191 | submodule 192 | .symbols 193 | .functions 194 | .insert(info.name.name.clone(), next_id); 195 | submodule.functions.insert(next_id); 196 | ctx.unresolved_function_signatures.insert( 197 | next_id, 198 | ( 199 | info.params.iter().map(|x| &x.arg_type).cloned().collect(), 200 | info.return_type.clone(), 201 | ), 202 | ); 203 | } 204 | ast::ModuleStatement::Struct(info) => { 205 | if !submodule.symbols.structs.contains_key(&info.name.name) { 206 | let next_id = gen.next_defid(); 207 | submodule 208 | .symbols 209 | .structs 210 | .insert(info.name.name.clone(), next_id); 211 | submodule.structs.insert(next_id); 212 | } 213 | } 214 | /* 215 | ast::ModuleStatement::Type(info) => { 216 | let next_id = gen.next_defid(); 217 | submodule 218 | .symbols 219 | .types 220 | .insert(info.name.name.clone(), next_id); 221 | submodule.types.insert(next_id); 222 | } 223 | */ 224 | ast::ModuleStatement::Module(info) => { 225 | let next_id = gen.next_defid(); 226 | submodule 227 | .symbols 228 | .modules 229 | .insert(info.name.name.clone(), next_id); 230 | submodule.modules.insert(next_id); 231 | } 232 | ast::ModuleStatement::StructImpl(info) => { 233 | if !submodule.symbols.structs.contains_key(&info.name.name) { 234 | let next_id = gen.next_defid(); 235 | submodule 236 | .symbols 237 | .structs 238 | .insert(info.name.name.clone(), next_id); 239 | submodule.structs.insert(next_id); 240 | } 241 | 242 | let struct_id = *submodule.symbols.structs.get(&info.name.name).unwrap(); 243 | 244 | for method in &info.methods { 245 | let next_id = gen.next_defid(); 246 | let struct_methods = 247 | submodule.symbols.methods.entry(struct_id).or_default(); 248 | submodule.functions.insert(next_id); 249 | struct_methods.insert(method.name.name.clone(), next_id); 250 | 251 | ctx.unresolved_function_signatures.insert( 252 | next_id, 253 | ( 254 | method.params.iter().map(|x| &x.arg_type).cloned().collect(), 255 | method.return_type.clone(), 256 | ), 257 | ); 258 | } 259 | } 260 | } 261 | } 262 | 263 | ctx.gen = gen; 264 | 265 | ctx.body.modules.insert(module_id, submodule); 266 | } 267 | 268 | for ct in &mod_def.contents { 269 | if let ast::ModuleStatement::Module(info) = ct { 270 | let next_id = ctx.gen.next_defid(); 271 | ctx = prepass_sub_module(ctx, &submodule_parents_ids, next_id, info, file_id)?; 272 | } 273 | } 274 | 275 | Ok(ctx) 276 | } 277 | 278 | pub fn prepass_imports( 279 | mut ctx: BuildCtx, 280 | mod_def: &ast::Module, 281 | file_id: usize, 282 | ) -> Result { 283 | let mod_id = *ctx 284 | .body 285 | .top_level_module_names 286 | .get(&mod_def.name.name) 287 | .unwrap(); 288 | 289 | for import in &mod_def.imports { 290 | let imported_module_id = ctx 291 | .body 292 | .top_level_module_names 293 | .get(&import.module[0].name) 294 | .ok_or_else(|| LoweringError::ModuleNotFound { 295 | span: import.module[0].span, 296 | module: import.module[0].name.clone(), 297 | file_id, 298 | })?; 299 | let mut imported_module = 300 | ctx.body 301 | .modules 302 | .get(imported_module_id) 303 | .ok_or(LoweringError::IdNotFound { 304 | span: mod_def.span, 305 | id: *imported_module_id, 306 | file_id, 307 | })?; 308 | 309 | for module_path in import.module.iter().skip(1) { 310 | let imported_module_id = imported_module 311 | .symbols 312 | .modules 313 | .get(&module_path.name) 314 | .ok_or_else(|| LoweringError::ModuleNotFound { 315 | span: module_path.span, 316 | module: module_path.name.clone(), 317 | file_id, 318 | })?; 319 | imported_module = ctx.body.modules.get(imported_module_id).ok_or({ 320 | LoweringError::IdNotFound { 321 | span: module_path.span, 322 | id: *imported_module_id, 323 | file_id, 324 | } 325 | })?; 326 | } 327 | 328 | let mut imports = HashMap::new(); 329 | 330 | for sym in &import.symbols { 331 | if let Some(id) = imported_module.symbols.functions.get(&sym.name) { 332 | imports.insert(sym.name.clone(), *id); 333 | } else if let Some(id) = imported_module.symbols.structs.get(&sym.name) { 334 | imports.insert(sym.name.clone(), *id); 335 | } else if let Some(id) = imported_module.symbols.types.get(&sym.name) { 336 | imports.insert(sym.name.clone(), *id); 337 | } else if let Some(id) = imported_module.symbols.constants.get(&sym.name) { 338 | imports.insert(sym.name.clone(), *id); 339 | } else { 340 | Err(LoweringError::ImportNotFound { 341 | module_span: mod_def.span, 342 | import_span: import.span, 343 | symbol: sym.clone(), 344 | file_id, 345 | })?; 346 | } 347 | } 348 | 349 | ctx.body 350 | .modules 351 | .get_mut(&mod_id) 352 | .unwrap() 353 | .imports 354 | .extend(imports); 355 | } 356 | 357 | for c in &mod_def.contents { 358 | if let ast::ModuleStatement::Module(info) = c { 359 | ctx = prepass_imports_submodule(ctx, info, mod_id, file_id)?; 360 | } 361 | } 362 | 363 | Ok(ctx) 364 | } 365 | 366 | pub fn prepass_imports_submodule( 367 | mut ctx: BuildCtx, 368 | mod_def: &ast::Module, 369 | parent_id: DefId, 370 | file_id: usize, 371 | ) -> Result { 372 | let mod_id = *ctx 373 | .body 374 | .modules 375 | .get(&parent_id) 376 | .unwrap() 377 | .symbols 378 | .modules 379 | .get(&mod_def.name.name) 380 | .ok_or_else(|| LoweringError::ModuleNotFound { 381 | span: mod_def.span, 382 | module: mod_def.name.name.clone(), 383 | file_id, 384 | })?; 385 | 386 | for import in &mod_def.imports { 387 | let imported_module_id = ctx 388 | .body 389 | .top_level_module_names 390 | .get(&import.module[0].name) 391 | .ok_or_else(|| LoweringError::ModuleNotFound { 392 | span: import.module[0].span, 393 | module: import.module[0].name.clone(), 394 | file_id, 395 | })?; 396 | let mut imported_module = ctx.body.modules.get(imported_module_id).ok_or_else(|| { 397 | LoweringError::ModuleNotFound { 398 | span: import.module[0].span, 399 | module: import.module[0].name.clone(), 400 | file_id, 401 | } 402 | })?; 403 | 404 | for module_path in import.module.iter().skip(1) { 405 | let imported_module_id = imported_module 406 | .symbols 407 | .modules 408 | .get(&module_path.name) 409 | .ok_or_else(|| LoweringError::ModuleNotFound { 410 | span: module_path.span, 411 | module: module_path.name.clone(), 412 | file_id, 413 | })?; 414 | imported_module = ctx.body.modules.get(imported_module_id).ok_or({ 415 | LoweringError::IdNotFound { 416 | span: import.span, 417 | id: *imported_module_id, 418 | file_id, 419 | } 420 | })?; 421 | } 422 | 423 | let mut imports = HashMap::new(); 424 | 425 | for sym in &import.symbols { 426 | if let Some(id) = imported_module.symbols.functions.get(&sym.name) { 427 | imports.insert(sym.name.clone(), *id); 428 | } else if let Some(id) = imported_module.symbols.structs.get(&sym.name) { 429 | imports.insert(sym.name.clone(), *id); 430 | } else if let Some(id) = imported_module.symbols.types.get(&sym.name) { 431 | imports.insert(sym.name.clone(), *id); 432 | } else if let Some(id) = imported_module.symbols.constants.get(&sym.name) { 433 | imports.insert(sym.name.clone(), *id); 434 | } else { 435 | Err(LoweringError::ImportNotFound { 436 | module_span: mod_def.span, 437 | import_span: import.span, 438 | symbol: sym.clone(), 439 | file_id, 440 | })?; 441 | } 442 | } 443 | 444 | ctx.body 445 | .modules 446 | .get_mut(&mod_id) 447 | .unwrap() 448 | .imports 449 | .extend(imports); 450 | } 451 | 452 | Ok(ctx) 453 | } 454 | -------------------------------------------------------------------------------- /lib/edlang_parser/src/grammar.lalrpop: -------------------------------------------------------------------------------- 1 | use crate::tokens::Token; 2 | use crate::lexer::LexicalError; 3 | use edlang_ast as ast; 4 | use std::str::FromStr; 5 | 6 | grammar<'module_name>(module_name: &'module_name str); 7 | 8 | extern { 9 | type Location = usize; 10 | type Error = LexicalError; 11 | 12 | enum Token { 13 | // keywords 14 | "let" => Token::KeywordLet, 15 | "const" => Token::KeywordConst, 16 | "fn" => Token::KeywordFn, 17 | "return" => Token::KeywordReturn, 18 | "struct" => Token::KeywordStruct, 19 | "if" => Token::KeywordIf, 20 | "else" => Token::KeywordElse, 21 | "while" => Token::KeywordWhile, 22 | "for" => Token::KeywordFor, 23 | "match" => Token::KeywordMatch, 24 | "mod" => Token::KeywordMod, 25 | "pub" => Token::KeywordPub, 26 | "mut" => Token::KeywordMut, 27 | "use" => Token::KeywordUse, 28 | "in" => Token::KeywordIn, 29 | "extern" => Token::KeywordExtern, 30 | "as" => Token::KeywordAs, 31 | "exported" => Token::KeywordExported, 32 | "impl" => Token::KeywordImpl, 33 | 34 | // literals 35 | "identifier" => Token::Identifier(), 36 | "integer" => Token::Integer(), 37 | "string" => Token::String(), 38 | "boolean" => Token::Boolean(), 39 | 40 | // Other 41 | 42 | "(" => Token::LeftParen, 43 | ")" => Token::RightParen, 44 | "{" => Token::LeftBracket, 45 | "}" => Token::RightBracket, 46 | "[" => Token::LeftSquareBracket, 47 | "]" => Token::RightSquareBracket, 48 | "=" => Token::Assign, 49 | ";" => Token::Semicolon, 50 | ":" => Token::Colon, 51 | "::" => Token::DoubleColon, 52 | "->" => Token::Arrow, 53 | "," => Token::Coma, 54 | "<" => Token::LessThanSign, 55 | ">" => Token::MoreThanSign, 56 | ">=" => Token::MoreThanEqSign, 57 | "<=" => Token::LessThanEqSign, 58 | "." => Token::Dot, 59 | ".." => Token::TwoDots, 60 | 61 | // operators 62 | "+" => Token::OperatorAdd, 63 | "-" => Token::OperatorSub, 64 | "*" => Token::OperatorMul, 65 | "/" => Token::OperatorDiv, 66 | "%" => Token::OperatorRem, 67 | "&&" => Token::OperatorAnd, 68 | "||" => Token::OperatorOr, 69 | "==" => Token::OperatorEq, 70 | "!=" => Token::OperatorNe, 71 | "!" => Token::OperatorNot, 72 | "~" => Token::OperatorBitwiseNot, 73 | "^" => Token::OperatorBitwiseXor, 74 | "&" => Token::OperatorBitwiseAnd, 75 | "|" => Token::OperatorBitwiseOr, 76 | } 77 | } 78 | 79 | // lalrpop macros 80 | 81 | Dot: Vec = { 82 | ".")*> => match e { 83 | None => v, 84 | Some(e) => { 85 | v.push(e); 86 | v 87 | } 88 | } 89 | }; 90 | 91 | Comma: Vec = { 92 | ",")*> => match e { 93 | None => v, 94 | Some(e) => { 95 | v.push(e); 96 | v 97 | } 98 | } 99 | }; 100 | 101 | SemiColon: Vec = { 102 | ";")*> => match e { 103 | None => v, 104 | Some(e) => { 105 | v.push(e); 106 | v 107 | } 108 | } 109 | }; 110 | 111 | DoubleColon: Vec = { 112 | "::")*> => match e { 113 | None => v, 114 | Some(e) => { 115 | v.push(e); 116 | v 117 | } 118 | } 119 | }; 120 | 121 | PlusSeparated: Vec = { 122 | "+")*> => match e { 123 | None => v, 124 | Some(e) => { 125 | v.push(e); 126 | v 127 | } 128 | } 129 | }; 130 | 131 | List: Vec = { 132 | => vec![<>], 133 | > => { 134 | s.push(n); 135 | s 136 | }, 137 | } 138 | 139 | pub(crate) Ident: ast::Ident = { 140 | => ast::Ident { 141 | name, 142 | span: ast::Span::new(lo, hi), 143 | } 144 | } 145 | 146 | pub(crate) TypeQualifier: ast::TypeQualifier = { 147 | "&" => ast::TypeQualifier::Ref, 148 | "&" "mut" => ast::TypeQualifier::RefMut, 149 | "*" "const" => ast::TypeQualifier::Ptr, 150 | "*" "mut" => ast::TypeQualifier::PtrMut, 151 | } 152 | 153 | pub(crate) Type: ast::Type = { 154 | => ast::Type::Basic { 155 | name, 156 | generics: vec![], 157 | qualifiers, 158 | span: ast::Span::new(lo, hi), 159 | }, 160 | "<" > ">" => ast::Type::Basic { 161 | name, 162 | generics, 163 | qualifiers, 164 | span: ast::Span::new(lo, hi), 165 | }, 166 | "[" "]" => ast::Type::Array { 167 | of: Box::new(of), 168 | size: None, 169 | qualifiers, 170 | span: ast::Span::new(lo, hi), 171 | }, 172 | "[" ";" "]" => ast::Type::Array { 173 | of: Box::new(of), 174 | size: Some(size.try_into().unwrap()), 175 | qualifiers, 176 | span: ast::Span::new(lo, hi), 177 | }, 178 | } 179 | 180 | /// For things like A:: { a: value } 181 | pub(crate) IdentWithGenerics: (ast::Ident, Vec) = { 182 | => (name, vec![]), 183 | "::" "<" > ">" => (name, generics), 184 | } 185 | 186 | pub(crate) PathExpr: ast::PathExpr = { 187 | => ast::PathExpr { 188 | first, 189 | extra: extra.unwrap_or(vec![]), 190 | span: ast::Span::new(lo, hi), 191 | }, 192 | } 193 | 194 | pub PathSegments: Vec = { 195 | => vec![<>], 196 | => { 197 | s.push(n); 198 | s 199 | }, 200 | } 201 | 202 | pub(crate) PathSegment: ast::PathSegment = { 203 | "." => ast::PathSegment::Field(<>), 204 | "." => ast::PathSegment::Method { 205 | value, 206 | span: ast::Span::new(lo, hi), 207 | }, 208 | "[" "]" => ast::PathSegment::Index { 209 | value, 210 | span: ast::Span::new(lo, hi), 211 | }, 212 | } 213 | 214 | pub(crate) Block: ast::Block = { 215 | "{" > "}" => ast::Block { 216 | body, 217 | span: ast::Span::new(lo, hi), 218 | } 219 | } 220 | 221 | pub(crate) Statement: ast::Statement = { 222 | => ast::Statement::If(<>), 223 | => ast::Statement::While(<>), 224 | => ast::Statement::For(<>), 225 | ";" => ast::Statement::Let(<>), 226 | ";" => ast::Statement::Assign(<>), 227 | ";" => ast::Statement::FnCall(<>), 228 | ";" => ast::Statement::Return(<>), 229 | } 230 | 231 | pub(crate) FnCallExpr: ast::FnCallExpr = { 232 | "(" > ")" => ast::FnCallExpr { 233 | name, 234 | params, 235 | span: ast::Span::new(lo, hi), 236 | } 237 | } 238 | 239 | pub(crate) LetStmt: ast::LetStmt = { 240 | "let" ":" "=" => ast::LetStmt { 241 | is_mut: is_mut.is_some(), 242 | name, 243 | r#type: target_type, 244 | value, 245 | span: ast::Span::new(lo, hi), 246 | }, 247 | "let" ":" "=" => ast::LetStmt { 248 | is_mut: is_mut.is_some(), 249 | name, 250 | r#type: target_type, 251 | value: ast::Expression::StructInit(value), 252 | span: ast::Span::new(lo, hi), 253 | }, 254 | } 255 | 256 | pub(crate) AssignStmt: ast::AssignStmt = { 257 | "=" => ast::AssignStmt { 258 | name, 259 | value, 260 | deref_times: deref.len(), 261 | span: ast::Span::new(lo, hi), 262 | }, 263 | "=" => ast::AssignStmt { 264 | name, 265 | value: ast::Expression::StructInit(value), 266 | deref_times: deref.len(), 267 | span: ast::Span::new(lo, hi), 268 | }, 269 | } 270 | 271 | pub(crate) ReturnStmt: ast::ReturnStmt = { 272 | "return" => ast::ReturnStmt { 273 | value, 274 | span: ast::Span::new(lo, hi), 275 | }, 276 | } 277 | 278 | pub(crate) WhileStmt: ast::WhileStmt = { 279 | "while" => { 280 | ast::WhileStmt { 281 | condition, 282 | block, 283 | span: ast::Span::new(lo, hi), 284 | } 285 | } 286 | } 287 | 288 | pub(crate) ForStmt: ast::ForStmt = { 289 | "for" "in" => ast::ForStmt { 290 | name, 291 | from, 292 | to: None, 293 | block, 294 | span: ast::Span::new(lo, hi), 295 | }, 296 | "for" "in" ".." => ast::ForStmt { 297 | name, 298 | from, 299 | to: Some(to), 300 | block, 301 | span: ast::Span::new(lo, hi), 302 | }, 303 | } 304 | 305 | pub(crate) IfStmt: ast::IfStmt = { 306 | "if" )?> => { 307 | ast::IfStmt { 308 | condition, 309 | then_block, 310 | else_block, 311 | span: ast::Span::new(lo, hi), 312 | } 313 | } 314 | } 315 | 316 | pub(crate) Term: ast::Expression = { 317 | #[precedence(level="0")] 318 | => ast::Expression::Value(<>), 319 | => ast::Expression::FnCall(<>), 320 | #[precedence(level="2")] #[assoc(side="left")] 321 | "(" ")", 322 | } 323 | 324 | pub(crate) Expression: ast::Expression = { 325 | #[precedence(level="0")] 326 | , 327 | #[precedence(level="1")] #[assoc(side="left")] 328 | "&" "mut" => ast::Expression::AsRef(Box::new(e), true, ast::Span::new(lo, hi)), 329 | "&" => ast::Expression::AsRef(Box::new(e), false, ast::Span::new(lo, hi)), 330 | "*" => ast::Expression::Deref(Box::new(e), ast::Span::new(lo, hi)), 331 | => ast::Expression::Unary( 332 | op, 333 | Box::new(rhs) 334 | ), 335 | #[precedence(level="2")] #[assoc(side="left")] 336 | => ast::Expression::Binary( 337 | Box::new(lhs), 338 | op, 339 | Box::new(rhs) 340 | ), 341 | #[precedence(level="3")] #[assoc(side="left")] 342 | => ast::Expression::Binary( 343 | Box::new(lhs), 344 | op, 345 | Box::new(rhs) 346 | ), 347 | #[precedence(level="4")] #[assoc(side="left")] 348 | => ast::Expression::Binary( 349 | Box::new(lhs), 350 | op, 351 | Box::new(rhs) 352 | ), 353 | #[precedence(level="5")] #[assoc(side="left")] 354 | "as" => ast::Expression::Cast(Box::new(a), b, ast::Span::new(lo, hi)), 355 | "(" ")" => ast::Expression::StructInit(<>), 356 | => ast::Expression::ArrayInit(<>), 357 | } 358 | 359 | pub BinaryFirstLvlOp: ast::BinaryOp = { 360 | "==" => ast::BinaryOp::Compare(ast::CmpOp::Eq, ast::Span::new(lo, hi)), 361 | "!=" => ast::BinaryOp::Compare(ast::CmpOp::NotEq, ast::Span::new(lo, hi)), 362 | "<" => ast::BinaryOp::Compare(ast::CmpOp::Lt, ast::Span::new(lo, hi)), 363 | ">" => ast::BinaryOp::Compare(ast::CmpOp::Gt, ast::Span::new(lo, hi)), 364 | "<=" => ast::BinaryOp::Compare(ast::CmpOp::LtEq, ast::Span::new(lo, hi)), 365 | ">=" => ast::BinaryOp::Compare(ast::CmpOp::GtEq, ast::Span::new(lo, hi)), 366 | "&&" => ast::BinaryOp::Logic(ast::LogicOp::And, ast::Span::new(lo, hi)), 367 | "||" => ast::BinaryOp::Logic(ast::LogicOp::Or, ast::Span::new(lo, hi)), 368 | } 369 | 370 | pub BinarySecondLvlOp: ast::BinaryOp = { 371 | "/" => ast::BinaryOp::Arith(ast::ArithOp::Div, ast::Span::new(lo, hi)), 372 | "*" => ast::BinaryOp::Arith(ast::ArithOp::Mul, ast::Span::new(lo, hi)), 373 | "%" => ast::BinaryOp::Arith(ast::ArithOp::Mod, ast::Span::new(lo, hi)), 374 | } 375 | 376 | pub BinaryThirdLvlOp: ast::BinaryOp = { 377 | "+" => ast::BinaryOp::Arith(ast::ArithOp::Add, ast::Span::new(lo, hi)), 378 | "-" => ast::BinaryOp::Arith(ast::ArithOp::Sub, ast::Span::new(lo, hi)), 379 | "&" => ast::BinaryOp::Bitwise(ast::BitwiseOp::And, ast::Span::new(lo, hi)), 380 | "|" => ast::BinaryOp::Bitwise(ast::BitwiseOp::Or, ast::Span::new(lo, hi)), 381 | "^" => ast::BinaryOp::Bitwise(ast::BitwiseOp::Xor, ast::Span::new(lo, hi)), 382 | } 383 | 384 | pub UnaryOp: ast::UnaryOp = { 385 | "-" => ast::UnaryOp::ArithNeg(ast::Span::new(lo, hi)), 386 | "!" => ast::UnaryOp::LogicalNot(ast::Span::new(lo, hi)), 387 | "~" => ast::UnaryOp::BitwiseNot(ast::Span::new(lo, hi)), 388 | } 389 | 390 | pub(crate) ValueExpr: ast::ValueExpr = { 391 | => ast::ValueExpr::Int { 392 | value, 393 | span: ast::Span::new(lo, hi), 394 | }, 395 | => ast::ValueExpr::Bool{ 396 | value, 397 | span: ast::Span::new(lo, hi), 398 | }, 399 | => ast::ValueExpr::Str{ 400 | value, 401 | span: ast::Span::new(lo, hi), 402 | }, 403 | => ast::ValueExpr::Path(<>), 404 | } 405 | 406 | pub(crate) StructInitField: (ast::Ident, ast::StructInitField) = { 407 | ":" => (name, ast::StructInitField { 408 | value, 409 | span: ast::Span::new(lo, hi), 410 | }), 411 | ":" => (name, ast::StructInitField { 412 | value: ast::Expression::StructInit(value), 413 | span: ast::Span::new(lo, hi), 414 | }) 415 | } 416 | 417 | pub(crate) StructInitExpr: ast::StructInitExpr = { 418 | "{" > "}" => ast::StructInitExpr { 419 | name: name.0, 420 | generics: name.1, 421 | fields: fields.into_iter().collect(), 422 | span: ast::Span::new(lo, hi), 423 | } 424 | } 425 | 426 | pub(crate) ArrayInitExpr: ast::ArrayInitExpr = { 427 | "[" > "]" => ast::ArrayInitExpr { 428 | data: data.into_iter().collect(), 429 | span: ast::Span::new(lo, hi), 430 | } 431 | } 432 | 433 | pub(crate) FnParam: ast::FnParam = { 434 | ":" => ast::FnParam { 435 | name, 436 | arg_type, 437 | span: ast::Span::new(lo, hi), 438 | } 439 | } 440 | 441 | pub(crate) Function: ast::Function = { 442 | "extern" "fn" "(" > ")" 443 | " )?> ";" => ast::Function { 444 | is_public: is_public.is_some(), 445 | is_extern: true, 446 | is_exported: false, 447 | name, 448 | params, 449 | return_type, 450 | body: None, 451 | span: ast::Span::new(lo, hi), 452 | }, 453 | "fn" "(" > ")" 454 | " )?> => ast::Function { 455 | is_public: is_public.is_some(), 456 | is_extern: false, 457 | is_exported: is_exported.is_some(), 458 | name, 459 | params, 460 | return_type, 461 | body: Some(body), 462 | span: ast::Span::new(lo, hi), 463 | } 464 | } 465 | 466 | pub(crate) Constant: ast::Constant = { 467 | "const" ":" "=" ";" => ast::Constant { 468 | name, 469 | r#type, 470 | value, 471 | span: ast::Span::new(lo, hi), 472 | } 473 | } 474 | 475 | pub(crate) StructField: ast::StructField = { 476 | ":" => ast::StructField { 477 | name, 478 | r#type, 479 | span: ast::Span::new(lo, hi), 480 | } 481 | } 482 | 483 | pub(crate) Struct: ast::Struct = { 484 | "struct" > ">")?> "{" > "}" => ast::Struct { 485 | name, 486 | fields, 487 | generics: generics.unwrap_or(vec![]), 488 | span: ast::Span::new(lo, hi), 489 | } 490 | } 491 | 492 | pub StructImpl: ast::StructImpl = { 493 | "impl" > ">")?> "{" ?> "}" => ast::StructImpl { 494 | name, 495 | methods: methods.unwrap_or(vec![]), 496 | generics: generics.unwrap_or(vec![]), 497 | span: ast::Span::new(lo, hi), 498 | } 499 | } 500 | 501 | pub(crate) Import: ast::Import = { 502 | "use" > > "}")?> ";" => ast::Import { 503 | module, 504 | symbols: symbols.unwrap_or(vec![]), 505 | span: ast::Span::new(lo, hi), 506 | } 507 | } 508 | 509 | pub(crate) ExternalModule: ast::Ident = { 510 | "mod" ";" => name 511 | } 512 | 513 | pub TopLevelModule: ast::Module = { 514 | ?> ?> > => ast::Module { 515 | name: ast::Ident { 516 | name: module_name.to_string(), 517 | span: ast::Span::new(0, 0), 518 | }, 519 | imports: imports.unwrap_or(vec![]), 520 | external_modules: external_modules.unwrap_or(vec![]), 521 | contents, 522 | span: ast::Span::new(lo, hi), 523 | } 524 | } 525 | 526 | pub Module: ast::Module = { 527 | "mod" "{" ?> > "}" => ast::Module { 528 | name, 529 | imports: imports.unwrap_or(vec![]), 530 | external_modules: Vec::new(), 531 | contents, 532 | span: ast::Span::new(lo, hi), 533 | } 534 | } 535 | 536 | pub(crate) ModuleStatement: ast::ModuleStatement = { 537 | => ast::ModuleStatement::Function(<>), 538 | => ast::ModuleStatement::Constant(<>), 539 | => ast::ModuleStatement::Struct(<>), 540 | => ast::ModuleStatement::StructImpl(<>), 541 | => ast::ModuleStatement::Module(<>), 542 | } 543 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU AFFERO GENERAL PUBLIC LICENSE 2 | Version 3, 19 November 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU Affero General Public License is a free, copyleft license for 11 | software and other kinds of works, specifically designed to ensure 12 | cooperation with the community in the case of network server software. 13 | 14 | The licenses for most software and other practical works are designed 15 | to take away your freedom to share and change the works. By contrast, 16 | our General Public Licenses are intended to guarantee your freedom to 17 | share and change all versions of a program--to make sure it remains free 18 | software for all its users. 19 | 20 | When we speak of free software, we are referring to freedom, not 21 | price. Our General Public Licenses are designed to make sure that you 22 | have the freedom to distribute copies of free software (and charge for 23 | them if you wish), that you receive source code or can get it if you 24 | want it, that you can change the software or use pieces of it in new 25 | free programs, and that you know you can do these things. 26 | 27 | Developers that use our General Public Licenses protect your rights 28 | with two steps: (1) assert copyright on the software, and (2) offer 29 | you this License which gives you legal permission to copy, distribute 30 | and/or modify the software. 31 | 32 | A secondary benefit of defending all users' freedom is that 33 | improvements made in alternate versions of the program, if they 34 | receive widespread use, become available for other developers to 35 | incorporate. Many developers of free software are heartened and 36 | encouraged by the resulting cooperation. However, in the case of 37 | software used on network servers, this result may fail to come about. 38 | The GNU General Public License permits making a modified version and 39 | letting the public access it on a server without ever releasing its 40 | source code to the public. 41 | 42 | The GNU Affero General Public License is designed specifically to 43 | ensure that, in such cases, the modified source code becomes available 44 | to the community. It requires the operator of a network server to 45 | provide the source code of the modified version running there to the 46 | users of that server. Therefore, public use of a modified version, on 47 | a publicly accessible server, gives the public access to the source 48 | code of the modified version. 49 | 50 | An older license, called the Affero General Public License and 51 | published by Affero, was designed to accomplish similar goals. This is 52 | a different license, not a version of the Affero GPL, but Affero has 53 | released a new version of the Affero GPL which permits relicensing under 54 | this license. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | TERMS AND CONDITIONS 60 | 61 | 0. Definitions. 62 | 63 | "This License" refers to version 3 of the GNU Affero General Public License. 64 | 65 | "Copyright" also means copyright-like laws that apply to other kinds of 66 | works, such as semiconductor masks. 67 | 68 | "The Program" refers to any copyrightable work licensed under this 69 | License. Each licensee is addressed as "you". "Licensees" and 70 | "recipients" may be individuals or organizations. 71 | 72 | To "modify" a work means to copy from or adapt all or part of the work 73 | in a fashion requiring copyright permission, other than the making of an 74 | exact copy. The resulting work is called a "modified version" of the 75 | earlier work or a work "based on" the earlier work. 76 | 77 | A "covered work" means either the unmodified Program or a work based 78 | on the Program. 79 | 80 | To "propagate" a work means to do anything with it that, without 81 | permission, would make you directly or secondarily liable for 82 | infringement under applicable copyright law, except executing it on a 83 | computer or modifying a private copy. Propagation includes copying, 84 | distribution (with or without modification), making available to the 85 | public, and in some countries other activities as well. 86 | 87 | To "convey" a work means any kind of propagation that enables other 88 | parties to make or receive copies. Mere interaction with a user through 89 | a computer network, with no transfer of a copy, is not conveying. 90 | 91 | An interactive user interface displays "Appropriate Legal Notices" 92 | to the extent that it includes a convenient and prominently visible 93 | feature that (1) displays an appropriate copyright notice, and (2) 94 | tells the user that there is no warranty for the work (except to the 95 | extent that warranties are provided), that licensees may convey the 96 | work under this License, and how to view a copy of this License. If 97 | the interface presents a list of user commands or options, such as a 98 | menu, a prominent item in the list meets this criterion. 99 | 100 | 1. Source Code. 101 | 102 | The "source code" for a work means the preferred form of the work 103 | for making modifications to it. "Object code" means any non-source 104 | form of a work. 105 | 106 | A "Standard Interface" means an interface that either is an official 107 | standard defined by a recognized standards body, or, in the case of 108 | interfaces specified for a particular programming language, one that 109 | is widely used among developers working in that language. 110 | 111 | The "System Libraries" of an executable work include anything, other 112 | than the work as a whole, that (a) is included in the normal form of 113 | packaging a Major Component, but which is not part of that Major 114 | Component, and (b) serves only to enable use of the work with that 115 | Major Component, or to implement a Standard Interface for which an 116 | implementation is available to the public in source code form. A 117 | "Major Component", in this context, means a major essential component 118 | (kernel, window system, and so on) of the specific operating system 119 | (if any) on which the executable work runs, or a compiler used to 120 | produce the work, or an object code interpreter used to run it. 121 | 122 | The "Corresponding Source" for a work in object code form means all 123 | the source code needed to generate, install, and (for an executable 124 | work) run the object code and to modify the work, including scripts to 125 | control those activities. However, it does not include the work's 126 | System Libraries, or general-purpose tools or generally available free 127 | programs which are used unmodified in performing those activities but 128 | which are not part of the work. For example, Corresponding Source 129 | includes interface definition files associated with source files for 130 | the work, and the source code for shared libraries and dynamically 131 | linked subprograms that the work is specifically designed to require, 132 | such as by intimate data communication or control flow between those 133 | subprograms and other parts of the work. 134 | 135 | The Corresponding Source need not include anything that users 136 | can regenerate automatically from other parts of the Corresponding 137 | Source. 138 | 139 | The Corresponding Source for a work in source code form is that 140 | same work. 141 | 142 | 2. Basic Permissions. 143 | 144 | All rights granted under this License are granted for the term of 145 | copyright on the Program, and are irrevocable provided the stated 146 | conditions are met. This License explicitly affirms your unlimited 147 | permission to run the unmodified Program. The output from running a 148 | covered work is covered by this License only if the output, given its 149 | content, constitutes a covered work. This License acknowledges your 150 | rights of fair use or other equivalent, as provided by copyright law. 151 | 152 | You may make, run and propagate covered works that you do not 153 | convey, without conditions so long as your license otherwise remains 154 | in force. You may convey covered works to others for the sole purpose 155 | of having them make modifications exclusively for you, or provide you 156 | with facilities for running those works, provided that you comply with 157 | the terms of this License in conveying all material for which you do 158 | not control copyright. Those thus making or running the covered works 159 | for you must do so exclusively on your behalf, under your direction 160 | and control, on terms that prohibit them from making any copies of 161 | your copyrighted material outside their relationship with you. 162 | 163 | Conveying under any other circumstances is permitted solely under 164 | the conditions stated below. Sublicensing is not allowed; section 10 165 | makes it unnecessary. 166 | 167 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 168 | 169 | No covered work shall be deemed part of an effective technological 170 | measure under any applicable law fulfilling obligations under article 171 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 172 | similar laws prohibiting or restricting circumvention of such 173 | measures. 174 | 175 | When you convey a covered work, you waive any legal power to forbid 176 | circumvention of technological measures to the extent such circumvention 177 | is effected by exercising rights under this License with respect to 178 | the covered work, and you disclaim any intention to limit operation or 179 | modification of the work as a means of enforcing, against the work's 180 | users, your or third parties' legal rights to forbid circumvention of 181 | technological measures. 182 | 183 | 4. Conveying Verbatim Copies. 184 | 185 | You may convey verbatim copies of the Program's source code as you 186 | receive it, in any medium, provided that you conspicuously and 187 | appropriately publish on each copy an appropriate copyright notice; 188 | keep intact all notices stating that this License and any 189 | non-permissive terms added in accord with section 7 apply to the code; 190 | keep intact all notices of the absence of any warranty; and give all 191 | recipients a copy of this License along with the Program. 192 | 193 | You may charge any price or no price for each copy that you convey, 194 | and you may offer support or warranty protection for a fee. 195 | 196 | 5. Conveying Modified Source Versions. 197 | 198 | You may convey a work based on the Program, or the modifications to 199 | produce it from the Program, in the form of source code under the 200 | terms of section 4, provided that you also meet all of these conditions: 201 | 202 | a) The work must carry prominent notices stating that you modified 203 | it, and giving a relevant date. 204 | 205 | b) The work must carry prominent notices stating that it is 206 | released under this License and any conditions added under section 207 | 7. This requirement modifies the requirement in section 4 to 208 | "keep intact all notices". 209 | 210 | c) You must license the entire work, as a whole, under this 211 | License to anyone who comes into possession of a copy. This 212 | License will therefore apply, along with any applicable section 7 213 | additional terms, to the whole of the work, and all its parts, 214 | regardless of how they are packaged. This License gives no 215 | permission to license the work in any other way, but it does not 216 | invalidate such permission if you have separately received it. 217 | 218 | d) If the work has interactive user interfaces, each must display 219 | Appropriate Legal Notices; however, if the Program has interactive 220 | interfaces that do not display Appropriate Legal Notices, your 221 | work need not make them do so. 222 | 223 | A compilation of a covered work with other separate and independent 224 | works, which are not by their nature extensions of the covered work, 225 | and which are not combined with it such as to form a larger program, 226 | in or on a volume of a storage or distribution medium, is called an 227 | "aggregate" if the compilation and its resulting copyright are not 228 | used to limit the access or legal rights of the compilation's users 229 | beyond what the individual works permit. Inclusion of a covered work 230 | in an aggregate does not cause this License to apply to the other 231 | parts of the aggregate. 232 | 233 | 6. Conveying Non-Source Forms. 234 | 235 | You may convey a covered work in object code form under the terms 236 | of sections 4 and 5, provided that you also convey the 237 | machine-readable Corresponding Source under the terms of this License, 238 | in one of these ways: 239 | 240 | a) Convey the object code in, or embodied in, a physical product 241 | (including a physical distribution medium), accompanied by the 242 | Corresponding Source fixed on a durable physical medium 243 | customarily used for software interchange. 244 | 245 | b) Convey the object code in, or embodied in, a physical product 246 | (including a physical distribution medium), accompanied by a 247 | written offer, valid for at least three years and valid for as 248 | long as you offer spare parts or customer support for that product 249 | model, to give anyone who possesses the object code either (1) a 250 | copy of the Corresponding Source for all the software in the 251 | product that is covered by this License, on a durable physical 252 | medium customarily used for software interchange, for a price no 253 | more than your reasonable cost of physically performing this 254 | conveying of source, or (2) access to copy the 255 | Corresponding Source from a network server at no charge. 256 | 257 | c) Convey individual copies of the object code with a copy of the 258 | written offer to provide the Corresponding Source. This 259 | alternative is allowed only occasionally and noncommercially, and 260 | only if you received the object code with such an offer, in accord 261 | with subsection 6b. 262 | 263 | d) Convey the object code by offering access from a designated 264 | place (gratis or for a charge), and offer equivalent access to the 265 | Corresponding Source in the same way through the same place at no 266 | further charge. You need not require recipients to copy the 267 | Corresponding Source along with the object code. If the place to 268 | copy the object code is a network server, the Corresponding Source 269 | may be on a different server (operated by you or a third party) 270 | that supports equivalent copying facilities, provided you maintain 271 | clear directions next to the object code saying where to find the 272 | Corresponding Source. Regardless of what server hosts the 273 | Corresponding Source, you remain obligated to ensure that it is 274 | available for as long as needed to satisfy these requirements. 275 | 276 | e) Convey the object code using peer-to-peer transmission, provided 277 | you inform other peers where the object code and Corresponding 278 | Source of the work are being offered to the general public at no 279 | charge under subsection 6d. 280 | 281 | A separable portion of the object code, whose source code is excluded 282 | from the Corresponding Source as a System Library, need not be 283 | included in conveying the object code work. 284 | 285 | A "User Product" is either (1) a "consumer product", which means any 286 | tangible personal property which is normally used for personal, family, 287 | or household purposes, or (2) anything designed or sold for incorporation 288 | into a dwelling. In determining whether a product is a consumer product, 289 | doubtful cases shall be resolved in favor of coverage. For a particular 290 | product received by a particular user, "normally used" refers to a 291 | typical or common use of that class of product, regardless of the status 292 | of the particular user or of the way in which the particular user 293 | actually uses, or expects or is expected to use, the product. A product 294 | is a consumer product regardless of whether the product has substantial 295 | commercial, industrial or non-consumer uses, unless such uses represent 296 | the only significant mode of use of the product. 297 | 298 | "Installation Information" for a User Product means any methods, 299 | procedures, authorization keys, or other information required to install 300 | and execute modified versions of a covered work in that User Product from 301 | a modified version of its Corresponding Source. The information must 302 | suffice to ensure that the continued functioning of the modified object 303 | code is in no case prevented or interfered with solely because 304 | modification has been made. 305 | 306 | If you convey an object code work under this section in, or with, or 307 | specifically for use in, a User Product, and the conveying occurs as 308 | part of a transaction in which the right of possession and use of the 309 | User Product is transferred to the recipient in perpetuity or for a 310 | fixed term (regardless of how the transaction is characterized), the 311 | Corresponding Source conveyed under this section must be accompanied 312 | by the Installation Information. But this requirement does not apply 313 | if neither you nor any third party retains the ability to install 314 | modified object code on the User Product (for example, the work has 315 | been installed in ROM). 316 | 317 | The requirement to provide Installation Information does not include a 318 | requirement to continue to provide support service, warranty, or updates 319 | for a work that has been modified or installed by the recipient, or for 320 | the User Product in which it has been modified or installed. Access to a 321 | network may be denied when the modification itself materially and 322 | adversely affects the operation of the network or violates the rules and 323 | protocols for communication across the network. 324 | 325 | Corresponding Source conveyed, and Installation Information provided, 326 | in accord with this section must be in a format that is publicly 327 | documented (and with an implementation available to the public in 328 | source code form), and must require no special password or key for 329 | unpacking, reading or copying. 330 | 331 | 7. Additional Terms. 332 | 333 | "Additional permissions" are terms that supplement the terms of this 334 | License by making exceptions from one or more of its conditions. 335 | Additional permissions that are applicable to the entire Program shall 336 | be treated as though they were included in this License, to the extent 337 | that they are valid under applicable law. If additional permissions 338 | apply only to part of the Program, that part may be used separately 339 | under those permissions, but the entire Program remains governed by 340 | this License without regard to the additional permissions. 341 | 342 | When you convey a copy of a covered work, you may at your option 343 | remove any additional permissions from that copy, or from any part of 344 | it. (Additional permissions may be written to require their own 345 | removal in certain cases when you modify the work.) You may place 346 | additional permissions on material, added by you to a covered work, 347 | for which you have or can give appropriate copyright permission. 348 | 349 | Notwithstanding any other provision of this License, for material you 350 | add to a covered work, you may (if authorized by the copyright holders of 351 | that material) supplement the terms of this License with terms: 352 | 353 | a) Disclaiming warranty or limiting liability differently from the 354 | terms of sections 15 and 16 of this License; or 355 | 356 | b) Requiring preservation of specified reasonable legal notices or 357 | author attributions in that material or in the Appropriate Legal 358 | Notices displayed by works containing it; or 359 | 360 | c) Prohibiting misrepresentation of the origin of that material, or 361 | requiring that modified versions of such material be marked in 362 | reasonable ways as different from the original version; or 363 | 364 | d) Limiting the use for publicity purposes of names of licensors or 365 | authors of the material; or 366 | 367 | e) Declining to grant rights under trademark law for use of some 368 | trade names, trademarks, or service marks; or 369 | 370 | f) Requiring indemnification of licensors and authors of that 371 | material by anyone who conveys the material (or modified versions of 372 | it) with contractual assumptions of liability to the recipient, for 373 | any liability that these contractual assumptions directly impose on 374 | those licensors and authors. 375 | 376 | All other non-permissive additional terms are considered "further 377 | restrictions" within the meaning of section 10. If the Program as you 378 | received it, or any part of it, contains a notice stating that it is 379 | governed by this License along with a term that is a further 380 | restriction, you may remove that term. If a license document contains 381 | a further restriction but permits relicensing or conveying under this 382 | License, you may add to a covered work material governed by the terms 383 | of that license document, provided that the further restriction does 384 | not survive such relicensing or conveying. 385 | 386 | If you add terms to a covered work in accord with this section, you 387 | must place, in the relevant source files, a statement of the 388 | additional terms that apply to those files, or a notice indicating 389 | where to find the applicable terms. 390 | 391 | Additional terms, permissive or non-permissive, may be stated in the 392 | form of a separately written license, or stated as exceptions; 393 | the above requirements apply either way. 394 | 395 | 8. Termination. 396 | 397 | You may not propagate or modify a covered work except as expressly 398 | provided under this License. Any attempt otherwise to propagate or 399 | modify it is void, and will automatically terminate your rights under 400 | this License (including any patent licenses granted under the third 401 | paragraph of section 11). 402 | 403 | However, if you cease all violation of this License, then your 404 | license from a particular copyright holder is reinstated (a) 405 | provisionally, unless and until the copyright holder explicitly and 406 | finally terminates your license, and (b) permanently, if the copyright 407 | holder fails to notify you of the violation by some reasonable means 408 | prior to 60 days after the cessation. 409 | 410 | Moreover, your license from a particular copyright holder is 411 | reinstated permanently if the copyright holder notifies you of the 412 | violation by some reasonable means, this is the first time you have 413 | received notice of violation of this License (for any work) from that 414 | copyright holder, and you cure the violation prior to 30 days after 415 | your receipt of the notice. 416 | 417 | Termination of your rights under this section does not terminate the 418 | licenses of parties who have received copies or rights from you under 419 | this License. If your rights have been terminated and not permanently 420 | reinstated, you do not qualify to receive new licenses for the same 421 | material under section 10. 422 | 423 | 9. Acceptance Not Required for Having Copies. 424 | 425 | You are not required to accept this License in order to receive or 426 | run a copy of the Program. Ancillary propagation of a covered work 427 | occurring solely as a consequence of using peer-to-peer transmission 428 | to receive a copy likewise does not require acceptance. However, 429 | nothing other than this License grants you permission to propagate or 430 | modify any covered work. These actions infringe copyright if you do 431 | not accept this License. Therefore, by modifying or propagating a 432 | covered work, you indicate your acceptance of this License to do so. 433 | 434 | 10. Automatic Licensing of Downstream Recipients. 435 | 436 | Each time you convey a covered work, the recipient automatically 437 | receives a license from the original licensors, to run, modify and 438 | propagate that work, subject to this License. You are not responsible 439 | for enforcing compliance by third parties with this License. 440 | 441 | An "entity transaction" is a transaction transferring control of an 442 | organization, or substantially all assets of one, or subdividing an 443 | organization, or merging organizations. If propagation of a covered 444 | work results from an entity transaction, each party to that 445 | transaction who receives a copy of the work also receives whatever 446 | licenses to the work the party's predecessor in interest had or could 447 | give under the previous paragraph, plus a right to possession of the 448 | Corresponding Source of the work from the predecessor in interest, if 449 | the predecessor has it or can get it with reasonable efforts. 450 | 451 | You may not impose any further restrictions on the exercise of the 452 | rights granted or affirmed under this License. For example, you may 453 | not impose a license fee, royalty, or other charge for exercise of 454 | rights granted under this License, and you may not initiate litigation 455 | (including a cross-claim or counterclaim in a lawsuit) alleging that 456 | any patent claim is infringed by making, using, selling, offering for 457 | sale, or importing the Program or any portion of it. 458 | 459 | 11. Patents. 460 | 461 | A "contributor" is a copyright holder who authorizes use under this 462 | License of the Program or a work on which the Program is based. The 463 | work thus licensed is called the contributor's "contributor version". 464 | 465 | A contributor's "essential patent claims" are all patent claims 466 | owned or controlled by the contributor, whether already acquired or 467 | hereafter acquired, that would be infringed by some manner, permitted 468 | by this License, of making, using, or selling its contributor version, 469 | but do not include claims that would be infringed only as a 470 | consequence of further modification of the contributor version. For 471 | purposes of this definition, "control" includes the right to grant 472 | patent sublicenses in a manner consistent with the requirements of 473 | this License. 474 | 475 | Each contributor grants you a non-exclusive, worldwide, royalty-free 476 | patent license under the contributor's essential patent claims, to 477 | make, use, sell, offer for sale, import and otherwise run, modify and 478 | propagate the contents of its contributor version. 479 | 480 | In the following three paragraphs, a "patent license" is any express 481 | agreement or commitment, however denominated, not to enforce a patent 482 | (such as an express permission to practice a patent or covenant not to 483 | sue for patent infringement). To "grant" such a patent license to a 484 | party means to make such an agreement or commitment not to enforce a 485 | patent against the party. 486 | 487 | If you convey a covered work, knowingly relying on a patent license, 488 | and the Corresponding Source of the work is not available for anyone 489 | to copy, free of charge and under the terms of this License, through a 490 | publicly available network server or other readily accessible means, 491 | then you must either (1) cause the Corresponding Source to be so 492 | available, or (2) arrange to deprive yourself of the benefit of the 493 | patent license for this particular work, or (3) arrange, in a manner 494 | consistent with the requirements of this License, to extend the patent 495 | license to downstream recipients. "Knowingly relying" means you have 496 | actual knowledge that, but for the patent license, your conveying the 497 | covered work in a country, or your recipient's use of the covered work 498 | in a country, would infringe one or more identifiable patents in that 499 | country that you have reason to believe are valid. 500 | 501 | If, pursuant to or in connection with a single transaction or 502 | arrangement, you convey, or propagate by procuring conveyance of, a 503 | covered work, and grant a patent license to some of the parties 504 | receiving the covered work authorizing them to use, propagate, modify 505 | or convey a specific copy of the covered work, then the patent license 506 | you grant is automatically extended to all recipients of the covered 507 | work and works based on it. 508 | 509 | A patent license is "discriminatory" if it does not include within 510 | the scope of its coverage, prohibits the exercise of, or is 511 | conditioned on the non-exercise of one or more of the rights that are 512 | specifically granted under this License. You may not convey a covered 513 | work if you are a party to an arrangement with a third party that is 514 | in the business of distributing software, under which you make payment 515 | to the third party based on the extent of your activity of conveying 516 | the work, and under which the third party grants, to any of the 517 | parties who would receive the covered work from you, a discriminatory 518 | patent license (a) in connection with copies of the covered work 519 | conveyed by you (or copies made from those copies), or (b) primarily 520 | for and in connection with specific products or compilations that 521 | contain the covered work, unless you entered into that arrangement, 522 | or that patent license was granted, prior to 28 March 2007. 523 | 524 | Nothing in this License shall be construed as excluding or limiting 525 | any implied license or other defenses to infringement that may 526 | otherwise be available to you under applicable patent law. 527 | 528 | 12. No Surrender of Others' Freedom. 529 | 530 | If conditions are imposed on you (whether by court order, agreement or 531 | otherwise) that contradict the conditions of this License, they do not 532 | excuse you from the conditions of this License. If you cannot convey a 533 | covered work so as to satisfy simultaneously your obligations under this 534 | License and any other pertinent obligations, then as a consequence you may 535 | not convey it at all. For example, if you agree to terms that obligate you 536 | to collect a royalty for further conveying from those to whom you convey 537 | the Program, the only way you could satisfy both those terms and this 538 | License would be to refrain entirely from conveying the Program. 539 | 540 | 13. Remote Network Interaction; Use with the GNU General Public License. 541 | 542 | Notwithstanding any other provision of this License, if you modify the 543 | Program, your modified version must prominently offer all users 544 | interacting with it remotely through a computer network (if your version 545 | supports such interaction) an opportunity to receive the Corresponding 546 | Source of your version by providing access to the Corresponding Source 547 | from a network server at no charge, through some standard or customary 548 | means of facilitating copying of software. This Corresponding Source 549 | shall include the Corresponding Source for any work covered by version 3 550 | of the GNU General Public License that is incorporated pursuant to the 551 | following paragraph. 552 | 553 | Notwithstanding any other provision of this License, you have 554 | permission to link or combine any covered work with a work licensed 555 | under version 3 of the GNU General Public License into a single 556 | combined work, and to convey the resulting work. The terms of this 557 | License will continue to apply to the part which is the covered work, 558 | but the work with which it is combined will remain governed by version 559 | 3 of the GNU General Public License. 560 | 561 | 14. Revised Versions of this License. 562 | 563 | The Free Software Foundation may publish revised and/or new versions of 564 | the GNU Affero General Public License from time to time. Such new versions 565 | will be similar in spirit to the present version, but may differ in detail to 566 | address new problems or concerns. 567 | 568 | Each version is given a distinguishing version number. If the 569 | Program specifies that a certain numbered version of the GNU Affero General 570 | Public License "or any later version" applies to it, you have the 571 | option of following the terms and conditions either of that numbered 572 | version or of any later version published by the Free Software 573 | Foundation. If the Program does not specify a version number of the 574 | GNU Affero General Public License, you may choose any version ever published 575 | by the Free Software Foundation. 576 | 577 | If the Program specifies that a proxy can decide which future 578 | versions of the GNU Affero General Public License can be used, that proxy's 579 | public statement of acceptance of a version permanently authorizes you 580 | to choose that version for the Program. 581 | 582 | Later license versions may give you additional or different 583 | permissions. However, no additional obligations are imposed on any 584 | author or copyright holder as a result of your choosing to follow a 585 | later version. 586 | 587 | 15. Disclaimer of Warranty. 588 | 589 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 590 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 591 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 592 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 593 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 594 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 595 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 596 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 597 | 598 | 16. Limitation of Liability. 599 | 600 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 601 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 602 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 603 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 604 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 605 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 606 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 607 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 608 | SUCH DAMAGES. 609 | 610 | 17. Interpretation of Sections 15 and 16. 611 | 612 | If the disclaimer of warranty and limitation of liability provided 613 | above cannot be given local legal effect according to their terms, 614 | reviewing courts shall apply local law that most closely approximates 615 | an absolute waiver of all civil liability in connection with the 616 | Program, unless a warranty or assumption of liability accompanies a 617 | copy of the Program in return for a fee. 618 | 619 | END OF TERMS AND CONDITIONS 620 | 621 | How to Apply These Terms to Your New Programs 622 | 623 | If you develop a new program, and you want it to be of the greatest 624 | possible use to the public, the best way to achieve this is to make it 625 | free software which everyone can redistribute and change under these terms. 626 | 627 | To do so, attach the following notices to the program. It is safest 628 | to attach them to the start of each source file to most effectively 629 | state the exclusion of warranty; and each file should have at least 630 | the "copyright" line and a pointer to where the full notice is found. 631 | 632 | A edlang programming language compiler. 633 | Copyright (C) 2023 Edgar Luque 634 | 635 | This program is free software: you can redistribute it and/or modify 636 | it under the terms of the GNU Affero General Public License as published 637 | by the Free Software Foundation, either version 3 of the License, or 638 | (at your option) any later version. 639 | 640 | This program is distributed in the hope that it will be useful, 641 | but WITHOUT ANY WARRANTY; without even the implied warranty of 642 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 643 | GNU Affero General Public License for more details. 644 | 645 | You should have received a copy of the GNU Affero General Public License 646 | along with this program. If not, see . 647 | 648 | Also add information on how to contact you by electronic and paper mail. 649 | 650 | If your software can interact with users remotely through a computer 651 | network, you should also make sure that it provides a way for users to 652 | get its source. For example, if your program is a web application, its 653 | interface could display a "Source" link that leads users to an archive 654 | of the code. There are many ways you could offer source, and different 655 | solutions will be better for different programs; see section 13 for the 656 | specific requirements. 657 | 658 | You should also get your employer (if you work as a programmer) or school, 659 | if any, to sign a "copyright disclaimer" for the program, if necessary. 660 | For more information on this, and how to apply and follow the GNU AGPL, see 661 | . 662 | --------------------------------------------------------------------------------