	.TITLE	EXPRESS
	.IDENT	"V1.3"
	.NLIST	BEX
;
;   Author:	D. Mischler	26-MAY-87
;
;   This module contains all code necessary for
;   expression evaluation.
;
	.PAGE
	.SBTTL	Table definitions
	.PSECT	RODATA,D,RO
;
;   Table of character attributes.
;   High bit set = Legal symbol constituent.
;   Bit 6 set	 = Legal digit in one or more radices.
;   Bit 5 set	 = Legal expression terminator.
;   Low 4 bits	 = Digit value.
;
S	=	200
D	=	100
T	=	40
SYMCHR::
	.BYTE	T,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0	; NUL through SI.
	.BYTE	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0	; DLE through US.
	.BYTE	T,0,0,0,S,0,0,0,T,0,0,0,T,0,S,0	; SP through '/'.
	.BYTE	S!D!0,S!D!1,S!D!2,S!D!3,S!D!4	; '0' through '4'.
	.BYTE	S!D!5,S!D!6,S!D!7,S!D!8.,S!D!9.	; '5' through '9'.
	.BYTE	T,T,0,T,0,0,0,S!D!10.,S!D!11.	; ':' through 'B'.
	.BYTE	S!D!12.,S!D!13.,S!D!14.,S!D!15.	; 'C' through 'F'.
	.BYTE	S,S,S,S,S,S,S,S,S,S,S,S,S,S,S,S	; 'G' through 'V'.
	.BYTE	S,S,S,S,0,0,0,0,0		; 'W' through '_'.
	.WORD	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0	; '`' through DEL.

;
;   Binary operator table.
;
BINTBL:
	.WORD	'+,ADDOPS	; Addition.
	.WORD	'-,SUBOPS	; Subtract.
	.WORD	'*,MULOPS	; Multiply.
	.WORD	'/,DIVOPS	; Divide.
	.WORD	'&,ANDOPS	; Logical AND.
	.WORD	'!,OROPS	; Logical inclusive OR.
	.WORD	'>,CLOSE	; Close sub-expression.
BINLEN	=	<.-BINTBL>/4

;
;   Unary operator table.
;
UNATBL:	.WORD	'-,NEGATE	; Negate operand.
	.WORD	'@,DEFER	; Fetch value from address.
	.WORD	'',ASCBYT	; Single ASCII character.
	.WORD	'",ASCWRD	; Double ASCII character.
	.WORD	'%,RAD50	; RAD50 conversion.
	.WORD	'<,OPEN		; Open sub-expression.
	.WORD	'^,ESCAPE	; Unary operator escape.
UNALEN	=	<.-UNATBL>/4

;
;   Secondary unary operator table.
;
UN2TBL:	.WORD	'B,BINRDX	; Binary radix.
	.WORD	'C,COMPL	; 1's complement.
	.WORD	'D,DECRDX	; Decimal radix.
	.WORD	'O,OCTRDX	; Octal radix.
	.WORD	'R,RAD50	; RAD50 conversion.
	.WORD	'X,HEXRDX	; Hexadecimal radix.
UN2LEN	=	<.-UN2TBL>/4
	.PAGE
	.SBTTL	Process binary operators
	.PSECT	CODE,I,RO
;
;   Subroutine to evaluate an expression.
;   On entry:	R0 points to expression string.
;
;   On exit:	R0 points after expression.
;		R1 contains expression value.
;		The carry will be set if an error is detected.
;
XPRESS::
	JSR	R5,$SAVRG	; Save registers R3 - R5.
;
;   Subexpression recursion point.
;
RECURS:	CALL	OPRAND		; Get an operand, OK?
	BCS	50$		; No, complain.
10$:	MOVB	(R0),R3		; End of the line?
	BEQ	40$		; Yes, return with value.
	BITB	#T,SYMCHR(R3)	; Expression terminator?		
	BNE	40$		; Yes, return value.
	MOV	#BINTBL,R3	; Point to binary operators table.
	MOV	#BINLEN,R4	; Get table length.
