Google
 

Trailing-Edge - PDP-10 Archives - decuslib20-07 - decus/20-0172/parse2.bli
There is 1 other file named parse2.bli in the archive. Click here to see a list.
!<BLF/lowercase_user>
!<BLF/uppercase_key>
MODULE prs2 (					!

%IF %BLISS (BLISS32)
%THEN
		ADDRESSING_MODE (EXTERNAL = LONG_RELATIVE, 	!
		NONEXTERNAL = LONG_RELATIVE) ,
%FI

		IDENT = '7.0'
		) =
BEGIN

!++
! Facility: BLISS Formatter
!
! Abstract:
!
!	This module formats BLISS declarations. The main
!	entry to this module is the routine 'prs$decl',
!	which is called to parse an entire declaration.
!	Since all declarations in BLISS end in a semicolon,
!	prs$decl gobbles up the semicolon.
!
! Environment: transportable, using Xport
!
!
! REVISION HISTORY
!
!	15-Sep-81	TT	In the beginning, there was PRETTY.
!				And then a programmer. And the Supervisor
!				said "Giveth this man something to maintain."
!				And so the union began. Forthwith is the
!				history of that union, given in words as yours
!				truely sees fit.
!
!				Many changes to allow for formal attributes
!				in routine declarations. Psect allocation
!				attributes weren't handled at all. And when a
!				routine was declared as forward & novalue, its
!				name was displayed to the terminal once at the
!				forward declaration and again when the routine
!				was actually found. Fix to DO_ROUTINE to assure
!				that each routine is displayed only once.
!
!	28-Sep-81	TT	A second kludge to PRS$DECL. A syntax change
!				permits Psects within Plit declarations, so
!				semicolon is no longer a valid terminator.
!				Special case Psects in Plits a la Psect
!				allocation attributes not to error out if a
!				semicolon is not found.
!
!	 4-Nov-81	TT	Remove hack I'd inserted in DO_ROUTINE that
!				permitted PRS$PAREN_ELIST to handle the new
!				Bliss V2.1 & V3 syntax in routine formals.
!				The hack was called IN_ROUTINE_DECL. Impetus
!				for removing hack was that DO_ROUTINE was not
!				finding the desired semi-colon in its main
!				loop, thought it was working with nested
!				routines, and therefore stopppd putting out
!				page marks. This occurred on all routines AFTER
!				a routine of the form ROUT (A : B) was parsed.
!				The fix is a new routine, called
!				DO_ROUTINE_FORMALS to handle the left through
!				the first right parenthesis.
!				
! END OF REVISION HISTORY
!--

!<BLF/page>
!
! Table of contents:
!--

FORWARD ROUTINE
    do_attr_list : NOVALUE,
    do_bind : NOVALUE,
    do_decl_def : NOVALUE,
    do_enable : NOVALUE,
    do_external : NOVALUE,
    do_field : NOVALUE,
    do_global : NOVALUE,
    do_kwmacro : NOVALUE,
    do_linkage : NOVALUE,
    do__macro : NOVALUE,
    do_macro_body : NOVALUE,
    do_name_list : NOVALUE,
    do_psect : NOVALUE,
    do_require : NOVALUE,
    do_routine : NOVALUE,
    do_routine_formals : NOVALUE,
    do_structure : NOVALUE,
    in_set,
    prs$decl : NOVALUE,
    prs$_mac_level,
    prs$set_level : NOVALUE;

!
! Include files:
!--

REQUIRE 'BLFCSW';				! Defines control switches, i.e. 'sw_...'

REQUIRE 'BLFMAC';				! Defines WRITE, LEX, etc.

REQUIRE 'SYMCOD';				! Defines symbol property table, 'sym...'

REQUIRE 'TOKTYP';				! Defines 'token' and the token type values 's_...'

REQUIRE 'UTLCOD';				! Defines error codes, i.e. 'er_...'

!
! Macros:
!--

MACRO
    plit_count (aplit) =
	( ( aplit ) - %UPVAL ) %;

!
! Equated symbols:
!--

LITERAL
    true = 1 EQL 1,
    false = 1 NEQ 1;

!
! Own storage:
!--

OWN
    macro_level : INITIAL (0);			! For imbedded macro-defs

!
! External references:
!--

EXTERNAL
    symprop : sym_table;

EXTERNAL ROUTINE
    ctl$switch,
    lex$getsym : NOVALUE,			!
    lst$module : NOVALUE,
    lst$routine : NOVALUE,
    out$break : NOVALUE,			!
    out$default : NOVALUE,
    out$erase : NOVALUE,
    out$eject : NOVALUE,			!
    out$force : NOVALUE,			!
    out$indent : NOVALUE,			!
    out$ntbreak : NOVALUE,			!
    out$pend_skip : NOVALUE,			!
    out$set_tab : NOVALUE,
    out$skip : NOVALUE,				!
    out$space : NOVALUE,			!
    out$stoks : NOVALUE,			!
    out$terminal : NOVALUE,
    out$tok : NOVALUE,				!
    prs$body : NOVALUE,
    prs$expression : NOVALUE,			! Parse3
    prs$module : NOVALUE,			! Parse1
    prs$paren_elist : NOVALUE,			! Parse3
    prs$plit_body : NOVALUE,			! Parse1
    prs$switches : NOVALUE,			! Parse1
    scn$mbstrt : NOVALUE,			! Manually format macros
    scn$mfin : NOVALUE,				!
    scn$plit : NOVALUE,
    utl$error : NOVALUE;			!

