From 795d1aab2f255813724260c61f8b6245a3060578 Mon Sep 17 00:00:00 2001 From: BismuthBloom Date: Tue, 10 Mar 2026 17:23:27 -0400 Subject: [PATCH 1/5] Addition to implicit multiplication for numbers or vars next to parentheses --- io/src/FromString.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/io/src/FromString.cpp b/io/src/FromString.cpp index 11351d7c..7df9cf88 100644 --- a/io/src/FromString.cpp +++ b/io/src/FromString.cpp @@ -213,7 +213,12 @@ auto ImplicitMultiplication(std::stringstream&& sstr) -> std::string continue; } - if (lastToken == ")" && token == "(") { + if (lastToken == ")" && token != ",") { + secondPassResult << '*' << token; + lastToken = token; + continue; + } + else if (!is_function(lastToken) && token == "(") { secondPassResult << '*' << token; lastToken = token; continue; From baf5bf298b1ed48932c01bcc710f43323ef6f6b1 Mon Sep 17 00:00:00 2001 From: BismuthBloom Date: Tue, 17 Mar 2026 16:41:53 -0400 Subject: [PATCH 2/5] Fixed bug for simple implicit multiplication and added a parse test --- io/src/FromString.cpp | 8 +++++++- io/tests/InFixTests.cpp | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/io/src/FromString.cpp b/io/src/FromString.cpp index 7df9cf88..8285c219 100644 --- a/io/src/FromString.cpp +++ b/io/src/FromString.cpp @@ -208,17 +208,23 @@ auto ImplicitMultiplication(std::stringstream&& sstr) -> std::string while (sstr >> token) { if (is_function(token) || is_operator(token)) { + if (lastToken == ")") { + secondPassResult << "*"; + } secondPassResult << token; lastToken = token; continue; } + // only "," out of nonfunc or operators should ignore implicit mult. + // (a+b)c -> (a+b)*c if (lastToken == ")" && token != ",") { secondPassResult << '*' << token; lastToken = token; continue; } - else if (!is_function(lastToken) && token == "(") { + // a(b+c) -> a*(b+c) + else if (lastToken != "" && !is_function(lastToken) && token == "(") { secondPassResult << '*' << token; lastToken = token; continue; diff --git a/io/tests/InFixTests.cpp b/io/tests/InFixTests.cpp index 2b06723c..4453e6a9 100644 --- a/io/tests/InFixTests.cpp +++ b/io/tests/InFixTests.cpp @@ -116,4 +116,22 @@ TEST_CASE("In-Fix Works With Parenthesis", "[Parsing]") const auto result = std::string{ "(4+3)" } | Oasis::PreProcessInFix | InFixWithDefaultArgs; REQUIRE(result.has_value()); REQUIRE(result.value()->Equals(expected)); +} + +TEST_CASE("In-Fix Works With Trivial Implicit Multiplication", "[Parsing]") +{ + const Oasis::Multiply<> expected { + Oasis::Variable { "y" }, + Oasis::Add { + Oasis::Variable { "x" }, + Oasis::Real { 1 } }, + Oasis::Log { + Oasis::Variable { "a" }, + Oasis::Variable { "x" } } + }; + + auto InFixWithDefaultArgs = [](const std::string& in) { return Oasis::FromInFix(in); }; + const auto result = std::string {"y(x+1)log(a,x)" } | Oasis::PreProcessInFix | InFixWithDefaultArgs; + REQUIRE(result.has_value()); + REQUIRE(result.value()->Equals(expected)); } \ No newline at end of file From e59f220403492243ec41c433fcd629cbb010464c Mon Sep 17 00:00:00 2001 From: BismuthBloom Date: Tue, 17 Mar 2026 17:12:31 -0400 Subject: [PATCH 3/5] Fixed * before operator and added another test case --- io/src/FromString.cpp | 4 ++-- io/tests/InFixTests.cpp | 25 +++++++++++++++++++++---- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/io/src/FromString.cpp b/io/src/FromString.cpp index 8285c219..a1b8f4d0 100644 --- a/io/src/FromString.cpp +++ b/io/src/FromString.cpp @@ -208,7 +208,7 @@ auto ImplicitMultiplication(std::stringstream&& sstr) -> std::string while (sstr >> token) { if (is_function(token) || is_operator(token)) { - if (lastToken == ")") { + if (is_function(token) && lastToken == ")") { secondPassResult << "*"; } secondPassResult << token; @@ -224,7 +224,7 @@ auto ImplicitMultiplication(std::stringstream&& sstr) -> std::string continue; } // a(b+c) -> a*(b+c) - else if (lastToken != "" && !is_function(lastToken) && token == "(") { + else if (lastToken != "" && !is_function(lastToken) && token == "(") { secondPassResult << '*' << token; lastToken = token; continue; diff --git a/io/tests/InFixTests.cpp b/io/tests/InFixTests.cpp index 4453e6a9..ed2c821d 100644 --- a/io/tests/InFixTests.cpp +++ b/io/tests/InFixTests.cpp @@ -120,7 +120,7 @@ TEST_CASE("In-Fix Works With Parenthesis", "[Parsing]") TEST_CASE("In-Fix Works With Trivial Implicit Multiplication", "[Parsing]") { - const Oasis::Multiply<> expected { + const Oasis::Multiply<> mult { Oasis::Variable { "y" }, Oasis::Add { Oasis::Variable { "x" }, @@ -131,7 +131,24 @@ TEST_CASE("In-Fix Works With Trivial Implicit Multiplication", "[Parsing]") }; auto InFixWithDefaultArgs = [](const std::string& in) { return Oasis::FromInFix(in); }; - const auto result = std::string {"y(x+1)log(a,x)" } | Oasis::PreProcessInFix | InFixWithDefaultArgs; - REQUIRE(result.has_value()); - REQUIRE(result.value()->Equals(expected)); + const auto multresult = std::string {"y(x+1)log(a,x)" } | Oasis::PreProcessInFix | InFixWithDefaultArgs; + REQUIRE(multresult.has_value()); + REQUIRE(multresult.value()->Equals(mult)); + + const Oasis::Add<> add { + Oasis::Multiply { + Oasis::Variable { "a" }, + Oasis::Add { + Oasis::Variable { "b" }, + Oasis::Variable { "c" } } }, + Oasis::Multiply { + Oasis::Variable { "a" }, + Oasis::Add { + Oasis::Variable { "b" }, + Oasis::Variable { "c" } } } + }; + + const auto addresult = std::string {"a(b+c)+a(b+c)" } | Oasis::PreProcessInFix | InFixWithDefaultArgs; + REQUIRE(addresult.has_value()); + REQUIRE(addresult.value()->Equals(add)); } \ No newline at end of file From 023e4117f8349179d7ed97baa6650dfdb99a08e7 Mon Sep 17 00:00:00 2001 From: BismuthBloom Date: Fri, 20 Mar 2026 16:03:11 -0400 Subject: [PATCH 4/5] Final test and fix for implicit multiplication next to ( or ) --- io/src/FromString.cpp | 7 ++++++- io/tests/InFixTests.cpp | 22 ++++++++++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/io/src/FromString.cpp b/io/src/FromString.cpp index a1b8f4d0..7947c6a9 100644 --- a/io/src/FromString.cpp +++ b/io/src/FromString.cpp @@ -22,6 +22,8 @@ #include "Oasis/FromString.hpp" +#include + namespace { enum class Operator { @@ -207,6 +209,7 @@ auto ImplicitMultiplication(std::stringstream&& sstr) -> std::string std::string lastToken, token; while (sstr >> token) { + printf("token: %s\n", token.c_str()); if (is_function(token) || is_operator(token)) { if (is_function(token) && lastToken == ")") { secondPassResult << "*"; @@ -224,7 +227,7 @@ auto ImplicitMultiplication(std::stringstream&& sstr) -> std::string continue; } // a(b+c) -> a*(b+c) - else if (lastToken != "" && !is_function(lastToken) && token == "(") { + else if (lastToken != "" && !is_function(lastToken) && !is_operator(lastToken) && token == "(") { secondPassResult << '*' << token; lastToken = token; continue; @@ -257,6 +260,7 @@ auto ImplicitMultiplication(std::stringstream&& sstr) -> std::string lastToken = token; } + printf("\n"); return secondPassResult.str(); } @@ -276,6 +280,7 @@ auto FromInFix(const std::string& str, ParseImaginaryOption option) -> std::expe std::stringstream ss { str }; while (ss >> token) { + printf("token: %s\n", token.c_str()); // Operand if (auto newNumber = is_number(token); newNumber) { st.push(std::make_unique(newNumber.value())); diff --git a/io/tests/InFixTests.cpp b/io/tests/InFixTests.cpp index ed2c821d..9d931798 100644 --- a/io/tests/InFixTests.cpp +++ b/io/tests/InFixTests.cpp @@ -11,6 +11,8 @@ #include "Oasis/Multiply.hpp" #include "Oasis/FromString.hpp" #include "Oasis/Variable.hpp" +#include "Oasis/Exponent.hpp" +#include "Oasis/Derivative.hpp" template auto operator|(const std::string& str, FnT fn) -> boost::callable_traits::return_type_t @@ -131,7 +133,7 @@ TEST_CASE("In-Fix Works With Trivial Implicit Multiplication", "[Parsing]") }; auto InFixWithDefaultArgs = [](const std::string& in) { return Oasis::FromInFix(in); }; - const auto multresult = std::string {"y(x+1)log(a,x)" } | Oasis::PreProcessInFix | InFixWithDefaultArgs; + const auto multresult = std::string { "y(x+1)log(a,x)" } | Oasis::PreProcessInFix | InFixWithDefaultArgs; REQUIRE(multresult.has_value()); REQUIRE(multresult.value()->Equals(mult)); @@ -148,7 +150,23 @@ TEST_CASE("In-Fix Works With Trivial Implicit Multiplication", "[Parsing]") Oasis::Variable { "c" } } } }; - const auto addresult = std::string {"a(b+c)+a(b+c)" } | Oasis::PreProcessInFix | InFixWithDefaultArgs; + const auto addresult = std::string { "a(b+c)+a(b+c)" } | Oasis::PreProcessInFix | InFixWithDefaultArgs; REQUIRE(addresult.has_value()); REQUIRE(addresult.value()->Equals(add)); + + const Oasis::Multiply ddexp { + Oasis::Variable { "a" }, + Oasis::Add { + Oasis::Variable { "x" }, + Oasis::Exponent { + Oasis::Add { + Oasis::Variable { "x" }, + Oasis::Real { 1 } }, + Oasis::Real { 2 } } } + }; + const Oasis::Derivative dd {ddexp, Oasis::Variable { "x" } }; + + const auto ddresult = std::string { "dd(a(x+(x+1)^2),x)" } | Oasis::PreProcessInFix | InFixWithDefaultArgs; + REQUIRE(ddresult.has_value()); + REQUIRE(ddresult.value()->Equals(dd)); } \ No newline at end of file From c875f001ee7b72fc95d239884f36252e44b478b0 Mon Sep 17 00:00:00 2001 From: BismuthBloom Date: Fri, 20 Mar 2026 16:29:46 -0400 Subject: [PATCH 5/5] Remove debug statements --- io/src/FromString.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/io/src/FromString.cpp b/io/src/FromString.cpp index 7947c6a9..459c25e5 100644 --- a/io/src/FromString.cpp +++ b/io/src/FromString.cpp @@ -22,8 +22,6 @@ #include "Oasis/FromString.hpp" -#include - namespace { enum class Operator { @@ -209,7 +207,6 @@ auto ImplicitMultiplication(std::stringstream&& sstr) -> std::string std::string lastToken, token; while (sstr >> token) { - printf("token: %s\n", token.c_str()); if (is_function(token) || is_operator(token)) { if (is_function(token) && lastToken == ")") { secondPassResult << "*"; @@ -260,7 +257,6 @@ auto ImplicitMultiplication(std::stringstream&& sstr) -> std::string lastToken = token; } - printf("\n"); return secondPassResult.str(); } @@ -280,7 +276,6 @@ auto FromInFix(const std::string& str, ParseImaginaryOption option) -> std::expe std::stringstream ss { str }; while (ss >> token) { - printf("token: %s\n", token.c_str()); // Operand if (auto newNumber = is_number(token); newNumber) { st.push(std::make_unique(newNumber.value()));