Использование вами аккумулятора делает вывод неоправданно строгим. Аккумуляторы хороши на строгих языках, потому что они допускают хвостовую рекурсию; в ленивых языках они не нужны (и часто плохие). Я переписал ваш код ниже, чтобы не использовать его.
module Interpreter where
--------------------------------------------------------------------
type Id = [Char]
type Output = [Char]
type Value = Int
type Table = [(Id, Value)]
data Stm = CompoundStm Stm Stm |
AssignStm Id Exp |
PrintStm ExpList deriving Show
data Exp = IdExp Id |
NumExp Value |
OpExp Exp Op Exp |
EseqExp Stm Exp deriving Show
data ExpList = PairExpList Exp ExpList |
LastExpList Exp deriving Show
data Op = Plus | Minus | Times | Div deriving Show
example :: Stm
example = CompoundStm (AssignStm "a" (OpExp (NumExp 5) Plus (NumExp 3)))
(CompoundStm (AssignStm "b" (EseqExp (PrintStm (PairExpList (IdExp "a")
(LastExpList (OpExp (IdExp "a") Minus (NumExp 1))))) (OpExp (NumExp 10) Times
(IdExp "a")))) (PrintStm (LastExpList (IdExp "b"))))
--------------------------------------------------------------------
tableUpdate :: Table -> Id -> Value -> Table
tableUpdate t i v = (i,v):t
tableLookup :: Table -> Id -> Value
tableLookup ((x,v):t) i | x == i = v
tableLookup ((x,v):t) i | x /= i = tableLookup t i
--------------------------------------------------------------------
execute :: Stm -> IO()
execute s = putStr ((\(o,_)->o) (interpStm s []))
interpStm :: Stm -> Table -> (Output, Table)
interpStm (CompoundStm l r) t = let (o, t') = interpStm l t in
let (o', t'') = interpStm r t' in
(o ++ o', t'')
interpStm (PrintStm el) t = interpExpList el t
interpStm (AssignStm i e) t = let (v, o, t') = interpExp e t in
(o, tableUpdate t' i v)
interpExp :: Exp -> Table -> (Value, Output, Table)
interpExp (IdExp i) t = (tableLookup t i, "", t)
interpExp (NumExp v) t = (v, "", t)
interpExp (EseqExp s e) t = let (o, t') = interpStm s t in
let (v, o', t'') = interpExp e t' in
(v, o ++ o', t'')
interpExp (OpExp l op r) t = let (v, o, t') = interpExp l t in
let (v', o', t'') = interpExp r t' in
(g v op v', o++o', t'')
where
g v1 Plus v2 = v1 + v2
g v1 Minus v2 = v1 - v2
g v1 Times v2 = v1 * v2
g v1 Div v2 = v1 `div` v2
interpExpList :: ExpList -> Table -> (Output, Table)
interpExpList (LastExpList e) t = let (v, o, t') = interpExp e t in
(o ++ show v ++ "\n", t')
interpExpList (PairExpList e el) t = let (v, o, t') = interpExp e t in
let (o', t'') = interpExpList el t' in
(o ++ show v ++ " " ++ o', t')
С этим изменением, выход идет должным образом лениво.
Вы заметите, что есть лот повторного кода вида let (value, newTable) = f oldTable in ...
и лот повторного кода формы let (output, value) = exp; (moreOutput, value) = exp2 in (output ++ moreOutput, exp3)
. Есть пара монад, которые пишут этот код для вас! Вот пример использования StateT Table (Writer Output):
module Interpreter where
import Control.Monad.Writer
import Control.Monad.State
import Data.Maybe
--------------------------------------------------------------------
type Id = [Char]
type Output = [Char]
type Value = Int
type Table = [(Id, Value)]
data Stm = CompoundStm Stm Stm |
AssignStm Id Exp |
PrintStm ExpList deriving Show
data Exp = IdExp Id |
NumExp Value |
OpExp Exp Op Exp |
EseqExp Stm Exp deriving Show
data ExpList = PairExpList Exp ExpList |
LastExpList Exp deriving Show
data Op = Plus | Minus | Times | Div deriving Show
type InterpreterM = StateT Table (Writer Output)
example :: Stm
example = CompoundStm (AssignStm "a" (OpExp (NumExp 5) Plus (NumExp 3)))
(CompoundStm (AssignStm "b" (EseqExp (PrintStm (PairExpList (IdExp "a")
(LastExpList (OpExp (IdExp "a") Minus (NumExp 1))))) (OpExp (NumExp 10) Times
(IdExp "a")))) (PrintStm (LastExpList (IdExp "b"))))
--------------------------------------------------------------------
tableUpdate :: Id -> Value -> InterpreterM ()
tableUpdate i v = modify ((i,v):)
tableLookup :: Id -> InterpreterM Value
tableLookup i = gets (fromJust . lookup i)
--------------------------------------------------------------------
execute :: Stm -> IO ()
execute s = putStr . execWriter $ evalStateT (interpStm s) []
interpStm :: Stm -> InterpreterM ()
interpStm (CompoundStm l r) = interpStm l >> interpStm r
interpStm (PrintStm el) = interpExpList el
interpStm (AssignStm i e) = interpExp e >>= tableUpdate i
interpExp :: Exp -> InterpreterM Value
interpExp (IdExp i) = tableLookup i
interpExp (NumExp v) = return v
interpExp (EseqExp s e) = interpStm s >> interpExp e
interpExp (OpExp l op r) = liftM2 (g op) (interpExp l) (interpExp r)
where
g Plus v1 v2 = v1 + v2
g Minus v1 v2 = v1 - v2
g Times v1 v2 = v1 * v2
g Div v1 v2 = v1 `div` v2
interpExpList :: ExpList -> InterpreterM ()
interpExpList (LastExpList e) = interpExp e >>= \v -> tell (show v ++ "\n")
interpExpList (PairExpList e el) = interpExp e >>= \v -> tell (show v ++ " ") >> interpExpList el
Существует множество других изменений, которые могут быть предложены здесь, но, надеюсь, вы согласитесь, что эта окончательная форма намного, намного приятнее для чтения, чем предыдущая.