Base class for the abstract syntax tree of a scriban program.
Example #1
0
        private static object Round(TemplateContext context, ScriptNode callerContext, ScriptArray parameters)
        {
            if (parameters.Count < 1 || parameters.Count > 2)
            {
                throw new ScriptRuntimeException(callerContext.Span, $"Unexpected number of arguments [{parameters.Count}] for math.round. Expecting at least 1 parameter <precision>? <value>");
            }

            var value = ScriptValueConverter.ToDouble(callerContext.Span, parameters[parameters.Count - 1]);
            int precision = 0;
            if (parameters.Count == 2)
            {
                precision = ScriptValueConverter.ToInt(callerContext.Span, parameters[0]);
            }

            return Round(precision, value);
        }
 public object Invoke(TemplateContext context, ScriptNode callerContext, ScriptArray parameters, ScriptBlockStatement blockStatement)
 {
     return(_customFunction(context, callerContext, parameters));
 }
Example #3
0
        public override object Invoke(TemplateContext context, ScriptNode callerContext, ScriptArray arguments, ScriptBlockStatement blockStatement)
        {
            var expectedNumberOfParameters = Parameters.Length;

            if (_hasTemplateContext)
            {
                expectedNumberOfParameters--;
                if (_hasSpan)
                {
                    expectedNumberOfParameters--;
                }
            }

            var minimumRequiredParameters = expectedNumberOfParameters - _optionalParameterCount;

            // Check parameters
            if ((_hasObjectParams && arguments.Count < minimumRequiredParameters - 1) || (!_hasObjectParams && arguments.Count < minimumRequiredParameters))
            {
                if (minimumRequiredParameters != expectedNumberOfParameters)
                {
                    throw new ScriptRuntimeException(callerContext.Span, $"Invalid number of arguments `{arguments.Count}` passed to `{callerContext}` while expecting at least `{minimumRequiredParameters}` arguments");
                }
                else
                {
                    throw new ScriptRuntimeException(callerContext.Span, $"Invalid number of arguments `{arguments.Count}` passed to `{callerContext}` while expecting `{expectedNumberOfParameters}` arguments");
                }
            }

            // Convert arguments
            object[] paramArguments = null;
            var      argMask        = 0;

            if (_hasObjectParams)
            {
                var objectParamsCount = arguments.Count - _lastParamsIndex;
                if (_hasTemplateContext)
                {
                    objectParamsCount++;
                    if (_hasSpan)
                    {
                        objectParamsCount++;
                    }
                }
                paramArguments = new object[objectParamsCount];
                _arguments[_lastParamsIndex] = paramArguments;
                argMask |= 1 << _lastParamsIndex;
            }

            // Copy TemplateContext/SourceSpan parameters
            int argOffset = 0;

            if (_hasTemplateContext)
            {
                _arguments[0] = context;
                argOffset++;
                argMask |= 1;
                if (_hasSpan)
                {
                    _arguments[1] = callerContext.Span;
                    argOffset++;
                    argMask |= 2;
                }
            }

            var argOrderedIndex = argOffset;

            // Setup any default parameters
            if (_optionalParameterCount > 0)
            {
                for (int i = Parameters.Length - 1; i >= Parameters.Length - _optionalParameterCount; i--)
                {
                    _arguments[i] = Parameters[i].DefaultValue;
                    argMask      |= 1 << i;
                }
            }

            int paramsIndex = 0;

            for (int i = 0; i < arguments.Count; i++)
            {
                Type argType = null;
                try
                {
                    int argIndex;
                    var arg      = arguments[i];
                    var namedArg = arg as ScriptNamedArgument;
                    if (namedArg != null)
                    {
                        var namedArgValue = GetValueFromNamedArgument(context, callerContext, namedArg);
                        arg      = namedArgValue.Value;
                        argIndex = namedArgValue.Index;
                        argType  = namedArgValue.Type;
                        if (_hasObjectParams && argIndex == _lastParamsIndex)
                        {
                            argType  = _paramsElementType;
                            argIndex = argIndex + paramsIndex;
                            paramsIndex++;
                        }
                    }
                    else
                    {
                        argIndex = argOrderedIndex;
                        if (_hasObjectParams && argIndex == _lastParamsIndex)
                        {
                            argType  = _paramsElementType;
                            argIndex = argIndex + paramsIndex;
                            paramsIndex++;
                        }
                        else
                        {
                            argType = Parameters[argIndex].ParameterType;
                            argOrderedIndex++;
                        }
                    }

                    var argValue = context.ToObject(callerContext.Span, arg, argType);
                    if (paramArguments != null && argIndex >= _lastParamsIndex)
                    {
                        paramArguments[argIndex - _lastParamsIndex] = argValue;
                    }
                    else
                    {
                        _arguments[argIndex] = argValue;
                        argMask |= 1 << argIndex;
                    }
                }
                catch (Exception exception)
                {
                    throw new ScriptRuntimeException(callerContext.Span, $"Unable to convert parameter #{i} of type `{arguments[i]?.GetType()}` to type `{argType}`", exception);
                }
            }

            // In case we have named arguments we need to verify that all arguments were set
            if (argMask != (1 << Parameters.Length) - 1)
            {
                if (minimumRequiredParameters != expectedNumberOfParameters)
                {
                    throw new ScriptRuntimeException(callerContext.Span, $"Invalid number of arguments `{arguments.Count}` passed to `{callerContext}` while expecting at least `{minimumRequiredParameters}` arguments");
                }
                else
                {
                    throw new ScriptRuntimeException(callerContext.Span, $"Invalid number of arguments `{arguments.Count}` passed to `{callerContext}` while expecting `{expectedNumberOfParameters}` arguments");
                }
            }

            // Call method
            try
            {
                var result = Method.Invoke(_target, _arguments);
                // NOTE: The following line should not be touch as it is being matched by ScribanAsyncCodeGen
                return(result);
            }
            catch (TargetInvocationException exception)
            {
                throw new ScriptRuntimeException(callerContext.Span, $"Unexpected exception when calling {callerContext}", exception.InnerException);
            }
        }
