Click here to Skip to main content
15,895,142 members
Articles / Programming Languages / C#

Building a General Purpose Interpreter, Math Engine and Parser in C#

Rate me:
Please Sign up or sign in to vote.
4.97/5 (70 votes)
13 Feb 2015GPL313 min read 85K   7.9K   224  
An easy to understand and use language interpreter, run time parser and calculation engine from scratch in C#.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;

namespace MathProcessorLib
{
    public delegate void IntermediateResult(Token result);

    public static class Calculator
    {
        public static event IntermediateResult IntermediateResultProduced = x => { };
        static Variables variables = Variables.GetVariables();
        static int loopDepth = 0;
        public static String DefaultFormatString { get; set; }


        public static void SaveXML(XElement root)
        {
            XElement element = new XElement("mplib");
            element.Add(new XElement("fs", DefaultFormatString));
            variables.SaveXML(element);
            FunctionDefiner.SaveXML(element);
            root.Add(element);
        }

        public static void LoadXML(XElement xe)
        {            
            XElement element = xe.Element("mplib");
            DefaultFormatString = element.Element("fs").Value;
            variables.LoadXML(element);
            FunctionDefiner.LoadXML(element);
        }

        public static void SendIntermediateResult(Token token)
        {
            IntermediateResultProduced(token);
        }

        static Calculator()
        {
            DefaultFormatString = "F04";
        }

        static Token VerifyFormatString(List<Token> tokens)
        {
            if (tokens.Count < 3)
            {
                return Token.Error("Invalid command");
            }
            String formatStr = tokens[1].TokenName;
            double testValue = 0;
            try
            {
                String retValue = testValue.ToString(formatStr);
                if (retValue == tokens[1].TokenName)
                {
                    return Token.Error("Given format string is invalid [ " + retValue + " ]");
                }
            }
            catch (Exception)
            {
                return Token.Error("Given format string is invalid [ " + formatStr + " ]");
            }
            return Token.Void;
        }

        internal static bool GetTrimmedExpression(string inExpression, out string outExpression)
        {
            outExpression = inExpression.Trim();
            bool suppressOutput = false;            
            if (outExpression.Length > 0 && outExpression[outExpression.Length - 1] == ';')
            {
                int i = outExpression.Length - 1;
                int count = 1;
                while (--i >= 0 && (outExpression[i] == ';' || Char.IsWhiteSpace(outExpression[i]))) { count++; }
                outExpression = outExpression.Remove(outExpression.Length - count);
                suppressOutput = true;
            }
            return suppressOutput;
        }

        internal static Token ProcessCommand(string expression, bool isIntermedite)
        {
            Token resultToken = Token.Void;
            bool suppressOutput = GetTrimmedExpression(expression, out expression);
            if (expression.Length == 0)
            {
                return resultToken;
            }            
            List<Token> tokens;
            resultToken = Tokenizer.TokenizeString(expression, out tokens);
            if (resultToken.TokenType == TokenType.Error)
            {
                return resultToken;
            }

            if (tokens.First().TokenType == TokenType.Directive)
            {
                resultToken = ExecuteDirective(tokens);
            }
            else
            {
                List<Token> postfixTokens = new List<Token>();
                resultToken = InfixToPostfix(tokens, postfixTokens);
                if (resultToken.TokenType != TokenType.Error)
                {
                    resultToken = CalculateAndSaveAns(postfixTokens);
                    if (resultToken.TokenType != TokenType.Error && isIntermedite && !suppressOutput)
                    {
                        IntermediateResultProduced(resultToken);
                    }
                }
            }
            if (!suppressOutput || resultToken.TokenType == TokenType.Error)
            {
                return resultToken;
            }
            else
            {
                return Token.Void;
            }
        }

        internal static Token ExecuteDirective(List<Token> tokens)
        {
            string name = tokens.First().TokenName;
            tokens.RemoveAt(0);
            return Function.InvokeFunction(name, tokens);
        }

        internal static Token CalculateAndSaveAns (List<Token> postfixTokens)
        {
            Token resultToken = Calculate(postfixTokens);
            if (resultToken.TokenType != TokenType.Error)
            {
                if (resultToken.TokenType == TokenType.Vector || resultToken.TokenType == TokenType.Bool ||
                    resultToken.TokenType == TokenType.Matrix || resultToken.TokenType == TokenType.Text)
                {
                    Token ans = resultToken.Clone();
                    ans.TokenName = "ans";
                    variables.SetToken(ans);
                }
            }
            return resultToken;
        }

