保证程序健壮性和可读性以及执行效率,在任何输入下都能得到合适处理,现代cpp编写
支持二元操作
& and
| or
^ xor
+ plus
- minis
* multiply
/ divide
% module
一元运算
- opposite 相反数
~ not 按位取反
可以报出的异常包括:
括号不匹配
出现无效字符
操作数和运算符的相对位置有误
除零错误
等等
测试样例
完整代码
#include <string>
#include <string_view>
#include <sstream>
#include <iostream>
#include <stack>
#include <cctype>
#include <concepts>
#include <functional>
#include <unordered_map>
#include <ranges>
#include <optional>
#include <cstdlib>
#include <iomanip>
constexpr std::string_view PAREN { "()[]{}<>" };
constexpr std::string_view BINARY { "&|^+-*/%" };
std::unordered_map<char, std::function<long(long, long)>> f {
{'&', std::bit_and<long>()},
{'|', std::bit_or<long>()},
{'^', std::bit_xor<long>()},
{'+', std::plus<long>()},
{'-', std::minus<long>()},
{'*', std::multiplies<long>()},
{'/', std::function<long(long, long)>([](long v1, long v2){
if(!v2){
std::cerr << "divides by 0\n";
std::exit(0);
}
return std::divides<long>()(v1, v2);
})},
{'%', std::modulus<long>()},
};
template <typename Tp>
Tp get(std::stack<Tp>& Stk, std::istringstream& Vs) try{
if(Stk.empty()) throw Vs.tellg();
Tp t = Stk.top(); Stk.pop();
return t;
}catch(std::istringstream::pos_type indicator){
std::cerr << "stack top-get failed at position indicator:\t"
<< indicator - 2 << '\n';
std::exit(0);
}
bool alpha_validate(std::string_view expr){
for(auto alpha : expr)
if(!(alpha == ' ')
&& !std::isdigit(alpha)
&& BINARY.find(alpha) == std::string_view::npos
&& PAREN.find(alpha) == std::string_view::npos ){
std::cerr << "invalid alpha:\t" << alpha << '\n';
return false;
}
return true;
}
bool parenthesis_validate(std::string_view expr){
std::stack<std::string_view::size_type> parenStk;
for(auto index : expr
| std::views::filter([](char alpha)
{ return !(PAREN.find(alpha) == std::string_view::npos); })
| std::views::transform([](char alpha)
{ return PAREN.find(alpha); } )
){
if(!(index & 1U)){
parenStk.push(index);
continue;
}
if(!(index == parenStk.top() + 1)){
std::cerr << "incompatible parenthesis:"
<< PAREN[parenStk.top()]
<< " & "
<< PAREN[index]
<<"\n";
return false;
}
parenStk.pop();
}
return parenStk.empty();
}
inline long extract(std::istringstream& Vs) try{
long opd {}; char alpha {};
if(!(Vs && std::isdigit(Vs.peek()))) throw Vs.tellg();
do{
Vs.get(alpha);
opd = opd * 10 + (alpha - '0');
}while(Vs && std::isdigit(Vs.peek()));
return opd;
}catch(std::istringstream::pos_type indicator){
std::cerr << "extract failed at position indicator:\t"
<< indicator - 2<< '\n';
std::exit(0);
}
long calculate(std::istringstream& Vs){
std::stack<long> opdStk;
std::stack<char> oprStk;
char alpha {};
while(Vs >> alpha){
if(alpha == ' ') continue;
if(std::isdigit(alpha) && Vs.unget()){
opdStk.push(extract(Vs));
continue;
}
auto oindex { BINARY.find(alpha) };
auto pindex { PAREN.find(alpha) };
if(!(oindex == std::string_view::npos)){
if(alpha == '-' && opdStk.empty()){
Vs.get(alpha);
if(alpha == '>'){
std::cerr << "unary operator '-' meets no operand\n";
std::exit(0);
}
opdStk.push(std::negate<long>()(
std::isdigit(alpha) && Vs.unget() ?
extract(Vs) :
calculate(Vs) ) );
}
else{
while(!oprStk.empty() && oindex < BINARY.find(oprStk.top()))
opdStk.push(f[get(oprStk, Vs)]
(get(opdStk, Vs), get(opdStk, Vs)));
oprStk.push(alpha);
}
continue;
}
if(!(pindex & 1U)){
opdStk.push(calculate(Vs));
continue;
}
while(!oprStk.empty())
opdStk.push(f[get(oprStk, Vs)]
(get(opdStk, Vs), get(opdStk, Vs)));
return opdStk.top();
}
return opdStk.top();
}
[[discard]] std::optional<long>
calculate(std::string_view expr) noexcept{
if(!alpha_validate(expr)
|| !parenthesis_validate(expr))
return std::nullopt;
std::string __expr__(expr);
__expr__ = '<' + __expr__ + '>';
std::istringstream Vs(__expr__);
return calculate(Vs);
}
void test(std::string_view expr, bool binary = false){
auto result { calculate(expr) };
if(result){
std::cout << expr << " = ";
if(binary) std::cout << "0x" << std::setfill('0') << std::setw(5) << std::hex;
std::cout << *result
<< "\n";
}
endl(std::cout);
}
int main(void){
/*
std::string expr;
std::getline(std::cin, expr);
*/
test("{1+2*3-4/2}");
test("[-3+8*(4-2)/4]+{-5%3}");
test("{-(4+3)*6+8-2}/[2+(-5)]");
test("{4+2[3-2*5}");
test("(5^3 + 98|56^(43-2))", true);
test("{(3-8)*(2+[4-2]/3+1}");
test("1/0");
return 0;
}