compiler.frm 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  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, GetUnit,
  9. InInst, InstBegin, InstAddActual, InstEnd, InstNameGet, InstActualGet,
  10. UnitNameGet, UnitIsGeneric, UnitIsDefModule,
  11. RefineSeen, RefineIsDef, RefineInst, RefineGen,
  12. RefineActualCount, RefineActualGet, SetTwoPhase;
  13. IMPORT
  14. Strings, Storage, SYSTEM, FileIO, SymTab, QbeGen, AST, Lower, Check;
  15. (* and any others needed *)
  16. TYPE
  17. INT32 = FileIO.INT32 (* 32 bit integers needed *);
  18. GName = SymTab.Name;
  19. GPath = ARRAY [0 .. 255] OF CHAR;
  20. MODULE ListHandler;
  21. (* ------------------- Source Listing and Error handler -------------- *)
  22. FROM FileIO IMPORT CR, LF, EOF, File, StdOut,
  23. WriteString, Write, WriteLn, WriteInt, Long0;
  24. FROM Storage IMPORT ALLOCATE;
  25. FROM SYSTEM IMPORT TSIZE;
  26. IMPORT lst, CharAt, errors, INT32;
  27. EXPORT StoreError, PrintListing, PrintConsoleErrors, ResetErrors;
  28. TYPE
  29. Err = POINTER TO ErrDesc;
  30. ErrDesc = RECORD
  31. nr, line, col: INTEGER;
  32. next: Err
  33. END;
  34. CONST
  35. tab = CHR(9);
  36. VAR
  37. firstErr, lastErr: Err;
  38. Extra: INTEGER;
  39. PROCEDURE StoreError (nr, line, col: INTEGER; pos: INT32);
  40. (* Store an error message for later printing *)
  41. VAR
  42. nextErr: Err;
  43. BEGIN
  44. ALLOCATE(nextErr, TSIZE(ErrDesc));
  45. nextErr^.nr := nr; nextErr^.line := line; nextErr^.col := col;
  46. nextErr^.next := NIL;
  47. IF firstErr = NIL
  48. THEN firstErr := nextErr
  49. ELSE lastErr^.next := nextErr
  50. END;
  51. lastErr := nextErr;
  52. INC(errors)
  53. END StoreError;
  54. PROCEDURE GetLine (VAR pos: INT32;
  55. VAR line: ARRAY OF CHAR;
  56. VAR eof: BOOLEAN);
  57. (* Read a source line. Return empty line if eof *)
  58. VAR
  59. ch: CHAR;
  60. i: CARDINAL;
  61. BEGIN
  62. i := 0; eof := FALSE; ch := CharAt(pos); INC(pos);
  63. WHILE (ch # CR) AND (ch # LF) AND (ch # EOF) DO
  64. (* bound-check: a line longer than the caller's buffer must
  65. truncate, not overflow it (stack corruption / segfault) *)
  66. IF i < HIGH(line) THEN line[i] := ch; INC(i) END;
  67. ch := CharAt(pos); INC(pos);
  68. END;
  69. eof := (i = 0) AND (ch = EOF); line[i] := CHR(0);
  70. IF ch = CR THEN (* check for MsDos *)
  71. ch := CharAt(pos);
  72. IF ch = LF THEN INC(pos); Extra := 0 END
  73. END
  74. END GetLine;
  75. PROCEDURE ErrText (nr: INTEGER; f: File);
  76. (* Error message text for nr on file f (listing and console share it) *)
  77. PROCEDURE Msg (s: ARRAY OF CHAR);
  78. BEGIN
  79. WriteString(f, s)
  80. END Msg;
  81. BEGIN
  82. CASE nr OF
  83. -->Errors
  84. (* add customized cases here *)
  85. | 200: Msg("duplicate identifier")
  86. | 201: Msg("undeclared identifier")
  87. | 202: Msg("module name mismatch")
  88. | 210: Msg("incompatible assignment")
  89. | 211: Msg("arithmetic operand must be numeric")
  90. | 212: Msg("boolean operand required")
  91. | 213: Msg("incompatible comparison")
  92. | 214: Msg("BOOLEAN condition required")
  93. | 215: Msg("not a RECORD type")
  94. | 216: Msg("unknown field")
  95. | 217: Msg("not an ARRAY type")
  96. | 218: Msg("array index must be integer")
  97. | 219: Msg("not a POINTER type")
  98. | 220: Msg("FOR needs integer variable and bounds")
  99. | 221: Msg("not a type name")
  100. | 222: Msg("set operand mismatch")
  101. | 223: Msg("cyclical type definition")
  102. | 224: Msg("ordinal type required")
  103. | 230: Msg("not supported yet")
  104. | 231: Msg("opaque type outside definition")
  105. | 232: Msg("bad RETURN")
  106. | 233: Msg("invalid call")
  107. | 234: Msg("invalid UTF-8 in U-literal")
  108. | 235: Msg("implementation does not match definition")
  109. | 236: Msg("bad variant record")
  110. ELSE Msg("Error: "); WriteInt(f, nr, 0);
  111. END
  112. END ErrText;
  113. PROCEDURE PrintErr (line: ARRAY OF CHAR; nr, col: INTEGER);
  114. (* Print an error message *)
  115. PROCEDURE Pointer;
  116. VAR
  117. i: INTEGER;
  118. BEGIN
  119. WriteString(lst, "***** ");
  120. i := 0;
  121. WHILE (i < col + Extra - 2)
  122. AND (i <= VAL(INTEGER, HIGH(line)))
  123. AND (line[i] # CHR(0)) DO
  124. IF line[i] = tab
  125. THEN Write(lst, tab)
  126. ELSE Write(lst, ' ')
  127. END;
  128. INC(i)
  129. END;
  130. WriteString(lst, "^ ")
  131. END Pointer;
  132. BEGIN
  133. Pointer;
  134. ErrText(nr, lst);
  135. WriteLn(lst)
  136. END PrintErr;
  137. PROCEDURE PrintListing;
  138. (* Print a source listing with error messages *)
  139. VAR
  140. nextErr: Err;
  141. eof, done: BOOLEAN;
  142. lnr, errC: INTEGER;
  143. srcPos: INT32;
  144. line: ARRAY [0 .. 4095] OF CHAR; (* long lines (e.g. big enums) *)
  145. BEGIN
  146. WriteString(lst, "Listing:");
  147. WriteLn(lst); WriteLn(lst);
  148. srcPos := 0; nextErr := firstErr;
  149. GetLine(srcPos, line, eof); lnr := 1; errC := 0;
  150. WHILE NOT eof DO
  151. WriteInt(lst, lnr, 5); WriteString(lst, " ");
  152. WriteString(lst, line); WriteLn(lst);
  153. done := FALSE;
  154. WHILE (nextErr # NIL) AND NOT done DO
  155. IF nextErr^.line = lnr THEN
  156. PrintErr(line, nextErr^.nr, nextErr^.col); INC(errC);
  157. nextErr := nextErr^.next
  158. ELSE done := TRUE
  159. END
  160. END;
  161. GetLine(srcPos, line, eof); INC(lnr);
  162. END;
  163. IF nextErr # NIL THEN
  164. WriteInt(lst, lnr, 5); WriteLn(lst);
  165. WHILE nextErr # NIL DO
  166. PrintErr(line, nextErr^.nr, nextErr^.col); INC(errC);
  167. nextErr := nextErr^.next
  168. END
  169. END;
  170. WriteLn(lst);
  171. WriteInt(lst, errC, 5); WriteString(lst, " error");
  172. IF errC # 1 THEN Write(lst, 's') END;
  173. WriteLn(lst); WriteLn(lst); WriteLn(lst);
  174. END PrintListing;
  175. PROCEDURE PrintConsoleErrors (fname: ARRAY OF CHAR);
  176. (* "file:line:col: message" errors on StdOut, for editor
  177. integration. Columns are 1-based token starts, matching the
  178. listing carets (NOT the col-1 of CRQ.FRM). *)
  179. PROCEDURE WriteNum (n: INTEGER);
  180. (* FileIO.WriteInt pads a leading blank; editors want none. *)
  181. VAR
  182. buf: ARRAY [0 .. 15] OF CHAR;
  183. i: CARDINAL;
  184. BEGIN
  185. IF n < 0 THEN Write(StdOut, "-"); n := -n END;
  186. i := 0;
  187. IF n = 0 THEN buf[0] := "0"; i := 1 END;
  188. WHILE (n > 0) AND (i <= HIGH(buf)) DO
  189. buf[i] := CHR(ORD("0") + VAL(CARDINAL, n MOD 10));
  190. n := n DIV 10; INC(i)
  191. END;
  192. WHILE i > 0 DO DEC(i); Write(StdOut, buf[i]) END
  193. END WriteNum;
  194. VAR
  195. e: Err;
  196. BEGIN
  197. e := firstErr;
  198. WHILE e # NIL DO
  199. WriteString(StdOut, fname);
  200. Write(StdOut, ":");
  201. WriteNum(e^.line);
  202. Write(StdOut, ":");
  203. WriteNum(e^.col);
  204. WriteString(StdOut, ": ");
  205. ErrText(e^.nr, StdOut);
  206. WriteLn(StdOut);
  207. e := e^.next
  208. END
  209. END PrintConsoleErrors;
  210. PROCEDURE ResetErrors;
  211. (* Fresh error list per source file in a session. *)
  212. BEGIN
  213. firstErr := NIL
  214. END ResetErrors;
  215. BEGIN
  216. firstErr := NIL; Extra := 1;
  217. END ListHandler;
  218. (* --------------------------- main module ------------------------------- *)
  219. PROCEDURE ChangeExtension (oldName, Ext: ARRAY OF CHAR;
  220. VAR newName: ARRAY OF CHAR);
  221. (* Constructs newName as complete file name by appending ext to oldName
  222. Examples: (assume ext = "EXT")
  223. old.any ==> old.EXT
  224. old ==> old.EXT
  225. This is not a file renaming facility, merely a string manipulation
  226. routine. *)
  227. VAR
  228. i, l: CARDINAL;
  229. BEGIN
  230. Strings.Assign(oldName, newName);
  231. i := LENGTH(oldName); l := i;
  232. WHILE (i > 0) AND (oldName[i -1] # '.')
  233. AND (oldName[i -1] # '\') AND (oldName[i -1] # '/') DO
  234. DEC(i)
  235. END;
  236. IF (i > 0) AND (oldName[i-1] = '.') THEN
  237. Strings.Delete(newName, i - 1, l + 1 - i)
  238. END;
  239. IF Ext[0] = '.' THEN Strings.Delete(Ext, 0, 1) END;
  240. Strings.Append(".", newName);
  241. Strings.Append(Ext, newName)
  242. END ChangeExtension;
  243. PROCEDURE BaseName (path: ARRAY OF CHAR; VAR name: ARRAY OF CHAR);
  244. (* Basename of a path without its extension (program image name). *)
  245. VAR
  246. i, j, k: CARDINAL;
  247. BEGIN
  248. i := 0; j := 0;
  249. WHILE (i <= HIGH(path)) AND (path[i] # CHR(0)) DO
  250. IF (path[i] = '/') OR (path[i] = '\') THEN j := i + 1 END;
  251. INC(i)
  252. END;
  253. k := j;
  254. WHILE (k < i) AND (path[k] # '.') DO INC(k) END;
  255. i := 0;
  256. WHILE j < k DO name[i] := path[j]; INC(i); INC(j) END;
  257. name[i] := CHR(0)
  258. END BaseName;
  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. doCheck: BOOLEAN; (* run the resolve/check pass (Check.Resolve) *)
  266. doTwoPhase: BOOLEAN; (* probe the two-phase body skipper (ProbeBody) *)
  267. (* generics (Increment 2): module name -> source path, def and impl *)
  268. genDefNm: ARRAY [0 .. 31] OF ARRAY [0 .. 63] OF CHAR;
  269. genDefPth: ARRAY [0 .. 31] OF ARRAY [0 .. 255] OF CHAR;
  270. nGenDef: CARDINAL;
  271. genImpNm: ARRAY [0 .. 31] OF ARRAY [0 .. 63] OF CHAR;
  272. genImpPth: ARRAY [0 .. 31] OF ARRAY [0 .. 255] OF CHAR;
  273. nGenImp: CARDINAL;
  274. uname, iname, gname: GName;
  275. gpath: GPath;
  276. isDef: BOOLEAN;
  277. PROCEDURE EqNm (a, b: ARRAY OF CHAR): BOOLEAN;
  278. VAR i: CARDINAL;
  279. BEGIN
  280. i := 0;
  281. WHILE (i <= HIGH(a)) AND (i <= HIGH(b)) DO
  282. IF a[i] # b[i] THEN RETURN FALSE END;
  283. IF a[i] = CHR(0) THEN RETURN TRUE END;
  284. INC(i)
  285. END;
  286. RETURN FALSE
  287. END EqNm;
  288. PROCEDURE NoteGen (name: ARRAY OF CHAR; isD: BOOLEAN;
  289. path: ARRAY OF CHAR);
  290. VAR i: CARDINAL;
  291. BEGIN
  292. IF isD THEN
  293. IF nGenDef <= HIGH(genDefNm) THEN
  294. i := 0;
  295. WHILE (i <= HIGH(genDefNm[nGenDef])) AND (name[i] # CHR(0)) DO
  296. genDefNm[nGenDef][i] := name[i]; INC(i)
  297. END;
  298. genDefNm[nGenDef][i] := CHR(0);
  299. i := 0;
  300. WHILE (i <= HIGH(genDefPth[nGenDef])) AND (path[i] # CHR(0)) DO
  301. genDefPth[nGenDef][i] := path[i]; INC(i)
  302. END;
  303. genDefPth[nGenDef][i] := CHR(0);
  304. INC(nGenDef)
  305. END
  306. ELSE
  307. IF nGenImp <= HIGH(genImpNm) THEN
  308. i := 0;
  309. WHILE (i <= HIGH(genImpNm[nGenImp])) AND (name[i] # CHR(0)) DO
  310. genImpNm[nGenImp][i] := name[i]; INC(i)
  311. END;
  312. genImpNm[nGenImp][i] := CHR(0);
  313. i := 0;
  314. WHILE (i <= HIGH(genImpPth[nGenImp])) AND (path[i] # CHR(0)) DO
  315. genImpPth[nGenImp][i] := path[i]; INC(i)
  316. END;
  317. genImpPth[nGenImp][i] := CHR(0);
  318. INC(nGenImp)
  319. END
  320. END
  321. END NoteGen;
  322. PROCEDURE FindGenIdx (name: ARRAY OF CHAR; isD: BOOLEAN): INTEGER;
  323. (* Index of the generic module's file in the registry, or -1. *)
  324. VAR i: CARDINAL;
  325. BEGIN
  326. i := 0;
  327. IF isD THEN
  328. WHILE i < nGenDef DO
  329. IF EqNm(genDefNm[i], name) THEN RETURN VAL(INTEGER, i) END;
  330. INC(i)
  331. END
  332. ELSE
  333. WHILE i < nGenImp DO
  334. IF EqNm(genImpNm[i], name) THEN RETURN VAL(INTEGER, i) END;
  335. INC(i)
  336. END
  337. END;
  338. RETURN -1
  339. END FindGenIdx;
  340. PROCEDURE DoInst;
  341. (* Re-parse the generic's source with the instance name and the
  342. actuals bound, then lower the resulting unit. *)
  343. VAR j: CARDINAL;
  344. gi: INTEGER;
  345. saved: FileIO.File;
  346. BEGIN
  347. RefineInst(iname); RefineGen(gname); isDef := RefineIsDef();
  348. gi := FindGenIdx(gname, isDef);
  349. IF gi < 0 THEN
  350. FileIO.WriteString(FileIO.StdOut, "generic not found: ");
  351. FileIO.WriteString(FileIO.StdOut, gname);
  352. FileIO.WriteLn(FileIO.StdOut);
  353. RETURN
  354. END;
  355. IF isDef THEN
  356. j := 0;
  357. WHILE (j <= HIGH(gpath)) AND (genDefPth[gi][j] # CHR(0)) DO
  358. gpath[j] := genDefPth[gi][j]; INC(j)
  359. END;
  360. gpath[j] := CHR(0)
  361. ELSE
  362. j := 0;
  363. WHILE (j <= HIGH(gpath)) AND (genImpPth[gi][j] # CHR(0)) DO
  364. gpath[j] := genImpPth[gi][j]; INC(j)
  365. END;
  366. gpath[j] := CHR(0)
  367. END;
  368. saved := src;
  369. FileIO.Open(src, gpath, FALSE);
  370. IF NOT FileIO.Okay THEN src := saved; RETURN END;
  371. ResetErrors;
  372. InstBegin(iname);
  373. j := 0;
  374. WHILE j < RefineActualCount() DO
  375. InstAddActual(RefineActualGet(j)); INC(j)
  376. END;
  377. AST.Init;
  378. Lower.BeginUnit;
  379. Check.BeginUnit;
  380. SetTwoPhase(doTwoPhase);
  381. Parse;
  382. InstEnd;
  383. IF doCheck THEN Check.Resolve(GetUnit()) END;
  384. Lower.LowerUnitNow(GetUnit(), FALSE);
  385. IF (src # FileIO.StdIn) & (src # FileIO.StdOut) THEN
  386. FileIO.Close(src)
  387. END;
  388. src := saved
  389. END DoInst;
  390. BEGIN
  391. (* collect the session's source files (program module last) *)
  392. nFiles := 0;
  393. doCheck := TRUE;
  394. doTwoPhase := FALSE;
  395. LOOP
  396. FileIO.NextParameter(sourceName);
  397. IF (sourceName[0] = CHR(0)) OR (nFiles > HIGH(files)) THEN EXIT END;
  398. IF EqNm(sourceName, "-noresolve") THEN
  399. doCheck := FALSE
  400. ELSIF EqNm(sourceName, "-twophase") THEN
  401. doTwoPhase := TRUE
  402. ELSE
  403. Strings.Assign(sourceName, files[nFiles]);
  404. INC(nFiles)
  405. END
  406. END;
  407. IF nFiles = 0 THEN
  408. FileIO.WriteString(FileIO.StdOut, "No input file specified");
  409. HALT
  410. END;
  411. BaseName(files[nFiles - 1], progName);
  412. SymTab.Init;
  413. QbeGen.OpenModule(progName);
  414. Lower.BeginSession(progName);
  415. Check.BeginSession(progName);
  416. Error := StoreError;
  417. (* parse each unit into the shared session *)
  418. bad := FALSE; f := 0;
  419. WHILE f < nFiles DO
  420. Strings.Assign(files[f], sourceName);
  421. FileIO.Open(src, sourceName, FALSE);
  422. IF NOT FileIO.Okay THEN
  423. FileIO.WriteString(FileIO.StdOut, "Could not open input file");
  424. FileIO.WriteLn(FileIO.StdOut);
  425. HALT
  426. END;
  427. ChangeExtension(sourceName, ".LST", listName);
  428. FileIO.Open(lst, listName, TRUE);
  429. IF NOT FileIO.Okay THEN lst := FileIO.StdOut END;
  430. ResetErrors;
  431. FileIO.WriteString(FileIO.StdOut, "Parsing");
  432. FileIO.WriteLn(FileIO.StdOut);
  433. AST.Init;
  434. Lower.BeginUnit;
  435. Check.BeginUnit;
  436. SetTwoPhase(doTwoPhase);
  437. Parse;
  438. UnitNameGet(uname);
  439. IF doCheck THEN Check.Resolve(GetUnit()) END;
  440. IF RefineSeen() THEN
  441. DoInst
  442. ELSIF UnitIsGeneric() THEN
  443. (* record the generic's source for later instantiation; do not
  444. emit the abstract template *)
  445. NoteGen(uname, UnitIsDefModule(), sourceName)
  446. ELSE
  447. Lower.LowerUnitNow(GetUnit(), f = nFiles - 1)
  448. END;
  449. PrintListing;
  450. IF lst # FileIO.StdOut THEN FileIO.Close(lst) END;
  451. IF NOT Successful() THEN
  452. PrintConsoleErrors(sourceName);
  453. bad := TRUE
  454. END;
  455. (* Release the source handle before the next file: the host
  456. FileIO caps open files at BitSetSize (16), and a session may
  457. compile more sources than that. *)
  458. IF (src # FileIO.StdIn) & (src # FileIO.StdOut) THEN
  459. FileIO.Close(src)
  460. END;
  461. INC(f)
  462. END;
  463. Lower.EndSession;
  464. Check.EndSession;
  465. IF bad OR NOT SymTab.HaveProgram()
  466. THEN FileIO.WriteString(FileIO.StdOut, "Incorrect source")
  467. ELSE FileIO.WriteString(FileIO.StdOut, "Parsed correctly")
  468. END
  469. END -->Grammar.