::::::::::
parser.pro
::::::::::

-------- SIMTEL20 Ada Software Repository Prologue ------------
--                                                           -*
-- Unit name    : PARSER
-- Version      : 1.0
-- Author       : Richard Conn
--              : Texas Instruments, Ada Technology Branch
--              : PO Box 801, MS 8007
--              : McKinney, TX  75069
-- DDN Address  : RCONN at SIMTEL20
-- Copyright    : (c) 
-- Date created :  21 July 85
-- Release date :  30 July 85
-- Last update  :  30 July 85
-- Machine/System Compiled/Run on : DG MV10000 (ROLM ADE) and
--					DEC VAX 11/785 (DEC Ada)
--                                                           -*
---------------------------------------------------------------
--                                                           -*
-- Keywords     :  parser, generic parser, UNIX, ARGC, ARGV
----------------:
--
-- Abstract     :  
--
--|   PARSER is a generic parser that functions in a manner similar to
--| the ARGC/ARGV parser of UNIX.  It contains one procedure, PARSE,
--| which accepts a string as input and returns ARGC, a count of the number
--| of tokens in the string, and ARGV, a vector of strings, each string
--| containing a token.
--|
--|   PARSER is instantiated with two strings (DEL for DELIMITER and
--| DEL_TOKEN for DELIMITER_TOKEN).  The DEL string is composed of characters
--| which delimit each token (and are not a part of the token).  All
--| characters less than space are automatically delimiters, and the DEL
--| string should contain at least one character (such as a space).
--| DEL_TOKEN is a string composed of characters which delimit tokens and
--| which are tokens themselves.  If "=" is a DEL_TOKEN, for example, then
--| "CAT= DOG" is composed of three tokens, "CAT", "=", and "DOG", where if
--| "=" is a DEL, then "CAT= DOG" is composed of two tokens, "CAT" and "DOG".
--| This assumes that the space character is a DEL.
--|
--|   PARSER may also be instantiated with ARGC_LIMIT, which indicates the
--| maximum number of tokens allowed.  If this limit is exceeded, then the
--| last ARGV token contains the remainder of the string.  The default value
--| of ARGC_LIMIT is 20.
--|
--|   ARG_STRING_LENGTH is the last instantiation option for PARSER.  It
--| indicates the maximum length of an ARGV string, and it defaults to 80.
--|
--                                                           -*
------------------ Revision history ---------------------------
--                                                           -*
-- DATE         VERSION	AUTHOR                  HISTORY
-- 19850730        1.0  Richard Conn            Initial Release
--                                                           -*
------------------ Distribution and Copyright -----------------
--                                                           -*
-- This prologue must be included in all copies of this software.
--
-- This software is released to the Ada community.
-- This software is released to the Public Domain (note:
--   software released to the Public Domain is not subject
--   to copyright protection).
-- Restrictions on use or distribution:  NONE
--                                                           -*
------------------ Disclaimer ---------------------------------
--                                                           -*
-- This software and its documentation are provided "AS IS" and
-- without any expressed or implied warranties whatsoever.
-- No warranties as to performance, merchantability, or fitness
-- for a particular purpose exist.
--
-- Because of the diversity of conditions and hardware under
-- which this software may be used, no warranty of fitness for
-- a particular purpose is offered.  The user is advised to
-- test the software thoroughly before relying on it.  The user
-- must assume the entire risk and liability of using this
-- software.
--
-- In no event shall any person or organization of people be
-- held responsible for any direct, indirect, consequential
-- or inconsequential damages or lost profits.
--                                                           -*
-------------------END-PROLOGUE--------------------------------
::::::::::
parser.ada
::::::::::
--|MODULE: PARSER
--|AUTHOR: CONN
--|LOCATION: COMPONENTS
--|ABSTRACT:
--|   PARSER is a generic parser that functions in a manner similar to
--| the ARGC/ARGV parser of UNIX.  It contains one procedure, PARSE,
--| which accepts a string as input and returns ARGC, a count of the number
--| of tokens in the string, and ARGV, a vector of strings, each string
--| containing a token.
--|
--|   PARSER is instantiated with two strings (DEL for DELIMITER and
--| DEL_TOKEN for DELIMITER_TOKEN).  The DEL string is composed of characters
--| which delimit each token (and are not a part of the token).  All
--| characters less than space are automatically delimiters, and the DEL
--| string should contain at least one character (such as a space).
--| DEL_TOKEN is a string composed of characters which delimit tokens and
--| which are tokens themselves.  If "=" is a DEL_TOKEN, for example, then
--| "CAT= DOG" is composed of three tokens, "CAT", "=", and "DOG", where if
--| "=" is a DEL, then "CAT= DOG" is composed of two tokens, "CAT" and "DOG".
--| This assumes that the space character is a DEL.
--|
--|   PARSER may also be instantiated with ARGC_LIMIT, which indicates the
--| maximum number of tokens allowed.  If this limit is exceeded, then the
--| last ARGV token contains the remainder of the string.  The default value
--| of ARGC_LIMIT is 20.
--|
--|   ARG_STRING_LENGTH is the last instantiation option for PARSER.  It
--| indicates the maximum length of an ARGV string, and it defaults to 80.
--|
generic
    DEL               : STRING;
    DEL_TOKEN         : STRING;
    ARGC_LIMIT        : NATURAL := 20;
    ARG_STRING_LENGTH : NATURAL := 80;

