如何在解析时跳过指定的符号
How do I skip specified symbols while parsing
我正在尝试编写一个 trifecta 解析器,它可以解析下面所有三个 phone 数字。当我尝试通过调用 parseString parsePhone mempty phoneNum2
来使用 parsePhone
时,解析器在第一个破折号处失败并表示它期望 '('
.
当我在 phoneNum1 上调用解析器时,它在 ')'
处失败,说它期望 '('
.
为什么我的 skipSymbol 解析器失败了?我认为由于我使用 <|>
,解析器可以不检测 '('
并继续前进。我尝试使用 skipSymbol
的技术一定会失败吗?
phoneNum1 = "(123) 456 7890"
phoneNum2 = "123-456-7890"
phoneNum3 = "1234567890"
type NumberingPlanArea = Integer
type Exchange = Integer
type LineNumber = Integer
data PhoneNumber =
PhoneNumber NumberingPlanArea
Exchange LineNumber
deriving (Eq, Show)
parse3digits :: Parser Integer
parse3digits = read <$> replicateM 3 digit
skipSymbol :: Parser ()
skipSymbol =
skipMany (char '(')
<|> skipMany (char ')')
<|> skipMany (char '-')
<|> skipMany (char ' ')
parsePhone :: Parser PhoneNumber
parsePhone =
skipSymbol >>
parse3digits >>=
\area -> skipSymbol >>
parse3digits >>=
\exch -> skipSymbol >>
integer >>=
\line ->
pure $ PhoneNumber area exch line
skipMany p
应用解析器 p
零次或多次。在操作上,它是这样的:
- 尝试申请
p
。
- 如果
p
成功,重复步骤1。
- 如果
p
在没有消耗输入的情况下失败,则成功并 return ()
。 (这就是“零或更多”中的“零”的意思。)
- 如果
p
在消耗一些输入后失败,报告失败。
让我们看看skipSymbol
如何对输入)
进行操作。
- Parsec 尝试
skipSymbol
的左手选择,即 skipMany (char '(')
。
skipMany (char '(')
尝试应用 char '('
,但由于输入字符为 )
. 而未消耗输入而失败
- 因为
char '('
没有消耗输入就失败了,skipMany (char '(')
成功了没有消耗输入。这意味着 skipSymbol
中的其他选项将不会被尝试。
- 当前输入的字符仍然是
)
(这就是导致parse3Digits
稍后失败的原因)。
如前所述 ,修复方法是将 skipSymbol
的定义更改为
skipSymbol :: Parser ()
skipSymbol = skipMany $ choice [char c | c <- "()- "]
此版本循环选择,而不是在循环之间进行选择。
我正在尝试编写一个 trifecta 解析器,它可以解析下面所有三个 phone 数字。当我尝试通过调用 parseString parsePhone mempty phoneNum2
来使用 parsePhone
时,解析器在第一个破折号处失败并表示它期望 '('
.
当我在 phoneNum1 上调用解析器时,它在 ')'
处失败,说它期望 '('
.
为什么我的 skipSymbol 解析器失败了?我认为由于我使用 <|>
,解析器可以不检测 '('
并继续前进。我尝试使用 skipSymbol
的技术一定会失败吗?
phoneNum1 = "(123) 456 7890"
phoneNum2 = "123-456-7890"
phoneNum3 = "1234567890"
type NumberingPlanArea = Integer
type Exchange = Integer
type LineNumber = Integer
data PhoneNumber =
PhoneNumber NumberingPlanArea
Exchange LineNumber
deriving (Eq, Show)
parse3digits :: Parser Integer
parse3digits = read <$> replicateM 3 digit
skipSymbol :: Parser ()
skipSymbol =
skipMany (char '(')
<|> skipMany (char ')')
<|> skipMany (char '-')
<|> skipMany (char ' ')
parsePhone :: Parser PhoneNumber
parsePhone =
skipSymbol >>
parse3digits >>=
\area -> skipSymbol >>
parse3digits >>=
\exch -> skipSymbol >>
integer >>=
\line ->
pure $ PhoneNumber area exch line
skipMany p
应用解析器 p
零次或多次。在操作上,它是这样的:
- 尝试申请
p
。 - 如果
p
成功,重复步骤1。 - 如果
p
在没有消耗输入的情况下失败,则成功并 return()
。 (这就是“零或更多”中的“零”的意思。) - 如果
p
在消耗一些输入后失败,报告失败。
让我们看看skipSymbol
如何对输入)
进行操作。
- Parsec 尝试
skipSymbol
的左手选择,即skipMany (char '(')
。 skipMany (char '(')
尝试应用char '('
,但由于输入字符为)
. 而未消耗输入而失败
- 因为
char '('
没有消耗输入就失败了,skipMany (char '(')
成功了没有消耗输入。这意味着skipSymbol
中的其他选项将不会被尝试。 - 当前输入的字符仍然是
)
(这就是导致parse3Digits
稍后失败的原因)。
如前所述 skipSymbol
的定义更改为
skipSymbol :: Parser ()
skipSymbol = skipMany $ choice [char c | c <- "()- "]
此版本循环选择,而不是在循环之间进行选择。