123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472 |
- (* Paco, Copyright 2000 - 2002, Patrik Reali, ETH Zurich *)
- MODULE PC; (** AUTHOR "prk / be"; PURPOSE "Parallel Compiler: main module"; *)
- IMPORT
- Commands, Modules, Streams, Files, Configuration, Diagnostics, CompilerInterface,
- Texts, TextUtilities, Strings, UTF8Strings, DynamicStrings, XMLObjects, XML, XMLScanner, XMLParser,
- StringPool, PCM, PCS, PCT, PCP, PCLIR, PCBT, PCOF, PCOM, PCV, PCC;
- CONST
- Name = "PACO";
- Description = "Parallel Active Oberon Compiler";
- FileExtension = "MOD";
- DefaultErrorFile = "Errors.XML";
- ErrorTag = "Error";
- ErrCodeAttr = "code";
- (* compiler options: -> PCM *)
- DefCodeOpt = {PCM.ArrayCheck, PCM.AssertCheck, PCM.TypeCheck, PCM.PtrInit, PCM.FullStackInit};
- DefParserOpt = {};
- DefDest = "386";
- Debug = TRUE;
- NoBreakPC = -1;
- VAR
- ErrorFile: ARRAY 256 OF CHAR;
- TYPE
- StringBuf = ARRAY 256 OF CHAR;
- OptionString* = ARRAY 256 OF CHAR;
- VAR
- LastDest: ARRAY 16 OF CHAR; (* last code generator loaded *)
- PROCEDURE OutMsg(scanner: PCS.Scanner);
- VAR s: PCS.Scanner; t: PCS.Token; name: StringBuf;
- BEGIN
- s := PCS.ForkScanner(scanner);
- s.Get(t);
- IF t = PCS.module THEN
- s.Get(t);
- IF t = PCS.ident THEN
- StringPool.GetString(s.name, name);
- PCM.LogWStr(" compiling "); PCM.LogWStr(PCM.prefix); PCM.LogWStr(name);
- IF PCM.suffix # Modules.extension[0] THEN
- PCM.LogWStr(PCM.suffix)
- ELSIF Modules.ModuleByName(name) # NIL THEN
- PCM.LogWStr(" (in use)")
- END;
- PCM.LogWStr(" ...");
- PCM.LogFlush;
- END;
- END;
- END OutMsg;
- PROCEDURE Configure(CONST base, dest: ARRAY OF CHAR; errorIsFatal: BOOLEAN);
- VAR name: ARRAY 32 OF CHAR; i, j: LONGINT; p: PROCEDURE;
- BEGIN
- i := 0;
- WHILE (base[i] # 0X) DO name[i] := base[i]; INC(i) END;
- j := 0;
- WHILE dest[j] # 0X DO name[i] := dest[j]; INC(i); INC(j) END;
- name[i] := 0X;
- GETPROCEDURE (name, "Install", p);
- IF p # NIL THEN
- p; (*call Install*)
- PCV.SetBasicSizes;
- ELSIF errorIsFatal THEN
- PCM.LogWStr("Cannot install code-generator (no Install procedure)");
- PCM.LogWLn;
- PCM.error := TRUE
- END
- END Configure;
- PROCEDURE LoadBackEnd(CONST dest: ARRAY OF CHAR);
- BEGIN
- COPY(dest, LastDest);
- Configure("PCG", dest, TRUE);
- IF ~PCM.error THEN
- PCP.Assemble := NIL; (*default = no assembler*)
- Configure("PCA", dest, FALSE)
- END;
- END LoadBackEnd;
- PROCEDURE GetOptions(r: Streams.Reader; VAR opts: ARRAY OF CHAR);
- VAR i: LONGINT; ch: CHAR;
- BEGIN
- i := 0;
- WHILE opts[i] # 0X DO INC(i) END;
- r.SkipWhitespace;
- ch := r.Peek();
- WHILE (ch = "\") DO
- r.Char(ch); (* skip \ *)
- r.Char(ch);
- WHILE (ch > " ") DO
- opts[i] := ch; INC(i); r.Char(ch)
- END;
- opts[i] := " "; INC(i);
- r.SkipWhitespace;
- ch := r.Peek()
- END;
- opts[i] := 0X
- END GetOptions;
- (** Extract input file prefix from global options string, exported for PC.Mod *)
- PROCEDURE GetSourcePrefix*(CONST options : OptionString; VAR prefix : ARRAY OF CHAR);
- VAR ch, lastCh : CHAR; i : LONGINT;
- BEGIN
- prefix := "";
- i := 0; ch := 0X;
- LOOP
- lastCh := ch;
- ch := options[i]; INC(i);
- IF (ch = 0X) OR (i >= LEN(options)) THEN EXIT; END;
- IF (ch = "p") THEN
- IF (i = 0) OR (lastCh = " ") THEN (* be sure that "p" is the first character of an option *)
- SubString(options, i, prefix);
- END;
- END;
- END;
- END GetSourcePrefix;
- PROCEDURE SubString(CONST options : ARRAY OF CHAR; VAR from : LONGINT; VAR str: ARRAY OF CHAR);
- VAR ch: CHAR; j: LONGINT;
- BEGIN
- ASSERT(from < LEN(options));
- ch := options[from]; INC(from); j := 0;
- WHILE (ch # 0X) & (ch # " ") & (from < LEN(options)) & (j < LEN(str)-1) DO
- str[j] := ch; ch := options[from]; INC(j); INC(from);
- END;
- str[j] := 0X;
- END SubString;
- PROCEDURE ParseOptions(CONST options: ARRAY OF CHAR; VAR prefix, extension, dest, dump, objF: ARRAY OF CHAR; VAR cOpt, pOpt: SET);
- VAR i: LONGINT; ch: CHAR; ignore : OptionString;
- BEGIN
- (* default options *)
- cOpt := DefCodeOpt;
- pOpt := DefParserOpt;
- COPY("", prefix);
- COPY(Modules.extension[0], extension);
- COPY(DefDest, dest);
- COPY("", dump);
- (* parse options *)
- i := 0;
- REPEAT
- ch := options[i]; INC(i);
- (* fof: note that symmetric difference works as a switch: {1,2}/{2}={1}, {1,2}/{3}={1,2,3} *)
- IF ch = "s" THEN pOpt := pOpt / {PCM.NewSF}
- ELSIF ch = "e" THEN pOpt := pOpt / {PCM.ExtSF}
- ELSIF ch = "n" THEN pOpt := pOpt / {PCM.NoFiles}
- ELSIF ch = "f" THEN pOpt := pOpt / {PCM.Breakpoint}
- ELSIF ch = "o" THEN pOpt := pOpt / {PCM.NoOpOverloading} (* do NOT allow operator overloading *)
- ELSIF ch = "N" THEN cOpt := cOpt / {PCM.NilCheck}
- ELSIF ch = "c" THEN pOpt := pOpt / {PCM.CacheImports}
- ELSIF ch = "x" THEN cOpt := cOpt / {PCM.ArrayCheck}
- ELSIF ch = "a" THEN cOpt := cOpt / {PCM.AssertCheck}
- ELSIF ch = "z" THEN cOpt := cOpt / {PCM.FullStackInit}
- ELSIF ch = "b" THEN pOpt := pOpt / {PCM.BigEndian}
- ELSIF ch = "." THEN DEC(i); SubString(options, i, extension)
- ELSIF ch = "p" THEN SubString(options, i, ignore); (* Skip prefix for input filenames (only as global option) *)
- ELSIF ch = "P" THEN SubString(options, i, prefix); (* Prefix for output filenames *)
- ELSIF ch = "d" THEN SubString(options, i, dest)
- ELSIF ch = "D" THEN SubString(options, i, dump)
- ELSIF ch = "O" THEN cOpt := cOpt / {PCM.Optimize}
- ELSIF ch = "F" THEN SubString(options, i, objF)
- ELSIF ch = "W" THEN pOpt := pOpt / {PCM.Warnings}
- ELSIF ch = "S" THEN pOpt := pOpt / {PCM.SkipOldSFImport}
- ELSIF ch = "M" THEN pOpt := pOpt / {PCM.MultipleModules}
- ELSIF ch = "A" THEN cOpt := cOpt / {PCM.AlignedStack}
- END
- UNTIL ch = 0X;
- END ParseOptions;
- PROCEDURE EmitScope(scope: PCT.Scope);
- VAR name: StringBuf;
- BEGIN
- IF (scope.code # NIL) & (scope.code IS PCLIR.Code) THEN
- IF Debug THEN PCT.GetScopeName(scope, name) END;
- PCLIR.Emit(scope.code(PCLIR.Code));
- scope.code := NIL
- END
- END EmitScope;
- PROCEDURE Module*(scanner: PCS.Scanner; CONST source, options: ARRAY OF CHAR; breakpc: LONGINT; log: Streams.Writer;
- diagnostics : Diagnostics.Diagnostics; VAR error: BOOLEAN);
- VAR
- scope: PCT.ModScope; dest, objF: ARRAY 16 OF CHAR;
- size: LONGINT; R: PCM.Rider; new, extend, nofile, skip: BOOLEAN;
- version: CHAR; res: WORD;
- str: StringBuf;
- msg: ARRAY 32 OF CHAR;
- finished: BOOLEAN; copyscanner: PCS.Scanner; sym: SHORTINT;
- BEGIN {EXCLUSIVE}
- PCM.Init (source, log, diagnostics); (* also resets PCM.count!! *)
- ParseOptions(options, PCM.prefix, PCM.suffix, dest, PCM.dump, objF, PCM.codeOptions, PCM.parserOptions);
- IF dest # LastDest THEN LoadBackEnd(dest) END;
- new := PCM.NewSF IN PCM.parserOptions;
- extend := PCM.ExtSF IN PCM.parserOptions;
- nofile := PCM.NoFiles IN PCM.parserOptions;
- skip := PCM.SkipOldSFImport IN PCM.parserOptions;
- PCM.bigEndian := PCM.BigEndian IN PCM.parserOptions;
- PCM.breakpc := MAX(LONGINT);
- IF PCM.Breakpoint IN PCM.parserOptions THEN
- IF breakpc = NoBreakPC THEN
- PCM.LogWLn; PCM.LogWStr("No PC Selected");
- RETURN
- END;
- PCM.breakpc := breakpc
- END;
- finished := ~ (PCM.MultipleModules IN PCM.parserOptions);
- REPEAT
- OutMsg(scanner);
- new := PCM.NewSF IN PCM.parserOptions;
- extend := PCM.ExtSF IN PCM.parserOptions;
- nofile := PCM.NoFiles IN PCM.parserOptions;
- skip := PCM.SkipOldSFImport IN PCM.parserOptions;
- PCM.bigEndian := PCM.BigEndian IN PCM.parserOptions;
- PCM.breakpc := MAX(LONGINT);
- IF PCM.Breakpoint IN PCM.parserOptions THEN
- IF breakpc = NoBreakPC THEN
- PCM.LogWLn; PCM.LogWStr("No PC Selected");
- RETURN
- END;
- PCM.breakpc := breakpc
- END;
- IF PCLIR.CG.Init() THEN
- NEW(scope); PCT.InitScope(scope, NIL, {}, FALSE);
- PCP.ParseModule(scope, scanner);
- IF ~PCM.error & ~nofile THEN
- version := PCM.FileVersion;
- StringPool.GetString(scope.owner.name, str);
- PCM.Open(str, R, version);
- IF ~(PCM.Breakpoint IN PCM.parserOptions) THEN
- IF PCM.CacheImports IN PCM.parserOptions THEN
- PCT.Unregister(PCT.database, scope.owner.name);
- END;
- PCOM.Export(R, scope.owner, new, extend, skip, msg);
- PCM.LogWStr(msg)
- END;
- IF ~PCM.error THEN
- PCT.TraverseScopes(scope, EmitScope);
- IF objF # "" THEN
- Configure("PCOF", objF, TRUE)
- ELSE
- PCOF.Install
- END;
- IF ~PCM.error & ~(PCM.Breakpoint IN PCM.parserOptions) THEN PCBT.generate(R, scope, size) END;
- END
- END;
- IF ~PCM.error THEN
- PCM.LogWStr(" "); PCM.LogWNum(size); PCM.LogWStr(" done ");
- IF PCM.bigEndian THEN PCM.LogWStr("(BigEndian Mode)") END;
- PCM.LogWLn
- ELSE
- finished := TRUE;
- PCM.LogWStr(" not done"); PCM.LogWLn
- END;
- PCLIR.CG.Done(res); (* ignore res ? *)
- ELSE
- finished := TRUE;
- PCM.LogWLn; PCM.LogWStr(" Code generator not installed");
- PCM.LogWLn; PCM.error := TRUE;
- END;
- PCC.Cleanup;
- error := PCM.error;
- PCM.Reset;
- PCBT.context := NIL;
- PCM.LogFlush;
- copyscanner := PCS.ForkScanner(scanner);
- copyscanner.Get(sym);
- finished := finished OR (sym # PCS.module);
- UNTIL finished
- END Module;
- (** Compile code contained in t, beginning at position pos *)
- PROCEDURE CompileText*(t: Texts.Text; CONST source: ARRAY OF CHAR; pos, pc: LONGINT; CONST opt: ARRAY OF CHAR; log: Streams.Writer;
- diagnostics : Diagnostics.Diagnostics; VAR error: BOOLEAN);
- BEGIN
- IF t = NIL THEN
- log.String ("No text available"); log.Ln; log.Update;
- error := TRUE; RETURN
- END;
- Module(PCS.InitWithText(t, pos), source, opt, pc, log, diagnostics, error);
- END CompileText;
- PROCEDURE CompileInterface(t: Texts.Text; CONST source: ARRAY OF CHAR; pos: LONGINT; CONST pc,opt: ARRAY OF CHAR; log: Streams.Writer;
- diagnostics : Diagnostics.Diagnostics; VAR error: BOOLEAN);
- VAR pcNum: LONGINT;
- BEGIN
- Strings.StrToInt(pc, pcNum);
- CompileText(t,source,pos,pcNum, opt,log, diagnostics, error);
- END CompileInterface;
- (** Compile file *)
- PROCEDURE CompileFile*(CONST name, opt: ARRAY OF CHAR; pc: LONGINT; log: Streams.Writer;
- diagnostics : Diagnostics.Diagnostics; VAR error: BOOLEAN);
- VAR
- atu: Texts.Text; format: LONGINT; res: WORD;
- BEGIN
- NEW(atu);
- TextUtilities.LoadAuto(atu, name, format, res);
- IF res # 0 THEN
- log.String (name); log.String (" not found"); log.Ln; log.Update;
- error := TRUE; RETURN
- END;
- log.String (name);
- Module(PCS.InitWithText(atu, 0), name, opt, pc, log, diagnostics, error);
- END CompileFile;
- (** Compile ascii file *)
- PROCEDURE CompileAsciiFile*(CONST name, opt: ARRAY OF CHAR; pc: LONGINT; log: Streams.Writer;
- diagnostics : Diagnostics.Diagnostics; VAR error: BOOLEAN);
- VAR
- f: Files.File; r: Files.Reader;
- BEGIN
- f := Files.Old(name);
- IF f = NIL THEN
- log.String (name); log.String (" not found");
- log.Ln; log.Update;
- error := TRUE; RETURN
- END;
- log.String (name);
- Files.OpenReader(r, f, 0);
- Module(PCS.InitWithReader(r, f.Length(),0), name, opt, pc, log, diagnostics, error);
- END CompileAsciiFile;
- PROCEDURE Compile*(context : Commands.Context);
- VAR
- globalOpt, localOpt: OptionString;
- fullname, prefix, filename: ARRAY 256 OF CHAR;
- count: LONGINT;
- error: BOOLEAN;
- diagnostics : Diagnostics.DiagnosticsList;
- BEGIN
- PCT.InitDB(PCT.database);
- error := FALSE;
- globalOpt := ""; GetOptions(context.arg, globalOpt);
- GetSourcePrefix(globalOpt, prefix);
- count := 0;
- NEW(diagnostics);
- WHILE ~context.arg.EOLN() & ~error DO
- context.arg.String(filename);
- IF filename # "" THEN
- INC(count);
- COPY(globalOpt, localOpt);
- GetOptions(context.arg, localOpt);
- COPY(prefix, fullname); Strings.Append(fullname, filename);
- diagnostics.Reset;
- CompileFile(fullname, localOpt, MAX(LONGINT), context.out, diagnostics, error);
- diagnostics.ToStream(context.out, Diagnostics.All);
- PCM.LogFlush;
- IF count MOD 32 = 0 THEN PCT.InitDB(PCT.database) END;
- END
- END;
- PCT.InitDB(PCT.database);
- END Compile;
- PROCEDURE TrapHandler(pos, line, row: LONGINT; CONST msg: ARRAY OF CHAR);
- BEGIN
- PCM.LogWStr("could not load error messages: "); PCM.LogWLn;
- PCM.LogWStr(ErrorFile); PCM.LogWStr(" invalid (pos ");
- PCM.LogWNum(pos); PCM.LogWStr(", line ");
- PCM.LogWNum(line); PCM.LogWStr(", row ");
- PCM.LogWNum(row); PCM.LogWStr(" ");
- PCM.LogWStr(msg); PCM.LogWStr(")"); PCM.LogWLn;
- END TrapHandler;
- (** (Re)load error messages *)
- PROCEDURE InitErrMsg*; (** ~ *)
- VAR
- f: Files.File; scanner: XMLScanner.Scanner; parser: XMLParser.Parser; errors: XML.Document;
- e: XML.Element; enum, msgEnum: XMLObjects.Enumerator; p: ANY;
- code, i: LONGINT; str: XML.String;
- dynStr: DynamicStrings.DynamicString;
- r : Files.Reader;
- res : WORD;
- BEGIN
- Configuration.Get("Paco.ErrorMessages", ErrorFile, res);
- IF (res # Configuration.Ok) THEN ErrorFile := DefaultErrorFile END;
- f := Files.Old(ErrorFile);
- IF f = NIL THEN
- PCM.LogWStr("could not load error messages: ");
- PCM.LogWStr(ErrorFile); PCM.LogWStr(" not found"); PCM.LogWLn;
- RETURN;
- END;
- (* f # NIL *)
- Files.OpenReader(r, f, 0);
- NEW(scanner, r);
- NEW(parser, scanner); parser.reportError := TrapHandler;
- errors := parser.Parse();
- e := errors.GetRoot();
- enum := e.GetContents();
- WHILE enum.HasMoreElements() DO
- p := enum.GetNext();
- IF p IS XML.Element THEN
- e := p(XML.Element);
- str := e.GetName();
- IF str^ = ErrorTag THEN
- (* extract error code *)
- str := e.GetAttributeValue(ErrCodeAttr);
- Strings.StrToInt(str^, code);
- (* extract error message *)
- msgEnum := e.GetContents();
- NEW(dynStr);
- WHILE msgEnum.HasMoreElements() DO
- p := msgEnum.GetNext();
- IF p IS XML.Chars THEN
- str := p(XML.Chars).GetStr();
- dynStr.Append(str^);
- ELSIF p IS XML.CDataSect THEN
- str := p(XML.CDataSect).GetStr();
- dynStr.Append(str^);
- ELSIF p IS XML.CharReference THEN
- NEW(str, 5);
- i := 0;
- IF UTF8Strings.EncodeChar(p(XML.CharReference).GetCode(), str^, i) THEN
- dynStr.Append(str^);
- END;
- ELSE
- (* ignore *)
- END;
- END;
- str := dynStr.ToArrOfChar();
- PCM.SetErrorMsg(code, str^);
- dynStr.Init();
- END;
- END;
- END;
- END InitErrMsg;
- PROCEDURE Cleanup;
- BEGIN
- CompilerInterface.Unregister(Name);
- END Cleanup;
- BEGIN
- LastDest := "";
- PCM.LogWStr("Parallel Compiler / prk"); PCM.LogWLn;
- PCV.Install;
- InitErrMsg;
- Modules.InstallTermHandler(Cleanup);
- CompilerInterface.Register(Name, Description, FileExtension, CompileInterface);
- END PC.
- (*
- 21.11.07 fof new compiler option /M added (multiple modules within one file allowed, MODULE ident .... ident. MODULE ident ... ident. etc.)
- 10.08.07 sst new compiler option /p added
- 15.11.06 ug new compiler option /S added, FileVersion incremented
- 25.11.03 mb added InitErrMsg: read error messages from XML file
- 20.09.03 prk "/Dcode" compiler option added
- 24.06.03 prk Check that name after END is the same as declared after MODULE
- 25.02.03 prk PC split into PC (Aos pure) and PC (Oberon dependent)
- *)
|