M2comp.mod 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. MODULE M2comp;
  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 M2compS IMPORT lst, src, errors, Error, CharAt;
  7. FROM M2compP IMPORT Parse, Successful;
  8. IMPORT
  9. Strings, Storage, SYSTEM, FileIO, AST, MGen;
  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;
  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. 0: Msg("EOF expected")
  88. | 1: Msg("ident expected")
  89. | 2: Msg("integer expected")
  90. | 3: Msg("real expected")
  91. | 4: Msg("string expected")
  92. | 5: Msg("'.' expected")
  93. | 6: Msg("'MODULE' expected")
  94. | 7: Msg("';' expected")
  95. | 8: Msg("'FROM' expected")
  96. | 9: Msg("'IMPORT' expected")
  97. | 10: Msg("',' expected")
  98. | 11: Msg("'BEGIN' expected")
  99. | 12: Msg("'END' expected")
  100. | 13: Msg("'CONST' expected")
  101. | 14: Msg("'TYPE' expected")
  102. | 15: Msg("'VAR' expected")
  103. | 16: Msg("'=' expected")
  104. | 17: Msg("':' expected")
  105. | 18: Msg("'PROCEDURE' expected")
  106. | 19: Msg("'(' expected")
  107. | 20: Msg("')' expected")
  108. | 21: Msg("'[' expected")
  109. | 22: Msg("']' expected")
  110. | 23: Msg("'EXPORT' expected")
  111. | 24: Msg("'QUALIFIED' expected")
  112. | 25: Msg("'ARRAY' expected")
  113. | 26: Msg("'OF' expected")
  114. | 27: Msg("'RECORD' expected")
  115. | 28: Msg("'SET' expected")
  116. | 29: Msg("'POINTER' expected")
  117. | 30: Msg("'TO' expected")
  118. | 31: Msg("'..' expected")
  119. | 32: Msg("'EXIT' expected")
  120. | 33: Msg("':=' expected")
  121. | 34: Msg("'^' expected")
  122. | 35: Msg("'IF' expected")
  123. | 36: Msg("'THEN' expected")
  124. | 37: Msg("'ELSIF' expected")
  125. | 38: Msg("'ELSE' expected")
  126. | 39: Msg("'WHILE' expected")
  127. | 40: Msg("'DO' expected")
  128. | 41: Msg("'REPEAT' expected")
  129. | 42: Msg("'UNTIL' expected")
  130. | 43: Msg("'LOOP' expected")
  131. | 44: Msg("'FOR' expected")
  132. | 45: Msg("'BY' expected")
  133. | 46: Msg("'RETURN' expected")
  134. | 47: Msg("'#' expected")
  135. | 48: Msg("'<>' expected")
  136. | 49: Msg("'<' expected")
  137. | 50: Msg("'<=' expected")
  138. | 51: Msg("'>' expected")
  139. | 52: Msg("'>=' expected")
  140. | 53: Msg("'IN' expected")
  141. | 54: Msg("'+' expected")
  142. | 55: Msg("'-' expected")
  143. | 56: Msg("'OR' expected")
  144. | 57: Msg("'*' expected")
  145. | 58: Msg("'/' expected")
  146. | 59: Msg("'DIV' expected")
  147. | 60: Msg("'MOD' expected")
  148. | 61: Msg("'AND' expected")
  149. | 62: Msg("'NOT' expected")
  150. | 63: Msg("not expected")
  151. | 64: Msg("invalid MulOp")
  152. | 65: Msg("invalid AddOp")
  153. | 66: Msg("invalid Fact")
  154. | 67: Msg("invalid Relation")
  155. | 68: Msg("invalid SimExpr")
  156. | 69: Msg("invalid AssignOrCall")
  157. | 70: Msg("invalid IndexType")
  158. | 71: Msg("invalid BoundedBase")
  159. | 72: Msg("invalid Type")
  160. | 73: Msg("invalid Declaration")
  161. (* add customized cases here *)
  162. | 200: Msg("duplicate identifier")
  163. | 201: Msg("undeclared identifier")
  164. | 202: Msg("module/procedure name mismatch")
  165. | 210: Msg("incompatible assignment")
  166. | 211: Msg("arithmetic operand must be numeric")
  167. | 212: Msg("boolean operand required")
  168. | 213: Msg("incompatible comparison")
  169. | 214: Msg("BOOLEAN condition required")
  170. | 215: Msg("not a RECORD type")
  171. | 216: Msg("unknown field")
  172. | 217: Msg("not an ARRAY type")
  173. | 218: Msg("array index must be integer")
  174. | 219: Msg("not a POINTER type")
  175. | 220: Msg("FOR needs integer variable and bounds")
  176. | 221: Msg("not a type name")
  177. | 222: Msg("set operand mismatch")
  178. | 223: Msg("cyclical type definition")
  179. | 224: Msg("ordinal type required")
  180. | 230: Msg("not supported in this phase")
  181. | 231: Msg("procedure forward mismatch or missing body")
  182. | 232: Msg("bad RETURN")
  183. | 233: Msg("invalid procedure call")
  184. ELSE Msg("Error: "); WriteInt(lst, nr, 0);
  185. END;
  186. WriteLn(lst)
  187. END PrintErr;
  188. PROCEDURE PrintListing;
  189. (* Print a source listing with error messages *)
  190. VAR
  191. nextErr: Err;
  192. eof: BOOLEAN;
  193. lnr, errC: INTEGER;
  194. srcPos: INT32;
  195. line: ARRAY [0 .. 255] OF CHAR;
  196. BEGIN
  197. WriteString(lst, "Listing:");
  198. WriteLn(lst); WriteLn(lst);
  199. srcPos := 0; nextErr := firstErr;
  200. GetLine(srcPos, line, eof); lnr := 1; errC := 0;
  201. WHILE ~ eof DO
  202. WriteInt(lst, lnr, 5); WriteString(lst, " ");
  203. WriteString(lst, line); WriteLn(lst);
  204. WHILE (nextErr # NIL) & (nextErr^.line = lnr) DO
  205. PrintErr(line, nextErr^.nr, nextErr^.col); INC(errC);
  206. nextErr := nextErr^.next
  207. END;
  208. GetLine(srcPos, line, eof); INC(lnr);
  209. END;
  210. IF nextErr # NIL THEN
  211. WriteInt(lst, lnr, 5); WriteLn(lst);
  212. WHILE nextErr # NIL DO
  213. PrintErr(line, nextErr^.nr, nextErr^.col); INC(errC);
  214. nextErr := nextErr^.next
  215. END
  216. END;
  217. WriteLn(lst);
  218. WriteInt(lst, errC, 5); WriteString(lst, " error");
  219. IF errC # 1 THEN Write(lst, 's') END;
  220. WriteLn(lst); WriteLn(lst); WriteLn(lst);
  221. END PrintListing;
  222. BEGIN
  223. firstErr := NIL; Extra := 1;
  224. END ListHandler;
  225. (* --------------------------- main module ------------------------------- *)
  226. PROCEDURE ChangeExtension (oldName, Ext: ARRAY OF CHAR;
  227. VAR newName: ARRAY OF CHAR);
  228. (* Constructs newName by replacing the extension of oldName with Ext. *)
  229. VAR
  230. i, l: CARDINAL;
  231. BEGIN
  232. Strings.Assign(oldName, newName);
  233. i := LENGTH(oldName); l := i;
  234. WHILE (i > 0) & (oldName[i -1] # '.')
  235. & (oldName[i -1] # '\') & (oldName[i -1] # '/') DO
  236. DEC(i)
  237. END;
  238. IF (i > 0) & (oldName[i-1] = '.') THEN
  239. Strings.Delete(newName, i - 1, l + 1 - i)
  240. END;
  241. IF Ext[0] = '.' THEN Strings.Delete(Ext, 0, 1) END;
  242. Strings.Append(".", newName);
  243. Strings.Append(Ext, newName)
  244. END ChangeExtension;
  245. VAR
  246. sourceName, listName: ARRAY [0 .. 255] OF CHAR;
  247. failed: BOOLEAN;
  248. emitOk: BOOLEAN;
  249. BEGIN
  250. (* check on correct parameter usage *)
  251. FileIO.NextParameter(sourceName);
  252. IF sourceName[0] = 0C THEN
  253. FileIO.WriteString(FileIO.StdOut, "No input file specified");
  254. HALT
  255. END;
  256. (* step 1: syntax only, no symbol table yet (see M2comp.atg) *)
  257. (* install error reporting procedure - Scanner.Error *)
  258. Error := StoreError;
  259. failed := FALSE;
  260. LOOP
  261. IF sourceName[0] = 0C THEN EXIT END;
  262. (* open the source file - Scanner.src *)
  263. FileIO.Open(src, sourceName, FALSE);
  264. IF ~ FileIO.Okay THEN
  265. FileIO.WriteString(FileIO.StdOut, "Could not open input file");
  266. FileIO.WriteLn(FileIO.StdOut);
  267. HALT
  268. END;
  269. (* open the output file for the source listing - Scanner.lst *)
  270. ChangeExtension(sourceName, ".LST", listName);
  271. FileIO.Open(lst, listName, TRUE);
  272. IF ~ FileIO.Okay THEN
  273. FileIO.WriteString(FileIO.StdOut, "Could not open listing file");
  274. FileIO.WriteLn(FileIO.StdOut);
  275. (* default Scanner.lst to screen *) lst := FileIO.StdOut;
  276. END;
  277. (* instigate the compilation - Parser.Parse *)
  278. FileIO.WriteString(FileIO.StdOut, "Parsing ");
  279. FileIO.WriteString(FileIO.StdOut, sourceName);
  280. FileIO.WriteLn(FileIO.StdOut);
  281. Parse;
  282. (* backend: emit the MC64 image while errors still reach
  283. the listing below *)
  284. IF Successful() THEN
  285. emitOk := MGen.EmitModule(AST.GetRoot())
  286. ELSE emitOk := FALSE
  287. END;
  288. (* generate the source listing on lst file *)
  289. PrintListing;
  290. IF lst # FileIO.StdOut THEN FileIO.Close(lst) END;
  291. (* fail fast: later files build on this one's tables *)
  292. IF NOT (Successful() & emitOk)
  293. THEN
  294. FileIO.WriteString(FileIO.StdOut, "Incorrect source");
  295. FileIO.WriteLn(FileIO.StdOut);
  296. failed := TRUE;
  297. EXIT
  298. END;
  299. FileIO.NextParameter(sourceName);
  300. END;
  301. (* examine the outcome *)
  302. IF NOT failed THEN
  303. FileIO.WriteString(FileIO.StdOut, "Parsed correctly");
  304. END;
  305. END M2comp.