CSCI 200 Lab 4 Evaluating infix arithmetic expressions

Similar documents
CSCI 204 Introduction to Computer Science II. Lab 6: Stack ADT

STACKS AND QUEUES. Problem Solving with Computers-II

CS 211 Programming Practicum Spring 2017

Problem 1: Building and testing your own linked indexed list

Project 1: Implementation of the Stack ADT and Its Application

CSIS 10B Lab 2 Bags and Stacks

STACKS. A stack is defined in terms of its behavior. The common operations associated with a stack are as follows:

Stacks. Chapter 5. Copyright 2012 by Pearson Education, Inc. All rights reserved

CS 211 Programming Practicum Spring 2018

Postfix Notation is a notation in which the operator follows its operands in the expression (e.g ).

Stacks. Chapter 5. Copyright 2012 by Pearson Education, Inc. All rights reserved

CSCI Lab 9 Implementing and Using a Binary Search Tree (BST)

CS 211 Programming Practicum Fall 2018

Stacks. stacks of dishes or trays in a cafeteria. Last In First Out discipline (LIFO)

CS 206 Introduction to Computer Science II

March 13/2003 Jayakanth Srinivasan,

In this chapter you ll learn:

Formal Languages and Automata Theory, SS Project (due Week 14)

CMPSCI 187 / Spring 2015 Postfix Expression Evaluator

CSCI 200 Lab 1 Implementing and Testing Simple ADTs in Java

CPSC 211 Data Structures & Implementations (c) Texas A&M University [ 165] Postfix Expressions

CSCI 200 Lab 3 Using and implementing sets

Top of the Stack. Stack ADT

CSCI 200 Lab 11 A Heap-Based Priority Queue

Operators and Expressions

Introduction to Programming Using Java (98-388)

Special Section: Building Your Own Compiler

The Stack and Queue Types

Collections Chapter 12. Instructor: Scott Kristjanson CMPT 125/125 SFU Burnaby, Fall 2013

CSCI 200 Lab 2 Inheritance, Polymorphism & Data Streams

An Introduction to Trees

Expressions and Data Types CSC 121 Spring 2015 Howard Rosenthal

infix expressions (review)

Assignment 5. Introduction

Lab 7 1 Due Thu., 6 Apr. 2017

BBM 201 DATA STRUCTURES

09 STACK APPLICATION DATA STRUCTURES AND ALGORITHMS REVERSE POLISH NOTATION

Data Structure using C++ Lecture 04. Data Structures and algorithm analysis in C++ Chapter , 3.2, 3.2.1

Infix to Postfix Conversion

Stack ADT. ! push(x) puts the element x on top of the stack! pop removes the topmost element from the stack.

Postfix (and prefix) notation

CSCI 136 Data Structures & Advanced Programming. Fall 2018 Instructors Bill Lenhart & Bill Jannen

3. Java - Language Constructs I

CSE 230 Intermediate Programming in C and C++

CISC-235. At this point we fnally turned our atention to a data structure: the stack

Advanced Java Concepts Unit 3: Stacks and Queues

Operators. Java operators are classified into three categories:

19 Much that I bound, I could not free; Much that I freed returned to me. Lee Wilson Dodd

Stack. 4. In Stack all Operations such as Insertion and Deletion are permitted at only one end. Size of the Stack 6. Maximum Value of Stack Top 5

Stack Applications. Lecture 27 Sections Robb T. Koether. Hampden-Sydney College. Wed, Mar 29, 2017

BBM 201 DATA STRUCTURES

IV. Stacks. A. Introduction 1. Consider the 4 problems on pp (1) Model the discard pile in a card game. (2) Model a railroad switching yard

COMP-421 Compiler Design. Presented by Dr Ioanna Dionysiou

A complex expression to evaluate we need to reduce it to a series of simple expressions. E.g * 7 =>2+ 35 => 37. E.g.

CS Introduction to Data Structures How to Parse Arithmetic Expressions

Stack Abstract Data Type

Expressions and Data Types CSC 121 Fall 2015 Howard Rosenthal

Programming, Data Structures and Algorithms Prof. Hema A Murthy Department of Computer Science and Engineering Indian Institute of Technology, Madras

The Bucharest University of Economic Studies. Data Structures. ADTs-Abstract Data Types Stacks and Queues

