IMPLEMENTATION MODULE Ycc;

FROM Conversions IMPORT IntConversions, PatternMapper;
FROM DateTime IMPORT Now, Time, Instant, Today;
FROM Directories IMPORT Operations;
IMPORT ModuleTable;
IMPORT Space;
FROM Streams IMPORT StdOut, InputStream, Stream, StdErr, OutputStream;
IMPORT String;
IMPORT SYSTEM;
FROM YaflCfg IMPORT YaflCfg, CurrentSpot;
FROM YaflClasses IMPORT ClassDeclaration;
FROM YaflClDefinition IMPORT ClassDefinition;
FROM YaflClImplementation IMPORT ClassImplementation;
FROM YaflGC IMPORT YaflGC;
FROM YaflMethods IMPORT MethodDeclaration;
FROM YaflModules IMPORT ImplementationModule, DefinitionModule, ModuleSet;
FROM YaflNTList IMPORT NTList;
FROM YaflCompiler IMPORT Compiler;
FROM YaflError IMPORT MainErrorHandler, WarningHandler, CompilerError;
FROM YaflWorlds IMPORT WorldList;
IMPORT YaflWorlds;                                                        
FROM YaflPredefined IMPORT PredefItems;
FROM YaflProject IMPORT ProjectFile, ProjectModule, ProjectWorld;

FROM YaflDictionary IMPORT ClassDictionary, MethodDictionary, 
                            EntryDictionary,CallDictionary;

FROM YaflController IMPORT Controller;

FROM CGCode     IMPORT CCodeGeneratorCreator;
FROM MetrGCode     IMPORT MetrCodeGeneratorCreator;
FROM CGModules     IMPORT CDefModuleCodeGenerator;
FROM YaflCreator IMPORT Creators;

---------------------------------------------------------------------------

   CLASS CompilationRun;      
     VAR
       TheModuleName: ARRAY OF CHAR;
       TheErrors,
       TheWarnings: List(CompilerError);
       TheParsedLines,
       TheGeneratedLines: INTEGER;      
       TheBeginInstant, TheEndInstant: Instant;
       TheCompiledModule : ImplementationModule;
   
     REDEFINE METHOD CREATE (ModuleName : ARRAY OF CHAR;
                             KeepModule : BOOLEAN;
                             CodeGCList: CodeGeneratorCreatorList);
       VAR
         Comp: Compiler;
         OldLines: INTEGER;

       METHOD CheckErrors;
         BEGIN
         IF MainErrorHandler.ErrorCount > 0 THEN
           MainErrorHandler.PrintErrorList;
           MainErrorHandler.SetExitCode (MainErrorHandler.ErrorFound);
           END;
         MainErrorHandler.Reset;
         -------------------------------
         -- Issue warnings, and eventually consider 
         -- them as fatal depending on configuration
         -- options.
         -------------------------------
         IF WarningHandler.ErrorCount > 0 THEN
           IF YaflCfg.WarningLevel > 0 THEN
             WarningHandler.PrintErrorList;
             IF YaflCfg.WarningLevel > 1 THEN
               MainErrorHandler.SetExitCode (MainErrorHandler.WarningFound);
               END;
             END;
           WarningHandler.Reset;
           END;
         END CheckErrors;
        
       METHOD DoCompile: ImplementationModule;
         BEGIN
         RESULT := Comp.Compile (ModuleName, 
                        VerboseLevel := YaflCfg.VerboseLevel,
                        PleaseTag := YaflCfg.PleaseTag,
                        PleaseCheckType := YaflCfg.PleaseCheckType,
                        PleaseGenerateMetrics := YaflCfg.PleaseGenerateMetrics,
                        PleaseLint := YaflCfg.PleaseLint,
                        CodeGCList
                        );
         END DoCompile;                       
        
       BEGIN  
       OldLines := ModuleTable.Lines;                                    
       BASE;
       TheBeginInstant.CREATE (Today.Instant, Now.Instant);
       CurrentSpot.SetCurrentCompilationRun (THIS);
       TheModuleName := ModuleName;
       Comp.CREATE;
       TheCompiledModule := DoCompile;
       IF TheCompiledModule = VOID THEN
         MainErrorHandler.SetExitCode (MainErrorHandler.NoSource);
         IF NOT YaflCfg.SilentMode THEN
           StdOut.WriteLine("Cannot open implementation module: " + ModuleName);
           END;                            
        ELSE
         CheckErrors;
         TheGeneratedLines := Comp.GeneratedLines;
         END;
       IF NOT KeepModule THEN
         EraseCompiledModule;
         END;
       TheParsedLines := ModuleTable.Lines - OldLines;
       TheEndInstant.CREATE (Today.Instant, Now.Instant);
       CurrentSpot.SetCurrentCompilationRun (VOID);
       END CREATE;
            
     METHOD CompiledModule : ImplementationModule;
       BEGIN              
       RESULT := TheCompiledModule;
       END CompiledModule;

     METHOD EraseCompiledModule;
       BEGIN
       TheCompiledModule := VOID;
       END EraseCompiledModule;
      
     METHOD Errors: List(CompilerError);
       BEGIN
       RESULT := TheErrors;
       END Errors;
       
     METHOD Warnings: List(CompilerError);
       BEGIN
       RESULT := TheWarnings;
       END Warnings;
       
     METHOD AppendError (Err: CompilerError);  
       BEGIN
       IF TheErrors = VOID THEN
         TheErrors.CREATE;
         END;
       TheErrors.Append (Err);
       END AppendError;
       
     METHOD AppendWarning (Err: CompilerError);
       BEGIN
       IF TheWarnings = VOID THEN
         TheWarnings.CREATE;
         END;
       TheWarnings.Append (Err);
       END AppendWarning;
       
     METHOD Ok: BOOLEAN;     
       BEGIN
       RESULT := (TheErrors = VOID) OR (TheErrors.Size = 0);
       END Ok;
       
     METHOD ParsedLines: INTEGER;
       BEGIN
       RESULT := TheParsedLines;
       END ParsedLines;
       
     METHOD GeneratedLines: INTEGER;
       BEGIN              
       RESULT := TheGeneratedLines;
       END GeneratedLines;        
       
     METHOD BeginInstant: Instant;
       BEGIN
       RESULT := TheBeginInstant;
       END BeginInstant;
       
     METHOD EndInstant: Instant;
       BEGIN
       RESULT := TheEndInstant;
       END EndInstant;
       
     METHOD Duration: INTEGER;
       BEGIN           
       RESULT := BeginInstant.Interval (EndInstant);
       END Duration;
       
     METHOD ModuleName: ARRAY OF CHAR;
       BEGIN          
       RESULT := TheModuleName;
       END ModuleName;
       
   END CompilationRun;
