F #, FParsec und rekursives Aufrufen eines Stream-Parsers
Ich entwickle einen mehrteiligen MIME-Parser mit F # und FParsec. Ich entwickle iterativ und daher handelt es sich hier um sehr unverbesserlichen, spröden Code, der nur mein erstes unmittelbares Problem löst. Rot, Grün, Refactor.
Ich muss eher einen Stream als eine Zeichenfolge analysieren, was mich wirklich zu einer Schleife veranlasst. Angesichts dieser Einschränkung muss ich nach meinem besten Verständnis einen Parser rekursiv aufrufen. Wie das geht, ist jenseits meines Wissens, zumindest so, wie ich bisher vorgegangen bin.
namespace MultipartMIMEParser
open FParsec
open System.IO
type private Post = { contentType : string
; boundary : string
; subtype : string
; content : string }
type MParser (s:Stream) =
let ($) f x = f x
let ascii = System.Text.Encoding.ASCII
let str cs = System.String.Concat (cs:char list)
let q = "\""
let qP = pstring q
let pSemicolon = pstring ";"
let manyNoDoubleQuote = many $ noneOf q
let enquoted = between qP qP manyNoDoubleQuote |>> str
let skip = skipStringCI
let pContentType = skip "content-type: "
>>. manyTill anyChar (attempt $ preturn () .>> pSemicolon)
|>> str
let pBoundary = skip " boundary=" >>. enquoted
let pSubtype = opt $ pSemicolon >>. skip " type=" >>. enquoted
let pContent = many anyChar |>> str // TODO: The content parser needs to recurse on the stream.
let pStream = pipe4 pContentType pBoundary pSubtype pContent
$ fun c b t s -> { contentType=c; boundary=b; subtype=t; content=s }
let result s = match runParserOnStream pStream () "" s ascii with
| Success (r,_,_) -> r
| Failure (e,_,_) -> failwith (sprintf "%A" e)
let r = result s
member p.ContentType = r.contentType
member p.Boundary = r.boundary
member p.ContentSubtype = r.subtype
member p.Content = r.content
Die erste Zeile des POST-Beispiels lautet:
content-type: Multipart/related; boundary="RN-Http-Body-Boundary"; type="multipart/related"
It erstreckt sich über eine einzelne Zeile in der Datei. Weitere Unterteile im Inhalt sindcontent-type
-Werte, die sich über mehrere Zeilen erstrecken, sodass ich weiß, dass ich meine Parser verfeinern muss, wenn ich sie wiederverwenden möchte.
Irgendwie muss ich @ anrufpContent
mit den (string?) Ergebnissen vonpBoundary
, so dass ich den Rest des Streams an den entsprechenden Grenzen aufteilen und dann irgendwie mehrere Teile für den Inhalt des Posts zurückgeben kann, von denen jeder ein separater Post ist, mit Überschriften und Inhalten (die natürlich etwas sein müssen) andere als eine Zeichenfolge). Mein Kopf dreht sich. Dieser Code scheint bereits viel zu komplex, um eine einzelne Zeile zu analysieren.
Viel Wertschätzung für Einsicht und Weisheit!