From: emery@Dr_No.mitre.org (David Emery)
Subject: Re: How are SLOC defined in Ada?
Organization: The Mitre Corp., Bedford, MA.
	<1992Sep22.184140.6329@nntpd2.cxo.dec.com>
Date: Mon, 28 Sep 1992 02:00:48 GMT

-- Here is the public domain 'statement counter', originally coded by 
-- Bill Whitaker in the dark ages.  I've made some modifications (some
-- of which may not work... :-), but the basic algorithm is unchanged.
-- 				dave
package Lines_Of_Code is 
  
  type Lines is 
    record
      AS    : Natural;			-- Ada statements
      Cmnts : Natural;			-- Comments
-- emery hack
      Lfs : Natural;			-- lines (newlines)
-- end of hack
    end record;

  function LOC (FILE_NAME  : STRING) return Lines;
  --|  Description
  --|    This function calculates the Ada statements and comments of a valid
  --|  Ada fragment specified by a FILE_NAME string parameter.
  --|  It need not be a complete compilation unit but it must have closed
  --|  all open parentheses and string delimiters.

  --|  The Ada statement is defined by a semicolon terminator
  --|  outside of comments, parentheses, or string or character literals
  --|  This definition is insensitive to formatting or layout of the source

  --|  William A. Whitaker  WIS JPMO   3 March 1984
  --|  Modified rh 17 May 1986
  --|  		emery 18 MAR 87

end Lines_Of_Code;
with text_io;
package natural_IO is new text_io.integer_io (natural);

with Lines_Of_Code; use Lines_Of_Code;
with Command_Line; use Command_Line;
with Text_IO; use Text_IO;
with Natural_IO; use Natural_IO;
procedure Count_Ada_Statements is 

  stmts_posn : Text_IO.positive_count := 35;
  cmts_posn : Text_IO.positive_count := 50;
  ls_posn : Text_IO.positive_count := 65;
  line_len : positive := 79;
  dashes : string(1..line_len) := (others => '_');

  Usage      : constant String  := "Usage: cas file1 .. filen ";

  type str_ptr is access string;
  totals : Lines_Of_Code.lines := (0,0,0);
  this_file : str_ptr;
  this_count : Lines_Of_Code.lines;


  procedure print_entry (s : string; l : Lines_of_Code.lines);
  procedure print_header;
  procedure print_entry (s : string; l : Lines_of_Code.lines)
  is separate;
  procedure print_header
  is separate;

begin

  if argc <= 1 then
    Put_line (Usage);
  else
    print_header;
    for I in 1..argc - 1 loop
      if (i mod 52 = 0) then
	print_header;
	Text_IO.new_page;
      end if;
      this_file := new string'(argv.all(i).all.s);
      this_count := loc (this_file.all);
      totals.AS := totals.AS + this_count.AS;
      totals.Cmnts := totals.Cmnts + this_count.Cmnts;
      totals.Lfs := totals.Lfs + this_count.Lfs;
      print_entry (s => this_file.all, l => this_count);
    end loop;
    Text_IO.put_line (dashes);
    Text_IO.new_line;
    print_entry (s => "***TOTALS***", l => totals);
  end if;
end Count_Ada_Statements;


with Text_IO; 
package body Lines_Of_Code is 

  use Text_IO;

  function Loc (File_Name  : String) return Lines is 

    Input    	  : File_Type; 
    C       	  : Character := ' '; 
    Line_Count    : Natural := 0; 
    Comment_Count : Natural := 0;
    Level   	  : Natural := 0; 
-- hack
    New_Lines 	  : Natural := 0;