-----------------------------------------------------   
  CLASS YaflFrontEnd; 
  
    METHOD Disclaimer: ARRAY OF CHAR;
      BEGIN
      RESULT := "Yafl Compiler - Version " + Version + 
                   " - PhiDaNi Software 1991,98";
      END Disclaimer;                   
      
    METHOD DisplayDisclaimer;
      BEGIN
      l (Disclaimer);
      END DisplayDisclaimer;

    VAR
      DisclaimerDisplayed: BOOLEAN;
                   
    METHOD l(a: ARRAY OF CHAR);
      BEGIN
      IF a <> VOID THEN
        IF NOT DisclaimerDisplayed THEN
          DisclaimerDisplayed := TRUE;
          DisplayDisclaimer;
          END;
        StdOut.WriteLine (a);
        END;
      END l;                   
     
    METHOD ToString (i: INTEGER): ARRAY OF CHAR;
      BEGIN
      RESULT := IntConversions.IntToString (i, 0);
      END ToString;

    METHOD Help;
      BEGIN
l("");
l("Options:");
l("  (x) is the default option");
l("");
l("                          Compilation flags");
l("-bs[0|1|2|3|(4)] syntax only | tag | check type | generate code");
l("-by[0|(1)] generate YAM code");
l("");
l("                        Code generation flags");
l("-d[0|(1)]  debug flag              -ca[(0)|1] check assertions");
l("-k[0|(1)|2]  trace mode level      -cd[(0)|1] check debug statements");
l("-cr[(0)|1] check array accesses    -cc[(0)|1] check case statements");
l("-co[(0)|1] check void object usage -cm[(0)|1] check void method usage");
l("-cw[(0)|1] check what statements   -cs[(0)|1] check dual stack overflow");
l("-cf[0|1] force the generation of attribute information");
l("-ce[0|(1)] generate metrics data   -cx[(0)|1] generate absolute path");
l("-cv[0|(1)|2] generate version checking code   name references");
l("-cp[(0)|1] generate profiling information");
l("-cp[(0)|1] generate profiling information");
l("");
l("                           Error handling");
l("-w[0|(1)|2] disable | enable | fatal warnings -es[0|1] silent mode");
l("-eo<filename> output errors on <filename>     -ez[(0)|1] immediate output");
l("-ea<filename> append errors on <filename>     -ex[(0)|1] extended checks");
l("-el[(0)|1] literate programming error processing");
l("");
l("                           Miscellaneous");
l("-s[(0)|1] display statistics                  -v[(0)|1|2|3] verbose");
l("-h display this help information              -ms[(0)|1] strip memory");
l("                                              -mf[(0)|1] flush memory");
l("-ma<num> set the maximum amount of dynamic memory to <num> bytes");
l("-mc<filename> use <filename> as supplementary configuration file");
l("-md      dump the entire configuration");
l("");
l("                         Portability issues");
l("-oi<num> set the maximum C internal ident len to <num> characters");
l("-ox<num> set the maximum C external ident len to <num> characters");
l("-os<num> set the maximum C string literal len to <num> characters");
l("-ot<c> set the target ('u' is Unix, 'd' is Dos, 'w' is Windows, 'm' is Mvs");
l("                       'o' is Os/2  'q' is Qnx)");
l("");
l("                      Global optimization flags");
l("-ql[(0)|1] optimize method call       -qi[(0)|1] generate in line");
l("-qr[(0)|1] remove unused methods      -qd[(0)|1] remove ASSERTs and DEBUGs");
l("-qs[(0)|1] optimize value stack handling");
l("-qa[(0)|1] optimize all");
      END Help;
      
    METHOD CleanUpMemory;
      BEGIN
      ModuleTable.Zap;
      PredefItems.Reset;
      MainErrorHandler.SetRef (VOID);
      WarningHandler.SetRef (VOID);
      END CleanUpMemory;
        
    VAR
      NotFirst: ONCE BOOLEAN;

    METHOD CheckMemory;                             
    
      METHOD ShortOnMemory: BOOLEAN;                     
        VAR
          Remaining: INTEGER;
        BEGIN
        Remaining := SYSTEM.GetMaxMem - SYSTEM.Allocated;
        RESULT := SYSTEM.GetMaxMem / Remaining > 2;
        END ShortOnMemory;
        
      BEGIN               
      IF NotFirst THEN
        IF (YaflCfg.StripMemMode OR YaflCfg.FlushMemMode) THEN
          YaflCfg.GarbageCollector;
          END;
        IF YaflCfg.FlushMemMode THEN
          CleanUpMemory;
          YaflCfg.GarbageCollector;
         ELSIF ShortOnMemory THEN
          YaflCfg.GarbageCollector;
          IF ShortOnMemory THEN
            CleanUpMemory;
            YaflCfg.GarbageCollector;
            IF ShortOnMemory THEN
              StdErr.WriteLine ("Warning: memory is getting short");
              END;
            END;
          END;
        END;
      NotFirst := TRUE;
      END CheckMemory;
      
    ----------------------------------------------
    -- The Compile method compiles the module identified
    -- by ModuleName, and returns the number of lines which
    -- have been generated. If proper options are set, and if
    -- the code generation process is cancelled, it returns 0.
    ----------------------------------------------
    METHOD Compile (ModuleName: ARRAY OF CHAR; 
                    KeepModule : BOOLEAN): CompilationRun;
      BEGIN
      IF NOT NotFirst THEN
        IF (YaflCfg.VerboseLevel > 0) THEN
          l("Currently active worlds:");
          WorldList.Dump (StdOut);
          END;
        END;           
      CheckMemory;
      RESULT.CREATE (ModuleName, KeepModule, GetCodeGCList);
      END Compile;
      
    METHOD ProcessOption (Opt: ARRAY OF CHAR): BOOLEAN;
      BEGIN
      ASSERT Opt[0] = '-';
      RESULT := YaflCfg.CommandLineOption (Opt);
      END ProcessOption;
      
    METHOD InitYaflEnvironment;
      BEGIN
      YaflCfg.Initialize;
      END InitYaflEnvironment;

    REDEFINE METHOD CREATE;
      BEGIN
      InitYaflEnvironment;
      BASE;
      END CREATE;

    VAR 
      TheCGList: CodeGeneratorCreatorList;

    METHOD BuildCodeGCList: CodeGeneratorCreatorList;
      VAR
        CCreator: CCodeGeneratorCreator;
        MetrCreator: MetrCodeGeneratorCreator;
      BEGIN
      RESULT.CREATE;
      IF YaflCfg.PleaseGenerateCode THEN
        CCreator.CREATE;
        RESULT.Append(CCreator);
        END;
      IF YaflCfg.PleaseGenerateMetrics THEN
        MetrCreator.CREATE;         

        RESULT.Append(MetrCreator);
        END;
      END BuildCodeGCList;

    METHOD GetCodeGCList: CodeGeneratorCreatorList;
      BEGIN
      IF TheCGList = VOID THEN
        TheCGList := BuildCodeGCList;
        END;
      RESULT := TheCGList;
      END GetCodeGCList;

    END YaflFrontEnd;

