Haskell 帕塞克赢得';t解析这个表达式,我就可以';我不明白为什么

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

我试图为一种简单的语言编写一个解析器;基本上现在它有文本、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 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!这真是太糟糕了。我不熟悉这种应用型的风格;我得多看看。太棒了!这真是太糟糕了。我不熟悉这种应用型的风格;我得多调查一下。