compiler.frm 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945
  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, SetFileName;
  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. | 237: Msg("RETRY outside EXCEPT")
  115. | 238: Msg("EXPORT must precede declarations")
  116. ELSE Msg("Error: "); WriteInt(f, nr, 0);
  117. END
  118. END ErrText;
  119. PROCEDURE PrintErr (line: ARRAY OF CHAR; nr, col: INTEGER);
  120. (* Print an error message *)
  121. PROCEDURE Pointer;
  122. VAR
  123. i: INTEGER;
  124. BEGIN
  125. WriteString(lst, "***** ");
  126. i := 0;
  127. WHILE (i < col + Extra - 2)
  128. AND (i <= VAL(INTEGER, HIGH(line)))
  129. AND (line[i] # CHR(0)) DO
  130. IF line[i] = tab
  131. THEN Write(lst, tab)
  132. ELSE Write(lst, ' ')
  133. END;
  134. INC(i)
  135. END;
  136. WriteString(lst, "^ ")
  137. END Pointer;
  138. BEGIN
  139. Pointer;
  140. ErrText(nr, lst);
  141. WriteLn(lst)
  142. END PrintErr;
  143. PROCEDURE PrintListing;
  144. (* Print a source listing with error messages *)
  145. VAR
  146. nextErr: Err;
  147. eof, done: BOOLEAN;
  148. lnr, errC: INTEGER;
  149. srcPos: INT32;
  150. line: ARRAY [0 .. 4095] OF CHAR; (* long lines (e.g. big enums) *)
  151. BEGIN
  152. WriteString(lst, "Listing:");
  153. WriteLn(lst); WriteLn(lst);
  154. srcPos := 0; nextErr := firstErr;
  155. GetLine(srcPos, line, eof); lnr := 1; errC := 0;
  156. WHILE NOT eof DO
  157. WriteInt(lst, lnr, 5); WriteString(lst, " ");
  158. WriteString(lst, line); WriteLn(lst);
  159. done := FALSE;
  160. WHILE (nextErr # NIL) AND NOT done DO
  161. IF nextErr^.line = lnr THEN
  162. PrintErr(line, nextErr^.nr, nextErr^.col); INC(errC);
  163. nextErr := nextErr^.next
  164. ELSE done := TRUE
  165. END
  166. END;
  167. GetLine(srcPos, line, eof); INC(lnr);
  168. END;
  169. IF nextErr # NIL THEN
  170. WriteInt(lst, lnr, 5); WriteLn(lst);
  171. WHILE nextErr # NIL DO
  172. PrintErr(line, nextErr^.nr, nextErr^.col); INC(errC);
  173. nextErr := nextErr^.next
  174. END
  175. END;
  176. WriteLn(lst);
  177. WriteInt(lst, errC, 5); WriteString(lst, " error");
  178. IF errC # 1 THEN Write(lst, 's') END;
  179. WriteLn(lst); WriteLn(lst); WriteLn(lst);
  180. END PrintListing;
  181. PROCEDURE PrintConsoleErrors (fname: ARRAY OF CHAR);
  182. (* "file:line:col: message" errors on StdOut, for editor
  183. integration. Columns are 1-based token starts, matching the
  184. listing carets (NOT the col-1 of CRQ.FRM). *)
  185. PROCEDURE WriteNum (n: INTEGER);
  186. (* FileIO.WriteInt pads a leading blank; editors want none. *)
  187. VAR
  188. buf: ARRAY [0 .. 15] OF CHAR;
  189. i: CARDINAL;
  190. BEGIN
  191. IF n < 0 THEN Write(StdOut, "-"); n := -n END;
  192. i := 0;
  193. IF n = 0 THEN buf[0] := "0"; i := 1 END;
  194. WHILE (n > 0) AND (i <= HIGH(buf)) DO
  195. buf[i] := CHR(ORD("0") + VAL(CARDINAL, n MOD 10));
  196. n := n DIV 10; INC(i)
  197. END;
  198. WHILE i > 0 DO DEC(i); Write(StdOut, buf[i]) END
  199. END WriteNum;
  200. VAR
  201. e: Err;
  202. BEGIN
  203. e := firstErr;
  204. WHILE e # NIL DO
  205. WriteString(StdOut, fname);
  206. Write(StdOut, ":");
  207. WriteNum(e^.line);
  208. Write(StdOut, ":");
  209. WriteNum(e^.col);
  210. WriteString(StdOut, ": ");
  211. ErrText(e^.nr, StdOut);
  212. WriteLn(StdOut);
  213. e := e^.next
  214. END
  215. END PrintConsoleErrors;
  216. PROCEDURE ResetErrors;
  217. (* Fresh error list per source file in a session. *)
  218. BEGIN
  219. firstErr := NIL
  220. END ResetErrors;
  221. BEGIN
  222. firstErr := NIL; Extra := 1;
  223. END ListHandler;
  224. (* --------------------------- main module ------------------------------- *)
  225. PROCEDURE ChangeExtension (oldName, Ext: ARRAY OF CHAR;
  226. VAR newName: ARRAY OF CHAR);
  227. (* Constructs newName as complete file name by appending ext to oldName
  228. Examples: (assume ext = "EXT")
  229. old.any ==> old.EXT
  230. old ==> old.EXT
  231. This is not a file renaming facility, merely a string manipulation
  232. routine. *)
  233. VAR
  234. i, l: CARDINAL;
  235. BEGIN
  236. Strings.Assign(oldName, newName);
  237. i := LENGTH(oldName); l := i;
  238. WHILE (i > 0) AND (oldName[i -1] # '.')
  239. AND (oldName[i -1] # '\') AND (oldName[i -1] # '/') DO
  240. DEC(i)
  241. END;
  242. IF (i > 0) AND (oldName[i-1] = '.') THEN
  243. Strings.Delete(newName, i - 1, l + 1 - i)
  244. END;
  245. IF Ext[0] = '.' THEN Strings.Delete(Ext, 0, 1) END;
  246. Strings.Append(".", newName);
  247. Strings.Append(Ext, newName)
  248. END ChangeExtension;
  249. PROCEDURE BaseName (path: ARRAY OF CHAR; VAR name: ARRAY OF CHAR);
  250. (* Basename of a path without its extension (program image name). *)
  251. VAR
  252. i, j, k: CARDINAL;
  253. BEGIN
  254. i := 0; j := 0;
  255. WHILE (i <= HIGH(path)) AND (path[i] # CHR(0)) DO
  256. IF (path[i] = '/') OR (path[i] = '\') THEN j := i + 1 END;
  257. INC(i)
  258. END;
  259. k := j;
  260. WHILE (k < i) AND (path[k] # '.') DO INC(k) END;
  261. i := 0;
  262. WHILE j < k DO name[i] := path[j]; INC(i); INC(j) END;
  263. name[i] := CHR(0)
  264. END BaseName;
  265. PROCEDURE FileExists (path: ARRAY OF CHAR): BOOLEAN;
  266. (* The self-hosted FileIO has no Exists query; Open in input mode and
  267. close immediately. *)
  268. VAR f: FileIO.File; ok: BOOLEAN;
  269. BEGIN
  270. FileIO.Open(f, path, FALSE);
  271. ok := FileIO.Okay;
  272. IF ok THEN FileIO.Close(f) END;
  273. RETURN ok
  274. END FileExists;
  275. VAR
  276. sourceName, listName, progName: ARRAY [0 .. 255] OF CHAR;
  277. files: ARRAY [0 .. 127] OF ARRAY [0 .. 255] OF CHAR;
  278. bootstrapEnv: ARRAY [0 .. 31] OF CHAR;
  279. nFiles: CARDINAL;
  280. f: CARDINAL;
  281. bad: BOOLEAN;
  282. doTwoPhase: BOOLEAN; (* two-phase parse: defer procedure bodies *)
  283. separate: BOOLEAN; (* M2 -c: one object, defs are interfaces *)
  284. progIsProgram: BOOLEAN;
  285. tmpCmd: ARRAY [0 .. 255] OF CHAR;
  286. defNames: ARRAY [0 .. 127] OF ARRAY [0 .. 63] OF CHAR;
  287. nDefNames: CARDINAL;
  288. (* generics (Increment 2): module name -> source path, def and impl *)
  289. genDefNm: ARRAY [0 .. 31] OF ARRAY [0 .. 63] OF CHAR;
  290. genDefPth: ARRAY [0 .. 31] OF ARRAY [0 .. 255] OF CHAR;
  291. nGenDef: CARDINAL;
  292. genImpNm: ARRAY [0 .. 31] OF ARRAY [0 .. 63] OF CHAR;
  293. genImpPth: ARRAY [0 .. 31] OF ARRAY [0 .. 255] OF CHAR;
  294. nGenImp: CARDINAL;
  295. uname, iname, gname: GName;
  296. gpath: GPath;
  297. isDef: BOOLEAN;
  298. (* -I interface search + transitive .def resolution for M2 -c *)
  299. incDirs: ARRAY [0 .. 15] OF ARRAY [0 .. 255] OF CHAR;
  300. nIncDirs: CARDINAL;
  301. scanMods: ARRAY [0 .. 127] OF GName;
  302. nScanMods: CARDINAL;
  303. sepNames: ARRAY [0 .. 127] OF GName;
  304. sepPaths: ARRAY [0 .. 127] OF ARRAY [0 .. 255] OF CHAR;
  305. sepState: ARRAY [0 .. 127] OF INTEGER;
  306. sepOrder: ARRAY [0 .. 127] OF CARDINAL;
  307. nSep, nSepOrder: CARDINAL;
  308. scanBuf: ARRAY [0 .. 65535] OF CHAR;
  309. scanPath, scanDir: ARRAY [0 .. 255] OF CHAR;
  310. scanWantFrom, scanFromSeen, scanImporting: BOOLEAN;
  311. PROCEDURE EqNm (a, b: ARRAY OF CHAR): BOOLEAN;
  312. (* NUL comparison. Do not use HIGH on the right-hand string literal:
  313. V3 literal descriptors use the literal length, so HIGH would stop
  314. before the NUL terminator in a separately compiled compiler. *)
  315. VAR i: CARDINAL;
  316. BEGIN
  317. i := 0;
  318. WHILE i < 256 DO
  319. IF a[i] # b[i] THEN RETURN FALSE END;
  320. IF a[i] = CHR(0) THEN RETURN TRUE END;
  321. INC(i)
  322. END;
  323. RETURN FALSE
  324. END EqNm;
  325. PROCEDURE EqNmCI (a, b: ARRAY OF CHAR): BOOLEAN;
  326. (* Case-insensitive module/file-name comparison. Source file names
  327. are lower-case in many corpora while module identifiers are not. *)
  328. VAR i: CARDINAL; ca, cb: CHAR;
  329. BEGIN
  330. i := 0;
  331. WHILE i < 64 DO
  332. ca := a[i]; cb := b[i];
  333. IF (ca >= "A") AND (ca <= "Z") THEN ca := CHR(ORD(ca) + 32) END;
  334. IF (cb >= "A") AND (cb <= "Z") THEN cb := CHR(ORD(cb) + 32) END;
  335. IF ca # cb THEN RETURN FALSE END;
  336. IF ca = CHR(0) THEN RETURN TRUE END;
  337. INC(i)
  338. END;
  339. RETURN FALSE
  340. END EqNmCI;
  341. PROCEDURE IsOpt (s, opt: ARRAY OF CHAR): BOOLEAN;
  342. (* Option comparison without relying on HIGH of a string literal:
  343. V3 string-literal descriptors use the literal length, while the
  344. caller's fixed buffer is NUL-padded. *)
  345. VAR i: CARDINAL;
  346. BEGIN
  347. i := 0;
  348. WHILE i < 64 DO
  349. IF s[i] # opt[i] THEN RETURN FALSE END;
  350. IF s[i] = CHR(0) THEN RETURN TRUE END;
  351. INC(i)
  352. END;
  353. RETURN FALSE
  354. END IsOpt;
  355. PROCEDURE CopySlice (src: ARRAY OF CHAR; start, count: CARDINAL;
  356. VAR dst: ARRAY OF CHAR);
  357. VAR i: CARDINAL;
  358. BEGIN
  359. i := 0;
  360. WHILE (i < count) AND (start + i <= HIGH(src)) AND (i <= HIGH(dst)) DO
  361. dst[i] := src[start + i]; INC(i)
  362. END;
  363. IF i <= HIGH(dst) THEN dst[i] := CHR(0) END
  364. END CopySlice;
  365. PROCEDURE StrCopy (src: ARRAY OF CHAR; VAR dst: ARRAY OF CHAR);
  366. VAR i: CARDINAL;
  367. BEGIN
  368. i := 0;
  369. WHILE (i <= HIGH(src)) AND (i <= HIGH(dst)) AND (src[i] # CHR(0)) DO
  370. dst[i] := src[i]; INC(i)
  371. END;
  372. IF i <= HIGH(dst) THEN dst[i] := CHR(0) END
  373. END StrCopy;
  374. PROCEDURE StrCat (a, b: ARRAY OF CHAR; VAR dst: ARRAY OF CHAR);
  375. VAR i, j: CARDINAL;
  376. BEGIN
  377. i := 0;
  378. WHILE (i <= HIGH(a)) AND (i <= HIGH(dst)) AND (a[i] # CHR(0)) DO
  379. dst[i] := a[i]; INC(i)
  380. END;
  381. j := 0;
  382. WHILE (j <= HIGH(b)) AND (i <= HIGH(dst)) AND (b[j] # CHR(0)) DO
  383. dst[i] := b[j]; INC(i); INC(j)
  384. END;
  385. IF i <= HIGH(dst) THEN dst[i] := CHR(0) END
  386. END StrCat;
  387. PROCEDURE LowerCopy (src: ARRAY OF CHAR; VAR dst: ARRAY OF CHAR);
  388. VAR i: CARDINAL; ch: CHAR;
  389. BEGIN
  390. i := 0;
  391. WHILE (i <= HIGH(src)) AND (i <= HIGH(dst)) AND (src[i] # CHR(0)) DO
  392. ch := src[i];
  393. IF (ch >= "A") AND (ch <= "Z") THEN
  394. ch := CHR(ORD(ch) + ORD("a") - ORD("A"))
  395. END;
  396. dst[i] := ch; INC(i)
  397. END;
  398. IF i <= HIGH(dst) THEN dst[i] := CHR(0) END
  399. END LowerCopy;
  400. PROCEDURE DirOfPath (path: ARRAY OF CHAR; VAR dir: ARRAY OF CHAR);
  401. VAR i, k: CARDINAL;
  402. BEGIN
  403. dir[0] := CHR(0); k := 0; i := 0;
  404. WHILE (i <= HIGH(path)) AND (path[i] # CHR(0)) DO
  405. IF (path[i] = "/") OR (path[i] = CHR(92)) THEN k := i + 1 END;
  406. INC(i)
  407. END;
  408. i := 0;
  409. WHILE i < k DO dir[i] := path[i]; INC(i) END;
  410. dir[i] := CHR(0)
  411. END DirOfPath;
  412. PROCEDURE TryDef (dir, name, ext: ARRAY OF CHAR;
  413. VAR path: ARRAY OF CHAR): BOOLEAN;
  414. VAR n: CARDINAL; i: CARDINAL;
  415. BEGIN
  416. path[0] := CHR(0);
  417. n := 0;
  418. i := 0;
  419. WHILE (i <= HIGH(dir)) AND (dir[i] # CHR(0)) DO
  420. IF n <= HIGH(path) THEN path[n] := dir[i] END;
  421. INC(n); INC(i)
  422. END;
  423. IF (n > 0) AND (path[n - 1] # "/") THEN
  424. IF n <= HIGH(path) THEN path[n] := "/" END;
  425. INC(n)
  426. END;
  427. i := 0;
  428. WHILE (i <= HIGH(name)) AND (name[i] # CHR(0)) DO
  429. IF n <= HIGH(path) THEN path[n] := name[i] END;
  430. INC(n); INC(i)
  431. END;
  432. i := 0;
  433. WHILE (i <= HIGH(ext)) AND (ext[i] # CHR(0)) DO
  434. IF n <= HIGH(path) THEN path[n] := ext[i] END;
  435. INC(n); INC(i)
  436. END;
  437. IF n <= HIGH(path) THEN path[n] := CHR(0) END;
  438. RETURN FileExists(path)
  439. END TryDef;
  440. PROCEDURE FindDef (name, targetDir: ARRAY OF CHAR;
  441. VAR path: ARRAY OF CHAR): BOOLEAN;
  442. VAR i: CARDINAL; low: GName; d: ARRAY [0 .. 255] OF CHAR;
  443. BEGIN
  444. path[0] := CHR(0);
  445. LowerCopy(name, low);
  446. (* -I directories take precedence; the target's own directory is a
  447. fallback (important when a runtime and a host copy share a name). *)
  448. i := 0;
  449. WHILE i < nIncDirs DO
  450. StrCopy(incDirs[i], d);
  451. IF TryDef(d, name, ".def", path) THEN RETURN TRUE END;
  452. IF TryDef(d, name, ".DEF", path) THEN RETURN TRUE END;
  453. IF TryDef(d, low, ".def", path) THEN RETURN TRUE END;
  454. IF TryDef(d, low, ".DEF", path) THEN RETURN TRUE END;
  455. INC(i)
  456. END;
  457. IF TryDef(targetDir, name, ".def", path) THEN RETURN TRUE END;
  458. IF TryDef(targetDir, name, ".DEF", path) THEN RETURN TRUE END;
  459. IF TryDef(targetDir, low, ".def", path) THEN RETURN TRUE END;
  460. IF TryDef(targetDir, low, ".DEF", path) THEN RETURN TRUE END;
  461. RETURN FALSE
  462. END FindDef;
  463. PROCEDURE IsNameChar (ch: CHAR): BOOLEAN;
  464. BEGIN
  465. RETURN ((ch >= "A") AND (ch <= "Z"))
  466. OR ((ch >= "a") AND (ch <= "z"))
  467. OR ((ch >= "0") AND (ch <= "9"))
  468. OR (ch = "_")
  469. END IsNameChar;
  470. PROCEDURE AddScanMod (name: ARRAY OF CHAR);
  471. VAR i: CARDINAL;
  472. BEGIN
  473. i := 0;
  474. WHILE i < nScanMods DO
  475. IF EqNm(scanMods[i], name) THEN RETURN END;
  476. INC(i)
  477. END;
  478. IF nScanMods <= HIGH(scanMods) THEN
  479. StrCopy(name, scanMods[nScanMods]); INC(nScanMods)
  480. END
  481. END AddScanMod;
  482. PROCEDURE HandleScanTok (tok: GName);
  483. VAR wantFrom, fromSeen, importing: BOOLEAN;
  484. BEGIN
  485. (* These flags live in ScanFileImports's caller-visible globals. *)
  486. IF EqNmCI(tok, "FROM") THEN
  487. scanWantFrom := TRUE; scanFromSeen := FALSE; scanImporting := FALSE
  488. ELSIF EqNmCI(tok, "IMPORT") THEN
  489. IF scanFromSeen THEN scanImporting := FALSE
  490. ELSE scanImporting := TRUE
  491. END;
  492. scanWantFrom := FALSE
  493. ELSIF scanWantFrom THEN
  494. AddScanMod(tok);
  495. scanWantFrom := FALSE; scanFromSeen := TRUE
  496. ELSIF scanImporting THEN
  497. AddScanMod(tok)
  498. END
  499. END HandleScanTok;
  500. PROCEDURE ScanFileImports (path: ARRAY OF CHAR);
  501. VAR f: FileIO.File; nRead, i, start: CARDINAL;
  502. ch, quote: CHAR; cmt: CARDINAL; tok: GName;
  503. BEGIN
  504. nScanMods := 0;
  505. FileIO.Open(f, path, FALSE);
  506. IF NOT FileIO.Okay THEN RETURN END;
  507. nRead := HIGH(scanBuf) + 1;
  508. FileIO.ReadBytes(f, scanBuf, nRead);
  509. FileIO.Close(f);
  510. cmt := 0; quote := CHR(0);
  511. scanWantFrom := FALSE; scanFromSeen := FALSE; scanImporting := FALSE;
  512. i := 0;
  513. WHILE i < nRead DO
  514. ch := scanBuf[i];
  515. IF cmt > 0 THEN
  516. IF (ch = "(") AND (i + 1 < nRead) AND (scanBuf[i + 1] = "*") THEN
  517. INC(cmt); INC(i, 2)
  518. ELSIF (ch = "*") AND (i + 1 < nRead) AND (scanBuf[i + 1] = ")") THEN
  519. DEC(cmt); INC(i, 2)
  520. ELSE INC(i)
  521. END
  522. ELSIF quote # CHR(0) THEN
  523. IF ch = quote THEN
  524. IF (i + 1 < nRead) AND (scanBuf[i + 1] = quote) THEN INC(i, 2)
  525. ELSE quote := CHR(0); INC(i)
  526. END
  527. ELSE INC(i)
  528. END
  529. ELSIF (ch = "(") AND (i + 1 < nRead) AND (scanBuf[i + 1] = "*") THEN
  530. cmt := 1; INC(i, 2)
  531. ELSIF (ch = "/") AND (i + 1 < nRead) AND (scanBuf[i + 1] = "/") THEN
  532. WHILE (i < nRead) AND (scanBuf[i] # CHR(10)) DO INC(i) END
  533. ELSIF (ch = "'") OR (ch = '"') THEN
  534. quote := ch; INC(i)
  535. ELSIF (ch = ";") THEN
  536. scanWantFrom := FALSE; scanFromSeen := FALSE; scanImporting := FALSE;
  537. INC(i)
  538. ELSIF IsNameChar(ch) THEN
  539. start := i;
  540. WHILE (i < nRead) AND IsNameChar(scanBuf[i]) DO INC(i) END;
  541. IF i - start <= HIGH(tok) THEN
  542. CopySlice(scanBuf, start, i - start, tok);
  543. HandleScanTok(tok)
  544. END
  545. ELSE INC(i)
  546. END
  547. END
  548. END ScanFileImports;
  549. PROCEDURE FindSep (name: ARRAY OF CHAR): INTEGER;
  550. VAR i: CARDINAL;
  551. BEGIN
  552. i := 0;
  553. WHILE i < nSep DO
  554. IF EqNm(sepNames[i], name) THEN RETURN VAL(INTEGER, i) END;
  555. INC(i)
  556. END;
  557. RETURN -1
  558. END FindSep;
  559. PROCEDURE ResolveModule (name: ARRAY OF CHAR): BOOLEAN; FORWARD;
  560. PROCEDURE AddDefPath (path, name: ARRAY OF CHAR): BOOLEAN;
  561. VAR idx, j: INTEGER; pend: ARRAY [0 .. 127] OF GName; pn: CARDINAL;
  562. BEGIN
  563. idx := FindSep(name);
  564. IF idx >= 0 THEN RETURN TRUE END;
  565. IF nSep > HIGH(sepNames) THEN RETURN FALSE END;
  566. idx := VAL(INTEGER, nSep);
  567. StrCopy(name, sepNames[nSep]); StrCopy(path, sepPaths[nSep]);
  568. sepState[nSep] := 1; INC(nSep);
  569. ScanFileImports(path);
  570. pn := nScanMods;
  571. j := 0;
  572. WHILE j < VAL(INTEGER, pn) DO
  573. StrCopy(scanMods[j], pend[j]); INC(j)
  574. END;
  575. j := 0;
  576. WHILE j < VAL(INTEGER, pn) DO
  577. IF NOT ResolveModule(pend[j]) THEN END;
  578. INC(j)
  579. END;
  580. sepState[VAL(CARDINAL, idx)] := 2;
  581. IF nSepOrder <= HIGH(sepOrder) THEN
  582. sepOrder[nSepOrder] := VAL(CARDINAL, idx); INC(nSepOrder)
  583. END;
  584. RETURN TRUE
  585. END AddDefPath;
  586. PROCEDURE ResolveModule (name: ARRAY OF CHAR): BOOLEAN;
  587. VAR path: ARRAY [0 .. 255] OF CHAR; idx: INTEGER;
  588. BEGIN
  589. IF EqNm(name, "SYSTEM") OR EqNm(name, "libc") THEN RETURN TRUE END;
  590. idx := FindSep(name);
  591. IF idx >= 0 THEN RETURN TRUE END;
  592. IF NOT FindDef(name, scanDir, path) THEN RETURN FALSE END;
  593. RETURN AddDefPath(path, name)
  594. END ResolveModule;
  595. PROCEDURE ResolveFileImports (path: ARRAY OF CHAR);
  596. VAR j: INTEGER; pend: ARRAY [0 .. 127] OF GName; pn: CARDINAL;
  597. BEGIN
  598. ScanFileImports(path);
  599. pn := nScanMods;
  600. j := 0;
  601. WHILE j < VAL(INTEGER, pn) DO
  602. StrCopy(scanMods[j], pend[j]); INC(j)
  603. END;
  604. j := 0;
  605. WHILE j < VAL(INTEGER, pn) DO
  606. IF NOT ResolveModule(pend[j]) THEN END;
  607. INC(j)
  608. END
  609. END ResolveFileImports;
  610. PROCEDURE AddIncDir (s: ARRAY OF CHAR);
  611. VAR start, i: CARDINAL; d: ARRAY [0 .. 255] OF CHAR;
  612. BEGIN
  613. start := 0; i := 0;
  614. LOOP
  615. IF (i > HIGH(s)) OR (s[i] = CHR(0)) OR (s[i] = ":") THEN
  616. IF i > start THEN
  617. CopySlice(s, start, i - start, d);
  618. IF nIncDirs <= HIGH(incDirs) THEN
  619. StrCopy(d, incDirs[nIncDirs]); INC(nIncDirs)
  620. END
  621. END;
  622. IF (i > HIGH(s)) OR (s[i] = CHR(0)) THEN EXIT END;
  623. start := i + 1
  624. END;
  625. INC(i)
  626. END
  627. END AddIncDir;
  628. PROCEDURE PrepareSeparate (target: ARRAY OF CHAR);
  629. VAR j: CARDINAL; own: ARRAY [0 .. 255] OF CHAR; nm: GName;
  630. BEGIN
  631. nSep := 0; nSepOrder := 0;
  632. DirOfPath(target, scanDir);
  633. (* the target's own interface supplies exported globals/consts *)
  634. ChangeExtension(target, ".def", own);
  635. IF (NOT EqNm(target, own)) AND FileExists(own) THEN
  636. BaseName(own, nm);
  637. IF NOT AddDefPath(own, nm) THEN END
  638. END;
  639. ResolveFileImports(target);
  640. IF nSepOrder + 1 <= HIGH(files) THEN
  641. StrCopy(target, files[nSepOrder]);
  642. j := 0;
  643. WHILE j < nSepOrder DO
  644. StrCopy(sepPaths[sepOrder[j]], files[j]); INC(j)
  645. END;
  646. nFiles := nSepOrder + 1
  647. END
  648. END PrepareSeparate;
  649. PROCEDURE NoteGen (name: ARRAY OF CHAR; isD: BOOLEAN;
  650. path: ARRAY OF CHAR);
  651. VAR i: CARDINAL;
  652. BEGIN
  653. IF isD THEN
  654. IF nGenDef <= HIGH(genDefNm) THEN
  655. i := 0;
  656. WHILE (i <= HIGH(genDefNm[nGenDef])) AND (name[i] # CHR(0)) DO
  657. genDefNm[nGenDef][i] := name[i]; INC(i)
  658. END;
  659. genDefNm[nGenDef][i] := CHR(0);
  660. i := 0;
  661. WHILE (i <= HIGH(genDefPth[nGenDef])) AND (path[i] # CHR(0)) DO
  662. genDefPth[nGenDef][i] := path[i]; INC(i)
  663. END;
  664. genDefPth[nGenDef][i] := CHR(0);
  665. INC(nGenDef)
  666. END
  667. ELSE
  668. IF nGenImp <= HIGH(genImpNm) THEN
  669. i := 0;
  670. WHILE (i <= HIGH(genImpNm[nGenImp])) AND (name[i] # CHR(0)) DO
  671. genImpNm[nGenImp][i] := name[i]; INC(i)
  672. END;
  673. genImpNm[nGenImp][i] := CHR(0);
  674. i := 0;
  675. WHILE (i <= HIGH(genImpPth[nGenImp])) AND (path[i] # CHR(0)) DO
  676. genImpPth[nGenImp][i] := path[i]; INC(i)
  677. END;
  678. genImpPth[nGenImp][i] := CHR(0);
  679. INC(nGenImp)
  680. END
  681. END
  682. END NoteGen;
  683. PROCEDURE FindGenIdx (name: ARRAY OF CHAR; isD: BOOLEAN): INTEGER;
  684. (* Index of the generic module's file in the registry, or -1. *)
  685. VAR i: CARDINAL;
  686. BEGIN
  687. i := 0;
  688. IF isD THEN
  689. WHILE i < nGenDef DO
  690. IF EqNm(genDefNm[i], name) THEN RETURN VAL(INTEGER, i) END;
  691. INC(i)
  692. END
  693. ELSE
  694. WHILE i < nGenImp DO
  695. IF EqNm(genImpNm[i], name) THEN RETURN VAL(INTEGER, i) END;
  696. INC(i)
  697. END
  698. END;
  699. RETURN -1
  700. END FindGenIdx;
  701. PROCEDURE DoInst (isTarget: BOOLEAN);
  702. (* Re-parse the generic's source with the instance name and the
  703. actuals bound. In separate mode only the target unit is lowered;
  704. imported instance implementations are parsed for their symbols but
  705. left out of this object. *)
  706. VAR j: CARDINAL;
  707. gi: INTEGER;
  708. saved: FileIO.File;
  709. BEGIN
  710. RefineInst(iname); RefineGen(gname); isDef := RefineIsDef();
  711. gi := FindGenIdx(gname, isDef);
  712. IF gi < 0 THEN
  713. FileIO.WriteString(FileIO.StdOut, "generic not found: ");
  714. FileIO.WriteString(FileIO.StdOut, gname);
  715. FileIO.WriteLn(FileIO.StdOut);
  716. RETURN
  717. END;
  718. IF isDef THEN
  719. j := 0;
  720. WHILE (j <= HIGH(gpath)) AND (genDefPth[gi][j] # CHR(0)) DO
  721. gpath[j] := genDefPth[gi][j]; INC(j)
  722. END;
  723. gpath[j] := CHR(0)
  724. ELSE
  725. j := 0;
  726. WHILE (j <= HIGH(gpath)) AND (genImpPth[gi][j] # CHR(0)) DO
  727. gpath[j] := genImpPth[gi][j]; INC(j)
  728. END;
  729. gpath[j] := CHR(0)
  730. END;
  731. saved := src;
  732. FileIO.Open(src, gpath, FALSE);
  733. IF NOT FileIO.Okay THEN src := saved; RETURN END;
  734. SetFileName(gpath);
  735. ResetErrors;
  736. InstBegin(iname);
  737. j := 0;
  738. WHILE j < RefineActualCount() DO
  739. RefineActualVal(j, iname);
  740. RefineActualMod(j, gname);
  741. InstAddActual(RefineActualKind(j), RefineActualGet(j), iname, gname); INC(j)
  742. END;
  743. AST.Init;
  744. Lower.BeginUnit;
  745. SetTwoPhase(doTwoPhase);
  746. Parse;
  747. InstEnd;
  748. IF (NOT separate) OR isTarget THEN
  749. IF separate THEN Lower.SetDefUnit(isDef) END;
  750. Lower.LowerUnitNow(GetUnit(), FALSE);
  751. IF separate THEN Lower.SetDefUnit(FALSE) END
  752. END;
  753. IF (src # FileIO.StdIn) & (src # FileIO.StdOut) THEN
  754. FileIO.Close(src)
  755. END;
  756. src := saved
  757. END DoInst;
  758. BEGIN
  759. (* Bootstrap compatibility: compiler units may use the legacy 1-D
  760. open ABI while user programs use pointer-based views. *)
  761. FileIO.GetEnv("V3_LEGACY_OPEN1D", bootstrapEnv);
  762. IF bootstrapEnv[0] = "1" THEN Lower.SetLegacyOpen1D END;
  763. (* collect the session's source files (program module last) *)
  764. nFiles := 0;
  765. separate := FALSE;
  766. nDefNames := 0;
  767. doTwoPhase := TRUE; (* two-phase parse on by default (-notwophase off) *)
  768. LOOP
  769. FileIO.NextParameter(sourceName);
  770. IF (sourceName[0] = CHR(0)) OR (nFiles > HIGH(files)) THEN EXIT END;
  771. IF IsOpt(sourceName, "-notwophase") THEN
  772. doTwoPhase := FALSE
  773. ELSIF IsOpt(sourceName, "-legacyopen1d") THEN
  774. Lower.SetLegacyOpen1D
  775. ELSIF IsOpt(sourceName, "-c") THEN
  776. separate := TRUE
  777. ELSIF IsOpt(sourceName, "-I") THEN
  778. FileIO.NextParameter(tmpCmd);
  779. IF tmpCmd[0] = CHR(0) THEN
  780. FileIO.WriteString(FileIO.StdOut, "-I expects a directory");
  781. FileIO.WriteLn(FileIO.StdOut);
  782. exit(2)
  783. END;
  784. AddIncDir(tmpCmd)
  785. ELSE
  786. Strings.Assign(sourceName, files[nFiles]);
  787. INC(nFiles)
  788. END
  789. END;
  790. IF nFiles = 0 THEN
  791. FileIO.WriteString(FileIO.StdOut, "No input file specified");
  792. FileIO.WriteLn(FileIO.StdOut);
  793. exit(2)
  794. END;
  795. (* M2 -c Foo.mod: resolve Foo.def and its import interfaces through
  796. -I directories. Explicit interface lists keep the old path. *)
  797. IF separate AND (nFiles = 1) THEN
  798. PrepareSeparate(files[0])
  799. END;
  800. BaseName(files[nFiles - 1], progName);
  801. SymTab.Init;
  802. QbeGen.OpenModule(progName);
  803. IF separate THEN
  804. Lower.SetSeparate(TRUE);
  805. QbeGen.SetSeparateMode(TRUE)
  806. END;
  807. Lower.BeginSession(progName);
  808. Error := StoreError;
  809. (* parse each unit into the shared session *)
  810. bad := FALSE; f := 0;
  811. WHILE f < nFiles DO
  812. Strings.Assign(files[f], sourceName);
  813. FileIO.Open(src, sourceName, FALSE);
  814. SetFileName(sourceName);
  815. IF NOT FileIO.Okay THEN
  816. FileIO.WriteString(FileIO.StdOut, "Could not open input file: ");
  817. FileIO.WriteString(FileIO.StdOut, sourceName);
  818. FileIO.WriteLn(FileIO.StdOut);
  819. exit(2)
  820. END;
  821. ChangeExtension(sourceName, ".LST", listName);
  822. FileIO.Open(lst, listName, TRUE);
  823. IF NOT FileIO.Okay THEN lst := FileIO.StdOut END;
  824. ResetErrors;
  825. FileIO.WriteString(FileIO.StdOut, "Parsing");
  826. FileIO.WriteLn(FileIO.StdOut);
  827. AST.Init;
  828. Lower.BeginUnit;
  829. SetTwoPhase(doTwoPhase);
  830. Parse;
  831. UnitNameGet(uname);
  832. IF RefineSeen() THEN
  833. DoInst(f = nFiles - 1)
  834. ELSIF UnitIsGeneric() THEN
  835. (* record the generic's source for later instantiation; do not
  836. emit the abstract template *)
  837. NoteGen(uname, UnitIsDefModule(), sourceName)
  838. ELSIF separate AND UnitIsDefModule() AND (f < nFiles - 1) THEN
  839. (* Interfaces are type-checking only, except the target's own
  840. .def: lower that one to emit its exported globals/consts. *)
  841. IF nDefNames <= HIGH(defNames) THEN
  842. Strings.Assign(uname, defNames[nDefNames]);
  843. INC(nDefNames)
  844. END;
  845. ChangeExtension(files[nFiles - 1], ".def", tmpCmd);
  846. IF EqNm(sourceName, tmpCmd) THEN
  847. Lower.SetDefUnit(TRUE);
  848. Lower.LowerUnitNow(GetUnit(), FALSE);
  849. Lower.SetDefUnit(FALSE)
  850. ELSIF NOT SymTab.ModForeign(uname) THEN
  851. (* a FOR "C" interface has no Modula-2 init object *)
  852. QbeGen.NoteExternalInit(uname)
  853. END
  854. ELSE
  855. IF separate THEN
  856. progIsProgram := SymTab.HaveProgram();
  857. Lower.LowerUnitNow(GetUnit(), progIsProgram)
  858. ELSE
  859. Lower.LowerUnitNow(GetUnit(), f = nFiles - 1)
  860. END
  861. END;
  862. PrintListing;
  863. IF lst # FileIO.StdOut THEN FileIO.Close(lst) END;
  864. IF NOT Successful() THEN
  865. PrintConsoleErrors(sourceName);
  866. bad := TRUE
  867. END;
  868. (* Release the source handle before the next file: the host
  869. FileIO caps open files at BitSetSize (16), and a session may
  870. compile more sources than that. *)
  871. IF (src # FileIO.StdIn) & (src # FileIO.StdOut) THEN
  872. FileIO.Close(src)
  873. END;
  874. INC(f)
  875. END;
  876. IF separate THEN
  877. QbeGen.SetProgramMode(SymTab.HaveProgram())
  878. END;
  879. Lower.EndSession;
  880. IF bad OR (NOT separate AND NOT SymTab.HaveProgram()) THEN
  881. FileIO.WriteString(FileIO.StdOut, "Incorrect source");
  882. FileIO.WriteLn(FileIO.StdOut);
  883. exit(1)
  884. ELSE
  885. FileIO.WriteString(FileIO.StdOut, "Parsed correctly");
  886. FileIO.WriteLn(FileIO.StdOut);
  887. exit(0)
  888. END
  889. END -->Grammar.