//Loads the "core.ag" standard library. public void LoadCoreLibrary() { //Check the file exists. var dirParts = DefaultDirectories.StandardLibDirectory.Concat(new string[] { "core.ag" }).ToArray(); var corePath = CPFilePath.GetPlatformFilePath(dirParts); if (!File.Exists(corePath)) { Error.FatalNoContext("Failed loading the core library from '" + corePath + "'. No core functions could be loaded, Algo cannot run. Please reinstall Algo, or redownload the 'core.ag' standard library."); return; } //Load it. RunAlgoScript(corePath); }
//When a file is imported. public override object VisitStat_import([NotNull] algoParser.Stat_importContext context) { //The following are checked for the parent library, in order: //1. Executing directory of the script + whatever referenced folder path. //2. Packages directory for Algo. //3. Standard libraries. //Make sure they're not trying to import multiple as a single identifier. if (context.literal_params().expr().Length > 1 && context.AS_SYM() != null) { Error.Warning(context, "Failed to import scripts, cannot import multiple scripts as a single identifier."); return(null); } //Import each. foreach (var item in context.literal_params().expr()) { //Getting directory tree text. string importLoc = ""; //Evaluating the statement to get dir text. AlgoValue locVal = (AlgoValue)VisitExpr(item); if (locVal.Type != AlgoValueType.String) { Error.Fatal(context, "Given file path to import was not a string."); return(null); } importLoc = (string)locVal.Value; List <string> fileParts = importLoc.Split('/').ToList(); //Append the extension to the end (imports don't require an extension). if (!fileParts[fileParts.Count - 1].EndsWith(".ag")) { fileParts[fileParts.Count - 1] = fileParts.Last() + ".ag"; } //Is the import being placed into a different scope? string importScope = ""; if (context.AS_SYM() != null) { //Yes, get the name of the scope. importScope = context.IDENTIFIER().GetText(); } //Test 1: Executing directory of the script. string[] dirParts = new string[] { Environment.CurrentDirectory }.Concat(fileParts).ToArray(); string dirToCheck = CPFilePath.GetPlatformFilePath(dirParts); //Is it there? if (File.Exists(dirToCheck)) { //Yes! Run the load function. RunAlgoScript(dirToCheck, importScope); continue; } //Nope. //Test 2: Packages directory for Algo. dirParts = DefaultDirectories.PackagesDirectory.Concat(fileParts).ToArray(); dirToCheck = CPFilePath.GetPlatformFilePath(dirParts); //Is it there? if (File.Exists(dirToCheck)) { //Yep, load it. RunAlgoScript(dirToCheck, importScope); continue; } //Nope. //Test 3: Standard libraries. dirParts = DefaultDirectories.StandardLibDirectory.Concat(fileParts).ToArray(); dirToCheck = CPFilePath.GetPlatformFilePath(dirParts); //Is it there? if (File.Exists(dirToCheck)) { //Yep, load it. RunAlgoScript(dirToCheck, importScope); continue; } //No, nowhere else to check from, so throw a linking warning. Error.Warning(context, "Failed to link the Algo script '" + importLoc + "'. It has not been loaded."); } return(null); }
/// <summary> /// Runs Algo in "inline" mode (no package manager, etc). Returns an exit code. /// </summary> private static int RunAlgoInline(InlineCLIOptions opts, string[] originalArgs) { //Set developer mode on if necessary. if (opts.DeveloperMode) { AlgoRuntimeInformation.DeveloperMode = true; } if (opts.TestMode) { AlgoRuntimeInformation.UnitTestMode = true; } //Displaying any generic info and then shutting off? if (opts.ShowVersionOnly) { PrintVersionInfo(); return(0); } //Compiling a file? if (opts.Compile != null) { //Attempt to compile. ALEC.Compile(opts.Compile); return(0); } //Running the script interpreter, or the live interpreter? if (opts.ScriptFile == null) { //Run live. //Print version info. If --nohead is on, then the header info for the interpreter is skipped. if (!opts.NoHeader) { PrintVersionInfo(); Console.WriteLine("Starting interpreter...\n"); } //Create a visitor. if (visitor == null) { visitor = new algoVisitor(); //Load core library. visitor.LoadCoreLibrary(); } //Interactive interpreter. while (true) { Console.Write(">> "); string line = Console.ReadLine(); //Catch keywords and null strings. if (line == "quit" || line == "exit" || line == "stop") { break; } if (line == "help") { PrintHelp(); continue; } if (line == "clear") { Console.Clear(); continue; } if (line == "") { continue; } //Parse line. var s_chars = new AntlrInputStream(line); var s_lexer = new algoLexer(s_chars); var s_tokens = new CommonTokenStream(s_lexer); var s_parse = new algoParser(s_tokens); //Turn on continuous mode. AlgoRuntimeInformation.ContinuousMode = true; //Execute. s_parse.BuildParseTree = true; var s_tree = s_parse.compileUnit(); try { visitor.VisitCompileUnit(s_tree); } catch (Exception e) { //Internal exception. if (!AlgoRuntimeInformation.UnitTestMode) { Error.Internal(e.Message); } else { throw e; } } } return(0); } else { //Run normal script. //Does the given file location exist? string fullPath = CPFilePath.GetPlatformFilePath(new string[] { Environment.CurrentDirectory, opts.ScriptFile }); if (!File.Exists(fullPath)) { Error.FatalNoContext("No file with the name '" + opts.ScriptFile + "' exists relative to your current directory."); return(-1); } //Loading in the file arguments. List <string> args = originalArgs.ToList(); args.RemoveAll(x => x.StartsWith("-")); algoVisitor.SetConsoleArguments(args.Skip(1).ToArray()); //Read in the input. AlgoRuntimeInformation.FileLoaded = opts.ScriptFile; string input = File.ReadAllText(fullPath); var chars = new AntlrInputStream(input); var lexer = new algoLexer(chars); var tokens = new CommonTokenStream(lexer); //Debug print. if (AlgoRuntimeInformation.DeveloperMode) { ANTLRDebug.PrintTokens(lexer); } //Debug print tree. var parser = new algoParser(tokens); parser.BuildParseTree = true; var tree = parser.compileUnit(); if (AlgoRuntimeInformation.DeveloperMode) { ANTLRDebug.PrintParseList(tree, parser); //Add a gap. Console.WriteLine(" --------------------\n | BEGIN EVALUATION |\n --------------------\n"); } //Walking the tree. visitor = new algoVisitor(); visitor.LoadCoreLibrary(); visitor.VisitCompileUnit(tree); if (AlgoRuntimeInformation.DeveloperMode) { Console.ForegroundColor = ConsoleColor.Gray; Console.WriteLine("\n ------------------\n | END EVALUATION |\n ------------------\n"); //Print variables. ANTLRDebug.PrintScopes(); } return(0); } }
//On construct, add all the standard libraries as loaded function plugins. //Also, load in all plugins in the "/packages/" directory (and all subdirectories). public AlgoFunctionPlugins() { //Add standard libraries. //string.* Plugins.Add(new StandardLibrary.AlgoStd_String()); //*.* (core) Plugins.Add(new StandardLibrary.AlgoStd_Core()); //input.* and output.* (io) Plugins.Add(new StandardLibrary.AlgoStd_IO()); //web.* Plugins.Add(new StandardLibrary.AlgoStd_Web()); //json.* Plugins.Add(new StandardLibrary.AlgoStd_JSON()); //*.* (reflection) Plugins.Add(new StandardLibrary.AlgoStd_Reflection()); //random.* (maths) Plugins.Add(new StandardLibrary.AlgoStd_Maths()); //Are there any plugin files in the /packages/ directory? string[] files = null; string[] dirs = null; try { files = Directory.GetFiles(CPFilePath.GetPlatformFilePath(DefaultDirectories.PackagesDirectory)); dirs = Directory.GetDirectories(CPFilePath.GetPlatformFilePath(DefaultDirectories.PackagesDirectory)); } catch { //Failed to load, this is likely a compiled ALEC executable. Don't bother doing any more. return; } List <string> dllFiles = new List <string>(); foreach (var file in files) { if (file.Contains(".dll")) { dllFiles.Add(file); } } //Also search one directory deeper. foreach (var dir in dirs) { DirectoryInfo d = new DirectoryInfo(dir); FileInfo[] dirFiles = d.GetFiles(); foreach (var file in dirFiles) { if (file.Extension == ".dll") { dllFiles.Add(file.FullName); } } } //Attempt to load all the DLL files. foreach (var file in dllFiles) { LoadPlugin(file); } }
//////////////////////////// /// MAIN COMPILE METHODS /// //////////////////////////// public static void Compile(string file) { //Print the compile header. PrintCompileHeader(); //Note the compile start time. CompileStartTime = DateTime.Now; //Does the file that is being compiled exist? if (!File.Exists(file)) { Error.FatalCompile("The file you wish to compile does not exist."); return; } //Get the FileInfo, set the name of the project. FileInfo fi = new FileInfo(file); if (fi.Name.Contains(".")) { ProjectName = fi.Name.Split('.')[0]; } else { ProjectName = fi.Name; } //Yes, read the file into memory and strip all the comments. Log("Linking base Algo file '" + file + "'."); string toCompile = ""; try { toCompile = File.ReadAllText(file); } catch (Exception e) { Error.FatalCompile("Could not read from base script file, error '" + e.Message + "'."); return; } //Attaching the "core" library to the start. Log("Attaching the 'core' library to the base script."); MainScript = "import \"core\";" + MainScript; //Strip all comment lines. var scriptLines = toCompile.Replace("\r", "").Split('\n'); foreach (var line in scriptLines) { if (!line.StartsWith("//")) { MainScript += line + "\n"; } } Log("Successfully linked main file, linking references..."); //Get all linked import reference files. LinkFile(MainScript, file); Log("Successfully linked base and all referenced Algo scripts.", ALECEvent.Success); //Replacing import references with their proper scripts. Log("Attempting to replace abstract import links..."); bool success = ReplaceImportReferences(); if (!success) { Error.FatalCompile("Failed to replace import links with script references. Do you have a circular import loop?"); return; } //Do a sanity check to make sure it compiles as normal Algo. Log("Sanity checking the syntax of the provided script..."); SyntaxCheck(MainScript, MainScript.Replace("\r", "").Split('\n').Length - scriptLines.Length); //That's done, now convert it to a literal string. Log("Converting script into literal form for compilation..."); MainScript = MainScript.Replace("\"", "\"\""); Log("Successfully converted into literal form."); //Create the compiler (with arguments). CSharpCodeProvider provider = new CSharpCodeProvider(); CompilerParameters cp = new CompilerParameters(); cp.GenerateExecutable = true; cp.OutputAssembly = ProjectName + ".exe"; cp.GenerateInMemory = false; //Reference the main Algo assembly (this one) when compiling. Assembly entryasm = Assembly.GetEntryAssembly(); if (AlgoPlatformInfo.IsWindows) { cp.ReferencedAssemblies.Add(entryasm.Location); } else { cp.ReferencedAssemblies.Add(CPFilePath.GetPlatformFilePath(new string[] { DefaultDirectories.AssemblyDirectory, "Algo.exe" })); } cp.ReferencedAssemblies.Add(CPFilePath.GetPlatformFilePath(new string[] { DefaultDirectories.AssemblyDirectory, "Antlr4.Runtime.dll" })); //Attempt to compile. string finalScript = ALECTemplates.ALECEntryPoint.Replace("[CUSTOM-CODE-HERE]", MainScript); CompilerResults results = provider.CompileAssemblyFromSource(cp, finalScript); if (results.Errors.HasErrors) { //Uh oh, failed. //Collect the errors. string final = "Attempting to compile returned some errors:\n"; List <string> errors = new List <string>(); foreach (CompilerError error in results.Errors) { errors.Add(error.ErrorText + " (Line " + error.Line + ", column " + error.Column + ")."); } for (int i = 0; i < errors.Count; i++) { final += "[" + (i + 1) + "] - " + errors[i] + "\n"; } //Log. Error.FatalCompile(final); return; } //Successfully compiled, try to output to file. Log("Successfully compiled the Algo script into assembly.", ALECEvent.Success); Log("Output has been saved in '" + ProjectName + ".exe'."); //If Linux, MKBundle. if (AlgoPlatformInfo.IsLinux) { //Attempt to run MKBundle. Log("Linux detected as the operating system, attempting to create a native binary..."); Log("MAKE SURE YOU HAVE MKBUNDLE INSTALLED, AND HAVE A MONO 'machine.config' AT /etc/mono/4.5/machine.config."); Process proc = new Process(); proc.StartInfo.FileName = "/bin/bash"; proc.StartInfo.Arguments = "-c \" mkbundle -o " + ProjectName + " --simple " + cp.OutputAssembly + " --machine-config /etc/mono/4.5/machine.config --no-config --nodeps " + CPFilePath.GetPlatformFilePath(new string[] { DefaultDirectories.AssemblyDirectory, "*.dll" }) + " " + CPFilePath.GetPlatformFilePath(new string[] { DefaultDirectories.AssemblyDirectory, "Algo.exe" }) + "\""; proc.StartInfo.UseShellExecute = false; proc.StartInfo.RedirectStandardOutput = true; proc.Start(); while (!proc.StandardOutput.EndOfStream) { Log(proc.StandardOutput.ReadLine()); } Log("MKBundle has finished executing."); //Delete the main executable. Log("Attempting to clean up..."); try { File.Delete(ProjectName + ".exe"); } catch (Exception e) { Error.WarningCompile("Failed to clean up Windows executable, given error '" + e.Message + "'."); } } else if (AlgoPlatformInfo.IsWindows) { //It's Windows, use ILRepack instead. Log("Windows detected as the operating system, attempting to create a native binary..."); Log("Attempting to bundle dependencies into packed executable..."); RepackOptions opt = new RepackOptions(); opt.OutputFile = ProjectName + "_packed.exe"; opt.SearchDirectories = new string[] { AppDomain.CurrentDomain.BaseDirectory, Environment.CurrentDirectory }; //Setting input assemblies. string[] files = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll"); opt.InputAssemblies = new string[] { ProjectName + ".exe", entryasm.Location }.Concat(files).ToArray(); try { //Merging. ILRepack pack = new ILRepack(opt); pack.Repack(); Log("Successfully merged all dependencies with the output executable.", ALECEvent.Success); //Replacing the depending executable with the new one. Log("Cleaning up build files..."); try { File.Delete(ProjectName + ".exe"); File.Move(ProjectName + "_packed.exe", ProjectName + ".exe"); } catch (Exception e) { Error.WarningCompile("File cleanup failed with error '" + e.Message + "'."); Error.WarningCompile("Failed to clean up build files, the executable to use is named '" + ProjectName + "_packed.exe' rather than '" + ProjectName + ".exe'."); } } catch (Exception e) { Error.WarningCompile("Packing the executable's dependencies failed, with error '" + e.Message + "'. You will need to include algo.exe and all it's dependencies along with the built executable for it to run."); } } else { Error.FatalCompile("Could not detect the operating system to compile native binary."); return; } //Print the compile footer. PrintCompileFooter(); return; }
//Recursively grabs all import references through linked files in scripts, strips comments. private static void LinkFile(string toCompile, string fileName) { //Strip all comment lines. List <string> lines = toCompile.Replace("\r", "").Split('\n').ToList(); for (int i = 0; i < lines.Count; i++) { if (lines[i].StartsWith("//")) { lines.RemoveAt(i); i--; } } //Join it back together, split with line end instead. string source = string.Join("", lines); lines = source.Split(';').ToList(); //Get all the import lines. List <string> imports = new List <string>(); foreach (var line in lines) { if (ImportRegex.IsMatch(line)) { imports.Add(ImportRegex.Match(line).Value); } } //Check if there are actually any imports in this file. if (imports.Count == 0) { Log("No imports detected, skipping linking."); return; } Log("Detected " + imports.Count + " referenced external scripts."); //For each of those, check contain a valid import (regex). foreach (var line in imports) { if (!ImportRegex.IsMatch(line)) { //Uh oh, failed the link. Error.FatalCompile("An invalid import statement was found in script '" + fileName + "', \"" + line.Substring(0, 30) + "...\"."); return; } //Matches, get the substring out. string symbolicName = line.Substring("import \"".Length).TrimEnd('"'); string referencedFile = symbolicName; if (!referencedFile.EndsWith(".ag")) { referencedFile += ".ag"; } //Has the file been linked already? if (LinkedScripts.Keys.Contains(symbolicName)) { Log("Script '" + symbolicName + "' has already been linked, skipping."); continue; } //Is the file in std or packages? string stdPath = CPFilePath.GetPlatformFilePath(DefaultDirectories.StandardLibDirectory.Concat(referencedFile.Split('/')).ToArray()); string pkgPath = CPFilePath.GetPlatformFilePath(DefaultDirectories.PackagesDirectory.Concat(referencedFile.Split('/')).ToArray()); if (File.Exists(stdPath)) { Log("'" + referencedFile + "' discovered as a standard library file, attempting to read..."); referencedFile = stdPath; } else if (File.Exists(pkgPath)) { Log("'" + referencedFile + "' discovered as a package, attempting to read..."); referencedFile = pkgPath; } else { Log("Discovered external reference to '" + referencedFile + "', attempting to read..."); } //Not linked yet, trying to read the file into LinkedScripts. try { //Add the symbolic name rather than the filename, easier to substitute into the file that way. LinkedScripts.Add(symbolicName, File.ReadAllText(referencedFile)); } catch (Exception e) { Error.FatalCompile("Failed to read file '" + referencedFile + "', with error '" + e.Message + "'."); return; } //Read in. Log("Successfully read and linked file '" + referencedFile + "'.", ALECEvent.Success); //Link files for all linked scripts (recursively). for (int i = 0; i < LinkedScripts.Count; i++) { var file = LinkedScripts.ElementAt(i); LinkFile(file.Value, file.Key); } } }