UNB/ CS/ David Bremner/ teaching/ cs4613/ lectures/ lecture08/ exp2.rkt
#lang plait
(define-type BinOp
  [plus]
  [++]) ;; string concat

(define-type Exp
  [binE (operator : BinOp)
        (left : Exp)
        (right : Exp)]
  [numE (value : Number)]
  [strE (value : String)])

(define-type Value
  [strV (value : String)]
  [numV (value : Number)])

(define (on-strings func l r)
  (cond
    [(and (strV? l) (strV? r))
     (strV (func (strV-value l) (strV-value r)))]
    [else  (error 'interp "expected 2 numbers")]))

(define (on-nums func l r)
  (cond
    [(and (numV? l) (numV? r))
     (numV (func (numV-value l) (numV-value r)))]
    [else  (error 'interp "expected 2 numbers")]))

(define (interp expr)
  (type-case Exp expr
    [(numE n) (numV n)]
    [(strE s) (strV s)]
    [(binE o l r)
     (let ([l-val (interp l)]
           [r-val (interp r)])
       (type-case BinOp o
         [(++) (on-strings string-append l-val r-val)]
         [(plus) (on-nums + l-val r-val)]))]))

(test (interp (binE (plus) (numE 3) (numE 4)))
        (numV 7))
(test (interp (binE (++) (strE "3") (strE "4")))
      (strV "34"))
(test/exn (interp (binE (plus) (numE 3) (strE "4")))
          "numbers")