compiler.frm 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. MODULE -->Grammar;
  2. (* Driver for the M2comp Modula-2 compiler (Coco/R frontend).
  3. Generated <Grammar>S (scanner) + <Grammar>P (parser) do lexing,
  4. parsing, SymTab checking and AST building; on success the MGen
  5. tree-walking backend emits a single-module MC64 .MC4 image. *)
  6. FROM -->Scanner IMPORT lst, src, errors, Error, CharAt;
  7. FROM -->Parser IMPORT Parse, Successful;
  8. IMPORT
  9. Strings, Storage, SYSTEM, FileIO, AST, MGen, SymTab;
  10. TYPE
  11. INT32 = FileIO.INT32 (* 32 bit integers needed *);
  12. MODULE ListHandler;
  13. (* ------------------- Source Listing and Error handler -------------- *)
  14. FROM FileIO IMPORT CR, LF, EOF, WriteString, Write, WriteLn, WriteInt, Long0;
  15. FROM Storage IMPORT ALLOCATE;
  16. FROM SYSTEM IMPORT TSIZE;
  17. IMPORT lst, CharAt, errors, INT32;
  18. EXPORT StoreError, PrintListing, ResetErrors;
  19. TYPE
  20. Err = POINTER TO ErrDesc;
  21. ErrDesc = RECORD
  22. nr, line, col: INTEGER;
  23. next: Err
  24. END;
  25. CONST
  26. tab = 11C;
  27. VAR
  28. firstErr, lastErr: Err;
  29. Extra: INTEGER;
  30. PROCEDURE StoreError (nr, line, col: INTEGER; pos: INT32);
  31. (* Store an error message for later printing *)
  32. VAR
  33. nextErr: Err;
  34. BEGIN
  35. ALLOCATE(nextErr, TSIZE(ErrDesc));
  36. nextErr^.nr := nr; nextErr^.line := line; nextErr^.col := col;
  37. nextErr^.next := NIL;
  38. IF firstErr = NIL
  39. THEN firstErr := nextErr
  40. ELSE lastErr^.next := nextErr
  41. END;
  42. lastErr := nextErr;
  43. INC(errors)
  44. END StoreError;
  45. PROCEDURE GetLine (VAR pos: INT32;
  46. VAR line: ARRAY OF CHAR;
  47. VAR eof: BOOLEAN);
  48. (* Read a source line. Return empty line if eof *)
  49. VAR
  50. ch: CHAR;
  51. i: CARDINAL;
  52. BEGIN
  53. i := 0; eof := FALSE; ch := CharAt(pos); INC(pos);
  54. WHILE (ch # CR) & (ch # LF) & (ch # EOF) DO
  55. line[i] := ch; INC(i); ch := CharAt(pos); INC(pos);
  56. END;
  57. eof := (i = 0) & (ch = EOF); line[i] := 0C;
  58. IF ch = CR THEN (* check for MsDos *)
  59. ch := CharAt(pos);
  60. IF ch = LF THEN INC(pos); Extra := 0 END
  61. END
  62. END GetLine;
  63. PROCEDURE PrintErr (line: ARRAY OF CHAR; nr, col: INTEGER);
  64. (* Print an error message *)
  65. PROCEDURE Msg (s: ARRAY OF CHAR);
  66. BEGIN
  67. WriteString(lst, s)
  68. END Msg;
  69. PROCEDURE Pointer;
  70. VAR
  71. i: INTEGER;
  72. BEGIN
  73. WriteString(lst, "***** ");
  74. i := 0;
  75. WHILE i < col + Extra - 2 DO
  76. IF line[i] = tab
  77. THEN Write(lst, tab)
  78. ELSE Write(lst, ' ')
  79. END;
  80. INC(i)
  81. END;
  82. WriteString(lst, "^ ")
  83. END Pointer;
  84. BEGIN
  85. Pointer;
  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/procedure 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 in this phase")
  108. | 231: Msg("procedure forward mismatch or missing body")
  109. | 232: Msg("bad RETURN")
  110. | 233: Msg("invalid procedure call")
  111. ELSE Msg("Error: "); WriteInt(lst, nr, 0);
  112. END;
  113. WriteLn(lst)
  114. END PrintErr;
  115. PROCEDURE ResetErrors;
  116. (* Drops stored errors so the next input file starts clean. *)
  117. BEGIN
  118. firstErr := NIL; lastErr := NIL
  119. END ResetErrors;
  120. PROCEDURE PrintListing;
  121. (* Print a source listing with error messages *)
  122. VAR
  123. nextErr: Err;
  124. eof: BOOLEAN;
  125. lnr, errC: INTEGER;
  126. srcPos: INT32;
  127. line: ARRAY [0 .. 255] OF CHAR;
  128. BEGIN
  129. WriteString(lst, "Listing:");
  130. WriteLn(lst); WriteLn(lst);
  131. srcPos := 0; nextErr := firstErr;
  132. GetLine(srcPos, line, eof); lnr := 1; errC := 0;
  133. WHILE ~ eof DO
  134. WriteInt(lst, lnr, 5); WriteString(lst, " ");
  135. WriteString(lst, line); WriteLn(lst);
  136. WHILE (nextErr # NIL) & (nextErr^.line = lnr) DO
  137. PrintErr(line, nextErr^.nr, nextErr^.col); INC(errC);
  138. nextErr := nextErr^.next
  139. END;
  140. GetLine(srcPos, line, eof); INC(lnr);
  141. END;
  142. IF nextErr # NIL THEN
  143. WriteInt(lst, lnr, 5); WriteLn(lst);
  144. WHILE nextErr # NIL DO
  145. PrintErr(line, nextErr^.nr, nextErr^.col); INC(errC);
  146. nextErr := nextErr^.next
  147. END
  148. END;
  149. WriteLn(lst);
  150. WriteInt(lst, errC, 5); WriteString(lst, " error");
  151. IF errC # 1 THEN Write(lst, 's') END;
  152. WriteLn(lst); WriteLn(lst); WriteLn(lst);
  153. END PrintListing;
  154. BEGIN
  155. firstErr := NIL; Extra := 1;
  156. END ListHandler;
  157. (* --------------------------- main module ------------------------------- *)
  158. PROCEDURE ChangeExtension (oldName, Ext: ARRAY OF CHAR;
  159. VAR newName: ARRAY OF CHAR);
  160. (* Constructs newName by replacing the extension of oldName with Ext. *)
  161. VAR
  162. i, l: CARDINAL;
  163. BEGIN
  164. Strings.Assign(oldName, newName);
  165. i := LENGTH(oldName); l := i;
  166. WHILE (i > 0) & (oldName[i -1] # '.')
  167. & (oldName[i -1] # '\') & (oldName[i -1] # '/') DO
  168. DEC(i)
  169. END;
  170. IF (i > 0) & (oldName[i-1] = '.') THEN
  171. Strings.Delete(newName, i - 1, l + 1 - i)
  172. END;
  173. IF Ext[0] = '.' THEN Strings.Delete(Ext, 0, 1) END;
  174. Strings.Append(".", newName);
  175. Strings.Append(Ext, newName)
  176. END ChangeExtension;
  177. VAR
  178. sourceName, listName: ARRAY [0 .. 255] OF CHAR;
  179. failed: BOOLEAN;
  180. emitOk: BOOLEAN;
  181. progSeen: BOOLEAN;
  182. uk: INTEGER;
  183. nDefs, nImpls: CARDINAL;
  184. i, j: CARDINAL;
  185. found: BOOLEAN;
  186. progRoot, sessionRoot, chain: AST.Node;
  187. progName: SymTab.Name;
  188. unitRoots: ARRAY [0 .. 15] OF AST.Node;
  189. unitIsDef: ARRAY [0 .. 15] OF BOOLEAN;
  190. PROCEDURE Fail (msg: ARRAY OF CHAR);
  191. (* Fail-fast session abort with a one-line outcome. *)
  192. BEGIN
  193. FileIO.WriteString(FileIO.StdOut, msg);
  194. FileIO.WriteLn(FileIO.StdOut);
  195. failed := TRUE
  196. END Fail;
  197. BEGIN
  198. (* check on correct parameter usage *)
  199. FileIO.NextParameter(sourceName);
  200. IF sourceName[0] = 0C THEN
  201. FileIO.WriteString(FileIO.StdOut, "No input file specified");
  202. HALT
  203. END;
  204. (* step 5: one symbol table per session (not per file), so
  205. definitions, implementations and the program share it *)
  206. (* install error reporting procedure - Scanner.Error *)
  207. Error := StoreError;
  208. SymTab.Init();
  209. failed := FALSE;
  210. progSeen := FALSE;
  211. nDefs := 0;
  212. nImpls := 0;
  213. LOOP
  214. IF sourceName[0] = 0C THEN EXIT END;
  215. (* open the source file - Scanner.src *)
  216. FileIO.Open(src, sourceName, FALSE);
  217. IF ~ FileIO.Okay THEN
  218. FileIO.WriteString(FileIO.StdOut, "Could not open input file");
  219. FileIO.WriteLn(FileIO.StdOut);
  220. HALT
  221. END;
  222. (* open the output file for the source listing - Scanner.lst *)
  223. ChangeExtension(sourceName, ".LST", listName);
  224. FileIO.Open(lst, listName, TRUE);
  225. IF ~ FileIO.Okay THEN
  226. FileIO.WriteString(FileIO.StdOut, "Could not open listing file");
  227. FileIO.WriteLn(FileIO.StdOut);
  228. (* default Scanner.lst to screen *) lst := FileIO.StdOut;
  229. END;
  230. (* fresh error list per input file (M2compS.Reset already
  231. zeroes the error count inside Parse) *)
  232. ResetErrors;
  233. (* instigate the compilation - Parser.Parse *)
  234. FileIO.WriteString(FileIO.StdOut, "Parsing ");
  235. FileIO.WriteString(FileIO.StdOut, sourceName);
  236. FileIO.WriteLn(FileIO.StdOut);
  237. Parse;
  238. (* generate the source listing on lst file *)
  239. PrintListing;
  240. IF lst # FileIO.StdOut THEN FileIO.Close(lst) END;
  241. (* fail fast: later files build on this one's tables *)
  242. IF NOT Successful()
  243. THEN
  244. FileIO.WriteString(FileIO.StdOut, "Incorrect source");
  245. FileIO.WriteLn(FileIO.StdOut);
  246. failed := TRUE;
  247. EXIT
  248. END;
  249. (* session order: definitions and implementations in
  250. dependency order (the grammar enforces per-library order:
  251. a definition precedes its implementation, an IMPORT needs
  252. a completed implementation), then exactly one program;
  253. remember each unit root for the single backend pass *)
  254. uk := SymTab.UnitKind();
  255. IF progSeen THEN
  256. Fail("Incorrect source"); EXIT
  257. ELSIF uk = SymTab.UnitDef THEN
  258. IF nDefs + nImpls > 15 THEN
  259. Fail("Incorrect source"); EXIT
  260. END;
  261. unitRoots[nDefs + nImpls] := AST.GetRoot();
  262. unitIsDef[nDefs + nImpls] := TRUE;
  263. INC(nDefs)
  264. ELSIF uk = SymTab.UnitImpl THEN
  265. IF nDefs + nImpls > 15 THEN
  266. Fail("Incorrect source"); EXIT
  267. END;
  268. unitRoots[nDefs + nImpls] := AST.GetRoot();
  269. unitIsDef[nDefs + nImpls] := FALSE;
  270. INC(nImpls)
  271. ELSE
  272. progSeen := TRUE;
  273. progRoot := AST.GetRoot();
  274. Strings.Assign(progRoot^.name, progName)
  275. END;
  276. FileIO.NextParameter(sourceName);
  277. END;
  278. (* a session without a program emits nothing *)
  279. IF NOT failed & ~progSeen THEN
  280. Fail("Incorrect source")
  281. END;
  282. (* merge each implementation into its definition unit, then emit
  283. the program together with the library units in one image *)
  284. IF NOT failed THEN
  285. i := 0;
  286. WHILE (i < nDefs + nImpls) & ~failed DO
  287. IF ~unitIsDef[i] THEN
  288. found := FALSE;
  289. j := 0;
  290. WHILE (j < nDefs + nImpls) & ~found DO
  291. IF unitIsDef[j] & Strings.Equal(unitRoots[j]^.name,
  292. unitRoots[i]^.name) THEN
  293. found := TRUE
  294. ELSE
  295. INC(j)
  296. END
  297. END;
  298. IF ~found THEN Fail("Incorrect source")
  299. ELSE
  300. unitRoots[j]^.left := AST.Append(unitRoots[j]^.left,
  301. unitRoots[i]^.left);
  302. unitRoots[j]^.right := unitRoots[i]^.right
  303. END
  304. END;
  305. INC(i)
  306. END
  307. END;
  308. IF NOT failed THEN
  309. chain := NIL;
  310. i := 0;
  311. WHILE i < nDefs + nImpls DO
  312. IF unitIsDef[i] THEN
  313. chain := AST.Append(chain, unitRoots[i])
  314. END;
  315. INC(i)
  316. END;
  317. IF chain = NIL THEN sessionRoot := progRoot
  318. ELSE
  319. sessionRoot := AST.MkModule(progName,
  320. AST.Append(chain, progRoot^.left), progRoot^.right)
  321. END;
  322. (* backend: emit the MC64 image (errors reach StdOut only;
  323. listings are already written) *)
  324. emitOk := MGen.EmitModule(sessionRoot);
  325. IF emitOk THEN MGen.DumpImage END;
  326. IF NOT emitOk THEN Fail("Incorrect source") END
  327. END;
  328. (* examine the outcome *)
  329. IF NOT failed THEN
  330. FileIO.WriteString(FileIO.StdOut, "Parsed correctly");
  331. END;
  332. END -->Grammar.