読者です 読者をやめる 読者になる 読者になる

CORDEA blog

Programming及びFedora21等のLinux OSのことが多めです。

Nim の pegs モジュールを使う

この記事は Nim Advent Calendar 2016 24 日目の記事です。

クリスマスとか年末に絡めた何かを考えていたのですが、思いつかなかったので普通に書きます。

PEG とは

PEG について、解説的なものを入れようかと思ったのですが、
Wikipedia がとても詳しく、私が生半可な知識で書くのも良くないのでそちらをご覧ください。
https://ja.wikipedia.org/wiki/Parsing_Expression_Grammar

使ってみる

使い方は PEG に慣れていればなんの抵抗もないはずですが、
私のように正規表現しか書いたことのない人間には馴染みの薄い物です。

では先日使用した json をそのまま流用して試してみます。
使用した jsonjson.org の JSON Example の一番上のやつです。
http://json.org/example.html
この json を example.json として保存し、使用しています。

"SortAs": "SGML",

json からkey, value (この場合は SortAs:SGML )を得たい場合
PEG ではこのように書くことができます

'"' {( \w )+} '":' \s+ '"' {( \w )+} '"' ','?

 

"Abbrev": "ISO 8879:1986",

この場合は

'"' {( \w )+} '":' \s+ '"' {( \w / \s / ':' )+} '"' ','?

となります。


これらの規則は非終端記号として定義できます。

grammer <- '"' {( \w )+} '":' \s+ '"' {( \w / \s / ':' )+} '"' ','?

key, value 部分をまとめて以下のような記述にもできます。

grammer <- '"' cap '":' \s+ '"' cap '"' ','?
cap <- {( \w / \s / ':' )+}


最後に、json 全体をパースしてみます

rule <- '{' \s+
    '"glossary":' \s+ '{' \s+
        title
        '"GlossDiv":' \s+ '{' \s+
            title
            '"GlossList":' \s+ '{' \s+
                '"GlossEntry":' \s+ '{' \s+
                    (glossDef / pair)*

glossDef <- '"GlossDef":' \s+ '{' \s+ pair '},' \s+
pair <- '"' cap '":' \s+ '"' cap '"' ','? \s+
title <- '"title":' \s+ '"' cap '",' \s+
cap <- {( \w / \s / [.,-:;] )+}


このままだと nim ではなく PEG の話になるので申し訳程度にコードと結果を載せておきます
ちなみに今回の json でしか機能せず汎用性は全くありませんので...

import pegs

const peg = """
rule <- '{' \s+
    '"glossary":' \s+ '{' \s+
        title
        '"GlossDiv":' \s+ '{' \s+
            title
            '"GlossList":' \s+ '{' \s+
                '"GlossEntry":' \s+ '{' \s+
                    (glossDef / pair)*

glossDef <- '"GlossDef":' \s+ '{' \s+ pair '},' \s+
pair <- '"' cap '":' \s+ '"' cap '"' ','? \s+
title <- '"title":' \s+ '"' cap '",' \s+
cap <- {( \w / \s / [.,-:;] )+}
"""
let str = "example.json".readFile()
if str =~ peg(peg):
    for i in 0..(matches.len - 1):
        if i < 2:
            echo "title: ", matches[i]
        else:
            if matches[i] != nil:
                if i mod 2 == 0:
                    echo "key: ", matches[i]
                else:
                    echo "value: ", matches[i]

この出力は以下の通りです。

title: example glossary
title: S
key: ID
value: SGML
key: SortAs
value: SGML
key: GlossTerm
value: Standard Generalized Markup Language
key: Acronym
value: SGML
key: Abbrev
value: ISO 8879:1986
key: para
value: A meta-markup language, used to create markup languages such as DocBook.
key: GlossSee
value: markup

参考