; CSE341, Programming Languages ; Lecture 17: Implementing Languages Including Closures #lang racket (provide (all-defined-out)) ; a larger language with two kinds of values, booleans and numbers ; an expression is any of these: (struct const (int) #:transparent) ; int should hold a number (struct negate (e1) #:transparent) ; e1 should hold an expression (struct add (e1 e2) #:transparent) ; e1, e2 should hold expressions (struct multiply (e1 e2) #:transparent) ; e1, e2 should hold expressions (struct bool (b) #:transparent) ; b should hold #t or #f (struct eq-num (e1 e2) #:transparent) ; e1, e2 should hold expressions (struct if-then-else (e1 e2 e3) #:transparent) ; e1, e2, e3 should hold expressions ; a value in this language is a legal const or bool ; this version of eval-exp will give unfriendly error messages on ; legal ASTs with bad types because it does not check that we have ; the correct type of value after evaluating subexpressions (define (eval-exp-wrong e) (cond [(const? e) e] [(negate? e) (const (- (const-int (eval-exp-wrong (negate-e1 e)))))] [(add? e) (let ([i1 (const-int (eval-exp-wrong (add-e1 e)))] [i2 (const-int (eval-exp-wrong (add-e2 e)))]) (const (+ i1 i2)))] [(multiply? e) (let ([i1 (const-int (eval-exp-wrong (multiply-e1 e)))] [i2 (const-int (eval-exp-wrong (multiply-e2 e)))]) (const (* i1 i2)))] [(bool? e) e] [(eq-num? e) (let ([i1 (const-int (eval-exp-wrong (eq-num-e1 e)))] [i2 (const-int (eval-exp-wrong (eq-num-e2 e)))]) (bool (= i1 i2)))] ; creates (bool #t) or (bool #f) [(if-then-else? e) (if (bool-b (eval-exp-wrong (if-then-else-e1 e))) (eval-exp-wrong (if-then-else-e2 e)) (eval-exp-wrong (if-then-else-e3 e)))] [#t (error "eval-exp expected an exp")] ; not strictly necessary but helps debugging )) ; this version adds error checking to ensure everything has the correct type (define (eval-exp e) (cond [(const? e) e] [(negate? e) (let ([v (eval-exp (negate-e1 e))]) (if (const? v) (const (- (const-int v))) (error "negate applied to non-number")))] [(add? e) (let ([v1 (eval-exp (add-e1 e))] [v2 (eval-exp (add-e2 e))]) (if (and (const? v1) (const? v2)) (const (+ (const-int v1) (const-int v2))) (error "add applied to non-number")))] [(multiply? e) (let ([v1 (eval-exp (multiply-e1 e))] [v2 (eval-exp (multiply-e2 e))]) (if (and (const? v1) (const? v2)) (const (* (const-int v1) (const-int v2))) (error "multiply applied to non-number")))] [(bool? e) e] [(eq-num? e) (let ([v1 (eval-exp (eq-num-e1 e))] [v2 (eval-exp (eq-num-e2 e))]) (if (and (const? v1) (const? v2)) (bool (= (const-int v1) (const-int v2))) ; creates (bool #t) or (bool #f) (error "eq-num applied to non-number")))] [(if-then-else? e) (let ([v-test (eval-exp (if-then-else-e1 e))]) (if (bool? v-test) (if (bool-b v-test) (eval-exp (if-then-else-e2 e)) (eval-exp (if-then-else-e3 e))) (error "if-then-else applied to non-boolean")))] [#t (error "eval-exp expected an exp")] ; not strictly necessary but helps debugging )) ; this is a legal and correct AST ; we should be able to evaluate it (define good-test (multiply (negate (add (const 2) (const 2))) (const 7))) ; this is an illegal AST (creating a const from a boolean) ; we don't care what happens when we try to evaluate it (define non-test (multiply (negate (add (const #t) (const 2))) (const 7))) ; this is a /legal/ but incorrect AST (multiplying a const and a bool) ; we should give a reasonable error message /in terms of the AST/ (define bad-test (multiply (negate (add (const 2) (const 2))) (if-then-else (bool #f) (const 7) (bool #t)))) ; these functions are 'macros' in that they add new functionality to our ; language without extending the interpreter (define (andalso e1 e2) (if-then-else e1 e2 (bool #f))) (define and-test (andalso (eq-num (const 1) (const 1)) (eq-num (const 5) (const 5)))) (define (double e) (multiply e (const 2))) ; take a Racket list of arithmetic expressions, ; and evaluate to the product of all the expressions (define (list-product es) (if (null? es) (const 1) (multiply (car es) (list-product (cdr es))))) (define exp-list (list (const 2) (const 3) (add (const 3) (const 1)) (negate (const -1)))) (define list-product-test (list-product exp-list))