        public static Token ProcessCommand(String expression)
        {
            return ProcessCommand(expression, false);
        }

        static public Token InfixToPostfix(List<Token> infixTokens, List<Token> postfixTokens)
        {
            Stack<Token> operatorStack = new Stack<Token>();
            int i = 0;
            int bracketStart = 0;
            bool assignmentValid = false;

            for (; i < infixTokens.Count; i++)
            {
                if (infixTokens[i].TokenType == TokenType.Vector || infixTokens[i].TokenType == TokenType.Text ||
                    infixTokens[i].TokenType == TokenType.Bool || infixTokens[i].TokenType == TokenType.Block ||
                    infixTokens[i].TokenType == TokenType.Break || infixTokens[i].TokenType == TokenType.Matrix
                )
                {
                    if (infixTokens[i].TokenType == TokenType.Break && loopDepth == 0)
                        return Token.Error("break statement can only be inside a loop.");
                    if ((i == 0 || infixTokens[i - 1].TokenName == "="))
                        assignmentValid = true;
                    postfixTokens.Add(infixTokens[i]);
                }
                else if (infixTokens[i].TokenType == TokenType.Operator)
                {
                    if (infixTokens[i].TokenName == "(")
                    {
                        bracketStart++;
                    }
                    else if (infixTokens[i].TokenName == ")")
                    {
                        bracketStart--;
                    }
                    if (infixTokens[i].TokenName != "=")
                        assignmentValid = false;
                    if (infixTokens[i].TokenName == "=")
                    {
                        if (assignmentValid)
                            operatorStack.Push(infixTokens[i]);
                        else
                            return Token.Error("Wrong use of '=' operator.");
                    }
                    else if (operatorStack.Count == 0 || infixTokens[i].TokenName == "(")
                    {
                        operatorStack.Push(infixTokens[i]);
                    }
                    else if (infixTokens[i].TokenName == ")")
                    {
                        while (operatorStack.Count > 0 && operatorStack.Peek().TokenName != "(")
                        {
                            postfixTokens.Add(operatorStack.Pop());
                        }
                        if (operatorStack.Count > 0)
                        {
                            operatorStack.Pop();
                        }
                        else
                        {
                            return Token.Error("Round bracket [()] mismatch.");
                        }
                    }
                    else if (OperatorPriority(operatorStack.Peek().TokenName) >= OperatorPriority(infixTokens[i].TokenName))
                    {
                        if (!infixTokens[i].TokenName.StartsWith("u"))
                        {
                            while (operatorStack.Count > 0 && OperatorPriority(operatorStack.Peek().TokenName) >= OperatorPriority(infixTokens[i].TokenName))
                            {
                                if (operatorStack.Peek().TokenName == "(")
                                    break;
                                postfixTokens.Add(operatorStack.Pop());
                            }
                        }
                        operatorStack.Push(infixTokens[i]);
                    }
                    else
                    {
                        operatorStack.Push(infixTokens[i]);
                    }
                }
                else if (infixTokens[i].TokenType == TokenType.Function || infixTokens[i].TokenType == TokenType.LoopOrCondition ||
                         infixTokens[i].TokenType == TokenType.UserFunction || infixTokens[i].TokenType == TokenType.FunctionDefiner)
                {
                    assignmentValid = false;
                    operatorStack.Push(infixTokens[i++]);
                    if (infixTokens.Count - i < 2)
                        return Token.Error("Expression not valid");
                    if (infixTokens[i++].TokenName != "(")
                    {
                        return Token.Error("Wrong use of [ " + infixTokens[i - 2].TokenName + " ]");
                    }
                    List<Token> subTokenList = new List<Token>();
                    int openCount = 1;
                    int paramCount = 0;
                    bool commaBegin = false;
                    while (i < infixTokens.Count)
                    {
                        if (infixTokens[i].TokenName == ")")
                        {
                            if (openCount != 1)
                                subTokenList.Add(infixTokens[i]);
                            openCount--;
                            if (openCount == 0)
                            {
                                Token token = InfixToPostfix(subTokenList, postfixTokens);
                                if (token.TokenType == TokenType.Error)
                                    return token;
                                subTokenList.Clear();
                                break;
                            }
                        }
                        else if (infixTokens[i].TokenName == "(")
                        {
                            subTokenList.Add(infixTokens[i]);
                            openCount++;
                        }
                        else if (openCount == 1 && infixTokens[i].TokenName == ",")
                        {
                            commaBegin = true;
                            paramCount++;
                            Token token = InfixToPostfix(subTokenList, postfixTokens);
                            if (token.TokenType == TokenType.Error)
                                return token;
                            subTokenList.Clear();
                        }
                        else
                        {
                            commaBegin = false;
                            subTokenList.Add(infixTokens[i]);
                            if (paramCount == 0)
                                paramCount = 1;
                        }
                        i++;
                    }
                    if (openCount != 0 || commaBegin)
                        return Token.Error("Bracket mismatch or bad comma");
                    if (operatorStack.Peek().TokenType == TokenType.LoopOrCondition || operatorStack.Peek().TokenType == TokenType.FunctionDefiner)
                    {
                        i++;
                        if (i < infixTokens.Count && infixTokens[i].TokenType == TokenType.Block)
                        {
                            paramCount++;
                            postfixTokens.Add(infixTokens[i]);
                        }
                        else
                            return Token.Error("Expecting  block of commands inside curly brackets - { }");
                    }
                    if (operatorStack.Peek().TokenName == "if")
                    {
                        if (i + 1 < infixTokens.Count)
                        {
                            if (infixTokens[i + 1].TokenName == "else")
                            {
                                i++;
                                if (i + 1 == infixTokens.Count)
                                    return Token.Error("Wrong use of 'else'");
                                i++;
                                if (infixTokens[i].TokenType != TokenType.Block)
                                    return Token.Error("'else' should be followed by commands inside curly brackets - { }");
                                postfixTokens.Add(infixTokens[i]);
                                paramCount++;
                            }
                        }
                    }
                    postfixTokens.Add(new Token(TokenType.Vector, paramCount));
                    postfixTokens.Add(operatorStack.Pop());
                }
                else
                {
                    return Token.Error("Un-recognized token");
                }
            }
            if (bracketStart != 0)
                return Token.Error("Bracket mismatch");
            int temp = operatorStack.Count;
            for (i = 0; i < temp; i++)
            {
                postfixTokens.Add(operatorStack.Pop());
            }
            return Token.Void;
        }

