/// <summary>
        /// Look up a named value. If there is no value with the given
        /// <paramref name="name"/> (and optional <paramref name="qualifier"/>),
        /// an exception is thrown.
        /// </summary>
        /// <param name="name">The name, required.</param>
        /// <param name="qualifier">The qualifier, optional.</param>
        /// <returns>
        /// The value bound to the given name.
        /// </returns>
        public override object Lookup(string name, string qualifier)
        {
            if (string.IsNullOrEmpty(qualifier))
            {
                object value;
                if (_values.TryGetValue(name, out value))
                {
                    return(value);
                }

                var funKey = new FunKey(name);
                if (_functions.ContainsKey(funKey))
                {
                    return(new Function(name));
                }

                value = LookupUniqueField(name);
                return(value == DBNull.Value ? null : value);
            }

            if (_rows.TryGetValue(qualifier, out INamedValues row))
            {
                if (row.Exists(name))
                {
                    object value = row.GetValue(name);
                    return(value == DBNull.Value ? null : value);
                }
            }

            throw LookupError("No such field: {0}.{1}", qualifier, name);
        }
        /// <summary>
        /// Register the given function (<paramref name="methodInfo"/>)
        /// with the given <paramref name="name"/> in this environment.
        /// <para/>
        /// You can register more than one function with the same name,
        /// if the functions differ in their number of parameters (arity).
        /// The evaluator will choose the actual function by the actual
        /// number of arguments at the time of invocation.
        /// </summary>
        /// <param name="name">The name (required)</param>
        /// <param name="methodInfo">The method (required)</param>
        /// <param name="state">The method's state (required for non-static methods)</param>
        /// <remarks>
        /// Later registrations overwrite earlier registrations
        /// for the same name/arity combination.
        /// <para/>
        /// The method can be static or instance or lambda; <paramref name="state"/>
        /// will be the implicit "this" parameter passed to all non-static methods
        /// (static methods ignore this parameter).
        /// <para/>
        /// The method need not be public (it is invoked through reflection).
        /// </remarks>
        public void Register(string name, MethodInfo methodInfo, object state = null)
        {
            Assert.ArgumentNotNullOrEmpty(name, nameof(name));
            Assert.ArgumentNotNull(methodInfo, nameof(methodInfo));

            var nameKey = new FunKey(name);
            int arity   = GetArity(methodInfo);

            _functions[nameKey] = null;

            var funKey = new FunKey(name, arity);

            _functions[funKey] = new Closure(methodInfo, state);
        }
        /// <summary>
        /// Invoke the given <paramref name="function"/> with the given
        /// <paramref name="args"/> and return the resulting value.
        /// </summary>
        /// <param name="function"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        /// <remarks>
        /// The function's parameters and the arguments passed must agree.
        /// If several functions with the same name were registered, the
        /// one with the same number of parameters as arguments are passed
        /// will be called. If there's no function with as many parameters
        /// as there are arguments, but there's a function with exactly
        /// one parameter of type array of object, then it will be called.
        /// Otherwise, an exception will be thrown.
        /// </remarks>
        public override object Invoke(Function function, params object[] args)
        {
            string name  = function.Name;
            int    arity = args?.Length ?? 0;

            Closure closure;

            var key = new FunKey(name, arity);

            if (_functions.TryGetValue(key, out closure))
            {
                return(closure.Invoke(args));
            }

            key = new FunKey(name, -1);             // varargs
            if (_functions.TryGetValue(key, out closure))
            {
                return(closure.Invoke(new object[] { args }));
            }

            throw InvocationError("No such function: {0}/{1}", function.Name, arity);
        }