Lecture 4: Stack Applications CS2504/CS4092 Algorithms and Linear Data Structures. Parentheses and Mathematical Expressions

Stack Applications. parsing a postfix expression evaluating postfix expressions. converting infix sums from infix to postfix

Also, recursive methods are usually declared private, and require a public non-recursive method to initiate them.

A Simple Syntax-Directed Translator

Weiss Chapter 1 terminology (parenthesized numbers are page numbers)

Announcements. Lab Friday, 1-2:30 and 3-4:30 in Boot your laptop and start Forte, if you brought your laptop

Top of the Stack. Stack ADT

Introduction to Computer Science II (ITI 1121) Midterm Examination

Stacks Calculator Application. Alexandra Stefan

Contents. Figures. Tables. Examples. Foreword. Preface. 1 Basics of Java Programming 1. xix. xxi. xxiii. xxvii. xxix

Chapter 3 Structure of a C Program

Stacks, Queues and Hierarchical Collections. 2501ICT Logan

PA3 Design Specification

Stack and Its Implementation

Stacks, Queues (cont d)

Data Structures and Algorithms

ADVANCED DATA STRUCTURES USING C++ ( MT-CSE-110 )

Data Abstraction and Specification of ADTs

Lecture 4 Stack and Queue

CSC 1214: Object-Oriented Programming

CS W3134: Data Structures in Java

Stacks Fall 2018 Margaret Reid-Miller

10/26/2017 CHAPTER 3 & 4. Stacks & Queues. The Collection Framework

Assignment 4 Publication date: 27/12/2015 Submission date: 17/01/ :59 People in-charge: R. Mairon and Y. Twitto

Stacks II. Adventures in Notation. stacks2 1

Topic 14. The BinaryTree ADT

CS 211. Project 5 Infix Expression Evaluation

Stacks. Manolis Koubarakis. Data Structures and Programming Techniques

[CS302-Data Structures] Homework 2: Stacks

CS 171: Introduction to Computer Science II. Stacks. Li Xiong

Stacks (Section 2) By: Pramod Parajuli, Department of Computer Science, St. Xavier s College, Nepal.

Context-Free Grammar. Concepts Introduced in Chapter 2. Parse Trees. Example Grammar and Derivation

Programming Lecture 3

Lecture Data Structure Stack

-The Hacker's Dictionary. Friedrich L. Bauer German computer scientist who proposed "stack method of expression evaluation" in 1955.

Queues Fall 2018 Margaret Reid-Miller

Some Applications of Stack. Spring Semester 2007 Programming and Data Structure 1

AP COMPUTER SCIENCE JAVA CONCEPTS IV: RESERVED WORDS

Object Oriented Programming: In this course we began an introduction to programming from an object-oriented approach.

Computer Science 62. Bruce/Mawhorter Fall 16. Midterm Examination. October 5, Question Points Score TOTAL 52 SOLUTIONS. Your name (Please print)

Introduction to Computer Science II (ITI 1121) Midterm Examination

Transcription:

CSCI 200 Lab 4 Evaluating infix arithmetic expressions Please work with your current pair partner on this lab. There will be new pairs for the next lab. Preliminaries In this lab you will use stacks and queues to implement a number of programs which, combined, evaluate infix integer arithmetic expressions such as 21 + 4 * 5. You ll first implement a tokenizer program that takes a string representing a complete infix expression and breaks it into discrete pieces called tokens. (In the expression 21 + 4 * 5, the tokens are 21, +, 4, *, and 5) Once we have a sequence of tokens, we can go on to convert the sequence into a form that a computer can evaluate more easily and then perform the evaluation. Here is an example of what we're doing. Consider the previous infix expression: 21 + 4 * 5. It's called an infix expression because the operators are placed between the operands: + between 21 and 4 and * between 4 and 5. The tokenizer separates the expression into the tokens 21, +, 4, *, and 5, and places them in a queue in that order. Next, an infix-to-postfix converter program reorders the tokens so that the expression is in postfix order with the operators following the operands. For our example, this process results in the expression: 21 4 5 * + (meaning that the multiplication (*) of 4 by 5 should occur before the addition (+) of 21 and the result of the multiplication). If the infix expression contains parentheses, the conversion process eliminates them since postfix expressions don t need them for evaluation. Finally, a postfix-expression evaluator program uses a stack to evaluate the expression: 21 4 5 * + = 21 20 + = 41 Note that the order of operators in the postfix expression and the order of operations in the evaluation process follow the same order that we would use to evaluate the original infix expression. Take some time to work through an exercise individually using pencil and paper; if you can t do this by hand then you SHOULD NOT even TRY to program it. Start by producing the list of tokens the tokenizer program should give for the input expression 344+6*90/3 + (9-3)%5. Using this list of tokens, determine the reordered list of tokens the infix-to-postfix program should produce as its output. (Remember that it removes parentheses) Finally, use this second list of tokens to determine the value that the postfix-expression evaluator program produces; you should get 525. Once you have completed the exercise on your own, compare your solution with your pair partner s, and correct any problems in your solutions. Show your solution to the exercise to the lab TA or instructor before you proceed.

