ECE291 Computer Engineering II Kalbarczyk, Fall 1999

Machine Problem 2: Infix Calculator

Purpose User I/O, Parsing algorithms
Points 50
Due Date Tuesday 10/5/99 5:00

Introduction

Calculators have been one of the greatest gifts to the engineer this century. They eliminated the tedious work of calculating numerical results. The implimentations of various calculators have varied in order to solve the problem of correctly making calculations with a minimal effort to both the user and designer of the calculator.

In order to make the calculator more intuitive to use, the infix notation is used. This is the "normal" way that we are all used to seeing algebraic equations. The infix notation of a mathematical expression is a notation in which operators are placed between the operands that they operate on. This is in contrast to prefix or postfix notation in which all operators appear before or after the operands respectively.

Below is an example of how the equation 1 + 1 would look in prefix, infix, and postfix notations.

Prefix Infix Postfix
+ 1 1 1 + 1 1 1 +

The infix notation is what most people are taught in grammar school, however it is difficult to impliment this on a computer since you must know what each of the operands are before you can perform an operation. In addition, the order of precidence of the operations makes it difficult to determine which operations should be performed first.

In this MP, you will write an infix calculator that must to conquer these problems.

The solution code for this MP is capable of performing a great many functions. Your program will only be required to impliment a subset of these operations. There are a few basic required functions of your program. In addition, you will have to choose from a number of extra functions to give your calculator. These more advaced functions appear in italics throughout this writeup.

Order of Precidence

When calculations are performed it is important to obser the order of precidence. For a example, operations in the innermost set of parenthesis have the highest precidence, follwed by multiplication and division calculated from left to right, which is then followed by addiditon and subtraction which is also calculated from left to right.

Operator Type
-
~
unary minus (negation)
unary bitwise NOT
! unary factorial
*
/
%
multiplication
division
modulus
+
-
addition
subtraction
<
>
bitwise shift left
bitwise shift right
& bitwise AND
^ bitwise XOR
| bitwise OR
()
[]
{}
parentheses
brackets
braces

The operations that are listed first on the table have the highest precidence. All operations that are in the same table row have equal precidence and depend upon the order in which they appear. For the unary minus, and unary bitwise NOT operations, operations are performed from right to left. For all other operators, operations are performed left to right.

Note that even though you sould normally consider parentheses to have the highest precidence, they will be calculated last. This is to ensure that all operations within parentheses are calculated before the parentheses itself is evaluated.

Our Implementation

The task of solving an equation is a difficult one. The following algorithm will allow your program to solve any algebraic equation.

  1. Take input (if no input is read in, then stop)
  2. Determine if all the parentheses match correctly
  3. Parse the string
  4. Determine if the equation is in it's simplest form. If it is, then go to step #1
  5. Find the innermost set of parentheses
  6. Find the highest precidence operator within the innermost set of parentheses
  7. Perform some operation indicated by the operator found in step 6, making the equation simpler.
  8. Go to step #4

A careful discussion of each of these steps is written below.

Taking Input

Often we take for granted how input is entered into programs. This can be a difficult process to program in assembly. Your program must only accept those characters found the the accept string. Upon any other entry, the computer should beep. While your program should accept line feeds, it must ignore them to allow input from a file. In addition, you must allow backspacing, but you must make sure that the buffer is never overflowed, or underflowed. After a carriage return is entered, a '$' should be added to the end of the string to identify it.

The program should exit if the input string was left empty, or if the escape key was pressed. The input procedure will not however exit the program by itself. Instead, the input procedure will set the carry flag, which will be used as a signal for main to exit. To prevent the program from quitting in other cases, the carry flag should be cleared.

If your implimentation is unable to handle any of the inputs that are within the accept string, you should remove those characters from the string, and decrease the constant NUMACCEPT accordingly.

Checking Parentheses

It is important that all of the parentheses match correctly. This means that each open parentheses must have a matching closed parenteses. The following are several examples of input.

