#lang plait
(define-type LAE
[Num (val : Number)]
[Add (l : LAE) (r : LAE)]
[Sub (l : LAE) (r : LAE)]
[Mul (l : LAE) (r : LAE)]
[Div (l : LAE) (r : LAE)]
[Id (name : Symbol)]
[Let1 (name : Symbol)
(val : LAE)
(expr : LAE)])
; returns expr[to/from].
; leaves no free occurences of `to'
(define (subst expr from to)
(type-case LAE expr
[(Num n) expr]
[(Add l r) (Add (subst l from to)
(subst r from to))]
[(Sub l r) (Sub (subst l from to)
(subst r from to))]
[(Mul l r) (Mul (subst l from to)
(subst r from to))]
[(Div l r) (Div (subst l from to)
(subst r from to))]
[(Id name) (if (eq? name from) to expr)]
[(Let1 bound-id named-expr bound-body)
(if (eq? bound-id from)
expr ; <-- don't go in!
(Let1 bound-id
named-expr
(subst bound-body from to)))]))
(test (subst
;; {+ x {let1 {x 3} x}
(Add (Id 'x) (Let1 'x (Num 3) (Id 'x)))
'x (Num 5))
;; {+ 5 {let1 {x 3} x}}
(Add (Num 5) (Let1 'x (Num 3) (Id 'x))))
(test (subst
;; {+ x {let1 {y 3} x}}
(Add (Id 'x) (Let1 'y (Num 3) (Id 'x)))
'x (Num 5))
;; {+ 5 {let1 {y 3} 5}}
(Add (Num 5) (Let1 'y (Num 3) (Num 5))))