// here we can write directly our algorithm so as to generate a track
inlinechar algorithm(int t)
{
return (~t >> 2) * ((127 & t * (7 & t >> 10)) < (245 & t * (2 + (5 & t >> 14))));
}
Hello. I have this function which computes t according to bitwise operations and arithmetic computation. Sometimes this single line of code is really complex. However I am trying to parse a string (an input) in order to get those operands and values. I found some parsers on GitHub so as to do this work, but I would like to do mine. Do you have an (simple) idea in order to parse a string to a lot of operands and integers? Spliting a string is easy, but how could I "translate" vector elements to operands keeping the previous result computation and respecting brackets? Just a first step with one operand like >> so that I can do the others? Thank you for your help ++
This expression is in what is called infix notation. To evaluate it needs to be converted into what is called postfix notation. The main 2 methods for doing that are Shunting Yard and Recursive Descent. There's loads of info/code on the internet to do this. eg given an infix expression of 1 + (2 * 3), the postfix expression would be 1 2 3 * +. This takes into account operator precedence and removes all brackets. Evaluating this postfix expression is quite simple using a stack.
Thank you @seeplus for the good explanation. I found some good recursive descent codes on the web which allow me to reach what I am trying to do. This code is a first approach and it works well as expected. It uses only simple mathematical operations and brackets, but it is a good example how it should be ++
Note L164. This will never be executed due to the return statement on L162!
Also, why use dynamic memory here?
You don't need removeSpaces() as white-space can be handled easily with tokenize(). Also it would the code slightly easier if tokenize() returned tokens rather than having it as a param.
For calculate(), shouldn't s be passed by const ref and not by value?
It uses only simple mathematical operations and brackets
Yes - but with no expression error reporting. In practice any invalid expression would be reported. Also, it doesn't deal with unary + or -.
factor() changed to allow unary + and -. Also to deal with decimal numbers (but not scientific notation) and to enable type of result to be specified. Again, without any error reporting!:
#include <string>
#include <iostream>
#include <list>
#include <sstream>
#include <cctype>
std::list<std::string> tokenize(const std::string& str) {
std::string num;
std::list<std::string> tokens;
for (unsignedchar c : str) {
if (std::isspace(c))
continue;
if (std::isdigit(c) || c == '.')
num += c;
else {
if (!num.empty()) {
tokens.push_back(num);
num.clear();
}
tokens.emplace_back(1, c);
}
}
if (!num.empty()) {
tokens.push_back(num);
num.clear();
}
return tokens;
}
template <typename T = int>
class Calculator {
public:
Calculator(const std::string& expression);
T exp();
private:
std::list<std::string> mTokens;
std::string mCurrent;
void next();
T term();
T factor();
T toNum(const std::string& s) const;
};
template <typename T>
Calculator<T>::Calculator(const std::string& expression) : mTokens(tokenize(expression)), mCurrent(mTokens.front()) {}
template <typename T>
void Calculator<T>::next() {
mTokens.pop_front();
mCurrent = !mTokens.empty() ? mTokens.front() : std::string {};
}
template <typename T>
T Calculator<T>::exp() {
auto result { term() };
while (mCurrent == "+" || mCurrent == "-") {
constauto cur { mCurrent };
next();
result += (cur == "+") ? term() : -term();
}
return result;
}
template <typename T>
T Calculator<T>::term() {
auto result { factor() };
while (mCurrent == "*" || mCurrent == "/") {
if (constauto cur { mCurrent }; next(), cur == "*")
result *= factor();
else {
constauto denominator { factor() };
result = denominator != 0 ? result / denominator : 0;
}
}
return result;
}
template <typename T>
T Calculator<T>::factor() {
T result {};
bool neg {};
if (mCurrent == "+" || mCurrent == "-") {
neg = mCurrent == "-";
next();
}
if (mCurrent == "(") {
next();
result = exp();
} else
result = toNum(mCurrent);
next();
return neg ? -result : result;
}
template <typename T>
T Calculator<T>::toNum(const std::string& s) const {
T x;
std::stringstream { s } >> x;
return x;
}
template<typename T = int>
T calculate(const std::string& s) {
return Calculator<T> { s }.exp();
}
int main() {
const std::string expression1 { "(7 * (2 + 3) - 5) / 2" };
const std::string expression2 { "-(-5.1--6.4)" };
std::cout << expression1 << " = " << calculate(expression1) << '\n';
std::cout << expression2 << " = " << calculate<double>(expression2) << '\n';
}
What other operators do you want to add? bit &, | are fairly easy and so is ^ (for power instead of x-or in c/c++). Adding a 2-char operator (eg <<, >> for bit shift) is a bit more tricky...
Really good script - especially the last one. This evening I was working on this script including some features, but yours is better and more readable than mine. To answer to your question, I want to include in this equation all bitwise operands like >> << & ~ ^ % and |. Also I would like to implement a ternary operator (so I will add after bitwise operands ? and : - maybe a little bit more complex). It becomes interesting after some hours...
Thank you for your help ++
In which case you need to recode tokenize() so that it can recognize operands of more than one char. First though, I would recommend adding error detection/reporting to the current code. As I said before, adding new single char operands is fairly easy once you know the operator precedence required. For each different precedence you simply add a new function and have the function calls changed as appropriate.
This implements the bit operators (>>, <<, &, |, ^, ~). As Calculator can now operate on any valid arithmetic type, then special tests have to be made for these (and also %) which only operate on integral types. It also uses C++20 concepts to ensure Calculator only uses an arithmetic type.
Hello @seeplus - I am very impressed how you can quickly code a full script when I have to work on it during a few hours. I reached something more or less similar than yours, but I was not really satisfied by mine. Thank you very much for your kind help. There are many interesting things inside your script - a great C++ lesson for me ++
template <typename T> requires std::is_arithmetic_v<T>
T Calculator<T>::toNum(const std::string& s) const {
T x {};
std::from_chars(s.data(), s.data() + s.size(), x);
return x;
}
Note that it now also requires #include <charconv>
quickly code a full script
I wrote my first parser in Dartmouth Basic over 50 years ago. Since I've coded variations of them in Fortran, Pascal, Algol68, various Assemblers and other basics, c and C++ ....