Google
 

Trailing-Edge - PDP-10 Archives - CFS_TSU04_19910205_1of1 - update/t20src/diulex.bli
There are 4 other files named diulex.bli in the archive. Click here to see a list.
MODULE DIULEX  (IDENT='253' %BLISS32 (,
		 ADDRESSING_MODE (EXTERNAL = GENERAL))) =

!	COPYRIGHT (C) DIGITAL EQUIPMENT CORPORATION 1986.
!	ALL RIGHTS RESERVED.
!
!	THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY  BE  USED  AND
!	COPIED ONLY IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE AND WITH
!	THE INCLUSION OF THE ABOVE COPYRIGHT NOTICE.   THIS  SOFTWARE  OR
!	ANY  OTHER  COPIES  THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE
!	AVAILABLE TO ANY OTHER PERSON.  NO TITLE TO AND OWNERSHIP OF  THE
!	SOFTWARE IS HEREBY TRANSFERRED.
!
!	THE INFORMATION IN THIS SOFTWARE IS  SUBJECT  TO  CHANGE  WITHOUT
!	NOTICE  AND  SHOULD  NOT  BE CONSTRUED AS A COMMITMENT BY DIGITAL
!	EQUIPMENT CORPORATION.
!
!	DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE OR  RELIABILITY  OF
!	ITS SOFTWARE ON EQUIPMENT THAT IS NOT SUPPLIED BY DIGITAL.
!
!++
!
!  TITLE:  PARLEX
!
!  FACILITY:  Data Interchange Utility
!
!  ABSTRACT:
!
!	This module contains all the routines necessary to do a lexical
!	scan of the tokens for the Data Definition Utility language.
!
!  ENVIRONMENT:
!
!	All routines run in user access mode.
!
!	These routines are NOT AST reentrant.
!
!  AUTHOR:  Colleen Deininger,	28-MAY-81
!
!++
!
! MODIFICATION HISTORY:
!
!   Version 3.0
!	20-Jul-84       (CLR) Put XPORT use back in!  Use compatible Bliss!
!	23-Feb-84 01693 (CGM) Remove XPORT use from the CDDL.
!	 6-Jan-84 01506 (CDD) 
!	21-Dec-83 01296 (mrw) 
!	 9-Dec-83 01102 (mrw)  
!	23-Nov-83 01035 (CGM) FIX BUG WHERE THE STATUS FROM DDU$$P_LEX_NUMBER
!	                      IS NOT CHECKED.
!	21-Nov-83 00868 (CDD) 
!	30-Sep-83 00771 (MRW) moved imbedded CDDL messages to message file
!	 1-Sep-83 00755 (CGM) Adding the INDEXED FOR COBOL BY clause.
!	26-Jul-83 00730 (KJM) Added support for PL/I
!	 8-Apr-83 00653 (MAB) Allow CDD objects to have version numbers.
!
!	17-Feb-83 00475 (CDD) Promote to new version
!
!	12-Jan-83 00116 (CDD) Change the version number to X3.0
!
!	16-Sep-82 00033 (CDD) 
!
!    Version 2.0
!	29-Jul-82	(CGM)	Changed error severities, names and messages.
!
!	22-Jul-82	(CGM)	Added handling for DBMS datatype synonyms.
!
!	19-Jul-82	(CGM)	Added support for form feeds "<FF>".  Form
!				feeds are replaced with a space when parsing
!				the source.
!
!	23-Jun-82	(CGD)	Deleted support for back slashes since they
!				are no longer used in fully qualified names.
!
!	3-Mar-82	(CGD)	Made LEX check for redundant EOF token &
!				return.
!				Made CDDL$_VALUEOVFL warning continue with
!				the current token rather than ignoring it.
!				Changed CDDL$_BADDESC to CDDL$_BADCHAR.
!				Made LEX_COLUMN know about tabs and be
!				set accordingly.
!
!	13-JAN-82	(CGD)	Added /RECOMPILE option capabilities.
!
!  253  Rename file to DIULEX.
!       Gregory A. Scott 1-Jul-86
!
!--
BEGIN

!   INCLUDE FILES:

	require 'DIUPATPROLOG';
	library 'BLI:XPORT';
	%if %BLISS (BLISS32) %THEN
	require 'VMS';
	%FI
	library 'DIUPATDATA';
	library 'DIUDEB';
	library 'DIUPATLANGSP';

! Table of contents:

    FORWARD ROUTINE
	DDU$$P_LEX_NAME,		! Parse CDDname, pathname, or keyword
	DDU$$P_LEX_NUMBER,		! Parse numeric token
	DDU$$P_LEX_PERCENT,		! Parse token beginning with percent
	ENTER_TEXT	: NOVALUE,	! Store text in the heap
	GET_CHAR	: NOVALUE,	! Get next input character
	LEX		: NOVALUE,	! Get next token
	PAR_COLUMN_NUMBER,		! Column number for token
	PAR_GET_TOKEN,			! Manage token storage
	PAR_LINE_NUMBER,		! Line number of a token
	UTL_GET;			! Do heap allocation
! Declarations for lexical locator

%if %bliss (BLISS36) %then
    literal
	RMS$_EOF		= 9999,
	SS$_NORMAL		= 1;
%fi

    LITERAL
	DDU$K_SOURCE		= 0,
	COLUMNS_PER_TAB		= 8,
	MAX_NUM_LEX_TOKS	= 20;

    $FIELD
	LOC_COLUMN		= [$BYTE],
	LOC_LINENUM		= [$SHORT_INTEGER],
	LOC_UNUSED		= [$BYTE];

    literal
	LOC_SIZE		= $FIELD_SET_SIZE*%upval;

    MACRO
	LOC_STR =
	    BLOCK [LOC_SIZE/%upval]
		FIELD (LOC_COLUMN, LOC_LINENUM, LOC_UNUSED) %;

    literal
	BUF_SIZE		= 256;

! Information about the present lexical token

    OWN
	FILE_IOB :		ref $XPO_IOB (),
	END_OF_INPUT_LINE :	VOLATILE INITIAL (FALSE),
	START_LINE :		%BLISS32 (LONG),
	LEX_LINENUM :		%BLISS32 (LONG)	INITIAL (0),
	LEX_COLUMN :		%BLISS32 (LONG)	INITIAL (0),
	COLUMN :		%BLISS32 (LONG)	INITIAL (0),
	LEX_LOCATOR :		LOC_STR,
	LEX_TKNSTRT :		%BLISS32 (LONG),
	TOKEN_STORAGE :		BLOCKVECTOR [MAX_NUM_LEX_TOKS, TKN_SIZE]
				FIELD (TKN_FIELDS),
	NEXT_TOKEN_INDEX :	%BLISS32 (LONG)	INITIAL (0);
GLOBAL ROUTINE PAR_LINE_NUMBER (L) =
!++
! FUNCTIONAL DESCRIPTION:
!
!	Return the line number for a lexical token.
!
! INPUT PARAMETERS:
!
!	L		Lexical location structure
!
! OUTPUT PARAMETERS:
!
!	NONE
!
! IMPLICIT INPUTS:
!
!	NONE
!
! IMPLICIT OUTPUTS:
!
!	NONE
!
!--
BEGIN

    MAP
	L :	LOC_STR;

    RETURN .L [LOC_LINENUM]
END;
GLOBAL ROUTINE LEX_INIT (file) : NOVALUE =
!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine sets up the file IOB of the file to be parsed.
!	It should be called from PAT$PARSER before parsing begins.
!
! INPUT PARAMETERS:
!
!	file	Address of the XPORT IOB for the file to be parsed.
!
! OUTPUT PARAMETERS:
!
!	None
!
! IMPLICIT INPUTS:
!
!	None
!
! IMPLICIT OUTPUTS:
!
!	file_iob	Set to the address of the XPORT IOB for the file to
!			be parsed.
!
!--
BEGIN

file_iob = .file;
END;
GLOBAL ROUTINE PAR_COLUMN_NUMBER (L) =
!++
! FUNCTIONAL DESCRIPTION:
!
!	Return the column number for a lexical token.
!
! INPUT PARAMETERS:
!
! 	L		Lexical location
!
! OUTPUT PARAMETERS:
!
!	NONE
!
! IMPLICIT INPUTS:
!
!	NONE
!
! IMPLICIT OUTPUTS:
!
!	NONE
!
!--
BEGIN
    MAP
	L :	LOC_STR;

    RETURN .L [LOC_COLUMN]
END;
ROUTINE DDU$$P_LEX_NAME (TOKEN_PTR, CHAR_PTR) =
!++
!
! FUNCTIONAL DESCRIPTION:
!
!	This routine lexically parses a CDD_name, path name or keyword token.
!
!  PARAMETERS:
!
!	token_ptr		address of the token block being defined.
!	char_ptr		pointer to current character position.
!
!  IMPLICIT INPUTS:
!
!	END_OF_INPUT_LINE	TRUE if current character is at end of line
!	LEX_TKNSTRT		Starting character position of current token
!
!  IMPLICIT OUTPUTS:
!
!	END_OF_INPUT_LINE	Flag for last character on input line
!	LEX_TKNSTRT		Starting character position of current token
!
!  COMPLETION STATUS:
!
!	SS$_NORMAL if OK, FALSE if not
!
!  SIGNALLED STATUS:
!
!	This routine does not intercept signals sent by routines it calls.
!
!  SIDE EFFECTS:
!
!	May cause more input lines to be read.
!
!--
BEGIN
    MAP
	TOKEN_PTR 	: REF	TKN_STR;

    BIND
	CHAR		= .CHAR_PTR;
