添加项目文件。
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.14.36127.28 d17.14
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ScientificCalculator", "ScientificCalculator\ScientificCalculator.vcxproj", "{5C991D9D-797E-4C35-8C97-E6F43099CBD6}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{5C991D9D-797E-4C35-8C97-E6F43099CBD6}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{5C991D9D-797E-4C35-8C97-E6F43099CBD6}.Debug|x64.Build.0 = Debug|x64
|
||||
{5C991D9D-797E-4C35-8C97-E6F43099CBD6}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{5C991D9D-797E-4C35-8C97-E6F43099CBD6}.Debug|x86.Build.0 = Debug|Win32
|
||||
{5C991D9D-797E-4C35-8C97-E6F43099CBD6}.Release|x64.ActiveCfg = Release|x64
|
||||
{5C991D9D-797E-4C35-8C97-E6F43099CBD6}.Release|x64.Build.0 = Release|x64
|
||||
{5C991D9D-797E-4C35-8C97-E6F43099CBD6}.Release|x86.ActiveCfg = Release|Win32
|
||||
{5C991D9D-797E-4C35-8C97-E6F43099CBD6}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {227C30D6-2F25-4EA4-B670-3C930B28DA7B}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,44 @@
|
||||
#include "Calc.h"
|
||||
#include <iostream>
|
||||
|
||||
// 静态成员初始化
|
||||
Tokenizer Calc::tokenizer;
|
||||
InfixToPostfix Calc::converter;
|
||||
PostfixEval Calc::evaluator;
|
||||
|
||||
// 主计算接口:表达式字符串 -> 计算结果
|
||||
double Calc::eval(const string& expression) {
|
||||
vector<Token> tokens = tokenizer.run(expression);
|
||||
vector<Token> postfix = converter.run(tokens);
|
||||
return evaluator.run(postfix);
|
||||
}
|
||||
|
||||
// 安全计算接口:带 try-catch 封装,返回 bool 标志
|
||||
bool Calc::safeEval(const string& expr, double& res) {
|
||||
try {
|
||||
res = eval(expr);
|
||||
return true;
|
||||
}
|
||||
catch (const exception& e) {
|
||||
cerr << "[Error] " << e.what() << endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 调试接口:打印中间结果(token 列表 和 后缀表达式)
|
||||
void Calc::debug(const string& expr) {
|
||||
try {
|
||||
vector<Token> tokens = tokenizer.run(expr);
|
||||
cout << "[Token List]" << endl;
|
||||
for (const auto& t : tokens) cout << t.value << " ";
|
||||
cout << endl;
|
||||
|
||||
vector<Token> postfix = converter.run(tokens);
|
||||
cout << "[Postfix]" << endl;
|
||||
for (const auto& t : postfix) cout << t.value << " ";
|
||||
cout << endl;
|
||||
}
|
||||
catch (const exception& e) {
|
||||
cerr << "[Debug Error] " << e.what() << endl;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef CALC_H
|
||||
#define CALC_H
|
||||
|
||||
#include"Tokenizer.h"
|
||||
#include"InfixToPostfix.h"
|
||||
#include"PostfixEval.h"
|
||||
#include<optional>
|
||||
#include<unordered_map>
|
||||
|
||||
using namespace std;
|
||||
|
||||
class Calc {
|
||||
public:
|
||||
//主接口函数:输入表达式字符串,返回计算结果
|
||||
static double eval(const string& expresssion);
|
||||
|
||||
//安全接口:带异常处理,返回optional
|
||||
static bool safeEval(const string& expr,double& res);
|
||||
|
||||
//调试函数:打印token列表与后缀表达式
|
||||
static void debug(const string& expr);
|
||||
private:
|
||||
static Tokenizer tokenizer;
|
||||
static InfixToPostfix converter;
|
||||
static PostfixEval evaluator;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,74 @@
|
||||
#include "InfixToPostfix.h"
|
||||
#include <stdexcept>
|
||||
|
||||
//主函数:使用逆波兰表示法将中缀表达式转换为后缀表达式
|
||||
vector<Token> InfixToPostfix::run(const vector<Token>& infix) {
|
||||
vector<Token> output;// 运算符栈
|
||||
stack<Token> opStack;//输出后缀表达式
|
||||
|
||||
for (const Token& token : infix) {
|
||||
if (token.is_num() || token.is_var())
|
||||
//数字或变量直接放入输出队列
|
||||
output.push_back(token);
|
||||
else if (token.is_fun())
|
||||
//函数压入运算符栈(函数优先级最高)
|
||||
opStack.push(token);
|
||||
else if (token.is_ope()) {
|
||||
//遇到运算符时,弹出栈中优先级更高或相同且结合性为左结合的运算符
|
||||
while (!opStack.empty() && opStack.top().is_ope() &&
|
||||
(precedence(opStack.top()) > precedence(token) ||
|
||||
(precedence(opStack.top()) == precedence(token) && associativity(token) == Associativity::Left))) {
|
||||
output.push_back(opStack.top());
|
||||
opStack.pop();
|
||||
}
|
||||
//当前运算符入栈
|
||||
opStack.push(token);
|
||||
}
|
||||
else if (token.is_LP())
|
||||
//遇到左括号,直接入栈
|
||||
opStack.push(token);
|
||||
else if (token.is_RP())
|
||||
//遇到右括号,弹出栈中运算符直到遇到左括号
|
||||
pop_ULP(opStack, output);
|
||||
}
|
||||
|
||||
//遍历完输入后,弹出栈中所有运算符
|
||||
while (!opStack.empty()) {
|
||||
if (opStack.top().is_LP() || opStack.top().is_RP())
|
||||
throw runtime_error("括号不匹配");//左右括号不匹配时弹出错误
|
||||
output.push_back(opStack.top());
|
||||
opStack.pop();
|
||||
}
|
||||
|
||||
return output;//返回后缀表达式序列
|
||||
}
|
||||
|
||||
//返回运算符优先级,数字越大则优先级越高
|
||||
int InfixToPostfix::precedence(const Token& token) const {
|
||||
return token.precedence;
|
||||
}
|
||||
|
||||
//返回运算符结核性,左/右
|
||||
Associativity InfixToPostfix::associativity(const Token& token) const {
|
||||
return token.associativity;
|
||||
}
|
||||
|
||||
//处理右括号,弹出栈中运算符知道遇到左括号
|
||||
void InfixToPostfix::pop_ULP(stack<Token>& opStack, vector<Token>& output) {
|
||||
//弹出直到遇到左括号
|
||||
while (!opStack.empty() && !opStack.top().is_LP()) {
|
||||
output.push_back(opStack.top());
|
||||
opStack.pop();
|
||||
}
|
||||
|
||||
//如果为遇到左括号则抛出异常
|
||||
if (opStack.empty())
|
||||
throw runtime_error("括号不匹配:缺失左括号");
|
||||
opStack.pop(); // 弹出左括号
|
||||
|
||||
//如果左括号之前是函数,则函数也出栈并加入输出
|
||||
if (!opStack.empty() && opStack.top().is_fun()) {
|
||||
output.push_back(opStack.top());
|
||||
opStack.pop();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef INFIX_TO_POSTFIX_H
|
||||
#define INFIX_TO_POSTFIX_H
|
||||
|
||||
#include"Token.h"
|
||||
#include<vector>
|
||||
#include<stack>
|
||||
|
||||
using namespace std;
|
||||
|
||||
class InfixToPostfix {
|
||||
public:
|
||||
//主转换函数:将中缀表达式转换为后缀表达式
|
||||
vector<Token> run(const vector<Token>& infix);
|
||||
|
||||
private:
|
||||
//返回指定运算符的优先级
|
||||
int precedence(const Token& token) const;
|
||||
|
||||
//判断运算符的结合性(左/右)
|
||||
Associativity associativity(const Token& token) const;
|
||||
|
||||
//遇到做右括号时,从栈中弹出操作符直到遇到左括号
|
||||
void pop_ULP(stack<Token>& opStack, vector<Token>& output);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,97 @@
|
||||
#include"PostfixEval.h"
|
||||
#include<algorithm>
|
||||
#include<cctype>
|
||||
|
||||
double PostfixEval::run(const std::vector<Token>& postfix, const unordered_map<string, double>& vars) {
|
||||
stack<double> stk;
|
||||
|
||||
for (const auto& token : postfix) {
|
||||
if (token.is_num())
|
||||
//数字转换为double入栈
|
||||
stk.push(stod(token.value));
|
||||
else if (token.is_var()) {
|
||||
//查找变量映射表
|
||||
auto it = vars.find(token.value);
|
||||
// 支持内置常量 PAI 和 e
|
||||
|
||||
if (it == vars.end()) {
|
||||
if (token.value == "PAI")
|
||||
stk.push(2.0 * std::asin(1.0)); // π = 2 * arcsin(1)
|
||||
else if (token.value == "e")
|
||||
stk.push(std::exp(1.0)); // e = exp(1)
|
||||
else
|
||||
throw runtime_error("变量未定义:" + token.value);
|
||||
}
|
||||
else stk.push(it->second);
|
||||
}
|
||||
else if (token.is_ope()) {
|
||||
//取出两个操作数,执行二元运算
|
||||
if (stk.size() < 2) throw runtime_error("操作数不足");
|
||||
double b = stk.top();
|
||||
stk.pop();
|
||||
double a = stk.top();
|
||||
stk.pop();
|
||||
double res = apply_BO(token.value, a, b);
|
||||
stk.push(res);
|
||||
}
|
||||
else if (token.is_fun()) {
|
||||
//取出一个操作数,执行一元基本初等函数的计算
|
||||
if (stk.empty()) throw runtime_error("操作数不足");
|
||||
double x = stk.top();
|
||||
stk.pop();
|
||||
double res = apply_UF(token.value, x);
|
||||
stk.push(res);
|
||||
}
|
||||
else
|
||||
throw runtime_error("未知Token类型");
|
||||
}
|
||||
|
||||
if (stk.size() != 1)
|
||||
throw runtime_error("表达式错误,计算结果栈不唯一");
|
||||
|
||||
return stk.top();
|
||||
}
|
||||
|
||||
//进行二元运算符的运算
|
||||
double PostfixEval::apply_BO(const string& op, double a, double b) {
|
||||
if (op == "+") return a + b;
|
||||
if (op == "-") return a - b;
|
||||
if (op == "*") return a * b;
|
||||
if (op == "/") {
|
||||
if (b == 0) throw runtime_error("除数不能为0");
|
||||
return a / b;
|
||||
}
|
||||
if (op == "^") return pow(a, b);
|
||||
if (op == "%") {
|
||||
if (b == 0) throw runtime_error("模除数不能为零");
|
||||
return std::fmod(a, b);
|
||||
}
|
||||
throw runtime_error("未知运算符:" + op);
|
||||
}
|
||||
|
||||
//进行一元基本初等函数的运算
|
||||
double PostfixEval::apply_UF(const string& func, double x) {
|
||||
string f = func;
|
||||
transform(f.begin(), f.end(), f.begin(), ::tolower);
|
||||
|
||||
if (f == "sin") return sin(x);
|
||||
if (f == "cos") return cos(x);
|
||||
if (f == "tan") return tan(x);
|
||||
if (f == "sqrt") {
|
||||
if (x < 0)
|
||||
throw runtime_error("sqrt参数不能为负");
|
||||
return sqrt(x);
|
||||
}
|
||||
if (f == "log") {
|
||||
if (x <= 0)
|
||||
throw runtime_error("log参数必须大于0");
|
||||
return log(x);
|
||||
}
|
||||
if (f == "exp") return exp(x);
|
||||
if (f == "abs") return fabs(x);
|
||||
if (f == "asin") return asin(x);
|
||||
if (f == "acos") return acos(x);
|
||||
if (f == "atan") return atan(x);
|
||||
|
||||
throw runtime_error("未知函数:" + func);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef POSTFIXEVAL_H
|
||||
#define POSYFIXEVAL_H
|
||||
|
||||
#include "Token.h"
|
||||
#include <vector>
|
||||
#include <stack>
|
||||
#include <cmath>
|
||||
#include <stdexcept>
|
||||
#include<unordered_map>
|
||||
|
||||
using namespace std;
|
||||
class PostfixEval {
|
||||
public:
|
||||
//主函数:对后缀表达式进行求值
|
||||
double run(const std::vector<Token>& postfix, const unordered_map<string, double>& vars = {});
|
||||
|
||||
private:
|
||||
//应用于二元操作符,如+,-,*,/,^
|
||||
double apply_BO(const string& op, double a, double b);
|
||||
|
||||
//应用于医院基本初等函数,如sin, cos, log
|
||||
double apply_UF(const string& func, double x);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,142 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>17.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{5c991d9d-797e-4c35-8c97-e6f43099cbd6}</ProjectGuid>
|
||||
<RootNamespace>ScientificCalculator</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Calc.h" />
|
||||
<ClInclude Include="InfixToPostfix.h" />
|
||||
<ClInclude Include="PostfixEval.h" />
|
||||
<ClInclude Include="Token.h" />
|
||||
<ClInclude Include="Tokenizer.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Calc.cpp" />
|
||||
<ClCompile Include="InfixToPostfix.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="PostfixEval.cpp" />
|
||||
<ClCompile Include="Tokenizer.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="源文件">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="头文件">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="资源文件">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Tokenizer.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="InfixToPostfix.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="PostfixEval.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Calc.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="main.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Token.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Tokenizer.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="InfixToPostfix.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="PostfixEval.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Calc.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,52 @@
|
||||
#ifndef TOKEN_H
|
||||
#define TOKEN_H
|
||||
|
||||
#include<string>
|
||||
|
||||
using namespace std;
|
||||
//记号类型
|
||||
enum class TokenType {
|
||||
Number,
|
||||
Operator,
|
||||
LeftParen,
|
||||
RightParen,
|
||||
Function,
|
||||
Variable
|
||||
};
|
||||
|
||||
//运算符结合性
|
||||
enum class Associativity {
|
||||
Left,
|
||||
Right,
|
||||
None
|
||||
};
|
||||
|
||||
//Token结构体
|
||||
struct Token {
|
||||
TokenType type;//记号类型
|
||||
string value; //实际字符串
|
||||
int precedence = -1; //优先级,仅对运算符和函数有效
|
||||
Associativity associativity = Associativity::None; //结合性,仅对运算符和函数有效
|
||||
|
||||
//构造函数
|
||||
Token(TokenType t, const string& v,
|
||||
int p = -1, Associativity assoc = Associativity::None)
|
||||
:type(t), value(v), precedence(p), associativity(assoc) {}
|
||||
|
||||
//辅助判断方法
|
||||
|
||||
// 判断是否为数字(例如 "3.14", "42")
|
||||
bool is_num() const { return type == TokenType::Number; }
|
||||
// 判断是否为运算符(例如 "+", "-", "*", "/")
|
||||
bool is_ope() const { return type == TokenType::Operator; }
|
||||
// 判断是否为数学函数(例如 "sin", "cos", "log")
|
||||
bool is_fun() const { return type == TokenType::Function; }
|
||||
// 判断是否为左括号 "(",用于表达式分组
|
||||
bool is_LP() const { return type == TokenType::LeftParen; }
|
||||
// 判断是否为右括号 ")",用于表达式分组结束
|
||||
bool is_RP() const { return type == TokenType::RightParen; }
|
||||
// 判断是否为变量(如 "x", "y",可扩展支持符号代数)
|
||||
bool is_var()const { return type == TokenType::Variable; }
|
||||
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,115 @@
|
||||
#include"Tokenizer.h"
|
||||
#include<cctype>
|
||||
#include<stdexcept>
|
||||
|
||||
using namespace std;
|
||||
|
||||
vector<Token> Tokenizer::run(const string& expr) {
|
||||
expression = expr;
|
||||
pos = 0;
|
||||
vector<Token> tokens;
|
||||
|
||||
// 主循环:逐字符读取表达式
|
||||
while (pos < expression.length()) {
|
||||
skipWhitespace();// 跳过空白字符
|
||||
if (pos >= expression.length()) break;
|
||||
|
||||
char current = expression[pos];
|
||||
|
||||
// 如果是数字或一元负号开头,读取完整数字
|
||||
if (is_dig(current) || (current == '-' && is_UMC(tokens)))
|
||||
tokens.push_back(readNumber());
|
||||
|
||||
// 如果是字母,读取函数或变量名
|
||||
else if (is_let(current))
|
||||
tokens.push_back(readIdentifier());
|
||||
|
||||
// 否则读取操作符或括号
|
||||
else
|
||||
tokens.push_back(readOperatorOrParen());
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
void Tokenizer::skipWhitespace() {
|
||||
// 跳过所有空格、制表符等空白字符
|
||||
while (pos < expression.length() && isspace(expression[pos]))
|
||||
++pos;
|
||||
}
|
||||
|
||||
Token Tokenizer::readNumber() {
|
||||
size_t start = pos;
|
||||
bool hasDecimal = false;// 标记小数点是否出现过
|
||||
|
||||
if (expression[pos] == '-')
|
||||
++pos; // 若是一元负号,先跳过
|
||||
|
||||
while (pos < expression.length() && (is_dig(expression[pos]) || expression[pos] == '.')) {
|
||||
if (expression[pos] == '.') {
|
||||
if (hasDecimal)
|
||||
throw runtime_error("数字格式无效:多个小数点");// 小数点重复错误
|
||||
hasDecimal = true;
|
||||
}
|
||||
++pos;
|
||||
}
|
||||
|
||||
string numberStr = expression.substr(start, pos - start);
|
||||
return Token(TokenType::Number, numberStr);// 构造数字 Token
|
||||
}
|
||||
|
||||
Token Tokenizer::readIdentifier() {
|
||||
size_t start = pos;
|
||||
|
||||
// 读取连续的字母字符(函数名或变量名)
|
||||
while (pos < expression.length() && is_let(expression[pos]))
|
||||
++pos;
|
||||
|
||||
string name = expression.substr(start, pos - start);
|
||||
|
||||
//识别函数与变量(若后面紧跟‘(’则为函数,否则为变量)
|
||||
skipWhitespace();
|
||||
if (pos < expression.length() && expression[pos] == '(')
|
||||
return Token(TokenType::Function, name);
|
||||
else
|
||||
return Token(TokenType::Variable, name);
|
||||
}
|
||||
|
||||
Token Tokenizer::readOperatorOrParen() {
|
||||
char c = expression[pos++]; // 读取当前字符并自增指针
|
||||
|
||||
// 判断字符类型并创建相应 Token
|
||||
switch (c) {
|
||||
case '+': return Token(TokenType::Operator, "+", 1, Associativity::Left);
|
||||
case '-': return Token(TokenType::Operator, "-", 1, Associativity::Left);
|
||||
case '*': return Token(TokenType::Operator, "*", 2, Associativity::Left);
|
||||
case '/': return Token(TokenType::Operator, "/", 2, Associativity::Left);
|
||||
case '^': return Token(TokenType::Operator, "^", 3, Associativity::Right);
|
||||
case '(': return Token(TokenType::LeftParen, "(");
|
||||
case ')': return Token(TokenType::RightParen, ")");
|
||||
default:
|
||||
throw runtime_error(string("未知符号 ") + c); // 未知字符错误
|
||||
}
|
||||
}
|
||||
|
||||
bool Tokenizer::is_UMC(const vector<Token>& tokens) {
|
||||
// 如果是表达式开头,或前一个是运算符或左括号,则当前负号为一元
|
||||
if (tokens.empty()) return true;
|
||||
Token last = tokens.back();
|
||||
return last.is_ope() || last.is_LP();
|
||||
}
|
||||
|
||||
bool Tokenizer::is_dig(char c) const {
|
||||
// 判断是否为数字字符
|
||||
return std::isdigit(static_cast<unsigned char>(c));
|
||||
}
|
||||
|
||||
bool Tokenizer::is_let(char c) const {
|
||||
// 判断是否为英文字母
|
||||
return std::isalpha(static_cast<unsigned char>(c));
|
||||
}
|
||||
|
||||
bool Tokenizer::is_OpeCh(char c) const {
|
||||
// 判断是否为合法的运算符字符
|
||||
return c == '+' || c == '-' || c == '*' || c == '/' || c == '^';
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#ifndef TOKENIZER_H
|
||||
#define TOKENIZER_H
|
||||
|
||||
#include<string>
|
||||
#include<vector>
|
||||
#include"Token.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
class Tokenizer {
|
||||
public:
|
||||
//主解析函数:将表达式字符串转换为Token序列
|
||||
vector<Token> run(const string& expr);
|
||||
|
||||
private:
|
||||
string expression; //当前处理的表达式
|
||||
size_t pos = 0; //当前指针位置
|
||||
|
||||
//工具函数:跳过空格字符
|
||||
void skipWhitespace();
|
||||
|
||||
//读取一个完整数字(含小数、小数点、可能的一元负号)
|
||||
Token readNumber();
|
||||
|
||||
//读取函数名(如sin、cos、log等)或变量名(如x、y)
|
||||
Token readIdentifier();
|
||||
|
||||
//读取运算符或括号
|
||||
Token readOperatorOrParen();
|
||||
|
||||
//判断当前位置是否处于一元符号的上下文
|
||||
bool is_UMC(const vector<Token>& tokens);
|
||||
|
||||
//字符判断辅助函数
|
||||
bool is_dig(char c)const;
|
||||
bool is_let(char c)const;
|
||||
bool is_OpeCh(char c)const;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,29 @@
|
||||
#include <iostream>
|
||||
#include<string>
|
||||
#include "Calc.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main() {
|
||||
string input;
|
||||
cout << "科学计算器 (输入 q 退出):\n";
|
||||
|
||||
while (true) {
|
||||
cout << "表达式 > ";
|
||||
getline(cin, input);
|
||||
|
||||
if (input == "q" || input == "quit")
|
||||
break;
|
||||
|
||||
double result;
|
||||
if (Calc::safeEval(input, result)) {
|
||||
cout << "= " << result << endl;
|
||||
}
|
||||
else {
|
||||
cout << "[错误] 表达式无法计算\n";
|
||||
}
|
||||
}
|
||||
|
||||
cout << "感谢使用,再见!" << endl;
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user