20$:	CMPB	(R3),(R0)	; Found operator?
	BEQ	30$		; Yes, evaluate it.
	CMP	(R3)+,(R3)+	; Point to next table entry.
	SOB	R4,20$		; Check all entries.
;   Unknown binary operator encountered.
	MOV	#E.BONF,R1	; Point to error message.
	SEC			; Indicate failure.
	RETURN
;   Evaluate binary operator.
30$:	INC	R0		; Point past operator character.
	TST	(R3)+		; Point to operator dispatch address.
	CALL	@(R3)+		; Call operator evaluation routine, OK?
	BCC	10$		; Yes, check for another operator.
	RETURN			; Indicate failure.
;   Exit evaluation level.
40$:	CLC			; Indicate success.
50$:	RETURN
	.PAGE
	.SBTTL	Process operands and unary operators
;
;   Subroutine to get an operand.
;
OPRAND:	MOV	#UNATBL,R3	; Point to unary operator table.
	MOV	#UNALEN,R4	; Get table length.
10$:	CMPB	(R3),(R0)	; Found operator?
	BEQ	20$		; Yes, evaluate it.
	CMP	(R3)+,(R3)+	; Point to next table entry.
	SOB	R4,10$		; Check all entries.
;   Operand must be numeric or symbolic.
	CMPB	(R0),#'0	; Is character too low to be numeric?
	BLO	SYMBOL		; Yes, assume symbolic.
	CMPB	(R0),#'9	; Too high to be decimal?
	BHI	SYMBOL		; Yes, assume symbolic.
	CALLR	NUMBER		; Process numeric value.
;   Process unary operator.
20$:	INC	R0		; Point past unary operator character.
	TST	(R3)+		; Point to operator dispatch address.
	CALLR	@(R3)+		; Process unary operator.
	.PAGE
;
;   Process symbolic value.
;
SYMBOL:	MOV	R0,R5		; Save pointer to symbol name.
10$:	MOVB	(R5)+,R3	; Fetch a character, patently illegal?
	BMI	20$		; Yes, trash it.
	TSTB	SYMCHR(R3)	; Is character a legal symbol constituent?
	BMI	10$		; Yes, try next one too.
20$:	DEC	R5		; Back up over non-symbol character.
	SUB	R0,R5		; Is "symbol" zero characters long?
	BEQ	SUBXMS		; Yes, complain.
;   Check for register name symbol.
	MOV	R0,R4		; Save symbol name pointer.
	CALL	U$SYMN		; Get symbol name in R2, R3.
	MOV	R0,R5		; Save expression pointer.
	MOV	#REGSYM,R0	; Point to register symbol table head.
	CALL	S$LNAM		; Look up symbol value, OK?
	BCC	30$		; Yes, get value and exit.
;   Check main symbol table.
	MOV	#SYMTBL,R0	; Point to main symbol table head.
	CALL	S$LNAM		; Look up symbol value, OK?
	BCS	SYMNTF		; No, symbol not found.
30$:	MOV	S.VALU(R0),R1	; Get symbol value.
	MOV	R5,R0		; Restore expression pointer.
	RETURN

;
;   Missing subexpresssion.
;
SUBXMS:	MOV	#E.MSBX,R1	; Point to error message.
	SEC			; Indicate failure.
	RETURN

;
;   Symbol not found.
;
SYMNTF:	MOV	#E.SYNF,R1	; Point to error message.
	MOV	R4,R0		; Point to unknown symbol name.
	SEC			; Indicate failure.
	RETURN
	.PAGE
;
;   Process a numeric value.
;   Allow decimal values to be denoted by a trailing period.
;
NUMBER:	CLR	R1		; Zero value.
	MOV	R0,R5		; Copy numeric constant pointer.
10$:	MOVB	(R5)+,R3	; Fetch a character, obviously illegal?
	BMI	20$		; Yes, cut off scan.
	BITB	#D,SYMCHR(R3)	; Is character a legal digit in some radix?
	BNE	10$		; Yes, keep scanning.
20$:	DEC	R5		; Point back to scan terminator.
	MOV	#10.,R4		; Assume decimal radix forced.
	CMPB	#'.,(R5)	; Has decimal conversion been forced?
	BEQ	30$		; Yes, assumption paid off.
	MOVB	RADIX,R4	; Get current radix.