! Classes of characters that might appear in input text:

    LITERAL
	I_ILLEGAL	= 0,
	I_OTHER		= 1,
	I_LETTER	= 2,
	I_L_PAREN	= 3,
	I_R_PAREN	= 4,
	I_DOT		= 5,
	I_PSSWORD	= 6,
	I_DIGIT		= 7,
	I_SEMICOLON	= 8;

! Class of each ASCII character:

    BIND
	IDENT = UPLIT %BLISS32 (BYTE) (
		I_ILLEGAL,	! 00 NUL
		I_ILLEGAL,	! 01 SOH
		I_ILLEGAL,	! 02 STX
		I_ILLEGAL,	! 03 ETX
		I_ILLEGAL,	! 04 EOT
		I_ILLEGAL,	! 05 ENQ
		I_ILLEGAL,	! 06 ACK
		I_ILLEGAL,	! 07 BEL
		I_ILLEGAL,	! 08 BS
		I_PSSWORD,	! 09 HT
		I_ILLEGAL,	! 0A LF
		I_ILLEGAL,	! 0B VT
		I_ILLEGAL,	! 0C FF
		I_ILLEGAL,	! 0D CR
		I_ILLEGAL,	! 0E SO
		I_ILLEGAL,	! 0F SI
		I_ILLEGAL,	! 10 DLE
		I_ILLEGAL,	! 11 DC1
		I_ILLEGAL,	! 12 DC2
		I_ILLEGAL,	! 13 DC3
		I_ILLEGAL,	! 14 DC4
		I_ILLEGAL,	! 15 NAK
		I_ILLEGAL,	! 16 SYN
		I_ILLEGAL,	! 17 ETB
		I_ILLEGAL,	! 18 CAN
		I_ILLEGAL,	! 19 EM
		I_ILLEGAL,	! 1A SUB
		I_ILLEGAL,	! 1B ESC
		I_ILLEGAL,	! 1C FS
		I_ILLEGAL,	! 1D GS
		I_ILLEGAL,	! 1E RS
		I_ILLEGAL,	! 1F US
		I_PSSWORD,	! 20 SP
		I_PSSWORD,	! 21 !
		I_PSSWORD,	! 22 "
		I_PSSWORD,	! 23 #
		I_OTHER,	! 24 $
		I_PSSWORD,	! 25 %
		I_PSSWORD,	! 26 &
		I_PSSWORD,	! 27 '
		I_L_PAREN,	! 28 (
		I_R_PAREN,	! 29 )
		I_PSSWORD,	! 2A *
		I_PSSWORD,	! 2B +
		I_PSSWORD,	! 2C ,
		I_OTHER,	! 2D -
		I_DOT,		! 2E .
		I_PSSWORD,	! 2F /
		I_DIGIT,	! 30 0
		I_DIGIT,	! 31 1
		I_DIGIT,	! 32 2
		I_DIGIT,	! 33 3
		I_DIGIT,	! 34 4
		I_DIGIT,	! 35 5
		I_DIGIT,	! 36 6
		I_DIGIT,	! 37 7
		I_DIGIT,	! 38 8
		I_DIGIT,	! 39 9
		I_PSSWORD,	! 3A :
		I_SEMICOLON,	! 3B ;
		I_PSSWORD,	! 3C <
		I_PSSWORD,	! 3D =
		I_PSSWORD,	! 3E >
		I_PSSWORD,	! 3F ?
		I_PSSWORD,	! 40 @
		I_LETTER,	! 41 A
		I_LETTER,	! 42 B
		I_LETTER,	! 43 C
		I_LETTER,	! 44 D
		I_LETTER,	! 45 E
		I_LETTER,	! 46 F
		I_LETTER,	! 47 G
		I_LETTER,	! 48 H
		I_LETTER,	! 49 I
		I_LETTER,	! 4A J
		I_LETTER,	! 4B K
		I_LETTER,	! 4C L
		I_LETTER,	! 4D M
		I_LETTER,	! 4E N
		I_LETTER,	! 4F O
		I_LETTER,	! 50 P
		I_LETTER,	! 51 Q
		I_LETTER,	! 52 R
		I_LETTER,	! 53 S
		I_LETTER,	! 54 T
		I_LETTER,	! 55 U
		I_LETTER,	! 56 V
		I_LETTER,	! 57 W
		I_LETTER,	! 58 X
		I_LETTER,	! 59 Y
		I_LETTER,	! 5A Z
		I_PSSWORD,	! 5B [
		I_PSSWORD,	! 5C \
		I_PSSWORD,	! 5D ]
		I_PSSWORD,	! 5E ^
		I_OTHER,	! 5F _
		I_PSSWORD,	! 60 `
		I_LETTER,	! 61 a
		I_LETTER,	! 62 b
		I_LETTER,	! 63 c
		I_LETTER,	! 64 d
		I_LETTER,	! 65 e
		I_LETTER,	! 66 f
		I_LETTER,	! 67 g
		I_LETTER,	! 68 h
		I_LETTER,	! 69 i
		I_LETTER,	! 6A j
		I_LETTER,	! 6B k
		I_LETTER,	! 6C l
		I_LETTER,	! 6D m
		I_LETTER,	! 6E n
		I_LETTER,	! 6F o
		I_LETTER,	! 70 p
		I_LETTER,	! 71 q
		I_LETTER,	! 72 r
		I_LETTER,	! 73 s
		I_LETTER,	! 74 t
		I_LETTER,	! 75 u
		I_LETTER,	! 76 v
		I_LETTER,	! 77 w
		I_LETTER,	! 78 x
		I_LETTER,	! 79 y
		I_LETTER,	! 7A z
		I_PSSWORD,	! 7B {
		I_PSSWORD,	! 7C |
		I_PSSWORD,	! 7D }
		I_PSSWORD,	! 7E ~
		I_ILLEGAL	! 7F DEL
%BLISS32 (	,REP 128 OF (I_ILLEGAL)))
	: VECTOR [%BLISS32 (,BYTE)];
! Table of keywords:

macro
%IF %BLISS (BLISS36) %THEN
zchar (foo, tok) = %ascii %string (%char (%charcount (foo)), foo,
    %char (tok)) %;
%ELSE
zchar (foo, tok) = %ascic foo, %char (tok) %;
%FI

    BIND
	RESERVED_TABLE = UPLIT (
	UPLIT %BLISS32 (BYTE) ( %STRING (		! A
		zchar (	'ALIGNED',	T_ALIGNED),
		zchar (	'ARE',		T_ARE),
		zchar (	'ARRAY',	T_ARRAY),
		zchar (	'AS',		T_AS),
		zchar ( 'ASCII_7',	T_ASCII_7),
		zchar ( 'ASCII_8',	T_ASCII_8),
		zchar ( 'ASCII_9',	T_ASCII_9),
		%char (0))),
	UPLIT %BLISS32 (BYTE) ( %STRING (		! B
		zchar (	'BASE',		T_BASE),
		zchar (	'BASIC',	T_BASIC),
		zchar (	'BIT',		T_BIT),
		zchar (	'BLANK',	T_BLANK),
		zchar (	'BOUNDARY',	T_BOUNDARY),
		zchar (	'BY',		T_BY),
		zchar (	'BYTE',		T_BYTE),
		zchar (	'BYTES',	T_BYTES),
		%char (0))),
	UPLIT %BLISS32 (BYTE) ( %STRING (		! C
		zchar (	'CHARACTER',	T_CHARACTER),
		zchar ( 'CHARACTERS',	T_CHARACTERS),
		zchar (	'COBOL',	T_COBOL),
		zchar (	'COLUMN_MAJOR',	T_COLUMN_MAJOR),
		zchar (	'COMPLEX',	T_COMPLEX),
		zchar (	'COMPUTED',	T_COMPUTED),
		zchar (	'CONDITION',	T_CONDITION),
		zchar (	'COPY',		T_COPY),
		%char (0))),
	UPLIT %BLISS32 (BYTE) ( %STRING (		! D
		zchar (	'DATATRIEVE',	T_DATATRIEVE),
		zchar (	'DATATYPE',	T_DATATYPE),
		zchar (	'DATE',		T_DATE),
		zchar (	'DECIMAL',	T_DECIMAL),
		zchar (	'DEFAULT_VALUE', T_DEFAULT_VALUE),
		zchar (	'DEFINE',	T_DEFINE),
		zchar (	'DEPENDING',	T_DEPENDING),
		zchar (	'DESCRIPTION',	T_DESCRIPTION),
		zchar (	'DIGIT',	T_DIGIT),
		zchar (	'DIGITS',	T_DIGITS),
		zchar ( 'DTR',		T_DTR),
		zchar (	'D_FLOATING',	T_D_FLOATING),
		zchar (	'D_FLOATING_COMPLEX', T_D_FLOATING_COMPLEX),
		%char (0))),
	UPLIT %BLISS32 (BYTE) ( %STRING (		! E
		zchar ( 'EBCDIC_8',	T_EBCDIC_8),
		zchar ( 'EBCDIC_9',	T_EBCDIC_9),
		zchar (	'EDIT_STRING',	T_EDIT_STRING),
		zchar (	'END',		T_END),
		%char (0))),
	UPLIT %BLISS32 (BYTE) ( %STRING (		! F
		zchar (	'FIELD',	T_FIELD),
		zchar ( 'FILLER',	T_FILLER),
		zchar (	'FLOATING',	T_FLOATING),
		zchar (	'FLOATING_COMPLEX', T_FLOATING_COMPLEX),
		zchar (	'FOR',		T_FOR),
		zchar (	'FRACTION',	T_FRACTION),
		zchar (	'FRACTIONS',	T_FRACTIONS),
		zchar (	'FROM',		T_FROM),
		zchar (	'F_FLOATING',	T_F_FLOATING),
		zchar (	'F_FLOATING_COMPLEX', T_F_FLOATING_COMPLEX),
		%char (0))),
	UPLIT %BLISS32 (BYTE) ( %STRING (		! G
		zchar (	'G_FLOATING',	T_G_FLOATING),
		zchar (	'G_FLOATING_COMPLEX', T_G_FLOATING_COMPLEX),
		zchar (	'GROUP',	T_GROUP),
		%char (0))),
	UPLIT  %BLISS32 (BYTE) ( %STRING (		! H
		zchar (	'H_FLOATING',	T_H_FLOATING),
		zchar (	'H_FLOATING_COMPLEX', T_H_FLOATING_COMPLEX),
		%char (0))),
	UPLIT  %BLISS32 (BYTE) ( %STRING (		! I
		zchar (	'IF',		T_IF),
		zchar (	'INDEXED',	T_INDEXED),
		zchar (	'INITIAL_VALUE', T_INITIAL_VALUE),
		zchar (	'IS',		T_IS),
		%char (0))),
	UPLIT  %BLISS32 (BYTE) ( %STRING (		! J
		zchar (	'JUSTIFIED',	T_JUSTIFIED),
		%char (0))),
	UPLIT %BLISS32 (BYTE) (0),			! No K
	UPLIT %BLISS32 (BYTE) ( %STRING (		! L
		zchar (	'LEFT',		T_LEFT),
		zchar (	'LONGWORD',	T_LONGWORD),
		%char (0))),
	UPLIT %BLISS32 (BYTE) ( %STRING (		! M
		zchar ( 'MATCHING',	T_MATCHING),
		zchar (	'MISSING_VALUE', T_MISSING_VALUE),
		zchar ( 'MOVE',		T_MOVE),
		%char (0))),
	UPLIT %BLISS32 (BYTE) ( %STRING (		! N
		zchar (	'NAME',		T_NAME),
		zchar (	'NUMERIC',	T_NUMERIC),
		%char (0))),
	UPLIT %BLISS32 (BYTE) ( %STRING (		! O
		zchar (	'OCCURS',	T_OCCURS),
		zchar (	'OCTAWORD',	T_OCTAWORD),
		zchar (	'OF',		T_OF),
		zchar (	'ON',		T_ON),
		zchar ( 'OTHERS',	T_OTHERS),
		zchar (	'OVERPUNCHED',	T_OVERPUNCHED),
		%char (0))),
	UPLIT %BLISS32 (BYTE) ( %STRING (		! P
		zchar (	'PACKED',	T_PACKED),
		zchar (	'PICTURE',	T_PICTURE),
		zchar (	'PLI',		T_PLI),
		zchar (	'POINTER',	T_POINTER),
		%char (0))),
	UPLIT %BLISS32 (BYTE) ( %STRING (		! Q
		zchar (	'QUADWORD',	T_QUADWORD),
		zchar (	'QUERY_HEADER',	T_QUERY_HEADER),
		zchar (	'QUERY_NAME',	T_QUERY_NAME),
		%char (0))),
	UPLIT %BLISS32 (BYTE) ( %STRING (		! R
		zchar (	'RECORD',	T_RECORD),
		zchar (	'RIGHT',	T_RIGHT),
		zchar ( 'ROUNDED',	T_ROUNDED),
		zchar (	'ROW_MAJOR',	T_ROW_MAJOR),
		%char (0))),
	UPLIT %BLISS32 (BYTE) ( %STRING (		! S
		zchar (	'SCALE',	T_SCALE),
		zchar (	'SEPARATE',	T_SEPARATE),
		zchar (	'SIGNED',	T_SIGNED),
		zchar ( 'SIXBIT',	T_SIXBIT),
		zchar (	'SIZE',		T_SIZE),
		zchar (	'STRING',	T_STRING),
		zchar (	'STRUCTURE',	T_STRUCTURE),
		zchar ( 'SYNC',		T_SYNC),
		zchar ( 'SYNCHRONIZED',	T_SYNCHRONIZED),
		%char (0))),
	UPLIT %BLISS32 (BYTE) ( %STRING (		! T
		zchar (	'TEXT',		T_TEXT),
		zchar (	'THRU',		T_THRU),
		zchar (	'TIME',		T_TIME),
		zchar (	'TIMES',	T_TIMES),
		zchar (	'TO',		T_TO),
		zchar ( 'TRANSFORM',	T_TRANSFORM),
		zchar ( 'TRUNCATED',	T_TRUNCATED),
		zchar (	'TYPE',		T_TYPE),
		%char (0))),
	UPLIT %BLISS32 (BYTE) ( %STRING (		! U
		zchar (	'UNSIGNED',	T_UNSIGNED),
		zchar (	'UNSPECIFIED',	T_UNSPECIFIED),
		%char (0))),
	UPLIT %BLISS32 (BYTE) ( %STRING (		! V
		zchar (	'VALID',	T_VALID),
		zchar (	'VALUE',	T_VALUE),
		zchar (	'VALUES',	T_VALUES),
		zchar (	'VARIANT',	T_VARIANT),
		zchar ( 'VARIANTS',	T_VARIANTS),
		zchar (	'VARYING',	T_VARYING),
		zchar (	'VIRTUAL',	T_VIRTUAL),
		%char (0))),
	UPLIT %BLISS32 (BYTE) ( %STRING (		! W
		zchar (	'WHEN',		T_WHEN),
		zchar (	'WORD',		T_WORD),
		%char (0))),
	UPLIT %BLISS32 (BYTE) (0),			! No X
	UPLIT %BLISS32 (BYTE) (0),			! No Y
	UPLIT %BLISS32 (BYTE) ( %STRING (		! Z
		zchar (	'ZERO',		T_ZERO),
		zchar (	'ZONED',	T_ZONED),
		%char (0))) )
	: VECTOR;
! Lower case to upper case and dash to underscore

    BIND
	    UPCASE = ch$transtable (
	    %c' ', %c' ', %c' ', %c' ',	! NUL, SOH, STX, ETX
	    %c' ', %c' ', %c' ', %c' ',	! EOT, ENQ, ACK, BEL
	    %c' ', %c' ', %c' ', %c' ',	!  BS,  HT,  LF,  VT
	    %c' ', %c' ', %c' ', %c' ',	!  FF,  CR,  SO,  SI
	    %c' ', %c' ', %c' ', %c' ',	! DLE, DC1, DC2, DC3
	    %c' ', %c' ', %c' ', %c' ',	! DC4, NAK, SYN, ETB
	    %c' ', %c' ', %c' ', %c' ',	! CAN,  EM, SUB, ESC
	    %c' ', %c' ', %c' ', %c' ',	!  FS,  GS,  RS,  US
	    %c' ', %c' ', %c' ', %c' ',	!  SP,   !,   ",   #
	    %c'$', %c' ', %c' ', %c' ',	!   $,   %,   &,   '
	    %c' ', %c' ', %c' ', %c' ',	!   (,   ),   *,   +
	    %c' ', %c'_', %c'.', %c' ',	!   ,,   -,   .,   /
	    %c'0', %c'1', %c'2', %c'3',	!   0,   1,   2,   3
	    %c'4', %c'5', %c'6', %c'7',	!   4,   5,   6,   7
	    %c'8', %c'9', %c' ', %c';',	!   8,   9,   :,   ;
	    %c' ', %c' ', %c' ', %c' ',	!   <,   =,   >,   ?
	    %c' ', %c'A', %c'B', %c'C',	!   @,   A,   B,   C
	    %c'D', %c'E', %c'F', %c'G',	!   D,   E,   F,   G
	    %c'H', %c'I', %c'J', %c'K',	!   H,   I,   J,   K
	    %c'L', %c'M', %c'N', %c'O',	!   L,   M,   N,   O
	    %c'P', %c'Q', %c'R', %c'S',	!   P,   Q,   R,   S
	    %c'T', %c'U', %c'V', %c'W',	!   T,   U,   V,   W
	    %c'X', %c'Y', %c'Z', %c' ',	!   X,   Y,   Z,   [
	    %c' ', %c' ', %c' ', %c'_',	!   \,   ],   ^,   _
	    %c' ', %c'A', %c'B', %c'C',	!   `,   a,   b,   c
	    %c'D', %c'E', %c'F', %c'G',	!   d,   e,   f,   g
	    %c'H', %c'I', %c'J', %c'K',	!   h,   i,   j,   k
	    %c'L', %c'M', %c'N', %c'O',	!   l,   m,   n,   o
	    %c'P', %c'Q', %c'R', %c'S',	!   p,   q,   r,   s
	    %c'T', %c'U', %c'V', %c'W',	!   t,   u,   v,   w
	    %c'X', %c'Y', %c'Z', %c' ',	!   x,   y,   z,   {
	    %c' ', %c' ', %c' ', %c' ');!   |,   },   ~, DEL
    LITERAL
	MINUS		= %C'-';

    LOCAL
	TEXT :		vector [ch$allocation (BUF_SIZE)],
	STATUS,
	PTR,
	LENGTH;

