Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

How to read string, as math expression and solve in unity?

Discussion in 'Scripting' started by benjiHilton, Aug 21, 2019.

  1. benjiHilton

    benjiHilton

    Joined:
    Oct 5, 2015
    Posts:
    3
    Hi,
    All i need is for my script to be able to read a string as a mathematical expression, and solve it. It doesn't need to understand any complicated math (variables or formulas). Just pure basic algebra. Unity's version only works in the editor but doesn't work when i build. I've tried importing NCalc and Expressive-master, but i can't get them to work (never tried to import custom C#, .NET libraries with unity before). Any help/tips would be appreciated.
     
  2. csofranz

    csofranz

    Joined:
    Apr 29, 2017
    Posts:
    1,556
    It rather depends how complex your expressions are that Need to be parsed (if you are using parantheses, the Parser must be recursive). This may get you started.
     
  3. Vinayak-VC

    Vinayak-VC

    Joined:
    Apr 16, 2019
    Posts:
    60
    Code (CSharp):
    1.  
    2.  void Calculate()
    3.     {
    4.         foreach (char item in exp)
    5.         {
    6.             if (item == '+' || item == '-' || item == '/' || item == '*')
    7.             {
    8.                 Oprator += 1;
    9.                 Opratorarray.Add(item.ToString());
    10.             }
    11.             else
    12.             {
    13.                 if (Oprand == Oprator)
    14.                 {
    15.                     Oprand += 1;
    16.                     Oprandarray.Add(item.ToString());
    17.                 }
    18.                 else
    19.                 {
    20.                     Oprandarray[Oprator] = Oprandarray[Oprator] + item;
    21.                 }
    22.             }
    23.         }
    24.  
    25.         if (Oprand <= Oprator)
    26.         {
    27.             Debug.LogError("Expression is not valid");
    28.             return;
    29.         }
    30.         ans = float.Parse(Oprandarray[0]);
    31.  
    32.         foreach (string item in Opratorarray)
    33.         {
    34.             switch (item)
    35.             {
    36.                 case "+":
    37.                     ans += float.Parse(Oprandarray[count]);
    38.                     break;
    39.                 case "-":
    40.                     ans += float.Parse(Oprandarray[count]);
    41.                     break;
    42.                 case "/":
    43.                     ans /= float.Parse(Oprandarray[count]);
    44.                     break;
    45.                 case "*":
    46.                     ans *= float.Parse(Oprandarray[count]);
    47.                     break;
    48.                 default:
    49.                     Debug.LogError("Expression is not valid");
    50.                     break;
    51.             }
    52.             count += 1;
    53.         }
    54.  
    55.         Debug.Log("Answer is : "+ans);
    56.     }
    57.  
     
    Last edited: Aug 21, 2019
  4. csofranz

    csofranz

    Joined:
    Apr 29, 2017
    Posts:
    1,556
    Hey, that's a nice quick and dirty hack (love it), but I don't think it'll correctly evaluate

    3 + 4 * 5

    and it'll barf on

    3 + 4 * -5
     
  5. Vinayak-VC

    Vinayak-VC

    Joined:
    Apr 16, 2019
    Posts:
    60
    Yes But He said "Just pure basic algebra" ;);)
     
    aQemcozz likes this.
  6. csofranz

    csofranz

    Joined:
    Apr 29, 2017
    Posts:
    1,556
    Indeed :)

    ... but according to Ms Wormwood, a third grade teacher, 3 + 4 * 5 is basic algebra.
     
  7. Vinayak-VC

    Vinayak-VC

    Joined:
    Apr 16, 2019
    Posts:
    60
    3+4*5 will work but 3+4*-5 won't

    need to do some more work for that

    There is one more problem that

    5 + 3 * 2 = 11
    but this logic will return 16
    need to work on that as well
     
  8. csofranz

    csofranz

    Joined:
    Apr 29, 2017
    Posts:
    1,556
    Hmm. Looking at your code (which - again - I believe is really clever) I think it will evaluate

    3+4*5

    to 35 which is wrong (it should be 23)

    The problem is that your code currently does not regard precedence of Operation, and unfortunately I see no simple fix for that. The code also won't correctly handle

    -3+4

    but I beleive you can fix that by always treating - as unary operator, and insert a '+' if no other operator follows
     
  9. willemsenzo

    willemsenzo

    Joined:
    Nov 15, 2012
    Posts:
    585
  10. csofranz

    csofranz

    Joined:
    Apr 29, 2017
    Posts:
    1,556
  11. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    This is the evaluator I wrote for use in our code base:
    https://github.com/lordofduct/space...SpacepuppyUnityFramework/Dynamic/Evaluator.cs

    I attempt to keep the gc to a minimum when evaluating the strings by keeping generation of substrings to a minimum. And I wanted abilities for some built in functions as well as calling functions/properties on an object passed in... this accessing an object dynamically has a dependency on DynamicUtil which you would likely want to strip out.

    Basically I'm saying this is not plug and play... but could give you an idea of how to pull it off by ripping out the bits you want.
     
  12. willemsenzo

    willemsenzo

    Joined:
    Nov 15, 2012
    Posts:
    585
    Sorry I just woke up when I wrote that. Makes me curious why it didn't work for him, I managed to import it into Unity just fine, you just need to target the assembly as .NET 3.5.
     
  13. Boz0r

    Boz0r

    Joined:
    Feb 27, 2014
    Posts:
    419
    Look into Abstract Syntax Trees. I think there's a bunch of guides online for how to build one from parsing algebra in strings.
     
  14. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,741
    There are libraries by people which are presumably more robust than something you code yourself.
     
  15. willemsenzo

    willemsenzo

    Joined:
    Nov 15, 2012
    Posts:
    585
    Here an example I just made using NCalc. Download the zip and put the .dll in a folder called Plugins. The code looks something like this

    Code (CSharp):
    1. using UnityEngine;
    2. using NCalc;
    3.  
    4. public class Calculator : MonoBehaviour
    5. {
    6.     public void Start()
    7.     {
    8.         object answer = GetAnswer("10*20");
    9.         Debug.Log(answer.ToString());
    10.     }
    11.  
    12.     public object GetAnswer(string expression)
    13.     {
    14.         Expression ex = new Expression(expression);
    15.         return ex.Evaluate();
    16.     }
    17. }
    18.  
     

    Attached Files:

    Last edited: Aug 21, 2019
  16. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,580
    Lurking-Ninja likes this.
  17. Lurking-Ninja

    Lurking-Ninja

    Joined:
    Jan 20, 2015
    Posts:
    9,913
    Antypodish likes this.
  18. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,833
    N.B. "Algebra" means it does involve variables or formulas. I think you meant basic arithmetic.

    Wikipedia: "Elementary algebra differs from arithmetic in the use of abstractions, such as using letters to stand for numbers that are either unknown or allowed to take on many values."
     
    lordofduct likes this.
  19. samf1111

    samf1111

    Joined:
    Sep 18, 2019
    Posts:
    16
    retrived from: https://wiki.unity3d.com/index.php/ExpressionParser

    Code (CSharp):
    1. /* * * * * * * * * * * * * *
    2. * A simple expression parser
    3. * --------------------------
    4. *
    5. * The parser can parse a mathematical expression into a simple custom
    6. * expression tree. It can recognise methods and fields/contants which
    7. * are user extensible. It can also contain expression parameters which
    8. * are registrated automatically. An expression tree can be "converted"
    9. * into a delegate.
    10. *
    11. * Written by Bunny83
    12. * 2014-11-02
    13. *
    14. * Features:
    15. * - Elementary arithmetic [ + - * / ]
    16. * - Power [ ^ ]
    17. * - Brackets ( )
    18. * - Most function from System.Math (abs, sin, round, floor, min, ...)
    19. * - Constants ( e, PI )
    20. * - MultiValue return (quite slow, produce extra garbage each call)
    21. *
    22. * * * * * * * * * * * * * */
    23. using System.Linq;
    24. using System.Collections.Generic;
    25. namespace B83.ExpressionParser
    26. {
    27.     public interface IValue
    28.     {
    29.         double Value { get; }
    30.     }
    31.     public class Number : IValue
    32.     {
    33.         private double m_Value;
    34.         public double Value
    35.         {
    36.             get { return m_Value; }
    37.             set { m_Value = value; }
    38.         }
    39.         public Number(double aValue)
    40.         {
    41.             m_Value = aValue;
    42.         }
    43.         public override string ToString()
    44.         {
    45.             return "" + m_Value + "";
    46.         }
    47.     }
    48.     public class OperationSum : IValue
    49.     {
    50.         private IValue[] m_Values;
    51.         public double Value
    52.         {
    53.             get { return m_Values.Select(v => v.Value).Sum(); }
    54.         }
    55.         public OperationSum(params IValue[] aValues)
    56.         {
    57.             // collapse unnecessary nested sum operations.
    58.             List<IValue> v = new List<IValue>(aValues.Length);
    59.             foreach (var I in aValues)
    60.             {
    61.                 var sum = I as OperationSum;
    62.                 if (sum == null)
    63.                     v.Add(I);
    64.                 else
    65.                     v.AddRange(sum.m_Values);
    66.             }
    67.             m_Values = v.ToArray();
    68.         }
    69.         public override string ToString()
    70.         {
    71.             return "( " + string.Join(" + ", m_Values.Select(v => v.ToString()).ToArray()) + " )";
    72.         }
    73.     }
    74.     public class OperationProduct : IValue
    75.     {
    76.         private IValue[] m_Values;
    77.         public double Value
    78.         {
    79.             get { return m_Values.Select(v => v.Value).Aggregate((v1, v2) => v1 * v2); }
    80.         }
    81.         public OperationProduct(params IValue[] aValues)
    82.         {
    83.             m_Values = aValues;
    84.         }
    85.         public override string ToString()
    86.         {
    87.             return "( " + string.Join(" * ", m_Values.Select(v => v.ToString()).ToArray()) + " )";
    88.         }
    89.     }
    90.     public class OperationPower : IValue
    91.     {
    92.         private IValue m_Value;
    93.         private IValue m_Power;
    94.         public double Value
    95.         {
    96.             get { return System.Math.Pow(m_Value.Value, m_Power.Value); }
    97.         }
    98.         public OperationPower(IValue aValue, IValue aPower)
    99.         {
    100.             m_Value = aValue;
    101.             m_Power = aPower;
    102.         }
    103.         public override string ToString()
    104.         {
    105.             return "( " + m_Value + "^" + m_Power + " )";
    106.         }
    107.     }
    108.     public class OperationNegate : IValue
    109.     {
    110.         private IValue m_Value;
    111.         public double Value
    112.         {
    113.             get { return -m_Value.Value; }
    114.         }
    115.         public OperationNegate(IValue aValue)
    116.         {
    117.             m_Value = aValue;
    118.         }
    119.         public override string ToString()
    120.         {
    121.             return "( -" + m_Value + " )";
    122.         }
    123.     }
    124.     public class OperationReciprocal : IValue
    125.     {
    126.         private IValue m_Value;
    127.         public double Value
    128.         {
    129.             get { return 1.0 / m_Value.Value; }
    130.         }
    131.         public OperationReciprocal(IValue aValue)
    132.         {
    133.             m_Value = aValue;
    134.         }
    135.         public override string ToString()
    136.         {
    137.             return "( 1/" + m_Value + " )";
    138.         }
    139.     }
    140.     public class MultiParameterList : IValue
    141.     {
    142.         private IValue[] m_Values;
    143.         public IValue[] Parameters { get { return m_Values; } }
    144.         public double Value
    145.         {
    146.             get { return m_Values.Select(v => v.Value).FirstOrDefault(); }
    147.         }
    148.         public MultiParameterList(params IValue[] aValues)
    149.         {
    150.             m_Values = aValues;
    151.         }
    152.         public override string ToString()
    153.         {
    154.             return string.Join(", ", m_Values.Select(v => v.ToString()).ToArray());
    155.         }
    156.     }
    157.     public class CustomFunction : IValue
    158.     {
    159.         private IValue[] m_Params;
    160.         private System.Func<double[], double> m_Delegate;
    161.         private string m_Name;
    162.         public double Value
    163.         {
    164.             get
    165.             {
    166.                 if (m_Params == null)
    167.                     return m_Delegate(null);
    168.                 return m_Delegate(m_Params.Select(p => p.Value).ToArray());
    169.             }
    170.         }
    171.         public CustomFunction(string aName, System.Func<double[], double> aDelegate, params IValue[] aValues)
    172.         {
    173.             m_Delegate = aDelegate;
    174.             m_Params = aValues;
    175.             m_Name = aName;
    176.         }
    177.         public override string ToString()
    178.         {
    179.             if (m_Params == null)
    180.                 return m_Name;
    181.             return m_Name + "( " + string.Join(", ", m_Params.Select(v => v.ToString()).ToArray()) + " )";
    182.         }
    183.     }
    184.     public class Parameter : Number
    185.     {
    186.         public string Name { get; private set; }
    187.         public override string ToString()
    188.         {
    189.             return Name+"["+base.ToString()+"]";
    190.         }
    191.         public Parameter(string aName) : base(0)
    192.         {
    193.             Name = aName;
    194.         }
    195.     }
    196.     public class Expression : IValue
    197.     {
    198.         public Dictionary<string, Parameter> Parameters = new Dictionary<string, Parameter>();
    199.         public IValue ExpressionTree { get; set; }
    200.         public double Value
    201.         {
    202.             get { return ExpressionTree.Value; }
    203.         }
    204.         public double[] MultiValue
    205.         {
    206.             get {
    207.                 var t = ExpressionTree as MultiParameterList;
    208.                 if (t != null)
    209.                 {
    210.                     double[] res = new double[t.Parameters.Length];
    211.                     for (int i = 0; i < res.Length; i++)
    212.                         res[i] = t.Parameters[i].Value;
    213.                     return res;
    214.                 }
    215.                 return null;
    216.             }
    217.         }
    218.         public override string ToString()
    219.         {
    220.             return ExpressionTree.ToString();
    221.         }
    222.         public ExpressionDelegate ToDelegate(params string[] aParamOrder)
    223.         {
    224.             var parameters = new List<Parameter>(aParamOrder.Length);
    225.             for(int i = 0; i < aParamOrder.Length; i++)
    226.             {
    227.                 if (Parameters.ContainsKey(aParamOrder[i]))
    228.                     parameters.Add(Parameters[aParamOrder[i]]);
    229.                 else
    230.                     parameters.Add(null);
    231.             }
    232.             var parameters2 = parameters.ToArray();
    233.             return (p) => Invoke(p, parameters2);
    234.         }
    235.         public MultiResultDelegate ToMultiResultDelegate(params string[] aParamOrder)
    236.         {
    237.             var parameters = new List<Parameter>(aParamOrder.Length);
    238.             for (int i = 0; i < aParamOrder.Length; i++)
    239.             {
    240.                 if (Parameters.ContainsKey(aParamOrder[i]))
    241.                     parameters.Add(Parameters[aParamOrder[i]]);
    242.                 else
    243.                     parameters.Add(null);
    244.             }
    245.             var parameters2 = parameters.ToArray();
    246.             return (p) => InvokeMultiResult(p, parameters2);
    247.         }
    248.         double Invoke(double[] aParams, Parameter[] aParamList)
    249.         {
    250.             int count = System.Math.Min(aParamList.Length, aParams.Length);
    251.             for (int i = 0; i < count; i++ )
    252.             {
    253.                 if (aParamList[i] != null)
    254.                     aParamList[i].Value = aParams[i];
    255.             }
    256.             return Value;
    257.         }
    258.         double[] InvokeMultiResult(double[] aParams, Parameter[] aParamList)
    259.         {
    260.             int count = System.Math.Min(aParamList.Length, aParams.Length);
    261.             for (int i = 0; i < count; i++)
    262.             {
    263.                 if (aParamList[i] != null)
    264.                     aParamList[i].Value = aParams[i];
    265.             }
    266.             return MultiValue;
    267.         }
    268.         public static Expression Parse(string aExpression)
    269.         {
    270.             return new ExpressionParser().EvaluateExpression(aExpression);
    271.         }
    272.         public class ParameterException : System.Exception { public ParameterException(string aMessage) : base(aMessage) { } }
    273.     }
    274.     public delegate double ExpressionDelegate(params double[] aParams);
    275.     public delegate double[] MultiResultDelegate(params double[] aParams);
    276.     public class ExpressionParser
    277.     {
    278.         private List<string> m_BracketHeap = new List<string>();
    279.         private Dictionary<string, System.Func<double>> m_Consts = new Dictionary<string, System.Func<double>>();
    280.         private Dictionary<string, System.Func<double[], double>> m_Funcs = new Dictionary<string, System.Func<double[], double>>();
    281.         private Expression m_Context;
    282.         public ExpressionParser()
    283.         {
    284.             var rnd = new System.Random();
    285.             m_Consts.Add("PI", () => System.Math.PI);
    286.             m_Consts.Add("e", () => System.Math.E);
    287.             m_Funcs.Add("sqrt", (p) => System.Math.Sqrt(p.FirstOrDefault()));
    288.             m_Funcs.Add("abs", (p) => System.Math.Abs(p.FirstOrDefault()));
    289.             m_Funcs.Add("ln", (p) => System.Math.Log(p.FirstOrDefault()));
    290.             m_Funcs.Add("floor", (p) => System.Math.Floor(p.FirstOrDefault()));
    291.             m_Funcs.Add("ceiling", (p) => System.Math.Ceiling(p.FirstOrDefault()));
    292.             m_Funcs.Add("round", (p) => System.Math.Round(p.FirstOrDefault()));
    293.             m_Funcs.Add("sin", (p) => System.Math.Sin(p.FirstOrDefault()));
    294.             m_Funcs.Add("cos", (p) => System.Math.Cos(p.FirstOrDefault()));
    295.             m_Funcs.Add("tan", (p) => System.Math.Tan(p.FirstOrDefault()));
    296.             m_Funcs.Add("asin", (p) => System.Math.Asin(p.FirstOrDefault()));
    297.             m_Funcs.Add("acos", (p) => System.Math.Acos(p.FirstOrDefault()));
    298.             m_Funcs.Add("atan", (p) => System.Math.Atan(p.FirstOrDefault()));
    299.             m_Funcs.Add("atan2", (p) => System.Math.Atan2(p.FirstOrDefault(),p.ElementAtOrDefault(1)));
    300.             //System.Math.Floor
    301.             m_Funcs.Add("min", (p) => System.Math.Min(p.FirstOrDefault(), p.ElementAtOrDefault(1)));
    302.             m_Funcs.Add("max", (p) => System.Math.Max(p.FirstOrDefault(), p.ElementAtOrDefault(1)));
    303.             m_Funcs.Add("rnd", (p) =>
    304.             {
    305.                 if (p.Length == 2)
    306.                     return p[0] + rnd.NextDouble() * (p[1] - p[0]);
    307.                 if (p.Length == 1)
    308.                     return rnd.NextDouble() * p[0];
    309.                 return rnd.NextDouble();
    310.             });
    311.         }
    312.         public void AddFunc(string aName, System.Func<double[],double> aMethod)
    313.         {
    314.             if (m_Funcs.ContainsKey(aName))
    315.                 m_Funcs[aName] = aMethod;
    316.             else
    317.                 m_Funcs.Add(aName, aMethod);
    318.         }
    319.         public void AddConst(string aName, System.Func<double> aMethod)
    320.         {
    321.             if (m_Consts.ContainsKey(aName))
    322.                 m_Consts[aName] = aMethod;
    323.             else
    324.                 m_Consts.Add(aName, aMethod);
    325.         }
    326.         public void RemoveFunc(string aName)
    327.         {
    328.             if (m_Funcs.ContainsKey(aName))
    329.                 m_Funcs.Remove(aName);
    330.         }
    331.         public void RemoveConst(string aName)
    332.         {
    333.             if (m_Consts.ContainsKey(aName))
    334.                 m_Consts.Remove(aName);
    335.         }
    336.         int FindClosingBracket(ref string aText, int aStart, char aOpen, char aClose)
    337.         {
    338.             int counter = 0;
    339.             for (int i = aStart; i < aText.Length; i++)
    340.             {
    341.                 if (aText[i] == aOpen)
    342.                     counter++;
    343.                 if (aText[i] == aClose)
    344.                     counter--;
    345.                 if (counter == 0)
    346.                     return i;
    347.             }
    348.             return -1;
    349.         }
    350.         void SubstitudeBracket(ref string aExpression, int aIndex)
    351.         {
    352.             int closing = FindClosingBracket(ref aExpression, aIndex, '(', ')');
    353.             if (closing > aIndex + 1)
    354.             {
    355.                 string inner = aExpression.Substring(aIndex + 1, closing - aIndex - 1);
    356.                 m_BracketHeap.Add(inner);
    357.                 string sub = "&" + (m_BracketHeap.Count - 1) + ";";
    358.                 aExpression = aExpression.Substring(0, aIndex) + sub + aExpression.Substring(closing + 1);
    359.             }
    360.             else throw new ParseException("Bracket not closed!");
    361.         }
    362.         IValue Parse(string aExpression)
    363.         {
    364.             aExpression = aExpression.Trim();
    365.             int index = aExpression.IndexOf('(');
    366.             while (index >= 0)
    367.             {
    368.                 SubstitudeBracket(ref aExpression, index);
    369.                 index = aExpression.IndexOf('(');
    370.             }
    371.             if (aExpression.Contains(','))
    372.             {
    373.                 string[] parts = aExpression.Split(',');
    374.                 List<IValue> exp = new List<IValue>(parts.Length);
    375.                 for (int i = 0; i < parts.Length; i++)
    376.                 {
    377.                     string s = parts[i].Trim();
    378.                     if (!string.IsNullOrEmpty(s))
    379.                         exp.Add(Parse(s));
    380.                 }
    381.                 return new MultiParameterList(exp.ToArray());
    382.             }
    383.             else if (aExpression.Contains('+'))
    384.             {
    385.                 string[] parts = aExpression.Split('+');
    386.                 List<IValue> exp = new List<IValue>(parts.Length);
    387.                 for (int i = 0; i < parts.Length; i++)
    388.                 {
    389.                     string s = parts[i].Trim();
    390.                     if (!string.IsNullOrEmpty(s))
    391.                         exp.Add(Parse(s));
    392.                 }
    393.                 if (exp.Count == 1)
    394.                     return exp[0];
    395.                 return new OperationSum(exp.ToArray());
    396.             }
    397.             else if (aExpression.Contains('-'))
    398.             {
    399.                 string[] parts = aExpression.Split('-');
    400.                 List<IValue> exp = new List<IValue>(parts.Length);
    401.                 if (!string.IsNullOrEmpty(parts[0].Trim()))
    402.                     exp.Add(Parse(parts[0]));
    403.                 for (int i = 1; i < parts.Length; i++)
    404.                 {
    405.                     string s = parts[i].Trim();
    406.                     if (!string.IsNullOrEmpty(s))
    407.                         exp.Add(new OperationNegate(Parse(s)));
    408.                 }
    409.                 if (exp.Count == 1)
    410.                     return exp[0];
    411.                 return new OperationSum(exp.ToArray());
    412.             }
    413.             else if (aExpression.Contains('*'))
    414.             {
    415.                 string[] parts = aExpression.Split('*');
    416.                 List<IValue> exp = new List<IValue>(parts.Length);
    417.                 for (int i = 0; i < parts.Length; i++)
    418.                 {
    419.                     exp.Add(Parse(parts[i]));
    420.                 }
    421.                 if (exp.Count == 1)
    422.                     return exp[0];
    423.                 return new OperationProduct(exp.ToArray());
    424.             }
    425.             else if (aExpression.Contains('/'))
    426.             {
    427.                 string[] parts = aExpression.Split('/');
    428.                 List<IValue> exp = new List<IValue>(parts.Length);
    429.                 if (!string.IsNullOrEmpty(parts[0].Trim()))
    430.                     exp.Add(Parse(parts[0]));
    431.                 for (int i = 1; i < parts.Length; i++)
    432.                 {
    433.                     string s = parts[i].Trim();
    434.                     if (!string.IsNullOrEmpty(s))
    435.                         exp.Add(new OperationReciprocal(Parse(s)));
    436.                 }
    437.                 return new OperationProduct(exp.ToArray());
    438.             }
    439.             else if (aExpression.Contains('^'))
    440.             {
    441.                 int pos = aExpression.IndexOf('^');
    442.                 var val = Parse(aExpression.Substring(0, pos));
    443.                 var pow = Parse(aExpression.Substring(pos + 1));
    444.                 return new OperationPower(val, pow);
    445.             }
    446.             int pPos = aExpression.IndexOf("&");
    447.             if (pPos > 0)
    448.             {
    449.                 string fName = aExpression.Substring(0, pPos);
    450.                 foreach (var M in m_Funcs)
    451.                 {
    452.                     if (fName == M.Key)
    453.                     {
    454.                         var inner = aExpression.Substring(M.Key.Length);
    455.                         var param = Parse(inner);
    456.                         var multiParams = param as MultiParameterList;
    457.                         IValue[] parameters;
    458.                         if (multiParams != null)
    459.                             parameters = multiParams.Parameters;
    460.                         else
    461.                             parameters = new IValue[] { param };
    462.                         return new CustomFunction(M.Key, M.Value, parameters);
    463.                     }
    464.                 }
    465.             }
    466.             foreach (var C in m_Consts)
    467.             {
    468.                 if (aExpression == C.Key)
    469.                 {
    470.                     return new CustomFunction(C.Key,(p)=>C.Value(),null);
    471.                 }
    472.             }
    473.             int index2a = aExpression.IndexOf('&');
    474.             int index2b = aExpression.IndexOf(';');
    475.             if (index2a >= 0 && index2b >= 2)
    476.             {
    477.                 var inner = aExpression.Substring(index2a + 1, index2b - index2a - 1);
    478.                 int bracketIndex;
    479.                 if (int.TryParse(inner, out bracketIndex) && bracketIndex >= 0 && bracketIndex < m_BracketHeap.Count)
    480.                 {
    481.                     return Parse(m_BracketHeap[bracketIndex]);
    482.                 }
    483.                 else
    484.                     throw new ParseException("Can't parse substitude token");
    485.             }
    486.             double doubleValue;
    487.             if (double.TryParse(aExpression, out doubleValue))
    488.             {
    489.                 return new Number(doubleValue);
    490.             }
    491.             if (ValidIdentifier(aExpression))
    492.             {
    493.                 if (m_Context.Parameters.ContainsKey(aExpression))
    494.                     return m_Context.Parameters[aExpression];
    495.                 var val = new Parameter(aExpression);
    496.                 m_Context.Parameters.Add(aExpression, val);
    497.                 return val;
    498.             }
    499.             throw new ParseException("Reached unexpected end within the parsing tree");
    500.         }
    501.         private bool ValidIdentifier(string aExpression)
    502.         {
    503.             aExpression = aExpression.Trim();
    504.             if (string.IsNullOrEmpty(aExpression))
    505.                 return false;
    506.             if (aExpression.Length < 1)
    507.                 return false;
    508.             if (aExpression.Contains(" "))
    509.                 return false;
    510.             if (!"abcdefghijklmnopqrstuvwxyz§$".Contains(char.ToLower(aExpression[0])))
    511.                 return false;
    512.             if (m_Consts.ContainsKey(aExpression))
    513.                 return false;
    514.             if (m_Funcs.ContainsKey(aExpression))
    515.                 return false;
    516.             return true;
    517.         }
    518.         public Expression EvaluateExpression(string aExpression)
    519.         {
    520.             var val = new Expression();
    521.             m_Context = val;
    522.             val.ExpressionTree = Parse(aExpression);
    523.             m_Context = null;
    524.             m_BracketHeap.Clear();
    525.             return val;
    526.         }
    527.         public double Evaluate(string aExpression)
    528.         {
    529.             return EvaluateExpression(aExpression).Value;
    530.         }
    531.         public static double Eval(string aExpression)
    532.         {
    533.             return new ExpressionParser().Evaluate(aExpression);
    534.         }
    535.         public class ParseException : System.Exception { public ParseException(string aMessage) : base(aMessage) { } }
    536.     }
    537. }
    how to use:

    Code (CSharp):
    1.  
    2. using B83.ExpressionParser;
    3.  
    4. void Start()
    5. {
    6.     ExpressionParser parser = new ExpressionParser();
    7.     Expression exp = parser.EvaluateExpression("(5+3)*8^2-5*(-2)");
    8.     Debug.Log("Result: " + exp.Value);  // prints: "Result: 522"
    9. }
    10.  
     
  20. thorham3011

    thorham3011

    Joined:
    Sep 7, 2017
    Posts:
    25
    .Net has a built in function in the DataTable class:

    Code (csharp):
    1. System.Data.DataTable table = new System.Data.DataTable();
    2.  
    3. string expression = "(123 + 456) * 789";
    4.  
    5. try
    6. {
    7.    print(table.Compute(expression, ""));
    8. }
    9.  
    10. catch
    11. {
    12.    // handle input error
    13. }
     
    kmedved, gauravbc46 and StarManta like this.
  21. Bireswar

    Bireswar

    Joined:
    Nov 29, 2020
    Posts:
    5
    There's a quick and easy solution to evaluate expressions in form of strings

    Code (CSharp):
    1. using UnityEngine;
    2. using TMPro;
    3.  
    4. public class Result : MonoBehaviour
    5. {
    6.     [HideInInspector]
    7.     public static TMP_Text result;
    8.  
    9.     private static double ans;
    10.  
    11.     void Start()
    12.     {
    13.         result = GetComponent<TMP_Text>();
    14.     }
    15.  
    16.     public static void ExpressionAnalyzer(string expression)
    17.     {
    18.  
    19.         ans = 0;
    20.         ExpressionEvaluator.Evaluate(expression, out ans);
    21.         if (ans - (int)ans == 0)
    22.             result.text = ((int)ans).ToString();
    23.         else
    24.             result.text = ans.ToString();
    25.     }
    26. }
    27.  
    Here's the Unity Manual Reference
     
    Last edited: May 25, 2022
  22. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,531
    The ExpressionEvaluator class is a UnityEditor only feature. So it can not be used at runtime. The OP already mentioned this:
     
    Last edited: Apr 25, 2022
  23. Bireswar

    Bireswar

    Joined:
    Nov 29, 2020
    Posts:
    5
    I have tested it myself using 2022.1.0b5 Unity editor version by building a calculator based wholly on this code. Maybe if you upgrade to the latest version it might work.
     
  24. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    The documentation shows that it was moved from UnityEditor namespace to UnityEngine namespace in version 2022, so yes, that's why it's working for you in your project.
     
    GGsparta, Bunny83 and PraetorBlue like this.
  25. koirat

    koirat

    Joined:
    Jul 7, 2012
    Posts:
    2,009
    I'm using my own evaluator based on postfix notation. It was relatively easy to make and is very fast to parse and evaluate.

    It's a good move to shift Evaluator to UnityEngine, pity it lacks custom functions that could be added to parsed equation and ability to add variables instead of constant numbers in equation.
    Now every time you need to evaluate it for different variables you will have to edit the string.
     
    Last edited: May 10, 2022
    Bunny83 likes this.
  26. Bireswar

    Bireswar

    Joined:
    Nov 29, 2020
    Posts:
    5
    Yeah, I said that maybe a version facility. With this thing it becomes relatively easy to do calculation
     
  27. progCan

    progCan

    Joined:
    Apr 27, 2021
    Posts:
    5