home *** CD-ROM | disk | FTP | other *** search
/ Programmer's ROM - The Computer Language Library / programmersrom.iso / ada / tools / constrct.src < prev    next >
Encoding:
Text File  |  1988-05-03  |  143.7 KB  |  4,149 lines

  1. --::::::::::
  2. --CONSTRCT.PRO
  3. --::::::::::
  4.  
  5.  
  6.  
  7. -------- SIMTEL20 Ada Software Repository Prologue ------------
  8. --                                                           -*
  9. -- Unit name    : CONSTRUCT and CREATE_CO
  10. -- Version      : 841201
  11. -- Author       : Mitre Corp.
  12. -- DDN Address  : wis_ada at mitre
  13. -- Date created : 10 NOV 84
  14. -- Release date : 15 DEC 84
  15. -- Last update  : 21 JAN 85
  16. -- Machine/System Compiled/Run on : Intellimac 7000M
  17. --                                  UNIX
  18. --                                  Telesoft unvalidated
  19. --                                                           -*
  20. ---------------------------------------------------------------
  21. --                                                           -*
  22. -- Keywords     :  Automatic Compilation, Compilation Order,
  23. --                 Configuration Management, Dependency Graph
  24. --                 Scan Ada Source for withs and separates
  25. ----------------:
  26. --
  27. -- Abstract     :  
  28. --         The function of Construct is to perform the minimal number
  29. --    of system commands to bring a project up to date given that
  30. --    changes to project files have occurred.  If a project is already
  31. --    up to date, Construct will indicate this and no commands will be
  32. --    performed.  Construct can also be used to supply descriptive
  33. --    information in the form of dependency graphs and name lists of
  34. --    project files.
  35. --
  36. --         Create_CO reads a set of Ada source code files and creates
  37. --    a configuration object which describes the dependencies that
  38. --    exist among the files.  The configuration object is formatted so
  39. --    that it may be read by Construct.  In determining dependencies,
  40. --    Create_CO observes the filenaming conventions of the TeleSoft
  41. --    Ada compiler (i.e., filename extensions of .text, .sym, .code)
  42. --    and the compiler's language restriction that specifications and
  43. --    bodies of Ada packages reside in the same file.
  44. ----------------:  
  45. --                                                           -*
  46. ------------------ Revision history ---------------------------
  47. --                                                           -*
  48. -- DATE      VERSION AUTHOR        HISTORY
  49. -- 12/15/84  841201  Mitre Corp    Initial Release
  50. -- 01/21/85  841201  Mitre Corp    Converted subfile headers
  51. --                                 into Ada comments
  52. --                                                           -*
  53. ------------------ Distribution and Copyright -----------------
  54. --                                                           -*
  55. -- This prologue must be included in all copies of this software.
  56. --
  57. -- This software is released to the Public Domain (note:
  58. --   software released to the Public Domain is not subject
  59. --   to copyright protection).
  60. --
  61. -- Restrictions on use or distribution:  Although there are
  62. --      no current plans to provide maintenance for CONSTRUCT
  63. --      or CREATE_CO, we would appreciate your reporting
  64. --      problems and experiences to:
  65. --              
  66. --                wis_ada at mitre (net address)
  67. --
  68. --      or call at:
  69. --
  70. --                (703)  883-7697
  71. --                                                           -*
  72. ------------------ Disclaimer ---------------------------------
  73. --                                                           -*
  74. -- This software and its documentation are provided "AS IS" and
  75. -- without any expressed or implied warranties whatsoever.
  76. -- No warranties as to performance, merchantability, or fitness
  77. -- for a particular purpose exist.
  78. --
  79. -- Because of the diversity of conditions and hardware under
  80. -- which this software may be used, no warranty of fitness for
  81. -- a particular purpose is offered.  The user is advised to
  82. -- test the software thoroughly before relying on it.  The user
  83. -- must assume the entire risk and liability of using this
  84. -- software.
  85. --
  86. -- In no event shall any person or organization of people be
  87. -- held responsible for any direct, indirect, consequential
  88. -- or inconsequential damages or lost profits.
  89. --                                                           -*
  90. -------------------END-PROLOGUE--------------------------------
  91.  
  92. --::::::::::::::
  93. --arguments.text
  94. --::::::::::::::
  95. -- This package prompts for and validates all arguments entered by 
  96. -- the user (i.e., configuration object name, target name, and options).
  97. with STR_PACK;  use STR_PACK;
  98. with TEXT_IO;  use TEXT_IO;
  99. package ARGUMENTS is
  100.  
  101.   -- The CO_FILE is used by BLD_GRAPH to build the dependency tree.
  102.   -- This file is opened by ARGUMENTS in order to validate the CO
  103.   -- argument but is closed by BLD_GRAPH when processing is complete.
  104.   CO_FILE    : FILE_TYPE;
  105.  
  106.   -- TARGET_ARG is used by DISPLAY to print the graph and by CON_PROC
  107.   -- to determine necessary commands.  A list of targets may be included
  108.   -- in TARGET_ARG and separated by blanks.
  109.   TARGET_ARG : STR_PACK.DYNAMIC_STRING;
  110.   
  111.   -- The set of valid options include:
  112.   -- EXECUTE to actually execute commands, CMD_PRINT to print the required
  113.   -- commands, TOP_DOWN_GRAPH to print a textual top-down graph, BOTTOM_UP
  114.   -- to print a textual bottom-up graph, LIST to print lists of basic and
  115.   -- derived names in the CO_FILE, DUMP_TREE to debug the dependency tree,
  116.   -- and PROCESS_DUMP to debug the command processing.
  117.   type OPTION_TYPE is
  118.     (EXECUTE, CMD_PRINT, TOP_DOWN_GRAPH, BOTTOM_UP_GRAPH,  LIST,
  119.      DUMP_TREE, PROCESS_DUMP);
  120.  
  121.   -- Used by other packages to determine which options were set by the user.
  122.   function IS_SET (OPTION : OPTION_TYPE) return BOOLEAN;
  123.  
  124.   -- Called by CONSTRUCT to initiate the processing of arguments.
  125.   procedure PROMPT;
  126.  
  127. end ARGUMENTS;
  128.  
  129.  
  130.  
  131. with ENVIRONS;
  132. package body ARGUMENTS is
  133.  
  134.   -- Global data within package body:
  135.  
  136.   -- String types are needed for use with TEXT_IO (i.e., CO_STR and
  137.   -- TARGET_STR); otherwise, dynamic strings are used.
  138.   -- Options are manipulated as a table of boolean values.
  139.   CO_STR                 : STRING(1..ENVIRONS.MAX_FILENAME_LENGTH);
  140.   TARGET_STR             : STRING(1..ENVIRONS.MAX_LINE_LENGTH);
  141.   OPTION_TABLE           : array (OPTION_TYPE) of BOOLEAN; 
  142.  
  143.   -- The user is given 3 attempts to enter a correct argument
  144.   -- or '?' for help.
  145.   MAX_NUM_OF_ATTEMPTS    : constant POSITIVE := 3;
  146.   HELP_CHAR              : constant CHARACTER := '?';
  147.  
  148.  
  149.  
  150. -- Used to fill an entire string with blanks for initialization.
  151. -- Included because packed aggregates were not yet implemented
  152. -- in given compiler.
  153. procedure FILL_WITH_BLANKS(STR : in out STRING) is
  154. begin
  155.   for I in STR'RANGE loop
  156.     STR(I) := ' ';
  157.   end loop;
  158. end FILL_WITH_BLANKS;
  159.  
  160.  
  161. -- This procedure prompts for a configuration object and validates the
  162. -- user input.  The default CO is "co_file".  Invalid entries include
  163. -- (1) a list of COs, (2) a non-existent CO, including one which
  164. -- exceeds the maximum allowed length for the given system, and 
  165. -- (3) an inaccessible CO, for example one without read priviledge.  
  166. -- The user is given 3 trys to enter a correct CO.  After 3 incorrect
  167. -- trys the program aborts.
  168. procedure ENTER_CO is
  169.  
  170.   -- Used with GET_LINE to obtain user input.
  171.   RAW_INPUT_LINE    : STRING(1..ENVIRONS.MAX_LINE_LENGTH);
  172.   LAST              : NATURAL;
  173.  
  174.   -- DEFAULT_CO is assumed when a null or blank string is entered by user.
  175.   DEFAULT_CO        : constant STRING := "co_file";
  176.  
  177.   -- Used with STR_PACK routines to obtain token(s) entered by user.
  178.   CO_TEMP, CO_TOKEN : STR_PACK.DYNAMIC_STRING;
  179.   SEPARATOR         : CHARACTER;
  180.  
  181.   -- Flag used to determine whether successful before maximum number of
  182.   -- user attempts exceeded.  
  183.   SUCCESSFUL_INPUT  : BOOLEAN;
  184.  
  185.  
  186.  
  187.   -- This procedure opens the CO_FILE unless an empty string is passed in,
  188.   -- a string longer than the maximum filename allowed by the system is passed
  189.   -- in, or a name_error or use_error is raised by TEXT_IO.  Help information
  190.   -- is displayed if the HELP_CHAR is passed in. 
  191.   procedure OPEN_IF_VALID(STR : in STRING; SUCCESS : out BOOLEAN) is
  192.  
  193.   -- Displays help information to the user.
  194.   procedure WRITE_CO_HELP is
  195.   begin
  196.     PUT_LINE("****************  HELP INFORMATION  ****************");
  197.     PUT_LINE("Enter a single configuration object name or carriage");
  198.     PUT("return for the default which is "); PUT(DEFAULT_CO); 
  199.     PUT_LINE(".  Three ");
  200.     PUT_LINE("input attempts are given.");
  201.     PUT_LINE("****************************************************");
  202.   end WRITE_CO_HELP;
  203.  
  204.   begin   -- OPEN_IF_VALID
  205.     if STR'LENGTH = 0 then
  206.       -- There is an empty string here meaning the user entered
  207.       -- several separators with no tokens in between and this
  208.       -- is presumed to be a name_error.  
  209.       raise NAME_ERROR;
  210.     elsif STR'LENGTH > ENVIRONS.MAX_FILENAME_LENGTH then
  211.       -- There is certainly a name_error but it may not be caught
  212.       -- by TEXT_IO if the truncated string happens to be valid. 
  213.       raise NAME_ERROR;
  214.     -- Want to test for the HELP_CHAR but do not want to disallow a 
  215.     -- possible filename beginning with the HELP_CHAR.
  216.     elsif STR'LENGTH = 1 and STR(STR'FIRST) = HELP_CHAR then
  217.       WRITE_CO_HELP;
  218.       NEW_LINE;
  219.       SUCCESS := FALSE;
  220.     else
  221.       OPEN(CO_FILE, IN_FILE, STR);
  222.       SUCCESS := TRUE;
  223.     end if;  -- STR'LENGTH = 0 then ... elsif ... elsif ... else ...
  224.  
  225.   exception
  226.     when NAME_ERROR =>
  227.       PUT_LINE("*** Given CO does not exist ***");
  228.       NEW_LINE;
  229.       SUCCESS := FALSE;
  230.  
  231.     when USE_ERROR =>
  232.       PUT_LINE("*** Given CO is not accessible ***");
  233.       NEW_LINE;
  234.       SUCCESS := FALSE;
  235.  
  236.     when others =>
  237.       PUT_LINE("*** Unknown error while opening CO ***"); 
  238.       NEW_LINE;
  239.       SUCCESS := FALSE;
  240.   end OPEN_IF_VALID;
  241.  
  242.  
  243. begin  -- ENTER_CO
  244.   -- Allow maximum number of trys to enter correct CO.
  245.   for TRY in 1..MAX_NUM_OF_ATTEMPTS loop
  246.  
  247.     -- Prompt user and store input as dynamic string.
  248.     if TRY = 1 then PUT("CONFIGURATION OBJECT => ");
  249.     else PUT("RE-TRY => ");
  250.     end if;
  251.     GET_LINE(RAW_INPUT_LINE, LAST);
  252.     STR_PACK.CONVERT_TO_DYNAMIC
  253.       (RAW_INPUT_LINE(RAW_INPUT_LINE'FIRST..LAST), CO_TEMP);
  254.   
  255.     -- Validate user input.
  256.     -- STR_PACK.GET_TOKEN is used to eliminate extra blanks a user may 
  257.     -- have entered (e.g., a filename with leading blanks would not be
  258.     -- equivalent to the same filename w/out blanks and therefore may
  259.     -- not be valid).  This presumes that token separators used in 
  260.     -- GET_TOKEN may not appear in valid entries.  This is not 
  261.     -- necessarily a desirable restriction.
  262.     case STR_PACK.NUM_OF_TOKENS(CO_TEMP) is
  263.       when 0 =>
  264.         FILL_WITH_BLANKS(CO_STR);
  265.         CO_STR(CO_STR'FIRST..DEFAULT_CO'LENGTH) := DEFAULT_CO;
  266.         OPEN_IF_VALID(DEFAULT_CO, SUCCESSFUL_INPUT);
  267.       when 1 =>
  268.         STR_PACK.GET_TOKEN(CO_TEMP, CO_TOKEN, SEPARATOR);
  269.         FILL_WITH_BLANKS(CO_STR);
  270.         STR_PACK.CONVERT_TO_STRING(CO_TOKEN, CO_STR);
  271.         OPEN_IF_VALID
  272.           (CO_STR(CO_STR'FIRST..STR_PACK.LENGTH(CO_TOKEN)), SUCCESSFUL_INPUT);
  273.       when others =>
  274.         PUT_LINE("*** A list of COs is not allowed ***");
  275.         NEW_LINE;
  276.         SUCCESSFUL_INPUT := FALSE;
  277.     end case;   -- STR_PACK.NUM_OF_TOKENS(CO_TEMP) is
  278.  
  279.     -- Exit if valid CO entered before end of loop of allowed user 
  280.     -- input attempts.
  281.     if SUCCESSFUL_INPUT then 
  282.       if TRY > 1 then NEW_LINE;
  283.       end if;
  284.       exit;  -- for TRY in 1..MAX_NUM_OF_ATTEMPTS loop
  285.     end if;
  286.  
  287.   end loop;    -- for TRY in 1..MAX_NUM_OF_ATTEMPTS 
  288.  
  289.   -- Abort if valid CO never entered during loop of allowed user 
  290.   -- input attempts.
  291.   if not SUCCESSFUL_INPUT then
  292.     PUT_LINE("*** Number of input attempts exceeded ***");
  293.     raise ENVIRONS.ERROR;
  294.   end if;
  295.  
  296. end ENTER_CO;
  297.  
  298.  
  299.  
  300.  
  301. -- This procedure prompts for the targets argument and validates the
  302. -- user input.  The default targets list is indicated by assigning the
  303. -- the empty string.  The only invalid entry checked for is names 
  304. -- which exceed the maximum allowed length for the given system.
  305. -- For each target in the list, the user is given 3 trys to 
  306. -- enter a correct target.  If 3 trys are ever exceeded the program
  307. -- aborts. 
  308. procedure ENTER_TARGETS is
  309.  
  310.   -- Used with GET_LINE to obtain user input.
  311.   RAW_INPUT_LINE   : STRING(1..ENVIRONS.MAX_LINE_LENGTH);
  312.   RETRY_LINE       : STRING(1..ENVIRONS.MAX_LINE_LENGTH);
  313.   LAST             : NATURAL;
  314.  
  315.   -- Used with STR_PACK routines to obtain token(s) entered by user.
  316.   TARGET_TEMP_1    : STR_PACK.DYNAMIC_STRING;
  317.   TARGET_TEMP_2    : STR_PACK.DYNAMIC_STRING;
  318.   TARGET_TOKEN     : STR_PACK.DYNAMIC_STRING;
  319.   SEPARATOR        : CHARACTER;
  320.  
  321.   -- Flag used to determine whether successful before maximum number of
  322.   -- user attempts exceeded.
  323.   SUCCESSFUL_INPUT : BOOLEAN;
  324.  
  325.  
  326.   
  327.   -- This procedure appends another token to the list of targets
  328.   -- if the token passed in is valid.  The only invalid target at
  329.   -- this point is one longer than the maximum allowed filename 
  330.   -- length.  Other invalid targets (e.g., those which do not exist
  331.   -- or are inaccessible) are identified in CON_PROC.  In this
  332.   -- procedure, if the HELP_CHAR is passed in as the token then
  333.   -- help information is displayed.
  334.   procedure APPEND_IF_VALID (TOKEN   : in STR_PACK.DYNAMIC_STRING;
  335.                              SUCCESS : out BOOLEAN) is
  336.  
  337.     -- First character of token used to check for HELP_CHAR.
  338.     FIRST_CHAR : CHARACTER;
  339.  
  340.     -- Displays help information to the user.
  341.     procedure WRITE_TARGET_HELP is
  342.     begin
  343.       PUT_LINE("****************  HELP INFORMATION  ****************"); 
  344.       PUT_LINE("Enter a single target name, a list of target names, ");
  345.       PUT_LINE("or carriage return for the default which is the ");
  346.       PUT_LINE("first target in the specified configuration object.");
  347.       PUT_LINE("Three input attempts per target are given.");
  348.       PUT_LINE("****************************************************");
  349.     end WRITE_TARGET_HELP;
  350.  
  351.   begin  -- APPEND_IF_VALID
  352.     if STR_PACK.EMPTY(TOKEN) then 
  353.       SUCCESS := TRUE;
  354.     else 
  355.       STR_PACK.READ(FIRST_CHAR, TOKEN);
  356.       if FIRST_CHAR = HELP_CHAR then
  357.         WRITE_TARGET_HELP;
  358.         NEW_LINE;
  359.         SUCCESS := FALSE;
  360.       elsif STR_PACK.LENGTH(TOKEN) > ENVIRONS.MAX_FILENAME_LENGTH then
  361.         PUT_LINE("*** Length of the target exceeds system limitations ***");
  362.         NEW_LINE;
  363.         SUCCESS := FALSE;
  364.       else 
  365.         STR_PACK.APPEND(TOKEN, TARGET_ARG);
  366.         STR_PACK.APPEND(" ", TARGET_ARG);
  367.         SUCCESS := TRUE;
  368.       end if; -- FIRST_CHAR = HELP_CHAR then ... elsif ... else ... 
  369.     end if;   -- STR_PACK.EMPTY(TOKEN) then ... else ...
  370.   end APPEND_IF_VALID;
  371.  
  372.  
  373. begin   -- ENTER_TARGETS
  374.   -- Prompt user and store original input line as dynamic string.
  375.       PUT("TARGET(S) => ");
  376.       GET_LINE(RAW_INPUT_LINE, LAST); 
  377.       STR_PACK.CONVERT_TO_DYNAMIC
  378.         (RAW_INPUT_LINE(RAW_INPUT_LINE'FIRST..LAST), TARGET_TEMP_1);
  379.  
  380.   -- Validate each target from original input and append to dynamic string 
  381.   -- of targets.  Note the use of STR_PACK.GET_TOKEN precludes entering 
  382.   -- targets which include the token separators in their name.
  383.   STR_PACK.ASSIGN(TARGET_ARG, STR_PACK.EMPTY_STR);
  384.   loop
  385.     STR_PACK.GET_TOKEN(TARGET_TEMP_1, TARGET_TOKEN, SEPARATOR);
  386.     APPEND_IF_VALID(TARGET_TOKEN, SUCCESSFUL_INPUT);
  387.  
  388.     -- Allow 2 retrys if not successful.  Replace only the invalid target.
  389.     if not SUCCESSFUL_INPUT then 
  390.       for TRY in 2..MAX_NUM_OF_ATTEMPTS loop
  391.         PUT("RE-TRY => ");
  392.         GET_LINE(RETRY_LINE, LAST);
  393.         STR_PACK.CONVERT_TO_DYNAMIC
  394.           (RETRY_LINE(RETRY_LINE'FIRST..LAST), TARGET_TEMP_2);
  395.  
  396.         -- Don't want to raise END_OF_STRING from STR_PACK.GET_TOKEN here
  397.         -- so use STR_PACK.NUM_OF_TOKENS to determine next step.
  398.         case STR_PACK.NUM_OF_TOKENS(TARGET_TEMP_2) is
  399.           when 0 =>
  400.             SUCCESSFUL_INPUT := TRUE;
  401.           when 1 =>
  402.             STR_PACK.GET_TOKEN(TARGET_TEMP_2, TARGET_TOKEN, SEPARATOR);
  403.             APPEND_IF_VALID(TARGET_TOKEN, SUCCESSFUL_INPUT);
  404.           when others =>
  405.             PUT_LINE("*** A list is not allowed on re-try ***");
  406.             NEW_LINE;
  407.             SUCCESSFUL_INPUT := FALSE;
  408.         end case;   -- STR_PACK.NUM_OF_TOKENS(TARGET_TEMP_2) is
  409.     
  410.       -- Exit if valid target list entered before end of loop of allowed user 
  411.       -- input attempts.
  412.       if SUCCESSFUL_INPUT then 
  413.         NEW_LINE;
  414.         exit;  -- for TRY in 2..MAX_NUM_OF_ATTEMPTS loop
  415.       end if;
  416.  
  417.       end loop;   -- for TRY in 2..MAX_NUM_OF_ATTEMPTS 
  418.  
  419.       -- Abort if valid target list never entered during loop of allowed user 
  420.       -- input attempts.
  421.       if not SUCCESSFUL_INPUT then
  422.         PUT_LINE("*** Number of input attempts exceeded ***");
  423.         raise ENVIRONS.ERROR;
  424.       end if;
  425.  
  426.     end if;  -- not SUCCESSFUL_INPUT 
  427.   end loop;  -- Used to validate each token.
  428.   
  429. exception
  430.   when STR_PACK.END_OF_STRING =>   -- Raised by GET_TOKEN when no more tokens.
  431.     FILL_WITH_BLANKS(TARGET_STR);
  432.     STR_PACK.CONVERT_TO_STRING(TARGET_ARG, TARGET_STR);
  433.  
  434. end ENTER_TARGETS;
  435.  
  436.  
  437.  
  438.  
  439. -- This procedure prompts for the options and validates the user
  440. -- input.  The default options are EXECUTE, CMD_PRINT, NO_TOP_DOWN_GRAPH,
  441. -- NO_BOTTOM_UP_GRAPH, NO_LIST, NO_DUMP_TREE, and NO_PROCESS_DUMP.  For
  442. -- each option entered, the user is given 3 trys to enter a correct 
  443. -- option.  After 3 incorrect trys the program aborts.
  444. procedure ENTER_OPTIONS is
  445.  
  446.   -- Used with GET_LINE to obtain user input.
  447.   RAW_INPUT_LINE         : STRING(1..ENVIRONS.MAX_LINE_LENGTH);
  448.   RETRY_LINE             : STRING(1..ENVIRONS.MAX_LINE_LENGTH);
  449.   LAST                   : NATURAL;
  450.  
  451.   -- Used with STR_PACK routines to obtain token(s) entered by user.
  452.   OPTION_TEMP_1          : STR_PACK.DYNAMIC_STRING;
  453.   OPTION_TEMP_2          : STR_PACK.DYNAMIC_STRING;
  454.   OPTION_TOKEN           : STR_PACK.DYNAMIC_STRING;
  455.   SEPARATOR              : CHARACTER;
  456.  
  457.   -- Flag used to determine whether successful before maximum number of
  458.   -- user attempts exceeded.
  459.   SUCCESSFUL_INPUT       : BOOLEAN;
  460.  
  461.  
  462.   -- Used to convert input string to all upper case so that input options 
  463.   -- can be easily compared with valid options.
  464.   procedure CONVERT_TO_UPPER_CASE(STR : in out STRING) is
  465.   begin
  466.     for I in STR'RANGE loop
  467.       case STR(I) is
  468.         when 'a' => STR(I) := 'A';
  469.         when 'b' => STR(I) := 'B';
  470.         when 'c' => STR(I) := 'C';
  471.         when 'd' => STR(I) := 'D';
  472.         when 'e' => STR(I) := 'E';
  473.         when 'f' => STR(I) := 'F';
  474.         when 'g' => STR(I) := 'G';
  475.         when 'h' => STR(I) := 'H';
  476.         when 'i' => STR(I) := 'I';
  477.         when 'j' => STR(I) := 'J';
  478.         when 'k' => STR(I) := 'K';
  479.         when 'l' => STR(I) := 'L';
  480.         when 'm' => STR(I) := 'M';
  481.         when 'n' => STR(I) := 'N';
  482.         when 'o' => STR(I) := 'O';
  483.         when 'p' => STR(I) := 'P';
  484.         when 'q' => STR(I) := 'Q';
  485.         when 'r' => STR(I) := 'R';
  486.         when 's' => STR(I) := 'S';
  487.         when 't' => STR(I) := 'T';
  488.         when 'u' => STR(I) := 'U';
  489.         when 'v' => STR(I) := 'V';
  490.         when 'w' => STR(I) := 'W';
  491.         when 'x' => STR(I) := 'X';
  492.         when 'y' => STR(I) := 'Y';
  493.         when 'z' => STR(I) := 'Z';
  494.         when others => null;
  495.       end case;   -- STR(I) is
  496.     end loop;     -- for I in STR'RANGE 
  497.   end CONVERT_TO_UPPER_CASE;
  498.  
  499.         
  500.   -- This procedure sets the option corresponding to the token passed in;
  501.   -- unless the first character of the token is the HELP_CHAR, then help
  502.   -- information is displayed instead.
  503.   procedure SET_IF_VALID(TOKEN   : in STR_PACK.DYNAMIC_STRING;
  504.                          SUCCESS : out BOOLEAN) is
  505.  
  506.     -- Used to convert the token into an enumerated option type and a
  507.     -- flag indicating whether or not to set that option.
  508.     PREFIX     : BOOLEAN;
  509.     OPTION     : OPTION_TYPE;
  510.  
  511.     -- Used to determine whether help was requested.
  512.     FIRST_CHAR : CHARACTER;
  513.  
  514.     -- Used to check for valid option.
  515.     INVALID_OPTION         : exception;
  516.     MAX_OPT_LENGTH         : constant NATURAL := 15;
  517.     OPT_LENGTH             : NATURAL;
  518.     OPT_STR                : STRING(1..ENVIRONS.MAX_LINE_LENGTH);
  519.  
  520.  
  521.   -- Used to display help information.
  522.   procedure WRITE_OPTION_HELP is
  523.   begin
  524.     PUT_LINE("***************  HELP INFORMATION  *****************");
  525.     PUT_LINE("Enter a single option, a list of options, or ");
  526.     PUT_LINE("carriage return for the default options which ");
  527.     PUT_LINE("are EXECUTE and CMD_PRINT.  The prefix 'NO_' turns");
  528.     PUT_LINE("an option off.  Valid options are: ");
  529.     PUT_LINE("  1)  EXECUTE         - execute the commands needed");
  530.     PUT_LINE("                        to update the system.");
  531.     PUT_LINE("  2)  CMD_PRINT       - print the commands needed to ");
  532.     PUT_LINE("                        update the system.");
  533.     PUT_LINE("  3)  TOP_DOWN_GRAPH  - print a textual top-down ");
  534.     PUT_LINE("                        graph of dependencies for ");
  535.     PUT_LINE("                        the given target.");
  536.     PUT_LINE("  4)  BOTTOM_UP_GRAPH - print a textual bottom-up ");
  537.     PUT_LINE("                        graph of dependencies for ");
  538.     PUT_LINE("                        the given target.");
  539.     PUT_LINE("  4)  LIST            - print lists of all basic and  ");
  540.     PUT_LINE("                        derived names in the given CO.");
  541.     PUT_LINE("Options may be entered in mixed case or abbreviated,");
  542.     PUT_LINE("as well as entered in any order.  If conflicting ");
  543.     PUT_LINE("options are entered, the last one entered will be ");
  544.     PUT_LINE("used.  Three input attempts per option are given.");
  545.     PUT_LINE("****************************************************");
  546.   end WRITE_OPTION_HELP;
  547.  
  548.  
  549.   -- Used to parse the option token and convert from a dynamic string
  550.   -- type to an enumerated option type and a flag indicating whether the
  551.   -- option is to be set.
  552.   procedure CONVERT_TO_OPTION (TOKEN : in STR_PACK.DYNAMIC_STRING;
  553.                                PREF  : out BOOLEAN;
  554.                                OPT   : out OPTION_TYPE) is
  555.  
  556.     -- Used to split the option token into its prefix and option type.
  557.     PREF_STR              : constant STRING := "NO_";
  558.     FIRST_CHARS, PREF_DYN : STR_PACK.DYNAMIC_STRING;
  559.     CHAR                  : CHARACTER;
  560.     OPT_DYN               : STR_PACK.DYNAMIC_STRING;
  561.  
  562.     -- Used to compare input to set of valid options.
  563.     EXEC_STR       : constant STRING(1..MAX_OPT_LENGTH) := "EXECUTE        ";
  564.     TOP_DOWN_STR   : constant STRING(1..MAX_OPT_LENGTH) := "TOP_DOWN_GRAPH ";
  565.     BOTTOM_UP_STR  : constant STRING(1..MAX_OPT_LENGTH) := "BOTTOM_UP_GRAPH";
  566.     LIST_STR       : constant STRING(1..MAX_OPT_LENGTH) := "LIST           ";
  567.     CMD_STR        : constant STRING(1..MAX_OPT_LENGTH) := "CMD_PRINT      ";
  568.     TREE_STR       : constant STRING(1..MAX_OPT_LENGTH) := "DUMP_TREE      ";
  569.     PROC_STR       : constant STRING(1..MAX_OPT_LENGTH) := "PROCESS_DUMP   ";
  570.  
  571.   begin   -- CONVERT_TO_OPTION
  572.     -- Determine whether a prefix is present.
  573.     if STR_PACK.LENGTH(TOKEN) < PREF_STR'LAST then
  574.       PREF := FALSE;
  575.     else
  576.       STR_PACK.ASSIGN(FIRST_CHARS, STR_PACK.EMPTY_STR);
  577.       for I in PREF_STR'RANGE loop
  578.         STR_PACK.READ(CHAR, I, TOKEN);
  579.         STR_PACK.APPEND(CHAR, FIRST_CHARS);
  580.       end loop;
  581.       STR_PACK.CONVERT_TO_DYNAMIC(PREF_STR, PREF_DYN);
  582.       if FIRST_CHARS = PREF_DYN then PREF := TRUE;
  583.       else PREF := FALSE;
  584.       end if;
  585.     end if;  -- STR_PACK.LENGTH(TOKEN) < PREF_STR'LAST then ... else ...
  586.  
  587.     -- Determine option type as dynamic string.
  588.     if PREF then
  589.       STR_PACK.ASSIGN(OPT_DYN, STR_PACK.EMPTY_STR);
  590.       for I in PREF_STR'LENGTH + 1 .. STR_PACK.LENGTH(TOKEN) loop
  591.         STR_PACK.READ(CHAR, I, TOKEN);
  592.         STR_PACK.APPEND(CHAR, OPT_DYN);
  593.       end loop;
  594.     else STR_PACK.ASSIGN(OPT_DYN, TOKEN);
  595.     end if;   -- PREF then ... else ...
  596.  
  597.     -- Determine option type as enumeration value.
  598.     OPT_LENGTH := STR_PACK.LENGTH(OPT_DYN);
  599.     STR_PACK.CONVERT_TO_STRING(OPT_DYN, OPT_STR);
  600.     if OPT_LENGTH > MAX_OPT_LENGTH then
  601.       raise INVALID_OPTION;
  602.     elsif OPT_STR(1..OPT_LENGTH) = EXEC_STR(1..OPT_LENGTH) then
  603.       OPT := EXECUTE;
  604.     elsif OPT_STR(1..OPT_LENGTH) = TOP_DOWN_STR(1..OPT_LENGTH) then
  605.       OPT := TOP_DOWN_GRAPH;
  606.     elsif OPT_STR(1..OPT_LENGTH) = BOTTOM_UP_STR(1..OPT_LENGTH) then
  607.       OPT := BOTTOM_UP_GRAPH;
  608.     elsif OPT_STR(1..OPT_LENGTH) = LIST_STR(1..OPT_LENGTH) then
  609.       OPT := LIST;
  610.     elsif OPT_STR(1..OPT_LENGTH) = CMD_STR(1..OPT_LENGTH) then
  611.       OPT := CMD_PRINT;
  612.     elsif OPT_STR(1..OPT_LENGTH) = TREE_STR(1..OPT_LENGTH) then 
  613.       OPT := DUMP_TREE;
  614.     elsif OPT_STR(1..OPT_LENGTH) = PROC_STR(1..OPT_LENGTH) then 
  615.       OPT := PROCESS_DUMP;
  616.     else raise INVALID_OPTION;
  617.     end if;  -- ... elsif ... else ...
  618.   end CONVERT_TO_OPTION;
  619.  
  620.     
  621.   begin   -- SET_IF_VALID
  622.     if STR_PACK.EMPTY(TOKEN) then
  623.       SUCCESS := TRUE;
  624.     else
  625.       STR_PACK.READ(FIRST_CHAR, TOKEN);
  626.       if FIRST_CHAR = HELP_CHAR then
  627.         WRITE_OPTION_HELP;
  628.         NEW_LINE;
  629.         SUCCESS := FALSE;
  630.       else
  631.         CONVERT_TO_OPTION(TOKEN, PREFIX, OPTION);  -- May raise INVALID_OPTION.
  632.         if PREFIX then OPTION_TABLE(OPTION) := FALSE;
  633.         else OPTION_TABLE(OPTION) := TRUE;
  634.         end if;
  635.         SUCCESS := TRUE;
  636.       end if;   -- FIRST_CHAR = HELP_CHAR then ... else ...
  637.     end if;  -- STR_PACK.EMPTY(TOKEN) then ... else ...
  638.  
  639.   exception
  640.     when INVALID_OPTION =>
  641.       PUT("*** "); 
  642.       STR_PACK.CONVERT_TO_STRING(TOKEN, OPT_STR);
  643.       PUT(OPT_STR(OPT_STR'FIRST..STR_PACK.LENGTH(TOKEN)));
  644.       PUT_LINE(" is an invalid option ***");
  645.       NEW_LINE;
  646.       SUCCESS := FALSE;
  647.   end SET_IF_VALID;
  648.  
  649.  
  650.  
  651. begin   -- ENTER_OPTIONS
  652.   -- Prompt user and store original input as dynamic string.  
  653.     PUT("OPTION(S) => ");
  654.     GET_LINE(RAW_INPUT_LINE, LAST);
  655.     CONVERT_TO_UPPER_CASE(RAW_INPUT_LINE);
  656.     STR_PACK.CONVERT_TO_DYNAMIC
  657.       (RAW_INPUT_LINE(RAW_INPUT_LINE'FIRST..LAST), OPTION_TEMP_1);
  658.  
  659.   -- Validate each option from original input and set option table.
  660.   OPTION_TABLE := (TRUE,TRUE,FALSE,FALSE,FALSE,FALSE,FALSE); -- Defaults.
  661.   loop
  662.     STR_PACK.GET_TOKEN(OPTION_TEMP_1, OPTION_TOKEN, SEPARATOR);
  663.     SET_IF_VALID(OPTION_TOKEN, SUCCESSFUL_INPUT);
  664.     
  665.     -- Allow 2 re-trys if invalid.  Change only the invalid option.
  666.     if not SUCCESSFUL_INPUT then
  667.       for TRY in 2..MAX_NUM_OF_ATTEMPTS loop
  668.         PUT("RE-TRY => ");
  669.         GET_LINE(RETRY_LINE, LAST);
  670.         CONVERT_TO_UPPER_CASE(RETRY_LINE);
  671.         STR_PACK.CONVERT_TO_DYNAMIC
  672.           (RETRY_LINE(RETRY_LINE'FIRST..LAST), OPTION_TEMP_2);
  673.  
  674.         -- Don't want to raise END_OF_STRING from STR_PACK.GET_TOKEN here
  675.         -- so use STR_PACK.NUM_OF_TOKENS to determine next step.
  676.         case STR_PACK.NUM_OF_TOKENS(OPTION_TEMP_2) is
  677.           when 0 =>
  678.             SUCCESSFUL_INPUT := TRUE;
  679.           when 1 => 
  680.             STR_PACK.GET_TOKEN(OPTION_TEMP_2, OPTION_TOKEN, SEPARATOR);
  681.             SET_IF_VALID(OPTION_TOKEN, SUCCESSFUL_INPUT);
  682.           when others =>
  683.             PUT_LINE("*** A list is not allowed on re-try ***");
  684.             NEW_LINE;
  685.             SUCCESSFUL_INPUT := FALSE;
  686.         end case;  -- STR_PACK.NUM_OF_TOKENS(OPTION_TEMP_2) is
  687.  
  688.         -- Exit if valid option list entered before end of loop of allowed user 
  689.         -- input attempts.
  690.         if SUCCESSFUL_INPUT then
  691.           NEW_LINE;
  692.           exit;  -- for TRY in 2..MAX_NUM_OF_ATTEMPTS loop
  693.         end if;
  694.  
  695.       end loop;  -- for TRY in 2..MAX_NUM_OF_ATTEMPTS
  696.  
  697.       -- Abort if valid option list never entered during loop of allowed user 
  698.       -- input attempts.
  699.       if not SUCCESSFUL_INPUT then
  700.         PUT_LINE("*** Number of input attempts exceeded ***");
  701.         raise ENVIRONS.ERROR;
  702.       end if;
  703.  
  704.     end if;  -- not SUCCESSFUL_INPUT then ...
  705.   end loop;  -- Used to validate each token.
  706.  
  707.   exception
  708.     when STR_PACK.END_OF_STRING =>  -- Raised by GET_TOKEN when no more tokens.
  709.       null; 
  710.  
  711. end ENTER_OPTIONS;
  712.  
  713.  
  714. -- Function to determine whether a given option is set.
  715. function IS_SET (OPTION : OPTION_TYPE) return BOOLEAN is
  716. begin
  717.   return OPTION_TABLE(OPTION);
  718. end IS_SET;
  719.  
  720.  
  721. -- Procedure to prompt for all the arguments, to call the appropriate
  722. -- procedures that enter the arguments for processing by Construct,
  723. -- and to re-display the arguments as given by the user.
  724. procedure PROMPT is
  725. begin
  726.  
  727.   PUT_LINE("Enter arguments following each prompt or '?' for help.");
  728.   NEW_LINE;
  729.   ENTER_CO;
  730.   ENTER_TARGETS;
  731.   ENTER_OPTIONS;
  732.  
  733.   NEW_LINE;
  734.   PUT("THE CO IS:          "); 
  735.   PUT_LINE(CO_STR);
  736.  
  737.   PUT("THE TARGET(S) ARE:  ");  
  738.   if STR_PACK.EMPTY(TARGET_ARG) then PUT_LINE("Default");    
  739.   else PUT_LINE(TARGET_STR(TARGET_STR'FIRST..STR_PACK.LENGTH(TARGET_ARG)));
  740.   end if;
  741.  
  742.   PUT("THE OPTION(S) ARE:  ");  
  743.   for OPTION in EXECUTE..CMD_PRINT loop
  744.     if IS_SET(OPTION) then
  745.       PUT(OPTION_TYPE'IMAGE(OPTION));
  746.       PUT("         ");
  747.     end if;
  748.   end loop;
  749.   NEW_LINE;
  750.   PUT("                    ");
  751.   for OPTION in TOP_DOWN_GRAPH..LIST loop
  752.     if IS_SET(OPTION) then
  753.       PUT(OPTION_TYPE'IMAGE(OPTION));
  754.       PUT("  ");
  755.     end if;
  756.   end loop;
  757.   NEW_LINE;
  758.   PUT("                    ");
  759.   for OPTION in DUMP_TREE..PROCESS_DUMP loop
  760.     if IS_SET(OPTION) then
  761.       PUT(OPTION_TYPE'IMAGE(OPTION));
  762.       PUT("       ");
  763.     end if;
  764.   end loop;
  765.  
  766.   NEW_LINE;
  767.   NEW_LINE;
  768.  
  769. end PROMPT;
  770.  
  771.  
  772. end ARGUMENTS;
  773.  
  774. --::::::::::::::
  775. --ast_graph.text
  776. --::::::::::::::
  777. -- This package defins the central data structure for the CONSTRUCT program and
  778. -- a dump routine which prints out the data structure for debugging purposes.
  779. -- The data structure is a linked list of names, with pointers to dependent and
  780. -- and defining rules.  The name list also contains several work-area fields
  781. -- (TIME-STAMP, USED, TOUCHED, and EXPANSION_LINE) which are used during
  782. -- processing of the data structure.  The rules contain lists of targets and
  783. -- dependents which are pointers back to the name blocks.  The rules also
  784. -- contain links to the st of commands and a source line number for the rule.
  785. with CALENDAR;
  786. with ENVIRONS;
  787. with STR_PACK;  use STR_PACK;
  788. with TEXT_IO; use TEXT_IO, INTEGER_IO;
  789.  
  790.  
  791. package AST_GRAPH is
  792.    
  793.    type RULE_DESCRIPTION;
  794.    type LINK_TO_RULE_DESCRIPTION is access RULE_DESCRIPTION;
  795.    
  796.    
  797.    type LIST_OF_RULES;
  798.    type LINK_TO_LIST_OF_RULES is access LIST_OF_RULES;
  799.    
  800.    type LIST_OF_NAMES;
  801.    type LINK_TO_LIST_OF_NAMES is access LIST_OF_NAMES;
  802.  
  803.    type LIST_OF_COMMANDS;
  804.    type LINK_TO_LIST_OF_COMMANDS is access LIST_OF_COMMANDS;
  805.   
  806.    type NAME_BLOCK;
  807.    type LINK_TO_NAME_BLOCK is access NAME_BLOCK;
  808.    
  809.    type NAME_BLOCK is
  810.       record
  811.          PREVIOUS_NAME_BLOCK : LINK_TO_NAME_BLOCK := NULL;
  812.          NAME_VALUE : STR_PACK.DYNAMIC_STRING ;
  813.          DEPENDENT_RULES : LINK_TO_LIST_OF_RULES := NULL;
  814.          NUMBER_OF_DEPENDENTS : INTEGER := 0;
  815.          DEFINING_RULES : LINK_TO_LIST_OF_RULES := NULL;
  816.          TIME_STAMP : CALENDAR.TIME;
  817.          NEXT_NAME_BLOCK : LINK_TO_NAME_BLOCK := NULL;
  818.          USED : BOOLEAN := FALSE;
  819.          TOUCHED : BOOLEAN := FALSE;
  820.          EXPANSION_LINE : INTEGER := 0;
  821.       end record;
  822.    
  823.    NAME_LIST : LINK_TO_NAME_BLOCK := NULL;
  824.    NAME_LINK : LINK_TO_NAME_BLOCK := NULL;
  825.    NEW_RULE_DESCRIPTION : LINK_TO_RULE_DESCRIPTION;
  826.    
  827.    type RULE_DESCRIPTION is
  828.       record
  829.          CO_LINE_NUMBER : INTEGER := 0;
  830.          TARGETS : LINK_TO_LIST_OF_NAMES := NULL;
  831.          DEPENDENTS : LINK_TO_LIST_OF_NAMES := NULL;
  832.          TOP_OF_COMMANDS : LINK_TO_LIST_OF_COMMANDS := NULL;
  833.          COMMANDS : LINK_TO_LIST_OF_COMMANDS := NULL;
  834.       end record;
  835.    
  836.    type LIST_OF_NAMES is
  837.       record
  838.          PREVIOUS_NAME : LINK_TO_LIST_OF_NAMES;
  839.          NAME : LINK_TO_NAME_BLOCK;
  840.          CYCLE_DETECTED : BOOLEAN := FALSE;--Used for detecting cycles in graph.
  841.          NEXT_NAME : LINK_TO_LIST_OF_NAMES;
  842.       end record;
  843.    
  844.    type LIST_OF_RULES is
  845.       record
  846.          PREVIOUS_RULE : LINK_TO_LIST_OF_RULES;
  847.          RULE : LINK_TO_RULE_DESCRIPTION;
  848.          NEXT_RULE : LINK_TO_LIST_OF_RULES;
  849.       end record;
  850.    
  851.    type LIST_OF_COMMANDS is
  852.       record
  853.       PREVIOUS_COMMAND : LINK_TO_LIST_OF_COMMANDS;
  854.       COMMAND : STR_PACK.DYNAMIC_STRING;
  855.       NEXT_COMMAND : LINK_TO_LIST_OF_COMMANDS;
  856.       end record;
  857.  
  858.  
  859.    CO_LINE : STR_PACK.DYNAMIC_STRING;
  860.    
  861.  
  862.    procedure DUMP_AST_GRAPH(DUMP_OUTPUT : FILE_TYPE);
  863.  
  864. end AST_GRAPH;
  865.  
  866.  
  867. package body AST_GRAPH is
  868.  
  869.  
  870. -- This procedure controls the dumping of AST_GRAPH as it has been built.  It
  871. -- is not needed for normal execution of CONSTRUCT, but is available for
  872. -- debugging.  It loops through the NAME_BLOCK list printing each name and
  873. -- calling internal routines to print the lists of defining rules and target
  874. -- rules.  All output is sent to DUMP_OUTPUT which must be opened by the caller.
  875. procedure DUMP_AST_GRAPH(DUMP_OUTPUT : FILE_TYPE) is
  876.     LOCAL_COPY_OF_NAMES : LINK_TO_LIST_OF_NAMES;
  877.     TEMP_NAME           : STRING(1..ENVIRONS.MAX_CMD_LENGTH);
  878.     TEMP_COMMAND        : STRING(1..ENVIRONS.MAX_CMD_LENGTH);
  879.     SIZE                : NATURAL;
  880.  
  881.    
  882. -- This procedure prints an entir list of rules.  It is used for processing of
  883. -- both target and dependent lists.  For each rule, all target and dependent
  884. -- names are listed as well as the associated command lines.
  885. procedure LOOP_THRU_RULES(ARG_LINK_TO_LIST_OF_RULES:in LINK_TO_LIST_OF_RULES)is
  886.  
  887.    LOCAL_COPY_OF_NAMES : LINK_TO_LIST_OF_NAMES;
  888.    LOCAL_COPY_OF_COMMANDS : LINK_TO_LIST_OF_COMMANDS;
  889.    LOCAL_COPY_OF_RULES : LINK_TO_LIST_OF_RULES;
  890.  
  891.  begin
  892.    LOCAL_COPY_OF_RULES := ARG_LINK_TO_LIST_OF_RULES;
  893.  
  894.    loop
  895.      NEW_LINE ( DUMP_OUTPUT );
  896.      PUT_LINE( DUMP_OUTPUT,
  897.                "This is a CO_LINE_NUMBER of a defining or dependent rule: ");
  898.      PUT( DUMP_OUTPUT, LOCAL_COPY_OF_RULES.RULE.CO_LINE_NUMBER);
  899.      NEW_LINE ( DUMP_OUTPUT );
  900.      LOCAL_COPY_OF_NAMES := LOCAL_COPY_OF_RULES.RULE.TARGETS;
  901.              
  902.      -- loop through TARGETS
  903.      if LOCAL_COPY_OF_NAMES = NULL then
  904.         PUT_LINE( DUMP_OUTPUT, "TARGETS is NULL."); 
  905.      else
  906.         loop
  907.           PUT( DUMP_OUTPUT, "TARGET: ");
  908.           SIZE := STR_PACK.LENGTH(    LOCAL_COPY_OF_NAMES.NAME.NAME_VALUE );
  909.           STR_PACK.CONVERT_TO_STRING( LOCAL_COPY_OF_NAMES.NAME.NAME_VALUE,
  910.                                       TEMP_NAME(1..SIZE) );
  911.           PUT_LINE ( DUMP_OUTPUT, TEMP_NAME(1..SIZE) );
  912.           
  913.           exit when LOCAL_COPY_OF_NAMES.PREVIOUS_NAME = NULL;
  914.           LOCAL_COPY_OF_NAMES := LOCAL_COPY_OF_NAMES.PREVIOUS_NAME;
  915.         end loop;
  916.      end if;
  917.  
  918.  
  919.      LOCAL_COPY_OF_NAMES := LOCAL_COPY_OF_RULES.RULE.DEPENDENTS;
  920.      -- loop through DEPENDENTS
  921.      if LOCAL_COPY_OF_NAMES = NULL then 
  922.         PUT_LINE( DUMP_OUTPUT,"DEPENDENTS is NULL.");
  923.      else
  924.         loop
  925.           PUT( DUMP_OUTPUT, "DEPENDENT: ");
  926.           SIZE := STR_PACK.LENGTH(    LOCAL_COPY_OF_NAMES.NAME.NAME_VALUE );
  927.           STR_PACK.CONVERT_TO_STRING( LOCAL_COPY_OF_NAMES.NAME.NAME_VALUE,
  928.                                       TEMP_NAME(1..SIZE) );
  929.           PUT_LINE ( DUMP_OUTPUT, TEMP_NAME(1..SIZE) );
  930.  
  931.           exit when LOCAL_COPY_OF_NAMES.PREVIOUS_NAME = NULL;
  932.           LOCAL_COPY_OF_NAMES := LOCAL_COPY_OF_NAMES.PREVIOUS_NAME;
  933.         end loop;
  934.      end if;
  935.  
  936.      LOCAL_COPY_OF_COMMANDS := LOCAL_COPY_OF_RULES.RULE.COMMANDS;
  937.          
  938.      -- loop through COMMANDS
  939.      if LOCAL_COPY_OF_COMMANDS = NULL then
  940.         PUT_LINE( DUMP_OUTPUT, "COMMANDS is NULL.");
  941.      else
  942.         loop
  943.           PUT( DUMP_OUTPUT, "COMMAND: ");
  944.           SIZE := STR_PACK.LENGTH( LOCAL_COPY_OF_COMMANDS.COMMAND );
  945.           STR_PACK.CONVERT_TO_STRING(LOCAL_COPY_OF_COMMANDS.COMMAND,
  946.                                      TEMP_COMMAND(1..SIZE) );
  947.           PUT_LINE ( DUMP_OUTPUT, TEMP_COMMAND(1..SIZE) );
  948.           
  949.           exit when LOCAL_COPY_OF_COMMANDS.PREVIOUS_COMMAND = NULL;
  950.           LOCAL_COPY_OF_COMMANDS := LOCAL_COPY_OF_COMMANDS.PREVIOUS_COMMAND;
  951.         end loop;
  952.      end if;
  953.      
  954.      exit when LOCAL_COPY_OF_RULES.PREVIOUS_RULE = NULL;
  955.      LOCAL_COPY_OF_RULES := LOCAL_COPY_OF_RULES.PREVIOUS_RULE;
  956.    end loop;
  957.  end LOOP_THRU_RULES;
  958.   
  959. begin
  960.  
  961.   NEW_LINE ( DUMP_OUTPUT ); NEW_LINE ( DUMP_OUTPUT );
  962.   PUT_LINE ( DUMP_OUTPUT,"AST GRAPH DUMP BY NAME VALUE");
  963.   NEW_LINE ( DUMP_OUTPUT );
  964.  
  965.   loop
  966.     NEW_LINE ( DUMP_OUTPUT );  NEW_LINE ( DUMP_OUTPUT ); 
  967.     NEW_LINE ( DUMP_OUTPUT ); 
  968.     PUT( DUMP_OUTPUT, "NAME_LINK.NAME_VALUE: ");
  969.     SIZE := STR_PACK.LENGTH( NAME_LINK.NAME_VALUE );
  970.     STR_PACK.CONVERT_TO_STRING(NAME_LINK.NAME_VALUE, TEMP_NAME(1..SIZE) );
  971.     PUT_LINE ( DUMP_OUTPUT, TEMP_NAME(1..SIZE) );
  972.     
  973.     if NAME_LINK.DEFINING_RULES = NULL then
  974.       PUT_LINE ( DUMP_OUTPUT, "DEFINING_RULES is NULL.");
  975.     else
  976.       NEW_LINE ( DUMP_OUTPUT );
  977.       PUT_LINE( DUMP_OUTPUT,
  978.                 "These are the CO_LINE_NUMBERs, TARGETS, DEPENDENTS,");
  979.       PUT_LINE( DUMP_OUTPUT,
  980.                 " and COMMANDS for each rule where NAME_VALUE is defined.");
  981.            
  982.       LOOP_THRU_RULES ( NAME_LINK.DEFINING_RULES );
  983.  
  984.     end if; -- if NAME_LINK.DEFINING_RULES = NULL
  985.      
  986.     if NAME_LINK.DEPENDENT_RULES = NULL then
  987.       PUT_LINE( DUMP_OUTPUT, "DEPENDENT_RULES is NULL.");
  988.     else
  989.       NEW_LINE ( DUMP_OUTPUT );
  990.       PUT_LINE( DUMP_OUTPUT,
  991.                 "These are the CO_LINE_NUMBERs, TARGETS, DEPENDENTS,");
  992.       PUT_LINE( DUMP_OUTPUT,
  993.                 " and COMMANDS for each rule where NAME_VALUE is dependent.");
  994.          
  995.       LOOP_THRU_RULES ( NAME_LINK.DEPENDENT_RULES );
  996.  
  997.     end if;
  998.     exit when NAME_LINK.PREVIOUS_NAME_BLOCK = NULL;
  999.     NAME_LINK := NAME_LINK.PREVIOUS_NAME_BLOCK;
  1000.     
  1001.   end loop; -- On the whole NAME_LIST
  1002.    
  1003.  
  1004. end DUMP_AST_GRAPH;
  1005.  
  1006.                       
  1007. end AST_GRAPH;
  1008.  
  1009. --::::::::::::::
  1010. --bld_graph.text
  1011. --::::::::::::::
  1012. -- This package consists of a single procedure which controls building of the
  1013. -- data structure containing the dependency graph upon which the processing
  1014. -- of CONSTRUCT is based.  The main loop reads one line from the co_file and
  1015. -- then calls routines in BLD_LST to build target, dependent, and command lists.
  1016. -- Note that BLD_LST also reads from the co_file.
  1017. package BLD_GRAPH is
  1018.    procedure BUILD;
  1019. end BLD_GRAPH;
  1020.  
  1021.  
  1022. with BLD_LST, AST_GRAPH, STR_PACK, TEXT_IO, ARGUMENTS;
  1023. use TEXT_IO;
  1024. package body BLD_GRAPH is
  1025.  
  1026.     procedure BUILD is
  1027.  
  1028.       TYPE_OF_LIST : BLD_LST.LIST_TYPE;
  1029.  
  1030.     begin
  1031.  
  1032.     -- Create a null name block for use in BLD_LST.BUILD_LIST.
  1033.     -- BLD_LST.DELETE_NULL_NAME_BLOCK will get rid of it to prepare for
  1034.     -- Construction processing.  Construction processing expects the NAME_VALUE
  1035.     -- to contain a real TOKEN and the PREVIOUS_NAME_BLOCK will
  1036.     -- be null.  However, it is easier in BLD_LST.BUILD_LIST to use
  1037.     -- a null name block where the NAME_VALUE is empty.
  1038.  
  1039.     AST_GRAPH.NAME_LIST := new AST_GRAPH.NAME_BLOCK;
  1040.     
  1041.     -- The input file, CO_FILE, is already opened in package ARGUMENTS.
  1042.     -- Must create the output file for dumping the tree.
  1043.     if ARGUMENTS.IS_SET( ARGUMENTS.DUMP_TREE ) then
  1044.        CREATE( BLD_LST.DUMP_OUTPUT, OUT_FILE, "DUMP_OUTPUT");
  1045.     end if;
  1046.     
  1047.     loop
  1048.        GET_LINE ( ARGUMENTS.CO_FILE, BLD_LST.CO_LINE, BLD_LST.LAST_CHAR );
  1049.        STR_PACK.CONVERT_TO_DYNAMIC
  1050.          (BLD_LST.CO_LINE(BLD_LST.CO_LINE'FIRST..BLD_LST.LAST_CHAR),
  1051.           AST_GRAPH.CO_LINE);
  1052.        BLD_LST.CO_LINE_COUNT := BLD_LST.CO_LINE_COUNT + 1;
  1053.        TYPE_OF_LIST := BLD_LST.TARGETS;
  1054.        BLD_LST.BUILD_LIST( TYPE_OF_LIST  );
  1055.        TYPE_OF_LIST := BLD_LST.DEPENDENTS;
  1056.        BLD_LST.BUILD_LIST( TYPE_OF_LIST );
  1057.        BLD_LST.BUILD_CMD_LIST;
  1058.     end loop;  --  Until END_ERROR is raised.
  1059.  
  1060.     exception
  1061.        when END_ERROR =>
  1062.           CLOSE ( ARGUMENTS.CO_FILE );
  1063.           BLD_LST.DELETE_NULL_NAME_BLOCK;
  1064.           if ARGUMENTS.IS_SET( ARGUMENTS.DUMP_TREE ) then
  1065.              AST_GRAPH.DUMP_AST_GRAPH(BLD_LST.DUMP_OUTPUT); 
  1066.           end if;
  1067.           if IS_OPEN ( BLD_LST.DUMP_OUTPUT ) then
  1068.              CLOSE ( BLD_LST.DUMP_OUTPUT );
  1069.           end if;
  1070.    end BUILD;
  1071.  
  1072. end BLD_GRAPH;
  1073.  
  1074. --::::::::::::::
  1075. --bld_lst.text
  1076. --::::::::::::::
  1077. -- This package contains the routines which process the co_file and create the
  1078. -- linked lists of dynamic data which constitute the central data structure of
  1079. -- CONSTRUCT.  It is closely tied to BLD_GRAPH.
  1080. with AST_GRAPH, ENVIRONS;
  1081. with TEXT_IO; use TEXT_IO;
  1082. package BLD_LST is
  1083.   DUMP_OUTPUT    : FILE_TYPE;
  1084.   LAST_CHAR      : NATURAL;
  1085.   CO_LINE_COUNT  : INTEGER  := 0;
  1086.   CO_LINE        : STRING(1..ENVIRONS.MAX_LINE_LENGTH);
  1087.  
  1088.    type LIST_TYPE is ( TARGETS, DEPENDENTS );
  1089.    procedure BUILD_LIST( TYPE_OF_LIST : in LIST_TYPE );
  1090.    procedure BUILD_CMD_LIST;
  1091.    procedure ADD_RULE_LIST_TO_NAME_BLOCK
  1092.              ( LOCAL_LINK_TO_LIST_OF_RULES : 
  1093.                in out AST_GRAPH.LINK_TO_LIST_OF_RULES );
  1094.    procedure ADD_NAME_LIST_TO_RULE_DESCRIPTION
  1095.              ( LOCAL_LINK_TO_LIST_OF_NAMES :
  1096.                in out AST_GRAPH.LINK_TO_LIST_OF_NAMES );
  1097.    procedure ADD_NAME_BLOCK( TYPE_OF_LIST : in LIST_TYPE );
  1098.    procedure DELETE_NULL_NAME_BLOCK;
  1099.  
  1100. end BLD_LST;
  1101.  
  1102.  
  1103.  
  1104. with STR_PACK, ARGUMENTS;
  1105. use STR_PACK;
  1106. package body BLD_LST is
  1107.  
  1108.    TOKEN : STR_PACK.DYNAMIC_STRING;
  1109.    TERMINATOR : CHARACTER;
  1110.    TERMINATOR_FOR_LIST : CHARACTER;
  1111.  
  1112.    -- This procedure processes a list of either target or dependent names as
  1113.    -- indicated by the input parameter.  For target lists, a new rule
  1114.    -- description is initiated.  For both types of lists, the name block is
  1115.    -- searched for a match. On matches, rule list is added in the appropriate
  1116.    -- field of the name_block.  When no match occurs, a new name block entry
  1117.    -- is created and properly linked.  Comments are ignored; new lines are read
  1118.    -- when a continuation mark is encountered; and processing halts when the
  1119.    -- terminator character ( ':' or ';') for the list is found.
  1120.    procedure BUILD_LIST ( TYPE_OF_LIST : in LIST_TYPE ) is
  1121.    begin
  1122.       AST_GRAPH.NAME_LINK := AST_GRAPH.NAME_LIST;
  1123.    
  1124.       if TYPE_OF_LIST = TARGETS then
  1125.          AST_GRAPH.NEW_RULE_DESCRIPTION := new AST_GRAPH.RULE_DESCRIPTION;
  1126.          AST_GRAPH.NEW_RULE_DESCRIPTION.CO_LINE_NUMBER := CO_LINE_COUNT;
  1127.          TERMINATOR_FOR_LIST := ENVIRONS.TARGET_LIST_TERMINATOR;
  1128.       else
  1129.          TERMINATOR_FOR_LIST := ENVIRONS.DEPENDENT_LIST_TERMINATOR;
  1130.       end if;
  1131.        
  1132.       loop
  1133.          STR_PACK.GET_TOKEN ( AST_GRAPH.CO_LINE, TOKEN, TERMINATOR );
  1134.          loop
  1135.             if STR_PACK.EMPTY( TOKEN ) then -- Occurs when the CO line starts
  1136.                exit;                        -- with a comment. That is, TOKEN
  1137.             end if;                         -- is EMPTY and TERMINATOR is '-'.
  1138.  
  1139.             if TOKEN = AST_GRAPH.NAME_LINK.NAME_VALUE then
  1140.                if TYPE_OF_LIST = TARGETS then
  1141.                   ADD_RULE_LIST_TO_NAME_BLOCK
  1142.                              ( AST_GRAPH.NAME_LINK.DEFINING_RULES );
  1143.                   ADD_NAME_LIST_TO_RULE_DESCRIPTION 
  1144.                              ( AST_GRAPH.NEW_RULE_DESCRIPTION.TARGETS );
  1145.                else -- TYPE_OF_LIST = DEPENDENTS
  1146.                   ADD_RULE_LIST_TO_NAME_BLOCK
  1147.                              ( AST_GRAPH.NAME_LINK.DEPENDENT_RULES );
  1148.                   ADD_NAME_LIST_TO_RULE_DESCRIPTION
  1149.                              ( AST_GRAPH.NEW_RULE_DESCRIPTION.DEPENDENTS );
  1150.                end if;
  1151.  
  1152.                -- Reset AST_GRAPH.NAME_LINK to
  1153.                -- the end of the list of name blocks.
  1154.  
  1155.                AST_GRAPH.NAME_LINK := AST_GRAPH.NAME_LIST;
  1156.                exit;
  1157.             end if;
  1158.  
  1159.             if AST_GRAPH.NAME_LINK.PREVIOUS_NAME_BLOCK = null then
  1160.                ADD_NAME_BLOCK( TYPE_OF_LIST );
  1161.                if TYPE_OF_LIST = TARGETS then
  1162.                   ADD_NAME_LIST_TO_RULE_DESCRIPTION
  1163.                              ( AST_GRAPH.NEW_RULE_DESCRIPTION.TARGETS );
  1164.                else -- TYPE_OF_LIST = DEPENDENTS
  1165.                   ADD_NAME_LIST_TO_RULE_DESCRIPTION
  1166.                              ( AST_GRAPH.NEW_RULE_DESCRIPTION.DEPENDENTS );
  1167.                end if;
  1168.  
  1169.                -- Reset AST_GRAPH.NAME_LIST to the end of the 
  1170.                -- list of name blocks.
  1171.                AST_GRAPH.NAME_LIST := AST_GRAPH.NAME_LINK;
  1172.  
  1173.                exit;
  1174.              end if;
  1175.   
  1176.              AST_GRAPH.NAME_LINK := AST_GRAPH.NAME_LINK.PREVIOUS_NAME_BLOCK;
  1177.  
  1178.           end loop;
  1179.  
  1180.        if TERMINATOR = ENVIRONS.COMMENT  or 
  1181.           TERMINATOR = ENVIRONS.CONTINUATION then
  1182.              GET_LINE ( ARGUMENTS.CO_FILE, CO_LINE, LAST_CHAR);
  1183.              STR_PACK.CONVERT_TO_DYNAMIC 
  1184.                     (CO_LINE(CO_LINE'FIRST..LAST_CHAR), AST_GRAPH.CO_LINE);
  1185.              CO_LINE_COUNT := CO_LINE_COUNT + 1;
  1186.         end if;
  1187.  
  1188.         exit when TERMINATOR = TERMINATOR_FOR_LIST;
  1189.  
  1190.       end loop;
  1191.  
  1192. end BUILD_LIST;
  1193.  
  1194.  
  1195. -- This procedure is called after AST_GRAPH has been built. It removes the null
  1196. -- name block entry which was created for ease of processing during creation of
  1197. -- AST_GRAPH.
  1198. procedure DELETE_NULL_NAME_BLOCK is
  1199. begin
  1200.     -- Get rid of null Name Block at top of name list
  1201.     while AST_GRAPH.NAME_LINK.PREVIOUS_NAME_BLOCK /= null loop
  1202.       AST_GRAPH.NAME_LINK := AST_GRAPH.NAME_LINK.PREVIOUS_NAME_BLOCK;
  1203.     end loop;
  1204.  
  1205.     AST_GRAPH.NAME_LINK := AST_GRAPH.NAME_LINK.NEXT_NAME_BLOCK;
  1206.     AST_GRAPH.NAME_LINK.PREVIOUS_NAME_BLOCK := null;
  1207.     AST_GRAPH.NAME_LINK := AST_GRAPH.NAME_LIST;
  1208.    
  1209. end DELETE_NULL_NAME_BLOCK;
  1210.  
  1211.  
  1212. -- This procedure copies source lines one at a time from co_file into a
  1213. -- linked list of commads pointed to by the current rule description.
  1214. -- The procedure terminates when a command list termination character
  1215. -- (nominally, a '$') is found.
  1216. procedure BUILD_CMD_LIST is
  1217.    NEW_COMMAND : AST_GRAPH.LINK_TO_LIST_OF_COMMANDS;
  1218.    CHAR        : CHARACTER;
  1219.  
  1220.    begin
  1221.       loop
  1222.          GET_LINE ( ARGUMENTS.CO_FILE, CO_LINE, LAST_CHAR );
  1223.          STR_PACK.CONVERT_TO_DYNAMIC 
  1224.              (CO_LINE(CO_LINE'FIRST..LAST_CHAR), AST_GRAPH.CO_LINE );
  1225.          CO_LINE_COUNT := CO_LINE_COUNT + 1;
  1226.          STR_PACK.READ( CHAR, AST_GRAPH.CO_LINE );
  1227.          exit when CHAR = ENVIRONS.END_OF_COMMANDS;
  1228.  
  1229.          if AST_GRAPH.NEW_RULE_DESCRIPTION.COMMANDS /= null then
  1230.           NEW_COMMAND               := new AST_GRAPH.LIST_OF_COMMANDS;
  1231.           NEW_COMMAND.PREVIOUS_COMMAND :=
  1232.              AST_GRAPH.NEW_RULE_DESCRIPTION.COMMANDS;
  1233.           STR_PACK.ASSIGN(NEW_COMMAND.COMMAND, AST_GRAPH.CO_LINE);
  1234.           NEW_COMMAND.NEXT_COMMAND  := null;
  1235.           AST_GRAPH.NEW_RULE_DESCRIPTION.COMMANDS.NEXT_COMMAND := NEW_COMMAND;
  1236.           AST_GRAPH.NEW_RULE_DESCRIPTION.COMMANDS := NEW_COMMAND;
  1237.          else
  1238.           NEW_COMMAND               := new AST_GRAPH.LIST_OF_COMMANDS;
  1239.           NEW_COMMAND.PREVIOUS_COMMAND:= null;
  1240.           STR_PACK.ASSIGN(NEW_COMMAND.COMMAND, AST_GRAPH.CO_LINE);
  1241.           NEW_COMMAND.NEXT_COMMAND  := null;
  1242.           AST_GRAPH.NEW_RULE_DESCRIPTION.COMMANDS := NEW_COMMAND;
  1243.           AST_GRAPH.NEW_RULE_DESCRIPTION.TOP_OF_COMMANDS := NEW_COMMAND;
  1244.          end if;
  1245.  
  1246.       end loop;
  1247.  
  1248.    end BUILD_CMD_LIST;
  1249.  
  1250.  
  1251. -- This procedure adds a pointer to the current rule description to the list
  1252. -- of rules which is passed in.  If the list is null, the new rule is 
  1253. -- properly added at the head.
  1254. procedure ADD_RULE_LIST_TO_NAME_BLOCK
  1255.           ( LOCAL_LINK_TO_LIST_OF_RULES : in out
  1256.             AST_GRAPH.LINK_TO_LIST_OF_RULES )      is
  1257.  
  1258.    begin
  1259.       if LOCAL_LINK_TO_LIST_OF_RULES /= null then
  1260.          LOCAL_LINK_TO_LIST_OF_RULES.NEXT_RULE :=
  1261.            new AST_GRAPH.LIST_OF_RULES'(LOCAL_LINK_TO_LIST_OF_RULES,
  1262.                                         AST_GRAPH.NEW_RULE_DESCRIPTION, null );
  1263.          LOCAL_LINK_TO_LIST_OF_RULES := LOCAL_LINK_TO_LIST_OF_RULES.NEXT_RULE;
  1264.       else
  1265.          LOCAL_LINK_TO_LIST_OF_RULES :=
  1266.            new AST_GRAPH.LIST_OF_RULES'(null,
  1267.                                         AST_GRAPH.NEW_RULE_DESCRIPTION, null);
  1268.       end if;
  1269.  
  1270.    end ADD_RULE_LIST_TO_NAME_BLOCK;
  1271.  
  1272.  
  1273. -- This procedure adds the current name link to a list of names within a rule
  1274. -- description.  If the list of names which is passed in is null,  the current
  1275. -- name is properly added at the head of the list.
  1276. procedure ADD_NAME_LIST_TO_RULE_DESCRIPTION
  1277.           ( LOCAL_LINK_TO_LIST_OF_NAMES :
  1278.                in out AST_GRAPH.LINK_TO_LIST_OF_NAMES ) is
  1279.    begin
  1280.  
  1281.       -- LOCAL_LINK_TO_LIST_OF_NAMES may be .TARGETS or .DEPENDENTS
  1282.       if LOCAL_LINK_TO_LIST_OF_NAMES /= null then
  1283.          LOCAL_LINK_TO_LIST_OF_NAMES.NEXT_NAME :=
  1284.            new AST_GRAPH.LIST_OF_NAMES; 
  1285.          LOCAL_LINK_TO_LIST_OF_NAMES.NEXT_NAME.PREVIOUS_NAME :=
  1286.                  LOCAL_LINK_TO_LIST_OF_NAMES;
  1287.          LOCAL_LINK_TO_LIST_OF_NAMES.NEXT_NAME.NAME :=
  1288.                  AST_GRAPH.NAME_LINK;
  1289.          LOCAL_LINK_TO_LIST_OF_NAMES.NEXT_NAME.NEXT_NAME := null;
  1290.          LOCAL_LINK_TO_LIST_OF_NAMES := LOCAL_LINK_TO_LIST_OF_NAMES.NEXT_NAME;
  1291.       else
  1292.          LOCAL_LINK_TO_LIST_OF_NAMES :=
  1293.            new AST_GRAPH.LIST_OF_NAMES;
  1294.          LOCAL_LINK_TO_LIST_OF_NAMES.PREVIOUS_NAME := null;
  1295.          LOCAL_LINK_TO_LIST_OF_NAMES.NAME :=
  1296.                  AST_GRAPH.NAME_LINK;
  1297.          LOCAL_LINK_TO_LIST_OF_NAMES.NEXT_NAME := null;
  1298.       end if;
  1299.  
  1300.    end ADD_NAME_LIST_TO_RULE_DESCRIPTION;
  1301.  
  1302.  
  1303. -- This procedure adds a new name block to the list of name blocks.  The name
  1304. -- field is set and either a target or dependent rule pointer is set, depending
  1305. -- on the part of the dependency rule being processed.
  1306. procedure ADD_NAME_BLOCK ( TYPE_OF_LIST : in LIST_TYPE ) is
  1307.           
  1308.    begin
  1309.       AST_GRAPH.NAME_LINK := new AST_GRAPH.NAME_BLOCK;
  1310.       AST_GRAPH.NAME_LINK.PREVIOUS_NAME_BLOCK := AST_GRAPH.NAME_LIST;
  1311.       AST_GRAPH.NAME_LIST.NEXT_NAME_BLOCK := AST_GRAPH.NAME_LINK;
  1312.       STR_PACK.ASSIGN( AST_GRAPH.NAME_LINK.NAME_VALUE, TOKEN );
  1313.       if TYPE_OF_LIST = TARGETS then
  1314.          AST_GRAPH.NAME_LINK.DEFINING_RULES := 
  1315.            new AST_GRAPH.LIST_OF_RULES'
  1316.              (null, AST_GRAPH.NEW_RULE_DESCRIPTION, null);
  1317.       else -- TYPE_OF_LIST = DEPENDENTS
  1318.          AST_GRAPH.NAME_LINK.DEPENDENT_RULES :=
  1319.            new AST_GRAPH.LIST_OF_RULES'
  1320.              (null, AST_GRAPH.NEW_RULE_DESCRIPTION, null);
  1321.       end if;
  1322.  
  1323.       AST_GRAPH.NAME_LIST := AST_GRAPH.NAME_LINK;
  1324.    end ADD_NAME_BLOCK;
  1325.  
  1326.                       
  1327. end BLD_LST;
  1328.  
  1329. --::::::::::::::
  1330. --calendar.text
  1331. --::::::::::::::
  1332.  
  1333. package CALENDAR is
  1334.  
  1335.     subtype TIME is  LONG_INTEGER;
  1336.  
  1337. end CALENDAR;
  1338.  
  1339. --::::::::::::::
  1340. --con_proc.text
  1341. --::::::::::::::
  1342. pragma SOURCE_INFO(ON);
  1343.  
  1344. package CON_PROC is
  1345.    
  1346.    procedure CONSTRUCTION_PROCESSING;
  1347.    
  1348. end CON_PROC;
  1349.  
  1350.  
  1351. -----------------------------------------------------------------
  1352. --           Package Body
  1353. -----------------------------------------------------------------
  1354.  
  1355. with TEXT_IO; use TEXT_IO, INTEGER_IO;
  1356. with HOST;
  1357. with AST_GRAPH;
  1358. with STR_PACK; use STR_PACK;
  1359. with CALENDAR;
  1360. with ENVIRONS;
  1361. with ARGUMENTS;
  1362.  
  1363. package body CON_PROC is 
  1364.  
  1365.       NO_CYCLES : BOOLEAN := TRUE;
  1366.       VIOLATORS            : AST_GRAPH.LINK_TO_LIST_OF_NAMES;
  1367.       CYCLE_NUMBER         : INTEGER := 1;
  1368.       DUMP_IT              : STRING(1..14);
  1369.  
  1370.  
  1371. -------------------------------------------------------------------
  1372. --   Package PROCESSING_INTERNALS is an internal package, used only
  1373. --   by the procedure CONSTRUCTION_PROCESSING. The subprograms in
  1374. --   this package are isolated in a sub-package simply to avoid
  1375. --   a large number of nested subprograms inside the procedure
  1376. --   CONSTRUCTION_PROCESSING.
  1377. --   
  1378. --   "Still needed":
  1379. --   
  1380. --   1) The procedures in this package make heavy use of access types
  1381. --      and allocators (x := new wazoo...). There is no mechanism for
  1382. --      storage reclamation. Instead of performing an allocation each
  1383. --      time a new value is required, a list of objects should be
  1384. --      maintained, marked "free" or "used".  When a new object is
  1385. --      required, the appropriate list would first be searched, to
  1386. --      see if a free object was available. If so, it is marked
  1387. --      used, and the new values put in. The access type object that
  1388. --      "points" to this object would be given the location of this
  1389. --      object as its value.  If no free objects are available, a
  1390. --      new one is allocated and linked into the list of free and
  1391. --      used objects.
  1392. --   
  1393. --   2) The use of the datat structure "NAME_BLOCK_LIST" is 
  1394. --      redundant; the data structure AST_GRAPH.LIST_OF_NAMES
  1395. --      would serve equally well.  
  1396.  
  1397.       package PROCESSING_INTERNALS is 
  1398.  
  1399.          type RELEVANT_RULE_LIST;
  1400.          type LINK_TO_RELEVANT_RULE_LIST is access RELEVANT_RULE_LIST;
  1401.          type RELEVANT_RULE_LIST is 
  1402.             record                  
  1403.                NEXT, PREVIOUS : LINK_TO_RELEVANT_RULE_LIST;
  1404.                RULE           : AST_GRAPH.LINK_TO_RULE_DESCRIPTION;
  1405.             end record;
  1406.    
  1407.          type NAME_BLOCK_LIST;
  1408.          type LINK_TO_NAME_BLOCK_LIST is access NAME_BLOCK_LIST;
  1409.          type NAME_BLOCK_LIST is    -- This will be changed, to faciliate
  1410.             record                  -- storage reclamation.
  1411.                NEXT, PREVIOUS : LINK_TO_NAME_BLOCK_LIST;
  1412.                NAME           : AST_GRAPH.LINK_TO_NAME_BLOCK;
  1413.             end record;
  1414.    
  1415.  
  1416.  
  1417.          procedure INITIALIZE_ALL_PROCESSING_DATA_STRUCTURES;
  1418.  
  1419.          procedure PLACE_ALL_TARGETS_IN_NAME_BLOCK_QUEUE;
  1420.  
  1421.          function NAME_BLOCK_QUEUE_IS_NOT_EMPTY return BOOLEAN;
  1422.  
  1423.          function NEXT_NAME_BLOCK_ON_QUEUE return 
  1424.                AST_GRAPH.LINK_TO_NAME_BLOCK;
  1425.  
  1426.          function LIST_OF_DEFINING_RULES
  1427.                (NAME : in AST_GRAPH.LINK_TO_NAME_BLOCK)
  1428.                 return AST_GRAPH.LINK_TO_LIST_OF_RULES;
  1429.          
  1430.          function THERE_IS_ANOTHER_RULE 
  1431.                (RULE_LIST : in AST_GRAPH.LINK_TO_LIST_OF_RULES)
  1432.                return BOOLEAN;
  1433.          
  1434.          function RELEVANT_RULE_STACK_IS_NOT_EMPTY 
  1435.                return BOOLEAN;
  1436.  
  1437.          function NEXT_RULE 
  1438.                (RULE_LIST : in AST_GRAPH.LINK_TO_LIST_OF_RULES)
  1439.                return AST_GRAPH.LINK_TO_LIST_OF_RULES;
  1440.  
  1441.          function NEXT_RELEVANT_RULE_ON_QUEUE 
  1442.              return AST_GRAPH.LINK_TO_RULE_DESCRIPTION;
  1443.  
  1444.          procedure ADD_TO_RELEVANT_RULE_STACK
  1445.                (NEW_RULE  : in AST_GRAPH.LINK_TO_RULE_DESCRIPTION);
  1446.          
  1447.          procedure ADD_DEPENDENTS_TO_NAME_BLOCK_QUEUE
  1448.                (RULE : in AST_GRAPH.LINK_TO_RULE_DESCRIPTION);
  1449.  
  1450.          function TIMESTAMPS_OUT_OF_SEQUENCE
  1451.                (RULE : in AST_GRAPH.LINK_TO_RULE_DESCRIPTION)
  1452.                return BOOLEAN ;
  1453.  
  1454.          procedure EXECUTE_THE_COMMAND_LIST
  1455.                (CMD_LIST : in AST_GRAPH.LINK_TO_LIST_OF_COMMANDS);
  1456.  
  1457.          procedure TOUCH_ALL_TARGETS 
  1458.              (RULE : in AST_GRAPH.LINK_TO_RULE_DESCRIPTION);
  1459.  
  1460.         procedure PUT_DYNAMIC_STRING (DSTRING : in DYNAMIC_STRING);
  1461.          
  1462.  
  1463.    end PROCESSING_INTERNALS; -- end of package spec
  1464.  
  1465.  
  1466.    package body PROCESSING_INTERNALS is 
  1467.  
  1468.  
  1469.       RELEVANT_RULE_STACK   : LINK_TO_RELEVANT_RULE_LIST;
  1470.       NAME_BLOCK_QUEUE      : LINK_TO_NAME_BLOCK_LIST;
  1471.  
  1472.  
  1473.       procedure PUT_DYNAMIC_STRING (DSTRING : in DYNAMIC_STRING) is
  1474.         TMP : STRING (1 .. LENGTH(DSTRING) ); 
  1475.       begin 
  1476.         CONVERT_TO_STRING (DSTRING,TMP); 
  1477.         PUT(TMP); 
  1478.       end PUT_DYNAMIC_STRING;
  1479.  
  1480.  
  1481.  
  1482.       procedure INITIALIZE_ALL_PROCESSING_DATA_STRUCTURES is
  1483.  
  1484.           TMP_NAME_BLOCK  : AST_GRAPH.LINK_TO_NAME_BLOCK;
  1485.  
  1486.       begin
  1487.  
  1488.           RELEVANT_RULE_STACK  := null;
  1489.           NAME_BLOCK_QUEUE := null;
  1490.  
  1491.           TMP_NAME_BLOCK := AST_GRAPH.NAME_LIST;
  1492.  
  1493.           while TMP_NAME_BLOCK.PREVIOUS_NAME_BLOCK /= null loop
  1494.              TMP_NAME_BLOCK.TOUCHED := FALSE;
  1495.              TMP_NAME_BLOCK := TMP_NAME_BLOCK.PREVIOUS_NAME_BLOCK;
  1496.           end loop;
  1497.  
  1498.           TMP_NAME_BLOCK.TOUCHED := FALSE;
  1499.  
  1500.       end INITIALIZE_ALL_PROCESSING_DATA_STRUCTURES;
  1501.       
  1502.  
  1503.  
  1504.       procedure PLACE_ALL_TARGETS_IN_NAME_BLOCK_QUEUE is
  1505.           TMP_NAME_BLOCK      : AST_GRAPH.LINK_TO_NAME_BLOCK;
  1506.           TMP_TARGET_ARG, GOAL  : STR_PACK.DYNAMIC_STRING;
  1507.           SEP                 : CHARACTER;
  1508.  
  1509.       begin
  1510.  
  1511.           if STR_PACK.LENGTH (ARGUMENTS.TARGET_ARG) = 0 then -- default goal
  1512.              TMP_NAME_BLOCK := AST_GRAPH.NAME_LIST;
  1513.  
  1514.              while TMP_NAME_BLOCK.PREVIOUS_NAME_BLOCK /= null loop
  1515.                 TMP_NAME_BLOCK := TMP_NAME_BLOCK.PREVIOUS_NAME_BLOCK;
  1516.              end loop;
  1517.  
  1518.              NAME_BLOCK_QUEUE := new NAME_BLOCK_LIST;
  1519.              NAME_BLOCK_QUEUE.NAME := TMP_NAME_BLOCK;
  1520.  
  1521.           else   -- NOT default goal, 1 or more given...
  1522.  
  1523.              STR_PACK.ASSIGN (TMP_TARGET_ARG,STR_PACK.EMPTY_STR);
  1524.              STR_PACK.ASSIGN (TMP_TARGET_ARG,ARGUMENTS.TARGET_ARG);
  1525.  
  1526.              loop   -- Until an invalid Goal or end of list...
  1527.                 GET_TOKEN (TMP_TARGET_ARG, GOAL, SEP);
  1528.  
  1529.                 TMP_NAME_BLOCK := AST_GRAPH.NAME_LIST;
  1530.  
  1531.                 while TMP_NAME_BLOCK.PREVIOUS_NAME_BLOCK /= null loop
  1532.                    exit when TMP_NAME_BLOCK.NAME_VALUE = GOAL;
  1533.                    TMP_NAME_BLOCK := TMP_NAME_BLOCK.PREVIOUS_NAME_BLOCK;
  1534.                 end loop;
  1535.    
  1536.                 if GOAL /= TMP_NAME_BLOCK.NAME_VALUE then
  1537.                    NEW_LINE;
  1538.                    PUT("CONSTRUCT: ");
  1539.                    PUT_DYNAMIC_STRING (GOAL);
  1540.                    PUT_LINE(" is NOT a valid target in the CO.");
  1541.                    NEW_LINE;
  1542.                    raise ENVIRONS.ERROR;
  1543.                 end if;
  1544.  
  1545.                 if NAME_BLOCK_QUEUE = null then
  1546.                    NAME_BLOCK_QUEUE := new NAME_BLOCK_LIST;
  1547.                    NAME_BLOCK_QUEUE.NAME := TMP_NAME_BLOCK;
  1548.                 else
  1549.                    NAME_BLOCK_QUEUE.PREVIOUS := new NAME_BLOCK_LIST;
  1550.                    NAME_BLOCK_QUEUE.PREVIOUS.NAME := TMP_NAME_BLOCK;
  1551.                    NAME_BLOCK_QUEUE.PREVIOUS.NEXT := NAME_BLOCK_QUEUE;
  1552.                    NAME_BLOCK_QUEUE := NAME_BLOCK_QUEUE.PREVIOUS;
  1553.                 end if; -- NAME_BLOCK_QUEUE = null 
  1554.  
  1555.  
  1556.              end loop; -- Until an invalid Goal or end of list...
  1557.    
  1558.           end if;  -- if STR_PACK.LENGTH (TMP_TARGET_ARG) = 0 
  1559.    
  1560.       exception
  1561.  
  1562.          when STR_PACK.END_OF_STRING =>
  1563.             null; -- End of TARGET_ARG list was found
  1564.          when others =>  -- Invalid Goal or unknown trouble...
  1565.             raise;
  1566.          
  1567.       end PLACE_ALL_TARGETS_IN_NAME_BLOCK_QUEUE;
  1568.       
  1569.  
  1570.       function NAME_BLOCK_QUEUE_IS_NOT_EMPTY return BOOLEAN is
  1571.       begin
  1572.          return (NAME_BLOCK_QUEUE /= null);
  1573.       end NAME_BLOCK_QUEUE_IS_NOT_EMPTY ;
  1574.  
  1575.       
  1576.       function NEXT_NAME_BLOCK_ON_QUEUE return 
  1577.  
  1578.          AST_GRAPH.LINK_TO_NAME_BLOCK is
  1579.          TMP : LINK_TO_NAME_BLOCK_LIST;
  1580.  
  1581.       begin
  1582.  
  1583.           TMP := NAME_BLOCK_QUEUE;
  1584.  
  1585.           while TMP.NEXT /= null loop -- Find end of queue
  1586.              TMP := TMP.NEXT;
  1587.           end loop;
  1588.  
  1589.           if TMP.PREVIOUS /= null then
  1590.              TMP.PREVIOUS.NEXT := null; -- Remove the last entry on the queue
  1591.           else  --last entry in queue
  1592.              NAME_BLOCK_QUEUE := null;
  1593.           end if;
  1594.  
  1595.           return TMP.NAME;
  1596.  
  1597.       end NEXT_NAME_BLOCK_ON_QUEUE;
  1598.       
  1599.  
  1600.       function LIST_OF_DEFINING_RULES
  1601.              (NAME : in AST_GRAPH.LINK_TO_NAME_BLOCK)
  1602.               return AST_GRAPH.LINK_TO_LIST_OF_RULES is
  1603.  
  1604.          TMP : AST_GRAPH.LINK_TO_LIST_OF_RULES;
  1605.  
  1606.       begin
  1607.  
  1608.          TMP := new AST_GRAPH.LIST_OF_RULES;
  1609.          TMP.all := NAME.DEFINING_RULES.all;
  1610.          return TMP;
  1611.  
  1612.       end LIST_OF_DEFINING_RULES;
  1613.           
  1614.  
  1615.       function THERE_IS_ANOTHER_RULE 
  1616.             (RULE_LIST : in AST_GRAPH.LINK_TO_LIST_OF_RULES)
  1617.             return BOOLEAN is
  1618.  
  1619.       begin
  1620.  
  1621.          return (RULE_LIST /= null  and then 
  1622.                  RULE_LIST.PREVIOUS_RULE /= null);
  1623.  
  1624.       end THERE_IS_ANOTHER_RULE;
  1625.       
  1626.  
  1627.  
  1628.       function RELEVANT_RULE_STACK_IS_NOT_EMPTY 
  1629.             return BOOLEAN is
  1630.       begin
  1631.          return RELEVANT_RULE_STACK /= null;
  1632.       end RELEVANT_RULE_STACK_IS_NOT_EMPTY;
  1633.       
  1634.       
  1635.  
  1636.       function NEXT_RELEVANT_RULE_ON_QUEUE 
  1637.              return AST_GRAPH.LINK_TO_RULE_DESCRIPTION is
  1638.          TMP : LINK_TO_RELEVANT_RULE_LIST;
  1639.       begin
  1640.          TMP := RELEVANT_RULE_STACK;
  1641.          if RELEVANT_RULE_STACK.NEXT = null then  -- only one on queue
  1642.             RELEVANT_RULE_STACK := null;
  1643.          else 
  1644.             -- Remove this rule from queue
  1645.             RELEVANT_RULE_STACK := RELEVANT_RULE_STACK.NEXT;
  1646.             RELEVANT_RULE_STACK.PREVIOUS := null;
  1647.          end if;
  1648.  
  1649.          return TMP.RULE;
  1650.       end NEXT_RELEVANT_RULE_ON_QUEUE;
  1651.       
  1652.  
  1653.       function NEXT_RULE 
  1654.             (RULE_LIST : in AST_GRAPH.LINK_TO_LIST_OF_RULES)
  1655.             return AST_GRAPH.LINK_TO_LIST_OF_RULES is
  1656.          TMP : AST_GRAPH.LINK_TO_LIST_OF_RULES;
  1657.       begin
  1658.          TMP := new AST_GRAPH.LIST_OF_RULES;
  1659.          TMP.all := RULE_LIST.PREVIOUS_RULE.all;  -- Change to NEXT_RULE
  1660.          return TMP;                   -- when AST_GRAPH changes
  1661.       end NEXT_RULE;
  1662.       
  1663.  
  1664.       procedure ADD_TO_RELEVANT_RULE_STACK
  1665.             (NEW_RULE  : in AST_GRAPH.LINK_TO_RULE_DESCRIPTION) is
  1666.  
  1667.         TMP : LINK_TO_RELEVANT_RULE_LIST;
  1668.         NEW_RULE_LIST : LINK_TO_RELEVANT_RULE_LIST;
  1669.       begin
  1670.          if RELEVANT_RULE_STACK = null then  -- First entry in list...
  1671.             RELEVANT_RULE_STACK := new RELEVANT_RULE_LIST;
  1672.             RELEVANT_RULE_STACK.RULE := new AST_GRAPH.RULE_DESCRIPTION;
  1673.             RELEVANT_RULE_STACK.RULE.all  := NEW_RULE.all;
  1674.          else
  1675.             NEW_RULE_LIST := new RELEVANT_RULE_LIST; --Insert at head of queue
  1676.             RELEVANT_RULE_STACK.PREVIOUS := NEW_RULE_LIST;
  1677.             NEW_RULE_LIST.RULE := new AST_GRAPH.RULE_DESCRIPTION;
  1678.             NEW_RULE_LIST.RULE.all  := NEW_RULE.all;
  1679.             NEW_RULE_LIST.NEXT := RELEVANT_RULE_STACK;
  1680.             RELEVANT_RULE_STACK := NEW_RULE_LIST;
  1681.  
  1682.             -- If this rule was already on the queue, unlink the
  1683.             -- earlier entry.
  1684.                  TMP := RELEVANT_RULE_STACK.NEXT;
  1685.  
  1686.                  while TMP /= null loop
  1687.                     if TMP.RULE.CO_LINE_NUMBER = NEW_RULE.CO_LINE_NUMBER then
  1688.                        TMP.PREVIOUS.NEXT := TMP.NEXT;
  1689.                        if TMP.NEXT /= null then -- If not at end of queue
  1690.                           TMP.NEXT.PREVIOUS := TMP.PREVIOUS;
  1691.                        end if;
  1692.                        exit;
  1693.                     end if;
  1694.                   TMP := TMP.NEXT;
  1695.                end loop;   -- while TMP /= null
  1696.  
  1697.          end if;  -- RELEVANT_RULE_STACK.RULE = null 
  1698.  
  1699.       end ADD_TO_RELEVANT_RULE_STACK;
  1700.  
  1701.  
  1702.       procedure ADD_TO_NAME_BLOCK_QUEUE
  1703.             (NAME_BLOCK  : in AST_GRAPH.LINK_TO_NAME_BLOCK) is
  1704.  
  1705.          NEW_NAME_BLOCK  : LINK_TO_NAME_BLOCK_LIST;
  1706.       begin
  1707.          if NAME_BLOCK_QUEUE = null then  
  1708.             NAME_BLOCK_QUEUE := new NAME_BLOCK_LIST;
  1709.             NAME_BLOCK_QUEUE.NAME := NAME_BLOCK;
  1710.          else
  1711.             NEW_NAME_BLOCK := new NAME_BLOCK_LIST;
  1712.             NAME_BLOCK_QUEUE.PREVIOUS := NEW_NAME_BLOCK;
  1713.             NEW_NAME_BLOCK.NAME := NAME_BLOCK;
  1714.             NEW_NAME_BLOCK.NEXT := NAME_BLOCK_QUEUE;
  1715.             NAME_BLOCK_QUEUE := NEW_NAME_BLOCK;
  1716.          end if;
  1717.       end ADD_TO_NAME_BLOCK_QUEUE;
  1718.       
  1719.  
  1720.       procedure ADD_DEPENDENTS_TO_NAME_BLOCK_QUEUE
  1721.             (RULE : in AST_GRAPH.LINK_TO_RULE_DESCRIPTION) is
  1722.          DEPENDENTS_LIST  : AST_GRAPH.LINK_TO_LIST_OF_NAMES;
  1723.       begin
  1724.          DEPENDENTS_LIST := new AST_GRAPH.LIST_OF_NAMES;
  1725.          DEPENDENTS_LIST.all := RULE.DEPENDENTS.all;
  1726.          while DEPENDENTS_LIST /= null loop
  1727.             ADD_TO_NAME_BLOCK_QUEUE (DEPENDENTS_LIST.NAME);
  1728.             DEPENDENTS_LIST := DEPENDENTS_LIST.PREVIOUS_NAME; -- Change to
  1729.                         --.NEXT_NAME when AST_GRAPH is changed.
  1730.             end loop;
  1731.       end ADD_DEPENDENTS_TO_NAME_BLOCK_QUEUE;
  1732.       
  1733.       function TIMESTAMPS_OUT_OF_SEQUENCE
  1734.             (RULE : in AST_GRAPH.LINK_TO_RULE_DESCRIPTION)
  1735.             return BOOLEAN is
  1736.          TARGET_LIST, DEPENDENT_LIST : AST_GRAPH.LINK_TO_LIST_OF_NAMES;
  1737.          OLDEST_TARGET : CALENDAR.TIME;
  1738.          OUT_OF_SEQUENCE : BOOLEAN := FALSE;
  1739.          RETURN_TRUE     : exception;
  1740.  
  1741.       begin
  1742.  
  1743.          -- Determine the "oldest" of the targets for this rule
  1744.          TARGET_LIST := new AST_GRAPH.LIST_OF_NAMES;
  1745.          TARGET_LIST.all := RULE.TARGETS.all;
  1746.  
  1747.          begin  -- block statement used here to trap exceptions
  1748.             OLDEST_TARGET := HOST.MODIFICATION_TIMESTAMP 
  1749.                             (TARGET_LIST.NAME.NAME_VALUE);
  1750.          exception
  1751.             when HOST.ACCESSIBILITY_ERROR =>
  1752.                -- This is not an error, but the rule clearly must be "fired"
  1753.                raise RETURN_TRUE;
  1754.             when others =>
  1755.                PUT("CONSTRUCT: Error while trying to access ");
  1756.                PUT_DYNAMIC_STRING(TARGET_LIST.NAME.NAME_VALUE);
  1757.                NEW_LINE;
  1758.                raise ENVIRONS.ERROR;
  1759.          end;  -- of block statement used to trap exceptions
  1760.  
  1761.          TARGET_LIST := TARGET_LIST.PREVIOUS_NAME; 
  1762.  
  1763.          while TARGET_LIST /= null loop 
  1764.  
  1765.                begin  -- block statement used here to trap exceptions
  1766.  
  1767.                if HOST.MODIFICATION_TIMESTAMP (TARGET_LIST.NAME.NAME_VALUE) 
  1768.                          < OLDEST_TARGET then
  1769.                   OLDEST_TARGET := HOST.MODIFICATION_TIMESTAMP 
  1770.                                 (TARGET_LIST.NAME.NAME_VALUE);
  1771.                end if;
  1772.  
  1773.                exception
  1774.                   when HOST.ACCESSIBILITY_ERROR =>
  1775.                      -- This is not an error, but the rule must be "fired"
  1776.                      raise RETURN_TRUE;
  1777.                   when others =>
  1778.                      PUT("CONSTRUCT: Error while trying to access ");
  1779.                      PUT_DYNAMIC_STRING(TARGET_LIST.NAME.NAME_VALUE);
  1780.                      NEW_LINE;
  1781.                      raise ENVIRONS.ERROR;
  1782.                end;  -- of block statement used to trap exceptions
  1783.  
  1784.  
  1785.             TARGET_LIST := TARGET_LIST.PREVIOUS_NAME; 
  1786.          end loop;
  1787.  
  1788.          -- Now see if anything is out of sequence (or "touched").
  1789.  
  1790.          DEPENDENT_LIST := new AST_GRAPH.LIST_OF_NAMES;
  1791.          DEPENDENT_LIST.all := RULE.DEPENDENTS.all;
  1792.  
  1793.          begin  -- block statement used here to trap exceptions
  1794.  
  1795.             if (DEPENDENT_LIST.NAME.TOUCHED  or else
  1796.                HOST.MODIFICATION_TIMESTAMP (DEPENDENT_LIST.NAME.NAME_VALUE)
  1797.                   > OLDEST_TARGET) then
  1798.                   raise RETURN_TRUE;
  1799.             end if;
  1800.  
  1801.          exception
  1802.             when RETURN_TRUE => raise; --"pass it on"
  1803.             -- At this point, all dependents should exist; if one 
  1804.             -- does not, it is an error.
  1805.             when others =>
  1806.                PUT("CONSTRUCT: Error while trying to access ");
  1807.                PUT_DYNAMIC_STRING(DEPENDENT_LIST.NAME.NAME_VALUE);
  1808.                NEW_LINE;
  1809.                raise ENVIRONS.ERROR;
  1810.          end;  -- of block statement used to trap exceptions
  1811.  
  1812.  
  1813.          DEPENDENT_LIST := DEPENDENT_LIST.PREVIOUS_NAME;
  1814.  
  1815.          while (DEPENDENT_LIST /= null )  loop
  1816.  
  1817.          begin  -- block statement used here to trap exceptions
  1818.  
  1819.             if (DEPENDENT_LIST.NAME.TOUCHED  or
  1820.                HOST.MODIFICATION_TIMESTAMP (DEPENDENT_LIST.NAME.NAME_VALUE)
  1821.                   > OLDEST_TARGET) then
  1822.                   raise RETURN_TRUE;
  1823.             end if;
  1824.  
  1825.          exception
  1826.             when RETURN_TRUE => raise; --"pass it on"
  1827.             -- At this point, all dependents should exist; if one 
  1828.             -- does not, it is an error.
  1829.             when others =>
  1830.                PUT("CONSTRUCT: Error while trying to access ");
  1831.                PUT_DYNAMIC_STRING(DEPENDENT_LIST.NAME.NAME_VALUE);
  1832.                NEW_LINE;
  1833.                raise ENVIRONS.ERROR;
  1834.          end;  -- of block statement used to trap exceptions
  1835.  
  1836.             DEPENDENT_LIST := DEPENDENT_LIST.PREVIOUS_NAME; 
  1837.          end loop;
  1838.  
  1839.          -- If control gets here, all of the timestamps are in sequence
  1840.          return (FALSE);
  1841.  
  1842.       exception
  1843.          when RETURN_TRUE =>
  1844.             return (TRUE);
  1845.          when others =>
  1846.             raise;
  1847.       end TIMESTAMPS_OUT_OF_SEQUENCE;
  1848.       
  1849.  
  1850.  
  1851.       procedure EXECUTE_A_COMMAND 
  1852.             (COMMAND : in STR_PACK.DYNAMIC_STRING) is
  1853.       begin
  1854.          if ARGUMENTS.IS_SET (ARGUMENTS.CMD_PRINT) then
  1855.             PUT(">>");
  1856.             PUT_DYNAMIC_STRING (COMMAND);
  1857.             NEW_LINE;
  1858.          end if;
  1859.  
  1860.          begin -- Block statement to catch exceptions
  1861.             HOST.EXECUTE_CMDS (COMMAND);
  1862.          exception
  1863.             when HOST.EXECUTION_ERROR =>
  1864.                NEW_LINE;
  1865.                PUT("CONSTRUCT: Error while trying to execute: ");
  1866.                PUT_DYNAMIC_STRING (COMMAND);
  1867.                NEW_LINE;
  1868.                raise ENVIRONS.ERROR;
  1869.          end; -- of block statement
  1870.  
  1871.       end EXECUTE_A_COMMAND;
  1872.  
  1873.       procedure EXECUTE_THE_COMMAND_LIST
  1874.             (CMD_LIST : in AST_GRAPH.LINK_TO_LIST_OF_COMMANDS) is
  1875.          TMP_CMD_LIST : AST_GRAPH.LINK_TO_LIST_OF_COMMANDS;
  1876.       begin
  1877.  
  1878.          TMP_CMD_LIST := CMD_LIST;
  1879.          while TMP_CMD_LIST /= null loop
  1880.             EXECUTE_A_COMMAND (TMP_CMD_LIST.COMMAND);
  1881.             TMP_CMD_LIST := TMP_CMD_LIST.NEXT_COMMAND;
  1882.          end loop;
  1883.  
  1884.  
  1885.       end EXECUTE_THE_COMMAND_LIST;
  1886.  
  1887.  
  1888.       procedure TOUCH_ALL_TARGETS 
  1889.              (RULE : in AST_GRAPH.LINK_TO_RULE_DESCRIPTION) is
  1890.          LIST_OF_TARGETS : AST_GRAPH.LINK_TO_LIST_OF_NAMES;
  1891.          CMDS   : AST_GRAPH.LINK_TO_LIST_OF_COMMANDS;
  1892.  
  1893.       begin
  1894.  
  1895.          if ARGUMENTS.IS_SET (ARGUMENTS.CMD_PRINT) then
  1896.             CMDS := RULE.TOP_OF_COMMANDS;
  1897.             while CMDS /= null loop
  1898.               PUT(">> ");
  1899.               PUT_DYNAMIC_STRING (CMDS.COMMAND);
  1900.               NEW_LINE;
  1901.               CMDS := CMDS.NEXT_COMMAND;
  1902.             end loop;
  1903.          end if;
  1904.  
  1905.          LIST_OF_TARGETS := RULE.TARGETS;
  1906.          while LIST_OF_TARGETS /= null loop
  1907.             LIST_OF_TARGETS.NAME.TOUCHED := TRUE;
  1908.             LIST_OF_TARGETS := LIST_OF_TARGETS.PREVIOUS_NAME; -- Change
  1909.                       -- to NEXT_NAME when AST_GRAPH is changed.
  1910.          end loop;
  1911.       end TOUCH_ALL_TARGETS;
  1912.  
  1913.       
  1914.    end PROCESSING_INTERNALS; -- end of package body
  1915.  
  1916.       -------------------------------------------------------------------
  1917.  
  1918. use PROCESSING_INTERNALS;
  1919.  
  1920.       function COUNT_OF_RELATIONS
  1921.        (PARENT    : in  AST_GRAPH.LINK_TO_NAME_BLOCK)   
  1922.         return INTEGER is
  1923.        COUNT        : INTEGER;
  1924.        DEPENDENT    : AST_GRAPH.LINK_TO_LIST_OF_NAMES;
  1925.        RULE         : AST_GRAPH.LINK_TO_LIST_OF_RULES;
  1926.       begin
  1927.        COUNT := 0;
  1928.        RULE  := PARENT.DEFINING_RULES;
  1929.        while RULE /= null loop
  1930.          DEPENDENT := RULE.RULE.DEPENDENTS;
  1931.          
  1932.          while DEPENDENT /= null loop
  1933.            COUNT      := COUNT+1;
  1934.            DEPENDENT  := DEPENDENT.PREVIOUS_NAME;
  1935.          end loop;
  1936.     
  1937.          RULE := RULE.PREVIOUS_RULE;
  1938.        end loop;
  1939.        return COUNT;
  1940.       end COUNT_OF_RELATIONS;
  1941.  
  1942.  
  1943.  
  1944.   procedure REDUCE_COUNTS_OF_PARENTS
  1945.     (USES : in  AST_GRAPH.LINK_TO_LIST_OF_RULES) is
  1946.     PARENT   : AST_GRAPH.LINK_TO_LIST_OF_NAMES;
  1947.     USE_ONE  : AST_GRAPH.LINK_TO_LIST_OF_RULES;
  1948.   begin
  1949.     USE_ONE := USES;
  1950.     while USE_ONE /= null loop
  1951.       PARENT := USE_ONE.RULE.TARGETS;
  1952.       while PARENT /= null loop
  1953.        PARENT.NAME.NUMBER_OF_DEPENDENTS := PARENT.NAME.NUMBER_OF_DEPENDENTS-1;
  1954.  
  1955.        if ARGUMENTS.IS_SET(ARGUMENTS.PROCESS_DUMP) then
  1956.          PUT("REDUCE  ");
  1957.          STR_PACK.CONVERT_TO_STRING(PARENT.NAME.NAME_VALUE, DUMP_IT);
  1958.          PUT(DUMP_IT); PUT(PARENT.NAME.NUMBER_OF_DEPENDENTS); NEW_LINE;
  1959.        end if;
  1960.  
  1961.        PARENT                           := PARENT.PREVIOUS_NAME;
  1962.       end loop;
  1963.       USE_ONE := USE_ONE.PREVIOUS_RULE;
  1964.     end loop;
  1965.   end REDUCE_COUNTS_OF_PARENTS;
  1966.  
  1967.  
  1968.   procedure REMOVE_SAFE_NAMES is
  1969.     GRAPH            : AST_GRAPH.LINK_TO_NAME_BLOCK;
  1970.     SAFE_NAMES_EXIST : BOOLEAN := TRUE;
  1971.   begin
  1972.     --Loop through the graph removing all names that have zero
  1973.     --NUMBER_OF_DEPENDENTS.  When a name is removed reduce by one
  1974.     --the NUMBER_OF_DEPENDENTS counts for all names having this
  1975.     --name as a dependent.  The loop terminates when no names are
  1976.     --removed in any given pass.  Any names not removed are part
  1977.     --of a cycle.
  1978.     while SAFE_NAMES_EXIST loop
  1979.       SAFE_NAMES_EXIST := FALSE;
  1980.       GRAPH            := AST_GRAPH.NAME_LIST;
  1981.  
  1982.       while GRAPH /= null loop
  1983.        if GRAPH.NUMBER_OF_DEPENDENTS = 0 then
  1984.          SAFE_NAMES_EXIST := TRUE;
  1985.  
  1986.          if ARGUMENTS.IS_SET(ARGUMENTS.PROCESS_DUMP) then
  1987.            PUT("REMOVE  ");
  1988.            STR_PACK.CONVERT_TO_STRING(GRAPH.NAME_VALUE, DUMP_IT);
  1989.            PUT(DUMP_IT); PUT(GRAPH.NUMBER_OF_DEPENDENTS); NEW_LINE;
  1990.          end if;
  1991.  
  1992.          REDUCE_COUNTS_OF_PARENTS( GRAPH.DEPENDENT_RULES );
  1993.          GRAPH.NUMBER_OF_DEPENDENTS := -1;
  1994.        end if;
  1995.        GRAPH := GRAPH.PREVIOUS_NAME_BLOCK;
  1996.       end loop;
  1997.  
  1998.     end loop;
  1999.   end REMOVE_SAFE_NAMES;
  2000.  
  2001.  
  2002.  
  2003.   procedure ADD
  2004.     (NAME  : in     AST_GRAPH.LINK_TO_NAME_BLOCK;
  2005.      LIST  : in out AST_GRAPH.LINK_TO_LIST_OF_NAMES;
  2006.      EXISTS: out    AST_GRAPH.LINK_TO_LIST_OF_NAMES) is
  2007.     EXISTING_NAME :AST_GRAPH.LINK_TO_LIST_OF_NAMES;
  2008.   begin
  2009.     EXISTS := null;
  2010.     --Search for already existing names
  2011.     EXISTING_NAME := LIST;
  2012.     while EXISTING_NAME /= null loop
  2013.       if EXISTING_NAME.NAME = NAME then
  2014.        EXISTS := EXISTING_NAME;
  2015.        exit;
  2016.       end if;
  2017.       EXISTING_NAME := EXISTING_NAME.PREVIOUS_NAME;
  2018.     end loop;
  2019.  
  2020.     if EXISTS = null then
  2021.       --Add name to the list.
  2022.       EXISTING_NAME               := new AST_GRAPH.LIST_OF_NAMES;
  2023.       EXISTING_NAME.NAME          := NAME;
  2024.       EXISTING_NAME.PREVIOUS_NAME := LIST;
  2025.       LIST                        := EXISTING_NAME;
  2026.     end if;
  2027.   end Add;
  2028.  
  2029.  
  2030.   procedure GET_OFFENDING_DEPENDENT
  2031.     (RULES    : in  AST_GRAPH.LINK_TO_LIST_OF_RULES;
  2032.      OFFENDER : out AST_GRAPH.LINK_TO_LIST_OF_NAMES) is
  2033.     CURRENT     :   AST_GRAPH.LINK_TO_LIST_OF_RULES;
  2034.     DEPENDENT   :   AST_GRAPH.LINK_TO_LIST_OF_NAMES;
  2035.   begin
  2036.     OFFENDER := null;
  2037.     CURRENT  := RULES;
  2038.     RULE_SEARCH: while CURRENT /= null loop
  2039.                   DEPENDENT := CURRENT.RULE.DEPENDENTS;
  2040.       DEP_SEARCH: while DEPENDENT /= null loop
  2041.                     if DEPENDENT.NAME.NUMBER_OF_DEPENDENTS > 0 and
  2042.                        DEPENDENT.CYCLE_DETECTED = FALSE        then
  2043.                       OFFENDER := DEPENDENT;
  2044.                       exit;
  2045.                     end if;
  2046.                     DEPENDENT := DEPENDENT.PREVIOUS_NAME;
  2047.                   end loop DEP_SEARCH;
  2048.  
  2049.                   CURRENT := CURRENT.PREVIOUS_RULE;
  2050.                 end loop RULE_SEARCH;
  2051.   end GET_OFFENDING_DEPENDENT;
  2052.  
  2053.  
  2054.  
  2055.   procedure PRINT_AND_REMOVE_CYCLE
  2056.     (CYCLE         : in AST_GRAPH.LINK_TO_LIST_OF_NAMES;
  2057.      OFFENDER_LIST : in AST_GRAPH.LINK_TO_LIST_OF_NAMES) is
  2058.     CURRENT   :    AST_GRAPH.LINK_TO_LIST_OF_NAMES;
  2059.     OFFENDER  : STRING(1..14);
  2060.     COLUMN    : INTEGER;
  2061.   begin
  2062.     --Break the chain at the end of the cycle and start at the top of cycle.
  2063.     if CYCLE /= null then
  2064.       CYCLE.PREVIOUS_NAME := null;
  2065.       NEW_LINE; PUT("CYCLE"); PUT(CYCLE_NUMBER, 3); PUT(":  ");
  2066.       STR_PACK.CONVERT_TO_STRING(CYCLE.NAME.NAME_VALUE, OFFENDER);
  2067.       PUT( OFFENDER(1..STR_PACK.LENGTH(CYCLE.NAME.NAME_VALUE)) );
  2068.       COLUMN       := 11 + STR_PACK.LENGTH(CYCLE.NAME.NAME_VALUE);
  2069.       CYCLE_NUMBER := CYCLE_NUMBER+1;
  2070.     end if;
  2071.  
  2072.     --Each name on the cycle list is part of a cycle.  Here we print the
  2073.     --cycle and reduce the appropriate dependency counts so that this
  2074.     --cycle will not be detected again.
  2075.     CURRENT             := OFFENDER_LIST;
  2076.     while CURRENT /= null loop
  2077.       if COLUMN + STR_PACK.LENGTH(CURRENT.NAME.NAME_VALUE) + 4 > 80 then
  2078.        NEW_LINE; PUT("           ");
  2079.        COLUMN := 11;
  2080.       end if;
  2081.       COLUMN := COLUMN + STR_PACK.LENGTH(CURRENT.NAME.NAME_VALUE) + 4;
  2082.  
  2083.       PUT( " <= " );
  2084.       STR_PACK.CONVERT_TO_STRING(CURRENT.NAME.NAME_VALUE, OFFENDER);
  2085.       PUT( OFFENDER(1..STR_PACK.LENGTH(CURRENT.NAME.NAME_VALUE)) );
  2086.       CURRENT := CURRENT.PREVIOUS_NAME;
  2087.     end loop;
  2088.  
  2089.  
  2090.     --Now we must remove each name in the cycle whose dependency count has
  2091.     --gone to zero.
  2092.       REMOVE_SAFE_NAMES;
  2093.  
  2094.     --Remove offenders from the violator list when their counts go to zero.
  2095.     --Since it is difficult to remove from the middle of a LIST_OF_NAMES,
  2096.     --and since GET_OFFENDING_DEPENDENT will ignore adding violators
  2097.     --whose counts are zero, it is only necessary to remove violators from
  2098.     --the top of the list.
  2099.     while VIOLATORS.NAME.NUMBER_OF_DEPENDENTS <= 0 loop
  2100.       VIOLATORS := VIOLATORS.PREVIOUS_NAME;
  2101.       if VIOLATORS = null then
  2102.        exit;
  2103.       end if;
  2104.     end loop;
  2105.  
  2106.   end PRINT_AND_REMOVE_CYCLE;
  2107.  
  2108.  
  2109.   procedure CYC_CHECK is
  2110.     OFFENDER_LIST  : AST_GRAPH.LINK_TO_LIST_OF_NAMES;
  2111.     CYCLE_FOUND    : AST_GRAPH.LINK_TO_LIST_OF_NAMES;
  2112.     GRAPH          : AST_GRAPH.LINK_TO_NAME_BLOCK;
  2113.     OFFENDERS_DEPENDENT: AST_GRAPH.LINK_TO_LIST_OF_NAMES;
  2114.     OFFENDER           : AST_GRAPH.LINK_TO_NAME_BLOCK;
  2115.     NO_VIOLATORS       : BOOLEAN;
  2116.   begin
  2117.     
  2118.     --Traverse the graph and count the number of immediate dependents
  2119.     --for each name in the name list;
  2120.     GRAPH := AST_GRAPH.NAME_LIST;
  2121.     while GRAPH /= null loop
  2122.       GRAPH.NUMBER_OF_DEPENDENTS := COUNT_OF_RELATIONS( GRAPH );
  2123.  
  2124.       if ARGUMENTS.IS_SET(ARGUMENTS.PROCESS_DUMP) then
  2125.        STR_PACK.CONVERT_TO_STRING(GRAPH.NAME_VALUE, DUMP_IT);
  2126.        PUT(DUMP_IT); PUT(GRAPH.NUMBER_OF_DEPENDENTS); NEW_LINE;
  2127.       end if;
  2128.  
  2129.       GRAPH := GRAPH.PREVIOUS_NAME_BLOCK;
  2130.     end loop;
  2131.  
  2132.     REMOVE_SAFE_NAMES;
  2133.  
  2134.     --Now check the NAME_LIST and make a list of violators.
  2135.     GRAPH := AST_GRAPH.NAME_LIST;
  2136.     while GRAPH /= null loop
  2137.       if GRAPH.NUMBER_OF_DEPENDENTS > 0 then
  2138.  
  2139.        if ARGUMENTS.IS_SET(ARGUMENTS.PROCESS_DUMP) then
  2140.          PUT("VIOLATE ");
  2141.          STR_PACK.CONVERT_TO_STRING(GRAPH.NAME_VALUE, DUMP_IT);
  2142.          PUT(DUMP_IT); PUT(GRAPH.NUMBER_OF_DEPENDENTS); NEW_LINE;
  2143.        end if;
  2144.  
  2145.        ADD(GRAPH, VIOLATORS, CYCLE_FOUND);
  2146.       end if;
  2147.       GRAPH := GRAPH.PREVIOUS_NAME_BLOCK;
  2148.     end loop;
  2149.  
  2150.  
  2151.  
  2152.     if VIOLATORS = null then
  2153.       NO_CYCLES := TRUE; 
  2154.     else
  2155.       --Violators exist.  So find each cycle and print it.
  2156.       NO_CYCLES := FALSE;
  2157.       while VIOLATORS /= null loop
  2158.  
  2159.        if ARGUMENTS.IS_SET(ARGUMENTS.PROCESS_DUMP) then
  2160.          OFFENDER_LIST := VIOLATORS;
  2161.          while OFFENDER_LIST /= null loop
  2162.            STR_PACK.DUMP("VIOLATE ", OFFENDER_LIST.NAME.NAME_VALUE);
  2163.            PUT(OFFENDER_LIST.NAME.NUMBER_OF_DEPENDENTS, 3);
  2164.            OFFENDER_LIST := OFFENDER_LIST.PREVIOUS_NAME;
  2165.          end loop;
  2166.        end if;
  2167.  
  2168.        OFFENDER := VIOLATORS.NAME;
  2169.        ADD(OFFENDER, OFFENDER_LIST, CYCLE_FOUND);
  2170.        loop
  2171.          GET_OFFENDING_DEPENDENT(OFFENDER.DEFINING_RULES, OFFENDERS_DEPENDENT);
  2172.  
  2173.          if OFFENDERS_DEPENDENT = null then
  2174.            --This occurs only when an object's dependency count is non-zero
  2175.            --but all dependencies in its defining rules have been flagged as
  2176.            --having been detected in a cycle(this is caused by multiple
  2177.            --targets in the same rule).  In this case we remove this object
  2178.            --by setting its dependency count to zero and calling PRINT_AND_
  2179.            --REMOVE_CYCLE with null OFFENDER_LIST and CYCLE.
  2180.            OFFENDER.NUMBER_OF_DEPENDENTS := 0;
  2181.            OFFENDER_LIST := null;
  2182.            PRINT_AND_REMOVE_CYCLE(null, null);
  2183.            exit;
  2184.          end if;
  2185.  
  2186.          ADD(OFFENDERS_DEPENDENT.NAME, OFFENDER_LIST, CYCLE_FOUND);
  2187.          if CYCLE_FOUND /= null then
  2188.            OFFENDERS_DEPENDENT.CYCLE_DETECTED := TRUE;
  2189.            OFFENDER.NUMBER_OF_DEPENDENTS := OFFENDER.NUMBER_OF_DEPENDENTS-1;
  2190.            PRINT_AND_REMOVE_CYCLE(CYCLE_FOUND,OFFENDER_LIST);
  2191.            OFFENDER_LIST := null;
  2192.            exit;
  2193.          end if;
  2194.          OFFENDER := OFFENDERS_DEPENDENT.NAME;
  2195.        end loop;
  2196.       end loop;
  2197.     end if;
  2198.   end CYC_CHECK;
  2199.  
  2200.  
  2201.       procedure CONSTRUCTION_PROCESSING is
  2202.      
  2203.          CURRENT_NAME_BLOCK    : AST_GRAPH.LINK_TO_NAME_BLOCK;
  2204.           CURRENT_LIST_OF_RULES : AST_GRAPH.LINK_TO_LIST_OF_RULES;
  2205.           CURRENT_RULE          : AST_GRAPH.LINK_TO_RULE_DESCRIPTION;
  2206.       ALREADY_UP_TO_DATE    : BOOLEAN;
  2207.     
  2208.       begin
  2209.       --First check for the existence of cycles and continue only if
  2210.       --no cycles exist.
  2211.       CYC_CHECK;
  2212.       if NO_CYCLES = FALSE then
  2213.        return;
  2214.       end if;
  2215.  
  2216.       CURRENT_NAME_BLOCK    := new  AST_GRAPH.NAME_BLOCK;
  2217.       CURRENT_LIST_OF_RULES := new  AST_GRAPH.LIST_OF_RULES;
  2218.       CURRENT_RULE          := new  AST_GRAPH.RULE_DESCRIPTION;
  2219.  
  2220.  
  2221.       INITIALIZE_ALL_PROCESSING_DATA_STRUCTURES;
  2222.  
  2223.       PLACE_ALL_TARGETS_IN_NAME_BLOCK_QUEUE;
  2224.  
  2225.       while NAME_BLOCK_QUEUE_IS_NOT_EMPTY loop
  2226.             
  2227.         CURRENT_NAME_BLOCK := NEXT_NAME_BLOCK_ON_QUEUE;
  2228.  
  2229.         CURRENT_LIST_OF_RULES := 
  2230.                CURRENT_NAME_BLOCK.DEFINING_RULES;
  2231.         if CURRENT_LIST_OF_RULES /= null then
  2232.            CURRENT_RULE.all := CURRENT_LIST_OF_RULES.RULE.all;
  2233.            ADD_TO_RELEVANT_RULE_STACK (CURRENT_RULE);
  2234.            ADD_DEPENDENTS_TO_NAME_BLOCK_QUEUE (CURRENT_RULE);
  2235.         end if; -- CURRENT_LIST_OF_RULES /= null 
  2236.  
  2237.         while THERE_IS_ANOTHER_RULE (CURRENT_LIST_OF_RULES) loop
  2238.            CURRENT_LIST_OF_RULES := NEXT_RULE (CURRENT_LIST_OF_RULES);
  2239.            CURRENT_RULE.all := CURRENT_LIST_OF_RULES.RULE.all;
  2240.            ADD_TO_RELEVANT_RULE_STACK (CURRENT_RULE);
  2241.            ADD_DEPENDENTS_TO_NAME_BLOCK_QUEUE (CURRENT_RULE);
  2242.         end loop;  -- while THERE_IS_ANOTHER_RULE (CURRENT_LIST_OF_RULES)
  2243.  
  2244.       end loop;  -- while NAME_BLOCK_QUEUE_IS_NOT_EMPTY
  2245.  
  2246.       -- When execution gets here, all relevant rules have been partially
  2247.       -- ordered and placed in the RELEVANT_RULE_STACK.
  2248.       -- We now evaluate the rules on the stack.
  2249.       -- If a rule is "fired" (i.e. a condition is met that
  2250.       -- should result in the execution of the commands associated
  2251.       -- with that rule), the command list is executed and/or printed,
  2252.       -- depending on the options the user selected.
  2253.  
  2254.  
  2255.       ALREADY_UP_TO_DATE := TRUE;   -- ASSUME THERE IS NO WORK TO DO...
  2256.       while RELEVANT_RULE_STACK_IS_NOT_EMPTY loop
  2257.  
  2258.         CURRENT_RULE := NEXT_RELEVANT_RULE_ON_QUEUE;
  2259.  
  2260.         if TIMESTAMPS_OUT_OF_SEQUENCE (CURRENT_RULE) then
  2261.  
  2262.         ALREADY_UP_TO_DATE := FALSE;  -- THERE IS WORK TO DO...
  2263.  
  2264.             if ARGUMENTS.IS_SET (ARGUMENTS.CMD_PRINT) then
  2265.                NEW_LINE;
  2266.                PUT("CONSTRUCT: Processing commands for rule on CO line ");
  2267.                PUT(CURRENT_RULE.CO_LINE_NUMBER);
  2268.                NEW_LINE;
  2269.             end if;
  2270.  
  2271.             if ARGUMENTS.IS_SET (ARGUMENTS.EXECUTE) then
  2272.                EXECUTE_THE_COMMAND_LIST (CURRENT_RULE.TOP_OF_COMMANDS);
  2273.  
  2274.             else  -- Only "Touch" the targets
  2275.                TOUCH_ALL_TARGETS (CURRENT_RULE);
  2276.  
  2277.             end if;  --if ARGUMENTS.IS_SET (ARGUMENTS.EXECUTE)
  2278.  
  2279.          end if; -- if TIMESTAMPS_OUT_OF_SEQUENCE (CURRENT_RULE)
  2280.  
  2281.       end loop;  -- while RELEVANT_RULE_STACK_IS_NOT_EMPTY
  2282.  
  2283.       -- NOW THE ALL OF THE RELEVANT RULES HAVE BEEN EXAMINED, WERE
  2284.       -- ANY OF THEM OUT OF DATE?
  2285.       if  ALREADY_UP_TO_DATE then
  2286.      PUT_LINE("CONSTRUCT:  Target(s) is (are) already up to date.");
  2287.       elsif ARGUMENTS.IS_SET (ARGUMENTS.EXECUTE) then
  2288.      NEW_LINE;
  2289.      PUT_LINE("CONSTRUCT:  Target(s) is (are) now up to date.");
  2290.       end if;
  2291.  
  2292.  
  2293.    end CONSTRUCTION_PROCESSING; ----- End of the procedure body
  2294.  
  2295.  
  2296.  
  2297. end CON_PROC; -------- End of the package body
  2298.  
  2299.  
  2300. --::::::::::::::
  2301. --construct.text
  2302. --::::::::::::::
  2303. -- This is the main program for CONSTRUCT.  It controls a straight line flow
  2304. -- of execution.  PROMPT and AST_GRAPH are always called.  Then, depending on
  2305. -- the options selected, calls are made to any of the following: TOP_DOWN_GRAPH,-- BOTTOM_UP_GRAPH, DISPLAY, or CON_PROC.  Cycle checking is done under the
  2306. -- control of CON_PROC.  The main program handles the ERROR exception, aborting
  2307. -- the run with a generalized error to the user.  A specific error message is
  2308. -- expected from the routine at the time of raising error.
  2309. with BLD_GRAPH, TEXT_IO, DISPLAY, ARGUMENTS, CON_PROC, ENVIRONS;
  2310. use TEXT_IO;
  2311. procedure CONSTRUCT is
  2312.  
  2313. begin
  2314.  
  2315. NEW_LINE;
  2316. PUT_LINE("    THIS IS A PROTOTYPE VERSION OF CONSTRUCT:  VERSION 841201");
  2317. NEW_LINE;
  2318.  
  2319. ARGUMENTS.PROMPT; -- Enters the arguments checking for
  2320.                   -- user input errors.
  2321. BLD_GRAPH.BUILD;    -- Syntactically analyzes the Configuration Object
  2322.                   -- and builds a dependency graph.
  2323.  
  2324. if ARGUMENTS.IS_SET(ARGUMENTS.TOP_DOWN_GRAPH) then 
  2325.    DISPLAY.TOP_DOWN_GRAPH;
  2326. end if;
  2327.  
  2328. if ARGUMENTS.IS_SET(ARGUMENTS.BOTTOM_UP_GRAPH) then 
  2329.    DISPLAY.BOTTOM_UP_GRAPH;
  2330. end if;
  2331.  
  2332.  
  2333. if ARGUMENTS.IS_SET(ARGUMENTS.LIST) then
  2334.   DISPLAY.NAMES;
  2335. end if;
  2336.  
  2337. if ARGUMENTS.IS_SET(ARGUMENTS.EXECUTE) or
  2338.    ARGUMENTS.IS_SET(ARGUMENTS.CMD_PRINT) then
  2339.      CON_PROC.CONSTRUCTION_PROCESSING; -- Determines the minimal set of
  2340. end if;                                -- commands necessary to update a system
  2341.  
  2342. NEW_LINE;
  2343. PUT("Construct completed.");
  2344. NEW_LINE;
  2345.  
  2346. exception
  2347.   when ENVIRONS.ERROR =>
  2348.      PUT("Construct aborted due to error in execution.");
  2349.      NEW_LINE;
  2350.    when others => 
  2351.      NEW_LINE;
  2352.      PUT("Construct aborted due to unknown error.");
  2353.      NEW_LINE;
  2354.      raise;
  2355.  
  2356. end CONSTRUCT;
  2357.  
  2358.  
  2359. --::::::::::::::
  2360. --create_co.text
  2361. --::::::::::::::
  2362. -- THIS program reads several Ada compilation unit and builds a dependency
  2363. -- list which is readable by the tool CONSTRUCT.  A new Configuration
  2364. -- Object file is created each time the program is run.  CREATE_CO assumes
  2365. -- there are at most two compilation units per file and that these two must
  2366. -- be a specification and body pair.  CREATE_CO processes subprograms,
  2367. -- packages, subunits, and generics.
  2368. with STR_PACK, HOST, TEXT_IO, ENVIRONS;
  2369. use  STR_PACK,       TEXT_IO;
  2370. procedure CREATE_CO is
  2371.  
  2372.   type PGM_STATE is (START_UP      , SUBPROGRAM_SPEC, SUBPROGRAM_BODY,
  2373.                      PACKAGE_SPEC  , PACKAGE_BODY   , GENERIC_PARMS  );
  2374.   type CONTEXT_STATE is (WITH_CLAUSE, SEPARATE_CLAUSE);
  2375.   MAX_COLUMN        : CONSTANT INTEGER :=76;
  2376.   MAX_STRING        : CONSTANT INTEGER :=255;
  2377.   CURRENT_UNIT_NAME : STRING(1..MAX_STRING);
  2378.   CURRENT_UNIT_SIZE : NATURAL;
  2379.   PROGRAM_STATE : PGM_STATE;
  2380.   SOURCE_FILE   : FILE_TYPE;
  2381.   CO_FILE       : FILE_TYPE;
  2382.   D_TOKEN       : DYNAMIC_STRING;
  2383.   FILE_LIST     : DYNAMIC_STRING;
  2384.   UNIT_LIST     : DYNAMIC_STRING;
  2385.   TOKEN         : STRING(1..MAX_STRING);
  2386.   FNAME         : DYNAMIC_STRING;
  2387.   D_LINE        : DYNAMIC_STRING;
  2388.   FILENAME      : STRING(1..MAX_STRING);
  2389.   SEPARATOR     : CHARACTER;
  2390.   LINE          : STRING(1..MAX_STRING);
  2391.   LAST          : INTEGER;
  2392.   SPACE_COUNT   : INTEGER;
  2393.   FILE_LENGTH   : INTEGER;
  2394.   NAME_START    : INTEGER;
  2395.   SIZE          : NATURAL;
  2396.   IS_BODY       : BOOLEAN;
  2397.   IS_SPEC       : BOOLEAN;
  2398.   INDENT_COUNT  : INTEGER;
  2399.   BLANKS        : constant STRING(1..MAX_COLUMN) := 
  2400. "                                                                            ";
  2401.  
  2402.  
  2403.   -- This procedure checks each character of a string and changes all upper
  2404.   -- case letters to lower case.  Changes are made in place destroying the
  2405.   -- original string value.
  2406.   procedure LOWER(WORD : in out STRING) is
  2407.  
  2408.   begin
  2409.     for I in 1..WORD'LENGTH loop
  2410.       case WORD(I) is
  2411.         when 'A' => WORD(I) := 'a';
  2412.         when 'B' => WORD(I) := 'b';
  2413.         when 'C' => WORD(I) := 'c';
  2414.         when 'D' => WORD(I) := 'd';
  2415.         when 'E' => WORD(I) := 'e';
  2416.         when 'F' => WORD(I) := 'f';
  2417.         when 'G' => WORD(I) := 'g';
  2418.         when 'H' => WORD(I) := 'h';
  2419.         when 'I' => WORD(I) := 'i';
  2420.         when 'J' => WORD(I) := 'j';
  2421.         when 'K' => WORD(I) := 'k';
  2422.         when 'L' => WORD(I) := 'l';
  2423.         when 'M' => WORD(I) := 'm';
  2424.         when 'N' => WORD(I) := 'n';
  2425.         when 'O' => WORD(I) := 'o';
  2426.         when 'P' => WORD(I) := 'p';
  2427.         when 'Q' => WORD(I) := 'q';
  2428.         when 'R' => WORD(I) := 'r';
  2429.         when 'S' => WORD(I) := 's';
  2430.         when 'T' => WORD(I) := 't';
  2431.         when 'U' => WORD(I) := 'u';
  2432.         when 'V' => WORD(I) := 'v';
  2433.         when 'W' => WORD(I) := 'w';
  2434.         when 'X' => WORD(I) := 'x';
  2435.         when 'Y' => WORD(I) := 'y';
  2436.         when 'Z' => WORD(I) := 'z';
  2437.         when others => null;
  2438.       end case;
  2439.     end loop;
  2440.   end LOWER;
  2441.  
  2442.  
  2443.   -- This procedure gets a token and a separator from the input token
  2444.   -- stream which is associated with the source file being read.  The
  2445.   -- DYNAMIC_STRING token is converted to an Ada STRING, reduced to lower
  2446.   -- case and its length is returned in SIZE.  When an end of string is
  2447.   -- encountered, GET_LOWER_CASE_STRING automatically reads new lines
  2448.   -- until a new token is found or end of file occurs.
  2449.   procedure GET_LOWER_CASE_STRING
  2450.     (STR          : out STRING;
  2451.      SIZE         : out NATURAL;
  2452.      SEPARATOR    : out CHARACTER) is
  2453.  
  2454.     TOKEN         : DYNAMIC_STRING;
  2455.     NOT_SUCCESSFUL: BOOLEAN := TRUE;
  2456.   begin
  2457.     while NOT_SUCCESSFUL loop
  2458.       begin
  2459.         GET_TOKEN(TOKEN, SEPARATOR);
  2460.         NOT_SUCCESSFUL := FALSE;
  2461.         -- For comments accept token but still get new line.
  2462.         if SEPARATOR = '-' then
  2463.             GET_LINE(SOURCE_FILE, LINE, LAST);
  2464.             SET_TOKEN_STREAM( LINE(1..LAST) );
  2465.         end if;
  2466.       exception
  2467.         when END_OF_STRING =>
  2468.             GET_LINE(SOURCE_FILE, LINE, LAST);
  2469.             SET_TOKEN_STREAM( LINE(1..LAST) );
  2470.       end;
  2471.     end loop;
  2472.     CONVERT_TO_STRING(TOKEN, STR);
  2473.     SIZE := LENGTH(TOKEN);
  2474.     LOWER( STR(1..SIZE) );
  2475.   end GET_LOWER_CASE_STRING;
  2476.  
  2477.  
  2478.   -- This function merely guards against constraint errors by comparing
  2479.   -- the length of strings prior to comparing the strings themselves.
  2480.   function EQUAL
  2481.     (LEFT     : in STRING;
  2482.      RIGHT    : in STRING) return BOOLEAN is
  2483.   begin
  2484.     if LEFT'LENGTH = RIGHT'LENGTH then
  2485.       if LEFT = RIGHT then
  2486.         return TRUE;
  2487.       end if;
  2488.     end if;
  2489.     return FALSE;
  2490.   end EQUAL;
  2491.  
  2492.  
  2493.  
  2494.   -- This procedure skips over a matched pair of parentheses, counting
  2495.   -- but ignoring nested parantheses.  On input the separator may be a
  2496.   -- '(' indicating the pair has already been encountered. Otherwise,
  2497.   -- SKIP_PARENTHESES looks at the next separator to determine whether
  2498.   -- the parenthetical expression is even present.
  2499.   procedure SKIP_PARENTHESES
  2500.     (TOKEN     : in out STRING;
  2501.      SIZE      : in out NATURAL;
  2502.      SEPARATOR : in out CHARACTER) is
  2503.     LEVEL            : NATURAL;
  2504.     MORE_PARENTHESES : BOOLEAN := TRUE;
  2505.   
  2506.   begin
  2507.     if SEPARATOR = '(' then
  2508.       LEVEL := 1;
  2509.     else
  2510.       LEVEL := 0;
  2511.     end if;
  2512.  
  2513.     while MORE_PARENTHESES loop
  2514.       GET_LOWER_CASE_STRING(TOKEN, SIZE, SEPARATOR);
  2515.       if    SEPARATOR = '(' then
  2516.         LEVEL := LEVEL+1;
  2517.       elsif SEPARATOR = ')' then
  2518.         LEVEL := LEVEL-1;
  2519.       end if;
  2520.       if LEVEL = 0 then
  2521.         MORE_PARENTHESES := FALSE;
  2522.       end if;
  2523.     end loop;
  2524.   end SKIP_PARENTHESES;
  2525.  
  2526.  
  2527.  
  2528.   -- This function checks unit_simple_names against the set of predefined
  2529.   -- system units.  A true value is returned on matches.
  2530.   -->>>>>>>>>>>>>>>>>> C O M P I L E R - D E P E N D E N T<<<<<<<<<<<<<<<--
  2531.   function SYSTEM_NAME(STR : STRING) return BOOLEAN is
  2532.  
  2533.   begin
  2534.     if EQUAL(STR, "text_io")   or 
  2535.        EQUAL(STR, "system" )   or 
  2536.        EQUAL(STR, "direct_io") or 
  2537.        EQUAL(STR, "unix_call") or
  2538.        EQUAL(STR, "unchecked_conversion") or
  2539.        EQUAL(STR, "host_lcd_if")          then
  2540.        return TRUE;
  2541.     else
  2542.        return FALSE;
  2543.     end if;
  2544.   end SYSTEM_NAME;
  2545.  
  2546.  
  2547.   -- This procdedure determines the relevant program state by recognizing
  2548.   -- the PACKAGE, PROCEDURE, FUNCTION, BODY, IS, and GENERIC reserved
  2549.   -- words.  The token, size, and separator parameters are checked on 
  2550.   -- input and new values are returned reflecting the file position on
  2551.   -- output.  The is_body parameter is returned true when a compilation
  2552.   -- unit body is recognized.
  2553.   procedure CHECK_PROGRAM_STATE
  2554.     (TOKEN    : in out STRING;
  2555.      SIZE     : in out NATURAL;
  2556.      SEPARATOR: in out CHARACTER;
  2557.      IS_BODY  :    out BOOLEAN;
  2558.      IS_SPEC  :    out BOOLEAN) is
  2559.  
  2560.   begin
  2561.     case PROGRAM_STATE is
  2562.     when START_UP | GENERIC_PARMS =>
  2563.         -- For the reserved word PACKAGE, check whether this is a
  2564.         -- specification or body.
  2565.         if EQUAL(TOKEN(1..SIZE), "package") then
  2566.           GET_LOWER_CASE_STRING(TOKEN, SIZE, SEPARATOR);
  2567.           if EQUAL(TOKEN(1..SIZE), "body") then
  2568.             PROGRAM_STATE := PACKAGE_BODY;
  2569.             IS_BODY := TRUE;
  2570.           else
  2571.             PROGRAM_STATE := PACKAGE_SPEC;
  2572.             CURRENT_UNIT_NAME(1..SIZE) := TOKEN(1..SIZE);
  2573.             CURRENT_UNIT_SIZE          := SIZE;
  2574.             IS_SPEC := TRUE;
  2575.           end if;
  2576.  
  2577.         -- For subprograms, skip over parenthesis.  If this is a function,
  2578.         -- also skip over the RETURN and the type_mark.  If the next
  2579.         -- token is IS, mark as a subprogram body.  Otherwise, mark as a
  2580.         -- subprogram specification.
  2581.         elsif EQUAL(TOKEN(1..SIZE), "procedure") or
  2582.               EQUAL(TOKEN(1..SIZE), "function" ) then
  2583.           GET_LOWER_CASE_STRING(TOKEN, SIZE, SEPARATOR);
  2584.           CURRENT_UNIT_NAME(1..SIZE) := TOKEN(1..SIZE);
  2585.           CURRENT_UNIT_SIZE          := SIZE;
  2586.           SKIP_PARENTHESES(TOKEN, SIZE, SEPARATOR);
  2587.           --If parameters existed then SEPARATOR will be a ')' and
  2588.           --we must check the next TOKEN for 'is' or ';'.  Otherwise,
  2589.           --the current TOKEN must be checked.
  2590.           if SEPARATOR = ')' then
  2591.             GET_LOWER_CASE_STRING(TOKEN, SIZE, SEPARATOR);
  2592.           end if;
  2593.  
  2594.           --If this was a function call skip over the return and type
  2595.           if EQUAL(TOKEN(1..SIZE), "return") then
  2596.             GET_LOWER_CASE_STRING(TOKEN, SIZE, SEPARATOR);
  2597.           end if;
  2598.  
  2599.  
  2600.           if EQUAL(TOKEN(1..SIZE), "is") then
  2601.             PROGRAM_STATE := SUBPROGRAM_BODY;
  2602.             IS_BODY := TRUE;
  2603.           else
  2604.             PROGRAM_STATE := SUBPROGRAM_SPEC;
  2605.           end if;
  2606.         elsif EQUAL(TOKEN(1..SIZE), "generic") then
  2607.           PROGRAM_STATE := GENERIC_PARMS;
  2608.         end if;
  2609.  
  2610.     when PACKAGE_SPEC =>
  2611.         -- Once a package specification has been encountered, the corresponding
  2612.         -- package body is identified by keyin on the reserved words PACKAGE
  2613.         -- BODY and then comparing the next token with the name captured when
  2614.         -- the specification was encountered.
  2615.         if EQUAL(TOKEN(1..SIZE), "package") then
  2616.           GET_LOWER_CASE_STRING(TOKEN, SIZE, SEPARATOR);
  2617.           if EQUAL(TOKEN(1..SIZE), "body") then
  2618.             GET_LOWER_CASE_STRING(TOKEN, SIZE, SEPARATOR);
  2619.             if EQUAL(TOKEN(1..SIZE),CURRENT_UNIT_NAME(1..CURRENT_UNIT_SIZE))then
  2620.               PROGRAM_STATE := PACKAGE_BODY;
  2621.               IS_BODY := TRUE;
  2622.             end if;
  2623.           end if;
  2624.         end if;
  2625.  
  2626.     when SUBPROGRAM_SPEC =>
  2627.         -- Once a subprogram specification has been found, the corresponding
  2628.         -- body is identified by keying on the reserved words PROCEDURE and
  2629.         -- FUNCTION, then comparing the next token with the name captured when
  2630.         -- the specification was encountered.
  2631.         if EQUAL(TOKEN(1..SIZE), "procedure") or
  2632.            EQUAL(TOKEN(1..SIZE), "function" ) then
  2633.           GET_LOWER_CASE_STRING(TOKEN,SIZE,SEPARATOR);
  2634.           if EQUAL(TOKEN(1..SIZE),CURRENT_UNIT_NAME(1..CURRENT_UNIT_SIZE)) then
  2635.             PROGRAM_STATE := SUBPROGRAM_BODY;
  2636.             IS_BODY := TRUE;
  2637.           end if;
  2638.         end if;
  2639.  
  2640.     when others =>
  2641.         -- All other program states are ignored at this time.
  2642.         null;
  2643.     end case;
  2644.   end CHECK_PROGRAM_STATE;
  2645.  
  2646.             
  2647.   -- This procedure prompts the user for a character string and creates a file
  2648.   -- that name which will be the configuration object file name.
  2649.   procedure OPEN_CO is
  2650.     CO_NAME  : STRING(1..MAX_STRING);
  2651.   begin
  2652.     NEW_LINE;
  2653.     PUT("ENTER CO_FILE_NAME =>");
  2654.     GET_LINE(CO_NAME, LAST);
  2655.     CREATE(CO_FILE, OUT_FILE, CO_NAME(1..LAST));
  2656.   end;
  2657.  
  2658.  
  2659.   -- This procedure prompts the user for a filename search pattern,
  2660.   -- builds a command to place a list of valid filenames in a file, and
  2661.   -- then reads that file building a dynamic string of files to be 
  2662.   -- scanned.  It calls the system dependent routine HOST.EXECUTE_CMDS
  2663.   -- and also builds a system dependent string wich is passed as the
  2664.   -- command to be executed.
  2665.   -->>>>>>>>>>>>>>>>>>>>>>>S Y S T E M   D E P E N D E N T<<<<<<<<<<<<<<<--
  2666.   procedure GET_LIST_OF_FILENAMES is
  2667.     CO_TEMP       : FILE_TYPE;
  2668.     COMMAND       : STRING(1..MAX_STRING);
  2669.     D_COMMAND     : DYNAMIC_STRING;
  2670.   begin 
  2671.     ASSIGN(FILE_LIST, EMPTY_STR);
  2672.     PUT("ENTER NAMES OF FILES TO BE SCANNED => ");
  2673.     GET_LINE(LINE, LAST);
  2674.     COMMAND(1..3)            := "ls ";
  2675.     COMMAND(4..LAST+3)       := LINE(1..LAST);
  2676.     COMMAND(LAST+4..LAST+21) := " >> CREATE_CO.TEMP";
  2677.  
  2678.     begin --build file list
  2679.       CONVERT_TO_DYNAMIC(COMMAND(1..LAST+21), D_COMMAND);
  2680.       HOST.EXECUTE_CMDS(D_COMMAND);
  2681.       OPEN(CO_TEMP, IN_FILE, "CREATE_CO.TEMP");
  2682.  
  2683.       loop  --Until end of file
  2684.         GET_LINE(CO_TEMP, LINE, LAST);
  2685.         APPEND(" ", FILE_LIST);
  2686.         APPEND(LINE(1..LAST), FILE_LIST);
  2687.       end loop;
  2688.     exception
  2689.       when HOST.EXECUTION_ERROR =>
  2690.                                        PUT_LINE("**ERROR** NO FILES FOUND");
  2691.                                        DELETE(CO_TEMP);
  2692.       when END_ERROR =>
  2693.                         DELETE(CO_TEMP); --End of file expected.
  2694.     end;
  2695.   end GET_LIST_OF_FILENAMES;
  2696.  
  2697.  
  2698.   -- This procedure writes a dependency rule.  A .sym target is specified 
  2699.   -- whenever a package specification has been recognized.  A .code target
  2700.   -- is always specified.  The target names are built from the name of the 
  2701.   -- source file stripped of the .text suffix and any leading directory
  2702.   -- names.  Dependencies are then retrieved from the UNIT_LIST previously
  2703.   -- built by SAVE_DEPENDENCIES.  Each line of the dependency rule consists
  2704.   -- of five 15-character fields.
  2705.   procedure WRITE_DEPENDENCIES is
  2706.       SIZE        : NATURAL;
  2707.       UNIT_NAME   : STRING(1..MAX_STRING);
  2708.       TEMP_NAME   : STRING(1..MAX_STRING);
  2709.       ROOT_LENGTH : NATURAL;
  2710.  
  2711.  
  2712.     -- This procedure writes a string and pads with blanks to a multiple of
  2713.     -- 14. It guarantees that a blank trails and also checks for exceeding
  2714.     -- line length.
  2715.     procedure PUT_15(STR : in STRING; SEP : in CHARACTER) is
  2716.       SIZE      : INTEGER;
  2717.       PAD       : INTEGER;
  2718.     begin
  2719.       SIZE := STR'LENGTH+1;
  2720.       SPACE_COUNT := SPACE_COUNT + ( ((SIZE-1)/15+1) * 15);
  2721.       if SPACE_COUNT > MAX_COLUMN then
  2722.         PUT(CO_FILE, "\");
  2723.         NEW_LINE(CO_FILE); PUT(CO_FILE, BLANKS(1..INDENT_COUNT));
  2724.         SPACE_COUNT := INDENT_COUNT +((SIZE-1)/15+1) * 15;
  2725.       end if;
  2726.  
  2727.       PUT(CO_FILE, STR    (1..SIZE-1) );
  2728.       PAD := (SIZE) mod 15;
  2729.       if PAD /= 0 then
  2730.         PUT(CO_FILE, BLANKS (PAD..14)  );
  2731.       end if;
  2732.       PUT(CO_FILE, SEP);
  2733.     end PUT_15;
  2734.  
  2735.  
  2736.   begin
  2737.    
  2738.     -- Remove directories, if any from file name
  2739.     NAME_START := 1;
  2740.     for I in 1..FILE_LENGTH loop
  2741.       if FILENAME(I) = '/' then
  2742.         NAME_START  := I+1;
  2743.       end if;
  2744.     end loop;
  2745.     ROOT_LENGTH := (FILE_LENGTH-5) - NAME_START + 1;
  2746.  
  2747.     SPACE_COUNT := 0;
  2748.     TEMP_NAME(1..ROOT_LENGTH) := FILENAME(NAME_START..FILE_LENGTH-5);
  2749.     if IS_SPEC then
  2750.       TEMP_NAME(ROOT_LENGTH+1..ROOT_LENGTH+4) := ".sym";
  2751.       PUT_15(TEMP_NAME(1..ROOT_LENGTH+4), ' ');
  2752.     end if;
  2753.     TEMP_NAME(ROOT_LENGTH+1..ROOT_LENGTH+5)   := ".code";
  2754.     PUT_15(TEMP_NAME(1..ROOT_LENGTH+5), ':');
  2755.     INDENT_COUNT := SPACE_COUNT;
  2756.     TEMP_NAME(1..FILE_LENGTH-NAME_START+1) := FILENAME(NAME_START..FILE_LENGTH);
  2757.     PUT_15(TEMP_NAME(1..FILE_LENGTH-NAME_START+1), ' '); 
  2758.  
  2759.     --Check for an empty unit list which means that the specification
  2760.     --has no dependencies and this routine may be exited
  2761.     if EMPTY(UNIT_LIST) then
  2762.       raise END_OF_STRING;
  2763.     end if;
  2764.  
  2765.     loop
  2766.       GET_TOKEN(UNIT_LIST, D_TOKEN, SEPARATOR);
  2767.       CONVERT_TO_STRING(D_TOKEN, UNIT_NAME(1..LENGTH(D_TOKEN)));
  2768.       PUT_15(UNIT_NAME(1..LENGTH(D_TOKEN)), ' ');
  2769.     end loop;
  2770.   exception
  2771.     when END_OF_STRING => PUT(CO_FILE, ';');
  2772.                           NEW_LINE(CO_FILE);
  2773.   end WRITE_DEPENDENCIES;
  2774.  
  2775.  
  2776.  
  2777.   -- This procedure is called whenever a WITH or SEPERATE clause is
  2778.   -- encountered.  Unit names are checked against the list of system
  2779.   -- supplied names, and user-defined units are added to the CO file.
  2780.   -- Termination occurs on a ';' for WITHs and on a ')' for SEPARATEs.
  2781.   procedure SAVE_DEPENDENCIES 
  2782.           (UNIT_DESCRIPTOR : in CONTEXT_STATE) is
  2783.     UNIT_NAME     : STRING(1..MAX_STRING);
  2784.     SIZE          : INTEGER;
  2785.   begin
  2786.     loop
  2787.       GET_LOWER_CASE_STRING(UNIT_NAME, SIZE, SEPARATOR);
  2788.       if not SYSTEM_NAME( UNIT_NAME(1..SIZE) ) then
  2789.         if UNIT_DESCRIPTOR = WITH_CLAUSE then
  2790.           UNIT_NAME(SIZE+1..SIZE+5) := ".sym ";
  2791.         else
  2792.           UNIT_NAME(SIZE+1..SIZE+5) := ".code";
  2793.         end if;
  2794.         APPEND(" ",                 UNIT_LIST);
  2795.         APPEND(UNIT_NAME(1..SIZE+5), UNIT_LIST);
  2796.       end if;
  2797.       exit when SEPARATOR = ';' or SEPARATOR = ')';
  2798.     end loop;
  2799.   end SAVE_DEPENDENCIES;
  2800.  
  2801.  
  2802.   -- This procedure scans a line of source text.  It processes multiple
  2803.   -- statements per line looking for with or separate clauses.  It exits
  2804.   -- when a comment is encountered.  
  2805.   procedure PROCESS_A_LINE is
  2806.   begin
  2807.     loop
  2808.       GET_LOWER_CASE_STRING(TOKEN, SIZE, SEPARATOR);
  2809.       if EQUAL(TOKEN(1..SIZE), "with")  and
  2810.         PROGRAM_STATE /= GENERIC_PARMS then 
  2811.         SAVE_DEPENDENCIES(WITH_CLAUSE);
  2812.       elsif EQUAL(TOKEN(1..SIZE), "separate") then
  2813.         SAVE_DEPENDENCIES(SEPARATE_CLAUSE);
  2814.         raise END_ERROR; --Since only a proper body follows.
  2815.       elsif SEPARATOR = ENVIRONS.COMMENT then
  2816.         return;
  2817.       else
  2818.         --If a comment was found then this can't be a body.  Nor
  2819.         --is it necessary to look for the end of this statement.
  2820.         CHECK_PROGRAM_STATE(TOKEN, SIZE, SEPARATOR, IS_BODY, IS_SPEC);
  2821.         if IS_BODY then
  2822.           raise END_ERROR;
  2823.         end if;
  2824.         LOOK_FOR_NEXT_STATEMENT: loop
  2825.            GET_TOKEN(D_TOKEN, SEPARATOR);
  2826.            if SEPARATOR = ';' then
  2827.              exit;
  2828.            elsif SEPARATOR = '-' then
  2829.              return;
  2830.            end if;
  2831.         end loop LOOK_FOR_NEXT_STATEMENT;
  2832.       end if;
  2833.     end loop;
  2834.   exception
  2835.     when END_OF_STRING => null; --it is ok.
  2836.   end PROCESS_A_LINE;
  2837.  
  2838.  
  2839.  
  2840. begin
  2841.  
  2842.   --Prompt user for co_file name. Then get list of files to be processed
  2843.   --and added to the co_file.
  2844.   OPEN_CO;
  2845.   GET_LIST_OF_FILENAMES;
  2846.   GET_TOKEN(FILE_LIST, FNAME, SEPARATOR);
  2847.  
  2848.   while not EMPTY( FNAME ) loop
  2849.     CONVERT_TO_STRING(FNAME, FILENAME);
  2850.     FILE_LENGTH := LENGTH(FNAME);
  2851.  
  2852.     begin  -- Protect for illegal files
  2853.       OPEN( SOURCE_FILE, IN_FILE, FILENAME(1..LENGTH(FNAME)) );
  2854.       PUT_LINE( FILENAME(1..FILE_LENGTH) );
  2855.       PROGRAM_STATE := START_UP;
  2856.       IS_BODY       := FALSE;
  2857.       IS_SPEC       := FALSE;
  2858.  
  2859.       begin --process a file
  2860.         PROCESS_A_FILE: loop
  2861.          GET_LINE(SOURCE_FILE, LINE, LAST);
  2862.           SET_TOKEN_STREAM(LINE(1..LAST));
  2863.  
  2864.           PROCESS_A_LINE;
  2865.  
  2866.         end loop PROCESS_A_FILE;
  2867.       exception
  2868.         when END_ERROR => WRITE_DEPENDENCIES;
  2869.                           PUT(CO_FILE, "ada ");
  2870.                           PUT(CO_FILE, FILENAME(NAME_START..FILE_LENGTH-5) );
  2871.                           NEW_LINE(CO_FILE);PUT(CO_FILE, '$');NEW_LINE(CO_FILE);
  2872.                           CLOSE(SOURCE_FILE);
  2873.       end;
  2874.       GET_TOKEN(FILE_LIST, FNAME, SEPARATOR);
  2875.     exception
  2876.       when NAME_ERROR=> PUT("**ERROR** FILE:");
  2877.                         PUT(FILENAME(1..FILE_LENGTH));
  2878.                         PUT_LINE(" CAN'T BE OPENED.");
  2879.                         GET_TOKEN(FILE_LIST, FNAME, SEPARATOR);
  2880.     end;  -- Protect for illegal files
  2881.   end loop;
  2882. exception
  2883.   -- END_OF_STRING may be raised by a call to GET_TOKEN in an attempt to get
  2884.   -- another file name after the last name in the list has been read.
  2885.   when END_OF_STRING => --Dont CLOSE(CO_FILE); since that puts an end of page.
  2886.                         null;
  2887. end CREATE_CO;
  2888. --::::::::::::::
  2889. --display.text
  2890. --::::::::::::::
  2891. -- This package performs the processing for generating the top_down and bottom_
  2892. -- up graphs and for displaying the lists of basic and derived names.
  2893. package DISPLAY is
  2894.  
  2895.   procedure TOP_DOWN_GRAPH;  
  2896.   procedure BOTTOM_UP_GRAPH;
  2897.   procedure NAMES;
  2898.  
  2899. end DISPLAY;
  2900.  
  2901.  
  2902. with AST_GRAPH, STR_PACK, ARGUMENTS, ENVIRONS;
  2903. with TEXT_IO; use TEXT_IO, INTEGER_IO, STR_PACK;
  2904. package body DISPLAY is
  2905.  
  2906.   type LIST_OF_LEVELS;
  2907.   type LINK_TO_LEVELS is access LIST_OF_LEVELS;
  2908.   type LIST_OF_LEVELS is
  2909.       record
  2910.         RELATIONS     : AST_GRAPH.LINK_TO_LIST_OF_NAMES;
  2911.         NEXT_LEVEL    : LINK_TO_LEVELS;
  2912.       end record;
  2913.   type DIRECTION is (TOP_DOWN, BOTTOM_UP);
  2914.  
  2915.  
  2916.   OBJECT           : AST_GRAPH.LINK_TO_NAME_BLOCK;
  2917.   ALREADY_EXPANDED : BOOLEAN;
  2918.   NEW_LEVEL_ADDED  : BOOLEAN;
  2919.  
  2920.   LINE_NO       :          NATURAL := 1;
  2921.   MAX_SIZE      :          NATURAL := 62;
  2922.   INDENT_SIZE   : constant NATURAL := 4;
  2923.   MAX_INDENT    : constant NATURAL := MAX_SIZE/INDENT_SIZE;
  2924.   INDENT_COUNT  :          NATURAL := 0;
  2925.  
  2926.  
  2927.   -- This procedure is called at the start of each graph to reinitialize values
  2928.   -- so that multiple graphs may be displayed.
  2929.   procedure RESET is
  2930.     OBJECT_NAME    : AST_GRAPH.LINK_TO_NAME_BLOCK;
  2931.   begin
  2932.     LINE_NO        := 1;
  2933.     MAX_SIZE       := 62;
  2934.     INDENT_COUNT   := 0;
  2935.     
  2936.     OBJECT_NAME     := AST_GRAPH.NAME_LIST;
  2937.     WHILE OBJECT_NAME /= null loop
  2938.       OBJECT_NAME.EXPANSION_LINE := 0;
  2939.       OBJECT_NAME                := OBJECT_NAME.PREVIOUS_NAME_BLOCK;
  2940.     end loop;
  2941.   end RESET;
  2942.  
  2943.  
  2944.   -- This procedure looks up the name of the object whose graph is to be
  2945.   -- drawn and creates the initial level for production of the graph.
  2946.   procedure GET_FIRST_LEVEL
  2947.      (GOAL          : STR_PACK.DYNAMIC_STRING;
  2948.       LEVEL         : out LINK_TO_LEVELS) is
  2949.      MATCH  : AST_GRAPH.LINK_TO_LIST_OF_NAMES;
  2950.      NAMES  : AST_GRAPH.LINK_TO_NAME_BLOCK;
  2951.      SEPARATOR     : CHARACTER;
  2952.  
  2953.   begin
  2954.     LEVEL := null;
  2955.  
  2956.     --Search for TARGET_ARG in NAME_LIST.
  2957.     NAMES := AST_GRAPH.NAME_LIST;
  2958.     while NAMES /= null loop
  2959.       if GOAL = NAMES.NAME_VALUE then
  2960.         --TARGET_ARG is found. Put it in a LIST OF NAMES and put that 
  2961.         --list in a LIST OF LEVELS. Then exit this procedure.
  2962.         MATCH            := new AST_GRAPH.LIST_OF_NAMES;
  2963.         MATCH.NAME       := NAMES;
  2964.         LEVEL            := new LIST_OF_LEVELS;
  2965.         LEVEL.RELATIONS  := MATCH;
  2966.         exit;
  2967.       end if;
  2968.  
  2969.       NAMES := NAMES.PREVIOUS_NAME_BLOCK;
  2970.     end loop;
  2971.   end GET_FIRST_LEVEL;
  2972.  
  2973.  
  2974.   -- This procedure checks for a null list and retrieves a name, removing
  2975.   -- it from the list, if the list is not null.
  2976.   procedure GET_OBJECT
  2977.      (NAMES : in out AST_GRAPH.LINK_TO_LIST_OF_NAMES;
  2978.       OBJECT: out    AST_GRAPH.LINK_TO_NAME_BLOCK) is
  2979.   begin
  2980.     if NAMES = null then
  2981.       OBJECT := null;
  2982.     else
  2983.       OBJECT := NAMES.NAME;
  2984.       NAMES  := NAMES.PREVIOUS_NAME;
  2985.     end if;
  2986.   end GET_OBJECT;
  2987.  
  2988.  
  2989.   -- Given an object, this procedure creates a list of that objects relatives
  2990.   -- by searching all rules in which that object occurs. If the order is top
  2991.   -- down relatives will be chosen from the DEPENDENTS list of rules in which
  2992.   -- the object is a TARGET. Otherwise, relatives are chosen from the list of
  2993.   -- TARGETS given in rules where this object is a DEPENDENT.
  2994.   procedure GET_RELATIONS
  2995.      (OBJECT    : in  AST_GRAPH.LINK_TO_NAME_BLOCK   ;
  2996.       RELATIONS : out AST_GRAPH.LINK_TO_LIST_OF_NAMES;
  2997.       ORDER     : in  DIRECTION                      ) is
  2998.     R_LIST           : AST_GRAPH.LINK_TO_LIST_OF_RULES;
  2999.  
  3000.  
  3001.     -- This procedure appends one list of names to another.  Names are copied
  3002.     -- from the end of one list to the end of the other.  Thus order is not
  3003.     -- preserved.
  3004.     procedure ADD
  3005.        (LIST     : in out  AST_GRAPH.LINK_TO_LIST_OF_NAMES;
  3006.         ADD_ON   : in      AST_GRAPH.LINK_TO_LIST_OF_NAMES) is
  3007.        CURRENT   : AST_GRAPH.LINK_TO_LIST_OF_NAMES;
  3008.        ADDED     : AST_GRAPH.LINK_TO_LIST_OF_NAMES;
  3009.     begin
  3010.       ADDED   := ADD_ON;
  3011.       while ADDED /= null loop
  3012.         --Copy a name from the add-on list.
  3013.         CURRENT := new AST_GRAPH.LIST_OF_NAMES;
  3014.         CURRENT.NAME := ADDED.NAME;
  3015.  
  3016.         --Link it to top of LIST and reset LIST.
  3017.         CURRENT.PREVIOUS_NAME := LIST;
  3018.         LIST                  := CURRENT;
  3019.  
  3020.         --Point to the next name to be added
  3021.         ADDED := ADDED.PREVIOUS_NAME;
  3022.       end loop;
  3023.     end ADD;
  3024.  
  3025.  
  3026.   begin
  3027.     RELATIONS := null;
  3028.     if ORDER = TOP_DOWN then
  3029.       R_LIST := OBJECT.DEFINING_RULES;
  3030.     else
  3031.       R_LIST := OBJECT.DEPENDENT_RULES;
  3032.     end if;
  3033.  
  3034.     --Loop through the list of rules adding the appropiate list
  3035.     --of relations(either targets or dependents) to be returned.
  3036.     while R_LIST /= null loop
  3037.       if ORDER = TOP_DOWN then
  3038.         ADD(RELATIONS,R_LIST.RULE.DEPENDENTS);
  3039.       else
  3040.         ADD(RELATIONS,R_LIST.RULE.TARGETS);
  3041.       end if;
  3042.       R_LIST := R_LIST.PREVIOUS_RULE;
  3043.     end loop;
  3044.  
  3045.   end GET_RELATIONS;
  3046.  
  3047.  
  3048.   -- This procedure prints one line of either a top_down or bottom_up graph.
  3049.   -- It checks for previously expanded objects, inserting the referenced line
  3050.   -- number when appropriate.  It computes the proper number and contents of
  3051.   -- the indentation field.  This procedure also counts lines and issues a
  3052.   -- page eject after 55 lines.
  3053.   procedure PRINT_LINE is
  3054.     COUNT           : NATURAL;
  3055.     INDENTER        : STRING(1..MAX_SIZE) :=
  3056.       "..........................................................    ";
  3057.     NAME            : STRING(1..MAX_SIZE);
  3058.   begin
  3059.     PUT(LINE_NO, 4);  PUT(' ');
  3060.     if ALREADY_EXPANDED then
  3061.       PUT('[');  PUT(OBJECT.EXPANSION_LINE, 3);  PUT(']');
  3062.     else
  3063.       PUT("     ");
  3064.     end if;
  3065.  
  3066.     if INDENT_COUNT /= 0 then
  3067.       COUNT := MAX_SIZE - (  INDENT_COUNT * INDENT_SIZE  ) + 1;
  3068.       PUT(  INDENTER(COUNT..MAX_SIZE)  );
  3069.     end if;
  3070.     STR_PACK.CONVERT_TO_STRING(OBJECT.NAME_VALUE, NAME);
  3071.     PUT(  NAME(  1..STR_PACK.LENGTH(OBJECT.NAME_VALUE)  ) );
  3072.     NEW_LINE;
  3073.  
  3074.     LINE_NO := LINE_NO + 1;
  3075.     if LINE_NO MOD 55  =  0 then
  3076.       NEW_PAGE;  NEW_LINE; NEW_LINE;
  3077.     end if;
  3078.   end PRINT_LINE;
  3079.  
  3080.  
  3081.   -- This procedure controls the printing of a single graph. Starting with the
  3082.   -- target object, Graph obtains the list of all relatives(in either direction)
  3083.   -- This list constitutes a level.  One object from the current level is 
  3084.   -- selected for printing; it is removed from the level; and its relatives are
  3085.   -- added at a new level.  The new level becomes the current level and the 
  3086.   -- process iterates.  When an atttempt is made to obtain an object and the 
  3087.   -- level is found to be empty, the current level pops back up one level.  When
  3088.   -- the top level is exited, the graph is complete.
  3089.   procedure GRAPH (GOAL           : STR_PACK.DYNAMIC_STRING;
  3090.                    ORDER          : DIRECTION              ) is
  3091.     CURRENT_LEVEL   : LINK_TO_LEVELS;
  3092.     NEW_RELATIONS   : AST_GRAPH.LINK_TO_LIST_OF_NAMES;
  3093.     NEW_LEVEL       : LINK_TO_LEVELS;
  3094.     NEW_LEVEL_ADDED : BOOLEAN;
  3095.     GRAPH_NAME      : STRING(1..MAX_SIZE);
  3096.   begin
  3097.     --Clean up counters and flags so that multiple graphs may be drawn.
  3098.     RESET;
  3099.     --Get the list of objects for which graphs are to be expanded,
  3100.     --and write the title line;
  3101.     GET_FIRST_LEVEL(GOAL, CURRENT_LEVEL);
  3102.  
  3103.     STR_PACK.CONVERT_TO_STRING(GOAL, GRAPH_NAME);
  3104.     NEW_PAGE; PUT("          CONSTRUCT GRAPH FOR ");
  3105.               PUT_LINE(GRAPH_NAME(1..STR_PACK.LENGTH(GOAL)));
  3106.               NEW_LINE;
  3107.  
  3108.     --Now loop through all levels looking at a name, adding a level
  3109.     --when that name has relations and is not already expanded, 
  3110.     --popping back up a level when all names at level have been
  3111.     --processed.
  3112.     while CURRENT_LEVEL /= null loop
  3113.  
  3114.       --Remove object from active list. If it is null, move to
  3115.       --previous level. Otherwise print it and add its relations to
  3116.       --a new level if appropriate.
  3117.       NEW_LEVEL_ADDED := FALSE;
  3118.       GET_OBJECT(CURRENT_LEVEL.RELATIONS, OBJECT);
  3119.       if OBJECT = null then
  3120.         --Pop back up a level since all objects here are listed
  3121.         CURRENT_LEVEL := CURRENT_LEVEL.NEXT_LEVEL;
  3122.         if INDENT_COUNT > 0 then
  3123.           INDENT_COUNT  := INDENT_COUNT - 1;
  3124.         end if;
  3125.       
  3126.       else
  3127.         --Expand a level if necessary and print a line
  3128.         if OBJECT.EXPANSION_LINE /= 0 then
  3129.           --Object has already been expanded.
  3130.           ALREADY_EXPANDED := TRUE;
  3131.         
  3132.         else
  3133.           --GET list of relations for this object and create a new
  3134.           --level if there are any.
  3135.           ALREADY_EXPANDED := FALSE;
  3136.           GET_RELATIONS(OBJECT, NEW_RELATIONS, ORDER);
  3137.           if NEW_RELATIONS /= null then
  3138.             --Mark current object as expanded and add level to list.
  3139.             OBJECT.EXPANSION_LINE := LINE_NO;
  3140.             NEW_LEVEL             := new LIST_OF_LEVELS;
  3141.             NEW_LEVEL.NEXT_LEVEL  := CURRENT_LEVEL;
  3142.             NEW_LEVEL.RELATIONS   := NEW_RELATIONS;
  3143.             CURRENT_LEVEL         := NEW_LEVEL;
  3144.             NEW_LEVEL_ADDED       := TRUE;
  3145.           end if;
  3146.         end if;
  3147.  
  3148.         PRINT_LINE;
  3149.         if NEW_LEVEL_ADDED then
  3150.           INDENT_COUNT := INDENT_COUNT+1;
  3151.         end if;
  3152.       end if;
  3153.     end loop;
  3154.     NEW_LINE;
  3155.     NEW_LINE;
  3156.     PUT_LINE("END OF GRAPH");
  3157.   end GRAPH;  
  3158.  
  3159.  
  3160.   -- This procedure processes the list of goals.  If the list is empty, it
  3161.   -- defaults to the first goal in the CO file.  Otherwise, it gets each
  3162.   -- goal in the list and calls GRAPH to print its graph.
  3163.   procedure PROCESS_GOAL_LIST(ORDER : DIRECTION) is
  3164.     OBJECT_NAME         : AST_GRAPH.LINK_TO_NAME_BLOCK;
  3165.     GOAL                : STR_PACK.DYNAMIC_STRING;
  3166.     LOCAL_GOAL_LIST     : STR_PACK.DYNAMIC_STRING;
  3167.     SEPARATOR           : CHARACTER;
  3168.   begin
  3169.     if STR_PACK.EMPTY(ARGUMENTS.TARGET_ARG) then
  3170.       -- Default goal to first entry in name list
  3171.       OBJECT_NAME := AST_GRAPH.NAME_LIST;
  3172.       while OBJECT_NAME.PREVIOUS_NAME_BLOCK /= null loop
  3173.         OBJECT_NAME := OBJECT_NAME.PREVIOUS_NAME_BLOCK;
  3174.         STR_PACK.ASSIGN(GOAL, OBJECT_NAME.NAME_VALUE);
  3175.       end loop;
  3176.       GRAPH(GOAL, ORDER);
  3177.     
  3178.     else
  3179.       -- Get each token and print its graph.
  3180.       STR_PACK.ASSIGN(LOCAL_GOAL_LIST, ARGUMENTS.TARGET_ARG);
  3181.       while not EMPTY(LOCAL_GOAL_LIST) loop
  3182.         STR_PACK.GET_TOKEN(LOCAL_GOAL_LIST, GOAL, SEPARATOR);
  3183.         GRAPH(GOAL, ORDER);
  3184.       end loop;
  3185.     end if;
  3186.   end PROCESS_GOAL_LIST;
  3187.  
  3188.  
  3189.   -- This procedure calls PROCESS_GOAL_LIST requesting that graph be displayed
  3190.   -- in a top down manner.
  3191.   procedure TOP_DOWN_GRAPH is
  3192.   begin
  3193.     PROCESS_GOAL_LIST( TOP_DOWN );
  3194.   end TOP_DOWN_GRAPH;
  3195.  
  3196.  
  3197.  
  3198.   -- This procedure calls PROCESS_GOAL_LIST requesting that graph be displayed
  3199.   -- in a bottom up manner.
  3200.   procedure BOTTOM_UP_GRAPH is
  3201.   begin
  3202.     PROCESS_GOAL_LIST( BOTTOM_UP );
  3203.   end BOTTOM_UP_GRAPH;
  3204.  
  3205.  
  3206.  
  3207.   -- This procedure prints out the basic names and the derived names
  3208.   -- from the dependency graph.
  3209.   procedure NAMES is
  3210.     STR        : STRING(1..ENVIRONS.MAX_FILENAME_LENGTH);
  3211.     STR_LENGTH : NATURAL;
  3212.     COLUMN     : POSITIVE := 1;
  3213.   begin
  3214.     -- Initialize to beginning of dependency graph.
  3215.     AST_GRAPH.NAME_LINK := AST_GRAPH.NAME_LIST;
  3216.  
  3217.     -- Print banner for Basic Names.
  3218.     NEW_LINE;
  3219.     NEW_LINE;
  3220.     PUT_LINE("LIST OF BASIC NAMES");
  3221.     PUT_LINE("-------------------");
  3222.     NEW_LINE;
  3223.  
  3224.     -- Print Basic Names, four across page.
  3225.     while AST_GRAPH.NAME_LINK /= null loop
  3226.       if AST_GRAPH.NAME_LINK.DEFINING_RULES = null then
  3227.         STR_PACK.CONVERT_TO_STRING(AST_GRAPH.NAME_LINK.NAME_VALUE, STR);
  3228.         STR_LENGTH := STR_PACK.LENGTH(AST_GRAPH.NAME_LINK.NAME_VALUE);
  3229.         PUT(STR(1..STR_LENGTH));
  3230.         -- Pad to max length and add 5 spaces.
  3231.         for I in 1..(ENVIRONS.MAX_FILENAME_LENGTH - STR_LENGTH + 5) loop
  3232.           PUT(" ");
  3233.         end loop;
  3234.         -- Start a new line if four names have been printed.
  3235.         if COLUMN = 4 then
  3236.           NEW_LINE;
  3237.           COLUMN := 1;
  3238.         else 
  3239.           COLUMN := COLUMN + 1;
  3240.         end if;
  3241.       end if;
  3242.       AST_GRAPH.NAME_LINK := AST_GRAPH.NAME_LINK.PREVIOUS_NAME_BLOCK;
  3243.     end loop;
  3244.  
  3245.     -- Reset to beginning of graph.
  3246.     AST_GRAPH.NAME_LINK := AST_GRAPH.NAME_LIST;
  3247.     COLUMN              := 1;
  3248.  
  3249.     -- Print banner for Derived Names.
  3250.     NEW_LINE;
  3251.     NEW_LINE;
  3252.     NEW_LINE;
  3253.     PUT_LINE("LIST OF DERIVED NAMES");
  3254.     PUT_LINE("---------------------");
  3255.     NEW_LINE;
  3256.  
  3257.     -- Print Derived Names four across page.
  3258.     while AST_GRAPH.NAME_LINK /= null loop
  3259.       if AST_GRAPH.NAME_LINK.DEFINING_RULES /= null then
  3260.         STR_PACK.CONVERT_TO_STRING(AST_GRAPH.NAME_LINK.NAME_VALUE, STR);
  3261.         STR_LENGTH := STR_PACK.LENGTH(AST_GRAPH.NAME_LINK.NAME_VALUE);
  3262.         PUT(STR(1..STR_LENGTH));
  3263.         -- Pad to max length and add 5 spaces.
  3264.         for I in 1..(ENVIRONS.MAX_FILENAME_LENGTH - STR_LENGTH + 5) loop
  3265.           PUT(" ");
  3266.         end loop;
  3267.         -- Start a new line if four names have been printed.
  3268.         if COLUMN = 4 then
  3269.           NEW_LINE;
  3270.           COLUMN := 1;
  3271.         else 
  3272.           COLUMN := COLUMN + 1;
  3273.         end if;
  3274.       end if;
  3275.       AST_GRAPH.NAME_LINK := AST_GRAPH.NAME_LINK.PREVIOUS_NAME_BLOCK;
  3276.     end loop;
  3277.  
  3278.     NEW_LINE;
  3279.     NEW_LINE;
  3280.     PUT_LINE("END OF NAMES");
  3281.   end;
  3282.  
  3283.  
  3284. end DISPLAY;
  3285. --::::::::::::::
  3286. --environs.text
  3287. --::::::::::::::
  3288. -- This package constitutes the extended environment for the CONSTRUCT program.
  3289. -- The exception ERROR is used to indicate errors which abort a process during
  3290. -- a CONSTRUCT run.  The constants are used throughout CONSTRUCT and their
  3291. -- collection here provides additional control and facilitates parameterization.
  3292. package ENVIRONS is
  3293.  
  3294.    ERROR : exception;
  3295.  
  3296. -->>>>>>>>>>>>>>>>>>>C O N S T A N T   D E C L A R A T I O N S<<<<<<<<<<<<<<<--
  3297.    CONTINUATION              : constant CHARACTER := '\';
  3298.    COMMENT                   : constant CHARACTER := '-';
  3299.    TARGET_LIST_TERMINATOR    : constant CHARACTER := ':';
  3300.    DEPENDENT_LIST_TERMINATOR : constant CHARACTER := ';';
  3301.    MAX_LINE_LENGTH           : constant INTEGER   := 80;
  3302.    MAX_CMD_LENGTH            : constant INTEGER   := 200;
  3303.    MAX_FILENAME_LENGTH       : constant INTEGER   := 14;
  3304.    END_OF_COMMANDS           : constant CHARACTER := '$';
  3305.    FIRST_CHAR_OF_ENTRY_NAME  : constant CHARACTER := '%';
  3306. -->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<--
  3307.  
  3308.  
  3309.  
  3310. end ENVIRONS;
  3311. --::::::::::::::
  3312. --host.text
  3313. --::::::::::::::
  3314.  
  3315. with STR_PACK;
  3316. with CALENDAR;
  3317.  
  3318. -- This package contains the host-specific routines needed by Construct.  It is
  3319. -- the interface between Construct and the underlying Operating System.  The
  3320. -- functions provided are: retrieval of an object's time stamp, determination
  3321. -- of whether arguments were supplied on the invocation of Construct, and the
  3322. -- ability to pass a command to the operating system for execution.  Two
  3323. -- exceptions may be propogated from Host. ACCESSIBILITY_ERROR is raised when a
  3324. -- timestamp is requested for an object but is unavailable. EXECUTION_ERROR is
  3325. -- raised upon illegal command execution.
  3326. -- To provide this interface Host uses the packages SYSTEM and
  3327. -- UNCHECKED_CONVERSION, as well as the Telesoft/UNIX-specific packages
  3328. -- HOST_LCD_IF and UNIX_CALL.
  3329. package HOST is
  3330.  
  3331.  
  3332.    function MODIFICATION_TIMESTAMP
  3333.       (FOR_DBO : STR_PACK.DYNAMIC_STRING)  
  3334.       return CALENDAR.TIME;
  3335.  
  3336.    ACCESSIBILITY_ERROR : exception;
  3337.  
  3338.    function ARGS_EXIST return BOOLEAN;
  3339.    
  3340.    procedure EXECUTE_CMDS (CMD_LIST : in STR_PACK.DYNAMIC_STRING); 
  3341.  
  3342.    EXECUTION_ERROR : EXCEPTION;
  3343.  
  3344.  
  3345. end HOST;
  3346.  
  3347.  
  3348. with HOST_LCD_IF;
  3349. with UNIX_CALL; 
  3350. with SYSTEM;
  3351. with UNCHECKED_CONVERSION;
  3352. with ENVIRONS;
  3353. package body HOST is
  3354.  
  3355.  
  3356.    -- This function retrieves the timestamp of a UNIX file.  If the file doesn't
  3357.    -- exist or is protected, it raises an ACCESSIBILITY_ERROR.  A check is made
  3358.    -- to make sure that a filename less than 14 characters in length is
  3359.    -- terminated by a NUL(this is a UNIX requirement).
  3360.    function MODIFICATION_TIMESTAMP
  3361.       (FOR_DBO : STR_PACK.DYNAMIC_STRING)  
  3362.       return CALENDAR.TIME is
  3363.  
  3364.       GET_TIME : constant UNIX_CALL.CALL := 666;
  3365.  
  3366.       NAME_LENGTH : INTEGER;
  3367.    
  3368.       function ADDR_TO_INT is new UNCHECKED_CONVERSION
  3369.         (SYSTEM.ADDRESS, LONG_INTEGER);
  3370.    
  3371.       TIMESTAMP  : CALENDAR.TIME;
  3372.  
  3373.       RSLT : UNIX_CALL.RESULT;
  3374.    
  3375.       FILENAME : STRING(1 .. ENVIRONS.MAX_FILENAME_LENGTH);
  3376.  
  3377.    begin
  3378.  
  3379.  
  3380.       STR_PACK.CONVERT_TO_STRING (FOR_DBO, FILENAME);
  3381.  
  3382.       NAME_LENGTH := STR_PACK.LENGTH (FOR_DBO);
  3383.       if NAME_LENGTH < 14 then
  3384.          FILENAME (NAME_LENGTH + 1) := ASCII.NUL;
  3385.       end if;
  3386.  
  3387.       RSLT := UNIX_CALL.SYSCALL1 (GET_TIME, ADDR_TO_INT(FILENAME'address));
  3388.  
  3389.       if (RSLT = -1) then
  3390.          raise ACCESSIBILITY_ERROR;
  3391.       end if;
  3392.  
  3393.       TIMESTAMP := RSLT; -- This may become less trivial when CALENDAR
  3394.                          -- changes ( and Time /= Long_Integer!)
  3395.  
  3396.       return (TIMESTAMP);
  3397.  
  3398.    end MODIFICATION_TIMESTAMP;
  3399.  
  3400.  
  3401.  
  3402.    -- This function obtains the parameter list and the number of parameters
  3403.    -- from the Operating System(HOST_LCD_IF).  If the number of parameters is
  3404.    -- greater than 0, a true value is returned.
  3405.    function ARGS_EXIST return BOOLEAN is
  3406.       PARMS : STRING(1 .. 100);
  3407.       PARM_COUNT : INTEGER;
  3408.    
  3409.    begin
  3410.  
  3411.       HOST_LCD_IF.PARAM_STRING (PARMS, PARM_COUNT);
  3412.       return (PARM_COUNT > 0);
  3413.  
  3414.    end ARGS_EXIST;
  3415.  
  3416.  
  3417.    -- This procedure passes a single string to the operating system to be
  3418.    -- executed as a command.  If a non-zero execution code is returned, the
  3419.    -- exception EXECUTION_ERROR is raised.
  3420.    procedure EXECUTE_CMDS (CMD_LIST : in STR_PACK.DYNAMIC_STRING) is
  3421.  
  3422.       RET_CODE : UNIX_CALL.RESULT;
  3423.       INVOKE : constant UNIX_CALL.CALL := 667;
  3424.       STR_BUFFER : STRING(1 .. ENVIRONS.MAX_CMD_LENGTH);
  3425.      
  3426.       function ADDR_TO_INT is new UNCHECKED_CONVERSION
  3427.         (SYSTEM.ADDRESS, LONG_INTEGER);
  3428.  
  3429.    begin
  3430.  
  3431.       for I in STR_BUFFER'range loop
  3432.          STR_BUFFER(I) := ' ';
  3433.       end loop;
  3434.  
  3435.       STR_PACK.CONVERT_TO_STRING (CMD_LIST, STR_BUFFER);
  3436.  
  3437.       RET_CODE := UNIX_CALL.SYSCALL1 (INVOKE, ADDR_TO_INT(STR_BUFFER'address)); 
  3438.    
  3439.       if (RET_CODE /= 0) then
  3440.          raise EXECUTION_ERROR;
  3441.       end if;
  3442.    
  3443.    end EXECUTE_CMDS;
  3444.  
  3445.  
  3446. end HOST;
  3447. --::::::::::::::
  3448. --str_pack.text
  3449. --::::::::::::::
  3450. -- This package defines and maintains dynamic strings that are used
  3451. -- by the other packages and the procedure CONSTRUCT.
  3452.  
  3453. package STR_PACK is
  3454.  
  3455.   -- Each variable of this type is composed of a sequence of
  3456.   -- characters and an indication of the number of characters in the
  3457.   -- sequence (i.e., its length >= 0).  Each character in the sequence
  3458.   -- is ordered relative to its position, where the first character
  3459.   -- has position l, and the position of each character thereafter is
  3460.   -- incremented by l.  If there are no characters in the sequence,
  3461.   -- then the length is 0, else the length is the position of the
  3462.   -- last character in the sequence.  Initially, a variable of this
  3463.   -- type is an empty string (i.e., its length is zero).
  3464.   type DYNAMIC_STRING is limited private;
  3465.  
  3466.   -- This constant represents an empty string (i.e., its length is
  3467.   -- zero and thus contains no characters).
  3468.   -- EMPTY_STR : constant DYNAMIC_STRING;   
  3469.   -- Deferred constants not yet implemented so a function is used.
  3470.  
  3471.  
  3472.   function EMPTY_STR return DYNAMIC_STRING;
  3473.  
  3474.   procedure ASSIGN
  3475.     (STR       : in out DYNAMIC_STRING;
  3476.      D_STRING  : in     DYNAMIC_STRING);
  3477.  
  3478.  
  3479.   procedure CONVERT_TO_DYNAMIC
  3480.     (CHAR_STRING : in STRING;
  3481.      STR         : in out DYNAMIC_STRING);
  3482.  
  3483.  
  3484.   procedure APPEND
  3485.     (CHAR : in CHARACTER;
  3486.      STR  : in out DYNAMIC_STRING);
  3487.  
  3488.  
  3489.   procedure APPEND
  3490.     (CHAR_STRING : in STRING;
  3491.      STR         : in out DYNAMIC_STRING);
  3492.  
  3493.   procedure APPEND
  3494.     (D_STRING : in DYNAMIC_STRING;
  3495.      STR      : in out DYNAMIC_STRING);
  3496.  
  3497.   procedure CONVERT_TO_STRING
  3498.      (STR    : in DYNAMIC_STRING;
  3499.       CH_STR : out STRING);
  3500.  
  3501.   procedure SET_TOKEN_STREAM
  3502.     (LINE  : in STRING);
  3503.   
  3504.  
  3505.   procedure GET_TOKEN
  3506.     (TOKEN       : in out DYNAMIC_STRING;
  3507.      SEPARATOR   : in out CHARACTER);
  3508.  
  3509.   procedure RETURN_TOKEN;
  3510.  
  3511.  
  3512.   procedure GET_TOKEN
  3513.     (STR      : in out DYNAMIC_STRING;
  3514.      TOKEN    : out    DYNAMIC_STRING;
  3515.      SEPARATOR: out    CHARACTER);
  3516.  
  3517.   function LENGTH
  3518.     (STR : in DYNAMIC_STRING)
  3519.      return NATURAL;
  3520.  
  3521.   
  3522.   procedure READ
  3523.     (CHAR : out CHARACTER;
  3524.      STR  : in DYNAMIC_STRING);
  3525.   
  3526.  
  3527.  
  3528.   procedure READ
  3529.     (CHAR : out CHARACTER;
  3530.      ITH  : in POSITIVE;
  3531.      STR  : in DYNAMIC_STRING);
  3532.  
  3533.  
  3534.   function EMPTY(STR: DYNAMIC_STRING) return BOOLEAN;
  3535.  
  3536.   function "="
  3537.     (LEFT  : DYNAMIC_STRING;
  3538.      RIGHT : DYNAMIC_STRING)
  3539.      return BOOLEAN;
  3540.  
  3541.   procedure DUMP
  3542.     (TITLE : in STRING;
  3543.      STR   : in DYNAMIC_STRING);
  3544.  
  3545.  
  3546.   function NUM_OF_TOKENS 
  3547.     (DYN_STR : DYNAMIC_STRING) return NATURAL;
  3548.  
  3549.  
  3550.   END_OF_STRING : exception;
  3551.  
  3552.  
  3553. private
  3554.   -- Each dynamic string is decomposed into segments which are at
  3555.   -- most SEGMENT_SIZE characters in length.  The head and tail
  3556.   -- segments may be partial(i.e. less than SEGMENT_SIZE), but all
  3557.   -- middle segments must be a full SEGMENT_SIZE in length.
  3558.  
  3559.   SEGMENT_SIZE : constant INTEGER := 10;
  3560.   BLANKS       : CONSTANT STRING (1..SEGMENT_SIZE) := "          ";
  3561.  
  3562.   type STR_REC;
  3563.  
  3564.   type STR_SEGMENT is access STR_REC;
  3565.  
  3566.   -- This data structure defines a segment of a dynamic string as
  3567.   -- follows:
  3568.   -- (  i)  STR contains this part of the string
  3569.   -- ( ii)  NEXT_SEGMENT points to the next segment in the
  3570.   --        dynamic string.
  3571.   -- (iii)  PRIOR_SEGMENT points to the previous segment in the
  3572.   --        dynamic string.
  3573.   --======================================================================
  3574.   --Note that the procedures RESET, ASSIGN, and APPEND have been written
  3575.   --so that when a new value is assigned to a dynamic string already
  3576.   --allocated segment blocks are kept in the linked list even though they
  3577.   --are not currently in use. Depending on the storage manager, this 
  3578.   --approach may need modification.  Modification will also be required if
  3579.   --new strings are created using ADD rather than APPEND.
  3580.   type STR_REC is
  3581.     record
  3582.       -- SEGMENT   : STRING (1..SEGMENT_SIZE) := (1..SEGMENT_SIZE => ' ');
  3583.       -- Packed aggregates are not yet implemented.  Array must be
  3584.       -- initialized  with string of spaces.
  3585.       SEGMENT      : STRING (1..SEGMENT_SIZE) := "          "; 
  3586.       NEXT_SEGMENT : STR_SEGMENT;
  3587.       PRIOR_SEGMENT: STR_SEGMENT;
  3588.     end record;
  3589.  
  3590.   -- This data structure defines a dynamic string that is composed
  3591.   -- of a linked list of segments that are ordered from HEAD to TAIL.
  3592.   -- NUM_CHAR indicates the length of the dynamic string (i.e., the
  3593.   -- sum of the lengths of all segments of which it is composed).
  3594.   -- FIRST is the position within the head segment of the first
  3595.   -- character.  LAST is the position within the tail segment of the
  3596.   -- last character.
  3597.   type DYNAMIC_STRING is
  3598.     record
  3599.       HEAD     : STR_SEGMENT := null;
  3600.       TAIL     : STR_SEGMENT := null;
  3601.       NUM_CHAR : NATURAL :=0;
  3602.       FIRST    : NATURAL :=0;
  3603.       LAST     : NATURAL :=0;
  3604.     end record;
  3605.  
  3606. end STR_PACK;
  3607.  
  3608.  
  3609.  
  3610. with TEXT_IO; use TEXT_IO, INTEGER_IO;
  3611. with SYSTEM;
  3612. package body STR_PACK is
  3613.  
  3614.   TOKEN_STREAM        : DYNAMIC_STRING;
  3615.   PSEUDO_TOKEN_STREAM : DYNAMIC_STRING;
  3616.   OLD_TOKEN_HEAD      : STR_SEGMENT;
  3617.   OLD_TOKEN_FIRST     : NATURAL;
  3618.   OLD_TOKEN_NUM_CHAR  : NATURAL;
  3619.  
  3620.   -- A token stream is useful to conserve heap space when a file is being
  3621.   -- processed as a sequence of dynamic strings.  By having two pointers
  3622.   -- to the same dynamic string(aliasing) the same space may be used for
  3623.   -- the next dynamic string after breaking the first one into tokens.  A
  3624.   -- special TOKEN procedure must be used in conjunction with SET_TOKEN_STREAM.
  3625.   -- That TOKEN procedure doesn't have a dynamic string for input and thus uses.
  3626.   -- PSEUDO_TOKEN_STREAM.
  3627.   procedure SET_TOKEN_STREAM
  3628.     (LINE  : in STRING) is
  3629.   begin
  3630.     CONVERT_TO_DYNAMIC(LINE, TOKEN_STREAM);
  3631.     PSEUDO_TOKEN_STREAM  := TOKEN_STREAM;
  3632.     OLD_TOKEN_HEAD       := PSEUDO_TOKEN_STREAM.HEAD;
  3633.     OLD_TOKEN_FIRST      := PSEUDO_TOKEN_STREAM.FIRST;
  3634.     OLD_TOKEN_NUM_CHAR   := PSEUDO_TOKEN_STREAM.NUM_CHAR;
  3635.   end SET_TOKEN_STREAM;
  3636.  
  3637.   -- This GET_TOKEN procedure may only be used in conjunction with
  3638.   -- SET_TOKEN_STREAM.  This procedure extracts a token from the
  3639.   -- PSEUDO_TOKEN_STREAM.  It also makes an additional check to convert
  3640.   -- the standard GET_TOKEN's "end of string marker  ;  " to an 'E' so that
  3641.   -- Ada syntax may be properly analyzed.
  3642.   procedure GET_TOKEN
  3643.     (TOKEN     : in out DYNAMIC_STRING;
  3644.      SEPARATOR : in out CHARACTER) is
  3645.   begin
  3646.     OLD_TOKEN_HEAD       := PSEUDO_TOKEN_STREAM.HEAD;
  3647.     OLD_TOKEN_FIRST      := PSEUDO_TOKEN_STREAM.FIRST;
  3648.     OLD_TOKEN_NUM_CHAR   := PSEUDO_TOKEN_STREAM.NUM_CHAR;
  3649.     GET_TOKEN(PSEUDO_TOKEN_STREAM, TOKEN, SEPARATOR);
  3650.     if SEPARATOR                                     = ';' and then
  3651.        LENGTH(PSEUDO_TOKEN_STREAM)                   = 0   and then
  3652.        TOKEN_STREAM.TAIL.SEGMENT(TOKEN_STREAM.LAST) /= ';' and then
  3653.        TOKEN_STREAM.TAIL.SEGMENT(TOKEN_STREAM.LAST) /= ' ' then
  3654.          SEPARATOR := 'E';
  3655.     end if;
  3656.   end GET_TOKEN;
  3657.  
  3658.  
  3659.   -- This procedure resets the pointers to the token stream so that the most
  3660.   -- recently obtained token is reinstated at the head of the stream.
  3661.   procedure RETURN_TOKEN is
  3662.   begin
  3663.     PSEUDO_TOKEN_STREAM.HEAD     := OLD_TOKEN_HEAD;
  3664.     PSEUDO_TOKEN_STREAM.FIRST    := OLD_TOKEN_FIRST;
  3665.     PSEUDO_TOKEN_STREAM.NUM_CHAR := OLD_TOKEN_NUM_CHAR;
  3666.   end RETURN_TOKEN;
  3667.  
  3668.  
  3669.  
  3670.   -- The is a debugging procedure which prints out a complete dynamic string,
  3671.   -- including its header information as well as all allocated space.
  3672.   procedure DUMP
  3673.     (TITLE : in STRING;
  3674.      STR   : in DYNAMIC_STRING) is
  3675.     CURRENT : STR_SEGMENT;
  3676.     COUNT   : INTEGER;
  3677.   begin
  3678.     NEW_LINE;
  3679.     PUT(TITLE);
  3680.     PUT(STR.NUM_CHAR,5); PUT(STR.FIRST,5); PUT(STR.LAST,5);
  3681.    
  3682.     CURRENT := STR.HEAD;
  3683.     while CURRENT /= null loop
  3684.       PUT(CURRENT.SEGMENT);
  3685.       CURRENT := CURRENT.NEXT_SEGMENT;
  3686.       COUNT := COUNT+1;
  3687.       if COUNT = 5 then
  3688.         NEW_LINE;
  3689.         COUNT := 0;
  3690.       end if;
  3691.     end loop;
  3692.     NEW_LINE;
  3693.   end DUMP;
  3694.  
  3695.  
  3696.   -- This function returns an empty string value for the private type
  3697.   -- DYNAMIC_STRING.
  3698.   function EMPTY_STR return DYNAMIC_STRING is
  3699.     STR : DYNAMIC_STRING;
  3700.   begin
  3701.     return STR;
  3702.   end EMPTY_STR;
  3703.  
  3704.  
  3705.  
  3706.   -- This function returns TRUE if the number of characters in a dynamic
  3707.   -- string is zero. FALSE, otherwise.
  3708.   function EMPTY(STR: DYNAMIC_STRING) return BOOLEAN is
  3709.     begin
  3710.        return STR.NUM_CHAR = 0;
  3711.     end EMPTY;
  3712.  
  3713.   
  3714.   -- This procedure resets a DYNAMIC_STRING to the empty value.  However, the
  3715.   -- HEAD pointer is left pointing to the linked list of segments so that the
  3716.   -- space may be reused.
  3717.   procedure RESET(STR : in out DYNAMIC_STRING) is
  3718.   begin
  3719.     STR.NUM_CHAR:= 0;
  3720.     STR.FIRST   := 0;
  3721.     STR.LAST    := 0;
  3722.     STR.TAIL    := STR.HEAD;
  3723.   end RESET;
  3724.  
  3725.   
  3726.   -- This procedure returns the lead character in a dynamic string.  The
  3727.   -- character is not deleted. The END_OF_STRING exception is returned
  3728.   -- if the dynamic string is empty.
  3729.   procedure READ(CHAR : out CHARACTER;
  3730.                  STR  : in DYNAMIC_STRING) is
  3731.   begin
  3732.     if STR.NUM_CHAR = 0 then
  3733.       raise END_OF_STRING;
  3734.     else
  3735.       CHAR := STR.HEAD.SEGMENT(STR.FIRST);
  3736.     end if;
  3737.   end READ;
  3738.  
  3739.  
  3740.   -- This function returns the number of characters in a dynamic string, not
  3741.   -- the amount of allocated space.
  3742.   function LENGTH
  3743.     (STR : in DYNAMIC_STRING)
  3744.      return NATURAL is
  3745.   begin
  3746.      return STR.NUM_CHAR;
  3747.   end;
  3748.  
  3749.  
  3750.   -- This procedure assigns the value of one dynamic string to another. It
  3751.   -- does not merely copy the pointer values since thsi would result in
  3752.   -- undesirable aliasing.  Each segment is copied in its entirety without
  3753.   -- realigning characters across the segments.
  3754.   procedure ASSIGN
  3755.     (STR     : in out DYNAMIC_STRING;
  3756.      D_STRING: in DYNAMIC_STRING) is
  3757.     CURRENT_SEGMENT : STR_SEGMENT;
  3758.     OLD_SEGMENT     : STR_SEGMENT;
  3759.     NEW_SEGMENT     : STR_SEGMENT;
  3760.   begin
  3761.     --First check for the empty string.
  3762.     RESET(STR);
  3763.     if D_STRING.NUM_CHAR = 0 then
  3764.       --String is empty.  We are all done.
  3765.       return;
  3766.     end if;
  3767.  
  3768.  
  3769.     --If the string was not empty, copy its contents over to STR.
  3770.     --First, set the dynamic string fields and copy over the complete
  3771.     --contents of D_STRING.HEAD.  This way the null NEXT and PRIOR_
  3772.     --SEGMENT pointers are captured for one segment strings. Note we
  3773.     --must be carefull always to create new segments and never to 
  3774.     --point to segments from D_STRING.
  3775.     STR.FIRST    := D_STRING.FIRST;
  3776.     STR.LAST     := D_STRING.LAST;
  3777.     STR.NUM_CHAR := D_STRING.NUM_CHAR; 
  3778.     if STR.HEAD   = null then
  3779.       STR.HEAD     := new STR_REC;
  3780.     end if;
  3781.     STR.HEAD.SEGMENT := D_STRING.HEAD.SEGMENT;
  3782.  
  3783.     -- Now copy over any middle segments.
  3784.     CURRENT_SEGMENT          := STR.HEAD;
  3785.     OLD_SEGMENT              := D_STRING.HEAD;
  3786.     while OLD_SEGMENT /= D_STRING.TAIL loop
  3787.       OLD_SEGMENT      := OLD_SEGMENT.NEXT_SEGMENT;
  3788.  
  3789.       if CURRENT_SEGMENT.NEXT_SEGMENT = null then
  3790.         NEW_SEGMENT                  := new STR_REC;
  3791.         CURRENT_SEGMENT.NEXT_SEGMENT := NEW_SEGMENT;
  3792.         NEW_SEGMENT.PRIOR_SEGMENT    := CURRENT_SEGMENT;
  3793.       else
  3794.         NEW_SEGMENT                  := CURRENT_SEGMENT.NEXT_SEGMENT;
  3795.       end if;
  3796.       NEW_SEGMENT.SEGMENT          := OLD_SEGMENT.SEGMENT;
  3797.      
  3798.       CURRENT_SEGMENT  := NEW_SEGMENT;
  3799.     end loop;
  3800.  
  3801.     --Since the contents of tail have been copied over already, merely
  3802.     --point TAIL to the current segment.
  3803.     STR.TAIL := CURRENT_SEGMENT;
  3804.   end ASSIGN;
  3805.  
  3806.  
  3807.   -- This procedure removes the first character in a dynamic string, reducing
  3808.   -- by one thelength of the string.  When the HEAD segment is emptied the HEAD
  3809.   -- pointer is moved and the space for that segment may be returned to the 
  3810.   -- heap.
  3811.   procedure DELETE_CHAR
  3812.     (STR  : in out DYNAMIC_STRING) is
  3813.   begin
  3814.     if STR.NUM_CHAR = 0 then
  3815.       raise END_OF_STRING;
  3816.   
  3817.     else
  3818.       --Delete the first character and see if the head segment has become
  3819.       --empty.
  3820.       STR.NUM_CHAR := STR.NUM_CHAR-1;
  3821.       if STR.FIRST < SEGMENT_SIZE then
  3822.         STR.FIRST := STR.FIRST+1;
  3823.       else
  3824.         --Remove this head segment.
  3825.         STR.HEAD := STR.HEAD.NEXT_SEGMENT;
  3826.         STR.FIRST:= 1;
  3827.       end if;
  3828.  
  3829.       --Now check for an empty string and clean up just in case.
  3830.       if STR.NUM_CHAR = 0 then
  3831.         STR.HEAD := null;
  3832.         STR.TAIL := null;
  3833.         STR.FIRST:= 0;
  3834.         STR.LAST := 0;
  3835.       end if;
  3836.     end if;
  3837.   end DELETE_CHAR;
  3838.  
  3839.  
  3840.  
  3841.   -- This procedure moves characters from the dynamic string to the output
  3842.   -- string.  The number of characters moved is the minimum of the two string
  3843.   -- lengths.  The dynamic is not modified by this procedure.
  3844.   procedure CONVERT_TO_STRING
  3845.      (STR         : in DYNAMIC_STRING;
  3846.       CH_STR      : out STRING ) is
  3847.     SIZE              : NATURAL;
  3848.     POS               : NATURAL;
  3849.     CURRENT_SEGMENT   : STR_SEGMENT;
  3850.   begin
  3851.     if CH_STR'LENGTH > STR.NUM_CHAR then
  3852.       SIZE := STR.NUM_CHAR;
  3853.     else
  3854.       SIZE := CH_STR'LENGTH;
  3855.     end if;
  3856.     if SIZE = 0 then
  3857.       return;
  3858.     elsif SIZE <= SEGMENT_SIZE-STR.FIRST+1 then
  3859.       --Requested string is completely within the head segment.
  3860.       CH_STR(1..SIZE) := STR.HEAD.SEGMENT(STR.FIRST..STR.FIRST+SIZE-1);
  3861.  
  3862.     else
  3863.       --Requested string is split across several segments
  3864.         --Get head segment.
  3865.         POS := SEGMENT_SIZE-STR.FIRST+1;
  3866.         CH_STR(1..POS) := STR.HEAD.SEGMENT(STR.FIRST..SEGMENT_SIZE);
  3867.  
  3868.         --Copy over the middle segments in their entirety.
  3869.         CURRENT_SEGMENT := STR.HEAD.NEXT_SEGMENT;
  3870.         while POS+(SEGMENT_SIZE) <= SIZE loop
  3871.           CH_STR(POS+1..POS+(SEGMENT_SIZE)) := CURRENT_SEGMENT.SEGMENT;
  3872.           CURRENT_SEGMENT := CURRENT_SEGMENT.NEXT_SEGMENT;
  3873.           POS             := POS + SEGMENT_SIZE;
  3874.         end loop;
  3875.  
  3876.         --Get tailing segment, if necessary.
  3877.         if POS < SIZE then
  3878.           CH_STR(POS+1..SIZE) := 
  3879.                           CURRENT_SEGMENT.SEGMENT(1..SIZE-POS);
  3880.         end if;
  3881.     end if;
  3882.   end CONVERT_TO_STRING;
  3883.  
  3884.  
  3885.   -- This function is used to check for separators which are two characters in
  3886.   -- in length.  A true value is returned only if the 1st two characters of the
  3887.   -- dynamic string match the two character parameters.  The dynamic string is
  3888.   -- not modified.
  3889.   function LOOKAHEAD(STR: DYNAMIC_STRING;
  3890.                      FIRST_CH: CHARACTER;
  3891.                      NEXT_CH : CHARACTER) return boolean is
  3892.   begin
  3893.     if STR.NUM_CHAR <= 1 then
  3894.       return FALSE;
  3895.     elsif STR.HEAD.SEGMENT(STR.FIRST) /= FIRST_CH then
  3896.       return FALSE;
  3897.     else
  3898.        if STR.FIRST /= SEGMENT_SIZE then
  3899.          return STR.HEAD.SEGMENT(STR.FIRST+1)    = NEXT_CH;
  3900.        else
  3901.          return STR.HEAD.NEXT_SEGMENT.SEGMENT(1) = NEXT_CH;
  3902.        end if;
  3903.     end if;
  3904.   end LOOKAHEAD;
  3905.  
  3906.  
  3907.  
  3908.  
  3909.  
  3910.  
  3911.   -- This procedure reads the Ith character within a dynamic string, where
  3912.   -- 0 < I <= LENGTH(string).  If I <= 0, a constraint error is raised; if
  3913.   -- I > LENGTH(string) an END_OF_STRING error is raised.  The dynamic
  3914.   -- string is unmodified.
  3915.   procedure READ
  3916.     (CHAR : out CHARACTER;
  3917.      ITH  : in POSITIVE;
  3918.      STR  : in DYNAMIC_STRING) is
  3919.     POS                : NATURAL;
  3920.     CURRENT_SEGMENT    : STR_SEGMENT;
  3921.   begin
  3922.     if ITH > STR.NUM_CHAR then
  3923.       raise END_OF_STRING;
  3924.  
  3925.     elsif ITH <= SEGMENT_SIZE - STR.FIRST + 1 then
  3926.       --The character is positioned within the head segment so
  3927.       --retrieval is immediate.
  3928.       CHAR := STR.HEAD.SEGMENT( STR.FIRST+ITH-1 );
  3929.  
  3930.     else
  3931.       --The character is positioned in a segment other than the
  3932.       --head so the correct segment must be found prior to retrieval.
  3933.       POS         := ITH - (SEGMENT_SIZE-STR.FIRST+1);
  3934.       CURRENT_SEGMENT := STR.HEAD.NEXT_SEGMENT;
  3935.       while POS > SEGMENT_SIZE loop
  3936.         POS             := POS - SEGMENT_SIZE;
  3937.         CURRENT_SEGMENT := CURRENT_SEGMENT.NEXT_SEGMENT;
  3938.       end loop;
  3939.       CHAR := CURRENT_SEGMENT.SEGMENT(POS);
  3940.     end if;
  3941.   end READ;
  3942.  
  3943.  
  3944.   -- This function compares two dynamic strings on a character by character
  3945.   -- basis.  Truth is returned only if the two string are of equal length
  3946.   -- and characters at each position are equal.
  3947.   function "="
  3948.     (LEFT  : DYNAMIC_STRING;
  3949.      RIGHT : DYNAMIC_STRING)
  3950.     return BOOLEAN is
  3951.  
  3952.     LEFT_CHAR  : CHARACTER;
  3953.     RIGHT_CHAR : CHARACTER;
  3954.   begin
  3955.     if LEFT.NUM_CHAR = RIGHT.NUM_CHAR then
  3956.  
  3957.       for I in 1..LEFT.NUM_CHAR loop
  3958.         READ(LEFT_CHAR, I,  LEFT);
  3959.         READ(RIGHT_CHAR,I,  RIGHT);
  3960.         if LEFT_CHAR /= RIGHT_CHAR then
  3961.           return FALSE;
  3962.         end if;
  3963.       end loop;
  3964.       return TRUE;
  3965.     
  3966.     else
  3967.       return FALSE;
  3968.     end if;
  3969.   end "=";
  3970.  
  3971.  
  3972.   -- This procedure adds a character to the end of a dynamic string, allocating
  3973.   -- a new segment when necessary.
  3974.   procedure APPEND
  3975.     (CHAR : in CHARACTER;
  3976.      STR  : in out DYNAMIC_STRING) is
  3977.      OLD_TAIL : STR_SEGMENT;
  3978.   begin
  3979.     if STR.NUM_CHAR = 0 then
  3980.       --This is a null string.  Allocate one segment and insert a
  3981.       --character.
  3982.       if STR.HEAD = null then
  3983.         STR.HEAD := new STR_REC;
  3984.       end if;
  3985.       STR.TAIL := STR.HEAD;
  3986.       STR.FIRST:= 1;
  3987.       STR.LAST := 1;
  3988.       STR.HEAD.SEGMENT    := BLANKS;
  3989.       STR.HEAD.SEGMENT(1) := CHAR;
  3990.  
  3991.     elsif STR.LAST = SEGMENT_SIZE then
  3992.       --A new segment must be added to hold this character.
  3993.       OLD_TAIL         := STR.TAIL;
  3994.       if STR.TAIL.NEXT_SEGMENT = null then
  3995.         STR.TAIL               := new STR_REC;
  3996.         STR.TAIL.PRIOR_SEGMENT := OLD_TAIL;
  3997.       else
  3998.         STR.TAIL               := STR.TAIL.NEXT_SEGMENT;
  3999.       end if;
  4000.       STR.TAIL.SEGMENT       := BLANKS;
  4001.       STR.TAIL.SEGMENT(1)    := CHAR;
  4002.       STR.LAST               := 1;
  4003.       OLD_TAIL.NEXT_SEGMENT  := STR.TAIL;
  4004.  
  4005.     else
  4006.       --There is room in this segment for the character.
  4007.       STR.LAST := STR.LAST+1;
  4008.       STR.TAIL.SEGMENT(STR.LAST) := CHAR;
  4009.  
  4010.     end if;
  4011.  
  4012.     STR.NUM_CHAR := STR.NUM_CHAR+1;
  4013.   end APPEND;
  4014.  
  4015.  
  4016.  
  4017.   -- This procedure uses the APPEND character procedure to add a character
  4018.   -- string to the end of a dynamic string.
  4019.   procedure APPEND
  4020.     (CHAR_STRING : in STRING;
  4021.      STR  : in out DYNAMIC_STRING) is
  4022.   begin
  4023.     for I in 1..CHAR_STRING'LENGTH loop
  4024.       APPEND(CHAR_STRING(I), STR);
  4025.     end loop;
  4026.   end APPEND;
  4027.  
  4028.  
  4029.   -- This procedure uses the APPEND charater procedure to add one dynamic 
  4030.   -- string to the end of another.  Characters are appended one at a time.
  4031.   -- Only the string being appended to is modified.
  4032.   procedure APPEND
  4033.     (D_STRING : in DYNAMIC_STRING;
  4034.      STR      : in out DYNAMIC_STRING) is
  4035.     
  4036.     CHAR : CHARACTER;
  4037.   begin
  4038.     for I in 1..D_STRING.NUM_CHAR loop
  4039.       READ(CHAR, I, D_STRING);
  4040.       APPEND(CHAR, STR);
  4041.     end loop;
  4042.   end APPEND;
  4043.  
  4044.  
  4045.   -- This procedure transfers the value of a character string to a dynamic
  4046.   -- string, replacing the original value of the dynamic string.
  4047.   procedure CONVERT_TO_DYNAMIC
  4048.     (CHAR_STRING : in STRING;
  4049.      STR  : in out DYNAMIC_STRING) is
  4050.   begin
  4051.     RESET(STR);
  4052.     APPEND(CHAR_STRING, STR);
  4053.   end CONVERT_TO_DYNAMIC;
  4054.  
  4055.   
  4056.   
  4057.   -- This procedure removes a token from the front of a dynamic string and
  4058.   -- also removes the token's separator.  Both the token and the separator
  4059.   -- are returned to the caller.  The separators are "  ();:\,  ", blank, and
  4060.   -- the comment delimiter --.  Extraneous blanks are ignored.  A semicolon is
  4061.   -- returned as the separator when the token is terminated by the end of string  -- An END_OF_STRING exception is raised only when the dynamic string from
  4062.   -- which the token is being extracted is already empty whe GET_TOKEN is called
  4063.   procedure GET_TOKEN
  4064.      (STR      : in out DYNAMIC_STRING;
  4065.       TOKEN    : out    DYNAMIC_STRING;
  4066.       SEPARATOR: out CHARACTER) is
  4067.      CHAR : CHARACTER;
  4068.   begin
  4069.     RESET(TOKEN);
  4070.     if EMPTY(STR) then
  4071.       raise END_OF_STRING;
  4072.     end if;
  4073.  
  4074.     --Get the next token and protect against end of string.
  4075.     begin
  4076.       --First skip all leading blanks
  4077.       READ(CHAR, STR);
  4078.       while CHAR = ' ' loop
  4079.         DELETE_CHAR(STR);
  4080.         READ(CHAR, STR);
  4081.       end loop;
  4082.  
  4083.       --Now copy characters until a separator is found.
  4084.       while CHAR /= ' ' and CHAR /= ',' and
  4085.             CHAR /= ':' and CHAR /= '\' and
  4086.             CHAR /= ';' and CHAR /= ')' and
  4087.             CHAR /= '(' and
  4088.             not LOOKAHEAD(STR, '-', '-')       loop
  4089.         APPEND(CHAR, TOKEN);
  4090.         DELETE_CHAR(STR);
  4091.         READ(CHAR, STR);
  4092.       end loop;
  4093.  
  4094.       --Gobble up the separator;
  4095.       SEPARATOR := CHAR;
  4096.       DELETE_CHAR(STR);
  4097.       if SEPARATOR = '-' then
  4098.         DELETE_CHAR(STR);
  4099.  
  4100.       else
  4101.       --Gobble up redundant blanks
  4102.         while not EMPTY(STR) loop
  4103.           READ(CHAR, STR);
  4104.           if CHAR = ' ' then
  4105.             DELETE_CHAR(STR);
  4106.           else
  4107.             exit;
  4108.           end if;
  4109.         end loop;
  4110.         if (SEPARATOR = ' ') and
  4111.            (CHAR = ',' or CHAR = ';'  or CHAR = ')'  or
  4112.             CHAR = '\' or CHAR = '('  or CHAR = ':') then
  4113.           SEPARATOR := CHAR;
  4114.           DELETE_CHAR(STR);
  4115.         end if;
  4116.       end if;
  4117.  
  4118.  
  4119.     exception
  4120.       when END_OF_STRING => SEPARATOR := ';';  --end of string is o.k. here.
  4121.     end;
  4122.  
  4123.   end GET_TOKEN;
  4124.  
  4125.   -- This function counts the number of non-empty tokens within a dynamic 
  4126.   -- string.  The string is unmodified.
  4127.   function NUM_OF_TOKENS (DYN_STR : DYNAMIC_STRING) return NATURAL is
  4128.  
  4129.     COUNT              : NATURAL := 0;
  4130.     TEMP_STR, TOKEN : DYNAMIC_STRING;
  4131.     SEPARATOR       : CHARACTER;
  4132.  
  4133.   begin
  4134.     ASSIGN(TEMP_STR, DYN_STR);
  4135.     loop
  4136.       GET_TOKEN(TEMP_STR, TOKEN, SEPARATOR); -- Raises exception at EOL.
  4137.       if not EMPTY(TOKEN) then
  4138.         COUNT := COUNT + 1;
  4139.       end if;
  4140.     end loop;
  4141.  
  4142.   exception
  4143.     when END_OF_STRING =>
  4144.       return (COUNT);
  4145.   end;
  4146.  
  4147.  
  4148. end STR_PACK;
  4149.