[Lisp処理系を作る] 14日目 パーサを作る

ソースとなる文字列を解釈して Lisp のオブジェクトに変換する処理を実装する。 パーサとしての文字列処理自体は Lisp とは直接関係が無いので、適当にソースを見て下さい。

仕様

大まかな仕様は以下な感じ。

  • 文字列からは n 個の S 式と解釈できる。
  • S 式は Atom か List 。
  • List は "(" で始まり ")" で閉じる。
  • そうでなければ Atom と解釈する。
    • 数字のみ場合は数値
    • 二重引用符で囲まれたものは文字列
    • 他はだいたい Symbol として解釈

ここからさらに色々と追加される。

  • ";" から行末まではコメント
  • "'" がある場合は "(quote 次の S 式)" として解釈する
  • "#'" がある場合は "(function 次の S 式)" として解釈する
  • 以下の記号は何かしら意味があるらしいが実装していなかったりわからないのでとりあえずパースエラーにしている
    • "`"
      • "," と組合せで "'" の特殊な処理が出来るらしい。
    • ","
    • ":"
      • パッケージの解決に使うらしい。
    • "\"
      • エスケープらしいがよくわからない。
    • "#"
      • 配列(List とは違う)のを扱うらしい。
    • "|"
      • なんじゃらほい

この辺の細かい仕様については解説している資料が殆ど無くて、 非常に断片的にしか情報が得られなかったので結構大変でした。

実装

詳しくはソースを見てもらうとして、簡単に実装方法を説明しておく。

クラスは Parser クラスと Scanner クラスの 2 つになる。

Scanner クラス

与えられた1つの文字列をトークンの列に分断する。 Lexer ともいえるのかも。この辺はちょっとあいまい。

基本的に 空白文字や "(" などが見つかったらそれまでの文字列をトークンとして分断する感じ。 コメントもここの時点で読み飛ばすようにしている。

Parser クラス

Scanner クラスからトークンを 1 つずつ受け取り、その並びを規則に従い S 式を作成する。 ここで "(" があるのにその後のトークンに ")" が無い場合など、規則違反があったらエラーにするなどの判断をする。