TFXRef.Mod 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976
  1. MODULE TFXRef; (** AUTHOR "thomas.frey@alumni.ethz.ch"; PURPOSE "Generate a cross reference of Modules"; *)
  2. IMPORT
  3. TS := TFTypeSys, TFAOParser, MultiLogger, Streams, Trace, Commands, KernelLog, Kernel,
  4. TextUtilities, Texts, ST := TFScopeTools, S := BimboScanner, Strings, Files, UTF8Strings, TFClasses, Dates,
  5. TFDocGenerator;
  6. CONST
  7. KindNoStart = 0;
  8. KindComment = 1;
  9. KindDeclaration = 2;
  10. KindUse = 3;
  11. TYPE
  12. Range = RECORD
  13. a, b : LONGINT;
  14. kind : LONGINT;
  15. no : TS.NamedObject;
  16. END;
  17. NamedObjectArray = POINTER TO ARRAY OF TS.NamedObject;
  18. LocalExternalUsesSet = OBJECT
  19. VAR nof : LONGINT;
  20. items : NamedObjectArray;
  21. PROCEDURE &Init;
  22. BEGIN
  23. nof := 0;
  24. NEW(items, 1024);
  25. END Init;
  26. PROCEDURE Add(x : TS.NamedObject);
  27. VAR i : LONGINT;
  28. BEGIN
  29. i := 0;
  30. WHILE (i < nof) & (items[i] # x) DO INC(i) END;
  31. IF i < nof THEN RETURN END;
  32. IF nof = LEN(items) THEN Grow END;
  33. items[nof] := x;
  34. INC(nof);
  35. END Add;
  36. PROCEDURE Grow;
  37. VAR temp : NamedObjectArray;
  38. i : LONGINT;
  39. BEGIN
  40. NEW(temp, LEN(items) * 2);
  41. FOR i := 0 TO LEN(items) - 1 DO
  42. temp[i] := items[i]
  43. END;
  44. items := temp
  45. END Grow;
  46. END LocalExternalUsesSet;
  47. StringList = POINTER TO ARRAY OF Strings.String;
  48. GlobalUse = OBJECT
  49. VAR
  50. items : StringList;
  51. nofItems : LONGINT;
  52. PROCEDURE &Init;
  53. BEGIN
  54. NEW(items, 16);
  55. nofItems := 0;
  56. END Init;
  57. PROCEDURE AddFile(CONST filename : ARRAY OF CHAR);
  58. BEGIN
  59. IF nofItems = LEN(items) THEN Grow END;
  60. items[nofItems] := Strings.NewString(filename);
  61. INC(nofItems)
  62. END AddFile;
  63. PROCEDURE Grow;
  64. VAR temp : StringList;
  65. i : LONGINT;
  66. BEGIN
  67. NEW(temp, LEN(items) * 2);
  68. FOR i := 0 TO LEN(items) - 1 DO
  69. temp[i] := items[i]
  70. END;
  71. items := temp
  72. END Grow;
  73. END GlobalUse;
  74. VAR
  75. ml : MultiLogger.LogWindow;
  76. globalUses : TFClasses.StringHashMap;
  77. (* could be a hash, sorted list, priority queue *)
  78. ranges : POINTER TO ARRAY OF Range;
  79. localUses : LocalExternalUsesSet;
  80. currentAuthor : ARRAY 128 OF CHAR;
  81. currentPurpose : ARRAY 4096 OF CHAR;
  82. PROCEDURE MakeRange(from, to, kind : LONGINT; no : TS.NamedObject);
  83. BEGIN
  84. ranges[from].kind := kind;
  85. ranges[from].a := from;
  86. ranges[from].b := to;
  87. ranges[from].no := no;
  88. END MakeRange;
  89. PROCEDURE DumpConst(scope : TS.Scope; c : TS.Const);
  90. BEGIN
  91. CheckExpression(c.expression, scope)
  92. END DumpConst;
  93. PROCEDURE DumpObject(o : TS.Class);
  94. BEGIN
  95. IF o.scope.superQualident # NIL THEN
  96. CheckDesignator(o.scope.superQualident, o.container);
  97. END;
  98. DumpDeclarations(o.scope);
  99. END DumpObject;
  100. PROCEDURE DumpArray(a : TS.Array; scope : TS.Scope);
  101. BEGIN
  102. IF a.expression # NIL THEN CheckExpression(a.expression, scope) END;
  103. DumpType(a.base, scope)
  104. END DumpArray;
  105. PROCEDURE DumpRecord(r : TS.Record);
  106. BEGIN
  107. DumpDeclarations(r.scope);
  108. END DumpRecord;
  109. PROCEDURE DumpProcedure(p : TS.ProcedureType);
  110. BEGIN
  111. END DumpProcedure;
  112. PROCEDURE CheckExpressionList(e : TS.ExpressionList; sig : TS.ProcedureSignature; scope : TS.Scope);
  113. (*VAR i, a, b : LONGINT;(* nr, f : Reference;*)*)
  114. BEGIN
  115. (* i := 0;
  116. f := NIL;*)
  117. WHILE e # NIL DO
  118. CheckExpression(e.expression, scope);
  119. (* IF (sig # NIL) & (sig.params # NIL) THEN
  120. IF i < sig.params.nofObjs THEN
  121. a := -1; b := -1; GetExpressionRange(e.expression, a, b);
  122. IF (a >= 0) & (b > a) THEN
  123. NEW(nr); nr.next := actualParameter; actualParameter := nr; nr.np := -1;
  124. nr.no := sig.params.objs[i];
  125. nr.fp := posKeeper.AddPos(a);
  126. nr.tp := posKeeper.AddPos(b);
  127. IF f # NIL THEN f.np := nr.fp END; f := nr;
  128. END
  129. ELSE
  130. GetExpressionRange(e.expression, a, b);
  131. KernelLog.String("pos = "); KernelLog.Int(a, 0); KernelLog.String(" more parameter than expected ")
  132. END
  133. END;
  134. INC(i);*)
  135. e := e.next
  136. END
  137. END CheckExpressionList;
  138. PROCEDURE CheckExpression(e : TS.Expression; scope : TS.Scope);
  139. VAR t : TS.Type;
  140. sr : TS.SetRange;
  141. BEGIN
  142. IF e = NIL THEN KernelLog.String("Expression is NIL"); RETURN END;
  143. IF e.kind = TS.ExpressionPrimitive THEN
  144. IF e.basicType = TS.BasicSet THEN
  145. sr := e.setValue.setRanges;
  146. WHILE sr # NIL DO
  147. IF sr.a # NIL THEN CheckExpression(sr.a, scope) END;
  148. IF sr.b # NIL THEN CheckExpression(sr.b, scope) END;
  149. sr := sr.next
  150. END;
  151. END;
  152. ELSIF e.kind = TS.ExpressionUnary THEN
  153. CheckExpression(e.a, scope);
  154. ELSIF e.kind = TS.ExpressionBinary THEN
  155. CheckExpression(e.a, scope);
  156. IF e.op # TS.OpIs THEN CheckExpression(e.b, scope)
  157. ELSE
  158. t := ST.FindType(e.b.designator, scope);
  159. CheckDesignator(e.b.designator, scope);
  160. IF t = NIL THEN KernelLog.String("pos = "); KernelLog.Int(e.b.designator(TS.Ident).pos.a, 0); KernelLog.String(" Type not found ") END;
  161. END
  162. ELSIF e.kind = TS.ExpressionDesignator THEN
  163. CheckDesignator(e.designator, scope)
  164. END;
  165. END CheckExpression;
  166. PROCEDURE CheckDesignator(d : TS.Designator; scope : TS.Scope);
  167. VAR no: TS.NamedObject;
  168. curScope : TS.Scope;
  169. type, temptype : TS.Type;
  170. first : BOOLEAN;
  171. s : ARRAY 64 OF CHAR;
  172. m : TS.Module;
  173. te : TS.ExpressionList;
  174. lastpos : LONGINT;
  175. PROCEDURE Check(id : TS.Ident; no : TS.NamedObject);
  176. BEGIN
  177. IF no = NIL THEN RETURN END;
  178. localUses.Add(no);
  179. MakeRange(id.pos.a, id.pos.b, KindUse, no);
  180. END Check;
  181. BEGIN
  182. first := TRUE;
  183. curScope := scope;
  184. WHILE d # NIL DO
  185. IF d IS TS.Ident THEN
  186. lastpos := d(TS.Ident).pos.a;
  187. TS.s.GetString(d(TS.Ident).name, s);
  188. IF first & (s = "SELF") THEN
  189. curScope := scope.parent;
  190. (* look for object or module represented by SELF*)
  191. WHILE (curScope.parent # NIL) & (curScope.owner # NIL) &
  192. ~((curScope.owner IS TS.Class) OR (curScope.owner IS TS.Module)) DO
  193. curScope := curScope.parent
  194. END;
  195. IF curScope = NIL THEN
  196. KernelLog.String("SELF could not be resolved"); KernelLog.Ln;
  197. END;
  198. ELSIF first & (s = "SYSTEM") THEN
  199. d := d.next;
  200. IF d # NIL THEN
  201. IF d IS TS.Ident THEN
  202. TS.s.GetString(d(TS.Ident).name, s);
  203. IF s = "VAL" THEN
  204. d := d.next;
  205. IF d # NIL THEN
  206. IF d IS TS.ActualParameters THEN
  207. te := d(TS.ActualParameters).expressionList;
  208. IF te # NIL THEN
  209. IF te.expression.kind = TS.ExpressionDesignator THEN
  210. temptype := ST.FindType(te.expression.designator, scope);
  211. IF temptype = NIL THEN KernelLog.String("pos = "); KernelLog.Int(te.expression.designator(TS.Ident).pos.a, 0); KernelLog.String(" Type not found ") END;
  212. END;
  213. te := te.next;
  214. CheckExpression(te.expression, scope);
  215. ELSE
  216. KernelLog.String("type arameter expeced"); KernelLog.Ln;
  217. END
  218. ELSE
  219. KernelLog.String("parameters expeced"); KernelLog.Ln;
  220. END
  221. ELSE
  222. KernelLog.String("Pos= "); KernelLog.Int(d(TS.Ident).pos.a, 0); KernelLog.String(s); KernelLog.String("Ident expeced"); KernelLog.Ln;
  223. END
  224. END
  225. ELSE
  226. KernelLog.String(s); KernelLog.String("Ident expeced"); KernelLog.Ln;
  227. END
  228. ELSE
  229. KernelLog.String("Pos= "); KernelLog.Int(d(TS.Ident).pos.a, 0); KernelLog.String(s); KernelLog.String("incomplete SYSTEM call"); KernelLog.Ln;
  230. END
  231. ELSE
  232. IF curScope # NIL THEN
  233. no := curScope.Find(s, first);
  234. IF (no # NIL) & (d.next # NIL) & (d.next IS TS.Dereference) & (no IS TS.ProcDecl) THEN
  235. no.scope.parent.FixSuperScope;
  236. IF no.scope.parent.super # NIL THEN
  237. no := no.scope.parent.super.Find(s, FALSE)
  238. ELSE KernelLog.String(" super is NIL"); KernelLog.String(s); KernelLog.Ln;
  239. END
  240. END;
  241. Check(d(TS.Ident), no);
  242. IF no # NIL THEN
  243. IF no IS TS.Var THEN
  244. type := ST.DealiaseType(no(TS.Var).type);
  245. IF type # NIL THEN
  246. IF type.kind = TS.TRecord THEN curScope := type.record.scope
  247. ELSIF type.kind = TS.TObject THEN curScope := type.object.scope END
  248. END
  249. ELSIF no IS TS.ProcDecl THEN
  250. IF no(TS.ProcDecl).signature # NIL THEN
  251. type := ST.DealiaseType(no(TS.ProcDecl).signature.return);
  252. IF type # NIL THEN
  253. IF type.kind = TS.TRecord THEN curScope := type.record.scope
  254. ELSIF type.kind = TS.TObject THEN curScope := type.object.scope END
  255. END
  256. END;
  257. ELSIF no IS TS.Import THEN
  258. m := TS.GetModule(no(TS.Import));
  259. IF m # NIL THEN
  260. curScope := m.scope;
  261. (* ELSE
  262. KernelLog.String("No symbol information for : "); KernelLog.String(no(TS.Import).import^); KernelLog.Ln *)
  263. END
  264. ELSIF no IS TS.Const THEN
  265. IF d.next # NIL THEN
  266. END
  267. (* ELSE
  268. KernelLog.String(" Pos= "); KernelLog.Int(d(TS.Ident).pos.a, 0); KernelLog.String(" : ");
  269. KernelLog.String("variable, const or procedure expected but "); ST.ID(no); KernelLog.Ln; *)
  270. END
  271. ELSE
  272. (* KernelLog.String("named object nil"); KernelLog.String(s); KernelLog.Ln; *)
  273. END
  274. ELSE
  275. KernelLog.String("no scope"); KernelLog.Ln;
  276. END
  277. END
  278. ELSIF d IS TS.Dereference THEN IF d.next # NIL THEN d := d.next END;
  279. ELSIF d IS TS.Index THEN
  280. (* automatic dealiasing if index access *)
  281. IF (type # NIL) & (type.kind = TS.TPointer) THEN
  282. type := ST.DealiaseType(type.pointer.type) END;
  283. IF (type = NIL) OR ( type.kind # TS.TArray) THEN
  284. IF type # NIL THEN ST.ShowType(type) END;
  285. KernelLog.String("Type is not an array pos= "); KernelLog.Int(lastpos, 0); KernelLog.Ln
  286. ELSE
  287. type := ST.DealiaseType(type.array.base);
  288. IF type # NIL THEN
  289. IF type.kind = TS.TRecord THEN curScope := type.record.scope
  290. ELSIF type.kind = TS.TObject THEN curScope := type.object.scope END
  291. END
  292. END;
  293. CheckExpressionList(d(TS.Index).expressionList, NIL, scope);
  294. ELSIF d IS TS.ActualParameters THEN
  295. (* no is the item before "(" *)
  296. IF no # NIL THEN
  297. IF no IS TS.ProcDecl THEN
  298. CheckExpressionList(d(TS.ActualParameters).expressionList, no(TS.ProcDecl).signature, scope)
  299. ELSIF (no IS TS.Var) THEN
  300. type := ST.DealiaseType(no(TS.Var).type);
  301. IF (type # NIL) & (type.kind = TS.TProcedure) THEN
  302. (* delegate *)
  303. IF type.procedure = NIL THEN
  304. KernelLog.String("no(TS.Var).type.procedure"); KernelLog.Ln;
  305. ELSIF type.procedure.signature = NIL THEN
  306. KernelLog.String("no(TS.Var).type.procedure.signature"); KernelLog.Ln;
  307. ELSE
  308. CheckExpressionList(d(TS.ActualParameters).expressionList, type.procedure.signature, scope)
  309. END;
  310. ELSE (* type guard *)
  311. IF d(TS.ActualParameters).expressionList # NIL THEN
  312. IF d(TS.ActualParameters).expressionList.next # NIL THEN
  313. KernelLog.String("lastpos= "); KernelLog.Int(lastpos, 0);
  314. KernelLog.String(" Can only guard for one type at once."); KernelLog.Ln
  315. ELSE
  316. IF d(TS.ActualParameters).expressionList.expression.kind = TS.ExpressionDesignator THEN
  317. type := ST.DealiaseType(ST.FindType(d(TS.ActualParameters).expressionList.expression.designator, scope));
  318. IF type # NIL THEN
  319. IF type.kind = TS.TRecord THEN curScope := type.record.scope
  320. ELSIF type.kind = TS.TObject THEN curScope := type.object.scope END
  321. END;
  322. CheckDesignator(d(TS.ActualParameters).expressionList.expression.designator, scope);
  323. ELSE
  324. KernelLog.String("Type expected"); KernelLog.Ln
  325. END
  326. END
  327. ELSE
  328. KernelLog.String("Expressionlist ist NIL"); KernelLog.Ln
  329. END
  330. END
  331. ELSE
  332. END
  333. ELSE
  334. (* not found... fallback *)
  335. CheckExpressionList(d(TS.ActualParameters).expressionList, NIL, scope)
  336. (* probably because of a not found
  337. KernelLog.String("lastpos= "); KernelLog.Int(lastpos, 0);
  338. KernelLog.String(" No proc"); KernelLog.Ln *)
  339. END
  340. END;
  341. first := FALSE;
  342. (* Auto dereferencing *)
  343. IF type # NIL THEN
  344. IF type.kind = TS.TPointer THEN type := ST.DealiaseType(type.pointer.type) END;
  345. IF type # NIL THEN
  346. IF type.kind = TS.TRecord THEN curScope := type.record.scope
  347. ELSIF type.kind = TS.TObject THEN curScope := type.object.scope END
  348. END
  349. END;
  350. d := d.next
  351. END
  352. END CheckDesignator;
  353. PROCEDURE DumpType*(t : TS.Type; scope : TS.Scope);
  354. BEGIN
  355. CASE t.kind OF
  356. |TS.TAlias : CheckDesignator(t.qualident, scope)
  357. |TS.TObject : DumpObject(t.object)
  358. |TS.TArray : DumpArray(t.array, scope);
  359. |TS.TPointer : DumpType(t.pointer.type, scope)
  360. |TS.TRecord : DumpRecord(t.record);
  361. |TS.TProcedure : DumpProcedure(t.procedure)
  362. ELSE
  363. Trace.String("Unknown Type"); Trace.String("t.kind= "); Trace.Int(t.kind, 0); Trace.Ln;
  364. END
  365. END DumpType;
  366. PROCEDURE DumpCases(case : TS.Case; scope : TS.Scope);
  367. VAR cr : TS.CaseRange;
  368. BEGIN
  369. WHILE case # NIL DO
  370. cr := case.caseRanges;
  371. WHILE cr # NIL DO
  372. CheckExpression(cr.a, scope);
  373. IF cr.b # NIL THEN CheckExpression(cr.b, scope) END;
  374. cr := cr.next
  375. END;
  376. IF case.statements # NIL THEN DumpStatementSequence(case.statements, scope) END;
  377. case := case.next
  378. END;
  379. END DumpCases;
  380. PROCEDURE DumpTypeDecl(t : TS.TypeDecl; scope : TS.Scope);
  381. BEGIN
  382. DumpType(t.type, scope);
  383. END DumpTypeDecl;
  384. PROCEDURE DumpVar(v : TS.Var; scope : TS.Scope);
  385. BEGIN
  386. DumpType(v.type, scope);
  387. END DumpVar;
  388. PROCEDURE DumpStatementSequence(s : TS.Statement; scope : TS.Scope);
  389. VAR ts : TS.Statement;
  390. BEGIN
  391. WHILE s # NIL DO
  392. IF s IS TS.Assignment THEN
  393. CheckDesignator(s(TS.Assignment).designator, scope);
  394. CheckExpression(s(TS.Assignment).expression, scope);
  395. ELSIF s IS TS.ProcedureCall THEN
  396. CheckDesignator(s(TS.ProcedureCall).designator, scope);
  397. ELSIF s IS TS.IFStatement THEN
  398. CheckExpression(s(TS.IFStatement).expression, scope);
  399. DumpStatementSequence(s(TS.IFStatement).then, scope);
  400. ts := s(TS.IFStatement).else;
  401. IF ts # NIL THEN
  402. DumpStatementSequence(ts, scope);
  403. END;
  404. ELSIF s IS TS.WHILEStatement THEN
  405. CheckExpression(s(TS.WHILEStatement).expression, scope);
  406. DumpStatementSequence(s(TS.WHILEStatement).statements, scope);
  407. ELSIF s IS TS.REPEATStatement THEN
  408. DumpStatementSequence(s(TS.REPEATStatement).statements, scope);
  409. CheckExpression(s(TS.REPEATStatement).expression, scope);
  410. ELSIF s IS TS.LOOPStatement THEN
  411. DumpStatementSequence(s(TS.LOOPStatement).statements, scope);
  412. ELSIF s IS TS.FORStatement THEN
  413. CheckDesignator(s(TS.FORStatement).variable, scope);
  414. CheckExpression(s(TS.FORStatement).fromExpression, scope);
  415. CheckExpression(s(TS.FORStatement).toExpression, scope);
  416. IF s(TS.FORStatement).byExpression # NIL THEN
  417. CheckExpression(s(TS.FORStatement).byExpression, scope);
  418. END;
  419. DumpStatementSequence(s(TS.FORStatement).statements, scope);
  420. ELSIF s IS TS.EXITStatement THEN
  421. ELSIF s IS TS.RETURNStatement THEN
  422. IF s(TS.RETURNStatement).expression # NIL THEN CheckExpression(s(TS.RETURNStatement).expression, scope) END;
  423. ELSIF s IS TS.AWAITStatement THEN
  424. CheckExpression(s(TS.AWAITStatement).expression, scope);
  425. ELSIF s IS TS.StatementBlock THEN
  426. DumpStatementSequence(s(TS.StatementBlock).statements, scope);
  427. ELSIF s IS TS.WITHStatement THEN
  428. CheckDesignator(s(TS.WITHStatement).variable, scope);
  429. CheckDesignator(s(TS.WITHStatement).type, scope);
  430. DumpStatementSequence(s(TS.WITHStatement).statements, scope);
  431. ELSIF s IS TS.CASEStatement THEN
  432. CheckExpression(s(TS.CASEStatement).expression, scope);
  433. DumpCases(s(TS.CASEStatement).cases, scope);
  434. IF s(TS.CASEStatement).else # NIL THEN
  435. DumpStatementSequence(s(TS.CASEStatement).else, scope)
  436. END;
  437. END;
  438. NoteCommentRanges(s.preComment);
  439. NoteCommentRanges(s.postComment);
  440. s := s.next
  441. END
  442. END DumpStatementSequence;
  443. PROCEDURE CheckSignature(sig : TS.ProcedureSignature; scope : TS.Scope);
  444. VAR i : LONGINT; cur : TS.NamedObject; t : TS.Type;
  445. BEGIN
  446. IF sig = NIL THEN RETURN END;
  447. IF sig.return # NIL THEN DumpType(sig.return, scope) END;
  448. IF sig.params # NIL THEN
  449. t := NIL;
  450. FOR i := 0 TO sig.params.nofObjs - 1 DO
  451. cur := sig.params.objs[i];
  452. NoteDeclaration(cur);
  453. IF cur IS TS.Var THEN IF t # cur(TS.Var).type THEN DumpType(cur(TS.Var).type, scope) END; t := cur(TS.Var).type
  454. ELSE KernelLog.String("non- variable as a parameter"); KernelLog.Ln
  455. END
  456. END
  457. END
  458. END CheckSignature;
  459. PROCEDURE DumpProcDecl(p : TS.ProcDecl);
  460. VAR s : TS.Statement;
  461. cur : TS.NamedObject; i : LONGINT;
  462. BEGIN
  463. CheckSignature(p.signature, p.scope.parent);
  464. (*IF (p.signature # NIL) & (p.signature.params # NIL) THEN
  465. FOR i := 0 TO p.signature.params.nofObjs - 1 DO
  466. cur := p.signature.params.objs[i];
  467. NoteDeclaration(cur);
  468. END
  469. END;*)
  470. DumpDeclarations(p.scope);
  471. IF p.scope.ownerBody # NIL THEN
  472. s := p.scope.ownerBody;
  473. DumpStatementSequence(s, p.scope)
  474. END;
  475. END DumpProcDecl;
  476. PROCEDURE DumpDeclarations(d : TS.Scope);
  477. VAR i : LONGINT;
  478. last, cur : TS.NamedObject;
  479. BEGIN
  480. IF d = NIL THEN RETURN END;
  481. FOR i := 0 TO d.elements.nofObjs - 1 DO
  482. cur := d.elements.objs[i];
  483. CommentsFromNamedObject(cur);
  484. NoteDeclaration(cur);
  485. IF cur IS TS.Const THEN
  486. DumpConst(d, cur(TS.Const))
  487. ELSIF cur IS TS.TypeDecl THEN
  488. DumpTypeDecl(cur(TS.TypeDecl), d);
  489. ELSIF cur IS TS.Var THEN
  490. DumpVar(cur(TS.Var), d)
  491. ELSIF cur IS TS.ProcDecl THEN
  492. DumpProcDecl(cur(TS.ProcDecl))
  493. ELSIF cur IS TS.Import THEN
  494. END;
  495. last := cur;
  496. END
  497. END DumpDeclarations;
  498. PROCEDURE NoteCommentRanges(comments : TS.Comments);
  499. VAR cur : TS.Comment;
  500. r : Streams.StringReader;
  501. token : ARRAY 32 OF CHAR;
  502. BEGIN
  503. IF comments = NIL THEN RETURN END;
  504. cur := comments.first;
  505. WHILE cur # NIL DO
  506. IF (currentAuthor = "") & (Strings.Pos("AUTHOR", cur.str^) >= 0) THEN
  507. IF Strings.Pos("PURPOSE", cur.str^) >= 0 THEN
  508. NEW(r, LEN(cur.str^));
  509. r.Set(cur.str^);
  510. WHILE r.res # Streams.EOF DO
  511. r.SkipWhitespace;
  512. r.Token(token);
  513. r.SkipWhitespace;
  514. IF token = "AUTHOR" THEN
  515. r.String(currentAuthor);
  516. KernelLog.String("currentAuthor= "); KernelLog.String(currentAuthor); KernelLog.Ln;
  517. ELSIF token = "PURPOSE" THEN
  518. r.String(currentPurpose);
  519. KernelLog.String("currentPurpose= "); KernelLog.String(currentPurpose); KernelLog.Ln;
  520. END
  521. END
  522. END
  523. END;
  524. MakeRange(cur.pos.a, cur.pos.b, KindComment, NIL);
  525. cur := cur.next
  526. END
  527. END NoteCommentRanges;
  528. PROCEDURE CommentsFromNamedObject(no : TS.NamedObject);
  529. BEGIN
  530. NoteCommentRanges(no.preComment);
  531. NoteCommentRanges(no.postComment);
  532. END CommentsFromNamedObject;
  533. PROCEDURE NoteDeclaration(no : TS.NamedObject);
  534. BEGIN
  535. MakeRange(no.pos.a, no.pos.b, KindDeclaration, no);
  536. END NoteDeclaration;
  537. PROCEDURE DumpM*(m : TS.Module);
  538. BEGIN
  539. CommentsFromNamedObject(m);
  540. NoteDeclaration(m);
  541. DumpDeclarations(m.scope);
  542. IF m.scope.ownerBody # NIL THEN
  543. DumpStatementSequence(m.scope.ownerBody, m.scope)
  544. END
  545. END DumpM;
  546. PROCEDURE DumpLocalUses;
  547. VAR i : LONGINT;
  548. filename, scopePath, name, path : ARRAY 1024 OF CHAR;
  549. a : ANY;
  550. u : GlobalUse;
  551. BEGIN
  552. FOR i := 0 TO localUses.nof - 1 DO
  553. ST.GetSourceReference(localUses.items[i], filename, scopePath);
  554. a := globalUses.Find(scopePath);
  555. IF a = NIL THEN
  556. NEW(u);
  557. globalUses.Add(scopePath, u);
  558. ELSE
  559. u := a(GlobalUse);
  560. END;
  561. u.AddFile(filename);
  562. END;
  563. END DumpLocalUses;
  564. PROCEDURE GenerateModule(module : TS.Module; r : Streams.Reader; out : Streams.Writer);
  565. VAR ch : CHAR;
  566. w : Streams.Writer;
  567. currentRange, pos, nextEnd : LONGINT;
  568. inRange, inComment, lastInRange : BOOLEAN;
  569. token : ARRAY 1024 OF CHAR;
  570. filename, scopePath, name, path : ARRAY 1024 OF CHAR;
  571. i : LONGINT;
  572. referencedModule : TS.Module;
  573. CONST DoXml = TRUE;
  574. BEGIN
  575. (* Source files > 1MB are not supported *)
  576. NEW(localUses);
  577. IF ranges = NIL THEN NEW(ranges, 1000000)
  578. ELSE
  579. FOR i := 0 TO LEN(ranges) - 1 DO
  580. ranges[i].kind := KindNoStart;
  581. ranges[i].no := NIL
  582. END
  583. END;
  584. DumpM(module);
  585. IF out = NIL THEN
  586. NEW(ml, module.name^, w);
  587. ELSE
  588. w := out;
  589. END;
  590. pos := 0;
  591. inRange := FALSE; lastInRange := FALSE; inComment := FALSE;
  592. IF DoXml THEN
  593. w.String('<!DOCTYPE html>'); w.Ln;
  594. w.String('<html>'); w.Ln();
  595. w.String(' <head>'); w.Ln();
  596. w.String(' <title>'); w.String(module.name^); w.String('</title>'); w.Ln();
  597. w.String(' <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>'); w.Ln();
  598. w.String(' <link rel="stylesheet" href="code.css" type="text/css" media="screen"/>'); w.Ln();
  599. w.String(' <script src="highlight.js"> </script>'); w.Ln();
  600. w.String(' </head>'); w.Ln();
  601. w.String('<body onLoad="setup();">'); w.Ln();
  602. w.String('<nav>'); w.Ln();
  603. w.String(' <div class="menu">'); w.Ln();
  604. w.String(' <ul>'); w.Ln();
  605. w.String(' <li><a href="index.html">Index</a></li>'); w.Ln();
  606. w.String(' </ul>'); w.Ln();
  607. w.String(' </div>'); w.Ln();
  608. w.String('</nav>'); w.Ln();
  609. w.String('<div class="scroll"><code><pre>'); w.Ln();
  610. END;
  611. ch := r.Get();
  612. REPEAT
  613. IF ~inRange THEN
  614. IF (ranges[pos].kind # KindNoStart) & (ranges[pos].b > pos) THEN
  615. inRange := TRUE;
  616. currentRange := pos;
  617. nextEnd := ranges[pos].b;
  618. CASE ranges[pos].kind OF
  619. | KindComment :
  620. w.String('<span class="comment">');
  621. inComment := TRUE;
  622. | KindDeclaration:
  623. ST.GetSourceReference(ranges[pos].no, filename, scopePath);
  624. Files.SplitPath(filename, path, name);
  625. w.String('<a name="'); w.String(scopePath);w.String('">');
  626. | KindUse :
  627. scopePath := ""; filename := "";
  628. IF ranges[pos].no.container # TFAOParser.Universe THEN
  629. IF ranges[pos].no IS TS.Import THEN
  630. referencedModule := TS.GetModule(ranges[pos].no(TS.Import));
  631. IF referencedModule # NIL THEN
  632. COPY(referencedModule.name^, scopePath);
  633. IF referencedModule.filename # NIL THEN
  634. COPY(referencedModule.filename^, filename)
  635. END
  636. END
  637. ELSE
  638. ST.GetSourceReference(ranges[pos].no, filename, scopePath);
  639. END;
  640. Files.SplitPath(filename, path, name);
  641. w.String('<a href="'); w.String(name); w.String('.html#'); w.String(scopePath); w.String('">');
  642. END
  643. END
  644. END
  645. ELSE
  646. IF pos = nextEnd THEN
  647. IF token # "" THEN
  648. w.String(token);
  649. token := "";
  650. END;
  651. CASE ranges[currentRange].kind OF
  652. | KindComment :
  653. w.String('</span>');
  654. | KindDeclaration:
  655. w.String('</a>');
  656. | KindUse:
  657. IF ranges[currentRange].no.container # TFAOParser.Universe THEN
  658. w.String('</a>');
  659. END
  660. END;
  661. inRange := FALSE;
  662. inComment := FALSE;
  663. END
  664. END;
  665. IF ~inComment THEN
  666. IF ~S.reservedChar[ORD(ch)] THEN
  667. Strings.AppendChar(token, ch);
  668. WHILE ~S.newChar[ORD(ch)] DO
  669. ch := r.Get();
  670. Strings.AppendChar(token, ch);
  671. END
  672. ELSE
  673. IF IsKeyWord(token) THEN
  674. w.String('<span class="keyword">');
  675. w.String(token);
  676. w.String('</span>');
  677. ELSE
  678. w.String(token);
  679. END;
  680. token := "";
  681. IF ch = "<" THEN w.String("&lt;")
  682. ELSE w.Char(ch)
  683. END;
  684. WHILE ~S.newChar[ORD(ch)] DO
  685. ch := r.Get();
  686. w.Char(ch);
  687. END
  688. END
  689. ELSE
  690. IF ch = "<" THEN w.String("&lt;")
  691. ELSE w.Char(ch)
  692. END;
  693. WHILE ~S.newChar[ORD(ch)] DO
  694. ch := r.Get();
  695. w.Char(ch);
  696. END
  697. END;
  698. INC(pos);
  699. ch := r.Get();
  700. UNTIL r.res # 0;
  701. IF DoXml THEN
  702. w.String('</pre></code>'); w.Ln();
  703. w.String('<div class="footer">'); PageTime(w); w.String('</div>');
  704. w.String("</div></body></html>"); w.Ln();
  705. END;
  706. w.Update;
  707. DumpLocalUses;
  708. END GenerateModule;
  709. PROCEDURE InitWithText(t: Texts.Text; pos: LONGINT): Strings.String;
  710. VAR buffer: Strings.String; len, i, j, ch: LONGINT; r: Texts.TextReader;
  711. bytesPerChar: LONGINT;
  712. PROCEDURE ExpandBuf(VAR oldBuf: Strings.String; newSize: LONGINT);
  713. VAR newBuf: Strings.String; i: LONGINT;
  714. BEGIN
  715. IF LEN(oldBuf^) >= newSize THEN RETURN END;
  716. NEW(newBuf, newSize);
  717. FOR i := 0 TO LEN(oldBuf^)-1 DO
  718. newBuf[i] := oldBuf[i];
  719. END;
  720. oldBuf := newBuf;
  721. END ExpandBuf;
  722. BEGIN
  723. t.AcquireRead;
  724. len := t.GetLength();
  725. bytesPerChar := 2;
  726. NEW(buffer, len * bytesPerChar); (* UTF8 encoded characters use up to 5 bytes *)
  727. NEW(r, t);
  728. r.SetPosition(pos);
  729. j := 0;
  730. FOR i := 0 TO len-1 DO
  731. r.ReadCh(ch);
  732. WHILE ~UTF8Strings.EncodeChar(ch, buffer^, j) DO
  733. (* buffer too small *)
  734. INC(bytesPerChar);
  735. ExpandBuf(buffer, bytesPerChar * len);
  736. END;
  737. END;
  738. t.ReleaseRead;
  739. RETURN buffer;
  740. END InitWithText;
  741. PROCEDURE ProcessFile(CONST filename, targetPath : ARRAY OF CHAR; indexFile : Streams.Writer);
  742. VAR
  743. module : TS.Module;
  744. t : Texts.Text; res : WORD; format: LONGINT;
  745. r : Streams.StringReader;
  746. str : Strings.String;
  747. name, path, targetFile : ARRAY 1024 OF CHAR;
  748. f : Files.File;
  749. fw : Files.Writer;
  750. trap : BOOLEAN;
  751. BEGIN
  752. KernelLog.String("filename= "); KernelLog.String(filename); KernelLog.Ln;
  753. NEW(t);
  754. Files.SplitPath(filename, path, name);
  755. Files.JoinPath(targetPath, name, targetFile);
  756. Strings.Append(targetFile, ".html");
  757. TFAOParser.ScanModule(filename, FALSE, module);
  758. IF module # NIL THEN
  759. module.filename := Strings.NewString(filename);
  760. TextUtilities.LoadAuto(t, filename, format, res);
  761. str := InitWithText(t, 0);
  762. NEW(r, Strings.Length(str^));
  763. r.Set(str^);
  764. f := Files.New(targetFile);
  765. Files.OpenWriter(fw, f, 0);
  766. currentAuthor := "";
  767. currentPurpose := "";
  768. GenerateModule(module, r, fw);
  769. IF (indexFile # NIL) THEN
  770. indexFile.String('<tr><td><a href="'); indexFile.String(name); indexFile.String('.html">');
  771. indexFile.String(module.name^); indexFile.String('</a></td><td>');
  772. indexFile.String(currentPurpose); indexFile.String('</td><td>');
  773. indexFile.String(currentAuthor); indexFile.String('</td></tr>');
  774. indexFile.Ln
  775. END;
  776. fw.Update();
  777. Files.Register(f)
  778. END;
  779. FINALLY
  780. IF trap THEN (* trap will be set in case a trap occurs in the block above *)
  781. KernelLog.String("Parse error for "); KernelLog.String(filename); KernelLog.Ln;
  782. END
  783. END ProcessFile;
  784. PROCEDURE Generate*(par : Commands.Context) ;
  785. VAR
  786. filename :ARRAY 256 OF CHAR;
  787. sr : Streams.Reader;
  788. t0, t1 : LONGINT;
  789. module : TS.Module;
  790. t : Texts.Text; res : WORD; format: LONGINT;
  791. textReader : TextUtilities.TextReader;
  792. BEGIN
  793. NEW(globalUses);
  794. sr := par.arg;
  795. sr.String(filename);
  796. KernelLog.String("Parsing "); KernelLog.String(filename);
  797. t0 := Kernel.GetTicks();
  798. NEW(t);
  799. TFAOParser.ScanModule(filename, FALSE, module);
  800. IF module # NIL THEN
  801. module.filename := Strings.NewString(filename);
  802. TextUtilities.LoadAuto(t, filename, format, res);
  803. NEW(textReader, t);
  804. GenerateModule(module, textReader, NIL);
  805. TFDocGenerator.DocumentModule(module);
  806. END;
  807. t1 := Kernel.GetTicks();
  808. KernelLog.String("t1-t0= "); KernelLog.Int(t1-t0, 0); KernelLog.Ln;
  809. KernelLog.String(" done.");
  810. END Generate;
  811. (** Make sure to have built all the TFPET Symbol files first *)
  812. PROCEDURE MakeXRef*(par : Commands.Context) ;
  813. VAR e : Files.Enumerator;
  814. path, name, exclude : ARRAY 256 OF CHAR; flags : SET; time, date, size : LONGINT;
  815. sr : Streams.Reader;
  816. indexFileWriter : Files.Writer;
  817. f : Files.File;
  818. BEGIN
  819. NEW(globalUses);
  820. sr := par.arg;
  821. sr.String(path); sr.SkipWhitespace();
  822. sr.String(exclude);
  823. IF (path # "") & ~Strings.EndsWith("/", path) THEN Strings.Append(path, "/") END;
  824. Strings.Append(path, "*.Mod");
  825. KernelLog.String(path); KernelLog.Ln;
  826. IF exclude # "" THEN
  827. KernelLog.String("Excluding "); KernelLog.String(exclude); KernelLog.Ln;
  828. END;
  829. NEW(e);
  830. e.Open(path, {});
  831. KernelLog.String("Processing ... "); KernelLog.Ln;
  832. f := Files.New("xref/index.html");
  833. Files.OpenWriter(indexFileWriter, f, 0);
  834. indexFileWriter.String("<html><table>"); indexFileWriter.Ln;
  835. WHILE e.HasMoreEntries() DO
  836. IF e.GetEntry(name, flags, time, date, size) THEN
  837. IF (exclude = "") OR ~Strings.Match(exclude, name) THEN
  838. (*AddTask(name);*)
  839. ProcessFile(name, "xref", indexFileWriter);
  840. ELSE
  841. KernelLog.String("Excluding "); KernelLog.String(name); KernelLog.Ln;
  842. END
  843. END
  844. END;
  845. indexFileWriter.String("</table></html>"); indexFileWriter.Ln;
  846. indexFileWriter.Update;
  847. Files.Register(f)
  848. END MakeXRef;
  849. PROCEDURE PageTime(out : Streams.Writer);
  850. VAR dateTimeStr : ARRAY 32 OF CHAR;
  851. BEGIN
  852. Strings.FormatDateTime("yyyy.mm.dd hh:nn:ss", Dates.Now(), dateTimeStr);
  853. out.String(dateTimeStr)
  854. END PageTime;
  855. PROCEDURE IsKeyWord(CONST str : ARRAY OF CHAR) : BOOLEAN;
  856. VAR s : LONGINT;
  857. BEGIN
  858. s := 0;
  859. IF str = "ARRAY" THEN s := S.array
  860. ELSIF str = "AWAIT" THEN s := S.passivate
  861. ELSIF str = "BEGIN" THEN s := S.begin
  862. ELSIF str = "BY" THEN s := S.by
  863. ELSIF str = "CONST" THEN s := S.const
  864. ELSIF str = "CASE" THEN s := S.case
  865. ELSIF str = "CODE" THEN s := S.code
  866. ELSIF str = "DO" THEN s := S.do
  867. ELSIF str = "DIV" THEN s := S.div
  868. ELSIF str = "DEFINITION" THEN s := S.definition
  869. ELSIF str = "END" THEN s := S.end
  870. ELSIF str = "ELSE" THEN s := S.else
  871. ELSIF str = "ELSIF" THEN s := S.elsif
  872. ELSIF str = "EXIT" THEN s := S.exit
  873. ELSIF str = "FALSE" THEN s := S.false
  874. ELSIF str = "FOR" THEN s := S.for
  875. ELSIF str = "IF" THEN s := S.if
  876. ELSIF str = "IN" THEN s := S.in
  877. ELSIF str = "IS" THEN s := S.is
  878. ELSIF str = "IMPORT" THEN s := S.import
  879. ELSIF str = "IMPLEMENTS" THEN s := S.implements
  880. ELSIF str = "LOOP" THEN s := S.loop
  881. ELSIF str = "MOD" THEN s := S.mod
  882. ELSIF str = "MODULE" THEN s := S.module
  883. ELSIF str = "NIL" THEN s := S.nil
  884. ELSIF str = "OR" THEN s := S.or
  885. ELSIF str = "OF" THEN s := S.of
  886. ELSIF str = "OBJECT" THEN s := S.object
  887. ELSIF str = "PROCEDURE" THEN s := S.procedure
  888. ELSIF str = "POINTER" THEN s := S.pointer
  889. ELSIF str = "RECORD" THEN s := S.record
  890. ELSIF str = "REPEAT" THEN s := S.repeat
  891. ELSIF str = "RETURN" THEN s := S.return
  892. ELSIF str = "REFINES" THEN s := S.refines
  893. ELSIF str = "THEN" THEN s := S.then
  894. ELSIF str = "TRUE" THEN s := S.true
  895. ELSIF str = "TO" THEN s := S.to
  896. ELSIF str = "TYPE" THEN s := S.type
  897. ELSIF str = "UNTIL" THEN s := S.until
  898. ELSIF str = "VAR" THEN s := S.var
  899. ELSIF str = "WHILE" THEN s := S.while
  900. ELSIF str = "WITH" THEN s := S.with
  901. END;
  902. RETURN s # 0
  903. END IsKeyWord;
  904. END TFXRef.
  905. (* Make sure the TFPET symbol files are available (takes a few minutes) *)
  906. TFAOParser.MakeSymbolFiles "D:\Aos\trunk\source\" "*Oberon*"~ (* d:/release/*.Mod *)
  907. System.Free TFXRef TFDocGenerator~
  908. TFXRef.MakeXRef "D:\Aos\trunk\source\" "*Oberon*"~
  909. TFXRef.Generate HelloWorld.Mod ~
  910. TFXRef.Generate I386.VMWareTools.Mod ~
  911. TFXRef.Generate TFModuleTrees.Mod ~
  912. TFXRef.Generate String.Mod ~