1 + 1       OK
(1 + 1)     OK
(1) + (1)   OK
((((1))))   OK
1 + (1 + 1  No matching closed parentheses
)1 + 1(     No mathcin open parenteses and no matching closed parentheses
(1 + 1      No matching closed parentheses
1 + 1)      No matching open parentheses

Parsing the String

Since it is difficult for the computer to perform mathematical operations on ASCII numbers, it is necessary to parse the string, or convert it into a more useable form. When parsing the string, each byte is individually examined and manipulated.

If it is determined that the byte being examined is an operator, then that operator is stored directly into the control string.

If it is a number, then the constant STARTNUM should be placed into the control string, followed by the word sized binary equivalent for the number, followed by the constant ENDNUM.

If the byte being examined is a space, it should be ignored.

Parsing should end when a '$' has been found in the input string, and a ENDSTR character should be placed at the end of the control string.

Several examples of how input is parsed are shown below. For these examples, STARTNUM is displayed as a 'N', ENDNUM is displayed as 'n' and ENDSTR is shown as '$'. You should however use the defined constants in your code. Also note that numbers have been written in hex in the little endian format of the x86.

An example of how number and operators are parsed, the equation 1*1 would be parsed as the following string of bytes in the control string:
'N'01h00h'n''*''N'01h00h'n''$'

The equation (1 + 1) would be parsed as the following string of bytes in the control string:
'(''N'01h00h'n''+''N'01h00h'n'')''$'

A minus sign can be difficult to handle. In general, your program may assume that whenever a '-' is immediately followed by a digit, there is a negative number, and whenever a '-' is immediately followed by anything other than a digit, it is a binary subtraction.

As an example the equation 2 - -1 should be parsed into the following string of bytes:
'N'02h00h'n''-''N'FFhFFh'n''$'

As you can see, since the first minus sign was immediately followed by a space, it was treated as the subtraction operation. The second minus sign however was immediately followed by an ASCII digit, so it was trated as a negation. The only time your code should interpret a number as being negative is if it is followed by an ASCII digit.

String Verification

As the input string is parsed, it is also checked that it follows certain rules. In general, numbers are followed by operators and operators are followed by numbers. Also, the equation should start and end with a number.

Parentheses are special operators which must be delt with seperately. In general, since everything inside of a pair of parentheses will be evaluated to a single number, an open parentheses may be treated externally as the start of a number, and a closed parentheses may be treated externally as the end of a number.

The following is a list of rules that the input string must follow.

The rules above work only for the basic operations that your program may perform. Depending upon the extra functions that your program performs other special cases may arise, the following table describes all possible exceptions.

Tolken Preceded By Followed By
the start of the string -- a number
an open parentheses
bitwise NOT '~' or unary negation '-'
a number the start of the string
open parentheses
a binary operator
bitwise NOT '~' or unary negation
closed parentheses with implicit multiply
a binary operator
closed parentheses
the end of the string
factorial '!'
open parentheses with implicit multiply
binary operators a number
closed parentheses
factorial '!'
a number
open parentheses.
bitwise NOT '~' or unary negation '-'
open parentheses the start of the string
open parentheses
a binary operation
bitwise NOT '~' or unary negation '-'
factorial '!' with implicit multiply
a number with implicit multiply
closed parentheses with implicit multiply
a number
open parentheses.
bitwise NOT '~' or unary negation
close parentheses a number
close parentheses
factorial '!'
a binary operator
close parentheses
the end of the string
factorial '!'
a number with implicit multiply
open parentheses with implicit multiply
the end of the string a number
close parentheses
factorial '!'
--
bitwise NOT '~' or unary negation '-' the start of the string
open parentheses
a binary operator
bitwise NOT '~' or unary negation '-'
close parentheses with implicit multiplication
a number
bitwise NOT '~' or unary negation
open parentheses
factorial '!' a number
close parentheses
factorial '!'
a binary operator
the end of the string
factorial '!'
open parentheses with implicit multiply

In order to impliment the complex set of rules above, a simple algorithm is needed which will successfully allow the rules to be implimented.

In general, the computer will always be expecting to see something. It will either expect to see a number, or an operation, or in some cases it doesn't care which appears next in the input string. By making this general assumption, and adding a few special cases, we can develop a simple algorithm which will perform the necessary checks, easily and painlessly.

  1. Begin by expecting a number.
  2. After a number, expect an operator.
  3. After an operator, expect a number.
  4. Verify that the program was not expecting a number when the input string ends.

This greatly simplifies the algorithm, but there are a number of exceptional cases that must be examined. Most noteably, parentheses have special rules that differ from other types of operations.

  1. Open parentheses should only be accepted when expecting a number.
  2. After an open parentheses, expect a number.
  3. Closed parentheses should be accepted when expecting an operator.
  4. After a closed parentheses, expect an operator.

By carefully following these rules and exceptions, your code should be capable of verifying that a valid string has been entered. There may be other exceptions to these rules depending upon which functions your calculator handles. These exceptions are listed later in this writeup.

Determining When Done

It can easily be determined if the string is fully simplified. If the string contains nothing except a single number, then it is fully simplified. If it contains no numbers, then there is an error that there are no operands to work with. If the string contains more than one number and no operators, then there is an error since the string is not fully simplified, and there are no operations to perform.

Finding an Innermost Set of Parentheses

An innermost set of parenthese is a matching pair of parenthese that contain no other parentheses. If you scan an equation from left to right, you will notice that the first closed parentheses is part of an innermost pair. This simple observation should make it easy for you to find an innermost pair of parentheses.

The procedure FindInner, returns two values. The first output value is returned in register BX, and indicates where the open parentheses is, and the second output value is SI and when added to BX, indicates where the closed parentheses is.

One case that you may consider is when there are no parentheses left in the equation. In this case, we would like to look at the entire equation as a whole. Therefore, the program should act as if there are parentheses in the equation that start at the first position in the string, and end in the last position. For example, if you were to enter the equation 1 * 1 as shown in an above example, the findInner procedure should report that the innermost set of parenthases begins at offset 0, and ends at offset 8 .

Finding the Highest Precidented Operator

Each operator is given a specific priority level. The operator with the highest priority has the highest precidence, and therefore should be evaluated first. There is howver the difficulty associated with operators that have equal priority. In this case, most of the operators are evaluated from left to right as they appear in the equation. Only the unary minus and bitwise NOT operators are evaluated from right to left.

Defined in the MP is a table for your program to look up the precidence of each of the operators. Each of the operators that are evaluated from left to right are given an even priority, while each of the operators that are evaluated from right to left are given an odd priority.

To find the highest priority operator, your program should scan the substring from left to right. When an operator is found, it's priority should be looked up in the priority table. This priority level and the address of theis operator should then be recorded. For operators that are evaluated from right to left, the priority of the operator should be decremented at this point so that any operators of the same type that lie to the right appear to have a higher priority. For each following operator, the operator's priority should be again looked up in the table. Only if the looked up value of the priority is greater than the previously recoded priority, should the new information is stored. This process should continue until the end of the substring is reached.

In the case that there are no operators between a set of parentheses, then the parenthses themselves must be evaluated. Therefore, the open parentheses will should be returned as the operator to be evaluated.

Solving the Highest Precidented Operator

When solving a particular operator, the goal is to simplify the cotnrol string. The operands of the operator must be found by scanning forward and backwards in the control string. If there are two operands, then one of them must be replaced with nulls, while the other one is replaced with the results of the operation. If there is only one operand, then that operand is replaced with the results of the operation. In either case, the operator itself is replaced with NULLs. In the special case of parentheses, each of the parentheses should simply be replaced with NULLs. No operands need be evaluated.

The NULLs that are inserted into the string are placed there to indicate white space in the control string. These characters should simply be ignored or skiped over whenever they are found in the control string through the solving process.

Scanning the String

For some of the procedures, it is necesary to scan the control string. It is important to note that the control string contains numbers, which may take on any value, as well as operators which have very specific values. It is possible that a number will contain a byte that takes on a value of an operator. For this reason, it is important that your program does skips over numbers when it is scanning the string.

In addition, NULLs may have been inserted into the string durring the solving process to indicate that a part of the equation has been simplified. To handle this, your code should always ignore NULLs when scanning the string.

Free Help

You have been given for free the procedure LibDspCtrlStr. This library procedure will convert the control string to ASCII characters and display it to the screen. You may wish to insert calls to this procedure in your code to help with the debugging process. This procedure is called before each simplification of the control string.

Extra Funcitonality

Your program must only perform addition, subtraction, multiplication, the modulus operation and accept parentheses, as well as accept positive and negative numbers. In the above sections, meathods have been described as to how to implement these simple functions. However, you will also have to give your code your choice of the following extra functions. These functions may cause a number of exceptional cases that you will have to deal with. Some of the extra functions and their associated exceptions are described below:

Brackets [], and Braces {}

By accepting other Brackets and Braces, it becomes more difficult to determine if parentheses match correctly. For instance, the types of parentheses must match correctly.
(1 + 1]     Parentheses type mismatch
([1 + 1)]   Parentheses type mismatch

As you can see from the above examples, you may not simply be able to count the number of open and closed parentheses, as the last example would pass this test.

Instead, you will make use of the stack. Each time an open parentheses is found, you should push that value onto the stack. When a closed parentheses is found, you should pop the last value off the stack and verify that the closed parentheses that was found matches with the open parentheses that was pushed onto the stack. Since there may be errors in the input string, you must make sure that the stack is left in the same condition as when the procedure began. It is very important that you do not try to return from the procedure before poping off everything that you pushed onto the stack, and you should not pop anything off of the stack before pushing something on first.

Also, to reduce added work in the rest of the code, when checking the parentheses in the string, all open braces and open brackets should be replaced with open parentheses. Similarly, close braces and close brackets should be replaced with close parentheses. This replacement occurs in the input string when the checkParens function is called.

As an example, the equation ({[1 + 1]}) would be converted in the input string into (((1 + 1))) in the checkParens procedure. This string should then be stored into the control string as the following string of bytes:
'(''(''(''N'01h00h'n''+''N'01h00h'n'')' ')'')''$'

Implicit Multiplication

Implicit multiplication is assuming that you intend to multiply the value inside of a pair of parentheses with a value adjacent to a pair of parentheses, without explicitly writing the multiplication symbol. An example of implicit multiplication is interpreting the equation 2(2) to mean 2 * 2.

Allowing for implicit parentheses changes the rules for verifying parentheses, as now the parentheses themselves may or may not be interpreted as a binary operator. the following is a list verification rules for parentheses if implicit multiplication is allowed:

  1. Open parentheses should always be accepted.
  2. After an open parentheses, the program should expect a number.
  3. Close parntheses should be accepted when expecting an operator.
  4. After a close parentheses, the program should allow anything to follow.

When evaluating parentheses, it is necessary to search the string to determine if an implicit multiplication should be performed. If a backwards search reveals that the preceding tolken is a number then the first parentheses should be replaced by a multiply ('*'), otherwise, it should be replaced with a NULL as usual. If a forward search reveals that the following tolken is a number then the second parentheses should be replaced by a multiply ('*'), otherwise, it should be replaced by a NULL as before.

Factorial

Since the factorial is a unary post-operator, it has it's own set of special rules when verifying the string.

  1. A factorial should be accepted when expecting an operator, but not when expecting a number.
  2. After a factorial, the program should expect an operator (or the end of the string.)

You will also have to add the functionality of evaluating the factorial in the SolveOne procedure.

Adding the factorial also adds complexity if your program performs implicit multiplication. When evaluating an open parentheses with implicit multiplication, your program must replace the open parentheses with a multiply ('*') when it is preceded either by a number, or by a factorial. Otherwise, it should be replaced with a NULL as usual.

Bitwise NOT '~' and Unary Negation '-'

To impliment unary negation, additional cases must be handled when parsing the input string since a minus sign ('-') could indicate subtraction, a negative number, or unary negation. The following rules decide how a minus sign should be handled.

  1. If immediately followed by an ASCII digit, interpret it as a negative number.
  2. If immediately followed by another minus sign, a bitwise NOT or an open parentheses, interpret it as a negation.
  3. In all other cases, interpret the minus sign as subtraction.

The fact that unary negation and the bitwise not are unary pre-operators, these operators have their own set of sepcial rules when verifying the string.

  1. They should only be accepted when expecting a number, but not when expecting an operator.
  2. After one of these operators, the program should expect a number.

You will also have to add the functionality of evaluating these operands into the SolveOne procedure.

In addition, these two operators must be evaluated from right to left. For this reason, in the priority table, each of the operators that are evaluated from right to left have an odd priority, while each of the operators that are evaluated from left to right have an even priority. This can be used to easily lower the priority of only those operators that have an odd priority, so that the next operator with the same priority may replace it as the operator with the highest precidence.

If your program also impliments implicit multiplication, agian there is added complexity. When evaluating the closed parentheses with implicit multiplication, your program must replace the close parentheses with a multiply ('*') when it is followed by a number, or a bitwise NOT, or a unary negation. Otherwise, it should be replaced with a NULL as usual.

You may notice that the unary negation appears as a differnt symbol when using LibDspCtrlStr this is to allow us to distinguish between a unary negation of a positive number, and a negative number.

AND '&', OR '|', and XOR '^'

All that is needed to impliment these operations is to add the fucntions to the SolveOne procedure.

Shift left '<' and Shift right '>'

While for these functions it is only needed to add the functions to the SolveOne procedure, it is necessary to take special care when shifting by a negative number, or when shifting by large numbers whose low eight bits are zeroed out.

Intelligent handeling of '-' sign

The intelligent handleing of the minus sign simply refers to an attempt to not cause an error. While by default, your program should always asume that if a minus sign is followed by an ASCII character that it is a negative number, a more intelligent way of dealing with this is to treat the negative number as actually subtracting a positive number. In this way the equation 1-1 where no white space was entered could be interpreted as 1 - 1 rather than 1 -1 which cannot be evaluated.

However, if a minus sign is followed by anything other than a decimal digit, your program should not try to parse this as being a negative number. For instace the equation 1*- 1 should still cause an error, as there is white space between teh minus sign and the number 1.

A Sample Run of the Algorithm

The follwoing shows how a sample equation in the control string would be simplified one step at a time. At each step, a single operation is performed, simplifing the equation.

After the equation (2 * ( 4 + 6 / 2)) is parsed, the following would be displayed to the screen after each step.

(2 * (4 + 6 / 2))
(2 * (4 + 3))
(2 * (7))
(2 * 7)
(14)
14

The following table shows what each byte of the control string would contain after each step is performed.

'(''N'02h00h'n''*''(''N'04h00h'n''+''N'06h00h'n' '/''N'02h00h'n'')'')''$'
'(''N'02h00h'n''*''(''N'04h00h'n''+''N'03h00h'n' NULLNULLNULLNULLNULL')'')''$'
'(''N'02h00h'n''*''(''N'07h00h'n'NULLNULLNULLNULLNULL NULLNULLNULLNULLNULL')'')''$'
'(''N'02h00h'n''*'NULL'N'07h00h'n'NULLNULLNULLNULLNULL NULLNULLNULLNULLNULLNULL')''$'
'(''N'0Dh00h'n'NULLNULLNULLNULLNULLNULLNULLNULLNULLNULLNULL NULLNULLNULLNULLNULLNULL')''$'
NULL'N'0Dh00h'n'NULLNULLNULLNULLNULLNULLNULLNULLNULLNULLNULL NULLNULLNULLNULLNULLNULLNULL'$'

You can see how after each operation, an operator and an operand are replaced with NULLs and an operand is replaced with the results of an operation. After the last step, the equation is found to only contain the number 14 (000Dh), which is the correct answer to the equation.

Sample Input and Output

Variables

Procedures

Preliminary Procedure

Monitor the newsgroup and this on-line section for revisions to the MP or to the writeup

Final Steps

  1. Verify that your program is working properly using the various test files available on the network drive.
  2. Print a copy of the MP2 grading sheet.
  3. Demonstrate MP2.EXE to a TA or to the instructor.
  4. Handin in your program by running:
  5. Staple the MP2 grading sheet to the front of your MP2.ASM file and give both to the same TA that approved your demonstration.

MP2.ASM


TITLE MP2:Infix-Calculator     Your_Name     Todays_Date
.MODEL SMALL
.386
COMMENT *
        In this MP, you will write a program which will take input
        from the keyboard which will be parsed and solved as an 
        arithmatic equation
        
        ECE291: Machine Problem 2
        Prof. Zbigniew Kalbarczyk
        Guest Author: Brandon Tipp
        Univeristy of Illinois
        Dept. of Electrical & Computer Engineering
        Fall 1999
        
        Ver 1.0
        *

;====== SECTION 1: Define constants =======================================
;misc ASCII codes
BEL     EQU     007h
BS      EQU     008h
CR      EQU     00Dh
LF      EQU     00Ah
SPACE   EQU     020h
ESCKEY  EQU     01Bh

MAX_BUFF_LEN EQU 80  ; Bytes; limit input to one line
NUM_ACCEPT   EQU 33  ; The number of acceptable characters

; Definitions for the control string
NULL       EQU  00
STARTNUM   EQU  78
ENDNUM     EQU  110
ENDSTR     EQU  36
NEGATE     EQU  0C4h ; ascii for a hyphen

PUBLIC BEL, BS, CR, LF, SPACE, ESCKEY
PUBLIC MAX_BUFF_LEN, NUM_ACCEPT
PUBLIC NULL, STARTNUM, ENDNUM, ENDSTR, NEGATE


; GetOperands MACRO
; This macro may be used and edited freely.  Edit this comment
; block to reflect changes
; Purpose: Get the left and right operands to an operation.
;          Nullifies the right operand
; Inputs: X, the place to store the left operand  (must be a register, not bp, di, si, or bx)
;         Y, the place to store the right operand (must be a register, not bp, di, si, or bx)
; Ouputs: X, holds the number that was indicated by GetOp1
;         Y, holds the number that was indicated by GetOp2
;         The number indicated by GetOp2 is nullified from the string
;         BP , the return value from GetOp1, indicating where the number is
; Notes:  Performs no error checking.
GetOperands MACRO x, y 
        call GetOp2
        mov y, word ptr controlStr[bp + 1]
        mov word ptr controlStr[bp],  NULL
        mov word ptr controlStr[bp + 2], NULL
        call GetOp1
        mov x, word ptr controlStr[bp + 1]
endm


;====== SECTION 2: Declare external procedures ============================

EXTRN   kbdine:near, dspout:near, dspmsg:near, binasc:near, ascbin: near
EXTRN   mp2xit:near, kbdin:near 

;====== Library procedures ================================================
;Free Library procedure
EXTRN  LibDspCtrlStr:near

;The follwing library procedures must be replaced with your own code.
EXTRN   LibInput:near
EXTRN   LibCheckParens:near
EXTRN   LibParse:near
EXTRN   LibCheckDone:near
EXTRN   LibFindInner:near
EXTRN   LibFindOne:near
EXTRN   LibSolveOne:near
EXTRN   LibGetOp1:near
EXTRN   LibGetOp2:near

;These procedures need to be public so the library may call them
PUBLIC  GetOp1, GetOp2

;====== SECTION 3: Define stack segment ===================================

STKSEG SEGMENT STACK                    ; *** STACK SEGMENT ***
        db      64 dup ('STACK   ')
STKSEG ENDS

;====== SECTION 4: Define code segment ====================================

CSEG SEGMENT PUBLIC 'code'              ; *** CODE SEGMENT ***
ASSUME  cs:cseg, ds:cseg, ss:stkseg, es:nothing

;====== SECTION 5: Declare variables for main procedure ===================
inputBuff      db MAX_BUFF_LEN dup (?), '$'
controlStr     db MAX_BUFF_LEN * 3 dup (?), ENDSTR

buff     db 7 dup(?)

accept   db '0123456789!*/%+-(){}[] <>&|^~',BS,CR,LF,ESCKEY ; all acceptable input chars

inputMsg db CR,LF,'Please enter an equation.',CR,LF
         db 'Hit , or enter an empty equation to exit.',CR,LF,'$'

parseMsg db 'Verifying and parsing the string...',CR,LF,'$'

; A lookup table of the priority of each of the operations
priority db 33 dup (0) ; control characters
         db 16         ; !
         db 0, 0, 0    ; ", #, $
         db 14         ; %
         db 8          ; &
         db 0          ; '
         db 2          ; (
         db 0          ; )
         db 14         ; *
         db 12         ; +
         db 0          ; ,
         db 12         ; -
         db 0          ; .
         db 14         ; /
         db 12 dup (0) ; numerals, :, ;
         db 10         ; <
         db 0          ; =
         db 10         ; >
         db 31 dup (0) ; ?, @, caps, [, \, ]
         db 6          ; ^
         db 29 dup (0) ; _, ', lower case letters, {
         db 4          ; |
         db 0          ; }
         db 17         ; ~
         db 69 dup (0) ; DELete, the extended charaters
         db 17         ; Long dash for negate
         db 59 dup (0) ; the rest of the extended characters