;   Convert the number in the selected radix.
30$:	MOVB	(R0)+,R2	; Fetch a character, obviously illegal?
	BMI	40$		; Yes, conversion must be complete.
	MOVB	SYMCHR(R2),R2	; Get character attributes.
	BITB	#D,R2		; Is character a legal digit in some radix?
	BEQ	40$		; No, forget it.
	BIC	#^C<17>,R2	; Mask character to digit value.
	CMP	R2,R4		; Is digit legal in this radix?
	BHIS	40$		; No, terminate conversion.
	MUL	R4,R1		; Make room for new digit.
	ADD	R2,R1		; Add in new digit.
	BR	30$
;   If radix is decimal then remove '.' terminator.
40$:	CMP	#10.,R4		; Was conversion in decimal?
	BNE	50$		; No, back up over terminator.
	CMPB	#'.,-1(R0)	; Was terminator a period?
	BEQ	60$		; Yes, skip over it.
50$:	DEC	R0		; Back up over terminator.
60$:	CLC			; Indicate success.
	RETURN
	.PAGE
	.SBTTL	Unary operators
;
;   Fetch ASCII character.
;
ASCBYT:	CLR	R1		; Take no chances.
ASCII:	BISB	(R0)+,R1	; Get character, null?
	BNE	10$		; No, just leave.
	DEC	R0		; Back up over null.
10$:	RETURN

;
;   Fetch a pair of ASCII character.
;
ASCWRD:	CALL	ASCBYT		; Fetch first character.
	SWAB	R1		; Put first character in high byte.
	CALL	ASCII		; Fetch second character.
	SWAB	R1		; Put characters in proper bytes.
	RETURN

;
;   Temporarily set radix to binary.
;
BINRDX:	MOVB	RADIX,-(SP)	; Push current radix.
	MOVB	#2,RADIX	; Set radix to binary.
	BR	RADSET		; Fetch operand and restore radix.

;
;   Complement operand.
;
COMPL:	CALL	OPRAND		; Get an operand, OK?
	BCS	ERRXIT		; No, complain.
	COM	R1		; Complement it.
	BR	SUCRTN		; Take successful return.

;
;   Temporarily set radix to decimal.
;
DECRDX:	MOVB	RADIX,-(SP)	; Push current radix.
	MOVB	#10.,RADIX	; Set radix to decimal.
	BR	RADSET		; Fetch operand and restore radix.

;
;   Fetch value from specified address.
;
DEFER:	CALL	OPRAND		; Get an operand, OK?
	BCS	20$		; No, complain.
	BIT	#1,R1		; Is address odd?
	BNE	10$		; Yes, complain.
	MOV	R1,R5		; Copy address.
	CALL	M$RD5P		; Read word value, OK?
	BCC	20$		; Yes, exit.
10$:	MOV	#E.DFER,R1	; Point to deferred address error message.
	SEC			; Indicate failure.
20$:	RETURN
	.PAGE
;
;   Escape to second set of unary operators.
;
ESCAPE:	MOV	#UN2TBL,R3	; Point to secondary unary operator table.
	MOV	#UN2LEN,R4	; Get table length.
10$:	CMPB	(R3),(R0)	; Found operator?
	BEQ	20$		; Yes, evaluate it.
	CMP	(R3)+,(R3)+	; Point to next table entry.
	SOB	R4,10$		; Check all entries.
;   Can't find operator in table.
	DEC	R0		; Point back to unary operator escape.
	MOV	#E.BONF,R1	; Point to appropriate message.
	SEC			; Indicate failure.
	RETURN
;   Evaluate secondary unary operator.
20$:	INC	R0		; Skip past operator character.
	TST	(R3)+		; Point to operator dispatch address.
	CALLR	@(R3)+		; Process unary operator.

;
;   Temporarily set radix to hexadecimal.
;
HEXRDX:	MOVB	RADIX,-(SP)	; Push current radix.
	MOVB	#16.,RADIX	; Set radix to hexadecimal.
	BR	RADSET		; Fetch operand and restore radix.

