Compiler.cs 18 KB

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