internal Personality(VM vm, string name, string id) { VM = vm; // These variables MUST always be set: variables["name"] = name_var = new Variable(name); variables["id"] = id_var = new Variable(id) { Readonly = true }; variables["enabled_user"] = enabledUser_var = new Variable(true); variables["birthday"] = new Variable(new VType.Date(DateTime.MinValue)); // readonly age, returns DateTime.Now - BirthDay variables["age"] = new VariableFunc(() => { try { var span = DateTime.Now - ((VType.Date)variables["birthday"].Value).Value; return (float)(new DateTime(1, 1, 1) + span).Year - 1f; } catch (ArgumentOutOfRangeException) { return -1f; } }, null); // ToDo 5: Add mood variables. }
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); }
private static float toNumber(Variable v) { float result = 0f; if (v != null && v.IsSet) float.TryParse(v.Value.ToString(), out result); // ToDo 9: Log error if not number. return result; }
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; }
public Variable Get(Key key, Logger log = null) { if (key.NextIf("local")) { Variable result; if (!Variables.TryGetValue(key.Peek, out result)) Variables[key.Peek] = result = new Variable(); return result; } else return Controller.Get(key, Root.Log); }
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); }
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 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]; }
public Script QueryScript(Variable query, Logger log) { if (query == null || !query.IsSet) log.Error(StringsScripting.Query_empty); else if (query.Value is VType.Query) return QueryScript((VType.Query)query.Value, log); else if (query.Value is string) return QueryScript((string)query.Value, log); else log.ErrorF(StringsScripting.Formatted_Invalid_Type, "Query", query.Value.GetType().Name); return null; }
public bool ChangePersonalityID(Personality p, string newID) { newID = KeyClean(newID); if (personalities.ContainsKey(newID)) return false; Dirty = true; Variable<Personality> var; if (personalities.TryRemove(p.ID, out var)) personalities[newID] = var; else personalities[newID] = new Variable<Personality>(p); p.ID = newID; return true; }
/// <summary> /// Craete a new personality with given name. /// </summary> /// <param name="name"></param> /// <returns>New Personality or null if name already exits.</returns> public Personality CreatePersonality(string name) { var id = KeyClean(name); Dirty = true; // If personality with id already exists, add number. if (personalities.ContainsKey(id)) { int num = 1; while (personalities.ContainsKey(id + (++num))) { } id = id + num; } var p = new Personality(this, name, id); personalities[id] = new Variable<Personality>(p); return p; }
/// <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(); }
private void writeValue(string prefix, StringBuilder sb, string key, Variable v) { if (v == null || !v.CanWriteValue()) return; sb.Append(prefix); sb.Append("#(."); sb.Append(key); sb.Append("="); v.WriteValue(sb); sb.AppendLine(")"); }
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; }
public Variable(Variable[] value) { _value = value; }
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)); }