Compiler.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  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. ["GOTJUEL"] = "",
  129. };
  130. foreach (char c in "ABCDEFGHIJKLMNOPQRSTUVWXYZ")
  131. globals[c.ToString()] = "";
  132. var funcs = new List<FunctionDefinition>
  133. {
  134. new FunctionDefinition("RAND", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  135. new FunctionDefinition("TOSTR", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0], "") }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  136. new FunctionDefinition("STRCOUNT", new[] { new FunctionParameter("input", new string[0]), new FunctionParameter("match", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  137. new FunctionDefinition("MIN", new[] { new FunctionParameter("a", new string[0], isArrayParameter: true), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  138. new FunctionDefinition("MAX", new[] { new FunctionParameter("a", new string[0], isArrayParameter: true), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  139. new FunctionDefinition("POWER", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  140. new FunctionDefinition("GETPALAMLV", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  141. new FunctionDefinition("GETBIT", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  142. new FunctionDefinition("UNICODE", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  143. 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()),
  144. 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()),
  145. new FunctionDefinition("HE_SHE", new[] { new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  146. 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()),
  147. new FunctionDefinition("STRLENS", 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 FunctionVariable[0], true, "_GLOBAL", new Marker()),
  149. new FunctionDefinition("CSVNAME", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  150. new FunctionDefinition("CSVTALENT", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  151. new FunctionDefinition("CSVCSTR", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  152. new FunctionDefinition("GETNUM", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  153. 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()),
  154. 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()),
  155. new FunctionDefinition("GETTIME", new FunctionParameter[0], new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  156. new FunctionDefinition("SAVENOS", new FunctionParameter[0], new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  157. new FunctionDefinition("GETCOLOR", new FunctionParameter[0], new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  158. new FunctionDefinition("CURRENTREDRAW", new FunctionParameter[0], new FunctionVariable[0], true, "_GLOBAL", new Marker()),
  159. };
  160. var stringStatements = new List<string>
  161. {
  162. "DRAWLINEFORM",
  163. "PRINTFORML",
  164. "PRINT",
  165. "PRINTW",
  166. "PRINTV",
  167. "PRINTL",
  168. "PRINTLC",
  169. "PRINTC",
  170. "ALIGNMENT",
  171. "CALL",
  172. "CUSTOMDRAWLINE",
  173. "DATAFORM",
  174. "GOTO",
  175. "PRINTFORMDL",
  176. "PRINTFORMW",
  177. "PRINTFORMDW",
  178. "PRINTFORMC",
  179. "PRINTFORMLC",
  180. "PRINTFORM",
  181. "PRINTPLAINFORM",
  182. "DEBUGPRINTL",
  183. "DEBUGPRINTFORML",
  184. "REUSELASTLINE",
  185. "PRINTPLAIN",
  186. "PRINT_TRAIN_NAME",
  187. "PRINT_STR",
  188. };
  189. string csvPath = Path.Combine(InputDirectory, "CSV");
  190. string erbPath = Path.Combine(InputDirectory, "ERB");
  191. var csvDefinition = new CSVDefinition();
  192. foreach (var file in Directory.EnumerateFiles(csvPath, "*.csv", SearchOption.AllDirectories))
  193. {
  194. string path = Path.GetFileNameWithoutExtension(file);
  195. if (path.EndsWith("_TR"))
  196. continue;
  197. Preprocessor.ProcessCSV(csvDefinition, path, File.ReadLines(file, Encoding.UTF8));
  198. }
  199. ConcurrentDictionary<FunctionDefinition, string> preprocessedFunctions = new ConcurrentDictionary<FunctionDefinition, string>();
  200. #if DEBUG
  201. foreach (var file in Directory.EnumerateFiles(erbPath, "*.erb", SearchOption.AllDirectories))
  202. #else
  203. Parallel.ForEach(Directory.EnumerateFiles(erbPath, "*.erb", SearchOption.AllDirectories), new ParallelOptions
  204. {
  205. MaxDegreeOfParallelism = 4
  206. }, file =>
  207. #endif
  208. {
  209. try
  210. {
  211. foreach (var kv in Preprocessor.PreprocessFile(File.ReadAllText(file), Path.GetFileName(file)))
  212. preprocessedFunctions[kv.Key] = kv.Value;
  213. }
  214. catch (Exception ex)
  215. {
  216. lock (Errors)
  217. Errors.Add(new Tuple<ParserError, string>(new ParserError($"Internal pre-process lexer error [{ex}]", new Marker()), Path.GetFileName(file)));
  218. }
  219. #if DEBUG
  220. }
  221. #else
  222. });
  223. #endif
  224. DeclaredFunctions = preprocessedFunctions.ToDictionary(kv => kv.Key, kv => kv.Value);
  225. funcs.AddRange(DeclaredFunctions.Keys.Where(x => x.IsReturnFunction));
  226. var procedures = DeclaredFunctions.Keys.Where(x => !x.IsReturnFunction).ToList();
  227. #if DEBUG
  228. foreach (var kv in DeclaredFunctions)
  229. #else
  230. Parallel.ForEach(DeclaredFunctions, new ParallelOptions
  231. {
  232. MaxDegreeOfParallelism = 4
  233. }, kv =>
  234. #endif
  235. {
  236. try
  237. {
  238. var locals = new VariableDictionary();
  239. foreach (var param in kv.Key.Variables)
  240. locals[param.Name] = param.DefaultValue ?? param.ValueType == Interpreter.ValueType.String ? new Value("") : new Value(0d);
  241. Parser parser = new Parser(kv.Value, kv.Key, funcs, procedures, globals, locals, stringStatements, csvDefinition);
  242. var nodes = parser.Parse(out var localErrors);
  243. lock (Errors)
  244. Errors.AddRange(localErrors.Select(x => new Tuple<ParserError, string>(x, $"{kv.Key.Filename} - {kv.Key.Name}")));
  245. }
  246. catch (Exception ex)
  247. {
  248. lock (Errors)
  249. Errors.Add(new Tuple<ParserError, string>(new ParserError($"Internal post-process lexer error [{ex}]", new Marker()), $"{kv.Key.Filename} - {kv.Key.Name}"));
  250. }
  251. #if DEBUG
  252. }
  253. #else
  254. });
  255. #endif
  256. long fileSize = 0;
  257. int fileCount = 0;
  258. foreach (var file in Directory.EnumerateFiles(InputDirectory, "*.erb", SearchOption.AllDirectories))
  259. {
  260. var info = new FileInfo(file);
  261. fileSize += info.Length;
  262. fileCount++;
  263. }
  264. var htmlWriter = new HTMLWriter
  265. {
  266. Errors = Errors.OrderBy(x => x.Item2).ToList(),
  267. FunctionCount = DeclaredFunctions.Count,
  268. TotalFileSize = fileSize,
  269. TotalFileCount = fileCount
  270. };
  271. using (var stream = File.Create(Path.Combine(outputDirectory, "report.html")))
  272. htmlWriter.WriteReportToFile(stream);
  273. }
  274. }
  275. }