fparsec - 限制应用解析器的字符数
fparsec - limit number of characters that a parser is applied to
我遇到一个问题,在流的解析过程中,我指出需要通过多次(按顺序)应用特定解析器来解析接下来的 N 个字符。
(剥离玩具)示例:
17<tag><anothertag><a42...
^
|- I'm here
假设 17 表示接下来的 N=17 个字符组成标签,所以我需要重复应用我的“tagParser”但在 17 个字符后停止并且不消耗其余部分,即使它看起来像标签,因为具有不同的含义,将被另一个解析器解析。
我不能使用 many
或 many1
因为那会吃掉超过 N 个字符的流。
我也不能用parray
,因为我不知道N个字符内有多少个解析器成功的应用
我正在研究 manyMinMaxSatisfy
但无法弄清楚在这种情况下如何使用它。
有没有办法切割流的 N 个字符并将它们提供给某些解析器?或者有没有办法调用许多应用程序但最多 N 个字符?
谢谢。
您可以使用 getPosition
来确保您没有超过指定的字符数。我把它放在一起(使用 F# 6),它似乎可以工作,尽管 simpler/faster 解决方案可能是可能的:
let manyLimit nChars p =
parse {
let! startPos = getPosition
let rec loop values =
parse {
let! curPos = getPosition
let nRemain = (startPos.Index + nChars) - curPos.Index
if nRemain = 0 then
return values
elif nRemain > 0 then
let! value = p
return! loop (value :: values)
else
return! fail $"limit exceeded by {-nRemain} chars"
}
let! values = loop []
return values |> List.rev
}
测试代码:
let ptag =
between
(skipChar '<')
(skipChar '>')
(manySatisfy (fun c -> c <> '>'))
let parser =
parse {
let! nChars = pint64
let! tags = manyLimit nChars ptag
let! rest = restOfLine true
return tags, rest
}
run parser "17<tag><anothertag><a42..."
|> printfn "%A"
输出为:
Success: (["tag"; "anothertag"], "<a42...")
相当低级的解析器,对原始 Reply
对象进行操作。它读取字符数,创建子字符串以提供给标签解析器并消耗剩余部分。应该有更简单的方法,但我对 FParsec
没有太多经验
open FParsec
type Tag = Tag of string
let pTag = // parses tag string and constructs 'Tag' object
skipChar '<' >>. many1Satisfy isLetter .>> skipChar '>'
|>> Tag
let pCountPrefixedTags stream =
let count = pint32 stream // read chars count
if count.Status = Ok then
let count = count.Result
// take exactly 'count' chars
let tags = manyMinMaxSatisfy count count (fun _ -> true) stream
if tags.Status = Ok then
// parse substring with tags
let res = run (many1 pTag) tags.Result
match res with
| Success (res, _, _) -> Reply(res)
| Failure (_, error, _) -> Reply(ReplyStatus.Error, error.Messages)
else
Reply(tags.Status, tags.Error)
else
Reply(count.Status, count.Error)
let consumeStream =
many1Satisfy (fun _ -> true)
run (pCountPrefixedTags .>>. consumeStream) "17<tag><anothertag><notTag..."
|> printfn "%A" // Success: ([Tag "tag"; Tag "anothertag"], "<notTag...")
您也可以在不进入流级别的情况下执行此操作。
open FParsec
let ptag =
between
(skipChar '<')
(skipChar '>')
(manySatisfy (fun c -> c <> '>'))
let tagsFromChars (l: char[]) =
let s = new System.String(l)
match run (many ptag) s with
| Success(result, _, _) -> result
| Failure(errorMsg, _, _) -> []
let parser =
parse {
let! nChars = pint32
let! tags = parray nChars anyChar |>> tagsFromChars
let! rest = restOfLine true
return tags, rest
}
run parser "17<tag><anothertag><a42..."
|> printfn "%A"
我遇到一个问题,在流的解析过程中,我指出需要通过多次(按顺序)应用特定解析器来解析接下来的 N 个字符。
(剥离玩具)示例:
17<tag><anothertag><a42...
^
|- I'm here
假设 17 表示接下来的 N=17 个字符组成标签,所以我需要重复应用我的“tagParser”但在 17 个字符后停止并且不消耗其余部分,即使它看起来像标签,因为具有不同的含义,将被另一个解析器解析。
我不能使用 many
或 many1
因为那会吃掉超过 N 个字符的流。
我也不能用parray
,因为我不知道N个字符内有多少个解析器成功的应用
我正在研究 manyMinMaxSatisfy
但无法弄清楚在这种情况下如何使用它。
有没有办法切割流的 N 个字符并将它们提供给某些解析器?或者有没有办法调用许多应用程序但最多 N 个字符?
谢谢。
您可以使用 getPosition
来确保您没有超过指定的字符数。我把它放在一起(使用 F# 6),它似乎可以工作,尽管 simpler/faster 解决方案可能是可能的:
let manyLimit nChars p =
parse {
let! startPos = getPosition
let rec loop values =
parse {
let! curPos = getPosition
let nRemain = (startPos.Index + nChars) - curPos.Index
if nRemain = 0 then
return values
elif nRemain > 0 then
let! value = p
return! loop (value :: values)
else
return! fail $"limit exceeded by {-nRemain} chars"
}
let! values = loop []
return values |> List.rev
}
测试代码:
let ptag =
between
(skipChar '<')
(skipChar '>')
(manySatisfy (fun c -> c <> '>'))
let parser =
parse {
let! nChars = pint64
let! tags = manyLimit nChars ptag
let! rest = restOfLine true
return tags, rest
}
run parser "17<tag><anothertag><a42..."
|> printfn "%A"
输出为:
Success: (["tag"; "anothertag"], "<a42...")
相当低级的解析器,对原始 Reply
对象进行操作。它读取字符数,创建子字符串以提供给标签解析器并消耗剩余部分。应该有更简单的方法,但我对 FParsec
open FParsec
type Tag = Tag of string
let pTag = // parses tag string and constructs 'Tag' object
skipChar '<' >>. many1Satisfy isLetter .>> skipChar '>'
|>> Tag
let pCountPrefixedTags stream =
let count = pint32 stream // read chars count
if count.Status = Ok then
let count = count.Result
// take exactly 'count' chars
let tags = manyMinMaxSatisfy count count (fun _ -> true) stream
if tags.Status = Ok then
// parse substring with tags
let res = run (many1 pTag) tags.Result
match res with
| Success (res, _, _) -> Reply(res)
| Failure (_, error, _) -> Reply(ReplyStatus.Error, error.Messages)
else
Reply(tags.Status, tags.Error)
else
Reply(count.Status, count.Error)
let consumeStream =
many1Satisfy (fun _ -> true)
run (pCountPrefixedTags .>>. consumeStream) "17<tag><anothertag><notTag..."
|> printfn "%A" // Success: ([Tag "tag"; Tag "anothertag"], "<notTag...")
您也可以在不进入流级别的情况下执行此操作。
open FParsec
let ptag =
between
(skipChar '<')
(skipChar '>')
(manySatisfy (fun c -> c <> '>'))
let tagsFromChars (l: char[]) =
let s = new System.String(l)
match run (many ptag) s with
| Success(result, _, _) -> result
| Failure(errorMsg, _, _) -> []
let parser =
parse {
let! nChars = pint32
let! tags = parray nChars anyChar |>> tagsFromChars
let! rest = restOfLine true
return tags, rest
}
run parser "17<tag><anothertag><a42..."
|> printfn "%A"