Skip to content

Instantly share code, notes, and snippets.

@hirosof
Created September 4, 2014 13:03
Show Gist options
  • Save hirosof/53fd99c2003a842bb938 to your computer and use it in GitHub Desktop.
Save hirosof/53fd99c2003a842bb938 to your computer and use it in GitHub Desktop.
#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