PARSER.MOD 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. IMPLEMENTATION MODULE Parser;
  2. (*
  3. * REPERTOIRE
  4. * Release 1.6
  5. * By Charles Bradford and Cole Brecheen
  6. * (c) Copyright 1985-1992 PMI
  7. * Green Bay, Wisconsin
  8. * All rights reserved
  9. * (414) 468-6040
  10. *
  11. * $Header: D:/logfiles/mods/parser.mov 1.9 17 Mar 1991 17:57:48 coleb $
  12. *
  13. *)
  14. (*EntryDiag:
  15. IMPORT Diagnostics;
  16. :EntryDiag*)
  17. IMPORT GenLists;
  18. IMPORT ListUtils;
  19. IMPORT M2Strings;
  20. IMPORT PosUtils;
  21. IMPORT StrEdit;
  22. IMPORT SYSTEM;
  23. VAR
  24. Initialized : BOOLEAN;
  25. CONST
  26. space = 40C;
  27. VAR
  28. LineLngth: CARDINAL;
  29. PROCEDURE GetNxtLstChr(InList: GenLists.GenList; VAR currentLine,
  30. currentIndex: CARDINAL; VAR TheChar: CHAR);
  31. (* On entry, currentIndex should identify a character that has
  32. already been gotten. On exit, it will identify the character
  33. just returned; currentLine will identify the line from which
  34. it was gotten, even if it was the last character on the line. *)
  35. VAR
  36. typeCode : CARDINAL;
  37. BEGIN
  38. LineLngth := M2Strings.Length(TheLine);
  39. IF (currentIndex + 1) >= LineLngth THEN
  40. (* we are at a line end *)
  41. currentIndex := 0;
  42. IF currentLine < GenLists.ListLength(InList) THEN
  43. INC(currentLine);
  44. GenLists.GetElmt(InList, currentLine, TheLine, typeCode);
  45. (*read next line*)
  46. LineLngth := M2Strings.Length(TheLine);
  47. TheChar := TheLine[currentIndex];
  48. ELSE
  49. TheChar := EOF;
  50. (* we are at file end *)
  51. END;
  52. ELSE
  53. INC(currentIndex);
  54. TheChar := TheLine[currentIndex];
  55. END;
  56. END GetNxtLstChr;
  57. PROCEDURE SkipComments(VAR inList: GenLists.GenList;
  58. VAR currentIndex, currentLine: CARDINAL);
  59. (* Handles nested comments
  60. currentIndex passed in points to first char of a StartComment
  61. currentIndex passed out points to the char after EndComment *)
  62. VAR
  63. terminator: CHAR;
  64. tmpLst: GenLists.GenList;
  65. BEGIN
  66. GenLists.NewList(tmpLst);
  67. GetLogicalLst(
  68. inList,
  69. StartComment,
  70. EndComment,
  71. currentLine,
  72. currentIndex,
  73. tmpLst,
  74. terminator);
  75. GenLists.DisposeList(tmpLst);
  76. END SkipComments;
  77. PROCEDURE ReadComments(VAR InList, OutList: GenLists.GenList;
  78. NewStartComment, NewEndComment: ARRAY OF CHAR;
  79. VAR currentIndex, currentLine: CARDINAL);
  80. (* Extracts comments from InList and appends them to OutList.
  81. OutList must be initialized before it gets here. If it has
  82. at least one element, we append the first line of the comment
  83. to the last line of OutList. Handles nested comments;
  84. currentIndex passed in points to first char of a StartComment;
  85. currentIndex passed out points to the char after EndComment. *)
  86. VAR
  87. StartCmtIndex, EndCmtIndex, TypeCode,
  88. NestingLevel, CommentLength, NewCommentLength,
  89. outListLngth: CARDINAL;
  90. tmpch: CHAR;
  91. BEGIN
  92. CommentLength := M2Strings.Length(StartComment);
  93. NewCommentLength := M2Strings.Length(NewStartComment);
  94. NestingLevel := 1;
  95. StartCmtIndex := PosUtils.Positn(StartComment, TheLine, currentIndex);
  96. IF currentIndex # StartCmtIndex THEN
  97. (*We weren't on a StartComment.*)
  98. RETURN;
  99. END;
  100. INC(currentIndex, CommentLength);
  101. (* get currentIndex past the opening comment *)
  102. outListLngth := GenLists.ListLength(OutList);
  103. IF outListLngth > 0 THEN
  104. ListUtils.InsertIntoElmt(OutList, outListLngth,
  105. SYSTEM.ADR(NewStartComment), NewCommentLength, 65535);
  106. ELSE
  107. INC(outListLngth);
  108. GenLists.ListInsert(NewStartComment, GenLists.StrCode, OutList,
  109. outListLngth);
  110. END;
  111. LOOP
  112. GetNxtLstChr(InList, currentLine, currentIndex, tmpch);
  113. IF tmpch = StartComment[0] THEN
  114. StartCmtIndex := PosUtils.Positn(StartComment, TheLine,
  115. currentIndex);
  116. IF currentIndex = StartCmtIndex THEN
  117. (*We were on a StartComment.*)
  118. INC(NestingLevel);
  119. END;
  120. END;
  121. IF tmpch = EndComment[0] THEN
  122. EndCmtIndex := PosUtils.Positn(EndComment, TheLine, currentIndex);
  123. IF currentIndex = EndCmtIndex THEN
  124. (*We were on an EndComment.*)
  125. DEC(NestingLevel);
  126. END;
  127. IF NestingLevel = 0 THEN
  128. INC(currentIndex, CommentLength);
  129. ListUtils.InsertIntoElmt(OutList, outListLngth,
  130. SYSTEM.ADR(NewEndComment), NewCommentLength, 65535);
  131. EXIT;
  132. END;
  133. END;
  134. IF currentIndex > 0 THEN
  135. ListUtils.InsertIntoElmt(OutList, outListLngth,
  136. SYSTEM.ADR(tmpch), 1, 65535);
  137. ELSE
  138. (* Every time currentIndex goes to zero we start a new
  139. line in the OutList. *)
  140. INC(outListLngth);
  141. GenLists.ListInsert(tmpch, GenLists.StrCode, OutList,
  142. outListLngth);
  143. END;
  144. END;
  145. END ReadComments;
  146. PROCEDURE GetLogicalLst(
  147. inList: GenLists.GenList;
  148. opener, closer: ARRAY OF CHAR;
  149. VAR currentLine, currentIndex: CARDINAL;
  150. VAR outList: GenLists.GenList;
  151. VAR terminator: CHAR);
  152. (* On entry, currentIndex should identify a character that has
  153. * already been gotten (opener). On exit, it will identify the
  154. * character just returned (closer or Parser.EOF); currentLine
  155. * will identify the line from which it was gotten, even if it
  156. * was the last character on the line. outList should be
  157. * initialized on entry, and will be bracketed by opener and
  158. * closer on exit.
  159. *)
  160. VAR
  161. openerIndex, closerIndex,
  162. NestingLevel, openerLngth,
  163. outListLngth: CARDINAL;
  164. tmpch: CHAR;
  165. BEGIN
  166. openerLngth := M2Strings.Length(opener);
  167. NestingLevel := 1;
  168. openerIndex := PosUtils.Positn(opener, TheLine, currentIndex);
  169. IF currentIndex # openerIndex THEN
  170. (*We weren't on a opener.*)
  171. RETURN;
  172. END;
  173. outListLngth := GenLists.ListLength(outList);
  174. IF outListLngth > 0 THEN
  175. ListUtils.InsertIntoElmt(outList, outListLngth,
  176. SYSTEM.ADR(opener), openerLngth, 65535);
  177. ELSE
  178. INC(outListLngth);
  179. GenLists.ListInsert(opener, GenLists.StrCode, outList,
  180. outListLngth);
  181. END;
  182. LOOP
  183. GetNxtLstChr(inList, currentLine, currentIndex, tmpch);
  184. IF tmpch = opener[0] THEN
  185. openerIndex := PosUtils.Positn(opener, TheLine,
  186. currentIndex);
  187. IF currentIndex = openerIndex THEN
  188. (* We were on an opener. We don't increment currentIndex
  189. * past it because, if opener contains more than one
  190. * character, we want them all copied into outList.
  191. *)
  192. INC(NestingLevel);
  193. END;
  194. END;
  195. IF tmpch = closer[0] THEN
  196. closerIndex := PosUtils.Positn(closer, TheLine, currentIndex);
  197. IF currentIndex = closerIndex THEN
  198. (*We were on a closer.*)
  199. DEC(NestingLevel);
  200. END;
  201. IF NestingLevel = 0 THEN
  202. INC(currentIndex, openerLngth);
  203. ListUtils.InsertIntoElmt(outList, outListLngth,
  204. SYSTEM.ADR(closer), openerLngth, 65535);
  205. EXIT;
  206. END;
  207. END;
  208. IF currentIndex > 0 THEN
  209. ListUtils.InsertIntoElmt(outList, outListLngth,
  210. SYSTEM.ADR(tmpch), 1, 65535);
  211. ELSE
  212. (* Every time currentIndex goes to zero we start a new
  213. line in the OutList. *)
  214. INC(outListLngth);
  215. GenLists.ListInsert(tmpch, GenLists.StrCode, outList,
  216. outListLngth);
  217. END;
  218. END;
  219. END GetLogicalLst;
  220. PROCEDURE GetLogicalStr(
  221. inList: GenLists.GenList;
  222. opener, closer: ARRAY OF CHAR;
  223. VAR currentLine, currentIndex: CARDINAL;
  224. VAR outStr: ARRAY OF CHAR;
  225. VAR terminator: CHAR);
  226. VAR
  227. tmpLst: GenLists.GenList;
  228. BEGIN
  229. GenLists.NewList(tmpLst);
  230. GetLogicalLst(inList, opener, closer, currentLine, currentIndex,
  231. tmpLst, terminator);
  232. ListUtils.ListToString(tmpLst, outStr);
  233. GenLists.DisposeList(tmpLst);
  234. END GetLogicalStr;
  235. PROCEDURE GetNextWord(
  236. InList: GenLists.GenList;
  237. VAR CurrentLine, CurrentIndex: CARDINAL;
  238. VAR NextWord: ARRAY OF CHAR;
  239. VAR Terminator: CHAR);
  240. (* To make this start at the beginning of the first line,
  241. you have to pass 0 to both CurrentLine and CurrentIndex.
  242. Algorithm is:
  243. starting from position CurrentIndex in line CurrentLine:
  244. skip over any blanks
  245. if at end of line, get a new line
  246. NextWord := text up to next delimiter
  247. If delimiter found = StartComment[0], skip any comments
  248. skip over any blanks
  249. leave index on next character
  250. that next character will normally not be a delimiter;
  251. Weird conditions:
  252. if next char is a delimiter other than a blank,
  253. a null nextword will be returned next time
  254. if get last word on a line, index is left at the end of line
  255. *)
  256. VAR
  257. NextDelim, TypeCode: CARDINAL;
  258. dumstr: ARRAY [0..191] OF CHAR;
  259. QuoteChar: CHAR;
  260. DelimFound : BOOLEAN;
  261. BEGIN
  262. StrEdit.SetLength(NextWord, 0);
  263. LineLngth := M2Strings.Length(TheLine);
  264. LOOP
  265. (* We do this first loop just to make sure we have a line to
  266. work with (we might need to read another one) and to get
  267. to the next nonblank character (which might be a nonblank
  268. delimiter. *)
  269. IF CurrentIndex >= LineLngth THEN
  270. (* we are at a line end *)
  271. CurrentIndex := 0;
  272. IF CurrentLine < GenLists.ListLength(InList) THEN
  273. (* We aren't at the end of the file. *)
  274. INC(CurrentLine);
  275. GenLists.GetElmt(InList, CurrentLine, TheLine, TypeCode);
  276. (*read next line*)
  277. LineLngth := M2Strings.Length(TheLine);
  278. ELSE
  279. Terminator := EOF;
  280. (* We are at file end; EOF as Terminator is what
  281. the caller is supposed to look for to know that the
  282. end has been reached. *)
  283. RETURN;
  284. END;
  285. ELSIF PosUtils.Present(TheLine[CurrentIndex], whitespace) THEN
  286. (* skip the character if it's a blank and loop again *)
  287. INC(CurrentIndex);
  288. ELSE
  289. EXIT;
  290. (* We are on a non-blank, but notice that it might be
  291. a delimiter. *)
  292. END;
  293. END;
  294. LastIndex := CurrentIndex;
  295. NextDelim := CurrentIndex;
  296. IF ((TheLine[NextDelim] = '"') OR (TheLine[NextDelim] = "'"))
  297. AND PosUtils.Present(TheLine[NextDelim], delimiters) THEN
  298. (*Looks like beginning of a string literal. Find end of it.*)
  299. INC(CurrentIndex);
  300. (*Prevents copying opening quote into NextWord.*)
  301. QuoteChar := TheLine[NextDelim];
  302. REPEAT
  303. (* skip until come to next delimiter *)
  304. INC(NextDelim);
  305. UNTIL (NextDelim >= LineLngth) OR
  306. (TheLine[NextDelim] = QuoteChar);
  307. ELSIF ((TheLine[NextDelim] = StartComment[0]) AND
  308. (TheLine[NextDelim + 1] = StartComment[1])) THEN
  309. StrEdit.AssignStr(StartComment, NextWord);
  310. CurrentIndex := NextDelim;
  311. (* We want to give the user a choice between reading or
  312. skipping the comment. We do that by telling them that
  313. NextWord = StartComment. If they want to skip it, they
  314. call SkipComments. If they want it, they call
  315. ReadComments. *)
  316. Terminator := space;
  317. (* Notice that this is the only case where we exit with
  318. CurrentIndex pointing to the word itself rather than
  319. to the first nonblank character following the word. *)
  320. RETURN;
  321. ELSE
  322. DelimFound := FALSE;
  323. WHILE (NextDelim < LineLngth) AND (NOT DelimFound) DO
  324. IF (PosUtils.Present(TheLine[NextDelim], delimiters)) THEN
  325. DelimFound := TRUE;
  326. ELSE
  327. (* skip until we come to next delimiter *)
  328. INC(NextDelim);
  329. END;
  330. END;
  331. END;
  332. M2Strings.Copy(TheLine, CurrentIndex, NextDelim - CurrentIndex,
  333. NextWord);
  334. (* NextWord now contains everything between CurrentIndex and
  335. NextDelim -- not including the delimiter itself. *)
  336. CurrentIndex := NextDelim;
  337. (* Now we need to tell them what the terminating character was.
  338. If there was whitespace between the end of the word and the
  339. next item, we'll only tell them that the Terminator was a space
  340. if the space was required (i.e., if the next item began with
  341. something other than a delimiter. Otherwise we'll let them
  342. assume that everything was jammed together with the least
  343. possible amount of whitespace. *)
  344. WHILE (CurrentIndex < LineLngth) AND
  345. (PosUtils.Present(TheLine[CurrentIndex], whitespace)) DO
  346. INC(CurrentIndex);
  347. (* skip over blanks *)
  348. END;
  349. IF (CurrentIndex < LineLngth) AND
  350. (* if on a delimiter *)
  351. (PosUtils.Present(TheLine[CurrentIndex], delimiters)) THEN
  352. Terminator := TheLine[CurrentIndex];
  353. (* return delimiter after word *)
  354. IF ((TheLine[CurrentIndex] # StartComment[0]) OR
  355. (TheLine[CurrentIndex + 1] # StartComment[1])) THEN
  356. (* We have a kludgy convention that for comments, and nothing
  357. else, we leave CurrentIndex pointing to the first character
  358. of the comment. *)
  359. INC(CurrentIndex);
  360. END;
  361. (* skip over delimiter before exit *)
  362. ELSIF CurrentIndex >= LineLngth THEN
  363. (* if at end of line *)
  364. Terminator := EOL;
  365. ELSE
  366. (* We know we aren't sitting on a delimiter, and we're not at
  367. the end of the line, so we must have been terminated by a
  368. blank between words *)
  369. Terminator := space;
  370. END;
  371. END GetNextWord;
  372. PROCEDURE Init();
  373. BEGIN
  374. IF Initialized THEN
  375. RETURN;
  376. ELSE
  377. Initialized := TRUE;
  378. END;
  379. (*EntryDiag:
  380. Diagnostics.Init();
  381. :EntryDiag*)
  382. GenLists.Init();
  383. ListUtils.Init();
  384. M2Strings.Init();
  385. PosUtils.Init();
  386. StrEdit.Init();
  387. (*EntryDiag:
  388. Diagnostics.diagS('Entering Parser', '');
  389. :EntryDiag*)
  390. StrEdit.AssignStr(' ;:(){}[]-=",.^+*/|#', delimiters);
  391. StrEdit.Append(delimiters, "'");
  392. StrEdit.Append(delimiters, EOL);
  393. StrEdit.Append(delimiters, EOF);
  394. StrEdit.Append(delimiters, TAB);
  395. StrEdit.SetLength(TheLine, 0);
  396. StartComment := '(*';
  397. EndComment := '*)';
  398. whitespace[0] := space;
  399. whitespace[1] := TAB;
  400. whitespace[2] := EOL;
  401. whitespace[3] := 12C; (* linefeed *)
  402. whitespace[4] := 0C;
  403. (*EntryDiag:
  404. Diagnostics.diagS('Exiting Parser', '');
  405. :EntryDiag*)
  406. END Init;
  407. BEGIN
  408. Initialized := FALSE;
  409. Init();
  410. END Parser.