4040
4141We have added multiple, disjoint types, but mostly swept issues of
4242errors under the rug by considering type mismatches as meaningless.
43- Now let 's redesign the semantics to specify the error behavior of such
44- programs.
43+ But undefined behavior, while convenient from the compiler writer's
44+ perspective, is the gateway to all kinds of bad outcomes for
45+ programmers. Let's redesign the semantics to specify the error
46+ behavior of such programs.
4547
4648
4749We'll call it @bold{@this-lang}.
@@ -134,18 +136,32 @@ results that may be given by the interpretation function:
134136Type mismatches can arise as the result of primitive operations being
135137applied to arguments for which the primitive is undefined, so we
136138revise @racket[interp-prim1] to check all necessary preconditions
137- before carrying out an operation, and producing an error in case
138- those conditions are not met:
139+ before carrying out an operation.
140+
141+ We will take advantage of Racket's exception system to @racket[raise]
142+ an error result whenever an error occurs. This is a nice way of
143+ shortcircuiting the remaining evaluation since as soon as we encounter
144+ an error, we know this is going to be the answer for the whole
145+ program.
139146
140147@codeblock-include["extort/interp-prim.rkt " ]
141148
142- Within the interpreter, we update the type signature to reflect the
143- fact that interpreting an expression produces an answer, no longer
144- just an expression. We must also take care to observe that evaluating
145- a subexpression may produce an error and as such it should prevent
146- further evaluation. To do this , the interpreter is written to check
147- for an error result of any subexpression it evaluates before
148- proceeding to evaluate another subexpression:
149+ Notice that the signature for these functions indicate that you get a
150+ value, or they raise an exception. The functions for interpreting
151+ primitives check all of the necessary preconditions of a primitive
152+ before calling the corresponding Racket function. This ensures that
153+ the Racket functions are always @emph{safe}, they cannot raise an
154+ error. As a final catch-all case , we know that some precondition must
155+ not have held and we can raise the @racket['err ] answer to indicate an
156+ error.
157+
158+ Within the interpreter, we restructure things so that there is now a
159+ top-level @racket[interp] function that installs an exception handler.
160+ Should interpreting the expression raise @racket['err ], it handles
161+ this exception and returns the @racket['err ] answer. The
162+ @racket[interp] function makes use of an auxilary function
163+ @racket[interp-e] that does the work of recursively interpreting the
164+ meaning of an expression:
149165
150166@codeblock-include["extort/interp.rkt " ]
151167
@@ -158,6 +174,16 @@ examples given earlier:
158174(interp (parse '(if (zero? #f ) 1 2 )))
159175]
160176
177+ Note that if you use the @racket[interp-e] function, expressions that
178+ cause errors will raise an exception:
179+
180+ @ex[
181+ (eval:error (interp-e (parse '(add1 #f ))))
182+ (eval:error (interp-e (parse '(zero? #t ))))
183+ (eval:error (interp-e (parse '(if (zero? #f ) 1 2 ))))
184+ ]
185+
186+
161187This interpreter implicitly relies on the state of the input and
162188output port, but we can define a pure interpreter like before,
163189which we take as the specification of our language:
@@ -376,7 +402,6 @@ Linking in the run-time allows us to define the @racket[exec] and
376402@racket[exec/io] functions:
377403
378404@codeblock-include["extort/exec.rkt " ]
379- @codeblock-include["extort/exec-io.rkt " ]
380405
381406We can run examples:
382407
0 commit comments