Merge pull request #2 from PlexSheep/devel

Recursive formulation of Expressions
This commit is contained in:
PlexSheep 2023-02-11 19:04:11 +01:00 committed by GitHub
commit 37c71da907
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 305 additions and 19 deletions

118
Cargo.lock generated
View File

@ -2,12 +2,27 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 3
[[package]]
name = "aho-corasick"
version = "0.7.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.69" version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800"
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.3.2" version = "1.3.2"
@ -124,6 +139,88 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "num"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606"
dependencies = [
"num-bigint",
"num-complex",
"num-integer",
"num-iter",
"num-rational",
"num-traits",
]
[[package]]
name = "num-bigint"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-complex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d"
dependencies = [
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-iter"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
dependencies = [
"autocfg",
"num-bigint",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
]
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.17.0" version = "1.17.0"
@ -178,12 +275,31 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "regex"
version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
[[package]] [[package]]
name = "rust_command_line_calculator" name = "rust_command_line_calculator"
version = "0.1.0" version = "0.1.1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"clap", "clap",
"num",
"regex",
] ]
[[package]] [[package]]

View File

@ -18,3 +18,5 @@ path = "src/main.rs"
[dependencies] [dependencies]
clap = { version = "4.0", features = ["derive"] } clap = { version = "4.0", features = ["derive"] }
anyhow = "1.0" anyhow = "1.0"
num = "0.4.0"
regex = "1.7.1"

View File

@ -1,2 +1,155 @@
use std::fmt;
use regex::Regex;
// In an expression like `sqrt(25)` the Task would correspond to `sqrt`. This is the enum to
// configure possible Tasks.
// None means, the Expression doesn't send it's Value to a Task Handler
#[derive(Debug)] // automatically generate Debug Formatter
pub enum Task {
None,
Sqrt,
Power,
Log(u64),
}
// How to clone a Task, i was supprised I had to do it myself.
impl Clone for Task {
fn clone(&self) -> Self {
// This can probably be done cleaner than with a verbose match. FIXME
match self {
Task::None => Task::None,
Task::Sqrt => Task::Sqrt,
Task::Power => Task::Power,
Task::Log(base) => Task::Log(*base), // TODO add base for log
}
}
}
impl Task {
pub fn new(task_text: &str) -> Task {
match task_text {
"none" => Task::None,
"sqrt" => Task::Sqrt,
"power"|"pow" => Task::Power,
"log"|"ln" => Task::Log(10), // TODO add base
// what to do if a bad task was given:
&_ => {eprintln!("Bad Task: {}", task_text); std::process::exit(1); },
}
}
}
// An Expression is something that can be calculated. 20+5 is an expression. Expressions can
// contain other
// Expressions and have tasks: 20+sqrt(20+5)
// Expressions are marked down with braces and a task before those braces:
// task(Expression)
// once the Value of the Expression got calculated, the calculated value should be sent to the
// TaskHandler, if the Task of the Expression is not Task::None
pub struct Expression {
text: String,
task: Task,
complex: bool,
inner_value: f64,
outer_value: f64,
children: Vec<Expression>,
}
// Debug Formatter for Expression
impl fmt::Debug for Expression {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Expression")
.field("text", &self.text)
.field("task", &self.task)
.field("is complex?", &self.complex)
.field("inner value", &self.inner_value)
.field("outer value", &self.outer_value)
.field("children", &self.children)
.finish()
}
}
// implement clone by ourselves, as it's not automatically done for us.
impl Clone for Expression{
fn clone(&self) -> Self {
Expression {
text: self.text.clone(),
task: self.task.clone(),
complex: self.complex.clone(), // TODO add support for complex numbers
inner_value: self.inner_value.clone(),
outer_value: self.outer_value.clone(),
children: self.children.clone(),
}
}
}
/*
* Main logic for the Expression struct
*/
impl Expression {
/*
* Main function for making text into Expression
* example: "12 + log_10(10 + 15) + 3"
* has a sub expression log_10(10 + 5), which has Task::Log with base 10
*/
pub fn new(expression_text: String, task: Task) -> Expression {
// find children
// TODO add error for unused task parameters
let re_sub_expression = Regex::new(r"\w+\(.+?\)").unwrap();
if re_sub_expression.is_match(&expression_text) {
let mut children: Vec<Expression> = Vec::new();
for sub_expression_text in re_sub_expression.captures_iter(&expression_text) {
// if any task parameters are set ( syntax: task_para(expression) )
if sub_expression_text[0].contains('_') {
let task_and_expr: Vec<&str> = sub_expression_text[0].split(['_', '(']).collect();
let task_text = task_and_expr[0].clone().to_lowercase();
let task_param = task_and_expr[1].clone().to_string();
let task = match task_text.as_str() {
"none" => Task::None,
"sqrt" => Task::Sqrt,
"power" => Task::Power,
"log" => {let base: u64 = task_param.parse().unwrap(); Task::Log(base)},
// what to do if a bad task was given:
&_ => {eprintln!("Bad Task: {}", task_text); std::process::exit(1); },
};
let expression_inner = task_and_expr[2].clone().to_string();
children.push(Expression::new(expression_inner, task));
}
// if there are no parameters we need to do diffrent splitting and assume defaults
else {
let task_and_expr: Vec<&str> = sub_expression_text[0].split(['(']).collect();
let task_text = task_and_expr[0].clone().to_lowercase();
let task = match task_text.as_str() {
"none" => Task::None,
"sqrt" => Task::Sqrt,
"power" => Task::Power,
"log" => Task::Log(10),
// what to do if a bad task was given:
&_ => {eprintln!("Bad Task: {}", task_text); std::process::exit(1); },
};
let expression_inner = task_and_expr[1].clone().to_string();
children.push(Expression::new(expression_inner, task));
}
}
#[cfg(debug_assertions)]
dbg!(children);
}
let expression = Expression {
text: expression_text,
// TODO generate these from the text!
task: task,
complex: false,
inner_value: 0.0,
outer_value: 0.0,
children: Vec::new(),
};
expression
}
pub fn process(&self) {
println!("{}", self.text);
}
}

