#lang plait
(define-type Exp
[numE (n : Number)]
[plusE (left : Exp) (right : Exp)]
[timesE (left : Exp) (right : Exp)]
[lamE (var : Symbol) (body : Exp)]
[appE (fun : Exp) (arg : Exp)]
[varE (name : Symbol)]
[let1E (var : Symbol)
(value : Exp)
(body : Exp)])
(define (parse s)
(local
[(define (sx n) (list-ref (s-exp->list s) n))
(define (px n) (parse (sx n)))
(define (? pat) (s-exp-match? pat s))]
(cond
[(? `SYMBOL) (varE (s-exp->symbol s))]
[(? `NUMBER) (numE (s-exp->number s))]
[(? `(+ ANY ANY)) (plusE (px 1) (px 2))]
[(? `(* ANY ANY)) (timesE (px 1) (px 2))]
[(? `(let1 (SYMBOL ANY) ANY))
(let* ([def (sx 1)]
[parts (s-exp->list def)]
[var (s-exp->symbol (list-ref parts 0))]
[val (parse (list-ref parts 1))]
[body (px 2)])
(let1E var val body))]
[(? `(lam SYMBOL ANY))
(lamE (s-exp->symbol (sx 1)) (px 2))]
[(? `(ANY ANY)) (appE (px 0) (px 1))]
[else (error 'parse (to-string s))])))
(test (parse
`{let1 {x 3}
{let1 {f {lam y {+ x y}}}
{f 3}}})
(let1E 'x (numE 3)
(let1E 'f (lamE 'y (plusE (varE 'x) (varE 'y)))
(appE (varE 'f) (numE 3)))))