您要做的是在评估表达式期间而不是在解析期间跟踪变量的值。假设您将表达式解析为以下类型:
data Expr = Literal Int | Variable Var | Assign Var Expr | Add Expr Expr | ...
newtype Var = Var String deriving (Ord, Eq, Show)
然后,您可以简单地使用所有变量的当前值在您的评估函数周围传递一个 Map:
import qualified Data.Map as M
import Control.Monad.State
data Expr = Literal Int | Variable Var | Assign Var Expr | Add Expr Expr
newtype Var = Var String deriving (Ord, Eq, Show)
-- Each Expr corresponds to a single line in your language, so
-- a = 2+1
-- a + 2
-- corresponds to
-- [Assign (Var "a") (Add (Literal 2) (Literal 1)),
-- Add (Variable (Var "a")) (Literal 2)]
eval :: [Expr] -> Int
eval es = last $ evalState (mapM eval' es) M.empty -- M.empty :: M.Map Var Int
where
eval' (Literal n) = return n
eval' (Variable v) = do
vs <- get
case M.lookup v vs of
Just x -> return x
_ -> error $ "variable " ++ show v ++ " is undefined!"
eval' (Assign v ex) = do
x <- eval' ex
modify (M.insert v x)
return x
eval' (Add a b) = do
x <- eval' a
y <- eval' b
return (x+y)
当然,没有什么可以阻止您在解析表达式时对表达式求值,从而无需像这样的抽象语法树。那里的总体思路是一样的。在整个解析过程中,您需要保持一些状态,以跟踪所有变量的当前值。