recursion - F#, FParsec, and Calling a Stream Parser Recursively, Second Take -



recursion - F#, FParsec, and Calling a Stream Parser Recursively, Second Take -

thank replies my first post , my sec post on project. question same question first, code updated according feedback received on 2 questions. how phone call parser recursively?

i'm scratching head , staring blankly @ code. i've no thought go here. that's when turn stackoverflow.

i've included in code comments compile-time errors i'm receiving. 1 stumbling block may discriminated union. i've not worked discriminated unions much, may using mine incorrectly.

the illustration post i'm working with, bits of i've included in previous 2 questions, consists of 1 boundary includes sec post new boundary. sec post includes several additional parts separated sec boundary. each of several additional parts new post consisting of headers , xml.

my goal in project build library used in our c# solution, library taking stream , returning post parsed headers , parts recursively. really want f# shine here.

namespace multipartmimeparser open fparsec open system.io type header = { name : string ; value : string ; addl : (string * string) list alternative } type content = content of string | post of post list , post = { headers : header list ; content : content } type userstate = { boundary : string } static fellow member default = { boundary="" } module internal p = allow ($) f x = f x allow undefined = failwith "undefined." allow ascii = system.text.encoding.ascii allow str cs = system.string.concat (cs:char list) allow makeheader ((n,v),nvps) = { name=n; value=v; addl=nvps} allow runp p s = match runparseronstream p userstate.default "" s ascii | success (r,_,_) -> r | failure (e,_,_) -> failwith (sprintf "%a" e) allow blankfield = parray 2 newline allow delimited d e = allow pend = preturn () .>> e allow part = spaces >>. (manytill $ noneof d $ (attempt (preturn () .>> pstring d) <|> pend)) |>> str in part .>>. part allow delimited3 firstdelimiter seconddelimiter thirddelimiter endmarker = delimited firstdelimiter endmarker .>>. opt (many (delimited seconddelimiter endmarker >>. delimited thirddelimiter endmarker)) allow isboundary ((n:string),_) = n.tolower() = "boundary" allow pheader = allow includesboundary (h:header) = match h.addl | xs -> xs |> list.exists isboundary | none -> false allow setboundary b = { boundary=b } in delimited3 ":" ";" "=" blankfield |>> makeheader >>= fun header stream -> if includesboundary header stream.userstate <- setboundary (header.addl.value |> list.find isboundary |> snd) reply () else reply () allow pheaders = manytill pheader $ effort (preturn () .>> blankfield) allow rec pcontent (stream:charstream<userstate>) = match stream.userstate.boundary | "" -> // content text. allow nl = system.environment.newline allow unlines (ss:string list) = system.string.join (nl,ss) allow line = restofline false allow lines = manytill line $ effort (preturn () .>> blankfield) in pipe2 pheaders lines $ fun h c -> { headers=h ; content=content $ unlines c } | _ -> // content contains boundaries. allow b = "--" + stream.userstate.boundary // vs complains pcontent in next line: // type mismatch. expecting // parser<'a,userstate> // given // charstream<userstate> -> parser<post,userstate> // type 'reply<'a>' not match type 'parser<post,userstate>' allow p = pipe2 pheaders pcontent $ fun h c -> { headers=h; content=c } in skipstring b >>. manytill p (attempt (preturn () .>> blankfield)) // vs complains content.post in next line: // type mismatch. expecting // post list -> post // given // post list -> content // type 'post' not match type 'content' |>> content.post // vs complains pcontent in next line: // type mismatch. expecting // parser<'a,userstate> // given // charstream<userstate> -> parser<post,userstate> // type 'reply<'a>' not match type 'parser<post,userstate>' allow pstream = runp (pipe2 pheaders pcontent $ fun h c -> { headers=h; content=c }) type mparser (s:stream) = allow r = p.pstream s allow findheader name = match r.headers |> list.tryfind (fun h -> h.name.tolower() = name) | h -> h.value | none -> "" fellow member p.boundary = allow header = r.headers |> list.tryfind (fun h -> match h.addl | xs -> xs |> list.exists p.isboundary | none -> false) in match header | h -> h.addl.value |> list.find p.isboundary |> snd | none -> "" fellow member p.contentid = findheader "content-id" fellow member p.contentlocation = findheader "content-location" fellow member p.contentsubtype = findheader "type" fellow member p.contenttransferencoding = findheader "content-transfer-encoding" fellow member p.contenttype = findheader "content-type" fellow member p.content = r.content fellow member p.headers = r.headers fellow member p.messageid = findheader "message-id" fellow member p.mimeversion = findheader "mime-version"

edit

in response feedback i've received far (thank you!), made next adjustments, receiving errors annotated:

let rec pcontent (stream:charstream<userstate>) = match stream.userstate.boundary | "" -> // content text. allow nl = system.environment.newline allow unlines (ss:string list) = system.string.join (nl,ss) allow line = restofline false allow lines = manytill line $ effort (preturn () .>> blankfield) in pipe2 pheaders lines $ fun h c -> { headers=h ; content=content $ unlines c } | _ -> // content contains boundaries. allow b = "--" + stream.userstate.boundary // next complaint `pcontent stream`: // look expected have type // reply<'a> // here has type // parser<post,userstate> allow p = pipe2 pheaders (fun stream -> pcontent stream) $ fun h c -> { headers=h; content=c } in skipstring b >>. manytill p (attempt (preturn () .>> blankfield)) // vs complains line above: // type mismatch. expecting // parser<post,userstate> // given // parser<'a list,userstate> // type 'post' not match type ''a list' // see above complaint `pcontent stream`. same complaint here. allow pstream = runp (pipe2 pheaders (fun stream -> pcontent stream) $ fun h c -> { headers=h; content=c })

i tried throwing in reply ()s, returned parsers, meaning c above became parser<...> rather content. seemed have been step backwards, or @ to the lowest degree in wrong direction. admit ignorance, though, , welcome correction!

i can help 1 of errors.

f# binds arguments left right, need utilize either parentheses around recursive calls pcontent or pipe-backward operator <| show want evaluate recursive phone call , bind homecoming value.

it's worth noting <| same $ operator.

content.post not constructor post object. need function take post list , homecoming post. (does list module need?)

recursion f# multipart fparsec

Comments

Popular posts from this blog

assembly - What is the addressing mode for ld, add, and rjmp instructions? -

vowpalwabbit - Interpreting Vowpal Wabbit results: Why are some lines appended by "h"? -

Is there a way to convert an HTML page styled with Bootstrap CSS into email-compatible html? -