parsec的完整解析器示例?

问题描述:

我试图为一个简单的功能语言,有点像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

嗯,

*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

我写了一系列关于如何用parsec解析罗马数字的例子。这是非常基本的,但你或其他新来者可能会发现:

https://github.com/russell91/roman

本书Write Yourself a Scheme in 48 Hours是一种性能优良,在秒差距的功能深入介绍和教程。它通过深入的示例向您演示所有内容,最后您在parsec解释器中实现了相当大部分的方案。

+2

OP明确表示这个例子是不够的。 – is7s