是否可以强制回溯所有选项?
Is it possible to force backtrack all options?
我需要解析函数声明的语法
foo x = 1
Func "foo" (Ident "x") = 1
foo (x = 1) = 1
Func "foo" (Label "x" 1) = 1
foo x = y = 1
Func "foo" (Ident "x") = (Label "y" 1)
我写了这个解析器
module SimpleParser where
import Text.Parsec.String (Parser)
import Text.Parsec.Language (emptyDef)
import Text.Parsec
import qualified Text.Parsec.Token as Tok
import Text.Parsec.Char
import Prelude
lexer :: Tok.TokenParser ()
lexer = Tok.makeTokenParser style
where
style = emptyDef {
Tok.identLetter = alphaNum
}
parens :: Parser a -> Parser a
parens = Tok.parens lexer
commaSep :: Parser a -> Parser [a]
commaSep = Tok.commaSep1 lexer
commaSep1 :: Parser a -> Parser [a]
commaSep1 = Tok.commaSep1 lexer
identifier :: Parser String
identifier = Tok.identifier lexer
reservedOp :: String -> Parser ()
reservedOp = Tok.reservedOp lexer
data Expr = IntLit Int | Ident String | Label String Expr | Func String Expr Expr | ExprList [Expr] deriving (Eq, Ord, Show)
integer :: Parser Integer
integer = Tok.integer lexer
litInt :: Parser Expr
litInt = do
n <- integer
return $ IntLit (fromInteger n)
ident :: Parser Expr
ident = Ident <$> identifier
paramLabelItem = litInt <|> paramLabel
paramLabel :: Parser Expr
paramLabel = do
lbl <- try (identifier <* reservedOp "=")
body <- paramLabelItem
return $ Label lbl body
paramItem :: Parser Expr
paramItem = parens paramRecord <|> litInt <|> try paramLabel <|> ident
paramRecord :: Parser Expr
paramRecord = ExprList <$> commaSep1 paramItem
func :: Parser Expr
func = do
name <- identifier
params <- paramRecord
reservedOp "="
body <- paramRecord
return $ (Func name params body)
parseExpr :: String -> Either ParseError Expr
parseExpr s = parse func "" s
我可以解析 foo (x) = 1
但我无法解析 foo x = 1
parseExpr "foo x = 1"
Left (line 1, column 10):
unexpected end of input
expecting digit, "," or "="
我知道它试图像 Func "foo" (Label "x" 1)
一样解析此代码但失败了。但是在失败之后为什么它不能像 Func "foo" (Ident "x") = 1
那样尝试解析它
有什么办法吗?
我也试过交换 ident
和 paramLabel
paramItem = parens paramRecord <|> litInt <|> try paramLabel <|> ident
paramItem = parens paramRecord <|> litInt <|> try ident <|> paramLabel
在这种情况下我可以解析 foo x = 1
但我无法解析 foo (x = 1) = 2
parseExpr "foo (x = 1) = 2"
Left (line 1, column 8):
unexpected "="
expecting "," or ")"
以下是我对秒差距回溯工作原理(以及不工作)的理解:
在:
(try a <|> try b <|> c)
如果 a
失败,将尝试 b
,如果 b
随后失败,将尝试 c
。
然而,在:
(try a <|> try b <|> c) >> d
如果 a
成功但 d
失败,Parsec 不会 返回并尝试 b
。一旦 a
成功,Parsec 就认为整个选择都已解析,然后继续 d
。它永远不会回去尝试 b
或 c
.
出于同样的原因,这也不起作用:
(try (try a <|> try b <|> c)) >> d
一旦a
或b
成功,则整个选择成功,因此外部try
成功。然后解析移至 d
.
一个解决方案是将 d
分发到选择范围内:
try (a >> d) <|> try (b >> d) <|> (c >> d)
现在如果 a
成功但 d
失败,将尝试 b >> d
。
我需要解析函数声明的语法
foo x = 1
Func "foo" (Ident "x") = 1
foo (x = 1) = 1
Func "foo" (Label "x" 1) = 1
foo x = y = 1
Func "foo" (Ident "x") = (Label "y" 1)
我写了这个解析器
module SimpleParser where
import Text.Parsec.String (Parser)
import Text.Parsec.Language (emptyDef)
import Text.Parsec
import qualified Text.Parsec.Token as Tok
import Text.Parsec.Char
import Prelude
lexer :: Tok.TokenParser ()
lexer = Tok.makeTokenParser style
where
style = emptyDef {
Tok.identLetter = alphaNum
}
parens :: Parser a -> Parser a
parens = Tok.parens lexer
commaSep :: Parser a -> Parser [a]
commaSep = Tok.commaSep1 lexer
commaSep1 :: Parser a -> Parser [a]
commaSep1 = Tok.commaSep1 lexer
identifier :: Parser String
identifier = Tok.identifier lexer
reservedOp :: String -> Parser ()
reservedOp = Tok.reservedOp lexer
data Expr = IntLit Int | Ident String | Label String Expr | Func String Expr Expr | ExprList [Expr] deriving (Eq, Ord, Show)
integer :: Parser Integer
integer = Tok.integer lexer
litInt :: Parser Expr
litInt = do
n <- integer
return $ IntLit (fromInteger n)
ident :: Parser Expr
ident = Ident <$> identifier
paramLabelItem = litInt <|> paramLabel
paramLabel :: Parser Expr
paramLabel = do
lbl <- try (identifier <* reservedOp "=")
body <- paramLabelItem
return $ Label lbl body
paramItem :: Parser Expr
paramItem = parens paramRecord <|> litInt <|> try paramLabel <|> ident
paramRecord :: Parser Expr
paramRecord = ExprList <$> commaSep1 paramItem
func :: Parser Expr
func = do
name <- identifier
params <- paramRecord
reservedOp "="
body <- paramRecord
return $ (Func name params body)
parseExpr :: String -> Either ParseError Expr
parseExpr s = parse func "" s
我可以解析 foo (x) = 1
但我无法解析 foo x = 1
parseExpr "foo x = 1"
Left (line 1, column 10):
unexpected end of input
expecting digit, "," or "="
我知道它试图像 Func "foo" (Label "x" 1)
一样解析此代码但失败了。但是在失败之后为什么它不能像 Func "foo" (Ident "x") = 1
有什么办法吗?
我也试过交换 ident
和 paramLabel
paramItem = parens paramRecord <|> litInt <|> try paramLabel <|> ident
paramItem = parens paramRecord <|> litInt <|> try ident <|> paramLabel
在这种情况下我可以解析 foo x = 1
但我无法解析 foo (x = 1) = 2
parseExpr "foo (x = 1) = 2"
Left (line 1, column 8):
unexpected "="
expecting "," or ")"
以下是我对秒差距回溯工作原理(以及不工作)的理解:
在:
(try a <|> try b <|> c)
如果 a
失败,将尝试 b
,如果 b
随后失败,将尝试 c
。
然而,在:
(try a <|> try b <|> c) >> d
如果 a
成功但 d
失败,Parsec 不会 返回并尝试 b
。一旦 a
成功,Parsec 就认为整个选择都已解析,然后继续 d
。它永远不会回去尝试 b
或 c
.
出于同样的原因,这也不起作用:
(try (try a <|> try b <|> c)) >> d
一旦a
或b
成功,则整个选择成功,因此外部try
成功。然后解析移至 d
.
一个解决方案是将 d
分发到选择范围内:
try (a >> d) <|> try (b >> d) <|> (c >> d)
现在如果 a
成功但 d
失败,将尝试 b >> d
。