M2.mod 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. MODULE M2;
  2. (* This is an example of a rudimentary main module for use with COCO/R.
  3. It assumes the FileIO/Storage I/O libraries (as supplied with this
  4. project) are available.
  5. The auxiliary modules <Grammar>S (scanner) and <Grammar>P (parser)
  6. are assumed to have been constructed with COCO/R compiler generator. *)
  7. FROM M2S IMPORT lst, src, errors, Error, CharAt;
  8. FROM M2P IMPORT Parse, Successful;
  9. IMPORT
  10. Strings, Storage, SYSTEM, FileIO, SymTab, QbeGen;
  11. (* and any others needed *)
  12. TYPE
  13. INT32 = FileIO.INT32 (* 32 bit integers needed *);
  14. MODULE ListHandler;
  15. (* ------------------- Source Listing and Error handler -------------- *)
  16. FROM FileIO IMPORT CR, LF, EOF, File, StdOut,
  17. WriteString, Write, WriteLn, WriteInt, Long0;
  18. FROM Storage IMPORT ALLOCATE;
  19. FROM SYSTEM IMPORT TSIZE;
  20. IMPORT lst, CharAt, errors, INT32;
  21. EXPORT StoreError, PrintListing, PrintConsoleErrors, ResetErrors;
  22. TYPE
  23. Err = POINTER TO ErrDesc;
  24. ErrDesc = RECORD
  25. nr, line, col: INTEGER;
  26. next: Err
  27. END;
  28. CONST
  29. tab = CHR(9);
  30. VAR
  31. firstErr, lastErr: Err;
  32. Extra: INTEGER;
  33. PROCEDURE StoreError (nr, line, col: INTEGER; pos: INT32);
  34. (* Store an error message for later printing *)
  35. VAR
  36. nextErr: Err;
  37. BEGIN
  38. ALLOCATE(nextErr, TSIZE(ErrDesc));
  39. nextErr^.nr := nr; nextErr^.line := line; nextErr^.col := col;
  40. nextErr^.next := NIL;
  41. IF firstErr = NIL
  42. THEN firstErr := nextErr
  43. ELSE lastErr^.next := nextErr
  44. END;
  45. lastErr := nextErr;
  46. INC(errors)
  47. END StoreError;
  48. PROCEDURE GetLine (VAR pos: INT32;
  49. VAR line: ARRAY OF CHAR;
  50. VAR eof: BOOLEAN);
  51. (* Read a source line. Return empty line if eof *)
  52. VAR
  53. ch: CHAR;
  54. i: CARDINAL;
  55. BEGIN
  56. i := 0; eof := FALSE; ch := CharAt(pos); INC(pos);
  57. WHILE (ch # CR) AND (ch # LF) AND (ch # EOF) DO
  58. line[i] := ch; INC(i); ch := CharAt(pos); INC(pos);
  59. END;
  60. eof := (i = 0) AND (ch = EOF); line[i] := CHR(0);
  61. IF ch = CR THEN (* check for MsDos *)
  62. ch := CharAt(pos);
  63. IF ch = LF THEN INC(pos); Extra := 0 END
  64. END
  65. END GetLine;
  66. PROCEDURE ErrText (nr: INTEGER; f: File);
  67. (* Error message text for nr on file f (listing and console share it) *)
  68. PROCEDURE Msg (s: ARRAY OF CHAR);
  69. BEGIN
  70. WriteString(f, s)
  71. END Msg;
  72. BEGIN
  73. CASE nr OF
  74. 0: Msg("EOF expected")
  75. | 1: Msg("ident expected")
  76. | 2: Msg("integer expected")
  77. | 3: Msg("real expected")
  78. | 4: Msg("string expected")
  79. | 5: Msg("'.' expected")
  80. | 6: Msg("'DEFINITION' expected")
  81. | 7: Msg("'MODULE' expected")
  82. | 8: Msg("';' expected")
  83. | 9: Msg("'END' expected")
  84. | 10: Msg("'IMPLEMENTATION' expected")
  85. | 11: Msg("'BEGIN' expected")
  86. | 12: Msg("'INLINE' expected")
  87. | 13: Msg("'LABEL' expected")
  88. | 14: Msg("',' expected")
  89. | 15: Msg("'[' expected")
  90. | 16: Msg("']' expected")
  91. | 17: Msg("'EXPORT' expected")
  92. | 18: Msg("'QUALIFIED' expected")
  93. | 19: Msg("'FROM' expected")
  94. | 20: Msg("'IMPORT' expected")
  95. | 21: Msg("'TYPE' expected")
  96. | 22: Msg("'=' expected")
  97. | 23: Msg("'VIRTUAL' expected")
  98. | 24: Msg("'POINTER' expected")
  99. | 25: Msg("'..' expected")
  100. | 26: Msg("'(' expected")
  101. | 27: Msg("')' expected")
  102. | 28: Msg("'ARRAY' expected")
  103. | 29: Msg("'OF' expected")
  104. | 30: Msg("'RECORD' expected")
  105. | 31: Msg("'CASE' expected")
  106. | 32: Msg("':' expected")
  107. | 33: Msg("'|' expected")
  108. | 34: Msg("'ELSE' expected")
  109. | 35: Msg("'SET' expected")
  110. | 36: Msg("'TO' expected")
  111. | 37: Msg("'PROCEDURE' expected")
  112. | 38: Msg("'VAR' expected")
  113. | 39: Msg("'CLASS' expected")
  114. | 40: Msg("'FORWARD' expected")
  115. | 41: Msg("'CONST' expected")
  116. | 42: Msg("'::=' expected")
  117. | 43: Msg("'IN' expected")
  118. | 44: Msg("'GOTO' expected")
  119. | 45: Msg("'EXIT' expected")
  120. | 46: Msg("':=' expected")
  121. | 47: Msg("'IF' expected")
  122. | 48: Msg("'THEN' expected")
  123. | 49: Msg("'ELSIF' expected")
  124. | 50: Msg("'WHILE' expected")
  125. | 51: Msg("'DO' expected")
  126. | 52: Msg("'REPEAT' expected")
  127. | 53: Msg("'UNTIL' expected")
  128. | 54: Msg("'LOOP' expected")
  129. | 55: Msg("'FOR' expected")
  130. | 56: Msg("'BY' expected")
  131. | 57: Msg("'RETURN' expected")
  132. | 58: Msg("'WITH' expected")
  133. | 59: Msg("'^' expected")
  134. | 60: Msg("'IS' expected")
  135. | 61: Msg("'#' expected")
  136. | 62: Msg("'<>' expected")
  137. | 63: Msg("'<' expected")
  138. | 64: Msg("'<=' expected")
  139. | 65: Msg("'>' expected")
  140. | 66: Msg("'>=' expected")
  141. | 67: Msg("'+' expected")
  142. | 68: Msg("'-' expected")
  143. | 69: Msg("'OR' expected")
  144. | 70: Msg("'*' expected")
  145. | 71: Msg("'/' expected")
  146. | 72: Msg("'DIV' expected")
  147. | 73: Msg("'MOD' expected")
  148. | 74: Msg("'AND' expected")
  149. | 75: Msg("'&' expected")
  150. | 76: Msg("'<<' expected")
  151. | 77: Msg("'>>' expected")
  152. | 78: Msg("'{' expected")
  153. | 79: Msg("'}' expected")
  154. | 80: Msg("'NOT' expected")
  155. | 81: Msg("'~' expected")
  156. | 82: Msg("not expected")
  157. | 83: Msg("invalid MulOp")
  158. | 84: Msg("invalid Fact")
  159. | 85: Msg("invalid AddOp")
  160. | 86: Msg("invalid Rel")
  161. | 87: Msg("invalid DesignStat")
  162. | 88: Msg("invalid Statement")
  163. | 89: Msg("invalid MethodImpl")
  164. | 90: Msg("invalid ClassField")
  165. | 91: Msg("invalid ClassRest")
  166. | 92: Msg("invalid FieldList")
  167. | 93: Msg("invalid FieldList")
  168. | 94: Msg("invalid SetType")
  169. | 95: Msg("invalid ArrayType")
  170. | 96: Msg("invalid Type")
  171. | 97: Msg("invalid TypeItem")
  172. | 98: Msg("invalid ProcDecl")
  173. | 99: Msg("invalid ProcDecl")
  174. |100: Msg("invalid ClassItem")
  175. |101: Msg("invalid ConstDecl")
  176. |102: Msg("invalid Import")
  177. |103: Msg("invalid Unit")
  178. (* add customized cases here *)
  179. | 200: Msg("duplicate identifier")
  180. | 201: Msg("undeclared identifier")
  181. | 202: Msg("module name mismatch")
  182. | 210: Msg("incompatible assignment")
  183. | 211: Msg("arithmetic operand must be numeric")
  184. | 212: Msg("boolean operand required")
  185. | 213: Msg("incompatible comparison")
  186. | 214: Msg("BOOLEAN condition required")
  187. | 215: Msg("not a RECORD type")
  188. | 216: Msg("unknown field")
  189. | 217: Msg("not an ARRAY type")
  190. | 218: Msg("array index must be integer")
  191. | 219: Msg("not a POINTER type")
  192. | 220: Msg("FOR needs integer variable and bounds")
  193. | 221: Msg("not a type name")
  194. | 222: Msg("set operand mismatch")
  195. | 223: Msg("cyclical type definition")
  196. | 224: Msg("ordinal type required")
  197. | 230: Msg("not supported yet")
  198. | 231: Msg("opaque type outside definition")
  199. | 232: Msg("bad RETURN")
  200. | 233: Msg("invalid call")
  201. | 234: Msg("invalid UTF-8 in U-literal")
  202. | 235: Msg("implementation does not match definition")
  203. | 236: Msg("bad variant record")
  204. ELSE Msg("Error: "); WriteInt(f, nr, 0);
  205. END
  206. END ErrText;
  207. PROCEDURE PrintErr (line: ARRAY OF CHAR; nr, col: INTEGER);
  208. (* Print an error message *)
  209. PROCEDURE Pointer;
  210. VAR
  211. i: INTEGER;
  212. BEGIN
  213. WriteString(lst, "***** ");
  214. i := 0;
  215. WHILE i < col + Extra - 2 DO
  216. IF line[i] = tab
  217. THEN Write(lst, tab)
  218. ELSE Write(lst, ' ')
  219. END;
  220. INC(i)
  221. END;
  222. WriteString(lst, "^ ")
  223. END Pointer;
  224. BEGIN
  225. Pointer;
  226. ErrText(nr, lst);
  227. WriteLn(lst)
  228. END PrintErr;
  229. PROCEDURE PrintListing;
  230. (* Print a source listing with error messages *)
  231. VAR
  232. nextErr: Err;
  233. eof, done: BOOLEAN;
  234. lnr, errC: INTEGER;
  235. srcPos: INT32;
  236. line: ARRAY [0 .. 255] OF CHAR;
  237. BEGIN
  238. WriteString(lst, "Listing:");
  239. WriteLn(lst); WriteLn(lst);
  240. srcPos := 0; nextErr := firstErr;
  241. GetLine(srcPos, line, eof); lnr := 1; errC := 0;
  242. WHILE NOT eof DO
  243. WriteInt(lst, lnr, 5); WriteString(lst, " ");
  244. WriteString(lst, line); WriteLn(lst);
  245. done := FALSE;
  246. WHILE (nextErr # NIL) AND NOT done DO
  247. IF nextErr^.line = lnr THEN
  248. PrintErr(line, nextErr^.nr, nextErr^.col); INC(errC);
  249. nextErr := nextErr^.next
  250. ELSE done := TRUE
  251. END
  252. END;
  253. GetLine(srcPos, line, eof); INC(lnr);
  254. END;
  255. IF nextErr # NIL THEN
  256. WriteInt(lst, lnr, 5); WriteLn(lst);
  257. WHILE nextErr # NIL DO
  258. PrintErr(line, nextErr^.nr, nextErr^.col); INC(errC);
  259. nextErr := nextErr^.next
  260. END
  261. END;
  262. WriteLn(lst);
  263. WriteInt(lst, errC, 5); WriteString(lst, " error");
  264. IF errC # 1 THEN Write(lst, 's') END;
  265. WriteLn(lst); WriteLn(lst); WriteLn(lst);
  266. END PrintListing;
  267. PROCEDURE PrintConsoleErrors (fname: ARRAY OF CHAR);
  268. (* "file:line:col: message" errors on StdOut, for editor
  269. integration. Columns are 1-based token starts, matching the
  270. listing carets (NOT the col-1 of CRQ.FRM). *)
  271. PROCEDURE WriteNum (n: INTEGER);
  272. (* FileIO.WriteInt pads a leading blank; editors want none. *)
  273. VAR
  274. buf: ARRAY [0 .. 15] OF CHAR;
  275. i: CARDINAL;
  276. BEGIN
  277. IF n < 0 THEN Write(StdOut, "-"); n := -n END;
  278. i := 0;
  279. IF n = 0 THEN buf[0] := "0"; i := 1 END;
  280. WHILE (n > 0) AND (i <= HIGH(buf)) DO
  281. buf[i] := CHR(ORD("0") + VAL(CARDINAL, n MOD 10));
  282. n := n DIV 10; INC(i)
  283. END;
  284. WHILE i > 0 DO DEC(i); Write(StdOut, buf[i]) END
  285. END WriteNum;
  286. VAR
  287. e: Err;
  288. BEGIN
  289. e := firstErr;
  290. WHILE e # NIL DO
  291. WriteString(StdOut, fname);
  292. Write(StdOut, ":");
  293. WriteNum(e^.line);
  294. Write(StdOut, ":");
  295. WriteNum(e^.col);
  296. WriteString(StdOut, ": ");
  297. ErrText(e^.nr, StdOut);
  298. WriteLn(StdOut);
  299. e := e^.next
  300. END
  301. END PrintConsoleErrors;
  302. PROCEDURE ResetErrors;
  303. (* Fresh error list per source file in a session. *)
  304. BEGIN
  305. firstErr := NIL
  306. END ResetErrors;
  307. BEGIN
  308. firstErr := NIL; Extra := 1;
  309. END ListHandler;
  310. (* --------------------------- main module ------------------------------- *)
  311. PROCEDURE ChangeExtension (oldName, Ext: ARRAY OF CHAR;
  312. VAR newName: ARRAY OF CHAR);
  313. (* Constructs newName as complete file name by appending ext to oldName
  314. Examples: (assume ext = "EXT")
  315. old.any ==> old.EXT
  316. old ==> old.EXT
  317. This is not a file renaming facility, merely a string manipulation
  318. routine. *)
  319. VAR
  320. i, l: CARDINAL;
  321. BEGIN
  322. Strings.Assign(oldName, newName);
  323. i := LENGTH(oldName); l := i;
  324. WHILE (i > 0) AND (oldName[i -1] # '.')
  325. AND (oldName[i -1] # '\') AND (oldName[i -1] # '/') DO
  326. DEC(i)
  327. END;
  328. IF (i > 0) AND (oldName[i-1] = '.') THEN
  329. Strings.Delete(newName, i - 1, l + 1 - i)
  330. END;
  331. IF Ext[0] = '.' THEN Strings.Delete(Ext, 0, 1) END;
  332. Strings.Append(".", newName);
  333. Strings.Append(Ext, newName)
  334. END ChangeExtension;
  335. PROCEDURE BaseName (path: ARRAY OF CHAR; VAR name: ARRAY OF CHAR);
  336. (* Basename of a path without its extension (program image name). *)
  337. VAR
  338. i, j, k: CARDINAL;
  339. BEGIN
  340. i := 0; j := 0;
  341. WHILE (i <= HIGH(path)) AND (path[i] # CHR(0)) DO
  342. IF (path[i] = '/') OR (path[i] = '\') THEN j := i + 1 END;
  343. INC(i)
  344. END;
  345. k := j;
  346. WHILE (k < i) AND (path[k] # '.') DO INC(k) END;
  347. i := 0;
  348. WHILE j < k DO name[i] := path[j]; INC(i); INC(j) END;
  349. name[i] := CHR(0)
  350. END BaseName;
  351. VAR
  352. sourceName, listName, progName: ARRAY [0 .. 255] OF CHAR;
  353. files: ARRAY [0 .. 31] OF ARRAY [0 .. 255] OF CHAR;
  354. nFiles: CARDINAL;
  355. f: CARDINAL;
  356. bad: BOOLEAN;
  357. BEGIN
  358. (* collect the session's source files (program module last) *)
  359. nFiles := 0;
  360. LOOP
  361. FileIO.NextParameter(sourceName);
  362. IF (sourceName[0] = CHR(0)) OR (nFiles > HIGH(files)) THEN EXIT END;
  363. Strings.Assign(sourceName, files[nFiles]);
  364. INC(nFiles)
  365. END;
  366. IF nFiles = 0 THEN
  367. FileIO.WriteString(FileIO.StdOut, "No input file specified");
  368. HALT
  369. END;
  370. BaseName(files[nFiles - 1], progName);
  371. SymTab.Init;
  372. QbeGen.OpenModule(progName);
  373. Error := StoreError;
  374. (* parse each unit into the shared session *)
  375. bad := FALSE; f := 0;
  376. WHILE f < nFiles DO
  377. Strings.Assign(files[f], sourceName);
  378. FileIO.Open(src, sourceName, FALSE);
  379. IF NOT FileIO.Okay THEN
  380. FileIO.WriteString(FileIO.StdOut, "Could not open input file");
  381. FileIO.WriteLn(FileIO.StdOut);
  382. HALT
  383. END;
  384. ChangeExtension(sourceName, ".LST", listName);
  385. FileIO.Open(lst, listName, TRUE);
  386. IF NOT FileIO.Okay THEN lst := FileIO.StdOut END;
  387. ResetErrors;
  388. FileIO.WriteString(FileIO.StdOut, "Parsing");
  389. FileIO.WriteLn(FileIO.StdOut);
  390. Parse;
  391. PrintListing;
  392. IF lst # FileIO.StdOut THEN FileIO.Close(lst) END;
  393. IF NOT Successful() THEN
  394. PrintConsoleErrors(sourceName);
  395. bad := TRUE
  396. END;
  397. (* Release the source handle before the next file: the host
  398. FileIO caps open files at BitSetSize (16), and a session may
  399. compile more sources than that. *)
  400. IF (src # FileIO.StdIn) & (src # FileIO.StdOut) THEN
  401. FileIO.Close(src)
  402. END;
  403. INC(f)
  404. END;
  405. IF bad OR NOT SymTab.HaveProgram()
  406. THEN FileIO.WriteString(FileIO.StdOut, "Incorrect source")
  407. ELSE FileIO.WriteString(FileIO.StdOut, "Parsed correctly")
  408. END
  409. END M2.