Skip to content

Commit

Permalink
Merge pull request #17 from coco33920/add_user_defined_function
Browse files Browse the repository at this point in the history
Add user defined function
  • Loading branch information
coco33920 authored Nov 15, 2023
2 parents a0da3e6 + a12ff59 commit 05fb455
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 30 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "mini-calc"
version = "2.4.0"
version = "2.5.0"
license = "GPL-3.0-or-later"
description = "A minimalistic configurable rust calculator"
homepage = "https://calc.nwa2coco.fr"
Expand Down
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ Check [the website](https://calc.nwa2coco.fr) for more informations.
- [X] Add unary operator
- [X] not (!)
- [ ] For later
- [ ] Defining your own functions
- [X] Defining your own functions
- [ ] Add RPN mode
- [ ] Hidden multiplication

Expand Down Expand Up @@ -218,3 +218,9 @@ You can now use logic! I implemented the following functions:
Example:

![img.png](docs/assets/logic.png)

## User defined functions!

You can define your own functions!

![img.png](docs/assets/user_defined.png)
Binary file added docs/assets/user_defined.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/configuration/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ pub fn load_color(string: String) -> Color {

pub fn replace_variable(str: String) -> String {
str.replace("%author%", "Charlotte Thomas")
.replace("%version%", "v2.4.0")
.replace("%version%", "v2.5.0")
.to_string()
}

Expand Down
60 changes: 39 additions & 21 deletions src/interpreting/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,20 @@ use crate::interpreting::function::{
use crate::interpreting::stdlib::exec;
use crate::parsing::ast::{Ast, Parameters};

pub fn interpret(ast: Ast, mut ram: &mut HashMap<String, Parameters>) -> Parameters {
pub fn interpret(
ast: &Ast,
mut ram: &mut HashMap<String, Parameters>,
mut function: &mut HashMap<String, (Vec<Ast>, Ast)>,
) -> Parameters {
match ast {
Ast::Nil => Parameters::Null,
Ast::Node {
value: v,
left: l,
right: r,
} => {
let param1 = interpret(*l, &mut ram);
let param2 = interpret(*r, &mut ram);
let param1 = interpret(l, &mut ram, &mut function);
let param2 = interpret(r, &mut ram, &mut function);
let last = match v {
Parameters::PlusOperation => add(param1, param2, Some(&ram)),
Parameters::MinusOperation => minus(param1, param2, Some(&ram)),
Expand All @@ -31,24 +35,32 @@ pub fn interpret(ast: Ast, mut ram: &mut HashMap<String, Parameters>) -> Paramet
Parameters::LesserOrEqualOperation => lesser_or_equal(param1, param2, Some(&ram)),
Parameters::AndOperation => and(param1, param2, Some(&ram)),
Parameters::OrOperation => or(param1, param2, Some(&ram)),
Parameters::Assign => {
let (a, b) = assign(param1, param2);
if a != "".to_string() {
(ram).insert(a, b);
Parameters::Assign => match *(l.clone()) {
Ast::Call { name: n, lst: list } => {
if n.as_str() != "" {
(function).insert(n.to_string(), (list, *r.clone()));
}
Parameters::Null
}
Parameters::Null
}
Parameters::Float(f) => Parameters::Float(f),
Parameters::Int(i) => Parameters::Int(i),
Parameters::Identifier(s) => Parameters::Identifier(s),
Parameters::Bool(b) => Parameters::Bool(b),
_ => {
let (a, b) = assign(param1, param2);
if a != "".to_string() {
(ram).insert(a, b);
}
Parameters::Null
}
},
Parameters::Float(f) => Parameters::Float(*f),
Parameters::Int(i) => Parameters::Int(*i),
Parameters::Identifier(s) => Parameters::Identifier(s.clone()),
Parameters::Bool(b) => Parameters::Bool(*b),
Parameters::Null => Parameters::Null,
};
last.clone()
}
Ast::Call { name: n, lst: list } => {
let v: Vec<Parameters> = list.iter().map(|x| interpret(x.clone(), ram)).collect();
exec(n, v, Some(&ram))
let v: Vec<Parameters> = list.iter().map(|x| interpret(x, ram, function)).collect();
exec(n.to_string(), v, Some(&ram), Some(function))
}
}
}
Expand All @@ -63,78 +75,84 @@ mod test {
#[test]
fn test_interpreter_int() {
let mut ram: HashMap<String, Parameters> = HashMap::new();
let mut function: HashMap<String,(Vec<Ast>,Ast)> = HashMap::new();
let expected = Parameters::Int(2);
let ast = Ast::Node {
value: Parameters::Int(2),
left: Box::from(Ast::Nil),
right: Box::from(Ast::Nil),
};
let result = interpret(ast, &mut ram);
let result = interpret(&ast, &mut ram,&mut function);
assert_eq!(result, expected)
}

#[test]
fn test_interpreter_float() {
let mut ram: HashMap<String, Parameters> = HashMap::new();
let mut function: HashMap<String,(Vec<Ast>,Ast)> = HashMap::new();
let expected = Parameters::Float(2.0);
let ast = Ast::Node {
value: Parameters::Float(2.0),
left: Box::from(Ast::Nil),
right: Box::from(Ast::Nil),
};
let result = interpret(ast, &mut ram);
let result = interpret(&ast, &mut ram,&mut function);
assert_eq!(result, expected)
}

#[test]
fn test_interpreter_plus_operation() {
let mut ram: HashMap<String, Parameters> = HashMap::new();
let mut function: HashMap<String,(Vec<Ast>,Ast)> = HashMap::new();
let expected = Parameters::Int(2);
let ast = Ast::Node {
value: Parameters::PlusOperation,
left: Box::from(Ast::new(Parameters::Int(1))),
right: Box::from(Ast::new(Parameters::Int(1))),
};
let result = interpret(ast, &mut ram);
let result = interpret(&ast, &mut ram,&mut function);
assert_eq!(result, expected)
}

#[test]
fn test_interpreter_minus_operation() {
let mut ram: HashMap<String, Parameters> = HashMap::new();
let mut function: HashMap<String,(Vec<Ast>,Ast)> = HashMap::new();
let expected = Parameters::Int(0);
let ast = Ast::Node {
value: Parameters::MinusOperation,
left: Box::from(Ast::new(Parameters::Int(1))),
right: Box::from(Ast::new(Parameters::Int(1))),
};
let result = interpret(ast, &mut ram);
let result = interpret(&ast, &mut ram,&mut function);
assert_eq!(result, expected)
}

#[test]
fn test_interpreter_mult_operation() {
let mut ram: HashMap<String, Parameters> = HashMap::new();
let mut function: HashMap<String,(Vec<Ast>,Ast)> = HashMap::new();
let expected = Parameters::Int(1);
let ast = Ast::Node {
value: Parameters::MultiplicationOperation,
left: Box::from(Ast::new(Parameters::Int(1))),
right: Box::from(Ast::new(Parameters::Int(1))),
};
let result = interpret(ast, &mut ram);
let result = interpret(&ast, &mut ram,&mut function);
assert_eq!(result, expected)
}

#[test]
fn test_interpreter_divide_operation() {
let mut ram: HashMap<String, Parameters> = HashMap::new();
let mut function: HashMap<String,(Vec<Ast>,Ast)> = HashMap::new();
let expected = Parameters::Float(1.0);
let ast = Ast::Node {
value: Parameters::DivideOperation,
left: Box::from(Ast::new(Parameters::Int(1))),
right: Box::from(Ast::new(Parameters::Int(1))),
};
let result = interpret(ast, &mut ram);
let result = interpret(&ast, &mut ram,&mut function);
assert_eq!(result, expected)
}
}
43 changes: 40 additions & 3 deletions src/interpreting/stdlib.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
use std::collections::HashMap;
use std::f64::consts::PI;
use std::f64::consts::{E, PI};

use crate::parsing::ast::Parameters;
use crate::interpreting::interpreter::interpret;
use crate::parsing::ast::{Ast, Parameters};

pub fn exec(
s: String,
lst: Vec<Parameters>,
ram: Option<&HashMap<String, Parameters>>,
functions: Option<&mut HashMap<String, (Vec<Ast>, Ast)>>,
) -> Parameters {
match s.as_str() {
"cos" => cos(&lst, ram),
Expand All @@ -28,7 +30,42 @@ pub fn exec(
"ceil" => ceil(&lst, ram),
"floor" => floor(&lst, ram),
"round" => round(&lst, ram),
_ => cos(&lst, ram),
s => {
let mut sram: HashMap<String, Parameters> = HashMap::new();
sram.insert("pi".to_string(), Parameters::Float(PI));
sram.insert("e".to_string(), Parameters::Float(E));
match functions.cloned() {
None => Parameters::Identifier("This function is unknown".to_string()),
Some(mut f) => {
let fs = f.get_mut(s);
let (vec, ast): (&mut Vec<Ast>, &mut Ast) = match fs {
None => {
return Parameters::Identifier("This function is unknown".to_string());
}
Some((a, b)) => (a, b),
};
let mut names = Vec::new();
for v in vec {
match v {
Ast::Nil => (),
Ast::Call { .. } => (),
Ast::Node {
value: v,
left: _l,
right: _r,
} => match v {
Parameters::Identifier(s) => names.push(s.clone()),
_ => (),
},
}
}
names.iter().zip(lst).for_each(|(name, param)| {
sram.insert(name.to_string(), param);
});
interpret(ast, &mut sram, &mut HashMap::new())
}
}
}
}
}

Expand Down
7 changes: 4 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use linefeed::{Interface, ReadResult};
use crate::configuration::loader::{load, load_config, Loaded};
use crate::interpreting::interpreter::interpret;
use crate::lexing::lexer::lex;
use crate::parsing::ast::Parameters;
use crate::parsing::ast::{Ast, Parameters};
use crate::parsing::parser::CalcParser;

mod configuration;
Expand All @@ -34,7 +34,7 @@ fn main() {
let style = loaded.prompt_style;
let text = loaded.prompt;
let mut verbose = false;
let version: String = "v2.4.0".to_string();
let version: String = "v2.5.0".to_string();
interface
.set_prompt(&format!(
"\x01{prefix}\x02{text}\x01{suffix}\x02",
Expand All @@ -44,6 +44,7 @@ fn main() {
))
.unwrap();
let mut ram: HashMap<String, Parameters> = HashMap::new();
let mut functions: HashMap<String, (Vec<Ast>, Ast)> = HashMap::new();
ram.insert("pi".to_string(), Parameters::Float(PI));
ram.insert("e".to_string(), Parameters::Float(E));
while let ReadResult::Input(line) = interface.read_line().unwrap() {
Expand Down Expand Up @@ -80,7 +81,7 @@ fn main() {
println!("{:#?}", p);
println!()
}
let result = interpret(p.clone(), &mut ram);
let result = interpret(&p, &mut ram, &mut functions);
if result != Parameters::Null {
result.pretty_print(Some(&ram))
}
Expand Down

0 comments on commit 05fb455

Please sign in to comment.