2013-10-23 31 views
0

我正在嘗試編寫簡單語言的解析器;基本上現在它有文字,ifs,功能應用程序,而不是其他。Parsec不會解析這個表達式,我不知道爲什麼

下面是我得到了代碼:

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 "" 

現在,如果我嘗試在ghci中解析一個單一的數字表達,一切都很好:

> test pExpr "1" 
Right (Term (Num 1.0)) 

太好了!和許多其他的事情工作太:

> 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,它本身可以是pIfpApply。那麼這不是一個如果,所以我們嘗試應用,其本身嘗試pTerm',其中嘗試pTerm,其中嘗試pBool,失敗,然後pNum,成功!然後pTerm成功與Num 1.0,所以pTerm'成功與Term (Num 1.0),這意味着pExpr成功與Term (Num 1.0),並且得到傳遞到cond變量......對吧?好吧,顯然不是,因爲它失敗了!我不明白爲什麼它會在這裏失敗。

回答

3

你有問題沒吃完所有的空格和thenelse被解釋爲標識符。 A 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) "" 
+0

棒極了!這真是個壞蛋。我對這種應用風格很陌生;我得看看更多。 –

1

你需要做一些改變。

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 
+0

正確解析基本'if'語句;然而,從pExpr中刪除'pApply'選項並不好。畢竟功能應用是一個完美有效的表達。例如,我們需要能夠說出「甚至是一個」,甚至「其他」奇怪的「。 好的呼籲增加空格,但。使用Parsec最大的煩惱之一:P –

相關問題