'===========================================================================
' Subject: FORMULA PARSER AND EXECUTOR        Date: 06-09-98 (23:43)       
'  Author: Bert van Dam                       Code: QB, QBasic, PDS        
'  Origin: bd1rsd@usa.net                   Packet: ALGOR.ABC
'===========================================================================
'Simple formula parser and executor            june 9, 1998
'                                              Bert van Dam

'This program takes a simple formula as a string (for example as entered
'by a user), converts it into an asm subroutine and executes it. This is
'not meant a a complete application. It was merely meant to show that the
'question "can you have basic calculate a formula entered by a user" has
'the answer "yes!" Since it is a demo it has some limitations: it can handle
'numbers smaller than FF (254) including the answer!, it handles simple
'operands only (+-x/) and it parses 'left-to-right' ignoring all math
'rules. So this example 2x3+6-1 is seen as ((2x3)+6)-1 which only happens
'to be correct by accident. Divisions need to result in integers since
'remainders are ignored. So if you actually want to use this technique
'you'll need to do some work yourself!


CLS
PRINT "---===[ Simple Formula Parser and Executor Using Inline Assembler ]===---"
'This program is donated to the public domain by Bert van Dam. I can be
'reached in the QUIK_BAS fidonet area (2:285/750.16) or the internet at
'bd1rsd@usa.net
PRINT

'Parse formula, this could be entered by a user
TestFormula$ = "2x3+6-1="

PRINT "Parsing formula "; TestFormula$
PRINT
FOR t = 1 TO LEN(TestFormula$)
    'Chop into parts containing the operator and the following number
    Token$ = MID$(TestFormula$, t, 1)
    IF INSTR("+-x/=", Token$) <> 0 THEN
        'Process current part based on the leading operator
        SELECT CASE LEFT$(Part$, 1)
        CASE "+"
            'Remove the operator
            NewPart$ = RIGHT$(Part$, LEN(Part$) - 1)
            'Print the required assembler code
            PRINT "add ax,"; NewPart$
            'Add the code in hex to the formula string
            Formula$ = Formula$ + CHR$(&H5) + CHR$(VAL(NewPart$)) + CHR$(&H0)
            'Clear the current part
            Part$ = ""
        CASE "-"
            NewPart$ = RIGHT$(Part$, LEN(Part$) - 1)
            PRINT "sub ax,"; NewPart$
            Formula$ = Formula$ + CHR$(&H2D) + CHR$(VAL(NewPart$)) + CHR$(&H0)
            Part$ = ""
        CASE "x"
            NewPart$ = RIGHT$(Part$, LEN(Part$) - 1)
            PRINT "mov bx,"; NewPart$
            Formula$ = Formula$ + CHR$(&HBB) + CHR$(VAL(NewPart$)) + CHR$(&H0)
            PRINT "mul bx"
            Formula$ = Formula$ + CHR$(&HF7) + CHR$(&HE3)
            Part$ = ""
        CASE "/"
            NewPart$ = RIGHT$(Part$, LEN(Part$) - 1)
            PRINT "mov bx,"; NewPart$
            Formula$ = Formula$ + CHR$(&HBB) + CHR$(VAL(NewPart$)) + CHR$(&H0)
            PRINT "div bx"
            Formula$ = Formula$ + CHR$(&HF7) + CHR$(&HF3)
            Part$ = ""
        CASE ELSE
            'No leading operator so this must be the first part
            PRINT "mov ax,"; Part$
            'Start a formu;a string and add the code in hex
            Formula$ = CHR$(&HB8) + CHR$(VAL(Part$)) + CHR$(&H0)
            Part$ = ""
        END SELECT
    END IF
    'Keep building this part intul the next operator ss found
    Part$ = Part$ + Token$
NEXT t
PRINT

'This is the asm header needed to transform the formula into an
'assembler subroutine. it contains the following code
'   push bp         save the stack position
'   mov bp,sp
'   push ax         registers needed in the calculations
'   push bx        
'   push dx         needed by div and add
Header$ = CHR$(&H55) + CHR$(&H8B) + CHR$(&HEC)
Header$ = Header$ + CHR$(&H50) + CHR$(&H53) + CHR$(&H52)

'This is the asm trailer needed to transform the formula into an
'assembler subroutine. it contains the following code
'   mov bx,[bp+6]   determine propper position on the stack
'   mov [bx],ax     place ax on the stack
'   pop dx          retore used registers
'   pop bx
'   pop ax
'   mov sp, bp      restore the stack position
'   pop bp
'   retf 2          do a far return to basic
Trailer$ = CHR$(&H8B) + CHR$(&H5E) + CHR$(&H6)
Trailer$ = Trailer$ + CHR$(&H89) + CHR$(&H7) + CHR$(&H5A) + CHR$(&H5B) + CHR$(&H58)
Trailer$ = Trailer$ + CHR$(&H8B) + CHR$(&HE5) + CHR$(&H5D) + CHR$(&HCA) + CHR$(&H2) + CHR$(&H0)

'Make the complete asm subroutine by adding the parts together
Program$ = Header$ + Formula$ + Trailer$
DEF SEG = VARSEG(Program$)
LetSee% = SADD(Program$)

'And call the routine to see the answer
Test% = 0
CALL ABSOLUTE(Test%, LetSee%)
PRINT "The result of "; TestFormula$; Test%
