첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.

728x90
반응형
728x170

TestProject.zip
다운로드

▶ MainForm.cs

using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace TestProject
{
    /// <summary>
    /// 메인 폼
    /// </summary>
    public partial class MainForm : Form
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Enumeration
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region 우선 순위 - Precedence

        /// <summary>
        /// 우선 순위
        /// </summary>
        private enum Precedence
        {
            /// <summary>
            /// NONE
            /// </summary>
            NONE = 11,

            /// <summary>
            /// UNARY
            /// </summary>
            /// <remarks>실제로 사용되지 않습니다.</remarks>
            UNARY = 10,

            /// <summary>
            /// POWER
            /// </summary>
            /// <remarks>^을 사용하여 지수를 의미한다.</remarks>
            POWER = 9,

            /// <summary>
            /// TIMES
            /// </summary>
            TIMES = 8,

            /// <summary>
            /// DIV
            /// </summary>
            DIV = 7,

            /// <summary>
            /// MODULUS
            /// </summary>
            MODULUS = 6,

            /// <summary>
            /// PLUS
            /// </summary>
            PLUS = 5,
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 변수 딕셔너리
        /// </summary>
        private Dictionary<string, string> variableDictionary;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - MainForm()

        /// <summary>
        /// 생성자
        /// </summary>
        public MainForm()
        {
            InitializeComponent();

            #region 이벤트를 설정한다.

            this.evaluateButton.Click += evaluateButton_Click;

            #endregion
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Private
        //////////////////////////////////////////////////////////////////////////////// Event

        #region 평가 버튼 클릭시 처리하기 - evaluateButton_Click(sender, e)

        /// <summary>
        /// 평가 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void evaluateButton_Click(object sender, EventArgs e)
        {
            if(this.variableDictionary != null)
            {
                this.variableDictionary.Clear();
            }
            else
            {
                this.variableDictionary = new Dictionary<string,string>();
            }

            if(this.name1TextBox.Text.Trim().Length > 0)
            {
                this.variableDictionary.Add(this.name1TextBox.Text.Trim(), this.value1TextBox.Text.Trim());
            }

            if(this.name2TextBox.Text.Trim().Length > 0)
            {
                this.variableDictionary.Add(this.name2TextBox.Text.Trim(), this.value2TextBox.Text.Trim());
            }

            if(this.name3TextBox.Text.Trim().Length > 0)
            {
                this.variableDictionary.Add(this.name3TextBox.Text.Trim(), this.value3TextBox.Text.Trim());
            }

            if(this.name4TextBox.Text.Trim().Length > 0)
            {
                this.variableDictionary.Add(this.name4TextBox.Text.Trim(), this.value4TextBox.Text.Trim());
            }

            string expression = this.expressionTextBox.Text.Trim();

            this.resultTextBox.Text = string.Empty;

            try
            {
                this.resultTextBox.Text = EvaluateExpression(expression).ToString();
            }
            catch(Exception exception)
            {
                MessageBox.Show(this, exception.Message, "에러", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////// Function

        #region 수식 평가하기 - EvaluateExpression(source)

        /// <summary>
        /// 수식 평가하기
        /// </summary>
        /// <param name="source">소스 문자열</param>
        /// <returns>수식 평가값</returns>
        private double EvaluateExpression(string source)
        {
            int parentCount     = 0;
            int currentPosition = 0;

            string expression       = source.Replace(" ", string.Empty);
            int    expressionLength = expression.Length;

            if(expressionLength == 0)
            {
                return 0;
            }

            bool isUnary = true;

            Precedence currentPrecedence = Precedence.NONE;

            for(int i = 0; i < expressionLength; i++)
            {
                string character = expression.Substring(i, 1);

                bool nextUnary = false;

                if(character == " ")
                {
                }
                else if(character == "(")
                {
                    parentCount += 1;

                    nextUnary = true;
                }
                else if(character == ")")
                {
                    parentCount -= 1;

                    nextUnary = false;

                    if(parentCount < 0)
                    {
                        throw new FormatException("닫기 괄호가 너무 많습니다 : '" + source + "'");
                    }
                }
                else if(parentCount == 0)
                {
                    if((character == "^") || (character == "*" ) ||
                       (character == "/") || (character == "\\") ||
                       (character == "%") || (character == "+" ) ||
                       (character == "-"))
                    {
                        nextUnary = true;

                        switch(character)
                        {
                            case "^" :

                                if(currentPrecedence >= Precedence.POWER)
                                {
                                    currentPrecedence = Precedence.POWER;
                                    currentPosition   = i;
                                }

                                break;

                            case "*" :
                            case "/" :

                                if(currentPrecedence >= Precedence.TIMES)
                                {
                                    currentPrecedence = Precedence.TIMES;
                                    currentPosition   = i;
                                }

                                break;

                            case "%" :

                                if(currentPrecedence >= Precedence.MODULUS)
                                {
                                    currentPrecedence = Precedence.MODULUS;
                                    currentPosition   = i;
                                }

                                break;

                            case "+" :
                            case "-" :

                                if((!isUnary) && currentPrecedence >= Precedence.PLUS)
                                {
                                    currentPrecedence = Precedence.PLUS;
                                    currentPosition   = i;
                                }

                                break;
                        }
                    }
                }

                isUnary = nextUnary;
            }

            if(parentCount != 0)
            {
                throw new FormatException("닫기 괄호가 누락되었습니다 : '" + source + "'");
            }

            if(currentPrecedence < Precedence.NONE)
            {
                string expressionLeft  = expression.Substring(0, currentPosition);
                string expressionRight = expression.Substring(currentPosition + 1);

                switch(expression.Substring(currentPosition, 1))
                {
                    case "^" : return Math.Pow(EvaluateExpression(expressionLeft), EvaluateExpression(expressionRight));
                    case "*" : return EvaluateExpression(expressionLeft) * EvaluateExpression(expressionRight);
                    case "/" : return EvaluateExpression(expressionLeft) / EvaluateExpression(expressionRight);
                    case "%" : return EvaluateExpression(expressionLeft) % EvaluateExpression(expressionRight);
                    case "+" : return EvaluateExpression(expressionLeft) + EvaluateExpression(expressionRight);
                    case "-" : return EvaluateExpression(expressionLeft) - EvaluateExpression(expressionRight);
                }
            }

            if(expression.StartsWith("(") && expression.EndsWith(")"))
            {
                return EvaluateExpression(expression.Substring(1, expressionLength - 2));
            }

            if(expression.StartsWith("-"))
            {
                return -EvaluateExpression(expression.Substring(1));
            }

            if(expression.StartsWith("+"))
            {
                return EvaluateExpression(expression.Substring(1));
            }

            if(expressionLength > 5 && expression.EndsWith(")"))
            {
                int parentPosition = expression.IndexOf("(");

                if(parentPosition > 0)
                {
                    string expressionLeft  = expression.Substring(0, parentPosition);
                    string expressionRight = expression.Substring(parentPosition + 1, expressionLength - parentPosition - 2);

                    switch(expressionLeft.ToLower())
                    {
                        case "sin"       : return Math.Sin(EvaluateExpression(expressionRight));
                        case "cos"       : return Math.Cos(EvaluateExpression(expressionRight));
                        case "tan"       : return Math.Tan(EvaluateExpression(expressionRight));
                        case "sqrt"      : return Math.Sqrt(EvaluateExpression(expressionRight));
                        case "factorial" : return GetFactorial(EvaluateExpression(expressionRight));
                    }
                }
            }

            if(variableDictionary.ContainsKey(expression))
            {
                try
                {
                    return double.Parse(variableDictionary[expression]);
                }
                catch(Exception)
                {
                    throw new FormatException("변수(" + expression + ")가 실수가 아닌 값(" + variableDictionary[expression] + ")을 갖고 있습니다.");
                }
            }

            try
            {
                return double.Parse(expression);
            }
            catch(Exception)
            {
                throw new FormatException("수식(" + source + ")을 상수로 평가시 에러가 발생했습니다.");
            }
        }

        #endregion
        #region 팩토리얼 구하기 - GetFactorial(value)

        /// <summary>
        /// 팩토리얼 구하기
        /// </summary>
        /// <param name="value">값</param>
        /// <returns>팩토리얼 값</returns>
        private double GetFactorial(double value)
        {
            if((long)value != value)
            {
                throw new ArgumentException("팩토리얼 함수에 대한 매개 변수는 정수이어야 합니다 : " + value.ToString());
            }

            double result = 1;

            for(int i = 2; i <= value; i++)
            {
                result *= i;
            }

            return result;
        }

        #endregion
    }
}
728x90
반응형
그리드형(광고전용)
Posted by icodebroker

댓글을 달아 주세요