2
0

AutodocParser.Mod 35 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235
  1. MODULE AutodocParser;
  2. IMPORT Files, Texts, Out, Strings, Platform, Int;
  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. record = 15;
  18. array = 16;
  19. pointer = 17;
  20. to = 18;
  21. of = 19;
  22. procedure = 20;
  23. begin = 21;
  24. end = 22;
  25. lparen = 30;
  26. rparen = 31;
  27. lbrak = 32;
  28. rbrak = 33;
  29. lbrace = 34;
  30. rbrace = 35;
  31. period = 36;
  32. comma = 37;
  33. upto = 38;
  34. colon = 39;
  35. semicol = 40;
  36. equals = 41;
  37. becomes = 42;
  38. plus = 43;
  39. minus = 44;
  40. times = 45;
  41. div = 46;
  42. mod = 47;
  43. rdiv = 48;
  44. not = 49;
  45. arrow = 50;
  46. eot = 70;
  47. (** Forms of Types **)
  48. undefType* = 0;
  49. namedType* = 1;
  50. recordType* = 2;
  51. arrayType* = 3;
  52. pointerType* = 4;
  53. procedureType* = 5;
  54. (** Values of Param.passed **)
  55. byValue* = 0;
  56. byVar* = 1;
  57. (** Comment separator **)
  58. tab = 9X;
  59. (** Parser Settings **)
  60. debug* = TRUE;
  61. TYPE
  62. Str* = ARRAY 256 OF CHAR;
  63. LongStr* = ARRAY 40960 OF CHAR;
  64. Object* = POINTER TO ObjectDesc;
  65. ObjectDesc* = RECORD
  66. name*: Str;
  67. comment*: LongStr;
  68. exported*: BOOLEAN;
  69. next*: Object
  70. END;
  71. List* = POINTER TO ListDesc;
  72. ListDesc* = RECORD(ObjectDesc)
  73. first*, last*: Object
  74. END;
  75. Group* = POINTER TO GroupDesc;
  76. GroupDesc* = RECORD(ListDesc)
  77. ordinalConsts*: BOOLEAN
  78. END;
  79. Const* = POINTER TO ConstDesc;
  80. ConstDesc* = RECORD(ObjectDesc)
  81. value*: Str;
  82. isOrdinal*: BOOLEAN; (** TRUE if type of const is integer or char *)
  83. intVal*: INTEGER (** If isOrdinal, holds value in integer format *)
  84. END;
  85. Type* = POINTER TO TypeDesc;
  86. TypeDesc* = RECORD(ObjectDesc)
  87. form*: INTEGER; (** See @Form of Types *)
  88. len*: Str; (** Length of array (may be an expression), or '' *)
  89. base*: Type; (** Base type of rec/arr/pointer, return of procedure *)
  90. fields*: List
  91. END;
  92. Var* = POINTER TO VarDesc; (** Global variables and record fields *)
  93. VarDesc* = RECORD(ObjectDesc)
  94. type*: Type
  95. END;
  96. Param* = POINTER TO ParamDesc;
  97. ParamDesc* = RECORD(ObjectDesc)
  98. passed*: INTEGER; (** See values of Param.pass *)
  99. type*: Type
  100. END;
  101. Procedure* = POINTER TO ProcedureDesc;
  102. ProcedureDesc* = RECORD(ObjectDesc)
  103. returnType*: Type;
  104. params*: List
  105. END;
  106. Module* = POINTER TO ModuleDesc;
  107. ModuleDesc* = RECORD(ObjectDesc)
  108. foreign*: BOOLEAN; (** TRUE if module has a [foreign] mark *)
  109. consts*: List;
  110. types*: List;
  111. vars*: List;
  112. procedures*: List
  113. END;
  114. VAR
  115. curFname: Str; (** Set by SetFname and used in Mark for error output *)
  116. R: Files.Rider; (** Rider of the currently parsed module *)
  117. c: CHAR; (** One step ahead character read from rider R *)
  118. line, col: INTEGER; (** Position in R *)
  119. lastError: INTEGER; (** Position in R of last error, or -1 *)
  120. sym: INTEGER; (** One step ahead (syntactic) symbol read *)
  121. id: ARRAY 256 OF CHAR; (** Identifier read *)
  122. len: INTEGER; (** Actual length of id *)
  123. sval: Str; (** String read, when sym = string *)
  124. ival: INTEGER;
  125. writingDoc: BOOLEAN; (** TRUE when inside a doc comment *)
  126. doc: LongStr; (** Currently saved documentation comment *)
  127. docLen: INTEGER; (** Actual length of doc *)
  128. docLine: INTEGER; (** Line where the last doc-comment started *)
  129. curTitle: Str; (** Title of the current group of comments *)
  130. PrintObject: PROCEDURE (o: Object; indent: INTEGER; inlined: BOOLEAN);
  131. ParseType: PROCEDURE (docObj: Object): Type;
  132. ParseParamType: PROCEDURE (): Type;
  133. (** Debug **)
  134. PROCEDURE Debug*(s: ARRAY OF CHAR);
  135. BEGIN
  136. IF debug THEN Out.String(s); Out.Ln END
  137. END Debug;
  138. (** Error Handling **)
  139. (** Used for error output in Mark *)
  140. PROCEDURE SetFname*(fname: ARRAY OF CHAR);
  141. BEGIN curFname := fname
  142. END SetFname;
  143. PROCEDURE Mark(s: ARRAY OF CHAR);
  144. VAR pos: INTEGER;
  145. BEGIN
  146. pos := Files.Pos(R);
  147. IF (lastError = -1) OR (lastError + 7 < pos) THEN
  148. Out.String(curFname); Out.Char(':');
  149. Out.Int(line, 0); Out.Char(':'); Out.Int(col, 0);
  150. Out.String(': error: '); Out.String(s); Out.Ln
  151. END;
  152. lastError := pos
  153. END Mark;
  154. PROCEDURE SymToStr(sym: INTEGER; VAR s: ARRAY OF CHAR);
  155. BEGIN
  156. IF sym = null THEN s := 'nothing'
  157. ELSIF sym = ident THEN Strings.Copy(id, s)
  158. ELSIF sym = int THEN Int.Str(ival, s)
  159. ELSIF sym = real THEN s := 'real number'
  160. ELSIF sym = set THEN s := 'set'
  161. ELSIF sym = string THEN s := 'string'
  162. ELSIF sym = module THEN s := 'MODULE'
  163. ELSIF sym = import THEN s := 'IMPORT'
  164. ELSIF sym = const THEN s := 'CONST'
  165. ELSIF sym = type THEN s := 'TYPE'
  166. ELSIF sym = var THEN s := 'VAR'
  167. ELSIF sym = record THEN s := 'RECORD'
  168. ELSIF sym = array THEN s := 'ARRAY'
  169. ELSIF sym = pointer THEN s := 'POINTER'
  170. ELSIF sym = to THEN s := 'TO'
  171. ELSIF sym = of THEN s := 'OF'
  172. ELSIF sym = procedure THEN s := 'PROCEDURE'
  173. ELSIF sym = begin THEN s := 'BEGIN'
  174. ELSIF sym = end THEN s := 'END'
  175. ELSIF sym = div THEN s := 'DIV'
  176. ELSIF sym = mod THEN s := 'MOD'
  177. ELSIF sym = lparen THEN s := '('
  178. ELSIF sym = rparen THEN s := ')'
  179. ELSIF sym = lbrak THEN s := '['
  180. ELSIF sym = rbrak THEN s := ']'
  181. ELSIF sym = lbrace THEN s := '{'
  182. ELSIF sym = rbrace THEN s := '}'
  183. ELSIF sym = period THEN s := '.'
  184. ELSIF sym = comma THEN s := ','
  185. ELSIF sym = upto THEN s := '..'
  186. ELSIF sym = colon THEN s := ':'
  187. ELSIF sym = semicol THEN s := ';'
  188. ELSIF sym = equals THEN s := '='
  189. ELSIF sym = becomes THEN s := ':='
  190. ELSIF sym = plus THEN s := '+'
  191. ELSIF sym = minus THEN s := '-'
  192. ELSIF sym = times THEN s := '*'
  193. ELSIF sym = rdiv THEN s := '/'
  194. ELSIF sym = not THEN s := '~'
  195. ELSIF sym = arrow THEN s := '^'
  196. ELSIF sym = eot THEN s := 'end of text'
  197. ELSE s := 'Symbol #'; Int.Append(sym, s)
  198. END
  199. END SymToStr;
  200. PROCEDURE MarkExp(name: ARRAY OF CHAR);
  201. VAR s, word: ARRAY 256 OF CHAR;
  202. BEGIN
  203. s := name; Strings.Append(' expected, but ', s);
  204. SymToStr(sym, word); Strings.Append(word, s);
  205. Strings.Append(' found', s);
  206. Mark(s)
  207. END MarkExp;
  208. PROCEDURE MarkEnd(title, name: ARRAY OF CHAR);
  209. VAR s, word: ARRAY 256 OF CHAR;
  210. BEGIN
  211. Strings.Copy(title, s); Strings.Append(' ', s); Strings.Append(name, s);
  212. Strings.Append(' is not closed.', s); Mark(s)
  213. END MarkEnd;
  214. (** Handle Comments **)
  215. PROCEDURE ClearComments;
  216. BEGIN doc[0] := 0X; docLen := 0; docLine := -1
  217. END ClearComments;
  218. PROCEDURE RemoveLastComment;
  219. BEGIN
  220. WHILE (docLen # 0) & (doc[docLen] # tab) DO DEC(docLen) END;
  221. doc[docLen] := 0X
  222. END RemoveLastComment;
  223. (** Comments **)
  224. PROCEDURE AppendComment(VAR comment: ARRAY OF CHAR);
  225. VAR L, i, j: INTEGER;
  226. BEGIN
  227. L := 0; WHILE (doc[L] # 0X) & (doc[L] # tab) DO INC(L) END;
  228. j := Strings.Length(comment); i := 0;
  229. WHILE (i # L) & (j < LEN(comment) - 1) DO
  230. comment[j] := doc[i]; INC(i); INC(j)
  231. END;
  232. comment[j] := 0X;
  233. IF doc[L] = 0X THEN doc[0] := 0X; docLen := 0
  234. ELSE Strings.Delete(doc, 0, L + 1); DEC(docLen, L)
  235. END
  236. END AppendComment;
  237. PROCEDURE GetLastComment(VAR comment: ARRAY OF CHAR);
  238. VAR L, i, j: INTEGER;
  239. BEGIN
  240. IF docLen # 0 THEN
  241. L := docLen; WHILE (L # -1) & (doc[L] # tab) DO DEC(L) END;
  242. Strings.Extract(doc, L + 1, docLen - L - 1, comment)
  243. ELSE comment[0] := 0X
  244. END
  245. END GetLastComment;
  246. PROCEDURE SaveAllComments(o: Object);
  247. VAR i: INTEGER;
  248. BEGIN Strings.Copy(doc, o.comment); ClearComments;
  249. i := 0;
  250. WHILE o.comment[i] # 0X DO
  251. IF o.comment[i] = tab THEN o.comment[i] := 0AX END;
  252. INC(i)
  253. END
  254. END SaveAllComments;
  255. (** Stores the first comment from global variable doc in the given object o,
  256. but does that only if doc is not empty and if o does not yet have a comment.
  257. Also does that anyway if lastLine = -1 or if it is equal to the line where
  258. the comment started.
  259. The last comment in doc is from start of doc till the first tab character.
  260. Parameter lastLine should be equal to the line number of the last syntax
  261. symbol of the object, or -1 if comment goes before it. *)
  262. PROCEDURE SaveComment(o: Object; lastLine: INTEGER);
  263. BEGIN
  264. IF (o # NIL) & (doc[0] # 0X) & ((lastLine = -1) OR (docLine = lastLine)) THEN
  265. IF o.comment[0] = 0X THEN AppendComment(o.comment)
  266. ELSIF docLine = lastLine THEN Strings.Append(0AX, o.comment);
  267. AppendComment(o.comment)
  268. END
  269. END
  270. END SaveComment;
  271. (** Scanner **)
  272. PROCEDURE Read;
  273. BEGIN
  274. IF c = 0AX THEN INC(line); col := 0 END;
  275. IF ~R.eof THEN Files.ReadChar(R, c); INC(col) ELSE c := 0X END
  276. END Read;
  277. PROCEDURE IsLetter(x: CHAR): BOOLEAN;
  278. RETURN ('a' <= x) & (x <= 'z') OR ('A' <= x) & (x <= 'Z') OR (x = '_')
  279. END IsLetter;
  280. PROCEDURE IsDec(x: CHAR): BOOLEAN;
  281. RETURN ('0' <= x) & (x <= '9') END IsDec;
  282. PROCEDURE IsHex(x: CHAR): BOOLEAN;
  283. RETURN IsDec(x) OR ('a' <= x) & (x <= 'f') OR ('A' <= x) & (x <= 'F')
  284. END IsHex;
  285. PROCEDURE FromHex(x: CHAR): INTEGER;
  286. VAR n: INTEGER;
  287. BEGIN
  288. IF ('A' <= x) & (x <= 'F') THEN n := 10 - ORD('A') + ORD(x)
  289. ELSIF ('a' <= x) & (x <= 'f') THEN n := 10 - ORD('a') + ORD(x)
  290. ELSIF ('0' <= x) & (x <= '9') THEN n := ORD(x) - ORD('0')
  291. ELSE ASSERT(FALSE)
  292. END
  293. RETURN n END FromHex;
  294. PROCEDURE ToHex(n: INTEGER): CHAR;
  295. VAR x: CHAR;
  296. BEGIN
  297. IF (0 <= n) & (n < 10) THEN x := CHR(ORD('0') + n)
  298. ELSIF (10 <= n) & (n < 16) THEN x := CHR(ORD('A') - 10 + n)
  299. ELSE ASSERT(FALSE)
  300. END
  301. RETURN x END ToHex;
  302. (** Reads a decimal or hexadecimal number (or a hexadecimal char literal),
  303. puts it in id, len, ival, sym. *)
  304. PROCEDURE ReadNumber;
  305. VAR hex, isChar: BOOLEAN;
  306. i: INTEGER;
  307. BEGIN
  308. len := 0;
  309. REPEAT
  310. IF len < LEN(id) - 1 THEN id[len] := c; INC(len) END;
  311. Read
  312. UNTIL ~IsHex(c);
  313. id[len] := 0X;
  314. isChar := c = 'X';
  315. IF (c = 'H') OR (c = 'X') THEN hex := TRUE; Read ELSE hex := FALSE END;
  316. ival := 0; i := 0;
  317. IF hex THEN
  318. WHILE id[i] # 0X DO ival := ival * 16 + FromHex(id[i]); INC(i) END;
  319. IF isChar THEN sym := char ELSE sym := int END
  320. ELSE
  321. WHILE id[i] # 0X DO
  322. IF IsDec(id[i]) THEN ival := ival * 10 + ORD(id[i]) - ORD('0')
  323. ELSE Mark('Not a hexadecimal number')
  324. END;
  325. INC(i)
  326. END;
  327. sym := int
  328. END
  329. END ReadNumber;
  330. PROCEDURE WriteDoc(c: CHAR);
  331. BEGIN
  332. IF writingDoc & (docLen < LEN(doc) - 1) &
  333. ((c > ' ') OR (docLen # 0) & (doc[docLen - 1] > ' '))
  334. THEN
  335. IF c < ' ' THEN c := ' ' END;
  336. doc[docLen] := c; INC(docLen)
  337. END
  338. END WriteDoc;
  339. PROCEDURE ReadComment(toplevel: BOOLEAN);
  340. VAR closed, tmp: BOOLEAN;
  341. x: CHAR;
  342. title: BOOLEAN;
  343. BEGIN
  344. IF toplevel & (docLen = 0) THEN docLine := line END;
  345. Read; closed := FALSE; writingDoc := FALSE;
  346. IF c = '*' THEN Read; (* Second star *)
  347. IF c = ')' THEN Read; closed := TRUE
  348. ELSIF toplevel THEN writingDoc := TRUE;
  349. IF docLen # 0 THEN
  350. doc[docLen] := tab; INC(docLen)
  351. END
  352. END
  353. END;
  354. IF ~closed THEN
  355. REPEAT
  356. WHILE (c # 0X) & (c # '*') DO
  357. IF c = '(' THEN Read;
  358. IF c = '*' THEN
  359. tmp := writingDoc;
  360. ReadComment(FALSE);
  361. writingDoc := tmp
  362. ELSE WriteDoc('(')
  363. END
  364. END;
  365. WriteDoc(c); Read
  366. END;
  367. IF c = '*' THEN Read;
  368. IF c # ')' THEN WriteDoc('*') END
  369. END
  370. UNTIL (c = 0X) OR (c = ')');
  371. IF c = ')' THEN Read END
  372. END;
  373. IF writingDoc & (docLen # 0) THEN
  374. IF doc[docLen - 1] = '*' THEN (* Title comment *)
  375. DEC(docLen); doc[docLen] := 0X; title := TRUE
  376. ELSE title := FALSE
  377. END;
  378. REPEAT DEC(docLen) UNTIL (docLen = -1) OR (doc[docLen] > ' ');
  379. IF (docLen # -1) & (docLen < LEN(doc) - 2) THEN x := doc[docLen];
  380. IF ~title & (x # '!') & (x # ',') & (x # '.') &
  381. (x # ':') & (x # ';') & (x # '?') & (x # '*')
  382. THEN INC(docLen); doc[docLen] := '.'
  383. END
  384. END;
  385. INC(docLen); doc[docLen] := 0X;
  386. IF title THEN
  387. IF doc[0] = 0X THEN curTitle := '-'
  388. ELSE curTitle[0] := 0X; GetLastComment(curTitle); RemoveLastComment
  389. END
  390. END
  391. END
  392. END ReadComment;
  393. (** Uses global var id to set global var sym.
  394. Identifies such keywords as MODULE and BEGIN. *)
  395. PROCEDURE IdentifyKeyword;
  396. BEGIN
  397. IF id = 'MODULE' THEN sym := module
  398. ELSIF id = 'IMPORT' THEN sym := import
  399. ELSIF id = 'CONST' THEN sym := const
  400. ELSIF id = 'TYPE' THEN sym := type
  401. ELSIF id = 'VAR' THEN sym := var
  402. ELSIF id = 'RECORD' THEN sym := record
  403. ELSIF id = 'ARRAY' THEN sym := array
  404. ELSIF id = 'POINTER' THEN sym := pointer
  405. ELSIF id = 'TO' THEN sym := to
  406. ELSIF id = 'OF' THEN sym := of
  407. ELSIF id = 'PROCEDURE' THEN sym := procedure
  408. ELSIF id = 'BEGIN' THEN sym := begin
  409. ELSIF id = 'END' THEN sym := end
  410. ELSIF id = 'DIV' THEN sym := div
  411. ELSIF id = 'MOD' THEN sym := mod
  412. ELSE sym := ident
  413. END
  414. END IdentifyKeyword;
  415. PROCEDURE ReadIdentOrKeyword;
  416. BEGIN
  417. len := 0;
  418. REPEAT
  419. IF len < LEN(id) - 1 THEN id[len] := c; INC(len) END;
  420. Read
  421. UNTIL ~IsLetter(c) & ~IsDec(c);
  422. id[len] := 0X;
  423. IdentifyKeyword
  424. END ReadIdentOrKeyword;
  425. PROCEDURE ReadString;
  426. VAR q: CHAR;
  427. BEGIN q := c; len := 0; Read;
  428. WHILE (c >= ' ') & (c # q) DO
  429. IF len < LEN(id) - 3 THEN
  430. id[len] := c; INC(len)
  431. END;
  432. Read
  433. END;
  434. id[len] := 0X;
  435. IF c = q THEN Read ELSE Mark('String not terminated') END;
  436. sym := string
  437. END ReadString;
  438. PROCEDURE GetSym;
  439. VAR z: ARRAY 200 OF CHAR;
  440. BEGIN
  441. sym := null;
  442. REPEAT
  443. WHILE (c # 0X) & (c <= ' ') DO Read END;
  444. IF IsLetter(c) THEN ReadIdentOrKeyword
  445. ELSIF IsDec(c) THEN ReadNumber
  446. ELSIF (c = '"') OR (c = "'") THEN ReadString
  447. ELSIF c = '+' THEN Read; sym := plus
  448. ELSIF c = '-' THEN Read; sym := minus
  449. ELSIF c = '*' THEN Read; sym := times
  450. ELSIF c = '/' THEN Read; sym := rdiv
  451. ELSIF c = '~' THEN Read; sym := not
  452. ELSIF c = ',' THEN Read; sym := comma
  453. ELSIF c = ':' THEN Read;
  454. IF c = '=' THEN Read; sym := becomes ELSE sym := colon END
  455. ELSIF c = '.' THEN Read;
  456. IF c = '.' THEN Read; sym := upto ELSE sym := period END
  457. ELSIF c = '(' THEN Read;
  458. IF c = '*' THEN ReadComment(TRUE) ELSE sym := lparen END
  459. ELSIF c = ')' THEN Read; sym := rparen
  460. ELSIF c = '[' THEN Read; sym := lbrak
  461. ELSIF c = ']' THEN Read; sym := rbrak
  462. ELSIF c = '{' THEN Read; sym := lbrace
  463. ELSIF c = '}' THEN Read; sym := rbrace
  464. ELSIF c = ';' THEN Read; sym := semicol
  465. ELSIF c = '=' THEN Read; sym := equals
  466. ELSIF c = '^' THEN Read; sym := arrow
  467. ELSIF c = 0X THEN sym := eot
  468. ELSE Read
  469. END
  470. UNTIL sym # null
  471. END GetSym;
  472. (** List **)
  473. PROCEDURE NewList(): List;
  474. VAR L: List;
  475. BEGIN NEW(L)
  476. RETURN L END NewList;
  477. PROCEDURE NewGroup(): List;
  478. VAR G: Group;
  479. BEGIN NEW(G); Strings.Copy(curTitle, G.comment); G.ordinalConsts := FALSE
  480. RETURN G END NewGroup;
  481. (** Returns object with the minimum name from a non-empty list L *)
  482. PROCEDURE FindMinName(L: List): Object;
  483. VAR x, min: Object;
  484. BEGIN
  485. min := L.first; x := min.next;
  486. WHILE x # NIL DO
  487. IF x.name < min.name THEN min := x END;
  488. x := x.next
  489. END
  490. RETURN min END FindMinName;
  491. (** Returns object with the minimum ordinal value from a non-empty list L *)
  492. PROCEDURE FindMinIntVal(L: List): Object;
  493. VAR x, min: Object;
  494. val, minVal: INTEGER;
  495. BEGIN
  496. min := L.first; minVal := L.first(Const).intVal; x := min.next;
  497. WHILE x # NIL DO val := x(Const).intVal;
  498. IF val < minVal THEN min := x; minVal := val END;
  499. x := x.next
  500. END
  501. RETURN min END FindMinIntVal;
  502. PROCEDURE AddToList(L: List; o: Object);
  503. BEGIN
  504. IF L.first = NIL THEN L.first := o ELSE L.last.next := o END;
  505. WHILE o.next # NIL DO o := o.next END;
  506. L.last := o
  507. END AddToList;
  508. (** Removes o from list L. *)
  509. PROCEDURE RemoveFromList(L: List; o: Object);
  510. VAR x: Object;
  511. BEGIN
  512. IF L.first = o THEN L.first := L.first.next
  513. ELSE x := L.first;
  514. WHILE x.next # o DO x := x.next END;
  515. x.next := x.next.next
  516. END;
  517. o.next := NIL
  518. END RemoveFromList;
  519. (** Moves o from list L such that L.last = o. *)
  520. PROCEDURE MoveToEndOfList(L: List; o: Object);
  521. BEGIN IF L.last # o THEN RemoveFromList(L, o); AddToList(L, o) END
  522. END MoveToEndOfList;
  523. (** If L is empty, creates a group with title = curTitle in it.
  524. If L is not empty and last group's title is not curTitle,
  525. finds it in L and moves it to the last position.
  526. If it is not found, creates a new group in the end of L. *)
  527. PROCEDURE UpdateCurGroup(L: List);
  528. VAR x: Object;
  529. BEGIN x := L.first;
  530. WHILE (x # NIL) & (x.comment # curTitle) DO x := x.next END;
  531. IF x = NIL THEN x := NewGroup(); AddToList(L, x)
  532. ELSE MoveToEndOfList(L, x)
  533. END
  534. END UpdateCurGroup;
  535. (** Printing **)
  536. PROCEDURE PrintIndent(n: INTEGER; inlined: BOOLEAN);
  537. BEGIN
  538. IF ~inlined THEN
  539. WHILE n > 0 DO Out.String(' '); DEC(n) END
  540. END
  541. END PrintIndent;
  542. PROCEDURE PrintComment(o: Object; indent: INTEGER);
  543. BEGIN
  544. IF o.comment[0] # 0X THEN
  545. PrintIndent(indent, FALSE);
  546. Out.String('(* '); Out.String(o.comment);
  547. Out.String(' *)'); Out.Ln
  548. END
  549. END PrintComment;
  550. PROCEDURE PrintList(L: List; indent: INTEGER; inlined: BOOLEAN);
  551. VAR o: Object;
  552. BEGIN
  553. IF (L # NIL) & (L.first # NIL) THEN
  554. IF L.comment[0] # 0X THEN
  555. Out.String('### '); Out.String(L.comment); Out.Ln
  556. END;
  557. o := L.first;
  558. WHILE o # NIL DO
  559. PrintObject(o, indent, FALSE);
  560. o := o.next
  561. END
  562. ELSE PrintIndent(indent, FALSE); Out.Char('-'); Out.Ln
  563. END
  564. END PrintList;
  565. PROCEDURE PrintConst(C: Const; indent: INTEGER; inlined: BOOLEAN);
  566. BEGIN
  567. PrintIndent(indent, inlined);
  568. Out.String('Const '); Out.String(C.name);
  569. Out.String(' with value '); Out.String(C.value); Out.Ln;
  570. PrintComment(C, indent)
  571. END PrintConst;
  572. PROCEDURE PrintParam(par: Param; indent: INTEGER; inlined: BOOLEAN);
  573. BEGIN
  574. PrintIndent(indent, inlined);
  575. IF par.passed = byVar THEN Out.String('Variable')
  576. ELSIF par.passed = byValue THEN Out.String('Value')
  577. END;
  578. Out.String(' parameter '); Out.String(par.name);
  579. Out.String(' of '); PrintObject(par.type, indent, TRUE)
  580. END PrintParam;
  581. PROCEDURE PrintVar(v: Var; indent: INTEGER; inlined: BOOLEAN);
  582. BEGIN
  583. PrintIndent(indent, inlined);
  584. Out.String(v.name);
  585. Out.String(' of '); PrintObject(v.type, indent, TRUE);
  586. IF ~inlined & (v.comment[0] # 0X) THEN Out.Ln END;
  587. PrintComment(v, indent)
  588. END PrintVar;
  589. PROCEDURE PrintType(T: Type; indent: INTEGER; inlined: BOOLEAN);
  590. VAR x: Object;
  591. BEGIN
  592. PrintIndent(indent, inlined);
  593. IF T = NIL THEN Out.String('NIL')
  594. ELSIF T.form = namedType THEN
  595. Out.String('type '); Out.String(T.name);
  596. IF T.base # NIL THEN
  597. Out.String(' is '); PrintType(T.base, indent, TRUE)
  598. END
  599. ELSIF T.form = arrayType THEN
  600. IF T.len[0] = 0X THEN Out.String('open ') END;
  601. Out.String('array type ');
  602. IF T.len[0] # 0X THEN Out.String('with length ');
  603. Out.String(T.len); Out.Char(' ')
  604. END;
  605. Out.String('of '); PrintObject(T.base, indent, TRUE)
  606. ELSIF T.form = recordType THEN Out.String('record type ');
  607. IF T.base # NIL THEN Out.String('that extends ');
  608. Out.String(T.base.name); Out.Char(' ')
  609. END;
  610. IF T.fields.first # NIL THEN Out.String('with fields:'); Out.Ln;
  611. PrintList(T.fields, indent + 1, FALSE)
  612. ELSE Out.String('with no fields')
  613. END
  614. ELSIF T.form = procedureType THEN Out.String('procedure type ');
  615. IF T.fields.first # NIL THEN
  616. PrintIndent(indent, FALSE); Out.Char('(');
  617. PrintList(T.fields, indent + 1, TRUE);
  618. Out.String(') ')
  619. END;
  620. IF T.base # NIL THEN
  621. Out.String('that returns '); PrintObject(T.base, indent, TRUE)
  622. END
  623. ELSIF T.form = pointerType THEN Out.String('pointer type to ');
  624. PrintObject(T.base, indent, TRUE)
  625. ELSE Out.String('?')
  626. END;
  627. IF ~inlined THEN Out.Ln; PrintComment(T, indent) END
  628. END PrintType;
  629. PROCEDURE PrintProcedure(P: Procedure; indent: INTEGER; inlined: BOOLEAN);
  630. BEGIN
  631. PrintIndent(indent, inlined);
  632. Out.String('Procedure '); Out.String(P.name);
  633. IF P.returnType # NIL THEN
  634. Out.String(' returns '); PrintType(P.returnType, indent, TRUE)
  635. END;
  636. IF P.params.first # NIL THEN
  637. Out.String(', parameters:'); Out.Ln;
  638. PrintList(P.params, indent + 1, FALSE)
  639. ELSE Out.Ln
  640. END;
  641. IF ~inlined THEN Out.Ln; PrintComment(P, indent) END
  642. END PrintProcedure;
  643. PROCEDURE PrintModule(M: Module; indent: INTEGER; inlined: BOOLEAN);
  644. BEGIN
  645. PrintIndent(indent, inlined);
  646. Out.String('Module '); Out.String(M.name); Out.Ln;
  647. PrintComment(M, indent);
  648. PrintIndent(indent, FALSE);
  649. Out.String('Constants:'); Out.Ln; PrintList(M.consts, indent + 1, FALSE);
  650. PrintIndent(indent, FALSE);
  651. Out.String('Types:'); Out.Ln; PrintList(M.types, indent + 1, FALSE);
  652. PrintIndent(indent, FALSE);
  653. Out.String('Variables:'); Out.Ln; PrintList(M.vars, indent + 1, FALSE);
  654. PrintIndent(indent, FALSE);
  655. Out.String('Procedures:'); Out.Ln; PrintList(M.procedures, indent + 1, FALSE)
  656. END PrintModule;
  657. PROCEDURE PrintObject0(o: Object; indent: INTEGER; inlined: BOOLEAN);
  658. BEGIN
  659. IF o = NIL THEN PrintIndent(indent, inlined); Out.String('NIL')
  660. ELSIF o IS Module THEN PrintModule(o(Module), indent, inlined)
  661. ELSIF o IS Var THEN PrintVar(o(Var), indent, inlined)
  662. ELSIF o IS Const THEN PrintConst(o(Const), indent, inlined)
  663. ELSIF o IS Type THEN PrintType(o(Type), indent, inlined)
  664. ELSIF o IS Procedure THEN PrintProcedure(o(Procedure), indent, inlined)
  665. ELSIF o IS Param THEN PrintParam(o(Param), indent, inlined)
  666. ELSIF o IS List THEN PrintList(o(List), indent, inlined)
  667. ELSE PrintIndent(indent, inlined); Out.String('?')
  668. END;
  669. IF ~inlined THEN Out.Ln END
  670. END PrintObject0;
  671. PROCEDURE Print*(o: Object);
  672. BEGIN PrintObject(o, 0, FALSE)
  673. END Print;
  674. (** Object **)
  675. PROCEDURE InitObject(o: Object);
  676. BEGIN o.name[0] := 0X; o.comment[0] := 0X; o.next := NIL
  677. END InitObject;
  678. (** Sets exported field of object and skips the star mark. *)
  679. PROCEDURE CheckExportMark(o: Object);
  680. BEGIN
  681. IF sym = times THEN GetSym; o.exported := TRUE ELSE o.exported := FALSE END
  682. END CheckExportMark;
  683. (** Type **)
  684. PROCEDURE NewType(form: INTEGER): Type;
  685. VAR T: Type;
  686. BEGIN NEW(T); T.form := form; T.len[0] := 0X; T.base := NIL
  687. RETURN T END NewType;
  688. (** Param **)
  689. PROCEDURE NewParam(passed: INTEGER): Param;
  690. VAR par: Param;
  691. BEGIN NEW(par); InitObject(par); par.passed := passed; Strings.Copy(id, par.name)
  692. RETURN par END NewParam;
  693. (** Const **)
  694. PROCEDURE NewConst(): Const;
  695. VAR C: Const;
  696. BEGIN NEW(C); InitObject(C); Strings.Copy(id, C.name);
  697. C.isOrdinal := FALSE; C.intVal := 0
  698. RETURN C END NewConst;
  699. (** Var **)
  700. PROCEDURE NewVar(): Var;
  701. VAR v: Var;
  702. BEGIN NEW(v); InitObject(v); Strings.Copy(id, v.name)
  703. RETURN v END NewVar;
  704. (** Parser **)
  705. PROCEDURE ConstructString(VAR s: ARRAY OF CHAR);
  706. VAR i: INTEGER;
  707. x: CHAR;
  708. BEGIN i := 0; x := id[0];
  709. WHILE (x # 0X) & (x # "'") DO INC(i); x := id[i] END;
  710. IF x # 0X THEN x := '"' ELSE x := "'" END;
  711. s[0] := x; i := 0;
  712. WHILE id[i] # 0X DO s[i + 1] := id[i]; INC(i) END;
  713. s[i + 1] := x; s[i + 2] := 0X
  714. END ConstructString;
  715. PROCEDURE ConstructChar(VAR s: ARRAY OF CHAR);
  716. VAR i, n: INTEGER;
  717. x: CHAR;
  718. BEGIN n := ival; i := 0;
  719. REPEAT s[i] := ToHex(n MOD 16); n := n DIV 16; INC(i) UNTIL n = 0;
  720. s[i] := 'X'; s[i + 1] := 0X; DEC(i);
  721. WHILE n < i DO x := s[i]; s[i] := s[n]; s[n] := x; INC(n); DEC(i) END
  722. END ConstructChar;
  723. PROCEDURE ParseConstExpr(VAR s: ARRAY OF CHAR;
  724. VAR isOrdinal: BOOLEAN; VAR intVal: INTEGER);
  725. VAR start, end, tmp, i: INTEGER;
  726. x: CHAR;
  727. BEGIN isOrdinal := FALSE; intVal := 0;
  728. IF sym = lparen THEN s := '('
  729. ELSIF sym = int THEN Int.Str(ival, s); isOrdinal := TRUE; intVal := ival
  730. ELSIF sym = char THEN ConstructChar(s); isOrdinal := TRUE; intVal := ival
  731. ELSIF sym = ident THEN Strings.Copy(id, s)
  732. ELSIF sym = string THEN ConstructString(s)
  733. ELSE MarkExp('constant expression'); s[0] := 0X
  734. END;
  735. i := Strings.Length(s);
  736. IF i # 0 THEN
  737. start := Files.Pos(R); x := c;
  738. REPEAT GetSym UNTIL (sym = eot) OR (sym = comma) OR (sym = of) OR
  739. (sym = semicol);
  740. IF sym # eot THEN
  741. tmp := Files.Pos(R); end := tmp;
  742. IF sym = of THEN DEC(end, 3) ELSE DEC(end, 2) END;
  743. IF start < end THEN
  744. s[i] := x; INC(i);
  745. Files.Set(R, Files.Base(R), start);
  746. REPEAT
  747. Files.ReadChar(R, x);
  748. IF x < ' ' THEN x := ' ' END;
  749. IF (i < LEN(s) - 1) & ((x # ' ') OR (s[i - 1] # ' ')) THEN
  750. s[i] := x; INC(i)
  751. END
  752. UNTIL Files.Pos(R) >= end
  753. END;
  754. Files.Set(R, Files.Base(R), tmp)
  755. END
  756. END;
  757. WHILE (i # 1) & (s[i - 1] <= ' ') DO DEC(i) END;
  758. s[i] := 0X
  759. END ParseConstExpr;
  760. PROCEDURE ParseVars(isVarDecl: BOOLEAN): List;
  761. VAR first, v: Var;
  762. L: List;
  763. x: Object;
  764. passed, line2: INTEGER;
  765. T: Type;
  766. stop: BOOLEAN;
  767. BEGIN L := NewList(); stop := FALSE;
  768. WHILE ~stop & (sym = ident) DO
  769. IF isVarDecl THEN UpdateCurGroup(L) END;
  770. first := NewVar(); SaveAllComments(first); GetSym; CheckExportMark(first);
  771. IF isVarDecl THEN AddToList(L.last(List), first)
  772. ELSE AddToList(L, first)
  773. END;
  774. WHILE sym = comma DO GetSym;
  775. IF sym = ident THEN v := NewVar(); GetSym; CheckExportMark(v);
  776. IF isVarDecl THEN AddToList(L.last(List), v)
  777. ELSE AddToList(L, v)
  778. END;
  779. ELSE MarkExp('variable (field) name')
  780. END
  781. END;
  782. IF sym = colon THEN GetSym ELSE MarkExp(':') END;
  783. T := ParseType(NIL);
  784. IF first # NIL THEN
  785. first.type := T; x := first.next;
  786. WHILE x # NIL DO x(Var).type := T; x := x.next END
  787. END;
  788. IF (sym = semicol) OR ~isVarDecl THEN line2 := line;
  789. IF sym = semicol THEN GetSym; SaveComment(first, line2)
  790. ELSE stop := TRUE; SaveAllComments(first)
  791. END;
  792. IF first.comment[0] # 0X THEN x := first.next;
  793. WHILE x # NIL DO
  794. Strings.Copy(first.comment, x.comment); x := x.next
  795. END
  796. END
  797. ELSE MarkExp(';')
  798. END
  799. END
  800. RETURN L END ParseVars;
  801. PROCEDURE CheckOrdinal(C: Const);
  802. VAR x: CHAR;
  803. BEGIN
  804. IF ~C.isOrdinal THEN x := C.value[0];
  805. (* Literal char 'x' or "x" *)
  806. IF ((x = '"') OR (x = "'")) & (C.value[1] # 0X) & (C.value[2] = x) THEN
  807. C.isOrdinal := TRUE; C.intVal := ORD(C.value[1])
  808. END
  809. END
  810. END CheckOrdinal;
  811. PROCEDURE ParseConstDecl(M: Module);
  812. VAR C: Const;
  813. line2: INTEGER;
  814. isInt: BOOLEAN;
  815. BEGIN curTitle := '-';
  816. IF sym = const THEN GetSym;
  817. WHILE sym = ident DO
  818. Debug(id);
  819. UpdateCurGroup(M.consts);
  820. C := NewConst(); SaveComment(C, -1); GetSym; CheckExportMark(C);
  821. AddToList(M.consts.last(List), C);
  822. IF sym = equals THEN GetSym ELSE MarkExp('=') END;
  823. Debug('Begin ParseConstExpr');
  824. ParseConstExpr(C.value, C.isOrdinal, C.intVal); CheckOrdinal(C);
  825. Debug('End ParseConstExpr');
  826. line2 := line;
  827. IF sym = semicol THEN GetSym ELSE MarkExp(';') END;
  828. SaveComment(C, line2)
  829. END
  830. END
  831. END ParseConstDecl;
  832. PROCEDURE ParseTypeDecl(M: Module);
  833. VAR T: Type;
  834. line2: INTEGER;
  835. BEGIN curTitle := '-';
  836. IF sym = type THEN GetSym;
  837. WHILE sym = ident DO
  838. UpdateCurGroup(M.types);
  839. T := NewType(namedType); SaveAllComments(T);
  840. AddToList(M.types.last(List), T);
  841. Strings.Copy(id, T.name); GetSym; CheckExportMark(T);
  842. IF sym = equals THEN GetSym ELSE MarkExp('=') END;
  843. T.base := ParseType(T); line2 := line;
  844. IF sym = semicol THEN GetSym ELSE MarkExp(';') END;
  845. SaveComment(T, line2)
  846. END
  847. END
  848. END ParseTypeDecl;
  849. PROCEDURE ParseNamedType(): Type;
  850. VAR T: Type;
  851. BEGIN
  852. IF sym = ident THEN
  853. T := NewType(namedType);
  854. Strings.Copy(id, T.name);
  855. GetSym;
  856. IF sym = period THEN GetSym; Strings.Append('.', T.name);
  857. IF sym = ident THEN Strings.Append(id, T.name); GetSym
  858. ELSE MarkExp('identifier')
  859. END
  860. END
  861. ELSE T := NIL; MarkExp('type identifier')
  862. END
  863. RETURN T END ParseNamedType;
  864. PROCEDURE ParseArrayType(): Type;
  865. VAR T, T1: Type;
  866. isInt: BOOLEAN;
  867. tmp: INTEGER;
  868. BEGIN ASSERT(sym = array); GetSym;
  869. T := NewType(arrayType); T1 := T;
  870. IF (sym = int) OR (sym = ident) OR (sym = lparen) THEN
  871. ParseConstExpr(T.len, isInt, tmp)
  872. END;
  873. WHILE sym = comma DO GetSym;
  874. T1.base := NewType(arrayType); T1 := T1.base;
  875. ParseConstExpr(T1.len, isInt, tmp)
  876. END;
  877. IF sym = of THEN GetSym ELSE MarkExp('OF') END;
  878. T1.base := ParseType(NIL)
  879. RETURN T END ParseArrayType;
  880. PROCEDURE ParseRecordType(docObj: Object): Type;
  881. VAR T: Type;
  882. line2: INTEGER;
  883. BEGIN ASSERT(sym = record); line2 := line; GetSym;
  884. T := NewType(recordType);
  885. IF sym = lparen THEN GetSym; T.base := ParseNamedType();
  886. IF sym = rparen THEN GetSym ELSE MarkExp(')') END
  887. END;
  888. SaveComment(docObj, line2);
  889. T.fields := ParseVars(FALSE);
  890. IF sym = end THEN GetSym ELSE MarkExp('END') END
  891. RETURN T END ParseRecordType;
  892. PROCEDURE ParsePointerType(docObj: Object): Type;
  893. VAR T: Type;
  894. BEGIN ASSERT(sym = pointer); GetSym; T := NewType(pointerType);
  895. IF sym = to THEN GetSym ELSE MarkExp('TO') END;
  896. T.base := ParseType(docObj)
  897. RETURN T END ParsePointerType;
  898. PROCEDURE ParseFormalParamSection(L: List);
  899. VAR first, par: Param;
  900. x: Object;
  901. T: Type;
  902. passed: INTEGER;
  903. BEGIN
  904. IF sym = var THEN GetSym; passed := byVar ELSE passed := byValue END;
  905. IF sym = ident THEN first := NewParam(passed); GetSym;
  906. AddToList(L, first);
  907. WHILE sym = comma DO GetSym;
  908. IF sym = ident THEN par := NewParam(passed); GetSym;
  909. AddToList(L, par)
  910. ELSE MarkExp('parameter name')
  911. END
  912. END
  913. ELSE first := NIL; MarkExp('parameter name')
  914. END;
  915. IF sym = colon THEN GetSym; T := ParseParamType();
  916. IF first # NIL THEN
  917. first.type := T;
  918. x := first.next;
  919. WHILE x # NIL DO x(Param).type := T; x := x.next END
  920. END
  921. ELSE MarkExp(':')
  922. END
  923. END ParseFormalParamSection;
  924. PROCEDURE ParseProcedureType(): Type;
  925. VAR T: Type;
  926. BEGIN ASSERT(sym = procedure); GetSym;
  927. T := NewType(procedureType); T.fields := NewList();
  928. IF sym = lparen THEN GetSym;
  929. IF sym # rparen THEN ParseFormalParamSection(T.fields);
  930. WHILE sym = semicol DO GetSym; ParseFormalParamSection(T.fields) END
  931. END;
  932. IF sym = rparen THEN GetSym ELSE MarkExp(')') END;
  933. IF sym = colon THEN GetSym; T.base := ParseNamedType() END
  934. END;
  935. (*!TODO*)
  936. RETURN T END ParseProcedureType;
  937. PROCEDURE ParseParamType0(): Type;
  938. VAR T: Type;
  939. BEGIN
  940. IF sym = array THEN T := ParseArrayType()
  941. ELSIF sym = ident THEN T := ParseNamedType()
  942. ELSE T := NIL; MarkExp('type')
  943. END
  944. RETURN T END ParseParamType0;
  945. PROCEDURE ParseType0(docObj: Object): Type;
  946. VAR T: Type;
  947. BEGIN
  948. IF sym = array THEN T := ParseArrayType()
  949. ELSIF sym = record THEN T := ParseRecordType(docObj)
  950. ELSIF sym = pointer THEN T := ParsePointerType(docObj)
  951. ELSIF sym = procedure THEN T := ParseProcedureType()
  952. ELSIF sym = ident THEN T := ParseNamedType()
  953. ELSE T := NIL; MarkExp('type')
  954. END
  955. RETURN T END ParseType0;
  956. (** Reads input stream until "END name" is found.
  957. Stops on "name" (sym = ident), or sym = eot *)
  958. PROCEDURE ReachEndOf(name: ARRAY OF CHAR);
  959. BEGIN
  960. REPEAT
  961. WHILE (sym # eot) & (sym # end) DO GetSym END;
  962. IF sym = end THEN GetSym END
  963. UNTIL (sym = eot) OR (sym = ident) & (id = name)
  964. END ReachEndOf;
  965. PROCEDURE ParseProcedureDecl(M: Module);
  966. VAR name: Str;
  967. P: Procedure;
  968. BEGIN curTitle := '-';
  969. WHILE sym = procedure DO UpdateCurGroup(M.procedures);
  970. GetSym; NEW(P); InitObject(P);
  971. P.params := NewList(); P.exported := FALSE;
  972. AddToList(M.procedures.last(List), P);
  973. IF (sym = minus) OR (sym = times) OR (sym = arrow) THEN GetSym END;
  974. IF sym = ident THEN Strings.Copy(id, P.name); GetSym
  975. ELSE MarkExp('procedure name')
  976. END;
  977. IF (sym = minus) OR (sym = arrow) THEN GetSym END;
  978. IF sym = times THEN GetSym; P.exported := TRUE END;
  979. IF sym = lparen THEN GetSym;
  980. IF sym # rparen THEN ParseFormalParamSection(P.params);
  981. WHILE sym = semicol DO GetSym; ParseFormalParamSection(P.params) END
  982. END;
  983. IF sym = rparen THEN GetSym ELSE MarkExp(')') END;
  984. IF sym = colon THEN GetSym; P.returnType := ParseNamedType() END
  985. END;
  986. IF sym = semicol THEN GetSym ELSE MarkExp(';') END;
  987. ReachEndOf(P.name); SaveComment(P, -1);
  988. IF sym = ident THEN GetSym;
  989. IF sym = semicol THEN GetSym ELSE MarkExp(';') END
  990. ELSE (* sym = eot *) MarkEnd('Procedure', P.name)
  991. END
  992. END
  993. END ParseProcedureDecl;
  994. PROCEDURE ParseVarDecl(M: Module);
  995. BEGIN ASSERT(sym = var); curTitle := '-';
  996. GetSym; M.vars := ParseVars(TRUE)
  997. END ParseVarDecl;
  998. PROCEDURE Declarations(M: Module);
  999. BEGIN
  1000. Debug('Begin Const Declarations');
  1001. IF sym = const THEN ParseConstDecl(M) END;
  1002. Debug('Begin Type Declarations');
  1003. IF sym = type THEN ParseTypeDecl(M) END;
  1004. Debug('Begin Var Declarations');
  1005. IF sym = var THEN ParseVarDecl(M) END;
  1006. Debug('Begin Procedure Declarations');
  1007. ParseProcedureDecl(M)
  1008. END Declarations;
  1009. PROCEDURE ParseImport(M: Module);
  1010. BEGIN
  1011. REPEAT GetSym UNTIL (sym = eot) OR (sym = procedure) OR (sym = begin) OR
  1012. (sym = end) OR (sym = const) OR (sym = type) OR (sym = var)
  1013. END ParseImport;
  1014. PROCEDURE FindMin(G: Group; ordinal: BOOLEAN): Object;
  1015. VAR x: Object;
  1016. BEGIN
  1017. IF ordinal THEN x := FindMinIntVal(G) ELSE x := FindMinName(G) END
  1018. RETURN x END FindMin;
  1019. PROCEDURE GroupCheckOrdinalConsts(G: Group);
  1020. VAR x: Object;
  1021. BEGIN
  1022. IF (G.first # NIL) & (G.first IS Const) THEN x := G.first;
  1023. WHILE (x # NIL) & x(Const).isOrdinal DO x := x.next END;
  1024. G.ordinalConsts := x = NIL
  1025. ELSE G.ordinalConsts := FALSE
  1026. END
  1027. END GroupCheckOrdinalConsts;
  1028. PROCEDURE SortGroup(G: Group);
  1029. VAR x: Object;
  1030. L: List;
  1031. ordinal: BOOLEAN;
  1032. BEGIN
  1033. Debug('SortGroup begin');
  1034. IF G.first # NIL THEN L := NewList();
  1035. GroupCheckOrdinalConsts(G);
  1036. Debug('SortGroup before WHILE');
  1037. WHILE G.first # NIL DO
  1038. Debug('SortGroup WHILE iteration');
  1039. x := FindMin(G, G.ordinalConsts);
  1040. RemoveFromList(G, x);
  1041. AddToList(L, x)
  1042. END;
  1043. G.first := L.first; G.last := L.last
  1044. END
  1045. END SortGroup;
  1046. PROCEDURE SortGroups(L: List);
  1047. VAR x: Object;
  1048. common: Group;
  1049. BEGIN
  1050. IF (L # NIL) & (L.first # NIL) THEN
  1051. common := NIL; x := L.first;
  1052. WHILE x # NIL DO
  1053. SortGroup(x(Group));
  1054. IF x.comment = '-' THEN common := x(Group) END;
  1055. x := x.next
  1056. END;
  1057. IF (common # NIL) & (common # L.first) THEN
  1058. x := L.first; WHILE x.next # common DO x := x.next END;
  1059. x.next := common.next;
  1060. common.next := L.first;
  1061. L.first := common
  1062. END
  1063. END
  1064. END SortGroups;
  1065. PROCEDURE SortModule(M: Module);
  1066. BEGIN
  1067. SortGroups(M.consts);
  1068. SortGroups(M.vars);
  1069. SortGroups(M.types);
  1070. SortGroups(M.procedures)
  1071. END SortModule;
  1072. PROCEDURE ParseModule*(VAR r: Files.Rider; VAR err: ARRAY OF CHAR): Module;
  1073. VAR M: Module;
  1074. BEGIN NEW(M); InitObject(M); M.foreign := FALSE;
  1075. M.consts := NewList(); M.types := NewList();
  1076. M.vars := NewList(); M.procedures := NewList();
  1077. R := r; c := 0X; line := 1; col := 0; lastError := -1;
  1078. Read; ClearComments; curTitle := '-'; GetSym;
  1079. Debug('Begin ParseModule');
  1080. IF sym = module THEN GetSym;
  1081. IF sym = lbrak THEN GetSym;
  1082. IF (sym = ident) & (id = 'foreign') THEN M.foreign := TRUE END;
  1083. REPEAT GetSym UNTIL (sym = eot) OR (sym = rbrak);
  1084. GetSym
  1085. END;
  1086. IF sym = ident THEN Strings.Copy(id, M.name); GetSym
  1087. ELSE MarkExp('module name')
  1088. END;
  1089. IF sym = semicol THEN GetSym ELSE MarkExp(';') END;
  1090. IF sym = import THEN ParseImport(M) END;
  1091. SaveAllComments(M);
  1092. Debug('Begin Declarations');
  1093. Declarations(M);
  1094. Debug('End Declarations');
  1095. IF sym = begin THEN
  1096. REPEAT GetSym UNTIL (sym = eot) OR (sym = end)
  1097. END;
  1098. ReachEndOf(M.name);
  1099. Debug('End of module');
  1100. IF sym = ident THEN GetSym;
  1101. IF sym # period THEN MarkExp('.') END
  1102. ELSE (* sym = eot *) MarkEnd('Module', M.name)
  1103. END
  1104. ELSE MarkExp('MODULE')
  1105. END;
  1106. Debug('Begin SortModule');
  1107. IF lastError = -1 THEN SortModule(M)
  1108. ELSE M := NIL; err := 'Error' (*!FIXME*)
  1109. END;
  1110. Debug('End ParseModule');
  1111. RETURN M END ParseModule;
  1112. BEGIN curFname[0] := 0X;
  1113. PrintObject := PrintObject0;
  1114. ParseType := ParseType0;
  1115. ParseParamType := ParseParamType0
  1116. END AutodocParser.