C# 逆ポーランド記法 四則計算機 ソースコード2022年08月12日 18:05



using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public struct nameSet // 実行用スタックの要素
        {   public string name;            // 変数名,評価済みのとき"*VALUE*"
            public double value;            // 評価済み値
            public nameSet(string n, double v) { name = n; value = v;}
        }
        public struct opeSet // 逆ポーランド変換用スタックの要素
        {   public string name;             // 演算子
            public int value;               // 優先順位
            public opeSet(string n, int v) { name = n; value = v;}
        }
         public struct token // 単一トークンの形式
         {   public string atr;               // トークンの種類"NUM","OPE","VAR"
             public string str;               // トークンの文字列
             public token(string a, string s) { atr = a.ToUpper(); str=s;}
         }
        public nameSet[] STab=new nameSet[500];    // 記号表
        public int numStab;                        // 記号表のサイズ
        public token[] Polish = new token[500];    // 逆ポーランド記法の出力先
        public int numPolish;                      // 逆ポーランド記法のサイズ
        public nameSet[] Stack = new nameSet[500]; // 実行用スタック
        public int stackP;                         // スタックポインタ
        public opeSet[] StackOpe = new opeSet[500];// 逆ポーランド変換用スタック
        public string Alltext;                     // 入力文字列
        public string Delmiter = "()+-*/=";        // 区切り記号(演算子)
        public string Number = "0123456789";       // 数字
        public token[] LAData = new token[500];    // 語彙解析結果
        public int numLA;                          //  語彙解析結果のサイズ
        public Form1()
        {   InitializeComponent();
        }
        private void push(nameSet X)  // 実行用スタック Push
        {   if (stackP >= 500) MessageBox.Show("Stack Over");
            else
            {   Stack[stackP] = X;
                if(checkBox1.Checked)MessageBox.Show("push(" + stackP.ToString() + ")" +
                                                X.name + " Value=" + X.value.ToString());
                stackP++;
            }
        }
        private nameSet pop()           // 実行用スタック Pop
        {   if (stackP <= 0)
            {   MessageBox.Show("Stack Empty"); return new nameSet("*Error*", 0);}
            else
            {   stackP--;nameSet X = Stack[stackP];
                if (checkBox1.Checked) MessageBox.Show("pop(" + stackP.ToString() + ")" +
                                                  X.name +" Value=" + X.value.ToString());
                return X;
            }
        }
        private void pushOpe(opeSet X)     // 変換用 Push
        {   if (stackP >= 500) MessageBox.Show("Stack Over");
            else
            {   StackOpe[stackP] = X;
                if (checkBox1.Checked) MessageBox.Show("push(" + stackP.ToString() + ")" + 
                                               X.name +" Priority=" + X.value.ToString());
                stackP++;
            }
        }
        private opeSet popOpe()            // 変換用 Pop
        {   if (stackP <= 0)
            {   MessageBox.Show("Stack Empty"); return new opeSet("*Error*", 0);}
            else
            {   stackP--; opeSet X=StackOpe[stackP];
                if (checkBox1.Checked) MessageBox.Show("pop(" + stackP.ToString() + ")" + 
                                              X.name +" Priority=" + X.value.ToString());
                return X;
            }
        }
        private nameSet sepValue(string X)// TextBoxの変数名/値分離
        {   string[] S = new string[2]; S=X.Split('=');
            return new nameSet(S[0].Trim(' '),double.Parse(S[1]));
        }
        private token setPolish(string X) // TextBoxのトークン設定
        {   string[] S = new string[2]; S = X.Split(':');
            return new token(S[0].Trim(' '), S[1].Trim(' '));
        }
        private void setValue(string Name, double V)//変数値の設定
        {   STab[numStab].name=Name;
            int i=0; while (STab[i].name!=Name) i++;
            STab[i].value = V; if (i >= numStab) numStab++;
            dspName();
        }
        private double getValue(string Name) // 与えられた変数の値
        {   for (int i = 0; i < STab.Length; i++)
                if (STab[i].name == Name) return STab[i].value;
            return 0;
        }
        private void outStab(string s)   // "変数名=値"のデータを記号表に登録
        {   STab[numStab] = sepValue(s); numStab++;  
        }
        private void outSPolish(string s) // "種類:記号"のデータをトークンとして設定
        {   Polish[numPolish] = setPolish(s); numPolish++;
        }
        private void initialize() // 実行用初期設定
        {   numStab = 0;  // 名前表
            for (int i = 0; i < textBoxSymbol.Lines.Length; i++)
                    if (textBoxSymbol.Lines[i].Length >= 2) outStab(textBoxSymbol.Lines[i]);
            numPolish = 0; // 逆ポーランド記法
            for (int i = 0; i < textBoxPolish.Lines.Length; i++)
                   if (textBoxPolish.Lines[i].Length >= 2) outSPolish(textBoxPolish.Lines[i]);
            stackP = 0; // スタックポインタ
        }
        private void outLAData(string s) // "種類:記号"のデータを語彙解析結果として設定
        {   LAData[numLA] = setPolish(s); numLA++;   
        }
        private void initializeSA() // 変換の初期設定
        {   numLA = 0;  // 語彙解析結果
            for (int i = 0; i < textBoxLA.Lines.Length; i++)
                if (textBoxLA.Lines[i].Length >= 2) outLAData(textBoxLA.Lines[i]);
            stackP = 0;
        }
        private double evalDT(nameSet X) // 値を求める
        {   if (X.name == "*VALUE*") return X.value;
            return getValue(X.name);
        }
        private void exePolish()  // 逆ポーランド記法の実行
        {   initialize();         //初期設定
            for (int i = 0; i < numPolish; i++)  //【注】変数Pushの場合、値が必要になった時点で評価。
            {   if(Polish[i].atr=="VAR") push(new nameSet(Polish[i].str, 0));//変数Push(ここでは値はダミー)
                else if (Polish[i].atr == "OPE")
            {       nameSet A1, A2;
                    if (checkBox1.Checked) MessageBox.Show("演算実行 " + Polish[i].str);  //演算実行
                    A2 = pop(); double V2 = evalDT(A2);       // 演算対象Popと評価
                    if (Polish[i].str == "$+") { }      // 単項演算子実行 $+
                    else if (Polish[i].str == "$-") V2 = -V2; //                $-
                    else
                    {   A1 = pop();                           // 演算対象Pop
                        if (Polish[i].str == "=")             //  代入処理
                        {   if (A1.name == "*VALUE*") MessageBox.Show("代入に矛盾があります");
                            else setValue(A1.name, V2);
                        }
                        else                                  // 以下2項演算
                        {   double V1 = evalDT(A1);
                            if (Polish[i].str == "+") V2 = V1 + V2;
                            else if (Polish[i].str == "-") V2 = V1 - V2;
                            else if (Polish[i].str == "*") V2 = V1 * V2;
                            else if (Polish[i].str == "/") V2 = V1 / V2;
                            else MessageBox.Show("演算子の例外");
                        }
                    }
                    push(new nameSet("*VALUE*", V2)); // 演算結果のPush
                }
                else push(new nameSet("*VALUE*", double.Parse(Polish[i].str)));// 数値のPush
             }
        }
        private void dspName() // 記号表をテキストボックスに表示
        {   string S = "";
            for(int i=0;i<numStab;i++)S=S+STab[i].name+"="+STab[i].value.ToString()+"\r\n";
            textBoxSymbol.Text=S;
        }
        private void button1_Click(object sender, EventArgs e) { exePolish();}
        private void startLA() // 語彙解析開始
        {   Alltext = textBoxInput.Text.TrimStart();
        }
        private token numberProc(string ch)// 数字の設定
        {
            Boolean dot = false;
            if (ch == ".") dot = true;
            string s = ch;
            if (Alltext != "")
            {   ch = Alltext.Substring(0, 1);
                while (ch=="." || Number.IndexOf(ch) > -1) // 数字のあいだ以下を繰り返す
                {   if (ch == ".")
                    {   if (dot) MessageBox.Show(".の位置の誤り");
                        dot = true;
                    } 
                    s += ch; Alltext = Alltext.Substring(1, Alltext.Length - 1);
                    if (Alltext == "") break;
                    ch = Alltext.Substring(0, 1);
                }
            }
            return (new token("Num", s));
        }
        private token nameProc(string ch)// 変数名の設定
        {   string s = ch;
            if (Alltext != "")
            {   ch = Alltext.Substring(0, 1);
                while (ch != " " && Delmiter.IndexOf(ch) <= -1)// 空白、区切り記号のいずれ
                {   s += ch;                                   // でもないあいだ繰り返す
                    Alltext = Alltext.Substring(1, Alltext.Length - 1);
                    if (Alltext == "") break;
                    ch = Alltext.Substring(0, 1);
                }
            }
            return (new token("Var", s));
        }
        private token getLAToken() // 語彙解析
        {   Alltext=Alltext.TrimStart();
            if (Alltext == "") return (new token("$EOF$", "$EOF$"));
            string ch = Alltext.Substring(0, 1);
            Alltext = Alltext.Substring(1, Alltext.Length - 1);
            if (Delmiter.IndexOf(ch) > -1) return (new token("Ope", ch));
            else if (ch=="." || Number.IndexOf(ch) > -1) return numberProc(ch);
            else return nameProc(ch);
        }
        private void dspPolish()//逆ポーランド記法をテキストボックスに表示
        {   string S = "";
            for (int i = 0; i < numPolish; i++)
                S += Polish[i].atr + ":" + Polish[i].str + "\r\n";
            textBoxPolish.Text = S;
        }
        private void button2_Click(object sender, EventArgs e)// 連続語彙解析
        {   startLA();  string S = ""; token TK = getLAToken();
            while (TK.atr != "$EOF$")
            {   string X = TK.atr + ":" + TK.str; S = S + X + "\r\n";
                if (checkBox1.Checked) MessageBox.Show(X);
                TK = getLAToken();
            }
            textBoxLA.Text = S;
        }
        private int priority(string s) // 演算子による優先順位の設定
        {   if (s == "=") return 10;
            else if (s == "+" || s == "-") return 20;
            else if (s == "*" || s == "/") return 30;
            else if (s == ")") return  -1;
            else return 0;           
        }
        private void singleOpe(string s) // 単項演算子と左カッコの処理
        {   if (s == "+" || s == "-")             // 2項演算子の+,-と区別するために
                pushOpe(new opeSet("$" + s, 200));// それぞれ$を先頭に付ける。 
            else if (s == "(")                    // 左括弧の場合Push
                pushOpe(new opeSet("(", 0)); 
            else MessageBox.Show("演算子エラー");
        }
        private int operandProc(token S) // 演算対象の場合、そのまま変換結果に移動
        {   Polish[numPolish] = S;
            numPolish++;
            return 1; // 次のモードを演算子モードにする。
        }
        private void rightParPop()// 右カッコの処理
        {   if (stackP > 0)       // 左カッコになるまでPopして変換結果に移動
            {   opeSet X = popOpe();
                while (X.name != "(" && stackP >= 0)
                {   Polish[numPolish] = new token("OPE", X.name); numPolish++; X = popOpe();
                }
            }
            else MessageBox.Show("左カッコが足りません");
        }
        private int operationProc(string s, int Opt)// 演算子処理
        {   if (stackP > 0)       // スタック上に優先順位が高いか、
            {   while (stackP>0)  // 同じ演算子があればPopして変換結果に移動
                {   if (StackOpe[stackP-1].value < Opt) break;
                    opeSet X = popOpe(); Polish[numPolish] = new token("OPE", X.name); numPolish++;
                }
            }
            pushOpe(new opeSet(s, Opt));// 演算子をPush
            return 0; // 次のモードを演算対象モードとする。 
        }
        private void endPop()// スタック上に残った演算子を変換結果に移動。
        {   while (stackP > 0)
            {   opeSet X = popOpe();
                Polish[numPolish] = new token("OPE", X.name); numPolish++;
            }
        }
        private void SA()  // 逆ポーランドへの変換
        {   initializeSA(); int SMode = 0; numPolish = 0;
            for (int i = 0; i < numLA; i++)
            {   string ATR = LAData[i].atr.ToUpper();
                if (SMode == 0)  // 演算対象モード(演算子は、+,-(単項演算子)か左括弧)
                {   if (ATR == "OPE") singleOpe(LAData[i].str);// 単項演算子と左括弧の処理
                    else SMode = operandProc(LAData[i]);       //演算対象処理
                }
                else // 演算子モード(2項演算子か右括弧だけのモード)
                {   if (ATR == "OPE")
                    {   int Opt = priority(LAData[i].str);             // 演算子の優先順位設定
                        if (Opt == 0) MessageBox.Show("演算子エラー"); // (0:エラー,-1:右括弧)
                        else if (Opt < 0) rightParPop();               // 右括弧の処理
                        else SMode = operationProc(LAData[i].str, Opt);// 2項演算子処理
                    }
                    else MessageBox.Show("演算子がありません");
                }
            }
            endPop(); dspPolish(); // スタックに残った演算子を変換結果に移して結果表示
        }
        private void button3_Click(object sender, EventArgs e)
        {   SA();
        }
    }
}


<< 2022/08
01 02 03 04 05 06
07 08 09 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31

このブログについて

ネットで見つけたいろいろ雑記

バックナンバー

RSS