EXTERNAL
    tok;

EXTERNAL
    token : tok_block;				! One symbol at a time

GLOBAL

    !+
    ! Labelled block causes an ambiguity in BIND declarations
    ! Because colon is used in a declaration
    ! And a labelled block is an expression.
    !   e.g.
    !	BIND foo = name : ???????
    ! Are we at a labelled block or is "name" the bound
    ! expression?
    ! So we disallow labelled blocks in declaration expressions
    ! following equal signs.
    !-
    nolabl : INITIAL (false);
global ROUTINE do_attr_list (block_context) : NOVALUE = 	!

!++
! Functional description:
!
!	This routine parses attribute lists for declarations. It is
!	more general than the BLISS language in its permitted
!	sequences of attributes, and thus will overlook many errors
!	in BLISS syntax.
!
! Formal parameters:
!
!	None
!
! Implicit inputs:
!
!	None
!
! Implicit outputs:
!
!	None
!
! Routine value:
!
!	None
!
! Side effects:
!
!	None
!
!--

    BEGIN
    if .tok eql s_initial
    then
	out$skip (1)
    else
	begin
	! A colon or "(" has been recognized by the parser...
	out$stoks ();				! " : " or "("
	lex;
	out$indent (1);
	end;

    !+
    ! Pick up attributes
    !-

    WHILE .tok NEQ s_end_of_file DO

	SELECT .tok OF
	    SET

	    [s_byte, s_word, s_long, s_global, s_local] :
		(out$tok (); lex; );

	    [s_name, s_field, s_psect] :
		BEGIN
		out$stoks ();			! Name, " FIELD ", or " PSECT "
		lex;

		IF .symprop [.tok, sym_type] EQL open_bracket THEN prs$paren_elist (.block_context);

		END;

	    [s_initial] :
		BEGIN
		out$stoks ();			! " INITIAL "

		IF NOT ctl$switch (sw_plit) THEN scn$plit (+1);	! Suppress formatting of PLIT-bodies

		lex;
		prs$plit_body (.block_context);

		IF NOT ctl$switch (sw_plit) THEN scn$plit (-1);

		END;

	    [s_comma] :
		EXITLOOP;

	    [s_colon] :
		(out$stoks (); lex; );		! " : "

	    [s_semicolon, s_rparen] :
		EXITLOOP;

	    [OTHERWISE] :
		(utl$error (er_semi_decl); out$default (); lex; );
	    TES;

    out$indent (-1);
    END;					! End of routine 'do_attr_list'
ROUTINE do_bind (block_context) : NOVALUE = 	!

!++
! Functional description:
!
!	This routine is called to handle BIND declarations.
!
! Formal parameters:
!
!	None
!
! Implicit inputs:
!
!	None
!
! Implicit outputs:
!
!	None
!
! Routine value:
!
!	None
!
! Side effects:
!
!	None
!
!--

    BEGIN

    IF .tok EQL s_routine THEN (out$stoks (); lex);	! " ROUTINE "

    do_decl_def (.block_context);
    RETURN;
    END;					! End of routine 'do_bind'
ROUTINE do_decl_def (block_context) : NOVALUE = 	!

!++
! Functional description:
!
!	This routine is the default declarations handler.
!	It is used for declarations like: MAP, LOCAL, LITERAL, etc.
!
! Formal parameters:
!
!	None
!
! Implicit inputs:
!
!	None
!
! Implicit outputs:
!
!	None
!
! Routine value:
!
!	None
!
! Side effects:
!
!	None
!
!--

    BEGIN
    out$force ();

    WHILE .tok NEQ s_semicolon DO
	BEGIN

	!+
	! Once through for each item in the declaration
	!-

	IF .tok EQL s_name
	THEN
	    BEGIN
	    out$tok ();				! The name of the declared item
	    lex;

	    IF .symprop [.tok, sym_type] EQL open_bracket THEN prs$paren_elist (.block_context);

	    END
	ELSE
	    utl$error (er_name);

	IF .tok EQL s_equal
	THEN
	    BEGIN
	    out$stoks ();			! " = "
	    lex;
	    nolabl = true;			! See comment on 'nolabl' decl
	    prs$expression (.block_context);	! The bound value
	    nolabl = false;
	    END;

	IF .tok EQL s_colon
	THEN
	    BEGIN
	    do_attr_list (.block_context);
	    END;

	IF .tok EQL s_comma
	THEN
	    (out$erase (); out$tok (); out$force (); lex; )	! ","
	ELSE

	    IF .tok NEQ s_semicolon THEN (utl$error (er_semi_decl); out$default (); lex; );

	END;

    END;					! End of routine 'do_decl_def'
ROUTINE do_enable (block_context) : NOVALUE = 	!

