From 618b1124e776024d09fd7eab5ca15a7a366ff3c3 Mon Sep 17 00:00:00 2001 From: Tyler Swann <41113853+oraqlle@users.noreply.github.com> Date: Sat, 4 Jun 2022 22:24:14 +1000 Subject: [PATCH] Updated README with more concise instructions (#82) * Fixed up some of the README Added Rust nightly install instructions for source building. Cleared up some of the showcases so they were more compact (the expected output, not the code bits) Fixed the index according to Common Markdown Linting --- .github/templates/Cargo.toml | 1 + .github/templates/README.md | 170 ++++++++++++---- .github/version | 2 +- Cargo.lock | 12 +- Cargo.toml | 3 +- README.md | 184 +++++++++++++++--- src/lib/ast/tree.rs | 56 ++++++ src/lib/ast_lowering/ast_lowering_context.rs | 82 +++++--- src/lib/codegen/codegen_context.rs | 103 ++++++---- src/lib/hir/arena.rs | 2 +- src/lib/hir/has_hir_id.rs | 2 +- src/lib/hir/hir_node.rs | 5 +- src/lib/hir/hir_printer.rs | 2 +- src/lib/hir/tree.rs | 43 ++-- src/lib/hir/visit.rs | 25 +-- src/lib/hir/visit_mut.rs | 25 +-- src/lib/infer/constraint.rs | 52 +++-- src/lib/infer/monomorphize/monomorphizer.rs | 11 -- src/lib/parser/mod.rs | 43 ++-- src/lib/parser/tests.rs | 12 ++ src/lib/resolver/resolve_ctx.rs | 28 +++ src/lib/testcases/basic/impl_self/main.rk | 12 ++ src/lib/testcases/basic/impl_self/main.rk.out | 1 + .../testcases/basic/impl_self/main.rk.stdout | 0 src/lib/testcases/basic/recur/main.rk | 2 +- src/lib/testcases/basic/struct_impl/main.rk | 14 ++ .../testcases/basic/struct_impl/main.rk.out | 1 + .../basic/struct_impl/main.rk.stdout | 0 src/lib/tests.rs | 8 + std/src/externs.rk | 4 +- 30 files changed, 674 insertions(+), 231 deletions(-) create mode 100644 src/lib/testcases/basic/impl_self/main.rk create mode 100644 src/lib/testcases/basic/impl_self/main.rk.out create mode 100644 src/lib/testcases/basic/impl_self/main.rk.stdout create mode 100644 src/lib/testcases/basic/struct_impl/main.rk create mode 100644 src/lib/testcases/basic/struct_impl/main.rk.out create mode 100644 src/lib/testcases/basic/struct_impl/main.rk.stdout diff --git a/.github/templates/Cargo.toml b/.github/templates/Cargo.toml index 463911b3..7fc175c6 100644 --- a/.github/templates/Cargo.toml +++ b/.github/templates/Cargo.toml @@ -22,6 +22,7 @@ paste = "1.0.5" rustyline = "9.0.0" nom = "7.0.0" nom_locate = "4.0.0" +itertools = "0.10.3" [build-dependencies] walkdir = "2" diff --git a/.github/templates/README.md b/.github/templates/README.md index 44c77e48..36ed6cb9 100644 --- a/.github/templates/README.md +++ b/.github/templates/README.md @@ -9,19 +9,29 @@ Rock is highly inspired from Livescript and Rust, and will also borrow (pun inte No to be taken seriously (yet) -# Index -- [Features]( #features ) -- [Install]( #install ) - - [Using released binary]( #using-released-binary ) - - [With cargo from Git]( #with-cargo-from-git ) - - [From sources]( #from-sources ) -- [Quickstart]( #quickstart ) -- [Showcases]( #showcases ) - - [Polymorphic function]( #polymorphic-function ) - - [Custom infix operator]( #custom-infix-operator ) - - [Trait definition]( #trait-definition ) -- [REPL]( #repl ) -- [Development notes]( #development-notes ) +## Index + +- [Rock {version}](#rock-{version}) + - [Index](#index) + - [Features](#features) + - [Install](#install) + - [Using Released Binary](#using-released-binary) + - [From source](#from-source) + - [Adding Rust Nightly](#adding-rust-nightly) + - [With Cargo from Git](#with-cargo-from-git) + - [Manual Clone and Build from Git](#manual-clone-and-build-from-git) + - [Quickstart](#quickstart) + - [Showcases](#showcases) + - [Polymorphic function](#polymorphic-function) + - [Custom infix operator](#custom-infix-operator) + - [Trait Definition](#trait-definition) + - [Struct instance and methods]( #struct-instance-and-methods ) + - [Show implementation]( #show-implementation ) + - [Modules and Code Separation](#modules-and-code-separation) + - [REPL](#repl) + - [Development notes](#development-notes) + +--- ## Features @@ -33,13 +43,15 @@ No to be taken seriously (yet) - Compile to LLVM IR - REPL (ALPHA) +--- + ## Install Warning: This project has only been tested on Linux x86_64. How to install and run the compiler: -### Using released binary +### Using Released Binary You will need `clang` somewhere in your $PATH @@ -57,21 +69,81 @@ chmod +x rock You will need `llvm-12.0.1` and `clang-12.0.1` somewhere in your $PATH -#### With cargo from git +You will also want the nightly channel added for Rust. + +#### Adding Rust Nightly + +To check if you already have the nightly channel added for Rust, use: + +```sh +rustup show +``` + +This will give you infomation about which build channels you have for rust and which one is active. If you have the nightly, you should see something like this: + +```sh +Default host: x86_64-unknown-linux-gnu +rustup home: /home//.rustup + +installed toolchains +-------------------- + +stable-x86_64-unknown-linux-gnu (default) +nightly-x86_64-unknown-linux-gnu + +active toolchain +---------------- + +stable-x86_64-unknown-linux-gnu (default) +rustc 1.61.0 (fe5b13d68 2022-05-18) +``` + +If you don't see the nightly build you can add it using the following command: + +```sh +rustup install nightly +``` + +This will add the option to use the nightly build of Rust for this and any other projects. +Note you don't have to switch to the nightly to be the active toolchain but can use it specific projects, see below. + +#### With Cargo from Git + +If your active toolchain is stable: + +```sh +cargo +nightly install --git https://github.com/Champii/Rock --locked +rock -V +``` + +If your active rust toolchain is nightly: ``` sh cargo install --git https://github.com/Champii/Rock --locked rock -V ``` -#### Manual clone and build from git +#### Manual Clone and Build from Git + +If your active toolchain is stable: + +```sh +git clone https://github.com/Champii/Rock.git rock +cd rock +cargo +nightly run -- -- -V +``` + +If your active toolchain is nightly: +You can pick the release or debug build ``` sh -git clone https://github.com/Champii/Rock.git -cd Rock -cargo run -- -V +git clone https://github.com/Champii/Rock.git rock +cd rock +cargo run -- -- -V ``` +Note: If you clone and build manually, make sure to add `path-to-install/rock/target//` to you `$PATH` so you can run it anywhere on your system. + ## Quickstart - Lets create a new project folder to compute some factorials @@ -94,12 +166,7 @@ main = print fact 4 Assuming that you built Rock and put its binary in your PATH: ``` sh -rock run -``` - -Should output - -``` sh +$ rock run 24 ``` @@ -111,7 +178,6 @@ Note that you currently must be at the project root to run the compiler. (i.e. i ### Polymorphic function - ``` haskell id a = a @@ -121,13 +187,10 @@ main = print id "Test" ``` -``` sh -rock run -``` - Prints ``` sh +$ rock run 1 2.2 Test @@ -139,7 +202,8 @@ If we did something like this We would have constrained `a` to types that implement [`Num`](https://github.com/Champii/Rock/blob/master/std/src/num.rk) Note that this example would still be valid, as `Int64`, `Float64` and `String` are all implementors of `Num`(*). -The output would be + +The output would be: ``` sh 2 @@ -161,11 +225,10 @@ main = print (4 |> f) ``` ``` sh -rock run +$ rock run +6 ``` -Prints `6` - You can create any operator that is made of any combination of one or more of `'+', '-', '/', '*', '|', '<', '>', '=', '!', '$', '@', '&'` Most of the commonly defined operators like `+`, `<=`, etc are already implemented by the [stdlib](https://github.com/Champii/Rock/tree/master/std) that is automaticaly compiled with every package. @@ -192,17 +255,36 @@ main = ``` ``` sh -rock run +$ rock run +33 +42.42 ``` -Prints +### Struct instance and methods +``` haskell +struct Player + level :: Int64 + name :: String + +impl Player + new level = + Player + level: level + name: "Default" + getlevel player = player.level + +main = + let player = Player::new 1 + print Player::getlevel player ``` -33 -42.42 + +``` sh +$ rock run +1 ``` -### Struct instance and Show implementation +### Show implementation ``` haskell struct Player @@ -222,11 +304,10 @@ main = ``` ``` sh -rock run +$ rock run +MyName ``` -Prints `MyName` - ### Modules and code separation - `./myproj/src/foo.rk` @@ -245,7 +326,10 @@ use foo::bar main = print bar 1 ``` -Prints `2` +```sh +$ rock run +2 +``` Note that we could have skiped the `use foo::bar` diff --git a/.github/version b/.github/version index f0cfd3bb..576b7771 100644 --- a/.github/version +++ b/.github/version @@ -1 +1 @@ -v0.2.2 +v0.2.3 diff --git a/Cargo.lock b/Cargo.lock index ae05cff0..cdd18c79 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -238,6 +238,15 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "itertools" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +dependencies = [ + "either", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -464,7 +473,7 @@ checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" [[package]] name = "rock" -version = "0.2.2" +version = "0.2.3-develop" dependencies = [ "bincode", "bitflags", @@ -473,6 +482,7 @@ dependencies = [ "either", "env_logger", "inkwell", + "itertools", "lazy_static", "log", "nom", diff --git a/Cargo.toml b/Cargo.toml index c32f74ea..2394cdaf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rock" -version = "0.2.2" +version = "0.2.3-develop" authors = ["champii "] edition = "2018" @@ -22,6 +22,7 @@ paste = "1.0.5" rustyline = "9.0.0" nom = "7.0.0" nom_locate = "4.0.0" +itertools = "0.10.3" [build-dependencies] walkdir = "2" diff --git a/README.md b/README.md index eda59ece..3defab9e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,8 @@ +<<<<<<< HEAD # Rock v0.2.2 +======= +# Rock v0.2.3-develop +>>>>>>> 18e4cf37e8bbce6723f212962ac1dbfc0c355a2f [![Rust](https://github.com/Champii/Rock/actions/workflows/rust.yml/badge.svg?branch=-)](https://github.com/Champii/Rock/actions/workflows/rust.yml) @@ -9,6 +13,32 @@ Rock is highly inspired from Livescript and Rust, and will also borrow (pun inte No to be taken seriously (yet) +<<<<<<< HEAD +--- + +## Index + +- [Rock v0.2.2](#rock-v022) + - [Index](#index) + - [Features](#features) + - [Install](#install) + - [Using Released Binary](#using-released-binary) + - [From source](#from-source) + - [Adding Rust Nightly](#adding-rust-nightly) + - [With Cargo from Git](#with-cargo-from-git) + - [Manual Clone and Build from Git](#manual-clone-and-build-from-git) + - [Quickstart](#quickstart) + - [Showcases](#showcases) + - [Polymorphic function](#polymorphic-function) + - [Custom infix operator](#custom-infix-operator) + - [Trait Definition](#trait-definition) + - [Struct instance and Show implementation](#struct-instance-and-show-implementation) + - [Modules and Code Separation](#modules-and-code-separation) + - [REPL](#repl) + - [Development notes](#development-notes) + +--- +======= # Index - [Features]( #features ) - [Install]( #install ) @@ -20,8 +50,12 @@ No to be taken seriously (yet) - [Polymorphic function]( #polymorphic-function ) - [Custom infix operator]( #custom-infix-operator ) - [Trait definition]( #trait-definition ) + - [Struct instance and methods]( #struct-instance-and-methods ) + - [Show implementation]( #show-implementation ) + - [Modules and code separation]( #modules-and-code-separation ) - [REPL]( #repl ) - [Development notes]( #development-notes ) +>>>>>>> 18e4cf37e8bbce6723f212962ac1dbfc0c355a2f ## Features @@ -33,22 +67,31 @@ No to be taken seriously (yet) - Compile to LLVM IR - REPL (ALPHA) +--- + ## Install Warning: This project has only been tested on Linux x86_64. How to install and run the compiler: -### Using released binary +### Using Released Binary You will need `clang` somewhere in your $PATH Linux x86_64 only +<<<<<<< HEAD [Rock v0.2.2](https://github.com/Champii/Rock/releases/download/v0.2.2/rock) (Tested on arch, btw) ``` sh wget https://github.com/Champii/Rock/releases/download/v0.2.2/rock +======= +[Rock v0.2.3-develop](https://github.com/Champii/Rock/releases/download/v0.2.3-develop/rock) (Tested on arch, btw) + +``` sh +wget https://github.com/Champii/Rock/releases/download/v0.2.3-develop/rock +>>>>>>> 18e4cf37e8bbce6723f212962ac1dbfc0c355a2f chmod +x rock ./rock -V ``` @@ -57,21 +100,81 @@ chmod +x rock You will need `llvm-12.0.1` and `clang-12.0.1` somewhere in your $PATH -#### With cargo from git +You will also want the nightly channel added for Rust. + +#### Adding Rust Nightly + +To check if you already have the nightly channel added for Rust, use: + +```sh +rustup show +``` + +This will give you infomation about which build channels you have for rust and which one is active. If you have the nightly, you should see something like this: + +```sh +Default host: x86_64-unknown-linux-gnu +rustup home: /home//.rustup + +installed toolchains +-------------------- + +stable-x86_64-unknown-linux-gnu (default) +nightly-x86_64-unknown-linux-gnu + +active toolchain +---------------- + +stable-x86_64-unknown-linux-gnu (default) +rustc 1.61.0 (fe5b13d68 2022-05-18) +``` + +If you don't see the nightly build you can add it using the following command: + +```sh +rustup install nightly +``` + +This will add the option to use the nightly build of Rust for this and any other projects. +Note you don't have to switch to the nightly to be the active toolchain but can use it specific projects, see below. + +#### With Cargo from Git + +If your active toolchain is stable: + +```sh +cargo +nightly install --git https://github.com/Champii/Rock --locked +rock -V +``` + +If your active rust toolchain is nightly: ``` sh cargo install --git https://github.com/Champii/Rock --locked rock -V ``` -#### Manual clone and build from git +#### Manual Clone and Build from Git + +If your active toolchain is stable: + +```sh +git clone https://github.com/Champii/Rock.git rock +cd rock +cargo +nightly run -- -- -V +``` + +If your active toolchain is nightly: +You can pick the release or debug build ``` sh -git clone https://github.com/Champii/Rock.git -cd Rock -cargo run -- -V +git clone https://github.com/Champii/Rock.git rock +cd rock +cargo run -- -- -V ``` +Note: If you clone and build manually, make sure to add `path-to-install/rock/target//` to you `$PATH` so you can run it anywhere on your system. + ## Quickstart - Lets create a new project folder to compute some factorials @@ -94,12 +197,7 @@ main = print fact 4 Assuming that you built Rock and put its binary in your PATH: ``` sh -rock run -``` - -Should output - -``` sh +$ rock run 24 ``` @@ -111,7 +209,6 @@ Note that you currently must be at the project root to run the compiler. (i.e. i ### Polymorphic function - ``` haskell id a = a @@ -122,12 +219,7 @@ main = ``` ``` sh -rock run -``` - -Prints - -``` sh +$ rock run 1 2.2 Test @@ -139,7 +231,8 @@ If we did something like this We would have constrained `a` to types that implement [`Num`](https://github.com/Champii/Rock/blob/master/std/src/num.rk) Note that this example would still be valid, as `Int64`, `Float64` and `String` are all implementors of `Num`(*). -The output would be + +The output would be: ``` sh 2 @@ -161,17 +254,16 @@ main = print (4 |> f) ``` ``` sh -rock run +$ rock run +6 ``` -Prints `6` - You can create any operator that is made of any combination of one or more of `'+', '-', '/', '*', '|', '<', '>', '=', '!', '$', '@', '&'` Most of the commonly defined operators like `+`, `<=`, etc are already implemented by the [stdlib](https://github.com/Champii/Rock/tree/master/std) that is automaticaly compiled with every package. -There is a `--nostd` option to allow you to use your own custom implementation. +There is a `--nostd` option to allow you to use your own custom implementation. -### Trait definition +### Trait Definition This `trait ToString` is redondant with the `trait Show` implemented in the stdlib, and serves as a demonstration only @@ -195,20 +287,46 @@ main = rock run ``` -Prints +Prints: -``` +```sh 33 42.42 ``` -### Struct instance and Show implementation +### Struct instance and methods ``` haskell struct Player level :: Int64 name :: String +impl Player + new level = + Player + level: level + name: "Default" + getlevel player = player.level + +main = + let player = Player::new 1 + print Player::getlevel player +``` + +``` sh +rock run +``` + +Prints `1` + + +### Show implementation + +```haskell +struct Player + level :: Int64 + name :: String + impl Show Player show p = show p.name @@ -227,7 +345,7 @@ rock run Prints `MyName` -### Modules and code separation +### Modules and Code Separation - `./myproj/src/foo.rk` @@ -250,7 +368,7 @@ Prints `2` Note that we could have skiped the `use foo::bar` if we wrote -`main = print foo::bar 1` +`main = print foo::bar 1` ## REPL @@ -263,7 +381,7 @@ 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 +You can start a REPL session with ``` sh rock -r @@ -272,7 +390,11 @@ rock --repl ``` ``` sh +<<<<<<< HEAD Rock: v0.2.2 +======= +Rock: v0.2.3-develop +>>>>>>> 18e4cf37e8bbce6723f212962ac1dbfc0c355a2f ---- Type ':?' for help diff --git a/src/lib/ast/tree.rs b/src/lib/ast/tree.rs index 1f53aef2..35e3c245 100644 --- a/src/lib/ast/tree.rs +++ b/src/lib/ast/tree.rs @@ -177,7 +177,27 @@ pub struct FunctionDecl { } impl FunctionDecl { + pub fn new_self( + node_id: NodeId, + self_node_id: NodeId, + name: Identifier, + body: Body, + mut arguments: Vec, + ) -> Self { + arguments.insert(0, Identifier::new("self".to_string(), self_node_id)); + Self { + name, + signature: FuncType::from_args_nb(arguments.len()), + arguments, + body, + node_id, + } + } pub fn mangle(&mut self, prefixes: &[String]) { + if prefixes.is_empty() { + return; + } + self.name.name = prefixes.join("_") + "_" + &self.name.name; } } @@ -422,6 +442,26 @@ impl If { else_, } } + + pub fn get_flat(&self) -> Vec<(NodeId, Expression, Body)> { + let mut res = vec![]; + + res.push((self.node_id, self.predicat.clone(), self.body.clone())); + + if let Some(else_) = &self.else_ { + res.extend(else_.get_flat()); + } + + res + } + + pub fn last_else(&self) -> Option<&Body> { + if let Some(else_) = &self.else_ { + else_.last_else() + } else { + None + } + } } #[derive(Debug, Clone)] @@ -430,6 +470,22 @@ pub enum Else { Body(Body), } +impl Else { + pub fn get_flat(&self) -> Vec<(NodeId, Expression, Body)> { + match self { + Else::If(if_) => if_.get_flat(), + Else::Body(_body) => vec![], + } + } + + pub fn last_else(&self) -> Option<&Body> { + match self { + Else::If(if_) => if_.last_else(), + Else::Body(body) => Some(body), + } + } +} + impl Expression { #[allow(dead_code)] pub fn is_literal(&self) -> bool { diff --git a/src/lib/ast_lowering/ast_lowering_context.rs b/src/lib/ast_lowering/ast_lowering_context.rs index a9ec5d61..518add85 100644 --- a/src/lib/ast_lowering/ast_lowering_context.rs +++ b/src/lib/ast_lowering/ast_lowering_context.rs @@ -16,6 +16,7 @@ pub struct AstLoweringContext { operators_list: HashMap, traits: HashMap, trait_methods: HashMap>, + struct_methods: HashMap>, structs: HashMap, } @@ -27,6 +28,7 @@ impl AstLoweringContext { bodies: BTreeMap::new(), traits: HashMap::new(), trait_methods: HashMap::new(), + struct_methods: HashMap::new(), structs: HashMap::new(), operators_list, } @@ -45,6 +47,7 @@ impl AstLoweringContext { bodies: self.bodies.clone(), traits: self.traits.clone(), trait_methods: self.trait_methods.clone(), + struct_methods: self.struct_methods.clone(), spans: root.spans.clone(), structs: self.structs.clone(), }; @@ -133,26 +136,35 @@ impl AstLoweringContext { let body = self.bodies.get_mut(&hir_f.body_id).unwrap(); body.mangle(&types); - let r#trait = self.traits.get(&i.name).unwrap(); - - let type_sig = r#trait - .defs - .iter() - .find(|proto| *proto.name == *hir_f.name) - .unwrap() - .signature - .clone(); - - let type_sig = type_sig.apply_forall_types(&r#trait.types, &i.types); + let type_sig = if let Some(r#trait) = self.traits.get(&i.name) { + let type_sig = r#trait + .defs + .iter() + .find(|proto| *proto.name == *hir_f.name) + .unwrap() + .signature + .clone(); + + let type_sig = type_sig.apply_forall_types(&r#trait.types, &i.types); + type_sig + } else { + f.signature.clone() + }; hir_f.signature = type_sig.clone(); - let fn_decls = self - .trait_methods - .entry(hir_f.name.name.clone()) - .or_insert_with(HashMap::new); + let fn_decls = if self.traits.get(&i.name).is_some() { + self.trait_methods + .entry(hir_f.name.name.clone()) + .or_insert_with(HashMap::new) + } else { + self.struct_methods + .entry(hir_f.name.name.clone()) + .or_insert_with(HashMap::new) + }; let _hir_id = self.hir_map.next_hir_id(f.node_id); + hir_f.hir_id = _hir_id; (*fn_decls).insert(type_sig, hir_f); } @@ -237,7 +249,7 @@ impl AstLoweringContext { Statement::Expression(e) => { Box::new(hir::StatementKind::Expression(self.lower_expression(e))) } - Statement::If(e) => Box::new(hir::StatementKind::If(self.lower_if(e))), + Statement::If(e) => Box::new(hir::StatementKind::If(self.lower_if_chain(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))), }, @@ -333,19 +345,22 @@ 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.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))), - } - } + pub fn lower_if_chain(&mut self, r#if: &If) -> hir::IfChain { + let flat_if = r#if.get_flat(); + + let flat_hir_if = flat_if.iter().map(|(node_id, predicat, body)| { + let body = self.lower_body(body); - pub fn lower_else(&mut self, r#else: &Else) -> hir::Else { - match r#else { - Else::If(e) => hir::Else::If(self.lower_if(e)), - Else::Body(b) => hir::Else::Body(self.lower_body(b)), + hir::If { + hir_id: self.hir_map.next_hir_id(*node_id), + predicat: self.lower_expression(predicat), + body, + } + }); + + hir::IfChain { + ifs: flat_hir_if.collect(), + else_body: r#if.last_else().map(|body| self.lower_body(body)), } } @@ -364,7 +379,16 @@ impl AstLoweringContext { let mut expr = self.lower_operand(&primary.op); for secondary in &primary.secondaries.clone().unwrap() { - expr = self.lower_secondary(expr, secondary, primary.node_id); + let mut expr2 = self.lower_secondary(expr.clone(), secondary, primary.node_id); + + // If the caller is a dot notation, we need to inject self as first argument + if let hir::ExpressionKind::Dot(dot) = &mut *expr.kind { + if let hir::ExpressionKind::FunctionCall(fc) = &mut *expr2.kind { + fc.args.insert(0, dot.op.clone()); + } + } + + expr = expr2; } expr diff --git a/src/lib/codegen/codegen_context.rs b/src/lib/codegen/codegen_context.rs index 4f669e54..d9bd3a93 100644 --- a/src/lib/codegen/codegen_context.rs +++ b/src/lib/codegen/codegen_context.rs @@ -1,5 +1,7 @@ use std::convert::{TryFrom, TryInto}; +use itertools::Itertools; + use inkwell::{ basic_block::BasicBlock, builder::Builder, @@ -347,7 +349,7 @@ impl<'a> CodegenContext<'a> { ) -> Result, ()> { Ok(match &*stmt.kind { StatementKind::Expression(e) => self.lower_expression(e, builder)?, - StatementKind::If(e) => self.lower_if(e, builder)?.0, + StatementKind::If(e) => self.lower_if_chain(e, builder)?.0, StatementKind::Assign(a) => self.lower_assign(a, builder)?, StatementKind::For(f) => self.lower_for(f, builder)?, }) @@ -473,66 +475,99 @@ impl<'a> CodegenContext<'a> { } }) } - - pub fn lower_if( + pub fn lower_if_chain( &mut self, - r#if: &'a If, + if_chain: &'a IfChain, builder: &'a Builder, ) -> Result<(BasicValueEnum<'a>, BasicBlock<'a>), ()> { let block = builder.get_insert_block().unwrap(); + let cur_f = block.get_parent().unwrap(); + + let mut value_blocks = Vec::new(); + + for if_ in &if_chain.ifs { + let (value, block) = self.lower_if(&if_, builder)?; + + value_blocks.push((value.clone(), block)); + } + + // else block + if let Some(else_body) = &if_chain.else_body { + let (value, block) = self.lower_body(&else_body, "else_body", builder)?; + + value_blocks.push((value.clone(), block)); + } + + let (first_value, first_block) = value_blocks.first().unwrap(); builder.position_at_end(block); + builder.build_unconditional_branch(first_block.get_previous_basic_block().unwrap()); - let predicat = self.lower_expression(&r#if.predicat, builder)?; + let exit_block = self.context.append_basic_block(cur_f, "if_exit"); - let (then_value, then_block) = self.lower_body(&r#if.body, "then", builder)?; + builder.position_at_end(exit_block); - let (else_value, else_block) = if let Some(e) = &r#if.else_ { - self.lower_else(e, builder)? + let phi = builder.build_phi(first_value.get_type(), "phi"); + + let ifs = if_chain.ifs.iter().map(Some); + let ifs = if if_chain.else_body.is_some() { + ifs.chain(vec![None].into_iter()) } else { - (then_value, then_block) + ifs.chain(vec![]) }; - let rest_block = self.context.insert_basic_block_after(else_block, "rest"); + for (((value_a, block_a), (value_b, block_b)), if_) in + value_blocks.iter().circular_tuple_windows().zip(ifs) + { + let if_block = block_a.get_previous_basic_block().unwrap(); + + builder.position_at_end(if_block); - builder.position_at_end(then_block); - builder.build_unconditional_branch(rest_block); + if let Some(if_) = if_ { + let predicat = self.lower_expression(&if_.predicat, builder)?; - if r#if.else_.is_some() { - builder.position_at_end(else_block); - builder.build_unconditional_branch(rest_block); - } + let if_block_b = if value_b == first_value { + block_a.clone() + } else { + if if_chain.else_body.is_some() && *value_b == value_blocks.last().unwrap().0 { + block_b.clone() + } else { + block_b.get_previous_basic_block().unwrap() + } + }; - builder.position_at_end(block); + builder.build_conditional_branch(predicat.into_int_value(), *block_a, if_block_b); + } else { + // else block is ignored + } - builder.build_conditional_branch(predicat.into_int_value(), then_block, else_block); + builder.position_at_end(*block_a); - builder.position_at_end(rest_block); - let phi = builder.build_phi(then_value.get_type(), "phi"); + builder.build_unconditional_branch(exit_block); - phi.add_incoming(&[(&then_value, then_block), (&else_value, else_block)]); + phi.add_incoming(&[(value_a, *block_a)]); + } - Ok((phi.as_basic_value(), block)) + builder.position_at_end(exit_block); + + Ok((phi.as_basic_value(), exit_block)) } - pub fn lower_else( + pub fn lower_if( &mut self, - r#else: &'a Else, + r#if: &'a If, builder: &'a Builder, ) -> Result<(BasicValueEnum<'a>, BasicBlock<'a>), ()> { - Ok(match &r#else { - Else::If(i) => { - let block = self - .context - .append_basic_block(self.cur_func.unwrap(), "if"); + let if_block = self + .context + .append_basic_block(self.cur_func.unwrap(), "if"); - builder.position_at_end(block); + builder.position_at_end(if_block); - self.lower_if(i, builder)? - } - Else::Body(b) => self.lower_body(b, "else", builder)?, - }) + let (then_value, then_block) = self.lower_body(&r#if.body, "then", builder)?; + + Ok((then_value, then_block)) } pub fn lower_expression( diff --git a/src/lib/hir/arena.rs b/src/lib/hir/arena.rs index ba21f72b..e8ac8a15 100644 --- a/src/lib/hir/arena.rs +++ b/src/lib/hir/arena.rs @@ -93,7 +93,7 @@ generate_hirnode_collector!( Statement, Expression, If, - Else, + IfChain, FunctionCall, Literal, NativeOperator, diff --git a/src/lib/hir/has_hir_id.rs b/src/lib/hir/has_hir_id.rs index e05e38bb..0d4d6f01 100644 --- a/src/lib/hir/has_hir_id.rs +++ b/src/lib/hir/has_hir_id.rs @@ -63,7 +63,7 @@ impl_indirect_get_hir_id_trait!( Body Expression Array - Else + IfChain ); impl HasHirId for Vec { diff --git a/src/lib/hir/hir_node.rs b/src/lib/hir/hir_node.rs index d779a12e..edfab5cb 100644 --- a/src/lib/hir/hir_node.rs +++ b/src/lib/hir/hir_node.rs @@ -17,7 +17,7 @@ pub enum HirNode { Statement(Statement), Expression(Expression), If(If), - Else(Else), + IfChain(IfChain), FunctionCall(FunctionCall), Literal(Literal), NativeOperator(NativeOperator), @@ -38,7 +38,6 @@ impl<'ar> HasHirId for HirNode { HirNode::Statement(x) => x.get_hir_id(), HirNode::Expression(x) => x.get_hir_id(), HirNode::If(x) => x.get_hir_id(), - HirNode::Else(x) => x.get_hir_id(), HirNode::FunctionCall(x) => x.get_hir_id(), HirNode::Literal(x) => x.get_hir_id(), HirNode::NativeOperator(x) => x.get_hir_id(), @@ -75,7 +74,7 @@ generate_hirnode_from!( Statement, Expression, If, - Else, + IfChain, FunctionCall, Literal, NativeOperator, diff --git a/src/lib/hir/hir_printer.rs b/src/lib/hir/hir_printer.rs index 2ae6abce..f9ac3223 100644 --- a/src/lib/hir/hir_printer.rs +++ b/src/lib/hir/hir_printer.rs @@ -112,7 +112,7 @@ impl_visitor_trait2!( Body Statement If - Else + IfChain FunctionCall StructCtor Indice diff --git a/src/lib/hir/tree.rs b/src/lib/hir/tree.rs index f76f1b3a..449a278e 100644 --- a/src/lib/hir/tree.rs +++ b/src/lib/hir/tree.rs @@ -21,6 +21,7 @@ pub struct Root { pub node_types: BTreeMap, pub traits: HashMap, pub trait_methods: HashMap>, + pub struct_methods: HashMap>, pub top_levels: Vec, pub bodies: BTreeMap, pub spans: HashMap, @@ -350,7 +351,7 @@ impl Statement { pub enum StatementKind { Expression(Expression), Assign(Assign), - If(If), + If(IfChain), For(For), } @@ -425,27 +426,22 @@ impl Assign { } #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct If { - pub hir_id: HirId, - pub predicat: Expression, - pub body: Body, - pub else_: Option>, +pub struct IfChain { + pub ifs: Vec, + pub else_body: Option, } -#[derive(Debug, Clone, Serialize, Deserialize)] -pub enum Else { - If(If), - Body(Body), -} - -impl Else { +impl IfChain { pub fn get_terminal_hir_id(&self) -> HirId { - match self { - Else::If(i) => i.get_hir_id(), - Else::Body(b) => b.get_hir_id(), - } + self.ifs.iter().last().unwrap().get_hir_id() } } +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct If { + pub hir_id: HirId, + pub predicat: Expression, + pub body: Body, +} #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Expression { @@ -510,6 +506,8 @@ impl Expression { pub fn as_identifier(&self) -> Identifier { if let ExpressionKind::Identifier(i) = &*self.kind { i.last_segment() + } else if let ExpressionKind::Dot(d) = &*self.kind { + d.value.clone() } else { panic!("Not an identifier"); } @@ -524,6 +522,14 @@ impl Expression { } } + pub fn as_dot(&self) -> Dot { + if let ExpressionKind::Dot(l) = &*self.kind { + l.clone() + } else { + panic!("Not a dot"); + } + } + pub fn is_return(&self) -> bool { if let ExpressionKind::Return(_) = &*self.kind { true @@ -567,6 +573,9 @@ pub struct FunctionCall { } impl FunctionCall { + pub fn new(hir_id: HirId, op: Expression, args: Vec) -> Self { + Self { hir_id, op, args } + } pub fn mangle(&mut self, prefixes: Vec) { if let ExpressionKind::Identifier(id) = &mut *self.op.kind { let identifier = id.path.iter_mut().last().unwrap(); diff --git a/src/lib/hir/visit.rs b/src/lib/hir/visit.rs index 313f3d1c..3d8f49d9 100644 --- a/src/lib/hir/visit.rs +++ b/src/lib/hir/visit.rs @@ -43,8 +43,8 @@ generate_visitor_trait!( ForIn While Expression + IfChain If - Else FunctionCall StructCtor Indice @@ -71,6 +71,10 @@ pub fn walk_root<'a, V: Visitor<'a>>(visitor: &mut V, root: &'a Root) { walk_map!(visitor, visit_function_decl, impls); } + for impls in root.struct_methods.values() { + walk_map!(visitor, visit_function_decl, impls); + } + walk_map!(visitor, visit_fn_body, &root.bodies); } @@ -153,7 +157,7 @@ pub fn walk_statement<'a, V: Visitor<'a>>(visitor: &mut V, statement: &'a Statem 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), + StatementKind::If(expr) => visitor.visit_if_chain(expr), StatementKind::For(for_loop) => visitor.visit_for(for_loop), } } @@ -233,20 +237,17 @@ pub fn walk_native_operator<'a, V: Visitor<'a>>(visitor: &mut V, operator: &'a N visitor.visit_primitive(operator.kind.clone()); } -pub fn walk_if<'a, V: Visitor<'a>>(visitor: &mut V, r#if: &'a If) { - visitor.visit_expression(&r#if.predicat); - visitor.visit_body(&r#if.body); +pub fn walk_if_chain<'a, V: Visitor<'a>>(visitor: &mut V, if_chain: &'a IfChain) { + walk_list!(visitor, visit_if, &if_chain.ifs); - if let Some(r#else) = &r#if.else_ { - visitor.visit_else(r#else); + if let Some(body) = &if_chain.else_body { + visitor.visit_body(body); } } -pub fn walk_else<'a, V: Visitor<'a>>(visitor: &mut V, r#else: &'a Else) { - match r#else { - Else::If(expr) => visitor.visit_if(expr), - Else::Body(expr) => visitor.visit_body(expr), - } +pub fn walk_if<'a, V: Visitor<'a>>(visitor: &mut V, r#if: &'a If) { + visitor.visit_expression(&r#if.predicat); + visitor.visit_body(&r#if.body); } pub fn walk_type<'a, V: Visitor<'a>>(_visitor: &mut V, _t: &'a Type) { diff --git a/src/lib/hir/visit_mut.rs b/src/lib/hir/visit_mut.rs index 6a7a4474..cae78a52 100644 --- a/src/lib/hir/visit_mut.rs +++ b/src/lib/hir/visit_mut.rs @@ -43,8 +43,8 @@ generate_visitor_mut_trait!( ForIn While Expression + IfChain If - Else FunctionCall StructCtor Indice @@ -71,6 +71,10 @@ pub fn walk_root<'a, V: VisitorMut<'a>>(visitor: &mut V, root: &'a mut Root) { walk_map!(visitor, visit_function_decl, impls); } + for (_, impls) in &mut root.struct_methods { + walk_map!(visitor, visit_function_decl, impls); + } + walk_map!(visitor, visit_fn_body, &mut root.bodies); } @@ -153,7 +157,7 @@ pub fn walk_statement<'a, V: VisitorMut<'a>>(visitor: &mut V, statement: &'a mut match &mut *statement.kind { StatementKind::Expression(expr) => visitor.visit_expression(expr), StatementKind::Assign(assign) => visitor.visit_assign(assign), - StatementKind::If(expr) => visitor.visit_if(expr), + StatementKind::If(expr) => visitor.visit_if_chain(expr), StatementKind::For(for_loop) => visitor.visit_for(for_loop), } } @@ -246,20 +250,17 @@ pub fn walk_native_operator<'a, V: VisitorMut<'a>>( // } -pub fn walk_if<'a, V: VisitorMut<'a>>(visitor: &mut V, r#if: &'a mut If) { - visitor.visit_expression(&mut r#if.predicat); - visitor.visit_body(&mut r#if.body); +pub fn walk_if_chain<'a, V: VisitorMut<'a>>(visitor: &mut V, if_chain: &'a mut IfChain) { + walk_list!(visitor, visit_if, &mut if_chain.ifs); - if let Some(r#else) = &mut r#if.else_ { - visitor.visit_else(r#else); + if let Some(body) = &mut if_chain.else_body { + visitor.visit_body(body); } } -pub fn walk_else<'a, V: VisitorMut<'a>>(visitor: &mut V, r#else: &'a mut Else) { - match r#else { - Else::If(expr) => visitor.visit_if(expr), - Else::Body(expr) => visitor.visit_body(expr), - } +pub fn walk_if<'a, V: VisitorMut<'a>>(visitor: &mut V, r#if: &'a mut If) { + visitor.visit_expression(&mut r#if.predicat); + visitor.visit_body(&mut r#if.body); } pub fn walk_type<'a, V: VisitorMut<'a>>(_visitor: &mut V, _t: &'a mut Type) { diff --git a/src/lib/infer/constraint.rs b/src/lib/infer/constraint.rs index 3c4da289..0b2eb28f 100644 --- a/src/lib/infer/constraint.rs +++ b/src/lib/infer/constraint.rs @@ -427,25 +427,26 @@ impl<'a, 'ar> Visitor<'a> for ConstraintContext<'ar> { .set_type_eq(&assign.name.get_hir_id(), &assign.value.get_hir_id()); } + fn visit_if_chain(&mut self, if_chain: &'a IfChain) { + walk_if_chain(self, if_chain); + + let else_hir_id = if_chain + .else_body + .as_ref() + .map(|b| b.get_hir_id()) + .unwrap_or(HirId(0)); + + if_chain.ifs.iter().for_each(|if_| { + self.envs.set_type_eq(&else_hir_id, &if_.get_hir_id()); + }); + } + fn visit_if(&mut self, r#if: &'a If) { self.visit_expression(&r#if.predicat); self.visit_body(&r#if.body); self.envs.set_type_eq(&r#if.hir_id, &r#if.body.get_hir_id()); - - if let Some(e) = &r#if.else_ { - match &**e { - Else::Body(b) => { - self.visit_body(b); - self.envs - .set_type_eq(&b.get_hir_id(), &r#if.body.get_hir_id()); - } - Else::If(i) => { - self.visit_if(i); - } - } - } } fn visit_expression(&mut self, expr: &'a Expression) { @@ -570,13 +571,28 @@ impl<'a, 'ar> Visitor<'a> for ConstraintContext<'ar> { t @ Type::Struct(struct_t) => { self.envs.set_type(&d.op.get_hir_id(), t); - self.envs - .set_type(&d.get_hir_id(), struct_t.defs.get(&d.value.name).unwrap()); + if let Some(field) = struct_t.defs.get(&d.value.name) { + self.envs.set_type(&d.get_hir_id(), field); + + if let Type::Func(_ft) = &**struct_t.defs.get(&d.value.name).unwrap() { + let resolved = self.resolve(&d.value.get_hir_id()).unwrap(); - if let Type::Func(_ft) = &**struct_t.defs.get(&d.value.name).unwrap() { - let resolved = self.resolve(&d.value.get_hir_id()).unwrap(); + self.add_tmp_resolution_to_current_fn(&d.get_hir_id(), &resolved); + } + } else { + // check for struct impls + if let Some(method) = self.hir.struct_methods.get(&d.value.name) { + let method = method.values().next().unwrap(); - self.add_tmp_resolution_to_current_fn(&d.get_hir_id(), &resolved); + self.envs + .set_type(&method.hir_id, &method.signature.clone().into()); + + let resolved = self.resolve(&d.value.get_hir_id()).unwrap(); + + self.add_tmp_resolution_to_current_fn(&d.get_hir_id(), &resolved); + } else { + panic!("Not a field and not a method ? How did you get here ?"); + } } } other => { diff --git a/src/lib/infer/monomorphize/monomorphizer.rs b/src/lib/infer/monomorphize/monomorphizer.rs index e9bc63cb..fe178b29 100644 --- a/src/lib/infer/monomorphize/monomorphizer.rs +++ b/src/lib/infer/monomorphize/monomorphizer.rs @@ -261,17 +261,6 @@ impl<'a, 'b> VisitorMut<'a> for Monomorphizer<'b> { self.visit_expression(&mut r#if.predicat); self.visit_body(&mut r#if.body); - - if let Some(e) = &mut r#if.else_ { - match &mut **e { - Else::Body(b) => { - self.visit_body(b); - } - Else::If(i) => { - self.visit_if(i); - } - } - } } // FIXME: missing IF, assign, etc etc diff --git a/src/lib/parser/mod.rs b/src/lib/parser/mod.rs index 8f8228ae..048a3fcc 100644 --- a/src/lib/parser/mod.rs +++ b/src/lib/parser/mod.rs @@ -366,7 +366,7 @@ pub fn parse_impl(input: Parser) -> Res { many0(line_ending), indent(separated_list1( line_ending, - preceded(parse_block_indent, parse_fn), + preceded(parse_block_indent, alt((parse_self_fn, parse_fn))), )), )), |(_, name, types, _, defs)| Impl::new(name, types, defs), @@ -451,6 +451,27 @@ pub fn parse_prototype(input: Parser) -> Res { )(input) } +pub fn parse_self_fn(input: Parser) -> Res { + map( + tuple(( + parse_identity, + parse_identity, + tag("@"), + terminated( + tuple(( + parse_identifier_or_operator, + many0(preceded(space1, parse_identifier)), + )), + delimited(space0, char('='), space0), + ), + parse_body, + )), + |(node_id, self_node_id, _, (name, arguments), body)| { + FunctionDecl::new_self(node_id, self_node_id, name, body, arguments) + }, + )(input) +} + pub fn parse_fn(input: Parser) -> Res { map( tuple(( @@ -542,19 +563,18 @@ pub fn parse_if(input: Parser) -> Res { tuple(( parse_identity, terminated(tag("if"), space1), - terminated(parse_expression, space0), - many1(line_ending), - parse_then, + parse_expression, + opt(preceded(many1(line_ending), parse_then_multi)), parse_body, opt(tuple((line_ending, parse_else))), )), - |(node_id, _if_, cond, _, _, body, 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 { +pub fn parse_then_multi(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)? @@ -668,6 +688,7 @@ pub fn parse_assign_left_side(input: Parser) -> Res { pub fn parse_expression(input: Parser) -> Res { alt(( + map(parse_struct_ctor, Expression::new_struct_ctor), map( preceded(terminated(tag("return"), space1), parse_expression), Expression::new_return, @@ -681,7 +702,6 @@ pub fn parse_expression(input: Parser) -> Res { |(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) }), @@ -783,11 +803,8 @@ pub fn parse_arguments(input: Parser) -> Res { ), map( tuple(( - space1, - separated_list1( - tuple((space0, terminated(tag(","), space0), space0)), - parse_argument, - ), + space0, + separated_list1(tuple((space0, tag(","), space0)), parse_argument), )), |(_, args)| args, ), @@ -843,6 +860,8 @@ pub fn parse_identifier_path(input: Parser) -> Res { Identifier::new("(*)".to_string(), node_id) }), parse_identifier, + parse_capitalized_identifier, + // map(parse_type, |t| Identifier::new(t.to_string(), 0)), // fixme: 0 )), ), IdentifierPath::new, diff --git a/src/lib/parser/tests.rs b/src/lib/parser/tests.rs index 3e6f7159..e107712f 100644 --- a/src/lib/parser/tests.rs +++ b/src/lib/parser/tests.rs @@ -753,6 +753,18 @@ mod parse_if { assert!(rest.fragment().is_empty()); } + + #[test] + fn valid_classical_if() { + let input = Parser::new_extra( + "if a\n b\nelse c", + ParserCtx::new(PathBuf::new(), Config::default()), + ); + + let (rest, _parsed) = parse_if(input).finish().unwrap(); + + assert!(rest.fragment().is_empty()); + } } #[cfg(test)] diff --git a/src/lib/resolver/resolve_ctx.rs b/src/lib/resolver/resolve_ctx.rs index 140df1dc..af94e081 100644 --- a/src/lib/resolver/resolve_ctx.rs +++ b/src/lib/resolver/resolve_ctx.rs @@ -24,6 +24,23 @@ impl<'a> ResolveCtx<'a> { } } + pub fn add_to_struct_scope(&mut self, struct_name: String, name: String, node_id: NodeId) { + let mut struct_scope_name = self.cur_scope.clone(); + struct_scope_name.path.push(Identifier::new(struct_name, 0)); + + if let Some(ref mut scopes) = self.scopes.get_mut(&struct_scope_name) { + scopes.add(name, node_id); + } + } + + pub fn new_struct(&mut self, name: Identifier) { + let mut struct_scope_name = self.cur_scope.clone(); + struct_scope_name.path.push(name); + self.scopes.insert(struct_scope_name.clone(), Scopes::new()); + + // self.cur_scope = name; + } + pub fn new_mod(&mut self, name: IdentifierPath) { self.scopes.insert(name.clone(), Scopes::new()); @@ -69,6 +86,8 @@ impl<'a> Visitor<'a> for ResolveCtx<'a> { } } TopLevel::Struct(s) => { + self.new_struct(s.name.clone()); + self.add_to_current_scope(s.name.name.clone(), s.name.node_id); s.defs.iter().for_each(|p| { @@ -81,7 +100,16 @@ impl<'a> Visitor<'a> for ResolveCtx<'a> { proto.mangle(&i.types.iter().map(|t| t.get_name()).collect::>()); + // FIXME: This is a hack that pollute the scope with struct methods + // This conflicts with trait impls that need to be in scope to be solved + // Waiting for the struct dot notation instead self.add_to_current_scope((*proto.name).clone(), proto.node_id); + + self.add_to_struct_scope( + i.name.get_name(), + (*proto.name).clone(), + proto.node_id, + ); } } TopLevel::Mod(_, _m) => (), diff --git a/src/lib/testcases/basic/impl_self/main.rk b/src/lib/testcases/basic/impl_self/main.rk new file mode 100644 index 00000000..56e0ca6c --- /dev/null +++ b/src/lib/testcases/basic/impl_self/main.rk @@ -0,0 +1,12 @@ +struct Foo + bar :: Int64 + +impl Foo + @getbar = self.bar + +main = + let foo = Foo + bar: 42 + + foo.getbar() + diff --git a/src/lib/testcases/basic/impl_self/main.rk.out b/src/lib/testcases/basic/impl_self/main.rk.out new file mode 100644 index 00000000..f70d7bba --- /dev/null +++ b/src/lib/testcases/basic/impl_self/main.rk.out @@ -0,0 +1 @@ +42 \ No newline at end of file diff --git a/src/lib/testcases/basic/impl_self/main.rk.stdout b/src/lib/testcases/basic/impl_self/main.rk.stdout new file mode 100644 index 00000000..e69de29b diff --git a/src/lib/testcases/basic/recur/main.rk b/src/lib/testcases/basic/recur/main.rk index a21c8900..9df1a8c5 100644 --- a/src/lib/testcases/basic/recur/main.rk +++ b/src/lib/testcases/basic/recur/main.rk @@ -9,4 +9,4 @@ recur a = then a else recur (a - 1) -main = recur 1 +main = recur 2 diff --git a/src/lib/testcases/basic/struct_impl/main.rk b/src/lib/testcases/basic/struct_impl/main.rk new file mode 100644 index 00000000..5084ec57 --- /dev/null +++ b/src/lib/testcases/basic/struct_impl/main.rk @@ -0,0 +1,14 @@ +struct Foo + bar :: Int64 + toto :: String + +impl Foo + new a = + Foo + bar: a + toto: "Default" + getbar a = a.bar + +main = + let foo = Foo::new 42 + Foo::getbar foo diff --git a/src/lib/testcases/basic/struct_impl/main.rk.out b/src/lib/testcases/basic/struct_impl/main.rk.out new file mode 100644 index 00000000..f70d7bba --- /dev/null +++ b/src/lib/testcases/basic/struct_impl/main.rk.out @@ -0,0 +1 @@ +42 \ No newline at end of file diff --git a/src/lib/testcases/basic/struct_impl/main.rk.stdout b/src/lib/testcases/basic/struct_impl/main.rk.stdout new file mode 100644 index 00000000..e69de29b diff --git a/src/lib/tests.rs b/src/lib/tests.rs index 71b3fa14..f20d9c9d 100644 --- a/src/lib/tests.rs +++ b/src/lib/tests.rs @@ -122,6 +122,10 @@ 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_impl_self_main() { + run("testcases/basic/impl_self/main.rk", include_str!("testcases/basic/impl_self/main.rk"), include_str!("testcases/basic/impl_self/main.rk.out"), include_str!("testcases/basic/impl_self/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")); } @@ -186,6 +190,10 @@ 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_struct_impl_main() { + run("testcases/basic/struct_impl/main.rk", include_str!("testcases/basic/struct_impl/main.rk"), include_str!("testcases/basic/struct_impl/main.rk.out"), include_str!("testcases/basic/struct_impl/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")); } diff --git a/std/src/externs.rk b/std/src/externs.rk index 318ca397..b0fbcccb 100644 --- a/std/src/externs.rk +++ b/std/src/externs.rk @@ -10,8 +10,8 @@ c_strcat dest src = strcat dest, src extern strcpy :: String -> String -> String c_strcpy dest src = strcpy dest, src -#extern strncpy :: String -> String -> Int64 -> String -#c_strncpy dest src len = strncpy dest, src, len +extern strncpy :: String -> String -> Int64 -> String +c_strncpy dest src len = strncpy dest, src, len extern sprintf :: String -> String -> Int64 -> Int64 c_sprintf dest format i = sprintf dest, format, i