public Node Resolve(Node expression)
        {
            switch (expression)
            {
            case UnaryOperatorNode un:
                switch (un.NodeType)
                {
                case NodeTypes.Plus: return(Node.Plus(Resolve(un.Argument)));

                case NodeTypes.Minus: return(Node.Minus(Resolve(un.Argument)));

                case NodeTypes.Not: return(Node.Not(Resolve(un.Argument)));
                }

                break;

            case BinaryOperatorNode bn:
                var left  = Resolve(bn.Left);
                var right = Resolve(bn.Right);
                switch (bn.NodeType)
                {
                case NodeTypes.Add: return(Node.Add(left, right));

                case NodeTypes.Subtract: return(Node.Subtract(left, right));

                case NodeTypes.Multiply: return(Node.Multiply(left, right));

                case NodeTypes.Divide: return(Node.Divide(left, right));

                case NodeTypes.Modulo: return(Node.Modulo(left, right));

                case NodeTypes.LessThan: return(Node.LessThan(left, right));

                case NodeTypes.GreaterThan: return(Node.GreaterThan(left, right));

                case NodeTypes.LessThanOrEqual: return(Node.LessThanOrEqual(left, right));

                case NodeTypes.GreaterThanOrEqual: return(Node.GreaterThanOrEqual(left, right));

                case NodeTypes.Equals: return(Node.Equals(left, right));

                case NodeTypes.NotEquals: return(Node.NotEquals(left, right));

                case NodeTypes.And: return(Node.And(left, right));

                case NodeTypes.Or: return(Node.Or(left, right));

                case NodeTypes.Xor: return(Node.Xor(left, right));

                case NodeTypes.Pow: return(Node.Power(left, right));
                }

                break;

            case TernaryOperatorNode tn:
                return(Node.Conditional(Resolve(tn.Condition), Resolve(tn.IfTrue), Resolve(tn.IfFalse)));

            case FunctionNode fn:
                var args = new Node[fn.Arguments.Count];
                for (var i = 0; i < args.Length; i++)
                {
                    args[i] = Resolve(fn.Arguments[i]);
                }

                var funtionUpdated = Node.Function(fn.Name, args);
                if (FunctionMap != null && FunctionMap.TryGetValue(fn.Name, out var function))
                {
                    if (function is StaticResolverFunction staticResolverFunction)
                    {
                        var i = 0;
                        foreach (VariableNode argument in staticResolverFunction.Arguments)
                        {
                            VariableMap[argument.Name] = args[i];
                            i++;
                        }

                        var functionBodyResolved = Resolve(staticResolverFunction.GetBody());
                        return(functionBodyResolved);
                    }

                    if (function is DynamicResolverFunction dynamicResolverFunction)
                    {
                        var functionBodyResolved = dynamicResolverFunction.GetBody(args);
                        return(functionBodyResolved);
                    }
                }

                return(funtionUpdated);

            case ConstantNode cn:
                return(cn);

            case VariableNode vn:
                if (VariableMap != null && vn.NodeType == NodeTypes.Variable && VariableMap.TryGetValue(vn.Name, out var mapped))
                {
                    return(Resolve(mapped));
                }
                else
                {
                    var vargs = new VariableFoundEventArgs <VariableNode>(this, vn);
                    OnUnknownVariableFound(vargs);
                    if (vargs.Created)
                    {
                        return(vargs.Result);
                    }
                    else
                    {
                        return(vn);
                    }
                }
            }

            return(expression);
        }
 /// <summary>
 /// Called when a variable was found.
 /// </summary>
 /// <param name="args">The event arguments.</param>
 protected virtual void OnUnknownVariableFound(VariableFoundEventArgs <VariableNode> args) => UnknownVariableFound?.Invoke(this, args);