Parsing 用Haskell解析一个简单的解释器
我对Haskell比较陌生,主要编程背景来自OO语言。我试图为一种简单的编程语言编写一个带解析器的解释器。到目前为止,我的解释器处于一种我相当满意的状态,但在解析器方面有点困难 这是我遇到问题的一段代码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
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