Compiler.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  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. "DOWNBASE",
  34. "TCVAR",
  35. "CDFLAG",
  36. "ITEMPRICE",
  37. "RANDDATA",
  38. "LINECOUNT",
  39. "ISTIMEOUT",
  40. "__INT_MAX__",
  41. "__INT_MIN__",
  42. "RAND",
  43. "CHARANUM",
  44. "TALENT",
  45. "FLAG",
  46. "TFLAG",
  47. "CFLAG",
  48. "MASTER",
  49. "BASE",
  50. "MAXBASE",
  51. "PALAM",
  52. "TEQUIP",
  53. "EQUIP",
  54. "DAY",
  55. "MARK",
  56. "PALAMLV",
  57. "TARGET",
  58. "PLAYER",
  59. "NOWEX",
  60. "EX",
  61. "STAIN",
  62. "EXP",
  63. "ASSIPLAY",
  64. "ASSI",
  65. "ITEM",
  66. "EXPLV",
  67. "TIME",
  68. "MONEY",
  69. "SELECTCOM",
  70. "PREVCOM",
  71. "NEXTCOM",
  72. "DITEMTYPE",
  73. "NO",
  74. "RELATION",
  75. "JUEL",
  76. "GOTJUEL",
  77. };
  78. protected static string[] DefaultGlobalStringVariables =
  79. {
  80. "LOCALS",
  81. "ARGS",
  82. "RESULTS",
  83. "CSTR",
  84. "GLOBALS",
  85. "NICKNAME",
  86. "MASTERNAME",
  87. "NAME",
  88. "TRAINNAME",
  89. "BASENAME",
  90. "EQUIPNAME",
  91. "TEQUIPNAME",
  92. "STAINNAME",
  93. "EXNAME",
  94. "SOURCENAME",
  95. "CALLNAME",
  96. "FLAGNAME",
  97. "TFLAGNAME",
  98. "CFLAGNAME",
  99. "TCVARNAME",
  100. "STRNAME",
  101. "TSTRNAME",
  102. "CSTRNAME",
  103. "SAVESTRNAME",
  104. "CDFLAGNAME",
  105. "GLOBALNAME",
  106. "GLOBALSNAME",
  107. "GAMEBASE_AUTHOR",
  108. "GAMEBASE_INFO",
  109. "GAMEBASE_YEAR",
  110. "GAMEBASE_TITLE",
  111. "GAMEBASE_GAMECODE",
  112. "GAMEBASE_VERSION",
  113. "GAMEBASE_ALLOWVERSION",
  114. "GAMEBASE_DEFAULTCHARA",
  115. "GAMEBASE_NOITEM",
  116. "WINDOW_TITLE",
  117. "MONEYLABEL",
  118. "DRAWLINESTR",
  119. "LASTLOAD_VERSION",
  120. "LASTLOAD_NO",
  121. "LASTLOAD_TEXT",
  122. "SAVEDATA_TEXT",
  123. "TSTR",
  124. "STR",
  125. "SAVESTR",
  126. "ABLNAME",
  127. "MARKNAME",
  128. "TALENTNAME",
  129. "ITEMNAME",
  130. "PALAMNAME",
  131. "EXPNAME",
  132. };
  133. public void Compile(string outputDirectory)
  134. {
  135. var globals = new VariableDictionary();
  136. foreach (string stringVariable in DefaultGlobalStringVariables)
  137. globals.Add(stringVariable, "");
  138. foreach (string numberVariable in DefaultGlobalNumberVariables)
  139. globals.Add(numberVariable, 0);
  140. foreach (char c in "ABCDEFGHIJKLMNOPQRSTUVWXYZ")
  141. globals[c.ToString()] = "";
  142. var funcs = new List<FunctionDefinition>
  143. {
  144. new FunctionDefinition("ABS", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  145. new FunctionDefinition("CSVCALLNAME", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  146. new FunctionDefinition("TOFULL", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  147. new FunctionDefinition("TOINT", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  148. new FunctionDefinition("RAND", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  149. new FunctionDefinition("TOSTR", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0], "") }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  150. new FunctionDefinition("STRCOUNT", new[] { new FunctionParameter("input", new string[0]), new FunctionParameter("match", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  151. new FunctionDefinition("MIN", new[] { new FunctionParameter("a", new string[0], isArrayParameter: true), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  152. new FunctionDefinition("MAX", new[] { new FunctionParameter("a", new string[0], isArrayParameter: true), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  153. new FunctionDefinition("POWER", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  154. new FunctionDefinition("GETPALAMLV", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  155. new FunctionDefinition("GETBIT", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  156. new FunctionDefinition("UNICODE", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  157. 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()),
  158. 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()),
  159. new FunctionDefinition("HE_SHE", new[] { new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  160. 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()),
  161. new FunctionDefinition("STRLENS", new[] { new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  162. new FunctionDefinition("CSVNAME", new[] { new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  163. new FunctionDefinition("CSVNAME", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  164. new FunctionDefinition("CSVTALENT", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  165. new FunctionDefinition("CSVCSTR", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  166. new FunctionDefinition("CSVEXP", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  167. new FunctionDefinition("CSVABL", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  168. new FunctionDefinition("GETNUM", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  169. 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()),
  170. 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()),
  171. 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()),
  172. new FunctionDefinition("GROUPMATCH", 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("GETTIME", new FunctionParameter[0], new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  174. new FunctionDefinition("SAVENOS", new FunctionParameter[0], new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  175. new FunctionDefinition("GETCOLOR", new FunctionParameter[0], new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  176. new FunctionDefinition("CURRENTREDRAW", new FunctionParameter[0], new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  177. new FunctionDefinition("GETFONT", new FunctionParameter[0], new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  178. new FunctionDefinition("GETMILLISECOND", new FunctionParameter[0], new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  179. };
  180. var stringStatements = new List<string>
  181. {
  182. "DRAWLINEFORM",
  183. "PRINTFORML",
  184. "PRINT",
  185. "PRINTW",
  186. "PRINTV",
  187. "PRINTL",
  188. "PRINTLC",
  189. "PRINTC",
  190. "ALIGNMENT",
  191. "CALL",
  192. "CUSTOMDRAWLINE",
  193. "DATAFORM",
  194. "GOTO",
  195. "PRINTFORMDL",
  196. "PRINTFORMW",
  197. "PRINTFORMDW",
  198. "PRINTFORMC",
  199. "PRINTFORMLC",
  200. "PRINTFORM",
  201. "PRINTPLAINFORM",
  202. "DEBUGPRINTL",
  203. "DEBUGPRINTFORML",
  204. "REUSELASTLINE",
  205. "PRINTPLAIN",
  206. "PRINT_TRAIN_NAME",
  207. "PRINT_STR",
  208. };
  209. string csvPath = Path.Combine(InputDirectory, "CSV");
  210. string erbPath = Path.Combine(InputDirectory, "ERB");
  211. var csvDefinition = new CSVDefinition();
  212. foreach (var file in Directory.EnumerateFiles(csvPath, "*.csv", SearchOption.AllDirectories))
  213. {
  214. string path = Path.GetFileNameWithoutExtension(file);
  215. if (path.EndsWith("_TR"))
  216. continue;
  217. Preprocessor.ProcessCSV(csvDefinition, path, File.ReadLines(file, Encoding.UTF8));
  218. }
  219. ConcurrentDictionary<FunctionDefinition, string> preprocessedFunctions = new ConcurrentDictionary<FunctionDefinition, string>();
  220. #if DEBUG
  221. foreach (var file in Directory.EnumerateFiles(erbPath, "*.erb", SearchOption.AllDirectories))
  222. #else
  223. Parallel.ForEach(Directory.EnumerateFiles(erbPath, "*.erb", SearchOption.AllDirectories), new ParallelOptions
  224. {
  225. MaxDegreeOfParallelism = 4
  226. }, file =>
  227. #endif
  228. {
  229. try
  230. {
  231. foreach (var kv in Preprocessor.PreprocessFile(File.ReadAllText(file), Path.GetFileName(file)))
  232. preprocessedFunctions[kv.Key] = kv.Value;
  233. }
  234. catch (Exception ex)
  235. {
  236. lock (Errors)
  237. Errors.Add(new Tuple<ParserError, string>(new ParserError($"Internal pre-process lexer error [{ex}]", new Marker()), Path.GetFileName(file)));
  238. }
  239. #if DEBUG
  240. }
  241. #else
  242. });
  243. #endif
  244. DeclaredFunctions = preprocessedFunctions.ToDictionary(kv => kv.Key, kv => kv.Value);
  245. funcs.AddRange(DeclaredFunctions.Keys.Where(x => x.IsReturnFunction));
  246. var procedures = DeclaredFunctions.Keys.Where(x => !x.IsReturnFunction).ToList();
  247. #if DEBUG
  248. foreach (var kv in DeclaredFunctions)
  249. #else
  250. Parallel.ForEach(DeclaredFunctions, new ParallelOptions
  251. {
  252. MaxDegreeOfParallelism = 4
  253. }, kv =>
  254. #endif
  255. {
  256. try
  257. {
  258. var locals = new VariableDictionary();
  259. foreach (var param in kv.Key.Variables)
  260. locals[param.Name] = param.DefaultValue ?? param.ValueType == Interpreter.ValueType.String ? new Value("") : new Value(0d);
  261. Parser parser = new Parser(kv.Value, kv.Key, funcs, procedures, globals, locals, stringStatements, csvDefinition);
  262. var nodes = parser.Parse(out var localErrors, out var localWarnings);
  263. lock (Errors)
  264. Errors.AddRange(localErrors.Select(x => new Tuple<ParserError, string>(x, $"{kv.Key.Filename} - {kv.Key.Name}")));
  265. lock (Warnings)
  266. Warnings.AddRange(localWarnings.Select(x => new Tuple<ParserError, string>(x, $"{kv.Key.Filename} - {kv.Key.Name}")));
  267. }
  268. catch (Exception ex)
  269. {
  270. lock (Errors)
  271. Errors.Add(new Tuple<ParserError, string>(new ParserError($"Internal post-process lexer error [{ex}]", new Marker()), $"{kv.Key.Filename} - {kv.Key.Name}"));
  272. }
  273. #if DEBUG
  274. }
  275. #else
  276. });
  277. #endif
  278. long fileSize = 0;
  279. int fileCount = 0;
  280. foreach (var file in Directory.EnumerateFiles(InputDirectory, "*.erb", SearchOption.AllDirectories))
  281. {
  282. var info = new FileInfo(file);
  283. fileSize += info.Length;
  284. fileCount++;
  285. }
  286. var htmlWriter = new HTMLWriter
  287. {
  288. Errors = Errors.OrderBy(x => x.Item2).ToList(),
  289. Warnings = Warnings.OrderBy(x => x.Item2).ToList(),
  290. FunctionCount = DeclaredFunctions.Count,
  291. TotalFileSize = fileSize,
  292. TotalFileCount = fileCount
  293. };
  294. using (var stream = File.Create(Path.Combine(outputDirectory, "report.html")))
  295. htmlWriter.WriteReportToFile(stream);
  296. }
  297. }
  298. }