TEAM-ADA Archives

Team Ada: Ada Programming Language Advocacy

TEAM-ADA@LISTSERV.ACM.ORG

Options: Use Classic View

Use Proportional Font
Show Text Part by Default
Show All Mail Headers

Topic: [<< First] [< Prev] [Next >] [Last >>]

Print Reply
"Carlisle, Martin" <[log in to unmask]>
Thu, 14 Oct 1999 09:16:26 -0600
text/plain (225 lines)
After the signature block, please find the new AdaGIDe statistics counter
source (in Ada) that counts semicolons, lines, and other useful things.
Feel free to contribute new statistics.

Note in particular that semicolons inside character literals, strings and
comments are not counted.

This will appear in the next release of AdaGIDE (available at
http://wuarchive.wustl.edu/languages/ada/swtools/adagide)
in a day or so.

--Martin

--------------------------------------
Martin C. Carlisle,
Asst Prof of Computer Science
US Air Force Academy
[log in to unmask]
DISCLAIMER: The content of this message contains the author's opinions, and
does not reflect the policy of the US Air Force Academy or US Government.
---------------------------------------


---------------------------------------------------------------
--
--  ADA GNAT INTEGRATED DEVELOPMENT ENVIRONMENT (AdaGIDE)
--
--  COUNT.ADB
--  Description : compute Ada statistics
--
--  By: Dr. Martin C. Carlisle
--
--  Original Copyright (C) 1999 Martin C. Carlisle
--  This is a derivative work by:
--     US Air Force Academy Department of Computer Science
--
-- AdaGIDE is free software; you can redistribute it and/or
-- modify it under terms of the GNU General Public License
-- as published by the Free Software Foundation and modified
-- below; either version 2, or (at your option) any later version.
-- AdaGIDE is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty
-- of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-- See the GNU General Public License for more details.
-- You should have received a copy of the GNU General Public
-- License distributed with AdaGIDE; see file COPYING.HTML.  If
-- not, write to the Free Software Foundation, 59 Temple Place,
-- Suite 330, Boston, MA 02111-1307, USA.
--
---------------------------------------------------------------

with Ada.Text_Io;
use Ada.Text_Io;
with Ada.Integer_Text_Io;
use Ada.Integer_Text_Io;
with Ada.Command_Line;
use Ada.Command_Line;
with Ada.Strings.Unbounded;
with Ada.Strings.Fixed;
procedure Count is
   procedure Usage is
   begin
      Put_Line("Usage: " & Command_Name & " file1 [file2...]");
   end Usage;

   -- Get a line, no matter how long it is.
   -- Could use recursion here, but instead I'm using
   -- an unbounded string
   function Get_Line (
         File : File_Type )
     return String is
      Part : String (1 .. 256);
      Last : Natural;
      Long : Ada.Strings.Unbounded.Unbounded_String;
      use type Ada.Strings.Unbounded.Unbounded_String;
   begin
      Get_Line(File, Part, Last);
      if Last = Part'Last then
         Long := Ada.Strings.Unbounded.To_Unbounded_String(Part);
         while Last=Part'Last loop
            Get_Line(File,Part,Last);
            Long := Long & Part(1..Last);
         end loop;
         return Ada.Strings.Unbounded.To_String(Long);
      else
         return Part(1..Last);
      end if;
   end Get_Line;

   procedure Process_File (
         Name : in     String ) is
      Semicolons                  : Natural   := 0;
      Lines_Ending_In_Semicolon   : Natural   := 0;
      Lines                       : Natural   := 0;
      Blank_Lines                 : Natural   := 0;
      Comment_Lines               : Natural   := 0;
      File                        : File_Type;
   begin
      Put_Line("FILE: " & Name);
      Open(
         File => File,
         Name => Name,
         Mode => In_File);
      while not End_Of_File(File) loop
         declare
            Line             : String  := Get_Line (File);
            First_Char       : Natural := Ada.Strings.Fixed.Index_Non_Blank
              (Line);
            Comment_Location : Natural := Ada.Strings.Fixed.Index (Source
              => Line, Pattern => "--");
         begin
            Lines := Lines + 1;
            if First_Char=0 then
               Blank_Lines := Blank_Lines + 1;
            elsif First_Char = Comment_Location then
               Comment_Lines := Comment_Lines + 1;
            else
               -- we must use a state machine to walk through the string
               -- if we're here we know that we have neither a comment
               -- nor a blank line
               -- count semicolons if not inside strings, stop at comment
               declare
                  type State is
                        (In_String,
                         Code,
                         Minus);
                  Current_State  : State   := Code;
                  I              : Natural;
                  Last_Character : Natural;
               begin
                  I := Line'First;
                  while I <= Line'Last loop
                     case Line(I) is
                        when ''' =>
                           -- check for character literal
                           if Current_State/=In_String then
                              Current_State := Code; -- no longer in minus
                              if i+2<=Line'Last and then
                                 Line(i+2) = ''' then
                                 i:=i+2; -- go past char literal
                              end if;
                           end if;
                        when '-' =>
                           if Current_State=Code then
                              Current_State := Minus;
                           elsif Current_State = Minus then
                              I := I - 1; -- see below
                              exit; -- start of comment
                           end if;
                        when '"' =>
                           if Current_State=In_String then
                              Current_State := Code;
                           else
                              Current_State := In_String;
                           end if;
                        when ';' =>
                           if Current_State /= In_String then
                              Semicolons := Semicolons + 1;
                           end if;
                        when others =>
                           null;
                     end case;
                     I := I + 1;
                  end loop;
                  -- at this point, i is either one past end of line
                  -- or at the first - of a comment
                  -- in either case, check if semicolon is last
                  -- non-blank of Line(Line'First..i-1)
                  -- note we know line has at least one non-blank
                  Last_Character := Ada.Strings.Fixed.Index_Non_Blank(
                    Source => Line (Line'First .. I - 1),
                    Going  => Ada.Strings.Backward);
                  if Line(Last_Character) = ';' then
                     Lines_Ending_In_Semicolon :=
                        Lines_Ending_In_Semicolon
                        + 1;
                  end if;
               end;
            end if;
         end;
      end loop;
      Close(File);

      -- now that we have looped through whole file, print out the stats
      Put("Total Lines:                 ");
      Put(Lines);
      New_Line;
      Put("Lines ending w/semicolon:    ");
      Put(Lines_Ending_In_Semicolon);
      New_Line;
      Put("Blank Lines:                 ");
      Put(Blank_Lines);
      New_Line;
      Put("Comment Lines:               ");
      Put(Comment_Lines);
      New_Line;
      Put("Non-blank non-comment lines: ");
      Put(Lines-(Blank_Lines+Comment_Lines));
      New_Line;
      Put("Semicolons:                  ");
      Put(Semicolons);
      New_Line(2);
   exception
      when Name_Error =>
         Put_Line("File not found");
      when Status_Error|Use_Error =>
         Put_Line("Error reading file");
   end Process_File;
begin
   Put_Line("Ada Statistics Counter 1.0");
   Put_Line("October 14, 1999");
   Put_Line("By: Martin C. Carlisle");
   Put_Line("[log in to unmask]");
   New_Line;

   if Argument_Count < 1 then
      Usage;
      return;
   end if;

   for i in 1..Argument_Count loop
      Process_File(Argument(i));
   end loop;
end Count;

ATOM RSS1 RSS2