compiler.frm 16 KB

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