(use parser.peg)
; reference for parser.peg:  http://d.hatena.ne.jp/mjt/20110626/p2 

; parser.peg を使うテスト

; 型環境(signature) とラムダ項をパーズする.

; 型環境  ::= name : type [, 型環境]

; type ::= Typename (頭が大文字ならそれはプリミティブ型)
;        | a | b | .. (頭が小文字ならそれは多相型)
;        | type -> type
;        | (type) -> type

; ラムダ項 ::= 0 | 1 | 2 | ..
;            | #t | # f
;            | a | b | .. | y | z | aa | ab | ..
;            | (ラムダ項 ラムダ項)
;            | ^x -> ラムダ項

(define parse-lambda
    (letrec ((hat ($c #\^))
             (parel ($seq spaces ($c #\() spaces))
             (parer ($seq spaces ($c #\)) spaces))
             (shp ($c #\#))
             (arrow ($seq spaces ($string "->") spaces))

             (id  ($many1 ($one-of #[a-z\-\?])))
             (var ($do (id id) ($return `(var . ,(list->string id)) )))

             (int ($do (n ($many1 ($one-of #[0-9])))
                       ($return (cons 'int
                            (read-from-string (list->string n))))))
             (true ($do (s ($string "t")) ($return (cons 'bool #t))))
             (fals ($do (s ($string "f")) ($return (cons 'bool #f))))
             (bool ($do (shp) ($or true fals)))

             (lmbda  ($do (hat) (spaces) (arg id) (arrow) (body term)
                         ($return `(lambda . ,(list (list->string arg) body))) ))
             (abst  ($or ($do (parel) (l lmbda) (parer) ($return l))
                         lmbda))

             (appli  ($do (parel) (m1 term) (spaces) (m2 term) (parer)
                          ($return (list 'apply m1 m2))))

             (term ($or ($try abst) ($try appli) ($try var) ($try int) ($try bool))) )
    (cut peg-parse-string term <>)))

(define parse-env
    (letrec ((colon ($seq spaces ($c #\:) spaces))
             (comma ($seq spaces ($c #\,) spaces))
             (id    ($many ($one-of #[A-Za-z\?\-])))
             (arrow ($seq spaces ($string "->") spaces))
             (parel ($seq spaces ($c #\() spaces))
             (parer ($seq spaces ($c #\)) spaces))
             (to2   ($do parel (t1 type) parer arrow (t2 type)
                         ($return (cons t1 t2))))
             (to    ($do (t1 id) arrow (t2 type)
                         ($return (cons (list->string t1) t2))))
             (ti    ($do (t id)
                         ($return (list (list->string t))) ))
             (type  ($or ($try to2) ($try to) ($try ti)))
             (field ($do (x id) (_ colon) (t type)
                         ($return (cons (list->string x) t)) ))
             (env   ($sep-by field comma)))
    (cut peg-parse-string env <>)))

; 入力をS式にしてもらえば(read)で一発で読み込めちゃうんだけど、
; それじゃ芸がないからちゃんとパーズしようと思ったんだけど、
; 結果的にS式とあんまり変わらないや.