Compiler.cs 14 KB

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