compiler.frm 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. MODULE -->Grammar;
  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 -->Scanner IMPORT lst, src, errors, Error, CharAt;
  8. FROM -->Parser IMPORT Parse, Successful, SetLower;
  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. (* bound-check: a line longer than the caller's buffer must
  59. truncate, not overflow it (stack corruption / segfault) *)
  60. IF i < HIGH(line) THEN line[i] := ch; INC(i) END;
  61. ch := CharAt(pos); INC(pos);
  62. END;
  63. eof := (i = 0) AND (ch = EOF); line[i] := CHR(0);
  64. IF ch = CR THEN (* check for MsDos *)
  65. ch := CharAt(pos);
  66. IF ch = LF THEN INC(pos); Extra := 0 END
  67. END
  68. END GetLine;
  69. PROCEDURE ErrText (nr: INTEGER; f: File);
  70. (* Error message text for nr on file f (listing and console share it) *)
  71. PROCEDURE Msg (s: ARRAY OF CHAR);
  72. BEGIN
  73. WriteString(f, s)
  74. END Msg;
  75. BEGIN
  76. CASE nr OF
  77. -->Errors
  78. (* add customized cases here *)
  79. | 200: Msg("duplicate identifier")
  80. | 201: Msg("undeclared identifier")
  81. | 202: Msg("module name mismatch")
  82. | 210: Msg("incompatible assignment")
  83. | 211: Msg("arithmetic operand must be numeric")
  84. | 212: Msg("boolean operand required")
  85. | 213: Msg("incompatible comparison")
  86. | 214: Msg("BOOLEAN condition required")
  87. | 215: Msg("not a RECORD type")
  88. | 216: Msg("unknown field")
  89. | 217: Msg("not an ARRAY type")
  90. | 218: Msg("array index must be integer")
  91. | 219: Msg("not a POINTER type")
  92. | 220: Msg("FOR needs integer variable and bounds")
  93. | 221: Msg("not a type name")
  94. | 222: Msg("set operand mismatch")
  95. | 223: Msg("cyclical type definition")
  96. | 224: Msg("ordinal type required")
  97. | 230: Msg("not supported yet")
  98. | 231: Msg("opaque type outside definition")
  99. | 232: Msg("bad RETURN")
  100. | 233: Msg("invalid call")
  101. | 234: Msg("invalid UTF-8 in U-literal")
  102. | 235: Msg("implementation does not match definition")
  103. | 236: Msg("bad variant record")
  104. ELSE Msg("Error: "); WriteInt(f, nr, 0);
  105. END
  106. END ErrText;
  107. PROCEDURE PrintErr (line: ARRAY OF CHAR; nr, col: INTEGER);
  108. (* Print an error message *)
  109. PROCEDURE Pointer;
  110. VAR
  111. i: INTEGER;
  112. BEGIN
  113. WriteString(lst, "***** ");
  114. i := 0;
  115. WHILE (i < col + Extra - 2)
  116. AND (i <= VAL(INTEGER, HIGH(line)))
  117. AND (line[i] # CHR(0)) DO
  118. IF line[i] = tab
  119. THEN Write(lst, tab)
  120. ELSE Write(lst, ' ')
  121. END;
  122. INC(i)
  123. END;
  124. WriteString(lst, "^ ")
  125. END Pointer;
  126. BEGIN
  127. Pointer;
  128. ErrText(nr, lst);
  129. WriteLn(lst)
  130. END PrintErr;
  131. PROCEDURE PrintListing;
  132. (* Print a source listing with error messages *)
  133. VAR
  134. nextErr: Err;
  135. eof, done: BOOLEAN;
  136. lnr, errC: INTEGER;
  137. srcPos: INT32;
  138. line: ARRAY [0 .. 4095] OF CHAR; (* long lines (e.g. big enums) *)
  139. BEGIN
  140. WriteString(lst, "Listing:");
  141. WriteLn(lst); WriteLn(lst);
  142. srcPos := 0; nextErr := firstErr;
  143. GetLine(srcPos, line, eof); lnr := 1; errC := 0;
  144. WHILE NOT eof DO
  145. WriteInt(lst, lnr, 5); WriteString(lst, " ");
  146. WriteString(lst, line); WriteLn(lst);
  147. done := FALSE;
  148. WHILE (nextErr # NIL) AND NOT done DO
  149. IF nextErr^.line = lnr THEN
  150. PrintErr(line, nextErr^.nr, nextErr^.col); INC(errC);
  151. nextErr := nextErr^.next
  152. ELSE done := TRUE
  153. END
  154. END;
  155. GetLine(srcPos, line, eof); INC(lnr);
  156. END;
  157. IF nextErr # NIL THEN
  158. WriteInt(lst, lnr, 5); WriteLn(lst);
  159. WHILE nextErr # NIL DO
  160. PrintErr(line, nextErr^.nr, nextErr^.col); INC(errC);
  161. nextErr := nextErr^.next
  162. END
  163. END;
  164. WriteLn(lst);
  165. WriteInt(lst, errC, 5); WriteString(lst, " error");
  166. IF errC # 1 THEN Write(lst, 's') END;
  167. WriteLn(lst); WriteLn(lst); WriteLn(lst);
  168. END PrintListing;
  169. PROCEDURE PrintConsoleErrors (fname: ARRAY OF CHAR);
  170. (* "file:line:col: message" errors on StdOut, for editor
  171. integration. Columns are 1-based token starts, matching the
  172. listing carets (NOT the col-1 of CRQ.FRM). *)
  173. PROCEDURE WriteNum (n: INTEGER);
  174. (* FileIO.WriteInt pads a leading blank; editors want none. *)
  175. VAR
  176. buf: ARRAY [0 .. 15] OF CHAR;
  177. i: CARDINAL;
  178. BEGIN
  179. IF n < 0 THEN Write(StdOut, "-"); n := -n END;
  180. i := 0;
  181. IF n = 0 THEN buf[0] := "0"; i := 1 END;
  182. WHILE (n > 0) AND (i <= HIGH(buf)) DO
  183. buf[i] := CHR(ORD("0") + VAL(CARDINAL, n MOD 10));
  184. n := n DIV 10; INC(i)
  185. END;
  186. WHILE i > 0 DO DEC(i); Write(StdOut, buf[i]) END
  187. END WriteNum;
  188. VAR
  189. e: Err;
  190. BEGIN
  191. e := firstErr;
  192. WHILE e # NIL DO
  193. WriteString(StdOut, fname);
  194. Write(StdOut, ":");
  195. WriteNum(e^.line);
  196. Write(StdOut, ":");
  197. WriteNum(e^.col);
  198. WriteString(StdOut, ": ");
  199. ErrText(e^.nr, StdOut);
  200. WriteLn(StdOut);
  201. e := e^.next
  202. END
  203. END PrintConsoleErrors;
  204. PROCEDURE ResetErrors;
  205. (* Fresh error list per source file in a session. *)
  206. BEGIN
  207. firstErr := NIL
  208. END ResetErrors;
  209. BEGIN
  210. firstErr := NIL; Extra := 1;
  211. END ListHandler;
  212. (* --------------------------- main module ------------------------------- *)
  213. PROCEDURE ChangeExtension (oldName, Ext: ARRAY OF CHAR;
  214. VAR newName: ARRAY OF CHAR);
  215. (* Constructs newName as complete file name by appending ext to oldName
  216. Examples: (assume ext = "EXT")
  217. old.any ==> old.EXT
  218. old ==> old.EXT
  219. This is not a file renaming facility, merely a string manipulation
  220. routine. *)
  221. VAR
  222. i, l: CARDINAL;
  223. BEGIN
  224. Strings.Assign(oldName, newName);
  225. i := LENGTH(oldName); l := i;
  226. WHILE (i > 0) AND (oldName[i -1] # '.')
  227. AND (oldName[i -1] # '\') AND (oldName[i -1] # '/') DO
  228. DEC(i)
  229. END;
  230. IF (i > 0) AND (oldName[i-1] = '.') THEN
  231. Strings.Delete(newName, i - 1, l + 1 - i)
  232. END;
  233. IF Ext[0] = '.' THEN Strings.Delete(Ext, 0, 1) END;
  234. Strings.Append(".", newName);
  235. Strings.Append(Ext, newName)
  236. END ChangeExtension;
  237. PROCEDURE BaseName (path: ARRAY OF CHAR; VAR name: ARRAY OF CHAR);
  238. (* Basename of a path without its extension (program image name). *)
  239. VAR
  240. i, j, k: CARDINAL;
  241. BEGIN
  242. i := 0; j := 0;
  243. WHILE (i <= HIGH(path)) AND (path[i] # CHR(0)) DO
  244. IF (path[i] = '/') OR (path[i] = '\') THEN j := i + 1 END;
  245. INC(i)
  246. END;
  247. k := j;
  248. WHILE (k < i) AND (path[k] # '.') DO INC(k) END;
  249. i := 0;
  250. WHILE j < k DO name[i] := path[j]; INC(i); INC(j) END;
  251. name[i] := CHR(0)
  252. END BaseName;
  253. PROCEDURE IsLowerParam (s: ARRAY OF CHAR): BOOLEAN;
  254. (* "-lower": test-only flag that re-emits the parsed unit via Lower. *)
  255. BEGIN
  256. RETURN (s[0] = "-") AND (s[1] = "l") AND (s[2] = "o") AND (s[3] = "w")
  257. AND (s[4] = "e") AND (s[5] = "r") AND (s[6] = CHR(0))
  258. END IsLowerParam;
  259. VAR
  260. sourceName, listName, progName: ARRAY [0 .. 255] OF CHAR;
  261. files: ARRAY [0 .. 127] OF ARRAY [0 .. 255] OF CHAR;
  262. nFiles: CARDINAL;
  263. f: CARDINAL;
  264. bad: BOOLEAN;
  265. lowerAst: BOOLEAN;
  266. BEGIN
  267. (* collect the session's source files (program module last) *)
  268. nFiles := 0;
  269. lowerAst := FALSE;
  270. LOOP
  271. FileIO.NextParameter(sourceName);
  272. IF (sourceName[0] = CHR(0)) OR (nFiles > HIGH(files)) THEN EXIT END;
  273. IF IsLowerParam(sourceName) THEN
  274. lowerAst := TRUE
  275. ELSE
  276. Strings.Assign(sourceName, files[nFiles]);
  277. INC(nFiles)
  278. END
  279. END;
  280. IF nFiles = 0 THEN
  281. FileIO.WriteString(FileIO.StdOut, "No input file specified");
  282. HALT
  283. END;
  284. BaseName(files[nFiles - 1], progName);
  285. IF lowerAst THEN SetLower(TRUE) END;
  286. SymTab.Init;
  287. QbeGen.OpenModule(progName);
  288. Error := StoreError;
  289. (* parse each unit into the shared session *)
  290. bad := FALSE; f := 0;
  291. WHILE f < nFiles DO
  292. Strings.Assign(files[f], sourceName);
  293. FileIO.Open(src, sourceName, FALSE);
  294. IF NOT FileIO.Okay THEN
  295. FileIO.WriteString(FileIO.StdOut, "Could not open input file");
  296. FileIO.WriteLn(FileIO.StdOut);
  297. HALT
  298. END;
  299. ChangeExtension(sourceName, ".LST", listName);
  300. FileIO.Open(lst, listName, TRUE);
  301. IF NOT FileIO.Okay THEN lst := FileIO.StdOut END;
  302. ResetErrors;
  303. FileIO.WriteString(FileIO.StdOut, "Parsing");
  304. FileIO.WriteLn(FileIO.StdOut);
  305. Parse;
  306. PrintListing;
  307. IF lst # FileIO.StdOut THEN FileIO.Close(lst) END;
  308. IF NOT Successful() THEN
  309. PrintConsoleErrors(sourceName);
  310. bad := TRUE
  311. END;
  312. (* Release the source handle before the next file: the host
  313. FileIO caps open files at BitSetSize (16), and a session may
  314. compile more sources than that. *)
  315. IF (src # FileIO.StdIn) & (src # FileIO.StdOut) THEN
  316. FileIO.Close(src)
  317. END;
  318. INC(f)
  319. END;
  320. IF bad OR NOT SymTab.HaveProgram()
  321. THEN FileIO.WriteString(FileIO.StdOut, "Incorrect source")
  322. ELSE FileIO.WriteString(FileIO.StdOut, "Parsed correctly")
  323. END
  324. END -->Grammar.