D语言编译期生成和编译期执行技术

借助D语言新的mixin表达式,可以完成一些代码生成功能,比如:

[code]
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);
}
[/code]

下面的大段代码演示了另一个功能,编译期字符串解析:
[code]
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");
[/code]

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

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

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

[code]
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{}");
[/code]

大概是这样的工作方式,在编译期就可以检查出SQL和IDL字符串的语法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值