Example #4
0
 public virtual ValueTask <object> InvokeAsync(TemplateContext context, ScriptNode callerContext, ScriptArray arguments, ScriptBlockStatement blockStatement)
 {
     return(new ValueTask <object>(Invoke(context, callerContext, arguments, blockStatement)));
 }
Example #5
0
 public abstract object Invoke(TemplateContext context, ScriptNode callerContext, ScriptArray arguments, ScriptBlockStatement blockStatement);
Example #6
0
        public object Evaluate(TemplateContext context, ScriptNode callerContext, ScriptArray parameters, ScriptBlockStatement blockStatement)
        {
            if (parameters.Count == 0)
            {
                throw new ScriptRuntimeException(callerContext.Span, "Expecting at least the name of the template to include for the <include> function");
            }

            string templateName = null;
            try
            {
                templateName = ScriptValueConverter.ToString(callerContext.Span, parameters[0]);
            }
            catch (Exception ex)
            {
                throw new ScriptRuntimeException(callerContext.Span, $"Unexpected exception while converting first parameter for <include> function. Expecting a string", ex);
            }

            // If template name is empty, throw an exception
            if (templateName == null || string.IsNullOrEmpty(templateName = templateName.Trim()))
            {
                throw new ScriptRuntimeException(callerContext.Span, $"Include template name cannot be null or empty");
            }

            // Compute a new parameters for the include
            var newParameters = new ScriptArray(parameters.Count - 1);
            for (int i = 1; i < parameters.Count; i++)
            {
                newParameters[i] = parameters[i];
            }

            context.SetValue(ScriptVariable.Arguments, newParameters, true);

            Template template;

            if (!context.CachedTemplates.TryGetValue(templateName, out template))
            {
                if (context.TemplateLoader == null)
                {
                    throw new ScriptRuntimeException(callerContext.Span,
                        $"Unable to include <{templateName}>. No TemplateLoader registered in TemplateContext.Options.TemplateLoader");
                }

                string templateFilePath;

                var templateText = context.TemplateLoader.Load(context, callerContext.Span, templateName, out templateFilePath);

                if (templateText == null)
                {
                    throw new ScriptRuntimeException(callerContext.Span, $"The result of including <{templateName}> cannot be null");
                }

                // IF template file path is not defined, we use the template name instead
                templateFilePath = templateFilePath ?? templateName;

                // Clone parser options
                var parserOptions = context.TemplateLoaderParserOptions.Clone();

                // Parse include in default modes (while top page can be using front matter)
                parserOptions.Mode = parserOptions.Mode == ScriptMode.ScriptOnly
                    ? ScriptMode.ScriptOnly
                    : ScriptMode.Default;

                template = Template.Parse(templateText, templateFilePath, parserOptions);

                // If the template has any errors, throw an exception
                if (template.HasErrors)
                {
                    throw new ScriptParserRuntimeException(callerContext.Span, $"Error while parsing template <{templateName}> from [{templateFilePath}]", template.Messages);
                }

                context.CachedTemplates.Add(templateName, template);
            }

            // Query the pending includes stored in the context
            HashSet<string> pendingIncludes;
            object pendingIncludesObject;
            if (!context.Tags.TryGetValue(typeof(IncludeFunction), out pendingIncludesObject))
            {
                pendingIncludesObject = pendingIncludes = new HashSet<string>();
                context.Tags[typeof (IncludeFunction)] = pendingIncludesObject;
            }
            else
            {
                pendingIncludes = (HashSet<string>) pendingIncludesObject;
            }

            // Make sure that we cannot recursively include a template
            if (pendingIncludes.Contains(templateName))
            {
                throw new ScriptRuntimeException(callerContext.Span, $"The include [{templateName}] cannot be used recursively");
            }
            pendingIncludes.Add(templateName);

            context.PushOutput();
            object result = null;
            try
            {
                template.Render(context);
            }
            finally
            {
                result = context.PopOutput();
                pendingIncludes.Remove(templateName);
            }

            return result;
        }