!++
! Functional description:
!
!	This routine formats ENABLE declarations.
!
! Formal parameters:
!
!	None
!
! Implicit inputs:
!
!	None
!
! Implicit outputs:
!
!	None
!
! Routine value:
!
!	None
!
! Side effects:
!
!	None
!
!--

    BEGIN
    out$force ();
    out$stoks ();				! " ENABLE "
    lex;

    IF .tok EQL s_lparen
    THEN
	BEGIN
	prs$paren_elist (.block_context);
	END;

    RETURN;
    END;					! End of routine 'do_enable'
ROUTINE do_external (block_context) : NOVALUE = 	!

!++
! Functional description:
!
!	This routine is invoked for the "EXTERNAL" declarator
!
! Formal parameters:
!
!	None
!
! Implicit inputs:
!
!	None
!
! Implicit outputs:
!
!	None
!
! Routine value:
!
!	None
!
! Side effects:
!
!	None
!
!--

    BEGIN

    IF .tok EQL s_routine OR 			!
	.tok EQL s_literal OR 			!
	.tok EQL s_register
    THEN
	(out$stoks (); lex; );			! " ROUTINE " or " LITERAL " or " REGISTER "

    do_decl_def (.block_context);
    RETURN
    END;					! End of routine 'do_external'
ROUTINE do_field (block_context) : NOVALUE = 	!

!++
! Functional description:
!	Format a "FIELD" declaration.
!
! Formal parameters:
!
!	None
!
! Implicit inputs:
!
!	None
!
! Implicit outputs:
!
!	None
!
! Routine value:
!
!	None
!
! Side effects:
!
!	None
!
!--

    BEGIN
    out$force ();				! Put keyword on one line,

    WHILE .tok NEQ s_end_of_file DO 		! then the rest.
	BEGIN

	IF .tok NEQ s_name THEN utl$error (er_name);

	out$default ();				! Name being declared
	lex;

	IF .tok NEQ s_equal THEN utl$error (er_equal);

	out$default ();				! " = "
	out$force ();
	lex;

	IF .tok EQL s_set
	THEN 					! "Set" form
	    BEGIN
	    out$indent (1);
	    out$tok ();				! Print "set"
	    out$force ();			! On a new line
	    lex;

	    WHILE .tok NEQ s_end_of_file DO
		BEGIN

		IF .tok NEQ s_name THEN utl$error (er_name);

		out$default ();			! Field name
		lex;

		IF .tok NEQ s_equal THEN utl$error (er_equal);

		out$default ();			! " = "
		lex;
		prs$paren_elist (.block_context);	! Field components

		!+
		! The next token is a comma or "tes"
		!-

		IF .tok EQL s_tes
		THEN
		    BEGIN
		    out$force ();
		    out$tok ();			! "TES"
		    out$indent (-1);
		    lex;
		    EXITLOOP;
		    END
		ELSE

		    IF .tok EQL s_comma
		    THEN
			(out$erase (); out$tok (); out$force (); lex; )	! ","
		    ELSE
			utl$error (er_tes);

		END;

	    END
	ELSE
	    prs$paren_elist (.block_context);

	IF .tok EQL s_semicolon
	THEN
	    EXITLOOP 				! prs$decl will clean up
	ELSE

	    IF .tok EQL s_comma
	    THEN
		(out$erase (); out$tok (); out$force (); lex; )	! ","
	    ELSE
		utl$error (er_semi_decl);

	END;

    END;					! End of routine 'do_field'
ROUTINE do_global (block_context) : NOVALUE = 	!

!++
! Functional description:
!
!	This routine is invoked for the "GLOBAL" declarator.
!
! Formal parameters:
!
!	None
!
! Implicit inputs:
!
!	None
!
! Implicit outputs:
!
!	None
!
! Routine value:
!
!	None
!
! Side effects:
!
!	None
!
!--

    BEGIN

    SELECT .tok OF
	SET

	[s_bind] :
	    BEGIN
	    out$stoks ();
	    out$force ();
	    lex;
	    do_bind (.block_context);
	    END;

	[s_literal] :
	    BEGIN
	    out$stoks ();
	    out$force ();
	    lex;
	    do_decl_def (.block_context);
	    END;

	[s_routine] :
	    BEGIN
	    out$stoks ();
	    lex;
	    do_routine (.block_context);
	    END;

	[s_register] :
	    BEGIN
	    out$stoks ();			! " REGISTER "
	    out$force ();
	    lex;

	    WHILE .tok NEQ s_semicolon DO
		BEGIN
		nolabl = true;			! Assure colon seen as attr list head
		out$indent (-1);
		prs$expression (.block_context);	! "name=value"
		out$indent (+1);
		nolabl = false;

		IF .tok EQL s_colon THEN do_attr_list (.block_context);

		IF .tok EQL s_comma THEN (out$tok (); out$force (); lex)

		END;

	    END;

	[OTHERWISE] :
	    do_decl_def (.block_context);
	TES;

    END;					! End of routine 'do_global'
ROUTINE do_kwmacro (block_context) : NOVALUE = 	!

