9
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

D言語erでもパターンマッチしたい

Last updated at Posted at 2016-12-22

正規表現の方ではなく、データ構造の分解などが出来る方のパターンマッチです。
C# 7で入るだとか、ScalaやElixerの流行などで最近パターンマッチが人気です(多分)。

というわけで作りました。

dmatch

githubレポジトリ
現在開発中で正常に動作しないです

使い方

pmatchテンプレートの第一テンプレートパラメータに分解する変数、第二引数にコードを書きます。
コードは

パターン1 => 実行されるコード1
パターン2 => 実行されるコード2
...

のように記述します。
構文はラムダ式の構文に似ていますが、実行されるコードは式ではなく一つ以上の文を受け取ります。つまり、セミコロンが必要です。

パターン

RecordパターンとForward Rangeパターンは思いつかなかったのでML系のものを持ってきました。良い構文などあればissueお願いします。

Arrayパターン

[]
[a,b,c]
[a,b,c] ~ d
a ~ [b,c,d]
[a,b] ~ c ~ [d,e]

Forward Rangeパターン

a::b::c
a::b::[]

Variantパターン

a : int

Recordパターン

{a=alpha,b=beta}

パターンを組み合わせる構文

(a::b)

a @ b

その他

0x12 "str"などの値を入れることも出来ます

[1,a,b]

使用例

int sum(int[] ary) {
    mixin(pmatch!(ary,q{
        [] => return 0;
        x::xs => x + sum (xs);
    }
    return 0;
}

unittest {
    assert (sum([1,2,3] == 6);
}

実装

構文解析、意味解析、コード生成の3段階に分けています。
構文解析器はPEGを使っています。
@outlandkarasu@githubさんの以下の記事を参考にさせていただきました。
D言語で構文解析器をつくる(1) パーサー・コンビネーター編
D言語で構文解析器をつくる(2) セマンティック・アクション編
D言語で構文解析器をつくる(3) アブストラクト・シンタックス・クリスマスツリー編
パーサーコンビネータはほぼ記事のままですが、ASTを構築する部分はオリジナル実装になっています。
データを永続的なものにしてバックトラックを解析結果を捨てるだけで出来るようにしています。

意味解析部では主にArrayパターンを解析し、コード生成の際に必要などの添字やスライスの情報などをASTに付け足していきます。

コード生成部では

[1,2,x]

[__tmp__1,__tmp__2,x] if (__tmp__1==1&&__tmp__2==2)

のようなコードに変換した後、実際にコードを生成します。
例えば、

[a:int,b:int]~c => exec();


if (arg[0].type == typeid(int))
{
    auto a = arg[0].get!(int);
    if (arg[1].type == typeid(int))
    {
        auto b = arg[1].get!(int);
        auto c = arg[2 .. $ - 0];
        if (true)
        {
            exec();
        }
    }
}

のように、

a::b::c => exec();

auto __arg_saved__ = arg.save;
if (!__arg_saved__.empty)
{
    auto a = __arg_saved__.front;
    __arg_saved__.popFront;
    if (!__arg_saved__.empty)
    {
        auto b = __arg_saved__.front;
        __arg_saved__.popFront;
        auto c = __arg_saved__;
        if (true)
        {
            exec();
        }
    }
}

のようなコードに変換されます。(実際はインデントと改行はされません)

現在の状況

条件分岐が正常に動作しません。デバッグ中です。
年が明けるまでには使い物になるようにするつもりです。
生成されたコードの通り、代入しても参照型以外元のデータに変更が加えられません。変更可能なようにする予定です。

[a,#(a+1),#(a+2)] //[1,2,3],[12,13,14]などにマッチする

このような動作を実装するつもりですが、良い構文が思いつきません。issueください。

まとめ

なんとか間に合ったと思ったのですが実際動かすとバグバグで全然間に合っていませんでした...。
明日から冬期休業で時間が取れる(はず)なので進捗していきたいです。
v1.0.0をリリース出来たら細かい実装についても記事にしたいと思います。

明日は@youxkeiさんの「今年もD言語の構文を拡張する」とのことでとても楽しみです。

9
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?