! Scan characters up to the end of the string of letters and digits.
! These characters form a CDD_name (variable name), a keyword, or part
! of a path_name.

    WHILE (NOT .END_OF_INPUT_LINE) AND
	(.IDENT [ch$rchar (.CHAR)] EQLU I_LETTER
	OR .IDENT [ch$rchar (.CHAR)] EQLU I_OTHER	!??
	OR .IDENT [ch$rchar (.CHAR)] EQLU I_DIGIT)
    DO GET_CHAR (CHAR, false);

    STATUS = FALSE;
! Check to see if the string of letters and digits is a path_name.
! If it is, the string is not at the end of an input line and is followed
! by a left parenthesis, a semicolon, or a dot followed by a letter (start
! of next piece of path_name) or a minus sign.

    IF NOT .END_OF_INPUT_LINE AND
	(.IDENT [ch$rchar (.CHAR)] EQLU I_L_PAREN
	OR .IDENT [ch$rchar (.CHAR)] EQLU I_SEMICOLON OR
	(.IDENT [ch$rchar (.CHAR)] EQLU I_DOT
	    AND (.IDENT [ch$rchar (ch$plus (.CHAR, 1))] EQLU I_LETTER OR
		CH$RCHAR (ch$plus (.CHAR, 1)) EQLU MINUS)))
    THEN		! We have a PATH_NAME
	BEGIN
	    TOKEN_PTR [TKN_TERM] = T_PATH_NAME;
	    PTR = ch$ptr (TEXT);
	    WHILE TRUE DO
		BEGIN		! Loop on pieces of path_name

		    ! Compute length of piece of path_name, convert it to
		    ! upper case, and store it.

		    LENGTH = ch$diff (.CHAR, .LEX_TKNSTRT);
		    CH$TRANSLATE (UPCASE, .LENGTH, .LEX_TKNSTRT, 0, .LENGTH,
			.PTR);
		    PTR = ch$plus (.PTR, .LENGTH);

		    ! Path_name followed by a semicolon, so eat the
		    ! version number which follows.

		    IF .IDENT [ch$rchar (.CHAR)] EQLU I_SEMICOLON THEN
			BEGIN
			    PTR = CH$MOVE (1, .CHAR, .PTR);
			    GET_CHAR (CHAR, false);
			    IF (NOT .END_OF_INPUT_LINE) AND
				(CH$RCHAR (.CHAR) EQLU MINUS) THEN
				BEGIN
				    PTR = CH$MOVE (1, .CHAR, .PTR);
				    GET_CHAR (CHAR, false);
				END;
			    WHILE (NOT .END_OF_INPUT_LINE) AND
				(.IDENT [ch$rchar (.CHAR)] EQLU I_DIGIT) DO
				BEGIN
				    PTR = CH$MOVE (1, .CHAR, .PTR);
				    GET_CHAR (CHAR, false);
				END;
			END;

		    ! Path_name piece followed by a left parenthesis.
		    ! Eat the characters within the parentheses.

		    IF .IDENT [ch$rchar (.CHAR)] EQLU I_L_PAREN THEN
			BEGIN
			    PTR = CH$MOVE (1, .CHAR, .PTR);
			    DO
				BEGIN
				    GET_CHAR (CHAR, false);
				    PTR = CH$MOVE (1, .CHAR, .PTR);
				END
			    WHILE NOT .END_OF_INPUT_LINE AND
				.IDENT [ch$rchar (.CHAR)] NEQU I_ILLEGAL AND
				.IDENT [ch$rchar (.CHAR)] NEQU I_L_PAREN AND
				.IDENT [ch$rchar (.CHAR)] NEQU I_R_PAREN AND
				.IDENT [ch$rchar (.CHAR)] NEQU I_DOT;

			    IF .IDENT [ch$rchar (.CHAR)] NEQU I_R_PAREN
				OR .END_OF_INPUT_LINE
				THEN EXITLOOP;
			    GET_CHAR (CHAR, false);
			END;

		    ! End of path_name: next character is not a dot, or
		    ! reached the end of the input line, or not at the end of
		    ! the input line and next character is a dot but following
		    ! character is neither a letter nor a minus sign.
		    ! Store the path name and exit the loop to collect
		    ! a path_name.

		    IF .IDENT [ch$rchar (.CHAR)] NEQU I_DOT OR
			.END_OF_INPUT_LINE OR
			(NOT .END_OF_INPUT_LINE AND
			.IDENT [ch$rchar (ch$plus (.CHAR, 1))] NEQU I_LETTER
			and CH$RCHAR (ch$plus (.CHAR, 1)) NEQU MINUS)
		    THEN
			BEGIN
			    STATUS = TRUE;
			    ENTER_TEXT (ch$diff (.PTR, ch$ptr (TEXT)),
				ch$ptr (TEXT), TOKEN_PTR [TKN_TEXT]);
			    EXITLOOP;
			END;

		    ! Here if we found a dot which is not at the end of the
		    ! input line and which is followed by a letter or a minus
		    ! sign.  There are more path_name pieces to come, so we
		    ! eat the dot and move the start of the path_name piece
		    ! up to the next character, and then eat up a string of
		    ! letters and digits.

		    PTR = CH$MOVE (1, .CHAR, .PTR);
		    LEX_TKNSTRT = ch$plus (.CHAR, 1);

		    IF NOT .END_OF_INPUT_LINE
			THEN DO  GET_CHAR (CHAR, false)
			    WHILE (NOT .END_OF_INPUT_LINE) AND
			    (.IDENT [ch$rchar (.CHAR)] EQLU I_LETTER
			    OR .IDENT [ch$rchar (.CHAR)] EQLU I_OTHER	!??
			    OR .IDENT [ch$rchar (.CHAR)] EQLU I_DIGIT)
		END;	! Loop on pieces of path_name
	END		! End of pathname
