Autodoc.Mod 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. MODULE Autodoc;
  2. IMPORT Files, Texts, Out, Args, Strings, Platform, Dir,
  3. P := AutodocParser, H := AutodocHtml;
  4. CONST
  5. version = '1.0.0-alpha.1';
  6. year = 2023;
  7. delim = Platform.PathDelimiter;
  8. TYPE
  9. Module* = P.Module;
  10. VAR
  11. indexTitle: ARRAY 256 OF CHAR;
  12. indexComment: P.LongStr;
  13. PROCEDURE OpenFile(fname: ARRAY OF CHAR; VAR r: Files.Rider): BOOLEAN;
  14. VAR F: Files.File;
  15. BEGIN F := Files.Old(fname);
  16. IF F # NIL THEN Files.Set(r, F, 0) END
  17. RETURN F # NIL END OpenFile;
  18. PROCEDURE HandleFile*(in, out: ARRAY OF CHAR);
  19. VAR err: ARRAY 1024 OF CHAR;
  20. r: Files.Rider;
  21. module: Module;
  22. BEGIN
  23. IF OpenFile(in, r) THEN
  24. P.SetFname(in);
  25. module := P.ParseModule(r, err);
  26. IF module # NIL THEN
  27. IF indexTitle[0] = 0X THEN Strings.Copy(module.name, indexTitle) END;
  28. IF indexComment[0] = 0X THEN
  29. Strings.Copy(module.comment, indexComment)
  30. END;
  31. IF H.Save(module, out) THEN
  32. Out.String('## Created "'); Out.String(out);
  33. Out.String('".'); Out.Ln
  34. ELSE
  35. Out.String('## Error saving file "'); Out.String(out);
  36. Out.String('".'); Out.Ln
  37. END
  38. ELSE
  39. Out.String('## Error parsing file "');
  40. Out.String(in); Out.String('".'); Out.Ln
  41. END
  42. ELSE
  43. Out.String('## Error: Could not open file "');
  44. Out.String(in); Out.String('".'); Out.Ln
  45. END
  46. END HandleFile;
  47. (** Gets file name without path or extension.
  48. Example: 'a/b/c.txt' -> 'c' *)
  49. PROCEDURE GetBaseName(s: ARRAY OF CHAR; VAR name: ARRAY OF CHAR);
  50. VAR i, j, len: INTEGER;
  51. BEGIN
  52. (* len := Length of s *)
  53. len := 0; WHILE s[len] # 0X DO INC(len) END;
  54. (* j := position of last '.' in s, or -1 *)
  55. j := len - 1; WHILE (j # -1) & (s[j] # '.') DO DEC(j) END;
  56. (* i := position of last slash in s, or 0 *)
  57. i := len; WHILE (i # -1) & (s[i] # '/') & (s[i] # '\') DO DEC(i) END;
  58. INC(i);
  59. IF j < i THEN j := len END;
  60. Strings.Extract(s, i, j - i, name)
  61. END GetBaseName;
  62. PROCEDURE HandleFileToDir*(in, dir: ARRAY OF CHAR);
  63. VAR name, out: ARRAY 512 OF CHAR;
  64. len: INTEGER;
  65. BEGIN
  66. out := dir;
  67. len := Strings.Length(out);
  68. IF (len # 0) & (out[len - 1] # '/') & (out[len - 1] # '\') THEN
  69. Strings.Append(delim, out)
  70. END;
  71. GetBaseName(in, name);
  72. Strings.Append(name, out);
  73. Strings.Append('.html', out);
  74. HandleFile(in, out)
  75. END HandleFileToDir;
  76. PROCEDURE Usage;
  77. VAR s: ARRAY 256 OF CHAR;
  78. BEGIN
  79. Out.String('Free Oberon AutoDoc tool version ');
  80. Out.String(version); Out.Ln;
  81. Out.String('Copyright (c) 2022-'); Out.Int(year, 0);
  82. Out.String(' by Arthur Yefimov and others.'); Out.Ln;
  83. Out.String('Usage:'); Out.Ln; Args.Get(0, s);
  84. Out.String(' '); Out.String(s);
  85. Out.String(' {"-o" outputPath | "--lang" code | otherParam | sourceFile}');
  86. Out.Ln; Out.Ln;
  87. Out.String('outputPath may be a file name or a directory. It must be');
  88. Out.Ln;
  89. Out.String(' a directory if several source files are given.');
  90. Out.Ln;
  91. Out.String('Other parameters:'); Out.Ln;
  92. Out.String('-a ....... Get all objects, not only exported'); Out.Ln;
  93. Out.String('-k ....... Keep module import aliases'); Out.Ln;
  94. Out.String('--external-style or'); Out.Ln;
  95. Out.String('-e ....... Do not put CSS in HTML, link with style.css'); Out.Ln;
  96. Out.String('-L ....... Add a directory with modules to link to in HTML.');
  97. Out.Ln;
  98. Out.String(' Put extension in the end to change it: Program/.cp');
  99. Out.Ln;
  100. Out.String('--intro <intro> .......... Supply an introduction text file');
  101. Out.Ln;
  102. Out.String('--title or'); Out.Ln;
  103. Out.String('-t <title> ............... Supply the index page title'); Out.Ln;
  104. Out.String('--pal <pal> .............. Supply a named palette'); Out.Ln;
  105. Out.String(' <pal> is one of: default, bw, horror'); Out.Ln;
  106. Out.String('--template <filename> .... Use <filename> as HTML-file'); Out.Ln;
  107. Out.String(' with placeholders: %TITLE% %HEADING% %MENU% %BODY% %DATE%');
  108. Out.Ln;
  109. Out.String('--debug .................. Produce debug output in console');
  110. Out.Ln; Out.Ln; Out.Ln;
  111. Out.String('Examples:'); Out.Ln;
  112. Out.String(' '); Out.String(s);
  113. Out.String(' -o Apples.Mod'); Out.Ln;
  114. Out.String(' '); Out.String(s);
  115. Out.String(' -o doc.html --lang ru Apples.Mod'); Out.Ln;
  116. Out.String(' '); Out.String(s);
  117. Out.String(' -o docs/ Fruits.Mod Apples.Mod'); Out.Ln
  118. END Usage;
  119. (** Changes extension of the file to '.html'. Puts result is out. *)
  120. PROCEDURE FnameToHtml(fname: ARRAY OF CHAR; VAR out: ARRAY OF CHAR);
  121. VAR i: INTEGER;
  122. BEGIN
  123. i := Strings.Length(fname);
  124. IF i # 0 THEN
  125. (* Find last '.' in fname *)
  126. DEC(i); WHILE (i # -1) & (fname[i] # '.') DO DEC(i) END;
  127. IF i # -1 THEN Strings.Extract(fname, 0, i, out)
  128. ELSE Strings.Copy(fname, out)
  129. END;
  130. Strings.Append('.html', out)
  131. ELSE
  132. out[0] := 0X
  133. END
  134. END FnameToHtml;
  135. (** Returns TRUE if s ends with with. *)
  136. PROCEDURE EndsWith(s, with: ARRAY OF CHAR): BOOLEAN;
  137. VAR i, j: INTEGER;
  138. BEGIN
  139. i := 0; WHILE s[i] # 0X DO INC(i) END;
  140. j := 0; WHILE with[j] # 0X DO INC(j) END;
  141. IF i >= j THEN
  142. REPEAT DEC(i); DEC(j) UNTIL (j = -1) OR (s[i] # with[j])
  143. END
  144. RETURN j = -1 END EndsWith;
  145. (** s can be in form of 'Dir', 'Dir/' or 'Dir/*.cp' *)
  146. PROCEDURE ParseLinkDir(s: ARRAY OF CHAR);
  147. VAR r: Dir.Rec;
  148. z: ARRAY 256 OF CHAR; (* Copy of s *)
  149. ext: ARRAY 32 OF CHAR; (* Custom file extension, i.e. '.Mod' *)
  150. extLen, i: INTEGER;
  151. ok: BOOLEAN;
  152. BEGIN Strings.Copy(s, z); ok := TRUE; ext := '.Mod';
  153. i := 0; WHILE (z[i] # 0X) & ~((z[i] = '/') & (z[i + 1] = '.')) DO INC(i) END;
  154. IF z[i] # 0X THEN
  155. IF z[i + 2] # 0X THEN
  156. Strings.Extract(z, i + 1, LEN(ext), ext);
  157. z[i] := 0X
  158. END
  159. END;
  160. extLen := Strings.Length(ext);
  161. IF Dir.IsDir(z) THEN
  162. Dir.First(r, z);
  163. WHILE ~r.eod DO
  164. IF (r.name[0] # '.') & EndsWith(r.name, ext) THEN
  165. Strings.Copy(r.name, z);
  166. z[Strings.Length(z) - extLen] := 0X;
  167. H.AddLinkMod(z)
  168. END;
  169. Dir.Next(r)
  170. END
  171. END
  172. END ParseLinkDir;
  173. PROCEDURE CreateIndex*(title, comment, out: ARRAY OF CHAR): BOOLEAN;
  174. VAR len: INTEGER;
  175. s: ARRAY 4096 OF CHAR;
  176. ok: BOOLEAN;
  177. BEGIN
  178. Strings.Copy(out, s);
  179. len := Strings.Length(s);
  180. IF (len # 0) & (s[len - 1] # '/') & (s[len - 1] # '\') THEN
  181. Strings.Append(delim, s)
  182. END;
  183. Strings.Append('index.html', s);
  184. IF H.CreateIndex(title, comment, s) THEN
  185. ok := TRUE;
  186. Out.String('## Created "'); Out.String(s); Out.String('".'); Out.Ln
  187. ELSE ok := FALSE
  188. END
  189. RETURN ok END CreateIndex;
  190. PROCEDURE AddFname(VAR m: ARRAY OF ARRAY OF CHAR; VAR len: INTEGER;
  191. s: ARRAY OF CHAR);
  192. VAR i: INTEGER;
  193. BEGIN
  194. i := 0; (* Search for duplicate *)
  195. WHILE (i # len) & (m[i] # s) DO INC(i) END;
  196. IF i = len THEN Strings.Copy(s, m[i]); INC(len) END
  197. END AddFname;
  198. PROCEDURE SetIntro(fname: ARRAY OF CHAR);
  199. VAR T: Texts.Text;
  200. R: Texts.Reader;
  201. ch: CHAR;
  202. i: INTEGER;
  203. BEGIN
  204. NEW(T); Texts.Open(T, fname);
  205. Texts.OpenReader(R, T, 0);
  206. Texts.Read(R, ch);
  207. i := 0;
  208. WHILE ~R.eot DO
  209. IF i < LEN(indexComment) - 1 THEN indexComment[i] := ch; INC(i) END;
  210. Texts.Read(R, ch)
  211. END;
  212. indexComment[i] := 0X
  213. END SetIntro;
  214. PROCEDURE Do;
  215. VAR i, count, len: INTEGER;
  216. out, s: ARRAY 256 OF CHAR;
  217. fnames: ARRAY 64, 256 OF CHAR;
  218. fnameCount: INTEGER;
  219. createIndex: BOOLEAN;
  220. BEGIN
  221. count := Args.Count();
  222. IF count = 0 THEN
  223. Usage
  224. ELSE
  225. out[0] := 0X; i := 1; fnameCount := 0;
  226. indexTitle[0] := 0X; indexComment[0] := 0X;
  227. H.ClearLinkMods; createIndex := TRUE;
  228. WHILE i <= count DO
  229. Args.Get(i, s);
  230. IF s = '-o' THEN (* Output file or dir *)
  231. IF i < count THEN INC(i); Args.Get(i, out) END
  232. ELSIF s = '-L' THEN (* Link directory *)
  233. IF i < count THEN INC(i); Args.Get(i, s); ParseLinkDir(s) END
  234. ELSIF s = '-a' THEN (* All *)
  235. P.SetExportedOnly(FALSE)
  236. ELSIF s = '-k' THEN (* Keep module aliases *)
  237. P.SetKeepAliases(TRUE)
  238. ELSIF s = '--debug' THEN
  239. P.SetDebug(TRUE)
  240. ELSIF (s = '--external-style') OR (s = '-e') THEN
  241. H.SetExternalStyle(TRUE)
  242. ELSIF (s = '--no-index') OR (s = '-n') THEN
  243. createIndex := FALSE;
  244. H.LinkToIndex(FALSE)
  245. ELSIF (s = '--title') OR (s = '-t') THEN
  246. IF i < count THEN INC(i); Args.Get(i, indexTitle); END
  247. ELSIF (s = '--intro') OR (s = '-i') THEN
  248. IF i < count THEN INC(i); Args.Get(i, s); SetIntro(s) END
  249. ELSIF s = '--pal' THEN
  250. IF i < count THEN INC(i); Args.Get(i, s); H.SetPalette(s) END
  251. ELSIF s = '--template' THEN (* Template HTML file *)
  252. IF i < count THEN INC(i); Args.Get(i, s); H.SetTemplate(s) END
  253. ELSIF s = '--lang' THEN (* Output language *)
  254. IF i < count THEN
  255. INC(i); Args.Get(i, s); P.SetLang(s); H.SetLang(s)
  256. END
  257. ELSIF fnameCount < LEN(fnames) THEN (* One of the module file names *)
  258. AddFname(fnames, fnameCount, s); H.AddLinkModExt(s)
  259. END;
  260. INC(i)
  261. END;
  262. len := Strings.Length(out);
  263. IF fnameCount = 0 THEN
  264. Out.String('No files supplied.'); Out.Ln
  265. ELSIF (fnameCount > 1) OR
  266. (len # 0) & ((out[len - 1] = '/') OR (out[len - 1] = '\')) OR
  267. Dir.IsDir(out)
  268. THEN (* treat "-o" parameter as directory *)
  269. IF Dir.IsDir(out) THEN
  270. FOR i := 0 TO fnameCount - 1 DO
  271. HandleFileToDir(fnames[i], out)
  272. END;
  273. IF createIndex & ~CreateIndex(indexTitle, indexComment, out) THEN
  274. Out.String('Could not create an index file.'); Out.Ln
  275. END
  276. ELSE
  277. Out.String('Directory does not exist: "'); Out.String(out);
  278. Out.String('".'); Out.Ln
  279. END
  280. ELSE (* Single file given and "-o" is a file name *)
  281. IF out[0] = 0X THEN FnameToHtml(fnames[0], out) END;
  282. H.LinkToIndex(FALSE);
  283. HandleFile(fnames[0], out)
  284. END
  285. END
  286. END Do;
  287. BEGIN
  288. Do
  289. END Autodoc.