;; The first three lines of this file were inserted by DrScheme. They record metadata
;; about the language level of this file in a form that our tools can easily process.
#reader(lib "reader.ss" "plai" "lang")
;; An arithmetic interpreter that uses a typed parse tree to reduce implementation errors.
;;
;; Morgan McGuire morgan@cs.williams.edu
; define-type creates a series of related new types. Here, expr is the base type.
; (This is similar to a series of define-struct statements, except that to uses contracts
; to check type signatures and names the constructor after the type rather than calling it
; "make-type".)
(define-type expr
; Each clause defines an additional type. This one defines num, which has one part,
; n, that is a Scheme number. This clause creates num? and num-n (and set-num-n!, but
; we won't use that one).
(num (n number?))
(add (lhs expr?) (rhs expr?))
(sub (lhs expr?) (rhs expr?)))
; We write a regular Scheme procedure that accepts a Scheme list and converts it
; into a calc expression.
(define (parse e)
(cond
; If the expression is a number literal, make a calc number:
((number? e) (num e))
; If the expression looks like ( ... ), choose the type
; based on what is first.
((list? e)
(case (first e)
; Begins with "+": create an addition expression:
((+) (add (parse (second e))
(parse (third e))))
; Begins with "-": create a subtraction expression:
((-) (sub (parse (second e))
(parse (third e))))
))))
; The calc interpreter using the types defined above. Note that
; this is the same code as calc-1; we changed only the implementation
; of the num, add, and sub types.
(define (calc-2 e)
(cond [(num? e) (let ([n (num-n e)]) n)]
[(add? e) (let ([lhs (add-lhs e)]
[rhs (add-rhs e)])
(+ (calc-2 lhs) (calc-2 rhs)))]
[(sub? e) (let ([lhs (sub-lhs e)]
[rhs (sub-rhs e)])
(- (calc-2 lhs) (calc-2 rhs)))]))
; The calc interpreter from the textbook (more or less). This is our calc-2 interpreter
; re-written using the type-case special form to reduce some of the boilerplate.
(define (calc-3 e)
; type-case takes a type (here, expr) and a value (here a calc expression e) of
; that type, and then evaluates the one of the following clauses that describes e
(type-case expr e
; If e is a num, extract the underlying Scheme number and return it
(num (n) n)
(add (lhs rhs) (+ (calc-3 lhs) (calc-3 rhs)))
(sub (lhs rhs) (+ (calc-3 lhs) (calc-3 rhs)))))
;; Example:
(calc-3 (parse '(+ 1 2)))