-- end of hack

  begin 
    Open (Input, In_File, File_Name); 
    loop 
      Get (Input, C); 
      --  Check for comment on the line
      if C = '-' then 
	Get (Input, C); 
	--  Which is signaled by the '-' following a '-'
	if C = '-' then 
	  Comment_Count := Comment_Count + 1;
	  --  and then just skip the rest of the line and go to the next
	  Skip_Line (Input); 
	end if; 
      end if; 

      --  Check for one of the characters which introduce code constructs
      --  like string or character literal or formal parameter list
      --  within which a ';' does not terminate a statement
      if C = '(' or C = '"' or C = '%' or C = ''' then 
	--  Check for opening parentheses
	--  Every ';' within is in a formal parameter list
	if C = '(' then 
	  --  Count the number of levels of parentheses
	  Level := Level + 1; 
	  --  Read ahead until the whole construct is closed, LEVEL = 0
	  while Level > 0 loop 
	    Get (Input, C); 
	    if C = '(' then 
	      --  Increase the level if another '(' is found
	      Level := Level + 1; 
	    elsif C = ')' then 
	      --  Decrease the level if a ')' is found
	      Level := Level - 1; 
	    end if; 
	  end loop; 

	--  Now check for string brackets of either kind, " or %
	elsif C = '"' or C = '%' then 
	  --  Treat them in parallel, one must lead off
	  if C = '"' then 
	    loop 
	      Get (Input, C); 
	      --  Loop until the close comes
	      --  If there is a doubled character it just starts again
	      exit when C = '"'; 
	    end loop; 
	  --  The '%' is handled exactly the same way as '"'
	  elsif C = '%' then 
	    loop 
	      get (Input, C); 
	      exit when C = '%'; 
	    end loop; 
	  end if; 

	--  Character literals are just three characters long including '
	elsif C = ''' then 
	  Get (Input, C); 
	  Get (Input, C); 
	end if; 

      --  Any ';' that can be found at this point after all exclusions
      --  must be a valid "line of code" terminator
      elsif C = ';' then 
	Line_Count := Line_Count + 1; 

      end if; 


    end loop; 

  exception 
    --  Return through exception since the end of file may be encountered
    --  at several levels in the function and in any case it stops and returns
    --  Otherwise one would use END_OF_FILE but here it would have to appear
    --  in a number of places to no advantage
    --  and would cause multiple exits or returns
    --  This is much cleaner 
    when End_Error => 
--hacked
      new_lines := integer(Text_IO.line (Input));
      Text_IO.close (Input);
      return (Line_Count, Comment_Count, new_lines ); 
-- end of hacking
  end Loc; 


--  There has been a certain amount of discussion of how to quantify the 
-- number of lines of code in an ada program indeed this is a subject of some
-- discussion for most languages as ada becomes the dod standard for
-- implementations the question takes on more than a philosophical importance
-- whether you like it or not there are lots of pieces of paper generated in
-- which "lines of code" are quoted some of these are contracts and formal
-- costing estimates and can not easily be dismissed nor should there be any
-- ambiguity about their meaning money, at least, rides on the definition

--  there have been a number of stabs at this definition and i have faced
-- estimates within the past couple of weeks based on:

-- 	the number of physical lines (measured by counting ascii.cr)
-- 	the number of physical lines that are not comments or blank
-- 	the number of semicolons
-- 	the number of lines containing at least one semicolon

-- as a physicist i have a difficulty accepting measurements that will change
-- depending on formatting of the program or introduction of strings or
-- comments that do not change the execution therefore i would like to propose
-- a modification of the suggested methods that i believe is invarient, at
-- least to pretyprinting

-- the lines of code would be measured by counting the semicolon statement
-- terminators, after excluding:
-- 
--    comments
--    separators in formal parameter lists (by excluding parenthesized items)
--    string literals (set off by " or %)
--    character literals (i.e. ';')

-- i think this covers matters and makes a consistant and useful definition
-- as useful as any such quantification can be

--   i would very much like comments on this matter if i have fouled up let me
-- know right away or you may find this in a contract and your paycheck may
-- someday depend on it

--   in order to assure an unambiguous operational definition i am attaching a
-- ada function that describes the measure	 i believe that this does the
-- job without oversimplification or excessive complication

-- BILL WHITAKER

end Lines_Of_Code;


separate (Count_Ada_Statements)
procedure print_entry (s : string; l : Lines_of_Code.lines)
is
  next_col : positive;
begin
  Text_IO.put (s);
  next_col := s'length + 1;
  Text_IO.set_col (stmts_posn);
  natural_IO.put (l.AS);
  Text_IO.set_col (cmts_posn);
  natural_IO.put (l.Cmnts);
  Text_IO.set_col (ls_posn);
  natural_IO.put (l.Lfs);
  Text_IO.new_line;
end print_entry;

separate (Count_Ada_Statements)
procedure print_header
is
begin
  text_io.put ("file name");
  Text_IO.set_col (stmts_posn + 2);
  Text_IO.put ("Ada Stmts");
  Text_IO.set_col (cmts_posn + 2);
  text_io.put ("Comments");
  Text_IO.set_col (ls_posn + 1);
  text_io.put ("line feeds");
  Text_IO.new_line;
  text_IO.put_line (dashes);
end print_header;