;; An expr is
;;  - number
;;  - symbol
;;  - (make-add expr expr)
;;  - (make-mult expr expr)
;;  - (make-proc symbol expr)
;;  - (make-call expr expr)

(define-struct add (left right))
(define-struct mult (left right))
(define-struct proc (param body))
(define-struct call (func arg))

;;--------------------------------------------------------------------

;; Interpreter with substitution

;; interp : expr -> value
;; evaluates exp with given function def
(define (interp anexpr)
  (cond [(number? anexpr) anexpr]
        [(symbol? anexpr) (error 'interp "Reached free variable")]
	[(proc? anexpr) anexpr]
        [(add? anexpr)
	 (+ (interp (add-left anexpr))
	    (interp (add-right anexpr)))]
        [(mult? anexpr)
	 (* (interp (mult-left anexpr))
	    (interp (mult-right anexpr)))]
        [(call? anexpr)
	 (interp
	  (subst (proc-param (interp (call-func anexpr)))
		 (interp (call-arg anexpr))
		 (proc-body (interp (call-func anexpr)))))]))

;; subst : symbol value expr -> expr
;; returns new expr with all uses of symbol replaced with given value
(define (subst forsym newval anexpr)
  (cond [(number? anexpr) anexpr]
        [(symbol? anexpr) (cond [(symbol=? anexpr forsym) newval]
				[else anexpr])]
	[(proc? anexpr) (cond [(symbol=? (proc-param anexpr) forsym) anexpr]
			      [else (make-proc (proc-param anexpr)
					       (subst forsym newval
						      (proc-body anexpr)))])]
        [(add? anexpr) (make-add (subst forsym newval (add-left anexpr))
				 (subst forsym newval (add-right anexpr)))]
        [(mult? anexpr) (make-mult (subst forsym newval (mult-left anexpr))
				   (subst forsym newval (mult-right anexpr)))]
        [(call? anexpr) (make-call (subst forsym newval (call-func anexpr) )
				   (subst forsym newval (call-arg anexpr)))]))