-----------------------------------------------------    
  CLASS MultiLineMapper;
    INHERITS PatternMapper;
    
    METHOD MapMultiLine (FirstName, AltName: ARRAY OF CHAR):
                ARRAY OF ARRAY OF CHAR;
      VAR
        Pat: ARRAY OF ARRAY OF CHAR;
        BEGIN
      Pat := YaflCfg.FindMultiLineValue (FirstName);
      IF (Pat = VOID) AND (AltName <> VOID) THEN
        Pat := YaflCfg.FindMultiLineValue (AltName);
        END;
      IF Pat <> VOID THEN
        RESULT.CREATE (Pat.SIZE);
        FOR i := 0 TO RESULT.SIZE - 1 DO
          RESULT[i] := Map (Pat[i]);
          END;
        END;
      END MapMultiLine;

    METHOD WriteMultiLineMap (Output: OutputStream;
                              FirstName, AltName: ARRAY OF CHAR);
      VAR
        p: ARRAY OF ARRAY OF CHAR;
        BEGIN
      p := MapMultiLine (FirstName, AltName);
      IF p <> VOID THEN
        FOR i := 0 TO p.SIZE - 1 DO
          Output.WriteLine (p[i]);
          END;
        END;
      END WriteMultiLineMap;
        
  END MultiLineMapper;
  -----------------------------------------
  CLASS Yc;
    INHERITS YaflFrontEnd;
 
      VAR
        MissingTargetFilesOnlyFlag,
        DirectDependenciesOnlyFlag, 
        AllWorldsFlag,
        PleaseExplainFlag,
        PleaseDeleteTargetFlag,
        ExternalExecutionFlag,
        PleaseIgnoreReturnCodesFlag: BOOLEAN;
        
        CurrentCLevel, 
        CurrentYaflLevel, 
        CurrentLinkLevel: INTEGER;
        
      VAR
        CurrentPrjName,
        CurrentRootModule,
        CurrentExecutionFileName: ARRAY OF CHAR;                      
        
        Mapper: MultiLineMapper;

        ExecutionFile: OutputStream;
        ParsedLines,
        GeneratedLines: INTEGER;
        StartInstant, StopInstant: Instant;
        
    METHOD GetMapper: MultiLineMapper;
      BEGIN
      RESULT := Mapper;
      END GetMapper;        
        
-------------------------------------------------
    METHOD GenerateStub (Prj: ProjectFile;
                         Module: ImplementationModule;
                         Class: ClassDefinition);
      VAR
        Output: YaflGC;
        p: ARRAY OF DefinitionModule;
         i: INTEGER;
        Line, ModName, TargetName, ClassName : ARRAY OF CHAR;
        ModSet: ModuleSet;
        Image: ARRAY OF ARRAY OF CHAR;
      BEGIN
      CurrentSpot.PushCurrentModule(Module);
      ModName := Module.Id.Data;
      ClassName := Class.Id.Data;
      TargetName := Prj.StubFileName;
      IF YaflCfg.VerboseLevel > 0 THEN
        l ("Generating " + TargetName);
        END;
      Output.CREATE;
      Output.Create (Module.GetWorld.BuildTargetFName 
                     (ClassName,YaflCfg.IntermediateExt), Output.WriteAccess);
      IF Output.ErrorCode <> Stream.NoError THEN
        l("Cannot open " + TargetName + " for writing");
       ELSE 
        Output.Comment ("***********************************************");
        Output.Comment ("*  Automatically generated stub file [" +
                              YaflFrontEnd.Version + "]");
        Output.Comment ("*  Main class:  " + ClassName);
        Output.Comment ("*  Main module: " + ModName);
        Output.Comment ("***********************************************");

        Output.IncludeRuntime;
        Image := YaflCfg.FindMultiLineValue ("MAKE.STUB");
        IF (Image = VOID) OR (Image.SIZE = 0) THEN
          StdOut.WriteLine ("Warning: MAKE.STUB environment variable not "+
                            "defined");
          Image.CREATE (1);
          Image[0] := "";
          END;
        FOR k := 0 TO Image.SIZE - 1 DO
          Line := Image[k];
          i := 0;
          WHILE i < Line.SIZE DO
            IF (i = Line.SIZE - 1) OR (Line[i] <> '$') THEN
              Output.WriteChar (Line[i]);
             ELSE 
              i := i + 1;
              CASE SYSTEM.UCASE (Line[i]) OF
                'H':
                  IF ModSet = VOID THEN
                    ModSet := Class.Module.TransitiveClosure;
                    IF ModSet = VOID THEN
                      ModSet.CREATE;
                      END;
                    ModSet.Add (Class.Module);
                    END;
                  p := ModSet.Row;
                  FOR Pass := 1 TO 2 DO
                    FOR j := 0 TO p.SIZE - 1 DO
                      WHAT p[j].Gc OF
                        IN CDefModuleCodeGenerator:
                          TAG.Ctx.SetOutput(Output);
                          TAG.GenerateInclude (Module, Pass);
                          TAG.Ctx.SetOutput(VOID);
                          END;
                        END;
                      END;
                    END;
                  END;
                'D':
                  Output.DualInstance (ModName, ClassName);
                  END;
                'I':
                  Output.ModuleInitialization (ModName);
                  END;
                'M':
                  Output.WriteString (ModName);
                  END;
                'C':
                  Output.CreateAnchor (ModName, ClassName);
                  END;
               ELSE
                StdErr.WriteLine ('Unrecognized code in :' + Line);
                END;
              END;
            i := i + 1;
            END;
          Output.WriteLn;
          END;
        Output.Close;
        END; 
      CurrentSpot.PopCurrentModule;
      END GenerateStub;
