2

我正在努力输入任何电子邮件并输出等效的 XML 编码。

我从小处着手,使用其中一个电子邮件标头——“发件人标头”

以下是 From Header 的示例:

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 Header。

我想使用解析器“Happy”(http://www.haskell.org/happy/)来处理标记并生成解析树。

然后我想使用序列化程序来遍历解析树并输出 XML。

From Header 的格式由 Internet 消息格式 (IMF)、RFC 5322 ( https://www.rfc-editor.org/rfc/rfc5322 ) 指定。

以下是 From Headers 和所需 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>

来自具有多个邮箱的 Header,每个邮箱用逗号分隔:

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 Header:

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 Header 中散布“折叠空格”。这是一个 From Header,第一行是 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 Header 不正确,我希望生成一个错误。以下是一些错误的 From Headers 示例和所需的输出:

显示名称错误地放在地址之后:

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

输出应指定发现错误的位置:

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

此 From Header 在显示名称前有一个错误的“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

将任务分为五个步骤:

第 1 步:为 From Header 指定完整、权威的 BNF

第 2 步:创建一个词法分析函数 ,lex将 From Header 分解为一系列小块,例如from:displayNameangleAddress等。这些小块称为令牌

lex :: String -> [Token]

第 3 步:定义数据类型From, 来表示 From Header

步骤#4:创建一个解析器函数 ,parser它使用标记序列并生成一个类型的解析树From

parse :: [Token] -> From

步骤#5:创建一个函数,serialize遍历解析树并生成 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 Headers 分解为标记

这是一个示例,显示如何对 From 标头进行标记:

标记这个 From 标头:

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

词法分析器的输出是这个标记列表:

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

列表中的每一项都由标记的标签、位置信息和可选的值组成。位置信息是括号中的内容。“AlexPn”是指示这是位置信息的标签。接下来的三个数字表示令牌的位置:开始位置、行号和列号。

下面是 BFN 的词法分析器。观察 BNF 和令牌定义之间的一对一映射。例如,BNF 有这样的生产规则:

qcontent  = ( qtext  |  quoted-pair )

词法分析器有这个标记定义:

@qcontent = ( $qtext | @quoted_pair )

除了细微的句法差异外,它们是相同的。这真的很强大。假设电子邮件“From header”的定义是正确的(即 BNF 是正确的),那么我们可以非常确定词法分析器是正确的。

这是词法分析器:

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

步骤 #3:定义一个数据类型来表示 From Header

令牌序列将在内部使用来自数据类型的 this 表示:

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 Headers 的示例:

解析这个 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 start-tag, end-tag 对中。

这是 XML 序列化程序:

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

于 2013-06-27T22:41:39.543 回答