! These characters do not form a path_name, so they are either a CDD_name
! (that is, a variable) or a keyword.  We will convert the string to upper
! case, store it, and look for it in the keyword table (RESERVED_TABLE).
! The keyword table has an entry for each initial letter, and each entry
! ends with a zero character.  Within each alphabetic entry, the entry for
! a particular keyword has the legnth of the keyword, the characters which
! make up the keyword, and a character whose numerical value is the token
! number of the keyword.  This does constrain the token numbers available
! for keywords, especially on 10/20, which is why they are declared in the
! grammar with lower values than the symbols and other tokens; if this
! becomes a problem, rewrite the ZCHAR macro, which is defined just before
! the keyword table.

    ELSE		! We have a CDD_NAME or keyword
	WHILE TRUE DO
	    BEGIN
		LENGTH = ch$diff (.CHAR, .LEX_TKNSTRT);
		CH$TRANSLATE(UPCASE, .LENGTH, .LEX_TKNSTRT, 0, .LENGTH,
		    ch$ptr (TEXT));
		ENTER_TEXT (.LENGTH, ch$ptr (TEXT), TOKEN_PTR [TKN_TEXT]);
		IF .IDENT [ch$rchar (ch$ptr (TEXT))] NEQU I_LETTER THEN PTR = 0
		    ELSE PTR =
			ch$ptr(.RESERVED_TABLE[ch$rchar(ch$ptr(TEXT)) - %c'A']);
		IF ch$rchar (.PTR) NEQU 0 THEN	! Check for keyword
		    WHILE ch$rchar (.PTR) NEQU 0 DO
			BEGIN	! Look through alphabetic entry
			    IF ch$rchar (.PTR) EQLU .LENGTH THEN
				IF CH$EQL (ch$rchar (.PTR), ch$plus (.PTR, 1),
				    ch$rchar (.PTR), ch$ptr (TEXT))
				    THEN
					BEGIN	! Found it!
					    TOKEN_PTR [TKN_TERM] =
						ch$rchar (ch$plus (.PTR,
						ch$rchar (.PTR) + 1));
					    EXITLOOP;
					END;
			    PTR = ch$plus (.PTR, ch$rchar (.PTR) + 2);
			END;	! Looking through alphabetic entry

		STATUS = TRUE;
		IF .TOKEN_PTR [TKN_TERM] EQLU T_EOF THEN	! Left by LEX
		    TOKEN_PTR [TKN_TERM] = T_CDD_NAME;
		EXITLOOP;
	    END;
    RETURN .STATUS;
END;
ROUTINE DDU$$P_LEX_NUMBER (TOKEN_TYPE, CHAR_PTR) =
!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine lexically parses a numeric token.
!
!  INPUT PARAMETERS:
!
!	token_type	address of a word to be set to the type of
!			numeric token parsed.
!	char_ptr	pointer to current character position.
!
!  IMPLICIT INPUTS:
!
!	END_OF_INPUT_LINE	TRUE if current character is at end of line
!
!  IMPLICIT OUTPUTS:
!
!	END_OF_INPUT_LINE	Flag for last character in input line
!
!  COMPLETION STATUS:
!
!	TRUE if successful, FALSE otherwise.
!
!  SIGNALLED STATUS:
!
!	This routine does not intercept signals sent by routines it calls.
!
!  SIDE EFFECTS:
!
!	May cause input lines to be read.
!
!--
BEGIN
    BIND
	TKN_TYPE	= .TOKEN_TYPE	%BLISS32 (: LONG),
	CHAR		= .CHAR_PTR;

    LITERAL
	NINE		= %C'9',
	ZERO		= %C'0',
	E		= %C'E',
	PLUS		= %C'+',
	MINUS		= %C'-';

    LOCAL
	STATUS		%BLISS32 (: LONG),
	HAS_SIGN	%BLISS32 (: LONG);

    STATUS = TRUE;
