Haskell 帕塞克赢得';t解析这个表达式,我就可以';我不明白为什么
我试图为一种简单的语言编写一个解析器;基本上现在它有文本、ifs、函数应用程序和其他不多的东西 以下是我得到的代码:Haskell 帕塞克赢得';t解析这个表达式,我就可以';我不明白为什么,haskell,parsec,Haskell,Parsec,我试图为一种简单的语言编写一个解析器;基本上现在它有文本、ifs、函数应用程序和其他不多的东西 以下是我得到的代码: import Text.ParserCombinators.Parsec import Control.Monad (liftM) data Expr = Term Term | Apply Expr Expr | If Expr Expr Expr deriving (Show) data Term = Bool
import Text.ParserCombinators.Parsec
import Control.Monad (liftM)
data Expr = Term Term
| Apply Expr Expr
| If Expr Expr Expr
deriving (Show)
data Term = Bool Bool
| Num Double
| String String
| Identifier String
| Parens Expr
deriving (Show)
sstring s = spaces >> string s
schar c = spaces >> char c
keyword k = do
kw <- try (sstring k)
notFollowedBy alphaNum
return kw
pBool :: Parser Bool
pBool = do
bool <- keyword "True" <|> keyword "False"
case bool of
"True" -> return True
"False" -> return False
pDouble :: Parser Double
pDouble = do
ds <- many1 digit
dot <- optionMaybe $ char '.'
case dot of
Nothing -> return $ read ds
_ -> do
ds' <- many1 digit
return $ read (ds ++ "." ++ ds')
pString :: Parser String
pString = do
char '"'
str <- many1 $ noneOf "\""
char '"'
return str
pIdentifier :: Parser String
pIdentifier = spaces >> many1 letter
pParens :: Parser Expr
pParens = do
schar '('
expr <- pExpr
schar ')'
return expr
pTerm :: Parser Term
pTerm = try (liftM Bool pBool)
<|> try (liftM Num pDouble)
<|> try (liftM String pString)
<|> try (liftM Identifier pIdentifier)
<|> try (liftM Parens pParens)
-- TODO: make this left-associative
pApply :: Parser Expr
pApply = do
term <- pTerm'
mApp <- spaces >> optionMaybe pApply
return $ case mApp of
Just app -> Apply term app
Nothing -> term
-- pulls "parens" expressions out of terms
pTerm' :: Parser Expr
pTerm' = do
term <- pTerm
case term of
Parens expr -> return expr
otherwise -> return $ Term term
pIf :: Parser Expr
pIf = do
keyword "if"
cond <- pExpr
keyword "then"
ifTrue <- pExpr
keyword "else"
ifFalse <- pExpr
return $ If cond ifTrue ifFalse
pExpr :: Parser Expr
pExpr = try pIf <|> pApply
test parser = parse parser ""
太好了!还有许多其他事情也能起作用:
> test pExpr "1.234"
Right (Term (Num 1.234))
> test pApply "neg 1"
Right (Apply (Term (Identifier "neg")) (Term (Num 1.0)))
> test pExpr "f g 1"
Right (Apply (Term (Identifier "f")) (Apply (Term (Identifier "g")) (Term (Num 1.0))))
但是现在,如果我试图解析if
语句,我会得到一个错误:
> test pIf "if 1 then 2 else 3"
Left (line 1, column 4):
unexpected "1"
expecting space, "if", "True", "False", letter or "("
这对我来说毫无意义!让我们一步一步地看一下解析if语句的规则:
我们解析一个
“if”
关键字(没问题)。然后,对于下一个解析(1
),我们需要解析pExpr
,它本身可以是pIf
或pApply
。它不是if,所以我们尝试apply,它本身尝试pTerm'
,它尝试pTerm
,它尝试pBool
,失败,然后是pNum
,成功!然后pTerm
使用Num 1.0
成功,因此pTerm'
使用Term(Num 1.0)
成功,这意味着pExpr
使用Term(Num 1.0)
成功,并将其传递到cond
变量中。。。正确的?显然不是,因为它失败了!我不明白为什么它会在这里失败。您需要做一些更改
pExpr :: Parser Expr
pExpr = try pIf <|> pTerm'
pIf :: Parser Expr
pIf = do
keyword "if"
spaces
cond <- pExpr
keyword "then"
spaces
ifTrue <- pExpr
keyword "else"
spaces
ifFalse <- pExpr
return $ If cond ifTrue ifFalse
pExpr::Parser Expr
pExpr=尝试pIf-pTerm'
pIf::Parser Expr
pIf=do
关键词“如果”
空间
cond您需要做一些更改
pExpr :: Parser Expr
pExpr = try pIf <|> pTerm'
pIf :: Parser Expr
pIf = do
keyword "if"
spaces
cond <- pExpr
keyword "then"
spaces
ifTrue <- pExpr
keyword "else"
spaces
ifFalse <- pExpr
return $ If cond ifTrue ifFalse
pExpr::Parser Expr
pExpr=尝试pIf-pTerm'
pIf::Parser Expr
pIf=do
关键词“如果”
空间
cond您在没有吃掉所有空格时遇到问题,then
和else
被解释为标识符。lexeme
规则很方便在任何标记后使用空格。您的pIdentifier
需要明确检查它没有占用保留字。我修复了这些问题,并自由地使用了一些现有的组合器,并改为应用程序风格
import Text.ParserCombinators.Parsec
import Control.Applicative hiding ((<|>))
data Expr = Term Term
| Apply Expr Expr
| If Expr Expr Expr
deriving (Show)
data Term = Bool Bool
| Num Double
| String String
| Identifier String
| Parens Expr
deriving (Show)
keywords = ["if", "then", "else", "True", "False"]
lexeme p = p <* spaces
schar = lexeme . char
keyword k = lexeme . try $
string k <* notFollowedBy alphaNum
pBool :: Parser Bool
pBool = (True <$ keyword "True") <|> (False <$ keyword "False")
pDouble :: Parser Double
pDouble = lexeme $ do
ds <- many1 digit
option (read ds) $ do
char '.'
ds' <- many1 digit
return $ read (ds ++ "." ++ ds')
pString :: Parser String
pString = lexeme . between (char '"') (char '"') . many1 $ noneOf "\""
pIdentifier :: Parser String
pIdentifier = lexeme . try $ do
ident <- many1 letter
if ident `elem` keywords
then unexpected $ "reserved word " ++ show ident
else return ident
pParens :: Parser Expr
pParens = between (schar '(') (schar ')') pExpr
pTerm :: Parser Term
pTerm = choice [ Bool <$> pBool
, Num <$> pDouble
, String <$> pString
, Identifier <$> pIdentifier
, Parens <$> pParens
]
-- TODO: make this left-associative
pApply :: Parser Expr
pApply = do
term <- pTerm'
option term $
Apply term <$> pApply
-- pulls "parens" expressions out of terms
pTerm' :: Parser Expr
pTerm' = do
term <- pTerm
case term of
Parens expr -> return expr
_ -> return $ Term term
pIf :: Parser Expr
pIf = If <$ keyword "if" <*> pExpr
<* keyword "then" <*> pExpr
<* keyword "else" <*> pExpr
pExpr :: Parser Expr
pExpr = pIf <|> pApply
test parser = parse (spaces *> parser <* eof) ""
import Text.ParserCombinators.Parsec
导入控件。应用程序隐藏(())
数据表达式=术语
|应用Expr Expr
|如果Expr Expr Expr
派生(显示)
数据项=Bool Bool
|双数
|串
|标识符字符串
|帕伦斯表达式
派生(显示)
关键词=[“如果”、“那么”、“其他”、“真”、“假”]
lexeme p=p您在不占用所有空格方面存在问题,并且then
和else
被解释为标识符。lexeme
规则很方便在任何标记后使用空格。您的pIdentifier
需要明确检查它没有占用保留字。我修复了这些问题,并自由地使用了一些现有的组合器,并改为应用程序风格
import Text.ParserCombinators.Parsec
import Control.Applicative hiding ((<|>))
data Expr = Term Term
| Apply Expr Expr
| If Expr Expr Expr
deriving (Show)
data Term = Bool Bool
| Num Double
| String String
| Identifier String
| Parens Expr
deriving (Show)
keywords = ["if", "then", "else", "True", "False"]
lexeme p = p <* spaces
schar = lexeme . char
keyword k = lexeme . try $
string k <* notFollowedBy alphaNum
pBool :: Parser Bool
pBool = (True <$ keyword "True") <|> (False <$ keyword "False")
pDouble :: Parser Double
pDouble = lexeme $ do
ds <- many1 digit
option (read ds) $ do
char '.'
ds' <- many1 digit
return $ read (ds ++ "." ++ ds')
pString :: Parser String
pString = lexeme . between (char '"') (char '"') . many1 $ noneOf "\""
pIdentifier :: Parser String
pIdentifier = lexeme . try $ do
ident <- many1 letter
if ident `elem` keywords
then unexpected $ "reserved word " ++ show ident
else return ident
pParens :: Parser Expr
pParens = between (schar '(') (schar ')') pExpr
pTerm :: Parser Term
pTerm = choice [ Bool <$> pBool
, Num <$> pDouble
, String <$> pString
, Identifier <$> pIdentifier
, Parens <$> pParens
]
-- TODO: make this left-associative
pApply :: Parser Expr
pApply = do
term <- pTerm'
option term $
Apply term <$> pApply
-- pulls "parens" expressions out of terms
pTerm' :: Parser Expr
pTerm' = do
term <- pTerm
case term of
Parens expr -> return expr
_ -> return $ Term term
pIf :: Parser Expr
pIf = If <$ keyword "if" <*> pExpr
<* keyword "then" <*> pExpr
<* keyword "else" <*> pExpr
pExpr :: Parser Expr
pExpr = pIf <|> pApply
test parser = parse (spaces *> parser <* eof) ""
import Text.ParserCombinators.Parsec
导入控件。应用程序隐藏(())
数据表达式=术语
|应用Expr Expr
|如果Expr Expr Expr
派生(显示)
数据项=Bool Bool
|双数
|串
|标识符字符串
|帕伦斯表达式
派生(显示)
关键词=[“如果”、“那么”、“其他”、“真”、“假”]
词素p=p,正确解析基本if
语句;但是,从pExpr
中删除pApply
选项是不正确的。毕竟,函数应用程序是一个完全有效的表达式。我们需要能够说,例如,如果是偶数,那么是“偶数”或者是“奇数”
。不过,在添加空格方面做得不错。使用Parsec最大的麻烦之一是:p正确解析基本if
语句;但是,从pExpr
中删除pApply
选项是不正确的。毕竟,函数应用程序是一个完全有效的表达式。我们需要能够说,例如,如果是偶数,那么是“偶数”或者是“奇数”
。不过,在添加空格方面做得不错。使用Parsec最大的麻烦之一是:PSuperb!这真是太糟糕了。我不熟悉这种应用型的风格;我得多看看。太棒了!这真是太糟糕了。我不熟悉这种应用型的风格;我得多调查一下。