slides[]

advertisement
Case Study:
A Recursive Descent Interpreter
Implementation in C++
(Source Code Courtesy of Dr. Adam Drozdek)
Payap University
ICS220 - Data Structures and Algorithm Analysis
Instructor: Dr. Ken Cosh
Analysis and Presentation by Rob Agle
First, let’s clear up some
terminology…
Interpreter
• In general, a compiler is a program that converts an entire
program from high level source code into some lower level
representation (assembly or machine code for example).
• An interpreter on the other hand, traditionally translates high
level instructions and executes them on the fly (at runtime).
• The lines between these two concepts are blurring however…
Examples of “Interpreted” Languages
Python
Pearl
JavaScript
Ruby
Smalltalk
Java
Interpreter
For our purposes – we can simply say that our
interpreter will be used to translate and execute
one instruction statement at a time.
Interpreter
For our purposes – we can simply say that our
interpreter will be used to translate and execute
one instruction statement at a time.
Interpreter
For our purposes – we can simply say that our
interpreter will be used to translate and execute
one instruction statement at a time.
Interpreter
Things our interpreter understands:
Variable Names: Any alphanumeric string
Operators: + - / * =
Commands: Print, Status, End
Recursive Descent
A process that allows us to descend – or “go down” to
lower and lower levels of complexity via recursion, and
then work “backwards” towards a solution once all the
pieces are in place.
Recursive Descent
A process that allows us to descend – or “go down” to
lower and lower levels of complexity via recursion, and
then work “backwards” towards a solution once all the
pieces are in place.
Recursive Descent
For Example:
var = 2*(3+5);
Recursive Descent
For Example:
var = 2*(3+5);
Recursive Descent
For Example:
The statement: var = 2*(3+5); can be parsed and broken
down into its individual pieces using recursion.
Recursive Descent
For Example:
The statement: var = 2*(3+5); can be parsed and broken
down into its individual pieces using recursion.
Recursive Descent
var = 2*(3+5);
Don’t worry about how, just imagine we magically use
some combination of direct and indirect recursion
to break down the above statement into the
following…
Recursive Descent
var = 2*(3+5);
Don’t worry about how, just imagine we magically use
some combination of direct and indirect recursion
to break down the above statement into the
following…
var = 2 * ( 3 + 5 ) ;
ID
var = 2 * ( 3 + 5 ) ;
ID
var = 2 * ( 3 + 5 ) ;
Operator
Operator
Operator
ID
Factor
Factor
Factor
var = 2 * ( 3 + 5 ) ;
Operator
Operator
Operator
Term
Factor
ID
Term
Factor
Factor
var = 2 * ( 3 + 5 ) ;
Operator
Operator
Operator
Expression
Expression
Term
Factor
ID
Term
Factor
Factor
var = 2 * ( 3 + 5 ) ;
Operator
Operator
Operator
Expression
Expression
Term
Factor
ID
Term
Factor
Factor
var = 2 * ( 3 + 5 ) ;
Operator
Operator
Operator
Data:
• a list of all ID’s (variables)
•An array of characters to store an input statement
What do we need (object wise) to
accomplish
this?
Functionality:
•A way to get the input statement from the user
•A way to parse the input, get values for expressions (if any) and
its composite parts (terms, factors – if any) and perform indicated
operations.
•A few “black boxes”…
Data
What do we need (object wise) to
accomplish this?
Data:
• a list of all ID’s (variables)
•An array of characters to store an input statement
Functionality:
•A way to get the input statement from the user
•A way to parse the input, get values for expressions (if any) and
its composite parts (terms, factors – if any) and perform indicated
operations.
•A few “black boxes”…
Functionality
So – let’s go back to our concrete example
and trace the program…
Trace: Input -> var = 2*(3+5);
Trace: Input -> var = 2*(3+5);
Trace: Input -> var = 2*(3+5);
statement.idList =
Statement.ch =
Trace: Input -> var = 2*(3+5);
statement.idList =
Runtime Stack
Statement.ch =
getStatement()
e=
id =
command =
Trace: Input -> var = 2*(3+5);
statement.idList =
Runtime Stack
Statement.ch = var
getStatement()
e=
id = var
command = VAR
Trace: Input -> var = 2*(3+5);
statement.idList =
Runtime Stack
Statement.ch = =var
R
expression()
getStatement()
e=
?
id = var
command = VAR
Trace: Input -> var = 2*(3+5);
statement.idList =
Runtime Stack
Statement.ch = =
R
term()
R
expression()
t= ?
getStatement()
e=
?
id = var
command = VAR
Trace: Input -> var = 2*(3+5);
statement.idList =
Statement.ch = =
R
Runtime Stack
R
factor()
term()
f= ?
R
expression()
t= ?
getStatement()
e=
?
id = var
command = VAR
Trace: Input -> var = 2*(3+5);
statement.idList =
Statement.ch = =
R factor()
var = 1.0
minus = 1.0
id =
Runtime Stack
R
term()
f= ?
R
expression()
t= ?
getStatement()
e=
?
id = var
command = VAR
Trace: Input -> var = 2*(3+5);
statement.idList =
Statement.ch = 2=
R factor()
var = 1.0
minus = 1.0
id =
Runtime Stack
R
term()
f= ?
R
expression()
t= ?
getStatement()
e=
?
id = var
command = VAR
Trace: Input -> var = 2*(3+5);
statement.idList =
Statement.ch = 2
R factor()
var = 1.0
minus = 1.0
id =
Runtime Stack
R
term()
f= ?
R
expression()
t= ?
getStatement()
e=
?
id = var
command = VAR
Trace: Input -> var = 2*(3+5);
statement.idList =
Statement.ch = 2
R factor()
var = 1.0
minus = 1.0
id =
Runtime Stack
R
term()
f= ?
R
expression()
t= ?
getStatement()
e=
?
id = var
command = VAR
Trace: Input -> var = 2*(3+5);
statement.idList =
Statement.ch = 2*
R factor()
var = 1.0
2.0
minus = 1.0
id =
Runtime Stack
R
term()
f= ?
R
expression()
t= ?
getStatement()
e=
?
id = var
command = VAR
Trace: Input -> var = 2*(3+5);
statement.idList =
Statement.ch = *
R factor()
var = 2.0
minus = 1.0
id =
Runtime Stack
R
term()
f = ?2
R
expression()
t= ?
getStatement()
e=
?
id = var
command = VAR
Trace: Input -> var = 2*(3+5);
statement.idList =
Statement.ch = *
R
Runtime Stack
R
factor()
term()
f= 2 *?
R
expression()
t= ?
getStatement()
e=
?
id = var
command = VAR
Trace: Input -> var = 2*(3+5);
statement.idList =
Statement.ch = *
R factor()
var = 1.0
minus = 1.0
id =
Runtime Stack
R
term()
f= 2 *?
R
expression()
t= ?
getStatement()
e=
?
id = var
command = VAR
Trace: Input -> var = 2*(3+5);
statement.idList =
Statement.ch = *(
R factor()
var = 1.0
minus = 1.0
id =
Runtime Stack
R
term()
f= 2 *?
R
expression()
t= ?
getStatement()
e=
?
id = var
command = VAR
Trace: Input -> var = 2*(3+5);
statement.idList =
Statement.ch = (
R factor()
var = 1.0
minus = 1.0
id =
Runtime Stack
R
term()
f= 2 *?
R
expression()
t= ?
getStatement()
e=
?
id = var
command = VAR
Trace: Input -> var = 2*(3+5);
statement.idList =
Statement.ch = (
R
expression()
R factor()
var = 1.0
?
minus = 1.0
id =
Runtime Stack
R
term()
f= 2 *?
R
expression()
t= ?
getStatement()
e=
?
id = var
command = VAR
Trace: Input -> var = 2*(3+5);
statement.idList =
Statement.ch = (
R
R factor()
var = ?
minus = 1.0
id =
R
Runtime Stack
Here, we have our
first recursive
function call…
expression()
term()
f= 2 *?
R
expression()
t= ?
getStatement()
e=
?
id = var
command = VAR
Trace: Input -> var = 2*(3+5);
statement.idList =
Statement.ch = (
R
R factor()
var = ?
minus = 1.0
id =
R
Runtime Stack
This conveniently
allows us to naturally
follow mathematical
precedence …
expression()
term()
f= 2 *?
R
expression()
t= ?
getStatement()
e=
?
id = var
command = VAR
Trace: Input -> var = 2*(3+5);
statement.idList =
Statement.ch = (
term()
expression()
R factor()
var = ?
minus = 1.0
id =
R
Runtime Stack
A series of additional
indirect recursive
calls will determine
the value of (3+5)…
R
R
term()
f= 2 *?
R
expression()
t= ?
getStatement()
e=
?
id = var
command = VAR
Trace: Input -> var = 2*(3+5);
statement.idList =
Statement.ch = (
factor()
R
R
term()
expression()
R factor()
var = ?
minus = 1.0
id =
R
Runtime Stack
A series of additional
indirect recursive
calls will determine
the value of (3+5)…
R
term()
f= 2 *?
R
expression()
t= ?
getStatement()
e=
?
id = var
command = VAR
Trace: Input -> var = 2*(3+5);
statement.idList =
Statement.ch = (3
factor()
R
R
term()
expression()
R factor()
var = ?
minus = 1.0
id =
R
Runtime Stack
A series of additional
indirect recursive
calls will determine
the value of (3+5)…
R
term()
f= 2 *?
R
expression()
t= ?
getStatement()
e=
?
id = var
command = VAR
Trace: Input -> var = 2*(3+5);
statement.idList =
+
Statement.ch = 3
factor()
R
R
term()
expression()
R factor()
var = ?
minus = 1.0
id =
R
Runtime Stack
A series of additional
indirect recursive
calls will determine
the value of (3+5)…
R
term()
f= 2 *?
R
expression()
t= ?
getStatement()
e=
?
id = var
command = VAR
Trace: Input -> var = 2*(3+5);
statement.idList =
Statement.ch = +
R
expression()
R factor()
var = ?
minus = 1.0
id =
Runtime Stack
R
term()
f= 2 *?
R
expression()
t= ?
getStatement()
e=
?
id = var
command = VAR
Trace: Input -> var = 2*(3+5);
statement.idList =
Statement.ch = +)
R
expression()
R factor()
var = ?8
minus = 1.0
id =
We now see the same chain of recursive
calls to term and factor…
Which eventually sets t = 8 in expression().
This value will be returned to factor…
Runtime Stack
R
term()
f= 2 *?
R
expression()
t= ?
getStatement()
e=
?
id = var
command = VAR
Trace: Input -> var = 2*(3+5);
statement.idList =
Statement.ch = );
R factor()
var = 8
minus = 1.0
id =
•factor() can now return 8 to
term()
Runtime Stack
R
term()
f = 2 * ?8
R
expression()
t= ?
getStatement()
e=
?
id = var
command = VAR
Trace: Input -> var = 2*(3+5);
statement.idList =
Statement.ch = ;
R
Runtime Stack
•factor() can now return 8 to
term()
•term() can return 2*8 =16 to
expression()
term()
f= 2 *8
R
expression()
t = ?16
getStatement()
e=
?
id = var
command = VAR
Trace: Input -> var = 2*(3+5);
statement.idList =
•factor() can now return 8 to
term()
•term() can return 2*8 =16 to
expression()
•expression() also returns 16 to
getStatement…
Runtime Stack
Statement.ch = ;
R
expression()
t = 16
getStatement()
e=
?
16
id = var
command = VAR
Trace: Input -> var = 2*(3+5);
statement.idList =
var => 16
Runtime Stack
Statement.ch = ;
getStatement()
e=
16
id = var
command = VAR
Control is returned to the main function, where once
again getStatement will be called…
Final Thoughts
For the sake of time – a lot of the non-recursive functions
were overlooked and treated as black boxes.
Tracing your own input through the functions carefully
will leave you with a solid understanding of recursion.
Recursive descent was once a popular way to build a
parser. These days more complex parsers can be built
by parser generators. For more information (and a solid
headache), google: LR parsers.
Download