Jeremy Siek 3 лет назад
Родитель
Сommit
000bc69e2e
3 измененных файлов с 224 добавлено и 50 удалено
  1. 16 0
      book.bib
  2. 207 49
      book.tex
  3. 1 1
      defs.tex

+ 16 - 0
book.bib

@@ -1,3 +1,19 @@
+
+
+@Misc{PSF21:cpython,
+  title = 	 {CPython github repository},
+  organization = {Python Software Foundation},
+  howpublished = {\url{https://github.com/python/cpython}},
+  year = 	 2021}
+
+@Manual{PSF21:python_ref,
+  title = 	 {The Python Language Reference},
+  organization = {Python Software Foundation},
+  month = 	 {June},
+  year = 	 2021,
+  howpublished = {\url{https://docs.python.org/3/reference/}},
+}
+
 @book{Lutz:2013vp,
 	author = {Mark Lutz},
 	date-added = {2021-06-10 10:29:47 -0400},

+ 207 - 49
book.tex

@@ -532,7 +532,7 @@ node it is the child of). If a node has no children, it is a
 %% S-expression.
 %% \begin{lstlisting}
 %% (define ast1.4 `(- 8))
-%% (define ast1.1 `(+ (read) ,ast1.4))
+%% (define ast1_1 `(+ (read) ,ast1.4))
 %% \end{lstlisting}
 %% In general, the Racket expression that follows the comma (splice)
 %% can be any expression that produces an S-expression.
@@ -571,7 +571,7 @@ operator has zero children:
 \end{lstlisting}
 whereas the addition operator has two children:
 \begin{lstlisting}
-(define ast1.1 (Prim '+ (list rd neg-eight)))
+(define ast1_1 (Prim '+ (list rd neg-eight)))
 \end{lstlisting}
 
 We have made a design choice regarding the \code{Prim} structure.
@@ -906,7 +906,7 @@ Consider the following example. \index{subject}{match} \index{subject}{pattern m
 \begin{minipage}{0.5\textwidth}
 {\if\edition\racketEd\color{olive}
 \begin{lstlisting}
-(match ast1.1
+(match ast1_1
   [(Prim op (list child1 child2))
     (print op)])
 \end{lstlisting}
@@ -997,6 +997,8 @@ def leaf(arith):
             return False
         case BinOp(e1, Add(), e2):
             return False
+        case _:
+            return False
 
 print(leaf(Call(Name('input_int'), [])))
 print(leaf(UnaryOp(USub(), eight)))
@@ -1028,6 +1030,11 @@ print(leaf(Constant(8)))
 
 
 
+
+    
+
+
+
     
    True
    False
@@ -1056,11 +1063,12 @@ of your choice (e.g. \code{e1} and \code{e2}).
 \label{sec:recursion}
 \index{subject}{recursive function}
 
-Programs are inherently recursive. For example, an \LangInt{} expression is
-often made of smaller expressions. Thus, the natural way to process an
-entire program is with a recursive function.  As a first example of
-such a recursive function, we define \texttt{exp} below, which takes
-an arbitrary value and determines whether or not it is an \LangInt{}
+Programs are inherently recursive. For example, an \LangInt{}
+expression is often made of smaller expressions. Thus, the natural way
+to process an entire program is with a recursive function.  As a first
+example of such a recursive function, we define the function
+\code{exp} in Figure~\ref{fig:exp-predicate}, which takes an
+arbitrary value and determines whether or not it is an \LangInt{}
 expression.
 %
 We say that a function is defined by \emph{structural recursion} when
@@ -1070,12 +1078,16 @@ makes a recursive call on each
 child node.\footnote{This principle of structuring code according to
   the data definition is advocated in the book \emph{How to Design
     Programs} \url{https://htdp.org/2020-8-1/Book/index.html}.}.
-Below we also define a second function, named \code{Rint}, that
-determines whether an AST is an \LangInt{} program.  In general we can
+\python{We define a second function, named \code{stmt}, that recognizes
+whether a value is a \LangInt{} statement.}
+\python{Finally, }
+Figure~\ref{fig:exp-predicate} \racket{also} defines \code{Rint}, which
+determines whether an AST is a program in \LangInt{}.  In general we can
 expect to write one recursive function to handle each non-terminal in
 a grammar.\index{subject}{structural recursion}
-%
-\begin{center}
+
+\begin{figure}[tp]
+{\if\edition\racketEd\color{olive}
 \begin{minipage}{0.7\textwidth}
 \begin{lstlisting}
 (define (exp ast)
@@ -1092,7 +1104,7 @@ a grammar.\index{subject}{structural recursion}
     [(Program '() e) (exp e)]
     [else #f]))
 
-(Rint (Program '() ast1.1)
+(Rint (Program '() ast1_1)
 (Rint (Program '()
        (Prim '- (list (Prim 'read '())
                       (Prim '+ (list (Num 8)))))))
@@ -1118,7 +1130,83 @@ a grammar.\index{subject}{structural recursion}
    #f
 \end{lstlisting}
 \end{minipage}
-\end{center}
+\fi}
+{\if\edition\pythonEd\color{purple}
+\begin{minipage}{0.7\textwidth}
+\begin{lstlisting}
+def exp(e):
+  match e:
+    case Constant(n):
+      return True
+    case Call(Name('input_int'), []):
+      return True
+    case UnaryOp(USub(), e1):
+      return exp(e1)
+    case BinOp(e1, Add(), e2):
+      return exp(e1) and exp(e2)
+    case _:
+      return False
+
+def stmt(s):
+  match s:
+    case Call(Name('print'), [e]):
+      return exp(e)
+    case Expr(e):
+      return exp(e)
+    case _:
+      return False
+  
+def Rint(p):
+  match p:
+    case Module(body):
+      return all([stmt(s) for s in body])
+    case _:
+      return False
+
+print(Rint(Module([Expr(ast1_1)])))
+print(Rint(Module([Expr(BinOp(read, Sub(),
+                  UnaryOp(Add(), Constant(8))))])))
+\end{lstlisting}
+\end{minipage}
+\vrule
+\begin{minipage}{0.25\textwidth}
+\begin{lstlisting}
+
+
+
+
+
+
+  
+
+
+
+
+
+
+
+
+  
+
+
+
+
+
+
+
+
+
+
+   True
+   False
+\end{lstlisting}
+\end{minipage}
+\fi}
+
+\caption{Example of recursive functions for \LangInt{}. These functions
+  recognize whether an AST is in \LangInt{}.}
+\label{fig:exp-predicate}
+\end{figure}
 
 
 %% You may be tempted to merge the two functions into one, like this:
@@ -1147,71 +1235,131 @@ a grammar.\index{subject}{structural recursion}
 
 
 \section{Interpreters}
-\label{sec:interp-Rint}
+\label{sec:interp_Rint}
 \index{subject}{interpreter}
 
-In general, the intended behavior of a program is defined by the
-specification of the language. For example, the Scheme language is
-defined in the report by \cite{SPERBER:2009aa}. The Racket language is
-defined in its reference manual~\citep{plt-tr}. In this book we use
-interpreters to specify each language that we consider. An interpreter
-that is designated as the definition of a language is called a
+The behavior of a program is defined by the specification of the
+programming language.
+%
+\racket{For example, the Scheme language is defined in the report by
+  \cite{SPERBER:2009aa}. The Racket language is defined in its
+  reference manual~\citep{plt-tr}.}
+%
+\python{For example, the Python language is defined in the Python
+  Language Reference~\citep{PSF21:python_ref} and the CPython interpreter~\citep{PSF21:cpython}.}
+%
+In this book we use interpreters
+to specify each language that we consider. An interpreter that is
+designated as the definition of a language is called a
 \emph{definitional interpreter}~\citep{reynolds72:_def_interp}.
-\index{subject}{definitional interpreter} We warm up by creating a definitional
-interpreter for the \LangInt{} language, which serves as a second example
-of structural recursion. The \texttt{interp-Rint} function is defined in
-Figure~\ref{fig:interp-Rint}. The body of the function is a match on the
-input program followed by a call to the \lstinline{interp-exp} helper
-function, which in turn has one match clause per grammar rule for
-\LangInt{} expressions.
+\index{subject}{definitional interpreter} We warm up by creating a
+definitional interpreter for the \LangInt{} language, which serves as
+a second example of structural recursion. The \texttt{interp\_Rint}
+function is defined in Figure~\ref{fig:interp_Rint}. The body of the
+function is a match on the input program followed by a call to the
+\lstinline{interp_exp} helper function, which in turn has one match
+clause per grammar rule for \LangInt{} expressions.
 
 \begin{figure}[tp]
+{\if\edition\racketEd\color{olive}
 \begin{lstlisting}
-(define (interp-exp e)
+(define (interp_exp e)
   (match e
     [(Int n) n]
     [(Prim 'read '())
      (define r (read))
      (cond [(fixnum? r) r]
-           [else (error 'interp-exp "read expected an integer" r)])]
+           [else (error 'interp_exp "read expected an integer" r)])]
     [(Prim '- (list e))
-     (define v (interp-exp e))
+     (define v (interp_exp e))
      (fx- 0 v)]
     [(Prim '+ (list e1 e2))
-     (define v1 (interp-exp e1))
-     (define v2 (interp-exp e2))
+     (define v1 (interp_exp e1))
+     (define v2 (interp_exp e2))
      (fx+ v1 v2)]))
 
-(define (interp-Rint p)
+(define (interp_Rint p)
   (match p
-    [(Program '() e) (interp-exp e)]))
+    [(Program '() e) (interp_exp e)]))
+\end{lstlisting}
+\fi}
+{\if\edition\pythonEd\color{purple}
+\begin{lstlisting}
+def interp_exp(e):
+    match e:
+        case BinOp(left, Add(), right):
+            l = interp_exp(left)
+            r = interp_exp(right)
+            return l + r
+        case UnaryOp(USub(), v):
+            return - interp_exp(v)
+        case Constant(value):
+            return value
+        case Call(Name('input_int'), []):
+            return int(input())            
+
+def interp_stmt(s):
+    match s:
+        case Expr(Call(Name('print'), [arg])):
+            print(interp_exp(arg))
+        case Expr(value):
+            interp_exp(value)
+
+def interp_Pint(p):
+    match p:
+        case Module(body):
+            for s in body:
+                interp_stmt(s)
 \end{lstlisting}
+\fi}
 \caption{Interpreter for the \LangInt{} language.}
-\label{fig:interp-Rint}
+\label{fig:interp_Rint}
 \end{figure}
 
 Let us consider the result of interpreting a few \LangInt{} programs. The
 following program adds two integers.
+{\if\edition\racketEd\color{olive}
 \begin{lstlisting}
 (+ 10 32)
 \end{lstlisting}
+\fi}
+{\if\edition\pythonEd\color{purple}
+\begin{lstlisting}
+print(10 + 32)
+\end{lstlisting}
+\fi}
 The result is \key{42}, the answer to life, the universe, and
 everything: \code{42}!\footnote{\emph{The Hitchhiker's Guide to the
     Galaxy} by Douglas Adams.}.
 %
 We wrote the above program in concrete syntax whereas the parsed
 abstract syntax is:
+{\if\edition\racketEd\color{olive}
 \begin{lstlisting}
 (Program '() (Prim '+ (list (Int 10) (Int 32))))
 \end{lstlisting}
-
+\fi}
+{\if\edition\pythonEd\color{purple}
+\begin{lstlisting}
+Module([Expr(Call(Name('print'), [BinOp(Constant(10), Add(), Constant(32))]))])    
+\end{lstlisting}
+\fi}
 The next example demonstrates that expressions may be nested within
 each other, in this case nesting several additions and negations.
+{\if\edition\racketEd\color{olive}
 \begin{lstlisting}
 (+ 10 (- (+ 12 20)))
 \end{lstlisting}
+\fi}
+{\if\edition\pythonEd\color{purple}
+\begin{lstlisting}
+print(10 + -(12 + 20))
+\end{lstlisting}
+\fi}
+
 What is the result of the above program?
 
+{\if\edition\racketEd\color{olive}
 As mentioned previously, the \LangInt{} language does not support
 arbitrarily-large integers, but only $63$-bit integers, so we
 interpret the arithmetic operations of \LangInt{} using fixnum arithmetic
@@ -1241,26 +1389,36 @@ it is required to report that an error occurred. To signal an error,
 exit with a return code of \code{255}.  The interpreters in chapters
 \ref{ch:Rdyn} and \ref{ch:Rgrad} use
 \code{trapped-error}.
+\fi}
+
+% TODO: how to deal with too-large integers in the Python interpreter?
 
 %% This convention applies to the languages defined in this
 %% book, as a way to simplify the student's task of implementing them,
 %% but this convention is not applicable to all programming languages.
 %% 
 
-Moving on to the last feature of the \LangInt{} language, the \key{read}
-operation prompts the user of the program for an integer.  Recall that
-program \eqref{eq:arith-prog} performs a \key{read} and then subtracts
-\code{8}. So if we run
+Moving on to the last feature of the \LangInt{} language, the
+\READOP{} operation prompts the user of the program for an integer.
+Recall that program \eqref{eq:arith-prog} requests an integer input
+and then subtracts \code{8}. So if we run
+{\if\edition\racketEd\color{olive}
+\begin{lstlisting}
+(interp_Rint (Program '() ast1_1))
+\end{lstlisting}
+\fi}
+{\if\edition\pythonEd\color{purple}
 \begin{lstlisting}
-(interp-Rint (Program '() ast1.1))
+interp_Pint(Module([Expr(Call(Name('print'), [ast1_1]))]))
 \end{lstlisting}
-and if the input is \code{50}, the result is \code{42}.
+\fi}
+\noindent and if the input is \code{50}, the result is \code{42}.
 
-We include the \key{read} operation in \LangInt{} so a clever student
+We include the \READOP{} operation in \LangInt{} so a clever student
 cannot implement a compiler for \LangInt{} that simply runs the interpreter
 during compilation to obtain the output and then generates the trivial
-code to produce the output. (Yes, a clever student did this in the
-first instance of this course.)
+code to produce the output.\footnote{Yes, a clever student did this in the
+first instance of this course!}
 
 The job of a compiler is to translate a program in one language into a
 program in another language so that the output program behaves the
@@ -1356,7 +1514,7 @@ Appendix~\ref{appendix:utilities}.\\
 \begin{lstlisting}
 (define (test-pe p)
   (assert "testing pe-Rint"
-     (equal? (interp-Rint p) (interp-Rint (pe-Rint p)))))
+     (equal? (interp_Rint p) (interp_Rint (pe-Rint p)))))
 
 (test-pe (parse-program `(program () (+ 10 (- (+ 5 3))))))
 (test-pe (parse-program `(program () (+ 1 (+ 3 1)))))
@@ -9744,7 +9902,7 @@ Next consider the match case for \code{vector-ref}.  The
 \code{check-tag} auxiliary function (Figure~\ref{fig:interp-Rdyn-aux})
 is used to ensure that the first argument is a vector and the second
 is an integer. If they are not, a \code{trapped-error} is raised.
-Recall from Section~\ref{sec:interp-Rint} that when a definition
+Recall from Section~\ref{sec:interp_Rint} that when a definition
 interpreter raises a \code{trapped-error} error, the compiled code
 must also signal an error by exiting with return code \code{255}.  A
 \code{trapped-error} is also raised if the index is not less than
@@ -13750,7 +13908,7 @@ for the compilation of \LangPoly{}.
 \index{subject}{interpreter}
 
 We provide interpreters for each of the source languages \LangInt{},
-\LangVar{}, $\ldots$ in the files \code{interp-Rint.rkt},
+\LangVar{}, $\ldots$ in the files \code{interp\_Rint.rkt},
 \code{interp-Rvar.rkt}, etc.  The interpreters for the intermediate
 languages \LangCVar{} and \LangCIf{} are in \code{interp-Cvar.rkt} and
 \code{interp-C1.rkt}.  The interpreters for \LangCVec{}, \LangCFun{}, pseudo-x86,

+ 1 - 1
defs.tex

@@ -124,7 +124,7 @@
 \newcommand{\READ}{{\color{purple}\key{Call(Name('input\_int'),[])}}}
 \newcommand{\NEG}[1]{{\color{purple}\key{UnaryOp(USub(),} #1\code{)}}}
 \newcommand{\ADD}[2]{{\color{purple}\key{BinOp(Add()}\key{,}#1\code{,}#2\code{)}}}
-\newcommand{\PRINT}[1]{{\color{purple}\key{Call}\LP\key{Name}\LP\key{print}\RP\key{,}\LS#1\RS\RP}}
+\newcommand{\PRINT}[1]{{\color{purple}\key{Expr}\LP\key{Call}\LP\key{Name}\LP\key{'print'}\RP\key{,}\LS#1\RS\RP\RP}}
 \newcommand{\EXPR}[1]{{\color{purple}\key{Expr}\LP #1\RP}}
 \newcommand{\PROGRAM}[2]{\code{Module}\LP #2\RP}
 \fi