PC.Mod 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. (* Paco, Copyright 2000 - 2002, Patrik Reali, ETH Zurich *)
  2. MODULE PC; (** AUTHOR "prk / be"; PURPOSE "Parallel Compiler: main module"; *)
  3. IMPORT
  4. Commands, Modules, Streams, Files, Configuration, Diagnostics, CompilerInterface,
  5. Texts, TextUtilities, Strings, UTF8Strings, DynamicStrings, XMLObjects, XML, XMLScanner, XMLParser,
  6. StringPool, PCM, PCS, PCT, PCP, PCLIR, PCBT, PCOF, PCOM, PCV, PCC;
  7. CONST
  8. Name = "PACO";
  9. Description = "Parallel Active Oberon Compiler";
  10. FileExtension = "MOD";
  11. DefaultErrorFile = "Errors.XML";
  12. ErrorTag = "Error";
  13. ErrCodeAttr = "code";
  14. (* compiler options: -> PCM *)
  15. DefCodeOpt = {PCM.ArrayCheck, PCM.AssertCheck, PCM.TypeCheck, PCM.PtrInit, PCM.FullStackInit};
  16. DefParserOpt = {};
  17. DefDest = "386";
  18. Debug = TRUE;
  19. NoBreakPC = -1;
  20. VAR
  21. ErrorFile: ARRAY 256 OF CHAR;
  22. TYPE
  23. StringBuf = ARRAY 256 OF CHAR;
  24. OptionString* = ARRAY 256 OF CHAR;
  25. VAR
  26. LastDest: ARRAY 16 OF CHAR; (* last code generator loaded *)
  27. PROCEDURE OutMsg(scanner: PCS.Scanner);
  28. VAR s: PCS.Scanner; t: PCS.Token; name: StringBuf;
  29. BEGIN
  30. s := PCS.ForkScanner(scanner);
  31. s.Get(t);
  32. IF t = PCS.module THEN
  33. s.Get(t);
  34. IF t = PCS.ident THEN
  35. StringPool.GetString(s.name, name);
  36. PCM.LogWStr(" compiling "); PCM.LogWStr(PCM.prefix); PCM.LogWStr(name);
  37. IF PCM.suffix # Modules.extension[0] THEN
  38. PCM.LogWStr(PCM.suffix)
  39. ELSIF Modules.ModuleByName(name) # NIL THEN
  40. PCM.LogWStr(" (in use)")
  41. END;
  42. PCM.LogWStr(" ...");
  43. PCM.LogFlush;
  44. END;
  45. END;
  46. END OutMsg;
  47. PROCEDURE Configure(CONST base, dest: ARRAY OF CHAR; errorIsFatal: BOOLEAN);
  48. VAR name: ARRAY 32 OF CHAR; i, j: LONGINT; p: PROCEDURE;
  49. BEGIN
  50. i := 0;
  51. WHILE (base[i] # 0X) DO name[i] := base[i]; INC(i) END;
  52. j := 0;
  53. WHILE dest[j] # 0X DO name[i] := dest[j]; INC(i); INC(j) END;
  54. name[i] := 0X;
  55. GETPROCEDURE (name, "Install", p);
  56. IF p # NIL THEN
  57. p; (*call Install*)
  58. PCV.SetBasicSizes;
  59. ELSIF errorIsFatal THEN
  60. PCM.LogWStr("Cannot install code-generator (no Install procedure)");
  61. PCM.LogWLn;
  62. PCM.error := TRUE
  63. END
  64. END Configure;
  65. PROCEDURE LoadBackEnd(CONST dest: ARRAY OF CHAR);
  66. BEGIN
  67. COPY(dest, LastDest);
  68. Configure("PCG", dest, TRUE);
  69. IF ~PCM.error THEN
  70. PCP.Assemble := NIL; (*default = no assembler*)
  71. Configure("PCA", dest, FALSE)
  72. END;
  73. END LoadBackEnd;
  74. PROCEDURE GetOptions(r: Streams.Reader; VAR opts: ARRAY OF CHAR);
  75. VAR i: LONGINT; ch: CHAR;
  76. BEGIN
  77. i := 0;
  78. WHILE opts[i] # 0X DO INC(i) END;
  79. r.SkipWhitespace;
  80. ch := r.Peek();
  81. WHILE (ch = "\") DO
  82. r.Char(ch); (* skip \ *)
  83. r.Char(ch);
  84. WHILE (ch > " ") DO
  85. opts[i] := ch; INC(i); r.Char(ch)
  86. END;
  87. opts[i] := " "; INC(i);
  88. r.SkipWhitespace;
  89. ch := r.Peek()
  90. END;
  91. opts[i] := 0X
  92. END GetOptions;
  93. (** Extract input file prefix from global options string, exported for PC.Mod *)
  94. PROCEDURE GetSourcePrefix*(CONST options : OptionString; VAR prefix : ARRAY OF CHAR);
  95. VAR ch, lastCh : CHAR; i : LONGINT;
  96. BEGIN
  97. prefix := "";
  98. i := 0; ch := 0X;
  99. LOOP
  100. lastCh := ch;
  101. ch := options[i]; INC(i);
  102. IF (ch = 0X) OR (i >= LEN(options)) THEN EXIT; END;
  103. IF (ch = "p") THEN
  104. IF (i = 0) OR (lastCh = " ") THEN (* be sure that "p" is the first character of an option *)
  105. SubString(options, i, prefix);
  106. END;
  107. END;
  108. END;
  109. END GetSourcePrefix;
  110. PROCEDURE SubString(CONST options : ARRAY OF CHAR; VAR from : LONGINT; VAR str: ARRAY OF CHAR);
  111. VAR ch: CHAR; j: LONGINT;
  112. BEGIN
  113. ASSERT(from < LEN(options));
  114. ch := options[from]; INC(from); j := 0;
  115. WHILE (ch # 0X) & (ch # " ") & (from < LEN(options)) & (j < LEN(str)-1) DO
  116. str[j] := ch; ch := options[from]; INC(j); INC(from);
  117. END;
  118. str[j] := 0X;
  119. END SubString;
  120. PROCEDURE ParseOptions(CONST options: ARRAY OF CHAR; VAR prefix, extension, dest, dump, objF: ARRAY OF CHAR; VAR cOpt, pOpt: SET);
  121. VAR i: LONGINT; ch: CHAR; ignore : OptionString;
  122. BEGIN
  123. (* default options *)
  124. cOpt := DefCodeOpt;
  125. pOpt := DefParserOpt;
  126. COPY("", prefix);
  127. COPY(Modules.extension[0], extension);
  128. COPY(DefDest, dest);
  129. COPY("", dump);
  130. (* parse options *)
  131. i := 0;
  132. REPEAT
  133. ch := options[i]; INC(i);
  134. (* fof: note that symmetric difference works as a switch: {1,2}/{2}={1}, {1,2}/{3}={1,2,3} *)
  135. IF ch = "s" THEN pOpt := pOpt / {PCM.NewSF}
  136. ELSIF ch = "e" THEN pOpt := pOpt / {PCM.ExtSF}
  137. ELSIF ch = "n" THEN pOpt := pOpt / {PCM.NoFiles}
  138. ELSIF ch = "f" THEN pOpt := pOpt / {PCM.Breakpoint}
  139. ELSIF ch = "o" THEN pOpt := pOpt / {PCM.NoOpOverloading} (* do NOT allow operator overloading *)
  140. ELSIF ch = "N" THEN cOpt := cOpt / {PCM.NilCheck}
  141. ELSIF ch = "c" THEN pOpt := pOpt / {PCM.CacheImports}
  142. ELSIF ch = "x" THEN cOpt := cOpt / {PCM.ArrayCheck}
  143. ELSIF ch = "a" THEN cOpt := cOpt / {PCM.AssertCheck}
  144. ELSIF ch = "z" THEN cOpt := cOpt / {PCM.FullStackInit}
  145. ELSIF ch = "b" THEN pOpt := pOpt / {PCM.BigEndian}
  146. ELSIF ch = "." THEN DEC(i); SubString(options, i, extension)
  147. ELSIF ch = "p" THEN SubString(options, i, ignore); (* Skip prefix for input filenames (only as global option) *)
  148. ELSIF ch = "P" THEN SubString(options, i, prefix); (* Prefix for output filenames *)
  149. ELSIF ch = "d" THEN SubString(options, i, dest)
  150. ELSIF ch = "D" THEN SubString(options, i, dump)
  151. ELSIF ch = "O" THEN cOpt := cOpt / {PCM.Optimize}
  152. ELSIF ch = "F" THEN SubString(options, i, objF)
  153. ELSIF ch = "W" THEN pOpt := pOpt / {PCM.Warnings}
  154. ELSIF ch = "S" THEN pOpt := pOpt / {PCM.SkipOldSFImport}
  155. ELSIF ch = "M" THEN pOpt := pOpt / {PCM.MultipleModules}
  156. ELSIF ch = "A" THEN cOpt := cOpt / {PCM.AlignedStack}
  157. END
  158. UNTIL ch = 0X;
  159. END ParseOptions;
  160. PROCEDURE EmitScope(scope: PCT.Scope);
  161. VAR name: StringBuf;
  162. BEGIN
  163. IF (scope.code # NIL) & (scope.code IS PCLIR.Code) THEN
  164. IF Debug THEN PCT.GetScopeName(scope, name) END;
  165. PCLIR.Emit(scope.code(PCLIR.Code));
  166. scope.code := NIL
  167. END
  168. END EmitScope;
  169. PROCEDURE Module*(scanner: PCS.Scanner; CONST source, options: ARRAY OF CHAR; breakpc: LONGINT; log: Streams.Writer;
  170. diagnostics : Diagnostics.Diagnostics; VAR error: BOOLEAN);
  171. VAR
  172. scope: PCT.ModScope; dest, objF: ARRAY 16 OF CHAR;
  173. size: LONGINT; R: PCM.Rider; new, extend, nofile, skip: BOOLEAN;
  174. version: CHAR; res: WORD;
  175. str: StringBuf;
  176. msg: ARRAY 32 OF CHAR;
  177. finished: BOOLEAN; copyscanner: PCS.Scanner; sym: SHORTINT;
  178. BEGIN {EXCLUSIVE}
  179. PCM.Init (source, log, diagnostics); (* also resets PCM.count!! *)
  180. ParseOptions(options, PCM.prefix, PCM.suffix, dest, PCM.dump, objF, PCM.codeOptions, PCM.parserOptions);
  181. IF dest # LastDest THEN LoadBackEnd(dest) END;
  182. new := PCM.NewSF IN PCM.parserOptions;
  183. extend := PCM.ExtSF IN PCM.parserOptions;
  184. nofile := PCM.NoFiles IN PCM.parserOptions;
  185. skip := PCM.SkipOldSFImport IN PCM.parserOptions;
  186. PCM.bigEndian := PCM.BigEndian IN PCM.parserOptions;
  187. PCM.breakpc := MAX(LONGINT);
  188. IF PCM.Breakpoint IN PCM.parserOptions THEN
  189. IF breakpc = NoBreakPC THEN
  190. PCM.LogWLn; PCM.LogWStr("No PC Selected");
  191. RETURN
  192. END;
  193. PCM.breakpc := breakpc
  194. END;
  195. finished := ~ (PCM.MultipleModules IN PCM.parserOptions);
  196. REPEAT
  197. OutMsg(scanner);
  198. new := PCM.NewSF IN PCM.parserOptions;
  199. extend := PCM.ExtSF IN PCM.parserOptions;
  200. nofile := PCM.NoFiles IN PCM.parserOptions;
  201. skip := PCM.SkipOldSFImport IN PCM.parserOptions;
  202. PCM.bigEndian := PCM.BigEndian IN PCM.parserOptions;
  203. PCM.breakpc := MAX(LONGINT);
  204. IF PCM.Breakpoint IN PCM.parserOptions THEN
  205. IF breakpc = NoBreakPC THEN
  206. PCM.LogWLn; PCM.LogWStr("No PC Selected");
  207. RETURN
  208. END;
  209. PCM.breakpc := breakpc
  210. END;
  211. IF PCLIR.CG.Init() THEN
  212. NEW(scope); PCT.InitScope(scope, NIL, {}, FALSE);
  213. PCP.ParseModule(scope, scanner);
  214. IF ~PCM.error & ~nofile THEN
  215. version := PCM.FileVersion;
  216. StringPool.GetString(scope.owner.name, str);
  217. PCM.Open(str, R, version);
  218. IF ~(PCM.Breakpoint IN PCM.parserOptions) THEN
  219. IF PCM.CacheImports IN PCM.parserOptions THEN
  220. PCT.Unregister(PCT.database, scope.owner.name);
  221. END;
  222. PCOM.Export(R, scope.owner, new, extend, skip, msg);
  223. PCM.LogWStr(msg)
  224. END;
  225. IF ~PCM.error THEN
  226. PCT.TraverseScopes(scope, EmitScope);
  227. IF objF # "" THEN
  228. Configure("PCOF", objF, TRUE)
  229. ELSE
  230. PCOF.Install
  231. END;
  232. IF ~PCM.error & ~(PCM.Breakpoint IN PCM.parserOptions) THEN PCBT.generate(R, scope, size) END;
  233. END
  234. END;
  235. IF ~PCM.error THEN
  236. PCM.LogWStr(" "); PCM.LogWNum(size); PCM.LogWStr(" done ");
  237. IF PCM.bigEndian THEN PCM.LogWStr("(BigEndian Mode)") END;
  238. PCM.LogWLn
  239. ELSE
  240. finished := TRUE;
  241. PCM.LogWStr(" not done"); PCM.LogWLn
  242. END;
  243. PCLIR.CG.Done(res); (* ignore res ? *)
  244. ELSE
  245. finished := TRUE;
  246. PCM.LogWLn; PCM.LogWStr(" Code generator not installed");
  247. PCM.LogWLn; PCM.error := TRUE;
  248. END;
  249. PCC.Cleanup;
  250. error := PCM.error;
  251. PCM.Reset;
  252. PCBT.context := NIL;
  253. PCM.LogFlush;
  254. copyscanner := PCS.ForkScanner(scanner);
  255. copyscanner.Get(sym);
  256. finished := finished OR (sym # PCS.module);
  257. UNTIL finished
  258. END Module;
  259. (** Compile code contained in t, beginning at position pos *)
  260. PROCEDURE CompileText*(t: Texts.Text; CONST source: ARRAY OF CHAR; pos, pc: LONGINT; CONST opt: ARRAY OF CHAR; log: Streams.Writer;
  261. diagnostics : Diagnostics.Diagnostics; VAR error: BOOLEAN);
  262. BEGIN
  263. IF t = NIL THEN
  264. log.String ("No text available"); log.Ln; log.Update;
  265. error := TRUE; RETURN
  266. END;
  267. Module(PCS.InitWithText(t, pos), source, opt, pc, log, diagnostics, error);
  268. END CompileText;
  269. PROCEDURE CompileInterface(t: Texts.Text; CONST source: ARRAY OF CHAR; pos: LONGINT; CONST pc,opt: ARRAY OF CHAR; log: Streams.Writer;
  270. diagnostics : Diagnostics.Diagnostics; VAR error: BOOLEAN);
  271. VAR pcNum: LONGINT;
  272. BEGIN
  273. Strings.StrToInt(pc, pcNum);
  274. CompileText(t,source,pos,pcNum, opt,log, diagnostics, error);
  275. END CompileInterface;
  276. (** Compile file *)
  277. PROCEDURE CompileFile*(CONST name, opt: ARRAY OF CHAR; pc: LONGINT; log: Streams.Writer;
  278. diagnostics : Diagnostics.Diagnostics; VAR error: BOOLEAN);
  279. VAR
  280. atu: Texts.Text; format: LONGINT; res: WORD;
  281. BEGIN
  282. NEW(atu);
  283. TextUtilities.LoadAuto(atu, name, format, res);
  284. IF res # 0 THEN
  285. log.String (name); log.String (" not found"); log.Ln; log.Update;
  286. error := TRUE; RETURN
  287. END;
  288. log.String (name);
  289. Module(PCS.InitWithText(atu, 0), name, opt, pc, log, diagnostics, error);
  290. END CompileFile;
  291. (** Compile ascii file *)
  292. PROCEDURE CompileAsciiFile*(CONST name, opt: ARRAY OF CHAR; pc: LONGINT; log: Streams.Writer;
  293. diagnostics : Diagnostics.Diagnostics; VAR error: BOOLEAN);
  294. VAR
  295. f: Files.File; r: Files.Reader;
  296. BEGIN
  297. f := Files.Old(name);
  298. IF f = NIL THEN
  299. log.String (name); log.String (" not found");
  300. log.Ln; log.Update;
  301. error := TRUE; RETURN
  302. END;
  303. log.String (name);
  304. Files.OpenReader(r, f, 0);
  305. Module(PCS.InitWithReader(r, f.Length(),0), name, opt, pc, log, diagnostics, error);
  306. END CompileAsciiFile;
  307. PROCEDURE Compile*(context : Commands.Context);
  308. VAR
  309. globalOpt, localOpt: OptionString;
  310. fullname, prefix, filename: ARRAY 256 OF CHAR;
  311. count: LONGINT;
  312. error: BOOLEAN;
  313. diagnostics : Diagnostics.DiagnosticsList;
  314. BEGIN
  315. PCT.InitDB(PCT.database);
  316. error := FALSE;
  317. globalOpt := ""; GetOptions(context.arg, globalOpt);
  318. GetSourcePrefix(globalOpt, prefix);
  319. count := 0;
  320. NEW(diagnostics);
  321. WHILE ~context.arg.EOLN() & ~error DO
  322. context.arg.String(filename);
  323. IF filename # "" THEN
  324. INC(count);
  325. COPY(globalOpt, localOpt);
  326. GetOptions(context.arg, localOpt);
  327. COPY(prefix, fullname); Strings.Append(fullname, filename);
  328. diagnostics.Reset;
  329. CompileFile(fullname, localOpt, MAX(LONGINT), context.out, diagnostics, error);
  330. diagnostics.ToStream(context.out, Diagnostics.All);
  331. PCM.LogFlush;
  332. IF count MOD 32 = 0 THEN PCT.InitDB(PCT.database) END;
  333. END
  334. END;
  335. PCT.InitDB(PCT.database);
  336. END Compile;
  337. PROCEDURE TrapHandler(pos, line, row: LONGINT; CONST msg: ARRAY OF CHAR);
  338. BEGIN
  339. PCM.LogWStr("could not load error messages: "); PCM.LogWLn;
  340. PCM.LogWStr(ErrorFile); PCM.LogWStr(" invalid (pos ");
  341. PCM.LogWNum(pos); PCM.LogWStr(", line ");
  342. PCM.LogWNum(line); PCM.LogWStr(", row ");
  343. PCM.LogWNum(row); PCM.LogWStr(" ");
  344. PCM.LogWStr(msg); PCM.LogWStr(")"); PCM.LogWLn;
  345. END TrapHandler;
  346. (** (Re)load error messages *)
  347. PROCEDURE InitErrMsg*; (** ~ *)
  348. VAR
  349. f: Files.File; scanner: XMLScanner.Scanner; parser: XMLParser.Parser; errors: XML.Document;
  350. e: XML.Element; enum, msgEnum: XMLObjects.Enumerator; p: ANY;
  351. code, i: LONGINT; str: XML.String;
  352. dynStr: DynamicStrings.DynamicString;
  353. r : Files.Reader;
  354. res : WORD;
  355. BEGIN
  356. Configuration.Get("Paco.ErrorMessages", ErrorFile, res);
  357. IF (res # Configuration.Ok) THEN ErrorFile := DefaultErrorFile END;
  358. f := Files.Old(ErrorFile);
  359. IF f = NIL THEN
  360. PCM.LogWStr("could not load error messages: ");
  361. PCM.LogWStr(ErrorFile); PCM.LogWStr(" not found"); PCM.LogWLn;
  362. RETURN;
  363. END;
  364. (* f # NIL *)
  365. Files.OpenReader(r, f, 0);
  366. NEW(scanner, r);
  367. NEW(parser, scanner); parser.reportError := TrapHandler;
  368. errors := parser.Parse();
  369. e := errors.GetRoot();
  370. enum := e.GetContents();
  371. WHILE enum.HasMoreElements() DO
  372. p := enum.GetNext();
  373. IF p IS XML.Element THEN
  374. e := p(XML.Element);
  375. str := e.GetName();
  376. IF str^ = ErrorTag THEN
  377. (* extract error code *)
  378. str := e.GetAttributeValue(ErrCodeAttr);
  379. Strings.StrToInt(str^, code);
  380. (* extract error message *)
  381. msgEnum := e.GetContents();
  382. NEW(dynStr);
  383. WHILE msgEnum.HasMoreElements() DO
  384. p := msgEnum.GetNext();
  385. IF p IS XML.Chars THEN
  386. str := p(XML.Chars).GetStr();
  387. dynStr.Append(str^);
  388. ELSIF p IS XML.CDataSect THEN
  389. str := p(XML.CDataSect).GetStr();
  390. dynStr.Append(str^);
  391. ELSIF p IS XML.CharReference THEN
  392. NEW(str, 5);
  393. i := 0;
  394. IF UTF8Strings.EncodeChar(p(XML.CharReference).GetCode(), str^, i) THEN
  395. dynStr.Append(str^);
  396. END;
  397. ELSE
  398. (* ignore *)
  399. END;
  400. END;
  401. str := dynStr.ToArrOfChar();
  402. PCM.SetErrorMsg(code, str^);
  403. dynStr.Init();
  404. END;
  405. END;
  406. END;
  407. END InitErrMsg;
  408. PROCEDURE Cleanup;
  409. BEGIN
  410. CompilerInterface.Unregister(Name);
  411. END Cleanup;
  412. BEGIN
  413. LastDest := "";
  414. PCM.LogWStr("Parallel Compiler / prk"); PCM.LogWLn;
  415. PCV.Install;
  416. InitErrMsg;
  417. Modules.InstallTermHandler(Cleanup);
  418. CompilerInterface.Register(Name, Description, FileExtension, CompileInterface);
  419. END PC.
  420. (*
  421. 21.11.07 fof new compiler option /M added (multiple modules within one file allowed, MODULE ident .... ident. MODULE ident ... ident. etc.)
  422. 10.08.07 sst new compiler option /p added
  423. 15.11.06 ug new compiler option /S added, FileVersion incremented
  424. 25.11.03 mb added InitErrMsg: read error messages from XML file
  425. 20.09.03 prk "/Dcode" compiler option added
  426. 24.06.03 prk Check that name after END is the same as declared after MODULE
  427. 25.02.03 prk PC split into PC (Aos pure) and PC (Oberon dependent)
  428. *)