FParsec y una sintaxis basada delimitador
Pregunta
Estoy intentando utilizar fparsec para analizar un sencillo lenguaje de lista de tareas (los datos de TaskPaper en realidad) como un ejemplo sencillo analizador combinador. Pero me he encontrado un bug me parece que no puede descifrar. Soy nuevo en combinadores de analizadores sintácticos y FParsec parece confiar en mí sabiendo Parsec, pero estoy encontrando el inescrutable documentación parsec.
Las reglas de la lengua de papel tareas son simples (estoy ignorando @tags por ahora)
- Los proyectos terminan con un ':'
- Las tareas se inician con '-'
- Cualquier otra línea de texto es una nota de texto sin formato a cada proyecto o tarea
Así que la cadena "Proyecto 1: \ nAlgunos nota \ nProject 2:" se apartase de parseFile como [Proyecto ( "Proyecto 1"); NoteText ( "Algunos nota"); Nombre proyecto ( "Proyecto 2")], pero en cambio, recibo [Proyecto ( "Proyecto 1"); Nombre proyecto ( "Algunos señalan \ nProject 2")]
A continuación se muestra el código del analizador.
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)
Editado
La sintaxis es tomada de la aplicación de TaskPaper Hogbay Software TaskPaper página web Algunos ejemplos de la sintaxis
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
Solución
No soy muy fluido en FParsec, pero este funciona:
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
imprime:
[ProjectName "Project 1"; NoteText "Some note"; ProjectName "Project 2"; TaskText "One Task"]
Me había prueba contra otros ejemplos.
Por cierto:. Las pocas veces que he usado FParsec he preferido el estilo combinador sobre el estilo monádico