質問
FPARSECを使用して、単純なパーサーコンビネーターの例として、単純なTODOリスト言語(実際にはタスクペーパーのデータ)を解析しようとしています。しかし、私はバグに出くわし、パズルアウトできないようです。私はパーサーの組み合わせを初めて使用していますが、FParsecはParsecを知っていることに頼っているようですが、Parsecのドキュメントは不可解であると感じています。
タスクペーパー言語のルールは簡単です(今のところ@Tagsを無視しています)
- プロジェクトは「:」で終わります
- タスクは「 - 」で始まります
- 他のテキスト行は、プロジェクトまたはタスクのいずれかに関する単純なテキストノートです
したがって、 "プロジェクト1: nsome note nproject 2:" ParseFileから[ProjectName( "Project 1"); noteText( "Some note"); projectName( "Project 2")]として戻る必要がありますが、代わりに、i 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ソフトウェアのタスクペーパーアプリケーションから取得されます タスクペーパーのウェブサイト構文のいくつかの例
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にあまり堪能ではありませんが、これはうまくいきます。
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を使用した数回は、モナディックスタイルよりもコンビネータースタイルを好みました。
所属していません StackOverflow