Presentation is loading. Please wait.

Presentation is loading. Please wait.

CSCE 121: Introduction to Program Design and Concepts Dr. J

Similar presentations


Presentation on theme: "CSCE 121: Introduction to Program Design and Concepts Dr. J"— Presentation transcript:

1 CSCE 121:509-512 Introduction to Program Design and Concepts Dr. J
CSCE 121: Introduction to Program Design and Concepts Dr. J. Michael Moore Spring 2015 Set 9: Calculator Program Based on slides created by Bjarne Stroustrup and Jennifer Welch

2 Purpose of Chapters 6 and 7
Give an example of how to write a program to solve a non-trivial problem Analysis: refine understanding of the problem Design: create overall structure of program Implementation: write code, debug, test Repeat!

3 General Strategy What is the problem to be solved?
Is the statement clear? Is it doable? Break the problem into manageable parts Consider tools, libraries that can help Build a small, limited version solving a key part of the problem May have to try multiple times Build full-scale solution Ideally rely on prototype

4 Problem: A Simple Calculator
User enters an arithmetic expression at the keyboard Program evaluates the expression and prints result to the screen Repeat until user wants to quit

5 Give it a Try ?

6 Don’t Reinvent the Wheel!
Computers have been evaluating expressions for over 50 years There has to be a solution already Do some background research to figure out how to approach this problem Describe the problem with a grammar Write a parser

7 Grammar Calculator program needs to know if the input string of characters is a properly formatted arithmetic expression (3+4)/7 is good 3+4)/7 is bad A grammar is a collection of “rules” that specify a set of strings that are properly formatted (according to the rules)

8 Rules of a Grammar Each rule has the form (LHS)  (RHS)
To apply the rules: Start with a special LHS (the “start symbol”) Continually do replacements according to the rules until you can’t do any more End result is a properly formatted string (according to the given rules) RHS is a mix of “terminals” and “non-terminals” Terminals are characters that appear in the final result Nonterminals can be replaced; they appear in LHS

9 An Example Grammar Design a grammar that will generate all (and only) strings of parentheses that are “balanced” S  ( S ) // S is start symbol, ( and ) are terminals S  S S S  empty string // this is how to stop

10 Another Example Grammar
Small Java-like fragment (| means or) Block  { StmtList } | { } // { and } are terminals StmtList  Stmt | StmtList Stmt Stmt  Block | while (Cond) Stmt | // while, (, ) are terminals AssmtStmt | return | // return is a terminal Cond  x == y // x, ==, and y are terminals AssmtStmt  z := a + b // z, :=, a, + and b are terminals

11 Parse Tree Use grammar on previous slide to draw a parse tree for {
while (x == y) z := a + b return }

12 Applications of Grammars
Describe programming language Crucial part of compiler Describe natural languages Diagramming sentences Expert systems: programs that perform tasks with a lot of domain knowledge (e.g., medicine) Domain knowledge can be modeled as a set of rules Logic programming: special kind of programming language (e.g., Prolog) supports this way of writing programs. Program reasons backwards from a goal by chaining rules together until getting to the initial assumptions Even describe music, dance, structure of RNA, 2-D graphics!

13 A Grammar for Arithmetic Expressions
Expression  Term | Expression + Term | Expression – Term Term  Primary | Term * Primary Term / Primary Primary  Number | ( Expression ) Number  floating-point literal (rely on C++ for this) Terminals are +, 1, *, /, (, ), and the characters that make up floating-point literals (we’re not going into details of the grammar for them; instead, we’ll rely on the built-in capabilities of C++)

14 Parse Tree Examples Draw parse trees for the following expressions using the grammar on previous slide: 2 2+3 2+3*7 (2+3)*7

15 How Does Having a Grammar Help?
We will write a “recursive descent parser”, based on this grammar to Identify if input is properly formatted and If so, evaluate the expression Take a short detour about recursion…

