我正在尝试为一个简单的函数式语言创建一个解析器,有点像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"
Run Code Online (Sandbox Code Playgroud)
编辑添加了一些完成的代码,虽然这实际上不是我问的问题:
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)
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
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)
Run Code Online (Sandbox Code Playgroud)
Dan*_*her 10
嗯,
*Expr> parse expr "" "a(6)"
Right (FuncCall "a" [Number 6.0])
Run Code Online (Sandbox Code Playgroud)
填写缺失的部分后,那部分对我有用.
编辑:我通过编写自己的float解析器填写了缺失的部分,它可以解析整数文字.在float从解析器Text.Parsec.Token在另一方面,仅解析文字与小数部分或指数,所以它没有解析"6".
然而,
*Expr> parse expr "" "variable"
Left (line 1, column 9):
unexpected end of input
expecting "("
Run Code Online (Sandbox Code Playgroud)
如果在解析了标识符后调用失败,则会消耗该部分输入,因此不会尝试ident,并且整个解析失败.您可以a)try call在选择列表中创建它expr,以便调用失败而不消耗输入,或b)编写解析器callOrIdent以用于expr,例如
callOrIdent = do
name <- identifier
liftM (FuncCall name) (parens $ commaSep expr) <|> return (Identifier name)
Run Code Online (Sandbox Code Playgroud)
这避免了try因此可能表现更好.