AutodocParser.Mod 51 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655
  1. MODULE AutodocParser;
  2. IMPORT Files, Texts, Out, Strings, Platform, Int, Env;
  3. CONST
  4. (** Lexer constants **)
  5. null = 0;
  6. ident = 1;
  7. int = 2;
  8. real = 3;
  9. set = 4;
  10. char = 5;
  11. string = 6;
  12. module = 10;
  13. import = 11;
  14. const = 12;
  15. type = 13;
  16. var = 14;
  17. in = 15;
  18. out = 16;
  19. record = 17;
  20. array = 18;
  21. pointer = 19;
  22. to = 20;
  23. of = 21;
  24. procedure = 22;
  25. begin = 23;
  26. end = 24;
  27. lparen = 30;
  28. rparen = 31;
  29. lbrak = 32;
  30. rbrak = 33;
  31. lbrace = 34;
  32. rbrace = 35;
  33. period = 36;
  34. comma = 37;
  35. upto = 38;
  36. colon = 39;
  37. semicol = 40;
  38. equals = 41;
  39. becomes = 42;
  40. plus = 43;
  41. minus = 44;
  42. times = 45;
  43. div = 46;
  44. mod = 47;
  45. rdiv = 48;
  46. not = 49;
  47. arrow = 50;
  48. eot = 70;
  49. (** Forms of Types **)
  50. undefType* = 0;
  51. namedType* = 1;
  52. recordType* = 2;
  53. arrayType* = 3;
  54. pointerType* = 4;
  55. procedureType* = 5;
  56. (** Values of Param.passed **)
  57. byValue* = 0;
  58. byVar* = 1; (** When a formal parameter has VAR, IN or OUT before it *)
  59. (** Comment separators **)
  60. tab = 9X; (** Separates two special comments *)
  61. vtab = 0BX; (** Separates two comments that related to different objects *)
  62. (** - **)
  63. defLang = 'EN'; (** Default comment language *)
  64. TYPE
  65. Str* = ARRAY 256 OF CHAR;
  66. LongStr* = ARRAY 40960 OF CHAR;
  67. Object* = POINTER TO ObjectDesc;
  68. ObjectDesc* = RECORD
  69. name*: Str;
  70. comment*: LongStr;
  71. exported*: BOOLEAN;
  72. next*: Object
  73. END;
  74. List* = POINTER TO ListDesc;
  75. ListDesc* = RECORD(ObjectDesc)
  76. first*, last*: Object
  77. END;
  78. Group* = POINTER TO GroupDesc;
  79. GroupDesc* = RECORD(ListDesc)
  80. ordinalConsts*: BOOLEAN
  81. END;
  82. Import* = POINTER TO ImportDesc;
  83. ImportDesc* = RECORD(ObjectDesc)
  84. alias*: Str
  85. END;
  86. Const* = POINTER TO ConstDesc;
  87. ConstDesc* = RECORD(ObjectDesc)
  88. value*: Str;
  89. isOrdinal*: BOOLEAN; (** TRUE if type of const is integer or char *)
  90. intVal*: INTEGER (** If isOrdinal, holds value in integer format *)
  91. END;
  92. Type* = POINTER TO TypeDesc;
  93. TypeDesc* = RECORD(ObjectDesc)
  94. form*: INTEGER; (** See @Forms of Types *)
  95. len*: Str; (** Length of array (may be an expression), or '' *)
  96. base*: Type; (** Base type of rec/arr/pointer, return of procedure *)
  97. fields*: List
  98. END;
  99. Var* = POINTER TO VarDesc; (** Global variables and record fields *)
  100. VarDesc* = RECORD(ObjectDesc)
  101. type*: Type
  102. END;
  103. Param* = POINTER TO ParamDesc;
  104. ParamDesc* = RECORD(ObjectDesc)
  105. passed*: INTEGER; (** See constants above (values of Param.passed) *)
  106. type*: Type
  107. END;
  108. Procedure* = POINTER TO ProcedureDesc;
  109. ProcedureDesc* = RECORD(ObjectDesc)
  110. returnType*: Type;
  111. params*: List;
  112. receiver*: Param;
  113. modifier*: Str;
  114. code*: Str; (** Code of the procedure as string, when external is TRUE *)
  115. external*: BOOLEAN; (* TRUE if has a minus after the word PROCEDURE *)
  116. END;
  117. Module* = POINTER TO ModuleDesc;
  118. ModuleDesc* = RECORD(ObjectDesc)
  119. foreign*: BOOLEAN; (** TRUE if module has a [foreign] mark *)
  120. exportedOnly*: BOOLEAN; (** TRUE if only exported objects are included *)
  121. imports*: List; (** List of imports (no groups) *)
  122. consts*, types*, vars*: List; (** List of groups *)
  123. procedures*: List (** List of groups *)
  124. END;
  125. VAR
  126. curModule: Module; (** Currently generated module data structure *)
  127. curFname: Str; (** Set by SetFname and used in Mark for error output *)
  128. R: Files.Rider; (** Rider of the currently parsed module *)
  129. c: CHAR; (** One step ahead character read from rider R *)
  130. line, col: INTEGER; (** Position in R *)
  131. lastError: INTEGER; (** Position in R of last error, or -1 *)
  132. constExprBeginPos: INTEGER; (** After '=' or 'ARRAY', see ParseConstExpr *)
  133. constExprBeginC: CHAR; (** Value of c the moment constExprBeginPos is set *)
  134. objectIsExported: BOOLEAN; (** The parsed variable/field/param is exported *)
  135. sym: INTEGER; (** One step ahead (syntactic) symbol read *)
  136. id: ARRAY 256 OF CHAR; (** Identifier read *)
  137. len: INTEGER; (** Actual length of id *)
  138. ival: INTEGER; (** Integer value read *)
  139. writingDoc: BOOLEAN; (** TRUE when inside a doc comment *)
  140. docNewLine: BOOLEAN; (** 0AX reached and no non-spaces after it yet *)
  141. doc: LongStr; (** Currently saved documentation comment *)
  142. docLen: INTEGER; (** Actual length of doc *)
  143. docCol: INTEGER; (** Column of 1st non-space of 1st comment in doc, or -1 *)
  144. docLine: INTEGER; (** Line where the first doc-comment in doc started *)
  145. docEndLine: INTEGER; (** Line where the last doc-comment in doc ended *)
  146. pre: BOOLEAN; (** TRUE when the current comment line is pre-formatted *)
  147. (** Title of the current group of comments.
  148. A special value of '-' means an empty title. Assigned by ReadComment.
  149. Used by NewGroup and UpdateCurGroup. Reset by Parse* procedures. *)
  150. curTitle: Str;
  151. titleNotUsed: BOOLEAN; (** To clear curTitle between decl sections *)
  152. PrintObject: PROCEDURE (o: Object; indent: INTEGER; inlined: BOOLEAN);
  153. ParseType: PROCEDURE (docObj: Object): Type;
  154. ParseParamType: PROCEDURE (): Type;
  155. (** Parsing Parameters **)
  156. exportedOnly: BOOLEAN; (** If TRUE, only exported objects are added *)
  157. keepAliases: BOOLEAN; (** If FALSE, change alias to real module names *)
  158. (** Debug **)
  159. debug*: BOOLEAN;
  160. (** Comment Language **)
  161. lang: ARRAY 3 OF CHAR; (** In what langauge to write the documentation *)
  162. curLang: ARRAY 3 OF CHAR; (** Current comment language, changed with '%RU' *)
  163. langMark: INTEGER; (** curLang[langMark] is begin set, or langMark = -1 *)
  164. (** Parsing Parameters **)
  165. PROCEDURE SetExportedOnly*(yes: BOOLEAN);
  166. BEGIN exportedOnly := yes
  167. END SetExportedOnly;
  168. PROCEDURE SetKeepAliases*(yes: BOOLEAN);
  169. BEGIN keepAliases := yes
  170. END SetKeepAliases;
  171. (** Debug **)
  172. PROCEDURE Debug*(s: ARRAY OF CHAR);
  173. BEGIN
  174. IF debug THEN Out.String(s); Out.Ln END
  175. END Debug;
  176. PROCEDURE SetDebug*(yes: BOOLEAN);
  177. BEGIN debug := yes
  178. END SetDebug;
  179. (** Error Handling **)
  180. (** Used for error output in Mark *)
  181. PROCEDURE SetFname*(fname: ARRAY OF CHAR);
  182. BEGIN curFname := fname
  183. END SetFname;
  184. (** Show error s *)
  185. PROCEDURE Mark(s: ARRAY OF CHAR);
  186. VAR pos: INTEGER;
  187. BEGIN
  188. pos := Files.Pos(R);
  189. IF (lastError = -1) OR (lastError + 7 < pos) THEN
  190. Out.String(curFname); Out.Char(':');
  191. Out.Int(line, 0); Out.Char(':'); Out.Int(col, 0);
  192. Out.String(': error: '); Out.String(s); Out.Ln
  193. END;
  194. lastError := pos
  195. END Mark;
  196. (** Show error consisting of a + b + c *)
  197. PROCEDURE Mark3(a, b, c: ARRAY OF CHAR);
  198. VAR s: ARRAY 1024 OF CHAR;
  199. BEGIN s := a; Strings.Append(b, s); Strings.Append(c, s); Mark(s)
  200. END Mark3;
  201. (** Put textual representation of sym in s *)
  202. PROCEDURE SymToStr(sym: INTEGER; VAR s: ARRAY OF CHAR);
  203. BEGIN
  204. IF sym = null THEN s := 'nothing'
  205. ELSIF sym = ident THEN Strings.Copy(id, s)
  206. ELSIF sym = int THEN Int.Str(ival, s)
  207. ELSIF sym = real THEN s := 'real number'
  208. ELSIF sym = set THEN s := 'set'
  209. ELSIF sym = string THEN s := 'string'
  210. ELSIF sym = module THEN s := 'MODULE'
  211. ELSIF sym = import THEN s := 'IMPORT'
  212. ELSIF sym = const THEN s := 'CONST'
  213. ELSIF sym = type THEN s := 'TYPE'
  214. ELSIF sym = var THEN s := 'VAR'
  215. ELSIF sym = in THEN s := 'IN'
  216. ELSIF sym = out THEN s := 'OUT'
  217. ELSIF sym = record THEN s := 'RECORD'
  218. ELSIF sym = array THEN s := 'ARRAY'
  219. ELSIF sym = pointer THEN s := 'POINTER'
  220. ELSIF sym = to THEN s := 'TO'
  221. ELSIF sym = of THEN s := 'OF'
  222. ELSIF sym = procedure THEN s := 'PROCEDURE'
  223. ELSIF sym = begin THEN s := 'BEGIN'
  224. ELSIF sym = end THEN s := 'END'
  225. ELSIF sym = div THEN s := 'DIV'
  226. ELSIF sym = mod THEN s := 'MOD'
  227. ELSIF sym = lparen THEN s := '('
  228. ELSIF sym = rparen THEN s := ')'
  229. ELSIF sym = lbrak THEN s := '['
  230. ELSIF sym = rbrak THEN s := ']'
  231. ELSIF sym = lbrace THEN s := '{'
  232. ELSIF sym = rbrace THEN s := '}'
  233. ELSIF sym = period THEN s := '.'
  234. ELSIF sym = comma THEN s := ','
  235. ELSIF sym = upto THEN s := '..'
  236. ELSIF sym = colon THEN s := ':'
  237. ELSIF sym = semicol THEN s := ';'
  238. ELSIF sym = equals THEN s := '='
  239. ELSIF sym = becomes THEN s := ':='
  240. ELSIF sym = plus THEN s := '+'
  241. ELSIF sym = minus THEN s := '-'
  242. ELSIF sym = times THEN s := '*'
  243. ELSIF sym = rdiv THEN s := '/'
  244. ELSIF sym = not THEN s := '~'
  245. ELSIF sym = arrow THEN s := '^'
  246. ELSIF sym = eot THEN s := 'end of text'
  247. ELSE s := 'Symbol #'; Int.Append(sym, s)
  248. END
  249. END SymToStr;
  250. (** Show error that something is expected, but something else found. *)
  251. PROCEDURE MarkExp(name: ARRAY OF CHAR);
  252. VAR s, word: ARRAY 256 OF CHAR;
  253. BEGIN
  254. s := name; Strings.Append(' expected, but ', s);
  255. SymToStr(sym, word); Strings.Append(word, s);
  256. Strings.Append(' found', s); Mark(s)
  257. END MarkExp;
  258. (** Show error that a module or a procedure was not closed *)
  259. PROCEDURE MarkEnd(title, name: ARRAY OF CHAR);
  260. VAR s, word: ARRAY 256 OF CHAR;
  261. BEGIN
  262. Strings.Copy(title, s); Strings.Append(' ', s); Strings.Append(name, s);
  263. Strings.Append(' is not closed.', s); Mark(s)
  264. END MarkEnd;
  265. (** Handle Comments **)
  266. (** Remove all comments from doc *)
  267. PROCEDURE ClearComments;
  268. BEGIN doc[0] := 0X; docLen := 0; docLine := -1; docEndLine := -1
  269. END ClearComments;
  270. (** Comments **)
  271. (** Appends the first comment from global variable doc to the the given string.
  272. If vertical tab exists in doc, the first comment spans from doc[0] till
  273. the first vertical tab, otherwise till the first tab or 0X character. *)
  274. PROCEDURE AppendComment(VAR comment: ARRAY OF CHAR);
  275. VAR L, i, j: INTEGER;
  276. BEGIN
  277. L := 0; WHILE (doc[L] # 0X) & (doc[L] # vtab) DO INC(L) END;
  278. IF doc[L] = 0X THEN (** Vertical tab not found, find first tab *)
  279. L := 0; WHILE (doc[L] # 0X) & (doc[L] # tab) DO INC(L) END
  280. END;
  281. j := Strings.Length(comment); i := 0;
  282. WHILE (i # L) & (j < LEN(comment) - 1) DO
  283. comment[j] := doc[i]; INC(i); INC(j)
  284. END;
  285. comment[j] := 0X;
  286. IF doc[L] = 0X THEN ClearComments
  287. ELSE Strings.Delete(doc, 0, L + 1); DEC(docLen, L + 1)
  288. END
  289. END AppendComment;
  290. (** Puts text of the last comment to varpar comment, removes it from doc,
  291. puts in its place in doc the character vtab instead of tab. *)
  292. PROCEDURE GetLastComment(VAR comment: ARRAY OF CHAR);
  293. VAR L, i, j: INTEGER;
  294. BEGIN
  295. IF docLen # 0 THEN L := docLen;
  296. WHILE (L # -1) & (doc[L] # tab) & (doc[L] # vtab) DO DEC(L) END;
  297. Strings.Extract(doc, L + 1, docLen - L - 1, comment);
  298. WHILE (L # -1) & ((doc[L] = tab) OR (doc[L] = vtab)) DO DEC(L) END;
  299. IF L # -1 THEN doc[L + 1] := vtab; doc[L + 2] := 0X; docLen := L + 2
  300. ELSE ClearComments
  301. END
  302. ELSE comment[0] := 0X
  303. END
  304. END GetLastComment;
  305. (** Join all comments and append the to the comments of o.
  306. !TODO:
  307. If tabs or vertical tabs exist in doc, they are substituted with periods,
  308. but only if the left side does not end with a punctuation mark or a comma,
  309. in which case the character is substituted with a space. *)
  310. PROCEDURE SaveAllComments(o: Object);
  311. VAR i: INTEGER;
  312. BEGIN
  313. IF o # NIL THEN
  314. i := Strings.Length(o.comment);
  315. Strings.Append(doc, o.comment); ClearComments;
  316. WHILE o.comment[i] # 0X DO
  317. IF o.comment[i] < ' ' THEN o.comment[i] := 0AX END;
  318. INC(i)
  319. END
  320. ELSE ClearComments
  321. END
  322. END SaveAllComments;
  323. (** Stores the first comment from global variable doc in the given object o,
  324. but does that only if o does not yet have a comment or if lastLine = -1 or
  325. if lastLine is equal to the line where the comment started (= docLine).
  326. Parameter lastLine should be equal to the line number of the last symbol
  327. of the declaration (the semicolon), or -1 when saving a pre-comment.
  328. See AppendComment for more info on what "the first comment" means.
  329. If comment should be saved, but o = NIL, removes the comment from doc *)
  330. PROCEDURE SaveComment(o: Object; lastLine: INTEGER);
  331. VAR s: ARRAY 4096 OF CHAR;
  332. BEGIN
  333. IF (doc[0] # 0X) & ((lastLine = -1) OR (docLine = lastLine)) THEN
  334. IF o # NIL THEN
  335. IF o.comment[0] = 0X THEN AppendComment(o.comment)
  336. ELSIF (lastLine = -1) OR (docLine = lastLine) THEN
  337. Strings.Append(0AX, o.comment); AppendComment(o.comment)
  338. END
  339. ELSE AppendComment(s)
  340. END
  341. END
  342. END SaveComment;
  343. (** Scanner **)
  344. (** Text Driver *)
  345. PROCEDURE Read;
  346. BEGIN
  347. IF c = 0AX THEN INC(line); col := 0 END;
  348. IF ~R.eof THEN Files.ReadChar(R, c); INC(col) ELSE c := 0X END
  349. END Read;
  350. PROCEDURE IsLetter*(x: CHAR): BOOLEAN;
  351. RETURN ('a' <= x) & (x <= 'z') OR ('A' <= x) & (x <= 'Z') OR (x = '_')
  352. END IsLetter;
  353. PROCEDURE IsDec*(x: CHAR): BOOLEAN;
  354. RETURN ('0' <= x) & (x <= '9') END IsDec;
  355. PROCEDURE IsHex(x: CHAR): BOOLEAN;
  356. RETURN IsDec(x) OR ('a' <= x) & (x <= 'f') OR ('A' <= x) & (x <= 'F')
  357. END IsHex;
  358. (** Also used in AutodocHtml.PrintColorValue *)
  359. PROCEDURE FromHex*(x: CHAR): INTEGER;
  360. VAR n: INTEGER;
  361. BEGIN
  362. IF ('A' <= x) & (x <= 'F') THEN n := 10 - ORD('A') + ORD(x)
  363. ELSIF ('a' <= x) & (x <= 'f') THEN n := 10 - ORD('a') + ORD(x)
  364. ELSIF ('0' <= x) & (x <= '9') THEN n := ORD(x) - ORD('0')
  365. ELSE ASSERT(FALSE)
  366. END
  367. RETURN n END FromHex;
  368. PROCEDURE ToHex(n: INTEGER): CHAR;
  369. VAR x: CHAR;
  370. BEGIN
  371. IF (0 <= n) & (n < 10) THEN x := CHR(ORD('0') + n)
  372. ELSIF (10 <= n) & (n < 16) THEN x := CHR(ORD('A') - 10 + n)
  373. ELSE ASSERT(FALSE)
  374. END
  375. RETURN x END ToHex;
  376. (** Reads a decimal or hexadecimal number (or a hexadecimal char literal),
  377. puts it in id, len, ival, sym. *)
  378. PROCEDURE ReadNumber;
  379. VAR hex, allDec, isChar: BOOLEAN;
  380. i: INTEGER;
  381. BEGIN
  382. len := 0; allDec := TRUE;
  383. REPEAT
  384. IF ~IsDec(c) THEN allDec := FALSE END;
  385. IF len < LEN(id) - 1 THEN id[len] := c; INC(len) END;
  386. Read
  387. UNTIL ~IsHex(c);
  388. IF c = '.' THEN (* Real number *)
  389. IF len < LEN(id) - 1 THEN id[len] := '.'; INC(len) END;
  390. Read;
  391. WHILE IsDec(c) DO
  392. IF len < LEN(id) - 1 THEN id[len] := c; INC(len) END;
  393. Read
  394. END;
  395. IF (c = 'E') OR (c = 'e') OR (c = 'D') OR (c = 'd') THEN
  396. IF len < LEN(id) - 1 THEN id[len] := c; INC(len) END;
  397. Read;
  398. IF (c = '+') OR (c = '-') THEN
  399. IF len < LEN(id) - 1 THEN id[len] := c; INC(len) END;
  400. Read
  401. END;
  402. WHILE IsDec(c) DO
  403. IF len < LEN(id) - 1 THEN id[len] := c; INC(len) END;
  404. Read
  405. END
  406. END;
  407. id[len] := 0X;
  408. sym := real
  409. ELSE (* Integer *)
  410. id[len] := 0X;
  411. isChar := c = 'X';
  412. IF (c = 'H') OR (c = 'X') THEN hex := TRUE; Read ELSE hex := FALSE END;
  413. ival := 0; i := 0;
  414. IF hex THEN
  415. WHILE id[i] # 0X DO ival := ival * 16 + FromHex(id[i]); INC(i) END;
  416. IF isChar THEN sym := char ELSE sym := int END
  417. ELSE
  418. WHILE id[i] # 0X DO
  419. IF IsDec(id[i]) THEN ival := ival * 10 + ORD(id[i]) - ORD('0')
  420. ELSE Mark('Not a hexadecimal number')
  421. END;
  422. INC(i)
  423. END;
  424. sym := int
  425. END
  426. END
  427. END ReadNumber;
  428. (** Добавляет литеру (перенос строки или пробел) в конец `doc` по нижеследующей
  429. схеме. Не добавляет литеру, если она уже есть на конце `doc`, но добавляет
  430. её в любом случае, если pre = TRUE.
  431. (был - что там на конце сейчас, доб - что добавляем, рез - результат)
  432. был доб рез
  433. ' ' ' ' ничего/замена
  434. ' ' 0AX замена
  435. 0AX ' ' ничего
  436. 0AX 0AX ничего/замена *)
  437. PROCEDURE AppendDocChar(x: CHAR);
  438. VAR p: CHAR;
  439. BEGIN
  440. IF pre & (x = ' ') THEN doc[docLen] := x; INC(docLen)
  441. ELSIF docLen # 0 THEN p := doc[docLen - 1];
  442. IF p > ' ' THEN doc[docLen] := x; INC(docLen)
  443. ELSIF (p # x) & (x = 0AX) THEN doc[docLen - 1] := x
  444. END
  445. END
  446. END AppendDocChar;
  447. PROCEDURE DocTrimRight;
  448. BEGIN
  449. WHILE (docLen # 0) & (doc[docLen - 1] = ' ') DO DEC(docLen) END
  450. END DocTrimRight;
  451. PROCEDURE BeginPre;
  452. BEGIN
  453. IF ~pre THEN
  454. IF docLen < LEN(doc) - 11 THEN
  455. doc[docLen] := '`'; INC(docLen); doc[docLen] := '`'; INC(docLen);
  456. doc[docLen] := '`'; INC(docLen); doc[docLen] := 0AX; INC(docLen)
  457. END;
  458. pre := TRUE
  459. END
  460. END BeginPre;
  461. PROCEDURE EndPre;
  462. BEGIN
  463. IF pre THEN
  464. IF docLen < LEN(doc) - 4 THEN
  465. doc[docLen] := 0AX; INC(docLen); doc[docLen] := '`'; INC(docLen);
  466. doc[docLen] := '`'; INC(docLen); doc[docLen] := '`'; INC(docLen);
  467. doc[docLen] := 0X
  468. END;
  469. pre := FALSE
  470. END
  471. END EndPre;
  472. (** Set language in which to get the documentation comments *)
  473. PROCEDURE SetLang*(L: ARRAY OF CHAR);
  474. BEGIN Strings.Copy(L, lang); Strings.Cap(lang)
  475. END SetLang;
  476. (** If language is not set, take it from the OS *)
  477. PROCEDURE MaybeSetLang;
  478. BEGIN
  479. IF lang[0] = 0X THEN Env.GetLang(lang); lang[2] := 0X; Strings.Cap(lang) END
  480. END MaybeSetLang;
  481. (** Attach a character to the end of comment in global varaible doc *)
  482. PROCEDURE WriteDoc(c: CHAR);
  483. VAR i: INTEGER;
  484. BEGIN
  485. IF writingDoc & (docLen < LEN(doc) - 1) THEN
  486. IF c = 0AX THEN
  487. IF ~docNewLine THEN docNewLine := TRUE
  488. ELSE AppendDocChar(0AX)
  489. END
  490. ELSIF c <= ' ' THEN AppendDocChar(' ')
  491. ELSIF docNewLine & (c = '%') & (langMark < 0) THEN (* Begin curLang mark *)
  492. AppendDocChar(0AX); AppendDocChar(' '); langMark := 0
  493. ELSIF docNewLine & (langMark >= 0) &
  494. (('A' <= c) & (c <= 'Z') OR ('a' <= c) & (c <= 'z')) THEN
  495. curLang[langMark] := CAP(c); INC(langMark); curLang[langMark] := 0X;
  496. IF langMark = 2 THEN (* End of language mark *)
  497. langMark := -1;
  498. IF curLang = lang THEN doc[0] := 0X; docLen := 0 END
  499. END;
  500. AppendDocChar(' ')
  501. ELSE
  502. IF docNewLine THEN
  503. IF (col = docCol) OR (col = 1) THEN
  504. IF pre THEN DocTrimRight; EndPre; AppendDocChar(0AX)
  505. ELSE AppendDocChar(' ')
  506. END
  507. ELSIF col = docCol + 1 THEN EndPre; AppendDocChar(0AX)
  508. ELSE DocTrimRight; AppendDocChar(0AX); BeginPre;
  509. FOR i := 1 TO col - docCol DO AppendDocChar(' ') END
  510. END;
  511. docNewLine := FALSE
  512. END;
  513. IF (curLang = lang) OR (curLang[0] = 0X) THEN
  514. doc[docLen] := c; INC(docLen)
  515. END
  516. END
  517. END
  518. END WriteDoc;
  519. (** Returns TRUE if last comment in doc needs a period in the end *)
  520. PROCEDURE NeedPeriod(): BOOLEAN;
  521. VAR x: CHAR;
  522. i: INTEGER;
  523. res: BOOLEAN;
  524. PROCEDURE IsPunctuation(x: CHAR): BOOLEAN;
  525. RETURN (x = '.') OR (x = ':') OR (x = '?') OR
  526. (x = '!') OR (x = ';') OR (x = '*')
  527. END IsPunctuation;
  528. BEGIN
  529. res := FALSE;
  530. IF docLen # 0 THEN
  531. i := docLen - 1; x := doc[i];
  532. IF ~IsPunctuation(x) & (x # ',') THEN
  533. REPEAT
  534. DEC(i);
  535. IF i # -1 THEN x := doc[i] END
  536. UNTIL (i = -1) OR (x = tab) OR (x = vtab) OR IsPunctuation(x);
  537. IF (i # -1) & (x # tab) & (x # vtab) THEN res := TRUE END
  538. END
  539. END
  540. RETURN res END NeedPeriod;
  541. (** Recursive procedure to read (potentially nested) comments.
  542. toplevel is TRUE only for the top-level comments, only the top-level
  543. comments that are opened with two stars are being saved in doc.
  544. The procedure is called at '*' that comes after '(' *)
  545. PROCEDURE ReadComment(toplevel: BOOLEAN);
  546. VAR closed, tmp: BOOLEAN;
  547. title: BOOLEAN;
  548. BEGIN
  549. IF toplevel & (docLen = 0) THEN docLine := line END;
  550. Read; closed := FALSE; writingDoc := FALSE;
  551. docNewLine := FALSE; docCol := -1; pre := FALSE;
  552. curLang[0] := 0X; langMark := -1;
  553. IF c = '*' THEN Read; (* Second star *)
  554. IF c = ')' THEN Read; closed := TRUE
  555. ELSIF toplevel THEN writingDoc := TRUE;
  556. IF (docLen # 0) & (doc[docLen - 1] # tab) & (doc[docLen - 1] # vtab) THEN
  557. doc[docLen] := tab; INC(docLen)
  558. END
  559. END
  560. END;
  561. IF ~closed THEN
  562. WHILE (c # 0X) & (c = ' ') DO Read END;
  563. docCol := col;
  564. REPEAT
  565. WHILE (c # 0X) & (c # '*') DO
  566. IF c = '(' THEN Read;
  567. IF c = '*' THEN
  568. tmp := writingDoc;
  569. ReadComment(FALSE);
  570. writingDoc := tmp
  571. ELSE WriteDoc('(')
  572. END
  573. ELSE WriteDoc(c); Read
  574. END
  575. END;
  576. IF c = '*' THEN Read;
  577. IF c # ')' THEN WriteDoc('*') END
  578. END
  579. UNTIL (c = 0X) OR (c = ')');
  580. IF toplevel THEN docEndLine := line END;
  581. IF c = ')' THEN Read END
  582. END;
  583. IF writingDoc & (docLen # 0) THEN
  584. IF doc[docLen - 1] = '*' THEN (* Title comment *)
  585. DEC(docLen); doc[docLen] := 0X; title := TRUE
  586. ELSE title := FALSE
  587. END;
  588. REPEAT DEC(docLen) UNTIL (docLen = -1) OR (doc[docLen] > ' ');
  589. INC(docLen); doc[docLen] := 0X;
  590. IF ~title & (docLen < LEN(doc) - 1) & NeedPeriod() THEN
  591. doc[docLen] := '.'; INC(docLen); doc[docLen] := 0X
  592. END;
  593. IF title THEN
  594. titleNotUsed := TRUE;
  595. IF doc[0] = 0X THEN curTitle := '-'
  596. ELSE curTitle[0] := 0X; GetLastComment(curTitle)
  597. END
  598. END
  599. END;
  600. IF pre & writingDoc THEN EndPre END;
  601. doc[docLen] := 0X
  602. END ReadComment;
  603. (** Uses global var id to set global var sym.
  604. Identifies such keywords as MODULE and BEGIN. *)
  605. PROCEDURE IdentifyKeyword;
  606. BEGIN
  607. IF id = 'MODULE' THEN sym := module
  608. ELSIF id = 'IMPORT' THEN sym := import
  609. ELSIF id = 'CONST' THEN sym := const
  610. ELSIF id = 'TYPE' THEN sym := type
  611. ELSIF id = 'VAR' THEN sym := var
  612. ELSIF id = 'IN' THEN sym := in
  613. ELSIF id = 'OUT' THEN sym := out
  614. ELSIF id = 'RECORD' THEN sym := record
  615. ELSIF id = 'ARRAY' THEN sym := array
  616. ELSIF id = 'POINTER' THEN sym := pointer
  617. ELSIF id = 'TO' THEN sym := to
  618. ELSIF id = 'OF' THEN sym := of
  619. ELSIF id = 'PROCEDURE' THEN sym := procedure
  620. ELSIF id = 'BEGIN' THEN sym := begin
  621. ELSIF id = 'END' THEN sym := end
  622. ELSIF id = 'DIV' THEN sym := div
  623. ELSIF id = 'MOD' THEN sym := mod
  624. ELSE sym := ident
  625. END
  626. END IdentifyKeyword;
  627. PROCEDURE ReadIdentOrKeyword;
  628. BEGIN
  629. len := 0;
  630. REPEAT
  631. IF len < LEN(id) - 1 THEN id[len] := c; INC(len) END;
  632. Read
  633. UNTIL ~IsLetter(c) & ~IsDec(c);
  634. id[len] := 0X;
  635. IdentifyKeyword
  636. END ReadIdentOrKeyword;
  637. PROCEDURE ReadString;
  638. VAR q: CHAR;
  639. BEGIN q := c; len := 0; Read;
  640. WHILE (c >= ' ') & (c # q) DO
  641. IF len < LEN(id) - 3 THEN
  642. id[len] := c; INC(len)
  643. END;
  644. Read
  645. END;
  646. id[len] := 0X;
  647. IF c = q THEN Read ELSE Mark('String not terminated') END;
  648. sym := string
  649. END ReadString;
  650. PROCEDURE GetSym;
  651. VAR z: ARRAY 200 OF CHAR;
  652. BEGIN
  653. sym := null;
  654. REPEAT
  655. WHILE (c # 0X) & (c <= ' ') DO Read END;
  656. IF IsLetter(c) THEN ReadIdentOrKeyword
  657. ELSIF IsDec(c) THEN ReadNumber
  658. ELSIF (c = '"') OR (c = "'") THEN ReadString
  659. ELSIF c = '+' THEN Read; sym := plus
  660. ELSIF c = '-' THEN Read; sym := minus
  661. ELSIF c = '*' THEN Read; sym := times
  662. ELSIF c = '/' THEN Read; sym := rdiv
  663. ELSIF c = '~' THEN Read; sym := not
  664. ELSIF c = ',' THEN Read; sym := comma
  665. ELSIF c = ':' THEN Read;
  666. IF c = '=' THEN Read; sym := becomes ELSE sym := colon END
  667. ELSIF c = '.' THEN Read;
  668. IF c = '.' THEN Read; sym := upto ELSE sym := period END
  669. ELSIF c = '(' THEN Read;
  670. IF c = '*' THEN ReadComment(TRUE) ELSE sym := lparen END
  671. ELSIF c = ')' THEN Read; sym := rparen
  672. ELSIF c = '[' THEN Read; sym := lbrak
  673. ELSIF c = ']' THEN Read; sym := rbrak
  674. ELSIF c = '{' THEN Read; sym := lbrace
  675. ELSIF c = '}' THEN Read; sym := rbrace
  676. ELSIF c = ';' THEN Read; sym := semicol
  677. ELSIF c = '=' THEN Read; sym := equals
  678. ELSIF c = '^' THEN Read; sym := arrow
  679. ELSIF c = 0X THEN sym := eot
  680. ELSE Read
  681. END
  682. UNTIL sym # null
  683. END GetSym;
  684. (** List **)
  685. PROCEDURE NewList(): List;
  686. VAR L: List;
  687. BEGIN NEW(L)
  688. RETURN L END NewList;
  689. PROCEDURE NewGroup(): List;
  690. VAR G: Group;
  691. i: INTEGER;
  692. BEGIN NEW(G); G.comment[0] := 0X; G.ordinalConsts := FALSE;
  693. i := 0; WHILE (curTitle[i] # 0X) & (curTitle[i] # '|') DO INC(i) END;
  694. IF curTitle[i] # 0X THEN
  695. Strings.Extract(curTitle, 0, i, G.name);
  696. Strings.Extract(curTitle, i + 1, LEN(G.comment), G.comment)
  697. ELSE
  698. Strings.Copy(curTitle, G.name);
  699. G.comment[0] := 0X
  700. END
  701. RETURN G END NewGroup;
  702. (** Returns object with the minimum name from a non-empty list L *)
  703. PROCEDURE FindMinName(L: List): Object;
  704. VAR x, min: Object;
  705. BEGIN
  706. min := L.first; x := min.next;
  707. WHILE x # NIL DO
  708. IF x.name < min.name THEN min := x END;
  709. x := x.next
  710. END
  711. RETURN min END FindMinName;
  712. (** Returns object with the minimum ordinal value from a non-empty list L *)
  713. PROCEDURE FindMinIntVal(L: List): Object;
  714. VAR x, min: Object;
  715. val, minVal: INTEGER;
  716. BEGIN
  717. min := L.first; minVal := L.first(Const).intVal; x := min.next;
  718. WHILE x # NIL DO val := x(Const).intVal;
  719. IF val < minVal THEN min := x; minVal := val END;
  720. x := x.next
  721. END
  722. RETURN min END FindMinIntVal;
  723. PROCEDURE AddToList(L: List; o: Object);
  724. BEGIN
  725. IF L.first = NIL THEN L.first := o ELSE L.last.next := o END;
  726. WHILE o.next # NIL DO o := o.next END;
  727. L.last := o
  728. END AddToList;
  729. (** Removes o from list L. *)
  730. PROCEDURE RemoveFromList(L: List; o: Object);
  731. VAR x: Object;
  732. BEGIN
  733. IF L.first = o THEN L.first := L.first.next;
  734. IF L.first = NIL THEN L.last := NIL END
  735. ELSE x := L.first;
  736. WHILE x.next # o DO x := x.next END;
  737. x.next := x.next.next;
  738. IF x.next = NIL THEN L.last := x END
  739. END;
  740. o.next := NIL
  741. END RemoveFromList;
  742. (** Moves o from list L such that L.last = o. *)
  743. PROCEDURE MoveToEndOfList(L: List; o: Object);
  744. BEGIN IF L.last # o THEN RemoveFromList(L, o); AddToList(L, o) END
  745. END MoveToEndOfList;
  746. (** Append s to dst, replacing tabs with 0AX *)
  747. PROCEDURE JoinAndAppend(s: ARRAY OF CHAR; VAR dst: ARRAY OF CHAR);
  748. VAR i, j: INTEGER;
  749. BEGIN
  750. i := 0; j := Strings.Length(dst);
  751. WHILE (s[i] # 0X) & (j < LEN(dst) - 1) DO
  752. IF s[i] < ' ' THEN dst[j] := 0AX ELSE dst[j] := s[i] END;
  753. INC(i); INC(j)
  754. END;
  755. dst[j] := 0X
  756. END JoinAndAppend;
  757. (** If L is empty, creates a group with title = curTitle in it.
  758. If L is not empty and last group's title is not curTitle,
  759. finds it in L and moves it to the last position.
  760. If it is not found, creates a new group in the end of L with
  761. title = curTitle.
  762. If a group is created or moved, saves comments in the group. If there is
  763. more then one comment, leaves the last one in doc. If there is a single
  764. comment (no tabs in doc), does not touch it in case its closing star
  765. is on the same line or exactly one line above the current line. *)
  766. PROCEDURE UpdateCurGroup(L: List);
  767. VAR x: Object;
  768. save: BOOLEAN;
  769. i: INTEGER;
  770. BEGIN x := L.first; save := TRUE;
  771. WHILE (x # NIL) & (x.name # curTitle) DO x := x.next END;
  772. IF x = NIL THEN x := NewGroup(); AddToList(L, x)
  773. ELSIF x.next # NIL THEN MoveToEndOfList(L, x)
  774. ELSE save := FALSE
  775. END;
  776. titleNotUsed := FALSE;
  777. IF save & (docLen # 0) THEN
  778. i := docLen - 1;
  779. WHILE (i # -1) & (doc[i] # tab) DO DEC(i) END;
  780. IF i # -1 THEN (* More than one comment - leave the last *)
  781. doc[i] := 0X; JoinAndAppend(doc, x.comment); doc[i] := tab;
  782. Strings.Delete(doc, 0, i + 1); DEC(docLen, i + 1)
  783. ELSIF line - docEndLine > 1 THEN (* Single comment *)
  784. JoinAndAppend(doc, x.comment); ClearComments
  785. END
  786. END
  787. END UpdateCurGroup;
  788. (** Printing **)
  789. PROCEDURE PrintIndent(n: INTEGER; inlined: BOOLEAN);
  790. BEGIN
  791. IF ~inlined THEN
  792. WHILE n > 0 DO Out.String(' '); DEC(n) END
  793. END
  794. END PrintIndent;
  795. PROCEDURE PrintComment(o: Object; indent: INTEGER);
  796. BEGIN
  797. IF o.comment[0] # 0X THEN
  798. PrintIndent(indent, FALSE);
  799. Out.String('(* '); Out.String(o.comment);
  800. Out.String(' *)'); Out.Ln
  801. END
  802. END PrintComment;
  803. PROCEDURE PrintList(L: List; indent: INTEGER; inlined: BOOLEAN);
  804. VAR o: Object;
  805. BEGIN
  806. IF (L # NIL) & (L.first # NIL) THEN
  807. IF L.comment[0] # 0X THEN
  808. Out.String('### '); Out.String(L.comment); Out.Ln
  809. END;
  810. o := L.first;
  811. WHILE o # NIL DO
  812. PrintObject(o, indent, FALSE);
  813. o := o.next
  814. END
  815. ELSE PrintIndent(indent, FALSE); Out.Char('-'); Out.Ln
  816. END
  817. END PrintList;
  818. PROCEDURE PrintConst(C: Const; indent: INTEGER; inlined: BOOLEAN);
  819. BEGIN
  820. PrintIndent(indent, inlined);
  821. Out.String('Const '); Out.String(C.name);
  822. Out.String(' with value '); Out.String(C.value); Out.Ln;
  823. PrintComment(C, indent)
  824. END PrintConst;
  825. PROCEDURE PrintParam(par: Param; indent: INTEGER; inlined: BOOLEAN);
  826. BEGIN
  827. PrintIndent(indent, inlined);
  828. IF par.passed = byVar THEN Out.String('Variable')
  829. ELSIF par.passed = byValue THEN Out.String('Value')
  830. END;
  831. Out.String(' parameter '); Out.String(par.name);
  832. Out.String(' of '); PrintObject(par.type, indent, TRUE)
  833. END PrintParam;
  834. PROCEDURE PrintVar(v: Var; indent: INTEGER; inlined: BOOLEAN);
  835. BEGIN
  836. PrintIndent(indent, inlined);
  837. Out.String(v.name);
  838. Out.String(' of '); PrintObject(v.type, indent, TRUE);
  839. IF ~inlined & (v.comment[0] # 0X) THEN Out.Ln END;
  840. PrintComment(v, indent)
  841. END PrintVar;
  842. PROCEDURE PrintType(T: Type; indent: INTEGER; inlined: BOOLEAN);
  843. VAR x: Object;
  844. BEGIN
  845. PrintIndent(indent, inlined);
  846. IF T = NIL THEN Out.String('NIL')
  847. ELSIF T.form = namedType THEN
  848. Out.String('type '); Out.String(T.name);
  849. IF T.base # NIL THEN
  850. Out.String(' is '); PrintType(T.base, indent, TRUE)
  851. END
  852. ELSIF T.form = arrayType THEN
  853. IF T.len[0] = 0X THEN Out.String('open ') END;
  854. Out.String('array type ');
  855. IF T.len[0] # 0X THEN Out.String('with length ');
  856. Out.String(T.len); Out.Char(' ')
  857. END;
  858. Out.String('of '); PrintObject(T.base, indent, TRUE)
  859. ELSIF T.form = recordType THEN Out.String('record type ');
  860. IF T.base # NIL THEN Out.String('that extends ');
  861. Out.String(T.base.name); Out.Char(' ')
  862. END;
  863. IF T.fields.first # NIL THEN Out.String('with fields:'); Out.Ln;
  864. PrintList(T.fields, indent + 1, FALSE)
  865. ELSE Out.String('with no fields')
  866. END
  867. ELSIF T.form = procedureType THEN Out.String('procedure type ');
  868. IF T.fields.first # NIL THEN
  869. PrintIndent(indent, FALSE); Out.Char('(');
  870. PrintList(T.fields, indent + 1, TRUE);
  871. Out.String(') ')
  872. END;
  873. IF T.base # NIL THEN
  874. Out.String('that returns '); PrintObject(T.base, indent, TRUE)
  875. END
  876. ELSIF T.form = pointerType THEN Out.String('pointer type to ');
  877. PrintObject(T.base, indent, TRUE)
  878. ELSE Out.String('?')
  879. END;
  880. IF ~inlined THEN Out.Ln; PrintComment(T, indent) END
  881. END PrintType;
  882. PROCEDURE PrintProcedure(P: Procedure; indent: INTEGER; inlined: BOOLEAN);
  883. BEGIN
  884. PrintIndent(indent, inlined);
  885. Out.String('Procedure '); Out.String(P.name);
  886. IF P.returnType # NIL THEN
  887. Out.String(' returns '); PrintType(P.returnType, indent, TRUE)
  888. END;
  889. IF P.params.first # NIL THEN
  890. Out.String(', parameters:'); Out.Ln;
  891. PrintList(P.params, indent + 1, FALSE)
  892. ELSE Out.Ln
  893. END;
  894. IF ~inlined THEN Out.Ln; PrintComment(P, indent) END
  895. END PrintProcedure;
  896. PROCEDURE PrintModule(M: Module; indent: INTEGER; inlined: BOOLEAN);
  897. BEGIN
  898. PrintIndent(indent, inlined);
  899. Out.String('Module '); Out.String(M.name); Out.Ln;
  900. PrintComment(M, indent);
  901. PrintIndent(indent, FALSE);
  902. Out.String('Constants:'); Out.Ln; PrintList(M.consts, indent + 1, FALSE);
  903. PrintIndent(indent, FALSE);
  904. Out.String('Types:'); Out.Ln; PrintList(M.types, indent + 1, FALSE);
  905. PrintIndent(indent, FALSE);
  906. Out.String('Variables:'); Out.Ln; PrintList(M.vars, indent + 1, FALSE);
  907. PrintIndent(indent, FALSE);
  908. Out.String('Procedures:'); Out.Ln; PrintList(M.procedures, indent + 1, FALSE)
  909. END PrintModule;
  910. PROCEDURE PrintObject0(o: Object; indent: INTEGER; inlined: BOOLEAN);
  911. BEGIN
  912. IF o = NIL THEN PrintIndent(indent, inlined); Out.String('NIL')
  913. ELSIF o IS Module THEN PrintModule(o(Module), indent, inlined)
  914. ELSIF o IS Var THEN PrintVar(o(Var), indent, inlined)
  915. ELSIF o IS Const THEN PrintConst(o(Const), indent, inlined)
  916. ELSIF o IS Type THEN PrintType(o(Type), indent, inlined)
  917. ELSIF o IS Procedure THEN PrintProcedure(o(Procedure), indent, inlined)
  918. ELSIF o IS Param THEN PrintParam(o(Param), indent, inlined)
  919. ELSIF o IS List THEN PrintList(o(List), indent, inlined)
  920. ELSE PrintIndent(indent, inlined); Out.String('?')
  921. END;
  922. IF ~inlined THEN Out.Ln END
  923. END PrintObject0;
  924. PROCEDURE Print*(o: Object);
  925. BEGIN PrintObject(o, 0, FALSE)
  926. END Print;
  927. (** Object **)
  928. PROCEDURE InitObject(o: Object);
  929. BEGIN o.name[0] := 0X; o.comment[0] := 0X; o.next := NIL; o.exported := FALSE
  930. END InitObject;
  931. (** Sets exported field of object to TRUE or FALSE
  932. and skips the star (or minus) mark. *)
  933. PROCEDURE CheckExportMark(o: Object);
  934. BEGIN
  935. IF (sym = times) OR (sym = minus) THEN GetSym; o.exported := TRUE
  936. ELSE o.exported := FALSE
  937. END;
  938. objectIsExported := o.exported
  939. END CheckExportMark;
  940. (** Skips compiler directives such as [notag]
  941. after POINTER, ARRAY and RECORD symbols.
  942. Does not change o in any way (yet). *)
  943. PROCEDURE CheckDirective(o: Object);
  944. BEGIN
  945. IF sym = lbrak THEN GetSym;
  946. IF (sym = ident) OR (sym = int) THEN GetSym;
  947. IF sym = rbrak THEN GetSym END
  948. END
  949. END
  950. END CheckDirective;
  951. (** Finds import with the given alias in curModule. If parameter exported
  952. is TRUE, marks the import object as exported. Depending on keepAliases,
  953. on may replace value of VAR-parameter name from with the real name of
  954. the imported module. *)
  955. PROCEDURE CheckImportedModule(VAR name: ARRAY OF CHAR; exported: BOOLEAN);
  956. VAR x: Object;
  957. BEGIN
  958. x := curModule.imports.first;
  959. WHILE (x # NIL) & (x(Import).alias # name) DO x := x.next END;
  960. IF x # NIL THEN
  961. IF exported THEN x.exported := TRUE END;
  962. IF ~keepAliases THEN Strings.Copy(x.name, name) END
  963. ELSE Mark3('Module "', name, '" not imported.')
  964. END
  965. END CheckImportedModule;
  966. (** Type **)
  967. PROCEDURE NewType(form: INTEGER): Type;
  968. VAR T: Type;
  969. BEGIN NEW(T); T.form := form; T.len[0] := 0X; T.base := NIL
  970. RETURN T END NewType;
  971. (** Param **)
  972. PROCEDURE NewParam(passed: INTEGER): Param;
  973. VAR par: Param;
  974. BEGIN NEW(par); InitObject(par); par.passed := passed; Strings.Copy(id, par.name)
  975. RETURN par END NewParam;
  976. (** Import **)
  977. PROCEDURE NewImport(): Import;
  978. VAR I: Import;
  979. BEGIN NEW(I); InitObject(I); Strings.Copy(id, I.name)
  980. RETURN I END NewImport;
  981. (** Const **)
  982. PROCEDURE NewConst(): Const;
  983. VAR C: Const;
  984. BEGIN NEW(C); InitObject(C); Strings.Copy(id, C.name);
  985. C.isOrdinal := FALSE; C.intVal := 0
  986. RETURN C END NewConst;
  987. (** Var **)
  988. PROCEDURE NewVar(): Var;
  989. VAR v: Var;
  990. BEGIN NEW(v); InitObject(v); Strings.Copy(id, v.name)
  991. RETURN v END NewVar;
  992. (** Parser **)
  993. PROCEDURE ConstructString(VAR s: ARRAY OF CHAR);
  994. VAR i: INTEGER;
  995. x: CHAR;
  996. BEGIN i := 0; x := id[0];
  997. WHILE (x # 0X) & (x # "'") DO INC(i); x := id[i] END;
  998. IF x # 0X THEN x := '"' ELSE x := "'" END;
  999. s[0] := x; i := 0;
  1000. WHILE id[i] # 0X DO s[i + 1] := id[i]; INC(i) END;
  1001. s[i + 1] := x; s[i + 2] := 0X
  1002. END ConstructString;
  1003. PROCEDURE ConstructChar(VAR s: ARRAY OF CHAR);
  1004. VAR i, n: INTEGER;
  1005. x: CHAR;
  1006. BEGIN n := ival; i := 0;
  1007. REPEAT s[i] := ToHex(n MOD 16); n := n DIV 16; INC(i) UNTIL n = 0;
  1008. s[i] := 'X'; s[i + 1] := 0X; DEC(i);
  1009. WHILE n < i DO x := s[i]; s[i] := s[n]; s[n] := x; INC(n); DEC(i) END
  1010. END ConstructChar;
  1011. (** Reads const expression character by character, beginning with the
  1012. character at position constExprBeginPos and up to but not including
  1013. the next ';', comma or 'OF'. If end of text is reached, makes s empty.
  1014. Puts in s the string that has been read. Sets isOrdinal to TRUE if
  1015. the value is an integer number or a character literal, sets it to FALSE
  1016. otherwise. If isOrdinal becomes TRUE, then the copy of the const value is
  1017. cast to integer and stored in intVal. *)
  1018. PROCEDURE ParseConstExpr(VAR s: ARRAY OF CHAR;
  1019. VAR isOrdinal: BOOLEAN; VAR intVal: INTEGER);
  1020. VAR start, end, i, tmpCol, tmpLine: INTEGER;
  1021. x, tmpC: CHAR;
  1022. BEGIN isOrdinal := FALSE; intVal := 0; i := 0; x := constExprBeginC;
  1023. REPEAT end := Files.Pos(R); tmpC := c; tmpCol := col; tmpLine := line; GetSym
  1024. UNTIL (sym = semicol) OR (sym = of) OR (sym = eot) OR (sym = comma);
  1025. IF sym # eot THEN
  1026. IF constExprBeginPos < end THEN
  1027. IF x > ' ' THEN s[i] := x; INC(i) END;
  1028. Files.Set(R, Files.Base(R), constExprBeginPos);
  1029. REPEAT
  1030. Files.ReadChar(R, x);
  1031. IF x < ' ' THEN x := ' ' END;
  1032. IF (i < LEN(s) - 1) & ((x # ' ') OR (i # 0) & (s[i - 1] # ' ')) THEN
  1033. s[i] := x; INC(i)
  1034. END
  1035. UNTIL Files.Pos(R) >= end;
  1036. IF i > 0 THEN DEC(i) END
  1037. END;
  1038. Files.Set(R, Files.Base(R), end);
  1039. c := tmpC; col := tmpCol; line := tmpLine;
  1040. GetSym
  1041. END;
  1042. WHILE (i # 1) & (s[i - 1] <= ' ') DO DEC(i) END;
  1043. s[i] := 0X
  1044. END ParseConstExpr;
  1045. PROCEDURE ParseVars(isVarDecl: BOOLEAN): List;
  1046. VAR first, v: Var;
  1047. L: List;
  1048. x: Object;
  1049. passed, line2: INTEGER;
  1050. T: Type;
  1051. stop, added: BOOLEAN;
  1052. BEGIN L := NewList(); stop := FALSE;
  1053. WHILE ~stop & (sym = ident) DO Debug(id);
  1054. IF isVarDecl THEN UpdateCurGroup(L) END;
  1055. first := NewVar(); SaveAllComments(first); GetSym; CheckExportMark(first);
  1056. IF first.exported OR ~exportedOnly THEN
  1057. IF isVarDecl THEN AddToList(L.last(List), first)
  1058. ELSE AddToList(L, first)
  1059. END;
  1060. added := TRUE
  1061. ELSE added := FALSE; first := NIL
  1062. END;
  1063. WHILE sym = comma DO GetSym;
  1064. IF sym = ident THEN v := NewVar(); GetSym; CheckExportMark(v);
  1065. IF v.exported OR ~exportedOnly THEN
  1066. IF isVarDecl THEN AddToList(L.last(List), v)
  1067. ELSE AddToList(L, v)
  1068. END;
  1069. IF ~added THEN first := v; added := TRUE END
  1070. END
  1071. ELSE MarkExp('variable (field) name')
  1072. END
  1073. END;
  1074. IF sym = colon THEN GetSym ELSE MarkExp(':') END;
  1075. T := ParseType(NIL);
  1076. IF first # NIL THEN
  1077. first.type := T; x := first.next;
  1078. WHILE x # NIL DO x(Var).type := T; x := x.next END
  1079. END;
  1080. IF (sym = semicol) OR ~isVarDecl THEN line2 := line;
  1081. IF sym = semicol THEN GetSym; SaveComment(first, line2)
  1082. ELSE stop := TRUE; SaveAllComments(first)
  1083. END;
  1084. IF (first # NIL) & (first.comment[0] # 0X) THEN x := first.next;
  1085. WHILE x # NIL DO
  1086. Strings.Copy(first.comment, x.comment); x := x.next
  1087. END
  1088. END
  1089. ELSE MarkExp(';')
  1090. END
  1091. END;
  1092. RETURN L END ParseVars;
  1093. (** Sets C.isOrdinal to TRUE if C.value is a single character literal in
  1094. the form of 'x', "x" or 4AX or if C.value is an integer (dec, hex). *)
  1095. PROCEDURE CheckOrdinal(C: Const);
  1096. VAR x: CHAR;
  1097. PROCEDURE IsInt(s: ARRAY OF CHAR; VAR val: INTEGER): BOOLEAN;
  1098. VAR i, start: INTEGER;
  1099. minus, ok: BOOLEAN;
  1100. end: CHAR;
  1101. BEGIN val := 0; start := 0; minus := FALSE; ok := TRUE;
  1102. IF s[0] = '-' THEN minus := TRUE; start := 1
  1103. ELSIF s[0] = '+' THEN start := 1
  1104. END;
  1105. i := start;
  1106. WHILE IsHex(s[i]) DO INC(i) END; end := s[i];
  1107. IF ((end = 'X') OR (end = 'H')) & (s[i + 1] = 0X) THEN i := 0;
  1108. WHILE s[i] # end DO val := val * 16 + FromHex(s[i]); INC(i) END
  1109. ELSIF s[i] = 0X THEN i := 0;
  1110. WHILE s[i] # end DO val := val * 10 + ORD(s[i]) - ORD('0'); INC(i) END
  1111. ELSE ok := FALSE
  1112. END;
  1113. IF minus THEN val := -val END
  1114. RETURN ok & (s[0] # 0X) END IsInt;
  1115. BEGIN
  1116. IF ~C.isOrdinal THEN x := C.value[0];
  1117. (* Literal char 'x' or "x" *)
  1118. IF ((x = '"') OR (x = "'")) & (C.value[1] # 0X) & (C.value[2] = x) THEN
  1119. C.isOrdinal := TRUE; C.intVal := ORD(C.value[1])
  1120. ELSIF IsInt(C.value, C.intVal) THEN C.isOrdinal := TRUE
  1121. END
  1122. END
  1123. END CheckOrdinal;
  1124. PROCEDURE ParseConstDecl(M: Module);
  1125. VAR C: Const;
  1126. line2: INTEGER;
  1127. isInt: BOOLEAN;
  1128. BEGIN curTitle := '-';
  1129. IF sym = const THEN GetSym;
  1130. WHILE sym = ident DO Debug(id);
  1131. UpdateCurGroup(M.consts);
  1132. C := NewConst();
  1133. (* Сохранить все комментарии *)
  1134. SaveComment(C, -1);
  1135. GetSym; CheckExportMark(C);
  1136. IF C.exported OR ~exportedOnly THEN
  1137. AddToList(M.consts.last(List), C)
  1138. END;
  1139. constExprBeginPos := Files.Pos(R); constExprBeginC := c;
  1140. IF sym = equals THEN GetSym ELSE MarkExp('=') END;
  1141. ParseConstExpr(C.value, C.isOrdinal, C.intVal); CheckOrdinal(C);
  1142. line2 := line;
  1143. IF sym = semicol THEN GetSym ELSE MarkExp(';') END;
  1144. (* СОХРАНИТЬ ВСЕ КОММЕНТАРИИ ДО [ВЕРТ.ТАБА, если он есть, иначе до ТАБА],
  1145. ЕСЛИ (line2 совпадает) ИЛИ (у C нет комментария) *)
  1146. SaveComment(C, line2)
  1147. END
  1148. END
  1149. END ParseConstDecl;
  1150. PROCEDURE ParseTypeDecl(M: Module);
  1151. VAR T: Type;
  1152. line2: INTEGER;
  1153. BEGIN
  1154. IF sym = type THEN GetSym;
  1155. WHILE sym = ident DO Debug(id);
  1156. UpdateCurGroup(M.types);
  1157. T := NewType(namedType); SaveAllComments(T);
  1158. Strings.Copy(id, T.name); GetSym; CheckExportMark(T);
  1159. IF sym = equals THEN GetSym ELSE MarkExp('=') END;
  1160. T.base := ParseType(T); line2 := line;
  1161. IF sym = semicol THEN GetSym ELSE MarkExp(';') END;
  1162. IF ~exportedOnly OR T.exported THEN
  1163. AddToList(M.types.last(List), T)
  1164. END;
  1165. SaveComment(T, line2)
  1166. END
  1167. END
  1168. END ParseTypeDecl;
  1169. PROCEDURE ParseNamedType(): Type;
  1170. VAR T: Type;
  1171. BEGIN
  1172. IF sym = ident THEN
  1173. T := NewType(namedType);
  1174. Strings.Copy(id, T.name); GetSym;
  1175. IF sym = period THEN GetSym;
  1176. IF sym = ident THEN
  1177. CheckImportedModule(T.name, objectIsExported);
  1178. Strings.Append('.', T.name); Strings.Append(id, T.name); GetSym
  1179. ELSE MarkExp('identifier')
  1180. END
  1181. END
  1182. ELSE T := NIL; MarkExp('type identifier')
  1183. END
  1184. RETURN T END ParseNamedType;
  1185. PROCEDURE ParseArrayType(): Type;
  1186. VAR T, T1: Type;
  1187. isInt: BOOLEAN;
  1188. tmp: INTEGER;
  1189. BEGIN ASSERT(sym = array);
  1190. constExprBeginPos := Files.Pos(R); constExprBeginC := c;
  1191. GetSym;
  1192. T := NewType(arrayType); T1 := T; CheckDirective(T);
  1193. IF (sym # of) THEN
  1194. ParseConstExpr(T.len, isInt, tmp)
  1195. END;
  1196. WHILE sym = comma DO
  1197. constExprBeginPos := Files.Pos(R); constExprBeginC := c; GetSym;
  1198. T1.base := NewType(arrayType); T1 := T1.base;
  1199. ParseConstExpr(T1.len, isInt, tmp)
  1200. END;
  1201. IF sym = of THEN GetSym ELSE MarkExp('OF') END;
  1202. T1.base := ParseType(NIL)
  1203. RETURN T END ParseArrayType;
  1204. PROCEDURE ParseRecordType(docObj: Object): Type;
  1205. VAR T: Type;
  1206. line2: INTEGER;
  1207. BEGIN ASSERT(sym = record); line2 := line; GetSym;
  1208. T := NewType(recordType); CheckDirective(T);
  1209. IF sym = lparen THEN GetSym; T.base := ParseNamedType();
  1210. IF sym = rparen THEN GetSym ELSE MarkExp(')') END
  1211. END;
  1212. SaveComment(docObj, line2);
  1213. T.fields := ParseVars(FALSE);
  1214. IF sym = end THEN GetSym ELSE MarkExp('END') END
  1215. RETURN T END ParseRecordType;
  1216. PROCEDURE ParsePointerType(docObj: Object): Type;
  1217. VAR T: Type;
  1218. BEGIN ASSERT(sym = pointer); GetSym;
  1219. T := NewType(pointerType); CheckDirective(T);
  1220. IF sym = to THEN GetSym ELSE MarkExp('TO') END;
  1221. T.base := ParseType(docObj)
  1222. RETURN T END ParsePointerType;
  1223. PROCEDURE ParseFormalParamSection(L: List);
  1224. VAR first, par: Param;
  1225. x: Object;
  1226. T: Type;
  1227. passed: INTEGER;
  1228. BEGIN
  1229. IF (sym = var) OR (sym = in) OR (sym = out) THEN GetSym; passed := byVar;
  1230. IF sym = lbrak THEN GetSym;
  1231. IF (sym = ident) OR (sym = int) THEN GetSym ELSE MarkExp('hint') END;
  1232. IF sym = rbrak THEN GetSym ELSE MarkExp(']') END
  1233. END
  1234. ELSE passed := byValue
  1235. END;
  1236. IF sym = ident THEN first := NewParam(passed); GetSym;
  1237. AddToList(L, first);
  1238. WHILE sym = comma DO GetSym;
  1239. IF sym = ident THEN par := NewParam(passed); GetSym;
  1240. AddToList(L, par)
  1241. ELSE MarkExp('parameter name')
  1242. END
  1243. END
  1244. ELSE first := NIL; MarkExp('parameter name')
  1245. END;
  1246. IF sym = colon THEN GetSym; T := ParseParamType();
  1247. IF first # NIL THEN
  1248. first.type := T;
  1249. x := first.next;
  1250. WHILE x # NIL DO x(Param).type := T; x := x.next END
  1251. END
  1252. ELSE MarkExp(':')
  1253. END
  1254. END ParseFormalParamSection;
  1255. PROCEDURE ParseProcedureType(): Type;
  1256. VAR T: Type;
  1257. BEGIN ASSERT(sym = procedure); GetSym;
  1258. T := NewType(procedureType); T.fields := NewList();
  1259. IF sym = lparen THEN GetSym;
  1260. IF sym # rparen THEN ParseFormalParamSection(T.fields);
  1261. WHILE sym = semicol DO GetSym; ParseFormalParamSection(T.fields) END
  1262. END;
  1263. IF sym = rparen THEN GetSym ELSE MarkExp(')') END;
  1264. IF sym = colon THEN GetSym; T.base := ParseNamedType() END
  1265. END;
  1266. (*!TODO*)
  1267. RETURN T END ParseProcedureType;
  1268. PROCEDURE ParseParamType0(): Type;
  1269. VAR T: Type;
  1270. BEGIN
  1271. IF sym = array THEN T := ParseArrayType()
  1272. ELSIF sym = ident THEN T := ParseNamedType()
  1273. ELSIF sym = procedure THEN T := ParseProcedureType()
  1274. ELSE T := NIL; MarkExp('type')
  1275. END
  1276. RETURN T END ParseParamType0;
  1277. PROCEDURE ParseType0(docObj: Object): Type;
  1278. VAR T: Type;
  1279. BEGIN
  1280. IF sym = array THEN T := ParseArrayType()
  1281. ELSIF sym = record THEN T := ParseRecordType(docObj)
  1282. ELSIF sym = pointer THEN T := ParsePointerType(docObj)
  1283. ELSIF sym = procedure THEN T := ParseProcedureType()
  1284. ELSIF sym = ident THEN T := ParseNamedType()
  1285. ELSE T := NIL; MarkExp('type')
  1286. END
  1287. RETURN T END ParseType0;
  1288. (** Reads input stream until "END name" is found.
  1289. Stops on "name" (sym = ident), or sym = eot *)
  1290. PROCEDURE ReachEndOf(name: ARRAY OF CHAR);
  1291. BEGIN
  1292. REPEAT
  1293. WHILE (sym # eot) & (sym # end) DO GetSym END;
  1294. IF sym = end THEN GetSym END
  1295. UNTIL (sym = eot) OR (sym = ident) & (id = name)
  1296. END ReachEndOf;
  1297. PROCEDURE ParseProcedureDecl(M: Module);
  1298. VAR name: Str;
  1299. P: Procedure;
  1300. forward, foreign: BOOLEAN;
  1301. BEGIN
  1302. IF ~titleNotUsed THEN curTitle := '-' END;
  1303. WHILE sym = procedure DO UpdateCurGroup(M.procedures);
  1304. NEW(P); InitObject(P); SaveAllComments(P); GetSym; foreign := FALSE;
  1305. forward := FALSE; P.params := NewList(); P.exported := FALSE;
  1306. P.external := FALSE; P.modifier[0] := 0X; P.code[0] := 0X;
  1307. IF sym = lparen THEN NEW(P.receiver); InitObject(P.receiver); GetSym;
  1308. NEW(P.receiver.type); InitObject(P.receiver.type);
  1309. IF sym = var THEN GetSym; P.receiver.passed := byVar
  1310. ELSE P.receiver.passed := byValue
  1311. END;
  1312. IF sym = ident THEN Strings.Copy(id, P.receiver.name); GetSym
  1313. ELSE MarkExp('receiver name')
  1314. END;
  1315. IF sym = colon THEN GetSym ELSE MarkExp(':') END;
  1316. IF sym = ident THEN Strings.Copy(id, P.receiver.type.name); GetSym;
  1317. P.receiver.type.len[0] := 0X;
  1318. P.receiver.type.form := namedType
  1319. ELSE MarkExp('receiver name')
  1320. END;
  1321. IF sym = rparen THEN GetSym ELSE MarkExp(')') END
  1322. END;
  1323. IF sym = minus THEN GetSym; P.external := TRUE
  1324. ELSIF sym = arrow THEN GetSym; forward := TRUE
  1325. ELSIF sym = times THEN GetSym
  1326. END;
  1327. IF sym = ident THEN Strings.Copy(id, P.name); GetSym
  1328. ELSE MarkExp('procedure name')
  1329. END;
  1330. IF (sym = minus) OR (sym = arrow) THEN GetSym END;
  1331. IF sym = times THEN GetSym; P.exported := TRUE END;
  1332. IF sym = lbrak THEN GetSym; (* Foreign name *)
  1333. foreign := TRUE;
  1334. IF sym = string THEN GetSym
  1335. ELSE MarkExp('foreign name of procedure')
  1336. END;
  1337. IF sym = rbrak THEN GetSym ELSE MarkExp(']') END
  1338. END;
  1339. IF sym = lparen THEN GetSym;
  1340. IF sym # rparen THEN ParseFormalParamSection(P.params);
  1341. WHILE sym = semicol DO GetSym; ParseFormalParamSection(P.params) END
  1342. END;
  1343. IF sym = rparen THEN GetSym ELSE MarkExp(')') END;
  1344. IF sym = colon THEN GetSym; P.returnType := ParseNamedType() END
  1345. END;
  1346. IF (sym = comma) & (P.receiver # NIL) THEN GetSym;
  1347. IF sym = ident THEN Strings.Copy(id, P.modifier); GetSym END
  1348. END;
  1349. IF P.external & (sym = string) THEN Strings.Copy(id, P.code); GetSym END;
  1350. IF sym = semicol THEN GetSym ELSE MarkExp(';') END;
  1351. IF ~forward & ~foreign & ~P.external THEN
  1352. ReachEndOf(P.name); SaveAllComments(P);
  1353. IF sym = ident THEN GetSym;
  1354. IF sym = semicol THEN GetSym ELSE MarkExp(';') END
  1355. ELSE (* sym = eot *) MarkEnd('Procedure', P.name)
  1356. END
  1357. END;
  1358. IF P.exported OR ~exportedOnly THEN
  1359. AddToList(M.procedures.last(List), P)
  1360. END
  1361. END
  1362. END ParseProcedureDecl;
  1363. PROCEDURE ParseVarDecl(M: Module);
  1364. BEGIN ASSERT(sym = var); curTitle := '-';
  1365. GetSym; M.vars := ParseVars(TRUE)
  1366. END ParseVarDecl;
  1367. PROCEDURE Declarations(M: Module);
  1368. BEGIN
  1369. titleNotUsed := TRUE;
  1370. IF sym = const THEN ParseConstDecl(M) END;
  1371. IF sym = type THEN ParseTypeDecl(M) END;
  1372. IF sym = var THEN ParseVarDecl(M) END;
  1373. ParseProcedureDecl(M)
  1374. END Declarations;
  1375. PROCEDURE ParseImport(M: Module);
  1376. VAR I: Import;
  1377. BEGIN
  1378. IF sym = ident THEN
  1379. I := NewImport(); GetSym;
  1380. Strings.Copy(I.name, I.alias);
  1381. IF sym = becomes THEN GetSym;
  1382. Strings.Copy(id, I.name); GetSym
  1383. END;
  1384. AddToList(M.imports, I)
  1385. END
  1386. END ParseImport;
  1387. PROCEDURE ParseImportList(M: Module);
  1388. BEGIN
  1389. IF sym = import THEN
  1390. GetSym; ParseImport(M);
  1391. WHILE sym = comma DO GetSym; ParseImport(M) END;
  1392. IF sym = semicol THEN GetSym ELSE MarkExp(';') END
  1393. END
  1394. END ParseImportList;
  1395. PROCEDURE CleanImportList(M: Module);
  1396. VAR x, next: Object;
  1397. BEGIN
  1398. x := M.imports.first;
  1399. WHILE x # NIL DO next := x.next;
  1400. IF ~x.exported THEN RemoveFromList(M.imports, x)
  1401. ELSIF ~keepAliases THEN Strings.Copy(x.name, x(Import).alias)
  1402. END;
  1403. x := next
  1404. END
  1405. END CleanImportList;
  1406. PROCEDURE FindMin(G: Group; ordinal: BOOLEAN): Object;
  1407. VAR x: Object;
  1408. BEGIN
  1409. IF ordinal THEN x := FindMinIntVal(G) ELSE x := FindMinName(G) END
  1410. RETURN x END FindMin;
  1411. PROCEDURE GroupCheckOrdinalConsts(G: Group);
  1412. VAR x: Object;
  1413. BEGIN
  1414. IF (G.name[0] # 0X) & (G.first # NIL) & (G.first IS Const) THEN x := G.first;
  1415. WHILE (x # NIL) & x(Const).isOrdinal DO x := x.next END;
  1416. G.ordinalConsts := x = NIL
  1417. ELSE G.ordinalConsts := FALSE
  1418. END
  1419. END GroupCheckOrdinalConsts;
  1420. PROCEDURE SortGroup(G: Group);
  1421. VAR x: Object;
  1422. L: List;
  1423. ordinal: BOOLEAN;
  1424. BEGIN
  1425. IF G.first # NIL THEN L := NewList();
  1426. GroupCheckOrdinalConsts(G);
  1427. WHILE G.first # NIL DO
  1428. x := FindMin(G, G.ordinalConsts);
  1429. RemoveFromList(G, x);
  1430. AddToList(L, x)
  1431. END;
  1432. G.first := L.first; G.last := L.last
  1433. END
  1434. END SortGroup;
  1435. PROCEDURE SortGroups(L: List);
  1436. VAR x: Object;
  1437. common: Group;
  1438. BEGIN
  1439. IF (L # NIL) & (L.first # NIL) THEN
  1440. common := NIL; x := L.first;
  1441. WHILE x # NIL DO
  1442. SortGroup(x(Group));
  1443. IF x.name = '-' THEN common := x(Group) END;
  1444. x := x.next
  1445. END;
  1446. IF (common # NIL) & (common # L.first) THEN
  1447. x := L.first; WHILE x.next # common DO x := x.next END;
  1448. x.next := common.next;
  1449. common.next := L.first;
  1450. L.first := common
  1451. END
  1452. END
  1453. END SortGroups;
  1454. PROCEDURE SortModule(M: Module);
  1455. BEGIN
  1456. SortGroups(M.consts);
  1457. SortGroups(M.vars);
  1458. (* SortGroups(M.types); *)
  1459. SortGroups(M.procedures)
  1460. END SortModule;
  1461. PROCEDURE ParseModule*(VAR r: Files.Rider; VAR err: ARRAY OF CHAR): Module;
  1462. VAR M: Module;
  1463. BEGIN NEW(M); InitObject(M); curModule := M; MaybeSetLang;
  1464. M.foreign := FALSE; M.exportedOnly := exportedOnly;
  1465. M.imports := NewList(); M.consts := NewList();
  1466. M.types := NewList(); M.vars := NewList(); M.procedures := NewList();
  1467. R := r; c := 0X; line := 1; col := 0; lastError := -1;
  1468. objectIsExported := FALSE;
  1469. Read; ClearComments; curTitle := '-'; GetSym;
  1470. IF sym = module THEN GetSym;
  1471. IF sym = lbrak THEN GetSym;
  1472. IF (sym = ident) & (id = 'foreign') THEN M.foreign := TRUE END;
  1473. REPEAT GetSym UNTIL (sym = eot) OR (sym = rbrak);
  1474. GetSym
  1475. END;
  1476. IF sym = ident THEN Strings.Copy(id, M.name); GetSym
  1477. ELSE MarkExp('module name')
  1478. END;
  1479. IF sym = semicol THEN GetSym ELSE MarkExp(';') END;
  1480. SaveAllComments(M);
  1481. ParseImportList(M);
  1482. Declarations(M);
  1483. IF sym = begin THEN
  1484. REPEAT GetSym UNTIL (sym = eot) OR (sym = end)
  1485. END;
  1486. ReachEndOf(M.name);
  1487. IF sym = ident THEN GetSym;
  1488. IF sym # period THEN MarkExp('.') END
  1489. ELSE (* sym = eot *) MarkEnd('Module', M.name)
  1490. END
  1491. ELSE MarkExp('MODULE')
  1492. END;
  1493. IF exportedOnly THEN CleanImportList(M) END;
  1494. IF lastError = -1 THEN SortModule(M)
  1495. ELSE M := NIL; err := 'Error' (*!FIXME*)
  1496. END
  1497. RETURN M END ParseModule;
  1498. BEGIN
  1499. PrintObject := PrintObject0;
  1500. ParseType := ParseType0;
  1501. ParseParamType := ParseParamType0;
  1502. curFname[0] := 0X; lang[0] := 0X; debug := FALSE;
  1503. exportedOnly := TRUE; keepAliases := FALSE
  1504. END AutodocParser.