KOTET'S PERSONAL BLOG

#dlang 処理後の結果を見られるdmdのスイッチ -vcg-ast

Created: , Last modified:
#dlang #tech

これは1年以上前の記事です

ここに書かれている情報、見解は現在のものとは異なっている場合があります。

Dconf 2017で出てきたコマンドラインスイッチ-vcg-astについて書き残しておく。 これはgccでいう-Eオプションのようなもので、これはいろいろな"semantic steps" 1 がすべて終了したあとのASTを読めるかたちに書きだすオプションである。

-vcg-astが使われた動画。 使用箇所付近(25:00)から再生が始まる。

使ってみる

app.d

void main()
{
	import std.stdio : writeln;

	static immutable table = () {
		import std.range : iota, array;
		import std.algorithm : map;
		import std.math : sqrt;

		return 10.iota.map!((real n) => n.sqrt()).array;
	}();
	alias T = table;
	mixin("writeln(T);");
}

ここで$ dmd app.d -vcg-astすると、app.d.cgというファイルが生成される。 中を見たら7912行もあった。

app.d.cg

import object;
void main()
{
	import std.stdio : writeln;
	static immutable immutable(real[]) table = [0.00000L, 1.00000L, 1.41421L, 1.73205L, 2.00000L, 2.23607L, 2.44949L, 2.64575L, 2.82843L, 3.00000L];
	alias T = static immutable immutable(real[]) table = [0.00000L, 1.00000L, 1.41421L, 1.73205L, 2.00000L, 2.23607L, 2.44949L, 2.64575L, 2.82843L, 3.00000L];
	;
	writeln(table);
	return 0;
}
CommonType!(int, int)
alias CommonType = int;
CommonType!int
alias CommonType = int;
isFloatingPoint!int
enum bool isFloatingPoint = false;
Unqual!int
alias Unqual = int;
// 以下略

関数リテラルはCTFEの結果である配列になり、文字列mixinは普通のコードになり、さらにaliasも展開されている。 やたら行数が大きいのは自動的にimportされたobjectのせいだろう。 コードがコンパイラによってどのように展開されるかを知ることができてためになるし、なんだか見てて楽しい。

dubで使う

dub.json

{
	"name": "test",
	"authors": [
		"kotet"
	],
	"description": "A minimal D application.",
	"copyright": "Copyright © 2017, kotet",
	"license": "proprietary",
	"dependencies": {
		"progress": "*"
	},
	"targetType": "executable",
	"configurations": [
		{
			"name": "default"
		},
		{
			"name": "ast",
			"dflags": [
				"-vcg-ast"
			]
		}
	]
}

source/app.d

void main()
{
	import std.stdio : writeln;
	import std.range : iota;
	import progress.bar : Bar;

	auto b = new Bar();
	foreach (n; b.iter(10.iota))
	{
	}
	mixin("writeln(b);");
}

$ dub build -c astすると.d.cgファイルが出力される。

source/app.d.cg

import object;
void main()
{
	import std.stdio : writeln;
	import std.range : iota;
	import progress.bar : Bar;
	Bar b = new Bar;
	{
		Generator!int __r3001 = b.iter(iota(10));
		for (; !__r3001.empty(); __r3001.popFront())
		{
			int n = __r3001.front();
		}
	}
	writeln(b);
	return 0;
}
// 以下略

foreachforに展開されている。 たのしい。

関連リンク