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

【D言語】dmdをビルドする【Ubuntu】

D言語

Ubunt 13.04環境下でdmdソースコードからビルドしてみます。g++やgitが必要です。


$ mkdir D && cd D
$ git clone https://github.com/D-Programming-Language/dmd.git
$ git clone https://github.com/D-Programming-Language/druntime.git
$ git clone https://github.com/D-Programming-Language/phobos.git
$ cd dmd
$ make -f posix.mak -j9 MODEL=64
$ sudo make -f posix.mak install INSTALL_DIR=/usr/local
$ cd ../druntime
$ make -f posix.mak -j9 MODEL=64 DMD=../dmd/src/dmd
$ sudo make -f posix.mak install INSTALL_DIR=/usr/local
$ cd ../phobos
$ make -f posix.mak -j9 MODEL=64 DMD=../dmd/src/dmd
$ sudo make -f posix.mak install INSTALL_DIR=/usr/local

環境にもよりますが、ビルドには大体1分くらいかかります。-jやMODELやINSTALL_DIRは適宜変えてください。

担当:美馬(とても簡単にビルドできて良い)

【D言語】__LINE__と__FILE__の挙動が修正される

D言語

前回は、LINEFILEの挙動を修正するPull Requestについてでしたが、

http://codelogy.org/?p=960

最近、そのPull RequestがMergeされ、LINEFILEの挙動が修正されました!


LINEFILEの挙動が修正されると、例えば


import std.conv : to;
import std.stdio : writeln;

// 同じ内容で、structとclassを両方定義する
string defBoth(size_t line = __LINE__, string file = __FILE__)(string name, string def)
{
    return "struct S" ~ name ~ "{" ~ def ~ "}"
           // 行数を合わせるために#lineを使う
           "#line " ~ line.to!string() ~ " \""~ file ~ "\"\n"
           "class C" ~ name ~ "{" ~ def ~ "}";
}

mixin (defBoth("Point", q{
    int x, y;

    this(int x, int y)
    {
        this.x = x;
        this.y = y;
        __LINE__.writeln();
    }
}));

void main()
{
    SPoint p1 = SPoint(1, 2);
    CPoint p2 = new CPoint(3, 4);
}

今までは、上のdefBothのようなことを実現するために、文字列mixinを使わざるを得ませんでした。今回の修正で、普通のテンプレートの引数のデフォルト値として使われたLINEFILEが、インスタンス化された場所の値になるようになったので、


import std.conv : to;
import std.stdio : writeln;

static assert(__VERSION__ >= 2064);

// 同じ内容で、structとclassを両方定義する
mixin template defBoth(string name, string def, size_t line = __LINE__, string file = __FILE__)
{
    mixin (defBothImpl(name, def, line, file));
}

string defBothImpl(string name, string def, size_t line, string file)
{
    return "#line " ~ line.to!string() ~ " \""~ file ~ "\"\n"
           "struct S" ~ name ~ "{" ~ def ~ "}"
           "#line " ~ line.to!string() ~ " \""~ file ~ "\"\n"
           "class C" ~ name ~ "{" ~ def ~ "}";
}

mixin defBoth!("Point", q{
    int x, y;

    this(int x, int y)
    {
        this.x = x;
        this.y = y;
        __LINE__.writeln();
    }
});

void main()
{
    SPoint p1 = SPoint(1, 2);
    CPoint p2 = new CPoint(3, 4);
}

このように、template mixinを使うことができます。書かなくてはいけないカッコが1組減るので、見やすくなっています。 便利ですね。

担当:美馬(ctpgもtemplate mixinのインターフェースを提供する予定)

【D言語】__LINE__と__FILE__の挙動を修正するPullReq

D言語

以前、テンプレート引数として使われるLINEFILEがバグっぽい挙動をするという記事を書きました。

http://codelogy.org/?p=471

が、つい最近、それを修正するPull Requestが来ました。

https://github.com/D-Programming-Language/dmd/pull/2617


ドキュメントによると、テンプレート引数のデフォルト値として使われるLINEFILEは、インスタンス化された場所の値になります。今までは、インスタンス化された場所ではなく使用された場所の値になっていました。


module main;

template T(int line = __LINE__) // 3行目で__LINE__を使用
{
    // インスタンス化された行数ではなく、__LINE__が使用された行数になってる!!
    static assert(line == 3); 
    int i;
}

void main()
{
    T!().i = 5; // 12行目でTをインスタンス化
}

この挙動が修正されると、呼び出し元の行数が必要で、仕方なく文字列mixinを使っていた場所で、template mixinが使いやすくなります。ctpgでも、文字列mixinではなくtemplate mixinのインターフェースを提供できるようになります。便利ですね。

次:http://codelogy.org/?p=977

担当:美馬(ctpgを使ったパーサ生成がちょっと書きやすくなる!)

【D言語】std.exception.ifThrown

D言語

std.exceptionにはifThrownという関数があります。ifThrownは、例外が投げられた時、投げられた例外に応じて任意の値を返すことができるものです。通常のtry catchよりも関数型な書き方ができます。


module main;

import std.conv : to, ConvException;
import std.exception : ifThrown, RangeError;
import std.stdio : writeln;

static assert(__VERSION__ >= 2063);

void main(string[] args) {
     int num = args[1]
          .to!int()
          .ifThrown!ConvException(1) // 引数がintに変換できない場合
          .ifThrown!RangeError(2);  // 引数が渡されていない場合
     num.writeln();
}

$ rdmd main.d 0
0
$ rdmd main.d a
1
$ rdmd main.d
2

なかなか便利に使えます。

担当:美馬(try catchの出番が減りそう)

