/// <inheritdoc /> public async ContextObjectPromise GetValue(ContextObject contextObject, ScopeData scopeData) { if (!PathParts.Any() && Formats.Count == 1 && FormatterName == "") { //indicates the usage of brackets return(await Formats[0].GetValue(contextObject, scopeData)); } var contextForPath = contextObject.GetContextForPath(PathParts, scopeData, this); if (!Formats.Any() && FormatterName == null) { return(contextForPath); } if (contextForPath == contextObject) { contextForPath = contextObject.CloneForEdit(); } var arguments = new FormatterArgumentType[Formats.Count]; var naturalValue = contextObject.FindNextNaturalContextObject(); for (var index = 0; index < Formats.Count; index++) { var formatterArgument = Formats[index]; var value = await formatterArgument.MorestachioExpression.GetValue(naturalValue, scopeData); arguments[index] = new FormatterArgumentType(index, formatterArgument.Name, value?.Value); } //contextForPath.Value = await contextForPath.Format(FormatterName, argList, scopeData); if (Cache == null) { Cache = contextForPath.PrepareFormatterCall( contextForPath.Value?.GetType() ?? typeof(object), FormatterName, arguments, scopeData); } if (Cache != null /* && !Equals(Cache.Value, default(FormatterCache))*/) { contextForPath.Value = await scopeData.ParserOptions.Formatters.Execute(Cache, contextForPath.Value, scopeData.ParserOptions, arguments); contextForPath.MakeSyntetic(); } return(contextForPath); }
/// <inheritdoc /> public async ContextObjectPromise GetValue(ContextObject contextObject, ScopeData scopeData) { var leftValue = await LeftExpression.GetValue(contextObject, scopeData); FormatterArgumentType[] arguments; if (RightExpression != null) { arguments = new[] { new FormatterArgumentType(0, null, (await RightExpression.GetValue(contextObject, scopeData)).Value) }; } else { arguments = new FormatterArgumentType[0]; } var operatorFormatterName = "op_" + Operator.OperatorType; if (Cache == null) { Cache = leftValue.PrepareFormatterCall( leftValue.Value?.GetType() ?? typeof(object), operatorFormatterName, arguments, scopeData); } if (Cache != null /* && !Equals(Cache.Value, default(FormatterCache))*/) { return(scopeData.ParserOptions.CreateContextObject(".", await scopeData.ParserOptions.Formatters.Execute(Cache, leftValue.Value, scopeData.ParserOptions, arguments), contextObject.Parent)); } return(scopeData.ParserOptions.CreateContextObject(".", null, contextObject.Parent)); }
/// <inheritdoc /> public CompiledExpression Compile() { if (!PathParts.HasValue && Formats.Count == 0 && FormatterName == null) { return((contextObject, data) => contextObject.ToPromise()); } if (!PathParts.HasValue && Formats.Count == 1 && FormatterName == "") { //this enables the usage of brackets. A formatter that has no name and one argument e.g ".(data + 1)" or "(data + 1)" should be considered a bracket return((contextObject, data) => Formats[0].GetValue(contextObject, data)); } if (PathParts.Count == 1 && PathParts.Current.Value == PathType.Null) { return((contextObject, scopeData) => scopeData.ParserOptions .CreateContextObject("x:null", null).ToPromise()); } var pathParts = PathParts.ToArray(); Func <ContextObject, ScopeData, IMorestachioExpression, ContextObject> getContext = null; if (pathParts.Length != 0) { var pathQueue = new Func <ContextObject, ScopeData, IMorestachioExpression, ContextObject> [pathParts.Length]; var idx = 0; if (pathParts.Length > 0 && pathParts.First().Value == PathType.DataPath) { var key = pathParts[0].Key; pathQueue[idx++] = ((context, scopeData, expression) => { var variable = scopeData.GetVariable(context, key); if (variable != null) { return(variable); } return(context.ExecuteDataPath(key, expression, context.Value?.GetType(), scopeData)); }); } for (; idx < pathQueue.Length;) { var pathPart = pathParts[idx]; var key = pathPart.Key; switch (pathPart.Value) { case PathType.DataPath: pathQueue[idx++] = ((contextObject, scopeData, expression) => { return(contextObject.ExecuteDataPath(key, expression, contextObject.Value?.GetType(), scopeData)); }); break; case PathType.RootSelector: pathQueue[idx++] = ((contextObject, scopeData, expression) => { return(contextObject.ExecuteRootSelector()); }); break; case PathType.ParentSelector: pathQueue[idx++] = ((contextObject, scopeData, expression) => { var natContext = contextObject.FindNextNaturalContextObject(); return(natContext?.Parent ?? contextObject); }); break; case PathType.ObjectSelector: pathQueue[idx++] = ((contextObject, scopeData, expression) => { return(contextObject.ExecuteObjectSelector(key, contextObject.Value?.GetType(), scopeData)); }); break; case PathType.Null: pathQueue[idx++] = ((contextObject, scopeData, expression) => { return(scopeData.ParserOptions.CreateContextObject("x:null", null)); }); break; case PathType.Boolean: var booleanValue = key == "true"; pathQueue[idx++] = ((contextObject, scopeData, expression) => { var booleanContext = scopeData.ParserOptions.CreateContextObject(".", booleanValue); booleanContext.IsNaturalContext = true; return(booleanContext); }); break; case PathType.SelfAssignment: case PathType.ThisPath: pathQueue[idx++] = ((contextObject, scopeDate, expression) => contextObject); break; default: throw new ArgumentOutOfRangeException(); } } if (pathQueue.Length == 1) { getContext = pathQueue[0]; } else { getContext = (contextObject, data, expression) => { for (var index = 0; index < pathQueue.Length; index++) { var func = pathQueue[index]; contextObject = func(contextObject, data, expression); } return(contextObject); }; } } if (!Formats.Any() && FormatterName == null) { if (getContext == null) { return((contextObject, data) => contextObject.ToPromise()); } return((contextObject, data) => getContext(contextObject, data, this).ToPromise()); } var formatsCompiled = Formats.ToDictionary(f => f, f => { if (f.IsCompileTimeEval()) { return(f.GetCompileTimeValue()); } return(f.Compile()); }).ToArray(); var arguments = new FormatterArgumentType[formatsCompiled.Length]; var allConstants = formatsCompiled.All(e => e.Key.IsCompileTimeEval()); if (allConstants) { for (var index = 0; index < formatsCompiled.Length; index++) { var keyValuePair = formatsCompiled[index]; arguments[index] = new FormatterArgumentType(index, keyValuePair.Key.Name, keyValuePair.Value); } } FormatterCache cache = null; async Promise CallFormatter( ContextObject naturalContext, ContextObject outputContext, ScopeData scopeData) { if (!allConstants) { for (var index = 0; index < formatsCompiled.Length; index++) { var formatterArgument = formatsCompiled[index]; object value; if (formatterArgument.Value is CompiledExpression cex) { value = (await cex(naturalContext, scopeData))?.Value; } else { value = formatterArgument.Value; } arguments[index] = new FormatterArgumentType(index, formatterArgument.Key.Name, value); } } if (cache == null) { cache = outputContext.PrepareFormatterCall( outputContext.Value?.GetType() ?? typeof(object), FormatterName, arguments, scopeData); } if (cache != null) { outputContext.Value = await scopeData.ParserOptions.Formatters.Execute(cache, outputContext.Value, scopeData.ParserOptions, arguments); outputContext.MakeSyntetic(); } } if (getContext == null) { return(async(contextObject, scopeData) => { var ctx = scopeData.ParserOptions.CreateContextObject("", contextObject.Value, contextObject); contextObject = contextObject.FindNextNaturalContextObject(); await CallFormatter(contextObject, ctx, scopeData); return ctx; }); } return(async(contextObject, scopeData) => { var ctx = getContext(contextObject, scopeData, this); await CallFormatter(contextObject, ctx, scopeData); return ctx; }); }