        static int OperatorPriority(string operatorStr)
        {
            int priority = 0;
            switch (operatorStr)
            {
                case "(":
                case ")":
                    priority = 35;
                    break;

                case "u+":
                case "u-":
                case "u~":
                    priority = 25;
                    break;

                case "^":
                    priority = 22;
                    break;

                case "*":
                case "/":
                case "%":
                    priority = 20;
                    break;

                case "+":
                case "-":
                case "?":
                case "??":
                    priority = 15;
                    break;

                case "<":
                case ">":
                case "<=":
                case ">=":
                    priority = 12;
                    break;

                case "==":
                case "!=":
                    priority = 10;
                    break;

                case "=":
                    priority = 5;
                    break;

                case ",":
                    priority = 1;
                    break;

                default:
                    throw new ArgumentException("Not an operator");
            }
            return priority;
        }

        public static Token Calculate(List<Token> postfixTokens)
        {
            Stack<Token> operands = new Stack<Token>();
            Token result = Token.Void;
            try
            {
                foreach (Token t in postfixTokens)
                {
                    if (t.TokenType == TokenType.Vector || t.TokenType == TokenType.Text ||
                        t.TokenType == TokenType.Bool || t.TokenType == TokenType.Block ||
                        t.TokenType == TokenType.Break || t.TokenType == TokenType.Matrix
                        )
                    {
                        operands.Push(t);
                    }
                    else if (t.TokenType == TokenType.Function || t.TokenType == TokenType.LoopOrCondition ||
                             t.TokenType == TokenType.UserFunction || t.TokenType == TokenType.FunctionDefiner)
                    {
                        int paramCount = (int)(operands.Pop().FirstValue);
                        List<Token> parameters = new List<Token>();
                        for (int i = 0; i < paramCount; i++)
                        {
                            parameters.Add(operands.Pop());
                        }
                        parameters.Reverse();
                        Token resultToken;
                        if (t.TokenType == TokenType.Function)
                        {
                            resultToken = Function.InvokeFunction(t.TokenName, parameters);
                            if (resultToken.TokenType == TokenType.Error)
                                return resultToken;
                        }
                        else if (t.TokenType == TokenType.UserFunction)
                        {
                            resultToken = FunctionDefiner.ExecuteUserFunction(t.TokenName, parameters);
                            if (resultToken.TokenType == TokenType.Error)
                                return resultToken;
                        }
                        else if (t.TokenType == TokenType.LoopOrCondition)
                        {
                            if (t.TokenName == "repeat" || t.TokenName == "while")
                            {
                                loopDepth++;
                            }
                            resultToken = BlockCommands.Execute(t.TokenName, parameters);
                            if (resultToken.TokenType == TokenType.Error)
                            {
                                loopDepth = 0;
                                return resultToken;
                            }
                            if (t.TokenName == "repeat" || t.TokenName == "while")
                            {
                                loopDepth--;
                            }
                        }
                        else
                        {
                            if (operands.Count == 0)
                            {
                                return Token.Error("User function must be assigned to a name");
                            }
                            resultToken = FunctionDefiner.CreateUserFunction(parameters);
                            if (resultToken.TokenType == TokenType.Error)
                                return resultToken;
                        }
                        operands.Push(resultToken);
                    }
                    else if (operands.Count >= 1 && t.TokenName.StartsWith("u"))
                    {
                        if (operands.Peek().TokenType != TokenType.Vector && operands.Peek().TokenType != TokenType.Bool &&
                            operands.Peek().TokenType != TokenType.Matrix)
                            return Token.Error("Operand not valid for [ " + t.TokenName + " ]");
                        Token firstParam = operands.Pop();
                        Token temp = null;
                        if (!DoCalculateUnary(t.TokenName, firstParam, out temp))
                            return Token.Error("Operand not valid for [ " + t.TokenName + " ]");
                        operands.Push(temp);
                    }
                    else if (operands.Count > 1)
                    {
                        Token temp = null;
                        Token firstParam = operands.Pop();
                        Token secondParam = operands.Pop();
                        switch (t.TokenName)
                        {
                            case "*":
                            case "/":
                            case "%":
                            case "+":
                            case "-":
                            case "^":
                            case "==":
                            case "!=":
                            case "?":
                            case "??":
                            case "<":
                            case ">":
                            case ">=":
                            case "<=":
                                if ((firstParam.TokenType != TokenType.Vector && firstParam.TokenType != TokenType.Bool && firstParam.TokenType != TokenType.Matrix) ||
                                    (secondParam.TokenType != TokenType.Vector && secondParam.TokenType != TokenType.Bool && secondParam.TokenType != TokenType.Matrix)
                                   )
                                {
                                    return Token.Error("Operand not valid for [ " + t.TokenName + " ].");
                                }
                                temp = DoCalculate(t.TokenName, secondParam, firstParam);
                                if (temp.TokenType == TokenType.Error) //yep stack is upside down!
                                    return temp;
                                break;

                            case "=":
                                if (firstParam.TokenType == TokenType.Void || string.IsNullOrEmpty(secondParam.TokenName))
                                {
                                    return Token.Error("Assignment not valid.");
                                }
                                secondParam.ResetValues(firstParam.TokenType, firstParam.StrData, firstParam.Vector, firstParam.Extra);
                                if (variables.Contains(secondParam.TokenName))
                                {
                                    temp = variables.SetTokenVerify(secondParam);
                                }
                                else
                                {
                                    temp = variables.AddTokenVerify(secondParam);
                                }
                                if (temp.TokenType == TokenType.Error)
                                {
                                    return temp;
                                }
                                else
                                {
                                    temp = secondParam;
                                }
                                if (secondParam.TokenType == TokenType.UserFunction)
                                {
                                    if (operands.Count == 0)
                                    {
                                        return new Token(TokenType.Text, "", "User function named [ " + temp.TokenName + " ] added");
                                    }
                                    else
                                    {
                                        return Token.Error("Bad definition of User function");
                                    }
                                }
                                break;
                            default:
                                return Token.Error("Error in expression");
                        }
                        operands.Push(temp);
                    }
                    else
                    {
                        return Token.Error("Error in expression");
                    }
                }
                if (operands.Count != 1)
                    return Token.Error("Error in expression");
                result = operands.Pop();
                if (result.TokenType == TokenType.Block)
                {
                    result = BlockCommands.ExecuteBlock(result);
                    if (result.TokenType != TokenType.Error)
                        return Token.Void;
                }
                else if (result.TokenType == TokenType.Operator || result.TokenType == TokenType.LoopOrCondition)
                    return Token.Error("Error in expression");
            }
            catch (Exception)
            {
                return Token.Error("Error in expression");
            }
            return result;
        }

