using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using NTERA.Interpreter; using NTERA.Interpreter.Compiler; namespace NTERA.Compiler { public class Compiler { public string InputDirectory { get; } public List> Errors { get; } = new List>(); public Dictionary DeclaredFunctions = new Dictionary(); public Compiler(string inputDirectory) { InputDirectory = inputDirectory; } public void Compile(string outputDirectory) { var globals = new VariableDictionary { ["LOCAL"] = 0, ["LOCALS"] = "", ["ARG"] = "", ["ARGS"] = "", ["GLOBAL"] = "", ["GLOBALS"] = "", ["NICKNAME"] = "", ["MASTERNAME"] = "", ["CSTR"] = "", ["CUP"] = "", ["CDOWN"] = "", ["DOWNBASE"] = "", ["TCVAR"] = "", ["CDFLAG"] = "", ["ITEMPRICE"] = "", ["TRAINNAME"] = "", ["BASENAME"] = "", ["EQUIPNAME"] = "", ["TEQUIPNAME"] = "", ["STAINNAME"] = "", ["EXNAME"] = "", ["SOURCENAME"] = "", ["FLAGNAME"] = "", ["TFLAGNAME"] = "", ["CFLAGNAME"] = "", ["TCVARNAME"] = "", ["STRNAME"] = "", ["TSTRNAME"] = "", ["CSTRNAME"] = "", ["SAVESTRNAME"] = "", ["CDFLAGNAME"] = "", ["CDFLAGNAME"] = "", ["GLOBALNAME"] = "", ["GLOBALSNAME"] = "", ["GAMEBASE_AUTHOR"] = "", ["GAMEBASE_INFO"] = "", ["GAMEBASE_YEAR"] = "", ["GAMEBASE_TITLE"] = "", ["GAMEBASE_GAMECODE"] = "", ["GAMEBASE_VERSION"] = "", ["GAMEBASE_ALLOWVERSION"] = "", ["GAMEBASE_DEFAULTCHARA"] = "", ["GAMEBASE_NOITEM"] = "", ["WINDOW_TITLE"] = "", ["MONEYLABEL"] = "", ["DRAWLINESTR"] = "", ["LASTLOAD_VERSION"] = "", ["LASTLOAD_NO"] = "", ["LASTLOAD_TEXT"] = "", ["SAVEDATA_TEXT"] = "", ["TSTR"] = "", ["RANDDATA"] = "", ["LINECOUNT"] = "", ["ISTIMEOUT"] = "", ["__INT_MAX__"] = "", ["__INT_MIN__"] = "", ["NAME"] = "", ["CALLNAME"] = "", ["RAND"] = "", ["CHARANUM"] = "", ["TALENT"] = "", ["FLAG"] = "", ["TFLAG"] = "", ["CFLAG"] = "", ["MASTER"] = "", ["BASE"] = "", ["MAXBASE"] = "", ["SOURCE"] = "", ["ABL"] = "", ["PALAM"] = "", ["TEQUIP"] = "", ["EQUIP"] = "", ["DAY"] = "", ["MARK"] = "", ["PALAMLV"] = "", ["TARGET"] = "", ["PLAYER"] = "", ["NOWEX"] = "", ["EX"] = "", ["EXNAME"] = "", ["STAIN"] = "", ["EXP"] = "", ["ASSIPLAY"] = "", ["ASSI"] = "", ["ITEM"] = "", ["EXPLV"] = "", ["TIME"] = "", ["MONEY"] = "", ["SELECTCOM"] = "", ["JUEL"] = "", ["STR"] = "", ["PREVCOM"] = "", ["SAVESTR"] = "", ["ABLNAME"] = "", ["MARKNAME"] = "", ["TALENTNAME"] = "", ["ITEMNAME"] = "", ["PALAMNAME"] = "", ["EXPNAME"] = "", ["DITEMTYPE"] = "", ["NO"] = "", ["RELATION"] = "", ["IS"] = "" }; foreach (char c in "ABCDEFGHIJKLMNOPQRSTUVWXYZ") globals[c.ToString()] = ""; var funcs = new List { new FunctionDefinition("RAND", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()), new FunctionDefinition("TOSTR", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0], "") }, new FunctionVariable[0], true, "_GLOBAL", new Marker()), new FunctionDefinition("STRCOUNT", new[] { new FunctionParameter("input", new string[0]), new FunctionParameter("match", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()), new FunctionDefinition("MIN", new[] { new FunctionParameter("a", new string[0], isArrayParameter: true), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()), new FunctionDefinition("MAX", new[] { new FunctionParameter("a", new string[0], isArrayParameter: true), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()), new FunctionDefinition("POWER", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()), new FunctionDefinition("GETPALAMLV", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()), new FunctionDefinition("GETBIT", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()), new FunctionDefinition("UNICODE", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()), new FunctionDefinition("MATCH", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0]), new FunctionParameter("c", new string[0], "a"), new FunctionParameter("d", new string[0], "f"), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()), new FunctionDefinition("INRANGE", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0]), new FunctionParameter("c", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()), new FunctionDefinition("HE_SHE", new[] { new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()), new FunctionDefinition("SUBSTRING", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()), new FunctionDefinition("STRLENS", new[] { new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()), new FunctionDefinition("CSVNAME", new[] { new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()), new FunctionDefinition("CSVNAME", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()), new FunctionDefinition("CSVTALENT", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()), new FunctionDefinition("CSVCSTR", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()), new FunctionDefinition("GETNUM", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()), new FunctionDefinition("FINDCHARA", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()), new FunctionDefinition("LIMIT", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()), new FunctionDefinition("GETTIME", new FunctionParameter[0], new FunctionVariable[0], true, "_GLOBAL", new Marker()), new FunctionDefinition("SAVENOS", new FunctionParameter[0], new FunctionVariable[0], true, "_GLOBAL", new Marker()), new FunctionDefinition("GETCOLOR", new FunctionParameter[0], new FunctionVariable[0], true, "_GLOBAL", new Marker()), new FunctionDefinition("CURRENTREDRAW", new FunctionParameter[0], new FunctionVariable[0], true, "_GLOBAL", new Marker()), }; var stringStatements = new List { "DRAWLINEFORM", "PRINTFORML", "PRINT", "PRINTW", "PRINTV", "PRINTL", "PRINTLC", "PRINTC", "ALIGNMENT", "CALL", "CUSTOMDRAWLINE", "DATAFORM", "GOTO", "PRINTFORMDL", "PRINTFORMW", "PRINTFORMDW", "PRINTFORMC", "PRINTFORMLC", "PRINTFORM", "PRINTPLAINFORM", "DEBUGPRINTL", "DEBUGPRINTFORML", "REUSELASTLINE", "PRINTPLAIN", "PRINT_TRAIN_NAME", "PRINT_STR", }; string csvPath = Path.Combine(InputDirectory, "CSV"); string erbPath = Path.Combine(InputDirectory, "ERB"); var csvDefinition = new CSVDefinition(); 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)); } 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 = 4 }, file => #endif { try { foreach (var kv in Preprocessor.PreprocessFile(File.ReadAllText(file), Path.GetFileName(file))) preprocessedFunctions[kv.Key] = kv.Value; } catch (Exception ex) { lock (Errors) Errors.Add(new Tuple(new ParserError($"Internal pre-process lexer error [{ex}]", new Marker()), Path.GetFileName(file))); } #if DEBUG } #else }); #endif DeclaredFunctions = preprocessedFunctions.ToDictionary(kv => kv.Key, kv => kv.Value); funcs.AddRange(DeclaredFunctions.Keys.Where(x => x.IsReturnFunction)); var procedures = DeclaredFunctions.Keys.Where(x => !x.IsReturnFunction).ToList(); #if DEBUG foreach (var kv in DeclaredFunctions) #else Parallel.ForEach(DeclaredFunctions, new ParallelOptions { MaxDegreeOfParallelism = 4 }, kv => #endif { try { var locals = new VariableDictionary(); foreach (var param in kv.Key.Variables) locals[param.Name] = param.DefaultValue ?? param.ValueType == Interpreter.ValueType.String ? new Value("") : new Value(0d); Parser parser = new Parser(kv.Value, kv.Key, funcs, procedures, globals, locals, stringStatements, csvDefinition); var nodes = parser.Parse(out var localErrors); lock (Errors) Errors.AddRange(localErrors.Select(x => new Tuple(x, $"{kv.Key.Filename} - {kv.Key.Name}"))); } catch (Exception ex) { lock (Errors) Errors.Add(new Tuple(new ParserError($"Internal post-process lexer error [{ex}]", new Marker()), $"{kv.Key.Filename} - {kv.Key.Name}")); } #if DEBUG } #else }); #endif long fileSize = 0; int fileCount = 0; foreach (var file in Directory.EnumerateFiles(InputDirectory, "*.erb", SearchOption.AllDirectories)) { var info = new FileInfo(file); fileSize += info.Length; fileCount++; } var htmlWriter = new HTMLWriter { Errors = Errors.OrderBy(x => x.Item2).ToList(), FunctionCount = DeclaredFunctions.Count, TotalFileSize = fileSize, TotalFileCount = fileCount }; using (var stream = File.Create(Path.Combine(outputDirectory, "report.html"))) htmlWriter.WriteReportToFile(stream); } } }