errMsg   db BEL,'The equation was not fully simplified due to an error.',CR,LF,'$'
errMsg0  db BEL,'Type 0 error: Divide by zero.',CR,LF,'$'
errMsg1  db BEL,'Type 1 error: Parenthesis mismatch.',CR,LF,'$'
errMsg2  db BEL,'Type 2 error: Input overflow.',CR,LF,'$'
errMsg3  db BEL,'Type 3 error: Calculation overflow.',CR,LF,'$'
errMsg4  db BEL,'Type 4 error: Missing operand',CR,LF,'$'
errMsg5  db BEL,'Type 5 error: Encountered a unary operator while expecting a binary operator.',CR,LF,'$'
errMsg6  db BEL,'Type 6 error: Encountered a number while expecting a binary operator.',CR,LF,'$'
errMsg7  db BEL,'Type 7 error: Encountered a binary operator while expecting a number.',CR,LF,'$'
errMsg8  db BEL,'Type 8 error: Encountered end of string while expecting a number.',CR,LF,'$'
errMsg9  db BEL,'Type 9 error: No numbers found in control string.',CR,LF,'$'
errMsg10 db BEL,'Type 10 error: Unknown operation encountered.',CR,LF,'$'
errMsg11 db BEL,'Type 11 error: Attempting to resolve parenthesis containing too many operands.',CR,LF,'$'
errMsg12 db BEL,'Type 12 error: Cannot take the factorial of a negative number.',CR,LF,'$'

