Bladeren bron

type check Pif

Jeremy Siek 3 jaren geleden
bovenliggende
commit
43d220756f
2 gewijzigde bestanden met toevoegingen van 211 en 44 verwijderingen
  1. 210 43
      book.tex
  2. 1 1
      defs.tex

+ 210 - 43
book.tex

@@ -40,6 +40,7 @@
 
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
+\if\edition\racketEd
 \lstset{%
 language=Lisp,
 basicstyle=\ttfamily\small,
@@ -50,6 +51,19 @@ columns=flexible,
 moredelim=[is][\color{red}]{~}{~},
 showstringspaces=false
 }
+\fi
+\if\edition\pythonEd
+\lstset{%
+language=Python,
+basicstyle=\ttfamily\small,
+morekeywords={match,case,bool,int},
+deletekeywords={},
+escapechar=|,
+columns=flexible,
+moredelim=[is][\color{red}]{~}{~},
+showstringspaces=false
+}
+\fi
 
 
 %%% Any shortcut own defined macros place here
@@ -6368,13 +6382,15 @@ class InterpPif(InterpPvar):
         return l - r
       case UnaryOp(Not(), v):
         return not self.interp_exp(v, env)
-      case BoolOp(left, And(), right):
+      case BoolOp(And(), values):
+        left = values[0]; right = values[1]
         match self.interp_exp(left, env):
           case True:
             return self.interp_exp(right, env)
           case False:
             return False
-      case BoolOp(left, Or(), right):
+      case BoolOp(Or(), values):
+        left = values[0]; right = values[1]
         match self.interp_exp(left, env):
           case True:
             return True
@@ -6542,31 +6558,50 @@ error or returns an expression and its type (\INTTY{} or
 \BOOLTY{}). It returns an expression because there are situations
 in which we want to change or update the expression.
 
-Next we discuss the \code{match} cases in \code{type\_check\_exp} of
+Next we discuss the \code{type\_check\_exp} function in
 Figure~\ref{fig:type-check-Rvar}.  The type of an integer constant is
-\INTTY{}.  To handle variables, the type checker uses the
-environment \code{env} to map variables to types. Consider the case
-for \key{let}.  We type check the initializing expression to obtain
-its type \key{T} and then associate type \code{T} with the variable
-\code{x} in the environment used to type check the body of the
-\key{let}. Thus, when the type checker encounters a use of variable
-\code{x}, it can find its type in the environment.  Regarding
-primitive operators, we recursively analyze the arguments and then
-invoke \code{type\_check\_op} to check whether the argument types are
-allowed.
-
-Several auxiliary methods are used in the type checker. The method
-\code{operator-types} defines a dictionary that maps the operator
-names to their parameter and return types. The \code{type-equal?}
-method determines whether two types are equal, which for now simply
-dispatches to \code{equal?}  (deep equality). The
-\code{check-type-equal?} method triggers an error if the two types are
-not equal. The \code{type-check-op} method looks up the operator in
-the \code{operator-types} dictionary and then checks whether the
-argument types are equal to the parameter types.  The result is the
-return type of the operator.
+\INTTY{}.  To handle variables, the type checker uses the environment
+\code{env} to map variables to types.
+%
+\racket{Consider the case for \key{let}.  We type check the
+  initializing expression to obtain its type \key{T} and then
+  associate type \code{T} with the variable \code{x} in the
+  environment used to type check the body of the \key{let}.  Thus,
+  when the type checker encounters a use of variable \code{x}, it can
+  find its type in the environment.}
+%
+\python{Consider the case for assignment. We type check the
+  initializing expression to obtain its type \key{t}.  If the variable
+  \code{lhs.id} is already in the environment because there was a
+  prior assignment, we check that this initializer has the same type
+  as the prior one. If this is the first assignment to the variable,
+  we associate type \code{t} with the variable \code{lhs.id} in the
+  environment. Thus, when the type checker encounters a use of
+  variable \code{x}, it can find its type in the environment.}
+%
+\racket{Regarding primitive operators, we recursively analyze the
+  arguments and then invoke \code{type\_check\_op} to check whether
+  the argument types are allowed.}
+%
+\python{Regarding addition and negation, we recursively analyze the
+  arguments, check that they have type \INT{}, and return \INT{}.}
+
+\racket{Several auxiliary methods are used in the type checker. The
+  method \code{operator-types} defines a dictionary that maps the
+  operator names to their parameter and return types. The
+  \code{type-equal?}  method determines whether two types are equal,
+  which for now simply dispatches to \code{equal?}  (deep
+  equality). The \code{check-type-equal?} method triggers an error if
+  the two types are not equal. The \code{type-check-op} method looks
+  up the operator in the \code{operator-types} dictionary and then
+  checks whether the argument types are equal to the parameter types.
+  The result is the return type of the operator.}
+%
+\python{The auxiliary method \code{check\_type\_equal} method triggers
+  an error if the two types are not equal.}
 
 \begin{figure}[tbp]
+{\if\edition\racketEd\color{olive}  
 \begin{lstlisting}[basicstyle=\ttfamily\footnotesize]
 (define type-check-Rvar_class
   (class object%
@@ -6618,11 +6653,65 @@ return type of the operator.
 (define (type-check-Rvar p)
   (send (new type-check-Rvar_class) type-check-program p))
 \end{lstlisting}
+\fi}
+{\if\edition\pythonEd\color{purple}
+\begin{lstlisting}
+class TypeCheckPvar:
+  def check_type_equal(self, t1, t2, e):
+    if t1 != t2:
+      msg = 'error: ' + repr(t1) + ' != ' + repr(t2) + ' in ' + repr(e)
+      raise Exception(msg)
+          
+  def type_check_exp(self, e, env):
+    match e:
+      case BinOp(left, Add(), right):
+        l = self.type_check_exp(left, env)
+        check_type_equal(l, int, left)
+        r = self.type_check_exp(right, env)
+        check_type_equal(r, int, right)
+        return int
+      case UnaryOp(USub(), v):
+        t = self.type_check_exp(v, env)
+        check_type_equal(t, int, v)
+        return int
+      case Name(id):
+        return env[id]
+      case Constant(value) if isinstance(value, int):
+        return int
+      case Call(Name('input_int'), []):
+        return int
+
+  def type_check_stmts(self, ss, env):
+    if len(ss) == 0:
+      return
+    match ss[0]:
+      case Assign([lhs], value):
+        t = self.type_check_exp(value, env)
+        if lhs.id in env:
+          check_type_equal(env[lhs.id], t, value)
+        else:
+          env[lhs.id] = t
+        return self.type_check_stmts(ss[1:], env)
+      case Expr(Call(Name('print'), [arg])):
+        t = self.type_check_exp(arg, env)
+        check_type_equal(t, int, arg)
+        return self.type_check_stmts(ss[1:], env)
+      case Expr(value):
+        self.type_check_exp(value, env)
+        return self.type_check_stmts(ss[1:], env)
+
+  def type_check_P(self, p):
+    match p:
+      case Module(body):
+        self.type_check_stmts(body, {})
+\end{lstlisting}
+\fi}
 \caption{Type checker for the \LangVar{} language.}
 \label{fig:type-check-Rvar}
 \end{figure}
 
 \begin{figure}[tbp]
+{\if\edition\racketEd\color{olive}    
 \begin{lstlisting}[basicstyle=\ttfamily\footnotesize]
 (define type-check-Rif_class
   (class type-check-Rvar_class
@@ -6644,12 +6733,12 @@ return type of the operator.
     (define/override (type-check-exp env)
       (lambda (e)
         (match e
+          [(Bool b) (values (Bool b) 'Boolean)]
           [(Prim 'eq? (list e1 e2))
            (define-values (e1^ T1) ((type-check-exp env) e1))
            (define-values (e2^ T2) ((type-check-exp env) e2))
            (check-type-equal? T1 T2 e)
            (values (Prim 'eq? (list e1^ e2^)) 'Boolean)]
-          [(Bool b) (values (Bool b) 'Boolean)]
           [(If cnd thn els)
            (define-values (cnd^ Tc) ((type-check-exp env) cnd))
            (define-values (thn^ Tt) ((type-check-exp env) thn))
@@ -6663,34 +6752,112 @@ return type of the operator.
 (define (type-check-Rif p)
   (send (new type-check-Rif_class) type-check-program p))
 \end{lstlisting}
+\fi}
+{\if\edition\pythonEd\color{purple}
+\begin{lstlisting}
+class TypeCheckPif(TypeCheckPvar):
+  def type_check_exp(self, e, env):
+    match e:
+      case Constant(value) if isinstance(value, bool):
+        return bool
+      case BinOp(left, Sub(), right):
+        l = self.type_check_exp(left, env); check_type_equal(l, int, left)
+        r = self.type_check_exp(right, env); check_type_equal(r, int, right)
+        return int
+      case UnaryOp(Not(), v):
+        t = self.type_check_exp(v, env); check_type_equal(t, bool, v)
+        return bool 
+      case BoolOp(op, values):
+        left = values[0] ; right = values[1]
+        l = self.type_check_exp(left, env); check_type_equal(l, bool, left)
+        r = self.type_check_exp(right, env); check_type_equal(r, bool, right)
+        return bool
+      case Compare(left, [cmp], [right]) if isinstance(cmp, Eq) \
+                                            or isinstance(cmp, NotEq):
+        l = self.type_check_exp(left, env)
+        r = self.type_check_exp(right, env)
+        check_type_equal(l, r, e)
+        return bool
+      case Compare(left, [cmp], [right]):
+        l = self.type_check_exp(left, env); check_type_equal(l, int, left)
+        r = self.type_check_exp(right, env); check_type_equal(r, int, right)
+        return bool
+      case IfExp(test, body, orelse):
+        t = self.type_check_exp(test, env); check_type_equal(bool, t, test)
+        b = self.type_check_exp(body, env)
+        o = self.type_check_exp(orelse, env)
+        check_type_equal(b, o, e)
+        return b
+      case _:
+        return super().type_check_exp(e, env)
+
+  def type_check_stmts(self, ss, env):
+    if len(ss) == 0:
+      return
+    match ss[0]:
+      case If(test, body, orelse):
+        t = self.type_check_exp(test, env); check_type_equal(bool, t, test)
+        b = self.type_check_stmts(body, env)
+        o = self.type_check_stmts(orelse, env)
+        check_type_equal(b, o, ss[0])
+        return self.type_check_stmts(ss[1:], env)
+      case _:
+        return super().type_check_stmts(ss, env)
+\end{lstlisting}
+\fi}
 \caption{Type checker for the \LangIf{} language.}
 \label{fig:type-check-Rif}
 \end{figure}
 
 Next we discuss the type checker for \LangIf{} in
-Figure~\ref{fig:type-check-Rif}.  The operator \code{eq?} requires the
-two arguments to have the same type. The type of a Boolean constant is
-\code{Boolean}. The condition of an \code{if} must be of
-\code{Boolean} type and the two branches must have the same type.  The
-\code{operator-types} function adds dictionary entries for the other
-new operators.
+Figure~\ref{fig:type-check-Rif}.
+%
+The type of a Boolean constant is \code{Boolean}.
+%
+\racket{The \code{operator-types} function adds dictionary entries for
+  the other new operators.}
+%
+\python{Subtraction requires its arguments to be of type \INTTY{} and produces
+  an \INTTY{}. Negation requires its argument to be a \BOOLTY{} and
+  produces a \BOOLTY{}. Similarly for logical-and and logical-or. }
+%
+The equality operators requires the two arguments to have the same
+type.
+%
+\python{The other comparisons (less-than, etc.) require their
+arguments to be of type \INTTY{} and they produce a \BOOLTY{}.}
+%
+The condition of an \code{if} must
+be of \BOOLTY{} type and the two branches must have the same type.
+
 
 \begin{exercise}\normalfont
 Create 10 new test programs in \LangIf{}. Half of the programs should
 have a type error. For those programs, create an empty file with the
 same base name but with file extension \code{.tyerr}. For example, if
-the test \code{cond\_test\_14.rkt} is expected to error, then create
-an empty file named \code{cond\_test\_14.tyerr}.  This indicates to
-\code{interp-tests} and \code{compiler-tests} that a type error is
-expected. The other half of the test programs should not have type
-errors.
-
-In the \code{run-tests.rkt} script, change the second argument of
-\code{interp-tests} and \code{compiler-tests} to
-\code{type-check-Rif}, which causes the type checker to run prior to
-the compiler passes. Temporarily change the \code{passes} to an empty
-list and run the script, thereby checking that the new test programs
-either type check or not as intended.
+the test
+\racket{\code{cond\_test\_14.rkt}}\python{\code{cond\_test\_14.py}}
+is expected to error, then create
+an empty file named \code{cond\_test\_14.tyerr}.
+%
+\racket{This indicates to \code{interp-tests} and
+  \code{compiler-tests} that a type error is expected. }
+%
+\racket{This indicates to the \code{run-tests.py} scripts that a type
+  error is expected.}
+%
+The other half of the test programs should not have type errors.
+% 
+\racket{In the \code{run-tests.rkt} script, change the second argument
+  of \code{interp-tests} and \code{compiler-tests} to
+  \code{type-check-Rif}, which causes the type checker to run prior to
+  the compiler passes. Temporarily change the \code{passes} to an
+  empty list and run the script, thereby checking that the new test
+  programs either type check or not as intended.}
+%
+Run the test script to check that these test programs type check as
+expected.
+
 \end{exercise}
 
 

+ 1 - 1
defs.tex

@@ -164,7 +164,7 @@
 \newcommand{\UNIOP}[2]{\key{UnaryOp}\LP #1 \code{,} #2 \RP}
 \newcommand{\CUNIOP}[2]{#1~#2}
 \newcommand{\BINOP}[3]{\key{BinOp}\LP #1 \code{,} #2 \code{,} #3 \RP}
-\newcommand{\BOOLOP}[3]{\key{BoolOp}\LP #1 \code{,} #2 \code{,} #3 \RP}
+\newcommand{\BOOLOP}[3]{\key{BoolOp}\LP #1 \code{,} \LS #2 \code{,} #3 \RS \RP}
 \newcommand{\CMP}[3]{\key{Compare}\LP #1\code{,}\LS #2 \RS \code{,} #3\RP}
 \newcommand{\CBINOP}[3]{#1 #2 #3}
 \newcommand{\TRUE}{\key{True}}