`
qiezi
  • 浏览: 491507 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

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

    博客分类:
  • D
阅读更多
借助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字符串的语法。
分享到:
评论
3 楼 qiezi 2007-02-27  
混淆倒是不容易。

你后面说的这个“Walter 应该把可以解释执行的代码范围放宽”,目前看来他应该正在做这件事。1.007比1.006多出来的功能里面主要就是这部分。
2 楼 oldrev 2007-02-26  
编译期执行是好东西,不过我觉得应该添加一个关键字:eval,让它表示在编译器执行代码求值,避免编译器混淆出错,如:
int sqr(x)
{
return x*x;
}

const int foo = eval(sqr()); //编译时执行
const int bar = sqr();


其次 Walter 应该把可以解释执行的代码范围放宽,我个人认为只要编译器看得到所需源代码就应该允许编译期执行。
1 楼 ideage 2007-02-25  
真是个 理想工作方式.

更理想的是动态语言嵌入了.

相关推荐

    Java虚拟机

    第10章 早期(编译期)优化 10.1 概述 10.2 Javac编译器 10.2.1 Javac的源码与调试 10.2.2 解析与填充符号表 10.2.3 注解处理器 10.2.4 语义分析与字节码生成 10.3 Java语法糖的味道 10.3.1 泛型与类型擦除...

    Java 语言基础 —— 非常符合中国人习惯的Java基础教程手册

    一个对象的生命期包括三个阶段:创建对象、对象的引用和释放对 象 。 1.8.3 创建对象 创建对象包括声明、实例化和初始化三方面的内容。通常的格式为 : 1. 声明对象 对象声明实际上是给对象命名,也称定义一个实例...

    软件工程-理论与实践(许家珆)习题答案

    原型化开发方法包括生成原型和实现原型两个步骤。(×) 6. 面向对象的开发方法包括面向对象的分析、面向对象的设计和面向对象的程序设计。( √) 7. 软件危机的主要表现是软件的需求量迅速增加,软件价格上升。(×)...

    c语言编写单片机技巧

    张晓冬 等编著 )和《16/32 位微机原理、汇编语言及接口技术》(作者: 钟晓捷 陈涛 ,机械工业出版社 出版)等,可以在较大型的科技书店里查找或者直接从网上订购。 6. 初学者到底是应该先学C还是汇编? ...

    精通DirectX.3D图形与动画程序设计.pdf

     本书配套光盘提供了书中所有示例程序的可执行文件、工程文件和完整源代码,以方便读者编译、调试示例程序。  本书主要面向对direct3d三维图形程序设计感兴趣的编程人员、游戏开发人员以及可视化仿真工程技术...

    C语言入门经典(第4版)--源代码及课后练习答案

    近几年一直在跟踪.NET技术的发展,积极从事.NET技术文档和图书的翻译工作。 目录 封面 -12 封底 572 前言 -9 目录 -6 第1章 C语言编程 1 1.1 创建C程序 1 1.1.1 编辑 1 1.1.2 编译 2 1.1.3 链接 2 1.1.4 执行 ...

    asp.net知识库

    Coalesys PanelBar + R.a.d Treeview +Xml 构建的Asp.net 菜单和权限管理模块 突破屏蔽限制,自己的网站使劲弹新IE窗口 对页面SCROLLING的CSS不能生效原因 .Net 中IE使用WinForm控件的使用心得。 动态加载用户控件的...

    新版Android开发教程.rar

    的 Android SDK 提供了在 Android 平台上使用 JaVa 语言进行 Android 应用开发必须的工具和 API 接口。 特性 • 应用程序框架 支持组件的重用与替换 • Dalvik Dalvik Dalvik Dalvik 虚拟机 专为移动设备优化 • ...

    C语言FAQ 常见问题列表

    o 4.5 我可否用括号来强制执行我所需要的计算顺序? o 4.6 可是 && 和 || 运算符呢?我看到过类似 while((c = getchar()) != EOF && c != '\n') 的代码 …… o 4.7 我怎样才能理解复杂表达式?``序列点" 是什么?...

    深入浅出MFC【侯捷】

    执行期类型信息(RTTI) 动态生成(Dynamic Creation) 异常处理(Exception Handling) Template Template Functions Template Classes Template的编译与链接 第3章 MFC六大关键技术之仿真 MFC类层次结构 Frame 1...

    深入浅出MFC 2e

    执行期类型信息(RTTI) 动态生成(Dynamic Creation) 异常处理(Exception Handling) Template Template Functions Template Classes Template的编译与链接 第3章 MFC六大关键技术之仿真 MFC类层次结构 Frame 1...

    侯捷- -深入浅出MFC

    执行期类型信息(RTTI) 动态生成(Dynamic Creation) 异常处理(Exception Handling) Template Template Functions Template Classes Template的编译与链接 第3章 MFC六大关键技术之仿真 MFC类层次结构 Frame 1...

    PT80-NEAT开发指南v1.1

    NEAT 程序一般执行过程 ..................................................................................................................... 20 第四章 窗口 ................................................

    你必须知道的495个C语言问题(PDF)

    3.5 我可否用括号来强制执行我所需要的计算顺序? . . . . . . . . . . 14 3.6 可是&& 和|| 运算符呢?我看到过类似while((c = getchar()) != EOF && c != ’nn’) 的代码⋯⋯ . . . . . . . . . . . . . . . . . . ...

    操作系统(内存管理)

    文将对 Linux™ 程序员可以使用的内存管理技术进行概述,虽然关注的重点是 C 语言,但同样也适用于其他语言。文中将为您提供如何管理内存的细节,然后将进一步展示如何手工管理内存,如何使用引用计数或者内存池来半...

    51单片机C语言编程基础及实例

    a(D0),b(D1),c(D2),d(D3),e(D4),f(D5),g(D6),h(D7), 相应 8 个发光二极管正好与单片机一个端口 Pn 的 8 个引脚连接,这样单片机就可以通过引脚输出高 低电平控制 8 个发光二极的亮与灭,从而显示各种数字和符号;...

Global site tag (gtag.js) - Google Analytics