! Determine if number is signed or unsigned.

    IF CH$RCHAR (.CHAR) EQLU MINUS OR CH$RCHAR (.CHAR) EQLU PLUS THEN
	BEGIN
	    HAS_SIGN = TRUE;
	    GET_CHAR (CHAR, false);
	END
    ELSE HAS_SIGN = FALSE;

    IF CH$RCHAR (.CHAR) LSSU ZERO OR CH$RCHAR (.CHAR) GTRU NINE THEN
	STATUS = FALSE;
    WHILE CH$RCHAR (.CHAR) GEQU ZERO AND CH$RCHAR (.CHAR) LEQU NINE
	AND NOT .END_OF_INPUT_LINE DO
	GET_CHAR (CHAR, false);

! See if it is a signed or unsigned integer.

    IF .STATUS AND CH$RCHAR (.CHAR) NEQU E AND
	(NOT CH$EQL (1, .CHAR, 1, ch$ptr (UPLIT ('.')), %c' ') OR
	.END_OF_INPUT_LINE OR
	(CH$EQL (1, .CHAR, 1, ch$ptr (UPLIT('.')), %c' ') AND
	(CH$RCHAR (ch$plus (.CHAR, 1)) LSSU ZERO OR
	    CH$RCHAR (ch$plus (.CHAR, 1)) GTRU NINE)))
    THEN
	BEGIN	! It's an integer
	    IF .HAS_SIGN THEN TKN_TYPE = T_SIGNED_INTEGER
	    ELSE TKN_TYPE = T_UNSIGNED_INTEGER;
	    RETURN SS$_NORMAL;
	END;
! See if it is a fixed-point number.

    IF CH$EQL (1, .CHAR, 1, ch$ptr (UPLIT('.')), %c' ') AND .STATUS THEN
	BEGIN
	    GET_CHAR (CHAR, false);
	    IF CH$RCHAR (.CHAR) LSSU ZERO OR CH$RCHAR (.CHAR) GTRU NINE THEN
		STATUS = FALSE;
	    WHILE CH$RCHAR (.CHAR) GEQU ZERO AND CH$RCHAR (.CHAR) LEQU NINE
		AND NOT .END_OF_INPUT_LINE DO
		GET_CHAR (CHAR, false);

	    IF (CH$RCHAR (.CHAR) LSSU ZERO OR CH$RCHAR (.CHAR) GTRU NINE) AND
		CH$RCHAR (.CHAR) NEQU E AND .STATUS
	    THEN
		BEGIN	! It's a fixed point number.
		    TKN_TYPE = T_FIXED_POINT;
		    RETURN SS$_NORMAL;
		END;
	END;

! See if it is a floating-point number.

    IF CH$RCHAR (.CHAR) EQLU E AND .STATUS THEN
	BEGIN
	    GET_CHAR (CHAR, false);
	    IF CH$RCHAR (.CHAR) EQLU MINUS OR CH$RCHAR (.CHAR) EQLU PLUS
	    THEN GET_CHAR (CHAR, false);
	    IF CH$RCHAR (.CHAR) LSSU ZERO OR CH$RCHAR (.CHAR) GTRU NINE THEN
		STATUS = FALSE;
	    WHILE CH$RCHAR (.CHAR) GEQU ZERO AND CH$RCHAR (.CHAR) LEQU NINE DO
		GET_CHAR (CHAR, false);

	    IF .STATUS THEN
		BEGIN	! It's a floating point number.
		    TKN_TYPE = T_FLOATING_POINT;
		    RETURN SS$_NORMAL;
		END;
	END;

!   No valid number found.

    LSLOCAL_SYNTAX_ERRORM (.lex_locator, 'Illegal number.');
    RETURN .STATUS;
