如何检查Haskell中的数据类型?

时间:2012-07-13 02:52:56

标签: haskell types interpreter

我正在尝试在Haskell中编写一个brainfuck解释器作为一个练习/有趣的项目,我遇到了一个小问题。

Brainfuck的“while循环”结构只是括在括号内的一系列命令。我正在尝试以一种将运算符存储在[数据构造函数内部的循环中的方式构建语法树。

这就是命令和语法“tree”的数据声明当前的样子:

data Operator = Plus
                | Minus
                | RShift 
                | LShift
                | Dot
                | Comma
                | SBracket [Operator]
                | EBracket
                deriving (Show, Eq)


type STree = [Operator] 

我要做的是接受String之类的"+><[.>]"命令并将其解析为STree,如下所示:

[Plus, RShift, LShift, SBracket [Dot, RShift], EBracket]

到目前为止,我只能从String中获取一维列表,因为我不确定如何检查列表的头部是否为SBracket为了将新的运算符放在它的运算符列表中,而不是在主列表的头部。

这是我用来进行解析的函数:

matchChar :: Char -> Maybe Operator
matchChar c = case c of 
                '+' -> Just Plus
                '-' -> Just Minus
                '>' -> Just RShift  
                '<' -> Just LShift
                '.' -> Just Dot
                ',' -> Just Comma
                '[' -> Just (SBracket [])
                ']' -> Just EBracket
                _   -> Nothing

getChars :: [Char] -> STree
getChars str = foldr toOp [] str
    where 
            toOp x acc = case matchChar x of
                            Just a -> a:acc
                            Nothing -> acc

我希望能够做的是检查head acc是否为SBracket实例,如果是,则不要将新的Operator添加到列表中,而是将其添加到列表中到SBracket的{​​{1}}列表。

我尝试过模式匹配(Operator)以及尝试显式检查列表的头部(toOp x ((SBracket list):xs) = ...),但这些都不能正常工作

任何帮助都会很棒!

1 个答案:

答案 0 :(得分:7)

首先,我将SBracket [Operator]重新定义为Bracket STree,然后摆脱EBracket。然后我会更改你的解析器以跟踪“当前STree”以及父母列表。每次遇到括号时,都会将当前树推送到父列表并创建新树。当你遇到一个结束括号时,你拿走你当前的树,用Bracket构造函数包裹它,弹出你的第一个父项,将括号添加到它的末尾并将它作为你当前的树。


这是一个完全未经测试的(在此comp上没有ghc)版本,可能有效也可能无效:

data Operator = Plus
                | Minus
                | RShift
                | LShift
                | Dot
                | Comma
                | Bracket [Operator]
                deriving (Show, Eq)

parse :: [Char] -> Either String [Operator]
parse str = parse' str [] []
  where parse' :: [Char] -> [Operator] -> [[Operator]] -> Either String [Operator]
        parse' [] context [] = Right (reverse context)
        parse' [] context _ = Left "unclosed []"
        parse' (']':cs) _ [] = Left "unexpected ]"
        parse' (c:cs) ctx stack
            | c == '+' = parse' cs (Plus:ctx) stack
            | c == '-' = parse' cs (Minus:ctx) stack
            | c == '>' = parse' cs (RShift:ctx) stack
            | c == '<' = parse' cs (LShift:ctx) stack
            | c == '.' = parse' cs (Dot:ctx) stack
            | c == ',' = parse' cs (Comma:ctx) stack
            | c == '[' = parse' cs [] (ctx:stack)
            | c == ']' = parse' cs (Bracket (reverse ctx):s) tack
            | otherwise = parse' cs ctx stack
          where (s:tack) = stack