原创作者: qiezi   阅读:1042次   评论:0条   更新时间:2011-05-26    
借助D语言新的mixin表达式,可以完成一些代码生成功能,比如:

template attr_accessor(T, char[] name){
    mixin("
        private T _" ~ name ~ ";
        public " ~ name ~ "(){
            return _" ~ name ~ ";
        }
        public " ~ name ~ "(T v){
            _" ~ name " = v;
        }
    ");
}

class Foo{
    mixin attr_accessor!(int, "bar");
}

void main(){
    auto foo = new Foo;
    foo.bar = 3;
    writefln(foo.bar);
}


下面的大段代码演示了另一个功能,编译期字符串解析:
import std.metastrings;


template drop_white(char[] s){
	static if (s.length && (s[0] == ' ' || s[0] == '\t' || s[0] == '\r' || s[0] == '\n'))
		const char[] drop_white = s[1..$];
	else
		const char[] drop_white = s;
}

template drop_comment(char[] s){
	static if (s.length >= 2 && (s[0] == '/' && s[1] == '*'))
		const char[] drop_comment = scan_comment_end!(s[2..$]);
	else
		const char[] drop_comment = s;
}

template scan_comment_end(char[] s){
	static if (s.length >= 2){
		static if (s[0] == '*' && s[1] == '/')
			const char[] scan_comment_end = s[2..$];
		else
			const char[] scan_comment_end = scan_comment_end!(s[2..$]);
	}else{
		pragma(msg, "Failed to scan comment end");
		static assert(false);
	}
}

template Token(char[] t, char[] v, char[] r){
	const char[] type = t;
	const char[] value = v;
	const char[] remain = r;
	static if (t != "eof")
		alias next_token!(r) next;
}


template is_digit(char c){
	const bool is_digit = c >= '0' && c <= '9';
}

template is_letter(char c){
	const bool is_letter = c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z';
}
template get_token(char[] s){
	static if (s.length == 0 || s[0] == '\0'){
		const char[] type = "eof";
		const char[] value = "";
		const char[] remain = "";
	}else static if (s[0] == '_' || is_letter!(s[0])){
		alias parse_id!(s) token;
		const char[] type = "id";
		const char[] value = token.first;
		const char[] remain = token.second;
	}else static if (s[0] == '"'){
		alias parse_string!(s) token;
		const char[] type = "string";
		const char[] value = token.first;
		const char[] remain = token.second;
	}else static if (is_digit!(s[0])){
		alias parse_number!(s) token;
		const char[] type = "number";
		const char[] value = token.first;
		const char[] remain = token.second;
	}else static if (s[0] == '/'){
		alias parse_regexp!(s) token;
		const char[] type = "regexp";
		const char[] value = token.first;
		const char[] remain = token.second;
	}else{
		pragma(msg, "Can't parse token from: " ~ s);
		static assert(false);
	}
	debug pragma(msg, "Get token: " ~ value);
}

template parse_id(char[] s){
	static if (s.length && (s[0] == '_' || is_letter!(s[0]) || is_digit!(s[0]))){
		alias parse_id!(s[1..$]) id;
		const first = s[0..1] ~ id.first;
		alias id.second second;
	}else{
		const char[] first = "";
		alias s second;
	}
}

template parse_string(char[] s){
	static if (s[0] == '"')
		alias scan_string_end!(s[1..$]) parse_string;
	else{
		pragma(msg, "Failed to parse string");
		static assert(false);
	}
}

template parse_number(char[] s){
	static if (s.length && is_digit!(s[0])){
		alias parse_number!(s[1..$]) number;
		const char[] first = s[0..1] ~ number.first;
		const char[] second = number.second;
	}else{
		const char[] first = "";
		const char[] second = s;
	}
}

template parse_regexp(char[] s){
	static if (s.length && s[0] == '/')
		alias scan_regexp_end!(s[1..$]) parse_regexp;
	else{
		pragma(msg, "Failed to parse regexp");
		static assert(false);
	}
}

template scan_regexp_end(char[] s){
	static if (s.length){
		static if (s[0] == '/'){
			alias pair!("", s[1..$]) scan_regexp_end;
		}else static if (s.length >= 2 && s[0] == '\\'){
			alias scan_regexp_end!(s[2..$]) r;
			const char[] first = escape!(s[1]) ~ r.first;
			const char[] second = r.second;
		}else{
			alias scan_regexp_end!(s[1..$]) r;
			const char[] first = s[0..1] ~ r.first;
			const char[] second = r.second;
		}
	}else{
		pragma(msg, "Failed to parse regexp: " ~ s);
		static assert(false);
	}
}

template scan_string_end(char[] s){
	static if (s.length){
		static if (s[0] == '"')
			alias pair!("", s[1..$]) scan_string_end;
		else static if (s.length >= 2 && s[0] == '\\'){
			alias scan_string_end!(s[2..$]) s;
			alias pair!(escape!(s[1]) ~ r.first, r.second) scan_string_end;
		}else{
			alias scan_string_end!(s[1..$]) string;
			const char[] first = s[0..1] ~ string.first;
			const char[] second = string.second;
		}
	}else{
		pragma(msg, "Failed to parse string");
		static assert(false);
	}
}

template escape(char c){
	static if (c == 'n')
		const char[] escape = "\n";
	else static if (c == 't')
		const char[] escape = "\t";
	else static if (c == 'r')
		const char[] escape = "\r";
	else static if (c == '\\')
		const char[] escape = "\\";
	else{
		pragma(msg, "Failed to escape char '" ~ std.metastrings.ToString!(c) ~ "'");
		static assert(false);
	}
}


template next_token(char[] s){
	static if (s.length == 0){
		const char[] type = "eof";
		const char[] value = "";
		const char[] remain = "";
	}else{
		const char[] comment_and_white_dropped = drop_comment!(drop_white!(s));
		static if (s.length != comment_and_white_dropped.length){
			alias next_token!(comment_and_white_dropped) t;
		}else{
			alias get_token!(s) t;
		}
		alias t.type type;
		const char[] value = t.value;
		const char[] remain = t.remain;
	}
	static if (type != "eof")
		alias next_token!(remain) next;
}

template pair(char[] F, char[] S){
	alias F first;
	alias S second;
}

import std.stdio;


template output_token(alias T){
	static if (T.type != "eof"){
		pragma(msg, T.type ~ ": " ~ T.value);
		alias output_token!(T.next) output_token;
	}
}

template output(char[] token_parser, char[] source){
	mixin("alias " ~ token_parser ~ "!(source) parser;");
	alias output_token!(parser) output;
}

mixin output!("next_token", "a bc /a\\\\34/ \"bbc\" s 32 33 abv");


只需要编译它,就可以在编译期把字符串"a bc /a\\\\34/ \"bbc\" s 32 33 abv"解析为标记。

上面的代码全部使用模板来完成,实现极其复杂,而且无法在动态代码中重用,所以D 1.006加入了编译期执行方法,让这类代码可以在编译期和执行期都可以使用,不过目前为止,仅有小部分代码可以这样,但这会是它的大方向。

结合编译期生成和编译期执行这2项技术,可以完成一些复杂的编译器功能,一个理想的工作方式如下:

alias BNF!(import("ebnf.bnf")) EBNF;
alias EBNF!(import("sql92.ebnf")) SQL;
// 插入更多生成代码,或者是利用mixin生成。
User[] users = SQL!("select * from users where status = ?", 1);
alias EBNF!(import("idl.ebnf")) IDL;
mixin IDL!("interface X{} interface Y: X{}");


大概是这样的工作方式,在编译期就可以检查出SQL和IDL字符串的语法。
评论 共 0 条 请登录后发表评论

发表评论

您还没有登录,请您登录后再发表评论

文章信息

  • qiezi在2007-02-24创建
  • qiezi在2011-05-26更新
Global site tag (gtag.js) - Google Analytics