let* とスペシャル変数のバグ
http://fixdap.com/p/xyzzy/34388/
(defvar *special* :global) => *special* (let* ((*special* :local) (x *special*)) ; ここで *special* がシャドウされてない x) => :global
関係ありそうなのが
- src/eval.cc#1024 Flet_star
- src/lex.cc#20 lex_env::let
- xyzzy.src/lex.cc at master · southly/xyzzy.src · GitHub
- レキシカル環境に束縛を追加
- src/eval.cc#548 eval
- xyzzy.src/eval.cc at master · southly/xyzzy.src · GitHub
- レキシカル環境内で式を評価
- src/eval.cc#293 declare_progn
- xyzzy.src/eval.cc at master · southly/xyzzy.src · GitHub
- スペシャル変数へのレキシカルな束縛(シャドウ)を有効化して body を評価
(let* ((VAR VALUE-FORM)) . BODY)
Flet_star は
- 新しいレキシカル環境 lex_env nlex を作る
- nlex.let で nlex に (VAR VALUE-FORM) の束縛を追加
- このとき VALUE-FORM は、eval によって nlex の中で評価される
- BODY を declare_progn で nlex の中で評価
という流れ。
シンボルは C++ 側では class lsymbol のインスタンスで、その値が2つある。
lex_env::let でレキシカル環境にシンボルの束縛を登録できる。
eval でシンボルを評価すると、スペシャル変数だったらメンバ変数の方を、そうでなければレキシカル環境の値を返す*1。
declare_progn はレキシカル環境に登録されたシンボルがスペシャル変数だったら*2
ということをしているっぽい。
なので、Flet_star から nlet.let したときは単に eval じゃなくて declare_progn 経由で評価させないといけないのではないか。と思うのだけどどうやったら declare_progn 経由にできるのかよくわからない <- 今ココ
余談 #1: このバグはインタープリタで評価した時だけみたい
(funcall (compile nil (lambda () (let* ((*special* :local) (x *special*)) x)))) => :local
余談 #2: こんなバグ見つけた
*special* => :global (let ((*special* :local-1) (*special* :local-2)) *special*) ;; 2つ目の束縛が無視されてる => :local-1 ;; さっきの let で変更されてる *special* => :local-2 (setq *special* :global) => :global ;; コンパイルすると... (funcall (compile nil (lambda () (let ((*special* :local-1) (*special* :local-2)) *special*)))) ;; 2つ目の束縛は無視されてるけど => :local-1 ;; グローバルの値はだいじょぶ *special* => :global