Compiler.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  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("PRINT", true),
  208. new Keyword("PRINTW", true),
  209. new Keyword("PRINTV", true),
  210. new Keyword("PRINTL", true),
  211. new Keyword("PRINTLC", true),
  212. new Keyword("PRINTC", true),
  213. new Keyword("ALIGNMENT", true),
  214. new Keyword("CALL", true, true),
  215. new Keyword("CUSTOMDRAWLINE", true),
  216. new Keyword("GOTO", true),
  217. new Keyword("DEBUGPRINTL", true),
  218. new Keyword("REUSELASTLINE", true),
  219. new Keyword("PRINTPLAIN", true),
  220. new Keyword("PRINT_TRAIN_NAME", true),
  221. new Keyword("PRINT_STR", true),
  222. };
  223. string csvPath = Path.Combine(InputDirectory, "CSV");
  224. string erbPath = Path.Combine(InputDirectory, "ERB");
  225. var csvDefinition = new CSVDefinition();
  226. foreach (var file in Directory.EnumerateFiles(csvPath, "*.csv", SearchOption.AllDirectories))
  227. {
  228. string path = Path.GetFileNameWithoutExtension(file);
  229. if (path.EndsWith("_TR"))
  230. continue;
  231. Preprocessor.ProcessCSV(csvDefinition, path, File.ReadLines(file, Encoding.UTF8));
  232. }
  233. ConcurrentDictionary<FunctionDefinition, string> preprocessedFunctions = new ConcurrentDictionary<FunctionDefinition, string>();
  234. #if DEBUG
  235. foreach (var file in Directory.EnumerateFiles(erbPath, "*.erb", SearchOption.AllDirectories))
  236. #else
  237. Parallel.ForEach(Directory.EnumerateFiles(erbPath, "*.erb", SearchOption.AllDirectories), new ParallelOptions
  238. {
  239. MaxDegreeOfParallelism = 8
  240. }, file =>
  241. #endif
  242. {
  243. try
  244. {
  245. foreach (var kv in Preprocessor.PreprocessFile(File.ReadAllText(file), Path.GetFileName(file)))
  246. preprocessedFunctions[kv.Key] = kv.Value;
  247. }
  248. catch (Exception ex)
  249. {
  250. lock (Errors)
  251. Errors.Add(new Tuple<ParserError, string>(new ParserError($"Internal pre-process lexer error [{ex}]", new Marker()), Path.GetFileName(file)));
  252. }
  253. #if DEBUG
  254. }
  255. #else
  256. });
  257. #endif
  258. DeclaredFunctions = preprocessedFunctions.ToDictionary(kv => kv.Key, kv => kv.Value);
  259. funcs.AddRange(DeclaredFunctions.Keys.Where(x => x.IsReturnFunction));
  260. var procedures = DeclaredFunctions.Keys.Where(x => !x.IsReturnFunction).ToList();
  261. #if DEBUG
  262. foreach (var kv in DeclaredFunctions)
  263. #else
  264. Parallel.ForEach(DeclaredFunctions, new ParallelOptions
  265. {
  266. MaxDegreeOfParallelism = 8
  267. }, kv =>
  268. #endif
  269. {
  270. try
  271. {
  272. var locals = new VariableDictionary();
  273. foreach (var param in kv.Key.Variables)
  274. locals[param.Name] = param.DefaultValue ?? param.ValueType == Interpreter.ValueType.String ? new Value("") : new Value(0d);
  275. Parser parser = new Parser(kv.Value, kv.Key, funcs, procedures, globals, locals, stringStatements, csvDefinition);
  276. var nodes = parser.Parse(out var localErrors, out var localWarnings);
  277. lock (Errors)
  278. Errors.AddRange(localErrors.Select(x => new Tuple<ParserError, string>(x, $"{kv.Key.Filename} - {kv.Key.Name}")));
  279. lock (Warnings)
  280. Warnings.AddRange(localWarnings.Select(x => new Tuple<ParserError, string>(x, $"{kv.Key.Filename} - {kv.Key.Name}")));
  281. }
  282. catch (Exception ex)
  283. {
  284. lock (Errors)
  285. Errors.Add(new Tuple<ParserError, string>(new ParserError($"Internal post-process lexer error [{ex}]", new Marker()), $"{kv.Key.Filename} - {kv.Key.Name}"));
  286. }
  287. #if DEBUG
  288. }
  289. #else
  290. });
  291. #endif
  292. long fileSize = 0;
  293. int fileCount = 0;
  294. foreach (var file in Directory.EnumerateFiles(InputDirectory, "*.erb", SearchOption.AllDirectories))
  295. {
  296. var info = new FileInfo(file);
  297. fileSize += info.Length;
  298. fileCount++;
  299. }
  300. var htmlWriter = new HTMLWriter
  301. {
  302. Errors = Errors.OrderBy(x => x.Item2).ToList(),
  303. Warnings = Warnings.OrderBy(x => x.Item2).ToList(),
  304. FunctionCount = DeclaredFunctions.Count,
  305. TotalFileSize = fileSize,
  306. TotalFileCount = fileCount
  307. };
  308. using (var stream = File.Create(Path.Combine(outputDirectory, "report.html")))
  309. htmlWriter.WriteReportToFile(stream);
  310. }
  311. }
  312. }