Parsing 用Haskell解析一个简单的解释器

Parsing 用Haskell解析一个简单的解释器,parsing,haskell,Parsing,Haskell,我对Haskell比较陌生,主要编程背景来自OO语言。我试图为一种简单的编程语言编写一个带解析器的解释器。到目前为止,我的解释器处于一种我相当满意的状态,但在解析器方面有点困难 这是我遇到问题的一段代码 data IntExp = IVar Var | ICon Int | Add IntExp IntExp deriving (Read, Show) whitespace = many1 (char ' ') parseICon :: Parser IntExp parseICon

我对Haskell比较陌生,主要编程背景来自OO语言。我试图为一种简单的编程语言编写一个带解析器的解释器。到目前为止,我的解释器处于一种我相当满意的状态,但在解析器方面有点困难

这是我遇到问题的一段代码

data IntExp
 = IVar Var
 | ICon Int
 | Add IntExp IntExp
 deriving (Read, Show)

whitespace = many1 (char ' ')

parseICon :: Parser IntExp
parseICon =
  do x <- many (digit)
     return (ICon (read x :: Int))

parseIVar :: Parser IntExp
parseIVar = 
  do x <- many (letter)
     prime <- string "'" <|> string ""
     return (IVar (x ++ prime))

parseIntExp :: Parser IntExp
parseIntExp =
  do x <- try(parseICon)<|>try(parseIVar)<|>parseAdd
     return x

parseAdd :: Parser IntExp
parseAdd =
  do x <- parseIntExp   
     whitespace
     string "+"
     whitespace
     y <- parseIntExp
     return (Add x y)

runP :: Show a => Parser a -> String -> IO ()
runP p input
  = case parse p "" input of
      Left err ->
        do putStr "parse error at "
           print err
      Right x -> print x
dataintexp
=IVar变量
|图标Int
|添加IntExp IntExp
派生(读取、显示)
空格=many1(字符“”)
parseICon::Parser IntExp
帕西肯=

x您的问题实际上在
parseICon

parseICon =
  do x <- many (digit)
     return (ICon (read x :: Int))
当您所做的只是对解析器的结果应用常规函数时,您可以使用
fmap

parseICon :: Parser IntExp
parseICon = fmap (ICon . read) (many digit)
它们是完全一样的东西。如果导入
Control.Applicative
模块,您可以使事情看起来更美好,该模块为您提供了名为
()
fmap的运算符版本,以及另一个运算符
()
,允许您对多个参数的函数执行相同的操作。还有一些操作符
()
分别丢弃右值或左值,在本例中,它们允许您在丢弃结果(例如空格等)的同时解析某些内容

下面是一个稍加修改的代码版本,应用了上面的一些建议和其他一些小的风格调整:

whitespace = many1 $ char ' '

parseICon :: Parser IntExp
parseICon = ICon . read <$> many1 digit

parseIVar :: Parser IntExp
parseIVar = IVar <$> parseVarName

parseVarName :: Parser String
parseVarName = (++) <$> many1 letter <*> parsePrime

parsePrime :: Parser String
parsePrime = option "" $ string "'"

parseIntExp :: Parser IntExp
parseIntExp = parseICon <|> parseIVar <|> parseAdd

parsePlusWithSpaces :: Parser ()
parsePlusWithSpaces = whitespace *> string "+" *> whitespace *> pure ()

parseAdd :: Parser IntExp
parseAdd = Add <$> parseIntExp <* parsePlusWithSpaces <*> parseIntExp
whitespace=many1$char''
parseICon::Parser IntExp
parseICon=图标。读取多个1位数
parseIVar::Parser IntExp
parseIVar=IVar parseVarName
parseVarName::解析器字符串
parseVarName=(++)多个1字母的parsePrime
parsePrime::解析器字符串
parsePrime=选项“$string”
parseIntExp::Parser IntExp
parseIntExp=parseICon parseIVar parseAdd
parsePlusWithSpaces::解析器()
parsePlusWithSpaces=whitespace*>string“+”*>whitespace*>pure()
parseAdd::Parser IntExp

parseAdd=Add parseIntExp我也是Haskell的新手,只是想知道:

parseIntExp是否会进入parseAdd

似乎图标或IVar在到达“parseAdd”之前总是会被解析

e、 g.运行“3+m”

会尝试解析图标,并成功,给予

(图标3)代替(添加(图标3)(IVar m))


抱歉,如果我在这里太笨了,我只是不确定。

camccann的回答很好。一些进一步的提示。。。“词法分析”和空格处理通常使用Parsec.Token和Parsec.Language模块完成。这些lexer的风格相当地道——如果您从中获得Parsec源代码,那么有一些简单的示例,比如一个针对Henk的示例,您可以从中复制代码。令牌模块还为您提供了更好的数字解析器,因此您可以避免使用多个数字进行读取。此外,Parsec获取()&()符号的应用程序实例仅适用于3.0及更高版本。非常感谢您的回答和建议。看起来它会解决我的问题,我应该能够改进我的编码风格。干杯在
parseICon
示例中,我更喜欢
图标。是的,你是对的。我可能也应该在我的回答中提到这一点。。。对于这个简单的例子,最简单的方法可能是使用类似于
sebby
combinator的东西。另外,欢迎使用Haskell和Stack Overflow!
whitespace = many1 $ char ' '

parseICon :: Parser IntExp
parseICon = ICon . read <$> many1 digit

parseIVar :: Parser IntExp
parseIVar = IVar <$> parseVarName

parseVarName :: Parser String
parseVarName = (++) <$> many1 letter <*> parsePrime

parsePrime :: Parser String
parsePrime = option "" $ string "'"

parseIntExp :: Parser IntExp
parseIntExp = parseICon <|> parseIVar <|> parseAdd

parsePlusWithSpaces :: Parser ()
parsePlusWithSpaces = whitespace *> string "+" *> whitespace *> pure ()

parseAdd :: Parser IntExp
parseAdd = Add <$> parseIntExp <* parsePlusWithSpaces <*> parseIntExp