fparsec - combinator“很多”抱怨和...为什么不解析像这样的块评论?

时间:2014-06-03 17:36:32

标签: parsing f# comments multiline fparsec

首先,

This question与我的问题不重复。 其实我有3个问题。

在下面的代码中,我尝试创建一个解析器,它可能解析嵌套的多行块注释。与引用的其他问题相反,我尝试在没有任何递归函数的情况下以直接的方式解决问题(参见其他帖子的接受答案)。

我遇到的第一个问题是FParsec的skipManyTill解析器也从流中使用了末尾解析器。所以我创建了skipManyTillEx(Ex为'排除了endp&#39 ;;))。 skipManyTillEx似乎有效 - 至少对于我也添加到fsx脚本的一个测试用例。

然而,在显示的代码中,现在我得到了#34;组合器'许多'被应用于一个成功的解析器而不消耗..."错误。我的理论是,commentContent解析器是产生此错误的行。

在这里,我的问题:

  1. 有什么理由,为什么我选择的方法不起作用? 1中的解决方案,遗憾的是在我的系统上似乎没有编译,它使用递归的低级解析器来进行(嵌套)多行注释。
  2. 任何人都可以看到我实施skipManyTillEx的方式出现问题吗?我实现它的方式在某种程度上与skipManyTill的实现方式不同,主要是在如何控制解析流方面。在原始skipManyTill中,跟踪p和endp的Reply<_>以及stream.StateTag。在我的实现中,相比之下我没有看到使用stream.StateTag的必要性,完全依赖于Reply<_>状态代码。如果解析失败,skipManyTillEx会回溯到流初始状态并报告错误。可能回溯代码会导致许多&#39;错误?我需要做什么呢?
  3. (这是主要的问题) - 有没有人看到,如何修复解析器,这个&#34;很多......&#34;错误消息消失了?
  4. 以下是代码:

    #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 */"
    

2 个答案:

答案 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的方式出现问题吗?

没有。您的实现看起来不错。这只是问题的skipManyskipManyTillEx的组合。

let ignoreCommentContent : Parser<unit,unit> = skipManyTillEx (skipAnyChar)  (choice [pbcopen; pbcclose] |>> fun x -> ())
let commentContent : Parser<unit,unit> = skipMany (choice [ignoreCommentContent; ignoreSubComment])
skipMany中的

commentContent一直运行,直到ignoreCommentContentignoreSubComment都失败。但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}}解析器。

希望这会有所帮助:)