예제 #1
0
        /// <summary>
        /// The evaluates a string as a scriban expression or evaluate the passed function or return the passed value.
        /// </summary>
        /// <param name="context">The template context</param>
        /// <param name="span">The source span</param>
        /// <param name="value">The input value, either a scriban template in a string, or an alias function or directly a value.</param>
        /// <returns>The evaluation of the input value.</returns>
        /// <remarks>
        /// ```scriban-html
        /// {{ "1 + 2" | object.eval }}
        /// ```
        /// ```html
        /// 3
        /// ```
        /// </remarks>
        public static object Eval(TemplateContext context, SourceSpan span, object value)
        {
            if (value == null)
            {
                return(null);
            }

            if (value is string templateStr)
            {
                try
                {
                    var template = Template.Parse(templateStr, lexerOptions: new LexerOptions()
                    {
                        Lang = context.Language, Mode = ScriptMode.ScriptOnly
                    });
                    return(context.Evaluate(template.Page));
                }
                catch (Exception ex)
                {
                    throw new ArgumentException(ex.Message, nameof(value));
                }
            }

            if (value is IScriptCustomFunction function)
            {
                return(ScriptFunctionCall.Call(context, context.CurrentNode, function, false, null));
            }

            return(value);
        }
예제 #2
0
        private static IEnumerable EachInternal(TemplateContext context, ScriptNode callerContext, SourceSpan span, IEnumerable list, IScriptCustomFunction function, Type destType)
        {
            var arg = new ScriptArray(1);

            foreach (var item in list)
            {
                var itemToTransform = context.ToObject(span, item, destType);
                arg[0] = itemToTransform;
                var itemTransformed = ScriptFunctionCall.Call(context, callerContext, function, arg);
                yield return(itemTransformed);
            }
        }
예제 #3
0
        static IEnumerable FilterInternal(TemplateContext context, SourceSpan span, IEnumerable list, IScriptCustomFunction function, Type destType)
        {
            var arg = new ScriptArray(1);

            foreach (var item in list)
            {
                var itemToTransform = context.ToObject(span, item, destType);
                arg[0] = itemToTransform;
                var itemTransformed = ScriptFunctionCall.Call(context, context.CurrentNode, function, arg);
                if (context.ToBool(span, itemTransformed))
                {
                    yield return(itemToTransform);
                }
            }
        }
예제 #4
0
        /// <summary>
        /// Evaluates the specified expression
        /// </summary>
        /// <param name="targetExpression">The expression to evaluate</param>
        /// <param name="valueToSet">A value to set in case of a setter</param>
        /// <param name="setter">true if this a setter</param>
        /// <returns>The value of the targetExpression</returns>
        private object GetOrSetValue(ScriptExpression targetExpression, object valueToSet, bool setter)
        {
            object value = null;

            try
            {
                if (targetExpression is IScriptVariablePath nextPath)
                {
                    if (setter)
                    {
                        nextPath.SetValue(this, valueToSet);
                    }
                    else
                    {
                        value = nextPath.GetValue(this);
                    }
                }
                else if (!setter)
                {
                    value = Evaluate(targetExpression);
                }
                else
                {
                    throw new ScriptRuntimeException(targetExpression.Span, $"Unsupported target expression for assignment."); // unit test: 105-assign-error1.txt
                }
            }
            catch (Exception readonlyException) when(_getOrSetValueLevel == 1 && !(readonlyException is ScriptRuntimeException))
            {
                throw new ScriptRuntimeException(targetExpression.Span, $"Unexpected exception while accessing target expression: {readonlyException.Message}", readonlyException);
            }

            // If the variable being returned is a function, we need to evaluate it
            // If function call is disabled, it will be only when returning the final object (level 0 of recursion)
            var allowFunctionCall = (_isFunctionCallDisabled && _getOrSetValueLevel > 1) || !_isFunctionCallDisabled;

            if (allowFunctionCall && ScriptFunctionCall.IsFunction(value))
            {
                // Allow to pipe arguments only for top level returned function
                value = ScriptFunctionCall.Call(this, targetExpression, value, _getOrSetValueLevel == 1, null);
            }

            return(value);
        }