Part 1: Building and Testing a Token class hierarchy Rather than just using strings to represent tokens, we will classify the tokens into a class hierarchy of token types. The root of the hierarchy will be a Token interface. There are two basic types of tokens: arithmetic operators like + and * and operands like 21 and 4; in addition, there are two special token types: left parentheses and right parentheses. The UML diagram below shows the complete hierarchy, as we will implement it. <interface> Token + Type : static enum { OPERATOR, OPERAND, LEFT_PARENTHESIS, RIGHT_PARENTHESIS } + gettype() : Token.Type + tostring() : String +--------------------------------+--------------------------+--------------------+ <abstract class> <class> <class> <abstract class> Operator LeftParenthesis RightParenthesis Operand + getprecedence(): int + getvalue(): int + evaluate(operand, Operand): Operand +-----------+-----------+-----------+-----------+ <class> <class> <class> <class> <class> <class> Plus Minus Times Divide Remainder IntegerLiteral Start by moving the expressions package from this lab folder into your JavaPackages folder (but keep the classes InfixPostfixDriver, TokenizerTest, InfixPostfixTest and PostfixEvaluatorTest in your lab folder). Implement the above hierarchy inside this expressions package as you did for the worker hierarchy, but note that this hierarchy is really much simpler, even if it does contain more classes. The complete Token interface is given. Most of the methods will just return a constant value or the value of an instance variable. Implement each method at the highest possible level in the hierarchy that doesn't require using if or switch statements; declare methods in abstract classes as abstract if you don t implement them there. (There s no need to re-declare abstract methods already declared in a super-class or super-interface.) Please note the following: Operator classes: getprecedence: this method returns a precedence of 1 for the Plus and Minus classes, 2 for Times, Divide and Remainder (saying that operators *, / and % have higher precedence over + and -) Declare constants for the precedence levels in the Operator abstract class. evaluate: this method performs the appropriate operation on the values of the two operands and then returns a new IntegerLiteral operand constructed on the result of the operation

Operand classes: The abstract class Operand contains only abstract methods; it is present to allow for more kinds of operands in the future. The IntegerLiteral class should have an int instance variable to hold the value of the literal. One IntegerLiteral class constructor should take a String parameter and throw an IllegalArgumentException if the string can't be parsed into an int (using Integer.parseInt). A second constructor should take an int parameter. Leaf classes (the classes across the bottom of the diagram and the two parenthesis classes): Override the tostring method for each of the leaf classes. For example, the Divide class's tostring method should return "/", while for IntegerLiteral it should return a String representing the integer value returned by its getvalue method. Include appropriate Javadoc comments for each class. Include a package statement in all these class files, and make sure you save all classes in the expressions package inside your JavaPackages folder. This lab requires the use of the stack and queue data structures. We will use the zhstructures versions of both: the interfaces ZHStack and ZHQueue with the classes ZHArrayStack and ZHArrayQueue. All these interfaces and classes are generic over the class parameter <ElementType>. Important note about casting: There will be places where you need to treat a token specifically as a particular kind of token; for instance, you may need to call the getvalue method of an Operand token. If the variable type you're using is more general than the actual type of the token (probably Token) you may get a compiler error telling you that an object of that type (Token) doesn't have a method like that (getvalue). If you know that the object is in fact a more specific subtype (Operand), you can cast it to the subtype and the compiler error will change to a warning about an unchecked cast. You'll probably need to do that several times in this lab. Be sure that all code in this part of the lab compiles without errors before you go on. You may show this part to the TA or lab instructor at this point.

