(* M2Comp.mod continually calls the compiler for every source file. Copyright (C) 2001-2025 Free Software Foundation, Inc. Contributed by Gaius Mulley <gaius.mulley@southwales.ac.uk>. This file is part of GNU Modula-2. GNU Modula-2 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GNU Modula-2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Modula-2; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. *) IMPLEMENTATION MODULE M2Comp ; FROM M2Pass IMPORT SetPassToPass0, SetPassToPass1, SetPassToPass2, SetPassToPassC, SetPassToPass3, SetPassToNoPass, SetPassToPassHidden ; FROM M2Reserved IMPORT toktype ; FROM M2Search IMPORT FindSourceDefFile, FindSourceModFile ; FROM M2Code IMPORT Code ; FROM M2LexBuf IMPORT OpenSource, CloseSource, ResetForNewPass, currenttoken, GetToken, ReInitialize, currentstring, GetTokenNo, BuiltinTokenNo, UnknownTokenNo ; FROM M2FileName IMPORT CalculateFileName ; FROM M2Preprocess IMPORT PreprocessModule, MakeSaveTempsFileNameExt, OnExitDelete ; FROM libc IMPORT exit ; FROM M2Error IMPORT ErrorStringAt, ErrorStringAt2, ErrorStringsAt2, WriteFormat0, FlushErrors, FlushWarnings, ResetErrorScope ; FROM M2MetaError IMPORT MetaErrorString0, MetaErrorString1, MetaError0, MetaError1, MetaString0 ; FROM FormatStrings IMPORT Sprintf1 ; FROM P0SymBuild IMPORT P0Init, P1Init ; FROM M2Debug IMPORT Assert ; IMPORT m2flex ; IMPORT P0SyntaxCheck ; IMPORT P1Build ; IMPORT P2Build ; IMPORT PCBuild ; IMPORT P3Build ; IMPORT PHBuild ; IMPORT PCSymBuild ; IMPORT DynamicStrings ; FROM M2Batch IMPORT GetSource, GetModuleNo, GetDefinitionModuleFile, GetModuleFile, AssociateModule, AssociateDefinition, MakeImplementationSource, MakeProgramSource ; FROM SymbolTable IMPORT GetSymName, IsDefImp, NulSym, IsHiddenTypeDeclared, GetFirstUsed, GetMainModule, SetMainModule, ResolveConstructorTypes, SanityCheckConstants, IsDefinitionForC, IsBuiltinInModule, PutModLink, IsDefLink, IsModLink, PutLibName, GetModuleDefImportStatementList, GetModuleModImportStatementList, GetImportModule, IsImportStatement, IsImport, GetImportStatementList ; FROM M2Search IMPORT FindSourceDefFile ; FROM FIO IMPORT File, StdErr, StdOut, Close, EOF, IsNoError, WriteLine, WriteChar, FlushOutErr ; FROM SFIO IMPORT WriteS, OpenToRead, OpenToWrite, ReadS, WriteS ; FROM NameKey IMPORT Name, GetKey, KeyToCharStar, makekey ; FROM M2Printf IMPORT fprintf0, fprintf1 ; FROM M2Quiet IMPORT qprintf0, qprintf1, qprintf2 ; FROM M2Options IMPORT Verbose, GetM2Prefix, GetM, GetMM, GetDepTarget, GetMF, GetMP, GetObj, PPonly, Statistics, Quiet, WholeProgram, GetMD, GetMMD, ExtendedOpaque, GenModuleList ; FROM PathName IMPORT DumpPathName ; FROM Lists IMPORT List, NoOfItemsInList, GetItemFromList ; FROM Indexing IMPORT Index, InitIndex, KillIndex, GetIndice, PutIndice, HighIndice ; FROM DynamicStrings IMPORT String, InitString, KillString, InitStringCharStar, Dup, Mark, EqualArray, string, Length, ConCat, ConCatChar, InitStringChar, RIndex, Slice, Equal, RemoveWhitePrefix ; CONST Debugging = FALSE ; VAR ModuleType : (None, Definition, Implementation, Program) ; DepContent : Index ; DepOutput : String ; (* CompilingDefinitionModule - returns true if the current module being compiled is a definition module. *) PROCEDURE CompilingDefinitionModule() : BOOLEAN ; BEGIN RETURN( ModuleType=Definition ) END CompilingDefinitionModule ; (* CompilingImplementationModule - returns true if the current module being compiled is an implementation module. *) PROCEDURE CompilingImplementationModule() : BOOLEAN ; BEGIN RETURN( ModuleType=Implementation ) END CompilingImplementationModule ; (* CompilingProgramModule - returns true if the current module being compiled is a program module. *) PROCEDURE CompilingProgramModule() : BOOLEAN ; BEGIN RETURN( ModuleType=Program ) END CompilingProgramModule ; (* NeedToParseImplementation - *) PROCEDURE NeedToParseImplementation (sym: CARDINAL) : BOOLEAN ; BEGIN RETURN (IsDefImp(sym) AND IsHiddenTypeDeclared(sym) AND ExtendedOpaque) OR (IsDefImp(sym) AND IsBuiltinInModule(sym)) OR (WholeProgram AND (NOT IsDefinitionForC(sym))) END NeedToParseImplementation ; (* GenerateDefDependency - generate a single dependency for the definition module providing that it can be found and is not blocked by -MM. *) PROCEDURE GenerateDefDependency (module: CARDINAL) ; VAR stem, fullpath, named : String ; BEGIN stem := InitStringCharStar (KeyToCharStar (GetSymName (module))) ; named := NIL ; IF FindSourceDefFile (stem, fullpath, named) THEN IF EqualArray (named, '') OR (NOT GetMM ()) THEN MergeDep (DepContent, fullpath) ELSE fullpath := KillString (fullpath) END END ; stem := KillString (stem) ; named := KillString (named) END GenerateDefDependency ; (* GenerateDependenciesFromImport - lookup the module associated with the import and call GenerateDefDependency. *) PROCEDURE GenerateDependenciesFromImport (import: CARDINAL) ; VAR module : CARDINAL ; BEGIN Assert (IsImport (import)) ; module := GetImportModule (import) ; GenerateDefDependency (module) END GenerateDependenciesFromImport ; (* GenerateDependenciesFromList - iterative over the import lists and for each module issue a dependency. *) PROCEDURE GenerateDependenciesFromList (dep: List) ; VAR importList: List ; import : CARDINAL ; i, n, j, m: CARDINAL ; BEGIN n := NoOfItemsInList (dep) ; i := 1 ; WHILE i <= n DO import := GetItemFromList (dep, i) ; IF IsImportStatement (import) THEN importList := GetImportStatementList (import) ; j := 1 ; m := NoOfItemsInList (importList) ; WHILE j <= m DO import := GetItemFromList (importList, j) ; GenerateDependenciesFromImport (import) ; INC (j) END ELSE GenerateDependenciesFromImport (import) END ; INC (i) END END GenerateDependenciesFromList ; (* GenerateDependencies - generate a list of dependencies for the main module where the source code is found in sourcefile. *) PROCEDURE GenerateDependencies ; BEGIN IF IsDefImp (GetMainModule ()) THEN GenerateDependenciesFromList (GetModuleDefImportStatementList (GetMainModule ())) ; GenerateDefDependency (GetMainModule ()) END ; GenerateDependenciesFromList (GetModuleModImportStatementList (GetMainModule ())) ; WriteDepContents (DepOutput, DepContent) END GenerateDependencies ; (* Compile - compile file, s, using a 5 pass technique. *) PROCEDURE Compile (s: String) ; BEGIN DoPass0 (s) ; FlushWarnings ; FlushErrors ; ResetForNewPass ; ResetErrorScope ; qprintf0('Pass 1: scopes, enumerated types, imports and exports\n') ; DoPass1 ; FlushWarnings ; FlushErrors ; IF GetM () OR GetMM () THEN GenerateDependencies END ; IF NOT PPonly THEN qprintf0('Pass 2: constants and types\n') ; ResetForNewPass ; ResetErrorScope ; DoPass2 ; FlushWarnings ; FlushErrors ; qprintf0('Pass C: aggregate constants\n') ; ResetForNewPass ; ResetErrorScope ; DoPassC ; FlushWarnings ; FlushErrors ; qprintf0('Pass 3: quadruple generation\n') ; ResetForNewPass ; ResetErrorScope ; DoPass3 ; FlushWarnings ; FlushErrors ; qprintf0('Pass 4: gcc tree generation\n') ; Code ; FlushWarnings ; FlushErrors END END Compile ; (* compile - compile the filename. *) PROCEDURE compile (filename: ADDRESS) ; VAR f: String ; BEGIN f := InitStringCharStar (filename) ; Compile (f) ; f := KillString (f) END compile ; (* ExamineHeader - examines up until the ';', '[' or eof and determines if the source file is a program, implementation/definition module. *) PROCEDURE ExamineHeader (VAR name: ADDRESS; VAR isdefimp, module: BOOLEAN) ; BEGIN (* Stop if we see one of eof ';' '['. *) WHILE (currenttoken#eoftok) AND (currenttoken#semicolontok) AND (currenttoken#lsbratok) DO IF name = NIL THEN IF (currenttoken=implementationtok) OR (currenttoken=definitiontok) THEN isdefimp := TRUE ; GetToken END ; IF currenttoken=moduletok THEN module := TRUE ; GetToken ; IF currenttoken=identtok THEN name := currentstring END END ; END ; GetToken END ; END ExamineHeader ; (* ExamineCompilationUnit - opens the source file to obtain the module name and kind of module. *) PROCEDURE ExamineCompilationUnit () : CARDINAL ; VAR Message : String ; name : ADDRESS ; module, isdefimp: BOOLEAN ; BEGIN name := NIL ; isdefimp := FALSE ; (* default to program module *) module := FALSE ; (* Seen module keyword? *) ExamineHeader (name, isdefimp, module) ; IF name = NIL THEN IF module THEN Message := MetaString0 (InitString ('no {%kMODULE} keyword seen')) ELSE Message := MetaString0 (InitString ('no module ident seen')) END ; m2flex.M2Error (string (Message)) ; exit (1) ELSE (* The token used is will be overwritten when P0 is underway. At this point we are determining the module kind and the tokens read will be discarded (see ReInitialize below). *) IF isdefimp THEN RETURN MakeImplementationSource (BuiltinTokenNo, makekey (name)) ELSE RETURN MakeProgramSource (BuiltinTokenNo, makekey (name)) END END END ExamineCompilationUnit ; (* PeepInto - peeps into source, s, and initializes a definition/implementation or program module accordingly. *) PROCEDURE PeepInto (s: String) ; VAR mainModule: CARDINAL ; BEGIN IF OpenSource (s) THEN mainModule := ExamineCompilationUnit () ; IF mainModule # NulSym THEN SetMainModule (mainModule) END ; CloseSource ; ReInitialize ELSE fprintf1 (StdErr, 'failed to open %s\n', s) ; exit (1) END END PeepInto ; (* qprintLibName - print the libname. *) PROCEDURE qprintLibName (LibName: String) ; BEGIN IF (LibName # NIL) AND (NOT EqualArray (LibName, '')) THEN qprintf1 (' [%s]', LibName) END END qprintLibName ; (* CreateFileStem - create a stem using the template LibName_ModuleName. *) PROCEDURE CreateFileStem (SymName, LibName: String) : String ; BEGIN IF Length (LibName) > 0 THEN RETURN ConCat (Dup (LibName), ConCat (InitStringChar ('_'), SymName)) ELSE RETURN SymName END END CreateFileStem ; (* Return basename of path. CutExt determines whether the .extension should be removed. *) PROCEDURE BaseName (Path: String; CutExt: BOOLEAN) : String ; VAR ext, basename: INTEGER ; BEGIN basename := RIndex (Path, '/', 0) ; IF basename = -1 THEN basename := 0 ELSE basename := basename + 1 END ; IF CutExt THEN ext := RIndex (Path, '.', 0) ; IF ext=-1 THEN ext := 0 END ELSE ext := 0 END ; RETURN Slice (Path, basename, ext) END BaseName ; (* IsLibrary - return TRUE if line contains a library module. *) PROCEDURE IsLibrary (line: String) : BOOLEAN ; VAR moduleName, libname, filename: String ; result : BOOLEAN ; BEGIN result := FALSE ; moduleName := BaseName (line, TRUE) ; filename := NIL ; libname := NIL ; IF FindSourceDefFile (moduleName, filename, libname) THEN moduleName := KillString (moduleName) ; IF Length (libname) > 0 THEN moduleName := BaseName (line, FALSE) ; line := BaseName (line, FALSE) ; result := Equal (line, moduleName) ; line := KillString (line) ; END END ; libname := KillString (libname) ; filename := KillString (filename) ; moduleName := KillString (moduleName) ; RETURN result END IsLibrary ; (* IsUnique - return TRUE if line is unique in array content. *) PROCEDURE IsUnique (content: Index; line: String) : BOOLEAN ; VAR high, i: CARDINAL ; BEGIN high := HighIndice (content) ; i := 1 ; WHILE i <= high DO IF Equal (line, GetIndice (content, i)) THEN RETURN FALSE END ; INC (i) END ; RETURN TRUE END IsUnique ; (* Append - append line to array content. *) PROCEDURE Append (content: Index; line: String) ; VAR high: CARDINAL ; BEGIN high := HighIndice (content) ; PutIndice (content, high+1, line) END Append ; (* MergeDep - if line is unique in array content then append. Check to see (and ignore) if line is a library module and -MM is present. *) PROCEDURE MergeDep (content: Index; line: String) ; BEGIN line := RemoveWhitePrefix (line) ; IF (NOT EqualArray (line, "\")) AND (Length (line) > 0) THEN (* Ignore if -MM and is a library module. *) IF NOT (GetMM () AND IsLibrary (line)) THEN IF IsUnique (content, line) THEN Append (content, line) END END END END MergeDep ; (* splitLine - split a line into words separated by spaces and call MergeDep on each word. *) PROCEDURE splitLine (content: Index; line: String) ; VAR word : String ; space: INTEGER ; BEGIN REPEAT line := RemoveWhitePrefix (line) ; space := DynamicStrings.Index (line, ' ', 0) ; IF space > 0 THEN word := Slice (line, 0, space) ; word := RemoveWhitePrefix (word) ; IF Length (word) > 0 THEN MergeDep (content, word) END ; line := Slice (line, space, 0) ; ELSIF space < 0 THEN MergeDep (content, line) END UNTIL space <= 0 END splitLine ; (* MergeDeps - foreach dependency in ChildDep do add dependency to ChildDep if not already present. ignore all ChildDep if -MM and libname # "". *) PROCEDURE MergeDeps (content: Index; ChildDep, LibName: String) ; VAR line: String ; in : File ; BEGIN IF (content # NIL) AND (NOT (GetMM () AND (Length (LibName) > 0))) THEN in := OpenToRead (ChildDep) ; IF IsNoError (in) THEN line := ReadS (in) ; (* Skip over first line containing the module object. *) WHILE NOT EOF (in) DO line := ReadS (in) ; splitLine (content, line) END END ; Close (in) END END MergeDeps ; (* GetRuleTarget - return the rule target which is derived from the -MT arg or -o arg or filename.mod. *) PROCEDURE GetRuleTarget (filename: String) : String ; BEGIN IF GetDepTarget () # NIL THEN RETURN InitStringCharStar (GetDepTarget ()) ELSIF GetMF () # NIL THEN RETURN InitStringCharStar (GetMF ()) ELSE RETURN ConCat (BaseName (filename, TRUE), InitString ('.o')) END END GetRuleTarget ; (* ReadDepContents - reads the contents of file dep into a dynamic array and return the array. The file will be split into words and each word stored as an entry in the array. *) PROCEDURE ReadDepContents (filename, dep: String) : Index ; VAR content: Index ; line : String ; in : File ; BEGIN content := NIL ; IF GetM () OR GetMM () THEN in := OpenToRead (dep) ; (* The file might not be created (if -MD or -MMD is used as these options operate without preprocessing) in which case we create an dynamic array with the source filename and target. *) content := InitIndex (1) ; IF GetMD () OR GetMMD () OR (NOT IsNoError (in)) THEN (* No preprocessing done therefore create first two lines using target and source. *) PutIndice (content, 1, ConCatChar (GetRuleTarget (filename), ':')) ; PutIndice (content, 2, Dup (filename)) ELSE (* Preprocessing (using cc1) has created one for us, so we read it. *) WHILE NOT EOF (in) DO line := ReadS (in) ; splitLine (content, line) END END ; Close (in) END ; RETURN content END ReadDepContents ; (* WriteDep - write the dependencies and target to file out. *) PROCEDURE WriteDep (contents: Index; out: File) ; VAR i, h: CARDINAL ; line: String ; BEGIN i := 1 ; h := HighIndice (contents) ; WHILE i <= h DO line := GetIndice (contents, i) ; line := RemoveWhitePrefix (line) ; IF Length (line) > 0 THEN IF i = 1 THEN (* First line is always the target. *) IF GetDepTarget () # NIL THEN line := ConCatChar (InitStringCharStar (GetDepTarget ()), ':') END ELSIF i > 1 THEN WriteChar (out, ' ') END ; line := WriteS (out, line) ; IF i < h THEN WriteChar (out, ' ') ; WriteChar (out, '\') END ; WriteLine (out) END ; INC (i) END END WriteDep ; (* WritePhonyDep - write the dependencies and target to file out. *) PROCEDURE WritePhonyDep (contents: Index; out: File) ; VAR i, h: CARDINAL ; line: String ; BEGIN (* The first line is always the target and the second line is always the top level source file. *) i := 3 ; h := HighIndice (contents) ; WHILE i <= h DO line := GetIndice (contents, i) ; line := RemoveWhitePrefix (line) ; IF Length (line) > 0 THEN line := WriteS (out, line) ; WriteChar (out, ':') ; WriteLine (out) END ; INC (i) END END WritePhonyDep ; (* WriteDepContents - write the dynamic array to filename dep (or StdOut) if the GetMF file is NIL. *) PROCEDURE WriteDepContents (dep: String; contents: Index) ; VAR out: File ; BEGIN IF (contents # NIL) AND (GetM () OR GetMM ()) THEN IF GetMF () = NIL THEN out := StdOut ; dep := OnExitDelete (dep) ELSE out := OpenToWrite (dep) END ; IF IsNoError (out) THEN WriteDep (contents, out) ; IF GetMP () THEN WritePhonyDep (contents, out) END END ; IF GetMF () = NIL THEN FlushOutErr ELSE Close (out) ; END ; contents := KillIndex (contents) END END WriteDepContents ; (* CreateDepFilename - return a dependency filename associated with filename or use GetMF. *) PROCEDURE CreateDepFilename (filename: String) : String ; VAR depfile: String ; BEGIN IF GetMF () = NIL THEN depfile := MakeSaveTempsFileNameExt (filename, InitString ('.d')) ; RETURN OnExitDelete (depfile) ELSE RETURN InitStringCharStar (GetMF ()) END END CreateDepFilename ; (* Pass0CheckDef - *) PROCEDURE Pass0CheckDef (sym: CARDINAL) : BOOLEAN ; VAR ChildDep, SymName, FileName, LibName : String ; BEGIN LibName := NIL ; FileName := NIL ; SymName := InitStringCharStar (KeyToCharStar (GetSymName (sym))) ; IF IsDefImp (sym) THEN IF FindSourceDefFile (SymName, FileName, LibName) THEN ModuleType := Definition ; ChildDep := MakeSaveTempsFileNameExt (CreateFileStem (SymName, LibName), InitString ('.def.d')) ; IF OpenSource (AssociateDefinition (PreprocessModule (FileName, FALSE, TRUE, ChildDep), sym)) THEN IF NOT P0SyntaxCheck.CompilationUnit () THEN WriteFormat0 ('compilation failed') ; CloseSource ; SymName := KillString (SymName) ; FileName := KillString (FileName) ; LibName := KillString (LibName) ; RETURN FALSE END ; qprintf2 (' Module %-20s : %s', SymName, FileName) ; qprintLibName (LibName) ; PutLibName (sym, makekey (string (LibName))) ; IF IsDefinitionForC (sym) THEN qprintf0 (' (for C)') END ; IF IsDefLink (sym) THEN qprintf0 (' (linking)') END ; qprintf0 ('\n') ; CloseSource ; MergeDeps (DepContent, ChildDep, LibName) ELSE (* Unrecoverable error. *) MetaErrorString1 (Sprintf1 (InitString ('file {%%1EUAF%s} containing module {%%1a} cannot be found'), FileName), sym) END ELSE (* Unrecoverable error. *) MetaError1 ('the file containing the definition module {%1EMAa} cannot be found', sym) END ; ModuleType := Implementation ELSE ModuleType := Program END ; SymName := KillString (SymName) ; FileName := KillString (FileName) ; LibName := KillString (LibName) ; RETURN TRUE END Pass0CheckDef ; (* Pass0CheckMod - *) PROCEDURE Pass0CheckMod (sym: CARDINAL; PPSource: String) : BOOLEAN ; VAR Main : CARDINAL ; ChildDep, SymName, FileName, LibName : String ; BEGIN SymName := InitStringCharStar (KeyToCharStar (GetSymName (sym))) ; FileName := NIL ; LibName := NIL ; Main := GetMainModule () ; IF (Main = sym) OR NeedToParseImplementation (sym) THEN (* Only need to read implementation module if hidden types are declared or it is the main module. *) IF Main = sym THEN FileName := Dup (PPSource) ; LibName := InitStringCharStar (GetM2Prefix ()) ; PutLibName (sym, makekey (string (LibName))) ELSE IF FindSourceModFile (SymName, FileName, LibName) THEN ChildDep := MakeSaveTempsFileNameExt (CreateFileStem (SymName, LibName), InitString ('.mod.d')) ; FileName := PreprocessModule (FileName, FALSE, TRUE, ChildDep) ; PutLibName (sym, makekey (string (LibName))) ; MergeDeps (DepContent, ChildDep, LibName) ELSE qprintf1 (' Module %-20s : implementation source file not found\n', SymName) END END ; IF FileName # NIL THEN IF OpenSource (AssociateModule (Dup (FileName), sym)) THEN IF NOT P0SyntaxCheck.CompilationUnit () THEN WriteFormat0 ('compilation failed') ; CloseSource ; SymName := KillString (SymName) ; FileName := KillString (FileName) ; LibName := KillString (LibName) ; RETURN FALSE END ; qprintf2 (' Module %-20s : %s', SymName, FileName) ; qprintLibName (LibName) ; IF IsModLink (sym) THEN qprintf0 (' (linking)') END ; qprintf0 ('\n') ; CloseSource ELSE (* It is quite legitimate to implement a module in C (and pretend it was a M2 implementation) providing that it is not the main program module and the definition module does not declare a hidden type when -fextended-opaque is used. *) IF (NOT WholeProgram) OR (sym = Main) OR IsHiddenTypeDeclared (sym) THEN (* Unrecoverable error. *) MetaErrorString1 (Sprintf1 (InitString ('file {%%1EUAF%s} containing module {%%1a} cannot be found'), FileName), sym) ; END END END ELSIF GenModuleList THEN IF IsDefImp (sym) AND (NOT IsDefinitionForC (sym)) THEN (* The implementation module is only useful if -fgen-module-list= is used (to gather all dependencies). Note that we do not insist upon finding the implementation module. *) LibName := NIL ; IF FindSourceModFile (SymName, FileName, LibName) THEN PutLibName (sym, makekey (string (LibName))) ; qprintf2 (' Module %-20s : %s' , SymName, FileName) ; qprintLibName (LibName) ; qprintf0 (' (linking)\n') ; ChildDep := MakeSaveTempsFileNameExt (CreateFileStem (SymName, LibName), InitString ('.mod.d')) ; IF OpenSource (AssociateModule (PreprocessModule (FileName, FALSE, TRUE, ChildDep), sym)) THEN PutModLink (sym, TRUE) ; (* This source is only used to determine link time info. *) IF NOT P0SyntaxCheck.CompilationUnit () THEN WriteFormat0 ('compilation failed') ; CloseSource ; SymName := KillString (SymName) ; FileName := KillString (FileName) ; LibName := KillString (LibName) ; RETURN FALSE END ; CloseSource ; MergeDeps (DepContent, ChildDep, LibName) END END END END ; SymName := KillString (SymName) ; FileName := KillString (FileName) ; LibName := KillString (LibName) ; RETURN TRUE END Pass0CheckMod ; (* DoPass0 - *) PROCEDURE DoPass0 (filename: String) ; VAR sym : CARDINAL ; i : CARDINAL ; PPSource : String ; BEGIN P0Init ; SetPassToPass0 ; (* Maybe preprocess the main file. *) DepOutput := CreateDepFilename (filename) ; PPSource := PreprocessModule (filename, TRUE, FALSE, DepOutput) ; DepContent := ReadDepContents (filename, DepOutput) ; PeepInto (PPSource) ; i := 1 ; sym := GetModuleNo (i) ; qprintf1 ('Compiling: %s\n', PPSource) ; IF Debugging THEN DumpPathName ('DoPass0') END ; IF Verbose THEN fprintf1 (StdOut, 'Compiling: %s\n', PPSource) END ; qprintf0 ('Pass 0: lexical analysis, parsing, modules and associated filenames\n') ; WHILE sym # NulSym DO IF NOT Pass0CheckDef (sym) THEN RETURN END ; IF NOT Pass0CheckMod (sym, PPSource) THEN RETURN END ; INC (i) ; sym := GetModuleNo (i) END ; SetPassToNoPass END DoPass0 ; (* DoPass1 - parses the sources of all modules necessary to compile the required module, Main. *) PROCEDURE DoPass1 ; VAR name : Name ; Sym : CARDINAL ; i : CARDINAL ; FileName: String ; BEGIN P1Init ; SetPassToPass1 ; i := 1 ; Sym := GetModuleNo(i) ; WHILE Sym#NulSym DO FileName := GetDefinitionModuleFile(Sym) ; IF FileName#NIL THEN IF Debugging THEN name := GetSymName(Sym) ; qprintf1(' Module %a\n', name) END ; IF OpenSource(FileName) THEN ModuleType := Definition ; IF NOT P1Build.CompilationUnit() THEN MetaError0('compilation failed') ; CloseSource ; RETURN END ; CloseSource ELSE fprintf1(StdErr, 'failed to open %s\n', FileName) ; exit(1) END ; ModuleType := Implementation ELSE ModuleType := Program END ; FileName := GetModuleFile(Sym) ; IF FileName#NIL THEN IF Debugging THEN name := GetSymName(Sym) ; qprintf1(' Module %a\n', name) END ; IF OpenSource(FileName) THEN IF NOT P1Build.CompilationUnit() THEN MetaError0('compilation failed') ; CloseSource ; RETURN END ; CloseSource ELSE fprintf1(StdErr, 'failed to open %s\n', FileName) ; exit(1) END END ; INC(i) ; Sym := GetModuleNo(i) END ; SetPassToNoPass END DoPass1 ; (* DoPass2 - parses the sources of all modules necessary to compile the required module, Main. *) PROCEDURE DoPass2 ; VAR name : Name ; Sym : CARDINAL ; i : CARDINAL ; FileName: String ; BEGIN SetPassToPass2 ; i := 1 ; Sym := GetModuleNo(i) ; WHILE Sym#NulSym DO FileName := GetDefinitionModuleFile(Sym) ; IF FileName#NIL THEN IF Debugging THEN name := GetSymName(Sym) ; qprintf1(' Module %a\n', name) END ; IF OpenSource(FileName) THEN ModuleType := Definition ; IF NOT P2Build.CompilationUnit() THEN MetaError0('compilation failed') ; CloseSource ; RETURN END ; CloseSource ELSE fprintf1(StdErr, 'failed to open %s\n', FileName) ; exit(1) END ; ModuleType := Implementation ELSE ModuleType := Program END ; FileName := GetModuleFile(Sym) ; IF FileName#NIL THEN IF Debugging THEN name := GetSymName(Sym) ; qprintf1(' Module %a\n', name) END ; IF OpenSource(FileName) THEN IF NOT P2Build.CompilationUnit() THEN MetaError0('compilation failed') ; CloseSource ; RETURN END ; CloseSource ELSE fprintf1(StdErr, 'failed to open %s\n', FileName) ; exit(1) END END ; INC(i) ; Sym := GetModuleNo(i) END ; SetPassToNoPass END DoPass2 ; (* DoPassC - parses the sources of all modules necessary to compile the required module, Main. *) PROCEDURE DoPassC ; VAR name : Name ; Sym : CARDINAL ; i : CARDINAL ; FileName: String ; BEGIN SetPassToPassC ; i := 1 ; Sym := GetModuleNo(i) ; WHILE Sym#NulSym DO FileName := GetDefinitionModuleFile(Sym) ; IF FileName#NIL THEN IF Debugging THEN name := GetSymName(Sym) ; qprintf1(' Module %a\n', name) END ; IF OpenSource(FileName) THEN ModuleType := Definition ; IF NOT PCBuild.CompilationUnit() THEN MetaError0('compilation failed') ; CloseSource ; RETURN END ; CloseSource ELSE fprintf1(StdErr, 'failed to open %s\n', FileName) ; exit(1) END ; ModuleType := Implementation ELSE ModuleType := Program END ; FileName := GetModuleFile(Sym) ; IF FileName#NIL THEN IF Debugging THEN name := GetSymName(Sym) ; qprintf1(' Module %a\n', name) END ; IF OpenSource(FileName) THEN IF NOT PCBuild.CompilationUnit() THEN MetaError0('compilation failed') ; CloseSource ; RETURN END ; CloseSource ELSE fprintf1(StdErr, 'failed to open %s\n', FileName) ; exit(1) END END ; INC(i) ; Sym := GetModuleNo(i) END ; PCSymBuild.ResolveConstTypes ; ResolveConstructorTypes ; SanityCheckConstants ; SetPassToNoPass END DoPassC ; (* DoPass3 - parses the sources of all modules necessary to compile the required module, Main. *) PROCEDURE DoPass3 ; VAR Main, Sym : CARDINAL ; i : CARDINAL ; FileName: String ; BEGIN SetPassToPass3 ; Main := GetMainModule() ; i := 1 ; Sym := GetModuleNo(i) ; WHILE Sym#NulSym DO FileName := GetDefinitionModuleFile(Sym) ; IF FileName#NIL THEN IF OpenSource(FileName) THEN ModuleType := Definition ; IF NOT P3Build.CompilationUnit() THEN MetaError0('compilation failed') ; CloseSource ; RETURN END ; CloseSource ELSE fprintf1(StdErr, 'failed to open %s\n', FileName) ; exit(1) END ; ModuleType := Implementation ELSE ModuleType := Program END ; FileName := GetModuleFile(Sym) ; IF FileName#NIL THEN IF OpenSource(FileName) THEN IF (Main=Sym) OR WholeProgram THEN IF NOT P3Build.CompilationUnit() THEN MetaError0('compilation failed') ; CloseSource ; RETURN END ELSE (* not the main module .mod therefore must be implementing a hidden type - we dont want to generate any StatementSequence quadrupes but we do want to build TYPEs and ConstExpressions. *) SetPassToNoPass ; SetPassToPassHidden ; IF NOT PHBuild.CompilationUnit() THEN MetaError0('compilation failed') ; CloseSource ; RETURN END ; SetPassToNoPass ; SetPassToPass3 END ; CloseSource ELSE fprintf1(StdErr, 'failed to open %s\n', FileName) ; exit(1) END END ; INC(i) ; Sym := GetModuleNo(i) END ; SetPassToNoPass END DoPass3 ; BEGIN ModuleType := None ; DepContent := NIL ; DepOutput := NIL END M2Comp.