FParsec и синтаксис на основе разделителя - PullRequest
1 голос
/ 23 ноября 2010

Я пытаюсь использовать fparsec для анализа простого языка списка задач (на самом деле данных из TaskPaper) в качестве простого примера комбинатора синтаксического анализа.Но я столкнулся с ошибкой, которую не могу разгадать.Я новичок в комбинаторах синтаксического анализа, и FParsec, кажется, полагается, что я знаю Parsec, но я нахожу документацию parsec непостижимой.

Правила языка заданий просты (я игнорирую @tags длясейчас)

  • Проекты заканчиваются на ':'
  • Задачи начинаются с '-'
  • Любая другая строка текста представляет собой обычную текстовую заметку напроект или задача

Поэтому строка «Project 1: \ nSome note \ nProject 2:» должна возвращаться из parseFile как [ProjectName («Project 1»); NoteText («Some note»); ProjectName)(«Проект 2»)], но вместо этого я получаю [ProjectName («Проект 1»); ProjectName («Некоторая заметка \ nПроект 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)

Отредактировано

Синтаксис взят из приложения TaskPaper Hogbay Software Сайт TaskPaper Некоторые примеры синтаксиса

    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 Ответ

3 голосов
/ 23 ноября 2010

Я не очень беглый в 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, я предпочитал стиль комбинатора, а не монадический.

...