lispで言語実装

新年だし、言語実装の勉強をはじめよう。ベース言語にはlispを選択。以下の手順で進めようと考えている。

  1. 電卓の実装
  2. TinyCの実装
  3. 若干実用的な言語の実装

lisp処理系は、xyzzy lispを利用する。

電卓の実装(1) 字句解析

「(100+3)*10」のような入力に対して「1030」を返す電卓を作成する。言語実装セオリーに沿って、以下の順番で作成する。

  1. 字句解析 ← いまここ
  2. 構文解析
  3. 意味解析

普通、電卓程度では、ここまで分割しないと思われるが、勉強もかねてこのようにしたい。
字句解析に必要な関数は以下の4つ。

  1. set-src-stream … ソースとなる文字列(ストリーム)を登録する
  2. get-token … トークンを取得する
  3. unget-token … トークンを差し戻す
  4. peek-token … 先頭のトークンをのぞき見る

これらを以下のように実装した。

(progn
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ;;; 字句解析
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  (setq *src-stream nil); ソースとなるストリーム
  (setq *unget-token-list nil); unget-tokenされたトークンを保存するリスト

  (defun set-src-stream(stm)
    ;ソースとなるストリームをセット。
    (setq *src-stream stm))
  (defun peek(st)
    ;ストリームを覗き見る関数。
    (let ((c (read-char st nil)))
      (if c
          (unread-char c st))
      c))

  (defun get-token()
    ;トークンを取得。無い場合はnilを返す。
    (if (= 0 (length *unget-token-list))
        (let ((c (read-char *src-stream nil)))
          (cond ((equal c nil) nil)
                ((digit-char-p c) (let ((res 0))
                                    (while (progn ;do〜whileイデオム
                                             (setq res (+ (* res 10) (digit-char-p c)))
                                             (setq c (read-char *src-stream nil))
                                             (and c (digit-char-p c))));継続条件
                                    (if c (unread-char c *src-stream))
                                    res))
                ((member c '(#\SPC #\TAB #\RET #\LFD)) (get-token))
                (t (format nil "~A" c))))
      (pop *unget-token-list)))
  
  (defun unget-token(tok)
    ;トークンを差し戻す。
    (push tok *unget-token-list))

  (defun peek-token()
    ;トークンを覗き見る関数。無い場合はnilを返す。
    (let ((tok (get-token)))
      (unget-token tok)
      tok))
  
  (setq src "(-100  \n\t      + +3)*-10")
  (set-src-stream (make-string-input-stream src))
  (while (print (get-token)))
  )

出力結果

"(" 
"-" 
100 
"+" 
"+" 
3 
")" 
"*" 
"-" 
10 
nil 
nil