!++
! Functional description:
!
!	This routine formats KEYWORDMACRO declarations
!
! Formal parameters:
!
!	None
!
! Implicit inputs:
!
!	None
!
! Implicit outputs:
!
!	None
!
! Routine value:
!
!	None
!
! Side effects:
!
!	None
!
!--

    BEGIN

    LOCAL
	level;

    macro_level = .macro_level + 1;

    WHILE .tok NEQ s_end_of_file DO
	BEGIN

	!+
	! Once per macro declaration
	!-

	out$break ();
	out$stoks ();				! Macro name
	lex;

	IF .tok EQL s_lparen
	THEN
	    BEGIN				! Pick up formals list
	    out$tok ();				! "("
	    out$force ();
	    out$indent (2);
	    lex;

	    WHILE .tok NEQ s_end_of_file DO
		BEGIN

		!+
		! Once for each keyword formal
		!-

		IF .tok EQL s_name
		THEN
		    BEGIN
		    out$tok ();			! Formal parameter name
		    lex;
		    level = 0;

		    IF .tok EQL s_equal
		    THEN
			BEGIN

			!+
			!  Keyword default value expression
			!-

			out$stoks ();		! " = "
			lex;

			DO
			    BEGIN

			    SELECTONE .tok OF
				SET

				[s_lparen, s_lbracket, s_langle] :
				    (level = .level + 1; out$tok (); );

				[s_rparen, s_rbracket, s_rangle] :
				    (level = .level - 1; out$tok (); );

				[OTHERWISE] :
				    out$stoks ();
				TES;

			    lex;
			    END
			UNTIL (.level EQL 0 AND .tok EQL s_comma)	!
			    OR .level LSS 0	!
			    OR .tok EQL s_semicolon;

			END

		    END
		ELSE
		    utl$error (er_name);

		IF .tok EQL s_comma
		THEN
		    (out$erase (); out$tok (); out$force (); lex; )	! ","
		ELSE

		    IF .level LSS 0 THEN EXITLOOP;

		IF .tok EQL s_rparen
		THEN
		    BEGIN
		    out$tok ();			! ")"
		    lex;
		    EXITLOOP;
		    END;

		END;

	    out$indent (-2);
	    END					! of argument list.
	ELSE
	    utl$error (er_formal_list);

	IF .tok EQL s_equal
	THEN
	    BEGIN
	    do_macro_body (PLIT (s_percent));
	    out$space (1);
	    out$tok ();				! " %"

	    IF NOT ctl$switch (sw_macro) THEN scn$mfin (s_macro);	! Resume automatic formatting

	    lex;
	    END
	ELSE
	    utl$error (er_macro_body);

	IF .tok EQL s_comma
	THEN
	    BEGIN
	    out$erase ();
	    out$tok ();				! ","
	    lex;
	    END
	ELSE
	    BEGIN
	    macro_level = .macro_level - 1;
	    EXITLOOP;				! ";" Is handled by prs$decl
	    END;

	END

    END;					! End of routine 'do_kwmacro'
ROUTINE do_linkage (block_context) : NOVALUE = 	!

!++
! Functional description:
!
!	This routine parses a LINKAGE declaration.
!
! Formal parameters:
!
!	None
!
! Implicit inputs:
!
!	None
!
! Implicit outputs:
!
!	None
!
! Routine value:
!
!	None
!
! Side effects:
!
!	None
!
!--

    BEGIN

    WHILE .tok NEQ s_end_of_file DO
	BEGIN
	out$force ();
	out$stoks ();				! Name being declared
	lex;

	IF .tok EQL s_equal
	THEN
	    BEGIN
	    out$stoks ();			! " = "
	    lex;

	    IF .tok EQL s_name			! Name = linkage-type
	    THEN
		BEGIN

		!+
		! linkage-options
		!-

		out$stoks ();			! Linkage type
		lex;

		IF .tok EQL s_lparen
		THEN

		!+
		! parameter-locations list
		!-

		    BEGIN
		    out$tok ();			! "("
		    lex;

		    WHILE .tok NEQ s_end_of_file DO

			SELECTONE .tok OF
			    SET

			    [s_register] :
				BEGIN
				out$tok ();
				lex;

				IF .tok EQL s_equal
				THEN
				    (out$stoks (); lex;
				    prs$expression (.block_context); )
				ELSE
				    (utl$error (er_equal); out$default (); lex; );

				END;

			    [s_name] :
				(out$tok (); lex; );

			    [s_comma] :
				(out$erase (); out$tok (); out$space (1); lex; );

			    [s_rparen] :
				(out$tok (); lex; EXITLOOP );

			    [s_semicolon] :
				(out$tok (); out$space (1); lex; );

			    [OTHERWISE] :
				(utl$error (er_rparen); out$default (); lex; );
			    TES;

		    END;

		SELECTONE .tok OF
		    SET

		    [s_colon] :
			BEGIN

			!+
			! linkage modifiers
			!-

			out$stoks ();		! " : "
			lex;

			WHILE .tok NEQ s_end_of_file DO

			    SELECTONE .tok OF
				SET

				[s_global, s_name] :
				    BEGIN
				    out$stoks ();	! " GLOBAL " etc.
				    lex;

				    !+
				    ! GLOBAL-REGISTER-segment
				    ! or PRESERVE/NOPRESERVE-segment
				    !-

				    IF .tok EQL s_lparen
				    THEN
					BEGIN
					prs$paren_elist (.block_context);

					IF .tok NEQ s_comma AND .tok NEQ s_semicolon THEN out$force ();

					END;

				    END;

				[s_comma] :
				    BEGIN
				    out$erase ();
				    out$tok ();	! ", "
				    out$space (1);
				    lex;
				    EXITLOOP;
				    END;

				[s_semicolon] :
				    RETURN;

				[OTHERWISE] :
				    (out$default (); lex; );
				TES;

			END;

		    [s_comma] :
			(out$erase (); out$tok (); out$space (1); lex; );

		    [s_semicolon] :
			RETURN;

		    [OTHERWISE] :
			(utl$error (er_semi_decl); out$default (); lex; );
		    TES;

		END;

	    END
	ELSE

	    IF .tok EQL s_semicolon THEN RETURN ELSE (utl$error (er_equal); out$default (); lex; );

	END;

    END;					! End of routine 'do_linkage'
