/// <summary> /// unfreeze the status /// <para/>Exception will be raised if the context is not frozen /// </summary> public static void unfreeze() { if (Global.getSetting("flame mode")) { return; } Debugging.assert(_frozen); _frozen = false; }
/// <summary> /// Convert a list of <see cref="RawInstruction"/>s into a <see cref="Function"/> /// </summary> /// <param name="declaration_line"> First line, recursion, name, arguments</param> /// <param name="function_declaration"> The lines defining the function content</param> /// <returns> the corresponding <see cref="Function"/> object</returns> public static Function readFunction(string declaration_line, List <RawInstruction> function_declaration) { Debugging.assert(function_declaration.Count > 0); // >= 1 bool resp = false; // function decl List <string> decl = StringUtils.splitStringKeepingStructureIntegrity(declaration_line, ' ', Global.base_delimiters); if (decl.Count == 4) { // function KEYWORD type name(args) Parser.print("special function. definition with count 4: " + decl[1]); Debugging.assert(decl[1] == "recursive"); // hardcoded. decl.RemoveAt(1); resp = true; // REPLACE WITH "rec-function", "end-rec-function" } Debugging.assert(decl.Count == 3); // function type name(args) Debugging.assert(decl[0] == StringConstants.Keywords.FUNCTION_KEYWORD); // type string type_str = decl[1]; // name Debugging.assert(decl[2].Contains("(") && decl[2].Contains(")")); int name_sep_index = decl[2].IndexOf('('); string function_name = decl[2].Substring(0, name_sep_index); Debugging.assert(StringUtils.validObjectName(function_name), new AquilaExceptions.SyntaxExceptions.SyntaxError($"Invalid object name \"{function_name}\"")); // InvalidNamingException Debugging.assert(!functions_dict.ContainsKey(function_name), new AquilaExceptions.FunctionNameError($"The function \"{function_name}\" is a predefined function. You cannot overwrite it")); // args string function_args_str = decl[2].Substring(name_sep_index + 1); function_args_str = function_args_str.Substring(0, function_args_str.Length - 1); Parser.print("name: " + function_name); Parser.print("args: " + StringUtils.dynamic2Str(function_args_str)); List <string> function_args = function_args_str.Split(',').ToList(); for (int i = 0; i < function_args.Count; i++) { function_args[i] = StringUtils.normalizeWhiteSpaces(function_args[i]); } if (function_args.Count == 1 && function_args[0] == "") { function_args.Clear(); } Parser.print("args: " + StringUtils.dynamic2Str(function_args)); // Instructions List <Instruction> instr_list = function_declaration.Select(raw_instruction => raw_instruction.toInstr()).ToList(); return(new Function(function_name, type_str, function_args, instr_list, resp)); }
/// <summary> /// Get the <see cref="Variable"/> from the current variable Dictionary. /// You an give the variable name with or without the "$" (<see cref="StringConstants.Other.VARIABLE_PREFIX"/>) prefix /// </summary> /// <param name="var_name"> The variable name (with or without the prefix)</param> /// <returns> the corresponding <see cref="Variable"/></returns> private static Variable variableFromName(string var_name) { if (var_name.StartsWith(StringConstants.Other.VARIABLE_PREFIX)) { var_name = var_name.Substring(1); } Debugging.print("Variable access: ", var_name); //Interpreter.processInterpreterInput("vars"); Debugging.assert(Global.variableExistsInCurrentScope(var_name), new AquilaExceptions.NameError($"Variable name \"{var_name}\" does not exist in the current Context")); return(Global.variableFromName(var_name)); }
/// <summary> /// Default function /// </summary> /// <para> /// Creates a copy of a <see cref="DynamicList"/> /// </para> /// <param name="list_expr"> the target <see cref="DynamicList"/></param> /// <returns> A new <see cref="DynamicList"/>. It's values are the same as the target list</returns> private static Variable copyListFunction(Expression list_expr) { // evaluate every expression Variable var_ = list_expr.evaluate(); Debugging.assert(var_ is DynamicList); // TypeError DynamicList list = var_ as DynamicList; // copy list var raw = new List <dynamic>(list.getRawValue()); return(Variable.fromRawValue(raw)); }
/// <summary> /// Remove the last local context scope variable dict. /// This is called when exiting a nested instruction /// </summary> public static void resetLocalContextScope() { Debugging.print("exiting local scope (current): ", getLocalScopeDepth()); Debugging.assert(getLocalScopeDepth() > 0); foreach (var pair in getCurrentDict().Where(pair => pair.Value.isTraced())) { pair.Value.tracer.update(new Event(new Alteration("delete_var", pair.Value, pair.Value.getRawValue(), new dynamic[] {}))); } removeVariableStackElement(); Debugging.print("new local scope: " + getLocalScopeDepth()); }
/// <summary> /// Removes the last added variable stack layer. /// This is called when exiting, for example, a function call /// </summary> public static void resetMainContextScope() { Debugging.print("exiting main scope depth (current): " + getMainScopeDepth()); Debugging.assert(getMainScopeDepth() > 0); // cannot exit a function call or something in the main instruction loop foreach (var pair in _variable_stack.Peek().SelectMany(dictionary => dictionary.Where(pair => pair.Value.isTraced()))) { pair.Value.tracer.update(new Event(new Alteration("delete_var", pair.Value, pair.Value.getRawValue(), Array.Empty <dynamic>()))); } removeVariableStackLayer(); Debugging.print("new main scope depth: " + getMainScopeDepth()); }
/// <summary> /// /// </summary> /// <param name="name"></param> /// <param name="args"></param> /// <returns></returns> private static Dictionary <string, Variable> args2Dict(string name, object[] args) { Dictionary <string, Variable> d = new Dictionary <string, Variable>(); for (int i = 0; i < args.Length; i++) { Debugging.assert(args[i] is Expression); Variable v = (args[i] as Expression).evaluate(); string func_name = user_functions[name].func_args[i]; d.Add(func_name, v); } return(d); }
/// <summary> /// Append a value to the end of a list /// </summary> /// <param name="list_expr"> The list</param> /// <param name="value_expr"> The value</param> /// <returns> <see cref="NullVar"/> (equivalent of null/void)</returns> private static NullVar appendValue(Expression list_expr, Expression value_expr) { // extract list Variable list_var = list_expr.evaluate(); Debugging.assert(list_var is DynamicList); // TypeError DynamicList list = list_var as DynamicList; // index Integer index = list.length(); Expression index_expr = new Expression(index.getValue().ToString()); // this should definitely be ok // insert return(insertValueAt(list_expr, index_expr, value_expr)); }
public override int compare(Variable other) { Debugging.assert(hasSameParent(other)); dynamic other_value = other.getValue(); trace("compare", new [] { other_value }); if (numeric_value == other_value) { return(0); } if (numeric_value > other_value) { return(1); } return(-1); }
/// <summary> /// Delete the nth value of a list /// </summary> /// <param name="list_expr"> expression resulting in a <see cref="DynamicList"/> variable</param> /// <param name="index_expr"> expression resulting in a <see cref="Integer"/> variable</param> /// <returns> <see cref="NullVar"/> (equivalent of null/void)</returns> private static NullVar deleteValueAt(Expression list_expr, Expression index_expr) { // extract list Variable list_var = list_expr.evaluate(); Debugging.assert(list_var is DynamicList); // TypeError DynamicList list = list_var as DynamicList; // extract index Variable index_var = index_expr.evaluate(); Debugging.assert(index_var is Integer); // TypeError Integer index = index_var as Integer; // delete list.removeValue(index); return(new NullVar()); }
/// <summary> /// Calculate the square root of a float /// </summary> /// <param name="expr"> The float</param> /// <returns> The sqrt of the input float</returns> private static Variable sqrtFunction(Expression expr) { Variable v = expr.evaluate(); if (v is Integer) { int raw_int = v.getValue(); // ReSharper disable once RedundantCast float raw_float = (float)raw_int; v = new FloatVar(raw_float); } Debugging.assert(v is FloatVar); double real = (double)v.getValue(); float real_sqrt = (float)Math.Sqrt(real); return(new FloatVar(real_sqrt)); }
internal static void testModeInstructionUpdate() { Debugging.assert(Global.getSetting("test mode"), new AquilaExceptions.RuntimeError("Not in test mode")); // update instruction counter Global.test_values["instruction counter"]++; Debugging.print("- test run - Instruction count: ", Global.test_values["instruction counter"]); // still valid runtime ? if (Global.test_values["stopwatch"].ElapsedMilliseconds <= Global.test_values["max elapsed ms"] && Global.test_values["instruction counter"] <= Global.test_values["max instruction counter"]) { return; } // unfinished algorithm Global.test_values["unfinished run"] = true; throw new AquilaExceptions.RuntimeError("Test max run time exceeded"); }
/// <summary> /// Add teh <see cref="Event"/> on top of the <see cref="Tracer.events"/> stack and /// process the make some assertions to detect abnormal behaviours /// </summary> /// <param name="event_"></param> public override void update(Event event_) { // blocked context ? if (Context.isFrozen() && !Global.getSetting("allow tracing in frozen context")) { return; } // checks Debugging.assert(event_.alter != null); // variable events can only hold Alterations, so checking for null // update events.Push(event_); printTrace("updated (value: " + StringUtils.dynamic2Str(peekValue()) + ")"); // handle callUpdateHandler(event_.alter); }
public override int compare(Variable other) { Debugging.assert(hasSameParent(other)); DynamicList other_list = (DynamicList)other; if (length().compare(other_list.length()) != 0) { return(length().compare(other_list.length())); } List <Variable> other_list_value = other.getValue(); if (_list.Where((t, i) => other_list_value[i] != t).Any()) { return(-1); } return(0); }
/// <summary> /// Insert the given value in the list at the given index /// </summary> /// <param name="list_expr"> The list</param> /// <param name="index_expr"> The index</param> /// <param name="value_expr"> The value</param> /// <returns> <see cref="NullVar"/> (equivalent of null/void)</returns> private static NullVar insertValueAt(Expression list_expr, Expression index_expr, Expression value_expr) { // extract list Variable list_var = list_expr.evaluate(); Debugging.assert(list_var is DynamicList); // TypeError DynamicList list = list_var as DynamicList; // extract index Variable index_var = index_expr.evaluate(); Debugging.assert(index_var is Integer); Integer index = index_var as Integer; // extract var Variable var_ = value_expr.evaluate(); // insert list.insertValue(var_, index); return(new NullVar()); }
public Declaration(int line_index, string var_name, Expression var_expr, string var_type = StringConstants.Types.AUTO_TYPE, bool assignment = true, bool safe_mode = false, bool overwrite = false, bool constant = false, bool global = false) { // mode: 0 -> new var (must not exist); 1 -> force overwrite (must exist); 2 -> safe overwrite (can exist) this.line_index = line_index; _var_name = var_name; _var_expr = var_expr; _var_type = var_type; _assignment = assignment; _constant = constant; _global = global; // check variable naming Debugging.assert(StringUtils.validObjectName(var_name), new AquilaExceptions.SyntaxExceptions.SyntaxError($"Invalid object name \"{var_name}\"")); // the declaration is not initiated in the right scope... cannot do this here bool var_exists = Global.variableExistsInCurrentScope(var_name); Debugging.print("new declaration: var_name = " + var_name + ", var_expr = ", var_expr.expr, ", var_type = ", var_type, ", assignment = ", assignment, ", mode = ", StringUtils.boolString(safe_mode, overwrite, _constant, _global), ", exists = ", var_exists); // should not check overwriting modes if this is true if (Global.getSetting("implicit declaration in assignment")) { Debugging.print("implicit declaration in assignments, so skipping var existence checks (var exists: ", var_exists, ")"); return; } if (safe_mode && var_exists) { Debugging.assert(!Global.variableFromName(var_name).isTraced()); } if (overwrite) { Debugging.assert(var_exists); } }
/// <summary> /// Initialize the test run mode /// </summary> private static void initTestMode() { Debugging.assert(!Global.getSetting("test mode"), new AquilaExceptions.RuntimeError("Already in test mode")); Global.setSetting("test mode", true); if (Global.test_values.Count != 0) { Global.test_values.Clear(); } Stopwatch stopwatch = new Stopwatch(); Global.test_values.Add("stopwatch", stopwatch); Global.test_values.Add("max elapsed ms", 10000); // n/1000 seconds Global.test_values.Add("instruction counter", 0); Global.test_values.Add("max instruction counter", 120); Global.test_values.Add("unfinished run", false); Global.test_values.Add("variable names", new List <string>()); Global.test_values.Add("value variables", new List <string>()); Global.test_values.Add("index variables", new List <string>()); stopwatch.Start(); }
/// <summary> /// Call the function with input parameters (args) /// </summary> /// <param name="args"> The variables defining the new Main Context Stack</param> /// <returns> The return value of the function</returns> public Variable callFunction(Dictionary <string, Variable> args) { initialize(args); Debugging.assert(_in_function_scope); foreach (Instruction instruction in _instructions) { try { instruction.execute(); // return here (not continue, nor break) } catch (System.Reflection.TargetInvocationException out_exception) { if (!(out_exception.InnerException is AquilaControlFlowExceptions.ReturnValueException return_value_exception)) { throw; } Debugging.print("ReturnValueException was thrown"); string return_value_string = return_value_exception.getExprStr(); Expression return_value_expression = new Expression(return_value_string); Variable return_value = return_value_expression.evaluate(); if (_type != StringConstants.Types.AUTO_TYPE && _type != StringConstants.Types.NULL_TYPE) { Debugging.assert(return_value.getTypeString() == _type); } restore(); return(return_value); } } Debugging.print("no ReturnValueException thrown. returning NullVar"); if (_type != StringConstants.Types.AUTO_TYPE) { Debugging.assert(_type == StringConstants.Types.NULL_TYPE); } restore(); return(new NullVar()); }
/// <summary> /// Set the value of the <see cref="_traced_var"/> (traced <see cref="Variable"/>) to be 'n' steps in the past ('n' <see cref="Alteration"/>s) /// </summary> /// <param name="n"> number of times to rewind the value of <see cref="_traced_var"/> once</param> public void rewind(int n = 1) { last_stack_count -= n; Debugging.assert(events.Count > 1); // 1 is for variable creation Debugging.assert(n < events.Count); // didn't rewind before creation of variable ? dynamic dyn_value; for (int i = 0; i < n; i++) { printTrace("rewind: ", i, "/", n); Event popped = events.Pop(); printTrace("popped event: " + popped); dyn_value = popped.alter.main_value; printTrace("loop dyn_value: ", StringUtils.dynamic2Str(dyn_value)); } Event new_ = peekEvent(); printTrace("latest event after rewind: " + new_); dyn_value = peekValue(); printTrace("final rewind value: " + StringUtils.dynamic2Str(dyn_value)); _traced_var.forceSetValue(dyn_value); }
/// <summary> /// Process the <see cref="Event"/> /// </summary> /// <param name="event_"> <see cref="Event"/> that should be processed</param> public override void update(Event event_) { // blocked context ? if (Context.isFrozen() && !Global.getSetting("allow tracing in frozen context")) { printTrace("Context is blocked in function tracer update call. Normal behaviour ?"); return; } // checks Debugging.assert(event_ != null); // extract events.Push(event_); printTrace("event stack size: " + events.Count); Alteration alter = event_.alter; Variable affected = alter.affected; Debugging.assert(affected != null); Debugging.assert(affected.isTraced()); affected.tracer.update(new Event(new Alteration(traced_func, affected, event_.alter.main_value, event_.alter.minor_values))); //! just push event_ ! printTrace("updated all in function tracer update"); }
/// <summary> /// Takes an arithmetical or logical expression and returns the corresponding variable /// <para/>Examples: /// <para/>* "5 + 6" : returns Integer (11) /// <para/>* "$l[5 * (1 - $i)]" : returns the elements at index 5*(1-i) in the list "l" /// <para/>* "$l" : returns the list variable l /// </summary> /// <param name="expr_string"> expression to parse</param> /// <returns> Variable object containing the value of the evaluated expression value (at time t)</returns> public static Variable parse(string expr_string) { /* Order of operations: * checking expression string integrity * raw dynamic list * clean redundant symbols * raw integer value * raw boolean value (not done yet) * raw float value (not done yet) * mathematical or logical operation * function call * variable access (e.g. $name or in list by index) */ // clean expression expr_string = StringUtils.normalizeWhiteSpaces(expr_string); Exception invalid_expr_exception = new AquilaExceptions.SyntaxExceptions.SyntaxError($"The sentence \"{expr_string}\" is not understood"); Debugging.print("input expression: " + expr_string); // matching parentheses & brackets Debugging.assert(StringUtils.checkMatchingDelimiters(expr_string, '(', ')'), new AquilaExceptions.SyntaxExceptions.UnclosedTagError("Unclosed parenthesis")); Debugging.assert(StringUtils.checkMatchingDelimiters(expr_string, '[', ']'), new AquilaExceptions.SyntaxExceptions.UnclosedTagError("Unclosed bracket")); expr_string = StringUtils.removeRedundantMatchingDelimiters(expr_string, '(', ')'); Debugging.print("dynamic list ?"); // dynamic list { DynamicList list = StringUtils.parseListExpression(expr_string); if (list != null) { return(list); } } // now that lists are over, check for redundant brackets expr_string = StringUtils.removeRedundantMatchingDelimiters(expr_string, '[', ']'); if (expr_string == null) { throw new AquilaExceptions.SyntaxExceptions.SyntaxError("Null Expression"); } Debugging.assert(expr_string != ""); //! NullValue here, instead of Exception Debugging.print("int ?"); // try evaluating expression as an integer if (int.TryParse(expr_string, out int int_value)) { return(new Integer(int_value, true)); } Debugging.print("bool ?"); // try evaluating expression as a boolean if (expr_string == "true") { return(new BooleanVar(true, true)); } if (expr_string == "false") { return(new BooleanVar(false, true)); } Debugging.print("float ?"); // try evaluating expression as float if (!expr_string.Contains(' ')) { if (float.TryParse(expr_string, out float float_value)) { Debugging.print("french/classic float"); return(new FloatVar(float_value, true)); } if (float.TryParse(expr_string.Replace('.', ','), out float_value)) { Debugging.print("normalized float"); return(new FloatVar(float_value, true)); } if (expr_string.EndsWith("f") && float.TryParse(expr_string.Substring(0, expr_string.Length - 1), out float_value)) { Debugging.print("f-float"); return(new FloatVar(float_value, true)); } if (expr_string.EndsWith("f") && float.TryParse(expr_string.Replace('.', ',').Substring(0, expr_string.Length - 1), out float_value)) { Debugging.print("f-float"); return(new FloatVar(float_value, true)); } } Debugging.print("checking for negative expression"); // special step: check for -(expr) if (expr_string.StartsWith("-")) { Debugging.print("evaluating expression without \"-\" sign"); string opposite_sign_expr = expr_string.Substring(1); // take away the "-" Variable opposite_sign_var = parse(opposite_sign_expr); Debugging.print("evaluated expression without the \"-\" symbol is of type ", opposite_sign_var.getTypeString(), " and value ", opposite_sign_var.getValue()); // ReSharper disable once ConvertIfStatementToSwitchExpression if (opposite_sign_var is Integer) { return(new Integer(-opposite_sign_var.getValue())); } if (opposite_sign_var is FloatVar) { return(new FloatVar(-opposite_sign_var.getValue())); } throw new AquilaExceptions.InvalidTypeError($"Cannot cast \"-\" on a {opposite_sign_var.getTypeString()} variable"); } Debugging.print("AL operations ?"); // mathematical and logical operations foreach (char op in Global.al_operations) { // ReSharper disable once PossibleNullReferenceException if (expr_string.Contains(op.ToString())) { string simplified = StringUtils.simplifyExpr(expr_string, new [] { op }); // only look for specific delimiter // more than one simplified expression ? if (simplified.Split(op).Length > 1) { Debugging.print("operation ", expr_string, " and op: ", op); List <string> splitted_str = StringUtils.splitStringKeepingStructureIntegrity(expr_string, op, Global.base_delimiters); // custom: logic operations laziness here (tmp) //! Variable variable = parse(splitted_str[0]); if (Global.getSetting("lazy logic") && variable is BooleanVar) { Debugging.print("lazy logic evaluation"); bool first = (variable as BooleanVar).getValue(); switch (op) { case '|': // when first: if (first) { return(new BooleanVar(true, true)); } break; case '&': // when !first: if (!first) { return(new BooleanVar(false, true)); } break; } } var splitted_var = new List <Variable> { variable }; splitted_var.AddRange(splitted_str.GetRange(1, splitted_str.Count - 1).Select(parse)); // reduce the list to a list of one element // e.g. expr1 + expr2 + expr3 => final_expr while (splitted_var.Count > 1) { // merge the two first expressions Variable expr1_var = splitted_var[0]; Variable expr2_var = splitted_var[1]; Variable result = applyOperator(expr1_var, expr2_var, op); // merge result of 0 and 1 splitted_var[0] = result; // remove 1 (part of it found in 0 now) splitted_var.RemoveAt(1); } return(splitted_var[0]); } } } Debugging.print("not (!) operator ?"); // '!' operator (only one to take one variable) if (expr_string.StartsWith("!")) { Debugging.assert(expr_string[1] == '('); Debugging.assert(expr_string[expr_string.Length - 1] == ')'); Variable expr = parse(expr_string.Substring(2, expr_string.Length - 3)); Debugging.assert(expr is BooleanVar); Debugging.print("base val b4 not operator is ", expr.getValue()); return(((BooleanVar)expr).not()); } Debugging.print("value function call ?"); // value function call if (expr_string.Contains("(")) { string function_name = expr_string.Split('(')[0]; // extract function name int func_call_length = function_name.Length; function_name = StringUtils.normalizeWhiteSpaces(function_name); Debugging.print("function name: ", function_name); Functions.assertFunctionExists(function_name); expr_string = expr_string.Substring(func_call_length); // remove function name expr_string = expr_string.Substring(1, expr_string.Length - 2); // remove parenthesis Debugging.print("expr_string for function call ", expr_string); var arg_list = new List <Expression>(); foreach (string arg_string in StringUtils.splitStringKeepingStructureIntegrity(expr_string, ',', Global.base_delimiters)) { string purged_arg_string = StringUtils.normalizeWhiteSpaces(arg_string); Expression arg_expr = new Expression(purged_arg_string); arg_list.Add(arg_expr); } if (arg_list.Count == 1 && arg_list[0].expr == "") { arg_list = new List <Expression>(); } Debugging.print("creating value function call with ", arg_list.Count, " parameters"); FunctionCall func_call = new FunctionCall(function_name, arg_list); return(func_call.callFunction()); } // function call without parenthesis -> no parameters either if (!expr_string.StartsWith(StringConstants.Other.VARIABLE_PREFIX) && !expr_string.Contains(' ')) { Debugging.print($"Call the function \"{expr_string}\" with no parameters"); var func_call = new FunctionCall(expr_string, new List <Expression>()); return(func_call.callFunction()); } Debugging.print("variable ?"); // variable access // since it is the last possibility for the parse call to return something, assert it is a variable Debugging.assert(expr_string.StartsWith(StringConstants.Other.VARIABLE_PREFIX), invalid_expr_exception); Debugging.print("list access ?"); // ReSharper disable once PossibleNullReferenceException if (expr_string.Contains("[")) { // brackets Debugging.assert(expr_string.EndsWith("]"), invalid_expr_exception); // cannot be "$l[0] + 5" bc AL_operations have already been processed int bracket_start_index = expr_string.IndexOf('['); Debugging.assert(bracket_start_index > 1, invalid_expr_exception); // "$[$i - 4]" is not valid // variable Expression var_name_expr = new Expression(expr_string.Substring(0, bracket_start_index)); Debugging.print("list name: " + var_name_expr.expr); // index list IEnumerable <string> index_list = StringUtils.getBracketsContent(expr_string.Substring(bracket_start_index)); string index_list_expr_string = index_list.Aggregate("", (current, s) => current + s + ", "); index_list_expr_string = "[" + index_list_expr_string.Substring(0, index_list_expr_string.Length - 2) + "]"; var index_list_expr = new Expression(index_list_expr_string); Debugging.print("index: " + index_list_expr.expr); // create a value function call (list_at call) object[] args = { var_name_expr, index_list_expr }; return(Functions.callFunctionByName("list_at", args)); } // only variable name, no brackets Debugging.print("var by name: ", expr_string); return(variableFromName(expr_string)); }
public Integer length() => assigned ? new Integer(_list.Count) : throw new AquilaExceptions.InvalidUsageException("Unassigned variable"); // AssignmentError public void validateIndex(Integer index) { assertAssignment(); Debugging.assert(index.getValue() < _list.Count); // InvalidIndexException }
/// <summary> /// Get the value at the index in a list /// </summary> /// <param name="list_expr"> The list</param> /// <param name="index_list_expr"> The index</param> /// <returns></returns> private static Variable listAtFunction(Expression list_expr, Expression index_list_expr) { // extract list Variable list_var = list_expr.evaluate(); Debugging.assert(list_var is DynamicList); // TypeError DynamicList list = list_var as DynamicList; // extract index Variable index_list_var = index_list_expr.evaluate(); Debugging.assert(index_list_var is DynamicList); // TypeError DynamicList index_list = index_list_var as DynamicList; /*// test run ? * if (Global.getSetting("test mode")) * { * // function to add to index dict * void addToIndexList(string var_name) * { * if (var_name == "") return; * var index_var_name_list = (List<string>) Global.test_values["index values"]; * // variable name already in index dict ? * if (index_var_name_list.Contains(var_name)) return; * // add to index dict * index_var_name_list.Add(var_name); * } * }*/ // access at index Variable result = list.atIndexList(index_list); // trace list if (!list_var.isTraced()) { return(result); } Tracer.printTrace("list_at last event: " + list_var.tracer.peekEvent()); handleValueFunctionTracing("list_at", list, new dynamic[] { index_list.getValue() }); // trace index //Tracer.printTrace("tmp list at ", index_list.toString()); /*var index_list_var_value = index_list_var.getValue(); * for (int i = 0; i < index_list_var_value.Count; i++) * { * Variable index_var = index_list_var_value[i]; * var numeric_index = (NumericalValue) index_var; * NumericalValue source = numeric_index.getTracedSource(); * if (source == null) * { * Tracer.printTrace("tmp index without source var"); * numeric_index.setName($"comp /{i}/"); * list.tracer.update(new Event(new Alteration("tmp_list_at", numeric_index, * numeric_index.getValue(), new[] {list.getName(), index_list.getValue()}, * mode:"index"))); * continue; * } * * Tracer.printTrace("Traced source var: " + source.getName()); * numeric_index.setName($"comp /{i}/"); * list.tracer.update(new Event(new Alteration("tmp_list_at", numeric_index, * numeric_index.getValue(), new[] {list.getName(), index_list.getValue()}, * mode:"index"))); * }*/ return(result); }
/// <summary> /// Does the function exists in the <see cref="functions_dict"/> or in the <see cref="user_functions"/> dict ? /// </summary> /// <param name="function_name"> Boolean value describing the function's existence</param> public static void assertFunctionExists(string function_name) => Debugging.assert(functionExists(function_name), new AquilaExceptions.FunctionNameError($"The function \"{function_name}\" does not exist"));
/// <summary> /// This function should be called at the end of every <see cref="Instruction.execute"/> call that is susceptible of /// changing the value of a <see cref="Variable"/>. It works in 3 steps: /// <para/>* Checks for any new assigned <see cref="Variable"/>s in the <see cref="Global._variable_stack"/> (if found: adds to the <see cref="Global.usable_variables"/>) /// <para/>* Update/Process all the awaiting <see cref="Event"/>s (<seealso cref="update"/>) /// <para/>* Check for any new <see cref="Event"/> which has not been processed by the <see cref="Global.tracer_update_handler_function"/> /// </summary> /// <param name="add_call_info"> stands for "additional calling information". If it has any value, it will be printed by the <see cref="Debugging.print"/> function for debugging</param> public static void updateTracers(string add_call_info = "") { if (Global.getSetting("test mode")) { return; } printTrace("updating tracers"); // printTrace alter info about the call if needed if (add_call_info != "") { printTrace(add_call_info); } // check for new usable variables foreach (var pair in Global.getCurrentDict().Where(pair => !Global.usable_variables.Contains(pair.Key))) { printTrace("checking potential var ", pair.Value is NullVar ? StringConstants.Types.NULL_TYPE : pair.Value.getName()); if (pair.Value is NullVar || !pair.Value.assigned) { continue; } // new usable variable ! printTrace("var ", pair.Value.getName(), " is usable (non-null & assigned)"); Global.usable_variables.Add(pair.Key); } // checking tracers checkAllAwaitingEvents(); // check tracer event stacks printTrace("checking variable tracers event stack counts"); foreach (VarTracer tracer in Global.var_tracers) { // unread tracers updates while (tracer.last_stack_count != tracer.getStackCount()) { printTrace("stack count changed for ", tracer.getVar().getName(), " from ", tracer.last_stack_count, " to ", tracer.getStackCount()); //Global.stdoutWriteLine("call graphical function " + StringUtils.varList2String(tracer.getVar().getRawValue()) + " & call event: " + tracer.peekEvent().ToString()); // traced functions have already been processed. checking awaiting stacks int diff = tracer.getStackCount() - tracer.last_stack_count; Debugging.assert(diff > 0); // will run forever tracer.last_stack_count++; //! here } // awaiting tracer stacks while (tracer._awaiting_events.Count != 0) { printTrace("awaiting stacks: ", tracer._awaiting_events.Count); Alteration alter = tracer._awaiting_events.Peek().alter; callUpdateHandler(alter); tracer.update(tracer._awaiting_events.Pop()); } } // update data_tree printTrace("Updating Global.data_tree"); // ReSharper disable once InvertIf if (Global.getSetting("update data tree")) { Debugging.assert(Global.data_tree != null, new AquilaExceptions.RuntimeError("Tried to update data_tree, but it is null")); Global.data_tree = Global.data_tree.update(); } }
public override void execute() { setContext(); // get variable value Variable variable_value = _var_expr.evaluate(); // is the value assigned ? (only relevant if other variable) variable_value.assertAssignment(); Variable variable = Variable.fromRawValue(variable_value.getRawValue()); // keep track of source vars -> should do something generic for lots of attributes ... if (variable is NumericalValue) { ((NumericalValue)variable).source_vars = new Dictionary <string, NumericalValue>(((NumericalValue)variable_value).source_vars); } variable.setName(_var_name); // explicit typing if (_var_type != StringConstants.Types.AUTO_TYPE) { Debugging.print("checking variable explicit type"); Expression default_value = Global.default_values_by_var_type[_var_type]; Debugging.assert(variable_value.hasSameParent(default_value.evaluate())); // TypeException } // constant if (_constant) { if (variable_value.isConst()) { variable.setConst(); } else { throw new AquilaExceptions.InvalidVariableClassifierException( "The \"const\" cannot be used when assigning to a non-const value"); } } // actually declare it to its value if (_global) { Global.addGlobalVariable(_var_name, variable); } else { Global.getCurrentDict()[_var_name] = variable; // overwriting is mandatory Debugging.print("variable exists ", Global.variableExistsInCurrentScope(_var_name)); if (_assignment) { variable.assign(); // should not need this, but doing anyway } else { variable.assigned = false; } } Debugging.print("finished declaration with value assignment: ", variable.assigned); // automatic tracing ? if (_assignment && Global.getSetting("auto trace")) { Debugging.print("Tracing variable: \"auto trace\" setting set to true"); // Does NOT work by simply doing "variable.startTracing()", and idk why Tracing tracing_instr = new RawInstruction($"trace ${_var_name}", line_index).toInstr() as Tracing; //Tracing tracing_instr = new Tracing(line_index, new List<Expression>{_var_expr}); // <- does not work either :( tracing_instr.execute(); } // update things 'n stuff Global.onElementaryInstruction(); // reset Context Context.reset(); }
/// <summary> /// Applies an arithmetical or logical operation on two <see cref="Variable"/>s /// <para/>result = (variable1) op (variable2) /// </summary> /// <param name="v1"> var 1</param> /// <param name="v2"> var 2</param> /// <param name="op"> operator (e.g. '+', '-', '&')</param> /// <returns> result <see cref="Variable"/></returns> /// <exception cref="AquilaExceptions.InvalidTypeError"> Invalid type with this operator</exception> /// <exception cref="AquilaExceptions.SyntaxExceptions.SyntaxError"> Unknown operator char</exception> private static Variable applyOperator(Variable v1, Variable v2, char op) { int comparison; Debugging.print("applyOperator: ", v1.ToString(), " ", op, " ", v2.ToString(), " (", v1.getTypeString(), " ", op, " ", v2.getTypeString(), ")"); // Debugging.assert(v1.hasSameParent(v2)); // operations between same classes/subclasses if (!v1.hasSameParent(v2)) { if (v2.isConst()) { if (v2 is Integer) { Debugging.print("Converting int to float because of const status: ", v1.ToString()); Debugging.assert(v1 is FloatVar, new AquilaExceptions.InvalidTypeError($"The type \"{v1.getTypeString()}\" was not expected. \"{v1.getTypeString()}\" expected")); // if this is not a float, operation is not permitted ! v2 = new FloatVar((float)v2.getValue()); } } else { throw new AquilaExceptions.InvalidTypeError($"The type \"{v1.getTypeString()}\" was not expected"); } } switch (op) { // arithmetic case '+': if (v1 is NumericalValue) { return(((NumericalValue)v1).addition((NumericalValue)v2)); } else { throw new AquilaExceptions.InvalidTypeError($"Invalid type \"{v1.getTypeString()}\" with operator \"{op}\""); } case '-': if (v1 is NumericalValue) { return(((NumericalValue)v1).subtraction((NumericalValue)v2)); } else { throw new AquilaExceptions.InvalidTypeError($"Invalid type \"{v1.getTypeString()}\" with operator \"{op}\""); } case '/': if (v1 is NumericalValue) { return(((NumericalValue)v1).division((NumericalValue)v2)); } else { throw new AquilaExceptions.InvalidTypeError($"Invalid type \"{v1.getTypeString()}\" with operator \"{op}\""); } case '*': if (v1 is NumericalValue) { return(((NumericalValue)v1).mult((NumericalValue)v2)); } else { throw new AquilaExceptions.InvalidTypeError($"Invalid type \"{v1.getTypeString()}\" with operator \"{op}\""); } case '%': if (v1 is Integer) { return(((Integer)v1).modulo((Integer)v2)); } else { throw new AquilaExceptions.InvalidTypeError($"Invalid type \"{v1.getTypeString()}\" with operator \"{op}\""); } // logic case '<': Debugging.assert(v1 is Integer || v1 is FloatVar); comparison = v1 is Integer ? ((Integer)v1).compare(v2 as Integer) : ((FloatVar)v1).compare(v2 as FloatVar); return(new BooleanVar(comparison == -1)); case '>': Debugging.assert(v1 is Integer || v1 is FloatVar); comparison = v1 is Integer ? ((Integer)v1).compare(v2 as Integer) : ((FloatVar)v1).compare(v2 as FloatVar); return(new BooleanVar(comparison == 1)); case '{': Debugging.assert(v1 is Integer || v1 is FloatVar); comparison = v1 is Integer ? ((Integer)v1).compare(v2 as Integer) : ((FloatVar)v1).compare(v2 as FloatVar); return(new BooleanVar(comparison != 1)); case '}': Debugging.assert(v1 is Integer || v1 is FloatVar); comparison = v1 is Integer ? ((Integer)v1).compare(v2 as Integer) : ((FloatVar)v1).compare(v2 as FloatVar); return(new BooleanVar(comparison != -1)); case '~': Debugging.assert(v1 is Integer || v1 is FloatVar); comparison = v1 is Integer ? ((Integer)v1).compare(v2 as Integer) : ((FloatVar)v1).compare(v2 as FloatVar); return(new BooleanVar(comparison == 0)); case ':': Debugging.assert(v1 is Integer || v1 is FloatVar); comparison = v1 is Integer ? ((Integer)v1).compare(v2 as Integer) : ((FloatVar)v1).compare(v2 as FloatVar); return(new BooleanVar(comparison != 0)); case '|': Debugging.assert(v1 is BooleanVar); return(((BooleanVar)v1).or((BooleanVar)v2)); case '^': Debugging.assert(v1 is BooleanVar); return(((BooleanVar)v1).xor((BooleanVar)v2)); case '&': Debugging.assert(v1 is BooleanVar); return(((BooleanVar)v1).and((BooleanVar)v2)); default: throw new AquilaExceptions.SyntaxExceptions.SyntaxError("Unknown operand " + op); } }
/// <summary> /// Transforms a <see cref="RawInstruction"/> into an <see cref="Instruction"/>. /// <para/>The order of operations is: /// <para/>* variable declaration /// <para/>* variable modification /// <para/>* for loop /// <para/>* while loop /// <para/>* if statement /// <para/>* void function call /// </summary> /// <param name="line_index"> index of the line in the purged source code</param> /// <param name="raw_instr"> a <see cref="RawInstruction"/> to convert</param> /// <returns> the corresponding <see cref="Instruction"/></returns> /// <exception cref="AquilaExceptions.SyntaxExceptions.SyntaxError"> Invalid syntax</exception> private static Instruction rawInstr2Instr(int line_index, RawInstruction raw_instr) { /* Order of operations: * tracing * declaration * variable assignment * function definition * for loop * while loop * if statement * void function call */ Parser.print($"from raw instr to instr: \"{raw_instr._instr}\" at line {line_index}"); // split instruction List <string> instr = StringUtils.splitStringKeepingStructureIntegrity(raw_instr._instr, ' ', Global.base_delimiters); Parser.print("trace ?"); // variable tracing if (instr[0] == StringConstants.Keywords.TRACE_KEYWORD) { if (Global.getSetting("auto trace")) { Parser.print("\"trace\" instruction, but \"auto trace\" is set to true ?"); } List <Expression> traced_vars = new List <Expression>(); for (int i = 1; i < instr.Count; i++) { traced_vars.Add(new Expression(instr[i])); } return(new Tracing(line_index, traced_vars)); } Parser.print("decl ?"); // variable declaration if (instr.Contains(StringConstants.Keywords.DECLARATION_KEYWORD)) { // declaration modes bool safe_mode = false, overwrite = false, constant = false, global = false; while (instr[0] != StringConstants.Keywords.DECLARATION_KEYWORD) { switch (instr[0]) { case StringConstants.Keywords.SAFE_DECLARATION_KEYWORD: safe_mode = true; Parser.print("safe mode !"); break; case StringConstants.Keywords.OVERWRITE_DECLARATION_KEYWORD: overwrite = true; Parser.print("overwrite !"); break; case StringConstants.Keywords.CONST_DECLARATION_KEYWORD: constant = true; Parser.print("const !"); break; case StringConstants.Keywords.GLOBAL_DECLARATION_KEYWORD: global = true; Parser.print("global !"); break; default: throw new AquilaExceptions.UnknownKeywordError($"Unknown keyword in declaration: \"{instr[0]}\""); } instr.RemoveAt(0); } // remove the "decl" instr.RemoveAt(0); // define the type string type = StringConstants.Types.AUTO_TYPE; if (Global.type_list.Contains(instr[0])) { type = instr[0]; instr.RemoveAt(0); Debugging.assert(type != StringConstants.Types.NULL_TYPE, new AquilaExceptions.InvalidTypeError($"Cannot declare a variable with type: \"{StringConstants.Types.NULL_TYPE}\"")); } Parser.print("instr: ", instr); Expression default_value; bool valid_default_value; if (type != StringConstants.Types.AUTO_TYPE) { default_value = Global.default_values_by_var_type[type]; valid_default_value = true; } else { default_value = null; valid_default_value = false; } var variable_declaration_buffer = new List <string>(); var declarations = new List <Instruction>(); foreach (string s in instr) { Parser.print("Doing ", s, " with buffer ", StringUtils.dynamic2Str(variable_declaration_buffer)); // buffer should be "$var" "=" Parser.print(variable_declaration_buffer.Count); if (variable_declaration_buffer.Count == 2) { Parser.print("adding with assignment", StringUtils.dynamic2Str(variable_declaration_buffer)); Debugging.assert(variable_declaration_buffer[1] == "=", new AquilaExceptions.SyntaxExceptions.SyntaxError($"Invalid declaration syntax at token: {s}")); declarations.Add(new Declaration(line_index, variable_declaration_buffer[0].Substring(1), new Expression(s), type, true, safe_mode, overwrite, constant, global)); variable_declaration_buffer.Clear(); continue; } if (s.StartsWith(StringConstants.Other.VARIABLE_PREFIX)) { if (variable_declaration_buffer.Any()) { Debugging.assert(valid_default_value, new AquilaExceptions.InvalidTypeError($"Invalid variable type without assignment {StringConstants.Types.AUTO_TYPE}")); // add the (empty) declaration Parser.print("adding empty ", StringUtils.dynamic2Str(variable_declaration_buffer)); declarations.Add(new Declaration(line_index, variable_declaration_buffer[0].Substring(1), default_value, type, false, safe_mode, overwrite, constant, global)); // clear the buffer & restart variable_declaration_buffer.Clear(); variable_declaration_buffer.Add(s); } else { // add variable to the buffer (start of buffer) variable_declaration_buffer.Add(s); Debugging.assert(variable_declaration_buffer.Count < 3, new AquilaExceptions.SyntaxExceptions.SyntaxError($"Invalid declaration syntax (buffer count > 3): {s}")); } continue; } // must be "=" Debugging.assert(s == "=", new AquilaExceptions.SyntaxExceptions.SyntaxError($"Cannot declare a value: {s}")); variable_declaration_buffer.Add(s); } // trailing variable ? if (variable_declaration_buffer.Any()) { Parser.print("Trailing var decl buffer: ", variable_declaration_buffer); Debugging.assert(variable_declaration_buffer.Count == 1, new AquilaExceptions.SyntaxExceptions.SyntaxError($"Invalid trailing declarations: {StringUtils.dynamic2Str(variable_declaration_buffer)}")); declarations.Add(new Declaration(line_index, variable_declaration_buffer[0].Substring(1), default_value, type, false, safe_mode, overwrite, constant, global)); } return(new MultiInstruction(declarations.ToArray())); } Parser.print("assignment ?"); // variable assignment if (instr.Count > 1 && instr[1].EndsWith("=") && (instr[0][0] == '$' || instr[0].Contains("("))) { Debugging.assert(instr.Count > 2); // syntax ?unfinished line? string var_designation = instr[0]; string equal_sign = instr[1]; instr.RemoveAt(0); // remove "$name" instr.RemoveAt(0); // remove "{op?}=" // reunite all on the right side of the "=" sign string assignment_string = StringUtils.reuniteBySymbol(instr); // custom operator in assignment if (equal_sign.Length != 1) { Parser.print("Custom operator detected: ", equal_sign); Debugging.assert(equal_sign.Length == 2); assignment_string = $"{var_designation} {equal_sign[0]} ({assignment_string})"; } // get the Expresion Expression assignment = new Expression(assignment_string); return(new Assignment(line_index, var_designation, assignment)); } // increment || decrement if (instr.Count == 2 && (instr[1] == "++" || instr[1] == "--")) { Parser.print("Increment or Decrement detected"); return(new Assignment(line_index, instr[0], new Expression($"{instr[0]} {instr[1][0]} 1"))); } Parser.print("function definition ?"); if (instr[0] == StringConstants.Keywords.FUNCTION_KEYWORD) { Debugging.assert(raw_instr._is_nested); // syntax??? Debugging.assert(instr.Count == 3 || instr.Count == 4); // "function" "type" ("keyword"?) "name(args)" Function func = Functions.readFunction(raw_instr._instr, raw_instr._sub_instr_list); return(new FunctionDef(line_index, func)); } Parser.print("for loop ?"); // for loop if (instr[0] == StringConstants.Keywords.FOR_KEYWORD) { Debugging.assert(raw_instr._is_nested); // syntax??? Debugging.assert(instr[1].StartsWith("(") && instr[1].EndsWith(")")); // syntax List <string> sub_instr = StringUtils.splitStringKeepingStructureIntegrity(instr[1].Substring(1, instr[1].Length - 2), ';', Global.base_delimiters); sub_instr = StringUtils.normalizeWhiteSpacesInStrings(sub_instr); Parser.print(sub_instr); Debugging.assert(sub_instr.Count == 3); // syntax // start Instruction start = new RawInstruction(sub_instr[0], raw_instr._line_index).toInstr(); // stop Expression condition = new Expression(sub_instr[1]); // step Instruction step = new RawInstruction(sub_instr[2], raw_instr._line_index).toInstr(); // instr List <Instruction> loop_instructions = new List <Instruction>(); int add_index = 0; foreach (RawInstruction loop_instr in raw_instr._sub_instr_list) { loop_instructions.Add(rawInstr2Instr(line_index + ++add_index, loop_instr)); } return(new ForLoop(line_index, start, condition, step, loop_instructions)); } Parser.print("while loop ?"); // while loop if (instr[0] == StringConstants.Keywords.WHILE_KEYWORD) { // syntax check Debugging.assert(instr.Count == 2); // syntax // condition expression Expression condition = new Expression(instr[1]); // instr List <Instruction> loop_instructions = new List <Instruction>(); int add_index = 0; foreach (RawInstruction loop_instr in raw_instr._sub_instr_list) { loop_instructions.Add(rawInstr2Instr(line_index + ++add_index, loop_instr)); } return(new WhileLoop(line_index, condition, loop_instructions)); } Parser.print("if statement ?"); // if statement if (instr[0] == StringConstants.Keywords.IF_KEYWORD) { // syntax check Debugging.assert(instr.Count == 2); // syntax // condition expression Expression condition = new Expression(instr[1]); // instr List <Instruction> if_instructions = new List <Instruction>(); List <Instruction> else_instructions = new List <Instruction>(); bool if_section = true; int add_index = 0; foreach (RawInstruction loop_instr in raw_instr._sub_instr_list) { add_index++; if (if_section) { if (loop_instr._instr == StringConstants.Keywords.ELSE_KEYWORD) { if_section = false; continue; } if_instructions.Add(rawInstr2Instr(line_index + add_index, loop_instr)); } else { else_instructions.Add(rawInstr2Instr(line_index + add_index, loop_instr)); } } return(new IfCondition(line_index, condition, if_instructions, else_instructions)); } Parser.print("function call ?"); // function call with spaces between function name and parenthesis if (instr.Count == 2 && instr[1].StartsWith("(")) { Parser.print("function call with space between name and first parenthesis. Merging ", instr[0], " and ", instr[1]); instr[0] += instr[1]; instr.RemoveAt(1); } // void function call (no return value, or return value not used) if (instr[0].Contains('(')) { // syntax checks Debugging.assert(instr.Count == 1); // syntax Debugging.assert(instr[0][instr[0].Length - 1] == ')'); // syntax // function name string function_name = instr[0].Split('(')[0]; // extract function name if (Global.getSetting("check function existence before runtime")) { Functions.assertFunctionExists(function_name); // assert function exists } Parser.print("expr_string for function call ", instr[0]); // extract args string exprs = instr[0].Substring(function_name.Length + 1); // + 1 : '(' exprs = exprs.Substring(0, exprs.Length - 1); // ')' List <string> arg_expr_str = StringUtils.splitStringKeepingStructureIntegrity(exprs, ',', Global.base_delimiters); // no args ? if (arg_expr_str.Count == 1 && StringUtils.normalizeWhiteSpaces(arg_expr_str[0]) == "") { return(new VoidFunctionCall(line_index, function_name)); } // ReSharper disable once SuggestVarOrType_Elsewhere object[] args = arg_expr_str.Select(x => (object)new Expression(x)).ToArray(); return(new VoidFunctionCall(line_index, function_name, args)); } // try using this as a function ? if (instr.Count == 1 && Functions.functionExists(instr[0])) { Parser.print($"Call the function \"{instr[0]}\" with no parameters"); return(new VoidFunctionCall(line_index, instr[0])); } Parser.print("unrecognized line: \"", raw_instr._instr, "\""); throw new AquilaExceptions.SyntaxExceptions.SyntaxError($"Unknown syntax \"{raw_instr._instr}\""); }