Example #7
0
        private static object Map(TemplateContext context, ScriptNode callerContext, ScriptArray parameters)
        {
            if (parameters.Count != 2)
            {
                throw new ScriptRuntimeException(callerContext.Span, $"Unexpected number of arguments [{parameters.Count}] for map. Expecting at 2 parameters: <property> <array>");
            }

            var member = ScriptValueConverter.ToString(callerContext.Span, parameters[0]);
            var target = parameters[1];

            return Map(context, target, member);
        }
Example #8
0
        private ScriptExpression ParseExpression(ScriptNode parentNode, ref bool hasAnonymousFunction, ScriptExpression parentExpression = null, int precedence = 0)
        {
            int expressionCount = 0;
            expressionLevel++;
            try
            {
                ScriptFunctionCall functionCall = null;
                parseExpression:
                expressionCount++;
                ScriptExpression leftOperand = null;
                switch (Current.Type)
                {
                    case TokenType.Identifier:
                    case TokenType.IdentifierSpecial:
                        leftOperand = ParseVariableOrLiteral();

                        // Special handle of the $$ block delegate variable
                        if (ScriptVariable.BlockDelegate.Equals(leftOperand))
                        {
                            if (expressionCount != 1 || expressionLevel > 1)
                            {
                                LogError("Cannot use block delegate $$ in a nested expression");
                            }

                            if (!(parentNode is ScriptExpressionStatement))
                            {
                                LogError(parentNode, "Cannot use block delegate $$ outside an expression statement");
                            }

                            return leftOperand;
                        }
                        break;
                    case TokenType.Integer:
                        leftOperand = ParseInteger();
                        break;
                    case TokenType.Float:
                        leftOperand = ParseFloat();
                        break;
                    case TokenType.String:
                        leftOperand = ParseString();
                        break;
                    case TokenType.OpenParent:
                        leftOperand = ParseParenthesis(ref hasAnonymousFunction);
                        break;
                    case TokenType.OpenBrace:
                        leftOperand = ParseObjectInitializer();
                        break;
                    case TokenType.OpenBracket:
                        leftOperand = ParseArrayInitializer();
                        break;
                    case TokenType.Not:
                    case TokenType.Minus:
                    case TokenType.Arroba:
                    case TokenType.Plus:
                    case TokenType.Caret:
                        leftOperand = ParseUnaryExpression(ref hasAnonymousFunction);
                        break;
                }

                // Should not happen but in case
                if (leftOperand == null)
                {
                    LogError($"Unexpected token [{Current.Type}] for expression");
                    return null;
                }

                if (leftOperand is ScriptAnonymousFunction)
                {
                    hasAnonymousFunction = true;
                }

                while (!hasAnonymousFunction)
                {
                    // Parse Member expression are expected to be followed only by an identifier
                    if (Current.Type == TokenType.Dot)
                    {
                        var nextToken = PeekToken();
                        if (nextToken.Type == TokenType.Identifier)
                        {
                            NextToken();
                            var memberExpression = Open<ScriptMemberExpression>();
                            memberExpression.Target = leftOperand;
                            var member = ParseVariableOrLiteral();
                            if (!(member is ScriptVariable))
                            {
                                LogError("Unexpected literal member [{member}]");
                                return null;
                            }
                            memberExpression.Member = (ScriptVariable) member;
                            leftOperand = Close(memberExpression);
                        }
                        else
                        {
                            LogError(nextToken,
                                $"Invalid token [{nextToken.Type}]. The dot operator is expected to be followed by a plain identifier");
                            return null;
                        }
                        continue;
                    }

                    // If we have a bracket but left operand is a (variable || member || indexer), then we consider next as an indexer
                    // unit test: 130-indexer-accessor-accept1.txt
                    if (Current.Type == TokenType.OpenBracket && leftOperand is ScriptVariablePath && !IsPreviousCharWhitespace())
                    {
                        NextToken();
                        var indexerExpression = Open<ScriptIndexerExpression>();
                        indexerExpression.Target = leftOperand;
                        // unit test: 130-indexer-accessor-error5.txt
                        indexerExpression.Index = ExpectAndParseExpression(indexerExpression, ref hasAnonymousFunction, functionCall, 0, $"Expecting <index_expression> instead of [{Current.Type}]");

                        if (Current.Type != TokenType.CloseBracket)
                        {
                            LogError($"Unexpected [{Current.Type}]. Expecting ']'");
                        }
                        else
                        {
                            NextToken();
                        }

                        leftOperand = Close(indexerExpression);
                        continue;
                    }

                    if (Current.Type == TokenType.Equal)
                    {
                        var assignExpression = Open<ScriptAssignExpression>();

                        if (expressionLevel > 1)
                        {
                            // unit test: 101-assign-complex-error1.txt
                            LogError(assignExpression, $"Expression is only allowed for a top level assignment");
                        }

                        NextToken();
                        assignExpression.Target = leftOperand;

                        // unit test: 105-assign-error3.txt
                        assignExpression.Value = ExpectAndParseExpression(assignExpression, ref hasAnonymousFunction, parentExpression);

                        leftOperand = Close(assignExpression);
                        continue;
                    }

                    // Handle binary operators here
                    ScriptBinaryOperator binaryOperatorType;
                    if (BinaryOperators.TryGetValue(Current.Type, out binaryOperatorType))
                    {
                        var newPrecedence = GetOperatorPrecedence(binaryOperatorType);

                        // Check precedence to see if we should "take" this operator here (Thanks TimJones for the tip code! ;)
                        if (newPrecedence < precedence)
                            break;

                        var binaryExpression = Open<ScriptBinaryExpression>();
                        binaryExpression.Left = leftOperand;
                        binaryExpression.Operator = binaryOperatorType;

                        NextToken(); // skip the operator

                        // unit test: 110-binary-simple-error1.txt
                        binaryExpression.Right = ExpectAndParseExpression(binaryExpression, ref hasAnonymousFunction,
                            functionCall ?? parentExpression, newPrecedence,
                            $"Expecting an <expression> to the right of the operator instead of [{Current.Type}]");
                        leftOperand = Close(binaryExpression);

                        continue;
                    }

                    if (precedence > 0)
                    {
                        break;
                    }

                    // If we can parse a statement, we have a method call
                    if (StartAsExpression())
                    {
                        if (parentExpression != null)
                        {
                            break;
                        }

                        if (functionCall == null)
                        {
                            functionCall = Open<ScriptFunctionCall>();
                            functionCall.Target = leftOperand;
                        }
                        else
                        {
                            functionCall.Arguments.Add(leftOperand);
                        }
                        goto parseExpression;
                    }

                    if (Current.Type == TokenType.Pipe)
                    {
                        if (functionCall != null)
                        {
                            functionCall.Arguments.Add(leftOperand);
                            leftOperand = functionCall;
                        }

                        var pipeCall = Open<ScriptPipeCall>();
                        pipeCall.From = leftOperand;
                        NextToken(); // skip |

                        // unit test: 310-func-pipe-error1.txt
                        pipeCall.To = ExpectAndParseExpression(pipeCall, ref hasAnonymousFunction);
                        return Close(pipeCall);
                    }

                    break;
                }

                if (functionCall != null)
                {
                    functionCall.Arguments.Add(leftOperand);
                    return functionCall;
                }
                return Close(leftOperand);
            }
            finally
            {
                expressionLevel--;
            }
        }
