原创作者: qiezi
阅读:1036次
评论:0条
更新时间:2011-05-26
借助D语言新的mixin表达式,可以完成一些代码生成功能,比如:
下面的大段代码演示了另一个功能,编译期字符串解析:
只需要编译它,就可以在编译期把字符串"a bc /a\\\\34/ \"bbc\" s 32 33 abv"解析为标记。
上面的代码全部使用模板来完成,实现极其复杂,而且无法在动态代码中重用,所以D 1.006加入了编译期执行方法,让这类代码可以在编译期和执行期都可以使用,不过目前为止,仅有小部分代码可以这样,但这会是它的大方向。
结合编译期生成和编译期执行这2项技术,可以完成一些复杂的编译器功能,一个理想的工作方式如下:
大概是这样的工作方式,在编译期就可以检查出SQL和IDL字符串的语法。
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 条 请登录后发表评论