#lang plait
(define-type Exp
[num (n : Number)]
[plus (left : Exp) (right : Exp)]
[times (left : Exp) (right : 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
[(? `NUMBER) (num (s-exp->number s))]
[(? `(+ ANY ANY)) (plus (px 1) (px 2))]
[(? `(* ANY ANY)) (times (px 1) (px 2))]
[else (error 'parse (to-string s))])))
(test (parse `1) (num 1))
(test (parse `2.3) (num 2.3))
(test (parse `{+ 1 2}) (plus (num 1) (num 2)))
(test (parse `{+ 1
{+ {+ 2 3}
4}})
(plus (num 1)
(plus (plus (num 2)
(num 3))
(num 4))))
(test (parse `{+ 1 {* 2 3}})
(plus (num 1)
(times (num 2) (num 3))))
(test (parse `{+ {* 1 2} 3})
(plus (times (num 1) (num 2))
(num 3)))
(test (parse `{* {+ 1 2} {+ 3 4}})
(times
(plus (num 1) (num 2))
(plus (num 3) (num 4))))
(test/exn (parse `{1 + 2}) "")