From 5c950a1f40448b950d7caa650c1500bf2c780fc8 Mon Sep 17 00:00:00 2001 From: PlexSheep Date: Sat, 11 Feb 2023 16:07:28 +0100 Subject: [PATCH 1/2] reading expressions --- Cargo.lock | 118 ++++++++++++++++++++++++++++++++++++++- Cargo.toml | 2 + src/expression_parser.rs | 98 ++++++++++++++++++++++++++++++++ src/main.rs | 24 ++++++-- 4 files changed, 236 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 42dde8e..1c25e25 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,12 +2,27 @@ # It is not intended for manual editing. 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]] name = "anyhow" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + [[package]] name = "bitflags" version = "1.3.2" @@ -124,6 +139,88 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "once_cell" version = "1.17.0" @@ -178,12 +275,31 @@ dependencies = [ "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]] name = "rust_command_line_calculator" -version = "0.1.0" +version = "0.1.1" dependencies = [ "anyhow", "clap", + "num", + "regex", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index de3ada9..6aa94c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,3 +18,5 @@ path = "src/main.rs" [dependencies] clap = { version = "4.0", features = ["derive"] } anyhow = "1.0" +num = "0.4.0" +regex = "1.7.1" diff --git a/src/expression_parser.rs b/src/expression_parser.rs index 139597f..8e1e359 100644 --- a/src/expression_parser.rs +++ b/src/expression_parser.rs @@ -1,2 +1,100 @@ +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 +enum Task { + None, + Sqrt, + Power, + Log, +} +// 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 => Task::Log, + } + } +} + +// 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, +} + +// 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 + */ + pub fn new(expression_text: String) -> Expression { + + // find children + let re_sub_expression = Regex::new(r"\w+\(.+\)").unwrap(); + if re_sub_expression.is_match(&expression_text) { + for sub_expression_text in re_sub_expression.captures_iter(&expression_text) { + println!("{}", &sub_expression_text[0]); + } + } + + let expression = Expression { + text: expression_text, + // TODO generate these from the text! + task: Task::None, + complex: false, + inner_value: 0.0, + outer_value: 0.0, + children: Vec::new(), + }; + expression + } +} diff --git a/src/main.rs b/src/main.rs index 0385002..4e5100d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,10 @@ +use clap::{Parser, Subcommand}; + mod expression_parser; mod linear_algebra; -use clap::{Parser, Subcommand}; +use expression_parser::Expression; + #[derive(Parser)] #[command(author, version, about, long_about = None)] @@ -18,19 +21,30 @@ struct Arg { command: Option, /// An expression that should be used to calculate something - expressionVector: Vec, + expression_texts: Vec, } #[derive(Subcommand)] enum Commands { /// Assert if two expressions are equal to each other Equal { - expressionVector: Vec, } } fn main() { let args = Arg::parse(); - dbg!(args.expressionVector); -} + let mut expression_vec: Vec = 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 = Vec::new(); + expression_texts_concat.push(args.expression_texts.join(" ").trim().to_string()); + + for expression_text in expression_texts_concat { + expression_vec.push(Expression::new(expression_text)); + } + dbg!(expression_vec); + +} From fcc1f33e7bb14d0e074049ec8298c9c9343a1261 Mon Sep 17 00:00:00 2001 From: PlexSheep Date: Sat, 11 Feb 2023 18:42:18 +0100 Subject: [PATCH 2/2] child expressions parsing --- src/expression_parser.rs | 69 ++++++++++++++++++++++++++++++++++++---- src/main.rs | 35 ++++++++++---------- 2 files changed, 80 insertions(+), 24 deletions(-) diff --git a/src/expression_parser.rs b/src/expression_parser.rs index 8e1e359..a8c4fed 100644 --- a/src/expression_parser.rs +++ b/src/expression_parser.rs @@ -5,11 +5,11 @@ use regex::Regex; // configure possible Tasks. // None means, the Expression doesn't send it's Value to a Task Handler #[derive(Debug)] // automatically generate Debug Formatter -enum Task { +pub enum Task { None, Sqrt, Power, - Log, + Log(u64), } // How to clone a Task, i was supprised I had to do it myself. @@ -20,11 +20,23 @@ impl Clone for Task { Task::None => Task::None, Task::Sqrt => Task::Sqrt, Task::Power => Task::Power, - Task::Log => Task::Log, + 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) @@ -75,21 +87,59 @@ impl Clone for Expression{ 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) -> Expression { + pub fn new(expression_text: String, task: Task) -> Expression { // find children - let re_sub_expression = Regex::new(r"\w+\(.+\)").unwrap(); + // 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 = Vec::new(); for sub_expression_text in re_sub_expression.captures_iter(&expression_text) { - println!("{}", &sub_expression_text[0]); + // 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::None, + task: task, complex: false, inner_value: 0.0, outer_value: 0.0, @@ -97,4 +147,9 @@ impl Expression { }; expression } + + pub fn process(&self) { + println!("{}", self.text); + } } + diff --git a/src/main.rs b/src/main.rs index 4e5100d..fc8e00d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,32 +4,30 @@ mod expression_parser; mod linear_algebra; use expression_parser::Expression; +use expression_parser::Task; #[derive(Parser)] #[command(author, version, about, long_about = None)] struct Arg { +// /// Optional subcommand +// #[command(subcommand)] +// command: Option, + /// Show verbose output #[arg(short, long)] verbose: bool, - /// Show debug output - #[arg(short, long, default_value_t = false)] - debug: bool, - - #[command(subcommand)] - command: Option, - /// An expression that should be used to calculate something - expression_texts: Vec, + expressions: Vec, } -#[derive(Subcommand)] -enum Commands { - /// Assert if two expressions are equal to each other - Equal { - } -} +//#[derive(Subcommand)] +//enum Commands { +// /// Assert if two expressions are equal to each other +// Equal { +// } +//} fn main() { let args = Arg::parse(); @@ -40,11 +38,14 @@ fn main() { // TODO implement splitting of expressions, currently they are made only into a single big // expression text let mut expression_texts_concat: Vec = Vec::new(); - expression_texts_concat.push(args.expression_texts.join(" ").trim().to_string()); + expression_texts_concat.push(args.expressions.join(" ").trim().to_string()); for expression_text in expression_texts_concat { - expression_vec.push(Expression::new(expression_text)); + expression_vec.push(Expression::new(expression_text, Task::None)); + } + + for expression in expression_vec { + expression.process(); } - dbg!(expression_vec); }