; ARITH2.ASM
;
; A simple floating point calculator that demonstrates the use of the
; UCR Standard Library pattern matching routines.  Note that this
; program requires an FPU.

		.xlist
		.386
		.387
		option		segment:use16
		include 	stdlib.a
		includelib	stdlib.lib
		matchfuncs
		.list


dseg		segment	para public 'data'

; The following is a temporary used when converting a floating point
; string to a 64 bit real value.

CurValue	real8	0.0


; Some sample strings containing expressions to try out:


Str1		byte	"5+2*(3-1)",0
Str2		byte	"(5+2)*(7-10)",0
Str3		byte	"5",0
Str4		byte	"(6+2)/(5+1)-7e5*2/1.3e2+1.5",0
Str5		byte	"2.5*(2-(3+1)/4+1)",0
Str6		byte	"6+(-5*2)",0
Str7		byte	"6*-1",0
Str8		byte	"1.2e5/2.1e5",0
Str9		byte	"0.9999999999999999+1e-15",0
str10		byte	"2.1-1.1",0


; Grammar for simple infix -> postfix translation operation:
; Semantic rules appear in braces.
;
; E -> FE' {print result}
; E' -> +F {fadd} E' | -F {fsub} E' | <empty string>
; F -> TF'
; F -> *T {fmul} F' | /T {fdiv} F' | <empty string>
; T -> -T {fchs} | S
; S -> <constant> {fld constant} | (E)
;
;
;
; UCR Standard Library Pattern which handles the grammar above:

; An expression consists of an "E" item followed by the end of the string:

Expression	pattern	{sl_Match2,E,,EndOfString}
EndOfString	pattern	{EOS}


; An "E" item consists of an "F" item optionally followed by "+" or "-"
; and another "E" item:

E		pattern	{sl_Match2, F,,Eprime}
Eprime		pattern	{MatchChar, '+', Eprime2, epf}
epf		pattern	{sl_Match2, F,,epPlus}
epPlus		pattern	{DoFadd,,,Eprime}

Eprime2		pattern	{MatchChar, '-', Succeed, emf}
emf		pattern	{sl_Match2, F,,epMinus}
epMinus		pattern	{DoFsub,,,Eprime}

; An "F" item consists of a "T" item optionally followed  by "*" or "/"
; followed by another "T" item:

F		pattern	{sl_Match2, T,,Fprime}
Fprime		pattern	{MatchChar, '*', Fprime2, fmf}
fmf		pattern	{sl_Match2, T, 0, pMul}
pMul		pattern	{DoFmul,,,Fprime}

Fprime2		pattern	{MatchChar, '/', Succeed, fdf}
fdf		pattern	{sl_Match2, T, 0, pDiv}
pDiv		pattern	{DoFdiv, 0, 0,Fprime}

; T item consists of an "S" item or a "-" followed by another "T" item:

T		pattern	{MatchChar, '-', S, TT}
TT		pattern	{sl_Match2, T, 0,tpn}
tpn		pattern	{DoFchs}

; An "S" item is either a floating point constant or "(" followed by
; and "E" item followed by ")".
;
; The regular expression for a floating point constant is
;
;	[0-9]+ ( "." [0-9]* | ) ( ((e|E) (+|-| ) [0-9]+) | )
;
; Note: the pattern "Const" matches exactly the characters specified
;	by the above regular expression.  It is the pattern the calc-
;	ulator grabs when converting a string to a floating point number.


Const		pattern	{sl_match2, ConstStr, 0, FLDConst}
ConstStr	pattern	{sl_match2, DoDigits, 0, Const2}
Const2		pattern	{matchchar, '.', Const4, Const3}
Const3		pattern	{sl_match2, DoDigits, Const4, Const4}
Const4		pattern	{matchchar, 'e', const5, const6}
Const5		pattern	{matchchar, 'E', Succeed, const6}
Const6		pattern	{matchchar, '+', const7, const8}
Const7		pattern	{matchchar, '-', const8, const8}
Const8		pattern	{sl_match2, DoDigits}

FldConst	pattern	{PushValue}

