问题描述
我正在尝试使用fparsec解析简单的待办事项列表语言(实际上是TaskPaper的数据)作为简单的解析器组合器示例.但是我遇到了一个我似乎无法迷惑的错误.我是解析器组合器的新手,FParsec似乎依赖于我了解Parsec,但是我发现parsec文档难以理解.
I'm trying to use fparsec to parse a simple todo list language (the data from TaskPaper actually) as a simple parser combinator example. But I've run into a bug I can't seem to puzzle out. I'm new to parser combinators and FParsec seems to rely on me knowing Parsec, but I'm finding the parsec documentation inscrutable.
任务文件语言的规则很简单(我暂时忽略了@tags)
The rules of the task paper language are simple (I'm ignoring @tags for now)
- 项目以':' 结尾
- 任务以-"开头
- 任何其他文本行都是有关项目或任务的纯文本注释
因此,字符串"Project 1:\ nSome note \ nProject 2:"应从parseFile返回为[ProjectName("Project 1"); NoteText("Some note"); ProjectName("Project 2")]],但是相反,我得到了[ProjectName("Project 1"); ProjectName("Some note \ nProject 2")]
So the string "Project 1:\nSome note\nProject 2:" should return from parseFile as [ProjectName("Project 1");NoteText("Some note");ProjectName("Project 2")], but instead, I get [ProjectName("Project 1");ProjectName("Some note\nProject 2")]
下面是我的解析器代码.
Below is my parser code.
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网站语法示例
The syntax is taken from Hogbay Software's TaskPaper application TaskPaper websiteSome examples of the syntax
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
推荐答案
我在FParsec方面不是很流利,但是这种方法很有效:
I'm not super fluent in FParsec, but this one works:
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"]
我将对照其他示例对其进行测试.
I'd test it against other examples.
顺便说一句:几次使用FParsec时,我更喜欢组合器风格而不是单子风格.
BTW: the few times I've used FParsec I've preferred the combinator style over monadic style.
这篇关于FParsec和基于分隔符的语法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!