Compiler.cs 13 KB

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