添加项目文件。

This commit is contained in:
KiriAky
2025-07-01 16:04:35 +08:00
parent 27b960a881
commit 0f8e7d3282
13 changed files with 755 additions and 0 deletions
+31
View File
@@ -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
+44
View File
@@ -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;
}
}
+28
View File
@@ -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
+74
View File
@@ -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();
}
}
+27
View File
@@ -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
+97
View File
@@ -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);
}
+25
View File
@@ -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>
+52
View File
@@ -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
+115
View File
@@ -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 == '^';
}
+40
View File
@@ -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
+29
View File
@@ -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;
}