ROUTINE do__macro (block_context) : NOVALUE = 	!

!++
! Functional description:
!
!	This routine formats positional MACROs
!
! Formal parameters:
!
!	None
!
! Implicit inputs:
!
!	None
!
! Implicit outputs:
!
!	None
!
! Routine value:
!
!	None
!
! Side effects:
!
!	None
!
!--

    BEGIN
    macro_level = .macro_level + 1;

    WHILE .tok NEQ s_end_of_file DO
	BEGIN

	!+
	! Once for each positional macro
	!-

	out$break ();
	out$stoks ();				! Macro name.
	lex;

	IF .tok EQL s_lparen
	THEN
	    BEGIN
	    prs$paren_elist (.block_context);
	    END;

	IF .tok EQL s_lbracket
	THEN
	    BEGIN
	    out$space (1);
	    prs$paren_elist (.block_context);
	    END;

	IF .tok EQL s_equal
	THEN
	    BEGIN
	    do_macro_body (PLIT (s_percent));

	    IF .tok EQL s_percent
	    THEN
		BEGIN
		out$space (1);
		out$tok ();			! "%"

		IF NOT ctl$switch (sw_macro) THEN scn$mfin (s_macro);	! Resume auto. formatting

		lex;
		END
	    ELSE
		utl$error (er_end_macro)

	    END
	ELSE
	    utl$error (er_equal);

	IF .tok EQL s_comma
	THEN
	    BEGIN
	    out$erase ();
	    out$tok ();				! ","
	    lex;
	    END
	ELSE
	    BEGIN
	    macro_level = .macro_level - 1;
	    EXITLOOP;
	    END;

	END

    END;					! End of routine 'do__macro'
ROUTINE do_macro_body (block_context) : NOVALUE = 	!

!++
! Functional description:
!
!	This routine formats a macro body
!
! Formal parameters:
!
!	None
!
! Implicit inputs:
!
!	None
!
! Implicit outputs:
!
!	None
!
! Routine value:
!
!	None
!
! Side effects:
!
!	None
!
!--

    BEGIN
    out$indent (1);

    IF .tok EQL s_equal
    THEN
	BEGIN
	out$stoks ();				! " = "

	IF NOT ctl$switch (sw_macro) THEN scn$mbstrt (s_macro);	! Begin non-formatting

	out$force ();
	lex;
	END
    ELSE
	utl$error (er_equal);

    WHILE .tok NEQ s_end_of_file DO
	BEGIN

	IF .tok EQL s_percent
	THEN
	    EXITLOOP
	ELSE

	    IF ctl$switch (sw_macro)
	    THEN
		prs$body (s_percent)
	    ELSE
		(out$stoks (); lex; )		! Terminating symbol
	END;

    out$indent (-1);
    END;					! End of routine 'do_macro_body'
ROUTINE do_name_list (right_close) : NOVALUE = 	!

!++
! Functional description:
!
!	This is a utility routine that formats a name list
!
! Formal parameters:
!
!	Right_close - PLIT of integers representing the set of allowable
!			closing brackets
!
! Implicit inputs:
!
!	None
!
! Implicit outputs:
!
!	None
!
! Routine value:
!
!	None
!
! Side effects:
!
!	None
!
!--

    BEGIN
    out$tok ();					! Initial delimiter, e.g. "("
    lex;

    WHILE .tok EQL s_name DO
	BEGIN
	out$tok ();				! Name
	lex;

	IF .tok NEQ s_comma
	THEN
	    EXITLOOP
	ELSE
	    BEGIN
	    out$erase ();
	    out$tok ();				! ", "
	    out$space (1);
	    lex;
	    END;

	END;

    IF in_set (.tok, .right_close) THEN RETURN ELSE utl$error (er_name_list);

    END;					! End of routine 'do_name_list'
ROUTINE do_psect (block_context) : NOVALUE = 	!

