Parsec:值类型取决于键的键值对

时间:2013-11-15 12:03:54

标签: haskell parsec

我正在尝试解析sgf文件(描述go游戏的文件)。在这些文件中有表单的键值对(在sgf规范中,它们被称为属性id和属性值,但我使用键和值,希望人们在阅读标题时知道我在说什么):

key[value]

key[value1][value2]...[valuen]

也就是说,可能有一个或多个值。问题是值的类型取决于键。因此,例如,如果键是B(用于在go中玩黑石)。该值应该是由两个字母描述的坐标,例如:B[ab]。也可能是密钥是AB(用于添加一些黑色宝石,用于设置棋盘),然后该值是一个坐标列表,如下所示:AB[ab][cd][fg]。也可能是密钥是C(用于评论)。然后,该值只是一个字符串C[this is a comment]

当然这可以通过类型

来描述
type Property = (String, [String])

但我认为拥有像

这样的东西会更好
data Property = B Coordinate | AB [Coordinate] | C String ...

或者也许其他类型更好地利用类型系统,并且不需要我一直转换为字符串和从字符串转换。

问题是,我需要一个解析器,根据键类型返回不同的值类型,但我认为这会导致类型问题,因为解析器只能返回一种类型的值。

你会如何解析这样的事情?

1 个答案:

答案 0 :(得分:6)

这实际上是一个简单的选择,不需要monadic解析。我将使用应用程序界面来证明这一点。

为每个属性id 构建一个解析器,其属性值有点像这样:

black = B <$> (char 'C' *> coordinate)
white = W <$> (char 'W' *> coordinate)
addBlack = AB <$> (string "AB" *> many1 coordinate)

(假设你已经构建了一个coordinate解析器来吃括号并返回Coordinate类型的东西。)

其中每一个都有类型Parser Property(使用您的第二个更好的结构化数据类型),所以现在我们只需让解析器在它们之间进行选择。如果属性ID都具有不同的首字母,那么当您使用解析器输入错误的id时,它将在不消耗输入的情况下失败,这对于选择运算符来说是理想的:

myparser = black <|> white <|> addBlack

但我怀疑有一个AW id用于添加白色宝石,所以我们需要警告它们使用try重叠,当解析器失败时会回溯:

mybetterparser = black <|> white <|> (try addBlack <|> try addWhite)

我已经将解析器与一个共同的开头放在一起,并尝试使用它们回到开头。