Compiler.cs 11 KB

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