AutodocParser.Mod 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897
  1. MODULE AutodocParser;
  2. IMPORT Files, Texts, Out, Args, Strings, Config, 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. TYPE
  58. Str* = ARRAY 256 OF CHAR;
  59. LongStr* = ARRAY 40960 OF CHAR;
  60. Object* = POINTER TO ObjectDesc;
  61. ObjectDesc* = RECORD
  62. name*: Str;
  63. comment*: LongStr;
  64. exported*: BOOLEAN;
  65. next: Object
  66. END;
  67. List* = POINTER TO ListDesc;
  68. ListDesc* = RECORD
  69. first*, last: Object
  70. END;
  71. Group* = POINTER TO GroupDesc;
  72. GroupDesc* = RECORD(ObjectDesc)
  73. body*: List
  74. END;
  75. Const* = POINTER TO ConstDesc;
  76. ConstDesc* = RECORD(ObjectDesc)
  77. value*: Str;
  78. isOrdinal*: BOOLEAN; (** TRUE if type of const is integer or char *)
  79. intVal*: INTEGER (** If isOrdinal, holds value in integer format *)
  80. END;
  81. Type* = POINTER TO TypeDesc;
  82. TypeDesc* = RECORD(ObjectDesc)
  83. form*: INTEGER; (** See @Form of Types *)
  84. len*: Str; (** Length of array (may be an expression), or '' *)
  85. base*: Type; (** Base type of rec/arr/pointer, return of procedure *)
  86. fields*: List
  87. END;
  88. Var* = POINTER TO VarDesc; (** Global variables and record fields *)
  89. VarDesc* = RECORD(ObjectDesc)
  90. type*: Type
  91. END;
  92. Param* = POINTER TO ParamDesc;
  93. ParamDesc* = RECORD(ObjectDesc)
  94. passed*: INTEGER; (** See values of Param.pass *)
  95. type*: Type
  96. END;
  97. Procedure* = POINTER TO ProcedureDesc;
  98. ProcedureDesc* = RECORD(ObjectDesc)
  99. returnType*: Type;
  100. params*: List
  101. END;
  102. Module* = POINTER TO ModuleDesc;
  103. ModuleDesc* = RECORD(ObjectDesc)
  104. foreign*: BOOLEAN; (** TRUE if module has a [foreign] mark *)
  105. consts*: List;
  106. types*: List;
  107. vars*: List;
  108. procedures*: List
  109. END;
  110. VAR
  111. curFname: Str; (** Set by SetFname and used in Mark for error output *)
  112. R: Files.Rider; (** Rider of the currently parsed module *)
  113. c: CHAR; (** One step ahead character read from rider R *)
  114. line, col: INTEGER; (** Position in R *)
  115. lastError: INTEGER; (** Position in R of last error, or -1 *)
  116. sym: INTEGER; (** One step ahead (syntactic) symbol read *)
  117. id: ARRAY 256 OF CHAR; (** Identifier read *)
  118. len: INTEGER; (** Actual length of id *)
  119. sval: Str; (** String read, when sym = string *)
  120. ival: INTEGER;
  121. writingDoc: BOOLEAN; (** TRUE when inside a doc comment *)
  122. doc: LongStr; (** Currently saved documentation comment *)
  123. docLen: INTEGER; (** Actual length of doc *)
  124. PrintObject: PROCEDURE (o: Object; indent: INTEGER; inlined: BOOLEAN);
  125. ParseType: PROCEDURE (): Type;
  126. ParseParamType: PROCEDURE (): Type;
  127. (** Error Handling **)
  128. (** Used for error output in Mark *)
  129. PROCEDURE SetFname*(fname: ARRAY OF CHAR);
  130. BEGIN curFname := fname
  131. END SetFname;
  132. PROCEDURE Mark(s: ARRAY OF CHAR);
  133. VAR pos: INTEGER;
  134. BEGIN
  135. pos := Files.Pos(R);
  136. IF (lastError = -1) OR (lastError + 7 < pos) THEN
  137. Out.String(curFname); Out.Char(':');
  138. Out.Int(line, 0); Out.Char(':'); Out.Int(col, 0);
  139. Out.String(': error: '); Out.String(s); Out.Ln
  140. END;
  141. lastError := pos
  142. END Mark;
  143. PROCEDURE SymToStr(sym: INTEGER; VAR s: ARRAY OF CHAR);
  144. BEGIN
  145. IF sym = null THEN s := 'nothing'
  146. ELSIF sym = ident THEN Strings.Copy(id, s)
  147. ELSIF sym = int THEN Int.Str(ival, s)
  148. ELSIF sym = real THEN s := 'real number'
  149. ELSIF sym = set THEN s := 'set'
  150. ELSIF sym = string THEN s := 'string'
  151. ELSIF sym = module THEN s := 'MODULE'
  152. ELSIF sym = import THEN s := 'IMPORT'
  153. ELSIF sym = const THEN s := 'CONST'
  154. ELSIF sym = type THEN s := 'TYPE'
  155. ELSIF sym = var THEN s := 'VAR'
  156. ELSIF sym = record THEN s := 'RECORD'
  157. ELSIF sym = array THEN s := 'ARRAY'
  158. ELSIF sym = pointer THEN s := 'POINTER'
  159. ELSIF sym = to THEN s := 'TO'
  160. ELSIF sym = of THEN s := 'OF'
  161. ELSIF sym = procedure THEN s := 'PROCEDURE'
  162. ELSIF sym = begin THEN s := 'BEGIN'
  163. ELSIF sym = end THEN s := 'END'
  164. ELSIF sym = div THEN s := 'DIV'
  165. ELSIF sym = mod THEN s := 'MOD'
  166. ELSIF sym = lparen THEN s := '('
  167. ELSIF sym = rparen THEN s := ')'
  168. ELSIF sym = lbrak THEN s := '['
  169. ELSIF sym = rbrak THEN s := ']'
  170. ELSIF sym = lbrace THEN s := '{'
  171. ELSIF sym = rbrace THEN s := '}'
  172. ELSIF sym = period THEN s := '.'
  173. ELSIF sym = comma THEN s := ','
  174. ELSIF sym = upto THEN s := '..'
  175. ELSIF sym = colon THEN s := ':'
  176. ELSIF sym = semicol THEN s := ';'
  177. ELSIF sym = equals THEN s := '='
  178. ELSIF sym = becomes THEN s := ':='
  179. ELSIF sym = plus THEN s := '+'
  180. ELSIF sym = minus THEN s := '-'
  181. ELSIF sym = times THEN s := '*'
  182. ELSIF sym = rdiv THEN s := '/'
  183. ELSIF sym = not THEN s := '~'
  184. ELSIF sym = arrow THEN s := '^'
  185. ELSIF sym = eot THEN s := 'end of text'
  186. ELSE s := 'Symbol #'; Int.Append(sym, s)
  187. END
  188. END SymToStr;
  189. PROCEDURE MarkExp(name: ARRAY OF CHAR);
  190. VAR s, word: ARRAY 256 OF CHAR;
  191. BEGIN
  192. s := name; Strings.Append(' expected, but ', s);
  193. SymToStr(sym, word); Strings.Append(word, s);
  194. Strings.Append(' found', s);
  195. Mark(s)
  196. END MarkExp;
  197. PROCEDURE MarkEnd(title, name: ARRAY OF CHAR);
  198. VAR s, word: ARRAY 256 OF CHAR;
  199. BEGIN
  200. Strings.Copy(title, s); Strings.Append(' ', s); Strings.Append(name, s);
  201. Strings.Append(' is not closed.', s); Mark(s)
  202. END MarkEnd;
  203. (** Handle Comments **)
  204. PROCEDURE ClearComments;
  205. BEGIN
  206. END ClearComments;
  207. (** Scanner **)
  208. PROCEDURE Read;
  209. BEGIN
  210. IF c = 0AX THEN INC(line); col := 0 END;
  211. IF ~R.eof THEN Files.ReadChar(R, c); INC(col) ELSE c := 0X END
  212. END Read;
  213. PROCEDURE IsLetter(x: CHAR): BOOLEAN;
  214. RETURN ('a' <= x) & (x <= 'z') OR ('A' <= x) & (x <= 'Z') OR (x = '_')
  215. END IsLetter;
  216. PROCEDURE IsDec(x: CHAR): BOOLEAN;
  217. RETURN ('0' <= x) & (x <= '9') END IsDec;
  218. PROCEDURE IsHex(x: CHAR): BOOLEAN;
  219. RETURN IsDec(x) OR ('a' <= x) & (x <= 'f') OR ('A' <= x) & (x <= 'F')
  220. END IsHex;
  221. PROCEDURE FromHex(x: CHAR): INTEGER;
  222. VAR n: INTEGER;
  223. BEGIN
  224. IF ('A' <= x) & (x <= 'F') THEN n := 10 - ORD('A') + ORD(x)
  225. ELSIF ('a' <= x) & (x <= 'f') THEN n := 10 - ORD('a') + ORD(x)
  226. ELSIF ('0' <= x) & (x <= '9') THEN n := ORD(x) - ORD('0')
  227. ELSE ASSERT(FALSE)
  228. END
  229. RETURN n END FromHex;
  230. (** Reads a decimal or hexadecimal number (or a hexadecimal char literal),
  231. puts it in id, len, ival, sym. *)
  232. PROCEDURE ReadNumber;
  233. VAR hex, isChar: BOOLEAN;
  234. i: INTEGER;
  235. BEGIN
  236. len := 0;
  237. REPEAT
  238. IF len < LEN(id) - 1 THEN id[len] := c; INC(len) END;
  239. Read
  240. UNTIL ~IsHex(c);
  241. id[len] := 0X;
  242. isChar := c = 'X';
  243. IF (c = 'H') OR (c = 'X') THEN hex := TRUE; Read ELSE hex := FALSE END;
  244. ival := 0; i := 0;
  245. IF hex THEN
  246. WHILE id[i] # 0X DO ival := ival * 16 + FromHex(id[i]); INC(i) END;
  247. IF isChar THEN sym := char ELSE sym := int END
  248. ELSE
  249. WHILE id[i] # 0X DO
  250. IF IsDec(id[i]) THEN ival := ival * 10 + ORD(id[i]) - ORD('0')
  251. ELSE Mark('Not a hexadecimal number')
  252. END;
  253. INC(i)
  254. END;
  255. sym := int
  256. END
  257. END ReadNumber;
  258. PROCEDURE WriteDoc(c: CHAR);
  259. BEGIN
  260. IF writingDoc THEN
  261. IF docLen < LEN(doc) - 1 THEN
  262. IF (c > ' ') OR (docLen # 0) & (doc[docLen - 1] > ' ') THEN
  263. IF c < ' ' THEN c := ' ' END;
  264. doc[docLen] := c; INC(docLen)
  265. END
  266. END
  267. END
  268. END WriteDoc;
  269. PROCEDURE ReadComment(toplevel: BOOLEAN);
  270. VAR closed, tmp: BOOLEAN;
  271. BEGIN Read; closed := FALSE; writingDoc := FALSE;
  272. IF c = '*' THEN Read; (* Second star *)
  273. IF c = ')' THEN Read; closed := TRUE
  274. ELSIF toplevel THEN writingDoc := TRUE; docLen := 0
  275. END
  276. END;
  277. IF ~closed THEN
  278. REPEAT
  279. WHILE (c # 0X) & (c # '*') DO
  280. IF c = '(' THEN Read;
  281. IF c = '*' THEN
  282. tmp := writingDoc;
  283. ReadComment(FALSE);
  284. writingDoc := tmp
  285. ELSE WriteDoc('(')
  286. END
  287. END;
  288. WriteDoc(c); Read
  289. END;
  290. IF c = '*' THEN Read;
  291. IF c # ')' THEN WriteDoc('*') END
  292. END
  293. UNTIL (c = 0X) OR (c = ')');
  294. IF c = ')' THEN Read END
  295. END;
  296. IF writingDoc & (docLen # 0) THEN
  297. REPEAT DEC(docLen) UNTIL (docLen = -1) OR (doc[docLen] > ' ');
  298. doc[docLen + 1] := 0X;
  299. (*Out.Char('"'); Out.String(doc); Out.Char('"'); Out.Ln*)
  300. END
  301. END ReadComment;
  302. (** Identifies global var id and sets globar var sym. *)
  303. PROCEDURE IdentifyKeyword;
  304. BEGIN
  305. IF id = 'MODULE' THEN sym := module
  306. ELSIF id = 'IMPORT' THEN sym := import
  307. ELSIF id = 'CONST' THEN sym := const
  308. ELSIF id = 'TYPE' THEN sym := type
  309. ELSIF id = 'VAR' THEN sym := var
  310. ELSIF id = 'RECORD' THEN sym := record
  311. ELSIF id = 'ARRAY' THEN sym := array
  312. ELSIF id = 'POINTER' THEN sym := pointer
  313. ELSIF id = 'TO' THEN sym := to
  314. ELSIF id = 'OF' THEN sym := of
  315. ELSIF id = 'PROCEDURE' THEN sym := procedure
  316. ELSIF id = 'BEGIN' THEN sym := begin
  317. ELSIF id = 'END' THEN sym := end
  318. ELSIF id = 'DIV' THEN sym := div
  319. ELSIF id = 'MOD' THEN sym := mod
  320. ELSE sym := ident
  321. END
  322. END IdentifyKeyword;
  323. PROCEDURE ReadIdentOrKeyword;
  324. BEGIN
  325. len := 0;
  326. REPEAT
  327. IF len < LEN(id) - 1 THEN id[len] := c; INC(len) END;
  328. Read
  329. UNTIL ~IsLetter(c) & ~IsDec(c);
  330. id[len] := 0X;
  331. IdentifyKeyword
  332. END ReadIdentOrKeyword;
  333. PROCEDURE GetSym;
  334. VAR z: ARRAY 200 OF CHAR;
  335. BEGIN
  336. sym := null;
  337. REPEAT
  338. WHILE (c # 0X) & (c <= ' ') DO Read END;
  339. IF IsLetter(c) THEN ReadIdentOrKeyword
  340. ELSIF IsDec(c) THEN ReadNumber
  341. ELSIF c = '+' THEN Read; sym := plus
  342. ELSIF c = '-' THEN Read; sym := minus
  343. ELSIF c = '*' THEN Read; sym := times
  344. ELSIF c = '/' THEN Read; sym := rdiv
  345. ELSIF c = '~' THEN Read; sym := not
  346. ELSIF c = ',' THEN Read; sym := comma
  347. ELSIF c = ':' THEN Read;
  348. IF c = '=' THEN Read; sym := becomes ELSE sym := colon END
  349. ELSIF c = '.' THEN Read;
  350. IF c = '.' THEN Read; sym := upto ELSE sym := period END
  351. ELSIF c = '(' THEN Read;
  352. IF c = '*' THEN ReadComment(TRUE) ELSE sym := lparen END
  353. ELSIF c = ')' THEN Read; sym := rparen
  354. ELSIF c = '[' THEN Read; sym := lbrak
  355. ELSIF c = ']' THEN Read; sym := rbrak
  356. ELSIF c = '{' THEN Read; sym := lbrace
  357. ELSIF c = '}' THEN Read; sym := rbrace
  358. ELSIF c = ';' THEN Read; sym := semicol
  359. ELSIF c = '=' THEN Read; sym := equals
  360. ELSIF c = '^' THEN Read; sym := arrow
  361. ELSIF c = 0X THEN sym := eot
  362. ELSE Read
  363. END
  364. UNTIL sym # null
  365. (*;SymToStr(sym, z);Out.String(z);Out.Ln;*)
  366. END GetSym;
  367. (** List **)
  368. PROCEDURE NewList(): List;
  369. VAR L: List;
  370. BEGIN NEW(L)
  371. RETURN L END NewList;
  372. PROCEDURE AddToList(L: List; o: Object);
  373. BEGIN
  374. IF L.first = NIL THEN L.first := o ELSE L.last.next := o END;
  375. WHILE o.next # NIL DO o := o.next END;
  376. L.last := o
  377. END AddToList;
  378. (** Printing **)
  379. PROCEDURE PrintIndent(n: INTEGER; inlined: BOOLEAN);
  380. BEGIN
  381. IF ~inlined THEN
  382. WHILE n > 0 DO Out.String(' '); DEC(n) END
  383. END
  384. END PrintIndent;
  385. PROCEDURE PrintList(L: List; indent: INTEGER; inlined: BOOLEAN);
  386. VAR o: Object;
  387. BEGIN
  388. IF (L # NIL) & (L.first # NIL) THEN
  389. o := L.first;
  390. WHILE o # NIL DO
  391. PrintObject(o, indent, FALSE);
  392. o := o.next
  393. END
  394. ELSE PrintIndent(indent, FALSE); Out.Char('-'); Out.Ln
  395. END
  396. END PrintList;
  397. PROCEDURE PrintConst(C: Const; indent: INTEGER; inlined: BOOLEAN);
  398. BEGIN
  399. PrintIndent(indent, inlined);
  400. Out.String('Const '); Out.String(C.name);
  401. Out.String(' with value '); Out.String(C.value)
  402. END PrintConst;
  403. PROCEDURE PrintParam(par: Param; indent: INTEGER; inlined: BOOLEAN);
  404. BEGIN
  405. PrintIndent(indent, inlined);
  406. IF par.passed = byVar THEN Out.String('Variable')
  407. ELSIF par.passed = byValue THEN Out.String('Value')
  408. END;
  409. Out.String(' parameter '); Out.String(par.name);
  410. Out.String(' of '); PrintObject(par.type, indent, TRUE)
  411. END PrintParam;
  412. PROCEDURE PrintVar(v: Var; indent: INTEGER; inlined: BOOLEAN);
  413. BEGIN
  414. PrintIndent(indent, inlined);
  415. Out.String(v.name);
  416. Out.String(' of '); PrintObject(v.type, indent, TRUE)
  417. END PrintVar;
  418. PROCEDURE PrintType(T: Type; indent: INTEGER; inlined: BOOLEAN);
  419. VAR x: Object;
  420. BEGIN
  421. PrintIndent(indent, inlined);
  422. IF T = NIL THEN Out.String('NIL')
  423. ELSIF T.form = namedType THEN
  424. Out.String('type '); Out.String(T.name);
  425. IF T.base # NIL THEN
  426. Out.String(' is '); PrintType(T.base, indent, TRUE)
  427. END
  428. ELSIF T.form = arrayType THEN
  429. IF T.len[0] = 0X THEN Out.String('open ') END;
  430. Out.String('array type ');
  431. IF T.len[0] # 0X THEN Out.String('with length ');
  432. Out.String(T.len); Out.Char(' ')
  433. END;
  434. Out.String('of '); PrintObject(T.base, indent, TRUE)
  435. ELSIF T.form = recordType THEN Out.String('record type ');
  436. IF T.base # NIL THEN Out.String('that extends ');
  437. Out.String(T.base.name); Out.Char(' ')
  438. END;
  439. IF T.fields.first # NIL THEN Out.String('with fields:'); Out.Ln;
  440. PrintList(T.fields, indent + 1, FALSE)
  441. ELSE Out.String('with no fields')
  442. END
  443. ELSIF T.form = procedureType THEN Out.String('procedure type ');
  444. IF T.fields.first # NIL THEN
  445. PrintIndent(indent, FALSE); Out.Char('(');
  446. PrintList(T.fields, indent + 1, TRUE);
  447. Out.String(') ')
  448. END;
  449. IF T.base # NIL THEN
  450. Out.String('that returns '); PrintObject(T.base, ident, TRUE)
  451. END
  452. ELSIF T.form = pointerType THEN Out.String('pointer type to ');
  453. PrintObject(T.base, indent, TRUE)
  454. ELSE Out.String('?')
  455. END
  456. END PrintType;
  457. PROCEDURE PrintProcedure(P: Procedure; indent: INTEGER; inlined: BOOLEAN);
  458. BEGIN
  459. PrintIndent(indent, inlined);
  460. Out.String('Procedure '); Out.String(P.name);
  461. IF P.returnType # NIL THEN
  462. Out.String(' returns '); PrintType(P.returnType, indent, TRUE)
  463. END;
  464. IF P.params.first # NIL THEN
  465. Out.String(', parameters:'); Out.Ln;
  466. PrintList(P.params, indent + 1, FALSE)
  467. ELSE Out.Ln
  468. END
  469. END PrintProcedure;
  470. PROCEDURE PrintModule(M: Module; indent: INTEGER; inlined: BOOLEAN);
  471. BEGIN
  472. PrintIndent(indent, inlined);
  473. Out.String('Module '); Out.String(M.name); Out.Ln;
  474. PrintIndent(indent, FALSE);
  475. Out.String('Constants:'); Out.Ln; PrintList(M.consts, indent + 1, FALSE);
  476. PrintIndent(indent, FALSE);
  477. Out.String('Types:'); Out.Ln; PrintList(M.types, indent + 1, FALSE);
  478. PrintIndent(indent, FALSE);
  479. Out.String('Variables:'); Out.Ln; PrintList(M.vars, indent + 1, FALSE);
  480. PrintIndent(indent, FALSE);
  481. Out.String('Procedures:'); Out.Ln; PrintList(M.procedures, indent + 1, FALSE)
  482. END PrintModule;
  483. PROCEDURE PrintObject0(o: Object; indent: INTEGER; inlined: BOOLEAN);
  484. BEGIN
  485. IF o = NIL THEN PrintIndent(indent, inlined); Out.String('NIL')
  486. ELSIF o IS Module THEN PrintModule(o(Module), indent, inlined)
  487. ELSIF o IS Var THEN PrintVar(o(Var), indent, inlined)
  488. ELSIF o IS Const THEN PrintConst(o(Const), indent, inlined)
  489. ELSIF o IS Type THEN PrintType(o(Type), indent, inlined)
  490. ELSIF o IS Procedure THEN PrintProcedure(o(Procedure), indent, inlined)
  491. ELSIF o IS Param THEN PrintParam(o(Param), indent, inlined)
  492. ELSE PrintIndent(indent, inlined); Out.String('?')
  493. END;
  494. IF ~inlined THEN Out.Ln END
  495. END PrintObject0;
  496. PROCEDURE Print*(o: Object);
  497. BEGIN PrintObject(o, 0, FALSE)
  498. END Print;
  499. (** Object **)
  500. PROCEDURE InitObject(o: Object);
  501. BEGIN o.name[0] := 0X; o.comment[0] := 0X; o.next := NIL
  502. END InitObject;
  503. (** Sets exported field of object and skips the star mark. *)
  504. PROCEDURE CheckExportMark(o: Object);
  505. BEGIN
  506. IF sym = times THEN GetSym; o.exported := TRUE ELSE o.exported := FALSE END
  507. END CheckExportMark;
  508. (** Type **)
  509. PROCEDURE NewType(form: INTEGER): Type;
  510. VAR T: Type;
  511. BEGIN NEW(T); T.form := form; T.len[0] := 0X; T.base := NIL
  512. RETURN T END NewType;
  513. (** Param **)
  514. PROCEDURE NewParam(passed: INTEGER): Param;
  515. VAR par: Param;
  516. BEGIN NEW(par); InitObject(par); par.passed := passed; Strings.Copy(id, par.name)
  517. RETURN par END NewParam;
  518. (** Const **)
  519. PROCEDURE NewConst(): Const;
  520. VAR C: Const;
  521. BEGIN NEW(C); InitObject(C); Strings.Copy(id, C.name)
  522. RETURN C END NewConst;
  523. (** Var **)
  524. PROCEDURE NewVar(): Var;
  525. VAR v: Var;
  526. BEGIN NEW(v); InitObject(v); Strings.Copy(id, v.name)
  527. RETURN v END NewVar;
  528. (** Parser **)
  529. PROCEDURE ParseConstExpr(VAR s: ARRAY OF CHAR);
  530. VAR start, end, tmp, i: INTEGER;
  531. x: CHAR;
  532. BEGIN
  533. IF sym = lparen THEN s := '('; i := 1
  534. ELSIF sym = int THEN Int.Str(ival, s); i := Strings.Length(s);
  535. ELSIF sym = ident THEN Strings.Copy(id, s); i := Strings.Length(s);
  536. ELSE MarkExp('constant expression'); i := 0
  537. END;
  538. IF i # 0 THEN
  539. start := Files.Pos(R); x := c;
  540. REPEAT GetSym UNTIL (sym = eot) OR (sym = comma) OR (sym = of) OR
  541. (sym = semicol);
  542. IF sym # eot THEN
  543. tmp := Files.Pos(R); end := tmp;
  544. IF sym = of THEN DEC(end, 3) ELSE DEC(end, 2) END;
  545. IF start < end THEN
  546. s[i] := x; INC(i);
  547. Files.Set(R, Files.Base(R), start);
  548. REPEAT
  549. Files.ReadChar(R, x);
  550. IF x < ' ' THEN x := ' ' END;
  551. IF (i < LEN(s) - 1) & ((x # ' ') OR (s[i - 1] # ' ')) THEN
  552. s[i] := x; INC(i)
  553. END
  554. UNTIL Files.Pos(R) >= end
  555. END;
  556. Files.Set(R, Files.Base(R), tmp)
  557. END
  558. END;
  559. WHILE (i # 1) & (s[i - 1] <= ' ') DO DEC(i) END;
  560. s[i] := 0X
  561. END ParseConstExpr;
  562. PROCEDURE ParseVars(needSemicol: BOOLEAN): List;
  563. VAR first, v: Var;
  564. L: List;
  565. x: Object;
  566. passed: INTEGER;
  567. T: Type;
  568. stop: BOOLEAN;
  569. BEGIN L := NewList(); stop := FALSE;
  570. WHILE ~stop & (sym = ident) DO
  571. first := NewVar(); GetSym; CheckExportMark(first);
  572. AddToList(L, first);
  573. WHILE sym = comma DO GetSym;
  574. IF sym = ident THEN v := NewVar(); GetSym; CheckExportMark(v);
  575. AddToList(L, v)
  576. ELSE MarkExp('variable (field) name')
  577. END
  578. END;
  579. IF sym = colon THEN GetSym ELSE MarkExp(':') END;
  580. T := ParseType();
  581. IF first # NIL THEN
  582. first.type := T; x := first.next;
  583. WHILE x # NIL DO x(Var).type := T; x := x.next END
  584. END;
  585. IF sym = semicol THEN GetSym
  586. ELSIF needSemicol THEN MarkExp(';')
  587. ELSE stop := TRUE
  588. END
  589. END
  590. RETURN L END ParseVars;
  591. PROCEDURE ParseConstDecl(M: Module);
  592. VAR C: Const;
  593. BEGIN M.consts := NewList();
  594. IF sym = const THEN GetSym;
  595. WHILE sym = ident DO
  596. C := NewConst(); GetSym; CheckExportMark(C);
  597. AddToList(M.consts, C);
  598. IF sym = equals THEN GetSym ELSE MarkExp('=') END;
  599. ParseConstExpr(C.value);
  600. IF sym = semicol THEN GetSym ELSE MarkExp(';') END
  601. END
  602. END
  603. END ParseConstDecl;
  604. PROCEDURE ParseTypeDecl(M: Module);
  605. VAR T: Type;
  606. BEGIN M.types := NewList();
  607. IF sym = type THEN GetSym;
  608. WHILE sym = ident DO
  609. T := NewType(namedType); AddToList(M.types, T);
  610. Strings.Copy(id, T.name); GetSym; CheckExportMark(T);
  611. IF sym = equals THEN GetSym ELSE MarkExp('=') END;
  612. T.base := ParseType();
  613. IF sym = semicol THEN GetSym ELSE MarkExp(';') END;
  614. END
  615. END
  616. END ParseTypeDecl;
  617. PROCEDURE ParseNamedType(): Type;
  618. VAR T: Type;
  619. BEGIN
  620. IF sym = ident THEN
  621. T := NewType(namedType);
  622. Strings.Copy(id, T.name);
  623. GetSym;
  624. IF sym = period THEN GetSym; Strings.Append('.', T.name);
  625. IF sym = ident THEN Strings.Append(id, T.name); GetSym
  626. ELSE MarkExp('identifier')
  627. END
  628. END
  629. ELSE T := NIL; MarkExp('type identifier')
  630. END
  631. RETURN T END ParseNamedType;
  632. PROCEDURE ParseArrayType(): Type;
  633. VAR T, T1: Type;
  634. BEGIN ASSERT(sym = array); GetSym;
  635. T := NewType(arrayType); T1 := T;
  636. IF (sym = int) OR (sym = ident) OR (sym = lparen) THEN
  637. ParseConstExpr(T.len)
  638. END;
  639. WHILE sym = comma DO GetSym;
  640. T1.base := NewType(arrayType); T1 := T1.base;
  641. ParseConstExpr(T1.len)
  642. END;
  643. IF sym = of THEN GetSym ELSE MarkExp('OF') END;
  644. T1.base := ParseType()
  645. RETURN T END ParseArrayType;
  646. PROCEDURE ParseRecordType(): Type;
  647. VAR T: Type;
  648. BEGIN ASSERT(sym = record); GetSym; T := NewType(recordType);
  649. IF sym = lparen THEN GetSym; T.base := ParseNamedType();
  650. IF sym = rparen THEN GetSym ELSE MarkExp(')') END
  651. END;
  652. T.fields := ParseVars(FALSE);
  653. IF sym = end THEN GetSym ELSE MarkExp('END') END
  654. RETURN T END ParseRecordType;
  655. PROCEDURE ParsePointerType(): Type;
  656. VAR T: Type;
  657. BEGIN ASSERT(sym = pointer); GetSym; T := NewType(pointerType);
  658. IF sym = to THEN GetSym ELSE MarkExp('TO') END;
  659. T.base := ParseType()
  660. RETURN T END ParsePointerType;
  661. PROCEDURE ParseFormalParamSection(L: List);
  662. VAR first, par: Param;
  663. x: Object;
  664. T: Type;
  665. passed: INTEGER;
  666. BEGIN
  667. IF sym = var THEN GetSym; passed := byVar ELSE passed := byValue END;
  668. IF sym = ident THEN first := NewParam(passed); GetSym;
  669. AddToList(L, first);
  670. WHILE sym = comma DO GetSym;
  671. IF sym = ident THEN par := NewParam(passed); GetSym;
  672. AddToList(L, par)
  673. ELSE MarkExp('parameter name')
  674. END
  675. END
  676. ELSE first := NIL; MarkExp('parameter name')
  677. END;
  678. IF sym = colon THEN GetSym; T := ParseParamType();
  679. IF first # NIL THEN
  680. first.type := T;
  681. x := first.next;
  682. WHILE x # NIL DO x(Param).type := T; x := x.next END
  683. END
  684. ELSE MarkExp(':')
  685. END
  686. END ParseFormalParamSection;
  687. PROCEDURE ParseProcedureType(): Type;
  688. VAR T: Type;
  689. BEGIN ASSERT(sym = procedure); GetSym;
  690. T := NewType(procedureType); T.fields := NewList();
  691. IF sym = lparen THEN GetSym;
  692. IF sym # rparen THEN ParseFormalParamSection(T.fields);
  693. WHILE sym = semicol DO GetSym; ParseFormalParamSection(T.fields) END
  694. END;
  695. IF sym = rparen THEN GetSym ELSE MarkExp(')') END;
  696. IF sym = colon THEN GetSym; T.base := ParseNamedType() END
  697. END;
  698. (*!TODO*)
  699. RETURN T END ParseProcedureType;
  700. PROCEDURE ParseParamType0(): Type;
  701. VAR T: Type;
  702. BEGIN
  703. IF sym = array THEN T := ParseArrayType()
  704. ELSIF sym = ident THEN T := ParseNamedType()
  705. ELSE T := NIL; MarkExp('type')
  706. END
  707. RETURN T END ParseParamType0;
  708. PROCEDURE ParseType0(): Type;
  709. VAR T: Type;
  710. BEGIN
  711. IF sym = array THEN T := ParseArrayType()
  712. ELSIF sym = record THEN T := ParseRecordType()
  713. ELSIF sym = pointer THEN T := ParsePointerType()
  714. ELSIF sym = procedure THEN T := ParseProcedureType()
  715. ELSIF sym = ident THEN T := ParseNamedType()
  716. ELSE T := NIL; MarkExp('type')
  717. END
  718. RETURN T END ParseType0;
  719. (** Reads input stream until "END name" is found.
  720. Stops on "name" (sym = ident), or sym = eot *)
  721. PROCEDURE ReachEndOf(name: ARRAY OF CHAR);
  722. BEGIN
  723. REPEAT
  724. WHILE (sym # eot) & (sym # end) DO GetSym END;
  725. IF sym = end THEN GetSym END
  726. UNTIL (sym = eot) OR (sym = ident) & (id = name)
  727. END ReachEndOf;
  728. PROCEDURE ParseProcedureDecl(M: Module);
  729. VAR name: Str;
  730. P: Procedure;
  731. BEGIN M.procedures := NewList();
  732. WHILE sym = procedure DO GetSym; NEW(P); InitObject(P);
  733. P.params := NewList(); P.exported := FALSE;
  734. IF (sym = minus) OR (sym = times) OR (sym = arrow) THEN GetSym END;
  735. IF sym = ident THEN Strings.Copy(id, P.name); GetSym
  736. ELSE MarkExp('procedure name')
  737. END;
  738. IF (sym = minus) OR (sym = arrow) THEN GetSym END;
  739. IF sym = times THEN GetSym; P.exported := TRUE END;
  740. IF sym = lparen THEN GetSym;
  741. IF sym # rparen THEN ParseFormalParamSection(P.params);
  742. WHILE sym = semicol DO GetSym; ParseFormalParamSection(P.params) END
  743. END;
  744. IF sym = rparen THEN GetSym ELSE MarkExp(')') END;
  745. IF sym = colon THEN GetSym; P.returnType := ParseNamedType() END
  746. END;
  747. IF sym = semicol THEN GetSym ELSE MarkExp(';') END;
  748. ReachEndOf(P.name);
  749. IF sym = ident THEN GetSym;
  750. IF sym = semicol THEN GetSym ELSE MarkExp(';') END
  751. ELSE (* sym = eot *) MarkEnd('Procedure', P.name)
  752. END;
  753. AddToList(M.procedures, P)
  754. END
  755. END ParseProcedureDecl;
  756. PROCEDURE ParseVarDecl(M: Module);
  757. BEGIN ASSERT(sym = var); GetSym; M.vars := ParseVars(TRUE)
  758. END ParseVarDecl;
  759. PROCEDURE Declarations(M: Module);
  760. BEGIN
  761. IF sym = const THEN ParseConstDecl(M) END;
  762. IF sym = type THEN ParseTypeDecl(M) END;
  763. IF sym = var THEN ParseVarDecl(M) END;
  764. ParseProcedureDecl(M)
  765. END Declarations;
  766. PROCEDURE ParseImport(M: Module);
  767. BEGIN
  768. REPEAT GetSym UNTIL (sym = eot) OR (sym = procedure) OR (sym = begin) OR
  769. (sym = end) OR (sym = const) OR (sym = type) OR (sym = var)
  770. END ParseImport;
  771. PROCEDURE ParseModule*(VAR r: Files.Rider; VAR err: ARRAY OF CHAR): Module;
  772. VAR M: Module;
  773. BEGIN NEW(M); InitObject(M); M.foreign := FALSE;
  774. R := r; c := 0X; line := 1; col := 0; lastError := -1;
  775. Read; ClearComments; GetSym;
  776. IF sym = module THEN GetSym;
  777. IF sym = lbrak THEN GetSym;
  778. IF (sym = ident) & (id = 'foreign') THEN M.foreign := TRUE END;
  779. REPEAT GetSym UNTIL (sym = eot) OR (sym = rbrak);
  780. GetSym
  781. END;
  782. IF sym = ident THEN Strings.Copy(id, M.name); GetSym
  783. ELSE MarkExp('module name')
  784. END;
  785. IF sym = semicol THEN GetSym ELSE MarkExp(';') END;
  786. IF sym = import THEN ParseImport(M) END;
  787. Declarations(M);
  788. IF sym = begin THEN
  789. REPEAT GetSym UNTIL (sym = eot) OR (sym = end)
  790. END;
  791. ReachEndOf(M.name);
  792. IF sym = ident THEN GetSym;
  793. IF sym # period THEN MarkExp('.') END
  794. ELSE (* sym = eot *) MarkEnd('Module', M.name)
  795. END
  796. ELSE MarkExp('MODULE')
  797. END;
  798. IF lastError # -1 THEN M := NIL; err := 'Error' (*!FIXME*) END
  799. RETURN M END ParseModule;
  800. BEGIN curFname[0] := 0X;
  801. PrintObject := PrintObject0;
  802. ParseType := ParseType0;
  803. ParseParamType := ParseParamType0
  804. END AutodocParser.