Google
 

Trailing-Edge - PDP-10 Archives - CFS_TSU04_19910205_1of1 - update/ftncsr/expres.bli
There are 12 other files named expres.bli in the archive. Click here to see a list.
!COPYRIGHT (c) DIGITAL EQUIPMENT CORPORATION 1973, 1987
!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 WHICH IS NOT SUPPLIED BY DIGITAL.

!AUTHOR: F.J. INFANTE, D. B. TOLMAN/DCE/JNG/TFV/EGM/CKS/CDM/AHM/SRM/RVM/AlB/MEM

MODULE EXPRES(RESERVE(0,1,2,3),SREG=#17,FREG=#16,VREG=#15,DREGS=4,GLOROUTINES)=
BEGIN

GLOBAL BIND EXPREV = #11^24 + 0^18 + #4537;	! Version Date: 21-May-86

%(

***** Begin Revision History *****

24	-----	-----	CODE TO WORRY ABOUT STATEMENT FUNCTION DUMMIES
			HAS BEEN REMOVED SINCE THEY ARE NOW
			SPECIAL GENERATED SUBLOCAL VARIABLES

			CODE TO WORRY ABOUT VARIABLES THE SAME AS FUNCTION
			NAMES HAS BEEN REMOVED SINCE THE NAME OF THE FUNCTION
			CURRENTLY BEING COMPILED NO LONGER HAS FNNAME SET
			ON IT

25	-----	-----	DON'T LET DUMMY ARGUMENTS WHICH HAPPEN TO BE LIBRARY
			FUNCTION NAMES TURN INTO LIBRARY FUNCTION CALLS

26	-----   -----	PICK UP THE .ED NAMES FOR ACTUAL PARAMETER
			LIBRARY FUNCITONS
			THE ROUTINE LIBSRCH HAS BEEN CHANGED TO SRCHLIB
			WITH A SYMBOL TABLE POINTER AS PARAMETER

27	-----	-----	CLEAR THE ARG1PTR FOR NEGNOT NODES  IN MACRO
			BLDTREE

28	-----	-----	IMMEDIATELY NEGATE ALL CONSTANTS PRECEEDED BY 
			UNARY MINUS.  ROUTINE PRIMITIVE

29	-----	-----	DETECT A .NOT. B IN MACRO BLDTREE

30	-----	-----	REMOVE THE CONVERSION NODE INSERT CODE
			FOR UNARY NEGATION OF DOUBLE OCTAL AND LET
			NEGCNST DO IT NOW THAT CONSTANTS ARE IMMEDIATELY NEGATED

			ROUTINE PRIMITIVE

31	-----	-----	FIX PRIMITIVE SO THAT IT WILL NOT MAKE NAMSET
			CALLS  FOR LIBRARY ROUTINE ACTUAL PARAMETERS
32	542	22147	MAKE NOT NODES BE OF TYPE LOGICAL, (DCE)
33	626	23169	DON'T ALLOW FUNCTION OR ARRAY NAMES WITHOUT
			  PARENTHESIZED LISTS TO APPEAR IN EXPRESSIONS
			  IN ARGUMENT LISTS, E.G. CATCH FN(A+3), WHERE
			  FN IS A FUNCTION AND A IS AN ARRAY., (JNG)

***** Begin Version 6 *****

34	761	TFV	1-Mar-80	-----
	Choose either KLDP or GFLOATING dotted name for generic functions.
	Convert DP to SP based on /GFLOATING

35	1004	TFV	1-Jul-80	------
	Fix dottednames to lookup /GFLOATING routines only for doubleprecision.
	Replace VREG with temp.

36	1043	EGM	19-Jan-81	20-15466
	***NOT INCLUDED IN VERSION 7*** 
	Generate warning for consecutive arithmetic operands.

37	1056	DCE	3-Mar-81	-----
	Put type conversion node beneath .NOT. node if necessary (for register
	allocation especially).

38      1072    CKS     22-May-81	Q20-1524
	***NOT INCLUDED IN VERSION 7*** 
	Remove consecutive arithmetic operators illegal message until it can be
	put under flagger switch

***** Begin Version 7 *****

39	1202	DCE	1-Jul-80	-----
	Add routine NOLPAR to assist with implementation of expressions
	in output lists.

40	1203	DCE	21-Nov-80	-----
	Change the world to do I/O list parsing here.  Add NOTEOL and
	BEXPRLIST; remove old NOLPAR.  Remove all the parsing from the BNF.
	Now I/O lists and expressions will share a good deal of common code!

41	1235	CKS	1-Jul-81	-----
	Add ERREOL to type an error message for "TYPE *,".  Without this crock,
	that statement does not parse, does not generate code, but does not get
	an error message either. 

42	1243	CKS	30-Jul-81
	Add PRCDNCE23, precedence for lexeme 23, CONCAT, the // concatenation
	operator.

43	1244	CKS	2-Aug-81
	Make REFERENCE understand substring references.  Make PRIMITIVE
	understand concatenation.

44	1255	TFV	14-Aug-81	------
	Fix LOGEXPRESSION  to handle  character relational  expressions.
	Turn them into calls to CH.xx.  (EQ, NE, LT, LE, GT, GE).   (The
	names changed to Lxx. in edit 1422.)

45	1262	CKS	21-Sep-81
	Add action routines COLNEXP and RPAREXP to parse the
	optional expressions that occur in substring bounds

46	1264	CDM	25-Sept-81
	Change name of MAKLIBFUN to MAKLIBFN, the name as declared in
	GNRCFN.BLI.

47	1270	CDM	6-Oct-81
	Add additional parameter to MAKLIBFN call.

48	1400	CKS	20-Oct-81
	Modify REFERENCE to allow null argument list in function calls

49	1413	CDM/AHM	4-Nov-81
	Modify REFERENCE  to use  ARGUMENTLIST structure  in storing  argument
	nodes.  Also, make PRIMITIVE and  MAKECALL allow for the extra  parent
	word when they  are creating  an argument list  for concetenation  and
	character relational library subroutine calls.

50	1422	TFV	12-Nov-81	------
	Change name  of  MAKCALL  to  MAKECALL.   Change  the  names  in
	MAKECALL from CH.xx.  to Lxx. (These  are character  relationals
	which become function calls).   Change REFERENCE to generate  an
	extra  argument  for  character  functions;  it  is  the  .Dnnnn
	variable for the result.

51	1425	CDM	1-Dec-81
	Creation of MAKEFN to simplifly REFERENCE which is getting  humongous.
	REFERENCE is recursive  and was using  too much stack  space from  too
	many LOCAL variables.

52	1427	CKS	2-Dec-81
	MAKESUBSTR was not defaulting the upper bound of A(I)(:) correctly.  It
	was setting the upper bound from IDCHLEN of the ARRAYREF node.  Follow
	down to the base of an array ref and set from IDCHLEN from that.

53	1434	TFV	14-Dec-81	------
	MAKEFN has  to process  character functions  after checking  for
	library functions.  It will call CHARGLIST to build a  character
	function argulment list form  a non-character function  argument
	list.

54	1431	CKS	15-Dec-81
	Get VALFLGs right in substring nodes

55	1436	SRM	16-Dec-81
	Set CHARUSED if see // or ( : )

56	1456	CKS	11-Jan-82
	Call NAMSET (as opposed to NAMREF) for variables which appear in
	input lists.  Use a new bit in the statement descriptor block to
	recognize input statements.  Can't use TYPE since EXPRESS is an
	action routine called during the parse, before the semantic routine
	has a chance to set TYPE correctly.

57	1466	CDM	1-Feb-82
	Perform compile  time  arg  checking  for  statement  functions in
	new routine ARGSFCHECK.

58	1470	CKS	2-Feb-82
	Add LEXOPGEN, routine to read an operator lexeme.  It is the same as
	LEXEMEGEN except that if it sees tic (') it returns it as TICLEX
	instead of reading a character constant.  

59	1476	RVM	8-Feb-82
	Change the name of INEXTSGN to USERFUNCTION.

60	1501	RVM	16-Feb-82
	Due to a change in the meaning of the INEXTERN and USERFUNCTION
	attributes, only INEXTERN should be tested to see if the name of
	a routine may be used as an argument.

1505	AHM	12-Mar-82
	Have MAKECALL set the  psect index of  the symbol table  entry
	for "Lxy." to PSCODE to relocate those references by .CODE.

1513	RVM	22-Mar-82
	Have REFERENCE use the routine MAKDOTTEDNAME to make a dotted
	name of a library function used as an argument to a subroutine.

1523	RVM	29-Mar-82
	Implement an extension which makes the EXTERNAL statement
	optional for a user function name when the program unit that
	tries to pass that user function name as an argument also calls
	the function directly.  This edit provides some degree of
	compatibility with other DEC compilers and the compilers of
	other vendors.  Making EXTERNAL optional is accomplished by
	setting the INEXTERN and USERFUNCTION bits in the STE of any
	user function when the function is passed as an argument.  Note
	that this edit does not make the INTRINSIC statement optional.
	We have always allowed a program unit to have a variable as the
	same name as a library function called in that program unit.
	Making INTRINSIC optional would break this.

1530	TFV	4-May-82

	Fix PRIMITIVE for concatenations.  Use ARGHDRSIZ to build the
	header words for the concatneation argument list on STK.

1531	CDM	4-May-82
	Changes for SAVE statement processing after code review.

1551	AHM	4-Jun-82
	Remove edit 1505  because external references  no longer  have
	their psect index set.

1554	CKS	3-Jun-82
	Add call to PROSUB to do substring bounds checking if /DEBUG:BOUNDS

1604	CKS	21-Jul-82
	Handle long IO lists by calling MOVSTK and COPYXLIST if necessary.

1613	CDM	10-Aug-82
	Correct table indicating errors for statement function arg checking.
	Change /DEBUG:PARAMETERS to /DEBUG:ARGUMENTS

1620	CKS	24-Aug-82
	Improve err message when you use numeric operands to concatenation
	operator.  It is not easy to say "Illegal operator for numeric data"
	since // is an n-ary operator and you tend to say it many times.
	The message is "numeric operand to concatenation operator".

1651	CKS	18-Oct-82
	Fix omitted substring upper bound (C(I:)) to handle length * variables.
	Build a substring node for C(I:LEN(C)) for this case.

***** End V7 Development *****

1752	CDM	18-May-83
	Don't assume  that  a function  reference  may be  an  intrinsic
	function it  it  has been  previously  declared as  a  statement
	function.

2077	RJD	11-Feb-85	SPR:10-35087
	Check the structure of passed arguments.


***** Begin Version 10 *********

2253	AlB	28-Dec-83
	Compatibility flagging for non-integer substring bounds.
	Routine:
		MAKESUBSTR

2255	AlB	29-Dec-83
	Compatibility flagging of .NOT. used with non-logical operands.
	Routine:
		LOGEXPRESSION

2517	CDM	1-Feb-85
	Enhancements to argument checking, upgrading for statement
	functions to be up with external routines, and a few bug fixes in
	statement functions.  Added checks for structure in arguments;
	singleton (scalar), array, routine.  Added character length
	checking in statement functions.

***** End V10 Development *****

2532	CDM	9-May-85
	Only put out type checking information for known (called)
	function names.  Subroutine and ambiguous external names
	should not have type checking information output.

2557	JB	6-Nov-85
	Check for the use of specific function names in argument lists
	instead of in INTRINSIC statements. Call routine INTRCHK to
	perform the check.

***** End Revision History *****

***** Begin Version 11 *****

4515	CDM	20-Sep-85
	Phase I for VMS long symbols.  Create routine ONEWPTR for Sixbit
	symbols.  For now, return what is passed it.  For phase II, return
	[1,,pointer to symbol].

4516	CDM	2-Oct-85
	Phase I.I for VMS long symbols.  Pass Sixbit to all error message
	routines, do not pass addresses of Sixbit anymore.  In later edits
	this will pass [length,,pointer to symbol] instead of a pointer to
	this to the error message routines.

4537	MEM	21-May-86
	Add PRCDNCE25 for underline lexeme.

ENDV11
)%
SWITCHES NOLIST;
REQUIRE  LEXNAM.BLI;
REQUIRE ASHELP.BLI;
REQUIRE FIRST.BLI;
REQUIRE TABLES.BLI;
SWITCHES LIST;


BIND PRECEDENCE =	!THE PRECEDENCE OF THE EXPRESSION OPERATORS
	PLIT(	%PRECEDENCE,,OPERATOR FLAG COMBINED IF NEGATIVE%
	PRCDNCE0,	!NULL FOR INDEXING
	PRCDNCE1,
	PRCDNCE2,
	PRCDNCE3,
	PRCDNCE4,
	PRCDNCE5,
	PRCDNCE6,
	PRCDNCE7,
	PRCDNCE8,
	PRCDNCE9,
	PRCDNCE10,
	PRCDNCE11,
	PRCDNCE12,
	PRCDNCE13,
	PRCDNCE14,
	PRCDNCE15,
	PRCDNCE16,
	PRCDNCE17,
	PRCDNCE18,
	PRCDNCE19,
	PRCDNCE20,
	PRCDNCE21,
	PRCDNCE22,
%1243%  PRCDNCE23,
%1470%	PRCDNCE24,
%4537%	PRCDNCE25
	);

MACRO	OPER(X)= (.PRECEDENCE[X] LSS 0)$;

MACRO ERR0(X)= RETURN FATLEX( X, .LEXNAME[.LEXL<LEFT>], E0<0,0> ) $;
EXTERNAL % Routines %

	ARRXPN,
	CNVNODE,
	CCONST,		! Create complex constant
%2255%	CFLAGB,		! Put out flagger warning
%1434%	CHARGLIST,	! Create the  argument  list node for a character
			! function.  The first argument is the descriptor
			! for the result.
%2517%	CHEXLEN,	! Returns length of character expression
	COPYLIST,	! Create ptr to list (on STK)
%1604%	COPYXLIST,	! Same as COPYLIST but for saved STK created by MOVSTK
	CORMAN,
	EXPRTYPER,	! Get type right (insert type conv node)
	FATLERR,	! Error routine
	FATLEX,		! Fatal error handler
	LEXEMEGEN,	! Get next lexeme
	LEXICAL,	! The lexical scanner
	MAKLIBFN,	! Makes a node for library functions
%1604%	MOVSTK,		! Save STK in free core to handle long lists
	NAMREF,
	NAMSET,
%1422%	NEWDVAR,	! Routine to generate a .Dnnnn compile-time-constant
%1422%			! character descriptor
	NEWENTRY,	! Create new entry
%4515%	ONEWPTR,	! Returns [1,,pointer] to Sixbit argument
	PROGNAME,
%1554%	PROSUB,
	SRCHLIB,
	SAVSPACE,
	TBLSEARCH;	! Enter new variable name

EXTERNAL % GLOBALS %
%1436%	CHARUSED,	! Character data used. Set if see // or [ : ]
	ASTATFUN,
	E47,
%1434%	E180,		! Error for character function references with length *
%1466%	E185,		! Wrong number of arg's for a statement function
	E186,
%1531%	E192,		! "illegal for SAVE statement"
	E206,
	E207,
%2253%	E257,		! "Extension to Fortran-77: Non-integer substring bounds"
%2255%	E290,		! "Numeric in logical context"
%2517%	E318,		! Length mismatch for statement function
%2077%	E319,		! Illegal argument for statement function
	ENTRY,		! arg to TBLSEARCH - data for new entry
	EVALTAB EVALU,	! Table to access LINK type codes
	GSTCSCAN,
	GSTLEXEME,
	ISN,		! Number of the statement being processed
%1651%	LENATTRIB,	! Library function attributes for LEN.
	LEXL,		! Current lexeme
	LEXNAME,	! Table of lexeme names (ASCII)
%1004%	LIBATTSTR LIBATTRIBUTES,	!Table of library function attributes
%1004%	LIBFUNTAB,	! Table of library function names
	LOOK4CHAR,
	LSAVE,		! Is LEXL valid (-1 is yes, 0 is no)
	NAME,		! arg to TBLSEARCH - table to search
	MAKEPR,		! Make expr node
	STK,		! Parsing stack
	SP;		! Stack ptr for STK

FORWARD % Routines contained in this module %
	NOTEOL,		! Action routine to test end-of-line condition
	GIOLIST,	! Action routine to handle top level I/O lists
	EXPRESSION,	! Action routine to handle all expressions (NOT lists)

	BOTHCHAR,	! Routine to test relationals for both args of type character
%1422%	MAKECALL,	! Routine to turn character relationals into calls to 
			! Lxx.
%1425%	MAKEFN,		! Builds function call node.
	LOGEXPRESSION,	! Operator precedence top level expression parser
	MAKESUBSTR,	! Create substring node
	REFERENCE,	! Variables and Function references
	PRIMITIVE,	! Parentheses - complex constants, lists, etc.
	LEXOPGEN,	! LEXEMEGEN for operators - checks for ' coming up
	BEXPRLIST,	! Process list, implied DO loop parameters
	ARGSFCHECK,	! Routine to perform compile time argument checking for
			! statement functions.
%2557%	INTRCHK;	! Check if arguments are specific function names

OWN LISTOK;	! IF -1, the current context allows a list of elements (I/O list)
GLOBAL ROUTINE NOTEOL=

!	Routine is an "action" routine called by the syntax analyser to
!	ensure that an expression list is forthcoming.  Checks for the next
!	lexeme being an end of line, and if so, it fails, otherwise succeeds.
!	This routine is always called immediately prior to GIOLIST.
!	This routine is all new in edit 1203.

BEGIN
	IF .LSAVE EQL 0 THEN (LEXL = LEXEMEGEN(); LSAVE = -1);
	IF .LEXL<LEFT> EQL LINEND THEN RETURN -1 ELSE RETURN 0
END %NOTEOL%;
GLOBAL ROUTINE ERREOL =

!	As above, but type an error message if EOL is encountered

BEGIN
	IF .LSAVE EQL 0 THEN (LEXL = LEXEMEGEN(); LSAVE = -1);
	IF .LEXL<LEFT> EQL LINEND THEN RETURN ERR0L(.LEXNAM[IDENTIFIER]);
END %ERREOL%;
GLOBAL ROUTINE COLNEXP=	! [1262] New

! COLNEXP is an action routine used in parsing the optional expression in
! character substring bounds.  COLNEXP stands for colon or expression.  It
! looks at the upcoming lexeme; if it's a colon COLNEXP returns 0, leaving
! the colon unread, otherwise the lexeme must be the first lexeme of an
! expression, and COLNEXP reads the expression and returns a pointer to it.

BEGIN
	IF .LSAVE EQL 0 THEN (LEXL = LEXEMEGEN(); LSAVE = -1);
	IF .LEXL<LEFT> EQL COLON THEN RETURN (STK[SP = .SP+1] = 0)
				 ELSE RETURN EXPRESSION();
END;



GLOBAL ROUTINE RPAREXP=		! [1262] New

! RPAREXP is like COLNEXP but reads right paren or an expression.

BEGIN
	IF .LSAVE EQL 0 THEN (LEXL = LEXEMEGEN(); LSAVE = -1);
	IF .LEXL<LEFT> EQL RPAREN THEN RETURN (STK[SP = .SP+1] = 0)
				  ELSE RETURN EXPRESSION();
END;
GLOBAL ROUTINE GIOLIST=
BEGIN

!	Routine is an "action" routine called by the syntax analyser to 
!	parse a general I/O list of elements.  It is intertwined very heavily
!	with the general expression parsing routines, and leaves a representation
!	of the I/O list on STK.  In general, when a completed sublist is found,
!	STK will be updated and a zero value returned to indicate this.
!	If we merely have an expression, however, a pointer to it will get
!	passed back until the complete expression is available (until the
!	comma which terminates the list element is found).
!	This routine is always called immediately after NOTEOL.
!	This routine is all new in edit 1203.


	LOCAL IOELEMENT;	! ptr to an I/O list expression/sublist
	LOCAL LSP,		! save the STK pointer for COPYLIST
%1604%	      STKSV,		! saved STK for COPYXLIST
%1604%	      COUNT;		! count of saved STK elements

	LSP = .SP;		! save SP for COPYLIST
%1604%	STKSV = 0;		! no overflow from STK yet

	DO
	BEGIN	! process each list item
		LISTOK = -1;	! Sublists are allowed
		FLGREG<FELFLG> = 1; ! Bare array references are allowed too
		IF (IOELEMENT = LOGEXPRESSION()) LSS 0 THEN RETURN .VREG;
		IF .IOELEMENT NEQ 0 THEN (STK[SP = .SP+1] = 1; STK[SP = .SP+1] = .IOELEMENT);

%1604%		! Check for stack overflow
%1604%		IF .SP GEQ STKSIZ-20		! if STK is getting full
%1604%		THEN MOVSTK(LSP,STKSV,COUNT);	! move this portion of the list

	END
	UNTIL (IF .LEXL<LEFT> NEQ COMMA THEN TRUE
		ELSE (LEXL = LEXEMEGEN(); FALSE));


	! Done with the list - better have an end-of-line!

	IF .LEXL<LEFT> NEQ LINEND THEN ERR0(PLIT'comma or end of statement?0');

%1604%	IF .STKSV EQL 0
	THEN COPYLIST(.LSP)
%1604%	ELSE COPYXLIST(.LSP,.STKSV,.COUNT);

	RETURN 0;
END;
GLOBAL ROUTINE EXPRESSION=
BEGIN

!	Routine is an "action" routine called by the syntax analyser to
!	parse a general FORTRAN expression.  It returns a pointer to the
!	final resulting expression node in STK[SP = .SP+1].
!	This routine is NOT recursive, although LOGEXPRESSION (which is
!	called to do the work) is recursive.

	LOCAL LSP;  ! Local STK ptr

	LSP = .SP;
	LISTOK = 0;	! No lists allowed
	IF .LSAVE EQL 0 THEN  (LSAVE _ -1;LEXL _ LEXEMEGEN());
	IF .LEXL<LEFT> EQL LINEND THEN ERR0(.LEXNAM[IDENTIFIER]); ! No expression found
	RETURN  STK[SP = .LSP+1] = LOGEXPRESSION();
END;
ROUTINE REFSET(A,B) =			! [1456] New

! Call NAMSET or NAMREF.  Calls NAMSET if in READ statement (or READ-like
! statement), otherwise calls NAMREF.

! In a READ statement, we wish to call NAMSET for the variables that appear at
! the top level of the list.  For instance, in the list A,B(I),C(D(I)) we wish
! to call NAMSET for A, B, and C, but not D or I.  In fact, we wish to call
! NAMSET for any variable that appears in a context where an IO list is legal.
! The variable LISTOK is set if an IO list is legal.

BEGIN

	EXTERNAL STMNDESC;

	IF .LISTOK NEQ 0		! If reading an IO list
	THEN IF .IOINPT(@STMNDESC)	! and in an input statement
	     THEN NAMSET(.A,.B)		! the variable will be stored into
	     ELSE NAMREF(.A,.B)		! else it will just be referenced
	ELSE NAMREF(.A,.B);

END;
ROUTINE BOTHCHAR(ARG1,ARG2)=
BEGIN

%1255%	! Written 13-Aug-81 by TFV

	! Tests relational for both args of type character
	! Return TRUE for both character, otherwise FALSE

	MAP BASE ARG1:ARG2;

	IF .ARG1[VALTYPE] EQL CHARACTER AND .ARG2[VALTYPE] EQL CHARACTER
	THEN RETURN TRUE
	ELSE RETURN FALSE;

END;	! BOTHCHAR
ROUTINE MAKECALL(EXPR)=
BEGIN

%1255%	! Written 13-Aug-81 by TFV

	! EXPR is a relational expression node with character args
%1422%	! Turn it into a call to Lxx. (EQ, NE, GT, GE, LT, LE)

	REGISTER BASE ARG1:ARG2:FNID;
%1422%	REGISTER ARGUMENTLIST ARGBLOCK;
	MAP BASE EXPR;
	EXTERNAL TBLSEARCH, CORMAN, CGERR;

	ARG1 = .EXPR[ARG1PTR];	! Pick up first arg
	ARG2 = .EXPR[ARG2PTR];	! Pick up second arg

	EXPR[VALTYPE] = LOGICAL;	! Result is logical
	EXPR[OPRCLS] = FNCALL;		! Convert relational expression node 
					! into function call

	CASE .EXPR[OPERSP] OF SET	! Get name based on relational operator

		CGERR();		! Internal compiler error

%1422%		ENTRY = SIXBIT 'LLT.';	! LT
%1422%		ENTRY = SIXBIT 'LEQ.';	! EQ
%1422%		ENTRY = SIXBIT 'LLE.';	! LE

		CGERR();		! Internal compiler error

%1422%		ENTRY = SIXBIT 'LGE.';	! GE
%1422%		ENTRY = SIXBIT 'LNE.';	! NE
%1422%		ENTRY = SIXBIT 'LGT.'	! GT

	TES;

%4515%	ENTRY = ONEWPTR(.ENTRY);	! Put in [Count,,pointer] format

	EXPR[OPERSP] = LIBARY;		! It's a library function
%1422%	NAME = IDTAB;			! Get Lxx. symbol table entry
	EXPR[ARG1PTR] = FNID = TBLSEARCH();	! Get or create STE
	FNID[VALTYPE] = LOGICAL;	! Type is logical
	FNID[OPRCLS] = DATAOPR;
	FNID[OPERSP] = FNNAME;		! It's a function name
	FNID[IDLIBFNFLG] = 1;		! It's a library function too,
					!  so never allocate it

	BTTMSTFNFLG = 0;		! Not bottommost (destroy's AC16)

%1413%	NAME<LEFT> = ARGLSTSIZE(2);	! Make a four word argblock entry
	EXPR[ARG2PTR] = ARGBLOCK = CORMAN();

	ARGBLOCK[ARGCOUNT] = 2;		! Two arg function 

	ARGBLOCK[1, ARGNPTR] = .ARG1;	! Copy pointer to first arg

	IF .ARG1[OPRCLS] EQL DATAOPR
	THEN	ARGBLOCK[1, AVALFLG] = 1	! DATAOPR flag
	ELSE	ARG1[PARENT] = .EXPR;		! Up pointer

	ARGBLOCK[2, ARGNPTR] = .ARG2;	! Copy pointer to second arg

	IF .ARG2[OPRCLS] EQL DATAOPR
	THEN	ARGBLOCK[2, AVALFLG] = 1	! DATAOPR flag
	ELSE	ARG2[PARENT] = .EXPR;		! Up pointer

END;	! MAKECALL
GLOBAL ROUTINE LOGEXPRESSION=
BEGIN

!	Routine is called by the action routines EXPRESSION and GIOLIST to
!	parse arbitrary FORTRAN expressions and I/O lists, respectively.
!	The value of LISTOK determines what kinds of elements are to be
!	parsed (-1 for an I/O list and 0 for an expression).  This routine
!	is basically an operator precedence machine; the precedence of the
!	operators is given in the table in this file.  This routine is
!	recursive, and may be called from PRIMITIVE, REFERENCE, and/or
!	BEXPRLIST.

MACRO BLDTREE(OPRATOR)=
BEGIN
    LABEL BLDTR;
    BLDTR: BEGIN

	LOCAL OPR;
	REGISTER BASE R2:T1:T2;
	EXTERNAL CGERR;

	OPR = .OPRATOR;
	NAME = EXPTAB; !GENERATE AN EXPRESSION NODE
	T1 = NEWENTRY();
	T1[ARG2PTR] = R2 = .STAK[.STP]; STP = .STP-1;
	IF .OPR<LEFT> EQL LOGICALNOT
	THEN
	BEGIN	! Logical .NOT.
		EXTERNAL FATLEX,E132,E163,TPCDMY;
		%MAKE SURE THIS ISN'T A BINARY .NOT.%
		IF .STP  NEQ 0
		THEN
			IF .STAK[.STP-1]<LEFT> LEQ LASTLEX
			THEN
				IF NOT OPER( .STAK[.STP-1]<LEFT>)
				THEN	RETURN FATLEX(E132<0,0>);

%1255%		! .NOT. character constant becomes .NOT. hollerith constant
%1255%		! .NOT. character var or expression gives fatal error (ICN)
%1255%		! "Illegal combination of character and numeric"
%2255%		! .NOT. numeric operand may give flagger warning (NLC)
%2255%		! "Numeric operand in logical context"
%1255%		IF .R2[VALTYPE] EQL CHARACTER
%1255%		THEN	IF .R2[OPERATOR] EQL CHARCONST
%2255%			THEN
%2255%			    BEGIN
%2255%			    IF FLAGEITHER THEN CFLAGB(E290<0,0>); ! Warning (NLC)
%2255%			    R2[OPERATOR] = HOLLCONST	! Make it HOLLERITH
%2255%			    END
%2255%			ELSE RETURN FATLEX(E163<0,0>)	! Fatal error (ICN)
%2255%		ELSE
%2255%			IF FLAGEITHER
%2255%			THEN	IF .R2[VALTYPE] GEQ INTEGER
%2255%				THEN CFLAGB(E290<0,0>);	! Warning (NLC)

		T1[OPRCLS] = NEGNOT; T1[OPERSP] = NOTOP;

		IF .R2[OPRCLS] EQL DATAOPR
		THEN T1[A2VALFLG] = 1
		ELSE
		BEGIN
			R2[PARENT] = .T1;
			IF .R2[FNCALLSFLG] THEN T1[FNCALLSFLG] = 1;
		END;

		!NOT NODES SHOULD ALWAYS BE OF TYPE LOGICAL
		T1[VALTYPE] = LOGICAL;
		T1[ARG1PTR] = 0;

![1056] If a node of double size sits below a .NOT. node (shudder),
![1056] we need to insert an intervening type conversion node so that
![1056] register allocation does not use odd registers for dp and
![1056] complex numbers.
%[1056]%	IF .R2[DBLFLG] THEN T1[ARG2PTR] = TPCDMY(.T1,.R2);
%1255%		LEAVE BLDTR WITH .T1	! Don't call exprtyper for character relationals
	END;	! Logical .NOT.

	T1[ARG1PTR] =  .STAK[STP = .STP-1];
	CASE .OPR<LEFT>-6 OF SET

	BEGIN	! Relational
%1255%		T1[OPRCLS] = RELATIONAL;
%1255%		T1[OPERSP] = .OPR<RIGHT>;
%1255%		IF BOTHCHAR(.T1[ARG1PTR],.T1[ARG2PTR])
%1255%		THEN
%1255%		BEGIN	! Character relational make it a call to Lxx.
%1255%			MAKECALL(.T1);
%1255%			LEAVE BLDTR WITH .T1
%1255%		END;	! Character relational make it a call to Lxx.
%1255%	END;	! Relational

%NOT%		(T1[OPRCLS] = NEGNOT;T1[OPERSP] = NOTOP);
%AND%		(T1[OPRCLS] = BOOLEAN;T1[OPERSP] = ANDOP);
%OR%		(T1[OPRCLS] = BOOLEAN;T1[OPERSP] = OROP);
%MATCH%		(T1[OPRCLS] = BOOLEAN;T1[OPERSP] = IF .OPR<RIGHT> EQL 1 THEN EQVOP ELSE XOROP);
%POWER%		(T1[OPRCLS] = ARITHMETIC;T1[OPERSP] = EXPONOP);
		CGERR();
		CGERR();
		CGERR();
		CGERR();
		CGERR();
		CGERR();
%MINUS%		(T1[OPRCLS] = ARITHMETIC;T1[OPERSP] = SUBOP);
%DIVIDE%	(T1[OPRCLS] = ARITHMETIC;T1[OPERSP] = DIVOP);
%PLUS%		(T1[OPRCLS] = ARITHMETIC;T1[OPERSP] = ADDOP);
%TIMES%		(T1[OPRCLS] = ARITHMETIC;T1[OPERSP] = MULOP)
	TES;
	R2 = .T1;  EXPRTYPER(.T1); !SAVING EXPRESSION PTR
			!EXPRTYPER BUILDS A TYPE CONVERSION NODE IF NECESSARY
	T1 = .R2;	!RESTORING PTR
	R2 = .T1[ARG2PTR]; T2 = .T1[ARG1PTR]; !RESTORING PTRS
	IF .R2[OPRCLS] EQL DATAOPR THEN T1[A2VALFLG] = 1
		 ELSE (R2[PARENT] = .T1;IF .R2[FNCALLSFLG] THEN T1[FNCALLSFLG] = 1;);

	IF .T2[OPRCLS] EQL DATAOPR THEN T1[A1VALFLG] = 1
		 ELSE (T2[PARENT] = .T1; IF .T2[FNCALLSFLG] THEN T1[FNCALLSFLG] = 1;);

	.T1
    END
END$;	!OF BLDTREE

LOCAL STAK[14], STP; !STACK AND STACK PTR
LOCAL SLISTOK;
REGISTER BASE R1;
EXTERNAL POOL;
LABEL EXPR1,EXPR2;

!
!CHECK FOR STACK OVERFLOW
!
IF .SREG<RIGHT> GEQ (POOL<0,0>-50)<0,0> THEN RETURN FATLEX(E90<0,0>);
!
STP = -1;	!INITIALIZE THE STACK PTR
SLISTOK = .LISTOK; ! Save incoming value of LISTOK

WHILE 1 DO
BEGIN
EXPR1:
	IF .LEXL<LEFT> EQL LOGICALNOT
	THEN LISTOK = 0
	ELSE
	BEGIN
		R1 = PRIMITIVE(); ! Get an operand (or I/O list)
		IF .R1 LEQ 0 THEN RETURN .R1 ! Can't have an operator after a list...
		 ELSE STAK[STP = .STP + 1] = .R1;
		LISTOK = 0; ! Cannot have a list after a primitive seen (until comma)

	EXPR2:  WHILE 1 DO
		BEGIN
		   IF NOT OPER(.LEXL<LEFT>)
			THEN (IF .STP LEQ 0 THEN (LISTOK = .SLISTOK; RETURN .STAK[.STP]) )
			ELSE (
				IF .STP LEQ 0 THEN LEAVE EXPR2;
				IF .PRECEDENCE[.LEXL<LEFT>] GTR .PRECEDENCE[.STAK[.STP-1]<LEFT>]
				THEN LEAVE EXPR2; !LEAVE TO STACK THE OPERATOR
			     );
		!HERE IF NOT OPERATOR AND STACK PTR GTR 0
		!OR
		!IF OPERATOR PRECEDENCE LEQ PREVIOUS OPERATOR'S

		   STAK[.STP] = BLDTREE(STAK[.STP-1]);	!BUILD A TREE NODE
		END; !OF WHILE 1 DO
	    END; !OF IF LEXL NEQ NOTOP
	!
	!HERE IF STACKING HIGER PRECEDENCE OPERATOR OR
	!NOT OP SEEN OR FIRST OPERATOR SEEN
	!

	STAK[STP = .STP+1] = .LEXL;
	LEXL = LEXEMEGEN();
%[626]%	FLGREG<FELFLG> = 0;	!ARRAYREFS OR FNS W/O ARG LISTS NO LONGER LEGAL
END;	!OF WHILE 1 DO
	!EXIT FROM THIS LOOP IS BY RETURN FROM INSIDE THE LOOP
END;	!OF LOGEXPRESSION
ROUTINE MAKESUBSTR(IDPTR)=	! [1244] New

! Makes a substring node
! Args:		IDPTR = variable or ARRAYREF node
!		STK[SP+1] = lower bound expression
!		STK[SP+2] = upper bound expression
!
! Returns -1 if error:
!	IDPTR doesn't point to something of type CHARACTER
! Returns pointer to a substring node if no error

BEGIN	! MAKESUBSTR

	MAP BASE IDPTR;
	REGISTER BASE T1;		! TEMP
	REGISTER BASE SSNODE;		! SUBSTRING NODE
	LOCAL BASE LBOUND:UBOUND;	! LOWER AND UPPER BOUND EXPRESSIONS

	EXTERNAL CNVNODE,NEWENTRY,CORMAN,MAKPR1,ONEPLIT;
	EXTERNAL E162;

	! CHECK THAT EXPRESSION WE'RE SUBSTRINGING IS TYPE CHARACTER
	IF .IDPTR[VALTYPE] NEQ CHARACTER
	THEN RETURN FATLEX (E162<0,0>);	!"Substring of non-CHARACTER variable"

%1436%	CHARUSED = TRUE;		! Global flag for character
					! data used

	NAME = EXPTAB;			! MAKE A BLANK EXPRESSION NODE
	NAME<LEFT> = SUBNODESIZ; 	! 5 WORDS LONG INSTEAD OF 4
	SSNODE = NEWENTRY();		! FILL IT IN...
	SSNODE[VALTYPE] = CHARACTER;	! VALTYPE: CHARACTER
	SSNODE[OPRCLS] = SUBSTRING;	! OPRCLS: SUBSTRING
	SSNODE[ARG4PTR] = .IDPTR;	! ARG4PTR: CHAR VARIABLE OR ARRAYREF

	LBOUND = .STK[.SP+1];		! CONVERT LOWER BOUND TO INTEGER
	IF .LBOUND EQL 0
	THEN LBOUND = .ONEPLIT
%2253%	ELSE
%2253%		IF .LBOUND[VALTP1] NEQ INTEG1
%2253%		THEN	! Force lower bound to be integer
%2253%			BEGIN
%2253%			IF FLAGANSI THEN WARNERR(.ISN,E257<0,0>); ! Comp flagger
%2253%			LBOUND = CNVNODE(.LBOUND,INTEGER,0) ! Convert to integer
%2253%			END;

	UBOUND = .STK[.SP+2];		! GET UPPER BOUND EXPR, OR 0 IF OMITTED

	IF .IDPTR[OPRCLS] NEQ ARRAYREF	! GET LENGTH OF CHAR VARIABLE
	THEN T1 = .IDPTR[IDCHLEN]	! IF NON-ARRAYREF, GET FROM DATAOPR
	ELSE (T1 = .IDPTR[ARG1PTR]; T1 = .T1[IDCHLEN]);
					! IF ARRAYREF, FOLLOW TO BASE DATAOPR

%2253%	IF .UBOUND NEQ 0
%2253%	THEN
%2253%		BEGIN ! Upper bound specified
%2253%		IF .UBOUND[VALTP1] NEQ INTEG1
%2253%		THEN	!Force upper bound to be integer
%2253%			BEGIN
%2253%			IF FLAGANSI THEN WARNERR(.ISN,E257<0,0>); ! Comp flagger
%2253%			UBOUND = CNVNODE(.UBOUND,INTEGER,0) ! Convert
%2253%			END
%2253%		END ! Upper bound specified
	ELSE IF .T1 NEQ LENSTAR		! ELSE FILL IN DEFAULT
	     THEN UBOUND = MAKECNST(INTEGER,0,.T1)
%1651%	     ELSE
%1651%	     BEGIN			! For length *, put in explicit call
%1651%					! to LEN
%1651%	            LOCAL BASE LENSYM;
%1651%		    LOCAL ARGUMENTLIST ARGLST;
%1651%
%1651%		    BTTMSTFNFLG = FALSE; ! set destroy-ac-16 flag
%1651%
%1651%		    ! Make arg list and FNCALL node
%1651%
%1651%		    NAME<LEFT> = ARGLSTSIZE(1);	! get block for 1 arg
%1651%		    ARGLST = CORMAN();
%1651%
%1651%		    NAME = IDTAB;
%4515%		    ENTRY = ONEWPTR(SIXBIT 'LEN.');
%1651%		    LENSYM = TBLSEARCH();
%1651%		    IF NOT .FLAG	! if LEN. is a new entry, fill it in
%1651%		    THEN BEGIN
%1651%			LENSYM[VALTYPE] = INTEGER;
%1651%			LENSYM[OPERSP] = FNNAME;
%1651%		        LENSYM[IDFNATTRIB] = .LENATTRIB;
%1651%			LENSYM[IDPSECT] = PSCODE;
%1651%		    END;
%1651%
%1651%		    UBOUND = MAKEPR(FNCALL,LIBARY,INTEGER,.LENSYM,.ARGLST);
%1651%
%1651%		    ! Fill in arg block
%1651%
%1651%		    ARGLST[ARGCOUNT] = 1;
%1651%		    ARGLST[1,ARGNPTR] = .IDPTR;
%1651%		    ARGLST[1,AVALFLG] = 1;
%1651%		    
%1651%	     END;

%1431%	IF .UBOUND[OPRCLS] EQL DATAOPR	! SET VALFLG OR PARENT POINTER
%1431%	THEN SSNODE[A1VALFLG] = 1	!   FOR UPPER BOUND NODE
%1431%	ELSE UBOUND[PARENT] = .SSNODE;

 	! MAKE EXPRESSION NODE FOR LOWER BOUND - 1
        LBOUND = MAKPR1(.SSNODE,ARITHMETIC,SUBOP,INTEGER,.LBOUND,.ONEPLIT);

	SSNODE[ARG1PTR] = .UBOUND;	! ARG1PTR: UPPER BOUND
	SSNODE[ARG2PTR] = .LBOUND;	! ARG2PTR: LOWER BOUND MINUS 1

	IF .IDPTR[OPRCLS] NEQ DATAOPR 	! IF SUBSTRINGEE IS NOT A SIMPLE VARIABLE
	THEN IDPTR[PARENT] = .SSNODE;	! SET ITS PARENT POINTER

%1554%	RETURN PROSUB(.SSNODE);

END;	! MAKESUBSTR
GLOBAL ROUTINE REFERENCE=
BEGIN
	! Routine to  parse a  variable or  function reference.   Incoming
	! lexeme is already available in  LEXL and must be an  identifier.
	! REFERENCE then proceeds to check for array or function reference
	! and if  a left  paren is  seen then  the list  of subscripts  or
	! arguments is  scanned.   Return  a  pointer  to  a  variable  or
	! function reference node.


	LOCAL BASE IDPTR;

	REGISTER BASE T1:T2;


MACRO ERR65(X)= RETURN  FATLEX ( X, E65<0,0> ) $;


IF .LEXL<LEFT> NEQ IDENTIFIER THEN ERR0(.LEXNAM[IDENTIFIER]);

IDPTR = .LEXL<RIGHT>; !PTR TO IDENTIFIER
%1470% LEXL = LEXOPGEN(); !NEXT LEXEME TO LOOK FOR "("
IF .LEXL<LEFT> EQL LPAREN
THEN
    BEGIN	!ARRAY REFERENCE OR FUNCTION REFERENCE
	LOCAL SLISTOK;
	LOCAL LSP; LSP =.SP;	!SAV THE STK PTR FO SYNTAX

%1400%	! Check for function call with null argument list

%1400%	LOOK4CHAR = ")";	! CHECK FOR RIGHT PAREN
%1400%	IF LEXICAL(.GSTCSCAN) EQL 0 	! IF "NAME()"
%1400%	THEN			! THEN WE HAVE FUNCTION WITH NULL ARG LIST
%1400%	BEGIN			! ELSE NORMAL CASE, NOT "NAME()"
	SLISTOK = .LISTOK;
	LISTOK = 0;
	DO BEGIN	!WHILE REFERENCE FOLLOWED BY ","
		LEXL = LEXEMEGEN();
		IF .IDPTR[OPRSP1] NEQ ARRAYNM1  !IF NOT ARRAY THEN FUNCTION CALL
		THEN FLGREG<FELFLG> = 1; !SET FLG FOR CHECKING ARGS IN ARGLIST OF FUNCTION
		IF (.LEXL<LEFT> EQL DOLLAR) OR (.LEXL<LEFT> EQL ANDSGN)
		THEN RETURN FATLEX(E83<0,0>); !LABEL ARGS ARE ILLEGAL IN FUNCTION OR ARRAY REF'S
%[1244]%	IF .LEXL<LEFT> EQL COLON
%[1244]%	THEN STK[SP = .SP+1] = 0
		ELSE IF (STK[SP = .SP+1] = LOGEXPRESSION()) LSS 0 THEN RETURN .VREG;
	   END WHILE .LEXL<LEFT> EQL COMMA;

	![1244] Have seen IDENTIFIER ( LOGEXPRESSION , LOGEXPRESSION , ...
	!followed by something that is not a comma.
	!If it is a right paren, we have seen an array ref or fn call.
	!If it is a colon and there was exactly one LOGEXPRESSION, we have
	!a substring reference.

	IF .LEXL<LEFT> EQL COLON
	THEN
	IF .SP EQL .LSP+1		! IF 1 EXPRESSION BEFORE COLON
	THEN				! THEN WE HAVE A SUBSTRING REFERENCE
	BEGIN	! [1244] SUBSTRING

		! UNTIL NOW WE'VE BEEN PARSING WHAT COULD BE A FUNCTION CALL,
		! SO ALLOWED BARE ARRAY NAMES.  NOW THAT WE KNOW IT'S A
		! SUBSTRING REFERENCE, WE MUST DISALLOW ARRAY NAMES AS THE
		! LOWER BOUND EXPRESSION.

		T1 = .STK[.SP];		! CHECK LOWER BOUND EXPRESSION
		IF .T1 NEQ 0			! IF EXPRESSION PRESENT,
		THEN IF .T1[OPRCLS] EQL DATAOPR	! IF SIMPLE IDENTIFIER
		THEN IF .T1[PARENLSTFLG]	! WHICH NEEDED TO BE FOLLOWED
						! BY ARGS OR SUBSCRIPTS
		THEN RETURN NAMREF(VARIABL1,.T1); ! CALL NAMREF TO TYPE ERROR

		FLGREG<FELFLG> = 0;	! BARE ARRAY NAMES ARE NOW ILLEGAL

		LEXL = LEXEMEGEN();	! READ THE COLON

		IF .LEXL<LEFT> EQL RPAREN
		THEN STK[SP = .SP+1] = 0	! IF NULL EXPRESSION, PUSH 0
		ELSE IF (STK[SP = .SP+1] = LOGEXPRESSION()) LSS 0 THEN RETURN .VREG;
					! READ THE UPPER BOUND EXPRESSION
		IF .LEXL<LEFT> NEQ RPAREN THEN ERR0L(.LEXNAM[RPAREN]);
		LEXL = LEXEMEGEN();	! READ THE RIGHT PAREN

		SP = .LSP;		! RESTORE STK POINTER
%1456%		REFSET(VARIABL1,.IDPTR); ! RECORD (AND CHECK) SCALAR REFERENCE
		RETURN MAKESUBSTR(.IDPTR); ! BUILD SUBSTRING NODE AND RETURN
	END;	! [1244] SUBSTRING

	LISTOK = .SLISTOK;
	IF .LEXL<LEFT> NEQ RPAREN THEN ERR0(.LEXNAM[RPAREN]);
%1400%	END;			! NORMAL CASE, NOT "NAME()"

	FLGREG<FELFLG> = 0; !TURN OFF FELFLG FOR NEXT FUNCTION CALL

%1400%	IF .SP EQL .LSP
%1400%	THEN STK[SP = .SP+1] = -1	! NULL ARG LIST
%1400%	ELSE
%1400%	BEGIN	! NON-NULL ARG LIST
		COPYLIST(.LSP); !COPY LIST FROM STK TO FREE CORE
		INCR ARG FROM .STK[.SP] TO .STK[.SP]+.STK[.SP]<LEFT> DO
		BEGIN MAP BASE ARG;
			MACRO ARGPTR=0,0,FULL$, ARGFLG=0,0,LEFT$;
			LOCAL BASE R2;
			R2 = .ARG[ARGPTR];
			IF .R2[OPRCLS] EQL DATAOPR
			THEN ARG[P1AVALFLG] = 1
			ELSE ARG[P1AVALFLG] = 0;
		END; !OF INCR ARG
%1400%	END;	! NON-NULL ARG LIST
!
!NOW SEE IF FUNCTION CALL OR ARRAY REF TO MAKE PROPER NODE TYPE
!
%1470%	LEXL = LEXOPGEN(); !FOR POSSIBLE RETURN TO CALLING ROUTINE
	IF .IDPTR[OPRSP1] NEQ ARRAYNM1
	THEN !Identifier is function name
	BEGIN	!Is function

%1425%		IDPTR = MAKEFN(.IDPTR);	!Make function node

%1400%		IF .STK[.SP] NEQ -1 THEN
		SAVSPACE(.STK[.SP]<LEFT>,.STK[.SP]); !Save the arglist space
!		IDPTR =  .FNEXPR;

	END	!Is function
	ELSE
	BEGIN
		% ARRAY NAME%
%1456%		REFSET(ARRAYNM1, .IDPTR);	!RECORD THE REFERENCE
		IDPTR =  ARRXPN(.IDPTR,.STK[.SP]);

	! [1244] Have seen IDENTIFIER ( SUBSCRIPTS )
	! Check for IDENTIFIER ( SUBSCRIPTS ) ( LOWER : UPPER )

		IF .LEXL<LEFT> EQL LPAREN
		THEN
		BEGIN	! [1244] SUBSTRING OF ARRAYREF
			SP = .LSP;		! RESET SP TO REUSE SPACE
			LEXL = LEXEMEGEN(); 	! READ LEFT PAREN
			IF .LEXL<LEFT> EQL COLON
			THEN STK[SP = .SP+1] = 0  ! NULL EXPRESSION
			ELSE IF (STK[SP = .SP+1] = LOGEXPRESSION()) LSS 0
			     THEN RETURN .VREG;	! READ LOWER BOUND EXPRESSION
			IF .LEXL<LEFT> NEQ COLON THEN ERR0L(.LEXNAM[COLON]);
			LEXL = LEXEMEGEN();	! READ COLON
			IF .LEXL<LEFT> EQL RPAREN
			THEN STK[SP = .SP+1] = 0  ! NULL EXPRESSION
			ELSE IF (STK[SP = .SP+1] = LOGEXPRESSION()) LSS 0
			     THEN RETURN .VREG;	! READ UPPER BOUND EXPRESSION
			IF .LEXL<LEFT> NEQ RPAREN THEN ERR0L(.LEXNAM[RPAREN]);
%1470%			LEXL = LEXOPGEN();	! READ RIGHT PAREN
			SP = .LSP;		! RESET SP FOR MAKESUBSTR
			RETURN MAKESUBSTR(.IDPTR); ! BUILD SUBSTRING NODE
		END;	! [1244] SUBSTRING OF ARRAYREF
	END;
	SP = .LSP;	!RESTORING STK PTR TO ORIGINAL TO AVOID RECURSION PROBLEMS
   END
ELSE
	!CHECK USE OF NAME WITHOUT SUBSCRIPTS OR ARGS
   IF .IDPTR[PARENLSTFLG]
     THEN
	BEGIN !ARRAYNAME OR FUNCTION NAME W/O ARGS OR SUBSCRIPTS

		IF NOT .FLGREG<FELFLG>
%[626]%		OR OPER(.LEXL<LEFT>)	!CAN'T BE EMBEDDED IN AN EXPRESSION
		THEN !ERRONEOUS USE OF IDENTIFIER
		   BEGIN
			RETURN NAMREF(VARIABL1, .IDPTR)	! THIS WILL PRODUCE ERROR MESSAGE
		   END
		   ELSE	IF  .IDPTR[OPRSP1]  EQL  FNNAME1
			THEN	
			BEGIN

				! Get dotted name if this is a library function
%1476%				IF NOT .IDPTR[IDATTRIBUT(USERFUNCTION)]
				THEN
%1004%					IF ( T1 = SRCHLIB( .IDPTR) )  NEQ  -1
					THEN
					BEGIN
%1513%					     EXTERNAL LIBFUNTAB, MAKDOTTEDNAME;
					     ! Get offset into table
%1004%					     T1 = .T1 - LIBFUNTAB<0,0>;
%1513%					     ! Get dotted name of function
%2557%					     IF NOT .LIBATTRIBUTES[.T1, ATTSPGEN]
%2557%					     THEN IDPTR = MAKDOTTEDNAME(.T1,.IDPTR)
					END
%1523%					ELSE
%1523%					BEGIN
%1523%					     ! At this point we have the name
%1523%					     ! of a user routine that is being
%1523%					     ! passed as an argument.  Set the
%1523%					     ! bits needed to make the EXTERNAL
%1523%					     ! statement optional.
%1523%					     IDPTR[IDATTRIBUT(INEXTERN)] = 1;
%1523%					     IDPTR[IDATTRIBUT(USERFUNCTION)]=1;
%1523%					END;

				NAMREF(FNNAME1, .IDPTR)
			END
%1456%			ELSE	REFSET(ARRAYNM1, .IDPTR)
	  END
%1456%	  ELSE	REFSET( VARIABL1, .IDPTR );	!RECORD REFERENCE

RETURN .IDPTR	!RETURN HERE ONLY
END;	!OF REFERENCE
ROUTINE MAKEFN(IDPTR)=
BEGIN			
	! Created [1425] - Code was previously in REFERENCE.
	! This routine was created to simplify REFERENCE.

	! Builds a node for function reference.
	! This function returns address of the function node created.

MACRO ERR47(X)= RETURN FATLEX( X, E47<0,0> ) $;

%1422%	BIND GENLEN = 1;	! Generate both byte pointer and length for 
				! a  .Dnnnn compile-time-constant character
				! descriptor
LOCAL
	ARGUMENTLIST ARGLIST,	!Argument list
	BASE ARGPT,	! Node from argument pointer
	BASE FNEXPR,	
%1422%	NUMARGS,	! Actual number of arguments for a function
%1422%	BASE DVAR;	! Pointer to a  .Dnnnn compile-time-constant
%1422%			! character descriptor for the result of the
%1422%			! character function

	MAP BASE IDPTR;
	LABEL LIBCHK;
	REGISTER R2;	! For ptr to function arg list


%1531%	! A function name can't be SAVE-d.
%1531%	IF .IDPTR[IDSAVVARIABLE]
%1531%	THEN	FATLERR(.IDPTR[IDSYMBOL],UPLIT ASCIZ'Function name',
%1531%		.ISN,E192<0,0>);

	FLGREG<BTTMSTFL> = 0;  ! Turn off bottomost routine flag

	! Check for recursive statement function
%4516%	IF .IDPTR EQL .ASTATFUN  THEN ERR47(.IDPTR[IDSYMBOL]);

%1400%	IF .STK[.SP] EQL -1			! Number of argments
%1422%	THEN NUMARGS = 0			! No arguments
%1422%	ELSE NUMARGS = .STK[.SP]<LEFT>+1;	! Get number from stack

%1422%	NAME<LEFT> = ARGLSTSIZE(.NUMARGS);
%1422%	ARGLIST = R2 = CORMAN(); ! Core for function argument list

%1422%	! Now move the argument list from STK to ARGLIST

	NAME = EXPTAB;
	ENTRY[0] = .IDPTR;
	ENTRY[1] = .R2;
	FNEXPR = NEWENTRY();	!Make an expression node for FNCALL
	FNEXPR[VALTYPE] = .IDPTR[VALTYPE];
	FNEXPR[OPRCLS] = FNCALL;
%2532%	IDPTR[IDFUNCTION] = 1;		!Mark; this name is used in a
%2532%					! function call
%1413%	ARGLIST[ARGPARENT] = .FNEXPR;	!Pointer to parent node
%1422%	ARGLIST[ARGCOUNT] = .NUMARGS;	!Number of arguments

%1422%	IF .NUMARGS NEQ 0
%1400%	THEN
%1400%	BEGIN	! Move argument list from STK to  ARGLIST

		! Note that this might not be the final arg list.  If  this
		! is a character  function it will  have it's return  value
		! inserted into  the  first argument  by  CHARLIST,  called
		! below.

		DECR I FROM .STK[.SP]<LEFT> TO 0 DO
		BEGIN	! Copy arguments from STK

%1422%			ARGLIST[.I + 1, ARGFULL]  =  ARGPT =  @(.STK[.SP])[.I];

			! If AVALFLG is not set, init parent pointer

			IF NOT .ARGLIST[.I + 1, AVALFLG]
			THEN ARGPT[PARENT] = .FNEXPR;
		END;	! Copy arguments from STK

%1400%	END;	! Move arg list


%1413%	! Flag to indicate we need type checking blocks

%1413%	! We don't  want  arg  checking  if  the  function  is  a  dummy
%1413%	! argument.   LINK  must  know  the  name  of  the  function  at
%1413%	! LINK-time to do any fixup or error checking.

%1413%	IF NOT .IDPTR[IDATTRIBUT(DUMMY)]
%1413%	THEN	ARGLIST[ARGCHBLOCK] = 1;


!	Now if function call is to library routine, call special processing
!	routine	in module GNRCFN.
!
	LIBCHK:BEGIN

		! If  not  declared  external,  or  dummy  argument,  or
		! statement function name, then check to see if it is an
		! intrinsic function.
%1476%		IF  NOT .IDPTR[IDATTRIBUT(USERFUNCTION)]
			AND NOT .IDPTR[IDATTRIBUT(DUMMY)]
%1752%			AND NOT .IDPTR[IDATTRIBUT(SFN)]
		THEN
		BEGIN
			LOCAL LIBPTR;
			IF (LIBPTR = SRCHLIB(.IDPTR)) NEQ -1
			THEN 
			BEGIN	! Found it - make the lib call node.

%1270%				MAKLIBFN(.LIBPTR,.FNEXPR,.IDPTR);

%1413%				! Undo flag - don't want arg checking
%1413%				! for library calls.

%1413%				ARGLIST[ARGCHBLOCK] = 0;
				LEAVE LIBCHK;
			END;
		END;

		FNEXPR[OPERSP] = NONLIBARY;

		! Note possible  "set"  for non-library functions

%1400%		IF .STK[.SP] NEQ -1 THEN
		DECR I FROM .NUMARGS TO 1 DO
		BEGIN
%1422%			ARGPT =  .ARGLIST[ .I, ARGFULL];
			IF .ARGPT[OPRCLS] EQL DATAOPR
			THEN
			BEGIN
				IF .ARGPT[OPRSP1] EQL ARRAYNM1
				OR .ARGPT[OPRSP1] EQL VARIABL1
%2557%				THEN	NAMSET(VARYREF, .ARGPT);

%2557%				! If the argument is a function name,
%2557%				! call INTRCHK to check that it has
%2557%				! been declared INTRINSIC and is a
%2557%				! specific function name.
%2557%				IF .ARGPT[OPERSP] EQL FNNAME
%2557%				THEN INTRCHK(.ARGPT);

			END
			ELSE	IF .ARGPT[OPRCLS] EQL  ARRAYREF
				THEN	NAMSET( ARRAYNM1, .ARGPT[ARG1PTR]);
		END;
	END;	%LIBCHK%

%1434%	IDPTR = .FNEXPR[ARG1PTR];	! Fetch the symbol table entry for the
%1434%					! function call - it may have changed

%1422%	IF .IDPTR[VALTYPE] EQL CHARACTER
%1422%	THEN
%1422%	BEGIN	! Character function

%1422%		! Increment the  number of  arguments since  character
%1422%		! functions  have  an   extra  argument.   The   first
%1422%		! argument is the descriptor  for the result.  Make  a
%1422%		! .Dnnnn variable entry for the  compile-time-constant
%1422%		! character descriptor.  Fill in  the length field  of
%1422%		! the result from the function definition.

%1434%		! Give and error if the function reference has a result
%1434%		! length of *.

%1434%		IF .IDPTR[IDCHLEN] EQL LENSTAR THEN RETURN FATLEX(.IDPTR[IDSYMBOL], E180<0,0>);

%1434%		! First make the new argument list

%1434%		FNEXPR[ARG2PTR] = ARGLIST = CHARGLIST(.ARGLIST);

%1422%		! Fill in the first argument.  It is the .Dnnnn compile-time-
%1422%		! constant  character  descriptor  used for the result of the
%1422%		! function.

%1422%		DVAR = NEWDVAR(GENLEN);		! Generate the .Dnnnn variable
%1422%		DVAR[IDCHLEN] = .IDPTR[IDCHLEN];	! Fill in the length
%1422%		ARGLIST[1,ARGFULL] = .DVAR;	! Fill in the first argument
%1422%		ARGLIST[1,AVALFLG] = 1;		! Dataopr flag

%1422%	END;	! Character function

%1466%	! We now  have  the  arguments in  the  arg  list.  If  we  have  a
%1466%	! statement function, then do it now.

%1466%	IF .IDPTR[IDATTRIBUT(SFN)]
%1466%	THEN
%1466%	BEGIN
%1466%		ARGSFCHECK(.ARGLIST);		! Do arg checking now.
%1466%		ARGLIST[ARGCHBLOCK] = 0;	! Un-mark for LINK arg checking
%1466%	END;

	NAMREF(FNNAME1, .FNEXPR[ARG1PTR]) ;	!Record the reference

%1425%	RETURN  .FNEXPR;	!Address of function node

END;	!of MAKEFN
GLOBAL ROUTINE PRIMITIVE=
BEGIN

!	This routine parses a primitive of an expression (if LISTOK is 0)
!	or possibly an expression list (if LISTOK is -1).  The primitives
!	are:

!		[$ OR * OR &] label
!		[+,-] constant or literal
![1244]		[+,-] REFERENCE  - (ARRAY or FUNCTION or SUBSTRING)
!		A**B
![1244]		A // B // ... // Z
!		(constant,constant)  - a complex constant
!		(LOGEXPRESSION)  - a parenthesized expression
!
!	and leaves the next lexeme available when finished.

!	If this routine is entered with LISTOK set to -1, then this
!	routine is also willing to handle an expression list (I/O list)
!	including the parentheses.  Observe that the routine is fully recursive.

!	In the event that an expression is parsed, the value returned is a
!	pointer to the expression.  If an I/O list is found, a pointer to
!	the list is put on STK, and zero is returned to indicate this fact.
!	As usual, a -1 is returned on a wide variety of error conditions.

LOCAL BASE NEGATNODE,UNARYSIGN;
LOCAL BASE REALPART:IMAGPART;
LOCAL BASE SLISTOK;

LABEL PRIM1;
NEGATNODE = UNARYSIGN = 0;
SLISTOK = .LISTOK;	! Save incoming value for later

WHILE 1 DO
BEGIN	! Scan until no leading '+' or '-'

	IF .LEXL<LEFT> NEQ PLUS THEN
		IF .LEXL<LEFT> NEQ MINUS THEN EXITLOOP
			ELSE NEGATNODE = NOT .NEGATNODE;

	! We saw either a '+' or a '-'

%1244%	UNARYSIGN = -1;		! Remember we saw a sign
	LEXL = LEXEMEGEN();	! Get next lexeme
	FLGREG<FELFLG> = 0;	! CALL FOO(+ARRAY) is illegal
	LISTOK = 0;		! +(list) is illegal too

END; ! of +/- loop

PRIM1:

	IF .LEXL<LEFT> EQL LPAREN THEN

	! We have either:
	!  1. Parenthesized expression
	!  2. Complex constant
	!  3. Possible expression list (if LISTOK set)

	BEGIN
		LOCAL LSP;
		LSP = .SP;
		LEXL = LEXEMEGEN();
		IF .LISTOK THEN FLGREG<FELFLG> = 1; ! Bare array reference OK
		IF(REALPART = LOGEXPRESSION()) LSS 0 THEN RETURN -1; ! Pass failure through

		IF .LEXL<LEFT> EQL RPAREN THEN

		! We have " ( LOGEXPRESSION ) " - may be either:
		!  1. Parenthesized expression
		!  2. List of 1 element (if (A) with A an array...)

		BEGIN
%1470%			LEXL = LEXOPGEN();   ! Get another lexeme in any case
			IF .REALPART[OPRCLS] EQL DATAOPR AND
			(.REALPART[OPERSP] EQL ARRAYNAME OR
			 .REALPART[OPERSP] EQL FORMLARRAY)
			THEN	! We have got an array with no subscripts
			BEGIN
				STK[SP = .SP+1] = 1;	! ARRAY NAME ELEMENT
				STK[SP = .SP+1] = .REALPART; ! ARRAY PTR
				COPYLIST(.LSP);
				STK[SP = .SP+1] = 0;	! NO LOOP VARIABLE
				COPYLIST(.LSP);
				STK[.SP+1] = .STK[.SP];	! PTR TO "LIST"
				STK[.SP] = 2;		! A LIST
				SP = .SP+1;
				RETURN 0
			END
			ELSE
			BEGIN	! Must be a parenthesized expression
				IF .REALPART[OPRCLS] NEQ DATAOPR
					THEN REALPART[PARENFLG] = 1;
				LEAVE PRIM1;
			END
		END; ! of RPAREN processing

		IF .LEXL<LEFT> NEQ COMMA THEN ERR0(PLIT 'comma or right parenthesis?0');

		! We now have " ( LOGEXPRESSION , "   -- Try for another expression...

		IF .LISTOK THEN FLGREG<FELFLG> = 1; ! Bare array reference OK
		LEXL = LEXEMEGEN();
		IF(IMAGPART = LOGEXPRESSION()) LSS 0 THEN RETURN -1; ! Pass error through

		! We now have " ( LOGEXPRESSION , LOGEXPRESSION " -- Try for complex constant...

		IF .LEXL<LEFT> EQL RPAREN THEN
		BEGIN
			LOCAL CC;
			IF (CC = CCONST(.REALPART,.IMAGPART)) NEQ 0
			THEN	! It was a complex constant 
			BEGIN
				REALPART = .CC;
%1470%				LEXL = LEXOPGEN();
				LEAVE PRIM1
			END
		END; ! of RPAREN processing

		! If we get here, it is either a list or illegal

		IF NOT .LISTOK OR .NEGATNODE NEQ 0
		THEN RETURN FATLEX(PLIT 'expression?0',PLIT 'list?0',E0<0,0>);

		! A list is legal in this context, and we have one!

		RETURN BEXPRLIST(.REALPART,.IMAGPART); ! Process rest of I/O list
	END	! of LPAREN processing

	ELSE	! Not a parenthesized expression
	BEGIN
		IF .LEXL<LEFT>  EQL  CONSTLEX  OR  .LEXL<LEFT> EQL LITSTRING
%1470%		THEN (REALPART = .LEXL<RIGHT>; LEXL = LEXOPGEN())
		ELSE
			IF ( REALPART = REFERENCE()) LSS 0 THEN RETURN .VREG; ! Variable or function reference
		 ! REFERENCE returns with next lexeme in LEXL
	END;  !OF PRIM1:

!
!	Here now to check for ** operator (exponentiation).
!	REALPART should contain either:
!		1. Pointer to constant node
!		2. Pointer to expression node
!		3. Pointer to variable or function reference
!
	IF .LEXL<LEFT> EQL POWER
	THEN
	BEGIN ! Make an exponent node
		LOCAL BASE EXPON;REGISTER BASE T1;

%[626]%		FLGREG<FELFLG> = 0;	! CALL FOO(3**ARRAY) illegal
		LISTOK = 0;		! list is now illegal too
		NAME = EXPTAB; EXPON = NEWENTRY();
		EXPON[OPRCLS] = ARITHMETIC; EXPON[OPERSP] = EXPONOP;
		EXPON[ARG1PTR] = .REALPART; !BASE
		!NOW CHECK FOR SONS BEING DATAOPR OR NOT AND SET PARENT POINTERS APPROPRIATELY
		LEXL = LEXEMEGEN();
		IF (REALPART  = PRIMITIVE()) LSS 0 THEN RETURN -1; !RECURSE TO GET A**B**C = A**(B**C)
		EXPON[ARG2PTR] = .REALPART;
		EXPRTYPER(.EXPON);	!CHECK FOR TYPE CONVERSIONS
		REALPART = .EXPON[ARG1PTR];	!CHECK SONS NOW
		IF .REALPART[OPRCLS] EQL DATAOPR
			THEN EXPON[A1VALFLG] = 1
			ELSE ( REALPART[PARENT] = .EXPON;
				IF .REALPART[FNCALLSFLG] THEN EXPON[FNCALLSFLG] = 1;
			     );
		REALPART = .EXPON[ARG2PTR];	!CHECK SON AGAIN
		IF .REALPART[OPRCLS] EQL DATAOPR
			THEN EXPON[A2VALFLG] = 1
			ELSE ( REALPART[PARENT] = .EXPON;
				IF .REALPART[FNCALLSFLG] THEN EXPON[FNCALLSFLG] = 1;
			     );
		REALPART = .EXPON;
	END;

	![1244] Check for  X // Y // ... // Z

	IF .LEXL<LEFT> EQL CONCAT
	THEN
	BEGIN	! [1244] CONCATENATION

	! Here with REALPART = a REFERENCE (DATAOPR or ARRAYREF or SUBSTRING or FNCALL)
	! 		    or a complex constant
	! 		    or a parenthesized expression
	!		    or anything above preceded by + or -
	! Check if it's followed by //, the concatenation operator.  If so,
	! read a sequence of character primaries followed by //.  A character
	! primary is a REFERENCE or a parenthesized expression.  Quit when
	! a primary is followed by anything but //.

		LOCAL LSP;
		REGISTER BASE CONCNODE;

		EXTERNAL E90,E163;

		structure pbase [i,j,k,l] =
		   (.pbase + .j)<.k,.l>;


%1436%		CHARUSED = TRUE;	! Flag for character operator used
					! in program

		! CONCATENATION EXPRESSIONS CAN'T HAVE UNARY SIGN OPERATORS
		IF .UNARYSIGN THEN RETURN FATLEX(E206<0,0>);
					! "Illegal operator for char data"
		FLGREG<FELFLG> = 0;  LISTOK = 0; ! BARE ARRAY NAME NOW ILLEGAL

		NAME = EXPTAB;    CONCNODE = NEWENTRY(); ! MAKE EXPR NODE
		CONCNODE[VALTYPE] = CHARACTER;
		CONCNODE[OPRCLS] = CONCATENATION;
		CONCNODE[OPERSP] = CONCTV;

		LSP = .SP;			! SAVE SP FOR RECURSIVE CALLS

		! CHECK FIRST OPERAND TYPE, MUST BE CHARACTER
%1620%		IF .REALPART[VALTYPE] NEQ CHARACTER THEN FATLEX(E207<0,0>);
					! "Numeric operand of concatenation"

		! SET PARENT POINTER OF EXPRESSION, SET VALFLG IF NOT EXPRESSION
		IF .REALPART[OPRCLS] EQL DATAOPR
		THEN (MAP PBASE REALPART; REALPART[P1AVALFLG] = 1)
		ELSE REALPART[PARENT] = .CONCNODE;

		! A concatenation node looks  like a FNCALL node.   Make
		! an argument list to  hang off the concatenation  node.
		! Leave an  extral  zero  word in  place  of  the  first
		! argument;  it  will  be  used  for  the  concatenation
		! result.  Zero the header words using ARGHDRSIZ.

%1530%		DECR I FROM ARGHDRSIZ TO 1 DO STK[SP = .SP+1] = 0;

		! First argument is the result - filled in later

		STK[SP = .SP+1] = 0;

		! Second argument is the first operand to concat

		STK[SP = .SP+1] = .REALPART;

		DO
		BEGIN	! WHILE //
			IF .SP GEQ STKSIZ-1 THEN RETURN FATLEX(E90<0,0>);
					! "Expression too complex to compile"

			LEXL = LEXEMEGEN();		! READ THE //

			! Read character primary:
			!   a character constant
			!   a character variable, array element, substring
			!   a parenthesized character expression
			! We want to parse all the // operands in this loop,
			! so can't just call PRIMITIVE to pick up whatever
			! follows, as PRIMITIVE would pick up a whole //
			! expression.  So call PRIMITIVE only for parenthesized
			! expressions, otherwise just call reference

			IF .LEXL<LEFT> EQL LPAREN
			THEN REALPART = PRIMITIVE()
			ELSE IF .LEXL<LEFT> EQL LITSTRING
%1470%			     THEN (REALPART = .LEXL<RIGHT>; LEXL = LEXOPGEN())
			     ELSE REALPART = REFERENCE();

			IF .REALPART LSS 0 THEN RETURN .VREG; ! IF ERROR, PASS IT ON

			! MUST BE TYPE CHARACTER
			IF .REALPART[VALTYPE] NEQ CHARACTER
%1620%			THEN FATLEX(E207<0,0>);	
					! "Numeric operand of concatenation"

			! SET PARENT POINTER OF EXPRESSION, SET VALFLG IF NOT EXPRESSION
			IF .REALPART[OPRCLS] EQL DATAOPR  ! SET VALFLG OR PARENT POINTER
			THEN (MAP PBASE REALPART; REALPART[P1AVALFLG] = 1)
			ELSE REALPART[PARENT] = .CONCNODE;

			STK[SP = .SP+1] = .REALPART; 	  ! SAVE ARG ON STK

		END	! WHILE //
		UNTIL .LEXL<LEFT> NEQ CONCAT;

%1413%		STK[.LSP+2] = .SP - .LSP - ARGHDRSIZ; ! Set argument count
		COPYLIST(.LSP);		! COPY ARG LIST INTO LOCAL STORAGE
		CONCNODE[ARG2PTR] = .STK[.SP]; ! SET POINTER TO ARG LIST

		SP = .LSP;		! RESTORE STK POINTER

		REALPART = .CONCNODE;	! DONE
	END;	! [1244] CONCATENATION

	LISTOK = .SLISTOK;	! Safe to restore original value of LISTOK now

	! One final case - did we have a "-" originally?

	IF .NEGATNODE EQL 0 THEN RETURN .REALPART; ! Done

	IF .REALPART[OPRCLS]EQL DATAOPR AND .REALPART[OPERSP] EQL CONSTANT
		THEN RETURN NEGCNST(REALPART); ! No need to create NEGNOT node.

	! The hard case - we absolutely need to create a NEGNOT node...

	NAME = EXPTAB;
	NEGATNODE = NEWENTRY();
	NEGATNODE[OPRCLS] = NEGNOT;
	NEGATNODE[OPERSP] = NEGOP;
	NEGATNODE[ARG2PTR] = .REALPART;
	NEGATNODE[ARG1PTR] = 0;
	IF .REALPART[OPRCLS] EQL DATAOPR THEN NEGATNODE[A2VALFLG] = 1
		ELSE (REALPART[PARENT] = .NEGATNODE;
			IF .REALPART[FNCALLSFLG] THEN NEGATNODE[FNCALLSFLG] = 1
		     );
	NEGATNODE[VALTYPE] = (IF .REALPART[VALTYPE] EQL CONTROL THEN LOGICAL ELSE .REALPART[VALTYPE]);
	RETURN .NEGATNODE

END; !0F ROUTINE PRIMITIVE
GLOBAL ROUTINE LEXOPGEN=		! [1470] New

! This routine is used instead of LEXEMEGEN for reading an operator lexeme.
! It is the same as LEXEMEGEN except that it will return TICLEX if it sees a
! tic (') coming up.  This is so that EXPRESSION can read the unit specifier
! in IO statements, which can be delimited by tic.  LEXOPGEN returns TICLEX if
! the next lexeme starts with ', otherwise it returns the next lexeme.  This is
! the only time that TICLEX will be seen.

BEGIN
	LOOK4CHAR = "'";		! Look for tic
	IF LEXICAL(.GSTCSCAN) NEQ 0	! See if tic coming up
	THEN RETURN TICLEX^18		! If so, return TIC lexeme
	ELSE RETURN LEXICAL(.GSTLEXEME); ! Else return normal lexeme

END;  ! LEXOPGEN
GLOBAL ROUTINE BEXPRLIST(PTR1,PTR2)=
BEGIN

!	Called only from PRIMITIVE when we have parsed:
!
!	( LOGEXPRESSION, LOGEXPRESSION
!
!	and for some reason we did not have a complex constant.
!	PTR1 and PTR2 are pointers to the two LOGEXPRESSIONs already seen;
!	if zero, a sublist was seen (for which a ptr is on the STK).
!	This routine picks up the rest of the list (if any), and also
!	handles any DO loop parameters which might be present.
!	If successful, a list pointer is put onto the stack, and zero
!	is returned to indicate this fact.
!	This routine is all new for edit 1203.

	MAP BASE PTR1:PTR2;
	LOCAL LSP;

	! Adjust the STK so that both of the LOGEXPRESSIONs are present.
	! Observe that PTR1 and/or PTR2 may be zero in which case STK already
	! contains pointers to sublists (which may need to be moved).

	IF .PTR1 EQL 0 THEN SP = .SP-2;
	IF .PTR2 EQL 0 THEN SP = .SP-2;
	LSP = .SP;

	IF .PTR2 EQL 0 AND .PTR1 NEQ 0
	THEN
	BEGIN
		STK[.SP+3] = .STK[.SP+1];
		STK[.SP+4] = .STK[.SP+2]
	END;

	IF .PTR1 NEQ 0 THEN (STK[.SP+1] = 1; STK[.SP+2] = .PTR1);
	IF .PTR2 NEQ 0 THEN (STK[.SP+3] = 1; STK[.SP+4] = .PTR2);
	SP = .SP+4;

	! We are done with fixing up STK to contain PTR1 and PTR2.
	! Now process the rest of the list elements (if any), adjusting
	! STK accordingly.

	WHILE .LEXL<LEFT> EQL COMMA DO
	BEGIN
		FLGREG<FELFLG> = 1; ! Bare array references are allowed
		LEXL = LEXEMEGEN();
		IF(PTR1 = LOGEXPRESSION()) LSS 0 THEN RETURN -1;
		IF .PTR1 NEQ 0 THEN (STK[SP = .SP+1] = 1; STK[SP = .SP+1] = .PTR1)
	END;


	! The list of I/O elements/sublists is done

	COPYLIST(.LSP);		! Get ptr to list of elements (and loop index)

	! Try for either a right parenthesis or possibly loop parameters.

	IF .LEXL<LEFT> EQL RPAREN THEN
		STK[SP = .SP+1] = 0	! No loop variable, just a list
	ELSE IF .LEXL<LEFT> EQL EQUAL THEN ! We have a loop
	BEGIN
		STK[SP = .SP+1] = 1;
		LEXL = LEXEMEGEN();
		LISTOK = 0;	! No loops allowed in here
		FLGREG<FELFLG> = 0; ! Bare array references are not allowed

		! Get the loop parameters - first the lower bound

		IF (STK[SP = .SP+1] = LOGEXPRESSION()) LSS 0
			THEN RETURN -1;
		IF .LEXL<LEFT> NEQ COMMA THEN ERR0(.LEXNAM[COMMA]);
		LEXL = LEXEMEGEN();

		! Next the upper bound

		IF (STK[SP = .SP+1] = LOGEXPRESSION()) LSS 0
			THEN RETURN -1;

		IF .LEXL<LEFT> EQL COMMA THEN ! We have an increment too
		BEGIN
			LEXL = LEXEMEGEN();
			IF (STK[SP = .SP+1] = LOGEXPRESSION()) LSS 0
				THEN RETURN -1;
		END
		ELSE STK[SP = .SP+1] = 0;	! No increment

		IF .LEXL<LEFT> NEQ RPAREN
			THEN ERR0(.LEXNAM[RPAREN]);

		! We have all the DO loop parameters now, so it is time
		! to put all the pieces together from the pointers on STK

		COPYLIST(.LSP+2);	! Do loop elements
		LISTOK = -1;		! Restore list legality
	END ! Of .LEXL<LEXL> EQL EQUAL
	ELSE ERR0(.LEXNAM[RPAREN]);

	! Finish up with the STK elements

	COPYLIST(.LSP);
	STK[.SP+1] = .STK[.SP];	! Ptr to "list"
	STK[.SP] = 2;		! A list
	SP = .SP+1;
	LEXL = LEXEMEGEN();	! Always get the next lexeme
	RETURN 0

END; !OF ROUTINE BEXPRLIST
GLOBAL ROUTINE ARGSFCHECK(CALLLIST)=	![1466] New
BEGIN 
	! Performs argument checking  for statement functions  for the  arg
	! list passed to it.

	MAP ARGUMENTLIST CALLLIST;	! Passed: caller's arg list

	LOCAL
%2517%		CALLSTRUCTURE,		! Caller's argument's structure
%2517%		CHARFNOFFSET,		! Offset (or 0) for where character
%2517%					! fn's arguments start
		BASE CNODE,		! Scratch expression node
%2517%		LENCALL,		! Length of caller's argument
%2517%		LENSF,			! Length of sf's argument
		MAXNUM,			! Max number of args to check
		ARGUMENTLIST SFLIST,	! SF's arg list
		BASE SFNODE;		! SF statement node

	REGISTER
		BASE CALLARG,		! Callers's arg node
		BASE SFARG,		! SF's arg node
		BASE SYMTAB;		! Symbol table entry for SF name


	! Table accessed by  LINK's type  codes (table EVALU)  to give  the
	! action based  on actual  and  formal argument  values  1=complain
	! 0=legal.

	STRUCTURE ACTSTR[ACTUAL,FORMAL]=
		(.ACTSTR[.ACTUAL])<.FORMAL,1>;

	MACRO ACT(L1,I2,R4,O6,L7,D10,D12,G13,C14,C15,H17)=
		L1^1 OR I2^2 OR R4^4 OR O6^6 OR L7^7 OR D10^8
		OR D12^10 OR G13^11 OR C14^12 OR C15^13 OR H17^15$;

	BIND ACTSTR WARN =
	UPLIT(	
		!	**FORMALS**

		!	L  I  R  O  L  D  D  G  C  C  H    **ACTUALS**
		!	o  n  l  c  a  b  O  f  m  h  o
		!	g  t     t  b  l  c  l  p  a  l
		0,
%1613%		ACT(	0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),  ! Logical
%1613%		ACT(	1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1),  ! Integer
		0,
%1613%		ACT(	1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1),  ! Real
		0,
%1613%		ACT(	0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1),  ! Octal
%1613%		ACT(	1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1),  ! Label
%1613%		ACT(	1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1),  ! Dble Prec
		0,
%1613%		ACT(	1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1),  ! Dble Octal
%1613%		ACT(	1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1),  ! G-floating
%1613%		ACT(	1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1),  ! Complex
%1613%		ACT(	1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1),  ! Character
		0,
%1613%		ACT(	0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1),  ! Hollerith
		0
		!	L  I  R  O  L  D  D  G  C  C  H
		!	o  n  l  c  a  b  O  f  m  h  o
		!	g  t     t  b  l  c  l  p  a  l
	);


	CNODE = .CALLLIST[ARGPARENT];	! Calling node for SF
	SYMTAB = .CNODE[ARG1PTR];	! Symbol table entry
	SFNODE = .SYMTAB[IDSFNODE];	! Statement node for SF
	SFLIST = .SFNODE[SFNLIST];	! Arg list for SF

	! Inform user if calling statement  function with the wrong  number
	! of arguments.

%1613%	IF .FLGREG<DBGARGMNTS>	! /DEBUG:ARGUMENTS specified
	THEN
	BEGIN
		IF .CALLLIST[ARGCOUNT] NEQ .SFLIST[ARGCOUNT]
		THEN	FATLERR(.SYMTAB[IDSYMBOL],.ISN,
			E185<0,0>);
	END;


	! Loop through the arguments to  compare them.  In case the  number
	! of arguments isn't the same, take the smaller of the two for  the
	! upper bound.

	MAXNUM = .CALLLIST[ARGCOUNT];
	IF .CALLLIST[ARGCOUNT] GTR .SFLIST[ARGCOUNT]
	THEN	MAXNUM = .SFLIST[ARGCOUNT];

%2517%	! Set if there is an offset for character functions.  We start at
%2517%	! argument 2 if so, because argument one is the function's return
%2517%	! value.  No need to check that...
%2517%
%2517%	IF .SYMTAB[VALTYPE] EQL CHARACTER	! Character function?
%2517%	THEN CHARFNOFFSET = 1
%2517%	ELSE CHARFNOFFSET = 0;

	! Perform arg checking for each argument.

%2517%	INCR CNT FROM (1 + .CHARFNOFFSET) TO .MAXNUM
	DO
	BEGIN	! Each argument

		CALLARG = .CALLLIST[.CNT,ARGNPTR];
		SFARG = .SFLIST[.CNT,ARGNPTR];

		! Check if character constant  argument is being passed  to
		! numeric.  If so then do a  fixup to make this constant  a
		! hollerith.

		IF .CALLARG[OPERATOR] EQL CHARCONST THEN
		IF .SFARG[VALTYPE] NEQ CHARACTER
		THEN	CALLARG[VALTYPE] = HOLLERITH;


		! Do more checking if /DEBUG:ARGUMENTS is specified.

%1613%		IF .FLGREG<DBGARGMNTS>
		THEN
		BEGIN	! /DEBUG:ARGUMENTS specified

			! Inform user of any improper passing of actuals to
			! dummy aguments

			IF (.WARN[ .EVALU[.CALLARG[VALTYPE]],
				   .EVALU[.SFARG[VALTYPE]] ])
			THEN	FATLERR (.SYMTAB[IDSYMBOL],.CNT,
%2517%					.ISN - .CHARFNOFFSET,E186<0,0>)
%2517%			ELSE
%2517%			BEGIN	! No type mismatch
%2517%
%2517%				! If character arguments (both are the same
%2517%				! if we're here), then check the length of
%2517%				! of the arguments.  It's illegal for a
%2517%				! dummy variable of the sf to be longer
%2517%				! than the argument that is passed to it,
%2517%				! it could reference something that isn't
%2517%				! allocated or isn't character.
%2517%
%2517%				IF .CALLARG[VALTYPE] EQL CHARACTER
%2517%				THEN
%2517%				BEGIN	! Both arguments are character
%2517%
%2517%					! Get length of the arugments.  If
%2517%					! they aren't known at compile time
%2517%					! (length *), 0 is returned.
%2517%
%2517%					LENCALL = CHEXLEN(.CALLARG);
%2517%					LENSF = CHEXLEN(.SFARG);
%2517%
%2517%					! Check if len(caller) < len(sf).
%2517%					! Don't if either is unknown
%2517%					! (length *).
%2517%
%2517%					IF (.LENCALL NEQ LENSTAR)  AND
%2517%						(.LENSF NEQ LENSTAR)
%2517%					THEN IF .LENCALL LSS .LENSF
%2517%					THEN FATLERR (.SYMTAB[IDSYMBOL],
%2517%						.CNT - .CHARFNOFFSET, .ISN,
%2517%						E318<0,0>);	! Warning!
%2517%
%2517%				END;	! Both arguments are character
%2517%
%2517%			END;	! No type mismatch

%2517%			! Check structure of passed arguments.
%2517%			!
%2517%			! The basic structures are:
%2517%			! 
%2517%			! 	o Singleton (one unit of data)
%2517%			! 	o Bare array name (multiple units of data)
%2517%			! 	o Function name.
%2517%			!
%2517%			! We'll call these VARIABL1, ARRAYNM1, and FNNAME1
%2517%			! respectively (names are from the DATOPS1 field in
%2517%			! DATAOPR's).
%2517%			! 
%2517%			! Set CALLSTRUCTURE to the structure types for each
%2517%			! argument.  DATAOPR's have this info build in to
%2517%			! the node, expressions don't.  If the caller's
%2517%			! argument is a DATAOPR (all the sf's arguments
%2517%			! are), then make "0" and ARRAYNM1 into VARIABL1
%2517%			! (its ok to pass an array to a singleton), and
%2517%			! copy FNNAME1 as is.  If the caller's arguments
%2517%			! aren't DATAOPR's then they are VARIABL1
%2517%			! (singletons).
%2517%			! 
%2517%			! Compare to see if the calculated structures are
%2517%			! equal.  If not, complain.
%2517%	
%2517%			! Calculate/copy the caller's structure.
%2517%
%2517%			CALLSTRUCTURE = VARIABL1;	! Default; singleton
%2517%			IF .CALLARG[OPRCLS] EQL DATAOPR	! DATOPS1 valid?
%2517%			THEN IF .CALLARG[DATOPS1] GTR ARRAYNM1	! Already set?
%2517%			THEN CALLSTRUCTURE = FNNAME1;	! Function
%2517%
%2517%			! If the structures aren't the same, then complain!
%2517%			! The statement function's dummy arguments must be
%2517%			! DATAOPR's, so DATOPS1 is valid.  VARIABL1 and
%2517%			! FNNAME1 are the only values set for the sf
%2517%			! dummies (no array names are possible).
%2517%
%2517%			IF .CALLSTRUCTURE NEQ .SFARG[DATOPS1]
%2517%			THEN FATLERR (.SYMTAB[IDSYMBOL], .CNT - .CHARFNOFFSET,
%2517%				.ISN, E319<0,0>);

		END	! /DEBUG:ARGUMENTS specified
%2517%		ELSE
%2517%		BEGIN	! /DEBUG:ARGUMENTS not specified
%2517%
%2077%			! Check structure of passed arguments.  Its is verboten
%2077%			! to pass a constant when a routine is expected.
%2077%
%2077%			IF (.CALLARG[OPR1] EQL CONSTFL) AND
%2077%				(.SFARG[DATOPS1] EQL FNNAME1)
%2517%			THEN FATLERR (.SYMTAB[IDSYMBOL], .CNT - .CHARFNOFFSET,
%2077%				 .ISN, E319<0,0>);
%2517%
%2517%		END;	! /DEBUG:ARGUMENTS not specified
			
	END;	! Each argument

END;	! of ARGSFCHECK
GLOBAL ROUTINE INTRCHK(FUNC)=	![2557] New

!++
! FUNCTIONAL DESCRIPTION:
!
!	Check the function name specified in the argument list.  
!	If it was in an INTRINSIC statement, then check if it is 
!	a specific function name. Otherwise give an error message.
!
! FORMAL PARAMETERS:
!
!	FUNC	The function symbol table entry to check.
!
! IMPLICIT INPUTS:
!
!	LIBATTRIBUTES	Table of library functions, giving additional
!			information.
!
! IMPLICIT OUTPUTS:
!
!	None
!
! ROUTINE VALUE:
!
!	None
!
! SIDE EFFECTS:
!
!	None
!
!--


BEGIN
	MAP BASE FUNC;
	REGISTER INDX;	! Index into intrinsic function table

	IF .FUNC[IDATTRIBUT(INEXTERN)]			! EXTERNAL or INTRINSIC
	THEN IF NOT .FUNC[IDATTRIBUT(USERFUNCTION)]	! INTRINSIC only
	THEN IF (INDX = SRCHLIB(.FUNC)) NEQ -1		! Find func in table
	THEN IF .LIBATTRIBUTES[.INDX-LIBFUNTAB<0,0>, ATTSPGEN]	! Not specific
	THEN FATLEX(UPLIT ASCIZ 'a specific intrinsic function name',
			.FUNC[IDSYMBOL], E15<0,0>);	! Give error.

END;	! Of INTRCHK

END
ELUDOM