!++
! Functional description:
!
!	This routine parses PSECT declarations.
!
! Formal parameters:
!
!	None
!
! Implicit inputs:
!
!	None
!
! Implicit outputs:
!
!	None
!
! Routine value:
!
!	None
!
! Side effects:
!
!	None
!
!--

    BEGIN

    if .tok eql s_lparen		! PSECT allocation attribute
    then				! TT   15-Sep-81  thru
	begin
	out$default ();
	lex;

	if .tok eql s_name
	then
	    begin
	    out$tok ();
	    lex
	    end
	else
	    begin
	    utl$error(er_name);
	    out$default ();
	    lex
	    end;

	if .tok eql s_rparen
	then
	    begin
	    out$default ();
	    lex
	    end
	else
	    begin
 	    utl$error(er_rparen);
	    out$default ();
	    lex
	    end;

	if .tok eql s_initial
	then
	    do_attr_list (.block_context);
	end
	
    else					! TT   15-Sep81 ^
	WHILE .tok NEQ s_semicolon DO
	BEGIN

	!+
	! Once for each storage class (OWN, GLOBAL, etc.)
	!-

	out$force ();

	IF .tok EQL s_own OR 			!
	    .tok EQL s_global OR 		!
	    .tok EQL s_plit OR 			!
	    .tok EQL s_name
	    ! "name" is permitted, to handle "CODE" cleanly.
	THEN
	    BEGIN
	    out$stoks ();
	    lex;

	    IF .tok EQL s_equal
	    THEN
		(out$stoks (); lex; )		! " = "
	    ELSE
		(utl$error (er_equal); out$default (); lex; );


	    IF .tok EQL s_name
	    THEN
		BEGIN
                if .tok eql s_name
                then
	            (out$tok (); lex;);		! name

		IF .tok EQL s_lparen
		THEN
		    BEGIN

		    LOCAL
			plevel;

		    out$tok ();			! "("
		    lex;
		    plevel = 1;

		    UNTIL .plevel EQL 0 DO
			BEGIN

			SELECT .tok OF
			    SET

			    [s_lparen] :
				(plevel = .plevel + 1; out$default ());	! "("

			    [s_rparen] :
				(plevel = .plevel - 1; out$default ());	! ")"

			    [first_decl TO last_decl] :
				out$stoks ();	! "LOCAL", etc.

			    [OTHERWISE] : 	! Anything else...
				out$default ();
			    TES;

			lex;
			END;

		    END

		END
	    ELSE
    
		(utl$error (er_name); out$default (); lex);

	    END
	ELSE
	    BEGIN
	    utl$error (er_stge_class);
	    out$default ();
	    lex;
	    EXITLOOP;
	    END;

	IF .tok EQL s_comma
	THEN
	    (out$erase (); out$tok (); out$force (); lex; )
	ELSE
	    IF .tok NEQ s_semicolon THEN (utl$error (er_semi_decl); out$default (); lex; EXITLOOP );

	END;

    END;					! End of routine 'do_psect'
ROUTINE do_require : NOVALUE = 			!

!++
! Functional description:
!
!	This routine handles LIBRARY and REQUIRE declarations.
!
! Formal parameters:
!
!	None
!
! Implicit inputs:
!
!	None
!
! Implicit outputs:
!
!	None
!
! Routine value:
!
!	None
!
! Side effects:
!
!	None
!
!--

    BEGIN

    IF .tok EQL s_string
    THEN
	(out$stoks (); lex; )			! Required file name
    ELSE
	utl$error (er_string);

    RETURN;
    END;					! End of routine 'do_require'
ROUTINE do_routine (block_context) : NOVALUE = 	!

!++
! Functional description:
!
!	This routine processes all "ROUTINE" declarations.
!
! Formal parameters:
!
!	None
!
! Implicit inputs:
!
!	None
!
! Implicit outputs:
!
!	None
!
! Routine value:
!
!	None
!
! Side effects:
!
!	None
!
!--

    BEGIN

    OWN
	nest : INITIAL (0);			! ROUTINE nesting level
    local
	once;					!  TT   15-Sep-81

    once = false;

    DO
	BEGIN

	!+
	! An indent is done before dispatching to a declaration
	! handler. This is valid for most declarations, but
	! not for ROUTINEs, so it must be un-done.
	!-

	! Save the routine name for the listing file heading line.
	lst$routine (.token [tok_len], .token [tok_cp]);
	out$tok ();				! Routine name

	IF .tok EQL s_name and not .once
	THEN
	    begin
	    out$terminal ();		! Display routine name on the terminal
	    once = true;
	    end;

	IF .nest EQL 0 THEN out$eject (s_routine);	! First level routines start a new page

	nest = .nest + 1;
	out$space (1);
	lex;

	IF .tok EQL s_lparen			! TT  4-Nov-81
	THEN
	    BEGIN				! Pick up the formal list
	    do_routine_formals ();
	    lex;
	    END;

	IF .tok EQL s_colon
	THEN
	    BEGIN				! Pick up attributes
	    out$stoks ();			! " : "
	    lex;

	    WHILE .tok EQL s_name OR .tok EQL s_psect DO

		IF .tok EQL s_psect
		THEN
		    BEGIN
		    out$stoks ();		! " PSECT "
		    lex;

		    IF .tok EQL s_lparen THEN prs$paren_elist (.block_context);

		    END
		ELSE
		    (out$stoks (); lex; );	! Attribute name

	    END;				! Pick up attributes

	IF .tok EQL s_equal
	THEN
	    BEGIN
	    out$stoks ();			! " = "
	    out$force ();
	    lex;
	    END;

	out$indent (-1);			! See note on indentation above
	prs$expression (.block_context);
	out$indent (+1);

	IF .tok EQL s_semicolon THEN EXITLOOP;

	IF .tok EQL s_comma or .tok eql s_rparen
	THEN
	    (out$erase (); out$tok (); out$space (1); lex; )	! ","
	ELSE
	    (utl$error (er_semi_decl); out$default (); lex; );

	END
    UNTIL .tok EQL s_semicolon;

    nest = .nest - 1;
    RETURN;
    END;					! End of routine 'do_routine'
