public DekiScriptExpression Visit(DekiScriptCall expr, DekiScriptExpressionEvaluationState state)
        {
            DekiScriptExpression prefix    = expr.Prefix.VisitWith(this, state);
            DekiScriptExpression arguments = expr.Arguments.VisitWith(this, state);
            DekiScriptExpression result    = expr.IsCurryOperation ? DekiScriptExpression.Curry(expr.Location, prefix, arguments) : DekiScriptExpression.Call(expr.Location, prefix, arguments);

            // check if prefix has been resolved to a uri and the arguments have all been resolved
            if ((prefix is DekiScriptUri) && (arguments is DekiScriptLiteral))
            {
                // check if the uri can be resolved to a native idempotent function
                var descriptor = state.Runtime.ResolveRegisteredFunctionUri(((DekiScriptUri)result).Value);
                if ((descriptor != null) && descriptor.IsIdempotent)
                {
                    // evaluate function, since it never changes
                    return(state.Pop(DekiScriptExpression.Call(expr.Location, prefix, arguments).VisitWith(DekiScriptExpressionEvaluation.Instance, state)));
                }
            }
            return(result);
        }
        public DekiScriptExpression Visit(DekiScriptCall expr, DekiScriptOptimizerState state)
        {
            DekiScriptExpression prefix    = expr.Prefix.VisitWith(this, state);
            DekiScriptExpression arguments = expr.Arguments.VisitWith(this, state);
            DekiScriptExpression result    = new DekiScriptCall(expr.Line, expr.Column, prefix, arguments, expr.IsCurryOperation);

            // check if prefix has been resolved to a uri and the arguments have all been resolved
            if ((prefix is DekiScriptUri) && (arguments is DekiScriptLiteral))
            {
                DekiScriptFunction function;

                // check if the uri can be resolved to a native idempotent function
                if (
                    DekiScriptLibrary.Functions.TryGetValue(((DekiScriptUri)prefix).Value, out function) &&
                    (function is DekiScriptFunctionNative) &&
                    ((DekiScriptFunctionNative)function).IsIdempotent
                    )
                {
                    // evaluate function, since it never changes
                    return(new DekiScriptCall(expr.Line, expr.Column, prefix, arguments, false).Evaluate(state.Env));
                }
            }
            return(result);
        }
        public DekiScriptOutputBuffer.Range Visit(DekiScriptCall expr, DekiScriptExpressionEvaluationState state)
        {
            state.ThrowIfTimedout();

            // evaluate prefix
            DekiScriptLiteral prefix = state.Pop(expr.Prefix.VisitWith(this, state));

            if (prefix.ScriptType != DekiScriptType.URI)
            {
                if (prefix.ScriptType == DekiScriptType.NIL)
                {
                    throw new DekiScriptUndefinedNameException(expr.Location, expr.Prefix.ToString());
                }
                else
                {
                    throw new DekiScriptBadTypeException(expr.Location, prefix.ScriptType, new[] { DekiScriptType.URI });
                }
            }

            // evaluate arguments
            DekiScriptLiteral arguments = state.Pop(expr.Arguments.VisitWith(this, state));

            if ((arguments.ScriptType != DekiScriptType.MAP) && (arguments.ScriptType != DekiScriptType.LIST))
            {
                throw new DekiScriptBadTypeException(expr.Location, arguments.ScriptType, new[] { DekiScriptType.MAP, DekiScriptType.LIST });
            }

            // check if the URI was curried
            DekiScriptUri uri = (DekiScriptUri)prefix;

            if (!uri.Arguments.IsNil)
            {
                switch (uri.Arguments.ScriptType)
                {
                case DekiScriptType.LIST:

                    // append argument to list
                    DekiScriptList list = new DekiScriptList((DekiScriptList)uri.Arguments);
                    list.Add(arguments);
                    arguments = list;
                    break;

                case DekiScriptType.MAP:
                    if (arguments.ScriptType == DekiScriptType.MAP)
                    {
                        // concatenate both maps
                        DekiScriptMap map = new DekiScriptMap();
                        map.AddRange((DekiScriptMap)uri.Arguments);
                        map.AddRange((DekiScriptMap)arguments);
                        arguments = map;
                    }
                    else if ((arguments.ScriptType != DekiScriptType.LIST) || ((DekiScriptList)arguments).Value.Count > 0)
                    {
                        // we can't append a list to a map
                        throw new DekiScriptBadTypeException(expr.Location, arguments.ScriptType, new[] { DekiScriptType.MAP });
                    }
                    break;

                default:
                    throw new DekiScriptBadTypeException(expr.Location, arguments.ScriptType, new[] { DekiScriptType.MAP, DekiScriptType.LIST });
                }
            }

            // check if this is an invocation or curry operation
            if (expr.IsCurryOperation)
            {
                return(state.Push(new DekiScriptUri(uri.Value, arguments)));
            }

            // invoke function
            try {
                return(state.Push(state.Runtime.Invoke(expr.Location, uri.Value, arguments, state.Env)));
            } catch (DekiScriptFatalException) {
                throw;
            } catch (Exception e) {
                var descriptor = state.Runtime.ResolveRegisteredFunctionUri(uri.Value);
                throw new DekiScriptInvokeException(expr.Location, uri.Value, (descriptor != null) ? descriptor.Name : uri.Value.ToString(), e);
            }
        }
        public DekiScriptLiteral Visit(DekiScriptCall expr, DekiScriptEnv env)
        {
            // evaluate prefix
            DekiScriptLiteral prefix = expr.Prefix.VisitWith(this, env);

            if (prefix.ScriptType != DekiScriptType.URI)
            {
                if (prefix.ScriptType == DekiScriptType.NIL)
                {
                    throw new DekiScriptUndefinedNameException(expr.Line, expr.Column, expr.Prefix.ToString());
                }
                else
                {
                    throw new DekiScriptBadTypeException(expr.Line, expr.Column, prefix.ScriptType, new DekiScriptType[] { DekiScriptType.URI });
                }
            }

            // evaluate arguments
            DekiScriptLiteral arguments = expr.Arguments.VisitWith(this, env);

            if ((arguments.ScriptType != DekiScriptType.MAP) && (arguments.ScriptType != DekiScriptType.LIST))
            {
                throw new DekiScriptBadTypeException(expr.Line, expr.Column, arguments.ScriptType, new DekiScriptType[] { DekiScriptType.MAP, DekiScriptType.LIST });
            }

            // check if the URI was curried
            DekiScriptUri uri = (DekiScriptUri)prefix;

            if (!uri.Arguments.IsNil)
            {
                switch (uri.Arguments.ScriptType)
                {
                case DekiScriptType.LIST:

                    // append argument to list
                    DekiScriptList list = new DekiScriptList((DekiScriptList)uri.Arguments);
                    list.Add(arguments);
                    arguments = list;
                    break;

                case DekiScriptType.MAP:
                    if (arguments.ScriptType == DekiScriptType.MAP)
                    {
                        // concatenate both maps
                        DekiScriptMap map = new DekiScriptMap();
                        map.AddRange((DekiScriptMap)uri.Arguments);
                        map.AddRange((DekiScriptMap)arguments);
                        arguments = map;
                    }
                    else if ((arguments.ScriptType != DekiScriptType.LIST) || ((DekiScriptList)arguments).Value.Count > 0)
                    {
                        // we can't append a list to a map
                        throw new DekiScriptBadTypeException(expr.Line, expr.Column, arguments.ScriptType, new DekiScriptType[] { DekiScriptType.MAP });
                    }
                    break;

                default:
                    throw new DekiScriptBadTypeException(expr.Line, expr.Column, arguments.ScriptType, new DekiScriptType[] { DekiScriptType.MAP, DekiScriptType.LIST });
                }
            }

            // check if this is an invocation or curry operation
            if (expr.IsCurryOperation)
            {
                return(new DekiScriptUri(uri.Value, arguments));
            }
            else
            {
                // invoke function
                return(Coroutine.Invoke(DekiScriptRuntime.Invoke, uri.Value, arguments, env, new Result <DekiScriptLiteral>()).Wait());
            }
        }