        static bool DoCalculateUnary(string operation, Token firstParam, out Token result)
        {
            result = null;
            if (firstParam.TokenType == TokenType.Bool)
            {
                if (operation == "u~")
                {
                    double[] temp = firstParam.VectorArray;
                    for (int i = 0; i < firstParam.Count; i++)
                    {
                        if (temp[i] == 0)
                            temp[i] = 1;
                        else
                            temp[i] = 0;
                    }
                    result = new Token(TokenType.Bool, temp);
                }
                else
                {
                    return false;
                }
            }
            else if (firstParam.TokenType == TokenType.Vector)
            {
                result = new Token(TokenType.Vector, firstParam.VectorArray);
                if (operation == "u-")
                {
                    double[] vector = firstParam.VectorArray;
                    for (int i = 0; i < vector.Count(); i++)
                    {
                        vector[i] = -vector[i];
                    }
                    result.VectorArray = vector;
                }
                else if (operation == "u~")
                {
                    return false;
                }
            }
            else
            {
                result = new Token(TokenType.Matrix, firstParam.Extra, firstParam.VectorArray);
                if (operation == "u-")
                {
                    double[] vector = firstParam.VectorArray;
                    for (int i = 0; i < vector.Count(); i++)
                    {
                        vector[i] = -vector[i];
                    }
                    result.VectorArray = vector;
                }
            }
            return true;
        }

