代码之家  ›  专栏  ›  技术社区  ›  Ball

fparsec和基于分隔符的语法

  •  1
  • Ball  · 技术社区  · 14 年前

    我尝试使用fparsec将一个简单的todo列表语言(实际上是来自taskpaper的数据)解析为一个简单的解析器组合器示例。但我遇到了一个似乎无法解决的虫子。我是解析器组合器的新手,而fparsec似乎依赖于我对parsec的了解,但是我发现parsec文档是不可破解的。

    • 项目以“:”结尾
    • 任务以“-”开头
    • 任何其他文本行都是项目或任务的纯文本注释。

    下面是我的解析器代码。

    open FParsec.Primitives
    open FParsec.CharParsers
    type ProjectAst = ProjectName of string
                        | TaskText of string
                        | NoteText of string
    
    let asString (x:char list) :string =
        x
        |> List.map (fun y -> y.ToString())
        |> String.concat ""
    let makeNote x = NoteText(asString x)
    let parseProject =
        parse { let! s = many (noneOf ":\n\r\c")
                do! skipChar ':'
                return ProjectName( asString s ) }
    let parseTask =
        parse { do! skipChar '-'
                let! s = many (noneOf "\n\r\c")
                return TaskText( asString s) }
    let parseNote = many (noneOf "\n\r\c") |>> makeNote
    
    let parseLine = parseTask <|> (attempt parseProject) <|> parseNote
    let parseFile = sepBy parseLine (many1 whitespace)
    

    编辑

    语法取自Hogbay软件的taskpaper应用程序。 TaskPaper website 语法的一些例子

        Project 1:
        Description of Project One
        -task for project 1
        -another task for project 1
        details for another task
        -final task
    
        Go to store:
        -buy eggs
        -buy milk
    
    1 回复  |  直到 14 年前
        1
  •  3
  •   Mauricio Scheffer    14 年前

    我不是非常流利的fparsec,但这一个很有效:

    let newline = pchar '\n'
    let notNewLine = noneOf "\n"
    let allTillEOL = manyChars notNewLine
    
    let parseProject = 
        let r = manyCharsTill (noneOf ":\n") (pchar ':')
        r |>> ProjectName
    
    let parseTask = 
        let r = skipChar '-' >>. allTillEOL
        r |>> TaskText
    
    let parseNote = allTillEOL |>> NoteText
    
    let parseLine = parseTask <|> attempt parseProject <|> parseNote
    let parseFile = sepBy parseLine newline
    
    let a = run parseFile "Project 1:\nSome note\nProject 2:\n-One Task"
    match a with
    | Success (a,b,c) -> printfn "%A" a
    | Failure (a,b,c) -> printfn "failed: %s" a
    

    打印输出:

    [ProjectName "Project 1"; NoteText "Some note"; ProjectName "Project 2"; TaskText "One Task"]
    

    我会用其他例子来测试它。

    顺便说一句:在我使用fparsec的几次中,我更喜欢组合体样式而不是单体样式。