This question与我的问题不重复。 其实我有3个问题。
在下面的代码中,我尝试创建一个解析器,它可能解析嵌套的多行块注释。与引用的其他问题相反,我尝试在没有任何递归函数的情况下以直接的方式解决问题(参见其他帖子的接受答案)。
我遇到的第一个问题是FParsec的skipManyTill解析器也从流中使用了末尾解析器。所以我创建了skipManyTillEx(Ex为'排除了endp&#39 ;;))。 skipManyTillEx似乎有效 - 至少对于我也添加到fsx脚本的一个测试用例。
然而,在显示的代码中,现在我得到了#34;组合器'许多'被应用于一个成功的解析器而不消耗..."错误。我的理论是,commentContent
解析器是产生此错误的行。
在这里,我的问题:
skipManyTillEx
的方式出现问题吗?我实现它的方式在某种程度上与skipManyTill
的实现方式不同,主要是在如何控制解析流方面。在原始skipManyTill
中,跟踪p和endp的Reply<_>
以及stream.StateTag
。在我的实现中,相比之下我没有看到使用stream.StateTag
的必要性,完全依赖于Reply<_>
状态代码。如果解析失败,skipManyTillEx
会回溯到流初始状态并报告错误。可能回溯代码会导致许多&#39;错误?我需要做什么呢?以下是代码:
#r @"C:\hgprojects\fparsec\Build\VS11\bin\Debug\FParsecCS.dll"
#r @"C:\hgprojects\fparsec\Build\VS11\bin\Debug\FParsec.dll"
open FParsec
let testParser p input =
match run p input with
| Success(result, _, _) -> printfn "Success: %A" result
| Failure(errorMsg, _, _) -> printfn "Failure %s" errorMsg
input
let Show (s : string) : string =
printfn "%s" s
s
let test p i =
i |> Show |> testParser p |> ignore
////////////////////////////////////////////////////////////////////////////////////////////////
let skipManyTillEx (p : Parser<_,_>) (endp : Parser<_,_>) : Parser<unit,unit> =
fun stream ->
let tryParse (p : Parser<_,_>) (stm : CharStream<unit>) : bool =
let spre = stm.State
let reply = p stream
match reply.Status with
| ReplyStatus.Ok ->
stream.BacktrackTo spre
true
| _ ->
stream.BacktrackTo spre
false
let initialState = stream.State
let mutable preply = preturn () stream
let mutable looping = true
while (not (tryParse endp stream)) && looping do
preply <- p stream
match preply.Status with
| ReplyStatus.Ok -> ()
| _ -> looping <- false
match preply.Status with
| ReplyStatus.Ok -> preply
| _ ->
let myReply = Reply(Error, mergeErrors preply.Error (messageError "skipManyTillEx failed") )
stream.BacktrackTo initialState
myReply
let ublockComment, ublockCommentImpl = createParserForwardedToRef()
let bcopenTag = "/*"
let bccloseTag = "*/"
let pbcopen = pstring bcopenTag
let pbcclose = pstring bccloseTag
let ignoreCommentContent : Parser<unit,unit> = skipManyTillEx (skipAnyChar) (choice [pbcopen; pbcclose] |>> fun x -> ())
let ignoreSubComment : Parser<unit,unit> = ublockComment
let commentContent : Parser<unit,unit> = skipMany (choice [ignoreCommentContent; ignoreSubComment])
do ublockCommentImpl := between (pbcopen) (pbcclose) (commentContent) |>> fun c -> ()
do test (skipManyTillEx (pchar 'a' |>> fun c -> ()) (pchar 'b') >>. (restOfLine true)) "aaaabcccc"
// do test ublockComment "/**/"
//do test ublockComment "/* This is a comment \n With multiple lines. */"
do test ublockComment "/* Bla bla bla /* nested bla bla */ more outer bla bla */"
答案 0 :(得分:1)
终于找到了修复many
问题的方法。
将我的自定义skipManyTillEx
替换为另一个名为skipManyTill1Ex
的自定义函数。
skipManyTill1Ex
与之前的skipManyTillEx
相比,只有成功解析了1个或更多p
才会成功。
我期望此版本的空注释/ ** /的测试失败,但它可以正常工作。
...
let skipManyTill1Ex (p : Parser<_,_>) (endp : Parser<_,_>) : Parser<unit,unit> =
fun stream ->
let tryParse (p : Parser<_,_>) (stm : CharStream<unit>) : bool =
let spre = stm.State
let reply = p stm
match reply.Status with
| ReplyStatus.Ok ->
stream.BacktrackTo spre
true
| _ ->
stream.BacktrackTo spre
false
let initialState = stream.State
let mutable preply = preturn () stream
let mutable looping = true
let mutable matchCounter = 0
while (not (tryParse endp stream)) && looping do
preply <- p stream
match preply.Status with
| ReplyStatus.Ok ->
matchCounter <- matchCounter + 1
()
| _ -> looping <- false
match (preply.Status, matchCounter) with
| (ReplyStatus.Ok, c) when (c > 0) -> preply
| (_,_) ->
let myReply = Reply(Error, mergeErrors preply.Error (messageError "skipManyTill1Ex failed") )
stream.BacktrackTo initialState
myReply
let ublockComment, ublockCommentImpl = createParserForwardedToRef()
let bcopenTag = "/*"
let bccloseTag = "*/"
let pbcopen = pstring bcopenTag
let pbcclose = pstring bccloseTag
let ignoreCommentContent : Parser<unit,unit> = skipManyTill1Ex (skipAnyChar) (choice [pbcopen; pbcclose] |>> fun x -> ())
let ignoreSubComment : Parser<unit,unit> = ublockComment
let commentContent : Parser<unit,unit> = skipMany (choice [ignoreCommentContent; ignoreSubComment])
do ublockCommentImpl := between (pbcopen) (pbcclose) (commentContent) |>> fun c -> ()
do test (skipManyTillEx (pchar 'a' |>> fun c -> ()) (pchar 'b') >>. (restOfLine true)) "aaaabcccc"
do test ublockComment "/**/"
do test ublockComment "/* This is a comment \n With multiple lines. */"
do test ublockComment "/* Bla bla bla /* nested bla bla */ more outer bla bla */"
答案 1 :(得分:1)
让我们来看看你的问题......
1。有什么理由,为什么我选择的方法不起作用?
你的方法绝对可行,你只需要清除错误。
2。任何人都可以看到我实施
skipManyTillEx
的方式出现问题吗?
没有。您的实现看起来不错。这只是问题的skipMany
和skipManyTillEx
的组合。
let ignoreCommentContent : Parser<unit,unit> = skipManyTillEx (skipAnyChar) (choice [pbcopen; pbcclose] |>> fun x -> ())
let commentContent : Parser<unit,unit> = skipMany (choice [ignoreCommentContent; ignoreSubComment])
skipMany
中的 commentContent
一直运行,直到ignoreCommentContent
和ignoreSubComment
都失败。但ignoreCommentContent
是使用您的skipManyTillEx
实现的,skipMany
的实现方式可以在不消耗输入的情况下成功实现。这意味着外部many
将无法确定何时停止,因为如果没有消耗输入,则它不知道后续解析器是否已失败或者根本不消耗任何内容。
这就是为什么要求skipManyTillEx
解析器下面的每个解析器都必须使用输入。您的skipMany1TillEx
可能没有,这就是错误消息试图告诉您的内容。
要修复它,你必须实现一个open FParsec
open System
/// Type abbreviation for parsers without user state.
type Parser<'a> = Parser<'a, Unit>
/// Skips C-style multiline comment /*...*/ with arbitrary nesting depth.
let (comment : Parser<_>), commentRef = createParserForwardedToRef ()
/// Skips any character not beginning of comment end marker */.
let skipCommentChar : Parser<_> =
notFollowedBy (skipString "*/") >>. skipAnyChar
/// Skips anx mix of nested comments or comment characters.
let commentContent : Parser<_> =
skipMany (choice [ comment; skipCommentChar ])
// Skips C-style multiline comment /*...*/ with arbitrary nesting depth.
do commentRef := between (skipString "/*") (skipString "*/") commentContent
/// Prints the strings p skipped over on the console.
let printSkipped p =
p |> withSkippedString (printfn "Skipped: \"%s\" Matched: \"%A\"")
[
"/*simple comment*/"
"/** special / * / case **/"
"/*testing /*multiple*/ /*nested*/ comments*/ not comment anymore"
"/*not closed properly/**/"
]
|> List.iter (fun s ->
printfn "Test Case: \"%s\"" s
run (printSkipped comment) s |> printfn "Result: %A\n"
)
printfn "Press any key to exit..."
Console.ReadKey true |> ignore
,它至少会消耗一个元素。
3。有没有人看到,如何修复解析器,这个“很多...”错误消息消失了?
这种做法怎么样?
notFollowedBy
通过使用many
仅跳过不属于注释结束标记(* /)的字符,不需要嵌套的{{1}}解析器。
希望这会有所帮助:)