        static Token DoCalculate(string operation, Token firstParam, Token secondParam)
        {
            if (firstParam.TokenType == TokenType.Matrix || secondParam.TokenType == TokenType.Matrix)
            {
                return DoCalculateMatrix(operation, firstParam, secondParam);
            }
            if (firstParam.TokenType != secondParam.TokenType)
            {
                return Token.Error("Operation not valid. Operands have Different Types");
            }
            if (operation == "==" || operation == "!=" || operation == "<" || operation == ">" ||
                operation == "<=" || operation == ">="
               )
            {
                return CompareParams(operation, firstParam, secondParam);
            }
            else if (firstParam.TokenType == TokenType.Vector)
            {
                return DoArithmetic(operation, firstParam, secondParam);
            }
            else
            {
                return DoBoolean(operation, firstParam, secondParam);
            }
        }

        static Token CompareParams(string operation, Token firstParam, Token secondParam)
        {
            if (firstParam.Count < 1 || firstParam.Count != secondParam.Count)
                return Token.Error("Either first parameter does not have a numeric value or count of operands not same.");
            if (firstParam.Count > 1)
                return CompareParamArrays(operation, firstParam, secondParam);
            else
                return CompareParamValues(operation, firstParam, secondParam);
        }

        static Token CompareParamArrays(string operation, Token firstParam, Token secondParam)
        {
            int value = 0;  //0 means false
            int i = 0;
            switch (operation)
            {
                case "==":
                    value = 1;
                    for (; i < firstParam.Count; i++)
                    {
                        if (firstParam[i] != secondParam[i])
                        {
                            value = 0;
                            break;
                        }
                    }
                    break;
                case "!=":
                    for (; i < firstParam.Count; i++)
                    {
                        if (firstParam[i] != secondParam[i])
                        {
                            value = 1;
                            break;
                        }
                    }
                    break;
                case "<":
                    for (; i < firstParam.Count; i++)
                    {
                        if (firstParam[i] < secondParam[i])
                        {
                            value = 1;
                            break;
                        }
                    }
                    break;
                case ">":
                    for (; i < firstParam.Count; i++)
                    {
                        if (firstParam[i] > secondParam[i])
                        {
                            value = 1;
                            break;
                        }
                    }
                    break;
                case "<=":
                    value = 1;
                    for (; i < firstParam.Count; i++)
                    {
                        if (firstParam[i] > secondParam[i])
                        {
                            value = 0;
                            break;
                        }
                    }
                    break;
                case ">=":
                    value = 1;
                    for (; i < firstParam.Count; i++)
                    {
                        if (firstParam[i] < secondParam[i])
                        {
                            value = 0;
                            break;
                        }
                    }
                    break;
                default:
                    return Token.Error("Operation not valid");
            }
            return new Token(TokenType.Bool, value);
        }