package PARSER is

--
-- The following defines the system-dependent types/constants used within this
-- package.  It will probably be necessary to customize them for the
-- application.
--
    subtype ARG_STRING is STRING (1 .. ARG_STRING_LENGTH);

--
-- The following defines the PARSER-specific types which will be referenced
-- externally.
--
    type ARGUMENT is
        record
            ARG_TEXT : ARG_STRING;
            ARG_LAST : NATURAL;
        end record;
    type ARGS is array (1 .. ARGC_LIMIT) of ARGUMENT;

--
-- The following is the parser routine
--
    procedure PARSE (INPUT_LINE : STRING;
                     ARGC       : out NATURAL;
                     ARGV       : out ARGS);

end PARSER;

package body PARSER is

    procedure PARSE (INPUT_LINE : STRING;
                     ARGC       : out NATURAL;
                     ARGV       : out ARGS) is
        LARGC : NATURAL;
        LARGV : ARGS;
        POS   : NATURAL;
        APOS  : NATURAL;
        TPOS  : NATURAL;

        DELIM, DELIM_TOKEN, DELIM_TOKEN_FIRST : exception;

    begin
        POS := INPUT_LINE'FIRST;
        LARGC := 0;
        loop
            exit when POS > INPUT_LINE'LAST;
            if INPUT_LINE (POS) > ' ' then
                LARGC := LARGC + 1;
                APOS := LARGV (LARGC).ARG_TEXT'FIRST;
                if LARGC = ARGC_LIMIT then
                    while POS <= INPUT_LINE'LAST loop
                        LARGV (LARGC).ARG_TEXT (APOS) := INPUT_LINE (POS);
                        APOS := APOS + 1;
                        POS := POS + 1;
                    end loop;
                    TPOS := POS;
                else
                    begin
                        for J in DEL_TOKEN'FIRST .. DEL_TOKEN'LAST loop
                            if INPUT_LINE (POS) = DEL_TOKEN (J) then
                                raise DELIM_TOKEN_FIRST;
                            end if;
                        end loop;
                        for I in POS .. INPUT_LINE'LAST loop
                            TPOS := I;
                            exit when INPUT_LINE (I) < ' ';
                            for J in DEL'FIRST .. DEL'LAST loop
                                if INPUT_LINE (I) = DEL (J) then
                                    raise DELIM;
                                end if;
                            end loop;
                            for J in DEL_TOKEN'FIRST .. DEL_TOKEN'LAST loop
                                if INPUT_LINE (I) = DEL_TOKEN (J) then
                                    raise DELIM_TOKEN;
                                end if;
                            end loop;
                            if APOS <= LARGV (LARGC).ARG_TEXT'LAST then
                                LARGV (LARGC).ARG_TEXT (APOS) :=
                                  INPUT_LINE (I);
                                APOS := APOS + 1;
                            end if;
                        end loop;
                    exception
                        when DELIM =>
                            null;
                        when DELIM_TOKEN =>
                            TPOS := TPOS - 1;
                        when DELIM_TOKEN_FIRST =>
                            LARGV (LARGC).ARG_TEXT (APOS) := INPUT_LINE (POS);
                            APOS := APOS + 1;
                            TPOS := POS;
                        when others =>
                            raise;
                    end;
                end if;
                LARGV (LARGC).ARG_LAST := APOS - 1;
                POS := TPOS;
            end if;
            POS := POS + 1;
        end loop;
        ARGC := LARGC;
        ARGV := LARGV;
    end PARSE;

end PARSER;
::::::::::
ptest.ada
::::::::::
with PARSER,
     TEXT_IO;
procedure PTEST is
    INLINE : STRING (1 .. 80);
    LAST   : NATURAL;
    package NATIO is new TEXT_IO.INTEGER_IO (NATURAL);
--
-- Set up PARSER Package
--
    MY_DELIMITER       : STRING (1 .. 2) := " ,";
    MY_DELIMITER_TOKEN : STRING (1 .. 22) := "!&'()*+-/:;<=>?[\]{|}~";
    package MYPARSE is new PARSER
               (ARGC_LIMIT => 10,
                DEL        => MY_DELIMITER,
                DEL_TOKEN  => MY_DELIMITER_TOKEN);
    use MYPARSE;
    ARGC : NATURAL;
    ARGV : ARGS;
--
begin
    loop
        TEXT_IO.PUT ("Line> ");
        TEXT_IO.GET_LINE (INLINE, LAST);
        exit when LAST = 0;
        PARSE (INLINE (1 .. LAST), ARGC, ARGV);
        TEXT_IO.PUT ("Argument count = ");
        NATIO.PUT (ARGC, 2);
        TEXT_IO.NEW_LINE;
        if ARGC > 0 then
            for I in 1 .. ARGC loop
                NATIO.PUT (I, 2);
                TEXT_IO.PUT (" - ");
                NATIO.PUT (ARGV (I).ARG_LAST, 2);
                TEXT_IO.PUT ("> ");
                TEXT_IO.PUT (ARGV (I).ARG_TEXT (1 .. ARGV (I).ARG_LAST));
                TEXT_IO.NEW_LINE;
            end loop;
        end if;
    end loop;
end PTEST;
