/// <summary> /// Take source code and generates the corresponding <see cref="Algorithm"/> /// </summary> /// <param name="path"> lib_path to your source code</param> /// <param name="print_src"> printTrace the generated purged code</param> /// <param name="pretty_print"> pretty_print the <see cref="RawInstruction"/>s (useful to check nested-instruction priorities)</param> /// <param name="default_name"> give a name to your <see cref="Algorithm"/></param> /// <returns> the generated <see cref="Algorithm"/></returns> public static Algorithm algorithmFromSrcCode(string path, bool print_src = false, bool pretty_print = false, string default_name = "no-name-given") { // read code Context.setStatus(Context.StatusEnum.read_purge); Context.setInfo(path); Dictionary <int, string> lines = Parser.readSourceCode(path); Context.reset(); if (print_src) { StringUtils.printStringList(lines.Select(pair => pair.Value).ToList(), true); } // extract macros Context.setStatus(Context.StatusEnum.macro_preprocessing); Context.setInfo(null); List <(string, string)> macros = Parser.getMacroPreprocessorValues(lines.Select(pair => pair.Value).ToList()); foreach (var pair in macros) { Parser.handleMacro(pair.Item1, pair.Item2); } Context.reset(); // Parse code into RawInstructions List <RawInstruction> raw_instructions = RawInstruction.code2RawInstructions(lines); Parser.print("raw_instructions done"); // Pretty-printTrace code foreach (RawInstruction instruction in raw_instructions) { if (pretty_print) { instruction.prettyPrint(); } } // Build instructions from the RawInstructions List <Instruction> instructions = RawInstruction.buildInstructions(raw_instructions); string algo_name = default_name; Algorithm algo = new Algorithm(algo_name, instructions); return(algo); }
/// <summary> /// Transform a string list into a <see cref="RawInstruction"/>. Takes into account /// nested instructions. This method creates the base algorithm structure, using /// the <see cref="_is_nested"/> and <see cref="_sub_instr_list"/> attributes. /// </summary> /// <param name="lines"> list of strings</param> /// <returns> list of RawInstructions</returns> public static List <RawInstruction> code2RawInstructions(Dictionary <int, string> lines) { Context.setStatus(Context.StatusEnum.building_raw_instructions); Context.setInfo(lines); int line_index = 0; List <RawInstruction> instructions = new List <RawInstruction> (); while (line_index < lines.Count) // using while loop bc index will be modified { string line = lines.Values.ElementAt(line_index); int real_line_index = lines.Keys.ElementAt(line_index); Parser.print("doing line ", line); if (line.StartsWith("#")) // macro preprocessor line { line_index++; continue; } RawInstruction instr = new RawInstruction(line, real_line_index); foreach (var pair in Global.nested_instruction_flags.Where(flag => line.StartsWith(flag.Key + " "))) { Parser.print("FOUND " + pair.Key); instr._is_nested = true; int end_index = StringUtils.findCorrespondingElementIndex(lines.Select(pair_ => pair_.Value).ToList(), line_index + 1, pair.Key, pair.Value); Dictionary <int, string> sub_lines = new Dictionary <int, string>(); List <int> picked = lines.Select(pair_ => pair_.Key).ToList().GetRange(line_index + 1, end_index - line_index - 1); foreach (int i in picked) { sub_lines.Add(i, lines[i]); } instr._sub_instr_list = code2RawInstructions(sub_lines); Parser.print(line_index, " - ", end_index); line_index = end_index; } instructions.Add(instr); line_index++; } Context.reset(); return(instructions); }
/// <summary> /// Execute the input lines /// </summary> /// <param name="lines"> input lines</param> /// <param name="clear_lines"> clear the input lines at the end of execution</param> private static void executeLines(List <string> lines, bool clear_lines = true) { // execute line here try { int temp_i = 0; List <RawInstruction> raw_instructions = RawInstruction.code2RawInstructions(lines.ToDictionary(_ => temp_i++, x => x)); List <Instruction> instructions = RawInstruction.buildInstructions(raw_instructions); foreach (Instruction instr in instructions) { instr.execute(); } } catch (Exception e) { Global.stdoutWriteLine(e); } if (clear_lines) { lines.Clear(); } }
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> /// 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}\""); }