public inputBuff, controlStr
public inputMsg, parseMsg, priority, buff, accept
public inputMsg, errMsg0, errMsg1, errMsg2, errMsg3, errMsg4, errMsg5
public errMsg6, errMsg7, errMsg8, errMsg9, errMsg10, errMsg11, errMsg12

;====== SECTION 6: Procedures ============================================


;Comment your funcitons!!!
Input proc near
        call LibInput  ; comment this call out and replace it with your own code
        ret
Input endp

;Comment your code!!!
CheckParens proc near
        call LibCheckParens ; comment this call out and replace it with your own code
        ret
CheckParens endp

;Comment loops in your code!!!
Parse proc near
        call LibParse
        ret
parse endp

 
;Comment the registers that are used as variables!!!
CheckDone proc near
        call LibCheckDone
        ret
CheckDone endp


;Comment code that is confusing!!! 
FindInner proc near
        call LibFindInner
        ret
FindInner endp

;Don't write a comment for every line!!!
FindOne proc near
        call LibFindOne
        ret
FindOne endp

;Don't write paragraphs of comments!!!
SolveOne proc near
        call LibSolveOne
        ret
SolveOne endp

;Write your comments well!!!
GetOp1 proc near
        call LibGetOp1
        ret
GetOp1 endp

 
;When you duplicate code, don't duplicate commentes!!! 
GetOp2 proc near
        call LibGetOp2
        ret
GetOp2 endp 


MAIN PROC NEAR
     mov     ax, cseg                ; Initialize Default Segment register
     mov     ds, ax  
  
  begin:
     call    Input
     jc      FinalExit  ; if done, exit
     
     mov     dx, offset parseMsg
     call    dspmsg

     call    checkParens
     jc      Error 

     call    Parse
     jc      Error      ; on err, start over
  solve:
     call    LibDspCtrlStr ; show the current state of calculation
     
     call    CheckDone
     jc      Error
     jz      begin
     
   continue: 
     call    kbdin       ; pause before continueing
     cmp     al, ESCKEY
     jz      FinalExit
     cmp     al, LF
     jz      continue

     ;find and solve the operator of highest precidence
     call    FindInner
     call    FindOne
     jc      Error
     call    SolveOne
     jc      Error
     
     jmp solve
     
  Error:
     mov     dx, offset errMsg
     call    dspmsg
     jmp     begin     
     
FinalExit:
     call    mp2xit                  ; Exit to DOS

MAIN ENDP

CSEG ENDS
        END MAIN