diff --git a/.github/templates/Cargo.toml b/.github/templates/Cargo.toml index 554ec945..463911b3 100644 --- a/.github/templates/Cargo.toml +++ b/.github/templates/Cargo.toml @@ -20,6 +20,11 @@ bincode = "*" colored = "2.0.0" paste = "1.0.5" rustyline = "9.0.0" +nom = "7.0.0" +nom_locate = "4.0.0" + +[build-dependencies] +walkdir = "2" [lib] name = "rock" diff --git a/.github/templates/README.md b/.github/templates/README.md index 15cfa52f..6b1a963d 100644 --- a/.github/templates/README.md +++ b/.github/templates/README.md @@ -2,9 +2,12 @@ [![Rust](https://github.com/Champii/Rock/actions/workflows/rust.yml/badge.svg?branch={branch})](https://github.com/Champii/Rock/actions/workflows/rust.yml) -Little toy language made with Rust and LLVM. -Aim to follow the Rust model with enforced safeness with a borrow checker and native performances thanks to LLVM. -It's highly inspired from Livescript, and will borrow (pun intended) some features and syntaxes from Crystal, from functional languages like Haskell, or even from Rust itself. +Little language made with Rust and LLVM. + +Aim to follow the enforced safeness of the Rust model with a borrow checker (Soon™) and achieve high native performances thanks to LLVM. +Rock is highly inspired from Livescript and Rust, and will also borrow (pun intended) some features from Crystal, from functional languages like Haskell, and even from Rust itself. + +No to be taken seriously (yet) # VTable - [Features]( #features ) @@ -13,9 +16,11 @@ It's highly inspired from Livescript, and will borrow (pun intended) some featur - [With cargo from Git]( #with-cargo-from-git ) - [From sources]( #from-sources ) - [Quickstart]( #quickstart ) - - [Basic setup]( #basic-setup ) - - [REPL]( #repl ) - [Showcases]( #showcases ) + - [Polymorphic function]( #polymorphic-function ) + - [Custom infix operator]( #custom-infix-operator ) + - [Trait definition]( #trait-definition ) +- [REPL]( #repl ) - [Development notes]( #development-notes ) ## Features @@ -24,7 +29,7 @@ It's highly inspired from Livescript, and will borrow (pun intended) some featur - Type inference - Custom operators - Typeclass (Traits) -- Parametric Polymorphism by default +- Polymorphism by default - Compile to LLVM IR - REPL (ALPHA) @@ -55,7 +60,7 @@ You will need `llvm-12.0.1` and `clang-12.0.1` somewhere in your $PATH #### With cargo from git ``` sh -cargo install --git https://github.com/Champii/Rock +cargo install --git https://github.com/Champii/Rock --locked rock -V ``` @@ -69,8 +74,6 @@ cargo run -- -V ## Quickstart -### Basic setup - Lets create a new project folder to compute some factorials ``` sh @@ -79,14 +82,14 @@ mkdir -P factorial/src && cd factorial Add some files like this: -- Copy the std lib files from [std](https://github.com/Champii/Rock/blob/master/std/src) into `./src/` +- Copy all the stdlib files from [std/src/\*.rk](https://github.com/Champii/Rock/blob/master/std/src) into `factorial/src/` -- Create a `./src/main.rk` file: +- Create a `factorial/src/main.rk` file: ```haskell mod lib -use lib::prelude::* +use lib::prelude::(*) fact a = if a <= 1 @@ -110,45 +113,17 @@ Should output Take a look at `rock --help` for a quick tour of its flags and arguments -### REPL - -You can start a REPL session with - -``` sh -rock -r -# OR -rock --repl -``` - -``` sh -Rock: {version} ----- - -Type ':?' for help - -> add a b = a + b -> let x = 30 -30 -> let y = 12 -12 -> add x, y -42 -> :t add -add: (Int64 -> Int64 -> Int64) -> _ -``` - -Only supports basic expressions for now. +Note that you currently must be at the project root to run the compiler. (i.e. inside the `./factorial/` folder) ## Showcases -### Polymophic function +### Polymorphic function ``` haskell mod lib -use lib::prelude::* +use lib::prelude::(*) id a = a @@ -175,7 +150,7 @@ Test ``` haskell mod lib -use lib::prelude::* +use lib::prelude::(*) infix |> 1 |> x f = f x @@ -198,7 +173,7 @@ This `trait ToString` is redondant with the `trait Show` implemented in the lib, ``` haskell mod lib -use lib::prelude::* +use lib::prelude::(*) trait ToString a toString :: a -> String @@ -231,7 +206,7 @@ Prints ``` haskell mod lib -use lib::prelude::* +use lib::prelude::(*) struct Player level :: Int64 @@ -255,8 +230,46 @@ rock run Prints `MyName` +## REPL + +Only supports basic expressions for now. +Very unstable, very work in progress. + +Be warned that for a given session, the whole code is re-executed at each entry. +This includes I/O of all sorts (Looking at you, open/read/write in loops) + +Note that the REPL expects to be run from the project root, and expects some version of the stdlib +to be available in the `./src` folder + +You can start a REPL session with + +``` sh +rock -r +# OR +rock --repl +``` + +``` sh +Rock: {version} +---- + +Type ':?' for help + +> add a b = a + b +> let x = 30 +30 +> let y = 12 +12 +> add x, y +42 +> :t add +add: (Int64 -> Int64 -> Int64) +> _ +``` + ## Development notes This project, its syntax and its APIs are subject to change at any moment. -This is a personal project, so please bear with me -(Differently put: this is a big red hot pile of experimental garbage right now) +This is a personal project, so please bear with me + +Differently put: this is a big red hot pile of experimental garbage right now diff --git a/.github/version b/.github/version index fad30c54..1474d00f 100644 --- a/.github/version +++ b/.github/version @@ -1 +1 @@ -v0.1.7 +v0.2.0 diff --git a/Cargo.lock b/Cargo.lock index 597e6efe..2ff89c3f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -52,6 +52,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bytecount" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72feb31ffc86498dacdbd0fcebb56138e7177a8cc5cea4516031d15ae85a742e" + [[package]] name = "cc" version = "1.0.70" @@ -290,6 +296,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "nibble_vec" version = "0.1.0" @@ -312,6 +324,28 @@ dependencies = [ "memoffset", ] +[[package]] +name = "nom" +version = "7.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109" +dependencies = [ + "memchr", + "minimal-lexical", + "version_check", +] + +[[package]] +name = "nom_locate" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37794436ca3029a3089e0b95d42da1f0b565ad271e4d3bb4bad0c7bb70b10605" +dependencies = [ + "bytecount", + "memchr", + "nom", +] + [[package]] name = "once_cell" version = "1.8.0" @@ -430,7 +464,7 @@ checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" [[package]] name = "rock" -version = "0.1.7" +version = "0.1.8-new-parser" dependencies = [ "bincode", "bitflags", @@ -441,11 +475,14 @@ dependencies = [ "inkwell", "lazy_static", "log", + "nom", + "nom_locate", "paste", "regex", "rustyline", "serde", "serde_derive", + "walkdir", ] [[package]] @@ -472,6 +509,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "scopeguard" version = "1.1.0" @@ -533,9 +579,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "syn" -version = "1.0.77" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5239bc68e0fef57495900cfea4e8dc75596d9a319d7e16b1e0a440d24e6fe0a0" +checksum = "a4eac2e6c19f5c3abc0c229bea31ff0b9b091c7b14990e8924b92902a303a0c0" dependencies = [ "proc-macro2", "quote", @@ -596,6 +642,23 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" +[[package]] +name = "version_check" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" + +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + [[package]] name = "wasi" version = "0.10.2+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index 3493d43d..df27d6b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rock" -version = "0.1.7" +version = "0.2.0" authors = ["champii "] edition = "2018" @@ -20,6 +20,11 @@ bincode = "*" colored = "2.0.0" paste = "1.0.5" rustyline = "9.0.0" +nom = "7.0.0" +nom_locate = "4.0.0" + +[build-dependencies] +walkdir = "2" [lib] name = "rock" diff --git a/README.md b/README.md index 7143147b..78e94083 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,13 @@ -# Rock v0.1.7 +# Rock v0.2.0 [![Rust](https://github.com/Champii/Rock/actions/workflows/rust.yml/badge.svg?branch=master)](https://github.com/Champii/Rock/actions/workflows/rust.yml) -Little toy language made with Rust and LLVM. -Aim to follow the Rust model with enforced safeness with a borrow checker and native performances thanks to LLVM. -It's highly inspired from Livescript, and will borrow (pun intended) some features and syntaxes from Crystal, from functional languages like Haskell, or even from Rust itself. +Little language made with Rust and LLVM. + +Aim to follow the enforced safeness of the Rust model with a borrow checker (Soon™) and achieve high native performances thanks to LLVM. +Rock is highly inspired from Livescript and Rust, and will also borrow (pun intended) some features from Crystal, from functional languages like Haskell, and even from Rust itself. + +No to be taken seriously (yet) # VTable - [Features]( #features ) @@ -13,9 +16,11 @@ It's highly inspired from Livescript, and will borrow (pun intended) some featur - [With cargo from Git]( #with-cargo-from-git ) - [From sources]( #from-sources ) - [Quickstart]( #quickstart ) - - [Basic setup]( #basic-setup ) - - [REPL]( #repl ) - [Showcases]( #showcases ) + - [Polymorphic function]( #polymorphic-function ) + - [Custom infix operator]( #custom-infix-operator ) + - [Trait definition]( #trait-definition ) +- [REPL]( #repl ) - [Development notes]( #development-notes ) ## Features @@ -24,7 +29,7 @@ It's highly inspired from Livescript, and will borrow (pun intended) some featur - Type inference - Custom operators - Typeclass (Traits) -- Parametric Polymorphism by default +- Polymorphism by default - Compile to LLVM IR - REPL (ALPHA) @@ -40,10 +45,10 @@ You will need `clang` somewhere in your $PATH Linux x86_64 only -[Rock v0.1.7](https://github.com/Champii/Rock/releases/download/v0.1.7/rock) (Tested on arch, btw) +[Rock v0.2.0](https://github.com/Champii/Rock/releases/download/v0.2.0/rock) (Tested on arch, btw) ``` sh -wget https://github.com/Champii/Rock/releases/download/v0.1.7/rock +wget https://github.com/Champii/Rock/releases/download/v0.2.0/rock chmod +x rock ./rock -V ``` @@ -55,7 +60,7 @@ You will need `llvm-12.0.1` and `clang-12.0.1` somewhere in your $PATH #### With cargo from git ``` sh -cargo install --git https://github.com/Champii/Rock +cargo install --git https://github.com/Champii/Rock --locked rock -V ``` @@ -69,8 +74,6 @@ cargo run -- -V ## Quickstart -### Basic setup - Lets create a new project folder to compute some factorials ``` sh @@ -79,14 +82,14 @@ mkdir -P factorial/src && cd factorial Add some files like this: -- Copy the std lib files from [std](https://github.com/Champii/Rock/blob/master/std/src) into `./src/` +- Copy all the stdlib files from [std/src/\*.rk](https://github.com/Champii/Rock/blob/master/std/src) into `factorial/src/` -- Create a `./src/main.rk` file: +- Create a `factorial/src/main.rk` file: ```haskell mod lib -use lib::prelude::* +use lib::prelude::(*) fact a = if a <= 1 @@ -110,45 +113,17 @@ Should output Take a look at `rock --help` for a quick tour of its flags and arguments -### REPL - -You can start a REPL session with - -``` sh -rock -r -# OR -rock --repl -``` - -``` sh -Rock: v0.1.7 ----- - -Type ':?' for help - -> add a b = a + b -> let x = 30 -30 -> let y = 12 -12 -> add x, y -42 -> :t add -add: (Int64 -> Int64 -> Int64) -> _ -``` - -Only supports basic expressions for now. +Note that you currently must be at the project root to run the compiler. (i.e. inside the `./factorial/` folder) ## Showcases -### Polymophic function +### Polymorphic function ``` haskell mod lib -use lib::prelude::* +use lib::prelude::(*) id a = a @@ -175,7 +150,7 @@ Test ``` haskell mod lib -use lib::prelude::* +use lib::prelude::(*) infix |> 1 |> x f = f x @@ -198,7 +173,7 @@ This `trait ToString` is redondant with the `trait Show` implemented in the lib, ``` haskell mod lib -use lib::prelude::* +use lib::prelude::(*) trait ToString a toString :: a -> String @@ -231,7 +206,7 @@ Prints ``` haskell mod lib -use lib::prelude::* +use lib::prelude::(*) struct Player level :: Int64 @@ -255,8 +230,46 @@ rock run Prints `MyName` +## REPL + +Only supports basic expressions for now. +Very unstable, very work in progress. + +Be warned that for a given session, the whole code is re-executed at each entry. +This includes I/O of all sorts (Looking at you, open/read/write in loops) + +Note that the REPL expects to be run from the project root, and expects some version of the stdlib +to be available in the `./src` folder + +You can start a REPL session with + +``` sh +rock -r +# OR +rock --repl +``` + +``` sh +Rock: v0.2.0 +---- + +Type ':?' for help + +> add a b = a + b +> let x = 30 +30 +> let y = 12 +12 +> add x, y +42 +> :t add +add: (Int64 -> Int64 -> Int64) +> _ +``` + ## Development notes This project, its syntax and its APIs are subject to change at any moment. -This is a personal project, so please bear with me -(Differently put: this is a big red hot pile of experimental garbage right now) +This is a personal project, so please bear with me + +Differently put: this is a big red hot pile of experimental garbage right now diff --git a/build.rs b/build.rs index 7c53cb1d..11ca5d93 100644 --- a/build.rs +++ b/build.rs @@ -3,6 +3,9 @@ use std::fs::File; use std::io; use std::io::Write; use std::path::Path; +use std::path::PathBuf; + +use walkdir::WalkDir; fn visit_dirs(dir: &Path) -> io::Result> { let mut res = vec![]; @@ -25,8 +28,26 @@ fn visit_dirs(dir: &Path) -> io::Result> { Ok(res) } +fn schedule_rerun_if_folder_changed(path: &Path) { + for entry in WalkDir::new(path) { + let entry = entry.unwrap(); + + if entry.path() == path { + continue; + } + + if entry.path().is_dir() { + schedule_rerun_if_folder_changed(&entry.path()); + } else { + println!("cargo:rerun-if-changed={}", entry.path().to_str().unwrap()); + } + } +} + // build script's entry point fn main() { + schedule_rerun_if_folder_changed(&PathBuf::from("src/lib/testcases/")); + let out_dir = "src/lib"; let destination = Path::new(&out_dir).join("tests.rs"); let mut output_file = File::create(&destination).unwrap(); @@ -42,15 +63,12 @@ fn main() { fn write_test(output_file: &mut File, path: &str) { let path = path.replace("src/lib/", ""); - let name = path.replace("./", ""); - let name = name.replace("/", "_"); - let name = name.replace(".rk", ""); - let test_name = name; + let name = path.replace("./", "").replace("/", "_").replace(".rk", ""); write!( output_file, include_str!("src/lib/testcases/test_template"), - name = test_name, + name = name, path = path ) .unwrap(); @@ -62,19 +80,19 @@ fn write_header(output_file: &mut File) { r##"use std::path::PathBuf; #[allow(dead_code)] -fn run(path: &str, input: &str, expected_ret: &str, expected_output: &str) {{ - let mut config = super::Config::default(); + fn run(path: &str, input: &str, expected_ret: &str, expected_output: &str) {{ + let mut config = super::Config::default(); - config.project_config.entry_point = PathBuf::from(path); + config.project_config.entry_point = PathBuf::from(path); - let expected_ret = expected_ret.parse::().unwrap(); + let expected_ret = expected_ret.parse::().unwrap(); - let (ret_code, stdout) = super::test::run(path, input.to_string(), config); + let (ret_code, stdout) = super::test::run(path, input.to_string(), config); - assert_eq!(expected_ret, ret_code); - assert_eq!(expected_output, stdout); -}} -"## + assert_eq!(expected_ret, ret_code); + assert_eq!(expected_output, stdout); + }} + "## ) .unwrap(); } diff --git a/src/bin/main.rs b/src/bin/main.rs index b6d5b808..31a6c659 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -66,7 +66,7 @@ fn run(config: Config) { .output() .expect("failed to execute binary"); - print!("{}", String::from_utf8(cmd.stdout).unwrap()); + print!("{}", unsafe { String::from_utf8_unchecked(cmd.stdout) }); match cmd.status.code() { Some(code) => { diff --git a/src/lib/ast/ast_print.rs b/src/lib/ast/ast_print.rs index adc8fae3..dbdf59ca 100644 --- a/src/lib/ast/ast_print.rs +++ b/src/lib/ast/ast_print.rs @@ -1,7 +1,7 @@ use std::fmt::Debug; +use crate::ast::tree::*; use crate::ast::visit::*; -use crate::ast::*; use crate::helpers::*; use crate::ty::*; use paste::paste; @@ -83,7 +83,7 @@ macro_rules! impl_visitor_trait { impl_visitor_trait!( Root - Mod + // Mod TopLevel StructDecl Use @@ -92,9 +92,12 @@ impl_visitor_trait!( Impl FunctionDecl Identifier - ArgumentDecl + // ArgumentDecl Body Statement + For + While + ForIn Expression If Else diff --git a/src/lib/ast/identity.rs b/src/lib/ast/identity.rs deleted file mode 100644 index 5defa86e..00000000 --- a/src/lib/ast/identity.rs +++ /dev/null @@ -1,39 +0,0 @@ -use std::sync::atomic::{AtomicU64, Ordering}; - -use crate::parser::{Span, TokenId}; - -use crate::ast::NodeId; - -static GLOBAL_NEXT_NODE_ID: AtomicU64 = AtomicU64::new(0); - -#[derive(Debug, Clone, Hash, PartialEq, Eq)] -pub struct Identity { - pub node_id: NodeId, - pub token_id: TokenId, - pub span: Span, - pub scope_depth: u8, -} - -impl Identity { - pub fn new(token_id: TokenId, span: Span) -> Self { - Self { - span, - node_id: Self::next_node_id(), - token_id, - scope_depth: 0, - } - } - - pub fn new_placeholder() -> Self { - Self { - span: Span::new_placeholder(), - node_id: 0, - token_id: 0, - scope_depth: 0, - } - } - - pub fn next_node_id() -> NodeId { - GLOBAL_NEXT_NODE_ID.fetch_add(1, Ordering::SeqCst) - } -} diff --git a/src/lib/ast/mod.rs b/src/lib/ast/mod.rs index 5a533149..f040355b 100644 --- a/src/lib/ast/mod.rs +++ b/src/lib/ast/mod.rs @@ -1,12 +1,10 @@ #[macro_use] pub mod ast_print; -mod identity; -pub mod span_collector; -mod tree; +pub mod return_placement; +pub mod tree; pub mod visit; -pub use identity::*; pub use tree::*; pub use visit::*; diff --git a/src/lib/ast/return_placement.rs b/src/lib/ast/return_placement.rs new file mode 100644 index 00000000..4d879355 --- /dev/null +++ b/src/lib/ast/return_placement.rs @@ -0,0 +1,77 @@ +use crate::ast::tree::*; + +pub struct ReturnInserter<'a> { + pub body: &'a Body, +} + +impl<'a> ReturnInserter<'a> { + pub fn run(&mut self) -> Body { + let mut body = self.body.clone(); + + self.visit_body(&mut body); + + body.clone() + } + + fn visit_body(&mut self, body: &mut Body) { + // let mut is_assign = None; + + if let Some(stmt) = body.stmts.iter_mut().last() { + // if let StatementKind::Assign(ref mut a) = *stmt.kind.clone() { + // is_assign = Some(a.clone()); + // } + + self.visit_statement(stmt); + } + + // if let Some(a) = is_assign { + // body.stmts.push(Statement { + // kind: Box::new(StatementKind::Expression(Box::new(Expression { + // kind: ExpressionKind::Return(Box::new(a.name.as_expression())), + // }))), + // }); + // } + } + + fn visit_statement(&mut self, stmt: &mut Statement) { + let mut is_assign = None; + + match *stmt { + Statement::Expression(ref mut e) => { + *e = Box::new(Expression::Return(e.clone())); + } + Statement::If(ref mut i) => { + self.visit_if(i); + } + Statement::Assign(ref mut a) => { + is_assign = Some(a.value.clone()); + } + Statement::For(ref mut _fa) => { + unimplemented!("Cannot have loop in return position"); + } + } + + // FIXME: do this only if a.name is Identifier + // or else return the ident + if let Some(value) = is_assign { + *stmt = Statement::Expression(Box::new(Expression::Return(Box::new(value)))); + } + } + + fn visit_if(&mut self, r#if: &mut If) { + self.visit_body(&mut r#if.body); + + if let Some(ref mut r#else) = r#if.else_.as_mut() { + self.visit_else(r#else); + } else { + unimplemented!("Else is not found"); + } + } + + fn visit_else(&mut self, r#else: &mut Else) { + match r#else { + Else::If(i) => self.visit_if(i), + Else::Body(b) => self.visit_body(b), + } + } +} diff --git a/src/lib/ast/span_collector.rs b/src/lib/ast/span_collector.rs deleted file mode 100644 index 627846b8..00000000 --- a/src/lib/ast/span_collector.rs +++ /dev/null @@ -1,65 +0,0 @@ -use paste::paste; -use std::collections::HashMap; - -use crate::ast::visit::*; -use crate::parser::Span; -use crate::{ast::visit::Visitor, ast::*}; - -#[derive(Debug, Default)] -pub struct SpanCollector { - list: HashMap, -} - -impl SpanCollector { - pub fn new() -> Self { - Self { - ..Default::default() - } - } - - pub fn take_list(self) -> HashMap { - self.list - } - - pub fn insert(&mut self, ident: &Identity) { - self.list.insert(ident.node_id, ident.span.clone()); - } -} - -macro_rules! generate_span_collector { - ($($expr:ty)+) => { - impl<'a> Visitor<'a> for SpanCollector { - paste! { - $( - fn [](&mut self, node: &'a $expr) { - self.insert(&node.identity); - - [](self, node); - } - )+ - } - } - }; -} - -generate_span_collector!( - Mod - TopLevel - Prototype - Use - FunctionDecl - Identifier - ArgumentDecl - If - PrimaryExpr - Literal - NativeOperator -); - -pub fn collect_spans(root: &Root) -> HashMap { - let mut span_collector = SpanCollector::new(); - - span_collector.visit_root(root); - - span_collector.take_list() -} diff --git a/src/lib/ast/tree.rs b/src/lib/ast/tree.rs index 466c80f4..66bd6a7c 100644 --- a/src/lib/ast/tree.rs +++ b/src/lib/ast/tree.rs @@ -1,13 +1,15 @@ use std::collections::HashMap; use crate::{ - ast::{identity::Identity, NodeId}, + ast::NodeId, helpers::*, - parser::{Span, Token}, + parser::span2::Span, resolver::ResolutionMap, ty::{FuncType, Type}, }; +use super::{ast_print::AstPrintContext, visit::Visitor}; + #[derive(Debug, Clone)] pub struct Root { pub r#mod: Mod, @@ -17,23 +19,35 @@ pub struct Root { pub spans: HashMap, } +impl Root { + pub fn new(r#mod: Mod) -> Self { + Self { + r#mod, + resolutions: ResolutionMap::default(), + operators_list: HashMap::new(), + unused: vec![], + spans: HashMap::new(), + } + } + + pub fn print(&self) { + AstPrintContext::new().visit_root(self); + } +} + #[derive(Debug, Clone)] pub struct Mod { - pub tokens: Vec, pub top_levels: Vec, - pub identity: Identity, } -impl Mod {} - -#[derive(Debug, Clone)] -pub struct TopLevel { - pub kind: TopLevelKind, - pub identity: Identity, +impl Mod { + pub fn new(top_levels: Vec) -> Self { + Self { top_levels } + } } #[derive(Debug, Clone)] -pub enum TopLevelKind { +pub enum TopLevel { Prototype(Prototype), Function(FunctionDecl), Trait(Trait), @@ -41,23 +55,67 @@ pub enum TopLevelKind { Struct(StructDecl), Mod(Identifier, Mod), Use(Use), - Infix(Identifier, u8), + Infix(Operator, u8), +} + +impl TopLevel { + pub fn new_function(f: FunctionDecl) -> Self { + Self::Function(f) + } + + pub fn new_infix(op: Operator, pred: u8) -> Self { + Self::Infix(op, pred) + } + + pub fn new_prototype(proto: Prototype) -> Self { + Self::Prototype(proto) + } + + pub fn new_use(u: Use) -> Self { + Self::Use(u) + } + + pub fn new_struct(s: StructDecl) -> Self { + Self::Struct(s) + } + + pub fn new_trait(t: Trait) -> Self { + Self::Trait(t) + } + + pub fn new_impl(t: Impl) -> Self { + Self::Impl(t) + } + + pub fn new_mod(ident: Identifier, mod_: Mod) -> Self { + Self::Mod(ident, mod_) + } } #[derive(Debug, Clone)] pub struct StructDecl { - pub identity: Identity, - pub name: Type, + pub name: Identifier, pub defs: Vec, } +impl StructDecl { + pub fn new(name: Identifier, defs: Vec) -> Self { + Self { name, defs } + } +} + #[derive(Debug, Clone)] pub struct StructCtor { - pub identity: Identity, - pub name: Type, + pub name: Identifier, pub defs: HashMap, } +impl StructCtor { + pub fn new(name: Identifier, defs: HashMap) -> Self { + Self { name, defs } + } +} + #[derive(Debug, Clone)] pub struct Trait { pub name: Type, @@ -65,6 +123,12 @@ pub struct Trait { pub defs: Vec, } +impl Trait { + pub fn new(name: Type, types: Vec, defs: Vec) -> Self { + Self { name, types, defs } + } +} + #[derive(Debug, Clone)] pub struct Impl { pub name: Type, @@ -72,11 +136,17 @@ pub struct Impl { pub defs: Vec, } +impl Impl { + pub fn new(name: Type, types: Vec, defs: Vec) -> Self { + Self { name, types, defs } + } +} + #[derive(Debug, Clone)] pub struct Prototype { pub name: Identifier, pub signature: FuncType, - pub identity: Identity, + pub node_id: NodeId, } impl Prototype { @@ -88,16 +158,21 @@ impl Prototype { #[derive(Debug, Clone)] pub struct Use { pub path: IdentifierPath, - pub identity: Identity, + pub node_id: NodeId, +} + +impl Use { + pub fn new(path: IdentifierPath, node_id: NodeId) -> Self { + Self { path, node_id } + } } #[derive(Debug, Clone)] pub struct FunctionDecl { pub name: Identifier, - // pub mangled_name: Option, - pub arguments: Vec, + pub arguments: Vec, pub body: Body, - pub identity: Identity, + pub node_id: NodeId, pub signature: FuncType, } @@ -115,11 +190,15 @@ pub struct IdentifierPath { } impl IdentifierPath { + pub fn new(path: Vec) -> Self { + Self { path } + } + pub fn new_root() -> Self { Self { path: vec![Identifier { name: "root".to_string(), - identity: Identity::new_placeholder(), + node_id: 42, // FIXME: should have a valid node_id ? }], } } @@ -194,7 +273,13 @@ impl IdentifierPath { #[derive(Debug, Clone, Eq)] pub struct Identifier { pub name: String, - pub identity: Identity, + pub node_id: NodeId, +} + +impl Identifier { + pub fn new(name: String, node_id: NodeId) -> Self { + Self { name, node_id } + } } impl PartialEq for Identifier { @@ -211,6 +296,7 @@ impl std::hash::Hash for Identifier { impl std::ops::Deref for Identifier { type Target = String; + fn deref(&self) -> &Self::Target { &self.name } @@ -218,47 +304,90 @@ impl std::ops::Deref for Identifier { generate_has_name!(Identifier); -pub type ArgumentsDecl = Vec; - -#[derive(Debug, Clone)] -pub struct ArgumentDecl { - pub name: String, - pub identity: Identity, -} - #[derive(Debug, Clone)] pub struct Body { pub stmts: Vec, } -#[derive(Debug, Clone)] -pub struct Statement { - pub kind: Box, +impl Body { + pub fn new(stmts: Vec) -> Self { + Self { stmts } + } } #[derive(Debug, Clone)] -pub enum StatementKind { +pub enum Statement { Expression(Box), Assign(Box), If(Box), + For(For), +} + +impl Statement { + pub fn new_expression(expr: Expression) -> Self { + Self::Expression(Box::new(expr)) + } + + pub fn new_if(if_: If) -> Self { + Self::If(Box::new(if_)) + } + + pub fn new_for(for_: For) -> Self { + Self::For(for_) + } + + pub fn new_assign(assign: Assign) -> Self { + Self::Assign(Box::new(assign)) + } +} + +#[derive(Debug, Clone)] +pub enum For { + In(ForIn), + While(While), +} + +#[derive(Debug, Clone)] +pub struct While { + pub predicat: Expression, + pub body: Body, +} + +impl While { + pub fn new(predicat: Expression, body: Body) -> Self { + Self { predicat, body } + } +} + +#[derive(Debug, Clone)] +pub struct ForIn { + pub value: Identifier, + pub expr: Expression, + pub body: Body, +} + +impl ForIn { + pub fn new(value: Identifier, expr: Expression, body: Body) -> Self { + Self { value, expr, body } + } } #[derive(Debug, Clone)] pub enum AssignLeftSide { - Identifier(Identifier), + Identifier(Expression), Indice(Expression), Dot(Expression), } -// impl AssignLeftSide { -// pub fn get_node_id(&self) -> NodeId { -// use AssignLeftSide::*; -// match self { -// Identifier(id) => id.identity.node_id, -// Indice(expr) => expr., -// } -// } -// } +impl AssignLeftSide { + pub fn as_expression(&self) -> Expression { + match self { + AssignLeftSide::Identifier(i) => i.clone(), + AssignLeftSide::Indice(i) => i.clone(), + AssignLeftSide::Dot(d) => d.clone(), + } + } +} #[derive(Debug, Clone)] pub struct Assign { @@ -267,84 +396,121 @@ pub struct Assign { pub is_let: bool, } +impl Assign { + pub fn new(name: AssignLeftSide, value: Expression, is_let: bool) -> Self { + Self { + name, + value, + is_let, + } + } +} + #[derive(Debug, Clone)] pub struct If { - pub identity: Identity, + pub node_id: NodeId, pub predicat: Expression, pub body: Body, pub else_: Option>, } +impl If { + pub fn new( + node_id: NodeId, + predicat: Expression, + body: Body, + else_: Option>, + ) -> Self { + Self { + node_id, + predicat, + body, + else_, + } + } +} + #[derive(Debug, Clone)] pub enum Else { If(If), Body(Body), } -#[derive(Debug, Clone)] -pub struct Expression { - pub kind: ExpressionKind, -} - impl Expression { #[allow(dead_code)] pub fn is_literal(&self) -> bool { - match &self.kind { - ExpressionKind::UnaryExpr(unary) => unary.is_literal(), + match &self { + Expression::UnaryExpr(unary) => unary.is_literal(), _ => false, } } #[allow(dead_code)] pub fn is_identifier(&self) -> bool { - match &self.kind { - ExpressionKind::UnaryExpr(unary) => unary.is_identifier(), + match &self { + Expression::UnaryExpr(unary) => unary.is_identifier(), _ => false, } } #[allow(dead_code)] pub fn is_binop(&self) -> bool { - matches!(&self.kind, ExpressionKind::BinopExpr(_, _, _)) + matches!(&self, Expression::BinopExpr(_, _, _)) } #[allow(dead_code)] pub fn is_indice(&self) -> bool { - match &self.kind { - ExpressionKind::UnaryExpr(unary) => unary.is_indice(), + match &self { + Expression::UnaryExpr(unary) => unary.is_indice(), _ => false, } } #[allow(dead_code)] - pub fn from_unary(unary: &UnaryExpr) -> Expression { - Expression { - kind: ExpressionKind::UnaryExpr(unary.clone()), + pub fn is_dot(&self) -> bool { + match &self { + Expression::UnaryExpr(unary) => unary.is_dot(), + _ => false, } } #[allow(dead_code)] - pub fn create_2_args_func_call(op: Operand, arg1: UnaryExpr, arg2: UnaryExpr) -> Expression { - Expression { - kind: ExpressionKind::UnaryExpr(UnaryExpr::PrimaryExpr(PrimaryExpr { - identity: Identity::new_placeholder(), - op, - secondaries: Some(vec![SecondaryExpr::Arguments(vec![ - Argument { arg: arg1 }, - Argument { arg: arg2 }, - ])]), - })), + pub fn new_unary(unary: UnaryExpr) -> Expression { + Expression::UnaryExpr(unary) + } + + #[allow(dead_code)] + pub fn new_binop(unary: UnaryExpr, operator: Operator, expr: Expression) -> Expression { + Expression::BinopExpr(unary, operator, Box::new(expr)) + } + + pub fn new_struct_ctor(ctor: StructCtor) -> Expression { + Expression::StructCtor(ctor) + } + + pub fn new_native_operator( + operator: NativeOperator, + id1: Identifier, + id2: Identifier, + ) -> Expression { + Expression::NativeOperation(operator, id1, id2) + } + + pub fn as_identifier(&self) -> Option<&Identifier> { + match self { + Expression::UnaryExpr(unary) => unary.as_identifier(), + _ => None, } } } #[derive(Debug, Clone)] -pub enum ExpressionKind { +pub enum Expression { BinopExpr(UnaryExpr, Operator, Box), UnaryExpr(UnaryExpr), NativeOperation(NativeOperator, Identifier, Identifier), StructCtor(StructCtor), - Return(Box), + Return(Box), // NOTE: Shouldn't that be a statement? } #[derive(Debug, Clone)] @@ -356,14 +522,14 @@ pub enum UnaryExpr { impl UnaryExpr { pub fn is_literal(&self) -> bool { match self { - UnaryExpr::PrimaryExpr(p) => matches!(&p.op.kind, OperandKind::Literal(_)), + UnaryExpr::PrimaryExpr(p) => matches!(&p.op, Operand::Literal(_)), _ => false, } } pub fn is_identifier(&self) -> bool { match self { - UnaryExpr::PrimaryExpr(p) => matches!(&p.op.kind, OperandKind::Identifier(_)), + UnaryExpr::PrimaryExpr(p) => matches!(&p.op, Operand::Identifier(_)), _ => false, } } @@ -375,9 +541,16 @@ impl UnaryExpr { } } + pub fn is_dot(&self) -> bool { + match self { + UnaryExpr::UnaryExpr(_, unary) => unary.is_dot(), + UnaryExpr::PrimaryExpr(prim) => prim.is_dot(), + } + } + pub fn create_2_args_func_call(op: Operand, arg1: UnaryExpr, arg2: UnaryExpr) -> UnaryExpr { UnaryExpr::PrimaryExpr(PrimaryExpr { - identity: Identity::new_placeholder(), + node_id: u64::MAX, op, secondaries: Some(vec![SecondaryExpr::Arguments(vec![ Argument { arg: arg1 }, @@ -385,14 +558,25 @@ impl UnaryExpr { ])]), }) } + + pub fn new_primary(primary: PrimaryExpr) -> UnaryExpr { + UnaryExpr::PrimaryExpr(primary) + } + + pub fn as_identifier(&self) -> Option<&Identifier> { + match self { + UnaryExpr::PrimaryExpr(primary) => primary.as_identifier(), + _ => None, + } + } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct Operator(pub Identifier); #[derive(Debug, Clone)] pub struct PrimaryExpr { - pub identity: Identity, + pub node_id: NodeId, pub op: Operand, pub secondaries: Option>, } @@ -410,44 +594,88 @@ impl PrimaryExpr { false } } + + pub fn is_dot(&self) -> bool { + if let Some(secondaries) = &self.secondaries { + secondaries.iter().any(|secondary| secondary.is_dot()) + } else { + false + } + } + + pub fn new_empty(op: Operand) -> PrimaryExpr { + PrimaryExpr { + op, + node_id: u64::MAX, + secondaries: None, + } + } + + pub fn new(node_id: NodeId, op: Operand, secondaries: Vec) -> PrimaryExpr { + PrimaryExpr { + op, + node_id, + secondaries: if secondaries.len() == 0 { + None + } else { + Some(secondaries) + }, + } + } + + pub fn as_identifier(&self) -> Option<&Identifier> { + match &self.op { + Operand::Identifier(id) => Some(id.path.last().unwrap()), + _ => None, + } + } } #[derive(Debug, Clone)] -pub struct Operand { - pub kind: OperandKind, +pub enum Operand { + Literal(Literal), + Identifier(IdentifierPath), + Expression(Box), // parenthesis } impl Operand { - pub fn from_identifier(id: &Identifier) -> Self { - Self { - kind: OperandKind::Identifier(IdentifierPath { - path: vec![id.clone()], - }), - } + pub fn new_identifier_path(id: IdentifierPath) -> Self { + Self::Identifier(id) + } + + pub fn new_identifier(id: Identifier) -> Self { + Self::Identifier(IdentifierPath { + path: vec![id.clone()], + }) + } + + pub fn new_expression(expr: Expression) -> Self { + Self::Expression(Box::new(expr)) } #[allow(dead_code)] pub fn is_literal(&self) -> bool { - matches!(&self.kind, OperandKind::Literal(_)) + matches!(&self, Operand::Literal(_)) } #[allow(dead_code)] pub fn is_identifier(&self) -> bool { - matches!(&self.kind, OperandKind::Identifier(_)) + matches!(&self, Operand::Identifier(_)) } -} -#[derive(Debug, Clone)] -pub enum OperandKind { - Literal(Literal), - Identifier(IdentifierPath), - Expression(Box), // parenthesis + pub fn new_literal(lit: Literal) -> Operand { + Operand::Literal(lit) + } + + pub fn from_identifier(id: Identifier) -> Operand { + Operand::new_identifier(id) + } } -impl OperandKind { +impl Operand { #[allow(dead_code)] pub fn to_identifier_path(&self) -> IdentifierPath { - if let OperandKind::Identifier(id) = self { + if let Operand::Identifier(id) = self { id.clone() } else { panic!("Not an identifier path") @@ -461,25 +689,78 @@ pub enum SecondaryExpr { Indice(Box), // Boxing here to keep the enum size low Dot(Identifier), } + impl SecondaryExpr { pub fn is_indice(&self) -> bool { matches!(self, SecondaryExpr::Indice(_)) } + + pub fn is_dot(&self) -> bool { + matches!(self, SecondaryExpr::Dot(_)) + } + + pub fn is_arguments(&self) -> bool { + matches!(self, SecondaryExpr::Arguments(_)) + } } #[derive(Debug, Clone)] pub struct Literal { pub kind: LiteralKind, - pub identity: Identity, + pub node_id: NodeId, +} + +impl Literal { + pub fn new_bool(b: bool, node_id: NodeId) -> Self { + Self { + kind: LiteralKind::Bool(b), + node_id, + } + } + + pub fn new_number(num: i64, node_id: NodeId) -> Self { + Self { + kind: LiteralKind::Number(num), + node_id, + } + } + + pub fn new_float(num: f64, node_id: NodeId) -> Self { + Self { + kind: LiteralKind::Float(num), + node_id, + } + } + + pub fn new_array(arr: Array, node_id: NodeId) -> Self { + Self { + kind: LiteralKind::Array(arr), + node_id, + } + } + + pub fn new_string(str: String, node_id: NodeId) -> Self { + Self { + kind: LiteralKind::String(str), + node_id, + } + } + + pub fn as_i64(&self) -> i64 { + match self.kind { + LiteralKind::Number(n) => n, + _ => panic!("Not a Number"), + } + } } #[derive(Debug, Clone)] pub enum LiteralKind { + Bool(bool), Number(i64), Float(f64), - String(String), - Bool(bool), Array(Array), + String(String), } #[derive(Debug, Clone)] @@ -487,6 +768,12 @@ pub struct Array { pub values: Vec, } +impl Array { + pub fn new(values: Vec) -> Self { + Self { values } + } +} + pub type Arguments = Vec; #[derive(Debug, Clone)] @@ -494,13 +781,25 @@ pub struct Argument { pub arg: UnaryExpr, } +impl Argument { + pub fn new(arg: UnaryExpr) -> Self { + Self { arg } + } +} + #[derive(Debug, Clone)] pub struct NativeOperator { pub kind: NativeOperatorKind, - pub identity: Identity, + pub node_id: NodeId, } -#[derive(Debug, Clone)] +impl NativeOperator { + pub fn new(node_id: NodeId, kind: NativeOperatorKind) -> Self { + Self { kind, node_id } + } +} + +#[derive(Debug, Clone, PartialEq)] pub enum NativeOperatorKind { IAdd, ISub, @@ -521,4 +820,33 @@ pub enum NativeOperatorKind { Flt, Fle, BEq, + Len, +} + +impl NativeOperatorKind { + pub fn from_str(s: &str) -> Self { + match s { + "IAdd" => Self::IAdd, + "ISub" => Self::ISub, + "IMul" => Self::IMul, + "IDiv" => Self::IDiv, + "FAdd" => Self::FAdd, + "FSub" => Self::FSub, + "FMul" => Self::FMul, + "FDiv" => Self::FDiv, + "IEq" => Self::IEq, + "Igt" => Self::Igt, + "Ige" => Self::Ige, + "Ilt" => Self::Ilt, + "Ile" => Self::Ile, + "FEq" => Self::FEq, + "Fgt" => Self::Fgt, + "Fge" => Self::Fge, + "Flt" => Self::Flt, + "Fle" => Self::Fle, + "BEq" => Self::BEq, + "Len" => Self::Len, + _ => panic!("Unknown native operator"), + } + } } diff --git a/src/lib/ast/visit.rs b/src/lib/ast/visit.rs index 502a91e2..94a172f8 100644 --- a/src/lib/ast/visit.rs +++ b/src/lib/ast/visit.rs @@ -1,6 +1,6 @@ use paste::paste; -use crate::ast::*; +use crate::ast::tree::*; use crate::ty::*; macro_rules! generate_visitor_trait { @@ -40,9 +40,12 @@ generate_visitor_trait!( StructDecl Identifier IdentifierPath - ArgumentDecl + // ArgumentDecl Body Statement + For + ForIn + While Expression If Else @@ -69,23 +72,23 @@ pub fn walk_mod<'a, V: Visitor<'a>>(visitor: &mut V, _mod: &'a Mod) { } pub fn walk_top_level<'a, V: Visitor<'a>>(visitor: &mut V, top_level: &'a TopLevel) { - match &top_level.kind { - TopLevelKind::Prototype(p) => visitor.visit_prototype(p), - TopLevelKind::Use(u) => visitor.visit_use(u), - TopLevelKind::Trait(t) => visitor.visit_trait(t), - TopLevelKind::Impl(i) => visitor.visit_impl(i), - TopLevelKind::Struct(i) => visitor.visit_struct_decl(i), - TopLevelKind::Mod(name, m) => { + match &top_level { + TopLevel::Prototype(p) => visitor.visit_prototype(p), + TopLevel::Use(u) => visitor.visit_use(u), + TopLevel::Trait(t) => visitor.visit_trait(t), + TopLevel::Impl(i) => visitor.visit_impl(i), + TopLevel::Struct(i) => visitor.visit_struct_decl(i), + TopLevel::Mod(name, m) => { visitor.visit_identifier(name); visitor.visit_mod(m); } - TopLevelKind::Function(f) => visitor.visit_function_decl(f), - TopLevelKind::Infix(ident, _) => visitor.visit_identifier(ident), + TopLevel::Function(f) => visitor.visit_function_decl(f), + TopLevel::Infix(ident, _) => visitor.visit_operator(ident), }; } pub fn walk_struct_decl<'a, V: Visitor<'a>>(visitor: &mut V, s: &'a StructDecl) { - visitor.visit_type(&s.name); + visitor.visit_identifier(&s.name); walk_list!(visitor, visit_prototype, &s.defs); } @@ -119,7 +122,7 @@ pub fn walk_use<'a, V: Visitor<'a>>(visitor: &mut V, r#use: &'a Use) { pub fn walk_function_decl<'a, V: Visitor<'a>>(visitor: &mut V, function_decl: &'a FunctionDecl) { visitor.visit_identifier(&function_decl.name); - walk_list!(visitor, visit_argument_decl, &function_decl.arguments); + walk_list!(visitor, visit_identifier, &function_decl.arguments); visitor.visit_body(&function_decl.body); } @@ -135,25 +138,44 @@ pub fn walk_identifier<'a, V: Visitor<'a>>(visitor: &mut V, identifier: &'a Iden visitor.visit_name(&identifier.name); } -pub fn walk_argument_decl<'a, V: Visitor<'a>>(visitor: &mut V, argument: &'a ArgumentDecl) { +/* pub fn walk_argument_decl<'a, V: Visitor<'a>>(visitor: &mut V, argument: &'a ArgumentDecl) { visitor.visit_name(&argument.name); -} +} */ pub fn walk_body<'a, V: Visitor<'a>>(visitor: &mut V, body: &'a Body) { walk_list!(visitor, visit_statement, &body.stmts); } pub fn walk_statement<'a, V: Visitor<'a>>(visitor: &mut V, statement: &'a Statement) { - match statement.kind.as_ref() { - StatementKind::Expression(expr) => visitor.visit_expression(expr), - StatementKind::Assign(assign) => visitor.visit_assign(assign), - StatementKind::If(expr) => visitor.visit_if(expr), + match &statement { + Statement::Expression(expr) => visitor.visit_expression(expr), + Statement::Assign(assign) => visitor.visit_assign(assign), + Statement::If(expr) => visitor.visit_if(expr), + Statement::For(for_loop) => visitor.visit_for(for_loop), + } +} + +pub fn walk_for<'a, V: Visitor<'a>>(visitor: &mut V, for_loop: &'a For) { + match for_loop { + For::In(for_in) => visitor.visit_for_in(for_in), + For::While(while_loop) => visitor.visit_while(while_loop), } } +pub fn walk_for_in<'a, V: Visitor<'a>>(visitor: &mut V, for_in: &'a ForIn) { + visitor.visit_identifier(&for_in.value); + visitor.visit_expression(&for_in.expr); + visitor.visit_body(&for_in.body); +} + +pub fn walk_while<'a, V: Visitor<'a>>(visitor: &mut V, while_loop: &'a While) { + visitor.visit_expression(&while_loop.predicat); + visitor.visit_body(&while_loop.body); +} + pub fn walk_assign_left_side<'a, V: Visitor<'a>>(visitor: &mut V, assign_left: &'a AssignLeftSide) { match assign_left { - AssignLeftSide::Identifier(id) => visitor.visit_identifier(id), + AssignLeftSide::Identifier(id) => visitor.visit_expression(id), AssignLeftSide::Indice(expr) => visitor.visit_expression(expr), AssignLeftSide::Dot(expr) => visitor.visit_expression(expr), } @@ -180,27 +202,27 @@ pub fn walk_else<'a, V: Visitor<'a>>(visitor: &mut V, r#else: &'a Else) { } pub fn walk_expression<'a, V: Visitor<'a>>(visitor: &mut V, expr: &'a Expression) { - match &expr.kind { - ExpressionKind::BinopExpr(unary, operator, expr) => { + match &expr { + Expression::BinopExpr(unary, operator, expr) => { visitor.visit_unary_expr(unary); visitor.visit_operator(operator); visitor.visit_expression(&*expr); } - ExpressionKind::UnaryExpr(unary) => visitor.visit_unary_expr(unary), - ExpressionKind::StructCtor(ctor) => visitor.visit_struct_ctor(ctor), - ExpressionKind::NativeOperation(op, left, right) => { + Expression::UnaryExpr(unary) => visitor.visit_unary_expr(unary), + Expression::StructCtor(ctor) => visitor.visit_struct_ctor(ctor), + Expression::NativeOperation(op, left, right) => { visitor.visit_identifier(left); visitor.visit_identifier(right); visitor.visit_native_operator(op); } - ExpressionKind::Return(expr) => { + Expression::Return(expr) => { visitor.visit_expression(expr); } } } pub fn walk_struct_ctor<'a, V: Visitor<'a>>(visitor: &mut V, s: &'a StructCtor) { - visitor.visit_type(&s.name); + visitor.visit_identifier(&s.name); walk_map!(visitor, visit_expression, &s.defs); } @@ -242,10 +264,10 @@ pub fn walk_operator<'a, V: Visitor<'a>>(visitor: &mut V, operator: &'a Operator } pub fn walk_operand<'a, V: Visitor<'a>>(visitor: &mut V, operand: &'a Operand) { - match &operand.kind { - OperandKind::Literal(l) => visitor.visit_literal(l), - OperandKind::Identifier(i) => visitor.visit_identifier_path(i), - OperandKind::Expression(e) => visitor.visit_expression(&*e), + match &operand { + Operand::Literal(l) => visitor.visit_literal(l), + Operand::Identifier(i) => visitor.visit_identifier_path(i), + Operand::Expression(e) => visitor.visit_expression(&*e), } } diff --git a/src/lib/ast_lowering/ast_lowering_context.rs b/src/lib/ast_lowering/ast_lowering_context.rs index a29216fb..3da79b20 100644 --- a/src/lib/ast_lowering/ast_lowering_context.rs +++ b/src/lib/ast_lowering/ast_lowering_context.rs @@ -1,13 +1,13 @@ use std::collections::{BTreeMap, HashMap}; use crate::{ - ast::*, + ast::{return_placement::ReturnInserter, tree::*, NodeId}, hir::{self, Arena, FnBodyId, HirId}, infer::Envs, ty::*, }; -use super::{hir_map::HirMap, return_placement::ReturnInserter, InfixDesugar}; +use super::{hir_map::HirMap, InfixDesugar}; pub struct AstLoweringContext { hir_map: HirMap, @@ -62,40 +62,40 @@ impl AstLoweringContext { } pub fn lower_top_level(&mut self, top_level: &TopLevel) { - match &top_level.kind { - TopLevelKind::Prototype(p) => { + match &top_level { + TopLevel::Prototype(p) => { let top_level = hir::TopLevel { kind: hir::TopLevelKind::Prototype(self.lower_prototype(p)), }; self.top_levels.push(top_level); } - TopLevelKind::Function(f) => { + TopLevel::Function(f) => { let top_level = hir::TopLevel { kind: hir::TopLevelKind::Function(self.lower_function_decl(f)), }; self.top_levels.push(top_level); } - TopLevelKind::Trait(t) => { + TopLevel::Trait(t) => { self.lower_trait(t); } - TopLevelKind::Struct(s) => { + TopLevel::Struct(s) => { self.lower_struct_decl(s); } - TopLevelKind::Impl(i) => { + TopLevel::Impl(i) => { self.lower_impl(i); } - TopLevelKind::Mod(_name, mod_) => self.lower_mod(mod_), - TopLevelKind::Infix(_, _) => (), - TopLevelKind::Use(_u) => (), + TopLevel::Mod(_name, mod_) => self.lower_mod(mod_), + TopLevel::Infix(_, _) => (), + TopLevel::Use(_u) => (), }; } pub fn lower_struct_decl(&mut self, s: &StructDecl) -> hir::StructDecl { let hir_t = hir::StructDecl { - hir_id: self.hir_map.next_hir_id(s.identity.clone()), - name: s.name.clone(), + // hir_id: self.hir_map.next_hir_id(s.name.node_id), + name: self.lower_identifier(&s.name), defs: s .defs .iter() @@ -103,7 +103,7 @@ impl AstLoweringContext { .collect(), }; - self.structs.insert(s.name.get_name(), hir_t.clone()); + self.structs.insert(s.name.to_string(), hir_t.clone()); hir_t } @@ -153,14 +153,14 @@ impl AstLoweringContext { .entry(hir_f.name.name.clone()) .or_insert_with(HashMap::new); - let _hir_id = self.hir_map.next_hir_id(f.identity.clone()); + let _hir_id = self.hir_map.next_hir_id(f.node_id); (*fn_decls).insert(type_sig, hir_f); } } pub fn lower_prototype(&mut self, p: &Prototype) -> hir::Prototype { - let id = self.hir_map.next_hir_id(p.identity.clone()); + let id = self.hir_map.next_hir_id(p.node_id); let ident = self.lower_identifier(&p.name); hir::Prototype { @@ -172,7 +172,7 @@ impl AstLoweringContext { pub fn lower_function_decl(&mut self, f: &FunctionDecl) -> hir::FunctionDecl { let body_id = self.hir_map.next_body_id(); - let id = self.hir_map.next_hir_id(f.identity.clone()); + let id = self.hir_map.next_hir_id(f.node_id); let ident = self.lower_identifier(&f.name); let body = self.lower_fn_body(&f.body, ident.clone(), body_id.clone(), id.clone()); @@ -193,13 +193,13 @@ impl AstLoweringContext { } } - pub fn lower_argument_decl(&mut self, argument: &ArgumentDecl) -> hir::ArgumentDecl { - let id = self.hir_map.next_hir_id(argument.identity.clone()); + pub fn lower_argument_decl(&mut self, identifier: &Identifier) -> hir::ArgumentDecl { + let id = self.hir_map.next_hir_id(identifier.node_id); hir::ArgumentDecl { name: hir::Identifier { hir_id: id, - name: argument.name.clone(), + name: identifier.name.clone(), }, } } @@ -236,22 +236,43 @@ impl AstLoweringContext { pub fn lower_statement(&mut self, stmt: &Statement) -> hir::Statement { hir::Statement { - kind: match &*stmt.kind { - StatementKind::Expression(e) => { + kind: match &*stmt { + Statement::Expression(e) => { Box::new(hir::StatementKind::Expression(self.lower_expression(e))) } - StatementKind::If(e) => Box::new(hir::StatementKind::If(self.lower_if(e))), - StatementKind::Assign(a) => { - Box::new(hir::StatementKind::Assign(self.lower_assign(a))) - } + Statement::If(e) => Box::new(hir::StatementKind::If(self.lower_if(e))), + Statement::Assign(a) => Box::new(hir::StatementKind::Assign(self.lower_assign(a))), + Statement::For(f) => Box::new(hir::StatementKind::For(self.lower_for(f))), }, } } + pub fn lower_for(&mut self, for_loop: &For) -> hir::For { + match for_loop { + For::In(i) => hir::For::In(self.lower_for_in(i)), + For::While(w) => hir::For::While(self.lower_while(w)), + } + } + + pub fn lower_for_in(&mut self, for_in: &ForIn) -> hir::ForIn { + hir::ForIn { + value: self.lower_identifier(&for_in.value), + expr: self.lower_expression(&for_in.expr), + body: self.lower_body(&for_in.body), + } + } + + pub fn lower_while(&mut self, while_loop: &While) -> hir::While { + hir::While { + predicat: self.lower_expression(&while_loop.predicat), + body: self.lower_body(&while_loop.body), + } + } + pub fn lower_assign_left_side(&mut self, assign_left: &AssignLeftSide) -> hir::AssignLeftSide { match assign_left { AssignLeftSide::Identifier(id) => { - hir::AssignLeftSide::Identifier(self.lower_identifier(id)) + hir::AssignLeftSide::Identifier(self.lower_identifier(id.as_identifier().unwrap())) } AssignLeftSide::Indice(indice) => { let expr_hir = self.lower_expression(indice); @@ -287,18 +308,18 @@ impl AstLoweringContext { } pub fn lower_expression(&mut self, expr: &Expression) -> hir::Expression { - match &expr.kind { - ExpressionKind::UnaryExpr(unary) => self.lower_unary(unary), - ExpressionKind::StructCtor(s) => self.lower_struct_ctor(s), - ExpressionKind::NativeOperation(op, left, right) => { + match &expr { + Expression::UnaryExpr(unary) => self.lower_unary(unary), + Expression::StructCtor(s) => self.lower_struct_ctor(s), + Expression::NativeOperation(op, left, right) => { self.lower_native_operation(op, left, right) } - ExpressionKind::BinopExpr(_unary, _op, _expr22) => { + Expression::BinopExpr(_unary, _op, _expr22) => { let mut infix = InfixDesugar::new(self.operators_list.clone()); self.lower_expression(&infix.desugar(expr)) } - ExpressionKind::Return(expr) => hir::Expression { + Expression::Return(expr) => hir::Expression { kind: Box::new(hir::ExpressionKind::Return(self.lower_expression(&*expr))), }, } @@ -306,8 +327,8 @@ impl AstLoweringContext { pub fn lower_struct_ctor(&mut self, s: &StructCtor) -> hir::Expression { hir::Expression::new_struct_ctor(hir::StructCtor { - hir_id: self.hir_map.next_hir_id(s.identity.clone()), - name: s.name.clone(), + // hir_id: self.hir_map.next_hir_id(s.node_id), + name: self.lower_identifier(&s.name), defs: s .defs .iter() @@ -318,7 +339,7 @@ impl AstLoweringContext { pub fn lower_if(&mut self, r#if: &If) -> hir::If { hir::If { - hir_id: self.hir_map.next_hir_id(r#if.identity.clone()), + hir_id: self.hir_map.next_hir_id(r#if.node_id), predicat: self.lower_expression(&r#if.predicat), body: self.lower_body(&r#if.body), else_: r#if.else_.as_ref().map(|e| Box::new(self.lower_else(e))), @@ -347,19 +368,19 @@ impl AstLoweringContext { let mut expr = self.lower_operand(&primary.op); for secondary in &primary.secondaries.clone().unwrap() { - expr = self.lower_secondary(expr, secondary, &primary.identity.clone()); + expr = self.lower_secondary(expr, secondary, primary.node_id); } expr } pub fn lower_operand(&mut self, operand: &Operand) -> hir::Expression { - match &operand.kind { - OperandKind::Literal(l) => hir::Expression::new_literal(self.lower_literal(l)), - OperandKind::Identifier(i) => { + match &operand { + Operand::Literal(l) => hir::Expression::new_literal(self.lower_literal(l)), + Operand::Identifier(i) => { hir::Expression::new_identifier_path(self.lower_identifier_path(i)) } - OperandKind::Expression(e) => self.lower_expression(&**e), + Operand::Expression(e) => self.lower_expression(&**e), } } @@ -367,23 +388,23 @@ impl AstLoweringContext { &mut self, op: hir::Expression, secondary: &SecondaryExpr, - identity: &Identity, + node_id: NodeId, ) -> hir::Expression { match secondary { SecondaryExpr::Arguments(args) => { hir::Expression::new_function_call(hir::FunctionCall { - hir_id: self.hir_map.next_hir_id(identity.clone()), + hir_id: self.hir_map.next_hir_id(node_id.clone()), op, args: args.iter().map(|arg| self.lower_unary(&arg.arg)).collect(), }) } SecondaryExpr::Indice(expr) => hir::Expression::new_indice(hir::Indice { - hir_id: self.hir_map.next_hir_id(identity.clone()), + hir_id: self.hir_map.next_hir_id(node_id.clone()), op, value: self.lower_expression(expr), }), SecondaryExpr::Dot(expr) => hir::Expression::new_dot(hir::Dot { - hir_id: self.hir_map.next_hir_id(identity.clone()), + hir_id: self.hir_map.next_hir_id(node_id.clone()), op, value: self.lower_identifier(expr), }), @@ -391,7 +412,7 @@ impl AstLoweringContext { } pub fn lower_literal(&mut self, lit: &Literal) -> hir::Literal { - let hir_id = self.hir_map.next_hir_id(lit.identity.clone()); + let hir_id = self.hir_map.next_hir_id(lit.node_id); hir::Literal { hir_id, @@ -422,7 +443,7 @@ impl AstLoweringContext { } pub fn lower_identifier(&mut self, id: &Identifier) -> hir::Identifier { - let hir_id = self.hir_map.next_hir_id(id.identity.clone()); + let hir_id = self.hir_map.next_hir_id(id.node_id); hir::Identifier { hir_id, @@ -444,7 +465,7 @@ impl AstLoweringContext { } pub fn lower_native_operator(&mut self, op: &NativeOperator) -> hir::NativeOperator { - let hir_id = self.hir_map.next_hir_id(op.identity.clone()); + let hir_id = self.hir_map.next_hir_id(op.node_id); let kind = match op.kind { NativeOperatorKind::IAdd => hir::NativeOperatorKind::IAdd, @@ -466,6 +487,7 @@ impl AstLoweringContext { NativeOperatorKind::Flt => hir::NativeOperatorKind::Flt, NativeOperatorKind::Fle => hir::NativeOperatorKind::Fle, NativeOperatorKind::BEq => hir::NativeOperatorKind::BEq, + NativeOperatorKind::Len => hir::NativeOperatorKind::Len, }; hir::NativeOperator { hir_id, kind } diff --git a/src/lib/ast_lowering/hir_map.rs b/src/lib/ast_lowering/hir_map.rs index 74930e7a..5425b785 100644 --- a/src/lib/ast_lowering/hir_map.rs +++ b/src/lib/ast_lowering/hir_map.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use crate::{ - ast::*, + ast::NodeId, hir::{FnBodyId, HirId}, }; @@ -18,12 +18,12 @@ impl HirMap { Self::default() } - pub fn next_hir_id(&mut self, identity: Identity) -> HirId { + pub fn next_hir_id(&mut self, node_id: NodeId) -> HirId { let hir_id = HirId(self.hir_id_next); self.hir_id_next += 1; - self.add_hir_mapping(hir_id.clone(), identity.node_id); + self.add_hir_mapping(hir_id.clone(), node_id); hir_id } @@ -53,11 +53,11 @@ impl HirMap { pub fn duplicate_hir_mapping(&mut self, hir_id: HirId) -> Option { let node_id = self.get_node_id(&hir_id)?; - let mut fake_ident = Identity::new_placeholder(); + /* let mut fake_ident = Identity::new_placeholder(); - fake_ident.node_id = node_id; + fake_ident.node_id = node_id; */ - let new_id = self.next_hir_id(fake_ident); + let new_id = self.next_hir_id(node_id); self.add_hir_mapping(new_id.clone(), node_id); diff --git a/src/lib/ast_lowering/infix_desugar.rs b/src/lib/ast_lowering/infix_desugar.rs index df009e9c..6d1f79cb 100644 --- a/src/lib/ast_lowering/infix_desugar.rs +++ b/src/lib/ast_lowering/infix_desugar.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use crate::ast::{Expression, ExpressionKind, Identifier, Operand, UnaryExpr}; +use crate::ast::tree::{Expression, Identifier, Operand, UnaryExpr}; #[derive(Debug)] pub enum ExprOrIdentifier { @@ -42,7 +42,7 @@ impl InfixDesugar { let left = stack.pop().unwrap(); stack.push(UnaryExpr::create_2_args_func_call( - Operand::from_identifier(id), + Operand::from_identifier(id.clone()), left.clone(), right.clone(), )); @@ -50,12 +50,12 @@ impl InfixDesugar { } } - Expression::from_unary(&stack.pop().unwrap()) + Expression::new_unary(stack.pop().unwrap()) } pub fn populate_rec(&mut self, expr: &Expression) { - match &expr.kind { - ExpressionKind::BinopExpr(unary, op, expr2) => { + match &expr { + Expression::BinopExpr(unary, op, expr2) => { self.output.push(ExprOrIdentifier::Expr(unary.clone())); self.pop_higher_operators( @@ -66,9 +66,7 @@ impl InfixDesugar { self.desugar(expr2); } - ExpressionKind::UnaryExpr(unary) => { - self.output.push(ExprOrIdentifier::Expr(unary.clone())) - } + Expression::UnaryExpr(unary) => self.output.push(ExprOrIdentifier::Expr(unary.clone())), _ => unimplemented!(), } } diff --git a/src/lib/ast_lowering/loop_desugar.rs b/src/lib/ast_lowering/loop_desugar.rs new file mode 100644 index 00000000..f0c7ba8f --- /dev/null +++ b/src/lib/ast_lowering/loop_desugar.rs @@ -0,0 +1,63 @@ +use std::collections::HashMap; + +use crate::ast::{Expression, ExpressionKind, Identifier, Operand, Statement, UnaryExpr}; + +pub struct Loop { + pre_loop: Vec, + predicat: Expression, + pre_body: Vec, + body: Body, + post_body: Vec, +} + +#[derive(Debug)] +pub struct LoopDesugar {} + +impl LoopDesugar { + pub fn new() -> Self { + LoopDesugar {} + } + + pub fn desugar(&mut self, for_loop: &For) -> Loop { + match for_loop { + For::In(for_in) => self.desugar_for_in(&for_in), + For::While(while_loop) => self.desugar_while(&while_loop), + } + } + + // for item in arr + // do item + // + // --- becomes + // + // let i = 0 + // let arr_len = ~Len arr + // for i < arr_len + // let item = arr[i] // pre_body + // do item + // i = i + 1 // post_body + + + + fn desugar_for_in(&mut self, for_in: &ForIn) -> Loop { + let i_var = Statement::new_assign() + + Loop { + pre_loop: vec![], + predicat: (), + pre_body: vec![], + body: for_in.body.clone(), + post_body: vec![], + } + } + + fn desugar_while(&mut self, while_loop: &While) -> Loop { + Loop { + pre_loop: vec![], + predicat: while_loop.predicat.clone(), + pre_body: vec![], + body: while_loop.body.clone(), + post_body: vec![], + } + } +} diff --git a/src/lib/ast_lowering/mod.rs b/src/lib/ast_lowering/mod.rs index db0ee4ea..7560bdc4 100644 --- a/src/lib/ast_lowering/mod.rs +++ b/src/lib/ast_lowering/mod.rs @@ -1,9 +1,8 @@ -use crate::{ast::Root, hir}; +use crate::{ast::tree::Root, hir}; mod ast_lowering_context; mod hir_map; mod infix_desugar; -mod return_placement; use ast_lowering_context::AstLoweringContext; pub use hir_map::*; diff --git a/src/lib/ast_lowering/return_placement.rs b/src/lib/ast_lowering/return_placement.rs deleted file mode 100644 index 20507d26..00000000 --- a/src/lib/ast_lowering/return_placement.rs +++ /dev/null @@ -1,52 +0,0 @@ -use crate::ast::*; - -pub struct ReturnInserter<'a> { - pub body: &'a Body, -} - -impl<'a> ReturnInserter<'a> { - pub fn run(&mut self) -> Body { - let mut body = self.body.clone(); - - self.visit_body(&mut body); - - body.clone() - } - - fn visit_body(&mut self, body: &mut Body) { - if let Some(stmt) = body.stmts.iter_mut().last() { - self.visit_statement(stmt) - } - } - - fn visit_statement(&mut self, stmt: &mut Statement) { - match *stmt.kind { - StatementKind::Expression(ref mut e) => { - e.kind = ExpressionKind::Return(e.clone()); - } - StatementKind::If(ref mut i) => { - self.visit_if(i); - } - StatementKind::Assign(ref mut a) => { - a.value.kind = ExpressionKind::Return(Box::new(a.value.clone())); - } - } - } - - fn visit_if(&mut self, r#if: &mut If) { - self.visit_body(&mut r#if.body); - - if let Some(ref mut r#else) = r#if.else_.as_mut() { - self.visit_else(r#else); - } else { - unimplemented!("Else is not found"); - } - } - - fn visit_else(&mut self, r#else: &mut Else) { - match r#else { - Else::If(i) => self.visit_if(i), - Else::Body(b) => self.visit_body(b), - } - } -} diff --git a/src/lib/codegen/codegen_context.rs b/src/lib/codegen/codegen_context.rs index b8a10ad1..62b7ed0a 100644 --- a/src/lib/codegen/codegen_context.rs +++ b/src/lib/codegen/codegen_context.rs @@ -51,30 +51,23 @@ impl<'a> CodegenContext<'a> { let pass_manager = PassManager::create(()); - pass_manager.add_demote_memory_to_register_pass(); pass_manager.add_promote_memory_to_register_pass(); + // pass_manager.add_demote_memory_to_register_pass(); pass_manager.add_argument_promotion_pass(); pass_manager.add_always_inliner_pass(); pass_manager.add_gvn_pass(); pass_manager.add_new_gvn_pass(); pass_manager.add_function_attrs_pass(); pass_manager.add_prune_eh_pass(); - pass_manager.add_loop_vectorize_pass(); - pass_manager.add_cfg_simplification_pass(); pass_manager.add_constant_merge_pass(); pass_manager.add_scalarizer_pass(); pass_manager.add_merged_load_store_motion_pass(); - pass_manager.add_ind_var_simplify_pass(); pass_manager.add_instruction_combining_pass(); - pass_manager.add_licm_pass(); - pass_manager.add_loop_deletion_pass(); - pass_manager.add_loop_unswitch_pass(); pass_manager.add_memcpy_optimize_pass(); pass_manager.add_partially_inline_lib_calls_pass(); pass_manager.add_lower_switch_pass(); pass_manager.add_reassociate_pass(); pass_manager.add_simplify_lib_calls_pass(); - pass_manager.add_tail_call_elimination_pass(); pass_manager.add_aggressive_inst_combiner_pass(); pass_manager.add_instruction_simplify_pass(); pass_manager.add_function_inlining_pass(); @@ -83,8 +76,22 @@ impl<'a> CodegenContext<'a> { pass_manager.add_strip_symbol_pass(); pass_manager.add_strip_dead_prototypes_pass(); pass_manager.add_internalize_pass(true); - pass_manager.add_aggressive_dce_pass(); pass_manager.add_sccp_pass(); + pass_manager.add_aggressive_dce_pass(); + pass_manager.add_global_dce_pass(); + pass_manager.add_tail_call_elimination_pass(); + pass_manager.add_basic_alias_analysis_pass(); + pass_manager.add_licm_pass(); + pass_manager.add_ind_var_simplify_pass(); + pass_manager.add_loop_vectorize_pass(); + pass_manager.add_loop_unswitch_pass(); + pass_manager.add_loop_idiom_pass(); + pass_manager.add_loop_rotate_pass(); + pass_manager.add_loop_unroll_and_jam_pass(); + pass_manager.add_loop_unroll_pass(); + pass_manager.add_loop_deletion_pass(); + pass_manager.add_cfg_simplification_pass(); + pass_manager.add_verifier_pass(); pass_manager.run_on(&self.module); @@ -307,6 +314,8 @@ impl<'a> CodegenContext<'a> { name: &str, builder: &'a Builder, ) -> Result<(AnyValueEnum<'a>, BasicBlock<'a>), ()> { + // self.scopes.push(); + let basic_block = self .context .append_basic_block(self.cur_func.unwrap(), name); @@ -320,6 +329,8 @@ impl<'a> CodegenContext<'a> { .last() .unwrap()?; + // self.scopes.pop(); + Ok((stmt, basic_block)) } @@ -332,34 +343,132 @@ impl<'a> CodegenContext<'a> { StatementKind::Expression(e) => self.lower_expression(e, builder)?.as_any_value_enum(), StatementKind::If(e) => self.lower_if(e, builder)?.0, StatementKind::Assign(a) => self.lower_assign(a, builder)?, + StatementKind::For(f) => self.lower_for(f, builder)?, }) } + pub fn lower_for( + &mut self, + for_loop: &'a For, + builder: &'a Builder, + ) -> Result, ()> { + match &for_loop { + For::In(i) => self.lower_for_in(i, builder), + For::While(w) => self.lower_while(w, builder), + } + } + + pub fn lower_for_in( + &mut self, + for_in: &'a ForIn, + builder: &'a Builder, + ) -> Result, ()> { + let block = builder.get_insert_block().unwrap(); + let cur_f = block.get_parent().unwrap(); + + let (value, while_body) = self.lower_body(&for_in.body, "for_in_body", builder)?; + + let predicat = self.lower_expression(&for_in.expr, builder)?; + + let exit_block = self.context.append_basic_block(cur_f, "for_in_exit"); + + builder.position_at_end(while_body); + builder.build_conditional_branch(predicat.into_int_value(), while_body, exit_block); + + builder.position_at_end(block); + builder.build_unconditional_branch(while_body); + + builder.position_at_end(exit_block); + + Ok(value) + } + + pub fn lower_while( + &mut self, + while_loop: &'a While, + builder: &'a Builder, + ) -> Result, ()> { + let block = builder.get_insert_block().unwrap(); + let cur_f = block.get_parent().unwrap(); + + let (value, while_body) = self.lower_body(&while_loop.body, "while_body", builder)?; + + let predicat = self.lower_expression(&while_loop.predicat, builder)?; + + let exit_block = self.context.append_basic_block(cur_f, "while_exit"); + + builder.position_at_end(while_body); + builder.build_conditional_branch(predicat.into_int_value(), while_body, exit_block); + + builder.position_at_end(block); + builder.build_unconditional_branch(while_body); + + builder.position_at_end(exit_block); + + Ok(value) + } + pub fn lower_assign( &mut self, assign: &'a Assign, builder: &'a Builder, ) -> Result, ()> { - let value = self.lower_expression(&assign.value, builder)?; - - match &assign.name { + Ok(match &assign.name { AssignLeftSide::Identifier(id) => { - self.scopes - .add(id.get_hir_id(), value.as_basic_value_enum()); + let value = self.lower_expression(&assign.value, builder)?; + + // FIXME: This is twisted + let val = self + .hir + .resolutions + .get(&id.get_hir_id()) + .and_then(|reso| self.scopes.get(reso)) + .map(|ptr| ptr.into_pointer_value()) + .or_else(|| { + self.hir.node_types.get(&assign.get_hir_id()).and_then(|t| { + (t.is_primitive() && !t.is_array() && !t.is_string()).then(|| { + let ptr = + builder.build_alloca(value.get_type(), &id.name.to_string()); + + self.scopes.add(id.get_hir_id(), ptr.as_basic_value_enum()); + + ptr + }) + }) + }) + .map(|ptr| { + builder.build_store(ptr, value); + ptr.as_basic_value_enum() + }) + .or({ + // self.scopes.add(id.get_hir_id(), value.clone()); + Some(value) + }) + .unwrap(); + + self.scopes.add(id.get_hir_id(), val); + + val.as_any_value_enum() } AssignLeftSide::Indice(indice) => { let ptr = self.lower_indice_ptr(indice, builder)?.into_pointer_value(); + let value = self.lower_expression(&assign.value, builder)?; + builder.build_store(ptr, value); + + ptr.as_any_value_enum() } AssignLeftSide::Dot(dot) => { let ptr = self.lower_dot_ptr(dot, builder)?.into_pointer_value(); + let value = self.lower_expression(&assign.value, builder)?; + builder.build_store(ptr, value); - } - } - Ok(value.as_any_value_enum()) + ptr.as_any_value_enum() + } + }) } pub fn lower_if( @@ -387,6 +496,8 @@ impl<'a> CodegenContext<'a> { }; // FIXME: Need a last block if the 'if' is not the last statement in the fn body + // Investigate PHI values + // // let rest_block = self // .context // .append_basic_block(self.module.get_last_function().unwrap(), "rest"); @@ -442,7 +553,15 @@ impl<'a> CodegenContext<'a> { self.lower_native_operation(op, left, right, builder)? } ExpressionKind::Return(expr) => { + // println!("RETURN {:#?}", expr); let val = self.lower_expression(expr, builder)?; + // println!("RETURN2 {:#?}", val); + + // let val = val + // .is_pointer_value() + // .then(|| builder.build_load(val.into_pointer_value(), "test")) + // .or(Some(val)) + // .unwrap(); builder.build_return(Some(&val.as_basic_value_enum())); @@ -673,16 +792,27 @@ impl<'a> CodegenContext<'a> { pub fn lower_identifier( &mut self, id: &Identifier, - _builder: &'a Builder, + builder: &'a Builder, ) -> Result, ()> { let reso = self.hir.resolutions.get(&id.hir_id).unwrap(); - let val = match self.scopes.get(reso) { + let val = match self.scopes.get(reso.clone()) { None => { + self.module.print_to_stderr(); + println!("GEN : NO REO {:#?} {:#?}", id, reso); return Err(()); } Some(val) => val, }; + let t = self.hir.node_types.get(&id.get_hir_id()).unwrap(); + + // Dereference primitives only + // FIXME: get Array and String out of PrimitveType + let val = if val.is_pointer_value() && t.is_primitive() && !t.is_array() && !t.is_string() { + builder.build_load(val.into_pointer_value(), &id.name.to_string()) + } else { + val + }; Ok(val) } @@ -849,6 +979,26 @@ impl<'a> CodegenContext<'a> { .build_int_compare(IntPredicate::EQ, left, right, "beq") .as_basic_value_enum() } + + // arrays + NativeOperatorKind::Len => { + let arr_size = self + .hir + .node_types + .get(&left.get_hir_id()) + .and_then(|arr_t| arr_t.try_as_primitive_type()) + .and_then(|prim_t| prim_t.try_as_array()) + .map(|(_inner_t, size)| size) + .unwrap(); + + // FIXME: ignored right argument for now + // let right = self.lower_identifier(right, builder)?.into_int_value(); + + self.context + .i64_type() + .const_int(arr_size as u64, false) + .as_basic_value_enum() + } }) } } diff --git a/src/lib/codegen/interpreter.rs b/src/lib/codegen/interpreter.rs index 2a34ca38..797f1018 100644 --- a/src/lib/codegen/interpreter.rs +++ b/src/lib/codegen/interpreter.rs @@ -82,6 +82,8 @@ fn process_line( } let mut get_type = false; + let mut print_ir = false; + let mut print_hir = false; if line.starts_with(":?") { print_help(); @@ -89,6 +91,14 @@ fn process_line( return; } + if line.starts_with(":i") { + print_ir = true; + } + + if line.starts_with(":h") { + print_hir = true; + } + if line.starts_with(":t ") { get_type = true; @@ -102,27 +112,25 @@ fn process_line( let mut is_top_level = false; // FIXME: dirty hack to know if this is a function - let line_parts = line.split("=").collect::>(); + let line_parts = line.split('=').collect::>(); if line_parts.len() > 1 && !line_parts[0].starts_with("let ") - && line_parts[0].split(" ").count() > 1 + && line_parts[0].split(" ").filter(|x| !x.is_empty()).count() > 1 { is_top_level = true; - } else { - if line.starts_with("use ") || line.starts_with("mod ") { - is_top_level = true; - } + } else if line.starts_with("use ") || line.starts_with("mod ") { + is_top_level = true; } if is_top_level { top_levels.push(line.clone()); } else { - if !get_type { + if !get_type && !print_ir && !print_hir { commands.push(" ".to_owned() + &line); } } - let mut parsing_ctx = ParsingCtx::new(&config); + let mut parsing_ctx = ParsingCtx::new(config); let src = SourceFile::from_expr(top_levels.join("\n"), commands.join("\n"), !is_top_level).unwrap(); @@ -132,10 +140,10 @@ fn process_line( let hir = match crate::parse_str(&mut parsing_ctx, config) { Ok(hir) => hir, Err(_e) => { - if is_top_level { + if is_top_level || print_ir || print_hir { top_levels.pop(); } else { - if !get_type { + if !get_type && !print_ir && !print_hir { commands.pop(); } } @@ -144,8 +152,14 @@ fn process_line( } }; + if print_hir { + hir.print(); + + return; + } + if get_type { - if is_top_level { + if is_top_level || print_ir { top_levels.pop(); } else { // commands.pop(); @@ -154,8 +168,8 @@ fn process_line( if get_type { let t = hir - .get_function_by_name(line.split(" ").nth(0).unwrap()) - .map(|f| format!("{:?}", Type::from(f.signature.clone()))) + .get_function_by_name(line.split(' ').next().unwrap()) + .map(|f| format!("{:?}", Type::from(f.signature))) .or_else(|| Some("UNKNOWN".red().to_string())) .unwrap(); @@ -186,10 +200,14 @@ fn process_line( codegen_ctx.optimize(); } - if config.show_ir { + if config.show_ir || print_ir { codegen_ctx.module.print_to_stderr(); } + if print_ir { + return; + } + let engine = codegen_ctx .module .create_jit_execution_engine(OptimizationLevel::None) @@ -202,11 +220,15 @@ fn process_line( pub fn print_help() { println!( - "\n{}\n\n {} : {}\n {} : {}\n", + "\n{}\n\n {} : {}\n {} : {}\n {} : {}\n {} : {}\n", "Help:".bright_green(), + ":h".bright_yellow(), + "Print the HIR".bright_black(), + ":i".bright_yellow(), + "Print the IR".bright_black(), ":t".bright_yellow(), "Print the type of an expression".bright_black(), ":?".bright_yellow(), - "Print This help".bright_black(), + "Print this help".bright_black(), ); } diff --git a/src/lib/codegen/mod.rs b/src/lib/codegen/mod.rs index 360d2674..9aba0b50 100644 --- a/src/lib/codegen/mod.rs +++ b/src/lib/codegen/mod.rs @@ -15,6 +15,7 @@ pub fn generate(config: &Config, hir: Root) -> Result<(), Diagnostic> { if codegen_ctx.lower_hir(&hir, &builder).is_err() { // FIXME: have a movable `Diagnostics` // codegen_ctx.parsing_ctx.return_if_error()?; + panic!("GEN ERROR"); } match codegen_ctx.module.verify() { diff --git a/src/lib/diagnostics/diagnostic.rs b/src/lib/diagnostics/diagnostic.rs index 3bcb97e6..a7bc2b12 100644 --- a/src/lib/diagnostics/diagnostic.rs +++ b/src/lib/diagnostics/diagnostic.rs @@ -1,12 +1,14 @@ use std::fmt::Display; -use crate::{diagnostics::DiagnosticType, parser::Span}; +use crate::parser::Parser; +use crate::{diagnostics::DiagnosticType, parser::span::Span, parser::span2::Span as Span2}; use crate::{ hir::HirId, parser::SourceFile, ty::{FuncType, Type}, }; use colored::*; +use nom::error::VerboseError; #[derive(Clone, Debug)] pub struct Diagnostic { @@ -159,7 +161,7 @@ impl Diagnostic { ); let line_span_start = line_start; - let mut line_span_stop = line_start + (self.span.end - self.span.start) + 1; + let mut line_span_stop = line_start + (self.span.end - self.span.start); let line_colored = lines[line - 1].iter().cloned().collect::(); if line_span_stop > line_colored.len() { @@ -229,7 +231,10 @@ impl Display for DiagnosticKind { Self::ModuleNotFound(path) => format!("Module not found: {}", path), Self::DuplicatedOperator => "DuplicatedOperator".to_string(), Self::TypeConflict(t1, t2, _in1, _in2) => { - format!("Type conflict: Expected {:?} but got {:?} ", t1, t2) + format!( + "Type conflict:\n{:<8}Expected {:?}\n{:<8}But got {:?}", + "", t1, "", t2 + ) } Self::UnresolvedType(t) => { format!( @@ -268,3 +273,27 @@ impl Display for DiagnosticKind { write!(f, "{}", s) } } + +impl<'a> From> for Diagnostic { + fn from(err: Parser<'a>) -> Self { + let span2 = Span2::from(err); + let span = Span::from(span2); + + let msg = "Syntax error".to_string(); + + Diagnostic::new_syntax_error(span, msg) + } +} + +impl<'a> From>> for Diagnostic { + fn from(err: VerboseError>) -> Self { + let (input, _kind) = err.errors.into_iter().next().unwrap(); + + let span2 = Span2::from(input); + let span = Span::from(span2); + + let msg = "Syntax error".to_string(); + + Diagnostic::new_syntax_error(span, msg) + } +} diff --git a/src/lib/diagnostics/diagnostics_list.rs b/src/lib/diagnostics/diagnostics_list.rs index 3015d0f0..838ad1fb 100644 --- a/src/lib/diagnostics/diagnostics_list.rs +++ b/src/lib/diagnostics/diagnostics_list.rs @@ -37,7 +37,14 @@ impl Diagnostics { pub fn print(&self, files: &HashMap) { for (i, diag) in self.list.iter().enumerate() { - let input = files.get(&diag.span.file_path).unwrap(); + let input = match files.get(&diag.span.file_path) { + Some(input) => input, + None => { + warn!("Diagnostic has been silenced because the file is not found"); + + continue; + } + }; diag.print(input, self.list_types.get(i).unwrap()); } diff --git a/src/lib/helpers/scopes.rs b/src/lib/helpers/scopes.rs index dc06320d..b7fbd119 100644 --- a/src/lib/helpers/scopes.rs +++ b/src/lib/helpers/scopes.rs @@ -98,3 +98,32 @@ where self.ordering.push(s); } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn basic_scope() { + let mut scopes = Scopes::default(); + + scopes.add("a", 1); + scopes.add("b", 2); + + assert_eq!(scopes.get("a").unwrap(), 1); + assert_eq!(scopes.get("b").unwrap(), 2); + + scopes.push(); + + scopes.add("a", 3); + scopes.add("b", 4); + + assert_eq!(scopes.get("a").unwrap(), 3); + assert_eq!(scopes.get("b").unwrap(), 4); + + scopes.pop(); + + assert_eq!(scopes.get("a").unwrap(), 1); + assert_eq!(scopes.get("b").unwrap(), 2); + } +} diff --git a/src/lib/hir/has_hir_id.rs b/src/lib/hir/has_hir_id.rs index 023fc3d6..e05e38bb 100644 --- a/src/lib/hir/has_hir_id.rs +++ b/src/lib/hir/has_hir_id.rs @@ -22,11 +22,9 @@ macro_rules! impl_direct_get_hir_id_trait { impl_direct_get_hir_id_trait!( Prototype FunctionDecl - StructDecl Identifier If FunctionCall - StructCtor Indice Dot Literal @@ -52,11 +50,16 @@ macro_rules! impl_indirect_get_hir_id_trait { impl_indirect_get_hir_id_trait!( TopLevel Statement + StructCtor + StructDecl Assign AssignLeftSide ArgumentDecl IdentifierPath FnBody + For + ForIn + While Body Expression Array diff --git a/src/lib/hir/tree.rs b/src/lib/hir/tree.rs index 38cadbfb..4e75a536 100644 --- a/src/lib/hir/tree.rs +++ b/src/lib/hir/tree.rs @@ -5,7 +5,7 @@ use crate::{ ast_lowering::HirMap, hir::hir_id::*, infer::Envs, - parser::Span, + parser::span2::Span, resolver::ResolutionMap, ty::{FuncType, StructType, Type}, }; @@ -153,15 +153,15 @@ pub struct Impl { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct StructDecl { - pub hir_id: HirId, - pub name: Type, + // pub hir_id: HirId, + pub name: Identifier, pub defs: Vec, } impl StructDecl { pub fn to_type(&self) -> Type { Type::Struct(StructType { - name: self.name.get_name(), + name: self.name.name.clone(), defs: self .defs .iter() @@ -178,15 +178,25 @@ impl StructDecl { .collect(), }) } + + pub fn get_terminal_hir_id(&self) -> HirId { + self.name.hir_id.clone() + } } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct StructCtor { - pub hir_id: HirId, - pub name: Type, + // pub hir_id: HirId, + pub name: Identifier, pub defs: BTreeMap, } +impl StructCtor { + pub fn get_terminal_hir_id(&self) -> HirId { + self.name.hir_id.clone() + } +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TopLevel { pub kind: TopLevelKind, @@ -326,6 +336,7 @@ impl Statement { StatementKind::Expression(e) => e.get_hir_id(), StatementKind::Assign(a) => a.get_hir_id(), StatementKind::If(e) => e.get_hir_id(), + StatementKind::For(f) => f.get_hir_id(), } } } @@ -335,6 +346,47 @@ pub enum StatementKind { Expression(Expression), Assign(Assign), If(If), + For(For), +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum For { + In(ForIn), + While(While), +} + +impl For { + pub fn get_terminal_hir_id(&self) -> HirId { + match self { + For::In(i) => i.get_hir_id(), + For::While(w) => w.get_hir_id(), + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct While { + pub predicat: Expression, + pub body: Body, +} + +impl While { + pub fn get_terminal_hir_id(&self) -> HirId { + self.body.get_hir_id() + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ForIn { + pub value: Identifier, + pub expr: Expression, + pub body: Body, +} + +impl ForIn { + pub fn get_terminal_hir_id(&self) -> HirId { + self.body.get_hir_id() + } } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -592,6 +644,7 @@ pub enum NativeOperatorKind { Fge, Flt, BEq, + Len, } impl std::fmt::Display for NativeOperatorKind { diff --git a/src/lib/hir/visit.rs b/src/lib/hir/visit.rs index 06e7ccab..313f3d1c 100644 --- a/src/lib/hir/visit.rs +++ b/src/lib/hir/visit.rs @@ -39,6 +39,9 @@ generate_visitor_trait!( FnBody Body Statement + For + ForIn + While Expression If Else @@ -79,7 +82,7 @@ pub fn walk_top_level<'a, V: Visitor<'a>>(visitor: &mut V, top_level: &'a TopLev } pub fn walk_struct_decl<'a, V: Visitor<'a>>(visitor: &mut V, s: &'a StructDecl) { - visitor.visit_type(&s.name); + visitor.visit_identifier(&s.name); walk_list!(visitor, visit_prototype, &s.defs); } @@ -151,9 +154,28 @@ pub fn walk_statement<'a, V: Visitor<'a>>(visitor: &mut V, statement: &'a Statem StatementKind::Expression(expr) => visitor.visit_expression(expr), StatementKind::Assign(assign) => visitor.visit_assign(assign), StatementKind::If(expr) => visitor.visit_if(expr), + StatementKind::For(for_loop) => visitor.visit_for(for_loop), } } +pub fn walk_for<'a, V: Visitor<'a>>(visitor: &mut V, for_loop: &'a For) { + match for_loop { + For::In(for_in) => visitor.visit_for_in(for_in), + For::While(while_loop) => visitor.visit_while(while_loop), + } +} + +pub fn walk_for_in<'a, V: Visitor<'a>>(visitor: &mut V, for_in: &'a ForIn) { + visitor.visit_identifier(&for_in.value); + visitor.visit_expression(&for_in.expr); + visitor.visit_body(&for_in.body); +} + +pub fn walk_while<'a, V: Visitor<'a>>(visitor: &mut V, while_loop: &'a While) { + visitor.visit_expression(&while_loop.predicat); + visitor.visit_body(&while_loop.body); +} + pub fn walk_expression<'a, V: Visitor<'a>>(visitor: &mut V, expr: &'a Expression) { match &*expr.kind { ExpressionKind::Lit(lit) => visitor.visit_literal(lit), @@ -172,7 +194,7 @@ pub fn walk_expression<'a, V: Visitor<'a>>(visitor: &mut V, expr: &'a Expression } pub fn walk_struct_ctor<'a, V: Visitor<'a>>(visitor: &mut V, s: &'a StructCtor) { - visitor.visit_type(&s.name); + visitor.visit_identifier(&s.name); walk_map!(visitor, visit_expression, &s.defs); } diff --git a/src/lib/hir/visit_mut.rs b/src/lib/hir/visit_mut.rs index ae4c7dd1..5c2928fe 100644 --- a/src/lib/hir/visit_mut.rs +++ b/src/lib/hir/visit_mut.rs @@ -39,6 +39,9 @@ generate_visitor_mut_trait!( FnBody Body Statement + For + ForIn + While Expression If Else @@ -72,13 +75,13 @@ pub fn walk_root<'a, V: VisitorMut<'a>>(visitor: &mut V, root: &'a mut Root) { } pub fn walk_struct_decl<'a, V: VisitorMut<'a>>(visitor: &mut V, s: &'a mut StructDecl) { - visitor.visit_type(&mut s.name); + visitor.visit_identifier(&mut s.name); walk_list!(visitor, visit_prototype, &mut s.defs); } pub fn walk_struct_ctor<'a, V: VisitorMut<'a>>(visitor: &mut V, s: &'a mut StructCtor) { - visitor.visit_type(&mut s.name); + visitor.visit_identifier(&mut s.name); walk_map!(visitor, visit_expression, &mut s.defs); } @@ -151,9 +154,28 @@ pub fn walk_statement<'a, V: VisitorMut<'a>>(visitor: &mut V, statement: &'a mut StatementKind::Expression(expr) => visitor.visit_expression(expr), StatementKind::Assign(assign) => visitor.visit_assign(assign), StatementKind::If(expr) => visitor.visit_if(expr), + StatementKind::For(for_loop) => visitor.visit_for(for_loop), } } +pub fn walk_for<'a, V: VisitorMut<'a>>(visitor: &mut V, for_loop: &'a mut For) { + match for_loop { + For::In(for_in) => visitor.visit_for_in(for_in), + For::While(while_loop) => visitor.visit_while(while_loop), + } +} + +pub fn walk_for_in<'a, V: VisitorMut<'a>>(visitor: &mut V, for_in: &'a mut ForIn) { + visitor.visit_identifier(&mut for_in.value); + visitor.visit_expression(&mut for_in.expr); + visitor.visit_body(&mut for_in.body); +} + +pub fn walk_while<'a, V: VisitorMut<'a>>(visitor: &mut V, while_loop: &'a mut While) { + visitor.visit_expression(&mut while_loop.predicat); + visitor.visit_body(&mut while_loop.body); +} + pub fn walk_assign_left_side<'a, V: VisitorMut<'a>>( visitor: &mut V, assign_left: &'a mut AssignLeftSide, diff --git a/src/lib/infer/constraint.rs b/src/lib/infer/constraint.rs index 286e3db8..1548ab92 100644 --- a/src/lib/infer/constraint.rs +++ b/src/lib/infer/constraint.rs @@ -97,7 +97,12 @@ impl<'a> ConstraintContext<'a> { .or_else(|| { self.envs.diagnostics.push_error( Diagnostic::new_unresolved_trait_call( - self.envs.spans.get(&call_hir_id.clone()).unwrap().clone(), + self.envs + .spans + .get(&call_hir_id.clone()) + .unwrap() + .clone() + .into(), call_hir_id.clone(), new_sig, existing_impls.keys().cloned().collect(), @@ -162,7 +167,12 @@ impl<'a> ConstraintContext<'a> { self.envs .diagnostics .push_error(Diagnostic::new_type_conflict( - self.envs.spans.get(&fc.op.get_hir_id()).unwrap().clone(), + self.envs + .spans + .get(&fc.op.get_hir_id()) + .unwrap() + .clone() + .into(), fc.to_func_type(self.envs.get_current_env().unwrap()).into(), f.signature.clone().into(), fc.to_func_type(self.envs.get_current_env().unwrap()).into(), @@ -214,7 +224,12 @@ impl<'a> ConstraintContext<'a> { self.envs .diagnostics .push_error(Diagnostic::new_type_conflict( - self.envs.spans.get(&fc.op.get_hir_id()).unwrap().clone(), + self.envs + .spans + .get(&fc.op.get_hir_id()) + .unwrap() + .clone() + .into(), fc.to_func_type(self.envs.get_current_env().unwrap()).into(), sig.clone().into(), fc.to_func_type(self.envs.get_current_env().unwrap()).into(), @@ -356,7 +371,7 @@ impl<'a, 'ar> Visitor<'a> for ConstraintContext<'ar> { fn visit_struct_decl(&mut self, s: &StructDecl) { let t = s.into(); - self.envs.set_type(&s.hir_id, &t); + self.envs.set_type(&s.name.hir_id, &t); let struct_t = t.as_struct_type(); @@ -364,16 +379,31 @@ impl<'a, 'ar> Visitor<'a> for ConstraintContext<'ar> { self.envs .set_type(&p.hir_id, struct_t.defs.get(&p.name.name).unwrap()); }); + // FIXME: should be done in the ast_lowering phase + /* s.defs.iter().for_each(|p| { + let ty = *struct_t.defs.get(&p.name.name).unwrap().clone(); + + // FIXME: should not have to do this conversion from trait. + let ty = if let Type::Trait(t) = ty.clone() { + println!("WHAT {:#?}, {:#?}, {:#?}", p, t, self.hir.structs); + self.hir.structs.get(&ty.get_name()).unwrap().to_type() + } else { + ty + }; + + println!("TRANSFORMED {:#?}", ty); + self.envs.set_type(&p.hir_id, &ty) + }); */ } fn visit_struct_ctor(&mut self, s: &StructCtor) { - let s_decl = self.hir.structs.get(&s.name.get_name()).unwrap(); + let s_decl = self.hir.structs.get(&s.name.name).unwrap(); self.visit_struct_decl(s_decl); let t = s_decl.into(); - self.envs.set_type(&s.hir_id, &t); + self.envs.set_type(&s.name.hir_id, &t); let struct_t = t.as_struct_type(); @@ -464,11 +494,14 @@ impl<'a, 'ar> Visitor<'a> for ConstraintContext<'ar> { | NativeOperatorKind::FDiv | NativeOperatorKind::FMul => PrimitiveType::Float64, NativeOperatorKind::BEq => PrimitiveType::Bool, + NativeOperatorKind::Len => PrimitiveType::Void, // ignored }; - self.envs - .set_type(&left.hir_id.clone(), &arg_t.clone().into()); - self.envs.set_type(&right.hir_id.clone(), &arg_t.into()); + if !matches!(arg_t, PrimitiveType::Void) { + self.envs + .set_type(&left.hir_id.clone(), &arg_t.clone().into()); + self.envs.set_type(&right.hir_id.clone(), &arg_t.into()); + } self.visit_native_operator(op); } @@ -512,7 +545,12 @@ impl<'a, 'ar> Visitor<'a> for ConstraintContext<'ar> { self.envs .diagnostics .push_error(Diagnostic::new_type_conflict( - self.envs.spans.get(&i.value.get_hir_id()).unwrap().clone(), + self.envs + .spans + .get(&i.value.get_hir_id()) + .unwrap() + .clone() + .into(), Type::Primitive(PrimitiveType::Int64), other.clone(), Type::Primitive(PrimitiveType::Int64), @@ -525,7 +563,12 @@ impl<'a, 'ar> Visitor<'a> for ConstraintContext<'ar> { .envs .diagnostics .push_error(Diagnostic::new_type_conflict( - self.envs.spans.get(&i.value.get_hir_id()).unwrap().clone(), + self.envs + .spans + .get(&i.value.get_hir_id()) + .unwrap() + .clone() + .into(), Type::Primitive(PrimitiveType::Array(Box::new(value_t.clone()), 0)), other.clone(), Type::Primitive(PrimitiveType::Array(Box::new(value_t), 0)), @@ -537,6 +580,7 @@ impl<'a, 'ar> Visitor<'a> for ConstraintContext<'ar> { self.visit_expression(&d.op); self.visit_identifier(&d.value); + // println!("envs: {:#?}", self.envs); match &self.envs.get_type(&d.op.get_hir_id()).unwrap().clone() { t @ Type::Struct(struct_t) => { self.envs.set_type(&d.op.get_hir_id(), t); @@ -556,7 +600,12 @@ impl<'a, 'ar> Visitor<'a> for ConstraintContext<'ar> { self.envs .diagnostics .push_error(Diagnostic::new_type_conflict( - self.envs.spans.get(&d.value.get_hir_id()).unwrap().clone(), + self.envs + .spans + .get(&d.value.get_hir_id()) + .unwrap() + .clone() + .into(), value_t.clone(), other.clone(), value_t, @@ -604,13 +653,35 @@ impl<'a, 'ar> Visitor<'a> for ConstraintContext<'ar> { } } + fn visit_for_in(&mut self, for_in: &'a ForIn) { + self.visit_expression(&for_in.expr); + + self.envs + .get_type(&for_in.expr.get_hir_id()) + .cloned() + .and_then(|expr_t| { + expr_t + .is_array() + .then(|| expr_t.try_as_primitive_type().unwrap()) + .and_then(|p| p.try_as_array()) + .map(|(inner_t, _size)| { + self.envs.set_type(&for_in.value.get_hir_id(), &inner_t) + }) + }); + + self.visit_body(&for_in.body); + + // assert expr to arr type + // set item to inner type; + } + fn visit_identifier_path(&mut self, id: &'a IdentifierPath) { self.visit_identifier(id.path.iter().last().unwrap()); } fn visit_identifier(&mut self, id: &Identifier) { // We set the type to resolution if any - if let Some(reso) = self.resolve_rec(&id.hir_id) { + if let Some(reso) = self.resolve(&id.hir_id) { if self.envs.get_type(&reso).is_some() { self.envs.set_type_eq(&id.get_hir_id(), &reso); } @@ -637,9 +708,10 @@ impl<'a, 'ar> Visitor<'a> for ConstraintContext<'ar> { | NativeOperatorKind::IDiv | NativeOperatorKind::IMul => PrimitiveType::Int64, NativeOperatorKind::FAdd - | NativeOperatorKind::FSub | NativeOperatorKind::FDiv - | NativeOperatorKind::FMul => PrimitiveType::Float64, + | NativeOperatorKind::FMul + | NativeOperatorKind::FSub => PrimitiveType::Float64, + NativeOperatorKind::Len => PrimitiveType::Int64, }; self.envs.set_type(&op.hir_id, &t.into()); diff --git a/src/lib/infer/monomorphize/monomorphizer.rs b/src/lib/infer/monomorphize/monomorphizer.rs index 72d923bf..44b8d3b4 100644 --- a/src/lib/infer/monomorphize/monomorphizer.rs +++ b/src/lib/infer/monomorphize/monomorphizer.rs @@ -397,15 +397,18 @@ impl<'a, 'b> VisitorMut<'a> for Monomorphizer<'b> { } fn visit_struct_decl(&mut self, s: &'a mut StructDecl) { - let old_hir_id = s.hir_id.clone(); + let old_hir_id = s.name.hir_id.clone(); - s.hir_id = self.duplicate_hir_id(&old_hir_id); + s.name.hir_id = self.duplicate_hir_id(&old_hir_id); if let Some(t) = self.root.type_envs.get_type(&old_hir_id) { - self.root.node_types.insert(s.hir_id.clone(), t.clone()); + self.root + .node_types + .insert(s.name.hir_id.clone(), t.clone()); } - self.trans_resolutions.insert(old_hir_id, s.hir_id.clone()); + self.trans_resolutions + .insert(old_hir_id, s.name.hir_id.clone()); s.defs.iter().for_each(|p| { let t = *s @@ -419,26 +422,29 @@ impl<'a, 'b> VisitorMut<'a> for Monomorphizer<'b> { self.root.node_types.insert(p.name.get_hir_id(), t); }); - self.structs.insert(s.name.get_name(), s.clone()); + self.structs.insert(s.name.name.clone(), s.clone()); } fn visit_struct_ctor(&mut self, s: &'a mut StructCtor) { - let old_hir_id = s.hir_id.clone(); + let old_hir_id = s.name.hir_id.clone(); - let mut s_decl = self.root.structs.get(&s.name.get_name()).unwrap().clone(); + let mut s_decl = self.root.structs.get(&s.name.name).unwrap().clone(); // TODO: Do that once self.visit_struct_decl(&mut s_decl); - s.hir_id = self.duplicate_hir_id(&old_hir_id); + s.name.hir_id = self.duplicate_hir_id(&old_hir_id); if let Some(t) = self.root.type_envs.get_type(&old_hir_id) { - self.root.node_types.insert(s.hir_id.clone(), t.clone()); + self.root + .node_types + .insert(s.name.hir_id.clone(), t.clone()); } - self.trans_resolutions.insert(old_hir_id, s.hir_id.clone()); + self.trans_resolutions + .insert(old_hir_id, s.name.hir_id.clone()); - self.visit_type(&mut s.name); + // self.visit_identifier(&mut s.name); s.defs = s .defs diff --git a/src/lib/infer/state.rs b/src/lib/infer/state.rs index f7ad34e4..c24718ed 100644 --- a/src/lib/infer/state.rs +++ b/src/lib/infer/state.rs @@ -3,7 +3,7 @@ use std::collections::{BTreeMap, HashMap}; use crate::{ diagnostics::{Diagnostic, Diagnostics}, hir::*, - parser::Span, + parser::span2::Span, ty::*, }; @@ -81,7 +81,7 @@ impl Envs { (Type::Func(src_f), Some(Type::Func(prev_f))) if !src_f.eq(&prev_f) => { if prev_f.is_solved() && src_f.is_solved() { self.diagnostics.push_error(Diagnostic::new_type_conflict( - self.spans.get(dest).unwrap().clone(), + self.spans.get(dest).unwrap().clone().into(), previous.clone().unwrap(), src.clone(), previous.unwrap(), @@ -92,7 +92,7 @@ impl Envs { (src, Some(previous)) if !src.eq(&previous) => { if previous.is_solved() && src.is_solved() { self.diagnostics.push_error(Diagnostic::new_type_conflict( - self.spans.get(dest).unwrap().clone(), + self.spans.get(dest).unwrap().clone().into(), previous.clone(), src.clone(), previous, @@ -112,7 +112,7 @@ impl Envs { (Type::Func(src_f), Some(Type::Func(prev_f))) if !src_f.eq(&prev_f) => { if prev_f.is_solved() && src_f.is_solved() { self.diagnostics.push_error(Diagnostic::new_type_conflict( - self.spans.get(src).unwrap().clone(), + self.spans.get(src).unwrap().clone().into(), previous.clone().unwrap(), src_t.clone(), previous.unwrap(), @@ -123,11 +123,11 @@ impl Envs { (src_t, Some(previous)) if !src_t.eq(&previous) => { if previous.is_solved() && src_t.is_solved() { self.diagnostics.push_error(Diagnostic::new_type_conflict( - self.spans.get(src).unwrap().clone(), + self.spans.get(src).unwrap().clone().into(), previous.clone(), src_t.clone(), previous, - src_t.clone(), + src_t, )); } } diff --git a/src/lib/parser/lexer.rs b/src/lib/parser/lexer.rs deleted file mode 100644 index 909ecc36..00000000 --- a/src/lib/parser/lexer.rs +++ /dev/null @@ -1,588 +0,0 @@ -use crate::diagnostics::Diagnostic; - -use super::{ParsingCtx, Token, TokenType}; - -bitflags! { - struct Sep: u32 { - const WS = 0b00000001; - const EOL = 0b00000010; - const NOTALPHANUM = 0b00000100; - } -} - -macro_rules! closure_vec { - ($($m:path),*,) => { - { - vec![ - $(Box::new($m as for<'r> fn(&'r mut Lexer<'a>) -> Option),)* - ] - } - }; -} - -pub struct Lexer<'a> { - ctx: &'a mut ParsingCtx, - pub input: Vec, - cur_idx: usize, - last_char: char, - cur_line: usize, - end: bool, - base_indent: u8, - accepted_operator_chars: Vec, -} - -impl<'a> Lexer<'a> { - pub fn new(ctx: &'a mut ParsingCtx) -> Lexer { - let mut input: Vec = ctx.get_current_file().content.chars().collect(); - - input.push('\0'); - - Lexer { - ctx, - last_char: input[0], - input, - cur_idx: 0, - cur_line: 1, - end: false, - base_indent: 0, - accepted_operator_chars: super::accepted_operator_chars(), - } - } - - pub fn next(&mut self) -> Token { - if self.end { - return self.new_token(TokenType::Eof, self.cur_idx, "".to_string()); - } - - if let Some(t) = self.try_indent() { - return t; - } - - while self.last_char == ' ' { - self.forward(1); - } - - self.discard_comment(); - - let v = closure_vec![ - Self::try_fn_keyword, - Self::try_let_keyword, - Self::try_mod_keyword, - Self::try_extern_keyword, - Self::try_if_keyword, - Self::try_else_keyword, - Self::try_then_keyword, - Self::try_for_keyword, - Self::try_struct_keyword, - Self::try_infix_keyword, - Self::try_use_keyword, - Self::try_trait_keyword, - Self::try_impl_keyword, - Self::try_parens, - Self::try_braces, - Self::try_array, - Self::try_type, - Self::try_array_decl, - Self::try_bool, - Self::try_ident, - Self::try_arrow, - Self::try_operator_ident, - Self::try_native_operator, - Self::try_digit, - Self::try_coma, - Self::try_dot, - Self::try_double_semi_colon, - Self::try_semi_colon, - Self::try_equal, - Self::try_this, - Self::try_string, - Self::try_end_of, - ]; - - for method in v { - if let Some(t) = method(self) { - return t; - } - } - - if self.cur_idx >= self.input.len() - 1 { - return self.new_token(TokenType::Eof, self.cur_idx, "".to_string()); - } - - self.ctx - .diagnostics - .push_error(Diagnostic::new_unexpected_token( - self.ctx.new_span(self.cur_idx, self.cur_idx), - )); - - self.end = true; - - self.new_token(TokenType::Eof, self.cur_idx, "".to_string()) - } - - fn has_separator(&self, token_len: usize, sep: Sep) -> bool { - if sep == Sep::empty() { - return true; - } - - let mut seps = vec![]; - - if sep & Sep::WS == Sep::WS { - seps.push(' '); - } - - if sep & Sep::EOL == Sep::EOL { - seps.push('\n'); - } - - if sep & Sep::NOTALPHANUM == Sep::NOTALPHANUM - && !self.input[self.cur_idx + token_len].is_alphanumeric() - { - seps.push(self.input[self.cur_idx + token_len]); - } - - seps.contains(&self.input[self.cur_idx + token_len]) - } - - fn discard_comment(&mut self) { - if self.last_char == '#' { - while self.last_char != '\n' && self.last_char != '\0' { - self.forward(1); - } - } - } - - fn forward(&mut self, n: usize) { - self.cur_idx += n; - - if self.cur_idx >= self.input.len() { - self.end = true; - - self.cur_idx = self.input.len() - 1; - - self.last_char = '\0'; - } else { - self.last_char = self.input[self.cur_idx]; - } - } - - fn new_token(&self, t: TokenType, start: usize, txt: String) -> Token { - Token { - t, - span: self.ctx.new_span(start, self.cur_idx - 1), - txt, - } - } - - fn match_consume(&mut self, s: &str, t: TokenType, end: Sep) -> Option { - // TODO: optimisation: test the first letter first with last_char - - if self - .input - .iter() - .skip(self.cur_idx) - .take(s.len()) - .collect::() - == *s - { - if !self.has_separator(s.len(), end) { - return None; - } - - let start = self.cur_idx; - - self.forward(s.len()); - - return Some(self.new_token(t, start, s.to_string())); - } - - None - } - - // Actual lexer methods - - fn try_arrow(&mut self) -> Option { - self.match_consume("->", TokenType::Arrow, Sep::empty()) - } - - fn try_fn_keyword(&mut self) -> Option { - self.match_consume("fn", TokenType::Fn, Sep::WS) - } - - fn try_let_keyword(&mut self) -> Option { - self.match_consume("let", TokenType::Let, Sep::WS) - } - - fn try_mod_keyword(&mut self) -> Option { - self.match_consume("mod", TokenType::Mod, Sep::WS) - } - - fn try_extern_keyword(&mut self) -> Option { - self.match_consume("extern", TokenType::Extern, Sep::WS) - } - - fn try_if_keyword(&mut self) -> Option { - self.match_consume("if", TokenType::If, Sep::WS) - } - - fn try_else_keyword(&mut self) -> Option { - self.match_consume("else", TokenType::Else, Sep::WS | Sep::EOL) - } - - fn try_for_keyword(&mut self) -> Option { - self.match_consume("for", TokenType::For, Sep::WS | Sep::EOL) - } - - fn try_struct_keyword(&mut self) -> Option { - self.match_consume("struct", TokenType::Struct, Sep::WS) - } - - fn try_then_keyword(&mut self) -> Option { - if self.last_char == 't' { - self.match_consume("then", TokenType::Then, Sep::WS) - } else if self.last_char == '=' { - self.match_consume("=>", TokenType::Then, Sep::empty()) - } else { - None - } - } - - fn try_infix_keyword(&mut self) -> Option { - self.match_consume("infix", TokenType::Infix, Sep::WS) - } - - fn try_use_keyword(&mut self) -> Option { - self.match_consume("use", TokenType::Use, Sep::WS) - } - - fn try_trait_keyword(&mut self) -> Option { - self.match_consume("trait", TokenType::Trait, Sep::WS) - } - - fn try_impl_keyword(&mut self) -> Option { - self.match_consume("impl", TokenType::Impl, Sep::WS) - } - - fn try_bool(&mut self) -> Option { - if self.last_char == 't' { - self.match_consume("true", TokenType::Bool(true), Sep::NOTALPHANUM) - } else if self.last_char == 'f' { - self.match_consume("false", TokenType::Bool(false), Sep::NOTALPHANUM) - } else { - None - } - } - - fn try_ident(&mut self) -> Option { - let start = self.cur_idx; - - if self.last_char.is_alphabetic() || self.last_char == '_' { - let mut identifier = vec![]; - - while self.last_char.is_alphanumeric() || self.last_char == '_' { - identifier.push(self.last_char); - - self.forward(1); - } - - // if is_keyword, return None - - return Some(self.new_token( - TokenType::Identifier(identifier.iter().collect()), - start, - identifier.iter().collect(), - )); - } - - None - } - - fn try_parens(&mut self) -> Option { - if self.last_char == '(' { - self.match_consume("(", TokenType::OpenParens, Sep::empty()) - } else if self.last_char == ')' { - self.match_consume(")", TokenType::CloseParens, Sep::empty()) - } else { - None - } - } - - fn try_braces(&mut self) -> Option { - if self.last_char == '{' { - self.match_consume("{", TokenType::OpenBrace, Sep::empty()) - } else if self.last_char == '}' { - self.match_consume("}", TokenType::CloseBrace, Sep::empty()) - } else { - None - } - } - - fn try_array(&mut self) -> Option { - if self.last_char == '[' { - self.match_consume("[", TokenType::OpenArray, Sep::empty()) - } else if self.last_char == ']' { - self.match_consume("]", TokenType::CloseArray, Sep::empty()) - } else { - None - } - } - - fn try_array_decl(&mut self) -> Option { - self.match_consume("[]", TokenType::ArrayType, Sep::empty()) - } - - fn try_coma(&mut self) -> Option { - self.match_consume(",", TokenType::Coma, Sep::empty()) - } - - fn try_dot(&mut self) -> Option { - self.match_consume(".", TokenType::Dot, Sep::empty()) - } - - fn try_double_semi_colon(&mut self) -> Option { - self.match_consume("::", TokenType::DoubleSemiColon, Sep::empty()) - } - - fn try_semi_colon(&mut self) -> Option { - self.match_consume(":", TokenType::SemiColon, Sep::empty()) - } - - fn try_equal(&mut self) -> Option { - self.match_consume("=", TokenType::Equal, Sep::empty()) - } - - fn try_this(&mut self) -> Option { - if self.last_char == '@' { - self.forward(1); - - let res = self.new_token( - TokenType::Identifier("this".to_string()), - self.cur_idx, - "this".to_string(), - ); - - return Some(res); - } - - None - } - - fn try_digit(&mut self) -> Option { - let start = self.cur_idx; - let mut is_neg = false; - - if self.last_char == '-' { - is_neg = true; - - self.forward(1); - } - - let mut is_float = false; - - if self.last_char.is_digit(10) { - let mut number = vec![]; - - while self.last_char.is_digit(10) || self.last_char == '.' { - if self.last_char == '.' { - is_float = true; - } - - number.push(self.last_char); - - self.forward(1); - } - - let mut nb_str: String = number.iter().collect(); - - if is_neg { - nb_str.insert(0, '-'); - } - - let nb = if is_float { - TokenType::Float(nb_str.parse::().unwrap()) - } else { - TokenType::Number(nb_str.parse::().unwrap()) - }; - - // if is_keyword, return None - - return Some(self.new_token(nb, start, nb_str)); - } - - None - } - - fn try_operator_ident(&mut self) -> Option { - let start = self.cur_idx; - - let mut identifier = vec![]; - - if self - .accepted_operator_chars - .iter() - .any(|c| *c == self.last_char) - { - while self - .accepted_operator_chars - .iter() - .any(|c| *c == self.last_char) - { - identifier.push(self.last_char); - - self.forward(1); - } - - // if is_keyword, return None - - return Some(self.new_token( - TokenType::Operator(identifier.iter().collect()), - start, - identifier.iter().collect(), - )); - } - - None - } - - fn try_native_operator(&mut self) -> Option { - if self.last_char == '~' { - let mut res = None; - let ops = vec![ - "~IAdd", "~ISub", "~IMul", "~IDiv", "~FAdd", "~FSub", "~FMul", "~FDiv", "~IEq", - "~Igt", "~Ige", "~Ilt", "~Ile", "~FEq", "~Fgt", "~Fge", "~Flt", "~Fle", "~BEq", - ]; - - for op in ops { - res = self.match_consume(op, TokenType::NativeOperator(op.to_string()), Sep::WS); - - if res.is_some() { - break; - } - } - - res - } else { - None - } - } - - fn try_end_of(&mut self) -> Option { - if self.last_char == '\n' { - let res = Some(Token { - t: TokenType::Eol, - span: self.ctx.new_span(self.cur_idx, self.cur_idx), - txt: "\n".to_string(), - }); - - self.forward(1); - - self.cur_line += 1; - - return res; - } - - None - } - - fn try_indent(&mut self) -> Option { - let save = self.cur_idx; - - if self.cur_idx > 0 && self.input[self.cur_idx - 1] == '\n' { - let mut indent = 0; - - while self.input[self.cur_idx] == ' ' { - let mut count = 0; - - while self.input[self.cur_idx] == ' ' - && (count < self.base_indent || self.base_indent == 0) - { - self.forward(1); - - count += 1; - } - - if self.base_indent == 0 { - self.base_indent = count; - } - - if count == self.base_indent { - indent += 1; - } - } - - if indent > 0 { - return Some(self.new_token(TokenType::Indent(indent), save, " ".to_string())); - } - - self.cur_idx = save; - } - - None - } - - fn try_string(&mut self) -> Option { - let start = self.cur_idx; - - if self.last_char == '"' { - let mut s = vec![]; - - self.forward(1); - - while self.last_char != '"' { - s.push(self.last_char); - - self.forward(1); - } - - self.forward(1); - - let res: String = s.iter().collect(); - - return Some(self.new_token(TokenType::String(res.clone()), start, res)); - } - - None - } - - fn try_type(&mut self) -> Option { - let start = self.cur_idx; - - if self.last_char.is_alphabetic() && self.last_char.is_uppercase() { - let mut identifier = vec![]; - - while self.last_char.is_alphanumeric() { - identifier.push(self.last_char); - - self.forward(1); - } - - // if is_keyword, return None - - return Some(self.new_token( - TokenType::Type(identifier.iter().collect()), - start, - identifier.iter().collect(), - )); - } - - None - } - - // - - pub fn collect(&mut self) -> Vec { - let mut res = vec![]; - - loop { - let next = self.next(); - - res.push(next.clone()); - - if next.t == TokenType::Eof { - break; - } - } - - res - } -} diff --git a/src/lib/parser/mod.rs b/src/lib/parser/mod.rs index 792035fd..4ae51779 100644 --- a/src/lib/parser/mod.rs +++ b/src/lib/parser/mod.rs @@ -1,85 +1,1010 @@ -use colored::*; - -mod lexer; -mod parser_impl; -mod parsing_context; -mod source_file; -mod span; -mod token; - -pub use lexer::*; -pub use parser_impl::*; -pub use parsing_context::*; -pub use source_file::*; -pub use span::*; -pub use token::*; - -use crate::ast::visit::*; -use crate::{ast::ast_print::AstPrintContext, diagnostics::Diagnostic}; - -fn parse_generic(ctx: &mut ParsingCtx, mut f: F) -> Result<(R, Vec), Diagnostic> +use std::{ + collections::{BTreeMap, HashMap}, + path::PathBuf, +}; + +use nom::{ + branch::alt, + bytes::complete::{tag, take_while}, + character::complete::{alphanumeric0, char, line_ending, one_of, satisfy, space0, space1}, + combinator::{eof, map, opt, peek, recognize}, + error::{make_error, ErrorKind, ParseError, VerboseError}, + error_position, + multi::{many0, many1, separated_list0, separated_list1}, + sequence::{delimited, preceded, terminated, tuple}, + Err, IResult, +}; + +use nom_locate::{position, LocatedSpan}; + +use crate::{ + ast::{ + tree::{self, *}, + NodeId, + }, + diagnostics::Diagnostic, + ty::{FuncType, PrimitiveType, StructType, Type}, +}; + +pub type Parser<'a> = LocatedSpan<&'a str, ParserCtx>; + +type Res = IResult>; + +pub mod parsing_context; +pub mod source_file; +pub mod span; +pub mod span2; + +pub use parsing_context::ParsingCtx; +pub use source_file::SourceFile; +pub use span::Span as OldSpan; +pub use span2::Span; + +#[cfg(test)] +mod tests; + +// TODO: +// - add support for escaped string +// - fix typing (check every types) + +pub fn accepted_operator_chars() -> Vec { + return vec!['+', '-', '/', '*', '|', '<', '>', '=', '!', '$', '@', '&']; +} + +#[derive(Debug, Clone)] +pub struct ParserCtx { + files: HashMap, + cur_file_path: PathBuf, + identities: BTreeMap, + operators_list: HashMap, + block_indent: usize, + first_indent: Option, + next_node_id: NodeId, + structs: HashMap, +} + +impl ParserCtx { + pub fn new(file_path: PathBuf) -> Self { + Self { + files: HashMap::new(), + cur_file_path: file_path, + identities: BTreeMap::new(), + operators_list: HashMap::new(), + block_indent: 0, + first_indent: None, + next_node_id: 0, + structs: HashMap::new(), + } + } + + #[cfg(test)] + pub fn new_with_operators(file_path: PathBuf, operators: HashMap) -> Self { + Self { + files: HashMap::new(), + cur_file_path: file_path, + identities: BTreeMap::new(), + operators_list: operators, + block_indent: 0, + first_indent: None, + next_node_id: 0, + structs: HashMap::new(), + } + } + + pub fn new_from(&self, name: &str) -> Self { + Self { + files: HashMap::new(), + cur_file_path: self + .cur_file_path + .parent() + .unwrap() + .join(name.to_owned() + ".rk"), + identities: BTreeMap::new(), + operators_list: HashMap::new(), + block_indent: 0, + first_indent: None, + next_node_id: self.next_node_id, + structs: HashMap::new(), + } + } + + pub fn new_identity(&mut self, span: Span) -> NodeId { + let node_id = self.next_node_id; + + self.next_node_id += 1; + + self.identities.insert(node_id, span); + + node_id + } + + pub fn current_file_path(&self) -> &PathBuf { + &self.cur_file_path + } + + pub fn operators(&self) -> &HashMap { + &self.operators_list + } + + pub fn add_operator(&mut self, op: String, prec: u8) { + self.operators_list.insert(op, prec); + } + + pub fn identities(&self) -> BTreeMap { + self.identities.clone() + } + + pub fn operators_list(&self) -> HashMap { + self.operators_list.clone() + } + + pub fn files(&self) -> HashMap { + self.files.clone() + } +} + +/* pub fn create_parser(s: &str) -> Parser<'_> { + LocatedSpan::new_extra(s, ParserCtx::new(PathBuf::from(""))) +} + */ +/* pub fn create_parser_with_filename(s: &str, path: PathBuf) -> Parser<'_> { + LocatedSpan::new_extra(s, ParserCtx::new(path)) +} + */ +pub fn parse_root(input: Parser) -> Res { + // TODO: move eof check in parse_mod + map(terminated(parse_mod, eof), Root::new)(input) +} + +pub fn parse_mod(input: Parser) -> Res { + map( + many1(terminated( + parse_top_level, + many1(preceded(opt(parse_comment), line_ending)), + )), + Mod::new, + )(input) +} + +pub fn parse_top_level(input: Parser) -> Res { + alt(( + preceded( + terminated(tag("extern"), space1), + map(parse_prototype, TopLevel::new_prototype), + ), + parse_infix, + map(parse_use, TopLevel::new_use), + map(parse_struct_decl, TopLevel::new_struct), + map(parse_trait, TopLevel::new_trait), + map(parse_impl, TopLevel::new_impl), + map(parse_fn, TopLevel::new_function), + map(parse_mod_decl, |(name, mod_)| TopLevel::new_mod(name, mod_)), + ))(input) +} + +pub fn parse_comment(input: Parser) -> Res { + let (input, _) = tuple((tag("#"), many0(satisfy(|c: char| c != '\n'))))(input)?; + + Ok((input, ())) +} + +pub fn parse_eol(input: Parser) -> Res { + let (input, _) = tuple((opt(parse_comment), line_ending))(input)?; + + Ok((input, ())) +} + +pub fn parse_mod_decl(input: Parser) -> Res { + let (mut input, mod_name) = preceded(terminated(tag("mod"), space1), parse_identifier)(input)?; + + let mut new_ctx = input.extra.new_from(&mod_name.name); + let file_path = new_ctx.current_file_path().to_str().unwrap().to_string(); + + let file = SourceFile::from_file(file_path).unwrap(); // FIXME: ERRORS ARE swallowed HERE + new_ctx + .files + .insert(new_ctx.current_file_path().clone(), file); + + let content = std::fs::read_to_string(&new_ctx.current_file_path()).unwrap(); + + let new_parser = Parser::new_extra(&content, new_ctx); + + use nom::Finish; + // FIXME: Errors are swallowed here + let (input2, mod_) = parse_mod(new_parser).finish().unwrap(); + + // hydrate `input` with the new parser's operators + // TODO: handle duplicate operators + input + .extra + .operators_list + .extend(input2.extra.operators_list); + + // extend identities + input.extra.identities.extend(input2.extra.identities); + input.extra.next_node_id = input2.extra.next_node_id; + input.extra.files.extend(input2.extra.files); + + Ok((input, (mod_name, mod_))) +} + +pub fn parse_trait(input: Parser) -> Res { + map( + tuple(( + terminated(tag("trait"), space1), + parse_type, + many0(delimited(space1, parse_type, space0)), + many0(line_ending), + indent(separated_list1( + line_ending, + preceded(parse_block_indent, parse_prototype), + )), + )), + |(_, name, types, _, defs)| Trait::new(name, types, defs), + )(input) +} + +pub fn parse_impl(input: Parser) -> Res { + map( + tuple(( + terminated(tag("impl"), space1), + parse_type, + many0(delimited(space1, parse_type, space0)), + many0(line_ending), + indent(separated_list1( + line_ending, + preceded(parse_block_indent, parse_fn), + )), + )), + |(_, name, types, _, defs)| Impl::new(name, types, defs), + )(input) +} + +pub fn parse_struct_decl(input: Parser) -> Res { + let (mut input, struct_decl) = map( + tuple(( + terminated(tag("struct"), space1), + parse_capitalized_identifier, + many0(line_ending), + indent(separated_list0( + line_ending, + preceded(parse_block_indent, parse_prototype), + )), + )), + |(_tag, name, _, defs)| StructDecl::new(name, defs), + )(input)?; + + let struct_t: StructType = struct_decl.clone().into(); + + input + .extra + .structs + .insert(struct_decl.name.name.clone(), struct_t.into()); + + Ok((input, struct_decl)) +} + +pub fn parse_use(input: Parser) -> Res { + preceded( + terminated(tag("use"), space1), + map( + tuple((parse_identity, parse_identifier_path)), + |(node_id, ident)| Use::new(ident, node_id), + ), + )(input) +} + +pub fn parse_infix(input: Parser) -> Res { + let (input, (parsed_op, pred)) = preceded( + terminated(tag("infix"), space1), + tuple(( + terminated(many1(allowed_operator_chars), space1), + parse_number, + )), + )(input)?; + + let (input, pos) = position(input)?; + + let (mut input, node_id) = new_identity(input, &pos); + + let op = parsed_op.join(""); + + input.extra.add_operator(op.clone(), pred.as_i64() as u8); + + let op = Operator(Identifier { name: op, node_id }); + + Ok((input, TopLevel::new_infix(op, pred.as_i64() as u8))) +} + +pub fn parse_identifier_or_operator(input: Parser) -> Res { + alt((parse_identifier, map(parse_operator, |op| op.0)))(input) +} + +pub fn parse_prototype(input: Parser) -> Res { + map( + tuple(( + parse_identity, + terminated( + parse_identifier_or_operator, + delimited(space0, tag("::"), space0), + ), + parse_signature, + )), + |(node_id, name, signature)| Prototype { + node_id, + name, + signature, + }, + )(input) +} + +pub fn parse_fn(input: Parser) -> Res { + map( + tuple(( + parse_identity, + terminated( + tuple(( + parse_identifier_or_operator, + many0(preceded(space1, parse_identifier)), + )), + delimited(space0, char('='), space0), + ), + parse_body, + )), + |(node_id, (name, arguments), body)| FunctionDecl { + node_id, + name, + body, + signature: FuncType::from_args_nb(arguments.len()), // FIXME: Should not generate random signature + arguments, + }, + )(input) +} + +fn indent<'a, O, E, F>(mut parser: F) -> impl FnMut(Parser<'a>) -> IResult, O, E> where - F: FnMut(&mut Parser) -> Result, + F: nom::Parser, O, E>, { - let tokens = Lexer::new(ctx).collect(); + move |mut input: Parser<'a>| { + if let Some(indent) = input.extra.first_indent { + input.extra.block_indent += indent; + } + + let (mut input, output) = parser.parse(input)?; + + if let Some(indent) = input.extra.first_indent { + input.extra.block_indent -= indent; + } - // Debug tokens - if ctx.config.show_tokens { - println!("TOKENS {:#?}", tokens); + Ok((input, output)) } +} - ctx.return_if_error()?; +pub fn parse_block_indent(input: Parser) -> Res { + let (mut input, indent) = space1(input)?; + let indent_len = indent.fragment().len(); - let mut parser = Parser::new(tokens.clone(), ctx); + if input.extra.first_indent == None { + input.extra.first_indent = Some(indent_len); + input.extra.block_indent = indent_len; + } - let ast = match f(&mut parser) { - Ok(ast) => ast, - Err(e) => { - ctx.return_if_error()?; + if indent_len == input.extra.block_indent { + Ok((input, indent_len)) + } else { + Err(nom::Err::Error(ParseError::from_error_kind( + input, + ErrorKind::Tag, + ))) + } +} - return Err(e); - } +pub fn parse_body(input: Parser) -> Res { + let (input, opt_eol) = opt(line_ending)(input)?; // NOTE: should not fail + + if opt_eol.is_some() { + indent(map( + separated_list1( + many1(parse_eol), + preceded(parse_block_indent, parse_statement), + ), + Body::new, + ))(input) + } else { + map(parse_statement, |stmt| Body::new(vec![stmt]))(input) + } +} + +pub fn parse_statement(input: Parser) -> Res { + alt(( + map(parse_if, Statement::new_if), + map(parse_for, Statement::new_for), + map(parse_assign, Statement::new_assign), + map(parse_expression, Statement::new_expression), + ))(input) +} + +pub fn parse_if(input: Parser) -> Res { + map( + tuple(( + parse_identity, + terminated(tag("if"), space1), + terminated(parse_expression, space0), + many1(line_ending), + parse_then, + parse_body, + opt(tuple((line_ending, parse_else))), + )), + |(node_id, _if_, cond, _, _, body, else_)| { + If::new(node_id, cond, body, else_.map(|(_, else_)| Box::new(else_))) + }, + )(input.clone()) +} + +pub fn parse_then(input: Parser) -> Res { + // NOTE: This is a tweek for then block that are at indent 0 (i.e. in the test files) + let (input, indent) = if input.extra.first_indent.is_some() && input.extra.block_indent > 0 { + parse_block_indent(input)? + } else { + (input, 0) }; - Ok((ast, tokens)) + if indent == input.extra.block_indent { + let (input, _) = terminated(tag("then"), space0)(input)?; + + Ok((input, ())) + } else { + Err(nom::Err::Error(ParseError::from_error_kind( + input, + ErrorKind::Tag, + ))) + } } -pub fn parse_root(ctx: &mut ParsingCtx) -> Result { - if ctx.config.verbose { - println!(" -> Compiling {}", "root".green()); +pub fn parse_else(input: Parser) -> Res { + // NOTE: This is a tweek for else block that are at indent 0 (i.e. in the test files) + let (input, indent) = if input.extra.first_indent.is_some() && input.extra.block_indent > 0 { + parse_block_indent(input)? + } else { + (input, 0) + }; + + if indent == input.extra.block_indent { + alt(( + map( + tuple(( + terminated(tag("else"), space1), + terminated(parse_if, space0), + )), + |(_, if_)| Else::If(if_), + ), + map( + tuple(( + terminated(tag("else"), space0), + terminated(parse_body, space0), + )), + |(_, body)| Else::Body(body), + ), + ))(input) + } else { + Err(nom::Err::Error(ParseError::from_error_kind( + input, + ErrorKind::Tag, + ))) } +} - debug!(" -> Parsing Root"); +pub fn parse_for(input: Parser) -> Res { + alt((map(parse_for_in, For::In), map(parse_while, For::While)))(input) +} - let (mut ast, tokens) = parse_generic(ctx, |p| p.run_root())?; +pub fn parse_for_in(input: Parser) -> Res { + map( + tuple(( + terminated(tag("for"), space1), + terminated(parse_identifier, space0), + terminated(tag("in"), space0), + terminated(parse_expression, space0), + parse_body, + )), + |(_, var, _, expr, body)| ForIn::new(var, expr, body), + )(input) +} - ast.r#mod.tokens = tokens; +pub fn parse_while(input: Parser) -> Res { + map( + tuple(( + terminated(tag("while"), space1), + terminated(parse_expression, space0), + parse_body, + )), + |(_, cond, body)| While::new(cond, body), + )(input) +} - // Debug ast - if ctx.config.show_ast { - AstPrintContext::new().visit_root(&ast); - } +pub fn parse_assign(input: Parser) -> Res { + map( + tuple(( + opt(terminated(tag("let"), space1)), + terminated(parse_assign_left_side, space0), + terminated(tag("="), space0), + terminated(parse_expression, space0), + )), + |(opt_let, var, _, expr)| Assign::new(var, expr, opt_let.is_some()), + )(input) +} - Ok(ast) +pub fn parse_assign_left_side(input: Parser) -> Res { + let (input, expr) = parse_expression(input)?; + + let res = if expr.is_dot() { + AssignLeftSide::Dot(expr) + } else if expr.is_indice() { + AssignLeftSide::Indice(expr) + } else if expr.is_identifier() { + AssignLeftSide::Identifier(expr) + } else { + return Err(nom::Err::Error(ParseError::from_error_kind( + input, + ErrorKind::Tag, + ))); + }; + + Ok((input, res)) +} + +pub fn parse_expression(input: Parser) -> Res { + alt(( + map( + tuple(( + parse_unary, + delimited(space0, parse_operator, space0), + parse_expression, + )), + |(l, op, r)| Expression::new_binop(l, op, r), + ), + map(parse_unary, Expression::new_unary), + map(parse_struct_ctor, Expression::new_struct_ctor), + map(parse_native_operator, |(op, id1, id2)| { + Expression::new_native_operator(op, id1, id2) + }), + // TODO: Return + ))(input) } -pub fn parse_mod(name: String, ctx: &mut ParsingCtx) -> Result { - debug!(" -> Parsing Mod {}", name); +pub fn parse_native_operator( + input: Parser, +) -> Res { + map( + tuple(( + preceded( + tag("~"), + alt(( + tag("IAdd"), + tag("ISub"), + tag("IMul"), + tag("IDiv"), + tag("FAdd"), + tag("FSub"), + tag("FMul"), + tag("FDiv"), + tag("IEq"), + tag("Igt"), + tag("Ige"), + tag("Ilt"), + tag("Ile"), + tag("FEq"), + tag("Fgt"), + tag("Fge"), + tag("Flt"), + tag("Fle"), + tag("BEq"), + tag("Len"), + )), + ), + preceded(space1, parse_identifier), + preceded(space1, parse_identifier), + )), + |(tag, id1, id2)| { + let (_input, node_id) = new_identity(input.clone(), &tag); + ( + NativeOperator::new(node_id, NativeOperatorKind::from_str(tag.fragment())), + id1, + id2, + ) + }, + )(input.clone()) +} + +pub fn parse_struct_ctor(input: Parser) -> Res { + map( + tuple(( + // parse_identity, + terminated(parse_capitalized_identifier, line_ending), + indent(separated_list0( + line_ending, + preceded( + parse_block_indent, + tuple(( + terminated(parse_identifier, delimited(space0, tag(":"), space0)), + parse_expression, + )), + ), + )), + )), + |(name, decls)| StructCtor::new(name, decls.into_iter().collect()), + )(input) +} + +pub fn parse_unary(input: Parser) -> Res { + map(parse_primary, UnaryExpr::new_primary)(input) +} + +pub fn parse_primary(input: Parser) -> Res { + map( + tuple((parse_identity, parse_operand, many0(parse_secondary))), + |(node_id, op, secs)| PrimaryExpr::new(node_id, op, secs), + )(input) +} - let current_file = ctx.get_current_file(); +pub fn parse_secondary(input: Parser) -> Res { + alt(( + map(parse_indice, SecondaryExpr::Indice), + map(parse_dot, SecondaryExpr::Dot), + map(parse_arguments, SecondaryExpr::Arguments), + ))(input) +} - ctx.resolve_and_add_file(name)?; +pub fn parse_arguments(input: Parser) -> Res { + alt(( + map( + tuple(( + terminated(tag("("), space0), + separated_list0(tuple((space0, tag(","), space0)), parse_argument), + terminated(tag(")"), space0), + )), + |(_, args, _)| args, + ), + map( + tuple(( + space1, + separated_list1( + tuple((space0, terminated(tag(","), space0), space0)), + parse_argument, + ), + )), + |(_, args)| args, + ), + ))(input) +} - let (mut ast, tokens) = parse_generic(ctx, |p| p.run_mod())?; +pub fn parse_argument(input: Parser) -> Res { + map(parse_unary, Argument::new)(input) +} - ctx.current_file = Some(current_file.file_path); +pub fn parse_indice(input: Parser) -> Res> { + map( + tuple(( + terminated(tag("["), space0), + terminated(parse_expression, space0), + terminated(tag("]"), space0), + )), + |(_, index, _)| Box::new(index), + )(input) +} - ast.tokens = tokens; +pub fn parse_dot(input: Parser) -> Res { + map( + tuple(( + terminated(tag("."), space0), + terminated(parse_identifier, space0), + )), + |(_, ident)| ident, + )(input) +} + +pub fn parse_operand(input: Parser) -> Res { + alt(( + map(parse_literal, Operand::new_literal), + map(parse_identifier_path, Operand::new_identifier_path), + map( + delimited( + terminated(tag("("), space0), + parse_expression, + terminated(space0, tag(")")), + ), + Operand::new_expression, + ), + ))(input) +} - // Debug ast - if ctx.config.show_ast { - AstPrintContext::new().visit_mod(&ast); +pub fn parse_identifier_path(input: Parser) -> Res { + map( + separated_list1( + tag("::"), + alt(( + map(tuple((parse_identity, tag("(*)"))), |(node_id, _)| { + Identifier::new("(*)".to_string(), node_id) + }), + parse_identifier, + )), + ), + IdentifierPath::new, + )(input) +} + +pub fn parse_capitalized_identifier(input: Parser) -> Res { + let (input, (node_id, txt)) = tuple((parse_identity, parse_capitalized_text))(input)?; + + Ok(( + input, + Identifier { + name: txt.to_string(), + node_id, + }, + )) +} + +pub fn parse_identifier(input: Parser) -> Res { + let (input, ident_parsed) = + recognize(many1(one_of("abcdefghijklmnopqrstuvwxyz_0123456789")))(input)?; + + let (input, node_id) = new_identity(input, &ident_parsed); + + Ok(( + input, + Identifier { + name: ident_parsed.to_string(), + node_id, + }, + )) +} + +pub fn parse_operator(input: Parser) -> Res { + let (input, parsed_op) = recognize(many1(one_of(LocatedSpan::new( + // We parse any accepted operators chars here, and then check if it is a valid operator later + crate::parser::accepted_operator_chars() + .iter() + .cloned() + .collect::() + .as_str(), + ))))(input)?; + + if parsed_op.to_string() == "=" { + return Err(Err::Error(error_position!(input, ErrorKind::Eof))); } + let (input, pos) = position(input)?; + + let (input, node_id) = new_identity(input, &pos); + + Ok(( + input, + Operator(Identifier { + name: parsed_op.to_string(), + node_id, + }), + )) +} + +pub fn parse_literal(input: Parser) -> Res { + alt(( + parse_bool, + parse_float, + parse_number, + parse_array, + parse_string, + ))(input) +} + +pub fn parse_string(input: Parser) -> Res { + map( + tuple(( + parse_identity, + terminated(tag("\""), space0), + recognize(take_while(|c: char| c != '"')), + terminated(tag("\""), space0), + )), + |(node_id, _, s, _)| Literal::new_string(String::from(*s.fragment()), node_id), + )(input) +} + +pub fn parse_array(input: Parser) -> Res { + map( + tuple(( + parse_identity, + terminated(tag("["), space0), + separated_list0( + tuple((space0, terminated(tag(","), space0), space0)), + parse_expression, + ), + terminated(tag("]"), space0), + )), + |(node_id, _, elements, _)| { + Literal::new_array(Array::new(elements.into_iter().collect()), node_id) + }, + )(input) +} + +pub fn parse_bool(input: Parser) -> Res { + let (input, bool_parsed) = alt((tag("true"), tag("false")))(input)?; + + let num: bool = bool_parsed + .parse() + .map_err(|_| Err::Error(make_error(input.clone(), ErrorKind::Alpha)))?; + + let (input, node_id) = new_identity(input, &bool_parsed); + + Ok((input, Literal::new_bool(num, node_id))) +} + +pub fn parse_float(input: Parser) -> Res { + let (input, float_parsed) = + recognize(tuple((parse_number, char('.'), opt(parse_number))))(input)?; + + let num: f64 = float_parsed + .parse() + .map_err(|_| Err::Error(make_error(input.clone(), ErrorKind::Digit)))?; + + let (input, node_id) = new_identity(input, &float_parsed); + + Ok((input, Literal::new_float(num, node_id))) +} + +pub fn parse_number(input: Parser) -> Res { + let (input, parsed) = take_while(is_digit)(input)?; + + let num: i64 = parsed + .parse() + .map_err(|_| Err::Error(make_error(input.clone(), ErrorKind::Digit)))?; + + let (input, node_id) = new_identity(input, &parsed); + + Ok((input, Literal::new_number(num, node_id))) +} + +// Types + +pub fn parse_signature(input: Parser) -> Res { + let (input, parsed) = tuple(( + parse_type, + many0(preceded(delimited(space0, tag("->"), space0), parse_type)), + ))(input)?; + + let mut types = vec![parsed.0]; + + types.extend(parsed.1); + + let ret = types.pop().unwrap(); + + Ok((input, FuncType::new(types, ret))) +} + +pub fn parse_type(input: Parser) -> Res { + let (input, ty) = alt(( + map( + terminated( + one_of("abcdefghijklmnopqrstuvwxyz"), + peek(alt((space1, line_ending, eof))), + ), + |c| Type::ForAll(String::from(c)), + ), + map(delimited(tag("["), parse_type, tag("]")), |t| { + Type::Primitive(PrimitiveType::Array( + Box::new(t), + 0, // FIXME + )) + }), + map( + alt(( + map(tag("Bool"), |_| PrimitiveType::Bool), + map(tag("Int64"), |_| PrimitiveType::Int64), + map(tag("Float64"), |_| PrimitiveType::Float64), + map(tag("String"), |_| PrimitiveType::String), + )), + |t| Type::from(t), + ), + map(parse_struct_type, Type::Struct), + map(parse_capitalized_text, Type::Trait), + ))(input)?; + + Ok((input, ty)) +} + +pub fn parse_struct_type(input: Parser) -> Res { + let (input, name) = parse_capitalized_text(input)?; + + let ty = if let Some(struct_t) = input.extra.structs.get(&name) { + struct_t.as_struct_type() + } else { + return Err(nom::Err::Error(ParseError::from_error_kind( + input, + ErrorKind::Tag, + ))); + }; + + Ok((input, ty)) +} + +pub fn parse_capitalized_text(input: Parser) -> Res { + let (input, parsed) = tuple((satisfy(char::is_uppercase), alphanumeric0))(input)?; + + let txt = + format!("{}", parsed.0) + &String::from_utf8(parsed.1.bytes().collect::>()).unwrap(); + + Ok((input, txt)) +} + +// Helpers + +fn is_digit(c: char) -> bool { + c.is_numeric() +} + +fn new_identity<'a>(mut input: Parser<'a>, parsed: &Parser<'a>) -> (Parser<'a>, NodeId) { + let node_id = input.extra.new_identity(Span::from(parsed.clone())); + + (input, node_id) +} + +fn parse_identity(input: Parser) -> Res { + let (input, pos) = position(input)?; + + let (input, node_id) = new_identity(input, &pos); + + Ok((input, node_id)) +} + +pub fn allowed_operator_chars(input: Parser) -> Res { + let (input, c) = one_of(LocatedSpan::new( + crate::parser::accepted_operator_chars() + .iter() + .map(|c| c.to_string()) + .collect::>() + .join("") + .as_str(), + ))(input)?; + + Ok((input, c.to_string())) +} + +pub fn parse(parsing_ctx: &mut ParsingCtx) -> Result { + use nom::Finish; + + let content = &parsing_ctx.get_current_file().content; + + let parser = LocatedSpan::new_extra( + content.as_str(), + ParserCtx::new(parsing_ctx.get_current_file().file_path.clone()), + ); + + let ast = parse_root(parser).finish(); + + let ast = match ast { + Ok((ctx, mut ast)) => { + parsing_ctx.identities = ctx.extra.identities(); + parsing_ctx.files = ctx.extra.files(); + + ast.operators_list = ctx.extra.operators_list(); + ast.spans = ctx.extra.identities().into_iter().collect(); + + // Debug ast + if parsing_ctx.config.show_ast { + ast.print(); + } + + Ok(ast) + } + Err(e) => { + let diagnostic = Diagnostic::from(e); + + parsing_ctx.diagnostics.push_error(diagnostic.clone()); + + Err(diagnostic) + } + }?; + + parsing_ctx.return_if_error()?; + Ok(ast) } diff --git a/src/lib/parser/parser_impl.rs b/src/lib/parser/parser_impl.rs deleted file mode 100644 index 945c3ae2..00000000 --- a/src/lib/parser/parser_impl.rs +++ /dev/null @@ -1,1256 +0,0 @@ -use std::{collections::HashMap, convert::TryInto}; - -use crate::{ast::*, diagnostics::Diagnostic, parser::*, resolver::ResolutionMap, ty::*}; - -type Error = Diagnostic; - -macro_rules! expect { - ($tok:expr, $self:expr) => { - if $tok != $self.cur_tok().t { - error_expect!($tok, $self); - } else { - let cur_tok = $self.cur_tok(); - - $self.consume(); - - cur_tok - } - }; -} - -macro_rules! expect_or_restore { - ($tok:expr, $self:expr) => { - if $self.cur_tok().t != $tok { - $self.restore(); - - error_expect!($tok, $self); - } else { - let cur_tok = $self.cur_tok(); - - $self.consume(); - - cur_tok - } - }; -} - -macro_rules! error_expect { - ($expected:expr, $self:expr) => { - trace!( - "Error expect: want {:?} got {:?}", - $expected, - $self.cur_tok().t - ); - - // This is not the error! macro from env_logger, see below - error!( - format!("Expected {:?} but got {:?}", $expected, $self.cur_tok().t), - $self - ); - }; -} - -macro_rules! error { - ($msg:expr, $self:expr) => { - return Err(Diagnostic::new_syntax_error( - $self.cur_tok().span.clone(), - $msg, - )) - }; -} - -macro_rules! try_or_restore { - ($expr:expr, $self:expr) => { - match $expr { - Ok(t) => t, - Err(e) => { - $self.restore(); - - return Err(e); - } - } - }; -} - -macro_rules! try_or_restore_and { - ($expr:expr, $and:expr, $self:expr) => { - match $expr { - Ok(t) => t, - Err(_e) => { - $self.restore(); - - #[allow(unreachable_code)] - return $and; - } - } - }; -} - -pub trait Parse { - fn parse(ctx: &mut Parser) -> Result - where - Self: Sized; -} - -// TODO: Create getters and setters instead of exposing publicly -pub struct Parser<'a> { - ctx: &'a mut ParsingCtx, - pub input: Vec, - pub tokens: Vec, - pub cur_tok_id: TokenId, - save: Vec, - pub block_indent: u8, - func_sigs: HashMap, - pub struct_types: HashMap, -} - -impl<'a> Parser<'a> { - pub fn new(tokens: Vec, ctx: &'a mut ParsingCtx) -> Parser { - let input: Vec = ctx.get_current_file().content.chars().collect(); - - Parser { - ctx, - input, - tokens, - save: vec![0], - cur_tok_id: 0, - block_indent: 0, - func_sigs: HashMap::new(), - struct_types: HashMap::new(), - } - } - - pub fn run_root(&mut self) -> Result { - Root::parse(self) - } - - pub fn run_mod(&mut self) -> Result { - Mod::parse(self) - } - - pub fn cur_tok(&self) -> Token { - match self.tokens.get(self.cur_tok_id as usize) { - Some(a) => a.clone(), - _ => Token::eof(), - } - } - - pub fn consume(&mut self) { - trace!("Consume token {:?}", self.cur_tok()); - - self.cur_tok_id += 1; - } - - pub fn save(&mut self) { - trace!("Save()"); - - self.save.push(self.cur_tok_id); - } - - pub fn save_pop(&mut self) { - trace!("Save_pop()"); - - self.save.pop().unwrap(); - } - - pub fn restore(&mut self) { - trace!("Restore()"); - - let save = self.save.pop().unwrap(); - - self.cur_tok_id = save; - } - - pub fn seek(&self, distance: usize) -> Token { - match self.tokens.get(self.cur_tok_id as usize + distance) { - Some(a) => a.clone(), - _ => Token::eof(), - } - } - - pub fn add_func_sig(&mut self, name: String, sig: FuncType) { - self.func_sigs.insert(name, sig); - } - - pub fn add_struct_type(&mut self, s: &StructDecl) { - self.struct_types.insert(s.name.get_name(), s.clone()); - } -} - -impl Parse for Root { - fn parse(ctx: &mut Parser) -> Result { - let r#mod = Mod::parse(ctx)?; - - r#mod - .top_levels - .iter() - .find(|top| match &top.kind { - TopLevelKind::Function(f) => f.name.name == "main", - _ => false, - }) - .ok_or_else(Diagnostic::new_no_main)?; - - Ok(Root { - spans: HashMap::new(), - unused: vec![], - resolutions: ResolutionMap::default(), - operators_list: ctx.ctx.operators_list.clone(), - r#mod, - }) - } -} - -impl Parse for Mod { - fn parse(ctx: &mut Parser) -> Result { - let mut res = vec![]; - - while TokenType::Eof != ctx.cur_tok().t { - match TopLevel::parse(ctx) { - Ok(top) => res.push(top), - Err(e) => { - ctx.ctx.diagnostics.push_error(e.clone()); - - return Err(e); - } - }; - } - - expect!(TokenType::Eof, ctx); - - Ok(Mod { - tokens: ctx.tokens.clone(), - identity: Identity::new(0, ctx.ctx.new_span(0, 0)), - top_levels: res, - }) - } -} - -impl Parse for TopLevel { - fn parse(ctx: &mut Parser) -> Result { - let token_id = ctx.cur_tok_id; - let token = ctx.cur_tok(); - - let kind = match ctx.cur_tok().t { - TokenType::Extern => { - ctx.consume(); // extern keyword - - TopLevelKind::Prototype(Prototype::parse(ctx)?) - } - TokenType::Trait => { - ctx.consume(); - - TopLevelKind::Trait(Trait::parse(ctx)?) - } - TokenType::Impl => { - ctx.consume(); - - TopLevelKind::Impl(Impl::parse(ctx)?) - } - TokenType::Struct => { - ctx.consume(); - - TopLevelKind::Struct(StructDecl::parse(ctx)?) - } - TokenType::Mod => { - ctx.consume(); // mod keyword - - let name = Identifier::parse(ctx)?; - - let mod_node = super::parse_mod(name.name.clone(), ctx.ctx) - .map_err(|diag| Diagnostic::new(name.identity.span.clone(), diag.get_kind()))?; - - TopLevelKind::Mod(name, mod_node) - } - TokenType::Use => { - ctx.consume(); // use keyword - - TopLevelKind::Use(Use::parse(ctx)?) - } - TokenType::Infix => { - ctx.consume(); // infix keyword - - let identifier = Identifier::parse(ctx)?; - - let precedence = if let TokenType::Number(num) = ctx.cur_tok().t { - ctx.consume(); - - num - } else { - panic!("Bad infix syntax"); - }; - - ctx.ctx - .add_operator(&identifier, precedence.try_into().unwrap())?; - - TopLevelKind::Infix(identifier, precedence as u8) - } - _ => { - // ctx.save(); - if let Ok(proto) = Prototype::parse(ctx) { - ctx.add_func_sig(proto.name.name.clone(), proto.signature.clone()); - - TopLevelKind::Prototype(proto) - } else { - let mut f = FunctionDecl::parse(ctx)?; - - let mut has_applied = false; - - if let Some(sig) = ctx.func_sigs.get(&f.name.name) { - f.signature = sig.clone(); - - has_applied = true; - } - if has_applied { - ctx.func_sigs.remove(&f.name.name); - } - - TopLevelKind::Function(f) - } - } - }; - - while ctx.cur_tok().t == TokenType::Eol { - ctx.consume(); - } - - Ok(TopLevel { - kind, - identity: Identity::new(token_id, token.span), - }) - } -} - -impl Parse for StructDecl { - fn parse(ctx: &mut Parser) -> Result { - let token_id = ctx.cur_tok_id; - let token = ctx.cur_tok(); - - let mut defs = vec![]; - - ctx.save(); - - let name = try_or_restore!(Type::parse(ctx), ctx); - - // TODO: resolve type here ? else it is infered as Trait(name) - - ctx.consume(); // Eol - - while let TokenType::Indent(_) = ctx.cur_tok().t { - ctx.consume(); // indent - - defs.push(Prototype::parse(ctx)?); - - expect!(TokenType::Eol, ctx); - } - - ctx.save_pop(); - - let struct_decl = StructDecl { - identity: Identity::new(token_id, token.span), - name, - defs, - }; - - ctx.add_struct_type(&struct_decl); - - Ok(struct_decl) - } -} - -impl Parse for Trait { - fn parse(ctx: &mut Parser) -> Result { - let mut types = vec![]; - let mut defs = vec![]; - - ctx.save(); - - let name = try_or_restore!(Type::parse(ctx), ctx); - - while ctx.cur_tok().t != TokenType::Eol { - types.push(Type::parse(ctx)?); - } - - ctx.consume(); // Eol - - while let TokenType::Indent(_) = ctx.cur_tok().t { - ctx.consume(); // indent - - defs.push(Prototype::parse(ctx)?); - - expect!(TokenType::Eol, ctx); - } - - ctx.save_pop(); - - Ok(Trait { name, types, defs }) - } -} - -impl Parse for Impl { - fn parse(ctx: &mut Parser) -> Result { - let mut types = vec![]; - let mut defs = vec![]; - - ctx.save(); - - let name = try_or_restore!(Type::parse(ctx), ctx); - - while ctx.cur_tok().t != TokenType::Eol { - types.push(Type::parse(ctx)?); - } - - ctx.consume(); // Eol - - ctx.block_indent += 1; - - while let TokenType::Indent(_) = ctx.cur_tok().t { - ctx.consume(); // indent - - defs.push(FunctionDecl::parse(ctx)?); - - expect!(TokenType::Eol, ctx); - } - - ctx.block_indent -= 1; - - ctx.save_pop(); - - Ok(Impl { name, types, defs }) - } -} - -impl Parse for Prototype { - fn parse(ctx: &mut Parser) -> Result { - let token_id = ctx.cur_tok_id; - let token = ctx.cur_tok(); - - ctx.save(); - - let name = try_or_restore!(Identifier::parse(ctx), ctx); - - expect_or_restore!(TokenType::DoubleSemiColon, ctx); - - let signature = try_or_restore!(FuncType::parse(ctx), ctx); - - ctx.save_pop(); - - Ok(Prototype { - name, - signature, - identity: Identity::new(token_id, token.span), - }) - } -} - -impl Parse for Use { - fn parse(ctx: &mut Parser) -> Result { - let token_id = ctx.cur_tok_id; - let token = ctx.cur_tok(); - - Ok(Use { - path: IdentifierPath::parse(ctx)?, - identity: Identity::new(token_id, token.span), - }) - } -} - -impl Parse for FunctionDecl { - fn parse(ctx: &mut Parser) -> Result { - let mut arguments = vec![]; - - let token_id = ctx.cur_tok_id; - let token = ctx.cur_tok(); - - ctx.save(); - - let name = try_or_restore!(Identifier::parse(ctx), ctx); - - if TokenType::OpenParens == ctx.cur_tok().t - || TokenType::Identifier(ctx.cur_tok().txt) == ctx.cur_tok().t - { - // manage error and restore here - arguments = ArgumentsDecl::parse(ctx)?; - } - - expect_or_restore!(TokenType::Operator("=".to_string()), ctx); - - let body = try_or_restore!(Body::parse(ctx), ctx); - - ctx.save_pop(); - - Ok(FunctionDecl { - name, - signature: FuncType::from_args_nb(arguments.len()), - arguments, - body, - identity: Identity::new(token_id, token.span), - }) - } -} - -impl Parse for ArgumentsDecl { - fn parse(ctx: &mut Parser) -> Result { - let mut res = vec![]; - - ctx.save(); - - loop { - let arg = try_or_restore!(ArgumentDecl::parse(ctx), ctx); - - res.push(arg); - - match ctx.cur_tok().t { - TokenType::Identifier(_) => {} - _ => break, - } - } - - ctx.save_pop(); - - Ok(res) - } -} - -impl Parse for ArgumentDecl { - fn parse(ctx: &mut Parser) -> Result { - let token_id = ctx.cur_tok_id; - let token = ctx.cur_tok(); - - expect!(TokenType::Identifier(ctx.cur_tok().txt), ctx); - - Ok(ArgumentDecl { - name: token.txt.clone(), - identity: Identity::new(token_id, token.span), - }) - } -} - -impl Parse for Body { - fn parse(ctx: &mut Parser) -> Result { - let mut multi = false; - - if ctx.cur_tok().t == TokenType::Eol { - multi = true; - - ctx.block_indent += 1; - } - - let mut stmts = vec![]; - - if multi { - loop { - ctx.save(); - - if ctx.cur_tok().t == TokenType::Eol { - while ctx.cur_tok().t == TokenType::Eol { - ctx.consume(); - } - } else { - // Deactivated because of struct last EOF - // ctx.restore(); - // break; - } - - if ctx.cur_tok().t != TokenType::Indent(ctx.block_indent) { - ctx.restore(); - break; - } else { - ctx.save_pop(); - ctx.consume(); - } - - ctx.save(); - let stmt = match Statement::parse(ctx) { - Ok(stmt) => stmt, - Err(_) => { - ctx.restore(); - break; - } - }; - - ctx.save_pop(); - - stmts.push(stmt); - } - } else { - stmts.push(Statement::parse(ctx)?) - } - - if multi { - ctx.block_indent -= 1; - } - - Ok(Body { stmts }) - } -} - -impl Parse for Statement { - fn parse(ctx: &mut Parser) -> Result { - let kind = if ctx.cur_tok().t == TokenType::If { - match If::parse(ctx) { - Ok(expr) => StatementKind::If(Box::new(expr)), - Err(_e) => error!("Expected if".to_string(), ctx), - } - } else if let Ok(assign) = Assign::parse(ctx) { - StatementKind::Assign(Box::new(assign)) - } else { - match Expression::parse(ctx) { - Ok(expr) => StatementKind::Expression(Box::new(expr)), - Err(_e) => error!("Expected expression".to_string(), ctx), - } - }; - - Ok(Statement { - kind: Box::new(kind), - }) - } -} - -impl Parse for AssignLeftSide { - fn parse(ctx: &mut Parser) -> Result { - if ctx.seek(1).t == TokenType::OpenArray { - if let Ok(expr) = PrimaryExpr::parse(ctx) { - // TODO: - // if expr.is_indice() { - - return Ok(AssignLeftSide::Indice(Expression::from_unary( - &UnaryExpr::PrimaryExpr(expr), - ))); - // } - } - } - if ctx.seek(1).t == TokenType::Dot { - if let Ok(expr) = PrimaryExpr::parse(ctx) { - // TODO: - // if expr.is_indice() { - - return Ok(AssignLeftSide::Dot(Expression::from_unary( - &UnaryExpr::PrimaryExpr(expr), - ))); - // } - } - } - - if let Ok(id) = Identifier::parse(ctx) { - return Ok(AssignLeftSide::Identifier(id)); - } - - error!( - "Expected Identifier or Indice as assignation left side".to_string(), - ctx - ) - } -} - -impl Parse for Assign { - fn parse(ctx: &mut Parser) -> Result { - ctx.save(); - - let (is_let, name) = if TokenType::Let == ctx.cur_tok().t { - expect!(TokenType::Let, ctx); - - let name = AssignLeftSide::Identifier(try_or_restore!(Identifier::parse(ctx), ctx)); - - (true, name) - } else { - let name = try_or_restore!(AssignLeftSide::parse(ctx), ctx); - - (false, name) - }; - - expect_or_restore!(TokenType::Operator("=".to_string()), ctx); - - let mut multi = false; - - if matches!(ctx.cur_tok().t, TokenType::Eol) { - ctx.consume(); // Eol - ctx.consume(); // Indent - - multi = true; - - ctx.block_indent += 1; - } - - let value = try_or_restore!(Expression::parse(ctx), ctx); - - if multi { - ctx.block_indent -= 1; - } - - ctx.save_pop(); - - Ok(Assign { - name, - value, - is_let, - }) - } -} - -impl Parse for If { - fn parse(ctx: &mut Parser) -> Result { - let token_id = ctx.cur_tok_id; - let token = ctx.cur_tok(); - - ctx.save(); - - expect_or_restore!(TokenType::If, ctx); - - let expr = try_or_restore!(Expression::parse(ctx), ctx); - - expect_or_restore!(TokenType::Eol, ctx); - expect_or_restore!(TokenType::Indent(ctx.block_indent), ctx); - expect_or_restore!(TokenType::Then, ctx); - - let body = try_or_restore!(Body::parse(ctx), ctx); - // in case of single line body - if ctx.cur_tok().t == TokenType::Eol { - expect!(TokenType::Eol, ctx); - // expect_or_restore!(TokenType::Indent(ctx.block_indent + 2), ctx); - } - - let next = ctx.seek(1); - - if next.t != TokenType::Else { - ctx.save_pop(); - - return Ok(If { - predicat: expr, - body, - else_: None, - identity: Identity::new(token_id, token.span), - }); - } - - expect_or_restore!(TokenType::Indent(ctx.block_indent), ctx); - - expect_or_restore!(TokenType::Else, ctx); - - let else_ = try_or_restore!(Else::parse(ctx), ctx); - - ctx.save_pop(); - - Ok(If { - predicat: expr, - body, - else_: Some(Box::new(else_)), - identity: Identity::new(token_id, token.span), - }) - } -} - -impl Parse for Expression { - fn parse(ctx: &mut Parser) -> Result { - if ctx.cur_tok().t == TokenType::NativeOperator(ctx.cur_tok().txt) { - ctx.save(); - - let op = NativeOperator::parse(ctx)?; - let left = Identifier::parse(ctx)?; - let right = Identifier::parse(ctx)?; - - ctx.save_pop(); - - return Ok(Expression { - kind: ExpressionKind::NativeOperation(op, left, right), - }); - } - - if let TokenType::Type(_) = ctx.cur_tok().t { - if let Ok(s) = StructCtor::parse(ctx) { - return Ok(Expression { - kind: ExpressionKind::StructCtor(s), - }); - } else { - println!("NOT A CTOR"); - } - } - - let left = UnaryExpr::parse(ctx)?; - - let mut res = Expression { - kind: ExpressionKind::UnaryExpr(left.clone()), - }; - - // FIXME - match ctx.cur_tok().t { - TokenType::Operator(_) => (), - _ => return Ok(res), - }; - - ctx.save(); - - let op = try_or_restore_and!(Operator::parse(ctx), Ok(res), ctx); - - let right = try_or_restore_and!(Expression::parse(ctx), Ok(res), ctx); - - ctx.save_pop(); - - res.kind = ExpressionKind::BinopExpr(left, op, Box::new(right)); - - Ok(res) - } -} - -impl Parse for StructCtor { - fn parse(ctx: &mut Parser) -> Result { - let token_id = ctx.cur_tok_id; - let token = ctx.cur_tok(); - - let mut defs = HashMap::new(); - - ctx.save(); - - let name = try_or_restore!(Type::parse(ctx), ctx); - - ctx.consume(); // Eol - - ctx.block_indent += 1; - - while let TokenType::Indent(i) = ctx.cur_tok().t { - if i != ctx.block_indent { - break; - } - - ctx.consume(); // indent - - let def_name = try_or_restore!(Identifier::parse(ctx), ctx); - - expect_or_restore!(TokenType::SemiColon, ctx); - - let mut multi = false; - - if matches!(ctx.cur_tok().t, TokenType::Eol) { - ctx.consume(); // Eol - ctx.consume(); // Indent - - multi = true; - - ctx.block_indent += 1; - } - - let expr = try_or_restore!(Expression::parse(ctx), ctx); - - if multi { - ctx.block_indent -= 1; - } - - defs.insert(def_name, expr); - - if matches!(ctx.cur_tok().t, TokenType::Eol) { - ctx.consume(); - } - // expect!(TokenType::Eol, ctx); - } - - ctx.block_indent -= 1; - - ctx.save_pop(); - - Ok(StructCtor { - identity: Identity::new(token_id, token.span), - name, - defs, - }) - } -} - -impl Parse for Else { - fn parse(ctx: &mut Parser) -> Result { - Ok(match ctx.cur_tok().t { - TokenType::If => Else::If(If::parse(ctx)?), - _ => Else::Body(Body::parse(ctx)?), - }) - } -} - -impl Parse for UnaryExpr { - fn parse(ctx: &mut Parser) -> Result { - if ctx.cur_tok().t == TokenType::Operator(ctx.cur_tok().txt) { - ctx.save(); - - let op = try_or_restore!(Operator::parse(ctx), ctx); - - let unary = try_or_restore!(UnaryExpr::parse(ctx), ctx); - - ctx.save_pop(); - - return Ok(UnaryExpr::UnaryExpr(op, Box::new(unary))); - } - - Ok(UnaryExpr::PrimaryExpr(PrimaryExpr::parse(ctx)?)) - } -} - -impl Parse for Operator { - fn parse(ctx: &mut Parser) -> Result { - Ok(Operator(Identifier::parse(ctx)?)) - } -} - -impl Parse for PrimaryExpr { - fn parse(ctx: &mut Parser) -> Result { - let token_id = ctx.cur_tok_id; - let token = ctx.cur_tok(); - - let operand = Operand::parse(ctx)?; - - let mut secondaries = vec![]; - - if ctx.cur_tok().t == TokenType::Operator(ctx.cur_tok().txt) - || ctx.cur_tok().t == TokenType::Equal - { - return Ok(PrimaryExpr { - identity: Identity::new(token_id, token.span), - op: operand, - secondaries: None, - }); - } - - while let Ok(second) = SecondaryExpr::parse(ctx) { - secondaries.push(second); - - if ctx.cur_tok().t == TokenType::Operator(ctx.cur_tok().txt.clone()) - || ctx.cur_tok().t == TokenType::Equal - { - break; - } - } - - let secondaries = if secondaries.is_empty() { - None - } else { - Some(secondaries) - }; - - Ok(PrimaryExpr { - identity: Identity::new(token_id, token.span), - op: operand, - secondaries, - }) - } -} - -impl Parse for Operand { - fn parse(ctx: &mut Parser) -> Result { - let kind = if let Ok(lit) = Literal::parse(ctx) { - OperandKind::Literal(lit) - } else if let Ok(ident) = IdentifierPath::parse(ctx) { - OperandKind::Identifier(ident) - } else if ctx.cur_tok().t == TokenType::OpenParens { - ctx.save(); - - expect_or_restore!(TokenType::OpenParens, ctx); - - let expr = try_or_restore!(Expression::parse(ctx), ctx); - - expect_or_restore!(TokenType::CloseParens, ctx); - - ctx.save_pop(); - - OperandKind::Expression(Box::new(expr)) - } else { - error!("Expected operand".to_string(), ctx); - }; - - Ok(Operand { kind }) - } -} - -impl Parse for SecondaryExpr { - fn parse(ctx: &mut Parser) -> Result { - if TokenType::OpenArray == ctx.cur_tok().t { - ctx.save(); - - ctx.consume(); - - if let Ok(expr) = Expression::parse(ctx) { - expect_or_restore!(TokenType::CloseArray, ctx); - - ctx.save_pop(); - - return Ok(SecondaryExpr::Indice(Box::new(expr))); - } - } else if TokenType::Dot == ctx.cur_tok().t { - ctx.save(); - - ctx.consume(); - - if let Ok(expr) = Identifier::parse(ctx) { - ctx.save_pop(); - - return Ok(SecondaryExpr::Dot(expr)); - } - } else if let Ok(args) = Arguments::parse(ctx) { - return Ok(SecondaryExpr::Arguments(args)); - } - - error!("Expected secondary".to_string(), ctx); - } -} - -impl Parse for Literal { - fn parse(ctx: &mut Parser) -> Result { - let token_id = ctx.cur_tok_id; - let token = ctx.cur_tok(); - - Ok(Self { - kind: LiteralKind::parse(ctx)?, - identity: Identity::new(token_id, token.span), - }) - } -} - -impl Parse for LiteralKind { - fn parse(ctx: &mut Parser) -> Result { - if let TokenType::Number(num) = ctx.cur_tok().t { - ctx.consume(); - - return Ok(LiteralKind::Number(num)); - } - - if let TokenType::Float(float) = ctx.cur_tok().t { - ctx.consume(); - - return Ok(LiteralKind::Float(float)); - } - - if let TokenType::Bool(b) = ctx.cur_tok().t { - ctx.consume(); - - let v = b; - - return Ok(LiteralKind::Bool(v)); - } - - if let TokenType::String(s) = ctx.cur_tok().t { - ctx.consume(); - - return Ok(LiteralKind::String(s)); - } - - if TokenType::OpenArray == ctx.cur_tok().t { - return Ok(LiteralKind::Array(Array::parse(ctx)?)); - } - - error!("Expected literal".to_string(), ctx); - } -} - -impl Parse for Array { - fn parse(ctx: &mut Parser) -> Result { - expect!(TokenType::OpenArray, ctx); - - let mut values = vec![]; - - if TokenType::CloseArray == ctx.cur_tok().t { - ctx.consume(); - } else { - loop { - values.push(Expression::parse(ctx)?); - - if TokenType::CloseArray == ctx.cur_tok().t { - ctx.consume(); - - break; - } else { - expect!(TokenType::Coma, ctx); - } - } - } - Ok(Array { values }) - } -} - -impl Parse for IdentifierPath { - fn parse(ctx: &mut Parser) -> Result { - let mut path = vec![]; - - ctx.save(); - - loop { - let tok = try_or_restore!(Identifier::parse(ctx), ctx); - - path.push(tok.clone()); - - if TokenType::DoubleSemiColon != ctx.cur_tok().t { - break; - } - - expect!(TokenType::DoubleSemiColon, ctx); - } - - ctx.save_pop(); - - Ok(Self { path }) - } -} - -impl Parse for Identifier { - fn parse(ctx: &mut Parser) -> Result { - let token_id = ctx.cur_tok_id; - let token = ctx.cur_tok(); - - let txt = match ctx.cur_tok().t { - TokenType::Identifier(id) => id, - TokenType::Operator(op) => op, - _ => error!("Not an operator".to_string(), ctx), - }; - - ctx.consume(); - - Ok(Self { - name: txt, - identity: Identity::new(token_id, token.span), - }) - } -} - -impl Parse for Arguments { - fn parse(ctx: &mut Parser) -> Result { - let mut res = vec![]; - - ctx.save(); - - // TODO: factorise this with a match! macro ? - if TokenType::OpenParens == ctx.cur_tok().t && TokenType::CloseParens == ctx.seek(1).t { - ctx.consume(); - ctx.consume(); - - ctx.save_pop(); - - return Ok(res); - } - - loop { - let arg = try_or_restore!(Argument::parse(ctx), ctx); - - res.push(arg); - - if TokenType::Coma != ctx.cur_tok().t { - break; - } - - ctx.consume(); - } - - ctx.save_pop(); - - Ok(res) - } -} - -impl Parse for Argument { - fn parse(ctx: &mut Parser) -> Result { - Ok(Argument { - arg: UnaryExpr::parse(ctx)?, - }) - } -} - -impl Parse for NativeOperator { - fn parse(ctx: &mut Parser) -> Result { - let token_id = ctx.cur_tok_id; - let token = ctx.cur_tok(); - - let op = match ctx.cur_tok().t { - TokenType::NativeOperator(op) => op, - _ => error!("Expected native operator".to_string(), ctx), - }; - - let kind = match op.as_ref() { - "~IAdd" => NativeOperatorKind::IAdd, - "~ISub" => NativeOperatorKind::ISub, - "~IMul" => NativeOperatorKind::IMul, - "~IDiv" => NativeOperatorKind::IDiv, - "~FAdd" => NativeOperatorKind::FAdd, - "~FSub" => NativeOperatorKind::FSub, - "~FMul" => NativeOperatorKind::FMul, - "~FDiv" => NativeOperatorKind::FDiv, - "~IEq" => NativeOperatorKind::IEq, - "~Igt" => NativeOperatorKind::Igt, - "~Ige" => NativeOperatorKind::Ige, - "~Ilt" => NativeOperatorKind::Ilt, - "~Ile" => NativeOperatorKind::Ile, - "~FEq" => NativeOperatorKind::FEq, - "~Fgt" => NativeOperatorKind::Fgt, - "~Fge" => NativeOperatorKind::Fge, - "~Flt" => NativeOperatorKind::Flt, - "~Fle" => NativeOperatorKind::Fle, - "~BEq" => NativeOperatorKind::BEq, - _ => error!("Unknown native operator".to_string(), ctx), - }; - - ctx.consume(); - - Ok(NativeOperator { - kind, - identity: Identity::new(token_id, token.span), - }) - } -} - -impl Parse for Type { - fn parse(ctx: &mut Parser) -> Result { - let token = ctx.cur_tok(); - - if let TokenType::Type(_t) = token.t { - ctx.consume(); - - if let Some(prim) = PrimitiveType::from_name(&token.txt) { - Ok(Type::Primitive(prim)) - } else { - if let Some(s) = ctx.struct_types.get(&token.txt) { - return Ok(s.into()); - } - Ok(Type::Trait(token.txt)) - } - } else if token.txt.len() == 1 && token.txt.chars().next().unwrap().is_lowercase() { - ctx.consume(); - - Ok(Type::ForAll(token.txt)) - } else if TokenType::OpenArray == token.t { - ctx.consume(); - - let inner_t = Type::parse(ctx)?; - - // FIXME: FIXED ARRAY SIZE OF 1KB ! - let t = Type::Primitive(PrimitiveType::Array(Box::new(inner_t), 1024)); - - expect!(TokenType::CloseArray, ctx); - - Ok(t) - } else if TokenType::OpenParens == token.t { - ctx.consume(); - - let t = Type::Func(FuncType::parse(ctx)?); - - expect!(TokenType::CloseParens, ctx); - - Ok(t) - } else { - panic!("Not a type"); - } - } -} - -impl Parse for FuncType { - fn parse(ctx: &mut Parser) -> Result { - let mut arguments = vec![]; - - loop { - let t = Type::parse(ctx)?; - - arguments.push(t); - - if ctx.cur_tok().t != TokenType::Arrow { - break; - } - - ctx.consume(); - } - - let ret = Box::new(arguments.pop().unwrap()); - - Ok(FuncType { arguments, ret }) - } -} diff --git a/src/lib/parser/parsing_context.rs b/src/lib/parser/parsing_context.rs index 9f04d0a5..8acbd28b 100644 --- a/src/lib/parser/parsing_context.rs +++ b/src/lib/parser/parsing_context.rs @@ -1,25 +1,27 @@ use std::{ - collections::HashMap, + collections::{BTreeMap, HashMap}, path::{Component, PathBuf}, }; use colored::*; use crate::{ - ast::{ast_print::AstPrintContext, Identifier, Root}, + ast::{ast_print::AstPrintContext, tree, Identifier, NodeId}, diagnostics::{Diagnostic, DiagnosticType, Diagnostics}, + parser::span2::Span, Config, }; -use super::{SourceFile, Span}; +use super::SourceFile; #[derive(Default, Debug)] pub struct ParsingCtx { - files: HashMap, + pub files: HashMap, pub config: Config, pub current_file: Option, pub diagnostics: Diagnostics, pub operators_list: HashMap, + pub identities: BTreeMap, } impl ParsingCtx { @@ -125,16 +127,27 @@ impl ParsingCtx { Ok(()) } - pub fn new_span(&self, start: usize, end: usize) -> Span { - Span::new(self.get_current_file().file_path, start, end) + pub fn new_span(&self, start: usize, _end: usize) -> Span { + Span { + file_path: self.get_current_file().file_path, + offset: start, + ..Default::default() + } } pub fn resolve_and_add_file(&mut self, name: String) -> Result { let current_file = self.get_current_file(); - let new_file = current_file.resolve_new(name.clone()).map_err(|m| { + let new_file = current_file.resolve_new(name).map_err(|m| { // Placeholder span, to be overriden by calling mod (TopLevel::parse()) - Diagnostic::new_module_not_found(Span::new(current_file.file_path.clone(), 0, 0), m) + Diagnostic::new_module_not_found( + Span { + file_path: current_file.file_path.clone(), + ..Default::default() + } + .into(), + m, + ) })?; if self.config.verbose { @@ -162,7 +175,7 @@ impl ParsingCtx { Ok(new_file) } - pub fn add_operator(&mut self, name: &Identifier, precedence: u8) -> Result<(), Diagnostic> { + /* pub fn add_operator(&mut self, name: &Identifier, precedence: u8) -> Result<(), Diagnostic> { if self.operator_exists(name) { return Err(Diagnostic::new_duplicated_operator( name.identity.span.clone(), @@ -172,14 +185,14 @@ impl ParsingCtx { self.operators_list.insert(name.name.clone(), precedence); Ok(()) - } + } */ pub fn operator_exists(&self, name: &Identifier) -> bool { self.operators_list.contains_key(&name.name) } #[allow(dead_code)] - pub fn print_ast(&self, ast: &Root) { + pub fn print_ast(&self, ast: &tree::Root) { use crate::ast::visit::Visitor; AstPrintContext::new().visit_root(ast); diff --git a/src/lib/parser/source_file.rs b/src/lib/parser/source_file.rs index bd2a649a..4d55c9cd 100644 --- a/src/lib/parser/source_file.rs +++ b/src/lib/parser/source_file.rs @@ -5,7 +5,7 @@ use std::{ use crate::diagnostics::Diagnostic; -use super::Span; +use super::span::Span; #[derive(Default, Debug, Clone)] pub struct SourceFile { @@ -43,11 +43,11 @@ impl SourceFile { } let top_levels = r##"mod lib -use lib::prelude::* +use lib::prelude::(*) "## .to_owned() + &top_levels - + &r##" + + r##" main = "## + &print_str.to_string() @@ -58,6 +58,7 @@ custom = .to_owned() + &expr; + // println!("{}", top_levels); Ok(SourceFile { file_path: PathBuf::from("./src/main.rk"), mod_path: PathBuf::from("root"), diff --git a/src/lib/parser/span.rs b/src/lib/parser/span.rs index 68e6385e..5089460d 100644 --- a/src/lib/parser/span.rs +++ b/src/lib/parser/span.rs @@ -1,3 +1,4 @@ +use super::span2::Span as Span2; use std::path::PathBuf; #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)] @@ -24,3 +25,13 @@ impl Span { } } } + +impl From for Span { + fn from(span: Span2) -> Self { + Self { + start: span.offset, + end: span.txt.len() + span.offset, + file_path: span.file_path, + } + } +} diff --git a/src/lib/parser/span2.rs b/src/lib/parser/span2.rs new file mode 100644 index 00000000..c4ea839b --- /dev/null +++ b/src/lib/parser/span2.rs @@ -0,0 +1,26 @@ +use std::path::PathBuf; + +use crate::parser::Parser; + +// TODO: merge spans + +#[derive(Debug, Default, Clone, Hash, PartialEq, Eq)] +pub struct Span { + pub file_path: PathBuf, + pub offset: usize, + pub line: usize, + pub column: usize, + pub txt: String, +} + +impl<'a> From> for Span { + fn from(source: Parser<'a>) -> Self { + Self { + file_path: source.extra.current_file_path().clone(), + offset: source.location_offset(), + line: source.location_line() as usize, + column: source.get_column(), + txt: source.to_string(), + } + } +} diff --git a/src/lib/parser/tests.rs b/src/lib/parser/tests.rs new file mode 100644 index 00000000..9406086e --- /dev/null +++ b/src/lib/parser/tests.rs @@ -0,0 +1,903 @@ +use nom::Finish; + +use super::*; + +#[cfg(test)] +mod parse_literal { + use super::*; + + #[test] + fn bool() { + let input = Parser::new_extra("true", ParserCtx::new(PathBuf::new())); + + let (_rest, num_parsed) = parse_literal(input).finish().unwrap(); + + assert!(matches!(num_parsed.kind, LiteralKind::Bool(true))); + } + + #[test] + fn number() { + let input = Parser::new_extra("42", ParserCtx::new(PathBuf::new())); + + let (_rest, num_parsed) = parse_literal(input).finish().unwrap(); + + assert!(matches!(num_parsed.kind, LiteralKind::Number(42))); + } + + #[test] + fn float() { + let input = Parser::new_extra("42.42", ParserCtx::new(PathBuf::new())); + + let (_rest, num_parsed) = parse_literal(input).finish().unwrap(); + + assert!(matches!(num_parsed.kind, LiteralKind::Float(f) if f == 42.42)); + } +} + +#[cfg(test)] +mod parse_bool { + use super::*; + + #[test] + fn r#true() { + let input = Parser::new_extra("true", ParserCtx::new(PathBuf::new())); + + let (_rest, parsed) = parse_bool(input).finish().unwrap(); + + assert!(matches!(parsed.kind, LiteralKind::Bool(true))); + } + + #[test] + fn r#false() { + let input = Parser::new_extra("false", ParserCtx::new(PathBuf::new())); + + let (_rest, parsed) = parse_bool(input).finish().unwrap(); + + assert!(matches!(parsed.kind, LiteralKind::Bool(false))); + } + + #[test] + fn invalid() { + let input = Parser::new_extra("atrue", ParserCtx::new(PathBuf::new())); + + assert!(parse_bool(input).finish().is_err()); + } +} + +#[cfg(test)] +mod parse_float { + use super::*; + + #[test] + fn valid_with_last_part() { + let input = Parser::new_extra("42.42", ParserCtx::new(PathBuf::new())); + + let (_rest, parsed) = parse_float(input).finish().unwrap(); + + assert!(matches!(parsed.kind, LiteralKind::Float(f) if f == 42.42)); + } + + #[test] + fn valid_no_last_part() { + let input = Parser::new_extra("42.", ParserCtx::new(PathBuf::new())); + + let (_rest, parsed) = parse_float(input).finish().unwrap(); + + assert!(matches!(parsed.kind, LiteralKind::Float(f) if f == 42.0)); + } + + #[test] + fn invalid() { + let input = Parser::new_extra("a42.", ParserCtx::new(PathBuf::new())); + + assert!(parse_float(input).finish().is_err()); + } +} + +#[cfg(test)] +mod parse_number { + use super::*; + + #[test] + fn valid() { + let input = Parser::new_extra("42", ParserCtx::new(PathBuf::new())); + + let (_rest, parsed) = parse_number(input).finish().unwrap(); + + assert!(matches!(parsed.kind, LiteralKind::Number(42))); + } + + #[test] + fn invalid() { + let input = Parser::new_extra("a42", ParserCtx::new(PathBuf::new())); + + assert!(parse_number(input).finish().is_err()); + } +} + +#[cfg(test)] +mod parse_signature { + use super::*; + + #[test] + fn valid_1_arg() { + let input = Parser::new_extra("Int64", ParserCtx::new(PathBuf::new())); + + let (_rest, parsed) = parse_signature(input).finish().unwrap(); + + assert_eq!(parsed.arguments, vec![]); + assert_eq!(parsed.ret, Box::new(Type::int64())); + } + + #[test] + fn valid_2_arg() { + let input = Parser::new_extra("Int64 -> Int64", ParserCtx::new(PathBuf::new())); + + let (_rest, parsed) = parse_signature(input).finish().unwrap(); + + assert_eq!(parsed.arguments, vec![Type::int64()]); + assert_eq!(parsed.ret, Box::new(Type::int64())); + } +} + +#[cfg(test)] +mod parse_type { + use super::*; + + #[test] + fn valid() { + let input = Parser::new_extra("Int64", ParserCtx::new(PathBuf::new())); + + let (_rest, parsed) = parse_type(input).finish().unwrap(); + + assert_eq!(parsed, Type::int64()); + } + + #[test] + fn valid_for_all() { + let input = Parser::new_extra("a", ParserCtx::new(PathBuf::new())); + + let (_rest, parsed) = parse_type(input).finish().unwrap(); + + assert_eq!(parsed, Type::forall("a")); + } + + #[test] + fn invalid() { + let input = Parser::new_extra("int64", ParserCtx::new(PathBuf::new())); + + assert!(parse_type(input).finish().is_err()); + } +} + +#[cfg(test)] +mod parse_infix_op { + use super::*; + + #[test] + fn valid() { + let input = Parser::new_extra("infix + 5", ParserCtx::new(PathBuf::new())); + + let (rest, parsed) = parse_infix(input).finish().unwrap(); + + assert!(matches!(parsed, TopLevel::Infix(_op, 5))); + + let operators = HashMap::from([("+".to_string(), 5)]); + assert_eq!(rest.extra.operators_list, operators); + } +} + +#[cfg(test)] +mod parse_operator { + use super::*; + + #[test] + fn valid() { + let operators = HashMap::from([("+".to_string(), 5)]); + + let input = Parser::new_extra( + "+", + ParserCtx::new_with_operators(PathBuf::new(), operators), + ); + + let (_rest, parsed) = parse_operator(input).finish().unwrap(); + + assert_eq!( + parsed, + Operator(Identifier { + name: String::from("+"), + node_id: 0, + }) + ); + } +} + +#[cfg(test)] +mod parse_identifier { + use super::*; + + #[test] + fn valid() { + let input = Parser::new_extra("foo", ParserCtx::new(PathBuf::new())); + + let (_rest, parsed) = parse_identifier(input).finish().unwrap(); + + assert_eq!( + parsed, + Identifier { + name: String::from("foo"), + node_id: 0, + } + ); + } +} + +#[cfg(test)] +mod parse_identifier_path { + use super::*; + + #[test] + fn valid() { + let input = Parser::new_extra("foo::bar", ParserCtx::new(PathBuf::new())); + + let (_rest, parsed) = parse_identifier_path(input).finish().unwrap(); + + assert_eq!( + parsed, + IdentifierPath { + path: vec![ + Identifier { + name: String::from("foo"), + node_id: 0, + }, + Identifier { + name: String::from("bar"), + node_id: 0, + }, + ], + } + ); + } +} + +#[cfg(test)] +mod parse_operand { + use super::*; + + #[test] + fn valid_literal() { + let input = Parser::new_extra("42", ParserCtx::new(PathBuf::new())); + + let (_rest, parsed) = parse_operand(input).finish().unwrap(); + + assert!(matches!( + parsed, + Operand::Literal(Literal { + kind: LiteralKind::Number(42), + node_id: 0, + }) + )); + } + + #[test] + fn valid_identifier_path() { + let input = Parser::new_extra("foo::bar", ParserCtx::new(PathBuf::new())); + + let (_rest, parsed) = parse_operand(input).finish().unwrap(); + + assert!(matches!( + parsed, + Operand::Identifier(IdentifierPath { path: _ }) + )); + } + + #[test] + fn valid_expression() { + let input = Parser::new_extra("(3)", ParserCtx::new(PathBuf::new())); + + let (_rest, parsed) = parse_operand(input).finish().unwrap(); + + assert!(matches!(parsed, Operand::Expression(_expr))); + } +} + +#[cfg(test)] +mod parse_expression { + use super::*; + + #[test] + fn valid_unary() { + let input = Parser::new_extra("3", ParserCtx::new(PathBuf::new())); + + let (_rest, parsed) = parse_expression(input).finish().unwrap(); + + assert!(matches!(parsed, Expression::UnaryExpr(_))); + } + + #[test] + fn valid_binary() { + let operators = HashMap::from([("+".to_string(), 5)]); + + let input = Parser::new_extra( + "3 + 4", + ParserCtx::new_with_operators(PathBuf::new(), operators), + ); + + let (_rest, parsed) = parse_expression(input).finish().unwrap(); + + assert!(matches!(parsed, Expression::BinopExpr(_, _, _))); + } +} +#[cfg(test)] +mod parse_primary { + use super::*; + + mod arguments { + use super::*; + + mod parenthesis { + use super::*; + + #[test] + fn valid_no_args() { + let input = Parser::new_extra("foo()", ParserCtx::new(PathBuf::new())); + + let (_rest, parsed) = parse_primary(input).finish().unwrap(); + + let secondaries = parsed.secondaries.unwrap(); + assert_eq!(secondaries.len(), 1); + + let args = &secondaries[0]; + + match args { + SecondaryExpr::Arguments(arr) => assert_eq!(arr.len(), 0), + _ => panic!("expected Arguments"), + } + } + + #[test] + fn valid_one_arg() { + let input = Parser::new_extra("foo(2)", ParserCtx::new(PathBuf::new())); + + let (_rest, parsed) = parse_primary(input).finish().unwrap(); + + let secondaries = parsed.secondaries.unwrap(); + assert_eq!(secondaries.len(), 1); + + let args = &secondaries[0]; + + match args { + SecondaryExpr::Arguments(arr) => assert_eq!(arr.len(), 1), + _ => panic!("expected Arguments"), + } + } + + #[test] + fn valid_two_args() { + let input = Parser::new_extra("foo(2, 3)", ParserCtx::new(PathBuf::new())); + + let (_rest, parsed) = parse_primary(input).finish().unwrap(); + + let secondaries = parsed.secondaries.unwrap(); + assert_eq!(secondaries.len(), 1); + + let args = &secondaries[0]; + + match args { + SecondaryExpr::Arguments(arr) => assert_eq!(arr.len(), 2), + _ => panic!("expected Arguments"), + } + } + } + + mod no_parenthesis { + use super::*; + + #[test] + fn valid_one_arg() { + let input = Parser::new_extra("foo 2", ParserCtx::new(PathBuf::new())); + + let (_rest, parsed) = parse_primary(input).finish().unwrap(); + + let secondaries = parsed.secondaries.unwrap(); + assert_eq!(secondaries.len(), 1); + + let args = &secondaries[0]; + + match args { + SecondaryExpr::Arguments(arr) => assert_eq!(arr.len(), 1), + _ => panic!("expected Arguments"), + } + } + + #[test] + fn valid_two_args() { + let input = Parser::new_extra("foo 2, 3", ParserCtx::new(PathBuf::new())); + + let (_rest, parsed) = parse_primary(input).finish().unwrap(); + + let secondaries = parsed.secondaries.unwrap(); + assert_eq!(secondaries.len(), 1); + + let args = &secondaries[0]; + + match args { + SecondaryExpr::Arguments(arr) => assert_eq!(arr.len(), 2), + _ => panic!("expected Arguments"), + } + } + } + } + + #[test] + fn valid_indice() { + let input = Parser::new_extra("foo[3]", ParserCtx::new(PathBuf::new())); + + let (_rest, parsed) = parse_primary(input).finish().unwrap(); + + let secondaries = parsed.secondaries.unwrap(); + assert_eq!(secondaries.len(), 1); + + let args = &secondaries[0]; + + match args { + SecondaryExpr::Indice(_expr) => {} + _ => panic!("expected indice"), + } + } + + #[test] + fn valid_dot() { + let input = Parser::new_extra("foo.toto", ParserCtx::new(PathBuf::new())); + + let (_rest, parsed) = parse_primary(input).finish().unwrap(); + + let secondaries = parsed.secondaries.unwrap(); + assert_eq!(secondaries.len(), 1); + + let args = &secondaries[0]; + + match args { + SecondaryExpr::Dot(_expr) => {} + _ => panic!("expected dot"), + } + } + + #[test] + fn valid_mixed() { + let input = Parser::new_extra("foo.toto()[a]", ParserCtx::new(PathBuf::new())); + + let (_rest, parsed) = parse_primary(input).finish().unwrap(); + + let secondaries = parsed.secondaries.unwrap(); + + assert_eq!(secondaries.len(), 3); + } +} + +#[cfg(test)] +mod parse_fn_decl { + use super::*; + + #[test] + fn valid_no_args() { + let input = Parser::new_extra("toto =\n 2\n", ParserCtx::new(PathBuf::new())); + + let (_rest, parsed) = parse_fn(input).finish().unwrap(); + + let expected = FunctionDecl { + name: Identifier { + name: String::from("toto"), + node_id: 0, + }, + body: Body::new(vec![Statement::Expression(Box::new( + Expression::UnaryExpr(UnaryExpr::PrimaryExpr(PrimaryExpr { + op: Operand::Literal(Literal { + kind: LiteralKind::Number(2), + node_id: 0, + }), + node_id: 0, + secondaries: None, + })), + ))]), + arguments: vec![], + signature: FuncType { + ret: Box::new(Type::forall("a")), + arguments: vec![], + }, + node_id: 0, + }; + + assert_eq!(parsed.name, expected.name); + assert_eq!(parsed.arguments, expected.arguments); + assert_eq!(parsed.signature, expected.signature); + } + + #[test] + fn valid_2_args() { + let operators = HashMap::from([("+".to_string(), 5)]); + + let input = Parser::new_extra( + "toto a b =\n a + b", + ParserCtx::new_with_operators(PathBuf::new(), operators), + ); + + let (_rest, parsed) = parse_fn(input).finish().unwrap(); + + let expected = FunctionDecl { + name: Identifier { + name: String::from("toto"), + node_id: 0, + }, + arguments: vec![ + Identifier { + name: String::from("a"), + node_id: 0, + }, + Identifier { + name: String::from("b"), + node_id: 0, + }, + ], + node_id: 0, + body: Body { + stmts: vec![Statement::Expression(Box::new(Expression::BinopExpr( + UnaryExpr::PrimaryExpr(PrimaryExpr { + op: Operand::Identifier(IdentifierPath { + path: vec![Identifier { + name: String::from("a"), + node_id: 0, + }], + }), + node_id: 0, + secondaries: None, + }), + Operator(Identifier { + name: String::from("+"), + node_id: 0, + }), + Box::new(Expression::UnaryExpr(UnaryExpr::PrimaryExpr(PrimaryExpr { + op: Operand::Identifier(IdentifierPath { + path: vec![Identifier { + name: String::from("b"), + node_id: 0, + }], + }), + node_id: 0, + secondaries: None, + }))), + )))], + }, + signature: FuncType { + ret: Box::new(Type::forall("c")), + arguments: vec![Type::forall("a"), Type::forall("b")], + }, + }; + + assert_eq!(parsed.name, expected.name); + assert_eq!(parsed.arguments, expected.arguments); + // assert_eq!(parsed.body, expected.body); + assert_eq!(parsed.signature, expected.signature); + } + + #[test] + fn valid_multiline() { + let operators = HashMap::from([("+".to_string(), 5)]); + + let input = Parser::new_extra( + "toto a b =\n a + b\n a + b", + ParserCtx::new_with_operators(PathBuf::new(), operators), + ); + + let (rest, _parsed) = parse_fn(input).finish().unwrap(); + + assert!(rest.fragment().is_empty()); + } + + #[test] + fn valid_one_line() { + let operators = HashMap::from([("+".to_string(), 5)]); + + let input = Parser::new_extra( + "toto a b = a + b", + ParserCtx::new_with_operators(PathBuf::new(), operators), + ); + + let (rest, _parsed) = parse_fn(input).finish().unwrap(); + + assert!(rest.fragment().is_empty()); + } +} + +#[cfg(test)] +mod parse_prototype { + use super::*; + + #[test] + fn valid() { + let input = Parser::new_extra("toto :: Int64 -> Int64", ParserCtx::new(PathBuf::new())); + + let (_rest, parsed) = parse_prototype(input).finish().unwrap(); + + let expected = Prototype { + name: Identifier { + name: String::from("toto"), + node_id: 0, + }, + signature: FuncType { + ret: Box::new(Type::int64()), + arguments: vec![Type::int64()], + }, + node_id: 0, + }; + + assert_eq!(parsed.name, expected.name); + } +} + +#[cfg(test)] +mod parse_use { + use super::*; + + #[test] + fn valid() { + let input = Parser::new_extra("use foo", ParserCtx::new(PathBuf::new())); + + let (_rest, parsed) = parse_use(input).finish().unwrap(); + + assert_eq!( + parsed.path, + IdentifierPath { + path: vec![Identifier { + name: String::from("foo"), + node_id: 0, + }], + } + ); + } +} + +#[cfg(test)] +mod parse_if { + use super::*; + + #[test] + fn valid_if() { + let input = Parser::new_extra("if a\nthen b", ParserCtx::new(PathBuf::new())); + + let (rest, _parsed) = parse_if(input).finish().unwrap(); + + assert!(rest.fragment().is_empty()); + } + + #[test] + fn valid_if_else() { + let input = Parser::new_extra("if a\nthen b\nelse c", ParserCtx::new(PathBuf::new())); + + let (rest, _parsed) = parse_if(input).finish().unwrap(); + + assert!(rest.fragment().is_empty()); + } + + #[test] + fn valid_if_else_if() { + let input = Parser::new_extra( + "if a\nthen b\nelse if false\nthen c", + ParserCtx::new(PathBuf::new()), + ); + + let (rest, _parsed) = parse_if(input).finish().unwrap(); + + assert!(rest.fragment().is_empty()); + } + + #[test] + fn valid_if_else_if_else() { + let input = Parser::new_extra( + "if a\nthen b\nelse if true\nthen c\nelse d", + ParserCtx::new(PathBuf::new()), + ); + + let (rest, _parsed) = parse_if(input).finish().unwrap(); + + assert!(rest.fragment().is_empty()); + } + + #[test] + fn valid_multiline_if_else() { + let input = Parser::new_extra("if a\nthen\n b\nelse\n d", ParserCtx::new(PathBuf::new())); + + let (rest, _parsed) = parse_if(input).finish().unwrap(); + + assert!(rest.fragment().is_empty()); + } +} + +#[cfg(test)] +mod parse_for { + use super::*; + + #[test] + fn valid_for_in() { + let input = Parser::new_extra("for x in a\n b", ParserCtx::new(PathBuf::new())); + + let (rest, _parsed) = parse_for(input).finish().unwrap(); + + assert!(rest.fragment().is_empty()); + } + + #[test] + fn valid_while() { + let input = Parser::new_extra("while a\n b = 2", ParserCtx::new(PathBuf::new())); + + let (rest, _parsed) = parse_for(input).finish().unwrap(); + + assert!(rest.fragment().is_empty()); + } +} + +#[cfg(test)] +mod parse_assign { + use super::*; + + #[test] + fn valid_assign() { + let input = Parser::new_extra("let a = 2", ParserCtx::new(PathBuf::new())); + + let (rest, _parsed) = parse_assign(input).finish().unwrap(); + + assert!(rest.fragment().is_empty()); + } + + #[test] + fn valid_reassign_ident() { + let input = Parser::new_extra("a = 2", ParserCtx::new(PathBuf::new())); + + let (rest, _parsed) = parse_assign(input).finish().unwrap(); + + assert!(rest.fragment().is_empty()); + } + + #[test] + fn valid_reassign_dot() { + let input = Parser::new_extra("a.b = 2", ParserCtx::new(PathBuf::new())); + + let (rest, _parsed) = parse_assign(input).finish().unwrap(); + + assert!(rest.fragment().is_empty()); + } + + #[test] + fn valid_reassign_indice() { + let input = Parser::new_extra("a[2] = 2", ParserCtx::new(PathBuf::new())); + + let (rest, _parsed) = parse_assign(input).finish().unwrap(); + + assert!(rest.fragment().is_empty()); + } +} + +#[cfg(test)] +mod parse_struct_decl { + use super::*; + + #[test] + fn valid_struct_decl() { + let input = Parser::new_extra("struct Foo", ParserCtx::new(PathBuf::new())); + + let (rest, _parsed) = parse_struct_decl(input).finish().unwrap(); + + assert!(rest.fragment().is_empty()); + } + + #[test] + fn valid_struct_decl_with_fields() { + let input = Parser::new_extra( + "struct Foo\n a :: Int64\n b :: Float64", + ParserCtx::new(PathBuf::new()), + ); + + let (rest, _parsed) = parse_struct_decl(input).finish().unwrap(); + + assert!(rest.fragment().is_empty()); + } +} + +#[cfg(test)] +mod parse_struct_ctor { + use super::*; + + #[test] + fn valid_struct_ctor() { + let input = Parser::new_extra("Foo\n", ParserCtx::new(PathBuf::new())); + + let (rest, _parsed) = parse_struct_ctor(input).finish().unwrap(); + + assert!(rest.fragment().is_empty()); + } + + #[test] + fn valid_struct_ctor_with_fields() { + let input = Parser::new_extra("Foo\n a: 2\n b: 3.0", ParserCtx::new(PathBuf::new())); + + let (rest, _parsed) = parse_struct_ctor(input).finish().unwrap(); + + assert!(rest.fragment().is_empty()); + } +} + +#[cfg(test)] +mod parse_trait { + use super::*; + + #[test] + fn valid_trait() { + let input = Parser::new_extra( + "trait Foo\n a :: Int64 -> Int64\n b :: Float64 -> String", + ParserCtx::new(PathBuf::new()), + ); + + let (rest, _parsed) = parse_trait(input).finish().unwrap(); + + assert!(rest.fragment().is_empty()); + } +} + +#[cfg(test)] +mod parse_impl { + use super::*; + + #[test] + fn valid_impl() { + let input = Parser::new_extra( + "impl Foo\n a =\n 2\n b a =\n a", + ParserCtx::new(PathBuf::new()), + ); + + let (rest, _parsed) = parse_impl(input).finish().unwrap(); + + assert!(rest.fragment().is_empty()); + } +} + +#[cfg(test)] +mod parse_native_operator { + use super::*; + + #[test] + fn valid_native_operator() { + let input = Parser::new_extra("~IAdd a b", ParserCtx::new(PathBuf::new())); + + let (_rest, (parsed, _, _)) = parse_native_operator(input).finish().unwrap(); + + assert_eq!(parsed.kind, NativeOperatorKind::IAdd); + } +} + +#[cfg(test)] +mod parse_array { + use super::*; + + #[test] + fn valid_array() { + let input = Parser::new_extra("[1, 2, 3]", ParserCtx::new(PathBuf::new())); + + let (rest, _parsed) = parse_array(input).finish().unwrap(); + + assert!(rest.fragment().is_empty()); + } +} + +#[cfg(test)] +mod parse_string { + use super::*; + + #[test] + fn valid_string() { + let input = Parser::new_extra("\"foo\"", ParserCtx::new(PathBuf::new())); + + let (rest, _parsed) = parse_string(input).finish().unwrap(); + + assert!(rest.fragment().is_empty()); + } +} diff --git a/src/lib/parser/token.rs b/src/lib/parser/token.rs deleted file mode 100644 index d8529281..00000000 --- a/src/lib/parser/token.rs +++ /dev/null @@ -1,85 +0,0 @@ -use super::Span; - -pub fn accepted_operator_chars() -> Vec { - return vec!['+', '-', '/', '*', '|', '<', '>', '=', '!', '$', '@', '&']; -} - -#[derive(Clone, Debug, PartialEq)] -pub enum TokenType { - // keywords - Fn, - Let, - Mod, - Use, - Extern, - If, - Then, - Else, - For, - // In, - Struct, - Infix, - Trait, - Impl, - - // punct - Arrow, - Coma, - Dot, - SemiColon, - DoubleSemiColon, - Equal, - ArrayType, - // EqualEqual, // == - // DashEqual, // != - OpenParens, - CloseParens, - OpenArray, - CloseArray, - OpenBrace, - CloseBrace, - - //Operator - Operator(String), - NativeOperator(String), - - // primitives - Identifier(String), - Number(i64), - Float(f64), - String(String), - Bool(bool), - Type(String), - - // indent - Indent(u8), - - // whitespaces - Eol, - Eof, -} - -impl Default for TokenType { - fn default() -> TokenType { - TokenType::Eof - } -} - -#[derive(Clone, Debug)] -pub struct Token { - pub t: TokenType, - pub span: Span, - pub txt: String, -} - -pub type TokenId = usize; - -impl Token { - pub fn eof() -> Self { - Token { - t: TokenType::Eof, - span: Span::new_placeholder(), - txt: "".to_string(), - } - } -} diff --git a/src/lib/resolver/mod.rs b/src/lib/resolver/mod.rs index 69245d62..41e3ce06 100644 --- a/src/lib/resolver/mod.rs +++ b/src/lib/resolver/mod.rs @@ -1,5 +1,5 @@ use crate::{ - ast::{span_collector, visit::*, IdentifierPath, Root}, + ast::{tree::IdentifierPath, tree::Root, visit::*}, diagnostics::Diagnostic, helpers::scopes::Scopes, parser::ParsingCtx, @@ -11,6 +11,8 @@ mod unused_collector; use std::collections::HashMap; +use crate::parser::span::Span as OldSpan; + pub use resolution_map::*; pub use resolve_ctx::*; @@ -32,14 +34,12 @@ pub fn resolve(root: &mut Root, parsing_ctx: &mut ParsingCtx) -> Result<(), Diag let (mut unused_fns, unused_methods) = unused_collector::collect_unused(root); - root.spans = span_collector::collect_spans(root); - for unused_fn in &unused_fns { - let span = root.spans.get(unused_fn).unwrap(); + let span = parsing_ctx.identities.get(unused_fn).unwrap(); parsing_ctx .diagnostics - .push_warning(Diagnostic::new_unused_function(span.clone())); + .push_warning(Diagnostic::new_unused_function(OldSpan::from(span.clone()))); } unused_fns.extend(unused_methods); diff --git a/src/lib/resolver/resolve_ctx.rs b/src/lib/resolver/resolve_ctx.rs index 77059c0a..0d205129 100644 --- a/src/lib/resolver/resolve_ctx.rs +++ b/src/lib/resolver/resolve_ctx.rs @@ -1,9 +1,10 @@ use std::collections::HashMap; use crate::{ - ast::{visit::*, *}, + ast::{tree::*, visit::*, NodeId}, diagnostics::Diagnostic, helpers::scopes::*, + parser::span2::Span as Span2, parser::ParsingCtx, resolver::ResolutionMap, }; @@ -11,15 +12,15 @@ use crate::{ #[derive(Debug)] pub struct ResolveCtx<'a> { pub parsing_ctx: &'a mut ParsingCtx, - pub scopes: HashMap>, // + pub scopes: HashMap>, // pub cur_scope: IdentifierPath, pub resolutions: ResolutionMap, } impl<'a> ResolveCtx<'a> { - pub fn add_to_current_scope(&mut self, name: String, ident: Identity) { + pub fn add_to_current_scope(&mut self, name: String, node_id: NodeId) { if let Some(ref mut scopes) = self.scopes.get_mut(&self.cur_scope) { - scopes.add(name, ident); + scopes.add(name, node_id); } } @@ -41,48 +42,52 @@ impl<'a> ResolveCtx<'a> { } } - pub fn get(&mut self, name: String) -> Option { + pub fn get(&mut self, name: String) -> Option { match self.scopes.get_mut(&self.cur_scope) { Some(ref mut scopes) => scopes.get(name), None => None, } } + + pub fn get_span2(&self, node_id: NodeId) -> Span2 { + self.parsing_ctx.identities.get(&node_id).unwrap().clone() + } } impl<'a> Visitor<'a> for ResolveCtx<'a> { fn visit_mod(&mut self, m: &'a Mod) { // We add every top level first for top in &m.top_levels { - match &top.kind { - TopLevelKind::Prototype(p) => { - self.add_to_current_scope((*p.name).clone(), p.identity.clone()); + match &top { + TopLevel::Prototype(p) => { + self.add_to_current_scope((*p.name).clone(), p.node_id); } - TopLevelKind::Use(_u) => (), - TopLevelKind::Trait(t) => { + TopLevel::Use(_u) => (), + TopLevel::Trait(t) => { for proto in &t.defs { - self.add_to_current_scope((*proto.name).clone(), proto.identity.clone()); + self.add_to_current_scope((*proto.name).clone(), proto.node_id); } } - TopLevelKind::Struct(s) => { - self.add_to_current_scope(s.name.get_name(), s.identity.clone()); + TopLevel::Struct(s) => { + self.add_to_current_scope(s.name.name.clone(), s.name.node_id); s.defs.iter().for_each(|p| { - self.add_to_current_scope((*p.name).clone(), p.identity.clone()); + self.add_to_current_scope((*p.name).clone(), p.node_id); }) } - TopLevelKind::Impl(i) => { + TopLevel::Impl(i) => { for proto in &i.defs { let mut proto = proto.clone(); proto.mangle(&i.types.iter().map(|t| t.get_name()).collect::>()); - self.add_to_current_scope((*proto.name).clone(), proto.identity.clone()); + self.add_to_current_scope((*proto.name).clone(), proto.node_id); } } - TopLevelKind::Mod(_, _m) => (), - TopLevelKind::Infix(_, _) => (), - TopLevelKind::Function(f) => { - self.add_to_current_scope((*f.name).clone(), f.identity.clone()); + TopLevel::Mod(_, _m) => (), + TopLevel::Infix(_, _) => (), + TopLevel::Function(f) => { + self.add_to_current_scope((*f.name).clone(), f.node_id); } } } @@ -90,21 +95,36 @@ impl<'a> Visitor<'a> for ResolveCtx<'a> { walk_list!(self, visit_top_level, &m.top_levels); } + // fn visit_for(&mut self, for_loop: &'a For) { + // self.visit_expression(&for_loop.expr); + // self.visit_body(&for_loop.body); + // } + + fn visit_for_in(&mut self, for_in: &'a ForIn) { + self.visit_expression(&for_in.expr); + + self.add_to_current_scope(for_in.value.name.clone(), for_in.value.node_id); + + self.visit_body(&for_in.body); + } + fn visit_assign(&mut self, assign: &'a Assign) { self.visit_expression(&assign.value); match &assign.name { AssignLeftSide::Identifier(id) => { + let ident = id.as_identifier().unwrap(); + if !assign.is_let { - if let Some(previous_assign_node_id) = self.get(id.name.clone()) { + if let Some(previous_assign_node_id) = self.get(ident.name.clone()) { self.resolutions - .insert(id.identity.node_id, previous_assign_node_id.node_id); + .insert(ident.node_id, previous_assign_node_id); } - self.visit_identifier(id) + self.visit_identifier(ident) } - self.add_to_current_scope(id.name.clone(), id.identity.clone()); + self.add_to_current_scope(ident.name.clone(), ident.node_id); } AssignLeftSide::Indice(expr) => self.visit_expression(expr), AssignLeftSide::Dot(expr) => self.visit_expression(expr), @@ -112,17 +132,17 @@ impl<'a> Visitor<'a> for ResolveCtx<'a> { } fn visit_top_level(&mut self, top: &'a TopLevel) { - match &top.kind { - TopLevelKind::Prototype(p) => self.visit_prototype(p), - TopLevelKind::Use(u) => { + match &top { + TopLevel::Prototype(p) => self.visit_prototype(p), + TopLevel::Use(u) => { self.visit_use(u); } - TopLevelKind::Infix(_, _) => (), - TopLevelKind::Trait(t) => self.visit_trait(t), - TopLevelKind::Impl(i) => self.visit_impl(i), - TopLevelKind::Struct(s) => self.visit_struct_decl(s), - TopLevelKind::Function(f) => self.visit_function_decl(f), - TopLevelKind::Mod(name, m) => { + TopLevel::Infix(_, _) => (), + TopLevel::Trait(t) => self.visit_trait(t), + TopLevel::Impl(i) => self.visit_impl(i), + TopLevel::Struct(s) => self.visit_struct_decl(s), + TopLevel::Function(f) => self.visit_function_decl(f), + TopLevel::Mod(name, m) => { let current_mod = self.cur_scope.clone(); self.new_mod(self.cur_scope.child(name.clone())); @@ -143,15 +163,17 @@ impl<'a> Visitor<'a> for ResolveCtx<'a> { } fn visit_struct_ctor(&mut self, s: &'a StructCtor) { - match self.get(s.name.get_name()) { - Some(pointed) => self.resolutions.insert(s.identity.node_id, pointed.node_id), + match self.get(s.name.name.clone()) { + Some(pointed) => self.resolutions.insert(s.name.node_id, pointed), None => self .parsing_ctx .diagnostics - .push_error(Diagnostic::new_unknown_identifier(s.identity.span.clone())), + .push_error(Diagnostic::new_unknown_identifier( + self.get_span2(s.name.node_id).into(), + )), }; - self.visit_type(&s.name); + self.visit_identifier(&s.name); walk_struct_ctor(self, s); } @@ -159,7 +181,13 @@ impl<'a> Visitor<'a> for ResolveCtx<'a> { fn visit_function_decl(&mut self, f: &'a FunctionDecl) { self.push_scope(); - walk_function_decl(self, f); + self.visit_identifier(&f.name); + + for arg in &f.arguments { + self.add_to_current_scope(arg.name.clone(), arg.node_id); + } + + self.visit_body(&f.body); self.pop_scope(); } @@ -177,7 +205,7 @@ impl<'a> Visitor<'a> for ResolveCtx<'a> { match self.scopes.get(&mod_path) { Some(scopes) => { - if ident.name == "*" { + if ident.name == "(*)" { let scope = scopes.scopes.get(0).unwrap(); for (k, v) in &scope.items.clone() { @@ -189,7 +217,9 @@ impl<'a> Visitor<'a> for ResolveCtx<'a> { self.add_to_current_scope((*ident).name.clone(), pointed); } None => self.parsing_ctx.diagnostics.push_error( - Diagnostic::new_unknown_identifier(ident.identity.span.clone()), + Diagnostic::new_unknown_identifier( + self.get_span2(ident.node_id).into(), + ), ), }; } @@ -199,7 +229,7 @@ impl<'a> Visitor<'a> for ResolveCtx<'a> { .parsing_ctx .diagnostics .push_error(Diagnostic::new_module_not_found( - ident.identity.span.clone(), + self.get_span2(ident.node_id).into(), mod_path .path .iter() @@ -210,10 +240,10 @@ impl<'a> Visitor<'a> for ResolveCtx<'a> { }; } - fn visit_argument_decl(&mut self, arg: &'a ArgumentDecl) { - self.add_to_current_scope(arg.name.clone(), arg.identity.clone()); - } - + /* fn visit_argument_decl(&mut self, arg: &'a ArgumentDecl) { + self.add_to_current_scope(arg.name.clone(), arg.node_id); + } + */ fn visit_identifier_path(&mut self, path: &'a IdentifierPath) { let ident = path.last_segment_ref(); @@ -229,14 +259,12 @@ impl<'a> Visitor<'a> for ResolveCtx<'a> { match self.scopes.get(&mod_path) { Some(scopes) => match scopes.get((*ident).to_string()) { - Some(pointed) => self - .resolutions - .insert(ident.identity.node_id, pointed.node_id), + Some(pointed) => self.resolutions.insert(ident.node_id, pointed), None => { self.parsing_ctx .diagnostics .push_error(Diagnostic::new_unknown_identifier( - ident.identity.span.clone(), + self.get_span2(ident.node_id).into(), )) } }, @@ -246,7 +274,7 @@ impl<'a> Visitor<'a> for ResolveCtx<'a> { .parsing_ctx .diagnostics .push_error(Diagnostic::new_module_not_found( - ident.identity.span.clone(), + self.get_span2(ident.node_id).into(), mod_path .path .iter() @@ -259,13 +287,13 @@ impl<'a> Visitor<'a> for ResolveCtx<'a> { fn visit_identifier(&mut self, id: &'a Identifier) { match self.get((*id).to_string()) { - Some(pointed) => self - .resolutions - .insert(id.identity.node_id, pointed.node_id), + Some(pointed) => self.resolutions.insert(id.node_id, pointed), None => self .parsing_ctx .diagnostics - .push_error(Diagnostic::new_unknown_identifier(id.identity.span.clone())), + .push_error(Diagnostic::new_unknown_identifier( + self.get_span2(id.node_id).into(), + )), }; } } diff --git a/src/lib/resolver/unused_collector.rs b/src/lib/resolver/unused_collector.rs index fa856025..2b59d353 100644 --- a/src/lib/resolver/unused_collector.rs +++ b/src/lib/resolver/unused_collector.rs @@ -1,6 +1,9 @@ use std::collections::HashMap; -use crate::{ast::*, resolver::ResolutionMap}; +use crate::{ + ast::{tree::*, visit::*, NodeId}, + resolver::ResolutionMap, +}; #[derive(Debug, Default)] pub struct UnusedCollector { @@ -37,23 +40,23 @@ impl<'a> Visitor<'a> for UnusedCollector { // We add every top level first for top in &m.top_levels { - match &top.kind { - TopLevelKind::Prototype(_p) => {} - TopLevelKind::Use(_u) => (), - TopLevelKind::Trait(t) => { + match &top { + TopLevel::Prototype(_p) => {} + TopLevel::Use(_u) => (), + TopLevel::Trait(t) => { for f in &t.defs { - self.method_list.insert(f.identity.node_id, false); + self.method_list.insert(f.node_id, false); } } - TopLevelKind::Impl(_i) => {} - TopLevelKind::Struct(_s) => {} - TopLevelKind::Mod(_, _m) => (), - TopLevelKind::Infix(_, _) => (), - TopLevelKind::Function(f) => { - self.fn_list.insert(f.identity.node_id, false); + TopLevel::Impl(_i) => {} + TopLevel::Struct(_s) => {} + TopLevel::Mod(_, _m) => (), + TopLevel::Infix(_, _) => (), + TopLevel::Function(f) => { + self.fn_list.insert(f.node_id, false); if f.name.name == *"main" { - self.fn_list.insert(f.identity.node_id, true); + self.fn_list.insert(f.node_id, true); } } } @@ -63,18 +66,18 @@ impl<'a> Visitor<'a> for UnusedCollector { } fn visit_top_level(&mut self, top_level: &'a TopLevel) { - match &top_level.kind { - TopLevelKind::Prototype(p) => self.visit_prototype(p), - TopLevelKind::Use(_u) => (), - TopLevelKind::Trait(t) => self.visit_trait(t), - TopLevelKind::Impl(i) => self.visit_impl(i), - TopLevelKind::Struct(i) => self.visit_struct_decl(i), - TopLevelKind::Mod(name, m) => { + match &top_level { + TopLevel::Prototype(p) => self.visit_prototype(p), + TopLevel::Use(_u) => (), + TopLevel::Trait(t) => self.visit_trait(t), + TopLevel::Impl(i) => self.visit_impl(i), + TopLevel::Struct(i) => self.visit_struct_decl(i), + TopLevel::Mod(name, m) => { self.visit_identifier(name); self.visit_mod(m); } - TopLevelKind::Function(f) => self.visit_function_decl(f), - TopLevelKind::Infix(_ident, _) => (), + TopLevel::Function(f) => self.visit_function_decl(f), + TopLevel::Infix(_ident, _) => (), }; } @@ -83,13 +86,13 @@ impl<'a> Visitor<'a> for UnusedCollector { } fn visit_function_decl(&mut self, f: &'a FunctionDecl) { - walk_list!(self, visit_argument_decl, &f.arguments); + walk_list!(self, visit_identifier, &f.arguments); self.visit_body(&f.body); } fn visit_identifier(&mut self, id: &'a Identifier) { - if let Some(reso) = self.resolutions.get_recur(&id.identity.node_id) { + if let Some(reso) = self.resolutions.get_recur(&id.node_id) { if let Some(used) = self.fn_list.get_mut(&reso) { *used = true; } else if let Some(used) = self.method_list.get_mut(&reso) { diff --git a/src/lib/rock.rs b/src/lib/rock.rs index 68d2cdee..6028601d 100644 --- a/src/lib/rock.rs +++ b/src/lib/rock.rs @@ -1,14 +1,16 @@ -#![feature(associated_type_bounds, destructuring_assignment, derive_default_enum)] +#![feature(associated_type_bounds)] #[macro_use] extern crate serde_derive; -#[macro_use] extern crate bitflags; #[macro_use] extern crate log; +#[macro_use] +extern crate nom_locate; + use std::path::PathBuf; #[macro_use] @@ -62,7 +64,7 @@ pub fn compile_str(input: &SourceFile, config: &Config) -> Result<(), Diagnostic pub fn parse_str(parsing_ctx: &mut ParsingCtx, config: &Config) -> Result { // Text to Ast debug!(" -> Parsing"); - let mut ast = parser::parse_root(parsing_ctx)?; + let mut ast = parser::parse(parsing_ctx)?; // Name resolving debug!(" -> Resolving"); @@ -86,6 +88,7 @@ pub fn generate_ir(hir: hir::Root, config: &Config) -> Result<(), Diagnostic> { Ok(()) } + mod test { use super::*; use crate::{parser::SourceFile, Config}; diff --git a/src/lib/testcases/basic/multiline_struct_const/main.rk b/src/lib/testcases/basic/multiline_struct_const/main.rk index 4beec3c8..ee020c56 100644 --- a/src/lib/testcases/basic/multiline_struct_const/main.rk +++ b/src/lib/testcases/basic/multiline_struct_const/main.rk @@ -3,9 +3,8 @@ struct Foo titi :: Float64 main = - let lol = - Foo - toto: 42 - titi: 10.2 + let lol = Foo + toto: 42 + titi: 10.2 lol.toto diff --git a/src/lib/testcases/basic/nested_array/main.rk b/src/lib/testcases/basic/nested_array/main.rk index 1b184563..e5ac41a4 100644 --- a/src/lib/testcases/basic/nested_array/main.rk +++ b/src/lib/testcases/basic/nested_array/main.rk @@ -1,2 +1 @@ -lol = [[12, 24], [42, 0]] -main = lol()[1][0] +main = [[12, 24], [42, 0]][1][0] diff --git a/src/lib/testcases/basic/nested_struct_dect_multiline/main.rk b/src/lib/testcases/basic/nested_struct_dect_multiline/main.rk index 58fa03dc..362542a8 100644 --- a/src/lib/testcases/basic/nested_struct_dect_multiline/main.rk +++ b/src/lib/testcases/basic/nested_struct_dect_multiline/main.rk @@ -6,12 +6,9 @@ struct Foo titi :: Float64 main = - let lol = - Foo - toto: - Bar - mdr: 42 - titi: - 10.2 + let lol = Foo + toto: Bar + mdr: 42 + titi: 10.2 lol.toto.mdr diff --git a/src/lib/testcases/basic/reassign_return/main.rk b/src/lib/testcases/basic/reassign_return/main.rk new file mode 100644 index 00000000..139401ae --- /dev/null +++ b/src/lib/testcases/basic/reassign_return/main.rk @@ -0,0 +1,7 @@ +infix + 4 ++ a b = ~IAdd a b + +main = + let i = 21 + i = i + 21 + i diff --git a/src/lib/testcases/basic/reassign_return/main.rk.out b/src/lib/testcases/basic/reassign_return/main.rk.out new file mode 100644 index 00000000..f70d7bba --- /dev/null +++ b/src/lib/testcases/basic/reassign_return/main.rk.out @@ -0,0 +1 @@ +42 \ No newline at end of file diff --git a/src/lib/testcases/basic/reassign_return/main.rk.stdout b/src/lib/testcases/basic/reassign_return/main.rk.stdout new file mode 100644 index 00000000..e69de29b diff --git a/src/lib/testcases/basic/struct_array_field/main.rk b/src/lib/testcases/basic/struct_array_field/main.rk index 39c83411..e98935a4 100644 --- a/src/lib/testcases/basic/struct_array_field/main.rk +++ b/src/lib/testcases/basic/struct_array_field/main.rk @@ -3,9 +3,8 @@ struct Foo titi :: Float64 main = - let lol = - Foo - titi: 10.2 - toto: [10, 42] + let lol = Foo + titi: 10.2 + toto: [10, 42] lol.toto[1] diff --git a/src/lib/testcases/basic/while/main.rk b/src/lib/testcases/basic/while/main.rk new file mode 100644 index 00000000..392e7206 --- /dev/null +++ b/src/lib/testcases/basic/while/main.rk @@ -0,0 +1,12 @@ +infix + 4 ++ a b = ~IAdd a b + +infix < 3 +< a b = ~Ilt a b + +main = + let i = 0 + let x = 42 + while i < x + i = i + 1 + i diff --git a/src/lib/testcases/basic/while/main.rk.out b/src/lib/testcases/basic/while/main.rk.out new file mode 100644 index 00000000..f70d7bba --- /dev/null +++ b/src/lib/testcases/basic/while/main.rk.out @@ -0,0 +1 @@ +42 \ No newline at end of file diff --git a/src/lib/testcases/basic/while/main.rk.stdout b/src/lib/testcases/basic/while/main.rk.stdout new file mode 100644 index 00000000..e69de29b diff --git a/src/lib/testcases/mods/basic_mod/main.rk b/src/lib/testcases/mods/basic_mod/main.rk index dcb4f719..d80c607b 100644 --- a/src/lib/testcases/mods/basic_mod/main.rk +++ b/src/lib/testcases/mods/basic_mod/main.rk @@ -1,7 +1,6 @@ mod _std -use _std::+ -use _std::* +use _std::(*) test g h i j = g diff --git a/src/lib/testcases/mods/full_fact/main.rk b/src/lib/testcases/mods/full_fact/main.rk index 13b47ffd..481a0b29 100644 --- a/src/lib/testcases/mods/full_fact/main.rk +++ b/src/lib/testcases/mods/full_fact/main.rk @@ -1,6 +1,6 @@ mod lib -use lib::prelude::* +use lib::prelude::(*) fact a = if a <= 1 diff --git a/src/lib/testcases/mods/full_fact/prelude.rk b/src/lib/testcases/mods/full_fact/prelude.rk index 7aea7a8b..4370815b 100644 --- a/src/lib/testcases/mods/full_fact/prelude.rk +++ b/src/lib/testcases/mods/full_fact/prelude.rk @@ -1,5 +1,5 @@ use super::print::print use super::show::show -use super::num::* -use super::eq::* -# use super::helpers::* +use super::num::(*) +use super::eq::(*) +# use super::helpers::(*) diff --git a/src/lib/testcases/mods/func_arg_resolution/main.rk b/src/lib/testcases/mods/func_arg_resolution/main.rk index 652ef2b0..a1ecd045 100644 --- a/src/lib/testcases/mods/func_arg_resolution/main.rk +++ b/src/lib/testcases/mods/func_arg_resolution/main.rk @@ -1,7 +1,6 @@ mod _std -use _std::|> -use _std::+ +use _std::(*) f a = a + 2 diff --git a/src/lib/testcases/mods/nested_trait_resolution/main.rk b/src/lib/testcases/mods/nested_trait_resolution/main.rk index 5faea77a..3a88b657 100644 --- a/src/lib/testcases/mods/nested_trait_resolution/main.rk +++ b/src/lib/testcases/mods/nested_trait_resolution/main.rk @@ -1,7 +1,6 @@ mod _std -use _std::|> -use _std::+ +use _std::(*) f a = a + 2 main = 40 |> f diff --git a/src/lib/testcases/mods/struct_new/lib.rk b/src/lib/testcases/mods/struct_new/lib.rk new file mode 100644 index 00000000..f8adda6e --- /dev/null +++ b/src/lib/testcases/mods/struct_new/lib.rk @@ -0,0 +1,8 @@ +struct Foo + i :: Int64 + s :: String + +new_foo str = + Foo + i: 42 + s: str diff --git a/src/lib/testcases/mods/struct_new/main.rk b/src/lib/testcases/mods/struct_new/main.rk new file mode 100644 index 00000000..d3915208 --- /dev/null +++ b/src/lib/testcases/mods/struct_new/main.rk @@ -0,0 +1,7 @@ +mod lib + +use lib::(*) + +main = + let foo = new_foo "bar" + foo.i diff --git a/src/lib/testcases/mods/struct_new/main.rk.out b/src/lib/testcases/mods/struct_new/main.rk.out new file mode 100644 index 00000000..f70d7bba --- /dev/null +++ b/src/lib/testcases/mods/struct_new/main.rk.out @@ -0,0 +1 @@ +42 \ No newline at end of file diff --git a/src/lib/testcases/mods/struct_new/main.rk.stdout b/src/lib/testcases/mods/struct_new/main.rk.stdout new file mode 100644 index 00000000..e69de29b diff --git a/src/lib/testcases/mods/unused_fn/main.rk b/src/lib/testcases/mods/unused_fn/main.rk index 84cc01ce..4d476bfb 100644 --- a/src/lib/testcases/mods/unused_fn/main.rk +++ b/src/lib/testcases/mods/unused_fn/main.rk @@ -1,8 +1,6 @@ mod _std -use _std::+ -use _std::* -use _std::- +use _std::(*) test g h = g diff --git a/src/lib/testcases/mods/unused_impl_fn/main.rk b/src/lib/testcases/mods/unused_impl_fn/main.rk index 36b5c972..61b3a75d 100644 --- a/src/lib/testcases/mods/unused_impl_fn/main.rk +++ b/src/lib/testcases/mods/unused_impl_fn/main.rk @@ -1,7 +1,6 @@ mod _std -use _std::+ -use _std::* +use _std::(*) test g h i j = g diff --git a/src/lib/tests.rs b/src/lib/tests.rs index 31e211a8..e581e18d 100644 --- a/src/lib/tests.rs +++ b/src/lib/tests.rs @@ -1,183 +1,195 @@ use std::path::PathBuf; #[allow(dead_code)] -fn run(path: &str, input: &str, expected_ret: &str, expected_output: &str) { - let mut config = super::Config::default(); + fn run(path: &str, input: &str, expected_ret: &str, expected_output: &str) { + let mut config = super::Config::default(); - config.project_config.entry_point = PathBuf::from(path); + config.project_config.entry_point = PathBuf::from(path); - let expected_ret = expected_ret.parse::().unwrap(); + let expected_ret = expected_ret.parse::().unwrap(); - let (ret_code, stdout) = super::test::run(path, input.to_string(), config); + let (ret_code, stdout) = super::test::run(path, input.to_string(), config); - assert_eq!(expected_ret, ret_code); - assert_eq!(expected_output, stdout); + assert_eq!(expected_ret, ret_code); + assert_eq!(expected_output, stdout); + } + #[test] +fn testcases_mods_basic_mod_main() { + run("testcases/mods/basic_mod/main.rk", include_str!("testcases/mods/basic_mod/main.rk"), include_str!("testcases/mods/basic_mod/main.rk.out"), include_str!("testcases/mods/basic_mod/main.rk.stdout")); } #[test] -fn testcases_trait_late_resolution_main() { - run("testcases/trait/late_resolution/main.rk", include_str!("testcases/trait/late_resolution/main.rk"), include_str!("testcases/trait/late_resolution/main.rk.out"), include_str!("testcases/trait/late_resolution/main.rk.stdout")); +fn testcases_mods_func_arg_resolution_main() { + run("testcases/mods/func_arg_resolution/main.rk", include_str!("testcases/mods/func_arg_resolution/main.rk"), include_str!("testcases/mods/func_arg_resolution/main.rk.out"), include_str!("testcases/mods/func_arg_resolution/main.rk.stdout")); } #[test] -fn testcases_trait_multi_resolution_main() { - run("testcases/trait/multi_resolution/main.rk", include_str!("testcases/trait/multi_resolution/main.rk"), include_str!("testcases/trait/multi_resolution/main.rk.out"), include_str!("testcases/trait/multi_resolution/main.rk.stdout")); +fn testcases_mods_unused_impl_fn_main() { + run("testcases/mods/unused_impl_fn/main.rk", include_str!("testcases/mods/unused_impl_fn/main.rk"), include_str!("testcases/mods/unused_impl_fn/main.rk.out"), include_str!("testcases/mods/unused_impl_fn/main.rk.stdout")); } #[test] -fn testcases_fails_basic_fn_bad_arg_nb2_main() { - run("testcases/fails/basic/fn_bad_arg_nb2/main.rk", include_str!("testcases/fails/basic/fn_bad_arg_nb2/main.rk"), include_str!("testcases/fails/basic/fn_bad_arg_nb2/main.rk.out"), include_str!("testcases/fails/basic/fn_bad_arg_nb2/main.rk.stdout")); +fn testcases_mods_unused_fn_main() { + run("testcases/mods/unused_fn/main.rk", include_str!("testcases/mods/unused_fn/main.rk"), include_str!("testcases/mods/unused_fn/main.rk.out"), include_str!("testcases/mods/unused_fn/main.rk.stdout")); } #[test] -fn testcases_fails_basic_fn_bad_arg_nb_main() { - run("testcases/fails/basic/fn_bad_arg_nb/main.rk", include_str!("testcases/fails/basic/fn_bad_arg_nb/main.rk"), include_str!("testcases/fails/basic/fn_bad_arg_nb/main.rk.out"), include_str!("testcases/fails/basic/fn_bad_arg_nb/main.rk.stdout")); +fn testcases_mods_full_fact_main() { + run("testcases/mods/full_fact/main.rk", include_str!("testcases/mods/full_fact/main.rk"), include_str!("testcases/mods/full_fact/main.rk.out"), include_str!("testcases/mods/full_fact/main.rk.stdout")); } #[test] -fn testcases_fails_basic_struct_bad_field_type_main() { - run("testcases/fails/basic/struct_bad_field_type/main.rk", include_str!("testcases/fails/basic/struct_bad_field_type/main.rk"), include_str!("testcases/fails/basic/struct_bad_field_type/main.rk.out"), include_str!("testcases/fails/basic/struct_bad_field_type/main.rk.stdout")); +fn testcases_mods_nested_trait_resolution_main() { + run("testcases/mods/nested_trait_resolution/main.rk", include_str!("testcases/mods/nested_trait_resolution/main.rk"), include_str!("testcases/mods/nested_trait_resolution/main.rk.out"), include_str!("testcases/mods/nested_trait_resolution/main.rk.stdout")); } #[test] -fn testcases_fails_basic_fn_bad_arg_main() { - run("testcases/fails/basic/fn_bad_arg/main.rk", include_str!("testcases/fails/basic/fn_bad_arg/main.rk"), include_str!("testcases/fails/basic/fn_bad_arg/main.rk.out"), include_str!("testcases/fails/basic/fn_bad_arg/main.rk.stdout")); +fn testcases_mods_struct_new_main() { + run("testcases/mods/struct_new/main.rk", include_str!("testcases/mods/struct_new/main.rk"), include_str!("testcases/mods/struct_new/main.rk.out"), include_str!("testcases/mods/struct_new/main.rk.stdout")); } #[test] -fn testcases_mods_unused_impl_fn_main() { - run("testcases/mods/unused_impl_fn/main.rk", include_str!("testcases/mods/unused_impl_fn/main.rk"), include_str!("testcases/mods/unused_impl_fn/main.rk.out"), include_str!("testcases/mods/unused_impl_fn/main.rk.stdout")); +fn testcases_fails_basic_fn_bad_arg_nb2_main() { + run("testcases/fails/basic/fn_bad_arg_nb2/main.rk", include_str!("testcases/fails/basic/fn_bad_arg_nb2/main.rk"), include_str!("testcases/fails/basic/fn_bad_arg_nb2/main.rk.out"), include_str!("testcases/fails/basic/fn_bad_arg_nb2/main.rk.stdout")); } #[test] -fn testcases_mods_unused_fn_main() { - run("testcases/mods/unused_fn/main.rk", include_str!("testcases/mods/unused_fn/main.rk"), include_str!("testcases/mods/unused_fn/main.rk.out"), include_str!("testcases/mods/unused_fn/main.rk.stdout")); +fn testcases_fails_basic_struct_bad_field_type_main() { + run("testcases/fails/basic/struct_bad_field_type/main.rk", include_str!("testcases/fails/basic/struct_bad_field_type/main.rk"), include_str!("testcases/fails/basic/struct_bad_field_type/main.rk.out"), include_str!("testcases/fails/basic/struct_bad_field_type/main.rk.stdout")); } #[test] -fn testcases_mods_nested_trait_resolution_main() { - run("testcases/mods/nested_trait_resolution/main.rk", include_str!("testcases/mods/nested_trait_resolution/main.rk"), include_str!("testcases/mods/nested_trait_resolution/main.rk.out"), include_str!("testcases/mods/nested_trait_resolution/main.rk.stdout")); +fn testcases_fails_basic_fn_bad_arg_nb_main() { + run("testcases/fails/basic/fn_bad_arg_nb/main.rk", include_str!("testcases/fails/basic/fn_bad_arg_nb/main.rk"), include_str!("testcases/fails/basic/fn_bad_arg_nb/main.rk.out"), include_str!("testcases/fails/basic/fn_bad_arg_nb/main.rk.stdout")); } #[test] -fn testcases_mods_full_fact_main() { - run("testcases/mods/full_fact/main.rk", include_str!("testcases/mods/full_fact/main.rk"), include_str!("testcases/mods/full_fact/main.rk.out"), include_str!("testcases/mods/full_fact/main.rk.stdout")); +fn testcases_fails_basic_fn_bad_arg_main() { + run("testcases/fails/basic/fn_bad_arg/main.rk", include_str!("testcases/fails/basic/fn_bad_arg/main.rk"), include_str!("testcases/fails/basic/fn_bad_arg/main.rk.out"), include_str!("testcases/fails/basic/fn_bad_arg/main.rk.stdout")); } #[test] -fn testcases_mods_func_arg_resolution_main() { - run("testcases/mods/func_arg_resolution/main.rk", include_str!("testcases/mods/func_arg_resolution/main.rk"), include_str!("testcases/mods/func_arg_resolution/main.rk.out"), include_str!("testcases/mods/func_arg_resolution/main.rk.stdout")); +fn testcases_basic_if_else_main() { + run("testcases/basic/if_else/main.rk", include_str!("testcases/basic/if_else/main.rk"), include_str!("testcases/basic/if_else/main.rk.out"), include_str!("testcases/basic/if_else/main.rk.stdout")); } #[test] -fn testcases_mods_basic_mod_main() { - run("testcases/mods/basic_mod/main.rk", include_str!("testcases/mods/basic_mod/main.rk"), include_str!("testcases/mods/basic_mod/main.rk.out"), include_str!("testcases/mods/basic_mod/main.rk.stdout")); +fn testcases_basic_1_arg_fn_main() { + run("testcases/basic/1_arg_fn/main.rk", include_str!("testcases/basic/1_arg_fn/main.rk"), include_str!("testcases/basic/1_arg_fn/main.rk.out"), include_str!("testcases/basic/1_arg_fn/main.rk.stdout")); } #[test] -fn testcases_basic_bool_true_main() { - run("testcases/basic/bool_true/main.rk", include_str!("testcases/basic/bool_true/main.rk"), include_str!("testcases/basic/bool_true/main.rk.out"), include_str!("testcases/basic/bool_true/main.rk.stdout")); +fn testcases_basic_indice_assign_main() { + run("testcases/basic/indice_assign/main.rk", include_str!("testcases/basic/indice_assign/main.rk"), include_str!("testcases/basic/indice_assign/main.rk.out"), include_str!("testcases/basic/indice_assign/main.rk.stdout")); } #[test] -fn testcases_basic_op_func_main() { - run("testcases/basic/op_func/main.rk", include_str!("testcases/basic/op_func/main.rk"), include_str!("testcases/basic/op_func/main.rk.out"), include_str!("testcases/basic/op_func/main.rk.stdout")); +fn testcases_basic_extern_main() { + run("testcases/basic/extern/main.rk", include_str!("testcases/basic/extern/main.rk"), include_str!("testcases/basic/extern/main.rk.out"), include_str!("testcases/basic/extern/main.rk.stdout")); +} +#[test] +fn testcases_basic_monomorph_in_trait_main() { + run("testcases/basic/monomorph_in_trait/main.rk", include_str!("testcases/basic/monomorph_in_trait/main.rk"), include_str!("testcases/basic/monomorph_in_trait/main.rk.out"), include_str!("testcases/basic/monomorph_in_trait/main.rk.stdout")); } #[test] fn testcases_basic_multiline_struct_const_main() { run("testcases/basic/multiline_struct_const/main.rk", include_str!("testcases/basic/multiline_struct_const/main.rk"), include_str!("testcases/basic/multiline_struct_const/main.rk.out"), include_str!("testcases/basic/multiline_struct_const/main.rk.stdout")); } #[test] -fn testcases_basic_2_arg_fn_main() { - run("testcases/basic/2_arg_fn/main.rk", include_str!("testcases/basic/2_arg_fn/main.rk"), include_str!("testcases/basic/2_arg_fn/main.rk.out"), include_str!("testcases/basic/2_arg_fn/main.rk.stdout")); +fn testcases_basic_nested_struct_dect_multiline_main() { + run("testcases/basic/nested_struct_dect_multiline/main.rk", include_str!("testcases/basic/nested_struct_dect_multiline/main.rk"), include_str!("testcases/basic/nested_struct_dect_multiline/main.rk.out"), include_str!("testcases/basic/nested_struct_dect_multiline/main.rk.stdout")); } #[test] -fn testcases_basic_monomorph_in_trait_main() { - run("testcases/basic/monomorph_in_trait/main.rk", include_str!("testcases/basic/monomorph_in_trait/main.rk"), include_str!("testcases/basic/monomorph_in_trait/main.rk.out"), include_str!("testcases/basic/monomorph_in_trait/main.rk.stdout")); +fn testcases_basic_let_main() { + run("testcases/basic/let/main.rk", include_str!("testcases/basic/let/main.rk"), include_str!("testcases/basic/let/main.rk.out"), include_str!("testcases/basic/let/main.rk.stdout")); } #[test] -fn testcases_basic_reassign_self_main() { - run("testcases/basic/reassign_self/main.rk", include_str!("testcases/basic/reassign_self/main.rk"), include_str!("testcases/basic/reassign_self/main.rk.out"), include_str!("testcases/basic/reassign_self/main.rk.stdout")); +fn testcases_basic_struct_array_field_main() { + run("testcases/basic/struct_array_field/main.rk", include_str!("testcases/basic/struct_array_field/main.rk"), include_str!("testcases/basic/struct_array_field/main.rk.out"), include_str!("testcases/basic/struct_array_field/main.rk.stdout")); } #[test] fn testcases_basic_trait_use_before_decl_main() { run("testcases/basic/trait_use_before_decl/main.rk", include_str!("testcases/basic/trait_use_before_decl/main.rk"), include_str!("testcases/basic/trait_use_before_decl/main.rk.out"), include_str!("testcases/basic/trait_use_before_decl/main.rk.stdout")); } #[test] -fn testcases_basic_nested_struct_dect_multiline_main() { - run("testcases/basic/nested_struct_dect_multiline/main.rk", include_str!("testcases/basic/nested_struct_dect_multiline/main.rk"), include_str!("testcases/basic/nested_struct_dect_multiline/main.rk.out"), include_str!("testcases/basic/nested_struct_dect_multiline/main.rk.stdout")); -} -#[test] -fn testcases_basic_array_main() { - run("testcases/basic/array/main.rk", include_str!("testcases/basic/array/main.rk"), include_str!("testcases/basic/array/main.rk.out"), include_str!("testcases/basic/array/main.rk.stdout")); -} -#[test] -fn testcases_basic_nested_struct_main() { - run("testcases/basic/nested_struct/main.rk", include_str!("testcases/basic/nested_struct/main.rk"), include_str!("testcases/basic/nested_struct/main.rk.out"), include_str!("testcases/basic/nested_struct/main.rk.stdout")); +fn testcases_basic_while_main() { + run("testcases/basic/while/main.rk", include_str!("testcases/basic/while/main.rk"), include_str!("testcases/basic/while/main.rk.out"), include_str!("testcases/basic/while/main.rk.stdout")); } #[test] fn testcases_basic_simple_struct_main() { run("testcases/basic/simple_struct/main.rk", include_str!("testcases/basic/simple_struct/main.rk"), include_str!("testcases/basic/simple_struct/main.rk.out"), include_str!("testcases/basic/simple_struct/main.rk.stdout")); } #[test] -fn testcases_basic_reassign_main() { - run("testcases/basic/reassign/main.rk", include_str!("testcases/basic/reassign/main.rk"), include_str!("testcases/basic/reassign/main.rk.out"), include_str!("testcases/basic/reassign/main.rk.stdout")); -} -#[test] -fn testcases_basic_indice_assign_main() { - run("testcases/basic/indice_assign/main.rk", include_str!("testcases/basic/indice_assign/main.rk"), include_str!("testcases/basic/indice_assign/main.rk.out"), include_str!("testcases/basic/indice_assign/main.rk.stdout")); +fn testcases_basic_bool_false_main() { + run("testcases/basic/bool_false/main.rk", include_str!("testcases/basic/bool_false/main.rk"), include_str!("testcases/basic/bool_false/main.rk.out"), include_str!("testcases/basic/bool_false/main.rk.stdout")); } #[test] -fn testcases_basic_dot_assign_main() { - run("testcases/basic/dot_assign/main.rk", include_str!("testcases/basic/dot_assign/main.rk"), include_str!("testcases/basic/dot_assign/main.rk.out"), include_str!("testcases/basic/dot_assign/main.rk.stdout")); +fn testcases_basic_nested_array_main() { + run("testcases/basic/nested_array/main.rk", include_str!("testcases/basic/nested_array/main.rk"), include_str!("testcases/basic/nested_array/main.rk.out"), include_str!("testcases/basic/nested_array/main.rk.stdout")); } #[test] -fn testcases_basic_extern_main() { - run("testcases/basic/extern/main.rk", include_str!("testcases/basic/extern/main.rk"), include_str!("testcases/basic/extern/main.rk.out"), include_str!("testcases/basic/extern/main.rk.stdout")); +fn testcases_basic_reassign_return_main() { + run("testcases/basic/reassign_return/main.rk", include_str!("testcases/basic/reassign_return/main.rk"), include_str!("testcases/basic/reassign_return/main.rk.out"), include_str!("testcases/basic/reassign_return/main.rk.stdout")); } #[test] -fn testcases_basic_1_arg_fn_main() { - run("testcases/basic/1_arg_fn/main.rk", include_str!("testcases/basic/1_arg_fn/main.rk"), include_str!("testcases/basic/1_arg_fn/main.rk.out"), include_str!("testcases/basic/1_arg_fn/main.rk.stdout")); +fn testcases_basic_reassign_main() { + run("testcases/basic/reassign/main.rk", include_str!("testcases/basic/reassign/main.rk"), include_str!("testcases/basic/reassign/main.rk.out"), include_str!("testcases/basic/reassign/main.rk.stdout")); } #[test] fn testcases_basic_monomorph_main() { run("testcases/basic/monomorph/main.rk", include_str!("testcases/basic/monomorph/main.rk"), include_str!("testcases/basic/monomorph/main.rk.out"), include_str!("testcases/basic/monomorph/main.rk.stdout")); } #[test] -fn testcases_basic_bool_false_main() { - run("testcases/basic/bool_false/main.rk", include_str!("testcases/basic/bool_false/main.rk"), include_str!("testcases/basic/bool_false/main.rk.out"), include_str!("testcases/basic/bool_false/main.rk.stdout")); +fn testcases_basic_op_func_main() { + run("testcases/basic/op_func/main.rk", include_str!("testcases/basic/op_func/main.rk"), include_str!("testcases/basic/op_func/main.rk.out"), include_str!("testcases/basic/op_func/main.rk.stdout")); } #[test] -fn testcases_basic_struct_array_field_main() { - run("testcases/basic/struct_array_field/main.rk", include_str!("testcases/basic/struct_array_field/main.rk"), include_str!("testcases/basic/struct_array_field/main.rk.out"), include_str!("testcases/basic/struct_array_field/main.rk.stdout")); +fn testcases_basic_struct_index_main() { + run("testcases/basic/struct_index/main.rk", include_str!("testcases/basic/struct_index/main.rk"), include_str!("testcases/basic/struct_index/main.rk.out"), include_str!("testcases/basic/struct_index/main.rk.stdout")); } #[test] -fn testcases_basic_main_main() { - run("testcases/basic/main/main.rk", include_str!("testcases/basic/main/main.rk"), include_str!("testcases/basic/main/main.rk.out"), include_str!("testcases/basic/main/main.rk.stdout")); +fn testcases_basic_0_arg_fn_main() { + run("testcases/basic/0_arg_fn/main.rk", include_str!("testcases/basic/0_arg_fn/main.rk"), include_str!("testcases/basic/0_arg_fn/main.rk.out"), include_str!("testcases/basic/0_arg_fn/main.rk.stdout")); } #[test] fn testcases_basic_fn_arg_main() { run("testcases/basic/fn_arg/main.rk", include_str!("testcases/basic/fn_arg/main.rk"), include_str!("testcases/basic/fn_arg/main.rk.out"), include_str!("testcases/basic/fn_arg/main.rk.stdout")); } #[test] -fn testcases_basic_recur_main() { - run("testcases/basic/recur/main.rk", include_str!("testcases/basic/recur/main.rk"), include_str!("testcases/basic/recur/main.rk.out"), include_str!("testcases/basic/recur/main.rk.stdout")); +fn testcases_basic_fn_arg_array_main() { + run("testcases/basic/fn_arg_array/main.rk", include_str!("testcases/basic/fn_arg_array/main.rk"), include_str!("testcases/basic/fn_arg_array/main.rk.out"), include_str!("testcases/basic/fn_arg_array/main.rk.stdout")); } #[test] -fn testcases_basic_struct_index_main() { - run("testcases/basic/struct_index/main.rk", include_str!("testcases/basic/struct_index/main.rk"), include_str!("testcases/basic/struct_index/main.rk.out"), include_str!("testcases/basic/struct_index/main.rk.stdout")); +fn testcases_basic_trait_monomorph_main() { + run("testcases/basic/trait_monomorph/main.rk", include_str!("testcases/basic/trait_monomorph/main.rk"), include_str!("testcases/basic/trait_monomorph/main.rk.out"), include_str!("testcases/basic/trait_monomorph/main.rk.stdout")); } #[test] -fn testcases_basic_nested_array_main() { - run("testcases/basic/nested_array/main.rk", include_str!("testcases/basic/nested_array/main.rk"), include_str!("testcases/basic/nested_array/main.rk.out"), include_str!("testcases/basic/nested_array/main.rk.stdout")); +fn testcases_basic_nested_struct_main() { + run("testcases/basic/nested_struct/main.rk", include_str!("testcases/basic/nested_struct/main.rk"), include_str!("testcases/basic/nested_struct/main.rk.out"), include_str!("testcases/basic/nested_struct/main.rk.stdout")); } #[test] -fn testcases_basic_if_else_main() { - run("testcases/basic/if_else/main.rk", include_str!("testcases/basic/if_else/main.rk"), include_str!("testcases/basic/if_else/main.rk.out"), include_str!("testcases/basic/if_else/main.rk.stdout")); +fn testcases_basic_reassign_self_main() { + run("testcases/basic/reassign_self/main.rk", include_str!("testcases/basic/reassign_self/main.rk"), include_str!("testcases/basic/reassign_self/main.rk.out"), include_str!("testcases/basic/reassign_self/main.rk.stdout")); } #[test] fn testcases_basic_operator_precedence_main() { run("testcases/basic/operator_precedence/main.rk", include_str!("testcases/basic/operator_precedence/main.rk"), include_str!("testcases/basic/operator_precedence/main.rk.out"), include_str!("testcases/basic/operator_precedence/main.rk.stdout")); } #[test] -fn testcases_basic_let_main() { - run("testcases/basic/let/main.rk", include_str!("testcases/basic/let/main.rk"), include_str!("testcases/basic/let/main.rk.out"), include_str!("testcases/basic/let/main.rk.stdout")); +fn testcases_basic_main_main() { + run("testcases/basic/main/main.rk", include_str!("testcases/basic/main/main.rk"), include_str!("testcases/basic/main/main.rk.out"), include_str!("testcases/basic/main/main.rk.stdout")); } #[test] -fn testcases_basic_fn_arg_array_main() { - run("testcases/basic/fn_arg_array/main.rk", include_str!("testcases/basic/fn_arg_array/main.rk"), include_str!("testcases/basic/fn_arg_array/main.rk.out"), include_str!("testcases/basic/fn_arg_array/main.rk.stdout")); +fn testcases_basic_bool_true_main() { + run("testcases/basic/bool_true/main.rk", include_str!("testcases/basic/bool_true/main.rk"), include_str!("testcases/basic/bool_true/main.rk.out"), include_str!("testcases/basic/bool_true/main.rk.stdout")); } #[test] -fn testcases_basic_0_arg_fn_main() { - run("testcases/basic/0_arg_fn/main.rk", include_str!("testcases/basic/0_arg_fn/main.rk"), include_str!("testcases/basic/0_arg_fn/main.rk.out"), include_str!("testcases/basic/0_arg_fn/main.rk.stdout")); +fn testcases_basic_array_main() { + run("testcases/basic/array/main.rk", include_str!("testcases/basic/array/main.rk"), include_str!("testcases/basic/array/main.rk.out"), include_str!("testcases/basic/array/main.rk.stdout")); } #[test] -fn testcases_basic_trait_monomorph_main() { - run("testcases/basic/trait_monomorph/main.rk", include_str!("testcases/basic/trait_monomorph/main.rk"), include_str!("testcases/basic/trait_monomorph/main.rk.out"), include_str!("testcases/basic/trait_monomorph/main.rk.stdout")); +fn testcases_basic_2_arg_fn_main() { + run("testcases/basic/2_arg_fn/main.rk", include_str!("testcases/basic/2_arg_fn/main.rk"), include_str!("testcases/basic/2_arg_fn/main.rk.out"), include_str!("testcases/basic/2_arg_fn/main.rk.stdout")); +} +#[test] +fn testcases_basic_dot_assign_main() { + run("testcases/basic/dot_assign/main.rk", include_str!("testcases/basic/dot_assign/main.rk"), include_str!("testcases/basic/dot_assign/main.rk.out"), include_str!("testcases/basic/dot_assign/main.rk.stdout")); +} +#[test] +fn testcases_basic_recur_main() { + run("testcases/basic/recur/main.rk", include_str!("testcases/basic/recur/main.rk"), include_str!("testcases/basic/recur/main.rk.out"), include_str!("testcases/basic/recur/main.rk.stdout")); +} +#[test] +fn testcases_trait_multi_resolution_main() { + run("testcases/trait/multi_resolution/main.rk", include_str!("testcases/trait/multi_resolution/main.rk"), include_str!("testcases/trait/multi_resolution/main.rk.out"), include_str!("testcases/trait/multi_resolution/main.rk.stdout")); +} +#[test] +fn testcases_trait_late_resolution_main() { + run("testcases/trait/late_resolution/main.rk", include_str!("testcases/trait/late_resolution/main.rk"), include_str!("testcases/trait/late_resolution/main.rk.out"), include_str!("testcases/trait/late_resolution/main.rk.stdout")); } diff --git a/src/lib/ty/func_type.rs b/src/lib/ty/func_type.rs index d4261178..a4ff9db1 100644 --- a/src/lib/ty/func_type.rs +++ b/src/lib/ty/func_type.rs @@ -123,7 +123,7 @@ impl FuncType { .enumerate() .filter_map(|(i, arg_t)| -> Option<(Type, Type)> { if !arg_t.is_forall() { - warn!("Trying to apply type to a not forall"); + warn!("Trying to apply type to a not forall: {:#?}", arg_t); return None; } @@ -290,7 +290,7 @@ mod tests { fn apply_forall_types() { let sig = FuncType::from_args_nb(2); - let res = sig.apply_forall_types(&vec![Type::forall("b")], &vec![Type::int64()]); + let res = sig.apply_forall_types(&[Type::forall("b")], &[Type::int64()]); assert_eq!(res.arguments[0], Type::forall("a")); assert_eq!(res.arguments[1], Type::int64()); @@ -312,10 +312,20 @@ mod tests { fn apply_partial_types() { let sig = FuncType::from_args_nb(2); - let res = sig.apply_partial_types(&vec![None, Some(Type::int64())], Some(Type::int64())); + let res = sig.apply_partial_types(&[None, Some(Type::int64())], Some(Type::int64())); assert_eq!(res.arguments[0], Type::forall("a")); assert_eq!(res.arguments[1], Type::int64()); assert_eq!(*res.ret, Type::int64()); } + + #[test] + fn apply_to_same_forall() { + let sig = FuncType::new(vec![Type::forall("a")], Type::forall("a")); + + let res = sig.apply_forall_types(&[Type::forall("a")], &[Type::int64()]); + + assert_eq!(res.arguments[0], Type::int64()); + assert_eq!(*res.ret, Type::int64()); + } } diff --git a/src/lib/ty/primitive_type.rs b/src/lib/ty/primitive_type.rs index 9ee7fd51..c09bef96 100644 --- a/src/lib/ty/primitive_type.rs +++ b/src/lib/ty/primitive_type.rs @@ -81,4 +81,12 @@ impl PrimitiveType { pub fn is_array(&self) -> bool { matches!(self, PrimitiveType::Array(_, _)) } + + pub fn try_as_array(&self) -> Option<(Type, usize)> { + if let PrimitiveType::Array(t, s) = self { + Some((*t.clone(), *s)) + } else { + None + } + } } diff --git a/src/lib/ty/struct_type.rs b/src/lib/ty/struct_type.rs index 0bb156c8..2e7ebdcf 100644 --- a/src/lib/ty/struct_type.rs +++ b/src/lib/ty/struct_type.rs @@ -29,7 +29,47 @@ impl fmt::Debug for StructType { } } -impl From<&ast::StructDecl> for StructType { +impl From<&ast::tree::StructDecl> for StructType { + fn from(s: &ast::tree::StructDecl) -> Self { + s.into() + } +} + +impl From for StructType { + fn from(s: ast::tree::StructDecl) -> Self { + StructType { + name: s.name.to_string(), + defs: s + .defs + .iter() + .map(|proto| { + if proto.signature.arguments.is_empty() { + (proto.name.name.clone(), proto.signature.ret.clone()) + } else { + ( + proto.name.name.clone(), + Box::new(proto.signature.clone().into()), + ) + } + }) + .collect(), + } + } +} + +/* impl From<&ast::tree::StructCtor> for StructType { + fn from(s: &ast::tree::StructCtor) -> Self { + s.into() + } +} + +impl From for StructType { + fn from(s: ast::tree::StructCtor) -> Self { + s.ty.clone() + } +} + */ +/* impl From<&ast::StructDecl> for StructType { fn from(s: &ast::StructDecl) -> Self { s.into() } @@ -38,7 +78,7 @@ impl From<&ast::StructDecl> for StructType { impl From for StructType { fn from(s: ast::StructDecl) -> Self { StructType { - name: s.name.get_name(), + name: s.name.to_string(), defs: s .defs .iter() @@ -55,12 +95,12 @@ impl From for StructType { .collect(), } } -} +} */ impl From for StructType { fn from(s: hir::StructDecl) -> Self { StructType { - name: s.name.get_name(), + name: s.name.name, defs: s .defs .iter() diff --git a/src/lib/ty/type.rs b/src/lib/ty/type.rs index 6493264b..301065ed 100644 --- a/src/lib/ty/type.rs +++ b/src/lib/ty/type.rs @@ -152,6 +152,9 @@ impl fmt::Debug for Type { let s = match self { Self::Func(f) => format!("{:?}", f), Self::Struct(s) => format!("{:?}", s), + Self::Trait(t) => format!("Trait {:?}", t), + Self::ForAll(t) => format!("forall. {:?}", t), + Self::Undefined(t) => format!("UNDEFINED {:?}", t), _ => self.get_name().cyan().to_string(), }; @@ -217,6 +220,15 @@ impl From for Type { fn from(t: String) -> Self { if t.len() == 1 && (t.chars().next().unwrap()).is_lowercase() { Type::ForAll(t) + } else if t.chars().next().unwrap() == '[' { + Type::Primitive(PrimitiveType::Array( + Box::new(Type::from( + t.trim_matches('[').trim_matches(']').to_string(), + )), + 0, // FIXME + )) + } else if PrimitiveType::from_name(&t).is_some() { + Type::Primitive(PrimitiveType::from_name(&t).unwrap()) } else { Type::Trait(t) } diff --git a/std/src/clone.rk b/std/src/clone.rk new file mode 100644 index 00000000..6f6ff74a --- /dev/null +++ b/std/src/clone.rk @@ -0,0 +1,21 @@ +use super::externs::(*) + +trait Clone a + clone :: a -> a + +impl Clone Int64 + clone a = a + +impl Clone Float64 + clone a = a + +impl Clone String + clone a = + let s = c_malloc strlen a + c_strcpy s, a + s + +use super::functor::(*) + +impl Clone [Int64] + clone a = map clone, a diff --git a/std/src/externs.rk b/std/src/externs.rk new file mode 100644 index 00000000..08832bbe --- /dev/null +++ b/std/src/externs.rk @@ -0,0 +1,37 @@ +extern malloc :: Int64 -> String +c_malloc i = malloc i + +extern strlen :: String -> Int64 +c_strlen s = strlen s + +extern strcat :: String -> String -> String +c_strcat dest src = strcat dest, src + +extern strcpy :: String -> String -> String +c_strcpy dest src = strcpy dest, src + +extern sprintf :: String -> String -> Int64 -> Int64 +c_sprintf dest format i = sprintf dest, format, i + +extern gcvt :: Float64 -> Int64 -> String -> String -> String +c_gcvt float digits unused1 unused2 = gcvt float, digits, unused1, unused2 + +extern puts :: String -> Int64 +c_puts s = puts s + +extern memcpy :: String -> String -> Int64 -> String +c_memcpy dest src size = memcpy dest, src, size + +# FS + +extern open :: String -> Int64 -> Int64 -> Int64 +c_open path flags mode = open path, flags, mode + +extern close :: Int64 -> Int64 +c_close fd = close fd + +extern strtol :: String -> Int64 -> Int64 -> Int64 +c_strtol octal_str unused base = strtol octal_str, unused, base + +extern read :: Int64 -> String -> Int64 -> Int64 +c_read fd buf len = read fd, buf, len diff --git a/std/src/fs.rk b/std/src/fs.rk new file mode 100644 index 00000000..5a143301 --- /dev/null +++ b/std/src/fs.rk @@ -0,0 +1,35 @@ +use super::externs::(*) + +use super::print::print +use super::show::show +use super::num::(*) +use super::eq::(*) + +struct File + fd :: Int64 + path :: String + +impl Show File + show f = + "File { fd: " + show f.fd + ", path: " + f.path + " }" + +oct_to_dec oct = c_strtol oct, 0, 8 + +new_file p = + let f = c_open p, 0, oct_to_dec "0777" + File + fd: f + path: p + +read_file f = + let s = c_malloc 100 + let len = c_read f.fd, s, 100 + s + +close_file f = + c_close f.fd + +_shut_up_warnings = + read_file new_file "" + close_file 0 + _shut_up_warnings() diff --git a/std/src/functor.rk b/std/src/functor.rk new file mode 100644 index 00000000..bdd97f93 --- /dev/null +++ b/std/src/functor.rk @@ -0,0 +1,22 @@ +use super::num::(*) +use super::eq::(*) + +map f arr = + let i = 0 + let arr2 = arr + while i < (~Len arr arr) + arr2[i] = f arr[i] + i = i + 1 + arr2 + +foreach f arr = + let i = 0 + while i < (~Len arr arr) + f arr[i] + i = i + 1 + arr + +_shut_up_warnings = + map 0, 0 + foreach 0, 0 + _shut_up_warnings() diff --git a/std/src/lib.rk b/std/src/lib.rk index 301f9c36..5d4d3acf 100644 --- a/std/src/lib.rk +++ b/std/src/lib.rk @@ -1,7 +1,14 @@ +mod externs +use externs::(*) + mod num mod eq mod show mod print +mod fs +mod functor +mod clone +mod vec # mod helpers mod prelude diff --git a/std/src/num.rk b/std/src/num.rk index 56e3a12c..f9f84ffc 100644 --- a/std/src/num.rk +++ b/std/src/num.rk @@ -20,3 +20,12 @@ impl Num Float64 - c d = ~FSub c d * c d = ~FMul c d / c d = ~FDiv c d + +use super::externs::(*) + +impl Num String + + a b = + let s = c_malloc (c_strlen a + c_strlen b) + c_strcpy s, a + c_strcat s, b + s diff --git a/std/src/prelude.rk b/std/src/prelude.rk index e773bd1e..9135268e 100644 --- a/std/src/prelude.rk +++ b/std/src/prelude.rk @@ -1,5 +1,6 @@ use super::show::show use super::print::print -use super::num::* -use super::eq::* -# use super::helpers::* +use super::num::(*) +use super::eq::(*) +use super::functor::(*) +use super::clone::(*) diff --git a/std/src/print.rk b/std/src/print.rk index 149d5b55..785eac65 100644 --- a/std/src/print.rk +++ b/std/src/print.rk @@ -1,5 +1,8 @@ -extern puts :: String -> Int64 - +use super::externs::(*) use super::show::show -print a = puts show a +print a = c_puts show a + +_shut_up_warnings = + print 0 + _shut_up_warnings() diff --git a/std/src/show.rk b/std/src/show.rk index 3e3425d8..740fe3ee 100644 --- a/std/src/show.rk +++ b/std/src/show.rk @@ -1,16 +1,16 @@ -extern malloc :: Int64 -> String -extern strlen :: String -> Int64 -extern strcpy :: String -> String -> Int64 -extern sprintf :: String -> String -> Int64 -> Int64 -extern gcvt :: Float64 -> Int64 -> String -> String -> String +use super::externs::(*) +use super::num::(*) +use super::eq::(*) itoa a = - let s = malloc 10 - sprintf s, "%d", a + let s = c_malloc 10 + + c_sprintf s, "%d", a + s ftoa c = - gcvt c, 10, (malloc 4), (malloc 4) + c_gcvt c, 10, (c_malloc 4), (c_malloc 4) trait Show a show :: a -> String @@ -29,6 +29,40 @@ impl Show Bool impl Show String show c = - let s = malloc strlen c - strcpy s, c + let s = c_malloc c_strlen c + + c_strcpy s, c + s + + +show_arr a = + let s = c_malloc 100 + + c_strcpy s, "[" + + let i = 0 + + let len = ~Len a a + + while i < len + c_strcat s, show a[i] + c_strcat s, ", " + + i = i + 1 + + c_strcat s, "]" + + s + +impl Show [Int64] + show a = show_arr a + +impl Show [Float64] + show a = show_arr a + +impl Show [String] + show a = show_arr a + +impl Show [Bool] + show a = show_arr a diff --git a/std/src/vec.rk b/std/src/vec.rk new file mode 100644 index 00000000..4157e4a9 --- /dev/null +++ b/std/src/vec.rk @@ -0,0 +1,48 @@ +struct Vec + data :: String + cap :: Int64 + len :: Int64 + +use super::show::show +use super::externs::(*) + +impl Show Vec + show v = show v.len + +new_vec = + Vec + data: c_malloc 4 + cap: 1 + len: 0 + + +use super::num::(*) +use super::eq::(*) + +realloc_vec v = + let orig_data = v.data + + v.cap = v.cap * 2 + + v.data = c_malloc (v.cap * 4) + + c_memcpy v.data, orig_data + + 0 + +push_vec v item = + let d = v.data + d[v.len] = item + + v.len = v.len + 1 + + if v.len == v.cap + then realloc_vec v + else 0 + 0 + +_shut_up_warnings = + new_vec() + push_vec 0, 0 + realloc_vec 0 + _shut_up_warnings()