Example #9
0
        public override async ValueTask <object> InvokeAsync(TemplateContext context, ScriptNode callerContext, ScriptArray scriptArguments, ScriptBlockStatement blockStatement)
        {
            Array paramArguments = null;
            var   arguments      = PrepareArguments(context, callerContext, scriptArguments, ref paramArguments);

            try
            {
                // Call the method via reflection
                var result = InvokeImpl(context, callerContext.Span, arguments);
                return(IsAwaitable ? await ConfigureAwait(result) : result);
            }
            catch (TargetInvocationException exception)
            {
                if (exception.InnerException != null)
                {
                    throw exception.InnerException;
                }
                throw new ScriptRuntimeException(callerContext.Span, $"Unexpected exception when calling {callerContext}");
            }
            finally
            {
                context.ReleaseReflectionArguments(arguments);
            }
        }
Example #10
0
 public object Evaluate(TemplateContext context, ScriptNode callerContext, ScriptArray parameters, ScriptBlockStatement blockStatement)
 {
     return customFunction(context, callerContext, parameters);
 }
Example #11
0
        internal void EnterFunction(ScriptNode caller)
        {
            functionDepth++;
            if (functionDepth > RecursiveLimit)
            {
                throw new ScriptRuntimeException(caller.Span, $"Exceeding number of recursive depth limit [{RecursiveLimit}] for function call: [{caller}]"); // unit test: 305-func-error2.txt
            }

            PushVariableScope(ScriptVariableScope.Local);
        }
