如何使用Alex和Happy对Lex,Parse和Serialize-to-XML电子邮件进行处理

时间:2013-06-27 22:41:40

标签: xml parsing lexer happy alex

我正努力输入任何电子邮件并输出等效的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"

请您展示如何实现词法分析器,解析器和序列化程序?

1 个答案:

答案 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

步骤#1:为数据格式指定完整,权威的BNF

RFC 5322中指定了From标头的完整,权威的BNF。我提取了适用于From标头的部分:

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

步骤2:创建一个词法分析器,将“从标题分解为标记”

这是一个示例,显示如何标记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

步骤3:定义数据类型以表示From Header

令牌序列将在内部用数据类型表示:

@qcontent = ( $qtext | @quoted_pair )

步骤#4:创建一个解析器 - 使用令牌序列并生成一个类型为“From”的解析树

这是一个示例,显示如何解析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

步骤#5:遍历解析树并在值

周围添加XML开始标记,结束标记对

每个语法制作都有一个功能。例如,这是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