using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime; using System.Text; using System.Threading.Tasks; using NTERA.Engine.Compiler; namespace NTERA.Engine.Runtime { public class JITCompiler : IExecutionProvider { public ICollection DefinedProcedures { get; protected set; } public ICollection DefinedFunctions { get; protected set; } public ICollection DefinedConstants { get; protected set; } public CSVDefinition CSVDefinition { get; protected set; } protected Dictionary ProcedureFiles { get; set; } public Dictionary CompiledProcedures { get; protected set; } = new Dictionary(); public string InputDirectory { get; } public static int Threads = 4; public JITCompiler(string path) { InputDirectory = path; Compile(); } protected void Compile() { string csvPath = Path.Combine(InputDirectory, "CSV"); string erbPath = Path.Combine(InputDirectory, "ERB"); var csvDefinition = new CSVDefinition(); Console.WriteLine("Preprocessing CSV files..."); foreach (var file in Directory.EnumerateFiles(csvPath, "*.csv", SearchOption.AllDirectories)) { string path = Path.GetFileNameWithoutExtension(file); if (path.EndsWith("_TR")) continue; Preprocessor.ProcessCSV(csvDefinition, path, File.ReadLines(file, Encoding.UTF8)); } Console.WriteLine("Preprocessing header files..."); ConcurrentBag preprocessedConstants = new ConcurrentBag(); #if DEBUG foreach (var file in Directory.EnumerateFiles(erbPath, "*.erh", SearchOption.AllDirectories)) #else Parallel.ForEach(Directory.EnumerateFiles(erbPath, "*.erh", SearchOption.AllDirectories), new ParallelOptions { MaxDegreeOfParallelism = Threads }, file => #endif { foreach (var variable in Preprocessor.PreprocessHeaderFile(File.ReadAllText(file))) preprocessedConstants.Add(variable); #if DEBUG } #else }); #endif DefinedConstants = preprocessedConstants.ToArray(); Console.WriteLine("Preprocessing functions..."); ProcedureFiles = new Dictionary(); ConcurrentDictionary preprocessedFunctions = new ConcurrentDictionary(); #if DEBUG foreach (var file in Directory.EnumerateFiles(erbPath, "*.erb", SearchOption.AllDirectories)) #else Parallel.ForEach(Directory.EnumerateFiles(erbPath, "*.erb", SearchOption.AllDirectories), new ParallelOptions { MaxDegreeOfParallelism = Threads }, file => #endif { foreach (var kv in Preprocessor.PreprocessCodeFile(File.ReadAllText(file), Path.GetFileName(file), DefinedConstants)) { preprocessedFunctions[kv.Key] = kv.Value; ProcedureFiles[kv.Key] = file; } #if DEBUG } #else }); #endif DefinedProcedures = preprocessedFunctions.Select(kv => kv.Key).ToArray(); var declaredFunctions = BaseDefinitions.DefaultGlobalFunctions.ToList(); declaredFunctions.AddRange(DefinedProcedures.Where(x => x.IsReturnFunction)); DefinedFunctions = declaredFunctions; GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce; GC.Collect(); } public IEnumerable GetExecutionNodes(FunctionDefinition function) { if (CompiledProcedures.ContainsKey(function)) return CompiledProcedures[function]; string filename = ProcedureFiles[function]; var preprocessed = Preprocessor.PreprocessCodeFile(File.ReadAllText(filename), Path.GetFileName(filename), DefinedConstants); Parser parser = new Parser(preprocessed.Single(x => x.Key.Name == function.Name).Value, function, DefinedFunctions, DefinedProcedures.Where(x => !x.IsReturnFunction).ToArray(), BaseDefinitions.DefaultGlobalVariables, function.Variables, BaseDefinitions.DefaultKeywords, CSVDefinition, DefinedConstants); var nodes = parser.Parse(out var localErrors, out var localWarnings)?.ToArray(); if (localErrors.Count > 0) throw new ParserException($"Failed to compile '{function.Name}'"); CompiledProcedures.Add(function, nodes); return nodes; } } }