2014-11-11 4 views
3

my last question이 응답을 얻지 못했기 때문에 나는 다른 방향으로 앞서 나가고 있습니다. 롤!F #, FParsec 및 Updating UserState

사용자 상태를 관리하거나 이전 구문 분석기의 결과에 액세스 할 때 the official documentation 이상의 예제를 찾을 수 없습니다.

N.b. 이 코드는 이 아니고이 아닙니다.

namespace MultipartMIMEParser 

open FParsec 
open System.IO 


type Header = { name : string 
       ; value : string 
       ; addl : (string * string) list option } 

type Content = Content of string 
      | Post of Post list 
and Post = { headers : Header list 
      ; content : Content } 

type private UserState = { Boundary : string } 
    with static member Default = { Boundary="" } 


module internal P = 
    let ($) f x = f x 
    let undefined = failwith "Undefined." 
    let ascii = System.Text.Encoding.ASCII 
    let str cs = System.String.Concat (cs:char list) 

    let makeHeader ((n,v),nvps) = { name=n; value=v; addl=nvps} 

    let runP p s = match runParserOnStream p UserState.Default "" s ascii with 
       | Success (r,_,_) -> r 
       | Failure (e,_,_) -> failwith (sprintf "%A" e) 

    let blankField = parray 2 newline 

    let delimited d e = 
     let pEnd = preturn() .>> e 
     let part = spaces >>. (manyTill $ noneOf d $ (attempt (preturn() .>> pstring d) <|> pEnd)) |>> str 
     in part .>>. part 

    let delimited3 firstDelimiter secondDelimiter thirdDelimiter endMarker = 
     delimited firstDelimiter endMarker 
     .>>. opt (many (delimited secondDelimiter endMarker 
         >>. delimited thirdDelimiter endMarker)) 

    // TODO: This is the parser I'm asking about. 
    let pHeader = 
     let includesBoundary s = undefined 
     let setBoundary b = { Boundary=b } 
     in delimited3 ":" ";" "=" blankField 
      |>> makeHeader 
      >>. fun stream -> if includesBoundary // How do I access the output from makeHeader here? 
          then stream.UserState <- setBoundary b // I need b to be read from the output of makeHeader. 
           Reply() 
          else Reply() 

    let pHeaders = manyTill pHeader $ attempt (preturn() .>> blankField) 

    // N.b. This is the mess I'm currently wrestling with. It does not compile, and is 
    // not sound yet. 
    let rec pContent boundary = 
     match boundary with 
     | "" -> // Content is text. 
       let line = restOfLine false 
       in pipe2 pHeaders (manyTill line $ attempt (preturn() .>> blankField)) 
        $ fun h c -> { headers=h 
           ; content=Content $ System.String.Join (System.Environment.NewLine,c) } 
     | _ -> // Content contains boundaries. 
       let b = "--"+boundary 
       let p = pipe2 pHeaders (pContent b) $ fun h c -> { headers=h; content=c } 
       in skipString b >>. manyTill p (attempt (preturn() .>> blankField)) 

    let pStream = runP (pipe2 pHeaders pContent $ fun h c -> { headers=h; content=c }) 


type MParser (s:Stream) = 
    let r = P.pStream s 

    let findHeader name = 
     match r.headers |> List.tryFind (fun h -> h.name.ToLower() = name) with 
     | Some h -> h.value 
     | None -> "" 

    member p.Boundary = 
    let isBoundary ((s:string),_) = s.ToLower() = "boundary" 
    let header = r.headers 
       |> List.tryFind (fun h -> if h.addl.IsSome 
              then h.addl.Value |> List.exists isBoundary 
              else false) 
    in match header with 
     | Some h -> h.addl.Value |> List.find isBoundary |> snd 
     | None -> "" 
    member p.ContentID = findHeader "content-id" 
    member p.ContentLocation = findHeader "content-location" 
    member p.ContentSubtype = findHeader "type" 
    member p.ContentTransferEncoding = findHeader "content-transfer-encoding" 
    member p.ContentType = findHeader "content-type" 
    member p.Content = r.content 
    member p.Headers = r.headers 
    member p.MessageID = findHeader "message-id" 
    member p.MimeVersion = findHeader "mime-version" 

내가 구문 분석을 시도하고있는 POST의 절단 된 예는 다음과 같습니다

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... 
+0

을 코드 컴파일? 더 많은 예제를 찾고 계십니까? 너 정확히 뭐야? – weismat

+0

@weismat, 의견을 보내 주셔서 감사합니다. 코드에 내 질문을 남겼는데, 이는 과도하게보기 쉽습니다. 기본적으로, "includesBoundary'와'setBoundary'에 대한 호출에서'makeHeader'의 출력에 어떻게 접근합니까?" @ Tarmil이 내가 찾던 대답을 나에게 주었다. –

답변

4

을 그러니까 기본적으로, 당신이 pHeader에하고 싶은 것은 실용적이 아니라, 모나드로 파서를 사용하는 것입니다 . 귀하의 코드 스타일을 기반으로 당신은 하스켈에서 왔으므로이 단어들을 알고 있다고 가정합니다. 다음 이런 식으로 뭔가 :

let pHeader = 
     let includesBoundary s = undefined 
     let setBoundary b = { Boundary=b } 
     in delimited3 ":" ";" "=" blankField 
      |>> makeHeader 
      >>= fun header stream -> 
       if includesBoundary header 
       then let b = undefined // some expression including header, if I understood correctly 
        stream.UserState <- setBoundary b 
        Reply() 
       else Reply() 

또는 당신은 (하스켈-표기 할 해당한다)를 계산 표현식에서 쓸 수 있습니다 : 나는 당신이 외에 기대보다 자세한해야한다 생각

let pHeader = 
     let includesBoundary s = undefined 
     let setBoundary b = { Boundary=b } 
     parse { 
      let! header = 
       delimited3 ":" ";" "=" blankField 
       |>> makeHeader 
      return! fun stream -> 
       if includesBoundary header 
       then let b = undefined // some expression including header, if I understood correctly 
        stream.UserState <- setBoundary b 
        Reply() 
       else Reply() 
     } 
+0

아름다운! 방금^연산자 대신에 잘못된 연산자 인'>> '을 사용했습니다. 정말 고맙습니다! –