Я сейчас работаю над одним из проектов практики.
Это динамически типизированный язык, поэтому переменные не обязательно должны быть объявлены, но каждое значение имеет связанный тип. Я Реализована с использованием алгебраического типа данных в Haskell:
data Value = BoolValue Bool --^A Boolean value.
| NumberValue Double --^A numeric value.
| StringValue String --^A string value.
-- (several others omitted for simplicity)
Для выполнения программ, я использую монады трансформаторов StateT
и ErrorT
на вершине IO
:
-- | A monad representing a step in an RPL program.
--
-- This type is an instance of 'MonadState', so each action is a function that
-- takes an 'RPLContext' as input and produces a (potentially different)
-- 'RPLContext' as its result. It is also an instance of 'MonadError', so an
-- action may fail (with 'throwRPLError'). And it is built on the 'IO' monad,
-- so 'RPL' computations can interact with the outside world.
type RPL = StateT RPLContext (ErrorT RPLError IO)
-- | Executes an 'RPL' computation.
-- The monadic result value (of type @[email protected]) is discarded, leaving only the final
-- 'RPLContext'.
runRPL :: RPL a --^The computation to run
-> RPLContext --^The computation's initial context
-> IO (Either RPLError RPLContext)
--^An 'IO' action that performs the operation, producing either
-- a modified context if it succeeds, or an error if it fails.
runRPL a = runErrorT . (execStateT a)
«контекст» является комбинация стека данных (это язык на основе стека) и «среда», которая содержит все переменные, которые в настоящее время находятся в области:
-- | The monadic state held by an 'RPL' computation.
data RPLContext = RPLContext {
contextStack :: Stack, --^The context's data stack.
contextEnv :: Env --^The context's environment.
}
(Обратите внимание, что Stack
это просто псевдоним для [Value]
.)
В верхней части этого фундамента, у меня есть целый ряд вспомогательных функций, чтобы сделать вещи, как манипулировать стека в текущем контексте (удерживаемый в StateT
части из RPL
Монада). Например, вот функции, участвующие в проталкивая значение в стек:
-- | Pushes a value onto the stack.
pushValue :: Value -> RPL()
pushValue x = modifyStack (x:)
-- | Transforms the current stack by a function.
modifyStack :: (Stack -> Stack) -> RPL()
modifyStack f = do
stack <- getStack
putStack $ f stack
-- | Returns the entire current stack.
getStack :: RPL Stack
getStack = fmap contextStack get
-- | Replaces the entire current stack with a new one.
putStack :: Stack -> RPL()
putStack stack = do
context <- get
put $ context { contextStack = stack }
getStack
, putStack
и modifyStack
моделируются после MonadState
«s get
, put
и modify
функций, но они работают только на одном поле от записи RPLContext
.
Все встроенные команды языка - это всего лишь действия в монаде RPL
, которые строятся поверх таких инструментов, как pushValue
.
Для анализа кода на моем языке я использую Parsec. Это довольно хорошо.
На отдельной дорожке, не связанный с моим переводчиком RPL, вы можете найти «Write Yourself a Scheme in 48 Hours» полезный.
ли это домашнее задание? – augustss
Это, по сути, то, что такое 'IO monad'. – PyRulez