2

任意の電子メール メッセージを入力し、同等の XML エンコーディングを出力できるように取り組んでいます。

メールヘッダーの 1 つである「From ヘッダー」から始めます。

From ヘッダーの例を次に示します。

From: John Doe <john@doe.org>

この XML に変換したい:

<From>
    <Mailbox>
        <DisplayName>John Doe</DisplayName>
        <Address>john@doe.org</Address>
    </Mailbox>
</From>

字句解析器 "Alex" ( http://www.haskell.org/alex/doc/html/ ) を使用して、From ヘッダーを分解 (トークン化) したいと考えています。

パーサー「Happy」( http://www.haskell.org/happy/ ) を使用してトークンを処理し、解析ツリーを生成したいと考えています。

次に、シリアライザーを使用して解析ツリーをたどり、XML を出力したいと考えています。

From ヘッダーの形式は、Internet Message Format (IMF) の RFC 5322 ( https://www.rfc-editor.org/rfc/rfc5322 ) で指定されています。

From ヘッダーと必要な XML 出力の例をいくつか次に示します。

表示名のないヘッダーから:

From: <john@doe.org>

必要な XML 出力:

<From>
    <Mailbox>
        <Address>john@doe.org</Address>
    </Mailbox>
</From>

表示名がなく、アドレスが山かっこで囲まれていないヘッダーから:

From: john@doe.org

必要な XML 出力:

<From>
    <Mailbox>
        <Address>john@doe.org</Address>
    </Mailbox>
</From>

それぞれがコンマで区切られた複数のメールボックスを持つヘッダーから:

From: <john@doe.org>, "Simon St. John" <simon@stjohn.org>, sally@smith.org

必要な XML 出力:

<From>
    <Mailbox>
        <Address>john@doe.org</Address>
    </Mailbox>
    <Mailbox>
        <DisplayName>Simon St. John</DisplayName>
        <Address>simon@stjohn.org</Address>
    </Mailbox>
    <Mailbox>
        <Address>sally@smith.org</Address>
    </Mailbox>
</From>

RFC 5322 によると、コメントの構文は ( … ) です。以下は、コメントを含む From ヘッダーです。

From: (this is a comment) "John Doe" <john@doe.org>

字句解析中にすべてのコメントを削除したい。

目的の XML 出力は次のとおりです。

<From>
    <Mailbox>
        <DisplayName>John Doe</DisplayName>
        <Address>john@doe.org</Address>
    </Mailbox>
</From>

RFC によると、From ヘッダー全体に「折りたたみ空白」が散らばっている可能性があります。以下は、1 行目に From: トークン、2 行目に表示名、3 行目にアドレスを含む From ヘッダーです。

From: 
    "John Doe" 
    <john@doe.org>

XML 出力は、折り畳みの空白の影響を受けません。

<From>
    <Mailbox>
        <DisplayName>John Doe</DisplayName>
        <Address>john@doe.org</Address>
    </Mailbox>
</From>

RFC によると、アドレスの @ 文字の後には、次のように角かっこで囲まれた文字列を指定できます。

From: "John Doe" <john@[website]>

そのようなメールを見たことがないことを認めなければなりません。それにもかかわらず、RFC はそれが許可されていると述べているので、レクサーとパーサーがそのような入力を処理することは確かに必要です。目的の出力は次のとおりです。

<From>
    <Mailbox>
        <DisplayName>John Doe</DisplayName>
        <Address>john@[website]</Address>
    </Mailbox>
</From>

エラー処理

Fromヘッダーが正しくない場合にエラーを生成したい。誤った From ヘッダーと目的の出力の例をいくつか示します。

アドレスの後に表示名が誤って配置されています。

From: <john@doe.org> "John Doe"

出力には、エラーが発見された場所が示されているはずです。

serialize: parse error at line 1 and column 22. Error occurred at "John Doe"

この From ヘッダーには、表示名の前に誤った「23」があります。

From: 23 "John Doe" <john@doe.org>

繰り返しますが、出力にはエラーが発見された場所が示されているはずです。

serialize: parse error at line 1 and column 10. Error occurred at "John Doe"

レクサー、パーサー、シリアライザーの実装方法を教えてください。

4

1 に答える 1

3

タスクを 5 つのステップに分割します。

ステップ #1 : From ヘッダーの完全で信頼できる BNF を指定する

ステップ #2 : From ヘッダーを、、lexなどの小さなチャンクのシーケンスに分割する字句解析関数 を作成します。これらの小さなチャンクはトークンと呼ばれますfrom:displayNameangleAddress

lex :: String -> [Token]

ステップ #3From : From ヘッダーを表すデータ型を定義する

ステップ #4parser :トークンのシーケンスを消費し、型の解析ツリーを生成するパーサー関数 を作成します。From

parse :: [Token] -> From

ステップ #5serialize :解析ツリーをたどって XML を生成する関数 を作成する

serialize :: From -> XML

ステップ #1: データ形式の完全で信頼できる BNF を指定する

From ヘッダーの完全で信頼できる BNF は、RFC 5322 で指定されています。From ヘッダーに該当する部分を抽出しました。

http://www.xfront.com/parsing/RFC-5322/From-Header/From-Header-BNF.pdf

ステップ #2: From ヘッダーをトークンに分割するレクサーを作成する

以下は、From ヘッダーがトークン化される方法を示す例です。

この From ヘッダーをトークン化します。

From: "John Doe" <john@doe.org>

lexer の出力は、次のトークンのリストです。

[ 
  TokenFrom (AlexPn 0 1 1)
  , TokenDisplayName (AlexPn 6 1 7) "\"John Doe\""
  , TokenAngleAddress (AlexPn 17 1 18) "<john@doe.org>"
]

リスト内の各項目は、トークンのラベル、位置情報、およびオプションで値で構成されます。カッコ内は位置情報です。「AlexPn」は位置情報であることを示すラベルです。次の 3 つの数字は、トークンの位置を示します。開始位置、行番号、列番号です。

以下は、BFN のレクサーです。BNF とトークン定義の間の 1 対 1 のマッピングを観察します。たとえば、BNF には次の生産規則があります。

qcontent  = ( qtext  |  quoted-pair )

レクサーには、次のトークン定義があります。

@qcontent = ( $qtext | @quoted_pair )

わずかな構文の違いを除けば、それらは同一です。それは本当に強力です。電子メールの「From ヘッダー」の定義が正しい (つまり、BNF が正しい) と仮定すると、レクサーが正しいことはほぼ確実です。

レクサーは次のとおりです。

http://www.xfront.com/parsing/RFC-5322/From-Header/Lexer.x.txt

ステップ 3: From ヘッダーを表すデータ型を定義する

一連のトークンは、次の from データ型を使用して内部的に表現されます。

data From
    = From MailboxList
    deriving Show

type MailboxList
    = [ Mailbox ]

data Mailbox
    = LongMailbox DisplayName AngleAddress
    | AngleMailbox AngleAddress
    | ShortMailbox AddressSpecification
    deriving Show

data DisplayName
    = DisplayName String
    deriving Show

data AngleAddress
    = AngleAddress String
    deriving Show

data AddressSpecification
    = AddressSpecification String
    deriving Show

ステップ #4: パーサーを作成します -- トークンのシーケンスを消費し、タイプ「From」の解析ツリーを生成します

以下は、From ヘッダーがどのように解析されるかを示す例です。

この From ヘッダーを解析します。

From: "John Doe" <john@doe.org>

パーサーの出力は、次の解析ツリーです。

From 
    [
        LongMailbox 
            (DisplayName "John Doe") 
            (AngleAddress "john@doe.org")
    ]

パーサーは次のとおりです。

http://www.xfront.com/parsing/RFC-5322/From-Header/Parser.y.txt

ステップ 5: 解析ツリーをたどり、XML の開始タグと終了タグのペアを値の周りに追加する

文法生成ごとに機能があります。たとえば、From 文法生成の関数は次のとおりです。

serialize :: From -> String
serialize (From mailboxList) = "<From>" ++ serializeMailboxList mailboxList ++ "</From>"

関数の引数は、From というラベルを持つ解析ツリーのルートです。この関数は別の関数 serializeMailboxList を呼び出して、ルートの子を処理します。結果は From 開始タグと終了タグのペアでラップされます。

XML シリアライザーは次のとおりです。

http://www.xfront.com/parsing/RFC-5322/From-Header/serialize.hs.txt

于 2013-06-27T22:41:39.543 に答える