top page > computer > haskell > web_lecture > for_programmer > nsc_evalsym.html
更新日:
文責: 重城良国

Nano Scheme: 変数の評価

(工事中 30%)

はじめに

「評価」のしくみをつくる。初期環境に適当な変数を定義しそれを「評価」する。

シンボル

値構築子Sym

Environment.hs

nscでは変数は特別なシンボルという値によって表される。

data Value
| Sym Symbol
= Int Integer

showValue (Sym s) = s
showValue (Int i) = show i

トークン

Parse.hs

シンボルを示すトークンを追加する。

data Token
= TkSym Symbol
| TkInt Integer
deriving Show

字句解析

シンボルに使える文字

シンボルに使える文字であることを確認する関数isSymを定義する。

isSym :: Char -> Bool
isSym c = any ($ c) [isAlphaNum, (`elem` "+-*/<=>?")]

関数anyは条件を表す関数とリストをとってその要素のうち条件を満たすものがあることを確認する関数だ。

% ghci
Prelude> any even [1, 3, 4, 5]
True
Prelude> any even [1, 3, 5, 7]
False

($ c)のように$に対して部分適用すると関数と引数の関係を逆転できる。any ($ c) [...]は条件のリストのうちcがそれを満たすものがあることを確認する。

字句解析器の拡張

字句解析器をシンボルにも使えるように拡張する。

tokens (c : s)
| isDigit c = ...
| isSym c = let (t, r) = span isSym s in
(TkSym (c : t) :) `mapply` tokens r
| isSpace c = ...
tokens "" = Just []
tokens _ = Nothing

数値トークンの読みこみとほぼ同じことをしている。与えられた文字列をc : tとrに分解して前者からはシンボル値を作り、後者は再帰的にトークン列に分解し、それらを合わせて結果としている。

構文解析

シンボルトークンをnscが扱うシンボル値にする。

parse1 (TkSym s : ts) = Just (Sym s, ts)

を追加する。

ここまでを試す

シンボルが読みこめる。読みこんだシンボルを「そのまま」表示する。

% echo "nsc hoge" | runghc -Wall nsc.hs
hoge

評価

テスト用変数

Primitive.hs

テスト用の変数を初期環境に定義する。

env0 = fromList [
("hoge", Int 12345)
]

変数値の参照

Environment.hs

変数の値を環境からとりだす関数referを定義する。

refer :: Symbol -> Env -> Maybe Value
refer = M.lookup

関数referはData.Mapモジュールの関数lookupがそのまま使える。関数referを公開する。

module Environment (Env, M.fromList, refer, Value(..), showValue, Symbol) where

関数eval

Eval.hs

モジュールEvalを作り評価関数を作る。

{-# LANGUAGE TupleSections #-}

module Eval (eval) where

import Environment
import Maybe

eval :: Value -> Env -> Maybe (Value, Env)
eval (Sym s) e = (, e) `mapply` refer s e
eval i@(Int _) e = Just (i, e)

refer s eで環境からシンボルに対応する値をとりだす。(, e) `mapply`で「そのまま」の環境を追加する。

Prelude> :load Eval.hs
*Eval> (showValue . fst) `mapply` eval (Sym "hoge") (fromList [("hoge", Int 12345)])
Just "12345"

fromListで変数hogeが12345に関連づけられている環境を作り、その環境でシンボルhogeを評価している。関数fstで結果から値のみをとりだしshowValueで文字列化した。

関数evaluate

Value型のリストの各要素を次々に評価していく関数evaluateを作る。

evaluate :: [Value] -> Env -> Maybe ([Value], Env)
evaluate [] e = Just ([], e)
evaluate (v : vs) e = case eval v e of
Just (v', e') -> (\(vs', e'') -> (v' : vs', e''))
`mapply` evaluate vs e'
_ -> Nothing

eval v eでリストの先頭の値を評価する。エラーでなければevaluate vs e'で新しい環境で残りの値を評価する。その結果を(vs', e'')で受けvs'にv'を追加する。

module Eval (evaluate, eval) where

として公開する名前のリストにevaluateを追加する。

*Eval> (map showValue . fst) `mapply` evaluate [Int 123, Sym "hoge"] (fromList [("hoge", Int 12345)])
Just ["123","12345"]

整数値123とシンボルhogeの2つを要素とする値のリストを評価した。

モジュールMain

nsc.hs

まずはモジュールEvalを導入する。

import Eval

関数schemeにevaluateの段階を追加する。

scheme src e = (`evaluate` e) `mbind` (parse `mbind` tokens src)

関数evaluateはMaybe値を返す関数なのでmapplyではなくmbindを使っている。

変数の評価を試す

コマンドラインで

% echo 'nsc hoge' | runhaskell -Wall nsc.hs
12345

Slackで

Hubotを再起動する。

nsc hoge

とするとHubotが

12345

と返事する。

まとめ

整数値に続いてシンボル値を追加した。シンボルは環境を参照して対応する値に評価される。「評価」の枠組みを作成した。

「Nano Scheme: 整数値の表示」へもどる 「Nano Scheme: リスト」へ

正当なCSSです! HTML5 Powered with CSS3 / styling, and Semantics