        static Token CompareParamValues(string operation, Token firstParam, Token secondParam)
        {
            int result = 0;
            switch (operation)
            {
                case "==":
                    if (firstParam.FirstValue == secondParam.FirstValue)
                        result = 1;
                    break;
                case "!=":
                    if (firstParam.FirstValue != secondParam.FirstValue)
                        result = 1;
                    break;
                case "<":
                    if (firstParam.FirstValue < secondParam.FirstValue)
                        result = 1;
                    break;
                case ">":
                    if (firstParam.FirstValue > secondParam.FirstValue)
                        result = 1;
                    break;
                case "<=":
                    if (firstParam.FirstValue <= secondParam.FirstValue)
                        result = 1;
                    break;
                case ">=":
                    if (firstParam.FirstValue >= secondParam.FirstValue)
                        result = 1;
                    break;
                default:
                    return Token.Error("Operation not valid");
            }
            return new Token(TokenType.Bool, 1, result);
        }

        static double PerformBoolOperation(string operation, double firstNumber, double secondNumber)
        {
            bool firstValue = (firstNumber == 0) ? false : true;
            bool secondValue = (secondNumber == 0) ? false : true;

            switch (operation)
            {
                case "+":
                    firstValue = firstValue | secondValue;
                    break;

                case "*":
                    firstValue = firstValue & secondValue;
                    break;

                case "^":
                    firstValue = firstValue ^ secondValue;
                    break;

                case "?":
                    firstValue = (!firstValue) | secondValue;
                    break;

                case "??":
                    firstValue = ((!firstValue) | secondValue) & ((!secondValue) | firstValue);
                    break;

                default:
                    throw new ArgumentException("Operation no supported");
            }
            return firstValue ? 1 : 0;
        }

        static Token DoBoolean(string operation, Token firstParam, Token secondParam)
        {
            int firstCount = firstParam.Count;
            int secondCount = secondParam.Count;

            if (firstCount == 0 || secondCount == 0)
                return Token.Error("Either first parameter does not have a boolean value or count of operands not same.");

            double[] bools = new double[Math.Max(firstCount, secondCount)];

            if (firstCount >= 1 && secondCount == 1)
            {
                for (int i = 0; i < firstCount; i++)
                {
                    bools[i] = PerformBoolOperation(operation, firstParam[i], secondParam.FirstValue);
                }
            }
            else if (firstCount == 1 && secondCount >= 1)
            {
                for (int i = 0; i < secondCount; i++)
                {
                    bools[i] = PerformBoolOperation(operation, firstParam.FirstValue, secondParam[i]);
                }
            }
            else
            {
                if (firstCount != secondCount)
                    return Token.Error("First and second parameters do not have equal number of elements.");

                for (int i = 0; i < secondCount; i++)
                {
                    bools[i] = PerformBoolOperation(operation, firstParam[i], secondParam[i]);
                }
            }
            return new Token(TokenType.Bool, bools);
        }

        static Token DoArithmetic(string operation, Token firstParam, Token secondParam)
        {
            int firstCount = firstParam.Count;
            int secondCount = secondParam.Count;

            if (firstCount == 0 || secondCount == 0)
                return Token.Error("Either first or second or both parameter(s) have 0 elements.");

            double[] numbers = new double[Math.Max(firstCount, secondCount)];

            if (firstCount >= 1 && secondCount == 1)
            {
                for (int i = 0; i < firstCount; i++)
                {
                    numbers[i] = PerformArithmeticOperation(operation, firstParam[i], secondParam.FirstValue);
                }
            }
            else if (firstCount == 1 && secondCount >= 1)
            {
                for (int i = 0; i < secondCount; i++)
                {
                    numbers[i] = PerformArithmeticOperation(operation, firstParam.FirstValue, secondParam[i]);
                }
            }
            else
            {
                if (firstCount != secondCount)
                    return Token.Error("First and second parameters do not have equal number of elements.");

                for (int i = 0; i < secondCount; i++)
                {
                    numbers[i] = PerformArithmeticOperation(operation, firstParam[i], secondParam[i]);
                }
            }
            return new Token(TokenType.Vector, numbers);
        }

