`
qiezi
  • 浏览: 498696 次
  • 性别: 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  
真是个 理想工作方式.

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

相关推荐

    D语言真相 The Case for D(1-5)

    由于可以直接生成机器码,D语言的程序执行速度快,而且它的模板元编程和内联函数等功能,允许在编译期进行计算,进一步提高了代码的运行效率。 此外,D语言还有强大的标准库,涵盖了字符串处理、网络编程、并发处理...

    D 语言编程参考手册 1[1].0(上).

    D语言在Win32平台上的应用主要涉及调用约定、可执行文件格式等方面。其中,调用约定(Calling Conventions)是指函数参数传递的方式,D语言支持多种调用约定,如`cdecl`、`pascal`、`stdcall`、`fastcall`等,以适应...

    2010年期考编译原理期末复习题

    2. **编译的必要性**:用高级语言编写的源程序必须先通过编译,生成机器语言或汇编语言的目标代码,然后才能在计算机上执行。因此,上述说法是正确的。 3. **目标程序性质**:编译程序生成的目标程序并不总是机器...

    第三周论文精读.pdf

    3. Constant Folding:该技术可以将常量表达式计算结果缓存在编译期,以提高代码的执行速度。 4. Strength Reduction:该技术可以将复杂的运算简化为简单的运算,以提高代码的执行速度。 5. Register Allocation:...

    gcc-v4.3使用手册.pdf

    - **模板元编程**:GCC支持C++模板元编程技术,这使得可以在编译期完成复杂的计算任务。 #### 七、Objective-C运行时特性 Objective-C是一种面向对象的编程语言,GCC-v4.3支持Objective-C的一些特殊特性,如: - **...

    网页设计与制作(HTML+CSS+JS)-3期(KC003) 网页设计与制作 html5+CSS3+JavaScript 第1章

    动态网页制作技术包括PHP、JSP和ASP.NET,它们允许服务器端处理数据并生成动态内容,而CSS(Cascading Style Sheets)是样式表语言,用于定义静态网页的外观,所以选项D不是动态网页技术,正确答案是B。 在域名...

    大学《编译原理》期末试题含答案(八).docx

    《编译原理》期末试题包含了多个关于编译器设计的核心知识点,主要涉及正则表达式、LR(1)文法、LL(1)分析表、语法制导定义、中间代码生成、作用域与生存期、类型系统以及编译器移植等。 1. **正则表达式的DFA状态...

    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 泛型与类型擦除...

    从0到C ——Linux 上 C 语言编程入门

    2. **编译源代码**:使用GCC(GNU Compiler Collection)编译源代码,生成机器码。 3. **把目标代码链接起来**:将编译产生的多个目标文件链接成一个可执行文件。 4. **调试程序**:使用调试工具(如GDB)查找并修复...

    c++ 飞机大战练习

    - **编译时和运行时检查**:C++的模板元编程和运行时类型信息(RTTI)可以在编译期或运行期提供额外的安全检查。 4. **飞机大战游戏具体实现**: - **飞机模型**:每个飞机可以是一个类,包含位置、速度、生命值...

    北大青鸟5.0二期笔试试题(11月份有答案)

    16. ASP.NET运行机制:ASP.NET中,`.aspx`页面首次执行时会编译生成对应的`.dll`文件,第二次执行时直接使用编译后的结果,故第一次执行通常比第二次执行慢。选项a)正确。 17. DWR框架:Direct Web Remoting (DWR)...

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

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

    2012年10月自学考试数据库系统原理试题及答案

    ### 数据库系统原理知识点解析 #### 一、单项选择题解析 **1....- **选项分析**: - A.... - B.... - C. 编译系统:同样与数据管理...这些知识点涵盖了数据库系统的基本理论和技术,对于理解数据库系统的工作原理非常重要。

    超市中如何使用计算机对顾客购物品种的相关性进行分析原稿.doc

    - MATLAB Compiler能够将MATLAB编写的代码编译成独立的可执行程序或DLL文件。 - 在本项目中,它被用来将MATLAB编写的分析代码转化为VC++可以调用的形式,从而实现数据处理和图形展示的功能。 #### 三、数据分析...

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

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

    c语言编写单片机技巧

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

    下半年软件评测师上午试题.docx

    1. CPU 中,控制器负责保证指令的正确执行和处理异常事件。 2. 循环冗余校验码(CRC)是一种数据校验方法,使用生成多项式进行编码,数据位和校验位的格式为 k 个数据位之后跟 r 个校验位。 3. 存储器可以按寻址...

Global site tag (gtag.js) - Google Analytics