/// <summary> /// Try get value from a given path. /// </summary> /// <param name="path">Given path.</param> /// <param name="value">Resolved value.</param> /// <returns>True if the memory contains an element with the specified key; otherwise, false.</returns> public bool TryGetValue(string path, out object value) { value = null; if (memory == null || path.Length == 0) { return(false); } var parts = path.Split(".[]".ToCharArray(), StringSplitOptions.RemoveEmptyEntries) .Select(x => x.Trim('\'', '"')) .ToArray(); var curScope = memory; foreach (var part in parts) { string error = null; if (int.TryParse(part, out var idx) && FunctionUtils.TryParseList(curScope, out var li)) { (value, error) = FunctionUtils.AccessIndex(li, idx); if (error != null) { return(false); } } else { if (!FunctionUtils.TryAccessProperty(curScope, part, out value)) { return(false); } } curScope = value; } if (value is IExpressionProperty ep) { value = ep.GetObject(memory); } return(true); }
private static (object value, string error) Evaluator(Expression expression, IMemory state, Options options) { object value = null; string error; var instance = expression.Children[0]; var index = expression.Children[1]; object inst; (inst, error) = instance.TryEvaluate(state, options); if (error == null) { object idxValue; (idxValue, error) = index.TryEvaluate(state, new Options(options) { NullSubstitution = null }); if (error == null) { if (idxValue.IsInteger()) { var idx = 0; (idx, error) = FunctionUtils.ParseInt32(idxValue); if (error == null) { (value, error) = FunctionUtils.AccessIndex(inst, idx); } } else if (idxValue is string idxStr) { FunctionUtils.TryAccessProperty(inst, idxStr, out value); } else { error = $"Could not coerce {index}<{idxValue?.GetType()}> to an int or string"; } } } return(value, error); }
private static (object value, string error) Evaluator(Expression expression, IMemory state, Options options) { object result = null; string error; object instance; (instance, error) = expression.Children[0].TryEvaluate(state, options); if (error == null) { var isInstanceList = false; IList list = null; if (FunctionUtils.TryParseList(instance, out IList ilist)) { isInstanceList = true; list = ilist; } else if (instance is JObject jobj) { list = FunctionUtils.Object2KVPairList(jobj); } else if (FunctionUtils.ConvertToJToken(instance) is JObject jobject) { list = FunctionUtils.Object2KVPairList(jobject); } else { error = $"{expression.Children[0]} is not a collection or structure object to run foreach"; } if (error == null) { var iteratorName = (string)(expression.Children[1].Children[0] as Constant).Value; var stackedMemory = StackedMemory.Wrap(state); result = new List <object>(); for (var idx = 0; idx < list.Count; idx++) { var local = new Dictionary <string, object> { { iteratorName, FunctionUtils.AccessIndex(list, idx).value }, }; // the local iterator is pushed as one memory layer in the memory stack stackedMemory.Push(new SimpleObjectMemory(local)); var(r, e) = expression.Children[2].TryEvaluate(stackedMemory, new Options(options) { NullSubstitution = null }); stackedMemory.Pop(); if (FunctionUtils.IsLogicTrue(r) && e == null) { // add if only if it evaluates to true ((List <object>)result).Add(local[iteratorName]); } } if (!isInstanceList) { // re-construct object var jobjResult = new JObject(); foreach (var item in (List <object>)result) { FunctionUtils.TryAccessProperty(item, "key", out var keyVal); FunctionUtils.TryAccessProperty(item, "value", out var val); jobjResult.Add(keyVal as string, FunctionUtils.ConvertToJToken(val)); } result = jobjResult; } } } return(result, error); }
// In this simple object scope, we don't allow you to set a path in which some parts in middle don't exist // for example // if you set dialog.a.b = x, but dialog.a don't exist, this will result in an error // because we can't and shouldn't smart create structure in the middle // you can implement a customized Scope that support such behavior /// <summary> /// Set value to a given path. /// </summary> /// <param name="path">Memory path.</param> /// <param name="value">Value to set.</param> public void SetValue(string path, object value) { if (memory == null) { return; } var parts = path.Split(".[]".ToCharArray(), StringSplitOptions.RemoveEmptyEntries) .Select(x => x.Trim('\'', '"')) .ToArray(); var curScope = memory; var curPath = string.Empty; // valid path so far string error = null; // find the 2nd last value, the container for (var i = 0; i < parts.Length - 1; i++) { if (int.TryParse(parts[i], out var index) && FunctionUtils.TryParseList(curScope, out var li)) { curPath += $"[{parts[i]}]"; (curScope, error) = FunctionUtils.AccessIndex(li, index); } else { curPath += $".{parts[i]}"; if (FunctionUtils.TryAccessProperty(curScope, parts[i], out var newScope)) { curScope = newScope; } else { return; } } if (error != null || curScope == null) { return; } } // set the last value if (int.TryParse(parts.Last(), out var idx)) { if (FunctionUtils.TryParseList(curScope, out var li)) { if (li is JArray) { value = JToken.FromObject(value); } if (idx > li.Count) { error = $"{idx} index out of range"; } else if (idx == li.Count) { // expand for one li.Add(value); } else { li[idx] = value; } } else { error = $"set value for an index to a non-list object"; } if (error != null) { return; } } else { (_, error) = SetProperty(curScope, parts.Last(), value); if (error != null) { return; } } }