/// <summary> /// Raised by the "return()" function in Aquila /// </summary> /// <param name="expr_as_msg"> The expression string which should be returned by the <see cref="Algorithm"/> or <see cref="Function"/></param> public ReturnValueException(string expr_as_msg) { Debugging.print("Resetting Context in Return Exception constructor. Context: " + (Context.StatusEnum)Context.getStatus()); Context.reset(); _return_expr = expr_as_msg; }
public Variable callFunction() { Debugging.print("calling \"" + _function_name + "\" as value"); // manually set context Context.setStatus(Context.StatusEnum.predefined_function_call); Context.setInfo(this); // for rec functions: bool frozen_at_start = Context.isFrozen(); _called = true; // from list to array of objects // ReSharper disable once SuggestVarOrType_Elsewhere object[] args = _arg_expr_list.Select(x => (object)x).ToArray(); // call by name Variable result = Functions.callFunctionByName(_function_name, args); // have to trace manually here if (isTraced()) { Tracer.printTrace("Tracing manually value function"); tracer.update(new Event(new Alteration(_function_name, this, result, args))); } // reset Context if (!frozen_at_start) { Context.reset(); } return(result); }
public override void execute() { setContext(); int context_integrity_check = Context.getStatusStackCount(); Debugging.print("Assignment: ", _var_name, " = ", _var_value.expr); // set the new value Variable variable; try { variable = Expression.parse(_var_name); } catch (AquilaExceptions.NameError) { // implicit declaration if (!Global.getSetting("implicit declaration in assignment")) { throw; } Debugging.print("Implicit declaration in Assignment!"); Declaration decl = new Declaration(line_index, _var_name.Substring(1), _var_value); // in the Assignment constructor: already check if _var_name != "" decl.execute(); // update things 'n stuff Global.onElementaryInstruction(); // reset Context // Smooth Context Context.resetUntilCountReached(context_integrity_check); Context.reset(); return; } // parsing new value Variable val = _var_value.evaluate(); Debugging.print("assigning " + _var_name + " with expr " + _var_value.expr + " with value " + val + " (2nd value assigned: " + val.assigned + ") and type: " + val.getTypeString()); // assert the new is not an unassigned (only declared) variable val.assertAssignment(); if (variable.hasSameParent(val)) { variable.setValue(val); } else { throw new AquilaExceptions.InvalidTypeError("You cannot change the type of your variables (" + variable.getTypeString() + " -> " + val.getTypeString() + "). This will never be supported because it would be considered bad style."); } // update things 'n stuff Global.onElementaryInstruction(); // reset Context // Smooth Context Context.resetUntilCountReached(context_integrity_check); Context.reset(); }
/// <summary> /// Call a function from the <see cref="functions_dict"/> or <see cref="functions_dict"/> dictionary. /// This can be a default function or a custom function /// </summary> /// <param name="name"> The function name (key)</param> /// <param name="args"> The arguments for you function</param> /// <returns> Returned value from the function</returns> /// <exception cref="AquilaExceptions.FunctionNameError"> Function does not exist</exception> public static Variable callFunctionByName(string name, params object[] args) { if (functions_dict.ContainsKey(name)) { // no new context scope needed, because no function defined here would benefit from it. plus, wouldn't it break some functionalities ? idk Context.assertStatus(Context.StatusEnum.predefined_function_call); Debugging.print("invoking value function ", name, " dynamically with ", args.Length, " argument(s)"); return(functions_dict[name].DynamicInvoke(args) as Variable); } if (user_functions.ContainsKey(name)) { Debugging.print("calling user function: " + name); Dictionary <string, Variable> arg_dict = args2Dict(name, args); // user-functions: should not be frozen // bool unfreeze = Context.tryFreeze(); Global.newMainContextScope(); Variable result = user_functions[name].callFunction(arg_dict); Global.resetMainContextScope(); // if (unfreeze) Context.unfreeze(); return(result); } throw new AquilaExceptions.FunctionNameError($"Function \"{name}\" does not exist"); }
/// <summary> /// Prints a new-line-char to the stdout /// </summary> /// <returns> <see cref="NullVar"/> (equivalent of null/void)</returns> private static NullVar printEndlFunction() { Debugging.print("begin printing to console"); Global.stdoutWrite('\n'); Debugging.print("end printing to console"); return(new NullVar()); }
/// <summary> /// Prints the string of the Expression /// </summary> /// <param name="expr"> <see cref="NullVar"/> (equivalent of null/void)</param> /// <returns> <see cref="NullVar"/> (equivalent of null/void)</returns> private static NullVar printStrFunction(Expression expr) { Debugging.print("begin printing to console"); Global.stdoutWrite(expr.expr); Debugging.print("end printing to console"); return(new NullVar()); }
/// <summary> /// Prints the value of an <see cref="Expression"/> to the stdout. Does add a return '\n' symbol /// </summary> /// <param name="value"> Expression you want to print (the evaluated value)</param> /// <returns> <see cref="NullVar"/> (equivalent of null/void)</returns> private static NullVar printValEndlFunction(Expression value) { Debugging.print("begin printing to console: (" + value.expr + ")"); Global.stdoutWriteLine(value.evaluate().ToString()); Debugging.print("end printing to console"); return(new NullVar()); }
protected override void updateTestModeValues() { Global.instruction_count++; Debugging.print("instruction count incrementation"); if (!Global.getSetting("test mode")) { return; } Algorithm.testModeInstructionUpdate(); }
/// <summary> /// Run the Algorithm as a test run. <see cref="Instruction"/> by <see cref="Instruction"/>, /// until the list of instructions is exhausted and we can return the <see cref="_return_value"/>, /// using <see cref="Expression.parse"/> on it (it is an <see cref="Expression"/>) /// </summary> /// <returns> The evaluated <see cref="_return_value"/> after all the <see cref="_instructions"/> have been executed</returns> /// <exception cref="AquilaExceptions.RuntimeError"> ReturnValueException is null</exception> public Variable testRun() { initTestMode(); try { // Run start Debugging.print("Starting Algorithm test run"); setStartContext(); foreach (Instruction instr in _instructions) { try { instr.execute(); } catch (System.Reflection.TargetInvocationException out_exception) { // normal TargetInvocationException if (!(out_exception.InnerException is AquilaControlFlowExceptions.ReturnValueException)) { throw; } // casted ReturnValueException AquilaControlFlowExceptions.ReturnValueException exception = (AquilaControlFlowExceptions.ReturnValueException)out_exception.InnerException; if (exception == null) { throw new AquilaExceptions.RuntimeError("The inner ReturnValueException in the TargetInvocationException is null"); // something went wrong } _return_value = new Expression(exception.getExprStr()); Context.reset(); Debugging.print("Ended Algorithm test run with return value"); Global.setSetting("test mode", false); return(_return_value.evaluate()); } } // no resetting here. algorithm finished setEndContext(); Debugging.print("Ended Algorithm test run with no return"); Global.setSetting("test mode", false); return(new NullVar()); // NoReturnCallWarning } catch (Exception e) { Global.stdoutWriteLine(e.ToString()); Debugging.print("Ended Algorithm test run with exception"); Global.setSetting("test mode", false); return(new NullVar()); } }
public override void execute() { setContext(); foreach (Instruction instruction in _instructions) { Debugging.print("multiple instruction: ", instruction); instruction.execute(); } Context.reset(); }
/// <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> /// Add a <see cref="Function"/> to the <see cref="user_functions"/> dict. /// If the function already exists and the settings are set accordingly ("user function overwriting"), /// the function will be overwritten /// </summary> /// <param name="func"> <see cref="Function"/> object</param> public static void addUserFunction(Function func) { if (user_functions.ContainsKey(func.getName()) && Global.getSetting("user function overwriting")) { Debugging.print("overwriting already existing user defined function: " + func.getName()); user_functions[func.getName()] = func; } else { user_functions.Add(func.getName(), func); } }
/// <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> /// 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> /// Should be called BEFORE executing the <see cref="Function"/>. /// Initializes a new Main Context Stack, as well as a new Local Context Stack with the given variables /// </summary> /// <exception cref="Context.ContextException"> If not a recursive function, already executing this very function</exception> private void initialize(Dictionary <string, Variable> args) { _call_depth++; Debugging.print("calling function with depth: ", _call_depth); // Debugging.assert(Global.getSetting("flame mode") || Context.isFrozen()); if (!_rec_function && _in_function_scope) { throw new Context.ContextException("Already in function scope. Missing \"recursive\" keyword ?"); // recursive ? } // new local context scope using custom function args Global.newLocalContextScope(args); _in_function_scope = true; }
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> /// Trace the variable value <see cref="Alteration"/>. /// This is done manually in <see cref="FunctionCall"/> to prevent from infinite recursive calls /// </summary> /// <param name="info_name"> name of the method</param> /// <param name="sub_values"> sub_values are e.g. function parameters</param> /// <param name="check"> force checking for different value in ?</param> protected void trace(string info_name, dynamic[] sub_values, bool check = false) { if (!_traced) { return; } if (check) { Debugging.print("checking if any values have changed"); if (tracer.peekValue() == getValue()) { Debugging.print("values equal. no updating done"); return; } } tracer.update(new Event(new Alteration(info_name, this, getRawValue(), sub_values, trace_mode))); }
/// <summary> /// Load an Aquila library /// </summary> /// <param name="lib_path"> lib_path to the library source file</param> /// <exception cref="AquilaExceptions.LibraryLoadingFailedError"> Library load failed</exception> public static void loadLibrary(string lib_path) { Debugging.print("loading lib: " + lib_path); // generate the Algorithm Algorithm algo = algorithmFromSrcCode(lib_path, default_name: $"use-header-file \"{lib_path}\""); // try loading the library try { algo.run(); } catch (Exception e) { throw new AquilaExceptions.LibraryLoadingFailedError($"Unable to load library at \"{lib_path}\" because of: " + e.Message); } Debugging.print("finished loading lib: " + lib_path); }
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); } }
public override void execute() { setContext(); int context_integrity_check = Context.getStatusStackCount(); // the tracing instruction execution doesn't take any Tracer.updateTracers() calls foreach (Expression traced_expr in _traced_vars) { Variable traced_var = traced_expr.evaluate(); if (traced_var.isTraced()) { Debugging.print("trying to trace a variable that is already traced: " + traced_expr.expr); continue; } traced_var.startTracing(); } // Smooth Context Context.resetUntilCountReached(context_integrity_check); Context.reset(); }
/// <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> /// Enter a new variable stack layer. /// This is called when entering, for example, a function call /// </summary> public static void newMainContextScope() { Debugging.print("new main scope depth from (current): ", getMainScopeDepth()); addVariableStackLayer(); // defaults are added automatically Debugging.print("new main scope depth: " + getMainScopeDepth()); }
/// <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> /// 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)); }
_variable_stack.Peek().Remove(getCurrentDict()); // last element index /// <summary> /// Add a new local context scope variable dict. /// This is called when entering a nested instruction /// </summary> public static void newLocalContextScope(Dictionary <string, Variable> vars = null) { Debugging.print("new local scope depth from (current): ", getLocalScopeDepth()); addVariableStackElement(vars); Debugging.print("new local scope depth: " + getLocalScopeDepth()); }
public MultiInstruction(Instruction[] instructions) { _instructions = instructions; Debugging.print("num multiple instructions: ", _instructions.Length); }
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(); }