; CSE413, Winter 2019 ; Encoding languages as data - lists with tags, then racket structs ; Examples courtesy of Dan Grossman, CSE 413 #lang racket (provide (all-defined-out)) ; make everything available to other modules ; here we make a little language of arithmetic expressions using Racket lists ; in order to motivate a better way with Racket's structs ; just helper functions that make lists where first element is a symbol ; Note: More robust could check at run-time the type of thing being put in (define (Const i) (list 'Const i)) (define (Negate e) (list 'Negate e)) (define (Add e1 e2) (list 'Add e1 e2)) (define (Multiply e1 e2) (list 'Multiply e1 e2)) ; just helper functions that test what "kind of exp" ; Note: More robust could raise better errors for non-exp values (define (Const? x) (eq? (car x) 'Const)) (define (Negate? x) (eq? (car x) 'Negate)) (define (Add? x) (eq? (car x) 'Add)) (define (Multiply? x) (eq? (car x) 'Multiply)) ; just helper functions that get the pieces for "one kind of exp" ; Note: More robust could check "what kind of exp" ; could do things like this: ; (define (Const-int e) (cadr e)) ; but this avoids "unnecessary function wrapping" (cf. (if e #t #f)) (define Const-int cadr) (define Negate-e cadr) (define Add-e1 cadr) (define Add-e2 caddr) (define Multiply-e1 cadr) (define Multiply-e2 caddr) ; an elegant recursive evaluator with one complication: ; we return something like (list 'Const 42) instead of 42 ; this is "a good way to do it" when there are multiple kinds of things you might return ; (which there isn't here but will be on homework and in the next example) (define (eval-exp1 e) (cond [(Const? e) e] ; note returning an exp, not a number [(Negate? e) (Const (- (Const-int (eval-exp1 (Negate-e e)))))] [(Add? e) (let ([v1 (Const-int (eval-exp1 (Add-e1 e)))] [v2 (Const-int (eval-exp1 (Add-e2 e)))]) (Const (+ v1 v2)))] [(Multiply? e) (let ([v1 (Const-int (eval-exp1 (Multiply-e1 e)))] [v2 (Const-int (eval-exp1 (Multiply-e2 e)))]) (Const (* v1 v2)))] [#t (error "eval-exp expected an exp")])) ; a simple test case for eval-exp1 (define a-test1 (eval-exp1 (Multiply (Negate (Add (Const 2) (Const 2))) (Const 7)))) ; Our second approach using Racket's structs: more convenient and stronger guarantees! (struct const (int) #:transparent) (struct negate (e) #:transparent) (struct add (e1 e2) #:transparent) (struct multiply (e1 e2) #:transparent) ; as in eval-exp1, we do not return something like 42. ; now we return something like (const 42) (define (eval-exp2 e) (cond [(const? e) e] ; note returning an exp, not a number [(negate? e) (const (- (const-int (eval-exp2 (negate-e e)))))] [(add? e) (let ([v1 (const-int (eval-exp2 (add-e1 e)))] [v2 (const-int (eval-exp2 (add-e2 e)))]) (const (+ v1 v2)))] [(multiply? e) (let ([v1 (const-int (eval-exp2 (multiply-e1 e)))] [v2 (const-int (eval-exp2 (multiply-e2 e)))]) (const (* v1 v2)))] [#t (error "eval-exp expected an exp")])) ; a simple test case for eval-exp2 (define a-test2 (eval-exp2 (multiply (negate (add (const 2) (const 2))) (const 7))))