Part 2: A Tokenizer for expressions Complete the supplied Tokenizer class inside the expressions package using the following guidelines. The maketoken method gets a string that should represent one of the tokens in the hierarchy above. It determines which type of token it is, calls the appropriate token constructor and returns a new token object (e.g., new Plus() is created and returned for the string "+"). You may want to use a switch/case statement defined over the first character of the parameter string to determine the type of token. All tokens except integer literals come from strings of a single character, so the method should throw an exception if the string length is not one unless the first character is a digit. (We're not allowing negative literals in this lab; use (0 5) to get 5.) The method should also throw an exception if the string doesn't match any of the token types. Recall that the method call Character.isDigit(c), where c is a char, returns true if c is a digit. The parsestring method gets a string and scans it for tokens. Since there may or may not be whitespace between adjacent tokens, it is necessary to write the tokenizer by hand rather than using Java s Scanner or StringTokenizer classes. Here's a pseudo-code version of the algorithm: algorithm parsestring( tokensstr : String ) : ZHQueue<Token> set queue a new empty ZHArrayQueue<Token> set i 0 while i < the length of tokensstr do if the character at location i in tokensstr is an operator or a parenthesis then call maketoken on a substring of tokensstr consisting of that character add the resulting token to the queue increment i else if character at location i in tokensstr is a digit then loop to find the next position j tokensstr.length that is not a digit call maketoken on a substring of tokensstr from location i to j-1 inclusive add the resulting token to the queue set i j else if character at location i in tokensstr is whitespace then increment i else [it s an illegal character at the start of a token] throw an IllegalArgumentException return the queue Note: you can use the Character.isDigit method to test for digits and the Character.isWhitespace method to test for whitespace. You can use the substring method to get substrings, but remember that the character at the terminal position in the substring method is not included in the result. (e.g., str.substring(i, j) returns a string with the characters of str in positions i to j-1.)

When you have completed the Tokenizer class so that it compiles without errors, use the provided TokenizerTest.java JUnit test class (available in your lab folder) to test that the classes you have implemented for this lab so far work and interact properly. This test class is complete and tests the Tokenizer class, which uses all the Token classes.

Part 3: Converting an infix expression to a postfix expression In this part, you'll implement a class with a static method that takes a queue of tokens in infix order and returns a queue of tokens representing the same expression in postfix order. You ll then use a JUnit test class and a driver program to test your conversion. Create an InfixToPostfix class inside your expressions package in your JavaPackages folder. Include the following three methods: // takes an infix queue and returns the equivalent postfix queue public static ZHQueue<Token> convert(zhqueue<token> infixqueue); private static void processrightparenthesis(zhstack<token> opstack, ZHQueue<Token> postfixqueue); private static void processoperator(zhstack<token> opstack, ZHQueue<Token> postfixqueue, Operator op); The convert method is the only public method of the InfixToPostfix class. It is the method that performs the conversion. It works by processing tokens from the infixqueue one-by-one, moving operands (IntegerLiteral tokens, in this case) directly into a queue called postfixqueue and pushing operators and left parentheses onto a stack called opstack. Operators require special handling, since we move them to postfixqueue only after their second operand, and we must account for operator precedence before we push the operator onto opstack. When we encounter a right parenthesis, we have to process any operators pushed onto the stack since the last left parenthesis. The pseudo-code algorithm for this method is as follows:

algorithm convert(infixqueue : ZHQueue<Token>) : ZHQueue<Token> if infixqueue is empty then throw a new IllegalArgumentException with the message "Empty infix expression" set postfixqueue a new empty ZHArrayQueue<Token> set opstack a new empty ZHArrayStack<Token> while infixqueue is not empty do dequeue nexttok from infixqueue if nexttok is an operand (i.e. nexttok instanceof Operand) then enqueue nexttok to postfixqueue else if nexttok is a left parenthesis then push nexttok onto opstack else if nexttok is a right parenthesis then call processrightparenthesis else [ nexttok is an operator ] call processoperator while opstack is not empty do pop nextop from opstack if nextop is a left parenthesis then throw a new IllegalArgumentException with the message "Unmatched left parenthesis" enqueue nextop to postfixqueue return postfixqueue To make the code for the convert method easier to implement and understand, we broke out two parts of the algorithm into separate sub-algorithms: processrightparenthesis and processoperator. We call these methods are called when we encounter a right parenthesis or an operator in the infix expression queue, respectively. Since they only make sense when in the context of the convert method, we make them private methods.

When we encounter a right parenthesis, all we have to do is pop all the operands from opstack and enqueue them into postfixqueue until we find the matching left parenthesis on the stack; if we reach the bottom of the stack before finding a matching left parenthesis, the right parenthesis must be unmatched and we throw an exception. The algorithm for the processrightparenthesis method is as follows: algorithm processrightparenthesis(opstack : ZHStack<Token>, postfixqueue : ZHQueue<Token>) : void while opstack is not empty and top of opstack is not a left parenthesis do pop an element from opstack and enqueue it to postfixqueue if opstack is empty then throw an IllegalArgumentException with the message "Unmatched right parenthesis" pop the left parenthesis from opstack and discard When we encounter an operator, we need to push it onto opstack. First, however, we need to check any operators already on the stack; as long as there is an operator on the top of the stack with greater or equal precedence to the new operator, we need to pop that operator and enqueue it to postfixqueue. Once we reach the bottom of the stack or find a left parenthesis on the top of the stack, we can push the new operator and return. The algorithm for the processoperator method is below: algorithm processoperator(opstack : ZHStack<Token>, postfixqueue : ZHQueue<Token>, op : Operator) : void if opstack is not empty then set toptok top element of opstack [ peek without popping ] while opstack is not empty and toptok is not a left parenthesis and precedence(op) precedence(toptok) do pop element from opstack and enqueue it to postfixqueue if opstack is not empty then toptok top element of opstack [ again, peek without popping ] push op onto opstack When you are done implementing this algorithm, use the provided InfixPostfixTest.java test program (in your lab folder) to test your InfixToPostfix program. Additionally, the InfixPostfixDriver.java program tests the conversion; it requests the user to input a string representing an infix arithmetic expression, calls Tokenizer.parseString on the input, calls InfixToPostfix.convert on the tokenizer output queue, and then displays the resulting postfix queue using the iterator. You may use this program to test your InfixToPostfix class more thoroughly. Verify that it correctly converts well-formed infix expressions to the equivalent postfix expression and that it throws an exception on mismatched parentheses. You

should predict your expected outcome for each test and then check whether you got the expected result.

Part 4: Evaluating a postfix expression In this part, you'll implement a class with a static method that takes a queue of tokens in postfix order and returns an int that is the value of the expression. You ll then use a JUnit test class and a driver program to test the evaluation. Create a PostfixEvaluator class inside your expressions package in your JavaPackages folder with the following method: public static int evaluate(zhqueue<token> postfixqueue); This evaluate method works by processing tokens from the postfixqueue, again one-by-one, in this case pushing operands (not operators) onto a stack. Whenever we encounter an operator, there should be two operands for that operator already on the stack. We pop the two operands (note that the right operand would pop before the left operand because we re using a stack), perform the operation, and push the result onto the stack. When the whole queue is processed, the stack should contain a single IntegerLiteral representing the value of the expression. The algorithm for evaluate is as follows: algorithm evaluate(postfixqueue : ZHQueue<Token>) : int if postfixqueue is empty then throw an IllegalArgumentException with the message "Empty postfix expression" set opstack an empty ZHArrayStack<Operand> while postfixqueue is not empty do dequeue nexttok from postfixqueue if nexttok is an operand then push nexttok onto opstack else [ nexttok is an operator ] if opstack is empty then throw an IllegalArgumentException with the message "Operator with no operands" pop rightoperand from opstack if opstack is empty then throw an IllegalArgumentException with the message "Operator with only one operand" pop leftoperand from opstack call evaluate(leftoperand, rightoperand) on the operator nexttok push the result back onto opstack pop result from opstack if opstack is not empty then throw an IllegalArgumentException with the message "Too many operands" return result

To test your work, use the PostfixEvaluatorTest.java test program. Again, you should also use the provided InfixPostfixDriver.java program to test your code more thoroughly. The driver program calls the PostfixEvaluator.evaluate method and displays the result but first you need to uncomment the last two lines in the driver program. You should keep the display of the postfix expressions for completeness and to aid in error checking. Test your program on a wide variety of infix expressions (both well-formed and ill-formed) to verify that it correctly evaluates the expressions.