END;
ROUTINE DDU$$P_LEX_PERCENT (CHAR_PTR, TOKEN_PTR) =
!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine lexically parses a token beginning with a percent sign.
!
!  PARAMETERS:
!
!	char_ptr		pointer to current character position.
!	token_ptr		address of the current token pointer.
!
!  IMPLICIT INPUTS:
!
!	END_OF_INPUT_LINE	TRUE if current character is at end of line
!	LEX_TKNSTRT		Pointer to first character in token
!
!  IMPLICIT OUTPUTS:
!
!	LEX_TKNSTRT		Updated pointer to first character in token.
!
!  COMPLETION STATUS:
!
!	TRUE if OK, FALSE otherwise.
!
!  SIGNALLED STATUS:
!
!	This routine does not intercept signals sent by routines it calls.
!
!  SIDE EFFECTS:
!
!	May read more input lines.
!
!--
BEGIN
    MAP
	TOKEN_PTR 	: REF	TKN_STR;
	
    BIND
	CHAR		= .CHAR_PTR;

    LITERAL
	ZERO		= %C'0',
	SEVEN		= %C'7',
	NINE		= %C'9',
	A		= %C'A',
	F		= %C'F',
	O		= %C'O',
	X		= %C'X',
	SGL_QUOTE	= %C'''';

    LOCAL
	STATUS		%BLISS32 (: LONG);
    GET_CHAR (CHAR, false);
    STATUS = TRUE;
    IF CH$RCHAR (.CHAR) EQLU X THEN		! Hex number
	BEGIN
	    TOKEN_PTR [TKN_TERM] = T_HEX_NUMBER;
	    GET_CHAR (CHAR, false);
	    IF CH$RCHAR (.CHAR) EQLU SGL_QUOTE THEN
		DO GET_CHAR (CHAR, false)
		    WHILE ((CH$RCHAR (.CHAR) GEQU ZERO AND
			    CH$RCHAR (.CHAR) LEQU NINE) OR
			(CH$RCHAR (.CHAR) GEQU A AND CH$RCHAR (.CHAR) LEQU F))
			AND NOT .END_OF_INPUT_LINE;
        END

    ELSE IF CH$RCHAR (.CHAR) EQLU O THEN	! Octal number
	    BEGIN
		TOKEN_PTR [TKN_TERM] = T_OCTAL_NUMBER;
		GET_CHAR (CHAR, false);
		IF CH$RCHAR (.CHAR) EQLU SGL_QUOTE THEN
		    DO GET_CHAR (CHAR, false)
			WHILE (CH$RCHAR (.CHAR) GEQU ZERO AND
			    CH$RCHAR (.CHAR) LEQU SEVEN
			    AND NOT .END_OF_INPUT_LINE);
	    END
	ELSE STATUS = FALSE;
			
    IF NOT .STATUS OR CH$RCHAR (.CHAR) NEQU SGL_QUOTE
	THEN
	    BEGIN
		LSLOCAL_SYNTAX_ERRORM (.lex_locator,
		    'Illegal hexadecimal or octal number');
		RETURN FALSE;
	    END;

    LEX_TKNSTRT = ch$plus (.LEX_TKNSTRT, 3);
    ENTER_TEXT (ch$diff (.CHAR, .LEX_TKNSTRT),
	.LEX_TKNSTRT, TOKEN_PTR [TKN_TEXT]);
    RETURN .STATUS;
END;
ROUTINE ENTER_TEXT (LENGTH, TEXT, TEXT_PTR) : NOVALUE =
!++
! FUNCTIONAL DESCRIPTION:
!
!	This is a hacked up routine for storing text strings in
!	the heap.
!
!  INPUT PARAMETERS:
!
!	length		is the length of the text to be stored.
!	text		is a pointer to the text string to be stored.
!
!  OUTPUT PARAMETERS:
!
!	text_ptr	is the address of the text descriptor to be set to
!			point to the text string.
!
!  IMPLICIT INPUTS:
!
!	none
!
!  IMPLICIT OUTPUTS:
!
!	none
!
!  COMPLETION STATUS:
!
!	none
!
!  SIGNALLED STATUS:
!
!	This routine does not intercept signals sent by routines it calls.
!
!  SIDE EFFECTS:
!
!	The system may be called on to allocate more heap storage.
!
!--
BEGIN
    %IF %BLISS (BLISS32) %THEN
    MAP
	LENGTH : LONG;
	%FI

    BIND
	t_desc = utl_get (STR$K_D_BLN*%upval): $STR_DESCRIPTOR (class=dynamic);

    $STR_DESC_INIT (DESCRIPTOR = t_desc, class=dynamic);
    $STR_COPY (STRING=(.length, .text), TARGET=t_desc);
    .text_ptr = t_desc;
END;
ROUTINE GET_CHAR (CHAR_ADDR, SKIP_COMMENT) : NOVALUE =
!++
!  FUNCTIONAL DESCRIPTION:
!
!	This routine returns the next input character and sets the line and
!	column pointers.  It also skips over comments if requested.
!
!  INPUT PARAMETERS:
!
!	skip_comment	skip comments if TRUE
!
!  OUTPUT PARAMETERS:
!
!	char_addr	address of a pointer to be set to point to
!			the next input character.
!
!  IMPLICIT INPUTS:
!
!	file_iob	address of XPORT IOB for file being parsed.
!	LEX_LINENUM	Lexical line number
!	COLUMN		Column number
!	LEX_COLUMN	Lexical column number
!
!  IMPLICIT OUTPUTS:
!
!	START_LINE	TRUE if this is the first token on a line
!	LEX_LINENUM	Lexical line number
!	LEX_COLUMN	Lexical column number
!	END_OF_INPUT_LINE TRUE if character is last one on input line
!	COLUMN		Column number
!
!  COMPLETION STATUS:
!
!	none
!
!  SIGNALLED STATUS:
!
!	This routine does not intercept signals sent by routines it calls.
!
!  SIDE EFFECTS:
!
!	Reads input lines.
!
!--
BEGIN
    MAP
	SKIP_COMMENT %BLISS32 ( : LONG);

    BIND
	CHAR		= .CHAR_ADDR;

    LITERAL
	TAB		= %X'09',	! a tab character
	COMMENT_DELIM	= %C'!';	! the character '!'

    OWN
	TEMP_BUFF	: $STR_DESCRIPTOR (CLASS=DYNAMIC, STRING=(0,0)),
	EOL_COLUMN	: %BLISS32 (BYTE) INITIAL (%CHAR (04)),	! ^D character
	EOF_COLUMN	: %BLISS32 (BYTE) INITIAL (%CHAR (26)),	! ^Z character
	FORM_FEED	: %BLISS32 (BYTE) INITIAL (%CHAR (12));	! ^L character

    LOCAL
	FF_PTR,
	STATUS;
!   Get the next character.  If the line has been totally scanned, then get
!   the first character on the next line.  This will point past the last
!   character on an end of line.

    START_LINE = FALSE;

    IF (.LEX_LINENUM EQL 0) OR (.COLUMN EQL .temp_buff [STR$H_LENGTH])
	OR (.END_OF_INPUT_LINE)
    THEN
	BEGIN				! Get another line
	    STATUS = $XPO_GET (IOB = .file_iob);
	    $STR_COPY (STRING = $STR_CONCAT (
		(.file_iob [IOB$H_STRING], .file_iob [IOB$A_STRING]),
		(1, ch$ptr (EOL_COLUMN))),
		TARGET = TEMP_BUFF);
	    LEX_LINENUM = .LEX_LINENUM + 1;
	    START_LINE = TRUE;
	    IF .STATUS EQLU XPO$_END_FILE THEN
		BEGIN
		    LEX_COLUMN = 1;
		    CHAR = ch$ptr (EOF_COLUMN);
		    END_OF_INPUT_LINE = TRUE;
		    RETURN;
		END;
	    IF NOT .STATUS THEN SIGNAL (.STATUS);

	    FF_PTR = CH$FIND_CH (.TEMP_BUFF [STR$H_LENGTH],
		.TEMP_BUFF [STR$A_POINTER], .FORM_FEED);
	    WHILE NOT CH$FAIL (.FF_PTR) DO
		BEGIN
		    CH$MOVE (1, ch$ptr (UPLIT %BLISS32 (BYTE) (' ')), .FF_PTR);
		    FF_PTR = CH$FIND_CH (
			(.TEMP_BUFF [STR$H_LENGTH] -
			ch$diff (.FF_PTR,
			   ch$plus (.TEMP_BUFF [STR$A_POINTER], 1))),
			ch$plus (.FF_PTR, 1), .FORM_FEED);
		END;
	    LEX_COLUMN = 1;
	    COLUMN = 1;
	END				! Get another line
    ELSE
	BEGIN				! Get character from current line
	    COLUMN = .COLUMN + 1;
	    IF (.COLUMN GTR 1) AND
		(CH$RCHAR (ch$plus (.temp_buff [STR$A_POINTER], .COLUMN - 2))
		EQLU TAB)
	    THEN	! next tab position
		LEX_COLUMN =
		    ((.LEX_COLUMN + (COLUMNS_PER_TAB - 1)
		    / COLUMNS_PER_TAB) * COLUMNS_PER_TAB) + 1
	    ELSE
		LEX_COLUMN = .LEX_COLUMN + 1;
	END;				! Get character from current line
!   Skip over comments if the SKIP_COMMENT flag has been set.  Treat the
!   comment as if it were an end of line.

    IF .SKIP_COMMENT THEN
	IF (.COLUMN LEQU .temp_buff [STR$H_LENGTH]) AND
	    CH$RCHAR (ch$plus (.temp_buff [STR$A_POINTER], .COLUMN - 1))
	    EQLU COMMENT_DELIM
	THEN
	    BEGIN
		COLUMN = .temp_buff [STR$H_LENGTH];
		LEX_COLUMN = .temp_buff [STR$H_LENGTH];
		END_OF_INPUT_LINE = TRUE;
	    END;

!   Set the character pointer to the current character.

    CHAR = ch$plus (.temp_buff [STR$A_POINTER], .COLUMN - 1);

!   Notice if we just read the last character on a line.

    IF .COLUMN EQL .temp_buff [STR$H_LENGTH]
	THEN END_OF_INPUT_LINE = TRUE
	ELSE END_OF_INPUT_LINE = FALSE;
END;
ROUTINE LEX (TOKEN_PTR) : NOVALUE =
!++
!  FUNCTIONAL DESCRIPTION:
!
!	This routine returns the next input token.
!
!  INPUT PARAMETERS:
!
!	None
!
!  OUTPUT PARAMETERS:
!
!	token_ptr	Pointer to a token data structure where the new token
!			information will be returned.
!
!  IMPLICIT INPUTS:
!
!	START_LINE	TRUE if token is the first token on a line
!	LEX_LOCATOR	Text source location for new lexeme
!	LEX_COLUMN	Source column
!	LEX_LINENUM	Source line number
!
!  IMPLICIT OUTPUTS:
!
!	LEX_COLUMN	Source column
!	LEX_LINENUM	Lexical line number, reset to zero when an end-of-file
!			token is produced -- see code in GET_CHAR, above.
!	LEX_TKNSTRT	Pointer to start of lexeme
!
!  COMPLETION STATUS:
!
!	none
!
!  SIGNALLED STATUS:
!
!	This routine does not intercept signals sent by routines it calls.
!
!  SIDE EFFECTS:
!
!	Reads input file.
!
!--
BEGIN

    MAP
	TOKEN_PTR	: REF	TKN_STR;

! Classes of characters which might appear in the input:

    LITERAL
	C_IGNORABLE	=  0,
	C_DIGIT		=  1,
	C_LETTER	=  2,
	C_SLASH		=  3,
	C_QUOTE		=  4,
	C_PERCENT	=  5,
	C_STAR		=  6,
	C_DOT		=  7,
	C_COLON		=  8,
	C_SEMICOLON     =  9,
	C_L_PAREN	= 10,
	C_R_PAREN	= 11,
	C_MINUS		= 12,
	C_OTHERS	= 13,
	C_EOF		= 14;
! Class of each possible ASCII input character:

    BIND
	CLASS = UPLIT %BLISS32 (BYTE) (
		C_IGNORABLE,	! 00 NUL
		C_OTHERS,	! 01 SOH
		C_OTHERS,	! 02 STX
		C_OTHERS,	! 03 ETX
		C_IGNORABLE, 	! 04 EOT
		C_OTHERS,	! 05 ENQ
		C_OTHERS,	! 06 ACK
		C_OTHERS,	! 07 BEL
		C_OTHERS,	! 08 BS
		C_IGNORABLE,	! 09 HT
		C_IGNORABLE,	! 0A LF
		C_IGNORABLE,	! 0B VT
		C_IGNORABLE,	! 0C FF
		C_IGNORABLE,	! 0D CR
		C_OTHERS,	! 0E SO
		C_OTHERS,	! 0F SI
		C_OTHERS,	! 10 DLE
		C_OTHERS,	! 11 DC1
		C_OTHERS,	! 12 DC2
		C_OTHERS,	! 13 DC3
		C_OTHERS,	! 14 DC4
		C_OTHERS,	! 15 NAK
		C_OTHERS,	! 16 SYN
		C_OTHERS,	! 17 ETB
		C_OTHERS,	! 18 CAN
		C_OTHERS,	! 19 EM
		C_EOF,		! 1A SUB
		C_OTHERS,	! 1B ESC
		C_OTHERS,	! 1C FS
		C_OTHERS,	! 1D GS
		C_OTHERS,	! 1E RS
		C_OTHERS,	! 1F US
		C_IGNORABLE,	! 20 SP
		C_OTHERS,	! 21 !
		C_QUOTE,	! 22 "
		C_OTHERS,	! 23 #
		C_LETTER,	! 24 $
		C_PERCENT,	! 25 %
		C_OTHERS,	! 26 &
		C_QUOTE,	! 27 '
		C_L_PAREN,	! 28 (
		C_R_PAREN,	! 29 )
		C_STAR,		! 2A *
		C_DIGIT,	! 2B +
		C_IGNORABLE, 	! 2C ,
		C_MINUS,	! 2D -
		C_DOT,		! 2E .
		C_SLASH,	! 2F /
		C_DIGIT,	! 30 0
		C_DIGIT,	! 31 1
		C_DIGIT,	! 32 2
		C_DIGIT,	! 33 3
		C_DIGIT,	! 34 4
		C_DIGIT,	! 35 5
		C_DIGIT,	! 36 6
		C_DIGIT,	! 37 7
		C_DIGIT,	! 38 8
		C_DIGIT,	! 39 9
		C_COLON,	! 3A :
		C_SEMICOLON,	! 3B ;
		C_OTHERS,	! 3C <
		C_OTHERS,	! 3D =
		C_OTHERS,	! 3E >
		C_OTHERS,	! 3F ?
		C_OTHERS,	! 40 @
		C_LETTER,	! 41 A
		C_LETTER,	! 42 B
		C_LETTER,	! 43 C
		C_LETTER,	! 44 D
		C_LETTER,	! 45 E
		C_LETTER,	! 46 F
		C_LETTER,	! 47 G
		C_LETTER,	! 48 H
		C_LETTER,	! 49 I
		C_LETTER,	! 4A J
		C_LETTER,	! 4B K
		C_LETTER,	! 4C L
		C_LETTER,	! 4D M
		C_LETTER,	! 4E N
		C_LETTER,	! 4F O
		C_LETTER,	! 50 P
		C_LETTER,	! 51 Q
		C_LETTER,	! 52 R
		C_LETTER,	! 53 S
		C_LETTER,	! 54 T
		C_LETTER,	! 55 U
		C_LETTER,	! 56 V
		C_LETTER,	! 57 W
		C_LETTER,	! 58 X
		C_LETTER,	! 59 Y
		C_LETTER,	! 5A Z
		C_OTHERS,	! 5B [
		C_OTHERS,	! 5C \
		C_OTHERS,	! 5D ]
		C_OTHERS,	! 5E ^
		C_LETTER,	! 5F _
		C_OTHERS,	! 60 `
		C_LETTER,	! 61 a
		C_LETTER,	! 62 b
		C_LETTER,	! 63 c
		C_LETTER,	! 64 d
		C_LETTER,	! 65 e
		C_LETTER,	! 66 f
		C_LETTER,	! 67 g
		C_LETTER,	! 68 h
		C_LETTER,	! 69 i
		C_LETTER,	! 6A j
		C_LETTER,	! 6B k
		C_LETTER,	! 6C l
		C_LETTER,	! 6D m
		C_LETTER,	! 6E n
		C_LETTER,	! 6F o
		C_LETTER,	! 70 p
		C_LETTER,	! 71 q
		C_LETTER,	! 72 r
		C_LETTER,	! 73 s
		C_LETTER,	! 74 t
		C_LETTER,	! 75 u
		C_LETTER,	! 76 v
		C_LETTER,	! 77 w
		C_LETTER,	! 78 x
		C_LETTER,	! 79 y
		C_LETTER,	! 7A z
		C_OTHERS,	! 7B {
		C_OTHERS,	! 7C |
		C_OTHERS,	! 7D }
		C_OTHERS,	! 7E ~
		C_OTHERS	! 7F DEL
%BLISS32 (	,REP 128 OF (C_OTHERS)))
	: VECTOR [%BLISS32 (,BYTE)];
    LITERAL
	BUF_SIZE	= 256,
	END_OF_FILE	= %X'1A',
	END_OF_LINE	= %X'04',
	COMMENT		= %C'!';

    OWN
	WITHIN_DESC	: %BLISS32 (LONG)	INITIAL (FALSE),
	LOOK_AHEAD	: %BLISS32 (LONG)	INITIAL (FALSE),
	CHAR;		! Pointer to current character

    LOCAL
	PAST_CHAR,	! Pointer to previous character
	TEXT		: vector [ch$allocation (BUF_SIZE)],
	STATUS %BLISS32 (: LONG);
! Initialize the new token.

    TOKEN_PTR [TKN_TERM] = T_EOF;
    TOKEN_PTR [TKN_INTVALUE] = 0;
    TOKEN_PTR [TKN_START_LINE] = FALSE;
    IF .CHAR NEQU 0 THEN
	IF CH$RCHAR (.CHAR) EQLU END_OF_FILE THEN
	    BEGIN
		TOKEN_PTR [TKN_LOCATOR] = .LEX_LOCATOR;
		CHAR = 0;	! Prepare to read next file by clearing these
		LEX_LINENUM = 0;! -- see code in GET_CHAR, above
		RETURN;
	    END;

    WHILE TRUE DO		! Large loop to find next lexeme
	BEGIN			! Exit this loop when a lexeme is found
	    IF .CHAR EQLU 0 THEN
		GET_CHAR (CHAR, true)
	    ELSE
		BEGIN
		    IF .LOOK_AHEAD AND CH$RCHAR (.CHAR) EQLU COMMENT THEN
			BEGIN
			    LEX_COLUMN = .LEX_COLUMN - 1;
			    COLUMN = .COLUMN - 1;
			    LOOK_AHEAD = FALSE;
			END;
		    IF CH$RCHAR (.CHAR) NEQU END_OF_FILE AND NOT .LOOK_AHEAD
		    THEN
			IF .WITHIN_DESC THEN GET_CHAR (CHAR, false)
			ELSE GET_CHAR (CHAR, true);
		END;

	    LOOK_AHEAD = FALSE;
	    IF .START_LINE THEN TOKEN_PTR [TKN_START_LINE] = TRUE;
	    LEX_LOCATOR [LOC_LINENUM] = .LEX_LINENUM; ! Set the line and column
	    LEX_LOCATOR [LOC_COLUMN] = .LEX_COLUMN;   ! locations of the token.
	    LEX_TKNSTRT = .CHAR;	! Save location of 1st token character.

	    IF CH$RCHAR (.CHAR) EQLU END_OF_FILE AND NOT .WITHIN_DESC THEN
		BEGIN
		    TOKEN_PTR [TKN_TERM] = T_EOF;
		    TOKEN_PTR [TKN_LOCATOR] = .LEX_LOCATOR;
		    ENTER_TEXT (5, ch$ptr (UPLIT %BLISS32 (BYTE) ('<EOF>')),
			TOKEN_PTR [TKN_TEXT]);
		    CHAR = 0;		! Prepare to read next file by clearing
		    LEX_LINENUM = 0;	! these -- see code in GET_CHAR, above
		    RETURN;
		END;
! Code to collect DESCRIPTIONS.
! These are special lexemes.  Each line is made into a separate T_DESCR_TEXT
! token.  The description text ends with a '*/' or at end-of-file.

	    IF .WITHIN_DESC THEN	! Inside DESCRIPTION:
		BEGIN
		    PAST_CHAR = .LEX_TKNSTRT;
		    TOKEN_PTR [TKN_TERM] = T_DESCR_TEXT;
		    WHILE TRUE DO
			BEGIN
			    IF .END_OF_INPUT_LINE OR
				(CH$RCHAR (.CHAR) EQLU END_OF_LINE)
				THEN EXITLOOP;
			    IF .CLASS [ch$rchar (.CHAR)] EQLU C_STAR AND
				.CLASS [ch$rchar(ch$plus(.CHAR,1))] EQLU C_SLASH
			    THEN
				BEGIN
				    WITHIN_DESC = FALSE;
				    GET_CHAR (CHAR, false);
				    EXITLOOP;
				END;
			    IF CH$RCHAR (.CHAR) EQLU END_OF_FILE THEN
				BEGIN
				    LSLOCAL_SYNTAX_ERRORM (.lex_locator,
					'DESCRIPTION not terminated.');
				    TOKEN_PTR [TKN_TERM] = T_EOF;
				    TOKEN_PTR [TKN_LOCATOR] = .LEX_LOCATOR;
				    ENTER_TEXT (5,
					ch$ptr(UPLIT %BLISS32 (BYTE) ('<EOF>')),
					TOKEN_PTR [TKN_TEXT]);
				    CHAR = 0;		! Prepare for next file;
				    LEX_LINENUM = 0;	! See code in GET_CHAR.
				    RETURN;
				END;
			    PAST_CHAR = .CHAR;
			    GET_CHAR (CHAR, false);
			END;
		    IF .PAST_CHAR EQLU .LEX_TKNSTRT THEN
			ENTER_TEXT (ch$diff (.PAST_CHAR, .LEX_TKNSTRT),
			    .LEX_TKNSTRT, TOKEN_PTR [TKN_TEXT])
		    ELSE
			ENTER_TEXT (ch$diff (.PAST_CHAR, .LEX_TKNSTRT) + 1,
			    .LEX_TKNSTRT, TOKEN_PTR [TKN_TEXT]);
		    EXITLOOP;
		END;		! End of DESCRIPTION code
! Machine to handle collecting normal lexemes.

	    CASE .CLASS [ch$rchar (.CHAR)] FROM C_IGNORABLE TO C_EOF OF
		SET

		[C_IGNORABLE]:		! Skip these characters
		    TRUE;

		[C_DIGIT]:		! Expect a number
		    BEGIN
			LOCAL
			    TOKEN_TYPE;

			LOOK_AHEAD = TRUE;
			STATUS = DDU$$P_LEX_NUMBER (TOKEN_TYPE, CHAR);
			IF .STATUS THEN
			    BEGIN
				TOKEN_PTR [TKN_TERM] = .TOKEN_TYPE;
				ENTER_TEXT (ch$diff (.CHAR, .LEX_TKNSTRT),
				    .LEX_TKNSTRT, TOKEN_PTR [TKN_TEXT]);
				IF .TOKEN_TYPE EQLU T_SIGNED_INTEGER OR
				    .TOKEN_TYPE EQLU T_UNSIGNED_INTEGER
				THEN
				    BEGIN
					status = $STR_BINARY (string =
					    .token_ptr [TKN_TEXT],
					    result =
					    token_ptr [TKN_INTVALUE]);
					IF NOT .STATUS THEN
					    LSLOCAL_SYNTAX_ERRORM (.lex_locator,
						'value overflow.');
					STATUS = SS$_NORMAL;
				    END;
				EXITLOOP;
			    END;
		    END;

		[C_LETTER]:	! Expect a path_name, keyword, or CDD_name
		    BEGIN
			STATUS = DDU$$P_LEX_NAME (.TOKEN_PTR, CHAR);
			LOOK_AHEAD = TRUE;
			IF .STATUS THEN EXITLOOP;
			LSLOCAL_SYNTAX_ERRORM (.lex_locator, 'bad identifier.');
		    END;

		[C_MINUS]:	! Expect a path_name or a number
		    BEGIN
			LOCAL
			    TOKEN_TYPE;

			LOOK_AHEAD = TRUE;
			IF (NOT .END_OF_INPUT_LINE) AND
			    (.CLASS [ch$rchar (ch$plus (.CHAR, 1))] EQLU C_DOT)
			    THEN
			    BEGIN	! A path_name
				STATUS = DDU$$P_LEX_NAME (.TOKEN_PTR, CHAR);
				IF NOT .STATUS THEN
				    LSLOCAL_SYNTAX_ERRORM (.lex_locator,
					'bad identifier.');
			    END		! A path_name
			ELSE
			    BEGIN	! A number
				STATUS = DDU$$P_LEX_NUMBER (TOKEN_TYPE, CHAR);
				IF .STATUS THEN
				    BEGIN
					TOKEN_PTR [TKN_TERM] = .TOKEN_TYPE;
					ENTER_TEXT (ch$diff (.CHAR,
					    .LEX_TKNSTRT),
					    .LEX_TKNSTRT, TOKEN_PTR [TKN_TEXT]);
					IF .TOKEN_TYPE EQLU T_SIGNED_INTEGER OR
					    .TOKEN_TYPE EQLU T_UNSIGNED_INTEGER
					THEN
					    BEGIN
						status = $str_binary (
						    string =
						    .token_ptr [TKN_TEXT],
						    result =
						    token_ptr [TKN_INTVALUE]);
						IF NOT .STATUS THEN
						    LSLOCAL_SYNTAX_ERRORM (
							.lex_locator,
							'value overflow.');
						STATUS = SS$_NORMAL;
					    END;
					EXITLOOP;
				    END;
			    END;		! A number
		    END;

		[C_SLASH]:		! Expect a description
		    BEGIN
			GET_CHAR (CHAR, false);
			IF (NOT .END_OF_INPUT_LINE) AND
			    (.CLASS [ch$rchar (.CHAR)] EQLU C_STAR) THEN
			    WITHIN_DESC = TRUE
			ELSE
			    BEGIN
				LSLOCAL_SYNTAX_ERRORM (.lex_locator,
				    'bad character.');
				LOOK_AHEAD = TRUE;
			    END;
		    END;

		[C_QUOTE]:		! Expect a quoted string
		    BEGIN
			LOCAL
			    PTR,
			    TEST;

			PTR = ch$ptr (TEXT);
			TEST = .CHAR;
			TOKEN_PTR [TKN_TERM] = T_QUOTED_STRING;

			GET_CHAR (CHAR, false);
			WHILE (CH$RCHAR (.CHAR) NEQU CH$RCHAR (.TEST) OR
			    CH$RCHAR (ch$plus (.CHAR, 1)) EQLU CH$RCHAR (.TEST))
			    AND CH$RCHAR (.CHAR) NEQU END_OF_LINE 
			    AND NOT .END_OF_INPUT_LINE
			DO
			    BEGIN
				IF CH$RCHAR (.CHAR) EQLU CH$RCHAR (.TEST) THEN
				    GET_CHAR (CHAR, false);

				PTR = CH$MOVE (1, .CHAR, .PTR);
				GET_CHAR (CHAR, false);
			    END;

			IF CH$RCHAR (.CHAR) EQLU CH$RCHAR (.TEST) THEN
			    BEGIN
				ENTER_TEXT (ch$diff (.PTR, ch$ptr (TEXT)),
				    ch$ptr (TEXT), TOKEN_PTR [TKN_TEXT]);
				EXITLOOP;
			    END
			ELSE
			    BEGIN
				LOOK_AHEAD = TRUE;
				LSLOCAL_SYNTAX_ERRORM (.lex_locator,
				    'illegal quoted string.');
			    END;
		    END;

		[C_PERCENT]:		! Expect a hex or octal number
		    BEGIN
			STATUS = DDU$$P_LEX_PERCENT (CHAR, .TOKEN_PTR);
			IF .STATUS THEN EXITLOOP
			ELSE LOOK_AHEAD = TRUE;
		    END;

		[C_STAR, C_DOT, C_COLON, C_SEMICOLON, C_L_PAREN, C_R_PAREN]:
		    BEGIN		! A single-character lexeme
			TOKEN_PTR [TKN_TERM] =
			    (SELECTONE .CLASS [ch$rchar (.CHAR)] OF
				SET
				    [C_STAR]:		T_STAR;
				    [C_DOT]:		T_DOT;
				    [C_COLON]:		T_COLON;
				    [C_SEMICOLON]:	T_SEMICOLON;
				    [C_L_PAREN]:	T_L_PAREN;
				    [C_R_PAREN]:	T_R_PAREN;
				TES);
			ENTER_TEXT (1, .LEX_TKNSTRT, TOKEN_PTR [TKN_TEXT]);
			EXITLOOP;
		    END;

		[C_OTHERS]:		! Illegal character
		    LSLOCAL_SYNTAX_ERRORM (.lex_locator, 'bad character.');

		[C_EOF]:		! End-of-file (^Z)
		    BEGIN
		    TOKEN_PTR [TKN_TERM] = T_EOF;
		    CHAR = 0;		! Prepare to be called again on the
		    LEX_LINENUM = 0;	!  next file - see code in GET_CHAR.
		    EXITLOOP;
		    END;

	    TES;
	END;		! End of large loop to find next lexeme

    TOKEN_PTR [TKN_LOCATOR] = .LEX_LOCATOR;

END;
GLOBAL ROUTINE PAR_GET_TOKEN =
!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine manages the token storage and gets the next lexical
!	token.
!
!  INPUT PARAMETERS:
!
!	None
!
!  OUTPUT PARAMETERS:
!
!	None
!
!  IMPLICIT INPUTS:
!
!	none
!
!  IMPLICIT OUTPUTS:
!
!	None
!
!  COMPLETION STATUS:
!
!	The address of the lexical token.
!
!  SIGNALLED STATUS:
!
!	This routine does not intercept signals sent by routines it calls.
!
!  SIDE EFFECTS:
!
!	None
!
!--
BEGIN

    OWN
	TOKEN_PTR	: REF	TKN_STR;

    TOKEN_PTR = TOKEN_STORAGE [.NEXT_TOKEN_INDEX, TKN_BASE];
    NEXT_TOKEN_INDEX = .NEXT_TOKEN_INDEX + 1;
    IF .NEXT_TOKEN_INDEX EQLU MAX_NUM_LEX_TOKS THEN
	NEXT_TOKEN_INDEX = 0;

    LEX (.TOKEN_PTR);
    RETURN .TOKEN_PTR
END;
ROUTINE UTL_GET (SIZE_REQUEST) =
!++
! FUNCTIONAL DESCRIPTION:
!
!	This is a hacked up heap allocation routine.
!
!  INPUT PARAMETERS:
!
!	size_request	is the number of bytes to be allocated.
!
!  OUTPUT PARAMETERS:
!
!	DDU-status	is the completion status of the operation.
!
!  IMPLICIT INPUTS:
!
!	none
!
!  IMPLICIT OUTPUTS:
!
!	none
!
!  COMPLETION STATUS:
!
!	The address of the allocated storage.
!
!  SIGNALLED STATUS:
!
!	This routine does not intercept signals sent by routines it calls.
!
!  SIDE EFFECTS:
!
!	VMS or XPORT may be called on to allocate more heap storage.
!
!--
BEGIN
    MAP
	SIZE_REQUEST	%BLISS32 (: LONG);

    OWN
	ANCHOR : VECTOR [2] INITIAL (0, 0);

    LITERAL
	INITIAL_HEAP_SIZE = 1^14;		! In bytes

    LOCAL
	CURRENT : REF VECTOR,		! address of the current block from which storage is allocated
	FIRST_FREE,				! index of the first free byte
	HEAP_SIZE;

    LOCAL
	SIZE,					! Local for size_request
	NODE,					! Return Value
	L : REF VECTOR;				!

%if %bliss (bliss36) %then
macro
LIB$GET_VM (				! Get virtual memory
		num_bytes,		! Bytes to alocate, by reference
		base_addr ) =		! First address allocated, by reference
($XPO_GET_MEM (UNITS = .num_bytes, RESULT = base_addr); 1) %;
%fi

    CURRENT = .ANCHOR [0];

    IF .CURRENT EQL NULL
    THEN
	IF .ANCHOR [1] LEQ 0
	THEN
	    BEGIN

	    ! Use default values

		HEAP_SIZE = INITIAL_HEAP_SIZE/2;
		FIRST_FREE = INITIAL_HEAP_SIZE
	    END
	ELSE
	    BEGIN
		HEAP_SIZE = .ANCHOR [1]/2;
		FIRST_FREE = .ANCHOR [1]
	    END

    ELSE
	BEGIN
	    HEAP_SIZE = .CURRENT [1];
	    FIRST_FREE = .ANCHOR [1]
	END;

    SIZE = .SIZE_REQUEST;
    DEB_ASSERT (.SIZE GEQ 0 AND .SIZE LSS .HEAP_SIZE, 'Invalid storage request');

    IF (.FIRST_FREE + .SIZE) GTR .HEAP_SIZE
    THEN
	BEGIN

	! We double the size of the heap to prevent fragmentation.

	HEAP_SIZE = .HEAP_SIZE*2;

	IF NOT LIB$GET_VM (HEAP_SIZE, L) THEN DEB_ASSERT (FALSE, 'Storage overflow');

	L [0] = .ANCHOR [0];			! Chain old one in
	ANCHOR [0] = .L;
	FIRST_FREE = 8;
	L [1] = .HEAP_SIZE
	END;

    NODE = .ANCHOR [0] + .FIRST_FREE;		! Location of allocated node
    ANCHOR [1] = .FIRST_FREE + .SIZE;
    CH$FILL (%X'00', .SIZE, .NODE);
    .NODE
END;						!End of UTL_GET

END
ELUDOM