private DekiScriptLiteral InvokeHelper(DekiScriptRuntime runtime, DekiScriptList args) { // convert passed in arguments object[] arguments = new object[Parameters.Length]; int i = 0; try { for (; i < Parameters.Length; ++i) { var value = args[i].NativeValue; // check if we need to convert the value if ((value != null) && (Parameters[i].NativeType != typeof(object))) { // check for the special case where we cast from XML to STR if ((value is XDoc) && (Parameters[i].NativeType == typeof(string))) { XDoc xml = (XDoc)value; if (xml.HasName("html")) { value = xml["body[not(@target)]"].Contents; } else { value = xml.ToString(); } } else { // rely on the default type conversion rules value = SysUtil.ChangeType(value, Parameters[i].NativeType); } } arguments[i] = value; } } catch { throw new ArgumentException(string.Format("could not convert parameter '{0}' (index {1}) from {2} to {3}", Parameters[i].Name, i, args[i].ScriptTypeName, DekiScriptLiteral.AsScriptTypeName(Parameters[i].ScriptType))); } // invoke method var result = _invoke(runtime, arguments); // check if result is a URI if (result is XUri) { // normalize URI if possible DreamContext context = DreamContext.CurrentOrNull; if (context != null) { result = context.AsPublicUri((XUri)result); } } var literal = DekiScriptLiteral.FromNativeValue(result); try { return(literal.Convert(ReturnType)); } catch (DekiScriptInvalidCastException) { throw new DekiScriptInvalidReturnCastException(Location.None, literal.ScriptType, ReturnType); } }
public static Hashtable ListGroupBy( [DekiScriptParam("list value")] ArrayList list, [DekiScriptParam("expression to apply for grouping (use '$' to refer to the current item)")] string expression, DekiScriptRuntime runtime ) { Hashtable result = new Hashtable(StringComparer.OrdinalIgnoreCase); DekiScriptExpression expr = DekiScriptParser.Parse(new Location("list.groupby(expression)"), expression); // loop over all items in list foreach (object entry in list) { DekiScriptEnv env = runtime.CreateEnv(); env.Vars.Add(DekiScriptRuntime.DEFAULT_ID, DekiScriptLiteral.FromNativeValue(entry)); // evalute grouping expression string key = runtime.Evaluate(expr, DekiScriptEvalMode.EvaluateSafeMode, env).AsString(); if (key != null) { // check if an accumulation list already exists; otherwise, create one ArrayList accumulator = (ArrayList)result[key]; if (accumulator == null) { accumulator = new ArrayList(); result[key] = accumulator; } accumulator.Add(entry); } } return(result); }
public static Hashtable MapFilter( [DekiScriptParam("map value")] Hashtable map, [DekiScriptParam("condition to execute for each item (use '$' to refer to the key-value pair)")] string condition, DekiScriptRuntime runtime ) { DekiScriptExpression expr = DekiScriptParser.Parse(new Location("map.select(condition)"), condition); Hashtable result = new Hashtable(StringComparer.OrdinalIgnoreCase); foreach (DictionaryEntry entry in map) { DekiScriptMap keyvalue = new DekiScriptMap(); object value = Eval(entry.Value, runtime); keyvalue.Add("key", DekiScriptLiteral.FromNativeValue(entry.Key)); keyvalue.Add("value", DekiScriptLiteral.FromNativeValue(value)); DekiScriptEnv env = runtime.CreateEnv(); env.Vars.Add(DekiScriptRuntime.DEFAULT_ID, keyvalue); DekiScriptLiteral test = runtime.Evaluate(expr, DekiScriptEvalMode.EvaluateSafeMode, env); if (!test.IsNilFalseZero) { result.Add(entry.Key, value); } } return(result); }
//--- Methods --- public int Compare(object left, object right) { _values.Add("left", DekiScriptLiteral.FromNativeValue(left)); _values.Add("right", DekiScriptLiteral.FromNativeValue(right)); _env.Vars.Add(DekiScriptRuntime.DEFAULT_ID, _values); DekiScriptLiteral test = _runtime.Evaluate(_compare, DekiScriptEvalMode.EvaluateSafeMode, _env); return((int)(test.AsNumber() ?? 0.0)); }
public static ArrayList ListApply( [DekiScriptParam("list value")] ArrayList list, [DekiScriptParam("expression to apply (use '$' to refer to the current item)")] string expression, DekiScriptRuntime runtime ) { DekiScriptExpression expr = DekiScriptParser.Parse(new Location("list.apply(expression)"), expression); ArrayList result = new ArrayList(); foreach (object entry in list) { DekiScriptEnv env = runtime.CreateEnv(); env.Vars.Add(DekiScriptRuntime.DEFAULT_ID, DekiScriptLiteral.FromNativeValue(entry)); result.Add(runtime.Evaluate(expr, DekiScriptEvalMode.EvaluateSafeMode, env).NativeValue); } return(result); }
public static ArrayList ListIntersect( [DekiScriptParam("first list")] ArrayList first, [DekiScriptParam("second list")] ArrayList second, [DekiScriptParam("expression to determine if item from the first list should be included in final list (return true to include, false to exclude; use '$left' and '$right' to refer to the current item from the first and seconds lists respectively; default: equality condition)", true)] string condition, DekiScriptRuntime runtime ) { ArrayList result = new ArrayList(); if (condition != null) { // loop over both lists and keep items from the first list based on the outcome of the condition DekiScriptExpression expr = DekiScriptParser.Parse(new Location("list.intersect(condition)"), condition); foreach (object left in first) { foreach (object right in second) { DekiScriptEnv env = runtime.CreateEnv(); DekiScriptMap keyvalue = new DekiScriptMap(); keyvalue.Add("left", DekiScriptLiteral.FromNativeValue(left)); keyvalue.Add("right", DekiScriptLiteral.FromNativeValue(right)); env.Vars.Add(DekiScriptRuntime.DEFAULT_ID, keyvalue); DekiScriptLiteral test = runtime.Evaluate(expr, DekiScriptEvalMode.EvaluateSafeMode, env); if (!test.IsNilFalseZero) { result.Add(left); break; } } } } else { // using simple containment check to keep items foreach (object item in first) { if (second.Contains(item)) { result.Add(item); } } } return(result); }
public static Hashtable MapApply( [DekiScriptParam("map value")] Hashtable map, [DekiScriptParam("expression to apply (use '$' to refer to the item)")] string expression, DekiScriptRuntime runtime ) { DekiScriptExpression expr = DekiScriptParser.Parse(new Location("map.apply(expression)"), expression); Hashtable result = new Hashtable(StringComparer.OrdinalIgnoreCase); foreach (DictionaryEntry entry in map) { DekiScriptMap keyvalue = new DekiScriptMap(); keyvalue.Add("key", DekiScriptLiteral.FromNativeValue(entry.Key)); keyvalue.Add("value", DekiScriptLiteral.FromNativeValue(Eval(entry.Value, runtime))); DekiScriptEnv env = runtime.CreateEnv(); env.Vars.Add(DekiScriptRuntime.DEFAULT_ID, keyvalue); result.Add(entry.Key, runtime.Evaluate(expr, DekiScriptEvalMode.EvaluateSafeMode, env)); } return(result); }
public static object ListReduce( [DekiScriptParam("list value")] ArrayList list, [DekiScriptParam("expression to compute combined value (use '$value' and '$item' to refer to the current value and item, respectively)")] string expression, [DekiScriptParam("starting value (default: nil)", true)] object value, DekiScriptRuntime runtime ) { DekiScriptExpression expr = DekiScriptParser.Parse(new Location("list.reduce(expression)"), expression); foreach (object entry in list) { DekiScriptEnv env = runtime.CreateEnv(); DekiScriptMap values = new DekiScriptMap(); values.Add("value", DekiScriptLiteral.FromNativeValue(value)); values.Add("item", DekiScriptLiteral.FromNativeValue(entry)); env.Vars.Add(DekiScriptRuntime.DEFAULT_ID, values); value = runtime.Evaluate(expr, DekiScriptEvalMode.EvaluateSafeMode, env).NativeValue; } return(value); }
public static XDoc WebLink( [DekiScriptParam("link uri")] string uri, [DekiScriptParam("link contents; can be text, an image, or another document (default: link uri)", true)] object text, [DekiScriptParam("link hover title (default: none)", true)] string title, [DekiScriptParam("link target (default: none)", true)] string target ) { // check __settings.nofollow if links should be followed or not bool nofollow = true; DreamContext context = DreamContext.CurrentOrNull; if (context != null) { DekiScriptEnv env = context.GetState <DekiScriptEnv>(); if (env != null) { // TODO (steveb): this should be stored in the runtime instead! DekiScriptBool setting = env.Vars.GetAt(new[] { DekiScriptEnv.SETTINGS, "nofollow" }) as DekiScriptBool; if (setting != null) { nofollow = setting.AsBool() ?? true; } } } XDoc result = DekiScriptLiteral.FromNativeValue(text ?? uri).AsEmbeddableXml(true); XDoc body = result["body[not(@target)]"]; result.Start("body").Start("a"); if (nofollow) { result.Attr("rel", "custom nofollow"); } else { result.Attr("rel", "custom"); } result.Attr("href", WebCheckUri(uri)).Attr("title", title).Attr("target", target).AddNodes(body).End().End(); body.Remove(); return(result); }
public static int ListCount( [DekiScriptParam("list value")] ArrayList list, [DekiScriptParam("condition to execute for each item (use '$' to refer to the item)")] string condition, DekiScriptRuntime runtime ) { DekiScriptExpression expr = DekiScriptParser.Parse(new Location("list.count(condition)"), condition); int count = 0; foreach (object entry in list) { DekiScriptEnv env = runtime.CreateEnv(); env.Vars.Add(DekiScriptRuntime.DEFAULT_ID, DekiScriptLiteral.FromNativeValue(entry)); DekiScriptLiteral test = runtime.Evaluate(expr, DekiScriptEvalMode.EvaluateSafeMode, env); if (!test.IsNilFalseZero) { ++count; } } return(count); }
public static string StringSerialize( [DekiScriptParam("value", true)] object value ) { return(DekiScriptLiteral.FromNativeValue(value).ToString()); }
public object Store( [DekiExtParam("document id")] string id, [DekiExtParam("document to cache", true)] object document, [DekiExtParam("caching duration in seconds (range: 300+; default: 300)", true)] double?ttl ) { if (document == null) { return(Clear(id)); } // fetch entry from cache CacheEntry result; bool isNew = true; lock (_cacheLookup) { _cacheLookup.TryGetValue(id, out result); } // check if we have a cached entry if (result != null) { _log.DebugFormat("cache hit for '{0}'", result.Id); isNew = false; result.ResetMemoryExpiration(); } else { _log.DebugFormat("new cache item for '{0}'", id); result = new CacheEntry(id, ttl); } // update cache with document DekiScriptLiteral literal = DekiScriptLiteral.FromNativeValue(document); XDoc xml = new DekiScriptList().Add(literal).ToXml(); result.Cache = xml.ToString(); // start timer to clean-up cached result if (result.Cache != null) { XDoc infoDoc = new XDoc("cache-entry") .Elem("guid", result.Guid) .Elem("id", result.Id) .Elem("expires", result.Expires); lock (result) { Storage.At(CACHE_DATA, result.Guid + ".bin").PutAsync(new DreamMessage(DreamStatus.Ok, null, MimeType.BINARY, Encoding.UTF8.GetBytes(result.Cache))).Wait(); Storage.At(CACHE_INFO, result.Guid + ".xml").PutAsync(infoDoc).Wait(); } if (isNew) { lock (_cacheLookup) { _cacheLookup[id] = result; } // this timer removes the cache entry from disk SetupCacheTimer(result); } } return(document); }
public DekiScriptOutputBuffer.Range Visit(DekiScriptBinary expr, DekiScriptExpressionEvaluationState state) { DekiScriptExpression Left = expr.Left; DekiScriptExpression Right = expr.Right; switch (expr.OpCode) { case DekiScriptBinary.Op.LeftValue: return(Left.VisitWith(this, state)); case DekiScriptBinary.Op.IdentityEqual: { DekiScriptLiteral left = state.Pop(Left.VisitWith(this, state)); DekiScriptLiteral right = state.Pop(Right.VisitWith(this, state)); return(state.Push(DekiScriptBinary.IdentityEqual(left, right))); } case DekiScriptBinary.Op.IdentityNotEqual: { DekiScriptLiteral left = state.Pop(Left.VisitWith(this, state)); DekiScriptLiteral right = state.Pop(Right.VisitWith(this, state)); return(state.Push(DekiScriptBinary.IdentityNotEqual(left, right))); } case DekiScriptBinary.Op.IsType: { DekiScriptLiteral left = state.Pop(Left.VisitWith(this, state)); string type = ((DekiScriptString)Right).Value; return(state.Push(DekiScriptExpression.Constant(type.EqualsInvariantIgnoreCase("any") || left.ScriptTypeName.EqualsInvariantIgnoreCase(type)))); } case DekiScriptBinary.Op.Equal: { DekiScriptLiteral left = state.Pop(Left.VisitWith(this, state)); DekiScriptLiteral right = state.Pop(Right.VisitWith(this, state)); return(state.Push(DekiScriptBinary.Equal(left, right))); } case DekiScriptBinary.Op.NotEqual: { DekiScriptLiteral left = state.Pop(Left.VisitWith(this, state)); DekiScriptLiteral right = state.Pop(Right.VisitWith(this, state)); return(state.Push(DekiScriptBinary.NotEqual(left, right))); } case DekiScriptBinary.Op.GreaterOrEqual: { DekiScriptLiteral left = state.Pop(Left.VisitWith(this, state)); DekiScriptLiteral right = state.Pop(Right.VisitWith(this, state)); if (DekiScriptLiteral.CoerceValuesToSameType(ref left, ref right)) { switch (left.ScriptType) { case DekiScriptType.BOOL: case DekiScriptType.NUM: return(state.Push(DekiScriptExpression.Constant(left.AsNumber() >= right.AsNumber()))); case DekiScriptType.STR: return(state.Push(DekiScriptExpression.Constant(left.AsString().CompareInvariant(right.AsString()) >= 0))); default: return(DekiScriptOutputBuffer.Range.Empty); } } else { return(DekiScriptOutputBuffer.Range.Empty); } } case DekiScriptBinary.Op.GreaterThan: { DekiScriptLiteral left = state.Pop(Left.VisitWith(this, state)); DekiScriptLiteral right = state.Pop(Right.VisitWith(this, state)); if (DekiScriptLiteral.CoerceValuesToSameType(ref left, ref right)) { switch (left.ScriptType) { case DekiScriptType.BOOL: case DekiScriptType.NUM: return(state.Push(DekiScriptExpression.Constant(left.AsNumber() > right.AsNumber()))); case DekiScriptType.STR: return(state.Push(DekiScriptExpression.Constant(left.AsString().CompareInvariant(right.AsString()) > 0))); default: return(DekiScriptOutputBuffer.Range.Empty); } } else { return(DekiScriptOutputBuffer.Range.Empty); } } case DekiScriptBinary.Op.LessOrEqual: { DekiScriptLiteral left = state.Pop(Left.VisitWith(this, state)); DekiScriptLiteral right = state.Pop(Right.VisitWith(this, state)); if (DekiScriptLiteral.CoerceValuesToSameType(ref left, ref right)) { switch (left.ScriptType) { case DekiScriptType.BOOL: case DekiScriptType.NUM: return(state.Push(DekiScriptExpression.Constant(left.AsNumber() <= right.AsNumber()))); case DekiScriptType.STR: return(state.Push(DekiScriptExpression.Constant(left.AsString().CompareInvariant(right.AsString()) <= 0))); default: return(DekiScriptOutputBuffer.Range.Empty); } } else { return(DekiScriptOutputBuffer.Range.Empty); } } case DekiScriptBinary.Op.LessThan: { DekiScriptLiteral left = state.Pop(Left.VisitWith(this, state)); DekiScriptLiteral right = state.Pop(Right.VisitWith(this, state)); if (DekiScriptLiteral.CoerceValuesToSameType(ref left, ref right)) { switch (left.ScriptType) { case DekiScriptType.BOOL: case DekiScriptType.NUM: return(state.Push(DekiScriptExpression.Constant(left.AsNumber() < right.AsNumber()))); case DekiScriptType.STR: return(state.Push(DekiScriptExpression.Constant(left.AsString().CompareInvariant(right.AsString()) < 0))); default: return(DekiScriptOutputBuffer.Range.Empty); } } else { return(DekiScriptOutputBuffer.Range.Empty); } } case DekiScriptBinary.Op.LogicalAnd: { DekiScriptLiteral result = state.Pop(Left.VisitWith(this, state)); if (!result.IsNilFalseZero) { return(Right.VisitWith(this, state)); } return(state.Push(result)); } case DekiScriptBinary.Op.LogicalOr: { DekiScriptLiteral result = state.Pop(Left.VisitWith(this, state)); if (result.IsNilFalseZero) { return(Right.VisitWith(this, state)); } return(state.Push(result)); } case DekiScriptBinary.Op.Addition: return(state.Push(DekiScriptExpression.Constant(state.Pop(Left.VisitWith(this, state)).AsNumber() + state.Pop(Right.VisitWith(this, state)).AsNumber()))); case DekiScriptBinary.Op.Division: return(state.Push(DekiScriptExpression.Constant(state.Pop(Left.VisitWith(this, state)).AsNumber() / state.Pop(Right.VisitWith(this, state)).AsNumber()))); case DekiScriptBinary.Op.Modulo: { DekiScriptLiteral left = state.Pop(Left.VisitWith(this, state)); DekiScriptLiteral right = state.Pop(Right.VisitWith(this, state)); if ((left is DekiScriptString) && ((right is DekiScriptMap) || (right is DekiScriptList))) { // NOTE (steveb): string.format shorthand notation: "abc = $abc" % { abc: 123 } -OR- "0 = $0" % [ 123 ] return(state.Push(DekiScriptLiteral.FromNativeValue(DekiScriptLibrary.StringFormat(((DekiScriptString)left).Value, right.NativeValue)))); } else { return(state.Push(DekiScriptExpression.Constant(left.AsNumber() % right.AsNumber()))); } } case DekiScriptBinary.Op.Multiplication: return(state.Push(DekiScriptExpression.Constant(state.Pop(Left.VisitWith(this, state)).AsNumber() * state.Pop(Right.VisitWith(this, state)).AsNumber()))); case DekiScriptBinary.Op.Subtraction: return(state.Push(DekiScriptExpression.Constant(state.Pop(Left.VisitWith(this, state)).AsNumber() - state.Pop(Right.VisitWith(this, state)).AsNumber()))); case DekiScriptBinary.Op.NullCoalesce: { DekiScriptLiteral result = state.Pop(Left.VisitWith(this, state)); if (result.IsNil) { return(Right.VisitWith(this, state)); } return(state.Push(result)); } case DekiScriptBinary.Op.Concat: { DekiScriptLiteral left = state.Pop(Left.VisitWith(this, state)); DekiScriptLiteral right = state.Pop(Right.VisitWith(this, state)); if (left is DekiScriptNil) { return(state.Push(right)); } else if (right is DekiScriptNil) { return(state.Push(left)); } else if ((left is DekiScriptMap) && (right is DekiScriptMap)) { // left and right expressions are maps, merge them DekiScriptMap result = new DekiScriptMap(); result.AddRange((DekiScriptMap)left); result.AddRange((DekiScriptMap)right); return(state.Push(result)); } else if ((left is DekiScriptList) && (right is DekiScriptList)) { // left and right expressions are lists, concatenate them DekiScriptList result = new DekiScriptList(); result.AddRange((DekiScriptList)left); result.AddRange((DekiScriptList)right); return(state.Push(result)); } else { // treat left and right expressions as strings string leftText = left.AsString(); string rightText = right.AsString(); if ((leftText != null) && (rightText != null)) { return(state.Push(DekiScriptExpression.Constant(leftText + rightText))); } else if (leftText != null) { return(state.Push(DekiScriptExpression.Constant(leftText))); } else if (rightText != null) { return(state.Push(DekiScriptExpression.Constant(rightText))); } else { return(DekiScriptOutputBuffer.Range.Empty); } } } case DekiScriptBinary.Op.UriAppend: { // TODO (steveb): we should throw an exception when the LHS is not a valid string or uri XUri left = XUri.TryParse(state.Pop(Left.VisitWith(this, state)).AsString()); string result = null; if (left != null) { DekiScriptLiteral right = state.Pop(Right.VisitWith(this, state)); if (right is DekiScriptString) { result = DekiScriptLibrary.UriBuild(left, right.AsString(), null); } else if (right is DekiScriptMap) { result = DekiScriptLibrary.UriBuild(left, null, (Hashtable)right.NativeValue); } else { result = left.ToString(); } } return(state.Push(DekiScriptLiteral.FromNativeValue(result))); } case DekiScriptBinary.Op.InCollection: { DekiScriptLiteral left = state.Pop(Left.VisitWith(this, state)); DekiScriptLiteral right = state.Pop(Right.VisitWith(this, state)); if (right is DekiScriptList) { foreach (DekiScriptLiteral item in ((DekiScriptList)right).Value) { if (!DekiScriptBinary.Equal(left, item).IsNilFalseZero) { return(state.Push(DekiScriptBool.True)); } } return(state.Push(DekiScriptBool.False)); } else if (right is DekiScriptMap) { foreach (DekiScriptLiteral item in ((DekiScriptMap)right).Value.Values) { if (!DekiScriptBinary.Equal(left, item).IsNilFalseZero) { return(state.Push(DekiScriptBool.True)); } } return(state.Push(DekiScriptBool.False)); } else { return(state.Push(DekiScriptBool.False)); } } } throw new InvalidOperationException("invalid op code:" + expr.OpCode); }