Example #12
0
 /// <summary>
 /// Evaluates the specified script node.
 /// </summary>
 /// <param name="scriptNode">The script node.</param>
 /// <param name="aliasReturnedFunction">if set to <c>true</c> and a function would be evaluated as part of this node, return the object function without evaluating it.</param>
 /// <returns>The result of the evaluation.</returns>
 /// <remarks>
 /// <see cref="Result"/> is set to null when calling directly this method.
 /// </remarks>
 public object Evaluate(ScriptNode scriptNode, bool aliasReturnedFunction)
 {
     var previousFunctionCallState = isFunctionCallDisabled;
     try
     {
         isFunctionCallDisabled = aliasReturnedFunction;
         scriptNode?.Evaluate(this);
         var result = Result;
         Result = null;
         return result;
     }
     finally
     {
         isFunctionCallDisabled = previousFunctionCallState;
     }
 }
Example #13
0
 /// <summary>
 /// Evaluates the specified script node.
 /// </summary>
 /// <param name="scriptNode">The script node.</param>
 /// <returns>The result of the evaluation.</returns>
 /// <remarks>
 /// <see cref="Result"/> is set to null when calling directly this method.
 /// </remarks>
 public object Evaluate(ScriptNode scriptNode)
 {
     return Evaluate(scriptNode, false);
 }
            public object Evaluate(TemplateContext context, ScriptNode callerContext, ScriptArray parameters, ScriptBlockStatement blockStatement)
            {
                // Check parameters
                if ((hasObjectParams && parameters.Count < parametersInfo.Length - 1) || (!hasObjectParams && parameters.Count != parametersInfo.Length))
                {
                    throw new ScriptRuntimeException(callerContext.Span, $"Invalid number of arguments passed [{parameters.Count}] while expecting [{parametersInfo.Length}] for [{callerContext}]");
                }

                // Convert arguments
                var arguments = new object[parametersInfo.Length];
                object[] paramArguments = null;
                if (hasObjectParams)
                {
                    paramArguments = new object[parameters.Count - lastParamsIndex];
                    arguments[lastParamsIndex] = paramArguments;
                }

                for (int i = 0; i < parameters.Count; i++)
                {
                    var destType = hasObjectParams && i >= lastParamsIndex ? typeof(object) : parametersInfo[i].ParameterType;
                    try
                    {
                        var argValue = ScriptValueConverter.ToObject(callerContext.Span, parameters[i], destType);
                        if (hasObjectParams && i >= lastParamsIndex)
                        {
                            paramArguments[i - lastParamsIndex] = argValue;
                        }
                        else
                        {
                            arguments[i] = argValue;
                        }
                    }
                    catch (Exception exception)
                    {
                        throw new ScriptRuntimeException(callerContext.Span, $"Unable to convert parameter #{i} of type [{parameters[i]?.GetType()}] to type [{destType}]", exception);
                    }
                }

                // Call method
                try
                {
                    var result = method.Invoke(target, arguments);
                    return result;
                }
                catch (Exception exception)
                {
                    throw new ScriptRuntimeException(callerContext.Span, $"Unexpected exception when calling {callerContext}", exception);
                }
            }
 public ValueTask <object> InvokeAsync(TemplateContext context, ScriptNode callerContext, ScriptArray arguments, ScriptBlockStatement blockStatement)
 {
     return(new ValueTask <object>(_customFunction(context, callerContext, arguments)));
 }