        static double PerformArithmeticOperation(string operation, double firstNumber, double secondNumber)
        {
            switch (operation)
            {
                case "+":
                    return firstNumber + secondNumber;
                case "-":
                    return firstNumber - secondNumber;
                case "*":
                    return firstNumber * secondNumber;
                case "/":
                    return firstNumber / secondNumber;
                case "^":
                    return Math.Pow(firstNumber, secondNumber);
                case "%":
                    return firstNumber % secondNumber;
                default:
                    throw new ArgumentException("Operation no supported");
            }
        }

        static Token DoCalculateMatrix(string operation, Token firstParam, Token secondParam)
        {
            /* Scalar operations*/
            Token result = null;
            if (firstParam.TokenType == TokenType.Vector ||
                (firstParam.TokenType == TokenType.Matrix && firstParam.Count == 1))
            {
                if (firstParam.Count != 1)
                    return Token.Error("First parameter is an array. Scalar operation not possible. Pass single value");
                double[] temp = new double[secondParam.Count];
                for (int i = 0; i < secondParam.Count; i++)
                {
                    temp[i] = PerformArithmeticOperation(operation, firstParam.FirstValue, secondParam[i]);
                }
                double extra = firstParam.TokenType == TokenType.Matrix ? firstParam.Extra : secondParam.Extra;
                result = new Token(TokenType.Matrix, extra, temp);
            }
            else if (secondParam.TokenType == TokenType.Vector ||
                     (secondParam.TokenType == TokenType.Matrix && secondParam.Count == 1))
            {
                if (secondParam.Count != 1)
                    return Token.Error("Second parameter is an array. Scalar operation not possible. Pass single value");
                double[] temp = new double[firstParam.Count];
                for (int i = 0; i < firstParam.Count; i++)
                {
                    temp[i] = PerformArithmeticOperation(operation, firstParam[i], secondParam.FirstValue);
                }
                double extra = firstParam.TokenType == TokenType.Matrix ? firstParam.Extra : secondParam.Extra;
                result = new Token(TokenType.Matrix, extra, temp);
            }
            else /* Both operands are Matrix*/
            {
                /*        /, % and ^ return error!           */
                if (operation == "^" || operation == "%" || operation == "/")
                    return Token.Error("'" + operation + "' not defined for Matrices");

                /* "<" ,  ">" , "<=" and ">=" not used for matrices
                 */
                if (operation == "==" || operation == "!=")
                {
                    if (firstParam.Count != secondParam.Count ||
                        firstParam.Extra != secondParam.Extra)
                    {
                        return Token.Error("Argument matrices are not compatible for comparison.");
                    }
                    return CompareParams(operation, firstParam, secondParam);
                }

                double[] temp = new double[firstParam.Count];
                if (operation == "+" || operation == "-")
                {
                    if (firstParam.Count < 1 || secondParam.Count != firstParam.Count ||
                        firstParam.Extra != secondParam.Extra)
                        return Token.Error("Argument(s) not valid for the specified operation");

                    for (int i = 0; i < firstParam.Count; i++)
                    {
                        temp[i] = PerformArithmeticOperation(operation, firstParam[i], secondParam[i]);
                    }
                    result = new Token(TokenType.Matrix, firstParam.Extra, temp);
                }
                else if (operation == "*")
                {
                    int firstRows = (int)firstParam.Extra;
                    int firstCols = (int)((firstParam.Count) / firstParam.Extra);
                    int secondCols = (int)((secondParam.Count) / secondParam.Extra);

                    if (firstCols != secondParam.Extra)
                        return Token.Error("Columns of first matrix do not equal rows of second matrix");

                    double[] data = new double[firstRows * secondCols];
                    double value = 0;
                    for (int i = 0; i < firstRows; i++)
                    {
                        for (int j = 0; j < secondCols; j++)
                        {
                            value = 0;
                            for (int k = 0; k < firstCols; k++)
                            {
                                value += firstParam[i * firstCols + k] * secondParam[j + secondCols * k];
                            }
                            data[i * secondCols + j] = value;
                        }
                    }
                    result = new Token(TokenType.Matrix, firstRows, data);
                }
            }
            return result;
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)


Written By
Technical Lead https://mathiversity.com
Unknown
I am a full-stack developer. My skills include JavaScript, C#/.Net, MS Azure cloud etc. I love to work on complex programming tasks requiring deep analysis, planning and use of efficient algorithms and data structures.

Comments and Discussions