Case Study:A Recursive Descent Interpreter
Implementation in C++(Source Code Courtesy of Dr. Adam Drozdek)
Payap UniversityICS220 - Data Structures and Algorithm AnalysisInstructor: Dr. Ken CoshAnalysis and Presentation by Rob Agle
First, let’s clear up some terminology…
Interpreter
Examples of “Interpreted” Languages
PythonPearl
JavaScript
RubySmalltalk
Java
• 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…
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 Descentvar = 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 Descentvar = 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 ) ;
var = 2 * ( 3 + 5 ) ;ID
var = 2 * ( 3 + 5 ) ;ID
Operator Operator Operator
var = 2 * ( 3 + 5 ) ;ID
Operator
Factor
Operator Operator
Factor Factor
var = 2 * ( 3 + 5 ) ;ID
Operator
Factor
Operator Operator
Factor Factor
Term Term
var = 2 * ( 3 + 5 ) ;ID
Operator
Factor
Operator Operator
Factor Factor
Term Term
Expression
Expression
var = 2 * ( 3 + 5 ) ;ID
Operator
Factor
Operator Operator
Factor Factor
Term Term
Expression
Expression
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”…
Data
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”…
What do we need (object wise) to accomplish this?
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 =
Statement.ch =
Runti
me
Stac
ke =id =command =
getStatement()
Trace: Input -> var = 2*(3+5);
statement.idList =
Statement.ch =
Runti
me
Stac
ke =id =command =
getStatement()
varVAR
var
R expression()
Trace: Input -> var = 2*(3+5);
statement.idList =
Statement.ch =
Runti
me
Stac
ke = id =command =
getStatement()
varVAR
?
=var
R expression()
Trace: Input -> var = 2*(3+5);
statement.idList =
Statement.ch =
Runti
me
Stac
ke = id =command =
getStatement()
varVAR
?
=
t =
R term()
?
R expression()
Trace: Input -> var = 2*(3+5);
statement.idList =
Statement.ch =
Runti
me
Stac
ke = id =command =
getStatement()
varVAR
?
=
t =
R term()
?
R factor()
f = ?
R expression()
Trace: Input -> var = 2*(3+5);
statement.idList =
Statement.ch =
Runti
me
Stac
ke = id =command =
getStatement()
varVAR
?
=
t =
R term()
?
R factor()
f = ?
var = minus = id =
1.0 1.0
statement.idList =
Statement.ch = =2
R expression()
Trace: Input -> var = 2*(3+5);
Runti
me
Stac
ke = id =command =
getStatement()
varVAR
?
t =
R term()
?
R factor()
f = ?
var = minus = id =
1.0 1.0
2
R expression()
Trace: Input -> var = 2*(3+5);
statement.idList =
Statement.ch =
Runti
me
Stac
ke = id =command =
getStatement()
varVAR
?
t =
R term()
?
R factor()
f = ?
var = minus = id =
1.0 1.0
2
R expression()
Trace: Input -> var = 2*(3+5);
statement.idList =
Statement.ch =
Runti
me
Stac
ke = id =command =
getStatement()
varVAR
?
t =
R term()
?
R factor()
f = ?
var = minus = id =
1.0 1.0
*
statement.idList =
Statement.ch =
1.0 2.0
2
R expression()
Trace: Input -> var = 2*(3+5);
Runti
me
Stac
ke = id =command =
getStatement()
varVAR
?
t =
R term()
?
R factor()
f = ?
var = minus = id =
1.0
statement.idList =
Statement.ch =
2
2.0
*
R expression()
Trace: Input -> var = 2*(3+5);
Runti
me
Stac
ke = id =command =
getStatement()
varVAR
?
t =
R term()
?
R factor()
f = ?
var = minus = id =
1.0
statement.idList =
Statement.ch =
2
R expression()
Trace: Input -> var = 2*(3+5);
Runti
me
Stac
ke = id =command =
getStatement()
varVAR
?
t =
R term()
?
f =
*
R factor()
* ?
R expression()
Trace: Input -> var = 2*(3+5);
statement.idList =
Statement.ch =
Runti
me
Stac
ke = id =command =
getStatement()
varVAR
?
*
t =
R term()
?
R factor()
f = 2
var = minus = id =
1.0 1.0
* ?
(
R expression()
Trace: Input -> var = 2*(3+5);
statement.idList =
Statement.ch =
Runti
me
Stac
ke = id =command =
getStatement()
varVAR
?
*
t =
R term()
?
R factor()
f = 2
var = minus = id =
1.0 1.0
* ?
(
R expression()
Trace: Input -> var = 2*(3+5);
statement.idList =
Statement.ch =
Runti
me
Stac
ke = id =command =
getStatement()
varVAR
?
t =
R term()
?
R factor()
f = 2
var = minus = id =
1.0 1.0
* ?
?
R expression()
(
R expression()
Trace: Input -> var = 2*(3+5);
statement.idList =
Statement.ch =
Runti
me
Stac
ke = id =command =
getStatement()
varVAR
?
t =
R term()
?
R factor()
f = 2
var = minus = id =
1.0 1.0
* ?
?
R expression()
(
R expression()
Trace: Input -> var = 2*(3+5);
statement.idList =
Statement.ch =
Runti
me
Stac
ke = id =command =
getStatement()
varVAR
?
t =
R term()
?
R factor()
f = 2
var = minus = id =
1.0
* ?
Here, we have our first recursive function call…
?
R expression()
(
R expression()
Trace: Input -> var = 2*(3+5);
statement.idList =
Statement.ch =
Runti
me
Stac
ke = id =command =
getStatement()
varVAR
?
t =
R term()
?
R factor()
f = 2
var = minus = id =
1.0
* ?
This conveniently allows us to naturally follow mathematical
precedence …
?
R expression()
(
R expression()
Trace: Input -> var = 2*(3+5);
statement.idList =
Statement.ch =
Runti
me
Stac
ke = id =command =
getStatement()
varVAR
?
t =
R term()
?
R factor()
f = 2
var = minus = id =
1.0
* ?
A series of additional indirect recursive
calls will determine the value of (3+5)…
R term()
?
R expression()
(
R expression()
Trace: Input -> var = 2*(3+5);
statement.idList =
Statement.ch =
Runti
me
Stac
ke = id =command =
getStatement()
varVAR
?
t =
R term()
?
R factor()
f = 2
var = minus = id =
1.0
* ?
A series of additional indirect recursive
calls will determine the value of (3+5)…
R term()R factor()
3
statement.idList =
Statement.ch =
?
R expression()
(
R expression()
Trace: Input -> var = 2*(3+5);
Runti
me
Stac
ke = id =command =
getStatement()
varVAR
?
t =
R term()
?
R factor()
f = 2
var = minus = id =
1.0
* ?
A series of additional indirect recursive
calls will determine the value of (3+5)…
R term()R factor()
R factor()+
statement.idList =
Statement.ch = 3
?
R expression()
R expression()
Trace: Input -> var = 2*(3+5);
Runti
me
Stac
ke = id =command =
getStatement()
varVAR
?
t =
R term()
?
R factor()
f = 2
var = minus = id =
1.0
* ?
A series of additional indirect recursive
calls will determine the value of (3+5)…
R term()
+
statement.idList =
Statement.ch =
?
R expression()
R expression()
Trace: Input -> var = 2*(3+5);
Runti
me
Stac
ke = id =command =
getStatement()
varVAR
?
t =
R term()
?
R factor()
f = 2
var = minus = id =
1.0
* ?
)
statement.idList =
Statement.ch =
8
+
?
R expression()
R expression()
Trace: Input -> var = 2*(3+5);
Runti
me
Stac
ke = id =command =
getStatement()
varVAR
?
t =
R term()
?
R factor()
f = 2
var = minus = id =
1.0
* ?
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…
;
statement.idList =
Statement.ch =
R factor()
* 82 * ?
)
8
R expression()
Trace: Input -> var = 2*(3+5);
Runti
me
Stac
ke = id =command =
getStatement()
varVAR
?
t =
R term()
?
f =
var = minus = id =
1.0 • factor() can now return 8 to
term()
16
statement.idList =
Statement.ch =
R expression()
Trace: Input -> var = 2*(3+5);
Runti
me
Stac
ke = id =command =
getStatement()
varVAR
?
t =
R term()
?
f = 2 * 8
• factor() can now return 8 to term()
• term() can return 2*8 =16 to expression()
;
16
statement.idList =
Statement.ch =
R expression()
Trace: Input -> var = 2*(3+5);
Runti
me
Stac
ke = id =command =
getStatement()
varVAR
?
t = 16
• factor() can now return 8 to term()
• term() can return 2*8 =16 to expression()
• expression() also returns 16 to getStatement…
;
16
statement.idList =
Statement.ch =
Trace: Input -> var = 2*(3+5);
Runti
me
Stac
ke = id =command =
getStatement()
varVAR
;
var => 16
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.
Top Related