; DoDigits handles the regular expression [0-9]+

DoDigits	pattern	{Anycset, Digits, 0, SpanDigits}
SpanDigits	pattern	{Spancset, Digits}

; The S production handles constants or an expression in parentheses.

S		pattern	{MatchChar, '(', Const, IntE}
IntE		pattern	{sl_Match2, E, 0, CloseParen}
CloseParen	pattern	{MatchChar, ')'}


; The Succeed pattern always succeeds.

Succeed		pattern	{DoSucceed}


; We use digits from the UCR Standard Library cset standard sets.

		include	stdsets.a

dseg		ends



cseg		segment	para public 'code'
		assume	cs:cseg, ds:dseg

; DoSucceed matches the empty string.  In other words, it matches anything
; and always returns success without eating any characters from the input
; string.

DoSucceed	proc	far
		mov	ax, di
		stc
		ret
DoSucceed	endp


; DoFadd - Adds the two items on the top of the FPU stack.

DoFadd		proc	far
		faddp	st(1), st
		mov	ax, di			;Required by sl_Match
		stc				;Always succeed.
		ret
DoFadd		endp


; DoFsub - Subtracts the two values on the top of the FPU stack.

DoFsub		proc	far
		fsubp	st(1), st
		mov	ax, di			;Required by sl_Match
		stc
		ret
DoFsub		endp


; DoFmul- Multiplies the two values on the FPU stack.

DoFmul		proc	far
		fmulp	st(1), st
		mov	ax, di			;Required by sl_Match
		stc
		ret
DoFmul		endp


; DoFdiv- Divides the two values on the FPU stack.

DoFDiv		proc	far
		fdivp	st(1), st
		mov	ax, di			;Required by sl_Match
		stc
		ret
DoFDiv		endp


; DoFchs- Negates the value on the top of the FPU stack.

DoFchs		proc	far
		fchs
		mov	ax, di			;Required by sl_Match
		stc
		ret
DoFchs		endp


; PushValue-	We've just matched a string that corresponds to a
;		floating point constant.  Convert it to a floating
;		point value and push that value onto the FPU stack.

PushValue	proc	far
		push	ds
		push	es
		pusha
		mov	ax, dseg
		mov	ds, ax

		lesi	Const		;FP val matched by this pat.
		patgrab			;Get a copy of the string.
		atof			;Convert to real.
		free			;Return mem used by patgrab.
		lesi	CurValue	;Copy floating point accumulator
		sdfpa			; to a local variable and then
		fld	CurValue	; copy that value to the FPU stk.

		popa
		mov	ax, di
		pop	es
		pop	ds
		stc
		ret
PushValue	endp



; DoExp-	This routine expects a pointer to a string containing
;		an arithmetic expression in ES:DI.  It evaluates the
;		given expression and prints the result.

DoExp		proc	near
		finit			;Be sure to do this!
		fwait

		puts			;Print the expression

		ldxi	Expression
		xor	cx, cx
		match
		jc	GoodVal
		printff
		byte	" is an illegal expression",cr,lf,0
		ret

GoodVal:	fstp	CurValue
		printff
		byte	" = %12.6ge\n",0
		dword	CurValue
		ret
DoExp		endp



; The main program tests the expression evaluator.

Main		proc
		mov	ax, dseg
		mov	ds, ax
		mov	es, ax
		meminit

		lesi	Str1
		call	DoExp
		lesi	Str2
		call	DoExp
		lesi	Str3
		call	DoExp
		lesi	Str4
		call	DoExp
		lesi	Str5
		call	DoExp
		lesi	Str6
		call	DoExp
		lesi	Str7
		call	DoExp
		lesi	Str8
		call	DoExp
		lesi	Str9
		call	DoExp
		lesi	Str10
		call	DoExp

Quit:		ExitPgm
Main		endp

cseg		ends

sseg		segment	para stack 'stack'
stk		db	1024 dup ("stack   ")
sseg		ends

zzzzzzseg	segment	para public 'zzzzzz'
LastBytes	db	16 dup (?)
zzzzzzseg	ends
		end	Main
