2011-11-21 72 views
23

我試圖爲一個簡單的功能語言,有點像Caml的解析器,但我似乎堅持最簡單的事情。parsec的完整解析器示例?

所以我想知道是否有一些更完整的parsec解析器的例子,超越了「這是你如何解析2 + 3」。尤其是函數調用等方面。

我讀過「給你寫一個Scheme」,但是Scheme的語法很簡單,並不能真正幫助學習。

我最多的問題是如何利用try<|>choice正確,因爲我真的不知道爲什麼秒差距似乎永遠不會使用這個解析器解析a(6)是一個函數調用:

expr = choice [number, call, ident] 

number = liftM Number float <?> "Number" 

ident = liftM Identifier identifier <?> "Identifier" 

call = do 
    name <- identifier 
    args <- parens $ commaSep expr 
    return $ FuncCall name args 
    <?> "Function call" 

編輯增加了完成一些代碼,雖然這其實不是我問的事情:

AST.hs

module AST where 

data AST 
    = Number Double 
    | Identifier String 
    | Operation BinOp AST AST 
    | FuncCall String [AST] 
    deriving (Show, Eq) 

data BinOp = Plus | Minus | Mul | Div 
    deriving (Show, Eq, Enum) 

Lexer.hs

module Lexer (
      identifier, reserved, operator, reservedOp, charLiteral, stringLiteral, 
      natural, integer, float, naturalOrFloat, decimal, hexadecimal, octal, 
      symbol, lexeme, whiteSpace, parens, braces, angles, brackets, semi, 
      comma, colon, dot, semiSep, semiSep1, commaSep, commaSep1 
    ) where 

import Text.Parsec 
import qualified Text.Parsec.Token as P 
import Text.Parsec.Language (haskellStyle) 

lexer = P.makeTokenParser haskellStyle 

identifier = P.identifier lexer 
reserved = P.reserved lexer 
operator = P.operator lexer 
reservedOp = P.reservedOp lexer 
charLiteral = P.charLiteral lexer 
stringLiteral = P.stringLiteral lexer 
natural = P.natural lexer 
integer = P.integer lexer 
float = P.float lexer 
naturalOrFloat = P.naturalOrFloat lexer 
decimal = P.decimal lexer 
hexadecimal = P.hexadecimal lexer 
octal = P.octal lexer 
symbol = P.symbol lexer 
lexeme = P.lexeme lexer 
whiteSpace = P.whiteSpace lexer 
parens = P.parens lexer 
braces = P.braces lexer 
angles = P.angles lexer 
brackets = P.brackets lexer 
semi = P.semi lexer 
comma = P.comma lexer 
colon = P.colon lexer 
dot = P.dot lexer 
semiSep = P.semiSep lexer 
semiSep1 = P.semiSep1 lexer 
commaSep = P.commaSep lexer 
commaSep1 = P.commaSep1 lexer 

Parser.hs

module Parser where 

import Control.Monad (liftM) 
import Text.Parsec 
import Text.Parsec.String (Parser) 
import Lexer 
import AST 

expr = number <|> callOrIdent 

number = liftM Number float <?> "Number" 

callOrIdent = do 
    name <- identifier 
    liftM (FuncCall name) (parens $ commaSep expr) <|> return (Identifier name) 
+0

具體的問題應該很容易回答,但我更願意嘗試一個完整的,可編譯的代碼示例來展示您的問題......您能提供一個嗎? – sclv

+0

但是我注意到,你不會在任何地方使用'try'。在你最小的例子中,我不確定它是否重要,但是在任何更大的樣本中它當然會。 – sclv

+0

試圖提供我迄今爲止的整個計劃。 – Lanbo

回答

10

嗯,

*Expr> parse expr "" "a(6)" 
Right (FuncCall "a" [Number 6.0]) 

那部分填寫遺漏的部分後,對我的作品。

編輯:我寫我自己的float分析器,它可以解析整數文字填寫缺失的部分。另一方面,Text.Parsec.Tokenfloat解析器僅解析帶有小數部分或指數的文字,因此解析「6」失敗。

然而,

*Expr> parse expr "" "variable" 
Left (line 1, column 9): 
unexpected end of input 
expecting "(" 
具有解析的標識符之後,當呼叫失敗

,輸入的一部分被消耗,因此IDENT未試過,和整體解析失敗。您可以a)在expr的選擇列表中使其成爲try call,以便調用失敗而不消耗輸入,或者b)編寫解析器callOrIdent以在expr中使用,例如,

callOrIdent = do 
    name <- identifier 
    liftM (FuncCall name) (parens $ commaSep expr) <|> return (Identifier name) 

其避免try,並且因此可以更好地執行。

+0

我將'Text.Parsec.Token'的詞法分析器函數用於'identifier'等。出於某種原因,對於您給我的代碼,我得到完全不同的分析結果。 – Lanbo

+0

@Scán啊,'Token'的'float'解析器不會解析整數文字,您必須編寫'a(6.0)'或類似文件。但除此之外,其行爲如同上述,或多或少。 –

+0

......我現在覺得很蠢。謝謝! (我仍然希望那些完整的例子) – Lanbo

-2

本書Write Yourself a Scheme in 48 Hours是一種性能優良,在秒差距的功能深入介紹和教程。它通過深入的示例向您演示所有內容,最後您在parsec解釋器中實現了相當大部分的方案。

+2

OP明確表示這個例子是不夠的。 – is7s