非破壊Hash

ひさしぶりにParsecをいじってみた.

前回,(http://d.hatena.ne.jp/hoge1e3/20080129)

var a; a=3; var b; b=a+3;

というプログラムがあったときに,a や b にアドレスを割り付けたい,と言ったので,この部分の実装をやろうとした,が失敗.

 var a;  

という変数宣言を読ませる部分.ここで,
「a は アドレス 0 に割り当てる」
という処理をしたい.
その後,var b; がきたら
「b は アドレス 1 に割り当てる」
という処理をしたい.

Parsec には getState / setState があるから,状態にアドレスの連番とハッシュテーブル( 変数名 - アドレス ) をもたせたいと思って,次のように状態を定義し,

data ParserState = PStat Int (HashTable String Int) 
 -- HashTable は 標準のData.HashTable

変数宣言を解析する部分を次のようにした.

vardecl :: Parser Decl
vardecl = do { tok "var" 
             ; name <- symbol; tok ";"
             ; st <- getState  
             ; h <- (getSyms st) 
             ; update h name (getNum st)
             ; setState (PStat ((getNum st)+1) h )
             ; return $ VarDecl name (getNum st)
             } 

が,

Couldn't match expected type `GenParser Char ParserState t'
	   against inferred type `HashTable String Int'

これはdo文はdo文でもパーサのdo文だから,ここで IO を挟んだりはできない,ということらしい.

getState, setStateと馴染むためには,おそらくData.HashTableのようなIOベースのハッシュテーブルじゃなくて,非破壊的なハッシュテーブル,つまり

update :: Hash k v -> k -> v -> Hash k v

のように,
「もとのHash の内容にそっくりだけど,キーk に 値v を設定している点だけがちがう新しい Hash」
を返す,というメソッドがあればいいのに.

多分メモリを食うし速度も遅いからないのだろうか.