HaskellとParsecでLisp REPL その2(Listも使えるREPL)
LISPとはそもそもLISt Processingの略なので、リストを全く扱えない状態からいち早く脱却したい。
というわけで次はリストの実装。ユーザから(a1 a2 a3)といった文字列を受けとり、List型として表現できるようにする。
gistからちゃんとしたgithub repoに変えてみた。リリースタグにリンクできるだけでなく、前回からの変更点も:
このようにリンクできるのがうれしい。
LispVal型の拡張
Atom型だけだった代数型データにListを追加:
data LispVal = Atom String | List [LispVal]
ListはLispValのリストを保有する。
showが使えるようにshowValも拡張:
showVal (List contents) = "(" ++ (unwords $ map show contents) ++ ")"
パーサの拡張
トップレベルのパーサparseExprがListを認識するよう拡張:
parseExpr :: Parser LispVal parseExpr = parseAtom <|> parseList
まずはparseAtomを試し、そのパースが失敗した場合parseListを試す。
parseList :: Parser LispVal parseList = do { char '('; xs <- sepBy parseExpr (skipMany1 space); char ')'; return $ List xs; }
評価器・REPL・main
evalやREPL、mainは変更の必要なし。現在リストは変換されず、そのまま値として扱われる。
使用例
これだけの変更で、ユーザがリストを入力するとちゃんとパースされて表示される:
~$ ./v2 >> (a b c) (a b c) >> (a (b c)) (a (b c)) >> (a (b (c (d)))) (a (b (c (d)))) >> (a b 2) No match: "lisp" (line 1, column 6): unexpected "2" expecting space, letter or "(" >> (+ a1 a2) (+ a1 a2) >> (+ 1 2) No match: "lisp" (line 1, column 4): unexpected "1" expecting space, letter or "(" >> quit ~$
再帰的な(a (b (c (d))))といった構造もちゃんとパースできている。しかしまだ数字はパースされない。次回は数字も受け入れるようにする。