#lang scheme (require test-engine/scheme-tests) (require 2htdp/universe) (require "image-ops.ss") (define-syntax-rule (define/abstract (m . args)) ;; ==> (define/public (m . args) (error 'm "is an abstract method"))) ;; ----------------------------------------------------------------------------- ;; graphical constants (global so we can test) (define OP (text "(" 11 "black")) (define CL (text ")" 11 "black")) (define PL (text "+" 11 "black")) (define ML (text "*" 11 "black")) (define SP (text " " 11 "black")) (define SB (text "-" 11 "black")) (define PAIR-OF-PAREN (+ (image-width OP) (image-width CL))) ;; ----------------------------------------------------------------------------- ;; data representation of Expr = number | plus Expr Expr+ | mult Expr Expr+ ;; all Exprs implement expr<%> (define expr<%> (interface () render ;; Number -> Image ;; render this expression but stay within the given width )) ;; students don't know this; I am doing this for your sake (define iexpr<%> ;; --- a specializatin interface --- (interface (expr<%>) ;; --- things i wouldn't tell the world about --- render-1-line ;; -> Image ;; render this expression as a single line ;; OVERRIDE, if expression is not composite render-stacked ;; Number -> Image ;; render this expression in a stacked fashion ;; OVERRIDE, if expression is not composite render-op ;; -> Image ;; retrieve the operator for this expression ;; ABSTRACT, fill in if expression is composite get-list ;; -> [Listof iexpr<%>] ;; retrieve the list of operands for this expression ;; ABSTRACT, fill in if expression is composite prefix? ;; -> Boolean ;; is this expression to be rendered as prefix? ;; ABSTRACT, fill in if expression is composite )) ;; ----------------------------------------------------------------------------- ;; TEMPLATE and HOOK ABSTRACT CLASS ;; aexpr% collects common methods (define aexpr% (class* object% (iexpr<%>) (define/public (render width) (local ((define e:image (render-1-line))) (if (<= (image-width e:image) width) e:image (render-stacked width)))) (define/public (render-stacked width) (local ((define w-- (- width PAIR-OF-PAREN)) (define lst (map (lambda (e) (send e render w--)) (get-list))) (define subs (if (prefix?) (image-stack* (cons (render-op) lst)) (separated-by lst (render-op) image-stack*)))) (image-top OP (image-bottom subs CL)))) (define/public (render-1-line) (local ((define lst (map (lambda (e) (send e render-1-line)) (get-list)))) (if (prefix?) (parens (separated-by (cons (render-op) lst) SP image-append*)) (local ((define sep (image-append* (list SP (render-op) SP)))) (parens (separated-by lst sep image-append*)))))) ;; the following two methoda are functions, and if it weren't for ;; Bootcamp restrictions, I would define them as such. ;; Image -> Image ;; surround image with open and close parentheses (define/private (parens img) (image-append* (list OP img CL))) ;; [Listof Image] Image ([Listof Image] -> Image) -> Image ;; create an image from all images in loi, separated by sep, combined via C (define/private (separated-by loi sep combinator) (local ((define r (reverse loi))) (foldl (lambda (f r) (combinator (list r sep f))) (first loi) (rest loi)))) (define/abstract (render-op)) (define/abstract (get-list)) (define/abstract (prefix?)) (super-new))) ;; ----------------------------------------------------------------------------- ;; CONCRETE CLASSES ;; represent a numeric value (define num% (class* aexpr% (iexpr<%>) (init-field n) ;; Number, the numeric value (define/override (render-stacked width) (error "too wide!!!")) (define/override (render-1-line) (text (number->string n) 11 "black")) (super-new))) ;; represent a plus expression with n >= 2 sub-expressions (define pls% (class* aexpr% (iexpr<%>) (init-field x ;; [Listof iexpr<%>], list of operands in?) ;; Boolean, whether it needs an infix representation (define/override (render-op) PL) (define/override (get-list) x) (define/override (prefix?) (not in?)) (super-new))) ;; represent a multiplication expression with n >= 2 sub-expressions (define mul% (class* aexpr% (iexpr<%>) (init-field x ;; [Listof iexpr<%>], list of operands in?) ;; Boolean, whether it needs an infix representation (define/override (render-op) ML) (define/override (get-list) x) (define/override (prefix?) (not in?)) (super-new))) ;; ----------------------------------------------------------------------------- ;; the EXTENSION ;; represent a subtraction expression with n = 2 sub-expressions (define sub% (class* aexpr% (iexpr<%>) (init-field x ;; iexpr<%>, first operand y ;; iexpr<%>, second operand in?) ;; Boolean, whether it needs an infix representation (define/override (render-op) SB) (define/override (get-list) (list x y)) (define/override (prefix?) (not in?)) (super-new))) ;; ----------------------------------------------------------------------------- ; ; ; ; ; ; ; ; ; ;;;;; ;;; ;;;; ;;;;; ;;; ; ;; ;; ; ; ; ; ; ; ; ; ;; ; ; ;; ; ; ;;;;; ;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;; ; ;;; ;;;; ;;;; ;;; ; ; ; ;; ; ; ; ; ;;;; ;; constructors for testing (define (make-pls x in?) (new pls% [x x][in? in?])) (define (make-mul x in?) (new mul% [x x][in? in?])) (define (make-num x) (new num% [n x])) (define (make-sub x y in?) (new sub% [x x][y y][in? in?])) ;; data for testing (define one (text "1" 11 "black")) (define two (text "2" 11 "black")) (define thr (text "3" 11 "black")) (define pls123 (make-pls (map make-num (list 1 3 2)) true)) (define pls123-1-line (image-append* (list OP one SP PL SP thr SP PL SP two CL))) (define pls123-stack (image-top OP (image-bottom (image-stack* (list one PL thr PL two)) CL))) (define mul123 (make-mul (map make-num (list 1 3 2)) true)) (define mul123-1-line (image-append* (list OP one SP ML SP thr SP ML SP two CL))) (define mul-pls-1-3 (make-mul (list (make-pls (map make-num (list 1 3 2)) true) (make-pls (map make-num (list 1 3 2)) true)) false)) (define mul-pls-1-3-stack (local ((define subs (list ML pls123-1-line pls123-1-line))) (image-top OP (image-bottom (image-stack* subs) CL)))) (define wide-expr (make-mul (map make-num (list 123 123)) false)) (define wide (local ((define t123 (text "123" 11 "black"))) (image-append* (list OP ML t123 t123 CL)))) (define sub13 (make-sub (make-num 1) (make-num 3) true)) (define sub13-1-line (image-append* (list OP one SP SB SP thr CL))) (check-expect (send pls123 render (- (image-width pls123-1-line) 1)) pls123-stack) (check-expect (send mul-pls-1-3 render (* 2 (image-width pls123-1-line))) mul-pls-1-3-stack) (check-error (send wide-expr render (/ (image-width wide) 2)) "too wide!!!") (check-expect (send pls123 render-1-line) pls123-1-line) (check-expect (send mul123 render-1-line) mul123-1-line) (check-expect (send sub13 render (image-width sub13-1-line)) sub13-1-line) ;; ----------------------------------------------------------------------------- ;; run the examples specified in problem statement: (define hw-example (make-pls (list (make-mul (map make-num (list 1 2)) true) (make-pls (map make-num (list 3 4)) false)) true)) (send hw-example render 120) (send hw-example render 60) (send hw-example render 30) (test)