Created
September 4, 2014 13:03
-
-
Save hirosof/53fd99c2003a842bb938 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <iomanip> | |
#include <cstdio> | |
#include <sstream> | |
#include <cmath> | |
#include <locale> | |
#include "../CHSStack.hpp" /*自作テンプレート・可変長スタッククラス*/ | |
#include "../CHSLinearList.hpp" /*自作テンプレート・線形リストクラス*/ | |
#include "../CHSSimpleLogger/CHSSimpleLogger.h" /*自作ログ出力クラス*/ | |
using namespace std; | |
#pragma warning(disable:4996) | |
#ifdef __SHOW_DETAILS__ | |
#define VIEW_INSIDE_OPERATION | |
#define VIEW_INVERSE_PORLAND | |
#endif | |
struct FormulaData { | |
bool OperatorFlag; | |
union { | |
double Number; | |
char Operator; | |
}data; | |
}; | |
typedef CHSLinearList<FormulaData> FormulaDataList; | |
int GetPriorityOperatorLevel(char Ope);//演算子レベルを取得 | |
bool ParseFormula(FormulaDataList *pOutList); //数式を解析 | |
bool CalcFormula(FormulaDataList *pInList, double *pResult); //数式を計算する | |
void ShowDataListAddData(FormulaData fd); | |
char Formula[1025]; //数式データ | |
CHSSimpleLogger Log; | |
int main(void) { | |
FormulaDataList formList; | |
stringstream ss; | |
double Total = 0; | |
setlocale(LC_ALL, "Japanese"); | |
#ifdef __SHOW_DETAILS__ | |
Log.Create(TEXT("FormulaCalcTest05DetailsView_LastRunData.txt")); | |
SetConsoleTitleA("シンプル数式電卓(FormulaCalcTest05) [詳細表示モード]"); | |
#else | |
Log.Create(TEXT("FormulaCalcTest05_LastRunData.txt")); | |
SetConsoleTitleA("シンプル数式電卓(FormulaCalcTest05)"); | |
#endif | |
while (1) { | |
printf("<<1024バイト以内の数式を入力してください。>>\n[補足説明:';'→[Enter]入力で数式入力終了、quitでプログラム終了]\n"); | |
Formula[0] = 0; | |
scanf("%1024[^;]", Formula); | |
fflush(stdin); | |
if (strncmp(Formula, "quit", 4) == 0) { | |
//printf("計算結果の合計:%.8f\n" , Total); | |
break; | |
} | |
//printf("\n<<入力された数式>>\n%s\n\n", Formula); | |
#if defined(VIEW_INVERSE_PORLAND) || defined(VIEW_INSIDE_OPERATION) | |
printf("\n"); | |
#endif | |
if (ParseFormula(&formList)) { | |
int Nums = formList.GetNums(); | |
if (Nums) { | |
#ifdef VIEW_INVERSE_PORLAND | |
//逆ポーランド記法の出力 | |
FormulaData Data; | |
formList.Get(0, &Data); | |
#ifdef VIEW_INSIDE_OPERATION | |
printf("\n"); | |
#endif | |
printf("<<逆ポーランド記法>>\n"); | |
for (int i = 0; i < Nums; i++) { | |
if (formList.Get(i, &Data)) { | |
if (Data.OperatorFlag) { | |
printf("%c ", Data.data.Operator); | |
} else { | |
cout << Data.data.Number << ' '; | |
} | |
} | |
} | |
printf("\n"); | |
#endif | |
double res; | |
bool bret = CalcFormula(&formList, &res); | |
if (bret) Total += res; | |
ss.str(""); | |
ss << Formula; | |
#ifdef _UNICODE | |
Log.WriteWithTitle(TEXT("数式入力"),TEXT("%S"), ss.str().c_str()); | |
#else | |
Log.WriteWithTitle(TEXT("数式入力"), TEXT("%s"), ss.str().c_str()); | |
#endif | |
ss.str(""); | |
#if defined(VIEW_INVERSE_PORLAND) || defined(VIEW_INSIDE_OPERATION) | |
if (bret) { | |
printf("\n<<計算結果>>\n"); | |
cout << fixed << res << "(" << scientific << res << ")\n" <<endl; | |
cout.unsetf(ios_base::fixed); | |
cout.unsetf(ios_base::scientific); | |
ss << fixed << res; | |
} else { | |
printf("\n<<計算結果>>\n"); | |
printf("計算エラー\n\n"); | |
ss << "計算エラー"; | |
} | |
#else | |
if (bret) { | |
cout <<"= " << fixed << res << "\n" << endl; | |
ss << fixed << res; | |
} else { | |
printf("= 計算エラー\n\n"); | |
ss << "計算エラー"; | |
} | |
#endif | |
#ifdef _UNICODE | |
Log.WriteWithTitle(TEXT("計算結果"), TEXT("= %S"), ss.str().c_str()); | |
#else | |
Log.WriteWithTitle(TEXT("計算結果"), TEXT("= %s"), ss.str().c_str()); | |
#endif | |
} else { | |
printf("式データが空です。\n\n"); | |
} | |
} else { | |
printf("解析エラー\n\n"); | |
} | |
} | |
return 0; | |
} | |
int GetPriorityOperatorLevel(char Ope) { | |
switch (Ope) { | |
case '+': | |
case '-': | |
return 100; | |
case '*': | |
case '/': | |
return 200; | |
case '^': | |
return 300; | |
case '(': | |
case ')': | |
return 400; | |
} | |
return -1; | |
} | |
bool ParseFormula(FormulaDataList *pOutList) { | |
if (!pOutList)return false; | |
pOutList->AllDelete(); | |
size_t length = strlen(Formula); | |
bool bSignMinus = false; | |
double Number = 0; | |
char Current; | |
char OldChar = '\0'; | |
char OldOpe; | |
bool bProcessed = false; | |
bool bFractionFlag = false; | |
double FractionDigit = 1; | |
int Level, OldLevel; | |
CHSStack<char> OpeStack; | |
FormulaData fd; | |
int BracketLevel = 0; | |
bool SignCheckFlag = false; | |
#ifdef VIEW_INSIDE_OPERATION | |
printf("<<数式解析(逆ポーランド記法への変換)の詳細のダンプ>>\n"); | |
#endif | |
for (size_t i = 0; i < length; i++) { | |
Current = Formula[i]; | |
//半角スペースとタブは無視 | |
if ((Current == ' ') || (Current == '\t') || (Current == '\r') || (Current == '\n')) { | |
#ifdef VIEW_INSIDE_OPERATION | |
printf("入力(%d文字目):空白文字または改行文字の為スキップ\n" , i+1 ); | |
#endif | |
continue; | |
} | |
if ((Current == '=') && (i == length - 1))continue; | |
bProcessed = false; | |
#ifdef VIEW_INSIDE_OPERATION | |
printf("入力(%d文字目):%c\n" , i+1 , Current); | |
#endif | |
//符号チェックをするかのフラグ | |
SignCheckFlag = (OldChar == '\0'); | |
SignCheckFlag |= (OldChar == '('); | |
SignCheckFlag |= (OldChar == '*'); | |
SignCheckFlag |= (OldChar == '/'); | |
SignCheckFlag |= (OldChar == '^'); | |
if (SignCheckFlag) { | |
//最初または(の次のデータの場合 +、-(符号) についての処理 | |
bProcessed = true; | |
if (Current == '-') { | |
bSignMinus = true; | |
} else if (Current == '+') { | |
bSignMinus = false; | |
} else { | |
if (((Current >= '0') && (Current <= '9')) == false) { | |
if (Current != '(') { | |
printf("構文エラー:%d文字目の %c は無効な演算子です。\n", i + 1, Current); | |
return false; | |
} | |
} | |
bProcessed = false; | |
} | |
} | |
if (bProcessed == false) { | |
if ((Current >= '0') && (Current <= '9')) { | |
if (OldChar == ')') { | |
//以前の演算子と比べる | |
Level = GetPriorityOperatorLevel('*'); | |
if (OpeStack.Peek(&OldOpe)) OldLevel = GetPriorityOperatorLevel(OldOpe); | |
else OldLevel = -1; | |
while (OldLevel >= Level) { | |
if (OldOpe == '(') break; | |
OpeStack.Pop(&fd.data.Operator); | |
#ifdef VIEW_INSIDE_OPERATION | |
printf("スタックから演算子の %c をポップ\n", fd.data.Operator); | |
#endif | |
fd.OperatorFlag = true; | |
pOutList->Append(fd); | |
#ifdef VIEW_INSIDE_OPERATION | |
ShowDataListAddData(fd); | |
#endif | |
if (OpeStack.Peek(&OldOpe)) OldLevel = GetPriorityOperatorLevel(OldOpe); | |
else OldLevel = -1; | |
} | |
OpeStack.Push('*'); | |
#ifdef VIEW_INSIDE_OPERATION | |
printf("スタックに演算子の %c をプッシュ\n", '*'); | |
#endif | |
} | |
//数値の場合 | |
if (bFractionFlag == false) { | |
Number = (Number * 10) + (9 - ('9' - Current)); | |
} else { | |
FractionDigit /= 10; | |
Number += ((double)(9 - ('9' - Current))) * FractionDigit; | |
} | |
} else { | |
if (Current == '.') { | |
if (bFractionFlag) { | |
printf("構文エラー:%d文字目の %c は無効な小数点です。\n", i + 1, Current); | |
return false; | |
} | |
bFractionFlag = true; | |
FractionDigit = 1; | |
} else { | |
if (((OldChar >= '0') && (OldChar <= '9'))||(OldChar =='.')) { | |
if (bSignMinus) { | |
Number *= -1; | |
bSignMinus = false; | |
} | |
fd.OperatorFlag = false; | |
fd.data.Number = Number; | |
pOutList->Append(fd); | |
#ifdef VIEW_INSIDE_OPERATION | |
ShowDataListAddData(fd); | |
#endif | |
bFractionFlag = false; | |
Number = 0; | |
} | |
//文字(演算子) | |
Level = GetPriorityOperatorLevel(Current); | |
if (OpeStack.Peek(&OldOpe)) OldLevel = GetPriorityOperatorLevel(OldOpe); | |
else OldLevel = -1; | |
if (Level == -1) { | |
printf("構文エラー:%d文字目の %c は無効な演算子です。\n", i + 1, Current); | |
return false; | |
} else { | |
while (OldLevel >= Level) { | |
//printf("pop\n"); | |
if (OldOpe != '(') { | |
OpeStack.Pop(&fd.data.Operator); | |
#ifdef VIEW_INSIDE_OPERATION | |
printf("スタックから演算子の %c をポップ\n", fd.data.Operator); | |
#endif | |
fd.OperatorFlag = true; | |
pOutList->Append(fd); | |
#ifdef VIEW_INSIDE_OPERATION | |
ShowDataListAddData(fd); | |
#endif | |
} else break; | |
if (OpeStack.Peek(&OldOpe)) OldLevel = GetPriorityOperatorLevel(OldOpe); | |
else OldLevel = -1; | |
} | |
if (Current == ')') { | |
if (BracketLevel == 0) { | |
printf("構文エラー:%d文字目の %c は無効です。\n", i + 1, Current); | |
return false; | |
} else { | |
BracketLevel--; | |
do { | |
OldOpe = 0; | |
OpeStack.Pop(&OldOpe); | |
#ifdef VIEW_INSIDE_OPERATION | |
printf("スタックから演算子の %c をポップ\n", OldOpe); | |
#endif | |
if (OldOpe != '(') { | |
fd.data.Operator = OldOpe; | |
fd.OperatorFlag = true; | |
#ifdef VIEW_INSIDE_OPERATION | |
ShowDataListAddData(fd); | |
#endif | |
pOutList->Append(fd); | |
} | |
//printf("A"); | |
} while (OldOpe != '('); | |
} | |
} else { | |
if (Current == '(') { | |
BracketLevel++; | |
//直前が数値または)だったなら * をPush | |
if (((OldChar >= '0') && (OldChar <= '9')) || (OldChar == ')')) { | |
if (OpeStack.Pop(&OldOpe)) { | |
#ifdef VIEW_INSIDE_OPERATION | |
printf("スタックから演算子の %c をポップ\n", OldOpe); | |
#endif | |
if (OldOpe != '^') { | |
//累乗(^)でなければスタックに戻す | |
OpeStack.Push(OldOpe); | |
#ifdef VIEW_INSIDE_OPERATION | |
printf("累乗を示す演算子ではないので、スタックに先ほどポップした演算子の %c を戻す(再プッシュ)\n", OldOpe); | |
#endif | |
} else { | |
//(の前に累乗が計算されるようにする | |
//たとえば 2^3(4+5) = 8(4+5) になるようにする。 | |
fd.data.Operator = OldOpe; | |
fd.OperatorFlag = true; | |
pOutList->Append(fd); | |
#ifdef VIEW_INSIDE_OPERATION | |
ShowDataListAddData(fd); | |
#endif | |
} | |
} | |
OpeStack.Push('*'); | |
#ifdef VIEW_INSIDE_OPERATION | |
printf("スタックに演算子の %c をプッシュ\n", '*'); | |
#endif | |
} else if ((OldChar == '-') && bSignMinus) { | |
bSignMinus = false; | |
fd.OperatorFlag = false; | |
fd.data.Number = -1; | |
pOutList->Append(fd); | |
#ifdef VIEW_INSIDE_OPERATION | |
ShowDataListAddData(fd); | |
#endif | |
Number = 0; | |
OpeStack.Push('*'); | |
#ifdef VIEW_INSIDE_OPERATION | |
printf("スタックに演算子の %c をプッシュ\n", '*'); | |
#endif | |
} | |
} | |
//printf("Push:%c\n", Current); | |
OpeStack.Push(Current); | |
#ifdef VIEW_INSIDE_OPERATION | |
printf("スタックに演算子の %c をプッシュ\n", Current); | |
#endif | |
} | |
} | |
} | |
} | |
} | |
OldChar = Current; | |
} | |
//ループの最後が数値で終わった場合出力に追加 | |
if (((OldChar >= '0') && (OldChar <= '9')) || (OldChar == '.')) { | |
if (bSignMinus) { | |
Number *= -1; | |
bSignMinus = false; | |
} | |
fd.OperatorFlag = false; | |
fd.data.Number = Number; | |
pOutList->Append(fd); | |
#ifdef VIEW_INSIDE_OPERATION | |
ShowDataListAddData(fd); | |
#endif | |
Number = 0; | |
} | |
//スタックに残っている演算子をすべてPopにして出力に追加 | |
while (OpeStack.Pop(&OldOpe)) { | |
#ifdef VIEW_INSIDE_OPERATION | |
printf("スタックから演算子の %c をポップ\n", OldOpe); | |
#endif | |
if (OldOpe != '(') { | |
fd.data.Operator = OldOpe; | |
fd.OperatorFlag = true; | |
pOutList->Append(fd); | |
#ifdef VIEW_INSIDE_OPERATION | |
ShowDataListAddData(fd); | |
#endif | |
} | |
} | |
//if (BracketLevel != 0) printf("警告:括弧の数が合いません\n"); | |
return true; | |
} | |
bool CalcFormula(FormulaDataList *pInList, double *pResult) { | |
if (!pInList || !pResult)return false; | |
int nums = pInList->GetNums(); | |
if (nums == 0) return false; | |
CHSStack<double> NumStack; | |
double num1, num2;//スタックからの取り出し先 | |
FormulaData fd; | |
bool bPopQuit; | |
#ifdef VIEW_INSIDE_OPERATION | |
printf("\n<<数式計算(逆ポーランド記法の計算)の詳細のダンプ>>\n"); | |
#endif | |
for (int i = 0; i < nums; i++) { | |
pInList->Get(i, &fd); | |
#ifdef VIEW_INSIDE_OPERATION | |
if (fd.OperatorFlag)printf("演算子の %c の入力\n" , fd.data.Operator); | |
else cout << "実数の " << fd.data.Number << " の入力" << endl; | |
#endif | |
if (fd.OperatorFlag) { | |
bPopQuit = NumStack.Pop(&num2); | |
bPopQuit &= NumStack.Pop(&num1); | |
#ifdef VIEW_INSIDE_OPERATION | |
if (bPopQuit) { | |
cout << "スタックから " << num2 << " をポップ" << endl; | |
cout << "スタックから " << num1 << " をポップ" << endl; | |
} | |
#endif | |
if (bPopQuit == false) { | |
printf("論理エラー:計算に必要な数値データが足りません。(演算子:%c)\n", fd.data.Operator); | |
return false; | |
} | |
//計算実行 | |
switch (fd.data.Operator) { | |
case '+': | |
NumStack.Push(num1 + num2); | |
#ifdef VIEW_INSIDE_OPERATION | |
cout << "スタックに " << num1 + num2 <<" をプッシュ(" << num1 << " + " << num2 << ")"<< endl; | |
#endif | |
break; | |
case '-': | |
NumStack.Push(num1 - num2); | |
#ifdef VIEW_INSIDE_OPERATION | |
cout << "スタックに " << num1 - num2 << " をプッシュ(" << num1 << " - " << num2 << ")" << endl; | |
#endif | |
break; | |
case '*': | |
NumStack.Push(num1 * num2); | |
#ifdef VIEW_INSIDE_OPERATION | |
cout << "スタックに " << num1 * num2 << " をプッシュ(" << num1 << " × " << num2 << ")" << endl; | |
#endif | |
break; | |
case '/': | |
if (num2 == 0) { | |
printf("論理エラー:ゼロ除算が発生しました。\n"); | |
return false; | |
} | |
NumStack.Push(num1 / num2); | |
#ifdef VIEW_INSIDE_OPERATION | |
cout << "スタックに " << num1 / num2 << " をプッシュ(" << num1 << " ÷ " << num2 << ")" << endl; | |
#endif | |
break; | |
case '^': | |
NumStack.Push(pow(num1, num2)); | |
#ifdef VIEW_INSIDE_OPERATION | |
cout << "スタックに " << pow(num1, num2) << " をプッシュ(" << num1 << " ^ " << num2 << ")" << endl; | |
#endif | |
break; | |
} | |
} else { | |
//数値はpush | |
NumStack.Push(fd.data.Number); | |
#ifdef VIEW_INSIDE_OPERATION | |
cout << "スタックに " <<fd.data.Number << " をプッシュ(入力)" << endl; | |
#endif | |
} | |
} | |
if (NumStack.Pop(pResult)) { | |
#ifdef VIEW_INSIDE_OPERATION | |
cout << "スタックから " << *pResult << " をポップしてそれを呼び出し元に戻す" << endl; | |
#endif | |
return true; | |
} | |
return false; | |
} | |
void ShowDataListAddData(FormulaData fd) { | |
if (fd.OperatorFlag) printf("出力バッファに演算子の %c を追加\n", fd.data.Operator); | |
else cout << "出力バッファに実数の" << fd.data.Number << "を追加" << endl; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment