Compiler.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  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. };
  89. protected static string[] DefaultGlobalStringVariables =
  90. {
  91. "LOCALS",
  92. "ARGS",
  93. "RESULTS",
  94. "CSTR",
  95. "GLOBALS",
  96. "NICKNAME",
  97. "MASTERNAME",
  98. "NAME",
  99. "TRAINNAME",
  100. "BASENAME",
  101. "EQUIPNAME",
  102. "TEQUIPNAME",
  103. "STAINNAME",
  104. "EXNAME",
  105. "SOURCENAME",
  106. "CALLNAME",
  107. "FLAGNAME",
  108. "TFLAGNAME",
  109. "CFLAGNAME",
  110. "TCVARNAME",
  111. "STRNAME",
  112. "TSTRNAME",
  113. "CSTRNAME",
  114. "SAVESTRNAME",
  115. "CDFLAGNAME",
  116. "GLOBALNAME",
  117. "GLOBALSNAME",
  118. "GAMEBASE_AUTHOR",
  119. "GAMEBASE_INFO",
  120. "GAMEBASE_YEAR",
  121. "GAMEBASE_TITLE",
  122. "GAMEBASE_GAMECODE",
  123. "GAMEBASE_VERSION",
  124. "GAMEBASE_ALLOWVERSION",
  125. "GAMEBASE_DEFAULTCHARA",
  126. "GAMEBASE_NOITEM",
  127. "WINDOW_TITLE",
  128. "MONEYLABEL",
  129. "DRAWLINESTR",
  130. "LASTLOAD_VERSION",
  131. "LASTLOAD_NO",
  132. "LASTLOAD_TEXT",
  133. "SAVEDATA_TEXT",
  134. "TSTR",
  135. "STR",
  136. "SAVESTR",
  137. "ABLNAME",
  138. "MARKNAME",
  139. "TALENTNAME",
  140. "ITEMNAME",
  141. "PALAMNAME",
  142. "EXPNAME",
  143. };
  144. public void Compile(string outputDirectory)
  145. {
  146. var globals = new VariableDictionary();
  147. foreach (string stringVariable in DefaultGlobalStringVariables)
  148. globals.Add(stringVariable, "");
  149. foreach (string numberVariable in DefaultGlobalNumberVariables)
  150. globals.Add(numberVariable, 0);
  151. foreach (char c in "ABCDEFGHIJKLMNOPQRSTUVWXYZ")
  152. globals[c.ToString()] = "";
  153. var funcs = new List<FunctionDefinition>
  154. {
  155. new FunctionDefinition("ABS", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  156. new FunctionDefinition("CSVCALLNAME", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  157. new FunctionDefinition("TOFULL", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  158. new FunctionDefinition("TOINT", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  159. new FunctionDefinition("RAND", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  160. new FunctionDefinition("TOSTR", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0], "") }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  161. new FunctionDefinition("STRCOUNT", new[] { new FunctionParameter("input", new string[0]), new FunctionParameter("match", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  162. new FunctionDefinition("MIN", new[] { new FunctionParameter("a", new string[0], isArrayParameter: true), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  163. new FunctionDefinition("MAX", new[] { new FunctionParameter("a", new string[0], isArrayParameter: true), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  164. new FunctionDefinition("POWER", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  165. new FunctionDefinition("GETPALAMLV", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  166. new FunctionDefinition("GETBIT", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  167. new FunctionDefinition("GETEXPLV", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  168. new FunctionDefinition("UNICODE", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  169. 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()),
  170. 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()),
  171. new FunctionDefinition("HE_SHE", new[] { new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  172. 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()),
  173. new FunctionDefinition("STRLENS", new[] { new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  174. new FunctionDefinition("CSVNAME", new[] { new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  175. new FunctionDefinition("CSVNAME", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  176. new FunctionDefinition("CSVBASE", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  177. new FunctionDefinition("CSVTALENT", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  178. new FunctionDefinition("CSVCSTR", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  179. new FunctionDefinition("CSVEXP", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  180. new FunctionDefinition("CSVABL", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  181. new FunctionDefinition("GETNUM", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  182. 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()),
  183. 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()),
  184. 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()),
  185. 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()),
  186. new FunctionDefinition("GETTIME", new FunctionParameter[0], new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  187. new FunctionDefinition("SAVENOS", new FunctionParameter[0], new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  188. new FunctionDefinition("GETCOLOR", new FunctionParameter[0], new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  189. new FunctionDefinition("CURRENTREDRAW", new FunctionParameter[0], new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  190. new FunctionDefinition("GETFONT", new FunctionParameter[0], new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  191. new FunctionDefinition("GETMILLISECOND", new FunctionParameter[0], new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  192. };
  193. var stringStatements = new List<Keyword>
  194. {
  195. new Keyword("DRAWLINEFORM", true, true),
  196. new Keyword("PRINTFORML", true, true),
  197. new Keyword("DATAFORM", true, true),
  198. new Keyword("PRINTFORMD", true, true),
  199. new Keyword("PRINTFORMDL", true, true),
  200. new Keyword("PRINTFORMW", true, true),
  201. new Keyword("PRINTFORMDW", true, true),
  202. new Keyword("PRINTFORMC", true, true),
  203. new Keyword("PRINTFORMLC", true, true),
  204. new Keyword("PRINTFORM", true, true),
  205. new Keyword("PRINTPLAINFORM", true, true),
  206. new Keyword("DEBUGPRINTFORML", true, true),
  207. new Keyword("THROW", true, true),
  208. new Keyword("PRINT", true),
  209. new Keyword("PRINTW", true),
  210. new Keyword("PRINTV", true),
  211. new Keyword("PRINTL", true),
  212. new Keyword("PRINTLC", true),
  213. new Keyword("PRINTC", true),
  214. new Keyword("ALIGNMENT", true),
  215. new Keyword("CALL", true, true),
  216. new Keyword("CUSTOMDRAWLINE", true),
  217. new Keyword("GOTO", true),
  218. new Keyword("DEBUGPRINTL", true),
  219. new Keyword("REUSELASTLINE", true),
  220. new Keyword("PRINTPLAIN", true),
  221. new Keyword("PRINT_TRAIN_NAME", true),
  222. new Keyword("PRINT_STR", true),
  223. };
  224. string csvPath = Path.Combine(InputDirectory, "CSV");
  225. string erbPath = Path.Combine(InputDirectory, "ERB");
  226. var csvDefinition = new CSVDefinition();
  227. Console.WriteLine("Preprocessing CSV files...");
  228. foreach (var file in Directory.EnumerateFiles(csvPath, "*.csv", SearchOption.AllDirectories))
  229. {
  230. string path = Path.GetFileNameWithoutExtension(file);
  231. if (path.EndsWith("_TR"))
  232. continue;
  233. Preprocessor.ProcessCSV(csvDefinition, path, File.ReadLines(file, Encoding.UTF8));
  234. }
  235. Console.WriteLine("Preprocessing header files...");
  236. ConcurrentBag<FunctionVariable> preprocessedConstants = new ConcurrentBag<FunctionVariable>();
  237. #if DEBUG
  238. foreach (var file in Directory.EnumerateFiles(erbPath, "*.erh", SearchOption.AllDirectories))
  239. #else
  240. Parallel.ForEach(Directory.EnumerateFiles(erbPath, "*.erh", SearchOption.AllDirectories), new ParallelOptions
  241. {
  242. MaxDegreeOfParallelism = 8
  243. }, file =>
  244. #endif
  245. {
  246. try
  247. {
  248. foreach (var variable in Preprocessor.PreprocessHeaderFile(File.ReadAllText(file)))
  249. preprocessedConstants.Add(variable);
  250. }
  251. catch (Exception ex)
  252. {
  253. lock (Errors)
  254. Errors.Add(new Tuple<ParserError, string>(new ParserError($"Internal pre-process lexer error [{ex}]", new Marker()), Path.GetFileName(file)));
  255. }
  256. #if DEBUG
  257. }
  258. #else
  259. });
  260. #endif
  261. Console.WriteLine("Preprocessing functions...");
  262. ConcurrentDictionary<FunctionDefinition, string> preprocessedFunctions = new ConcurrentDictionary<FunctionDefinition, string>();
  263. #if DEBUG
  264. foreach (var file in Directory.EnumerateFiles(erbPath, "*.erb", SearchOption.AllDirectories))
  265. #else
  266. Parallel.ForEach(Directory.EnumerateFiles(erbPath, "*.erb", SearchOption.AllDirectories), new ParallelOptions
  267. {
  268. MaxDegreeOfParallelism = 8
  269. }, file =>
  270. #endif
  271. {
  272. try
  273. {
  274. foreach (var kv in Preprocessor.PreprocessCodeFile(File.ReadAllText(file), Path.GetFileName(file), null))
  275. preprocessedFunctions[kv.Key] = kv.Value;
  276. }
  277. catch (Exception ex)
  278. {
  279. lock (Errors)
  280. Errors.Add(new Tuple<ParserError, string>(new ParserError($"Internal pre-process lexer error [{ex}]", new Marker()), Path.GetFileName(file)));
  281. }
  282. #if DEBUG
  283. }
  284. #else
  285. });
  286. #endif
  287. DeclaredFunctions = preprocessedFunctions.ToDictionary(kv => kv.Key, kv => kv.Value);
  288. funcs.AddRange(DeclaredFunctions.Keys.Where(x => x.IsReturnFunction));
  289. var procedures = DeclaredFunctions.Keys.Where(x => !x.IsReturnFunction).ToList();
  290. Console.WriteLine("Compiling functions...");
  291. #if DEBUG
  292. foreach (var kv in DeclaredFunctions)
  293. #else
  294. Parallel.ForEach(DeclaredFunctions, new ParallelOptions
  295. {
  296. MaxDegreeOfParallelism = 8
  297. }, kv =>
  298. #endif
  299. {
  300. try
  301. {
  302. var locals = new VariableDictionary();
  303. foreach (var param in kv.Key.Variables)
  304. locals[param.Name] = param.DefaultValue ?? param.ValueType == Interpreter.ValueType.String ? new Value("") : new Value(0d);
  305. Parser parser = new Parser(kv.Value, kv.Key, funcs, procedures, globals, locals, stringStatements, csvDefinition);
  306. var nodes = parser.Parse(out var localErrors, out var localWarnings);
  307. lock (Errors)
  308. Errors.AddRange(localErrors.Select(x => new Tuple<ParserError, string>(x, $"{kv.Key.Filename} - {kv.Key.Name}")));
  309. lock (Warnings)
  310. Warnings.AddRange(localWarnings.Select(x => new Tuple<ParserError, string>(x, $"{kv.Key.Filename} - {kv.Key.Name}")));
  311. }
  312. catch (Exception ex)
  313. {
  314. lock (Errors)
  315. Errors.Add(new Tuple<ParserError, string>(new ParserError($"Internal post-process lexer error [{ex}]", new Marker()), $"{kv.Key.Filename} - {kv.Key.Name}"));
  316. }
  317. #if DEBUG
  318. }
  319. #else
  320. });
  321. #endif
  322. long fileSize = 0;
  323. int fileCount = 0;
  324. foreach (var file in Directory.EnumerateFiles(InputDirectory, "*.erb", SearchOption.AllDirectories))
  325. {
  326. var info = new FileInfo(file);
  327. fileSize += info.Length;
  328. fileCount++;
  329. }
  330. var htmlWriter = new HTMLWriter
  331. {
  332. Errors = Errors.OrderBy(x => x.Item2).ToList(),
  333. Warnings = Warnings.OrderBy(x => x.Item2).ToList(),
  334. FunctionCount = DeclaredFunctions.Count,
  335. TotalFileSize = fileSize,
  336. TotalFileCount = fileCount
  337. };
  338. using (var stream = File.Create(Path.Combine(outputDirectory, "report.html")))
  339. htmlWriter.WriteReportToFile(stream);
  340. }
  341. }
  342. }