ROUTINE do_routine_formals : NOVALUE =

!++
! Functional description:
!
!	This routine processes all argument lists on routine declarations.
!	PRS$PAREN_ELIST used to do this but the new V2.1/V3 syntax got things
!	upset. Now we have a separate routine. Much cleaner.
!
! Formal Parameters:
!
!	None
!
! Implicit inputs:
!
!	Expects "(" to be the current TOKen.
!
! Routine value:
!
!	None
!
! Side effects
!
!	None
!
!--

    BEGIN
    out$tok ();				! "("
    lex;

    UNTIL .tok EQL s_rparen DO
    BEGIN
	SELECTONE .tok OF
 	SET						! Allowed in formals.
	[s_name]:			(out$tok ());
	[s_comma]:			(out$tok (); out$space(1));
	[s_semicolon]:			(out$stoks ());
	[s_colon]:			(out$stoks ());
	[OTHERWISE]:			(out$default ());  ! Lost.
	TES;

	lex				! Next TOKen in the formals list.
    END;

    out$tok ();				! ")"
    END;				! End of routine 'do_routine_formals'
ROUTINE do_structure (block_context) : NOVALUE = 	!

!++
! Functional description:
!
!	This routine formats STRUCTURE declarations.
!
! Formal parameters:
!
!	None
!
! Implicit inputs:
!
!	None
!
! Implicit outputs:
!
!	None
!
! Routine value:
!
!	None
!
! Side effects:
!
!	None
!
!--

    BEGIN

    DO
	BEGIN

	!+
	! For each STRUCTURE declaration
	!-

	out$break ();
	out$tok ();				! Structure name
	lex;

	IF .tok EQL s_lbracket
	THEN
	    BEGIN
	    out$space (1);
	    do_name_list (PLIT (s_semicolon, s_rbracket));

	    IF .tok EQL s_semicolon
	    THEN
		BEGIN
		out$erase ();
		out$tok ();			! "; "
		out$space (1);
		lex;

		DO
		    BEGIN

		    !+
		    ! Each access formal and default
		    !-

		    IF .tok NEQ s_name THEN EXITLOOP;

		    out$tok ();			! Allocation formal
		    lex;

		    IF .tok EQL s_equal
		    THEN
			BEGIN
			out$stoks ();		! " = "
			lex;
			prs$expression (.block_context);
			END;

		    IF .tok EQL s_comma
		    THEN
			BEGIN
			out$erase ();
			out$tok ();		! ","
			out$space (1);
			lex;
			END;

		    END
		UNTIL .tok EQL s_rbracket;

		END;

	    IF .tok EQL s_rbracket
	    THEN
		BEGIN
		out$tok ();			! "]"
		lex;
		END
	    ELSE
		utl$error (er_rbracket);

	    END;

	IF .tok EQL s_equal
	THEN
	    BEGIN
	    out$stoks ();			!" = "
	    out$indent (1);
	    out$force ();
	    lex;

	    IF .tok EQL s_lbracket
	    THEN
		BEGIN
		out$tok ();			! "["
		lex;
		prs$expression (.block_context);	! Structure size expression

		IF .tok NEQ s_rbracket THEN utl$error (er_rbracket) ELSE (out$tok (); out$force (); lex; );

						! "]"
		END;

	    out$indent (-1);
	    prs$expression (.block_context);	! Structure body
	    END;

	IF .tok EQL s_comma
	THEN
	    (out$erase (); out$tok (); out$space (1); lex; )	! ","
	ELSE

	    IF .tok NEQ s_semicolon THEN utl$error (er_semi_decl);

	END
    UNTIL .tok EQL s_semicolon;

    END;					! End of routine 'do_structure'
ROUTINE in_set (elem, sett) = 			!

!++
! Functional description:
!
!	This routine returns 'true' if the elem is in the set
!	and 'false' otherwise. A set is represented by a PLIT.
!
! Formal parameters:
!
!	Elem - an integer
!	Sett - a PLIT of integers
!
! Implicit inputs:
!
!	None
!
! Implicit outputs:
!
!	None
!
! Routine value:
!
!	True or false
!
! Side effects:
!
!	None
!
!--

    BEGIN

    MAP
	sett : REF VECTOR;

    INCR i FROM 0 TO .plit_count (.sett) - 1 DO

	IF .elem EQL .sett [.i] THEN RETURN true;

    RETURN false;
    END;					! End of routine 'in_set'
