2014-11-10 5 views
1

Я разрабатываю многопоточный MIME-парсер, используя F # и FParsec. Я развиваюсь итеративно, и поэтому это очень нерафинированный, хрупкий код - он решает только мою первую проблему. Красный, Зеленый, Рефактор.F #, FParsec, и вызывать рекурсивный анализатор потока

Мне нужно разбирать поток, а не строку, которая действительно бросает меня за цикл. Учитывая это ограничение, насколько я понимаю, мне нужно вызвать рекурсивно парсер. Как это сделать, это за пределами моего кен, по крайней мере, с тем, как я продолжал до сих пор.

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 

Первая строка примера POST следующим образом:

content-type: Multipart/related; boundary="RN-Http-Body-Boundary"; type="multipart/related"

Она охватывает одну строку в файле. Дальнейшие части содержимого включают значения content-type, которые охватывают несколько строк, поэтому я знаю, что мне придется уточнять свои парсеры, если я их повторно использую.

Как-то мне нужно позвонить pContent с результатами (строки?) pBoundary, чтобы я мог разделить остальную часть потока на соответствующих границах, а затем как-то вернуть несколько частей для содержимого сообщения, каждый из которых будет отдельный пост, с заголовками и контентом (который, очевидно, должен быть чем-то другим, кроме строки). Моя голова кружится. Этот код уже кажется слишком сложным для синтаксического анализа одной строки.

Большое значение для понимания и мудрости!

ответ

2

Это фрагмент, который может заставить вас двигаться в правильном направлении.

Получите ваши парсеры, чтобы выплюнуть что-то с тем же базовым типом. Для этой цели я предпочитаю использовать дискриминационные союзы F #. Если вам действительно нужно вставлять значения в тип Post, тогда перейдите в возвращаемое дерево AST. Я просто подхожу к нему.

#if INTERACTIVE 
#r"""..\..\FParsecCS.dll""" // ... edit path as appropriate to bin/debug, etc. 
#r"""..\..\FParsec.dll""" 
#endif 

let packet = @"content-type: Multipart/related; boundary=""RN-Http-Body-Boundary""; type=""multipart/related"" 

--RN-Http-Body-Boundary 
Message-ID: <[email protected]> 
Mime-Version: 1.0 
Content-Type: multipart/related; type=""application/xml""; 
    boundary=""----=_Part_235_11184805.1160080657052"" 

------=_Part_235_11184805.1160080657052 
Content-Type: Application/XML 
Content-Transfer-Encoding: binary 
Content-Location: RN-Preamble 
Content-ID: <[email protected]>" 

//XML document begins here... 

type AST = 
| Document of AST list 
| Header of AST list 
/// ie. Content-Type is the tag, and it consists of a list of key value pairs 
| Tag of string * AST list 
| KeyValue of string * string 
| Body of string 

AST AST DU above может представлять собой первый проход данных примера, который вы отправили в своем другом вопросе. Это может быть более зернистым, чем это, но проще, как правило, лучше. Я имею в виду, конечный пункт назначения в вашем примере - тип Post, и вы можете достичь этого с помощью простого простого сопоставления шаблонов.

+0

Я закончил с дискриминационным союзом, хотя я не уверен, что я хорошо его разработал. Я спросил [новый вопрос] (http://stackoverflow.com/questions/26891564/f-fparsec-and-calling-a-stream-parser-recursively-second-take) на основе этой обратной связи и отзывов от [мой второй вопрос] (http://stackoverflow.com/questions/26875192/f-fparsec-and-updating-userstate). Спасибо! –