【D言語】パッケージマネージャとしてのdub

D言語

前回の記事では、どちらかというとビルドツールとしてのdubの紹介になりました。今回は、パッケージマネージャとしてのdubを見てみます。


code.dlang.orgに、dubで使用出来るパッケージの一覧があります。vibe.dや、derelictなどの名前が見られます。


前回の記事に出てきたpackage.jsonの中に、dependenciesという項目がありました。ここに、パッケージ名とバージョンを追加することによって、そのパッケージを使用出来るようになります。


例として、ctpgパッケージを使ってみましょう。ctpgは、コンパイル時のパーサジェネレータです。


$ cd
$ dub init ctpg_test
Successfully created an empty project in '/home/〜〜〜/ctpg_test'.
$ cd ctpg_test

package.jsonを以下のように書きます。


{
    "name": "ctpg_test",
    "description": "An example project skeleton",
    "homepage": "http://example.org",
    "copyright": "Copyright © 2000, Your Name",
    "authors": [
        "Your Name"
    ],
    "dependencies": {
        "ctpg": "~master"
    }
}

バージョンの部分に書かれている~masterは、githubでのmasterブランチをバージョンとして使用するという意味です。そして、source/app.dで実際にctpgを使ってみます。


import std.stdio : writeln;
import std.conv : to;
import ctpg;

// 四則演算式を計算するパーサを生成する
mixin(genParsers(
q{
    @_setSkip(skip)

    // rootのパーサ
    int root = addExp $;

    // 加算と減算
    int addExp =
          mulExp !"+" addExp >> (lhs, rhs){ return lhs + rhs; }
        / mulExp !"-" addExp >> (lhs, rhs){ return lhs - rhs; }
        / mulExp;

    // 乗算と除算
    int mulExp =
          primary !"*" mulExp >> (lhs, rhs){ return lhs * rhs; }
        / primary !"/" mulExp >> (lhs, rhs){ return lhs / rhs; }
        / primary;

    // 数字と括弧でくくられた式
    int primary = !"(" addExp !")" / [0-9]+ >> to!int;
}));


void main()
{
    parse!root("4 *   5 + 2   * 11").value.writeln();
    parse!root("4 * ( 5 + 2 ) * 11").value.writeln();
}

実行すると、


$ dub
Checking dependencies in '/home/〜〜〜/ctpg_test'
The following changes will be performed:
Install ctpg ~master, userWide
Downloading ctpg ~master...
Installing ctpg ~master to /home/〜〜〜/.dub/packages/...
ctpg has been installed with version ~master
Building configuration "application", build type debug
Running dmd (compile)...
Linking...
Running /tmp/dub/1480571894/ctpg_test...
42
308

ちゃんとctpgが使えました!


同じようにして、vibe.dやderelictを使うこともできます。便利ですね。

担当:美馬(とても便利でよい)

【D言語】D言語のビルドツール兼パッケージマネージャ、dub

D言語

D言語のパッケージマネージャ兼ビルドツールであるところのdubが便利なので、導入しつつ軽く使ってみます。


Windowsの場合

ここに、インストーラがあります。


Ubuntuの場合

ここLinux用のバイナリがあります。D-APTからでもインストールできます。


OS Xの場合

同じく、ここOS X 10.7用のバイナリがあります。Homebrewを使ってインストールする事もできます。


新しくdubを使ってプロジェクトを作る時は、dub initを使います。


$ cd
$ dub init hoge
Successfully created an empty project in '/home/〜〜〜/hoge'.

すると、hogeディレクトリの中に様々なディレクトリと、package.jsonというファイルが作成されます。package.jsonの中身は、以下のようになっています。


$ cd hoge
$ cat package.json
{
    "name": "hoge",
    "description": "An example project skeleton",
    "homepage": "http://example.org",
    "copyright": "Copyright © 2000, Your Name",
    "authors": [
        "Your Name"
    ],
    "dependencies": {
    }
}

適当に編集しましょう。


既存のプロジェクトをdubでビルドできるようにするには、このpackage.jsonを自分で書きます。


dubは、デフォルトではsourceディレクトリまたはsrcディレクトリ内のソースコードをビルドしてくれます。例えば、~/hoge/source/app.dを


module app;

void main()
{
    import std.stido;
    "========== app ==========".writeln();
}

とすると、


$ dub
Checking dependencies in '/home/〜〜〜/hoge'
Building configuration "application", build type debug
Running dmd (compile)...
Linking...
Running /tmp/dub/〜〜〜/hoge...
========== app ==========

となります。ちゃんとビルドされています。便利ですね。

次:パッケージマネージャとしてのdub

担当:美馬(dub本当に便利)

【D言語】無名再帰してみる

D言語

無名再帰と言えば不動点コンビネータですね。 D言語だと、定義のとおりに不動点コンビネータを書けます。


R delegate(Args) fix(R, Args...)(R delegate(R delegate(Args), Args) f)
{
    return (Args args) => f(fix(f), args);
}

ラムダ構文があるので、少し楽に書けます。 上で定義した不動点コンビネータを使うと、以下のように無名再帰ができます。


R delegate(Args) fix(R, Args...)(R delegate(R delegate(Args), Args) f)
{
    return (Args args) => f(fix(f), args);
}

void main()
{
    import std.stdio;
    fix((int delegate(int) rec, int num) => num != 0 ? num * rec(num - 1) : 1)(5).writeln();
}

残寝ながら、recとnumの引数の型は省略できません。そこまで推論してくれないみたいですね。

参考:Wikipedia「不動点コンビネータ」

担当:美馬(型を省略したい・・・)