繁体   English   中英

在Haskell中将字符串解析为数据类型

[英]Parse String to Datatype in Haskell

我正在学校上Haskell课程,并且必须在Haskell中定义逻辑命题数据类型。 到目前为止,一切工作正常(定义和功能),我已将其声明为Ord,Eq和show的实例。 当我需要定义一个与用户交互的程序时,就会出现问题:我必须将来自用户的输入解析为我的数据类型:

type Var = String
data FProp = V Var
           | No FProp
           | Y FProp FProp
           | O FProp FProp
           | Si FProp FProp
           | Sii FProp FProp

其中公式:qq ^ p为:(Y(否(V“ q”))(V“ p”))

我一直在研究,发现可以将我的数据类型声明为Read的实例。

这是明智的吗? 如果是,我可以得到一些帮助来定义解析方法吗?

REPL解释器的读取部分通常看起来像这样

repl :: ForthState -> IO () -- parser definition
repl state
    = do putStr "> " -- puts a > character to indicate it's waiting for input
         input <- getLine -- this is what you're looking for, to read a line. 
         if input == "quit" -- allows user to quit the interpreter 
            then do putStrLn "Bye!"
                    return ()
            else let (is, cs, d, output) = eval (words input) state -- your grammar definition is somewhere down the chain when eval is called on input
                 in  do mapM_ putStrLn output
                        repl (is, cs, d, [])

main = do putStrLn "Welcome to your very own interpreter!"
          repl initialForthState -- runs the parser, starting with read

您的eval方法将具有各种循环,堆栈操作,条件等,以实际找出用户输入的内容。 希望这至少对阅读输入部分有帮助。

由于这是一个家庭作业问题,因此不是一个完整的答案,但是这里有一些提示。

另一个答案建议使用getLine然后在words分割。 听起来您似乎更想要传统的令牌生成器,它可以让您编写如下内容:

(Y
   (No (V q))
   (V p))

这是一个将字符串转换为令牌的实现,令牌可以是一串字母数字字符或一个非字母数字可打印字符。 您需要扩展它以支持带引号的字符串:

import Data.Char

type Token = String

tokenize :: String -> [Token]
{- Here, a token is either a string of alphanumeric characters, or else one
 - non-spacing printable character, such as "(" or ")".
 -}
tokenize [] = []
tokenize (x:xs) | isSpace x = tokenize xs
                | not (isPrint x) = error $
  "Invalid character " ++ show x ++ " in input."
                | not (isAlphaNum x) = [x]:(tokenize xs)
                | otherwise = let (token, rest) = span isAlphaNum (x:xs)
                              in token:(tokenize rest)

它将示例转换为["(","Y","(","No","(","V","q",")",")","(","V","p",")",")"] 请注意,您可以访问Unicode的全部曲目。

以交互方式对此进行评估的main功能可能类似于:

main = interact ( unlines . map show . map evaluate . parse . tokenize )

其中parse将令牌列表转换为AST列表,而evaluate将AST转换为可打印的表达式。

至于实现解析器,您的语言似乎具有与LISP类似的语法,LISP是最简单的语言之一。 您甚至不需要优先级规则。 递归下降解析器可以做到这一点,并且可能是最容易手动实现的。 您可以在parse ("(":xs) =模式匹配,但是模式匹配语法也可以非常轻松地实现超前查找,例如parse ("(":x1:xs) =可以预见一个标记。

如果递归地调用解析器,则将定义一个仅使用单个表达式并且具有类型签名的帮助器函数,如:: [Token] -> (AST, [Token]) 这使您可以解析内部表达式,检查下一个标记是")" ,然后继续进行解析。 但是,在外部,您将需要使用所有令牌并返回AST或它们的列表。

编写解析器的一种时尚方式是使用Monadic解析器组合器。 (也许有人会举一个例子。)工业强度的解决方案将是像Parsec这样的库,但这在这里可能是过大了。 解析仍然是(主要是!)一个已解决的问题,如果您只想按时完成任务,那么使用现成的库是个好主意。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM