0
完成FParsec教程後,我決定嘗試編寫SDP (會話描述協議RFC 4366)的解析器 - 至少前3行。 SDP在ABNF(RFC 4234)中規定);所以,我正在努力工作。如何使用FParsec解析記錄或對象?
「用戶指南」第5.1節末尾的提示指出了「您從語法的葉節點開始使用簡單的解析器,然後逐步完成工作,直到您最終獲得完整的解析器。語法」有了這個方向,並從斯蒂芬的回答TIPSS用管,這是我現在有:
open FParsec
open System.Net
// handy for debugging, supposedly, but I couldn't get it to work
let breakParse (p: Parser<_,_>) stream =
p stream
// Input
let session = "v=0
o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5
s=SDP Seminar
"
type Sdp =
{ Version : System.UInt16;
Origin : Owner;
SessionName : string }
and Owner =
{ Username : string
SessionId : string
SessionVersion : string
NetType : NetworkType
AddrType : AddressType
Address : UnicastAddress }
and NetworkType =
| Undefined
| Internet
and AddressType =
| Undefined
| IPv4
| IPv6
and UnicastAddress =
| IPaddress of System.Net.IPAddress
| FQDomainName of string
| ExternalAddress of string
let sep : Parser<unit, unit> = skipChar '='
let getValue typeChar = skipChar typeChar .>> sep
let many1Digit : Parser<string, unit> = many1Satisfy isDigit
let many1Hex : Parser<string, unit> = many1Satisfy isHex
let nonWhitespace : Parser<string, unit> = many1Satisfy (isNoneOf @" \n\r\t")
//proto-version = %x76 "=" 1*DIGIT CRLF
let getVersion = getValue 'v' >>. many1Digit .>> spaces |>> System.Convert.ToUInt16
//origin-field = %x6f "=" username SP sess-id SP sess-version SP
// nettype SP addrtype SP unicast-address CRLF
// username cannot contain whitespace; i.e., only visible chars
let getUsername : Parser<string, unit> = getValue 'o' >>. nonWhitespace .>> spaces
//sess-id = 1*DIGIT
let getSessionId = many1Digit .>> spaces
//sess-version = 1*DIGIT
let getSessionVersion = many1Digit .>> spaces
let getNetType : Parser<NetworkType, unit> =
pstring "IN" |>> (function
| "IN" -> NetworkType.Internet
| _ -> NetworkType.Undefined)
.>> spaces
let getAddrType : Parser<AddressType, unit> =
anyString 3 |>> (function
| "IP4" -> AddressType.IPv4
| "IP6" -> AddressType.IPv6
| _ -> AddressType.Undefined)
.>> spaces
let getAddress : Parser<UnicastAddress, unit> =
(restOfLine true) |>> (fun a -> IPAddress.Parse a |> IPaddress)
let getUserSession = pipe3 getUsername getSessionId getSessionVersion (fun u i v -> (u, i, v))
let pipeOrigin = pipe4 getUserSession getNetType getAddrType getAddress
(fun us n t a ->
let u, i, v = us
{Username=u; SessionId=i; SessionVersion=v; NetType=n;
AddrType=t; Address=a})
//session-name-field = %x73 "=" text CRLF
let getSessionName = getValue 's' >>. restOfLine true
let threelines = pipe3 getVersion pipeOrigin getSessionName
(fun v o sn -> {Version=v; Origin=o; SessionName=sn})
let sessionDesc = run threelines session
而這個工作(除了的getAddress不處理FQDN或外部地址還),這個結果:
val sessionDesc : ParserResult<Sdp,unit> =
Success: {Version = 0us;
Origin = {Username = "jdoe";
SessionId = "2890844526";
SessionVersion = "2890842807";
NetType = Internet;
AddrType = IPv4;
Address = IPaddress 10.47.16.5;};
SessionName = "SDP Seminar";}
現在這是目標記錄類型Sdp。但是通過遍歷一些元組來獲得結果是一種複雜的方式。
我已閱讀5.4節的用戶指南,但所有示例都解析爲歧視聯盟。記錄類型是整理結果的最佳選擇;或者,還有更好的方法?
根據您的建議,我使用管道組合器編輯了更新代碼的問題;但是,我不滿足於去元組,然後到一個記錄。也許你有更直接的想法來創建記錄? – 2014-10-08 18:57:34
使用「管道」組合器可以創建一個記錄,如在'pipe3 getVersion pipeOrigin getSessionName(fun vo sn - > {Version = v; Origin = o; SessionName = sn})''中,儘管在你的情況下,你仍然必須讓'getVersion'返回一個'uint16'和'pipeOrigin '所有者'。 – 2014-10-10 08:01:18
嗯,是的,我已經使用您的建議更改更新了代碼,並獲得了所需的記錄。但是,我仍然想知道,如果可能具有自動屬性的對象比記錄更可取...尤其是,我繼續解析會話描述的其他行? – 2014-10-11 15:37:46