Compiler.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Text;
  7. using System.Threading.Tasks;
  8. using NTERA.Engine;
  9. using NTERA.Engine.Compiler;
  10. namespace NTERA.Compiler
  11. {
  12. public class Compiler
  13. {
  14. public string InputDirectory { get; }
  15. public List<Tuple<ParserError, string>> Errors { get; } = new List<Tuple<ParserError, string>>();
  16. public List<Tuple<ParserError, string>> Warnings { get; } = new List<Tuple<ParserError, string>>();
  17. public Dictionary<FunctionDefinition, string> DeclaredFunctions = new Dictionary<FunctionDefinition, string>();
  18. public int Threads { get; set; }
  19. public Compiler(string inputDirectory, int threads)
  20. {
  21. InputDirectory = inputDirectory;
  22. Threads = threads;
  23. }
  24. protected static string[] DefaultGlobalNumberVariables =
  25. {
  26. "LOCAL",
  27. "ABL",
  28. "RESULT",
  29. "SOURCE",
  30. "ARG",
  31. "GLOBAL",
  32. "IS",
  33. "CUP",
  34. "CDOWN",
  35. "UP",
  36. "DOWN",
  37. "DOWNBASE",
  38. "COUNT",
  39. "TCVAR",
  40. "CDFLAG",
  41. "ITEMPRICE",
  42. "RANDDATA",
  43. "LINECOUNT",
  44. "ISTIMEOUT",
  45. "__INT_MAX__",
  46. "__INT_MIN__",
  47. "RAND",
  48. "CHARANUM",
  49. "TALENT",
  50. "FLAG",
  51. "TFLAG",
  52. "CFLAG",
  53. "MASTER",
  54. "BASE",
  55. "MAXBASE",
  56. "PALAM",
  57. "TEQUIP",
  58. "EQUIP",
  59. "DAY",
  60. "MARK",
  61. "PALAMLV",
  62. "TARGET",
  63. "PLAYER",
  64. "NOWEX",
  65. "EX",
  66. "STAIN",
  67. "EXP",
  68. "ASSIPLAY",
  69. "ASSI",
  70. "ITEM",
  71. "EXPLV",
  72. "TIME",
  73. "MONEY",
  74. "SELECTCOM",
  75. "PREVCOM",
  76. "NEXTCOM",
  77. "DITEMTYPE",
  78. "NO",
  79. "RELATION",
  80. "JUEL",
  81. "GOTJUEL",
  82. "EJAC",
  83. "BOUGHT",
  84. "ITEMSALES",
  85. "FORWARD", //special casing for SORTCHARA
  86. "BACK", //special casing for SORTCHARA
  87. "LEFT", //special casing for __FORMAT
  88. "RIGHT", //special casing for __FORMAT
  89. "DEBUG_MODE",
  90. "NOITEM",
  91. };
  92. protected static string[] DefaultGlobalStringVariables =
  93. {
  94. "LOCALS",
  95. "ARGS",
  96. "RESULTS",
  97. "CSTR",
  98. "GLOBALS",
  99. "NICKNAME",
  100. "MASTERNAME",
  101. "NAME",
  102. "TRAINNAME",
  103. "BASENAME",
  104. "EQUIPNAME",
  105. "TEQUIPNAME",
  106. "STAINNAME",
  107. "EXNAME",
  108. "SOURCENAME",
  109. "CALLNAME",
  110. "FLAGNAME",
  111. "TFLAGNAME",
  112. "CFLAGNAME",
  113. "TCVARNAME",
  114. "STRNAME",
  115. "TSTRNAME",
  116. "CSTRNAME",
  117. "SAVESTRNAME",
  118. "CDFLAGNAME",
  119. "GLOBALNAME",
  120. "GLOBALSNAME",
  121. "GAMEBASE_AUTHOR",
  122. "GAMEBASE_INFO",
  123. "GAMEBASE_YEAR",
  124. "GAMEBASE_TITLE",
  125. "GAMEBASE_GAMECODE",
  126. "GAMEBASE_VERSION",
  127. "GAMEBASE_ALLOWVERSION",
  128. "GAMEBASE_DEFAULTCHARA",
  129. "GAMEBASE_NOITEM",
  130. "WINDOW_TITLE",
  131. "MONEYLABEL",
  132. "DRAWLINESTR",
  133. "LASTLOAD_VERSION",
  134. "LASTLOAD_NO",
  135. "LASTLOAD_TEXT",
  136. "SAVEDATA_TEXT",
  137. "TSTR",
  138. "STR",
  139. "SAVESTR",
  140. "ABLNAME",
  141. "MARKNAME",
  142. "TALENTNAME",
  143. "ITEMNAME",
  144. "PALAMNAME",
  145. "EXPNAME",
  146. };
  147. public void Compile(string outputDirectory)
  148. {
  149. var globals = new VariableDictionary();
  150. foreach (string stringVariable in DefaultGlobalStringVariables)
  151. globals.Add(stringVariable, "");
  152. foreach (string numberVariable in DefaultGlobalNumberVariables)
  153. globals.Add(numberVariable, 0);
  154. foreach (char c in "ABCDEFGHIJKLMNOPQRSTUVWXYZ")
  155. globals[c.ToString()] = "";
  156. var funcs = new List<FunctionDefinition>
  157. {
  158. new FunctionDefinition("ABS", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  159. new FunctionDefinition("SQRT", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  160. new FunctionDefinition("SIGN", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  161. new FunctionDefinition("CSVCALLNAME", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  162. new FunctionDefinition("TOFULL", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  163. new FunctionDefinition("TOINT", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  164. new FunctionDefinition("RAND", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  165. new FunctionDefinition("VARSIZE", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  166. new FunctionDefinition("TOSTR", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0], "") }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  167. new FunctionDefinition("TOUPPER", new[] { new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  168. new FunctionDefinition("TOLOWER", new[] { new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  169. new FunctionDefinition("TOHALF", new[] { new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  170. new FunctionDefinition("TOFULL", new[] { new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  171. new FunctionDefinition("TOUPPER", new[] { new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  172. new FunctionDefinition("ISNUMERIC", new[] { new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  173. new FunctionDefinition("LOG10", new[] { new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  174. new FunctionDefinition("ESCAPE", new[] { new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  175. new FunctionDefinition("STRCOUNT", new[] { new FunctionParameter("input", new string[0]), new FunctionParameter("match", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  176. new FunctionDefinition("MIN", new[] { new FunctionParameter("a", new string[0], isArrayParameter: true), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  177. new FunctionDefinition("MAX", new[] { new FunctionParameter("a", new string[0], isArrayParameter: true), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  178. new FunctionDefinition("POWER", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  179. new FunctionDefinition("GETPALAMLV", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  180. new FunctionDefinition("GETBIT", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  181. new FunctionDefinition("GETEXPLV", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  182. new FunctionDefinition("UNICODE", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  183. 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()),
  184. 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()),
  185. new FunctionDefinition("HE_SHE", new[] { new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  186. 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()),
  187. new FunctionDefinition("STRLENS", new[] { new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  188. new FunctionDefinition("CSVNAME", new[] { new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  189. new FunctionDefinition("CSVNAME", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  190. new FunctionDefinition("CSVBASE", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  191. new FunctionDefinition("CSVTALENT", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  192. new FunctionDefinition("CSVCFLAG", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  193. new FunctionDefinition("CSVRELATION", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  194. new FunctionDefinition("CSVCSTR", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  195. new FunctionDefinition("CSVEXP", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  196. new FunctionDefinition("CSVABL", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  197. new FunctionDefinition("GETNUM", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  198. 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()),
  199. 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()),
  200. new FunctionDefinition("SUMCARRAY", 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()),
  201. new FunctionDefinition("MAXCARRAY", 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()),
  202. new FunctionDefinition("SUBSTRINGU", 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()),
  203. new FunctionDefinition("FINDELEMENT", 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 FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  204. new FunctionDefinition("GROUPMATCH", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0], isArrayParameter: true), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  205. new FunctionDefinition("GETTIME", new FunctionParameter[0], new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  206. new FunctionDefinition("SAVENOS", new FunctionParameter[0], new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  207. new FunctionDefinition("GETCOLOR", new FunctionParameter[0], new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  208. new FunctionDefinition("CURRENTREDRAW", new FunctionParameter[0], new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  209. new FunctionDefinition("GETFONT", new FunctionParameter[0], new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  210. new FunctionDefinition("GETMILLISECOND", new FunctionParameter[0], new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  211. };
  212. var stringStatements = new List<Keyword>
  213. {
  214. new Keyword("DRAWLINEFORM", true, true),
  215. new Keyword("PRINTFORML", true, true),
  216. new Keyword("DATAFORM", true, true),
  217. new Keyword("PRINTFORMD", true, true),
  218. new Keyword("PRINTFORMDL", true, true),
  219. new Keyword("PRINTFORMW", true, true),
  220. new Keyword("PRINTFORMDW", true, true),
  221. new Keyword("PRINTFORMC", true, true),
  222. new Keyword("PRINTFORMLC", true, true),
  223. new Keyword("PRINTFORM", true, true),
  224. new Keyword("PRINTPLAINFORM", true, true),
  225. new Keyword("DEBUGPRINTFORM", true, true),
  226. new Keyword("DEBUGPRINTFORML", true, true),
  227. new Keyword("THROW", true, true),
  228. new Keyword("PRINT", true),
  229. new Keyword("PRINTD", true),
  230. new Keyword("PRINTDW", true),
  231. new Keyword("PRINTDL", true),
  232. new Keyword("PRINTW", true),
  233. new Keyword("PRINTV", true),
  234. new Keyword("PRINTL", true),
  235. new Keyword("PRINTLC", true),
  236. new Keyword("PRINTC", true),
  237. new Keyword("ALIGNMENT", true),
  238. new Keyword("CALL", true, true),
  239. new Keyword("CUSTOMDRAWLINE", true),
  240. new Keyword("GOTO", true),
  241. new Keyword("DEBUGPRINTL", true),
  242. new Keyword("REUSELASTLINE", true),
  243. new Keyword("PRINTPLAIN", true),
  244. new Keyword("PRINT_TRAIN_NAME", true),
  245. new Keyword("PRINT_STR", true),
  246. };
  247. string csvPath = Path.Combine(InputDirectory, "CSV");
  248. string erbPath = Path.Combine(InputDirectory, "ERB");
  249. var csvDefinition = new CSVDefinition();
  250. Console.WriteLine("Preprocessing CSV files...");
  251. foreach (var file in Directory.EnumerateFiles(csvPath, "*.csv", SearchOption.AllDirectories))
  252. {
  253. string path = Path.GetFileNameWithoutExtension(file);
  254. if (path.EndsWith("_TR"))
  255. continue;
  256. Preprocessor.ProcessCSV(csvDefinition, path, File.ReadLines(file, Encoding.UTF8));
  257. }
  258. Console.WriteLine("Preprocessing header files...");
  259. ConcurrentBag<FunctionVariable> preprocessedConstants = new ConcurrentBag<FunctionVariable>();
  260. #if DEBUG
  261. foreach (var file in Directory.EnumerateFiles(erbPath, "*.erh", SearchOption.AllDirectories))
  262. #else
  263. Parallel.ForEach(Directory.EnumerateFiles(erbPath, "*.erh", SearchOption.AllDirectories), new ParallelOptions
  264. {
  265. MaxDegreeOfParallelism = Threads
  266. }, file =>
  267. #endif
  268. {
  269. try
  270. {
  271. foreach (var variable in Preprocessor.PreprocessHeaderFile(File.ReadAllText(file)))
  272. preprocessedConstants.Add(variable);
  273. }
  274. catch (Exception ex)
  275. {
  276. lock (Errors)
  277. Errors.Add(new Tuple<ParserError, string>(new ParserError($"Internal pre-process lexer error [{ex}]", new Marker()), Path.GetFileName(file)));
  278. }
  279. #if DEBUG
  280. }
  281. #else
  282. });
  283. #endif
  284. Console.WriteLine("Preprocessing functions...");
  285. ConcurrentDictionary<FunctionDefinition, string> preprocessedFunctions = new ConcurrentDictionary<FunctionDefinition, string>();
  286. #if DEBUG
  287. foreach (var file in Directory.EnumerateFiles(erbPath, "*.erb", SearchOption.AllDirectories))
  288. #else
  289. Parallel.ForEach(Directory.EnumerateFiles(erbPath, "*.erb", SearchOption.AllDirectories), new ParallelOptions
  290. {
  291. MaxDegreeOfParallelism = Threads
  292. }, file =>
  293. #endif
  294. {
  295. try
  296. {
  297. foreach (var kv in Preprocessor.PreprocessCodeFile(File.ReadAllText(file), Path.GetFileName(file), preprocessedConstants.ToArray()))
  298. preprocessedFunctions[kv.Key] = kv.Value;
  299. }
  300. catch (Exception ex)
  301. {
  302. lock (Errors)
  303. Errors.Add(new Tuple<ParserError, string>(new ParserError($"Internal pre-process lexer error [{ex}]", new Marker()), Path.GetFileName(file)));
  304. }
  305. #if DEBUG
  306. }
  307. #else
  308. });
  309. #endif
  310. DeclaredFunctions = preprocessedFunctions.ToDictionary(kv => kv.Key, kv => kv.Value);
  311. funcs.AddRange(DeclaredFunctions.Keys.Where(x => x.IsReturnFunction));
  312. var procedures = DeclaredFunctions.Keys.Where(x => !x.IsReturnFunction).ToList();
  313. Console.WriteLine("Compiling functions...");
  314. #if DEBUG
  315. foreach (var kv in DeclaredFunctions)
  316. #else
  317. Parallel.ForEach(DeclaredFunctions, new ParallelOptions
  318. {
  319. MaxDegreeOfParallelism = Threads
  320. }, kv =>
  321. #endif
  322. {
  323. try
  324. {
  325. var locals = new VariableDictionary();
  326. foreach (var param in kv.Key.Variables)
  327. locals[param.Name] = param.CalculatedValue;
  328. Parser parser = new Parser(kv.Value, kv.Key, funcs, procedures, globals, locals, stringStatements, csvDefinition, preprocessedConstants.ToArray());
  329. var nodes = parser.Parse(out var localErrors, out var localWarnings);
  330. lock (Errors)
  331. Errors.AddRange(localErrors.Select(x => new Tuple<ParserError, string>(x, $"{kv.Key.Filename} - {kv.Key.Name}")));
  332. lock (Warnings)
  333. Warnings.AddRange(localWarnings.Select(x => new Tuple<ParserError, string>(x, $"{kv.Key.Filename} - {kv.Key.Name}")));
  334. }
  335. catch (Exception ex)
  336. {
  337. lock (Errors)
  338. Errors.Add(new Tuple<ParserError, string>(new ParserError($"Internal post-process lexer error [{ex}]", new Marker()), $"{kv.Key.Filename} - {kv.Key.Name}"));
  339. }
  340. #if DEBUG
  341. }
  342. #else
  343. });
  344. #endif
  345. long fileSize = 0;
  346. int fileCount = 0;
  347. foreach (var file in Directory.EnumerateFiles(InputDirectory, "*.erb", SearchOption.AllDirectories))
  348. {
  349. var info = new FileInfo(file);
  350. fileSize += info.Length;
  351. fileCount++;
  352. }
  353. var htmlWriter = new HTMLWriter
  354. {
  355. Errors = Errors.OrderBy(x => x.Item2).ToList(),
  356. Warnings = Warnings.OrderBy(x => x.Item2).ToList(),
  357. FunctionCount = DeclaredFunctions.Count,
  358. TotalFileSize = fileSize,
  359. TotalFileCount = fileCount
  360. };
  361. using (var stream = File.Create(Path.Combine(outputDirectory, "report.html")))
  362. htmlWriter.WriteReportToFile(stream);
  363. }
  364. }
  365. }