-----------------------------------------------
      METHOD GenerateArfFile (Prj: ProjectFile);
        VAR
          a: ARRAY OF CHAR;
          Output: OutputStream;
          PrjMod: ProjectModule;
          ModList: List(ProjectModule);
        BEGIN
        a := Prj.LinkerResponseFileName;
        IF YaflCfg.VerboseLevel > 0 THEN
          l("Generating "+ a);
          END;
        Output.CREATE;
        Output.Create (a, Stream.WriteAccess);
        Mapper.Associate ("E", YaflCfg.ObjExt);
        IF Output.ErrorCode = Output.NoError THEN
          ModList := Prj.Modules;
          FOR i := 0 TO ModList.Size - 1 DO
            PrjMod := ModList.Get(i);
            PrjMod.SetPatterns (Mapper);
            IF i = 0 THEN
              Mapper.WriteMultiLineMap (Output, "ARF.FIRSTOBJ", "ARF.OBJ");
             ELSIF i = ModList.Size - 1 THEN
              Mapper.WriteMultiLineMap (Output, "ARF.LASTOBJ", "ARF.OBJ");
             ELSE
              Mapper.WriteMultiLineMap (Output, "ARF.OBJ", VOID);
              END;                                              
            END;
          Output.Close;
         ELSE
          StdErr.WriteLine ('Cannot open file ' + a + ' for output');
          END;
        END GenerateArfFile;
        
    METHOD CheckClass (Cl: ClassDefinition): BOOLEAN;
      VAR
        CreateMeth: MethodDeclaration;
      BEGIN
      IF Cl.Once THEN
        StdErr.WriteLine ("The ROOT class should not be ONCE");
       ELSIF Cl.ClassFormals <> VOID THEN
        StdErr.WriteLine ("The ROOT class should definitely not be " +
                          "parameterized");
       ELSE
        CreateMeth := Cl.Create;
        IF CreateMeth.Class <> Cl THEN
          StdErr.WriteLine ("The ROOT class should define its own CREATE " +
                            "method");
         ELSE
          IF CreateMeth.Arity <> 0 THEN
            StdErr.WriteLine ("The ROOT CREATE method should be parameterless");
           ELSE
            RESULT := TRUE;
            END; 
          END;                            
        END;
      END CheckClass;
      
    METHOD FindClass (DefModule: DefinitionModule;
                      ClassName: ARRAY OF CHAR): ClassDeclaration;
      BEGIN
      RESULT := DefModule.GetClass (ClassName);
      FOR i := 0 TO DefModule.Classes.Size - 1 WHILE RESULT = VOID DO
        IF String.Equals (String.UpperCase (ClassName),
                          String.UpperCase (DefModule.Classes.Get(i).Id.Data))
           THEN
          RESULT := DefModule.Classes.Get(i);
          END;                    
        END;
      END FindClass;                  
      
    METHOD CheckModule (DefModule: DefinitionModule;
                        ClassName: ARRAY OF CHAR): ClassDefinition;
      VAR
        p: NTList(ClassDefinition);                        
        ClassDecl: ClassDeclaration;
      BEGIN
      IF ClassName <> VOID THEN
        ClassDecl := FindClass (DefModule, Space.StoreString(ClassName));
        IF ClassDecl = VOID THEN
          StdErr.WriteLine ("Cannot find class " + ClassName);
         ELSE
          WHAT ClassDecl OF
            IN ClassDefinition:
              IF CheckClass (TAG) THEN
                RESULT := TAG;
                END;
              END;
            END;
          END;
       ELSE      
        p := DefModule.Classes; 
        IF p.Size = 1 THEN
          IF CheckClass (p.Get(0)) THEN
            RESULT := p.Get(0);
            END;
         ELSE
          StdErr.WriteLine ("More or less than a single public " +
                            "class defined in " + DefModule.Id.Data);
          END;
        END;
      END CheckModule;                        


    REDEFINE METHOD Help;
      BEGIN
      BASE;
