public void ExecuteAsList(Context sender, StringBuilder output) { if (Lines.Length == 0) { sender.Root.Log.Error(StringsScripting.List_empty); return; } if (Lines.Length == 1) { vm.ExecLine(sender, Lines[0].Data, output); return; } // get the weight of each line. var weight = getWeight(sender); float max = weight[weight.Length - 1]; // get a random value from 0 to max. float r = (float)(random.NextDouble() * max); // find the weight that is within r. float w; for (int i = 0; i < weight.Length; ++i) { w = weight[i]; if (w <= 0f) continue; else if (r <= w) { vm.ExecLine(sender, Lines[i].Data, output); return; } } sender.Root.Log.Error(StringsScripting.List_no_return); }
private static Variable elseif(Context sender, Variable[] args) { if (sender.LastIf == true) return new Variable(false); return @if(sender, args); }
private static Variable @else(Context sender, Variable[] args) { if (args.Length != 0) sender.Root.Log.ErrorF(StringsScripting.Formatted_Function_arguments_not_empty, "Else"); sender.ExitLine = sender.LastIf; sender.LastIf = !sender.ExitLine; return new Variable(sender.LastIf); }
public virtual void WriteValueUser(Context sender, StringBuilder output) { if (!IsSet || sender.Root.Valid == BlockBase.Validation.Running) return; if (Value is Script && ((Script)Value).List) ((Script)Value).ExecuteAsList(sender, output); else output.Append(Value.ToString()); }
private static Variable wait(Context sender, Variable[] args) { if (args.Length == 0) sender.Root.Log.WarningF(StringsScripting.Formatted_Function_arguments_empty, "Wait"); else if (args.Length == 1) sender.Controller.Add(new Scripting.Events.Timed(new TimeSpan(0, 0, (int)toNumber(args[0])), false)); else sender.Root.Log.WarningF(StringsScripting.Formatted_Function_arguments_more_than_one, "Wait"); return null; }
private static Variable restart(Context sender, Variable[] args) { if (args.Length != 0) sender.Root.Log.WarningF(StringsScripting.Formatted_Function_arguments_not_empty, "Restart"); if (sender.Root.Valid == BlockBase.Validation.Passed) { sender.Repeat = true; sender.Line = 0; } return null; }
private static Variable isSet(Context sender, Variable[] args) { if (args.Length == 0) { sender.Root.Log.WarningF(StringsScripting.Formatted_Function_arguments_empty, "IsSet"); return new Variable(false); } foreach (var arg in args) { if (!arg.IsSet) return new Variable(false); } return new Variable(true); }
public Key(string str, Context context) { str = VM.KeyClean(str, context.Root.Log); keys = str.Split(keySeparator); index = 0; if (keys.Length > 0) { if (keys[0] == "") keys[0] = "self"; else if (keys[0] == "script" || keys[0] == "list") { if (keys[1] == "") keys[1] = context.Root.Group.Key; if (keys.Length == 2) keys = new string[] { keys[0], context.Root.Group.Key, keys[1] }; } } }
private static Variable @if(Context sender, Variable[] args) { sender.ExitLine = false; foreach (var arg in args) { if (arg.IsSet == false) sender.ExitLine = true; else if (arg.Value is bool == false) { sender.Root.Log.ErrorF(StringsScripting.Formatted_Function_invalid_type, "If", arg.Value.GetType().Name, typeof(bool).Name); sender.ExitLine = true; } else if ((bool)arg.Value == false) sender.ExitLine = true; } sender.LastIf = !sender.ExitLine; return new Variable(sender.LastIf); }
private static Variable random(Context sender, Variable[] args) { if (args.Length == 0) return new Variable((float)Random.NextDouble()); int min = 0; int max = 0; if (args.Length == 1) max = (int)toNumber(args[0]); else if (args.Length == 2) { min = (int)toNumber(args[0]); max = (int)toNumber(args[1]); } else // ToDo : Add to strings: sender.Root.Log.WarningF("{0} Only accepts 0-2 arguments!", "Random"); return new Variable((float)Random.Next(min, max)); }
private float[] getWeight(Context sender) { var weight = new float[Lines.Length]; float max = 0f; for (int l = 0; l < Lines.Length; ++l) { Line line = Lines[l]; if (line.Data.StartsWith("#x(", StringComparison.InvariantCultureIgnoreCase)) { int i = 1; string key; Variable[] args; vm.execSplitCommand(sender, line.Data, ref i, out key, out args); if (args.Length > 0 && args[0].IsSet) { object val = args[0].Value; if (val is float) weight[l] = max += (float)val; else if (val is bool) { if ((bool)val == true) weight[l] = max += 1f; else weight[l] = -1f; } else { sender.Root.Log.Error(string.Format(StringsScripting.Formatted_List_invalid_weight_type, val.GetType().Name)); weight[l] = -1f; } } } else weight[l] = max += 1f; } return weight; }
private static Variable @goto(Context sender, Variable[] args) { if (args.Length == 0) { sender.Root.Log.ErrorF(StringsScripting.Formatted_Function_arguments_empty, "GoTo"); return null; } int i = -1; foreach (var arg in args) { ++i; if (!arg.IsSet) continue; BlockBase script = null; object value = arg.Value; if (value is BlockBase) script = (BlockBase)value; else if (value is VType.Query) script = sender.Controller.VM.QueryScript((VType.Query)value, sender.Root.Log); else if (value is string) // string can be a single query key. script = sender.Controller.VM.QueryScript((string)value, sender.Root.Log); else sender.Root.Log.ErrorF(StringsScripting.Formatted_Function_invalid_type, "GoTo", arg.Value.GetType().Name, typeof(Script).Name); if (script != null) { if (ReferenceEquals(script, sender.Root)) sender.Root.Log.Error(StringsScripting.FunctionGoTo_cannot_goto_self); else if (sender.Root.Valid != BlockBase.Validation.Running) sender.Controller.Add(script); } } return null; }
/// <summary> gets key and parentheses as args. </summary> /// <param name="i"> indexer, expected to start on the first character of the key. </param> /// <param name="key"> set to null if no key. </param> /// <param name="args"> never null </param> internal void execSplitCommand(Context sender, string str, ref int i, out string key, out Variable[] args) { args = null; var sb = new StringBuilder(); char c; while (i < str.Length) { c = str[i]; if (c == ' ') break; else if (c == '(') { ++i; args = execParentheses(sender, str, ref i); break; } else ++i; sb.Append(c); } if (sb.Length > 0) { bool isFloat; bool isPercent; key = KeyClean(sb.ToString(), out isFloat, out isPercent, sender.Root.Log); if (isFloat || isPercent) sender.Root.Log.Warning(string.Format(StringsScripting.Formatted_Expected_key_got_number, key)); } else key = null; if (args == null) args = new Variable[0]; }
/// <summary> /// Execute a line of code. /// </summary> /// <param name="sender"></param> /// <param name="line"></param> /// <param name="output"></param> internal void ExecLine(Context sender, string line, StringBuilder output) { var log = sender.Root.Log; string key; Variable[] args; int i = 0; char c; while (i < line.Length && !sender.ExitLine) { c = line[i]; ++i; switch (c) { case '#': case '@': execSplitCommand(sender, line, ref i, out key, out args); if (key != null) { Variable variable = sender.Get(new Key(key, sender), log); if (variable == null || variable.IsSet == false) continue; var func = variable.Value as Function; if (func != null) variable = func(sender, args); // ToDo : Do we need to do anything to other value types? // output if @ if (c == '@' && variable != null) variable.WriteValueUser(sender, output); } else { if (c == '@' && args != null && args.Length > 0 && args[0] != null) args[0].WriteValueUser(sender, output); } break; //case '\\': // ToDo : escape character. //break; default: output.Append(c); break; } } }
/// <summary> /// Check whats in sb, then add it to the list. /// [float, bool, and, or, variable] /// </summary> /// <returns>false if nothing was added.</returns> private void execParenthCheckAdd(Context sender, List<execParenthItem> items, StringBuilder sb) { if (sb.Length == 0) return; bool isFloat; bool isPercent; string str = KeyClean(sb.ToString(), out isFloat, out isPercent, sender.Root.Log); sb.Clear(); if (str == null) return; // is str float? if (isFloat) items.Add(new Variable(float.Parse(str))); // percent to float? else if (isPercent) items.Add(new Variable(float.Parse(str) * 0.01f)); // bool else if (str == "true") items.Add(new Variable(true)); else if (str == "false") items.Add(new Variable(false)); // logic operators else if (str == "not") items.Add(Operators.Not); else if (str == "and") items.Add(Operators.And); else if (str == "or") items.Add(Operators.Or); else // variable items.Add(sender.Get(new Key(str, sender))); return; }
/// <summary> /// Recursively parses and executes everything in the parentheses, then returns variable array. /// </summary> /// <param name="sender"></param> /// <param name="str"></param> /// <param name="i"> indexer, must be equal to the char after '(' </param> /// <returns></returns> private Variable[] execParentheses(Context sender, string str, ref int i) { var log = sender.Root.Log; int start = i; var outArgs = new List<Variable>(); outArgs.Add(null); // Set to the output before return. var items = new List<execParenthItem>(); // Split in to lists of values and operators. var sb = new StringBuilder(); bool finished = false; char c; while (i < str.Length && !finished) { c = str[i]; switch (c) { // string case '"': execParenthCheckAdd(sender, items, sb); ++i; items.Add(new Variable(parseStringQuoted(str, ref i, log))); continue; // finish parentheses case ')': finished = true; break; // sub parentheses case '(': { int count = items.Count; execParenthCheckAdd(sender, items, sb); // check if it's attached to a function bool funcParenth = false; if (count < items.Count && items[items.Count - 1].IsValue) { var v = items[items.Count - 1].Value; funcParenth = v.IsSet && v.Value is Function; } ++i; var _char = i; var args = execParentheses(sender, str, ref i); if (funcParenth) // simply add the whole array as-is. items.Add(new Variable(args)); else if (args.Length == 0) log.Warning(StringsScripting.Sub_parentheses_zero_arguments, -1, _char); else { items.Add(args[0]); // sense it's not for a function, we only care about the first arg. if (args.Length > 1) log.Warning(StringsScripting.Sub_parentheses_too_many_arguments, -1, _char); } } continue; // new arg case ',': execParenthCheckAdd(sender, items, sb); ++i; outArgs.AddRange(execParentheses(sender, str, ref i)); finished = true; // finished, sense execParentheses will go until end. continue; // white-space separates stuffs case ' ': case '\t': execParenthCheckAdd(sender, items, sb); break; // equal and assign case '=': execParenthCheckAdd(sender, items, sb); // (a=b) is not equal to (a==b). The first assigns b to a and returns a, second returns bool if a equals b. // so we must use different operators. if (i + 1 < str.Length && str[i + 1] == '=') // == { ++i; items.Add(Operators.Equal); } else // just a single = items.Add(Operators.Assign); break; // math case '*': execParenthCheckAdd(sender, items, sb); items.Add(Operators.Multiply); break; case '/': execParenthCheckAdd(sender, items, sb); items.Add(Operators.Divide); break; case '+': execParenthCheckAdd(sender, items, sb); items.Add(Operators.Add); break; case '-': execParenthCheckAdd(sender, items, sb); items.Add(Operators.Subtract); break; // logic // "and" "or" are handled in execParenthCheckAdd case '>': execParenthCheckAdd(sender, items, sb); items.Add(Operators.More); break; case '<': execParenthCheckAdd(sender, items, sb); items.Add(Operators.Less); break; default: sb.Append(c); break; } ++i; } execParenthCheckAdd(sender, items, sb); log.SetId(-1, start); // check if empty if (items.Count == 0) { log.Warning(StringsScripting.Parentheses_empty); return new Variable[0]; } else if (items[0].IsValue == false && items[0].Operator != Operators.Not) { log.Error(StringsScripting.Parentheses_first_item_is_not_variable); return new Variable[0]; } // Evaluate int j; // Operator precedence // 1. Parentheses ( ) // 2. Execute functions // 3. Logic Not not // 4. Multiply & Divide * / // 5. Add & Subtract + - // 6. logic 1 > < == // 7. logic 2 and or // 8. Assignment = //Note: 'not' and assignment goes right to left. // 1. Done when we parse // 2. Execute functions j = 0; for (; j < items.Count; ++j) { if (items[j].IsValue) { var func = items[j].Value.Value as Function; if (func == null) continue; Variable[] args = null; // check if args is next item if (j + 1 < items.Count && items[j + 1].IsValue) { var argvar = items[j + 1].Value.Value as Variable[]; if (argvar == null) { log.Error(StringsScripting.Parentheses_Expected_function_arguments); return new Variable[0]; } args = argvar; items.RemoveAt(j + 1); } if (args == null) args = new Variable[0]; items[j] = func(sender, args); } } // 3. Logic not j = items.Count - 2; while (j >= 0) { if (!items[j].IsValue && items[j].Operator == Operators.Not) { var r = items[j + 1].Value; items[j] = Variable.Evaluate(sender, null, Operators.Not, r); items.RemoveAt(j + 1); } --j; } // At this point it is required that there is exactly one operator inbetween each variable. j = 0; while (j + 2 < items.Count) { var l = items[j]; var o = items[j + 1]; var r = items[j + 2]; if (!l.IsValue || !r.IsValue) { if (!l.IsValue) log.ErrorF(StringsScripting.Formatted_Expected_variable_got_operator, l.Operator); if (!r.IsValue) log.ErrorF(StringsScripting.Formatted_Expected_variable_got_operator, r.Operator); return new Variable[0]; } else if (o.IsValue) { log.Error(StringsScripting.Expected_operator_got_variable); return new Variable[0]; } else if (o.Operator == Operators.Not) { log.ErrorF(StringsScripting.Formatted_Unexpected_Operator, o.ToString()); return new Variable[0]; } j += 2; } // 4. Multiply & Divide j = 0; while (j + 2 < items.Count) { var l = items[j].Value; var op = items[j + 1].Operator; var r = items[j + 2].Value; if (op == Operators.Multiply || op == Operators.Divide) { items[j] = Variable.Evaluate(sender, l, op, r); items.RemoveRange(j + 1, 2); } else j += 2; } // 5. Add & Subtract j = 0; while (j + 2 < items.Count) { var l = items[j].Value; var op = items[j + 1].Operator; var r = items[j + 2].Value; if (op == Operators.Add || op == Operators.Subtract) { items[j] = Variable.Evaluate(sender, l, op, r); items.RemoveRange(j + 1, 2); } else j += 2; } // 6. logic 1 j = 0; while (j + 2 < items.Count) { var l = items[j].Value; var op = items[j + 1].Operator; var r = items[j + 2].Value; if (op == Operators.More || op == Operators.Less || op == Operators.Equal) { items[j] = Variable.Evaluate(sender, l, op, r); items.RemoveRange(j + 1, 2); } else j += 2; } // 7. logic 2 j = 0; while (j + 2 < items.Count) { var l = items[j].Value; var op = items[j + 1].Operator; var r = items[j + 2].Value; if (op == Operators.And || op == Operators.Or) { items[j] = Variable.Evaluate(sender, l, op, r); items.RemoveRange(j + 1, 2); } else j += 2; } // 8. Assignment j = items.Count - 1; while (j - 2 >= 0) { var l = items[j - 2].Value; var op = items[j - 1].Operator; var r = items[j - 0].Value; if (op == Operators.Assign) { items[j - 2] = Variable.Evaluate(sender, l, op, r); items.RemoveRange(j - 1, 2); } j -= 2; } // finily finished if (items.Count != 1) { // ToDo : probley just return a array of the items? (if not add to StringsScripting.txt) log.Error(string.Format("execParentheses items.Count is {0}, expecting a count of 1!", items.Count)); return new Variable[0]; } outArgs[0] = items[0].Value; return outArgs.ToArray(); }
public static Variable Evaluate(Context sender, Variable left, Operators op, Variable right) { var log = sender.Root.Log; bool validating = sender.Root.Valid == BlockBase.Validation.Running; object l = null, r; // make sure variable is not null. if (right == null) { log.Error(StringsScripting.Evaluate_null_variable); return null; } r = right.Value; if (op != Operators.Not) // "Not" doesn't use left variable. { if (left == null) { log.Error(StringsScripting.Evaluate_null_variable); return null; } l = left.Value; } // allow equals to work on unset variables. if (op == Operators.Equal) { if (l == null || r == null) return new Variable(l == null && r == null); } // make sure variable is set. if (!right.IsSet) { log.Error(string.Format(StringsScripting.Formatted_Evaluate_Operator_unset_variable, op.ToString())); // ToDo : Different error? return null; } if (op != Operators.Not && op != Operators.Assign) // "Not" and "Assign" can use a unset left variable. { if (!left.IsSet) { log.Error(string.Format(StringsScripting.Formatted_Evaluate_Operator_unset_variable, op.ToString())); // ToDo : Different error? return null; } } // Do not allow assign to readonly variables. if (op == Operators.Assign && left.Readonly) { log.Error(StringsScripting.Evaluate_Assign_Readonly); return null; } // try to evaluate the viriable value directly. if (l is IVType) { var results = ((IVType)l).Evaluate(sender, left, op, right); if (results != null) return results; } if (r is IVType) { var results = ((IVType)r).Evaluate(sender, left, op, right); if (results != null) return results; } switch (op) { // logic not case Operators.Not: object value = right.Value; if (value is bool) return new Variable(!(bool)value); if (value is string) return new Variable(Query.Not((string)value)); log.Error(string.Format(StringsScripting.Formatted_Evaluate_Operator_type_invalid, op.ToString(), "", value.GetType().Name)); return null; // assign case Operators.Assign: if (left.IsSet) { if (l.GetType() == r.GetType()) { if (!validating) // Don't change variable if we are validating. left.Value = r; } else { log.Error(string.Format(StringsScripting.Formatted_Evaluate_Assign_type_mismatch, l.GetType().Name, r.GetType().Name)); return null; } } else { if (validating) left.Value = getDefault(right.Value.GetType()); else left.Value = right.Value; } return left; // Math case Operators.Add: if (l is float && r is float) return new Variable((float)l + (float)r); if (l is string && r is string) return new Variable(string.Concat(l, r)); log.Error(string.Format(StringsScripting.Formatted_Evaluate_Operator_type_invalid, op.ToString(), l.GetType().Name, r.GetType().Name)); return null; case Operators.Subtract: if (l is float && r is float) return new Variable((float)l - (float)r); if (l is string && r is string) return new Variable(((string)l).Replace((string)r, "")); log.Error(string.Format(StringsScripting.Formatted_Evaluate_Operator_type_invalid, op.ToString(), l.GetType().Name, r.GetType().Name)); return null; case Operators.Multiply: if (l is float && r is float) return new Variable((float)l * (float)r); if (l is float && r is bool) return new Variable((float)l * ((bool)r ? 1f : 0f)); log.Error(string.Format(StringsScripting.Formatted_Evaluate_Operator_type_invalid, op.ToString(), l.GetType().Name, r.GetType().Name)); return null; case Operators.Divide: if (l is float && r is float) { if (validating) return new Variable(default(float)); float fl = (float)l; float fr = (float)r; if (fr == 0) { log.Warning(StringsScripting.Divide_by_zero); fr = 1; } return new Variable(fl / fr); } log.Error(string.Format(StringsScripting.Formatted_Evaluate_Operator_type_invalid, op.ToString(), l.GetType().Name, r.GetType().Name)); return null; // Logic case Operators.Equal: if (l is string && r is string) // for strings we want to ignore the case. return new Variable((l as string).Equals((string)r, StringComparison.InvariantCultureIgnoreCase)); return new Variable(l.Equals(r)); case Operators.More: if (l is float && r is float) return new Variable((float)l > (float)r); log.Error(string.Format(StringsScripting.Formatted_Evaluate_Operator_type_invalid, op.ToString(), l.GetType().Name, r.GetType().Name)); return null; case Operators.Less: if (l is float && r is float) return new Variable((float)l < (float)r); log.Error(string.Format(StringsScripting.Formatted_Evaluate_Operator_type_invalid, op.ToString(), l.GetType().Name, r.GetType().Name)); return null; case Operators.And: if (l is bool && r is bool) return new Variable((bool)l && (bool)r); if (l is string && r is string) return new Variable(new Query((string)l, op, (string)r)); log.Error(string.Format(StringsScripting.Formatted_Evaluate_Operator_type_invalid, op.ToString(), l.GetType().Name, r.GetType().Name)); return null; case Operators.Or: if (l is bool && r is bool) return new Variable((bool)l || (bool)r); if (l is string && r is string) return new Variable(new Query((string)l, op, (string)r)); log.Error(string.Format(StringsScripting.Formatted_Evaluate_Operator_type_invalid, op.ToString(), l.GetType().Name, r.GetType().Name)); return null; } log.Error(string.Format(StringsScripting.Formatted_Evaluate_Invalid_operator, op.ToString())); return null; }
private static Variable date(Context sender, Variable[] args) { if (args.Length == 0) return new Variable(new Date(DateTime.Now)); if (args.Length == 1) { if (!args[0].IsSet) sender.Root.Log.ErrorF(StringsScripting.Formatted_Function_argument_unset, "Date", ""); else if (args[0].Value is string) { DateTime result; try { if (DateTime.TryParse((string)args[0].Value, out result)) return new Variable(new Date(result)); else sender.Root.Log.ErrorF(StringsScripting.Formatted_Unable_to_parse, "Date", (string)args[0].Value, typeof(DateTime).Name); } catch (ArgumentOutOfRangeException ex) { sender.Root.Log.Error(ex.Message); } } else sender.Root.Log.ErrorF(StringsScripting.Formatted_Function_invalid_type, "Date", args[0].Value.GetType().Name, typeof(string).Name); return new Variable(new Date(DateTime.Now)); } else sender.Root.Log.WarningF(StringsScripting.Formatted_Function_arguments_less_than_two, "Date"); return new Variable(new Date(DateTime.Now)); }
private static Variable time(Context sender, Variable[] args) { if (args.Length == 0) return new Variable(new TimeFrame(TimeSpan.Zero)); if (args.Length == 1) { if (!args[0].IsSet) sender.Root.Log.ErrorF(StringsScripting.Formatted_Function_argument_unset, "Time", ""); else if (args[0].Value is string) { TimeSpan result; if (TimeSpan.TryParse((string)args[0].Value, out result)) return new Variable(new TimeFrame(result)); else sender.Root.Log.ErrorF(StringsScripting.Formatted_Unable_to_parse, "Time", (string)args[0].Value, typeof(TimeSpan).Name); } else sender.Root.Log.ErrorF(StringsScripting.Formatted_Function_invalid_type, "Time", args[0].Value.GetType().Name, typeof(string).Name); return new Variable(new TimeFrame(TimeSpan.Zero)); } else sender.Root.Log.WarningF(StringsScripting.Formatted_Function_arguments_less_than_two, "Time"); return new Variable(new TimeFrame(TimeSpan.Zero)); }