展开iCalendar文档的合金模型

时间:2017-07-12 21:41:41

标签: alloy

下面是我创建的合金模型,用于展开iCalendar文档中的行。我想知道是否有更好的 - 更简单的模型?

以下是iCalendar和展开的简要说明,其次是我的Alloy模型。

iCalendar 是一种计算机文件格式,允许Internet用户通过以这种格式共享或发送文件,将会议请求和任务发送给其他Internet用户。 Wikipedia

iCalendar文件由一系列行组成。每行不能超过75个字符(实际上是75个八位字节,但这与此讨论无关)。如果一条线超过该长度,则该线必须“折叠”到下一条线上。一行开头的空格字符表示它是前一行的延续。

从折叠表示移动到单行表示的过程称为"展开"。展开是通过删除紧跟在后面的CRLF和空格字符来完成的...解析内容行时,必须首先展开折叠的行。 RFC5545

以下是iCalendar文件示例:

BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
BEGIN:VEVENT
DESCRIPTION;ALTREP="cid:part1.0001@example.org":The Fall'98 Wild
 Wizards Conference - - Las Vegas\, NV\, USA
END:VEVENT
END:VCALENDAR

DESCRIPTION属性的值会折叠到下一行,如Wizards Conference ...

开头的空格所示

以下是iCalendar文档中展开行的Alloy模型。

这是我在这个问题中发现的关键抽象:文档,行,空格,数据和展开。具体来说,有一个包含行的文档。每行都有数据。如果一行以空格开头,则它是一个延续行,必须与前一行合并。必须重复应用展开操作到文档。生成的文档必须没有折叠线。

一组有序的文档表示展开过程中文档的状态:

open util/ordering[Document]

该文档包含一组行。每行都有下一行(当然除了最后一行)。每行都有数据。展开后,一行将有其数据加上下一行的数据。因此,一条线具有一组数据。显然,其中一条线是第一条。

sig Document {
    lines: Line -> lone Line,
    content: Line -> set Data,
    firstLine: Line
} {
    firstLine in lines.Line
    // No Line maps to firstLine, i.e., firstLine is the first line
    no line: Line | lines[line] = firstLine
    // All lines are reachable from firstLine
    (lines.Line + lines[Line] - firstLine) in firstLine.^lines
    // The lines are sequential, i.e., lines are acyclic
    no ^lines & iden
    // No space at start of first line
    no firstLine.spaceAtStart
}

一条线的起点可能有一个空格。

sig Line {
    spaceAtStart: lone Space
} 

空格由单例集表示。数据是一组值。

one sig Space {}
sig Data {}

每次调用展开操作时,它都会在文档中展开一行。

展开操作的示例:假设文档包含以下行序列:

Line0, Line1, Line2

假设Line1在开头有一个空格。这意味着Line0在Line1上继续;展开将两条线合并为一条。展开后,该文件有:

Line0', Line2

其中Line0'包含Line0加Line1的数据。

pred unfold (doc, doc': Document, line: doc.lines) {
    // precondition: “line” has a next line (i.e., it's not the last line)
    some line[Line] 
    // precondition: the next line starts with a space
    some line[Line].spaceAtStart
    let line1 = line.Line, line2 = line[Line], line3 = doc.lines[line2] {
        // Merge line1 and line2. Set line3 to follow line1.
        // Add the content of line2 to line1. Delete line2 -> line3. 
        // Unless ... no line follows line2. Then the unfold
        // results in no line following line1. Remove line1 -> line2.
        some doc.lines[line2]   =>  doc'.lines = doc.lines ++ (line1 -> line3) -  (line2 -> line3)
                                                    else doc'.lines = doc.lines -  (line1 -> line2)
        doc'.content = doc.content ++ (line1 -> (doc.content[line1] + doc.content[line2]))
        doc'.firstLine = doc.firstLine
    }
}

用一些行初始化文档。为每一行提供唯一数据。制作一些需要展开的线条。

pred init (doc: Document) {
    some doc.lines
    let allLines = doc.lines[Line] + doc.lines.Line {
        all line: allLines | let data = doc.content[line] | one data and 
    all otherLine: allLines - line | doc.content[otherLine] != data
        some allLines.spaceAtStart
    }
}

在执行跟踪的每个步骤中,如果至少有一行需要展开,则执行展开操作。否则,不对文档做任何事情(即跳过)。

fact traces {
    init [first]
    all doc: Document - last | let doc' = doc.next {
        // If spaces exist in the document, do an unfold operation.
        // Otherwise, do nothing (i.e., skip).
        let allLines = doc.lines[Line] + doc.lines.Line {
            some allLines.spaceAtStart => not skip [doc, doc']
            some line: doc.lines | unfold [doc, doc', line] or skip [doc, doc']
        }
    }
}

"跳过"表示不对文档进行任何更改,即文档的下一个状态与上一个状态相同。

pred skip (doc, doc': Document) {
    doc'.lines = doc.lines
    doc'.content = doc.content
    doc'.firstLine = doc.firstLine
}

断言:文档的最终状态没有折叠线。

assert no_lines_are_folded {
    let allLines = last.lines[Line] + last.lines.Line |
        no allLines.spaceAtStart
}

check no_lines_are_folded for 6

合金分析仪没有找到反例。是啊!

0 个答案:

没有答案
相关问题