Example #16
0
        private static object Slice(TemplateContext context, ScriptNode callerContext, ScriptArray parameters)
        {
            if (parameters.Count < 2 || parameters.Count > 3)
            {
                throw new ScriptRuntimeException(callerContext.Span, $"Unexpected number of arguments [{parameters.Count}] for slice. Expecting at least 2 parameters <start> <length>? <text>");
            }

            var text = ScriptValueConverter.ToString(callerContext.Span, parameters[parameters.Count - 1]);
            var start = ScriptValueConverter.ToInt(callerContext.Span, parameters[0]);
            var length = -1;
            if (parameters.Count == 3)
            {
                length = ScriptValueConverter.ToInt(callerContext.Span, parameters[1]);
            }

            return Slice(text, start, length);
        }
Example #17
0
 private ScriptExpression ParseExpression(ScriptNode parentNode, ScriptExpression parentExpression = null, int precedence = 0)
 {
     bool hasAnonymousFunction = false;
     return ParseExpression(parentNode, ref hasAnonymousFunction, parentExpression, precedence);
 }
            public object Invoke(TemplateContext context, ScriptNode callerContext, ScriptArray parameters, ScriptBlockStatement blockStatement)
            {
                var expectedNumberOfParameters = parametersInfo.Length;

                if (hasTemplateContext)
                {
                    expectedNumberOfParameters--;
                    if (hasSpan)
                    {
                        expectedNumberOfParameters--;
                    }
                }

                // Check parameters
                if ((hasObjectParams && parameters.Count < expectedNumberOfParameters - 1) || (!hasObjectParams && parameters.Count != expectedNumberOfParameters))
                {
                    throw new ScriptRuntimeException(callerContext.Span, $"Invalid number of arguments passed [{parameters.Count}] while expecting [{expectedNumberOfParameters}] for [{callerContext}]");
                }

                // Convert arguments
                var arguments = new object[parametersInfo.Length];

                object[] paramArguments = null;
                if (hasObjectParams)
                {
                    paramArguments             = new object[parameters.Count - lastParamsIndex];
                    arguments[lastParamsIndex] = paramArguments;
                }

                // Copy TemplateContext/SourceSpan parameters
                int argIndex = 0;

                if (hasTemplateContext)
                {
                    arguments[0] = context;
                    argIndex++;
                    if (hasSpan)
                    {
                        arguments[1] = callerContext.Span;
                        argIndex++;
                    }
                }

                for (int i = 0; i < parameters.Count; i++, argIndex++)
                {
                    var destType = hasObjectParams && i >= lastParamsIndex ? typeof(object) : parametersInfo[argIndex].ParameterType;
                    try
                    {
                        var argValue = context.ToObject(callerContext.Span, parameters[i], destType);
                        if (hasObjectParams && i >= lastParamsIndex)
                        {
                            paramArguments[argIndex - lastParamsIndex] = argValue;
                        }
                        else
                        {
                            arguments[argIndex] = argValue;
                        }
                    }
                    catch (Exception exception)
                    {
                        throw new ScriptRuntimeException(callerContext.Span, $"Unable to convert parameter #{i} of type [{parameters[i]?.GetType()}] to type [{destType}]", exception);
                    }
                }

                // Call method
                try
                {
                    var result = method.Invoke(target, arguments);
                    return(result);
                }
                catch (Exception exception)
                {
                    throw new ScriptRuntimeException(callerContext.Span, $"Unexpected exception when calling {callerContext}", exception);
                }
            }
