#lang plait
(define-type FLANG
[Num (val : Number)]
[Add (l : FLANG) (r : FLANG)]
[Sub (l : FLANG) (r : FLANG)]
[Mul (l : FLANG) (r : FLANG)]
[Div (l : FLANG) (r : FLANG)]
[Id (name : Symbol)]
[Let1 (id : Symbol) (named-expr : FLANG) (bound-body : FLANG)]
[Lam (param : Symbol) (body : FLANG)]
[Call (lam : FLANG) (val : FLANG)]) ; first type!
(define (parse-error sx)
(error 'parse-sx (string-append "parse error: " (to-string sx))))
(define (sx-ref sx n) (list-ref (s-exp->list sx) n))
(define (parse-sx sx)
(cond
[(s-exp-number? sx) (Num (s-exp->number sx))]
[(s-exp-symbol? sx) (Id (s-exp->symbol sx))]
[(s-exp-match? `(let1 (SYMBOL ANY) ANY) sx)
(let* ([def (sx-ref sx 1)]
[id (s-exp->symbol (sx-ref def 0))]
[val (parse-sx (sx-ref def 1))]
[expr (parse-sx (sx-ref sx 2))])
(Let1 id val expr))]
[(s-exp-match? `(lam SYMBOL ANY) sx)
(let* ([id (s-exp->symbol (sx-ref sx 1))]
[body (parse-sx (sx-ref sx 2))])
(Lam id body))]
[(s-exp-match? `(ANY ANY) sx)
(Call (parse-sx (sx-ref sx 0))
(parse-sx (sx-ref sx 1)))]
[(s-exp-list? sx)
(let* ([l (λ () (parse-sx (sx-ref sx 1)))]
[r (λ () (parse-sx (sx-ref sx 2)))])
(case (s-exp->symbol (sx-ref sx 0))
[(+) (Add (l) (r))]
[(-) (Sub (l) (r))]
[(*) (Mul (l) (r))]
[(/) (Div (l) (r))]
[(call) (Call (l) (r))]
[else (parse-error sx)]))]
[else (parse-error sx)]))
;; gets a Racket numeric binary operator, and uses it within a FLANG
;; `Num' wrapper (note H.O type)
(define (arith-op op expr1 expr2)
(local
[(define (Num->number e)
(type-case FLANG e
[(Num n) n]
[else (error 'arith-op "expects a number")]))]
(Num (op (Num->number expr1)
(Num->number expr2)))))
;; evaluates FLANG expressions by reducing them to *expressions*
(define (eval expr)
(type-case FLANG expr
[(Num n) expr] ; <- change here
[(Add l r) (arith-op + (eval l) (eval r))]
[(Sub l r) (arith-op - (eval l) (eval r))]
[(Mul l r) (arith-op * (eval l) (eval r))]
[(Div l r) (arith-op / (eval l) (eval r))]
[(Let1 bound-id named-expr bound-body)
(eval (subst bound-body
bound-id
(eval named-expr)))] ; <- no `(Num ...)'
[(Id name) (error 'eval "free identifier")]
[(Lam bound-id bound-body) expr] ; <- similar to `Num'
[(Call lam arg-expr)
(let [(funV (eval lam))]
(type-case FLANG funV
[(Lam bound-id bound-body)
(eval (subst bound-body ; <- just like `let1'
bound-id
(eval arg-expr)))]
[else (error 'eval "`call' expects a function")]))]))
;; substitutes the second argument with the third argument in the
;; first argument, as per the rules of substitution; the resulting
;; expression contains no free instances of the second argument
(define (subst expr from to)
(type-case FLANG 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)
(Let1 bound-id
(subst named-expr from to)
(if (eq? bound-id from)
bound-body
(subst bound-body from to)))]
[(Call l r) (Call (subst l from to) (subst r from to))]
[(Lam bound-id bound-body)
(if (eq? bound-id from)
expr
(Lam bound-id (subst bound-body from to)))]))
(define (run sx)
(let ([result (eval (parse-sx sx))])
(type-case FLANG result
[(Num n) n]
[else (error 'run "returned a non-number")])))
(test (run `{let1 {identity {lam x x}}
{let1 {foo {lam x {+ x 1}}}
{{identity foo} 123}}})
124)
(test (run
`{{{lam x {x 1}} {lam x {lam y {+ x y}}}} 123})
124)