;
;   Negate operand.
;
NEGATE:	CALL	OPRAND		; Get an operand, OK?
	BCS	ERRXIT		; No, complain.
	NEG	R1		; Negate it.
SUCRTN:	CLC			; Indicate success.
ERRXIT:	RETURN

;
;   Temporarily set radix to octal.
;
OCTRDX:	MOVB	RADIX,-(SP)	; Push current radix.
	MOVB	#8.,RADIX	; Set radix to octal.
RADSET:	CALL	OPRAND		; Fetch next operand.
	MOVB	(SP)+,RADIX	; Restore radix.
	RETURN

;
;   Convert next 3 characters to RAD50.
;
RAD50:	MOV	#1,R1		; Accept periods.
	CALL	$CAT5B		; Perform RAD50 conversion.
	TSTB	R2		; Was conversion terminated by end-of-line?
	BNE	10$		; No, assume everything is OK.
	DEC	R0		; Point to terminator.
10$:	RETURN
	.PAGE
	.SBTTL	Binary operators
;
;   Add operands.
;
ADDOPS:	CALL	GETOPR		; Fetch second operand.
	ADD	(SP)+,R1	; Add operands.
	BR	SUCXIT

;
;   Logically AND operands.
;
ANDOPS:	CALL	GETOPR		; Fetch second operand.
	COM	(SP)		; Invert source operand.
	BIC	(SP)+,R1	; Finish and operation.
	BR	SUCXIT

;
;   Divide operands.
;
DIVOPS:	CALL	GETOPR		; Fetch second operand.
	MOV	(SP)+,R5	; Pop source operand.
	CLR	R4		; Zero extend operand.
	DIV	R1,R4		; Perform division, OK?
	BCS	10$		; No, divide by zero attempted.
	BVS	20$		; No, result not representable.
	MOV	R4,R1		; Put quotient in place.
	BR	SUCXIT
;   Divide by zero attempted.
10$:	MOV	#E.DBZA,R1	; Point to error message.
	RETURN
;   Quotient cannot be represented.
20$:	MOV	#E.QCBR,R1	; Point to error message.
	SEC			; Indicate failure.
	RETURN

;
;   Multiply operands.
;
MULOPS:	CALL	GETOPR		; Fetch second operand.
	MUL	(SP)+,R1	; Multiply operands.
	BR	SUCXIT

;
;   Logically OR operands.
;
OROPS:	CALL	GETOPR		; Fetch second operand.
	BIS	(SP)+,R1	; OR operands.
	BR	SUCXIT

;
;   Subtract operands.
;
SUBOPS:	CALL	GETOPR		; Fetch second operand.
	SUB	R1,(SP)		; Subtract operands.
	MOV	(SP)+,R1	; Pop value.
SUCXIT:	CLC			; Indicate success.
	RETURN
	.PAGE
;
;   Subroutine to fetch second operand for a binary operator.
;   If an error occurs then a 2-level return will be effected.
;
GETOPR:	MOV	(SP),-(SP)	; Move return address down.
	MOV	R1,2(SP)	; Insert operand value in stack.
	CALL	OPRAND		; Fetch next operand, OK?
	BCC	10$		; Yes, return to evaluate operator.
	CMP	(SP)+,(SP)+	; Pop stack to previous return address.
	SEC			; Indicate failure.
10$:	RETURN

	.ENABL	LSB
;
;   Open a subexpression.
;
OPEN:	CALL	RECURS		; Recurse to process subexpression.
OPNRTN:	BCS	20$		; Preserve first diagnostic seen.
10$:	MOV	#E.UMSI,R1	; Indicate unmatched open/close indicators.
	SEC			; Indicate failure.
20$:	RETURN

;
;   Close a subexpression.
;
CLOSE:	CMP	#OPNRTN,2(SP)	; Was subexpression ever opened?
	BNE	10$		; No, complain.
	CMP	(SP)+,(SP)+	; Pop last two return addresses.
	CLC			; Indicate success.
	RETURN
	.DSABL	LSB
	.END