예제 #5
0
        /// <summary>
        /// The evaluates a string as a scriban template or evaluate the passed function or return the passed value.
        /// </summary>
        /// <param name="context">The template context</param>
        /// <param name="span">The source span</param>
        /// <param name="value">The input value, either a scriban template in a string, or an alias function or directly a value.</param>
        /// <returns>The evaluation of the input value.</returns>
        /// <remarks>
        /// ```scriban-html
        /// {{ "This is a template text {{ 1 + 2 }}" | object.eval_template }}
        /// ```
        /// ```html
        /// This is a template text 3
        /// ```
        /// </remarks>
        public static object EvalTemplate(TemplateContext context, SourceSpan span, object value)
        {
            if (value == null)
            {
                return(null);
            }

            if (value is string templateStr)
            {
                try
                {
                    var template = Template.Parse(templateStr, lexerOptions: new LexerOptions()
                    {
                        Lang = context.Language, Mode = ScriptMode.Default
                    });
                    var output = new StringBuilderOutput();
                    context.PushOutput(output);
                    try
                    {
                        context.Evaluate(template.Page);
                    }
                    finally
                    {
                        context.PopOutput();
                    }
                    return(output.ToString());
                }
                catch (Exception ex)
                {
                    throw new ArgumentException(ex.Message, nameof(value));
                }
            }

            if (value is IScriptCustomFunction function)
            {
                return(ScriptFunctionCall.Call(context, context.CurrentNode, function, false, null));
            }

            return(value);
        }
예제 #6
0
        public static string Join(TemplateContext context, SourceSpan span, IEnumerable list, string delimiter, object function = null)
        {
            if (list == null)
            {
                return(string.Empty);
            }

            var scriptingFunction = function as IScriptCustomFunction;

            if (function != null && scriptingFunction == null)
            {
                throw new ArgumentException($"The parameter `{function}` is not a function. Maybe prefix it with @?", nameof(function));
            }

            var  text       = new StringBuilder();
            bool afterFirst = false;
            var  arg        = new ScriptArray(1);

            foreach (var obj in list)
            {
                if (afterFirst)
                {
                    text.Append(delimiter);
                }

                var item = context.ObjectToString(obj);
                if (scriptingFunction != null)
                {
                    arg[0] = item;
                    var result = ScriptFunctionCall.Call(context, context.CurrentNode, scriptingFunction, arg);
                    item = context.ObjectToString(result);
                }

                text.Append(item);
                afterFirst = true;
            }
            return(text.ToString());
        }
예제 #7
0
        public override void Evaluate(TemplateContext context)
        {
            // Check that the Target is actually a function
            var functionCall = Target as ScriptFunctionCall;

            if (functionCall == null)
            {
                var parameterLessFunction = context.Evaluate(Target, true);
                if (!(parameterLessFunction is IScriptCustomFunction))
                {
                    var targetPrettyname = ScriptSyntaxAttribute.Get(Target);
                    throw new ScriptRuntimeException(Target.Span, $"Expecting a direct function instead of the expression [{Target}/{targetPrettyname.Name}]");
                }

                context.BlockDelegates.Push(Body);
                context.Result = ScriptFunctionCall.Call(context, this, parameterLessFunction);
            }
            else
            {
                context.BlockDelegates.Push(Body);
                context.Result = context.Evaluate(functionCall);
            }
        }