GLOBAL ROUTINE prs$decl (block_context) : NOVALUE = 	!

!++
! Functional description:
!
!	This routine is called to format a declaration through
!	its semicolon.
!
! Formal parameters:
!
!	None
!
! Implicit inputs:
!
!	None
!
! Implicit outputs:
!
!	None
!
! Routine value:
!
!	None
!
! Side effects:
!
!	None
!
!--

    BEGIN

    LOCAL
	i,					! Loop index
	ltok;					! The declarator

    LITERAL
	num_types = 16;

    BIND
	decl = UPLIT (
	    s_bind, 							!
	    s_enable, 							!
	    s_external, 						!
	    s_field, 							!
	    s_forward, 							!
	    s_global, 							!
	    s_keywordmacro, 						!
	    s_library, 							!
	    s_linkage, 							!
	    s_macro, 							!
	    s_module, 							!
	    s_psect, 							!
	    s_require, 							!
	    s_routine, 							!
	    s_structure, 						!
	    s_switches							!
	    ) : VECTOR [num_types],
	dispatch = UPLIT (do_bind,
	    do_enable, 							!
	    do_external, 						!
	    do_field, 							!
	    do_external, 						! FORWARD
	    do_global, 							!
	    do_kwmacro, 						!
	    do_require, 						! LIBRARY
	    do_linkage, 						!
	    do__macro, 							!
	    prs$module, 						!
	    do_psect, 							!
	    do_require, 						!
	    do_routine, 						!
	    do_structure, 						!
	    prs$switches						!
	    ) : VECTOR [num_types];

    ltok = .tok;

    IF .ltok NEQ s_routine AND
	.ltok NEQ s_module AND
	 .ltok NEQ s_psect			! TT   15-Sep-81
	THEN out$skip (1);			! Skip a line before most decls

    out$tok ();					! Declarative keyword
    out$space (1);

    IF .ltok NEQ s_module THEN out$indent (1);

    lex;
    i = 0;

    WHILE .tok NEQ s_end_of_file DO
	BEGIN

	IF .i EQL num_types
	THEN
	    (do_decl_def (.block_context); EXITLOOP; )
	ELSE

	    IF .ltok EQL .decl [.i] THEN ((.dispatch [.i]) (.block_context); EXITLOOP; ) ELSE i = .i + 1;

	END;

    IF .ltok NEQ s_module THEN out$indent (-1);

    IF .tok EQL s_eludom
    THEN
	BEGIN
	out$ntbreak ();
	out$tok ();				! "ELUDOM"
	out$break ();
	lex;
	! These calls occur only if "ELUDOM" is not
	! the last token in the file, e.g. if another MODULE follows.
	lst$module (0, 0);			! Erase module name
	out$eject (s_eludom);
	RETURN;
	END;

    IF .tok EQL s_rparen AND .ltok EQL s_psect		! TT 15-Sep-81
    THEN					! Did a PSECT allocation attr.
	return;

    IF .tok EQL s_Lparen AND .ltok EQL s_psect		! TT 28-Sep-81
    THEN					! Did a PSECT in a Plit/Uplit.
	return;

    IF .tok NEQ s_semicolon
    THEN					! In the routine decl, comma is
	utl$error (er_semi_decl)		!  the delimiter, not semicolon
    ELSE
	BEGIN
	out$erase ();
	out$tok ();				! ";" or ","
	out$force ();
	out$set_tab (true);			! Assure following lines tabbed ok

	IF .ltok NEQ s_routine THEN out$pend_skip (1);	! Skip before comments

	lex;
	END;

    END;					! End of routine 'prs$decl'
GLOBAL ROUTINE prs$_mac_level = 		!

!++
! Functional description:
!
!	This function returns the value of the own variable "macro_level"
!	for use in formatting %IF, etc. within macro definitions.
!
! Formal parameters:
!
!	None
!
! Implicit inputs:
!
!	None
!
! Implicit outputs:
!
!	None
!
! Routine value:
!
!	None
!
! Side effects:
!
!	None
!
!--

    BEGIN
    RETURN .macro_level;
    END;					! End of routine 'prs$_mac_level'
GLOBAL ROUTINE prs$set_level : NOVALUE = 	!

!++
! Functional description:
!
!	This routine sets the value of the own variable "macro_level"
!	to zero. This must be done between files in case there is an
!	incomplete macro definition in some file.
!
! Formal parameters:
!
!	None
!
! Implicit inputs:
!
!	None
!
! Implicit outputs:
!
!	None
!
! Routine value:
!
!	None
!
! Side effects:
!
!	None
!
!--

    BEGIN
    macro_level = 0;
    END;					! End of routine 'prs$set_level'
%TITLE 'Last page of Module "PARSE2.BLI"'
END						! End of module 'PARSE2'

ELUDOM