You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

DescriptionGenerator.cs 9.6 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. using Microsoft.CodeAnalysis.CSharp;
  2. using Protobuf.Text;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Reflection.Metadata.Ecma335;
  7. using System.Text;
  8. using System.Text.RegularExpressions;
  9. using System.Threading.Tasks;
  10. namespace Tensorflow.CodeGen
  11. {
  12. public class DescriptionGenerator
  13. {
  14. private static readonly string replaceStrInner = "~~%~~";
  15. private static readonly string replaceStrInnerQuotationMarks = "^%^";
  16. Dictionary<string, Dictionary<string, string>> _opDescriptions = new Dictionary<string, Dictionary<string, string>>();
  17. Dictionary<string, OpDef> _opDescriptionDefs = new Dictionary<string, OpDef>();
  18. public DescriptionGenerator(string apiDefDirectory)
  19. {
  20. DirectoryInfo directory = new DirectoryInfo(apiDefDirectory);
  21. int errors = 0;
  22. foreach (FileInfo file in directory.GetFiles())
  23. {
  24. string target = file.Name.Split('.')[0].Split('_').Last();
  25. OpDef op = null;
  26. try
  27. {
  28. op = ReadOpDefs(file.FullName).Op[0];
  29. }
  30. catch
  31. {
  32. errors++;
  33. continue;
  34. }
  35. _opDescriptionDefs[target] = op;
  36. _opDescriptions[target] = new Dictionary<string, string>();
  37. foreach (var arg in op.InputArg)
  38. {
  39. string argName = arg.Name;
  40. var token = SyntaxFactory.ParseToken(argName);
  41. if (token.IsKeyword())
  42. {
  43. argName = $"{argName}_";
  44. }
  45. _opDescriptions[target][argName] = arg.Description ?? "";
  46. }
  47. foreach (var arg in op.Attr)
  48. {
  49. var token = SyntaxFactory.ParseToken(arg.Name);
  50. string realKey = arg.Name;
  51. if (token.IsKeyword())
  52. {
  53. realKey += "_";
  54. }
  55. _opDescriptions[target][realKey] = arg.Description ?? "";
  56. }
  57. _opDescriptions[target]["SUMMARY"] = op.Summary ?? "";
  58. _opDescriptions[target]["DESC"] = op.Description ?? "";
  59. }
  60. Console.WriteLine($"Warning: {errors} description files cannot be analyzed! Please revise it if " +
  61. $"the failed files number is large, or ignore it.");
  62. }
  63. /// <summary>
  64. ///
  65. /// </summary>
  66. /// <param name="op"></param>
  67. /// <param name="sb"></param>
  68. public void AppendDescription(OpDef fullOp, StringBuilder sb)
  69. {
  70. var opName = fullOp.Name;
  71. if(_opDescriptions.TryGetValue(opName, out var op))
  72. {
  73. var def = _opDescriptionDefs[opName];
  74. sb.AppendLine("/// <summary>");
  75. sb.AppendLine($"/// {op["SUMMARY"]}");
  76. sb.AppendLine("/// </summary>");
  77. string totalDesc = op["DESC"];
  78. if (!string.IsNullOrEmpty(totalDesc))
  79. {
  80. totalDesc = totalDesc.Replace(replaceStrInnerQuotationMarks, "\"");
  81. sb.AppendLine("/// <remarks>");
  82. string[] lines = totalDesc.Split(replaceStrInner);
  83. foreach (var line in lines)
  84. {
  85. sb.AppendLine($"/// {line}");
  86. }
  87. sb.AppendLine("/// </remarks>");
  88. }
  89. var argNames = GetInputArgNames(fullOp);
  90. foreach (var argName in argNames)
  91. {
  92. if(op.TryGetValue(argName, out var desc))
  93. {
  94. desc = desc.Replace(replaceStrInnerQuotationMarks, "\"");
  95. string[] lines = desc.Split(replaceStrInner);
  96. sb.AppendLine($"/// <param name=\"{argName}\">");
  97. foreach (var line in lines)
  98. {
  99. sb.AppendLine($"/// {line}");
  100. }
  101. sb.AppendLine("/// </param>");
  102. }
  103. else
  104. {
  105. sb.AppendLine($"/// <param name=\"{argName}\"></param>");
  106. }
  107. }
  108. List<string> returnValueDescs = new();
  109. foreach (var arg in def.OutputArg)
  110. {
  111. if (!string.IsNullOrEmpty(arg.Description))
  112. {
  113. returnValueDescs.Add($"{arg.Name}: {arg.Description}");
  114. }
  115. }
  116. string returnValueDesc = "";
  117. if (returnValueDescs.Count > 0)
  118. {
  119. returnValueDesc = string.Join(" && ", returnValueDescs);
  120. }
  121. sb.AppendLine($"/// <returns>{returnValueDesc}</returns>");
  122. }
  123. else
  124. {
  125. sb.AppendLine("/// <summary>");
  126. sb.AppendLine($"///");
  127. sb.AppendLine("/// </summary>");
  128. var argNames = GetInputArgNames(fullOp);
  129. foreach (var argName in argNames)
  130. {
  131. sb.AppendLine($"/// <param name=\"{argName}\"></param>");
  132. }
  133. sb.AppendLine($"/// <returns></returns>");
  134. }
  135. }
  136. /// <summary>
  137. ///
  138. /// </summary>
  139. /// <param name="op">
  140. /// </param>
  141. /// <returns></returns>
  142. /// <remarks></remarks>
  143. public List<string> GetInputArgNames(OpDef op)
  144. {
  145. List<string> names = new();
  146. foreach (var arg in op.InputArg)
  147. {
  148. string argName = arg.Name;
  149. var token = SyntaxFactory.ParseToken(argName);
  150. if (token.IsKeyword())
  151. {
  152. argName = $"{argName}_";
  153. }
  154. names.Add(argName);
  155. }
  156. var attrValueDic = Utils.GetAttrsDefaultValue(op, out var dynamicDefaultValues);
  157. foreach (var (key, typeStr, value) in attrValueDic)
  158. {
  159. var token = SyntaxFactory.ParseToken(key);
  160. string realKey = key;
  161. if (token.IsKeyword())
  162. {
  163. realKey += "_";
  164. }
  165. names.Add(realKey);
  166. }
  167. return names;
  168. }
  169. private static OpList ReadOpDefs(string path)
  170. {
  171. var text = File.ReadAllText(path);
  172. text = RemoveLintTags(text);
  173. text = PreProcessText(text);
  174. string pattern = @"<<END([\s\S]*?)END";
  175. // 定义用于替换的字符串
  176. string replaceStrPrefix = "\"";
  177. string replaceStrSuffix = "\"";
  178. // 将匹配到的文本段全部替换
  179. string replacedText = Regex.Replace(text, pattern, match => {
  180. string matchedText = match.Value;
  181. string innerText = match.Groups[1].Value;
  182. innerText = innerText.Replace("\"", replaceStrInnerQuotationMarks)
  183. .Replace("\r\n", replaceStrInner).Replace("\n", replaceStrInner); // 替换内部换行符
  184. return replaceStrPrefix + innerText + replaceStrSuffix; // 替换首尾
  185. }, RegexOptions.Multiline);
  186. var opDefs = new TextParser(TextParser.Settings.Default.WithIgnoreUnknownFields(true)).Parse<OpList>(replacedText);
  187. return opDefs;
  188. }
  189. static string PreProcessText(string input)
  190. {
  191. int depth = 0;
  192. int endBlockDepth = -1;
  193. StringBuilder sb = new StringBuilder();
  194. for (int i = 0; i < input.Length; i++)
  195. {
  196. char c = input[i];
  197. if (c == '{')
  198. {
  199. depth++;
  200. sb.Append(c);
  201. }
  202. else if (c == '}')
  203. {
  204. if (depth == endBlockDepth)
  205. {
  206. sb.Append("END\n");
  207. endBlockDepth = -1;
  208. }
  209. sb.Append(c);
  210. depth--;
  211. }
  212. else if (c == '<' && i + 5 < input.Length && input.Substring(i, 5) == "<<END")
  213. {
  214. endBlockDepth = depth;
  215. sb.Append("<<END");
  216. i += 4;
  217. }
  218. else if (c == 'E' && i + 3 < input.Length && input.Substring(i, 3) == "END")
  219. {
  220. endBlockDepth = -1;
  221. sb.Append("END");
  222. i += 2;
  223. }
  224. else
  225. {
  226. sb.Append(c);
  227. }
  228. }
  229. string output = sb.ToString();
  230. return output;
  231. }
  232. static string RemoveLintTags(string input)
  233. {
  234. string[] lines = input.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None);
  235. StringBuilder sb = new StringBuilder();
  236. foreach (string line in lines)
  237. {
  238. if (!line.TrimStart().StartsWith("# LINT"))
  239. {
  240. sb.AppendLine(line);
  241. }
  242. }
  243. return sb.ToString().TrimEnd();
  244. }
  245. }
  246. }