예제 #8
0
        /// <summary>
        /// Evaluates the specified expression
        /// </summary>
        /// <param name="targetExpression">The expression to evaluate</param>
        /// <param name="valueToSet">A value to set in case of a setter</param>
        /// <param name="setter">true if this a setter</param>
        /// <param name="level">The indirection level (0 before entering the expression)</param>
        /// <returns>The value of the targetExpression</returns>
        private object GetOrSetValue(ScriptExpression targetExpression, object valueToSet, bool setter, int level)
        {
            object value = null;

            try
            {
                var nextVariable = targetExpression as ScriptVariable;
                if (nextVariable != null)
                {
                    if (setter)
                    {
                        SetValue(nextVariable, valueToSet, false);
                    }
                    else
                    {
                        value = GetValueFromVariable(nextVariable);
                    }
                }
                else
                {
                    if (targetExpression is ScriptMemberExpression nextDot)
                    {
                        var targetObject = GetOrSetValue(nextDot.Target, valueToSet, false, level + 1);

                        if (targetObject == null)
                        {
                            throw new ScriptRuntimeException(nextDot.Span,
                                                             $"Object [{nextDot.Target}] is null. Cannot access member: {nextDot}"); // unit test: 131-member-accessor-error1.txt
                        }

                        if (targetObject is string || targetObject.GetType().GetTypeInfo().IsPrimitive)
                        {
                            throw new ScriptRuntimeException(nextDot.Span,
                                                             $"Cannot get or set a member on the primitive [{targetObject}/{targetObject.GetType()}] when accessing member: {nextDot}"); // unit test: 132-member-accessor-error2.txt
                        }

                        var accessor = GetMemberAccessor(targetObject);

                        var memberName = nextDot.Member.Name;

                        if (setter)
                        {
                            if (!accessor.TrySetValue(this, targetExpression.Span, targetObject, memberName, valueToSet))
                            {
                                throw new ScriptRuntimeException(nextDot.Member.Span,
                                                                 $"Cannot set a value for the readonly member: {nextDot}"); // unit test: 132-member-accessor-error3.txt
                            }
                        }
                        else
                        {
                            if (!accessor.TryGetValue(this, targetExpression.Span, targetObject, memberName, out value))
                            {
                                TryGetMember?.Invoke(this, targetExpression.Span, targetObject, memberName, out value);
                            }
                        }
                    }
                    else
                    {
                        if (targetExpression is ScriptIndexerExpression nextIndexer)
                        {
                            var targetObject = GetOrSetValue(nextIndexer.Target, valueToSet, false, level + 1);
                            if (targetObject == null)
                            {
                                throw new ScriptRuntimeException(nextIndexer.Target.Span,
                                                                 $"Object [{nextIndexer.Target}] is null. Cannot access indexer: {nextIndexer}"); // unit test: 130-indexer-accessor-error1.txt
                            }
                            else
                            {
                                var index = Evaluate(nextIndexer.Index);
                                if (index == null)
                                {
                                    throw new ScriptRuntimeException(nextIndexer.Index.Span,
                                                                     $"Cannot access target [{nextIndexer.Target}] with a null indexer: {nextIndexer}"); // unit test: 130-indexer-accessor-error2.txt
                                }
                                else
                                {
                                    if (targetObject is IDictionary || targetObject is ScriptObject)
                                    {
                                        var accessor      = GetMemberAccessor(targetObject);
                                        var indexAsString =
                                            ToString(nextIndexer.Index.Span, index);

                                        if (setter)
                                        {
                                            if (!accessor.TrySetValue(this, targetExpression.Span, targetObject, indexAsString, valueToSet))
                                            {
                                                throw new ScriptRuntimeException(nextIndexer.Index.Span,
                                                                                 $"Cannot set a value for the readonly member [{indexAsString}] in the indexer: {nextIndexer.Target}['{indexAsString}']"); // unit test: 130-indexer-accessor-error3.txt
                                            }
                                        }
                                        else
                                        {
                                            if (!accessor.TryGetValue(this, targetExpression.Span, targetObject, indexAsString, out value))
                                            {
                                                TryGetMember?.Invoke(this, targetExpression.Span, targetObject, indexAsString, out value);
                                            }
                                        }
                                    }
                                    else
                                    {
                                        var accessor = GetListAccessor(targetObject);
                                        if (accessor == null)
                                        {
                                            throw new ScriptRuntimeException(nextIndexer.Target.Span, $"Expecting a list. Invalid value [{targetObject}/{targetObject.GetType().Name}] for the target [{nextIndexer.Target}] for the indexer: {nextIndexer}"); // unit test: 130-indexer-accessor-error4.txt
                                        }
                                        else
                                        {
                                            int i = ToInt(nextIndexer.Index.Span, index);

                                            // Allow negative index from the end of the array
                                            if (i < 0)
                                            {
                                                i = accessor.GetLength(this, targetExpression.Span, targetObject) + i;
                                            }

                                            if (i >= 0)
                                            {
                                                if (setter)
                                                {
                                                    accessor.SetValue(this, targetExpression.Span, targetObject, i, valueToSet);
                                                }
                                                else
                                                {
                                                    value = accessor.GetValue(this, targetExpression.Span, targetObject, i);
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        else if (!setter)
                        {
                            value = Evaluate(targetExpression);
                        }
                        else
                        {
                            throw new ScriptRuntimeException(targetExpression.Span,
                                                             $"Unsupported expression for target for assignment: {targetExpression} = ..."); // unit test: 105-assign-error1.txt
                        }
                    }
                }
            }
            catch (Exception readonlyException) when(level == 0 && !(readonlyException is ScriptRuntimeException))
            {
                throw new ScriptRuntimeException(targetExpression.Span, $"Unexpected exception while accessing `{targetExpression}`", readonlyException);
            }

            // If the variable being returned is a function, we need to evaluate it
            // If function call is disabled, it will be only when returning the final object (level 0 of recursion)
            if ((!_isFunctionCallDisabled || level > 0) && ScriptFunctionCall.IsFunction(value))
            {
                value = ScriptFunctionCall.Call(this, targetExpression, value);
            }

            return(value);
        }