我正在使用 F# 和 FParsec 开发一个多部分 MIME 解析器。我正在迭代开发,所以这是高度未精炼、脆弱的代码——它只解决了我的第一个直接问题。红色,绿色,重构。
我需要解析一个流而不是一个字符串,这真的让我陷入了循环。鉴于该约束,据我所知,我需要递归调用解析器。如何做到这一点超出了我的理解,至少以我迄今为止的方式进行。
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
以便我可以在适当的边界上拆分流的其余部分,然后以某种方式返回帖子内容的多个部分,每个部分都是单独的post,带有标题和内容(显然必须是字符串以外的内容)。我的头在旋转。这段代码看起来太复杂了,无法解析一行。
非常感谢洞察力和智慧!