我正努力输入任何电子邮件并输出等效的XML编码。
我开始很小,有一个电子邮件标题 - “From Header”
以下是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(http://tools.ietf.org/html/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>
来自包含多个邮箱的标头,每个邮箱用逗号分隔:
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>
我希望在lexing期间删除所有评论。
所需的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"
请您展示如何实现词法分析器,解析器和序列化程序?
答案 0 :(得分:3)
将任务分为五个步骤:
第1步:为From Header指定完整,权威的BNF
第2步:创建一个词法分析函数lex
,将“从标题”分成一系列小块,例如from:
,{{1} },displayName
等等。这些小块称为令牌
angleAddress
第3步:定义数据类型lex :: String -> [Token]
,以表示从标题
步骤#4 :创建一个解析器函数From
,它使用标记序列并生成类型为parser
From
第5步:创建一个函数parse :: [Token] -> From
,用于遍历解析树并生成XML
serialize
RFC 5322中指定了From标头的完整,权威的BNF。我提取了适用于From标头的部分:
http://www.xfront.com/parsing/RFC-5322/From-Header/From-Header-BNF.pdf
这是一个示例,显示如何标记From标头:
对此标题进行标记:
serialize :: From -> XML
词法分析器的输出是这个令牌列表:
From: "John Doe" <john@doe.org>
列表中的每个项目都包含令牌的标签,位置信息,然后可选择一个值。位置信息是括号中的内容。 “AlexPn”是表示这是位置信息的标签。接下来的三个数字表示令牌的位置:起始位置,行号和列号。
以下是BFN的词法分析器。观察BNF和令牌定义之间的一对一映射。例如,BNF有这个生产规则:
[
TokenFrom (AlexPn 0 1 1)
, TokenDisplayName (AlexPn 6 1 7) "\"John Doe\""
, TokenAngleAddress (AlexPn 17 1 18) "<john@doe.org>"
]
词法分析器具有此标记定义:
qcontent = ( qtext | quoted-pair )
除了较小的句法差异外,它们是相同的。这真的很厉害。假设电子邮件“From header”的定义是正确的(即BNF是正确的),那么我们可以非常肯定词法分析器是正确的。
这是词法分析员:
http://www.xfront.com/parsing/RFC-5322/From-Header/Lexer.x.txt
令牌序列将在内部用数据类型表示:
@qcontent = ( $qtext | @quoted_pair )
这是一个示例,显示如何解析From Headers:
解析此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
解析器的输出是这个解析树:
From: "John Doe" <john@doe.org>
这是解析器:
http://www.xfront.com/parsing/RFC-5322/From-Header/Parser.y.txt
每个语法制作都有一个功能。例如,这是From语法生成的函数:
From
[
LongMailbox
(DisplayName "John Doe")
(AngleAddress "john@doe.org")
]
函数的参数是解析树的根,其标签为From。该函数调用另一个函数serializeMailboxList来处理根的子节点。结果包含在From start-tag,end-tag对中。
这是XML序列化程序:
http://www.xfront.com/parsing/RFC-5322/From-Header/serialize.hs.txt