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