compiler.frm 30 KB

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