Example #19
0
        public static object Call(TemplateContext context, ScriptNode callerContext, object functionObject, List<ScriptExpression> arguments = null)
        {
            if (callerContext == null) throw new ArgumentNullException(nameof(callerContext));
            if (functionObject == null)
            {
                throw new ScriptRuntimeException(callerContext.Span, $"The target function [{callerContext}] is null");
            }
            var function = functionObject as ScriptFunction;
            var externFunction = functionObject as IScriptCustomFunction;

            if (function == null && externFunction == null)
            {
                throw new ScriptRuntimeException(callerContext.Span, $"Invalid object function [{functionObject?.GetType()}]");
            }

            ScriptBlockStatement blockDelegate = null;
            if (context.BlockDelegates.Count > 0)
            {
                blockDelegate = context.BlockDelegates.Pop();
            }

            var argumentValues = new ScriptArray();
            if (arguments != null)
            {
                foreach (var argument in arguments)
                {
                    var value = context.Evaluate(argument);

                    // Handle parameters expansion for a function call when the operator ^ is used
                    var unaryExpression = argument as ScriptUnaryExpression;
                    if (unaryExpression != null && unaryExpression.ExpandParameters(value, argumentValues))
                    {
                        continue;
                    }

                    argumentValues.Add(value);
                }
            }

            // Handle pipe arguments here
            if (context.PipeArguments.Count > 0)
            {
                var additionalArgument = context.PipeArguments.Pop();

                var value = context.Evaluate(additionalArgument);

                // Handle parameters expansion for a function call when the operator ~ is used
                var unaryExpression = additionalArgument as ScriptUnaryExpression;
                if (unaryExpression == null || !unaryExpression.ExpandParameters(value, argumentValues))
                {
                    argumentValues.Add(value);
                }
            }

            object result = null;
            context.EnterFunction(callerContext);
            try
            {
                if (externFunction != null)
                {
                    result = externFunction.Evaluate(context, callerContext, argumentValues, blockDelegate);
                }
                else
                {
                    context.SetValue(ScriptVariable.Arguments, argumentValues, true);

                    // Set the block delegate
                    if (blockDelegate != null)
                    {
                        context.SetValue(ScriptVariable.BlockDelegate, blockDelegate, true);
                    }
                    result = context.Evaluate(function.Body);
                }
            }
            finally
            {
                context.ExitFunction();
            }

            // Restore the flow state to none
            context.FlowState = ScriptFlowState.None;
            return result;
        }
Example #20
0
        private static object Sort(TemplateContext context, ScriptNode callerContext, ScriptArray parameters)
        {
            if (parameters.Count < 1 || parameters.Count > 2)
            {
                throw new ScriptRuntimeException(callerContext.Span, $"Unexpected number of arguments [{parameters.Count}] for sort. Expecting at least 1 parameter <property>? <array>");
            }

            var target = parameters[parameters.Count - 1];
            string member = null;
            if (parameters.Count == 2)
            {
               member = ScriptValueConverter.ToString(callerContext.Span, parameters[0]);
            }

            return Sort(context, target, member);
        }