Goo*_*ide 5 f# parser-combinators fparsec
考虑以下将数字字符串转换为ints的解析器:
let toInt (s:string) =
match Int32.TryParse(s) with
| (true, n) -> preturn n
| _ -> fail "Number must be below 2147483648"
let naturalNum = many1Chars digit >>= toInt <?> "natural number"
Run Code Online (Sandbox Code Playgroud)
当我在非数字字符串上运行它时,"abc"它显示正确的错误消息:
let toInt (s:string) =
match Int32.TryParse(s) with
| (true, n) -> preturn n
| _ -> fail "Number must be below 2147483648"
let naturalNum = many1Chars digit >>= toInt <?> "natural number"
Run Code Online (Sandbox Code Playgroud)
但是,当我给它一个超出int范围的数字字符串时,它会给出以下适得其反的信息:
Error in Ln: 1 Col: 1
abc
^
Expecting: natural number
Run Code Online (Sandbox Code Playgroud)
主消息"Expecting: decimal digit"没有意义,因为我们已经有很多位数了。
有没有办法摆脱它而只展示它"Number must be below 2147483648"?
完整示例:
open System
open FParsec
[<EntryPoint>]
let main argv =
let toInt (s:string) =
match Int32.TryParse(s) with
| (true, n) -> preturn n
| _ -> fail "Number must be below 2147483648"
let naturalNum = many1Chars digit >>= toInt <?> "natural number"
match run naturalNum "9999999999999999" with
| Failure (msg, _, _) -> printfn "%s" msg
| Success (a, _, _) -> printfn "%A" a
0
Run Code Online (Sandbox Code Playgroud)
我认为这里问题的根源在于这是一个非语法问题,它不太适合前瞻解析器的模型。如果您可以用语法方式表达“太多数字”,那么它对于解析器也有意义,但事实上它会返回并尝试消耗更多输入。因此,我认为最干净的解决方案是在解析后在单独的过程中进行 int 转换。
也就是说,FParsec 看起来足够灵活,您仍然应该能够将其组合在一起。这就是你所要求的,我认为:
let naturalNum: Parser<int, _> =
fun stream ->
let reply = many1Chars digit stream
match reply.Status with
| Ok ->
match Int32.TryParse(reply.Result) with
| (true, n) -> Reply(n)
| _ -> Reply(Error, messageError "Number must be below 2147483648")
| _ ->
Reply(Error, reply.Error)
Run Code Online (Sandbox Code Playgroud)
或者,如果您想要“自然数”错误消息而不是“十进制数字”,请将最后一行替换为:
Reply(Error, messageError "Expecting: natural number")
Run Code Online (Sandbox Code Playgroud)