16 Recursion Grammars are recursive structures
Definition in terms of itself To avoid never terminating, must have stopping cases A natural way to compute recursive structures is to use recursion Function that calls itself either directly or indirectly

17 Recursion in Programming
Advantage: can simplify thinking about a problem and help develop an elegant solution Especially if the problem is already framed in a recursive style Disadvantage: straightforward implementations can be inefficient Closely related to the notion of proof by induction in mathematics

18 Recursive Definition of Factorial
Compute factorial of positive integer n, denoted n! Mathematical definition #1 (nonrecursive): n! = 1*2*3*…*(n-1)*n Mathematical definition #2 (recursive): if n = 1, then n! = 1 if n > 1, then n! = n*(n-1)! Rule 1 is the stopping case Rule 2 defines factorial in terms of factorial, but OK because we are getting closer to stopping case

19 Recursive Factorial Program
Follow the recursive definition int fact(int n) { if (n==1) return 1; int rest = fact(n-1); // calling self int result = n*rest; return result; } Each call to fact does a little bit of work itself (checking for stopping case and one multiplication) and passes on the bulk of the work to the next recursive call

20 Memory Management with Recursive Programs
During execution, each call to fact gets a separate stack frame, with separate copies of the formal parameter n and local variables rest and result Overhead of recursion: Space: extra copies of variables in stack frames Time: for function calls

21 Recursion and Iteration
Both involve repetition: Iteration has explicit repetition, in the loop Recursive has implicit repetition, in the sequence of function calls Iteration has less overhead Can convert recursive solution (which may be easier to find in some cases) to iterative solution

22 Rules for Recursive Functions
Always have at least one stopping case (also called base case) Each recursive call must get you closer to a stopping case

23 Direct vs. Indirect Recursion
Direct recursion is when a function calls itself As in factorial example Indirect recursion (or mutual recursion) is when function f1 calls function f2, …, which calls function f1 Path can be of any length We’ll see an example of this in the upcoming calculator program

24 Back to Calculator Problem: Grammar Rules
Use the grammar as a guide in developing a set of mutually recursive functions Expression  Term | Expression + Term | Expression – Term Term  Primary | Term * Primary Term / Primary Primary  Number | ( Expression ) Number  floating-point literal (rely on C++ for this) expression(): deals with + and -, calls term term(): deals with * and /, calls primary primary(): deals with ( and ), calls expression All three return value All three call something to get terminals… Have a function for each nonterminal

25 Tokens Need some code to provide the terminals of the grammar from the user’s input Call each terminal a Token: +,-,*,/, (, ), floating-point literal For now, assume we have a class Token and each Token object t has t.kind and t.value (numeric value for floating-point literal) For now, assume we have a function get() that reads characters from cin and composes Tokens

26 Token struct struct Token { char kind; double value; Token(char ch) : kind(ch), value(0) {} Token(char ch, double val) : kind(ch), value(val) {} }; Token get_token() { /* collect characters from cin and turn into a Token */ }

27 Expression: First Attempt
Expression  Term | Expression + Term | Expression – Term Expression: First Attempt double expression() { /* try to get a Term, if that doesn’t work (?) try to get an Expression by recursively calling expression, then try to get + or -, then try to get a Term */ } How can we tell if getting a Term failed? Even worse, we are setting ourselves up for an infinite loop: recursive call that does not get us any closer to the stopping case (end of input string)

28 Expression: Second Attempt
Expression  Term | Expression + Term | Expression – Term Expression: Second Attempt Problem with this grammar is that it has “left recursion”: Calls itself without having consumed any of the input string Get around the problem by converting from recursion to iteration “Unrolling” the recursion shows that we can rephrase the rule as Expression  Term +|- Term +|- Term +|- … Term Swapping the order of Term and Expression (i.e., Expression  Term + Expression) causes a different problem: order of operations will no longer be left to right, but right to left.

29 Expression Function Expression  Term +|- Term +|- … Term
double expression() { double val = term(); // get first term while (true) { Token t = get_token(); // get next token switch (t.kind) { case ‘+’: val += term(); break; // add case ‘-’: val -= term(); break; // subtract default: return val; } // pattern changes }

30 Term Same problem with left recursion here. Unroll to get
Term  Primary | Term * Primary | Term / Primary Term Same problem with left recursion here. Unroll to get Term  Primary *|/ Primary *|/ … Primary

31 Divide by 0? Term Function Term  Primary *|/ Primary *|/ … Primary
double term() { double val = primary(); while (true) { Token t = get_token(); switch (t.kind) { case ‘*’: val *= primary(); break; case ‘/’: val /= primary(); break; default: return val; } } Divide by 0?

32 Term Function Version 2 double term() { double val = primary(); while (true) { Token t = get_token(); switch (t.kind) { case ‘*’: val *= primary(); break; case ‘/’: { // scope for variable d double d = primary(); if (d==0) error(“divide by zero”); val /= d; break;} default: return val; } }

33 Primary Function Primary  Number | ( Expression )
double primary() { Token t = get_token(); switch (t.kind) { case ‘(‘: { // scope for d and t double d = expression(); t = get_token(); if (t.kind != ‘)’ error(“’)’ expected”); return d; } case ‘8’: return t.value; // ’8’ means number default: error(“primary expected”); }

34 How to Start? main calls expression, since Expression is the start symbol of the grammar. int main() try { cout << expression() << endl; } catch(runtime_error& e) { cerr << e.what() << endl; return 1; catch(...) { cerr << “exception\n”; return 2;

35 Program Organization main() expression() term() primary() get_token()

36 Try it Out See calc1.cpp Doesn’t work
Let’s look more carefully at expression()

37 Expression Function Revisited
Expression  Term +|- Term +|- … Term double expression() { double val = term(); // get first term while (true) { Token t = get_token(); // get next token switch (t.kind) { case ‘+’: val += term(); break; // add case ‘-’: val -= term(); break; // subtract default: return val; } // pattern changes } If token t is not + or – , we just throw it away. This can’t be a good idea.

38 Putting Back a Token We need a way to “put back” a token that we are not yet ready to deal with Issue arises in expression() term() We need a concept of a “token stream” Higher-level abstraction of the input stream, where characters are grouped into tokens Ability to put back a token (analogous to cin)

39 Token_stream class Token_stream { bool full; // is there a Token in the buffer? Token buffer; // keep put-back Token here public: Token_stream() : full(false), buffer(0) {} Token get(); // next slide void putback(Token t) { if (full) error(“putback into full buffer”); buffer = t; full = true; } };

40 Getting a Token Token Token_stream::get() {
if (full) { full = false; return buffer;} char ch; cin >> ch; switch(ch) { case '(': case ')': case '+': case '-': case '*': case '/': return Token(ch); case '.': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { cin.putback(ch); // put digit back into the input stream double val; cin >> val; // let C++ do work to get floating pt num return Token('8',val); } default: error("Bad token");

41 Try it Out See calc2.cpp Notices bad syntax
Doesn’t print out answer unless extra character is typed (in certain cases)

42 Fixes and Additions Let user repeatedly enter expressions
Have user enter special character ‘;’ to make program print out value See calc3.cpp int main() try { double val = 0; while (true) { Token t = ts.get(); if (t.kind == ';') // modify Token::get() also cout << val << endl; else ts.putback(t); val = expression(); } ...

43 Acknowledgments Photo on slide 1: by John Koetsier, licensed under CC BY-NC-ND 2.0 Material on grammars from Automata, Computability, and Complexity: Theory and Applications, by Elaine Rich Slides are based on those for the textbook:


Download ppt "CSCE 121: Introduction to Program Design and Concepts Dr. J"

Similar presentations


Ads by Google