Explorar o código

extensible interpreters

Jeremy Siek %!s(int64=3) %!d(string=hai) anos
pai
achega
ef9046b2f8
Modificáronse 1 ficheiros con 64 adicións e 58 borrados
  1. 64 58
      book.tex

+ 64 - 58
book.tex

@@ -22,7 +22,7 @@
 
 \def\racketEd{0}
 \def\pythonEd{1}
-\def\edition{1}
+\def\edition{0}
 
 % material that is specific to the Racket edition of the book
 \newcommand{\racket}[1]{{\if\edition\racketEd{#1}\fi}}
@@ -1809,38 +1809,41 @@ $52$ then $10$, the following produces $42$ (not $-42$).
 \subsection{Extensible Interpreters via Method Overriding}
 \label{sec:extensible-interp}
 
-To prepare for discussing the interpreter for \LangVar{}, we 
-explain why we to implement the interpreter using
-object-oriented programming, that is, as a collection of methods
-inside of a class. Throughout this book we define many interpreters,
-one for each of the languages that we study. Because each language
-builds on the prior one, there is a lot of commonality between these
-interpreters. We want to write down the common parts just once
-instead of many times. A naive approach would be to have, for example,
-the interpreter for \LangIf{} handle all of the new features in that
-language and then have a default case that dispatches to the
-interpreter for \LangVar{}. The following code sketches this idea.
+To prepare for discussing the interpreter for \LangVar{}, we explain
+why we to implement the interpreter using object-oriented programming,
+that is, as a collection of methods inside of a class. Throughout this
+book we define many interpreters, one for each of the languages that
+we study. Because each language builds on the prior one, there is a
+lot of commonality between these interpreters. We want to write down
+the common parts just once instead of many times. A naive approach
+would be to have, for example, the interpreter for \LangVar{} handle
+the cases for variables and \code{let} and then have a default case
+that dispatches to the interpreter for \LangInt{}. The following code
+sketches this idea. (We explain the \code{env} parameter soon, in
+Section~\ref{sec:interp-Lvar}.)
+
 \begin{center}
 {\if\edition\racketEd  
 \begin{minipage}{0.45\textwidth}
 \begin{lstlisting}
-(define (interp_Lvar_exp e)
+(define ((interp_Lint env) e)
   (match e
     [(Prim '- (list e1))
-     (fx- 0 (interp_Lvar_exp e1))]
+     (fx- 0 ((interp_Lint env) e1))]
     ...))
 \end{lstlisting}
 \end{minipage}
 \begin{minipage}{0.45\textwidth}
   \begin{lstlisting}
-(define (interp_Lif_exp e)
+(define ((interp_Lvar env) e)
   (match e
-    [(If cnd thn els)
-     (match (interp_Lif_exp cnd)
-       [#t (interp_Lif_exp thn)]
-       [#f (interp_Lif_exp els)])]
-    ...
-    [else (interp_Lvar_exp e)]))    
+    [(Var x)
+     (dict-ref env x)]
+    [(Let x e body)
+     (define v ((interp_exp env) e))
+     (define env^ (dict-set env x v))
+     ((interp_exp env^) body)]
+    [else ((interp_Lint env) e)]))    
 \end{lstlisting}
 \end{minipage}
 \fi}
@@ -1848,50 +1851,50 @@ interpreter for \LangVar{}. The following code sketches this idea.
 {\if\edition\pythonEd
 \begin{minipage}{0.45\textwidth}
 \begin{lstlisting}
-def interp_Lvar_exp(e):
+def interp_Lint(e):
   match e:
     case UnaryOp(USub(), e1):
-       return - interp_Lvar_exp(e1)
+       return - interp_Lint(e1)
     ...
 \end{lstlisting}
 \end{minipage}
 \begin{minipage}{0.45\textwidth}
 \begin{lstlisting}
-def interp_Lif_exp(e):
+def interp_Lvar(e):
   match e:
     case IfExp(cnd, thn, els):
-      match interp_Lif_exp(cnd):
+      match interp_Lvar(cnd):
         case True:
-          return interp_Lif_exp(thn)
+          return interp_Lvar(thn)
         case False:
-          return interp_Lif_exp(els)
+          return interp_Lvar(els)
     ...
     case _:
-      return interp_Lvar_exp(e)
+      return interp_Lint(e)
 \end{lstlisting}
 \end{minipage}
 \fi}
 \end{center}
 The problem with this approach is that it does not handle situations
-in which an \LangIf{} feature, such as a conditional expression, is
-nested inside an \LangVar{} feature, like the \code{-} operator, as in
-the following program.
-{\if\edition\racketEd  
+in which an \LangVar{} feature, such as a variable, is nested inside
+an \LangInt{} feature, like the \code{-} operator, as in the following
+program.  {\if\edition\racketEd
 \begin{lstlisting}
-(Prim '- (list (If (Bool #t) (Int 42) (Int 0))))
+(Let 'y (Int 10) (Prim '- (list (Var 'y))))
 \end{lstlisting}
 \fi}
 {\if\edition\pythonEd
-\begin{lstlisting}
-print(-(42 if True else 0))
+  \begin{lstlisting}
+y = 10 
+print(-y)
 \end{lstlisting}
 \fi}
 %
-If we invoke \code{interp\_Lif\_exp} on this program, it dispatches to
-\code{interp\_Lvar\_exp} to handle the \code{-} operator, but then it
-recursively calls \code{interp\_Lvar\_exp} again on the argument of
-\code{-}, which is an \code{If}.  But there is no case for \code{If}
-in \code{interp\_Lvar\_exp} so we get an error!
+If we invoke \code{interp\_Lvar} on this program, it dispatches to
+\code{interp\_Lint} to handle the \code{-} operator, but then it
+recursively calls \code{interp\_Lint} again on the argument of
+\code{-}, which is a \code{Var}.  But there is no case for \code{Var}
+in \code{interp\_Lint} so we get an error!
 
 To make our interpreters extensible we need something called
 \emph{open recursion}\index{subject}{open recursion}, where the tying of the
@@ -1919,28 +1922,29 @@ inherits from the class for \LangVar{} and the method
 {\if\edition\racketEd  
 \begin{minipage}{0.45\textwidth}
 \begin{lstlisting}
-(define interp_Lvar_class
+(define interp_Lint_class
   (class object%
-    (define/public (interp_exp e)
+    (define/public ((interp_exp env) e)
       (match e
         [(Prim '- (list e))
-         (fx- 0 (interp_exp e))]
+         (fx- 0 ((interp_exp env) e))]
         ...))
     ...))
 \end{lstlisting}
 \end{minipage}
 \begin{minipage}{0.45\textwidth}
   \begin{lstlisting}
-(define interp_Lif_class
+(define interp_Lint_class
   (class interp_Lvar_class
-    (define/override (interp_exp e)
+    (define/override ((interp_exp env) e)
       (match e
-        [(If cnd thn els)
-         (match (interp_exp cnd)
-           [#t (interp_exp thn)]
-           [#f (interp_exp els)])]
-        ...
-        [else (super interp_exp e)]))
+        [(Var x)
+         (dict-ref env x)]
+        [(Let x e body)
+         (define v ((interp_exp env) e))
+         (define env^ (dict-set env x v))
+         ((interp_exp env^) body)]
+        [else (super (interp_exp env) e)]))
     ...
   ))
 \end{lstlisting}
@@ -1980,12 +1984,13 @@ def InterpLif(InterpRVar):
 Getting back to the troublesome example, repeated here:
 {\if\edition\racketEd  
 \begin{lstlisting}
-(define e0 (Prim '- (list (If (Bool #t) (Int 42) (Int 0)))))
+(define e0 (Let 'y (Int 10) (Prim '- (Var 'y))))
 \end{lstlisting}
 \fi}
 {\if\edition\pythonEd
 \begin{lstlisting}
--(42 if True else 0)
+y = 10
+print(-y)
 \end{lstlisting}
 \fi}
 \noindent We can invoke the \code{interp\_exp} method for \LangIf{} on this
@@ -1993,7 +1998,7 @@ expression, call it \code{e0}, by creating an object of the \LangIf{} class
 and calling the \code{interp\_exp} method.
 {\if\edition\racketEd
 \begin{lstlisting}
-(send (new interp_Lif_class) interp_exp e0)
+(send (new interp_Lvar_class) interp_exp e0)
 \end{lstlisting}
 \fi}
 {\if\edition\pythonEd
@@ -2001,16 +2006,17 @@ and calling the \code{interp\_exp} method.
 InterpLif().interp_exp(e0)
 \end{lstlisting}
 \fi}
-\noindent The default case of \code{interp\_exp} in \LangIf{} handles it by
-dispatching to the \code{interp\_exp} method in \LangVar{}, which
+\noindent The default case of \code{interp\_exp} in \LangVar{} handles it by
+dispatching to the \code{interp\_exp} method in \LangInt{}, which
 handles the \code{-} operator. But then for the recursive method call,
-it dispatches back to \code{interp\_exp} in \LangIf{}, where the
-\code{If} is handled correctly. Thus, method overriding gives us the
+it dispatches back to \code{interp\_exp} in \LangVar{}, where the
+\code{Var} node is handled correctly. Thus, method overriding gives us the
 open recursion that we need to implement our interpreters in an
 extensible way.
 
 
 \subsection{Definitional Interpreter for \LangVar{}}
+\label{sec:interp-Lvar}
 
 {\if\edition\racketEd
 \begin{figure}[tp]