Compiler.cs 11 KB

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