Haskell的Brainfuck翻译

时间:2017-05-18 11:59:11

标签: haskell interpreter brainfuck

我正在codewar做一个与Haskell相关的问题,就是为着名的深奥语言Brainfuck写一个翻译。

最初,我正在考虑使用Array编写程序。在我开始实现解释器之后,我意识到解释器的效率会有多低,因为数组有很多变化。然后我切换到使用STArray。但是除了保存数组指针数组之外,我还需要输出String的可变引用,这在STArray中是不可能的。所以我完全惊呆了。

写一个monadic解析器可能是个好主意,我当时想。但事实证明,我不知道应该用什么样的表达来模拟问题。我只阅读了一些关于monadic解析风格的论文,这些都是。 Brainfuck比天真AddMinus等更复杂。

感谢任何解决问题的指导。以下是我的代码。我在这里发布它只是为了表明代码是多么混乱。不要尝试编译它,因为它充满了类型错误。

executeString' :: String -> String -> Maybe String
executeString' []     _     = Just ""
executeString' ","    _     = Nothing
-- executeString' source input = Just $ map chr $ elems $ consume 

length' ::String ->  Int 
length' source = right - left 
    where step (l, r) '>' = (l,   r+1)
          step (l, r) '<' = (l+1, r)
          (left, right) = foldl' step (0, 0) source

-- decrement the data pointer 
neverMinus :: Int -> Int 
neverMinus n = if n == 0 then 255 else n - 1 

-- increment the data pointer 
alwaysPlus :: Int -> Int 
alwaysPlus n = if n == 255 then 0 else n + 1 

consume :: String -> String -> (Array Int Int, Array Int Char) 
consume source input = runSTArray $ do 
       pointer <- newArray (0, arrlength') 0  
       forM_ source $ \t -> do 
           pointed <- readArray pointer point 
           elem <- readArray pointer pointed
           isWrong <- readArray pointer error 
           status' <- readArray pointer status 
           when (1 == isWrong) $ return 0 
           when (doJump status') $ return 0
           case t of 
               '>' -> writeArray pointer point (pointed + 1) 
               '<' -> writeArray pointer point (pointed - 1) 
               '+' -> writeArray pointer pointed (alwaysPlus elem) 
               '-' -> writeArray pointer pointed (neverMinus elem) 
               ',' -> do  index <- readArray pointer inputIndex 
                          writeArray pointer pointed (ord $
                                head . drop index input)
                          writeArray pointer inputIndex (index+1)
                          1 
               '[' -> writeArray pointer status jump 
               ']' -> writeArray pointer status execute 
       return pointer
    where arrlength'  = length'' + 4 
          length'' = length' source 
          strlength' = 1 +  foldl' (\count s -> case s of 
                                                 '.' -> count + 1) 0 source 
          point      = length'' + 1
          inputIndex = length'' + 2
          status     = length'' + 3 -- should the program execute current instruction or jump
          error      = length'' + 4 -- if there is program error during execution

-- Program status 
jump    = 1
execute = 0

doJump :: Int -> Bool
doJump jump    = True
duJump execute = False

1 个答案:

答案 0 :(得分:0)

使用像haskell这样的语言编写brainfuck解释器的一些技巧。

最初,我在考虑使用Array编写程序。在我开始实现解释器之后,我意识到解释器的效率会有多低,因为数组有很多变化。然后我切换到使用STArray。但是除了保存数组指针数组之外,我还需要输出String的可变引用,这在STArray中是不可能的。所以我完全惊呆了。

关于List Zippers的研究。这是一个可以定义的数据结构 - &gt; [枢轴元素的左侧] {透视元素} [枢轴元素的右侧],在代码中看起来像。

`data Tape a = Tape [a] a [a]`

您可以轻松地在此数据类型上定义><+-等。

编写一个monadic解析器可能是一个好主意,我当时想。但事实证明我不知道我应该用什么样的表达来模拟问题。我只阅读了一些关于monadic解析风格的论文,这就是全部。 Brainfuck比天真的Add和Minus等复杂得多

事实并非如此,Brainfuck比典型的算术解析器复杂得多。正如其中一条评论所指出的那样。如下所示,应该让你走上正轨。

stripComments = filter (`elem` "+-<>[],.")

```

token :: Parser Token
token = const TRight <$> char '>'
  <|> const TLeft  <$> char '<'
  <|> const TInc   <$> char '+'
  <|> const TDec   <$> char '-'
  <|> const TPrint <$> char ','
  <|> const TRead  <$> char '.'
  <|> const TLoopS <$> char '['
  <|> const TLoopE <$> char ']'

```

最后你需要一个评估策略,我会使用类似下面的内容。 eval :: String -> Tape Token -> Tape Int -> String 其中第一个参数是程序的输入,Tape Token将是解析的程序,Tape Int将是磁带上的操纵值,您将在其上应用值,最后一个参数将是输出。

我相信这可以帮助你走上正轨。

我曾经写过类似的https://gist.github.com/sherubthakur/16a784e61efbe54d885ad60c6e18f254