View File

@ -1,36 +1,51 @@
use clap::{Parser, Subcommand};
mod expression_parser; mod expression_parser;
mod linear_algebra; mod linear_algebra;
use clap::{Parser, Subcommand}; use expression_parser::Expression;
use expression_parser::Task;
#[derive(Parser)] #[derive(Parser)]
#[command(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
struct Arg { struct Arg {
// /// Optional subcommand
// #[command(subcommand)]
// command: Option<Commands>,
/// Show verbose output /// Show verbose output
#[arg(short, long)] #[arg(short, long)]
verbose: bool, verbose: bool,
/// Show debug output
#[arg(short, long, default_value_t = false)]
debug: bool,
#[command(subcommand)]
command: Option<Commands>,
/// An expression that should be used to calculate something /// An expression that should be used to calculate something
expressionVector: Vec<String>, expressions: Vec<String>,
} }
#[derive(Subcommand)] //#[derive(Subcommand)]
enum Commands { //enum Commands {
/// Assert if two expressions are equal to each other // /// Assert if two expressions are equal to each other
Equal { // Equal {
expressionVector: Vec<String>, // }
} //}
}
fn main() { fn main() {
let args = Arg::parse(); let args = Arg::parse();
dbg!(args.expressionVector); let mut expression_vec: Vec<Expression> = Vec::new();
// join expression_texts to a big expression text, split at '%'. Remove unnessecary whitespace
// at the start ot end.
// TODO implement splitting of expressions, currently they are made only into a single big
// expression text
let mut expression_texts_concat: Vec<String> = Vec::new();
expression_texts_concat.push(args.expressions.join(" ").trim().to_string());
for expression_text in expression_texts_concat {
expression_vec.push(Expression::new(expression_text, Task::None));
} }
for expression in expression_vec {
expression.process();
}
}