1

我正在尝试使用 fparsec 来解析一个简单的待办事项列表语言(实际上是来自 TaskPaper 的数据)作为一个简单的解析器组合器示例。但是我遇到了一个我似乎无法解开的错误。我是解析器组合器的新手,FParsec 似乎依赖于我了解 Parsec,但我发现 parsec 文档难以理解。

任务论文语言的规则很简单(我现在忽略@tags)

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

所以字符串 "Project 1:\nSome note\nProject 2:" 应该从 parseFile 作为 [ProjectName("Project 1");NoteText("Some note");ProjectName("Project 2")] 返回,但相反,我get [ProjectName("Project 1");ProjectName("Some note\nProject 2")]

下面是我的解析器代码。

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 Software 的 TaskPaper 应用程序TaskPaper 网站 一些语法示例

    项目一:
    项目一说明
    - 项目 1 的任务
    - 项目 1 的另一个任务
    另一个任务的详细信息
    -最终任务

    去商店:
    - 买鸡蛋
    - 买牛奶
4

1 回答 1

3

我对 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 的几次我更喜欢组合样式而不是单子样式。

于 2010-11-23T04:42:25.173 回答