使用parsec的完整解析器示例?

时间:2011-11-21 21:05:40

标签: haskell parsec

我正在尝试为一个简单的函数式语言创建一个解析器,有点像Caml,但我似乎被最简单的东西困住了。

所以我想知道是否有更完整的parsec解析器示例,超出了“这就是你解析2 + 3”的方法。特别是函数调用等等。

我读过“给你写一个方案”,但是方案的语法很简单,并没有真正帮助学习。

我遇到的最大问题是如何正确使用try<|>choice,因为我真的不明白为什么parsec似乎永远不会将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)

3 个答案:

答案 0 :(得分:10)

嗯,

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

在填写缺失的部分后,该部分对我有用。

编辑:我通过编写自己的float解析器填写了缺失的部分,它可以解析整数文字。另一方面,来自float的{​​{1}}解析器仅使用小数部分或指数解析文字,因此解析“6”失败。

然而,

Text.Parsec.Token

如果在解析了标识符后调用失败,则会消耗该部分输入,因此不会尝试ident,并且整个解析失败。您可以a)在*Expr> parse expr "" "variable" Left (line 1, column 9): unexpected end of input expecting "(" 的选项列表中设置try call,以便调用失败而不消耗输入,或者b)编写解析器callOrIdent以在expr中使用,例如

expr

避免callOrIdent = do name <- identifier liftM (FuncCall name) (parens $ commaSep expr) <|> return (Identifier name) 因此可能表现更好。

答案 1 :(得分:2)

我写了一系列关于如何用parsec解析Roman Numerals的例子。这是非常基本的,但是您或其他新手可能会发现它很有用:

https://github.com/russell91/roman

答案 2 :(得分:-2)

本书Write Yourself a Scheme in 48 Hours是Parsec功能的优秀,深入概述和教程。它通过深入的示例引导您完成所有内容,最后您在parsec解释器中实现了相当重要的一部分方案。