l("");
l("-p<file>: Use <file> as project file");
l("");
l("                         File generation flags");
l("-yr<module>: Use <module> as root module");
l("");
l("                           Execution flags");
l("-yo<file>: Use <file> as output of the execution commands, instead");
l("           of executing directly from within YM. Default is StdOut");
l("-yf(y|c|l): Force the compilation at the YAFL | C | Link level");
l("-yi(y|c|l): Ignore file timestamps at the YAFL | C | Link level");
l("-yir: Ignore the compilation return codes");
l("-yd: Only consider direct dependencies");
l("-ye: Explain why each module is being recompiled");
l("-yg: Only compile when target file is missing");
l("-yx: Use external compiler for the YAFL compilations");
l("-yt: Delete target files before proceeding");
      END Help;

     METHOD ConsiderExecution (Image: ARRAY OF ARRAY OF CHAR): BOOLEAN;
       VAR
          Res: INTEGER;                                       
         Command: ARRAY OF CHAR;
         IgnoreResult: BOOLEAN;
       BEGIN                       
       RESULT := TRUE;
       IF ExecutionFile <> VOID THEN
         FOR i := 0 TO Image.SIZE - 1 DO
           ExecutionFile.WriteLine (Image[i]);
           END;                
        ELSE               
         FOR i := 0 TO Image.SIZE - 1 DO
           Command := Image[i];
           IF Command[0] = '-' THEN
             IgnoreResult := TRUE;
             Command := Command.SLICE (1, Command.SIZE - 1);
            ELSE
             IgnoreResult := FALSE;
             END;                         
           IF YaflCfg.VerboseLevel > 0 THEN
             l("Executing: " + Command);
             END;
           l(VOID);
           Res := SYSTEM.Execute (Command);
           IF YaflCfg.VerboseLevel > 0 THEN
             l("Returning: "  + ToString(Res));
             END;
           IF NOT IgnoreResult THEN
             RESULT := RESULT AND (Res = 0);
             END;
           END;                       
         END;
       END ConsiderExecution;
       
     METHOD CompileYafl (Prj: ProjectFile;
                         TheList: List(ProjectModule)): BOOLEAN;
       VAR    
         Continue : BOOLEAN;
         CompRun: CompilationRun;                        
         p: ARRAY OF ARRAY OF CHAR;
       BEGIN   
       RESULT := TRUE;
       Mapper.Associate ('X', ToString (TheList.Size));
       Mapper.Associate ('E', YaflCfg.ImpExt);
       IF ExternalExecution THEN
         Continue := TRUE;                   
         FOR i := 0 TO TheList.Size - 1 WHILE (Continue) DO 
           YaflCfg.GetController.Capture (Controller.Yafl);
           
           TheList.Get(i).SetPatterns (Mapper);
           Mapper.Associate ('I', ToString (i));
           IF i = 0 THEN                        
             p := Mapper.MapMultiLine ("MAKE.FIRSTYAFL", "MAKE.YAFL");                          
            ELSIF i = TheList.Size - 1 THEN
             p := Mapper.MapMultiLine ("MAKE.LASTYAFL", "MAKE.YAFL");                           
            ELSE 
             p := Mapper.MapMultiLine ("MAKE.YAFL", VOID);                              
             END;                                       
           IF p <> VOID THEN         
             RESULT := RESULT AND ConsiderExecution (p);
             Continue := NOT YaflCfg.Interrupted AND 
                         (RESULT OR PleaseIgnoreReturnCodes);
             END;
           END;                   
        ELSE -- Internal execution
         Continue := TRUE;
         FOR i := 0 TO TheList.Size - 1 WHILE (Continue) DO
           l(ToString (i+1) + '/' + ToString(TheList.Size) + ":" +
             TheList.Get(i).Get);
           ----------------------------------------
           YaflCfg.GetController.Capture (Controller.Yafl);
           
           CompRun := Compile (TheList.Get(i).Get, FALSE);
           Account (CompRun);
           RESULT := RESULT AND CompRun.Ok;
           Continue := NOT YaflCfg.Interrupted AND
                       (PleaseIgnoreReturnCodes OR CompRun.Ok);
           CompRun := VOID;
           END;        
         END;
       END CompileYafl;                  

     METHOD CompileModuleC (CommandLine : ARRAY OF ARRAY OF CHAR;
                            PrjModule   : ProjectModule) : BOOLEAN;
       BEGIN          
       IF CommandLine <> VOID THEN
         RESULT := ConsiderExecution (CommandLine);
         END; -- IF
       END CompileModuleC;                     

     ------------------------------------------------------------       
     METHOD CompileC (Prj: ProjectFile;
                      TheList: List(ProjectModule)): BOOLEAN;
       VAR              
         Continue : BOOLEAN;
         p: ARRAY OF ARRAY OF CHAR;
       BEGIN          
       RESULT := TRUE;
       Mapper.Associate ('E', YaflCfg.IntermediateExt);
       Mapper.Associate ('W', YaflCfg.DirPrefix);
       Mapper.Associate ('X', ToString(TheList.Size));
       Continue := TRUE;
       FOR i := 0 TO TheList.Size - 1 WHILE Continue DO                             
         YaflCfg.GetController.Capture (Controller.C);
         
         TheList.Get(i).SetPatterns (Mapper);
         Mapper.Associate ('I', ToString (i+1));
         IF i = 0 THEN                                        
           p := Mapper.MapMultiLine ("MAKE.FIRSTC", "MAKE.C");                          
          ELSIF i = TheList.Size - 1 THEN
           p := Mapper.MapMultiLine ("MAKE.LASTC", "MAKE.C");                           
          ELSE 
           p := Mapper.MapMultiLine ("MAKE.C", VOID);                           
           END; -- IF                                
         RESULT := RESULT AND CompileModuleC (p, TheList.Get (i));
         Continue := (NOT YaflCfg.Interrupted) AND 
                     (RESULT OR PleaseIgnoreReturnCodes);
         END; -- FOR       
       END CompileC;                     
     ------------------------------------------------------------       
     METHOD DeleteTargetFiles (TheList: List(ProjectModule);
                               Yafl: BOOLEAN);
       VAR
         PMod: ProjectModule;              
         
         METHOD TryDelete (FName: ARRAY OF CHAR);
           BEGIN         
           IF YaflCfg.VerboseLevel > 0 THEN
             StdOut.WriteLine ("Deleting : " + FName);
             END;                                     
           Operations.Delete (FName);
           END TryDelete;
         
       BEGIN                 
       FOR i := 0 TO TheList.Size - 1 DO
         PMod := TheList.Get(i);
         IF Yafl THEN
           TryDelete (PMod.FileName (PMod.CFile));
           TryDelete (PMod.FileName (PMod.H1File));
           TryDelete (PMod.FileName (PMod.H2File));
           END;
         TryDelete (PMod.FileName (PMod.ObjFile));
         END;
       END DeleteTargetFiles;
     ------------------------------------------------------------       
     METHOD Link (Prj: ProjectFile): BOOLEAN;
       VAR                                              
         Continue : BOOLEAN;
         BigImage,
         p: ARRAY OF ARRAY OF CHAR;
         BEGIN   
       
       YaflCfg.GetController.Capture (Controller.LinkPrepare);
       
       GenerateArfFile(Prj);
       Mapper.Associate ("E", YaflCfg.ObjExt);
       Continue := TRUE;
       FOR i := 0 TO Prj.Modules.Size - 1 WHILE Continue DO
         Prj.Modules.Get(i).SetPatterns (Mapper);
         IF i = 0 THEN                  
           p := Mapper.MapMultiLine ("MAKE.FIRSTLINK", "MAKE.LINK");                            
          ELSIF i = Prj.Modules.Size - 1 THEN
           p := Mapper.MapMultiLine ("MAKE.LASTLINK", "MAKE.LINK");                             
          ELSE 
           p := Mapper.MapMultiLine ("MAKE.LINK", VOID);                                
           END;                                 
         BigImage := BigImage + p;
         END;

       YaflCfg.GetController.Capture (Controller.Link);

       IF NOT YaflCfg.Interrupted THEN
         RESULT := (BigImage = VOID) OR ConsiderExecution (BigImage);
         YaflCfg.GetController.Capture (Controller.LinkDone);
         END;
       END Link; 
       
     METHOD CompileStub (Prj: ProjectFile): BOOLEAN;
       VAR
         p: ARRAY OF ARRAY OF CHAR;
       BEGIN
       Mapper.Associate ('I', "0");
       Prj.RootModule.SetPatterns (Mapper);
       Mapper.Associate ('R', Prj.RootClass);
       p := Mapper.MapMultiLine ("MAKE.C", VOID);                               
       RESULT := (p = VOID) OR ConsiderExecution(p);       
       END CompileStub;

     METHOD ExecutePrj (Prj: ProjectFile): BOOLEAN;                       
       VAR
         ToCompile: List(ProjectModule);
         SomethingHappened: BOOLEAN;
     
       METHOD DoPass (Yafl, PleaseForce: BOOLEAN): List(ProjectModule);
         VAR
           TheList: List(ProjectModule);
           Offending, PMod: ProjectModule;
           BEGIN
         RESULT.CREATE;
         TheList := Prj.Modules;
         FOR i := 0 TO TheList.Size - 1 DO
           PMod := TheList.Get(i);
           IF AllWorlds OR (PMod.World = Prj.RootModule.World) THEN
             IF PleaseForce THEN
               RESULT.Append (PMod);
              ELSE               
               Offending := PMod.NeedsRecompilation(Yafl,
                                                    MissingTargetFilesOnly);
               IF Offending <> VOID THEN
                 IF (Offending = PMod) OR NOT DirectDependenciesOnly THEN
                   RESULT.Append (PMod);
                   IF PleaseExplain THEN
                     l ("Compiling " + PMod.Get + 
                                       " because of " + Offending.Get);
                     END;
                   END;
                 END;
               END;
             END;
           END;
         END DoPass;              
         
       BEGIN                                   
       Prj.ClearTimeStamps;
       Prj.SetPatterns (Mapper);
       RESULT := TRUE;          
       IF YaflLevel <> Ignore THEN
         ToCompile := DoPass (Yafl := TRUE,
                              PleaseForce := YaflLevel = Force);     
         IF PleaseDeleteTargetFlag THEN
           DeleteTargetFiles (ToCompile, Yafl := TRUE);
           END;                              
         RESULT := CompileYafl (Prj, ToCompile);                              
         SomethingHappened := TRUE;
         END;
       IF RESULT AND (NOT YaflCfg.Interrupted) THEN
         Prj.ClearTimeStamps;
         IF CLevel <> Ignore THEN
           ToCompile := DoPass (Yafl := FALSE,
                                PleaseForce := CLevel = Force);      
           IF PleaseDeleteTargetFlag THEN
             DeleteTargetFiles (ToCompile, Yafl := FALSE);
             END;                              
           RESULT := CompileC(Prj, ToCompile);                          
           SomethingHappened := TRUE;
           IF (RESULT OR PleaseIgnoreReturnCodes) AND 
              ((CLevel = Force) OR 
                 Prj.NeedsStubRecompilation(MissingTargetFilesOnly)) THEN
             RESULT := RESULT AND CompileStub (Prj);
             END;
           END;       
         IF RESULT AND (NOT YaflCfg.Interrupted) THEN
           IF (LinkLevel = Force) OR 
              ((LinkLevel <> Ignore) AND SomethingHappened) THEN
             RESULT := Link(Prj);
             END;
           END;
         END;
       END ExecutePrj;

     METHOD MissingTargetFilesOnly : BOOLEAN;
       BEGIN                 
       RESULT := MissingTargetFilesOnlyFlag;
       END MissingTargetFilesOnly;  

     METHOD SetMissingTargetFilesOnly (Value : BOOLEAN);
       BEGIN  
       MissingTargetFilesOnlyFlag := Value;
       END SetMissingTargetFilesOnly;   
                                             
     METHOD DirectDependenciesOnly : BOOLEAN;
       BEGIN                 
       RESULT := DirectDependenciesOnlyFlag;
       END DirectDependenciesOnly;  

     METHOD SetDirectDependenciesOnly (Value : BOOLEAN);
       BEGIN  
       DirectDependenciesOnlyFlag := Value;
       END SetDirectDependenciesOnly;   
       
     METHOD AllWorlds : BOOLEAN;
       BEGIN         
       RESULT := AllWorldsFlag;
       END AllWorlds;
       
     METHOD SetAllWorlds (Value : BOOLEAN);
       BEGIN            
       AllWorldsFlag := Value;
       END SetAllWorlds;    
         
     METHOD PleaseExplain : BOOLEAN;
       BEGIN         
       RESULT := PleaseExplainFlag;
       END PleaseExplain;
       
     METHOD SetPleaseExplain (Value : BOOLEAN);
       BEGIN            
       PleaseExplainFlag := Value;
       END SetPleaseExplain;    

     METHOD SetPleaseDeleteTarget (Value : BOOLEAN);
       BEGIN            
       PleaseDeleteTargetFlag := Value;
       END SetPleaseDeleteTarget;    

     METHOD PleaseIgnoreReturnCodes : BOOLEAN;
       BEGIN
       RESULT := PleaseIgnoreReturnCodesFlag;
       END PleaseIgnoreReturnCodes;
       
     METHOD SetPleaseIgnoreReturnCodes (Value : BOOLEAN);
       BEGIN
       PleaseIgnoreReturnCodesFlag := Value;
       END SetPleaseIgnoreReturnCodes;

     METHOD ExternalExecution : BOOLEAN;
       BEGIN         
       RESULT := ExternalExecutionFlag;
       END ExternalExecution;
       
     METHOD SetExternalExecution (Value : BOOLEAN);
       BEGIN            
       ExternalExecutionFlag := Value;
       END SetExternalExecution;    
                             
     METHOD RootModule : ARRAY OF CHAR;
       BEGIN          
       RESULT := CurrentRootModule;
       END RootModule;  
       
     METHOD SetRootModule (Name : ARRAY OF CHAR);
       BEGIN             
       CurrentRootModule := Name;
       END SetRootModule;        
       
     METHOD PrjName : ARRAY OF CHAR;
       BEGIN       
       RESULT := CurrentPrjName;
       END PrjName;             
      
     METHOD SetPrjName (Name : ARRAY OF CHAR);
       BEGIN          
       CurrentPrjName := Name;
       END SetPrjName;   
                       
     METHOD ExecutionFileName : ARRAY OF CHAR;
       BEGIN                 
       RESULT := CurrentExecutionFileName;
       END ExecutionFileName;  
                        
     METHOD SetExecutionFileName (Name : ARRAY OF CHAR);
       BEGIN
       CurrentExecutionFileName := Name;
       END SetExecutionFileName;
                
     METHOD CLevel : INTEGER;
       BEGIN      
       RESULT := CurrentCLevel;
       END CLevel;     
       
     METHOD SetCLevel (Value : INTEGER);
       BEGIN         
       CurrentCLevel := Value;
       END SetCLevel;         
     
     METHOD YaflLevel : INTEGER;
       BEGIN      
       RESULT := CurrentYaflLevel;
       END YaflLevel;     
       
     METHOD SetYaflLevel (Value : INTEGER);
       BEGIN         
       CurrentYaflLevel := Value;
       END SetYaflLevel;              
         
     METHOD LinkLevel : INTEGER;
       BEGIN      
       RESULT := CurrentLinkLevel;
       END LinkLevel;     
       
     METHOD SetLinkLevel (Value : INTEGER);
       BEGIN         
       CurrentLinkLevel := Value;
       END SetLinkLevel;              

     METHOD YOption (Opt: ARRAY OF CHAR): BOOLEAN;
       BEGIN
       RESULT := TRUE;
       ASSERT Opt <> VOID;
       ASSERT Opt.SIZE > 0;
       ASSERT Opt[0] = '-';
       ASSERT SYSTEM.UCASE(Opt[1]) = 'Y';
       IF Opt.SIZE > 2 THEN
         CASE SYSTEM.UCASE(Opt[2]) OF
           'G':
             SetMissingTargetFilesOnly (TRUE);
             END;
           'A':
             SetAllWorlds (TRUE);
             END;
           'T':
             SetPleaseDeleteTarget (TRUE);
             END;
           'D':
             SetDirectDependenciesOnly (TRUE);
             END;
           'E':
             SetPleaseExplain (TRUE);
             END;
           'X':
             SetExternalExecution (TRUE);
             END;
           'R':
             SetRootModule (Opt.SLICE (3, Opt.SIZE -3));
             END;
           'O':
             IF Opt.SIZE = 3 THEN
               ExecutionFile := StdOut;
              ELSE
               SetExecutionFileName (Opt.SLICE (3, Opt.SIZE -3));
               END;
             END;
           'F':
             IF Opt.SIZE > 3 THEN
               CASE SYSTEM.UCASE(Opt[3]) OF
                 'C':
                   SetCLevel (Force);
                   END;
                 'Y':
                   SetYaflLevel (Force);
                   END;
                 'L':
                   SetLinkLevel (Force);
                   END;
                ELSE
                 RESULT := FALSE;
                 END;
              ELSE
               RESULT := FALSE;
               END;
             END;
           'I':
             IF Opt.SIZE > 3 THEN
               CASE SYSTEM.UCASE(Opt[3]) OF
                 'C':
                   SetCLevel (Ignore);
                   END;
                 'Y':
                   SetYaflLevel (Ignore);
                   END;
                 'L':
                   SetLinkLevel (Ignore);
                   END;
                 'R':
                   SetPleaseIgnoreReturnCodes (TRUE);
                   END;
                ELSE
                 RESULT := FALSE;
                 END;
              ELSE
               RESULT := FALSE;
               END;
             END;
          ELSE
           RESULT := FALSE;
           END;
         END;
       END YOption;
       
     REDEFINE METHOD ProcessOption (Opt: ARRAY OF CHAR): BOOLEAN;
       BEGIN
       ASSERT Opt <> VOID;
       ASSERT Opt.SIZE > 0;
       ASSERT Opt[0] = '-';
       RESULT := BASE(Opt);
       IF NOT RESULT THEN
         RESULT := TRUE;
         CASE SYSTEM.UCASE(Opt[1]) OF
           'Y':
             RESULT := YOption (Opt);
             END;
           'P':   
             SetPrjName (Opt.SLICE (2, Opt.SIZE - 2));
             END;
          ELSE
           RESULT := FALSE;
           END;
         END;
       END ProcessOption;

     METHOD BuildPrj (Prj: ProjectFile;
                      RootModule: ARRAY OF CHAR);
       VAR
         Class: ClassDefinition;
         Module: ImplementationModule;
         ModArr: ARRAY OF DefinitionModule;
         PrjMod: ProjectModule;
       BEGIN             
       Prj.UseWorlds (WorldList.Row);
       ASSERT RootModule <> VOID;
       Module := ModuleTable.LocateImplementationModule (RootModule);
       IF Module = VOID THEN
         StdErr.WriteLine ("Cannot open module " + RootModule);
        ELSE
         Class := CheckModule (Module.DefModule, Prj.RootClass);
         IF Class <> VOID THEN
           Prj.SetRootClass (Class.Id.Data);
           ModArr := Module.ProjectClosure.Row;
           FOR i := 0 TO ModArr.SIZE - 1 DO
             PrjMod.CREATE (Prj);
             PrjMod.SetTrueModule (ModArr[i]);
             Prj.Modules.Append (PrjMod);
             END;
           FOR i := 0 TO Prj.Modules.Size - 1 DO
             Prj.Modules.Get(i).SetDependencies;             
             END;
           PrjMod := Prj.GetModule (Module.Id.Data);
           IF PrjMod <> VOID THEN
             Prj.SetRootModule (PrjMod);
             END;
           Prj.SetPatterns (Mapper);
           GenerateArfFile(Prj);
           GenerateStub (Prj, Module, Class);
           VOID := Prj.Save;

           IF YaflCfg.VerboseLevel > 1 THEN
             Prj.Dump;
             END; -- IF
             
           END;              
         END;
       END BuildPrj;      
       
     METHOD GlobalOptimization (Prj: ProjectFile):BOOLEAN;
       --------------------------------
       -- Visit the parse tree :
       --   * Mark unused classes and methods
       --   * Visit the method calls
       --------------------------------
       METHOD Visit(Prj: ProjectFile);
         VAR
           Pass, OldCardinality : INTEGER;
                               
           METHOD Dump (Pass : INTEGER);
             VAR
               a: ARRAY OF CHAR;
             BEGIN
             a := IntConversions.IntToString (Pass, 0);
             ClassDictionary.Dump (YaflCfg.NameMapper.
                                      BuildTargetFileName("","clas", a, "storm"));
             EntryDictionary.Dump (YaflCfg.NameMapper.
                                      BuildTargetFileName("","entr", a, "storm"));
             CallDictionary.Dump (YaflCfg.NameMapper.
                                      BuildTargetFileName("","call", a, "storm"));
             MethodDictionary.Dump (YaflCfg.NameMapper.
                                      BuildTargetFileName("","meth", a, "storm"));
             END Dump;
           
           METHOD DoVisit;
             BEGIN
             CallDictionary.Clear;
             EntryDictionary.Clear;
             MethodDictionary.Clear;
             ClassDictionary.Clear;
             
             Prj.VisitParseTree;         
             
             CallDictionary.RemoveUseless;
             EntryDictionary.RemoveUseless;
             MethodDictionary.RemoveUseless;
             ClassDictionary.RemoveUseless;
             
             Dump(Pass);
             Pass := Pass + 1;
             END DoVisit;    
             
         BEGIN            
         OldCardinality := -1;                     
         DoVisit;
         WHILE (OldCardinality <> MethodDictionary.UsefulCount) DO
           OldCardinality := MethodDictionary.UsefulCount;
           DoVisit;
           END; -- While
         ClassDictionary.PropagateUseful;
         MethodDictionary.MarkCallReferences;
         CallDictionary.FindAllocating;
         CallDictionary.InitMonomorphableFlag;
         Dump(Pass);
         END Visit;
         
       METHOD ShowResults : BOOLEAN;
         BEGIN           
         RESULT := TRUE;
         IF YaflCfg.StatsStream <> VOID THEN
           ClassDictionary.Stats (YaflCfg.StatsStream);
           MethodDictionary.Stats (YaflCfg.StatsStream);
           EntryDictionary.Stats (YaflCfg.StatsStream);
           CallDictionary.Stats (YaflCfg.StatsStream);
           END;
         END ShowResults;

       METHOD Error (Message: ARRAY OF CHAR);
         BEGIN
         MainErrorHandler.EmitError (Message);
         END Error;
      
      VAR
        GenerateCodeFlag, GenerateMetricsFlag : BOOLEAN;
          
      BEGIN         
      RESULT := TRUE;
      Prj.ClearTimeStamps;
      Prj.SetPatterns (GetMapper);   
      YaflCfg.SetDirPrefix("storm");
      YaflCfg.SetCheckVersionLevel (0);
      
      -- Assume no code is generated 
      GenerateCodeFlag := YaflCfg.PleaseGenerateCode;
      GenerateMetricsFlag := YaflCfg.PleaseGenerateMetrics;
      YaflCfg.SetGenerateCode (FALSE);
      YaflCfg.SetGenerateMetrics (FALSE);

      RESULT := Prj.EnterInDictionary (FrontEnd := THIS);
      IF NOT RESULT THEN
        Error ("Building tables phase aborted");
        END;          
      Visit(Prj);
      
      CleanUpMemory;
      
      YaflCfg.SetDebugMode (Value := FALSE);
      YaflCfg.SetTraceModeLevel (Value := 0);
      
      RESULT := RESULT AND (YaflLevel <> Ignore) AND 
                                     Prj.GenerateGlobalOptimizedCode (THIS);
      IF NOT RESULT THEN
        Error ("C code not generated");
        END;
              
      RESULT := RESULT AND ShowResults;
      IF NOT RESULT THEN
        Error ("No results available");
        END;
        
      RESULT := RESULT AND (CLevel<>Ignore) AND CompileStub(Prj);
      IF NOT RESULT THEN
        Error ("Stub compilation phase aborted");
        END;                             
        
      RESULT := RESULT AND (CLevel <> Ignore) AND CompileC(Prj, Prj.Modules);
      IF NOT RESULT THEN 
        Error ("C compilation phase aborted");
        END;
        
      RESULT := RESULT AND (LinkLevel <> Ignore) AND Link(Prj);
      IF NOT RESULT THEN
        Error ("Link phase aborted");
        END;
                  
      -- Restores YaflCfg
      YaflCfg.SetGenerateCode (GenerateCodeFlag);
      YaflCfg.SetGenerateMetrics (GenerateMetricsFlag);
      
    END GlobalOptimization;
       
     METHOD Account (CompRun: CompilationRun);
       BEGIN
       GeneratedLines := GeneratedLines + CompRun.GeneratedLines;
       ParsedLines := ParsedLines + CompRun.ParsedLines;
       END Account;
       
     METHOD ProcessArgument (Arg: ARRAY OF CHAR): BOOLEAN;
       VAR
         CompRun: CompilationRun;
       BEGIN
       RESULT := TRUE;
       IF (Arg <> VOID) AND (SYSTEM.OperatingSystem = SYSTEM.Mvs) AND
          (Arg.SIZE > 0) AND (Arg[0] = '/') THEN
         Arg[0] := '-';
         END;
       IF (Arg <> VOID) AND (Arg.SIZE > 0) THEN
         CASE Arg[0] OF
           '@':
             CompileBatch (Arg.SLICE (1, Arg.SIZE - 1));
             END;
           '?':
             Help;
             RESULT := FALSE;
             END;
           '-':
             IF (Arg.SIZE > 1) AND (SYSTEM.UCASE(Arg[1]) = 'H') OR
                (SYSTEM.UCASE(Arg[1]) = '?') THEN
               Help;
               RESULT := FALSE;
              ELSIF NOT ProcessOption(Arg) THEN
               MainErrorHandler.SetExitCode (MainErrorHandler.BadOption);
               IF NOT YaflCfg.SilentMode THEN
                 l ("Unrecognized option: " + Arg);
                 END;
               END;
             END;
          ELSE 
           CompRun := Compile(Arg, FALSE);
           Account (CompRun);
           RESULT := TRUE;
           END;
         END;
       END ProcessArgument;

     METHOD CompileBatch (FName: ARRAY OF CHAR);
       VAR
         Input: InputStream;
         a: ARRAY OF CHAR;
       BEGIN
       Input.CREATE;
       Input.Open (FName, Stream.ReadAccess);
       IF Input.ErrorCode = Stream.NoError THEN
         WHILE NOT Input.Eof DO
           a := String.Strip(Input.ReadLine);
           IF a.SIZE > 0 THEN
             VOID := ProcessArgument (a);
             END;
           END;
        ELSE
         MainErrorHandler.SetExitCode (MainErrorHandler.NoIndirectFile);
         IF NOT YaflCfg.SilentMode THEN
           l("Cannot open: " + FName);
           END;
         END;
       END CompileBatch;         
               
     METHOD LoadProject (Prj: ProjectFile): BOOLEAN;
       VAR
         Input: InputStream;
         
         METHOD Try (FName: ARRAY OF CHAR): BOOLEAN;
           BEGIN   
           Input.Open (FName, Input.ReadAccess);
           RESULT := Input.ErrorCode = Input.NoError;
           END Try;                                  
           
       BEGIN
       Input.CREATE;
       IF Try (YaflWorlds.DefaultWorld.BuildProjectFName(PrjName, 
                                                     YaflCfg.ProjectFileExt)) OR
            Try (PrjName) THEN
         RESULT := Prj.Parse (Input);
         Input.Close;
         END;
       END LoadProject;

     METHOD ProcessArguments : BOOLEAN;
       BEGIN   
       RESULT := TRUE;
       FOR i := 0 TO SYSTEM.ArgCount - 1 WHILE RESULT DO
         RESULT := ProcessArgument (SYSTEM.Argument (i));
         END;                         
       END ProcessArguments;
                        
     METHOD CompilePrj (Prj : ProjectFile) : BOOLEAN;
       BEGIN
       IF ExecutionFileName <> VOID THEN
         ExecutionFile.CREATE;
         ExecutionFile.Create (ExecutionFileName, 
                               ExecutionFile.WriteAccess);
         IF ExecutionFile.ErrorCode <> ExecutionFile.NoError THEN
           StdErr.WriteLine ("Cannot open file " + ExecutionFileName +
                             " for writing");
           ExecutionFile := VOID;
           END; -- IF
         END; -- IF
       IF YaflCfg.PleaseGlobalOptimize THEN
         RESULT := GlobalOptimization (Prj);
        ELSE
         RESULT := ExecutePrj(Prj);
         END; -- IF
       IF (ExecutionFile <> VOID) AND 
          (ExecutionFile <> StdOut) THEN
         ExecutionFile.Close;
         END; -- IF
       END CompilePrj;  
       
     METHOD Process;
       VAR 
         Prj : ProjectFile;
       BEGIN  
       IF PrjName <> VOID THEN     
         Prj.CREATE (PrjName);
         IF RootModule <> VOID THEN
           BuildPrj(Prj, RootModule); 
          ELSE
           IF LoadProject (Prj) THEN
             IF CompilePrj (Prj) THEN  
               l ("Completed without error");
               END; -- IF  
            ELSE
             StdErr.WriteLine ("Cannot load project file");
             END; -- IF     
           END; -- IF  
         END; -- IF  
       END Process;        
       
     METHOD NoArguments;
       BEGIN    
       l ("Type yafl -h for information on supported options");
       SYSTEM.HALT;
       END NoArguments;
     
     REDEFINE METHOD CREATE;
      BEGIN
      StartInstant.CREATE (Today.Instant, Now.Instant);

      IF SYSTEM.ArgCount = 0 THEN
        NoArguments;
        END; -- IF
        
      Mapper.CREATE;
      Mapper.Associate ('W', YaflCfg.DirPrefix);
      IF ProcessArguments THEN 
        Process;
        END; -- IF
        
      IF (YaflCfg.StatLevel > 0) AND 
         (NOT YaflCfg.SilentMode) THEN

        StopInstant.CREATE (Today.Instant, Now.Instant);
        l (ToString (Space.Stored) + " symbols stored");
        l (ToString (Space.Collisions) + " collisions");
        l (ToString (ParsedLines) + " lines parsed");
        l (ToString (GeneratedLines) + " lines generated");
        l (ToString (StartInstant.Interval (StopInstant)) + " seconds");

        IF YaflCfg.StatLevel > 1 THEN
          YaflCfg.GetController.Report;
          END; -- IF
        END; -- IF
      END CREATE;
    
  END Yc;
  
  CLASS DummyCompilationRun;
    INHERITS CompilationRun;

    REDEFINE METHOD CREATE;
      BEGIN
      END CREATE;
  END DummyCompilationRun;    
END Ycc;
